rename: @deepseek-ai/dsh-subagent-process -> @deepseek-ai/dsh-subagent-subprocess

The extracted library's name sat one edit away from @deepseek-ai/dsh-subagent-inprocess
(process/inprocess), inviting a typo'd import to silently resolve to the wrong
package. subagent-subprocess also reads as the deliberate counterpart to
subagent-inprocess (in-process vs. subprocess), matching how the two shared
drivers actually differ.

Package directory, npm name, module doc, JSDoc module tags, test-file name and
its temp-dir prefixes, the subagent-acp import and its Config/tsconfig/package.json
references, root tsconfig.json/tsconfig.build.json/knip.json entries, and the
packages/subagent group README all renamed together; regenerated
docs/module-graph.md and docs/config-catalog.md. Pure rename — no behavior,
export, or Config shape changed.
This commit is contained in:
pku-xht
2026-07-09 13:42:03 +08:00
parent 6660d2a83e
commit c321819053
17 changed files with 36 additions and 36 deletions

View File

@@ -8,10 +8,10 @@ The subagent seam: an agent delegating work to a child agent. Like the [bash](..
| `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`) |
| `subagent-process/` | Shared out-of-process machinery: env scrub, dispose ladder, isolated config dirs (pure lib; registers nothing) | — |
| `subagent-subprocess/` | Shared out-of-process machinery: env scrub, dispose ladder, isolated config dirs (pure lib; registers nothing) | — |
| `subagent-acp/` | Out-of-process backend: a child agent in a spawned subprocess, driven over ACP | (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 share the `subagent-inprocess` driver (a pure library — both depend on it, neither on the other), the out-of-process `subagent-acp` backend builds on the `subagent-process` library (the credential env scrub, the dispose ladder, isolated config dirs) and ships alongside them here; the test-only `dsh-subagent-mock` (in [support](../support/README.md)) is 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), the out-of-process `subagent-acp` backend builds on the `subagent-subprocess` library (the credential env scrub, the dispose ladder, isolated config dirs) and ships alongside them here; the test-only `dsh-subagent-mock` (in [support](../support/README.md)) is separate. All **product** packages except the mock.
The proposal and design rationale: [docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md](../../docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md).

View File

@@ -57,7 +57,7 @@ A spawn/transport/RPC failure resolves `error` (or `aborted` if a cancel was req
## Environment scrub
The child env is built by [`buildChildEnv` from `@deepseek-ai/dsh-subagent-process`](../subagent-process/README.md) — the ambient env minus credential-shaped vars, with `config.env` layered on top after the scrub; the pattern and full semantics live there. For this backend that means the parent harness's own secrets never leak into the spawned agent implicitly, while the child's OWN `DEEPSEEK_API_KEY` is supplied deliberately via `config.env` and survives.
The child env is built by [`buildChildEnv` from `@deepseek-ai/dsh-subagent-subprocess`](../subagent-subprocess/README.md) — the ambient env minus credential-shaped vars, with `config.env` layered on top after the scrub; the pattern and full semantics live there. For this backend that means the parent harness's own secrets never leak into the spawned agent implicitly, while the child's OWN `DEEPSEEK_API_KEY` is supplied deliberately via `config.env` and survives.
## Testing

View File

@@ -25,7 +25,7 @@
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-llm": "^0.0.1",
"@deepseek-ai/dsh-subagent": "^0.0.1",
"@deepseek-ai/dsh-subagent-process": "^0.0.1",
"@deepseek-ai/dsh-subagent-subprocess": "^0.0.1",
"cordis": "^4.0.0-rc.6"
},
"dependencies": {
@@ -36,7 +36,7 @@
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-subagent-process": "workspace:^",
"@deepseek-ai/dsh-subagent-subprocess": "workspace:^",
"@cordisjs/plugin-loader": "^1.0.0-rc.4",
"cordis": "^4.0.0-rc.6"
}

View File

@@ -40,7 +40,7 @@ import {
import { AgentId } from '@deepseek-ai/dsh-agent'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { SubagentResult, SubagentRun, SubagentStartRequest, SubagentStopReason } from '@deepseek-ai/dsh-subagent'
import { buildChildEnv, disposeChildProcess, spawnFailure } from '@deepseek-ai/dsh-subagent-process'
import { buildChildEnv, disposeChildProcess, spawnFailure } from '@deepseek-ai/dsh-subagent-subprocess'
/**
* How the client answers a child's `session/request_permission`. The first cut

View File

@@ -6,7 +6,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import SubagentService from '@deepseek-ai/dsh-subagent'
import { buildChildEnv, SENSITIVE_ENV_PATTERN } from '@deepseek-ai/dsh-subagent-process'
import { buildChildEnv, SENSITIVE_ENV_PATTERN } from '@deepseek-ai/dsh-subagent-subprocess'
import type { Agent } from '@deepseek-ai/dsh-agent'
import * as acp from '../src/index.ts'
import { acpStopReason, acpContentText, DEFAULT_DISPOSE_EOF_GRACE_MS, DEFAULT_DISPOSE_GRACE_MS, startAcpRun, toAcpPrompt, type AcpRunSpec } from '../src/run.ts'

View File

@@ -27,7 +27,7 @@
"path": "../subagent"
},
{
"path": "../subagent-process"
"path": "../subagent-subprocess"
}
]
}

View File

@@ -1,4 +1,4 @@
# @deepseek-ai/dsh-subagent-process
# @deepseek-ai/dsh-subagent-subprocess
Shared machinery for **out-of-process subagent backends** — providers that spawn an external agent as a child process, such as the [ACP backend](../subagent-acp/README.md). A pure library (no provider, no registration, no Config): what every spawn-a-CLI-child backend needs to keep the parent deployment's credentials out of the child, tear the child down to quiescence, and isolate it from the host user's on-disk CLI state. Design rationale: [the Claude Code / Codex subagent backends RFC](../../../docs/rfc/proposed/feature/2026-07-07-claude-code-and-codex-subagent-backends.md).
@@ -37,4 +37,4 @@ A per-run isolated config directory for an external CLI child (the target of `CL
## Testing
`tests/subagent-process.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.
`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.

View File

@@ -1,5 +1,5 @@
{
"name": "@deepseek-ai/dsh-subagent-process",
"name": "@deepseek-ai/dsh-subagent-subprocess",
"description": "Shared out-of-process subagent machinery: credential env scrub, spawn-failure capture, child-exit waits, the EOF-to-SIGTERM-to-SIGKILL dispose ladder, and isolated config dirs (pure lib; registers nothing)",
"version": "0.0.1",
"private": true,

View File

@@ -15,7 +15,7 @@
* periods, a pinned config dir is a PARAMETER here: defaults belong in each
* consuming plugin's Config, per the no-hardcoded-tunables rule.
*
* @module @deepseek-ai/dsh-subagent-process
* @module @deepseek-ai/dsh-subagent-subprocess
*/
import type { ChildProcess } from 'node:child_process'

View File

@@ -256,9 +256,9 @@ describe('disposeChildProcess', () => {
describe('createIsolatedConfigDir', () => {
it('creates a fresh private mkdtemp dir under the OS temp root', async () => {
const dir = await createIsolatedConfigDir('dsh-subagent-process-test-')
const dir = await createIsolatedConfigDir('dsh-subagent-subprocess-test-')
try {
expect(dir.path.startsWith(join(tmpdir(), 'dsh-subagent-process-test-'))).toBe(true)
expect(dir.path.startsWith(join(tmpdir(), 'dsh-subagent-subprocess-test-'))).toBe(true)
const st = await stat(dir.path)
expect(st.isDirectory()).toBe(true)
// Private (0700) per the defensive-patterns temp-dir rule.
@@ -269,8 +269,8 @@ describe('createIsolatedConfigDir', () => {
})
it('creates a distinct dir per call (per-run isolation)', async () => {
const a = await createIsolatedConfigDir('dsh-subagent-process-test-')
const b = await createIsolatedConfigDir('dsh-subagent-process-test-')
const a = await createIsolatedConfigDir('dsh-subagent-subprocess-test-')
const b = await createIsolatedConfigDir('dsh-subagent-subprocess-test-')
try {
expect(a.path).not.toBe(b.path)
} finally {
@@ -280,7 +280,7 @@ describe('createIsolatedConfigDir', () => {
})
it('remove() deletes a fresh dir recursively and is idempotent', async () => {
const dir = await createIsolatedConfigDir('dsh-subagent-process-test-')
const dir = await createIsolatedConfigDir('dsh-subagent-subprocess-test-')
await writeFile(join(dir.path, 'settings.json'), '{}')
await dir.remove()
expect(existsSync(dir.path)).toBe(false)
@@ -289,7 +289,7 @@ describe('createIsolatedConfigDir', () => {
})
it('returns a pinned dir verbatim and NEVER removes it', async () => {
const pinned = await mkdtemp(join(tmpdir(), 'dsh-subagent-process-pinned-'))
const pinned = await mkdtemp(join(tmpdir(), 'dsh-subagent-subprocess-pinned-'))
try {
const dir = await createIsolatedConfigDir('ignored-prefix-', pinned)
expect(dir.path).toBe(pinned)
@@ -302,7 +302,7 @@ describe('createIsolatedConfigDir', () => {
})
it('does not create a missing pinned path (the deployment owns its lifecycle)', async () => {
const missing = join(tmpdir(), `dsh-subagent-process-missing-${process.pid}`)
const missing = join(tmpdir(), `dsh-subagent-subprocess-missing-${process.pid}`)
const dir = await createIsolatedConfigDir('ignored-prefix-', missing)
expect(dir.path).toBe(missing)
expect(existsSync(missing)).toBe(false)
@@ -311,7 +311,7 @@ describe('createIsolatedConfigDir', () => {
})
it('remove() is best-effort: an rm rejection resolves instead of rejecting', async () => {
const dir = await createIsolatedConfigDir('dsh-subagent-process-locked-')
const dir = await createIsolatedConfigDir('dsh-subagent-subprocess-locked-')
try {
// The swallow contract is error-kind agnostic; EACCES stands in for the
// family (EBUSY, a vanished mount, …) that best-effort must absorb.