Files
deepseek-harness/apps/cli
Dudu-0223 ff563d07e0 feat(subagent): name subagents in their creator catalog
Models coordinate subagents through send_message, interrupt_agent, list_agents,
and Team task owners, but every one of those addressed a member by a
36-character Session id. Each new child now has a name, unique among its
creator's children, recorded as a field of the subagent/catalog entry the
creator already appends (payload version 3).

The Session id stays the only execution identity for routing, authorization,
persistence, task ownership, and navigation. A name is a model-facing
reference: 1-32 lower-case letters, digits, or hyphens starting with a letter,
never `lead`, so a UUID never parses as one. Omission selects the first free
`agent-N`; an explicit duplicate or invalid name fails before startup. The
creator reserves the name synchronously at admission and releases it when the
catalog entry commits or startup fails, so no second write, rollback protocol,
or optional naming service exists.

The root is `lead`, and a descendant's full name is its path from the root
without a `lead/` prefix, such as `reviewer/checker`. Sibling-unique segments
make full names tree-unique without a root registry, and cold resume rebuilds
a member's path from the catalogs along its creation chain. A member below an
unnamed historical creator keeps its Session id as its name. resolveName()
reads only the catalogs along the path and grants no authority.

Tools, runtime-authored text, and the Web client consume names: delegation
takes an optional name and returns { kind, subagentId, name }; send_message and
interrupt_agent take `agent`; list_agents rows carry name, status, and
description; Team task results report the owner's name while storage keeps
ownerId; identity runtime context, message framing, and settlement notices use
full names. Recorded sessions, CLI and Web expectations, the Python runtime
snapshots, bilingual docs, an upgrade guide, and an Agent Note follow.
2026-11-02 14:58:32 +08:00
..

@deepseek-ai/dsh

English | 中文

The dsh command is the sole supported Node application launcher: profiles are ordered stacks of plugin-bundle patch layers under the user's own overrides. SDK and ACP are profiles, not separate public bins. The Python runtime wheel packages this same command; the SDK defaults to sdk, and the minimal example selects sdk-minimal. src/args.ts owns the command grammar, and src/bin.ts loads only the selected runner. Invalid commands, options from another mode, and fatal configuration or boot failures exit nonzero.

Entry modes

Command Purpose
dsh <name> / dsh --profile <name> Boot the named profile under $DSH_HOME/profiles/<name>.
dsh --profile <name> --from-default-profile <template> Replace a custom profile with a fresh copy of a shipped template, then boot it.
dsh --profile acp Serve automation clients over ACP stdio until disconnect.
dsh --profile headless "job" Run one fresh persisted session, print the final answer, and exit.
dsh --profile sdk Serve SDK clients over JSON-RPC stdio until shutdown or disconnect.
dsh --profile sdk-minimal Serve SDK clients with the standalone minimal agent tree.
dsh web Boot the Web profile.
dsh plugin --profile <name> <pnpm args> Manage a profile's plugins by forwarding to pnpm in the profile directory.

The invoking directory is the default workspace root. The web, headless, sdk, sdk-minimal, and acp profiles auto-initialize on first use from shipped templates. Create or reset another profile at a non-shipped name with --from-default-profile, or initialize a base-backed profile through dsh plugin. The desktop name is reserved for the Electron-owned profile, so the CLI rejects boot and config-dump requests for it. The npm CLI also rejects its plugin-management requests; the Desktop-installed command can manage the initialized Desktop profile using that installation’s runtime.

App arguments

The launcher parses only its own flags and hands everything after them to the booted profile, where any injected app plugin may parse the shared immutable snapshot (dsh-cmdline). The first token the launcher does not recognize starts the app's arguments:

dsh --profile web --port 8080       # --port belongs to the web app
dsh --profile tui --resume <id>     # example, assuming the tui profile is installed; --resume belongs to the terminal app
dsh --profile headless "run the tests"
dsh --profile web --help            # the web app's flags, not the launcher's
dsh --help                          # the launcher's own help

Profiles

A profile directory holds a package.json (out-of-tree plugin dependencies plus the profile manifest dsh.profile with its ordered bundles list) and a cordis.patch.yml (the user's own patch layer). dsh-hmr, when enabled in YAML, watches the profile manifest and both profile and home patch files, then recomposes all layers through one serialized reload. Without HMR, changes apply on restart. Edits arriving during watcher registration use the same nonfatal reload reporting as later edits. Plugin Manager shares package operations and the profile write lock with dsh plugin; package updates retain disabled bundle selections. CLI package commands inherit authentication variables and terminal descriptors, including interactive build approval; service calls retain their scrubbed environment and captured diagnostics.

Installation and profile startup enforce the declared engines.dsh range, or DSH peer ranges when it is absent, against the same runtime version shown by dsh --version. Incompatible plugins require an explicitly acknowledged exact-version exemption. The plugin manager's compatibility reference documents version-exemptions, allow-version, revoke-version, persistence, and risks.

The tree composes over an empty root:

  • each bundle's patch in dsh.profile.bundles order
  • then the profile's cordis.patch.yml, then the home-level $DSH_HOME/cordis.patch.yml
  • then --patch overlays

Bundles named in dsh.profile.bundles resolve from the dsh installation first (@deepseek-ai/dsh-base, @deepseek-ai/dsh-web-app, @deepseek-ai/dsh-headless, @deepseek-ai/dsh-sdk-app, @deepseek-ai/dsh-sdk-minimal, @deepseek-ai/dsh-acp-app), then from the profile's own node_modules, where pnpm installs out-of-tree plugins.

Use --dump-default-config and --dump-config to inspect the composed tree without booting it. --dump-config-schema imports the composed tree's declared plugin schemas and prints JSON Schema for entries and patches instead of configuration values; read the schema-dump safety and scope before inspecting untrusted plugins.

The CLI behavior reference owns exact layer precedence, flags, shutdown behavior, deployment defaults, and source execution. The startup and reload failure table compares optional and required plugin failures with configuration HMR.

Optional overlays

config/examples/ ships opt-in overlays for GitHub review webhooks, memory MCP servers, and runtime Cordis tools. They are never part of a default profile; the user guides and developer practice guides own setup and safety instructions.

Development

Production runs require built package and frontend artifacts. From the repository root, run pnpm run build separately, then use pnpm dsh <args...> to run the TypeScript entry and forward every argument; the source-execution reference owns the module-resolution contract.

The @deepseek-ai/dsh/profile-boot export provides the shared profile lifecycle to the Desktop host. A resolved application profile supplies its own installation anchor for runtime package resolution while retaining the Harness home patch, proxy environment, telemetry switch, patch reload, and bounded shutdown.

Packaged installations call the same runCli() entry with their package-manager executable. The Desktop carrier also enables plugin operations for its initialized profile; npm launches omit these options. Installation-owned package environments apply only to plugin package operations; the invoking directory, ordinary profile selection, and agent-shell PATH retain their CLI meanings.

The Web failure matrix runs the built CLI through startup failures and native configuration HMR with awaitWriteFinish enabled in test:expected. It verifies authenticated HTTP responses, diagnostics, recovery, process exits, and disposal without model API calls; the startup acceptance also covers the shipped required Web dependencies and port conflicts.