@openclaw/codex 2026.9.1-beta.1 → 2026.9.2

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 (81) hide show
  1. package/dist/api.js +8 -9
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-Bp1ZOnk2.js} +105 -75
  3. package/dist/attempt-context-CSazkWns.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-DxGJRYbY.js} +53 -57
  6. package/dist/{capabilities-BRMh0aIq.js → capabilities-B1mgvpP8.js} +2 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-DEsIufkt.js → command-formatters-DSbGMXA0.js} +5 -4
  9. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-DOJvGv6-.js} +243 -148
  10. package/dist/{compact-BglNFZrQ.js → compact-Y_xBqrHN.js} +109 -65
  11. package/dist/{config-giW8X0QA.js → config-CyapYSji.js} +328 -374
  12. package/dist/config-utils-C57JiJrU.js +69 -0
  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 +144 -14
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BwpvTxaX.js} +1119 -1135
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-CY9Jn4bA.js} +4 -3
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-B9kp6iGC.js} +15 -6
  19. package/dist/event-projector-usage-ZqEVbAEY.js +212 -0
  20. package/dist/{harness-Csz0F-DK.js → harness-Czh2Sdoc.js} +65 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
  23. package/dist/index.js +495 -457
  24. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-CRh-51AK.js} +9 -21
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-DcpnJF9x.js} +117 -80
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-DT5vo6qo.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-CcrgSMQR.js} +26 -29
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-B3bvQGcg.js} +63 -122
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-nfbUOtUB.js} +10 -7
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-aQVdyL5g.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-B7gHXJ3d.js} +44 -30
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BwCLTY_U.js} +6 -6
  34. package/dist/provider-capabilities-BpG9Ltc5.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-Fx_l4eNa.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-C8H22rPi.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-BzthFQSM.js} +1616 -2634
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-Bh_gf6-t.js} +22 -15
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-4Ki04sW-.js} +282 -399
  40. package/dist/session-binding-record-CyaXZvTe.js +270 -0
  41. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-B47uwekb.js} +812 -504
  42. package/dist/{session-cli-DMc4oS2w.js → session-cli-BjLhpmS1.js} +1 -1
  43. package/dist/session-history-DWmMqiqW.js +93 -0
  44. package/dist/session-history-read-LHldwvyh.js +36 -0
  45. package/dist/session-history-worker-runtime.js +48 -0
  46. package/dist/session-history.worker-zZVcx0ME.js +267 -0
  47. package/dist/session-history.worker.js +2 -0
  48. package/dist/session-initialization-BCFVnLL4.js +75 -0
  49. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-BaueHmSj.js} +2 -2
  50. package/dist/session-retirement-DNdmYdFc.js +97 -0
  51. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-DReTpGz6.js} +20 -4
  52. package/dist/settled-turn-context-BOM3Y1pl.js +45 -0
  53. package/dist/settled-turn-finalizer-BI-b1sry.js +115 -0
  54. package/dist/{shared-client-DKz23hHY.js → shared-client-CKhX2kFt.js} +3659 -847
  55. package/dist/{side-question-DjA_jsNe.js → side-question-B6DWAw1A.js} +168 -81
  56. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DPK30jad.js} +2412 -2145
  57. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-CbucAmmp.js} +27 -16
  58. package/dist/thread-policy-CTalhWFy.js +105 -0
  59. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-BLgqkUc1.js} +61 -9
  60. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-_njEB84z.js} +114 -110
  61. package/dist/transcript-mirror-attestation-Byl34PC-.js +76 -0
  62. package/dist/transport-stdio-CzTngV0j.js +728 -0
  63. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  64. package/dist/upstream-session-fork-1fwbNKzQ.js +886 -0
  65. package/dist/{usage-ROIaW3wo.js → usage-i9fv5-4m.js} +3 -3
  66. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-DKQn67A7.js} +3 -3
  67. package/openclaw.plugin.json +1 -30
  68. package/package.json +6 -6
  69. package/dist/binding-connection-BMvPX73o.js +0 -45
  70. package/dist/command-rpc-CCeCkf_v.js +0 -90
  71. package/dist/event-projector-usage-Bk1I90Bg.js +0 -328
  72. package/dist/incognito-session-ET_ufDH-.js +0 -2
  73. package/dist/model-catalog-i9rNBthX.js +0 -48
  74. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  75. package/dist/request-DXXGHko0.js +0 -157
  76. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  77. package/dist/session-retirement-CcNC3YSY.js +0 -36
  78. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  79. package/dist/thread-resume-BE9qNEW0.js +0 -30
  80. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  81. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
@@ -1,13 +1,13 @@
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 { g as CODEX_PLUGIN_MARKETPLACE_NAME_PATTERN } from "./config-utils-C57JiJrU.js";
3
+ import { a as readCodexBindingTimestamp, c as readStoredCodexAppServerBinding, d as validateBindingForWrite, i as readCodexBindingSessionEntry, l as sessionBindingIdentity, n as ownsStoredSessionGeneration, o as readCodexSessionOwnershipBinding, r as readCodexAppServerThreadBinding, s as readCurrentCodexAppServerBinding, t as bindingStoreKey, u as stripUndefinedBinding } from "./session-binding-record-CyaXZvTe.js";
3
4
  import { n as CODEX_APP_SERVER_BINDING_NAMESPACE, t as CODEX_APP_SERVER_BINDING_MAX_ENTRIES } from "./session-binding-meta-B7aEMU7g.js";
4
5
  import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
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";
8
- import { z } from "zod";
7
+ import { AgentHarnessPreflightError, AgentHarnessSessionSupersededError, embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
8
+ import { isDeepStrictEqual } from "node:util";
9
+ import { ensureAuthProfileStore, resolveDefaultAgentDir, resolveProviderIdForAuth } from "openclaw/plugin-sdk/agent-runtime";
9
10
  import { AsyncLocalStorage } from "node:async_hooks";
10
- import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
11
11
  //#region extensions/codex/src/app-server/session-binding.ts
12
12
  /** SQLite-backed Codex app-server thread bindings. */
13
13
  var session_binding_exports = /* @__PURE__ */ __exportAll({
@@ -28,8 +28,10 @@ var session_binding_exports = /* @__PURE__ */ __exportAll({
28
28
  readStoredCodexAppServerBinding: () => readStoredCodexAppServerBinding,
29
29
  reclaimCurrentCodexSessionGeneration: () => reclaimCurrentCodexSessionGeneration,
30
30
  resolveCodexRunSessionBindingAuthority: () => resolveCodexRunSessionBindingAuthority,
31
+ resolveCodexSessionBinding: () => resolveCodexSessionBinding,
31
32
  scopeCodexRunBindingStore: () => scopeCodexRunBindingStore,
32
- sessionBindingIdentity: () => sessionBindingIdentity
33
+ sessionBindingIdentity: () => sessionBindingIdentity,
34
+ validateBindingForWrite: () => validateBindingForWrite
33
35
  });
34
36
  const CODEX_APP_SERVER_NATIVE_AUTH_PROVIDER = "openai";
35
37
  const PUBLIC_OPENAI_MODEL_PROVIDER = "openai";
@@ -40,220 +42,52 @@ const BINDING_LEASE_STALE_MS = 65e3;
40
42
  const BINDING_LEASE_WAIT_MS = 7e4;
41
43
  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
- /** Resolves the same agent scope OpenClaw uses for transcript/session ownership. */
44
- function sessionBindingIdentity(params) {
45
- const { sessionAgentId } = resolveSessionAgentIds$1(params);
46
- const sessionKey = params.sessionKey?.trim();
47
- return {
48
- kind: "session",
49
- agentId: sessionAgentId,
50
- sessionId: params.sessionId,
51
- ...sessionKey ? { sessionKey } : {}
52
- };
53
- }
54
45
  /** Decides whether a run may share the durable stable-key binding owner. */
55
46
  function resolveCodexRunSessionBindingAuthority(params) {
56
- const sessionKey = params.identity.sessionKey?.trim();
57
- if (!sessionKey) return "ephemeral";
58
- try {
59
- const storePath = params.storePath?.trim() || resolveStorePath(params.config?.session?.store, { agentId: params.identity.agentId });
60
- const entry = getSessionEntry({
61
- agentId: params.identity.agentId,
62
- hydrateSkillPromptRefs: false,
63
- readConsistency: "latest",
64
- sessionKey,
65
- storePath
66
- });
67
- if (!entry) return "ephemeral";
68
- return entry.sessionId === params.identity.sessionId ? "current" : "superseded";
69
- } catch {
70
- return "superseded";
71
- }
47
+ return captureCodexSessionGenerationAuthority(params)[0];
48
+ }
49
+ function captureCodexSessionGenerationAuthority(params, assertCallerCurrent = () => {}) {
50
+ const readEntry = () => {
51
+ try {
52
+ return readCodexBindingSessionEntry(params);
53
+ } catch {
54
+ return null;
55
+ }
56
+ };
57
+ const entry = readEntry();
58
+ const current = entry?.sessionId === params.identity.sessionId;
59
+ const authority = entry === void 0 ? "ephemeral" : current ? "current" : "superseded";
60
+ const previousSessionId = current ? entry.previousSessionId : void 0;
61
+ const assertHostCurrent = () => {
62
+ if (authority === "ephemeral") return;
63
+ const latest = readEntry();
64
+ if (authority !== "current" || !latest || latest.sessionId !== params.identity.sessionId || latest.previousSessionId !== previousSessionId) throw createCodexSessionGenerationSupersededError(params.identity.sessionId);
65
+ };
66
+ const assertCurrent = () => {
67
+ assertCallerCurrent();
68
+ assertHostCurrent();
69
+ };
70
+ return [
71
+ authority,
72
+ previousSessionId,
73
+ assertHostCurrent,
74
+ assertCurrent
75
+ ];
72
76
  }
73
77
  /** Builds the terminal coordination error used when a newer OpenClaw session owns the binding. */
74
78
  function createCodexSessionGenerationSupersededError(sessionId) {
75
79
  return new AgentHarnessSessionSupersededError(`Codex session generation is no longer current: ${sessionId}`);
76
80
  }
77
- const optionalStringSchema = z.string().optional().catch(void 0);
78
- const optionalBooleanSchema = z.boolean().optional().catch(void 0);
79
- const optionalNonBlankStringSchema = z.string().refine((value) => Boolean(value.trim())).optional().catch(void 0);
80
- const optionalTimestampSchema = z.string().refine((value) => Number.isFinite(Date.parse(value))).optional().catch(void 0);
81
- const pendingSupervisionBranchSchema = z.object({
82
- sourceThreadId: z.string().trim().min(1),
83
- connectionFingerprint: z.string().trim().min(1).optional(),
84
- lastTurnId: z.string().trim().min(1).optional(),
85
- cleanupThreadIds: z.array(z.string().trim().min(1)).max(2).optional()
86
- }).strict().superRefine((pending, context) => {
87
- const cleanupThreadIds = pending.cleanupThreadIds ?? [];
88
- if (new Set(cleanupThreadIds).size !== cleanupThreadIds.length) context.addIssue({
89
- code: "custom",
90
- message: "pending supervision cleanup thread ids must be unique"
91
- });
92
- if (cleanupThreadIds.includes(pending.sourceThreadId)) context.addIssue({
93
- code: "custom",
94
- message: "pending supervision cleanup cannot target its source"
95
- });
96
- });
97
- const contextEngineProjectionSchema = z.object({
98
- schemaVersion: z.literal(1),
99
- mode: z.literal("thread_bootstrap"),
100
- epoch: z.string().refine((value) => Boolean(value.trim())),
101
- fingerprint: optionalStringSchema
102
- }).strict();
103
- const contextEngineSchema = z.object({
104
- schemaVersion: z.literal(1),
105
- engineId: z.string(),
106
- policyFingerprint: z.string(),
107
- projection: contextEngineProjectionSchema.optional().catch(void 0)
108
- }).strict();
109
- const destructiveApprovalModeSchema = z.enum([
110
- "allow",
111
- "deny",
112
- "auto",
113
- "ask"
114
- ]).optional().catch(void 0);
115
- const accountAppPolicyEntrySchema = z.object({
116
- source: z.literal("account"),
117
- appName: z.string(),
118
- allowDestructiveActions: z.boolean(),
119
- allowOpenWorld: z.boolean().optional(),
120
- destructiveApprovalMode: destructiveApprovalModeSchema,
121
- mcpServerNames: z.array(z.string())
122
- }).strict();
123
- const pluginAppPolicyEntrySchema = z.object({
124
- source: z.literal("plugin").optional(),
125
- configKey: z.string(),
126
- marketplaceName: z.string().regex(CODEX_PLUGIN_MARKETPLACE_NAME_PATTERN),
127
- pluginName: z.string(),
128
- allowDestructiveActions: z.boolean(),
129
- allowOpenWorld: z.boolean().optional(),
130
- destructiveApprovalMode: destructiveApprovalModeSchema,
131
- mcpServerNames: z.array(z.string())
132
- }).strict();
133
- const pluginAppPolicyContextSchema = z.object({
134
- fingerprint: z.string(),
135
- apps: z.record(z.string(), z.union([accountAppPolicyEntrySchema, pluginAppPolicyEntrySchema])),
136
- pluginAppIds: z.record(z.string(), z.array(z.string())).default({})
137
- }).strict();
138
- const threadBindingSchema = z.object({
139
- threadId: z.string().refine((value) => Boolean(value.trim())),
140
- clientId: optionalStringSchema,
141
- cwd: z.string(),
142
- rolloutPath: optionalNonBlankStringSchema,
143
- connectionScope: z.literal("supervision").optional(),
144
- supervisionSourceThreadId: z.string().trim().min(1).optional(),
145
- authProfileId: optionalStringSchema,
146
- agentWorkspaceDeveloperInstructions: optionalNonBlankStringSchema,
147
- model: optionalStringSchema,
148
- preserveNativeModel: z.literal(true).optional().catch(void 0),
149
- pendingSupervisionBranch: pendingSupervisionBranchSchema.optional(),
150
- modelProvider: z.string().transform((value) => value.trim()).pipe(z.string().min(1)).optional().catch(void 0),
151
- approvalPolicy: z.preprocess((value) => value === "on-failure" ? "on-request" : value, z.enum([
152
- "never",
153
- "on-request",
154
- "untrusted"
155
- ]).optional()).catch(void 0),
156
- sandbox: z.enum([
157
- "read-only",
158
- "workspace-write",
159
- "danger-full-access"
160
- ]).optional().catch(void 0),
161
- serviceTier: z.preprocess(normalizeCodexServiceTier, z.custom((value) => typeof value === "string").optional()).optional().catch(void 0),
162
- networkProxyProfileName: optionalStringSchema,
163
- networkProxyConfigFingerprint: optionalStringSchema,
164
- dynamicToolsFingerprint: optionalStringSchema,
165
- dynamicToolsContainDeferred: optionalBooleanSchema,
166
- webSearchThreadConfigFingerprint: optionalStringSchema,
167
- nativeSkillIsolationFingerprint: optionalStringSchema,
168
- userMcpServersFingerprint: optionalStringSchema,
169
- mcpServersFingerprint: optionalStringSchema,
170
- configuredMcpOwnershipVersion: z.literal(1).optional().catch(void 0),
171
- ringZeroConfigFingerprint: optionalStringSchema,
172
- ringZeroClientInstanceId: optionalStringSchema,
173
- /** Durable fact preventing a later unrestricted turn from widening this thread. */
174
- nativeToolPolicyRestricted: z.literal(true).optional().catch(void 0),
175
- nativeHookRelayGeneration: optionalNonBlankStringSchema,
176
- appServerRuntimeFingerprint: optionalStringSchema,
177
- pluginAppsFingerprint: optionalStringSchema,
178
- pluginAppsInputFingerprint: optionalStringSchema,
179
- pluginAppPolicyContext: pluginAppPolicyContextSchema.optional().catch(void 0),
180
- contextEngine: contextEngineSchema.optional().catch(void 0),
181
- environmentSelectionFingerprint: optionalStringSchema,
182
- conversationStartId: optionalStringSchema,
183
- conversationSourceTransferComplete: z.literal(true).optional().catch(void 0),
184
- historyCoveredThrough: optionalTimestampSchema,
185
- continuityCalibration: z.object({
186
- promptChars: z.number().int().positive(),
187
- inputTokens: z.number().int().positive()
188
- }).optional().catch(void 0)
189
- }).superRefine((binding, context) => {
190
- if (binding.connectionScope === "supervision") {
191
- if (!binding.supervisionSourceThreadId) context.addIssue({
192
- code: "custom",
193
- message: "supervision connection ownership requires its native source thread id"
194
- });
195
- if (binding.preserveNativeModel !== true) context.addIssue({
196
- code: "custom",
197
- message: "supervision connection ownership requires native model ownership"
198
- });
199
- if (binding.conversationSourceTransferComplete !== true) context.addIssue({
200
- code: "custom",
201
- message: "supervision connection ownership requires a completed source transfer"
202
- });
203
- if (!binding.pendingSupervisionBranch && (!binding.model?.trim() || !binding.modelProvider)) context.addIssue({
204
- code: "custom",
205
- message: "materialized supervision bindings require a native model and provider"
206
- });
207
- }
208
- if (binding.supervisionSourceThreadId && binding.connectionScope !== "supervision") context.addIssue({
209
- code: "custom",
210
- message: "a supervision source thread id requires supervision connection ownership"
211
- });
212
- if (!binding.pendingSupervisionBranch) return;
213
- if (binding.threadId !== binding.pendingSupervisionBranch.sourceThreadId) context.addIssue({
214
- code: "custom",
215
- message: "pending supervision source must match the provisional thread binding"
216
- });
217
- if (binding.supervisionSourceThreadId !== binding.pendingSupervisionBranch.sourceThreadId) context.addIssue({
218
- code: "custom",
219
- message: "pending supervision source must match its durable source identity"
220
- });
221
- if (binding.preserveNativeModel !== true) context.addIssue({
222
- code: "custom",
223
- message: "pending supervision bindings must defer model selection to Codex App Server"
224
- });
225
- if (binding.connectionScope !== "supervision") context.addIssue({
226
- code: "custom",
227
- message: "pending supervision bindings require supervision connection ownership"
228
- });
229
- });
230
81
  var CodexSupervisionBindingReplacementError = class extends Error {
231
82
  constructor(threadId, operation) {
232
83
  super(`Refusing to replace supervised Codex thread ${threadId} while ${operation}; its native user-home connection and model ownership must be preserved`);
233
84
  this.name = "CodexSupervisionBindingReplacementError";
234
85
  }
235
86
  };
236
- function assertCodexBindingMayBeReplaced(binding, operation) {
87
+ function assertCodexBindingMayBeReplaced(binding, operation, expected) {
88
+ if (expected) throw new AgentHarnessPreflightError(`Codex native model ownership prevents ${operation}. Continue or compact the original session in its native runtime, or create a new chat with a concrete model; the original binding was preserved.`);
237
89
  if (binding?.connectionScope === "supervision") throw new CodexSupervisionBindingReplacementError(binding.threadId, operation);
238
90
  }
239
- const bindingLeaseSchema = z.object({
240
- token: z.string().refine((value) => Boolean(value.trim())),
241
- expiresAt: z.number().finite()
242
- });
243
- const storedSessionIdSchema = z.string().transform((value) => value.trim()).pipe(z.string().min(1)).optional().catch(void 0);
244
- const storedBindingSchema = z.discriminatedUnion("state", [z.object({
245
- version: z.literal(1),
246
- state: z.literal("active"),
247
- binding: threadBindingSchema,
248
- sessionId: storedSessionIdSchema,
249
- lease: bindingLeaseSchema.optional().catch(void 0)
250
- }), z.object({
251
- version: z.literal(1),
252
- state: z.literal("cleared"),
253
- sessionId: storedSessionIdSchema,
254
- lease: bindingLeaseSchema.optional().catch(void 0),
255
- retired: z.literal(true).optional().catch(void 0)
256
- })]);
257
91
  function hashCodexAppServerBindingFingerprint(canonical) {
258
92
  return `sha256:${createHash("sha256").update(canonical).digest("hex")}`;
259
93
  }
@@ -288,7 +122,7 @@ function createStoredCodexAppServerBinding(value, options = {}) {
288
122
  const record = normalizeLegacyBindingFingerprints(rawRecord);
289
123
  if (record.schemaVersion !== 1 && record.schemaVersion !== 2) return;
290
124
  const pluginAppPolicyContext = readPluginAppPolicyContext(record.pluginAppPolicyContext, record.schemaVersion);
291
- const historyCoveredThrough = readTimestamp(record.historyCoveredThrough) ?? readTimestamp(record.updatedAt) ?? readTimestamp(record.createdAt) ?? readTimestamp(options.now) ?? (/* @__PURE__ */ new Date()).toISOString();
125
+ const historyCoveredThrough = readCodexBindingTimestamp(record.historyCoveredThrough) ?? readCodexBindingTimestamp(record.updatedAt) ?? readCodexBindingTimestamp(record.createdAt) ?? readCodexBindingTimestamp(options.now) ?? (/* @__PURE__ */ new Date()).toISOString();
292
126
  const authProfileId = typeof record.authProfileId === "string" ? record.authProfileId : void 0;
293
127
  const binding = readCodexAppServerThreadBinding({
294
128
  ...record,
@@ -318,37 +152,72 @@ function scopeCodexRunBindingStore(params) {
318
152
  ...params.bindingStore,
319
153
  read: (identity) => params.bindingStore.read(mapIdentity(identity)),
320
154
  hasOtherThreadOwner: (threadId, identity) => params.bindingStore.hasOtherThreadOwner(threadId, identity ? mapIdentity(identity) : void 0),
321
- mutate: (identity, mutation) => params.bindingStore.mutate(mapIdentity(identity), mutation),
155
+ mutate: (identity, mutation, assertCurrent) => params.bindingStore.mutate(mapIdentity(identity), mutation, assertCurrent),
322
156
  prepareSessionGenerationReclaim: (identity) => params.bindingStore.prepareSessionGenerationReclaim(mapSessionIdentity(identity)),
323
- adoptSessionGeneration: (identity, expectedPreviousSessionId) => params.bindingStore.adoptSessionGeneration(mapSessionIdentity(identity), expectedPreviousSessionId),
157
+ adoptSessionGeneration: (identity, expectedPreviousSessionId, assertCurrent) => params.bindingStore.adoptSessionGeneration(mapSessionIdentity(identity), expectedPreviousSessionId, assertCurrent),
324
158
  resetSessionGeneration: (identity) => params.bindingStore.resetSessionGeneration(mapSessionIdentity(identity)),
325
159
  retireSessionGeneration: (identity) => params.bindingStore.retireSessionGeneration(mapSessionIdentity(identity)),
160
+ withSessionDeletion: (identity, assertCurrent, run) => params.bindingStore.withSessionDeletion(mapSessionIdentity(identity), assertCurrent, run),
326
161
  withThreadArchiveFence: (run) => params.bindingStore.withThreadArchiveFence(run),
327
162
  withLease: (identity, run) => params.bindingStore.withLease(mapIdentity(identity), run)
328
163
  };
329
164
  }
330
- /** Lets the authoritative OpenClaw session generation claim a stale stable binding row. */
331
- async function reclaimCurrentCodexSessionGeneration(params) {
332
- const sessionKey = params.identity.sessionKey?.trim();
333
- if (!sessionKey) return true;
165
+ async function reclaimPreparedCodexSessionGeneration(params, authority, assertCurrent = authority[3]) {
334
166
  const plan = await params.bindingStore.prepareSessionGenerationReclaim(params.identity);
167
+ assertCurrent();
335
168
  if (plan.kind === "resolved") return plan.result;
336
- try {
337
- const storePath = params.storePath?.trim() || resolveStorePath(params.config?.session?.store, { agentId: params.identity.agentId });
338
- if (getSessionEntry({
339
- agentId: params.identity.agentId,
340
- hydrateSkillPromptRefs: false,
341
- readConsistency: "latest",
342
- sessionKey,
343
- storePath
344
- })?.sessionId !== params.identity.sessionId) return false;
345
- } catch {
346
- return false;
169
+ const [state, previousSessionId, assertHostCurrent] = authority;
170
+ if (state !== "current") return false;
171
+ params.onHostGenerationVerified?.(assertHostCurrent);
172
+ if (previousSessionId === plan.expectedPreviousSessionId) {
173
+ const adopted = await params.bindingStore.adoptSessionGeneration(params.identity, previousSessionId, assertCurrent);
174
+ if (adopted !== "absent") return adopted !== "conflict";
347
175
  }
348
- return await params.bindingStore.mutate(params.identity, {
176
+ if (params.reclaimStale === false) return false;
177
+ return params.bindingStore.mutate(params.identity, {
349
178
  kind: "reclaim-generation",
350
179
  expectedPreviousSessionId: plan.expectedPreviousSessionId
351
- });
180
+ }, assertCurrent);
181
+ }
182
+ /** Lets the authoritative OpenClaw session generation claim a stale stable binding row. */
183
+ async function reclaimCurrentCodexSessionGeneration(params) {
184
+ params.assertCurrent?.();
185
+ if (!params.identity.sessionKey?.trim()) return true;
186
+ const authority = captureCodexSessionGenerationAuthority(params, params.assertCurrent);
187
+ if (authority[0] === "superseded") return false;
188
+ return reclaimPreparedCodexSessionGeneration(params, authority);
189
+ }
190
+ /** Resolve continuity before selecting native queues, catalogs, or connections. */
191
+ async function resolveCodexSessionBinding(params) {
192
+ let assertCurrent = params.assertCurrent ?? (() => {});
193
+ const assertAdmissionCurrent = () => {
194
+ assertCurrent();
195
+ params.signal?.throwIfAborted();
196
+ };
197
+ assertAdmissionCurrent();
198
+ params.assertBinding?.(readCodexSessionOwnershipBinding(params));
199
+ const identity = params.identity;
200
+ const authority = identity.kind === "session" && identity.sessionKey?.trim() ? captureCodexSessionGenerationAuthority({
201
+ ...params,
202
+ identity
203
+ }, assertCurrent) : void 0;
204
+ assertCurrent = authority?.[3] ?? assertCurrent;
205
+ assertAdmissionCurrent();
206
+ let binding = params.bindingStore.read(identity);
207
+ if (!binding && authority && identity.kind === "session") {
208
+ if (!await reclaimPreparedCodexSessionGeneration({
209
+ ...params,
210
+ identity,
211
+ reclaimStale: params.reclaimStale === true
212
+ }, authority, assertAdmissionCurrent) && params.reclaimStale) throw createCodexSessionGenerationSupersededError(identity.sessionId);
213
+ binding = params.bindingStore.read(identity);
214
+ }
215
+ assertAdmissionCurrent();
216
+ params.assertBinding?.(binding);
217
+ return {
218
+ binding,
219
+ assertCurrent
220
+ };
352
221
  }
353
222
  /** Creates the single binding facade owned by the Codex plugin runtime. */
354
223
  function createCodexAppServerBindingStore(state) {
@@ -382,9 +251,10 @@ function createCodexAppServerBindingStore(state) {
382
251
  }
383
252
  };
384
253
  const renewLease = (key, owner) => {
385
- if (owner.failure) return;
254
+ if (owner.failure || owner.phase !== "held") return;
386
255
  try {
387
256
  let renewed = false;
257
+ owner.assertCurrent?.();
388
258
  const stored = update(key, (raw) => {
389
259
  const current = readStoredCodexAppServerBinding(raw);
390
260
  if (raw !== void 0 && !current) throw new Error(`Invalid Codex app-server binding row: ${key}`);
@@ -405,15 +275,18 @@ function createCodexAppServerBindingStore(state) {
405
275
  owner.failure = bindingLeaseLostError(key, error);
406
276
  }
407
277
  };
408
- const transactKey = async (key, apply, ttlMs) => {
278
+ const transactKey = async (key, apply, ttlMs, assertCurrent) => {
409
279
  const deadline = Date.now() + BINDING_LEASE_WAIT_MS;
410
280
  while (true) {
411
281
  let busy = false;
412
282
  let leaseLost = false;
413
283
  let result;
414
284
  const ownedLease = leaseContext.getStore()?.get(key);
285
+ if (ownedLease && ownedLease.phase !== "held") throw bindingLeaseLostError(key);
415
286
  if (ownedLease?.failure) throw ownedLease.failure;
416
287
  const ownedToken = ownedLease?.token;
288
+ assertCurrent?.();
289
+ ownedLease?.assertCurrent?.();
417
290
  update(key, (raw) => {
418
291
  const current = readStoredCodexAppServerBinding(raw);
419
292
  if (raw !== void 0 && !current) throw new Error(`Invalid Codex app-server binding row: ${key}`);
@@ -441,14 +314,117 @@ function createCodexAppServerBindingStore(state) {
441
314
  await sleep(BINDING_LEASE_RETRY_INTERVAL_MS);
442
315
  }
443
316
  };
444
- return {
445
- async read(identity) {
317
+ const withBindingLease = async (identity, run, options = {}) => {
318
+ options.assertCurrent?.();
319
+ const key = bindingStoreKey(identity);
320
+ const owned = leaseContext.getStore();
321
+ const existingOwner = owned?.get(key);
322
+ if (existingOwner) {
323
+ if (existingOwner.phase !== "held") throw bindingLeaseLostError(key);
324
+ const failureBeforeRun = existingOwner.failure;
325
+ if (failureBeforeRun) throw failureBeforeRun;
326
+ const result = await run();
327
+ options.assertCurrent?.();
328
+ const failureAfterRun = existingOwner.failure;
329
+ if (failureAfterRun) throw failureAfterRun;
330
+ return result;
331
+ }
332
+ const token = randomUUID();
333
+ const acquired = await transactKey(key, (current) => {
334
+ if (current?.state === "cleared" && current.retired === true && ownsStoredSessionGeneration(identity, current) && !options.allowRetired) return { result: false };
335
+ const lease = {
336
+ token,
337
+ expiresAt: Date.now() + BINDING_LEASE_STALE_MS
338
+ };
339
+ if (current?.state === "active") return {
340
+ result: true,
341
+ next: {
342
+ ...current,
343
+ ...preservedSessionGeneration(identity, current),
344
+ lease
345
+ }
346
+ };
347
+ if (current?.state === "cleared" && current.retired === true) return {
348
+ result: true,
349
+ next: {
350
+ ...current,
351
+ lease
352
+ }
353
+ };
354
+ return {
355
+ result: true,
356
+ next: {
357
+ version: 1,
358
+ state: "cleared",
359
+ ...preservedSessionGeneration(identity, current),
360
+ lease
361
+ }
362
+ };
363
+ }, void 0, options.assertCurrent);
364
+ options.assertCurrent?.();
365
+ if (!acquired) throw new Error(`Codex binding generation was retired: ${key}`);
366
+ const owner = {
367
+ token,
368
+ phase: "held",
369
+ assertCurrent: options.assertCurrent
370
+ };
371
+ const nested = new Map(owned);
372
+ nested.set(key, owner);
373
+ const heartbeat = setInterval(() => renewLease(key, owner), BINDING_LEASE_RENEW_INTERVAL_MS);
374
+ heartbeat.unref();
375
+ try {
376
+ const result = await leaseContext.run(nested, run);
377
+ options.assertCurrent?.();
378
+ if (owner.failure) throw owner.failure;
379
+ return result;
380
+ } finally {
381
+ clearInterval(heartbeat);
382
+ owner.phase = "closed";
383
+ options.assertCurrent?.();
384
+ try {
385
+ const current = readStoredCodexAppServerBinding(state.lookup(key));
386
+ if (current?.lease?.token === token) {
387
+ const ttlMs = current.state === "active" || current.retired === true && !key.startsWith("session:") ? void 0 : current.retired === true ? PHYSICAL_SESSION_RETIRE_TTL_MS : 1;
388
+ options.assertCurrent?.();
389
+ update(key, (raw) => {
390
+ const stored = readStoredCodexAppServerBinding(raw);
391
+ if (stored?.lease?.token !== token) return;
392
+ const { lease: _lease, ...released } = stored;
393
+ return released;
394
+ }, ttlMs === void 0 ? void 0 : { ttlMs });
395
+ }
396
+ } catch (error) {
397
+ options.assertCurrent?.();
398
+ embeddedAgentLog.warn("failed to release codex app-server binding lease", {
399
+ key,
400
+ error
401
+ });
402
+ }
403
+ }
404
+ };
405
+ const transitionSessionGeneration = async (identity, mode) => {
406
+ return await runBindingMutation(async () => {
446
407
  const key = bindingStoreKey(identity);
447
- const raw = state.lookup(key);
448
- const stored = readStoredCodexAppServerBinding(raw);
449
- if (raw !== void 0 && !stored) throw new Error(`Invalid Codex app-server binding row: ${key}`);
450
- return stored?.state === "active" && ownsStoredSessionGeneration(identity, stored) ? stored.binding : void 0;
451
- },
408
+ const ttlMs = mode === "reset" ? leaseContext.getStore()?.has(key) ? void 0 : 1 : identity.sessionKey?.trim() ? void 0 : PHYSICAL_SESSION_RETIRE_TTL_MS;
409
+ return await transactKey(key, (current, leaseToken) => {
410
+ if (!current) return { result: "absent" };
411
+ if (!ownsStoredSessionGeneration(identity, current)) return { result: "conflict" };
412
+ if (current.state === "cleared" && current.retired === true) return { result: mode === "retire" ? "applied" : "conflict" };
413
+ return {
414
+ result: "applied",
415
+ next: {
416
+ version: 1,
417
+ state: "cleared",
418
+ ...mode === "retire" ? { retired: true } : {},
419
+ ...storedSessionGeneration(identity, current),
420
+ ...current.lease && current.lease.token === leaseToken ? { lease: current.lease } : {}
421
+ }
422
+ };
423
+ }, ttlMs);
424
+ });
425
+ };
426
+ return {
427
+ read: (identity) => readCurrentCodexAppServerBinding(state, identity),
452
428
  async hasOtherThreadOwner(threadId, currentIdentity) {
453
429
  const currentKey = currentIdentity ? bindingStoreKey(currentIdentity) : void 0;
454
430
  return state.entries().some(({ key, value }) => {
@@ -485,7 +461,7 @@ function createCodexAppServerBindingStore(state) {
485
461
  expectedPreviousSessionId: currentSessionId
486
462
  };
487
463
  },
488
- async mutate(identity, mutation) {
464
+ async mutate(identity, mutation, assertCurrent) {
489
465
  return await runBindingMutation(async () => {
490
466
  const key = bindingStoreKey(identity);
491
467
  const retainLegacyClear = mutation.kind === "clear" && key.startsWith("conversation:legacy-");
@@ -523,9 +499,8 @@ function createCodexAppServerBindingStore(state) {
523
499
  const active = ownsGeneration ? storedActive : void 0;
524
500
  const retiredGeneration = current?.state === "cleared" && current.retired === true && ownsGeneration;
525
501
  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
502
  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 };
503
+ 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
504
  if (mutation.kind === "clear" && retiredGeneration) return { result: true };
530
505
  if (mutation.kind === "clear") return {
531
506
  result: true,
@@ -563,10 +538,10 @@ function createCodexAppServerBindingStore(state) {
563
538
  ...ownedLease
564
539
  }
565
540
  };
566
- }, mutation.kind === "clear" && !retainLegacyClear && !leaseContext.getStore()?.has(key) ? 1 : void 0);
541
+ }, mutation.kind === "clear" && !retainLegacyClear && !leaseContext.getStore()?.has(key) ? 1 : void 0, assertCurrent);
567
542
  });
568
543
  },
569
- async adoptSessionGeneration(identity, expectedPreviousSessionId) {
544
+ async adoptSessionGeneration(identity, expectedPreviousSessionId, assertCurrent) {
570
545
  return await runBindingMutation(async () => {
571
546
  const key = bindingStoreKey(identity);
572
547
  const expectedSessionId = expectedPreviousSessionId.trim();
@@ -583,48 +558,11 @@ function createCodexAppServerBindingStore(state) {
583
558
  sessionId: targetSessionId
584
559
  }
585
560
  };
586
- });
587
- });
588
- },
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);
561
+ }, void 0, assertCurrent);
626
562
  });
627
563
  },
564
+ resetSessionGeneration: (identity) => transitionSessionGeneration(identity, "reset"),
565
+ retireSessionGeneration: (identity) => transitionSessionGeneration(identity, "retire"),
628
566
  async withThreadArchiveFence(run) {
629
567
  pendingArchives += 1;
630
568
  const operation = archiveTail.then(async () => {
@@ -638,79 +576,74 @@ function createCodexAppServerBindingStore(state) {
638
576
  pendingArchives -= 1;
639
577
  }
640
578
  },
641
- async withLease(identity, run) {
579
+ async withSessionDeletion(identity, assertCurrent, run) {
642
580
  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
581
+ const deleteIf = state.deleteIf?.bind(state);
582
+ if (!deleteIf) throw new Error("Codex session deletion requires conditional plugin-state deletion");
583
+ return await runBindingMutation(async () => {
584
+ assertCurrent();
585
+ if (state.lookup(key) === void 0) {
586
+ let active = true;
587
+ try {
588
+ return await run(void 0, {
589
+ commit() {
590
+ assertCurrent();
591
+ if (!active || state.lookup(key) !== void 0) throw new Error("Codex binding changed before session deletion");
592
+ },
593
+ rollback() {}
594
+ });
595
+ } finally {
596
+ active = false;
682
597
  }
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;
598
+ }
599
+ return await withBindingLease(identity, async () => {
600
+ const owner = leaseContext.getStore().get(key);
601
+ const expected = state.lookup(key);
602
+ const stored = readStoredCodexAppServerBinding(expected);
603
+ if (!stored || !ownsStoredSessionGeneration(identity, stored)) throw new Error("Codex binding generation changed before session deletion");
604
+ const { lease: _lease, ...expectedValue } = stored;
605
+ let deleted;
606
+ let active = true;
607
+ const assertActive = () => {
608
+ assertCurrent();
609
+ if (!active || owner.phase === "closed" || owner.failure) throw owner.failure ?? bindingLeaseLostError(key);
702
610
  };
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 });
611
+ try {
612
+ return await run(stored.state === "active" ? stored.binding : void 0, {
613
+ commit() {
614
+ assertActive();
615
+ if (deleted) return;
616
+ const current = state.lookup(key);
617
+ const { lease, ...value } = readStoredCodexAppServerBinding(current) ?? {};
618
+ 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");
619
+ deleted = current;
620
+ owner.phase = "deleted";
621
+ },
622
+ rollback() {
623
+ assertActive();
624
+ if (!deleted) return;
625
+ const restored = {
626
+ ...deleted,
627
+ lease: {
628
+ token: owner.token,
629
+ expiresAt: Date.now() + BINDING_LEASE_STALE_MS
630
+ }
631
+ };
632
+ if (!state.registerIfAbsent(key, restored)) throw new Error("Codex binding changed before session deletion rollback");
633
+ deleted = void 0;
634
+ owner.phase = "held";
635
+ }
636
+ });
637
+ } finally {
638
+ active = false;
705
639
  }
706
- } catch (error) {
707
- embeddedAgentLog.warn("failed to release codex app-server binding lease", {
708
- key,
709
- error
710
- });
711
- }
712
- }
713
- }
640
+ }, {
641
+ allowRetired: true,
642
+ assertCurrent
643
+ });
644
+ });
645
+ },
646
+ withLease: withBindingLease
714
647
  };
715
648
  }
716
649
  function matchesPendingSupervisionBranch(binding, expected) {
@@ -724,31 +657,6 @@ function matchesPendingSupervisionBranch(binding, expected) {
724
657
  function isSameSupervisionOwner(current, replacement) {
725
658
  return replacement.connectionScope === "supervision" && replacement.threadId === current.threadId && replacement.supervisionSourceThreadId === current.supervisionSourceThreadId;
726
659
  }
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
- /** Stable plugin-state key for one current binding owner. */
733
- function bindingStoreKey(identity) {
734
- if (identity.kind === "session") {
735
- const rawAgentId = identity.agentId.trim();
736
- const sessionId = identity.sessionId.trim();
737
- if (!rawAgentId) throw new Error("Codex app-server binding requires an agent id");
738
- if (!sessionId) throw new Error("Codex app-server binding requires a session id");
739
- const agentId = resolveSessionAgentIds$1({ agentId: rawAgentId }).sessionAgentId;
740
- const sessionKey = identity.sessionKey?.trim();
741
- if (sessionKey) return `session-key:${agentId}:${createHash("sha256").update(sessionKey).digest("base64url")}`;
742
- return `session:${agentId}:${sessionId}`;
743
- }
744
- const bindingId = identity.bindingId.trim();
745
- if (!bindingId) throw new Error("Codex app-server conversation binding requires a binding id");
746
- return `conversation:${bindingId}`;
747
- }
748
- function readStoredCodexAppServerBinding(value) {
749
- const result = storedBindingSchema.safeParse(value);
750
- return result.success ? stripUndefinedValue(result.data) : void 0;
751
- }
752
660
  function storedSessionGeneration(identity, current) {
753
661
  if (identity.kind === "session") return { sessionId: identity.sessionId };
754
662
  return current?.sessionId ? { sessionId: current.sessionId } : {};
@@ -757,31 +665,6 @@ function preservedSessionGeneration(identity, current) {
757
665
  if (current?.sessionId) return { sessionId: current.sessionId };
758
666
  return storedSessionGeneration(identity, current);
759
667
  }
760
- function ownsStoredSessionGeneration(identity, current) {
761
- return identity.kind !== "session" || !current?.sessionId || current.sessionId === identity.sessionId;
762
- }
763
- function validateBindingForWrite(binding) {
764
- const validated = readCodexAppServerThreadBinding(binding);
765
- if (!validated) throw new Error("Invalid Codex app-server thread binding");
766
- return stripUndefinedBinding(validated);
767
- }
768
- /** Parses stored or shipped sidecar data into the current domain value. */
769
- function readCodexAppServerThreadBinding(value) {
770
- const result = threadBindingSchema.safeParse(value);
771
- if (!result.success) return;
772
- return result.data;
773
- }
774
- function stripUndefinedBinding(binding) {
775
- return stripUndefinedValue(binding);
776
- }
777
- function stripUndefinedValue(value) {
778
- if (Array.isArray(value)) return value.map(stripUndefinedValue);
779
- if (!value || typeof value !== "object") return value;
780
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0).map(([key, entry]) => [key, stripUndefinedValue(entry)]));
781
- }
782
- function readTimestamp(value) {
783
- return optionalTimestampSchema.parse(value);
784
- }
785
668
  function readPluginAppPolicyContext(value, bindingSchemaVersion) {
786
669
  const record = asOptionalRecord(value);
787
670
  if (!record || typeof record.fingerprint !== "string") return;
@@ -871,4 +754,4 @@ function normalizeCodexAppServerBindingModelProvider(params) {
871
754
  return modelProvider;
872
755
  }
873
756
  //#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 };
757
+ export { isCodexAppServerNativeAuthProfile as a, resolveCodexRunSessionBindingAuthority as c, session_binding_exports as d, hashCodexAppServerBindingFingerprint as i, resolveCodexSessionBinding as l, assertCodexBindingMayBeReplaced as n, normalizeCodexAppServerBindingModelProvider as o, createCodexSessionGenerationSupersededError as r, reclaimCurrentCodexSessionGeneration as s, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS as t, scopeCodexRunBindingStore as u };