@openclaw/codex 2026.7.2-beta.7 → 2026.8.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +147 -0
  3. package/dist/app-server-policy-bg5QPSVP.js +25 -0
  4. package/dist/attempt-client-cleanup-URjUlCHg.js +735 -0
  5. package/dist/binding-connection-DAvZwp96.js +45 -0
  6. package/dist/{bounded-turn-D2ZAgZVz.js → bounded-turn-4BjrZEwW.js} +63 -56
  7. package/dist/{capabilities-BFDTVYr4.js → capabilities-CH3K57v4.js} +8 -2
  8. package/dist/cli-metadata.js +1 -1
  9. package/dist/{command-formatters-C336eRVS.js → command-formatters-D0nZ3qtd.js} +18 -18
  10. package/dist/{command-handlers-DAHUSry9.js → command-handlers-DC86lgT4.js} +507 -101
  11. package/dist/command-rpc-xUIIonsO.js +90 -0
  12. package/dist/{compact-D_uvHurS.js → compact-COqgBRny.js} +234 -214
  13. package/dist/{session-binding-BXpo9ymA.js → config-CpNeo5AE.js} +284 -1163
  14. package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
  15. package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
  16. package/dist/doctor-contract-api.js +48 -50
  17. package/dist/{dynamic-tools-D2gYkSBO.js → dynamic-tools-fJZhl4CE.js} +3442 -1433
  18. package/dist/{effective-mcp-catalog-Bbd98Inw.js → effective-mcp-catalog-4YKn51aG.js} +20 -15
  19. package/dist/elicitation-input-DMkeki8d.js +71 -0
  20. package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
  21. package/dist/{attempt-notifications-AP-a-Q5k.js → event-projector-usage-CLwNEJqW.js} +113 -29
  22. package/dist/harness-DoKQcF8n.js +258 -0
  23. package/dist/harness.js +2 -160
  24. package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
  25. package/dist/index.js +730 -256
  26. package/dist/isolated-completion-DPMtlH9r.js +75 -0
  27. package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
  28. package/dist/managed-binary-By4FZC6i.js +212 -0
  29. package/dist/media-understanding-provider.js +5 -5
  30. package/dist/model-catalog-CmGoqVKB.js +48 -0
  31. package/dist/{models-BBMKU4jA.js → models-CZw1wClT.js} +12 -17
  32. package/dist/native-subagent-monitor-GGq3RnOo.js +3225 -0
  33. package/dist/node-exec-server.runtime-Bsu1LdFZ.js +192 -0
  34. package/dist/{plugin-app-cache-key-DztPafOA.js → plugin-app-cache-key-DMNQ8Zi8.js} +18 -6
  35. package/dist/plugin-approval-roundtrip-C75Yiiwc.js +95 -0
  36. package/dist/plugin-marketplace-discovery-CSWDkMrP.js +399 -0
  37. package/dist/{computer-use-CYEc8U3w.js → plugin-metadata-cache-BXH5NF-4.js} +109 -6
  38. package/dist/protocol-validators-VAVyEnDp.js +2399 -0
  39. package/dist/{rate-limits-CU-gVCCS.js → rate-limits-BI1dMHDG.js} +17 -35
  40. package/dist/{request-qKh331yu.js → request-DSK0W6YI.js} +3 -3
  41. package/dist/{run-attempt-CY9WlDLc.js → run-attempt-BV3HzuCf.js} +3416 -7194
  42. package/dist/{runtime-artifact-CmtG_t1L.js → runtime-artifact-BvyT1uyd.js} +11 -29
  43. package/dist/{sandbox-guard-NuhpXmPB.js → sandbox-guard-C1qEeZ5C.js} +29 -30
  44. package/dist/session-binding-BTrLf9Qm.js +874 -0
  45. package/dist/{session-catalog-DyOgR4WH.js → session-catalog-ezP_UcRM.js} +1584 -1289
  46. package/dist/session-catalog-home-id-BNwAJ9D7.js +18 -0
  47. package/dist/{session-cli-2PZaFLRh.js → session-cli-B1hKvfkI.js} +20 -7
  48. package/dist/session-retirement-CE4iMiLh.js +36 -0
  49. package/dist/{session-upstream-marker-B6U7tLw5.js → session-upstream-marker-D_2npHlA.js} +1 -1
  50. package/dist/{settled-turn-finalizer-CXY2Z9dT.js → settled-turn-finalizer-Dx1onIqg.js} +40 -28
  51. package/dist/{shared-client-DTbvH-XV.js → shared-client-Dw-rzii0.js} +1315 -469
  52. package/dist/{side-question-_ulBk1AL.js → side-question-DbXkJZzg.js} +140 -69
  53. package/dist/{thread-lifecycle-DF53ogiA.js → thread-lifecycle-DKdrnTMX.js} +927 -768
  54. package/dist/thread-ownership-i2NBwDUL.js +92 -0
  55. package/dist/thread-resume-CdJk2Ehf.js +30 -0
  56. package/dist/transcript-mirror-BU0_LKAL.js +588 -0
  57. package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DhoGy33Z.js} +13 -2
  58. package/dist/upstream-prompt-provenance-CFmsKwr9.js +39 -0
  59. package/dist/{upstream-session-fork-Dy4Xvuoj.js → upstream-session-fork-BDVJCLAD.js} +14 -8
  60. package/dist/{usage-CQ6GhD5-.js → usage-9dKJCgcy.js} +3 -3
  61. package/dist/{web-search-provider.runtime-CCFhg2z5.js → web-search-provider.runtime-DCdx4DGu.js} +8 -9
  62. package/openclaw.plugin.json +49 -4
  63. package/package.json +6 -5
  64. package/dist/app-server-policy-BSaO2JIT.js +0 -49
  65. package/dist/conversation-control-xsIeXbo4.js +0 -287
  66. package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
  67. package/dist/notification-correlation-CU6_AnOb.js +0 -67
  68. package/dist/transcript-mirror-DzsdNCf5.js +0 -6924
@@ -0,0 +1,735 @@
1
+ import { B as CodexAppServerRpcError, X as unsubscribeCodexAppServerLiveThread, p as retireSharedCodexAppServerClientIfCurrent, ut as isJsonObject } from "./shared-client-Dw-rzii0.js";
2
+ import { asFiniteNumber, normalizeOptionalString, normalizeOptionalString as normalizeOptionalString$1, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
4
+ import { embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
5
+ import { redactSensitiveText } from "openclaw/plugin-sdk/logging-core";
6
+ import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
7
+ //#region extensions/codex/src/app-server/event-projector-values.ts
8
+ function readNonEmptyString(record, key) {
9
+ return normalizeOptionalString$1(record[key]);
10
+ }
11
+ function readNonEmptyStringArray(record, key) {
12
+ const value = record[key];
13
+ if (!Array.isArray(value)) return [];
14
+ const entries = [];
15
+ for (const entry of value) {
16
+ const normalized = normalizeOptionalString$1(entry);
17
+ if (normalized) entries.push(normalized);
18
+ }
19
+ return entries;
20
+ }
21
+ function readNullableString(record, key) {
22
+ const value = record[key];
23
+ if (value === null) return null;
24
+ return typeof value === "string" ? value : void 0;
25
+ }
26
+ function readNonNegativeInteger(record, key) {
27
+ const value = asFiniteNumber(record[key]);
28
+ return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
29
+ }
30
+ function readCodexErrorNotificationMessage(record) {
31
+ const error = record.error;
32
+ return isJsonObject(error) ? readStringField(error, "message") : void 0;
33
+ }
34
+ function readHookOutputEntries(value) {
35
+ if (!Array.isArray(value)) return [];
36
+ return value.flatMap((entry) => {
37
+ if (!isJsonObject(entry)) return [];
38
+ const text = readStringField(entry, "text");
39
+ if (!text) return [];
40
+ const kind = readStringField(entry, "kind");
41
+ return [{
42
+ ...kind ? { kind } : {},
43
+ text
44
+ }];
45
+ });
46
+ }
47
+ function splitPlanText(text) {
48
+ return text.split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
49
+ }
50
+ function extractRawAssistantText(item) {
51
+ const parts = (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
52
+ if (!isJsonObject(entry)) return [];
53
+ const type = readStringField(entry, "type");
54
+ if (type !== "output_text" && type !== "text") return [];
55
+ const value = readStringField(entry, "text");
56
+ return value === void 0 ? [] : [value];
57
+ });
58
+ return parts.length > 0 ? parts.join("").trim() : void 0;
59
+ }
60
+ function readItemString(item, key) {
61
+ const value = item[key];
62
+ return typeof value === "string" ? value : void 0;
63
+ }
64
+ function readItem(value) {
65
+ if (!isJsonObject(value)) return;
66
+ const type = typeof value.type === "string" ? value.type : void 0;
67
+ const id = typeof value.id === "string" ? value.id : void 0;
68
+ if (!type || !id) return;
69
+ return value;
70
+ }
71
+ //#endregion
72
+ //#region extensions/codex/src/app-server/event-projector-items.ts
73
+ function itemKind(item) {
74
+ switch (item.type) {
75
+ case "dynamicToolCall":
76
+ case "mcpToolCall": return "tool";
77
+ case "commandExecution": return "command";
78
+ case "fileChange": return "patch";
79
+ case "webSearch": return "search";
80
+ case "reasoning":
81
+ case "contextCompaction": return "analysis";
82
+ default: return;
83
+ }
84
+ }
85
+ function itemTitle(item) {
86
+ switch (item.type) {
87
+ case "commandExecution": return "Command";
88
+ case "fileChange": return "File change";
89
+ case "mcpToolCall": return "MCP tool";
90
+ case "dynamicToolCall": return "Tool";
91
+ case "webSearch": return "Web search";
92
+ case "contextCompaction": return "Context compaction";
93
+ case "reasoning": return "Reasoning";
94
+ default: return item.type;
95
+ }
96
+ }
97
+ function itemStatus(item) {
98
+ const status = readItemString(item, "status");
99
+ if (status === "failed" || status === "error") return "failed";
100
+ if (status === "declined") return "blocked";
101
+ if (status === "inProgress" || status === "in_progress" || status === "running") return "running";
102
+ return "completed";
103
+ }
104
+ function unknownItemStatus(item) {
105
+ const status = readItemString(item, "status");
106
+ switch (status) {
107
+ case void 0:
108
+ case "completed":
109
+ case "failed":
110
+ case "error":
111
+ case "declined":
112
+ case "inProgress":
113
+ case "in_progress":
114
+ case "running": return;
115
+ default: return status;
116
+ }
117
+ }
118
+ function auditNativeToolTerminalStatus(item) {
119
+ if (item.type === "imageView" || item.type === "sleep") return "completed";
120
+ const status = readItemString(item, "status");
121
+ if (status === "completed") return "completed";
122
+ if (status === "failed" || status === "error") return "failed";
123
+ if (status === "declined") return "blocked";
124
+ return "unknown";
125
+ }
126
+ function auditNativeToolUnfinishedStatus(item) {
127
+ return item.type === "webSearch" || item.type === "imageGeneration" ? "unknown" : "failed";
128
+ }
129
+ function isNonSuccessItemStatus(status) {
130
+ return status === "failed" || status === "blocked";
131
+ }
132
+ function itemName(item) {
133
+ if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
134
+ if (item.type === "mcpToolCall" && typeof item.tool === "string") {
135
+ const server = typeof item.server === "string" ? item.server : void 0;
136
+ return server ? `${server}.${item.tool}` : item.tool;
137
+ }
138
+ if (item.type === "commandExecution") return "bash";
139
+ if (item.type === "fileChange") return "apply_patch";
140
+ if (item.type === "webSearch") return "web_search";
141
+ }
142
+ function auditNativeToolName(item) {
143
+ if (item.type === "dynamicToolCall") return;
144
+ const progressName = itemName(item);
145
+ if (progressName) return progressName;
146
+ if (item.type === "collabAgentToolCall") return typeof item.tool === "string" && item.tool.trim() ? `collab.${item.tool.trim()}` : "collab_agent";
147
+ if (item.type === "imageGeneration") return "image_generation";
148
+ if (item.type === "imageView") return "image_view";
149
+ if (item.type === "sleep") return "sleep";
150
+ }
151
+ function isSideEffectingNativeToolItem(item) {
152
+ return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
153
+ }
154
+ function shouldSynthesizeToolProgressForItem(item) {
155
+ switch (item.type) {
156
+ case "commandExecution":
157
+ case "fileChange":
158
+ case "webSearch":
159
+ case "mcpToolCall": return true;
160
+ default: return false;
161
+ }
162
+ }
163
+ function shouldRecordNativeToolTranscript(item) {
164
+ return shouldSynthesizeToolProgressForItem(item);
165
+ }
166
+ function isMutatingNativeToolItem(item) {
167
+ if (item.type === "commandExecution") return true;
168
+ return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
169
+ }
170
+ function shouldClearTerminalPresentationForNativeItem(item) {
171
+ switch (item.type) {
172
+ case "collabAgentToolCall":
173
+ case "commandExecution":
174
+ case "fileChange":
175
+ case "imageGeneration":
176
+ case "imageView":
177
+ case "mcpToolCall":
178
+ case "webSearch": return true;
179
+ default: return false;
180
+ }
181
+ }
182
+ //#endregion
183
+ //#region extensions/codex/src/app-server/notification-correlation.ts
184
+ /**
185
+ * Correlates Codex app-server notifications with the active thread/turn so
186
+ * projectors can ignore global or stale events without losing diagnostics.
187
+ */
188
+ /** Returns true when a notification payload belongs to the exact active thread and turn. */
189
+ function isCodexNotificationForTurn(value, threadId, turnId) {
190
+ if (!isJsonObject(value)) return false;
191
+ return readCodexNotificationThreadId(value) === threadId && readCodexNotificationTurnId(value) === turnId;
192
+ }
193
+ /**
194
+ * Reads a thread id from canonical top-level or nested thread payloads.
195
+ * The generated v2 schemas require top-level `threadId` on turn/item-scoped
196
+ * notifications and define `Turn` without one, so `turn.threadId` is not a
197
+ * wire shape and is deliberately not read here.
198
+ */
199
+ function readCodexNotificationThreadId(record) {
200
+ const thread = isJsonObject(record.thread) ? record.thread : void 0;
201
+ return normalizeOptionalString(record.threadId) ?? (thread ? normalizeOptionalString(thread.id) : void 0);
202
+ }
203
+ /** Reads a turn id from either top-level notification params or nested turn payloads. */
204
+ function readCodexNotificationTurnId(record) {
205
+ return readNestedTurnId(record) ?? normalizeOptionalString(record.turnId);
206
+ }
207
+ function readNestedTurnId(record) {
208
+ const turn = record.turn;
209
+ return isJsonObject(turn) ? normalizeOptionalString(turn.id) : void 0;
210
+ }
211
+ //#endregion
212
+ //#region extensions/codex/src/app-server/event-projector-diagnostics.ts
213
+ function redactCodexEventKind(method) {
214
+ return redactSensitiveText(sanitizeTerminalText(method));
215
+ }
216
+ var CodexProjectionDiagnostics = class {
217
+ constructor(threadId, turnId) {
218
+ this.threadId = threadId;
219
+ this.turnId = turnId;
220
+ this.warningKeys = /* @__PURE__ */ new Set();
221
+ }
222
+ warnUnknownItemStatus(item) {
223
+ if (!item) return;
224
+ const status = unknownItemStatus(item);
225
+ if (!status) return;
226
+ const safeStatus = redactCodexEventKind(status);
227
+ const safeItemType = redactCodexEventKind(item.type);
228
+ this.warnOnce(JSON.stringify([
229
+ "status",
230
+ item.type,
231
+ status
232
+ ]), "codex app-server item reported unknown status; continuing projection", {
233
+ itemId: item.id,
234
+ itemType: safeItemType,
235
+ status: safeStatus
236
+ });
237
+ }
238
+ warnUnknownEvent(notification, params) {
239
+ const notificationThreadId = readCodexNotificationThreadId(params);
240
+ const notificationTurnId = readCodexNotificationTurnId(params);
241
+ const eventKind = redactCodexEventKind(notification.method);
242
+ this.warnOnce(JSON.stringify(["method", notification.method]), `codex app-server projector received unknown event kind; continuing: ${eventKind}`, {
243
+ eventKind,
244
+ activeThreadId: this.threadId,
245
+ activeTurnId: this.turnId,
246
+ threadId: notificationThreadId,
247
+ turnId: notificationTurnId,
248
+ matchesActiveThread: notificationThreadId === this.threadId,
249
+ matchesActiveTurn: notificationTurnId === this.turnId
250
+ });
251
+ }
252
+ warnOnce(key, message, context) {
253
+ if (this.warningKeys.has(key)) return;
254
+ this.warningKeys.add(key);
255
+ embeddedAgentLog.warn(message, context);
256
+ }
257
+ };
258
+ //#endregion
259
+ //#region extensions/codex/src/app-server/turn-router.ts
260
+ /** Keyed routing for all turn traffic on one shared Codex app-server client. */
261
+ const DEFAULT_PREBIND_NOTIFICATION_LIMIT = 256;
262
+ const CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS = 3e4;
263
+ const routers = /* @__PURE__ */ new WeakMap();
264
+ /** Returns the sole router installed on a physical app-server client. */
265
+ function getCodexAppServerTurnRouter(client) {
266
+ const existing = routers.get(client);
267
+ if (existing) return existing;
268
+ const router = new ClientTurnRouter(client);
269
+ routers.set(client, router);
270
+ return router;
271
+ }
272
+ var ClientTurnRouter = class {
273
+ constructor(client) {
274
+ this.routes = /* @__PURE__ */ new Map();
275
+ this.nativeTurnCompletionWatchers = /* @__PURE__ */ new Map();
276
+ this.disposed = false;
277
+ client.addNotificationHandler((notification) => this.routeNotification(notification));
278
+ client.addRequestHandler((request, signal) => this.routeRequest(request, signal));
279
+ client.addCloseHandler((closedClient) => {
280
+ this.dispose(closedClient.getCloseError());
281
+ });
282
+ }
283
+ reserveThread(options) {
284
+ this.assertActive();
285
+ const threadId = requireId(options.threadId, "thread id");
286
+ if (this.routes.has(threadId)) throw new Error(`codex app-server thread route already reserved: ${threadId}`);
287
+ const route = {
288
+ threadId,
289
+ controller: new AbortController(),
290
+ ended: createDeferred(),
291
+ activated: createDeferred(),
292
+ gate: "open",
293
+ pending: [],
294
+ notificationTail: Promise.resolve(),
295
+ completedNativeTurnIds: /* @__PURE__ */ new Set(),
296
+ ignoredTurnNotificationKeys: /* @__PURE__ */ new Set()
297
+ };
298
+ this.routes.set(threadId, route);
299
+ if (options.onNotification || options.onRequest) this.activateNow(route, options);
300
+ const releaseOn = options.releaseOn;
301
+ if (releaseOn) {
302
+ const release = () => this.release(route, abortReason(releaseOn));
303
+ releaseOn.addEventListener("abort", release, { once: true });
304
+ route.detachReleaseOn = () => releaseOn.removeEventListener("abort", release);
305
+ if (releaseOn.aborted) release();
306
+ }
307
+ return {
308
+ threadId,
309
+ signal: route.controller.signal,
310
+ get observedNativeTurnId() {
311
+ return route.observedNativeTurn?.id;
312
+ },
313
+ activate: (handlers) => this.activate(route, handlers),
314
+ armTurn: () => this.armTurn(route),
315
+ bindTurn: (turnId) => this.bindTurn(route, turnId),
316
+ cancelTurn: () => this.cancelTurn(route),
317
+ drain: () => this.drainNotifications(route),
318
+ release: () => this.release(route)
319
+ };
320
+ }
321
+ watchNativeTurnCompletion(options) {
322
+ this.assertActive();
323
+ const threadId = requireId(options.threadId, "thread id");
324
+ const turnId = requireId(options.turnId, "turn id");
325
+ if (options.signal?.aborted) return {
326
+ completion: Promise.resolve(false),
327
+ cancel: () => {}
328
+ };
329
+ if (this.routes.get(threadId)?.completedNativeTurnIds.delete(turnId)) return {
330
+ completion: Promise.resolve(true),
331
+ cancel: () => {}
332
+ };
333
+ const { promise: completion, resolve: settle } = createDeferred();
334
+ const watchers = this.nativeTurnCompletionWatchers.get(threadId) ?? /* @__PURE__ */ new Set();
335
+ this.nativeTurnCompletionWatchers.set(threadId, watchers);
336
+ let settled = false;
337
+ const finish = (completed) => {
338
+ if (settled) return;
339
+ settled = true;
340
+ watchers.delete(watcher);
341
+ if (watchers.size === 0) this.nativeTurnCompletionWatchers.delete(threadId);
342
+ clearTimeout(timeout);
343
+ options.signal?.removeEventListener("abort", abort);
344
+ settle(completed);
345
+ };
346
+ const watcher = {
347
+ turnId,
348
+ finish
349
+ };
350
+ watchers.add(watcher);
351
+ const timeout = setTimeout(() => finish(false), Math.max(1, options.timeoutMs));
352
+ timeout.unref?.();
353
+ const abort = () => finish(false);
354
+ options.signal?.addEventListener("abort", abort, { once: true });
355
+ return {
356
+ completion,
357
+ cancel: () => finish(false)
358
+ };
359
+ }
360
+ dispose(cause) {
361
+ if (this.disposed) return;
362
+ this.disposed = true;
363
+ const closeError = cause ? new Error("codex app-server turn router closed", { cause }) : /* @__PURE__ */ new Error("codex app-server turn router closed");
364
+ for (const route of this.routes.values()) this.release(route, closeError);
365
+ for (const watchers of this.nativeTurnCompletionWatchers.values()) for (const watcher of watchers) watcher.finish(false);
366
+ }
367
+ async activate(route, handlers) {
368
+ this.assertRoute(route);
369
+ this.activateNow(route, handlers);
370
+ await this.waitForNotifications(route);
371
+ this.assertRoute(route);
372
+ }
373
+ activateNow(route, handlers) {
374
+ if (route.handlers) throw new Error(`codex app-server thread route already activated: ${route.threadId}`);
375
+ this.assertRoute(route);
376
+ if (!handlers.onNotification && !handlers.onRequest) throw new Error("codex app-server thread route requires a notification or request handler");
377
+ route.handlers = handlers;
378
+ if (!handlers.onNotification) route.pending.length = 0;
379
+ else if (route.gate !== "armed") this.flushNotifications(route);
380
+ route.activated.resolve();
381
+ }
382
+ armTurn(route) {
383
+ this.assertRoute(route);
384
+ if (route.gate !== "open") throw new Error(`codex app-server thread route cannot arm from ${route.gate}`);
385
+ route.gate = "armed";
386
+ route.ignoredTurnNotificationKeys.clear();
387
+ route.completedNativeTurnIds.clear();
388
+ if (route.observedNativeTurn?.completed) route.observedNativeTurn = void 0;
389
+ route.binding = createDeferred();
390
+ }
391
+ async cancelTurn(route) {
392
+ if (route.released || route.gate !== "armed") return;
393
+ route.gate = "open";
394
+ route.binding?.resolve();
395
+ route.binding = void 0;
396
+ this.flushNotifications(route);
397
+ await this.waitForNotifications(route);
398
+ this.assertRoute(route);
399
+ }
400
+ async bindTurn(route, turnIdInput) {
401
+ this.assertRoute(route);
402
+ if (!route.handlers) throw new Error("codex app-server thread route must be activated before binding a turn");
403
+ if (route.gate !== "armed") throw new Error(`codex app-server thread route cannot bind from ${route.gate}`);
404
+ const turnId = requireId(turnIdInput, "turn id");
405
+ route.gate = "bound";
406
+ route.turnId = turnId;
407
+ this.flushNotifications(route);
408
+ route.binding?.resolve();
409
+ await this.waitForNotifications(route);
410
+ this.assertRoute(route);
411
+ }
412
+ routeNotification(notification) {
413
+ if (this.disposed) return;
414
+ const scope = readScope(notification.params);
415
+ const watchers = scope.threadId ? this.nativeTurnCompletionWatchers.get(scope.threadId) : void 0;
416
+ const route = scope.threadId ? this.routes.get(scope.threadId) : void 0;
417
+ if (!watchers && !route) return;
418
+ if (scope.turnId && watchers) {
419
+ for (const watcher of watchers) if (watcher.turnId === scope.turnId && notification.method === "turn/completed") watcher.finish(true);
420
+ }
421
+ if (!route) return;
422
+ const routeScope = {
423
+ threadId: route.threadId,
424
+ ...scope.turnId ? { turnId: scope.turnId } : {}
425
+ };
426
+ const receivedAtMs = Date.now();
427
+ if (route.gate !== "bound" && scope.turnId) {
428
+ if (notification.method === "turn/started") route.observedNativeTurn = {
429
+ id: scope.turnId,
430
+ completed: false
431
+ };
432
+ else if (notification.method === "turn/completed") {
433
+ route.completedNativeTurnIds.add(scope.turnId);
434
+ if (!route.observedNativeTurn || route.observedNativeTurn.completed || route.observedNativeTurn.id === scope.turnId) route.observedNativeTurn = {
435
+ id: scope.turnId,
436
+ completed: true
437
+ };
438
+ }
439
+ }
440
+ if (!route.handlers) {
441
+ this.bufferNotification(route, notification, routeScope, receivedAtMs);
442
+ return;
443
+ }
444
+ const handler = route.handlers.onNotification;
445
+ if (!handler) return;
446
+ if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) {
447
+ this.warnDroppedStaleTurnNotification(route, notification, routeScope);
448
+ return;
449
+ }
450
+ if (route.gate === "armed") {
451
+ this.bufferNotification(route, notification, routeScope, receivedAtMs);
452
+ return;
453
+ }
454
+ route.handlers.onNotificationReceived?.(notification, routeScope, receivedAtMs);
455
+ this.enqueueNotification(route, handler, notification, routeScope);
456
+ return route.notificationTail;
457
+ }
458
+ async routeRequest(request, signal = new AbortController().signal) {
459
+ if (this.disposed || signal.aborted) return;
460
+ const scope = readScope(request.params);
461
+ if (!scope.threadId) return;
462
+ const route = this.routes.get(scope.threadId);
463
+ if (!route) return;
464
+ const requestSignal = AbortSignal.any([signal, route.controller.signal]);
465
+ if (!route.handlers && !await waitForPromiseOrAbort(route.activated.promise, requestSignal)) return;
466
+ if (requestSignal.aborted || !route.handlers) return;
467
+ const handler = route.handlers.onRequest;
468
+ if (!handler) return;
469
+ while (route.gate === "armed") {
470
+ const binding = route.binding?.promise;
471
+ if (!binding || !await waitForPromiseOrAbort(binding, requestSignal)) return;
472
+ if (requestSignal.aborted) return;
473
+ }
474
+ if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) return;
475
+ if (!await waitForPromiseOrAbort(this.waitForNotifications(route), requestSignal)) return;
476
+ if (requestSignal.aborted) return;
477
+ try {
478
+ const result = await handler(request, {
479
+ threadId: scope.threadId,
480
+ ...scope.turnId ? { turnId: scope.turnId } : {}
481
+ }, requestSignal);
482
+ return requestSignal.aborted ? void 0 : result;
483
+ } catch (error) {
484
+ if (requestSignal.aborted) return;
485
+ throw error;
486
+ }
487
+ }
488
+ flushNotifications(route) {
489
+ const handler = route.handlers?.onNotification;
490
+ if (!handler) return;
491
+ for (const pending of route.pending.splice(0)) {
492
+ if (route.gate === "bound" && pending.scope.turnId && pending.scope.turnId !== route.turnId) {
493
+ this.warnDroppedStaleTurnNotification(route, pending.notification, pending.scope);
494
+ continue;
495
+ }
496
+ route.handlers?.onNotificationReceived?.(pending.notification, pending.scope, pending.receivedAtMs);
497
+ this.enqueueNotification(route, handler, pending.notification, pending.scope);
498
+ }
499
+ }
500
+ warnDroppedStaleTurnNotification(route, notification, scope) {
501
+ if (notification.method === "turn/completed" || !scope.turnId || !route.turnId) return;
502
+ const eventKind = redactCodexEventKind(notification.method);
503
+ const key = JSON.stringify([notification.method, scope.turnId]);
504
+ if (route.ignoredTurnNotificationKeys.has(key)) return;
505
+ route.ignoredTurnNotificationKeys.add(key);
506
+ embeddedAgentLog.warn("codex app-server notification ignored for inactive turn", {
507
+ eventKind,
508
+ activeThreadId: route.threadId,
509
+ activeTurnId: route.turnId,
510
+ threadId: scope.threadId,
511
+ turnId: scope.turnId,
512
+ matchesActiveThread: true,
513
+ matchesActiveTurn: false
514
+ });
515
+ }
516
+ bufferNotification(route, notification, scope, receivedAtMs) {
517
+ if (route.pending.length < DEFAULT_PREBIND_NOTIFICATION_LIMIT) {
518
+ route.pending.push({
519
+ notification,
520
+ receivedAtMs,
521
+ scope
522
+ });
523
+ return;
524
+ }
525
+ const error = /* @__PURE__ */ new Error(`codex app-server pre-bind notification buffer exceeded ${DEFAULT_PREBIND_NOTIFICATION_LIMIT} entries for thread ${route.threadId}`);
526
+ embeddedAgentLog.warn(error.message);
527
+ this.release(route, error);
528
+ }
529
+ enqueueNotification(route, handler, notification, scope) {
530
+ if (route.released) return;
531
+ route.notificationTail = route.notificationTail.then(() => handler(notification, scope)).catch((error) => {
532
+ if (!route.released) embeddedAgentLog.warn("codex app-server keyed notification handler failed", {
533
+ method: notification.method,
534
+ threadId: route.threadId,
535
+ turnId: route.turnId,
536
+ error
537
+ });
538
+ });
539
+ }
540
+ async waitForNotifications(route) {
541
+ await Promise.race([route.notificationTail, route.ended.promise]);
542
+ }
543
+ async drainNotifications(route) {
544
+ await route.notificationTail;
545
+ }
546
+ release(route, error = /* @__PURE__ */ new Error("codex app-server thread route is released")) {
547
+ if (route.released) return;
548
+ route.released = error;
549
+ route.pending.length = 0;
550
+ route.ended.resolve();
551
+ route.activated.resolve();
552
+ route.binding?.resolve();
553
+ route.detachReleaseOn?.();
554
+ route.controller.abort(error);
555
+ if (this.routes.get(route.threadId) === route) this.routes.delete(route.threadId);
556
+ }
557
+ assertActive() {
558
+ if (this.disposed) throw new Error("codex app-server turn router is closed");
559
+ }
560
+ assertRoute(route) {
561
+ if (route.released) throw route.released;
562
+ }
563
+ };
564
+ async function waitForPromiseOrAbort(promise, signal) {
565
+ if (signal.aborted) return false;
566
+ let removeAbort;
567
+ try {
568
+ return await Promise.race([promise.then(() => true), new Promise((resolve) => {
569
+ const onAbort = () => resolve(false);
570
+ signal.addEventListener("abort", onAbort, { once: true });
571
+ removeAbort = () => signal.removeEventListener("abort", onAbort);
572
+ if (signal.aborted) onAbort();
573
+ })]);
574
+ } finally {
575
+ removeAbort?.();
576
+ }
577
+ }
578
+ function abortReason(signal) {
579
+ return signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason ?? "codex app-server thread route aborted"));
580
+ }
581
+ function readScope(value) {
582
+ if (!isJsonObject(value)) return {};
583
+ const threadId = readCodexNotificationThreadId(value);
584
+ const turnId = readCodexNotificationTurnId(value);
585
+ return {
586
+ ...threadId ? { threadId } : {},
587
+ ...turnId ? { turnId } : {}
588
+ };
589
+ }
590
+ function requireId(value, label) {
591
+ const normalized = value.trim();
592
+ if (!normalized) throw new Error(`codex app-server ${label} must not be empty`);
593
+ return normalized;
594
+ }
595
+ //#endregion
596
+ //#region extensions/codex/src/app-server/attempt-client-cleanup.ts
597
+ /**
598
+ * Best-effort cleanup helpers for Codex app-server startup attempts and turns.
599
+ */
600
+ /** Timeout for best-effort app-server turn interruption during cleanup. */
601
+ const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
602
+ /** Timeout for best-effort thread unsubscribe during cleanup. */
603
+ const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5e3;
604
+ const CODEX_NO_ACTIVE_TURN_ERROR_CODE = -32600;
605
+ const CODEX_NO_ACTIVE_TURN_ERROR_MESSAGE = "no active turn to interrupt";
606
+ /** Identifies Codex's exact proof that an interrupt target already finished. */
607
+ function isCodexAlreadyTerminalInterruptError(error) {
608
+ return error instanceof CodexAppServerRpcError && error.code === CODEX_NO_ACTIVE_TURN_ERROR_CODE && error.message === CODEX_NO_ACTIVE_TURN_ERROR_MESSAGE;
609
+ }
610
+ /** Raised when a thread subscription may be live on a client OpenClaw no longer controls. */
611
+ var CodexAppServerUnsafeSubscriptionError = class extends Error {
612
+ constructor(message, options) {
613
+ super(message, options);
614
+ this.name = "CodexAppServerUnsafeSubscriptionError";
615
+ }
616
+ };
617
+ function isCodexAppServerUnsafeSubscriptionError(error) {
618
+ return error instanceof CodexAppServerUnsafeSubscriptionError;
619
+ }
620
+ /** Asserts Codex resumed the exact thread this attempt subscribed to. */
621
+ function assertCodexThreadResumeSubscription(requestedThreadId, returnedThreadId) {
622
+ if (returnedThreadId !== requestedThreadId) throw new CodexAppServerUnsafeSubscriptionError(`Codex thread/resume returned ${returnedThreadId} for ${requestedThreadId}`);
623
+ }
624
+ async function closeClientAndWaitIfAvailable(client) {
625
+ const closeable = client;
626
+ if (typeof closeable.closeAndWait === "function") {
627
+ await closeable.closeAndWait();
628
+ return;
629
+ }
630
+ closeable.close?.();
631
+ }
632
+ async function closeCodexStartupClientBestEffort(client) {
633
+ if (!client) return;
634
+ const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
635
+ if (!retiredSharedClient || retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
636
+ }
637
+ /** Retires an unsafe turn client without replacing an already-authoritative failure. */
638
+ async function retireUnsafeCodexTurnClientBestEffort(client, operation) {
639
+ try {
640
+ await closeCodexStartupClientBestEffort(client);
641
+ } catch (error) {
642
+ embeddedAgentLog.debug("codex app-server unsafe turn client retirement failed", {
643
+ operation,
644
+ error
645
+ });
646
+ try {
647
+ client.close();
648
+ } catch (closeError) {
649
+ embeddedAgentLog.debug("codex app-server unsafe turn client close failed", {
650
+ operation,
651
+ error: closeError
652
+ });
653
+ }
654
+ }
655
+ }
656
+ /** Sends a bounded turn interrupt and waits for Codex to confirm terminal abort handling. */
657
+ async function interruptCodexTurnAndWaitBestEffort(client, params) {
658
+ const timeoutMs = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? params.timeoutMs : CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS;
659
+ const requestParams = {
660
+ threadId: params.threadId,
661
+ turnId: params.turnId
662
+ };
663
+ let completion;
664
+ try {
665
+ completion = params.turnId ? getCodexAppServerTurnRouter(client).watchNativeTurnCompletion({
666
+ threadId: params.threadId,
667
+ turnId: params.turnId,
668
+ timeoutMs
669
+ }) : void 0;
670
+ await client.request("turn/interrupt", requestParams, { timeoutMs });
671
+ return completion ? await completion.completion : true;
672
+ } catch (error) {
673
+ if (isCodexAlreadyTerminalInterruptError(error)) return true;
674
+ embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
675
+ return false;
676
+ } finally {
677
+ completion?.cancel();
678
+ }
679
+ }
680
+ /** Unsubscribes from a thread while swallowing cleanup-only failures. */
681
+ async function unsubscribeCodexThreadBestEffort(client, params) {
682
+ try {
683
+ await unsubscribeCodexAppServerLiveThread(client, params.threadId, params.timeoutMs);
684
+ return true;
685
+ } catch (error) {
686
+ embeddedAgentLog.debug("codex app-server thread unsubscribe cleanup failed", {
687
+ threadId: params.threadId,
688
+ error
689
+ });
690
+ return false;
691
+ }
692
+ }
693
+ /**
694
+ * Retires the shared client after a timed-out turn so later runs do not reuse a
695
+ * potentially wedged app-server connection.
696
+ */
697
+ async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
698
+ const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client, { failActiveLeases: params.suspectPhysicalClient });
699
+ const detachedSharedClient = Boolean(retiredSharedClient);
700
+ if (!(params.suspectPhysicalClient && (retiredSharedClient?.closed ?? false))) {
701
+ await interruptCodexTurnAndWaitBestEffort(client, {
702
+ threadId: params.threadId,
703
+ turnId: params.turnId,
704
+ timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
705
+ });
706
+ await unsubscribeCodexThreadBestEffort(client, {
707
+ threadId: params.threadId,
708
+ timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
709
+ });
710
+ }
711
+ let closedClient = retiredSharedClient?.closed ?? false;
712
+ if (!detachedSharedClient) {
713
+ const close = client.close;
714
+ if (typeof close === "function") try {
715
+ close.call(client);
716
+ closedClient = true;
717
+ } catch (error) {
718
+ embeddedAgentLog.debug("codex app-server client close failed during timeout cleanup", {
719
+ threadId: params.threadId,
720
+ turnId: params.turnId,
721
+ error
722
+ });
723
+ }
724
+ }
725
+ embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
726
+ threadId: params.threadId,
727
+ turnId: params.turnId,
728
+ reason: params.reason,
729
+ detachedSharedClient,
730
+ closedClient,
731
+ activeSharedClientLeases: retiredSharedClient?.activeLeases ?? 0
732
+ });
733
+ }
734
+ //#endregion
735
+ export { shouldSynthesizeToolProgressForItem as A, readNullableString as B, isSideEffectingNativeToolItem as C, itemTitle as D, itemStatus as E, readItem as F, readItemString as I, readNonEmptyString as L, normalizeOptionalString$1 as M, readCodexErrorNotificationMessage as N, shouldClearTerminalPresentationForNativeItem as O, readHookOutputEntries as P, readNonEmptyStringArray as R, isNonSuccessItemStatus as S, itemName as T, splitPlanText as V, readCodexNotificationTurnId as _, closeCodexStartupClientBestEffort as a, auditNativeToolUnfinishedStatus as b, isCodexAppServerUnsafeSubscriptionError as c, unsubscribeCodexThreadBestEffort as d, CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS as f, readCodexNotificationThreadId as g, isCodexNotificationForTurn as h, assertCodexThreadResumeSubscription as i, extractRawAssistantText as j, shouldRecordNativeToolTranscript as k, retireCodexAppServerClientAfterTimedOutTurn as l, CodexProjectionDiagnostics as m, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS as n, interruptCodexTurnAndWaitBestEffort as o, getCodexAppServerTurnRouter as p, CodexAppServerUnsafeSubscriptionError as r, isCodexAlreadyTerminalInterruptError as s, CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS as t, retireUnsafeCodexTurnClientBestEffort as u, auditNativeToolName as v, itemKind as w, isMutatingNativeToolItem as x, auditNativeToolTerminalStatus as y, readNonNegativeInteger as z };