Merge branch 'codex/simp-hide-concrete-agent-loop' into codex/simp-hide-subagent-internals

# Conflicts:
#	docs/rfc/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md
#	packages/subagent/subagent-subprocess/src/index.ts
#	packages/subagent/tool-subagent/src/index.ts
This commit is contained in:
Tianyi Cui
2026-07-14 19:02:27 +08:00
504 changed files with 3420 additions and 9172 deletions

View File

@@ -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. */

View File

@@ -153,11 +153,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([])
@@ -166,10 +163,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