+func TestViewMessagesOnlyGrowBetweenTurns(t *testing.T) {
+ server := newModelFake(t)
+ test := newTestTurnLoop(t, server)
+ for n, text := range []string{"one", "two", "three"} {
+ _, err := test.loop.Submit(terminalSource, text)
+ require.NoError(t, err)
+ test.events.WaitFor(t, "idle", n+1)
+ }
+
+ first := server.Call(1).body["input"].([]any)
+ second := server.Call(2).body["input"].([]any)
+
+ require.Len(t, first, 4)
+ require.Len(t, second, 5)
+ require.Equal(t, first[:2], second[:2])
+ require.Equal(t, []string{
+ "<chat>\n",
+ "0+1|user: one\n1+1|reply: a line\n",
+ "2+1|user: two\n3+1|reply: a line\n",
+ "</chat>\n\nNow: Thursday 2026-01-01 12:00 +00:00",
+ "three",
+ }, turnInputTexts(t, server.Call(2)))
+}
+
+// TurnTestLoop is a loop that runs in the background of a test, with its parts.
+type TurnTestLoop struct {
+ agent *Agent
+ memory *Memory
+ loop *TurnLoop
+ events *TurnEventRecorder
+ logs *SyncBuffer
+ usagePath string
+}
+
+// TurnEventRecorder keeps the events of a loop.
+type TurnEventRecorder struct {
+ mutex sync.Mutex
+ events []Event
+}
+
+func newTestTurnLoop(t *testing.T, server *ModelFake) *TurnTestLoop {
+ t.Helper()
+
+ return newTestTurnLoopWith(t, server, newFakeSummarizer())
+}
+
+// newTestTurnLoopWith returns a running loop on a new agent whose builds use
+// summarizer and whose commands run in a temporary directory.
+func newTestTurnLoopWith(t *testing.T, server *ModelFake, summarizer Summarizer) *TurnTestLoop {
+ t.Helper()
+
+ agent, err := openAgent(filepath.Join(t.TempDir(), "agent"))
+ require.NoError(t, err)
+
+ agent.config.Workdir = t.TempDir()
+ clock := newTestClock()
+ memory, err := openMemory(t.Context(), t.TempDir(), clock.Now, summarizer, discardLog())
+ require.NoError(t, err)
+
+ test := &TurnTestLoop{
+ agent: agent,
+ memory: memory,
+ events: &TurnEventRecorder{},
+ logs: &SyncBuffer{},
+ usagePath: filepath.Join(t.TempDir(), "usage.jsonl"),
+ }
+
+ client := newModelClient(server.URL, ModelAPIKey(modelTestKey), agent.CacheKey(), test.usagePath)
+ whatsapp, _, _ := whatsappStartTest(t, t.TempDir(), clock)
+ test.loop, err = newTurnLoop(agent, memory, newTestSubagents(t), whatsapp, newTestSchedules(t, clock.Now), client, clock.Now, daemonTestLogger(test.logs), test.events.Add)
+ require.NoError(t, err)
+
+ ctx, cancel := context.WithCancel(t.Context())
+ done := make(chan error)
+ go func() {
+ done <- test.loop.Run(ctx)
+ }()
+
+ t.Cleanup(func() {
+ cancel()
+ require.ErrorIs(t, <-done, context.Canceled)
+ require.NoError(t, memory.Close())
+ })
+
+ return test