@openclaw/codex 2026.8.1-beta.3 → 2026.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/api.js +4 -5
  2. package/dist/{attempt-client-cleanup-URjUlCHg.js → attempt-client-cleanup-CVPvp9dL.js} +47 -5
  3. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  4. package/dist/{binding-connection-DAvZwp96.js → binding-connection-CCQFs8lf.js} +2 -2
  5. package/dist/{bounded-turn-4BjrZEwW.js → bounded-turn-rq5tiSXk.js} +13 -18
  6. package/dist/{capabilities-CH3K57v4.js → capabilities-CGm_94eL.js} +1 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-D0nZ3qtd.js → command-formatters-C54Yg3eA.js} +2 -2
  9. package/dist/{command-handlers-DC86lgT4.js → command-handlers-BvOAHTrA.js} +89 -62
  10. package/dist/{compact-COqgBRny.js → compact-Hd7SAc0N.js} +8 -9
  11. package/dist/{config-CpNeo5AE.js → config-Cxw3Cl3k.js} +346 -286
  12. package/dist/doctor-contract-api.js +115 -14
  13. package/dist/{dynamic-tools-fJZhl4CE.js → dynamic-tools-BFoUcRdx.js} +147 -903
  14. package/dist/{effective-mcp-catalog-4YKn51aG.js → effective-mcp-catalog-BTJHnDSx.js} +2 -2
  15. package/dist/{event-projector-assistant-message-BwxV4d6N.js → event-projector-assistant-message-C5-Ds8mA.js} +18 -1
  16. package/dist/{event-projector-usage-CLwNEJqW.js → event-projector-usage-D3PHFFXh.js} +3 -3
  17. package/dist/{harness-DoKQcF8n.js → harness-DVfUQBlQ.js} +42 -52
  18. package/dist/harness.js +1 -1
  19. package/dist/index.js +278 -295
  20. package/dist/{isolated-completion-DPMtlH9r.js → isolated-completion-Cbbx_5Ed.js} +4 -4
  21. package/dist/launch-args-sXOAg0SX.js +84 -0
  22. package/dist/{managed-binary-By4FZC6i.js → managed-binary-ZkGodW19.js} +43 -7
  23. package/dist/media-understanding-provider.js +1 -1
  24. package/dist/model-catalog-o6Cgjoss.js +101 -0
  25. package/dist/{models-CZw1wClT.js → models-DgFWSuxA.js} +25 -28
  26. package/dist/{native-subagent-monitor-GGq3RnOo.js → native-subagent-monitor-CwFyoylz.js} +207 -30
  27. package/dist/{node-exec-server.runtime-Bsu1LdFZ.js → node-exec-server.runtime-DXrqpw1c.js} +8 -5
  28. package/dist/{plugin-approval-roundtrip-C75Yiiwc.js → plugin-approval-roundtrip-D80UZiJ_.js} +1 -1
  29. package/dist/{plugin-marketplace-discovery-CSWDkMrP.js → plugin-marketplace-discovery-mdmScYOj.js} +30 -39
  30. package/dist/{plugin-metadata-cache-BXH5NF-4.js → plugin-metadata-cache-CulOABwu.js} +162 -135
  31. package/dist/{protocol-validators-VAVyEnDp.js → protocol-validators-Bg5rpNxk.js} +148 -5
  32. package/dist/{rate-limits-BI1dMHDG.js → rate-limits-hFtzt2Up.js} +1 -1
  33. package/dist/{sandbox-guard-C1qEeZ5C.js → request-CCm4LaED.js} +183 -2
  34. package/dist/{run-attempt-BV3HzuCf.js → run-attempt-BwNQDH2u.js} +599 -361
  35. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-DeBvleJw.js} +1 -1
  36. package/dist/{session-binding-BTrLf9Qm.js → session-binding-q2WSTVmC.js} +195 -122
  37. package/dist/{session-catalog-ezP_UcRM.js → session-catalog-C2Tae4rI.js} +229 -122
  38. package/dist/session-catalog-home-id-DAKUrm3C.js +112 -0
  39. package/dist/{session-cli-B1hKvfkI.js → session-cli-BuDjm5Ou.js} +1 -1
  40. package/dist/session-permission-policy-gLm40Vtq.js +160 -0
  41. package/dist/session-retirement-CdkI0bvb.js +90 -0
  42. package/dist/{session-upstream-marker-D_2npHlA.js → session-upstream-marker-Bcy33kam.js} +2 -3
  43. package/dist/{settled-turn-finalizer-Dx1onIqg.js → settled-turn-finalizer-B8JJcxCi.js} +7 -6
  44. package/dist/{shared-client-Dw-rzii0.js → shared-client-kNkljY-R.js} +1720 -786
  45. package/dist/{side-question-DbXkJZzg.js → side-question-GwD73tX6.js} +69 -39
  46. package/dist/{thread-lifecycle-DKdrnTMX.js → thread-lifecycle-Ss-uwnC4.js} +852 -359
  47. package/dist/{thread-ownership-i2NBwDUL.js → thread-ownership-CeYBqg_4.js} +18 -8
  48. package/dist/{plugin-app-cache-key-DMNQ8Zi8.js → thread-resume-DF54hagX.js} +52 -6
  49. package/dist/{transcript-mirror-BU0_LKAL.js → transcript-mirror-CkEQIna-.js} +112 -13
  50. package/dist/transport-stdio-C7-s9TLG.js +708 -0
  51. package/dist/{upstream-session-fork-BDVJCLAD.js → upstream-session-fork-BACGlOFa.js} +78 -86
  52. package/dist/{usage-9dKJCgcy.js → usage-C2l8Mils.js} +3 -3
  53. package/dist/{web-search-provider.runtime-DCdx4DGu.js → web-search-provider.runtime-B2XgRdFv.js} +3 -3
  54. package/openclaw.plugin.json +19 -1
  55. package/package.json +8 -8
  56. package/dist/app-server-policy-bg5QPSVP.js +0 -25
  57. package/dist/command-rpc-xUIIonsO.js +0 -90
  58. package/dist/incognito-session-ET_ufDH-.js +0 -2
  59. package/dist/model-catalog-CmGoqVKB.js +0 -48
  60. package/dist/request-DSK0W6YI.js +0 -157
  61. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  62. package/dist/session-retirement-CE4iMiLh.js +0 -36
  63. package/dist/thread-resume-CdJk2Ehf.js +0 -30
  64. package/dist/transport-stdio-DhoGy33Z.js +0 -99
@@ -1,4 +1,4 @@
1
- import { n as resolveCodexAppServerSpawnEnv } from "./transport-stdio-DhoGy33Z.js";
1
+ import { n as resolveCodexAppServerSpawnEnv } from "./transport-stdio-C7-s9TLG.js";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { createHash } from "node:crypto";
@@ -1,10 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { N as normalizeCodexServiceTier, y as CODEX_PLUGIN_MARKETPLACE_NAME_PATTERN } from "./config-CpNeo5AE.js";
2
+ import { A as normalizeCodexServiceTier, N as CODEX_PLUGIN_MARKETPLACE_NAME_PATTERN } from "./config-Cxw3Cl3k.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";
@@ -42,7 +44,7 @@ const BINDING_LEASE_RENEW_INTERVAL_MS = Math.floor(BINDING_LEASE_STALE_MS / 3);
42
44
  const PHYSICAL_SESSION_RETIRE_TTL_MS = BINDING_LEASE_WAIT_MS;
43
45
  /** Resolves the same agent scope OpenClaw uses for transcript/session ownership. */
44
46
  function sessionBindingIdentity(params) {
45
- const { sessionAgentId } = resolveSessionAgentIds$1(params);
47
+ const { sessionAgentId } = resolveSessionAgentIdsStrict(params);
46
48
  const sessionKey = params.sessionKey?.trim();
47
49
  return {
48
50
  kind: "session",
@@ -147,6 +149,7 @@ const threadBindingSchema = z.object({
147
149
  model: optionalStringSchema,
148
150
  preserveNativeModel: z.literal(true).optional().catch(void 0),
149
151
  pendingSupervisionBranch: pendingSupervisionBranchSchema.optional(),
152
+ pendingResumeConfiguration: z.literal(true).optional(),
150
153
  modelProvider: z.string().transform((value) => value.trim()).pipe(z.string().min(1)).optional().catch(void 0),
151
154
  approvalPolicy: z.preprocess((value) => value === "on-failure" ? "on-request" : value, z.enum([
152
155
  "never",
@@ -318,11 +321,12 @@ function scopeCodexRunBindingStore(params) {
318
321
  ...params.bindingStore,
319
322
  read: (identity) => params.bindingStore.read(mapIdentity(identity)),
320
323
  hasOtherThreadOwner: (threadId, identity) => params.bindingStore.hasOtherThreadOwner(threadId, identity ? mapIdentity(identity) : void 0),
321
- mutate: (identity, mutation) => params.bindingStore.mutate(mapIdentity(identity), mutation),
324
+ mutate: (identity, mutation, assertCurrent) => params.bindingStore.mutate(mapIdentity(identity), mutation, assertCurrent),
322
325
  prepareSessionGenerationReclaim: (identity) => params.bindingStore.prepareSessionGenerationReclaim(mapSessionIdentity(identity)),
323
326
  adoptSessionGeneration: (identity, expectedPreviousSessionId) => params.bindingStore.adoptSessionGeneration(mapSessionIdentity(identity), expectedPreviousSessionId),
324
327
  resetSessionGeneration: (identity) => params.bindingStore.resetSessionGeneration(mapSessionIdentity(identity)),
325
328
  retireSessionGeneration: (identity) => params.bindingStore.retireSessionGeneration(mapSessionIdentity(identity)),
329
+ withSessionDeletion: (identity, assertCurrent, run) => params.bindingStore.withSessionDeletion(mapSessionIdentity(identity), assertCurrent, run),
326
330
  withThreadArchiveFence: (run) => params.bindingStore.withThreadArchiveFence(run),
327
331
  withLease: (identity, run) => params.bindingStore.withLease(mapIdentity(identity), run)
328
332
  };
@@ -382,9 +386,10 @@ function createCodexAppServerBindingStore(state) {
382
386
  }
383
387
  };
384
388
  const renewLease = (key, owner) => {
385
- if (owner.failure) return;
389
+ if (owner.failure || owner.phase !== "held") return;
386
390
  try {
387
391
  let renewed = false;
392
+ owner.assertCurrent?.();
388
393
  const stored = update(key, (raw) => {
389
394
  const current = readStoredCodexAppServerBinding(raw);
390
395
  if (raw !== void 0 && !current) throw new Error(`Invalid Codex app-server binding row: ${key}`);
@@ -405,15 +410,18 @@ function createCodexAppServerBindingStore(state) {
405
410
  owner.failure = bindingLeaseLostError(key, error);
406
411
  }
407
412
  };
408
- const transactKey = async (key, apply, ttlMs) => {
413
+ const transactKey = async (key, apply, ttlMs, assertCurrent) => {
409
414
  const deadline = Date.now() + BINDING_LEASE_WAIT_MS;
410
415
  while (true) {
411
416
  let busy = false;
412
417
  let leaseLost = false;
413
418
  let result;
414
419
  const ownedLease = leaseContext.getStore()?.get(key);
420
+ if (ownedLease && ownedLease.phase !== "held") throw bindingLeaseLostError(key);
415
421
  if (ownedLease?.failure) throw ownedLease.failure;
416
422
  const ownedToken = ownedLease?.token;
423
+ assertCurrent?.();
424
+ ownedLease?.assertCurrent?.();
417
425
  update(key, (raw) => {
418
426
  const current = readStoredCodexAppServerBinding(raw);
419
427
  if (raw !== void 0 && !current) throw new Error(`Invalid Codex app-server binding row: ${key}`);
@@ -441,6 +449,115 @@ function createCodexAppServerBindingStore(state) {
441
449
  await sleep(BINDING_LEASE_RETRY_INTERVAL_MS);
442
450
  }
443
451
  };
452
+ const withBindingLease = async (identity, run, options = {}) => {
453
+ options.assertCurrent?.();
454
+ const key = bindingStoreKey(identity);
455
+ const owned = leaseContext.getStore();
456
+ const existingOwner = owned?.get(key);
457
+ if (existingOwner) {
458
+ if (existingOwner.phase !== "held") throw bindingLeaseLostError(key);
459
+ const failureBeforeRun = existingOwner.failure;
460
+ if (failureBeforeRun) throw failureBeforeRun;
461
+ const result = await run();
462
+ options.assertCurrent?.();
463
+ const failureAfterRun = existingOwner.failure;
464
+ if (failureAfterRun) throw failureAfterRun;
465
+ return result;
466
+ }
467
+ const token = randomUUID();
468
+ const acquired = await transactKey(key, (current) => {
469
+ if (current?.state === "cleared" && current.retired === true && ownsStoredSessionGeneration(identity, current) && !options.allowRetired) return { result: false };
470
+ const lease = {
471
+ token,
472
+ expiresAt: Date.now() + BINDING_LEASE_STALE_MS
473
+ };
474
+ if (current?.state === "active") return {
475
+ result: true,
476
+ next: {
477
+ ...current,
478
+ ...preservedSessionGeneration(identity, current),
479
+ lease
480
+ }
481
+ };
482
+ if (current?.state === "cleared" && current.retired === true) return {
483
+ result: true,
484
+ next: {
485
+ ...current,
486
+ lease
487
+ }
488
+ };
489
+ return {
490
+ result: true,
491
+ next: {
492
+ version: 1,
493
+ state: "cleared",
494
+ ...preservedSessionGeneration(identity, current),
495
+ lease
496
+ }
497
+ };
498
+ }, void 0, options.assertCurrent);
499
+ options.assertCurrent?.();
500
+ if (!acquired) throw new Error(`Codex binding generation was retired: ${key}`);
501
+ const owner = {
502
+ token,
503
+ phase: "held",
504
+ assertCurrent: options.assertCurrent
505
+ };
506
+ const nested = new Map(owned);
507
+ nested.set(key, owner);
508
+ const heartbeat = setInterval(() => renewLease(key, owner), BINDING_LEASE_RENEW_INTERVAL_MS);
509
+ heartbeat.unref();
510
+ try {
511
+ const result = await leaseContext.run(nested, run);
512
+ options.assertCurrent?.();
513
+ if (owner.failure) throw owner.failure;
514
+ return result;
515
+ } finally {
516
+ clearInterval(heartbeat);
517
+ owner.phase = "closed";
518
+ options.assertCurrent?.();
519
+ try {
520
+ const current = readStoredCodexAppServerBinding(state.lookup(key));
521
+ if (current?.lease?.token === token) {
522
+ const ttlMs = current.state === "active" || current.retired === true && !key.startsWith("session:") ? void 0 : current.retired === true ? PHYSICAL_SESSION_RETIRE_TTL_MS : 1;
523
+ options.assertCurrent?.();
524
+ update(key, (raw) => {
525
+ const stored = readStoredCodexAppServerBinding(raw);
526
+ if (stored?.lease?.token !== token) return;
527
+ const { lease: _lease, ...released } = stored;
528
+ return released;
529
+ }, ttlMs === void 0 ? void 0 : { ttlMs });
530
+ }
531
+ } catch (error) {
532
+ options.assertCurrent?.();
533
+ embeddedAgentLog.warn("failed to release codex app-server binding lease", {
534
+ key,
535
+ error
536
+ });
537
+ }
538
+ }
539
+ };
540
+ const transitionSessionGeneration = async (identity, mode) => {
541
+ return await runBindingMutation(async () => {
542
+ const key = bindingStoreKey(identity);
543
+ const ttlMs = mode === "reset" ? leaseContext.getStore()?.has(key) ? void 0 : 1 : identity.sessionKey?.trim() ? void 0 : PHYSICAL_SESSION_RETIRE_TTL_MS;
544
+ return await transactKey(key, (current, leaseToken) => {
545
+ if (!current) return { result: "absent" };
546
+ if (!ownsStoredSessionGeneration(identity, current)) return { result: "conflict" };
547
+ if (current.state === "cleared" && current.retired === true) return { result: mode === "retire" ? "applied" : "conflict" };
548
+ return {
549
+ result: "applied",
550
+ next: {
551
+ version: 1,
552
+ state: "cleared",
553
+ ...mode === "retire" ? { retired: true } : {},
554
+ ...storedSessionGeneration(identity, current),
555
+ ...current.lease && current.lease.token === leaseToken ? { lease: current.lease } : {}
556
+ }
557
+ };
558
+ }, ttlMs);
559
+ });
560
+ };
444
561
  return {
445
562
  async read(identity) {
446
563
  const key = bindingStoreKey(identity);
@@ -485,7 +602,7 @@ function createCodexAppServerBindingStore(state) {
485
602
  expectedPreviousSessionId: currentSessionId
486
603
  };
487
604
  },
488
- async mutate(identity, mutation) {
605
+ async mutate(identity, mutation, assertCurrent) {
489
606
  return await runBindingMutation(async () => {
490
607
  const key = bindingStoreKey(identity);
491
608
  const retainLegacyClear = mutation.kind === "clear" && key.startsWith("conversation:legacy-");
@@ -523,9 +640,9 @@ function createCodexAppServerBindingStore(state) {
523
640
  const active = ownsGeneration ? storedActive : void 0;
524
641
  const retiredGeneration = current?.state === "cleared" && current.retired === true && ownsGeneration;
525
642
  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);
643
+ const clearsPendingSupervisionOwner = mutation.kind === "clear" && matchesPendingSupervisionClear(active?.binding, mutation.threadId, mutation.expectedPendingSupervisionBranch);
527
644
  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 };
645
+ 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.expectedPendingSupervisionBranch ? !clearsPendingSupervisionOwner : mutation.threadId !== void 0 && active?.binding.threadId !== mutation.threadId || active?.binding.connectionScope === "supervision"))) return { result: false };
529
646
  if (mutation.kind === "clear" && retiredGeneration) return { result: true };
530
647
  if (mutation.kind === "clear") return {
531
648
  result: true,
@@ -563,7 +680,7 @@ function createCodexAppServerBindingStore(state) {
563
680
  ...ownedLease
564
681
  }
565
682
  };
566
- }, mutation.kind === "clear" && !retainLegacyClear && !leaseContext.getStore()?.has(key) ? 1 : void 0);
683
+ }, mutation.kind === "clear" && !retainLegacyClear && !leaseContext.getStore()?.has(key) ? 1 : void 0, assertCurrent);
567
684
  });
568
685
  },
569
686
  async adoptSessionGeneration(identity, expectedPreviousSessionId) {
@@ -586,45 +703,8 @@ function createCodexAppServerBindingStore(state) {
586
703
  });
587
704
  });
588
705
  },
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
- },
706
+ resetSessionGeneration: (identity) => transitionSessionGeneration(identity, "reset"),
707
+ retireSessionGeneration: (identity) => transitionSessionGeneration(identity, "retire"),
628
708
  async withThreadArchiveFence(run) {
629
709
  pendingArchives += 1;
630
710
  const operation = archiveTail.then(async () => {
@@ -638,79 +718,73 @@ function createCodexAppServerBindingStore(state) {
638
718
  pendingArchives -= 1;
639
719
  }
640
720
  },
641
- async withLease(identity, run) {
721
+ async withSessionDeletion(identity, assertCurrent, run) {
642
722
  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
723
+ const deleteIf = state.deleteIf?.bind(state);
724
+ if (!deleteIf) throw new Error("Codex session deletion requires conditional plugin-state deletion");
725
+ return await runBindingMutation(async () => {
726
+ assertCurrent();
727
+ if (state.lookup(key) === void 0) {
728
+ let active = true;
729
+ try {
730
+ return await run(void 0, {
731
+ commit() {
732
+ assertCurrent();
733
+ if (!active || state.lookup(key) !== void 0) throw new Error("Codex binding changed before session deletion");
734
+ },
735
+ rollback() {}
736
+ });
737
+ } finally {
738
+ active = false;
682
739
  }
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;
740
+ }
741
+ return await withBindingLease(identity, async () => {
742
+ const owner = leaseContext.getStore().get(key);
743
+ const stored = readStoredCodexAppServerBinding(state.lookup(key));
744
+ if (!stored || !ownsStoredSessionGeneration(identity, stored)) throw new Error("Codex binding generation changed before session deletion");
745
+ const { lease: _lease, ...expectedValue } = stored;
746
+ let deleted;
747
+ let active = true;
748
+ const assertActive = () => {
749
+ assertCurrent();
750
+ if (!active || owner.phase === "closed" || owner.failure) throw owner.failure ?? bindingLeaseLostError(key);
702
751
  };
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 });
752
+ try {
753
+ return await run(stored.state === "active" ? stored.binding : void 0, {
754
+ commit() {
755
+ assertActive();
756
+ if (deleted) return;
757
+ const current = state.lookup(key);
758
+ const { lease, ...value } = readStoredCodexAppServerBinding(current) ?? {};
759
+ 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");
760
+ deleted = current;
761
+ owner.phase = "deleted";
762
+ },
763
+ rollback() {
764
+ assertActive();
765
+ if (!deleted) return;
766
+ const restored = {
767
+ ...deleted,
768
+ lease: {
769
+ token: owner.token,
770
+ expiresAt: Date.now() + BINDING_LEASE_STALE_MS
771
+ }
772
+ };
773
+ if (!state.registerIfAbsent(key, restored)) throw new Error("Codex binding changed before session deletion rollback");
774
+ deleted = void 0;
775
+ owner.phase = "held";
776
+ }
777
+ });
778
+ } finally {
779
+ active = false;
705
780
  }
706
- } catch (error) {
707
- embeddedAgentLog.warn("failed to release codex app-server binding lease", {
708
- key,
709
- error
710
- });
711
- }
712
- }
713
- }
781
+ }, {
782
+ allowRetired: true,
783
+ assertCurrent
784
+ });
785
+ });
786
+ },
787
+ withLease: withBindingLease
714
788
  };
715
789
  }
716
790
  function matchesPendingSupervisionBranch(binding, expected) {
@@ -725,9 +799,8 @@ function isSameSupervisionOwner(current, replacement) {
725
799
  return replacement.connectionScope === "supervision" && replacement.threadId === current.threadId && replacement.supervisionSourceThreadId === current.supervisionSourceThreadId;
726
800
  }
727
801
  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);
802
+ if (!expected || threadId !== expected.sourceThreadId) return false;
803
+ return !binding || binding.connectionScope === "supervision" && binding.supervisionSourceThreadId === expected.sourceThreadId && matchesPendingSupervisionBranch(binding, expected);
731
804
  }
732
805
  /** Stable plugin-state key for one current binding owner. */
733
806
  function bindingStoreKey(identity) {
@@ -736,7 +809,7 @@ function bindingStoreKey(identity) {
736
809
  const sessionId = identity.sessionId.trim();
737
810
  if (!rawAgentId) throw new Error("Codex app-server binding requires an agent id");
738
811
  if (!sessionId) throw new Error("Codex app-server binding requires a session id");
739
- const agentId = resolveSessionAgentIds$1({ agentId: rawAgentId }).sessionAgentId;
812
+ const agentId = resolveSessionAgentIdsStrict({ agentId: rawAgentId }).sessionAgentId;
740
813
  const sessionKey = identity.sessionKey?.trim();
741
814
  if (sessionKey) return `session-key:${agentId}:${createHash("sha256").update(sessionKey).digest("base64url")}`;
742
815
  return `session:${agentId}:${sessionId}`;