Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.
Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.
The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.
Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
One host registry serves every composition in the process, so its two
service-wide collections answered per-owner questions process-wide. `start()`
asked only whether SOME surface was attached, so an agent whose own composition
loads no `tool-tasks` could start work it has no tool to collect or stop as soon
as any other preset attached one — and the answer changed depending on which
sessions happened to be open. `settle()` walked every registered listener, so a
task settling without a waiter injected one completion notice per mounted
preset into the same owner.
Both collections now sit in `ScopedLayers`, the layered-registry primitive
`tools` and `skills` already use: a registration files into its registering
context's scope, and a read unions the global layer with the owner's scope
chain. A surface or listener registered from an unscoped context lands in the
global layer and serves every owner, which is exactly the host-plane
composition's own controls, so the TUI path is unchanged without a special
case.
This supersedes the consumer-side filter in the previous commit. That filter
produced the right notices but sat in the wrong layer: it left the `start()`
gate process-wide, it could not be enforced against a producer that resolves
the registry directly, and it made a Consumer carry scope knowledge that the
other layered registries keep in the registry. `tool-tasks` is scope-agnostic
again and the `dsh-scope` edge moves to `tasks-local`.
`start()`'s refusal is now owner-relative, so its model-visible text names the
agent rather than the process. The shipped `minimal` preset keeps
`enableRunInBackground: false`, no longer as the safety boundary — the registry
owns that now — but so an agent that could never collect a task is not offered
the parameter at all.
Refs #2141
The ShellDialect marker on BashExecutor and the load-time rejection in
tool-bash/tool-pwsh force every test and example fake executor to declare
a dialect for a guard with no in-repo or plausible deployment to catch
(shipped compositions always pair the tool with its matching executor),
and they break the example composition suites that stub ctx.bash without
a dialect (agent-spine-demo/cli-demo/acp-demo lost the bash tool).
Keep the non-dialect doc fixes that landed with the attempt: the tool-bash
README bashEnv contract trim with the stale example import removed, the
acp-snapshot suite.ts 'usable pwsh' JSDoc, and the pwsh.cordis.yml comment
indent. The parity note records the attempt and revert under Alternatives.
The seam gains ShellDialect ('bash' | 'powershell' - concrete shells, not
families: zsh or fish would be their own values, never 'bash'); bash-local
declares bash (bash-sandbox inherits), pwsh-local declares powershell, and
both tools throw at load when the mounted executor speaks another dialect -
previously tool-pwsh over bash-local handed PowerShell text to bash -c and
the deployment error surfaced as ordinary nonzero exits. Pinned by mismatch
tests on both tools; the parity note records the contract (both languages).
Also from the review round: the tool-bash README's managed-environment
section becomes a summary linking the owning dsh-bash-env contract (the
duplicated prose carried a stale owner in its example import), the
pwshOnly JSDoc drops the stale 'on PATH' phrasing, and the task-tools
contract comment in the two pwsh compositions is indented into its block.
- scrubbedParentEnv folds case before the DSH_ prefix check (Windows env
names are case-insensitive; a parent dsh_* entry read back as \*
in the child) and the service spec pins the lowercase probe.
- The acp.snapshot.ts pwsh probe follows resolvePwshPath() like the package
suites, so a Windows host with only an install-location pwsh still runs
the scenario.
- pwsh-tool-turn is re-recorded around [Console]::Out.Write('PWSH_OK'):
the fixture carries no platform newline, so one recording replays on
Windows and POSIX alike (record + refresh; replay-verified keyless).
- The pwsh-local Known Limitations bullet drops the self-defeating no-op
advice: & { } is scoped to param(...), using/#requires scripts run from a
file (both languages, pairing re-recorded).
- The capability-seams graph moves ctx.bashEnv ownership to bash-env and
lists pwsh-local/tool-pwsh on the ctx.bash seam (source updated,
docs regenerated).
- The tool-bash presenter fixture retires the stale 'command aborted'
literal for the shipped 'tool call aborted' message.
Review feedback (tianyicui): 'process' is a poor service name. The family is
now packages/subprocess/ — @deepseek-ai/dsh-subprocess (ctx.subprocess,
abstract SubprocessService, Subprocess* vocabulary) and
@deepseek-ai/dsh-subprocess-local (LocalSubprocessService) — renamed
throughout code, compositions, docs (en+zh, pairs re-recorded), catalogs,
and gates. 'subprocess' is the precise term for managed OS children (the
Python-stdlib sense), avoids colliding with Node's global process object,
and reads as one system beside dsh-subagent-subprocess.
ds-review-bot findings addressed:
- kill() on a settled handle is now a no-op (no signal to a possibly-reused
pgid, no referenced grace timer delaying exit); pinned by a spy test.
- The moved DshEnvironmentKey/DshEnvironment/CollectedOutput types get
drift-checked type-equiv blocks on the new subprocess.md page, restoring
their manifest registration.
- subprocess.md is registered in the core.md sub-page index (en+zh).
Review feedback (tianyicui, PR #657 inline): the missing-service message
should mention dsh-tasks, which defines ctx.tasks, rather than promoting a
specific backend. The seam's own surfaces (README, the direct-mount fence)
keep pointing at implementations, so the pointer chain still lands on
dsh-tasks-local without the producer strings going stale when another
backend becomes the recommended default. Agent Note updated accordingly
(en+zh, re-recorded).
New process/ capability family: @deepseek-ai/dsh-process owns ctx.processes —
abstract ProcessManager.spawn(spec) over a fully-explicit ProcessSpawnSpec —
plus the shared DSH_* managed-environment and CollectedOutput vocabulary;
@deepseek-ai/dsh-process-local carries the former bash-local run.ts plumbing
(detached groups, tail-keep spill-backed output, credential scrub, kill
escalation, kill-and-join disposal) with no config of its own.
dsh-bash-local becomes a consumer: it keeps command defaulting, the fused
deadline timedOut/aborted classification, the model-friendly terminal env
(now merged through the ordinary env channel), and the [stderr]-marked
background read merge, and spawns through ctx.processes. Background-process
lifetime moves to the manager, so an executor reload no longer kills live
background work; a background spawn failure is injected once into the read
path instead of being buffered as fake stderr. dsh-bash re-exports the moved
vocabulary so bash consumers keep one import root; dsh-bash-sandbox only
redeclares the inherited inject.
Every composition loading a bash executor now loads dsh-process-local (CLI,
examples, python bundled runtime, create-sdk bash feature, inline test
configs).
The tasks/ family now matches the capability-seam shape: @deepseek-ai/dsh-tasks
keeps the abstract TaskService (ctx.tasks contract, vocabulary types, snapshot
invariant companion) and the new @deepseek-ai/dsh-tasks-local carries the
process-local registry (LocalTaskService: in-memory store, settlement,
owner-cleanup effects, teardown, TASK_WAIT_TIMEOUT). Compositions and test
harnesses now load dsh-tasks-local; producers, TaskKindMap merges, and
dsh-tool-tasks keep importing the seam only.
Producer misconfiguration diagnostics name dsh-tasks-local because loading the
implementation is the fix. The registry behavior suite moves to tasks-local;
the seam keeps a stub-subclass registration test and the probe-based invariant
suite.
The background-task change repeated its lifecycle design across implemented RFCs, package READMEs, JSDoc, test commentary, and model-visible schemas. That repetition obscured the contracts that maintainers must preserve and added avoidable prompt tokens.
Rewrite the implemented RFCs around the current design, keep authorization, exact-owner cleanup, wait/abort ordering, producer quiescence, and teardown-failure guarantees at their owning surfaces, and remove peer surveys, review history, control-flow narration, and emphatic restatement.
Shorten the task and subagent schema wording, synchronize the bilingual tool cookbook, and regenerate the config, service, RFC, tool, and replay snapshot derivatives. Runtime behavior is unchanged; test edits update prose-only assertions and descriptions.
Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox
RFC's deferred cross-family phase.
- dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the
deployment default mode + workspaceRoot and the per-session override event,
renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter.
Decouples the bash seam from dsh-session.
- dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences
write/edit by the per-call mode (read-only denies, workspace-write contains to
the workspace + temp roots via the shared writableRoots, danger passes
through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent
re-canonicalization. A policy fence in trusted code, not a kernel boundary.
- dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider
ladder, denial/hint markers, approveEscalation) both tool families use;
approveEscalation takes a structural approver so dsh-sandbox gains no
approval/agent dependency, and both tools stay duplication-free.
- tool-fs: write/edit advertise sandbox_permissions/justification under a
confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker,
and resolve the same one-approved-wider retry.
- examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating
that disabled the fs stack under confined modes.
RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old
sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.