Files
deepseek-harness/packages/support/llm-replay
Tianyi Cui 7aabd2a3df Add in-process subagent backends: spawn (fresh) and fork (seeded)
The second PR of the subagent seam: the two in-process backends that run a
child agent on the same cordis context, reusing the agent factory's quiescent
AgentHandle teardown. Both register on ctx.subagents (PR1's named-provider
registry) and share one run driver.

- dsh-subagent-spawn: a FRESH child via ctx.agents.create — own session, the
  parent's model by default (overridable), zero inherited conversation. Also
  exports the shared in-process run driver (startInProcessRun): mint ids, stamp
  cwd/parentSession-lineage/depth, drive the one-shot (send → whenIdle), read
  the last assistant/message + turn/end reason, dispose to quiescence.
- dsh-subagent-fork: a child SEEDED with the parent's balanced completed-turn
  prefix (the log up to and including its last turn/end), so the child inherits
  context. The in-flight unbalanced turn is excluded — a raw seed would fail the
  invariants replay. Proven: a regression test goes red if the boundary seeds
  the open turn.
- Seam extension: CreateAgentOptions.seed, threaded through AgentLoop.createAgent
  → ctx.sessions.prepare({ seed }) (the primitive resume already used). This is
  the fork-lineage path the TODO(sub-agents) markers anticipated.
- Depth: a merge-extensible AgentOptions.subagentDepth (0 top-level, parent+1 for
  a child); the depthLimit capability refuses a spawn past request.maxDepth.

Tests: real-loop unit tests for both backends (mock MODEL only, real loop +
invariants), a multi-subagent test (one parent drives a fork AND a spawn child
then keeps working), and a with-key e2e (a real parent delegates via the
`subagent` tool to a real child that writes a file on disk — world-verified).
100% per-file coverage. The coding-agent demo wires the spawn backend + tool.

Snapshot coverage of nested agents is deferred to a stacked follow-up
(TODO(subagent-snapshots)): dsh-llm-replay is a single global positional cursor
that cannot route calls to a parent vs. a child on one context. Recorded in the
RFC's deferrals and a new AGENTS.md rule: designing a subsystem must design its
test infrastructure END TO END up front, verifying the snapshot/e2e harness can
express the new shape — a gap this plan hit.
2026-06-22 05:58:40 +08:00
..

@deepseek-ai/dsh-llm-replay

A replay LLM plugin for keyless snapshot tests. It 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 test can boot the real agent against a fixed model transcript with no API key.

Its consumer is the ACP snapshot harness in examples/acp-agent, which loads this plugin (via cordis.snapshot.yml) in place of a real LLM adapter. The package exists so its derive/parse/replay logic falls under the per-file 100% coverage gate on packages/*/src (the same logic, while it lived under examples/, was outside the gate).

How the fixture works

The fixture IS the persisted session log (<scenario>/session.jsonl). Its assistant/chunk events carry every StreamChunk, so grouping them by (turn, step) reconstructs each stream() call's chunk sequence (one model call per loop step). 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, where the log holds only a turn/end {error} and 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.

Config

Key Type Default Notes
file string $DSH_SNAPSHOT_FILE Path to the per-scenario session.jsonl fixture. Required (config or env).
overrideFile string $DSH_SNAPSHOT_OVERRIDE Optional path to a ReplayEntry[] sidecar that replaces the derived script.
- id: llm-replay
  name: '@deepseek-ai/dsh-llm-replay'
  # file/overrideFile default to $DSH_SNAPSHOT_FILE / $DSH_SNAPSHOT_OVERRIDE,
  # set by the snapshot harness per scenario.

Exports

  • installLlmReplay(ctx, config) — install the llm/stream listener; returns the disposer (HMR safety). Use this in tests to drive replay without the Loader or env vars.
  • loadReplayScript(config) — resolve the ReplayEntry[] for a scenario (sidecar override if present, else derived from the JSONL; fail-loud if the fixture is missing).
  • deriveReplayScript(events) / parseSessionLog(text) — the pure helpers that turn a recorded session log into a script. A derived group must end in a finish chunk; a group without one is the fingerprint of a thrown stream() and must instead be expressed via an override sidecar.
  • Types ReplayEntry / ReplayConfig / Config.

Plugin export shape

Named name / inject / Config / apply, with no default export: the cordis Loader's unwrapExports does exports.default ?? exports, so a stray default would collapse the module to the bare function and drop the inject namespace (see docs/postmortem/0001).