Files
deepseek-harness/.agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.md
T
Turtle f290a8b851 refactor(cli)!: one shared base config with per-surface overlays
`dsh` shipped two config trees that were 43 rows the same: apps/cli/cordis.yml
composed web as 74 flat rows, while the TUI booted examples/tui-agent/cordis.yml
whose single `@deepseek-ai/dsh-tui-demo` row mounted twelve plugins behind a
twenty-key pass-through Config. Neither file was what its location claimed —
apps/cli hardcoded the "example" as the product default and the "demo" bundle
was the application — and every capability change had to be made twice.

- apps/cli/base.cordis.yml holds the 43 shared rows; tui.cordis.yml and
  web.cordis.yml are patch lists stating only what differs per surface
- overlays apply as SIBLING patch lists at one include level, because include
  patches never cross an include boundary. Precedence: base < surface <
  (--config | personal ~/.dsh/config.yaml) < launcher flag/profile patches
- `--config` now applies an overlay INSTEAD OF the personal one, so a demo or
  test tree never inherits the user's route; new `--config-replace` boots a file
  as the entire tree (the old `--config` behaviour). Both survive /resume
- vendor/include: index each `insert`ed row as it is added so a later patch can
  configure or disable it. Upstream built the id index once before the patch
  loop, leaving every surface-only row — the whole TUI front door — silently
  unpatchable from user config. Logged as local modification 8
- session identity moves to dsh-agent-loop's CONFIGURED_AGENT_IDENTITIES_KEY;
  dsh-tui's MAIN_SESSION_ID_KEY is deleted (only the bundle read it)
- delete examples/tui-agent, examples/cordis-agent, packages/examples/tui-demo;
  TUI tests → apps/cli/tests, cordis e2e → packages/cordis/tool-cordis/tests,
  examples/code-mode survives as an overlay leaf
- `dsh web` gains --config, threaded into AppCLIEntry as an extra overlay

Three latent defects surfaced and are fixed here: the TUI captured the optional
sessionQuery service once at construction and could permanently disable /resume
when it won the mount race; the session-store root silently reverted to a
project-local ./.sessions; --config-replace was dropped by the resume handoff.

Verified by booting each tree through the real Loader (TUI 55 entries, web 75,
zero unsettled) rather than reading YAML. All eight terminal snapshots replay
byte-identically; 14/14 PTY smoke, 112/112 snapshots, 25/25 doc-sync, hygiene
and lint clean.
2026-07-29 21:15:42 +08:00

5.7 KiB

Agent Note: Remove the stdio and Echo agents

Status: implemented

English | 中文

Problem

DeepSeek Harness exposed two redundant product agents beside the TUI and Headless coding agents. The line-oriented stdio agent duplicated terminal interaction and non-interactive execution with a mixed prompt/output protocol. Echo duplicated Headless as a network-free mock model plus one teaching tool, making a test fixture into a user-facing agent and the default quick-start path.

Both agents carried support surfaces beyond their leaf configurations. Stdio owned a UI plugin, app package, SDK interface, REPL leaf, prompt protocol, and Loader tests. Echo owned a runnable command, mock adapter, tool, CI demo gate, graph entry, teaching references, and a shared test fixture. Keeping any of those product paths would preserve the redundant agent indirectly.

Standard input and output remain protocol boundaries for ACP, JSON-RPC, MCP, and child processes. Deterministic model adapters also remain valid inside tests. Those mechanisms do not justify a line-oriented or mock-only product agent.

Decision

The stdio and Echo agents are removed without compatibility packages, modes, commands, or aliases. The stdio UI and app packages, examples/repl-agent, examples/echo-agent, demo:repl, demo:echo, their dedicated tests, and supporting manifests, gates, graphs, and documentation entries are deleted.

The remaining application roles are explicit:

  • @deepseek-ai/dsh-tui owns terminal-interactive execution. It rejects non-TTY streams before Loader boot; apps/cli/base.cordis.yml plus the tui.cordis.yml overlay own the complete coding composition, with the Code Mode overlay in examples/code-mode and PTY plus terminal-snapshot coverage in apps/cli/tests/.
  • @deepseek-ai/dsh-cli-demo owns non-interactive execution, including pipes. examples/headless-agent owns the real-model one-shot composition, replay snapshots, generic real-agent suites, and test-only keyless Loader fixtures.
  • @deepseek-ai/dsh-acp-demo and @deepseek-ai/dsh-jsonrpc own their framed protocol integrations.

The SDK project model and create/config workflows replace the stdio run-interface option with tui; generated TUI projects compose @deepseek-ai/dsh-tui and create or resume one exact session. Repository-facing demo documentation requires a DeepSeek API key and leads with the real Headless or TUI agents.

Keyless validation is test-owned. The Headless Loader smoke uses a fixture adapter to exercise a real tool round trip, the CLI built-bin suite pins output, persistence, failure, and signal semantics, and package-specific Loader tests keep deterministic adapters beside their scenarios. None is exposed as a runnable mock agent.

Verification

TUI and Headless Loader coverage run the real app packages in source and built modes. PTY-driven subprocess coverage is reserved for the TUI lifecycle; other entry-point smokes use the one-shot pipe protocol. Headless proves its task/result and tool-call contracts. Generated graphs and repository searches reject stale package, command, leaf, SDK-interface, createStdioChat, and StdioRuntime references.

The TUI PTY smoke includes the Code Mode overlay composition, while examples/cordis-agent/tests/keyless-smoke.e2e.ts provides a minimal PTY boot over the real Cordis-agent Loader tree. The built dsh bin rejects a piped TUI launch before Loader boot and points at its one-shot -p mode; apps/cli/tests/built-bin.e2e.ts pins that path, while cli-demo's built-bin suite runs text, JSON, and structurally parsed stream-json output under plain Node, persists fresh sessions, and rejects invalid arguments and missing config without contaminating stdout. Time-context integration uses the real Headless composition for two ordered turns, while its package tests own finer elapsed-time behavior.

Alternatives considered

  • Keep the line agent only for pipes — rejected because Headless has a bounded task contract, format-pure stdout, durable completion, and process exit status.
  • Keep, fold, or promote the readline helper as a package — rejected because it had one app consumer and no independently swappable contract. Folding it into the stdio app removed an unjustified support-package boundary but still retained the redundant product; a future standalone line UI needs a real second consumer before reintroducing that package.
  • Keep Echo as the keyless quick start — rejected because the first product experience should exercise the real model and supported coding agent, not a scripted adapter with a bespoke tool.
  • Keep Echo only as a CI demo command — rejected because test-owned Headless fixtures cover the same Loader and built-artifact boundaries without preserving a mock product leaf.
  • Remove every stdio or mock mechanism — rejected because framed protocols, process I/O, and deterministic test adapters are independent infrastructure, not the removed agents.

Consequences

  • Interactive and non-interactive product execution each have one owner and one runnable coding leaf.
  • The repository has no keyless user-facing agent demo; local agent demos require DEEPSEEK_API_KEY.
  • CI retains keyless real-entry coverage through test fixtures rather than a product command.
  • Existing stdio-agent configurations, Echo commands, and SDK --interface=stdio invocations fail instead of being translated.
  • Piped multi-turn interaction in one process and the readline provider for non-TTY ask_user_question are intentionally gone; resume covers durable multi-turn work, and a non-TTY composition must supply its own interaction provider.