Add the activity capability seam: ctx.activities is a pure observation
plane where long-running work (background bash/pwsh, workflow runs) opens a
handle, appends transient output into a byte-offset ring buffer, and settles
exactly once. Nothing model-visible changes: reads are non-consuming and
lossy-by-design (absolute UTF-8 offsets survive eviction and report gaps),
correlation carries {callId, jobId}, and visibility is owner-fenced like jobs.
The seam is strictly optional. Producers tap it through ctx.get('activities')
and contain every observation failure, so an unregistered or broken registry
never affects the underlying job (proven by broken-registry tests).
Ship the full path in one change:
- @deepseek-ai/dsh-activity: Service Definition, pumpActivityOutput pull
substrate, invariant companion; dsh-activity-local: ring-buffer provider
with retention config and owner cleanup.
- shell: ShellProcess.observed exposes non-consuming stdout/stderr readers;
tool-bash/tool-pwsh mirror background jobs into activities.
- tool-workflow: phase/log/agent lines mirrored as a workflow activity.
- api-activity-controller: Typert Remote control (roster baseline+buckets)
and observe (opened/output/status frames, coalesced and byte-capped)
streams plus the client feed service with reference-counted observation.
- ui-activity: session-header action rendering live output through
TerminalBlock, which now shows output while a command is still running.
Docs: activity subsystem page, architecture row, regenerated catalogs, and
the Agent Note (2026-08-24-activity-observation-seam), all bilingual. A
keyless web e2e replays a real run_in_background stream to ARIA goldens.
@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, configuration errors, and boot failures exit nonzero.
Entry modes
| Command | Purpose |
|---|---|
dsh --profile <name> |
Boot the named profile under $DSH_HOME/profiles/<name>. |
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 |
Alias of --profile web. |
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; any other profile must be created through dsh plugin.
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). Launcher flags therefore come first, and 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 patchReload lifecycle) and a cordis.patch.yml (the user's own patch layer). patchReload: live watches the profile and home-level patch files; startup applies them once.
The tree composes over an empty root:
- each bundle's patch in
dsh.profile.bundlesorder - then the profile's
cordis.patch.yml, then the home-level$DSH_HOME/cordis.patch.yml - then
--patchoverlays
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.
The CLI behavior reference owns exact layer precedence, flags, shutdown behavior, deployment defaults, and source execution.
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.