Merge remote-tracking branch 'origin/master' into worktree/provider-routed-llm-adapters
# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.md # docs/event-producer-consumer.md # docs/persistence-catalog.md # examples/acp-agent/tests/snapshots/advanced-toolchain/session.1.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/session.2.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/session.jsonl # examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl # examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl # examples/acp-agent/tests/snapshots/skill-load/session.jsonl # examples/acp-agent/tests/snapshots/text-turn/session.jsonl # examples/sandbox-acp-agent/cordis.yml # examples/sandbox-acp-agent/tests/snapshots/escalation-approved/session.jsonl # examples/sandbox-acp-agent/tests/snapshots/escalation-rejected/session.jsonl # examples/sandbox-acp-agent/tests/snapshots/mode-switching/session.jsonl # packages/compact/compact-basic/README.md # packages/compact/compact-basic/src/index.ts # packages/compact/compact-basic/tests/compact-basic.spec.ts # packages/core/agent-loop/README.md # packages/core/agent-loop/src/loop.ts # packages/core/agent-loop/tests/properties.spec.ts # packages/core/session/README.md # packages/core/session/src/types.ts # packages/core/session/tests/derived-cache.spec.ts # packages/llm/llm-deepseek/src/index.ts # packages/llm/llm-pi-ai/README.md # packages/llm/llm-pi-ai/src/adapter.ts # packages/llm/llm-pi-ai/src/convert.ts # packages/llm/llm-pi-ai/tests/adapter.spec.ts # packages/llm/llm/README.md # packages/llm/llm/src/call-config.ts # packages/llm/llm/src/index.ts # packages/ui/acp-agent/src/index.ts # packages/ui/acp/tests/harness.ts # packages/ui/jsonrpc/README.md # packages/ui/jsonrpc/src/server.ts # packages/ui/stdio-agent/README.md # packages/ui/stdio-agent/src/index.ts # python/sdk/README.i18n.yaml
This commit is contained in:
@@ -17,3 +17,22 @@ Spawn advertises `{ outputSchema: true, depthLimit: true, toolFilter: true, pers
|
||||
| Key | Meaning |
|
||||
|---|---|
|
||||
| `providerName` | Registry name on `ctx.subagents` (default `spawn`). |
|
||||
|
||||
## Model Experience
|
||||
|
||||
### Child-agent request
|
||||
|
||||
**What the model sees**: The fresh child receives the standalone task content verbatim, inherits the parent model and workspace by default, and sees the global prompt with any configured child-scoped persona shadow. A tool filter removes global wire schemas, executable lookup, and Code Mode SDK bindings for that child but leaves independently registered guidance. It receives zero parent conversation messages; the filter is visibility/composition, not an authority grant inherited from the parent.
|
||||
|
||||
**Token effect**: The child pays for a new independent context and history; no parent-history tokens are duplicated. Persona changes this child's repeated prompt cost, while filtering changes its schema or generated SDK cost.
|
||||
|
||||
### Parent tool result, indirectly
|
||||
|
||||
**What the model sees**: Through `dsh-tool-subagent`, the parent receives only the child's final output or stop-reason error.
|
||||
|
||||
**Token effect**: Parent input grows by one data-dependent result retained until compaction.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Runs expose no `sendMessage`/`resume`** — the optional runtime capabilities are absent on in-process runs.
|
||||
- **Fresh means no parent transcript** — the child inherits cwd, lineage, model, and explicitly configured persona/tool restrictions, but none of the parent's conversation; use the fork provider when completed-turn context is required.
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
/**
|
||||
* The in-process SPAWN subagent backend: registers a {@link SubagentProvider}
|
||||
* on `ctx.subagents` that runs each child as a FRESH child {@link Agent} on the
|
||||
* same cordis context (its own session, own system prompt, zero parent
|
||||
* context). The cheapest transport, reusing the agent factory's quiescent
|
||||
* teardown.
|
||||
*
|
||||
* The run mechanics live in `@deepseek-ai/dsh-subagent-inprocess`
|
||||
* ({@link startInProcessRun}); this backend just passes NO seed (a fresh
|
||||
* child). The fork backend is an independent peer over the same driver.
|
||||
*
|
||||
* Structured output (`outputSchema`) is supported through the driver's
|
||||
* per-child scoped runtime: the child registers its real-schema capture tool,
|
||||
* prompt instruction, and enforcement listeners inside the creation setup
|
||||
* window, and its scope owns their lifetime.
|
||||
*
|
||||
* Plugin export shape: named `name`/`inject`/`Config`/`apply`, NO default.
|
||||
*
|
||||
* The in-process SPAWN subagent backend: registers a {@link SubagentProvider} on
|
||||
* `ctx.subagents` that runs each child as a fresh child {@link Agent} on the same cordis
|
||||
* context (its own session, own system prompt, zero parent context). The cheapest transport,
|
||||
* reusing the agent factory's quiescent teardown.
|
||||
* @module @deepseek-ai/dsh-subagent-spawn
|
||||
*/
|
||||
|
||||
@@ -25,10 +12,8 @@ import type { SubagentCapabilities, SubagentProvider, SubagentStartRequest } fro
|
||||
import { startInProcessRun } from '@deepseek-ai/dsh-subagent-inprocess'
|
||||
|
||||
export const name = 'subagent-spawn'
|
||||
// `tools` is deliberately NOT injected: the shared driver registers structured
|
||||
// output through the child's creation context, whose factory already requires
|
||||
// the tool service. Keeping it out of this backend's inject list preserves the
|
||||
// provider's independent apply timing.
|
||||
// `tools` is deliberately not injected: the child factory already provides it during setup,
|
||||
// and adding it here would unnecessarily change this provider's apply timing.
|
||||
export const inject = ['subagents']
|
||||
|
||||
/** Config: the registry name to register the provider under. */
|
||||
|
||||
@@ -152,11 +152,8 @@ describe('dsh-subagent-spawn', () => {
|
||||
})
|
||||
|
||||
it('rejects without publishing when the request signal is already aborted', async () => {
|
||||
// Regression: a signal aborted BEFORE the run starts never fires an `abort`
|
||||
// event, so the listener can't catch it. The driver must check the
|
||||
// already-aborted case up front and settle `aborted` without running the
|
||||
// child — otherwise an already-cancelled request runs to `completed`. The
|
||||
// empty script proves the child's model is never called.
|
||||
// An already-aborted signal emits no future event, so start must check it before listening and
|
||||
// settle aborted without running the child. The empty model script proves no turn occurs.
|
||||
const controller = new AbortController()
|
||||
controller.abort()
|
||||
const { ctx, parent } = await setup([])
|
||||
@@ -165,10 +162,8 @@ describe('dsh-subagent-spawn', () => {
|
||||
})
|
||||
|
||||
it('same-tick cancellation rejects start and prevents child publication', async () => {
|
||||
// Regression: cancellation before publication used to set a flag but let the
|
||||
// async factory publish a child anyway, so `started` fulfilled and lifecycle
|
||||
// observers saw an agent for an attempt the caller had already cancelled.
|
||||
// The empty script also proves no model turn can run.
|
||||
// Same-tick cancellation must win before async factory publication: no child may become
|
||||
// visible, `started` must not fulfill, and the empty script proves no model turn occurs.
|
||||
const { ctx, parent } = await setup([])
|
||||
const beforeAgents = ctx.agents.list().length
|
||||
const beforeSessions = ctx.sessions.list().length
|
||||
|
||||
Reference in New Issue
Block a user