@openclaw/codex 2026.7.1-beta.2 → 2026.7.1-beta.5

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-B7hqD3Xj.js → app-server-policy-BUk0GLMy.js} +5 -5
  2. package/dist/{attempt-notifications-tqyQ_Tdn.js → attempt-notifications-BGsEIIDI.js} +5 -40
  3. package/dist/{command-handlers-C9K-KRU-.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-CpurU-G9.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 +1117 -19
  12. package/dist/{media-understanding-provider-BOeoBhEw.js → media-understanding-provider-DgqRvU2_.js} +13 -8
  13. package/dist/media-understanding-provider.js +1 -1
  14. package/dist/{models-DpzY1xpp.js → models-DDmO1zwd.js} +2 -2
  15. package/dist/notification-correlation-Bo7KB3ks.js +35 -0
  16. package/dist/{plugin-app-cache-key-BrZUG9jj.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-CJiWigAJ.js → protocol-validators-dZQ-UTOa.js} +116 -59
  19. package/dist/{provider-capabilities-wqedK49Z.js → provider-capabilities-gTCwjfmh.js} +2916 -114
  20. package/dist/{provider-nmtMDcGH.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-DikbXia_.js → run-attempt-8SPLiDlT.js} +907 -3002
  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-Ca9wqmd0.js → side-question-DkjXvYv7.js} +132 -140
  29. package/dist/{thread-lifecycle-D6R40plk.js → thread-lifecycle-BskXnNP-.js} +609 -444
  30. package/dist/{web-search-provider.runtime-B6eZEFmd.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-DfFu9JH_.js +0 -15
  36. package/dist/compact-CILptrPS.js +0 -278
  37. package/dist/conversation-binding-DOJtUxOy.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-D6R40plk.js";
3
- import { c as isTrustedCodexModelBackedOpenAIProvider, u as readCodexPluginConfig } from "./config-C_whBx3M.js";
4
- import { a as formatCodexDisplayText } from "./app-server-policy-B7hqD3Xj.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,7 @@ 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([
2172
+ const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
1788
2173
  "agents_list",
1789
2174
  "sessions_spawn",
1790
2175
  "sessions_yield"
@@ -1850,7 +2235,7 @@ function createCodexDynamicToolBridge(params) {
1850
2235
  return getChannelAgentToolMeta(tool) === void 0;
1851
2236
  };
1852
2237
  const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
1853
- const directToolNames = new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
2238
+ const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
1854
2239
  return {
1855
2240
  availableSpecs: createCodexDynamicToolSpecs({
1856
2241
  entries: availableTools,
@@ -1918,6 +2303,7 @@ function createCodexDynamicToolBridge(params) {
1918
2303
  executionPrevented = consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
1919
2304
  const telemetryRawResult = sanitizeToolResult(rawResult);
1920
2305
  const rawIsError = isCodexToolResultError(rawResult);
2306
+ const rawResultFailureKind = resolveToolResultFailureKind(rawResult);
1921
2307
  const middlewareResult = await middlewareRunner.applyToolResultMiddleware({
1922
2308
  threadId: call.threadId,
1923
2309
  turnId: call.turnId,
@@ -1936,7 +2322,16 @@ function createCodexDynamicToolBridge(params) {
1936
2322
  result: middlewareResult
1937
2323
  });
1938
2324
  const resultIsError = rawIsError || isCodexToolResultError(result);
1939
- 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);
1940
2335
  runAgentHarnessAfterToolCallHook({
1941
2336
  toolName,
1942
2337
  toolCallId: call.callId,
@@ -1970,11 +2365,12 @@ function createCodexDynamicToolBridge(params) {
1970
2365
  isError: resultIsError,
1971
2366
  messagingTarget: confirmedMessagingTarget
1972
2367
  });
1973
- const terminalType = inferToolResultDiagnosticTerminalType(result, resultIsError);
2368
+ const terminalType = resultFailureKind === "blocked" ? "blocked" : resultIsError ? "error" : "completed";
1974
2369
  const response = withDiagnosticTerminalType({
1975
2370
  contentItems: convertToolContents(result.content, toolResultMaxChars),
1976
2371
  success: !resultIsError
1977
2372
  }, terminalType);
2373
+ withDiagnosticFailureDisposition(response, resultFailureKind);
1978
2374
  const blocksSourceReplyTermination = hasExplicitNonSourceMessageRoute(executedArgs, params.hookContext, confirmedMessagingTarget);
1979
2375
  const deliveredSourceReply = isDeliveredMessageToolOnlySourceReplyResult({
1980
2376
  sourceReplyDeliveryMode: params.hookContext?.sourceReplyDeliveryMode,
@@ -1999,11 +2395,12 @@ function createCodexDynamicToolBridge(params) {
1999
2395
  withDynamicToolAsyncStarted(response, asyncStarted);
2000
2396
  return withSideEffectEvidence(response, !(executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs)));
2001
2397
  } catch (error) {
2002
- 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.");
2003
2400
  const adjustedExecutedArgs = consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
2004
2401
  if (isRecord(adjustedExecutedArgs)) executedArgs = structuredClone(adjustedExecutedArgs);
2005
2402
  executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
2006
- const failedResult = failedToolResult(errorMessage);
2403
+ const failedResult = failedToolResult(errorMessage, executionDisposition);
2007
2404
  finalizeToolTerminalPresentation({
2008
2405
  toolCallId: call.callId,
2009
2406
  runId: toolResultHookContext.runId,
@@ -2034,13 +2431,13 @@ function createCodexDynamicToolBridge(params) {
2034
2431
  startedAt
2035
2432
  });
2036
2433
  const replaySafe = !didStartExecution || executionPrevented || isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
2037
- return withSideEffectEvidence(withDiagnosticTerminalType({
2434
+ return withSideEffectEvidence(withDiagnosticFailureDisposition({
2038
2435
  contentItems: [{
2039
2436
  type: "inputText",
2040
2437
  text: errorMessage
2041
2438
  }],
2042
2439
  success: false
2043
- }, "error"), didStartExecution && !replaySafe);
2440
+ }, executionDisposition), didStartExecution && !replaySafe);
2044
2441
  }
2045
2442
  }
2046
2443
  };
@@ -2056,14 +2453,14 @@ function notifyAgentToolResult(observer, toolName, result, isError) {
2056
2453
  embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${toolName} error=${String(error)}`);
2057
2454
  }
2058
2455
  }
2059
- function failedToolResult(message) {
2456
+ function failedToolResult(message, status = "failed") {
2060
2457
  return {
2061
2458
  content: [{
2062
2459
  type: "text",
2063
2460
  text: message
2064
2461
  }],
2065
2462
  details: {
2066
- status: "failed",
2463
+ status,
2067
2464
  error: message
2068
2465
  }
2069
2466
  };
@@ -2238,6 +2635,7 @@ function warnQuarantinedDynamicTools(tools) {
2238
2635
  function emitQuarantinedDynamicToolDiagnostics(tools, ctx) {
2239
2636
  for (const tool of tools) emitTrustedDiagnosticEvent({
2240
2637
  type: "tool.execution.blocked",
2638
+ agentId: ctx?.agentId,
2241
2639
  runId: ctx?.runId,
2242
2640
  sessionId: ctx?.sessionId,
2243
2641
  sessionKey: ctx?.sessionKey,
@@ -2394,13 +2792,6 @@ function isAsyncStartedToolResult(result) {
2394
2792
  const details = result.details;
2395
2793
  return isRecord(details) && details.async === true && details.status === "started";
2396
2794
  }
2397
- function inferToolResultDiagnosticTerminalType(result, isError) {
2398
- const details = result.details;
2399
- if (isRecord(details) && typeof details.status === "string") {
2400
- if (details.status.trim().toLowerCase() === "blocked") return "blocked";
2401
- }
2402
- return isError ? "error" : "completed";
2403
- }
2404
2795
  function withDiagnosticTerminalType(response, terminalType) {
2405
2796
  Object.defineProperty(response, "diagnosticTerminalType", {
2406
2797
  configurable: true,
@@ -2409,6 +2800,16 @@ function withDiagnosticTerminalType(response, terminalType) {
2409
2800
  });
2410
2801
  return response;
2411
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
+ }
2412
2813
  function withSideEffectEvidence(response, sideEffectEvidence) {
2413
2814
  if (!sideEffectEvidence) return response;
2414
2815
  Object.defineProperty(response, "sideEffectEvidence", {
@@ -2645,7 +3046,7 @@ async function handleCodexAppServerElicitationRequest(params) {
2645
3046
  }
2646
3047
  function matchesCurrentThread(requestParams, threadId) {
2647
3048
  if (!requestParams) return false;
2648
- return readString(requestParams, "threadId") === threadId;
3049
+ return readString$1(requestParams, "threadId") === threadId;
2649
3050
  }
2650
3051
  function turnIdMismatches(requestParams, turnId) {
2651
3052
  const rawTurnId = requestParams?.turnId;
@@ -2660,6 +3061,7 @@ function resolvePluginElicitation(params) {
2660
3061
  const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
2661
3062
  const context = params.pluginAppPolicyContext;
2662
3063
  const entries = context ? Object.values(context.apps) : [];
3064
+ const pluginEntries = entries.filter(isPluginAppPolicyContextEntry);
2663
3065
  const appId = readFirstString(meta, PLUGIN_APP_ID_META_KEYS) ?? readFirstString(requestParams, PLUGIN_APP_ID_META_KEYS);
2664
3066
  const connectorId = readFirstString(meta, PLUGIN_CONNECTOR_ID_META_KEYS);
2665
3067
  const isCodexConnectorApproval = isCodexConnectorApprovalElicitation(requestParams, meta);
@@ -2673,6 +3075,10 @@ function resolvePluginElicitation(params) {
2673
3075
  reason: "missing_policy_context"
2674
3076
  };
2675
3077
  const entry = context.apps[appId];
3078
+ if (entry?.source === "account" && !isCodexConnectorApproval) return {
3079
+ kind: "decline",
3080
+ reason: "account_app_source_mismatch"
3081
+ };
2676
3082
  return uniquePluginMatch(entry ? [entry] : [], "app_id");
2677
3083
  }
2678
3084
  if (isCodexConnectorApproval && connectorId) {
@@ -2683,7 +3089,7 @@ function resolvePluginElicitation(params) {
2683
3089
  const entry = context.apps[connectorId];
2684
3090
  return uniquePluginMatch(entry ? [entry] : [], "connector_id");
2685
3091
  }
2686
- const serverName = readString(requestParams, "serverName");
3092
+ const serverName = readString$1(requestParams, "serverName");
2687
3093
  if (serverName && context) {
2688
3094
  const matches = entries.filter((entry) => entry.mcpServerNames.includes(serverName));
2689
3095
  if (matches.length > 0) return uniquePluginMatch(matches, "server_name");
@@ -2691,7 +3097,7 @@ function resolvePluginElicitation(params) {
2691
3097
  const metadataResolution = resolvePluginStableMetadataMatch({
2692
3098
  meta,
2693
3099
  requestParams,
2694
- entries,
3100
+ entries: pluginEntries,
2695
3101
  context
2696
3102
  });
2697
3103
  if (metadataResolution.kind !== "not_plugin") return metadataResolution;
@@ -2702,7 +3108,7 @@ function resolvePluginElicitation(params) {
2702
3108
  return { kind: "not_plugin" };
2703
3109
  }
2704
3110
  function isCodexConnectorApprovalElicitation(requestParams, meta) {
2705
- 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;
2706
3112
  }
2707
3113
  function resolvePluginStableMetadataMatch(params) {
2708
3114
  const pluginName = readFirstString(params.meta, PLUGIN_NAME_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_NAME_META_KEYS);
@@ -2731,10 +3137,16 @@ function uniquePluginMatch(matches, source) {
2731
3137
  };
2732
3138
  }
2733
3139
  function hasDisplayNameOnlyPluginMatch(meta, entries) {
2734
- const connectorName = readString(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
3140
+ const connectorName = readString$1(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
2735
3141
  if (!connectorName) return false;
2736
3142
  const normalized = normalizePluginIdentityText(connectorName);
2737
- 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;
2738
3150
  }
2739
3151
  function normalizePluginIdentityText(value) {
2740
3152
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
@@ -2776,20 +3188,20 @@ function oneShotPluginPolicyApprovalOutcome(mode, outcome) {
2776
3188
  return mode === "ask" && outcome === "approved-session" ? "approved-once" : outcome;
2777
3189
  }
2778
3190
  function readPluginApprovalElicitation(entry, requestParams) {
2779
- if (readString(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) return;
3191
+ if (readString$1(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) return;
2780
3192
  const requestedSchema = requestParams.requestedSchema;
2781
- if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
3193
+ if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
2782
3194
  const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
2783
- const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || "Codex plugin approval";
3195
+ const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || "Codex plugin approval";
2784
3196
  const descriptionMeta = { ...meta };
2785
- 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);
2786
3198
  return {
2787
3199
  title,
2788
3200
  description: buildApprovalDescription({
2789
3201
  title,
2790
3202
  meta: descriptionMeta,
2791
3203
  requestedSchema,
2792
- serverName: sanitizeOptionalDisplayText(readString(requestParams, "serverName"))
3204
+ serverName: sanitizeOptionalDisplayText(readString$1(requestParams, "serverName"))
2793
3205
  }),
2794
3206
  requestedSchema,
2795
3207
  meta,
@@ -2828,34 +3240,34 @@ function declineElicitationResponse() {
2828
3240
  function logPluginElicitationDecline(reason, requestParams) {
2829
3241
  embeddedAgentLog.debug("codex plugin elicitation declined", {
2830
3242
  reason,
2831
- serverName: readString(requestParams, "serverName"),
2832
- mode: readString(requestParams, "mode")
3243
+ serverName: readString$1(requestParams, "serverName"),
3244
+ mode: readString$1(requestParams, "mode")
2833
3245
  });
2834
3246
  }
2835
3247
  function readBridgeableApprovalElicitation(requestParams) {
2836
- 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;
2837
3249
  const requestedSchema = requestParams.requestedSchema;
2838
- if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
2839
- 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";
2840
3252
  return {
2841
3253
  title,
2842
3254
  description: buildApprovalDescription({
2843
3255
  title,
2844
3256
  meta: requestParams["_meta"],
2845
3257
  requestedSchema,
2846
- serverName: sanitizeOptionalDisplayText(readString(requestParams, "serverName"))
3258
+ serverName: sanitizeOptionalDisplayText(readString$1(requestParams, "serverName"))
2847
3259
  }),
2848
3260
  requestedSchema,
2849
3261
  meta: requestParams["_meta"]
2850
3262
  };
2851
3263
  }
2852
3264
  function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
2853
- const serverName = readString(requestParams, "serverName");
2854
- 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;
2855
3267
  const requestedSchema = isJsonObject(requestParams?.requestedSchema) ? requestParams.requestedSchema : EMPTY_OBJECT_SCHEMA;
2856
- if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
3268
+ if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
2857
3269
  const meta = isJsonObject(requestParams?.["_meta"]) ? requestParams["_meta"] : {};
2858
- const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || COMPUTER_USE_APPROVAL_TITLE;
3270
+ const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || COMPUTER_USE_APPROVAL_TITLE;
2859
3271
  return {
2860
3272
  title,
2861
3273
  description: buildApprovalDescription({
@@ -2869,9 +3281,9 @@ function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
2869
3281
  };
2870
3282
  }
2871
3283
  function buildApprovalDescription(params) {
2872
- const connectorName = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY));
2873
- const toolTitle = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY));
2874
- 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));
2875
3287
  const summaryLines = [
2876
3288
  connectorName && `App: ${connectorName}`,
2877
3289
  toolTitle && `Tool: ${toolTitle}`,
@@ -2892,8 +3304,8 @@ function readPropertyDescriptionLines(requestedSchema) {
2892
3304
  return Object.entries(properties).map(([name, value]) => {
2893
3305
  const schema = isJsonObject(value) ? value : void 0;
2894
3306
  if (!schema) return;
2895
- const propTitle = sanitizeDisplayText(readString(schema, "title") ?? "") || sanitizeDisplayText(name) || "field";
2896
- 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"));
2897
3309
  return description ? `- ${propTitle}: ${description}` : `- ${propTitle}`;
2898
3310
  }).filter((line) => Boolean(line));
2899
3311
  }
@@ -2903,7 +3315,7 @@ function readDisplayParamLines(meta) {
2903
3315
  const lines = displayParams.slice(0, MAX_DISPLAY_PARAM_ENTRIES).map((entry) => {
2904
3316
  const param = isJsonObject(entry) ? entry : void 0;
2905
3317
  if (!param) return;
2906
- 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"));
2907
3319
  if (!name) return;
2908
3320
  return `- ${name}: ${formatDisplayParamValue(param.value)}`;
2909
3321
  }).filter((line) => Boolean(line));
@@ -2949,14 +3361,14 @@ function sanitizeOptionalDisplayText(value) {
2949
3361
  return (value === void 0 ? "" : sanitizeDisplayText(value)) || void 0;
2950
3362
  }
2951
3363
  function sanitizeDisplayText(value) {
2952
- const scanned = value.slice(0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
3364
+ const scanned = sliceUtf16Safe(value, 0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
2953
3365
  const clipped = value.length > DISPLAY_TEXT_SCAN_MAX_LENGTH;
2954
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();
2955
3367
  const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
2956
3368
  return clipped && escaped ? `${escaped}...` : escaped;
2957
3369
  }
2958
3370
  function truncateDisplayText(value, maxLength) {
2959
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
3371
+ return value.length <= maxLength ? value : `${truncateUtf16Safe(value, maxLength - 3)}...`;
2960
3372
  }
2961
3373
  async function requestPluginApprovalOutcome(params) {
2962
3374
  try {
@@ -3042,7 +3454,7 @@ function buildAcceptedContent(approvalPrompt, outcome) {
3042
3454
  }
3043
3455
  function readApprovalFieldValue(property, outcome) {
3044
3456
  if (!isApprovalField(property)) return;
3045
- if (readString(property.schema, "type") === "boolean") return true;
3457
+ if (readString$1(property.schema, "type") === "boolean") return true;
3046
3458
  const options = readEnumOptions(property.schema);
3047
3459
  if (options.length === 0) return;
3048
3460
  const sessionChoice = options.find((option) => isSessionApprovalOption(option));
@@ -3077,8 +3489,8 @@ function isPersistField(property) {
3077
3489
  function propertyText(property) {
3078
3490
  return [
3079
3491
  property.name,
3080
- readString(property.schema, "title"),
3081
- readString(property.schema, "description")
3492
+ readString$1(property.schema, "title"),
3493
+ readString$1(property.schema, "description")
3082
3494
  ].filter(Boolean).join(" ");
3083
3495
  }
3084
3496
  function readPersistHints(meta, mode = "legacy") {
@@ -3117,11 +3529,11 @@ function readEnumOptions(schema) {
3117
3529
  }
3118
3530
  if (Array.isArray(schema.oneOf)) return schema.oneOf.map((entry) => {
3119
3531
  const option = isJsonObject(entry) ? entry : void 0;
3120
- const value = readString(option, "const");
3532
+ const value = readString$1(option, "const");
3121
3533
  if (!value) return;
3122
3534
  return {
3123
3535
  value,
3124
- label: readString(option, "title") ?? value
3536
+ label: readString$1(option, "title") ?? value
3125
3537
  };
3126
3538
  }).filter((entry) => Boolean(entry));
3127
3539
  return [];
@@ -3134,17 +3546,37 @@ function isSessionApprovalOption(option) {
3134
3546
  const haystack = `${option.value} ${option.label}`.toLowerCase();
3135
3547
  return /\b(session|always|persistent)\b/.test(haystack) && /\b(allow|approve|accept)\b/.test(haystack);
3136
3548
  }
3137
- function readString(record, key) {
3549
+ function readString$1(record, key) {
3138
3550
  const value = record?.[key];
3139
3551
  return typeof value === "string" && value.trim() ? value : void 0;
3140
3552
  }
3141
3553
  function readFirstString(record, keys) {
3142
3554
  for (const key of keys) {
3143
- const value = readString(record, key);
3555
+ const value = readString$1(record, key);
3144
3556
  if (value) return value;
3145
3557
  }
3146
3558
  }
3147
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
3148
3580
  //#region extensions/codex/src/app-server/native-hook-relay.ts
3149
3581
  /**
3150
3582
  * Bridges Codex native hook callbacks into OpenClaw's native hook relay so
@@ -3163,6 +3595,7 @@ const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
3163
3595
  const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
3164
3596
  const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
3165
3597
  const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
3598
+ const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
3166
3599
  const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
3167
3600
  const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
3168
3601
  const pendingCodexNativeHookRelayUnregisters = /* @__PURE__ */ new Set();
@@ -3186,9 +3619,25 @@ function scheduleCodexNativeHookRelayUnregister(params) {
3186
3619
  }
3187
3620
  /** Computes the delayed unregister window from Codex's hook timeout. */
3188
3621
  function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
3189
- const hookTimeoutMs = typeof hookTimeoutSec === "number" && Number.isFinite(hookTimeoutSec) && hookTimeoutSec > 0 ? finiteSecondsToTimerSafeMilliseconds(Math.ceil(hookTimeoutSec)) ?? 0 : 0;
3622
+ const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
3190
3623
  return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
3191
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
+ }
3192
3641
  /** Registers an OpenClaw native hook relay for a Codex app-server turn. */
3193
3642
  function createCodexNativeHookRelay(params) {
3194
3643
  if (params.options?.enabled === false) return;
@@ -3215,6 +3664,7 @@ function createCodexNativeHookRelay(params) {
3215
3664
  turnStartTimeoutMs: params.turnStartTimeoutMs
3216
3665
  }),
3217
3666
  signal: params.signal,
3667
+ onPreToolUseFailure: params.onPreToolUseFailure,
3218
3668
  command: {
3219
3669
  nice: 10,
3220
3670
  timeoutMs: params.options?.gatewayTimeoutMs
@@ -3304,7 +3754,7 @@ function buildCodexNativeHookRelayDisabledConfig() {
3304
3754
  };
3305
3755
  }
3306
3756
  function normalizeHookTimeoutSec(value) {
3307
- 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;
3308
3758
  }
3309
3759
  function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
3310
3760
  const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
@@ -3332,27 +3782,2379 @@ function sortJsonValue(value) {
3332
3782
  return sorted;
3333
3783
  }
3334
3784
  //#endregion
3335
- //#region extensions/codex/src/app-server/provider-capabilities.ts
3336
- async function readConfiguredProviderWebSearchSupport(params) {
3337
- return (await params.client.request("modelProvider/capabilities/read", {}, {
3338
- timeoutMs: params.timeoutMs,
3339
- signal: params.signal
3340
- })).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";
3341
3793
  }
3342
- async function resolveCodexProviderWebSearchSupportForClient(params) {
3343
- const modelProviderOverride = params.modelProviderOverride?.trim().toLowerCase();
3344
- if (modelProviderOverride === "openai") return "supported";
3345
- 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) {
3346
3895
  try {
3347
- return await readConfiguredProviderWebSearchSupport(params);
3348
- } catch {
3349
- 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;
3350
3920
  }
3351
3921
  }
3352
- async function resolveCodexProviderWebSearchSupport(params) {
3353
- 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;
3354
3946
  try {
3355
- 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
+ });
3356
6158
  return await resolveCodexProviderWebSearchSupportForClient({
3357
6159
  client,
3358
6160
  timeoutMs: params.appServer.requestTimeoutMs,
@@ -3366,4 +6168,4 @@ async function resolveCodexProviderWebSearchSupport(params) {
3366
6168
  }
3367
6169
  }
3368
6170
  //#endregion
3369
- 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 };