fix(subagent): route explicit DSH_* config entries onto the managed env channel
The acp run passed config.env wholesale into the spawn spec's ordinary channel, which rejects the reserved DSH_* namespace — a deployment fact like DSH_PERMISSION_MODE (the acp-agent example's own knob, used by the with-key e2e) crashed the spawn. The run now splits DSH_* entries onto dshEnv, where the scrubbed base expects current facts to arrive. New layering test drives the split through the real seam via a MOCK_ECHO_ENV knob on the mock server; README env prose updated (en+zh, re-recorded).
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
* fully scripted by environment variables — no model, no network:
|
||||
*
|
||||
* - `MOCK_TEXT` — the assistant text it streams as one `agent_message_chunk`.
|
||||
* - `MOCK_ECHO_ENV` — if set to a variable NAME, stream that variable's value
|
||||
* (or `<NAME unset>`) instead of MOCK_TEXT — asserts what
|
||||
* environment actually reached the child process.
|
||||
* - `MOCK_STOP` — the ACP `StopReason` it returns from `prompt`
|
||||
* (`end_turn` default, or `max_tokens`/`refusal`/…).
|
||||
* - `MOCK_HANG` — if `1`, `prompt` never resolves on its own (it waits for
|
||||
@@ -67,7 +70,12 @@ import {
|
||||
type StopReason,
|
||||
} from '@agentclientprotocol/sdk'
|
||||
|
||||
const TEXT = process.env.MOCK_TEXT ?? 'mock child answer'
|
||||
// When MOCK_ECHO_ENV names a variable, stream that variable's value in place
|
||||
// of MOCK_TEXT — lets a test assert exactly what env reached this process.
|
||||
const echoEnvName = process.env.MOCK_ECHO_ENV
|
||||
const TEXT = echoEnvName !== undefined
|
||||
? process.env[echoEnvName] ?? `<${echoEnvName} unset>`
|
||||
: process.env.MOCK_TEXT ?? 'mock child answer'
|
||||
const ECHO_CWD = process.env.MOCK_ECHO_CWD === '1'
|
||||
const STOP = (process.env.MOCK_STOP ?? 'end_turn') as StopReason
|
||||
const HANG = process.env.MOCK_HANG === '1'
|
||||
|
||||
@@ -119,6 +119,21 @@ describe('child env layering (through the subprocess seam)', () => {
|
||||
delete process.env.ACP_TEST_AMBIENT_SECRET_TOKEN
|
||||
}
|
||||
})
|
||||
|
||||
it('routes explicit DSH_* config entries onto the managed channel', async () => {
|
||||
// A deployment sets child-harness facts like DSH_PERMISSION_MODE in
|
||||
// config.env; the run must split them onto the seam's managed channel
|
||||
// (the ordinary channel rejects the reserved namespace) and the child
|
||||
// must still see the value.
|
||||
const ctx = await setup({ MOCK_ECHO_ENV: 'DSH_ACP_TEST_FACT', DSH_ACP_TEST_FACT: 'managed' })
|
||||
const parent = { id: 'parent', session: { header: { cwd: process.cwd() } } } as unknown as Agent
|
||||
const run = await ctx.subagents.start('acp', { prompt: [{ type: 'text' as const, text: 'p' }], parent, signal: new AbortController().signal })
|
||||
const result = await run.result
|
||||
await run.dispose()
|
||||
const text = result.output.filter(b => b.type === 'text').map(b => (b as { text: string }).text).join('')
|
||||
expect(text).toBe('managed')
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
describe('cwd resolution', () => {
|
||||
|
||||
Reference in New Issue
Block a user