Files
deepseek-harness/examples/echo-agent
Tianyi Cui 3f83a4ee96 review: the persona becomes the system-prompt plugin's deployment config
Review round 2 (tianyicui inline comments):

- dsh-system-prompt itself registers the harness:identity (-100) and
  deployment:persona (0) sections — they must survive a swapped loop
  plugin, so they leave dsh-agent-loop; the persona text is the plugin's
  own validated 'persona' config. The model/cwd variables STAY on the
  loop: runtime facts of the agents it drives.
- AgentOptions.systemPrompt is deleted with all its forwarding plumbing:
  the app configs' systemPrompt keys become 'persona' routed through
  dsh-agent-core (schema = z.intersect of the owners'), the ACP bridge
  and tool-subagent stop carrying persona configuration, and subagent
  children now render the deployment persona like every other agent.
- Example personas drop transport/interface trivia (ACP, CLI) — facts
  irrelevant to the model.
- Root CONTEXT.md removed (not idiomatic); its persona definition was
  wrong under the new ownership anyway.
- Docs, READMEs, the prompt-variables RFC, and generated catalogs
  updated; new loop test pins the assemble-waterfall escape valve
  (an emptied assembly sends NO system field).
2026-07-05 23:23:46 +08:00
..
2026-06-11 10:55:05 +08:00

echo-agent

Runnable demo: stdin chat with a scripted mock model and an echo tool. The all-mock skeleton — "swap the backend, keep the app".

What it shows

This example is just a leaf cordis.yml: it loads the @deepseek-ai/dsh-stdio-agent app (which bundles the whole @deepseek-ai/dsh-agent-core spine, the console logger, JSONL persistence, the readline UI, and a pre-created main agent), and swaps in two example-local backends plus hmr:

  • mock-llm.ts — a mock LlmAdapter that streams scripted responses and calls the echo tool when the user types "echo ". Registered with ctx.llm.registerAdapter(['mock-echo'], …).
  • echo-tool.ts — a tool registered via ctx.tools.register(defineTool(…)) with typed execute args; echoes text back uppercased.

Swapping mock-llm for the real llm-deepseek adapter is all that separates this from coding-agent — the same app, a different backend.

Plugin files

File Role Key patterns demonstrated
src/mock-llm.ts LlmAdapter registration ctx.llm.registerAdapter(['mock-echo'], …), streaming chunks with the proper block-start/block-end protocol
src/echo-tool.ts Tool registration ctx.tools.register(defineTool(…)) with typed execute args, returning ContentBlock[]
cordis.yml Leaf wiring the two backends + hmr + one @deepseek-ai/dsh-stdio-agent entry carrying the app config

The spine, UI, persistence, and boot glue all live in @deepseek-ai/dsh-stdio-agent and the bundle it loads — this folder holds only the demo-specific mocks and the leaf wiring.

Run

pnpm run demo:echo
# or:
node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/echo-agent/cordis.yml

Type a message and press Enter. "echo " triggers a tool call round-trip (the mock model requests the echo tool, which echoes the text uppercased, and the next model step acknowledges it).

The session is persisted under .sessions/ relative to the directory you launch the demo from. pnpm run demo:echo runs from the repo root, so the logs land in <repo-root>/.sessions/ (a session with no cwd goes in the _no-cwd/ bucket, one .jsonl log per session). Clean up with: rm -rf .sessions