Skip to content

Commit 7641ec8

Browse files
committed
fix(codex): restore runtime policy metadata
1 parent 28cbeaa commit 7641ec8

8 files changed

Lines changed: 230 additions & 45 deletions

File tree

extensions/codex/src/app-server/config.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ describe("Codex app-server config", () => {
202202
},
203203
unix_sockets: {
204204
"/tmp/mock-proxy.sock": "allow",
205-
"/tmp/blocked.sock": "none",
205+
"/tmp/blocked.sock": "deny",
206206
},
207207
proxy_url: "http://127.0.0.1:3128",
208208
socks_url: "socks5h://127.0.0.1:8081",

extensions/codex/src/app-server/config.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,9 @@ const codexAppServerNetworkProxySchema = z
337337
baseProfile: z.enum(["read-only", "workspace"]).optional(),
338338
mode: z.enum(["limited", "full"]).optional(),
339339
domains: z.record(z.string(), codexAppServerNetworkProxyDomainPermissionSchema).optional(),
340-
unixSockets: z.record(z.string(), codexAppServerNetworkProxyUnixSocketPermissionSchema).optional(),
340+
unixSockets: z
341+
.record(z.string(), codexAppServerNetworkProxyUnixSocketPermissionSchema)
342+
.optional(),
341343
proxyUrl: z.string().trim().min(1).optional(),
342344
socksUrl: z.string().trim().min(1).optional(),
343345
enableSocks5: z.boolean().optional(),
@@ -493,6 +495,19 @@ export function resolveCodexPluginsPolicy(pluginConfig?: unknown): ResolvedCodex
493495
};
494496
}
495497

498+
function resolveCodexPluginDestructivePolicy(policy: CodexPluginDestructivePolicy): {
499+
allowDestructiveActions: boolean;
500+
destructiveApprovalMode: CodexPluginDestructiveApprovalMode;
501+
} {
502+
if (policy === "auto") {
503+
return { allowDestructiveActions: true, destructiveApprovalMode: "auto" };
504+
}
505+
return {
506+
allowDestructiveActions: policy,
507+
destructiveApprovalMode: policy ? "allow" : "deny",
508+
};
509+
}
510+
496511
type CodexAppServerRuntimeParams = {
497512
pluginConfig?: unknown;
498513
execMode?: OpenClawExecMode;
@@ -672,6 +687,9 @@ export function resolveCodexAppServerRuntime(
672687
headers,
673688
...(transport === "stdio" && clearEnv.length > 0 ? { clearEnv } : {}),
674689
},
690+
connectionClass,
691+
remoteAppsSubstrate,
692+
...(remoteWorkspaceRoot ? { remoteWorkspaceRoot } : {}),
675693
codeModeOnly: config.codeModeOnly === true,
676694
requestTimeoutMs: normalizePositiveNumber(config.requestTimeoutMs, 60_000),
677695
turnCompletionIdleTimeoutMs: normalizePositiveNumber(
@@ -688,16 +706,13 @@ export function resolveCodexAppServerRuntime(
688706
: {}),
689707
approvalPolicy: forcedPolicy?.approvalPolicy ?? approvalPolicy,
690708
approvalPolicySource,
691-
sandbox:
692-
forcedPolicy?.sandbox ??
693-
configuredSandbox ??
694-
defaultPolicy?.sandbox ??
695-
(policyMode === "guardian" ? "workspace-write" : "danger-full-access"),
709+
sandbox: resolvedSandbox,
696710
approvalsReviewer:
697711
forcedPolicy?.approvalsReviewer ??
698712
explicitApprovalsReviewer ??
699713
defaultPolicy?.approvalsReviewer ??
700714
(policyMode === "guardian" ? "auto_review" : "user"),
715+
...resolveCodexAppServerNetworkProxy(config.networkProxy, resolvedSandbox),
701716
...(serviceTier ? { serviceTier } : {}),
702717
},
703718
};
@@ -929,7 +944,7 @@ function resolveCodexAppServerNetworkProxy(
929944
enabled: true,
930945
mode: config.mode,
931946
domains: normalizeNetworkProxyPermissionMap(config.domains),
932-
unix_sockets: normalizeNetworkProxyPermissionMap(config.unixSockets),
947+
unix_sockets: normalizeNetworkProxyUnixSocketPermissionMap(config.unixSockets),
933948
proxy_url: readNonEmptyString(config.proxyUrl),
934949
socks_url: readNonEmptyString(config.socksUrl),
935950
enable_socks5: config.enableSocks5,
@@ -984,6 +999,20 @@ export function fingerprintCodexAppServerNetworkProxyConfigPatch(configPatch: Js
984999
return createHash("sha256").update(stableStringifyJson(configPatch)).digest("hex");
9851000
}
9861001

1002+
function normalizeNetworkProxyUnixSocketPermissionMap(
1003+
value: Record<string, CodexAppServerNetworkProxyUnixSocketPermission> | undefined,
1004+
): Record<string, "allow" | "deny"> | undefined {
1005+
const normalized = normalizeNetworkProxyPermissionMap(value);
1006+
return normalized
1007+
? Object.fromEntries(
1008+
Object.entries(normalized).map(([socketPath, permission]) => [
1009+
socketPath,
1010+
permission === "none" ? "deny" : permission,
1011+
]),
1012+
)
1013+
: undefined;
1014+
}
1015+
9871016
function normalizeNetworkProxyPermissionMap<TPermission extends string>(
9881017
value: Record<string, TPermission> | undefined,
9891018
): Record<string, TPermission> | undefined {

extensions/codex/src/app-server/session-binding.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ const pluginAppPolicyContextSchema = z
107107
marketplaceName: z.literal(CODEX_PLUGINS_MARKETPLACE_NAME),
108108
pluginName: z.string(),
109109
allowDestructiveActions: z.boolean(),
110+
destructiveApprovalMode: z.enum(["allow", "deny", "auto"]).optional().catch(undefined),
110111
mcpServerNames: z.array(z.string()),
111112
})
112113
.strict(),
@@ -143,11 +144,15 @@ const threadBindingSchema = z.object({
143144
.preprocess(normalizeCodexServiceTier, z.string().optional())
144145
.optional()
145146
.catch(undefined),
147+
networkProxyProfileName: optionalStringSchema,
148+
networkProxyConfigFingerprint: optionalStringSchema,
146149
dynamicToolsFingerprint: optionalStringSchema,
147150
dynamicToolsContainDeferred: optionalBooleanSchema,
151+
webSearchThreadConfigFingerprint: optionalStringSchema,
148152
userMcpServersFingerprint: optionalStringSchema,
149153
mcpServersFingerprint: optionalStringSchema,
150154
nativeHookRelayGeneration: optionalNonBlankStringSchema,
155+
appServerRuntimeFingerprint: optionalStringSchema,
151156
pluginAppsFingerprint: optionalStringSchema,
152157
pluginAppsInputFingerprint: optionalStringSchema,
153158
pluginAppPolicyContext: pluginAppPolicyContextSchema.optional().catch(undefined),

extensions/codex/src/app-server/side-question.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const createOpenClawCodingToolsMock = vi.fn();
2525
const toolExecuteMock = vi.fn();
2626
const handleCodexAppServerApprovalRequestMock = vi.fn();
2727
const configRuntimeMock = vi.hoisted(() => ({ rejectedProvider: undefined as string | undefined }));
28+
const resolveCodexProviderWebSearchSupportForClientMock = vi.fn();
2829

2930
vi.mock("./config.js", async (importOriginal) => {
3031
const actual = await importOriginal<typeof import("./config.js")>();
@@ -520,6 +521,7 @@ describe("runCodexAppServerSideQuestion", () => {
520521
senderE164: "+15550001",
521522
senderIsOwner: true,
522523
}),
524+
{ pluginConfig: { appServer: { mode: "yolo", approvalsReviewer: "user" } } },
523525
);
524526

525527
expect(result).toEqual({ text: "Side answer." });
@@ -1158,7 +1160,7 @@ describe("runCodexAppServerSideQuestion", () => {
11581160
enabled: true,
11591161
profileName: "side-proxy",
11601162
domains: { "api.openai.com": "allow" },
1161-
unixSockets: { "/tmp/proxy.sock": "allow" },
1163+
unixSockets: { "/tmp/proxy.sock": "allow", "/tmp/blocked.sock": "none" },
11621164
allowUpstreamProxy: true,
11631165
proxyUrl: "http://127.0.0.1:3128",
11641166
},
@@ -1182,7 +1184,7 @@ describe("runCodexAppServerSideQuestion", () => {
11821184
network: {
11831185
enabled: true,
11841186
domains: { "api.openai.com": "allow" },
1185-
unix_sockets: { "/tmp/proxy.sock": "allow" },
1187+
unix_sockets: { "/tmp/proxy.sock": "allow", "/tmp/blocked.sock": "deny" },
11861188
allow_upstream_proxy: true,
11871189
proxy_url: "http://127.0.0.1:3128",
11881190
},
@@ -1191,6 +1193,10 @@ describe("runCodexAppServerSideQuestion", () => {
11911193
});
11921194
expect(config?.["features.code_mode"]).toBe(true);
11931195
expect(config?.["features.code_mode_only"]).toBe(false);
1196+
const turnStartParams = client.request.mock.calls.find(
1197+
([method]) => method === "turn/start",
1198+
)?.[1] as Record<string, unknown> | undefined;
1199+
expect(turnStartParams).not.toHaveProperty("sandboxPolicy");
11941200
});
11951201

11961202
it("keeps Codex code-mode-only while disabling Guardian for provider-qualified local models", async () => {

extensions/codex/src/app-server/side-question.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import {
7575
type JsonObject,
7676
type JsonValue,
7777
} from "./protocol.js";
78+
import { resolveCodexProviderWebSearchSupportForClient } from "./provider-capabilities.js";
7879
import { readRecentCodexRateLimits } from "./rate-limit-cache.js";
7980
import { formatCodexUsageLimitErrorMessage } from "./rate-limits.js";
8081
import { buildCodexRuntimeThreadConfig } from "./runtime-thread-config.js";
@@ -157,10 +158,16 @@ export async function runCodexAppServerSideQuestion(
157158
"Codex /btw needs an active Codex thread. Send a normal message first, then try /btw again.",
158159
);
159160
}
161+
const sessionAgentId = bindingIdentity.agentId;
162+
const sandboxSessionKey =
163+
params.sandboxSessionKey?.trim() ||
164+
params.sessionKey?.trim() ||
165+
params.sessionId ||
166+
sessionAgentId;
160167
const nativeExecutionBlock = resolveCodexNativeExecutionBlock({
161168
config: params.cfg,
162-
agentId: params.agentId,
163-
sessionKey: params.sessionKey,
169+
agentId: sessionAgentId,
170+
sessionKey: sandboxSessionKey,
164171
sessionId: params.sessionId,
165172
surface: "/btw side-question mode",
166173
});
@@ -169,7 +176,6 @@ export async function runCodexAppServerSideQuestion(
169176
}
170177

171178
const pluginConfig = readCodexPluginConfig(options.pluginConfig);
172-
const sessionAgentId = bindingIdentity.agentId;
173179
const execPolicy = resolveOpenClawExecPolicyForCodexAppServer({
174180
approvals: loadExecApprovals(),
175181
config: params.cfg,
@@ -247,11 +253,6 @@ export async function runCodexAppServerSideQuestion(
247253
const turnRouter = getCodexAppServerTurnRouter(client);
248254
const cwd = binding.cwd || params.workspaceDir || process.cwd();
249255
const sideRunParams = buildSideRunAttemptParams(params, { cwd, authProfileId });
250-
const sandboxSessionKey =
251-
params.sandboxSessionKey?.trim() ||
252-
params.sessionKey?.trim() ||
253-
params.sessionId ||
254-
sessionAgentId;
255256
const sandbox = await resolveSandboxContext({
256257
config: params.cfg,
257258
agentId: sessionAgentId,
@@ -419,10 +420,13 @@ export async function runCodexAppServerSideQuestion(
419420
: options.nativeHookRelay?.enabled === false
420421
? buildCodexNativeHookRelayDisabledConfig()
421422
: undefined;
422-
const runtimeThreadConfig = buildCodexRuntimeThreadConfig(webSearchPlan.threadConfig, {
423-
nativeCodeModeEnabled: nativeToolSurfaceEnabled,
424-
nativeCodeModeOnlyEnabled: appServer.codeModeOnly,
425-
});
423+
const runtimeThreadConfig = buildCodexRuntimeThreadConfig(
424+
mergeCodexThreadConfigs(webSearchPlan.threadConfig, appServer.networkProxy?.configPatch),
425+
{
426+
nativeCodeModeEnabled: nativeToolSurfaceEnabled,
427+
nativeCodeModeOnlyEnabled: appServer.codeModeOnly,
428+
},
429+
);
426430
const threadConfig =
427431
mergeCodexThreadConfigs(nativeHookRelayConfig, runtimeThreadConfig) ?? runtimeThreadConfig;
428432
const forkResponse = await validateCodexThreadCreationResponse(
@@ -437,7 +441,7 @@ export async function runCodexAppServerSideQuestion(
437441
cwd,
438442
approvalPolicy,
439443
approvalsReviewer: modelScopedAppServer.approvalsReviewer,
440-
...(modelScopedAppServer.networkProxy ? {} : { sandbox: appServerSandbox }),
444+
...(appServer.networkProxy ? {} : { sandbox: appServerSandbox }),
441445
...(serviceTier ? { serviceTier } : {}),
442446
config: threadConfig,
443447
developerInstructions: SIDE_DEVELOPER_INSTRUCTIONS,
@@ -518,7 +522,9 @@ export async function runCodexAppServerSideQuestion(
518522
cwd,
519523
approvalPolicy,
520524
approvalsReviewer: modelScopedAppServer.approvalsReviewer,
521-
sandboxPolicy: codexSandboxPolicyForTurn(appServerSandbox, cwd),
525+
...(appServer.networkProxy
526+
? {}
527+
: { sandboxPolicy: codexSandboxPolicyForTurn(appServerSandbox, cwd) }),
522528
model: activeModel,
523529
personality: CODEX_NATIVE_PERSONALITY_NONE,
524530
...(serviceTier ? { serviceTier } : {}),

0 commit comments

Comments
 (0)