@openclaw/codex 2026.7.2-beta.7 → 2026.8.1-beta.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 (55) hide show
  1. package/README.md +2 -0
  2. package/dist/{app-server-policy-BSaO2JIT.js → app-server-policy-zcBDc1JU.js} +1 -1
  3. package/dist/attempt-client-cleanup-DMLXUfa8.js +735 -0
  4. package/dist/{notification-correlation-CU6_AnOb.js → binding-connection-I5Bo-AsX.js} +3 -37
  5. package/dist/{bounded-turn-D2ZAgZVz.js → bounded-turn-qgZmzy_4.js} +50 -49
  6. package/dist/{capabilities-BFDTVYr4.js → capabilities-CybGI1oz.js} +8 -2
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-C336eRVS.js → command-formatters-B1AEz8ZA.js} +18 -18
  9. package/dist/{command-handlers-DAHUSry9.js → command-handlers-Db8pu_ds.js} +484 -95
  10. package/dist/{compact-D_uvHurS.js → compact-LspQaGfl.js} +213 -199
  11. package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
  12. package/dist/doctor-contract-api.js +40 -43
  13. package/dist/{dynamic-tools-D2gYkSBO.js → dynamic-tools-BVdyWkUD.js} +2597 -1221
  14. package/dist/{effective-mcp-catalog-Bbd98Inw.js → effective-mcp-catalog-DIIhAtOZ.js} +20 -15
  15. package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
  16. package/dist/{attempt-notifications-AP-a-Q5k.js → event-projector-usage-e5huIQWu.js} +113 -29
  17. package/dist/harness.js +80 -14
  18. package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
  19. package/dist/index.js +591 -240
  20. package/dist/isolated-completion-C58NbL40.js +75 -0
  21. package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
  22. package/dist/media-understanding-provider.js +5 -5
  23. package/dist/{models-BBMKU4jA.js → models-BFlpn1Ex.js} +12 -17
  24. package/dist/native-subagent-monitor-DyTGmTUx.js +3111 -0
  25. package/dist/{plugin-app-cache-key-DztPafOA.js → plugin-app-cache-key-C3H2CVp6.js} +5 -5
  26. package/dist/plugin-marketplace-discovery-CzrGJdCR.js +385 -0
  27. package/dist/{computer-use-CYEc8U3w.js → plugin-metadata-cache-CNJPj4_b.js} +105 -5
  28. package/dist/protocol-validators-B4F7L8Rf.js +2346 -0
  29. package/dist/{rate-limits-CU-gVCCS.js → rate-limits-DZXmOL8-.js} +17 -35
  30. package/dist/{request-qKh331yu.js → request-BRl5_UfX.js} +3 -3
  31. package/dist/{run-attempt-CY9WlDLc.js → run-attempt-P8BdxC1I.js} +4837 -9047
  32. package/dist/{runtime-artifact-CmtG_t1L.js → runtime-artifact-BVSPdd6g.js} +8 -23
  33. package/dist/{sandbox-guard-NuhpXmPB.js → sandbox-guard-Cmwc9eIk.js} +6 -13
  34. package/dist/{session-binding-BXpo9ymA.js → session-binding-DPxIvMgE.js} +246 -265
  35. package/dist/{session-catalog-DyOgR4WH.js → session-catalog-xwNi_SDM.js} +159 -109
  36. package/dist/{session-cli-2PZaFLRh.js → session-cli-7hgXZDV7.js} +1 -1
  37. package/dist/session-retirement-DoMMJFKm.js +36 -0
  38. package/dist/{session-upstream-marker-B6U7tLw5.js → session-upstream-marker-CrIFX06J.js} +1 -1
  39. package/dist/{settled-turn-finalizer-CXY2Z9dT.js → settled-turn-finalizer-BRWBO4MO.js} +21 -13
  40. package/dist/{shared-client-DTbvH-XV.js → shared-client-kIBbGjk4.js} +587 -223
  41. package/dist/{side-question-_ulBk1AL.js → side-question-9HAL26l9.js} +101 -52
  42. package/dist/{thread-lifecycle-DF53ogiA.js → thread-lifecycle-Bu1BTDXr.js} +694 -753
  43. package/dist/thread-ownership-6kOQHlyC.js +92 -0
  44. package/dist/thread-resume-CPrJNQVM.js +30 -0
  45. package/dist/transcript-mirror-B9gGgnJS.js +570 -0
  46. package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DIImwDVS.js} +11 -0
  47. package/dist/upstream-prompt-provenance-ClRBije0.js +41 -0
  48. package/dist/{upstream-session-fork-Dy4Xvuoj.js → upstream-session-fork-CE-_LdnP.js} +5 -5
  49. package/dist/{usage-CQ6GhD5-.js → usage-CHlV0e-o.js} +3 -3
  50. package/dist/{web-search-provider.runtime-CCFhg2z5.js → web-search-provider.runtime-DRw2Vl42.js} +8 -9
  51. package/openclaw.plugin.json +18 -3
  52. package/package.json +5 -5
  53. package/dist/conversation-control-xsIeXbo4.js +0 -287
  54. package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
  55. package/dist/transcript-mirror-DzsdNCf5.js +0 -6924
@@ -0,0 +1,3111 @@
1
+ import { B as claimCodexAppServerLiveThread, K as releaseCodexAppServerLiveThread, ct as isJsonObject, q as retainCodexAppServerLiveThread } from "./shared-client-kIBbGjk4.js";
2
+ import { A as shouldSynthesizeToolProgressForItem, B as readNullableString$1, C as isSideEffectingNativeToolItem, D as itemTitle, E as itemStatus, F as readItem, L as readNonEmptyString, M as normalizeOptionalString$1, P as readHookOutputEntries, R as readNonEmptyStringArray, S as isNonSuccessItemStatus, T as itemName, k as shouldRecordNativeToolTranscript, w as itemKind, x as isMutatingNativeToolItem, y as auditNativeToolTerminalStatus } from "./attempt-client-cleanup-DMLXUfa8.js";
3
+ import { t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-ClRBije0.js";
4
+ import { n as sanitizeCodexHistoryImagePayloads } from "./image-payload-sanitizer-CnfoUPCE.js";
5
+ import { t as resolveCodexLocalRuntimeAttribution } from "./local-runtime-attribution-B_2mnqhb.js";
6
+ import { asFiniteNumber, asNonArrayRecord, normalizeOptionalString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ import fs from "node:fs/promises";
8
+ import path from "node:path";
9
+ import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
10
+ import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, embeddedAgentLog, emitAgentEvent, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, inferToolMetaFromArgs, runAgentHarnessAfterToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
11
+ import { getSessionEntry, parseSqliteSessionFileMarker, resolveTranscriptSessionKeyBySessionId } from "openclaw/plugin-sdk/session-store-runtime";
12
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
13
+ import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
14
+ import { readCodexSessionTranscriptEventsBeforeAdmission } from "openclaw/plugin-sdk/codex-session-transcript-runtime";
15
+ import { readSessionTranscriptEvents } from "openclaw/plugin-sdk/session-transcript-runtime";
16
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
17
+ import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
18
+ import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
19
+ //#region extensions/codex/src/app-server/event-projector-tool-output.ts
20
+ const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 1e4;
21
+ const TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX = "...(OpenClaw truncated Codex native tool output";
22
+ var ToolOutputAccumulator = class {
23
+ constructor() {
24
+ this.prefixByItem = /* @__PURE__ */ new Map();
25
+ this.originalLengthByItem = /* @__PURE__ */ new Map();
26
+ this.normalizedLengthByItem = /* @__PURE__ */ new Map();
27
+ this.trimStateByItem = /* @__PURE__ */ new Map();
28
+ this.truncatedItemIds = /* @__PURE__ */ new Set();
29
+ this.textByItem = /* @__PURE__ */ new Map();
30
+ }
31
+ append(itemId, delta) {
32
+ const originalLength = (this.originalLengthByItem.get(itemId) ?? this.textByItem.get(itemId)?.length ?? 0) + delta.length;
33
+ this.originalLengthByItem.set(itemId, originalLength);
34
+ const normalizedLength = updateToolOutputTrimState(this.trimStateByItem, itemId, delta);
35
+ this.normalizedLengthByItem.set(itemId, normalizedLength);
36
+ if (this.truncatedItemIds.has(itemId)) {
37
+ const next = appendBoundedToolTranscriptText(this.prefixByItem.get(itemId) ?? this.textByItem.get(itemId) ?? "", "", originalLength);
38
+ this.prefixByItem.set(itemId, next.rawPrefix);
39
+ this.textByItem.set(itemId, next.text);
40
+ return {
41
+ text: next.text,
42
+ originalLength,
43
+ normalizedLength,
44
+ rawPrefix: next.rawPrefix
45
+ };
46
+ }
47
+ const next = appendBoundedToolTranscriptText(this.prefixByItem.get(itemId) ?? this.textByItem.get(itemId) ?? "", delta, originalLength);
48
+ this.prefixByItem.set(itemId, next.rawPrefix);
49
+ this.textByItem.set(itemId, next.text);
50
+ if (originalLength > 1e4) this.truncatedItemIds.add(itemId);
51
+ return {
52
+ text: next.text,
53
+ originalLength,
54
+ normalizedLength,
55
+ rawPrefix: next.rawPrefix
56
+ };
57
+ }
58
+ };
59
+ function updateToolOutputTrimState(trimStateByItem, itemId, delta) {
60
+ const state = trimStateByItem.get(itemId) ?? {
61
+ totalLength: 0,
62
+ leadingWhitespaceLength: 0,
63
+ trailingWhitespaceLength: 0,
64
+ sawNonWhitespace: false
65
+ };
66
+ state.totalLength += delta.length;
67
+ const firstNonWhitespace = delta.search(/\S/u);
68
+ if (firstNonWhitespace === -1) {
69
+ if (!state.sawNonWhitespace) state.leadingWhitespaceLength += delta.length;
70
+ state.trailingWhitespaceLength += delta.length;
71
+ trimStateByItem.set(itemId, state);
72
+ return state.sawNonWhitespace ? state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength : 0;
73
+ }
74
+ if (!state.sawNonWhitespace) {
75
+ state.leadingWhitespaceLength += firstNonWhitespace;
76
+ state.sawNonWhitespace = true;
77
+ }
78
+ state.trailingWhitespaceLength = delta.match(/\s*$/u)?.[0].length ?? 0;
79
+ trimStateByItem.set(itemId, state);
80
+ return state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength;
81
+ }
82
+ function toolOutputRawEchoSignature(text) {
83
+ const trimmed = text.trim();
84
+ if (!trimmed) return;
85
+ return {
86
+ rawLength: trimmed.length,
87
+ rawPrefix: trimmed.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
88
+ };
89
+ }
90
+ function normalizeToolTranscriptArguments(value) {
91
+ return asNonArrayRecord(value);
92
+ }
93
+ function collectDynamicToolContentText(contentItems) {
94
+ if (!Array.isArray(contentItems)) return "";
95
+ return contentItems.flatMap((entry) => {
96
+ if (!isJsonObject(entry)) return [];
97
+ const text = readStringField(entry, "text");
98
+ return text ? [text] : [];
99
+ }).join("\n");
100
+ }
101
+ function appendBoundedToolTranscriptText(currentPrefix, delta, originalLength) {
102
+ if (originalLength <= 1e4) {
103
+ const rawPrefix = currentPrefix + delta;
104
+ return {
105
+ text: rawPrefix,
106
+ rawPrefix
107
+ };
108
+ }
109
+ const notice = toolTranscriptTruncationNotice(originalLength);
110
+ if (notice.length >= 1e4) return {
111
+ text: notice.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS),
112
+ rawPrefix: ""
113
+ };
114
+ const textBudget = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length;
115
+ const remaining = Math.max(0, textBudget - currentPrefix.length);
116
+ const rawPrefix = truncateUtf16Safe(remaining > 0 ? `${currentPrefix}${truncateUtf16Safe(delta, remaining)}` : currentPrefix, textBudget);
117
+ return {
118
+ text: `${rawPrefix}${notice}`,
119
+ rawPrefix
120
+ };
121
+ }
122
+ function toolTranscriptTruncationNotice(originalLength) {
123
+ return `\n${`${TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX}: original ${originalLength} chars, showing ${TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS}; rerun with narrower args.)`}`;
124
+ }
125
+ function truncateToolTranscriptText(text, originalLength = text.length) {
126
+ if (originalLength <= 1e4 && text.length <= 1e4) return text;
127
+ const notice = toolTranscriptTruncationNotice(originalLength);
128
+ if (notice.length >= 1e4) return notice.slice(1, 10001);
129
+ return `${truncateUtf16Safe(text, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length)}${notice}`;
130
+ }
131
+ function formatToolSummary(toolName, meta) {
132
+ const trimmedMeta = meta?.trim();
133
+ return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
134
+ }
135
+ function formatToolOutput(toolName, meta, output) {
136
+ const formattedOutput = formatToolProgressOutput(output);
137
+ if (!formattedOutput) return formatToolSummary(toolName, meta);
138
+ const fence = markdownFenceForText(formattedOutput);
139
+ return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
140
+ }
141
+ function markdownFenceForText(text) {
142
+ return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
143
+ }
144
+ function longestBacktickRun(value) {
145
+ let longest = 0;
146
+ let current = 0;
147
+ for (const char of value) {
148
+ if (char === "`") {
149
+ current += 1;
150
+ longest = Math.max(longest, current);
151
+ continue;
152
+ }
153
+ current = 0;
154
+ }
155
+ return longest;
156
+ }
157
+ //#endregion
158
+ //#region extensions/codex/src/app-server/tool-progress-normalization.ts
159
+ /**
160
+ * Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
161
+ * emitted into OpenClaw events or logs.
162
+ */
163
+ /** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
164
+ function resolveCodexToolProgressDetailMode(value) {
165
+ return value === "raw" ? "raw" : "explain";
166
+ }
167
+ function isCodexCommandBearingToolCall(name, args) {
168
+ const normalizedName = name?.trim().toLowerCase();
169
+ return normalizedName === "exec" || normalizedName === "bash" || normalizedName === "shell" || typeof args?.command === "string" && args.command.trim().length > 0;
170
+ }
171
+ /** Recursively redacts sensitive strings and handles circular values in event payloads. */
172
+ function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
173
+ if (typeof value === "string") return redactToolPayloadText(value);
174
+ if (Array.isArray(value)) {
175
+ if (seen.has(value)) return "[Circular]";
176
+ seen.add(value);
177
+ return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
178
+ }
179
+ if (value && typeof value === "object") {
180
+ if (seen.has(value)) return "[Circular]";
181
+ seen.add(value);
182
+ const out = {};
183
+ for (const [key, child] of Object.entries(value)) out[key] = typeof child === "string" ? redactSensitiveFieldValue(key, child) : sanitizeCodexAgentEventValue(child, seen);
184
+ return out;
185
+ }
186
+ return value;
187
+ }
188
+ /** Sanitizes a record-shaped Codex agent event payload. */
189
+ function sanitizeCodexAgentEventRecord(value) {
190
+ return sanitizeCodexAgentEventValue(value);
191
+ }
192
+ /** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
193
+ function sanitizeCodexToolArguments(value) {
194
+ if (!isJsonObject(value)) return;
195
+ return sanitizeCodexAgentEventRecord(value);
196
+ }
197
+ /** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
198
+ function sanitizeCodexToolResponse(response) {
199
+ return sanitizeCodexAgentEventRecord(response);
200
+ }
201
+ /** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
202
+ function inferCodexDynamicToolMeta(call, detailMode) {
203
+ return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
204
+ }
205
+ //#endregion
206
+ //#region extensions/codex/src/app-server/event-projector-tool-items.ts
207
+ function nativeToolActionFingerprint(item) {
208
+ if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
209
+ type: item.type,
210
+ command: item.command,
211
+ cwd: typeof item.cwd === "string" ? item.cwd : ""
212
+ });
213
+ if (item.type === "fileChange") return JSON.stringify({
214
+ type: item.type,
215
+ changes: itemFileChanges(item)
216
+ });
217
+ }
218
+ function isNativePostToolUseRelayItem(item) {
219
+ switch (item.type) {
220
+ case "commandExecution":
221
+ case "fileChange":
222
+ case "mcpToolCall": return true;
223
+ default: return false;
224
+ }
225
+ }
226
+ function shouldSuppressChannelProgressForItem(item) {
227
+ if (shouldSynthesizeToolProgressForItem(item)) return true;
228
+ return item.type === "dynamicToolCall";
229
+ }
230
+ function itemToolArgs(item) {
231
+ if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
232
+ command: item.command,
233
+ ...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
234
+ });
235
+ if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChangesForTranscript(item) });
236
+ if (item.type === "webSearch") return webSearchToolArgs(item);
237
+ if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
238
+ }
239
+ function isCommandBearingToolItem(item, args) {
240
+ if (item.type === "commandExecution") return true;
241
+ return typeof args?.command === "string" && args.command.trim().length > 0;
242
+ }
243
+ function webSearchToolArgs(item) {
244
+ const action = isJsonObject(item.action) ? item.action : void 0;
245
+ const actionType = action ? readNonEmptyString(action, "type") : void 0;
246
+ const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
247
+ const query = normalizeOptionalString$1(item.query) ?? (action && actionType === "search" ? readNonEmptyString(action, "query") : void 0) ?? queries[0];
248
+ const url = action ? readNonEmptyString(action, "url") : void 0;
249
+ const pattern = action ? readNonEmptyString(action, "pattern") : void 0;
250
+ const args = {};
251
+ if (query) args.query = query;
252
+ if (queries.length > 0) args.queries = queries;
253
+ if (actionType && actionType !== "search") args.action = actionType;
254
+ if (url) args.url = url;
255
+ if (pattern) args.pattern = pattern;
256
+ if (!query && !url && !pattern) args.queryUnavailable = true;
257
+ return sanitizeCodexAgentEventRecord(args);
258
+ }
259
+ function itemToolResult(item) {
260
+ if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
261
+ status: item.status,
262
+ exitCode: item.exitCode,
263
+ durationMs: item.durationMs
264
+ }) };
265
+ if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
266
+ status: item.status,
267
+ changes: itemFileChanges(item)
268
+ }) };
269
+ if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
270
+ status: item.status,
271
+ durationMs: item.durationMs,
272
+ ...item.error ? { error: item.error } : {},
273
+ ...item.result ? { result: item.result } : {}
274
+ }) };
275
+ if (item.type === "webSearch") return { result: webSearchToolResult(item) };
276
+ return {};
277
+ }
278
+ function webSearchToolResult(item) {
279
+ return sanitizeCodexAgentEventRecord({
280
+ status: itemStatus(item),
281
+ ...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
282
+ ...webSearchToolArgs(item)
283
+ });
284
+ }
285
+ function itemFileChangeRecords(item) {
286
+ const changes = item.changes;
287
+ return Array.isArray(changes) ? changes.filter(isJsonObject) : [];
288
+ }
289
+ function itemFileChanges(item) {
290
+ return itemFileChangeRecords(item).flatMap((change) => {
291
+ const path = normalizeOptionalString$1(change.path);
292
+ if (!path || change.kind === void 0) return [];
293
+ return [{
294
+ path,
295
+ kind: change.kind
296
+ }];
297
+ });
298
+ }
299
+ function fileChangeKindType(kind) {
300
+ if (typeof kind === "string") return kind;
301
+ return isJsonObject(kind) ? normalizeOptionalString$1(kind.type) : void 0;
302
+ }
303
+ function countFileContentLines(content) {
304
+ if (!content) return 0;
305
+ const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
306
+ if (lines.length > 1 && lines.at(-1) === "") lines.pop();
307
+ return lines.length;
308
+ }
309
+ function fileChangeDiffStat(diff, kind) {
310
+ const kindType = fileChangeKindType(kind);
311
+ if (kindType === "add") return {
312
+ added: countFileContentLines(diff),
313
+ removed: 0
314
+ };
315
+ if (kindType === "delete") return {
316
+ added: 0,
317
+ removed: countFileContentLines(diff)
318
+ };
319
+ let added = 0;
320
+ let removed = 0;
321
+ let inHunk = false;
322
+ for (const line of diff.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n")) {
323
+ if (line.startsWith("@@")) {
324
+ inHunk = true;
325
+ continue;
326
+ }
327
+ if (!inHunk) continue;
328
+ if (line.startsWith("+")) added += 1;
329
+ else if (line.startsWith("-")) removed += 1;
330
+ }
331
+ return {
332
+ added,
333
+ removed
334
+ };
335
+ }
336
+ function truncateFileChangeDiffAtLineBoundary(diff, maxChars) {
337
+ if (diff.length <= maxChars) return { diff };
338
+ if (maxChars <= 0) return { diffTruncated: true };
339
+ const boundary = diff.lastIndexOf("\n", maxChars - 1);
340
+ return boundary >= 0 ? {
341
+ diff: diff.slice(0, boundary + 1),
342
+ diffTruncated: true
343
+ } : { diffTruncated: true };
344
+ }
345
+ function itemFileChangesForTranscript(item) {
346
+ let remainingDiffChars = 1e4;
347
+ return itemFileChangeRecords(item).flatMap((change) => {
348
+ const path = normalizeOptionalString$1(change.path);
349
+ if (!path || change.kind === void 0) return [];
350
+ const result = {
351
+ path,
352
+ kind: change.kind
353
+ };
354
+ if (typeof change.diff !== "string") return [result];
355
+ result.stat = fileChangeDiffStat(change.diff, change.kind);
356
+ const bounded = truncateFileChangeDiffAtLineBoundary(change.diff, remainingDiffChars);
357
+ if (bounded.diff !== void 0) {
358
+ result.diff = bounded.diff;
359
+ remainingDiffChars -= bounded.diff.length;
360
+ }
361
+ if (bounded.diffTruncated) result.diffTruncated = true;
362
+ return [result];
363
+ });
364
+ }
365
+ function itemToolError(item, status, outputTextByItem) {
366
+ if (status === "blocked") return "codex native tool blocked";
367
+ if (status !== "failed") return;
368
+ return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
369
+ }
370
+ function itemMeta(item, detailMode = "explain") {
371
+ if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
372
+ command: item.command,
373
+ cwd: typeof item.cwd === "string" ? item.cwd : void 0
374
+ }, { detailMode });
375
+ if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
376
+ const toolName = itemName(item);
377
+ if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
378
+ }
379
+ function itemOutputText(item, outputTextByItem) {
380
+ if (item.type === "commandExecution") {
381
+ const output = item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim();
382
+ return output ? truncateToolTranscriptText(output) : void 0;
383
+ }
384
+ if (item.type === "dynamicToolCall") {
385
+ const output = collectDynamicToolContentText(item.contentItems).trim();
386
+ return output ? truncateToolTranscriptText(output) : void 0;
387
+ }
388
+ if (item.type === "mcpToolCall") {
389
+ const output = item.error ? stringifyJsonValue(item.error) : item.result ? stringifyJsonValue(item.result) : void 0;
390
+ return output ? truncateToolTranscriptText(output) : void 0;
391
+ }
392
+ }
393
+ function itemTranscriptResultText(item, outputTextByItem) {
394
+ const output = itemOutputText(item, outputTextByItem);
395
+ if (output) return output;
396
+ const result = itemToolResult(item).result;
397
+ const resultText = result ? stringifyJsonValue(result) : void 0;
398
+ return resultText ? truncateToolTranscriptText(resultText) : itemStatus(item);
399
+ }
400
+ function stringifyJsonValue(value) {
401
+ try {
402
+ return JSON.stringify(value, null, 2);
403
+ } catch {
404
+ return;
405
+ }
406
+ }
407
+ //#endregion
408
+ //#region extensions/codex/src/app-server/event-projector-tool-progress.ts
409
+ const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = /* @__PURE__ */ new Set([
410
+ "message",
411
+ "messages",
412
+ "reply",
413
+ "send",
414
+ "reaction",
415
+ "react",
416
+ "typing"
417
+ ]);
418
+ function shouldEmitTranscriptToolProgress(toolName, _args) {
419
+ const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
420
+ return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
421
+ }
422
+ var CodexToolProgressProjection = class {
423
+ constructor(params) {
424
+ this.params = params;
425
+ this.echoesByItem = /* @__PURE__ */ new Map();
426
+ this.resultSummaryItemIds = /* @__PURE__ */ new Set();
427
+ this.resultOutputItemIds = /* @__PURE__ */ new Set();
428
+ this.resultOutputStreamedItemIds = /* @__PURE__ */ new Set();
429
+ this.transcriptProgressSuppressedIds = /* @__PURE__ */ new Set();
430
+ this.transcriptArgumentsById = /* @__PURE__ */ new Map();
431
+ this.resultOutputDeltaState = /* @__PURE__ */ new Map();
432
+ this.output = new ToolOutputAccumulator();
433
+ this.metas = /* @__PURE__ */ new Map();
434
+ this.sideEffectingNativeIds = /* @__PURE__ */ new Set();
435
+ this.sideEffectingDynamicIds = /* @__PURE__ */ new Set();
436
+ this.transcriptProgressCallIds = /* @__PURE__ */ new Set();
437
+ }
438
+ get outputTextByItem() {
439
+ return this.output.textByItem;
440
+ }
441
+ get toolMetas() {
442
+ return [...this.metas.values()];
443
+ }
444
+ getToolMeta(itemId) {
445
+ return this.metas.get(itemId);
446
+ }
447
+ get lastToolError() {
448
+ return this.lastNativeToolError;
449
+ }
450
+ get hasPotentialSideEffects() {
451
+ return this.sideEffectingNativeIds.size > 0 || this.sideEffectingDynamicIds.size > 0;
452
+ }
453
+ setLastToolError(error) {
454
+ if (!error) {
455
+ this.lastNativeToolError = void 0;
456
+ return;
457
+ }
458
+ const terminalResolution = this.params.observeToolTerminal?.({
459
+ toolName: error.toolName,
460
+ ...error.meta ? { meta: error.meta } : {},
461
+ outcome: "failure",
462
+ failure: {
463
+ ...error.errorCode ? { errorCode: error.errorCode } : {},
464
+ ...error.error ? { error: error.error } : {},
465
+ ...error.validationErrorSummary ? { validationErrorSummary: error.validationErrorSummary } : {},
466
+ ...error.timedOut ? { timedOut: true } : {},
467
+ ...error.middlewareError ? { middlewareError: true } : {}
468
+ },
469
+ nativeMutation: {
470
+ mutatingAction: error.mutatingAction === true,
471
+ replaySafe: error.mutatingAction !== true,
472
+ ...error.actionFingerprint ? { actionFingerprint: error.actionFingerprint } : {},
473
+ ...error.fileTarget ? { fileTarget: error.fileTarget } : {}
474
+ }
475
+ });
476
+ this.lastNativeToolError = terminalResolution?.lastToolError ?? (this.lastNativeToolError?.mutatingAction && error.mutatingAction !== true ? this.lastNativeToolError : error);
477
+ }
478
+ recordDynamicToolResult(params) {
479
+ const resultText = collectDynamicToolContentText(params.contentItems);
480
+ const existing = this.metas.get(params.callId);
481
+ this.metas.set(params.callId, {
482
+ toolName: existing?.toolName ?? params.tool,
483
+ ...existing?.meta ? { meta: existing.meta } : {},
484
+ ...params.asyncStarted === true ? { asyncStarted: true } : {},
485
+ isError: !params.success
486
+ });
487
+ if (params.terminalResolution) this.lastNativeToolError = params.terminalResolution.lastToolError;
488
+ else if (!params.success) this.lastNativeToolError = {
489
+ toolName: params.tool,
490
+ error: resultText || (params.terminalType === "blocked" ? "codex dynamic tool blocked" : "codex dynamic tool failed")
491
+ };
492
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
493
+ if (params.sideEffectEvidence === true) this.sideEffectingDynamicIds.add(params.callId);
494
+ }
495
+ handleOutputDelta(params, toolName) {
496
+ const itemId = readStringField(params, "itemId");
497
+ const delta = readStringField(params, "delta");
498
+ if (!itemId || !delta) return;
499
+ const storedOutput = this.output.append(itemId, delta);
500
+ this.rememberEcho(itemId, {
501
+ displayText: storedOutput.text,
502
+ rawLength: storedOutput.normalizedLength,
503
+ rawPrefix: storedOutput.rawPrefix,
504
+ streamedDisplay: true
505
+ });
506
+ if (!this.shouldEmitToolOutput()) return;
507
+ if (this.transcriptProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.transcriptArgumentsById.get(itemId))) return;
508
+ const state = this.resultOutputDeltaState.get(itemId) ?? {
509
+ chars: 0,
510
+ messages: 0,
511
+ truncated: false
512
+ };
513
+ if (state.truncated) return;
514
+ const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
515
+ const remainingMessages = Math.max(0, 20 - state.messages);
516
+ if (remainingChars === 0 || remainingMessages === 0) {
517
+ state.truncated = true;
518
+ this.resultOutputDeltaState.set(itemId, state);
519
+ this.emitToolResultMessage({
520
+ itemId,
521
+ text: formatToolOutput(toolName, void 0, "(output truncated)")
522
+ });
523
+ return;
524
+ }
525
+ const chunk = delta.length > remainingChars ? truncateUtf16Safe(delta, remainingChars) : delta;
526
+ state.chars += chunk.length;
527
+ state.messages += 1;
528
+ const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= 20;
529
+ if (reachedLimit) state.truncated = true;
530
+ this.resultOutputDeltaState.set(itemId, state);
531
+ this.resultOutputStreamedItemIds.add(itemId);
532
+ this.emitToolResultMessage({
533
+ itemId,
534
+ text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
535
+ });
536
+ }
537
+ recordNativeToolError(params) {
538
+ const executionStarted = params.status !== "blocked";
539
+ const mutatingAction = executionStarted && isMutatingNativeToolItem(params.item);
540
+ const actionFingerprint = mutatingAction ? nativeToolActionFingerprint(params.item) : void 0;
541
+ const isFailure = isNonSuccessItemStatus(params.status);
542
+ const error = isFailure ? itemToolError(params.item, params.status, this.output.textByItem) : void 0;
543
+ const terminalResolution = this.params.observeToolTerminal?.({
544
+ toolCallId: params.item.id,
545
+ toolName: params.name,
546
+ arguments: itemToolArgs(params.item),
547
+ ...params.meta ? { meta: params.meta } : {},
548
+ executionStarted,
549
+ outcome: isFailure ? "failure" : "success",
550
+ ...isFailure ? { failure: error ? { error } : {} } : {},
551
+ nativeMutation: {
552
+ mutatingAction,
553
+ replaySafe: !mutatingAction,
554
+ ...actionFingerprint ? { actionFingerprint } : {}
555
+ }
556
+ });
557
+ if (terminalResolution) {
558
+ this.lastNativeToolError = terminalResolution.lastToolError;
559
+ return;
560
+ }
561
+ if (isFailure) this.lastNativeToolError = {
562
+ toolName: params.name,
563
+ ...params.meta ? { meta: params.meta } : {},
564
+ ...error ? { error } : {},
565
+ ...mutatingAction ? { mutatingAction: true } : {},
566
+ ...actionFingerprint ? { actionFingerprint } : {}
567
+ };
568
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
569
+ }
570
+ emitToolResultSummary(item) {
571
+ if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
572
+ if (this.resultSummaryItemIds.has(item.id)) return;
573
+ const toolName = itemName(item);
574
+ const args = itemToolArgs(item);
575
+ if (!toolName || !shouldEmitTranscriptToolProgress(toolName, args)) return;
576
+ this.resultSummaryItemIds.add(item.id);
577
+ const meta = this.shouldEmitToolOutput() || !isCommandBearingToolItem(item, args) ? itemMeta(item, this.toolProgressDetailMode()) : void 0;
578
+ this.emitToolResultMessage({
579
+ itemId: item.id,
580
+ text: formatToolSummary(toolName, meta)
581
+ });
582
+ }
583
+ emitToolResultOutput(item) {
584
+ if (!item || !this.params.onToolResult || !this.shouldEmitToolOutput()) return;
585
+ if (this.resultOutputItemIds.has(item.id) || this.resultOutputStreamedItemIds.has(item.id)) return;
586
+ const toolName = itemName(item);
587
+ const output = itemOutputText(item, this.output.textByItem);
588
+ if (!toolName || !output || !shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
589
+ this.emitToolResultMessage({
590
+ itemId: item.id,
591
+ text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
592
+ finalOutput: true,
593
+ isError: isNonSuccessItemStatus(itemStatus(item))
594
+ });
595
+ }
596
+ recordToolMeta(item) {
597
+ if (!item) return;
598
+ if (isSideEffectingNativeToolItem(item)) this.sideEffectingNativeIds.add(item.id);
599
+ else this.sideEffectingNativeIds.delete(item.id);
600
+ const toolName = itemName(item);
601
+ if (!toolName) return;
602
+ const meta = itemMeta(item, this.toolProgressDetailMode());
603
+ const existing = this.metas.get(item.id);
604
+ const terminalStatus = auditNativeToolTerminalStatus(item);
605
+ const isError = typeof existing?.isError === "boolean" ? existing.isError : terminalStatus === "completed" ? false : terminalStatus === "failed" || terminalStatus === "blocked" ? true : void 0;
606
+ this.metas.set(item.id, {
607
+ toolName,
608
+ ...meta ? { meta } : {},
609
+ ...existing?.asyncStarted ? { asyncStarted: true } : {},
610
+ ...isError === void 0 ? {} : { isError }
611
+ });
612
+ }
613
+ recordTranscriptCall(params) {
614
+ this.transcriptArgumentsById.set(params.id, params.arguments);
615
+ if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptProgressSuppressedIds.add(params.id);
616
+ else this.transcriptProgressSuppressedIds.delete(params.id);
617
+ this.emitTranscriptToolCallProgress(params);
618
+ }
619
+ recordTranscriptResult(params) {
620
+ this.emitTranscriptToolResultProgress(params);
621
+ }
622
+ matchesEcho(text) {
623
+ for (const state of this.echoesByItem.values()) {
624
+ if (state.streamedDisplayText === text || state.displayTexts.includes(text)) return true;
625
+ if (state.streamedRawSignature && text.length === state.streamedRawSignature.length && text.startsWith(state.streamedRawSignature.prefix)) return true;
626
+ for (const signature of state.rawSignatures) if (text.length === signature.length && text.startsWith(signature.prefix)) return true;
627
+ }
628
+ return false;
629
+ }
630
+ rememberCommandAggregateOutputEcho(item) {
631
+ if (item?.type !== "commandExecution" || typeof item.aggregatedOutput !== "string") return;
632
+ const signature = toolOutputRawEchoSignature(item.aggregatedOutput);
633
+ if (signature) this.rememberEcho(item.id, signature);
634
+ }
635
+ toolProgressDetailMode() {
636
+ return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
637
+ }
638
+ emitToolResultMessage(params) {
639
+ const rawText = params.text.trim();
640
+ const text = truncateToolTranscriptText(rawText);
641
+ if (!text) return;
642
+ this.rememberEcho(params.itemId, {
643
+ displayText: text,
644
+ rawText
645
+ });
646
+ if (params.finalOutput) this.resultOutputItemIds.add(params.itemId);
647
+ try {
648
+ Promise.resolve(this.params.onToolResult?.({
649
+ text,
650
+ ...params.isError === true ? { isError: true } : {}
651
+ })).catch(() => {});
652
+ } catch {}
653
+ }
654
+ shouldEmitToolResult() {
655
+ return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
656
+ }
657
+ shouldEmitToolOutput() {
658
+ return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
659
+ }
660
+ emitTranscriptToolCallProgress(params) {
661
+ if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
662
+ this.transcriptProgressCallIds.add(params.id);
663
+ const args = normalizeToolTranscriptArguments(params.arguments);
664
+ const meta = this.shouldEmitToolOutput() || !isCodexCommandBearingToolCall(params.name, args) ? inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() }) : void 0;
665
+ if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.resultSummaryItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
666
+ this.resultSummaryItemIds.add(params.id);
667
+ this.emitToolResultMessage({
668
+ itemId: params.id,
669
+ text: formatToolSummary(params.name, meta)
670
+ });
671
+ }
672
+ emitTranscriptToolResultProgress(params) {
673
+ if (this.transcriptProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.transcriptArgumentsById.get(params.id))) return;
674
+ if (!this.transcriptProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
675
+ id: params.id,
676
+ name: params.name,
677
+ arguments: {}
678
+ });
679
+ if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.resultOutputItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
680
+ const text = params.text?.trim();
681
+ if (text) this.emitToolResultMessage({
682
+ itemId: params.id,
683
+ text: formatToolOutput(params.name, void 0, text),
684
+ finalOutput: true,
685
+ isError: params.isError
686
+ });
687
+ }
688
+ rememberEcho(itemId, signature) {
689
+ if (!itemId) return;
690
+ const existing = this.echoesByItem.get(itemId) ?? {
691
+ displayTexts: [],
692
+ rawSignatures: []
693
+ };
694
+ const displayText = signature.displayText?.trim();
695
+ if (displayText) {
696
+ if (signature.streamedDisplay) existing.streamedDisplayText = displayText;
697
+ else if (!existing.displayTexts.includes(displayText)) {
698
+ if (existing.displayTexts.length >= 24) existing.displayTexts.shift();
699
+ existing.displayTexts.push(displayText);
700
+ }
701
+ }
702
+ const rawText = signature.rawText?.trim();
703
+ const rawLength = signature.rawLength ?? rawText?.length;
704
+ const rawPrefix = signature.rawPrefix?.trim() ?? rawText;
705
+ if (rawLength !== void 0 && rawPrefix && rawPrefix.length >= 1024) {
706
+ const next = {
707
+ length: rawLength,
708
+ prefix: rawPrefix.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
709
+ };
710
+ if (signature.streamedDisplay) existing.streamedRawSignature = next;
711
+ else {
712
+ const matchIndex = existing.rawSignatures.findIndex((entry) => entry.prefix === next.prefix);
713
+ if (matchIndex >= 0) existing.rawSignatures[matchIndex] = next;
714
+ else {
715
+ if (existing.rawSignatures.length >= 24) existing.rawSignatures.shift();
716
+ existing.rawSignatures.push(next);
717
+ }
718
+ }
719
+ }
720
+ this.echoesByItem.set(itemId, existing);
721
+ }
722
+ };
723
+ //#endregion
724
+ //#region extensions/codex/src/app-server/session-history.ts
725
+ /**
726
+ * Reads OpenClaw session history for Codex transcript mirroring and sanitizes
727
+ * image payloads before replaying messages into the app-server projector.
728
+ */
729
+ function isMissingFileError(error) {
730
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
731
+ }
732
+ /** Returns sanitized session-context messages for a Codex mirrored session file. */
733
+ async function readCodexMirroredSessionHistoryMessages(target, admission) {
734
+ try {
735
+ const entries = await readCodexMirroredSessionEntries(target, admission);
736
+ if (entries.length === 0) return [];
737
+ const firstEntry = entries[0];
738
+ if (firstEntry?.type !== "session") return [];
739
+ if (typeof firstEntry.id !== "string") return;
740
+ if (firstEntry.id !== target.sessionId) return [];
741
+ migrateSessionEntries(entries);
742
+ return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => {
743
+ return entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.type !== "session";
744
+ })).messages, "codex mirrored history");
745
+ } catch (error) {
746
+ if (isMissingFileError(error)) return [];
747
+ return;
748
+ }
749
+ }
750
+ async function readCodexMirroredSessionEntries(target, admission) {
751
+ if (target.sessionTarget) {
752
+ const { agentId, sessionId, sessionKey, storePath } = target.sessionTarget;
753
+ if (!agentId || !sessionId || !sessionKey || !storePath || sessionId !== target.sessionId || target.agentId !== void 0 && agentId !== target.agentId || target.sessionKey !== void 0 && sessionKey !== target.sessionKey) return [];
754
+ const transcriptTarget = {
755
+ agentId,
756
+ sessionId,
757
+ sessionKey,
758
+ storePath
759
+ };
760
+ return await (admission ? readCodexSessionTranscriptEventsBeforeAdmission(transcriptTarget, admission) : readSessionTranscriptEvents(transcriptTarget));
761
+ }
762
+ const sqliteMarker = parseSqliteSessionFileMarker(target.sessionFile);
763
+ if (sqliteMarker) {
764
+ if (sqliteMarker.sessionId !== target.sessionId || target.agentId !== void 0 && sqliteMarker.agentId !== target.agentId) return [];
765
+ const sessionKey = resolveSqliteMarkerSessionKey(target, sqliteMarker);
766
+ if (!sessionKey) return [];
767
+ const transcriptTarget = {
768
+ agentId: sqliteMarker.agentId,
769
+ sessionId: sqliteMarker.sessionId,
770
+ sessionKey,
771
+ storePath: sqliteMarker.storePath
772
+ };
773
+ return await (admission ? readCodexSessionTranscriptEventsBeforeAdmission(transcriptTarget, admission) : readSessionTranscriptEvents(transcriptTarget));
774
+ }
775
+ if (admission) {
776
+ if (admission.sessionId !== target.sessionId || target.agentId !== void 0 && admission.agentId !== target.agentId || target.sessionKey !== void 0 && admission.sessionKey !== target.sessionKey) return [];
777
+ return await readCodexSessionTranscriptEventsBeforeAdmission({
778
+ agentId: admission.agentId,
779
+ sessionId: admission.sessionId,
780
+ sessionKey: admission.sessionKey,
781
+ storePath: admission.storePath
782
+ }, admission);
783
+ }
784
+ return parseSessionEntries(await fs.readFile(target.sessionFile, "utf-8"));
785
+ }
786
+ function resolveSqliteMarkerSessionKey(target, marker) {
787
+ const explicitSessionKey = target.sessionKey?.trim();
788
+ if (explicitSessionKey) {
789
+ const explicitEntry = getSessionEntry({
790
+ agentId: marker.agentId,
791
+ sessionKey: explicitSessionKey,
792
+ storePath: marker.storePath
793
+ });
794
+ if (explicitEntry) return explicitEntry.sessionId === marker.sessionId ? explicitSessionKey : void 0;
795
+ }
796
+ return resolveTranscriptSessionKeyBySessionId({
797
+ agentId: marker.agentId,
798
+ sessionId: marker.sessionId,
799
+ storePath: marker.storePath
800
+ });
801
+ }
802
+ //#endregion
803
+ //#region extensions/codex/src/app-server/event-projector-tool-transcript.ts
804
+ const ZERO_USAGE = {
805
+ input: 0,
806
+ output: 0,
807
+ cacheRead: 0,
808
+ cacheWrite: 0,
809
+ totalTokens: 0,
810
+ cost: {
811
+ input: 0,
812
+ output: 0,
813
+ cacheRead: 0,
814
+ cacheWrite: 0,
815
+ total: 0
816
+ }
817
+ };
818
+ const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
819
+ const NATIVE_PATCH_REJECTION_RE = /^\s*patch rejected:\s*writing outside of the project;\s*rejected by user approval settings\s*$/iu;
820
+ const CODE_MODE_NATIVE_PATCH_SOURCE_RE = /^\s*(?:\/\/[^\r\n]*\r?\n\s*)?(?:const|let)\s+([A-Za-z_$][\w$]*)\s*=\s*await\s+tools\.apply_patch\(\s*("(?:\\[\s\S]|[^"\\])*")\s*\)\s*;?\s*text\(\s*\1\s*\)\s*;?\s*$/u;
821
+ const CODE_MODE_NATIVE_PATCH_RESULT_RE = /^\s*Script (completed|failed)\s*\r?\nWall time\s+\d+(?:\.\d+)?\s+seconds\s*\r?\nOutput:\s*([\s\S]*?)\s*$/iu;
822
+ function readCodeModeNativePatchInput(source) {
823
+ if (typeof source !== "string") return;
824
+ const match = CODE_MODE_NATIVE_PATCH_SOURCE_RE.exec(source);
825
+ if (!match?.[2]) return;
826
+ try {
827
+ const patch = JSON.parse(match[2]);
828
+ return typeof patch === "string" && /^\*\*\* Begin Patch\r?\n[\s\S]*\r?\n\*\*\* End Patch(?:\r?\n)?$/u.test(patch) ? patch : void 0;
829
+ } catch {
830
+ return;
831
+ }
832
+ }
833
+ function readInterceptedNativePatchInput(command) {
834
+ if (typeof command !== "string") return;
835
+ const lines = command.replace(/\r\n?/gu, "\n").split("\n");
836
+ const patchStart = lines.indexOf("*** Begin Patch");
837
+ const invocation = /^[\t ]*(?:cd[\t ]+(?:'([^'\n]+)'|([A-Za-z0-9_./-]+))[\t ]+&&[\t ]+)?apply_patch[\t ]*<<-?[\t ]*'([^'\n]+)'[\t ]*$/u.exec(lines[0] ?? "");
838
+ if (!invocation || patchStart !== 1) return;
839
+ const patchEnd = lines.indexOf("*** End Patch", patchStart + 1);
840
+ const cwd = invocation[1] ?? invocation[2];
841
+ const delimiter = invocation[3];
842
+ if (patchEnd < 0 || lines[patchEnd + 1] !== delimiter || lines.slice(patchEnd + 2).some((line) => line.trim().length > 0)) return;
843
+ return {
844
+ input: `${lines.slice(patchStart, patchEnd + 1).join("\n")}\n`,
845
+ ...cwd ? { cwd } : {}
846
+ };
847
+ }
848
+ var CodexToolTranscriptProjection = class {
849
+ constructor(params, threadId, turnId, progress, nextTranscriptTimestamp, options = {}) {
850
+ this.params = params;
851
+ this.threadId = threadId;
852
+ this.turnId = turnId;
853
+ this.progress = progress;
854
+ this.nextTranscriptTimestamp = nextTranscriptTimestamp;
855
+ this.options = options;
856
+ this.messages = [];
857
+ this.callIds = /* @__PURE__ */ new Set();
858
+ this.resultIds = /* @__PURE__ */ new Set();
859
+ this.namesById = /* @__PURE__ */ new Map();
860
+ this.trajectoryCallIds = /* @__PURE__ */ new Set();
861
+ this.trajectoryResultIds = /* @__PURE__ */ new Set();
862
+ this.trajectoryNamesById = /* @__PURE__ */ new Map();
863
+ this.trajectoryItemsById = /* @__PURE__ */ new Map();
864
+ this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
865
+ this.nativeMcpAppResultDetails = /* @__PURE__ */ new Map();
866
+ this.nativeMcpAppResultDetailsAttempted = /* @__PURE__ */ new Set();
867
+ this.rawNativeToolOutputByCallId = /* @__PURE__ */ new Map();
868
+ this.codeModeNativePatchInputsByCallId = /* @__PURE__ */ new Map();
869
+ }
870
+ get transcriptMessages() {
871
+ return this.messages;
872
+ }
873
+ recordDynamicToolCall(params) {
874
+ this.recordToolCall({
875
+ id: params.callId,
876
+ name: params.tool,
877
+ arguments: sanitizeCodexToolArguments(params.arguments)
878
+ });
879
+ }
880
+ recordDynamicToolResult(params, resultContentSource) {
881
+ this.recordToolResult({
882
+ id: params.callId,
883
+ name: params.tool,
884
+ text: collectDynamicToolContentText(params.contentItems),
885
+ isError: !params.success,
886
+ details: params.details,
887
+ ...resultContentSource ? { resultContentSource } : {}
888
+ });
889
+ }
890
+ recordNativeToolCall(item) {
891
+ if (!item || !shouldRecordNativeToolTranscript(item)) return;
892
+ const name = itemName(item);
893
+ if (name) this.recordToolCall({
894
+ id: item.id,
895
+ name,
896
+ arguments: itemToolArgs(item)
897
+ });
898
+ }
899
+ recordNativeToolResult(item, details) {
900
+ if (!item || !shouldRecordNativeToolTranscript(item)) return;
901
+ const name = itemName(item);
902
+ if (name) this.recordToolResult({
903
+ id: item.id,
904
+ name,
905
+ text: this.rawNativeToolOutputByCallId.get(item.id) ?? itemTranscriptResultText(item, this.progress.outputTextByItem),
906
+ isError: isNonSuccessItemStatus(itemStatus(item)),
907
+ details,
908
+ ...item.type === "webSearch" ? { resultContentSource: "network" } : {}
909
+ });
910
+ }
911
+ recordRawNativeToolItem(item) {
912
+ const type = typeof item.type === "string" ? item.type : void 0;
913
+ const callId = typeof item.call_id === "string" ? item.call_id : typeof item.callId === "string" ? item.callId : void 0;
914
+ if (!callId) return;
915
+ if ((type === "custom_tool_call" || type === "function_call") && (item.name === "apply_patch" || item.name === "exec_command" || item.name === "exec")) {
916
+ let args;
917
+ if (type === "custom_tool_call" && item.name === "apply_patch" && typeof item.input === "string") args = { input: item.input };
918
+ else if (type === "custom_tool_call" && item.name === "exec") {
919
+ const input = readCodeModeNativePatchInput(item.input);
920
+ if (input) this.codeModeNativePatchInputsByCallId.set(callId, input);
921
+ return;
922
+ } else if (type === "function_call" && typeof item.arguments === "string") try {
923
+ const parsed = JSON.parse(item.arguments);
924
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
925
+ const parsedArguments = parsed;
926
+ if (item.name === "apply_patch") args = parsedArguments;
927
+ else {
928
+ const patch = readInterceptedNativePatchInput(typeof parsedArguments.cmd === "string" ? parsedArguments.cmd : typeof parsedArguments.command === "string" ? parsedArguments.command : void 0);
929
+ if (patch) {
930
+ const workdir = typeof parsedArguments.workdir === "string" ? parsedArguments.workdir : typeof parsedArguments.cwd === "string" ? parsedArguments.cwd : void 0;
931
+ const cwd = patch.cwd ? workdir && !path.isAbsolute(patch.cwd) ? path.join(workdir, patch.cwd) : patch.cwd : workdir;
932
+ args = {
933
+ input: patch.input,
934
+ ...cwd ? { cwd } : {}
935
+ };
936
+ }
937
+ }
938
+ }
939
+ } catch {
940
+ return;
941
+ }
942
+ if (args) this.recordToolCall({
943
+ id: callId,
944
+ name: "apply_patch",
945
+ arguments: args
946
+ });
947
+ return;
948
+ }
949
+ if (type !== "custom_tool_call_output" && type !== "function_call_output" || this.namesById.get(callId) !== "apply_patch" && !this.codeModeNativePatchInputsByCallId.has(callId)) return;
950
+ const text = typeof item.output === "string" ? item.output : Array.isArray(item.output) ? collectDynamicToolContentText(item.output) : "";
951
+ if (!text.trim()) return;
952
+ const codeModePatchInput = this.codeModeNativePatchInputsByCallId.get(callId);
953
+ if (codeModePatchInput) {
954
+ this.codeModeNativePatchInputsByCallId.delete(callId);
955
+ const execution = CODE_MODE_NATIVE_PATCH_RESULT_RE.exec(text);
956
+ if (execution?.[1]?.toLowerCase() === "completed" && execution[2]?.trim() === "{}") return;
957
+ if (execution?.[1]?.toLowerCase() === "failed") {
958
+ const failure = execution[2]?.replace(/^Script error:\s*/iu, "").trim() || text;
959
+ this.recordToolCall({
960
+ id: callId,
961
+ name: "apply_patch",
962
+ arguments: { input: codeModePatchInput }
963
+ });
964
+ this.recordToolResult({
965
+ id: callId,
966
+ name: "apply_patch",
967
+ text: failure,
968
+ isError: true
969
+ });
970
+ }
971
+ return;
972
+ }
973
+ this.rawNativeToolOutputByCallId.set(callId, text);
974
+ const result = this.messages.find((message) => message.role === "toolResult" && message.toolCallId === callId);
975
+ if (!result) {
976
+ if (NATIVE_PATCH_REJECTION_RE.test(text)) this.recordToolResult({
977
+ id: callId,
978
+ name: "apply_patch",
979
+ text,
980
+ isError: true
981
+ });
982
+ return;
983
+ }
984
+ result.content = this.createToolResultMessage({
985
+ id: callId,
986
+ name: "apply_patch",
987
+ text,
988
+ isError: result.isError === true
989
+ }).content;
990
+ }
991
+ async recordNativeToolResultWithDetails(item) {
992
+ this.recordNativeToolResult(item, await this.prepareNativeMcpAppResultDetails(item));
993
+ }
994
+ async prepareNativeMcpAppResultDetails(item) {
995
+ if (!item || item.type !== "mcpToolCall" || itemStatus(item) === "running") return;
996
+ if (this.nativeMcpAppResultDetails.has(item.id)) return this.nativeMcpAppResultDetails.get(item.id);
997
+ if (this.nativeMcpAppResultDetailsAttempted.has(item.id) || !this.options.prepareNativeMcpAppResultDetails) return;
998
+ this.nativeMcpAppResultDetailsAttempted.add(item.id);
999
+ try {
1000
+ const details = await this.options.prepareNativeMcpAppResultDetails(item);
1001
+ if (details !== void 0) this.nativeMcpAppResultDetails.set(item.id, details);
1002
+ return details;
1003
+ } catch (error) {
1004
+ embeddedAgentLog.debug("codex native MCP App preview preparation failed", {
1005
+ itemId: item.id,
1006
+ error
1007
+ });
1008
+ return;
1009
+ }
1010
+ }
1011
+ recordTrajectoryEvent(params) {
1012
+ if (params.phase === "start") {
1013
+ this.trajectoryCallIds.add(params.item.id);
1014
+ this.trajectoryNamesById.set(params.item.id, params.name);
1015
+ this.trajectoryItemsById.set(params.item.id, params.item);
1016
+ this.options.trajectoryRecorder?.recordEvent("tool.call", {
1017
+ threadId: this.threadId,
1018
+ turnId: this.turnId,
1019
+ itemId: params.item.id,
1020
+ toolCallId: params.item.id,
1021
+ name: params.name,
1022
+ arguments: params.args
1023
+ });
1024
+ return;
1025
+ }
1026
+ this.trajectoryResultIds.add(params.item.id);
1027
+ const toolResult = itemToolResult(params.item).result;
1028
+ const output = itemOutputText(params.item, this.progress.outputTextByItem);
1029
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
1030
+ threadId: this.threadId,
1031
+ turnId: this.turnId,
1032
+ itemId: params.item.id,
1033
+ toolCallId: params.item.id,
1034
+ name: params.name,
1035
+ status: params.status,
1036
+ isError: isNonSuccessItemStatus(params.status),
1037
+ ...toolResult ? { result: toolResult } : {},
1038
+ ...output ? { output } : {}
1039
+ });
1040
+ }
1041
+ emitAfterToolCallObservation(item) {
1042
+ if (!this.shouldEmitAfterToolCallObservation(item)) return;
1043
+ const name = itemName(item);
1044
+ const status = itemStatus(item);
1045
+ if (!name || status === "running") return;
1046
+ this.afterToolCallObservedItemIds.add(item.id);
1047
+ const result = itemToolResult(item).result;
1048
+ const error = itemToolError(item, status, this.progress.outputTextByItem);
1049
+ const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
1050
+ const hookParams = {
1051
+ toolName: name,
1052
+ toolCallId: item.id,
1053
+ runId: this.params.runId,
1054
+ agentId: this.params.agentId,
1055
+ sessionId: this.params.sessionId,
1056
+ sessionKey: this.params.sessionKey,
1057
+ startArgs: itemToolArgs(item) ?? {},
1058
+ ...result !== void 0 ? { result } : {},
1059
+ ...error ? { error } : {},
1060
+ ...startedAt !== void 0 ? { startedAt } : {}
1061
+ };
1062
+ setImmediate(() => {
1063
+ runAgentHarnessAfterToolCallHook(hookParams);
1064
+ });
1065
+ }
1066
+ synthesizeMissingToolResults(params) {
1067
+ if (!params.synthesize) return;
1068
+ const missingTranscriptIds = [...this.callIds].filter((id) => !this.resultIds.has(id));
1069
+ const missingTrajectoryIds = [...this.trajectoryCallIds].filter((id) => !this.trajectoryResultIds.has(id));
1070
+ if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
1071
+ for (const id of missingTranscriptIds) {
1072
+ const name = this.namesById.get(id) ?? this.trajectoryNamesById.get(id);
1073
+ if (name) this.recordToolResult({
1074
+ id,
1075
+ name,
1076
+ text: formatMissingToolResultError({
1077
+ id,
1078
+ name
1079
+ }),
1080
+ isError: true
1081
+ });
1082
+ }
1083
+ for (const id of missingTrajectoryIds) {
1084
+ const name = this.trajectoryNamesById.get(id) ?? this.namesById.get(id);
1085
+ if (!name) continue;
1086
+ this.trajectoryResultIds.add(id);
1087
+ const text = formatMissingToolResultError({
1088
+ id,
1089
+ name
1090
+ });
1091
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
1092
+ threadId: this.threadId,
1093
+ turnId: this.turnId,
1094
+ itemId: id,
1095
+ toolCallId: id,
1096
+ name,
1097
+ status: "failed",
1098
+ isError: true,
1099
+ result: {
1100
+ status: "failed",
1101
+ reason: "missing_tool_result"
1102
+ },
1103
+ output: text
1104
+ });
1105
+ }
1106
+ if (params.terminalDisposition === "tool_error") {
1107
+ this.recordMissingToolError(missingTranscriptIds, missingTrajectoryIds);
1108
+ return;
1109
+ }
1110
+ if (params.terminalDisposition === "diagnostic_only") return;
1111
+ const missingCount = (/* @__PURE__ */ new Set([...missingTranscriptIds, ...missingTrajectoryIds])).size;
1112
+ return missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
1113
+ }
1114
+ async readMirroredSessionMessages() {
1115
+ return await readCodexMirroredSessionHistoryMessages({
1116
+ agentId: this.params.agentId,
1117
+ sessionFile: this.params.sessionFile,
1118
+ sessionId: this.params.sessionId,
1119
+ sessionKey: this.params.sessionKey
1120
+ }) ?? [];
1121
+ }
1122
+ recordToolCall(params) {
1123
+ if (!params.id || !params.name || this.callIds.has(params.id)) return;
1124
+ this.callIds.add(params.id);
1125
+ this.namesById.set(params.id, params.name);
1126
+ this.progress.recordTranscriptCall(params);
1127
+ this.messages.push(attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`));
1128
+ }
1129
+ recordToolResult(params) {
1130
+ if (!params.id || !params.name || this.resultIds.has(params.id)) return;
1131
+ this.resultIds.add(params.id);
1132
+ this.progress.recordTranscriptResult(params);
1133
+ this.messages.push(attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`));
1134
+ }
1135
+ recordMissingToolError(missingTranscriptIds, missingTrajectoryIds) {
1136
+ const firstMissingId = missingTranscriptIds.find((id) => Boolean(this.namesById.get(id))) ?? missingTrajectoryIds.find((id) => Boolean(this.trajectoryNamesById.get(id) ?? this.namesById.get(id)));
1137
+ if (!firstMissingId) return;
1138
+ const name = this.namesById.get(firstMissingId) ?? this.trajectoryNamesById.get(firstMissingId);
1139
+ if (!name) return;
1140
+ const item = this.trajectoryItemsById.get(firstMissingId);
1141
+ const meta = item ? itemMeta(item, this.progress.toolProgressDetailMode()) : this.progress.getToolMeta(firstMissingId)?.meta;
1142
+ const actionFingerprint = item ? nativeToolActionFingerprint(item) : void 0;
1143
+ this.progress.setLastToolError({
1144
+ toolName: name,
1145
+ ...meta ? { meta } : {},
1146
+ error: formatMissingToolResultError({
1147
+ id: firstMissingId,
1148
+ name
1149
+ }),
1150
+ ...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {},
1151
+ ...actionFingerprint ? { actionFingerprint } : {}
1152
+ });
1153
+ }
1154
+ shouldEmitAfterToolCallObservation(item) {
1155
+ if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
1156
+ return !(this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item));
1157
+ }
1158
+ createToolCallMessage(params) {
1159
+ const args = normalizeToolTranscriptArguments(params.arguments);
1160
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
1161
+ return {
1162
+ role: "assistant",
1163
+ content: [{
1164
+ type: "toolCall",
1165
+ id: params.id,
1166
+ name: params.name,
1167
+ arguments: args,
1168
+ input: args
1169
+ }],
1170
+ api: attribution.api ?? "openai-chatgpt-responses",
1171
+ provider: attribution.provider,
1172
+ model: this.params.modelId,
1173
+ usage: ZERO_USAGE,
1174
+ stopReason: "toolUse",
1175
+ timestamp: this.nextTranscriptTimestamp()
1176
+ };
1177
+ }
1178
+ createToolResultMessage(params) {
1179
+ const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
1180
+ return {
1181
+ role: "toolResult",
1182
+ toolCallId: params.id,
1183
+ toolName: params.name,
1184
+ isError: params.isError,
1185
+ content: [{
1186
+ type: "toolResult",
1187
+ id: params.id,
1188
+ name: params.name,
1189
+ toolName: params.name,
1190
+ toolCallId: params.id,
1191
+ toolUseId: params.id,
1192
+ tool_use_id: params.id,
1193
+ content: text,
1194
+ text
1195
+ }],
1196
+ ...params.details !== void 0 ? { details: params.details } : {},
1197
+ ...params.resultContentSource ? { __openclaw: { resultContentSource: params.resultContentSource } } : {},
1198
+ timestamp: this.nextTranscriptTimestamp()
1199
+ };
1200
+ }
1201
+ };
1202
+ function formatMissingToolResultError(params) {
1203
+ return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
1204
+ }
1205
+ function toolResultStatusText(params) {
1206
+ return params.isError ? `${params.name} failed` : `${params.name} completed`;
1207
+ }
1208
+ function resolveStartedAtFromDurationMs(durationMs) {
1209
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
1210
+ return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
1211
+ }
1212
+ //#endregion
1213
+ //#region extensions/codex/src/app-server/event-projector-events.ts
1214
+ function projectNormalizedToolItem(params) {
1215
+ const { item } = params;
1216
+ if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
1217
+ const name = itemName(item);
1218
+ if (!name) return;
1219
+ const status = params.phase === "result" ? itemStatus(item) : "running";
1220
+ const args = itemToolArgs(item);
1221
+ const commandBearing = isCommandBearingToolItem(item, args);
1222
+ const meta = itemMeta(item, params.detailMode);
1223
+ return {
1224
+ name,
1225
+ status,
1226
+ args,
1227
+ meta,
1228
+ event: shouldEmitTranscriptToolProgress(name, args) ? {
1229
+ stream: "tool",
1230
+ data: {
1231
+ phase: params.phase,
1232
+ name,
1233
+ itemId: item.id,
1234
+ toolCallId: item.id,
1235
+ ...meta ? { meta } : {},
1236
+ ...commandBearing ? { commandBearing: true } : {},
1237
+ ...params.phase === "start" && args ? { args } : {},
1238
+ ...params.phase === "result" ? {
1239
+ status,
1240
+ isError: isNonSuccessItemStatus(status),
1241
+ ...itemToolResult(item)
1242
+ } : {}
1243
+ }
1244
+ } : void 0
1245
+ };
1246
+ }
1247
+ var CodexEventProjection = class {
1248
+ constructor(threadId, turnId, emitAgentEvent, toolProgress, toolTranscript, onNativeToolResultRecorded) {
1249
+ this.threadId = threadId;
1250
+ this.turnId = turnId;
1251
+ this.emitAgentEvent = emitAgentEvent;
1252
+ this.toolProgress = toolProgress;
1253
+ this.toolTranscript = toolTranscript;
1254
+ this.onNativeToolResultRecorded = onNativeToolResultRecorded;
1255
+ this.reviewCount = 0;
1256
+ }
1257
+ get guardianReviewCount() {
1258
+ return this.reviewCount;
1259
+ }
1260
+ handleGuardianReview(method, params) {
1261
+ this.reviewCount += 1;
1262
+ const review = isJsonObject(params.review) ? params.review : void 0;
1263
+ const action = isJsonObject(params.action) ? params.action : void 0;
1264
+ this.emitAgentEvent({
1265
+ stream: "codex_app_server.guardian",
1266
+ data: {
1267
+ method,
1268
+ phase: method.endsWith("/started") ? "started" : "completed",
1269
+ reviewId: readStringField(params, "reviewId"),
1270
+ targetItemId: readNullableString$1(params, "targetItemId"),
1271
+ decisionSource: readStringField(params, "decisionSource"),
1272
+ status: review ? readStringField(review, "status") : void 0,
1273
+ riskLevel: review ? readStringField(review, "riskLevel") : void 0,
1274
+ userAuthorization: review ? readStringField(review, "userAuthorization") : void 0,
1275
+ rationale: review ? readNullableString$1(review, "rationale") : void 0,
1276
+ actionType: action ? readStringField(action, "type") : void 0
1277
+ }
1278
+ });
1279
+ }
1280
+ handleGuardianWarning(params) {
1281
+ this.emitAgentEvent({
1282
+ stream: "codex_app_server.guardian",
1283
+ data: {
1284
+ phase: "warning",
1285
+ message: readStringField(params, "message")
1286
+ }
1287
+ });
1288
+ }
1289
+ handleHook(method, params) {
1290
+ const run = isJsonObject(params.run) ? params.run : void 0;
1291
+ if (!run) return;
1292
+ const durationMs = asFiniteNumber(run.durationMs);
1293
+ const entries = readHookOutputEntries(run.entries);
1294
+ const hookTurnId = readNullableString$1(params, "turnId");
1295
+ this.emitAgentEvent({
1296
+ stream: "codex_app_server.hook",
1297
+ data: {
1298
+ phase: method === "hook/started" ? "started" : "completed",
1299
+ threadId: this.threadId,
1300
+ turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
1301
+ hookRunId: readStringField(run, "id"),
1302
+ eventName: readStringField(run, "eventName"),
1303
+ handlerType: readStringField(run, "handlerType"),
1304
+ executionMode: readStringField(run, "executionMode"),
1305
+ scope: readStringField(run, "scope"),
1306
+ source: readStringField(run, "source"),
1307
+ sourcePath: readStringField(run, "sourcePath"),
1308
+ status: readStringField(run, "status"),
1309
+ statusMessage: readNullableString$1(run, "statusMessage"),
1310
+ ...durationMs !== void 0 ? { durationMs } : {},
1311
+ ...entries.length > 0 ? { entries } : {}
1312
+ }
1313
+ });
1314
+ }
1315
+ emitStandardItemEvent(params) {
1316
+ const { item } = params;
1317
+ if (!item) return;
1318
+ const kind = itemKind(item);
1319
+ if (!kind) return;
1320
+ const name = itemName(item);
1321
+ const commandBearing = isCommandBearingToolItem(item, itemToolArgs(item));
1322
+ const meta = itemMeta(item, this.toolProgress.toolProgressDetailMode());
1323
+ const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
1324
+ this.emitAgentEvent({
1325
+ stream: "item",
1326
+ data: {
1327
+ itemId: item.id,
1328
+ phase: params.phase,
1329
+ kind,
1330
+ title: itemTitle(item),
1331
+ status: params.phase === "start" ? "running" : itemStatus(item),
1332
+ ...name ? { name } : {},
1333
+ ...meta ? { meta } : {},
1334
+ ...commandBearing ? { commandBearing: true } : {},
1335
+ ...suppressChannelProgress ? { suppressChannelProgress: true } : {}
1336
+ }
1337
+ });
1338
+ }
1339
+ async emitNormalizedToolItemEvent(params) {
1340
+ const projection = projectNormalizedToolItem({
1341
+ ...params,
1342
+ detailMode: this.toolProgress.toolProgressDetailMode()
1343
+ });
1344
+ if (!projection || !params.item) return;
1345
+ const { item } = params;
1346
+ const { name, status, args, meta, event } = projection;
1347
+ this.toolTranscript.recordTrajectoryEvent({
1348
+ phase: params.phase,
1349
+ item,
1350
+ name,
1351
+ args,
1352
+ status
1353
+ });
1354
+ if (params.phase === "result") this.toolProgress.recordNativeToolError({
1355
+ item,
1356
+ name,
1357
+ meta,
1358
+ status
1359
+ });
1360
+ if (!event) {
1361
+ if (params.phase === "result") {
1362
+ this.toolTranscript.emitAfterToolCallObservation(item);
1363
+ await this.onNativeToolResultRecorded?.();
1364
+ }
1365
+ return;
1366
+ }
1367
+ this.emitAgentEvent(event);
1368
+ if (params.phase === "result") {
1369
+ this.toolTranscript.emitAfterToolCallObservation(item);
1370
+ await this.onNativeToolResultRecorded?.();
1371
+ }
1372
+ }
1373
+ };
1374
+ //#endregion
1375
+ //#region extensions/codex/src/app-server/native-subagent-notification.ts
1376
+ /**
1377
+ * Extracts native Codex subagent completion notifications from trusted
1378
+ * inter-agent commentary messages emitted by the app-server.
1379
+ */
1380
+ const CODEX_SUBAGENT_NOTIFICATION_START = "<subagent_notification>";
1381
+ const CODEX_SUBAGENT_NOTIFICATION_END = "</subagent_notification>";
1382
+ /** Extracts trusted subagent completion payloads from a Codex server notification. */
1383
+ function extractCodexNativeSubagentCompletions(notification) {
1384
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
1385
+ if (!params) return [];
1386
+ const item = isJsonObject(params.item) ? params.item : void 0;
1387
+ if (!item) return [];
1388
+ const text = readTrustedInterAgentCommunicationContent(item);
1389
+ if (!text) return [];
1390
+ const author = readTrustedInterAgentCommunicationAuthor(item);
1391
+ return extractCodexNativeSubagentCompletionsFromText(text).filter((completion) => completion.agentPath === author);
1392
+ }
1393
+ /** Parses one or more tagged subagent completion payloads from commentary text. */
1394
+ function extractCodexNativeSubagentCompletionsFromText(text) {
1395
+ const completions = [];
1396
+ let cursor = 0;
1397
+ while (cursor < text.length) {
1398
+ const start = text.indexOf(CODEX_SUBAGENT_NOTIFICATION_START, cursor);
1399
+ if (start < 0) break;
1400
+ const bodyStart = start + 23;
1401
+ const end = text.indexOf(CODEX_SUBAGENT_NOTIFICATION_END, bodyStart);
1402
+ if (end < 0) break;
1403
+ const parsed = parseCodexNativeSubagentNotificationBody(text.slice(bodyStart, end));
1404
+ if (parsed) completions.push(parsed);
1405
+ cursor = end + 24;
1406
+ }
1407
+ return completions;
1408
+ }
1409
+ const codexNativeSubagentNotifications = {
1410
+ fromNotification: extractCodexNativeSubagentCompletions,
1411
+ fromText: extractCodexNativeSubagentCompletionsFromText
1412
+ };
1413
+ function parseCodexNativeSubagentNotificationBody(body) {
1414
+ let payload;
1415
+ try {
1416
+ payload = JSON.parse(body.trim());
1417
+ } catch {
1418
+ return;
1419
+ }
1420
+ if (!isJsonObject(payload)) return;
1421
+ const agentPath = readStringField(payload, "agent_path")?.trim();
1422
+ const status = isJsonObject(payload.status) ? payload.status : void 0;
1423
+ if (!agentPath || !status) return;
1424
+ const statusEntry = readCompletionStatus(status);
1425
+ if (!statusEntry) return;
1426
+ return {
1427
+ agentPath,
1428
+ status: statusEntry.status,
1429
+ statusLabel: statusEntry.label,
1430
+ result: statusEntry.result
1431
+ };
1432
+ }
1433
+ function readCompletionStatus(status) {
1434
+ for (const [rawKey, value] of Object.entries(status)) {
1435
+ const mappedStatus = mapCompletionStatus(normalizeStatusKey(rawKey));
1436
+ if (!mappedStatus) continue;
1437
+ const result = stringifyResult(value, mappedStatus);
1438
+ return {
1439
+ status: mappedStatus,
1440
+ label: mappedStatus === "succeeded" && result.kind === "no_final_assistant_message" ? "completed_without_final_message" : rawKey,
1441
+ result: result.text
1442
+ };
1443
+ }
1444
+ }
1445
+ function mapCompletionStatus(value) {
1446
+ if (value === "completed" || value === "succeeded" || value === "success") return "succeeded";
1447
+ if (value === "cancelled" || value === "canceled" || value === "interrupted" || value === "shutdown") return "cancelled";
1448
+ if (value === "failed" || value === "error" || value === "errored" || value === "systemerror" || value === "notfound") return "failed";
1449
+ }
1450
+ function stringifyResult(value, status) {
1451
+ if (typeof value === "string") {
1452
+ const text = value.trim();
1453
+ if (text) return { text };
1454
+ return status === "succeeded" ? completedWithoutFinalAssistantMessage() : { text: "(no output)" };
1455
+ }
1456
+ if (value === null || value === void 0) return status === "succeeded" ? completedWithoutFinalAssistantMessage() : { text: "(no output)" };
1457
+ try {
1458
+ return { text: JSON.stringify(value) };
1459
+ } catch {
1460
+ return { text: "(unserializable output)" };
1461
+ }
1462
+ }
1463
+ function completedWithoutFinalAssistantMessage() {
1464
+ return {
1465
+ text: "Codex native subagent completed without a final assistant message.",
1466
+ kind: "no_final_assistant_message"
1467
+ };
1468
+ }
1469
+ function readTrustedInterAgentCommunicationContent(item) {
1470
+ const communication = readTrustedInterAgentCommunication(item);
1471
+ return typeof communication?.content === "string" ? communication.content : void 0;
1472
+ }
1473
+ function readTrustedInterAgentCommunicationAuthor(item) {
1474
+ const communication = readTrustedInterAgentCommunication(item);
1475
+ return typeof communication?.author === "string" ? communication.author : void 0;
1476
+ }
1477
+ function readTrustedInterAgentCommunication(item) {
1478
+ if (readStringField(item, "type") !== "message" || readStringField(item, "role") !== "assistant" || readStringField(item, "phase") !== "commentary") return;
1479
+ const text = extractSingleTextPart(item);
1480
+ if (!text) return;
1481
+ let parsed;
1482
+ try {
1483
+ parsed = JSON.parse(text);
1484
+ } catch {
1485
+ return;
1486
+ }
1487
+ if (!isJsonObject(parsed)) return;
1488
+ if (typeof parsed.author !== "string" || typeof parsed.recipient !== "string" || typeof parsed.content !== "string" || parsed.trigger_turn !== false) return;
1489
+ return parsed;
1490
+ }
1491
+ function extractSingleTextPart(item) {
1492
+ const content = item.content;
1493
+ if (!Array.isArray(content) || content.length !== 1) return;
1494
+ const [entry] = content;
1495
+ if (!isJsonObject(entry)) return;
1496
+ const type = readStringField(entry, "type");
1497
+ if (type !== "output_text" && type !== "text") return;
1498
+ return readStringField(entry, "text")?.trim();
1499
+ }
1500
+ function normalizeStatusKey(value) {
1501
+ return value.replace(/[^a-z0-9]/giu, "").toLowerCase();
1502
+ }
1503
+ //#endregion
1504
+ //#region extensions/codex/src/app-server/native-subagent-task-ids.ts
1505
+ /**
1506
+ * Shared identifiers for representing Codex native subagents as OpenClaw task
1507
+ * runtime rows.
1508
+ */
1509
+ /** Task runtime namespace for Codex native subagent task rows. */
1510
+ const CODEX_NATIVE_SUBAGENT_RUNTIME = "subagent";
1511
+ /** Task kind used to distinguish native Codex subagents from other subagent runtimes. */
1512
+ const CODEX_NATIVE_SUBAGENT_TASK_KIND = "codex-native";
1513
+ /** Run id prefix for task rows keyed by Codex child thread ids. */
1514
+ const CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX = "codex-thread:";
1515
+ //#endregion
1516
+ //#region extensions/codex/src/app-server/native-subagent-task-mirror.ts
1517
+ /** Projects Codex thread and collab-agent notifications into task lifecycle updates. */
1518
+ var CodexNativeSubagentTaskMirror = class {
1519
+ constructor(params, runtime) {
1520
+ this.params = params;
1521
+ this.runtime = runtime;
1522
+ this.mirrorStateByThreadId = /* @__PURE__ */ new Map();
1523
+ this.terminalRunIds = /* @__PURE__ */ new Set();
1524
+ this.authoritativeRunIds = /* @__PURE__ */ new Set();
1525
+ this.expectedAuthoritativeRunIds = /* @__PURE__ */ new Set();
1526
+ this.now = params.now ?? Date.now;
1527
+ }
1528
+ markAuthoritativeCompletion(childThreadId) {
1529
+ const runId = codexNativeSubagentRunId(childThreadId);
1530
+ this.authoritativeRunIds.add(runId);
1531
+ this.terminalRunIds.add(runId);
1532
+ }
1533
+ markAuthoritativeCompletionExpected(childThreadId) {
1534
+ this.expectedAuthoritativeRunIds.add(codexNativeSubagentRunId(childThreadId));
1535
+ }
1536
+ handleNotification(notification) {
1537
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
1538
+ if (!params) return;
1539
+ if (notification.method === "thread/started") {
1540
+ this.handleThreadStarted(params);
1541
+ return;
1542
+ }
1543
+ if (notification.method === "thread/status/changed") {
1544
+ this.handleThreadStatusChanged(params);
1545
+ return;
1546
+ }
1547
+ if (notification.method === "item/started" || notification.method === "item/completed") {
1548
+ const item = isJsonObject(params.item) ? params.item : void 0;
1549
+ if (notification.method === "item/completed" && item && readStringField(item, "type") === "subAgentActivity") {
1550
+ this.handleSubagentActivityItem(params);
1551
+ return;
1552
+ }
1553
+ this.handleCollabAgentItem(params);
1554
+ }
1555
+ }
1556
+ handleThreadStarted(params) {
1557
+ const notification = readThreadStartedNotification(params);
1558
+ if (!notification) return;
1559
+ const thread = notification.thread;
1560
+ const spawn = readSubagentThreadSpawnSource(thread.source, this.params.parentThreadId);
1561
+ if (!spawn) return;
1562
+ const threadId = thread.id.trim();
1563
+ const label = normalizeOptionalString(spawn.agent_nickname) ?? normalizeOptionalString(thread.agentNickname) ?? normalizeOptionalString(spawn.agent_role) ?? normalizeOptionalString(thread.agentRole) ?? "Codex subagent";
1564
+ const task = normalizeOptionalString(thread.preview) ?? `Codex native subagent${label === "Codex subagent" ? "" : ` ${label}`}`;
1565
+ const createdAt = secondsToMillis(thread.createdAt) ?? this.now();
1566
+ if (!this.createRunningTask({
1567
+ threadId,
1568
+ label,
1569
+ task,
1570
+ startedAt: createdAt,
1571
+ progressSummary: "Codex native subagent started."
1572
+ })) return;
1573
+ this.applyStatus(threadId, thread.status);
1574
+ }
1575
+ handleThreadStatusChanged(params) {
1576
+ const notification = readThreadStatusChangedNotification(params);
1577
+ if (!notification) return;
1578
+ this.applyStatus(notification.threadId, notification.status);
1579
+ }
1580
+ applyStatus(threadId, status) {
1581
+ if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
1582
+ const statusType = status?.type;
1583
+ if (!statusType) return;
1584
+ const runId = codexNativeSubagentRunId(threadId);
1585
+ if (this.authoritativeRunIds.has(runId)) return;
1586
+ if (this.terminalRunIds.has(runId) && statusType !== "systemError") return;
1587
+ const eventAt = this.now();
1588
+ if (statusType === "active") {
1589
+ this.runtime.recordTaskRunProgressByRunId({
1590
+ runId,
1591
+ lastEventAt: eventAt,
1592
+ progressSummary: "Codex native subagent is active."
1593
+ });
1594
+ return;
1595
+ }
1596
+ if (statusType === "idle") {
1597
+ this.runtime.recordTaskRunProgressByRunId({
1598
+ runId,
1599
+ lastEventAt: eventAt,
1600
+ progressSummary: "Codex native subagent is idle."
1601
+ });
1602
+ return;
1603
+ }
1604
+ if (statusType === "systemError") {
1605
+ if (this.expectedAuthoritativeRunIds.has(runId)) {
1606
+ this.terminalRunIds.delete(runId);
1607
+ this.runtime.recordTaskRunProgressByRunId({
1608
+ runId,
1609
+ lastEventAt: eventAt,
1610
+ progressSummary: "Codex native subagent hit a system error; awaiting recovery."
1611
+ });
1612
+ return;
1613
+ }
1614
+ this.terminalRunIds.add(runId);
1615
+ this.runtime.finalizeTaskRunByRunId({
1616
+ runId,
1617
+ status: "failed",
1618
+ endedAt: eventAt,
1619
+ lastEventAt: eventAt,
1620
+ error: "Codex app-server reported a system error for the native subagent thread.",
1621
+ progressSummary: "Codex native subagent hit a system error.",
1622
+ terminalSummary: "Codex native subagent failed."
1623
+ });
1624
+ return;
1625
+ }
1626
+ if (statusType === "notLoaded") this.runtime.recordTaskRunProgressByRunId({
1627
+ runId,
1628
+ lastEventAt: eventAt,
1629
+ progressSummary: "Codex native subagent is not loaded."
1630
+ });
1631
+ }
1632
+ handleCollabAgentItem(params) {
1633
+ const item = isJsonObject(params.item) ? params.item : void 0;
1634
+ if (!item || readStringField(item, "type") !== "collabAgentToolCall") return;
1635
+ if ((readStringField(item, "senderThreadId") ?? readStringField(params, "threadId")) !== this.params.parentThreadId) return;
1636
+ const isSpawnAgentTool = normalizeToolName(readStringField(item, "tool")) === "spawnagent";
1637
+ const receiverThreadIds = readStringArray$1(item.receiverThreadIds);
1638
+ const agentsStates = readAgentsStates(item.agentsStates);
1639
+ const spawnChildThreadIds = /* @__PURE__ */ new Set([...receiverThreadIds, ...agentsStates.keys()]);
1640
+ if (isSpawnAgentTool) for (const childThreadId of spawnChildThreadIds) this.createTaskFromCollabSpawnItem(childThreadId, item);
1641
+ const toolCallStatus = normalizeCollabToolCallStatus(readStringField(item, "status"));
1642
+ const terminalToolCallThreadIds = /* @__PURE__ */ new Set();
1643
+ if (isSpawnAgentTool && isBlockedOrFailedCollabToolCallStatus(toolCallStatus)) {
1644
+ for (const threadId of spawnChildThreadIds) terminalToolCallThreadIds.add(threadId);
1645
+ for (const threadId of agentsStates.keys()) terminalToolCallThreadIds.add(threadId);
1646
+ }
1647
+ const terminalAgentStateThreadIds = /* @__PURE__ */ new Set();
1648
+ for (const [threadId, state] of agentsStates) {
1649
+ const normalizedStatus = normalizeAgentStateStatus(state.status);
1650
+ if (terminalToolCallThreadIds.has(threadId) && isNonTerminalAgentStateStatus(normalizedStatus)) continue;
1651
+ this.applyCollabAgentStatus(threadId, normalizedStatus, state.message);
1652
+ if (isTerminalAgentStateStatus(normalizedStatus)) terminalAgentStateThreadIds.add(threadId);
1653
+ }
1654
+ if (isBlockedOrFailedCollabToolCallStatus(toolCallStatus)) for (const threadId of terminalToolCallThreadIds) {
1655
+ if (terminalAgentStateThreadIds.has(threadId)) continue;
1656
+ const state = agentsStates.get(threadId);
1657
+ this.applyCollabAgentStatus(threadId, toolCallStatus, state?.message);
1658
+ }
1659
+ }
1660
+ handleSubagentActivityItem(params) {
1661
+ const item = isJsonObject(params.item) ? params.item : void 0;
1662
+ if (!item || readStringField(item, "type") !== "subAgentActivity" || readStringField(params, "threadId") !== this.params.parentThreadId) return;
1663
+ const threadId = normalizeOptionalString(readStringField(item, "agentThreadId"));
1664
+ const kind = normalizeSubagentActivityKind(readStringField(item, "kind"));
1665
+ if (!threadId || !kind) return;
1666
+ if (kind === "started") {
1667
+ this.createTaskFromSubagentActivity(threadId, normalizeOptionalString(readStringField(item, "agentPath")));
1668
+ return;
1669
+ }
1670
+ if (this.mirrorStateByThreadId.get(threadId) !== "mirrored") return;
1671
+ const message = kind === "interacted" ? "Codex native subagent received more input." : "Codex native subagent was interrupted.";
1672
+ this.applyCollabAgentStatus(threadId, kind === "interacted" ? "running" : "interrupted", message);
1673
+ }
1674
+ createTaskFromSubagentActivity(threadId, agentPath) {
1675
+ const eventAt = this.now();
1676
+ this.createRunningTask({
1677
+ threadId,
1678
+ label: "Codex subagent",
1679
+ task: agentPath ? `Codex native subagent ${agentPath}` : "Codex native subagent",
1680
+ startedAt: eventAt,
1681
+ progressSummary: "Codex native subagent started."
1682
+ });
1683
+ }
1684
+ createTaskFromCollabSpawnItem(threadId, item) {
1685
+ const prompt = normalizeOptionalString(readStringField(item, "prompt"));
1686
+ const createdAt = this.now();
1687
+ this.createRunningTask({
1688
+ threadId,
1689
+ label: "Codex subagent",
1690
+ task: prompt ?? "Codex native subagent",
1691
+ startedAt: createdAt,
1692
+ progressSummary: "Codex native subagent spawned."
1693
+ });
1694
+ }
1695
+ createRunningTask(params) {
1696
+ const threadId = params.threadId.trim();
1697
+ if (!threadId || this.mirrorStateByThreadId.get(threadId) === "mirrored") return false;
1698
+ this.mirrorStateByThreadId.set(threadId, "mirrored");
1699
+ const runId = codexNativeSubagentRunId(threadId);
1700
+ if (!this.runtime.tryCreateRunningTaskRun({
1701
+ sourceId: runId,
1702
+ agentId: this.params.agentId,
1703
+ runId,
1704
+ label: params.label,
1705
+ task: params.task,
1706
+ notifyPolicy: "silent",
1707
+ deliveryStatus: "not_applicable",
1708
+ preferMetadata: true,
1709
+ startedAt: params.startedAt,
1710
+ lastEventAt: this.now(),
1711
+ progressSummary: params.progressSummary
1712
+ })) {
1713
+ this.mirrorStateByThreadId.set(threadId, "failed");
1714
+ return false;
1715
+ }
1716
+ this.terminalRunIds.delete(runId);
1717
+ this.authoritativeRunIds.delete(runId);
1718
+ return true;
1719
+ }
1720
+ applyCollabAgentStatus(threadId, status, message) {
1721
+ if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
1722
+ const normalizedStatus = normalizeAgentStateStatus(status);
1723
+ if (!normalizedStatus) return;
1724
+ const runId = codexNativeSubagentRunId(threadId);
1725
+ if (this.authoritativeRunIds.has(runId)) return;
1726
+ if (this.terminalRunIds.has(runId) && isNonTerminalAgentStateStatus(normalizedStatus)) return;
1727
+ const eventAt = this.now();
1728
+ if (isNonTerminalAgentStateStatus(normalizedStatus)) {
1729
+ this.runtime.recordTaskRunProgressByRunId({
1730
+ runId,
1731
+ lastEventAt: eventAt,
1732
+ progressSummary: normalizeOptionalString(message) ?? (normalizedStatus === "pendingInit" ? "Codex native subagent is initializing." : normalizedStatus === "interrupted" ? "Codex native subagent was interrupted." : "Codex native subagent is running.")
1733
+ });
1734
+ return;
1735
+ }
1736
+ if (normalizedStatus === "completed") {
1737
+ this.terminalRunIds.add(runId);
1738
+ const summary = normalizeOptionalString(message) ?? "Codex native subagent completed.";
1739
+ if (this.expectedAuthoritativeRunIds.has(runId)) this.runtime.recordTaskRunProgressByRunId({
1740
+ runId,
1741
+ lastEventAt: eventAt,
1742
+ progressSummary: summary
1743
+ });
1744
+ else this.runtime.finalizeTaskRunByRunId({
1745
+ runId,
1746
+ status: "succeeded",
1747
+ endedAt: eventAt,
1748
+ lastEventAt: eventAt,
1749
+ progressSummary: summary,
1750
+ terminalSummary: summary
1751
+ });
1752
+ return;
1753
+ }
1754
+ if (normalizedStatus === "blocked") {
1755
+ this.terminalRunIds.add(runId);
1756
+ this.runtime.finalizeTaskRunByRunId({
1757
+ runId,
1758
+ status: "succeeded",
1759
+ endedAt: eventAt,
1760
+ lastEventAt: eventAt,
1761
+ progressSummary: normalizeOptionalString(message) ?? "Codex native subagent blocked.",
1762
+ terminalSummary: normalizeOptionalString(message) ?? "Codex native subagent blocked.",
1763
+ terminalOutcome: "blocked"
1764
+ });
1765
+ return;
1766
+ }
1767
+ this.terminalRunIds.add(runId);
1768
+ this.runtime.finalizeTaskRunByRunId({
1769
+ runId,
1770
+ status: normalizedStatus === "shutdown" ? "cancelled" : "failed",
1771
+ endedAt: eventAt,
1772
+ lastEventAt: eventAt,
1773
+ error: normalizeOptionalString(message) ?? `Codex native subagent status: ${normalizedStatus}`,
1774
+ progressSummary: normalizeOptionalString(message) ?? `Codex native subagent ${normalizedStatus}.`,
1775
+ terminalSummary: normalizeOptionalString(message) ?? "Codex native subagent did not complete."
1776
+ });
1777
+ }
1778
+ };
1779
+ /** Converts a Codex child thread id into the OpenClaw task-runtime run id. */
1780
+ function codexNativeSubagentRunId(threadId) {
1781
+ return `${CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX}${threadId.trim()}`;
1782
+ }
1783
+ /** Reads a subagent thread-spawn source only when it belongs to the expected parent thread. */
1784
+ function readSubagentThreadSpawnSource(source, parentThreadId) {
1785
+ if (!source || typeof source !== "object" || !("subAgent" in source)) return;
1786
+ const subAgent = source.subAgent;
1787
+ if (!subAgent || typeof subAgent !== "object" || !("thread_spawn" in subAgent)) return;
1788
+ const spawn = subAgent.thread_spawn;
1789
+ if (!spawn || typeof spawn !== "object") return;
1790
+ return spawn.parent_thread_id === parentThreadId ? spawn : void 0;
1791
+ }
1792
+ function readThreadStartedNotification(params) {
1793
+ const thread = params.thread;
1794
+ if (!isJsonObject(thread) || typeof thread.id !== "string") return;
1795
+ return { thread };
1796
+ }
1797
+ function readThreadStatusChangedNotification(params) {
1798
+ if (typeof params.threadId !== "string") return;
1799
+ const status = params.status;
1800
+ if (!isJsonObject(status) || !isCodexThreadStatusType(status.type)) return;
1801
+ return {
1802
+ threadId: params.threadId,
1803
+ status
1804
+ };
1805
+ }
1806
+ function isCodexThreadStatusType(value) {
1807
+ return value === "notLoaded" || value === "idle" || value === "systemError" || value === "active";
1808
+ }
1809
+ function readAgentsStates(value) {
1810
+ const states = /* @__PURE__ */ new Map();
1811
+ if (!isJsonObject(value)) return states;
1812
+ for (const [threadId, rawState] of Object.entries(value)) {
1813
+ if (!isJsonObject(rawState)) continue;
1814
+ const status = readStringField(rawState, "status");
1815
+ const message = readNullableString(rawState, "message");
1816
+ states.set(threadId, {
1817
+ status,
1818
+ message
1819
+ });
1820
+ }
1821
+ return states;
1822
+ }
1823
+ function readStringArray$1(value) {
1824
+ if (!Array.isArray(value)) return [];
1825
+ return value.filter((entry) => typeof entry === "string" && entry.trim() !== "");
1826
+ }
1827
+ function readNullableString(value, key) {
1828
+ const entry = value[key];
1829
+ return typeof entry === "string" || entry === null ? entry : void 0;
1830
+ }
1831
+ function normalizeToolName(value) {
1832
+ return value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
1833
+ }
1834
+ function normalizeSubagentActivityKind(value) {
1835
+ const key = value?.replace(/[^a-z]/giu, "").toLowerCase();
1836
+ return key === "started" || key === "interacted" || key === "interrupted" ? key : void 0;
1837
+ }
1838
+ function normalizeCollabToolCallStatus(value) {
1839
+ const key = value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
1840
+ if (key === "completed" || key === "succeeded" || key === "success") return "completed";
1841
+ if (key === "failed" || key === "error" || key === "errored") return "failed";
1842
+ if (key === "blocked" || key === "declined") return "blocked";
1843
+ if (key === "inprogress" || key === "running") return "running";
1844
+ return value?.trim();
1845
+ }
1846
+ function isBlockedOrFailedCollabToolCallStatus(value) {
1847
+ return value === "failed" || value === "blocked";
1848
+ }
1849
+ function isNonTerminalAgentStateStatus(value) {
1850
+ return value === "pendingInit" || value === "running" || value === "interrupted";
1851
+ }
1852
+ function isTerminalAgentStateStatus(value) {
1853
+ return value !== void 0 && !isNonTerminalAgentStateStatus(value);
1854
+ }
1855
+ function normalizeAgentStateStatus(value) {
1856
+ const key = value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
1857
+ if (!key) return;
1858
+ if (key === "pendinginit") return "pendingInit";
1859
+ if (key === "inprogress" || key === "running") return "running";
1860
+ if (key === "completed" || key === "succeeded" || key === "success") return "completed";
1861
+ if (key === "interrupted" || key === "cancelled" || key === "canceled" || key === "shutdown") return key === "shutdown" ? "shutdown" : "interrupted";
1862
+ if (key === "failed" || key === "error" || key === "systemerror") return "failed";
1863
+ if (key === "blocked" || key === "declined") return "blocked";
1864
+ return value?.trim();
1865
+ }
1866
+ function secondsToMillis(value) {
1867
+ if (typeof value !== "number" || !Number.isFinite(value)) return;
1868
+ return value * 1e3;
1869
+ }
1870
+ //#endregion
1871
+ //#region extensions/codex/src/app-server/native-subagent-monitor.ts
1872
+ /**
1873
+ * Mirrors Codex native subagent lifecycle and completion into OpenClaw task
1874
+ * runtime records, with app-server history as the recovery source.
1875
+ */
1876
+ const DEFAULT_RECOVERY_POLL_DELAYS_MS = [
1877
+ 2e3,
1878
+ 5e3,
1879
+ 1e4,
1880
+ 15e3,
1881
+ 3e4,
1882
+ 6e4,
1883
+ 12e4,
1884
+ 3e5
1885
+ ];
1886
+ const DEFAULT_COMPLETION_DELIVERY_RETRY_DELAYS_MS = [
1887
+ 5e3,
1888
+ 15e3,
1889
+ 3e4,
1890
+ 6e4,
1891
+ 12e4,
1892
+ 3e5
1893
+ ];
1894
+ const RECENT_TERMINAL_TASK_RECONCILE_GRACE_MS = 6e4;
1895
+ const THREAD_READ_TIMEOUT_MS = 3e4;
1896
+ const NATIVE_SUBAGENT_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
1897
+ "thread/started",
1898
+ "thread/status/changed",
1899
+ "turn/started",
1900
+ "turn/completed",
1901
+ "item/agentMessage/delta",
1902
+ "item/reasoning/summaryTextDelta",
1903
+ "item/started",
1904
+ "item/completed",
1905
+ "rawResponseItem/completed"
1906
+ ]);
1907
+ const RECOVERY_REVISION_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
1908
+ "thread/started",
1909
+ "thread/status/changed",
1910
+ "turn/started",
1911
+ "turn/completed"
1912
+ ]);
1913
+ const MAX_PENDING_DIRECT_SPAWN_EVIDENCE = 32;
1914
+ const defaultRuntime = {
1915
+ createAgentHarnessTaskRuntime,
1916
+ deliverAgentHarnessTaskCompletion
1917
+ };
1918
+ const monitors = /* @__PURE__ */ new WeakMap();
1919
+ const completionDeliveryOwners = /* @__PURE__ */ new Map();
1920
+ function registerMonitor(params) {
1921
+ let monitor = monitors.get(params.client);
1922
+ if (!monitor) {
1923
+ const childThreadOwnership = /* @__PURE__ */ new Map();
1924
+ const childThreadTransitions = new KeyedAsyncQueue();
1925
+ monitor = new Monitor(params.client, params.runtime ?? defaultRuntime, {
1926
+ retainClient: params.retainClient,
1927
+ retainParentThread: params.retainParentThread,
1928
+ claimChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
1929
+ const ownership = await claimCodexAppServerLiveThread(params.client, threadId);
1930
+ if (ownership) childThreadOwnership.set(threadId, ownership);
1931
+ return ownership;
1932
+ }),
1933
+ retainChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
1934
+ const ownership = childThreadOwnership.get(threadId);
1935
+ let retained = false;
1936
+ try {
1937
+ retained = await retainCodexAppServerLiveThread(params.client, threadId, ownership?.release);
1938
+ return retained;
1939
+ } finally {
1940
+ if (!retained && ownership) await ownership.release(threadId);
1941
+ if (childThreadOwnership.get(threadId) === ownership) childThreadOwnership.delete(threadId);
1942
+ }
1943
+ }),
1944
+ releaseChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
1945
+ const ownership = childThreadOwnership.get(threadId);
1946
+ if (ownership) {
1947
+ await ownership.release(threadId);
1948
+ if (childThreadOwnership.get(threadId) === ownership) childThreadOwnership.delete(threadId);
1949
+ } else await releaseCodexAppServerLiveThread(params.client, threadId);
1950
+ })
1951
+ });
1952
+ monitors.set(params.client, monitor);
1953
+ }
1954
+ return monitor.registerParent({
1955
+ parentThreadId: params.parentThreadId,
1956
+ requesterSessionKey: params.requesterSessionKey,
1957
+ taskRuntimeScope: params.taskRuntimeScope,
1958
+ agentId: params.agentId,
1959
+ claimDirectChild: params.claimDirectChild,
1960
+ rejectPendingDirectChild: params.rejectPendingDirectChild
1961
+ });
1962
+ }
1963
+ var Monitor = class {
1964
+ constructor(client, runtime = defaultRuntime, options = {}) {
1965
+ this.client = client;
1966
+ this.runtime = runtime;
1967
+ this.parentStates = /* @__PURE__ */ new Map();
1968
+ this.pendingDirectSpawnEvidence = /* @__PURE__ */ new Map();
1969
+ this.retiredParentStates = /* @__PURE__ */ new WeakSet();
1970
+ this.childStates = /* @__PURE__ */ new Map();
1971
+ this.childThreadIdsByAgentPath = /* @__PURE__ */ new Map();
1972
+ this.taskReconciliations = /* @__PURE__ */ new Map();
1973
+ this.taskReconciliationTimers = /* @__PURE__ */ new Map();
1974
+ this.threadStatusRevisions = /* @__PURE__ */ new Map();
1975
+ this.parentThreadRetentions = /* @__PURE__ */ new Map();
1976
+ this.disposed = false;
1977
+ this.recoveryPollDelaysMs = options.recoveryPollDelaysMs ?? DEFAULT_RECOVERY_POLL_DELAYS_MS;
1978
+ this.completionDeliveryRetryDelaysMs = options.completionDeliveryRetryDelaysMs ?? DEFAULT_COMPLETION_DELIVERY_RETRY_DELAYS_MS;
1979
+ this.completionDeliveryMaxRetries = options.completionDeliveryMaxRetries ?? this.completionDeliveryRetryDelaysMs.length;
1980
+ this.now = options.now ?? Date.now;
1981
+ this.retainClient = options.retainClient;
1982
+ this.retainParentThread = options.retainParentThread;
1983
+ this.claimChildThread = options.claimChildThread;
1984
+ this.retainChildThread = options.retainChildThread;
1985
+ this.releaseChildThread = options.releaseChildThread;
1986
+ this.removeNotificationHandler = client.addNotificationHandler(async (notification) => {
1987
+ if (!NATIVE_SUBAGENT_NOTIFICATION_METHODS.has(notification.method)) return;
1988
+ await this.handleNotification(notification);
1989
+ });
1990
+ this.removeCloseHandler = client.addCloseHandler(() => this.dispose());
1991
+ }
1992
+ dispose() {
1993
+ if (this.disposed) return;
1994
+ this.disposed = true;
1995
+ this.removeNotificationHandler();
1996
+ this.removeCloseHandler();
1997
+ for (const timer of this.taskReconciliationTimers.values()) clearTimeout(timer);
1998
+ this.taskReconciliationTimers.clear();
1999
+ for (const childState of this.childStates.values()) {
2000
+ this.releaseDirectChild(childState);
2001
+ if (childState.terminal && childState.pendingCompletion) {
2002
+ this.clearRecoveryTimers(childState);
2003
+ continue;
2004
+ }
2005
+ this.unregisterChild(childState);
2006
+ }
2007
+ this.releaseRetainedClient();
2008
+ for (const release of this.parentThreadRetentions.values()) release();
2009
+ this.parentThreadRetentions.clear();
2010
+ for (const state of this.parentStates.values()) state.owners.clear();
2011
+ this.pendingDirectSpawnEvidence.clear();
2012
+ for (const [parentThreadId] of this.parentStates) if (![...this.childStates.values()].some((childState) => childState.parentThreadId === parentThreadId)) this.parentStates.delete(parentThreadId);
2013
+ }
2014
+ registerParent(params) {
2015
+ const parentThreadId = params.parentThreadId.trim();
2016
+ if (!parentThreadId) throw new Error("Codex native subagent monitor requires a parent thread id");
2017
+ if (this.disposed) throw new Error("Codex native subagent monitor is closed");
2018
+ let state = this.parentStates.get(parentThreadId);
2019
+ if (state?.requesterSessionKey && params.requesterSessionKey && state.requesterSessionKey !== params.requesterSessionKey) throw new Error(`Codex thread ${parentThreadId} is already bound to another session`);
2020
+ if (!state) {
2021
+ state = {
2022
+ parentThreadId,
2023
+ owners: /* @__PURE__ */ new Map()
2024
+ };
2025
+ this.parentStates.set(parentThreadId, state);
2026
+ }
2027
+ state.requesterSessionKey ??= params.requesterSessionKey;
2028
+ state.taskRuntimeScope ??= params.taskRuntimeScope;
2029
+ state.agentId ??= params.agentId;
2030
+ const owner = Symbol("codex-native-subagent-owner");
2031
+ state.owners.set(owner, {
2032
+ claimDirectChild: params.claimDirectChild,
2033
+ rejectPendingDirectChild: params.rejectPendingDirectChild
2034
+ });
2035
+ this.prepareParentTaskRuntime(state);
2036
+ for (const childState of this.childStates.values()) if (childState.parentThreadId === parentThreadId && childState.pendingCompletion) this.deliverPendingCompletion(state, childState);
2037
+ let registered = true;
2038
+ const registeredState = state;
2039
+ this.reconcileTaskRowsForParent(registeredState).catch((error) => {
2040
+ embeddedAgentLog.warn("Failed to reconcile Codex native subagent task rows", {
2041
+ parentThreadId,
2042
+ error: formatErrorMessage(error)
2043
+ });
2044
+ });
2045
+ return {
2046
+ bindTurn: (turnIdInput) => {
2047
+ const turnId = turnIdInput.trim();
2048
+ if (!turnId || this.parentStates.get(parentThreadId) !== registeredState) return;
2049
+ const current = registeredState.owners.get(owner);
2050
+ if (!current || [...registeredState.owners.values()].some((other) => other !== current && other.turnId === turnId)) return;
2051
+ current.turnId = turnId;
2052
+ this.drainPendingDirectSpawnEvidence(registeredState, current, turnId);
2053
+ this.clearUnconsumablePendingDirectSpawnEvidence();
2054
+ },
2055
+ unregister: () => {
2056
+ if (!registered) return;
2057
+ registered = false;
2058
+ const current = this.parentStates.get(parentThreadId);
2059
+ if (current === registeredState) {
2060
+ current.owners.delete(owner);
2061
+ this.clearUnconsumablePendingDirectSpawnEvidence();
2062
+ this.pruneParentIfUnused(current);
2063
+ }
2064
+ }
2065
+ };
2066
+ }
2067
+ retireParent(parentThreadIdInput) {
2068
+ const parentThreadId = parentThreadIdInput.trim();
2069
+ const state = this.parentStates.get(parentThreadId);
2070
+ if (!state) return;
2071
+ this.retiredParentStates.add(state);
2072
+ state.owners.clear();
2073
+ this.clearPendingDirectSpawnEvidenceForParent(parentThreadId);
2074
+ for (const childState of Array.from(this.childStates.values())) if (childState.parentThreadId === parentThreadId) this.retireChild(state, childState, "Codex native subagent parent session ended.");
2075
+ if (this.parentStates.get(parentThreadId) === state) this.parentStates.delete(parentThreadId);
2076
+ }
2077
+ prepareParentTaskRuntime(state) {
2078
+ if (!state.requesterSessionKey || !state.taskRuntimeScope) return;
2079
+ state.taskRuntime ??= this.runtime.createAgentHarnessTaskRuntime({
2080
+ runtime: CODEX_NATIVE_SUBAGENT_RUNTIME,
2081
+ taskKind: CODEX_NATIVE_SUBAGENT_TASK_KIND,
2082
+ scope: state.taskRuntimeScope,
2083
+ runIdPrefix: CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX
2084
+ });
2085
+ state.mirror ??= new CodexNativeSubagentTaskMirror({
2086
+ parentThreadId: state.parentThreadId,
2087
+ requesterSessionKey: state.requesterSessionKey,
2088
+ agentId: state.agentId
2089
+ }, state.taskRuntime);
2090
+ }
2091
+ /** Handles one notification from the client-wide router observer. */
2092
+ async handleNotification(notification) {
2093
+ if (this.disposed) return;
2094
+ const state = this.resolveMirrorState(notification);
2095
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2096
+ const startedThread = isJsonObject(params?.thread) ? params.thread : void 0;
2097
+ const threadId = readStringField(params, "threadId")?.trim() ?? readStringField(startedThread, "id")?.trim();
2098
+ const threadStatus = isJsonObject(params?.status) ? normalizeIdentifier(readStringField(params.status, "type")) : void 0;
2099
+ const tracksRecoveryRevision = Boolean(threadId && this.threadStatusRevisions.has(threadId));
2100
+ if (RECOVERY_REVISION_NOTIFICATION_METHODS.has(notification.method) && threadId && tracksRecoveryRevision) this.threadStatusRevisions.get(threadId).value += 1;
2101
+ if (!state && (!threadId || !this.parentStates.has(threadId) && !this.childStates.has(threadId) && !tracksRecoveryRevision)) return;
2102
+ if (state?.mirror) try {
2103
+ state.mirror.handleNotification(notification);
2104
+ } catch (error) {
2105
+ embeddedAgentLog.warn("Failed to mirror Codex native subagent lifecycle event", {
2106
+ method: notification.method,
2107
+ error: formatErrorMessage(error)
2108
+ });
2109
+ }
2110
+ if (state) this.handleClosedChild(notification, state);
2111
+ const childState = threadId ? this.childStates.get(threadId) : void 0;
2112
+ if (notification.method === "turn/started" && childState) this.resumeChild(childState);
2113
+ if (childState && !childState.terminal) this.emitChildTaskActivity(notification, childState.childThreadId);
2114
+ this.captureChildAssistantMessage(notification);
2115
+ await this.handleChildTurnCompletion(notification);
2116
+ if (notification.method === "thread/status/changed" && threadId && threadStatus) if (threadStatus !== "systemerror") {
2117
+ if (childState) this.clearSystemErrorFallback(childState);
2118
+ } else {
2119
+ if (childState) {
2120
+ this.resumeChild(childState, { scheduleRecovery: false });
2121
+ this.setRecoveryFallback(childState, systemErrorFallbackCompletion(childState.childThreadId), this.now());
2122
+ }
2123
+ this.reconcileChildThread(threadId).catch((error) => {
2124
+ this.logRecoveryFailure(threadId, error);
2125
+ return false;
2126
+ }).then((reconciled) => {
2127
+ if (!reconciled && childState && this.childStates.get(threadId) === childState) this.scheduleRecoveryPoll(childState);
2128
+ });
2129
+ }
2130
+ await this.handleCompletionNotification(notification);
2131
+ }
2132
+ emitChildTaskActivity(notification, childThreadId) {
2133
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2134
+ if (!params) return;
2135
+ const runId = codexNativeSubagentRunId(childThreadId);
2136
+ if (notification.method === "item/agentMessage/delta") {
2137
+ const delta = readStringField(params, "delta");
2138
+ if (delta) emitAgentEvent({
2139
+ runId,
2140
+ stream: "assistant",
2141
+ data: { delta }
2142
+ });
2143
+ return;
2144
+ }
2145
+ if (notification.method === "item/reasoning/summaryTextDelta") {
2146
+ const delta = readStringField(params, "delta");
2147
+ if (delta) emitAgentEvent({
2148
+ runId,
2149
+ stream: "thinking",
2150
+ data: { delta }
2151
+ });
2152
+ return;
2153
+ }
2154
+ if (notification.method !== "item/started" && notification.method !== "item/completed") return;
2155
+ const item = readItem(params.item);
2156
+ if (item?.type === "agentMessage" && notification.method === "item/completed" && item.text) emitAgentEvent({
2157
+ runId,
2158
+ stream: "assistant",
2159
+ data: { text: item.text }
2160
+ });
2161
+ const projection = projectNormalizedToolItem({
2162
+ phase: notification.method === "item/started" ? "start" : "result",
2163
+ item
2164
+ });
2165
+ if (projection?.event) emitAgentEvent({
2166
+ runId,
2167
+ ...projection.event
2168
+ });
2169
+ }
2170
+ resumeChild(childState, options = {}) {
2171
+ if (childState.terminal) return;
2172
+ this.observeActiveChild(childState);
2173
+ this.clearRecoveryTimers(childState);
2174
+ childState.recoveryAttempt = 0;
2175
+ if (options.scheduleRecovery !== false) this.scheduleRecoveryPoll(childState);
2176
+ }
2177
+ observeActiveChild(childState) {
2178
+ childState.settledWithoutCompletion = false;
2179
+ childState.fallbackCompletion = void 0;
2180
+ this.releaseClientRetention ??= this.retainClient?.();
2181
+ }
2182
+ settleResumableChild(childState) {
2183
+ if (childState.terminal) return;
2184
+ childState.settledWithoutCompletion = true;
2185
+ childState.fallbackCompletion = void 0;
2186
+ this.releaseDirectChild(childState);
2187
+ this.clearRecoveryTimers(childState);
2188
+ this.releaseClientRetentionIfIdle();
2189
+ }
2190
+ captureChildAssistantMessage(notification) {
2191
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2192
+ const childThreadId = readStringField(params, "threadId")?.trim();
2193
+ const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
2194
+ if (!childState || childState.terminal) return;
2195
+ if (notification.method === "item/agentMessage/delta") {
2196
+ const turnId = readStringField(params, "turnId");
2197
+ const itemId = readStringField(params, "itemId");
2198
+ const delta = readStringField(params, "delta");
2199
+ if (turnId && itemId && delta) this.recordChildAssistantMessage(childState, turnId, itemId, delta);
2200
+ return;
2201
+ }
2202
+ if (notification.method !== "item/started" && notification.method !== "item/completed") return;
2203
+ this.captureChildAssistantMessageItem(childState, readStringField(params, "turnId"), isJsonObject(params?.item) ? params.item : void 0);
2204
+ }
2205
+ captureChildAssistantMessageItem(childState, turnId, item) {
2206
+ if (readStringField(item, "type") !== "agentMessage" || !turnId) return;
2207
+ const itemId = readStringField(item, "id");
2208
+ if (!itemId) return;
2209
+ const messages = this.getChildAssistantMessages(childState, turnId);
2210
+ if (readStringField(item, "phase") === "commentary") messages.commentaryIds.add(itemId);
2211
+ else messages.finalMessageIds.add(itemId);
2212
+ const text = readStringField(item, "text");
2213
+ if (text) this.recordChildAssistantMessage(childState, turnId, itemId, text, { replace: true });
2214
+ }
2215
+ captureChildTurnAssistantMessages(childState, turn) {
2216
+ const turnId = readStringField(turn, "id");
2217
+ if (!turnId || !Array.isArray(turn.items)) return;
2218
+ for (const item of turn.items) this.captureChildAssistantMessageItem(childState, turnId, isJsonObject(item) ? item : void 0);
2219
+ }
2220
+ recordChildAssistantMessage(childState, turnId, itemId, text, options = {}) {
2221
+ const messages = this.getChildAssistantMessages(childState, turnId);
2222
+ if (!messages.texts.has(itemId)) messages.order.push(itemId);
2223
+ const existing = messages.texts.get(itemId) ?? "";
2224
+ messages.texts.set(itemId, options.replace ? text : `${existing}${text}`);
2225
+ }
2226
+ getChildAssistantMessages(childState, turnId) {
2227
+ let messages = childState.assistantMessagesByTurn.get(turnId);
2228
+ if (!messages) {
2229
+ messages = {
2230
+ texts: /* @__PURE__ */ new Map(),
2231
+ order: [],
2232
+ commentaryIds: /* @__PURE__ */ new Set(),
2233
+ finalMessageIds: /* @__PURE__ */ new Set()
2234
+ };
2235
+ childState.assistantMessagesByTurn.set(turnId, messages);
2236
+ }
2237
+ return messages;
2238
+ }
2239
+ async handleChildTurnCompletion(notification) {
2240
+ if (notification.method !== "turn/completed") return;
2241
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2242
+ const childThreadId = readStringField(params, "threadId")?.trim();
2243
+ const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
2244
+ const state = childState ? this.parentStates.get(childState.parentThreadId) : void 0;
2245
+ const turn = isJsonObject(params?.turn) ? params.turn : void 0;
2246
+ if (!state || !childState || !turn || childState.terminal) return;
2247
+ const turnId = readStringField(turn, "id");
2248
+ const status = normalizeIdentifier(readStringField(turn, "status"));
2249
+ if (status === "interrupted") {
2250
+ this.removePendingDirectSpawnEvidenceForChild(childState.childThreadId);
2251
+ this.rejectPendingDirectChild(state, childState.childThreadId, "Codex child turn interrupted");
2252
+ if (turnId) childState.assistantMessagesByTurn.delete(turnId);
2253
+ this.settleResumableChild(childState);
2254
+ return;
2255
+ }
2256
+ if (status === "completed" || status === "failed") {
2257
+ const revision = this.threadStatusRevisions.get(childState.childThreadId);
2258
+ if (revision) revision.terminal = true;
2259
+ this.rejectPendingDirectChild(state, childState.childThreadId, "Codex child turn completed");
2260
+ this.releaseDirectChild(childState);
2261
+ this.removePendingDirectSpawnEvidenceForChild(childState.childThreadId);
2262
+ }
2263
+ this.captureChildTurnAssistantMessages(childState, turn);
2264
+ const completion = toChildTurnCompletion(childState, turn);
2265
+ if (!completion) return;
2266
+ await this.processObservedCompletion(state, childState, completion);
2267
+ }
2268
+ /** Reads one child through app-server history and delivers a terminal result when present. */
2269
+ async reconcileChildThread(childThreadIdInput) {
2270
+ const childState = this.childStates.get(childThreadIdInput.trim());
2271
+ if (!childState || childState.terminal || this.disposed) return false;
2272
+ if (childState.recoveryInFlight) return await childState.recoveryInFlight;
2273
+ const recovery = this.reconcileChildState(childState);
2274
+ childState.recoveryInFlight = recovery;
2275
+ try {
2276
+ return await recovery;
2277
+ } finally {
2278
+ if (childState.recoveryInFlight === recovery) childState.recoveryInFlight = void 0;
2279
+ }
2280
+ }
2281
+ resolveMirrorState(notification) {
2282
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2283
+ if (!params) return;
2284
+ if (notification.method === "thread/started") {
2285
+ const thread = isJsonObject(params.thread) ? params.thread : void 0;
2286
+ const parentThreadId = readThreadParentThreadId(thread);
2287
+ const childThreadId = thread ? readStringField(thread, "id")?.trim() : void 0;
2288
+ const agentPath = readStringField(readThreadSpawnSource(thread), "agent_path")?.trim();
2289
+ const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
2290
+ if (state && childThreadId && parentThreadId) return this.registerChildThread(parentThreadId, childThreadId, agentPath === void 0 ? {} : { agentPath }) ? state : void 0;
2291
+ return state;
2292
+ }
2293
+ if (notification.method === "thread/status/changed" || notification.method === "turn/started" || notification.method === "turn/completed" || notification.method === "item/agentMessage/delta") {
2294
+ const childThreadId = readStringField(params, "threadId")?.trim();
2295
+ const parentThreadId = childThreadId ? this.childStates.get(childThreadId)?.parentThreadId : void 0;
2296
+ return parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
2297
+ }
2298
+ if (notification.method === "item/started" || notification.method === "item/completed") {
2299
+ const item = isJsonObject(params.item) ? params.item : void 0;
2300
+ const parentThreadId = item ? (readStringField(item, "senderThreadId") ?? readStringField(params, "threadId"))?.trim() : void 0;
2301
+ const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
2302
+ if (state && parentThreadId) {
2303
+ const turnId = readStringField(params, "turnId");
2304
+ const owner = this.resolveParentOwner(state, turnId);
2305
+ if (notification.method === "item/completed" && readStringField(item, "type") === "subAgentActivity" && normalizeIdentifier(readStringField(item, "kind")) === "started") {
2306
+ const childThreadId = readStringField(item, "agentThreadId")?.trim();
2307
+ const agentPath = readStringField(item, "agentPath");
2308
+ if (childThreadId) this.registerDirectSpawnChild(state, turnId, {
2309
+ parentThreadId,
2310
+ childThreadId,
2311
+ ...agentPath === void 0 ? {} : { agentPath }
2312
+ }, owner);
2313
+ return state;
2314
+ }
2315
+ const isCompletedSpawnAgentTool = notification.method === "item/completed" && readStringField(item, "type") === "collabAgentToolCall" && normalizeIdentifier(readStringField(item, "tool")) === "spawnagent" && normalizeIdentifier(readStringField(item, "status")) === "completed";
2316
+ if (normalizeIdentifier(readStringField(item, "tool")) === "closeagent") return state;
2317
+ const childThreadIds = new Set(readStringArray(item?.receiverThreadIds));
2318
+ let accepted = true;
2319
+ for (const childThreadId of childThreadIds) accepted = Boolean(isCompletedSpawnAgentTool ? this.registerDirectSpawnChild(state, turnId, {
2320
+ parentThreadId,
2321
+ childThreadId
2322
+ }, owner) : this.registerChildThread(parentThreadId, childThreadId)) && accepted;
2323
+ if (!accepted) return;
2324
+ }
2325
+ return state;
2326
+ }
2327
+ }
2328
+ handleClosedChild(notification, state) {
2329
+ if (notification.method !== "item/completed") return;
2330
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2331
+ const item = isJsonObject(params?.item) ? params.item : void 0;
2332
+ if (readStringField(item, "type") !== "collabAgentToolCall" || normalizeIdentifier(readStringField(item, "tool")) !== "closeagent" || normalizeIdentifier(readStringField(item, "status")) !== "completed") return;
2333
+ const childThreadIds = /* @__PURE__ */ new Set([...readStringArray(item?.receiverThreadIds), ...readObjectStringKeys(item?.agentsStates)]);
2334
+ for (const childThreadId of childThreadIds) {
2335
+ const childState = this.childStates.get(childThreadId);
2336
+ if (childState && childState.parentThreadId !== state.parentThreadId) continue;
2337
+ if (childState) this.retireChild(state, childState, "Codex native subagent was closed.");
2338
+ else this.updateChildThreadOwnership("release", childThreadId, this.releaseChildThread);
2339
+ }
2340
+ }
2341
+ retireChild(state, childState, summary) {
2342
+ if (!childState.terminal) {
2343
+ childState.terminal = true;
2344
+ const revision = this.threadStatusRevisions.get(childState.childThreadId);
2345
+ if (revision) revision.terminal = true;
2346
+ const eventAt = this.now();
2347
+ state.mirror?.markAuthoritativeCompletion(childState.childThreadId);
2348
+ state.taskRuntime?.finalizeTaskRunByRunId({
2349
+ runId: codexNativeSubagentRunId(childState.childThreadId),
2350
+ status: "cancelled",
2351
+ endedAt: eventAt,
2352
+ lastEventAt: eventAt,
2353
+ error: summary,
2354
+ progressSummary: summary,
2355
+ terminalSummary: summary
2356
+ });
2357
+ }
2358
+ if (childState.pendingCompletion) {
2359
+ childState.pendingCompletion = void 0;
2360
+ state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2361
+ runId: codexNativeSubagentRunId(childState.childThreadId),
2362
+ deliveryStatus: "failed",
2363
+ error: summary
2364
+ });
2365
+ }
2366
+ this.unregisterChild(childState, { retainSubscription: false });
2367
+ this.updateChildThreadOwnership("release", childState.childThreadId, this.releaseChildThread);
2368
+ }
2369
+ async handleCompletionNotification(notification) {
2370
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2371
+ const parentThreadId = params ? readStringField(params, "threadId")?.trim() : void 0;
2372
+ const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
2373
+ if (!state) return;
2374
+ for (const nativeCompletion of codexNativeSubagentNotifications.fromNotification(notification)) {
2375
+ const childThreadId = this.childThreadIdsByAgentPath.get(buildParentAgentPathKey(state.parentThreadId, nativeCompletion.agentPath));
2376
+ const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
2377
+ if (!childState || childState.parentThreadId !== state.parentThreadId || childState.terminal) {
2378
+ embeddedAgentLog.warn("Ignoring Codex native subagent completion for unknown child thread", {
2379
+ parentThreadId: state.parentThreadId,
2380
+ agentPath: nativeCompletion.agentPath
2381
+ });
2382
+ continue;
2383
+ }
2384
+ const completion = {
2385
+ childThreadId: childState.childThreadId,
2386
+ status: nativeCompletion.status,
2387
+ statusLabel: nativeCompletion.statusLabel,
2388
+ result: nativeCompletion.result
2389
+ };
2390
+ await this.processObservedCompletion(state, childState, completion);
2391
+ }
2392
+ }
2393
+ async processObservedCompletion(state, childState, completion) {
2394
+ if (!isNoFinalCompletion(completion)) {
2395
+ await this.processCompletion(state, childState, completion);
2396
+ return;
2397
+ }
2398
+ this.resumeChild(childState, { scheduleRecovery: false });
2399
+ this.setRecoveryFallback(childState, completion, this.now());
2400
+ await this.reconcileChildThread(childState.childThreadId).catch((error) => {
2401
+ this.logRecoveryFailure(childState.childThreadId, error);
2402
+ return false;
2403
+ });
2404
+ }
2405
+ async reconcileChildState(childState) {
2406
+ const state = this.parentStates.get(childState.parentThreadId);
2407
+ if (!state) return false;
2408
+ const statusRead = this.retainThreadStatusRevision(childState.childThreadId);
2409
+ try {
2410
+ const recovery = await this.readThreadRecovery(childState.childThreadId);
2411
+ if (!statusRead.isCurrent() || this.childStates.get(childState.childThreadId) !== childState) return false;
2412
+ if (recovery.parentThreadId && recovery.parentThreadId !== childState.parentThreadId) {
2413
+ embeddedAgentLog.warn("Codex native subagent parent did not match monitor state", {
2414
+ childThreadId: childState.childThreadId,
2415
+ expectedParentThreadId: childState.parentThreadId,
2416
+ actualParentThreadId: recovery.parentThreadId
2417
+ });
2418
+ this.unregisterChild(childState);
2419
+ return false;
2420
+ }
2421
+ if (recovery.threadState === "active") {
2422
+ this.observeActiveChild(childState);
2423
+ return false;
2424
+ }
2425
+ if (recovery.threadState === "other") this.clearSystemErrorFallback(childState);
2426
+ if (recovery.resumable) {
2427
+ this.settleResumableChild(childState);
2428
+ return false;
2429
+ }
2430
+ const completion = recovery.completion;
2431
+ if (!completion) {
2432
+ if (recovery.fallbackCompletion) this.setRecoveryFallback(childState, recovery.fallbackCompletion, recovery.fallbackCompletion.completedAt ?? this.now());
2433
+ return false;
2434
+ }
2435
+ if (isNoFinalCompletion(completion)) {
2436
+ this.setRecoveryFallback(childState, completion, completion.completedAt ?? this.now());
2437
+ return false;
2438
+ }
2439
+ await this.processCompletion(state, childState, completion, completion.completedAt);
2440
+ return true;
2441
+ } finally {
2442
+ statusRead.release();
2443
+ }
2444
+ }
2445
+ requestThreadRead(childThreadId, includeTurns) {
2446
+ return this.client.request("thread/read", {
2447
+ threadId: childThreadId,
2448
+ includeTurns
2449
+ }, { timeoutMs: THREAD_READ_TIMEOUT_MS });
2450
+ }
2451
+ requestLatestThreadTurn(childThreadId) {
2452
+ return this.client.request("thread/turns/list", {
2453
+ threadId: childThreadId,
2454
+ limit: 1,
2455
+ sortDirection: "desc",
2456
+ itemsView: "full"
2457
+ }, { timeoutMs: THREAD_READ_TIMEOUT_MS });
2458
+ }
2459
+ async readThreadRecovery(childThreadId) {
2460
+ const response = await this.requestThreadRead(childThreadId, true).catch(() => this.requestThreadRead(childThreadId, false));
2461
+ const thread = isJsonObject(response.thread) ? response.thread : void 0;
2462
+ if (!thread || readStringField(thread, "id")?.trim() !== childThreadId) return {
2463
+ resumable: false,
2464
+ threadState: "unavailable"
2465
+ };
2466
+ const threadStatus = isJsonObject(thread.status) ? normalizeIdentifier(readStringField(thread.status, "type")) : void 0;
2467
+ let completion;
2468
+ let fallbackCompletion;
2469
+ let resumable = false;
2470
+ let threadState = threadStatus === "active" ? "active" : threadStatus === "systemerror" ? "system_error" : threadStatus ? "other" : "unavailable";
2471
+ if (threadStatus === "systemerror") {
2472
+ const turnsResponse = await this.requestLatestThreadTurn(childThreadId).catch(() => void 0);
2473
+ const data = isJsonObject(turnsResponse) && Array.isArray(turnsResponse.data) ? turnsResponse.data : [];
2474
+ const latestTurn = isJsonObject(data[0]) ? data[0] : void 0;
2475
+ const latestTurnStatus = normalizeIdentifier(readStringField(latestTurn, "status"));
2476
+ completion = latestTurn && latestTurnStatus === "failed" ? readTurnCompletion(latestTurn, childThreadId) : void 0;
2477
+ if (latestTurnStatus === "inprogress") threadState = "active";
2478
+ else if (!completion) fallbackCompletion = systemErrorFallbackCompletion(childThreadId);
2479
+ } else if (threadStatus !== "active") {
2480
+ const turnRecovery = readThreadTurnRecovery(thread, childThreadId);
2481
+ completion = turnRecovery.completion;
2482
+ resumable = turnRecovery.resumable;
2483
+ }
2484
+ return {
2485
+ parentThreadId: readThreadParentThreadId(thread),
2486
+ completion,
2487
+ fallbackCompletion,
2488
+ resumable,
2489
+ threadState
2490
+ };
2491
+ }
2492
+ async processCompletion(state, childState, completion, eventAt = this.now()) {
2493
+ if (childState.terminal) return;
2494
+ if (!this.claimCompletionDelivery(state, childState)) {
2495
+ this.unregisterChild(childState);
2496
+ return;
2497
+ }
2498
+ childState.terminal = true;
2499
+ const revision = this.threadStatusRevisions.get(childState.childThreadId);
2500
+ if (revision) revision.terminal = true;
2501
+ this.releaseDirectChild(childState);
2502
+ this.clearRecoveryTimers(childState);
2503
+ state.mirror?.markAuthoritativeCompletion(completion.childThreadId);
2504
+ state.taskRuntime?.finalizeTaskRunByRunId({
2505
+ runId: codexNativeSubagentRunId(completion.childThreadId),
2506
+ status: completion.status,
2507
+ endedAt: eventAt,
2508
+ lastEventAt: eventAt,
2509
+ ...completion.status === "succeeded" ? {} : { error: completion.result },
2510
+ progressSummary: completion.result,
2511
+ terminalSummary: completion.result
2512
+ });
2513
+ if (!state.requesterSessionKey || !state.taskRuntimeScope) {
2514
+ this.unregisterChild(childState);
2515
+ return;
2516
+ }
2517
+ childState.pendingCompletion = completion;
2518
+ state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2519
+ runId: codexNativeSubagentRunId(completion.childThreadId),
2520
+ deliveryStatus: "pending"
2521
+ });
2522
+ this.releaseClientRetentionIfIdle();
2523
+ await this.deliverPendingCompletion(state, childState);
2524
+ }
2525
+ async deliverPendingCompletion(state, childState) {
2526
+ const completion = childState.pendingCompletion;
2527
+ if (!completion || !state.requesterSessionKey || !state.taskRuntimeScope) return;
2528
+ if (childState.deliveringCompletion || childState.completionDeliveryTimer) return;
2529
+ childState.deliveringCompletion = true;
2530
+ try {
2531
+ const delivery = await this.runtime.deliverAgentHarnessTaskCompletion({
2532
+ scope: state.taskRuntimeScope,
2533
+ childSessionKey: codexNativeSubagentRunId(completion.childThreadId),
2534
+ childSessionId: completion.childThreadId,
2535
+ announceId: `codex-native:${state.parentThreadId}:${completion.childThreadId}:${completion.status}`,
2536
+ announceType: "Codex native subagent",
2537
+ taskLabel: "Codex native subagent",
2538
+ status: completion.status,
2539
+ statusLabel: completion.statusLabel,
2540
+ result: completion.result,
2541
+ replyInstruction: "Use the Codex native subagent result to continue or wrap up the parent task. If this is a Discord/channel session, send the visible response with the message tool instead of only writing a transcript final answer. Reply in your normal assistant voice and do not expose internal notification markup."
2542
+ });
2543
+ if (this.childStates.get(childState.childThreadId) !== childState || this.parentStates.get(state.parentThreadId) !== state) return;
2544
+ if (isDurableAgentHarnessCompletionDelivery(delivery)) {
2545
+ childState.pendingCompletion = void 0;
2546
+ childState.completionDeliveryAttempt = 0;
2547
+ state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2548
+ runId: codexNativeSubagentRunId(completion.childThreadId),
2549
+ deliveryStatus: "delivered"
2550
+ });
2551
+ this.unregisterChild(childState);
2552
+ return;
2553
+ }
2554
+ const error = delivery.error ?? "completion delivery did not produce a parent response";
2555
+ state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2556
+ runId: codexNativeSubagentRunId(completion.childThreadId),
2557
+ deliveryStatus: "pending",
2558
+ error
2559
+ });
2560
+ this.scheduleCompletionDeliveryRetry(childState, error);
2561
+ } catch (error) {
2562
+ if (this.childStates.get(childState.childThreadId) !== childState || this.parentStates.get(state.parentThreadId) !== state) return;
2563
+ const message = formatErrorMessage(error);
2564
+ state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2565
+ runId: codexNativeSubagentRunId(completion.childThreadId),
2566
+ deliveryStatus: "pending",
2567
+ error: message
2568
+ });
2569
+ this.scheduleCompletionDeliveryRetry(childState, message);
2570
+ embeddedAgentLog.warn("Failed to deliver Codex native subagent completion", {
2571
+ parentThreadId: state.parentThreadId,
2572
+ childThreadId: completion.childThreadId,
2573
+ error: message
2574
+ });
2575
+ } finally {
2576
+ childState.deliveringCompletion = false;
2577
+ }
2578
+ }
2579
+ scheduleCompletionDeliveryRetry(childState, error) {
2580
+ if (!childState.pendingCompletion || childState.completionDeliveryTimer || this.childStates.get(childState.childThreadId) !== childState) return;
2581
+ if (childState.completionDeliveryAttempt >= this.completionDeliveryMaxRetries) {
2582
+ this.parentStates.get(childState.parentThreadId)?.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2583
+ runId: codexNativeSubagentRunId(childState.childThreadId),
2584
+ deliveryStatus: "failed",
2585
+ error
2586
+ });
2587
+ this.unregisterChild(childState);
2588
+ return;
2589
+ }
2590
+ const delayMs = delayForAttempt(this.completionDeliveryRetryDelaysMs, childState.completionDeliveryAttempt++);
2591
+ childState.completionDeliveryTimer = setTimeout(() => {
2592
+ childState.completionDeliveryTimer = void 0;
2593
+ if (this.childStates.get(childState.childThreadId) !== childState) return;
2594
+ const state = this.parentStates.get(childState.parentThreadId);
2595
+ if (state) this.deliverPendingCompletion(state, childState);
2596
+ }, delayMs);
2597
+ unrefTimer(childState.completionDeliveryTimer);
2598
+ }
2599
+ registerChildThread(parentThreadIdInput, childThreadIdInput, options = {}) {
2600
+ const parentThreadId = parentThreadIdInput.trim();
2601
+ const childThreadId = childThreadIdInput.trim();
2602
+ if (!parentThreadId || !childThreadId || this.disposed) return;
2603
+ if (options.claimDirectChild && this.threadStatusRevisions.get(childThreadId)?.terminal) return;
2604
+ let childState = this.childStates.get(childThreadId);
2605
+ if (childState && childState.parentThreadId !== parentThreadId) {
2606
+ embeddedAgentLog.warn("Ignoring Codex native subagent child reparenting", {
2607
+ childThreadId,
2608
+ existingParentThreadId: childState.parentThreadId,
2609
+ attemptedParentThreadId: parentThreadId
2610
+ });
2611
+ return;
2612
+ }
2613
+ if (!childState) {
2614
+ this.updateChildThreadOwnership("claim", childThreadId, this.claimChildThread);
2615
+ this.releaseClientRetention ??= this.retainClient?.();
2616
+ if (!this.parentThreadRetentions.has(parentThreadId)) {
2617
+ const releaseParentThread = this.retainParentThread?.(parentThreadId);
2618
+ if (releaseParentThread) this.parentThreadRetentions.set(parentThreadId, releaseParentThread);
2619
+ }
2620
+ childState = {
2621
+ childThreadId,
2622
+ parentThreadId,
2623
+ agentPathKeys: /* @__PURE__ */ new Set(),
2624
+ assistantMessagesByTurn: /* @__PURE__ */ new Map(),
2625
+ recoveryAttempt: 0,
2626
+ terminal: false,
2627
+ settledWithoutCompletion: false,
2628
+ completionDeliveryAttempt: 0,
2629
+ deliveringCompletion: false
2630
+ };
2631
+ this.childStates.set(childThreadId, childState);
2632
+ this.threadStatusRevisions.set(childThreadId, this.threadStatusRevisions.get(childThreadId) ?? {
2633
+ value: 0,
2634
+ readers: 0,
2635
+ parentThreadId
2636
+ });
2637
+ }
2638
+ if (options.claimDirectChild && !childState.terminal && !childState.settledWithoutCompletion && !childState.releaseDirectChild) childState.releaseDirectChild = options.claimDirectChild(childThreadId);
2639
+ this.registerAgentPath(childState, childThreadId);
2640
+ this.parentStates.get(parentThreadId)?.mirror?.markAuthoritativeCompletionExpected(childThreadId);
2641
+ const agentPath = normalizeOptionalString(options.agentPath);
2642
+ if (agentPath) this.registerAgentPath(childState, agentPath);
2643
+ this.scheduleRecoveryPoll(childState);
2644
+ return childState;
2645
+ }
2646
+ resolveParentOwner(state, turnIdInput) {
2647
+ const turnId = turnIdInput?.trim();
2648
+ if (!turnId) return;
2649
+ const owners = [...state.owners.values()].filter((owner) => owner.turnId === turnId);
2650
+ return owners.length === 1 ? owners[0] : void 0;
2651
+ }
2652
+ registerDirectSpawnChild(state, turnIdInput, evidence, owner) {
2653
+ const childState = this.registerChildThread(state.parentThreadId, evidence.childThreadId, {
2654
+ ...evidence.agentPath === void 0 ? {} : { agentPath: evidence.agentPath },
2655
+ ...owner?.claimDirectChild ? { claimDirectChild: owner.claimDirectChild } : {}
2656
+ });
2657
+ if (!owner) this.bufferPendingDirectSpawnEvidence(turnIdInput, evidence);
2658
+ return childState;
2659
+ }
2660
+ bufferPendingDirectSpawnEvidence(turnIdInput, evidence) {
2661
+ const turnId = turnIdInput?.trim();
2662
+ if (!turnId || !this.hasUnboundParentOwner(evidence.parentThreadId)) return;
2663
+ const pending = this.pendingDirectSpawnEvidence.get(turnId) ?? [];
2664
+ if (pending.some((candidate) => candidate.parentThreadId === evidence.parentThreadId && candidate.childThreadId === evidence.childThreadId && candidate.agentPath === evidence.agentPath) || [...this.pendingDirectSpawnEvidence.values()].reduce((count, entries) => count + entries.length, 0) >= MAX_PENDING_DIRECT_SPAWN_EVIDENCE) return;
2665
+ pending.push(evidence);
2666
+ this.pendingDirectSpawnEvidence.set(turnId, pending);
2667
+ }
2668
+ drainPendingDirectSpawnEvidence(state, owner, turnId) {
2669
+ const pending = this.pendingDirectSpawnEvidence.get(turnId);
2670
+ this.pendingDirectSpawnEvidence.delete(turnId);
2671
+ if (!pending || !owner.claimDirectChild) return;
2672
+ for (const evidence of pending) if (evidence.parentThreadId === state.parentThreadId) this.registerChildThread(state.parentThreadId, evidence.childThreadId, {
2673
+ ...evidence.agentPath === void 0 ? {} : { agentPath: evidence.agentPath },
2674
+ claimDirectChild: owner.claimDirectChild
2675
+ });
2676
+ }
2677
+ hasUnboundParentOwner(parentThreadId) {
2678
+ return [...parentThreadId ? [this.parentStates.get(parentThreadId)].filter((state) => Boolean(state)) : this.parentStates.values()].some((state) => [...state.owners.values()].some((owner) => owner.turnId === void 0));
2679
+ }
2680
+ clearUnconsumablePendingDirectSpawnEvidence() {
2681
+ if (!this.hasUnboundParentOwner()) this.pendingDirectSpawnEvidence.clear();
2682
+ }
2683
+ clearPendingDirectSpawnEvidenceForParent(parentThreadId) {
2684
+ this.filterPendingDirectSpawnEvidence((evidence) => evidence.parentThreadId !== parentThreadId);
2685
+ }
2686
+ removePendingDirectSpawnEvidenceForChild(childThreadId) {
2687
+ this.filterPendingDirectSpawnEvidence((evidence) => evidence.childThreadId !== childThreadId);
2688
+ }
2689
+ filterPendingDirectSpawnEvidence(keep) {
2690
+ for (const [turnId, pending] of this.pendingDirectSpawnEvidence) {
2691
+ const remaining = pending.filter(keep);
2692
+ if (remaining.length) this.pendingDirectSpawnEvidence.set(turnId, remaining);
2693
+ else this.pendingDirectSpawnEvidence.delete(turnId);
2694
+ }
2695
+ }
2696
+ registerAgentPath(childState, agentPath) {
2697
+ const key = buildParentAgentPathKey(childState.parentThreadId, agentPath);
2698
+ const existingChild = this.childThreadIdsByAgentPath.get(key);
2699
+ if (existingChild && existingChild !== childState.childThreadId) {
2700
+ embeddedAgentLog.warn("Ignoring conflicting Codex native subagent agent path", {
2701
+ parentThreadId: childState.parentThreadId,
2702
+ agentPath,
2703
+ existingChildThreadId: existingChild,
2704
+ attemptedChildThreadId: childState.childThreadId
2705
+ });
2706
+ return;
2707
+ }
2708
+ this.childThreadIdsByAgentPath.set(key, childState.childThreadId);
2709
+ childState.agentPathKeys.add(key);
2710
+ }
2711
+ unregisterChild(childState, options = {}) {
2712
+ this.releaseDirectChild(childState);
2713
+ if (childState.terminal && options.retainSubscription !== false && !this.disposed) this.updateChildThreadOwnership("retain", childState.childThreadId, this.retainChildThread);
2714
+ this.clearRecoveryTimers(childState);
2715
+ if (childState.completionDeliveryTimer) clearTimeout(childState.completionDeliveryTimer);
2716
+ const deliveryOwnerKey = childState.deliveryOwnerKey;
2717
+ if (deliveryOwnerKey && completionDeliveryOwners.get(deliveryOwnerKey) === childState) completionDeliveryOwners.delete(deliveryOwnerKey);
2718
+ childState.deliveryOwnerKey = void 0;
2719
+ for (const key of childState.agentPathKeys) if (this.childThreadIdsByAgentPath.get(key) === childState.childThreadId) this.childThreadIdsByAgentPath.delete(key);
2720
+ if (this.childStates.get(childState.childThreadId) === childState) this.childStates.delete(childState.childThreadId);
2721
+ if (![...this.childStates.values()].some((remainingChild) => remainingChild.parentThreadId === childState.parentThreadId)) {
2722
+ const releaseParentThread = this.parentThreadRetentions.get(childState.parentThreadId);
2723
+ this.parentThreadRetentions.delete(childState.parentThreadId);
2724
+ releaseParentThread?.();
2725
+ }
2726
+ this.collectThreadStatusRevision(childState.childThreadId);
2727
+ this.releaseClientRetentionIfIdle();
2728
+ const state = this.parentStates.get(childState.parentThreadId);
2729
+ if (state) this.pruneParentIfUnused(state);
2730
+ }
2731
+ releaseDirectChild(childState) {
2732
+ const release = childState.releaseDirectChild;
2733
+ childState.releaseDirectChild = void 0;
2734
+ release?.();
2735
+ }
2736
+ rejectPendingDirectChild(state, childThreadId, reason) {
2737
+ for (const owner of state.owners.values()) owner.rejectPendingDirectChild?.(childThreadId, reason);
2738
+ }
2739
+ updateChildThreadOwnership(operation, childThreadId, update) {
2740
+ if (!update) return;
2741
+ update(childThreadId).catch((error) => {
2742
+ embeddedAgentLog.warn("Failed to update Codex native subagent thread ownership", {
2743
+ operation,
2744
+ childThreadId,
2745
+ error: formatErrorMessage(error)
2746
+ });
2747
+ });
2748
+ }
2749
+ releaseClientRetentionIfIdle() {
2750
+ if ([...this.childStates.values()].some((childState) => !childState.terminal && !childState.settledWithoutCompletion)) return;
2751
+ this.releaseRetainedClient();
2752
+ }
2753
+ releaseRetainedClient() {
2754
+ const release = this.releaseClientRetention;
2755
+ this.releaseClientRetention = void 0;
2756
+ release?.();
2757
+ }
2758
+ claimCompletionDelivery(state, childState) {
2759
+ const requesterSessionKey = state.requesterSessionKey?.trim();
2760
+ if (!requesterSessionKey) return true;
2761
+ const key = `${requesterSessionKey}\0${childState.childThreadId}`;
2762
+ const owner = completionDeliveryOwners.get(key);
2763
+ if (owner) return owner === childState;
2764
+ const runId = codexNativeSubagentRunId(childState.childThreadId);
2765
+ if (state.taskRuntime?.listTaskRecords().some((task) => task.runId === runId && task.deliveryStatus === "delivered")) return false;
2766
+ completionDeliveryOwners.set(key, childState);
2767
+ childState.deliveryOwnerKey = key;
2768
+ return true;
2769
+ }
2770
+ pruneParentIfUnused(state) {
2771
+ if (state.owners.size > 0) return;
2772
+ for (const childState of this.childStates.values()) if (childState.parentThreadId === state.parentThreadId) return;
2773
+ if (this.parentStates.get(state.parentThreadId) === state) {
2774
+ this.clearTerminalRevisionsForParent(state.parentThreadId);
2775
+ this.parentStates.delete(state.parentThreadId);
2776
+ }
2777
+ }
2778
+ clearTerminalRevisionsForParent(parentThreadId) {
2779
+ for (const [threadId, revision] of this.threadStatusRevisions) if (revision.parentThreadId === parentThreadId) this.collectThreadStatusRevision(threadId, revision);
2780
+ }
2781
+ collectThreadStatusRevision(threadId, revision = this.threadStatusRevisions.get(threadId)) {
2782
+ if (!revision || revision.readers > 0 || this.childStates.has(threadId)) return;
2783
+ if ((revision.parentThreadId ? this.parentStates.get(revision.parentThreadId) : void 0)?.owners.size) return;
2784
+ if (this.threadStatusRevisions.get(threadId) === revision) this.threadStatusRevisions.delete(threadId);
2785
+ }
2786
+ scheduleRecoveryPoll(childState) {
2787
+ if (childState.terminal || childState.settledWithoutCompletion || childState.recoveryTimer || this.disposed || this.recoveryPollDelaysMs.length === 0) return;
2788
+ const delayMs = delayForAttempt(this.recoveryPollDelaysMs, childState.recoveryAttempt++);
2789
+ childState.recoveryTimer = setTimeout(() => {
2790
+ childState.recoveryTimer = void 0;
2791
+ this.reconcileChildThread(childState.childThreadId).catch((error) => {
2792
+ this.logRecoveryFailure(childState.childThreadId, error);
2793
+ return false;
2794
+ }).then(async (reconciled) => {
2795
+ if (reconciled || this.childStates.get(childState.childThreadId) !== childState) return;
2796
+ const fallback = childState.fallbackCompletion;
2797
+ const state = this.parentStates.get(childState.parentThreadId);
2798
+ if (fallback && state && childState.recoveryAttempt >= 2) {
2799
+ await this.processCompletion(state, childState, fallback, fallback.completedAt ?? this.now());
2800
+ return;
2801
+ }
2802
+ this.scheduleRecoveryPoll(childState);
2803
+ });
2804
+ }, delayMs);
2805
+ unrefTimer(childState.recoveryTimer);
2806
+ }
2807
+ setRecoveryFallback(childState, completion, eventAt) {
2808
+ if (childState.terminal) return;
2809
+ const current = childState.fallbackCompletion;
2810
+ if (current?.status === completion.status && current.statusLabel === completion.statusLabel && current.result === completion.result) return;
2811
+ if (childState.recoveryTimer) {
2812
+ clearTimeout(childState.recoveryTimer);
2813
+ childState.recoveryTimer = void 0;
2814
+ }
2815
+ childState.recoveryAttempt = 0;
2816
+ childState.fallbackCompletion = {
2817
+ ...completion,
2818
+ completedAt: eventAt
2819
+ };
2820
+ this.scheduleRecoveryPoll(childState);
2821
+ }
2822
+ clearSystemErrorFallback(childState) {
2823
+ if (childState.fallbackCompletion?.statusLabel !== "system_error") return;
2824
+ childState.fallbackCompletion = void 0;
2825
+ }
2826
+ retainThreadStatusRevision(threadId) {
2827
+ const revision = this.threadStatusRevisions.get(threadId) ?? {
2828
+ value: 0,
2829
+ readers: 0
2830
+ };
2831
+ this.threadStatusRevisions.set(threadId, revision);
2832
+ revision.readers += 1;
2833
+ const capturedValue = revision.value;
2834
+ let retained = true;
2835
+ return {
2836
+ isCurrent: () => this.threadStatusRevisions.get(threadId) === revision && revision.value === capturedValue,
2837
+ release: () => {
2838
+ if (!retained) return;
2839
+ retained = false;
2840
+ revision.readers -= 1;
2841
+ this.collectThreadStatusRevision(threadId, revision);
2842
+ }
2843
+ };
2844
+ }
2845
+ clearRecoveryTimers(childState) {
2846
+ if (childState.recoveryTimer) {
2847
+ clearTimeout(childState.recoveryTimer);
2848
+ childState.recoveryTimer = void 0;
2849
+ }
2850
+ }
2851
+ async reconcileTaskRowsForParent(state) {
2852
+ if (this.disposed || this.parentStates.get(state.parentThreadId) !== state || !state.taskRuntime || !state.requesterSessionKey || !state.taskRuntimeScope) return;
2853
+ const candidates = /* @__PURE__ */ new Map();
2854
+ for (const task of state.taskRuntime.listTaskRecords()) {
2855
+ if (task.requesterSessionKey !== state.requesterSessionKey || !this.shouldReconcileCodexNativeTask(task)) continue;
2856
+ const childThreadId = task.runId.slice(13).trim();
2857
+ candidates.set(childThreadId, {
2858
+ parentState: state,
2859
+ requesterSessionKey: state.requesterSessionKey,
2860
+ childThreadId,
2861
+ recoveryAttempt: 0,
2862
+ taskRuntimeScope: state.taskRuntimeScope,
2863
+ agentId: state.agentId,
2864
+ taskRuntime: state.taskRuntime
2865
+ });
2866
+ }
2867
+ for (const candidate of candidates.values()) await this.reconcileTaskCandidate(candidate);
2868
+ }
2869
+ async reconcileTaskCandidate(candidate) {
2870
+ const key = `${candidate.requesterSessionKey}\0${candidate.childThreadId}`;
2871
+ const scheduled = this.taskReconciliationTimers.get(key);
2872
+ if (scheduled) {
2873
+ clearTimeout(scheduled);
2874
+ this.taskReconciliationTimers.delete(key);
2875
+ }
2876
+ const existing = this.taskReconciliations.get(key);
2877
+ if (existing) {
2878
+ await existing;
2879
+ return;
2880
+ }
2881
+ const reconciliation = this.reconcileTaskCandidateOnce(candidate);
2882
+ this.taskReconciliations.set(key, reconciliation);
2883
+ try {
2884
+ await reconciliation;
2885
+ } finally {
2886
+ if (this.taskReconciliations.get(key) === reconciliation) this.taskReconciliations.delete(key);
2887
+ }
2888
+ }
2889
+ scheduleTaskCandidateReconciliation(candidate) {
2890
+ const key = `${candidate.requesterSessionKey}\0${candidate.childThreadId}`;
2891
+ if (this.disposed || this.retiredParentStates.has(candidate.parentState) || this.recoveryPollDelaysMs.length === 0 || this.taskReconciliationTimers.has(key)) return;
2892
+ const delayMs = delayForAttempt(this.recoveryPollDelaysMs, candidate.recoveryAttempt++);
2893
+ const timer = setTimeout(() => {
2894
+ this.taskReconciliationTimers.delete(key);
2895
+ this.reconcileTaskCandidate(candidate).catch((error) => {
2896
+ this.logRecoveryFailure(candidate.childThreadId, error);
2897
+ this.scheduleTaskCandidateReconciliation(candidate);
2898
+ });
2899
+ }, delayMs);
2900
+ this.taskReconciliationTimers.set(key, timer);
2901
+ unrefTimer(timer);
2902
+ }
2903
+ async reconcileTaskCandidateOnce(candidate) {
2904
+ if (this.retiredParentStates.has(candidate.parentState)) return;
2905
+ const runId = codexNativeSubagentRunId(candidate.childThreadId);
2906
+ const task = candidate.taskRuntime.listTaskRecords().find((record) => record.runId === runId);
2907
+ if (!task || task.requesterSessionKey !== candidate.requesterSessionKey || !this.shouldReconcileCodexNativeTask(task)) return;
2908
+ const childBeforeRead = this.childStates.get(candidate.childThreadId);
2909
+ const statusRead = this.retainThreadStatusRevision(candidate.childThreadId);
2910
+ try {
2911
+ let recovery;
2912
+ try {
2913
+ recovery = await this.readThreadRecovery(candidate.childThreadId);
2914
+ } catch (error) {
2915
+ this.logRecoveryFailure(candidate.childThreadId, error);
2916
+ this.scheduleTaskCandidateReconciliation(candidate);
2917
+ return;
2918
+ }
2919
+ if (this.retiredParentStates.has(candidate.parentState)) return;
2920
+ if (!statusRead.isCurrent() || this.childStates.get(candidate.childThreadId) !== childBeforeRead) {
2921
+ this.scheduleTaskCandidateReconciliation(candidate);
2922
+ return;
2923
+ }
2924
+ const parentThreadId = recovery.parentThreadId;
2925
+ if (!parentThreadId) {
2926
+ this.scheduleTaskCandidateReconciliation(candidate);
2927
+ return;
2928
+ }
2929
+ let state = this.parentStates.get(parentThreadId);
2930
+ if (state && state.requesterSessionKey !== candidate.requesterSessionKey) return;
2931
+ if (!state) {
2932
+ state = {
2933
+ parentThreadId,
2934
+ owners: /* @__PURE__ */ new Map(),
2935
+ requesterSessionKey: candidate.requesterSessionKey,
2936
+ taskRuntimeScope: candidate.taskRuntimeScope,
2937
+ agentId: candidate.agentId,
2938
+ taskRuntime: candidate.taskRuntime
2939
+ };
2940
+ this.prepareParentTaskRuntime(state);
2941
+ this.parentStates.set(parentThreadId, state);
2942
+ }
2943
+ const childState = this.registerChildThread(parentThreadId, candidate.childThreadId);
2944
+ if (!childState) {
2945
+ this.pruneParentIfUnused(state);
2946
+ return;
2947
+ }
2948
+ if (recovery.threadState === "active") this.observeActiveChild(childState);
2949
+ if (recovery.threadState === "other") this.clearSystemErrorFallback(childState);
2950
+ if (recovery.resumable) {
2951
+ this.settleResumableChild(childState);
2952
+ return;
2953
+ }
2954
+ const completion = recovery.completion;
2955
+ if (!completion) {
2956
+ if (recovery.fallbackCompletion) {
2957
+ this.setRecoveryFallback(childState, recovery.fallbackCompletion, recovery.fallbackCompletion.completedAt ?? this.now());
2958
+ return;
2959
+ }
2960
+ this.scheduleRecoveryPoll(childState);
2961
+ return;
2962
+ }
2963
+ if (isNoFinalCompletion(completion)) {
2964
+ this.setRecoveryFallback(childState, completion, completion.completedAt ?? this.now());
2965
+ return;
2966
+ }
2967
+ await this.processCompletion(state, childState, completion, completion.completedAt);
2968
+ } finally {
2969
+ statusRead.release();
2970
+ }
2971
+ }
2972
+ shouldReconcileCodexNativeTask(task) {
2973
+ if (task.status === "queued" || task.status === "running" || task.deliveryStatus === "pending") return true;
2974
+ if (task.deliveryStatus !== "not_applicable" || task.endedAt === void 0) return false;
2975
+ return task.endedAt >= this.now() - RECENT_TERMINAL_TASK_RECONCILE_GRACE_MS;
2976
+ }
2977
+ logRecoveryFailure(childThreadId, error) {
2978
+ embeddedAgentLog.debug("Codex native subagent history is not ready", {
2979
+ childThreadId,
2980
+ error: formatErrorMessage(error)
2981
+ });
2982
+ }
2983
+ };
2984
+ const codexNativeSubagentMonitorRuntime = {
2985
+ Monitor,
2986
+ register: registerMonitor,
2987
+ retireParent: (client, parentThreadId) => {
2988
+ monitors.get(client)?.retireParent(parentThreadId);
2989
+ }
2990
+ };
2991
+ function readThreadTurnRecovery(thread, childThreadId) {
2992
+ const turns = Array.isArray(thread.turns) ? thread.turns : [];
2993
+ for (let index = turns.length - 1; index >= 0; index -= 1) {
2994
+ const turn = turns[index];
2995
+ if (!isJsonObject(turn)) continue;
2996
+ const status = normalizeIdentifier(readStringField(turn, "status"));
2997
+ return {
2998
+ completion: readTurnCompletion(turn, childThreadId),
2999
+ resumable: status === "interrupted"
3000
+ };
3001
+ }
3002
+ return { resumable: false };
3003
+ }
3004
+ function toChildTurnCompletion(childState, turn) {
3005
+ const status = normalizeIdentifier(readStringField(turn, "status"));
3006
+ if (status === "completed") {
3007
+ const turnId = readStringField(turn, "id");
3008
+ const result = turnId ? lastChildAssistantMessage(childState, turnId) : void 0;
3009
+ return {
3010
+ childThreadId: childState.childThreadId,
3011
+ status: "succeeded",
3012
+ statusLabel: result ? "turn_completed" : "completed_without_final_message",
3013
+ result: result ?? "Codex native subagent completed without a final assistant message."
3014
+ };
3015
+ }
3016
+ if (status === "failed") return {
3017
+ childThreadId: childState.childThreadId,
3018
+ status: "failed",
3019
+ statusLabel: "turn_failed",
3020
+ result: readTurnErrorMessage(turn) ?? "Codex native subagent failed."
3021
+ };
3022
+ }
3023
+ function lastChildAssistantMessage(childState, turnId) {
3024
+ const messages = childState.assistantMessagesByTurn.get(turnId);
3025
+ if (!messages) return;
3026
+ for (const itemId of messages.order.toReversed()) if (messages.finalMessageIds.has(itemId) && !messages.commentaryIds.has(itemId)) {
3027
+ const text = normalizeOptionalString(messages.texts.get(itemId));
3028
+ if (text) return text;
3029
+ }
3030
+ }
3031
+ function readTurnErrorMessage(turn) {
3032
+ const error = isJsonObject(turn.error) ? turn.error : void 0;
3033
+ return normalizeOptionalString(readStringField(error, "message")) ?? normalizeOptionalString(isJsonObject(error?.codexErrorInfo) ? readStringField(error.codexErrorInfo, "message") : void 0);
3034
+ }
3035
+ function systemErrorFallbackCompletion(childThreadId) {
3036
+ return {
3037
+ childThreadId,
3038
+ status: "failed",
3039
+ statusLabel: "system_error",
3040
+ result: "Codex app-server reported a system error for the native subagent thread."
3041
+ };
3042
+ }
3043
+ function readTurnCompletion(turn, childThreadId) {
3044
+ const status = normalizeIdentifier(readStringField(turn, "status"));
3045
+ if (status === "inprogress" || !status) return;
3046
+ const result = readLastAgentMessage(turn);
3047
+ const completedAtSeconds = asFiniteNumber(turn.completedAt);
3048
+ const completedAt = completedAtSeconds === void 0 ? void 0 : Math.round(completedAtSeconds * 1e3);
3049
+ if (status === "completed") return {
3050
+ childThreadId,
3051
+ status: "succeeded",
3052
+ statusLabel: result ? "task_complete" : "completed_without_final_message",
3053
+ result: result ?? "Codex native subagent completed without a final assistant message.",
3054
+ completedAt
3055
+ };
3056
+ if (status === "interrupted") return;
3057
+ if (status === "failed") return {
3058
+ childThreadId,
3059
+ status: "failed",
3060
+ statusLabel: "task_failed",
3061
+ result: readTurnErrorMessage(turn) ?? result ?? "Codex native subagent failed.",
3062
+ completedAt
3063
+ };
3064
+ }
3065
+ function readLastAgentMessage(turn) {
3066
+ const items = Array.isArray(turn.items) ? turn.items : [];
3067
+ let legacyResult;
3068
+ for (let index = items.length - 1; index >= 0; index -= 1) {
3069
+ const item = items[index];
3070
+ if (!isJsonObject(item)) continue;
3071
+ if (normalizeIdentifier(readStringField(item, "type")) !== "agentmessage") continue;
3072
+ const text = readStringField(item, "text")?.trim();
3073
+ if (!text) continue;
3074
+ const phase = normalizeIdentifier(readStringField(item, "phase"));
3075
+ if (phase === "finalanswer") return text;
3076
+ if (!phase) legacyResult ??= text;
3077
+ }
3078
+ return legacyResult;
3079
+ }
3080
+ function buildParentAgentPathKey(parentThreadId, agentPath) {
3081
+ return `${parentThreadId}\0${agentPath}`;
3082
+ }
3083
+ function isNoFinalCompletion(completion) {
3084
+ return completion.status === "succeeded" && completion.statusLabel === "completed_without_final_message";
3085
+ }
3086
+ function delayForAttempt(delays, attempt) {
3087
+ return Math.max(1, delays[Math.min(attempt, delays.length - 1)] ?? 1);
3088
+ }
3089
+ function readThreadParentThreadId(thread) {
3090
+ return readStringField(thread, "parentThreadId")?.trim() ?? readStringField(readThreadSpawnSource(thread), "parent_thread_id")?.trim();
3091
+ }
3092
+ function readThreadSpawnSource(thread) {
3093
+ const source = isJsonObject(thread?.source) ? thread.source : void 0;
3094
+ const subAgent = isJsonObject(source?.subAgent) ? source.subAgent : void 0;
3095
+ return isJsonObject(subAgent?.thread_spawn) ? subAgent.thread_spawn : void 0;
3096
+ }
3097
+ function readStringArray(value) {
3098
+ if (!Array.isArray(value)) return [];
3099
+ return value.filter((entry) => typeof entry === "string" && entry.trim() !== "");
3100
+ }
3101
+ function readObjectStringKeys(value) {
3102
+ return isJsonObject(value) ? Object.keys(value).filter((entry) => entry.trim() !== "") : [];
3103
+ }
3104
+ function normalizeIdentifier(value) {
3105
+ return value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
3106
+ }
3107
+ function unrefTimer(timer) {
3108
+ if (typeof timer === "object" && timer && "unref" in timer) timer.unref();
3109
+ }
3110
+ //#endregion
3111
+ export { CodexToolProgressProjection as a, isCodexCommandBearingToolCall as c, sanitizeCodexToolResponse as d, readCodexMirroredSessionHistoryMessages as i, resolveCodexToolProgressDetailMode as l, CodexEventProjection as n, shouldEmitTranscriptToolProgress as o, CodexToolTranscriptProjection as r, inferCodexDynamicToolMeta as s, codexNativeSubagentMonitorRuntime as t, sanitizeCodexToolArguments as u };