X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/4a605fa39078354654ae648e489c117c7a445197..98d137c3e945a09f76e85bd18238558c668fdca4:/agent_test.go?ds=inline diff --git a/agent_test.go b/agent_test.go index dabecd2..6a22bee 100644 --- a/agent_test.go +++ b/agent_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "os" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/require" @@ -46,7 +47,7 @@ func TestRenamedAgentKeepsItsCacheKey(t *testing.T) { config := agent.config config.Name = "Lena" - writeAgentConfig(t, dir, config) + agentWriteTestConfig(t, dir, config) renamed, err := openAgent(dir) require.NoError(t, err) @@ -61,22 +62,22 @@ func TestAgentConfigErrors(t *testing.T) { err string }{ { - name: "unknown field", + name: "rejects unknown field", config: `{"id": "00112233445566778899aabbccddeeff", "nmae": "Lena"}`, err: `unknown field "nmae"`, }, { - name: "missing id", + name: "rejects missing id", config: `{"name": "Lena"}`, err: "id must be 32 hexadecimal digits", }, { - name: "relative workdir", + name: "rejects relative workdir", config: `{"id": "00112233445566778899aabbccddeeff", "workdir": "src"}`, err: `workdir "src" is not an absolute path`, }, { - name: "name of two lines", + name: "rejects name of two lines", config: `{"id": "00112233445566778899aabbccddeeff", "name": "Le\nna"}`, err: "name must be one line of text", }, @@ -100,16 +101,95 @@ func TestAgentPromptReadsTheInstructionsEachTime(t *testing.T) { text, err := agent.Prompt() require.NoError(t, err) - require.Equal(t, prompt(agent.config.Name, ""), text) + require.Equal(t, prompt(agent.config.Name, filepath.Join(dir, agentInstructionsFileName), ""), text) + + require.NoError(t, os.WriteFile(filepath.Join(dir, agentInstructionsFileName), []byte("I am Lena.\n"), 0o600)) - require.NoError(t, os.WriteFile(filepath.Join(dir, instructionsFileName), []byte("I am Lena.\n"), 0o600)) text, err = agent.Prompt() require.NoError(t, err) - require.Equal(t, prompt(agent.config.Name, "I am Lena."), text) + require.Equal(t, prompt(agent.config.Name, filepath.Join(dir, agentInstructionsFileName), "I am Lena."), text) +} + +func TestAgentPromptUsesAnAbsoluteInstructionsPath(t *testing.T) { + dir := t.TempDir() + workdir, err := os.Getwd() + require.NoError(t, err) + + relative, err := filepath.Rel(workdir, dir) + require.NoError(t, err) + require.False(t, filepath.IsAbs(relative)) + + agent, err := openAgent(relative) + require.NoError(t, err) + + text, err := agent.Prompt() + require.NoError(t, err) + require.Contains(t, text, "The user's instructions live in the file "+filepath.Join(dir, agentInstructionsFileName)+",") +} + +func TestChildPromptDefinesItsAreaAndReports(t *testing.T) { + dir := t.TempDir() + agent, err := openAgent(dir) + require.NoError(t, err) + + config := agent.config + config.Name = "Garden" + config.Parent = "OneChat" + config.Responsibility = "Keep the garden plans and decisions." + agentWriteTestConfig(t, dir, config) + require.NoError(t, os.WriteFile(filepath.Join(dir, agentInstructionsFileName), []byte("Ask before editing plans."), 0o600)) + + agent, err = openAgent(dir) + require.NoError(t, err) + + text, err := agent.Prompt() + require.NoError(t, err) + require.Contains(t, text, "You are Garden, a child agent of OneChat.") + require.Contains(t, text, "You are responsible for: Keep the garden plans and decisions.") + require.Contains(t, text, "Work only in this area.") + require.Contains(t, text, "Report to OneChat only what needs the parent or the") + require.Contains(t, text, "End your turn with an empty reply when nothing needs the parent or the user.") + require.Contains(t, text, "Send nothing to WhatsApp. Draft replies in your report instead") + require.Contains(t, text, filepath.Join(dir, agentInstructionsFileName)) + require.True(t, strings.HasSuffix(text, "Ask before editing plans.")) + require.NotContains(t, text, "You act again only when the user writes.") +} + +func TestChildConfigNeedsAParentAndAnArea(t *testing.T) { + testCases := []struct { + name string + parent string + responsibility string + }{ + { + name: "a parent needs an area", + parent: "OneChat", + }, + { + name: "an area needs a parent", + responsibility: "Keep the garden plans.", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dir := t.TempDir() + agent, err := openAgent(dir) + require.NoError(t, err) + + config := agent.config + config.Parent = tc.parent + config.Responsibility = tc.responsibility + agentWriteTestConfig(t, dir, config) + + _, err = openAgent(dir) + require.ErrorContains(t, err, "a child needs both responsibility and parent") + }) + } } -// writeAgentConfig writes config as the config.json of the agent in dir. -func writeAgentConfig(t *testing.T, dir string, config AgentConfig) { +// agentWriteTestConfig writes config as the config.json of the agent in dir. +func agentWriteTestConfig(t *testing.T, dir string, config AgentConfig) { t.Helper() data, err := json.Marshal(config)