Files
deepseek-harness/packages/ui/acp-agent
Tianyi Cui 4a0941fb4b feat(ui): share the app bins' boot glue in @deepseek-ai/dsh-app-boot
The four near-twin helpers the two published bins carried — loadEnv,
installFailLoud, assertEntriesLoaded, boot — live once in
packages/ui/app-boot, parameterized by the bin's diagnostic prefix and
injectable at their side-effect seams (warn sink, process slice), so
every branch sits under the per-file 100% coverage gate: the unit suite
drives boot() in-process against the real Loader (relative-specifier
configs) through both the settled-tree path and the fiber-less-entry
rejection, and exercises the ENOENT/unloadable .env split, the
Error/non-Error/stackless fail-loud arms, and the disabled-entry
exclusion. resolveConfigPath (snapshot-aware) becomes the single path
resolver for both bins.

Each bin.ts is now a thin self-executing composition plus its
app-specific lifecycle (acp: replay env-skip + stdin-EOF dispose;
stdio: nothing extra), exports nothing, and stays coverage-excluded;
the built-bin smokes still prove both artifacts under plain node in the
node_modules-shaped temp dir (now symlinking ui/app-boot), including
the missing-config non-zero exit.

Implements docs/rfc/implemented/simplification/2026-07-04-share-app-bin-boot-glue.md
(moved from proposed/ and amended); the extract-example-app-packages
RFC's bin-ownership facts are amended in the same change.
2026-07-04 16:43:06 +08:00
..

@deepseek-ai/dsh-acp-agent

The ACP server app: a Cordis app plugin that composes the providerless agent spine (@deepseek-ai/dsh-agent-core) with the front-door cluster an Agent Client Protocol server needs, and a bin that boots a leaf cordis.yml speaking ACP JSON-RPC on stdio.

It is the structured counterpart to @deepseek-ai/dsh-stdio-agent: both consume the same spine, but this one bakes in the OPPOSITE front-door cluster.

What it bakes in — and what it deliberately omits

stdout is the ACP JSON-RPC channel, so the cluster is defined as much by what it LEAVES OUT as what it includes:

Plugin Why
@deepseek-ai/dsh-agent-core the spine, pre-creating no agents (ACP session/new creates them on demand)
@deepseek-ai/dsh-session-persistence-jsonl durable JSONL session log (the bridge advertises loadSession)
@deepseek-ai/dsh-acp the bridge that owns stdout for JSON-RPC
console logger omitted — it writes to stdout and would corrupt the protocol frames (the stdout-purity footgun)
hmr omitted — the editor owns the subprocess

Because the package wires no logger entry, an ACP leaf has nothing to get wrong by default: it only picks backends, so the common mistake — copying a console-logger entry from the stdio config — has no place here. (A leaf author technically can still add @cordisjs/plugin-logger-console as a sibling entry; the package can't forbid that. So the rule stands: never add a stdout logger to an ACP leaf — stdout is the JSON-RPC channel. Use a stderr exporter if you need logs.)

Config

Key Default Routed to
model (required) the per-session agent template the bridge creates agents from
systemPrompt (required) the per-session agent's system prompt
persistenceRoot ./.sessions the JSONL backend's root directory

The leaf supplies the swappable backends: an LLM adapter (llm-deepseek for the real model, llm-replay for keyless snapshot replay) and a bash executor (bash-local).

The bin

dsh-acp-agent [path-to-cordis.yml] (default ./cordis.yml):

  • loads a gitignored .env from the cwd — skipped in snapshot REPLAY so a stray key can never trigger a live call;
  • honors DSH_SNAPSHOT=replay by booting the sibling cordis.snapshot.yml (the keyless replay tree, llm-replay in place of llm-deepseek);
  • in a snapshot run, disposes the context on stdin EOF so the session log is fully flushed before exit.

Run it under node --expose-internals: the cordis Loader resolves the config's bare plugin specifiers through its internal module loader, active only under that flag. (demo:acp runs under tsx, whose tsconfig paths map resolves them instead.)

All diagnostics go to stderr — stdout is the protocol.