@openclaw/codex 2026.7.2-beta.4 → 2026.7.2-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{app-server-policy-BO3jYBkv.js → app-server-policy-BSaO2JIT.js} +1 -1
  2. package/dist/{attempt-notifications-DdmnBMUp.js → attempt-notifications-AP-a-Q5k.js} +1 -1
  3. package/dist/{bounded-turn-CaQL96kg.js → bounded-turn-D2ZAgZVz.js} +54 -24
  4. package/dist/{capabilities-CLICvTxf.js → capabilities-BFDTVYr4.js} +1 -1
  5. package/dist/cli-metadata.js +1 -1
  6. package/dist/{command-formatters-DgADqnHa.js → command-formatters-C336eRVS.js} +2 -2
  7. package/dist/{command-handlers-l_lVPNlB.js → command-handlers-DAHUSry9.js} +1233 -1222
  8. package/dist/{compact-CC5KhBzX.js → compact-D_uvHurS.js} +8 -8
  9. package/dist/{computer-use-Ip1-QGa-.js → computer-use-CYEc8U3w.js} +123 -115
  10. package/dist/{conversation-control-sNPsbTCP.js → conversation-control-xsIeXbo4.js} +6 -6
  11. package/dist/doctor-contract-api.js +24 -86
  12. package/dist/{dynamic-tools-CQI3UwqY.js → dynamic-tools-D2gYkSBO.js} +482 -91
  13. package/dist/effective-mcp-catalog-Bbd98Inw.js +114 -0
  14. package/dist/{event-projector-usage-D6tE7_qD.js → event-projector-usage-Xp4AlR2k.js} +20 -3
  15. package/dist/harness.js +19 -14
  16. package/dist/index.js +292 -328
  17. package/dist/media-understanding-provider.js +6 -1
  18. package/dist/{models-BNeMlHgg.js → models-BBMKU4jA.js} +2 -2
  19. package/dist/{notification-correlation-BGLixv5_.js → notification-correlation-CU6_AnOb.js} +3 -3
  20. package/dist/{plugin-app-cache-key-CvzvmBRz.js → plugin-app-cache-key-DztPafOA.js} +135 -42
  21. package/dist/{rate-limits-D0IJT-a6.js → rate-limits-CU-gVCCS.js} +1 -1
  22. package/dist/{request-BTccUTD3.js → request-qKh331yu.js} +3 -3
  23. package/dist/{run-attempt--858NXDl.js → run-attempt-CY9WlDLc.js} +1060 -402
  24. package/dist/{runtime-artifact-C2ITXdhM.js → runtime-artifact-CmtG_t1L.js} +90 -24
  25. package/dist/{sandbox-guard-D1Wky__S.js → sandbox-guard-NuhpXmPB.js} +4 -0
  26. package/dist/{session-binding-ZSMX1omP.js → session-binding-BXpo9ymA.js} +1006 -891
  27. package/dist/{session-catalog-DsKaezgR.js → session-catalog-DyOgR4WH.js} +289 -56
  28. package/dist/{session-cli-CYJTfRLG.js → session-cli-2PZaFLRh.js} +2 -2
  29. package/dist/{session-upstream-marker-D7XlI9us.js → session-upstream-marker-B6U7tLw5.js} +1 -1
  30. package/dist/{settled-turn-finalizer-DHltNgtF.js → settled-turn-finalizer-CXY2Z9dT.js} +19 -6
  31. package/dist/{shared-client-wmgNFNYp.js → shared-client-DTbvH-XV.js} +1993 -1720
  32. package/dist/{side-question-BfFZCsJ-.js → side-question-_ulBk1AL.js} +12 -10
  33. package/dist/{thread-lifecycle-cqrOlJkq.js → thread-lifecycle-DF53ogiA.js} +980 -346
  34. package/dist/{transcript-mirror-2p7kl6e4.js → transcript-mirror-DzsdNCf5.js} +80 -41
  35. package/dist/{upstream-session-fork-vPjwkT1i.js → upstream-session-fork-Dy4Xvuoj.js} +3 -3
  36. package/dist/{usage-DM_I3mPT.js → usage-CQ6GhD5-.js} +3 -3
  37. package/dist/{web-search-provider.runtime-Bx6HAc_k.js → web-search-provider.runtime-CCFhg2z5.js} +3 -3
  38. package/package.json +6 -8
  39. package/npm-shrinkwrap.json +0 -202
  40. package/dist/{auth-binding-C1czuHPA.js → auth-binding-B4FfbcCg.js} +1 -1
@@ -2,704 +2,527 @@ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
2
  import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.js";
3
3
  import path from "node:path";
4
4
  import { resolvePositiveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
5
+ import { AgentHarnessSessionSupersededError, embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
5
6
  import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
7
+ import { ensureAuthProfileStore, resolveDefaultAgentDir, resolveProviderIdForAuth, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
6
8
  import { readFileSync } from "node:fs";
7
9
  import { homedir, hostname } from "node:os";
8
- import { ensureAuthProfileStore, resolveDefaultAgentDir, resolveProviderIdForAuth, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
9
10
  import { resolveExecApprovalsFromFile } from "openclaw/plugin-sdk/exec-approvals-runtime";
10
11
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
11
12
  import { buildSecretInputSchema, normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
12
13
  import { normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
13
14
  import { detectWindowsSpawnCommandInlineArgs } from "openclaw/plugin-sdk/windows-spawn";
14
- import { parse } from "smol-toml";
15
15
  import { z } from "zod";
16
- import { AgentHarnessSessionSupersededError, embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
16
+ import { parse } from "smol-toml";
17
+ import { isLoopbackHost } from "openclaw/plugin-sdk/ssrf-runtime";
17
18
  import { AsyncLocalStorage } from "node:async_hooks";
18
19
  import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
19
- //#region extensions/codex/src/app-server/session-discovery-config.ts
20
- const codexSessionCatalogConfigSchema = z.object({ enabled: z.boolean().optional() }).strict();
21
- const codexDiscoveryConfigSchema = z.object({
22
- enabled: z.boolean().optional(),
23
- timeoutMs: z.number().positive().optional()
24
- }).strict();
20
+ //#region extensions/codex/src/app-server/config-contracts.ts
21
+ const CODEX_PLUGINS_MARKETPLACE_NAME = "openai-curated";
22
+ const CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME = "workspace-directory";
25
23
  //#endregion
26
- //#region extensions/codex/src/app-server/config.ts
24
+ //#region extensions/codex/src/app-server/config-utils.ts
27
25
  const START_OPTIONS_KEY_SECRET_SYMBOL = Symbol.for("openclaw.codexAppServerStartOptionsKeySecret");
28
26
  const START_OPTIONS_KEY_SECRET = getStartOptionsKeySecret();
29
- const UNIX_CODEX_REQUIREMENTS_PATH = "/etc/codex/requirements.toml";
30
- const WINDOWS_CODEX_REQUIREMENTS_SUFFIX = "\\OpenAI\\Codex\\requirements.toml";
31
- const CODEX_APP_SERVER_HOME_DIRNAME = "codex-home";
32
- const CODEX_CONFIG_TOML_FILENAME = "config.toml";
33
27
  const PLAIN_DECIMAL_NUMBER_RE = /^[+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))$/;
34
- const CODEX_PLUGINS_MARKETPLACE_NAME = "openai-curated";
35
- const CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME = "workspace-directory";
36
- function shouldAutoApproveCodexAppServerApprovals(appServer) {
37
- return appServer.networkProxy === void 0 && appServer.approvalPolicy === "never" && appServer.sandbox === "danger-full-access";
28
+ function readRecord(value) {
29
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
38
30
  }
39
- const DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME = "computer-use";
40
- const DEFAULT_CODEX_COMPUTER_USE_MCP_SERVER_NAME = "computer-use";
41
- const DEFAULT_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS = 6e4;
42
- const DEFAULT_CODEX_COMPUTER_USE_LIVE_TEST_TIMEOUT_MS = 6e4;
43
- const DEFAULT_CODEX_COMPUTER_USE_TOOL_CALL_TIMEOUT_MS = 6e4;
44
- const DEFAULT_CODEX_COMPUTER_USE_HEALTH_CHECK_INTERVAL_MINUTES = 60;
45
- const DEFAULT_CODEX_APP_SERVER_NETWORK_PROXY_PROFILE_PREFIX = "openclaw-network";
46
- const codexAppServerTransportSchema = z.enum([
47
- "stdio",
48
- "websocket",
49
- "unix"
50
- ]);
51
- const codexAppServerHomeScopeSchema = z.enum(["agent", "user"]);
52
- const SecretInputSchema = buildSecretInputSchema();
53
- const codexAppServerPolicyModeSchema = z.enum(["yolo", "guardian"]);
54
- const codexAppServerApprovalPolicySchema = z.preprocess((value) => value === "on-failure" ? "on-request" : value, z.enum([
55
- "never",
56
- "on-request",
57
- "untrusted"
58
- ]));
59
- const codexAppServerSandboxSchema = z.enum([
60
- "read-only",
61
- "workspace-write",
62
- "danger-full-access"
63
- ]);
64
- const codexAppServerApprovalsReviewerSchema = z.enum([
65
- "user",
66
- "auto_review",
67
- "guardian_subagent"
68
- ]);
69
- const codexDynamicToolsLoadingSchema = z.enum(["searchable", "direct"]);
70
- const codexComputerUseHealthIntervalSchema = z.union([
71
- z.literal(30),
72
- z.literal(60),
73
- z.literal(120),
74
- z.literal(240)
75
- ]);
76
- const codexComputerUsePluginCacheModeSchema = z.enum(["shared", "independent"]);
77
- const codexPluginDestructivePolicySchema = z.union([
78
- z.boolean(),
79
- z.literal("auto"),
80
- z.literal("ask")
81
- ]);
82
- const codexAppServerServiceTierSchema = z.preprocess((value) => value === null ? null : normalizeCodexServiceTier(value), z.string().trim().min(1).nullable().optional()).optional();
83
- const codexAppServerExperimentalSchema = z.object({ sandboxExecServer: z.boolean().optional() }).strict();
84
- const codexAppServerRemoteWorkspaceRootSchema = z.string().trim().min(1);
85
- const codexAppServerNetworkProxyDomainPermissionSchema = z.enum(["allow", "deny"]);
86
- const codexAppServerNetworkProxyUnixSocketPermissionSchema = z.enum(["allow", "none"]);
87
- const codexAppServerNetworkProxySchema = z.object({
88
- enabled: z.boolean().optional(),
89
- profileName: z.string().trim().min(1).optional(),
90
- baseProfile: z.enum(["read-only", "workspace"]).optional(),
91
- mode: z.enum(["limited", "full"]).optional(),
92
- domains: z.record(z.string(), codexAppServerNetworkProxyDomainPermissionSchema).optional(),
93
- unixSockets: z.record(z.string(), codexAppServerNetworkProxyUnixSocketPermissionSchema).optional(),
94
- proxyUrl: z.string().trim().min(1).optional(),
95
- socksUrl: z.string().trim().min(1).optional(),
96
- enableSocks5: z.boolean().optional(),
97
- enableSocks5Udp: z.boolean().optional(),
98
- allowUpstreamProxy: z.boolean().optional(),
99
- allowLocalBinding: z.boolean().optional(),
100
- dangerouslyAllowNonLoopbackProxy: z.boolean().optional(),
101
- dangerouslyAllowAllUnixSockets: z.boolean().optional()
102
- }).strict();
103
- const codexPluginEntryConfigSchema = z.object({
104
- enabled: z.boolean().optional(),
105
- marketplaceName: z.enum([CODEX_PLUGINS_MARKETPLACE_NAME, CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME]).optional(),
106
- pluginName: z.string().trim().min(1).optional(),
107
- allow_destructive_actions: codexPluginDestructivePolicySchema.optional()
108
- }).strict();
109
- const codexPluginsConfigSchema = z.object({
110
- enabled: z.boolean().optional(),
111
- allow_all_plugins: z.boolean().optional(),
112
- allow_destructive_actions: codexPluginDestructivePolicySchema.optional(),
113
- plugins: z.record(z.string(), codexPluginEntryConfigSchema).optional()
114
- }).strict();
115
- const codexSupervisionEndpointSchema = z.union([z.object({
116
- id: z.string().optional(),
117
- label: z.string().optional(),
118
- transport: z.literal("stdio-proxy").optional(),
119
- command: z.string().optional(),
120
- args: z.array(z.string()).optional(),
121
- cwd: z.string().optional()
122
- }).strict(), z.object({
123
- id: z.string().optional(),
124
- label: z.string().optional(),
125
- transport: z.literal("websocket"),
126
- url: z.string(),
127
- authTokenEnv: z.string().optional()
128
- }).strict()]);
129
- const codexSupervisionConfigSchema = z.object({
130
- enabled: z.boolean().optional(),
131
- endpoints: z.array(codexSupervisionEndpointSchema).optional(),
132
- allowRawTranscripts: z.boolean().optional(),
133
- allowWriteControls: z.boolean().optional()
134
- }).strict();
135
- const codexPluginConfigSchema = z.object({
136
- codexDynamicToolsLoading: codexDynamicToolsLoadingSchema.optional(),
137
- codexDynamicToolsExclude: z.array(z.string()).optional(),
138
- sessionCatalog: codexSessionCatalogConfigSchema.optional(),
139
- discovery: codexDiscoveryConfigSchema.optional(),
140
- computerUse: z.object({
141
- enabled: z.boolean().optional(),
142
- autoInstall: z.boolean().optional(),
143
- marketplaceDiscoveryTimeoutMs: z.number().positive().optional(),
144
- liveTestTimeoutMs: z.number().positive().optional(),
145
- toolCallTimeoutMs: z.number().positive().optional(),
146
- healthCheckEnabled: z.boolean().optional(),
147
- healthCheckIntervalMinutes: codexComputerUseHealthIntervalSchema.optional(),
148
- pluginCacheMode: codexComputerUsePluginCacheModeSchema.optional(),
149
- strictReadiness: z.boolean().optional(),
150
- autoRepair: z.boolean().optional(),
151
- marketplaceSource: z.string().optional(),
152
- marketplacePath: z.string().optional(),
153
- marketplaceName: z.string().optional(),
154
- pluginName: z.string().optional(),
155
- mcpServerName: z.string().optional()
156
- }).strict().optional(),
157
- codexPlugins: z.unknown().optional(),
158
- supervision: codexSupervisionConfigSchema.optional(),
159
- appServer: z.object({
160
- mode: codexAppServerPolicyModeSchema.optional(),
161
- transport: codexAppServerTransportSchema.optional(),
162
- homeScope: codexAppServerHomeScopeSchema.optional(),
163
- command: z.string().optional(),
164
- args: z.union([z.array(z.string()), z.string()]).optional(),
165
- url: z.string().optional(),
166
- authToken: SecretInputSchema.optional(),
167
- headers: z.record(z.string(), SecretInputSchema).optional(),
168
- clearEnv: z.array(z.string()).optional(),
169
- remoteWorkspaceRoot: codexAppServerRemoteWorkspaceRootSchema.optional(),
170
- codeModeOnly: z.boolean().optional(),
171
- loopDetectionPreToolUseRelay: z.boolean().optional(),
172
- requestTimeoutMs: z.number().positive().optional(),
173
- turnCompletionIdleTimeoutMs: z.number().positive().optional(),
174
- turnAssistantCompletionIdleTimeoutMs: z.number().positive().optional(),
175
- postToolRawAssistantCompletionIdleTimeoutMs: z.number().positive().optional(),
176
- approvalPolicy: codexAppServerApprovalPolicySchema.optional(),
177
- sandbox: codexAppServerSandboxSchema.optional(),
178
- approvalsReviewer: codexAppServerApprovalsReviewerSchema.optional(),
179
- serviceTier: codexAppServerServiceTierSchema,
180
- networkProxy: codexAppServerNetworkProxySchema.optional(),
181
- defaultWorkspaceDir: z.string().optional(),
182
- experimental: codexAppServerExperimentalSchema.optional()
183
- }).strict().optional()
184
- }).strict();
185
- function readCodexPluginConfig(value) {
186
- const parsed = codexPluginConfigSchema.safeParse(value);
187
- if (!parsed.success) return {};
188
- const { codexPlugins: rawCodexPlugins, ...config } = parsed.data;
189
- const plugins = codexPluginsConfigSchema.safeParse(rawCodexPlugins);
190
- if (!plugins.success) return config;
191
- return {
192
- ...config,
193
- ...plugins.data ? { codexPlugins: plugins.data } : {}
194
- };
31
+ function normalizeCodexServiceTier(value) {
32
+ if (typeof value !== "string") return;
33
+ const trimmed = value.trim();
34
+ if (!trimmed) return;
35
+ const normalized = trimmed.toLowerCase();
36
+ if (normalized === "fast" || normalized === "priority") return "priority";
37
+ if (normalized === "flex") return "flex";
38
+ return trimmed;
195
39
  }
196
- function isCodexSandboxExecServerEnabled(pluginConfig) {
197
- return readCodexPluginConfig(pluginConfig).appServer?.experimental?.sandboxExecServer === true;
40
+ function isCodexFastServiceTier(value) {
41
+ return normalizeCodexServiceTier(value) === "priority";
198
42
  }
199
- function assertCodexAppServerCommandHasNoInlineArgs(params) {
200
- const inlineArgs = detectWindowsSpawnCommandInlineArgs(params.command);
201
- if (!inlineArgs) return;
202
- const sourceLabel = params.source === "env" ? "OPENCLAW_CODEX_APP_SERVER_BIN" : "plugins.entries.codex.config.appServer.command";
203
- const argsLabel = params.source === "env" ? "OPENCLAW_CODEX_APP_SERVER_ARGS" : "plugins.entries.codex.config.appServer.args";
204
- throw new Error(`${sourceLabel} must be only the Codex app-server executable path; "${inlineArgs.executable}" was configured with inline arguments "${inlineArgs.arguments}". Move those arguments to ${argsLabel}, or remove the override to use the managed Codex startup path.`);
43
+ function normalizePositiveNumber(value, fallback) {
44
+ return resolvePositiveTimerTimeoutMs(value, fallback);
205
45
  }
206
- function resolveCodexPluginsPolicy(pluginConfig) {
207
- const config = readCodexPluginConfig(pluginConfig).codexPlugins;
208
- const configured = config !== void 0;
209
- const enabled = config?.enabled === true;
210
- const destructivePolicy = resolveCodexPluginDestructivePolicy(config?.allow_destructive_actions ?? true);
211
- const pluginPolicies = Object.entries(config?.plugins ?? {}).flatMap(([configKey, entry]) => {
212
- if (!isCodexPluginMarketplaceName(entry.marketplaceName) || !entry.pluginName) return [];
213
- const entryDestructivePolicy = resolveCodexPluginDestructivePolicy(entry.allow_destructive_actions ?? config?.allow_destructive_actions ?? true);
214
- return [{
215
- configKey,
216
- marketplaceName: entry.marketplaceName,
217
- pluginName: entry.pluginName,
218
- enabled: enabled && entry.enabled !== false,
219
- allowDestructiveActions: entryDestructivePolicy.allowDestructiveActions,
220
- destructiveApprovalMode: entryDestructivePolicy.destructiveApprovalMode
221
- }];
222
- }).toSorted((left, right) => left.configKey.localeCompare(right.configKey));
223
- return {
224
- configured,
225
- enabled,
226
- allowAllPlugins: enabled && config?.allow_all_plugins === true,
227
- allowDestructiveActions: destructivePolicy.allowDestructiveActions,
228
- destructiveApprovalMode: destructivePolicy.destructiveApprovalMode,
229
- pluginPolicies
230
- };
46
+ function normalizeHeaders(value) {
47
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
48
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key.trim(), normalizeCodexAppServerSecretInput({
49
+ value: child,
50
+ path: `plugins.entries.codex.config.appServer.headers.${key}`
51
+ })]).filter((entry) => Boolean(entry[0] && entry[1])));
231
52
  }
232
- function isCodexPluginMarketplaceName(value) {
233
- return value === "openai-curated" || value === "workspace-directory";
53
+ function normalizeCodexAppServerSecretInput(params) {
54
+ return normalizeResolvedSecretInputString(params);
234
55
  }
235
- function resolveCodexPluginDestructivePolicy(policy) {
236
- if (policy === "auto" || policy === "ask") return {
237
- allowDestructiveActions: true,
238
- destructiveApprovalMode: policy
239
- };
240
- return {
241
- allowDestructiveActions: policy,
242
- destructiveApprovalMode: policy ? "allow" : "deny"
243
- };
56
+ function normalizeStringList(value) {
57
+ return normalizeTrimmedStringList(value);
244
58
  }
245
- function resolveCodexAppServerRuntimeOptions(params = {}) {
246
- const env = params.env ?? process.env;
247
- const config = readCodexPluginConfig(params.pluginConfig).appServer ?? {};
248
- const transport = resolveTransport(config.transport);
249
- const homeScope = config.homeScope ?? "agent";
250
- const configCommand = readNonEmptyString(config.command);
251
- const envCommand = readNonEmptyString(env.OPENCLAW_CODEX_APP_SERVER_BIN);
252
- const command = configCommand ?? envCommand ?? "codex";
253
- const commandSource = configCommand ? "config" : envCommand ? "env" : "managed";
254
- if (commandSource === "config" || commandSource === "env") assertCodexAppServerCommandHasNoInlineArgs({
255
- command,
256
- source: commandSource
257
- });
258
- const args = resolveArgs(config.args, env.OPENCLAW_CODEX_APP_SERVER_ARGS);
259
- const headers = normalizeHeaders(config.headers);
260
- const clearEnv = normalizeStringList(config.clearEnv);
261
- const authToken = normalizeCodexAppServerSecretInput({
262
- value: config.authToken,
263
- path: "plugins.entries.codex.config.appServer.authToken"
264
- });
265
- const url = readNonEmptyString(config.url) ?? (transport === "unix" ? "unix://" : void 0);
266
- const connectionClass = inferCodexAppServerConnectionClass({
267
- transport,
268
- url
269
- });
270
- const remoteAppsSubstrate = "preconfigured";
271
- const remoteWorkspaceRoot = normalizeRemoteWorkspaceRoot(config.remoteWorkspaceRoot);
272
- const execMode = resolveEffectiveOpenClawExecModeForCodexAppServer({
273
- execMode: params.execMode,
274
- execPolicy: params.execPolicy
275
- });
276
- assertCodexAppServerAllowedForOpenClawExecMode(execMode);
277
- const explicitPolicyMode = resolvePolicyMode(config.mode) ?? resolvePolicyMode(env.OPENCLAW_CODEX_APP_SERVER_MODE);
278
- const configuredSandbox = resolveSandbox(config.sandbox) ?? resolveSandbox(env.OPENCLAW_CODEX_APP_SERVER_SANDBOX);
279
- const explicitApprovalsReviewer = resolveApprovalsReviewer(config.approvalsReviewer);
280
- const normalizedPolicyMode = resolveCodexPolicyModeForOpenClawExecMode(execMode);
281
- const ignoreLegacyYoloPolicyMode = normalizedPolicyMode === "guardian" && explicitPolicyMode === "yolo";
282
- const canUseModelBackedReviewer = canUseCodexModelBackedApprovalsReviewerForModel({
283
- modelProvider: params.modelProvider,
284
- model: params.model,
285
- config: params.config,
286
- env,
287
- agentDir: params.agentDir,
288
- codexConfigToml: params.codexConfigToml,
289
- homeScope
290
- });
291
- const forceUserReviewer = !canUseModelBackedReviewer && (explicitApprovalsReviewer === "auto_review" || explicitApprovalsReviewer === "guardian_subagent" || explicitPolicyMode === "guardian" && explicitApprovalsReviewer !== "user") || execMode !== void 0 && execMode !== "full" && (execMode !== "auto" || !canUseModelBackedReviewer);
292
- const forceGuardianReviewer = execMode === "auto" && canUseModelBackedReviewer;
293
- const execModeRequiringPromptingApprovals = execMode === "auto" || execMode === "ask" ? execMode : forceUserReviewer ? "ask" : void 0;
294
- const forceDangerFullAccessSandbox = params.execPolicy?.touched === true && params.execPolicy.security === "full" && params.execPolicy.ask === "always";
295
- const forceRuntimePolicy = forceUserReviewer || forceGuardianReviewer || forceDangerFullAccessSandbox;
296
- const defaultPolicy = explicitPolicyMode && !forceRuntimePolicy && !ignoreLegacyYoloPolicyMode ? void 0 : resolveDefaultCodexAppServerPolicy({
297
- transport,
298
- env,
299
- forceGuardian: normalizedPolicyMode === "guardian",
300
- forceUserReviewer: forceUserReviewer || !canUseModelBackedReviewer,
301
- execModeRequiringPromptingApprovals,
302
- requirementsToml: params.requirementsToml,
303
- requirementsPath: params.requirementsPath,
304
- readRequirementsFile: params.readRequirementsFile,
305
- platform: params.platform,
306
- hostName: params.hostName,
307
- execModeRequiringUserReviewer: forceUserReviewer ? execMode : void 0
308
- });
309
- const preserveExplicitAutoSandbox = forceGuardianReviewer && configuredSandbox === "read-only";
310
- const forcedPolicy = forceRuntimePolicy ? {
311
- approvalPolicy: defaultPolicy?.approvalPolicy ?? "on-request",
312
- sandbox: preserveExplicitAutoSandbox ? void 0 : forceDangerFullAccessSandbox ? selectForcedDangerFullAccessSandbox({
313
- configuredSandbox,
314
- defaultPolicy,
315
- openClawSandboxActive: Boolean(params.openClawSandboxActive)
316
- }) : selectForcedPromptingSandbox({
317
- configuredSandbox,
318
- defaultSandbox: defaultPolicy?.sandbox
319
- }),
320
- approvalsReviewer: defaultPolicy?.approvalsReviewer ?? (forceUserReviewer ? "user" : "auto_review")
321
- } : void 0;
322
- const policyMode = ignoreLegacyYoloPolicyMode ? normalizedPolicyMode : explicitPolicyMode ?? normalizedPolicyMode ?? defaultPolicy?.mode ?? "yolo";
323
- const serviceTier = normalizeCodexServiceTier(config.serviceTier);
324
- const resolvedSandbox = forcedPolicy?.sandbox ?? configuredSandbox ?? defaultPolicy?.sandbox ?? (policyMode === "guardian" ? "workspace-write" : "danger-full-access");
325
- if (transport === "websocket" && !url) throw new Error("plugins.entries.codex.config.appServer.url is required when appServer.transport is websocket");
326
- if (transport === "websocket" && homeScope === "user") throw new Error("plugins.entries.codex.config.appServer.homeScope=user requires appServer.transport=stdio or unix");
327
- if (transport === "unix" && homeScope !== "user") throw new Error("plugins.entries.codex.config.appServer.transport=unix requires appServer.homeScope=user");
328
- if (transport === "unix" && !url?.startsWith("unix://")) throw new Error("plugins.entries.codex.config.appServer.url must use unix:// when appServer.transport is unix");
329
- assertCodexAppServerConnectionSecurity({
330
- transport,
331
- url,
332
- authToken,
333
- headers
334
- });
335
- const configApprovalPolicy = resolveApprovalPolicy(config.approvalPolicy);
336
- const envApprovalPolicy = resolveApprovalPolicy(env.OPENCLAW_CODEX_APP_SERVER_APPROVAL_POLICY);
337
- const approvalPolicy = configApprovalPolicy ?? envApprovalPolicy ?? defaultPolicy?.approvalPolicy ?? (policyMode === "guardian" ? "on-request" : "never");
338
- const approvalPolicySource = configApprovalPolicy ? "config" : envApprovalPolicy ? "env" : defaultPolicy?.approvalPolicy ? "requirements" : "implicit";
339
- const computerUseConfig = resolveCodexComputerUseConfig({
340
- pluginConfig: params.pluginConfig,
341
- env
342
- });
343
- const managedCommandOrder = params.managedCommandOrder ?? (homeScope === "user" || computerUseConfig.enabled ? "desktop-first" : "package-first");
344
- const includeManagedCommandOrder = commandSource === "managed" && (managedCommandOrder === "desktop-first" || params.managedCommandOrder === "package-first");
345
- const managedComputerUsePluginNames = [.../* @__PURE__ */ new Set([DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME, computerUseConfig.pluginName])];
346
- return {
347
- start: {
348
- transport,
349
- homeScope,
350
- command,
351
- commandSource,
352
- ...includeManagedCommandOrder ? { managedCommandOrder } : {},
353
- ...commandSource === "managed" ? { managedComputerUsePluginNames } : {},
354
- args: args.length > 0 ? args : [
355
- "app-server",
356
- "--listen",
357
- "stdio://"
358
- ],
359
- ...url ? { url } : {},
360
- ...authToken ? { authToken } : {},
361
- headers,
362
- ...transport === "stdio" && clearEnv.length > 0 ? { clearEnv } : {}
363
- },
364
- connectionClass,
365
- remoteAppsSubstrate,
366
- ...remoteWorkspaceRoot ? { remoteWorkspaceRoot } : {},
367
- codeModeOnly: config.codeModeOnly === true,
368
- loopDetectionPreToolUseRelay: config.loopDetectionPreToolUseRelay !== false,
369
- requestTimeoutMs: normalizePositiveNumber(config.requestTimeoutMs, 6e4),
370
- turnCompletionIdleTimeoutMs: normalizePositiveNumber(config.turnCompletionIdleTimeoutMs, 6e4),
371
- turnAssistantCompletionIdleTimeoutMs: normalizePositiveNumber(config.turnAssistantCompletionIdleTimeoutMs, 1e4),
372
- ...config.postToolRawAssistantCompletionIdleTimeoutMs !== void 0 ? { postToolRawAssistantCompletionIdleTimeoutMs: normalizePositiveNumber(config.postToolRawAssistantCompletionIdleTimeoutMs, 6e4) } : {},
373
- approvalPolicy: forcedPolicy?.approvalPolicy ?? approvalPolicy,
374
- approvalPolicySource,
375
- sandbox: resolvedSandbox,
376
- approvalsReviewer: forcedPolicy?.approvalsReviewer ?? explicitApprovalsReviewer ?? defaultPolicy?.approvalsReviewer ?? (policyMode === "guardian" ? "auto_review" : "user"),
377
- ...serviceTier ? { serviceTier } : {},
378
- ...resolveCodexAppServerNetworkProxy(config.networkProxy, resolvedSandbox)
379
- };
59
+ function readBooleanEnv(value) {
60
+ if (value === void 0) return;
61
+ const normalized = value.trim().toLowerCase();
62
+ if ([
63
+ "1",
64
+ "true",
65
+ "yes",
66
+ "on"
67
+ ].includes(normalized)) return true;
68
+ if ([
69
+ "0",
70
+ "false",
71
+ "no",
72
+ "off"
73
+ ].includes(normalized)) return false;
380
74
  }
381
- /**
382
- * Rechecks Codex-owned plugin state at the final spawn boundary, where the
383
- * effective agent home is known, so Computer Use keeps the desktop app's TCC ownership.
384
- */
385
- function resolveCodexAppServerStartOptionsForAgent(params) {
386
- const startOptions = params.startOptions;
387
- if (startOptions.transport !== "stdio" || startOptions.commandSource !== "managed" || startOptions.managedCommandOrder !== void 0) return startOptions;
388
- if (startOptions.homeScope === "user") return {
389
- ...startOptions,
390
- managedCommandOrder: "desktop-first"
391
- };
392
- return codexConfigEnablesNativeComputerUse({
393
- agentDir: params.agentDir,
394
- codexConfigToml: params.codexConfigToml,
395
- env: params.env,
396
- homeScope: "agent",
397
- pluginNames: startOptions.managedComputerUsePluginNames ?? [DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME]
398
- }) ? {
399
- ...startOptions,
400
- managedCommandOrder: "desktop-first"
401
- } : startOptions;
75
+ function readExecSecurity(value) {
76
+ return value === "deny" || value === "allowlist" || value === "full" ? value : void 0;
402
77
  }
403
- function isCodexAppServerApprovalPolicyAllowedByRequirements(policy, params = {}) {
404
- const content = readCodexRequirementsToml(params);
405
- if (content === void 0) return true;
406
- const allowedApprovalPolicies = parseAllowedApprovalPoliciesFromCodexRequirements(content);
407
- return allowedApprovalPolicies === void 0 || allowedApprovalPolicies.has(policy);
78
+ function readExecAsk(value) {
79
+ return value === "off" || value === "on-miss" || value === "always" ? value : void 0;
408
80
  }
409
- function canUseCodexModelBackedApprovalsReviewerForModel(params) {
410
- const explicitProvider = params.modelProvider?.trim().toLowerCase();
411
- const inferredProvider = inferProviderFromModelRef(params.model);
412
- if (explicitProvider && explicitProvider !== "codex") return isTrustedCodexModelBackedApprovalsReviewerProvider(explicitProvider, params) && (inferredProvider === void 0 || isTrustedCodexModelBackedApprovalsReviewerProvider(inferredProvider, params));
413
- if (inferredProvider !== void 0) return isTrustedCodexModelBackedApprovalsReviewerProvider(inferredProvider, params);
414
- return isTrustedCodexModelBackedApprovalsReviewerProvider(explicitProvider, params);
81
+ function readNumberEnv(value) {
82
+ const trimmed = value?.trim();
83
+ if (!trimmed || !PLAIN_DECIMAL_NUMBER_RE.test(trimmed)) return;
84
+ const parsed = Number(trimmed);
85
+ return Number.isFinite(parsed) ? parsed : void 0;
415
86
  }
416
- function isTrustedCodexModelBackedOpenAIProvider(params) {
417
- if (!openAIBaseUrlEnvOverridesAreTrustedForModelBackedReview(params.env)) return false;
418
- const codexBaseUrlOverrides = readCodexBaseUrlOverridesForModelBackedReview(params);
419
- if (codexBaseUrlOverrides === false || !codexBaseUrlOverrides.openAI.every(isNativeOpenAIBaseUrl) || !codexBaseUrlOverrides.chatGPT.every(isNativeChatGPTBaseUrl)) return false;
420
- const openAIProviders = readConfiguredOpenAIProvidersForModelBackedReview(params.config);
421
- if (openAIProviders.length === 0) return true;
422
- return openAIProviders.every((openAIProvider) => configuredOpenAIProviderIsTrustedForModelBackedReview(openAIProvider, params.model));
87
+ function resolveArgs(configArgs, envArgs) {
88
+ if (Array.isArray(configArgs)) return configArgs.map((entry) => readNonEmptyString(entry)).filter((entry) => entry !== void 0);
89
+ if (typeof configArgs === "string") return splitShellWords(configArgs);
90
+ return splitShellWords(envArgs ?? "");
423
91
  }
424
- function resolveCodexModelBackedReviewerPolicyContext(params) {
425
- const provider = params.provider?.trim();
426
- if (provider && provider.toLowerCase() !== "codex") return {
427
- modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(provider),
428
- model: params.model
429
- };
430
- const bindingModelProvider = params.bindingModelProvider?.trim();
431
- const currentModel = params.model?.trim();
432
- const bindingModel = params.bindingModel?.trim();
433
- if (bindingModelProvider && currentModel && bindingModel && currentModel === bindingModel) return {
434
- modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(bindingModelProvider),
435
- model: params.model ?? params.bindingModel
436
- };
437
- const currentModelProvider = inferProviderFromModelRef(params.model);
438
- if (currentModelProvider) return {
439
- modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(currentModelProvider),
440
- model: params.model
441
- };
442
- if (bindingModelProvider) return {
443
- modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(bindingModelProvider),
444
- model: params.model ?? params.bindingModel
445
- };
446
- return {
447
- modelProvider: params.nativeAuthProfile === true ? "openai" : void 0,
448
- model: params.model ?? params.bindingModel
449
- };
92
+ function readNonEmptyString(value) {
93
+ if (typeof value !== "string") return;
94
+ return value.trim() || void 0;
450
95
  }
451
- function resolveCodexComputerUseConfig(params = {}) {
452
- const env = params.env ?? process.env;
453
- const config = readCodexPluginConfig(params.pluginConfig).computerUse ?? {};
454
- const marketplaceSource = readNonEmptyString(params.overrides?.marketplaceSource) ?? readNonEmptyString(config.marketplaceSource) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_SOURCE);
455
- const marketplacePath = readNonEmptyString(params.overrides?.marketplacePath) ?? readNonEmptyString(config.marketplacePath) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_PATH);
456
- const marketplaceName = readNonEmptyString(params.overrides?.marketplaceName) ?? readNonEmptyString(config.marketplaceName) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_NAME);
457
- const configuredPluginName = readNonEmptyString(params.overrides?.pluginName) ?? readNonEmptyString(config.pluginName) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_PLUGIN_NAME);
458
- const configuredMcpServerName = readNonEmptyString(params.overrides?.mcpServerName) ?? readNonEmptyString(config.mcpServerName) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MCP_SERVER_NAME);
459
- const autoInstall = params.overrides?.autoInstall ?? config.autoInstall ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_AUTO_INSTALL) ?? false;
460
- const marketplaceDiscoveryTimeoutMs = normalizePositiveNumber(params.overrides?.marketplaceDiscoveryTimeoutMs ?? config.marketplaceDiscoveryTimeoutMs ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS), DEFAULT_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS);
461
- const liveTestTimeoutMs = normalizePositiveNumber(params.overrides?.liveTestTimeoutMs ?? config.liveTestTimeoutMs ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_LIVE_TEST_TIMEOUT_MS), DEFAULT_CODEX_COMPUTER_USE_LIVE_TEST_TIMEOUT_MS);
462
- const toolCallTimeoutMs = normalizePositiveNumber(params.overrides?.toolCallTimeoutMs ?? config.toolCallTimeoutMs ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_TOOL_CALL_TIMEOUT_MS), DEFAULT_CODEX_COMPUTER_USE_TOOL_CALL_TIMEOUT_MS);
463
- const healthCheckIntervalMinutes = normalizeComputerUseHealthCheckIntervalMinutes(params.overrides?.healthCheckIntervalMinutes ?? config.healthCheckIntervalMinutes ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_HEALTH_CHECK_INTERVAL_MINUTES));
464
- const healthCheckEnabled = params.overrides?.healthCheckEnabled ?? config.healthCheckEnabled ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_HEALTH_CHECK_ENABLED) ?? false;
465
- const pluginCacheMode = normalizeComputerUsePluginCacheMode(params.overrides?.pluginCacheMode) ?? normalizeComputerUsePluginCacheMode(config.pluginCacheMode) ?? normalizeComputerUsePluginCacheMode(env.OPENCLAW_CODEX_COMPUTER_USE_PLUGIN_CACHE_MODE) ?? "independent";
466
- const strictReadiness = params.overrides?.strictReadiness ?? config.strictReadiness ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_STRICT_READINESS) ?? false;
467
- const autoRepair = params.overrides?.autoRepair ?? config.autoRepair ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_AUTO_REPAIR) ?? false;
468
- return {
469
- enabled: params.overrides?.enabled ?? config.enabled ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE) ?? Boolean(autoInstall || marketplaceSource || marketplacePath || marketplaceName || configuredPluginName || configuredMcpServerName),
470
- autoInstall,
471
- marketplaceDiscoveryTimeoutMs,
472
- liveTestTimeoutMs,
473
- toolCallTimeoutMs,
474
- healthCheckEnabled,
475
- healthCheckIntervalMinutes,
476
- pluginCacheMode,
477
- strictReadiness,
478
- autoRepair,
479
- pluginName: configuredPluginName ?? DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME,
480
- mcpServerName: configuredMcpServerName ?? DEFAULT_CODEX_COMPUTER_USE_MCP_SERVER_NAME,
481
- ...marketplaceSource ? { marketplaceSource } : {},
482
- ...marketplacePath ? { marketplacePath } : {},
483
- ...marketplaceName ? { marketplaceName } : {}
484
- };
96
+ function hashSecretForKey(value, label) {
97
+ if (!value) return null;
98
+ return createHmac("sha256", START_OPTIONS_KEY_SECRET).update(label).update("\0").update(value).digest("hex");
485
99
  }
486
- function normalizeComputerUseHealthCheckIntervalMinutes(value) {
487
- return value === 30 || value === 60 || value === 120 || value === 240 ? value : DEFAULT_CODEX_COMPUTER_USE_HEALTH_CHECK_INTERVAL_MINUTES;
100
+ function getStartOptionsKeySecret() {
101
+ const globalState = globalThis;
102
+ globalState[START_OPTIONS_KEY_SECRET_SYMBOL] ??= randomBytes(32);
103
+ return globalState[START_OPTIONS_KEY_SECRET_SYMBOL];
488
104
  }
489
- function normalizeComputerUsePluginCacheMode(value) {
490
- return value === "shared" || value === "independent" ? value : null;
105
+ function splitShellWords(value) {
106
+ const words = [];
107
+ let current = "";
108
+ let quote = null;
109
+ for (const char of value) {
110
+ if (quote) {
111
+ if (char === quote) quote = null;
112
+ else current += char;
113
+ continue;
114
+ }
115
+ if (char === "\"" || char === "'") {
116
+ quote = char;
117
+ continue;
118
+ }
119
+ if (/\s/.test(char)) {
120
+ if (current) {
121
+ words.push(current);
122
+ current = "";
123
+ }
124
+ continue;
125
+ }
126
+ current += char;
127
+ }
128
+ if (current) words.push(current);
129
+ return words;
491
130
  }
492
- function codexAppServerStartOptionsKey(options, params = {}) {
493
- return JSON.stringify({
494
- transport: options.transport,
495
- command: options.command,
496
- commandSource: options.commandSource ?? null,
497
- managedCommandOrder: options.managedCommandOrder ?? "package-first",
498
- managedComputerUsePluginNames: [...options.managedComputerUsePluginNames ?? []].toSorted(),
499
- managedFallbackCommandPaths: [...options.managedFallbackCommandPaths ?? []],
500
- args: options.args,
501
- cwd: options.cwd ?? null,
502
- url: options.url ?? null,
503
- authToken: hashSecretForKey(options.authToken, "authToken"),
504
- headers: Object.entries(options.headers).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, hashSecretForKey(value, `header:${key}`)]),
505
- env: Object.entries(options.env ?? {}).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, hashSecretForKey(value, `env:${key}`)]),
506
- clearEnv: [...options.clearEnv ?? []].toSorted(),
507
- authProfileId: params.authProfileId ?? null,
508
- authBindingFingerprint: params.authBindingFingerprint ?? null,
509
- agentDir: params.agentDir ?? null,
510
- fallbackApiKeyCacheKey: params.fallbackApiKeyCacheKey ?? null
511
- });
131
+ //#endregion
132
+ //#region extensions/codex/src/app-server/config-exec-policy.ts
133
+ function selectForcedPromptingSandbox(params) {
134
+ if (params.configuredSandbox === "read-only" || params.defaultSandbox === "read-only") return "read-only";
135
+ return params.defaultSandbox ?? "workspace-write";
512
136
  }
513
- function codexSandboxPolicyForTurn(mode, cwd) {
514
- if (mode === "danger-full-access") return { type: "dangerFullAccess" };
515
- if (mode === "read-only") return {
516
- type: "readOnly",
517
- networkAccess: false
518
- };
137
+ function selectForcedDangerFullAccessSandbox(params) {
138
+ if (params.configuredSandbox === "read-only") return "read-only";
139
+ if (params.defaultPolicy?.dangerFullAccessAllowed === false) {
140
+ if (params.openClawSandboxActive) return params.defaultPolicy.sandbox ?? "workspace-write";
141
+ throw new Error("legacy full exec security with ask requires Codex app-server danger-full-access");
142
+ }
143
+ return "danger-full-access";
144
+ }
145
+ function selectGuardianSandbox(allowedSandboxModes) {
146
+ if (allowedSandboxModes === void 0 || allowedSandboxModes.has("workspace-write")) return "workspace-write";
147
+ if (allowedSandboxModes.has("read-only")) return "read-only";
148
+ if (allowedSandboxModes.has("danger-full-access")) return "danger-full-access";
149
+ return "workspace-write";
150
+ }
151
+ function resolveApprovalPolicy(value) {
152
+ if (value === "on-failure") return "on-request";
153
+ return value === "on-request" || value === "untrusted" || value === "never" ? value : void 0;
154
+ }
155
+ function resolveSandbox(value) {
156
+ return value === "read-only" || value === "workspace-write" || value === "danger-full-access" ? value : void 0;
157
+ }
158
+ function resolveApprovalsReviewer(value) {
159
+ return value === "auto_review" || value === "guardian_subagent" || value === "user" ? value : void 0;
160
+ }
161
+ function resolveOpenClawExecPolicyFromConfig(params) {
162
+ const root = readRecord(params.config);
163
+ const globalExec = readRecord(readRecord(root?.tools)?.exec);
164
+ const globalPolicy = applyOpenClawExecPolicyLayer(createDefaultOpenClawExecPolicy(), globalExec);
165
+ const agentId = params.agentId?.trim();
166
+ if (!agentId) return globalPolicy;
167
+ const agents = readRecord(root?.agents);
168
+ const agentList = Array.isArray(agents?.list) ? agents.list : [];
169
+ const normalizedAgentId = normalizeAgentId(agentId);
170
+ return applyOpenClawExecPolicyLayer(globalPolicy, readRecord(readRecord(readRecord(agentList.find((entry) => {
171
+ const id = readRecord(entry)?.id;
172
+ return typeof id === "string" && normalizeAgentId(id) === normalizedAgentId;
173
+ }))?.tools)?.exec));
174
+ }
175
+ function resolveOpenClawExecPolicyForCodexAppServer(params) {
176
+ const overridePolicy = applyOpenClawExecPolicyLayer(resolveOpenClawExecPolicyFromConfig({
177
+ config: params.config,
178
+ agentId: params.agentId
179
+ }), params.execOverrides);
180
+ return applyOpenClawExecApprovalFloors(overridePolicy, resolveOpenClawExecApprovalFloorsForCodexAppServer({
181
+ approvals: params.approvals,
182
+ agentId: params.agentId,
183
+ policy: overridePolicy
184
+ }));
185
+ }
186
+ function resolveEffectiveOpenClawExecModeForCodexAppServer(params) {
187
+ if (params.execPolicy?.touched === true) return params.execPolicy.mode;
188
+ return params.execMode;
189
+ }
190
+ function resolveCodexPolicyModeForOpenClawExecMode(mode) {
191
+ if (!mode || mode === "full") return;
192
+ return "guardian";
193
+ }
194
+ function assertCodexAppServerAllowedForOpenClawExecMode(mode) {
195
+ if (mode === "deny" || mode === "allowlist") throw new Error(`Codex app-server local execution is not available when tools.exec.mode=${mode}`);
196
+ }
197
+ function createDefaultOpenClawExecPolicy() {
519
198
  return {
520
- type: "workspaceWrite",
521
- writableRoots: [cwd],
522
- networkAccess: false,
523
- excludeTmpdirEnvVar: false,
524
- excludeSlashTmp: false
199
+ security: "full",
200
+ ask: "off",
201
+ touched: false
525
202
  };
526
203
  }
527
- /** Resolves the passive supervision control connection without changing harness defaults. */
528
- function resolveCodexSupervisionAppServerRuntimeOptions(params = {}) {
529
- const pluginConfig = readCodexPluginConfig(params.pluginConfig);
530
- const appServer = pluginConfig.appServer ?? {};
531
- const transport = resolveTransport(appServer.transport);
532
- const homeScope = appServer.homeScope ?? (transport === "websocket" ? "agent" : "user");
533
- return resolveCodexAppServerRuntimeOptions({
534
- ...params,
535
- pluginConfig: {
536
- ...pluginConfig,
537
- appServer: {
538
- ...appServer,
539
- homeScope
540
- }
541
- }
542
- });
543
- }
544
- function resolveCodexAppServerNetworkProxy(config, sandbox) {
545
- if (config?.enabled !== true) return {};
546
- const fileSystemMode = config.baseProfile === "read-only" || !config.baseProfile && sandbox === "read-only" ? "read" : "write";
547
- const networkConfig = removeUndefinedJsonFields({
548
- enabled: true,
549
- mode: config.mode,
550
- domains: normalizeNetworkProxyPermissionMap(config.domains),
551
- unix_sockets: normalizeNetworkProxyPermissionMap(config.unixSockets),
552
- proxy_url: readNonEmptyString(config.proxyUrl),
553
- socks_url: readNonEmptyString(config.socksUrl),
554
- enable_socks5: config.enableSocks5,
555
- enable_socks5_udp: config.enableSocks5Udp,
556
- allow_upstream_proxy: config.allowUpstreamProxy,
557
- allow_local_binding: config.allowLocalBinding,
558
- dangerously_allow_non_loopback_proxy: config.dangerouslyAllowNonLoopbackProxy,
559
- dangerously_allow_all_unix_sockets: config.dangerouslyAllowAllUnixSockets
560
- });
561
- const profile = {
562
- filesystem: {
563
- ":minimal": "read",
564
- ":project_roots": { ".": fileSystemMode }
565
- },
566
- network: networkConfig
204
+ function applyOpenClawExecPolicyLayer(base, exec) {
205
+ if (!exec) return base;
206
+ const mode = readExecMode(exec.mode);
207
+ if (mode !== void 0) return {
208
+ ...resolveOpenClawExecPolicyForMode(mode),
209
+ touched: true
567
210
  };
568
- const profileName = resolveNetworkProxyPermissionProfileName(config, profile);
569
- const configPatch = {
570
- "features.network_proxy.enabled": true,
571
- default_permissions: profileName,
572
- permissions: { [profileName]: profile }
211
+ const security = readExecSecurity(exec.security);
212
+ const ask = readExecAsk(exec.ask);
213
+ if (security === void 0 && ask === void 0) return base;
214
+ const nextSecurity = security ?? base.security;
215
+ const nextAsk = ask ?? base.ask;
216
+ return {
217
+ mode: resolveOpenClawExecModeFromPolicy({
218
+ security: nextSecurity,
219
+ ask: nextAsk
220
+ }),
221
+ security: nextSecurity,
222
+ ask: nextAsk,
223
+ touched: true
573
224
  };
574
- return { networkProxy: {
575
- profileName,
576
- configFingerprint: fingerprintCodexAppServerNetworkProxyConfigPatch(configPatch),
577
- configPatch
578
- } };
579
- }
580
- function resolveNetworkProxyPermissionProfileName(config, profile) {
581
- const explicitProfileName = readNonEmptyString(config.profileName);
582
- if (explicitProfileName) return explicitProfileName;
583
- const suffix = createHash("sha256").update(stableStringifyJson({
584
- version: 1,
585
- profile
586
- })).digest("hex").slice(0, 16);
587
- return `${DEFAULT_CODEX_APP_SERVER_NETWORK_PROXY_PROFILE_PREFIX}-${suffix}`;
588
- }
589
- function fingerprintCodexAppServerNetworkProxyConfigPatch(configPatch) {
590
- return createHash("sha256").update(stableStringifyJson(configPatch)).digest("hex");
591
225
  }
592
- function normalizeNetworkProxyPermissionMap(value) {
593
- const entries = Object.entries(value ?? {}).map(([key, permission]) => [key.trim(), permission]).filter(([key]) => key.length > 0);
594
- return entries.length > 0 ? Object.fromEntries(entries) : void 0;
226
+ function resolveOpenClawExecApprovalFloorsForCodexAppServer(params) {
227
+ if (!params.approvals) return;
228
+ return resolveExecApprovalsFromFile({
229
+ file: params.approvals,
230
+ agentId: params.agentId,
231
+ overrides: {
232
+ security: params.policy.security,
233
+ ask: params.policy.ask
234
+ }
235
+ }).agent;
595
236
  }
596
- function removeUndefinedJsonFields(value) {
597
- return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
237
+ function applyOpenClawExecApprovalFloors(base, approvalFloors) {
238
+ if (!approvalFloors) return base;
239
+ const nextSecurity = approvalFloors.security ? minOpenClawExecSecurity(base.security, approvalFloors.security) : base.security;
240
+ const nextAsk = approvalFloors.ask ? maxOpenClawExecAsk(base.ask, approvalFloors.ask) : base.ask;
241
+ if (nextSecurity === base.security && nextAsk === base.ask) return base;
242
+ return {
243
+ mode: resolveOpenClawExecModeFromPolicy({
244
+ security: nextSecurity,
245
+ ask: nextAsk
246
+ }),
247
+ security: nextSecurity,
248
+ ask: nextAsk,
249
+ touched: true
250
+ };
598
251
  }
599
- function stableStringifyJson(value) {
600
- if (Array.isArray(value)) return `[${value.map((item) => stableStringifyJson(item)).join(",")}]`;
601
- if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableStringifyJson(item)}`).join(",")}}`;
602
- return JSON.stringify(value);
252
+ function resolveOpenClawExecPolicyForMode(mode) {
253
+ switch (mode) {
254
+ case "deny": return {
255
+ mode,
256
+ security: "deny",
257
+ ask: "off"
258
+ };
259
+ case "allowlist": return {
260
+ mode,
261
+ security: "allowlist",
262
+ ask: "off"
263
+ };
264
+ case "ask":
265
+ case "auto": return {
266
+ mode,
267
+ security: "allowlist",
268
+ ask: "on-miss"
269
+ };
270
+ case "full": return {
271
+ mode,
272
+ security: "full",
273
+ ask: "off"
274
+ };
275
+ }
276
+ return mode;
603
277
  }
604
- function withMcpElicitationsApprovalPolicy(policy) {
605
- if (typeof policy !== "string") return { granular: {
606
- ...policy.granular,
607
- mcp_elicitations: true
608
- } };
609
- if (policy === "never") return { granular: {
610
- mcp_elicitations: true,
611
- rules: false,
612
- sandbox_approval: false,
613
- request_permissions: false,
614
- skill_approval: false
615
- } };
616
- return { granular: {
617
- mcp_elicitations: true,
618
- rules: true,
619
- sandbox_approval: true,
620
- request_permissions: true,
621
- skill_approval: true
622
- } };
278
+ function resolveOpenClawExecModeFromPolicy(params) {
279
+ if (params.security === "deny") return "deny";
280
+ if (params.security === "allowlist" && params.ask === "off") return "allowlist";
281
+ if (params.security === "full" && params.ask !== "always") return "full";
282
+ return "ask";
623
283
  }
624
- function resolveTransport(value) {
625
- return value === "websocket" || value === "unix" ? value : "stdio";
284
+ function minOpenClawExecSecurity(left, right) {
285
+ const order = {
286
+ deny: 0,
287
+ allowlist: 1,
288
+ full: 2
289
+ };
290
+ return order[left] <= order[right] ? left : right;
626
291
  }
627
- function normalizeRemoteWorkspaceRoot(value) {
628
- return readNonEmptyString(value);
292
+ function maxOpenClawExecAsk(left, right) {
293
+ const order = {
294
+ off: 0,
295
+ "on-miss": 1,
296
+ always: 2
297
+ };
298
+ return order[left] >= order[right] ? left : right;
629
299
  }
630
- function inferCodexAppServerConnectionClass(params) {
631
- if (params.transport !== "websocket") return "local-loopback";
632
- return params.url && isLoopbackWebSocketUrl(params.url) ? "local-loopback" : "remote";
300
+ function readExecMode(value) {
301
+ return value === "deny" || value === "allowlist" || value === "ask" || value === "auto" || value === "full" ? value : void 0;
633
302
  }
634
- function assertCodexAppServerConnectionClassConfig(params) {
635
- if (params.connectionClass === "remote" && !hasIdentityBearingWebSocketAuth({
636
- authToken: params.authToken,
637
- headers: params.headers
638
- })) throw new Error("remote Codex app-server WebSocket URLs require appServer.authToken or an Authorization header");
303
+ //#endregion
304
+ //#region extensions/codex/src/app-server/session-discovery-config.ts
305
+ const codexSessionCatalogConfigSchema = z.object({ enabled: z.boolean().optional() }).strict();
306
+ const codexDiscoveryConfigSchema = z.object({
307
+ enabled: z.boolean().optional(),
308
+ timeoutMs: z.number().positive().optional()
309
+ }).strict();
310
+ //#endregion
311
+ //#region extensions/codex/src/app-server/config-parsing.ts
312
+ const DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME = "computer-use";
313
+ const DEFAULT_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS = 6e4;
314
+ const DEFAULT_CODEX_COMPUTER_USE_LIVE_TEST_TIMEOUT_MS = 6e4;
315
+ const DEFAULT_CODEX_COMPUTER_USE_TOOL_CALL_TIMEOUT_MS = 6e4;
316
+ const DEFAULT_CODEX_APP_SERVER_NETWORK_PROXY_PROFILE_PREFIX = "openclaw-network";
317
+ const codexAppServerTransportSchema = z.enum([
318
+ "stdio",
319
+ "websocket",
320
+ "unix"
321
+ ]);
322
+ const codexAppServerHomeScopeSchema = z.enum(["agent", "user"]);
323
+ const SecretInputSchema = buildSecretInputSchema();
324
+ const codexAppServerPolicyModeSchema = z.enum(["yolo", "guardian"]);
325
+ const codexAppServerApprovalPolicySchema = z.preprocess((value) => value === "on-failure" ? "on-request" : value, z.enum([
326
+ "never",
327
+ "on-request",
328
+ "untrusted"
329
+ ]));
330
+ const codexAppServerSandboxSchema = z.enum([
331
+ "read-only",
332
+ "workspace-write",
333
+ "danger-full-access"
334
+ ]);
335
+ const codexAppServerApprovalsReviewerSchema = z.enum([
336
+ "user",
337
+ "auto_review",
338
+ "guardian_subagent"
339
+ ]);
340
+ const codexDynamicToolsLoadingSchema = z.enum(["searchable", "direct"]);
341
+ const codexComputerUseHealthIntervalSchema = z.union([
342
+ z.literal(30),
343
+ z.literal(60),
344
+ z.literal(120),
345
+ z.literal(240)
346
+ ]);
347
+ const codexComputerUsePluginCacheModeSchema = z.enum(["shared", "independent"]);
348
+ const codexPluginDestructivePolicySchema = z.union([
349
+ z.boolean(),
350
+ z.literal("auto"),
351
+ z.literal("ask")
352
+ ]);
353
+ const codexAppServerServiceTierSchema = z.preprocess((value) => value === null ? null : normalizeCodexServiceTier(value), z.string().trim().min(1).nullable().optional()).optional();
354
+ const codexAppServerExperimentalSchema = z.object({ sandboxExecServer: z.boolean().optional() }).strict();
355
+ const codexAppServerRemoteWorkspaceRootSchema = z.string().trim().min(1);
356
+ const codexAppServerNetworkProxyDomainPermissionSchema = z.enum(["allow", "deny"]);
357
+ const codexAppServerNetworkProxyUnixSocketPermissionSchema = z.enum(["allow", "none"]);
358
+ const codexAppServerNetworkProxySchema = z.object({
359
+ enabled: z.boolean().optional(),
360
+ profileName: z.string().trim().min(1).optional(),
361
+ baseProfile: z.enum(["read-only", "workspace"]).optional(),
362
+ mode: z.enum(["limited", "full"]).optional(),
363
+ domains: z.record(z.string(), codexAppServerNetworkProxyDomainPermissionSchema).optional(),
364
+ unixSockets: z.record(z.string(), codexAppServerNetworkProxyUnixSocketPermissionSchema).optional(),
365
+ proxyUrl: z.string().trim().min(1).optional(),
366
+ socksUrl: z.string().trim().min(1).optional(),
367
+ enableSocks5: z.boolean().optional(),
368
+ enableSocks5Udp: z.boolean().optional(),
369
+ allowUpstreamProxy: z.boolean().optional(),
370
+ allowLocalBinding: z.boolean().optional(),
371
+ dangerouslyAllowNonLoopbackProxy: z.boolean().optional(),
372
+ dangerouslyAllowAllUnixSockets: z.boolean().optional()
373
+ }).strict();
374
+ const codexPluginEntryConfigSchema = z.object({
375
+ enabled: z.boolean().optional(),
376
+ marketplaceName: z.enum([CODEX_PLUGINS_MARKETPLACE_NAME, CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME]).optional(),
377
+ pluginName: z.string().trim().min(1).optional(),
378
+ allow_destructive_actions: codexPluginDestructivePolicySchema.optional()
379
+ }).strict();
380
+ const codexPluginsConfigSchema = z.object({
381
+ enabled: z.boolean().optional(),
382
+ allow_all_plugins: z.boolean().optional(),
383
+ allow_destructive_actions: codexPluginDestructivePolicySchema.optional(),
384
+ plugins: z.record(z.string(), codexPluginEntryConfigSchema).optional()
385
+ }).strict();
386
+ const codexSupervisionEndpointSchema = z.union([z.object({
387
+ id: z.string().optional(),
388
+ label: z.string().optional(),
389
+ transport: z.literal("stdio-proxy").optional(),
390
+ command: z.string().optional(),
391
+ args: z.array(z.string()).optional(),
392
+ cwd: z.string().optional()
393
+ }).strict(), z.object({
394
+ id: z.string().optional(),
395
+ label: z.string().optional(),
396
+ transport: z.literal("websocket"),
397
+ url: z.string(),
398
+ authTokenEnv: z.string().optional()
399
+ }).strict()]);
400
+ const codexSupervisionConfigSchema = z.object({
401
+ enabled: z.boolean().optional(),
402
+ endpoints: z.array(codexSupervisionEndpointSchema).optional(),
403
+ allowRawTranscripts: z.boolean().optional(),
404
+ allowWriteControls: z.boolean().optional()
405
+ }).strict();
406
+ const codexPluginConfigSchema = z.object({
407
+ codexDynamicToolsLoading: codexDynamicToolsLoadingSchema.optional(),
408
+ codexDynamicToolsExclude: z.array(z.string()).optional(),
409
+ sessionCatalog: codexSessionCatalogConfigSchema.optional(),
410
+ discovery: codexDiscoveryConfigSchema.optional(),
411
+ computerUse: z.object({
412
+ enabled: z.boolean().optional(),
413
+ autoInstall: z.boolean().optional(),
414
+ marketplaceDiscoveryTimeoutMs: z.number().positive().optional(),
415
+ liveTestTimeoutMs: z.number().positive().optional(),
416
+ toolCallTimeoutMs: z.number().positive().optional(),
417
+ healthCheckEnabled: z.boolean().optional(),
418
+ healthCheckIntervalMinutes: codexComputerUseHealthIntervalSchema.optional(),
419
+ pluginCacheMode: codexComputerUsePluginCacheModeSchema.optional(),
420
+ strictReadiness: z.boolean().optional(),
421
+ autoRepair: z.boolean().optional(),
422
+ marketplaceSource: z.string().optional(),
423
+ marketplacePath: z.string().optional(),
424
+ marketplaceName: z.string().optional(),
425
+ pluginName: z.string().optional(),
426
+ mcpServerName: z.string().optional()
427
+ }).strict().optional(),
428
+ codexPlugins: z.unknown().optional(),
429
+ supervision: codexSupervisionConfigSchema.optional(),
430
+ appServer: z.object({
431
+ mode: codexAppServerPolicyModeSchema.optional(),
432
+ transport: codexAppServerTransportSchema.optional(),
433
+ homeScope: codexAppServerHomeScopeSchema.optional(),
434
+ command: z.string().optional(),
435
+ args: z.union([z.array(z.string()), z.string()]).optional(),
436
+ url: z.string().optional(),
437
+ authToken: SecretInputSchema.optional(),
438
+ headers: z.record(z.string(), SecretInputSchema).optional(),
439
+ clearEnv: z.array(z.string()).optional(),
440
+ remoteWorkspaceRoot: codexAppServerRemoteWorkspaceRootSchema.optional(),
441
+ codeModeOnly: z.boolean().optional(),
442
+ loopDetectionPreToolUseRelay: z.boolean().optional(),
443
+ requestTimeoutMs: z.number().positive().optional(),
444
+ turnCompletionIdleTimeoutMs: z.number().positive().optional(),
445
+ turnAssistantCompletionIdleTimeoutMs: z.number().positive().optional(),
446
+ postToolRawAssistantCompletionIdleTimeoutMs: z.number().positive().optional(),
447
+ approvalPolicy: codexAppServerApprovalPolicySchema.optional(),
448
+ sandbox: codexAppServerSandboxSchema.optional(),
449
+ approvalsReviewer: codexAppServerApprovalsReviewerSchema.optional(),
450
+ serviceTier: codexAppServerServiceTierSchema,
451
+ networkProxy: codexAppServerNetworkProxySchema.optional(),
452
+ defaultWorkspaceDir: z.string().optional(),
453
+ experimental: codexAppServerExperimentalSchema.optional()
454
+ }).strict().optional()
455
+ }).strict();
456
+ function readCodexPluginConfig(value) {
457
+ const parsed = codexPluginConfigSchema.safeParse(value);
458
+ if (!parsed.success) return {};
459
+ const { codexPlugins: rawCodexPlugins, ...config } = parsed.data;
460
+ const plugins = codexPluginsConfigSchema.safeParse(rawCodexPlugins);
461
+ if (!plugins.success) return config;
462
+ return {
463
+ ...config,
464
+ ...plugins.data ? { codexPlugins: plugins.data } : {}
465
+ };
639
466
  }
640
- /** Applies the canonical remote-auth boundary to any Codex AppServer transport. */
641
- function assertCodexAppServerConnectionSecurity(params) {
642
- assertCodexAppServerConnectionClassConfig({
643
- connectionClass: inferCodexAppServerConnectionClass(params),
644
- authToken: params.authToken,
645
- headers: params.headers
646
- });
467
+ function isCodexSandboxExecServerEnabled(pluginConfig) {
468
+ return readCodexPluginConfig(pluginConfig).appServer?.experimental?.sandboxExecServer === true;
647
469
  }
648
- function isLoopbackWebSocketUrl(value) {
649
- let parsed;
650
- try {
651
- parsed = new URL(value);
652
- } catch {
653
- return false;
654
- }
655
- if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") return false;
656
- const host = parsed.hostname.toLowerCase();
657
- return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.startsWith("127.");
470
+ function assertCodexAppServerCommandHasNoInlineArgs(params) {
471
+ const inlineArgs = detectWindowsSpawnCommandInlineArgs(params.command);
472
+ if (!inlineArgs) return;
473
+ const sourceLabel = params.source === "env" ? "OPENCLAW_CODEX_APP_SERVER_BIN" : "plugins.entries.codex.config.appServer.command";
474
+ const argsLabel = params.source === "env" ? "OPENCLAW_CODEX_APP_SERVER_ARGS" : "plugins.entries.codex.config.appServer.args";
475
+ throw new Error(`${sourceLabel} must be only the Codex app-server executable path; "${inlineArgs.executable}" was configured with inline arguments "${inlineArgs.arguments}". Move those arguments to ${argsLabel}, or remove the override to use the managed Codex startup path.`);
658
476
  }
659
- function hasIdentityBearingWebSocketAuth(params) {
660
- if (readNonEmptyString(params.authToken)) return true;
661
- return Object.entries(params.headers).some(([key, value]) => key.trim().toLowerCase() === "authorization" && Boolean(readNonEmptyString(value)));
477
+ function resolveCodexPluginsPolicy(pluginConfig) {
478
+ const config = readCodexPluginConfig(pluginConfig).codexPlugins;
479
+ const configured = config !== void 0;
480
+ const enabled = config?.enabled === true;
481
+ const destructivePolicy = resolveCodexPluginDestructivePolicy(config?.allow_destructive_actions ?? true);
482
+ const pluginPolicies = Object.entries(config?.plugins ?? {}).flatMap(([configKey, entry]) => {
483
+ if (!isCodexPluginMarketplaceName(entry.marketplaceName) || !entry.pluginName) return [];
484
+ const entryDestructivePolicy = resolveCodexPluginDestructivePolicy(entry.allow_destructive_actions ?? config?.allow_destructive_actions ?? true);
485
+ return [{
486
+ configKey,
487
+ marketplaceName: entry.marketplaceName,
488
+ pluginName: entry.pluginName,
489
+ enabled: enabled && entry.enabled !== false,
490
+ allowDestructiveActions: entryDestructivePolicy.allowDestructiveActions,
491
+ destructiveApprovalMode: entryDestructivePolicy.destructiveApprovalMode
492
+ }];
493
+ }).toSorted((left, right) => left.configKey.localeCompare(right.configKey));
494
+ return {
495
+ configured,
496
+ enabled,
497
+ allowAllPlugins: enabled && config?.allow_all_plugins === true,
498
+ allowDestructiveActions: destructivePolicy.allowDestructiveActions,
499
+ destructiveApprovalMode: destructivePolicy.destructiveApprovalMode,
500
+ pluginPolicies
501
+ };
662
502
  }
663
- function resolvePolicyMode(value) {
664
- return value === "guardian" || value === "yolo" ? value : void 0;
503
+ function isCodexPluginMarketplaceName(value) {
504
+ return value === "openai-curated" || value === "workspace-directory";
665
505
  }
666
- function resolveDefaultCodexAppServerPolicy(params) {
667
- if (params.transport !== "stdio") return {
668
- mode: "yolo",
669
- dangerFullAccessAllowed: true
670
- };
671
- const content = readCodexRequirementsToml(params);
672
- if (content === void 0) {
673
- if (!params.forceGuardian) return {
674
- mode: "yolo",
675
- dangerFullAccessAllowed: true
676
- };
677
- return {
678
- mode: "guardian",
679
- dangerFullAccessAllowed: true,
680
- approvalPolicy: selectGuardianApprovalPolicy(void 0, params.execModeRequiringPromptingApprovals),
681
- approvalsReviewer: params.forceUserReviewer ? selectUserApprovalsReviewer(void 0, params.execModeRequiringUserReviewer) : selectGuardianApprovalsReviewer(void 0, params.execModeRequiringPromptingApprovals === "auto" ? "auto" : void 0),
682
- sandbox: selectGuardianSandbox(void 0)
683
- };
684
- }
685
- const allowedSandboxModes = parseAllowedSandboxModesFromCodexRequirements(content, readNonEmptyString(params.hostName) ?? hostname());
686
- const allowedApprovalPolicies = parseAllowedApprovalPoliciesFromCodexRequirements(content);
687
- const allowedApprovalsReviewers = parseAllowedApprovalsReviewersFromCodexRequirements(content);
688
- const yoloSandboxAllowed = allowedSandboxModes === void 0 || allowedSandboxModes.has("danger-full-access");
689
- const yoloApprovalAllowed = allowedApprovalPolicies === void 0 || allowedApprovalPolicies.has("never");
690
- const yoloReviewerAllowed = allowedApprovalsReviewers === void 0 || allowedApprovalsReviewers.has("user");
691
- if (!params.forceGuardian && yoloSandboxAllowed && yoloApprovalAllowed && yoloReviewerAllowed) return {
692
- mode: "yolo",
693
- dangerFullAccessAllowed: true
506
+ function resolveCodexPluginDestructivePolicy(policy) {
507
+ if (policy === "auto" || policy === "ask") return {
508
+ allowDestructiveActions: true,
509
+ destructiveApprovalMode: policy
694
510
  };
695
511
  return {
696
- mode: "guardian",
697
- dangerFullAccessAllowed: yoloSandboxAllowed,
698
- approvalPolicy: selectGuardianApprovalPolicy(allowedApprovalPolicies, params.execModeRequiringPromptingApprovals),
699
- approvalsReviewer: params.forceUserReviewer ? selectUserApprovalsReviewer(allowedApprovalsReviewers, params.execModeRequiringUserReviewer) : selectGuardianApprovalsReviewer(allowedApprovalsReviewers, params.execModeRequiringPromptingApprovals === "auto" ? "auto" : void 0),
700
- sandbox: selectGuardianSandbox(allowedSandboxModes)
512
+ allowDestructiveActions: policy,
513
+ destructiveApprovalMode: policy ? "allow" : "deny"
701
514
  };
702
515
  }
516
+ //#endregion
517
+ //#region extensions/codex/src/app-server/config-requirements.ts
518
+ const UNIX_CODEX_REQUIREMENTS_PATH = "/etc/codex/requirements.toml";
519
+ const WINDOWS_CODEX_REQUIREMENTS_SUFFIX = "\\OpenAI\\Codex\\requirements.toml";
520
+ function isCodexAppServerApprovalPolicyAllowedByRequirements(policy, params = {}) {
521
+ const content = readCodexRequirementsToml(params);
522
+ if (content === void 0) return true;
523
+ const allowedApprovalPolicies = parseAllowedApprovalPoliciesFromCodexRequirements(content);
524
+ return allowedApprovalPolicies === void 0 || allowedApprovalPolicies.has(policy);
525
+ }
703
526
  function readCodexRequirementsToml(params) {
704
527
  if (params.requirementsToml !== void 0) return params.requirementsToml ?? void 0;
705
528
  const requirementsPath = readNonEmptyString(params.requirementsPath) ?? resolveCodexRequirementsPath(params.env ?? process.env, params.platform ?? process.platform);
@@ -889,6 +712,52 @@ function selectUserApprovalsReviewer(allowedApprovalsReviewers, execModeRequirin
889
712
  if (allowedApprovalsReviewers === void 0 || allowedApprovalsReviewers.has("user")) return "user";
890
713
  throw new Error(`tools.exec.mode=${execModeRequiringUserReviewer ?? "ask"} requires Codex app-server user approvals`);
891
714
  }
715
+ //#endregion
716
+ //#region extensions/codex/src/app-server/config-reviewer.ts
717
+ const CODEX_APP_SERVER_HOME_DIRNAME = "codex-home";
718
+ const CODEX_CONFIG_TOML_FILENAME = "config.toml";
719
+ function canUseCodexModelBackedApprovalsReviewerForModel(params) {
720
+ const explicitProvider = params.modelProvider?.trim().toLowerCase();
721
+ const inferredProvider = inferProviderFromModelRef(params.model);
722
+ if (explicitProvider && explicitProvider !== "codex") return isTrustedCodexModelBackedApprovalsReviewerProvider(explicitProvider, params) && (inferredProvider === void 0 || isTrustedCodexModelBackedApprovalsReviewerProvider(inferredProvider, params));
723
+ if (inferredProvider !== void 0) return isTrustedCodexModelBackedApprovalsReviewerProvider(inferredProvider, params);
724
+ return isTrustedCodexModelBackedApprovalsReviewerProvider(explicitProvider, params);
725
+ }
726
+ function isTrustedCodexModelBackedOpenAIProvider(params) {
727
+ if (!openAIBaseUrlEnvOverridesAreTrustedForModelBackedReview(params.env)) return false;
728
+ const codexBaseUrlOverrides = readCodexBaseUrlOverridesForModelBackedReview(params);
729
+ if (codexBaseUrlOverrides === false || !codexBaseUrlOverrides.openAI.every(isNativeOpenAIBaseUrl) || !codexBaseUrlOverrides.chatGPT.every(isNativeChatGPTBaseUrl)) return false;
730
+ const openAIProviders = readConfiguredOpenAIProvidersForModelBackedReview(params.config);
731
+ if (openAIProviders.length === 0) return true;
732
+ return openAIProviders.every((openAIProvider) => configuredOpenAIProviderIsTrustedForModelBackedReview(openAIProvider, params.model));
733
+ }
734
+ function resolveCodexModelBackedReviewerPolicyContext(params) {
735
+ const provider = params.provider?.trim();
736
+ if (provider && provider.toLowerCase() !== "codex") return {
737
+ modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(provider),
738
+ model: params.model
739
+ };
740
+ const bindingModelProvider = params.bindingModelProvider?.trim();
741
+ const currentModel = params.model?.trim();
742
+ const bindingModel = params.bindingModel?.trim();
743
+ if (bindingModelProvider && currentModel && bindingModel && currentModel === bindingModel) return {
744
+ modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(bindingModelProvider),
745
+ model: params.model ?? params.bindingModel
746
+ };
747
+ const currentModelProvider = inferProviderFromModelRef(params.model);
748
+ if (currentModelProvider) return {
749
+ modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(currentModelProvider),
750
+ model: params.model
751
+ };
752
+ if (bindingModelProvider) return {
753
+ modelProvider: normalizeCodexModelBackedReviewerPolicyProvider(bindingModelProvider),
754
+ model: params.model ?? params.bindingModel
755
+ };
756
+ return {
757
+ modelProvider: params.nativeAuthProfile === true ? "openai" : void 0,
758
+ model: params.model ?? params.bindingModel
759
+ };
760
+ }
892
761
  function isCodexModelBackedApprovalsReviewerProvider(provider) {
893
762
  return provider?.trim().toLowerCase() === "openai";
894
763
  }
@@ -1041,278 +910,448 @@ function inferProviderFromModelRef(model) {
1041
910
  const slashIndex = normalized?.indexOf("/") ?? -1;
1042
911
  return slashIndex > 0 ? normalized?.slice(0, slashIndex) : void 0;
1043
912
  }
1044
- function selectForcedPromptingSandbox(params) {
1045
- if (params.configuredSandbox === "read-only" || params.defaultSandbox === "read-only") return "read-only";
1046
- return params.defaultSandbox ?? "workspace-write";
913
+ //#endregion
914
+ //#region extensions/codex/src/app-server/config-security.ts
915
+ function shouldAutoApproveCodexAppServerApprovals(appServer) {
916
+ return appServer.networkProxy === void 0 && appServer.approvalPolicy === "never" && appServer.sandbox === "danger-full-access";
1047
917
  }
1048
- function selectForcedDangerFullAccessSandbox(params) {
1049
- if (params.configuredSandbox === "read-only") return "read-only";
1050
- if (params.defaultPolicy?.dangerFullAccessAllowed === false) {
1051
- if (params.openClawSandboxActive) return params.defaultPolicy.sandbox ?? "workspace-write";
1052
- throw new Error("legacy full exec security with ask requires Codex app-server danger-full-access");
1053
- }
1054
- return "danger-full-access";
918
+ function resolveCodexAppServerNetworkProxy(config, sandbox) {
919
+ if (config?.enabled !== true) return {};
920
+ const fileSystemMode = config.baseProfile === "read-only" || !config.baseProfile && sandbox === "read-only" ? "read" : "write";
921
+ const networkConfig = removeUndefinedJsonFields({
922
+ enabled: true,
923
+ mode: config.mode,
924
+ domains: normalizeNetworkProxyPermissionMap(config.domains),
925
+ unix_sockets: normalizeNetworkProxyPermissionMap(config.unixSockets),
926
+ proxy_url: readNonEmptyString(config.proxyUrl),
927
+ socks_url: readNonEmptyString(config.socksUrl),
928
+ enable_socks5: config.enableSocks5,
929
+ enable_socks5_udp: config.enableSocks5Udp,
930
+ allow_upstream_proxy: config.allowUpstreamProxy,
931
+ allow_local_binding: config.allowLocalBinding,
932
+ dangerously_allow_non_loopback_proxy: config.dangerouslyAllowNonLoopbackProxy,
933
+ dangerously_allow_all_unix_sockets: config.dangerouslyAllowAllUnixSockets
934
+ });
935
+ const profile = {
936
+ filesystem: {
937
+ ":minimal": "read",
938
+ ":project_roots": { ".": fileSystemMode }
939
+ },
940
+ network: networkConfig
941
+ };
942
+ const profileName = resolveNetworkProxyPermissionProfileName(config, profile);
943
+ const configPatch = {
944
+ "features.network_proxy.enabled": true,
945
+ default_permissions: profileName,
946
+ permissions: { [profileName]: profile }
947
+ };
948
+ return { networkProxy: {
949
+ profileName,
950
+ configFingerprint: fingerprintCodexAppServerNetworkProxyConfigPatch(configPatch),
951
+ configPatch
952
+ } };
1055
953
  }
1056
- function selectGuardianSandbox(allowedSandboxModes) {
1057
- if (allowedSandboxModes === void 0 || allowedSandboxModes.has("workspace-write")) return "workspace-write";
1058
- if (allowedSandboxModes.has("read-only")) return "read-only";
1059
- if (allowedSandboxModes.has("danger-full-access")) return "danger-full-access";
1060
- return "workspace-write";
954
+ function resolveNetworkProxyPermissionProfileName(config, profile) {
955
+ const explicitProfileName = readNonEmptyString(config.profileName);
956
+ if (explicitProfileName) return explicitProfileName;
957
+ return `${DEFAULT_CODEX_APP_SERVER_NETWORK_PROXY_PROFILE_PREFIX}-${createHash("sha256").update(stableStringifyJson({
958
+ version: 1,
959
+ profile
960
+ })).digest("hex").slice(0, 16)}`;
1061
961
  }
1062
- function resolveApprovalPolicy(value) {
1063
- if (value === "on-failure") return "on-request";
1064
- return value === "on-request" || value === "untrusted" || value === "never" ? value : void 0;
962
+ function fingerprintCodexAppServerNetworkProxyConfigPatch(configPatch) {
963
+ return createHash("sha256").update(stableStringifyJson(configPatch)).digest("hex");
1065
964
  }
1066
- function resolveSandbox(value) {
1067
- return value === "read-only" || value === "workspace-write" || value === "danger-full-access" ? value : void 0;
965
+ function normalizeNetworkProxyPermissionMap(value) {
966
+ const entries = Object.entries(value ?? {}).map(([key, permission]) => [key.trim(), permission]).filter(([key]) => key.length > 0);
967
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
1068
968
  }
1069
- function resolveApprovalsReviewer(value) {
1070
- return value === "auto_review" || value === "guardian_subagent" || value === "user" ? value : void 0;
969
+ function removeUndefinedJsonFields(value) {
970
+ return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
1071
971
  }
1072
- function resolveOpenClawExecPolicyFromConfig(params) {
1073
- const root = readRecord(params.config);
1074
- const globalExec = readRecord(readRecord(root?.tools)?.exec);
1075
- const globalPolicy = applyOpenClawExecPolicyLayer(createDefaultOpenClawExecPolicy(), globalExec);
1076
- const agentId = params.agentId?.trim();
1077
- if (!agentId) return globalPolicy;
1078
- const agents = readRecord(root?.agents);
1079
- const agentList = Array.isArray(agents?.list) ? agents.list : [];
1080
- const normalizedAgentId = normalizeAgentId(agentId);
1081
- return applyOpenClawExecPolicyLayer(globalPolicy, readRecord(readRecord(readRecord(agentList.find((entry) => {
1082
- const id = readRecord(entry)?.id;
1083
- return typeof id === "string" && normalizeAgentId(id) === normalizedAgentId;
1084
- }))?.tools)?.exec));
972
+ function stableStringifyJson(value) {
973
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringifyJson(item)).join(",")}]`;
974
+ if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableStringifyJson(item)}`).join(",")}}`;
975
+ return JSON.stringify(value);
1085
976
  }
1086
- function resolveOpenClawExecPolicyForCodexAppServer(params) {
1087
- const overridePolicy = applyOpenClawExecPolicyLayer(resolveOpenClawExecPolicyFromConfig({
1088
- config: params.config,
1089
- agentId: params.agentId
1090
- }), params.execOverrides);
1091
- return applyOpenClawExecApprovalFloors(overridePolicy, resolveOpenClawExecApprovalFloorsForCodexAppServer({
1092
- approvals: params.approvals,
1093
- agentId: params.agentId,
1094
- policy: overridePolicy
1095
- }));
977
+ function withMcpElicitationsApprovalPolicy(policy) {
978
+ if (typeof policy !== "string") return { granular: {
979
+ ...policy.granular,
980
+ mcp_elicitations: true
981
+ } };
982
+ if (policy === "never") return { granular: {
983
+ mcp_elicitations: true,
984
+ rules: false,
985
+ sandbox_approval: false,
986
+ request_permissions: false,
987
+ skill_approval: false
988
+ } };
989
+ return { granular: {
990
+ mcp_elicitations: true,
991
+ rules: true,
992
+ sandbox_approval: true,
993
+ request_permissions: true,
994
+ skill_approval: true
995
+ } };
1096
996
  }
1097
- function resolveEffectiveOpenClawExecModeForCodexAppServer(params) {
1098
- if (params.execPolicy?.touched === true) return params.execPolicy.mode;
1099
- return params.execMode;
997
+ function resolveTransport(value) {
998
+ return value === "websocket" || value === "unix" ? value : "stdio";
1100
999
  }
1101
- function resolveCodexPolicyModeForOpenClawExecMode(mode) {
1102
- if (!mode || mode === "full") return;
1103
- return "guardian";
1000
+ function normalizeRemoteWorkspaceRoot(value) {
1001
+ return readNonEmptyString(value);
1002
+ }
1003
+ function inferCodexAppServerConnectionClass(params) {
1004
+ if (params.transport !== "websocket") return "local-loopback";
1005
+ return params.url && isLoopbackWebSocketUrl(params.url) ? "local-loopback" : "remote";
1006
+ }
1007
+ function assertCodexAppServerConnectionClassConfig(params) {
1008
+ if (params.connectionClass === "remote" && !hasIdentityBearingWebSocketAuth({
1009
+ authToken: params.authToken,
1010
+ headers: params.headers
1011
+ })) throw new Error("remote Codex app-server WebSocket URLs require appServer.authToken or an Authorization header");
1104
1012
  }
1105
- function assertCodexAppServerAllowedForOpenClawExecMode(mode) {
1106
- if (mode === "deny" || mode === "allowlist") throw new Error(`Codex app-server local execution is not available when tools.exec.mode=${mode}`);
1013
+ /** Applies the canonical remote-auth boundary to any Codex AppServer transport. */
1014
+ function assertCodexAppServerConnectionSecurity(params) {
1015
+ assertCodexAppServerConnectionClassConfig({
1016
+ connectionClass: inferCodexAppServerConnectionClass(params),
1017
+ authToken: params.authToken,
1018
+ headers: params.headers
1019
+ });
1107
1020
  }
1108
- function createDefaultOpenClawExecPolicy() {
1109
- return {
1110
- security: "full",
1111
- ask: "off",
1112
- touched: false
1113
- };
1021
+ function isLoopbackWebSocketUrl(value) {
1022
+ let parsed;
1023
+ try {
1024
+ parsed = new URL(value);
1025
+ } catch {
1026
+ return false;
1027
+ }
1028
+ if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") return false;
1029
+ return isLoopbackHost(parsed.hostname);
1114
1030
  }
1115
- function applyOpenClawExecPolicyLayer(base, exec) {
1116
- if (!exec) return base;
1117
- const mode = readExecMode(exec.mode);
1118
- if (mode !== void 0) return {
1119
- ...resolveOpenClawExecPolicyForMode(mode),
1120
- touched: true
1121
- };
1122
- const security = readExecSecurity(exec.security);
1123
- const ask = readExecAsk(exec.ask);
1124
- if (security === void 0 && ask === void 0) return base;
1125
- const nextSecurity = security ?? base.security;
1126
- const nextAsk = ask ?? base.ask;
1127
- return {
1128
- mode: resolveOpenClawExecModeFromPolicy({
1129
- security: nextSecurity,
1130
- ask: nextAsk
1131
- }),
1132
- security: nextSecurity,
1133
- ask: nextAsk,
1134
- touched: true
1135
- };
1031
+ function hasIdentityBearingWebSocketAuth(params) {
1032
+ if (readNonEmptyString(params.authToken)) return true;
1033
+ return Object.entries(params.headers).some(([key, value]) => key.trim().toLowerCase() === "authorization" && Boolean(readNonEmptyString(value)));
1136
1034
  }
1137
- function resolveOpenClawExecApprovalFloorsForCodexAppServer(params) {
1138
- if (!params.approvals) return;
1139
- return resolveExecApprovalsFromFile({
1140
- file: params.approvals,
1141
- agentId: params.agentId,
1142
- overrides: {
1143
- security: params.policy.security,
1144
- ask: params.policy.ask
1145
- }
1146
- }).agent;
1035
+ function resolvePolicyMode(value) {
1036
+ return value === "guardian" || value === "yolo" ? value : void 0;
1147
1037
  }
1148
- function applyOpenClawExecApprovalFloors(base, approvalFloors) {
1149
- if (!approvalFloors) return base;
1150
- const nextSecurity = approvalFloors.security ? minOpenClawExecSecurity(base.security, approvalFloors.security) : base.security;
1151
- const nextAsk = approvalFloors.ask ? maxOpenClawExecAsk(base.ask, approvalFloors.ask) : base.ask;
1152
- if (nextSecurity === base.security && nextAsk === base.ask) return base;
1153
- return {
1154
- mode: resolveOpenClawExecModeFromPolicy({
1155
- security: nextSecurity,
1156
- ask: nextAsk
1157
- }),
1158
- security: nextSecurity,
1159
- ask: nextAsk,
1160
- touched: true
1038
+ function resolveDefaultCodexAppServerPolicy(params) {
1039
+ if (params.transport !== "stdio") return {
1040
+ mode: "yolo",
1041
+ dangerFullAccessAllowed: true
1161
1042
  };
1162
- }
1163
- function resolveOpenClawExecPolicyForMode(mode) {
1164
- switch (mode) {
1165
- case "deny": return {
1166
- mode,
1167
- security: "deny",
1168
- ask: "off"
1169
- };
1170
- case "allowlist": return {
1171
- mode,
1172
- security: "allowlist",
1173
- ask: "off"
1174
- };
1175
- case "ask":
1176
- case "auto": return {
1177
- mode,
1178
- security: "allowlist",
1179
- ask: "on-miss"
1043
+ const content = readCodexRequirementsToml(params);
1044
+ if (content === void 0) {
1045
+ if (!params.forceGuardian) return {
1046
+ mode: "yolo",
1047
+ dangerFullAccessAllowed: true
1180
1048
  };
1181
- case "full": return {
1182
- mode,
1183
- security: "full",
1184
- ask: "off"
1049
+ return {
1050
+ mode: "guardian",
1051
+ dangerFullAccessAllowed: true,
1052
+ approvalPolicy: selectGuardianApprovalPolicy(void 0, params.execModeRequiringPromptingApprovals),
1053
+ approvalsReviewer: params.forceUserReviewer ? selectUserApprovalsReviewer(void 0, params.execModeRequiringUserReviewer) : selectGuardianApprovalsReviewer(void 0, params.execModeRequiringPromptingApprovals === "auto" ? "auto" : void 0),
1054
+ sandbox: selectGuardianSandbox(void 0)
1185
1055
  };
1186
1056
  }
1187
- return mode;
1057
+ const allowedSandboxModes = parseAllowedSandboxModesFromCodexRequirements(content, readNonEmptyString(params.hostName) ?? hostname());
1058
+ const allowedApprovalPolicies = parseAllowedApprovalPoliciesFromCodexRequirements(content);
1059
+ const allowedApprovalsReviewers = parseAllowedApprovalsReviewersFromCodexRequirements(content);
1060
+ const yoloSandboxAllowed = allowedSandboxModes === void 0 || allowedSandboxModes.has("danger-full-access");
1061
+ const yoloApprovalAllowed = allowedApprovalPolicies === void 0 || allowedApprovalPolicies.has("never");
1062
+ const yoloReviewerAllowed = allowedApprovalsReviewers === void 0 || allowedApprovalsReviewers.has("user");
1063
+ if (!params.forceGuardian && yoloSandboxAllowed && yoloApprovalAllowed && yoloReviewerAllowed) return {
1064
+ mode: "yolo",
1065
+ dangerFullAccessAllowed: true
1066
+ };
1067
+ return {
1068
+ mode: "guardian",
1069
+ dangerFullAccessAllowed: yoloSandboxAllowed,
1070
+ approvalPolicy: selectGuardianApprovalPolicy(allowedApprovalPolicies, params.execModeRequiringPromptingApprovals),
1071
+ approvalsReviewer: params.forceUserReviewer ? selectUserApprovalsReviewer(allowedApprovalsReviewers, params.execModeRequiringUserReviewer) : selectGuardianApprovalsReviewer(allowedApprovalsReviewers, params.execModeRequiringPromptingApprovals === "auto" ? "auto" : void 0),
1072
+ sandbox: selectGuardianSandbox(allowedSandboxModes)
1073
+ };
1188
1074
  }
1189
- function resolveOpenClawExecModeFromPolicy(params) {
1190
- if (params.security === "deny") return "deny";
1191
- if (params.security === "allowlist" && params.ask === "off") return "allowlist";
1192
- if (params.security === "full" && params.ask !== "always") return "full";
1193
- return "ask";
1075
+ //#endregion
1076
+ //#region extensions/codex/src/app-server/config-runtime.ts
1077
+ /**
1078
+ * Sole owner of the app-server home-scope decision. Ordinary harness connections
1079
+ * default to the isolated agent home; the supervision connection owns the operator's
1080
+ * native Codex home on local transports. Auth handoffs must read the scope from here
1081
+ * (or from resolved start options) because a prepared login on a native home rewrites
1082
+ * the account Codex CLI and Desktop share.
1083
+ */
1084
+ function resolveCodexAppServerHomeScope(params) {
1085
+ const configured = params.appServer?.homeScope;
1086
+ if (configured) return configured;
1087
+ return params.connectionScope === "supervision" && resolveTransport(params.appServer?.transport) !== "websocket" ? "user" : "agent";
1194
1088
  }
1195
- function minOpenClawExecSecurity(left, right) {
1196
- const order = {
1197
- deny: 0,
1198
- allowlist: 1,
1199
- full: 2
1089
+ function resolveCodexAppServerRuntimeOptions(params = {}) {
1090
+ const env = params.env ?? process.env;
1091
+ const config = readCodexPluginConfig(params.pluginConfig).appServer ?? {};
1092
+ const transport = resolveTransport(config.transport);
1093
+ const homeScope = resolveCodexAppServerHomeScope({ appServer: config });
1094
+ const configCommand = readNonEmptyString(config.command);
1095
+ const envCommand = readNonEmptyString(env.OPENCLAW_CODEX_APP_SERVER_BIN);
1096
+ const command = configCommand ?? envCommand ?? "codex";
1097
+ const commandSource = configCommand ? "config" : envCommand ? "env" : "managed";
1098
+ if (commandSource === "config" || commandSource === "env") assertCodexAppServerCommandHasNoInlineArgs({
1099
+ command,
1100
+ source: commandSource
1101
+ });
1102
+ const args = resolveArgs(config.args, env.OPENCLAW_CODEX_APP_SERVER_ARGS);
1103
+ const headers = normalizeHeaders(config.headers);
1104
+ const clearEnv = normalizeStringList(config.clearEnv);
1105
+ const authToken = normalizeCodexAppServerSecretInput({
1106
+ value: config.authToken,
1107
+ path: "plugins.entries.codex.config.appServer.authToken"
1108
+ });
1109
+ const url = readNonEmptyString(config.url) ?? (transport === "unix" ? "unix://" : void 0);
1110
+ const connectionClass = inferCodexAppServerConnectionClass({
1111
+ transport,
1112
+ url
1113
+ });
1114
+ const remoteAppsSubstrate = "preconfigured";
1115
+ const remoteWorkspaceRoot = normalizeRemoteWorkspaceRoot(config.remoteWorkspaceRoot);
1116
+ const execMode = resolveEffectiveOpenClawExecModeForCodexAppServer({
1117
+ execMode: params.execMode,
1118
+ execPolicy: params.execPolicy
1119
+ });
1120
+ assertCodexAppServerAllowedForOpenClawExecMode(execMode);
1121
+ const explicitPolicyMode = resolvePolicyMode(config.mode) ?? resolvePolicyMode(env.OPENCLAW_CODEX_APP_SERVER_MODE);
1122
+ const configuredSandbox = resolveSandbox(config.sandbox) ?? resolveSandbox(env.OPENCLAW_CODEX_APP_SERVER_SANDBOX);
1123
+ const explicitApprovalsReviewer = resolveApprovalsReviewer(config.approvalsReviewer);
1124
+ const normalizedPolicyMode = resolveCodexPolicyModeForOpenClawExecMode(execMode);
1125
+ const ignoreLegacyYoloPolicyMode = normalizedPolicyMode === "guardian" && explicitPolicyMode === "yolo";
1126
+ const canUseModelBackedReviewer = canUseCodexModelBackedApprovalsReviewerForModel({
1127
+ modelProvider: params.modelProvider,
1128
+ model: params.model,
1129
+ config: params.config,
1130
+ env,
1131
+ agentDir: params.agentDir,
1132
+ codexConfigToml: params.codexConfigToml,
1133
+ homeScope
1134
+ });
1135
+ const forceUserReviewer = !canUseModelBackedReviewer && (explicitApprovalsReviewer === "auto_review" || explicitApprovalsReviewer === "guardian_subagent" || explicitPolicyMode === "guardian" && explicitApprovalsReviewer !== "user") || execMode !== void 0 && execMode !== "full" && (execMode !== "auto" || !canUseModelBackedReviewer);
1136
+ const forceGuardianReviewer = execMode === "auto" && canUseModelBackedReviewer;
1137
+ const execModeRequiringPromptingApprovals = execMode === "auto" || execMode === "ask" ? execMode : forceUserReviewer ? "ask" : void 0;
1138
+ const forceDangerFullAccessSandbox = params.execPolicy?.touched === true && params.execPolicy.security === "full" && params.execPolicy.ask === "always";
1139
+ const forceRuntimePolicy = forceUserReviewer || forceGuardianReviewer || forceDangerFullAccessSandbox;
1140
+ const defaultPolicy = explicitPolicyMode && !forceRuntimePolicy && !ignoreLegacyYoloPolicyMode ? void 0 : resolveDefaultCodexAppServerPolicy({
1141
+ transport,
1142
+ env,
1143
+ forceGuardian: normalizedPolicyMode === "guardian",
1144
+ forceUserReviewer: forceUserReviewer || !canUseModelBackedReviewer,
1145
+ execModeRequiringPromptingApprovals,
1146
+ requirementsToml: params.requirementsToml,
1147
+ requirementsPath: params.requirementsPath,
1148
+ readRequirementsFile: params.readRequirementsFile,
1149
+ platform: params.platform,
1150
+ hostName: params.hostName,
1151
+ execModeRequiringUserReviewer: forceUserReviewer ? execMode : void 0
1152
+ });
1153
+ const preserveExplicitAutoSandbox = forceGuardianReviewer && configuredSandbox === "read-only";
1154
+ const forcedPolicy = forceRuntimePolicy ? {
1155
+ approvalPolicy: defaultPolicy?.approvalPolicy ?? "on-request",
1156
+ sandbox: preserveExplicitAutoSandbox ? void 0 : forceDangerFullAccessSandbox ? selectForcedDangerFullAccessSandbox({
1157
+ configuredSandbox,
1158
+ defaultPolicy,
1159
+ openClawSandboxActive: Boolean(params.openClawSandboxActive)
1160
+ }) : selectForcedPromptingSandbox({
1161
+ configuredSandbox,
1162
+ defaultSandbox: defaultPolicy?.sandbox
1163
+ }),
1164
+ approvalsReviewer: defaultPolicy?.approvalsReviewer ?? (forceUserReviewer ? "user" : "auto_review")
1165
+ } : void 0;
1166
+ const policyMode = ignoreLegacyYoloPolicyMode ? normalizedPolicyMode : explicitPolicyMode ?? normalizedPolicyMode ?? defaultPolicy?.mode ?? "yolo";
1167
+ const serviceTier = normalizeCodexServiceTier(config.serviceTier);
1168
+ const resolvedSandbox = forcedPolicy?.sandbox ?? configuredSandbox ?? defaultPolicy?.sandbox ?? (policyMode === "guardian" ? "workspace-write" : "danger-full-access");
1169
+ if (transport === "websocket" && !url) throw new Error("plugins.entries.codex.config.appServer.url is required when appServer.transport is websocket");
1170
+ if (transport === "websocket" && homeScope === "user") throw new Error("plugins.entries.codex.config.appServer.homeScope=user requires appServer.transport=stdio or unix");
1171
+ if (transport === "unix" && homeScope !== "user") throw new Error("plugins.entries.codex.config.appServer.transport=unix requires appServer.homeScope=user");
1172
+ if (transport === "unix" && !url?.startsWith("unix://")) throw new Error("plugins.entries.codex.config.appServer.url must use unix:// when appServer.transport is unix");
1173
+ assertCodexAppServerConnectionSecurity({
1174
+ transport,
1175
+ url,
1176
+ authToken,
1177
+ headers
1178
+ });
1179
+ const configApprovalPolicy = resolveApprovalPolicy(config.approvalPolicy);
1180
+ const envApprovalPolicy = resolveApprovalPolicy(env.OPENCLAW_CODEX_APP_SERVER_APPROVAL_POLICY);
1181
+ const approvalPolicy = configApprovalPolicy ?? envApprovalPolicy ?? defaultPolicy?.approvalPolicy ?? (policyMode === "guardian" ? "on-request" : "never");
1182
+ const approvalPolicySource = configApprovalPolicy ? "config" : envApprovalPolicy ? "env" : defaultPolicy?.approvalPolicy ? "requirements" : "implicit";
1183
+ const computerUseConfig = resolveCodexComputerUseConfig({
1184
+ pluginConfig: params.pluginConfig,
1185
+ env
1186
+ });
1187
+ const managedCommandOrder = params.managedCommandOrder ?? (homeScope === "user" || computerUseConfig.enabled ? "desktop-first" : "package-first");
1188
+ const includeManagedCommandOrder = commandSource === "managed" && (managedCommandOrder === "desktop-first" || params.managedCommandOrder === "package-first");
1189
+ const managedComputerUsePluginNames = [.../* @__PURE__ */ new Set([DEFAULT_CODEX_COMPUTER_USE_PLUGIN_NAME, computerUseConfig.pluginName])];
1190
+ return {
1191
+ start: {
1192
+ transport,
1193
+ homeScope,
1194
+ command,
1195
+ commandSource,
1196
+ ...includeManagedCommandOrder ? { managedCommandOrder } : {},
1197
+ ...commandSource === "managed" ? { managedComputerUsePluginNames } : {},
1198
+ args: args.length > 0 ? args : [
1199
+ "app-server",
1200
+ "--listen",
1201
+ "stdio://"
1202
+ ],
1203
+ ...url ? { url } : {},
1204
+ ...authToken ? { authToken } : {},
1205
+ headers,
1206
+ ...transport === "stdio" && clearEnv.length > 0 ? { clearEnv } : {}
1207
+ },
1208
+ connectionClass,
1209
+ remoteAppsSubstrate,
1210
+ ...remoteWorkspaceRoot ? { remoteWorkspaceRoot } : {},
1211
+ codeModeOnly: config.codeModeOnly === true,
1212
+ loopDetectionPreToolUseRelay: config.loopDetectionPreToolUseRelay !== false,
1213
+ requestTimeoutMs: normalizePositiveNumber(config.requestTimeoutMs, 6e4),
1214
+ turnCompletionIdleTimeoutMs: normalizePositiveNumber(config.turnCompletionIdleTimeoutMs, 6e4),
1215
+ turnAssistantCompletionIdleTimeoutMs: normalizePositiveNumber(config.turnAssistantCompletionIdleTimeoutMs, 1e4),
1216
+ ...config.postToolRawAssistantCompletionIdleTimeoutMs !== void 0 ? { postToolRawAssistantCompletionIdleTimeoutMs: normalizePositiveNumber(config.postToolRawAssistantCompletionIdleTimeoutMs, 6e4) } : {},
1217
+ approvalPolicy: forcedPolicy?.approvalPolicy ?? approvalPolicy,
1218
+ approvalPolicySource,
1219
+ sandbox: resolvedSandbox,
1220
+ approvalsReviewer: forcedPolicy?.approvalsReviewer ?? explicitApprovalsReviewer ?? defaultPolicy?.approvalsReviewer ?? (policyMode === "guardian" ? "auto_review" : "user"),
1221
+ ...serviceTier ? { serviceTier } : {},
1222
+ ...resolveCodexAppServerNetworkProxy(config.networkProxy, resolvedSandbox)
1200
1223
  };
1201
- return order[left] <= order[right] ? left : right;
1202
1224
  }
1203
- function maxOpenClawExecAsk(left, right) {
1204
- const order = {
1205
- off: 0,
1206
- "on-miss": 1,
1207
- always: 2
1225
+ /**
1226
+ * Rechecks Codex-owned plugin state at the final spawn boundary, where the
1227
+ * effective agent home is known, so Computer Use keeps the desktop app's TCC ownership.
1228
+ */
1229
+ function resolveCodexAppServerStartOptionsForAgent(params) {
1230
+ const startOptions = params.startOptions;
1231
+ if (startOptions.transport !== "stdio" || startOptions.commandSource !== "managed" || startOptions.managedCommandOrder !== void 0) return startOptions;
1232
+ if (startOptions.homeScope === "user") return {
1233
+ ...startOptions,
1234
+ managedCommandOrder: "desktop-first"
1208
1235
  };
1209
- return order[left] >= order[right] ? left : right;
1210
- }
1211
- function readExecMode(value) {
1212
- return value === "deny" || value === "allowlist" || value === "ask" || value === "auto" || value === "full" ? value : void 0;
1213
- }
1214
- function readRecord(value) {
1215
- return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1216
- }
1217
- function normalizeCodexServiceTier(value) {
1218
- if (typeof value !== "string") return;
1219
- const trimmed = value.trim();
1220
- if (!trimmed) return;
1221
- const normalized = trimmed.toLowerCase();
1222
- if (normalized === "fast" || normalized === "priority") return "priority";
1223
- if (normalized === "flex") return "flex";
1224
- return trimmed;
1225
- }
1226
- function isCodexFastServiceTier(value) {
1227
- return normalizeCodexServiceTier(value) === "priority";
1228
- }
1229
- function normalizePositiveNumber(value, fallback) {
1230
- return resolvePositiveTimerTimeoutMs(value, fallback);
1231
- }
1232
- function normalizeHeaders(value) {
1233
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1234
- return Object.fromEntries(Object.entries(value).map(([key, child]) => [key.trim(), normalizeCodexAppServerSecretInput({
1235
- value: child,
1236
- path: `plugins.entries.codex.config.appServer.headers.${key}`
1237
- })]).filter((entry) => Boolean(entry[0] && entry[1])));
1238
- }
1239
- function normalizeCodexAppServerSecretInput(params) {
1240
- return normalizeResolvedSecretInputString(params);
1241
- }
1242
- function normalizeStringList(value) {
1243
- return normalizeTrimmedStringList(value);
1244
- }
1245
- function readBooleanEnv(value) {
1246
- if (value === void 0) return;
1247
- const normalized = value.trim().toLowerCase();
1248
- if ([
1249
- "1",
1250
- "true",
1251
- "yes",
1252
- "on"
1253
- ].includes(normalized)) return true;
1254
- if ([
1255
- "0",
1256
- "false",
1257
- "no",
1258
- "off"
1259
- ].includes(normalized)) return false;
1260
- }
1261
- function readExecSecurity(value) {
1262
- return value === "deny" || value === "allowlist" || value === "full" ? value : void 0;
1263
- }
1264
- function readExecAsk(value) {
1265
- return value === "off" || value === "on-miss" || value === "always" ? value : void 0;
1236
+ return codexConfigEnablesNativeComputerUse({
1237
+ agentDir: params.agentDir,
1238
+ codexConfigToml: params.codexConfigToml,
1239
+ env: params.env,
1240
+ homeScope: "agent",
1241
+ pluginNames: startOptions.managedComputerUsePluginNames ?? ["computer-use"]
1242
+ }) ? {
1243
+ ...startOptions,
1244
+ managedCommandOrder: "desktop-first"
1245
+ } : startOptions;
1266
1246
  }
1267
- function readNumberEnv(value) {
1268
- const trimmed = value?.trim();
1269
- if (!trimmed || !PLAIN_DECIMAL_NUMBER_RE.test(trimmed)) return;
1270
- const parsed = Number(trimmed);
1271
- return Number.isFinite(parsed) ? parsed : void 0;
1247
+ function resolveCodexComputerUseConfig(params = {}) {
1248
+ const env = params.env ?? process.env;
1249
+ const config = readCodexPluginConfig(params.pluginConfig).computerUse ?? {};
1250
+ const marketplaceSource = readNonEmptyString(params.overrides?.marketplaceSource) ?? readNonEmptyString(config.marketplaceSource) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_SOURCE);
1251
+ const marketplacePath = readNonEmptyString(params.overrides?.marketplacePath) ?? readNonEmptyString(config.marketplacePath) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_PATH);
1252
+ const marketplaceName = readNonEmptyString(params.overrides?.marketplaceName) ?? readNonEmptyString(config.marketplaceName) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_NAME);
1253
+ const configuredPluginName = readNonEmptyString(params.overrides?.pluginName) ?? readNonEmptyString(config.pluginName) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_PLUGIN_NAME);
1254
+ const configuredMcpServerName = readNonEmptyString(params.overrides?.mcpServerName) ?? readNonEmptyString(config.mcpServerName) ?? readNonEmptyString(env.OPENCLAW_CODEX_COMPUTER_USE_MCP_SERVER_NAME);
1255
+ const autoInstall = params.overrides?.autoInstall ?? config.autoInstall ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_AUTO_INSTALL) ?? false;
1256
+ const marketplaceDiscoveryTimeoutMs = normalizePositiveNumber(params.overrides?.marketplaceDiscoveryTimeoutMs ?? config.marketplaceDiscoveryTimeoutMs ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS), DEFAULT_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS);
1257
+ const liveTestTimeoutMs = normalizePositiveNumber(params.overrides?.liveTestTimeoutMs ?? config.liveTestTimeoutMs ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_LIVE_TEST_TIMEOUT_MS), DEFAULT_CODEX_COMPUTER_USE_LIVE_TEST_TIMEOUT_MS);
1258
+ const toolCallTimeoutMs = normalizePositiveNumber(params.overrides?.toolCallTimeoutMs ?? config.toolCallTimeoutMs ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_TOOL_CALL_TIMEOUT_MS), DEFAULT_CODEX_COMPUTER_USE_TOOL_CALL_TIMEOUT_MS);
1259
+ const healthCheckIntervalMinutes = normalizeComputerUseHealthCheckIntervalMinutes(params.overrides?.healthCheckIntervalMinutes ?? config.healthCheckIntervalMinutes ?? readNumberEnv(env.OPENCLAW_CODEX_COMPUTER_USE_HEALTH_CHECK_INTERVAL_MINUTES));
1260
+ const healthCheckEnabled = params.overrides?.healthCheckEnabled ?? config.healthCheckEnabled ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_HEALTH_CHECK_ENABLED) ?? false;
1261
+ const pluginCacheMode = normalizeComputerUsePluginCacheMode(params.overrides?.pluginCacheMode) ?? normalizeComputerUsePluginCacheMode(config.pluginCacheMode) ?? normalizeComputerUsePluginCacheMode(env.OPENCLAW_CODEX_COMPUTER_USE_PLUGIN_CACHE_MODE) ?? "independent";
1262
+ const strictReadiness = params.overrides?.strictReadiness ?? config.strictReadiness ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_STRICT_READINESS) ?? false;
1263
+ const autoRepair = params.overrides?.autoRepair ?? config.autoRepair ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE_AUTO_REPAIR) ?? false;
1264
+ return {
1265
+ enabled: params.overrides?.enabled ?? config.enabled ?? readBooleanEnv(env.OPENCLAW_CODEX_COMPUTER_USE) ?? Boolean(autoInstall || marketplaceSource || marketplacePath || marketplaceName || configuredPluginName || configuredMcpServerName),
1266
+ autoInstall,
1267
+ marketplaceDiscoveryTimeoutMs,
1268
+ liveTestTimeoutMs,
1269
+ toolCallTimeoutMs,
1270
+ healthCheckEnabled,
1271
+ healthCheckIntervalMinutes,
1272
+ pluginCacheMode,
1273
+ strictReadiness,
1274
+ autoRepair,
1275
+ pluginName: configuredPluginName ?? "computer-use",
1276
+ mcpServerName: configuredMcpServerName ?? "computer-use",
1277
+ ...marketplaceSource ? { marketplaceSource } : {},
1278
+ ...marketplacePath ? { marketplacePath } : {},
1279
+ ...marketplaceName ? { marketplaceName } : {}
1280
+ };
1272
1281
  }
1273
- function resolveArgs(configArgs, envArgs) {
1274
- if (Array.isArray(configArgs)) return configArgs.map((entry) => readNonEmptyString(entry)).filter((entry) => entry !== void 0);
1275
- if (typeof configArgs === "string") return splitShellWords(configArgs);
1276
- return splitShellWords(envArgs ?? "");
1282
+ function normalizeComputerUseHealthCheckIntervalMinutes(value) {
1283
+ return value === 30 || value === 60 || value === 120 || value === 240 ? value : 60;
1277
1284
  }
1278
- function readNonEmptyString(value) {
1279
- if (typeof value !== "string") return;
1280
- return value.trim() || void 0;
1285
+ function normalizeComputerUsePluginCacheMode(value) {
1286
+ return value === "shared" || value === "independent" ? value : null;
1281
1287
  }
1282
- function hashSecretForKey(value, label) {
1283
- if (!value) return null;
1284
- return createHmac("sha256", START_OPTIONS_KEY_SECRET).update(label).update("\0").update(value).digest("hex");
1288
+ function codexAppServerStartOptionsKey(options, params = {}) {
1289
+ return JSON.stringify({
1290
+ transport: options.transport,
1291
+ command: options.command,
1292
+ commandSource: options.commandSource ?? null,
1293
+ managedCommandOrder: options.managedCommandOrder ?? "package-first",
1294
+ managedComputerUsePluginNames: [...options.managedComputerUsePluginNames ?? []].toSorted(),
1295
+ managedFallbackCommandPaths: [...options.managedFallbackCommandPaths ?? []],
1296
+ args: options.args,
1297
+ cwd: options.cwd ?? null,
1298
+ url: options.url ?? null,
1299
+ authToken: hashSecretForKey(options.authToken, "authToken"),
1300
+ headers: Object.entries(options.headers).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, hashSecretForKey(value, `header:${key}`)]),
1301
+ env: Object.entries(options.env ?? {}).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, hashSecretForKey(value, `env:${key}`)]),
1302
+ clearEnv: [...options.clearEnv ?? []].toSorted(),
1303
+ authProfileId: params.authProfileId ?? null,
1304
+ authBindingFingerprint: params.authBindingFingerprint ?? null,
1305
+ agentDir: params.agentDir ?? null,
1306
+ fallbackApiKeyCacheKey: params.fallbackApiKeyCacheKey ?? null
1307
+ });
1285
1308
  }
1286
- function getStartOptionsKeySecret() {
1287
- const globalState = globalThis;
1288
- globalState[START_OPTIONS_KEY_SECRET_SYMBOL] ??= randomBytes(32);
1289
- return globalState[START_OPTIONS_KEY_SECRET_SYMBOL];
1309
+ function codexSandboxPolicyForTurn(mode, cwd, nativeArgs = []) {
1310
+ if (mode === "danger-full-access") return { type: "dangerFullAccess" };
1311
+ if (mode === "read-only") return {
1312
+ type: "readOnly",
1313
+ networkAccess: false
1314
+ };
1315
+ let excludeTmpdirEnvVar = false;
1316
+ let excludeSlashTmp = false;
1317
+ for (let index = 0; index < nativeArgs.length; index += 1) {
1318
+ const arg = nativeArgs[index];
1319
+ const override = arg === "-c" || arg === "--config" ? nativeArgs[++index] : arg?.startsWith("--config=") ? arg.slice(9) : void 0;
1320
+ if (!override) continue;
1321
+ const separator = override.indexOf("=");
1322
+ if (separator < 0) continue;
1323
+ const key = override.slice(0, separator).trim();
1324
+ const value = override.slice(separator + 1).trim();
1325
+ if (value !== "true" && value !== "false") continue;
1326
+ if (key === "sandbox_workspace_write.exclude_tmpdir_env_var") excludeTmpdirEnvVar = value === "true";
1327
+ else if (key === "sandbox_workspace_write.exclude_slash_tmp") excludeSlashTmp = value === "true";
1328
+ }
1329
+ return {
1330
+ type: "workspaceWrite",
1331
+ writableRoots: [cwd],
1332
+ networkAccess: false,
1333
+ excludeTmpdirEnvVar,
1334
+ excludeSlashTmp
1335
+ };
1290
1336
  }
1291
- function splitShellWords(value) {
1292
- const words = [];
1293
- let current = "";
1294
- let quote = null;
1295
- for (const char of value) {
1296
- if (quote) {
1297
- if (char === quote) quote = null;
1298
- else current += char;
1299
- continue;
1300
- }
1301
- if (char === "\"" || char === "'") {
1302
- quote = char;
1303
- continue;
1304
- }
1305
- if (/\s/.test(char)) {
1306
- if (current) {
1307
- words.push(current);
1308
- current = "";
1337
+ /** Resolves the passive supervision control connection without changing harness defaults. */
1338
+ function resolveCodexSupervisionAppServerRuntimeOptions(params = {}) {
1339
+ const pluginConfig = readCodexPluginConfig(params.pluginConfig);
1340
+ const appServer = pluginConfig.appServer ?? {};
1341
+ const homeScope = resolveCodexAppServerHomeScope({
1342
+ appServer,
1343
+ connectionScope: "supervision"
1344
+ });
1345
+ return resolveCodexAppServerRuntimeOptions({
1346
+ ...params,
1347
+ pluginConfig: {
1348
+ ...pluginConfig,
1349
+ appServer: {
1350
+ ...appServer,
1351
+ homeScope
1309
1352
  }
1310
- continue;
1311
1353
  }
1312
- current += char;
1313
- }
1314
- if (current) words.push(current);
1315
- return words;
1354
+ });
1316
1355
  }
1317
1356
  //#endregion
1318
1357
  //#region extensions/codex/src/app-server/session-binding.ts
@@ -1335,6 +1374,8 @@ var session_binding_exports = /* @__PURE__ */ __exportAll({
1335
1374
  readStoredCodexAppServerBinding: () => readStoredCodexAppServerBinding,
1336
1375
  reclaimCurrentCodexSessionGeneration: () => reclaimCurrentCodexSessionGeneration,
1337
1376
  resolveCodexAppServerBindingModelProvider: () => resolveCodexAppServerBindingModelProvider,
1377
+ resolveCodexRunSessionBindingAuthority: () => resolveCodexRunSessionBindingAuthority,
1378
+ scopeCodexRunBindingStore: () => scopeCodexRunBindingStore,
1338
1379
  sessionBindingIdentity: () => sessionBindingIdentity
1339
1380
  });
1340
1381
  const CODEX_APP_SERVER_NATIVE_AUTH_PROVIDER = "openai";
@@ -1348,7 +1389,7 @@ const BINDING_LEASE_RENEW_INTERVAL_MS = Math.floor(BINDING_LEASE_STALE_MS / 3);
1348
1389
  const PHYSICAL_SESSION_RETIRE_TTL_MS = BINDING_LEASE_WAIT_MS;
1349
1390
  /** Resolves the same agent scope OpenClaw uses for transcript/session ownership. */
1350
1391
  function sessionBindingIdentity(params) {
1351
- const { sessionAgentId } = resolveSessionAgentIds(params);
1392
+ const { sessionAgentId } = resolveSessionAgentIds$1(params);
1352
1393
  const sessionKey = params.sessionKey?.trim();
1353
1394
  return {
1354
1395
  kind: "session",
@@ -1357,6 +1398,25 @@ function sessionBindingIdentity(params) {
1357
1398
  ...sessionKey ? { sessionKey } : {}
1358
1399
  };
1359
1400
  }
1401
+ /** Decides whether a run may share the durable stable-key binding owner. */
1402
+ function resolveCodexRunSessionBindingAuthority(params) {
1403
+ const sessionKey = params.identity.sessionKey?.trim();
1404
+ if (!sessionKey) return "ephemeral";
1405
+ try {
1406
+ const storePath = params.storePath?.trim() || resolveStorePath(params.config?.session?.store, { agentId: params.identity.agentId });
1407
+ const entry = getSessionEntry({
1408
+ agentId: params.identity.agentId,
1409
+ hydrateSkillPromptRefs: false,
1410
+ readConsistency: "latest",
1411
+ sessionKey,
1412
+ storePath
1413
+ });
1414
+ if (!entry) return "ephemeral";
1415
+ return entry.sessionId === params.identity.sessionId ? "current" : "superseded";
1416
+ } catch {
1417
+ return "superseded";
1418
+ }
1419
+ }
1360
1420
  /** Builds the terminal coordination error used when a newer OpenClaw session owns the binding. */
1361
1421
  function createCodexSessionGenerationSupersededError(sessionId) {
1362
1422
  return new AgentHarnessSessionSupersededError(`Codex session generation is no longer current: ${sessionId}`);
@@ -1424,6 +1484,7 @@ const threadBindingSchema = z.object({
1424
1484
  threadId: z.string().refine((value) => Boolean(value.trim())),
1425
1485
  clientId: optionalStringSchema,
1426
1486
  cwd: z.string(),
1487
+ rolloutPath: optionalNonBlankStringSchema,
1427
1488
  connectionScope: z.literal("supervision").optional(),
1428
1489
  supervisionSourceThreadId: z.string().trim().min(1).optional(),
1429
1490
  authProfileId: optionalStringSchema,
@@ -1447,6 +1508,7 @@ const threadBindingSchema = z.object({
1447
1508
  dynamicToolsFingerprint: optionalStringSchema,
1448
1509
  dynamicToolsContainDeferred: optionalBooleanSchema,
1449
1510
  webSearchThreadConfigFingerprint: optionalStringSchema,
1511
+ nativeSkillIsolationFingerprint: optionalStringSchema,
1450
1512
  userMcpServersFingerprint: optionalStringSchema,
1451
1513
  mcpServersFingerprint: optionalStringSchema,
1452
1514
  ringZeroConfigFingerprint: optionalStringSchema,
@@ -1585,6 +1647,22 @@ function createStoredCodexAppServerBinding(value, options = {}) {
1585
1647
  function bindingLeaseLostError(key, cause) {
1586
1648
  return new Error(`Lost Codex binding lease: ${key}`, cause === void 0 ? void 0 : { cause });
1587
1649
  }
1650
+ /** Carries one prepared run identity through callers that rederive it from public params. */
1651
+ function scopeCodexRunBindingStore(params) {
1652
+ const mapSessionIdentity = (identity) => identity.agentId === params.logicalIdentity.agentId && identity.sessionId === params.logicalIdentity.sessionId && identity.sessionKey?.trim() === params.logicalIdentity.sessionKey?.trim() ? params.physicalIdentity : identity;
1653
+ const mapIdentity = (identity) => identity.kind === "session" ? mapSessionIdentity(identity) : identity;
1654
+ return {
1655
+ read: (identity) => params.bindingStore.read(mapIdentity(identity)),
1656
+ hasOtherThreadOwner: (threadId, identity) => params.bindingStore.hasOtherThreadOwner(threadId, identity ? mapIdentity(identity) : void 0),
1657
+ mutate: (identity, mutation) => params.bindingStore.mutate(mapIdentity(identity), mutation),
1658
+ prepareSessionGenerationReclaim: (identity) => params.bindingStore.prepareSessionGenerationReclaim(mapSessionIdentity(identity)),
1659
+ adoptSessionGeneration: (identity, expectedPreviousSessionId) => params.bindingStore.adoptSessionGeneration(mapSessionIdentity(identity), expectedPreviousSessionId),
1660
+ resetSessionGeneration: (identity) => params.bindingStore.resetSessionGeneration(mapSessionIdentity(identity)),
1661
+ retireSessionGeneration: (identity) => params.bindingStore.retireSessionGeneration(mapSessionIdentity(identity)),
1662
+ withThreadArchiveFence: (run) => params.bindingStore.withThreadArchiveFence(run),
1663
+ withLease: (identity, run) => params.bindingStore.withLease(mapIdentity(identity), run)
1664
+ };
1665
+ }
1588
1666
  /** Lets the authoritative OpenClaw session generation claim a stale stable binding row. */
1589
1667
  async function reclaimCurrentCodexSessionGeneration(params) {
1590
1668
  const sessionKey = params.identity.sessionKey?.trim();
@@ -1592,7 +1670,7 @@ async function reclaimCurrentCodexSessionGeneration(params) {
1592
1670
  const plan = await params.bindingStore.prepareSessionGenerationReclaim(params.identity);
1593
1671
  if (plan.kind === "resolved") return plan.result;
1594
1672
  try {
1595
- const storePath = resolveStorePath(params.config?.session?.store, { agentId: params.identity.agentId });
1673
+ const storePath = params.storePath?.trim() || resolveStorePath(params.config?.session?.store, { agentId: params.identity.agentId });
1596
1674
  if (getSessionEntry({
1597
1675
  agentId: params.identity.agentId,
1598
1676
  hydrateSkillPromptRefs: false,
@@ -1727,10 +1805,17 @@ function createCodexAppServerBindingStore(state) {
1727
1805
  result: true
1728
1806
  };
1729
1807
  const currentSessionId = current.sessionId;
1730
- if (!currentSessionId || currentSessionId === identity.sessionId) return {
1808
+ if (!currentSessionId) return {
1731
1809
  kind: "resolved",
1732
1810
  result: current.state !== "cleared" || current.retired !== true
1733
1811
  };
1812
+ if (currentSessionId === identity.sessionId) return current.state === "cleared" && current.retired === true ? {
1813
+ kind: "verify",
1814
+ expectedPreviousSessionId: currentSessionId
1815
+ } : {
1816
+ kind: "resolved",
1817
+ result: true
1818
+ };
1734
1819
  return {
1735
1820
  kind: "verify",
1736
1821
  expectedPreviousSessionId: currentSessionId
@@ -1746,7 +1831,18 @@ function createCodexAppServerBindingStore(state) {
1746
1831
  if (mutation.kind === "reclaim-generation") {
1747
1832
  if (identity.kind !== "session" || !identity.sessionKey?.trim()) return { result: false };
1748
1833
  if (!current) return { result: true };
1749
- if (ownsGeneration) return { result: current.state !== "cleared" || current.retired !== true };
1834
+ if (ownsGeneration) {
1835
+ if (current.state === "cleared" && current.retired === true && current.sessionId === mutation.expectedPreviousSessionId) return {
1836
+ result: true,
1837
+ next: {
1838
+ version: 1,
1839
+ state: "cleared",
1840
+ sessionId: identity.sessionId,
1841
+ ...ownedLease
1842
+ }
1843
+ };
1844
+ return { result: current.state !== "cleared" || current.retired !== true };
1845
+ }
1750
1846
  if (current.sessionId !== mutation.expectedPreviousSessionId) return { result: false };
1751
1847
  if (current.state === "active" && current.binding.connectionScope === "supervision") return { result: false };
1752
1848
  return {
@@ -1825,6 +1921,25 @@ function createCodexAppServerBindingStore(state) {
1825
1921
  });
1826
1922
  });
1827
1923
  },
1924
+ async resetSessionGeneration(identity) {
1925
+ return await runBindingMutation(async () => {
1926
+ const key = bindingStoreKey(identity);
1927
+ return await transactKey(key, (current, leaseToken) => {
1928
+ if (!current) return { result: "absent" };
1929
+ if (!ownsStoredSessionGeneration(identity, current)) return { result: "conflict" };
1930
+ if (current.state === "cleared" && current.retired === true) return { result: "conflict" };
1931
+ return {
1932
+ result: "applied",
1933
+ next: {
1934
+ version: 1,
1935
+ state: "cleared",
1936
+ ...storedSessionGeneration(identity, current),
1937
+ ...current.lease && current.lease.token === leaseToken ? { lease: current.lease } : {}
1938
+ }
1939
+ };
1940
+ }, leaseContext.getStore()?.has(key) ? void 0 : 1);
1941
+ });
1942
+ },
1828
1943
  async retireSessionGeneration(identity) {
1829
1944
  return await runBindingMutation(async () => {
1830
1945
  const key = bindingStoreKey(identity);
@@ -1956,7 +2071,7 @@ function bindingStoreKey(identity) {
1956
2071
  const sessionId = identity.sessionId.trim();
1957
2072
  if (!rawAgentId) throw new Error("Codex app-server binding requires an agent id");
1958
2073
  if (!sessionId) throw new Error("Codex app-server binding requires a session id");
1959
- const agentId = resolveSessionAgentIds({ agentId: rawAgentId }).sessionAgentId;
2074
+ const agentId = resolveSessionAgentIds$1({ agentId: rawAgentId }).sessionAgentId;
1960
2075
  const sessionKey = identity.sessionKey?.trim();
1961
2076
  if (sessionKey) return `session-key:${agentId}:${createHash("sha256").update(sessionKey).digest("base64url")}`;
1962
2077
  return `session:${agentId}:${sessionId}`;
@@ -2096,4 +2211,4 @@ function resolveCodexAppServerBindingModelProvider(params) {
2096
2211
  return params.modelProvider?.trim() || (isCodexAppServerNativeAuthProfile(params) ? PUBLIC_OPENAI_MODEL_PROVIDER : void 0);
2097
2212
  }
2098
2213
  //#endregion
2099
- export { withMcpElicitationsApprovalPolicy as A, resolveCodexAppServerUserHomeDir as C, resolveCodexSupervisionAppServerRuntimeOptions as D, resolveCodexPluginsPolicy as E, resolveOpenClawExecPolicyForCodexAppServer as O, resolveCodexAppServerStartOptionsForAgent as S, resolveCodexModelBackedReviewerPolicyContext as T, isCodexAppServerApprovalPolicyAllowedByRequirements as _, hashCodexAppServerBindingFingerprint as a, readCodexPluginConfig as b, reclaimCurrentCodexSessionGeneration as c, CODEX_PLUGINS_MARKETPLACE_NAME as d, CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME as f, codexSandboxPolicyForTurn as g, codexAppServerStartOptionsKey as h, createCodexSessionGenerationSupersededError as i, shouldAutoApproveCodexAppServerApprovals as k, sessionBindingIdentity as l, canUseCodexModelBackedApprovalsReviewerForModel as m, assertCodexBindingMayBeReplaced as n, isCodexAppServerNativeAuthProfile as o, assertCodexAppServerConnectionSecurity as p, bindingStoreKey as r, normalizeCodexAppServerBindingModelProvider as s, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS as t, session_binding_exports as u, isCodexFastServiceTier as v, resolveCodexComputerUseConfig as w, resolveCodexAppServerRuntimeOptions as x, isCodexSandboxExecServerEnabled as y };
2214
+ export { resolveOpenClawExecPolicyForCodexAppServer as A, canUseCodexModelBackedApprovalsReviewerForModel as C, isCodexSandboxExecServerEnabled as D, isCodexAppServerApprovalPolicyAllowedByRequirements as E, CODEX_PLUGINS_MARKETPLACE_NAME as M, CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME as N, readCodexPluginConfig as O, withMcpElicitationsApprovalPolicy as S, resolveCodexModelBackedReviewerPolicyContext as T, resolveCodexAppServerStartOptionsForAgent as _, hashCodexAppServerBindingFingerprint as a, assertCodexAppServerConnectionSecurity as b, reclaimCurrentCodexSessionGeneration as c, sessionBindingIdentity as d, session_binding_exports as f, resolveCodexAppServerRuntimeOptions as g, resolveCodexAppServerHomeScope as h, createCodexSessionGenerationSupersededError as i, isCodexFastServiceTier as j, resolveCodexPluginsPolicy as k, resolveCodexRunSessionBindingAuthority as l, codexSandboxPolicyForTurn as m, assertCodexBindingMayBeReplaced as n, isCodexAppServerNativeAuthProfile as o, codexAppServerStartOptionsKey as p, bindingStoreKey as r, normalizeCodexAppServerBindingModelProvider as s, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS as t, scopeCodexRunBindingStore as u, resolveCodexComputerUseConfig as v, resolveCodexAppServerUserHomeDir as w, shouldAutoApproveCodexAppServerApprovals as x, resolveCodexSupervisionAppServerRuntimeOptions as y };