diff --git a/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts b/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts index 885e1de53b4..7135b052ab2 100644 --- a/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts +++ b/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.test.ts @@ -30,6 +30,7 @@ import { type NativeRuntimeContextSnapshot, } from "../../contracts/runtime-context.js"; import { createCodexTaskEnvelope } from "../../contracts/codex.js"; +import { configuredEnvironmentProjection } from "../../configured-environment.js"; import { localIntegrityBoundaryGolden } from "../../../test-support/local-integrity-boundary-golden.js"; import type { PrpEvent } from "../../protocol/replay-contract.js"; import { @@ -153,6 +154,35 @@ afterAll(async () => { }); describe("OpenCodeServerDriver", () => { + it("delivers configured task values without ambient credentials or driver-owned overrides", async () => { + await chmod(fixture, 0o755); + const root = await mkdtemp(join(tmpdir(), "paperclip-opencode-task-env-")); + roots.push(root); + const command = join(root, "configured-task-opencode.mjs"); + const canaryReceipt = join(root, "task-canary.json"); + await writeFile(command, `#!${process.execPath}\nimport { writeFile } from "node:fs/promises";\nawait writeFile(${JSON.stringify(canaryReceipt)}, JSON.stringify({ present: process.env.PUBLISHING_API_KEY === "synthetic-task-canary" }));\nawait import(${JSON.stringify(fixture)});\n`, { mode: 0o755 }); + const driver = new OpenCodeServerDriver({ + model: "openrouter/test-model", command, runtimeDirectory: root, + environment: { + ...configuredEnvironmentProjection({ PUBLISHING_API_KEY: "synthetic-task-canary", + OPENCODE_SERVER_PASSWORD: "must-not-override", XDG_CONFIG_HOME: "/must-not-override" }), + PATH: process.env.PATH, UNRELATED_SECRET: "must-not-leak", PAPERCLIP_API_KEY: "must-not-leak", + }, + }); + const session = await driver.openSession({ runId: "task-env", normalizedSessionId: "task-env", workingDirectory: root }); + try { + const environment = JSON.parse(await readFile(join(root, "task-env", "data", "fake-environment.json"), "utf8")); + expect(environment.keys).not.toContain("UNRELATED_SECRET"); + expect(environment.keys).not.toContain("PAPERCLIP_API_KEY"); + expect(dirname(environment.home)).toBe(join(root, "task-env", "homes")); + expect(environment.configHome).toBe(join(root, "task-env", "config")); + expect(session.ids()).toMatchObject({ providerSessionId: "ses_fake_1" }); + expect(JSON.parse(await readFile(canaryReceipt, "utf8"))).toEqual({ present: true }); + expect(environment.keys).toContain("PUBLISHING_API_KEY"); + } finally { + await session.close({ reason: "test" }); + } + }); it.each([false, true])("uses the native MCP client ceiling only for durable tool requests (%s)", async durableToolRequests => { await chmod(fixture, 0o755); const root = await mkdtemp(join(tmpdir(), "paperclip-opencode-tool-budget-")); diff --git a/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts b/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts index 159e793cd08..12ed0618233 100644 --- a/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts +++ b/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts @@ -29,6 +29,7 @@ import { startOpenCodePerformanceDiagnostics } from "./performance-diagnostics.j import { PROVIDER_PERFORMANCE_ENV } from "../../contracts/provider-performance.js"; import { annotateOmittedProviderText, parseOpenCodeSseFrames } from "./sse-frames.js"; import { githubCredentialEnvironment } from "../../github-credential-environment.js"; +import { configuredEnvironment } from "../../configured-environment.js"; import { CODEX_SKILLLESS_BASE_INSTRUCTIONS, @@ -3092,7 +3093,7 @@ function sanitizedEnvironment( const candidate = source ?? process.env; for (const key of allowed) if (candidate[key] !== undefined) result[key] = candidate[key]; - return { ...result, ...githubCredentialEnvironment(source ?? {}), ...overrides }; + return { ...configuredEnvironment(source), ...result, ...githubCredentialEnvironment(source ?? {}), ...overrides }; } function sanitizedEnvironmentKeys(): string[] {