@openclaw/codex 2026.9.1-beta.1 → 2026.9.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 (73) hide show
  1. package/dist/api.js +5 -6
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-DYHcRQAa.js} +31 -45
  3. package/dist/attempt-context-DmQUNWwT.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{binding-connection-BMvPX73o.js → binding-connection-E6uDsPlx.js} +2 -2
  6. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-wzJrwKKe.js} +15 -20
  7. package/dist/{capabilities-BRMh0aIq.js → capabilities-dIjNVCYH.js} +1 -1
  8. package/dist/cli-metadata.js +1 -1
  9. package/dist/{command-formatters-DEsIufkt.js → command-formatters-CLHhEB1Q.js} +5 -4
  10. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-CTGb2csu.js} +124 -101
  11. package/dist/{compact-BglNFZrQ.js → compact-_t0HQrUS.js} +38 -29
  12. package/dist/{config-giW8X0QA.js → config-BhmfC1s6.js} +294 -282
  13. package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
  14. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  15. package/dist/doctor-contract-api.js +143 -12
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BU9tmlYm.js} +599 -655
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-DzYBW4h1.js} +2 -2
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-DHRFXKbS.js} +4 -4
  19. package/dist/{event-projector-usage-Bk1I90Bg.js → event-projector-usage-C5mcrGjp.js} +15 -148
  20. package/dist/{harness-Csz0F-DK.js → harness-DXVZ5II5.js} +42 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/index.js +458 -404
  23. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-kg9kv4iH.js} +4 -4
  24. package/dist/launch-args-RXQwn8zV.js +90 -0
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-4-Vjz5GH.js} +14 -9
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-CUM68Hkl.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-sYnEc0gd.js} +25 -28
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-CnH147jH.js} +55 -115
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-CstNHCmC.js} +9 -6
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-CNe-Xrjx.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-CfFlKqDl.js} +15 -14
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BFrPhb2c.js} +6 -6
  34. package/dist/provider-capabilities-DW5p4k9w.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-CawQJfUS.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-DYAI5FQE.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-D1hJO4fX.js} +828 -2100
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-C3jVimFn.js} +21 -14
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-BHuTCRZQ.js} +198 -127
  40. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-yyvTWFOw.js} +564 -408
  41. package/dist/{session-cli-DMc4oS2w.js → session-cli-CmFEXMAM.js} +1 -1
  42. package/dist/session-history-C5J1AKzG.js +87 -0
  43. package/dist/session-initialization-BeWzjGiR.js +74 -0
  44. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-DeWVTCMj.js} +2 -2
  45. package/dist/session-retirement-BujDjXVF.js +97 -0
  46. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-C351-5Qw.js} +19 -4
  47. package/dist/settled-turn-context-D3SNWtUn.js +268 -0
  48. package/dist/settled-turn-finalizer-BVJx4yoc.js +104 -0
  49. package/dist/{shared-client-DKz23hHY.js → shared-client-DxcfF9KX.js} +3480 -679
  50. package/dist/{side-question-DjA_jsNe.js → side-question-BTZAII4l.js} +108 -70
  51. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DAcEJ_Om.js} +2188 -1915
  52. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-zGokd8i1.js} +25 -14
  53. package/dist/thread-policy-g4KbJPzp.js +54 -0
  54. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-C7pyVKeW.js} +59 -7
  55. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-DTUxFWa_.js} +79 -17
  56. package/dist/transport-stdio-CyPldczm.js +726 -0
  57. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  58. package/dist/upstream-session-fork-CYlBIGVt.js +966 -0
  59. package/dist/{usage-ROIaW3wo.js → usage-Bj-rrzkR.js} +3 -3
  60. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-D2FWSK4w.js} +3 -3
  61. package/openclaw.plugin.json +1 -30
  62. package/package.json +6 -6
  63. package/dist/command-rpc-CCeCkf_v.js +0 -90
  64. package/dist/incognito-session-ET_ufDH-.js +0 -2
  65. package/dist/model-catalog-i9rNBthX.js +0 -48
  66. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  67. package/dist/request-DXXGHko0.js +0 -157
  68. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  69. package/dist/session-retirement-CcNC3YSY.js +0 -36
  70. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  71. package/dist/thread-resume-BE9qNEW0.js +0 -30
  72. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  73. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
@@ -1,22 +1,23 @@
1
- import { n as resolveCodexAppServerSpawnEnv } from "./transport-stdio-DhoGy33Z.js";
1
+ import { i as resolvePackagedCodexNativeCommand } from "./managed-binary-4-Vjz5GH.js";
2
+ import { n as resolveCodexAppServerSpawnEnv } from "./transport-stdio-CyPldczm.js";
2
3
  import fs from "node:fs/promises";
3
4
  import path from "node:path";
5
+ import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
4
6
  import { createHash } from "node:crypto";
5
7
  import { constants } from "node:fs";
6
8
  import { resolveWindowsExecutablePath, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
7
- import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
8
9
  //#region extensions/codex/src/app-server/runtime-artifact.ts
9
10
  /** Exact local runtime artifact identity for verified Codex setup turns. */
10
11
  const ARTIFACT_ID_PREFIX = "codex-app-server:v1:";
11
12
  const ARTIFACT_HASH_DOMAIN = "openclaw-codex-app-server-runtime-artifact-v1\0";
12
- const MAX_ARTIFACT_ID_BYTES = 32 * 1024;
13
+ const MAX_ARTIFACT_ID_BYTES = 32768;
13
14
  const MAX_ARTIFACT_PATH_BYTES = 4096;
14
15
  const MAX_ARTIFACT_INVOCATION_PATHS = 8;
15
16
  const MAX_ARTIFACT_DEPTH = 64;
16
17
  const MAX_ARTIFACT_ENTRIES = 32768;
17
18
  const MAX_ARTIFACT_FILES = 8192;
18
19
  const MAX_ARTIFACT_TOTAL_BYTES = 1024n * 1024n * 1024n;
19
- const READ_CHUNK_BYTES = 64 * 1024;
20
+ const READ_CHUNK_BYTES = 65536;
20
21
  const CODE_MODE_HOST_PATH_ENV = "CODEX_CODE_MODE_HOST_PATH";
21
22
  const SAFE_NODE_OPTIONS_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
22
23
  "--enable-network-family-autoselection",
@@ -363,6 +364,8 @@ async function captureFilesystemDescriptor(params) {
363
364
  const spawnCwd = path.resolve(params.startOptions.cwd ?? process.cwd());
364
365
  const commandPath = await resolveCommandPath(params.startOptions.command, env, spawnCwd);
365
366
  const commandRealPath = await fs.realpath(commandPath);
367
+ let nativeCommand = params.spawnIdentity.nativeCommand;
368
+ let nativeCandidate = commandRealPath;
366
369
  let invocationPaths;
367
370
  if (process.platform === "win32") {
368
371
  const program = resolveWindowsSpawnProgram({
@@ -372,7 +375,10 @@ async function captureFilesystemDescriptor(params) {
372
375
  execPath: process.execPath,
373
376
  packageName: "@openai/codex"
374
377
  });
375
- if (program.resolution === "node-entrypoint" && !params.spawnIdentity.nativeCommand) throw new Error("Codex runtime cannot attest a custom Node launcher without its native target");
378
+ const entrypoint = program.leadingArgv[0];
379
+ if (program.resolution === "node-entrypoint" && entrypoint && !nativeCommand) nativeCommand = resolvePackagedCodexNativeCommand(await fs.realpath(entrypoint));
380
+ if (program.resolution === "node-entrypoint" && !nativeCommand) throw new Error("Codex runtime cannot attest a custom Node launcher without its native target");
381
+ nativeCandidate = program.command;
376
382
  const invocationCandidates = [
377
383
  commandRealPath,
378
384
  program.command,
@@ -383,17 +389,18 @@ async function captureFilesystemDescriptor(params) {
383
389
  const resolved = await resolveCommandPath(candidate, env, spawnCwd);
384
390
  invocationPaths.push(await fs.realpath(resolved));
385
391
  }
386
- } else invocationPaths = await resolvePosixInvocationPaths({
387
- commandRealPath,
388
- env,
389
- cwd: spawnCwd,
390
- nativeCommand: params.spawnIdentity.nativeCommand
391
- });
392
+ } else {
393
+ nativeCommand ??= resolvePackagedCodexNativeCommand(commandRealPath);
394
+ invocationPaths = await resolvePosixInvocationPaths({
395
+ commandRealPath,
396
+ env,
397
+ cwd: spawnCwd,
398
+ nativeCommand
399
+ });
400
+ }
392
401
  invocationPaths = [...new Set(invocationPaths)].toSorted(compareArtifactNames);
393
402
  if (invocationPaths.length > MAX_ARTIFACT_INVOCATION_PATHS) throw new Error("Codex runtime launcher exceeds the bounded invocation file count");
394
- const nativeCandidate = params.spawnIdentity.nativeCommand ?? invocationPaths[0];
395
- if (!nativeCandidate) throw new Error("Codex runtime did not resolve a native executable");
396
- const nativePath = await fs.realpath(await resolveCommandPath(nativeCandidate, env, spawnCwd));
403
+ const nativePath = await fs.realpath(await resolveCommandPath(nativeCommand ?? nativeCandidate, env, spawnCwd));
397
404
  const packageRoot = await resolvePackageRoot(nativePath);
398
405
  const configuredCodeModeHost = readEffectiveSpawnEnvironmentValue(env, CODE_MODE_HOST_PATH_ENV)?.trim();
399
406
  const adjacentCodeModeHost = path.join(path.dirname(nativePath), process.platform === "win32" ? "codex-code-mode-host.exe" : "codex-code-mode-host");
@@ -1,10 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { P as normalizeCodexServiceTier, y as CODEX_PLUGIN_MARKETPLACE_NAME_PATTERN } from "./config-giW8X0QA.js";
2
+ import { P as CODEX_PLUGIN_MARKETPLACE_NAME_PATTERN, j as normalizeCodexServiceTier } from "./config-BhmfC1s6.js";
3
3
  import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.js";
4
4
  import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { AgentHarnessSessionSupersededError, embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
6
6
  import { createHash, randomUUID } from "node:crypto";
7
- import { ensureAuthProfileStore, resolveDefaultAgentDir, resolveProviderIdForAuth, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
7
+ import { isDeepStrictEqual } from "node:util";
8
+ import { ensureAuthProfileStore, resolveDefaultAgentDir, resolveProviderIdForAuth } from "openclaw/plugin-sdk/agent-runtime";
9
+ import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
8
10
  import { z } from "zod";
9
11
  import { AsyncLocalStorage } from "node:async_hooks";
10
12
  import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
@@ -29,7 +31,8 @@ var session_binding_exports = /* @__PURE__ */ __exportAll({
29
31
  reclaimCurrentCodexSessionGeneration: () => reclaimCurrentCodexSessionGeneration,
30
32
  resolveCodexRunSessionBindingAuthority: () => resolveCodexRunSessionBindingAuthority,
31
33
  scopeCodexRunBindingStore: () => scopeCodexRunBindingStore,
32
- sessionBindingIdentity: () => sessionBindingIdentity
34
+ sessionBindingIdentity: () => sessionBindingIdentity,
35
+ validateBindingForWrite: () => validateBindingForWrite
33
36
  });
34
37
  const CODEX_APP_SERVER_NATIVE_AUTH_PROVIDER = "openai";
35
38
  const PUBLIC_OPENAI_MODEL_PROVIDER = "openai";
@@ -42,7 +45,7 @@ const BINDING_LEASE_RENEW_INTERVAL_MS = Math.floor(BINDING_LEASE_STALE_MS / 3);
42
45
  const PHYSICAL_SESSION_RETIRE_TTL_MS = BINDING_LEASE_WAIT_MS;
43
46
  /** Resolves the same agent scope OpenClaw uses for transcript/session ownership. */
44
47
  function sessionBindingIdentity(params) {
45
- const { sessionAgentId } = resolveSessionAgentIds$1(params);
48
+ const { sessionAgentId } = resolveSessionAgentIdsStrict(params);
46
49
  const sessionKey = params.sessionKey?.trim();
47
50
  return {
48
51
  kind: "session",
@@ -147,6 +150,7 @@ const threadBindingSchema = z.object({
147
150
  model: optionalStringSchema,
148
151
  preserveNativeModel: z.literal(true).optional().catch(void 0),
149
152
  pendingSupervisionBranch: pendingSupervisionBranchSchema.optional(),
153
+ pendingResumeConfiguration: z.literal(true).optional(),
150
154
  modelProvider: z.string().transform((value) => value.trim()).pipe(z.string().min(1)).optional().catch(void 0),
151
155
  approvalPolicy: z.preprocess((value) => value === "on-failure" ? "on-request" : value, z.enum([
152
156
  "never",
@@ -318,20 +322,23 @@ function scopeCodexRunBindingStore(params) {
318
322
  ...params.bindingStore,
319
323
  read: (identity) => params.bindingStore.read(mapIdentity(identity)),
320
324
  hasOtherThreadOwner: (threadId, identity) => params.bindingStore.hasOtherThreadOwner(threadId, identity ? mapIdentity(identity) : void 0),
321
- mutate: (identity, mutation) => params.bindingStore.mutate(mapIdentity(identity), mutation),
325
+ mutate: (identity, mutation, assertCurrent) => params.bindingStore.mutate(mapIdentity(identity), mutation, assertCurrent),
322
326
  prepareSessionGenerationReclaim: (identity) => params.bindingStore.prepareSessionGenerationReclaim(mapSessionIdentity(identity)),
323
327
  adoptSessionGeneration: (identity, expectedPreviousSessionId) => params.bindingStore.adoptSessionGeneration(mapSessionIdentity(identity), expectedPreviousSessionId),
324
328
  resetSessionGeneration: (identity) => params.bindingStore.resetSessionGeneration(mapSessionIdentity(identity)),
325
329
  retireSessionGeneration: (identity) => params.bindingStore.retireSessionGeneration(mapSessionIdentity(identity)),
330
+ withSessionDeletion: (identity, assertCurrent, run) => params.bindingStore.withSessionDeletion(mapSessionIdentity(identity), assertCurrent, run),
326
331
  withThreadArchiveFence: (run) => params.bindingStore.withThreadArchiveFence(run),
327
332
  withLease: (identity, run) => params.bindingStore.withLease(mapIdentity(identity), run)
328
333
  };
329
334
  }
330
335
  /** Lets the authoritative OpenClaw session generation claim a stale stable binding row. */
331
336
  async function reclaimCurrentCodexSessionGeneration(params) {
337
+ params.assertCurrent?.();
332
338
  const sessionKey = params.identity.sessionKey?.trim();
333
339
  if (!sessionKey) return true;
334
340
  const plan = await params.bindingStore.prepareSessionGenerationReclaim(params.identity);
341
+ params.assertCurrent?.();
335
342
  if (plan.kind === "resolved") return plan.result;
336
343
  try {
337
344
  const storePath = params.storePath?.trim() || resolveStorePath(params.config?.session?.store, { agentId: params.identity.agentId });
@@ -348,7 +355,7 @@ async function reclaimCurrentCodexSessionGeneration(params) {
348
355
  return await params.bindingStore.mutate(params.identity, {
349
356
  kind: "reclaim-generation",
350
357
  expectedPreviousSessionId: plan.expectedPreviousSessionId
351
- });
358
+ }, params.assertCurrent);
352
359
  }
353
360
  /** Creates the single binding facade owned by the Codex plugin runtime. */
354
361
  function createCodexAppServerBindingStore(state) {
@@ -382,9 +389,10 @@ function createCodexAppServerBindingStore(state) {
382
389
  }
383
390
  };
384
391
  const renewLease = (key, owner) => {
385
- if (owner.failure) return;
392
+ if (owner.failure || owner.phase !== "held") return;
386
393
  try {
387
394
  let renewed = false;
395
+ owner.assertCurrent?.();
388
396
  const stored = update(key, (raw) => {
389
397
  const current = readStoredCodexAppServerBinding(raw);
390
398
  if (raw !== void 0 && !current) throw new Error(`Invalid Codex app-server binding row: ${key}`);
@@ -405,15 +413,18 @@ function createCodexAppServerBindingStore(state) {
405
413
  owner.failure = bindingLeaseLostError(key, error);
406
414
  }
407
415
  };
408
- const transactKey = async (key, apply, ttlMs) => {
416
+ const transactKey = async (key, apply, ttlMs, assertCurrent) => {
409
417
  const deadline = Date.now() + BINDING_LEASE_WAIT_MS;
410
418
  while (true) {
411
419
  let busy = false;
412
420
  let leaseLost = false;
413
421
  let result;
414
422
  const ownedLease = leaseContext.getStore()?.get(key);
423
+ if (ownedLease && ownedLease.phase !== "held") throw bindingLeaseLostError(key);
415
424
  if (ownedLease?.failure) throw ownedLease.failure;
416
425
  const ownedToken = ownedLease?.token;
426
+ assertCurrent?.();
427
+ ownedLease?.assertCurrent?.();
417
428
  update(key, (raw) => {
418
429
  const current = readStoredCodexAppServerBinding(raw);
419
430
  if (raw !== void 0 && !current) throw new Error(`Invalid Codex app-server binding row: ${key}`);
@@ -441,6 +452,115 @@ function createCodexAppServerBindingStore(state) {
441
452
  await sleep(BINDING_LEASE_RETRY_INTERVAL_MS);
442
453
  }
443
454
  };
455
+ const withBindingLease = async (identity, run, options = {}) => {
456
+ options.assertCurrent?.();
457
+ const key = bindingStoreKey(identity);
458
+ const owned = leaseContext.getStore();
459
+ const existingOwner = owned?.get(key);
460
+ if (existingOwner) {
461
+ if (existingOwner.phase !== "held") throw bindingLeaseLostError(key);
462
+ const failureBeforeRun = existingOwner.failure;
463
+ if (failureBeforeRun) throw failureBeforeRun;
464
+ const result = await run();
465
+ options.assertCurrent?.();
466
+ const failureAfterRun = existingOwner.failure;
467
+ if (failureAfterRun) throw failureAfterRun;
468
+ return result;
469
+ }
470
+ const token = randomUUID();
471
+ const acquired = await transactKey(key, (current) => {
472
+ if (current?.state === "cleared" && current.retired === true && ownsStoredSessionGeneration(identity, current) && !options.allowRetired) return { result: false };
473
+ const lease = {
474
+ token,
475
+ expiresAt: Date.now() + BINDING_LEASE_STALE_MS
476
+ };
477
+ if (current?.state === "active") return {
478
+ result: true,
479
+ next: {
480
+ ...current,
481
+ ...preservedSessionGeneration(identity, current),
482
+ lease
483
+ }
484
+ };
485
+ if (current?.state === "cleared" && current.retired === true) return {
486
+ result: true,
487
+ next: {
488
+ ...current,
489
+ lease
490
+ }
491
+ };
492
+ return {
493
+ result: true,
494
+ next: {
495
+ version: 1,
496
+ state: "cleared",
497
+ ...preservedSessionGeneration(identity, current),
498
+ lease
499
+ }
500
+ };
501
+ }, void 0, options.assertCurrent);
502
+ options.assertCurrent?.();
503
+ if (!acquired) throw new Error(`Codex binding generation was retired: ${key}`);
504
+ const owner = {
505
+ token,
506
+ phase: "held",
507
+ assertCurrent: options.assertCurrent
508
+ };
509
+ const nested = new Map(owned);
510
+ nested.set(key, owner);
511
+ const heartbeat = setInterval(() => renewLease(key, owner), BINDING_LEASE_RENEW_INTERVAL_MS);
512
+ heartbeat.unref();
513
+ try {
514
+ const result = await leaseContext.run(nested, run);
515
+ options.assertCurrent?.();
516
+ if (owner.failure) throw owner.failure;
517
+ return result;
518
+ } finally {
519
+ clearInterval(heartbeat);
520
+ owner.phase = "closed";
521
+ options.assertCurrent?.();
522
+ try {
523
+ const current = readStoredCodexAppServerBinding(state.lookup(key));
524
+ if (current?.lease?.token === token) {
525
+ const ttlMs = current.state === "active" || current.retired === true && !key.startsWith("session:") ? void 0 : current.retired === true ? PHYSICAL_SESSION_RETIRE_TTL_MS : 1;
526
+ options.assertCurrent?.();
527
+ update(key, (raw) => {
528
+ const stored = readStoredCodexAppServerBinding(raw);
529
+ if (stored?.lease?.token !== token) return;
530
+ const { lease: _lease, ...released } = stored;
531
+ return released;
532
+ }, ttlMs === void 0 ? void 0 : { ttlMs });
533
+ }
534
+ } catch (error) {
535
+ options.assertCurrent?.();
536
+ embeddedAgentLog.warn("failed to release codex app-server binding lease", {
537
+ key,
538
+ error
539
+ });
540
+ }
541
+ }
542
+ };
543
+ const transitionSessionGeneration = async (identity, mode) => {
544
+ return await runBindingMutation(async () => {
545
+ const key = bindingStoreKey(identity);
546
+ const ttlMs = mode === "reset" ? leaseContext.getStore()?.has(key) ? void 0 : 1 : identity.sessionKey?.trim() ? void 0 : PHYSICAL_SESSION_RETIRE_TTL_MS;
547
+ return await transactKey(key, (current, leaseToken) => {
548
+ if (!current) return { result: "absent" };
549
+ if (!ownsStoredSessionGeneration(identity, current)) return { result: "conflict" };
550
+ if (current.state === "cleared" && current.retired === true) return { result: mode === "retire" ? "applied" : "conflict" };
551
+ return {
552
+ result: "applied",
553
+ next: {
554
+ version: 1,
555
+ state: "cleared",
556
+ ...mode === "retire" ? { retired: true } : {},
557
+ ...storedSessionGeneration(identity, current),
558
+ ...current.lease && current.lease.token === leaseToken ? { lease: current.lease } : {}
559
+ }
560
+ };
561
+ }, ttlMs);
562
+ });
563
+ };
444
564
  return {
445
565
  async read(identity) {
446
566
  const key = bindingStoreKey(identity);
@@ -485,7 +605,7 @@ function createCodexAppServerBindingStore(state) {
485
605
  expectedPreviousSessionId: currentSessionId
486
606
  };
487
607
  },
488
- async mutate(identity, mutation) {
608
+ async mutate(identity, mutation, assertCurrent) {
489
609
  return await runBindingMutation(async () => {
490
610
  const key = bindingStoreKey(identity);
491
611
  const retainLegacyClear = mutation.kind === "clear" && key.startsWith("conversation:legacy-");
@@ -523,9 +643,8 @@ function createCodexAppServerBindingStore(state) {
523
643
  const active = ownsGeneration ? storedActive : void 0;
524
644
  const retiredGeneration = current?.state === "cleared" && current.retired === true && ownsGeneration;
525
645
  const preservesSupervisionOwner = mutation.kind === "set" && active?.binding.connectionScope === "supervision" && isSameSupervisionOwner(active.binding, mutation.binding);
526
- const clearsPendingSupervisionOwner = mutation.kind === "clear" && active?.binding.connectionScope === "supervision" && matchesPendingSupervisionClear(active.binding, mutation.threadId, mutation.expectedPendingSupervisionBranch);
527
646
  const replacesExpectedOrdinaryOwner = mutation.kind === "replace-thread" && active?.binding.threadId === mutation.expectedThreadId && active.binding.connectionScope !== "supervision" && mutation.binding.connectionScope !== "supervision" && mutation.binding.threadId !== mutation.expectedThreadId;
528
- if (mutation.kind === "set" && (mutation.if?.kind === "absent" && storedActive || current !== void 0 && !ownsGeneration || retiredGeneration || active?.binding.connectionScope === "supervision" && !preservesSupervisionOwner) || mutation.kind === "patch" && active?.binding.threadId !== mutation.threadId || mutation.kind === "replace-thread" && !replacesExpectedOrdinaryOwner || (mutation.kind === "patch-pending-supervision-branch" || mutation.kind === "commit-pending-supervision-branch") && !matchesPendingSupervisionBranch(active?.binding, mutation.expected) || mutation.kind === "clear" && (mutation.threadId !== void 0 && active?.binding.threadId !== mutation.threadId || !ownsGeneration || active?.binding.connectionScope === "supervision" && !clearsPendingSupervisionOwner)) return { result: false };
647
+ if (mutation.kind === "set" && (mutation.if?.kind === "absent" && storedActive || current !== void 0 && !ownsGeneration || retiredGeneration || active?.binding.connectionScope === "supervision" && !preservesSupervisionOwner) || mutation.kind === "patch" && active?.binding.threadId !== mutation.threadId || mutation.kind === "replace-thread" && !replacesExpectedOrdinaryOwner || (mutation.kind === "patch-pending-supervision-branch" || mutation.kind === "commit-pending-supervision-branch") && !matchesPendingSupervisionBranch(active?.binding, mutation.expected) || mutation.kind === "clear" && (!ownsGeneration || mutation.threadId !== void 0 && active?.binding.threadId !== mutation.threadId || active?.binding.connectionScope === "supervision")) return { result: false };
529
648
  if (mutation.kind === "clear" && retiredGeneration) return { result: true };
530
649
  if (mutation.kind === "clear") return {
531
650
  result: true,
@@ -563,7 +682,7 @@ function createCodexAppServerBindingStore(state) {
563
682
  ...ownedLease
564
683
  }
565
684
  };
566
- }, mutation.kind === "clear" && !retainLegacyClear && !leaseContext.getStore()?.has(key) ? 1 : void 0);
685
+ }, mutation.kind === "clear" && !retainLegacyClear && !leaseContext.getStore()?.has(key) ? 1 : void 0, assertCurrent);
567
686
  });
568
687
  },
569
688
  async adoptSessionGeneration(identity, expectedPreviousSessionId) {
@@ -586,45 +705,8 @@ function createCodexAppServerBindingStore(state) {
586
705
  });
587
706
  });
588
707
  },
589
- async resetSessionGeneration(identity) {
590
- return await runBindingMutation(async () => {
591
- const key = bindingStoreKey(identity);
592
- return await transactKey(key, (current, leaseToken) => {
593
- if (!current) return { result: "absent" };
594
- if (!ownsStoredSessionGeneration(identity, current)) return { result: "conflict" };
595
- if (current.state === "cleared" && current.retired === true) return { result: "conflict" };
596
- return {
597
- result: "applied",
598
- next: {
599
- version: 1,
600
- state: "cleared",
601
- ...storedSessionGeneration(identity, current),
602
- ...current.lease && current.lease.token === leaseToken ? { lease: current.lease } : {}
603
- }
604
- };
605
- }, leaseContext.getStore()?.has(key) ? void 0 : 1);
606
- });
607
- },
608
- async retireSessionGeneration(identity) {
609
- return await runBindingMutation(async () => {
610
- const key = bindingStoreKey(identity);
611
- return await transactKey(key, (current, leaseToken) => {
612
- if (!current) return { result: "absent" };
613
- if (!ownsStoredSessionGeneration(identity, current)) return { result: "conflict" };
614
- if (current.state === "cleared" && current.retired === true) return { result: "applied" };
615
- return {
616
- result: "applied",
617
- next: {
618
- version: 1,
619
- state: "cleared",
620
- retired: true,
621
- ...storedSessionGeneration(identity, current),
622
- ...current.lease && current.lease.token === leaseToken ? { lease: current.lease } : {}
623
- }
624
- };
625
- }, identity.sessionKey?.trim() ? void 0 : PHYSICAL_SESSION_RETIRE_TTL_MS);
626
- });
627
- },
708
+ resetSessionGeneration: (identity) => transitionSessionGeneration(identity, "reset"),
709
+ retireSessionGeneration: (identity) => transitionSessionGeneration(identity, "retire"),
628
710
  async withThreadArchiveFence(run) {
629
711
  pendingArchives += 1;
630
712
  const operation = archiveTail.then(async () => {
@@ -638,79 +720,73 @@ function createCodexAppServerBindingStore(state) {
638
720
  pendingArchives -= 1;
639
721
  }
640
722
  },
641
- async withLease(identity, run) {
723
+ async withSessionDeletion(identity, assertCurrent, run) {
642
724
  const key = bindingStoreKey(identity);
643
- const owned = leaseContext.getStore();
644
- const existingOwner = owned?.get(key);
645
- if (existingOwner) {
646
- const failureBeforeRun = existingOwner.failure;
647
- if (failureBeforeRun) throw failureBeforeRun;
648
- const result = await run();
649
- const failureAfterRun = existingOwner.failure;
650
- if (failureAfterRun) throw failureAfterRun;
651
- return result;
652
- }
653
- const token = randomUUID();
654
- if (!await transactKey(key, (current) => {
655
- if (current?.state === "cleared" && current.retired === true && ownsStoredSessionGeneration(identity, current)) return { result: false };
656
- const lease = {
657
- token,
658
- expiresAt: Date.now() + BINDING_LEASE_STALE_MS
659
- };
660
- if (current?.state === "active") return {
661
- result: true,
662
- next: {
663
- ...current,
664
- ...preservedSessionGeneration(identity, current),
665
- lease
666
- }
667
- };
668
- if (current?.state === "cleared" && current.retired === true) return {
669
- result: true,
670
- next: {
671
- ...current,
672
- lease
673
- }
674
- };
675
- return {
676
- result: true,
677
- next: {
678
- version: 1,
679
- state: "cleared",
680
- ...preservedSessionGeneration(identity, current),
681
- lease
725
+ const deleteIf = state.deleteIf?.bind(state);
726
+ if (!deleteIf) throw new Error("Codex session deletion requires conditional plugin-state deletion");
727
+ return await runBindingMutation(async () => {
728
+ assertCurrent();
729
+ if (state.lookup(key) === void 0) {
730
+ let active = true;
731
+ try {
732
+ return await run(void 0, {
733
+ commit() {
734
+ assertCurrent();
735
+ if (!active || state.lookup(key) !== void 0) throw new Error("Codex binding changed before session deletion");
736
+ },
737
+ rollback() {}
738
+ });
739
+ } finally {
740
+ active = false;
682
741
  }
683
- };
684
- })) throw new Error(`Codex binding generation was retired: ${key}`);
685
- const owner = { token };
686
- const nested = new Map(owned);
687
- nested.set(key, owner);
688
- const heartbeat = setInterval(() => renewLease(key, owner), BINDING_LEASE_RENEW_INTERVAL_MS);
689
- heartbeat.unref();
690
- try {
691
- const result = await leaseContext.run(nested, run);
692
- if (owner.failure) throw owner.failure;
693
- return result;
694
- } finally {
695
- clearInterval(heartbeat);
696
- try {
697
- const removeOwnedLease = (raw, matches) => {
698
- const current = readStoredCodexAppServerBinding(raw);
699
- if (!current || !matches(current) || current.lease?.token !== token) return;
700
- const { lease: _lease, ...released } = current;
701
- return released;
742
+ }
743
+ return await withBindingLease(identity, async () => {
744
+ const owner = leaseContext.getStore().get(key);
745
+ const stored = readStoredCodexAppServerBinding(state.lookup(key));
746
+ if (!stored || !ownsStoredSessionGeneration(identity, stored)) throw new Error("Codex binding generation changed before session deletion");
747
+ const { lease: _lease, ...expectedValue } = stored;
748
+ let deleted;
749
+ let active = true;
750
+ const assertActive = () => {
751
+ assertCurrent();
752
+ if (!active || owner.phase === "closed" || owner.failure) throw owner.failure ?? bindingLeaseLostError(key);
702
753
  };
703
- if (!update(key, (raw) => removeOwnedLease(raw, (current) => current.state === "active"))) {
704
- if (!update(key, (raw) => removeOwnedLease(raw, (current) => current.state === "cleared" && current.retired === true), key.startsWith("session:") ? { ttlMs: PHYSICAL_SESSION_RETIRE_TTL_MS } : void 0)) update(key, (raw) => removeOwnedLease(raw, (current) => current.state === "cleared"), { ttlMs: 1 });
754
+ try {
755
+ return await run(stored.state === "active" ? stored.binding : void 0, {
756
+ commit() {
757
+ assertActive();
758
+ if (deleted) return;
759
+ const current = state.lookup(key);
760
+ const { lease, ...value } = readStoredCodexAppServerBinding(current) ?? {};
761
+ if (!current || lease?.token !== owner.token || lease.expiresAt <= Date.now() || !isDeepStrictEqual(value, expectedValue) || !deleteIf(key, (raw) => isDeepStrictEqual(raw, current))) throw new Error("Codex binding changed before session deletion");
762
+ deleted = current;
763
+ owner.phase = "deleted";
764
+ },
765
+ rollback() {
766
+ assertActive();
767
+ if (!deleted) return;
768
+ const restored = {
769
+ ...deleted,
770
+ lease: {
771
+ token: owner.token,
772
+ expiresAt: Date.now() + BINDING_LEASE_STALE_MS
773
+ }
774
+ };
775
+ if (!state.registerIfAbsent(key, restored)) throw new Error("Codex binding changed before session deletion rollback");
776
+ deleted = void 0;
777
+ owner.phase = "held";
778
+ }
779
+ });
780
+ } finally {
781
+ active = false;
705
782
  }
706
- } catch (error) {
707
- embeddedAgentLog.warn("failed to release codex app-server binding lease", {
708
- key,
709
- error
710
- });
711
- }
712
- }
713
- }
783
+ }, {
784
+ allowRetired: true,
785
+ assertCurrent
786
+ });
787
+ });
788
+ },
789
+ withLease: withBindingLease
714
790
  };
715
791
  }
716
792
  function matchesPendingSupervisionBranch(binding, expected) {
@@ -724,11 +800,6 @@ function matchesPendingSupervisionBranch(binding, expected) {
724
800
  function isSameSupervisionOwner(current, replacement) {
725
801
  return replacement.connectionScope === "supervision" && replacement.threadId === current.threadId && replacement.supervisionSourceThreadId === current.supervisionSourceThreadId;
726
802
  }
727
- function matchesPendingSupervisionClear(binding, threadId, expected) {
728
- if (!expected) return false;
729
- const sourceThreadId = expected.sourceThreadId;
730
- return threadId === sourceThreadId && binding.supervisionSourceThreadId === sourceThreadId && matchesPendingSupervisionBranch(binding, expected);
731
- }
732
803
  /** Stable plugin-state key for one current binding owner. */
733
804
  function bindingStoreKey(identity) {
734
805
  if (identity.kind === "session") {
@@ -736,7 +807,7 @@ function bindingStoreKey(identity) {
736
807
  const sessionId = identity.sessionId.trim();
737
808
  if (!rawAgentId) throw new Error("Codex app-server binding requires an agent id");
738
809
  if (!sessionId) throw new Error("Codex app-server binding requires a session id");
739
- const agentId = resolveSessionAgentIds$1({ agentId: rawAgentId }).sessionAgentId;
810
+ const agentId = resolveSessionAgentIdsStrict({ agentId: rawAgentId }).sessionAgentId;
740
811
  const sessionKey = identity.sessionKey?.trim();
741
812
  if (sessionKey) return `session-key:${agentId}:${createHash("sha256").update(sessionKey).digest("base64url")}`;
742
813
  return `session:${agentId}:${sessionId}`;
@@ -871,4 +942,4 @@ function normalizeCodexAppServerBindingModelProvider(params) {
871
942
  return modelProvider;
872
943
  }
873
944
  //#endregion
874
- export { hashCodexAppServerBindingFingerprint as a, reclaimCurrentCodexSessionGeneration as c, sessionBindingIdentity as d, session_binding_exports as f, createCodexSessionGenerationSupersededError as i, resolveCodexRunSessionBindingAuthority as l, assertCodexBindingMayBeReplaced as n, isCodexAppServerNativeAuthProfile as o, bindingStoreKey as r, normalizeCodexAppServerBindingModelProvider as s, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS as t, scopeCodexRunBindingStore as u };
945
+ export { hashCodexAppServerBindingFingerprint as a, reclaimCurrentCodexSessionGeneration as c, sessionBindingIdentity as d, session_binding_exports as f, createCodexSessionGenerationSupersededError as i, resolveCodexRunSessionBindingAuthority as l, assertCodexBindingMayBeReplaced as n, isCodexAppServerNativeAuthProfile as o, validateBindingForWrite as p, bindingStoreKey as r, normalizeCodexAppServerBindingModelProvider as s, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS as t, scopeCodexRunBindingStore as u };