Files
deepseek-harness/examples/acp-agent/cordis.yml
Tianyi Cui 8adcbceeed feat(hooks): dsh-hooks-claude + dsh-hooks-codex bridges (hooks stack PR-F)
The two bridge plugins that run a user's existing Claude Code / Codex hook
config on the harness's typed interception seams, built on the shared
dsh-hook-protocol library. A bridge is a faithfulness adapter, not a power
tool: anything it does a native cordis plugin does more powerfully — the
bridge exists only to run UNMODIFIED external hooks.

- dsh-hooks-claude: CC dialect. Seven hook points (SessionStart,
  UserPromptSubmit, PreToolUse, PostToolUse, Stop, SubagentStart,
  SubagentStop), CC per-event stdin payloads, env + ${CLAUDE_PLUGIN_ROOT}/
  ${CLAUDE_PROJECT_DIR} substitution, literal-or-regex matcher.
- dsh-hooks-codex: Codex dialect — a deliberate subset. Five hook points,
  always-regex matcher, snake_case payloads (turn_id/model, no trailing
  newline), no env/substitution, block-only decisions.

Both map the neutral merged outcome onto the seam's typed Decision and stamp
an explicit {kind:'plugin'} source on injected context (so it is never
mislabeled as a user prompt). Config parse-failure is contained; only command
hooks run. updatedInput is logged+warned (input rewrite deferred); the Stop
loop-guard is deferred (TODO).

Tests: per-file 100% — config-parse unit branches + per-seam mappings
end-to-end through the REAL loop + REAL bash + REAL shell scripts (scripted
mock model only) + a real-Loader export-shape guard. A keyless ACP snapshot
scenario (hook-prompt-block) proves a UserPromptSubmit hook blocks a prompt
end-to-end (rejected turn -> ACP cancelled, hook/* events in the log); a
with-key e2e (hooks.e2e.ts) proves a PreToolUse hook blocks real bash
(verified on disk). The snapshot normalizer now scrubs hook/result.durationMs.

RFC: docs/rfc/implemented/feature/2026-06-30-hook-bridges.md
2026-07-01 04:23:49 +08:00

107 lines
4.4 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 for agent-core's tool-bash schema.
# FIXME(config-comments): keep this executor note from implying bash is the
# whole tool set; subagent and todo_write are loaded below.
- 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.
For multi-step work, use the todo_write tool to track a task list:
send the WHOLE list each call (it replaces the previous one), keep at
most one task in_progress (exactly one while work remains), and mark a
task completed as soon as it is done. Skip it for trivial single-step
tasks.
# The subagent seam + both in-process backends + two model-facing tools, as leaf
# entries after the app (which provides ctx.agents/ctx.tools). spawn (a fresh
# child) and fork (a child seeded with the parent's completed-turn prefix) are
# both reachable by the model: dsh-tool-subagent is loaded once per backend with
# a distinct toolName (subagent → spawn, subagent_fork → fork), 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
toolName: subagent
- id: tool-subagent-fork
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: fork
toolName: subagent_fork
# The model-facing todo_write tool: whole-list task tracking written to the
# session log (todo/write), surfaced to the ACP client as a `plan` update.
- id: tool-todo
name: '@deepseek-ai/dsh-tool-todo'
# The Claude Code hook bridge, pointed at a `hooks.json` in the session cwd. With
# no such file present the parse fails-soft and the bridge registers nothing (a
# silent no-op); a session whose cwd holds a `hooks.json` runs those hooks on the
# interception seams. stdout is the ACP JSON-RPC channel — the bridge's warnings
# go through ctx.logger (no exporter here), never to stdout.
- id: hooks-claude
name: '@deepseek-ai/dsh-hooks-claude'
config:
configPath: ./hooks.json