@openclaw/codex 2026.8.1-beta.2 → 2026.8.1-beta.3

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 (59) hide show
  1. package/dist/api.js +147 -0
  2. package/dist/app-server-policy-bg5QPSVP.js +25 -0
  3. package/dist/{attempt-client-cleanup-DMLXUfa8.js → attempt-client-cleanup-URjUlCHg.js} +1 -1
  4. package/dist/{binding-connection-I5Bo-AsX.js → binding-connection-DAvZwp96.js} +15 -3
  5. package/dist/{bounded-turn-qgZmzy_4.js → bounded-turn-4BjrZEwW.js} +22 -16
  6. package/dist/{capabilities-CybGI1oz.js → capabilities-CH3K57v4.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-B1AEz8ZA.js → command-formatters-D0nZ3qtd.js} +2 -2
  9. package/dist/{command-handlers-Db8pu_ds.js → command-handlers-DC86lgT4.js} +39 -22
  10. package/dist/command-rpc-xUIIonsO.js +90 -0
  11. package/dist/{compact-LspQaGfl.js → compact-COqgBRny.js} +35 -29
  12. package/dist/{session-binding-DPxIvMgE.js → config-CpNeo5AE.js} +28 -888
  13. package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
  14. package/dist/doctor-contract-api.js +15 -14
  15. package/dist/{dynamic-tools-BVdyWkUD.js → dynamic-tools-fJZhl4CE.js} +1284 -651
  16. package/dist/{effective-mcp-catalog-DIIhAtOZ.js → effective-mcp-catalog-4YKn51aG.js} +2 -2
  17. package/dist/elicitation-input-DMkeki8d.js +71 -0
  18. package/dist/{event-projector-usage-e5huIQWu.js → event-projector-usage-CLwNEJqW.js} +16 -16
  19. package/dist/harness-DoKQcF8n.js +258 -0
  20. package/dist/harness.js +2 -226
  21. package/dist/index.js +171 -48
  22. package/dist/{isolated-completion-C58NbL40.js → isolated-completion-DPMtlH9r.js} +3 -3
  23. package/dist/managed-binary-By4FZC6i.js +212 -0
  24. package/dist/media-understanding-provider.js +2 -2
  25. package/dist/model-catalog-CmGoqVKB.js +48 -0
  26. package/dist/{models-BFlpn1Ex.js → models-CZw1wClT.js} +2 -2
  27. package/dist/{native-subagent-monitor-DyTGmTUx.js → native-subagent-monitor-GGq3RnOo.js} +185 -71
  28. package/dist/node-exec-server.runtime-Bsu1LdFZ.js +192 -0
  29. package/dist/{plugin-app-cache-key-C3H2CVp6.js → plugin-app-cache-key-DMNQ8Zi8.js} +15 -3
  30. package/dist/plugin-approval-roundtrip-C75Yiiwc.js +95 -0
  31. package/dist/{plugin-marketplace-discovery-CzrGJdCR.js → plugin-marketplace-discovery-CSWDkMrP.js} +40 -26
  32. package/dist/{plugin-metadata-cache-CNJPj4_b.js → plugin-metadata-cache-BXH5NF-4.js} +8 -5
  33. package/dist/{protocol-validators-B4F7L8Rf.js → protocol-validators-VAVyEnDp.js} +55 -2
  34. package/dist/{rate-limits-DZXmOL8-.js → rate-limits-BI1dMHDG.js} +1 -1
  35. package/dist/{request-BRl5_UfX.js → request-DSK0W6YI.js} +2 -2
  36. package/dist/{run-attempt-P8BdxC1I.js → run-attempt-BV3HzuCf.js} +1098 -666
  37. package/dist/{runtime-artifact-BVSPdd6g.js → runtime-artifact-BvyT1uyd.js} +4 -7
  38. package/dist/{sandbox-guard-Cmwc9eIk.js → sandbox-guard-C1qEeZ5C.js} +25 -19
  39. package/dist/session-binding-BTrLf9Qm.js +874 -0
  40. package/dist/{session-catalog-xwNi_SDM.js → session-catalog-ezP_UcRM.js} +1541 -1296
  41. package/dist/session-catalog-home-id-BNwAJ9D7.js +18 -0
  42. package/dist/{session-cli-7hgXZDV7.js → session-cli-B1hKvfkI.js} +20 -7
  43. package/dist/{session-retirement-DoMMJFKm.js → session-retirement-CE4iMiLh.js} +3 -3
  44. package/dist/{session-upstream-marker-CrIFX06J.js → session-upstream-marker-D_2npHlA.js} +1 -1
  45. package/dist/{settled-turn-finalizer-BRWBO4MO.js → settled-turn-finalizer-Dx1onIqg.js} +23 -19
  46. package/dist/{shared-client-kIBbGjk4.js → shared-client-Dw-rzii0.js} +755 -273
  47. package/dist/{side-question-9HAL26l9.js → side-question-DbXkJZzg.js} +52 -30
  48. package/dist/{thread-lifecycle-Bu1BTDXr.js → thread-lifecycle-DKdrnTMX.js} +310 -92
  49. package/dist/{thread-ownership-6kOQHlyC.js → thread-ownership-i2NBwDUL.js} +2 -2
  50. package/dist/{thread-resume-CPrJNQVM.js → thread-resume-CdJk2Ehf.js} +3 -3
  51. package/dist/{transcript-mirror-B9gGgnJS.js → transcript-mirror-BU0_LKAL.js} +46 -28
  52. package/dist/{transport-stdio-DIImwDVS.js → transport-stdio-DhoGy33Z.js} +2 -2
  53. package/dist/{upstream-prompt-provenance-ClRBije0.js → upstream-prompt-provenance-CFmsKwr9.js} +12 -14
  54. package/dist/{upstream-session-fork-CE-_LdnP.js → upstream-session-fork-BDVJCLAD.js} +12 -6
  55. package/dist/{usage-CHlV0e-o.js → usage-9dKJCgcy.js} +3 -3
  56. package/dist/{web-search-provider.runtime-DRw2Vl42.js → web-search-provider.runtime-DCdx4DGu.js} +3 -3
  57. package/openclaw.plugin.json +31 -1
  58. package/package.json +6 -5
  59. package/dist/app-server-policy-zcBDc1JU.js +0 -49
@@ -1,32 +1,34 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { A as resolveCodexAppServerUserHomeDir, _ as resolveCodexAppServerStartOptionsForAgent, g as resolveCodexAppServerRuntimeOptions, o as isCodexAppServerNativeAuthProfile, p as codexAppServerStartOptionsKey, v as resolveCodexComputerUseConfig } from "./session-binding-DPxIvMgE.js";
3
- import { n as resolveCodexAppServerSpawnEnv, t as createStdioTransport } from "./transport-stdio-DIImwDVS.js";
4
- import { createRequire } from "node:module";
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";
5
6
  import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
- import fs, { access } from "node:fs/promises";
7
+ import fs from "node:fs/promises";
7
8
  import path from "node:path";
8
9
  import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
9
10
  import { addTimerTimeoutGraceMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
10
11
  import { AgentHarnessPreflightError, OPENCLAW_VERSION, embeddedAgentLog, formatErrorMessage } from "openclaw/plugin-sdk/agent-harness-runtime";
11
12
  import { createHash, randomUUID } from "node:crypto";
12
- import fsSync, { constants, existsSync, readFileSync, realpathSync } from "node:fs";
13
+ import fsSync, { constants, existsSync } from "node:fs";
13
14
  import os from "node:os";
14
15
  import { isDeepStrictEqual } from "node:util";
15
16
  import { ensureAuthProfileStore, findPersistedAuthProfileCredential, loadAuthProfileStoreForSecretsRuntime, refreshOAuthCredentialForRuntime, resolveApiKeyForProfile, resolveAuthProfileOrder, resolveDefaultAgentDir, resolvePersistedAuthProfileOwnerAgentDir, resolveProviderIdForAuth } from "openclaw/plugin-sdk/agent-runtime";
16
17
  import { hasUsableOAuthCredential } from "openclaw/plugin-sdk/provider-auth";
17
18
  import { readSecretFile } from "openclaw/plugin-sdk/secret-file";
19
+ import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
18
20
  import { runExec } from "openclaw/plugin-sdk/process-runtime";
21
+ import { assertNoSymlinkParents, withTimeout } from "openclaw/plugin-sdk/security-runtime";
22
+ import { execFile } from "node:child_process";
19
23
  import { createInterface } from "node:readline";
20
24
  import { coerceErrorMessage, toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
21
25
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
26
+ import { parse } from "semver";
22
27
  import { EventEmitter } from "node:events";
23
28
  import net from "node:net";
24
29
  import { PassThrough, Writable } from "node:stream";
25
30
  import { StringDecoder } from "node:string_decoder";
26
31
  import WebSocket from "ws";
27
- import { fileURLToPath } from "node:url";
28
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
29
- import { withTimeout } from "openclaw/plugin-sdk/security-runtime";
30
32
  //#region extensions/codex/src/app-server/auth-start-options.ts
31
33
  const CODEX_APP_SERVER_HOME_DIRNAME = "codex-home";
32
34
  const CODEX_EPHEMERAL_AUTH_STORE_OVERRIDE = "cli_auth_credentials_store=\"ephemeral\"";
@@ -179,108 +181,385 @@ function skippedCacheResult(status, message) {
179
181
  };
180
182
  }
181
183
  //#endregion
184
+ //#region extensions/codex/src/app-server/computer-use-service-path.ts
185
+ /** Filesystem ownership guards for isolated Computer Use service provisioning. */
186
+ async function assertOwnedServicePath(params) {
187
+ await assertOwnedCodexHomePath({
188
+ ownershipRoot: params.ownershipRoot,
189
+ codexHome: params.codexHome,
190
+ allowMissing: true
191
+ });
192
+ assertPathAtOrInside(params.ownershipRoot, params.targetParent, "Computer Use service parent");
193
+ await assertNoSymlinkParents({
194
+ rootDir: params.ownershipRoot,
195
+ targetPath: params.targetParent,
196
+ allowMissing: true,
197
+ requireDirectories: true,
198
+ messagePrefix: "Computer Use service path"
199
+ });
200
+ await assertNotSymlink(params.targetPath, "Computer Use service target");
201
+ }
202
+ async function ensureOwnedCodexHome(codexHomeInput, ownershipRootInput = path.dirname(path.resolve(codexHomeInput))) {
203
+ const codexHome = path.resolve(codexHomeInput);
204
+ const ownershipRoot = path.resolve(ownershipRootInput);
205
+ await fs.mkdir(ownershipRoot, {
206
+ recursive: true,
207
+ mode: 448
208
+ });
209
+ await assertOwnedCodexHomePath({
210
+ ownershipRoot,
211
+ codexHome,
212
+ allowMissing: true
213
+ });
214
+ await ensureRealDirectoryTree(ownershipRoot, codexHome, "isolated Codex home");
215
+ await assertOwnedCodexHomePath({
216
+ ownershipRoot,
217
+ codexHome,
218
+ allowMissing: false
219
+ });
220
+ }
221
+ async function prepareOwnedServiceParent(params) {
222
+ await ensureOwnedCodexHome(params.codexHome, params.ownershipRoot);
223
+ await ensureRealDirectoryTree(params.ownershipRoot, params.targetParent, "Computer Use service parent");
224
+ await assertNoSymlinkParents({
225
+ rootDir: params.ownershipRoot,
226
+ targetPath: params.targetParent,
227
+ allowMissing: false,
228
+ requireDirectories: true,
229
+ messagePrefix: "Computer Use service path"
230
+ });
231
+ const [rootIdentity, parentIdentity] = await Promise.all([readRealDirectoryIdentity(params.ownershipRoot, "Computer Use ownership root"), readRealDirectoryIdentity(params.targetParent, "Computer Use service parent")]);
232
+ assertPathAtOrInside(rootIdentity.realPath, parentIdentity.realPath, "canonical Computer Use service parent");
233
+ return parentIdentity;
234
+ }
235
+ async function assertOwnedCodexHomePath(params) {
236
+ await readRealDirectoryIdentity(params.ownershipRoot, "Computer Use ownership root");
237
+ assertPathAtOrInside(params.ownershipRoot, params.codexHome, "isolated Codex home");
238
+ await assertNoSymlinkParents({
239
+ rootDir: params.ownershipRoot,
240
+ targetPath: params.codexHome,
241
+ allowMissing: params.allowMissing,
242
+ requireDirectories: true,
243
+ messagePrefix: "Computer Use service path"
244
+ });
245
+ }
246
+ async function readRealDirectoryIdentity(directoryPath, label) {
247
+ const logicalPath = path.resolve(directoryPath);
248
+ const before = await fs.lstat(logicalPath);
249
+ if (before.isSymbolicLink() || !before.isDirectory()) throw new Error(`${label} must be a real directory: ${logicalPath}`);
250
+ const realPath = await fs.realpath(logicalPath);
251
+ const [after, resolved] = await Promise.all([fs.lstat(logicalPath), fs.lstat(realPath)]);
252
+ if (after.isSymbolicLink() || !after.isDirectory() || !resolved.isDirectory() || before.dev !== after.dev || before.ino !== after.ino || after.dev !== resolved.dev || after.ino !== resolved.ino) throw new Error(`${label} changed while its ownership boundary was being established.`);
253
+ return {
254
+ logicalPath,
255
+ realPath,
256
+ dev: after.dev,
257
+ ino: after.ino
258
+ };
259
+ }
260
+ async function assertOwnedServiceParentStable(parent) {
261
+ await assertDirectoryIdentityStable(parent, "Computer Use service parent");
262
+ }
263
+ async function assertDirectoryIdentityStable(expected, label) {
264
+ if (!await directoryIdentityIsStable(expected)) throw new Error(`${label} changed during refresh; refusing to mutate the replacement path.`);
265
+ }
266
+ async function ownedServiceParentIsStable(parent) {
267
+ return await directoryIdentityIsStable(parent);
268
+ }
269
+ async function directoryIdentityIsStable(expected) {
270
+ try {
271
+ const current = await fs.lstat(expected.logicalPath);
272
+ if (current.isSymbolicLink() || !current.isDirectory() || current.dev !== expected.dev || current.ino !== expected.ino) return false;
273
+ return await fs.realpath(expected.logicalPath) === expected.realPath;
274
+ } catch {
275
+ return false;
276
+ }
277
+ }
278
+ async function assertNotSymlink(filePath, label) {
279
+ try {
280
+ if ((await fs.lstat(filePath)).isSymbolicLink()) throw new Error(`${label} must not be a symbolic link: ${filePath}`);
281
+ } catch (error) {
282
+ if (hasNodeErrorCode(error, "ENOENT")) return;
283
+ throw error;
284
+ }
285
+ }
286
+ async function ensureRealDirectoryTree(ownershipRoot, directoryPath, label) {
287
+ const root = path.resolve(ownershipRoot);
288
+ const target = path.resolve(directoryPath);
289
+ assertPathAtOrInside(root, target, label);
290
+ const relative = path.relative(root, target);
291
+ let current = root;
292
+ for (const segment of relative.split(path.sep).filter(Boolean)) {
293
+ current = path.join(current, segment);
294
+ const existing = await fs.lstat(current).catch((error) => {
295
+ if (hasNodeErrorCode(error, "ENOENT")) return;
296
+ throw error;
297
+ });
298
+ if (!existing) {
299
+ try {
300
+ await fs.mkdir(current, { mode: 448 });
301
+ } catch (error) {
302
+ if (!hasNodeErrorCode(error, "EEXIST")) throw error;
303
+ }
304
+ const created = await fs.lstat(current);
305
+ if (created.isSymbolicLink() || !created.isDirectory()) throw new Error(`${label} changed while its directory tree was being created: ${current}`);
306
+ } else if (existing.isSymbolicLink() || !existing.isDirectory()) throw new Error(`${label} must traverse real directories: ${current}`);
307
+ }
308
+ await assertNoSymlinkParents({
309
+ rootDir: root,
310
+ targetPath: target,
311
+ allowMissing: false,
312
+ requireDirectories: true,
313
+ messagePrefix: "Computer Use service path"
314
+ });
315
+ await readRealDirectoryIdentity(target, label);
316
+ }
317
+ function assertPathAtOrInside(rootPath, candidatePath, label) {
318
+ const relative = path.relative(path.resolve(rootPath), path.resolve(candidatePath));
319
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${label} must remain inside ${path.resolve(rootPath)}.`);
320
+ }
321
+ function hasNodeErrorCode(error, code) {
322
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
323
+ }
324
+ //#endregion
182
325
  //#region extensions/codex/src/app-server/computer-use-service.ts
183
326
  /** Native Computer Use service provisioning for isolated Codex homes. */
184
327
  const SERVICE_APP_NAME = "Codex Computer Use.app";
185
- const CLIENT_RELATIVE_PATH = path.join("Contents", "SharedSupport", "SkyComputerUseClient.app", "Contents", "MacOS", "SkyComputerUseClient");
328
+ const SERVICE_BUNDLE_ID = "com.openai.sky.CUAService";
329
+ const CLIENT_BUNDLE_ID = "com.openai.sky.CUAService.cli";
330
+ const OPENAI_TEAM_ID = "2DC432GLL2";
331
+ const CLIENT_APP_RELATIVE_PATH = path.join("Contents", "SharedSupport", "SkyComputerUseClient.app");
332
+ const CLIENT_RELATIVE_PATH = path.join(CLIENT_APP_RELATIVE_PATH, "Contents", "MacOS", "SkyComputerUseClient");
186
333
  const COPY_TIMEOUT_MS = 12e4;
334
+ const INSPECT_TIMEOUT_MS = 3e4;
335
+ const COMPLETED_SYNC_CACHE_MAX_ENTRIES = 64;
187
336
  const activeInstalls = /* @__PURE__ */ new Map();
188
- /** Ensures the official native client exists beneath the CODEX_HOME used by its launcher. */
337
+ const completedSyncs = /* @__PURE__ */ new Map();
338
+ /** Synchronizes the CODEX_HOME native client with the selected signed desktop distribution. */
189
339
  async function ensureCodexComputerUseServiceApp(params) {
190
340
  const platform = params.platform ?? process.platform;
191
341
  if (platform !== "darwin") return {
192
342
  status: "unsupported",
193
343
  changed: false
194
344
  };
195
- const targetPath = path.join(path.resolve(params.codexHome), "computer-use", SERVICE_APP_NAME);
345
+ const codexHome = path.resolve(params.codexHome);
346
+ const ownershipRoot = path.resolve(params.ownershipRoot ?? path.dirname(codexHome));
347
+ const targetParent = path.join(codexHome, "computer-use");
348
+ const targetPath = path.join(targetParent, SERVICE_APP_NAME);
349
+ await ensureOwnedCodexHome(codexHome, ownershipRoot);
350
+ await assertOwnedServicePath({
351
+ ownershipRoot,
352
+ codexHome,
353
+ targetParent,
354
+ targetPath
355
+ });
356
+ const candidates = params.sourceAppCandidates ?? resolveMacOSDesktopCodexComputerUseServiceAppCandidates(platform, params.appServerCommand);
357
+ 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
+ }
196
370
  const active = activeInstalls.get(targetPath);
197
- if (active) return await active;
371
+ if (active) {
372
+ if (active.syncKey === syncKey) return await active.promise;
373
+ await active.promise.catch(() => void 0);
374
+ return await ensureCodexComputerUseServiceApp(params);
375
+ }
376
+ if (completed) completedSyncs.delete(targetPath);
198
377
  const install = ensureCodexComputerUseServiceAppOnce({
199
378
  ...params,
379
+ codexHome,
380
+ ownershipRoot,
381
+ targetParent,
200
382
  targetPath,
201
- platform
383
+ platform,
384
+ 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;
202
397
  });
203
- activeInstalls.set(targetPath, install);
204
- try {
205
- return await install;
206
- } finally {
207
- if (activeInstalls.get(targetPath) === install) activeInstalls.delete(targetPath);
208
- }
398
+ const activeEntry = {
399
+ syncKey,
400
+ promise: install
401
+ };
402
+ activeInstalls.set(targetPath, activeEntry);
403
+ const clearActive = () => {
404
+ if (activeInstalls.get(targetPath) === activeEntry) activeInstalls.delete(targetPath);
405
+ };
406
+ install.then(clearActive, clearActive);
407
+ return await install;
408
+ }
409
+ function isCompletedSyncStatus(status) {
410
+ return status === "installed" || status === "refreshed" || status === "already_current";
209
411
  }
210
412
  async function ensureCodexComputerUseServiceAppOnce(params) {
211
- if (await hasExecutableClient(params.targetPath)) return {
212
- status: "already_installed",
413
+ const inspectServiceApp = params.inspectServiceApp ?? inspectTrustedServiceApp;
414
+ const source = await findUsableServiceApp(params.sourceAppCandidates ?? [], inspectServiceApp);
415
+ if (!source) return {
416
+ status: "source_missing",
213
417
  changed: false,
214
418
  targetPath: params.targetPath
215
419
  };
216
- const sourcePath = await findUsableServiceApp(params.sourceAppCandidates ?? resolveMacOSDesktopCodexComputerUseServiceAppCandidates(params.platform, params.appServerCommand));
217
- if (!sourcePath) return {
218
- status: "source_missing",
420
+ const { path: sourcePath, identity: sourceIdentity } = source;
421
+ const ownedParent = await prepareOwnedServiceParent({
422
+ ownershipRoot: params.ownershipRoot,
423
+ codexHome: params.codexHome,
424
+ targetParent: params.targetParent
425
+ });
426
+ const operationTargetPath = path.join(ownedParent.realPath, SERVICE_APP_NAME);
427
+ await assertNotSymlink(operationTargetPath, "Computer Use service target");
428
+ const initialTarget = await readServiceAppSnapshot(operationTargetPath, inspectServiceApp);
429
+ if (initialTarget.identity && identitiesMatch(initialTarget.identity, sourceIdentity)) return {
430
+ status: "already_current",
219
431
  changed: false,
220
- targetPath: params.targetPath
432
+ targetPath: params.targetPath,
433
+ sourcePath,
434
+ sourceBuild: sourceIdentity.build
221
435
  };
222
- const targetParent = path.dirname(params.targetPath);
223
- await fs.mkdir(targetParent, { recursive: true });
224
- const stagingRoot = await fs.mkdtemp(path.join(targetParent, ".service-app.staging-"));
436
+ await assertOwnedServiceParentStable(ownedParent);
437
+ const stagingRoot = await fs.mkdtemp(path.join(ownedParent.realPath, ".service-app.staging-"));
438
+ const stagingRootIdentity = await readRealDirectoryIdentity(stagingRoot, "Computer Use service staging directory");
225
439
  const stagedPath = path.join(stagingRoot, SERVICE_APP_NAME);
226
- const backupPath = path.join(targetParent, `.service-app.backup-${process.pid}-${Date.now()}`);
440
+ const backupPath = path.join(ownedParent.realPath, `.service-app.backup-${process.pid}-${Date.now()}`);
227
441
  let backupCreated = false;
228
442
  try {
229
443
  await (params.copyServiceApp ?? copyServiceAppWithDitto)(sourcePath, stagedPath);
230
- if (!await hasExecutableClient(stagedPath)) throw new Error(`Copied Computer Use service app is incomplete at ${stagedPath}.`);
231
- if (await pathExists$1(params.targetPath)) {
232
- await fs.rename(params.targetPath, backupPath);
444
+ await assertOwnedServiceParentStable(ownedParent);
445
+ await assertDirectoryIdentityStable(stagingRootIdentity, "Computer Use service staging directory");
446
+ await assertNotSymlink(stagedPath, "Copied Computer Use service app");
447
+ const stagedIdentity = await inspectServiceApp(stagedPath);
448
+ if (!stagedIdentity || !identitiesMatch(stagedIdentity, sourceIdentity)) throw new Error(`Copied Computer Use service app at ${stagedPath} does not match its selected signed source.`);
449
+ const stagedSnapshot = await readServiceAppSnapshot(stagedPath, inspectServiceApp);
450
+ const currentSourceIdentity = await inspectServiceApp(sourcePath);
451
+ await assertOwnedServiceParentStable(ownedParent);
452
+ if (!currentSourceIdentity || !identitiesMatch(currentSourceIdentity, sourceIdentity)) throw new Error("Selected Computer Use service source changed during refresh.");
453
+ await assertNotSymlink(operationTargetPath, "Computer Use service target");
454
+ if (await pathExists$1(operationTargetPath)) {
455
+ await assertOwnedServiceParentStable(ownedParent);
456
+ await fs.rename(operationTargetPath, backupPath);
457
+ await assertOwnedServiceParentStable(ownedParent);
233
458
  backupCreated = true;
234
- if (await hasExecutableClient(backupPath)) {
235
- await fs.rename(backupPath, params.targetPath);
459
+ const movedTarget = await readServiceAppSnapshot(backupPath, inspectServiceApp);
460
+ if (movedTarget.identity && identitiesMatch(movedTarget.identity, sourceIdentity)) {
461
+ await assertOwnedServiceParentStable(ownedParent);
462
+ await fs.rename(backupPath, operationTargetPath);
463
+ await assertOwnedServiceParentStable(ownedParent);
236
464
  backupCreated = false;
237
465
  return {
238
- status: "already_installed",
466
+ status: "already_current",
239
467
  changed: false,
240
468
  targetPath: params.targetPath,
241
- sourcePath
469
+ sourcePath,
470
+ sourceBuild: sourceIdentity.build
242
471
  };
243
472
  }
473
+ if (!snapshotsMatch(initialTarget, movedTarget)) {
474
+ await assertOwnedServiceParentStable(ownedParent);
475
+ await fs.rename(backupPath, operationTargetPath);
476
+ await assertOwnedServiceParentStable(ownedParent);
477
+ backupCreated = false;
478
+ throw new Error("Computer Use service target changed to an unexpected generation during refresh.");
479
+ }
244
480
  }
245
481
  try {
246
- await fs.rename(stagedPath, params.targetPath);
482
+ await assertOwnedServiceParentStable(ownedParent);
483
+ await fs.rename(stagedPath, operationTargetPath);
484
+ await assertOwnedServiceParentStable(ownedParent);
247
485
  } catch (error) {
248
- if (!await hasExecutableClient(params.targetPath)) {
486
+ await assertOwnedServiceParentStable(ownedParent);
487
+ await assertNotSymlink(operationTargetPath, "Computer Use service target");
488
+ const winnerIdentity = await inspectServiceApp(operationTargetPath);
489
+ if (!winnerIdentity || !identitiesMatch(winnerIdentity, sourceIdentity)) {
249
490
  if (backupCreated) {
250
- await fs.rename(backupPath, params.targetPath);
251
- backupCreated = false;
491
+ if (!await pathExists$1(operationTargetPath)) {
492
+ await assertOwnedServiceParentStable(ownedParent);
493
+ await fs.rename(backupPath, operationTargetPath);
494
+ await assertOwnedServiceParentStable(ownedParent);
495
+ backupCreated = false;
496
+ }
252
497
  }
253
498
  throw error;
254
499
  }
255
500
  if (backupCreated) {
501
+ await assertOwnedServiceParentStable(ownedParent);
502
+ await assertNotSymlink(backupPath, "Computer Use service backup");
256
503
  await fs.rm(backupPath, {
257
504
  recursive: true,
258
505
  force: true
259
506
  });
507
+ await assertOwnedServiceParentStable(ownedParent);
260
508
  backupCreated = false;
261
509
  }
262
510
  return {
263
- status: "already_installed",
511
+ status: "already_current",
264
512
  changed: false,
265
513
  targetPath: params.targetPath,
266
- sourcePath
514
+ sourcePath,
515
+ sourceBuild: sourceIdentity.build
267
516
  };
268
517
  }
518
+ await assertNotSymlink(operationTargetPath, "Installed Computer Use service app");
519
+ const installedSnapshot = await readServiceAppSnapshot(operationTargetPath, inspectServiceApp);
520
+ if (!installedSnapshot.identity || !identitiesMatch(installedSnapshot.identity, sourceIdentity)) {
521
+ if (filesystemSnapshotsMatch(installedSnapshot, stagedSnapshot)) {
522
+ await assertOwnedServiceParentStable(ownedParent);
523
+ await fs.rm(operationTargetPath, {
524
+ recursive: true,
525
+ force: true
526
+ });
527
+ await assertOwnedServiceParentStable(ownedParent);
528
+ if (backupCreated) {
529
+ await fs.rename(backupPath, operationTargetPath);
530
+ await assertOwnedServiceParentStable(ownedParent);
531
+ backupCreated = false;
532
+ }
533
+ }
534
+ throw new Error("Installed Computer Use service app failed post-install identity verification.");
535
+ }
269
536
  if (backupCreated) {
537
+ await assertOwnedServiceParentStable(ownedParent);
538
+ await assertNotSymlink(backupPath, "Computer Use service backup");
270
539
  await fs.rm(backupPath, {
271
540
  recursive: true,
272
541
  force: true
273
542
  });
543
+ await assertOwnedServiceParentStable(ownedParent);
274
544
  backupCreated = false;
275
545
  }
276
546
  return {
277
- status: "installed",
547
+ status: initialTarget.exists ? "refreshed" : "installed",
278
548
  changed: true,
279
549
  targetPath: params.targetPath,
280
- sourcePath
550
+ sourcePath,
551
+ sourceBuild: sourceIdentity.build,
552
+ ...initialTarget.identity ? { previousBuild: initialTarget.identity.build } : {}
281
553
  };
554
+ } catch (error) {
555
+ if (backupCreated && await ownedServiceParentIsStable(ownedParent) && !await pathExists$1(operationTargetPath)) {
556
+ await assertOwnedServiceParentStable(ownedParent);
557
+ await fs.rename(backupPath, operationTargetPath);
558
+ backupCreated = false;
559
+ }
560
+ throw error;
282
561
  } finally {
283
- await fs.rm(stagingRoot, {
562
+ if (await ownedServiceParentIsStable(ownedParent) && await directoryIdentityIsStable(stagingRootIdentity)) await fs.rm(stagingRoot, {
284
563
  recursive: true,
285
564
  force: true
286
565
  });
@@ -289,8 +568,43 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
289
568
  async function pathExists$1(filePath) {
290
569
  return await fs.lstat(filePath).then(() => true, () => false);
291
570
  }
292
- async function findUsableServiceApp(candidates) {
293
- for (const candidate of candidates) if (await hasExecutableClient(candidate)) return candidate;
571
+ async function findUsableServiceApp(candidates, inspectServiceApp) {
572
+ for (const candidate of candidates) {
573
+ const identity = await inspectServiceApp(candidate);
574
+ if (identity) return {
575
+ path: candidate,
576
+ identity
577
+ };
578
+ }
579
+ }
580
+ async function readServiceAppSnapshot(appPath, inspectServiceApp) {
581
+ if (!await pathExists$1(appPath)) return { exists: false };
582
+ return {
583
+ exists: true,
584
+ identity: await inspectServiceApp(appPath),
585
+ filesystemKey: await readServiceAppFilesystemKey(appPath)
586
+ };
587
+ }
588
+ async function readServiceAppFilesystemKey(appPath) {
589
+ const paths = [
590
+ appPath,
591
+ path.join(appPath, "Contents", "Info.plist"),
592
+ path.join(appPath, CLIENT_RELATIVE_PATH)
593
+ ];
594
+ return (await Promise.all(paths.map(async (entryPath) => await fs.stat(entryPath).then((stat) => `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeMs}`, () => "missing")))).join("|");
595
+ }
596
+ function snapshotsMatch(left, right) {
597
+ if (left.exists !== right.exists) return false;
598
+ if (!left.exists) return true;
599
+ if (left.filesystemKey && right.filesystemKey && left.filesystemKey !== right.filesystemKey) return false;
600
+ if (left.identity || right.identity) return Boolean(left.identity && right.identity && identitiesMatch(left.identity, right.identity));
601
+ return left.filesystemKey !== void 0 && left.filesystemKey === right.filesystemKey;
602
+ }
603
+ function filesystemSnapshotsMatch(left, right) {
604
+ return Boolean(left.exists && right.exists && left.filesystemKey && right.filesystemKey && left.filesystemKey === right.filesystemKey);
605
+ }
606
+ function identitiesMatch(left, right) {
607
+ return left.bundleId === right.bundleId && left.version === right.version && left.build === right.build && left.cdHash === right.cdHash && left.teamId === right.teamId && left.clientBundleId === right.clientBundleId && left.clientCdHash === right.clientCdHash && left.clientTeamId === right.clientTeamId;
294
608
  }
295
609
  async function hasExecutableClient(appPath) {
296
610
  try {
@@ -300,6 +614,90 @@ async function hasExecutableClient(appPath) {
300
614
  return false;
301
615
  }
302
616
  }
617
+ async function inspectTrustedServiceApp(appPath) {
618
+ if (!await hasExecutableClient(appPath)) return;
619
+ const clientAppPath = path.join(appPath, CLIENT_APP_RELATIVE_PATH);
620
+ try {
621
+ await verifyTrustedBundle(appPath, SERVICE_BUNDLE_ID, true);
622
+ await verifyTrustedBundle(clientAppPath, CLIENT_BUNDLE_ID, false);
623
+ const [info, serviceSignature, clientSignature] = await Promise.all([
624
+ readBundleInfo(appPath),
625
+ readCodeSignature(appPath),
626
+ readCodeSignature(clientAppPath)
627
+ ]);
628
+ if (!info || serviceSignature.identifier !== SERVICE_BUNDLE_ID || serviceSignature.teamId !== OPENAI_TEAM_ID || clientSignature.identifier !== CLIENT_BUNDLE_ID || clientSignature.teamId !== OPENAI_TEAM_ID) return;
629
+ return {
630
+ bundleId: serviceSignature.identifier,
631
+ version: info.version,
632
+ build: info.build,
633
+ cdHash: serviceSignature.cdHash,
634
+ teamId: serviceSignature.teamId,
635
+ clientBundleId: clientSignature.identifier,
636
+ clientCdHash: clientSignature.cdHash,
637
+ clientTeamId: clientSignature.teamId
638
+ };
639
+ } catch {
640
+ return;
641
+ }
642
+ }
643
+ async function verifyTrustedBundle(appPath, bundleId, deep) {
644
+ const requirement = `anchor apple generic and certificate leaf[subject.OU] = "${OPENAI_TEAM_ID}" and identifier "${bundleId}"`;
645
+ await runExec("/usr/bin/codesign", [
646
+ "--verify",
647
+ "--strict",
648
+ ...deep ? ["--deep"] : [],
649
+ `-R=${requirement}`,
650
+ appPath
651
+ ], {
652
+ logOutput: false,
653
+ timeoutMs: INSPECT_TIMEOUT_MS
654
+ });
655
+ }
656
+ async function readBundleInfo(appPath) {
657
+ const result = await runExec("/usr/bin/plutil", [
658
+ "-convert",
659
+ "json",
660
+ "-o",
661
+ "-",
662
+ "--",
663
+ path.join(appPath, "Contents", "Info.plist")
664
+ ], {
665
+ logOutput: false,
666
+ timeoutMs: INSPECT_TIMEOUT_MS
667
+ });
668
+ const parsed = JSON.parse(result.stdout);
669
+ if (!isRecord(parsed)) return;
670
+ const version = parsed.CFBundleShortVersionString;
671
+ const build = parsed.CFBundleVersion;
672
+ return typeof version === "string" && version && typeof build === "string" && build ? {
673
+ version,
674
+ build
675
+ } : void 0;
676
+ }
677
+ async function readCodeSignature(appPath) {
678
+ const result = await runExec("/usr/bin/codesign", [
679
+ "-d",
680
+ "--verbose=4",
681
+ appPath
682
+ ], {
683
+ logOutput: false,
684
+ timeoutMs: INSPECT_TIMEOUT_MS
685
+ });
686
+ const output = `${result.stdout}\n${result.stderr}`;
687
+ const identifier = readCodeSignField(output, "Identifier");
688
+ const teamId = readCodeSignField(output, "TeamIdentifier");
689
+ const cdHash = readCodeSignField(output, "CDHash").toLowerCase();
690
+ if (!identifier || !teamId || !/^[a-f0-9]+$/.test(cdHash)) throw new Error(`Could not inspect the signed identity at ${appPath}.`);
691
+ return {
692
+ identifier,
693
+ teamId,
694
+ cdHash
695
+ };
696
+ }
697
+ function readCodeSignField(output, field) {
698
+ const prefix = `${field}=`;
699
+ return output.split(/\r?\n/u).find((candidate) => candidate.startsWith(prefix))?.slice(prefix.length).trim() ?? "";
700
+ }
303
701
  async function copyServiceAppWithDitto(sourcePath, targetPath) {
304
702
  await runExec("/usr/bin/ditto", [
305
703
  "--noqtn",
@@ -458,7 +856,8 @@ async function resolveCodexAppServerPreparedAuthProfileSnapshot(params) {
458
856
  async function resolveCodexAppServerPreparedAuthHandoff(params) {
459
857
  const usesNativeHome = params.homeScope === "user";
460
858
  if (params.requirePreparedAuth && usesNativeHome) throw createCodexAppServerAuthError("Codex remote-exec cloud placement requires prepared OpenAI auth. Configure an OpenAI API-key, OAuth, or token profile and use appServer.homeScope=\"agent\"; ambient credentials and native Codex auth are not allowed.");
461
- if (params.authRequirement === "api-key" && !usesNativeHome) {
859
+ if (usesNativeHome) return { nativeAuthProfile: true };
860
+ if (params.authRequirement === "api-key") {
462
861
  const apiKey = params.resolvedApiKey?.trim();
463
862
  if (!apiKey) throw new Error("Prepared Codex API-key route is missing its resolved API key.");
464
863
  return {
@@ -476,7 +875,7 @@ async function resolveCodexAppServerPreparedAuthHandoff(params) {
476
875
  agentDir: params.agentDir,
477
876
  config: params.config
478
877
  });
479
- if (usesNativeHome || params.authRequirement !== "subscription" && !params.requirePreparedAuth) return {
878
+ if (params.authRequirement !== "subscription" && !params.requirePreparedAuth) return {
480
879
  authProfileId,
481
880
  nativeAuthProfile
482
881
  };
@@ -584,9 +983,11 @@ async function withCodexHomeEnvironment(startOptions, agentDir, pluginConfig) {
584
983
  codexHome,
585
984
  config: computerUseConfig
586
985
  });
587
- if (computerUseConfig.enabled && computerUseConfig.autoInstall) try {
986
+ const ownsIsolatedCodexHome = startOptions.homeScope !== "user" && !startOptions.env?.[CODEX_HOME_ENV_VAR]?.trim();
987
+ if (computerUseConfig.enabled && computerUseConfig.autoInstall && ownsIsolatedCodexHome) try {
588
988
  await ensureCodexComputerUseServiceApp({
589
989
  codexHome,
990
+ ownershipRoot: agentDir,
590
991
  appServerCommand: startOptions.command
591
992
  });
592
993
  } catch (error) {
@@ -1105,12 +1506,12 @@ function ensureCodexAppServerClientRuntime(client, context) {
1105
1506
  client.addRequestHandler(async (request) => {
1106
1507
  if (request.method !== "account/chatgptAuthTokens/refresh") return;
1107
1508
  if (runtime.context.authMode === "prepared-api-key") throw new Error("ChatGPT token refresh is unavailable for prepared Codex API-key auth.");
1108
- return await refreshCodexAppServerAuthTokens({
1509
+ return { ...await refreshCodexAppServerAuthTokens({
1109
1510
  agentDir: runtime.context.agentDir,
1110
1511
  authProfileId: runtime.context.authProfileId,
1111
1512
  ...runtime.context.authProfileStore ? { authProfileStore: runtime.context.authProfileStore } : {},
1112
1513
  config: runtime.context.config
1113
- });
1514
+ }) };
1114
1515
  });
1115
1516
  client.addNotificationHandler((notification) => {
1116
1517
  if (notification.method === "account/rateLimits/updated") {
@@ -1362,6 +1763,15 @@ function protectCodexAppServerLiveThread(client, threadId) {
1362
1763
  };
1363
1764
  }
1364
1765
  //#endregion
1766
+ //#region extensions/codex/src/app-server/elicitation-response.ts
1767
+ function createCodexElicitationResponse(action, content = null, meta = null) {
1768
+ return {
1769
+ action,
1770
+ content,
1771
+ _meta: meta
1772
+ };
1773
+ }
1774
+ //#endregion
1365
1775
  //#region extensions/codex/src/app-server/rpc-error.ts
1366
1776
  /** RPC error wrapper that preserves app-server error code and data. */
1367
1777
  var CodexAppServerRpcError = class extends Error {
@@ -1558,11 +1968,273 @@ function websocketFrameToText(data) {
1558
1968
  return Buffer.from(data).toString("utf8");
1559
1969
  }
1560
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
1561
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();
1562
2208
  /** Starts graceful transport shutdown and schedules a force kill fallback. */
1563
2209
  function closeCodexAppServerTransport(child, options = {}) {
1564
- child.stdin.end?.();
1565
- child.stdin.destroy?.();
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) {
1566
2238
  const forceKillDelayMs = options.forceKillDelayMs ?? 1e3;
1567
2239
  const forceKill = setTimeout(() => {
1568
2240
  if (hasCodexAppServerTransportExited(child)) return;
@@ -1574,6 +2246,12 @@ function closeCodexAppServerTransport(child, options = {}) {
1574
2246
  child.stdout.destroy?.();
1575
2247
  child.stderr.destroy?.();
1576
2248
  });
2249
+ try {
2250
+ child.stdin.end?.();
2251
+ child.stdin.destroy?.();
2252
+ } finally {
2253
+ resumeRoot?.();
2254
+ }
1577
2255
  child.unref?.();
1578
2256
  child.stdout.unref?.();
1579
2257
  child.stderr.unref?.();
@@ -1581,7 +2259,7 @@ function closeCodexAppServerTransport(child, options = {}) {
1581
2259
  }
1582
2260
  /** Closes a transport and waits briefly for an exit event. */
1583
2261
  async function closeCodexAppServerTransportAndWait(child, options = {}) {
1584
- if (!hasCodexAppServerTransportExited(child)) closeCodexAppServerTransport(child, options);
2262
+ if (!hasCodexAppServerTransportExited(child)) await beginCodexAppServerTransportClose(child, options);
1585
2263
  return await waitForCodexAppServerTransportExit(child, options.exitTimeoutMs ?? 2e3);
1586
2264
  }
1587
2265
  function hasCodexAppServerTransportExited(child) {
@@ -1614,15 +2292,6 @@ function signalCodexAppServerTransport(child, signal) {
1614
2292
  child.kill?.(signal);
1615
2293
  }
1616
2294
  //#endregion
1617
- //#region extensions/codex/src/app-server/version.ts
1618
- /**
1619
- * Version and package pins for the managed Codex app-server runtime.
1620
- */
1621
- /** Exact Codex app-server version shipped and supported by the OpenClaw Codex bridge. */
1622
- const CODEX_APP_SERVER_VERSION = "0.147.0";
1623
- /** npm package name for the managed Codex app-server binary. */
1624
- const MANAGED_CODEX_APP_SERVER_PACKAGE = "@openai/codex";
1625
- //#endregion
1626
2295
  //#region extensions/codex/src/app-server/client.ts
1627
2296
  /**
1628
2297
  * JSON-RPC client for Codex app-server transports, including request/response
@@ -1756,7 +2425,13 @@ var CodexAppServerClient = class CodexAppServerClient {
1756
2425
  title: "OpenClaw",
1757
2426
  version: OPENCLAW_VERSION
1758
2427
  },
1759
- capabilities: { experimentalApi: true }
2428
+ capabilities: {
2429
+ experimentalApi: true,
2430
+ extensions: {
2431
+ "openai/standard-form-input": {},
2432
+ "openai/form": {}
2433
+ }
2434
+ }
1760
2435
  });
1761
2436
  this.serverVersion = assertSupportedCodexAppServerVersion(response);
1762
2437
  this.runtimeIdentity = buildCodexAppServerRuntimeIdentity(response, this.serverVersion);
@@ -2206,7 +2881,7 @@ function defaultServerRequestResponse(request) {
2206
2881
  scope: "turn"
2207
2882
  };
2208
2883
  if (request.method === "item/tool/requestUserInput") return { answers: {} };
2209
- if (request.method === "mcpServer/elicitation/request") return { action: "decline" };
2884
+ if (request.method === "mcpServer/elicitation/request") return createCodexElicitationResponse("decline", null, { message: "OpenClaw has no interactive handler for this elicitation." });
2210
2885
  return {};
2211
2886
  }
2212
2887
  function stringifyCodexAppServerMessage(message) {
@@ -2226,14 +2901,20 @@ function timeoutServerRequestResponse(request) {
2226
2901
  var CodexAppServerVersionError = class extends Error {
2227
2902
  constructor(detectedVersion) {
2228
2903
  const detected = detectedVersion ? `detected ${detectedVersion}` : "OpenClaw could not determine the running Codex version";
2229
- super(`Codex app-server ${CODEX_APP_SERVER_VERSION} is required, but ${detected}. Update the configured Codex app-server binary, or remove custom command overrides to use the managed binary.`);
2904
+ super(`Codex app-server ${MIN_SUPPORTED_CODEX_APP_SERVER_VERSION} or newer is required, but ${detected}. Update the configured Codex app-server binary, or remove custom command overrides to use the managed binary.`);
2230
2905
  this.name = "CodexAppServerVersionError";
2231
2906
  this.detectedVersion = detectedVersion;
2232
2907
  }
2233
2908
  };
2234
2909
  function assertSupportedCodexAppServerVersion(response) {
2235
2910
  const detectedVersion = readCodexVersionFromUserAgent(response.userAgent);
2236
- if (detectedVersion !== "0.147.0") throw new CodexAppServerVersionError(detectedVersion);
2911
+ if (!detectedVersion) throw new CodexAppServerVersionError(detectedVersion);
2912
+ 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", {
2915
+ detectedVersion,
2916
+ validatedVersion: CODEX_APP_SERVER_VERSION
2917
+ });
2237
2918
  return detectedVersion;
2238
2919
  }
2239
2920
  function isUnsupportedCodexAppServerVersionError(error) {
@@ -2397,206 +3078,6 @@ function resolveCodexGatewayTimeoutWithGraceMs(timeoutMs, graceMs = 1e4) {
2397
3078
  return addTimerTimeoutGraceMs(timeout, resolveTimerTimeoutMs(graceMs, 0, 0)) ?? timeout;
2398
3079
  }
2399
3080
  //#endregion
2400
- //#region extensions/codex/src/app-server/managed-binary.ts
2401
- /**
2402
- * Resolves the managed Codex app-server binary shipped with or installed beside
2403
- * the Codex plugin before stdio startup.
2404
- */
2405
- const CODEX_PLUGIN_ROOT = resolveDefaultCodexPluginRoot(path.dirname(fileURLToPath(import.meta.url)));
2406
- const MACOS_DESKTOP_CODEX_APP_SERVER_COMMANDS = ["/Applications/ChatGPT.app/Contents/Resources/codex", "/Applications/Codex.app/Contents/Resources/codex"];
2407
- /** Rewrites managed stdio start options to point at an executable Codex binary path. */
2408
- async function resolveManagedCodexAppServerStartOptions(startOptions, options = {}) {
2409
- if (startOptions.transport !== "stdio" || startOptions.commandSource !== "managed") return startOptions;
2410
- const platform = options.platform ?? process.platform;
2411
- const paths = resolveManagedCodexAppServerPaths({
2412
- platform,
2413
- pluginRoot: options.pluginRoot,
2414
- managedCommandOrder: startOptions.managedCommandOrder
2415
- });
2416
- const pathExists = options.pathExists ?? commandPathExists;
2417
- const commandPaths = await findManagedCodexAppServerCommandPaths({
2418
- candidateCommandPaths: paths.candidateCommandPaths,
2419
- pathExists,
2420
- platform
2421
- });
2422
- const commandPath = expectDefined(commandPaths[0], "resolved managed Codex command path");
2423
- const managedFallbackCommandPaths = commandPaths.slice(1);
2424
- return {
2425
- ...startOptions,
2426
- command: commandPath,
2427
- commandSource: "resolved-managed",
2428
- ...managedFallbackCommandPaths.length > 0 ? { managedFallbackCommandPaths } : {}
2429
- };
2430
- }
2431
- /** Resolves the native artifact behind a successful managed launcher selection. */
2432
- function resolveManagedCodexNativeCommand(command, options = {}) {
2433
- const platform = options.platform ?? process.platform;
2434
- if (platform === "darwin" && MACOS_DESKTOP_CODEX_APP_SERVER_COMMANDS.some((candidate) => candidate === command)) return command;
2435
- const target = resolveCodexNativeTarget(platform, options.arch ?? process.arch);
2436
- if (!target) return;
2437
- const packageRoot = resolveManagedCodexPackageRootForCommand(command, platform);
2438
- if (!packageRoot) return;
2439
- const resolvePackageJson = options.resolvePackageJson ?? resolvePackageJsonFromRoot;
2440
- const pathExists = options.pathExists ?? existsSync;
2441
- for (const packageName of [target.packageName, MANAGED_CODEX_APP_SERVER_PACKAGE]) {
2442
- const packageJsonPath = resolvePackageJson(packageName, packageRoot);
2443
- if (!packageJsonPath) continue;
2444
- const candidate = path.join(path.dirname(packageJsonPath), "vendor", target.triple, "bin", platform === "win32" ? "codex.exe" : "codex");
2445
- if (pathExists(candidate)) return candidate;
2446
- }
2447
- }
2448
- function resolveManagedCodexPackageRootForCommand(command, platform) {
2449
- const pathApi = pathForPlatform(platform);
2450
- const commandPaths = [command];
2451
- try {
2452
- commandPaths.unshift(realpathSync(command));
2453
- } catch {}
2454
- for (const commandPath of commandPaths) {
2455
- let current = pathApi.dirname(commandPath);
2456
- while (true) {
2457
- if (pathApi.basename(current) === "codex" && pathApi.basename(pathApi.dirname(current)) === "@openai") return current;
2458
- if (pathApi.basename(current) === ".bin") return pathApi.join(pathApi.dirname(current), "@openai", "codex");
2459
- const parent = pathApi.dirname(current);
2460
- if (parent === current) break;
2461
- current = parent;
2462
- }
2463
- }
2464
- }
2465
- function resolveCodexNativeTarget(platform, arch) {
2466
- if ((platform === "linux" || platform === "android") && arch === "x64") return {
2467
- packageName: "@openai/codex-linux-x64",
2468
- triple: "x86_64-unknown-linux-musl"
2469
- };
2470
- if ((platform === "linux" || platform === "android") && arch === "arm64") return {
2471
- packageName: "@openai/codex-linux-arm64",
2472
- triple: "aarch64-unknown-linux-musl"
2473
- };
2474
- if (platform === "darwin" && arch === "x64") return {
2475
- packageName: "@openai/codex-darwin-x64",
2476
- triple: "x86_64-apple-darwin"
2477
- };
2478
- if (platform === "darwin" && arch === "arm64") return {
2479
- packageName: "@openai/codex-darwin-arm64",
2480
- triple: "aarch64-apple-darwin"
2481
- };
2482
- if (platform === "win32" && arch === "x64") return {
2483
- packageName: "@openai/codex-win32-x64",
2484
- triple: "x86_64-pc-windows-msvc"
2485
- };
2486
- if (platform === "win32" && arch === "arm64") return {
2487
- packageName: "@openai/codex-win32-arm64",
2488
- triple: "aarch64-pc-windows-msvc"
2489
- };
2490
- }
2491
- function resolvePackageJsonFromRoot(packageName, root) {
2492
- try {
2493
- return createRequire(path.join(root, "package.json")).resolve(`${packageName}/package.json`);
2494
- } catch {
2495
- return;
2496
- }
2497
- }
2498
- /** Returns the preferred and fallback managed Codex binary paths for a plugin root. */
2499
- function resolveManagedCodexAppServerPaths(params) {
2500
- const platform = params.platform ?? process.platform;
2501
- const candidateCommandPaths = resolveManagedCodexAppServerCommandCandidates(params.pluginRoot ?? CODEX_PLUGIN_ROOT, platform, params.managedCommandOrder ?? "package-first");
2502
- return {
2503
- commandPath: candidateCommandPaths[0] ?? "",
2504
- candidateCommandPaths
2505
- };
2506
- }
2507
- function resolveManagedCodexAppServerCommandCandidates(pluginRoot, platform, managedCommandOrder) {
2508
- const pathApi = pathForPlatform(platform);
2509
- const commandName = platform === "win32" ? "codex.cmd" : "codex";
2510
- const roots = resolveManagedCodexAppServerCandidateRoots(pluginRoot, platform);
2511
- const packageCommandPaths = [...roots.map((root) => pathApi.join(root, "node_modules", ".bin", commandName)), ...resolveManagedCodexPackageBinCandidates(roots, platform)];
2512
- const desktopCommandPaths = resolveDesktopCodexAppServerCommandCandidates(platform);
2513
- const orderedCommandPaths = managedCommandOrder === "desktop-first" ? [...desktopCommandPaths, ...packageCommandPaths] : [...packageCommandPaths, ...desktopCommandPaths];
2514
- return [...new Set(orderedCommandPaths)];
2515
- }
2516
- function resolveDesktopCodexAppServerCommandCandidates(platform) {
2517
- return platform === "darwin" ? [...MACOS_DESKTOP_CODEX_APP_SERVER_COMMANDS] : [];
2518
- }
2519
- function resolveDefaultCodexPluginRoot(moduleDir) {
2520
- const moduleBaseName = path.basename(moduleDir);
2521
- if (moduleBaseName === "dist" || moduleBaseName === "dist-runtime") return path.dirname(moduleDir);
2522
- return path.resolve(moduleDir, "..", "..");
2523
- }
2524
- function resolveManagedCodexAppServerCandidateRoots(pluginRoot, platform) {
2525
- const pathApi = pathForPlatform(platform);
2526
- const directRoots = [
2527
- pluginRoot,
2528
- pathApi.dirname(pluginRoot),
2529
- pathApi.dirname(pathApi.dirname(pluginRoot)),
2530
- isDistExtensionRoot(pluginRoot, platform) ? pathApi.dirname(pathApi.dirname(pathApi.dirname(pluginRoot))) : null
2531
- ].filter((root) => Boolean(root));
2532
- return [.../* @__PURE__ */ new Set([...directRoots, ...resolveNearestNodeModulesProjectRoots(directRoots, platform)])];
2533
- }
2534
- function resolveNearestNodeModulesProjectRoots(roots, platform) {
2535
- const pathApi = pathForPlatform(platform);
2536
- const projectRoots = [];
2537
- for (const root of roots) {
2538
- let current = pathApi.resolve(root);
2539
- while (true) {
2540
- if (pathApi.basename(current) === "node_modules") {
2541
- projectRoots.push(pathApi.dirname(current));
2542
- break;
2543
- }
2544
- const parent = pathApi.dirname(current);
2545
- if (parent === current) break;
2546
- current = parent;
2547
- }
2548
- }
2549
- return projectRoots;
2550
- }
2551
- function resolveManagedCodexPackageBinCandidates(roots, platform) {
2552
- if (platform === "win32") return [];
2553
- const candidates = [];
2554
- for (const root of roots) {
2555
- const candidate = resolveManagedCodexPackageBinCandidate(root);
2556
- if (candidate) candidates.push(candidate);
2557
- }
2558
- return candidates;
2559
- }
2560
- function resolveManagedCodexPackageBinCandidate(root) {
2561
- try {
2562
- const packageJsonPath = createRequire(path.join(root, "package.json")).resolve(`${MANAGED_CODEX_APP_SERVER_PACKAGE}/package.json`);
2563
- const packageRoot = path.dirname(packageJsonPath);
2564
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
2565
- const packageBin = packageJson.bin && typeof packageJson.bin === "object" ? packageJson.bin : void 0;
2566
- const binPath = typeof packageJson.bin === "string" ? packageJson.bin : typeof packageBin?.codex === "string" ? packageBin.codex : null;
2567
- return binPath ? path.resolve(packageRoot, binPath) : null;
2568
- } catch {
2569
- return null;
2570
- }
2571
- }
2572
- function isDistExtensionRoot(pluginRoot, platform) {
2573
- const pathApi = pathForPlatform(platform);
2574
- const extensionsDir = pathApi.dirname(pluginRoot);
2575
- const distDir = pathApi.dirname(extensionsDir);
2576
- return pathApi.basename(extensionsDir) === "extensions" && (pathApi.basename(distDir) === "dist" || pathApi.basename(distDir) === "dist-runtime");
2577
- }
2578
- function pathForPlatform(platform) {
2579
- return platform === "win32" ? path.win32 : path.posix;
2580
- }
2581
- async function findManagedCodexAppServerCommandPaths(params) {
2582
- const commandPaths = [];
2583
- for (const commandPath of params.candidateCommandPaths) if (await params.pathExists(commandPath, params.platform)) commandPaths.push(commandPath);
2584
- if (commandPaths.length > 0) return commandPaths;
2585
- throw new Error([
2586
- `Managed Codex app-server binary was not found for ${MANAGED_CODEX_APP_SERVER_PACKAGE}.`,
2587
- "Reinstall or update OpenClaw, or run pnpm install in a source checkout.",
2588
- "Set plugins.entries.codex.config.appServer.command or OPENCLAW_CODEX_APP_SERVER_BIN to use a custom Codex binary."
2589
- ].join(" "));
2590
- }
2591
- async function commandPathExists(filePath, platform) {
2592
- try {
2593
- await access(filePath, platform === "win32" ? constants.F_OK : constants.X_OK);
2594
- return true;
2595
- } catch {
2596
- return false;
2597
- }
2598
- }
2599
- //#endregion
2600
3081
  //#region extensions/codex/src/app-server/native-config-fence.ts
2601
3082
  const CODEX_NATIVE_CONFIG_FENCE_STATE = Symbol.for("openclaw.codexNativeConfigFenceState");
2602
3083
  function getFenceState() {
@@ -3088,18 +3569,19 @@ function createSharedCodexAppServerClientStartup(params) {
3088
3569
  onStartedClient: (startedClient) => {
3089
3570
  const state = getSharedCodexAppServerClientState();
3090
3571
  params.entry.client = startedClient;
3091
- state.entriesByClient.set(startedClient, params.entry);
3092
3572
  state.liveClients.add(startedClient);
3093
3573
  startedClient.addCloseHandler((closedClient) => {
3094
- getSharedCodexAppServerClientState().liveClients.delete(closedClient);
3095
- clearSharedClientEntryIfCurrent(params.key, closedClient);
3574
+ state.liveClients.delete(closedClient);
3575
+ if (state.entriesByClient.get(closedClient) === params.entry) clearSharedClientEntryIfCurrent(params.key, closedClient);
3096
3576
  });
3097
3577
  for (const callback of params.entry.onStartedClientCallbacks) callback(startedClient);
3098
3578
  retirePendingSharedClientEntryIfUnclaimed(params.key, params.entry);
3099
3579
  },
3100
3580
  onInitializedClient: () => initialized.resolve()
3101
3581
  }).then((client) => {
3582
+ const state = getSharedCodexAppServerClientState();
3102
3583
  params.entry.client = client;
3584
+ state.entriesByClient.set(client, params.entry);
3103
3585
  return client;
3104
3586
  }, (error) => {
3105
3587
  initialized.reject(error);
@@ -3139,7 +3621,7 @@ async function startInitializedCodexAppServerClient(params) {
3139
3621
  const timeoutMs = params.timeoutMs ?? 0;
3140
3622
  const startOptionsCandidates = resolveManagedFallbackStartOptions(params.startOptions);
3141
3623
  for (const [index, startOptions] of startOptionsCandidates.entries()) {
3142
- const runtimeArtifactModule = params.runtimeArtifactMode ? await import("./runtime-artifact-BVSPdd6g.js") : void 0;
3624
+ const runtimeArtifactModule = params.runtimeArtifactMode ? await import("./runtime-artifact-BvyT1uyd.js") : void 0;
3143
3625
  const nativeCommandBeforeStart = startOptions.commandSource === "resolved-managed" ? resolveManagedCodexNativeCommand(startOptions.command) : void 0;
3144
3626
  const runtimeArtifactBeforeStart = runtimeArtifactModule ? await runtimeArtifactModule.captureCodexAppServerRuntimeArtifactBeforeStart({
3145
3627
  startOptions,
@@ -3430,4 +3912,4 @@ function collectSharedClients(state) {
3430
3912
  return [...state.liveClients];
3431
3913
  }
3432
3914
  //#endregion
3433
- export { resolveCodexAppServerAuthProfileId as $, isCodexAppServerApprovalRequest as A, claimCodexAppServerLiveThread as B, resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs as C, resolveCodexTurnTerminalIdleTimeoutMs as D, resolveCodexTurnCompletionIdleTimeoutMs as E, isCodexAppServerPrewriteRequestCancellationError as F, protectCodexAppServerLiveThread as G, ensureCodexAppServerClientRuntime as H, isCodexAppServerRequestTimeoutError as I, unsubscribeCodexAppServerLiveThread as J, releaseCodexAppServerLiveThread as K, isUnsupportedCodexAppServerVersionError as L, isCodexAppServerConnectionClosedError as M, isCodexAppServerIndeterminateRequestCancellationError as N, withCodexStartupTimeout as O, isCodexAppServerIndeterminateTransportError as P, resolveCodexAppServerAuthAccountCacheKey as Q, resolveCodexAppServerClientInstanceId as R, resolveCodexGatewayTimeoutWithGraceMs as S, resolveCodexTurnAssistantCompletionIdleTimeoutMs as T, isCodexAppServerClientRuntimeLive as U, consumeCodexAppServerLiveThread as V, isCodexAppServerLiveThreadClaimed as W, readRecentCodexRateLimits as X, readCodexRateLimitsRevision as Y, rememberCodexRateLimitsRead as Z, withTimeout$1 as _, getLeasedSharedCodexAppServerClient as a, CODEX_INTERACTIVE_THREAD_SOURCE_KINDS as at, CodexAppServerStartupError as b, releaseCodexAppServerClientLease as c, isJsonObject as ct, retainSharedCodexAppServerClientByInstanceId as d, resolveCodexAppServerLocalHomeDir as dt, resolveCodexAppServerAuthProfileIdForAgent as et, retainSharedCodexAppServerClientIfCurrent as f, withAbortableTimeout as g, withLeasedCodexAppServerClientStartSelectionRetry as h, createIsolatedCodexAppServerClient as i, CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES as it, isCodexAppServerBrokenPipeError as j, getCodexAppServerClientInstanceId as k, releaseLeasedSharedCodexAppServerClient as l, resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath as lt, shared_client_exports as m, clearSharedCodexAppServerClientIfCurrentAndUnclaimed as n, resolveCodexAppServerPreparedApiKeyCacheKey as nt, getSharedCodexAppServerClient as o, CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE as ot, retireSharedCodexAppServerClientIfCurrent as p, retainCodexAppServerLiveThread as q, clearSharedCodexAppServerClientIfCurrentAndWait as r, resolveCodexAppServerPreparedAuthHandoff as rt, isCodexAppServerStartSelectionChangedError as s, flattenCodexDynamicToolFunctions as st, clearSharedCodexAppServerClientIfCurrent as t, resolveCodexAppServerFallbackApiKeyCacheKey as tt, resolveCodexNativeConfigFenceKey as u, resolveCodexAppServerHomeDir as ut, acquireCodexNativeConfigFence as v, resolveCodexStartupTimeoutMs as w, isCodexAppServerStartupError as x, CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS as y, CodexAppServerRpcError as z };
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 };