@openclaw/codex 2026.8.1-beta.3 → 2026.8.1

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 (64) hide show
  1. package/dist/api.js +4 -5
  2. package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-CVPvp9dL.js} +47 -5
  3. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  4. package/dist/{binding-connection-DAvZwp96.js → binding-connection-CCQFs8lf.js} +2 -2
  5. package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-rq5tiSXk.js} +13 -18
  6. package/dist/{capabilities-CH3K57v4.js → capabilities-CGm_94eL.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-D0nZ3qtd.js → command-formatters-C54Yg3eA.js} +2 -2
  9. package/dist/{command-handlers-DC86lgT4.js → command-handlers-BvOAHTrA.js} +89 -62
  10. package/dist/{compact-COqgBRny.js → compact-Hd7SAc0N.js} +8 -9
  11. package/dist/{config-CpNeo5AE.js → config-Cxw3Cl3k.js} +346 -286
  12. package/dist/doctor-contract-api.js +115 -14
  13. package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-BFoUcRdx.js} +147 -903
  14. package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
  15. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  16. package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-D3PHFFXh.js} +3 -3
  17. package/dist/{harness-DoKQcF8n.js → harness-DVfUQBlQ.js} +42 -52
  18. package/dist/harness.js +1 -1
  19. package/dist/index.js +278 -295
  20. package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-Cbbx_5Ed.js} +4 -4
  21. package/dist/launch-args-sXOAg0SX.js +84 -0
  22. package/dist/{managed-binary-By4FZC6i.js → managed-binary-ZkGodW19.js} +43 -7
  23. package/dist/media-understanding-provider.js +1 -1
  24. package/dist/model-catalog-o6Cgjoss.js +101 -0
  25. package/dist/{models-CZw1wClT.js → models-DgFWSuxA.js} +25 -28
  26. package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-CwFyoylz.js} +207 -30
  27. package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-DXrqpw1c.js} +8 -5
  28. package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-D80UZiJ_.js} +1 -1
  29. package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-mdmScYOj.js} +30 -39
  30. package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-CulOABwu.js} +162 -135
  31. package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-Bg5rpNxk.js} +148 -5
  32. package/dist/{rate-limits-BI1dMHDG.js → rate-limits-hFtzt2Up.js} +1 -1
  33. package/dist/{sandbox-guard-C1qEeZ5C.js → request-CCm4LaED.js} +183 -2
  34. package/dist/{run-attempt-BV3HzuCf.js → run-attempt-BwNQDH2u.js} +599 -361
  35. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-DeBvleJw.js} +1 -1
  36. package/dist/{session-binding-BTrLf9Qm.js → session-binding-q2WSTVmC.js} +195 -122
  37. package/dist/{session-catalog-ezP_UcRM.js → session-catalog-C2Tae4rI.js} +229 -122
  38. package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
  39. package/dist/{session-cli-B1hKvfkI.js → session-cli-BuDjm5Ou.js} +1 -1
  40. package/dist/session-permission-policy-gLm40Vtq.js +160 -0
  41. package/dist/session-retirement-CdkI0bvb.js +90 -0
  42. package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-Bcy33kam.js} +2 -3
  43. package/dist/{settled-turn-finalizer-Dx1onIqg.js → settled-turn-finalizer-B8JJcxCi.js} +7 -6
  44. package/dist/{shared-client-Dw-rzii0.js → shared-client-kNkljY-R.js} +1720 -786
  45. package/dist/{side-question-DbXkJZzg.js → side-question-GwD73tX6.js} +69 -39
  46. package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-Ss-uwnC4.js} +852 -359
  47. package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-CeYBqg_4.js} +18 -8
  48. package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → thread-resume-DF54hagX.js} +52 -6
  49. package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-CkEQIna-.js} +112 -13
  50. package/dist/transport-stdio-C7-s9TLG.js +708 -0
  51. package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-BACGlOFa.js} +78 -86
  52. package/dist/{usage-9dKJCgcy.js → usage-C2l8Mils.js} +3 -3
  53. package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
  54. package/openclaw.plugin.json +19 -1
  55. package/package.json +8 -8
  56. package/dist/app-server-policy-bg5QPSVP.js +0 -25
  57. package/dist/command-rpc-xUIIonsO.js +0 -90
  58. package/dist/incognito-session-ET_ufDH-.js +0 -2
  59. package/dist/model-catalog-CmGoqVKB.js +0 -48
  60. package/dist/request-DSK0W6YI.js +0 -157
  61. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  62. package/dist/session-retirement-CE4iMiLh.js +0 -36
  63. package/dist/thread-resume-CdJk2Ehf.js +0 -30
  64. package/dist/transport-stdio-DhoGy33Z.js +0 -99
@@ -1,186 +1,34 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { a as resolveCodexAppServerStartOptionsForAgent, i as resolveCodexAppServerRuntimeOptions, o as resolveCodexComputerUseConfig, t as codexAppServerStartOptionsKey, x as resolveCodexAppServerUserHomeDir } from "./config-CpNeo5AE.js";
3
- import { o as isCodexAppServerNativeAuthProfile } from "./session-binding-BTrLf9Qm.js";
4
- import { n as resolveCodexAppServerSpawnEnv, t as createStdioTransport } from "./transport-stdio-DhoGy33Z.js";
5
- import { a as CODEX_APP_SERVER_VERSION, n as resolveManagedCodexAppServerStartOptions, o as MIN_SUPPORTED_CODEX_APP_SERVER_VERSION, r as resolveManagedCodexNativeCommand } from "./managed-binary-By4FZC6i.js";
2
+ import { F as resolveCodexAppServerLocalHomeDir, I as resolveCodexAppServerUserHomeDir, L as withEphemeralCodexAuthStore, P as resolveCodexAppServerHomeDir, a as resolveCodexAppServerStartOptionsForAgent, i as resolveCodexAppServerRuntimeOptions, o as resolveCodexComputerUseConfig, t as codexAppServerStartOptionsKey } from "./config-Cxw3Cl3k.js";
3
+ import { a as CODEX_APP_SERVER_VERSION, c as resolveMacOSDesktopCodexAppPathCandidates, l as resolveMacOSDesktopCodexBundledMarketplaceCandidates, n as resolveManagedCodexAppServerStartOptions, o as MIN_SUPPORTED_CODEX_APP_SERVER_VERSION, r as resolveManagedCodexNativeCommand, s as resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath, t as isManagedCodexDesktopCommand, u as resolveMacOSDesktopCodexComputerUseServiceAppCandidates } from "./managed-binary-ZkGodW19.js";
4
+ import { o as isCodexAppServerNativeAuthProfile } from "./session-binding-q2WSTVmC.js";
5
+ import { a as hasCodexAppServerNaturalExit, i as closeCodexAppServerTransportAndWait, n as resolveCodexAppServerSpawnEnv, r as closeCodexAppServerTransport, t as createStdioTransport } from "./transport-stdio-C7-s9TLG.js";
6
+ import { l as readCodexDynamicToolCallParams } from "./protocol-validators-Bg5rpNxk.js";
6
7
  import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
8
  import fs from "node:fs/promises";
8
9
  import path from "node:path";
9
10
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
10
- import { addTimerTimeoutGraceMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
11
- import { AgentHarnessPreflightError, OPENCLAW_VERSION, embeddedAgentLog, formatErrorMessage } from "openclaw/plugin-sdk/agent-harness-runtime";
11
+ import { addTimerTimeoutGraceMs, parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
12
+ import { AgentHarnessPreflightError, OPENCLAW_VERSION, embeddedAgentLog, formatErrorMessage, formatToolExecutionErrorMessage, normalizeQuestionTimeoutSeconds, resolveToolExecutionErrorKind } from "openclaw/plugin-sdk/agent-harness-runtime";
12
13
  import { createHash, randomUUID } from "node:crypto";
13
- import fsSync, { constants, existsSync } from "node:fs";
14
+ import fsSync, { constants, existsSync, watch } from "node:fs";
14
15
  import os from "node:os";
15
16
  import { isDeepStrictEqual } from "node:util";
16
17
  import { ensureAuthProfileStore, findPersistedAuthProfileCredential, loadAuthProfileStoreForSecretsRuntime, refreshOAuthCredentialForRuntime, resolveApiKeyForProfile, resolveAuthProfileOrder, resolveDefaultAgentDir, resolvePersistedAuthProfileOwnerAgentDir, resolveProviderIdForAuth } from "openclaw/plugin-sdk/agent-runtime";
17
- import { hasUsableOAuthCredential } from "openclaw/plugin-sdk/provider-auth";
18
+ import { hasUsableOAuthCredential, resolveOpenAICodexAuthIdentity } from "openclaw/plugin-sdk/provider-auth";
18
19
  import { readSecretFile } from "openclaw/plugin-sdk/secret-file";
19
- import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
20
- import { runExec } from "openclaw/plugin-sdk/process-runtime";
21
20
  import { assertNoSymlinkParents, withTimeout } from "openclaw/plugin-sdk/security-runtime";
22
- import { execFile } from "node:child_process";
21
+ import { runExec } from "openclaw/plugin-sdk/process-runtime";
22
+ import { EventEmitter } from "node:events";
23
23
  import { createInterface } from "node:readline";
24
24
  import { coerceErrorMessage, toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
25
25
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
26
26
  import { parse } from "semver";
27
- import { EventEmitter } from "node:events";
27
+ import { hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
28
28
  import net from "node:net";
29
29
  import { PassThrough, Writable } from "node:stream";
30
30
  import { StringDecoder } from "node:string_decoder";
31
31
  import WebSocket from "ws";
32
- //#region extensions/codex/src/app-server/auth-start-options.ts
33
- const CODEX_APP_SERVER_HOME_DIRNAME = "codex-home";
34
- const CODEX_EPHEMERAL_AUTH_STORE_OVERRIDE = "cli_auth_credentials_store=\"ephemeral\"";
35
- function resolveCodexAppServerHomeDir(agentDir) {
36
- return path.join(path.resolve(agentDir), CODEX_APP_SERVER_HOME_DIRNAME);
37
- }
38
- /** Resolves the local CODEX_HOME used when starting one app-server connection. */
39
- function resolveCodexAppServerLocalHomeDir(startOptions, agentDir, env = process.env) {
40
- const configured = startOptions.env?.CODEX_HOME;
41
- if (configured?.trim()) return configured;
42
- return startOptions.homeScope === "user" ? resolveCodexAppServerUserHomeDir(env) : resolveCodexAppServerHomeDir(agentDir);
43
- }
44
- /** Forces OpenClaw-owned Codex auth to remain process-local. */
45
- function withEphemeralCodexAuthStore(params) {
46
- const { startOptions } = params;
47
- if (!(startOptions.commandSource === "managed" || startOptions.commandSource === "resolved-managed") || !params.preparedAuth && params.authProfileId === null) return startOptions;
48
- if (startOptions.args.at(-2) === "-c" && startOptions.args.at(-1) === CODEX_EPHEMERAL_AUTH_STORE_OVERRIDE) return startOptions;
49
- return {
50
- ...startOptions,
51
- args: [
52
- ...startOptions.args,
53
- "-c",
54
- CODEX_EPHEMERAL_AUTH_STORE_OVERRIDE
55
- ]
56
- };
57
- }
58
- //#endregion
59
- //#region extensions/codex/src/app-server/desktop-app-paths.ts
60
- /** Shared path candidates for Codex's macOS desktop app bundle. */
61
- const MACOS_DESKTOP_CODEX_APP_PATH_CANDIDATES = [{
62
- appName: "ChatGPT.app",
63
- appBundlePath: "/Applications/ChatGPT.app",
64
- appServerCommandPath: "/Applications/ChatGPT.app/Contents/Resources/codex",
65
- bundledMarketplacePath: "/Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled",
66
- computerUseServiceAppPaths: ["/Applications/ChatGPT.app/Contents/Resources/cua_node/lib/node_modules/@oai/sky/Codex Computer Use.app", "/Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled/plugins/computer-use/Codex Computer Use.app"]
67
- }, {
68
- appName: "Codex.app",
69
- appBundlePath: "/Applications/Codex.app",
70
- appServerCommandPath: "/Applications/Codex.app/Contents/Resources/codex",
71
- bundledMarketplacePath: "/Applications/Codex.app/Contents/Resources/plugins/openai-bundled",
72
- computerUseServiceAppPaths: ["/Applications/Codex.app/Contents/Resources/plugins/openai-bundled/plugins/computer-use/Codex Computer Use.app", "/Applications/Codex.app/Contents/Resources/cua_node/lib/node_modules/@oai/sky/Codex Computer Use.app"]
73
- }];
74
- function resolveMacOSDesktopCodexBundledMarketplaceCandidates(platform = process.platform) {
75
- return platform === "darwin" ? MACOS_DESKTOP_CODEX_APP_PATH_CANDIDATES.map((candidate) => candidate.bundledMarketplacePath) : [];
76
- }
77
- function resolveMacOSDesktopCodexComputerUseServiceAppCandidates(platform = process.platform, appServerCommand) {
78
- if (platform !== "darwin") return [];
79
- const matchingCandidate = appServerCommand ? MACOS_DESKTOP_CODEX_APP_PATH_CANDIDATES.find((candidate) => path.resolve(candidate.appServerCommandPath) === path.resolve(appServerCommand)) : void 0;
80
- const orderedCandidates = matchingCandidate ? [matchingCandidate, ...MACOS_DESKTOP_CODEX_APP_PATH_CANDIDATES.filter((candidate) => candidate !== matchingCandidate)] : MACOS_DESKTOP_CODEX_APP_PATH_CANDIDATES;
81
- return [...new Set(orderedCandidates.flatMap((candidate) => candidate.computerUseServiceAppPaths))];
82
- }
83
- function resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath(params = {}) {
84
- const candidates = params.candidates ?? resolveMacOSDesktopCodexBundledMarketplaceCandidates(params.platform);
85
- const pathExists = params.pathExists ?? existsSync;
86
- return candidates.find((candidate) => pathExists(candidate));
87
- }
88
- //#endregion
89
- //#region extensions/codex/src/app-server/computer-use-cache.ts
90
- /** Shared Computer Use plugin cache reconciliation for isolated Codex homes. */
91
- const DEFAULT_CODEX_COMPUTER_USE_BUNDLED_MARKETPLACE_PATH = resolveMacOSDesktopCodexBundledMarketplaceCandidates("darwin")[0] ?? "";
92
- const DEFAULT_BUNDLED_MARKETPLACE_NAME = "openai-bundled";
93
- async function ensureCodexComputerUseSharedPluginCache(params) {
94
- if (!params.config.enabled) return skippedCacheResult("disabled", "Computer Use cache sharing skipped because it is disabled.");
95
- if (params.config.pluginCacheMode === "independent") return skippedCacheResult("independent", "Computer Use cache sharing skipped because pluginCacheMode is independent.");
96
- if (params.config.marketplaceName || params.config.marketplacePath) return skippedCacheResult("explicit_marketplace", "Computer Use cache sharing skipped because an explicit marketplace is configured.");
97
- const bundledMarketplacePath = resolveComputerUseBundledMarketplacePath(params);
98
- const sourcePluginRoot = path.join(bundledMarketplacePath, "plugins", params.config.pluginName);
99
- const version = await readBundledPluginVersion(sourcePluginRoot);
100
- if (!version) return skippedCacheResult("source_missing", `Computer Use bundled plugin source was not found at ${sourcePluginRoot}.`);
101
- const marketplaceName = params.config.marketplaceName ?? DEFAULT_BUNDLED_MARKETPLACE_NAME;
102
- const cacheRoot = path.join(params.codexHome, "plugins", "cache", marketplaceName, params.config.pluginName);
103
- const cachePath = path.join(cacheRoot, version);
104
- return {
105
- status: "shared",
106
- changed: await ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version),
107
- cachePath,
108
- targetPath: sourcePluginRoot,
109
- version,
110
- removedStaleVersions: [],
111
- warnings: [],
112
- message: `Computer Use plugin cache ${cachePath} contains bundled plugin ${sourcePluginRoot}.`
113
- };
114
- }
115
- function resolveComputerUseBundledMarketplacePath(params) {
116
- return params.bundledMarketplacePath ?? resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath({ candidates: params.bundledMarketplacePathCandidates }) ?? params.bundledMarketplacePathCandidates?.[0] ?? DEFAULT_CODEX_COMPUTER_USE_BUNDLED_MARKETPLACE_PATH;
117
- }
118
- async function readBundledPluginVersion(sourcePluginRoot) {
119
- const pluginJsonPath = path.join(sourcePluginRoot, ".codex-plugin", "plugin.json");
120
- let raw;
121
- try {
122
- raw = await fs.readFile(pluginJsonPath, "utf8");
123
- } catch {
124
- return;
125
- }
126
- try {
127
- const parsed = JSON.parse(raw);
128
- return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
129
- } catch {
130
- return;
131
- }
132
- }
133
- async function ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version) {
134
- await fs.mkdir(path.dirname(cachePath), { recursive: true });
135
- const stat = await fs.lstat(cachePath).catch(() => void 0);
136
- if (stat?.isDirectory() && !stat.isSymbolicLink()) {
137
- if (await readBundledPluginVersion(cachePath) === version) return false;
138
- }
139
- const cacheRoot = path.dirname(cachePath);
140
- const cacheName = path.basename(cachePath);
141
- const stagingRoot = await fs.mkdtemp(path.join(cacheRoot, `.${cacheName}.staging-`));
142
- const stagedPath = path.join(stagingRoot, cacheName);
143
- const backupPath = path.join(cacheRoot, `.${cacheName}.backup-${process.pid}-${Date.now()}`);
144
- let backupCreated = false;
145
- try {
146
- await fs.cp(sourcePluginRoot, stagedPath, { recursive: true });
147
- if (stat) {
148
- await fs.rename(cachePath, backupPath);
149
- backupCreated = true;
150
- }
151
- try {
152
- await fs.rename(stagedPath, cachePath);
153
- } catch (error) {
154
- if (backupCreated) try {
155
- await fs.rename(backupPath, cachePath);
156
- backupCreated = false;
157
- } catch (restoreError) {
158
- throw new Error(`Failed to install Computer Use cache ${cachePath} and restore its prior copy: ${String(error)}`, { cause: restoreError });
159
- }
160
- throw error;
161
- }
162
- if (backupCreated) await fs.rm(backupPath, {
163
- recursive: true,
164
- force: true
165
- });
166
- return true;
167
- } finally {
168
- await fs.rm(stagingRoot, {
169
- recursive: true,
170
- force: true
171
- });
172
- }
173
- }
174
- function skippedCacheResult(status, message) {
175
- return {
176
- status,
177
- changed: false,
178
- message,
179
- removedStaleVersions: [],
180
- warnings: status === "source_missing" ? [message] : []
181
- };
182
- }
183
- //#endregion
184
32
  //#region extensions/codex/src/app-server/computer-use-service-path.ts
185
33
  /** Filesystem ownership guards for isolated Computer Use service provisioning. */
186
34
  async function assertOwnedServicePath(params) {
@@ -279,7 +127,7 @@ async function assertNotSymlink(filePath, label) {
279
127
  try {
280
128
  if ((await fs.lstat(filePath)).isSymbolicLink()) throw new Error(`${label} must not be a symbolic link: ${filePath}`);
281
129
  } catch (error) {
282
- if (hasNodeErrorCode(error, "ENOENT")) return;
130
+ if (hasNodeErrorCode$1(error, "ENOENT")) return;
283
131
  throw error;
284
132
  }
285
133
  }
@@ -292,14 +140,14 @@ async function ensureRealDirectoryTree(ownershipRoot, directoryPath, label) {
292
140
  for (const segment of relative.split(path.sep).filter(Boolean)) {
293
141
  current = path.join(current, segment);
294
142
  const existing = await fs.lstat(current).catch((error) => {
295
- if (hasNodeErrorCode(error, "ENOENT")) return;
143
+ if (hasNodeErrorCode$1(error, "ENOENT")) return;
296
144
  throw error;
297
145
  });
298
146
  if (!existing) {
299
147
  try {
300
148
  await fs.mkdir(current, { mode: 448 });
301
149
  } catch (error) {
302
- if (!hasNodeErrorCode(error, "EEXIST")) throw error;
150
+ if (!hasNodeErrorCode$1(error, "EEXIST")) throw error;
303
151
  }
304
152
  const created = await fs.lstat(current);
305
153
  if (created.isSymbolicLink() || !created.isDirectory()) throw new Error(`${label} changed while its directory tree was being created: ${current}`);
@@ -318,6 +166,259 @@ function assertPathAtOrInside(rootPath, candidatePath, label) {
318
166
  const relative = path.relative(path.resolve(rootPath), path.resolve(candidatePath));
319
167
  if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${label} must remain inside ${path.resolve(rootPath)}.`);
320
168
  }
169
+ function hasNodeErrorCode$1(error, code) {
170
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
171
+ }
172
+ //#endregion
173
+ //#region extensions/codex/src/app-server/computer-use-cache.ts
174
+ /** Shared Computer Use plugin cache reconciliation for isolated Codex homes. */
175
+ const DEFAULT_CODEX_COMPUTER_USE_BUNDLED_MARKETPLACE_PATH = resolveMacOSDesktopCodexBundledMarketplaceCandidates("darwin")[0] ?? "";
176
+ const DEFAULT_BUNDLED_MARKETPLACE_NAME = "openai-bundled";
177
+ async function ensureCodexComputerUseSharedPluginCache(params) {
178
+ if (!params.config.enabled) return skippedCacheResult("disabled", "Computer Use cache sharing skipped because it is disabled.");
179
+ if (params.config.pluginCacheMode === "independent") return skippedCacheResult("independent", "Computer Use cache sharing skipped because pluginCacheMode is independent.");
180
+ if (params.config.marketplaceName || params.config.marketplacePath) return skippedCacheResult("explicit_marketplace", "Computer Use cache sharing skipped because an explicit marketplace is configured.");
181
+ const bundledMarketplacePath = resolveComputerUseBundledMarketplacePath(params);
182
+ const sourcePluginRoot = path.join(bundledMarketplacePath, "plugins", params.config.pluginName);
183
+ const version = await readBundledPluginVersion(sourcePluginRoot);
184
+ if (!version) return skippedCacheResult("source_missing", `Computer Use bundled plugin source was not found at ${sourcePluginRoot}.`);
185
+ const marketplaceName = params.config.marketplaceName ?? DEFAULT_BUNDLED_MARKETPLACE_NAME;
186
+ const cacheRoot = path.join(params.codexHome, "plugins", "cache", marketplaceName, params.config.pluginName);
187
+ const cachePath = path.join(cacheRoot, version);
188
+ return {
189
+ status: "shared",
190
+ changed: await ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version, {
191
+ codexHome: params.codexHome,
192
+ ownershipRoot: params.ownershipRoot,
193
+ assertCurrent: params.assertCurrent,
194
+ forceRefresh: params.forceRefresh
195
+ }),
196
+ cachePath,
197
+ targetPath: sourcePluginRoot,
198
+ version,
199
+ removedStaleVersions: [],
200
+ warnings: [],
201
+ message: `Computer Use plugin cache ${cachePath} contains bundled plugin ${sourcePluginRoot}.`
202
+ };
203
+ }
204
+ function resolveComputerUseBundledMarketplacePath(params) {
205
+ return params.bundledMarketplacePath ?? resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath({ candidates: params.bundledMarketplacePathCandidates }) ?? params.bundledMarketplacePathCandidates?.[0] ?? DEFAULT_CODEX_COMPUTER_USE_BUNDLED_MARKETPLACE_PATH;
206
+ }
207
+ async function readBundledPluginVersion(sourcePluginRoot) {
208
+ const pluginJsonPath = path.join(sourcePluginRoot, ".codex-plugin", "plugin.json");
209
+ let raw;
210
+ try {
211
+ raw = await fs.readFile(pluginJsonPath, "utf8");
212
+ } catch {
213
+ return;
214
+ }
215
+ try {
216
+ const parsed = JSON.parse(raw);
217
+ return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
218
+ } catch {
219
+ return;
220
+ }
221
+ }
222
+ async function ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version, boundary) {
223
+ const cacheRoot = path.dirname(cachePath);
224
+ const ownedParent = boundary.ownershipRoot ? await prepareOwnedServiceParent({
225
+ ownershipRoot: boundary.ownershipRoot,
226
+ codexHome: boundary.codexHome,
227
+ targetParent: cacheRoot
228
+ }) : void 0;
229
+ if (!ownedParent) await fs.mkdir(cacheRoot, { recursive: true });
230
+ const physicalCachePath = ownedParent ? path.join(ownedParent.realPath, path.basename(cachePath)) : cachePath;
231
+ const stat = await fs.lstat(physicalCachePath).catch(() => void 0);
232
+ if (stat?.isDirectory() && !stat.isSymbolicLink()) {
233
+ if (await readBundledPluginVersion(physicalCachePath) === version && !boundary.forceRefresh) return false;
234
+ }
235
+ const cacheName = path.basename(cachePath);
236
+ const physicalCacheRoot = path.dirname(physicalCachePath);
237
+ const stagingRoot = await fs.mkdtemp(path.join(physicalCacheRoot, `.${cacheName}.staging-`));
238
+ const stagedPath = path.join(stagingRoot, cacheName);
239
+ const backupPath = path.join(physicalCacheRoot, `.${cacheName}.backup-${process.pid}-${Date.now()}`);
240
+ let backupCreated = false;
241
+ try {
242
+ await fs.cp(sourcePluginRoot, stagedPath, { recursive: true });
243
+ if (ownedParent) await assertDirectoryIdentityStable(ownedParent, "Computer Use plugin cache parent");
244
+ if (stat) {
245
+ boundary.assertCurrent?.();
246
+ await fs.rename(physicalCachePath, backupPath);
247
+ backupCreated = true;
248
+ }
249
+ try {
250
+ if (ownedParent) await assertDirectoryIdentityStable(ownedParent, "Computer Use plugin cache parent");
251
+ boundary.assertCurrent?.();
252
+ await fs.rename(stagedPath, physicalCachePath);
253
+ } catch (error) {
254
+ if (backupCreated) try {
255
+ if (ownedParent) await assertDirectoryIdentityStable(ownedParent, "Computer Use plugin cache parent");
256
+ await fs.rename(backupPath, physicalCachePath);
257
+ backupCreated = false;
258
+ } catch (restoreError) {
259
+ throw new Error(`Failed to install Computer Use cache ${cachePath} and restore its prior copy: ${String(error)}`, { cause: restoreError });
260
+ }
261
+ throw error;
262
+ }
263
+ if (backupCreated) {
264
+ if (ownedParent) await assertDirectoryIdentityStable(ownedParent, "Computer Use plugin cache parent");
265
+ await fs.rm(backupPath, {
266
+ recursive: true,
267
+ force: true
268
+ });
269
+ }
270
+ return true;
271
+ } finally {
272
+ if (!ownedParent || await directoryIdentityIsStable(ownedParent)) await fs.rm(stagingRoot, {
273
+ recursive: true,
274
+ force: true
275
+ });
276
+ }
277
+ }
278
+ function skippedCacheResult(status, message) {
279
+ return {
280
+ status,
281
+ changed: false,
282
+ message,
283
+ removedStaleVersions: [],
284
+ warnings: status === "source_missing" ? [message] : []
285
+ };
286
+ }
287
+ //#endregion
288
+ //#region extensions/codex/src/app-server/computer-use-marketplace.ts
289
+ /** Managed local wrapper for Codex's reserved bundled marketplace. */
290
+ const MARKETPLACE_NAME = "openai-bundled";
291
+ const activeInstalls$1 = /* @__PURE__ */ new Map();
292
+ function resolveCodexManagedBundledMarketplacePath(codexHome) {
293
+ return path.join(codexHome, ".tmp", "bundled-marketplaces", MARKETPLACE_NAME);
294
+ }
295
+ async function ensureCodexManagedBundledMarketplace(params) {
296
+ const candidates = params.candidates ?? resolveMacOSDesktopCodexAppPathCandidates();
297
+ const source = await resolveCodexManagedBundledMarketplaceSource({
298
+ ...params,
299
+ candidates
300
+ });
301
+ if (!source) return;
302
+ const parentPath = path.dirname(resolveCodexManagedBundledMarketplacePath(params.codexHome));
303
+ const targetPath = path.join(parentPath, MARKETPLACE_NAME);
304
+ const parent = await prepareOwnedServiceParent({
305
+ ownershipRoot: params.ownershipRoot,
306
+ codexHome: params.codexHome,
307
+ targetParent: parentPath
308
+ });
309
+ const physicalTargetPath = path.join(parent.realPath, MARKETPLACE_NAME);
310
+ await assertNotSymlink(physicalTargetPath, "managed bundled marketplace");
311
+ const active = activeInstalls$1.get(physicalTargetPath);
312
+ if (active) {
313
+ if (active.sourcePath === source.bundledMarketplacePath) return await active.promise;
314
+ await active.promise.catch(() => void 0);
315
+ return await ensureCodexManagedBundledMarketplace(params);
316
+ }
317
+ const install = reconcileManagedWrapper({
318
+ parent,
319
+ physicalTargetPath,
320
+ targetPath,
321
+ source,
322
+ ownershipCandidates: params.ownershipCandidates ?? candidates,
323
+ assertCurrent: params.assertCurrent
324
+ });
325
+ const activeEntry = {
326
+ sourcePath: source.bundledMarketplacePath,
327
+ promise: install
328
+ };
329
+ activeInstalls$1.set(physicalTargetPath, activeEntry);
330
+ const clearActive = () => {
331
+ if (activeInstalls$1.get(physicalTargetPath) === activeEntry) activeInstalls$1.delete(physicalTargetPath);
332
+ };
333
+ install.then(clearActive, clearActive);
334
+ return await install;
335
+ }
336
+ async function reconcileManagedWrapper(params) {
337
+ if (await wrapperMatches(params.physicalTargetPath, params.source.bundledMarketplacePath)) return params.targetPath;
338
+ return await publishManagedWrapper(params);
339
+ }
340
+ async function publishManagedWrapper(params) {
341
+ const { parent, physicalTargetPath, targetPath, source, ownershipCandidates, assertCurrent } = params;
342
+ const stagingPath = await fs.mkdtemp(path.join(parent.realPath, `.${MARKETPLACE_NAME}.staging-`));
343
+ const backupPath = path.join(parent.realPath, `.${MARKETPLACE_NAME}.backup-${process.pid}-${Date.now()}`);
344
+ let backupCreated = false;
345
+ try {
346
+ const manifestParent = path.join(stagingPath, ".agents", "plugins");
347
+ await fs.mkdir(manifestParent, {
348
+ recursive: true,
349
+ mode: 448
350
+ });
351
+ await Promise.all([fs.symlink(path.join(source.bundledMarketplacePath, ".agents", "plugins", "marketplace.json"), path.join(manifestParent, "marketplace.json")), fs.symlink(path.join(source.bundledMarketplacePath, "plugins"), path.join(stagingPath, "plugins"))]);
352
+ await assertDirectoryIdentityStable(parent, "managed bundled marketplace parent");
353
+ const existing = await fs.lstat(physicalTargetPath).catch((error) => {
354
+ if (hasNodeErrorCode(error, "ENOENT")) return;
355
+ throw error;
356
+ });
357
+ if (existing && !existing.isDirectory()) throw new Error(`Managed bundled marketplace must be a real directory: ${targetPath}`);
358
+ if (existing && !await wrapperMatchesAnySource(physicalTargetPath, ownershipCandidates)) throw new Error(`Refusing to replace an unowned bundled marketplace directory: ${targetPath}`);
359
+ if (existing) {
360
+ assertCurrent?.();
361
+ await fs.rename(physicalTargetPath, backupPath);
362
+ backupCreated = true;
363
+ await assertDirectoryIdentityStable(parent, "managed bundled marketplace parent");
364
+ }
365
+ assertCurrent?.();
366
+ await fs.rename(stagingPath, physicalTargetPath);
367
+ await assertDirectoryIdentityStable(parent, "managed bundled marketplace parent");
368
+ if (backupCreated) {
369
+ await fs.rm(backupPath, { recursive: true });
370
+ backupCreated = false;
371
+ }
372
+ return targetPath;
373
+ } catch (error) {
374
+ if (backupCreated) try {
375
+ await assertDirectoryIdentityStable(parent, "managed bundled marketplace parent");
376
+ if (await fs.lstat(physicalTargetPath).catch(() => void 0)) {
377
+ if (!await wrapperMatches(physicalTargetPath, source.bundledMarketplacePath)) throw new Error("managed bundled marketplace replacement is no longer owned", { cause: error });
378
+ await fs.rm(physicalTargetPath, { recursive: true });
379
+ }
380
+ await fs.rename(backupPath, physicalTargetPath);
381
+ backupCreated = false;
382
+ } catch (restoreError) {
383
+ throw new Error(`Failed to restore the prior managed bundled marketplace: ${String(error)}`, { cause: restoreError });
384
+ }
385
+ throw error;
386
+ } finally {
387
+ if (await directoryIdentityIsStable(parent)) await fs.rm(stagingPath, {
388
+ recursive: true,
389
+ force: true
390
+ });
391
+ }
392
+ }
393
+ async function resolveCodexManagedBundledMarketplaceSource(params) {
394
+ const candidates = params.candidates ?? resolveMacOSDesktopCodexAppPathCandidates();
395
+ const command = params.appServerCommand && path.resolve(params.appServerCommand);
396
+ const ordered = command ? candidates.filter((candidate) => path.resolve(candidate.appServerCommandPath) === command) : candidates;
397
+ for (const candidate of ordered) if (await isExpectedMarketplace(candidate.bundledMarketplacePath)) return candidate;
398
+ }
399
+ async function isExpectedMarketplace(root) {
400
+ try {
401
+ const manifest = JSON.parse(await fs.readFile(path.join(root, ".agents", "plugins", "marketplace.json"), "utf8"));
402
+ await fs.access(path.join(root, "plugins", "computer-use", ".codex-plugin", "plugin.json"));
403
+ return isRecord(manifest) && manifest.name === MARKETPLACE_NAME && Array.isArray(manifest.plugins) && manifest.plugins.some((plugin) => isRecord(plugin) && plugin.name === "computer-use");
404
+ } catch {
405
+ return false;
406
+ }
407
+ }
408
+ async function wrapperMatches(targetPath, sourcePath) {
409
+ try {
410
+ const target = await fs.lstat(targetPath);
411
+ if (!target.isDirectory() || target.isSymbolicLink()) return false;
412
+ const [manifest, plugins] = await Promise.all([fs.readlink(path.join(targetPath, ".agents", "plugins", "marketplace.json")), fs.readlink(path.join(targetPath, "plugins"))]);
413
+ return manifest === path.join(sourcePath, ".agents", "plugins", "marketplace.json") && plugins === path.join(sourcePath, "plugins");
414
+ } catch {
415
+ return false;
416
+ }
417
+ }
418
+ async function wrapperMatchesAnySource(targetPath, candidates) {
419
+ for (const candidate of candidates) if (await wrapperMatches(targetPath, candidate.bundledMarketplacePath)) return true;
420
+ return false;
421
+ }
321
422
  function hasNodeErrorCode(error, code) {
322
423
  return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
323
424
  }
@@ -332,9 +433,13 @@ const CLIENT_APP_RELATIVE_PATH = path.join("Contents", "SharedSupport", "SkyComp
332
433
  const CLIENT_RELATIVE_PATH = path.join(CLIENT_APP_RELATIVE_PATH, "Contents", "MacOS", "SkyComputerUseClient");
333
434
  const COPY_TIMEOUT_MS = 12e4;
334
435
  const INSPECT_TIMEOUT_MS = 3e4;
335
- const COMPLETED_SYNC_CACHE_MAX_ENTRIES = 64;
336
436
  const activeInstalls = /* @__PURE__ */ new Map();
337
- const completedSyncs = /* @__PURE__ */ new Map();
437
+ /** Finds the first signed native service from one ordered desktop owner set. */
438
+ async function resolveCodexComputerUseServiceAppSourcePath(params) {
439
+ const platform = params.platform ?? process.platform;
440
+ if (platform !== "darwin") return;
441
+ return (await findUsableServiceApp(params.sourceAppCandidates ?? resolveMacOSDesktopCodexComputerUseServiceAppCandidates(platform, params.appServerCommand), params.inspectServiceApp ?? inspectTrustedServiceApp))?.path;
442
+ }
338
443
  /** Synchronizes the CODEX_HOME native client with the selected signed desktop distribution. */
339
444
  async function ensureCodexComputerUseServiceApp(params) {
340
445
  const platform = params.platform ?? process.platform;
@@ -355,25 +460,12 @@ async function ensureCodexComputerUseServiceApp(params) {
355
460
  });
356
461
  const candidates = params.sourceAppCandidates ?? resolveMacOSDesktopCodexComputerUseServiceAppCandidates(platform, params.appServerCommand);
357
462
  const syncKey = [targetPath, ...candidates].join("\0");
358
- const completed = completedSyncs.get(targetPath);
359
- if (completed?.syncKey === syncKey) {
360
- completedSyncs.delete(targetPath);
361
- completedSyncs.set(targetPath, completed);
362
- return {
363
- status: "already_current",
364
- changed: false,
365
- targetPath: completed.targetPath,
366
- sourcePath: completed.sourcePath,
367
- sourceBuild: completed.sourceBuild
368
- };
369
- }
370
463
  const active = activeInstalls.get(targetPath);
371
464
  if (active) {
372
465
  if (active.syncKey === syncKey) return await active.promise;
373
466
  await active.promise.catch(() => void 0);
374
467
  return await ensureCodexComputerUseServiceApp(params);
375
468
  }
376
- if (completed) completedSyncs.delete(targetPath);
377
469
  const install = ensureCodexComputerUseServiceAppOnce({
378
470
  ...params,
379
471
  codexHome,
@@ -382,18 +474,6 @@ async function ensureCodexComputerUseServiceApp(params) {
382
474
  targetPath,
383
475
  platform,
384
476
  sourceAppCandidates: candidates
385
- }).then((result) => {
386
- if (isCompletedSyncStatus(result.status)) {
387
- completedSyncs.delete(targetPath);
388
- completedSyncs.set(targetPath, {
389
- syncKey,
390
- targetPath: result.targetPath,
391
- sourcePath: result.sourcePath,
392
- sourceBuild: result.sourceBuild
393
- });
394
- pruneMapToMaxSize(completedSyncs, COMPLETED_SYNC_CACHE_MAX_ENTRIES);
395
- }
396
- return result;
397
477
  });
398
478
  const activeEntry = {
399
479
  syncKey,
@@ -406,9 +486,6 @@ async function ensureCodexComputerUseServiceApp(params) {
406
486
  install.then(clearActive, clearActive);
407
487
  return await install;
408
488
  }
409
- function isCompletedSyncStatus(status) {
410
- return status === "installed" || status === "refreshed" || status === "already_current";
411
- }
412
489
  async function ensureCodexComputerUseServiceAppOnce(params) {
413
490
  const inspectServiceApp = params.inspectServiceApp ?? inspectTrustedServiceApp;
414
491
  const source = await findUsableServiceApp(params.sourceAppCandidates ?? [], inspectServiceApp);
@@ -453,6 +530,7 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
453
530
  await assertNotSymlink(operationTargetPath, "Computer Use service target");
454
531
  if (await pathExists$1(operationTargetPath)) {
455
532
  await assertOwnedServiceParentStable(ownedParent);
533
+ params.assertCurrent?.();
456
534
  await fs.rename(operationTargetPath, backupPath);
457
535
  await assertOwnedServiceParentStable(ownedParent);
458
536
  backupCreated = true;
@@ -480,6 +558,7 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
480
558
  }
481
559
  try {
482
560
  await assertOwnedServiceParentStable(ownedParent);
561
+ params.assertCurrent?.();
483
562
  await fs.rename(stagedPath, operationTargetPath);
484
563
  await assertOwnedServiceParentStable(ownedParent);
485
564
  } catch (error) {
@@ -742,10 +821,12 @@ const CODEX_APP_SERVER_PREPARED_AUTH_ENV_VARS = [
742
821
  const CODEX_APP_SERVER_HOME_ENV_VARS = [CODEX_HOME_ENV_VAR, HOME_ENV_VAR];
743
822
  const CODEX_AUTH_JSON_FILENAME = "auth.json";
744
823
  const CODEX_HOME_DIRNAME = ".codex";
824
+ const MAX_COMPUTER_USE_ARTIFACT_OWNERS = 128;
825
+ const activeComputerUseArtifactReconciliations = /* @__PURE__ */ new Map();
745
826
  const scopedOAuthRefreshQueues = /* @__PURE__ */ new WeakMap();
746
827
  async function bridgeCodexAppServerStartOptions(params) {
747
828
  if (params.startOptions.transport !== "stdio") return params.startOptions;
748
- const scopeStartOptions = () => withCodexHomeEnvironment(withEphemeralCodexAuthStore(params), params.agentDir, params.pluginConfig);
829
+ const scopeStartOptions = () => withCodexHomeEnvironment(withEphemeralCodexAuthStore(params), params.agentDir);
749
830
  if (params.preparedAuth) return withClearedEnvironmentVariables(await scopeStartOptions(), CODEX_APP_SERVER_PREPARED_AUTH_ENV_VARS);
750
831
  if (params.authProfileId === null) return scopeStartOptions();
751
832
  const store = resolveCodexAppServerAuthProfileStore({
@@ -772,8 +853,7 @@ function assertNoUnimportedAgentCodexAuthFile(params) {
772
853
  if (message) throw new AgentHarnessPreflightError(message);
773
854
  }
774
855
  function resolveUnimportedAgentCodexAuthMessage(params) {
775
- const managedCodexCli = params.startOptions.commandSource === "managed" || params.startOptions.commandSource === "resolved-managed";
776
- if (params.startOptions.transport !== "stdio" || !managedCodexCli || params.startOptions.homeScope === "user") return;
856
+ if (params.startOptions.transport !== "stdio" || params.startOptions.homeScope === "user") return;
777
857
  const codexHome = resolveCodexAppServerHomeDir(params.agentDir);
778
858
  const authPath = path.join(codexHome, CODEX_AUTH_JSON_FILENAME);
779
859
  if (!fsSync.existsSync(authPath)) return;
@@ -845,7 +925,7 @@ async function resolveCodexAppServerPreparedAuthProfileSnapshot(params) {
845
925
  const accountId = loginParams.type === "chatgptAuthTokens" ? loginParams.chatgptAccountId : resolveChatgptAccountId(profileId, credential);
846
926
  const stableChatgptAccountId = resolveStableChatgptAccountId(credential);
847
927
  const secretFreeCacheKey = credential.type === "api_key" && loginParams.type === "apiKey" ? `${accountId}:${fingerprintApiKeyAuthProfileCacheKey(loginParams.apiKey)}` : loginParams.type === "chatgptAuthTokens" && (credential.type === "token" || !stableChatgptAccountId) ? `${accountId}:${fingerprintTokenAuthProfileCacheKey(loginParams.accessToken)}` : accountId;
848
- const chatgptAccountId = resolveExplicitChatgptAccountId(credential);
928
+ const chatgptAccountId = loginParams.type === "chatgptAuthTokens" ? loginParams.chatgptAccountId : void 0;
849
929
  return {
850
930
  loginParams,
851
931
  secretFreeCacheKey,
@@ -974,28 +1054,10 @@ function fingerprintCodexCliAuthFileApiKeyCacheKey(apiKey) {
974
1054
  hash.update(apiKey);
975
1055
  return `CODEX_AUTH_JSON:sha256:${hash.digest("hex")}`;
976
1056
  }
977
- async function withCodexHomeEnvironment(startOptions, agentDir, pluginConfig) {
1057
+ async function withCodexHomeEnvironment(startOptions, agentDir) {
978
1058
  const codexHome = resolveCodexAppServerLocalHomeDir(startOptions, agentDir);
979
1059
  const nativeHome = startOptions.env?.[HOME_ENV_VAR]?.trim() ? startOptions.env[HOME_ENV_VAR] : void 0;
980
1060
  await fs.mkdir(codexHome, { recursive: true });
981
- const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
982
- await ensureCodexComputerUseSharedPluginCache({
983
- codexHome,
984
- config: computerUseConfig
985
- });
986
- const ownsIsolatedCodexHome = startOptions.homeScope !== "user" && !startOptions.env?.[CODEX_HOME_ENV_VAR]?.trim();
987
- if (computerUseConfig.enabled && computerUseConfig.autoInstall && ownsIsolatedCodexHome) try {
988
- await ensureCodexComputerUseServiceApp({
989
- codexHome,
990
- ownershipRoot: agentDir,
991
- appServerCommand: startOptions.command
992
- });
993
- } catch (error) {
994
- throw new AgentHarnessPreflightError("Codex Computer Use client provisioning failed.", {
995
- cause: error,
996
- scope: "harness"
997
- });
998
- }
999
1061
  if (nativeHome) await fs.mkdir(nativeHome, { recursive: true });
1000
1062
  const nextStartOptions = {
1001
1063
  ...startOptions,
@@ -1010,6 +1072,138 @@ async function withCodexHomeEnvironment(startOptions, agentDir, pluginConfig) {
1010
1072
  else delete nextStartOptions.clearEnv;
1011
1073
  return nextStartOptions;
1012
1074
  }
1075
+ /** Reconciles Computer Use artifacts for the exact managed command about to start. */
1076
+ async function reconcileCodexComputerUseStartArtifacts(params) {
1077
+ if (params.startOptions.transport !== "stdio") return;
1078
+ const codexHome = resolveCodexAppServerLocalHomeDir(params.startOptions, params.agentDir);
1079
+ const key = path.resolve(codexHome);
1080
+ let owner = activeComputerUseArtifactReconciliations.get(key);
1081
+ if (!owner) {
1082
+ owner = {
1083
+ active: 0,
1084
+ tail: Promise.resolve()
1085
+ };
1086
+ activeComputerUseArtifactReconciliations.set(key, owner);
1087
+ } else {
1088
+ activeComputerUseArtifactReconciliations.delete(key);
1089
+ activeComputerUseArtifactReconciliations.set(key, owner);
1090
+ }
1091
+ owner.active += 1;
1092
+ const epoch = params.desktopGeneration?.epoch;
1093
+ if (epoch !== void 0 && (owner.latestEpoch === void 0 || epoch > owner.latestEpoch)) owner.latestEpoch = epoch;
1094
+ const assertCurrent = () => {
1095
+ params.assertCurrent?.();
1096
+ if (epoch !== void 0 && owner.latestEpoch !== epoch) throw new Error("Codex Computer Use artifact reconciliation was superseded.");
1097
+ };
1098
+ const operation = owner.tail.catch(() => void 0).then(async () => {
1099
+ assertCurrent();
1100
+ const appliedCacheBinding = await reconcileCodexComputerUseStartArtifactsOnce({
1101
+ ...params,
1102
+ codexHome,
1103
+ assertCurrent,
1104
+ previousCacheBinding: owner.appliedCacheBinding
1105
+ });
1106
+ assertCurrent();
1107
+ owner.appliedCacheBinding = appliedCacheBinding;
1108
+ });
1109
+ const settled = operation.then(() => void 0, () => void 0);
1110
+ owner.tail = settled;
1111
+ try {
1112
+ await operation;
1113
+ } finally {
1114
+ owner.active = Math.max(0, owner.active - 1);
1115
+ if (owner.active === 0 && owner.latestEpoch === void 0 && activeComputerUseArtifactReconciliations.get(key) === owner && owner.tail === settled) activeComputerUseArtifactReconciliations.delete(key);
1116
+ pruneComputerUseArtifactOwners();
1117
+ }
1118
+ }
1119
+ async function reconcileCodexComputerUseStartArtifactsOnce(params) {
1120
+ const codexHome = params.codexHome;
1121
+ const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig: params.pluginConfig });
1122
+ const ownsIsolatedCodexHome = params.ownsIsolatedCodexHome ?? (params.startOptions.homeScope !== "user" && !params.startOptions.env?.[CODEX_HOME_ENV_VAR]?.trim());
1123
+ const shouldProvisionComputerUse = computerUseConfig.enabled && computerUseConfig.autoInstall && ownsIsolatedCodexHome;
1124
+ if (shouldProvisionComputerUse) await ensureOwnedCodexHome(codexHome, params.agentDir);
1125
+ else await fs.mkdir(codexHome, { recursive: true });
1126
+ const desktopCandidates = resolveMacOSDesktopCodexAppPathCandidates();
1127
+ const exactDesktopCandidate = desktopCandidates.find((candidate) => path.resolve(candidate.appServerCommandPath) === path.resolve(params.startOptions.command));
1128
+ const usesManagedBundledMarketplace = !computerUseConfig.marketplaceSource && !computerUseConfig.marketplacePath && !computerUseConfig.marketplaceName;
1129
+ const needsBundledMarketplace = usesManagedBundledMarketplace || computerUseConfig.pluginCacheMode === "shared" && !computerUseConfig.marketplaceName && !computerUseConfig.marketplacePath;
1130
+ const artifactCandidate = shouldProvisionComputerUse ? await resolveCompleteComputerUseArtifactCandidate({
1131
+ candidates: exactDesktopCandidate ? [exactDesktopCandidate] : desktopCandidates,
1132
+ needsBundledMarketplace
1133
+ }) : exactDesktopCandidate;
1134
+ params.assertCurrent();
1135
+ if (shouldProvisionComputerUse) {
1136
+ if (desktopCandidates.length > 0 && !artifactCandidate) throw new CodexComputerUseCandidateArtifactsUnavailableError();
1137
+ try {
1138
+ const marketplacePath = usesManagedBundledMarketplace ? await ensureCodexManagedBundledMarketplace({
1139
+ codexHome,
1140
+ ownershipRoot: params.agentDir,
1141
+ ...artifactCandidate ? {
1142
+ appServerCommand: artifactCandidate.appServerCommandPath,
1143
+ candidates: [artifactCandidate],
1144
+ ownershipCandidates: desktopCandidates
1145
+ } : {},
1146
+ assertCurrent: params.assertCurrent
1147
+ }) : void 0;
1148
+ params.assertCurrent();
1149
+ if (usesManagedBundledMarketplace && desktopCandidates.length > 0 && !marketplacePath) throw new CodexComputerUseCandidateArtifactsUnavailableError();
1150
+ const service = await ensureCodexComputerUseServiceApp({
1151
+ codexHome,
1152
+ ownershipRoot: params.agentDir,
1153
+ ...artifactCandidate ? {
1154
+ appServerCommand: artifactCandidate.appServerCommandPath,
1155
+ sourceAppCandidates: artifactCandidate.computerUseServiceAppPaths
1156
+ } : {},
1157
+ assertCurrent: params.assertCurrent
1158
+ });
1159
+ params.assertCurrent();
1160
+ if (desktopCandidates.length > 0 && service.status === "source_missing") throw new CodexComputerUseCandidateArtifactsUnavailableError();
1161
+ } catch (error) {
1162
+ params.assertCurrent();
1163
+ if (error instanceof CodexComputerUseCandidateArtifactsUnavailableError) throw error;
1164
+ throw new AgentHarnessPreflightError("Codex Computer Use client provisioning failed.", {
1165
+ cause: error,
1166
+ scope: "harness"
1167
+ });
1168
+ }
1169
+ }
1170
+ params.assertCurrent();
1171
+ const cacheBinding = [
1172
+ params.desktopGeneration?.epoch ?? "manual",
1173
+ artifactCandidate?.bundledMarketplacePath ?? "default",
1174
+ computerUseConfig.pluginName
1175
+ ].join("\0");
1176
+ const cache = await ensureCodexComputerUseSharedPluginCache({
1177
+ codexHome,
1178
+ config: computerUseConfig,
1179
+ ...ownsIsolatedCodexHome ? { ownershipRoot: params.agentDir } : {},
1180
+ ...artifactCandidate ? { bundledMarketplacePath: artifactCandidate.bundledMarketplacePath } : {},
1181
+ assertCurrent: params.assertCurrent,
1182
+ forceRefresh: params.forceCacheRefresh === true || params.previousCacheBinding !== cacheBinding
1183
+ });
1184
+ params.assertCurrent();
1185
+ return cache.status === "shared" ? cacheBinding : void 0;
1186
+ }
1187
+ async function resolveCompleteComputerUseArtifactCandidate(params) {
1188
+ for (const candidate of params.candidates) {
1189
+ if (params.needsBundledMarketplace && !await resolveCodexManagedBundledMarketplaceSource({ candidates: [candidate] })) continue;
1190
+ if (await resolveCodexComputerUseServiceAppSourcePath({ sourceAppCandidates: candidate.computerUseServiceAppPaths })) return candidate;
1191
+ }
1192
+ }
1193
+ function pruneComputerUseArtifactOwners() {
1194
+ while (activeComputerUseArtifactReconciliations.size > MAX_COMPUTER_USE_ARTIFACT_OWNERS) {
1195
+ const inactive = [...activeComputerUseArtifactReconciliations].find(([, owner]) => owner.active === 0);
1196
+ if (!inactive) return;
1197
+ activeComputerUseArtifactReconciliations.delete(inactive[0]);
1198
+ }
1199
+ }
1200
+ var CodexComputerUseCandidateArtifactsUnavailableError = class extends Error {
1201
+ constructor() {
1202
+ super("The selected Codex desktop app does not contain complete Computer Use artifacts.");
1203
+ this.code = "CODEX_COMPUTER_USE_CANDIDATE_ARTIFACTS_UNAVAILABLE";
1204
+ this.name = "CodexComputerUseCandidateArtifactsUnavailableError";
1205
+ }
1206
+ };
1013
1207
  function withoutClearedCodexHomeEnv(clearEnv) {
1014
1208
  if (!clearEnv) return;
1015
1209
  const reserved = new Set(CODEX_APP_SERVER_HOME_ENV_VARS);
@@ -1017,36 +1211,27 @@ function withoutClearedCodexHomeEnv(clearEnv) {
1017
1211
  return filtered.length === clearEnv.length ? clearEnv : filtered;
1018
1212
  }
1019
1213
  async function applyCodexAppServerAuthProfile(params) {
1020
- if (params.preparedAuth?.kind === "profile") {
1021
- await params.client.request("account/login/start", params.preparedAuth.snapshot.loginParams);
1022
- return;
1023
- }
1024
- if (params.preparedAuth?.kind === "api-key") {
1025
- await params.client.request("account/login/start", {
1026
- type: "apiKey",
1027
- apiKey: params.preparedAuth.apiKey
1028
- });
1029
- return;
1030
- }
1031
- if (params.authProfileId === null) {
1214
+ if (!params.preparedAuth && params.authProfileId === null) {
1032
1215
  await assertNativeCodexAccountMatchesRoute(params.client, params.authRequirement);
1033
1216
  return;
1034
1217
  }
1035
1218
  let loginParams;
1036
1219
  try {
1037
- loginParams = await resolveCodexAppServerAuthProfileLoginParams({
1220
+ loginParams = params.preparedAuth?.kind === "profile" ? params.preparedAuth.snapshot.loginParams : params.preparedAuth?.kind === "api-key" ? {
1221
+ type: "apiKey",
1222
+ apiKey: params.preparedAuth.apiKey
1223
+ } : await resolveCodexAppServerAuthProfileLoginParams({
1038
1224
  agentDir: params.agentDir,
1039
- authProfileId: params.authProfileId,
1225
+ authProfileId: params.authProfileId ?? void 0,
1040
1226
  authProfileStore: params.authProfileStore,
1041
1227
  config: params.config
1042
1228
  });
1043
1229
  } catch (error) {
1044
- if (params.authRequirement === "subscription" && error instanceof CodexAppServerAuthProfileUnavailableError) throw createCodexAppServerAuthError("Codex subscription auth profile could not produce login credentials.", error);
1230
+ if (params.authRequirement === "subscription" && error instanceof CodexAppServerAuthProfileUnavailableError) throw createCodexAppServerAuthError("Codex subscription auth profile could not produce login credentials. Sign in with `openclaw models auth login --provider openai`, select that profile, then retry.", error);
1045
1231
  throw error;
1046
1232
  }
1047
- if (params.authRequirement === "subscription" && loginParams?.type !== "chatgptAuthTokens") throw createCodexAppServerAuthError("Codex subscription auth profile could not produce login credentials.");
1233
+ if (params.authRequirement === "subscription" && loginParams?.type !== "chatgptAuthTokens") throw createCodexAppServerAuthError("Codex subscription auth profile could not produce login credentials. Sign in with `openclaw models auth login --provider openai`, select that profile, then retry.");
1048
1234
  if (!loginParams) {
1049
- if (params.authRequirement === "subscription") throw createCodexAppServerAuthError("Codex subscription auth profile could not produce login credentials.");
1050
1235
  if (params.authRequirement !== "api-key" || params.startOptions?.transport !== "stdio") return;
1051
1236
  const env = resolveCodexAppServerSpawnEnv(params.startOptions, process.env);
1052
1237
  const fallbackLoginParams = await resolveCodexAppServerFallbackApiKeyLoginParams({
@@ -1071,7 +1256,7 @@ async function assertNativeCodexAccountMatchesRoute(client, authRequirement) {
1071
1256
  const response = await client.request("account/read", { refreshToken: false });
1072
1257
  const accountType = isJsonObject(response.account) ? response.account.type : void 0;
1073
1258
  if (authRequirement === "subscription") {
1074
- if (accountType !== "chatgpt") throw createCodexAppServerAuthError("Codex subscription auth profile could not produce login credentials.");
1259
+ if (accountType !== "chatgpt") throw createCodexAppServerAuthError("Codex subscription route requires ChatGPT auth in the native Codex home. Run `codex login` for that home, or use appServer.homeScope=\"agent\" with an OpenClaw OAuth profile, then retry.");
1075
1260
  return;
1076
1261
  }
1077
1262
  if (accountType === "chatgpt") throw createCodexAppServerAuthError("Codex Platform route requires an API-key account, but the native Codex home is signed in with a ChatGPT subscription. Sign that home in with `codex login --with-api-key`, or set appServer.homeScope=\"agent\" so OpenClaw can inject its own key.");
@@ -1080,7 +1265,12 @@ function createCodexAppServerAuthError(message, cause) {
1080
1265
  const error = cause === void 0 ? new Error(message) : new Error(message, { cause });
1081
1266
  return Object.assign(error, { status: 401 });
1082
1267
  }
1083
- var CodexAppServerAuthProfileUnavailableError = class extends Error {};
1268
+ var CodexAppServerAuthProfileUnavailableError = class extends Error {
1269
+ constructor(..._args) {
1270
+ super(..._args);
1271
+ this.status = 401;
1272
+ }
1273
+ };
1084
1274
  async function resolveCodexAppServerAuthProfileLoginParams(params) {
1085
1275
  const store = resolveCodexAppServerAuthProfileStore(params);
1086
1276
  const profileId = resolveCodexAppServerAuthProfileId({
@@ -1089,7 +1279,7 @@ async function resolveCodexAppServerAuthProfileLoginParams(params) {
1089
1279
  config: params.config
1090
1280
  });
1091
1281
  const profile = profileId ? store.profiles[profileId] : void 0;
1092
- if (profileId && !profile) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" was not found.`);
1282
+ if (profileId && !profile) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" was not found. Select an existing OpenAI profile or sign in again with OpenClaw, then retry.`);
1093
1283
  if (profileId && profile && !isCodexAppServerAuthProfileCredential(profile)) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" must use the canonical OpenAI auth provider; run "openclaw doctor --fix" to migrate legacy provider IDs.`);
1094
1284
  return await resolveCodexAppServerAuthProfileLoginParamsInternal({
1095
1285
  ...params,
@@ -1097,11 +1287,24 @@ async function resolveCodexAppServerAuthProfileLoginParams(params) {
1097
1287
  });
1098
1288
  }
1099
1289
  async function refreshCodexAppServerAuthTokens(params) {
1290
+ const previousAccountId = params.previousAccountId?.trim();
1291
+ if (previousAccountId) {
1292
+ const store = resolveCodexAppServerAuthProfileStore(params);
1293
+ const profileId = resolveCodexAppServerAuthProfileId({
1294
+ authProfileId: params.authProfileId,
1295
+ store,
1296
+ config: params.config
1297
+ });
1298
+ const credential = profileId ? store.profiles[profileId] : void 0;
1299
+ const selectedAccountId = credential ? resolveExplicitChatgptAccountId(credential) ?? (credential.type === "oauth" ? resolveOpenAICodexAuthIdentity({ access: credential.access }).accountId : void 0) : void 0;
1300
+ if (selectedAccountId && selectedAccountId !== previousAccountId) throw new Error("ChatGPT workspace changed before Codex token refresh. Retry to start a client for the selected workspace.");
1301
+ }
1100
1302
  const loginParams = await resolveCodexAppServerAuthProfileLoginParamsInternal({
1101
1303
  ...params,
1102
1304
  forceOAuthRefresh: true
1103
1305
  });
1104
- if (!loginParams || loginParams.type !== "chatgptAuthTokens") throw new Error("Codex app-server ChatGPT token refresh requires an OAuth auth profile.");
1306
+ if (!loginParams || loginParams.type !== "chatgptAuthTokens") throw new Error("Codex app-server ChatGPT token refresh requires an OAuth auth profile. Sign in with `openclaw models auth login --provider openai`, select that profile, then retry.");
1307
+ if (previousAccountId && loginParams.chatgptAccountId !== previousAccountId) throw new Error("ChatGPT workspace changed during Codex token refresh. Retry to start a client for the selected workspace.");
1105
1308
  return {
1106
1309
  accessToken: loginParams.accessToken,
1107
1310
  chatgptAccountId: loginParams.chatgptAccountId,
@@ -1122,7 +1325,7 @@ async function resolveCodexAppServerAuthProfileLoginParamsInternal(params) {
1122
1325
  });
1123
1326
  if (!profileId) return;
1124
1327
  const credential = store.profiles[profileId];
1125
- if (!credential) throw new Error(`Codex app-server auth profile "${profileId}" was not found.`);
1328
+ if (!credential) throw new Error(`Codex app-server auth profile "${profileId}" was not found. Select an existing OpenAI profile or sign in again with OpenClaw, then retry.`);
1126
1329
  if (!isCodexAppServerAuthProfileCredential(credential)) throw new Error(`Codex app-server auth profile "${profileId}" must use the canonical OpenAI auth provider; run "openclaw doctor --fix" to migrate legacy provider IDs.`);
1127
1330
  const loginParams = await resolveLoginParamsForCredential(profileId, credential, {
1128
1331
  agentDir: params.agentDir,
@@ -1131,7 +1334,7 @@ async function resolveCodexAppServerAuthProfileLoginParamsInternal(params) {
1131
1334
  forceOAuthRefresh: params.forceOAuthRefresh === true,
1132
1335
  config: params.config
1133
1336
  });
1134
- if (!loginParams) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" does not contain usable credentials.`);
1337
+ if (!loginParams) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" does not contain usable credentials. Repair or replace the selected OpenAI credential, then retry.`);
1135
1338
  return loginParams;
1136
1339
  }
1137
1340
  async function resolveCodexAppServerFallbackApiKeyLoginParams(params) {
@@ -1245,7 +1448,7 @@ async function resolveOAuthCredentialForCodexAppServer(profileId, credential, pa
1245
1448
  });
1246
1449
  if (params.forceRefresh && !persistedOAuthCredential && overlaidOAuthCredential) {
1247
1450
  const refreshedRuntimeCredential = await refreshOAuthCredentialForRuntime({ credential: overlaidOAuthCredential });
1248
- if (!refreshedRuntimeCredential?.access?.trim()) throw new Error(`Codex app-server auth profile "${profileId}" could not refresh.`);
1451
+ if (!refreshedRuntimeCredential?.access?.trim()) throw new Error(`Codex app-server auth profile "${profileId}" could not refresh. Sign in again with OpenClaw, then retry.`);
1249
1452
  store.profiles[profileId] = refreshedRuntimeCredential;
1250
1453
  return refreshedRuntimeCredential;
1251
1454
  }
@@ -1273,8 +1476,8 @@ function shouldUseScopedOAuthCredential(params) {
1273
1476
  return !isDeepStrictEqual(persisted, params.suppliedCredential) && !hasMatchingOAuthIdentity(persisted, params.suppliedCredential);
1274
1477
  }
1275
1478
  function hasMatchingOAuthIdentity(persisted, supplied) {
1276
- const persistedAccountId = persisted.accountId?.trim();
1277
- const suppliedAccountId = supplied.accountId?.trim();
1479
+ const persistedAccountId = resolveOpenAICodexAuthIdentity(persisted).accountId?.trim();
1480
+ const suppliedAccountId = resolveOpenAICodexAuthIdentity(supplied).accountId?.trim();
1278
1481
  if (persistedAccountId && suppliedAccountId) return persistedAccountId === suppliedAccountId;
1279
1482
  const persistedEmail = persisted.email?.trim().toLowerCase();
1280
1483
  const suppliedEmail = supplied.email?.trim().toLowerCase();
@@ -1291,8 +1494,8 @@ async function resolveScopedOAuthCredential(params) {
1291
1494
  const credential = current?.type === "oauth" ? current : params.credential;
1292
1495
  if (!params.forceRefresh && hasUsableOAuthCredential(credential)) return credential;
1293
1496
  const refreshed = await refreshOAuthCredentialForRuntime({ credential });
1294
- if (!refreshed?.access?.trim()) throw new Error(`Codex app-server auth profile "${params.profileId}" could not refresh.`);
1295
- if (!isDeepStrictEqual(params.store.profiles[params.profileId], credential)) throw new Error(`Codex app-server auth profile "${params.profileId}" changed while refreshing.`);
1497
+ if (!refreshed?.access?.trim()) throw new Error(`Codex app-server auth profile "${params.profileId}" could not refresh. Sign in again with OpenClaw, then retry.`);
1498
+ if (!isDeepStrictEqual(params.store.profiles[params.profileId], credential)) throw new Error(`Codex app-server auth profile "${params.profileId}" changed while refreshing. Retry with the newly selected OpenAI profile.`);
1296
1499
  params.store.profiles[params.profileId] = refreshed;
1297
1500
  return refreshed;
1298
1501
  })();
@@ -1339,10 +1542,15 @@ function readFirstNonEmptyEnvEntry(env, keys) {
1339
1542
  }
1340
1543
  }
1341
1544
  function buildChatgptAuthTokensParams(profileId, credential, accessToken) {
1545
+ const storedAccountId = resolveExplicitChatgptAccountId(credential);
1546
+ const tokenAccountId = resolveOpenAICodexAuthIdentity({ access: accessToken }).accountId;
1547
+ if (storedAccountId && tokenAccountId && storedAccountId !== tokenAccountId) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" has a different ChatGPT account ID than its access token. Sign in again before retrying.`);
1548
+ const chatgptAccountId = storedAccountId ?? tokenAccountId;
1549
+ if (!chatgptAccountId) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" is missing its ChatGPT account ID. Sign in again before retrying.`);
1342
1550
  return {
1343
1551
  type: "chatgptAuthTokens",
1344
1552
  accessToken,
1345
- chatgptAccountId: resolveChatgptAccountId(profileId, credential),
1553
+ chatgptAccountId,
1346
1554
  chatgptPlanType: resolveChatgptPlanType(credential)
1347
1555
  };
1348
1556
  }
@@ -1462,12 +1670,46 @@ function readLimitId(snapshot) {
1462
1670
  return typeof value === "string" && value.trim() ? value.trim() : "codex";
1463
1671
  }
1464
1672
  //#endregion
1673
+ //#region extensions/codex/src/app-server/timeout.ts
1674
+ /**
1675
+ * Thin Codex app-server timeout adapter around OpenClaw's shared security
1676
+ * runtime timeout helper.
1677
+ */
1678
+ function resolveAbortError(signal) {
1679
+ return signal.reason instanceof Error ? signal.reason : new Error("Codex app-server operation aborted", { cause: signal.reason });
1680
+ }
1681
+ /** Awaits a promise with a Codex-specific timeout error message. */
1682
+ async function withTimeout$1(promise, timeoutMs, timeoutMessage, createError) {
1683
+ return await withTimeout(promise, timeoutMs, {
1684
+ message: timeoutMessage,
1685
+ ...createError ? { createError } : {}
1686
+ });
1687
+ }
1688
+ /** Bounds an operation by both its owner lifecycle and one total wall-clock budget. */
1689
+ async function withAbortableTimeout(params) {
1690
+ const signal = params.signal;
1691
+ if (signal?.aborted) throw resolveAbortError(signal);
1692
+ let removeAbortListener;
1693
+ const operation = signal ? Promise.race([params.promise, new Promise((_, reject) => {
1694
+ const onAbort = () => reject(resolveAbortError(signal));
1695
+ signal.addEventListener("abort", onAbort, { once: true });
1696
+ removeAbortListener = () => signal.removeEventListener("abort", onAbort);
1697
+ })]) : params.promise;
1698
+ try {
1699
+ return await withTimeout$1(operation, params.timeoutMs, params.timeoutMessage, params.createTimeoutError);
1700
+ } finally {
1701
+ removeAbortListener?.();
1702
+ }
1703
+ }
1704
+ //#endregion
1465
1705
  //#region extensions/codex/src/app-server/client-runtime.ts
1466
1706
  /** Client-scoped Codex auth and account observers. */
1467
1707
  /** Match Codex's native grace window without retaining inactive conversations indefinitely. */
1468
1708
  const CODEX_APP_SERVER_LIVE_THREAD_IDLE_TIMEOUT_MS = 30 * 6e4;
1469
1709
  /** Native-child parents are active ownership, so only otherwise-idle threads count against this cap. */
1470
1710
  const CODEX_APP_SERVER_LIVE_THREAD_MAX_IDLE = 64;
1711
+ /** Return a deterministic error before Codex cancels its ten-second external-auth request. */
1712
+ const CODEX_EXTERNAL_AUTH_REFRESH_TIMEOUT_MS = 9e3;
1471
1713
  const configuredClients = /* @__PURE__ */ new WeakMap();
1472
1714
  const physicalThreadReleases = /* @__PURE__ */ new WeakMap();
1473
1715
  const claimedThreadReleaseTokens = /* @__PURE__ */ new WeakMap();
@@ -1481,7 +1723,10 @@ function ensureCodexAppServerClientRuntime(client, context) {
1481
1723
  const existing = configuredClients.get(client);
1482
1724
  if (existing) {
1483
1725
  if (existing.closed) return;
1484
- existing.context = context;
1726
+ existing.context = {
1727
+ ...existing.context,
1728
+ config: context.config
1729
+ };
1485
1730
  return;
1486
1731
  }
1487
1732
  const runtime = {
@@ -1506,12 +1751,21 @@ function ensureCodexAppServerClientRuntime(client, context) {
1506
1751
  client.addRequestHandler(async (request) => {
1507
1752
  if (request.method !== "account/chatgptAuthTokens/refresh") return;
1508
1753
  if (runtime.context.authMode === "prepared-api-key") throw new Error("ChatGPT token refresh is unavailable for prepared Codex API-key auth.");
1509
- return { ...await refreshCodexAppServerAuthTokens({
1510
- agentDir: runtime.context.agentDir,
1511
- authProfileId: runtime.context.authProfileId,
1512
- ...runtime.context.authProfileStore ? { authProfileStore: runtime.context.authProfileStore } : {},
1513
- config: runtime.context.config
1514
- }) };
1754
+ const previousAccountId = isJsonObject(request.params) && typeof request.params.previousAccountId === "string" ? request.params.previousAccountId.trim() || void 0 : void 0;
1755
+ try {
1756
+ const tokens = await withTimeout$1(refreshCodexAppServerAuthTokens({
1757
+ agentDir: runtime.context.agentDir,
1758
+ authProfileId: runtime.context.authProfileId,
1759
+ ...previousAccountId ? { previousAccountId } : {},
1760
+ ...runtime.context.authProfileStore ? { authProfileStore: runtime.context.authProfileStore } : {},
1761
+ config: runtime.context.config
1762
+ }), CODEX_EXTERNAL_AUTH_REFRESH_TIMEOUT_MS, "Codex app-server ChatGPT token refresh timed out before its external-auth deadline. Retry the request; if it persists, sign in again with OpenClaw.");
1763
+ if (previousAccountId && tokens.chatgptAccountId !== previousAccountId) throw new Error("ChatGPT workspace changed during Codex token refresh. Retry to start a client for the selected workspace.");
1764
+ return { ...tokens };
1765
+ } catch (error) {
1766
+ runtime.context.onAuthRefreshFailure?.();
1767
+ throw error;
1768
+ }
1515
1769
  });
1516
1770
  client.addNotificationHandler((notification) => {
1517
1771
  if (notification.method === "account/rateLimits/updated") {
@@ -1547,10 +1801,11 @@ function scheduleRetainedThreadEviction(client, runtime) {
1547
1801
  }, Math.max(0, expiresAt - Date.now()));
1548
1802
  runtime.evictionTimer.unref?.();
1549
1803
  }
1550
- async function releaseRetainedThread(client, runtime, threadId) {
1804
+ async function releaseRetainedThread(client, runtime, threadId, assertCurrent) {
1551
1805
  const pendingRelease = runtime.releasingThreads.get(threadId);
1552
1806
  if (pendingRelease) {
1553
1807
  await pendingRelease.completion;
1808
+ assertCurrent?.();
1554
1809
  return false;
1555
1810
  }
1556
1811
  const retained = runtime.retainedThreads.get(threadId);
@@ -1562,7 +1817,10 @@ async function releaseRetainedThread(client, runtime, threadId) {
1562
1817
  }
1563
1818
  runtime.retainedThreads.delete(threadId);
1564
1819
  scheduleRetainedThreadEviction(client, runtime);
1565
- const transition = { completion: Promise.resolve().then(() => retained.release(threadId)) };
1820
+ const transition = { completion: Promise.resolve().then(() => {
1821
+ assertCurrent?.();
1822
+ return assertCurrent ? retained.release(threadId, assertCurrent) : retained.release(threadId);
1823
+ }) };
1566
1824
  runtime.releasingThreads.set(threadId, transition);
1567
1825
  try {
1568
1826
  await transition.completion;
@@ -1602,15 +1860,15 @@ async function retainCodexAppServerLiveThread(client, threadId, releaseThread, c
1602
1860
  const pendingRelease = runtime.releasingThreads.get(threadId);
1603
1861
  if (pendingRelease) {
1604
1862
  await pendingRelease.completion;
1605
- if (runtime.closed || claimed !== void 0 && runtime.claimedThreads.get(threadId) !== claimed) return false;
1863
+ return false;
1606
1864
  }
1607
1865
  runtime.retainedThreads.delete(threadId);
1608
1866
  const retained = {
1609
1867
  configFingerprint,
1610
1868
  serviceTier,
1611
1869
  expiresAt: Date.now() + CODEX_APP_SERVER_LIVE_THREAD_IDLE_TIMEOUT_MS,
1612
- release: (releaseThread ? physicalThreadReleases.get(releaseThread) ?? releaseThread : void 0) ?? (async (releasedThreadId) => {
1613
- await unsubscribeCodexAppServerLiveThread(client, releasedThreadId, 5e3);
1870
+ release: (releaseThread ? physicalThreadReleases.get(releaseThread) ?? releaseThread : void 0) ?? (async (releasedThreadId, assertCurrent) => {
1871
+ await unsubscribeCodexAppServerLiveThread(client, releasedThreadId, 5e3, assertCurrent);
1614
1872
  })
1615
1873
  };
1616
1874
  runtime.retainedThreads.set(threadId, retained);
@@ -1654,8 +1912,8 @@ async function claimCodexAppServerLiveThread(client, threadId) {
1654
1912
  }
1655
1913
  return claimCodexAppServerThreadOwnership(client, runtime, threadId, runtime.retainedThreads.get(threadId) ?? {
1656
1914
  expiresAt: Date.now() + CODEX_APP_SERVER_LIVE_THREAD_IDLE_TIMEOUT_MS,
1657
- release: async (releasedThreadId) => {
1658
- await unsubscribeCodexAppServerLiveThread(client, releasedThreadId, 5e3);
1915
+ release: async (releasedThreadId, assertCurrent) => {
1916
+ await unsubscribeCodexAppServerLiveThread(client, releasedThreadId, 5e3, assertCurrent);
1659
1917
  }
1660
1918
  });
1661
1919
  }
@@ -1664,7 +1922,7 @@ function claimCodexAppServerThreadOwnership(client, runtime, threadId, retained)
1664
1922
  const claimed = Symbol(threadId);
1665
1923
  runtime.claimedThreads.set(threadId, claimed);
1666
1924
  scheduleRetainedThreadEviction(client, runtime);
1667
- const release = async (releasedThreadId) => {
1925
+ const release = async (releasedThreadId, assertCurrent) => {
1668
1926
  if (releasedThreadId !== threadId || runtime.claimedThreads.get(threadId) !== claimed) return;
1669
1927
  const pendingRelease = runtime.releasingThreads.get(threadId);
1670
1928
  if (pendingRelease) {
@@ -1673,7 +1931,8 @@ function claimCodexAppServerThreadOwnership(client, runtime, threadId, retained)
1673
1931
  }
1674
1932
  const transition = { completion: Promise.resolve().then(async () => {
1675
1933
  if (runtime.closed || runtime.claimedThreads.get(threadId) !== claimed) return;
1676
- await retained.release(releasedThreadId);
1934
+ assertCurrent?.();
1935
+ await (assertCurrent ? retained.release(releasedThreadId, assertCurrent) : retained.release(releasedThreadId));
1677
1936
  }) };
1678
1937
  runtime.releasingThreads.set(threadId, transition);
1679
1938
  try {
@@ -1692,22 +1951,32 @@ function claimCodexAppServerThreadOwnership(client, runtime, threadId, retained)
1692
1951
  };
1693
1952
  }
1694
1953
  /** Distinguish active claimed ownership from an already-evicted idle subscription. */
1954
+ function hasCodexAppServerLiveThread(client, threadId) {
1955
+ const runtime = configuredClients.get(client);
1956
+ return runtime !== void 0 && !runtime.closed && (runtime.retainedThreads.has(threadId) || runtime.releasingThreads.has(threadId) || runtime.claimedThreads.has(threadId));
1957
+ }
1695
1958
  function isCodexAppServerLiveThreadClaimed(client, threadId) {
1696
1959
  const runtime = configuredClients.get(client);
1697
1960
  return runtime !== void 0 && !runtime.closed && runtime.claimedThreads.has(threadId);
1698
1961
  }
1699
- /** Release the exact physical subscription and finish only its observed claim generation. */
1700
- async function unsubscribeCodexAppServerLiveThread(client, threadId, timeoutMs) {
1962
+ /** Release the exact physical subscription and finish only its observed ownership generation. */
1963
+ async function unsubscribeCodexAppServerLiveThread(client, threadId, timeoutMs, assertCurrent) {
1701
1964
  const runtime = configuredClients.get(client);
1702
1965
  const claimed = runtime?.claimedThreads.get(threadId);
1966
+ const retained = runtime?.retainedThreads.get(threadId);
1703
1967
  let transition = runtime?.releasingThreads.get(threadId);
1704
1968
  if (transition?.physicalRelease) {
1705
1969
  await transition.physicalRelease;
1970
+ assertCurrent?.();
1706
1971
  return;
1707
1972
  }
1708
1973
  const physicalRelease = Promise.resolve().then(async () => {
1709
- if (claimed !== void 0 && runtime?.claimedThreads.get(threadId) !== claimed) return;
1710
- await client.request("thread/unsubscribe", { threadId }, { timeoutMs });
1974
+ if (claimed !== void 0 && runtime?.claimedThreads.get(threadId) !== claimed || retained !== void 0 && runtime?.retainedThreads.get(threadId) !== retained) return;
1975
+ assertCurrent?.();
1976
+ await client.request("thread/unsubscribe", { threadId }, {
1977
+ timeoutMs,
1978
+ assertCurrent
1979
+ });
1711
1980
  });
1712
1981
  const ownsTransition = runtime !== void 0 && transition === void 0;
1713
1982
  if (transition) transition.physicalRelease = physicalRelease;
@@ -1720,15 +1989,19 @@ async function unsubscribeCodexAppServerLiveThread(client, threadId, timeoutMs)
1720
1989
  }
1721
1990
  try {
1722
1991
  await physicalRelease;
1992
+ if (retained !== void 0 && runtime?.retainedThreads.get(threadId) === retained) {
1993
+ runtime.retainedThreads.delete(threadId);
1994
+ scheduleRetainedThreadEviction(client, runtime);
1995
+ }
1723
1996
  if (claimed !== void 0 && runtime?.claimedThreads.get(threadId) === claimed) runtime.claimedThreads.delete(threadId);
1724
1997
  } finally {
1725
1998
  if (ownsTransition && runtime.releasingThreads.get(threadId) === transition) runtime.releasingThreads.delete(threadId);
1726
1999
  }
1727
2000
  }
1728
2001
  /** Reset/end owns the exact thread; failed generation retirement must never release its successor. */
1729
- async function releaseCodexAppServerLiveThread(client, threadId) {
2002
+ async function releaseCodexAppServerLiveThread(client, threadId, assertCurrent) {
1730
2003
  const runtime = configuredClients.get(client);
1731
- return runtime ? await releaseRetainedThread(client, runtime, threadId) : false;
2004
+ return runtime ? await releaseRetainedThread(client, runtime, threadId, assertCurrent) : false;
1732
2005
  }
1733
2006
  /** Native child work pins its parent's subscription even after the foreground parent turn ends. */
1734
2007
  function protectCodexAppServerLiveThread(client, threadId) {
@@ -1746,11 +2019,9 @@ function protectCodexAppServerLiveThread(client, threadId) {
1746
2019
  runtime.protectedThreads.delete(threadId);
1747
2020
  const retained = runtime.retainedThreads.get(threadId);
1748
2021
  if (retained) {
2022
+ retained.expiresAt = Date.now() + CODEX_APP_SERVER_LIVE_THREAD_IDLE_TIMEOUT_MS;
1749
2023
  runtime.retainedThreads.delete(threadId);
1750
- runtime.retainedThreads.set(threadId, {
1751
- ...retained,
1752
- expiresAt: Date.now() + CODEX_APP_SERVER_LIVE_THREAD_IDLE_TIMEOUT_MS
1753
- });
2024
+ runtime.retainedThreads.set(threadId, retained);
1754
2025
  }
1755
2026
  } else runtime.protectedThreads.set(threadId, count - 1);
1756
2027
  scheduleRetainedThreadEviction(client, runtime);
@@ -1763,6 +2034,453 @@ function protectCodexAppServerLiveThread(client, threadId) {
1763
2034
  };
1764
2035
  }
1765
2036
  //#endregion
2037
+ //#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
2038
+ /** Retains the host-owned app preview without adding it to Codex's response payload. */
2039
+ function withDynamicToolTranscriptDetails(response, details) {
2040
+ if (details === void 0) return response;
2041
+ Object.defineProperty(response, "transcriptDetails", {
2042
+ configurable: true,
2043
+ enumerable: false,
2044
+ value: details
2045
+ });
2046
+ return response;
2047
+ }
2048
+ function withDynamicToolTerminalResolution(response, terminalResolution) {
2049
+ if (terminalResolution) {
2050
+ Object.defineProperties(response, {
2051
+ terminalResolution: {
2052
+ configurable: true,
2053
+ enumerable: false,
2054
+ value: terminalResolution
2055
+ },
2056
+ executionStarted: {
2057
+ configurable: true,
2058
+ enumerable: false,
2059
+ value: terminalResolution.executionStarted
2060
+ },
2061
+ ...terminalResolution.executedArguments ? { executedArguments: {
2062
+ configurable: true,
2063
+ enumerable: false,
2064
+ value: terminalResolution.executedArguments
2065
+ } } : {}
2066
+ });
2067
+ withDynamicToolSideEffectEvidence(response, terminalResolution.sideEffectEvidence);
2068
+ }
2069
+ return response;
2070
+ }
2071
+ function withDynamicToolExecutionState(response, state) {
2072
+ Object.defineProperties(response, {
2073
+ executedArguments: {
2074
+ configurable: true,
2075
+ enumerable: false,
2076
+ value: state.executedArguments
2077
+ },
2078
+ executionStarted: {
2079
+ configurable: true,
2080
+ enumerable: false,
2081
+ value: state.executionStarted
2082
+ }
2083
+ });
2084
+ return withDynamicToolSideEffectEvidence(response, state.sideEffectEvidence === true);
2085
+ }
2086
+ function withDynamicToolSideEffectEvidence(response, sideEffectEvidence) {
2087
+ if (!sideEffectEvidence) {
2088
+ delete response.sideEffectEvidence;
2089
+ return response;
2090
+ }
2091
+ Object.defineProperty(response, "sideEffectEvidence", {
2092
+ configurable: true,
2093
+ enumerable: false,
2094
+ value: true
2095
+ });
2096
+ return response;
2097
+ }
2098
+ function createFailedDynamicToolResponse(message, options) {
2099
+ const response = {
2100
+ contentItems: [{
2101
+ type: "inputText",
2102
+ text: message
2103
+ }],
2104
+ success: false
2105
+ };
2106
+ Object.defineProperties(response, {
2107
+ diagnosticTerminalReason: {
2108
+ configurable: true,
2109
+ enumerable: false,
2110
+ value: options?.terminalReason ?? "failed"
2111
+ },
2112
+ diagnosticTerminalType: {
2113
+ configurable: true,
2114
+ enumerable: false,
2115
+ value: "error"
2116
+ }
2117
+ });
2118
+ if (options?.executionStarted !== void 0) Object.defineProperty(response, "executionStarted", {
2119
+ configurable: true,
2120
+ enumerable: false,
2121
+ value: options.executionStarted
2122
+ });
2123
+ if (options?.executedArguments !== void 0) Object.defineProperty(response, "executedArguments", {
2124
+ configurable: true,
2125
+ enumerable: false,
2126
+ value: options.executedArguments
2127
+ });
2128
+ return withDynamicToolSideEffectEvidence(response, options?.sideEffectEvidence === true);
2129
+ }
2130
+ //#endregion
2131
+ //#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
2132
+ /** Leaf helper shared by native and dynamic tool diagnostics. */
2133
+ const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
2134
+ "codex_startup_timeout",
2135
+ "turn_completion_idle_timeout",
2136
+ "turn_progress_idle_timeout",
2137
+ "turn_terminal_idle_timeout"
2138
+ ]);
2139
+ /** Preserves timeout provenance when an enclosing run aborts an active tool. */
2140
+ function resolveCodexToolAbortTerminalReason(signal) {
2141
+ try {
2142
+ const reason = signal.reason;
2143
+ if (typeof reason === "string") {
2144
+ if (CODEX_TIMEOUT_ABORT_REASONS.has(reason)) return "timed_out";
2145
+ return reason === "client_closed" ? "failed" : "cancelled";
2146
+ }
2147
+ if (reason && typeof reason === "object") {
2148
+ const record = reason;
2149
+ if (record.name === "TimeoutError" || record.reason === "timeout") return "timed_out";
2150
+ }
2151
+ } catch {
2152
+ return "cancelled";
2153
+ }
2154
+ return "cancelled";
2155
+ }
2156
+ //#endregion
2157
+ //#region extensions/codex/src/app-server/dynamic-tool-execution.ts
2158
+ /**
2159
+ * Timeout, terminal-release, and diagnostic helpers for Codex dynamic tool
2160
+ * calls.
2161
+ */
2162
+ /** Default timeout for Codex dynamic tool calls. */
2163
+ const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 9e4;
2164
+ /** Hard cap for per-call Codex dynamic tool timeout overrides. */
2165
+ const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
2166
+ const CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS = 3e4;
2167
+ const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
2168
+ const CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS = 3e4;
2169
+ const CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS = 3e4;
2170
+ /** Timeout for image-understanding style dynamic tool calls. */
2171
+ const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
2172
+ /** Timeout for message-delivery dynamic tool calls. */
2173
+ const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 6e5;
2174
+ /** Outer default for collector waits: full swarm budget plus completion grace. */
2175
+ const CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS = 63e4;
2176
+ const LOG_FIELD_MAX_LENGTH = 160;
2177
+ function normalizeLogField(value) {
2178
+ if (typeof value !== "string") return;
2179
+ const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
2180
+ if (!normalized) return;
2181
+ return normalized.length > LOG_FIELD_MAX_LENGTH ? `${truncateUtf16Safe(normalized, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
2182
+ }
2183
+ function readNumericTimeoutMs(value) {
2184
+ if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
2185
+ if (typeof value === "string") {
2186
+ const parsed = parseStrictNonNegativeInteger(value);
2187
+ if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
2188
+ }
2189
+ }
2190
+ function formatDynamicToolTimeoutDetails(params) {
2191
+ const tool = normalizeLogField(params.call.tool) ?? "unknown";
2192
+ const baseMeta = {
2193
+ tool: params.call.tool,
2194
+ toolCallId: params.call.callId,
2195
+ threadId: params.call.threadId,
2196
+ turnId: params.call.turnId,
2197
+ timeoutMs: params.timeoutMs,
2198
+ timeoutKind: "codex_dynamic_tool_rpc"
2199
+ };
2200
+ if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
2201
+ responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
2202
+ consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
2203
+ meta: baseMeta
2204
+ };
2205
+ const action = normalizeLogField(params.call.arguments.action);
2206
+ const sessionId = normalizeLogField(params.call.arguments.sessionId);
2207
+ const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
2208
+ const actionPart = action ? ` action=${action}` : "";
2209
+ const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
2210
+ const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
2211
+ const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
2212
+ const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
2213
+ return {
2214
+ responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
2215
+ consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
2216
+ meta: {
2217
+ ...baseMeta,
2218
+ processAction: action,
2219
+ processSessionId: sessionId,
2220
+ processRequestedTimeoutMs: requestedTimeoutMs
2221
+ }
2222
+ };
2223
+ }
2224
+ /**
2225
+ * Runs a dynamic tool call with run-abort and the budget prepared by
2226
+ * resolveDynamicToolCallTimeoutMs, preserving tool-specific completion grace.
2227
+ */
2228
+ async function handleDynamicToolCallWithTimeout(params) {
2229
+ let didNotifyAgentToolResult = false;
2230
+ const conservativeRaceResponses = /* @__PURE__ */ new WeakSet();
2231
+ const finalizeTerminal = (response) => {
2232
+ const executionSnapshot = params.toolBridge.consumeToolExecutionSnapshot?.(params.call.callId);
2233
+ const ownerKey = params.toolBridge.sideEffectOwnerKeyForTool?.(params.call.tool);
2234
+ const observedExecutionStarted = executionSnapshot?.executionStarted ?? (conservativeRaceResponses.has(response) ? void 0 : response.executionStarted);
2235
+ const terminalResolution = params.observeToolTerminal?.({
2236
+ toolCallId: params.call.callId,
2237
+ toolName: params.call.tool,
2238
+ arguments: response.executedArguments ?? executionSnapshot?.executedArguments ?? params.call.arguments,
2239
+ ...params.toolMeta ? { meta: params.toolMeta } : {},
2240
+ ...ownerKey ? { ownerMutation: { ownerKey } } : {},
2241
+ ...observedExecutionStarted !== void 0 ? { executionStarted: observedExecutionStarted } : {},
2242
+ outcome: response.success ? "success" : "failure",
2243
+ ...!response.success ? { failure: { error: readDynamicToolResponseText(response) } } : {}
2244
+ });
2245
+ return withDynamicToolTerminalResolution(response, terminalResolution);
2246
+ };
2247
+ const createFailedAfterPossibleDispatch = (message, terminalReason) => {
2248
+ const response = createFailedDynamicToolResponse(message, {
2249
+ executionStarted: true,
2250
+ sideEffectEvidence: true,
2251
+ terminalReason
2252
+ });
2253
+ conservativeRaceResponses.add(response);
2254
+ return response;
2255
+ };
2256
+ const notifyAgentToolResult = (event) => {
2257
+ if (didNotifyAgentToolResult) return;
2258
+ didNotifyAgentToolResult = true;
2259
+ try {
2260
+ params.onAgentToolResult?.(event);
2261
+ } catch (error) {
2262
+ embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
2263
+ }
2264
+ };
2265
+ const notifyFailedToolResult = (message, terminalReason = "failed") => {
2266
+ notifyAgentToolResult({
2267
+ toolName: params.call.tool,
2268
+ result: {
2269
+ content: [{
2270
+ type: "text",
2271
+ text: message
2272
+ }],
2273
+ details: {
2274
+ status: terminalReason,
2275
+ error: message
2276
+ }
2277
+ },
2278
+ isError: true
2279
+ });
2280
+ };
2281
+ if (params.signal.aborted) {
2282
+ const message = "OpenClaw dynamic tool call aborted before execution.";
2283
+ const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
2284
+ params.onFallbackSelected?.();
2285
+ notifyFailedToolResult(message, terminalReason);
2286
+ return finalizeTerminal(createFailedDynamicToolResponse(message, {
2287
+ executionStarted: false,
2288
+ terminalReason
2289
+ }));
2290
+ }
2291
+ const controller = new AbortController();
2292
+ let timeout;
2293
+ let timedOut = false;
2294
+ let resolveAbort;
2295
+ const abortFromRun = () => {
2296
+ const message = "OpenClaw dynamic tool call aborted.";
2297
+ const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
2298
+ params.onFallbackSelected?.();
2299
+ controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
2300
+ notifyFailedToolResult(message, terminalReason);
2301
+ resolveAbort?.(createFailedAfterPossibleDispatch(message, terminalReason));
2302
+ };
2303
+ const abortPromise = new Promise((resolve) => {
2304
+ resolveAbort = resolve;
2305
+ });
2306
+ const timeoutPromise = new Promise((resolve) => {
2307
+ const { timeoutMs } = params;
2308
+ timeout = setTimeout(() => {
2309
+ timedOut = true;
2310
+ const timeoutDetails = formatDynamicToolTimeoutDetails({
2311
+ call: params.call,
2312
+ timeoutMs
2313
+ });
2314
+ params.onFallbackSelected?.();
2315
+ controller.abort(new Error(timeoutDetails.responseMessage));
2316
+ params.onTimeout?.();
2317
+ embeddedAgentLog.warn("codex dynamic tool call timed out", {
2318
+ ...timeoutDetails.meta,
2319
+ consoleMessage: timeoutDetails.consoleMessage
2320
+ });
2321
+ notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
2322
+ resolve(createFailedAfterPossibleDispatch(timeoutDetails.responseMessage, "timed_out"));
2323
+ }, timeoutMs);
2324
+ timeout.unref?.();
2325
+ });
2326
+ try {
2327
+ params.signal.addEventListener("abort", abortFromRun, { once: true });
2328
+ if (params.signal.aborted) abortFromRun();
2329
+ const response = await Promise.race([
2330
+ params.toolBridge.handleToolCall(params.call, {
2331
+ signal: controller.signal,
2332
+ onAgentToolResult: notifyAgentToolResult,
2333
+ toolCallOrdinal: params.toolCallOrdinal,
2334
+ retainExecutionSnapshot: true
2335
+ }),
2336
+ abortPromise,
2337
+ timeoutPromise
2338
+ ]);
2339
+ if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
2340
+ return finalizeTerminal(response);
2341
+ } catch (error) {
2342
+ const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
2343
+ const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
2344
+ notifyFailedToolResult(message, terminalReason);
2345
+ return finalizeTerminal(createFailedAfterPossibleDispatch(message, terminalReason));
2346
+ } finally {
2347
+ if (timeout) clearTimeout(timeout);
2348
+ params.signal.removeEventListener("abort", abortFromRun);
2349
+ resolveAbort = void 0;
2350
+ if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
2351
+ }
2352
+ }
2353
+ function readDynamicToolResponseText(response) {
2354
+ return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
2355
+ }
2356
+ /** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
2357
+ function toCodexDynamicToolProtocolResponse(response) {
2358
+ return {
2359
+ contentItems: response.contentItems,
2360
+ success: response.success
2361
+ };
2362
+ }
2363
+ /** Adds async-started progress details when a tool result continues out of band. */
2364
+ function toCodexDynamicToolProgressResponse(response, protocolResponse) {
2365
+ const transcriptDetails = isJsonObject(response.transcriptDetails) ? response.transcriptDetails : void 0;
2366
+ const mcpAppPreview = isJsonObject(transcriptDetails?.mcpAppPreview) ? transcriptDetails.mcpAppPreview : void 0;
2367
+ const progressDetails = mcpAppPreview ? { mcpAppPreview } : void 0;
2368
+ if (response.asyncStarted !== true && progressDetails === void 0) return protocolResponse;
2369
+ return {
2370
+ ...protocolResponse,
2371
+ ...progressDetails ? { details: progressDetails } : {},
2372
+ ...response.asyncStarted === true ? { details: {
2373
+ ...progressDetails,
2374
+ async: true,
2375
+ status: "started"
2376
+ } } : {}
2377
+ };
2378
+ }
2379
+ /** Decides whether a terminal dynamic tool response can release the Codex turn. */
2380
+ function shouldReleaseTurnAfterTerminalDynamicTool(state) {
2381
+ return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
2382
+ }
2383
+ /** Returns true when a non-async result should block terminal-release shortcuts. */
2384
+ function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
2385
+ return response.asyncStarted !== true;
2386
+ }
2387
+ /** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
2388
+ function resolveTerminalDynamicToolBatchAction(state) {
2389
+ if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
2390
+ if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
2391
+ if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
2392
+ return "idle";
2393
+ }
2394
+ /** Returns true for diagnostic events that terminate a dynamic tool call. */
2395
+ function isDynamicToolTerminalDiagnosticEvent(event) {
2396
+ return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
2397
+ }
2398
+ /** Matches terminal diagnostics to a specific dynamic tool call id/name. */
2399
+ function isMatchingDynamicToolTerminalDiagnostic(params) {
2400
+ if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
2401
+ if (params.runId !== void 0) return params.event.runId === params.runId;
2402
+ if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
2403
+ if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
2404
+ return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
2405
+ }
2406
+ /** Checks pending diagnostics for a terminal event matching a tool call. */
2407
+ function hasPendingDynamicToolTerminalDiagnostic(params) {
2408
+ return hasPendingInternalDiagnosticEvent((event) => {
2409
+ if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
2410
+ return isMatchingDynamicToolTerminalDiagnostic({
2411
+ event,
2412
+ call: params.call,
2413
+ runId: params.runId,
2414
+ sessionId: params.sessionId,
2415
+ sessionKey: params.sessionKey
2416
+ });
2417
+ });
2418
+ }
2419
+ /** Resolves per-tool timeout, applying media/message defaults and hard caps. */
2420
+ function resolveDynamicToolCallTimeoutMs(params) {
2421
+ const args = isJsonObject(params.call.arguments) ? params.call.arguments : void 0;
2422
+ if (params.call.tool === "ask_user" || params.call.tool === "secrets" && args?.action === "request") try {
2423
+ return normalizeQuestionTimeoutSeconds(args?.timeoutSeconds) * 1e3 + CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS;
2424
+ } catch {
2425
+ return CODEX_DYNAMIC_TOOL_TIMEOUT_MS;
2426
+ }
2427
+ if (params.call.tool === "computer") return clampDynamicToolTimeoutMs(readComputerToolTimeoutMs(params.call.arguments));
2428
+ if (params.call.tool === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
2429
+ if (params.call.tool === "agents_wait") {
2430
+ const requestedMs = readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_AGENTS_WAIT_TOOL_TIMEOUT_MS;
2431
+ return Math.max(1, Math.min(63e4, Math.floor(requestedMs)));
2432
+ }
2433
+ return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? CODEX_DYNAMIC_TOOL_TIMEOUT_MS);
2434
+ }
2435
+ /** Transport guard stays outside the handler's bounded tool wait and completion grace. */
2436
+ function resolveDynamicToolServerRequestTimeoutMs(call) {
2437
+ return Math.max(63e4, call ? resolveDynamicToolCallTimeoutMs({
2438
+ call,
2439
+ config: void 0
2440
+ }) : 0) + CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS;
2441
+ }
2442
+ function readComputerToolTimeoutMs(value) {
2443
+ const args = isJsonObject(value) ? value : void 0;
2444
+ const action = typeof args?.action === "string" ? args.action : void 0;
2445
+ const gatewayTimeoutMs = readPositiveFiniteTimeoutMs(args?.timeoutMs) ?? CODEX_DYNAMIC_COMPUTER_GATEWAY_TIMEOUT_MS;
2446
+ const gatewayCallCount = action === "screenshot" || action === "wait" ? 3 : 4;
2447
+ return (action === "wait" || action === "hold_key" ? Math.max(0, Number(args?.duration) || 0) * 1e3 : 0) + gatewayCallCount * gatewayTimeoutMs + CODEX_DYNAMIC_COMPUTER_COMPLETION_GRACE_MS;
2448
+ }
2449
+ function readDynamicToolCallTimeoutMs(value) {
2450
+ if (!isJsonObject(value)) return;
2451
+ const timeoutMs = readPositiveFiniteTimeoutMs(value.timeoutMs);
2452
+ if (timeoutMs !== void 0) return timeoutMs;
2453
+ const timeoutSecondsMs = readDynamicToolTimeoutSecondsAsMs(value.timeoutSeconds);
2454
+ return timeoutSecondsMs === void 0 ? void 0 : addTimerTimeoutGraceMs(timeoutSecondsMs, CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS);
2455
+ }
2456
+ function readConfiguredDynamicToolTimeoutMs(toolName, config) {
2457
+ if (toolName === "image_generate") {
2458
+ const imageModel = config?.agents?.defaults?.mediaModels?.image;
2459
+ if (!imageModel || typeof imageModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
2460
+ return readPositiveFiniteTimeoutMs(imageModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
2461
+ }
2462
+ if (toolName === "view_image") {
2463
+ const candidates = (config?.tools?.media?.models ?? []).filter((entry) => !entry.capabilities || entry.capabilities.includes("image"));
2464
+ const capabilityTimeoutMs = readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds);
2465
+ return Math.max(capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS, ...candidates.map((entry) => readTimeoutSecondsAsMs(entry.timeoutSeconds) ?? capabilityTimeoutMs ?? CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS));
2466
+ }
2467
+ if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
2468
+ }
2469
+ function readTimeoutSecondsAsMs(value) {
2470
+ const seconds = readPositiveFiniteTimeoutMs(value);
2471
+ return seconds === void 0 ? void 0 : seconds * 1e3;
2472
+ }
2473
+ function readDynamicToolTimeoutSecondsAsMs(value) {
2474
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) return;
2475
+ return value * 1e3;
2476
+ }
2477
+ function readPositiveFiniteTimeoutMs(value) {
2478
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
2479
+ }
2480
+ function clampDynamicToolTimeoutMs(timeoutMs) {
2481
+ return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
2482
+ }
2483
+ //#endregion
1766
2484
  //#region extensions/codex/src/app-server/elicitation-response.ts
1767
2485
  function createCodexElicitationResponse(action, content = null, meta = null) {
1768
2486
  return {
@@ -1937,359 +2655,35 @@ function createWebSocketTransport(options) {
1937
2655
  stdout,
1938
2656
  stderr,
1939
2657
  get killed() {
1940
- return killed;
1941
- },
1942
- kill: () => {
1943
- killed = true;
1944
- clearConnectionHealthTimers();
1945
- socket.close();
1946
- },
1947
- once: (event, listener) => events.once(event, listener)
1948
- };
1949
- }
1950
- /** Opens the owner-scoped Codex control socket used by the WebSocket upgrade. */
1951
- function connectCodexAppServerUnixSocket(socketPath) {
1952
- return net.createConnection(socketPath);
1953
- }
1954
- /** Resolves the canonical or explicitly configured Codex control socket. */
1955
- function resolveCodexAppServerUnixSocketPath(options) {
1956
- if (options.transport !== "unix") {
1957
- if (options.url?.startsWith("unix://")) throw new Error("codex app-server unix URL requires unix transport");
1958
- return;
1959
- }
1960
- const url = options.url ?? "unix://";
1961
- if (!url.startsWith("unix://")) throw new Error("codex app-server unix transport requires a unix:// URL");
1962
- return url.slice(7) || path.join(resolveCodexAppServerUserHomeDir(options.env ?? process.env), "app-server-control", "app-server-control.sock");
1963
- }
1964
- function websocketFrameToText(data) {
1965
- if (typeof data === "string") return data;
1966
- if (Buffer.isBuffer(data)) return data.toString("utf8");
1967
- if (Array.isArray(data)) return Buffer.concat(data).toString("utf8");
1968
- return Buffer.from(data).toString("utf8");
1969
- }
1970
- //#endregion
1971
- //#region extensions/codex/src/app-server/transport-process-containment.ts
1972
- const PROCESS_COLUMNS = "pid=,ppid=,pgid=,stat=,lstart=";
1973
- const MAX_CONTAINED_PROCESSES = 512;
1974
- const MAX_PROCESS_CONTAINMENT_MS = 2e3;
1975
- const MAX_PROCESS_QUIESCE_PASSES = 16;
1976
- const PROCESS_INSPECTION_MAX_BYTES = 8 * 1024 * 1024;
1977
- async function terminateCodexAppServerDescendants(child) {
1978
- const rootPid = child.pid;
1979
- if (process.platform === "win32" || !rootPid || !child.kill || hasExited(child)) return;
1980
- const deadline = Date.now() + MAX_PROCESS_CONTAINMENT_MS;
1981
- const snapshot = await readProcessSnapshot(deadline);
1982
- if (!snapshot || Date.now() >= deadline) return;
1983
- const root = snapshot.find((row) => row.pid === rootPid);
1984
- if (!root || !isSameLiveRoot(root, root)) return;
1985
- const initialDescendants = collectDescendants(snapshot, [rootPid]);
1986
- if (initialDescendants.length > MAX_CONTAINED_PROCESSES) return;
1987
- const stoppedDescendants = /* @__PURE__ */ new Map();
1988
- if (!await signalSameRoot(root, "SIGSTOP", deadline)) return;
1989
- let resumeRootOnUnwind = true;
1990
- try {
1991
- const descendants = await quiesceDescendants(root, initialDescendants, stoppedDescendants, deadline);
1992
- if (!descendants) return;
1993
- for (const descendant of descendants.toReversed()) {
1994
- if (Date.now() >= deadline) return;
1995
- if (!descendant.state.startsWith("Z")) {
1996
- if (!await signalSameProcess(descendant, "SIGKILL", deadline) || Date.now() >= deadline) return;
1997
- }
1998
- }
1999
- resumeRootOnUnwind = false;
2000
- let resumed = false;
2001
- return () => {
2002
- if (resumed) return;
2003
- resumed = true;
2004
- resumeTransportRoot(child, root, false);
2005
- };
2006
- } finally {
2007
- if (resumeRootOnUnwind) {
2008
- for (const descendant of stoppedDescendants.values()) signalProcess(descendant.pid, "SIGCONT");
2009
- resumeTransportRoot(child, root, true);
2010
- }
2011
- }
2012
- }
2013
- async function quiesceDescendants(root, initialDescendants, stopped, deadline) {
2014
- const provenByPid = new Map(initialDescendants.map((descendant) => [descendant.pid, descendant]));
2015
- const stopFailures = /* @__PURE__ */ new Map();
2016
- for (let pass = 0; pass < MAX_PROCESS_QUIESCE_PASSES; pass += 1) {
2017
- if (Date.now() >= deadline) return;
2018
- const snapshot = await readProcessSnapshot(deadline);
2019
- if (!snapshot || Date.now() >= deadline) return;
2020
- const currentRoot = snapshot.find((row) => row.pid === root.pid);
2021
- if (!currentRoot || !isSameLiveRoot(currentRoot, root)) return;
2022
- if (!isSameLiveRoot(currentRoot, root, true)) {
2023
- if (!await signalSameRoot(root, "SIGSTOP", deadline) || Date.now() >= deadline) return;
2024
- continue;
2025
- }
2026
- const snapshotByPid = new Map(snapshot.map((process) => [process.pid, process]));
2027
- const liveProven = [];
2028
- for (const proven of provenByPid.values()) {
2029
- const current = snapshotByPid.get(proven.pid);
2030
- if (!current) {
2031
- provenByPid.delete(proven.pid);
2032
- stopped.delete(identityKey(proven));
2033
- continue;
2034
- }
2035
- if (!hasSameIdentity(proven, current)) return;
2036
- provenByPid.set(current.pid, current);
2037
- const key = identityKey(current);
2038
- if (stopped.has(key)) stopped.set(key, current);
2039
- liveProven.push(current);
2040
- }
2041
- const descendants = collectDescendants(snapshot, [root.pid, ...liveProven.map(({ pid }) => pid)]);
2042
- for (const descendant of descendants) {
2043
- const proven = provenByPid.get(descendant.pid);
2044
- if (proven && !hasSameIdentity(proven, descendant)) return;
2045
- provenByPid.set(descendant.pid, descendant);
2046
- }
2047
- if (provenByPid.size > MAX_CONTAINED_PROCESSES) return;
2048
- const quiescenceTargets = new Map(liveProven.map((process) => [process.pid, process]));
2049
- for (const descendant of descendants) quiescenceTargets.set(descendant.pid, descendant);
2050
- let allStopped = true;
2051
- for (const descendant of quiescenceTargets.values()) {
2052
- if (Date.now() >= deadline) return;
2053
- if (isStoppedState(descendant.state)) continue;
2054
- const stopQueued = await signalSameProcess(descendant, "SIGSTOP", deadline);
2055
- if (Date.now() >= deadline) return;
2056
- if (stopQueued) {
2057
- stopFailures.delete(identityKey(descendant));
2058
- stopped.set(identityKey(descendant), descendant);
2059
- } else {
2060
- const key = identityKey(descendant);
2061
- const failures = (stopFailures.get(key) ?? 0) + 1;
2062
- if (failures >= 2) return;
2063
- stopFailures.set(key, failures);
2064
- }
2065
- if (!isUninterruptibleState(descendant.state) || !stopQueued) allStopped = false;
2066
- }
2067
- if (allStopped) return [...provenByPid.values()];
2068
- }
2069
- }
2070
- async function readProcessSnapshot(deadline) {
2071
- return await readProcesses(["-axo", PROCESS_COLUMNS], deadline);
2072
- }
2073
- async function readProcess(pid, deadline) {
2074
- return (await readProcesses([
2075
- "-o",
2076
- PROCESS_COLUMNS,
2077
- "-p",
2078
- String(pid)
2079
- ], deadline))?.find((row) => row.pid === pid);
2080
- }
2081
- async function readProcesses(args, deadline) {
2082
- const remainingMs = deadline - Date.now();
2083
- if (remainingMs <= 0) return;
2084
- return await new Promise((resolve) => {
2085
- let settled = false;
2086
- const settle = (processes) => {
2087
- if (settled) return;
2088
- settled = true;
2089
- clearTimeout(timer);
2090
- resolve(processes);
2091
- };
2092
- const inspector = execFile("ps", args, {
2093
- encoding: "utf8",
2094
- maxBuffer: PROCESS_INSPECTION_MAX_BYTES
2095
- }, (error, stdout) => {
2096
- settle(error ? void 0 : parseProcesses(stdout));
2097
- });
2098
- const timer = setTimeout(() => {
2099
- settle(void 0);
2100
- inspector.stdout?.destroy();
2101
- inspector.stderr?.destroy();
2102
- inspector.kill("SIGKILL");
2103
- inspector.unref();
2104
- }, Math.max(1, remainingMs));
2105
- timer.unref?.();
2106
- });
2107
- }
2108
- function parseProcesses(output) {
2109
- const rows = [];
2110
- for (const line of output.split("\n")) {
2111
- const match = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.+?)\s*$/.exec(line);
2112
- if (!match) continue;
2113
- const pid = Number(match[1] ?? "");
2114
- const ppid = Number(match[2] ?? "");
2115
- const pgid = Number(match[3] ?? "");
2116
- const startedAt = (match[5] ?? "").trim().replace(/\s+/g, " ");
2117
- if (![
2118
- pid,
2119
- ppid,
2120
- pgid
2121
- ].every(Number.isSafeInteger) || pid <= 0 || ppid < 0 || pgid <= 0 || !startedAt) continue;
2122
- rows.push({
2123
- pid,
2124
- ppid,
2125
- pgid,
2126
- state: match[4] ?? "",
2127
- startedAt
2128
- });
2129
- }
2130
- return rows;
2131
- }
2132
- function collectDescendants(snapshot, rootPids) {
2133
- const childrenByParent = /* @__PURE__ */ new Map();
2134
- for (const row of snapshot) {
2135
- const children = childrenByParent.get(row.ppid) ?? [];
2136
- children.push(row);
2137
- childrenByParent.set(row.ppid, children);
2138
- }
2139
- const descendants = [];
2140
- const pending = [...new Set(rootPids)];
2141
- const seen = new Set(pending);
2142
- for (const parentPid of pending) for (const child of childrenByParent.get(parentPid) ?? []) {
2143
- if (seen.has(child.pid)) continue;
2144
- seen.add(child.pid);
2145
- descendants.push(child);
2146
- pending.push(child.pid);
2147
- }
2148
- return descendants;
2149
- }
2150
- function isStoppedState(state) {
2151
- return state.startsWith("T") || state.startsWith("t") || state.startsWith("Z");
2152
- }
2153
- function isQuiescedState(state) {
2154
- return isStoppedState(state) || isUninterruptibleState(state);
2155
- }
2156
- function isUninterruptibleState(state) {
2157
- return state.startsWith("D") || state.startsWith("U");
2158
- }
2159
- function isSameLiveProcess(current, expected) {
2160
- return current.pgid === expected.pgid && !current.state.startsWith("Z") && hasSameIdentity(current, expected);
2161
- }
2162
- function isSameLiveRoot(current, expected, requireStopped = false) {
2163
- return current.ppid === process.pid && (!requireStopped || isQuiescedState(current.state)) && isSameLiveProcess(current, expected);
2164
- }
2165
- async function signalSameRoot(root, signal, deadline) {
2166
- const current = await readProcess(root.pid, deadline);
2167
- return Boolean(current && isSameLiveRoot(current, root) && signalProcess(current.pid, signal));
2168
- }
2169
- function resumeTransportRoot(child, root, allowSynchronousPidFallback) {
2170
- try {
2171
- if (child.kill) {
2172
- child.kill("SIGCONT");
2173
- return;
2174
- }
2175
- } catch {
2176
- if (!allowSynchronousPidFallback) return;
2177
- }
2178
- if (allowSynchronousPidFallback) signalProcess(root.pid, "SIGCONT");
2179
- }
2180
- async function signalSameProcess(expected, signal, deadline) {
2181
- const current = await readProcess(expected.pid, deadline);
2182
- return Boolean(current && isSameLiveProcess(current, expected) && signalProcess(current.pid, signal));
2183
- }
2184
- function hasSameIdentity(left, right) {
2185
- return identityKey(left) === identityKey(right);
2186
- }
2187
- function identityKey(row) {
2188
- return `${row.pid}\0${row.startedAt}`;
2189
- }
2190
- function hasExited(child) {
2191
- return child.exitCode != null || child.signalCode != null;
2192
- }
2193
- function signalProcess(pid, signal) {
2194
- try {
2195
- process.kill(pid, signal);
2196
- return true;
2197
- } catch {
2198
- return false;
2199
- }
2200
- }
2201
- //#endregion
2202
- //#region extensions/codex/src/app-server/transport.ts
2203
- /**
2204
- * Shared transport lifecycle helpers for stdio and WebSocket Codex app-server
2205
- * connections.
2206
- */
2207
- const CODEX_APP_SERVER_TRANSPORT_CLOSES = /* @__PURE__ */ new WeakMap();
2208
- /** Starts graceful transport shutdown and schedules a force kill fallback. */
2209
- function closeCodexAppServerTransport(child, options = {}) {
2210
- beginCodexAppServerTransportClose(child, options);
2211
- }
2212
- function beginCodexAppServerTransportClose(child, options) {
2213
- const current = CODEX_APP_SERVER_TRANSPORT_CLOSES.get(child);
2214
- if (current) return current;
2215
- if (process.platform === "win32" || !child.pid || !child.kill || hasCodexAppServerTransportExited(child)) {
2216
- finishCodexAppServerTransportClose(child, options);
2217
- const completed = Promise.resolve();
2218
- CODEX_APP_SERVER_TRANSPORT_CLOSES.set(child, completed);
2219
- return completed;
2220
- }
2221
- const closing = (async () => {
2222
- let resumeRoot;
2223
- try {
2224
- resumeRoot = await terminateCodexAppServerDescendants(child);
2225
- } catch {
2226
- resumeRoot = void 0;
2227
- }
2228
- try {
2229
- finishCodexAppServerTransportClose(child, options, resumeRoot);
2230
- } catch {
2231
- signalCodexAppServerTransport(child, "SIGKILL");
2232
- }
2233
- })();
2234
- CODEX_APP_SERVER_TRANSPORT_CLOSES.set(child, closing);
2235
- return closing;
2236
- }
2237
- function finishCodexAppServerTransportClose(child, options, resumeRoot) {
2238
- const forceKillDelayMs = options.forceKillDelayMs ?? 1e3;
2239
- const forceKill = setTimeout(() => {
2240
- if (hasCodexAppServerTransportExited(child)) return;
2241
- signalCodexAppServerTransport(child, "SIGKILL");
2242
- }, Math.max(1, forceKillDelayMs));
2243
- forceKill.unref?.();
2244
- child.once("exit", () => {
2245
- clearTimeout(forceKill);
2246
- child.stdout.destroy?.();
2247
- child.stderr.destroy?.();
2248
- });
2249
- try {
2250
- child.stdin.end?.();
2251
- child.stdin.destroy?.();
2252
- } finally {
2253
- resumeRoot?.();
2254
- }
2255
- child.unref?.();
2256
- child.stdout.unref?.();
2257
- child.stderr.unref?.();
2258
- child.stdin.unref?.();
2259
- }
2260
- /** Closes a transport and waits briefly for an exit event. */
2261
- async function closeCodexAppServerTransportAndWait(child, options = {}) {
2262
- if (!hasCodexAppServerTransportExited(child)) await beginCodexAppServerTransportClose(child, options);
2263
- return await waitForCodexAppServerTransportExit(child, options.exitTimeoutMs ?? 2e3);
2264
- }
2265
- function hasCodexAppServerTransportExited(child) {
2266
- return child.exitCode !== null && child.exitCode !== void 0 ? true : child.signalCode !== null && child.signalCode !== void 0;
2658
+ return killed;
2659
+ },
2660
+ kill: () => {
2661
+ killed = true;
2662
+ clearConnectionHealthTimers();
2663
+ socket.close();
2664
+ },
2665
+ once: (event, listener) => events.once(event, listener)
2666
+ };
2267
2667
  }
2268
- async function waitForCodexAppServerTransportExit(child, timeoutMs) {
2269
- if (hasCodexAppServerTransportExited(child)) return true;
2270
- return await new Promise((resolve) => {
2271
- let settled = false;
2272
- const onExit = () => {
2273
- if (settled) return;
2274
- settled = true;
2275
- clearTimeout(timeout);
2276
- resolve(true);
2277
- };
2278
- const timeout = setTimeout(() => {
2279
- if (settled) return;
2280
- settled = true;
2281
- child.off?.("exit", onExit);
2282
- resolve(false);
2283
- }, Math.max(1, timeoutMs));
2284
- child.once("exit", onExit);
2285
- });
2668
+ /** Opens the owner-scoped Codex control socket used by the WebSocket upgrade. */
2669
+ function connectCodexAppServerUnixSocket(socketPath) {
2670
+ return net.createConnection(socketPath);
2286
2671
  }
2287
- function signalCodexAppServerTransport(child, signal) {
2288
- if (child.pid && process.platform !== "win32") try {
2289
- process.kill(-child.pid, signal);
2672
+ /** Resolves the canonical or explicitly configured Codex control socket. */
2673
+ function resolveCodexAppServerUnixSocketPath(options) {
2674
+ if (options.transport !== "unix") {
2675
+ if (options.url?.startsWith("unix://")) throw new Error("codex app-server unix URL requires unix transport");
2290
2676
  return;
2291
- } catch {}
2292
- child.kill?.(signal);
2677
+ }
2678
+ const url = options.url ?? "unix://";
2679
+ if (!url.startsWith("unix://")) throw new Error("codex app-server unix transport requires a unix:// URL");
2680
+ return url.slice(7) || path.join(resolveCodexAppServerUserHomeDir(options.env ?? process.env), "app-server-control", "app-server-control.sock");
2681
+ }
2682
+ function websocketFrameToText(data) {
2683
+ if (typeof data === "string") return data;
2684
+ if (Buffer.isBuffer(data)) return data.toString("utf8");
2685
+ if (Array.isArray(data)) return Buffer.concat(data).toString("utf8");
2686
+ return Buffer.from(data).toString("utf8");
2293
2687
  }
2294
2688
  //#endregion
2295
2689
  //#region extensions/codex/src/app-server/client.ts
@@ -2300,11 +2694,11 @@ function signalCodexAppServerTransport(child, signal) {
2300
2694
  const CODEX_APP_SERVER_PARSE_LOG_MAX = 500;
2301
2695
  const CODEX_APP_SERVER_PARSE_BUFFER_MAX = 8 * 1024 * 1024;
2302
2696
  const CODEX_APP_SERVER_PARSE_BUFFER_MAX_LINES = 1e3;
2303
- const CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS = 66e4;
2304
2697
  const CODEX_APP_SERVER_STDERR_TAIL_MAX = 2e3;
2305
2698
  const CODEX_APP_SERVER_OVERLOADED_ERROR_CODE = -32001;
2306
2699
  const CODEX_APP_SERVER_OVERLOAD_MAX_RETRIES = 3;
2307
2700
  const CODEX_APP_SERVER_OVERLOAD_RETRY_BASE_MS = 50;
2701
+ const CODEX_APP_SERVER_PENDING_STARTUP_WARNINGS_MAX = 32;
2308
2702
  const CODEX_APP_SERVER_CLIENT_INSTANCE_IDS = /* @__PURE__ */ new WeakMap();
2309
2703
  const UNPAIRED_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
2310
2704
  /** Process-local generation fence for bindings tied to one app-server client instance. */
@@ -2318,6 +2712,10 @@ function getCodexAppServerClientInstanceId(client) {
2318
2712
  function resolveCodexAppServerClientInstanceId(client) {
2319
2713
  return client.getInstanceId?.call(client) ?? getCodexAppServerClientInstanceId(client);
2320
2714
  }
2715
+ /** Codex rejects this exact code before enqueueing, including mutating requests. */
2716
+ function isCodexAppServerOverloadError(error) {
2717
+ return error instanceof CodexAppServerRpcError && error.code === CODEX_APP_SERVER_OVERLOADED_ERROR_CODE;
2718
+ }
2321
2719
  var CodexAppServerLocalRequestCancellationError = class extends Error {
2322
2720
  constructor(method, reason, mayHaveWritten) {
2323
2721
  super(`${method} ${reason}`);
@@ -2373,9 +2771,11 @@ var CodexAppServerClient = class CodexAppServerClient {
2373
2771
  this.pending = /* @__PURE__ */ new Map();
2374
2772
  this.requestHandlers = /* @__PURE__ */ new Set();
2375
2773
  this.notificationHandlers = /* @__PURE__ */ new Set();
2774
+ this.pendingStartupWarnings = [];
2376
2775
  this.closeHandlers = /* @__PURE__ */ new Set();
2377
2776
  this.nextId = 1;
2378
2777
  this.initialized = false;
2778
+ this.modelCatalogRevision = 0;
2379
2779
  this.closed = false;
2380
2780
  this.transportExited = false;
2381
2781
  this.stderrTail = "";
@@ -2401,7 +2801,7 @@ var CodexAppServerClient = class CodexAppServerClient {
2401
2801
  child.stdin.on?.("error", (error) => this.closeWithError(toStringifiedError(error)));
2402
2802
  }
2403
2803
  /** Starts a new app-server client using resolved runtime start options. */
2404
- static start(options) {
2804
+ static async start(options, assertCurrent) {
2405
2805
  const defaults = resolveCodexAppServerRuntimeOptions().start;
2406
2806
  const startOptions = {
2407
2807
  ...defaults,
@@ -2410,7 +2810,18 @@ var CodexAppServerClient = class CodexAppServerClient {
2410
2810
  };
2411
2811
  if (startOptions.transport === "stdio" && startOptions.commandSource === "managed") throw new Error("Managed Codex app-server start options must be resolved before spawn.");
2412
2812
  if (startOptions.transport === "websocket" || startOptions.transport === "unix") return new CodexAppServerClient(createWebSocketTransport(startOptions));
2413
- return new CodexAppServerClient(createStdioTransport(startOptions));
2813
+ let client;
2814
+ try {
2815
+ await createStdioTransport(startOptions, process.env, assertCurrent, (child) => {
2816
+ client = new CodexAppServerClient(child);
2817
+ });
2818
+ return client;
2819
+ } catch (error) {
2820
+ assertCurrent?.();
2821
+ if (client?.transportExited && hasCodexAppServerNaturalExit(client.child)) throw buildCodexAppServerExitError(client.child.exitCode, client.child.signalCode, client.stderrTail);
2822
+ const stderr = client?.getStderrDiagnostic();
2823
+ throw stderr ? new Error(`${coerceErrorMessage(error)}; stderr=${JSON.stringify(stderr)}`, { cause: error }) : error;
2824
+ }
2414
2825
  }
2415
2826
  /** Builds a client around a fake transport for tests. */
2416
2827
  static fromTransportForTests(child) {
@@ -2429,7 +2840,8 @@ var CodexAppServerClient = class CodexAppServerClient {
2429
2840
  experimentalApi: true,
2430
2841
  extensions: {
2431
2842
  "openai/standard-form-input": {},
2432
- "openai/form": {}
2843
+ "openai/form": {},
2844
+ "io.modelcontextprotocol/ui": { mimeTypes: ["text/html;profile=mcp-app"] }
2433
2845
  }
2434
2846
  }
2435
2847
  });
@@ -2458,6 +2870,10 @@ var CodexAppServerClient = class CodexAppServerClient {
2458
2870
  getInstanceId() {
2459
2871
  return this.instanceId;
2460
2872
  }
2873
+ /** Account/config observations become stale before a mutation can enter the wire. */
2874
+ getModelCatalogRevision() {
2875
+ return this.modelCatalogRevision;
2876
+ }
2461
2877
  /** Installs the spawn-owner check run before config-loading thread requests. */
2462
2878
  setThreadSessionRequestGuard(guard) {
2463
2879
  this.threadSessionRequestGuard = guard;
@@ -2467,8 +2883,7 @@ var CodexAppServerClient = class CodexAppServerClient {
2467
2883
  return this.child.pid;
2468
2884
  }
2469
2885
  request(method, params, optionsInput) {
2470
- let options = optionsInput;
2471
- options ??= {};
2886
+ const options = optionsInput ?? {};
2472
2887
  if (this.closed) return Promise.reject(this.closeError ?? /* @__PURE__ */ new Error("codex app-server client is closed"));
2473
2888
  if (options.signal?.aborted) return Promise.reject(new CodexAppServerLocalRequestCancellationError(method, "aborted", false));
2474
2889
  const guard = method === "thread/start" || method === "thread/resume" || method === "thread/fork" ? this.threadSessionRequestGuard : void 0;
@@ -2503,11 +2918,11 @@ var CodexAppServerClient = class CodexAppServerClient {
2503
2918
  const elapsedMs = Date.now() - guardStartedAt;
2504
2919
  const remainingTimeoutMs = options.timeoutMs === void 0 ? void 0 : options.timeoutMs - elapsedMs;
2505
2920
  if (remainingTimeoutMs !== void 0 && remainingTimeoutMs <= 0) throw new CodexAppServerLocalRequestCancellationError(method, "timed out", false);
2506
- return await this.requestWithoutThreadSessionGuard(method, params, {
2921
+ return await this.requestWithOverloadRetry(method, params, {
2507
2922
  ...options,
2508
2923
  ...remainingTimeoutMs !== void 0 ? { timeoutMs: remainingTimeoutMs } : {}
2509
- }, () => {
2510
- requestMayHaveWritten = true;
2924
+ }, (mayHaveWritten) => {
2925
+ requestMayHaveWritten = mayHaveWritten;
2511
2926
  });
2512
2927
  } catch (error) {
2513
2928
  if (requestMayHaveWritten && !(error instanceof CodexAppServerRpcError)) {
@@ -2520,12 +2935,9 @@ var CodexAppServerClient = class CodexAppServerClient {
2520
2935
  }
2521
2936
  })();
2522
2937
  }
2523
- return this.requestWithoutThreadSessionGuard(method, params, options);
2938
+ return this.requestWithOverloadRetry(method, params, options);
2524
2939
  }
2525
- requestWithoutThreadSessionGuard(method, params, options, onWriteAttempt) {
2526
- return this.requestWithOverloadRetry(method, params, options, onWriteAttempt);
2527
- }
2528
- async requestWithOverloadRetry(method, params, options, onWriteAttempt) {
2940
+ async requestWithOverloadRetry(method, params, options, onWriteStateChange) {
2529
2941
  const deadline = options.timeoutMs !== void 0 && Number.isFinite(options.timeoutMs) ? Date.now() + options.timeoutMs : void 0;
2530
2942
  for (let retry = 0;; retry += 1) {
2531
2943
  if (options.signal?.aborted) throw new CodexAppServerLocalRequestCancellationError(method, "aborted", false);
@@ -2535,9 +2947,10 @@ var CodexAppServerClient = class CodexAppServerClient {
2535
2947
  return await this.requestOnce(method, params, {
2536
2948
  ...options,
2537
2949
  ...remainingTimeoutMs !== void 0 ? { timeoutMs: remainingTimeoutMs } : {}
2538
- }, onWriteAttempt);
2950
+ }, onWriteStateChange);
2539
2951
  } catch (error) {
2540
- if (!(error instanceof CodexAppServerRpcError) || error.code !== CODEX_APP_SERVER_OVERLOADED_ERROR_CODE || retry >= CODEX_APP_SERVER_OVERLOAD_MAX_RETRIES) throw error;
2952
+ if (!isCodexAppServerOverloadError(error) || retry >= CODEX_APP_SERVER_OVERLOAD_MAX_RETRIES) throw error;
2953
+ onWriteStateChange?.(false);
2541
2954
  const backoffMs = Math.round(CODEX_APP_SERVER_OVERLOAD_RETRY_BASE_MS * 2 ** retry * (.75 + Math.random() * .5));
2542
2955
  await this.waitForOverloadRetry(method, backoffMs, deadline, options.signal);
2543
2956
  }
@@ -2566,10 +2979,11 @@ var CodexAppServerClient = class CodexAppServerClient {
2566
2979
  if (signal?.aborted) abortListener();
2567
2980
  });
2568
2981
  }
2569
- requestOnce(method, params, options, onWriteAttempt) {
2982
+ requestOnce(method, params, options, onWriteStateChange) {
2570
2983
  if (this.closed) return Promise.reject(this.closeError ?? /* @__PURE__ */ new Error("codex app-server client is closed"));
2571
2984
  if (options.signal?.aborted) return Promise.reject(new CodexAppServerLocalRequestCancellationError(method, "aborted", false));
2572
2985
  const id = this.nextId++;
2986
+ if (method === "account/login/start" || method === "account/logout" || method === "config/value/write" || method === "config/batchWrite") this.modelCatalogRevision += 1;
2573
2987
  const message = {
2574
2988
  id,
2575
2989
  method,
@@ -2619,8 +3033,9 @@ var CodexAppServerClient = class CodexAppServerClient {
2619
3033
  return;
2620
3034
  }
2621
3035
  try {
3036
+ options.assertCurrent?.();
2622
3037
  mayHaveWritten = true;
2623
- onWriteAttempt?.();
3038
+ onWriteStateChange?.(true);
2624
3039
  this.writeMessage(message, (error) => rejectPending(error));
2625
3040
  } catch (error) {
2626
3041
  rejectPending(toStringifiedError(error));
@@ -2642,6 +3057,7 @@ var CodexAppServerClient = class CodexAppServerClient {
2642
3057
  /** Registers a notification handler and returns its disposer. */
2643
3058
  addNotificationHandler(handler) {
2644
3059
  this.notificationHandlers.add(handler);
3060
+ for (const notification of this.pendingStartupWarnings.splice(0)) this.handleNotification(notification);
2645
3061
  return () => this.notificationHandlers.delete(handler);
2646
3062
  }
2647
3063
  /** Registers a close handler and returns its disposer. */
@@ -2649,6 +3065,16 @@ var CodexAppServerClient = class CodexAppServerClient {
2649
3065
  this.closeHandlers.add(handler);
2650
3066
  return () => this.closeHandlers.delete(handler);
2651
3067
  }
3068
+ /** Registers a handler for physical transport exit and returns its disposer. */
3069
+ addTransportExitHandler(handler) {
3070
+ if (this.transportExited) {
3071
+ handler(this);
3072
+ return () => void 0;
3073
+ }
3074
+ const onExit = () => handler(this);
3075
+ this.child.once("exit", onExit);
3076
+ return () => this.child.off?.("exit", onExit);
3077
+ }
2652
3078
  /** Closes the transport without waiting for process/socket shutdown. */
2653
3079
  close() {
2654
3080
  if (!this.markClosed(/* @__PURE__ */ new Error("codex app-server client is closed"))) return;
@@ -2811,8 +3237,9 @@ var CodexAppServerClient = class CodexAppServerClient {
2811
3237
  }
2812
3238
  async runServerRequestHandlers(request) {
2813
3239
  const controller = new AbortController();
2814
- const timeoutResponse = timeoutServerRequestResponse(request);
2815
- if (!timeoutResponse) return await this.runServerRequestHandlersWithoutTimeout(request, controller.signal);
3240
+ if (request.method !== "item/tool/call") return await this.runServerRequestHandlersWithoutTimeout(request, controller.signal);
3241
+ const timeoutMs = resolveDynamicToolServerRequestTimeoutMs(readCodexDynamicToolCallParams(request.params));
3242
+ const timeoutResponse = timeoutServerRequestResponse(timeoutMs);
2816
3243
  let timeout;
2817
3244
  try {
2818
3245
  return await Promise.race([this.runServerRequestHandlersWithoutTimeout(request, controller.signal), new Promise((resolve) => {
@@ -2820,11 +3247,11 @@ var CodexAppServerClient = class CodexAppServerClient {
2820
3247
  embeddedAgentLog.warn("codex app-server server request timed out", {
2821
3248
  id: request.id,
2822
3249
  method: request.method,
2823
- timeoutMs: CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS
3250
+ timeoutMs
2824
3251
  });
2825
3252
  controller.abort(/* @__PURE__ */ new Error("codex app-server server request timed out"));
2826
3253
  resolve(timeoutResponse);
2827
- }, CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS);
3254
+ }, timeoutMs);
2828
3255
  timeout.unref?.();
2829
3256
  })]);
2830
3257
  } finally {
@@ -2839,6 +3266,12 @@ var CodexAppServerClient = class CodexAppServerClient {
2839
3266
  }
2840
3267
  }
2841
3268
  handleNotification(notification) {
3269
+ if (notification.method === "account/updated") this.modelCatalogRevision += 1;
3270
+ if (this.notificationHandlers.size === 0 && notification.method === "configWarning") {
3271
+ if (this.pendingStartupWarnings.length === CODEX_APP_SERVER_PENDING_STARTUP_WARNINGS_MAX) this.pendingStartupWarnings.shift();
3272
+ this.pendingStartupWarnings.push(notification);
3273
+ return;
3274
+ }
2842
3275
  for (const handler of this.notificationHandlers) try {
2843
3276
  Promise.resolve(handler(notification)).catch((error) => {
2844
3277
  embeddedAgentLog.warn("codex app-server notification handler failed", { error });
@@ -2887,12 +3320,11 @@ function defaultServerRequestResponse(request) {
2887
3320
  function stringifyCodexAppServerMessage(message) {
2888
3321
  return JSON.stringify(message, (_key, value) => typeof value === "string" ? value.replace(UNPAIRED_SURROGATE_RE, "") : value) ?? "null";
2889
3322
  }
2890
- function timeoutServerRequestResponse(request) {
2891
- if (request.method !== "item/tool/call") return;
3323
+ function timeoutServerRequestResponse(timeoutMs) {
2892
3324
  return {
2893
3325
  contentItems: [{
2894
3326
  type: "inputText",
2895
- text: `OpenClaw dynamic tool call timed out after ${CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS}ms before sending a response to Codex.`
3327
+ text: `OpenClaw dynamic tool call timed out after ${timeoutMs}ms before sending a response to Codex.`
2896
3328
  }],
2897
3329
  success: false
2898
3330
  };
@@ -2910,8 +3342,8 @@ function assertSupportedCodexAppServerVersion(response) {
2910
3342
  const detectedVersion = readCodexVersionFromUserAgent(response.userAgent);
2911
3343
  if (!detectedVersion) throw new CodexAppServerVersionError(detectedVersion);
2912
3344
  const detected = parse(detectedVersion);
2913
- if (!detected || detected.compare("0.147.0") < 0) throw new CodexAppServerVersionError(detectedVersion);
2914
- if (detected.compare("0.149.1") > 0) embeddedAgentLog.warn("codex app-server is newer than OpenClaw's managed runtime; continuing with normal startup validation", {
3345
+ if (!detected || detected.compare("0.149.0") < 0) throw new CodexAppServerVersionError(detectedVersion);
3346
+ if (detected.compare("0.151.0") > 0) embeddedAgentLog.warn("codex app-server is newer than OpenClaw's managed runtime; continuing with normal startup validation", {
2915
3347
  detectedVersion,
2916
3348
  validatedVersion: CODEX_APP_SERVER_VERSION
2917
3349
  });
@@ -2997,6 +3429,8 @@ const CODEX_POST_TOOL_RAW_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS = 5 * 6e4;
2997
3429
  const CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS = 5 * 6e4;
2998
3430
  /** Long terminal idle watch for app-server turns that never send completion. */
2999
3431
  const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 6e4;
3432
+ const TURN_TERMINAL_SETTLEMENT_IDLE_TIMEOUT_MS = 2 * 6e4;
3433
+ const TURN_FINALIZE_DRAIN_ABORT_GRACE_MS = 5e3;
3000
3434
  var CodexAppServerStartupError = class extends Error {
3001
3435
  constructor(reason, message = reason === "timed_out" ? "codex app-server startup timed out" : "codex app-server startup aborted") {
3002
3436
  super(message);
@@ -3078,6 +3512,324 @@ function resolveCodexGatewayTimeoutWithGraceMs(timeoutMs, graceMs = 1e4) {
3078
3512
  return addTimerTimeoutGraceMs(timeout, resolveTimerTimeoutMs(graceMs, 0, 0)) ?? timeout;
3079
3513
  }
3080
3514
  //#endregion
3515
+ //#region extensions/codex/src/app-server/desktop-generation-fingerprint.ts
3516
+ const MAX_COMPUTER_USE_PLUGIN_TREE_ENTRIES = 4096;
3517
+ /** Fingerprints every desktop candidate that can own a managed fallback artifact. */
3518
+ async function readMacOSDesktopGenerationFingerprint(candidates = resolveMacOSDesktopCodexAppPathCandidates("darwin")) {
3519
+ const entries = [];
3520
+ for (const candidate of candidates) {
3521
+ const command = await statFingerprint(candidate.appServerCommandPath);
3522
+ entries.push(`candidate:${candidate.appName}:${candidate.appServerCommandPath}:${command}`);
3523
+ for (const artifactPath of resolveMacOSDesktopGenerationPaths(candidate)) entries.push(`${artifactPath}\0${await statFingerprint(artifactPath)}`);
3524
+ const pluginRoot = resolveComputerUsePluginRoot(candidate);
3525
+ entries.push(`${pluginRoot}\0${await directoryTreeFingerprint(pluginRoot)}`);
3526
+ }
3527
+ return createHash("sha256").update(entries.join("\0")).digest("hex");
3528
+ }
3529
+ function resolveMacOSDesktopGenerationPaths(candidate) {
3530
+ return [
3531
+ candidate.appBundlePath,
3532
+ path.join(candidate.bundledMarketplacePath, ".agents", "plugins", "marketplace.json"),
3533
+ ...candidate.computerUseServiceAppPaths.flatMap((servicePath) => [
3534
+ servicePath,
3535
+ path.join(servicePath, "Contents", "Info.plist"),
3536
+ path.join(servicePath, "Contents", "SharedSupport", "SkyComputerUseClient.app", "Contents", "MacOS", "SkyComputerUseClient")
3537
+ ])
3538
+ ];
3539
+ }
3540
+ function resolveComputerUsePluginRoot(candidate) {
3541
+ return path.join(candidate.bundledMarketplacePath, "plugins", "computer-use");
3542
+ }
3543
+ /** Stable roots that cover bundle replacement and recursive artifact updates. */
3544
+ function resolveMacOSDesktopGenerationWatchPaths(candidates = resolveMacOSDesktopCodexAppPathCandidates("darwin")) {
3545
+ const watched = /* @__PURE__ */ new Set(["/Applications"]);
3546
+ for (const candidate of candidates) watched.add(candidate.appBundlePath);
3547
+ return [...watched];
3548
+ }
3549
+ async function directoryTreeFingerprint(root) {
3550
+ let rootStat;
3551
+ try {
3552
+ rootStat = await fs.lstat(root, { bigint: true });
3553
+ } catch (error) {
3554
+ if (isNodeError(error, "ENOENT") || isNodeError(error, "ENOTDIR")) return "missing";
3555
+ throw error;
3556
+ }
3557
+ if (!rootStat.isDirectory()) return statFingerprint(root);
3558
+ let entryCount = 0;
3559
+ const hash = createHash("sha256");
3560
+ hash.update("openclaw-codex-computer-use-plugin-tree-v1\0");
3561
+ const visit = async (directory, relativeDirectory, before) => {
3562
+ hash.update(`directory\0${relativeDirectory}\0${statTuple(before)}\0`);
3563
+ const entries = (await fs.readdir(directory, { withFileTypes: true })).toSorted((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
3564
+ for (const entry of entries) {
3565
+ entryCount += 1;
3566
+ if (entryCount > MAX_COMPUTER_USE_PLUGIN_TREE_ENTRIES) throw new Error("Codex Computer Use plugin exceeds the bounded tree size");
3567
+ const entryPath = path.join(directory, entry.name);
3568
+ const relativePath = path.join(relativeDirectory, entry.name);
3569
+ const entryStat = await fs.lstat(entryPath, { bigint: true });
3570
+ if (entryStat.isDirectory()) await visit(entryPath, relativePath, entryStat);
3571
+ else hash.update(`entry\0${relativePath}\0${await statFingerprint(entryPath)}\0`);
3572
+ }
3573
+ if (!sameStat(before, await fs.lstat(directory, { bigint: true }))) throw new Error(`Codex desktop artifact changed while fingerprinting: ${directory}`);
3574
+ };
3575
+ await visit(root, ".", rootStat);
3576
+ return hash.digest("hex");
3577
+ }
3578
+ async function statFingerprint(filePath) {
3579
+ try {
3580
+ const entry = await fs.lstat(filePath, { bigint: true });
3581
+ const type = entry.isSymbolicLink() ? "link" : entry.isDirectory() ? "directory" : entry.isFile() ? "file" : "other";
3582
+ const own = statTuple(entry);
3583
+ if (!entry.isSymbolicLink()) return `${type}:${own}:${entry.isFile() ? await readFileFingerprint(filePath, entry, false) : ""}`;
3584
+ const [link, realPath, target] = await Promise.all([
3585
+ fs.readlink(filePath),
3586
+ fs.realpath(filePath),
3587
+ fs.stat(filePath, { bigint: true })
3588
+ ]);
3589
+ const content = target.isFile() ? await readFileFingerprint(filePath, target, true) : "";
3590
+ return `${type}:${own}:${link}:${realPath}:${statTuple(target)}:${content}`;
3591
+ } catch (error) {
3592
+ if (isNodeError(error, "ENOENT") || isNodeError(error, "ENOTDIR")) return "missing";
3593
+ throw error;
3594
+ }
3595
+ }
3596
+ async function readFileFingerprint(filePath, expected, followsSymlink) {
3597
+ const noFollow = followsSymlink ? 0 : constants.O_NOFOLLOW ?? 0;
3598
+ const handle = await fs.open(filePath, constants.O_RDONLY | noFollow);
3599
+ try {
3600
+ const before = await handle.stat({ bigint: true });
3601
+ if (!sameStat(before, expected)) throw new Error(`Codex desktop artifact changed while fingerprinting: ${filePath}`);
3602
+ const hash = createHash("sha256");
3603
+ for await (const chunk of handle.createReadStream({ autoClose: false })) hash.update(chunk);
3604
+ if (!sameStat(before, await handle.stat({ bigint: true }))) throw new Error(`Codex desktop artifact changed while fingerprinting: ${filePath}`);
3605
+ return hash.digest("hex");
3606
+ } finally {
3607
+ await handle.close();
3608
+ }
3609
+ }
3610
+ function sameStat(left, right) {
3611
+ return statTuple(left) === statTuple(right);
3612
+ }
3613
+ function statTuple(stat) {
3614
+ return [
3615
+ stat.dev,
3616
+ stat.ino,
3617
+ stat.mode,
3618
+ stat.size,
3619
+ stat.mtimeNs,
3620
+ stat.ctimeNs
3621
+ ].join(":");
3622
+ }
3623
+ function isNodeError(error, code) {
3624
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
3625
+ }
3626
+ //#endregion
3627
+ //#region extensions/codex/src/app-server/desktop-generation-owner.ts
3628
+ const SETTLE_DELAY_MS = 1e3;
3629
+ /** Coalesces filesystem invalidations into one stable desktop generation. */
3630
+ function createCodexDesktopGenerationOwner(params) {
3631
+ let generation = params.initialGeneration;
3632
+ let invalidation = 0;
3633
+ let dirty = false;
3634
+ let refresh;
3635
+ let stopped = false;
3636
+ const markDirty = () => {
3637
+ invalidation += 1;
3638
+ dirty = true;
3639
+ };
3640
+ const reconcile = () => {
3641
+ if (refresh) return refresh;
3642
+ refresh = (async () => {
3643
+ for (;;) {
3644
+ if (stopped) throw new Error("Codex desktop generation owner stopped");
3645
+ const observedInvalidation = invalidation;
3646
+ const first = await params.readFingerprint();
3647
+ await delay(SETTLE_DELAY_MS);
3648
+ if (stopped) throw new Error("Codex desktop generation owner stopped");
3649
+ const second = await params.readFingerprint();
3650
+ if (stopped) throw new Error("Codex desktop generation owner stopped");
3651
+ if (observedInvalidation !== invalidation || first !== second) continue;
3652
+ const previous = generation;
3653
+ generation = previous?.fingerprint === second ? previous : {
3654
+ epoch: (previous?.epoch ?? 0) + 1,
3655
+ fingerprint: second
3656
+ };
3657
+ dirty = false;
3658
+ if (previous && generation !== previous) params.onGenerationChange?.(generation);
3659
+ return generation;
3660
+ }
3661
+ })().finally(() => {
3662
+ refresh = void 0;
3663
+ });
3664
+ return refresh;
3665
+ };
3666
+ return {
3667
+ read: () => generation,
3668
+ markDirty,
3669
+ wait: () => dirty ? reconcile() : Promise.resolve(generation),
3670
+ refresh: () => {
3671
+ markDirty();
3672
+ return reconcile();
3673
+ },
3674
+ isCurrent: (candidate) => Boolean(candidate && !dirty && generation && candidate.epoch === generation.epoch && candidate.fingerprint === generation.fingerprint),
3675
+ stop: () => {
3676
+ stopped = true;
3677
+ }
3678
+ };
3679
+ }
3680
+ function delay(ms) {
3681
+ return new Promise((resolve) => {
3682
+ setTimeout(resolve, ms);
3683
+ });
3684
+ }
3685
+ //#endregion
3686
+ //#region extensions/codex/src/app-server/desktop-generation.ts
3687
+ /** Lifecycle-owned generation for managed macOS Codex desktop artifacts. */
3688
+ const APPLICATIONS_PATH = "/Applications";
3689
+ const REARM_INITIAL_DELAY_MS = 100;
3690
+ const REARM_MAX_DELAY_MS = 3e4;
3691
+ const DESKTOP_GENERATION_STATE = Symbol.for("openclaw.codexDesktopGenerationState");
3692
+ function state() {
3693
+ const globalState = globalThis;
3694
+ return globalState[DESKTOP_GENERATION_STATE] ??= {};
3695
+ }
3696
+ function waitForCodexDesktopGeneration() {
3697
+ return state().owner?.wait() ?? Promise.resolve(void 0);
3698
+ }
3699
+ function isCodexDesktopGenerationCurrent(generation) {
3700
+ return state().owner?.isCurrent(generation) ?? false;
3701
+ }
3702
+ function createCodexDesktopGenerationService(params, runtime = {
3703
+ platform: process.platform,
3704
+ readFingerprint: readMacOSDesktopGenerationFingerprint,
3705
+ resolveWatchPaths: resolveMacOSDesktopGenerationWatchPaths,
3706
+ pathExists: existsSync,
3707
+ watchPath: (watchedPath, options, listener) => watch(watchedPath, options, listener)
3708
+ }) {
3709
+ return {
3710
+ id: "codex-desktop-generation",
3711
+ async start(ctx) {
3712
+ if (runtime.platform !== "darwin") return;
3713
+ const current = state();
3714
+ current.context = ctx;
3715
+ current.readFingerprint = runtime.readFingerprint;
3716
+ current.resolveWatchPaths = runtime.resolveWatchPaths;
3717
+ current.pathExists = runtime.pathExists;
3718
+ current.watchPath = runtime.watchPath;
3719
+ current.owner = createCodexDesktopGenerationOwner({
3720
+ readFingerprint: current.readFingerprint,
3721
+ onGenerationChange: params.onGenerationChange,
3722
+ initialGeneration: current.lastGeneration
3723
+ });
3724
+ armWatchers(current);
3725
+ refreshGeneration(current, current.owner, current.owner.refresh());
3726
+ },
3727
+ async stop() {
3728
+ const current = state();
3729
+ current.lastGeneration = current.owner?.read() ?? current.lastGeneration;
3730
+ current.owner?.stop();
3731
+ current.owner = void 0;
3732
+ current.armEpoch = (current.armEpoch ?? 0) + 1;
3733
+ current.context = void 0;
3734
+ current.readFingerprint = void 0;
3735
+ current.resolveWatchPaths = void 0;
3736
+ current.pathExists = void 0;
3737
+ current.watchPath = void 0;
3738
+ current.watchHealthy = void 0;
3739
+ current.rearmDelayMs = void 0;
3740
+ if (current.rearmTimer) {
3741
+ clearTimeout(current.rearmTimer);
3742
+ current.rearmTimer = void 0;
3743
+ }
3744
+ closeWatchers(current);
3745
+ }
3746
+ };
3747
+ }
3748
+ function armWatchers(current) {
3749
+ const owner = current.owner;
3750
+ if (!owner || current.watchers) return false;
3751
+ const armEpoch = (current.armEpoch ?? 0) + 1;
3752
+ current.armEpoch = armEpoch;
3753
+ const watchers = /* @__PURE__ */ new Set();
3754
+ current.watchers = watchers;
3755
+ const candidateNames = new Set(resolveMacOSDesktopCodexAppPathCandidates("darwin").map((candidate) => candidate.appName));
3756
+ let complete = true;
3757
+ for (const watchedPath of current.resolveWatchPaths?.() ?? []) {
3758
+ if (!current.pathExists?.(watchedPath)) continue;
3759
+ try {
3760
+ const watcher = current.watchPath?.(watchedPath, { recursive: watchedPath !== APPLICATIONS_PATH }, (_eventType, filename) => {
3761
+ if (!isCurrentArm(current, owner, watchers, armEpoch)) return;
3762
+ if (watchedPath === APPLICATIONS_PATH && filename && !candidateNames.has(filename.toString().split(path.sep)[0] ?? "")) return;
3763
+ owner.markDirty();
3764
+ scheduleRearm(current, owner);
3765
+ });
3766
+ if (!watcher) {
3767
+ complete = false;
3768
+ reportWatcherFailure(current, owner, /* @__PURE__ */ new Error(`Could not watch ${watchedPath}`));
3769
+ scheduleRearm(current, owner);
3770
+ continue;
3771
+ }
3772
+ watchers.add(watcher);
3773
+ watcher.on("error", (error) => {
3774
+ if (!isCurrentArm(current, owner, watchers, armEpoch)) return;
3775
+ reportWatcherFailure(current, owner, error);
3776
+ scheduleRearm(current, owner);
3777
+ });
3778
+ } catch (error) {
3779
+ complete = false;
3780
+ reportWatcherFailure(current, owner, error);
3781
+ scheduleRearm(current, owner);
3782
+ }
3783
+ }
3784
+ current.watchHealthy = complete;
3785
+ if (complete) current.rearmDelayMs = REARM_INITIAL_DELAY_MS;
3786
+ return complete;
3787
+ }
3788
+ function reportWatcherFailure(current, owner, error) {
3789
+ if (current.watchHealthy === false) return;
3790
+ current.watchHealthy = false;
3791
+ owner.markDirty();
3792
+ current.context?.serviceHealth?.reportFailure(error);
3793
+ current.context?.logger.warn(`codex desktop generation watcher failed: ${String(error)}`);
3794
+ }
3795
+ function isCurrentArm(current, owner, watchers, armEpoch) {
3796
+ return current.owner === owner && current.watchers === watchers && current.armEpoch === armEpoch;
3797
+ }
3798
+ function scheduleRearm(current, owner) {
3799
+ if (current.rearmTimer) {
3800
+ if (current.watchHealthy === false) return;
3801
+ clearTimeout(current.rearmTimer);
3802
+ }
3803
+ const delayMs = current.watchHealthy === false ? current.rearmDelayMs ?? REARM_INITIAL_DELAY_MS : REARM_INITIAL_DELAY_MS;
3804
+ if (current.watchHealthy === false) current.rearmDelayMs = Math.min(delayMs * 2, REARM_MAX_DELAY_MS);
3805
+ current.rearmTimer = setTimeout(() => {
3806
+ current.rearmTimer = void 0;
3807
+ if (current.owner !== owner) return;
3808
+ const wasUnhealthy = current.watchHealthy === false;
3809
+ closeWatchers(current);
3810
+ if (!armWatchers(current) || wasUnhealthy) owner.markDirty();
3811
+ refreshGeneration(current, owner, owner.wait());
3812
+ }, delayMs);
3813
+ current.rearmTimer.unref();
3814
+ }
3815
+ function logRefreshFailure(current, owner) {
3816
+ return (error) => {
3817
+ if (current.owner !== owner) return;
3818
+ current.context?.serviceHealth?.reportFailure(error);
3819
+ current.context?.logger.warn(`codex desktop generation refresh failed: ${String(error)}`);
3820
+ };
3821
+ }
3822
+ function refreshGeneration(current, owner, refresh) {
3823
+ refresh.then(() => {
3824
+ if (current.owner === owner && current.watchHealthy) current.context?.serviceHealth?.clearFailure();
3825
+ }).catch(logRefreshFailure(current, owner));
3826
+ }
3827
+ function closeWatchers(current) {
3828
+ const watchers = current.watchers;
3829
+ current.watchers = void 0;
3830
+ for (const watcher of watchers ?? []) watcher.close();
3831
+ }
3832
+ //#endregion
3081
3833
  //#region extensions/codex/src/app-server/native-config-fence.ts
3082
3834
  const CODEX_NATIVE_CONFIG_FENCE_STATE = Symbol.for("openclaw.codexNativeConfigFenceState");
3083
3835
  function getFenceState() {
@@ -3140,37 +3892,31 @@ async function waitForPreviousFence(previous, options) {
3140
3892
  });
3141
3893
  }
3142
3894
  //#endregion
3143
- //#region extensions/codex/src/app-server/timeout.ts
3144
- /**
3145
- * Thin Codex app-server timeout adapter around OpenClaw's shared security
3146
- * runtime timeout helper.
3147
- */
3148
- function resolveAbortError(signal) {
3149
- return signal.reason instanceof Error ? signal.reason : new Error("Codex app-server operation aborted", { cause: signal.reason });
3150
- }
3151
- /** Awaits a promise with a Codex-specific timeout error message. */
3152
- async function withTimeout$1(promise, timeoutMs, timeoutMessage, createError) {
3153
- return await withTimeout(promise, timeoutMs, {
3154
- message: timeoutMessage,
3155
- ...createError ? { createError } : {}
3156
- });
3157
- }
3158
- /** Bounds an operation by both its owner lifecycle and one total wall-clock budget. */
3159
- async function withAbortableTimeout(params) {
3160
- const signal = params.signal;
3161
- if (signal?.aborted) throw resolveAbortError(signal);
3162
- let removeAbortListener;
3163
- const operation = signal ? Promise.race([params.promise, new Promise((_, reject) => {
3164
- const onAbort = () => reject(resolveAbortError(signal));
3165
- signal.addEventListener("abort", onAbort, { once: true });
3166
- removeAbortListener = () => signal.removeEventListener("abort", onAbort);
3167
- })]) : params.promise;
3168
- try {
3169
- return await withTimeout$1(operation, params.timeoutMs, params.timeoutMessage, params.createTimeoutError);
3170
- } finally {
3171
- removeAbortListener?.();
3895
+ //#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
3896
+ var CodexThreadStartRequestError = class extends Error {
3897
+ constructor(cause) {
3898
+ super(`thread/start: ${formatErrorMessage(cause)}`, { cause });
3899
+ this.name = "CodexThreadStartRequestError";
3172
3900
  }
3173
- }
3901
+ };
3902
+ var CodexThreadBindingConflictError = class extends Error {
3903
+ constructor(threadId, operation) {
3904
+ super(`Codex thread binding changed while ${operation}: ${threadId}`);
3905
+ this.name = "CodexThreadBindingConflictError";
3906
+ }
3907
+ };
3908
+ var CodexRestrictedToolSurfaceAttestationError = class extends Error {
3909
+ constructor(cause) {
3910
+ super("Codex restricted-tool-surface MCP attestation failed", { cause });
3911
+ this.name = "CodexRestrictedToolSurfaceAttestationError";
3912
+ }
3913
+ };
3914
+ var CodexAdoptedThreadActiveError = class extends Error {
3915
+ constructor() {
3916
+ super("Codex session became active in another runner; wait for it to finish before continuing");
3917
+ this.name = "CodexAdoptedThreadActiveError";
3918
+ }
3919
+ };
3174
3920
  //#endregion
3175
3921
  //#region extensions/codex/src/app-server/shared-client.ts
3176
3922
  /**
@@ -3179,6 +3925,8 @@ async function withAbortableTimeout(params) {
3179
3925
  */
3180
3926
  var shared_client_exports = /* @__PURE__ */ __exportAll({
3181
3927
  assertCodexAppServerClientStartSelectionCurrent: () => assertCodexAppServerClientStartSelectionCurrent,
3928
+ captureExclusiveSharedCodexAppServerClient: () => captureExclusiveSharedCodexAppServerClient,
3929
+ captureSharedCodexAppServerCatalogLifetime: () => captureSharedCodexAppServerCatalogLifetime,
3182
3930
  clearSharedCodexAppServerClientAndWait: () => clearSharedCodexAppServerClientAndWait,
3183
3931
  clearSharedCodexAppServerClientIfCurrent: () => clearSharedCodexAppServerClientIfCurrent,
3184
3932
  clearSharedCodexAppServerClientIfCurrentAndUnclaimed: () => clearSharedCodexAppServerClientIfCurrentAndUnclaimed,
@@ -3187,6 +3935,9 @@ var shared_client_exports = /* @__PURE__ */ __exportAll({
3187
3935
  getLeasedSharedCodexAppServerClient: () => getLeasedSharedCodexAppServerClient,
3188
3936
  getSharedCodexAppServerClient: () => getSharedCodexAppServerClient,
3189
3937
  isCodexAppServerStartSelectionChangedError: () => isCodexAppServerStartSelectionChangedError,
3938
+ readCodexAppServerClientDesktopGeneration: () => readCodexAppServerClientDesktopGeneration,
3939
+ readCodexAppServerClientDesktopGenerationFingerprint: () => readCodexAppServerClientDesktopGenerationFingerprint,
3940
+ readCodexAppServerClientProcessIdentity: () => readCodexAppServerClientProcessIdentity,
3190
3941
  releaseCodexAppServerClientLease: () => releaseCodexAppServerClientLease,
3191
3942
  releaseLeasedSharedCodexAppServerClient: () => releaseLeasedSharedCodexAppServerClient,
3192
3943
  resolveCodexAppServerSpawnIdentity: () => resolveCodexAppServerSpawnIdentity,
@@ -3194,6 +3945,8 @@ var shared_client_exports = /* @__PURE__ */ __exportAll({
3194
3945
  retainSharedCodexAppServerClientByInstanceId: () => retainSharedCodexAppServerClientByInstanceId,
3195
3946
  retainSharedCodexAppServerClientIfCurrent: () => retainSharedCodexAppServerClientIfCurrent,
3196
3947
  retireSharedCodexAppServerClientIfCurrent: () => retireSharedCodexAppServerClientIfCurrent,
3948
+ retireSharedCodexAppServerClientsBeforeDesktopGeneration: () => retireSharedCodexAppServerClientsBeforeDesktopGeneration,
3949
+ waitForCodexAppServerClientDesktopGenerationDrain: () => waitForCodexAppServerClientDesktopGenerationDrain,
3197
3950
  withLeasedCodexAppServerClientStartSelectionRetry: () => withLeasedCodexAppServerClientStartSelectionRetry
3198
3951
  });
3199
3952
  const CODEX_APP_SERVER_INITIALIZE_TIMEOUT_MESSAGE = "codex app-server initialize timed out";
@@ -3205,8 +3958,10 @@ function getSharedCodexAppServerClientState() {
3205
3958
  globalState[SHARED_CODEX_APP_SERVER_CLIENT_STATE] ??= {
3206
3959
  clients: /* @__PURE__ */ new Map(),
3207
3960
  liveClients: /* @__PURE__ */ new Set(),
3961
+ isolatedClients: /* @__PURE__ */ new Set(),
3208
3962
  entriesByClient: /* @__PURE__ */ new WeakMap(),
3209
- leasedReleases: /* @__PURE__ */ new WeakMap()
3963
+ leasedReleases: /* @__PURE__ */ new WeakMap(),
3964
+ desktopGenerationDrainChecks: /* @__PURE__ */ new Set()
3210
3965
  };
3211
3966
  return globalState[SHARED_CODEX_APP_SERVER_CLIENT_STATE];
3212
3967
  }
@@ -3215,6 +3970,41 @@ function getCodexAppServerClientStartMetadata() {
3215
3970
  globalState[CODEX_APP_SERVER_CLIENT_START_METADATA] ??= /* @__PURE__ */ new WeakMap();
3216
3971
  return globalState[CODEX_APP_SERVER_CLIENT_START_METADATA];
3217
3972
  }
3973
+ /** Reads the exact successful spawn selection plus its initialized runtime identity. */
3974
+ function readCodexAppServerClientProcessIdentity(client) {
3975
+ const metadata = getCodexAppServerClientStartMetadata().get(client);
3976
+ if (!metadata) return;
3977
+ const runtimeIdentity = client.getRuntimeIdentity();
3978
+ return {
3979
+ clientId: client.getInstanceId(),
3980
+ ...resolveCodexAppServerSpawnIdentity(metadata.startOptions, metadata.nativeCommand),
3981
+ ...runtimeIdentity?.serverVersion ? { serverVersion: runtimeIdentity.serverVersion } : {},
3982
+ ...runtimeIdentity?.userAgent ? { userAgent: runtimeIdentity.userAgent } : {}
3983
+ };
3984
+ }
3985
+ /** Returns the lifecycle fingerprint that owns a managed desktop client. */
3986
+ function readCodexAppServerClientDesktopGenerationFingerprint(client) {
3987
+ return readCodexAppServerClientDesktopGeneration(client)?.fingerprint;
3988
+ }
3989
+ /** Returns the lifecycle generation that owns a managed desktop client. */
3990
+ function readCodexAppServerClientDesktopGeneration(client) {
3991
+ return getCodexAppServerClientStartMetadata().get(client)?.desktopGeneration;
3992
+ }
3993
+ /** Waits until older physical desktop clients for this client's Codex home exit. */
3994
+ async function waitForCodexAppServerClientDesktopGenerationDrain(params) {
3995
+ const metadata = getCodexAppServerClientStartMetadata().get(params.client);
3996
+ if (!metadata?.desktopGeneration) return;
3997
+ const drain = createOlderDesktopGenerationDrainWait({
3998
+ generation: metadata.desktopGeneration,
3999
+ startOptions: metadata.startOptions,
4000
+ agentDir: metadata.agentDir
4001
+ });
4002
+ try {
4003
+ await withCodexAppServerAcquireDeadline(params.timeoutMs ?? 0, drain.promise, params.signal, "Codex Computer Use install timed out waiting for older desktop clients");
4004
+ } finally {
4005
+ drain.cancel();
4006
+ }
4007
+ }
3218
4008
  /** Resolves non-secret spawn identity before startup; argv is represented only by its hash. */
3219
4009
  function resolveCodexAppServerSpawnIdentity(startOptions, resolvedNativeCommand) {
3220
4010
  const nativeCommand = resolvedNativeCommand ?? (startOptions.commandSource === "resolved-managed" ? resolveManagedCodexNativeCommand(startOptions.command) : void 0);
@@ -3245,6 +4035,7 @@ function isCodexAppServerStartSelectionChangedError(error) {
3245
4035
  function assertCodexAppServerClientStartSelectionCurrent(params) {
3246
4036
  const metadata = getCodexAppServerClientStartMetadata().get(params.client);
3247
4037
  if (!metadata) return;
4038
+ if (metadata.desktopGeneration && !isCodexDesktopGenerationCurrent(metadata.desktopGeneration)) throw new CodexAppServerStartSelectionChangedError();
3248
4039
  const requestedStartOptions = params.startOptions ?? metadata.requestedStartOptions;
3249
4040
  if (requestedStartOptions.commandSource !== "managed") return;
3250
4041
  const current = resolveCodexAppServerStartOptionsForAgent({
@@ -3256,7 +4047,7 @@ function assertCodexAppServerClientStartSelectionCurrent(params) {
3256
4047
  /** Resolves the per-CODEX_HOME key used to serialize native config loading. */
3257
4048
  function resolveCodexNativeConfigFenceKey(params) {
3258
4049
  const metadata = params.client ? getCodexAppServerClientStartMetadata().get(params.client) : void 0;
3259
- const startOptions = params.startOptions ?? metadata?.startOptions;
4050
+ const startOptions = metadata?.startOptions ?? params.startOptions;
3260
4051
  if (!startOptions || startOptions.transport !== "stdio") return;
3261
4052
  const configuredHome = startOptions.env?.CODEX_HOME?.trim();
3262
4053
  const agentDir = params.agentDir ?? metadata?.agentDir ?? resolveDefaultAgentDir(params.config ?? {});
@@ -3270,10 +4061,11 @@ function inferAuthRequirement(preparedAuth) {
3270
4061
  async function resolveCodexAppServerClientStartContext(options) {
3271
4062
  const agentDir = options?.agentDir ?? resolveDefaultAgentDir(options?.config ?? {});
3272
4063
  const requestedStartOptions = options?.startOptions ?? resolveCodexAppServerRuntimeOptions().start;
4064
+ const desktopGeneration = shouldTrackDesktopGeneration(requestedStartOptions, options?.pluginConfig) ? await waitForCodexDesktopGeneration() : void 0;
3273
4065
  const preparedAuth = options?.preparedAuth;
3274
4066
  const preparedApiKey = preparedAuth?.kind === "api-key" ? preparedAuth.apiKey.trim() : void 0;
3275
4067
  if (preparedAuth && options?.authProfileId !== void 0) throw new Error("Prepared Codex auth cannot also select a legacy auth profile.");
3276
- if (preparedAuth?.kind === "profile" && !preparedAuth.store.profiles[preparedAuth.profileId]) throw new Error(`Prepared Codex auth profile "${preparedAuth.profileId}" was not found.`);
4068
+ if (preparedAuth?.kind === "profile" && !preparedAuth.store.profiles[preparedAuth.profileId]) throw new Error(`Prepared Codex auth profile "${preparedAuth.profileId}" was not found. Select an existing OpenAI profile or sign in again with OpenClaw, then retry.`);
3277
4069
  if (preparedAuth?.kind === "api-key" && !preparedApiKey) throw new Error("Prepared Codex API-key auth is missing its resolved key.");
3278
4070
  if (preparedAuth && requestedStartOptions.homeScope === "user") throw new Error("Prepared Codex auth requires an isolated app-server home.");
3279
4071
  const preparedAuthRequirement = inferAuthRequirement(preparedAuth);
@@ -3281,11 +4073,11 @@ async function resolveCodexAppServerClientStartContext(options) {
3281
4073
  const authRequirement = options?.authRequirement ?? preparedAuthRequirement;
3282
4074
  const usesNativeAuth = !preparedAuth && (options?.authProfileId === null || requestedStartOptions.homeScope === "user");
3283
4075
  const requestedAuthProfileId = preparedAuth?.kind === "profile" ? preparedAuth.profileId : options?.authProfileId ?? void 0;
3284
- const authProfileStore = preparedAuth?.kind === "profile" ? preparedAuth.store : !usesNativeAuth && options?.authProfileStore ? resolveCodexAppServerAuthProfileStore({
4076
+ const authProfileStore = preparedAuth?.kind === "profile" ? preparedAuth.store : !usesNativeAuth && preparedAuth?.kind !== "api-key" ? resolveCodexAppServerAuthProfileStore({
3285
4077
  agentDir,
3286
4078
  authProfileId: requestedAuthProfileId,
3287
- authProfileStore: options.authProfileStore,
3288
- config: options.config
4079
+ authProfileStore: options?.authProfileStore,
4080
+ config: options?.config
3289
4081
  }) : options?.authProfileStore;
3290
4082
  const authProfileId = preparedAuth?.kind === "profile" ? preparedAuth.profileId : usesNativeAuth || preparedAuth?.kind === "api-key" ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
3291
4083
  authProfileId: requestedAuthProfileId,
@@ -3293,18 +4085,20 @@ async function resolveCodexAppServerClientStartContext(options) {
3293
4085
  config: options?.config,
3294
4086
  ...authProfileStore ? { authProfileStore } : {}
3295
4087
  });
3296
- const preparedAuthProfileSnapshot = preparedAuth?.kind === "profile" ? preparedAuth.snapshot ?? await resolveCodexAppServerPreparedAuthProfileSnapshot({
4088
+ const preparedAuthProfileSnapshot = !usesNativeAuth && authProfileId ? (preparedAuth?.kind === "profile" ? preparedAuth.snapshot : void 0) ?? await resolveCodexAppServerPreparedAuthProfileSnapshot({
3297
4089
  authProfileId,
3298
4090
  authProfileStore,
3299
4091
  agentDir,
3300
4092
  config: options?.config
3301
4093
  }) : void 0;
3302
- if (preparedAuth?.kind === "profile" && !preparedAuthProfileSnapshot) throw new Error(`Prepared Codex auth profile "${preparedAuth.profileId}" is unusable.`);
4094
+ if (preparedAuth?.kind === "profile" && !preparedAuthProfileSnapshot) throw new Error(`Prepared Codex auth profile "${preparedAuth.profileId}" is unusable. Repair or replace the selected OpenAI profile, then retry.`);
3303
4095
  const resolvedPreparedAuth = preparedAuth?.kind === "api-key" ? {
3304
4096
  kind: "api-key",
3305
4097
  apiKey: preparedApiKey
3306
- } : preparedAuth?.kind === "profile" ? {
3307
- ...preparedAuth,
4098
+ } : preparedAuthProfileSnapshot && authProfileId && authProfileStore ? {
4099
+ kind: "profile",
4100
+ profileId: authProfileId,
4101
+ store: authProfileStore,
3308
4102
  snapshot: preparedAuthProfileSnapshot
3309
4103
  } : void 0;
3310
4104
  return {
@@ -3323,51 +4117,20 @@ async function resolveCodexAppServerClientStartContext(options) {
3323
4117
  agentId: options?.agentId,
3324
4118
  agentDir,
3325
4119
  authProfileId: usesNativeAuth || preparedAuth?.kind === "api-key" ? null : authProfileId,
3326
- ...resolvedPreparedAuth ? { preparedAuth: resolvedPreparedAuth } : {},
4120
+ ...preparedAuth && resolvedPreparedAuth ? { preparedAuth: resolvedPreparedAuth } : {},
3327
4121
  authRequirement,
3328
4122
  config: options?.config,
3329
4123
  pluginConfig: options?.pluginConfig,
3330
4124
  ...authProfileStore ? { authProfileStore } : {}
3331
- })
3332
- };
3333
- }
3334
- function resolveWarmSharedCodexAppServerClientIdentity(options) {
3335
- if (!options?.config || typeof options.config !== "object" || !options.startOptions || options.pluginConfig !== void 0 || options.authProfileStore !== void 0 || options.preparedAuth !== void 0 || options.runtimeArtifactMode !== void 0 || options.expectedRuntimeArtifact !== void 0) return;
3336
- const authProfileSelector = options.authProfileId === null ? ["native"] : options.authProfileId === void 0 ? ["implicit"] : ["profile", options.authProfileId];
3337
- return {
3338
- config: options.config,
3339
- startOptions: options.startOptions,
3340
- selectorKey: JSON.stringify([
3341
- options.agentDir ?? resolveDefaultAgentDir(options.config),
3342
- authProfileSelector,
3343
- options.authBindingFingerprint ?? null,
3344
- options.authRequirement ?? null
3345
- ])
4125
+ }),
4126
+ ...options?.pluginConfig !== void 0 ? { pluginConfig: options.pluginConfig } : {},
4127
+ ...desktopGeneration ? { desktopGeneration } : {}
3346
4128
  };
3347
4129
  }
3348
- function readWarmSharedCodexAppServerClient(state, identity) {
3349
- const aliases = state.warmClientsByConfig?.get(identity.config)?.get(identity.startOptions);
3350
- const warm = aliases?.get(identity.selectorKey);
3351
- if (!warm) return;
3352
- if (state.clients.get(warm.key) !== warm.entry || warm.entry.client !== warm.client || warm.entry.closeWhenIdle || warm.entry.closeError) {
3353
- aliases?.delete(identity.selectorKey);
3354
- return;
3355
- }
3356
- return warm;
3357
- }
3358
- function rememberWarmSharedCodexAppServerClient(state, identity, warm) {
3359
- state.warmClientsByConfig ??= /* @__PURE__ */ new WeakMap();
3360
- let byStartOptions = state.warmClientsByConfig.get(identity.config);
3361
- if (!byStartOptions) {
3362
- byStartOptions = /* @__PURE__ */ new WeakMap();
3363
- state.warmClientsByConfig.set(identity.config, byStartOptions);
3364
- }
3365
- let aliases = byStartOptions.get(identity.startOptions);
3366
- if (!aliases) {
3367
- aliases = /* @__PURE__ */ new Map();
3368
- byStartOptions.set(identity.startOptions, aliases);
3369
- }
3370
- aliases.set(identity.selectorKey, warm);
4130
+ function shouldTrackDesktopGeneration(startOptions, pluginConfig) {
4131
+ if (startOptions.transport !== "stdio") return false;
4132
+ if (resolveCodexComputerUseConfig({ pluginConfig }).enabled && (startOptions.commandSource === "managed" || startOptions.commandSource === "resolved-managed")) return true;
4133
+ return startOptions.commandSource === "managed" && (startOptions.managedCommandOrder ?? "package-first") === "desktop-first";
3371
4134
  }
3372
4135
  /** Gets or starts a shared Codex app-server client without retaining a lease. */
3373
4136
  async function getSharedCodexAppServerClient(options) {
@@ -3399,7 +4162,7 @@ function releaseCodexAppServerClientLease(lease) {
3399
4162
  lease.client = void 0;
3400
4163
  return client ? releaseLeasedSharedCodexAppServerClient(client) : false;
3401
4164
  }
3402
- /** Retries one config-loading request after moving its lease to the current owner. */
4165
+ /** Retries one config-loading operation with a shared deadline and current client lease. */
3403
4166
  async function withLeasedCodexAppServerClientStartSelectionRetry(params) {
3404
4167
  let client = params.lease.client;
3405
4168
  if (!client) throw new Error("Codex app-server selection retry requires an active client lease");
@@ -3415,43 +4178,49 @@ async function withLeasedCodexAppServerClientStartSelectionRetry(params) {
3415
4178
  ...signal ? { signal } : {}
3416
4179
  };
3417
4180
  };
3418
- for (let attempt = 0; attempt < 2; attempt += 1) try {
3419
- return await params.run(client, requestOptions());
3420
- } catch (error) {
3421
- if (!isCodexAppServerStartSelectionChangedError(error) || attempt > 0) throw error;
3422
- retireSharedCodexAppServerClientIfCurrent(client);
3423
- params.lease.client = void 0;
3424
- if (!releaseLeasedSharedCodexAppServerClient(client)) {
3425
- client.close();
3426
- throw new Error("Codex app-server selection retry requires a leased shared client", { cause: error });
4181
+ for (let attempt = 0; attempt < 2; attempt += 1) {
4182
+ const attemptClient = client;
4183
+ let scopeActive = true;
4184
+ const assertCurrent = () => {
4185
+ if (!scopeActive || params.lease.client !== attemptClient) throw new CodexAppServerStartupError("aborted", "Codex app-server request scope is closed");
4186
+ };
4187
+ try {
4188
+ requestOptions();
4189
+ return await params.run(client, () => {
4190
+ assertCurrent();
4191
+ return {
4192
+ ...requestOptions(),
4193
+ assertCurrent
4194
+ };
4195
+ });
4196
+ } catch (error) {
4197
+ if (!isCodexAppServerStartSelectionChangedError(error) || attempt > 0) throw error;
4198
+ retireSharedCodexAppServerClientIfCurrent(client);
4199
+ params.lease.client = void 0;
4200
+ if (!releaseLeasedSharedCodexAppServerClient(client)) {
4201
+ client.close();
4202
+ throw new Error("Codex app-server selection retry requires a leased shared client", { cause: error });
4203
+ }
4204
+ const replacementOptions = requestOptions();
4205
+ client = await getLeasedSharedCodexAppServerClient({
4206
+ ...params.options,
4207
+ timeoutMs: replacementOptions.timeoutMs,
4208
+ ...signal ? { abandonSignal: signal } : {}
4209
+ });
4210
+ params.lease.client = client;
4211
+ params.onClientChange?.(client);
4212
+ } finally {
4213
+ scopeActive = false;
3427
4214
  }
3428
- const replacementOptions = requestOptions();
3429
- client = await getLeasedSharedCodexAppServerClient({
3430
- ...params.options,
3431
- timeoutMs: replacementOptions.timeoutMs,
3432
- ...signal ? { abandonSignal: signal } : {}
3433
- });
3434
- params.lease.client = client;
3435
- params.onClientChange(client);
3436
4215
  }
3437
4216
  throw new Error("Codex app-server selection retry loop exited unexpectedly");
3438
4217
  }
3439
4218
  async function acquireSharedCodexAppServerClient(options, leaseOptions) {
3440
4219
  if (options?.abandonSignal?.aborted) throw new CodexAppServerStartupError("aborted", "codex app-server initialize aborted");
3441
- const state = getSharedCodexAppServerClientState();
3442
- const warmIdentity = resolveWarmSharedCodexAppServerClientIdentity(options);
3443
- const warmClient = warmIdentity ? readWarmSharedCodexAppServerClient(state, warmIdentity) : void 0;
3444
- if (warmClient) {
3445
- options?.onStartedClient?.(warmClient.client);
3446
- const release = leaseOptions?.leased ? retainSharedClientEntry(warmClient.entry) : void 0;
3447
- return release ? {
3448
- client: warmClient.client,
3449
- release
3450
- } : { client: warmClient.client };
3451
- }
3452
4220
  const acquireStartedAt = Date.now();
3453
4221
  const timeoutMs = options?.timeoutMs ?? 0;
3454
- const { agentDir, usesNativeAuth, authProfileId, authProfileStore, preparedAuth, authRequirement, requestedStartOptions, startOptions } = await withCodexAppServerAcquireDeadline(timeoutMs, resolveCodexAppServerClientStartContext(options), options?.abandonSignal);
4222
+ const state = getSharedCodexAppServerClientState();
4223
+ const { agentDir, usesNativeAuth, authProfileId, authProfileStore, preparedAuth, authRequirement, requestedStartOptions, startOptions, desktopGeneration, pluginConfig } = await withCodexAppServerAcquireDeadline(timeoutMs, resolveCodexAppServerClientStartContext(options), options?.abandonSignal);
3455
4224
  const remainingTimeoutMs = resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt);
3456
4225
  const authIdentityCacheKey = preparedAuth?.kind === "api-key" ? resolveCodexAppServerPreparedApiKeyCacheKey(preparedAuth.apiKey) : preparedAuth?.snapshot.secretFreeCacheKey ?? (authRequirement === "api-key" && !authProfileId ? resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions }) : void 0);
3457
4226
  const baseKey = `${codexAppServerStartOptionsKey(startOptions, {
@@ -3459,12 +4228,18 @@ async function acquireSharedCodexAppServerClient(options, leaseOptions) {
3459
4228
  authBindingFingerprint: options?.authBindingFingerprint,
3460
4229
  agentDir: usesNativeAuth ? void 0 : agentDir,
3461
4230
  fallbackApiKeyCacheKey: authIdentityCacheKey
3462
- })}\0auth-requirement:${authRequirement ?? "native"}`;
4231
+ })}\0auth-requirement:${authRequirement ?? "native"}${desktopGeneration ? `\0desktop-generation:${desktopGeneration.epoch}` : ""}`;
3463
4232
  const runtimeArtifactMode = options?.runtimeArtifactMode ?? (options?.expectedRuntimeArtifact ? "capture" : void 0);
3464
4233
  const expectedRuntimeArtifactKey = options?.expectedRuntimeArtifact ? createHash("sha256").update(options.expectedRuntimeArtifact.id).update("\0").update(options.expectedRuntimeArtifact.fingerprint).digest("hex") : "mint";
3465
4234
  const key = runtimeArtifactMode ? `${baseKey}\0runtime-artifact:capture-v1:${expectedRuntimeArtifactKey}` : baseKey;
3466
- const entry = getOrCreateSharedClientEntry(state, key);
3467
- if (runtimeArtifactMode) entry.runtimeArtifactStartupAbort ??= new AbortController();
4235
+ let entry = getOrCreateSharedClientEntry(state, key);
4236
+ const existingClient = entry.client;
4237
+ const existingGeneration = existingClient ? getCodexAppServerClientStartMetadata().get(existingClient)?.desktopGeneration : void 0;
4238
+ if (existingClient && existingGeneration && !isCodexDesktopGenerationCurrent(existingGeneration)) {
4239
+ retireSharedCodexAppServerClientIfCurrent(existingClient);
4240
+ entry = getOrCreateSharedClientEntry(state, key);
4241
+ }
4242
+ entry.startupAbort ??= new AbortController();
3468
4243
  entry.closeWhenIdle = false;
3469
4244
  const releasePendingAcquire = retainPendingSharedClientAcquire(entry);
3470
4245
  const startedCallback = options?.onStartedClient;
@@ -3491,6 +4266,8 @@ async function acquireSharedCodexAppServerClient(options, leaseOptions) {
3491
4266
  key,
3492
4267
  requestedStartOptions,
3493
4268
  startOptions,
4269
+ desktopGeneration,
4270
+ ...pluginConfig !== void 0 ? { pluginConfig } : {},
3494
4271
  agentDir,
3495
4272
  authProfileId: usesNativeAuth || preparedAuth?.kind === "api-key" ? null : authProfileId,
3496
4273
  authProfileStore,
@@ -3498,7 +4275,8 @@ async function acquireSharedCodexAppServerClient(options, leaseOptions) {
3498
4275
  authRequirement,
3499
4276
  runtimeArtifactMode,
3500
4277
  ...options?.expectedRuntimeArtifact ? { expectedRuntimeArtifact: options.expectedRuntimeArtifact } : {},
3501
- runtimeArtifactSignal: entry.runtimeArtifactStartupAbort?.signal,
4278
+ runtimeArtifactSignal: entry.startupAbort.signal,
4279
+ abandonSignal: entry.startupAbort.signal,
3502
4280
  config: options?.config
3503
4281
  }));
3504
4282
  try {
@@ -3513,11 +4291,6 @@ async function acquireSharedCodexAppServerClient(options, leaseOptions) {
3513
4291
  config: options?.config
3514
4292
  });
3515
4293
  const release = leaseOptions?.leased ? retainSharedClientEntry(entry) : void 0;
3516
- if (warmIdentity) rememberWarmSharedCodexAppServerClient(state, warmIdentity, {
3517
- key,
3518
- entry,
3519
- client
3520
- });
3521
4294
  return release ? {
3522
4295
  client,
3523
4296
  release
@@ -3557,6 +4330,8 @@ function createSharedCodexAppServerClientStartup(params) {
3557
4330
  const ready = startInitializedCodexAppServerClient({
3558
4331
  requestedStartOptions: params.requestedStartOptions,
3559
4332
  startOptions: params.startOptions,
4333
+ ...params.desktopGeneration ? { desktopGeneration: params.desktopGeneration } : {},
4334
+ ...params.pluginConfig !== void 0 ? { pluginConfig: params.pluginConfig } : {},
3560
4335
  agentDir: params.agentDir,
3561
4336
  authProfileId: params.authProfileId,
3562
4337
  authProfileStore: params.authProfileStore,
@@ -3565,13 +4340,17 @@ function createSharedCodexAppServerClientStartup(params) {
3565
4340
  runtimeArtifactMode: params.runtimeArtifactMode,
3566
4341
  ...params.expectedRuntimeArtifact ? { expectedRuntimeArtifact: params.expectedRuntimeArtifact } : {},
3567
4342
  runtimeArtifactSignal: params.runtimeArtifactSignal,
4343
+ abandonSignal: params.abandonSignal,
3568
4344
  config: params.config,
3569
4345
  onStartedClient: (startedClient) => {
3570
4346
  const state = getSharedCodexAppServerClientState();
3571
4347
  params.entry.client = startedClient;
3572
4348
  state.liveClients.add(startedClient);
4349
+ startedClient.addTransportExitHandler((exitedClient) => {
4350
+ state.liveClients.delete(exitedClient);
4351
+ notifyDesktopGenerationDrainChecks(state);
4352
+ });
3573
4353
  startedClient.addCloseHandler((closedClient) => {
3574
- state.liveClients.delete(closedClient);
3575
4354
  if (state.entriesByClient.get(closedClient) === params.entry) clearSharedClientEntryIfCurrent(params.key, closedClient);
3576
4355
  });
3577
4356
  for (const callback of params.entry.onStartedClientCallbacks) callback(startedClient);
@@ -3587,6 +4366,7 @@ function createSharedCodexAppServerClientStartup(params) {
3587
4366
  initialized.reject(error);
3588
4367
  throw error;
3589
4368
  });
4369
+ initialized.promise.catch(() => void 0);
3590
4370
  ready.catch(() => void 0);
3591
4371
  return {
3592
4372
  initialized: initialized.promise,
@@ -3598,10 +4378,12 @@ async function createIsolatedCodexAppServerClient(options) {
3598
4378
  if (options?.abandonSignal?.aborted) throw new CodexAppServerStartupError("aborted", "codex app-server initialize aborted");
3599
4379
  const acquireStartedAt = Date.now();
3600
4380
  const timeoutMs = options?.timeoutMs ?? 0;
3601
- const { agentDir, usesNativeAuth, authProfileId, authProfileStore, preparedAuth, authRequirement, requestedStartOptions, startOptions } = await withCodexAppServerAcquireDeadline(timeoutMs, resolveCodexAppServerClientStartContext(options), options?.abandonSignal);
4381
+ const { agentDir, usesNativeAuth, authProfileId, authProfileStore, preparedAuth, authRequirement, requestedStartOptions, startOptions, desktopGeneration, pluginConfig } = await withCodexAppServerAcquireDeadline(timeoutMs, resolveCodexAppServerClientStartContext(options), options?.abandonSignal);
3602
4382
  return await startInitializedCodexAppServerClient({
3603
4383
  requestedStartOptions,
3604
4384
  startOptions,
4385
+ ...desktopGeneration ? { desktopGeneration } : {},
4386
+ ...pluginConfig !== void 0 ? { pluginConfig } : {},
3605
4387
  agentDir,
3606
4388
  authProfileId: usesNativeAuth || preparedAuth?.kind === "api-key" ? null : authProfileId,
3607
4389
  authProfileStore,
@@ -3613,7 +4395,18 @@ async function createIsolatedCodexAppServerClient(options) {
3613
4395
  config: options?.config,
3614
4396
  timeoutMs: resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt),
3615
4397
  abandonSignal: options?.abandonSignal,
3616
- onStartedClient: options?.onStartedClient
4398
+ onStartedClient: (client) => {
4399
+ trackIsolatedCodexAppServerClient(client);
4400
+ options?.onStartedClient?.(client);
4401
+ }
4402
+ });
4403
+ }
4404
+ function trackIsolatedCodexAppServerClient(client) {
4405
+ const state = getSharedCodexAppServerClientState();
4406
+ state.isolatedClients.add(client);
4407
+ client.addTransportExitHandler((exitedClient) => {
4408
+ state.isolatedClients.delete(exitedClient);
4409
+ notifyDesktopGenerationDrainChecks(state);
3617
4410
  });
3618
4411
  }
3619
4412
  async function startInitializedCodexAppServerClient(params) {
@@ -3621,7 +4414,41 @@ async function startInitializedCodexAppServerClient(params) {
3621
4414
  const timeoutMs = params.timeoutMs ?? 0;
3622
4415
  const startOptionsCandidates = resolveManagedFallbackStartOptions(params.startOptions);
3623
4416
  for (const [index, startOptions] of startOptionsCandidates.entries()) {
3624
- const runtimeArtifactModule = params.runtimeArtifactMode ? await import("./runtime-artifact-BvyT1uyd.js") : void 0;
4417
+ const desktopGeneration = params.desktopGeneration ?? (isManagedCodexDesktopCommand(startOptions.command) ? await withCodexAppServerAcquireDeadline(resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt), waitForCodexDesktopGeneration(), params.abandonSignal) : void 0);
4418
+ const assertDesktopGenerationCurrent = () => {
4419
+ if (params.abandonSignal?.aborted) throw new CodexAppServerStartupError("aborted", "codex app-server initialize aborted");
4420
+ if (desktopGeneration && !isCodexDesktopGenerationCurrent(desktopGeneration)) throw new CodexAppServerStartSelectionChangedError();
4421
+ };
4422
+ const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig: params.pluginConfig });
4423
+ const ownsIsolatedCodexHome = params.requestedStartOptions.homeScope !== "user" && !params.requestedStartOptions.env?.CODEX_HOME?.trim();
4424
+ const artifactDrain = desktopGeneration && ownsIsolatedCodexHome && computerUseConfig.enabled && (computerUseConfig.autoInstall || computerUseConfig.pluginCacheMode === "shared") ? createOlderDesktopGenerationDrainWait({
4425
+ generation: desktopGeneration,
4426
+ startOptions,
4427
+ agentDir: params.agentDir
4428
+ }) : void 0;
4429
+ try {
4430
+ if (artifactDrain) await withCodexAppServerAcquireDeadline(resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt), artifactDrain.promise, params.abandonSignal);
4431
+ await reconcileCodexComputerUseStartArtifacts({
4432
+ startOptions,
4433
+ agentDir: params.agentDir,
4434
+ pluginConfig: params.pluginConfig,
4435
+ ...desktopGeneration ? { desktopGeneration } : {},
4436
+ assertCurrent: assertDesktopGenerationCurrent,
4437
+ ownsIsolatedCodexHome
4438
+ });
4439
+ } catch (error) {
4440
+ if (isCodexComputerUseCandidateArtifactsUnavailableError(error)) {
4441
+ if (index + 1 < startOptionsCandidates.length) continue;
4442
+ throw new AgentHarnessPreflightError("Codex Computer Use artifacts are unavailable from the installed desktop apps.", {
4443
+ cause: error,
4444
+ scope: "harness"
4445
+ });
4446
+ }
4447
+ throw error;
4448
+ } finally {
4449
+ artifactDrain?.cancel();
4450
+ }
4451
+ const runtimeArtifactModule = params.runtimeArtifactMode ? await import("./runtime-artifact-DeBvleJw.js") : void 0;
3625
4452
  const nativeCommandBeforeStart = startOptions.commandSource === "resolved-managed" ? resolveManagedCodexNativeCommand(startOptions.command) : void 0;
3626
4453
  const runtimeArtifactBeforeStart = runtimeArtifactModule ? await runtimeArtifactModule.captureCodexAppServerRuntimeArtifactBeforeStart({
3627
4454
  startOptions,
@@ -3632,7 +4459,26 @@ async function startInitializedCodexAppServerClient(params) {
3632
4459
  if (index + 1 < startOptionsCandidates.length) continue;
3633
4460
  throw new Error("Codex app-server runtime artifact does not match verified inference");
3634
4461
  }
3635
- const client = CodexAppServerClient.start(startOptions);
4462
+ assertDesktopGenerationCurrent();
4463
+ let starting;
4464
+ let client;
4465
+ try {
4466
+ client = await withCodexAppServerAcquireDeadline(resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt), starting = CodexAppServerClient.start(startOptions, () => {
4467
+ assertDesktopGenerationCurrent();
4468
+ resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt);
4469
+ }), params.abandonSignal);
4470
+ } catch (error) {
4471
+ starting?.then((lateClient) => lateClient.close(), () => {});
4472
+ throw error;
4473
+ }
4474
+ const nativeCommandAtStart = startOptions.commandSource === "resolved-managed" ? resolveManagedCodexNativeCommand(startOptions.command) : void 0;
4475
+ getCodexAppServerClientStartMetadata().set(client, {
4476
+ requestedStartOptions: params.requestedStartOptions,
4477
+ startOptions,
4478
+ agentDir: params.agentDir,
4479
+ ...nativeCommandAtStart ? { nativeCommand: nativeCommandAtStart } : {},
4480
+ ...desktopGeneration ? { desktopGeneration } : {}
4481
+ });
3636
4482
  params.onStartedClient?.(client);
3637
4483
  let initialize;
3638
4484
  try {
@@ -3643,6 +4489,12 @@ async function startInitializedCodexAppServerClient(params) {
3643
4489
  if (shouldTryManagedFallbackStartOption(error, startOptions, index, startOptionsCandidates)) continue;
3644
4490
  throw error;
3645
4491
  }
4492
+ try {
4493
+ assertDesktopGenerationCurrent();
4494
+ } catch (error) {
4495
+ client.close();
4496
+ throw error;
4497
+ }
3646
4498
  params.onInitializedClient?.();
3647
4499
  let runtimeArtifact;
3648
4500
  try {
@@ -3666,9 +4518,11 @@ async function startInitializedCodexAppServerClient(params) {
3666
4518
  authProfileId: params.authProfileId ?? void 0,
3667
4519
  authMode: params.preparedAuth?.kind === "api-key" ? "prepared-api-key" : "profile",
3668
4520
  ...params.authProfileStore ? { authProfileStore: params.authProfileStore } : {},
3669
- config: params.config
4521
+ config: params.config,
4522
+ onAuthRefreshFailure: () => retireSharedCodexAppServerClientIfCurrent(client)
3670
4523
  });
3671
4524
  try {
4525
+ assertDesktopGenerationCurrent();
3672
4526
  await withCodexAppServerAcquireDeadline(resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt), applyCodexAppServerAuthProfile({
3673
4527
  client,
3674
4528
  agentDir: params.agentDir,
@@ -3679,14 +4533,8 @@ async function startInitializedCodexAppServerClient(params) {
3679
4533
  config: params.config,
3680
4534
  ...params.authProfileStore ? { authProfileStore: params.authProfileStore } : {}
3681
4535
  }), params.abandonSignal);
3682
- const nativeCommand = startOptions.commandSource === "resolved-managed" ? resolveManagedCodexNativeCommand(startOptions.command) : void 0;
3683
4536
  if (runtimeArtifactModule && runtimeArtifact) runtimeArtifactModule.bindCodexAppServerRuntimeArtifact(client, runtimeArtifact);
3684
- getCodexAppServerClientStartMetadata().set(client, {
3685
- requestedStartOptions: params.requestedStartOptions,
3686
- startOptions,
3687
- agentDir: params.agentDir,
3688
- ...nativeCommand ? { nativeCommand } : {}
3689
- });
4537
+ assertDesktopGenerationCurrent();
3690
4538
  const fenceKey = resolveCodexNativeConfigFenceKey({ client });
3691
4539
  if (fenceKey) client.setThreadSessionRequestGuard(async (options) => {
3692
4540
  const release = await acquireCodexNativeConfigFence(fenceKey, options);
@@ -3706,6 +4554,9 @@ async function startInitializedCodexAppServerClient(params) {
3706
4554
  }
3707
4555
  throw new Error("Managed Codex app-server fallback candidates were exhausted.");
3708
4556
  }
4557
+ function isCodexComputerUseCandidateArtifactsUnavailableError(error) {
4558
+ return error !== null && typeof error === "object" && "code" in error && error.code === "CODEX_COMPUTER_USE_CANDIDATE_ARTIFACTS_UNAVAILABLE";
4559
+ }
3709
4560
  function resolveManagedFallbackStartOptions(startOptions) {
3710
4561
  const commands = [startOptions.command, ...startOptions.managedFallbackCommandPaths ?? []];
3711
4562
  const candidates = [];
@@ -3735,6 +4586,15 @@ function clearSharedCodexAppServerClientIfCurrent(client) {
3735
4586
  }
3736
4587
  return false;
3737
4588
  }
4589
+ /** Captures a revocable observation of the exact shared client and native account/config. */
4590
+ function captureSharedCodexAppServerCatalogLifetime(client) {
4591
+ const state = getSharedCodexAppServerClientState();
4592
+ const entry = state.entriesByClient.get(client);
4593
+ const key = [...state.clients].find(([, candidate]) => candidate === entry)?.[0];
4594
+ const generation = readCodexAppServerClientDesktopGeneration(client);
4595
+ const revision = client.getModelCatalogRevision();
4596
+ return () => key !== void 0 && state.clients.get(key) === entry && entry?.client === client && !entry.closeWhenIdle && !entry.closeError && !client.getCloseError() && client.getModelCatalogRevision() === revision && (!generation || isCodexDesktopGenerationCurrent(generation));
4597
+ }
3738
4598
  /** Retains the matching shared client and returns a release callback. */
3739
4599
  function retainSharedCodexAppServerClientIfCurrent(client) {
3740
4600
  if (!client) return;
@@ -3754,6 +4614,20 @@ function retainSharedCodexAppServerClientByInstanceId(clientId) {
3754
4614
  };
3755
4615
  }
3756
4616
  }
4617
+ /** Captures sole physical-client ownership across awaited configuration adoption. */
4618
+ function captureExclusiveSharedCodexAppServerClient(client) {
4619
+ const state = getSharedCodexAppServerClientState();
4620
+ for (const [key, entry] of state.clients) {
4621
+ if (entry.client !== client) continue;
4622
+ const generation = entry.leaseGeneration;
4623
+ const assertExclusive = () => {
4624
+ if (state.clients.get(key) !== entry || entry.client !== client || entry.closeWhenIdle || entry.closeError || entry.activeLeases !== 1 || entry.pendingAcquires !== 0 || entry.leaseGeneration !== generation) throw new CodexAdoptedThreadActiveError();
4625
+ };
4626
+ assertExclusive();
4627
+ return assertExclusive;
4628
+ }
4629
+ throw new CodexAdoptedThreadActiveError();
4630
+ }
3757
4631
  /**
3758
4632
  * Retires a matching shared client. Default is graceful: detach from the map
3759
4633
  * (future acquisitions get a fresh client) and close once leases drain.
@@ -3797,6 +4671,62 @@ function retireSharedCodexAppServerClientIfCurrent(client, opts) {
3797
4671
  };
3798
4672
  }
3799
4673
  }
4674
+ /** Gracefully retires exact clients attached to an older desktop generation. */
4675
+ function retireSharedCodexAppServerClientsBeforeDesktopGeneration(generation) {
4676
+ const state = getSharedCodexAppServerClientState();
4677
+ for (const entry of state.clients.values()) {
4678
+ const client = entry.client;
4679
+ const attached = client ? getCodexAppServerClientStartMetadata().get(client) : void 0;
4680
+ if (client && attached?.desktopGeneration && attached.desktopGeneration.epoch < generation.epoch) retireSharedCodexAppServerClientIfCurrent(client);
4681
+ }
4682
+ }
4683
+ function createOlderDesktopGenerationDrainWait(params) {
4684
+ const targetHome = resolveCodexNativeConfigFenceKey({
4685
+ startOptions: params.startOptions,
4686
+ agentDir: params.agentDir
4687
+ });
4688
+ if (!targetHome) return {
4689
+ promise: Promise.resolve(),
4690
+ cancel: () => void 0
4691
+ };
4692
+ const state = getSharedCodexAppServerClientState();
4693
+ let settled = false;
4694
+ let resolveWait;
4695
+ const promise = new Promise((resolve) => {
4696
+ resolveWait = resolve;
4697
+ });
4698
+ const cancel = () => {
4699
+ if (settled) return;
4700
+ settled = true;
4701
+ state.desktopGenerationDrainChecks.delete(check);
4702
+ resolveWait();
4703
+ };
4704
+ const check = () => {
4705
+ if (!hasLiveOlderDesktopGenerationClient({
4706
+ state,
4707
+ generation: params.generation,
4708
+ targetHome
4709
+ })) cancel();
4710
+ };
4711
+ state.desktopGenerationDrainChecks.add(check);
4712
+ check();
4713
+ return {
4714
+ promise,
4715
+ cancel
4716
+ };
4717
+ }
4718
+ function hasLiveOlderDesktopGenerationClient(params) {
4719
+ for (const client of params.state.liveClients) if (isOlderDesktopGenerationClientForHome(client, params.generation, params.targetHome)) return true;
4720
+ for (const client of params.state.isolatedClients) if (isOlderDesktopGenerationClientForHome(client, params.generation, params.targetHome)) return true;
4721
+ return false;
4722
+ }
4723
+ function isOlderDesktopGenerationClientForHome(client, generation, targetHome) {
4724
+ const metadata = getCodexAppServerClientStartMetadata().get(client);
4725
+ return Boolean(metadata?.desktopGeneration && metadata.desktopGeneration.epoch < generation.epoch && resolveCodexNativeConfigFenceKey({ client }) === targetHome);
4726
+ }
4727
+ function notifyDesktopGenerationDrainChecks(state) {
4728
+ for (const check of state.desktopGenerationDrainChecks) check();
4729
+ }
3800
4730
  /** Clears a matching shared client and waits for its process to exit. */
3801
4731
  async function clearSharedCodexAppServerClientIfCurrentAndWait(client, options) {
3802
4732
  if (!client) return false;
@@ -3813,7 +4743,6 @@ async function clearSharedCodexAppServerClientAndWait(options) {
3813
4743
  const state = getSharedCodexAppServerClientState();
3814
4744
  const clients = collectSharedClients(state);
3815
4745
  state.clients.clear();
3816
- state.warmClientsByConfig = /* @__PURE__ */ new WeakMap();
3817
4746
  await Promise.all(clients.map((client) => client.closeAndWait(options)));
3818
4747
  }
3819
4748
  globalThis[SHARED_CODEX_APP_SERVER_CLIENT_DISPOSER] = clearSharedCodexAppServerClientAndWait;
@@ -3823,6 +4752,7 @@ function getOrCreateSharedClientEntry(state, key) {
3823
4752
  entry = {
3824
4753
  activeLeases: 0,
3825
4754
  pendingAcquires: 0,
4755
+ leaseGeneration: 0,
3826
4756
  closeWhenIdle: false,
3827
4757
  onStartedClientCallbacks: /* @__PURE__ */ new Set()
3828
4758
  };
@@ -3858,22 +4788,26 @@ function clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client) {
3858
4788
  }
3859
4789
  function retainPendingSharedClientAcquire(entry) {
3860
4790
  let released = false;
4791
+ entry.leaseGeneration += 1;
3861
4792
  entry.pendingAcquires += 1;
3862
4793
  return () => {
3863
4794
  if (released) return;
3864
4795
  released = true;
3865
4796
  entry.pendingAcquires = Math.max(0, entry.pendingAcquires - 1);
3866
4797
  closeRetiredSharedClientEntryIfIdle(entry);
4798
+ notifyDesktopGenerationDrainChecks(getSharedCodexAppServerClientState());
3867
4799
  };
3868
4800
  }
3869
4801
  function retainSharedClientEntry(entry) {
3870
4802
  let released = false;
4803
+ entry.leaseGeneration += 1;
3871
4804
  entry.activeLeases += 1;
3872
4805
  return () => {
3873
4806
  if (released) return;
3874
4807
  released = true;
3875
4808
  entry.activeLeases = Math.max(0, entry.activeLeases - 1);
3876
4809
  closeRetiredSharedClientEntryIfIdle(entry);
4810
+ notifyDesktopGenerationDrainChecks(getSharedCodexAppServerClientState());
3877
4811
  };
3878
4812
  }
3879
4813
  function closeRetiredSharedClientEntryIfIdle(entry) {
@@ -3901,7 +4835,7 @@ function closeSharedClientEntryIfUnclaimed(key, entry) {
3901
4835
  }
3902
4836
  function retirePendingSharedClientEntryIfUnclaimed(key, entry) {
3903
4837
  if (entry.activeLeases > 0 || entry.pendingAcquires > 0) return;
3904
- entry.runtimeArtifactStartupAbort?.abort(/* @__PURE__ */ new Error("Codex runtime artifact startup was abandoned"));
4838
+ entry.startupAbort?.abort(/* @__PURE__ */ new Error("Codex app-server startup was abandoned"));
3905
4839
  entry.closeWhenIdle = true;
3906
4840
  const state = getSharedCodexAppServerClientState();
3907
4841
  if (state.clients.get(key) === entry) state.clients.delete(key);
@@ -3912,4 +4846,4 @@ function collectSharedClients(state) {
3912
4846
  return [...state.liveClients];
3913
4847
  }
3914
4848
  //#endregion
3915
- export { rememberCodexRateLimitsRead as $, isCodexAppServerApprovalRequest as A, CodexAppServerRpcError as B, resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs as C, resolveCodexTurnTerminalIdleTimeoutMs as D, resolveCodexTurnCompletionIdleTimeoutMs as E, isCodexAppServerPrewriteRequestCancellationError as F, isCodexAppServerClientRuntimeLive as G, claimCodexAppServerLiveThread as H, isCodexAppServerRequestTimeoutError as I, releaseCodexAppServerLiveThread as J, isCodexAppServerLiveThreadClaimed as K, isUnsupportedCodexAppServerVersionError as L, isCodexAppServerConnectionClosedError as M, isCodexAppServerIndeterminateRequestCancellationError as N, withCodexStartupTimeout as O, isCodexAppServerIndeterminateTransportError as P, readRecentCodexRateLimits as Q, resolveCodexAppServerClientInstanceId as R, resolveCodexGatewayTimeoutWithGraceMs as S, resolveCodexTurnAssistantCompletionIdleTimeoutMs as T, consumeCodexAppServerLiveThread as U, createCodexElicitationResponse as V, ensureCodexAppServerClientRuntime as W, unsubscribeCodexAppServerLiveThread as X, retainCodexAppServerLiveThread as Y, readCodexRateLimitsRevision as Z, withTimeout$1 as _, getLeasedSharedCodexAppServerClient as a, resolveCodexAppServerPreparedAuthHandoff as at, CodexAppServerStartupError as b, releaseCodexAppServerClientLease as c, CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE as ct, retainSharedCodexAppServerClientByInstanceId as d, resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath as dt, resolveCodexAppServerAuthAccountCacheKey as et, retainSharedCodexAppServerClientIfCurrent as f, resolveCodexAppServerHomeDir as ft, withAbortableTimeout as g, withLeasedCodexAppServerClientStartSelectionRetry as h, createIsolatedCodexAppServerClient as i, resolveCodexAppServerPreparedApiKeyCacheKey as it, isCodexAppServerBrokenPipeError as j, getCodexAppServerClientInstanceId as k, releaseLeasedSharedCodexAppServerClient as l, flattenCodexDynamicToolFunctions as lt, shared_client_exports as m, clearSharedCodexAppServerClientIfCurrentAndUnclaimed as n, resolveCodexAppServerAuthProfileIdForAgent as nt, getSharedCodexAppServerClient as o, CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES as ot, retireSharedCodexAppServerClientIfCurrent as p, resolveCodexAppServerLocalHomeDir as pt, protectCodexAppServerLiveThread as q, clearSharedCodexAppServerClientIfCurrentAndWait as r, resolveCodexAppServerFallbackApiKeyCacheKey as rt, isCodexAppServerStartSelectionChangedError as s, CODEX_INTERACTIVE_THREAD_SOURCE_KINDS as st, clearSharedCodexAppServerClientIfCurrent as t, resolveCodexAppServerAuthProfileId as tt, resolveCodexNativeConfigFenceKey as u, isJsonObject as ut, acquireCodexNativeConfigFence as v, resolveCodexStartupTimeoutMs as w, isCodexAppServerStartupError as x, CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS as y, closeCodexAppServerTransportAndWait as z };
4849
+ export { CodexAppServerRpcError as $, CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS as A, reconcileCodexComputerUseStartArtifacts as At, resolveCodexTurnTerminalIdleTimeoutMs as B, CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE as Bt, withLeasedCodexAppServerClientStartSelectionRetry as C, retainCodexAppServerLiveThread as Ct, CodexThreadStartRequestError as D, readCodexRateLimitsRevision as Dt, CodexThreadBindingConflictError as E, withTimeout$1 as Et, resolveCodexGatewayTimeoutWithGraceMs as F, resolveCodexAppServerFallbackApiKeyCacheKey as Ft, isCodexAppServerConnectionClosedError as G, getCodexAppServerClientInstanceId as H, isJsonObject as Ht, resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs as I, resolveCodexAppServerPreparedApiKeyCacheKey as It, isCodexAppServerOverloadError as J, isCodexAppServerIndeterminateRequestCancellationError as K, resolveCodexStartupTimeoutMs as L, resolveCodexAppServerPreparedAuthHandoff as Lt, TURN_FINALIZE_DRAIN_ABORT_GRACE_MS as M, resolveCodexAppServerAuthProfileId as Mt, TURN_TERMINAL_SETTLEMENT_IDLE_TIMEOUT_MS as N, resolveCodexAppServerAuthProfileIdForAgent as Nt, acquireCodexNativeConfigFence as O, readRecentCodexRateLimits as Ot, isCodexAppServerStartupError as P, resolveCodexAppServerAuthProfileStore as Pt, resolveCodexAppServerClientInstanceId as Q, resolveCodexTurnAssistantCompletionIdleTimeoutMs as R, CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES as Rt, waitForCodexAppServerClientDesktopGenerationDrain as S, releaseCodexAppServerLiveThread as St, CodexRestrictedToolSurfaceAttestationError as T, withAbortableTimeout as Tt, isCodexAppServerApprovalRequest as U, resolveCodexManagedBundledMarketplacePath as Ut, withCodexStartupTimeout as V, flattenCodexDynamicToolFunctions as Vt, isCodexAppServerBrokenPipeError as W, assertNotSymlink as Wt, isCodexAppServerRequestTimeoutError as X, isCodexAppServerPrewriteRequestCancellationError as Y, isUnsupportedCodexAppServerVersionError as Z, retainSharedCodexAppServerClientByInstanceId as _, ensureCodexAppServerClientRuntime as _t, clearSharedCodexAppServerClientIfCurrentAndUnclaimed as a, resolveDynamicToolCallTimeoutMs as at, retireSharedCodexAppServerClientsBeforeDesktopGeneration as b, isCodexAppServerLiveThreadClaimed as bt, getLeasedSharedCodexAppServerClient as c, shouldReleaseTurnAfterTerminalDynamicTool as ct, readCodexAppServerClientDesktopGeneration as d, resolveCodexToolAbortTerminalReason as dt, createCodexElicitationResponse as et, readCodexAppServerClientDesktopGenerationFingerprint as f, createFailedDynamicToolResponse as ft, resolveCodexNativeConfigFenceKey as g, consumeCodexAppServerLiveThread as gt, releaseLeasedSharedCodexAppServerClient as h, claimCodexAppServerLiveThread as ht, clearSharedCodexAppServerClientIfCurrent as i, isMatchingDynamicToolTerminalDiagnostic as it, CodexAppServerStartupError as j, resolveCodexAppServerAuthAccountCacheKey as jt, createCodexDesktopGenerationService as k, rememberCodexRateLimitsRead as kt, getSharedCodexAppServerClient as l, toCodexDynamicToolProgressResponse as lt, releaseCodexAppServerClientLease as m, withDynamicToolTranscriptDetails as mt, captureExclusiveSharedCodexAppServerClient as n, hasPendingDynamicToolTerminalDiagnostic as nt, clearSharedCodexAppServerClientIfCurrentAndWait as o, resolveTerminalDynamicToolBatchAction as ot, readCodexAppServerClientProcessIdentity as p, withDynamicToolExecutionState as pt, isCodexAppServerIndeterminateTransportError as q, captureSharedCodexAppServerCatalogLifetime as r, isDynamicToolTerminalDiagnosticEvent as rt, createIsolatedCodexAppServerClient as s, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as st, assertCodexAppServerClientStartSelectionCurrent as t, handleDynamicToolCallWithTimeout as tt, isCodexAppServerStartSelectionChangedError as u, toCodexDynamicToolProtocolResponse as ut, retainSharedCodexAppServerClientIfCurrent as v, hasCodexAppServerLiveThread as vt, CodexAdoptedThreadActiveError as w, unsubscribeCodexAppServerLiveThread as wt, shared_client_exports as x, protectCodexAppServerLiveThread as xt, retireSharedCodexAppServerClientIfCurrent as y, isCodexAppServerClientRuntimeLive as yt, resolveCodexTurnCompletionIdleTimeoutMs as z, CODEX_INTERACTIVE_THREAD_SOURCE_KINDS as zt };