Use the external sandbox for approved native Codex ACP runs
Select Codex ACP's full-access initial mode only for host-validated external work-folder environments with approve-all authority. Keep local and restrictive permission modes unchanged. The regression failed before the fix; all 50 ACP environment and runtime tests pass. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
fbf2494104
commit
8787dab6f3
|
|
@ -114,6 +114,10 @@ Sandbox Codex tool commands preserve the environment initialized by the adapter:
|
|||
login-shell execution and shell snapshots are disabled so image profiles cannot
|
||||
replace the managed Git PATH. This applies to CLI and ACP execution in both
|
||||
runner generations; local execution keeps its existing settings.
|
||||
Native Codex ACP selects the provider's `agent-full-access` initial mode only
|
||||
inside a validated external work-folder sandbox with an explicit `approve-all`
|
||||
binding. This avoids starting an unsupported nested network namespace. Local
|
||||
execution and the `approve-reads` / `deny-all` modes retain their existing policy.
|
||||
CLI state is separate from the four shared collections. A change of task,
|
||||
agent, responsible user, or project cannot reuse a sandbox with another binding.
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,28 @@ describe("ACPX runtime sandbox", () => {
|
|||
.toBe("allow_login_shell = false\n\n[features]\nshell_snapshot = false\n");
|
||||
});
|
||||
|
||||
it.each(["approve-all", "approve-reads", "deny-all"] as const)(
|
||||
"uses the external sandbox boundary only for explicitly approved Codex execution (%s)", async (permissionMode) => {
|
||||
const fixture = await sandboxFixture("codex");
|
||||
const home = join(fixture.root, "home");
|
||||
const environment = {
|
||||
HOME: home, PAPERCLIP_RUNNER_EXTERNAL_SANDBOX: "1",
|
||||
INITIAL_AGENT_MODE: "agent-full-access",
|
||||
...Object.fromEntries(["task", "agent", "user", "project", "repos"].map((scope) =>
|
||||
[`PAPERCLIP_${scope.toUpperCase()}_DIR`, join(home, scope)])),
|
||||
};
|
||||
const sandbox = await prepareAcpxRuntimeSandbox({
|
||||
binding: { ...fixture.binding, permissionMode }, agent: "codex", environment,
|
||||
});
|
||||
expect(sandbox.launchEnvironment.INITIAL_AGENT_MODE).toBe(permissionMode === "approve-all" ? "agent-full-access" : undefined);
|
||||
const local = await prepareAcpxRuntimeSandbox({
|
||||
binding: { ...fixture.binding, permissionMode }, agent: "codex",
|
||||
environment: { ...environment, PAPERCLIP_RUNNER_EXTERNAL_SANDBOX: undefined },
|
||||
});
|
||||
expect(local.launchEnvironment.INITIAL_AGENT_MODE).toBeUndefined();
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
["pi", "OPENROUTER_API_KEY", "pi-home"],
|
||||
["claude", "ANTHROPIC_API_KEY", "claude-home"],
|
||||
|
|
|
|||
|
|
@ -439,6 +439,13 @@ export async function prepareAcpxRuntimeSandbox(input: {
|
|||
? {
|
||||
CODEX_HOME: agentHomeDirectory,
|
||||
NO_BROWSER: "1",
|
||||
// The admitted external sandbox supplies OS isolation. Codex ACP's
|
||||
// default mode otherwise starts a second network namespace, which
|
||||
// cannot initialize inside Daytona. Only the host's explicit
|
||||
// approve-all binding may select this provider mode.
|
||||
...(input.binding.permissionMode === "approve-all"
|
||||
&& externalWorkFolderEnvironment(input.environment ?? {}).HOME
|
||||
? { INITIAL_AGENT_MODE: "agent-full-access" } : {}),
|
||||
...(launchEnvironment.CODEX_API_KEY ||
|
||||
launchEnvironment.OPENAI_API_KEY
|
||||
? { DEFAULT_AUTH_REQUEST: JSON.stringify({ methodId: "api-key" }) }
|
||||
|
|
|
|||
Loading…
Reference in New Issue