Quellcode durchsuchen

test(acp-example): five snapshot scenarios + cancel/error input ops

Adds the first cut of snapshot scenarios, each asserting a normalized stdout
transcript golden and (for model turns) a re-persisted session-log golden:

- text-turn, tool-call-turn, multi-turn: RECORDED against the real API — the
  committed session.jsonl is a genuine harvested log; replay derives the model
  script from it and reproduces deterministically with no key. tool-call-turn
  exercises the real bash executor (echo SNAPSHOT_OK → tool/call + tool/result
  + a post-tool answer step).
- error-finish, cancel: AUTHORED via a replay.override.json sidecar (the live
  API can't be coaxed into a deterministic 401 or mid-stream cancel). error-
  finish replays a {kind:throw} 401 → the bridge answers the prompt with a
  JSON-RPC error and the log records turn/end{kind:error}; cancel replays a
  {kind:hang} → stopReason:cancelled.

Two input-DSL ops support these: promptExpectError (awaits the prompt, asserts
it rejects — the editor's view of a failed turn — and swallows it) and
promptAndCancel (dispatches the prompt unawaited, waits until the client
OBSERVES the streamed agent_message_chunk, then cancels — pinning frame order
so the cancel transcript is deterministic; fixes a flake Codex caught where the
late chunk and the cancelled response could interleave either way).

Scenarios carry a `recorded` flag so test:snapshot:record only re-runs the
live-API ones. reasoning/max-tokens scenarios are deferred (hard to force
deterministically from the live model). Per docs/rfc/implemented/2026-06-19.
Tianyi Cui vor 3 Monaten
Ursprung
Commit
c94f1563f5
24 geänderte Dateien mit 5173 neuen und 9 gelöschten Zeilen
  1. 22 7
      examples/acp-agent/tests/acp.snapshot.ts
  2. 54 2
      examples/acp-agent/tests/snapshot-harness.ts
  3. 7 0
      examples/acp-agent/tests/snapshots/cancel/input.json
  4. 3 0
      examples/acp-agent/tests/snapshots/cancel/replay.override.json
  5. 95 0
      examples/acp-agent/tests/snapshots/cancel/session.golden.txt
  6. 1 0
      examples/acp-agent/tests/snapshots/cancel/session.jsonl
  7. 62 0
      examples/acp-agent/tests/snapshots/cancel/stdout.golden.txt
  8. 7 0
      examples/acp-agent/tests/snapshots/error-finish/input.json
  9. 3 0
      examples/acp-agent/tests/snapshots/error-finish/replay.override.json
  10. 79 0
      examples/acp-agent/tests/snapshots/error-finish/session.golden.txt
  11. 1 0
      examples/acp-agent/tests/snapshots/error-finish/session.jsonl
  12. 49 0
      examples/acp-agent/tests/snapshots/error-finish/stdout.golden.txt
  13. 8 0
      examples/acp-agent/tests/snapshots/multi-turn/input.json
  14. 929 0
      examples/acp-agent/tests/snapshots/multi-turn/session.golden.txt
  15. 66 0
      examples/acp-agent/tests/snapshots/multi-turn/session.jsonl
  16. 615 0
      examples/acp-agent/tests/snapshots/multi-turn/stdout.golden.txt
  17. 7 0
      examples/acp-agent/tests/snapshots/text-turn/input.json
  18. 489 0
      examples/acp-agent/tests/snapshots/text-turn/session.golden.txt
  19. 35 0
      examples/acp-agent/tests/snapshots/text-turn/session.jsonl
  20. 342 0
      examples/acp-agent/tests/snapshots/text-turn/stdout.golden.txt
  21. 7 0
      examples/acp-agent/tests/snapshots/tool-call-turn/input.json
  22. 1469 0
      examples/acp-agent/tests/snapshots/tool-call-turn/session.golden.txt
  23. 100 0
      examples/acp-agent/tests/snapshots/tool-call-turn/session.jsonl
  24. 723 0
      examples/acp-agent/tests/snapshots/tool-call-turn/stdout.golden.txt

+ 22 - 7
examples/acp-agent/tests/acp.snapshot.ts

@@ -20,20 +20,35 @@ import { type NormalizeContext, normalizeSessionLog, normalizeStdout } from './s
 const SNAPSHOTS_DIR = join(dirname(fileURLToPath(import.meta.url)), 'snapshots')
 const RECORDING = process.env.DSH_SNAPSHOT === 'record'
 
-/** A scenario and whether it makes any model call (→ has a behavioral JSONL golden). */
+/** A snapshot scenario and how its fixtures are produced. */
 interface Scenario {
   name: string
   /** Whether the scenario drives at least one model turn (so a JSONL golden applies). */
   hasModelTurn: boolean
+  /**
+   * Whether `test:snapshot:record` regenerates this scenario's `session.jsonl`
+   * from the LIVE API. `recorded` scenarios are model-driven and reproducible;
+   * `authored` scenarios (a hand-written `replay.override.json` sidecar drives
+   * replay — e.g. a provider error or a cancel, which the live API can't be
+   * coaxed into deterministically) are NEVER re-recorded.
+   */
+  recorded: boolean
 }
 
 const SCENARIOS: Scenario[] = [
-  { name: 'handshake', hasModelTurn: false },
+  { name: 'handshake', hasModelTurn: false, recorded: false },
+  { name: 'text-turn', hasModelTurn: true, recorded: true },
+  { name: 'tool-call-turn', hasModelTurn: true, recorded: true },
+  { name: 'multi-turn', hasModelTurn: true, recorded: true },
+  { name: 'error-finish', hasModelTurn: true, recorded: false },
+  { name: 'cancel', hasModelTurn: true, recorded: false },
 ]
 
 for (const scenario of SCENARIOS) {
   describe(`snapshot: ${scenario.name}`, () => {
-    it('matches the stdout transcript golden', async () => {
+    // In RECORD mode, only re-run the `recorded` (live-API) scenarios; the
+    // `authored` ones (sidecar-driven errors/cancel) are never re-recorded.
+    it.skipIf(RECORDING && !scenario.recorded)('matches the goldens', async () => {
       const dir = join(SNAPSHOTS_DIR, scenario.name)
       const input = JSON.parse(await readFile(join(dir, 'input.json'), 'utf8')) as InputScript
       const overrideFile = join(dir, 'replay.override.json')
@@ -48,10 +63,10 @@ for (const scenario of SCENARIOS) {
         cwd: result.cwd,
       }
 
-      // RECORD mode: persist the freshly-harvested log back to the scenario's
-      // session.jsonl fixture (a model scenario must produce one). `--update`
-      // refreshes the Vitest goldens but NOT this fixture, so write it here.
-      if (RECORDING && scenario.hasModelTurn) {
+      // RECORD mode (recorded scenarios only): persist the freshly-harvested log
+      // back to the scenario's session.jsonl fixture. `--update` refreshes the
+      // Vitest goldens but NOT this fixture, so write it here.
+      if (RECORDING && scenario.recorded && scenario.hasModelTurn) {
         expect(result.sessionLog, 'record produced no session log to harvest').toBeDefined()
         await writeFile(join(dir, 'session.jsonl'), result.sessionLog as string)
       }

+ 54 - 2
examples/acp-agent/tests/snapshot-harness.ts

@@ -44,11 +44,19 @@ const repoTsconfig = fileURLToPath(new URL('../../../tsconfig.json', import.meta
  * harness interprets these in order. `newSession` captures the server-issued
  * (random) session id into a `{{sessionId}}` variable that later steps
  * reference, since a committed file cannot know the id in advance.
+ *
+ * `promptAndCancel` sends a prompt WITHOUT awaiting its response, waits until
+ * the client observes the first streamed `agent_message_chunk` (so the emitted
+ * frames deterministically precede the cancellation), then cancels the turn —
+ * the only way to exercise a cancel deterministically (a plain `prompt` step
+ * awaits the response, which a cancel/hang scenario would block on forever).
  */
 type InputStep =
   | { op: 'initialize'; terminalOutput?: boolean }
   | { op: 'newSession' }
   | { op: 'prompt'; text: string }
+  | { op: 'promptExpectError'; text: string }
+  | { op: 'promptAndCancel'; text: string }
   | { op: 'cancel' }
 
 /** A scenario's `input.json`: an ordered list of input steps. */
@@ -122,8 +130,23 @@ export async function runScenario(input: InputScript, opts: RunOptions): Promise
     Writable.toWeb(child.stdin) as WritableStream<Uint8Array>,
     Readable.toWeb(passthrough) as ReadableStream<Uint8Array>,
   )
+  // Watcher so a step can block until the client OBSERVES a particular
+  // session/update — used by promptAndCancel to pin frame order (send cancel
+  // only after the streamed agent_message_chunk has arrived, so those frames
+  // deterministically precede the cancelled prompt response).
+  const updateWaiters: { match: (u: SessionNotification['update']) => boolean; resolve: () => void }[] = []
+  const waitForUpdate = (match: (u: SessionNotification['update']) => boolean): Promise<void> =>
+    new Promise<void>(resolve => updateWaiters.push({ match, resolve }))
+
   const makeClient = (_agent: AcpAgent): Client => ({
-    sessionUpdate(_params: SessionNotification): Promise<void> {
+    sessionUpdate(params: SessionNotification): Promise<void> {
+      for (let i = updateWaiters.length - 1; i >= 0; i--) {
+        const waiter = updateWaiters[i]
+        if (waiter !== undefined && waiter.match(params.update)) {
+          updateWaiters.splice(i, 1)
+          waiter.resolve()
+        }
+      }
       return Promise.resolve()
     },
     requestPermission(_params: RequestPermissionRequest): Promise<RequestPermissionResponse> {
@@ -136,7 +159,7 @@ export async function runScenario(input: InputScript, opts: RunOptions): Promise
   let sessionLog: string | undefined
   try {
     for (const step of input.steps) {
-      await runStep(client, step, cwd, () => sessionId, (id) => { sessionId = id })
+      await runStep(client, step, cwd, waitForUpdate, () => sessionId, (id) => { sessionId = id })
     }
     // Done driving: close stdin so the server disposes gracefully (flushing
     // persistence) and exits. Then await exit so the harvested log is complete.
@@ -170,6 +193,7 @@ async function runStep(
   client: ClientSideConnection,
   step: InputStep,
   cwd: string,
+  waitForUpdate: (match: (u: SessionNotification['update']) => boolean) => Promise<void>,
   getSessionId: () => string | undefined,
   setSessionId: (id: string) => void,
 ): Promise<void> {
@@ -191,6 +215,34 @@ async function runStep(
       await client.prompt({ sessionId, prompt: [{ type: 'text', text: step.text }] })
       return
     }
+    case 'promptExpectError': {
+      const sessionId = getSessionId()
+      if (sessionId === undefined) throw new Error('snapshot-harness: promptExpectError before newSession')
+      // The model fails this turn (a recorded provider error), so the bridge
+      // answers the prompt with a JSON-RPC error and the SDK rejects. That
+      // rejection IS the expected editor experience — swallow it so the run
+      // completes and the stdout transcript (the error frame) is captured.
+      await client.prompt({ sessionId, prompt: [{ type: 'text', text: step.text }] })
+        .then(() => { throw new Error('snapshot-harness: expected the prompt to fail but it succeeded') },
+          () => { /* expected: the turn failed and the bridge returned an error */ })
+      return
+    }
+    case 'promptAndCancel': {
+      const sessionId = getSessionId()
+      if (sessionId === undefined) throw new Error('snapshot-harness: promptAndCancel before newSession')
+      // Dispatch the prompt WITHOUT awaiting (a hang fixture never resolves on
+      // its own). To pin frame order deterministically, wait until the client
+      // has OBSERVED the hang's streamed agent_message_chunk before cancelling —
+      // so those update frames always precede the cancelled prompt response in
+      // the transcript (without this, the late chunk and the response race; see
+      // the Codex review of commit 5). Then cancel and await the prompt, which
+      // the bridge settles as `cancelled` once the abort propagates.
+      const promptDone = client.prompt({ sessionId, prompt: [{ type: 'text', text: step.text }] })
+      await waitForUpdate(u => u.sessionUpdate === 'agent_message_chunk')
+      await client.cancel({ sessionId })
+      await promptDone
+      return
+    }
     case 'cancel': {
       const sessionId = getSessionId()
       if (sessionId === undefined) throw new Error('snapshot-harness: cancel before newSession')

+ 7 - 0
examples/acp-agent/tests/snapshots/cancel/input.json

@@ -0,0 +1,7 @@
+{
+  "steps": [
+    { "op": "initialize" },
+    { "op": "newSession" },
+    { "op": "promptAndCancel", "text": "Start a long task; this turn will be cancelled mid-stream." }
+  ]
+}

+ 3 - 0
examples/acp-agent/tests/snapshots/cancel/replay.override.json

@@ -0,0 +1,3 @@
+[
+  { "kind": "hang" }
+]

+ 95 - 0
examples/acp-agent/tests/snapshots/cancel/session.golden.txt

@@ -0,0 +1,95 @@
+{
+  "type": "session",
+  "version": 1,
+  "id": "{{sessionId}}",
+  "createdAt": 0,
+  "cwd": "{{cwd}}"
+}
+{
+  "type": "turn/start",
+  "seq": 0,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "trigger": {
+      "kind": "message",
+      "source": {
+        "kind": "user"
+      }
+    }
+  }
+}
+{
+  "type": "user/message",
+  "seq": 1,
+  "time": 0,
+  "data": {
+    "content": [
+      {
+        "type": "text",
+        "text": "Start a long task; this turn will be cancelled mid-stream."
+      }
+    ],
+    "source": {
+      "kind": "user"
+    }
+  }
+}
+{
+  "type": "step/start",
+  "seq": 2,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 3,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 0,
+      "blockType": "text"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 4,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "text-delta",
+      "index": 0,
+      "text": "partial"
+    }
+  }
+}
+{
+  "type": "step/end",
+  "seq": 5,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "turn/end",
+  "seq": 6,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "reason": {
+      "kind": "aborted",
+      "reason": "session/cancel"
+    }
+  }
+}

+ 1 - 0
examples/acp-agent/tests/snapshots/cancel/session.jsonl

@@ -0,0 +1 @@
+{"type":"session","version":1,"id":"00000000-0000-0000-0000-000000000000","createdAt":0}

+ 62 - 0
examples/acp-agent/tests/snapshots/cancel/stdout.golden.txt

@@ -0,0 +1,62 @@
+{
+  "jsonrpc": "2.0",
+  "id": 1,
+  "result": {
+    "protocolVersion": 1,
+    "agentInfo": {
+      "name": "deepseek-harness-acp",
+      "version": "0.0.1"
+    },
+    "agentCapabilities": {
+      "loadSession": true,
+      "promptCapabilities": {
+        "image": false,
+        "audio": false,
+        "embeddedContext": false
+      }
+    },
+    "authMethods": []
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 2,
+  "result": {
+    "sessionId": "{{sessionId}}"
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "user_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "Start a long task; this turn will be cancelled mid-stream."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "partial"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 3,
+  "result": {
+    "stopReason": "cancelled"
+  }
+}

+ 7 - 0
examples/acp-agent/tests/snapshots/error-finish/input.json

@@ -0,0 +1,7 @@
+{
+  "steps": [
+    { "op": "initialize" },
+    { "op": "newSession" },
+    { "op": "promptExpectError", "text": "This prompt triggers a recorded provider error." }
+  ]
+}

+ 3 - 0
examples/acp-agent/tests/snapshots/error-finish/replay.override.json

@@ -0,0 +1,3 @@
+[
+  { "kind": "throw", "chunks": [], "message": "simulated provider error (HTTP 401)", "code": "AUTH", "status": 401 }
+]

+ 79 - 0
examples/acp-agent/tests/snapshots/error-finish/session.golden.txt

@@ -0,0 +1,79 @@
+{
+  "type": "session",
+  "version": 1,
+  "id": "{{sessionId}}",
+  "createdAt": 0,
+  "cwd": "{{cwd}}"
+}
+{
+  "type": "turn/start",
+  "seq": 0,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "trigger": {
+      "kind": "message",
+      "source": {
+        "kind": "user"
+      }
+    }
+  }
+}
+{
+  "type": "user/message",
+  "seq": 1,
+  "time": 0,
+  "data": {
+    "content": [
+      {
+        "type": "text",
+        "text": "This prompt triggers a recorded provider error."
+      }
+    ],
+    "source": {
+      "kind": "user"
+    }
+  }
+}
+{
+  "type": "step/start",
+  "seq": 2,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "step/end",
+  "seq": 3,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "error",
+  "seq": 4,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "message": "simulated provider error (HTTP 401)",
+    "code": "AUTH"
+  }
+}
+{
+  "type": "turn/end",
+  "seq": 5,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "reason": {
+      "kind": "error",
+      "message": "simulated provider error (HTTP 401)",
+      "code": "AUTH"
+    }
+  }
+}

+ 1 - 0
examples/acp-agent/tests/snapshots/error-finish/session.jsonl

@@ -0,0 +1 @@
+{"type":"session","version":1,"id":"00000000-0000-0000-0000-000000000000","createdAt":0}

+ 49 - 0
examples/acp-agent/tests/snapshots/error-finish/stdout.golden.txt

@@ -0,0 +1,49 @@
+{
+  "jsonrpc": "2.0",
+  "id": 1,
+  "result": {
+    "protocolVersion": 1,
+    "agentInfo": {
+      "name": "deepseek-harness-acp",
+      "version": "0.0.1"
+    },
+    "agentCapabilities": {
+      "loadSession": true,
+      "promptCapabilities": {
+        "image": false,
+        "audio": false,
+        "embeddedContext": false
+      }
+    },
+    "authMethods": []
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 2,
+  "result": {
+    "sessionId": "{{sessionId}}"
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "user_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "This prompt triggers a recorded provider error."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 3,
+  "error": {
+    "code": -32603,
+    "message": "Internal error: turn failed: simulated provider error (HTTP 401)"
+  }
+}

+ 8 - 0
examples/acp-agent/tests/snapshots/multi-turn/input.json

@@ -0,0 +1,8 @@
+{
+  "steps": [
+    { "op": "initialize" },
+    { "op": "newSession" },
+    { "op": "prompt", "text": "Reply with exactly the word: ONE. No tools." },
+    { "op": "prompt", "text": "Reply with exactly the word: TWO. No tools." }
+  ]
+}

+ 929 - 0
examples/acp-agent/tests/snapshots/multi-turn/session.golden.txt

@@ -0,0 +1,929 @@
+{
+  "type": "session",
+  "version": 1,
+  "id": "{{sessionId}}",
+  "createdAt": 0,
+  "cwd": "{{cwd}}"
+}
+{
+  "type": "turn/start",
+  "seq": 0,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "trigger": {
+      "kind": "message",
+      "source": {
+        "kind": "user"
+      }
+    }
+  }
+}
+{
+  "type": "user/message",
+  "seq": 1,
+  "time": 0,
+  "data": {
+    "content": [
+      {
+        "type": "text",
+        "text": "Reply with exactly the word: ONE. No tools."
+      }
+    ],
+    "source": {
+      "kind": "user"
+    }
+  }
+}
+{
+  "type": "step/start",
+  "seq": 2,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 3,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 0,
+      "blockType": "reasoning"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 4,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "The"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 5,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " user"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 6,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " wants"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 7,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " me"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 8,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " to"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 9,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " reply"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 10,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " with"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 11,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " exactly"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 12,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " the"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 13,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " word"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 14,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " \""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 15,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "ONE"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 16,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 17,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " and"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 18,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " no"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 19,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " tools"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 20,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "."
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 21,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 1,
+      "blockType": "text"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 22,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "text-delta",
+      "index": 1,
+      "text": "ONE"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 23,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 0,
+      "block": {
+        "type": "reasoning",
+        "text": "The user wants me to reply with exactly the word \"ONE\" and no tools."
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 24,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 1,
+      "block": {
+        "type": "text",
+        "text": "ONE"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 25,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "usage",
+      "usage": {
+        "inputTokens": 113,
+        "outputTokens": 19,
+        "cacheReadTokens": 768,
+        "reasoningTokens": 17
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 26,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "finish",
+      "reason": {
+        "kind": "stop"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/message",
+  "seq": 27,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "content": [
+      {
+        "type": "reasoning",
+        "text": "The user wants me to reply with exactly the word \"ONE\" and no tools."
+      },
+      {
+        "type": "text",
+        "text": "ONE"
+      }
+    ]
+  }
+}
+{
+  "type": "usage",
+  "seq": 28,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "usage": {
+      "inputTokens": 113,
+      "outputTokens": 19,
+      "cacheReadTokens": 768,
+      "reasoningTokens": 17
+    }
+  }
+}
+{
+  "type": "step/end",
+  "seq": 29,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "turn/end",
+  "seq": 30,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "reason": {
+      "kind": "completed"
+    }
+  }
+}
+{
+  "type": "turn/start",
+  "seq": 31,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "trigger": {
+      "kind": "message",
+      "source": {
+        "kind": "user"
+      }
+    }
+  }
+}
+{
+  "type": "user/message",
+  "seq": 32,
+  "time": 0,
+  "data": {
+    "content": [
+      {
+        "type": "text",
+        "text": "Reply with exactly the word: TWO. No tools."
+      }
+    ],
+    "source": {
+      "kind": "user"
+    }
+  }
+}
+{
+  "type": "step/start",
+  "seq": 33,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 34,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 0,
+      "blockType": "reasoning"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 35,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "The"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 36,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " user"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 37,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " wants"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 38,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " me"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 39,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " to"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 40,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " reply"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 41,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " with"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 42,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " exactly"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 43,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " the"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 44,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " word"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 45,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " \""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 46,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "T"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 47,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "WO"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 48,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 49,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " and"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 50,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " use"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 51,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " no"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 52,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " tools"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 53,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "."
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 54,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 1,
+      "blockType": "text"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 55,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "text-delta",
+      "index": 1,
+      "text": "T"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 56,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "text-delta",
+      "index": 1,
+      "text": "WO"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 57,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 0,
+      "block": {
+        "type": "reasoning",
+        "text": "The user wants me to reply with exactly the word \"TWO\" and use no tools."
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 58,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 1,
+      "block": {
+        "type": "text",
+        "text": "TWO"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 59,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "usage",
+      "usage": {
+        "inputTokens": 129,
+        "outputTokens": 22,
+        "cacheReadTokens": 768,
+        "reasoningTokens": 19
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 60,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "chunk": {
+      "type": "finish",
+      "reason": {
+        "kind": "stop"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/message",
+  "seq": 61,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "content": [
+      {
+        "type": "reasoning",
+        "text": "The user wants me to reply with exactly the word \"TWO\" and use no tools."
+      },
+      {
+        "type": "text",
+        "text": "TWO"
+      }
+    ]
+  }
+}
+{
+  "type": "usage",
+  "seq": 62,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1,
+    "usage": {
+      "inputTokens": 129,
+      "outputTokens": 22,
+      "cacheReadTokens": 768,
+      "reasoningTokens": 19
+    }
+  }
+}
+{
+  "type": "step/end",
+  "seq": 63,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "step": 1
+  }
+}
+{
+  "type": "turn/end",
+  "seq": 64,
+  "time": 0,
+  "data": {
+    "turn": 2,
+    "reason": {
+      "kind": "completed"
+    }
+  }
+}

+ 66 - 0
examples/acp-agent/tests/snapshots/multi-turn/session.jsonl

@@ -0,0 +1,66 @@
+{"type":"session","version":1,"id":"c4893a53-19cb-4c09-81d2-e702a13c5123","createdAt":1781811971684,"cwd":"/tmp/acp-snap-cwd-zBZiqs"}
+{"type":"turn/start","seq":0,"time":1781811971687,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
+{"type":"user/message","seq":1,"time":1781811971688,"data":{"content":[{"type":"text","text":"Reply with exactly the word: ONE. No tools."}],"source":{"kind":"user"}}}
+{"type":"step/start","seq":2,"time":1781811971688,"data":{"turn":1,"step":1}}
+{"type":"assistant/chunk","seq":3,"time":1781811972281,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
+{"type":"assistant/chunk","seq":4,"time":1781811972281,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
+{"type":"assistant/chunk","seq":5,"time":1781811972455,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
+{"type":"assistant/chunk","seq":6,"time":1781811972501,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
+{"type":"assistant/chunk","seq":7,"time":1781811972502,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
+{"type":"assistant/chunk","seq":8,"time":1781811972502,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
+{"type":"assistant/chunk","seq":9,"time":1781811972503,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
+{"type":"assistant/chunk","seq":10,"time":1781811972503,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
+{"type":"assistant/chunk","seq":11,"time":1781811972523,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
+{"type":"assistant/chunk","seq":12,"time":1781811972523,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
+{"type":"assistant/chunk","seq":13,"time":1781811972523,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
+{"type":"assistant/chunk","seq":14,"time":1781811972523,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
+{"type":"assistant/chunk","seq":15,"time":1781811972524,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
+{"type":"assistant/chunk","seq":16,"time":1781811972524,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
+{"type":"assistant/chunk","seq":17,"time":1781811972557,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
+{"type":"assistant/chunk","seq":18,"time":1781811972558,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" no"}}}
+{"type":"assistant/chunk","seq":19,"time":1781811972558,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
+{"type":"assistant/chunk","seq":20,"time":1781811972558,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
+{"type":"assistant/chunk","seq":21,"time":1781811972594,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
+{"type":"assistant/chunk","seq":22,"time":1781811972595,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
+{"type":"assistant/chunk","seq":23,"time":1781811972595,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ONE\" and no tools."}}}}
+{"type":"assistant/chunk","seq":24,"time":1781811972595,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ONE"}}}}
+{"type":"assistant/chunk","seq":25,"time":1781811972595,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":113,"outputTokens":19,"cacheReadTokens":768,"reasoningTokens":17}}}}
+{"type":"assistant/chunk","seq":26,"time":1781811972595,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
+{"type":"assistant/message","seq":27,"time":1781811972597,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ONE\" and no tools."},{"type":"text","text":"ONE"}]}}
+{"type":"usage","seq":28,"time":1781811972597,"data":{"turn":1,"step":1,"usage":{"inputTokens":113,"outputTokens":19,"cacheReadTokens":768,"reasoningTokens":17}}}
+{"type":"step/end","seq":29,"time":1781811972597,"data":{"turn":1,"step":1}}
+{"type":"turn/end","seq":30,"time":1781811972597,"data":{"turn":1,"reason":{"kind":"completed"}}}
+{"type":"turn/start","seq":31,"time":1781811972603,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
+{"type":"user/message","seq":32,"time":1781811972604,"data":{"content":[{"type":"text","text":"Reply with exactly the word: TWO. No tools."}],"source":{"kind":"user"}}}
+{"type":"step/start","seq":33,"time":1781811972604,"data":{"turn":2,"step":1}}
+{"type":"assistant/chunk","seq":34,"time":1781811973140,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
+{"type":"assistant/chunk","seq":35,"time":1781811973140,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
+{"type":"assistant/chunk","seq":36,"time":1781811973337,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
+{"type":"assistant/chunk","seq":37,"time":1781811973371,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
+{"type":"assistant/chunk","seq":38,"time":1781811973371,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
+{"type":"assistant/chunk","seq":39,"time":1781811973371,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
+{"type":"assistant/chunk","seq":40,"time":1781811973372,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
+{"type":"assistant/chunk","seq":41,"time":1781811973405,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
+{"type":"assistant/chunk","seq":42,"time":1781811973406,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
+{"type":"assistant/chunk","seq":43,"time":1781811973406,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
+{"type":"assistant/chunk","seq":44,"time":1781811973406,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
+{"type":"assistant/chunk","seq":45,"time":1781811973406,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
+{"type":"assistant/chunk","seq":46,"time":1781811973406,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"T"}}}
+{"type":"assistant/chunk","seq":47,"time":1781811973439,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"WO"}}}
+{"type":"assistant/chunk","seq":48,"time":1781811973439,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
+{"type":"assistant/chunk","seq":49,"time":1781811973473,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
+{"type":"assistant/chunk","seq":50,"time":1781811973473,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" use"}}}
+{"type":"assistant/chunk","seq":51,"time":1781811973507,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" no"}}}
+{"type":"assistant/chunk","seq":52,"time":1781811973507,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
+{"type":"assistant/chunk","seq":53,"time":1781811973507,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
+{"type":"assistant/chunk","seq":54,"time":1781811973507,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
+{"type":"assistant/chunk","seq":55,"time":1781811973508,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"T"}}}
+{"type":"assistant/chunk","seq":56,"time":1781811973542,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"WO"}}}
+{"type":"assistant/chunk","seq":57,"time":1781811973543,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"TWO\" and use no tools."}}}}
+{"type":"assistant/chunk","seq":58,"time":1781811973543,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"TWO"}}}}
+{"type":"assistant/chunk","seq":59,"time":1781811973543,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":129,"outputTokens":22,"cacheReadTokens":768,"reasoningTokens":19}}}}
+{"type":"assistant/chunk","seq":60,"time":1781811973543,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
+{"type":"assistant/message","seq":61,"time":1781811973543,"data":{"turn":2,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"TWO\" and use no tools."},{"type":"text","text":"TWO"}]}}
+{"type":"usage","seq":62,"time":1781811973543,"data":{"turn":2,"step":1,"usage":{"inputTokens":129,"outputTokens":22,"cacheReadTokens":768,"reasoningTokens":19}}}
+{"type":"step/end","seq":63,"time":1781811973543,"data":{"turn":2,"step":1}}
+{"type":"turn/end","seq":64,"time":1781811973543,"data":{"turn":2,"reason":{"kind":"completed"}}}

+ 615 - 0
examples/acp-agent/tests/snapshots/multi-turn/stdout.golden.txt

@@ -0,0 +1,615 @@
+{
+  "jsonrpc": "2.0",
+  "id": 1,
+  "result": {
+    "protocolVersion": 1,
+    "agentInfo": {
+      "name": "deepseek-harness-acp",
+      "version": "0.0.1"
+    },
+    "agentCapabilities": {
+      "loadSession": true,
+      "promptCapabilities": {
+        "image": false,
+        "audio": false,
+        "embeddedContext": false
+      }
+    },
+    "authMethods": []
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 2,
+  "result": {
+    "sessionId": "{{sessionId}}"
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "user_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "Reply with exactly the word: ONE. No tools."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "The"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " user"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " wants"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " me"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " to"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " reply"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " with"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " exactly"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " the"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " word"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " \""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "ONE"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "\""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " and"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " no"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " tools"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "ONE"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 3,
+  "result": {
+    "stopReason": "end_turn"
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "user_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "Reply with exactly the word: TWO. No tools."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "The"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " user"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " wants"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " me"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " to"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " reply"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " with"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " exactly"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " the"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " word"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " \""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "T"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "WO"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "\""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " and"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " use"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " no"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " tools"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "T"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "WO"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 4,
+  "result": {
+    "stopReason": "end_turn"
+  }
+}

+ 7 - 0
examples/acp-agent/tests/snapshots/text-turn/input.json

@@ -0,0 +1,7 @@
+{
+  "steps": [
+    { "op": "initialize" },
+    { "op": "newSession" },
+    { "op": "prompt", "text": "Reply with exactly the word: PONG. Do not use any tools." }
+  ]
+}

+ 489 - 0
examples/acp-agent/tests/snapshots/text-turn/session.golden.txt

@@ -0,0 +1,489 @@
+{
+  "type": "session",
+  "version": 1,
+  "id": "{{sessionId}}",
+  "createdAt": 0,
+  "cwd": "{{cwd}}"
+}
+{
+  "type": "turn/start",
+  "seq": 0,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "trigger": {
+      "kind": "message",
+      "source": {
+        "kind": "user"
+      }
+    }
+  }
+}
+{
+  "type": "user/message",
+  "seq": 1,
+  "time": 0,
+  "data": {
+    "content": [
+      {
+        "type": "text",
+        "text": "Reply with exactly the word: PONG. Do not use any tools."
+      }
+    ],
+    "source": {
+      "kind": "user"
+    }
+  }
+}
+{
+  "type": "step/start",
+  "seq": 2,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 3,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 0,
+      "blockType": "reasoning"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 4,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "The"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 5,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " user"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 6,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " wants"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 7,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " me"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 8,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " to"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 9,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " reply"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 10,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " with"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 11,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " exactly"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 12,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " the"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 13,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " word"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 14,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " \""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 15,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "P"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 16,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "ONG"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 17,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 18,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " without"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 19,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " using"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 20,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " any"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 21,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " tools"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 22,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "."
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 23,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 1,
+      "blockType": "text"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 24,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "text-delta",
+      "index": 1,
+      "text": "P"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 25,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "text-delta",
+      "index": 1,
+      "text": "ONG"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 26,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 0,
+      "block": {
+        "type": "reasoning",
+        "text": "The user wants me to reply with exactly the word \"PONG\" without using any tools."
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 27,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 1,
+      "block": {
+        "type": "text",
+        "text": "PONG"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 28,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "usage",
+      "usage": {
+        "inputTokens": 117,
+        "outputTokens": 22,
+        "cacheReadTokens": 768,
+        "reasoningTokens": 19
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 29,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "finish",
+      "reason": {
+        "kind": "stop"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/message",
+  "seq": 30,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "content": [
+      {
+        "type": "reasoning",
+        "text": "The user wants me to reply with exactly the word \"PONG\" without using any tools."
+      },
+      {
+        "type": "text",
+        "text": "PONG"
+      }
+    ]
+  }
+}
+{
+  "type": "usage",
+  "seq": 31,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "usage": {
+      "inputTokens": 117,
+      "outputTokens": 22,
+      "cacheReadTokens": 768,
+      "reasoningTokens": 19
+    }
+  }
+}
+{
+  "type": "step/end",
+  "seq": 32,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "turn/end",
+  "seq": 33,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "reason": {
+      "kind": "completed"
+    }
+  }
+}

+ 35 - 0
examples/acp-agent/tests/snapshots/text-turn/session.jsonl

@@ -0,0 +1,35 @@
+{"type":"session","version":1,"id":"dc42ad0b-4188-44c6-8d56-fd6f9cabe44f","createdAt":1781811940516,"cwd":"/tmp/acp-snap-cwd-9lipaP"}
+{"type":"turn/start","seq":0,"time":1781811940519,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
+{"type":"user/message","seq":1,"time":1781811940519,"data":{"content":[{"type":"text","text":"Reply with exactly the word: PONG. Do not use any tools."}],"source":{"kind":"user"}}}
+{"type":"step/start","seq":2,"time":1781811940520,"data":{"turn":1,"step":1}}
+{"type":"assistant/chunk","seq":3,"time":1781811940960,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
+{"type":"assistant/chunk","seq":4,"time":1781811940960,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
+{"type":"assistant/chunk","seq":5,"time":1781811941102,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
+{"type":"assistant/chunk","seq":6,"time":1781811941133,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
+{"type":"assistant/chunk","seq":7,"time":1781811941134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
+{"type":"assistant/chunk","seq":8,"time":1781811941134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
+{"type":"assistant/chunk","seq":9,"time":1781811941134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
+{"type":"assistant/chunk","seq":10,"time":1781811941134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
+{"type":"assistant/chunk","seq":11,"time":1781811941134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
+{"type":"assistant/chunk","seq":12,"time":1781811941166,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
+{"type":"assistant/chunk","seq":13,"time":1781811941167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
+{"type":"assistant/chunk","seq":14,"time":1781811941167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
+{"type":"assistant/chunk","seq":15,"time":1781811941167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"P"}}}
+{"type":"assistant/chunk","seq":16,"time":1781811941167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONG"}}}
+{"type":"assistant/chunk","seq":17,"time":1781811941167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
+{"type":"assistant/chunk","seq":18,"time":1781811941199,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" without"}}}
+{"type":"assistant/chunk","seq":19,"time":1781811941200,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" using"}}}
+{"type":"assistant/chunk","seq":20,"time":1781811941200,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" any"}}}
+{"type":"assistant/chunk","seq":21,"time":1781811941200,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
+{"type":"assistant/chunk","seq":22,"time":1781811941200,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
+{"type":"assistant/chunk","seq":23,"time":1781811941233,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
+{"type":"assistant/chunk","seq":24,"time":1781811941233,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"P"}}}
+{"type":"assistant/chunk","seq":25,"time":1781811941234,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONG"}}}
+{"type":"assistant/chunk","seq":26,"time":1781811941234,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"PONG\" without using any tools."}}}}
+{"type":"assistant/chunk","seq":27,"time":1781811941234,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"PONG"}}}}
+{"type":"assistant/chunk","seq":28,"time":1781811941234,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":117,"outputTokens":22,"cacheReadTokens":768,"reasoningTokens":19}}}}
+{"type":"assistant/chunk","seq":29,"time":1781811941234,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
+{"type":"assistant/message","seq":30,"time":1781811941236,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"PONG\" without using any tools."},{"type":"text","text":"PONG"}]}}
+{"type":"usage","seq":31,"time":1781811941236,"data":{"turn":1,"step":1,"usage":{"inputTokens":117,"outputTokens":22,"cacheReadTokens":768,"reasoningTokens":19}}}
+{"type":"step/end","seq":32,"time":1781811941236,"data":{"turn":1,"step":1}}
+{"type":"turn/end","seq":33,"time":1781811941236,"data":{"turn":1,"reason":{"kind":"completed"}}}

+ 342 - 0
examples/acp-agent/tests/snapshots/text-turn/stdout.golden.txt

@@ -0,0 +1,342 @@
+{
+  "jsonrpc": "2.0",
+  "id": 1,
+  "result": {
+    "protocolVersion": 1,
+    "agentInfo": {
+      "name": "deepseek-harness-acp",
+      "version": "0.0.1"
+    },
+    "agentCapabilities": {
+      "loadSession": true,
+      "promptCapabilities": {
+        "image": false,
+        "audio": false,
+        "embeddedContext": false
+      }
+    },
+    "authMethods": []
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 2,
+  "result": {
+    "sessionId": "{{sessionId}}"
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "user_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "Reply with exactly the word: PONG. Do not use any tools."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "The"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " user"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " wants"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " me"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " to"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " reply"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " with"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " exactly"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " the"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " word"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " \""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "P"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "ONG"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "\""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " without"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " using"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " any"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " tools"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "P"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "ONG"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 3,
+  "result": {
+    "stopReason": "end_turn"
+  }
+}

+ 7 - 0
examples/acp-agent/tests/snapshots/tool-call-turn/input.json

@@ -0,0 +1,7 @@
+{
+  "steps": [
+    { "op": "initialize" },
+    { "op": "newSession" },
+    { "op": "prompt", "text": "Use the bash tool to run exactly: echo SNAPSHOT_OK. Then reply with the single word DONE and stop." }
+  ]
+}

+ 1469 - 0
examples/acp-agent/tests/snapshots/tool-call-turn/session.golden.txt

@@ -0,0 +1,1469 @@
+{
+  "type": "session",
+  "version": 1,
+  "id": "{{sessionId}}",
+  "createdAt": 0,
+  "cwd": "{{cwd}}"
+}
+{
+  "type": "turn/start",
+  "seq": 0,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "trigger": {
+      "kind": "message",
+      "source": {
+        "kind": "user"
+      }
+    }
+  }
+}
+{
+  "type": "user/message",
+  "seq": 1,
+  "time": 0,
+  "data": {
+    "content": [
+      {
+        "type": "text",
+        "text": "Use the bash tool to run exactly: echo SNAPSHOT_OK. Then reply with the single word DONE and stop."
+      }
+    ],
+    "source": {
+      "kind": "user"
+    }
+  }
+}
+{
+  "type": "step/start",
+  "seq": 2,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 3,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 0,
+      "blockType": "reasoning"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 4,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "The"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 5,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " user"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 6,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " wants"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 7,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " me"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 8,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " to"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 9,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " run"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 10,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " a"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 11,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " specific"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 12,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " command"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 13,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " and"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 14,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " then"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 15,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " reply"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 16,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " with"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 17,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " D"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 18,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "ONE"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 19,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "."
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 20,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-start",
+      "index": 1,
+      "blockType": "tool-call"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 21,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": ""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 22,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "{"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 23,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 24,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "command"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 25,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 26,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": ": "
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 27,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 28,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "echo"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 29,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": " S"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 30,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "NA"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 31,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "PS"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 32,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "H"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 33,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "OT"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 34,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "_OK"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 35,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 36,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": ", "
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 37,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 38,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "description"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 39,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 40,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": ": "
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 41,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 42,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "Run"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 43,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": " the"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 44,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": " exact"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 45,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": " echo"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 46,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": " command"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 47,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": " requested"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 48,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "\""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 49,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "tool-call-delta",
+      "index": 1,
+      "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "name": "bash",
+      "argumentsDelta": "}"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 50,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 0,
+      "block": {
+        "type": "reasoning",
+        "text": "The user wants me to run a specific command and then reply with DONE."
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 51,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "block-end",
+      "index": 1,
+      "block": {
+        "type": "tool-call",
+        "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+        "name": "bash",
+        "arguments": "{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run the exact echo command requested\"}"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 52,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "usage",
+      "usage": {
+        "inputTokens": 129,
+        "outputTokens": 86,
+        "cacheReadTokens": 768,
+        "reasoningTokens": 16
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 53,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "chunk": {
+      "type": "finish",
+      "reason": {
+        "kind": "tool-calls"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/message",
+  "seq": 54,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "content": [
+      {
+        "type": "reasoning",
+        "text": "The user wants me to run a specific command and then reply with DONE."
+      },
+      {
+        "type": "tool-call",
+        "id": "call_00_waDnb5eZcD1dBV49O7F39256",
+        "name": "bash",
+        "arguments": "{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run the exact echo command requested\"}"
+      }
+    ]
+  }
+}
+{
+  "type": "usage",
+  "seq": 55,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "usage": {
+      "inputTokens": 129,
+      "outputTokens": 86,
+      "cacheReadTokens": 768,
+      "reasoningTokens": 16
+    }
+  }
+}
+{
+  "type": "tool/call",
+  "seq": 56,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "callId": "call_00_waDnb5eZcD1dBV49O7F39256",
+    "name": "bash",
+    "arguments": "{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run the exact echo command requested\"}"
+  }
+}
+{
+  "type": "tool/result",
+  "seq": 57,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1,
+    "callId": "call_00_waDnb5eZcD1dBV49O7F39256",
+    "content": [
+      {
+        "type": "text",
+        "text": "SNAPSHOT_OK\n"
+      }
+    ],
+    "isError": false
+  }
+}
+{
+  "type": "step/end",
+  "seq": 58,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 1
+  }
+}
+{
+  "type": "step/start",
+  "seq": 59,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 60,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "block-start",
+      "index": 0,
+      "blockType": "reasoning"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 61,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "The"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 62,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " command"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 63,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " ran"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 64,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " successfully"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 65,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " and"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 66,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " output"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 67,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " \""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 68,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "S"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 69,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "NA"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 70,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "PS"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 71,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "H"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 72,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "OT"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 73,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "_OK"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 74,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "\"."
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 75,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " Now"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 76,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " I"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 77,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " need"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 78,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " to"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 79,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " reply"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 80,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " with"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 81,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " the"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 82,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " single"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 83,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " word"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 84,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": " \""
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 85,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "D"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 86,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "ONE"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 87,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "reasoning-delta",
+      "index": 0,
+      "text": "\"."
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 88,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "block-start",
+      "index": 1,
+      "blockType": "text"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 89,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "text-delta",
+      "index": 1,
+      "text": "D"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 90,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "text-delta",
+      "index": 1,
+      "text": "ONE"
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 91,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "block-end",
+      "index": 0,
+      "block": {
+        "type": "reasoning",
+        "text": "The command ran successfully and output \"SNAPSHOT_OK\". Now I need to reply with the single word \"DONE\"."
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 92,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "block-end",
+      "index": 1,
+      "block": {
+        "type": "text",
+        "text": "DONE"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 93,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "usage",
+      "usage": {
+        "inputTokens": 105,
+        "outputTokens": 30,
+        "cacheReadTokens": 896,
+        "reasoningTokens": 27
+      }
+    }
+  }
+}
+{
+  "type": "assistant/chunk",
+  "seq": 94,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "chunk": {
+      "type": "finish",
+      "reason": {
+        "kind": "stop"
+      }
+    }
+  }
+}
+{
+  "type": "assistant/message",
+  "seq": 95,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "content": [
+      {
+        "type": "reasoning",
+        "text": "The command ran successfully and output \"SNAPSHOT_OK\". Now I need to reply with the single word \"DONE\"."
+      },
+      {
+        "type": "text",
+        "text": "DONE"
+      }
+    ]
+  }
+}
+{
+  "type": "usage",
+  "seq": 96,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2,
+    "usage": {
+      "inputTokens": 105,
+      "outputTokens": 30,
+      "cacheReadTokens": 896,
+      "reasoningTokens": 27
+    }
+  }
+}
+{
+  "type": "step/end",
+  "seq": 97,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "step": 2
+  }
+}
+{
+  "type": "turn/end",
+  "seq": 98,
+  "time": 0,
+  "data": {
+    "turn": 1,
+    "reason": {
+      "kind": "completed"
+    }
+  }
+}

+ 100 - 0
examples/acp-agent/tests/snapshots/tool-call-turn/session.jsonl

@@ -0,0 +1,100 @@
+{"type":"session","version":1,"id":"8c1d42ba-5f00-44bb-aaa1-0832fae5c24a","createdAt":1781811967162,"cwd":"/tmp/acp-snap-cwd-rdktn3"}
+{"type":"turn/start","seq":0,"time":1781811967165,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
+{"type":"user/message","seq":1,"time":1781811967165,"data":{"content":[{"type":"text","text":"Use the bash tool to run exactly: echo SNAPSHOT_OK. Then reply with the single word DONE and stop."}],"source":{"kind":"user"}}}
+{"type":"step/start","seq":2,"time":1781811967165,"data":{"turn":1,"step":1}}
+{"type":"assistant/chunk","seq":3,"time":1781811967719,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
+{"type":"assistant/chunk","seq":4,"time":1781811967720,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
+{"type":"assistant/chunk","seq":5,"time":1781811967849,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
+{"type":"assistant/chunk","seq":6,"time":1781811967883,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
+{"type":"assistant/chunk","seq":7,"time":1781811967883,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
+{"type":"assistant/chunk","seq":8,"time":1781811967883,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
+{"type":"assistant/chunk","seq":9,"time":1781811967884,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" run"}}}
+{"type":"assistant/chunk","seq":10,"time":1781811967884,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
+{"type":"assistant/chunk","seq":11,"time":1781811967916,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" specific"}}}
+{"type":"assistant/chunk","seq":12,"time":1781811967917,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" command"}}}
+{"type":"assistant/chunk","seq":13,"time":1781811967950,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
+{"type":"assistant/chunk","seq":14,"time":1781811967950,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
+{"type":"assistant/chunk","seq":15,"time":1781811967951,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
+{"type":"assistant/chunk","seq":16,"time":1781811967984,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
+{"type":"assistant/chunk","seq":17,"time":1781811967984,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" D"}}}
+{"type":"assistant/chunk","seq":18,"time":1781811967984,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
+{"type":"assistant/chunk","seq":19,"time":1781811967985,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
+{"type":"assistant/chunk","seq":20,"time":1781811968085,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
+{"type":"assistant/chunk","seq":21,"time":1781811968086,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":""}}}
+{"type":"assistant/chunk","seq":22,"time":1781811968120,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"{"}}}
+{"type":"assistant/chunk","seq":23,"time":1781811968121,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":24,"time":1781811968121,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"command"}}}
+{"type":"assistant/chunk","seq":25,"time":1781811968121,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":26,"time":1781811968121,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":": "}}}
+{"type":"assistant/chunk","seq":27,"time":1781811968153,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":28,"time":1781811968153,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"echo"}}}
+{"type":"assistant/chunk","seq":29,"time":1781811968153,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":" S"}}}
+{"type":"assistant/chunk","seq":30,"time":1781811968153,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"NA"}}}
+{"type":"assistant/chunk","seq":31,"time":1781811968187,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"PS"}}}
+{"type":"assistant/chunk","seq":32,"time":1781811968188,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"H"}}}
+{"type":"assistant/chunk","seq":33,"time":1781811968188,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"OT"}}}
+{"type":"assistant/chunk","seq":34,"time":1781811968188,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"_OK"}}}
+{"type":"assistant/chunk","seq":35,"time":1781811968188,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":36,"time":1781811968254,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":", "}}}
+{"type":"assistant/chunk","seq":37,"time":1781811968255,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":38,"time":1781811968255,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"description"}}}
+{"type":"assistant/chunk","seq":39,"time":1781811968255,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":40,"time":1781811968255,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":": "}}}
+{"type":"assistant/chunk","seq":41,"time":1781811968289,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":42,"time":1781811968289,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"Run"}}}
+{"type":"assistant/chunk","seq":43,"time":1781811968289,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":" the"}}}
+{"type":"assistant/chunk","seq":44,"time":1781811968289,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":" exact"}}}
+{"type":"assistant/chunk","seq":45,"time":1781811968323,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":" echo"}}}
+{"type":"assistant/chunk","seq":46,"time":1781811968356,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":" command"}}}
+{"type":"assistant/chunk","seq":47,"time":1781811968356,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":" requested"}}}
+{"type":"assistant/chunk","seq":48,"time":1781811968390,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"\""}}}
+{"type":"assistant/chunk","seq":49,"time":1781811968390,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","argumentsDelta":"}"}}}
+{"type":"assistant/chunk","seq":50,"time":1781811968477,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to run a specific command and then reply with DONE."}}}}
+{"type":"assistant/chunk","seq":51,"time":1781811968477,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run the exact echo command requested\"}"}}}}
+{"type":"assistant/chunk","seq":52,"time":1781811968477,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":129,"outputTokens":86,"cacheReadTokens":768,"reasoningTokens":16}}}}
+{"type":"assistant/chunk","seq":53,"time":1781811968477,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
+{"type":"assistant/message","seq":54,"time":1781811968479,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run a specific command and then reply with DONE."},{"type":"tool-call","id":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run the exact echo command requested\"}"}]}}
+{"type":"usage","seq":55,"time":1781811968479,"data":{"turn":1,"step":1,"usage":{"inputTokens":129,"outputTokens":86,"cacheReadTokens":768,"reasoningTokens":16}}}
+{"type":"tool/call","seq":56,"time":1781811968479,"data":{"turn":1,"step":1,"callId":"call_00_waDnb5eZcD1dBV49O7F39256","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run the exact echo command requested\"}"}}
+{"type":"tool/result","seq":57,"time":1781811968493,"data":{"turn":1,"step":1,"callId":"call_00_waDnb5eZcD1dBV49O7F39256","content":[{"type":"text","text":"SNAPSHOT_OK\n"}],"isError":false}}
+{"type":"step/end","seq":58,"time":1781811968494,"data":{"turn":1,"step":1}}
+{"type":"step/start","seq":59,"time":1781811968494,"data":{"turn":1,"step":2}}
+{"type":"assistant/chunk","seq":60,"time":1781811969060,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
+{"type":"assistant/chunk","seq":61,"time":1781811969060,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
+{"type":"assistant/chunk","seq":62,"time":1781811969175,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" command"}}}
+{"type":"assistant/chunk","seq":63,"time":1781811969209,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" ran"}}}
+{"type":"assistant/chunk","seq":64,"time":1781811969244,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
+{"type":"assistant/chunk","seq":65,"time":1781811969244,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
+{"type":"assistant/chunk","seq":66,"time":1781811969244,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" output"}}}
+{"type":"assistant/chunk","seq":67,"time":1781811969244,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
+{"type":"assistant/chunk","seq":68,"time":1781811969278,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"S"}}}
+{"type":"assistant/chunk","seq":69,"time":1781811969278,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"NA"}}}
+{"type":"assistant/chunk","seq":70,"time":1781811969278,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"PS"}}}
+{"type":"assistant/chunk","seq":71,"time":1781811969279,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"H"}}}
+{"type":"assistant/chunk","seq":72,"time":1781811969279,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"OT"}}}
+{"type":"assistant/chunk","seq":73,"time":1781811969279,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
+{"type":"assistant/chunk","seq":74,"time":1781811969313,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
+{"type":"assistant/chunk","seq":75,"time":1781811969313,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Now"}}}
+{"type":"assistant/chunk","seq":76,"time":1781811969313,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
+{"type":"assistant/chunk","seq":77,"time":1781811969314,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" need"}}}
+{"type":"assistant/chunk","seq":78,"time":1781811969314,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
+{"type":"assistant/chunk","seq":79,"time":1781811969314,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
+{"type":"assistant/chunk","seq":80,"time":1781811969347,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
+{"type":"assistant/chunk","seq":81,"time":1781811969348,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
+{"type":"assistant/chunk","seq":82,"time":1781811969348,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
+{"type":"assistant/chunk","seq":83,"time":1781811969420,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
+{"type":"assistant/chunk","seq":84,"time":1781811969421,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
+{"type":"assistant/chunk","seq":85,"time":1781811969421,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
+{"type":"assistant/chunk","seq":86,"time":1781811969421,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
+{"type":"assistant/chunk","seq":87,"time":1781811969421,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
+{"type":"assistant/chunk","seq":88,"time":1781811969421,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
+{"type":"assistant/chunk","seq":89,"time":1781811969422,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
+{"type":"assistant/chunk","seq":90,"time":1781811969422,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
+{"type":"assistant/chunk","seq":91,"time":1781811969422,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The command ran successfully and output \"SNAPSHOT_OK\". Now I need to reply with the single word \"DONE\"."}}}}
+{"type":"assistant/chunk","seq":92,"time":1781811969422,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
+{"type":"assistant/chunk","seq":93,"time":1781811969422,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":105,"outputTokens":30,"cacheReadTokens":896,"reasoningTokens":27}}}}
+{"type":"assistant/chunk","seq":94,"time":1781811969422,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
+{"type":"assistant/message","seq":95,"time":1781811969422,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The command ran successfully and output \"SNAPSHOT_OK\". Now I need to reply with the single word \"DONE\"."},{"type":"text","text":"DONE"}]}}
+{"type":"usage","seq":96,"time":1781811969422,"data":{"turn":1,"step":2,"usage":{"inputTokens":105,"outputTokens":30,"cacheReadTokens":896,"reasoningTokens":27}}}
+{"type":"step/end","seq":97,"time":1781811969422,"data":{"turn":1,"step":2}}
+{"type":"turn/end","seq":98,"time":1781811969423,"data":{"turn":1,"reason":{"kind":"completed"}}}

+ 723 - 0
examples/acp-agent/tests/snapshots/tool-call-turn/stdout.golden.txt

@@ -0,0 +1,723 @@
+{
+  "jsonrpc": "2.0",
+  "id": 1,
+  "result": {
+    "protocolVersion": 1,
+    "agentInfo": {
+      "name": "deepseek-harness-acp",
+      "version": "0.0.1"
+    },
+    "agentCapabilities": {
+      "loadSession": true,
+      "promptCapabilities": {
+        "image": false,
+        "audio": false,
+        "embeddedContext": false
+      }
+    },
+    "authMethods": []
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 2,
+  "result": {
+    "sessionId": "{{sessionId}}"
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "user_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "Use the bash tool to run exactly: echo SNAPSHOT_OK. Then reply with the single word DONE and stop."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "The"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " user"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " wants"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " me"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " to"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " run"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " a"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " specific"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " command"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " and"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " then"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " reply"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " with"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " D"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "ONE"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "tool_call",
+      "toolCallId": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "title": "echo SNAPSHOT_OK",
+      "kind": "execute",
+      "status": "in_progress",
+      "rawInput": "echo SNAPSHOT_OK",
+      "content": [
+        {
+          "type": "content",
+          "content": {
+            "type": "text",
+            "text": "Run the exact echo command requested"
+          }
+        }
+      ]
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "tool_call_update",
+      "toolCallId": "call_00_waDnb5eZcD1dBV49O7F39256",
+      "status": "completed",
+      "content": [
+        {
+          "type": "content",
+          "content": {
+            "type": "text",
+            "text": "```console\nSNAPSHOT_OK\n```"
+          }
+        }
+      ]
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "The"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " command"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " ran"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " successfully"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " and"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " output"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " \""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "S"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "NA"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "PS"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "H"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "OT"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "_OK"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "\"."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " Now"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " I"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " need"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " to"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " reply"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " with"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " the"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " single"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " word"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": " \""
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "D"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "ONE"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_thought_chunk",
+      "content": {
+        "type": "text",
+        "text": "\"."
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "D"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "method": "session/update",
+  "params": {
+    "sessionId": "{{sessionId}}",
+    "update": {
+      "sessionUpdate": "agent_message_chunk",
+      "content": {
+        "type": "text",
+        "text": "ONE"
+      }
+    }
+  }
+}
+{
+  "jsonrpc": "2.0",
+  "id": 3,
+  "result": {
+    "stopReason": "end_turn"
+  }
+}