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.
A restriction was compiled against the global tool layer alone: only
global-layer tools were tested against `admits()`, and every chain-layer
tool was overlaid unfiltered afterward. That read the exempt set as "the
global layer" when what it means is "what this scope registers itself" —
two descriptions of the same set only while every model-facing tool sat in
the host composition.
Moving those rows onto the agent plane separated them. A preset's tools are
an ANCESTOR contribution to a joined agent, so a subagent's `toolFilter`
stopped constraining anything it was given; and with the global layer empty
`restrict()` rejected every name it received as unknown, failing the child
outright. With the same tools in the global layer the filter still admits
and applies normally, which is what makes this a regression of the move
rather than a standing limitation.
`view()` now filters everything a scope inherits — the global layer and
every ancestor layer on its chain — and exempts only the layer the scope
owns. That exemption is load-bearing rather than incidental: the delegation
runtime registers a child's `report` and structured-output tools into the
child's own layer, and a filter naming the capabilities the child may use
must not strip the machinery it answers through. Tool order, and with it
prefix-cache reuse, is unchanged: inherited names keep their global-then-
ancestor position and own-layer names still come last.
The diagnostic said "unknown global tool" while listing what is really the
inherited surface; it now names the surface it checks and says why an
own-layer name is not restrictable.
Fixes#2185
All agent/* and agent-loop/config-start-failed events take one payload
object carrying the agent subject; waterfall/serial payloads require a
signal and keep next as the final argument. PreStepContext and
RequestFailureContext are unfolded into payloads and retired.
goal/changed follows the same shape so agentEvents keeps its listener
error containment. ReactLoopAgent builds its scope carrier once in the
constructor. Regenerates scope resolvers, tool-cordis api catalog, and
docs catalogs; updates all affected listeners, tests, and the
core-data-structures docs (en + zh).
Rewrites the service API table, authority-versus-provenance contract, residency
routing, and deferred-work list; scopes the in-process driver README to one-shot
runs; and restates both model-facing tools' outputs, which no longer carry a
task id.
Implement the continuable background subagents RFC: a durable child
session with a series of Task-backed activations, each disposing its
run before the Task settles.
- dsh-subagent: rename SubagentRun.sendMessage to strict steer, drop
run-level resume, add SubagentProvider.resume dispatch via
SubagentService.resume, the continuation start field, and the
versioned model-hidden subagent/descriptor session event.
- dsh-subagent-inprocess/-spawn/-fork: publish the control-allocated
child id, append the descriptor inside the initial turn, implement
cold resume from the child's own transcript under the live parent
scope, and strict running-only steer.
- dsh-subagent-control (new): SubagentControlService owning stable
child ids, descriptor snapshot/fold/authorization, Task-backed
activation with settle-then-dispose ordering, the process-local
active-run association, and steer-or-resume sendMessage routing.
- dsh-tool-subagent: background route branches on the provider's
resume capability (continuable via the control service; one-shot
task for ACP), returning both child and task ids.
- dsh-tool-subagent-control (new): the globally named send_message
tool rendering steered/started routes.
Keyless coverage spans Task ownership and disposal ordering, running
delivery, cold follow-up, descriptor rejection and rollback, known-id
reconstruction, kill during lookup, admission races, and a new
subagent-continuable ACP snapshot scenario.
The native adapter's route was named deepseek, colliding with pi-ai's
catalog provider of the same name, so the two DeepSeek paths could never
be mounted side by side. The web settings page needs both configurable at
once. Compositions, fixtures, goldens, scaffolding defaults, and docs all
move together (pre-release, no shim); TUI/session-query-spill/
missing-credential goldens re-recorded through their keyless refresh
modes because provider-name length shifts box padding and spill
truncation points.
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).
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).
Replace send/steer/inject with one Agent.send primitive over the
(target × wakeup) matrix; followup/steer/inject become fixed-preset
alias methods on the now-abstract Agent class. Coalesce context/message
into user/message (injected context is a non-user source). Replace
agent/queued with agent/inbox/enqueue/dequeue/discard, add cancel
keepInbox, and add a FIFO-conservation invariant.
Drain idle injection flushes before agent teardown, snapshot approval and subagent provider inputs, and gate subagent lifecycle events on real child readiness. Align the RFCs and generated contracts with the hardened behavior.
Every subject-extractor row of the invariants carrier table is exercised
with a matching and a foreign-keyed carrier; the HMR re-apply seed path
(sessions of agents that predate the plugin are marked started) is pinned;
the scoped tool-provider disposal, plural restrict() validation, singular
scopeHost absentee, tool-subagent passthrough, stale-stage drop, and
disposing-parent spawn (INACTIVE_EFFECT, no orphan) each gain their test.
Two genuinely defensive branches carry justified v8-ignore markers.
SubagentStartRequest gains persona (capability-gated like toolFilter); the
in-process driver composes the child's scoped world in the factory's setup
window — persona as a scoped shadowing deployment:persona section,
toolFilter as a scoped tools.restrict() (loud unknown-name validation),
outputSchema as the scoped structured runtime. spawn/fork now advertise
every start-time capability; ACP stays all-false. A parent-scope teardown
effect links each child to its parent through the memoized handle, so a
disposed parent reaches its whole subtree even if the delegating tool's
finally never runs; subagent/start|end dispatch in the delegating parent's
scope.
structured.ts loses the placeholder schema, the final-assembly swap/strip,
the refcounted root runtime, and the WeakMap state: each child registers
its OWN capture tool (real schema), instruction section, and enforcement
listeners on child.ctx, riding the child's fiber. The commit listener is
call-keyed (a stale stage from a short-circuited post-execute chain is
dropped, never promoted on a later call), and one scoped prepend re-assert
listener preserves the final-assembly guarantee against a stripping global
listener.
tool-subagent gains persona/toolFilter/maxDepth passthrough config —
deny-listing the delegation tool (or maxDepth) is how a deployment bounds
recursion; the omitted-toolFilter schema key is forced absent (a
materialized {} would mean an empty allow-list, i.e. deny-everything).
The Codex simplification concern plus the duplication comment on the spawn
apply, resolved by deletion: the backend-lifetime holds are gone, so the
runtime registers at the first structured run and disposes when the last
settles — a deployment that never passes outputSchema carries no always-on
global state, and there is no per-backend acquisition block left to extract.
The driver spec now drives an INLINE spawn-shaped provider over
startInProcessRun, which removes the spawn/fork devDependencies (the
test-only workspace cycle); plugin-level structured coverage moves to the
backends' own specs (capture through the shipped plugin, mid-run backend
unload, seeded fork capture). tools.md, the driver README, and both backend
READMEs describe the run-scoped lifetime; the module-graph regenerates
without the cycle edges.