@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
@@ -1,10 +1,10 @@
1
- import { Q as isJsonObject, R as CodexAppServerRpcError, a as getLeasedSharedCodexAppServerClient, c as releaseLeasedSharedCodexAppServerClient } from "./shared-client-wmgNFNYp.js";
2
- import { _ as readCodexNotificationItem } from "./attempt-notifications-DdmnBMUp.js";
3
- import { l as sessionBindingIdentity, t as CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS } from "./session-binding-ZSMX1omP.js";
4
- import { n as resolveCodexNativeExecutionBlock } from "./sandbox-guard-D1Wky__S.js";
5
- import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-BGLixv5_.js";
6
- import { resolveAgentDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
1
+ import { W as isJsonObject, a as getLeasedSharedCodexAppServerClient, l as releaseLeasedSharedCodexAppServerClient, v as CodexAppServerRpcError } from "./shared-client-DTbvH-XV.js";
2
+ import { d as sessionBindingIdentity, t as CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS } from "./session-binding-BXpo9ymA.js";
3
+ import { _ as readCodexNotificationItem } from "./attempt-notifications-AP-a-Q5k.js";
4
+ import { n as resolveCodexNativeExecutionBlock } from "./sandbox-guard-NuhpXmPB.js";
5
+ import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-CU6_AnOb.js";
7
6
  import { embeddedAgentLog, resolveCompactionTimeoutMs } from "openclaw/plugin-sdk/agent-harness-runtime";
7
+ import { resolveAgentDir as resolveAgentDir$1, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
8
8
  //#region extensions/codex/src/app-server/compact.ts
9
9
  /**
10
10
  * Native Codex app-server compaction bridge for bound OpenClaw sessions.
@@ -353,7 +353,7 @@ async function compactCodexNativeThread(params, options) {
353
353
  authProfileId: requestedAuthProfileId ?? binding.authProfileId,
354
354
  pluginConfig: options.pluginConfig,
355
355
  config,
356
- agentDir: resolveAgentDir(config, agentId)
356
+ agentDir: resolveAgentDir$1(config, agentId)
357
357
  });
358
358
  } catch (error) {
359
359
  return {
@@ -371,7 +371,7 @@ async function compactCodexNativeThread(params, options) {
371
371
  const shouldReleaseDefaultLease = !options.clientFactory;
372
372
  const clientFactory = options.clientFactory ?? getLeasedSharedCodexAppServerClient;
373
373
  const runtimeAuthPlan = params.runtimeAuthPlan ?? params.runtimePlan?.auth;
374
- const usesPreparedApiKey = !usesSupervisionConnection && runtimeAuthPlan?.modelRoute?.authRequirement === "api-key";
374
+ const usesPreparedApiKey = !usesSupervisionConnection && appServer.start.homeScope !== "user" && runtimeAuthPlan?.modelRoute?.authRequirement === "api-key";
375
375
  const preparedApiKey = usesPreparedApiKey ? params.resolvedApiKey?.trim() : void 0;
376
376
  if (usesPreparedApiKey && !preparedApiKey) return {
377
377
  ok: false,
@@ -1,9 +1,91 @@
1
- import { E as resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath, H as isCodexAppServerConnectionClosedError, U as isCodexAppServerIndeterminateRequestCancellationError, W as isCodexAppServerIndeterminateTransportError, a as getLeasedSharedCodexAppServerClient, c as releaseLeasedSharedCodexAppServerClient, h as acquireCodexNativeConfigFence, l as resolveCodexNativeConfigFenceKey } from "./shared-client-wmgNFNYp.js";
2
- import { w as resolveCodexComputerUseConfig, x as resolveCodexAppServerRuntimeOptions } from "./session-binding-ZSMX1omP.js";
3
- import { n as describeControlFailure } from "./capabilities-CLICvTxf.js";
4
- import { r as requestCodexAppServerJson } from "./request-BTccUTD3.js";
1
+ import { C as isCodexAppServerIndeterminateRequestCancellationError, G as resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath, S as isCodexAppServerConnectionClosedError, _ as acquireCodexNativeConfigFence, a as getLeasedSharedCodexAppServerClient, l as releaseLeasedSharedCodexAppServerClient, u as resolveCodexNativeConfigFenceKey, w as isCodexAppServerIndeterminateTransportError } from "./shared-client-DTbvH-XV.js";
2
+ import { g as resolveCodexAppServerRuntimeOptions, v as resolveCodexComputerUseConfig } from "./session-binding-BXpo9ymA.js";
3
+ import { n as describeControlFailure } from "./capabilities-BFDTVYr4.js";
4
+ import { r as requestCodexAppServerJson } from "./request-qKh331yu.js";
5
5
  import { existsSync } from "node:fs";
6
6
  import { runExec } from "openclaw/plugin-sdk/process-runtime";
7
+ //#region extensions/codex/src/app-server/computer-use-process-repair.ts
8
+ const COMPUTER_USE_PROCESS_LIST_TIMEOUT_MS = 2e3;
9
+ const COMPUTER_USE_PROCESS_LIST_MAX_BUFFER_BYTES = 5 * 1024 * 1024;
10
+ function scopedRepairUnavailableStatus() {
11
+ return {
12
+ attempted: false,
13
+ killedPids: [],
14
+ warnings: ["Computer Use auto-repair skipped because no scoped Codex app-server process was available."],
15
+ message: "Computer Use stale child repair requires a scoped local app-server PID."
16
+ };
17
+ }
18
+ async function killStaleComputerUseMcpChildren(options = {}) {
19
+ if (process.platform !== "darwin") return {
20
+ attempted: true,
21
+ killedPids: [],
22
+ warnings: [`Computer Use stale child repair is currently macOS-only, not ${process.platform}.`],
23
+ message: "Computer Use stale child repair skipped on this platform."
24
+ };
25
+ if (!options.ancestorPid || !Number.isSafeInteger(options.ancestorPid) || options.ancestorPid <= 0) return scopedRepairUnavailableStatus();
26
+ let stdout;
27
+ try {
28
+ stdout = (await runExec("/bin/ps", ["-axo", "pid=,ppid=,command="], {
29
+ logOutput: false,
30
+ maxBuffer: COMPUTER_USE_PROCESS_LIST_MAX_BUFFER_BYTES,
31
+ timeoutMs: COMPUTER_USE_PROCESS_LIST_TIMEOUT_MS
32
+ })).stdout;
33
+ } catch (error) {
34
+ return {
35
+ attempted: true,
36
+ killedPids: [],
37
+ warnings: [`Could not list processes for Computer Use repair: ${describeControlFailure(error)}`],
38
+ message: "Computer Use stale child repair could not inspect running processes."
39
+ };
40
+ }
41
+ const killedPids = [];
42
+ const warnings = [];
43
+ const processInfos = parsePsOutput(stdout);
44
+ for (const processInfo of processInfos) {
45
+ if (!isStaleComputerUseMcpChild(processInfo.command)) continue;
46
+ if (!isDescendantOfPid(processInfo.pid, options.ancestorPid, processInfos)) continue;
47
+ try {
48
+ process.kill(processInfo.pid, "SIGTERM");
49
+ killedPids.push(processInfo.pid);
50
+ } catch (error) {
51
+ warnings.push(`Could not terminate stale Computer Use MCP child pid ${processInfo.pid}: ${describeControlFailure(error)}`);
52
+ }
53
+ }
54
+ return {
55
+ attempted: true,
56
+ killedPids,
57
+ warnings,
58
+ message: killedPids.length === 0 ? "No stale Computer Use MCP children were found under the scoped Codex app-server process." : `Terminated ${killedPids.length} stale Computer Use MCP child process${killedPids.length === 1 ? "" : "es"} under the scoped Codex app-server process.`
59
+ };
60
+ }
61
+ function parsePsOutput(stdout) {
62
+ return stdout.split(/\r?\n/u).flatMap((line) => {
63
+ const match = /^\s*(\d+)\s+(\d+)\s+(.+)$/u.exec(line);
64
+ if (!match) return [];
65
+ return [{
66
+ pid: Number(match[1]),
67
+ ppid: Number(match[2]),
68
+ command: match[3] ?? ""
69
+ }];
70
+ }).filter((processInfo) => Number.isSafeInteger(processInfo.pid) && processInfo.pid > 0 && Number.isSafeInteger(processInfo.ppid) && processInfo.ppid >= 0);
71
+ }
72
+ function isStaleComputerUseMcpChild(command) {
73
+ return command.includes("SkyComputerUseClient") && /(?:^|\s)mcp(?:\s|$)/u.test(command);
74
+ }
75
+ function isDescendantOfPid(pid, ancestorPid, processInfos) {
76
+ const parents = new Map(processInfos.map((processInfo) => [processInfo.pid, processInfo.ppid]));
77
+ const seen = /* @__PURE__ */ new Set();
78
+ let current = pid;
79
+ while (!seen.has(current)) {
80
+ seen.add(current);
81
+ const parent = parents.get(current);
82
+ if (!parent || parent <= 0) return false;
83
+ if (parent === ancestorPid) return true;
84
+ current = parent;
85
+ }
86
+ return false;
87
+ }
88
+ //#endregion
7
89
  //#region extensions/codex/src/app-server/computer-use.ts
8
90
  /**
9
91
  * Computer Use plugin/MCP readiness checks and optional install flow for Codex
@@ -20,6 +102,8 @@ const CURATED_MARKETPLACE_POLL_INTERVAL_MS = 2e3;
20
102
  const COMPUTER_USE_MARKETPLACE_NAME_PRIORITY = [
21
103
  "openai-bundled",
22
104
  "openai-curated",
105
+ "openai-api-curated",
106
+ "openai-curated-remote",
23
107
  "local"
24
108
  ];
25
109
  const COMPUTER_USE_LIVE_TEST_RETRY_COUNT = 1;
@@ -113,6 +197,7 @@ async function inspectCodexComputerUse(params) {
113
197
  if (!runtime) throw new Error("Computer Use install could not resolve its app-server runtime");
114
198
  client = await getLeasedSharedCodexAppServerClient({
115
199
  startOptions: runtime.start,
200
+ pluginConfig: params.pluginConfig,
116
201
  timeoutMs: params.timeoutMs ?? runtime.requestTimeoutMs,
117
202
  config: params.config,
118
203
  agentDir: params.agentDir,
@@ -178,18 +263,8 @@ async function ensureComputerUsePlugin(params) {
178
263
  config: params.config,
179
264
  plugin,
180
265
  tools: [],
181
- reason: pluginSetupReason(plugin, params.marketplace),
182
- message: pluginSetupMessage(params.config, plugin, params.marketplace)
183
- })
184
- };
185
- if (params.marketplace.kind === "remote") return {
186
- ok: false,
187
- status: statusFromPlugin({
188
- config: params.config,
189
- plugin,
190
- tools: [],
191
- reason: "remote_install_unsupported",
192
- message: remoteInstallUnsupportedMessage(plugin, params.marketplace)
266
+ reason: pluginSetupReason(plugin),
267
+ message: pluginSetupMessage(params.config, plugin)
193
268
  })
194
269
  };
195
270
  await params.request("plugin/install", pluginRequestParams(params.marketplace, params.config.pluginName));
@@ -202,8 +277,8 @@ async function ensureComputerUsePlugin(params) {
202
277
  config: params.config,
203
278
  plugin,
204
279
  tools: [],
205
- reason: pluginSetupReason(plugin, params.marketplace),
206
- message: pluginSetupMessage(params.config, plugin, params.marketplace)
280
+ reason: pluginSetupReason(plugin),
281
+ message: pluginSetupMessage(params.config, plugin)
207
282
  })
208
283
  };
209
284
  return {
@@ -213,9 +288,11 @@ async function ensureComputerUsePlugin(params) {
213
288
  }
214
289
  async function readComputerUseTools(params) {
215
290
  let server = await readMcpServerStatus(params.request, params.config.mcpServerName);
216
- if (!server && params.installPlugin) {
291
+ let tools = Object.keys(server?.tools ?? {}).toSorted();
292
+ if ((!server || tools.length === 0) && params.installPlugin) {
217
293
  await reloadMcpServers(params.request);
218
294
  server = await readMcpServerStatus(params.request, params.config.mcpServerName);
295
+ tools = Object.keys(server?.tools ?? {}).toSorted();
219
296
  }
220
297
  if (!server) return statusFromPlugin({
221
298
  config: params.config,
@@ -224,10 +301,17 @@ async function readComputerUseTools(params) {
224
301
  reason: "mcp_missing",
225
302
  message: `Computer Use is installed, but the ${params.config.mcpServerName} MCP server is not available.`
226
303
  });
304
+ if (tools.length === 0) return statusFromPlugin({
305
+ config: params.config,
306
+ plugin: params.plugin,
307
+ tools,
308
+ reason: "mcp_missing",
309
+ message: `Computer Use is installed, but the ${params.config.mcpServerName} MCP server exposes no tools.`
310
+ });
227
311
  const status = statusFromPlugin({
228
312
  config: params.config,
229
313
  plugin: params.plugin,
230
- tools: Object.keys(server.tools).toSorted(),
314
+ tools,
231
315
  reason: "ready",
232
316
  message: "Computer Use is ready."
233
317
  });
@@ -316,14 +400,6 @@ async function runCodexComputerUseLiveTest(params) {
316
400
  async function cleanupComputerUseProbeThread(request, threadId, config) {
317
401
  await Promise.allSettled([request("thread/unsubscribe", { threadId }, { timeoutMs: config.liveTestTimeoutMs }), request("thread/archive", { threadId }, { timeoutMs: config.liveTestTimeoutMs })]);
318
402
  }
319
- function scopedRepairUnavailableStatus() {
320
- return {
321
- attempted: false,
322
- killedPids: [],
323
- warnings: ["Computer Use auto-repair skipped because no scoped Codex app-server process was available."],
324
- message: "Computer Use stale child repair requires a scoped local app-server PID."
325
- };
326
- }
327
403
  async function resolveMarketplaceRef(params) {
328
404
  let preferredMarketplaceName = params.config.marketplaceName;
329
405
  if (params.config.marketplaceSource && params.allowAdd) {
@@ -380,17 +456,22 @@ function resolveBundledComputerUseMarketplacePath(params) {
380
456
  return resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath({ candidates: params.defaultBundledMarketplacePathCandidates });
381
457
  }
382
458
  function findComputerUseMarketplaces(listed, pluginName) {
383
- return listed.marketplaces.filter((marketplace) => marketplace.plugins.some((plugin) => plugin.name === pluginName || plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`)).map((marketplace) => {
384
- if (marketplace.path) return {
459
+ return listed.marketplaces.flatMap((marketplace) => {
460
+ const plugin = marketplace.plugins.find((candidate) => candidate.name === pluginName || candidate.id === pluginName || candidate.id === `${pluginName}@${marketplace.name}`);
461
+ if (!plugin) return [];
462
+ if (marketplace.path) return [{
385
463
  kind: "local",
386
464
  name: marketplace.name,
387
465
  path: marketplace.path
388
- };
389
- return {
466
+ }];
467
+ const remotePluginId = plugin.remotePluginId?.trim();
468
+ if (!remotePluginId) return [];
469
+ return [{
390
470
  kind: "remote",
391
471
  name: marketplace.name,
392
- remoteMarketplaceName: marketplace.name
393
- };
472
+ remoteMarketplaceName: marketplace.name,
473
+ remotePluginId
474
+ }];
394
475
  });
395
476
  }
396
477
  function chooseKnownComputerUseMarketplace(candidates) {
@@ -442,25 +523,21 @@ async function reloadMcpServers(request) {
442
523
  await request("config/mcpServer/reload", void 0);
443
524
  }
444
525
  function pluginRequestParams(marketplace, pluginName) {
445
- return {
446
- ...marketplace.kind === "local" ? { marketplacePath: marketplace.path } : {},
447
- ...marketplace.kind === "remote" ? { remoteMarketplaceName: marketplace.remoteMarketplaceName } : {},
526
+ return marketplace.kind === "local" ? {
527
+ marketplacePath: marketplace.path,
448
528
  pluginName
529
+ } : {
530
+ remoteMarketplaceName: marketplace.remoteMarketplaceName,
531
+ pluginName: marketplace.remotePluginId
449
532
  };
450
533
  }
451
- function pluginSetupReason(plugin, marketplace) {
452
- if (marketplace.kind === "remote") return "remote_install_unsupported";
534
+ function pluginSetupReason(plugin) {
453
535
  return plugin.summary.installed ? "plugin_disabled" : "plugin_not_installed";
454
536
  }
455
- function pluginSetupMessage(config, plugin, marketplace) {
456
- if (marketplace.kind === "remote") return remoteInstallUnsupportedMessage(plugin, marketplace);
537
+ function pluginSetupMessage(config, plugin) {
457
538
  if (!plugin.summary.installed) return "Computer Use is available but not installed. Run /codex computer-use install or enable computerUse.autoInstall.";
458
539
  return `Computer Use is installed, but the ${config.pluginName} plugin is disabled. Run /codex computer-use install or enable computerUse.autoInstall to re-enable it.`;
459
540
  }
460
- function remoteInstallUnsupportedMessage(plugin, marketplace) {
461
- const marketplaceName = marketplace.name ?? plugin.marketplaceName;
462
- return `Computer Use is ${plugin.summary.installed ? "installed but disabled" : "available"} in remote Codex marketplace ${marketplaceName}, but Codex app-server does not support remote plugin install yet. Configure computerUse.marketplaceSource or computerUse.marketplacePath for a local marketplace, then run /codex computer-use install.`;
463
- }
464
541
  function statusFromPlugin(params) {
465
542
  return {
466
543
  enabled: true,
@@ -582,75 +659,6 @@ function pluginWarnings(plugin) {
582
659
  if (source && typeof source === "object" && "type" in source && source.type === "remote") warnings.push("Computer Use plugin is resolved from a remote marketplace; live local bundles are preferred.");
583
660
  return warnings;
584
661
  }
585
- async function killStaleComputerUseMcpChildren(options = {}) {
586
- if (process.platform !== "darwin") return {
587
- attempted: true,
588
- killedPids: [],
589
- warnings: [`Computer Use stale child repair is currently macOS-only, not ${process.platform}.`],
590
- message: "Computer Use stale child repair skipped on this platform."
591
- };
592
- if (!options.ancestorPid || !Number.isSafeInteger(options.ancestorPid) || options.ancestorPid <= 0) return scopedRepairUnavailableStatus();
593
- let stdout;
594
- try {
595
- stdout = (await runExec("/bin/ps", ["-axo", "pid=,ppid=,command="], {
596
- logOutput: false,
597
- maxBuffer: 5 * 1024 * 1024
598
- })).stdout;
599
- } catch (error) {
600
- return {
601
- attempted: true,
602
- killedPids: [],
603
- warnings: [`Could not list processes for Computer Use repair: ${describeControlFailure(error)}`],
604
- message: "Computer Use stale child repair could not inspect running processes."
605
- };
606
- }
607
- const killedPids = [];
608
- const warnings = [];
609
- const processInfos = parsePsOutput(stdout);
610
- for (const processInfo of processInfos) {
611
- if (!isStaleComputerUseMcpChild(processInfo.command)) continue;
612
- if (!isDescendantOfPid(processInfo.pid, options.ancestorPid, processInfos)) continue;
613
- try {
614
- process.kill(processInfo.pid, "SIGTERM");
615
- killedPids.push(processInfo.pid);
616
- } catch (error) {
617
- warnings.push(`Could not terminate stale Computer Use MCP child pid ${processInfo.pid}: ${describeControlFailure(error)}`);
618
- }
619
- }
620
- return {
621
- attempted: true,
622
- killedPids,
623
- warnings,
624
- message: killedPids.length === 0 ? "No stale Computer Use MCP children were found under the scoped Codex app-server process." : `Terminated ${killedPids.length} stale Computer Use MCP child process${killedPids.length === 1 ? "" : "es"} under the scoped Codex app-server process.`
625
- };
626
- }
627
- function parsePsOutput(stdout) {
628
- return stdout.split(/\r?\n/u).flatMap((line) => {
629
- const match = /^\s*(\d+)\s+(\d+)\s+(.+)$/u.exec(line);
630
- if (!match) return [];
631
- return [{
632
- pid: Number(match[1]),
633
- ppid: Number(match[2]),
634
- command: match[3] ?? ""
635
- }];
636
- }).filter((processInfo) => Number.isSafeInteger(processInfo.pid) && processInfo.pid > 0 && Number.isSafeInteger(processInfo.ppid) && processInfo.ppid >= 0);
637
- }
638
- function isStaleComputerUseMcpChild(command) {
639
- return command.includes("SkyComputerUseClient") && /(?:^|\s)mcp(?:\s|$)/u.test(command);
640
- }
641
- function isDescendantOfPid(pid, ancestorPid, processInfos) {
642
- const parents = new Map(processInfos.map((processInfo) => [processInfo.pid, processInfo.ppid]));
643
- const seen = /* @__PURE__ */ new Set();
644
- let current = pid;
645
- while (!seen.has(current)) {
646
- seen.add(current);
647
- const parent = parents.get(current);
648
- if (!parent || parent <= 0) return false;
649
- if (parent === ancestorPid) return true;
650
- current = parent;
651
- }
652
- return false;
653
- }
654
662
  function createComputerUseRequest(params) {
655
663
  if (params.request) return params.request;
656
664
  if (params.client) return async (method, requestParams, options) => await params.client.request(method, requestParams, {
@@ -682,4 +690,4 @@ function resolveComputerUseConfig(params) {
682
690
  });
683
691
  }
684
692
  //#endregion
685
- export { runCodexComputerUseLiveTest as a, readCodexComputerUseStatus as i, installCodexComputerUse as n, killStaleComputerUseMcpChildren as r, ensureCodexComputerUse as t };
693
+ export { killStaleComputerUseMcpChildren as a, runCodexComputerUseLiveTest as i, installCodexComputerUse as n, readCodexComputerUseStatus as r, ensureCodexComputerUse as t };
@@ -1,9 +1,9 @@
1
- import { a as getLeasedSharedCodexAppServerClient, c as releaseLeasedSharedCodexAppServerClient, p as withLeasedCodexAppServerClientStartSelectionRetry, s as releaseCodexAppServerClientLease } from "./shared-client-wmgNFNYp.js";
2
- import { T as resolveCodexModelBackedReviewerPolicyContext, o as isCodexAppServerNativeAuthProfile, r as bindingStoreKey, s as normalizeCodexAppServerBindingModelProvider, v as isCodexFastServiceTier, x as resolveCodexAppServerRuntimeOptions } from "./session-binding-ZSMX1omP.js";
3
- import { x as resolveCodexBindingModelProviderFallback, y as resolveCodexAppServerRequestModelSelection } from "./thread-lifecycle-cqrOlJkq.js";
4
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-CLICvTxf.js";
5
- import { r as formatCodexDisplayText } from "./command-formatters-DgADqnHa.js";
6
- import { a as resolveCodexBindingAppServerConnection } from "./notification-correlation-BGLixv5_.js";
1
+ import { a as getLeasedSharedCodexAppServerClient, c as releaseCodexAppServerClientLease, h as withLeasedCodexAppServerClientStartSelectionRetry, l as releaseLeasedSharedCodexAppServerClient } from "./shared-client-DTbvH-XV.js";
2
+ import { T as resolveCodexModelBackedReviewerPolicyContext, g as resolveCodexAppServerRuntimeOptions, j as isCodexFastServiceTier, o as isCodexAppServerNativeAuthProfile, r as bindingStoreKey, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-BXpo9ymA.js";
3
+ import { S as resolveCodexBindingModelProviderFallback, b as resolveCodexAppServerRequestModelSelection } from "./thread-lifecycle-DF53ogiA.js";
4
+ import { t as CODEX_CONTROL_METHODS } from "./capabilities-BFDTVYr4.js";
5
+ import { r as formatCodexDisplayText } from "./command-formatters-C336eRVS.js";
6
+ import { a as resolveCodexBindingAppServerConnection } from "./notification-correlation-CU6_AnOb.js";
7
7
  import { ModelSelectionLockedError } from "openclaw/plugin-sdk/model-session-runtime";
8
8
  //#region extensions/codex/src/command-authorization.ts
9
9
  const CODEX_NATIVE_EXECUTION_AUTH_ERROR = "Only an owner or operator.admin can control Codex native execution.";
@@ -3,37 +3,11 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { listAgentIds, resolveAgentDir, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
5
5
  import { isDeepStrictEqual } from "node:util";
6
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
6
7
  import { patchSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
8
+ import { canonicalPathFromExistingAncestor, isPathInside, pathExists } from "openclaw/plugin-sdk/security-runtime";
7
9
  import { withFileLock } from "openclaw/plugin-sdk/file-lock";
8
- //#region extensions/codex/src/migration/session-binding-sidecar-archive.ts
9
- async function pathExists$1(filePath) {
10
- try {
11
- await fs.access(filePath);
12
- return true;
13
- } catch {
14
- return false;
15
- }
16
- }
17
- async function firstFreeArchivePath(sourcePath) {
18
- for (let index = 2;; index++) {
19
- const candidate = `${sourcePath}.migrated.${index}`;
20
- if (!await pathExists$1(candidate)) return candidate;
21
- }
22
- }
23
- async function archiveBindingSidecar(sourcePath) {
24
- const archivePath = `${sourcePath}.migrated`;
25
- if (!await pathExists$1(archivePath)) {
26
- await fs.rename(sourcePath, archivePath);
27
- return;
28
- }
29
- const [sourceBytes, archiveBytes] = await Promise.all([fs.readFile(sourcePath), fs.readFile(archivePath)]);
30
- if (sourceBytes.equals(archiveBytes)) {
31
- await fs.rm(sourcePath, { force: true });
32
- return;
33
- }
34
- await fs.rename(sourcePath, await firstFreeArchivePath(sourcePath));
35
- }
36
- //#endregion
10
+ import { archiveLegacyStateSource, legacyStateFileExists } from "openclaw/plugin-sdk/runtime-doctor";
37
11
  //#region extensions/codex/src/migration/session-binding-sidecars.ts
38
12
  const LEGACY_BINDING_SUFFIX = ".codex-app-server.json";
39
13
  const CODEX_AGENT_HARNESS_ID = "codex";
@@ -49,9 +23,9 @@ const LEGACY_BINDING_LOCK_OPTIONS = {
49
23
  };
50
24
  async function collectSessionSurfaces(params) {
51
25
  const surfaces = /* @__PURE__ */ new Map();
52
- const stateRoot = await canonicalizePath(params.stateDir);
26
+ const stateRoot = await canonicalPathFromExistingAncestor(params.stateDir);
53
27
  const add = async (root, storePath, agentId, scan) => {
54
- const canonicalRoot = await canonicalizePath(root);
28
+ const canonicalRoot = await canonicalPathFromExistingAncestor(root);
55
29
  const surface = surfaces.get(canonicalRoot) ?? {
56
30
  root: canonicalRoot,
57
31
  scan: false,
@@ -81,7 +55,7 @@ async function collectSessionSurfaces(params) {
81
55
  env: params.env
82
56
  });
83
57
  const root = path.dirname(storePath);
84
- await add(root, storePath, agentId, isPathWithin(stateRoot, await canonicalizePath(root)));
58
+ await add(root, storePath, agentId, isPathInside(stateRoot, await canonicalPathFromExistingAncestor(root)));
85
59
  }
86
60
  const legacyRoot = path.join(params.stateDir, "sessions");
87
61
  const defaultAgentId = resolveSessionAgentIds({ config: params.config }).defaultAgentId;
@@ -92,7 +66,7 @@ async function collectLegacyBindingSources(params, options = {}) {
92
66
  const surfaces = await collectSessionSurfaces(params);
93
67
  const sources = /* @__PURE__ */ new Map();
94
68
  const addSource = async (sidecarPath, surface) => {
95
- const canonicalSidecar = await canonicalizePath(sidecarPath);
69
+ const canonicalSidecar = await canonicalPathFromExistingAncestor(sidecarPath);
96
70
  const source = sources.get(canonicalSidecar) ?? {
97
71
  sidecarPath: canonicalSidecar,
98
72
  transcriptPath: sidecarPath.slice(0, -22),
@@ -167,7 +141,7 @@ async function* iterateIndexedSidecars(surface) {
167
141
  continue;
168
142
  }
169
143
  const sidecarPath = `${transcriptPath}${LEGACY_BINDING_SUFFIX}`;
170
- if (await isRegularFile(sidecarPath)) yield sidecarPath;
144
+ if (await legacyStateFileExists(sidecarPath)) yield sidecarPath;
171
145
  }
172
146
  }
173
147
  }
@@ -190,7 +164,7 @@ async function* walkSidecars(root) {
190
164
  }
191
165
  }
192
166
  async function collectBindingOwners(sources, surfaces, params) {
193
- const sourcePaths = new Set(await Promise.all(sources.map((source) => canonicalizePath(source.transcriptPath))));
167
+ const sourcePaths = new Set(await Promise.all(sources.map((source) => canonicalPathFromExistingAncestor(source.transcriptPath))));
194
168
  const owners = /* @__PURE__ */ new Map();
195
169
  const storePaths = new Set(surfaces.flatMap((surface) => [...surface.storePaths]));
196
170
  const storeAgentIds = /* @__PURE__ */ new Map();
@@ -201,7 +175,7 @@ async function collectBindingOwners(sources, surfaces, params) {
201
175
  }
202
176
  const failures = [];
203
177
  for (const storePath of storePaths) {
204
- const canonicalStorePath = await canonicalizePath(storePath);
178
+ const canonicalStorePath = await canonicalPathFromExistingAncestor(storePath);
205
179
  const index = await readLegacySessionIndex(storePath);
206
180
  if ("failure" in index) {
207
181
  failures.push(index.failure);
@@ -219,7 +193,7 @@ async function collectBindingOwners(sources, surfaces, params) {
219
193
  let canonicalLegacyTranscriptPath;
220
194
  try {
221
195
  legacyTranscriptPath = await resolveLegacySessionFileLocator(sessionsDir, entry, sessionId);
222
- canonicalLegacyTranscriptPath = await canonicalizePath(legacyTranscriptPath);
196
+ canonicalLegacyTranscriptPath = await canonicalPathFromExistingAncestor(legacyTranscriptPath);
223
197
  } catch {
224
198
  failures.push(`session index ${storePath} has an invalid locator for ${sessionKey}`);
225
199
  continue;
@@ -256,8 +230,8 @@ async function resolveLegacySessionFileLocator(sessionsDir, entry, sessionId) {
256
230
  const sessionFile = entry.sessionFile?.trim();
257
231
  if (!sessionFile) return fallback;
258
232
  const candidate = path.resolve(base, sessionFile);
259
- const [canonicalBase, canonicalCandidate] = await Promise.all([canonicalizePathForContainment(base), canonicalizePathForContainment(candidate)]);
260
- if (!isPathWithin(canonicalBase, canonicalCandidate)) throw new Error("legacy session file locator escapes its session directory");
233
+ const [canonicalBase, canonicalCandidate] = await Promise.all([canonicalPathFromExistingAncestor(base), canonicalPathFromExistingAncestor(candidate)]);
234
+ if (!isPathInside(canonicalBase, canonicalCandidate)) throw new Error("legacy session file locator escapes its session directory");
261
235
  return candidate;
262
236
  }
263
237
  function resolveLegacyBindingOwnerAgentId(params) {
@@ -302,7 +276,7 @@ async function migrateSource(source, candidates, params, store) {
302
276
  return await withFileLock(source.sidecarPath, LEGACY_BINDING_LOCK_OPTIONS, async () => {
303
277
  const [contents, stat] = await Promise.all([fs.readFile(source.sidecarPath, "utf8"), fs.stat(source.sidecarPath)]);
304
278
  const raw = JSON.parse(contents);
305
- const [{ bindingStoreKey, createStoredCodexAppServerBinding, normalizeStoredCodexAppServerBindingFingerprints, readStoredCodexAppServerBinding }, { legacyCodexConversationBindingId }] = await Promise.all([import("./session-binding-ZSMX1omP.js").then((n) => n.u), import("./conversation-binding-data-BHRpYbX3.js").then((n) => n.t)]);
279
+ const [{ bindingStoreKey, createStoredCodexAppServerBinding, normalizeStoredCodexAppServerBindingFingerprints, readStoredCodexAppServerBinding }, { legacyCodexConversationBindingId }] = await Promise.all([import("./session-binding-BXpo9ymA.js").then((n) => n.f), import("./conversation-binding-data-BHRpYbX3.js").then((n) => n.t)]);
306
280
  const agentId = owner?.agentId ?? (source.agentIds.size === 1 ? [...source.agentIds][0] : void 0);
307
281
  const baseStored = createStoredCodexAppServerBinding(raw, {
308
282
  now: stat.mtime.toISOString(),
@@ -402,7 +376,14 @@ async function migrateSource(source, candidates, params, store) {
402
376
  }
403
377
  for (const entry of entries) if (!hasExpected(await store.lookup(entry.key), entry.value)) return retain(`canonical plugin state changed at ${entry.key}`);
404
378
  }
405
- await archiveBindingSidecar(source.sidecarPath);
379
+ const archiveWarnings = [];
380
+ await archiveLegacyStateSource({
381
+ filePath: source.sidecarPath,
382
+ label: "Codex app-server binding",
383
+ changes: [],
384
+ warnings: archiveWarnings
385
+ });
386
+ if (archiveWarnings.length > 0 && await pathExists(source.sidecarPath)) return retain(archiveWarnings.join("; "));
406
387
  return {
407
388
  archived: true,
408
389
  importedKeys
@@ -427,7 +408,7 @@ async function recordSessionOwner(owner, env) {
427
408
  } catch {
428
409
  return "its session owner changed before Codex ownership could be recorded";
429
410
  }
430
- if (await canonicalizePath(currentTranscriptPath) !== await canonicalizePath(owner.transcriptPath) || currentOwner.entry.lifecycleRevision !== owner.lifecycleRevision) return "its session owner changed before Codex ownership could be recorded";
411
+ if (await canonicalPathFromExistingAncestor(currentTranscriptPath) !== await canonicalPathFromExistingAncestor(owner.transcriptPath) || currentOwner.entry.lifecycleRevision !== owner.lifecycleRevision) return "its session owner changed before Codex ownership could be recorded";
431
412
  const legacyHarnessId = currentOwner.entry.agentHarnessId?.trim();
432
413
  if (currentOwner.entry.agentHarnessId !== void 0 && !legacyHarnessId) return "its session owner changed before Codex ownership could be recorded";
433
414
  if (legacyHarnessId && legacyHarnessId !== CODEX_AGENT_HARNESS_ID) return `its session is owned by agent harness ${legacyHarnessId}`;
@@ -474,54 +455,11 @@ async function readDirectoryEntries(directory) {
474
455
  throw error;
475
456
  }
476
457
  }
477
- async function isRegularFile(filePath) {
478
- try {
479
- return (await fs.stat(filePath)).isFile();
480
- } catch {
481
- return false;
482
- }
483
- }
484
- function isRecord(value) {
485
- return value !== null && typeof value === "object" && !Array.isArray(value);
486
- }
487
458
  function isSafeLegacySessionId(value) {
488
459
  if (typeof value !== "string") return false;
489
460
  const trimmed = value.trim();
490
461
  return trimmed.length > 0 && trimmed.length <= 255 && /^[A-Za-z0-9][A-Za-z0-9._:@-]*$/.test(trimmed);
491
462
  }
492
- function isPathWithin(root, candidate) {
493
- const relative = path.relative(root, candidate);
494
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
495
- }
496
- async function canonicalizePath(filePath) {
497
- try {
498
- return await fs.realpath(filePath);
499
- } catch {
500
- return path.resolve(filePath);
501
- }
502
- }
503
- async function canonicalizePathForContainment(filePath) {
504
- const resolved = path.resolve(filePath);
505
- const suffix = [];
506
- let probe = resolved;
507
- while (true) try {
508
- const realProbe = await fs.realpath(probe);
509
- return suffix.length === 0 ? realProbe : path.join(realProbe, ...suffix.toReversed());
510
- } catch {
511
- const parent = path.dirname(probe);
512
- if (parent === probe) return resolved;
513
- suffix.push(path.basename(probe));
514
- probe = parent;
515
- }
516
- }
517
- async function pathExists(filePath) {
518
- try {
519
- await fs.access(filePath);
520
- return true;
521
- } catch {
522
- return false;
523
- }
524
- }
525
463
  const stateMigrations = [{
526
464
  id: "codex-app-server-sidecars-to-plugin-state",
527
465
  label: "Codex app-server thread bindings",
@@ -554,7 +492,7 @@ const stateMigrations = [{
554
492
  let migrated = 0;
555
493
  let partialImports = 0;
556
494
  for (const source of sources) {
557
- const result = await migrateSource(source, ownerCollection.owners.get(await canonicalizePath(source.transcriptPath)) ?? [], params, store);
495
+ const result = await migrateSource(source, ownerCollection.owners.get(await canonicalPathFromExistingAncestor(source.transcriptPath)) ?? [], params, store);
558
496
  if (result.warning) warnings.push(result.warning);
559
497
  if (result.notice) notices.push(result.notice);
560
498
  if (result.archived) migrated++;