"net/http"
"os"
"path/filepath"
+ "strconv"
"strings"
"testing"
"time"
require.Len(t, entries, 1)
}
+func TestTellReachesARunningSubagentBetweenItsToolCalls(t *testing.T) {
+ server := newModelFake(t)
+ arrived := make(chan struct{})
+ told := make(chan struct{})
+ server.reply = func(w http.ResponseWriter, call ModelFakeCall) {
+ switch {
+ case !subagentTestCall(call):
+ modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("Noted.")}, 100, 0, 5))
+ case call.number == 0:
+ close(arrived)
+ <-told
+ modelWriteEvents(w, modelCompletedEvent([]any{modelFunctionCallItem("fc_1", "call_1", "shell", `{"command":"true"}`)}, 100, 0, 5))
+ default:
+ modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("Used the second disk.")}, 100, 0, 5))
+ }
+ }
+
+ test := newTestTurnLoop(t, server)
+ runs := test.loop.runs
+ _, err := runs.Spawn(t.Context(), []string{"Copy the files."}, nil)
+ require.NoError(t, err)
+
+ <-arrived
+ name := test.events.Last("subagent")
+ output, err := runs.Tell(t.Context(), name, "Use the second disk.")
+ require.NoError(t, err)
+ require.Equal(t, "Sent. "+name+" reads it between its tool calls, and its report answers it.", output)
+ close(told)
+
+ test.events.WaitFor(t, "idle", 1)
+ require.Equal(t, "Use the second disk.", subagentTestText(subagentTestLastItem(server.Call(1))))
+ require.Equal(t, "work: "+subagentReport(name, []string{"Used the second disk."}), test.Log(t)[0])
+}
+
+func TestTellAfterTheFinalAnswerStartsAnotherChain(t *testing.T) {
+ server := newModelFake(t)
+ arrived := make(chan struct{})
+ told := make(chan struct{})
+ server.reply = func(w http.ResponseWriter, call ModelFakeCall) {
+ switch {
+ case !subagentTestCall(call):
+ modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("Noted.")}, 100, 0, 5))
+ case call.number == 0:
+ close(arrived)
+ <-told
+ modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("First.")}, 100, 0, 5))
+ default:
+ modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("Second.")}, 100, 0, 5))
+ }
+ }
+
+ test := newTestTurnLoop(t, server)
+ runs := test.loop.runs
+ _, err := runs.Spawn(t.Context(), []string{"Count."}, nil)
+ require.NoError(t, err)
+
+ <-arrived
+ name := test.events.Last("subagent")
+ _, err = runs.Tell(t.Context(), name, "Count again.")
+ require.NoError(t, err)
+ close(told)
+
+ test.events.WaitFor(t, "idle", 1)
+ require.Equal(t, "0|note: subagent\n\n1|user: Count.\n\n2|user: Count again.\n\n3|reply: First.", subagentTestText(subagentTestLastItem(server.Call(1))))
+ require.Equal(t, "work: "+subagentReport(name, []string{"First.", "Second."}), test.Log(t)[0])
+}
+
+func TestTellToAFinishedSubagentGoesOnFromItsLog(t *testing.T) {
+ server := newModelFake(t)
+ server.reply = func(w http.ResponseWriter, call ModelFakeCall) {
+ text := "Noted."
+ if subagentTestCall(call) {
+ text = "Reply " + strconv.Itoa(call.number) + "."
+ }
+
+ modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem(text)}, 100, 0, 5))
+ }
+
+ test := newTestTurnLoop(t, server)
+ runs := test.loop.runs
+ effort := "high"
+ _, err := runs.Spawn(t.Context(), []string{"Count."}, &effort)
+ require.NoError(t, err)
+ test.events.WaitFor(t, "idle", 1)
+
+ name := test.events.Last("subagent")
+ output, err := runs.Tell(t.Context(), name, "And the hidden files?")
+ require.NoError(t, err)
+ require.Equal(t, "Sent. "+name+"'s reply will reach you as a message.", output)
+ test.events.WaitFor(t, "idle", 2)
+
+ call := server.Call(2)
+ require.True(t, subagentTestCall(call))
+ require.Equal(t, "high", call.body["reasoning"].(map[string]any)["effort"])
+ require.Equal(t, "0|note: subagent at effort high\n\n1|user: Count.\n\n2|reply: Reply 0.\n\n3|user: And the hidden files?", subagentTestText(subagentTestLastItem(call)))
+
+ // The view of the new run holds the chat of now, with the first report.
+ texts := turnInputTexts(t, call)
+ require.Contains(t, strings.Join(texts[:len(texts)-2], ""), "work: ["+name+"]")
+ require.Equal(t, "work: "+subagentReport(name, []string{"Reply 2."}), test.Log(t)[2])
+
+ _, err = runs.Tell(t.Context(), "BlueFox", "Hello.")
+ require.EqualError(t, err, "no agent BlueFox")
+}
+
func TestCloseEndsTheSubagentsWithNoReport(t *testing.T) {
server := newModelFake(t)
arrived := make(chan struct{})