@openclaw/codex 2026.7.1-beta.1 → 2026.7.1-beta.4

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 (39) hide show
  1. package/dist/{app-server-policy-xi0ZmBGO.js → app-server-policy-BUk0GLMy.js} +5 -5
  2. package/dist/{attempt-notifications-l9LElGOU.js → attempt-notifications-BGsEIIDI.js} +5 -40
  3. package/dist/{command-handlers-vZy8xX5X.js → command-handlers-UVniG5Hl.js} +267 -232
  4. package/dist/command-rpc-BjgEPh19.js +784 -0
  5. package/dist/compact-CZzbcygx.js +603 -0
  6. package/dist/{computer-use-FPGG-mtX.js → computer-use-DDeySrnb.js} +3 -3
  7. package/dist/{config-C_whBx3M.js → config-CYEDnLJ2.js} +36 -15
  8. package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
  9. package/dist/doctor-contract-api.js +439 -22
  10. package/dist/harness.js +29 -10
  11. package/dist/index.js +1119 -28
  12. package/dist/{media-understanding-provider-C5yYzx_6.js → media-understanding-provider-DgqRvU2_.js} +13 -8
  13. package/dist/media-understanding-provider.js +1 -1
  14. package/dist/{models-Ccy1_7nN.js → models-DDmO1zwd.js} +2 -2
  15. package/dist/notification-correlation-Bo7KB3ks.js +35 -0
  16. package/dist/{plugin-app-cache-key-CDioyur-.js → plugin-app-cache-key-o-AHbdaf.js} +8 -4
  17. package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
  18. package/dist/{protocol-validators-CB4mtyrs.js → protocol-validators-dZQ-UTOa.js} +117 -62
  19. package/dist/{provider-capabilities-CE5k8cmO.js → provider-capabilities-gTCwjfmh.js} +2920 -114
  20. package/dist/{provider-DosqwRb6.js → provider-cc_62eQE.js} +19 -10
  21. package/dist/provider.js +2 -2
  22. package/dist/{request-BQuSQF29.js → request-BcJyl8KL.js} +2 -2
  23. package/dist/{run-attempt-O3Zs32KI.js → run-attempt-8SPLiDlT.js} +1016 -2877
  24. package/dist/{sandbox-guard-pTY2COoC.js → sandbox-guard-DA2TQfZW.js} +3 -3
  25. package/dist/session-binding-Dc03iwRF.js +595 -0
  26. package/dist/session-binding-meta-B7aEMU7g.js +6 -0
  27. package/dist/{shared-client-rR6TDJA3.js → shared-client-4ICy3U6d.js} +892 -103
  28. package/dist/{side-question-DDVgFmpv.js → side-question-DkjXvYv7.js} +132 -140
  29. package/dist/{thread-lifecycle-DeSMZSXr.js → thread-lifecycle-BskXnNP-.js} +609 -444
  30. package/dist/{web-search-provider.runtime-Q89rQhsL.js → web-search-provider.runtime-B5F42P7x.js} +3 -3
  31. package/npm-shrinkwrap.json +34 -34
  32. package/openclaw.plugin.json +20 -0
  33. package/package.json +9 -6
  34. package/dist/client-IcTBKEqA.js +0 -732
  35. package/dist/client-factory-CwjdQQ4j.js +0 -18
  36. package/dist/compact-BzB9AqN1.js +0 -278
  37. package/dist/conversation-binding-CEE2UyYb.js +0 -1570
  38. package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
  39. package/dist/session-binding-Cm0Gg7Z1.js +0 -339
@@ -1,18 +1,335 @@
1
- import { n as isJsonObject } from "./protocol-dh-ETiNd.js";
2
- import { C as isForcedPrivateQaCodexRuntime, S as filterCodexDynamicTools, _ as resolveCodexWebSearchPlan, w as normalizeCodexDynamicToolName, x as sanitizeInlineImageDataUrl, y as invalidInlineImageText } from "./thread-lifecycle-DeSMZSXr.js";
3
- import { c as isTrustedCodexModelBackedOpenAIProvider, u as readCodexPluginConfig } from "./config-C_whBx3M.js";
4
- import { a as formatCodexDisplayText } from "./app-server-policy-xi0ZmBGO.js";
5
- import { o as releaseLeasedSharedCodexAppServerClient } from "./shared-client-rR6TDJA3.js";
6
- import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-pTY2COoC.js";
7
- import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
8
- import { asOptionalRecord, isRecord, normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { r as isJsonObject } from "./protocol-2POPqAY4.js";
2
+ import { c as isTrustedCodexModelBackedOpenAIProvider, u as readCodexPluginConfig } from "./config-CYEDnLJ2.js";
3
+ import { c as readCodexTurn } from "./protocol-validators-dZQ-UTOa.js";
4
+ import { C as isForcedPrivateQaCodexRuntime, S as filterCodexDynamicTools, _ as resolveCodexWebSearchPlan, b as sanitizeCodexHistoryImagePayloads, w as normalizeCodexDynamicToolName, x as sanitizeInlineImageDataUrl, y as invalidInlineImageText } from "./thread-lifecycle-BskXnNP-.js";
5
+ import { c as formatCodexUsageLimitErrorMessage } from "./provider-cc_62eQE.js";
6
+ import { o as releaseLeasedSharedCodexAppServerClient } from "./shared-client-4ICy3U6d.js";
7
+ import { a as formatCodexDisplayText } from "./app-server-policy-BUk0GLMy.js";
8
+ import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-DA2TQfZW.js";
9
+ import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-Bo7KB3ks.js";
10
+ import fs from "node:fs/promises";
11
+ import { addTimerTimeoutGraceMs, asDateTimestampMs, finiteSecondsToTimerSafeMilliseconds, parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
12
+ import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
9
13
  import { createHash } from "node:crypto";
10
14
  import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
11
15
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
12
- import { HEARTBEAT_RESPONSE_TOOL_NAME, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, callGatewayTool, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractMessagingToolSend, extractMessagingToolSendResult, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeToolTerminalPresentation, formatApprovalDisplayPath, getChannelAgentToolMeta, getPluginToolMeta, hasNativeHookRelayInvocation, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isMessagingTool, isMessagingToolSendAction, isReplaySafeToolCall, isSubagentSessionKey, isToolResultError, isToolWrappedWithBeforeToolCallHook, normalizeAgentRuntimeTools, normalizeHeartbeatToolResponse, projectRuntimeToolInputSchema, registerNativeHookRelay, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveNativeHookRelayDeferredToolApproval, runAgentHarnessAfterToolCallHook, runBeforeToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, supportsModelTools, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
13
- import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
16
+ import { asOptionalRecord, isRecord, normalizeOptionalString, normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
17
+ import { HEARTBEAT_RESPONSE_TOOL_NAME, TOOL_PROGRESS_OUTPUT_MAX_CHARS, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, callGatewayTool, classifyAgentHarnessTerminalOutcome, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, emitAgentEvent, extractMessagingToolSend, extractMessagingToolSendResult, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatErrorMessage, formatToolAggregate, formatToolExecutionErrorMessage, formatToolProgressOutput, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, hasNativeHookRelayInvocation, inferToolMetaFromArgs, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isMessagingTool, isMessagingToolSendAction, isReplaySafeToolCall, isSubagentSessionKey, isToolResultError, isToolWrappedWithBeforeToolCallHook, normalizeAgentRuntimeTools, normalizeHeartbeatToolResponse, normalizeUsage, projectRuntimeToolInputSchema, registerNativeHookRelay, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, resolveToolResultFailureKind, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runBeforeToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, supportsModelTools, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
18
+ import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
19
+ import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
14
20
  import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
15
21
  import { buildExecAutoReviewInputForShellCommand, reviewExecRequestWithConfiguredModel } from "openclaw/plugin-sdk/agent-harness-exec-review-runtime";
22
+ import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
23
+ import { publishSessionTranscriptUpdateByIdentity, resolveSessionTranscriptTarget, withSessionTranscriptWriteLock } from "openclaw/plugin-sdk/session-transcript-runtime";
24
+ import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
25
+ import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
26
+ //#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
27
+ /** Leaf helper shared by native and dynamic tool diagnostics. */
28
+ const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
29
+ "codex_startup_timeout",
30
+ "turn_completion_idle_timeout",
31
+ "turn_progress_idle_timeout",
32
+ "turn_terminal_idle_timeout"
33
+ ]);
34
+ /** Preserves timeout provenance when an enclosing run aborts an active tool. */
35
+ function resolveCodexToolAbortTerminalReason(signal) {
36
+ try {
37
+ const reason = signal.reason;
38
+ if (typeof reason === "string") {
39
+ if (CODEX_TIMEOUT_ABORT_REASONS.has(reason)) return "timed_out";
40
+ return reason === "client_closed" ? "failed" : "cancelled";
41
+ }
42
+ if (reason && typeof reason === "object") {
43
+ const record = reason;
44
+ if (record.name === "TimeoutError" || record.reason === "timeout") return "timed_out";
45
+ }
46
+ } catch {
47
+ return "cancelled";
48
+ }
49
+ return "cancelled";
50
+ }
51
+ /** Hard cap for per-call Codex dynamic tool timeout overrides. */
52
+ const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
53
+ const CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS = 3e4;
54
+ const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
55
+ /** Timeout for message-delivery dynamic tool calls. */
56
+ const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 12e4;
57
+ const LOG_FIELD_MAX_LENGTH = 160;
58
+ function normalizeLogField(value) {
59
+ if (typeof value !== "string") return;
60
+ const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
61
+ if (!normalized) return;
62
+ return normalized.length > LOG_FIELD_MAX_LENGTH ? `${normalized.slice(0, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
63
+ }
64
+ function readNumericTimeoutMs(value) {
65
+ if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
66
+ if (typeof value === "string") {
67
+ const parsed = parseStrictNonNegativeInteger(value);
68
+ if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
69
+ }
70
+ }
71
+ function formatDynamicToolTimeoutDetails(params) {
72
+ const tool = normalizeLogField(params.call.tool) ?? "unknown";
73
+ const baseMeta = {
74
+ tool: params.call.tool,
75
+ toolCallId: params.call.callId,
76
+ threadId: params.call.threadId,
77
+ turnId: params.call.turnId,
78
+ timeoutMs: params.timeoutMs,
79
+ timeoutKind: "codex_dynamic_tool_rpc"
80
+ };
81
+ if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
82
+ responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
83
+ consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
84
+ meta: baseMeta
85
+ };
86
+ const action = normalizeLogField(params.call.arguments.action);
87
+ const sessionId = normalizeLogField(params.call.arguments.sessionId);
88
+ const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
89
+ const actionPart = action ? ` action=${action}` : "";
90
+ const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
91
+ const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
92
+ const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
93
+ const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
94
+ return {
95
+ responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
96
+ consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
97
+ meta: {
98
+ ...baseMeta,
99
+ processAction: action,
100
+ processSessionId: sessionId,
101
+ processRequestedTimeoutMs: requestedTimeoutMs
102
+ }
103
+ };
104
+ }
105
+ /**
106
+ * Runs a dynamic tool call with run-abort and per-call timeout handling,
107
+ * returning a Codex protocol response instead of throwing.
108
+ */
109
+ async function handleDynamicToolCallWithTimeout(params) {
110
+ let didNotifyAgentToolResult = false;
111
+ const notifyAgentToolResult = (event) => {
112
+ if (didNotifyAgentToolResult) return;
113
+ didNotifyAgentToolResult = true;
114
+ try {
115
+ params.onAgentToolResult?.(event);
116
+ } catch (error) {
117
+ embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
118
+ }
119
+ };
120
+ const notifyFailedToolResult = (message, terminalReason = "failed") => {
121
+ notifyAgentToolResult({
122
+ toolName: params.call.tool,
123
+ result: {
124
+ content: [{
125
+ type: "text",
126
+ text: message
127
+ }],
128
+ details: {
129
+ status: terminalReason,
130
+ error: message
131
+ }
132
+ },
133
+ isError: true
134
+ });
135
+ };
136
+ if (params.signal.aborted) {
137
+ const message = "OpenClaw dynamic tool call aborted before execution.";
138
+ const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
139
+ params.onFallbackSelected?.();
140
+ notifyFailedToolResult(message, terminalReason);
141
+ return failedDynamicToolResponse(message, { terminalReason });
142
+ }
143
+ const controller = new AbortController();
144
+ let timeout;
145
+ let timedOut = false;
146
+ let resolveAbort;
147
+ const abortFromRun = () => {
148
+ const message = "OpenClaw dynamic tool call aborted.";
149
+ const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
150
+ params.onFallbackSelected?.();
151
+ controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
152
+ notifyFailedToolResult(message, terminalReason);
153
+ resolveAbort?.(failedDynamicToolResponse(message, {
154
+ sideEffectEvidence: true,
155
+ terminalReason
156
+ }));
157
+ };
158
+ const abortPromise = new Promise((resolve) => {
159
+ resolveAbort = resolve;
160
+ });
161
+ const timeoutPromise = new Promise((resolve) => {
162
+ const timeoutMs = clampDynamicToolTimeoutMs(params.timeoutMs);
163
+ timeout = setTimeout(() => {
164
+ timedOut = true;
165
+ const timeoutDetails = formatDynamicToolTimeoutDetails({
166
+ call: params.call,
167
+ timeoutMs
168
+ });
169
+ params.onFallbackSelected?.();
170
+ controller.abort(new Error(timeoutDetails.responseMessage));
171
+ params.onTimeout?.();
172
+ embeddedAgentLog.warn("codex dynamic tool call timed out", {
173
+ ...timeoutDetails.meta,
174
+ consoleMessage: timeoutDetails.consoleMessage
175
+ });
176
+ notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
177
+ resolve(failedDynamicToolResponse(timeoutDetails.responseMessage, {
178
+ sideEffectEvidence: true,
179
+ terminalReason: "timed_out"
180
+ }));
181
+ }, timeoutMs);
182
+ timeout.unref?.();
183
+ });
184
+ try {
185
+ params.signal.addEventListener("abort", abortFromRun, { once: true });
186
+ if (params.signal.aborted) abortFromRun();
187
+ const response = await Promise.race([
188
+ params.toolBridge.handleToolCall(params.call, {
189
+ signal: controller.signal,
190
+ onAgentToolResult: notifyAgentToolResult,
191
+ toolCallOrdinal: params.toolCallOrdinal
192
+ }),
193
+ abortPromise,
194
+ timeoutPromise
195
+ ]);
196
+ if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
197
+ return response;
198
+ } catch (error) {
199
+ const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
200
+ const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
201
+ notifyFailedToolResult(message, terminalReason);
202
+ return failedDynamicToolResponse(message, {
203
+ sideEffectEvidence: true,
204
+ terminalReason
205
+ });
206
+ } finally {
207
+ if (timeout) clearTimeout(timeout);
208
+ params.signal.removeEventListener("abort", abortFromRun);
209
+ resolveAbort = void 0;
210
+ if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
211
+ }
212
+ }
213
+ function readDynamicToolResponseText(response) {
214
+ return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
215
+ }
216
+ function failedDynamicToolResponse(message, options) {
217
+ const response = {
218
+ contentItems: [{
219
+ type: "inputText",
220
+ text: message
221
+ }],
222
+ success: false
223
+ };
224
+ Object.defineProperty(response, "diagnosticTerminalType", {
225
+ configurable: true,
226
+ enumerable: false,
227
+ value: "error"
228
+ });
229
+ Object.defineProperty(response, "diagnosticTerminalReason", {
230
+ configurable: true,
231
+ enumerable: false,
232
+ value: options?.terminalReason ?? "failed"
233
+ });
234
+ if (options?.sideEffectEvidence === true) Object.defineProperty(response, "sideEffectEvidence", {
235
+ configurable: true,
236
+ enumerable: false,
237
+ value: true
238
+ });
239
+ return response;
240
+ }
241
+ /** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
242
+ function toCodexDynamicToolProtocolResponse(response) {
243
+ return {
244
+ contentItems: response.contentItems,
245
+ success: response.success
246
+ };
247
+ }
248
+ /** Adds async-started progress details when a tool result continues out of band. */
249
+ function toCodexDynamicToolProgressResponse(response, protocolResponse) {
250
+ if (response.asyncStarted !== true) return protocolResponse;
251
+ return {
252
+ ...protocolResponse,
253
+ details: {
254
+ async: true,
255
+ status: "started"
256
+ }
257
+ };
258
+ }
259
+ /** Decides whether a terminal dynamic tool response can release the Codex turn. */
260
+ function shouldReleaseTurnAfterTerminalDynamicTool(state) {
261
+ return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
262
+ }
263
+ /** Returns true when a non-async result should block terminal-release shortcuts. */
264
+ function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
265
+ return response.asyncStarted !== true;
266
+ }
267
+ /** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
268
+ function resolveTerminalDynamicToolBatchAction(state) {
269
+ if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
270
+ if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
271
+ if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
272
+ return "idle";
273
+ }
274
+ /** Returns true for diagnostic events that terminate a dynamic tool call. */
275
+ function isDynamicToolTerminalDiagnosticEvent(event) {
276
+ return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
277
+ }
278
+ /** Matches terminal diagnostics to a specific dynamic tool call id/name. */
279
+ function isMatchingDynamicToolTerminalDiagnostic(params) {
280
+ if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
281
+ if (params.runId !== void 0) return params.event.runId === params.runId;
282
+ if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
283
+ if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
284
+ return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
285
+ }
286
+ /** Checks pending diagnostics for a terminal event matching a tool call. */
287
+ function hasPendingDynamicToolTerminalDiagnostic(params) {
288
+ return hasPendingInternalDiagnosticEvent((event) => {
289
+ if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
290
+ return isMatchingDynamicToolTerminalDiagnostic({
291
+ event,
292
+ call: params.call,
293
+ runId: params.runId,
294
+ sessionId: params.sessionId,
295
+ sessionKey: params.sessionKey
296
+ });
297
+ });
298
+ }
299
+ /** Resolves per-tool timeout, applying media/message defaults and hard caps. */
300
+ function resolveDynamicToolCallTimeoutMs(params) {
301
+ return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? 9e4);
302
+ }
303
+ function readDynamicToolCallTimeoutMs(value) {
304
+ if (!isJsonObject(value)) return;
305
+ const timeoutMs = readPositiveFiniteTimeoutMs(value.timeoutMs);
306
+ if (timeoutMs !== void 0) return timeoutMs;
307
+ const timeoutSecondsMs = readDynamicToolTimeoutSecondsAsMs(value.timeoutSeconds);
308
+ return timeoutSecondsMs === void 0 ? void 0 : addTimerTimeoutGraceMs(timeoutSecondsMs, CODEX_DYNAMIC_TOOL_TIMEOUT_SECONDS_GRACE_MS);
309
+ }
310
+ function readConfiguredDynamicToolTimeoutMs(toolName, config) {
311
+ if (toolName === "image_generate") {
312
+ const imageGenerationModel = config?.agents?.defaults?.imageGenerationModel;
313
+ if (!imageGenerationModel || typeof imageGenerationModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
314
+ return readPositiveFiniteTimeoutMs(imageGenerationModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
315
+ }
316
+ if (toolName === "image") return readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds) ?? 6e4;
317
+ if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
318
+ }
319
+ function readTimeoutSecondsAsMs(value) {
320
+ const seconds = readPositiveFiniteTimeoutMs(value);
321
+ return seconds === void 0 ? void 0 : seconds * 1e3;
322
+ }
323
+ function readDynamicToolTimeoutSecondsAsMs(value) {
324
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) return;
325
+ return value * 1e3;
326
+ }
327
+ function readPositiveFiniteTimeoutMs(value) {
328
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
329
+ }
330
+ function clampDynamicToolTimeoutMs(timeoutMs) {
331
+ return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
332
+ }
16
333
  /** Default idle timeout while waiting for app-server turn completion. */
17
334
  const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 6e4;
18
335
  /** Short guard after apparent assistant completion. */
@@ -159,7 +476,7 @@ function mapExecDecisionToOutcome(decision) {
159
476
  return "denied";
160
477
  }
161
478
  function truncateForGateway(value, maxLength) {
162
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
479
+ return value.length <= maxLength ? value : `${truncateUtf16Safe(value, maxLength - 3)}...`;
163
480
  }
164
481
  function toLintErrorObject(value, fallbackMessage) {
165
482
  if (value instanceof Error) return value;
@@ -208,6 +525,7 @@ async function handleCodexAppServerApprovalRequest(params) {
208
525
  signal: params.signal
209
526
  });
210
527
  if (policyOutcome?.outcome === "denied") {
528
+ recordNativeToolFailureDisposition(params, context, policyOutcome.failureDisposition);
211
529
  emitApprovalEvent(params.paramsForRun, {
212
530
  phase: "resolved",
213
531
  kind: context.kind,
@@ -274,6 +592,7 @@ async function handleCodexAppServerApprovalRequest(params) {
274
592
  });
275
593
  const approvalId = requestResult?.id;
276
594
  if (!approvalId) {
595
+ recordNativeToolFailureDisposition(params, context, "failed");
277
596
  emitApprovalEvent(params.paramsForRun, {
278
597
  phase: "resolved",
279
598
  kind: context.kind,
@@ -295,10 +614,15 @@ async function handleCodexAppServerApprovalRequest(params) {
295
614
  ...context.eventDetails,
296
615
  message: "Codex app-server approval requested."
297
616
  });
298
- const outcome = mapExecDecisionToOutcome(approvalRequestExplicitlyUnavailable(requestResult) ? null : await waitForPluginApprovalDecision({
617
+ const requestUnavailable = approvalRequestExplicitlyUnavailable(requestResult);
618
+ const decision = requestUnavailable ? null : await waitForPluginApprovalDecision({
299
619
  approvalId,
300
620
  signal: params.signal
301
- }));
621
+ });
622
+ const approvalExpired = !requestUnavailable && decision === null;
623
+ const outcome = params.signal?.aborted ? "cancelled" : mapExecDecisionToOutcome(decision);
624
+ if (outcome === "cancelled") recordNativeToolFailureDisposition(params, context, params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : "cancelled");
625
+ else if (outcome === "unavailable") recordNativeToolFailureDisposition(params, context, approvalExpired ? "timed_out" : "failed");
302
626
  emitApprovalEvent(params.paramsForRun, {
303
627
  phase: "resolved",
304
628
  kind: context.kind,
@@ -313,6 +637,7 @@ async function handleCodexAppServerApprovalRequest(params) {
313
637
  return buildApprovalResponse(params.method, context.requestParams, outcome);
314
638
  } catch (error) {
315
639
  const cancelled = params.signal?.aborted === true;
640
+ recordNativeToolFailureDisposition(params, context, cancelled && params.signal ? resolveCodexToolAbortTerminalReason(params.signal) : "failed");
316
641
  emitApprovalEvent(params.paramsForRun, {
317
642
  phase: "resolved",
318
643
  kind: context.kind,
@@ -325,6 +650,12 @@ async function handleCodexAppServerApprovalRequest(params) {
325
650
  return buildApprovalResponse(params.method, context.requestParams, cancelled ? "cancelled" : "denied");
326
651
  }
327
652
  }
653
+ function recordNativeToolFailureDisposition(params, context, disposition) {
654
+ if (!context.itemId || !disposition) return;
655
+ try {
656
+ params.onNativeToolFailureDisposition?.(context.itemId, params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : disposition);
657
+ } catch {}
658
+ }
328
659
  /** Converts an OpenClaw approval outcome into the app-server method response. */
329
660
  function buildApprovalResponse(method, requestParams, outcome) {
330
661
  if (method === "item/commandExecution/requestApproval") return { decision: commandApprovalDecision(requestParams, outcome) };
@@ -343,12 +674,12 @@ function buildApprovalResponse(method, requestParams, outcome) {
343
674
  }
344
675
  function matchesCurrentTurn(requestParams, threadId, turnId) {
345
676
  if (!requestParams) return false;
346
- const requestThreadId = readString$1(requestParams, "threadId") ?? readString$1(requestParams, "conversationId");
347
- const requestTurnId = readString$1(requestParams, "turnId");
677
+ const requestThreadId = readString$2(requestParams, "threadId");
678
+ const requestTurnId = readString$2(requestParams, "turnId");
348
679
  return requestThreadId === threadId && requestTurnId === turnId;
349
680
  }
350
681
  function buildApprovalContext(params) {
351
- const itemId = readString$1(params.requestParams, "itemId") ?? readString$1(params.requestParams, "callId") ?? readString$1(params.requestParams, "approvalId");
682
+ const itemId = readString$2(params.requestParams, "itemId") ?? readString$2(params.requestParams, "callId") ?? readString$2(params.requestParams, "approvalId");
352
683
  const commandDetailLines = params.method === "item/commandExecution/requestApproval" ? describeCommandApprovalDetails(params.requestParams) : [];
353
684
  const commandPreview = sanitizeApprovalPreview(readDisplayCommandPreview(params.requestParams), commandDetailLines.length > 0 ? COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH : 180);
354
685
  const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
@@ -423,11 +754,11 @@ function toCodexAppServerApprovalCancellationError(reason) {
423
754
  return new Error(typeof reason === "string" && reason.trim() ? reason : "Codex app-server approval cancelled.");
424
755
  }
425
756
  async function buildAppServerExecAutoReviewInput(params) {
426
- const command = readString$1(params.requestParams, "command");
757
+ const command = readString$2(params.requestParams, "command");
427
758
  if (!command) return;
428
759
  return buildExecAutoReviewInputForShellCommand({
429
760
  command,
430
- cwd: readString$1(params.requestParams, "cwd") ?? params.paramsForRun.workspaceDir ?? null,
761
+ cwd: readString$2(params.requestParams, "cwd") ?? params.paramsForRun.workspaceDir ?? null,
431
762
  host: "codex-app-server",
432
763
  agent: {
433
764
  id: params.paramsForRun.agentId ?? null,
@@ -508,7 +839,7 @@ function readUnknownRecord(value) {
508
839
  async function runOpenClawToolPolicyForApprovalRequest(params) {
509
840
  const policyRequest = buildOpenClawToolPolicyRequest(params.method, params.requestParams);
510
841
  if (!policyRequest) return;
511
- const cwd = readString$1(params.requestParams, "cwd") ?? params.paramsForRun.workspaceDir;
842
+ const cwd = readString$2(params.requestParams, "cwd") ?? params.paramsForRun.workspaceDir;
512
843
  const nativeRelayOutcome = await runNativeRelayToolPolicyForApprovalRequest({
513
844
  method: params.method,
514
845
  requestParams: params.requestParams,
@@ -520,7 +851,8 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
520
851
  });
521
852
  if (nativeRelayOutcome?.blocked) return {
522
853
  outcome: "denied",
523
- reason: nativeRelayOutcome.reason
854
+ reason: nativeRelayOutcome.reason,
855
+ ...nativeRelayOutcome.failureDisposition ? { failureDisposition: nativeRelayOutcome.failureDisposition } : {}
524
856
  };
525
857
  if (nativeRelayOutcome?.outcome === "approved-once" || nativeRelayOutcome?.outcome === "approved-session") return { outcome: nativeRelayOutcome.outcome };
526
858
  if (nativeRelayOutcome?.handled) return { outcome: "no-decision" };
@@ -541,6 +873,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
541
873
  ...params.paramsForRun.agentId ? { agentId: params.paramsForRun.agentId } : {},
542
874
  ...params.paramsForRun.config ? { config: params.paramsForRun.config } : {},
543
875
  ...cwd ? { cwd } : {},
876
+ workspaceDir: params.paramsForRun.workspaceDir,
544
877
  ...params.paramsForRun.sessionKey ? { sessionKey: params.paramsForRun.sessionKey } : {},
545
878
  ...params.paramsForRun.sessionId ? { sessionId: params.paramsForRun.sessionId } : {},
546
879
  ...params.paramsForRun.runId ? { runId: params.paramsForRun.runId } : {},
@@ -549,7 +882,8 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
549
882
  });
550
883
  if (outcome.blocked) return {
551
884
  outcome: "denied",
552
- reason: outcome.reason
885
+ reason: outcome.reason,
886
+ ...outcome.kind === "failure" && outcome.disposition !== "blocked" ? { failureDisposition: outcome.disposition } : {}
553
887
  };
554
888
  if ("params" in outcome && toolPolicyParamsWereRewritten(policyRequest.params, outcome.params)) return {
555
889
  outcome: "denied",
@@ -579,7 +913,8 @@ async function runNativeRelayToolPolicyForApprovalRequest(params) {
579
913
  if (approvalOutcome?.outcome === "denied") return {
580
914
  handled: true,
581
915
  blocked: true,
582
- reason: approvalOutcome.reason
916
+ reason: approvalOutcome.reason,
917
+ ...approvalOutcome.failureDisposition ? { failureDisposition: approvalOutcome.failureDisposition } : {}
583
918
  };
584
919
  if (approvalOutcome?.outcome === "approved-once") return {
585
920
  handled: true,
@@ -599,7 +934,8 @@ async function runNativeRelayToolPolicyForApprovalRequest(params) {
599
934
  if (decision.blocked) return {
600
935
  handled: true,
601
936
  blocked: true,
602
- reason: decision.reason
937
+ reason: decision.reason,
938
+ ...decision.failureDisposition ? { failureDisposition: decision.failureDisposition } : {}
603
939
  };
604
940
  const approvalOutcome = await resolveNativeHookRelayDeferredToolApproval({
605
941
  relayId: params.nativeHookRelay.relayId,
@@ -609,7 +945,8 @@ async function runNativeRelayToolPolicyForApprovalRequest(params) {
609
945
  if (approvalOutcome?.outcome === "denied") return {
610
946
  handled: true,
611
947
  blocked: true,
612
- reason: approvalOutcome.reason
948
+ reason: approvalOutcome.reason,
949
+ ...approvalOutcome.failureDisposition ? { failureDisposition: approvalOutcome.failureDisposition } : {}
613
950
  };
614
951
  if (approvalOutcome?.outcome === "approved-once") return {
615
952
  handled: true,
@@ -620,14 +957,15 @@ async function runNativeRelayToolPolicyForApprovalRequest(params) {
620
957
  return {
621
958
  handled: true,
622
959
  blocked: true,
623
- reason: `OpenClaw native hook relay unavailable for Codex app-server approval: ${formatCodexDisplayText(formatErrorMessage$1(error))}`
960
+ reason: `OpenClaw native hook relay unavailable for Codex app-server approval: ${formatCodexDisplayText(formatErrorMessage$1(error))}`,
961
+ failureDisposition: "failed"
624
962
  };
625
963
  }
626
964
  }
627
965
  function buildNativeRelayPreToolUsePayload(params) {
628
- const command = readString$1(params.policyRequest.params, "command");
966
+ const command = readString$2(params.policyRequest.params, "command");
629
967
  if (!command) return;
630
- const turnId = readString$1(params.requestParams, "turnId");
968
+ const turnId = readString$2(params.requestParams, "turnId");
631
969
  return {
632
970
  hook_event_name: "PreToolUse",
633
971
  openclaw_approval_mode: "report",
@@ -645,7 +983,8 @@ function buildNativeRelayPreToolUsePayload(params) {
645
983
  function readNativeRelayPreToolUseDecision(response) {
646
984
  if (!response || response.exitCode !== 0) return {
647
985
  blocked: true,
648
- reason: sanitizeRelayDecisionReason(response?.stderr) || sanitizeRelayDecisionReason(response?.stdout) || "OpenClaw native hook relay failed for Codex app-server approval."
986
+ reason: sanitizeRelayDecisionReason(response?.stderr) || sanitizeRelayDecisionReason(response?.stdout) || "OpenClaw native hook relay failed for Codex app-server approval.",
987
+ failureDisposition: response?.failureDisposition ?? "failed"
649
988
  };
650
989
  const stdout = response.stdout?.trim();
651
990
  if (!stdout) return { blocked: false };
@@ -653,11 +992,13 @@ function readNativeRelayPreToolUseDecision(response) {
653
992
  const output = isJsonObject(parsed?.hookSpecificOutput) ? parsed.hookSpecificOutput : void 0;
654
993
  if (output?.permissionDecision === "deny") return {
655
994
  blocked: true,
656
- reason: readString$1(output, "permissionDecisionReason") || "OpenClaw native hook policy denied Codex app-server approval."
995
+ reason: readString$2(output, "permissionDecisionReason") || "OpenClaw native hook policy denied Codex app-server approval.",
996
+ ...response.failureDisposition ? { failureDisposition: response.failureDisposition } : {}
657
997
  };
658
998
  return {
659
999
  blocked: true,
660
- reason: output ? "OpenClaw native hook relay returned a non-deny Codex app-server approval decision." : "OpenClaw native hook relay returned an unreadable Codex app-server approval result."
1000
+ reason: output ? "OpenClaw native hook relay returned a non-deny Codex app-server approval decision." : "OpenClaw native hook relay returned an unreadable Codex app-server approval result.",
1001
+ failureDisposition: "failed"
661
1002
  };
662
1003
  }
663
1004
  function parseRelayJsonResponse(text) {
@@ -681,7 +1022,7 @@ function buildOpenClawToolPolicyRequest(method, requestParams) {
681
1022
  toolName: "exec",
682
1023
  params: {
683
1024
  ...command ? { command } : {},
684
- ...readString$1(requestParams, "cwd") ? { cwd: readString$1(requestParams, "cwd") } : {},
1025
+ ...readString$2(requestParams, "cwd") ? { cwd: readString$2(requestParams, "cwd") } : {},
685
1026
  approval: requestParams ?? {}
686
1027
  }
687
1028
  };
@@ -981,7 +1322,7 @@ function readPolicyCommand(record) {
981
1322
  function readCommandActions(record) {
982
1323
  const actions = record?.commandActions;
983
1324
  if (!Array.isArray(actions)) return [];
984
- return actions.map((action) => isJsonObject(action) ? readString$1(action, "command") : void 0).filter((command) => Boolean(command));
1325
+ return actions.map((action) => isJsonObject(action) ? readString$2(action, "command") : void 0).filter((command) => Boolean(command));
985
1326
  }
986
1327
  function readCommandActionsPreview(record) {
987
1328
  let source;
@@ -1004,19 +1345,19 @@ function readCommandPreview(record) {
1004
1345
  return source;
1005
1346
  }
1006
1347
  function readStringPreview(record, key) {
1007
- const value = readString$1(record, key);
1348
+ const value = readString$2(record, key);
1008
1349
  return value === void 0 ? void 0 : previewSource(value);
1009
1350
  }
1010
- function readString$1(record, key) {
1351
+ function readString$2(record, key) {
1011
1352
  const value = record?.[key];
1012
1353
  return typeof value === "string" ? value : void 0;
1013
1354
  }
1014
1355
  function truncate(value, maxLength) {
1015
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
1356
+ return value.length <= maxLength ? value : `${truncateUtf16Safe(value, maxLength - 3)}...`;
1016
1357
  }
1017
1358
  function previewSource(value) {
1018
1359
  return {
1019
- value: value.slice(0, APPROVAL_PREVIEW_SCAN_MAX_LENGTH),
1360
+ value: sliceUtf16Safe(value, 0, APPROVAL_PREVIEW_SCAN_MAX_LENGTH),
1020
1361
  clipped: value.length > APPROVAL_PREVIEW_SCAN_MAX_LENGTH
1021
1362
  };
1022
1363
  }
@@ -1024,7 +1365,7 @@ function appendPreviewPart(source, part, separator) {
1024
1365
  const value = `${source?.value ? `${source.value}${separator}` : ""}${part}`;
1025
1366
  const clipped = source?.clipped === true || value.length > APPROVAL_PREVIEW_SCAN_MAX_LENGTH;
1026
1367
  return {
1027
- value: value.slice(0, APPROVAL_PREVIEW_SCAN_MAX_LENGTH),
1368
+ value: sliceUtf16Safe(value, 0, APPROVAL_PREVIEW_SCAN_MAX_LENGTH),
1028
1369
  clipped
1029
1370
  };
1030
1371
  }
@@ -1063,6 +1404,40 @@ function formatErrorMessage$1(error) {
1063
1404
  return error instanceof Error ? error.message : String(error);
1064
1405
  }
1065
1406
  //#endregion
1407
+ //#region extensions/codex/src/app-server/session-history.ts
1408
+ /**
1409
+ * Reads OpenClaw session history for Codex transcript mirroring and sanitizes
1410
+ * image payloads before replaying messages into the app-server projector.
1411
+ */
1412
+ function isMissingFileError(error) {
1413
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
1414
+ }
1415
+ /** Returns sanitized session-context messages for a Codex mirrored session file. */
1416
+ async function readCodexMirroredSessionHistoryMessages(target) {
1417
+ try {
1418
+ await resolveSessionTranscriptTarget(resolveCodexHistoryTranscriptTarget(target));
1419
+ const entries = parseSessionEntries(await fs.readFile(target.sessionFile, "utf-8"));
1420
+ if (entries.length === 0) return [];
1421
+ const firstEntry = entries[0];
1422
+ if (firstEntry?.type !== "session" || typeof firstEntry.id !== "string") return;
1423
+ migrateSessionEntries(entries);
1424
+ return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => {
1425
+ return entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.type !== "session";
1426
+ })).messages, "codex mirrored history");
1427
+ } catch (error) {
1428
+ if (isMissingFileError(error)) return [];
1429
+ return;
1430
+ }
1431
+ }
1432
+ function resolveCodexHistoryTranscriptTarget(target) {
1433
+ return {
1434
+ ...target.agentId ? { agentId: target.agentId } : {},
1435
+ sessionFile: target.sessionFile,
1436
+ sessionId: target.sessionId,
1437
+ sessionKey: target.sessionKey ?? ""
1438
+ };
1439
+ }
1440
+ //#endregion
1066
1441
  //#region extensions/codex/src/app-server/vision-tools.ts
1067
1442
  /**
1068
1443
  * Filters Codex dynamic tools for turns that already contain image inputs so
@@ -1088,10 +1463,10 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
1088
1463
  "edit",
1089
1464
  "apply_patch"
1090
1465
  ];
1091
- const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = new Set(["read", "write"]);
1466
+ const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = /* @__PURE__ */ new Set(["read", "write"]);
1092
1467
  const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
1093
1468
  const CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME = "node_process";
1094
- const CODEX_NODE_EXEC_HIDDEN_PARAMETER_NAMES = new Set([
1469
+ const CODEX_NODE_EXEC_HIDDEN_PARAMETER_NAMES = /* @__PURE__ */ new Set([
1095
1470
  "host",
1096
1471
  "security",
1097
1472
  "ask",
@@ -1187,6 +1562,7 @@ async function buildDynamicTools(input) {
1187
1562
  const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
1188
1563
  const allTools = createOpenClawCodingTools({
1189
1564
  agentId: input.sessionAgentId,
1565
+ ...params.crestodianTool ? { crestodianTool: params.crestodianTool } : {},
1190
1566
  ...buildEmbeddedAttemptToolRunContext(params),
1191
1567
  exec: {
1192
1568
  ...params.execOverrides,
@@ -1208,6 +1584,7 @@ async function buildDynamicTools(input) {
1208
1584
  senderName: params.senderName,
1209
1585
  senderUsername: params.senderUsername,
1210
1586
  senderE164: params.senderE164,
1587
+ senderIsOwner: params.senderIsOwner,
1211
1588
  allowGatewaySubagentBinding: params.allowGatewaySubagentBinding || isForcedPrivateQaCodexRuntime(),
1212
1589
  ...sessionKeys,
1213
1590
  sessionId: params.sessionId,
@@ -1444,7 +1821,7 @@ function codexNetworkAccessForOpenClawSandbox(sandbox) {
1444
1821
  const network = sandbox?.docker?.network?.trim().toLowerCase();
1445
1822
  return Boolean(network && network !== "none");
1446
1823
  }
1447
- /** Returns a Codex config copy with app-server Codex plugin loading disabled for thread tools. */
1824
+ /** Returns a Codex config copy with all app exposure disabled for restricted thread tools. */
1448
1825
  function disableCodexPluginThreadConfig(pluginConfig) {
1449
1826
  const config = readCodexPluginConfig(pluginConfig);
1450
1827
  return {
@@ -1612,6 +1989,7 @@ function shouldForceMessageTool(params) {
1612
1989
  function emitDynamicToolStartedDiagnostic(params) {
1613
1990
  emitTrustedDiagnosticEvent({
1614
1991
  type: "tool.execution.started",
1992
+ agentId: params.agentId,
1615
1993
  runId: params.runId,
1616
1994
  sessionId: params.sessionId,
1617
1995
  sessionKey: params.sessionKey,
@@ -1623,13 +2001,15 @@ function emitDynamicToolStartedDiagnostic(params) {
1623
2001
  function emitDynamicToolErrorDiagnostic(params) {
1624
2002
  emitTrustedDiagnosticEvent({
1625
2003
  type: "tool.execution.error",
2004
+ agentId: params.agentId,
1626
2005
  runId: params.runId,
1627
2006
  sessionId: params.sessionId,
1628
2007
  sessionKey: params.sessionKey,
1629
2008
  toolName: params.call.tool,
1630
2009
  toolCallId: params.call.callId,
1631
2010
  durationMs: params.durationMs,
1632
- errorCategory: "codex_dynamic_tool_error"
2011
+ errorCategory: "codex_dynamic_tool_error",
2012
+ terminalReason: params.terminalReason ?? "failed"
1633
2013
  });
1634
2014
  }
1635
2015
  /** Emits the terminal event matching a dynamic tool response's diagnostic type. */
@@ -1638,6 +2018,7 @@ function emitDynamicToolTerminalDiagnostic(params) {
1638
2018
  if (terminalType === "completed") {
1639
2019
  emitTrustedDiagnosticEvent({
1640
2020
  type: "tool.execution.completed",
2021
+ agentId: params.agentId,
1641
2022
  runId: params.runId,
1642
2023
  sessionId: params.sessionId,
1643
2024
  sessionKey: params.sessionKey,
@@ -1650,6 +2031,7 @@ function emitDynamicToolTerminalDiagnostic(params) {
1650
2031
  if (terminalType === "blocked") {
1651
2032
  emitTrustedDiagnosticEvent({
1652
2033
  type: "tool.execution.blocked",
2034
+ agentId: params.agentId,
1653
2035
  runId: params.runId,
1654
2036
  sessionId: params.sessionId,
1655
2037
  sessionKey: params.sessionKey,
@@ -1660,7 +2042,10 @@ function emitDynamicToolTerminalDiagnostic(params) {
1660
2042
  });
1661
2043
  return;
1662
2044
  }
1663
- emitDynamicToolErrorDiagnostic(params);
2045
+ emitDynamicToolErrorDiagnostic({
2046
+ ...params,
2047
+ terminalReason: params.response.diagnosticTerminalReason ?? "failed"
2048
+ });
1664
2049
  }
1665
2050
  //#endregion
1666
2051
  //#region extensions/codex/src/app-server/dynamic-tools.ts
@@ -1784,7 +2169,11 @@ function hasExplicitNonSourceMessageRoute(args, hookContext, messagingTarget) {
1784
2169
  }
1785
2170
  /** Namespace attached to OpenClaw-owned dynamic tools exposed to Codex. */
1786
2171
  const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
1787
- const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = new Set(["sessions_yield"]);
2172
+ const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
2173
+ "agents_list",
2174
+ "sessions_spawn",
2175
+ "sessions_yield"
2176
+ ]);
1788
2177
  const EXPLICIT_MESSAGE_PROVIDER_KEYS = ["channel", "provider"];
1789
2178
  const EXPLICIT_MESSAGE_TARGET_KEYS = [
1790
2179
  "target",
@@ -1846,7 +2235,7 @@ function createCodexDynamicToolBridge(params) {
1846
2235
  return getChannelAgentToolMeta(tool) === void 0;
1847
2236
  };
1848
2237
  const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
1849
- const directToolNames = new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
2238
+ const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
1850
2239
  return {
1851
2240
  availableSpecs: createCodexDynamicToolSpecs({
1852
2241
  entries: availableTools,
@@ -1914,6 +2303,7 @@ function createCodexDynamicToolBridge(params) {
1914
2303
  executionPrevented = consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
1915
2304
  const telemetryRawResult = sanitizeToolResult(rawResult);
1916
2305
  const rawIsError = isCodexToolResultError(rawResult);
2306
+ const rawResultFailureKind = resolveToolResultFailureKind(rawResult);
1917
2307
  const middlewareResult = await middlewareRunner.applyToolResultMiddleware({
1918
2308
  threadId: call.threadId,
1919
2309
  turnId: call.turnId,
@@ -1932,7 +2322,16 @@ function createCodexDynamicToolBridge(params) {
1932
2322
  result: middlewareResult
1933
2323
  });
1934
2324
  const resultIsError = rawIsError || isCodexToolResultError(result);
1935
- notifyAgentToolResult(options?.onAgentToolResult, toolName, result, resultIsError);
2325
+ const finalResultFailureKind = resolveToolResultFailureKind(result);
2326
+ const resultFailureKind = rawResultFailureKind ?? finalResultFailureKind;
2327
+ const observerResult = rawResultFailureKind && finalResultFailureKind !== rawResultFailureKind ? {
2328
+ ...result,
2329
+ details: {
2330
+ ...isRecord(result.details) ? result.details : {},
2331
+ status: rawResultFailureKind
2332
+ }
2333
+ } : result;
2334
+ notifyAgentToolResult(options?.onAgentToolResult, toolName, observerResult, resultIsError);
1936
2335
  runAgentHarnessAfterToolCallHook({
1937
2336
  toolName,
1938
2337
  toolCallId: call.callId,
@@ -1966,11 +2365,12 @@ function createCodexDynamicToolBridge(params) {
1966
2365
  isError: resultIsError,
1967
2366
  messagingTarget: confirmedMessagingTarget
1968
2367
  });
1969
- const terminalType = inferToolResultDiagnosticTerminalType(result, resultIsError);
2368
+ const terminalType = resultFailureKind === "blocked" ? "blocked" : resultIsError ? "error" : "completed";
1970
2369
  const response = withDiagnosticTerminalType({
1971
2370
  contentItems: convertToolContents(result.content, toolResultMaxChars),
1972
2371
  success: !resultIsError
1973
2372
  }, terminalType);
2373
+ withDiagnosticFailureDisposition(response, resultFailureKind);
1974
2374
  const blocksSourceReplyTermination = hasExplicitNonSourceMessageRoute(executedArgs, params.hookContext, confirmedMessagingTarget);
1975
2375
  const deliveredSourceReply = isDeliveredMessageToolOnlySourceReplyResult({
1976
2376
  sourceReplyDeliveryMode: params.hookContext?.sourceReplyDeliveryMode,
@@ -1995,11 +2395,12 @@ function createCodexDynamicToolBridge(params) {
1995
2395
  withDynamicToolAsyncStarted(response, asyncStarted);
1996
2396
  return withSideEffectEvidence(response, !(executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs)));
1997
2397
  } catch (error) {
1998
- const errorMessage = error instanceof Error ? error.message : String(error);
2398
+ const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
2399
+ const errorMessage = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
1999
2400
  const adjustedExecutedArgs = consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
2000
2401
  if (isRecord(adjustedExecutedArgs)) executedArgs = structuredClone(adjustedExecutedArgs);
2001
2402
  executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
2002
- const failedResult = failedToolResult(errorMessage);
2403
+ const failedResult = failedToolResult(errorMessage, executionDisposition);
2003
2404
  finalizeToolTerminalPresentation({
2004
2405
  toolCallId: call.callId,
2005
2406
  runId: toolResultHookContext.runId,
@@ -2030,13 +2431,13 @@ function createCodexDynamicToolBridge(params) {
2030
2431
  startedAt
2031
2432
  });
2032
2433
  const replaySafe = !didStartExecution || executionPrevented || isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
2033
- return withSideEffectEvidence(withDiagnosticTerminalType({
2434
+ return withSideEffectEvidence(withDiagnosticFailureDisposition({
2034
2435
  contentItems: [{
2035
2436
  type: "inputText",
2036
2437
  text: errorMessage
2037
2438
  }],
2038
2439
  success: false
2039
- }, "error"), didStartExecution && !replaySafe);
2440
+ }, executionDisposition), didStartExecution && !replaySafe);
2040
2441
  }
2041
2442
  }
2042
2443
  };
@@ -2052,14 +2453,14 @@ function notifyAgentToolResult(observer, toolName, result, isError) {
2052
2453
  embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${toolName} error=${String(error)}`);
2053
2454
  }
2054
2455
  }
2055
- function failedToolResult(message) {
2456
+ function failedToolResult(message, status = "failed") {
2056
2457
  return {
2057
2458
  content: [{
2058
2459
  type: "text",
2059
2460
  text: message
2060
2461
  }],
2061
2462
  details: {
2062
- status: "failed",
2463
+ status,
2063
2464
  error: message
2064
2465
  }
2065
2466
  };
@@ -2234,6 +2635,7 @@ function warnQuarantinedDynamicTools(tools) {
2234
2635
  function emitQuarantinedDynamicToolDiagnostics(tools, ctx) {
2235
2636
  for (const tool of tools) emitTrustedDiagnosticEvent({
2236
2637
  type: "tool.execution.blocked",
2638
+ agentId: ctx?.agentId,
2237
2639
  runId: ctx?.runId,
2238
2640
  sessionId: ctx?.sessionId,
2239
2641
  sessionKey: ctx?.sessionKey,
@@ -2390,13 +2792,6 @@ function isAsyncStartedToolResult(result) {
2390
2792
  const details = result.details;
2391
2793
  return isRecord(details) && details.async === true && details.status === "started";
2392
2794
  }
2393
- function inferToolResultDiagnosticTerminalType(result, isError) {
2394
- const details = result.details;
2395
- if (isRecord(details) && typeof details.status === "string") {
2396
- if (details.status.trim().toLowerCase() === "blocked") return "blocked";
2397
- }
2398
- return isError ? "error" : "completed";
2399
- }
2400
2795
  function withDiagnosticTerminalType(response, terminalType) {
2401
2796
  Object.defineProperty(response, "diagnosticTerminalType", {
2402
2797
  configurable: true,
@@ -2405,6 +2800,16 @@ function withDiagnosticTerminalType(response, terminalType) {
2405
2800
  });
2406
2801
  return response;
2407
2802
  }
2803
+ function withDiagnosticFailureDisposition(response, disposition) {
2804
+ if (!disposition) return response;
2805
+ withDiagnosticTerminalType(response, disposition === "blocked" ? "blocked" : "error");
2806
+ if (disposition !== "blocked") Object.defineProperty(response, "diagnosticTerminalReason", {
2807
+ configurable: true,
2808
+ enumerable: false,
2809
+ value: disposition
2810
+ });
2811
+ return response;
2812
+ }
2408
2813
  function withSideEffectEvidence(response, sideEffectEvidence) {
2409
2814
  if (!sideEffectEvidence) return response;
2410
2815
  Object.defineProperty(response, "sideEffectEvidence", {
@@ -2641,7 +3046,7 @@ async function handleCodexAppServerElicitationRequest(params) {
2641
3046
  }
2642
3047
  function matchesCurrentThread(requestParams, threadId) {
2643
3048
  if (!requestParams) return false;
2644
- return readString(requestParams, "threadId") === threadId;
3049
+ return readString$1(requestParams, "threadId") === threadId;
2645
3050
  }
2646
3051
  function turnIdMismatches(requestParams, turnId) {
2647
3052
  const rawTurnId = requestParams?.turnId;
@@ -2656,6 +3061,7 @@ function resolvePluginElicitation(params) {
2656
3061
  const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
2657
3062
  const context = params.pluginAppPolicyContext;
2658
3063
  const entries = context ? Object.values(context.apps) : [];
3064
+ const pluginEntries = entries.filter(isPluginAppPolicyContextEntry);
2659
3065
  const appId = readFirstString(meta, PLUGIN_APP_ID_META_KEYS) ?? readFirstString(requestParams, PLUGIN_APP_ID_META_KEYS);
2660
3066
  const connectorId = readFirstString(meta, PLUGIN_CONNECTOR_ID_META_KEYS);
2661
3067
  const isCodexConnectorApproval = isCodexConnectorApprovalElicitation(requestParams, meta);
@@ -2669,6 +3075,10 @@ function resolvePluginElicitation(params) {
2669
3075
  reason: "missing_policy_context"
2670
3076
  };
2671
3077
  const entry = context.apps[appId];
3078
+ if (entry?.source === "account" && !isCodexConnectorApproval) return {
3079
+ kind: "decline",
3080
+ reason: "account_app_source_mismatch"
3081
+ };
2672
3082
  return uniquePluginMatch(entry ? [entry] : [], "app_id");
2673
3083
  }
2674
3084
  if (isCodexConnectorApproval && connectorId) {
@@ -2679,7 +3089,7 @@ function resolvePluginElicitation(params) {
2679
3089
  const entry = context.apps[connectorId];
2680
3090
  return uniquePluginMatch(entry ? [entry] : [], "connector_id");
2681
3091
  }
2682
- const serverName = readString(requestParams, "serverName");
3092
+ const serverName = readString$1(requestParams, "serverName");
2683
3093
  if (serverName && context) {
2684
3094
  const matches = entries.filter((entry) => entry.mcpServerNames.includes(serverName));
2685
3095
  if (matches.length > 0) return uniquePluginMatch(matches, "server_name");
@@ -2687,7 +3097,7 @@ function resolvePluginElicitation(params) {
2687
3097
  const metadataResolution = resolvePluginStableMetadataMatch({
2688
3098
  meta,
2689
3099
  requestParams,
2690
- entries,
3100
+ entries: pluginEntries,
2691
3101
  context
2692
3102
  });
2693
3103
  if (metadataResolution.kind !== "not_plugin") return metadataResolution;
@@ -2698,7 +3108,7 @@ function resolvePluginElicitation(params) {
2698
3108
  return { kind: "not_plugin" };
2699
3109
  }
2700
3110
  function isCodexConnectorApprovalElicitation(requestParams, meta) {
2701
- return readString(requestParams, "serverName") === CODEX_APPS_SERVER_NAME && readString(meta, MCP_TOOL_APPROVAL_KIND_KEY) === MCP_TOOL_APPROVAL_KIND && readString(meta, MCP_TOOL_APPROVAL_SOURCE_KEY) === MCP_TOOL_APPROVAL_CONNECTOR_SOURCE;
3111
+ return readString$1(requestParams, "serverName") === CODEX_APPS_SERVER_NAME && readString$1(meta, MCP_TOOL_APPROVAL_KIND_KEY) === MCP_TOOL_APPROVAL_KIND && readString$1(meta, MCP_TOOL_APPROVAL_SOURCE_KEY) === MCP_TOOL_APPROVAL_CONNECTOR_SOURCE;
2702
3112
  }
2703
3113
  function resolvePluginStableMetadataMatch(params) {
2704
3114
  const pluginName = readFirstString(params.meta, PLUGIN_NAME_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_NAME_META_KEYS);
@@ -2727,10 +3137,16 @@ function uniquePluginMatch(matches, source) {
2727
3137
  };
2728
3138
  }
2729
3139
  function hasDisplayNameOnlyPluginMatch(meta, entries) {
2730
- const connectorName = readString(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
3140
+ const connectorName = readString$1(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
2731
3141
  if (!connectorName) return false;
2732
3142
  const normalized = normalizePluginIdentityText(connectorName);
2733
- return entries.some((entry) => normalizePluginIdentityText(entry.pluginName) === normalized || normalizePluginIdentityText(entry.configKey) === normalized);
3143
+ return entries.some((entry) => normalizePluginIdentityText(appPolicyDisplayName(entry)) === normalized || isPluginAppPolicyContextEntry(entry) && normalizePluginIdentityText(entry.configKey) === normalized);
3144
+ }
3145
+ function isPluginAppPolicyContextEntry(entry) {
3146
+ return entry.source !== "account";
3147
+ }
3148
+ function appPolicyDisplayName(entry) {
3149
+ return isPluginAppPolicyContextEntry(entry) ? entry.pluginName : entry.appName;
2734
3150
  }
2735
3151
  function normalizePluginIdentityText(value) {
2736
3152
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
@@ -2772,20 +3188,20 @@ function oneShotPluginPolicyApprovalOutcome(mode, outcome) {
2772
3188
  return mode === "ask" && outcome === "approved-session" ? "approved-once" : outcome;
2773
3189
  }
2774
3190
  function readPluginApprovalElicitation(entry, requestParams) {
2775
- if (readString(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) return;
3191
+ if (readString$1(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) return;
2776
3192
  const requestedSchema = requestParams.requestedSchema;
2777
- if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
3193
+ if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
2778
3194
  const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
2779
- const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || "Codex plugin approval";
3195
+ const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || "Codex plugin approval";
2780
3196
  const descriptionMeta = { ...meta };
2781
- if (!readString(descriptionMeta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY)) descriptionMeta[MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY] = entry.pluginName;
3197
+ if (!readString$1(descriptionMeta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY)) descriptionMeta[MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY] = appPolicyDisplayName(entry);
2782
3198
  return {
2783
3199
  title,
2784
3200
  description: buildApprovalDescription({
2785
3201
  title,
2786
3202
  meta: descriptionMeta,
2787
3203
  requestedSchema,
2788
- serverName: sanitizeOptionalDisplayText(readString(requestParams, "serverName"))
3204
+ serverName: sanitizeOptionalDisplayText(readString$1(requestParams, "serverName"))
2789
3205
  }),
2790
3206
  requestedSchema,
2791
3207
  meta,
@@ -2824,34 +3240,34 @@ function declineElicitationResponse() {
2824
3240
  function logPluginElicitationDecline(reason, requestParams) {
2825
3241
  embeddedAgentLog.debug("codex plugin elicitation declined", {
2826
3242
  reason,
2827
- serverName: readString(requestParams, "serverName"),
2828
- mode: readString(requestParams, "mode")
3243
+ serverName: readString$1(requestParams, "serverName"),
3244
+ mode: readString$1(requestParams, "mode")
2829
3245
  });
2830
3246
  }
2831
3247
  function readBridgeableApprovalElicitation(requestParams) {
2832
- if (!requestParams || readString(requestParams, "mode") !== "form" || !isJsonObject(requestParams["_meta"]) || requestParams["_meta"][MCP_TOOL_APPROVAL_KIND_KEY] !== MCP_TOOL_APPROVAL_KIND || !isJsonObject(requestParams.requestedSchema)) return;
3248
+ if (!requestParams || readString$1(requestParams, "mode") !== "form" || !isJsonObject(requestParams["_meta"]) || requestParams["_meta"][MCP_TOOL_APPROVAL_KIND_KEY] !== MCP_TOOL_APPROVAL_KIND || !isJsonObject(requestParams.requestedSchema)) return;
2833
3249
  const requestedSchema = requestParams.requestedSchema;
2834
- if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
2835
- const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || "Codex MCP tool approval";
3250
+ if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
3251
+ const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || "Codex MCP tool approval";
2836
3252
  return {
2837
3253
  title,
2838
3254
  description: buildApprovalDescription({
2839
3255
  title,
2840
3256
  meta: requestParams["_meta"],
2841
3257
  requestedSchema,
2842
- serverName: sanitizeOptionalDisplayText(readString(requestParams, "serverName"))
3258
+ serverName: sanitizeOptionalDisplayText(readString$1(requestParams, "serverName"))
2843
3259
  }),
2844
3260
  requestedSchema,
2845
3261
  meta: requestParams["_meta"]
2846
3262
  };
2847
3263
  }
2848
3264
  function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
2849
- const serverName = readString(requestParams, "serverName");
2850
- if (!serverName || !expectedServerName || serverName !== expectedServerName || readString(requestParams, "mode") !== "form") return;
3265
+ const serverName = readString$1(requestParams, "serverName");
3266
+ if (!serverName || !expectedServerName || serverName !== expectedServerName || readString$1(requestParams, "mode") !== "form") return;
2851
3267
  const requestedSchema = isJsonObject(requestParams?.requestedSchema) ? requestParams.requestedSchema : EMPTY_OBJECT_SCHEMA;
2852
- if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
3268
+ if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
2853
3269
  const meta = isJsonObject(requestParams?.["_meta"]) ? requestParams["_meta"] : {};
2854
- const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || COMPUTER_USE_APPROVAL_TITLE;
3270
+ const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || COMPUTER_USE_APPROVAL_TITLE;
2855
3271
  return {
2856
3272
  title,
2857
3273
  description: buildApprovalDescription({
@@ -2865,9 +3281,9 @@ function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
2865
3281
  };
2866
3282
  }
2867
3283
  function buildApprovalDescription(params) {
2868
- const connectorName = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY));
2869
- const toolTitle = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY));
2870
- const toolDescription = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY));
3284
+ const connectorName = sanitizeOptionalDisplayText(readString$1(params.meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY));
3285
+ const toolTitle = sanitizeOptionalDisplayText(readString$1(params.meta, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY));
3286
+ const toolDescription = sanitizeOptionalDisplayText(readString$1(params.meta, MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY));
2871
3287
  const summaryLines = [
2872
3288
  connectorName && `App: ${connectorName}`,
2873
3289
  toolTitle && `Tool: ${toolTitle}`,
@@ -2888,8 +3304,8 @@ function readPropertyDescriptionLines(requestedSchema) {
2888
3304
  return Object.entries(properties).map(([name, value]) => {
2889
3305
  const schema = isJsonObject(value) ? value : void 0;
2890
3306
  if (!schema) return;
2891
- const propTitle = sanitizeDisplayText(readString(schema, "title") ?? "") || sanitizeDisplayText(name) || "field";
2892
- const description = sanitizeOptionalDisplayText(readString(schema, "description"));
3307
+ const propTitle = sanitizeDisplayText(readString$1(schema, "title") ?? "") || sanitizeDisplayText(name) || "field";
3308
+ const description = sanitizeOptionalDisplayText(readString$1(schema, "description"));
2893
3309
  return description ? `- ${propTitle}: ${description}` : `- ${propTitle}`;
2894
3310
  }).filter((line) => Boolean(line));
2895
3311
  }
@@ -2899,7 +3315,7 @@ function readDisplayParamLines(meta) {
2899
3315
  const lines = displayParams.slice(0, MAX_DISPLAY_PARAM_ENTRIES).map((entry) => {
2900
3316
  const param = isJsonObject(entry) ? entry : void 0;
2901
3317
  if (!param) return;
2902
- const name = sanitizeOptionalDisplayText(readString(param, "display_name")) ?? sanitizeOptionalDisplayText(readString(param, "name"));
3318
+ const name = sanitizeOptionalDisplayText(readString$1(param, "display_name")) ?? sanitizeOptionalDisplayText(readString$1(param, "name"));
2903
3319
  if (!name) return;
2904
3320
  return `- ${name}: ${formatDisplayParamValue(param.value)}`;
2905
3321
  }).filter((line) => Boolean(line));
@@ -2945,14 +3361,14 @@ function sanitizeOptionalDisplayText(value) {
2945
3361
  return (value === void 0 ? "" : sanitizeDisplayText(value)) || void 0;
2946
3362
  }
2947
3363
  function sanitizeDisplayText(value) {
2948
- const scanned = value.slice(0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
3364
+ const scanned = sliceUtf16Safe(value, 0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
2949
3365
  const clipped = value.length > DISPLAY_TEXT_SCAN_MAX_LENGTH;
2950
3366
  const sanitized = scanned.replace(ANSI_OSC_SEQUENCE_RE, "").replace(ANSI_CONTROL_SEQUENCE_RE, "").replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE, "").replace(INVISIBLE_FORMATTING_CONTROL_RE, " ").replace(CONTROL_CHARACTER_RE, " ").replace(/\s+/g, " ").trim();
2951
3367
  const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
2952
3368
  return clipped && escaped ? `${escaped}...` : escaped;
2953
3369
  }
2954
3370
  function truncateDisplayText(value, maxLength) {
2955
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
3371
+ return value.length <= maxLength ? value : `${truncateUtf16Safe(value, maxLength - 3)}...`;
2956
3372
  }
2957
3373
  async function requestPluginApprovalOutcome(params) {
2958
3374
  try {
@@ -3038,7 +3454,7 @@ function buildAcceptedContent(approvalPrompt, outcome) {
3038
3454
  }
3039
3455
  function readApprovalFieldValue(property, outcome) {
3040
3456
  if (!isApprovalField(property)) return;
3041
- if (readString(property.schema, "type") === "boolean") return true;
3457
+ if (readString$1(property.schema, "type") === "boolean") return true;
3042
3458
  const options = readEnumOptions(property.schema);
3043
3459
  if (options.length === 0) return;
3044
3460
  const sessionChoice = options.find((option) => isSessionApprovalOption(option));
@@ -3073,8 +3489,8 @@ function isPersistField(property) {
3073
3489
  function propertyText(property) {
3074
3490
  return [
3075
3491
  property.name,
3076
- readString(property.schema, "title"),
3077
- readString(property.schema, "description")
3492
+ readString$1(property.schema, "title"),
3493
+ readString$1(property.schema, "description")
3078
3494
  ].filter(Boolean).join(" ");
3079
3495
  }
3080
3496
  function readPersistHints(meta, mode = "legacy") {
@@ -3113,11 +3529,11 @@ function readEnumOptions(schema) {
3113
3529
  }
3114
3530
  if (Array.isArray(schema.oneOf)) return schema.oneOf.map((entry) => {
3115
3531
  const option = isJsonObject(entry) ? entry : void 0;
3116
- const value = readString(option, "const");
3532
+ const value = readString$1(option, "const");
3117
3533
  if (!value) return;
3118
3534
  return {
3119
3535
  value,
3120
- label: readString(option, "title") ?? value
3536
+ label: readString$1(option, "title") ?? value
3121
3537
  };
3122
3538
  }).filter((entry) => Boolean(entry));
3123
3539
  return [];
@@ -3130,17 +3546,37 @@ function isSessionApprovalOption(option) {
3130
3546
  const haystack = `${option.value} ${option.label}`.toLowerCase();
3131
3547
  return /\b(session|always|persistent)\b/.test(haystack) && /\b(allow|approve|accept)\b/.test(haystack);
3132
3548
  }
3133
- function readString(record, key) {
3549
+ function readString$1(record, key) {
3134
3550
  const value = record?.[key];
3135
3551
  return typeof value === "string" && value.trim() ? value : void 0;
3136
3552
  }
3137
3553
  function readFirstString(record, keys) {
3138
3554
  for (const key of keys) {
3139
- const value = readString(record, key);
3555
+ const value = readString$1(record, key);
3140
3556
  if (value) return value;
3141
3557
  }
3142
3558
  }
3143
3559
  //#endregion
3560
+ //#region extensions/codex/src/app-server/local-runtime-attribution.ts
3561
+ const OPENAI_PROVIDER_ID = "openai";
3562
+ const OPENAI_RESPONSES_API = "openai-responses";
3563
+ const OPENAI_CODEX_RESPONSES_API = "openai-chatgpt-responses";
3564
+ function normalizeRuntimeId(value) {
3565
+ return value?.trim().toLowerCase() ?? "";
3566
+ }
3567
+ /** Maps local Codex runtime plans onto the provider/api pair exposed to event projection. */
3568
+ function resolveCodexLocalRuntimeAttribution(params) {
3569
+ const authProfileProvider = normalizeRuntimeId(params.runtimePlan?.auth?.authProfileProviderForAuth);
3570
+ if (normalizeRuntimeId(params.runtimePlan?.observability.harnessId) === "codex" && authProfileProvider !== OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.provider) === OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.api) === OPENAI_RESPONSES_API) return {
3571
+ provider: OPENAI_PROVIDER_ID,
3572
+ api: OPENAI_CODEX_RESPONSES_API
3573
+ };
3574
+ return {
3575
+ provider: params.provider,
3576
+ api: params.model.api
3577
+ };
3578
+ }
3579
+ //#endregion
3144
3580
  //#region extensions/codex/src/app-server/native-hook-relay.ts
3145
3581
  /**
3146
3582
  * Bridges Codex native hook callbacks into OpenClaw's native hook relay so
@@ -3159,6 +3595,7 @@ const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
3159
3595
  const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
3160
3596
  const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
3161
3597
  const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
3598
+ const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
3162
3599
  const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
3163
3600
  const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
3164
3601
  const pendingCodexNativeHookRelayUnregisters = /* @__PURE__ */ new Set();
@@ -3182,9 +3619,25 @@ function scheduleCodexNativeHookRelayUnregister(params) {
3182
3619
  }
3183
3620
  /** Computes the delayed unregister window from Codex's hook timeout. */
3184
3621
  function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
3185
- const hookTimeoutMs = typeof hookTimeoutSec === "number" && Number.isFinite(hookTimeoutSec) && hookTimeoutSec > 0 ? finiteSecondsToTimerSafeMilliseconds(Math.ceil(hookTimeoutSec)) ?? 0 : 0;
3622
+ const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
3186
3623
  return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
3187
3624
  }
3625
+ /** Records a native pre-tool failure that Codex does not project as a tool item. */
3626
+ function emitCodexNativePreToolUseFailureDiagnostic(params) {
3627
+ emitTrustedDiagnosticEvent({
3628
+ type: "tool.execution.error",
3629
+ ...params.agentId ? { agentId: params.agentId } : {},
3630
+ sessionId: params.sessionId,
3631
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3632
+ runId: params.runId,
3633
+ toolName: params.failure.toolName,
3634
+ toolCallId: params.failure.toolCallId,
3635
+ durationMs: params.failure.durationMs,
3636
+ errorCategory: "before_tool_call",
3637
+ terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
3638
+ ...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
3639
+ });
3640
+ }
3188
3641
  /** Registers an OpenClaw native hook relay for a Codex app-server turn. */
3189
3642
  function createCodexNativeHookRelay(params) {
3190
3643
  if (params.options?.enabled === false) return;
@@ -3211,6 +3664,7 @@ function createCodexNativeHookRelay(params) {
3211
3664
  turnStartTimeoutMs: params.turnStartTimeoutMs
3212
3665
  }),
3213
3666
  signal: params.signal,
3667
+ onPreToolUseFailure: params.onPreToolUseFailure,
3214
3668
  command: {
3215
3669
  nice: 10,
3216
3670
  timeoutMs: params.options?.gatewayTimeoutMs
@@ -3300,7 +3754,7 @@ function buildCodexNativeHookRelayDisabledConfig() {
3300
3754
  };
3301
3755
  }
3302
3756
  function normalizeHookTimeoutSec(value) {
3303
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : 5;
3757
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
3304
3758
  }
3305
3759
  function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
3306
3760
  const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
@@ -3328,27 +3782,2379 @@ function sortJsonValue(value) {
3328
3782
  return sorted;
3329
3783
  }
3330
3784
  //#endregion
3331
- //#region extensions/codex/src/app-server/provider-capabilities.ts
3332
- async function readConfiguredProviderWebSearchSupport(params) {
3333
- return (await params.client.request("modelProvider/capabilities/read", {}, {
3334
- timeoutMs: params.timeoutMs,
3335
- signal: params.signal
3336
- })).webSearch ? "supported" : "unsupported";
3785
+ //#region extensions/codex/src/app-server/tool-progress-normalization.ts
3786
+ /**
3787
+ * Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
3788
+ * emitted into OpenClaw events or logs.
3789
+ */
3790
+ /** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
3791
+ function resolveCodexToolProgressDetailMode(value) {
3792
+ return value === "raw" ? "raw" : "explain";
3337
3793
  }
3338
- async function resolveCodexProviderWebSearchSupportForClient(params) {
3339
- const modelProviderOverride = params.modelProviderOverride?.trim().toLowerCase();
3340
- if (modelProviderOverride === "openai") return "supported";
3341
- if (modelProviderOverride) return "unsupported";
3794
+ /** Recursively redacts sensitive strings and handles circular values in event payloads. */
3795
+ function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
3796
+ if (typeof value === "string") return redactToolPayloadText(value);
3797
+ if (Array.isArray(value)) {
3798
+ if (seen.has(value)) return "[Circular]";
3799
+ seen.add(value);
3800
+ return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
3801
+ }
3802
+ if (value && typeof value === "object") {
3803
+ if (seen.has(value)) return "[Circular]";
3804
+ seen.add(value);
3805
+ const out = {};
3806
+ for (const [key, child] of Object.entries(value)) out[key] = typeof child === "string" ? redactSensitiveFieldValue(key, child) : sanitizeCodexAgentEventValue(child, seen);
3807
+ return out;
3808
+ }
3809
+ return value;
3810
+ }
3811
+ /** Sanitizes a record-shaped Codex agent event payload. */
3812
+ function sanitizeCodexAgentEventRecord(value) {
3813
+ return sanitizeCodexAgentEventValue(value);
3814
+ }
3815
+ /** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
3816
+ function sanitizeCodexToolArguments(value) {
3817
+ if (!isJsonObject(value)) return;
3818
+ return sanitizeCodexAgentEventRecord(value);
3819
+ }
3820
+ /** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
3821
+ function sanitizeCodexToolResponse(response) {
3822
+ return sanitizeCodexAgentEventRecord(response);
3823
+ }
3824
+ /** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
3825
+ function inferCodexDynamicToolMeta(call, detailMode) {
3826
+ return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
3827
+ }
3828
+ //#endregion
3829
+ //#region extensions/codex/src/app-server/transcript-mirror.ts
3830
+ const MIRROR_IDENTITY_META_KEY = "mirrorIdentity";
3831
+ const MIRROR_ORIGIN_META_KEY = "mirrorOrigin";
3832
+ const CODEX_APP_SERVER_MIRROR_ORIGIN = "codex-app-server";
3833
+ function attachCodexMirrorOrigin(message) {
3834
+ const record = message;
3835
+ const existing = record["__openclaw"];
3836
+ const baseMeta = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
3837
+ return {
3838
+ ...record,
3839
+ __openclaw: {
3840
+ ...baseMeta,
3841
+ [MIRROR_ORIGIN_META_KEY]: CODEX_APP_SERVER_MIRROR_ORIGIN
3842
+ }
3843
+ };
3844
+ }
3845
+ function buildSenderLabel(params) {
3846
+ const label = params.senderName ?? params.senderUsername ?? params.senderE164 ?? params.senderId;
3847
+ if (!label) return;
3848
+ if (!params.senderId || label.includes(params.senderId)) return label;
3849
+ return `${label} (${params.senderId})`;
3850
+ }
3851
+ function buildCodexUserPromptMessageFromPrepared(params, preparedUserMessage) {
3852
+ const senderId = normalizeOptionalString(params.senderId);
3853
+ const senderName = normalizeOptionalString(params.senderName);
3854
+ const senderUsername = normalizeOptionalString(params.senderUsername);
3855
+ const senderE164 = normalizeOptionalString(params.senderE164);
3856
+ const senderLabel = buildSenderLabel({
3857
+ senderId,
3858
+ senderName,
3859
+ senderUsername,
3860
+ senderE164
3861
+ });
3862
+ const sourceChannel = normalizeOptionalString(params.inputProvenance?.sourceChannel ?? params.messageChannel ?? params.messageProvider);
3863
+ if (preparedUserMessage) return {
3864
+ role: "user",
3865
+ timestamp: Date.now(),
3866
+ ...params.inputProvenance ? { provenance: params.inputProvenance } : {},
3867
+ ...sourceChannel ? { sourceChannel } : {},
3868
+ ...senderId ? { senderId } : {},
3869
+ ...senderName ? { senderName } : {},
3870
+ ...senderUsername ? { senderUsername } : {},
3871
+ ...senderE164 ? { senderE164 } : {},
3872
+ ...senderLabel ? { senderLabel } : {},
3873
+ ...preparedUserMessage
3874
+ };
3875
+ return {
3876
+ role: "user",
3877
+ content: params.prompt,
3878
+ timestamp: Date.now(),
3879
+ ...params.inputProvenance ? { provenance: params.inputProvenance } : {},
3880
+ ...sourceChannel ? { sourceChannel } : {},
3881
+ ...senderId ? { senderId } : {},
3882
+ ...senderName ? { senderName } : {},
3883
+ ...senderUsername ? { senderUsername } : {},
3884
+ ...senderE164 ? { senderE164 } : {},
3885
+ ...senderLabel ? { senderLabel } : {}
3886
+ };
3887
+ }
3888
+ function buildCodexUserPromptMessage(params) {
3889
+ return buildCodexUserPromptMessageFromPrepared(params, params.userTurnTranscriptRecorder?.message);
3890
+ }
3891
+ async function buildResolvedCodexUserPromptMessage(params) {
3892
+ return buildCodexUserPromptMessageFromPrepared(params, await params.userTurnTranscriptRecorder?.resolveMessage() ?? params.userTurnTranscriptRecorder?.message);
3893
+ }
3894
+ async function mirrorTranscriptBestEffort(params) {
3342
3895
  try {
3343
- return await readConfiguredProviderWebSearchSupport(params);
3344
- } catch {
3345
- return "unknown";
3896
+ const messages = await resolveFinalCodexMirrorMessages({
3897
+ params: params.params,
3898
+ messagesSnapshot: params.result.messagesSnapshot,
3899
+ turnId: params.turnId
3900
+ });
3901
+ const mirrorResult = await mirrorCodexAppServerTranscript({
3902
+ sessionFile: params.params.sessionFile,
3903
+ agentId: params.agentId,
3904
+ sessionKey: params.sessionKey,
3905
+ sessionId: params.params.sessionId,
3906
+ cwd: params.cwd,
3907
+ messages,
3908
+ idempotencyScope: `codex-app-server:${params.threadId}`,
3909
+ config: params.params.config
3910
+ });
3911
+ for (const message of mirrorResult.userMessagesPresent) try {
3912
+ params.notifyUserMessagePersisted(message);
3913
+ } catch (error) {
3914
+ embeddedAgentLog.warn("failed to notify codex app-server user-message persistence", { error: formatErrorMessage(error) });
3915
+ }
3916
+ return mirrorResult.assistantMirrorIdentitiesOwned.includes(`${params.turnId}:assistant`);
3917
+ } catch (error) {
3918
+ embeddedAgentLog.warn("failed to mirror codex app-server transcript", { error });
3919
+ return false;
3346
3920
  }
3347
3921
  }
3348
- async function resolveCodexProviderWebSearchSupport(params) {
3349
- let client;
3922
+ async function resolveFinalCodexMirrorMessages(params) {
3923
+ if (params.params.suppressNextUserMessagePersistence || !params.params.userTurnTranscriptRecorder) return params.messagesSnapshot;
3924
+ const resolvedPrompt = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
3925
+ const firstUserIndex = params.messagesSnapshot.findIndex((message) => message.role === "user");
3926
+ if (firstUserIndex === -1) return [resolvedPrompt, ...params.messagesSnapshot];
3927
+ const messages = params.messagesSnapshot.slice();
3928
+ messages[firstUserIndex] = resolvedPrompt;
3929
+ return messages;
3930
+ }
3931
+ function createCodexAppServerUserMessagePersistenceNotifier(runParams) {
3932
+ let notified = false;
3933
+ return (message) => {
3934
+ if (notified) return;
3935
+ notified = true;
3936
+ runParams.userTurnTranscriptRecorder?.markRuntimePersisted(message);
3937
+ try {
3938
+ runParams.onUserMessagePersisted?.(message);
3939
+ } catch (error) {
3940
+ embeddedAgentLog.warn("codex app-server user persistence notification failed", { error: formatErrorMessage(error) });
3941
+ }
3942
+ };
3943
+ }
3944
+ async function mirrorPromptAtTurnStartBestEffort(params) {
3945
+ if (params.params.suppressNextUserMessagePersistence) return;
3350
3946
  try {
3351
- client = await params.clientFactory(params.appServer.start, params.authProfileId, params.agentDir, params.config, { timeoutMs: params.appServer.requestTimeoutMs });
3947
+ const mirrorPromise = (async () => {
3948
+ const userPromptMessage = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
3949
+ const mirrorResult = await mirrorCodexAppServerTranscript({
3950
+ sessionFile: params.params.sessionFile,
3951
+ agentId: params.agentId,
3952
+ sessionKey: params.sessionKey,
3953
+ sessionId: params.params.sessionId,
3954
+ cwd: params.cwd,
3955
+ messages: [userPromptMessage],
3956
+ idempotencyScope: `codex-app-server:${params.threadId}`,
3957
+ config: params.params.config
3958
+ });
3959
+ for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
3960
+ })();
3961
+ params.params.userTurnTranscriptRecorder?.markRuntimePersistencePending(mirrorPromise);
3962
+ await mirrorPromise;
3963
+ } catch (error) {
3964
+ embeddedAgentLog.warn("failed to mirror codex app-server prompt at turn start", { error });
3965
+ }
3966
+ }
3967
+ /**
3968
+ * Tag a message with a stable logical identity for mirror dedupe. Callers
3969
+ * should use a value that is invariant for the same logical message across
3970
+ * re-emits (e.g. `${turnId}:prompt`, `${turnId}:assistant`) but distinct
3971
+ * for genuinely-distinct messages (different turns, different kinds). When
3972
+ * present this identity replaces the role/content fingerprint in the
3973
+ * idempotency key, so the dedupe survives caller-scope rotation without
3974
+ * collapsing distinct same-content turns.
3975
+ */
3976
+ function attachCodexMirrorIdentity(message, identity) {
3977
+ const record = message;
3978
+ const existing = record["__openclaw"];
3979
+ const baseMeta = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
3980
+ return {
3981
+ ...record,
3982
+ __openclaw: {
3983
+ ...baseMeta,
3984
+ [MIRROR_IDENTITY_META_KEY]: identity
3985
+ }
3986
+ };
3987
+ }
3988
+ function readMirrorIdentity(message) {
3989
+ const meta = message["__openclaw"];
3990
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return;
3991
+ const id = meta[MIRROR_IDENTITY_META_KEY];
3992
+ return typeof id === "string" && id.length > 0 ? id : void 0;
3993
+ }
3994
+ function fingerprintMirrorMessageContent(message) {
3995
+ const payload = JSON.stringify({
3996
+ role: message.role,
3997
+ content: message.content
3998
+ });
3999
+ return createHash("sha256").update(payload).digest("hex").slice(0, 16);
4000
+ }
4001
+ function buildMirrorDedupeIdentity(message) {
4002
+ const explicit = readMirrorIdentity(message);
4003
+ if (explicit) return explicit;
4004
+ return `${message.role}:${fingerprintMirrorMessageContent(message)}`;
4005
+ }
4006
+ async function mirrorCodexAppServerTranscript(params) {
4007
+ const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
4008
+ if (messages.length === 0) return {
4009
+ assistantMirrorIdentitiesOwned: [],
4010
+ userMessagesPresent: []
4011
+ };
4012
+ const transcriptTarget = resolveCodexMirrorTranscriptTarget(params);
4013
+ const { appendedUpdates, assistantMirrorIdentitiesOwned, userMessagesPresent } = await withSessionTranscriptWriteLock({
4014
+ ...transcriptTarget,
4015
+ config: params.config
4016
+ }, async (transcript) => {
4017
+ const nextAppendedUpdates = [];
4018
+ const nextAssistantMirrorIdentitiesOwned = /* @__PURE__ */ new Set();
4019
+ const nextUserMessagesPresent = [];
4020
+ const mirrorState = readTranscriptMirrorState(await transcript.readEvents());
4021
+ let nextMessageSeq = mirrorState.messageCount;
4022
+ for (const message of messages) {
4023
+ const dedupeIdentity = buildMirrorDedupeIdentity(message);
4024
+ const idempotencyKey = (message.role === "user" ? normalizeOptionalString(message.idempotencyKey) : void 0) ?? (params.idempotencyScope ? `${params.idempotencyScope}:${dedupeIdentity}` : void 0);
4025
+ const transcriptMessage = {
4026
+ ...attachCodexMirrorOrigin(message),
4027
+ ...idempotencyKey ? { idempotencyKey } : {}
4028
+ };
4029
+ if (idempotencyKey && mirrorState.idempotencyKeys.has(idempotencyKey)) {
4030
+ const persistedUserMessage = mirrorState.userMessagesByIdempotencyKey.get(idempotencyKey);
4031
+ if (persistedUserMessage) nextUserMessagesPresent.push(persistedUserMessage);
4032
+ if (message.role === "assistant") nextAssistantMirrorIdentitiesOwned.add(dedupeIdentity);
4033
+ continue;
4034
+ }
4035
+ const nextMessage = runAgentHarnessBeforeMessageWriteHook({
4036
+ message: transcriptMessage,
4037
+ agentId: params.agentId,
4038
+ sessionKey: params.sessionKey
4039
+ });
4040
+ if (!nextMessage) {
4041
+ if (message.role === "assistant") nextAssistantMirrorIdentitiesOwned.add(dedupeIdentity);
4042
+ continue;
4043
+ }
4044
+ const messageToAppend = idempotencyKey ? {
4045
+ ...attachCodexMirrorOrigin(nextMessage),
4046
+ idempotencyKey
4047
+ } : attachCodexMirrorOrigin(nextMessage);
4048
+ const appended = await transcript.appendMessage({
4049
+ message: messageToAppend,
4050
+ idempotencyLookup: idempotencyKey ? "caller-checked" : "scan",
4051
+ cwd: params.cwd
4052
+ });
4053
+ if (!appended) continue;
4054
+ const { messageId, message: appendedMessage } = appended;
4055
+ if (message.role === "assistant") nextAssistantMirrorIdentitiesOwned.add(dedupeIdentity);
4056
+ if (appendedMessage.role === "user") {
4057
+ nextUserMessagesPresent.push(appendedMessage);
4058
+ if (idempotencyKey) mirrorState.userMessagesByIdempotencyKey.set(idempotencyKey, appendedMessage);
4059
+ }
4060
+ nextMessageSeq += 1;
4061
+ nextAppendedUpdates.push({
4062
+ messageId,
4063
+ message: appendedMessage,
4064
+ messageSeq: nextMessageSeq
4065
+ });
4066
+ if (idempotencyKey) mirrorState.idempotencyKeys.add(idempotencyKey);
4067
+ }
4068
+ return {
4069
+ appendedUpdates: nextAppendedUpdates,
4070
+ assistantMirrorIdentitiesOwned: [...nextAssistantMirrorIdentitiesOwned],
4071
+ userMessagesPresent: nextUserMessagesPresent
4072
+ };
4073
+ });
4074
+ for (const update of appendedUpdates) try {
4075
+ await publishSessionTranscriptUpdateByIdentity({
4076
+ ...transcriptTarget,
4077
+ update: {
4078
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
4079
+ ...params.agentId ? { agentId: params.agentId } : {},
4080
+ message: update.message,
4081
+ messageId: update.messageId,
4082
+ messageSeq: update.messageSeq
4083
+ }
4084
+ });
4085
+ } catch (error) {
4086
+ embeddedAgentLog.warn("failed to publish codex app-server transcript update", { error: formatErrorMessage(error) });
4087
+ }
4088
+ return {
4089
+ assistantMirrorIdentitiesOwned,
4090
+ userMessagesPresent
4091
+ };
4092
+ }
4093
+ function resolveCodexMirrorTranscriptTarget(params) {
4094
+ return {
4095
+ ...params.agentId ? { agentId: params.agentId } : {},
4096
+ sessionFile: params.sessionFile,
4097
+ sessionId: params.sessionId,
4098
+ sessionKey: params.sessionKey ?? ""
4099
+ };
4100
+ }
4101
+ function readTranscriptMirrorState(events) {
4102
+ const idempotencyKeys = /* @__PURE__ */ new Set();
4103
+ const userMessagesByIdempotencyKey = /* @__PURE__ */ new Map();
4104
+ let messageCount = 0;
4105
+ for (const event of events) {
4106
+ if (!event || typeof event !== "object" || Array.isArray(event)) continue;
4107
+ const parsed = event;
4108
+ if (parsed.type === "message") messageCount += 1;
4109
+ if (typeof parsed.message?.idempotencyKey === "string") {
4110
+ idempotencyKeys.add(parsed.message.idempotencyKey);
4111
+ if (parsed.message.role === "user") userMessagesByIdempotencyKey.set(parsed.message.idempotencyKey, parsed.message);
4112
+ }
4113
+ }
4114
+ return {
4115
+ idempotencyKeys,
4116
+ messageCount,
4117
+ userMessagesByIdempotencyKey
4118
+ };
4119
+ }
4120
+ //#endregion
4121
+ //#region extensions/codex/src/app-server/event-projector.ts
4122
+ /** Projects metadata-only lifecycle diagnostics for native tool items. */
4123
+ var CodexNativeToolLifecycleProjector = class {
4124
+ constructor(context, threadId, turnId, options = {}) {
4125
+ this.context = context;
4126
+ this.threadId = threadId;
4127
+ this.turnId = turnId;
4128
+ this.options = options;
4129
+ this.startedAtByItem = /* @__PURE__ */ new Map();
4130
+ this.activeItems = /* @__PURE__ */ new Map();
4131
+ this.webSearchCompletionByItem = /* @__PURE__ */ new Map();
4132
+ this.completedItemIds = /* @__PURE__ */ new Set();
4133
+ this.approvalFailureDispositionByItem = /* @__PURE__ */ new Map();
4134
+ this.preToolUseFailureByItem = /* @__PURE__ */ new Map();
4135
+ this.finalized = false;
4136
+ }
4137
+ handleNotification(notification) {
4138
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
4139
+ if (!params || readCodexNotificationThreadId(params) !== this.threadId || readCodexNotificationTurnId(params) !== this.turnId) return;
4140
+ if (notification.method === "turn/completed") {
4141
+ const turn = readCodexTurn(params.turn);
4142
+ if (!turn || turn.id !== this.turnId) return;
4143
+ for (const item of turn.items ?? []) this.recordSnapshotItem(item);
4144
+ return;
4145
+ }
4146
+ if (notification.method === "rawResponseItem/completed") {
4147
+ const item = isJsonObject(params.item) ? params.item : void 0;
4148
+ if (item) this.recordRawWebSearchResult(item);
4149
+ return;
4150
+ }
4151
+ if (notification.method !== "item/started" && notification.method !== "item/completed") return;
4152
+ const item = readItem(params.item);
4153
+ if (!item) return;
4154
+ this.recordItem({
4155
+ phase: notification.method === "item/started" ? "start" : "result",
4156
+ item,
4157
+ sourceTimestampMs: asDateTimestampMs(notification.method === "item/started" ? params.startedAtMs : params.completedAtMs)
4158
+ });
4159
+ }
4160
+ recordItem(params) {
4161
+ const toolName = auditNativeToolName(params.item);
4162
+ if (!toolName || this.completedItemIds.has(params.item.id)) return;
4163
+ if (params.phase === "start") {
4164
+ this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs);
4165
+ return;
4166
+ }
4167
+ if (params.item.type === "webSearch") {
4168
+ this.webSearchCompletionByItem.set(params.item.id, {
4169
+ runWasAborted: this.options.runAbortSignal?.aborted === true,
4170
+ sourceTimestampMs: params.sourceTimestampMs
4171
+ });
4172
+ return;
4173
+ }
4174
+ const itemDurationMs = typeof params.item.durationMs === "number" ? params.item.durationMs : void 0;
4175
+ this.recordTerminal(params.item.id, toolName, auditNativeToolTerminalStatus(params.item), {
4176
+ itemDurationMs,
4177
+ sourceTimestampMs: params.sourceTimestampMs
4178
+ });
4179
+ }
4180
+ recordApprovalFailureDisposition(toolCallId, disposition) {
4181
+ if (!this.completedItemIds.has(toolCallId)) this.approvalFailureDispositionByItem.set(toolCallId, disposition);
4182
+ }
4183
+ recordPreToolUseFailure(failure, runWasAborted = this.options.runAbortSignal?.aborted === true) {
4184
+ if (this.completedItemIds.has(failure.toolCallId)) return;
4185
+ const record = {
4186
+ failure,
4187
+ terminalReason: runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : failure.disposition
4188
+ };
4189
+ if (this.finalized) {
4190
+ this.completedItemIds.add(failure.toolCallId);
4191
+ this.emitPreToolUseFailure(record, failure.toolName, failure.durationMs);
4192
+ return;
4193
+ }
4194
+ this.preToolUseFailureByItem.set(failure.toolCallId, record);
4195
+ }
4196
+ recordRawWebSearchResult(item) {
4197
+ if (readString(item, "type") !== "web_search_call") return;
4198
+ const toolCallId = readString(item, "id");
4199
+ if (!toolCallId || this.completedItemIds.has(toolCallId)) return;
4200
+ const toolName = "web_search";
4201
+ this.recordStarted(toolCallId, toolName, "unknown");
4202
+ const rawStatus = readString(item, "status");
4203
+ if (rawStatus === "in_progress" || rawStatus === "running") return;
4204
+ const status = rawStatus === "completed" ? "completed" : rawStatus === "cancelled" ? "cancelled" : rawStatus === "failed" || rawStatus === "error" || rawStatus === "incomplete" ? "failed" : "unknown";
4205
+ this.recordTerminal(toolCallId, toolName, status, { sourceTimestampMs: this.webSearchCompletionByItem.get(toolCallId)?.sourceTimestampMs });
4206
+ }
4207
+ recordTerminal(toolCallId, toolName, status, options = {}) {
4208
+ const runWasAborted = options.runWasAborted ?? this.options.runAbortSignal?.aborted === true;
4209
+ const preToolUseFailure = this.preToolUseFailureByItem.get(toolCallId);
4210
+ this.preToolUseFailureByItem.delete(toolCallId);
4211
+ const approvalFailureDisposition = this.approvalFailureDispositionByItem.get(toolCallId);
4212
+ this.approvalFailureDispositionByItem.delete(toolCallId);
4213
+ this.completedItemIds.add(toolCallId);
4214
+ this.activeItems.delete(toolCallId);
4215
+ this.webSearchCompletionByItem.delete(toolCallId);
4216
+ const startedAt = this.startedAtByItem.get(toolCallId);
4217
+ this.startedAtByItem.delete(toolCallId);
4218
+ const endedAt = options.sourceTimestampMs ?? Date.now();
4219
+ const durationMs = options.itemDurationMs ?? (startedAt === void 0 ? 0 : Math.max(0, endedAt - startedAt));
4220
+ if (preToolUseFailure) {
4221
+ this.emitPreToolUseFailure(preToolUseFailure, toolName, durationMs, options.sourceTimestampMs);
4222
+ return;
4223
+ }
4224
+ const terminalEvent = approvalFailureDisposition ? {
4225
+ type: "tool.execution.error",
4226
+ durationMs,
4227
+ errorCategory: "codex_native_tool_approval",
4228
+ terminalReason: approvalFailureDisposition
4229
+ } : status === "blocked" ? {
4230
+ type: "tool.execution.blocked",
4231
+ reason: "codex_native_tool_blocked",
4232
+ deniedReason: "codex_native_tool_blocked"
4233
+ } : status === "failed" || status === "cancelled" || status === "unknown" ? {
4234
+ type: "tool.execution.error",
4235
+ durationMs,
4236
+ errorCategory: status === "unknown" ? "codex_native_tool_outcome_unknown" : status === "cancelled" ? "aborted" : "codex_native_tool_error",
4237
+ ...status === "unknown" ? { errorCode: "tool_outcome_unknown" } : {},
4238
+ terminalReason: status === "unknown" ? "failed" : runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : status === "cancelled" ? "cancelled" : "failed"
4239
+ } : {
4240
+ type: "tool.execution.completed",
4241
+ durationMs
4242
+ };
4243
+ emitTrustedDiagnosticEvent({
4244
+ ...this.buildBase(toolCallId, toolName),
4245
+ ...terminalEvent,
4246
+ ...options.sourceTimestampMs !== void 0 ? { sourceTimestampMs: options.sourceTimestampMs } : {}
4247
+ });
4248
+ }
4249
+ finalizeActive(runWasAborted = this.options.runAbortSignal?.aborted === true) {
4250
+ this.finalized = true;
4251
+ for (const [toolCallId, { toolName, unfinishedStatus }] of this.activeItems) {
4252
+ const webSearchCompletion = this.webSearchCompletionByItem.get(toolCallId);
4253
+ const itemRunWasAborted = webSearchCompletion ? webSearchCompletion.runWasAborted : runWasAborted;
4254
+ this.recordTerminal(toolCallId, toolName, unfinishedStatus, {
4255
+ runWasAborted: itemRunWasAborted,
4256
+ sourceTimestampMs: webSearchCompletion?.sourceTimestampMs
4257
+ });
4258
+ }
4259
+ for (const [toolCallId, record] of this.preToolUseFailureByItem) if (!this.completedItemIds.has(toolCallId)) this.recordTerminal(toolCallId, record.failure.toolName, "failed", { itemDurationMs: record.failure.durationMs });
4260
+ this.activeItems.clear();
4261
+ this.webSearchCompletionByItem.clear();
4262
+ this.approvalFailureDispositionByItem.clear();
4263
+ this.preToolUseFailureByItem.clear();
4264
+ }
4265
+ emitPreToolUseFailure(record, toolName, durationMs, sourceTimestampMs) {
4266
+ emitCodexNativePreToolUseFailureDiagnostic({
4267
+ agentId: this.context.agentId,
4268
+ sessionId: this.context.sessionId,
4269
+ sessionKey: this.context.sessionKey,
4270
+ runId: this.context.runId,
4271
+ failure: {
4272
+ ...record.failure,
4273
+ toolName,
4274
+ durationMs
4275
+ },
4276
+ terminalReason: record.terminalReason,
4277
+ sourceTimestampMs
4278
+ });
4279
+ }
4280
+ recordSnapshotItem(item) {
4281
+ if (!auditNativeToolName(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
4282
+ const toolName = auditNativeToolName(item);
4283
+ if (!toolName) return;
4284
+ this.recordStarted(item.id, toolName, auditNativeToolUnfinishedStatus(item));
4285
+ this.recordItem({
4286
+ phase: "result",
4287
+ item
4288
+ });
4289
+ }
4290
+ recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs) {
4291
+ if (this.activeItems.has(toolCallId)) return;
4292
+ this.startedAtByItem.set(toolCallId, sourceTimestampMs ?? Date.now());
4293
+ this.activeItems.set(toolCallId, {
4294
+ toolName,
4295
+ unfinishedStatus
4296
+ });
4297
+ emitTrustedDiagnosticEvent({
4298
+ type: "tool.execution.started",
4299
+ ...this.buildBase(toolCallId, toolName),
4300
+ ...sourceTimestampMs !== void 0 ? { sourceTimestampMs } : {}
4301
+ });
4302
+ }
4303
+ buildBase(toolCallId, toolName) {
4304
+ return {
4305
+ agentId: this.context.agentId,
4306
+ runId: this.context.runId,
4307
+ sessionId: this.context.sessionId,
4308
+ sessionKey: this.context.sessionKey,
4309
+ toolName,
4310
+ toolCallId
4311
+ };
4312
+ }
4313
+ };
4314
+ const ZERO_USAGE = {
4315
+ input: 0,
4316
+ output: 0,
4317
+ cacheRead: 0,
4318
+ cacheWrite: 0,
4319
+ totalTokens: 0,
4320
+ cost: {
4321
+ input: 0,
4322
+ output: 0,
4323
+ cacheRead: 0,
4324
+ cacheWrite: 0,
4325
+ total: 0
4326
+ }
4327
+ };
4328
+ const MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM = 20;
4329
+ const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 12e3;
4330
+ const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
4331
+ const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
4332
+ const BYTES_PER_MB = 1024 * 1024;
4333
+ const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * BYTES_PER_MB;
4334
+ const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = /* @__PURE__ */ new Set([
4335
+ "message",
4336
+ "messages",
4337
+ "reply",
4338
+ "send",
4339
+ "reaction",
4340
+ "react",
4341
+ "typing"
4342
+ ]);
4343
+ function shouldEmitTranscriptToolProgress(toolName, _args) {
4344
+ const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
4345
+ return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
4346
+ }
4347
+ var CodexAppServerEventProjector = class {
4348
+ constructor(params, threadId, turnId, options = {}) {
4349
+ this.params = params;
4350
+ this.threadId = threadId;
4351
+ this.turnId = turnId;
4352
+ this.options = options;
4353
+ this.assistantTextByItem = /* @__PURE__ */ new Map();
4354
+ this.assistantItemOrder = [];
4355
+ this.assistantPhaseByItem = /* @__PURE__ */ new Map();
4356
+ this.latestTerminalAssistantCandidateSuperseded = false;
4357
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
4358
+ this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
4359
+ this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
4360
+ this.pendingRawCommentaryEchoes = 0;
4361
+ this.reasoningTextByGroup = /* @__PURE__ */ new Map();
4362
+ this.reasoningItemOrder = /* @__PURE__ */ new Map();
4363
+ this.planTextByItem = /* @__PURE__ */ new Map();
4364
+ this.activeItemIds = /* @__PURE__ */ new Set();
4365
+ this.completedItemIds = /* @__PURE__ */ new Set();
4366
+ this.activeCompactionItemIds = /* @__PURE__ */ new Set();
4367
+ this.toolProgressTexts = /* @__PURE__ */ new Set();
4368
+ this.toolResultSummaryItemIds = /* @__PURE__ */ new Set();
4369
+ this.toolResultOutputItemIds = /* @__PURE__ */ new Set();
4370
+ this.toolResultOutputStreamedItemIds = /* @__PURE__ */ new Set();
4371
+ this.transcriptToolProgressSuppressedIds = /* @__PURE__ */ new Set();
4372
+ this.toolTranscriptArgumentsById = /* @__PURE__ */ new Map();
4373
+ this.toolResultOutputDeltaState = /* @__PURE__ */ new Map();
4374
+ this.toolResultOutputTextByItem = /* @__PURE__ */ new Map();
4375
+ this.toolMetas = /* @__PURE__ */ new Map();
4376
+ this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
4377
+ this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
4378
+ this.sideEffectingToolItemIds = /* @__PURE__ */ new Set();
4379
+ this.sideEffectingDynamicToolCallIds = /* @__PURE__ */ new Set();
4380
+ this.toolTranscriptMessages = [];
4381
+ this.toolTranscriptCallIds = /* @__PURE__ */ new Set();
4382
+ this.toolTranscriptResultIds = /* @__PURE__ */ new Set();
4383
+ this.toolTranscriptNamesById = /* @__PURE__ */ new Map();
4384
+ this.toolTrajectoryCallIds = /* @__PURE__ */ new Set();
4385
+ this.toolTrajectoryResultIds = /* @__PURE__ */ new Set();
4386
+ this.toolTrajectoryNamesById = /* @__PURE__ */ new Map();
4387
+ this.toolTrajectoryItemsById = /* @__PURE__ */ new Map();
4388
+ this.transcriptToolProgressCallIds = /* @__PURE__ */ new Set();
4389
+ this.nativeGeneratedMediaItemIds = /* @__PURE__ */ new Set();
4390
+ this.nativeGeneratedMediaUrlsByItemId = /* @__PURE__ */ new Map();
4391
+ this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
4392
+ this.assistantStarted = false;
4393
+ this.reasoningStarted = false;
4394
+ this.reasoningEnded = false;
4395
+ this.streamedPartialAssistantItemReplaceable = false;
4396
+ this.promptErrorSource = null;
4397
+ this.synthesizedMissingToolResultError = null;
4398
+ this.aborted = false;
4399
+ this.guardianReviewCount = 0;
4400
+ this.completedCompactionCount = 0;
4401
+ this.nativeToolLifecycleProjector = new CodexNativeToolLifecycleProjector(params, threadId, turnId, { runAbortSignal: options.runAbortSignal });
4402
+ }
4403
+ getCompletedTurnStatus() {
4404
+ return this.completedTurn?.status;
4405
+ }
4406
+ hasCompletedTerminalAssistantText() {
4407
+ const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
4408
+ if (!latestCompletedItemId) return false;
4409
+ const finalItem = this.resolveFinalAssistantTextItem();
4410
+ return this.latestCompletedItemId === latestCompletedItemId && finalItem?.itemId === latestCompletedItemId && this.completedItemIds.has(latestCompletedItemId);
4411
+ }
4412
+ getLatestTerminalAssistantCandidate() {
4413
+ const itemId = this.latestTerminalAssistantCandidateItemId;
4414
+ if (!itemId) return;
4415
+ const text = this.assistantTextByItem.get(itemId)?.trim();
4416
+ return {
4417
+ itemId,
4418
+ hasText: Boolean(text && !this.toolProgressTexts.has(text))
4419
+ };
4420
+ }
4421
+ hasLatestTerminalAssistantCandidateText() {
4422
+ return !this.latestTerminalAssistantCandidateSuperseded && this.getLatestTerminalAssistantCandidate()?.hasText === true;
4423
+ }
4424
+ canReleaseLatestTerminalAssistantAfterToolHandoff() {
4425
+ return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
4426
+ }
4427
+ /** Restores a completed final item after only the enclosing turn timeout fired. */
4428
+ recoverCompletedTerminalAssistantAfterTurnWatchTimeout() {
4429
+ if (!this.aborted || this.promptError !== "codex app-server attempt timed out" || !this.hasCompletedTerminalAssistantText()) return false;
4430
+ this.aborted = false;
4431
+ this.promptError = void 0;
4432
+ this.promptErrorSource = null;
4433
+ return true;
4434
+ }
4435
+ /** Resolves the shared model-order position for a native tool item. */
4436
+ recordNativeToolOutcome(item) {
4437
+ if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
4438
+ const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
4439
+ if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
4440
+ }
4441
+ recordNativeToolApprovalFailure(toolCallId, disposition) {
4442
+ this.nativeToolLifecycleProjector.recordApprovalFailureDisposition(toolCallId, disposition);
4443
+ }
4444
+ recordNativeToolPreToolUseFailure(failure) {
4445
+ this.nativeToolLifecycleProjector.recordPreToolUseFailure(failure);
4446
+ }
4447
+ async handleNotification(notification) {
4448
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
4449
+ if (!params) return;
4450
+ if (isHookNotificationMethod(notification.method)) {
4451
+ if (!this.isHookNotificationForCurrentThread(params)) return;
4452
+ } else if (notification.method === "guardianWarning") {
4453
+ if (readCodexNotificationThreadId(params) !== this.threadId) return;
4454
+ } else if (!this.isNotificationForTurn(params)) return;
4455
+ this.nativeToolLifecycleProjector.handleNotification(notification);
4456
+ switch (notification.method) {
4457
+ case "item/agentMessage/delta":
4458
+ await this.handleAssistantDelta(params);
4459
+ break;
4460
+ case "item/reasoning/summaryTextDelta":
4461
+ case "item/reasoning/textDelta":
4462
+ await this.handleReasoningDelta(notification.method, params);
4463
+ break;
4464
+ case "item/plan/delta":
4465
+ this.handlePlanDelta(params);
4466
+ break;
4467
+ case "turn/plan/updated":
4468
+ this.handleTurnPlanUpdated(params);
4469
+ break;
4470
+ case "item/started":
4471
+ await this.handleItemStarted(params);
4472
+ break;
4473
+ case "item/completed":
4474
+ await this.handleItemCompleted(params);
4475
+ break;
4476
+ case "item/commandExecution/outputDelta":
4477
+ this.handleOutputDelta(params, "bash");
4478
+ break;
4479
+ case "item/autoApprovalReview/started":
4480
+ case "item/autoApprovalReview/completed":
4481
+ this.handleGuardianReviewNotification(notification.method, params);
4482
+ break;
4483
+ case "guardianWarning":
4484
+ this.handleGuardianWarning(params);
4485
+ break;
4486
+ case "hook/started":
4487
+ case "hook/completed":
4488
+ this.handleHookNotification(notification.method, params);
4489
+ break;
4490
+ case "thread/tokenUsage/updated":
4491
+ this.handleTokenUsage(params);
4492
+ break;
4493
+ case "turn/completed":
4494
+ await this.handleTurnCompleted(params);
4495
+ break;
4496
+ case "rawResponseItem/completed":
4497
+ await this.handleRawResponseItemCompleted(params);
4498
+ break;
4499
+ case "error":
4500
+ if (params.willRetry === true) break;
4501
+ this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
4502
+ this.promptErrorSource = "prompt";
4503
+ break;
4504
+ default: break;
4505
+ }
4506
+ }
4507
+ buildResult(toolTelemetry, options) {
4508
+ this.nativeToolLifecycleProjector.finalizeActive();
4509
+ const assistantTexts = this.collectAssistantTexts();
4510
+ const reasoningText = collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
4511
+ const planText = collectTextValues(this.planTextByItem).join("\n\n");
4512
+ const hasAssistantItemText = this.hasAssistantItemTextForSynthesis();
4513
+ const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
4514
+ const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
4515
+ this.synthesizeMissingToolResults({
4516
+ synthesize: legacyFailClosed,
4517
+ recordPromptError: legacyFailClosed && !hasDeliverableAssistantOnCompletedTurn
4518
+ });
4519
+ const lastAssistant = assistantTexts.length > 0 ? this.createAssistantMessage(assistantTexts.join("\n\n")) : void 0;
4520
+ const currentAttemptAssistant = this.createCurrentAttemptAssistantMessage();
4521
+ const turnId = this.turnId;
4522
+ const messagesSnapshot = this.params.suppressNextUserMessagePersistence ? [] : [attachCodexMirrorIdentity(buildCodexUserPromptMessage(this.params), `${turnId}:prompt`)];
4523
+ if (reasoningText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex reasoning", reasoningText), `${turnId}:reasoning`));
4524
+ if (planText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex plan", planText), `${turnId}:plan`));
4525
+ messagesSnapshot.push(...this.toolTranscriptMessages);
4526
+ if (lastAssistant) messagesSnapshot.push(attachCodexMirrorIdentity(lastAssistant, `${turnId}:assistant`));
4527
+ const turnFailed = this.completedTurn?.status === "failed";
4528
+ const promptError = this.promptError ?? this.synthesizedMissingToolResultError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
4529
+ const agentHarnessResultClassification = classifyAgentHarnessTerminalOutcome({
4530
+ assistantTexts,
4531
+ reasoningText,
4532
+ planText,
4533
+ promptError,
4534
+ turnCompleted: Boolean(this.completedTurn)
4535
+ });
4536
+ const toolMetas = [...this.toolMetas.values()];
4537
+ const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.nativeGeneratedMediaItemIds.size > 0 || this.sideEffectingToolItemIds.size > 0 || this.sideEffectingDynamicToolCallIds.size > 0;
4538
+ return {
4539
+ aborted: this.aborted,
4540
+ externalAbort: false,
4541
+ timedOut: false,
4542
+ idleTimedOut: false,
4543
+ timedOutDuringCompaction: false,
4544
+ timedOutDuringToolExecution: false,
4545
+ promptError,
4546
+ promptErrorSource: promptError ? this.promptErrorSource || "prompt" : null,
4547
+ sessionIdUsed: this.params.sessionId,
4548
+ ...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
4549
+ bootstrapPromptWarningSignaturesSeen: this.params.bootstrapPromptWarningSignaturesSeen,
4550
+ bootstrapPromptWarningSignature: this.params.bootstrapPromptWarningSignature,
4551
+ messagesSnapshot,
4552
+ assistantTexts,
4553
+ toolMetas,
4554
+ lastAssistant,
4555
+ currentAttemptAssistant,
4556
+ ...this.lastNativeToolError ? { lastToolError: this.lastNativeToolError } : {},
4557
+ didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
4558
+ didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
4559
+ messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
4560
+ messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
4561
+ messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
4562
+ messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
4563
+ heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
4564
+ toolMediaUrls: this.buildToolMediaUrls(toolTelemetry),
4565
+ toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
4566
+ successfulCronAdds: toolTelemetry.successfulCronAdds,
4567
+ cloudCodeAssistFormatError: false,
4568
+ attemptUsage: this.tokenUsage,
4569
+ replayMetadata: {
4570
+ hadPotentialSideEffects,
4571
+ replaySafe: !hadPotentialSideEffects
4572
+ },
4573
+ itemLifecycle: {
4574
+ startedCount: this.activeItemIds.size + this.completedItemIds.size,
4575
+ completedCount: this.completedItemIds.size,
4576
+ activeCount: this.activeItemIds.size,
4577
+ ...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {}
4578
+ },
4579
+ yieldDetected: options?.yieldDetected || false,
4580
+ didSendDeterministicApprovalPrompt: this.guardianReviewCount > 0 ? false : void 0
4581
+ };
4582
+ }
4583
+ recordDynamicToolCall(params) {
4584
+ const args = sanitizeCodexToolArguments(params.arguments);
4585
+ this.recordToolTranscriptCall({
4586
+ id: params.callId,
4587
+ name: params.tool,
4588
+ arguments: args
4589
+ });
4590
+ }
4591
+ recordDynamicToolResult(params) {
4592
+ const resultText = collectDynamicToolContentText(params.contentItems);
4593
+ if (params.asyncStarted === true) {
4594
+ const existing = this.toolMetas.get(params.callId);
4595
+ this.toolMetas.set(params.callId, {
4596
+ toolName: existing?.toolName ?? params.tool,
4597
+ ...existing?.meta ? { meta: existing.meta } : {},
4598
+ asyncStarted: true
4599
+ });
4600
+ }
4601
+ this.recordToolTranscriptResult({
4602
+ id: params.callId,
4603
+ name: params.tool,
4604
+ text: resultText,
4605
+ isError: !params.success
4606
+ });
4607
+ if (!params.success && params.terminalType === "blocked") this.lastNativeToolError = {
4608
+ toolName: params.tool,
4609
+ error: resultText || "codex dynamic tool blocked"
4610
+ };
4611
+ else if (params.success && this.lastNativeToolError && !this.lastNativeToolError.mutatingAction) this.lastNativeToolError = void 0;
4612
+ if (params.sideEffectEvidence === true) this.sideEffectingDynamicToolCallIds.add(params.callId);
4613
+ }
4614
+ markTimedOut() {
4615
+ this.aborted = true;
4616
+ this.promptError = "codex app-server attempt timed out";
4617
+ this.promptErrorSource = "prompt";
4618
+ }
4619
+ markAborted() {
4620
+ this.aborted = true;
4621
+ }
4622
+ isCompacting() {
4623
+ return this.activeCompactionItemIds.size > 0;
4624
+ }
4625
+ async handleAssistantDelta(params) {
4626
+ const itemId = readString(params, "itemId") ?? "assistant";
4627
+ const delta = readString(params, "delta") ?? "";
4628
+ if (!delta) return;
4629
+ if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
4630
+ const isCommentary = this.isCommentaryAssistantItem(itemId);
4631
+ if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
4632
+ if (!this.assistantStarted) {
4633
+ this.assistantStarted = true;
4634
+ await this.params.onAssistantMessageStart?.();
4635
+ }
4636
+ this.rememberAssistantItem(itemId);
4637
+ const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
4638
+ this.assistantTextByItem.set(itemId, text);
4639
+ if (isCommentary) this.emitCommentaryProgress({
4640
+ itemId,
4641
+ text
4642
+ });
4643
+ else {
4644
+ const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
4645
+ const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
4646
+ if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
4647
+ else if (this.streamedPartialAssistantItemId === void 0) this.streamedPartialAssistantItemReplaceable = !knownFinalAnswer;
4648
+ this.streamedPartialAssistantItemId = itemId;
4649
+ const replaceable = this.streamedPartialAssistantItemReplaceable;
4650
+ const replacement = replace && replaceable;
4651
+ const streamPayload = {
4652
+ text,
4653
+ delta: replacement ? "" : delta,
4654
+ ...replacement ? { replace: true } : {}
4655
+ };
4656
+ this.emitAgentEvent({
4657
+ stream: "assistant",
4658
+ data: {
4659
+ ...streamPayload,
4660
+ ...replaceable ? { replaceable: true } : {}
4661
+ }
4662
+ });
4663
+ if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
4664
+ }
4665
+ }
4666
+ async handleReasoningDelta(method, params) {
4667
+ const itemId = readString(params, "itemId") ?? "reasoning";
4668
+ const delta = readString(params, "delta") ?? "";
4669
+ if (!delta) return;
4670
+ this.reasoningStarted = true;
4671
+ if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
4672
+ const groupIndex = method === "item/reasoning/textDelta" ? readNonNegativeInteger(params, "contentIndex") ?? 0 : readNonNegativeInteger(params, "summaryIndex") ?? 0;
4673
+ const groupKey = `${method}\0${itemId}\0${groupIndex}`;
4674
+ const current = this.reasoningTextByGroup.get(groupKey);
4675
+ this.reasoningTextByGroup.set(groupKey, {
4676
+ itemId,
4677
+ method,
4678
+ index: groupIndex,
4679
+ text: `${current?.text ?? ""}${delta}`
4680
+ });
4681
+ await this.params.onReasoningStream?.({
4682
+ text: collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n"),
4683
+ isReasoningSnapshot: true
4684
+ });
4685
+ }
4686
+ handlePlanDelta(params) {
4687
+ const itemId = readString(params, "itemId") ?? "plan";
4688
+ const delta = readString(params, "delta") ?? "";
4689
+ if (!delta) return;
4690
+ const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
4691
+ this.planTextByItem.set(itemId, text);
4692
+ this.emitPlanUpdate({
4693
+ explanation: void 0,
4694
+ steps: splitPlanText(text)
4695
+ });
4696
+ }
4697
+ handleTurnPlanUpdated(params) {
4698
+ const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
4699
+ if (!isJsonObject(entry)) return [];
4700
+ const step = readString(entry, "step");
4701
+ const status = readString(entry, "status");
4702
+ if (!step) return [];
4703
+ return status ? [`${step} (${status})`] : [step];
4704
+ }) : void 0;
4705
+ this.emitPlanUpdate({
4706
+ explanation: readNullableString(params, "explanation"),
4707
+ steps: plan
4708
+ });
4709
+ }
4710
+ async handleItemStarted(params) {
4711
+ const item = readItem(params.item);
4712
+ const itemId = item?.id ?? readString(params, "itemId");
4713
+ if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
4714
+ this.rememberAssistantPhase(item);
4715
+ if (itemId) {
4716
+ this.activeItemIds.add(itemId);
4717
+ if (itemId !== this.latestTerminalAssistantCandidateItemId) {
4718
+ this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
4719
+ if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
4720
+ }
4721
+ }
4722
+ this.recordNativeToolOutcome(item);
4723
+ if (item?.type === "contextCompaction" && itemId) {
4724
+ this.activeCompactionItemIds.add(itemId);
4725
+ await runAgentHarnessBeforeCompactionHook({
4726
+ sessionFile: this.params.sessionFile,
4727
+ messages: await this.readMirroredSessionMessages(),
4728
+ ctx: {
4729
+ runId: this.params.runId,
4730
+ agentId: this.params.agentId,
4731
+ sessionKey: this.params.sessionKey,
4732
+ sessionId: this.params.sessionId,
4733
+ workspaceDir: this.params.workspaceDir,
4734
+ messageProvider: this.params.messageProvider ?? void 0,
4735
+ trigger: this.params.trigger,
4736
+ channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
4737
+ }
4738
+ });
4739
+ this.emitAgentEvent({
4740
+ stream: "compaction",
4741
+ data: {
4742
+ phase: "start",
4743
+ backend: "codex-app-server",
4744
+ threadId: this.threadId,
4745
+ turnId: this.turnId,
4746
+ itemId
4747
+ }
4748
+ });
4749
+ }
4750
+ this.recordToolMeta(item);
4751
+ this.emitStandardItemEvent({
4752
+ phase: "start",
4753
+ item
4754
+ });
4755
+ await this.emitNormalizedToolItemEvent({
4756
+ phase: "start",
4757
+ item
4758
+ });
4759
+ this.recordNativeToolTranscriptCall(item);
4760
+ this.emitToolResultSummary(item);
4761
+ this.emitAgentEvent({
4762
+ stream: "codex_app_server.item",
4763
+ data: {
4764
+ phase: "started",
4765
+ itemId,
4766
+ type: item?.type
4767
+ }
4768
+ });
4769
+ }
4770
+ async handleItemCompleted(params) {
4771
+ const item = readItem(params.item);
4772
+ this.recordNativeToolOutcome(item);
4773
+ this.clearTerminalPresentationForNativeItem(item);
4774
+ const itemId = item?.id ?? readString(params, "itemId");
4775
+ if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
4776
+ if (itemId) {
4777
+ this.activeItemIds.delete(itemId);
4778
+ this.completedItemIds.add(itemId);
4779
+ this.latestCompletedItemId = itemId;
4780
+ }
4781
+ this.rememberAssistantPhase(item);
4782
+ if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
4783
+ this.latestCompletedTerminalAssistantItemId = item.id;
4784
+ this.markLatestTerminalAssistantCandidate(item.id);
4785
+ this.pendingRawTerminalAssistantEchoItemId = item.id;
4786
+ } else if (itemId) {
4787
+ this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
4788
+ if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
4789
+ }
4790
+ if (item?.type === "agentMessage" && typeof item.text === "string") {
4791
+ this.rememberAssistantItem(item.id);
4792
+ this.assistantTextByItem.set(item.id, item.text);
4793
+ if (item.text && this.isCommentaryAssistantItem(item.id)) {
4794
+ this.emitCommentaryProgress({
4795
+ itemId: item.id,
4796
+ text: item.text
4797
+ });
4798
+ this.pendingRawCommentaryEchoes += 1;
4799
+ }
4800
+ }
4801
+ this.recordNativeGeneratedMedia(item);
4802
+ if (item?.type === "plan" && typeof item.text === "string" && item.text) {
4803
+ this.planTextByItem.set(item.id, item.text);
4804
+ this.emitPlanUpdate({
4805
+ explanation: void 0,
4806
+ steps: splitPlanText(item.text)
4807
+ });
4808
+ }
4809
+ if (item?.type === "contextCompaction" && itemId) {
4810
+ this.activeCompactionItemIds.delete(itemId);
4811
+ this.completedCompactionCount += 1;
4812
+ await runAgentHarnessAfterCompactionHook({
4813
+ sessionFile: this.params.sessionFile,
4814
+ messages: await this.readMirroredSessionMessages(),
4815
+ compactedCount: -1,
4816
+ ctx: {
4817
+ runId: this.params.runId,
4818
+ agentId: this.params.agentId,
4819
+ sessionKey: this.params.sessionKey,
4820
+ sessionId: this.params.sessionId,
4821
+ workspaceDir: this.params.workspaceDir,
4822
+ messageProvider: this.params.messageProvider ?? void 0,
4823
+ trigger: this.params.trigger,
4824
+ channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
4825
+ }
4826
+ });
4827
+ this.emitAgentEvent({
4828
+ stream: "compaction",
4829
+ data: {
4830
+ phase: "end",
4831
+ backend: "codex-app-server",
4832
+ completed: true,
4833
+ threadId: this.threadId,
4834
+ turnId: this.turnId,
4835
+ itemId
4836
+ }
4837
+ });
4838
+ }
4839
+ this.recordToolMeta(item);
4840
+ this.emitStandardItemEvent({
4841
+ phase: "end",
4842
+ item
4843
+ });
4844
+ await this.emitNormalizedToolItemEvent({
4845
+ phase: "result",
4846
+ item
4847
+ });
4848
+ this.recordNativeToolTranscriptCall(item);
4849
+ this.recordNativeToolTranscriptResult(item);
4850
+ this.emitToolResultSummary(item);
4851
+ this.emitToolResultOutput(item);
4852
+ this.emitAgentEvent({
4853
+ stream: "codex_app_server.item",
4854
+ data: {
4855
+ phase: "completed",
4856
+ itemId,
4857
+ type: item?.type
4858
+ }
4859
+ });
4860
+ }
4861
+ handleTokenUsage(params) {
4862
+ const tokenUsage = isJsonObject(params.tokenUsage) ? params.tokenUsage : void 0;
4863
+ const last = tokenUsage && isJsonObject(tokenUsage.last) ? tokenUsage.last : void 0;
4864
+ if (!last) return;
4865
+ const usage = normalizeCodexTokenUsage(last);
4866
+ if (usage) this.tokenUsage = usage;
4867
+ }
4868
+ handleGuardianReviewNotification(method, params) {
4869
+ this.guardianReviewCount += 1;
4870
+ const review = isJsonObject(params.review) ? params.review : void 0;
4871
+ const action = isJsonObject(params.action) ? params.action : void 0;
4872
+ this.emitAgentEvent({
4873
+ stream: "codex_app_server.guardian",
4874
+ data: {
4875
+ method,
4876
+ phase: method.endsWith("/started") ? "started" : "completed",
4877
+ reviewId: readString(params, "reviewId"),
4878
+ targetItemId: readNullableString(params, "targetItemId"),
4879
+ decisionSource: readString(params, "decisionSource"),
4880
+ status: review ? readString(review, "status") : void 0,
4881
+ riskLevel: review ? readString(review, "riskLevel") : void 0,
4882
+ userAuthorization: review ? readString(review, "userAuthorization") : void 0,
4883
+ rationale: review ? readNullableString(review, "rationale") : void 0,
4884
+ actionType: action ? readString(action, "type") : void 0
4885
+ }
4886
+ });
4887
+ }
4888
+ handleGuardianWarning(params) {
4889
+ this.emitAgentEvent({
4890
+ stream: "codex_app_server.guardian",
4891
+ data: {
4892
+ phase: "warning",
4893
+ message: readString(params, "message")
4894
+ }
4895
+ });
4896
+ }
4897
+ handleHookNotification(method, params) {
4898
+ const run = isJsonObject(params.run) ? params.run : void 0;
4899
+ if (!run) return;
4900
+ const durationMs = readNumber(run, "durationMs");
4901
+ const entries = readHookOutputEntries(run.entries);
4902
+ const hookTurnId = readNullableString(params, "turnId");
4903
+ this.emitAgentEvent({
4904
+ stream: "codex_app_server.hook",
4905
+ data: {
4906
+ phase: method === "hook/started" ? "started" : "completed",
4907
+ threadId: this.threadId,
4908
+ turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
4909
+ hookRunId: readString(run, "id"),
4910
+ eventName: readString(run, "eventName"),
4911
+ handlerType: readString(run, "handlerType"),
4912
+ executionMode: readString(run, "executionMode"),
4913
+ scope: readString(run, "scope"),
4914
+ source: readString(run, "source"),
4915
+ sourcePath: readString(run, "sourcePath"),
4916
+ status: readString(run, "status"),
4917
+ statusMessage: readNullableString(run, "statusMessage"),
4918
+ ...durationMs !== void 0 ? { durationMs } : {},
4919
+ ...entries.length > 0 ? { entries } : {}
4920
+ }
4921
+ });
4922
+ }
4923
+ async handleTurnCompleted(params) {
4924
+ const turn = readCodexTurn(params.turn);
4925
+ if (!turn || turn.id !== this.turnId) return;
4926
+ this.completedTurn = turn;
4927
+ if (turn.status === "failed") {
4928
+ this.promptError = formatCodexUsageLimitErrorMessage({
4929
+ message: turn.error?.message,
4930
+ codexErrorInfo: turn.error?.codexErrorInfo,
4931
+ rateLimits: this.options.readRecentRateLimits?.()
4932
+ }) ?? turn.error?.message ?? "codex app-server turn failed";
4933
+ this.promptErrorSource = "prompt";
4934
+ }
4935
+ const turnItems = turn.items ?? [];
4936
+ for (let index = turnItems.length - 1; index >= 0; index -= 1) {
4937
+ const item = turnItems[index];
4938
+ if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
4939
+ if (item?.type === "dynamicToolCall") break;
4940
+ if (shouldClearTerminalPresentationForNativeItem(item)) {
4941
+ this.clearTerminalPresentationForNativeItem(item);
4942
+ break;
4943
+ }
4944
+ }
4945
+ for (const item of turnItems) {
4946
+ this.rememberAssistantPhase(item);
4947
+ if (item.type === "agentMessage" && typeof item.text === "string") {
4948
+ this.rememberAssistantItem(item.id);
4949
+ this.assistantTextByItem.set(item.id, item.text);
4950
+ }
4951
+ this.recordNativeGeneratedMedia(item);
4952
+ if (item.type === "plan" && typeof item.text === "string" && item.text) {
4953
+ this.planTextByItem.set(item.id, item.text);
4954
+ this.emitPlanUpdate({
4955
+ explanation: void 0,
4956
+ steps: splitPlanText(item.text)
4957
+ });
4958
+ }
4959
+ this.recordToolMeta(item);
4960
+ await this.emitSnapshotOnlyNativeToolProgress(item);
4961
+ this.recordNativeToolTranscriptCall(item);
4962
+ this.recordNativeToolTranscriptResult(item);
4963
+ this.emitAfterToolCallObservation(item);
4964
+ this.emitToolResultSummary(item);
4965
+ this.emitToolResultOutput(item);
4966
+ }
4967
+ this.activeCompactionItemIds.clear();
4968
+ await this.maybeEndReasoning();
4969
+ }
4970
+ async emitSnapshotOnlyNativeToolProgress(item) {
4971
+ if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
4972
+ if (!this.activeItemIds.has(item.id)) {
4973
+ this.emitStandardItemEvent({
4974
+ phase: "start",
4975
+ item
4976
+ });
4977
+ await this.emitNormalizedToolItemEvent({
4978
+ phase: "start",
4979
+ item
4980
+ });
4981
+ }
4982
+ this.activeItemIds.delete(item.id);
4983
+ this.emitStandardItemEvent({
4984
+ phase: "end",
4985
+ item
4986
+ });
4987
+ await this.emitNormalizedToolItemEvent({
4988
+ phase: "result",
4989
+ item
4990
+ });
4991
+ this.completedItemIds.add(item.id);
4992
+ }
4993
+ isCurrentTurnSnapshotItem(item) {
4994
+ const itemTurnId = readItemString(item, "turnId");
4995
+ return itemTurnId === void 0 || itemTurnId === this.turnId;
4996
+ }
4997
+ handleOutputDelta(params, toolName) {
4998
+ const itemId = readString(params, "itemId");
4999
+ const delta = readString(params, "delta");
5000
+ if (!itemId || !delta) return;
5001
+ appendToolOutputDeltaText(this.toolResultOutputTextByItem, itemId, delta);
5002
+ if (!this.shouldEmitToolOutput()) return;
5003
+ if (this.transcriptToolProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.toolTranscriptArgumentsById.get(itemId))) return;
5004
+ const state = this.toolResultOutputDeltaState.get(itemId) ?? {
5005
+ chars: 0,
5006
+ messages: 0,
5007
+ truncated: false
5008
+ };
5009
+ if (state.truncated) return;
5010
+ const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
5011
+ const remainingMessages = Math.max(0, MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM - state.messages);
5012
+ if (remainingChars === 0 || remainingMessages === 0) {
5013
+ state.truncated = true;
5014
+ this.toolResultOutputDeltaState.set(itemId, state);
5015
+ this.emitToolResultMessage({
5016
+ itemId,
5017
+ text: formatToolOutput(toolName, void 0, "(output truncated)")
5018
+ });
5019
+ return;
5020
+ }
5021
+ const chunk = delta.length > remainingChars ? delta.slice(0, remainingChars) : delta;
5022
+ state.chars += chunk.length;
5023
+ state.messages += 1;
5024
+ const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM;
5025
+ if (reachedLimit) state.truncated = true;
5026
+ this.toolResultOutputDeltaState.set(itemId, state);
5027
+ this.toolResultOutputStreamedItemIds.add(itemId);
5028
+ this.emitToolResultMessage({
5029
+ itemId,
5030
+ text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
5031
+ });
5032
+ }
5033
+ async handleRawResponseItemCompleted(params) {
5034
+ const item = isJsonObject(params.item) ? params.item : void 0;
5035
+ if (!item) return;
5036
+ const role = readString(item, "role");
5037
+ const phase = readString(item, "phase");
5038
+ const rawItemId = readString(item, "id");
5039
+ const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
5040
+ const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
5041
+ const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
5042
+ if (pendingTerminalAssistantEchoItemId !== void 0 && !isPendingTerminalAssistantEcho) this.pendingRawTerminalAssistantEchoItemId = void 0;
5043
+ if (!isPendingTerminalAssistantEcho) {
5044
+ this.latestCompletedItemId = void 0;
5045
+ this.markTerminalAssistantCandidateSupersededBy(rawItemId);
5046
+ }
5047
+ await this.recordRawGeneratedImageMedia(item);
5048
+ if (role !== "assistant") return;
5049
+ if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
5050
+ this.pendingRawCommentaryEchoes -= 1;
5051
+ return;
5052
+ }
5053
+ const text = extractRawAssistantText(item);
5054
+ if (isPendingTerminalAssistantEcho) {
5055
+ const typedItemId = pendingTerminalAssistantEchoItemId;
5056
+ this.pendingRawTerminalAssistantEchoItemId = void 0;
5057
+ if (this.assistantTextByItem.get(typedItemId)?.trim() || !text) return;
5058
+ this.rememberAssistantItem(typedItemId);
5059
+ this.assistantTextByItem.set(typedItemId, text);
5060
+ return;
5061
+ }
5062
+ if (!text) return;
5063
+ const itemId = rawItemId ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
5064
+ const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && this.activeItemIds.size === 0;
5065
+ if (phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
5066
+ if (phase) this.assistantPhaseByItem.set(itemId, phase);
5067
+ this.rememberAssistantItem(itemId);
5068
+ this.assistantTextByItem.set(itemId, text);
5069
+ if (phase === "commentary") this.emitCommentaryProgress({
5070
+ itemId,
5071
+ text
5072
+ });
5073
+ else this.markLatestTerminalAssistantCandidate(itemId, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
5074
+ }
5075
+ markLatestTerminalAssistantCandidate(itemId, options) {
5076
+ this.latestTerminalAssistantCandidateItemId = itemId;
5077
+ this.latestTerminalAssistantCandidateSuperseded = false;
5078
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = options?.canReleaseAfterToolHandoff === true;
5079
+ this.terminalAssistantCandidateEarlierActiveItemIds = new Set(this.activeItemIds);
5080
+ }
5081
+ markTerminalAssistantCandidateSupersededBy(itemId, options) {
5082
+ if (!this.latestTerminalAssistantCandidateItemId) return;
5083
+ if (itemId && this.terminalAssistantCandidateEarlierActiveItemIds.has(itemId)) {
5084
+ if (!options?.preserveEarlierActiveItem) this.terminalAssistantCandidateEarlierActiveItemIds.delete(itemId);
5085
+ return;
5086
+ }
5087
+ this.latestTerminalAssistantCandidateSuperseded = true;
5088
+ this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
5089
+ this.terminalAssistantCandidateEarlierActiveItemIds.clear();
5090
+ }
5091
+ recordNativeGeneratedMedia(item) {
5092
+ if (item?.type !== "imageGeneration") return;
5093
+ const savedPath = readItemString(item, "savedPath")?.trim();
5094
+ if (savedPath) this.recordNativeGeneratedMediaUrl({
5095
+ itemId: item.id,
5096
+ mediaUrl: savedPath
5097
+ });
5098
+ }
5099
+ async recordRawGeneratedImageMedia(item) {
5100
+ if (readString(item, "type") !== "image_generation_call") return;
5101
+ const result = readString(item, "result");
5102
+ if (!result) return;
5103
+ const itemId = readString(item, "id") ?? `raw-image-${this.nativeGeneratedMediaItemIds.size}`;
5104
+ this.nativeGeneratedMediaItemIds.add(itemId);
5105
+ const maxBytes = resolveGeneratedImageMaxBytes(this.params.config);
5106
+ const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
5107
+ if (estimatedDecodedBytes !== void 0 && estimatedDecodedBytes > maxBytes) {
5108
+ embeddedAgentLog.warn("codex app-server raw image generation result exceeds media limit", {
5109
+ itemId,
5110
+ estimatedDecodedBytes,
5111
+ maxBytes
5112
+ });
5113
+ return;
5114
+ }
5115
+ const asset = generatedImageAssetFromBase64({
5116
+ base64: result,
5117
+ index: this.nativeGeneratedMediaItemIds.size,
5118
+ revisedPrompt: readString(item, "revised_prompt") ?? readString(item, "revisedPrompt"),
5119
+ fileNamePrefix: "codex-image-generation",
5120
+ sniffMimeType: true
5121
+ });
5122
+ if (!asset) return;
5123
+ try {
5124
+ const saved = await saveMediaBuffer(asset.buffer, asset.mimeType, GENERATED_IMAGE_MEDIA_SUBDIR, maxBytes, asset.fileName);
5125
+ this.recordNativeGeneratedMediaUrl({
5126
+ itemId,
5127
+ mediaUrl: saved.path,
5128
+ replaceExisting: true
5129
+ });
5130
+ } catch (error) {
5131
+ embeddedAgentLog.warn("codex app-server raw image generation result save failed", {
5132
+ itemId,
5133
+ error
5134
+ });
5135
+ }
5136
+ }
5137
+ recordNativeGeneratedMediaUrl(params) {
5138
+ if (this.nativeGeneratedMediaUrlsByItemId.has(params.itemId) && params.replaceExisting !== true) {
5139
+ this.nativeGeneratedMediaItemIds.add(params.itemId);
5140
+ return;
5141
+ }
5142
+ this.nativeGeneratedMediaUrlsByItemId.set(params.itemId, params.mediaUrl);
5143
+ this.nativeGeneratedMediaItemIds.add(params.itemId);
5144
+ }
5145
+ buildToolMediaUrls(toolTelemetry) {
5146
+ const mediaUrls = new Set(toolTelemetry.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
5147
+ if ((toolTelemetry.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.nativeGeneratedMediaUrlsByItemId.values()) mediaUrls.add(mediaUrl);
5148
+ return mediaUrls.size > 0 ? [...mediaUrls] : toolTelemetry.toolMediaUrls;
5149
+ }
5150
+ async maybeEndReasoning() {
5151
+ if (!this.reasoningStarted || this.reasoningEnded) return;
5152
+ this.reasoningEnded = true;
5153
+ await this.params.onReasoningEnd?.();
5154
+ }
5155
+ emitPlanUpdate(params) {
5156
+ if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
5157
+ this.emitAgentEvent({
5158
+ stream: "plan",
5159
+ data: {
5160
+ phase: "update",
5161
+ title: "Plan updated",
5162
+ source: "codex-app-server",
5163
+ ...params.explanation ? { explanation: params.explanation } : {},
5164
+ ...params.steps && params.steps.length > 0 ? { steps: params.steps } : {}
5165
+ }
5166
+ });
5167
+ }
5168
+ rememberAssistantPhase(item) {
5169
+ if (item?.type !== "agentMessage") return;
5170
+ const phase = readItemString(item, "phase");
5171
+ if (phase) this.assistantPhaseByItem.set(item.id, phase);
5172
+ }
5173
+ isCommentaryAssistantItem(itemId) {
5174
+ return this.assistantPhaseByItem.get(itemId) === "commentary";
5175
+ }
5176
+ shouldStreamAssistantPartial(itemId) {
5177
+ return this.assistantPhaseByItem.get(itemId) === "final_answer";
5178
+ }
5179
+ emitCommentaryProgress(params) {
5180
+ const progressText = params.text.replace(/\s+/g, " ").trim();
5181
+ if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
5182
+ this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
5183
+ this.emitAgentEvent({
5184
+ stream: "item",
5185
+ data: {
5186
+ itemId: params.itemId,
5187
+ kind: "preamble",
5188
+ title: "Preamble",
5189
+ phase: "update",
5190
+ progressText,
5191
+ source: "codex-app-server"
5192
+ }
5193
+ });
5194
+ }
5195
+ emitStandardItemEvent(params) {
5196
+ const { item } = params;
5197
+ if (!item) return;
5198
+ const kind = itemKind(item);
5199
+ if (!kind) return;
5200
+ const meta = itemMeta(item, this.toolProgressDetailMode());
5201
+ const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
5202
+ this.emitAgentEvent({
5203
+ stream: "item",
5204
+ data: {
5205
+ itemId: item.id,
5206
+ phase: params.phase,
5207
+ kind,
5208
+ title: itemTitle(item),
5209
+ status: params.phase === "start" ? "running" : itemStatus(item),
5210
+ ...itemName(item) ? { name: itemName(item) } : {},
5211
+ ...meta ? { meta } : {},
5212
+ ...suppressChannelProgress ? { suppressChannelProgress: true } : {}
5213
+ }
5214
+ });
5215
+ }
5216
+ async emitNormalizedToolItemEvent(params) {
5217
+ const { item } = params;
5218
+ if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
5219
+ const name = itemName(item);
5220
+ if (!name) return;
5221
+ const status = params.phase === "result" ? itemStatus(item) : "running";
5222
+ const args = itemToolArgs(item);
5223
+ const meta = itemMeta(item, this.toolProgressDetailMode());
5224
+ this.recordToolTrajectoryEvent({
5225
+ phase: params.phase,
5226
+ item,
5227
+ name,
5228
+ args,
5229
+ status
5230
+ });
5231
+ if (params.phase === "result") this.recordNativeToolError({
5232
+ item,
5233
+ name,
5234
+ meta,
5235
+ status
5236
+ });
5237
+ if (!shouldEmitTranscriptToolProgress(name, args)) {
5238
+ if (params.phase === "result") {
5239
+ this.emitAfterToolCallObservation(item);
5240
+ await this.options.onNativeToolResultRecorded?.();
5241
+ }
5242
+ return;
5243
+ }
5244
+ this.emitAgentEvent({
5245
+ stream: "tool",
5246
+ data: {
5247
+ phase: params.phase,
5248
+ name,
5249
+ itemId: item.id,
5250
+ toolCallId: item.id,
5251
+ ...meta ? { meta } : {},
5252
+ ...params.phase === "start" && args ? { args } : {},
5253
+ ...params.phase === "result" ? {
5254
+ status,
5255
+ isError: isNonSuccessItemStatus(status),
5256
+ ...itemToolResult(item)
5257
+ } : {}
5258
+ }
5259
+ });
5260
+ if (params.phase === "result") {
5261
+ this.emitAfterToolCallObservation(item);
5262
+ await this.options.onNativeToolResultRecorded?.();
5263
+ }
5264
+ }
5265
+ clearTerminalPresentationForNativeItem(item) {
5266
+ if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
5267
+ const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
5268
+ this.terminalPresentationClearedItemIds.add(item.id);
5269
+ this.params.onToolOutcome?.({
5270
+ toolName: itemName(item) ?? item.type,
5271
+ argsHash: "",
5272
+ resultHash: "",
5273
+ ...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
5274
+ terminalPresentation: void 0,
5275
+ presentationOnly: true
5276
+ });
5277
+ }
5278
+ recordNativeToolError(params) {
5279
+ if (!isNonSuccessItemStatus(params.status)) {
5280
+ if (!this.lastNativeToolError) return;
5281
+ if (!this.lastNativeToolError.mutatingAction) {
5282
+ this.lastNativeToolError = void 0;
5283
+ return;
5284
+ }
5285
+ const actionFingerprint = nativeToolActionFingerprint(params.item);
5286
+ if (this.lastNativeToolError.actionFingerprint && actionFingerprint && this.lastNativeToolError.actionFingerprint === actionFingerprint) this.lastNativeToolError = void 0;
5287
+ return;
5288
+ }
5289
+ const error = itemToolError(params.item, params.status, this.toolResultOutputTextByItem);
5290
+ const actionFingerprint = nativeToolActionFingerprint(params.item);
5291
+ this.lastNativeToolError = {
5292
+ toolName: params.name,
5293
+ ...params.meta ? { meta: params.meta } : {},
5294
+ ...error ? { error } : {},
5295
+ ...isMutatingNativeToolItem(params.item) ? { mutatingAction: true } : {},
5296
+ ...actionFingerprint ? { actionFingerprint } : {}
5297
+ };
5298
+ }
5299
+ recordToolTrajectoryEvent(params) {
5300
+ if (params.phase === "start") {
5301
+ this.toolTrajectoryCallIds.add(params.item.id);
5302
+ this.toolTrajectoryNamesById.set(params.item.id, params.name);
5303
+ this.toolTrajectoryItemsById.set(params.item.id, params.item);
5304
+ this.options.trajectoryRecorder?.recordEvent("tool.call", {
5305
+ threadId: this.threadId,
5306
+ turnId: this.turnId,
5307
+ itemId: params.item.id,
5308
+ toolCallId: params.item.id,
5309
+ name: params.name,
5310
+ arguments: params.args
5311
+ });
5312
+ return;
5313
+ }
5314
+ this.toolTrajectoryResultIds.add(params.item.id);
5315
+ const toolResult = itemToolResult(params.item).result;
5316
+ const output = itemOutputText(params.item, this.toolResultOutputTextByItem);
5317
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
5318
+ threadId: this.threadId,
5319
+ turnId: this.turnId,
5320
+ itemId: params.item.id,
5321
+ toolCallId: params.item.id,
5322
+ name: params.name,
5323
+ status: params.status,
5324
+ isError: isNonSuccessItemStatus(params.status),
5325
+ ...toolResult ? { result: toolResult } : {},
5326
+ ...output ? { output } : {}
5327
+ });
5328
+ }
5329
+ emitAfterToolCallObservation(item) {
5330
+ if (!this.shouldEmitAfterToolCallObservation(item)) return;
5331
+ const name = itemName(item);
5332
+ if (!name) return;
5333
+ const status = itemStatus(item);
5334
+ if (status === "running") return;
5335
+ this.afterToolCallObservedItemIds.add(item.id);
5336
+ const result = itemToolResult(item).result;
5337
+ const error = itemToolError(item, status, this.toolResultOutputTextByItem);
5338
+ const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
5339
+ const hookParams = {
5340
+ toolName: name,
5341
+ toolCallId: item.id,
5342
+ runId: this.params.runId,
5343
+ agentId: this.params.agentId,
5344
+ sessionId: this.params.sessionId,
5345
+ sessionKey: this.params.sessionKey,
5346
+ startArgs: itemToolArgs(item) ?? {},
5347
+ ...result !== void 0 ? { result } : {},
5348
+ ...error ? { error } : {},
5349
+ ...startedAt !== void 0 ? { startedAt } : {}
5350
+ };
5351
+ setImmediate(() => {
5352
+ runAgentHarnessAfterToolCallHook(hookParams);
5353
+ });
5354
+ }
5355
+ shouldEmitAfterToolCallObservation(item) {
5356
+ if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
5357
+ if (this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item)) return false;
5358
+ return true;
5359
+ }
5360
+ emitToolResultSummary(item) {
5361
+ if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
5362
+ const itemId = item.id;
5363
+ if (this.toolResultSummaryItemIds.has(itemId)) return;
5364
+ const toolName = itemName(item);
5365
+ if (!toolName) return;
5366
+ if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
5367
+ this.toolResultSummaryItemIds.add(itemId);
5368
+ const meta = itemMeta(item, this.toolProgressDetailMode());
5369
+ this.emitToolResultMessage({
5370
+ itemId,
5371
+ text: formatToolSummary(toolName, meta)
5372
+ });
5373
+ }
5374
+ emitToolResultOutput(item) {
5375
+ if (!item || !this.params.onToolResult || !this.shouldEmitToolOutput()) return;
5376
+ const itemId = item.id;
5377
+ if (this.toolResultOutputItemIds.has(itemId)) return;
5378
+ if (this.toolResultOutputStreamedItemIds.has(itemId)) return;
5379
+ const toolName = itemName(item);
5380
+ const output = itemOutputText(item, this.toolResultOutputTextByItem);
5381
+ if (!toolName || !output) return;
5382
+ if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
5383
+ this.emitToolResultMessage({
5384
+ itemId,
5385
+ text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
5386
+ finalOutput: true,
5387
+ isError: isNonSuccessItemStatus(itemStatus(item))
5388
+ });
5389
+ }
5390
+ emitToolResultMessage(params) {
5391
+ const text = params.text.trim();
5392
+ if (!text) return;
5393
+ this.toolProgressTexts.add(text);
5394
+ if (params.finalOutput) this.toolResultOutputItemIds.add(params.itemId);
5395
+ try {
5396
+ Promise.resolve(this.params.onToolResult?.({
5397
+ text,
5398
+ ...params.isError === true ? { isError: true } : {}
5399
+ })).catch(() => {});
5400
+ } catch {}
5401
+ }
5402
+ shouldEmitToolResult() {
5403
+ return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
5404
+ }
5405
+ shouldEmitToolOutput() {
5406
+ return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
5407
+ }
5408
+ toolProgressDetailMode() {
5409
+ return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
5410
+ }
5411
+ recordToolMeta(item) {
5412
+ if (!item) return;
5413
+ if (isSideEffectingNativeToolItem(item)) this.sideEffectingToolItemIds.add(item.id);
5414
+ else this.sideEffectingToolItemIds.delete(item.id);
5415
+ const toolName = itemName(item);
5416
+ if (!toolName) return;
5417
+ const meta = itemMeta(item, this.toolProgressDetailMode());
5418
+ const existing = this.toolMetas.get(item.id);
5419
+ this.toolMetas.set(item.id, {
5420
+ toolName,
5421
+ ...meta ? { meta } : {},
5422
+ ...existing?.asyncStarted ? { asyncStarted: true } : {}
5423
+ });
5424
+ }
5425
+ recordNativeToolTranscriptCall(item) {
5426
+ if (!item || !shouldRecordNativeToolTranscript(item)) return;
5427
+ const name = itemName(item);
5428
+ if (!name) return;
5429
+ this.recordToolTranscriptCall({
5430
+ id: item.id,
5431
+ name,
5432
+ arguments: itemToolArgs(item)
5433
+ });
5434
+ }
5435
+ recordNativeToolTranscriptResult(item) {
5436
+ if (!item || !shouldRecordNativeToolTranscript(item)) return;
5437
+ const name = itemName(item);
5438
+ if (!name) return;
5439
+ this.recordToolTranscriptResult({
5440
+ id: item.id,
5441
+ name,
5442
+ text: itemTranscriptResultText(item, this.toolResultOutputTextByItem),
5443
+ isError: isNonSuccessItemStatus(itemStatus(item))
5444
+ });
5445
+ }
5446
+ recordToolTranscriptCall(params) {
5447
+ if (!params.id || !params.name || this.toolTranscriptCallIds.has(params.id)) return;
5448
+ this.toolTranscriptCallIds.add(params.id);
5449
+ this.toolTranscriptNamesById.set(params.id, params.name);
5450
+ this.toolTranscriptArgumentsById.set(params.id, params.arguments);
5451
+ if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptToolProgressSuppressedIds.add(params.id);
5452
+ else this.transcriptToolProgressSuppressedIds.delete(params.id);
5453
+ this.emitTranscriptToolCallProgress(params);
5454
+ this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`));
5455
+ }
5456
+ recordToolTranscriptResult(params) {
5457
+ if (!params.id || !params.name || this.toolTranscriptResultIds.has(params.id)) return;
5458
+ this.toolTranscriptResultIds.add(params.id);
5459
+ this.emitTranscriptToolResultProgress(params);
5460
+ this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`));
5461
+ }
5462
+ synthesizeMissingToolResults(params) {
5463
+ if (!params.synthesize) return;
5464
+ const missingTranscriptIds = [...this.toolTranscriptCallIds].filter((id) => !this.toolTranscriptResultIds.has(id));
5465
+ const missingTrajectoryIds = [...this.toolTrajectoryCallIds].filter((id) => !this.toolTrajectoryResultIds.has(id));
5466
+ if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
5467
+ for (const id of missingTranscriptIds) {
5468
+ const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
5469
+ if (!name) continue;
5470
+ this.recordToolTranscriptResult({
5471
+ id,
5472
+ name,
5473
+ text: formatMissingToolResultError({
5474
+ id,
5475
+ name
5476
+ }),
5477
+ isError: true
5478
+ });
5479
+ }
5480
+ for (const id of missingTrajectoryIds) {
5481
+ const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
5482
+ if (!name) continue;
5483
+ this.toolTrajectoryResultIds.add(id);
5484
+ const text = formatMissingToolResultError({
5485
+ id,
5486
+ name
5487
+ });
5488
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
5489
+ threadId: this.threadId,
5490
+ turnId: this.turnId,
5491
+ itemId: id,
5492
+ toolCallId: id,
5493
+ name,
5494
+ status: "failed",
5495
+ isError: true,
5496
+ result: {
5497
+ status: "failed",
5498
+ reason: "missing_tool_result"
5499
+ },
5500
+ output: text
5501
+ });
5502
+ }
5503
+ if (!params.recordPromptError) {
5504
+ const firstMissingId = missingTranscriptIds.find((id) => {
5505
+ const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
5506
+ return Boolean(name);
5507
+ }) ?? missingTrajectoryIds.find((id) => {
5508
+ const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
5509
+ return Boolean(name);
5510
+ });
5511
+ if (firstMissingId) {
5512
+ const name = this.toolTranscriptNamesById.get(firstMissingId) ?? this.toolTrajectoryNamesById.get(firstMissingId);
5513
+ if (name) {
5514
+ const item = this.toolTrajectoryItemsById.get(firstMissingId);
5515
+ const meta = item ? itemMeta(item, this.toolProgressDetailMode()) : this.toolMetas.get(firstMissingId)?.meta;
5516
+ const actionFingerprint = item ? nativeToolActionFingerprint(item) : void 0;
5517
+ this.lastNativeToolError = {
5518
+ toolName: name,
5519
+ ...meta ? { meta } : {},
5520
+ error: formatMissingToolResultError({
5521
+ id: firstMissingId,
5522
+ name
5523
+ }),
5524
+ ...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {},
5525
+ ...actionFingerprint ? { actionFingerprint } : {}
5526
+ };
5527
+ }
5528
+ }
5529
+ return;
5530
+ }
5531
+ const missingCount = (/* @__PURE__ */ new Set([...missingTranscriptIds, ...missingTrajectoryIds])).size;
5532
+ this.synthesizedMissingToolResultError = missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
5533
+ this.promptErrorSource = this.promptErrorSource ?? "prompt";
5534
+ }
5535
+ emitTranscriptToolCallProgress(params) {
5536
+ if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
5537
+ this.transcriptToolProgressCallIds.add(params.id);
5538
+ const args = normalizeToolTranscriptArguments(params.arguments);
5539
+ const meta = inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() });
5540
+ if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.toolResultSummaryItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
5541
+ this.toolResultSummaryItemIds.add(params.id);
5542
+ this.emitToolResultMessage({
5543
+ itemId: params.id,
5544
+ text: formatToolSummary(params.name, meta)
5545
+ });
5546
+ }
5547
+ emitTranscriptToolResultProgress(params) {
5548
+ if (this.transcriptToolProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.toolTranscriptArgumentsById.get(params.id))) return;
5549
+ if (!this.transcriptToolProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
5550
+ id: params.id,
5551
+ name: params.name,
5552
+ arguments: {}
5553
+ });
5554
+ if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.toolResultOutputItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
5555
+ const text = params.text?.trim();
5556
+ if (!text) return;
5557
+ this.emitToolResultMessage({
5558
+ itemId: params.id,
5559
+ text: formatToolOutput(params.name, void 0, text),
5560
+ finalOutput: true,
5561
+ isError: params.isError
5562
+ });
5563
+ }
5564
+ formatCodexErrorMessage(params) {
5565
+ const error = isJsonObject(params.error) ? params.error : void 0;
5566
+ return formatCodexUsageLimitErrorMessage({
5567
+ message: error ? readString(error, "message") : void 0,
5568
+ codexErrorInfo: error?.codexErrorInfo,
5569
+ rateLimits: this.options.readRecentRateLimits?.()
5570
+ }) ?? readCodexErrorNotificationMessage(params);
5571
+ }
5572
+ emitAgentEvent(event) {
5573
+ try {
5574
+ emitAgentEvent({
5575
+ runId: this.params.runId,
5576
+ stream: event.stream,
5577
+ data: event.data,
5578
+ ...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
5579
+ });
5580
+ } catch (error) {
5581
+ embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
5582
+ }
5583
+ try {
5584
+ const maybePromise = this.params.onAgentEvent?.(event);
5585
+ Promise.resolve(maybePromise).catch((error) => {
5586
+ embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
5587
+ });
5588
+ } catch (error) {
5589
+ embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
5590
+ }
5591
+ }
5592
+ collectAssistantTexts() {
5593
+ const finalText = this.resolveFinalAssistantText();
5594
+ return finalText ? [finalText] : [];
5595
+ }
5596
+ hasAssistantItemTextForSynthesis() {
5597
+ for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
5598
+ const itemId = this.assistantItemOrder[i];
5599
+ if (!itemId) continue;
5600
+ if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
5601
+ const text = this.assistantTextByItem.get(itemId);
5602
+ if (text && text.length > 0) return true;
5603
+ }
5604
+ return false;
5605
+ }
5606
+ resolveFinalAssistantText() {
5607
+ return this.resolveFinalAssistantTextItem()?.text;
5608
+ }
5609
+ resolveFinalAssistantTextItem() {
5610
+ for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
5611
+ const itemId = this.assistantItemOrder[i];
5612
+ if (!itemId) continue;
5613
+ const text = this.assistantTextByItem.get(itemId)?.trim();
5614
+ if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
5615
+ if (text && !this.toolProgressTexts.has(text)) return {
5616
+ itemId,
5617
+ text
5618
+ };
5619
+ }
5620
+ }
5621
+ rememberAssistantItem(itemId) {
5622
+ if (!itemId || this.assistantItemOrder.includes(itemId)) return;
5623
+ this.assistantItemOrder.push(itemId);
5624
+ }
5625
+ createCurrentAttemptAssistantMessage() {
5626
+ for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
5627
+ const itemId = this.assistantItemOrder[i];
5628
+ if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
5629
+ const text = this.assistantTextByItem.get(itemId) ?? "";
5630
+ const normalizedText = text.trim();
5631
+ if (normalizedText && this.toolProgressTexts.has(normalizedText)) continue;
5632
+ return this.createAssistantMessage(text);
5633
+ }
5634
+ }
5635
+ async readMirroredSessionMessages() {
5636
+ return await readCodexMirroredSessionHistoryMessages({
5637
+ agentId: this.params.agentId,
5638
+ sessionFile: this.params.sessionFile,
5639
+ sessionId: this.params.sessionId,
5640
+ sessionKey: this.params.sessionKey
5641
+ }) ?? [];
5642
+ }
5643
+ createAssistantMessage(text) {
5644
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
5645
+ const usage = this.tokenUsage ? {
5646
+ input: this.tokenUsage.input ?? 0,
5647
+ output: this.tokenUsage.output ?? 0,
5648
+ cacheRead: this.tokenUsage.cacheRead ?? 0,
5649
+ cacheWrite: this.tokenUsage.cacheWrite ?? 0,
5650
+ totalTokens: this.tokenUsage.total ?? (this.tokenUsage.input ?? 0) + (this.tokenUsage.output ?? 0) + (this.tokenUsage.cacheRead ?? 0) + (this.tokenUsage.cacheWrite ?? 0),
5651
+ cost: ZERO_USAGE.cost
5652
+ } : ZERO_USAGE;
5653
+ return {
5654
+ role: "assistant",
5655
+ content: [{
5656
+ type: "text",
5657
+ text
5658
+ }],
5659
+ api: attribution.api ?? "openai-chatgpt-responses",
5660
+ provider: attribution.provider,
5661
+ model: this.params.modelId,
5662
+ usage,
5663
+ stopReason: this.aborted ? "aborted" : this.promptError ? "error" : "stop",
5664
+ errorMessage: this.promptError ? formatErrorMessage(this.promptError) : void 0,
5665
+ timestamp: Date.now()
5666
+ };
5667
+ }
5668
+ createAssistantMirrorMessage(title, text) {
5669
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
5670
+ return {
5671
+ role: "assistant",
5672
+ content: [{
5673
+ type: "text",
5674
+ text: `${title}:\n${text}`
5675
+ }],
5676
+ api: attribution.api ?? "openai-chatgpt-responses",
5677
+ provider: attribution.provider,
5678
+ model: this.params.modelId,
5679
+ usage: ZERO_USAGE,
5680
+ stopReason: "stop",
5681
+ timestamp: Date.now()
5682
+ };
5683
+ }
5684
+ createToolCallMessage(params) {
5685
+ const args = normalizeToolTranscriptArguments(params.arguments);
5686
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
5687
+ return {
5688
+ role: "assistant",
5689
+ content: [{
5690
+ type: "toolCall",
5691
+ id: params.id,
5692
+ name: params.name,
5693
+ arguments: args,
5694
+ input: args
5695
+ }],
5696
+ api: attribution.api ?? "openai-chatgpt-responses",
5697
+ provider: attribution.provider,
5698
+ model: this.params.modelId,
5699
+ usage: ZERO_USAGE,
5700
+ stopReason: "toolUse",
5701
+ timestamp: Date.now()
5702
+ };
5703
+ }
5704
+ createToolResultMessage(params) {
5705
+ const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
5706
+ return {
5707
+ role: "toolResult",
5708
+ toolCallId: params.id,
5709
+ toolName: params.name,
5710
+ isError: params.isError,
5711
+ content: [{
5712
+ type: "toolResult",
5713
+ id: params.id,
5714
+ name: params.name,
5715
+ toolName: params.name,
5716
+ toolCallId: params.id,
5717
+ toolUseId: params.id,
5718
+ tool_use_id: params.id,
5719
+ content: text,
5720
+ text
5721
+ }],
5722
+ timestamp: Date.now()
5723
+ };
5724
+ }
5725
+ isNotificationForTurn(params) {
5726
+ const threadId = readCodexNotificationThreadId(params);
5727
+ const turnId = readCodexNotificationTurnId(params);
5728
+ return threadId === this.threadId && turnId === this.turnId;
5729
+ }
5730
+ isHookNotificationForCurrentThread(params) {
5731
+ const threadId = readString(params, "threadId");
5732
+ const turnId = params.turnId;
5733
+ return threadId === this.threadId && (turnId === this.turnId || turnId === null);
5734
+ }
5735
+ };
5736
+ function isHookNotificationMethod(method) {
5737
+ return method === "hook/started" || method === "hook/completed";
5738
+ }
5739
+ function readString(record, key) {
5740
+ const value = record[key];
5741
+ return typeof value === "string" ? value : void 0;
5742
+ }
5743
+ function estimateBase64DecodedBytes(base64) {
5744
+ let nonWhitespaceLength = 0;
5745
+ let previousCode = -1;
5746
+ let lastCode = -1;
5747
+ for (let i = 0; i < base64.length; i += 1) {
5748
+ const code = base64.charCodeAt(i);
5749
+ if (isBase64WhitespaceCode(code)) continue;
5750
+ nonWhitespaceLength += 1;
5751
+ previousCode = lastCode;
5752
+ lastCode = code;
5753
+ }
5754
+ if (nonWhitespaceLength === 0) return;
5755
+ const equalsCode = "=".charCodeAt(0);
5756
+ const padding = lastCode === equalsCode ? previousCode === equalsCode ? 2 : 1 : 0;
5757
+ return Math.max(0, Math.floor(nonWhitespaceLength * 3 / 4) - padding);
5758
+ }
5759
+ function isBase64WhitespaceCode(code) {
5760
+ return code === 32 || code === 9 || code === 10 || code === 13;
5761
+ }
5762
+ function resolveGeneratedImageMaxBytes(config) {
5763
+ const configured = config?.agents?.defaults?.mediaMaxMb;
5764
+ if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * BYTES_PER_MB);
5765
+ return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
5766
+ }
5767
+ function normalizeNonEmptyString(value) {
5768
+ if (typeof value !== "string") return;
5769
+ return value.trim() || void 0;
5770
+ }
5771
+ function readNonEmptyString(record, key) {
5772
+ return normalizeNonEmptyString(record[key]);
5773
+ }
5774
+ function readNonEmptyStringArray(record, key) {
5775
+ const value = record[key];
5776
+ if (!Array.isArray(value)) return [];
5777
+ const entries = [];
5778
+ for (const entry of value) {
5779
+ const normalized = normalizeNonEmptyString(entry);
5780
+ if (normalized) entries.push(normalized);
5781
+ }
5782
+ return entries;
5783
+ }
5784
+ function readNullableString(record, key) {
5785
+ const value = record[key];
5786
+ if (value === null) return null;
5787
+ return typeof value === "string" ? value : void 0;
5788
+ }
5789
+ function readNumber(record, key) {
5790
+ const value = record[key];
5791
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
5792
+ }
5793
+ function resolveStartedAtFromDurationMs(durationMs) {
5794
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
5795
+ return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
5796
+ }
5797
+ function readNonNegativeInteger(record, key) {
5798
+ const value = readNumber(record, key);
5799
+ return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
5800
+ }
5801
+ function readCodexErrorNotificationMessage(record) {
5802
+ const error = record.error;
5803
+ return isJsonObject(error) ? readString(error, "message") : void 0;
5804
+ }
5805
+ function readHookOutputEntries(value) {
5806
+ if (!Array.isArray(value)) return [];
5807
+ return value.flatMap((entry) => {
5808
+ if (!isJsonObject(entry)) return [];
5809
+ const text = readString(entry, "text");
5810
+ if (!text) return [];
5811
+ const kind = readString(entry, "kind");
5812
+ return [{
5813
+ ...kind ? { kind } : {},
5814
+ text
5815
+ }];
5816
+ });
5817
+ }
5818
+ function normalizeCodexTokenUsage(record) {
5819
+ const inputTokens = readNumber(record, "inputTokens");
5820
+ const cacheRead = readNumber(record, "cachedInputTokens");
5821
+ return normalizeUsage({
5822
+ input: inputTokens !== void 0 && cacheRead !== void 0 ? Math.max(0, inputTokens - cacheRead) : inputTokens,
5823
+ output: readNumber(record, "outputTokens"),
5824
+ cacheRead,
5825
+ total: readNumber(record, "totalTokens")
5826
+ });
5827
+ }
5828
+ function splitPlanText(text) {
5829
+ return text.split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
5830
+ }
5831
+ function collectTextValues(map) {
5832
+ return [...map.values()].filter((text) => text.trim().length > 0);
5833
+ }
5834
+ function collectReasoningTextValues(groups, itemOrder) {
5835
+ return [...groups.values()].toSorted((left, right) => {
5836
+ const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
5837
+ if (itemDelta !== 0) return itemDelta;
5838
+ const methodDelta = reasoningMethodOrder(left.method) - reasoningMethodOrder(right.method);
5839
+ return methodDelta !== 0 ? methodDelta : left.index - right.index;
5840
+ }).map((group) => group.text).filter((text) => text.trim().length > 0);
5841
+ }
5842
+ function reasoningMethodOrder(method) {
5843
+ return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
5844
+ }
5845
+ function extractRawAssistantText(item) {
5846
+ return (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
5847
+ if (!isJsonObject(entry)) return [];
5848
+ const type = readString(entry, "type");
5849
+ if (type !== "output_text" && type !== "text") return [];
5850
+ const value = readString(entry, "text");
5851
+ return value ? [value] : [];
5852
+ }).join("").trim() || void 0;
5853
+ }
5854
+ function itemKind(item) {
5855
+ switch (item.type) {
5856
+ case "dynamicToolCall":
5857
+ case "mcpToolCall": return "tool";
5858
+ case "commandExecution": return "command";
5859
+ case "fileChange": return "patch";
5860
+ case "webSearch": return "search";
5861
+ case "reasoning":
5862
+ case "contextCompaction": return "analysis";
5863
+ default: return;
5864
+ }
5865
+ }
5866
+ function itemTitle(item) {
5867
+ switch (item.type) {
5868
+ case "commandExecution": return "Command";
5869
+ case "fileChange": return "File change";
5870
+ case "mcpToolCall": return "MCP tool";
5871
+ case "dynamicToolCall": return "Tool";
5872
+ case "webSearch": return "Web search";
5873
+ case "contextCompaction": return "Context compaction";
5874
+ case "reasoning": return "Reasoning";
5875
+ default: return item.type;
5876
+ }
5877
+ }
5878
+ function itemStatus(item) {
5879
+ const status = readItemString(item, "status");
5880
+ if (status === "failed" || status === "error") return "failed";
5881
+ if (status === "declined") return "blocked";
5882
+ if (status === "inProgress" || status === "in_progress" || status === "running") return "running";
5883
+ return "completed";
5884
+ }
5885
+ function auditNativeToolTerminalStatus(item) {
5886
+ if (item.type === "imageView" || item.type === "sleep") return "completed";
5887
+ const status = readItemString(item, "status");
5888
+ if (status === "completed") return "completed";
5889
+ if (status === "failed" || status === "error") return "failed";
5890
+ if (status === "declined") return "blocked";
5891
+ return "unknown";
5892
+ }
5893
+ function auditNativeToolUnfinishedStatus(item) {
5894
+ return item.type === "webSearch" || item.type === "imageGeneration" ? "unknown" : "failed";
5895
+ }
5896
+ function formatMissingToolResultError(params) {
5897
+ return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
5898
+ }
5899
+ function isNonSuccessItemStatus(status) {
5900
+ return status === "failed" || status === "blocked";
5901
+ }
5902
+ function itemName(item) {
5903
+ if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
5904
+ if (item.type === "mcpToolCall" && typeof item.tool === "string") {
5905
+ const server = typeof item.server === "string" ? item.server : void 0;
5906
+ return server ? `${server}.${item.tool}` : item.tool;
5907
+ }
5908
+ if (item.type === "commandExecution") return "bash";
5909
+ if (item.type === "fileChange") return "apply_patch";
5910
+ if (item.type === "webSearch") return "web_search";
5911
+ }
5912
+ function auditNativeToolName(item) {
5913
+ if (item.type === "dynamicToolCall") return;
5914
+ const progressName = itemName(item);
5915
+ if (progressName) return progressName;
5916
+ if (item.type === "collabAgentToolCall") return typeof item.tool === "string" && item.tool.trim() ? `collab.${item.tool.trim()}` : "collab_agent";
5917
+ if (item.type === "imageGeneration") return "image_generation";
5918
+ if (item.type === "imageView") return "image_view";
5919
+ if (item.type === "sleep") return "sleep";
5920
+ }
5921
+ function isSideEffectingNativeToolItem(item) {
5922
+ return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
5923
+ }
5924
+ function shouldSynthesizeToolProgressForItem(item) {
5925
+ switch (item.type) {
5926
+ case "commandExecution":
5927
+ case "fileChange":
5928
+ case "webSearch":
5929
+ case "mcpToolCall": return true;
5930
+ default: return false;
5931
+ }
5932
+ }
5933
+ function shouldRecordNativeToolTranscript(item) {
5934
+ return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
5935
+ }
5936
+ function isMutatingNativeToolItem(item) {
5937
+ if (item.type === "commandExecution") return true;
5938
+ return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
5939
+ }
5940
+ function shouldClearTerminalPresentationForNativeItem(item) {
5941
+ switch (item.type) {
5942
+ case "collabAgentToolCall":
5943
+ case "commandExecution":
5944
+ case "fileChange":
5945
+ case "imageGeneration":
5946
+ case "imageView":
5947
+ case "mcpToolCall":
5948
+ case "webSearch": return true;
5949
+ default: return false;
5950
+ }
5951
+ }
5952
+ function nativeToolActionFingerprint(item) {
5953
+ if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
5954
+ type: item.type,
5955
+ command: item.command,
5956
+ cwd: typeof item.cwd === "string" ? item.cwd : ""
5957
+ });
5958
+ if (item.type === "fileChange") return JSON.stringify({
5959
+ type: item.type,
5960
+ changes: itemFileChanges(item)
5961
+ });
5962
+ }
5963
+ function isNativePostToolUseRelayItem(item) {
5964
+ switch (item.type) {
5965
+ case "commandExecution":
5966
+ case "fileChange":
5967
+ case "mcpToolCall": return true;
5968
+ default: return false;
5969
+ }
5970
+ }
5971
+ function shouldSuppressChannelProgressForItem(item) {
5972
+ if (shouldSynthesizeToolProgressForItem(item)) return true;
5973
+ return item.type === "dynamicToolCall";
5974
+ }
5975
+ function itemToolArgs(item) {
5976
+ if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
5977
+ command: item.command,
5978
+ ...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
5979
+ });
5980
+ if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChanges(item) });
5981
+ if (item.type === "webSearch") return webSearchToolArgs(item);
5982
+ if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
5983
+ }
5984
+ function webSearchToolArgs(item) {
5985
+ const action = isJsonObject(item.action) ? item.action : void 0;
5986
+ const actionType = action ? readNonEmptyString(action, "type") : void 0;
5987
+ const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
5988
+ const query = normalizeNonEmptyString(item.query) ?? (action && actionType === "search" ? readNonEmptyString(action, "query") : void 0) ?? queries[0];
5989
+ const url = action ? readNonEmptyString(action, "url") : void 0;
5990
+ const pattern = action ? readNonEmptyString(action, "pattern") : void 0;
5991
+ const args = {};
5992
+ if (query) args.query = query;
5993
+ if (queries.length > 0) args.queries = queries;
5994
+ if (actionType && actionType !== "search") args.action = actionType;
5995
+ if (url) args.url = url;
5996
+ if (pattern) args.pattern = pattern;
5997
+ if (!query && !url && !pattern) args.queryUnavailable = true;
5998
+ return sanitizeCodexAgentEventRecord(args);
5999
+ }
6000
+ function itemToolResult(item) {
6001
+ if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
6002
+ status: item.status,
6003
+ exitCode: item.exitCode,
6004
+ durationMs: item.durationMs
6005
+ }) };
6006
+ if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
6007
+ status: item.status,
6008
+ changes: itemFileChanges(item)
6009
+ }) };
6010
+ if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
6011
+ status: item.status,
6012
+ durationMs: item.durationMs,
6013
+ ...item.error ? { error: item.error } : {},
6014
+ ...item.result ? { result: item.result } : {}
6015
+ }) };
6016
+ if (item.type === "webSearch") return { result: webSearchToolResult(item) };
6017
+ return {};
6018
+ }
6019
+ function webSearchToolResult(item) {
6020
+ return sanitizeCodexAgentEventRecord({
6021
+ status: itemStatus(item),
6022
+ ...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
6023
+ ...webSearchToolArgs(item)
6024
+ });
6025
+ }
6026
+ function itemFileChanges(item) {
6027
+ return Array.isArray(item.changes) ? item.changes.map((change) => ({
6028
+ path: change.path,
6029
+ kind: change.kind
6030
+ })) : [];
6031
+ }
6032
+ function itemToolError(item, status, outputTextByItem) {
6033
+ if (status === "blocked") return "codex native tool blocked";
6034
+ if (status !== "failed") return;
6035
+ return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
6036
+ }
6037
+ function itemMeta(item, detailMode = "explain") {
6038
+ if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
6039
+ command: item.command,
6040
+ cwd: typeof item.cwd === "string" ? item.cwd : void 0
6041
+ }, { detailMode });
6042
+ if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
6043
+ const toolName = itemName(item);
6044
+ if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
6045
+ }
6046
+ function itemOutputText(item, outputTextByItem) {
6047
+ if (item.type === "commandExecution") return item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim() || void 0;
6048
+ if (item.type === "dynamicToolCall") return collectDynamicToolContentText(item.contentItems).trim() || void 0;
6049
+ if (item.type === "mcpToolCall") {
6050
+ if (item.error) return stringifyJsonValue(item.error);
6051
+ return item.result ? stringifyJsonValue(item.result) : void 0;
6052
+ }
6053
+ }
6054
+ function itemTranscriptResultText(item, outputTextByItem) {
6055
+ const output = itemOutputText(item, outputTextByItem);
6056
+ if (output) return output;
6057
+ const result = itemToolResult(item).result;
6058
+ return result ? stringifyJsonValue(result) : itemStatus(item);
6059
+ }
6060
+ function appendToolOutputDeltaText(outputTextByItem, itemId, delta) {
6061
+ const current = outputTextByItem.get(itemId) ?? "";
6062
+ if (current.length >= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return;
6063
+ const remaining = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - current.length;
6064
+ const next = current + (delta.length > remaining ? delta.slice(0, remaining) : delta);
6065
+ outputTextByItem.set(itemId, next);
6066
+ }
6067
+ function normalizeToolTranscriptArguments(value) {
6068
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
6069
+ return value;
6070
+ }
6071
+ function collectDynamicToolContentText(contentItems) {
6072
+ if (!Array.isArray(contentItems)) return "";
6073
+ return contentItems.flatMap((entry) => {
6074
+ if (!isJsonObject(entry)) return [];
6075
+ const text = readString(entry, "text");
6076
+ return text ? [text] : [];
6077
+ }).join("\n");
6078
+ }
6079
+ function truncateToolTranscriptText(text) {
6080
+ if (text.length <= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return text;
6081
+ return `${text.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)}\n...(truncated)...`;
6082
+ }
6083
+ function toolResultStatusText(params) {
6084
+ return params.isError ? `${params.name} failed` : `${params.name} completed`;
6085
+ }
6086
+ function stringifyJsonValue(value) {
6087
+ try {
6088
+ return JSON.stringify(value, null, 2);
6089
+ } catch {
6090
+ return;
6091
+ }
6092
+ }
6093
+ function formatToolSummary(toolName, meta) {
6094
+ const trimmedMeta = meta?.trim();
6095
+ return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
6096
+ }
6097
+ function formatToolOutput(toolName, meta, output) {
6098
+ const formattedOutput = formatToolProgressOutput(output);
6099
+ if (!formattedOutput) return formatToolSummary(toolName, meta);
6100
+ const fence = markdownFenceForText(formattedOutput);
6101
+ return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
6102
+ }
6103
+ function markdownFenceForText(text) {
6104
+ return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
6105
+ }
6106
+ function longestBacktickRun(value) {
6107
+ let longest = 0;
6108
+ let current = 0;
6109
+ for (const char of value) {
6110
+ if (char === "`") {
6111
+ current += 1;
6112
+ longest = Math.max(longest, current);
6113
+ continue;
6114
+ }
6115
+ current = 0;
6116
+ }
6117
+ return longest;
6118
+ }
6119
+ function readItemString(item, key) {
6120
+ const value = item[key];
6121
+ return typeof value === "string" ? value : void 0;
6122
+ }
6123
+ function readItem(value) {
6124
+ if (!isJsonObject(value)) return;
6125
+ const type = typeof value.type === "string" ? value.type : void 0;
6126
+ const id = typeof value.id === "string" ? value.id : void 0;
6127
+ if (!type || !id) return;
6128
+ return value;
6129
+ }
6130
+ //#endregion
6131
+ //#region extensions/codex/src/app-server/provider-capabilities.ts
6132
+ async function readConfiguredProviderWebSearchSupport(params) {
6133
+ return (await params.client.request("modelProvider/capabilities/read", {}, {
6134
+ timeoutMs: params.timeoutMs,
6135
+ signal: params.signal
6136
+ })).webSearch ? "supported" : "unsupported";
6137
+ }
6138
+ async function resolveCodexProviderWebSearchSupportForClient(params) {
6139
+ const modelProviderOverride = params.modelProviderOverride?.trim().toLowerCase();
6140
+ if (modelProviderOverride === "openai") return "supported";
6141
+ if (modelProviderOverride) return "unsupported";
6142
+ try {
6143
+ return await readConfiguredProviderWebSearchSupport(params);
6144
+ } catch {
6145
+ return "unknown";
6146
+ }
6147
+ }
6148
+ async function resolveCodexProviderWebSearchSupport(params) {
6149
+ let client;
6150
+ try {
6151
+ client = await params.clientFactory({
6152
+ startOptions: params.appServer.start,
6153
+ authProfileId: params.authProfileId,
6154
+ agentDir: params.agentDir,
6155
+ config: params.config,
6156
+ timeoutMs: params.appServer.requestTimeoutMs
6157
+ });
3352
6158
  return await resolveCodexProviderWebSearchSupportForClient({
3353
6159
  client,
3354
6160
  timeoutMs: params.appServer.requestTimeoutMs,
@@ -3362,4 +6168,4 @@ async function resolveCodexProviderWebSearchSupport(params) {
3362
6168
  }
3363
6169
  }
3364
6170
  //#endregion
3365
- export { CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS as A, resolveCodexMessageToolProvider as C, shouldWarnCodexDynamicToolBuildStageSummary as D, shouldRequireCodexSandboxExecServerEnvironment as E, resolveCodexTurnTerminalIdleTimeoutMs as F, withCodexStartupTimeout as I, resolveCodexStartupTimeoutMs as M, resolveCodexTurnAssistantCompletionIdleTimeoutMs as N, filterToolsForVisionInputs as O, resolveCodexTurnCompletionIdleTimeoutMs as P, resolveCodexExternalSandboxPolicyForOpenClawSandbox as S, shouldEnableCodexAppServerNativeToolSurface as T, createCodexDynamicToolBuildStageTracker as _, buildCodexNativeHookRelayConfig as a, resolveCodexAppServerExecutionCwd as b, resolveCodexNativeHookRelayEvents as c, handleCodexAppServerElicitationRequest as d, createCodexDynamicToolBridge as f, buildDynamicTools as g, emitDynamicToolTerminalDiagnostic as h, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as i, resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs as j, handleCodexAppServerApprovalRequest as k, resolveCodexNativeHookRelayTtlMs as l, emitDynamicToolStartedDiagnostic as m, resolveCodexProviderWebSearchSupportForClient as n, buildCodexNativeHookRelayDisabledConfig as o, emitDynamicToolErrorDiagnostic as p, CODEX_NATIVE_HOOK_RELAY_EVENTS as r, createCodexNativeHookRelay as s, resolveCodexProviderWebSearchSupport as t, scheduleCodexNativeHookRelayUnregister as u, disableCodexPluginThreadConfig as v, resolveCodexSandboxEnvironmentSelection as w, resolveCodexAppServerHookChannelId as x, formatCodexDynamicToolBuildStageSummary as y };
6171
+ export { isDynamicToolTerminalDiagnosticEvent as $, createCodexDynamicToolBuildStageTracker as A, shouldWarnCodexDynamicToolBuildStageSummary as B, resolveCodexLocalRuntimeAttribution as C, emitDynamicToolStartedDiagnostic as D, emitDynamicToolErrorDiagnostic as E, resolveCodexExternalSandboxPolicyForOpenClawSandbox as F, resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs as G, readCodexMirroredSessionHistoryMessages as H, resolveCodexMessageToolProvider as I, resolveCodexTurnCompletionIdleTimeoutMs as J, resolveCodexStartupTimeoutMs as K, resolveCodexSandboxEnvironmentSelection as L, formatCodexDynamicToolBuildStageSummary as M, resolveCodexAppServerExecutionCwd as N, emitDynamicToolTerminalDiagnostic as O, resolveCodexAppServerHookChannelId as P, hasPendingDynamicToolTerminalDiagnostic as Q, shouldEnableCodexAppServerNativeToolSurface as R, scheduleCodexNativeHookRelayUnregister as S, createCodexDynamicToolBridge as T, handleCodexAppServerApprovalRequest as U, filterToolsForVisionInputs as V, CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS as W, withCodexStartupTimeout as X, resolveCodexTurnTerminalIdleTimeoutMs as Y, handleDynamicToolCallWithTimeout as Z, buildCodexNativeHookRelayDisabledConfig as _, shouldEmitTranscriptToolProgress as a, toCodexDynamicToolProgressResponse as at, resolveCodexNativeHookRelayEvents as b, mirrorPromptAtTurnStartBestEffort as c, resolveCodexToolProgressDetailMode as d, isMatchingDynamicToolTerminalDiagnostic as et, sanitizeCodexToolArguments as f, buildCodexNativeHookRelayConfig as g, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as h, CodexNativeToolLifecycleProjector as i, shouldReleaseTurnAfterTerminalDynamicTool as it, disableCodexPluginThreadConfig as j, buildDynamicTools as k, mirrorTranscriptBestEffort as l, CODEX_NATIVE_HOOK_RELAY_EVENTS as m, resolveCodexProviderWebSearchSupportForClient as n, resolveTerminalDynamicToolBatchAction as nt, buildCodexUserPromptMessage as o, toCodexDynamicToolProtocolResponse as ot, sanitizeCodexToolResponse as p, resolveCodexTurnAssistantCompletionIdleTimeoutMs as q, CodexAppServerEventProjector as r, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as rt, createCodexAppServerUserMessagePersistenceNotifier as s, resolveCodexToolAbortTerminalReason as st, resolveCodexProviderWebSearchSupport as t, resolveDynamicToolCallTimeoutMs as tt, inferCodexDynamicToolMeta as u, createCodexNativeHookRelay as v, handleCodexAppServerElicitationRequest as w, resolveCodexNativeHookRelayTtlMs as x, emitCodexNativePreToolUseFailureDiagnostic as y, shouldRequireCodexSandboxExecServerEnvironment as z };