Extract the shared in-process driver into dsh-subagent-inprocess (review feedback)
The shared run driver lived inside dsh-subagent-spawn, so the spawn package carried fork-aware seeding logic and dsh-subagent-fork depended backward on dsh-subagent-spawn — the two in-process backends were not independent. Move the driver (startInProcessRun, depthOf, SubagentDepthError, InProcessRunOptions) into a new pure-library package @deepseek-ai/dsh-subagent-inprocess that registers nothing. spawn and fork now both depend only on that driver and neither knows about the other; spawn no longer re-exports it and fork no longer imports from spawn. Also wire BOTH backends in examples/coding-agent/cordis.yml (config-only): load dsh-subagent-spawn + dsh-subagent-fork + two dsh-tool-subagent instances with distinct toolNames (subagent → spawn, subagent_fork → fork), demonstrating that exposing multiple transports needs no code change.
This commit is contained in:
+10
-7
@@ -60,13 +60,13 @@ graph TD
|
||||
agent-core --> system-prompt
|
||||
agent-core --> tool-bash
|
||||
agent-core --> tools
|
||||
subagent-inprocess --> agent
|
||||
subagent-inprocess --> llm
|
||||
subagent-inprocess --> session
|
||||
subagent-inprocess --> subagent
|
||||
subagent-mock --> agent
|
||||
subagent-mock --> llm
|
||||
subagent-mock --> subagent
|
||||
subagent-spawn --> agent
|
||||
subagent-spawn --> llm
|
||||
subagent-spawn --> session
|
||||
subagent-spawn --> subagent
|
||||
tool-subagent --> agent
|
||||
tool-subagent --> llm
|
||||
tool-subagent --> subagent
|
||||
@@ -82,7 +82,9 @@ graph TD
|
||||
subagent-fork --> agent
|
||||
subagent-fork --> session
|
||||
subagent-fork --> subagent
|
||||
subagent-fork --> subagent-spawn
|
||||
subagent-fork --> subagent-inprocess
|
||||
subagent-spawn --> subagent
|
||||
subagent-spawn --> subagent-inprocess
|
||||
```
|
||||
|
||||
| Package | Depends on |
|
||||
@@ -108,9 +110,10 @@ graph TD
|
||||
| `subagent` | `agent`, `llm`, `tools` |
|
||||
| `tool-bash` | `agent`, `bash`, `llm`, `tools` |
|
||||
| `agent-core` | `agent`, `agent-loop`, `invariants`, `llm`, `session`, `system-prompt`, `tool-bash`, `tools` |
|
||||
| `subagent-inprocess` | `agent`, `llm`, `session`, `subagent` |
|
||||
| `subagent-mock` | `agent`, `llm`, `subagent` |
|
||||
| `subagent-spawn` | `agent`, `llm`, `session`, `subagent` |
|
||||
| `tool-subagent` | `agent`, `llm`, `subagent`, `tools` |
|
||||
| `acp-agent` | `acp`, `agent-core`, `session-persistence-jsonl` |
|
||||
| `stdio-agent` | `agent`, `agent-core`, `session`, `session-persistence-jsonl`, `ui-stdio` |
|
||||
| `subagent-fork` | `agent`, `session`, `subagent`, `subagent-spawn` |
|
||||
| `subagent-fork` | `agent`, `session`, `subagent`, `subagent-inprocess` |
|
||||
| `subagent-spawn` | `subagent`, `subagent-inprocess` |
|
||||
@@ -57,17 +57,21 @@
|
||||
|
||||
Use the subagent tool to delegate a focused, self-contained subtask
|
||||
to a fresh child agent (it works in its own context and returns only
|
||||
its final result) — give it a complete, standalone instruction.
|
||||
its final result) — give it a complete, standalone instruction. Use
|
||||
subagent_fork instead when the subtask needs THIS conversation's
|
||||
context: the child inherits the log so far.
|
||||
|
||||
Check the [exit code: N] marker on every command; investigate
|
||||
failures before moving on. Verify your work by running the code or
|
||||
tests. Keep answers brief and factual.
|
||||
|
||||
# The subagent seam + an in-process spawn backend + the model-facing `subagent`
|
||||
# tool, as leaf entries after the app (which provides ctx.agents/ctx.tools). The
|
||||
# tool is bound to the `spawn` backend: a delegated task runs as a fresh child
|
||||
# agent on this same process. (fork is available too — load dsh-subagent-fork
|
||||
# and a second dsh-tool-subagent bound to it with a distinct toolName.)
|
||||
# The subagent seam + BOTH in-process backends + two model-facing tools, as leaf
|
||||
# entries after the app (which provides ctx.agents/ctx.tools). spawn (a fresh
|
||||
# child) and fork (a child seeded with the parent's completed-turn prefix) are
|
||||
# independent backends over the shared dsh-subagent-inprocess driver. Exposing
|
||||
# both transports is pure config: load each backend, then load dsh-tool-subagent
|
||||
# once per backend with a distinct toolName (the tool registry rejects a
|
||||
# duplicate name) — no code change.
|
||||
- id: subagent
|
||||
name: '@deepseek-ai/dsh-subagent'
|
||||
|
||||
@@ -76,7 +80,19 @@
|
||||
config:
|
||||
providerName: spawn
|
||||
|
||||
- id: subagent-fork
|
||||
name: '@deepseek-ai/dsh-subagent-fork'
|
||||
config:
|
||||
providerName: fork
|
||||
|
||||
- id: tool-subagent
|
||||
name: '@deepseek-ai/dsh-tool-subagent'
|
||||
config:
|
||||
provider: spawn
|
||||
toolName: subagent
|
||||
|
||||
- id: tool-subagent-fork
|
||||
name: '@deepseek-ai/dsh-tool-subagent'
|
||||
config:
|
||||
provider: fork
|
||||
toolName: subagent_fork
|
||||
@@ -5,10 +5,11 @@ The subagent seam: an agent delegating work to a child agent. Like the [bash](..
|
||||
| Package | Role | ctx key |
|
||||
|---|---|---|
|
||||
| `subagent/` | Abstract subagent seam: named-provider registry + vocabulary | `ctx.subagents` |
|
||||
| `subagent-spawn/` | In-process backend: a fresh child agent (+ the shared run driver) | (registers on `ctx.subagents`) |
|
||||
| `subagent-inprocess/` | Shared in-process run driver (pure lib; registers nothing) | — |
|
||||
| `subagent-spawn/` | In-process backend: a fresh child agent | (registers on `ctx.subagents`) |
|
||||
| `subagent-fork/` | In-process backend: a child seeded with the parent's completed-turn prefix | (registers on `ctx.subagents`) |
|
||||
| `tool-subagent/` | Model-facing `subagent` delegation tool over `ctx.subagents` | (registers on `ctx.tools`) |
|
||||
|
||||
The interface lives at `subagent/subagent/`. The in-process `subagent-spawn` / `subagent-fork` backends ship here; the out-of-process `dsh-subagent-acp` and the test-only `dsh-subagent-mock` (in [support](../support/README.md)) are separate. All **product** packages except the mock.
|
||||
The interface lives at `subagent/subagent/`. The in-process `subagent-spawn` / `subagent-fork` backends share the `subagent-inprocess` driver (a pure library — both depend on it, neither on the other) and ship here; the out-of-process `dsh-subagent-acp` and the test-only `dsh-subagent-mock` (in [support](../support/README.md)) are separate. All **product** packages except the mock.
|
||||
|
||||
The proposal and design rationale: [docs/rfc/proposed/feature/2026-06-21-subagent-capability-seam.md](../../docs/rfc/proposed/feature/2026-06-21-subagent-capability-seam.md).
|
||||
@@ -23,7 +23,7 @@
|
||||
"@deepseek-ai/dsh-agent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session": "^0.0.1",
|
||||
"@deepseek-ai/dsh-subagent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-subagent-spawn": "^0.0.1",
|
||||
"@deepseek-ai/dsh-subagent-inprocess": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.6"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -36,6 +36,7 @@
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent-inprocess": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent-spawn": "workspace:^",
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
* The in-process FORK subagent backend: registers a {@link SubagentProvider} on
|
||||
* `ctx.subagents` that runs each child as a child {@link Agent} SEEDED with a
|
||||
* prefix of the parent's session log — so the child inherits the parent's
|
||||
* conversation context instead of starting fresh. Shares the run driver with
|
||||
* `@deepseek-ai/dsh-subagent-spawn`; the only difference is the seed.
|
||||
* conversation context instead of starting fresh. The run mechanics live in
|
||||
* `@deepseek-ai/dsh-subagent-inprocess` ({@link startInProcessRun}); this
|
||||
* backend just computes the seed. The spawn backend is an independent peer over
|
||||
* the same driver.
|
||||
*
|
||||
* The seed boundary is the crux: at the moment a subagent tool's `execute`
|
||||
* runs, the parent's CURRENT turn is open and unbalanced (it holds the
|
||||
@@ -23,7 +25,7 @@ import z from 'schemastery'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import type { SubagentCapabilities, SubagentProvider, SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
|
||||
import { startInProcessRun } from '@deepseek-ai/dsh-subagent-spawn'
|
||||
import { startInProcessRun } from '@deepseek-ai/dsh-subagent-inprocess'
|
||||
|
||||
export const name = 'subagent-fork'
|
||||
export const inject = ['subagents', 'agents']
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"path": "../subagent"
|
||||
},
|
||||
{
|
||||
"path": "../subagent-spawn"
|
||||
"path": "../subagent-inprocess"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# @deepseek-ai/dsh-subagent-inprocess
|
||||
|
||||
The shared **in-process subagent run driver**. A pure library (no provider, no registration) that the in-process backends — [spawn](../subagent-spawn/README.md) (a fresh child) and [fork](../subagent-fork/README.md) (a child seeded with a prefix of the parent's log) — both build on. The backends are thin shells that differ ONLY in the session seed they pass; everything downstream lives here, so neither backend depends on the other.
|
||||
|
||||
## What it exports
|
||||
|
||||
### `startInProcessRun(ctx, request, options): SubagentRun`
|
||||
|
||||
Runs a child as a child [`Agent`](../../core/agent) on the same cordis context (`ctx.agents`):
|
||||
|
||||
1. computes child depth = `depthOf(parent) + 1`; if `request.maxDepth` is set and exceeded, throws `SubagentDepthError` (the `depthLimit` capability);
|
||||
2. creates a child via `ctx.agents.create` with a fresh `AgentId`/`SessionId`, the parent's `cwd` + `parentSession` lineage, the optional `options.seed` (fork's completed-turn prefix; omitted for a fresh child), and `agentOptions` (the child inherits the **parent's model** by default — a child with no model can't run — overridable via `request.agentOptions.model`; the system prompt is NOT inherited);
|
||||
3. drives the one-shot: `child.send(prompt)` then `await child.whenIdle()` (ordering matters — `send` enqueues synchronously, so `whenIdle` observes the queued work and resolves on the child's `running → idle` transition, never before the turn starts);
|
||||
4. reads the result, scoped to the child's OWN events (everything at or after `seedLength`, so a seeded child that produced no message of its own never returns the seeded parent's last message): the last `assistant/message` content (deep-cloned — the log is frozen) and the last `turn/end.reason` mapped to a `SubagentStopReason`.
|
||||
|
||||
`dispose()` delegates to `AgentHandle.dispose()` (stop loop → await quiescence → remove session); `cancel()` cancels the child's in-flight turn. A cancel landing before any `turn/end` (the pre-turn window) still settles `aborted`, honoring the cancel contract rather than the generic no-turn `error`.
|
||||
|
||||
### `InProcessRunOptions`
|
||||
|
||||
`{ providerName: string; seed?: SessionEvent[] }` — the per-backend inputs: the provider name (for error context) and the optional child-session seed.
|
||||
|
||||
### `depthOf(agent): number`
|
||||
|
||||
Delegation depth rides on a merge-extensible `AgentOptions.subagentDepth` field (0 for a top-level agent, parent + 1 for a child), so a nested spawn reads its parent's depth from `parent.options.subagentDepth`. `depthOf` reads it (absent ⇒ 0).
|
||||
|
||||
### `SubagentDepthError`
|
||||
|
||||
Thrown by `startInProcessRun` when a spawn would exceed the request's `maxDepth` cap; carries `attemptedDepth` and `maxDepth`.
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-subagent-inprocess",
|
||||
"description": "Shared in-process subagent run driver: drives a child agent on ctx.agents (used by the spawn and fork backends)",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-llm": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session": "^0.0.1",
|
||||
"@deepseek-ai/dsh-subagent": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent-loop": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"cordis": "^4.0.0-rc.6"
|
||||
}
|
||||
}
|
||||
+11
-10
@@ -1,15 +1,16 @@
|
||||
/**
|
||||
* The shared in-process subagent run driver. A subagent backend that runs the
|
||||
* child as a child {@link Agent} on the SAME cordis context (`ctx.agents`) —
|
||||
* the cheapest transport, reusing the agent factory's quiescent
|
||||
* {@link AgentHandle} teardown. Both in-process backends use this:
|
||||
* `@deepseek-ai/dsh-subagent-spawn` (a fresh child) and
|
||||
* `@deepseek-ai/dsh-subagent-fork` (a child seeded with a prefix of the
|
||||
* parent's log) differ ONLY in the `seed` they pass — everything downstream
|
||||
* (drive the child, read its final output, map the stop reason, dispose) is
|
||||
* identical and lives here.
|
||||
* The shared in-process subagent run driver: run a child as a child
|
||||
* {@link Agent} on the SAME cordis context (`ctx.agents`) — the cheapest
|
||||
* transport, reusing the agent factory's quiescent {@link AgentHandle}
|
||||
* teardown. The concrete in-process backends are thin shells over this driver,
|
||||
* differing ONLY in the `seed` they pass (a fresh child vs. a child seeded with
|
||||
* a prefix of the parent's log); everything downstream — drive the child, read
|
||||
* its final output, map the stop reason, dispose — is identical and lives here.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-subagent-spawn/in-process
|
||||
* This package owns no provider and registers nothing; it is a pure library the
|
||||
* backend packages depend on, so neither backend needs to know about the other.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-subagent-inprocess
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'node:crypto'
|
||||
@@ -0,0 +1,85 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import LlmService from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
||||
import AgentRegistry, { AgentId, type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import * as Invariants from '@deepseek-ai/dsh-invariants'
|
||||
import SubagentService from '@deepseek-ai/dsh-subagent'
|
||||
import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
|
||||
import { depthOf, SubagentDepthError, startInProcessRun } from '../src/index.ts'
|
||||
|
||||
type Script = ConstructorParameters<typeof MockAdapter>[0]
|
||||
|
||||
/**
|
||||
* Drives the shared in-process run driver DIRECTLY (no provider package), so the
|
||||
* driver's own contract — depth read/cap, the one-shot drive, the result read —
|
||||
* is covered independently of which backend (spawn/fork) calls it. The only
|
||||
* mocked boundary is the model; the real agent loop, SubagentService, and
|
||||
* dsh-invariants are mounted, so a malformed child session log fails the test.
|
||||
*/
|
||||
async function setup(script: Script) {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmService)
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(Invariants)
|
||||
await ctx.plugin(AgentLoop, { agents: [] })
|
||||
await ctx.plugin(SubagentService)
|
||||
ctx.llm.registerAdapter(['mock'], new MockAdapter(script))
|
||||
const parent = ctx.agentLoop.create(AgentId('parent'), { model: 'mock' })
|
||||
return { ctx, parent }
|
||||
}
|
||||
|
||||
function text(blocks: { type: string; text?: string }[]): string {
|
||||
return blocks.filter(b => b.type === 'text').map(b => b.text).join('')
|
||||
}
|
||||
|
||||
describe('depthOf', () => {
|
||||
it('reads 0 for an agent with no subagentDepth, the set value otherwise', async () => {
|
||||
const { parent } = await setup([])
|
||||
expect(depthOf(parent)).toBe(0)
|
||||
const withDepth = { options: { subagentDepth: 3 } } as unknown as Agent
|
||||
expect(depthOf(withDepth)).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('startInProcessRun', () => {
|
||||
it('drives a fresh child (no seed) to completion and returns its output', async () => {
|
||||
const { ctx, parent } = await setup([textResponse('driver child answer')])
|
||||
const run = startInProcessRun(ctx, { prompt: [{ type: 'text', text: 'do X' }], parent }, { providerName: 'spawn' })
|
||||
const result = await run.result
|
||||
expect(result.stopReason).toBe('completed')
|
||||
expect(text(result.output)).toBe('driver child answer')
|
||||
expect(depthOf(ctx.agents.get(run.id)!)).toBe(1)
|
||||
await run.dispose()
|
||||
})
|
||||
|
||||
it('throws SubagentDepthError when the child would exceed maxDepth', async () => {
|
||||
const { ctx, parent } = await setup([])
|
||||
expect(() => startInProcessRun(ctx, { prompt: [{ type: 'text', text: 'p' }], parent, maxDepth: 0 }, { providerName: 'spawn' }))
|
||||
.toThrow(SubagentDepthError)
|
||||
})
|
||||
|
||||
it('seeds the child session when a seed is supplied', async () => {
|
||||
// Drive the parent through one real turn, then seed the child with that
|
||||
// completed-turn prefix — the child must SEE the parent's history but its
|
||||
// result is scoped to its OWN events (not the seeded parent message).
|
||||
const { ctx, parent } = await setup([textResponse('parent turn'), textResponse('seeded child reply')])
|
||||
parent.send([{ type: 'text', text: 'parent q' }])
|
||||
await parent.whenIdle()
|
||||
const seed = parent.session.events.slice()
|
||||
const run = startInProcessRun(ctx, { prompt: [{ type: 'text', text: 'child q' }], parent }, { providerName: 'fork', seed })
|
||||
const result = await run.result
|
||||
expect(result.stopReason).toBe('completed')
|
||||
expect(text(result.output)).toBe('seeded child reply')
|
||||
const child = ctx.agents.get(run.id)!
|
||||
// The child inherited the parent's prefix.
|
||||
expect(child.session.events.slice(0, seed.length).some(e => e.type === 'user/message')).toBe(true)
|
||||
await run.dispose()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../vendor/cosmokit"
|
||||
},
|
||||
{
|
||||
"path": "../../../vendor/cordis"
|
||||
},
|
||||
{
|
||||
"path": "../../core/agent"
|
||||
},
|
||||
{
|
||||
"path": "../../llm/llm"
|
||||
},
|
||||
{
|
||||
"path": "../../core/session"
|
||||
},
|
||||
{
|
||||
"path": "../subagent"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,17 +2,11 @@
|
||||
|
||||
The in-process **spawn** subagent backend: a [`SubagentProvider`](../subagent/README.md) that runs each child as a **fresh** child [`Agent`](../../core/agent) on the same cordis context (`ctx.agents`) — its own session, its own (or the parent's) model, zero inherited conversation. The cheapest transport, reusing the agent factory's quiescent [`AgentHandle`](../../core/agent) teardown.
|
||||
|
||||
It also exports the **shared in-process run driver** (`startInProcessRun`) that the [fork](../subagent-fork/README.md) backend builds on — spawn and fork differ only in the session seed.
|
||||
The run mechanics live in the shared [`@deepseek-ai/dsh-subagent-inprocess`](../subagent-inprocess/README.md) driver (`startInProcessRun`); this backend just passes **no seed** (a fresh child). The [fork](../subagent-fork/README.md) backend is an independent peer over the same driver — neither knows about the other.
|
||||
|
||||
## What it does
|
||||
|
||||
`start(request)` →
|
||||
1. computes child depth = `depthOf(parent) + 1`; if `request.maxDepth` is set and exceeded, throws `SubagentDepthError` (the `depthLimit` capability);
|
||||
2. creates a child via `ctx.agents.create` with a fresh `AgentId`/`SessionId`, the parent's `cwd` + `parentSession` lineage, and `agentOptions` (the child inherits the **parent's model** by default — a child with no model can't run — overridable via `request.agentOptions.model`; the system prompt is NOT inherited);
|
||||
3. drives the one-shot: `child.send(prompt)` then `await child.whenIdle()` (ordering matters — `send` enqueues synchronously, so `whenIdle` observes the queued work and resolves on the child's `running → idle` transition, never before the turn starts);
|
||||
4. reads the result: the last `assistant/message` content (deep-cloned — the log is frozen) and the last `turn/end.reason` mapped to a `SubagentStopReason`.
|
||||
|
||||
`dispose()` delegates to `AgentHandle.dispose()` (stop loop → await quiescence → remove session); `cancel()` cancels the child's in-flight turn.
|
||||
`start(request)` delegates to `startInProcessRun(ctx, request, { providerName })` with no seed: a fresh child agent with the parent's `cwd`/`parentSession` lineage and (by default) the parent's model. See the [driver README](../subagent-inprocess/README.md) for the full lifecycle (depth check, one-shot drive, result read, dispose).
|
||||
|
||||
## Capabilities
|
||||
|
||||
@@ -23,7 +17,3 @@ It also exports the **shared in-process run driver** (`startInProcessRun`) that
|
||||
| Key | Meaning |
|
||||
|---|---|
|
||||
| `providerName` | Registry name on `ctx.subagents` (default `spawn`). |
|
||||
|
||||
## Depth tracking
|
||||
|
||||
Delegation depth rides on a merge-extensible `AgentOptions.subagentDepth` field (0 for a top-level agent, parent + 1 for a child), so a nested spawn reads its parent's depth from `parent.options.subagentDepth`. Read it with the exported `depthOf(agent)`.
|
||||
@@ -20,10 +20,8 @@
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-llm": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session": "^0.0.1",
|
||||
"@deepseek-ai/dsh-subagent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-subagent-inprocess": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.6"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -38,6 +36,7 @@
|
||||
"@deepseek-ai/dsh-llm-deepseek": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent-inprocess": "workspace:^",
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-bash": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-subagent": "workspace:^",
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
* context). The cheapest transport, reusing the agent factory's quiescent
|
||||
* teardown.
|
||||
*
|
||||
* The fork sibling (`@deepseek-ai/dsh-subagent-fork`) shares this package's run
|
||||
* driver ({@link startInProcessRun}) and differs ONLY in seeding the child with
|
||||
* a prefix of the parent's log.
|
||||
* 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.
|
||||
*
|
||||
* Plugin export shape: named `name`/`inject`/`Config`/`apply`, NO default.
|
||||
*
|
||||
@@ -17,10 +17,7 @@
|
||||
import type { Context } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import type { SubagentCapabilities, SubagentProvider, SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
|
||||
import { startInProcessRun } from './in-process.ts'
|
||||
|
||||
export { startInProcessRun, depthOf, SubagentDepthError } from './in-process.ts'
|
||||
export type { InProcessRunOptions } from './in-process.ts'
|
||||
import { startInProcessRun } from '@deepseek-ai/dsh-subagent-inprocess'
|
||||
|
||||
export const name = 'subagent-spawn'
|
||||
export const inject = ['subagents', 'agents']
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as Invariants from '@deepseek-ai/dsh-invariants'
|
||||
import SubagentService from '@deepseek-ai/dsh-subagent'
|
||||
import { MockAdapter, maxTokensResponse, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
|
||||
import * as spawn from '../src/index.ts'
|
||||
import { depthOf, SubagentDepthError } from '../src/in-process.ts'
|
||||
import { depthOf, SubagentDepthError } from '@deepseek-ai/dsh-subagent-inprocess'
|
||||
|
||||
type Script = ConstructorParameters<typeof MockAdapter>[0]
|
||||
|
||||
|
||||
@@ -17,17 +17,11 @@
|
||||
{
|
||||
"path": "../../../vendor/schemastery"
|
||||
},
|
||||
{
|
||||
"path": "../../core/agent"
|
||||
},
|
||||
{
|
||||
"path": "../../llm/llm"
|
||||
},
|
||||
{
|
||||
"path": "../../core/session"
|
||||
},
|
||||
{
|
||||
"path": "../subagent"
|
||||
},
|
||||
{
|
||||
"path": "../subagent-inprocess"
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+36
@@ -360,6 +360,9 @@ importers:
|
||||
'@deepseek-ai/dsh-subagent':
|
||||
specifier: workspace:^
|
||||
version: link:../subagent
|
||||
'@deepseek-ai/dsh-subagent-inprocess':
|
||||
specifier: workspace:^
|
||||
version: link:../subagent-inprocess
|
||||
'@deepseek-ai/dsh-subagent-spawn':
|
||||
specifier: workspace:^
|
||||
version: link:../subagent-spawn
|
||||
@@ -373,6 +376,36 @@ importers:
|
||||
specifier: ^4.0.0-rc.6
|
||||
version: 4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4)
|
||||
|
||||
packages/subagent/subagent-inprocess:
|
||||
devDependencies:
|
||||
'@deepseek-ai/dsh-agent':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/agent
|
||||
'@deepseek-ai/dsh-agent-loop':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/agent-loop
|
||||
'@deepseek-ai/dsh-invariants':
|
||||
specifier: workspace:^
|
||||
version: link:../../support/invariants
|
||||
'@deepseek-ai/dsh-llm':
|
||||
specifier: workspace:^
|
||||
version: link:../../llm/llm
|
||||
'@deepseek-ai/dsh-session':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/session
|
||||
'@deepseek-ai/dsh-subagent':
|
||||
specifier: workspace:^
|
||||
version: link:../subagent
|
||||
'@deepseek-ai/dsh-system-prompt':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/system-prompt
|
||||
'@deepseek-ai/dsh-tools':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/tools
|
||||
cordis:
|
||||
specifier: ^4.0.0-rc.6
|
||||
version: 4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4)
|
||||
|
||||
packages/subagent/subagent-spawn:
|
||||
dependencies:
|
||||
schemastery:
|
||||
@@ -406,6 +439,9 @@ importers:
|
||||
'@deepseek-ai/dsh-subagent':
|
||||
specifier: workspace:^
|
||||
version: link:../subagent
|
||||
'@deepseek-ai/dsh-subagent-inprocess':
|
||||
specifier: workspace:^
|
||||
version: link:../subagent-inprocess
|
||||
'@deepseek-ai/dsh-system-prompt':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/system-prompt
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
{ "path": "./packages/subagent/subagent" },
|
||||
{ "path": "./packages/support/subagent-mock" },
|
||||
{ "path": "./packages/subagent/tool-subagent" },
|
||||
{ "path": "./packages/subagent/subagent-inprocess" },
|
||||
{ "path": "./packages/subagent/subagent-spawn" },
|
||||
{ "path": "./packages/subagent/subagent-fork" }
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user