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:
@@ -25,7 +25,6 @@ import {
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SubagentResult, SubagentRun, SubagentStartRequest, SubagentStopReason } from '@deepseek-ai/dsh-subagent'
|
||||
import { splitEnvChannels } from '@deepseek-ai/dsh-subprocess'
|
||||
import type { SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess'
|
||||
|
||||
/** Fixed response to child permission requests: reject by default, or select the first allow option. */
|
||||
@@ -51,8 +50,8 @@ export interface AcpRunSpec {
|
||||
* parent env. A value here is forwarded even if its name matches the
|
||||
* credential-scrub pattern (an explicit opt-in for the child's own creds).
|
||||
* Explicit `DSH_*` entries are deployment-owned facts for the child harness
|
||||
* (e.g. `DSH_PERMISSION_MODE`) and ride the seam's managed channel, which
|
||||
* the scrubbed base reserves for current values.
|
||||
* (e.g. `DSH_PERMISSION_MODE`); they simply merge after the scrub that
|
||||
* dropped their stale ambient namesakes.
|
||||
*/
|
||||
env: Record<string, string>
|
||||
/**
|
||||
@@ -169,15 +168,14 @@ export async function startAcpRun(request: SubagentStartRequest, spec: AcpRunSpe
|
||||
const id = SessionId(randomUUID())
|
||||
|
||||
// Keep diagnostics on parent stderr ('inherit'); only ACP output contributes
|
||||
// to the result. The seam's scrub drops ambient credentials while spec.env
|
||||
// (the child's own key) merges after it; explicit DSH_* entries ride the
|
||||
// managed channel via the seam's split.
|
||||
// to the result. The seam's scrub drops ambient credentials and DSH_* names
|
||||
// while spec.env (the child's own key, its deployment facts) merges after it.
|
||||
const child = spec.spawn({
|
||||
argv: [spec.command, ...spec.args],
|
||||
cwd: spec.cwd,
|
||||
stdio: { stdin: 'pipe', stdout: 'pipe', stderr: 'inherit' },
|
||||
graceMs: spec.disposeGraceMs,
|
||||
...splitEnvChannels(spec.env),
|
||||
env: spec.env,
|
||||
})
|
||||
/* v8 ignore start -- 'pipe' dispositions expose both streams by the seam contract; defensive. */
|
||||
if (child.stdin === undefined || child.stdout === undefined) {
|
||||
|
||||
Reference in New Issue
Block a user