subprocess: one explicit env channel on the spawn spec
Drop SubprocessSpawnSpec.dshEnv and splitEnvChannels(); childEnv() is now scrubbed-base + explicit entries with no namespace validation. The invariant dropped is the reserved-namespace check on explicit entries (DSH_* rejected from env, non-DSH_* rejected from dshEnv). Explicit-entry trust already covers it: an explicit credential-shaped entry has always merged after the scrub as a deliberate caller opt-in, and an explicit DSH_* entry is the same deliberate act — the staleness invariant lives entirely in scrubbedParentEnv dropping AMBIENT credential-shaped and DSH_* names, which stays. The validation's only observed effect was rejecting legitimate explicit entries: both recent CI breakages (DSH_GATE_CONCURRENCY exported into every job crashing lsp specs, DSH_PERMISSION_MODE in acp config.env crashing the child spawn) were this check firing on values a caller meant to pass, each fixed by routing around the bureaucracy the seam itself imposed. The bash seam keeps its own request/spec dshEnv field: that is bash-owned trusted-plugin vocabulary (the ctx.bashEnv collected overlay) whose merge-last position guarantees a caller env entry cannot displace a managed fact; bash-local now flattens ENV_OVERRIDES -> spec.env -> spec.dshEnv into the seam's one env map. subagent-acp and lsp-local pass their single config env map straight through. DshEnvironment/DshEnvironmentKey/DSH_ENV_PREFIX stay on the subprocess seam as the namespace vocabulary (bash re-exports them; scrubbedParentEnv filters on the prefix). Tests: the two channel-rejection specs and the splitEnvChannels partition spec are deleted; one spawn spec now proves an explicit DSH_* env entry reaches the child while an ambient one is scrubbed; the acp/lsp forwarding specs keep their MOCK_ECHO_ENV / LSP_FAKE_ECHO_ENV assertions with the split comments rewritten to merge-after-scrub. Docs (en+zh, re-recorded) and the owning Agent Notes updated; cordis api/services catalogs regenerated.
This commit is contained in:
@@ -1582,7 +1582,7 @@ abstract spawn(spec: SubprocessSpawnSpec): SubprocessHandle
|
||||
|
||||
Types: [SubprocessHandle](../core-data-structures/subprocess.md) · [SubprocessSpawnSpec](../core-data-structures/subprocess.md)
|
||||
|
||||
Source: [`packages/subprocess/subprocess/src/index.ts:112`](../../packages/subprocess/subprocess/src/index.ts)
|
||||
Source: [`packages/subprocess/subprocess/src/index.ts:89`](../../packages/subprocess/subprocess/src/index.ts)
|
||||
|
||||
## `ctx.systemPrompt` — `SystemPrompt`
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
bash.md: 4ea0ace7e4af8cda3ff5bf1b4fc672f6425e5396
|
||||
bash.zh.md: 6642c85134748bceaa4a7783742a63da8b6acabd
|
||||
bash.md: 3747244662301a256e12037ea67c21017b5ac2c5
|
||||
bash.zh.md: 9927aa8d51ee410d70bed7a2d00e40061b499e15
|
||||
|
||||
@@ -46,17 +46,18 @@ interface BashExecRequest {
|
||||
stdin?: string | undefined
|
||||
/**
|
||||
* Ordinary environment entries for the command, merged after the credential
|
||||
* scrub. `DSH_*` is reserved for {@link dshEnv} and implementations reject it
|
||||
* here. Set by in-process plugins (the hooks bridges set
|
||||
* `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, …); the model-facing bash tool
|
||||
* does not expose it as a parameter.
|
||||
* scrub. Managed facts belong in {@link dshEnv}, which merges after this
|
||||
* map, so an entry here can never displace one. Set by in-process plugins
|
||||
* (the hooks bridges set `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, …); the
|
||||
* model-facing bash tool does not expose it as a parameter.
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/**
|
||||
* Harness-owned `DSH_*` variables for this execution. Executors discard
|
||||
* ambient `DSH_*` entries before merging this snapshot, so an unavailable
|
||||
* current fact cannot inherit a stale value from the harness process, and
|
||||
* reject non-`DSH_*` names supplied through this managed channel.
|
||||
* Harness-owned `DSH_*` variables for this execution (typed to managed
|
||||
* keys). Executors discard ambient `DSH_*` entries before merging this
|
||||
* snapshot last, so an unavailable current fact cannot inherit a stale
|
||||
* value from the harness process and a caller {@link env} entry cannot
|
||||
* displace a managed one.
|
||||
*/
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
/** Fully resolved per-call sandbox policy; sandboxing executors default it. */
|
||||
@@ -85,12 +86,12 @@ interface BashExecSpec {
|
||||
stdin?: string | undefined
|
||||
/**
|
||||
* Ordinary environment entries carried through from
|
||||
* {@link BashExecRequest.env}. `DSH_*` remains reserved for {@link dshEnv}.
|
||||
* {@link BashExecRequest.env}; {@link dshEnv} still merges after them.
|
||||
* OPTIONAL on the spec for the same reason as `stdin`: absent means no
|
||||
* ordinary extra environment.
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/** Managed `DSH_*` snapshot; implementations reject ordinary names. */
|
||||
/** Managed `DSH_*` snapshot (typed to managed keys); merges after {@link env}. */
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
/** Resolved sandbox policy; ignored by executors that do not confine. */
|
||||
sandboxPolicy: SandboxExecutionPolicy | undefined
|
||||
|
||||
@@ -46,17 +46,18 @@ interface BashExecRequest {
|
||||
stdin?: string | undefined
|
||||
/**
|
||||
* Ordinary environment entries for the command, merged after the credential
|
||||
* scrub. `DSH_*` is reserved for {@link dshEnv} and implementations reject it
|
||||
* here. Set by in-process plugins (the hooks bridges set
|
||||
* `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, …); the model-facing bash tool
|
||||
* does not expose it as a parameter.
|
||||
* scrub. Managed facts belong in {@link dshEnv}, which merges after this
|
||||
* map, so an entry here can never displace one. Set by in-process plugins
|
||||
* (the hooks bridges set `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, …); the
|
||||
* model-facing bash tool does not expose it as a parameter.
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/**
|
||||
* Harness-owned `DSH_*` variables for this execution. Executors discard
|
||||
* ambient `DSH_*` entries before merging this snapshot, so an unavailable
|
||||
* current fact cannot inherit a stale value from the harness process, and
|
||||
* reject non-`DSH_*` names supplied through this managed channel.
|
||||
* Harness-owned `DSH_*` variables for this execution (typed to managed
|
||||
* keys). Executors discard ambient `DSH_*` entries before merging this
|
||||
* snapshot last, so an unavailable current fact cannot inherit a stale
|
||||
* value from the harness process and a caller {@link env} entry cannot
|
||||
* displace a managed one.
|
||||
*/
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
/** Fully resolved per-call sandbox policy; sandboxing executors default it. */
|
||||
@@ -85,12 +86,12 @@ interface BashExecSpec {
|
||||
stdin?: string | undefined
|
||||
/**
|
||||
* Ordinary environment entries carried through from
|
||||
* {@link BashExecRequest.env}. `DSH_*` remains reserved for {@link dshEnv}.
|
||||
* {@link BashExecRequest.env}; {@link dshEnv} still merges after them.
|
||||
* OPTIONAL on the spec for the same reason as `stdin`: absent means no
|
||||
* ordinary extra environment.
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/** Managed `DSH_*` snapshot; implementations reject ordinary names. */
|
||||
/** Managed `DSH_*` snapshot (typed to managed keys); merges after {@link env}. */
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
/** Resolved sandbox policy; ignored by executors that do not confine. */
|
||||
sandboxPolicy: SandboxExecutionPolicy | undefined
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
subprocess.md: cdd4507c7d37f47ca243ddf38114f5aa6b6f3ad1
|
||||
subprocess.zh.md: 78325c3255c42ed591bbd98fdbdb4fdfcba48202
|
||||
subprocess.md: db4ab49ea953bfcf9ae59ede3187795eb71f0268
|
||||
subprocess.zh.md: 3f0a6c798056865ead1352f87328fb9dad6d5c0f
|
||||
|
||||
@@ -8,7 +8,7 @@ Source: [`packages/subprocess/subprocess/src/types.ts`](../../packages/subproces
|
||||
|
||||
## Managed environment namespace and captured output
|
||||
|
||||
`DSH_*` variables are Harness-owned child-process facts; implementations discard ambient `DSH_*` names before merging the caller's snapshot, and each collected stream reports its truncation and spill-recovery state through `CollectedOutput`.
|
||||
`DSH_*` variables are Harness-owned child-process facts; implementations discard ambient `DSH_*` names before the caller's explicit `env` merges, so a current fact arrives only as a deliberate entry, and each collected stream reports its truncation and spill-recovery state through `CollectedOutput`.
|
||||
|
||||
```ts type-equiv
|
||||
/** One environment key inside the managed {@link DSH_ENV_PREFIX} namespace. */
|
||||
@@ -114,19 +114,14 @@ interface SubprocessSpawnSpec {
|
||||
*/
|
||||
signal?: AbortSignal | undefined
|
||||
/**
|
||||
* Ordinary environment entries merged onto the implementation's scrubbed
|
||||
* parent base (see `scrubbedParentEnv`). `DSH_*` names are rejected and
|
||||
* belong in {@link dshEnv}; a deliberately forwarded credential-shaped
|
||||
* entry survives because this layer merges after the scrub.
|
||||
* Explicit environment entries merged onto the implementation's scrubbed
|
||||
* parent base (see `scrubbedParentEnv`), with no namespace validation:
|
||||
* every entry is a deliberate caller opt-in, so a forwarded
|
||||
* credential-shaped entry or a current `DSH_*` fact survives precisely
|
||||
* because this layer merges after the scrub that drops its ambient
|
||||
* namesake.
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/**
|
||||
* Harness-owned `DSH_*` variables for this execution. The scrubbed base has
|
||||
* already discarded ambient `DSH_*` entries, so an unavailable current fact
|
||||
* cannot inherit a stale value from the harness process; non-`DSH_*` names
|
||||
* on this channel are rejected.
|
||||
*/
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
## 受管环境命名空间与捕获的输出
|
||||
|
||||
`DSH_*` 变量是归 Harness 所有的子进程事实;实现会在合并调用方快照之前丢弃环境中已有的 `DSH_*` 名称,每条被收集的流都通过 `CollectedOutput` 报告自身的截断与 spill 恢复状态。
|
||||
`DSH_*` 变量是归 Harness 所有的子进程事实;实现会在合并调用方显式 `env` 之前丢弃环境中已有的 `DSH_*` 名称,因此当前事实只会以有意提供的条目形式到达,每条被收集的流都通过 `CollectedOutput` 报告自身的截断与 spill 恢复状态。
|
||||
|
||||
```ts type-equiv
|
||||
/** One environment key inside the managed {@link DSH_ENV_PREFIX} namespace. */
|
||||
@@ -114,19 +114,14 @@ interface SubprocessSpawnSpec {
|
||||
*/
|
||||
signal?: AbortSignal | undefined
|
||||
/**
|
||||
* Ordinary environment entries merged onto the implementation's scrubbed
|
||||
* parent base (see `scrubbedParentEnv`). `DSH_*` names are rejected and
|
||||
* belong in {@link dshEnv}; a deliberately forwarded credential-shaped
|
||||
* entry survives because this layer merges after the scrub.
|
||||
* Explicit environment entries merged onto the implementation's scrubbed
|
||||
* parent base (see `scrubbedParentEnv`), with no namespace validation:
|
||||
* every entry is a deliberate caller opt-in, so a forwarded
|
||||
* credential-shaped entry or a current `DSH_*` fact survives precisely
|
||||
* because this layer merges after the scrub that drops its ambient
|
||||
* namesake.
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/**
|
||||
* Harness-owned `DSH_*` variables for this execution. The scrubbed base has
|
||||
* already discarded ambient `DSH_*` entries, so an unavailable current fact
|
||||
* cannot inherit a stale value from the harness process; non-`DSH_*` names
|
||||
* on this channel are rejected.
|
||||
*/
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user