Merge remote-tracking branch 'origin/master' into codex/trim-ai-prose

# Conflicts:
#	docs/AGENTS.md
#	docs/config-catalog.md
#	packages/bash/bash-sandbox/src/index.ts
#	packages/bash/bash/src/session-mode.ts
#	packages/bash/tool-bash/README.md
#	packages/code-runtime/code-runtime-worker/README.md
#	packages/compact/compact/src/index.ts
#	packages/core/agent-core/README.md
#	packages/hooks/hooks-claude/src/config.ts
#	packages/hooks/hooks-claude/src/index.ts
#	packages/hooks/hooks-codex/src/config.ts
#	packages/hooks/hooks-codex/src/index.ts
#	packages/llm/llm/README.md
#	packages/session-persistence/session-persistence-jsonl/README.md
#	packages/session-persistence/session-persistence/README.md
#	packages/skill/skill-local/README.md
#	packages/support/acp-snapshot/README.md
#	packages/support/invariants/src/index.ts
#	packages/ui/acp/README.md
#	packages/ui/jsonrpc-agent/README.md
#	packages/ui/jsonrpc/README.md
#	packages/ui/permission/README.md
#	packages/ui/user-approval/README.md
#	packages/ui/user-interaction/README.md
#	packages/web/web-search-deepseek/README.md
This commit is contained in:
Tianyi Cui
2026-07-14 14:37:16 +08:00
105 changed files with 2343 additions and 339 deletions

View File

@@ -56,3 +56,25 @@ The child environment is built by [`buildChildEnv`](../subagent-subprocess/READM
The package has no default export. Cordis loader unwrapping would otherwise hide the named `inject` metadata; see [postmortem 0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md).
Keyless tests drive a scripted ACP subprocess over real stdio. The with-key e2e drives the repository's real ACP agent and self-skips without `DEEPSEEK_API_KEY`.
## Model Experience
### Child-agent request
**What the model sees**: The remote child receives the standalone task content through ACP plus its own process's configured system prompt, tools, and fresh session. It receives no parent conversation. This provider advertises no optional start-time capabilities, so the local service rejects requests for persona, tool filtering, depth enforcement, or structured output instead of silently omitting them.
**Token effect**: The child pays for an independent full context and its own multi-step history. These tokens never enter the parent's context.
### Parent tool result, indirectly
**What the model sees**: Through `dsh-tool-subagent`, the parent receives only the child's final streamed assistant text or that consumer's exact stop-reason error, not intermediate messages or tool traffic. A request already cancelled before publication becomes exactly `Error: subagent request was aborted before the ACP child started`; other start failures pass through as `Error: <message>`.
**Token effect**: Parent input grows only by the final result or error, which is data-dependent and retained until compaction. This provider adds no parent schema itself.
## Known Limitations and Deferred Work
- **A fresh process per run** — persistent-process pooling is a future optimization ([the seam RFC](../../../docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md)).
- **No optional start-time capabilities** — this provider cannot apply the local harness's `outputSchema`, depth cap, tool filter, or persona inside the remote process, so it advertises none and the service rejects requests that require them.
- **Only `agent_message_chunk` text is collected** — the child's tool-call activity, thought chunks, and plan updates are not surfaced to the parent.
- **Permission prompts are auto-answered** (`permission: allow | reject`) — no human is surfaced a child's `session/request_permission` in this cut.
- **No snapshot-tier replay coverage** (`TODO(acp-subagent-replay)`) — an ACP child is its own process with its own replay shape, deferred.

View File

@@ -21,3 +21,23 @@ Fork advertises `{ outputSchema: true, depthLimit: true, toolFilter: true, perso
| Key | Meaning |
|---|---|
| `providerName` | Registry name on `ctx.subagents` (default `fork`). |
See [`dsh-subagent-spawn`](../subagent-spawn/README.md) for the run lifecycle, model inheritance, and depth tracking — all shared.
## Model Experience
### Child-agent history and envelope
**What the model sees**: The child receives the parent's balanced completed-turn surface prefix, then the new task content verbatim. A configured persona shadows prompt text in the child's fresh scope; a tool restriction filters its global wire schemas, executable lookup, and Code Mode SDK bindings but not standalone guidance. The parent's tool view and authority are not inherited. An optional structured-output request adds its child-only contract. The parent's current in-flight turn is excluded.
**Token effect**: Forking duplicates retained completed history into separate child requests; the child then accumulates its own tokens independently. Persona changes repeated prompt cost, filtering changes schema or generated SDK cost, and a first-turn fork has no inherited history.
### Parent tool result, indirectly
**What the model sees**: The parent receives only the child's own final output through `dsh-tool-subagent`, not the inherited prefix or intermediate work.
**Token effect**: Parent input grows by one data-dependent final result retained until compaction.
## Known Limitations and Deferred Work
- **Runs expose no `sendMessage`/`resume`** — the optional runtime capabilities are absent on in-process runs.
- **The seed is a one-time snapshot** — the child sees the parent's completed turns as of the fork and nothing the parent logs afterwards; there is no live context sharing.

View File

@@ -39,3 +39,40 @@ After fulfillment, the caller owns the run. Provider-plugin unload does not revo
- A monotonic tool guard blocks later calls after capture, and `agent/turn-stop` ends the turn after the structured result commits.
A clean turn that never commits the required structured value reports `error`; the driver does not re-prompt. All registrations ride the child fiber and disappear with it.
## Model Experience
### Child-agent request
**What the model sees**: The shared driver sends the task verbatim as the child's user message and, when requested, shadows the persona and restricts global tool schemas, lookup, execution, and Code Mode SDK bindings in the unpublished child's fresh scope; parent restrictions are not inherited, and standalone tool-guidance sections remain. Spawn supplies no history; fork supplies its balanced seed.
**Token effect**: Child input is isolated from the parent and grows through the child's own steps. A persona changes repeated prompt text; filtering changes schema or generated SDK cost but not independently registered guidance.
### Structured-output system prompt, schema, and results
**What the model sees**: A structured run adds the structured-output instruction below. It also adds a child-scoped `structured_output` definition with exact description `Report your final structured result. Call this exactly once, when your answer is complete; the arguments must match this tool's parameter schema exactly.` and the requested schema. This runtime-only definition is outside the generated shipped [tool package map](../../../docs/tool-catalog.md#tool-package-map). Success returns `Structured output recorded.`; a later call becomes ``Error: structured output already recorded: the run is complete, so `<tool>` is not executed``.
**Token effect**: Fixed instruction and capability tokens are paid only by that child. Result text enters the child history, while the captured value alone becomes the parent result.
#### Structured-output instruction
```markdown
When you have your final answer, you MUST report it by calling the `structured_output` tool with arguments matching its parameter schema exactly. Do not finish with a plain text answer: only the tool call counts as your result.
```
### Parent start error, indirectly
**What the model sees**: Through `dsh-tool-subagent`, invalid depth state becomes exactly `Error: agent subagentDepth must be a non-negative safe integer`, `Error: subagent child depth exceeds the safe-integer range`, or `Error: subagent depth <attempted> exceeds maxDepth <max>`. A pre-publication cancellation passes its abort reason through the registry's `Error: <message>` wrapper.
**Token effect**: Zero tokens on a successful start; only the failed parent tool call retains this text.
### Parent result, indirectly
**What the model sees**: The driver extracts only the child's own last assistant output or captured structured value; seeded parent messages and intermediate child work do not become the result.
**Token effect**: The parent receives one data-dependent result through the consumer; all other child tokens stay in the child session.
## Known Limitations and Deferred Work
- **Runs expose no `sendMessage`/`resume`** — the optional runtime capabilities are absent on in-process runs.
- **Structured capture accepts the `defineTool` schema subset only** — unsupported JSON Schema constructs fail before the child is created; a provider needing a broader schema vocabulary requires a different runtime.

View File

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

View File

@@ -38,3 +38,14 @@ A per-run isolated config directory for an external CLI child (the target of `CL
## Testing
`tests/subagent-subprocess.spec.ts`: the env scrub and config-dir helpers run against the real process env and real filesystem (the rm-failure path injects its rejection at the fs boundary — a real recursive-rm failure is not portably provokable, and root ignores permission bits); the exit waits and the dispose ladder run against a scriptable fake child, driving each escalation tier deterministically. The [ACP backend suite](../subagent-acp/README.md) exercises the same ladder against real subprocesses (EOF-cooperative, EOF-ignoring, and SIGTERM-trapping children) end to end.
## Model Experience
Indirectly, through process-based subagent backends, whose child composition is constrained by credential scrubbing and isolated config directories.
## Known Limitations and Deferred Work
- **The credential scrub is name-based** — only variables matching `KEY` / `SECRET` / `TOKEN` are removed; differently named secrets such as `PASSWORD` pass through unless the backend supplies a stricter environment.
- **Signals target the direct child only** — teardown relies on a cooperative CLI to reap its descendants before exit; a re-parented or independently detached grandchild can outlive the ladder.
- **Fresh config-dir cleanup is best-effort** — an `rm` failure leaves private state under the OS temp root rather than failing disposal.
- **Pinned config directories are wholly operator-owned** — the helper neither creates, validates, locks, nor removes them, so concurrent runs may share and race on that state.

View File

@@ -59,3 +59,12 @@ Provider additions and removals also emit `subagent/provider-added` and `subagen
## Collection model
The current model-facing tool collects synchronously: it awaits the child result and disposes the run before returning. Background collection and polling remain outside this seam. See the [capability-seam RFC](../../../docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md) and `src/types.ts` for the complete contracts.
## Model Experience
Indirectly, through `dsh-tool-subagent`, which retains only a provider's data-dependent final output or exact `Error: no subagent provider registered for "<name>"`, `Error: subagent provider "<name>" does not support the "<capability>" capability`, and `Error: <message>` start failures in the parent while child working tokens remain child-only.
## Known Limitations and Deferred Work
- **The current consumer collects synchronously** — the model-facing tool starts a run and awaits `result`; steering (`sendMessage`) is part of the seam but intentionally unused, and background/poll/spill semantics are deferred to a future long-running-runtime design.
- **The lifecycle events are observe-only** — a run-affecting `subagent/end` continuation or decision surface is deferred until a consumer needs one.

View File

@@ -26,3 +26,41 @@ A non-`completed` stop reason becomes an `isError` tool result; partial child ou
| `maxDepth` | Absolute delegation-depth cap; requires provider `depthLimit` capability. |
`toolFilter` changes the child's visible global tool layer; it is not a parent-derived authority ceiling. See the [agent-scope security non-goal](../../../docs/rfc/implemented/architecture/2026-07-08-agent-scope-contexts.md#security-and-authority-are-explicit-non-goals).
## Model Experience
### Standalone-provider schema
**What the model sees**: While a fresh-context provider exists, the configured tool uses the generated default [`subagent` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent); the catalog also records how `toolName` changes the visible name.
**Token effect**: Fixed schema cost per parent request while mounted. Removing the provider removes the whole schema.
### Inherited-context-provider schema
**What the model sees**: Relative to the generated default [`subagent` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent), a provider that seeds completed turns replaces only the tool and `prompt` parameter descriptions with the text below; the shape and `description` parameter stay unchanged.
**Token effect**: Fixed schema cost per parent request while mounted. Exposing multiple providers adds one independently named schema per load.
#### Inherited-context-provider tool description
```markdown
Delegate a task to a subagent that INHERITS this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn), returning only its final result. Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive only its final answer, not its intermediate steps.
```
#### Inherited-context-provider prompt description
```markdown
The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new.
```
### Tool-call history and result
**What the model sees**: The task description and full prompt remain in the parent assistant tool call. Success contains only the child's data-dependent final text. Other stop reasons become exactly `Error: subagent run was cancelled`, `Error: subagent run failed`, `Error: subagent run hit its token limit before finishing`, `Error: subagent declined the task`, or `Error: subagent run ended abnormally (<reason>)`; a call without an owning agent becomes `Error: subagent tool requires a calling agent (exec.agent was undefined)`. Intermediate child steps never enter the parent.
**Token effect**: Prompt and final output are data-dependent retained tokens. All child working context is paid in the child and omitted from the parent.
## Known Limitations and Deferred Work
- **Delegation blocks the parent turn** — synchronous collect only; background start and poll collection are deferred to the long-running-runtime redesign.
- **Duplicate `toolName` across waiting loads is detected late** (`TODO(subagent-dup-toolname)`) — two loads waiting on providers collide only when a provider arrives, and the throw rolls back the provider's fiber rather than the misconfigured tool's; config-time detection needs a cross-fiber registry of intended names.
- **Child policy is fixed per tool registration** — `model`, persona, tool filter, and depth cap come from this plugin load's config, not model-call arguments; exposing another policy requires another distinctly named tool.