The snapshot tier was built single-session: dsh-llm-replay served calls from one global positional cursor, and the harness harvested one session log. A subagent runs as a second agent with its own session, so a parent→child scenario could neither replay deterministically nor harvest the child's log. This resolves the TODO(subagent-snapshots) deferral from the subagent RFC. - Stamp the calling session id onto the model request: GenerateOptions.sessionId (typed Branded<'SessionId'> to avoid the dsh-llm↔dsh-session cycle), set by the agent loop from agent.session.id. Adapters ignore it; an llm/stream listener routes by it. - Key replay per session: dsh-llm-replay loads the parent log plus one per child (childFiles / $DSH_SNAPSHOT_CHILD_FILES), derives a script per recorded session, and binds each live (freshly-random) session to a recorded script by first-call order — parent first (earliest createdAt, first to stream). Keys by WHO calls, so it survives a future concurrent/backgrounded subagent; a global cursor would not. An unrecorded extra session fails loud. - Harvest every log: the harness collects all .jsonl across cwd buckets, ordered primary-first (top-level, then children by createdAt), and RunResult exposes the plural sessionLogs. The spec writes each back on record (session.jsonl + session.<n>.jsonl) and diffs each against its fixture on replay. - Wire the subagent seam + spawn + fork + tool into the acp-agent example (both cordis configs) and add two nested scenarios recorded against the real API: subagent-spawn (parent + 1 child) and subagent-multi (parent + 2 children, 3 sessions). Both replay keyless in the default gate. A new RFC documents the design (docs/rfc/implemented/testing/). Single-session replay is unchanged (a call with no sessionId is one anonymous primary session). TODO follow-up: a dedicated branded-ids package could own the SessionId brand and dissolve the cross-package cycle note; out of scope for this testing PR.
75 lines
3.0 KiB
YAML
75 lines
3.0 KiB
YAML
# The acp-agent plugin tree: the ACP server. Also the snapshot RECORD config
|
|
# (the dsh-acp-agent bin selects it for DSH_SNAPSHOT=record): a real llm-deepseek
|
|
# run whose persisted log the snapshot harness harvests. Just the two swappable
|
|
# backends — the DeepSeek adapter and the local bash executor — plus the ACP
|
|
# server app (@deepseek-ai/dsh-acp-agent), which bundles the agent-core spine,
|
|
# JSONL persistence, and the ACP bridge.
|
|
#
|
|
# CRITICAL: this tree loads NO stdout logger and NO hmr — stdout is reserved for
|
|
# the ACP JSON-RPC protocol (see packages/ui/acp). That guarantee is now a
|
|
# property of @deepseek-ai/dsh-acp-agent (it contains no logger entry), not a
|
|
# leaf convention: there is no logger here to get wrong.
|
|
#
|
|
# Requires DEEPSEEK_API_KEY (and optionally DEEPSEEK_BASE_URL) — the
|
|
# dsh-acp-agent bin loads the gitignored repo-root .env first (on STDERR only).
|
|
|
|
# The DeepSeek adapter.
|
|
- id: llm-deepseek
|
|
name: '@deepseek-ai/dsh-llm-deepseek'
|
|
config:
|
|
apiKey: !!js process.env.DEEPSEEK_API_KEY
|
|
baseURL: !!js process.env.DEEPSEEK_BASE_URL
|
|
models:
|
|
- deepseek-v4-flash
|
|
- deepseek-v4-pro
|
|
|
|
# Local bash executor (the agent's only tool, via agent-core's tool-bash schema).
|
|
- id: bash
|
|
name: '@deepseek-ai/dsh-bash-local'
|
|
config:
|
|
timeoutMs: 60000
|
|
|
|
# The ACP server app: the agent-core spine + JSONL persistence + the ACP bridge.
|
|
# 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: acp-agent
|
|
name: '@deepseek-ai/dsh-acp-agent'
|
|
config:
|
|
model: deepseek-v4-flash
|
|
persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions'
|
|
systemPrompt: |
|
|
You are a coding assistant driven over the Agent Client Protocol.
|
|
|
|
Your tools are bash (plus bash_output/bash_kill for background tasks)
|
|
and subagent. 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.
|
|
|
|
Use the subagent tool to delegate a focused, self-contained subtask to
|
|
a fresh child agent (it works in its own context and returns only its
|
|
final result) — give it a complete, standalone instruction.
|
|
|
|
# The subagent seam + both in-process backends + the model-facing `subagent`
|
|
# tool, as leaf entries after the app (which provides ctx.agents/ctx.tools). The
|
|
# tool is bound to the `spawn` backend (a fresh child); the `fork` backend is
|
|
# loaded too so a multi-child scenario can exercise both transports.
|
|
- id: subagent
|
|
name: '@deepseek-ai/dsh-subagent'
|
|
|
|
- id: subagent-spawn
|
|
name: '@deepseek-ai/dsh-subagent-spawn'
|
|
config:
|
|
providerName: spawn
|
|
|
|
- id: subagent-fork
|
|
name: '@deepseek-ai/dsh-subagent-fork'
|
|
config:
|
|
providerName: fork
|
|
|
|
- id: tool-subagent
|
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
config:
|
|
provider: spawn
|