diff --git a/packages/paperclip-runner/src/drivers/runtime-context-materializer.test.ts b/packages/paperclip-runner/src/drivers/runtime-context-materializer.test.ts index 9f09593949..4e68be2944 100644 --- a/packages/paperclip-runner/src/drivers/runtime-context-materializer.test.ts +++ b/packages/paperclip-runner/src/drivers/runtime-context-materializer.test.ts @@ -1,3 +1,4 @@ +import { createRunnerdCodexAppServerArgs } from "../live/runnerd-codex-transport.js"; import { chmod, lstat, @@ -517,3 +518,12 @@ command = "untrusted-command" await expect(stat(join(symlinkCodexHome, "auth.json"))).rejects.toThrow(); }); }); + +it("exposes a trusted persistent personal directory without instruction files", () => { + const options = { environment: { AGENT_HOME: "/untrusted", HOME: "/ambient" }, codexHome: "/private-provider" }; + const supplied = createRunnerdCodexAppServerArgs({ ...options, persistentAgentHome: "/personal/agent" }).join("\n"); + expect(supplied).toContain('AGENT_HOME="/personal/agent"'); + expect(supplied).toContain('"/private-provider"="none"'); + expect(supplied).not.toContain('AGENT_HOME="/untrusted"'); + expect(createRunnerdCodexAppServerArgs(options).join("\n")).not.toContain("AGENT_HOME"); +}); diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts index 3bc94c1cb2..ae90a54fb4 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts @@ -1207,6 +1207,8 @@ export interface CapabilityRunnerdProcessEvidence { export interface CapabilityRunnerdCodexTransportOptions { /** Server-authorized local computer MCP on the selected execution environment. */ computerTool?: { command: string; args: readonly string[] } | null; + /** Controller-authorized durable personal directory, independent of instructions. */ + persistentAgentHome?: string; provider?: "codex" | "opencode" | "claude_managed" | "aws_agentcore" | "acpx"; opencodePermissionMode?: NativeOpenCodePermissionMode; acpxAgent?: QualifiedAcpxAgent; @@ -3447,6 +3449,7 @@ export function createRunnerdCodexAppServerArgs(input: { codexCommand?: string; readOnlyRoots?: string[]; instructionWorkingCopyRoot?: string; + persistentAgentHome?: string; }): string[] { // The filesystem policy denies HOME and CODEX_HOME to keep credentials and // runner state outside provider reach. Always bind those names to the actual @@ -3457,9 +3460,10 @@ export function createRunnerdCodexAppServerArgs(input: { ...input.environment, HOME: input.codexHome, CODEX_HOME: input.codexHome, + ...(input.persistentAgentHome ? { AGENT_HOME: input.persistentAgentHome } : {}), }, [...(input.readOnlyRoots ?? []), ...codexExecutableReadOnlyRoots(input.environment ?? {}, input.codexCommand)], - input.instructionWorkingCopyRoot, + input.instructionWorkingCopyRoot ?? input.persistentAgentHome, ); } @@ -4969,6 +4973,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { codexHome, codexCommand: effectiveCodexCommand, instructionWorkingCopyRoot: runtimeContext?.instructions.workingCopy?.rootPath, + persistentAgentHome: this.options.persistentAgentHome, readOnlyRoots: [ ...trustedRuntimeReadOnlyRoots( this.options.environment, @@ -5453,6 +5458,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { codexHome, codexCommand: typeof recordedCommand === "string" ? recordedCommand : this.options.codexCommand, instructionWorkingCopyRoot: runtimeContext?.instructions.workingCopy?.rootPath, + persistentAgentHome: this.options.persistentAgentHome, readOnlyRoots: [ ...trustedRuntimeReadOnlyRoots(this.options.environment), ...(runtimeContext diff --git a/server/src/services/native-runtime/native-session-executor.ts b/server/src/services/native-runtime/native-session-executor.ts index 407bfa9ee9..b4b0dd4252 100644 --- a/server/src/services/native-runtime/native-session-executor.ts +++ b/server/src/services/native-runtime/native-session-executor.ts @@ -13411,6 +13411,7 @@ async function createRunnerdBackendWithinSessionClaim( resumeWorkingDirectory: runnerExecution.workspace.cwd, externallySandboxed: adapterExecutionTargetIsCommandBacked(remoteTarget), computerTool: remoteTarget?.transport === "computer" ? remoteTarget.computerTool : undefined, + persistentAgentHome: remoteTarget?.transport === "computer" ? remoteTarget.fileAuthority.agentHome : undefined, opencodeRuntimeDirectory: remoteRunnerFilesystemRoot ? posix.join(remoteRunnerFilesystemRoot, "opencode") : undefined,