Merge remote-tracking branch 'origin/master' into worktree-rename-react-loop-agent
This commit is contained in:
@@ -43,6 +43,7 @@ Where your independent reasoning earns its keep. Start here, then keep going acr
|
||||
- **Plugin export shape + real-loader coverage.** A new/changed `cordis.yml`-loaded plugin: is it a function/namespace plugin (`name`/`inject`/`Config`/`apply` named exports) with NO `export default`? A stray default export makes the Loader's `unwrapExports` drop `inject` and the plugin crashes at load with `cannot get property … without inject` — invisible to hand-built `ctx.plugin({...})` tests and to line coverage. Confirm there's a test driving it through the REAL loader path (the no-key subprocess e2e for ACP is the model). And any opportunistic read of a service NOT in `static inject` should use `ctx.get(name)`, not `ctx.<name>` (the property proxy throws through a foreign shadow). See [packages/AGENTS.md](../../../packages/AGENTS.md) and [docs/postmortem/0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md).
|
||||
- **Seam discipline.** New swappable capability? Check it's split per the capability-seams RFC (interface / impl / consumer), and that the consumer injects the interface key, never an implementation type.
|
||||
- **Test quality.** A test that passes but asserts the wrong thing is worse than none. Check that new tests would actually fail if the behavior regressed, and that they exercise the contract (events fired, disposal reached) rather than restating the implementation.
|
||||
- **Snapshot coverage for transcript/UX changes.** If the PR changes the editor-facing transcript or end-to-end agent UX — the ACP bridge's event→update translation, the agent loop's observable output, tool presentation, or anything an editor renders — it must add or update a snapshot scenario (`examples/*/tests/**/*.snapshot.ts`, goldens under `examples/acp-agent/tests/snapshots/`) or note explicitly why none applies (AGENTS.md § Conventions). Review the golden diff itself: a changed `stdout.golden.txt` / `session.golden.txt` is a behavior change in disguise — confirm it's intended, not an accidental regression someone re-recorded away. A pure internal refactor with no observable-output change is exempt, but the PR should say so. See [docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md](../../../docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md).
|
||||
- **Intent and contracts.** Does the change do what the PR says, and honor the documented contract on *both* sides of every seam it touches (see AGENTS.md "Honor cross-seam contracts on BOTH sides")?
|
||||
|
||||
## How to respond
|
||||
|
||||
@@ -60,6 +60,15 @@ jobs:
|
||||
- name: Tests with coverage gate (per-file 100%)
|
||||
run: pnpm run test:coverage
|
||||
|
||||
# ACP snapshot tests (acp-snapshot-tests RFC): boot the real acp-agent
|
||||
# subprocess and replay recorded session-log fixtures, diffing the
|
||||
# normalized stdout transcript + re-persisted log against committed
|
||||
# goldens. KEYLESS by design — the same `test:snapshot` script the pre-push
|
||||
# hook runs (one source of truth), so the full-transcript regression net
|
||||
# is part of every PR gate, not just local pre-push.
|
||||
- name: Snapshot tests (ACP transcript replay)
|
||||
run: pnpm run test:snapshot
|
||||
|
||||
# Before hygiene: publint validates the packed artifacts (lib/index.js),
|
||||
# which only the tsdown bundling step emits.
|
||||
- name: Build (tsc -b + tsdown bundles)
|
||||
|
||||
@@ -41,7 +41,9 @@ examples/ Runnable demos (not workspaces). echo-agent = mock model + echo
|
||||
(pnpm run demo:coding, needs DEEPSEEK_API_KEY).
|
||||
acp-agent = the coding agent exposed as an ACP server over
|
||||
JSON-RPC stdio (pnpm run demo:acp, needs DEEPSEEK_API_KEY).
|
||||
base.yml = shared provider/tool core both real demos include.
|
||||
base.yml = shared provider/tool core both real demos include
|
||||
(= base-core.yml, the providerless core, + the llm-deepseek adapter;
|
||||
base-core.yml is reused by the acp-agent snapshot-replay config).
|
||||
docs/ architecture.md — the design doc. module-graph.md — generated
|
||||
inter-package dependency graph (Mermaid; `pnpm run gen-module-graph`).
|
||||
rfc/ — design decisions and proposals, one kind of doc grouped by
|
||||
@@ -60,10 +62,20 @@ scripts/ repo maintenance scripts (vendor-manifest guard, publint runner).
|
||||
|
||||
```sh
|
||||
pnpm install # pnpm workspaces, node >= 24
|
||||
pnpm run test # vitest run (packages/*/tests/**/*.spec.ts)
|
||||
pnpm run test # vitest run (packages|examples/*/tests/**/*.spec.ts)
|
||||
pnpm run test:coverage # vitest run --coverage (per-file 100% gate on packages/*/src)
|
||||
pnpm run test:e2e # real-API tests (packages|examples/*/tests/**/*.e2e.ts);
|
||||
# self-skips without DEEPSEEK_API_KEY — see Secrets below
|
||||
pnpm run test:snapshot # ACP snapshot tests (examples/*/tests/**/*.snapshot.ts):
|
||||
# boot the real acp-agent subprocess, replay a recorded
|
||||
# session JSONL, diff the normalized stdout + re-persisted
|
||||
# log against committed goldens. KEYLESS — runs in the
|
||||
# default gate. Filter one by scenario name (no `--`, which
|
||||
# vitest treats as a positional file filter): `pnpm run
|
||||
# test:snapshot -t <name>`.
|
||||
pnpm run test:snapshot:record # re-record fixtures + goldens against the real
|
||||
# API (needs DEEPSEEK_API_KEY); accept-the-diff = re-record
|
||||
# (or `pnpm run test:snapshot -u` to refresh goldens only)
|
||||
pnpm run typecheck # tsc -b tsconfig.build.json (declarations) + tsc -p
|
||||
# tsconfig.typecheck.json (tests/examples typecheck too)
|
||||
pnpm run lint # eslint .
|
||||
@@ -122,6 +134,7 @@ Dev/test/demo run **unbuilt** via tsx + the `paths` map in the root `tsconfig.js
|
||||
- **TODO markers**: use `FIXME`/`TODO`/`XXX` to flag known issues by urgency — see [docs/development.md](docs/development.md) for the semantics of each.
|
||||
- **Tests**: vitest, colocated under `packages/<name>/tests/*.spec.ts`. Every registry needs an HMR-safety test (dispose the contributing fiber, assert cleanup). **Excessive tests are welcome** — when in doubt, write the test; err on the side of covering edge cases, error paths, event ordering, and concurrency races even if they seem unlikely. Review findings get regression tests (see `packages/agent-loop/tests/review-fixes.spec.ts`). The same generosity applies to **real-API (with-key) e2e tests — inference is cheap here (we are DeepSeek), so do not ration them**: cover the agent's real flows (a real prompt that writes a file, multi-turn, tool use, cancellation) and run them frequently while developing, especially cheap **smoke tests** that boot the real example and check the world. A green mock/no-key suite proves the plumbing, not the product — the with-key smoke test is what catches "green units, broken product". See § Secrets / .env for the with-key policy and why self-skip is a CI accommodation, not a verdict that real-API tests are expensive.
|
||||
- **Prefer the REAL implementation over a mock/stand-in in tests.** When the genuine collaborator is available in the repo, wire it up instead of hand-rolling a fake — a test that registers an inline `defineTool({ name: 'bash', … })` to stand in for `dsh-tool-bash` proves the *bridge* moves bytes but not that the *shipping tool* renders the way the test asserts; the two drift and the test passes while the product is wrong. Mock only the genuinely expensive/non-deterministic boundary (the LLM adapter, the network, the clock) and keep everything downstream real: a bridge tool-call test runs the scripted mock MODEL but the REAL tool + REAL executor (e.g. `makeBridgeHarness({ withBash: true })` plugs `dsh-bash-local` + `dsh-tool-bash` and runs an actual `echo`), so it verifies the actual `presentCall`/`presentResult` an editor sees. This is the unit-test echo of "verify the world, not a synthetic stand-in" (see § Defensive patterns) — a fake you wrote will agree with whatever you assumed; the real thing won't.
|
||||
- **A change that affects the editor-facing transcript or end-to-end agent UX needs a snapshot test (or an explicit note in the PR why none applies).** The snapshot tier (`examples/*/tests/**/*.snapshot.ts`, `pnpm run test:snapshot`) boots the real example subprocess, replays a recorded session JSONL deterministically (keyless), and diffs the normalized stdout transcript + re-persisted session log against committed goldens — the full-transcript regression net that mock-level unit tests structurally cannot be (it is what catches a bridge-translation or loop-structure regression that leaves every unit green). When you change the ACP bridge, the agent loop's observable output, tool presentation, or anything an editor renders, add or update a scenario under `examples/acp-agent/tests/snapshots/` and re-record with `pnpm run test:snapshot:record`. Reviewing the golden diff is part of the review. The rule is scoped to transcript/UX-affecting changes — a pure internal refactor with no observable-output change does not need one, but say so. See [docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md](docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md).
|
||||
|
||||
## Defensive patterns (hard-won)
|
||||
|
||||
|
||||
+2
-1
@@ -57,7 +57,7 @@ DEEPSEEK_BASE_URL=https://... # optional
|
||||
lefthook is configured in `lefthook.yml` as an early local checkpoint before review:
|
||||
|
||||
- `pre-commit` runs staged-file ESLint fixes, `pnpm run typecheck`, and the vendor manifest guard.
|
||||
- `pre-push` runs `pnpm run test`, `pnpm run hygiene`, `pnpm run doc-sync`, and `pnpm run verify-module-graph`.
|
||||
- `pre-push` runs `pnpm run test`, `pnpm run test:snapshot`, `pnpm run hygiene`, `pnpm run doc-sync`, and `pnpm run verify-module-graph`.
|
||||
|
||||
The vendor manifest guard checks that changes under `vendor/*/src` are staged with the matching `vendor/README.md` manifest update. See `vendor/README.md` before editing vendored code.
|
||||
|
||||
@@ -74,6 +74,7 @@ The GitHub workflow runs these gates on each pull request:
|
||||
- `pnpm run doc-sync`
|
||||
- `pnpm run verify-module-graph`
|
||||
- `pnpm run test:coverage`
|
||||
- `pnpm run test:snapshot`
|
||||
- `pnpm run build`
|
||||
- `pnpm run knip && pnpm run publint`
|
||||
- an echo-agent smoke test that checks the demo's tool call, tool result, and JSONL output
|
||||
|
||||
@@ -58,6 +58,7 @@ Do NOT write one for a mechanical or local choice (a variable name, a one-file r
|
||||
| [Every session event is enclosed in a turn](implemented/2026-06-15-turn-enclosure-invariant.md) | 2026-06-15 |
|
||||
| [pnpm as the package manager instead of Yarn 4](implemented/2026-06-16-pnpm-over-yarn.md) | 2026-06-16 |
|
||||
| [Rich ACP bash rendering — the terminal card (`_meta`) and command classification](implemented/2026-06-18-acp-terminal-and-tool-rendering.md) | 2026-06-18 |
|
||||
| [ACP snapshot tests — record-once / replay-deterministic](implemented/2026-06-19-acp-snapshot-tests.md) | 2026-06-19 |
|
||||
|
||||
## Rejected
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# RFC: ACP snapshot tests — record-once / replay-deterministic
|
||||
|
||||
Status: implemented (accepted 2026-06-19)
|
||||
|
||||
<!-- XXX: legacy ADR/RFC body format, not yet normalized to a unified RFC template. -->
|
||||
|
||||
## Context
|
||||
|
||||
The harness has two test tiers: keyless unit `.spec.ts` (the 100%-per-file coverage gate) and real-API `.e2e.ts` (key-gated, self-skipping in CI). Neither continuously verifies the **complete output transcript** an ACP editor (Zed) sees on its stdin/stdout. The existing ACP e2e ([examples/acp-agent/tests/acp.e2e.ts](../../../examples/acp-agent/tests/acp.e2e.ts)) is the closest end-to-end check, but it is key-gated and asserts on a handful of *structured fields* (`stopReason`, a `tool_call` title), not the byte-for-byte stream of `session/update` frames. That leaves the "green units, broken product" gap: every unit test can pass while the actual editor-facing protocol output regresses — the same class of failure that shipped the inject bug ([docs/postmortem/0001](../../postmortem/0001-acp-default-export-drops-inject.md)), where 178 hand-mounted tests stayed green while a real Zed session crashed instantly.
|
||||
|
||||
The blocker for a full-transcript test is the model: the agent's output is driven by a non-deterministic LLM, and a key-gated test that hits the real API on every run is neither deterministic nor CI-runnable. We want the fidelity of a real run with the determinism of a fixture.
|
||||
|
||||
This RFC records the decision to add a third test tier — **snapshot tests** — and the design choices that make it deterministic, keyless-in-CI, and cheap to maintain.
|
||||
|
||||
## Decision
|
||||
|
||||
A snapshot test boots the **real** `examples/acp-agent` subprocess, drives it over real ACP stdio with a deterministic input script, and diffs its (normalized) output against committed golden files. The model is made deterministic by **recording a real run's session log once** against the real API and **replaying it** on every subsequent run. The committed fixture IS the persisted session JSONL — the same append-only log the harness writes for any session.
|
||||
|
||||
### The fixture is the persisted session JSONL
|
||||
|
||||
The per-scenario fixture is `<scenario>/session.jsonl`: the exact log produced by running the scenario once against the real API (the snapshot harness harvests the file the JSONL persistence backend writes). This log already contains everything needed to reproduce the run deterministically: its `assistant/chunk` events carry every parsed `StreamChunk` (the LLM's behavior), and its `tool/call`/`tool/result`/`turn/*`/`assistant/message`/`usage` events carry the harness's behavior. One artifact captures both, and it is the format the codebase already treats as the authoritative replay record ([packages/session/src/types.ts](../../../packages/session/src/types.ts): "raw chunks are the replay record").
|
||||
|
||||
An earlier draft used a hand-authored `llm.json` of model chunks; reusing the real session log instead means the fixture is a genuine product of the system (not a hand-built mock), and it doubles as a behavioral golden (see below). A byte-level HTTP-record library (Polly/nock/MSW) was rejected: adapter-specific, awkward with streaming SSE, and lower-level than the thing under test.
|
||||
|
||||
### Replay derives the model script from the log
|
||||
|
||||
The replay seam is the provider-agnostic `llm/stream` waterfall ([packages/llm/src/index.ts](../../../packages/llm/src/index.ts)) — a single listener intercepts every model call regardless of adapter (deepseek, pi-ai), because the loop routes all model calls through `ctx.llm.stream()`. The `llm-replay` plugin short-circuits that waterfall (never calls `next()`) and serves back streams reconstructed from the log: `deriveReplayScript(events)` groups `assistant/chunk` events by `(turn, step)` in log order, yielding one model stream per group. This grouping is exact because the agent loop makes **exactly one `ctx.llm.stream()` call per step** and tags every chunk with the current `(turn, step)` ([packages/agent-loop/src/loop.ts](../../../packages/agent-loop/src/loop.ts)): `step` increments once per loop iteration, so `(turn, step)` is unique per model call. A `finish {kind:'error'}` chunk is part of its group and replays naturally — no special-casing.
|
||||
|
||||
### The in-memory replay entry honors the full LLM contract
|
||||
|
||||
`deriveReplayScript` produces a list of `ReplayEntry`, the in-memory unit the replay listener serves positionally:
|
||||
|
||||
```
|
||||
{ kind: 'chunks', chunks: StreamChunk[] }
|
||||
| { kind: 'throw', chunks: StreamChunk[], message: string, code: string, status?: number }
|
||||
| { kind: 'hang' }
|
||||
```
|
||||
|
||||
`chunks` is what the log derives. The other two cover the LLM contract's failure branches the log **cannot** reconstruct from `assistant/chunk` alone: a *pure throw before any chunk* (e.g. an HTTP 401 — the log holds only a `turn/end {error}`, no chunks) and a *cancel/hang* (a timing behavior, not chunk content). A scenario needing those supplies an optional `<scenario>/replay.override.json` (a `ReplayEntry[]`) that **replaces** the derived script. The `throw` entry carries any prefix chunks so a mid-stream failure replays its partial output before throwing — the "honor cross-seam contracts on BOTH sides" defensive pattern. Synthesizing throw/cancel from the log's `turn/end {kind:error|aborted}` was rejected: it would couple `llm-replay` to loop-internal turn-closing semantics and the `turn/end` reason is lossy (it can't distinguish a thrown 401 from a finish-error). An explicit sidecar is the cleaner seam.
|
||||
|
||||
### Positional replay, one in-flight stream
|
||||
|
||||
Replay is positional: the Nth `stream()` call serves the Nth `ReplayEntry`. This is deterministic **only when at most one model stream is in flight at a time**. The first cut runs one ACP session per scenario, which guarantees that. Multi-session concurrency (the bridge multiplexes N sessions, which can prompt concurrently) would let scheduling decide which model call consumes which entry — so concurrent-session snapshots are out of scope until entries are keyed by request rather than position. A scenario whose control flow changes the number/order of model calls must be re-recorded; the cursor **fails loud on overrun** rather than silently reusing or skipping an entry. A missing `session.jsonl` in replay fails loud too ("record first") — never a silent skip.
|
||||
|
||||
### Recording harvests the log; keyless replay needs a providerless config
|
||||
|
||||
Recording runs the scenario with the real `llm-deepseek` adapter and the JSONL persistence backend, then copies the produced `.jsonl` into the scenario dir. Per-event appends are durable, but the harness shuts the subprocess down gracefully (close stdin → `await ctx.dispose()`) before harvesting so the final events are flushed. `llm-replay` itself does no recording — it is replay-only.
|
||||
|
||||
`examples/base.yml` always loads `@deepseek-ai/dsh-llm-deepseek`, whose `apply` throws when no API key is present ([packages/llm-deepseek/src/index.ts](../../../packages/llm-deepseek/src/index.ts)). So replay cannot reuse the normal config — it uses a dedicated `examples/acp-agent/cordis.snapshot.yml` that installs `llm-replay` in place of the adapter. To avoid duplicating the rest of the tree, the providerless core is factored into `examples/base-core.yml` (shared by `base.yml = base-core + llm-deepseek` and the replay config = `base-core + llm-replay`), and the agent-loop/persistence/ACP-bridge tail into `examples/acp-agent/acp-tail.yml` (shared by `cordis.yml` and the replay config). Recording reuses the normal `cordis.yml` (real adapter) — its persistence root reads `$DSH_SNAPSHOT_SESSIONS_ROOT` when the harness sets it — so there is no separate record config. In replay mode `start.ts` skips `.env` loading so a stray key cannot trigger a live call.
|
||||
|
||||
### Two goldens: normalize, then snapshot
|
||||
|
||||
A snapshot run asserts **two** normalized goldens, because the harness's external surfaces are distinct:
|
||||
|
||||
1. The **stdout transcript** — the framed `session/update` JSON-RPC the editor sees. Catches regressions in the ACP bridge's event→update translation (`streamSessionEventUpdate`).
|
||||
2. The **re-derived session JSONL** — the log the replay run itself persists, compared against the recorded fixture. Catches regressions in the loop, tool dispatch, and turn/step structure that never surface on stdout.
|
||||
|
||||
The two are genuinely additive: stdout is the bridge's *lossy projection* of the log (it drops `usage`, `step/*`, exact `seq`/`time`, and renders tool I/O differently), so a loop/tool/turn-structure regression can change the JSONL while leaving the stdout projection identical, and a bridge-translation regression can change stdout while the JSONL is untouched. Asserting the JSONL equality also echoes the proposed [universal replay fixture](../proposed/2026-06-11-deterministic-and-stress-testing.md) idea.
|
||||
|
||||
Both surfaces contain non-deterministic values that a pure normalization function scrubs **before** the snapshot: `randomUUID()` session ids → `{{sessionId}}`, the temp `mkdtemp` cwd → `{{cwd}}` (it appears in terminal-card `_meta` and the log header), JSON-RPC ids → a stable sequence, and the log's per-event `time` (epoch ms) + header `createdAt` dropped or zeroed (the log's `seq` is left intact — it is deterministic by contract, `seq = log.length`). Real bash runs during replay, so the JSONL normalizer additionally stabilizes tool-output volatility (any embedded paths/pids/timestamps) — scenarios keep bash commands tightly constrained (`echo`, file writes; no `date`/`env`/background/large-output) so this surface is small. The goldens are themselves **JSONL** — one compact, normalized record per line, in the same shape as the surfaces they mirror (NDJSON on the wire, JSONL on disk: `stdout.golden.jsonl`, `session.golden.jsonl`), so they stay `grep`/`jq`-able and faithful to what the agent actually emits. A separate raw-purity assertion keeps the guarantee that every stdout line parses as JSON (no logger leak onto the protocol channel). Vitest's `toMatchFileSnapshot` provides the golden store and the `-u`/`--update` "accept the diff" workflow.
|
||||
|
||||
### Isolation: normalization now, sandbox later
|
||||
|
||||
|
||||
Determinism of the tool environment comes from a per-test `mkdtemp` cwd, the executor's existing secret-scrubbing env (`/KEY|SECRET|TOKEN/i`), the fresh non-login `bash -c` per call, and the normalization pass — **not** from an OS sandbox. A real rootless sandbox (bwrap on Linux, sandbox-exec/Seatbelt on macOS) is the established cross-platform pattern (Claude Code, Codex), but it is per-OS, fragile on newer kernels (Ubuntu 24.04+ AppArmor blocks unprivileged user namespaces), and unnecessary for transcript determinism. It is reserved as a future tier via the documented `BashExecutor` capability seam ([a sandboxing executor replaces dsh-bash-local without touching a tool schema](2026-06-13-capability-seams.md)) — a new `bash-*` package, not a change here. Scenarios keep bash commands tightly constrained (no `date`/`env`/background/large-output) so the temp-dir tier suffices.
|
||||
|
||||
### Example-local plugin, not a new package
|
||||
|
||||
The replay plugin lives at `examples/acp-agent/src/llm-replay.ts`, referenced from the snapshot config by relative path — exactly how echo-agent wires its [mock-llm.ts](../../../examples/echo-agent/src/mock-llm.ts). It is test/example infrastructure with one consumer; the capability-seams rule says not to split into a published `packages/` trio preemptively. It is promoted to a package only when a second example needs it.
|
||||
|
||||
### Two subcommands, replay in the default gate
|
||||
|
||||
`pnpm run test:snapshot` runs replay (keyless) and is composed into the default `pnpm run test` gate so every PR gets the regression check (the main `vitest.config.ts` include stays narrow; the gate is `test && test:snapshot`). `pnpm run test:snapshot:record` requires `DEEPSEEK_API_KEY` (loaded from repo `.env` first), hits the real API, harvests the produced `session.jsonl`, and `--update`s both goldens in one pass. Both forward a scenario filter. A missing fixture in replay **fails loud** with a "record first" message rather than self-skipping (the e2e self-skip rule is a CI-secret accommodation, not appropriate here — a committed-fixture test that silently vanishes is a coverage hole). A no-model scenario's `session.jsonl` simply has no `assistant/chunk` events (empty derived script); fail-loud still applies if a model call happens with no entry. An orphan-fixture guard test fails on a golden/fixture not referenced by any scenario (Vitest does not prune orphaned raw goldens).
|
||||
|
||||
## Consequences
|
||||
|
||||
A new test tier and its fixtures to maintain: each scenario is a directory of `input.json` (the client stdin script) + `session.jsonl` (the recorded log) + an optional `replay.override.json` + an optional `workspace/` seed dir + the two `*.golden.jsonl` files, committed and reviewed. A scenario that needs the agent to operate on existing files (read, edit, grep) ships a `<scenario>/workspace/` directory; the harness copies its contents into the temp cwd before the run, so the seeded files are present for both record and replay (the cwd is normalized in the goldens, so the seeded paths stay stable). Re-recording when the model's phrasing changes churns the goldens — visible in review, which is the point of committing them. Bought: deterministic, keyless, full-transcript regression coverage that boots the real Loader (so it still guards the export-shape bug class), exercises the real bash executor, and gives a one-command accept-the-diff loop. The tier is ACP-first but the harness (subprocess + tee + input-DSL + workspace seeding + normalization + JSONL-derived replay) is example-agnostic and extends to other examples.
|
||||
|
||||
This RFC relates to but does not supersede the [proposed determinism RFC](../proposed/2026-06-11-deterministic-and-stress-testing.md): that proposal's "universal replay fixture" re-derives session *message history* after every test (an internal-consistency invariant), whereas snapshot tests pin the *external protocol output*. They are complementary — one guards the event-sourcing invariant, the other guards the editor-facing contract.
|
||||
@@ -30,6 +30,10 @@ Add to your Zed `settings.json` under `agent_servers`:
|
||||
|
||||
The editor sets each session's `cwd` to the project it opens; the agent's bash tools run there (see the per-session `cwd` note in `packages/acp`), so launch the server from the harness repo with `pnpm --dir …` and let ACP carry the workspace path per session.
|
||||
|
||||
## Snapshot tests (record-once / replay-deterministic)
|
||||
|
||||
This example is the home of the harness's **snapshot tests** — they boot this server as a real subprocess, drive it with a deterministic input script, and diff its normalized output against committed golden files. The model is made deterministic by `src/llm-replay.ts`, a function/namespace plugin that installs an `llm/stream` waterfall listener and short-circuits it, serving model streams reconstructed from a recorded **session JSONL** fixture (`<scenario>/session.jsonl`) — so replay needs no API key. The fixture IS the persisted session log: its `assistant/chunk` events carry every `StreamChunk`, so grouping them by `(turn, step)` reconstructs each `stream()` call (one model call per loop step). Recording is therefore "run the real agent once and harvest the `.jsonl`". The two failure modes not expressible as logged chunks — a pure throw before any chunk, and cancel/hang — use an optional `<scenario>/replay.override.json` sidecar (a `ReplayEntry[]` that replaces the derived script). A scenario that needs the agent to operate on existing files ships an optional `<scenario>/workspace/` directory — the harness copies its contents into the temp cwd before the run (see `workspace-edit`). See [docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md](../../docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md) for the full design.
|
||||
|
||||
## MVP limitations
|
||||
|
||||
The bridge supports N concurrent sessions per connection, each in its own workspace `cwd` (RFC 011). Remaining limits: prompts support ACP's baseline `text` and `resource_link` blocks only, `additionalDirectories` and `mcpServers` are rejected, and the tool-permission gate is deferred (`TODO(rfc010-permission-gate)` — tools run with the executor's full authority). See `packages/acp/README.md` for the full contract.
|
||||
@@ -0,0 +1,33 @@
|
||||
# The acp-agent "tail" shared by every acp-agent config (the normal demo, the
|
||||
# snapshot RECORD path which reuses cordis.yml, and the snapshot REPLAY config):
|
||||
# agent-loop (no pre-created agents — ACP session/new creates them on demand),
|
||||
# JSONL session persistence, and the ACP bridge with its system prompt. The
|
||||
# providerless core + an LLM adapter are included BEFORE this tail by each
|
||||
# config; nothing here loads an adapter, so the tail is provider-agnostic.
|
||||
#
|
||||
# Persistence root: $DSH_SNAPSHOT_SESSIONS_ROOT when the snapshot harness sets
|
||||
# it (so it can harvest / isolate the log), else ./.sessions for the demo.
|
||||
|
||||
- id: agent-loop
|
||||
name: '@deepseek-ai/dsh-agent-loop'
|
||||
config:
|
||||
agents: []
|
||||
|
||||
- id: session-persistence
|
||||
name: '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
config:
|
||||
root: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions'
|
||||
|
||||
- id: acp
|
||||
name: '@deepseek-ai/dsh-acp'
|
||||
config:
|
||||
model: deepseek-v4-flash
|
||||
systemPrompt: |
|
||||
You are a coding assistant driven over the Agent Client Protocol.
|
||||
|
||||
Your only tools are bash (plus bash_output/bash_kill for background
|
||||
tasks). Do ALL file operations through bash: read with cat/sed/head,
|
||||
search with grep, write with heredocs (cat <<'EOF' > file), edit with
|
||||
sed or a rewrite. Each bash call runs in a fresh shell — pass workdir
|
||||
instead of cd. Check the [exit code: N] marker; verify your work. Keep
|
||||
answers brief and factual.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Snapshot-test REPLAY config: the acp-agent plugin tree with the model replaced
|
||||
# by llm-replay (serves a recorded session JSONL — no API key, no network).
|
||||
#
|
||||
# It reuses ../base-core.yml (the providerless core) + ./acp-tail.yml (agent-
|
||||
# loop + persistence + the ACP bridge), the SAME pieces cordis.yml shares — only
|
||||
# the LLM adapter differs: llm-replay here, llm-deepseek there. It can't reuse
|
||||
# ../base.yml because that loads llm-deepseek, whose apply() throws without
|
||||
# DEEPSEEK_API_KEY, killing a keyless replay run at boot.
|
||||
#
|
||||
# stdout is reserved for the ACP JSON-RPC protocol — no stdout logger (see
|
||||
# cordis.yml). The replay fixture path comes from $DSH_SNAPSHOT_FILE (and an
|
||||
# optional $DSH_SNAPSHOT_OVERRIDE sidecar), set by the snapshot harness.
|
||||
|
||||
- id: timer
|
||||
name: '@cordisjs/plugin-timer'
|
||||
|
||||
# Providerless core (everything base.yml has EXCEPT the llm-deepseek adapter).
|
||||
- id: base-core
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: '../base-core.yml'
|
||||
|
||||
# The replay adapter: short-circuits llm/stream with the recorded log's chunks,
|
||||
# in place of llm-deepseek.
|
||||
- id: llm-replay
|
||||
name: './src/llm-replay.ts'
|
||||
|
||||
# agent-loop + persistence + the ACP bridge — shared with cordis.yml.
|
||||
- id: acp-tail
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: './acp-tail.yml'
|
||||
@@ -1,4 +1,6 @@
|
||||
# The acp-agent plugin tree, loaded via @cordisjs/plugin-include.
|
||||
# The acp-agent plugin tree, loaded via @cordisjs/plugin-include. Also the
|
||||
# snapshot RECORD config (start.ts selects it for DSH_SNAPSHOT=record): a real
|
||||
# llm-deepseek run whose persisted log the snapshot harness harvests.
|
||||
#
|
||||
# CRITICAL: this example loads NO stdout logger (no @cordisjs/plugin-logger-
|
||||
# console, no stdio-chat). stdout is reserved for the ACP JSON-RPC protocol —
|
||||
@@ -12,38 +14,17 @@
|
||||
- id: timer
|
||||
name: '@cordisjs/plugin-timer'
|
||||
|
||||
# Shared provider/tool core (llm, sessions, system-prompt, tools, agents,
|
||||
# invariants, llm-deepseek, bash-local, tool-bash). Nested include resolved
|
||||
# relative to THIS file's directory.
|
||||
# Shared provider/tool core, INCLUDING the real llm-deepseek adapter. Nested
|
||||
# include resolved relative to THIS file's directory.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: '../base.yml'
|
||||
|
||||
# agent-loop with NO pre-created agents: ACP `session/new` creates them on
|
||||
# demand (unlike coding-agent, which pre-creates `main`).
|
||||
- id: agent-loop
|
||||
name: '@deepseek-ai/dsh-agent-loop'
|
||||
# agent-loop (no pre-created agents) + JSONL persistence + the ACP bridge.
|
||||
# Shared with the snapshot REPLAY config (cordis.snapshot.yml) so the three
|
||||
# acp-agent configs don't drift.
|
||||
- id: acp-tail
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
agents: []
|
||||
|
||||
# Durable session persistence — required by the ACP bridge for `session/load`.
|
||||
- id: session-persistence
|
||||
name: '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
config:
|
||||
root: './.sessions'
|
||||
|
||||
# The ACP bridge: wires AgentSideConnection to stdin/stdout.
|
||||
- id: acp
|
||||
name: '@deepseek-ai/dsh-acp'
|
||||
config:
|
||||
model: deepseek-v4-flash
|
||||
systemPrompt: |
|
||||
You are a coding assistant driven over the Agent Client Protocol.
|
||||
|
||||
Your only tools are bash (plus bash_output/bash_kill for background
|
||||
tasks). Do ALL file operations through bash: read with cat/sed/head,
|
||||
search with grep, write with heredocs (cat <<'EOF' > file), edit with
|
||||
sed or a rewrite. Each bash call runs in a fresh shell — pass workdir
|
||||
instead of cd. Check the [exit code: N] marker; verify your work. Keep
|
||||
answers brief and factual.
|
||||
path: './acp-tail.yml'
|
||||
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* Replay LLM plugin for snapshot tests.
|
||||
*
|
||||
* Installs a single `llm/stream` waterfall listener that short-circuits the
|
||||
* waterfall (never calls `next()`) and yields model streams reconstructed from
|
||||
* a recorded **session JSONL** fixture — so a snapshot test can boot the real
|
||||
* agent against a fixed model transcript with no API key. See
|
||||
* docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md.
|
||||
*
|
||||
* The fixture IS the persisted session log (`<scenario>/session.jsonl`): its
|
||||
* `assistant/chunk` events carry every {@link StreamChunk}, so grouping them by
|
||||
* `(turn, step)` reconstructs each `stream()` call's chunk sequence (one model
|
||||
* call per loop step — see packages/agent-loop/src/loop.ts). Recording is
|
||||
* therefore "run the real agent once and harvest the `.jsonl`", done by the
|
||||
* snapshot harness — this plugin does not record.
|
||||
*
|
||||
* Two failure modes are NOT reconstructable from `assistant/chunk` alone — a
|
||||
* pure throw before any chunk (e.g. an HTTP 401: the log holds only a
|
||||
* `turn/end {error}`, no chunks) and a cancel/hang (timing, not chunk content).
|
||||
* A scenario that needs those supplies an optional sidecar
|
||||
* (`<scenario>/replay.override.json`: a `ReplayEntry[]`) that REPLACES the
|
||||
* derived script.
|
||||
*
|
||||
* It lives in the example (not packages/) because it is example/test
|
||||
* infrastructure with one consumer, exactly like echo-agent's `mock-llm.ts`;
|
||||
* the capability-seams rule says not to split into a published package
|
||||
* preemptively.
|
||||
*
|
||||
* Plugin export shape: named `name`/`inject`/`Config`/`apply`, NO default
|
||||
* export (the cordis Loader's `unwrapExports` does `exports.default ?? exports`,
|
||||
* so a stray default would drop the namespace — see docs/postmortem/0001).
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import type { Context } from 'cordis'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { LlmError, assertNever } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
/**
|
||||
* One recorded model call. A discriminated union (not a bare `StreamChunk[]`)
|
||||
* so it can faithfully replay BOTH branches of the documented LLM failure
|
||||
* contract — an adapter may THROW from `stream()` or end with a `finish` error
|
||||
* chunk — plus a `hang` marker for cancellation scenarios (mirrors the
|
||||
* `MockAdapter` `hang` support in packages/agent-loop/tests).
|
||||
*
|
||||
* A `throw` entry carries any `chunks` the adapter emitted BEFORE it threw, so
|
||||
* a mid-stream transport failure (partial output then `STREAM_CLOSED`) replays
|
||||
* the partial chunks first and only then throws — exactly what the agent loop
|
||||
* saw live (it may already have emitted partial assistant chunks).
|
||||
*
|
||||
* The normal/finish-terminated cases are DERIVED from the session JSONL
|
||||
* ({@link deriveReplayScript}); only the throw and hang cases need a
|
||||
* hand-authored sidecar entry (a thrown stream leaves no terminal `finish` in
|
||||
* the log, so it cannot be derived as `chunks`).
|
||||
*/
|
||||
export type ReplayEntry =
|
||||
| { kind: 'chunks'; chunks: StreamChunk[] }
|
||||
| { kind: 'throw'; chunks: StreamChunk[]; message: string; code: string; status?: number }
|
||||
| { kind: 'hang' }
|
||||
|
||||
/** Resolved plugin configuration. */
|
||||
export interface ReplayConfig {
|
||||
/** Path to the per-scenario `session.jsonl` fixture (the recorded log). */
|
||||
file: string
|
||||
/**
|
||||
* Optional path to a `ReplayEntry[]` sidecar that REPLACES the derived
|
||||
* script. Used by the two scenarios not expressible as `assistant/chunk`
|
||||
* (pure throw-before-chunk, cancel/hang). Absent for normal scenarios.
|
||||
*/
|
||||
overrideFile?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a session `.jsonl` buffer into its event list. Line 0 is the session
|
||||
* header (a `{type:'session',…}` record), every subsequent non-empty line is a
|
||||
* {@link SessionEvent}. The header is skipped; malformed lines fail loud.
|
||||
*/
|
||||
export function parseSessionLog(text: string): SessionEvent[] {
|
||||
const lines = text.split('\n').filter(line => line.trim().length > 0)
|
||||
const events: SessionEvent[] = []
|
||||
// Skip line 0 (the header). A reader distinguishes it by its `type:'session'`
|
||||
// tag; we simply drop the first line, which the JSONL backend guarantees is
|
||||
// the header.
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const parsed: unknown = JSON.parse(lines[i] as string)
|
||||
events.push(parsed as SessionEvent)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstruct the per-`stream()` replay script from a recorded session log.
|
||||
*
|
||||
* The agent loop makes exactly one `ctx.llm.stream()` call per step and appends
|
||||
* every chunk as an `assistant/chunk` event tagged with the current
|
||||
* `(turn, step)`. Grouping those events by `(turn, step)` in log order
|
||||
* therefore yields one `{kind:'chunks'}` entry per model call, in call order.
|
||||
*
|
||||
* A group is only valid if it ends in a `finish` chunk — the adapter contract
|
||||
* guarantees a successful (or finish-error) stream terminates with `finish`,
|
||||
* and the loop relies on it. A group WITHOUT a terminal `finish` is the
|
||||
* fingerprint of a *thrown* `stream()` (the loop recorded the prefix chunks,
|
||||
* then an `error`/`turn/end`, but no `finish`): such a stream cannot be
|
||||
* faithfully replayed as `{kind:'chunks'}` (that would look like a clean stop),
|
||||
* so deriving it is an error — the scenario must supply a `replay.override.json`
|
||||
* sidecar with an explicit `throw` (or `hang`) entry instead. {@link
|
||||
* deriveReplayScript} throws, naming the offending `(turn, step)`, so a missing
|
||||
* override fails loud rather than silently replaying a thrown call as success.
|
||||
*/
|
||||
export function deriveReplayScript(events: SessionEvent[]): ReplayEntry[] {
|
||||
const script: ReplayEntry[] = []
|
||||
let currentKey: string | undefined
|
||||
let current: StreamChunk[] = []
|
||||
const close = (key: string | undefined, chunks: StreamChunk[]): void => {
|
||||
if (chunks.length === 0) return
|
||||
if (chunks[chunks.length - 1]?.type !== 'finish') {
|
||||
throw new Error(
|
||||
`llm-replay: model call ${key} ended without a finish chunk (a thrown stream); `
|
||||
+ 'this scenario needs a replay.override.json sidecar',
|
||||
)
|
||||
}
|
||||
script.push({ kind: 'chunks', chunks })
|
||||
}
|
||||
for (const event of events) {
|
||||
if (event.type !== 'assistant/chunk') continue
|
||||
const { turn, step, chunk } = event.data
|
||||
const key = `${turn}/${step}`
|
||||
if (key !== currentKey) {
|
||||
// A new (turn, step) — i.e. a new stream() call. Close the previous one
|
||||
// (skip the initial empty buffer before any chunk has been seen).
|
||||
close(currentKey, current)
|
||||
currentKey = key
|
||||
current = []
|
||||
}
|
||||
current.push(chunk)
|
||||
}
|
||||
close(currentKey, current)
|
||||
return script
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the replay script for a scenario: the sidecar override if present,
|
||||
* otherwise the script derived from the recorded session JSONL. Fail-loud if
|
||||
* the JSONL fixture is missing (the scenario was never recorded) — never
|
||||
* silently returns an empty script, so a coverage hole can't masquerade as a
|
||||
* passing replay.
|
||||
*/
|
||||
export function loadReplayScript(config: ReplayConfig): ReplayEntry[] {
|
||||
if (config.overrideFile !== undefined && existsSync(config.overrideFile)) {
|
||||
const parsed: unknown = JSON.parse(readFileSync(config.overrideFile, 'utf8'))
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new Error(`llm-replay: override is not a JSON array: ${config.overrideFile}`)
|
||||
}
|
||||
return parsed as ReplayEntry[]
|
||||
}
|
||||
if (!existsSync(config.file)) {
|
||||
throw new Error(`llm-replay: fixture not found: ${config.file} — run \`pnpm run test:snapshot:record\` first`)
|
||||
}
|
||||
return deriveReplayScript(parseSessionLog(readFileSync(config.file, 'utf8')))
|
||||
}
|
||||
|
||||
/** Yield a recorded stream back, honoring abort like a real adapter. */
|
||||
async function* replayEntry(entry: ReplayEntry, signal: AbortSignal | undefined): AsyncIterable<StreamChunk> {
|
||||
switch (entry.kind) {
|
||||
case 'chunks':
|
||||
for (const chunk of entry.chunks) {
|
||||
if (signal?.aborted) throw new Error('aborted')
|
||||
yield chunk
|
||||
}
|
||||
return
|
||||
case 'throw':
|
||||
// Replay the THROW branch of the LLM contract: emit whatever the adapter
|
||||
// streamed before it threw (so the loop sees the same partial output it
|
||||
// saw live), then throw the recorded error (e.g. a provider 401, or a
|
||||
// mid-stream STREAM_CLOSED after partial chunks).
|
||||
for (const chunk of entry.chunks) {
|
||||
if (signal?.aborted) throw new Error('aborted')
|
||||
yield chunk
|
||||
}
|
||||
throw new LlmError(entry.message, entry.code, entry.status)
|
||||
case 'hang':
|
||||
// Replay a stream that stalls until cancelled (mirrors MockAdapter): one
|
||||
// chunk, then wait for abort and surface it as the consumer expects.
|
||||
yield { type: 'block-start', index: 0, blockType: 'text' }
|
||||
yield { type: 'text-delta', index: 0, text: 'partial' }
|
||||
await new Promise<void>((_resolve, reject) => {
|
||||
if (signal?.aborted) { reject(new Error('aborted')); return }
|
||||
signal?.addEventListener('abort', () => { reject(new Error('aborted')) }, { once: true })
|
||||
})
|
||||
return
|
||||
default:
|
||||
// Closed local union: an unknown kind means malformed (hand-edited or
|
||||
// drifted) sidecar data — fail loud with a runtime diagnostic.
|
||||
return assertNever(entry, 'llm-replay replay entry')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the replay `llm/stream` listener on `ctx`. Returns the listener
|
||||
* disposer (so a fiber dispose removes it — HMR safety). Exported separately
|
||||
* from {@link apply} so unit tests can drive it without the Loader or env vars.
|
||||
*
|
||||
* Replay is POSITIONAL: the Nth `stream()` call serves the Nth script entry.
|
||||
* This is deterministic only with at most one model stream in flight at a time;
|
||||
* the snapshot harness runs one ACP session per scenario to guarantee that. The
|
||||
* cursor is advanced synchronously at listener-invocation time (not lazily
|
||||
* inside the generator) so call ORDER, not iteration order, fixes the mapping.
|
||||
*/
|
||||
export function installLlmReplay(ctx: Context, config: ReplayConfig): () => void {
|
||||
const entries = loadReplayScript(config)
|
||||
let cursor = 0
|
||||
return ctx.on('llm/stream', (options: GenerateOptions, _next) => {
|
||||
const index = cursor++
|
||||
const entry: ReplayEntry | undefined = entries[index]
|
||||
return (async function* () {
|
||||
if (entry === undefined) {
|
||||
throw new Error(
|
||||
`llm-replay: script exhausted — requested model call #${index + 1} but the fixture has only ${entries.length}; re-record the scenario`,
|
||||
)
|
||||
}
|
||||
yield* replayEntry(entry, options.signal)
|
||||
})()
|
||||
})
|
||||
}
|
||||
|
||||
export const name = 'llm-replay'
|
||||
export const inject = ['llm']
|
||||
|
||||
export interface Config {
|
||||
/** Override the fixture path; defaults to `$DSH_SNAPSHOT_FILE`. */
|
||||
file?: string
|
||||
/** Override the sidecar path; defaults to `$DSH_SNAPSHOT_OVERRIDE`. */
|
||||
overrideFile?: string
|
||||
}
|
||||
|
||||
export function apply(ctx: Context, config: Config = {}): void {
|
||||
const file = config.file ?? process.env.DSH_SNAPSHOT_FILE
|
||||
if (file === undefined || file.length === 0) {
|
||||
throw new Error('llm-replay: a fixture path is required (Config.file or $DSH_SNAPSHOT_FILE)')
|
||||
}
|
||||
const overrideFile = config.overrideFile ?? process.env.DSH_SNAPSHOT_OVERRIDE
|
||||
installLlmReplay(ctx, overrideFile === undefined || overrideFile.length === 0 ? { file } : { file, overrideFile })
|
||||
}
|
||||
@@ -2,20 +2,35 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { Context } from 'cordis'
|
||||
import Loader from '@cordisjs/plugin-loader'
|
||||
|
||||
// Snapshot-test modes (set by the snapshot harness via env):
|
||||
// DSH_SNAPSHOT=replay — load cordis.snapshot.yml (providerless; llm-replay
|
||||
// serves a recorded session log). Skip .env so a stray
|
||||
// key can never trigger a live model call.
|
||||
// DSH_SNAPSHOT=record — load the normal cordis.yml (the real llm-deepseek
|
||||
// adapter + persistence) so a real run can be harvested
|
||||
// (the persistence root is redirected by env).
|
||||
// Absent — the normal demo (cordis.yml), driven by a real editor.
|
||||
const snapshotMode = process.env.DSH_SNAPSHOT
|
||||
const configPath = snapshotMode === 'replay' ? './cordis.snapshot.yml' : './cordis.yml'
|
||||
|
||||
// Load DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL from a gitignored repo-root .env
|
||||
// (Node native). Absent file is fine — the environment may already carry them.
|
||||
// In REPLAY mode we deliberately skip this: replay must never reach the network,
|
||||
// so we don't want a present .env to enable a live call.
|
||||
//
|
||||
// IMPORTANT: this server speaks ACP JSON-RPC on stdout. Do NOT add any
|
||||
// stdout logging here or in cordis.yml — it would corrupt the protocol frames.
|
||||
// A present-but-unreadable/malformed .env is a real misconfiguration: surface
|
||||
// it on STDERR (never stdout) rather than silently running with the wrong env.
|
||||
try {
|
||||
process.loadEnvFile(new URL('../../.env', import.meta.url).pathname)
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException | null)?.code !== 'ENOENT') {
|
||||
process.stderr.write(`acp-agent: failed to load .env: ${String(error)}\n`)
|
||||
if (snapshotMode !== 'replay') {
|
||||
try {
|
||||
process.loadEnvFile(new URL('../../.env', import.meta.url).pathname)
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException | null)?.code !== 'ENOENT') {
|
||||
process.stderr.write(`acp-agent: failed to load .env: ${String(error)}\n`)
|
||||
}
|
||||
// ENOENT (no .env) is fine — rely on the ambient environment.
|
||||
}
|
||||
// ENOENT (no .env) is fine — rely on the ambient environment.
|
||||
}
|
||||
|
||||
// Resolve relative cordis.yml paths from the repo root no matter where the
|
||||
@@ -29,6 +44,20 @@ await ctx.plugin(Loader)
|
||||
await ctx.loader.create({
|
||||
name: '@cordisjs/plugin-include',
|
||||
config: {
|
||||
path: './cordis.yml',
|
||||
path: configPath,
|
||||
},
|
||||
})
|
||||
|
||||
// Graceful shutdown for snapshot runs (both replay and record): when the client
|
||||
// closes our stdin (it is done driving the session), dispose the whole context.
|
||||
// Disposal awaits the agent-loop teardown and the persistence backend's final
|
||||
// `session/flush`, so the session `.jsonl` is fully written before the process
|
||||
// exits and the harness harvests it (and the subprocess exits cleanly so the
|
||||
// harness's waitForExit resolves). (In a normal editor session stdin stays open
|
||||
// for the connection's lifetime; the editor kills the process, so this never
|
||||
// fires.)
|
||||
if (snapshotMode !== undefined) {
|
||||
process.stdin.on('end', () => {
|
||||
void ctx.fiber.dispose().then(() => { process.exit(0) })
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { readFile, readdir, writeFile } from 'node:fs/promises'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { type InputScript, runScenario } from './snapshot-harness.ts'
|
||||
import { type NormalizeContext, normalizeSessionLog, normalizeStdout } from './snapshot-normalize.ts'
|
||||
|
||||
/**
|
||||
* ACP snapshot tests (REPLAY by default, keyless). Each scenario under
|
||||
* `snapshots/<name>/` ships an `input.json` (the client stdin script) and a
|
||||
* recorded `session.jsonl` fixture; replay boots the real acp-agent subprocess,
|
||||
* drives it, and diffs the normalized stdout transcript (and, for model
|
||||
* scenarios, the re-persisted session log) against committed goldens.
|
||||
*
|
||||
* `pnpm run test:snapshot:record` (DSH_SNAPSHOT=record + -u) re-records the
|
||||
* fixtures against the real API and refreshes the goldens in one pass.
|
||||
*/
|
||||
|
||||
const SNAPSHOTS_DIR = join(dirname(fileURLToPath(import.meta.url)), 'snapshots')
|
||||
const RECORDING = process.env.DSH_SNAPSHOT === 'record'
|
||||
|
||||
/** 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, recorded: false },
|
||||
{ name: 'reject-extra-dirs', hasModelTurn: false, recorded: false },
|
||||
{ name: 'text-turn', hasModelTurn: true, recorded: true },
|
||||
{ name: 'tool-call-turn', hasModelTurn: true, recorded: true },
|
||||
{ name: 'workspace-edit', 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}`, () => {
|
||||
// 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')
|
||||
const workspaceDir = join(dir, 'workspace')
|
||||
const result = await runScenario(input, {
|
||||
mode: RECORDING ? 'record' : 'replay',
|
||||
fixtureFile: join(dir, 'session.jsonl'),
|
||||
...existsSync(overrideFile) ? { overrideFile } : {},
|
||||
...existsSync(workspaceDir) ? { workspaceDir } : {},
|
||||
})
|
||||
|
||||
const ctx: NormalizeContext = {
|
||||
sessionIds: result.sessionId !== undefined ? [result.sessionId] : [],
|
||||
cwd: result.cwd,
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
await expect(normalizeStdout(result.rawStdout, ctx))
|
||||
.toMatchFileSnapshot(join(dir, 'stdout.golden.jsonl'))
|
||||
|
||||
if (scenario.hasModelTurn) {
|
||||
expect(result.sessionLog, 'a model scenario must persist a session log').toBeDefined()
|
||||
await expect(normalizeSessionLog(result.sessionLog as string, ctx))
|
||||
.toMatchFileSnapshot(join(dir, 'session.golden.jsonl'))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('snapshot fixtures', () => {
|
||||
it('every scenario directory is registered (no orphans)', async () => {
|
||||
// toMatchFileSnapshot does not prune orphaned golden/fixture files, so a
|
||||
// renamed/removed scenario could leave a stale dir that nothing exercises.
|
||||
// Fail loud on any snapshots/<dir> not present in SCENARIOS.
|
||||
const entries = await readdir(SNAPSHOTS_DIR, { withFileTypes: true })
|
||||
const onDisk = entries.filter(e => e.isDirectory()).map(e => e.name).sort()
|
||||
const registered = SCENARIOS.map(s => s.name).sort()
|
||||
expect(onDisk).toEqual(registered)
|
||||
})
|
||||
|
||||
it('every registered scenario has its required fixture files', async () => {
|
||||
for (const { name } of SCENARIOS) {
|
||||
const dir = join(SNAPSHOTS_DIR, name)
|
||||
expect(existsSync(join(dir, 'input.json')), `${name}/input.json`).toBe(true)
|
||||
expect(existsSync(join(dir, 'session.jsonl')), `${name}/session.jsonl`).toBe(true)
|
||||
expect(existsSync(join(dir, 'stdout.golden.jsonl')), `${name}/stdout.golden.jsonl`).toBe(true)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,293 @@
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import LlmService, { GenerateOptions, LlmAdapter, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import {
|
||||
type ReplayEntry,
|
||||
deriveReplayScript,
|
||||
installLlmReplay,
|
||||
loadReplayScript,
|
||||
parseSessionLog,
|
||||
} from '../src/llm-replay.ts'
|
||||
|
||||
/**
|
||||
* Unit tests for the replay llm/stream plugin. These drive the listener through
|
||||
* the REAL LlmService waterfall (not a hand-rolled stub) so they verify the
|
||||
* actual seam the snapshot harness depends on, plus the pure
|
||||
* derive/parse/load helpers that turn a recorded session JSONL into a script.
|
||||
*/
|
||||
|
||||
const TEXT_CHUNKS: StreamChunk[] = [
|
||||
{ type: 'block-start', index: 0, blockType: 'text' },
|
||||
{ type: 'text-delta', index: 0, text: 'hi' },
|
||||
{ type: 'block-end', index: 0, block: { type: 'text', text: 'hi' } },
|
||||
{ type: 'usage', usage: { inputTokens: 1, outputTokens: 1 } },
|
||||
{ type: 'finish', reason: { kind: 'stop' } },
|
||||
]
|
||||
|
||||
/** Build a minimal session-JSONL string: a header line + the given events. */
|
||||
function sessionJsonl(events: SessionEvent[]): string {
|
||||
const header = JSON.stringify({ type: 'session', version: 1, id: 's1', createdAt: 0 })
|
||||
return [header, ...events.map(e => JSON.stringify(e))].join('\n') + '\n'
|
||||
}
|
||||
|
||||
/** A SessionEvent of type assistant/chunk for (turn, step). */
|
||||
function chunkEvent(seq: number, turn: number, step: number, chunk: StreamChunk): SessionEvent {
|
||||
return { type: 'assistant/chunk', seq, time: 0, data: { turn, step, chunk } }
|
||||
}
|
||||
|
||||
let dir: string
|
||||
let file: string
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), 'llm-replay-spec-'))
|
||||
file = join(dir, 'session.jsonl')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(dir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
async function drain(iter: AsyncIterable<StreamChunk>): Promise<StreamChunk[]> {
|
||||
const out: StreamChunk[] = []
|
||||
for await (const chunk of iter) out.push(chunk)
|
||||
return out
|
||||
}
|
||||
|
||||
describe('parseSessionLog', () => {
|
||||
it('skips the header line and parses each event', () => {
|
||||
const events = [chunkEvent(1, 1, 1, TEXT_CHUNKS[0] as StreamChunk)]
|
||||
expect(parseSessionLog(sessionJsonl(events))).toEqual(events)
|
||||
})
|
||||
|
||||
it('ignores blank lines', () => {
|
||||
const header = JSON.stringify({ type: 'session', version: 1, id: 's1', createdAt: 0 })
|
||||
const ev = chunkEvent(1, 1, 1, TEXT_CHUNKS[0] as StreamChunk)
|
||||
expect(parseSessionLog(`${header}\n\n${JSON.stringify(ev)}\n\n`)).toEqual([ev])
|
||||
})
|
||||
})
|
||||
|
||||
describe('deriveReplayScript', () => {
|
||||
it('groups assistant/chunk by (turn, step) into one entry per stream() call', () => {
|
||||
const events: SessionEvent[] = TEXT_CHUNKS.map((c, i) => chunkEvent(i + 1, 1, 1, c))
|
||||
expect(deriveReplayScript(events)).toEqual([{ kind: 'chunks', chunks: TEXT_CHUNKS }])
|
||||
})
|
||||
|
||||
it('produces one entry per distinct (turn, step), in log order', () => {
|
||||
const callA = TEXT_CHUNKS
|
||||
const callB: StreamChunk[] = [
|
||||
{ type: 'block-start', index: 0, blockType: 'text' },
|
||||
{ type: 'text-delta', index: 0, text: 'two' },
|
||||
{ type: 'finish', reason: { kind: 'stop' } },
|
||||
]
|
||||
let seq = 1
|
||||
const events: SessionEvent[] = [
|
||||
...callA.map(c => chunkEvent(seq++, 1, 1, c)),
|
||||
...callB.map(c => chunkEvent(seq++, 1, 2, c)), // same turn, next step
|
||||
]
|
||||
expect(deriveReplayScript(events)).toEqual([
|
||||
{ kind: 'chunks', chunks: callA },
|
||||
{ kind: 'chunks', chunks: callB },
|
||||
])
|
||||
})
|
||||
|
||||
it('separates calls across turns too', () => {
|
||||
let seq = 1
|
||||
const events: SessionEvent[] = [
|
||||
...TEXT_CHUNKS.map(c => chunkEvent(seq++, 1, 1, c)),
|
||||
...TEXT_CHUNKS.map(c => chunkEvent(seq++, 2, 1, c)), // new turn, step resets to 1
|
||||
]
|
||||
expect(deriveReplayScript(events)).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('ignores non-assistant/chunk events', () => {
|
||||
let seq = 1
|
||||
const events: SessionEvent[] = [
|
||||
{ type: 'turn/start', seq: seq++, time: 0, data: { turn: 1, trigger: { kind: 'continuation' } } },
|
||||
...TEXT_CHUNKS.map(c => chunkEvent(seq++, 1, 1, c)),
|
||||
{ type: 'turn/end', seq: seq++, time: 0, data: { turn: 1, reason: { kind: 'completed' } } },
|
||||
]
|
||||
expect(deriveReplayScript(events)).toEqual([{ kind: 'chunks', chunks: TEXT_CHUNKS }])
|
||||
})
|
||||
|
||||
it('returns an empty script for a log with no assistant/chunk events', () => {
|
||||
expect(deriveReplayScript([])).toEqual([])
|
||||
})
|
||||
|
||||
it('keeps a finish-error chunk in the derived entry (replays naturally)', () => {
|
||||
const errChunks: StreamChunk[] = [
|
||||
{ type: 'block-start', index: 0, blockType: 'text' },
|
||||
{ type: 'finish', reason: { kind: 'error', message: 'boom', code: 'X' } },
|
||||
]
|
||||
const events = errChunks.map((c, i) => chunkEvent(i + 1, 1, 1, c))
|
||||
expect(deriveReplayScript(events)).toEqual([{ kind: 'chunks', chunks: errChunks }])
|
||||
})
|
||||
|
||||
it('throws on a group that lacks a terminal finish chunk (a thrown stream)', () => {
|
||||
// A thrown stream(): prefix chunks logged, then error/turn/end, NO finish.
|
||||
const events: SessionEvent[] = [
|
||||
chunkEvent(1, 1, 1, { type: 'block-start', index: 0, blockType: 'text' }),
|
||||
chunkEvent(2, 1, 1, { type: 'text-delta', index: 0, text: 'par' }),
|
||||
{ type: 'turn/end', seq: 3, time: 0, data: { turn: 1, reason: { kind: 'error', message: 'x' } } },
|
||||
]
|
||||
expect(() => deriveReplayScript(events)).toThrow(/without a finish chunk.*replay\.override\.json/s)
|
||||
})
|
||||
|
||||
it('names the offending (turn, step) when a group is incomplete', () => {
|
||||
const events: SessionEvent[] = [
|
||||
chunkEvent(1, 2, 3, { type: 'block-start', index: 0, blockType: 'text' }),
|
||||
]
|
||||
expect(() => deriveReplayScript(events)).toThrow(/2\/3/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('loadReplayScript', () => {
|
||||
it('derives from the session JSONL when no override is present', () => {
|
||||
writeFileSync(file, sessionJsonl(TEXT_CHUNKS.map((c, i) => chunkEvent(i + 1, 1, 1, c))), 'utf8')
|
||||
expect(loadReplayScript({ file })).toEqual([{ kind: 'chunks', chunks: TEXT_CHUNKS }])
|
||||
})
|
||||
|
||||
it('uses the sidecar override when present, ignoring the JSONL', () => {
|
||||
writeFileSync(file, sessionJsonl([]), 'utf8')
|
||||
const overrideFile = join(dir, 'replay.override.json')
|
||||
const override: ReplayEntry[] = [{ kind: 'throw', chunks: [], message: '401', code: 'AUTH', status: 401 }]
|
||||
writeFileSync(overrideFile, JSON.stringify(override), 'utf8')
|
||||
expect(loadReplayScript({ file, overrideFile })).toEqual(override)
|
||||
})
|
||||
|
||||
it('falls back to the JSONL when the override path is set but absent', () => {
|
||||
writeFileSync(file, sessionJsonl(TEXT_CHUNKS.map((c, i) => chunkEvent(i + 1, 1, 1, c))), 'utf8')
|
||||
expect(loadReplayScript({ file, overrideFile: join(dir, 'nope.json') }))
|
||||
.toEqual([{ kind: 'chunks', chunks: TEXT_CHUNKS }])
|
||||
})
|
||||
|
||||
it('fails loud when the fixture is missing', () => {
|
||||
expect(() => loadReplayScript({ file: join(dir, 'absent.jsonl') })).toThrow(/fixture not found/)
|
||||
})
|
||||
|
||||
it('throws when the override is not a JSON array', () => {
|
||||
writeFileSync(file, sessionJsonl([]), 'utf8')
|
||||
const overrideFile = join(dir, 'replay.override.json')
|
||||
writeFileSync(overrideFile, '{"not":"array"}', 'utf8')
|
||||
expect(() => loadReplayScript({ file, overrideFile })).toThrow(/not a JSON array/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('installLlmReplay (through the real waterfall)', () => {
|
||||
function writeLog(...calls: StreamChunk[][]): void {
|
||||
let seq = 1
|
||||
const events: SessionEvent[] = []
|
||||
calls.forEach((chunks, step) => {
|
||||
for (const c of chunks) events.push(chunkEvent(seq++, 1, step + 1, c))
|
||||
})
|
||||
writeFileSync(file, sessionJsonl(events), 'utf8')
|
||||
}
|
||||
|
||||
it('serves derived chunks back, short-circuiting the adapter', async () => {
|
||||
writeLog(TEXT_CHUNKS)
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
// No adapter registered for 'm' — replay must not reach it.
|
||||
installLlmReplay(ctx, { file })
|
||||
expect(await drain(ctx.llm.stream({ model: 'm', messages: [] }))).toEqual(TEXT_CHUNKS)
|
||||
})
|
||||
|
||||
it('serves the Nth call the Nth derived entry (positional)', async () => {
|
||||
const second: StreamChunk[] = [
|
||||
{ type: 'block-start', index: 0, blockType: 'text' },
|
||||
{ type: 'text-delta', index: 0, text: 'two' },
|
||||
{ type: 'finish', reason: { kind: 'stop' } },
|
||||
]
|
||||
writeLog(TEXT_CHUNKS, second)
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
installLlmReplay(ctx, { file })
|
||||
expect(await drain(ctx.llm.stream({ model: 'm', messages: [] }))).toEqual(TEXT_CHUNKS)
|
||||
expect(await drain(ctx.llm.stream({ model: 'm', messages: [] }))).toEqual(second)
|
||||
})
|
||||
|
||||
it('replays a sidecar throw-entry as an LlmError with code/status, after its prefix chunks', async () => {
|
||||
writeFileSync(file, sessionJsonl([]), 'utf8')
|
||||
const overrideFile = join(dir, 'replay.override.json')
|
||||
const partial: StreamChunk[] = [{ type: 'block-start', index: 0, blockType: 'text' }]
|
||||
writeFileSync(overrideFile, JSON.stringify([
|
||||
{ kind: 'throw', chunks: partial, message: 'unauthorized', code: 'AUTH', status: 401 },
|
||||
]), 'utf8')
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
installLlmReplay(ctx, { file, overrideFile })
|
||||
|
||||
const seen: StreamChunk[] = []
|
||||
await expect((async () => {
|
||||
for await (const c of ctx.llm.stream({ model: 'm', messages: [] })) seen.push(c)
|
||||
})()).rejects.toMatchObject({ message: 'unauthorized', code: 'AUTH', status: 401 })
|
||||
expect(seen).toEqual(partial)
|
||||
})
|
||||
|
||||
it('replays a sidecar hang-entry that surfaces abort when the signal fires', async () => {
|
||||
writeFileSync(file, sessionJsonl([]), 'utf8')
|
||||
const overrideFile = join(dir, 'replay.override.json')
|
||||
writeFileSync(overrideFile, JSON.stringify([{ kind: 'hang' }]), 'utf8')
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
installLlmReplay(ctx, { file, overrideFile })
|
||||
|
||||
const controller = new AbortController()
|
||||
const iterator = ctx.llm.stream({ model: 'm', messages: [], signal: controller.signal })[Symbol.asyncIterator]()
|
||||
// Deterministically consume the two pre-hang chunks (no sleep), then abort
|
||||
// and assert the next pull rejects — event-driven, per the no-sleeps rule.
|
||||
expect((await iterator.next()).value).toMatchObject({ type: 'block-start' })
|
||||
expect((await iterator.next()).value).toMatchObject({ type: 'text-delta' })
|
||||
controller.abort()
|
||||
await expect(iterator.next()).rejects.toThrow('aborted')
|
||||
})
|
||||
|
||||
it('fails loud when the script is exhausted', async () => {
|
||||
writeLog(TEXT_CHUNKS)
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
installLlmReplay(ctx, { file })
|
||||
await drain(ctx.llm.stream({ model: 'm', messages: [] }))
|
||||
await expect(drain(ctx.llm.stream({ model: 'm', messages: [] }))).rejects.toThrow(/exhausted/)
|
||||
})
|
||||
|
||||
it('aborts mid-replay when the signal is already set', async () => {
|
||||
writeLog(TEXT_CHUNKS)
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
installLlmReplay(ctx, { file })
|
||||
const controller = new AbortController()
|
||||
controller.abort()
|
||||
await expect(drain(ctx.llm.stream({ model: 'm', messages: [], signal: controller.signal })))
|
||||
.rejects.toThrow('aborted')
|
||||
})
|
||||
|
||||
it('removes the waterfall listener when the owning fiber is disposed (HMR safety)', async () => {
|
||||
writeLog(TEXT_CHUNKS, TEXT_CHUNKS)
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
|
||||
// A real adapter to fall through to AFTER dispose, proving the listener is gone.
|
||||
class FallthroughAdapter extends LlmAdapter {
|
||||
async * stream(_options: GenerateOptions): AsyncIterable<StreamChunk> {
|
||||
yield { type: 'finish', reason: { kind: 'stop' } }
|
||||
}
|
||||
}
|
||||
ctx.llm.registerAdapter(['m'], new FallthroughAdapter())
|
||||
|
||||
const fiber = await ctx.plugin(Object.assign((inner: Context) => {
|
||||
installLlmReplay(inner, { file })
|
||||
}, { inject: ['llm'] }))
|
||||
|
||||
// While installed, replay short-circuits to the derived fixture ('hi').
|
||||
expect(await drain(ctx.llm.stream({ model: 'm', messages: [] }))).toEqual(TEXT_CHUNKS)
|
||||
|
||||
await fiber.dispose()
|
||||
// After dispose the listener is gone; the call reaches the real adapter.
|
||||
expect(await drain(ctx.llm.stream({ model: 'm', messages: [] })))
|
||||
.toEqual([{ type: 'finish', reason: { kind: 'stop' } }])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* Shared harness for the ACP snapshot tests. A plain module (NOT a *.spec.ts /
|
||||
* *.snapshot.ts) so importing it never re-registers another file's tests.
|
||||
*
|
||||
* It boots the REAL examples/acp-agent subprocess via the cordis Loader (so the
|
||||
* export-shape bug class stays guarded — see docs/postmortem/0001), drives it
|
||||
* over real ACP JSON-RPC stdio with a deterministic input script, tees raw
|
||||
* stdout (for the golden + a purity check) into an SDK `ClientSideConnection`,
|
||||
* and — in record mode — harvests the persisted session JSONL after a graceful
|
||||
* shutdown flush. Two pure normalizers turn the captured stdout frames and the
|
||||
* session-log events into stable, snapshot-able text.
|
||||
*
|
||||
* See docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md.
|
||||
*/
|
||||
|
||||
import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'
|
||||
import { cp, mkdtemp, readFile, readdir, rm } from 'node:fs/promises'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { Readable, Writable } from 'node:stream'
|
||||
import {
|
||||
ClientSideConnection,
|
||||
ndJsonStream,
|
||||
PROTOCOL_VERSION,
|
||||
type Agent as AcpAgent,
|
||||
type Client,
|
||||
type RequestPermissionRequest,
|
||||
type RequestPermissionResponse,
|
||||
type SessionNotification,
|
||||
} from '@agentclientprotocol/sdk'
|
||||
|
||||
const startScript = fileURLToPath(new URL('../start.ts', import.meta.url))
|
||||
const tsxLoader = fileURLToPath(import.meta.resolve('tsx'))
|
||||
// The repo-root tsconfig: dev/test run UNBUILT and the `@deepseek-ai/dsh-*`
|
||||
// imports resolve through its `paths` map. The child's cwd is a temp dir
|
||||
// OUTSIDE the repo, so tsx's upward search would miss it — point tsx at the
|
||||
// repo tsconfig explicitly (same fix the e2e harness uses). Repo root is four
|
||||
// levels up from this file (examples/acp-agent/tests).
|
||||
const repoTsconfig = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
|
||||
|
||||
/**
|
||||
* One step of a scenario's deterministic input script (`input.json`). The
|
||||
* 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: 'newSessionExpectError'; additionalDirectories?: string[] }
|
||||
| { 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. */
|
||||
export interface InputScript {
|
||||
steps: InputStep[]
|
||||
}
|
||||
|
||||
/** The result of running a scenario: raw stdout + the harvested session log. */
|
||||
export interface RunResult {
|
||||
/** Raw stdout bytes (decoded utf8), every newline-delimited JSON-RPC frame. */
|
||||
rawStdout: string
|
||||
/** stderr (for diagnostics on failure). */
|
||||
stderr: string
|
||||
/** The session id the server issued (undefined if no session was created). */
|
||||
sessionId?: string
|
||||
/** The temp cwd the session ran in (the bash workspace). */
|
||||
cwd: string
|
||||
/** The persisted session log's content, if one was produced. */
|
||||
sessionLog?: string
|
||||
}
|
||||
|
||||
interface RunOptions {
|
||||
/** `replay` (default, keyless) or `record` (real API, harvests the log). */
|
||||
mode: 'replay' | 'record'
|
||||
/** The recorded session JSONL fixture path (replay reads it; record writes near it). */
|
||||
fixtureFile: string
|
||||
/** Optional sidecar override path (replay). */
|
||||
overrideFile?: string
|
||||
/**
|
||||
* Optional `<scenario>/workspace/` directory whose contents are copied into
|
||||
* the temp cwd BEFORE the run — the standard way to seed files the agent
|
||||
* operates on (a file to read, edit, or grep). Absent for scenarios that
|
||||
* start from an empty workspace.
|
||||
*/
|
||||
workspaceDir?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a scenario end-to-end against a freshly-spawned subprocess. Owns the
|
||||
* child and its temp dirs; always tears them down. Returns the captured stdout
|
||||
* and (record mode) the harvested session-log path.
|
||||
*/
|
||||
export async function runScenario(input: InputScript, opts: RunOptions): Promise<RunResult> {
|
||||
const cwd = await mkdtemp(join(tmpdir(), 'acp-snap-cwd-'))
|
||||
const sessionsRoot = await mkdtemp(join(tmpdir(), 'acp-snap-sessions-'))
|
||||
// Everything past the temp-dir creation runs under a try/finally that always
|
||||
// removes both dirs — so a failure in workspace seeding, spawn, or any step
|
||||
// never leaks them (the "e2e tests own their resources" rule).
|
||||
let child: ChildProcessWithoutNullStreams | undefined
|
||||
let sessionId: string | undefined
|
||||
let sessionLog: string | undefined
|
||||
const rawBuffers: Buffer[] = []
|
||||
const stderrChunks: string[] = []
|
||||
try {
|
||||
// Seed the workspace if the scenario ships one (a file the agent reads/edits).
|
||||
// Copied into the temp cwd so the agent's bash tools see it; the goldens
|
||||
// normalize the cwd, so the seeded paths stay stable across runs.
|
||||
if (opts.workspaceDir !== undefined && existsSync(opts.workspaceDir)) {
|
||||
await cp(opts.workspaceDir, cwd, { recursive: true })
|
||||
}
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
TSX_TSCONFIG_PATH: repoTsconfig,
|
||||
DSH_SNAPSHOT: opts.mode,
|
||||
DSH_SNAPSHOT_FILE: opts.fixtureFile,
|
||||
DSH_SNAPSHOT_SESSIONS_ROOT: sessionsRoot,
|
||||
...opts.overrideFile !== undefined ? { DSH_SNAPSHOT_OVERRIDE: opts.overrideFile } : {},
|
||||
}
|
||||
|
||||
child = spawn(
|
||||
process.execPath,
|
||||
['--import', tsxLoader, startScript],
|
||||
{ cwd, env, stdio: ['pipe', 'pipe', 'pipe'] },
|
||||
)
|
||||
|
||||
child.stderr.setEncoding('utf8')
|
||||
child.stderr.on('data', (c: string) => stderrChunks.push(c))
|
||||
|
||||
// Tee raw stdout: accumulate the bytes for the golden + purity check, and ALSO
|
||||
// feed the same bytes to the SDK client through a passthrough. Buffer the raw
|
||||
// bytes (not per-chunk utf8 strings) and decode once at the end, so a
|
||||
// multibyte sequence split across two 'data' events can't corrupt the golden.
|
||||
const passthrough = new Readable({ read() {} })
|
||||
child.stdout.on('data', (buf: Buffer) => {
|
||||
rawBuffers.push(buf)
|
||||
passthrough.push(buf)
|
||||
})
|
||||
child.stdout.on('end', () => passthrough.push(null))
|
||||
|
||||
const stream = ndJsonStream(
|
||||
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> {
|
||||
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> {
|
||||
return Promise.resolve({ outcome: { outcome: 'cancelled' } })
|
||||
},
|
||||
})
|
||||
const client = new ClientSideConnection(makeClient, stream)
|
||||
|
||||
for (const step of input.steps) {
|
||||
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.
|
||||
child.stdin.end()
|
||||
await waitForExit(child)
|
||||
// Harvest the persisted log (if any) while the temp dirs still exist.
|
||||
const sessionLogPath = await findSessionLog(sessionsRoot)
|
||||
if (sessionLogPath !== undefined) sessionLog = await readFile(sessionLogPath, 'utf8')
|
||||
} finally {
|
||||
// Failure-safe teardown: kill a still-running child and drop the temp dirs
|
||||
// even if seeding/spawn/a step/harvest threw, so a flaky run never leaks a
|
||||
// process or dir. `child` is undefined only if spawn itself threw.
|
||||
if (child !== undefined && child.exitCode === null && child.signalCode === null) {
|
||||
child.kill('SIGKILL')
|
||||
await waitForExit(child)
|
||||
}
|
||||
await rm(cwd, { recursive: true, force: true })
|
||||
await rm(sessionsRoot, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
return {
|
||||
rawStdout: Buffer.concat(rawBuffers).toString('utf8'),
|
||||
stderr: stderrChunks.join(''),
|
||||
cwd,
|
||||
...sessionId !== undefined ? { sessionId } : {},
|
||||
...sessionLog !== undefined ? { sessionLog } : {},
|
||||
}
|
||||
}
|
||||
|
||||
/** Drive one input step over the client connection. */
|
||||
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> {
|
||||
switch (step.op) {
|
||||
case 'initialize':
|
||||
await client.initialize({
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
clientCapabilities: step.terminalOutput === true ? { _meta: { terminal_output: true } } : {},
|
||||
})
|
||||
return
|
||||
case 'newSession': {
|
||||
const { sessionId } = await client.newSession({ cwd, mcpServers: [] })
|
||||
setSessionId(sessionId)
|
||||
return
|
||||
}
|
||||
case 'newSessionExpectError': {
|
||||
// The bridge rejects a session/new that widens the workspace scope
|
||||
// (non-empty additionalDirectories / mcpServers — unimplemented). The SDK
|
||||
// surfaces that as a rejected RPC; swallow it so the run completes and the
|
||||
// error frame is captured in the transcript.
|
||||
await client.newSession({
|
||||
cwd,
|
||||
mcpServers: [],
|
||||
...step.additionalDirectories !== undefined ? { additionalDirectories: step.additionalDirectories } : {},
|
||||
}).then(
|
||||
() => { throw new Error('snapshot-harness: expected session/new to be rejected but it succeeded') },
|
||||
() => { /* expected: the bridge rejected the unsupported workspace scope */ },
|
||||
)
|
||||
return
|
||||
}
|
||||
case 'prompt': {
|
||||
const sessionId = getSessionId()
|
||||
if (sessionId === undefined) throw new Error('snapshot-harness: prompt before newSession')
|
||||
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')
|
||||
await client.cancel({ sessionId })
|
||||
return
|
||||
}
|
||||
default:
|
||||
throw new Error(`snapshot-harness: unknown input op ${JSON.stringify(step)}`)
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolve once the child process exits (any code/signal). */
|
||||
function waitForExit(child: ChildProcessWithoutNullStreams): Promise<void> {
|
||||
if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve()
|
||||
return new Promise<void>(resolve => child.once('exit', () => { resolve() }))
|
||||
}
|
||||
|
||||
/** Find the single produced `.jsonl` session log under a sessions root, if any. */
|
||||
async function findSessionLog(root: string): Promise<string | undefined> {
|
||||
let cwdDirs: string[]
|
||||
try {
|
||||
cwdDirs = await readdir(root)
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
for (const dir of cwdDirs) {
|
||||
const sub = join(root, dir)
|
||||
let files: string[]
|
||||
try {
|
||||
files = await readdir(sub)
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
const jsonl = files.find(f => f.endsWith('.jsonl'))
|
||||
if (jsonl !== undefined) return join(sub, jsonl)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { type NormalizeContext, normalizeSessionLog, normalizeStdout } from '../tests/snapshot-normalize.ts'
|
||||
|
||||
/**
|
||||
* Unit tests for the pure snapshot normalizers. Live as a *.spec.ts (runs in
|
||||
* the default unit gate) and import the harness-side normalizers directly.
|
||||
*/
|
||||
|
||||
const ctx: NormalizeContext = {
|
||||
sessionIds: ['11111111-2222-3333-4444-555555555555'],
|
||||
cwd: '/tmp/acp-snap-cwd-abc123',
|
||||
}
|
||||
|
||||
describe('normalizeStdout', () => {
|
||||
it('rewrites JSON-RPC ids to a stable first-seen sequence', () => {
|
||||
const raw = [
|
||||
JSON.stringify({ jsonrpc: '2.0', id: 42, method: 'initialize' }),
|
||||
JSON.stringify({ jsonrpc: '2.0', id: 42, result: {} }),
|
||||
JSON.stringify({ jsonrpc: '2.0', id: 99, method: 'session/new' }),
|
||||
].join('\n')
|
||||
const out = normalizeStdout(raw, ctx)
|
||||
expect(out).toContain('"id":1')
|
||||
expect(out).toContain('"id":2')
|
||||
expect(out).not.toContain('42')
|
||||
expect(out).not.toContain('99')
|
||||
})
|
||||
|
||||
it('scrubs the cwd and session id anywhere they appear', () => {
|
||||
const raw = JSON.stringify({
|
||||
jsonrpc: '2.0', method: 'session/update',
|
||||
params: { sessionId: ctx.sessionIds[0], cwd: ctx.cwd, note: `at ${ctx.cwd}/x` },
|
||||
})
|
||||
const out = normalizeStdout(raw, ctx)
|
||||
expect(out).toContain('{{sessionId}}')
|
||||
expect(out).toContain('{{cwd}}')
|
||||
expect(out).not.toContain(ctx.cwd)
|
||||
expect(out).not.toContain(ctx.sessionIds[0] as string)
|
||||
})
|
||||
|
||||
it('scrubs a stray UUID not in the known list', () => {
|
||||
const raw = JSON.stringify({ jsonrpc: '2.0', method: 'x', params: { id: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' } })
|
||||
expect(normalizeStdout(raw, ctx)).toContain('{{sessionId}}')
|
||||
})
|
||||
|
||||
it('leaves notification frames without an id untouched in id-space', () => {
|
||||
const raw = JSON.stringify({ jsonrpc: '2.0', method: 'session/update', params: {} })
|
||||
const out = normalizeStdout(raw, ctx)
|
||||
expect(out).not.toContain('"id"')
|
||||
})
|
||||
|
||||
it('throws on a non-JSON stdout line (the purity check)', () => {
|
||||
const raw = `${JSON.stringify({ jsonrpc: '2.0', id: 1 })}\noops a log leaked\n`
|
||||
expect(() => normalizeStdout(raw, ctx)).toThrow()
|
||||
})
|
||||
|
||||
it('ignores blank lines', () => {
|
||||
const raw = `\n${JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'm' })}\n\n`
|
||||
expect(() => normalizeStdout(raw, ctx)).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalizeSessionLog', () => {
|
||||
const header = (over: object) => JSON.stringify({ type: 'session', version: 1, id: 's', createdAt: 123, ...over })
|
||||
const event = (over: object) => JSON.stringify({ type: 'turn/start', seq: 1, time: 999, data: { turn: 1 }, ...over })
|
||||
|
||||
it('zeroes the header createdAt', () => {
|
||||
const out = normalizeSessionLog(`${header({})}\n`, ctx)
|
||||
expect(out).toContain('"createdAt":0')
|
||||
expect(out).not.toContain('123')
|
||||
})
|
||||
|
||||
it('zeroes each event time but keeps seq', () => {
|
||||
const out = normalizeSessionLog(`${header({})}\n${event({ seq: 7, time: 999 })}\n`, ctx)
|
||||
expect(out).toContain('"time":0')
|
||||
expect(out).toContain('"seq":7') // seq is deterministic — NOT scrubbed
|
||||
expect(out).not.toContain('999')
|
||||
})
|
||||
|
||||
it('scrubs cwd and session id deep inside event data', () => {
|
||||
const ev = JSON.stringify({
|
||||
type: 'tool/result', seq: 2, time: 5,
|
||||
data: { content: [{ type: 'text', text: `wrote ${ctx.cwd}/proof.txt` }] },
|
||||
})
|
||||
const out = normalizeSessionLog(`${header({ cwd: ctx.cwd })}\n${ev}\n`, ctx)
|
||||
expect(out).toContain('{{cwd}}')
|
||||
expect(out).not.toContain(ctx.cwd)
|
||||
})
|
||||
|
||||
it('scrubs the session id in the header', () => {
|
||||
const out = normalizeSessionLog(`${header({ id: ctx.sessionIds[0] })}\n`, ctx)
|
||||
expect(out).toContain('{{sessionId}}')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Pure normalizers for the ACP snapshot goldens. They replace the
|
||||
* non-deterministic values in the two captured surfaces — the stdout JSON-RPC
|
||||
* transcript and the persisted session JSONL — with stable tokens, so a golden
|
||||
* compare reflects behavior, not run-to-run noise. Kept dependency-free and
|
||||
* side-effect-free so they unit-test trivially.
|
||||
*
|
||||
* Scrubbed: `randomUUID()` session ids → `{{sessionId}}`; the temp `mkdtemp`
|
||||
* cwd → `{{cwd}}` (it appears in terminal-card `_meta` and the log header);
|
||||
* JSON-RPC request `id` → a stable per-transcript sequence; the log's per-event
|
||||
* `time` (epoch ms) and header `createdAt` → 0. NOT scrubbed: the log's `seq`
|
||||
* (deterministic — `seq = log.length`, part of the event-log contract).
|
||||
*
|
||||
* See docs/rfc/implemented/2026-06-19-acp-snapshot-tests.md.
|
||||
*/
|
||||
|
||||
const SESSION_ID = '{{sessionId}}'
|
||||
const CWD = '{{cwd}}'
|
||||
|
||||
/** A UUID v4 string, the shape `randomUUID()` produces for session ids. */
|
||||
const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi
|
||||
|
||||
/** Inputs the normalizers need to recognize a run's volatile values. */
|
||||
export interface NormalizeContext {
|
||||
/** The session id(s) the run issued — replaced with `{{sessionId}}`. */
|
||||
sessionIds: string[]
|
||||
/** The temp cwd the run used — replaced with `{{cwd}}`. */
|
||||
cwd: string
|
||||
}
|
||||
|
||||
/** Replace cwd, session ids, and any stray UUID with stable tokens in a string. */
|
||||
function scrubString(value: string, ctx: NormalizeContext): string {
|
||||
let out = value
|
||||
// cwd first (longest, most specific), then explicit session ids, then any
|
||||
// residual UUID (covers ids that appear in places we didn't enumerate).
|
||||
out = out.split(ctx.cwd).join(CWD)
|
||||
for (const id of ctx.sessionIds) out = out.split(id).join(SESSION_ID)
|
||||
out = out.replace(UUID_RE, SESSION_ID)
|
||||
return out
|
||||
}
|
||||
|
||||
/** Recursively scrub a parsed JSON value (strings replaced; structure kept). */
|
||||
function scrubValue(value: unknown, ctx: NormalizeContext): unknown {
|
||||
if (typeof value === 'string') return scrubString(value, ctx)
|
||||
if (Array.isArray(value)) return value.map(v => scrubValue(v, ctx))
|
||||
if (value !== null && typeof value === 'object') {
|
||||
const out: Record<string, unknown> = {}
|
||||
for (const [k, v] of Object.entries(value)) out[k] = scrubValue(v, ctx)
|
||||
return out
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a raw stdout transcript (newline-delimited JSON-RPC frames) into a
|
||||
* stable golden in the SAME shape as the wire: one compact JSON frame per line
|
||||
* (NDJSON), with the JSON-RPC `id` rewritten to a per-transcript sequence
|
||||
* (1, 2, 3, …) and all volatile strings scrubbed. Throws if any non-empty line
|
||||
* is not valid JSON — that doubles as the stdout-purity check (no logger leaked
|
||||
* onto the protocol).
|
||||
*/
|
||||
export function normalizeStdout(rawStdout: string, ctx: NormalizeContext): string {
|
||||
const lines = rawStdout.split('\n').filter(line => line.trim().length > 0)
|
||||
// Map each distinct JSON-RPC id (request/response correlate by id) to a stable
|
||||
// sequence number, in first-seen order, so id churn doesn't perturb the golden.
|
||||
const idSeq = new Map<string, number>()
|
||||
const stableId = (id: unknown): number => {
|
||||
const key = JSON.stringify(id)
|
||||
let n = idSeq.get(key)
|
||||
if (n === undefined) { n = idSeq.size + 1; idSeq.set(key, n) }
|
||||
return n
|
||||
}
|
||||
const frames = lines.map((line) => {
|
||||
const frame = JSON.parse(line) as Record<string, unknown>
|
||||
if ('id' in frame && frame.id !== undefined && frame.id !== null) {
|
||||
frame.id = stableId(frame.id)
|
||||
}
|
||||
return scrubValue(frame, ctx) as Record<string, unknown>
|
||||
})
|
||||
return frames.map(f => JSON.stringify(f)).join('\n') + '\n'
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a session JSONL log into a stable golden: the header line's
|
||||
* volatile fields (`createdAt`, `id`, `cwd`) and every event's `time` are
|
||||
* zeroed/scrubbed, all volatile strings scrubbed, and `seq` is LEFT INTACT
|
||||
* (deterministic by contract). Output is JSONL in the same shape as the input —
|
||||
* one compact record per line.
|
||||
*/
|
||||
export function normalizeSessionLog(rawLog: string, ctx: NormalizeContext): string {
|
||||
const lines = rawLog.split('\n').filter(line => line.trim().length > 0)
|
||||
const records = lines.map((line) => {
|
||||
const record = JSON.parse(line) as Record<string, unknown>
|
||||
// Header line: { type: 'session', createdAt, id, cwd, … }.
|
||||
if (record.type === 'session') {
|
||||
if ('createdAt' in record) record.createdAt = 0
|
||||
} else if ('time' in record) {
|
||||
// Event line: zero the epoch-ms timestamp; keep seq (deterministic).
|
||||
record.time = 0
|
||||
}
|
||||
return scrubValue(record, ctx) as Record<string, unknown>
|
||||
})
|
||||
return records.map(r => JSON.stringify(r)).join('\n') + '\n'
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" },
|
||||
{ "op": "promptAndCancel", "text": "Start a long task; this turn will be cancelled mid-stream." }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{ "kind": "hang" }
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
{"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"}}}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"session","version":1,"id":"00000000-0000-0000-0000-000000000000","createdAt":0}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"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":"agent_message_chunk","content":{"type":"text","text":"partial"}}}}
|
||||
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"cancelled"}}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" },
|
||||
{ "op": "promptExpectError", "text": "This prompt triggers a recorded provider error." }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{ "kind": "throw", "chunks": [], "message": "simulated provider error (HTTP 401)", "code": "AUTH", "status": 401 }
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
{"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"}}}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"session","version":1,"id":"00000000-0000-0000-0000-000000000000","createdAt":0}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"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","id":3,"error":{"code":-32603,"message":"Internal error: turn failed: simulated provider error (HTTP 401)"}}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"session","version":1,"id":"00000000-0000-0000-0000-000000000000","createdAt":0}
|
||||
@@ -0,0 +1,2 @@
|
||||
{"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}}"}}
|
||||
@@ -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." }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{"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":" use"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" no"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":24,"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 use no tools."}}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ONE"}}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":106,"outputTokens":20,"cacheReadTokens":768,"reasoningTokens":18}}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":28,"time":0,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ONE\" and use no tools."},{"type":"text","text":"ONE"}]}}
|
||||
{"type":"usage","seq":29,"time":0,"data":{"turn":1,"step":1,"usage":{"inputTokens":106,"outputTokens":20,"cacheReadTokens":768,"reasoningTokens":18}}}
|
||||
{"type":"step/end","seq":30,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":31,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"turn/start","seq":32,"time":0,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":33,"time":0,"data":{"content":[{"type":"text","text":"Reply with exactly the word: TWO. No tools."}],"source":{"kind":"user"}}}
|
||||
{"type":"step/start","seq":34,"time":0,"data":{"turn":2,"step":1}}
|
||||
{"type":"assistant/chunk","seq":35,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"T"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"WO"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"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 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":122,"outputTokens":21,"cacheReadTokens":768,"reasoningTokens":18}}}}
|
||||
{"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 no tools."},{"type":"text","text":"TWO"}]}}
|
||||
{"type":"usage","seq":62,"time":0,"data":{"turn":2,"step":1,"usage":{"inputTokens":122,"outputTokens":21,"cacheReadTokens":768,"reasoningTokens":18}}}
|
||||
{"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"}}}
|
||||
@@ -0,0 +1,66 @@
|
||||
{"type":"session","version":1,"id":"803f0752-a3db-4394-9c93-3b6fcd410664","createdAt":1781834688308,"cwd":"/tmp/acp-snap-cwd-QVaaKH"}
|
||||
{"type":"turn/start","seq":0,"time":1781834688311,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1781834688312,"data":{"content":[{"type":"text","text":"Reply with exactly the word: ONE. No tools."}],"source":{"kind":"user"}}}
|
||||
{"type":"step/start","seq":2,"time":1781834688312,"data":{"turn":1,"step":1}}
|
||||
{"type":"assistant/chunk","seq":3,"time":1781834688735,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1781834688735,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1781834688864,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1781834688889,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1781834688890,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1781834688890,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1781834688890,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1781834688890,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1781834688920,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1781834688920,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1781834688921,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1781834688921,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1781834688921,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1781834688921,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1781834688948,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1781834688948,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" use"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1781834688979,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" no"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1781834688979,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1781834688979,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1781834689007,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1781834689007,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1781834689008,"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 use no tools."}}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1781834689008,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ONE"}}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1781834689008,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":106,"outputTokens":20,"cacheReadTokens":768,"reasoningTokens":18}}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1781834689008,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":28,"time":1781834689009,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ONE\" and use no tools."},{"type":"text","text":"ONE"}]}}
|
||||
{"type":"usage","seq":29,"time":1781834689010,"data":{"turn":1,"step":1,"usage":{"inputTokens":106,"outputTokens":20,"cacheReadTokens":768,"reasoningTokens":18}}}
|
||||
{"type":"step/end","seq":30,"time":1781834689010,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":31,"time":1781834689010,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"turn/start","seq":32,"time":1781834689017,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":33,"time":1781834689017,"data":{"content":[{"type":"text","text":"Reply with exactly the word: TWO. No tools."}],"source":{"kind":"user"}}}
|
||||
{"type":"step/start","seq":34,"time":1781834689017,"data":{"turn":2,"step":1}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1781834689551,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1781834689551,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1781834689643,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1781834689676,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1781834689676,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1781834689676,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1781834689676,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1781834689676,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1781834689702,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1781834689702,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1781834689702,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1781834689702,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1781834689702,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"T"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1781834689703,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"WO"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1781834689731,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1781834689731,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1781834689732,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" no"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1781834689759,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1781834689759,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1781834689759,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1781834689759,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"T"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1781834689760,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"WO"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1781834689788,"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 no tools."}}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1781834689788,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"TWO"}}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1781834689788,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":122,"outputTokens":21,"cacheReadTokens":768,"reasoningTokens":18}}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1781834689788,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":61,"time":1781834689789,"data":{"turn":2,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"TWO\" and no tools."},{"type":"text","text":"TWO"}]}}
|
||||
{"type":"usage","seq":62,"time":1781834689789,"data":{"turn":2,"step":1,"usage":{"inputTokens":122,"outputTokens":21,"cacheReadTokens":768,"reasoningTokens":18}}}
|
||||
{"type":"step/end","seq":63,"time":1781834689789,"data":{"turn":2,"step":1}}
|
||||
{"type":"turn/end","seq":64,"time":1781834689789,"data":{"turn":2,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,43 @@
|
||||
{"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":"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":" 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":"ONE"}}}}
|
||||
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}}
|
||||
{"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":" 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"}}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSessionExpectError", "additionalDirectories": ["/extra-dir"] }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"session","version":1,"id":"00000000-0000-0000-0000-000000000000","createdAt":0}
|
||||
@@ -0,0 +1,2 @@
|
||||
{"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,"error":{"code":-32602,"message":"Invalid params: additionalDirectories is not supported in this MVP"}}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" },
|
||||
{ "op": "prompt", "text": "Reply with exactly the word: PONG. Do not use any tools." }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{"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":878,"outputTokens":22,"cacheReadTokens":0,"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":878,"outputTokens":22,"cacheReadTokens":0,"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"}}}
|
||||
@@ -0,0 +1,35 @@
|
||||
{"type":"session","version":1,"id":"b8c052fd-b33f-475a-8a0c-bc3b75527602","createdAt":1781834679270,"cwd":"/tmp/acp-snap-cwd-TJst85"}
|
||||
{"type":"turn/start","seq":0,"time":1781834679273,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1781834679273,"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":1781834679273,"data":{"turn":1,"step":1}}
|
||||
{"type":"assistant/chunk","seq":3,"time":1781834680004,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1781834680004,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1781834680079,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1781834680113,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1781834680114,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1781834680114,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1781834680114,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1781834680114,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1781834680137,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1781834680138,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1781834680138,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1781834680138,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1781834680138,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"P"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1781834680138,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONG"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1781834680167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1781834680167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" without"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1781834680167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" using"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1781834680196,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" any"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1781834680196,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1781834680196,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1781834680196,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1781834680196,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"P"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1781834680225,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONG"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1781834680226,"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":1781834680226,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"PONG"}}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1781834680226,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":878,"outputTokens":22,"cacheReadTokens":0,"reasoningTokens":19}}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1781834680226,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":30,"time":1781834680227,"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":1781834680227,"data":{"turn":1,"step":1,"usage":{"inputTokens":878,"outputTokens":22,"cacheReadTokens":0,"reasoningTokens":19}}}
|
||||
{"type":"step/end","seq":32,"time":1781834680228,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":33,"time":1781834680228,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,24 @@
|
||||
{"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":"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"}}
|
||||
@@ -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." }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
{"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":" `"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" S"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"H"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"`"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"command"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":" S"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"H"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","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_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"Run"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":" echo"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":" S"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"H"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to run `echo SNAPSHOT_OK` and then reply with \"DONE\"."}}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run echo SNAPSHOT_OK\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":122,"outputTokens":95,"cacheReadTokens":768,"reasoningTokens":23}}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":63,"time":0,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run `echo SNAPSHOT_OK` and then reply with \"DONE\"."},{"type":"tool-call","id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run echo SNAPSHOT_OK\"}"}]}}
|
||||
{"type":"usage","seq":64,"time":0,"data":{"turn":1,"step":1,"usage":{"inputTokens":122,"outputTokens":95,"cacheReadTokens":768,"reasoningTokens":23}}}
|
||||
{"type":"tool/call","seq":65,"time":0,"data":{"turn":1,"step":1,"callId":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run echo SNAPSHOT_OK\"}"}}
|
||||
{"type":"tool/result","seq":66,"time":0,"data":{"turn":1,"step":1,"callId":"call_00_7bmU1TAadx8ADiZJ3BqC9330","content":[{"type":"text","text":"SNAPSHOT_OK\n"}],"isError":false}}
|
||||
{"type":"step/end","seq":67,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":68,"time":0,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":69,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" command"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" ran"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" output"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"S"}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"H"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Now"}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" need"}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" just"}}}
|
||||
{"type":"assistant/chunk","seq":91,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":92,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":93,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":94,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":95,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":98,"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 just \"DONE\"."}}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":235,"outputTokens":28,"cacheReadTokens":768,"reasoningTokens":25}}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":102,"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 just \"DONE\"."},{"type":"text","text":"DONE"}]}}
|
||||
{"type":"usage","seq":103,"time":0,"data":{"turn":1,"step":2,"usage":{"inputTokens":235,"outputTokens":28,"cacheReadTokens":768,"reasoningTokens":25}}}
|
||||
{"type":"step/end","seq":104,"time":0,"data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","seq":105,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,107 @@
|
||||
{"type":"session","version":1,"id":"0e5b2fc1-d220-4a81-b48b-939c14dea057","createdAt":1781834681068,"cwd":"/tmp/acp-snap-cwd-5F2H38"}
|
||||
{"type":"turn/start","seq":0,"time":1781834681072,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1781834681073,"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":1781834681073,"data":{"turn":1,"step":1}}
|
||||
{"type":"assistant/chunk","seq":3,"time":1781834681472,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1781834681472,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1781834681566,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1781834681597,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1781834681598,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1781834681598,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1781834681598,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" run"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1781834681598,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" `"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1781834681599,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1781834681629,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" S"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1781834681630,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1781834681630,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1781834681630,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"H"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1781834681630,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1781834681630,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1781834681661,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"`"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1781834681661,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1781834681662,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1781834681662,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1781834681695,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1781834681696,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1781834681696,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1781834681696,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1781834681696,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1781834681790,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1781834681790,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1781834681823,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1781834681824,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":1781834681824,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"command"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1781834681824,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1781834681824,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1781834681856,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1781834681856,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1781834681856,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":" S"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1781834681856,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1781834681856,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1781834681894,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"H"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1781834681895,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1781834681895,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1781834681895,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1781834681923,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1781834681923,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1781834681955,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1781834681956,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1781834681956,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1781834681956,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1781834681989,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"Run"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1781834681989,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":" echo"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1781834681989,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":" S"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1781834682020,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1781834682020,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1781834682020,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"H"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1781834682020,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1781834682020,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1781834682052,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1781834682053,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1781834682119,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to run `echo SNAPSHOT_OK` and then reply with \"DONE\"."}}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1781834682119,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run echo SNAPSHOT_OK\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":1781834682119,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":122,"outputTokens":95,"cacheReadTokens":768,"reasoningTokens":23}}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1781834682119,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":63,"time":1781834682121,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run `echo SNAPSHOT_OK` and then reply with \"DONE\"."},{"type":"tool-call","id":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run echo SNAPSHOT_OK\"}"}]}}
|
||||
{"type":"usage","seq":64,"time":1781834682121,"data":{"turn":1,"step":1,"usage":{"inputTokens":122,"outputTokens":95,"cacheReadTokens":768,"reasoningTokens":23}}}
|
||||
{"type":"tool/call","seq":65,"time":1781834682121,"data":{"turn":1,"step":1,"callId":"call_00_7bmU1TAadx8ADiZJ3BqC9330","name":"bash","arguments":"{\"command\": \"echo SNAPSHOT_OK\", \"description\": \"Run echo SNAPSHOT_OK\"}"}}
|
||||
{"type":"tool/result","seq":66,"time":1781834682136,"data":{"turn":1,"step":1,"callId":"call_00_7bmU1TAadx8ADiZJ3BqC9330","content":[{"type":"text","text":"SNAPSHOT_OK\n"}],"isError":false}}
|
||||
{"type":"step/end","seq":67,"time":1781834682137,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":68,"time":1781834682137,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1781834682760,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1781834682761,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1781834682826,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" command"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1781834682855,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" ran"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1781834682885,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1781834682886,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1781834682886,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" output"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1781834682915,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1781834682915,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"S"}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1781834682915,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"NA"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1781834682915,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"PS"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1781834682915,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"H"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1781834682916,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"OT"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1781834682946,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1781834682947,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1781834682947,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Now"}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1781834682947,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1781834682947,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" need"}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":1781834682947,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":1781834682976,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":1781834682977,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":1781834682977,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" just"}}}
|
||||
{"type":"assistant/chunk","seq":91,"time":1781834682977,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":92,"time":1781834682977,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":93,"time":1781834683007,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":94,"time":1781834683007,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":95,"time":1781834683007,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":1781834683007,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":1781834683007,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":98,"time":1781834683008,"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 just \"DONE\"."}}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":1781834683008,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":1781834683008,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":235,"outputTokens":28,"cacheReadTokens":768,"reasoningTokens":25}}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":1781834683008,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":102,"time":1781834683008,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The command ran successfully and output \"SNAPSHOT_OK\". Now I need to reply with just \"DONE\"."},{"type":"text","text":"DONE"}]}}
|
||||
{"type":"usage","seq":103,"time":1781834683008,"data":{"turn":1,"step":2,"usage":{"inputTokens":235,"outputTokens":28,"cacheReadTokens":768,"reasoningTokens":25}}}
|
||||
{"type":"step/end","seq":104,"time":1781834683008,"data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","seq":105,"time":1781834683008,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,55 @@
|
||||
{"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":"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":" `"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"echo"}}}}
|
||||
{"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":" 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":" \""}}}}
|
||||
{"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_7bmU1TAadx8ADiZJ3BqC9330","title":"echo SNAPSHOT_OK","kind":"execute","status":"in_progress","rawInput":"echo SNAPSHOT_OK","content":[{"type":"content","content":{"type":"text","text":"Run echo SNAPSHOT_OK"}}]}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_00_7bmU1TAadx8ADiZJ3BqC9330","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":" just"}}}}
|
||||
{"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"}}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" },
|
||||
{ "op": "prompt", "text": "A file named greeting.txt in the current directory contains one word. Use the bash tool to append a second line containing the word WORLD to it (so it has two lines), then read the file back with `cat greeting.txt` to confirm, and reply with the single word DONE. Use a single bash call per action." }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
{"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":"A file named greeting.txt in the current directory contains one word. Use the bash tool to append a second line containing the word WORLD to it (so it has two lines), then read the file back with `cat greeting.txt` to confirm, and reply with the single word DONE. Use a single bash call per action."}],"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":":\n"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"1"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Append"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" second"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" line"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" containing"}}}
|
||||
{"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":"WOR"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"LD"}}}
|
||||
{"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":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\n"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"2"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Read"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" back"}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" `"}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"cat"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"`"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" confirm"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\n"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"3"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Reply"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n\n"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"But"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" they"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" want"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"a"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" call"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" per"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" action"}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" -"}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" so"}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"'ll"}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" do"}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" two"}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" separate"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" calls"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":":"}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" one"}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" for"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" app"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ending"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" one"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" for"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reading"}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"command"}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" '"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"WOR"}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"LD"}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"'"}}}
|
||||
{"type":"assistant/chunk","seq":91,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" >>"}}}
|
||||
{"type":"assistant/chunk","seq":92,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":93,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":94,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":95,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":98,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"App"}}}
|
||||
{"type":"assistant/chunk","seq":102,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"end"}}}
|
||||
{"type":"assistant/chunk","seq":103,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" WORLD"}}}
|
||||
{"type":"assistant/chunk","seq":104,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" line"}}}
|
||||
{"type":"assistant/chunk","seq":105,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" to"}}}
|
||||
{"type":"assistant/chunk","seq":106,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":107,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":108,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":109,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":110,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to:\n1. Append a second line containing \"WORLD\" to greeting.txt\n2. Read the file back with `cat greeting.txt` to confirm\n3. Reply with \"DONE\"\n\nBut they want \"a single bash call per action\" - so I'll do two separate bash calls: one for appending, one for reading."}}}}
|
||||
{"type":"assistant/chunk","seq":111,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","arguments":"{\"command\": \"echo 'WORLD' >> greeting.txt\", \"description\": \"Append WORLD line to greeting.txt\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":112,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":161,"outputTokens":146,"cacheReadTokens":768,"reasoningTokens":74}}}}
|
||||
{"type":"assistant/chunk","seq":113,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":114,"time":0,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to:\n1. Append a second line containing \"WORLD\" to greeting.txt\n2. Read the file back with `cat greeting.txt` to confirm\n3. Reply with \"DONE\"\n\nBut they want \"a single bash call per action\" - so I'll do two separate bash calls: one for appending, one for reading."},{"type":"tool-call","id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","arguments":"{\"command\": \"echo 'WORLD' >> greeting.txt\", \"description\": \"Append WORLD line to greeting.txt\"}"}]}}
|
||||
{"type":"usage","seq":115,"time":0,"data":{"turn":1,"step":1,"usage":{"inputTokens":161,"outputTokens":146,"cacheReadTokens":768,"reasoningTokens":74}}}
|
||||
{"type":"tool/call","seq":116,"time":0,"data":{"turn":1,"step":1,"callId":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","arguments":"{\"command\": \"echo 'WORLD' >> greeting.txt\", \"description\": \"Append WORLD line to greeting.txt\"}"}}
|
||||
{"type":"tool/result","seq":117,"time":0,"data":{"turn":1,"step":1,"callId":"call_00_r3tvHl3fD0tmV0GKQt032338","content":[{"type":"text","text":"(no output)"}],"isError":false}}
|
||||
{"type":"step/end","seq":118,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":119,"time":0,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":120,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":121,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"App"}}}
|
||||
{"type":"assistant/chunk","seq":122,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ended"}}}
|
||||
{"type":"assistant/chunk","seq":123,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":124,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":125,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Now"}}}
|
||||
{"type":"assistant/chunk","seq":126,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" read"}}}
|
||||
{"type":"assistant/chunk","seq":127,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":128,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":129,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":130,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":131,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":132,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":133,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":134,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"command"}}}
|
||||
{"type":"assistant/chunk","seq":135,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":136,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":137,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":138,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"cat"}}}
|
||||
{"type":"assistant/chunk","seq":139,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":140,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":141,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":142,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":143,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":144,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":145,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":146,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":147,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":148,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"Read"}}}
|
||||
{"type":"assistant/chunk","seq":149,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":150,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":151,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" to"}}}
|
||||
{"type":"assistant/chunk","seq":152,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" confirm"}}}
|
||||
{"type":"assistant/chunk","seq":153,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":154,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":155,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"Appended successfully. Now read the file."}}}}
|
||||
{"type":"assistant/chunk","seq":156,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","arguments":"{\"command\": \"cat greeting.txt\", \"description\": \"Read greeting.txt to confirm\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":157,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":193,"outputTokens":74,"cacheReadTokens":896,"reasoningTokens":9}}}}
|
||||
{"type":"assistant/chunk","seq":158,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":159,"time":0,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"Appended successfully. Now read the file."},{"type":"tool-call","id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","arguments":"{\"command\": \"cat greeting.txt\", \"description\": \"Read greeting.txt to confirm\"}"}]}}
|
||||
{"type":"usage","seq":160,"time":0,"data":{"turn":1,"step":2,"usage":{"inputTokens":193,"outputTokens":74,"cacheReadTokens":896,"reasoningTokens":9}}}
|
||||
{"type":"tool/call","seq":161,"time":0,"data":{"turn":1,"step":2,"callId":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","arguments":"{\"command\": \"cat greeting.txt\", \"description\": \"Read greeting.txt to confirm\"}"}}
|
||||
{"type":"tool/result","seq":162,"time":0,"data":{"turn":1,"step":2,"callId":"call_00_SkCP8dgN8aCbLiZDcYa68316","content":[{"type":"text","text":"hello\nWORLD\n"}],"isError":false}}
|
||||
{"type":"step/end","seq":163,"time":0,"data":{"turn":1,"step":2}}
|
||||
{"type":"step/start","seq":164,"time":0,"data":{"turn":1,"step":3}}
|
||||
{"type":"assistant/chunk","seq":165,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":166,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":167,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":168,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" now"}}}
|
||||
{"type":"assistant/chunk","seq":169,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" has"}}}
|
||||
{"type":"assistant/chunk","seq":170,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" two"}}}
|
||||
{"type":"assistant/chunk","seq":171,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" lines"}}}
|
||||
{"type":"assistant/chunk","seq":172,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":173,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":174,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"'ll"}}}
|
||||
{"type":"assistant/chunk","seq":175,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":176,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":177,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" D"}}}
|
||||
{"type":"assistant/chunk","seq":178,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":179,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":180,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":181,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":182,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":183,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The file now has two lines. I'll reply with DONE."}}}}
|
||||
{"type":"assistant/chunk","seq":184,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":185,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":156,"outputTokens":17,"cacheReadTokens":1024,"reasoningTokens":14}}}}
|
||||
{"type":"assistant/chunk","seq":186,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":187,"time":0,"data":{"turn":1,"step":3,"content":[{"type":"reasoning","text":"The file now has two lines. I'll reply with DONE."},{"type":"text","text":"DONE"}]}}
|
||||
{"type":"usage","seq":188,"time":0,"data":{"turn":1,"step":3,"usage":{"inputTokens":156,"outputTokens":17,"cacheReadTokens":1024,"reasoningTokens":14}}}
|
||||
{"type":"step/end","seq":189,"time":0,"data":{"turn":1,"step":3}}
|
||||
{"type":"turn/end","seq":190,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,192 @@
|
||||
{"type":"session","version":1,"id":"06bd4899-ec95-43e3-82ca-42c719d8b19b","createdAt":1781834683850,"cwd":"/tmp/acp-snap-cwd-Jsq2M2"}
|
||||
{"type":"turn/start","seq":0,"time":1781834683853,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1781834683854,"data":{"content":[{"type":"text","text":"A file named greeting.txt in the current directory contains one word. Use the bash tool to append a second line containing the word WORLD to it (so it has two lines), then read the file back with `cat greeting.txt` to confirm, and reply with the single word DONE. Use a single bash call per action."}],"source":{"kind":"user"}}}
|
||||
{"type":"step/start","seq":2,"time":1781834683854,"data":{"turn":1,"step":1}}
|
||||
{"type":"assistant/chunk","seq":3,"time":1781834684260,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1781834684260,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1781834684370,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1781834684399,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1781834684399,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1781834684400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1781834684400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":":\n"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1781834684400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"1"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1781834684400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1781834684428,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Append"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1781834684429,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1781834684430,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" second"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1781834684430,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" line"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1781834684430,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" containing"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1781834684458,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1781834684458,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"WOR"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1781834684458,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"LD"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1781834684459,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1781834684459,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1781834684459,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1781834684486,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1781834684487,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\n"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1781834684487,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"2"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1781834684487,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1781834684487,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Read"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1781834684516,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1781834684516,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1781834684516,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" back"}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":1781834684545,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1781834684545,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" `"}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1781834684545,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"cat"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1781834684545,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1781834684574,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1781834684574,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"`"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1781834684574,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1781834684574,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" confirm"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1781834684603,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\n"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1781834684604,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"3"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1781834684604,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1781834684604,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Reply"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1781834684604,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1781834684632,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1781834684633,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1781834684633,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1781834684662,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n\n"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1781834684663,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"But"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1781834684663,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" they"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1781834684663,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" want"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1781834684695,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1781834684695,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"a"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1781834684724,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1781834684724,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1781834684724,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" call"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1781834684724,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" per"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1781834684725,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" action"}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1781834684725,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1781834684753,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" -"}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1781834684782,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" so"}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":1781834684783,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1781834684783,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"'ll"}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1781834684811,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" do"}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":1781834684840,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" two"}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":1781834684873,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" separate"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":1781834684873,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":1781834684873,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" calls"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":1781834684902,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":":"}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1781834684903,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" one"}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1781834684934,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" for"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1781834684935,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" app"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1781834684966,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ending"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1781834684966,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1781834684967,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" one"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1781834684967,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" for"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1781834684967,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reading"}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1781834684967,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1781834685092,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1781834685092,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1781834685120,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1781834685120,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1781834685120,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"command"}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1781834685120,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1781834685120,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1781834685149,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1781834685149,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":1781834685149,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" '"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":1781834685149,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"WOR"}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":1781834685149,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"LD"}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":1781834685149,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"'"}}}
|
||||
{"type":"assistant/chunk","seq":91,"time":1781834685178,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" >>"}}}
|
||||
{"type":"assistant/chunk","seq":92,"time":1781834685179,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":93,"time":1781834685179,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":94,"time":1781834685179,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":95,"time":1781834685207,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":1781834685207,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":1781834685236,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":98,"time":1781834685237,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":1781834685237,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":1781834685237,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":1781834685265,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"App"}}}
|
||||
{"type":"assistant/chunk","seq":102,"time":1781834685266,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"end"}}}
|
||||
{"type":"assistant/chunk","seq":103,"time":1781834685266,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" WORLD"}}}
|
||||
{"type":"assistant/chunk","seq":104,"time":1781834685266,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" line"}}}
|
||||
{"type":"assistant/chunk","seq":105,"time":1781834685309,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" to"}}}
|
||||
{"type":"assistant/chunk","seq":106,"time":1781834685310,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":107,"time":1781834685310,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":108,"time":1781834685310,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":109,"time":1781834685326,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":110,"time":1781834685385,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to:\n1. Append a second line containing \"WORLD\" to greeting.txt\n2. Read the file back with `cat greeting.txt` to confirm\n3. Reply with \"DONE\"\n\nBut they want \"a single bash call per action\" - so I'll do two separate bash calls: one for appending, one for reading."}}}}
|
||||
{"type":"assistant/chunk","seq":111,"time":1781834685385,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","arguments":"{\"command\": \"echo 'WORLD' >> greeting.txt\", \"description\": \"Append WORLD line to greeting.txt\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":112,"time":1781834685386,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":161,"outputTokens":146,"cacheReadTokens":768,"reasoningTokens":74}}}}
|
||||
{"type":"assistant/chunk","seq":113,"time":1781834685386,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":114,"time":1781834685387,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to:\n1. Append a second line containing \"WORLD\" to greeting.txt\n2. Read the file back with `cat greeting.txt` to confirm\n3. Reply with \"DONE\"\n\nBut they want \"a single bash call per action\" - so I'll do two separate bash calls: one for appending, one for reading."},{"type":"tool-call","id":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","arguments":"{\"command\": \"echo 'WORLD' >> greeting.txt\", \"description\": \"Append WORLD line to greeting.txt\"}"}]}}
|
||||
{"type":"usage","seq":115,"time":1781834685387,"data":{"turn":1,"step":1,"usage":{"inputTokens":161,"outputTokens":146,"cacheReadTokens":768,"reasoningTokens":74}}}
|
||||
{"type":"tool/call","seq":116,"time":1781834685387,"data":{"turn":1,"step":1,"callId":"call_00_r3tvHl3fD0tmV0GKQt032338","name":"bash","arguments":"{\"command\": \"echo 'WORLD' >> greeting.txt\", \"description\": \"Append WORLD line to greeting.txt\"}"}}
|
||||
{"type":"tool/result","seq":117,"time":1781834685400,"data":{"turn":1,"step":1,"callId":"call_00_r3tvHl3fD0tmV0GKQt032338","content":[{"type":"text","text":"(no output)"}],"isError":false}}
|
||||
{"type":"step/end","seq":118,"time":1781834685400,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":119,"time":1781834685400,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":120,"time":1781834686163,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":121,"time":1781834686163,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"App"}}}
|
||||
{"type":"assistant/chunk","seq":122,"time":1781834686261,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ended"}}}
|
||||
{"type":"assistant/chunk","seq":123,"time":1781834686290,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":124,"time":1781834686318,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":125,"time":1781834686319,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Now"}}}
|
||||
{"type":"assistant/chunk","seq":126,"time":1781834686319,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" read"}}}
|
||||
{"type":"assistant/chunk","seq":127,"time":1781834686352,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":128,"time":1781834686352,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":129,"time":1781834686381,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":130,"time":1781834686469,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":131,"time":1781834686469,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":132,"time":1781834686497,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":133,"time":1781834686498,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":134,"time":1781834686498,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"command"}}}
|
||||
{"type":"assistant/chunk","seq":135,"time":1781834686498,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":136,"time":1781834686498,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":137,"time":1781834686530,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":138,"time":1781834686530,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"cat"}}}
|
||||
{"type":"assistant/chunk","seq":139,"time":1781834686530,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":140,"time":1781834686530,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":141,"time":1781834686559,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":142,"time":1781834686591,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":143,"time":1781834686591,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":144,"time":1781834686591,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":145,"time":1781834686591,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":146,"time":1781834686591,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":147,"time":1781834686623,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":148,"time":1781834686623,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"Read"}}}
|
||||
{"type":"assistant/chunk","seq":149,"time":1781834686623,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" greeting"}}}
|
||||
{"type":"assistant/chunk","seq":150,"time":1781834686655,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":".txt"}}}
|
||||
{"type":"assistant/chunk","seq":151,"time":1781834686655,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" to"}}}
|
||||
{"type":"assistant/chunk","seq":152,"time":1781834686684,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":" confirm"}}}
|
||||
{"type":"assistant/chunk","seq":153,"time":1781834686684,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":154,"time":1781834686713,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":155,"time":1781834686745,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"Appended successfully. Now read the file."}}}}
|
||||
{"type":"assistant/chunk","seq":156,"time":1781834686745,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","arguments":"{\"command\": \"cat greeting.txt\", \"description\": \"Read greeting.txt to confirm\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":157,"time":1781834686745,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":193,"outputTokens":74,"cacheReadTokens":896,"reasoningTokens":9}}}}
|
||||
{"type":"assistant/chunk","seq":158,"time":1781834686745,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":159,"time":1781834686745,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"Appended successfully. Now read the file."},{"type":"tool-call","id":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","arguments":"{\"command\": \"cat greeting.txt\", \"description\": \"Read greeting.txt to confirm\"}"}]}}
|
||||
{"type":"usage","seq":160,"time":1781834686745,"data":{"turn":1,"step":2,"usage":{"inputTokens":193,"outputTokens":74,"cacheReadTokens":896,"reasoningTokens":9}}}
|
||||
{"type":"tool/call","seq":161,"time":1781834686745,"data":{"turn":1,"step":2,"callId":"call_00_SkCP8dgN8aCbLiZDcYa68316","name":"bash","arguments":"{\"command\": \"cat greeting.txt\", \"description\": \"Read greeting.txt to confirm\"}"}}
|
||||
{"type":"tool/result","seq":162,"time":1781834686758,"data":{"turn":1,"step":2,"callId":"call_00_SkCP8dgN8aCbLiZDcYa68316","content":[{"type":"text","text":"hello\nWORLD\n"}],"isError":false}}
|
||||
{"type":"step/end","seq":163,"time":1781834686758,"data":{"turn":1,"step":2}}
|
||||
{"type":"step/start","seq":164,"time":1781834686758,"data":{"turn":1,"step":3}}
|
||||
{"type":"assistant/chunk","seq":165,"time":1781834687255,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":166,"time":1781834687255,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":167,"time":1781834687336,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":168,"time":1781834687365,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" now"}}}
|
||||
{"type":"assistant/chunk","seq":169,"time":1781834687365,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" has"}}}
|
||||
{"type":"assistant/chunk","seq":170,"time":1781834687365,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" two"}}}
|
||||
{"type":"assistant/chunk","seq":171,"time":1781834687366,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" lines"}}}
|
||||
{"type":"assistant/chunk","seq":172,"time":1781834687396,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":173,"time":1781834687425,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":174,"time":1781834687426,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"'ll"}}}
|
||||
{"type":"assistant/chunk","seq":175,"time":1781834687455,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":176,"time":1781834687455,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":177,"time":1781834687455,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":" D"}}}
|
||||
{"type":"assistant/chunk","seq":178,"time":1781834687455,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":179,"time":1781834687455,"data":{"turn":1,"step":3,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":180,"time":1781834687484,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":181,"time":1781834687484,"data":{"turn":1,"step":3,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":182,"time":1781834687484,"data":{"turn":1,"step":3,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":183,"time":1781834687488,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The file now has two lines. I'll reply with DONE."}}}}
|
||||
{"type":"assistant/chunk","seq":184,"time":1781834687488,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":185,"time":1781834687488,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":156,"outputTokens":17,"cacheReadTokens":1024,"reasoningTokens":14}}}}
|
||||
{"type":"assistant/chunk","seq":186,"time":1781834687488,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":187,"time":1781834687489,"data":{"turn":1,"step":3,"content":[{"type":"reasoning","text":"The file now has two lines. I'll reply with DONE."},{"type":"text","text":"DONE"}]}}
|
||||
{"type":"usage","seq":188,"time":1781834687489,"data":{"turn":1,"step":3,"usage":{"inputTokens":156,"outputTokens":17,"cacheReadTokens":1024,"reasoningTokens":14}}}
|
||||
{"type":"step/end","seq":189,"time":1781834687489,"data":{"turn":1,"step":3}}
|
||||
{"type":"turn/end","seq":190,"time":1781834687489,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,106 @@
|
||||
{"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":"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":":\n"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"1"}}}}
|
||||
{"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":" Append"}}}}
|
||||
{"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":" second"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" line"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" containing"}}}}
|
||||
{"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":"WOR"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"LD"}}}}
|
||||
{"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":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" greeting"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":".txt"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\n"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"2"}}}}
|
||||
{"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":" Read"}}}}
|
||||
{"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":" file"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" back"}}}}
|
||||
{"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":" `"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"cat"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" greeting"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":".txt"}}}}
|
||||
{"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":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" confirm"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\n"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"3"}}}}
|
||||
{"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":" 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":" \""}}}}
|
||||
{"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":"\"\n\n"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"But"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" they"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" want"}}}}
|
||||
{"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":"a"}}}}
|
||||
{"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":" bash"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" call"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" per"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" action"}}}}
|
||||
{"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":" -"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" so"}}}}
|
||||
{"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":"'ll"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" do"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" two"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" separate"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" bash"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" calls"}}}}
|
||||
{"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":" for"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" app"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"ending"}}}}
|
||||
{"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":" for"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reading"}}}}
|
||||
{"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_r3tvHl3fD0tmV0GKQt032338","title":"echo 'WORLD' >> greeting.txt","kind":"execute","status":"in_progress","rawInput":"echo 'WORLD' >> greeting.txt","content":[{"type":"content","content":{"type":"text","text":"Append WORLD line to greeting.txt"}}]}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_00_r3tvHl3fD0tmV0GKQt032338","status":"completed","content":[{"type":"content","content":{"type":"text","text":"```console\n(no output)\n```"}}]}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"App"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"ended"}}}}
|
||||
{"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":"."}}}}
|
||||
{"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":" read"}}}}
|
||||
{"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":" file"}}}}
|
||||
{"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_SkCP8dgN8aCbLiZDcYa68316","title":"cat greeting.txt","kind":"execute","status":"in_progress","rawInput":"cat greeting.txt","content":[{"type":"content","content":{"type":"text","text":"Read greeting.txt to confirm"}}]}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_00_SkCP8dgN8aCbLiZDcYa68316","status":"completed","content":[{"type":"content","content":{"type":"text","text":"```console\nhello\nWORLD\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":" file"}}}}
|
||||
{"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":" has"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" two"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" lines"}}}}
|
||||
{"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":" I"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'ll"}}}}
|
||||
{"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":"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"}}
|
||||
@@ -0,0 +1 @@
|
||||
hello
|
||||
@@ -0,0 +1,37 @@
|
||||
# Providerless provider/tool core — everything the model and tools need EXCEPT
|
||||
# an LLM adapter. Split out of base.yml so two consumers can share it:
|
||||
# - base.yml = base-core.yml + the real llm-deepseek adapter (the demos).
|
||||
# - acp-agent/cordis.snapshot.yml = base-core.yml + llm-replay (keyless
|
||||
# snapshot replay — base.yml can't be reused there because llm-deepseek's
|
||||
# apply() throws without DEEPSEEK_API_KEY).
|
||||
#
|
||||
# Plugin entries use package names (resolved from node_modules), so they are
|
||||
# insensitive to the baseUrl reset that plugin-include performs per file.
|
||||
|
||||
- id: llm
|
||||
name: '@deepseek-ai/dsh-llm'
|
||||
|
||||
- id: sessions
|
||||
name: '@deepseek-ai/dsh-session'
|
||||
|
||||
- id: system-prompt
|
||||
name: '@deepseek-ai/dsh-system-prompt'
|
||||
|
||||
- id: tools
|
||||
name: '@deepseek-ai/dsh-tools'
|
||||
|
||||
- id: agents
|
||||
name: '@deepseek-ai/dsh-agent'
|
||||
|
||||
# Dev-mode event-contract assertions + session-log freeze (off in prod).
|
||||
- id: invariants
|
||||
name: '@deepseek-ai/dsh-invariants'
|
||||
|
||||
# Bash execution: the local executor implementation + the tool schemas.
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
timeoutMs: 60000
|
||||
|
||||
- id: tool-bash
|
||||
name: '@deepseek-ai/dsh-tool-bash'
|
||||
+13
-32
@@ -1,7 +1,12 @@
|
||||
# Shared provider/tool core for the example agents, loaded via a nested
|
||||
# @cordisjs/plugin-include from each example's cordis.yml. Contains everything
|
||||
# the model and tools need; each example adds its own infra (logger/timer/hmr),
|
||||
# its agent-loop config (the examples disagree — see below), and its UI plugin.
|
||||
# @cordisjs/plugin-include from each example's cordis.yml. This is
|
||||
# base-core.yml (the providerless core: llm, sessions, system-prompt, tools,
|
||||
# agents, invariants, bash-local, tool-bash) PLUS the real llm-deepseek adapter.
|
||||
#
|
||||
# The providerless core lives in base-core.yml so the keyless snapshot-replay
|
||||
# config (acp-agent/cordis.snapshot.yml) can reuse it with llm-replay in place
|
||||
# of the adapter — it can't reuse THIS file, because llm-deepseek's apply()
|
||||
# throws without DEEPSEEK_API_KEY.
|
||||
#
|
||||
# Deliberately EXCLUDES:
|
||||
# - the console logger: it writes to stdout, which the acp-agent reserves for
|
||||
@@ -12,28 +17,13 @@
|
||||
# pre-create NONE (ACP session/new creates agents on demand). So each example
|
||||
# declares agent-loop with its own `agents` list.
|
||||
#
|
||||
# Plugin entries here use package names (resolved from node_modules), so they
|
||||
# are insensitive to the baseUrl reset that plugin-include performs per file.
|
||||
# Requires DEEPSEEK_API_KEY (and optionally DEEPSEEK_BASE_URL) in the env.
|
||||
|
||||
- id: llm
|
||||
name: '@deepseek-ai/dsh-llm'
|
||||
|
||||
- id: sessions
|
||||
name: '@deepseek-ai/dsh-session'
|
||||
|
||||
- id: system-prompt
|
||||
name: '@deepseek-ai/dsh-system-prompt'
|
||||
|
||||
- id: tools
|
||||
name: '@deepseek-ai/dsh-tools'
|
||||
|
||||
- id: agents
|
||||
name: '@deepseek-ai/dsh-agent'
|
||||
|
||||
# Dev-mode event-contract assertions + session-log freeze (off in prod).
|
||||
- id: invariants
|
||||
name: '@deepseek-ai/dsh-invariants'
|
||||
# The providerless core (resolved relative to THIS file's directory).
|
||||
- id: base-core
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: './base-core.yml'
|
||||
|
||||
# The DeepSeek adapter. Swap to '@deepseek-ai/dsh-llm-pi-ai' for the pi-ai-backed
|
||||
# twin (same config shape; `reasoning: high` replaces thinking/reasoningEffort).
|
||||
@@ -45,12 +35,3 @@
|
||||
models:
|
||||
- deepseek-v4-flash
|
||||
- deepseek-v4-pro
|
||||
|
||||
# Bash execution: the local executor implementation + the tool schemas.
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
timeoutMs: 60000
|
||||
|
||||
- id: tool-bash
|
||||
name: '@deepseek-ai/dsh-tool-bash'
|
||||
@@ -7,7 +7,9 @@
|
||||
"entry": [
|
||||
"examples/echo-agent/src/*.ts",
|
||||
"examples/coding-agent/src/*.ts",
|
||||
"examples/acp-agent/tests/**/*.e2e.ts"
|
||||
"examples/acp-agent/src/*.ts",
|
||||
"examples/acp-agent/tests/**/*.e2e.ts",
|
||||
"examples/acp-agent/tests/**/*.snapshot.ts"
|
||||
],
|
||||
"project": ["scripts/**/*.ts", "examples/**/*.ts"]
|
||||
},
|
||||
|
||||
@@ -25,6 +25,9 @@ pre-push:
|
||||
- name: test
|
||||
run: pnpm run test
|
||||
|
||||
- name: snapshot
|
||||
run: pnpm run test:snapshot
|
||||
|
||||
- name: hygiene
|
||||
run: pnpm run hygiene
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"test": "vitest run",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
||||
"test:snapshot": "vitest run --config vitest.snapshot.config.ts",
|
||||
"test:snapshot:record": "DSH_SNAPSHOT=record vitest run --config vitest.snapshot.config.ts --update",
|
||||
"knip": "knip",
|
||||
"publint": "tsx scripts/publint-all.ts",
|
||||
"doc-typecheck": "tsx scripts/doc-typecheck.ts",
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ export default defineConfig({
|
||||
// instead applies the one root map to every importer.
|
||||
plugins: [tsconfigPaths({ projects: ['./tsconfig.test.json'] })],
|
||||
test: {
|
||||
include: ['packages/*/tests/**/*.spec.ts'],
|
||||
include: ['packages/*/tests/**/*.spec.ts', 'examples/*/tests/**/*.spec.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
// Coverage measures OUR runtime source. Types-only files carry no
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
// Snapshot tests: `pnpm run test:snapshot`, file pattern *.snapshot.ts.
|
||||
// REPLAY by default — they boot the real acp-agent subprocess against a
|
||||
// recorded session JSONL fixture (no API key, no network) and diff the
|
||||
// normalized stdout transcript + re-persisted log against committed goldens.
|
||||
// `pnpm run test:snapshot:record` (DSH_SNAPSHOT=record + -u) re-records the
|
||||
// fixtures against the real API and refreshes the goldens.
|
||||
//
|
||||
// Replay loads no .env (it must never reach the network — a recorded fixture
|
||||
// drives the model). Record reads DEEPSEEK_API_KEY from the env or a gitignored
|
||||
// repo-root .env, so a contributor with a key only in .env can still record.
|
||||
if (process.env.DSH_SNAPSHOT === 'record') {
|
||||
try {
|
||||
process.loadEnvFile(new URL('.env', import.meta.url).pathname)
|
||||
} catch (error) {
|
||||
// ENOENT (no .env) is fine — the key may already be in the environment.
|
||||
// Surface any other failure rather than silently recording with wrong env.
|
||||
if ((error as NodeJS.ErrnoException | null)?.code !== 'ENOENT') throw error
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
// Same resolution note as vitest.config.ts: bare workspace names resolve
|
||||
// through the root tsconfig paths map; the native option cannot do this.
|
||||
plugins: [tsconfigPaths({ projects: ['./tsconfig.test.json'] })],
|
||||
test: {
|
||||
include: ['examples/*/tests/**/*.snapshot.ts'],
|
||||
// Each test boots a subprocess; give it room, and run files one at a time
|
||||
// (a record run hits the live API, and replay subprocess boot is heavy).
|
||||
testTimeout: 120_000,
|
||||
hookTimeout: 30_000,
|
||||
fileParallelism: false,
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user