From a53be53d64732cf3ded39bb4fd008b78e5c076aa Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Fri, 10 Jul 2026 00:23:11 +0800 Subject: [PATCH] workflow: forward TSX_TSCONFIG_PATH through the worker env scrub The empty-env hardening wiped the one variable the UNBUILT worker's loader depends on: tsx finds a tsconfig by searching up from the worker's cwd, and a parent running outside the repo (the ACP snapshot harness pins the repo tsconfig through TSX_TSCONFIG_PATH exactly because its child cwd is a temp dir) lost the dsh-* paths map - the worker then resolved workspace imports to unbuilt lib/ bundles and died on CI with ERR_MODULE_NOT_FOUND (green locally only because stale built lib/ masked the wrong resolution). Forward exactly that variable when the parent carries it - loader plumbing, not a secret; the built shape stays fully empty - and pin the whole contract with an escape-based test: the worker env is exactly {TSX_TSCONFIG_PATH}, the credential canary still never crosses. --- .../tests/workflow-workerthread.spec.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts b/packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts index 32f63a6c97..ecaeed61f6 100644 --- a/packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts +++ b/packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from 'vitest' +import { fileURLToPath } from 'node:url' import { Context } from 'cordis' import Loader from '@cordisjs/plugin-loader' import { AgentId } from '@deepseek-ai/dsh-agent' @@ -272,6 +273,28 @@ describe('dsh-workflow-workerthread', () => { delete process.env.WORKFLOW_ENV_CANARY } }) + + it('the unbuilt worker forwards exactly TSX_TSCONFIG_PATH through the scrub: the paths-map pin survives, secrets do not', async () => { + const { ctx, parent } = await setup() + // The ACP snapshot harness runs the parent with its cwd OUTSIDE the + // repo and pins the repo tsconfig through this variable; the worker + // must inherit the pin (or its dsh-* imports silently resolve to + // unbuilt lib/ bundles) while every other variable stays scrubbed. + const tsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url)) + process.env.TSX_TSCONFIG_PATH = tsconfig + process.env.WORKFLOW_ENV_CANARY = 'leak me' + try { + const result = await run(ctx, parent, scripted(` + const proc = ${ESCAPE} + return { keys: Object.keys(proc.env), tsconfig: proc.env.TSX_TSCONFIG_PATH } + `)) + expect(result.stopReason).toBe('completed') + expect(result.value).toEqual({ keys: ['TSX_TSCONFIG_PATH'], tsconfig }) + } finally { + delete process.env.TSX_TSCONFIG_PATH + delete process.env.WORKFLOW_ENV_CANARY + } + }) }) describe('lifecycle: parse errors, cancellation, termination, disposal', () => {