@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.
- package/dist/{app-server-policy-B7hqD3Xj.js → app-server-policy-BUk0GLMy.js} +5 -5
- package/dist/{attempt-notifications-tqyQ_Tdn.js → attempt-notifications-BGsEIIDI.js} +5 -40
- package/dist/{command-handlers-C9K-KRU-.js → command-handlers-UVniG5Hl.js} +267 -232
- package/dist/command-rpc-BjgEPh19.js +784 -0
- package/dist/compact-CZzbcygx.js +603 -0
- package/dist/{computer-use-CpurU-G9.js → computer-use-DDeySrnb.js} +3 -3
- package/dist/{config-C_whBx3M.js → config-CYEDnLJ2.js} +36 -15
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +439 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1117 -19
- package/dist/{media-understanding-provider-BOeoBhEw.js → media-understanding-provider-DgqRvU2_.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-DpzY1xpp.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-BrZUG9jj.js → plugin-app-cache-key-o-AHbdaf.js} +8 -4
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-CJiWigAJ.js → protocol-validators-dZQ-UTOa.js} +116 -59
- package/dist/{provider-capabilities-wqedK49Z.js → provider-capabilities-gTCwjfmh.js} +2916 -114
- package/dist/{provider-nmtMDcGH.js → provider-cc_62eQE.js} +19 -10
- package/dist/provider.js +2 -2
- package/dist/{request-BQuSQF29.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-DikbXia_.js → run-attempt-8SPLiDlT.js} +907 -3002
- package/dist/{sandbox-guard-pTY2COoC.js → sandbox-guard-DA2TQfZW.js} +3 -3
- package/dist/session-binding-Dc03iwRF.js +595 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/{shared-client-rR6TDJA3.js → shared-client-4ICy3U6d.js} +892 -103
- package/dist/{side-question-Ca9wqmd0.js → side-question-DkjXvYv7.js} +132 -140
- package/dist/{thread-lifecycle-D6R40plk.js → thread-lifecycle-BskXnNP-.js} +609 -444
- package/dist/{web-search-provider.runtime-B6eZEFmd.js → web-search-provider.runtime-B5F42P7x.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +20 -0
- package/package.json +9 -6
- package/dist/client-IcTBKEqA.js +0 -732
- package/dist/client-factory-DfFu9JH_.js +0 -15
- package/dist/compact-CILptrPS.js +0 -278
- package/dist/conversation-binding-DOJtUxOy.js +0 -1570
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-Cm0Gg7Z1.js +0 -339
|
@@ -1,18 +1,335 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { c as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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 {
|
|
13
|
-
import {
|
|
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
|
|
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
|
|
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$
|
|
347
|
-
const requestTurnId = readString$
|
|
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$
|
|
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$
|
|
757
|
+
const command = readString$2(params.requestParams, "command");
|
|
427
758
|
if (!command) return;
|
|
428
759
|
return buildExecAutoReviewInputForShellCommand({
|
|
429
760
|
command,
|
|
430
|
-
cwd: readString$
|
|
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$
|
|
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$
|
|
966
|
+
const command = readString$2(params.policyRequest.params, "command");
|
|
629
967
|
if (!command) return;
|
|
630
|
-
const turnId = readString$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
1348
|
+
const value = readString$2(record, key);
|
|
1008
1349
|
return value === void 0 ? void 0 : previewSource(value);
|
|
1009
1350
|
}
|
|
1010
|
-
function readString$
|
|
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
|
|
1356
|
+
return value.length <= maxLength ? value : `${truncateUtf16Safe(value, maxLength - 3)}...`;
|
|
1016
1357
|
}
|
|
1017
1358
|
function previewSource(value) {
|
|
1018
1359
|
return {
|
|
1019
|
-
value: value
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
2434
|
+
return withSideEffectEvidence(withDiagnosticFailureDisposition({
|
|
2038
2435
|
contentItems: [{
|
|
2039
2436
|
type: "inputText",
|
|
2040
2437
|
text: errorMessage
|
|
2041
2438
|
}],
|
|
2042
2439
|
success: false
|
|
2043
|
-
},
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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) :
|
|
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/
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
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
|
-
|
|
3343
|
-
|
|
3344
|
-
if (
|
|
3345
|
-
if (
|
|
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
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
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
|
|
3353
|
-
|
|
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
|
-
|
|
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 {
|
|
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 };
|