@openclaw/codex 2026.7.1-beta.1 → 2026.7.1-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-server-policy-xi0ZmBGO.js → app-server-policy-BUk0GLMy.js} +5 -5
- package/dist/{attempt-notifications-l9LElGOU.js → attempt-notifications-BGsEIIDI.js} +5 -40
- package/dist/{command-handlers-vZy8xX5X.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-FPGG-mtX.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 +1119 -28
- package/dist/{media-understanding-provider-C5yYzx_6.js → media-understanding-provider-DgqRvU2_.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-Ccy1_7nN.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-CDioyur-.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-CB4mtyrs.js → protocol-validators-dZQ-UTOa.js} +117 -62
- package/dist/{provider-capabilities-CE5k8cmO.js → provider-capabilities-gTCwjfmh.js} +2920 -114
- package/dist/{provider-DosqwRb6.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-O3Zs32KI.js → run-attempt-8SPLiDlT.js} +1016 -2877
- 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-DDVgFmpv.js → side-question-DkjXvYv7.js} +132 -140
- package/dist/{thread-lifecycle-DeSMZSXr.js → thread-lifecycle-BskXnNP-.js} +609 -444
- package/dist/{web-search-provider.runtime-Q89rQhsL.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-CwjdQQ4j.js +0 -18
- package/dist/compact-BzB9AqN1.js +0 -278
- package/dist/conversation-binding-CEE2UyYb.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,11 @@ function hasExplicitNonSourceMessageRoute(args, hookContext, messagingTarget) {
|
|
|
1784
2169
|
}
|
|
1785
2170
|
/** Namespace attached to OpenClaw-owned dynamic tools exposed to Codex. */
|
|
1786
2171
|
const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
|
|
1787
|
-
const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = new Set([
|
|
2172
|
+
const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
2173
|
+
"agents_list",
|
|
2174
|
+
"sessions_spawn",
|
|
2175
|
+
"sessions_yield"
|
|
2176
|
+
]);
|
|
1788
2177
|
const EXPLICIT_MESSAGE_PROVIDER_KEYS = ["channel", "provider"];
|
|
1789
2178
|
const EXPLICIT_MESSAGE_TARGET_KEYS = [
|
|
1790
2179
|
"target",
|
|
@@ -1846,7 +2235,7 @@ function createCodexDynamicToolBridge(params) {
|
|
|
1846
2235
|
return getChannelAgentToolMeta(tool) === void 0;
|
|
1847
2236
|
};
|
|
1848
2237
|
const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
|
|
1849
|
-
const directToolNames = new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
|
|
2238
|
+
const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
|
|
1850
2239
|
return {
|
|
1851
2240
|
availableSpecs: createCodexDynamicToolSpecs({
|
|
1852
2241
|
entries: availableTools,
|
|
@@ -1914,6 +2303,7 @@ function createCodexDynamicToolBridge(params) {
|
|
|
1914
2303
|
executionPrevented = consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
|
|
1915
2304
|
const telemetryRawResult = sanitizeToolResult(rawResult);
|
|
1916
2305
|
const rawIsError = isCodexToolResultError(rawResult);
|
|
2306
|
+
const rawResultFailureKind = resolveToolResultFailureKind(rawResult);
|
|
1917
2307
|
const middlewareResult = await middlewareRunner.applyToolResultMiddleware({
|
|
1918
2308
|
threadId: call.threadId,
|
|
1919
2309
|
turnId: call.turnId,
|
|
@@ -1932,7 +2322,16 @@ function createCodexDynamicToolBridge(params) {
|
|
|
1932
2322
|
result: middlewareResult
|
|
1933
2323
|
});
|
|
1934
2324
|
const resultIsError = rawIsError || isCodexToolResultError(result);
|
|
1935
|
-
|
|
2325
|
+
const finalResultFailureKind = resolveToolResultFailureKind(result);
|
|
2326
|
+
const resultFailureKind = rawResultFailureKind ?? finalResultFailureKind;
|
|
2327
|
+
const observerResult = rawResultFailureKind && finalResultFailureKind !== rawResultFailureKind ? {
|
|
2328
|
+
...result,
|
|
2329
|
+
details: {
|
|
2330
|
+
...isRecord(result.details) ? result.details : {},
|
|
2331
|
+
status: rawResultFailureKind
|
|
2332
|
+
}
|
|
2333
|
+
} : result;
|
|
2334
|
+
notifyAgentToolResult(options?.onAgentToolResult, toolName, observerResult, resultIsError);
|
|
1936
2335
|
runAgentHarnessAfterToolCallHook({
|
|
1937
2336
|
toolName,
|
|
1938
2337
|
toolCallId: call.callId,
|
|
@@ -1966,11 +2365,12 @@ function createCodexDynamicToolBridge(params) {
|
|
|
1966
2365
|
isError: resultIsError,
|
|
1967
2366
|
messagingTarget: confirmedMessagingTarget
|
|
1968
2367
|
});
|
|
1969
|
-
const terminalType =
|
|
2368
|
+
const terminalType = resultFailureKind === "blocked" ? "blocked" : resultIsError ? "error" : "completed";
|
|
1970
2369
|
const response = withDiagnosticTerminalType({
|
|
1971
2370
|
contentItems: convertToolContents(result.content, toolResultMaxChars),
|
|
1972
2371
|
success: !resultIsError
|
|
1973
2372
|
}, terminalType);
|
|
2373
|
+
withDiagnosticFailureDisposition(response, resultFailureKind);
|
|
1974
2374
|
const blocksSourceReplyTermination = hasExplicitNonSourceMessageRoute(executedArgs, params.hookContext, confirmedMessagingTarget);
|
|
1975
2375
|
const deliveredSourceReply = isDeliveredMessageToolOnlySourceReplyResult({
|
|
1976
2376
|
sourceReplyDeliveryMode: params.hookContext?.sourceReplyDeliveryMode,
|
|
@@ -1995,11 +2395,12 @@ function createCodexDynamicToolBridge(params) {
|
|
|
1995
2395
|
withDynamicToolAsyncStarted(response, asyncStarted);
|
|
1996
2396
|
return withSideEffectEvidence(response, !(executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs)));
|
|
1997
2397
|
} catch (error) {
|
|
1998
|
-
const
|
|
2398
|
+
const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
|
|
2399
|
+
const errorMessage = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
|
|
1999
2400
|
const adjustedExecutedArgs = consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
|
|
2000
2401
|
if (isRecord(adjustedExecutedArgs)) executedArgs = structuredClone(adjustedExecutedArgs);
|
|
2001
2402
|
executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
|
|
2002
|
-
const failedResult = failedToolResult(errorMessage);
|
|
2403
|
+
const failedResult = failedToolResult(errorMessage, executionDisposition);
|
|
2003
2404
|
finalizeToolTerminalPresentation({
|
|
2004
2405
|
toolCallId: call.callId,
|
|
2005
2406
|
runId: toolResultHookContext.runId,
|
|
@@ -2030,13 +2431,13 @@ function createCodexDynamicToolBridge(params) {
|
|
|
2030
2431
|
startedAt
|
|
2031
2432
|
});
|
|
2032
2433
|
const replaySafe = !didStartExecution || executionPrevented || isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
|
|
2033
|
-
return withSideEffectEvidence(
|
|
2434
|
+
return withSideEffectEvidence(withDiagnosticFailureDisposition({
|
|
2034
2435
|
contentItems: [{
|
|
2035
2436
|
type: "inputText",
|
|
2036
2437
|
text: errorMessage
|
|
2037
2438
|
}],
|
|
2038
2439
|
success: false
|
|
2039
|
-
},
|
|
2440
|
+
}, executionDisposition), didStartExecution && !replaySafe);
|
|
2040
2441
|
}
|
|
2041
2442
|
}
|
|
2042
2443
|
};
|
|
@@ -2052,14 +2453,14 @@ function notifyAgentToolResult(observer, toolName, result, isError) {
|
|
|
2052
2453
|
embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${toolName} error=${String(error)}`);
|
|
2053
2454
|
}
|
|
2054
2455
|
}
|
|
2055
|
-
function failedToolResult(message) {
|
|
2456
|
+
function failedToolResult(message, status = "failed") {
|
|
2056
2457
|
return {
|
|
2057
2458
|
content: [{
|
|
2058
2459
|
type: "text",
|
|
2059
2460
|
text: message
|
|
2060
2461
|
}],
|
|
2061
2462
|
details: {
|
|
2062
|
-
status
|
|
2463
|
+
status,
|
|
2063
2464
|
error: message
|
|
2064
2465
|
}
|
|
2065
2466
|
};
|
|
@@ -2234,6 +2635,7 @@ function warnQuarantinedDynamicTools(tools) {
|
|
|
2234
2635
|
function emitQuarantinedDynamicToolDiagnostics(tools, ctx) {
|
|
2235
2636
|
for (const tool of tools) emitTrustedDiagnosticEvent({
|
|
2236
2637
|
type: "tool.execution.blocked",
|
|
2638
|
+
agentId: ctx?.agentId,
|
|
2237
2639
|
runId: ctx?.runId,
|
|
2238
2640
|
sessionId: ctx?.sessionId,
|
|
2239
2641
|
sessionKey: ctx?.sessionKey,
|
|
@@ -2390,13 +2792,6 @@ function isAsyncStartedToolResult(result) {
|
|
|
2390
2792
|
const details = result.details;
|
|
2391
2793
|
return isRecord(details) && details.async === true && details.status === "started";
|
|
2392
2794
|
}
|
|
2393
|
-
function inferToolResultDiagnosticTerminalType(result, isError) {
|
|
2394
|
-
const details = result.details;
|
|
2395
|
-
if (isRecord(details) && typeof details.status === "string") {
|
|
2396
|
-
if (details.status.trim().toLowerCase() === "blocked") return "blocked";
|
|
2397
|
-
}
|
|
2398
|
-
return isError ? "error" : "completed";
|
|
2399
|
-
}
|
|
2400
2795
|
function withDiagnosticTerminalType(response, terminalType) {
|
|
2401
2796
|
Object.defineProperty(response, "diagnosticTerminalType", {
|
|
2402
2797
|
configurable: true,
|
|
@@ -2405,6 +2800,16 @@ function withDiagnosticTerminalType(response, terminalType) {
|
|
|
2405
2800
|
});
|
|
2406
2801
|
return response;
|
|
2407
2802
|
}
|
|
2803
|
+
function withDiagnosticFailureDisposition(response, disposition) {
|
|
2804
|
+
if (!disposition) return response;
|
|
2805
|
+
withDiagnosticTerminalType(response, disposition === "blocked" ? "blocked" : "error");
|
|
2806
|
+
if (disposition !== "blocked") Object.defineProperty(response, "diagnosticTerminalReason", {
|
|
2807
|
+
configurable: true,
|
|
2808
|
+
enumerable: false,
|
|
2809
|
+
value: disposition
|
|
2810
|
+
});
|
|
2811
|
+
return response;
|
|
2812
|
+
}
|
|
2408
2813
|
function withSideEffectEvidence(response, sideEffectEvidence) {
|
|
2409
2814
|
if (!sideEffectEvidence) return response;
|
|
2410
2815
|
Object.defineProperty(response, "sideEffectEvidence", {
|
|
@@ -2641,7 +3046,7 @@ async function handleCodexAppServerElicitationRequest(params) {
|
|
|
2641
3046
|
}
|
|
2642
3047
|
function matchesCurrentThread(requestParams, threadId) {
|
|
2643
3048
|
if (!requestParams) return false;
|
|
2644
|
-
return readString(requestParams, "threadId") === threadId;
|
|
3049
|
+
return readString$1(requestParams, "threadId") === threadId;
|
|
2645
3050
|
}
|
|
2646
3051
|
function turnIdMismatches(requestParams, turnId) {
|
|
2647
3052
|
const rawTurnId = requestParams?.turnId;
|
|
@@ -2656,6 +3061,7 @@ function resolvePluginElicitation(params) {
|
|
|
2656
3061
|
const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
|
|
2657
3062
|
const context = params.pluginAppPolicyContext;
|
|
2658
3063
|
const entries = context ? Object.values(context.apps) : [];
|
|
3064
|
+
const pluginEntries = entries.filter(isPluginAppPolicyContextEntry);
|
|
2659
3065
|
const appId = readFirstString(meta, PLUGIN_APP_ID_META_KEYS) ?? readFirstString(requestParams, PLUGIN_APP_ID_META_KEYS);
|
|
2660
3066
|
const connectorId = readFirstString(meta, PLUGIN_CONNECTOR_ID_META_KEYS);
|
|
2661
3067
|
const isCodexConnectorApproval = isCodexConnectorApprovalElicitation(requestParams, meta);
|
|
@@ -2669,6 +3075,10 @@ function resolvePluginElicitation(params) {
|
|
|
2669
3075
|
reason: "missing_policy_context"
|
|
2670
3076
|
};
|
|
2671
3077
|
const entry = context.apps[appId];
|
|
3078
|
+
if (entry?.source === "account" && !isCodexConnectorApproval) return {
|
|
3079
|
+
kind: "decline",
|
|
3080
|
+
reason: "account_app_source_mismatch"
|
|
3081
|
+
};
|
|
2672
3082
|
return uniquePluginMatch(entry ? [entry] : [], "app_id");
|
|
2673
3083
|
}
|
|
2674
3084
|
if (isCodexConnectorApproval && connectorId) {
|
|
@@ -2679,7 +3089,7 @@ function resolvePluginElicitation(params) {
|
|
|
2679
3089
|
const entry = context.apps[connectorId];
|
|
2680
3090
|
return uniquePluginMatch(entry ? [entry] : [], "connector_id");
|
|
2681
3091
|
}
|
|
2682
|
-
const serverName = readString(requestParams, "serverName");
|
|
3092
|
+
const serverName = readString$1(requestParams, "serverName");
|
|
2683
3093
|
if (serverName && context) {
|
|
2684
3094
|
const matches = entries.filter((entry) => entry.mcpServerNames.includes(serverName));
|
|
2685
3095
|
if (matches.length > 0) return uniquePluginMatch(matches, "server_name");
|
|
@@ -2687,7 +3097,7 @@ function resolvePluginElicitation(params) {
|
|
|
2687
3097
|
const metadataResolution = resolvePluginStableMetadataMatch({
|
|
2688
3098
|
meta,
|
|
2689
3099
|
requestParams,
|
|
2690
|
-
entries,
|
|
3100
|
+
entries: pluginEntries,
|
|
2691
3101
|
context
|
|
2692
3102
|
});
|
|
2693
3103
|
if (metadataResolution.kind !== "not_plugin") return metadataResolution;
|
|
@@ -2698,7 +3108,7 @@ function resolvePluginElicitation(params) {
|
|
|
2698
3108
|
return { kind: "not_plugin" };
|
|
2699
3109
|
}
|
|
2700
3110
|
function isCodexConnectorApprovalElicitation(requestParams, meta) {
|
|
2701
|
-
return readString(requestParams, "serverName") === CODEX_APPS_SERVER_NAME && readString(meta, MCP_TOOL_APPROVAL_KIND_KEY) === MCP_TOOL_APPROVAL_KIND && readString(meta, MCP_TOOL_APPROVAL_SOURCE_KEY) === MCP_TOOL_APPROVAL_CONNECTOR_SOURCE;
|
|
3111
|
+
return readString$1(requestParams, "serverName") === CODEX_APPS_SERVER_NAME && readString$1(meta, MCP_TOOL_APPROVAL_KIND_KEY) === MCP_TOOL_APPROVAL_KIND && readString$1(meta, MCP_TOOL_APPROVAL_SOURCE_KEY) === MCP_TOOL_APPROVAL_CONNECTOR_SOURCE;
|
|
2702
3112
|
}
|
|
2703
3113
|
function resolvePluginStableMetadataMatch(params) {
|
|
2704
3114
|
const pluginName = readFirstString(params.meta, PLUGIN_NAME_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_NAME_META_KEYS);
|
|
@@ -2727,10 +3137,16 @@ function uniquePluginMatch(matches, source) {
|
|
|
2727
3137
|
};
|
|
2728
3138
|
}
|
|
2729
3139
|
function hasDisplayNameOnlyPluginMatch(meta, entries) {
|
|
2730
|
-
const connectorName = readString(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
|
|
3140
|
+
const connectorName = readString$1(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
|
|
2731
3141
|
if (!connectorName) return false;
|
|
2732
3142
|
const normalized = normalizePluginIdentityText(connectorName);
|
|
2733
|
-
return entries.some((entry) => normalizePluginIdentityText(entry
|
|
3143
|
+
return entries.some((entry) => normalizePluginIdentityText(appPolicyDisplayName(entry)) === normalized || isPluginAppPolicyContextEntry(entry) && normalizePluginIdentityText(entry.configKey) === normalized);
|
|
3144
|
+
}
|
|
3145
|
+
function isPluginAppPolicyContextEntry(entry) {
|
|
3146
|
+
return entry.source !== "account";
|
|
3147
|
+
}
|
|
3148
|
+
function appPolicyDisplayName(entry) {
|
|
3149
|
+
return isPluginAppPolicyContextEntry(entry) ? entry.pluginName : entry.appName;
|
|
2734
3150
|
}
|
|
2735
3151
|
function normalizePluginIdentityText(value) {
|
|
2736
3152
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
@@ -2772,20 +3188,20 @@ function oneShotPluginPolicyApprovalOutcome(mode, outcome) {
|
|
|
2772
3188
|
return mode === "ask" && outcome === "approved-session" ? "approved-once" : outcome;
|
|
2773
3189
|
}
|
|
2774
3190
|
function readPluginApprovalElicitation(entry, requestParams) {
|
|
2775
|
-
if (readString(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) return;
|
|
3191
|
+
if (readString$1(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) return;
|
|
2776
3192
|
const requestedSchema = requestParams.requestedSchema;
|
|
2777
|
-
if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
|
|
3193
|
+
if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
|
|
2778
3194
|
const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
|
|
2779
|
-
const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || "Codex plugin approval";
|
|
3195
|
+
const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || "Codex plugin approval";
|
|
2780
3196
|
const descriptionMeta = { ...meta };
|
|
2781
|
-
if (!readString(descriptionMeta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY)) descriptionMeta[MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY] = entry
|
|
3197
|
+
if (!readString$1(descriptionMeta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY)) descriptionMeta[MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY] = appPolicyDisplayName(entry);
|
|
2782
3198
|
return {
|
|
2783
3199
|
title,
|
|
2784
3200
|
description: buildApprovalDescription({
|
|
2785
3201
|
title,
|
|
2786
3202
|
meta: descriptionMeta,
|
|
2787
3203
|
requestedSchema,
|
|
2788
|
-
serverName: sanitizeOptionalDisplayText(readString(requestParams, "serverName"))
|
|
3204
|
+
serverName: sanitizeOptionalDisplayText(readString$1(requestParams, "serverName"))
|
|
2789
3205
|
}),
|
|
2790
3206
|
requestedSchema,
|
|
2791
3207
|
meta,
|
|
@@ -2824,34 +3240,34 @@ function declineElicitationResponse() {
|
|
|
2824
3240
|
function logPluginElicitationDecline(reason, requestParams) {
|
|
2825
3241
|
embeddedAgentLog.debug("codex plugin elicitation declined", {
|
|
2826
3242
|
reason,
|
|
2827
|
-
serverName: readString(requestParams, "serverName"),
|
|
2828
|
-
mode: readString(requestParams, "mode")
|
|
3243
|
+
serverName: readString$1(requestParams, "serverName"),
|
|
3244
|
+
mode: readString$1(requestParams, "mode")
|
|
2829
3245
|
});
|
|
2830
3246
|
}
|
|
2831
3247
|
function readBridgeableApprovalElicitation(requestParams) {
|
|
2832
|
-
if (!requestParams || readString(requestParams, "mode") !== "form" || !isJsonObject(requestParams["_meta"]) || requestParams["_meta"][MCP_TOOL_APPROVAL_KIND_KEY] !== MCP_TOOL_APPROVAL_KIND || !isJsonObject(requestParams.requestedSchema)) return;
|
|
3248
|
+
if (!requestParams || readString$1(requestParams, "mode") !== "form" || !isJsonObject(requestParams["_meta"]) || requestParams["_meta"][MCP_TOOL_APPROVAL_KIND_KEY] !== MCP_TOOL_APPROVAL_KIND || !isJsonObject(requestParams.requestedSchema)) return;
|
|
2833
3249
|
const requestedSchema = requestParams.requestedSchema;
|
|
2834
|
-
if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
|
|
2835
|
-
const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || "Codex MCP tool approval";
|
|
3250
|
+
if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
|
|
3251
|
+
const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || "Codex MCP tool approval";
|
|
2836
3252
|
return {
|
|
2837
3253
|
title,
|
|
2838
3254
|
description: buildApprovalDescription({
|
|
2839
3255
|
title,
|
|
2840
3256
|
meta: requestParams["_meta"],
|
|
2841
3257
|
requestedSchema,
|
|
2842
|
-
serverName: sanitizeOptionalDisplayText(readString(requestParams, "serverName"))
|
|
3258
|
+
serverName: sanitizeOptionalDisplayText(readString$1(requestParams, "serverName"))
|
|
2843
3259
|
}),
|
|
2844
3260
|
requestedSchema,
|
|
2845
3261
|
meta: requestParams["_meta"]
|
|
2846
3262
|
};
|
|
2847
3263
|
}
|
|
2848
3264
|
function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
|
|
2849
|
-
const serverName = readString(requestParams, "serverName");
|
|
2850
|
-
if (!serverName || !expectedServerName || serverName !== expectedServerName || readString(requestParams, "mode") !== "form") return;
|
|
3265
|
+
const serverName = readString$1(requestParams, "serverName");
|
|
3266
|
+
if (!serverName || !expectedServerName || serverName !== expectedServerName || readString$1(requestParams, "mode") !== "form") return;
|
|
2851
3267
|
const requestedSchema = isJsonObject(requestParams?.requestedSchema) ? requestParams.requestedSchema : EMPTY_OBJECT_SCHEMA;
|
|
2852
|
-
if (readString(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
|
|
3268
|
+
if (readString$1(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
|
|
2853
3269
|
const meta = isJsonObject(requestParams?.["_meta"]) ? requestParams["_meta"] : {};
|
|
2854
|
-
const title = sanitizeDisplayText(readString(requestParams, "message") ?? "") || COMPUTER_USE_APPROVAL_TITLE;
|
|
3270
|
+
const title = sanitizeDisplayText(readString$1(requestParams, "message") ?? "") || COMPUTER_USE_APPROVAL_TITLE;
|
|
2855
3271
|
return {
|
|
2856
3272
|
title,
|
|
2857
3273
|
description: buildApprovalDescription({
|
|
@@ -2865,9 +3281,9 @@ function readComputerUseApprovalElicitation(requestParams, expectedServerName) {
|
|
|
2865
3281
|
};
|
|
2866
3282
|
}
|
|
2867
3283
|
function buildApprovalDescription(params) {
|
|
2868
|
-
const connectorName = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY));
|
|
2869
|
-
const toolTitle = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY));
|
|
2870
|
-
const toolDescription = sanitizeOptionalDisplayText(readString(params.meta, MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY));
|
|
3284
|
+
const connectorName = sanitizeOptionalDisplayText(readString$1(params.meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY));
|
|
3285
|
+
const toolTitle = sanitizeOptionalDisplayText(readString$1(params.meta, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY));
|
|
3286
|
+
const toolDescription = sanitizeOptionalDisplayText(readString$1(params.meta, MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY));
|
|
2871
3287
|
const summaryLines = [
|
|
2872
3288
|
connectorName && `App: ${connectorName}`,
|
|
2873
3289
|
toolTitle && `Tool: ${toolTitle}`,
|
|
@@ -2888,8 +3304,8 @@ function readPropertyDescriptionLines(requestedSchema) {
|
|
|
2888
3304
|
return Object.entries(properties).map(([name, value]) => {
|
|
2889
3305
|
const schema = isJsonObject(value) ? value : void 0;
|
|
2890
3306
|
if (!schema) return;
|
|
2891
|
-
const propTitle = sanitizeDisplayText(readString(schema, "title") ?? "") || sanitizeDisplayText(name) || "field";
|
|
2892
|
-
const description = sanitizeOptionalDisplayText(readString(schema, "description"));
|
|
3307
|
+
const propTitle = sanitizeDisplayText(readString$1(schema, "title") ?? "") || sanitizeDisplayText(name) || "field";
|
|
3308
|
+
const description = sanitizeOptionalDisplayText(readString$1(schema, "description"));
|
|
2893
3309
|
return description ? `- ${propTitle}: ${description}` : `- ${propTitle}`;
|
|
2894
3310
|
}).filter((line) => Boolean(line));
|
|
2895
3311
|
}
|
|
@@ -2899,7 +3315,7 @@ function readDisplayParamLines(meta) {
|
|
|
2899
3315
|
const lines = displayParams.slice(0, MAX_DISPLAY_PARAM_ENTRIES).map((entry) => {
|
|
2900
3316
|
const param = isJsonObject(entry) ? entry : void 0;
|
|
2901
3317
|
if (!param) return;
|
|
2902
|
-
const name = sanitizeOptionalDisplayText(readString(param, "display_name")) ?? sanitizeOptionalDisplayText(readString(param, "name"));
|
|
3318
|
+
const name = sanitizeOptionalDisplayText(readString$1(param, "display_name")) ?? sanitizeOptionalDisplayText(readString$1(param, "name"));
|
|
2903
3319
|
if (!name) return;
|
|
2904
3320
|
return `- ${name}: ${formatDisplayParamValue(param.value)}`;
|
|
2905
3321
|
}).filter((line) => Boolean(line));
|
|
@@ -2945,14 +3361,14 @@ function sanitizeOptionalDisplayText(value) {
|
|
|
2945
3361
|
return (value === void 0 ? "" : sanitizeDisplayText(value)) || void 0;
|
|
2946
3362
|
}
|
|
2947
3363
|
function sanitizeDisplayText(value) {
|
|
2948
|
-
const scanned = value
|
|
3364
|
+
const scanned = sliceUtf16Safe(value, 0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
|
|
2949
3365
|
const clipped = value.length > DISPLAY_TEXT_SCAN_MAX_LENGTH;
|
|
2950
3366
|
const sanitized = scanned.replace(ANSI_OSC_SEQUENCE_RE, "").replace(ANSI_CONTROL_SEQUENCE_RE, "").replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE, "").replace(INVISIBLE_FORMATTING_CONTROL_RE, " ").replace(CONTROL_CHARACTER_RE, " ").replace(/\s+/g, " ").trim();
|
|
2951
3367
|
const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
|
|
2952
3368
|
return clipped && escaped ? `${escaped}...` : escaped;
|
|
2953
3369
|
}
|
|
2954
3370
|
function truncateDisplayText(value, maxLength) {
|
|
2955
|
-
return value.length <= maxLength ? value : `${value
|
|
3371
|
+
return value.length <= maxLength ? value : `${truncateUtf16Safe(value, maxLength - 3)}...`;
|
|
2956
3372
|
}
|
|
2957
3373
|
async function requestPluginApprovalOutcome(params) {
|
|
2958
3374
|
try {
|
|
@@ -3038,7 +3454,7 @@ function buildAcceptedContent(approvalPrompt, outcome) {
|
|
|
3038
3454
|
}
|
|
3039
3455
|
function readApprovalFieldValue(property, outcome) {
|
|
3040
3456
|
if (!isApprovalField(property)) return;
|
|
3041
|
-
if (readString(property.schema, "type") === "boolean") return true;
|
|
3457
|
+
if (readString$1(property.schema, "type") === "boolean") return true;
|
|
3042
3458
|
const options = readEnumOptions(property.schema);
|
|
3043
3459
|
if (options.length === 0) return;
|
|
3044
3460
|
const sessionChoice = options.find((option) => isSessionApprovalOption(option));
|
|
@@ -3073,8 +3489,8 @@ function isPersistField(property) {
|
|
|
3073
3489
|
function propertyText(property) {
|
|
3074
3490
|
return [
|
|
3075
3491
|
property.name,
|
|
3076
|
-
readString(property.schema, "title"),
|
|
3077
|
-
readString(property.schema, "description")
|
|
3492
|
+
readString$1(property.schema, "title"),
|
|
3493
|
+
readString$1(property.schema, "description")
|
|
3078
3494
|
].filter(Boolean).join(" ");
|
|
3079
3495
|
}
|
|
3080
3496
|
function readPersistHints(meta, mode = "legacy") {
|
|
@@ -3113,11 +3529,11 @@ function readEnumOptions(schema) {
|
|
|
3113
3529
|
}
|
|
3114
3530
|
if (Array.isArray(schema.oneOf)) return schema.oneOf.map((entry) => {
|
|
3115
3531
|
const option = isJsonObject(entry) ? entry : void 0;
|
|
3116
|
-
const value = readString(option, "const");
|
|
3532
|
+
const value = readString$1(option, "const");
|
|
3117
3533
|
if (!value) return;
|
|
3118
3534
|
return {
|
|
3119
3535
|
value,
|
|
3120
|
-
label: readString(option, "title") ?? value
|
|
3536
|
+
label: readString$1(option, "title") ?? value
|
|
3121
3537
|
};
|
|
3122
3538
|
}).filter((entry) => Boolean(entry));
|
|
3123
3539
|
return [];
|
|
@@ -3130,17 +3546,37 @@ function isSessionApprovalOption(option) {
|
|
|
3130
3546
|
const haystack = `${option.value} ${option.label}`.toLowerCase();
|
|
3131
3547
|
return /\b(session|always|persistent)\b/.test(haystack) && /\b(allow|approve|accept)\b/.test(haystack);
|
|
3132
3548
|
}
|
|
3133
|
-
function readString(record, key) {
|
|
3549
|
+
function readString$1(record, key) {
|
|
3134
3550
|
const value = record?.[key];
|
|
3135
3551
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
3136
3552
|
}
|
|
3137
3553
|
function readFirstString(record, keys) {
|
|
3138
3554
|
for (const key of keys) {
|
|
3139
|
-
const value = readString(record, key);
|
|
3555
|
+
const value = readString$1(record, key);
|
|
3140
3556
|
if (value) return value;
|
|
3141
3557
|
}
|
|
3142
3558
|
}
|
|
3143
3559
|
//#endregion
|
|
3560
|
+
//#region extensions/codex/src/app-server/local-runtime-attribution.ts
|
|
3561
|
+
const OPENAI_PROVIDER_ID = "openai";
|
|
3562
|
+
const OPENAI_RESPONSES_API = "openai-responses";
|
|
3563
|
+
const OPENAI_CODEX_RESPONSES_API = "openai-chatgpt-responses";
|
|
3564
|
+
function normalizeRuntimeId(value) {
|
|
3565
|
+
return value?.trim().toLowerCase() ?? "";
|
|
3566
|
+
}
|
|
3567
|
+
/** Maps local Codex runtime plans onto the provider/api pair exposed to event projection. */
|
|
3568
|
+
function resolveCodexLocalRuntimeAttribution(params) {
|
|
3569
|
+
const authProfileProvider = normalizeRuntimeId(params.runtimePlan?.auth?.authProfileProviderForAuth);
|
|
3570
|
+
if (normalizeRuntimeId(params.runtimePlan?.observability.harnessId) === "codex" && authProfileProvider !== OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.provider) === OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.api) === OPENAI_RESPONSES_API) return {
|
|
3571
|
+
provider: OPENAI_PROVIDER_ID,
|
|
3572
|
+
api: OPENAI_CODEX_RESPONSES_API
|
|
3573
|
+
};
|
|
3574
|
+
return {
|
|
3575
|
+
provider: params.provider,
|
|
3576
|
+
api: params.model.api
|
|
3577
|
+
};
|
|
3578
|
+
}
|
|
3579
|
+
//#endregion
|
|
3144
3580
|
//#region extensions/codex/src/app-server/native-hook-relay.ts
|
|
3145
3581
|
/**
|
|
3146
3582
|
* Bridges Codex native hook callbacks into OpenClaw's native hook relay so
|
|
@@ -3159,6 +3595,7 @@ const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
|
|
|
3159
3595
|
const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 5 * 6e4;
|
|
3160
3596
|
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
|
|
3161
3597
|
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
|
|
3598
|
+
const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
|
|
3162
3599
|
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
|
|
3163
3600
|
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
|
|
3164
3601
|
const pendingCodexNativeHookRelayUnregisters = /* @__PURE__ */ new Set();
|
|
@@ -3182,9 +3619,25 @@ function scheduleCodexNativeHookRelayUnregister(params) {
|
|
|
3182
3619
|
}
|
|
3183
3620
|
/** Computes the delayed unregister window from Codex's hook timeout. */
|
|
3184
3621
|
function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
|
|
3185
|
-
const hookTimeoutMs =
|
|
3622
|
+
const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
|
|
3186
3623
|
return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
|
|
3187
3624
|
}
|
|
3625
|
+
/** Records a native pre-tool failure that Codex does not project as a tool item. */
|
|
3626
|
+
function emitCodexNativePreToolUseFailureDiagnostic(params) {
|
|
3627
|
+
emitTrustedDiagnosticEvent({
|
|
3628
|
+
type: "tool.execution.error",
|
|
3629
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
3630
|
+
sessionId: params.sessionId,
|
|
3631
|
+
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3632
|
+
runId: params.runId,
|
|
3633
|
+
toolName: params.failure.toolName,
|
|
3634
|
+
toolCallId: params.failure.toolCallId,
|
|
3635
|
+
durationMs: params.failure.durationMs,
|
|
3636
|
+
errorCategory: "before_tool_call",
|
|
3637
|
+
terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
|
|
3638
|
+
...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
|
|
3639
|
+
});
|
|
3640
|
+
}
|
|
3188
3641
|
/** Registers an OpenClaw native hook relay for a Codex app-server turn. */
|
|
3189
3642
|
function createCodexNativeHookRelay(params) {
|
|
3190
3643
|
if (params.options?.enabled === false) return;
|
|
@@ -3211,6 +3664,7 @@ function createCodexNativeHookRelay(params) {
|
|
|
3211
3664
|
turnStartTimeoutMs: params.turnStartTimeoutMs
|
|
3212
3665
|
}),
|
|
3213
3666
|
signal: params.signal,
|
|
3667
|
+
onPreToolUseFailure: params.onPreToolUseFailure,
|
|
3214
3668
|
command: {
|
|
3215
3669
|
nice: 10,
|
|
3216
3670
|
timeoutMs: params.options?.gatewayTimeoutMs
|
|
@@ -3300,7 +3754,7 @@ function buildCodexNativeHookRelayDisabledConfig() {
|
|
|
3300
3754
|
};
|
|
3301
3755
|
}
|
|
3302
3756
|
function normalizeHookTimeoutSec(value) {
|
|
3303
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) :
|
|
3757
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC;
|
|
3304
3758
|
}
|
|
3305
3759
|
function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
|
|
3306
3760
|
const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
|
|
@@ -3328,27 +3782,2379 @@ function sortJsonValue(value) {
|
|
|
3328
3782
|
return sorted;
|
|
3329
3783
|
}
|
|
3330
3784
|
//#endregion
|
|
3331
|
-
//#region extensions/codex/src/app-server/
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3785
|
+
//#region extensions/codex/src/app-server/tool-progress-normalization.ts
|
|
3786
|
+
/**
|
|
3787
|
+
* Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
|
|
3788
|
+
* emitted into OpenClaw events or logs.
|
|
3789
|
+
*/
|
|
3790
|
+
/** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
|
|
3791
|
+
function resolveCodexToolProgressDetailMode(value) {
|
|
3792
|
+
return value === "raw" ? "raw" : "explain";
|
|
3337
3793
|
}
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
if (
|
|
3341
|
-
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) {
|
|
3342
3895
|
try {
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3896
|
+
const messages = await resolveFinalCodexMirrorMessages({
|
|
3897
|
+
params: params.params,
|
|
3898
|
+
messagesSnapshot: params.result.messagesSnapshot,
|
|
3899
|
+
turnId: params.turnId
|
|
3900
|
+
});
|
|
3901
|
+
const mirrorResult = await mirrorCodexAppServerTranscript({
|
|
3902
|
+
sessionFile: params.params.sessionFile,
|
|
3903
|
+
agentId: params.agentId,
|
|
3904
|
+
sessionKey: params.sessionKey,
|
|
3905
|
+
sessionId: params.params.sessionId,
|
|
3906
|
+
cwd: params.cwd,
|
|
3907
|
+
messages,
|
|
3908
|
+
idempotencyScope: `codex-app-server:${params.threadId}`,
|
|
3909
|
+
config: params.params.config
|
|
3910
|
+
});
|
|
3911
|
+
for (const message of mirrorResult.userMessagesPresent) try {
|
|
3912
|
+
params.notifyUserMessagePersisted(message);
|
|
3913
|
+
} catch (error) {
|
|
3914
|
+
embeddedAgentLog.warn("failed to notify codex app-server user-message persistence", { error: formatErrorMessage(error) });
|
|
3915
|
+
}
|
|
3916
|
+
return mirrorResult.assistantMirrorIdentitiesOwned.includes(`${params.turnId}:assistant`);
|
|
3917
|
+
} catch (error) {
|
|
3918
|
+
embeddedAgentLog.warn("failed to mirror codex app-server transcript", { error });
|
|
3919
|
+
return false;
|
|
3346
3920
|
}
|
|
3347
3921
|
}
|
|
3348
|
-
async function
|
|
3349
|
-
|
|
3922
|
+
async function resolveFinalCodexMirrorMessages(params) {
|
|
3923
|
+
if (params.params.suppressNextUserMessagePersistence || !params.params.userTurnTranscriptRecorder) return params.messagesSnapshot;
|
|
3924
|
+
const resolvedPrompt = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
|
|
3925
|
+
const firstUserIndex = params.messagesSnapshot.findIndex((message) => message.role === "user");
|
|
3926
|
+
if (firstUserIndex === -1) return [resolvedPrompt, ...params.messagesSnapshot];
|
|
3927
|
+
const messages = params.messagesSnapshot.slice();
|
|
3928
|
+
messages[firstUserIndex] = resolvedPrompt;
|
|
3929
|
+
return messages;
|
|
3930
|
+
}
|
|
3931
|
+
function createCodexAppServerUserMessagePersistenceNotifier(runParams) {
|
|
3932
|
+
let notified = false;
|
|
3933
|
+
return (message) => {
|
|
3934
|
+
if (notified) return;
|
|
3935
|
+
notified = true;
|
|
3936
|
+
runParams.userTurnTranscriptRecorder?.markRuntimePersisted(message);
|
|
3937
|
+
try {
|
|
3938
|
+
runParams.onUserMessagePersisted?.(message);
|
|
3939
|
+
} catch (error) {
|
|
3940
|
+
embeddedAgentLog.warn("codex app-server user persistence notification failed", { error: formatErrorMessage(error) });
|
|
3941
|
+
}
|
|
3942
|
+
};
|
|
3943
|
+
}
|
|
3944
|
+
async function mirrorPromptAtTurnStartBestEffort(params) {
|
|
3945
|
+
if (params.params.suppressNextUserMessagePersistence) return;
|
|
3350
3946
|
try {
|
|
3351
|
-
|
|
3947
|
+
const mirrorPromise = (async () => {
|
|
3948
|
+
const userPromptMessage = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
|
|
3949
|
+
const mirrorResult = await mirrorCodexAppServerTranscript({
|
|
3950
|
+
sessionFile: params.params.sessionFile,
|
|
3951
|
+
agentId: params.agentId,
|
|
3952
|
+
sessionKey: params.sessionKey,
|
|
3953
|
+
sessionId: params.params.sessionId,
|
|
3954
|
+
cwd: params.cwd,
|
|
3955
|
+
messages: [userPromptMessage],
|
|
3956
|
+
idempotencyScope: `codex-app-server:${params.threadId}`,
|
|
3957
|
+
config: params.params.config
|
|
3958
|
+
});
|
|
3959
|
+
for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
|
|
3960
|
+
})();
|
|
3961
|
+
params.params.userTurnTranscriptRecorder?.markRuntimePersistencePending(mirrorPromise);
|
|
3962
|
+
await mirrorPromise;
|
|
3963
|
+
} catch (error) {
|
|
3964
|
+
embeddedAgentLog.warn("failed to mirror codex app-server prompt at turn start", { error });
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
/**
|
|
3968
|
+
* Tag a message with a stable logical identity for mirror dedupe. Callers
|
|
3969
|
+
* should use a value that is invariant for the same logical message across
|
|
3970
|
+
* re-emits (e.g. `${turnId}:prompt`, `${turnId}:assistant`) but distinct
|
|
3971
|
+
* for genuinely-distinct messages (different turns, different kinds). When
|
|
3972
|
+
* present this identity replaces the role/content fingerprint in the
|
|
3973
|
+
* idempotency key, so the dedupe survives caller-scope rotation without
|
|
3974
|
+
* collapsing distinct same-content turns.
|
|
3975
|
+
*/
|
|
3976
|
+
function attachCodexMirrorIdentity(message, identity) {
|
|
3977
|
+
const record = message;
|
|
3978
|
+
const existing = record["__openclaw"];
|
|
3979
|
+
const baseMeta = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
3980
|
+
return {
|
|
3981
|
+
...record,
|
|
3982
|
+
__openclaw: {
|
|
3983
|
+
...baseMeta,
|
|
3984
|
+
[MIRROR_IDENTITY_META_KEY]: identity
|
|
3985
|
+
}
|
|
3986
|
+
};
|
|
3987
|
+
}
|
|
3988
|
+
function readMirrorIdentity(message) {
|
|
3989
|
+
const meta = message["__openclaw"];
|
|
3990
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return;
|
|
3991
|
+
const id = meta[MIRROR_IDENTITY_META_KEY];
|
|
3992
|
+
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
3993
|
+
}
|
|
3994
|
+
function fingerprintMirrorMessageContent(message) {
|
|
3995
|
+
const payload = JSON.stringify({
|
|
3996
|
+
role: message.role,
|
|
3997
|
+
content: message.content
|
|
3998
|
+
});
|
|
3999
|
+
return createHash("sha256").update(payload).digest("hex").slice(0, 16);
|
|
4000
|
+
}
|
|
4001
|
+
function buildMirrorDedupeIdentity(message) {
|
|
4002
|
+
const explicit = readMirrorIdentity(message);
|
|
4003
|
+
if (explicit) return explicit;
|
|
4004
|
+
return `${message.role}:${fingerprintMirrorMessageContent(message)}`;
|
|
4005
|
+
}
|
|
4006
|
+
async function mirrorCodexAppServerTranscript(params) {
|
|
4007
|
+
const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
|
|
4008
|
+
if (messages.length === 0) return {
|
|
4009
|
+
assistantMirrorIdentitiesOwned: [],
|
|
4010
|
+
userMessagesPresent: []
|
|
4011
|
+
};
|
|
4012
|
+
const transcriptTarget = resolveCodexMirrorTranscriptTarget(params);
|
|
4013
|
+
const { appendedUpdates, assistantMirrorIdentitiesOwned, userMessagesPresent } = await withSessionTranscriptWriteLock({
|
|
4014
|
+
...transcriptTarget,
|
|
4015
|
+
config: params.config
|
|
4016
|
+
}, async (transcript) => {
|
|
4017
|
+
const nextAppendedUpdates = [];
|
|
4018
|
+
const nextAssistantMirrorIdentitiesOwned = /* @__PURE__ */ new Set();
|
|
4019
|
+
const nextUserMessagesPresent = [];
|
|
4020
|
+
const mirrorState = readTranscriptMirrorState(await transcript.readEvents());
|
|
4021
|
+
let nextMessageSeq = mirrorState.messageCount;
|
|
4022
|
+
for (const message of messages) {
|
|
4023
|
+
const dedupeIdentity = buildMirrorDedupeIdentity(message);
|
|
4024
|
+
const idempotencyKey = (message.role === "user" ? normalizeOptionalString(message.idempotencyKey) : void 0) ?? (params.idempotencyScope ? `${params.idempotencyScope}:${dedupeIdentity}` : void 0);
|
|
4025
|
+
const transcriptMessage = {
|
|
4026
|
+
...attachCodexMirrorOrigin(message),
|
|
4027
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
4028
|
+
};
|
|
4029
|
+
if (idempotencyKey && mirrorState.idempotencyKeys.has(idempotencyKey)) {
|
|
4030
|
+
const persistedUserMessage = mirrorState.userMessagesByIdempotencyKey.get(idempotencyKey);
|
|
4031
|
+
if (persistedUserMessage) nextUserMessagesPresent.push(persistedUserMessage);
|
|
4032
|
+
if (message.role === "assistant") nextAssistantMirrorIdentitiesOwned.add(dedupeIdentity);
|
|
4033
|
+
continue;
|
|
4034
|
+
}
|
|
4035
|
+
const nextMessage = runAgentHarnessBeforeMessageWriteHook({
|
|
4036
|
+
message: transcriptMessage,
|
|
4037
|
+
agentId: params.agentId,
|
|
4038
|
+
sessionKey: params.sessionKey
|
|
4039
|
+
});
|
|
4040
|
+
if (!nextMessage) {
|
|
4041
|
+
if (message.role === "assistant") nextAssistantMirrorIdentitiesOwned.add(dedupeIdentity);
|
|
4042
|
+
continue;
|
|
4043
|
+
}
|
|
4044
|
+
const messageToAppend = idempotencyKey ? {
|
|
4045
|
+
...attachCodexMirrorOrigin(nextMessage),
|
|
4046
|
+
idempotencyKey
|
|
4047
|
+
} : attachCodexMirrorOrigin(nextMessage);
|
|
4048
|
+
const appended = await transcript.appendMessage({
|
|
4049
|
+
message: messageToAppend,
|
|
4050
|
+
idempotencyLookup: idempotencyKey ? "caller-checked" : "scan",
|
|
4051
|
+
cwd: params.cwd
|
|
4052
|
+
});
|
|
4053
|
+
if (!appended) continue;
|
|
4054
|
+
const { messageId, message: appendedMessage } = appended;
|
|
4055
|
+
if (message.role === "assistant") nextAssistantMirrorIdentitiesOwned.add(dedupeIdentity);
|
|
4056
|
+
if (appendedMessage.role === "user") {
|
|
4057
|
+
nextUserMessagesPresent.push(appendedMessage);
|
|
4058
|
+
if (idempotencyKey) mirrorState.userMessagesByIdempotencyKey.set(idempotencyKey, appendedMessage);
|
|
4059
|
+
}
|
|
4060
|
+
nextMessageSeq += 1;
|
|
4061
|
+
nextAppendedUpdates.push({
|
|
4062
|
+
messageId,
|
|
4063
|
+
message: appendedMessage,
|
|
4064
|
+
messageSeq: nextMessageSeq
|
|
4065
|
+
});
|
|
4066
|
+
if (idempotencyKey) mirrorState.idempotencyKeys.add(idempotencyKey);
|
|
4067
|
+
}
|
|
4068
|
+
return {
|
|
4069
|
+
appendedUpdates: nextAppendedUpdates,
|
|
4070
|
+
assistantMirrorIdentitiesOwned: [...nextAssistantMirrorIdentitiesOwned],
|
|
4071
|
+
userMessagesPresent: nextUserMessagesPresent
|
|
4072
|
+
};
|
|
4073
|
+
});
|
|
4074
|
+
for (const update of appendedUpdates) try {
|
|
4075
|
+
await publishSessionTranscriptUpdateByIdentity({
|
|
4076
|
+
...transcriptTarget,
|
|
4077
|
+
update: {
|
|
4078
|
+
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
4079
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
4080
|
+
message: update.message,
|
|
4081
|
+
messageId: update.messageId,
|
|
4082
|
+
messageSeq: update.messageSeq
|
|
4083
|
+
}
|
|
4084
|
+
});
|
|
4085
|
+
} catch (error) {
|
|
4086
|
+
embeddedAgentLog.warn("failed to publish codex app-server transcript update", { error: formatErrorMessage(error) });
|
|
4087
|
+
}
|
|
4088
|
+
return {
|
|
4089
|
+
assistantMirrorIdentitiesOwned,
|
|
4090
|
+
userMessagesPresent
|
|
4091
|
+
};
|
|
4092
|
+
}
|
|
4093
|
+
function resolveCodexMirrorTranscriptTarget(params) {
|
|
4094
|
+
return {
|
|
4095
|
+
...params.agentId ? { agentId: params.agentId } : {},
|
|
4096
|
+
sessionFile: params.sessionFile,
|
|
4097
|
+
sessionId: params.sessionId,
|
|
4098
|
+
sessionKey: params.sessionKey ?? ""
|
|
4099
|
+
};
|
|
4100
|
+
}
|
|
4101
|
+
function readTranscriptMirrorState(events) {
|
|
4102
|
+
const idempotencyKeys = /* @__PURE__ */ new Set();
|
|
4103
|
+
const userMessagesByIdempotencyKey = /* @__PURE__ */ new Map();
|
|
4104
|
+
let messageCount = 0;
|
|
4105
|
+
for (const event of events) {
|
|
4106
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) continue;
|
|
4107
|
+
const parsed = event;
|
|
4108
|
+
if (parsed.type === "message") messageCount += 1;
|
|
4109
|
+
if (typeof parsed.message?.idempotencyKey === "string") {
|
|
4110
|
+
idempotencyKeys.add(parsed.message.idempotencyKey);
|
|
4111
|
+
if (parsed.message.role === "user") userMessagesByIdempotencyKey.set(parsed.message.idempotencyKey, parsed.message);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
return {
|
|
4115
|
+
idempotencyKeys,
|
|
4116
|
+
messageCount,
|
|
4117
|
+
userMessagesByIdempotencyKey
|
|
4118
|
+
};
|
|
4119
|
+
}
|
|
4120
|
+
//#endregion
|
|
4121
|
+
//#region extensions/codex/src/app-server/event-projector.ts
|
|
4122
|
+
/** Projects metadata-only lifecycle diagnostics for native tool items. */
|
|
4123
|
+
var CodexNativeToolLifecycleProjector = class {
|
|
4124
|
+
constructor(context, threadId, turnId, options = {}) {
|
|
4125
|
+
this.context = context;
|
|
4126
|
+
this.threadId = threadId;
|
|
4127
|
+
this.turnId = turnId;
|
|
4128
|
+
this.options = options;
|
|
4129
|
+
this.startedAtByItem = /* @__PURE__ */ new Map();
|
|
4130
|
+
this.activeItems = /* @__PURE__ */ new Map();
|
|
4131
|
+
this.webSearchCompletionByItem = /* @__PURE__ */ new Map();
|
|
4132
|
+
this.completedItemIds = /* @__PURE__ */ new Set();
|
|
4133
|
+
this.approvalFailureDispositionByItem = /* @__PURE__ */ new Map();
|
|
4134
|
+
this.preToolUseFailureByItem = /* @__PURE__ */ new Map();
|
|
4135
|
+
this.finalized = false;
|
|
4136
|
+
}
|
|
4137
|
+
handleNotification(notification) {
|
|
4138
|
+
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
4139
|
+
if (!params || readCodexNotificationThreadId(params) !== this.threadId || readCodexNotificationTurnId(params) !== this.turnId) return;
|
|
4140
|
+
if (notification.method === "turn/completed") {
|
|
4141
|
+
const turn = readCodexTurn(params.turn);
|
|
4142
|
+
if (!turn || turn.id !== this.turnId) return;
|
|
4143
|
+
for (const item of turn.items ?? []) this.recordSnapshotItem(item);
|
|
4144
|
+
return;
|
|
4145
|
+
}
|
|
4146
|
+
if (notification.method === "rawResponseItem/completed") {
|
|
4147
|
+
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
4148
|
+
if (item) this.recordRawWebSearchResult(item);
|
|
4149
|
+
return;
|
|
4150
|
+
}
|
|
4151
|
+
if (notification.method !== "item/started" && notification.method !== "item/completed") return;
|
|
4152
|
+
const item = readItem(params.item);
|
|
4153
|
+
if (!item) return;
|
|
4154
|
+
this.recordItem({
|
|
4155
|
+
phase: notification.method === "item/started" ? "start" : "result",
|
|
4156
|
+
item,
|
|
4157
|
+
sourceTimestampMs: asDateTimestampMs(notification.method === "item/started" ? params.startedAtMs : params.completedAtMs)
|
|
4158
|
+
});
|
|
4159
|
+
}
|
|
4160
|
+
recordItem(params) {
|
|
4161
|
+
const toolName = auditNativeToolName(params.item);
|
|
4162
|
+
if (!toolName || this.completedItemIds.has(params.item.id)) return;
|
|
4163
|
+
if (params.phase === "start") {
|
|
4164
|
+
this.recordStarted(params.item.id, toolName, auditNativeToolUnfinishedStatus(params.item), params.sourceTimestampMs);
|
|
4165
|
+
return;
|
|
4166
|
+
}
|
|
4167
|
+
if (params.item.type === "webSearch") {
|
|
4168
|
+
this.webSearchCompletionByItem.set(params.item.id, {
|
|
4169
|
+
runWasAborted: this.options.runAbortSignal?.aborted === true,
|
|
4170
|
+
sourceTimestampMs: params.sourceTimestampMs
|
|
4171
|
+
});
|
|
4172
|
+
return;
|
|
4173
|
+
}
|
|
4174
|
+
const itemDurationMs = typeof params.item.durationMs === "number" ? params.item.durationMs : void 0;
|
|
4175
|
+
this.recordTerminal(params.item.id, toolName, auditNativeToolTerminalStatus(params.item), {
|
|
4176
|
+
itemDurationMs,
|
|
4177
|
+
sourceTimestampMs: params.sourceTimestampMs
|
|
4178
|
+
});
|
|
4179
|
+
}
|
|
4180
|
+
recordApprovalFailureDisposition(toolCallId, disposition) {
|
|
4181
|
+
if (!this.completedItemIds.has(toolCallId)) this.approvalFailureDispositionByItem.set(toolCallId, disposition);
|
|
4182
|
+
}
|
|
4183
|
+
recordPreToolUseFailure(failure, runWasAborted = this.options.runAbortSignal?.aborted === true) {
|
|
4184
|
+
if (this.completedItemIds.has(failure.toolCallId)) return;
|
|
4185
|
+
const record = {
|
|
4186
|
+
failure,
|
|
4187
|
+
terminalReason: runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : failure.disposition
|
|
4188
|
+
};
|
|
4189
|
+
if (this.finalized) {
|
|
4190
|
+
this.completedItemIds.add(failure.toolCallId);
|
|
4191
|
+
this.emitPreToolUseFailure(record, failure.toolName, failure.durationMs);
|
|
4192
|
+
return;
|
|
4193
|
+
}
|
|
4194
|
+
this.preToolUseFailureByItem.set(failure.toolCallId, record);
|
|
4195
|
+
}
|
|
4196
|
+
recordRawWebSearchResult(item) {
|
|
4197
|
+
if (readString(item, "type") !== "web_search_call") return;
|
|
4198
|
+
const toolCallId = readString(item, "id");
|
|
4199
|
+
if (!toolCallId || this.completedItemIds.has(toolCallId)) return;
|
|
4200
|
+
const toolName = "web_search";
|
|
4201
|
+
this.recordStarted(toolCallId, toolName, "unknown");
|
|
4202
|
+
const rawStatus = readString(item, "status");
|
|
4203
|
+
if (rawStatus === "in_progress" || rawStatus === "running") return;
|
|
4204
|
+
const status = rawStatus === "completed" ? "completed" : rawStatus === "cancelled" ? "cancelled" : rawStatus === "failed" || rawStatus === "error" || rawStatus === "incomplete" ? "failed" : "unknown";
|
|
4205
|
+
this.recordTerminal(toolCallId, toolName, status, { sourceTimestampMs: this.webSearchCompletionByItem.get(toolCallId)?.sourceTimestampMs });
|
|
4206
|
+
}
|
|
4207
|
+
recordTerminal(toolCallId, toolName, status, options = {}) {
|
|
4208
|
+
const runWasAborted = options.runWasAborted ?? this.options.runAbortSignal?.aborted === true;
|
|
4209
|
+
const preToolUseFailure = this.preToolUseFailureByItem.get(toolCallId);
|
|
4210
|
+
this.preToolUseFailureByItem.delete(toolCallId);
|
|
4211
|
+
const approvalFailureDisposition = this.approvalFailureDispositionByItem.get(toolCallId);
|
|
4212
|
+
this.approvalFailureDispositionByItem.delete(toolCallId);
|
|
4213
|
+
this.completedItemIds.add(toolCallId);
|
|
4214
|
+
this.activeItems.delete(toolCallId);
|
|
4215
|
+
this.webSearchCompletionByItem.delete(toolCallId);
|
|
4216
|
+
const startedAt = this.startedAtByItem.get(toolCallId);
|
|
4217
|
+
this.startedAtByItem.delete(toolCallId);
|
|
4218
|
+
const endedAt = options.sourceTimestampMs ?? Date.now();
|
|
4219
|
+
const durationMs = options.itemDurationMs ?? (startedAt === void 0 ? 0 : Math.max(0, endedAt - startedAt));
|
|
4220
|
+
if (preToolUseFailure) {
|
|
4221
|
+
this.emitPreToolUseFailure(preToolUseFailure, toolName, durationMs, options.sourceTimestampMs);
|
|
4222
|
+
return;
|
|
4223
|
+
}
|
|
4224
|
+
const terminalEvent = approvalFailureDisposition ? {
|
|
4225
|
+
type: "tool.execution.error",
|
|
4226
|
+
durationMs,
|
|
4227
|
+
errorCategory: "codex_native_tool_approval",
|
|
4228
|
+
terminalReason: approvalFailureDisposition
|
|
4229
|
+
} : status === "blocked" ? {
|
|
4230
|
+
type: "tool.execution.blocked",
|
|
4231
|
+
reason: "codex_native_tool_blocked",
|
|
4232
|
+
deniedReason: "codex_native_tool_blocked"
|
|
4233
|
+
} : status === "failed" || status === "cancelled" || status === "unknown" ? {
|
|
4234
|
+
type: "tool.execution.error",
|
|
4235
|
+
durationMs,
|
|
4236
|
+
errorCategory: status === "unknown" ? "codex_native_tool_outcome_unknown" : status === "cancelled" ? "aborted" : "codex_native_tool_error",
|
|
4237
|
+
...status === "unknown" ? { errorCode: "tool_outcome_unknown" } : {},
|
|
4238
|
+
terminalReason: status === "unknown" ? "failed" : runWasAborted && this.options.runAbortSignal ? resolveCodexToolAbortTerminalReason(this.options.runAbortSignal) : status === "cancelled" ? "cancelled" : "failed"
|
|
4239
|
+
} : {
|
|
4240
|
+
type: "tool.execution.completed",
|
|
4241
|
+
durationMs
|
|
4242
|
+
};
|
|
4243
|
+
emitTrustedDiagnosticEvent({
|
|
4244
|
+
...this.buildBase(toolCallId, toolName),
|
|
4245
|
+
...terminalEvent,
|
|
4246
|
+
...options.sourceTimestampMs !== void 0 ? { sourceTimestampMs: options.sourceTimestampMs } : {}
|
|
4247
|
+
});
|
|
4248
|
+
}
|
|
4249
|
+
finalizeActive(runWasAborted = this.options.runAbortSignal?.aborted === true) {
|
|
4250
|
+
this.finalized = true;
|
|
4251
|
+
for (const [toolCallId, { toolName, unfinishedStatus }] of this.activeItems) {
|
|
4252
|
+
const webSearchCompletion = this.webSearchCompletionByItem.get(toolCallId);
|
|
4253
|
+
const itemRunWasAborted = webSearchCompletion ? webSearchCompletion.runWasAborted : runWasAborted;
|
|
4254
|
+
this.recordTerminal(toolCallId, toolName, unfinishedStatus, {
|
|
4255
|
+
runWasAborted: itemRunWasAborted,
|
|
4256
|
+
sourceTimestampMs: webSearchCompletion?.sourceTimestampMs
|
|
4257
|
+
});
|
|
4258
|
+
}
|
|
4259
|
+
for (const [toolCallId, record] of this.preToolUseFailureByItem) if (!this.completedItemIds.has(toolCallId)) this.recordTerminal(toolCallId, record.failure.toolName, "failed", { itemDurationMs: record.failure.durationMs });
|
|
4260
|
+
this.activeItems.clear();
|
|
4261
|
+
this.webSearchCompletionByItem.clear();
|
|
4262
|
+
this.approvalFailureDispositionByItem.clear();
|
|
4263
|
+
this.preToolUseFailureByItem.clear();
|
|
4264
|
+
}
|
|
4265
|
+
emitPreToolUseFailure(record, toolName, durationMs, sourceTimestampMs) {
|
|
4266
|
+
emitCodexNativePreToolUseFailureDiagnostic({
|
|
4267
|
+
agentId: this.context.agentId,
|
|
4268
|
+
sessionId: this.context.sessionId,
|
|
4269
|
+
sessionKey: this.context.sessionKey,
|
|
4270
|
+
runId: this.context.runId,
|
|
4271
|
+
failure: {
|
|
4272
|
+
...record.failure,
|
|
4273
|
+
toolName,
|
|
4274
|
+
durationMs
|
|
4275
|
+
},
|
|
4276
|
+
terminalReason: record.terminalReason,
|
|
4277
|
+
sourceTimestampMs
|
|
4278
|
+
});
|
|
4279
|
+
}
|
|
4280
|
+
recordSnapshotItem(item) {
|
|
4281
|
+
if (!auditNativeToolName(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
|
|
4282
|
+
const toolName = auditNativeToolName(item);
|
|
4283
|
+
if (!toolName) return;
|
|
4284
|
+
this.recordStarted(item.id, toolName, auditNativeToolUnfinishedStatus(item));
|
|
4285
|
+
this.recordItem({
|
|
4286
|
+
phase: "result",
|
|
4287
|
+
item
|
|
4288
|
+
});
|
|
4289
|
+
}
|
|
4290
|
+
recordStarted(toolCallId, toolName, unfinishedStatus, sourceTimestampMs) {
|
|
4291
|
+
if (this.activeItems.has(toolCallId)) return;
|
|
4292
|
+
this.startedAtByItem.set(toolCallId, sourceTimestampMs ?? Date.now());
|
|
4293
|
+
this.activeItems.set(toolCallId, {
|
|
4294
|
+
toolName,
|
|
4295
|
+
unfinishedStatus
|
|
4296
|
+
});
|
|
4297
|
+
emitTrustedDiagnosticEvent({
|
|
4298
|
+
type: "tool.execution.started",
|
|
4299
|
+
...this.buildBase(toolCallId, toolName),
|
|
4300
|
+
...sourceTimestampMs !== void 0 ? { sourceTimestampMs } : {}
|
|
4301
|
+
});
|
|
4302
|
+
}
|
|
4303
|
+
buildBase(toolCallId, toolName) {
|
|
4304
|
+
return {
|
|
4305
|
+
agentId: this.context.agentId,
|
|
4306
|
+
runId: this.context.runId,
|
|
4307
|
+
sessionId: this.context.sessionId,
|
|
4308
|
+
sessionKey: this.context.sessionKey,
|
|
4309
|
+
toolName,
|
|
4310
|
+
toolCallId
|
|
4311
|
+
};
|
|
4312
|
+
}
|
|
4313
|
+
};
|
|
4314
|
+
const ZERO_USAGE = {
|
|
4315
|
+
input: 0,
|
|
4316
|
+
output: 0,
|
|
4317
|
+
cacheRead: 0,
|
|
4318
|
+
cacheWrite: 0,
|
|
4319
|
+
totalTokens: 0,
|
|
4320
|
+
cost: {
|
|
4321
|
+
input: 0,
|
|
4322
|
+
output: 0,
|
|
4323
|
+
cacheRead: 0,
|
|
4324
|
+
cacheWrite: 0,
|
|
4325
|
+
total: 0
|
|
4326
|
+
}
|
|
4327
|
+
};
|
|
4328
|
+
const MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM = 20;
|
|
4329
|
+
const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 12e3;
|
|
4330
|
+
const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
|
|
4331
|
+
const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
|
|
4332
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
4333
|
+
const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * BYTES_PER_MB;
|
|
4334
|
+
const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
4335
|
+
"message",
|
|
4336
|
+
"messages",
|
|
4337
|
+
"reply",
|
|
4338
|
+
"send",
|
|
4339
|
+
"reaction",
|
|
4340
|
+
"react",
|
|
4341
|
+
"typing"
|
|
4342
|
+
]);
|
|
4343
|
+
function shouldEmitTranscriptToolProgress(toolName, _args) {
|
|
4344
|
+
const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
|
|
4345
|
+
return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
|
|
4346
|
+
}
|
|
4347
|
+
var CodexAppServerEventProjector = class {
|
|
4348
|
+
constructor(params, threadId, turnId, options = {}) {
|
|
4349
|
+
this.params = params;
|
|
4350
|
+
this.threadId = threadId;
|
|
4351
|
+
this.turnId = turnId;
|
|
4352
|
+
this.options = options;
|
|
4353
|
+
this.assistantTextByItem = /* @__PURE__ */ new Map();
|
|
4354
|
+
this.assistantItemOrder = [];
|
|
4355
|
+
this.assistantPhaseByItem = /* @__PURE__ */ new Map();
|
|
4356
|
+
this.latestTerminalAssistantCandidateSuperseded = false;
|
|
4357
|
+
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
|
|
4358
|
+
this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
|
|
4359
|
+
this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
|
|
4360
|
+
this.pendingRawCommentaryEchoes = 0;
|
|
4361
|
+
this.reasoningTextByGroup = /* @__PURE__ */ new Map();
|
|
4362
|
+
this.reasoningItemOrder = /* @__PURE__ */ new Map();
|
|
4363
|
+
this.planTextByItem = /* @__PURE__ */ new Map();
|
|
4364
|
+
this.activeItemIds = /* @__PURE__ */ new Set();
|
|
4365
|
+
this.completedItemIds = /* @__PURE__ */ new Set();
|
|
4366
|
+
this.activeCompactionItemIds = /* @__PURE__ */ new Set();
|
|
4367
|
+
this.toolProgressTexts = /* @__PURE__ */ new Set();
|
|
4368
|
+
this.toolResultSummaryItemIds = /* @__PURE__ */ new Set();
|
|
4369
|
+
this.toolResultOutputItemIds = /* @__PURE__ */ new Set();
|
|
4370
|
+
this.toolResultOutputStreamedItemIds = /* @__PURE__ */ new Set();
|
|
4371
|
+
this.transcriptToolProgressSuppressedIds = /* @__PURE__ */ new Set();
|
|
4372
|
+
this.toolTranscriptArgumentsById = /* @__PURE__ */ new Map();
|
|
4373
|
+
this.toolResultOutputDeltaState = /* @__PURE__ */ new Map();
|
|
4374
|
+
this.toolResultOutputTextByItem = /* @__PURE__ */ new Map();
|
|
4375
|
+
this.toolMetas = /* @__PURE__ */ new Map();
|
|
4376
|
+
this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
|
|
4377
|
+
this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
|
|
4378
|
+
this.sideEffectingToolItemIds = /* @__PURE__ */ new Set();
|
|
4379
|
+
this.sideEffectingDynamicToolCallIds = /* @__PURE__ */ new Set();
|
|
4380
|
+
this.toolTranscriptMessages = [];
|
|
4381
|
+
this.toolTranscriptCallIds = /* @__PURE__ */ new Set();
|
|
4382
|
+
this.toolTranscriptResultIds = /* @__PURE__ */ new Set();
|
|
4383
|
+
this.toolTranscriptNamesById = /* @__PURE__ */ new Map();
|
|
4384
|
+
this.toolTrajectoryCallIds = /* @__PURE__ */ new Set();
|
|
4385
|
+
this.toolTrajectoryResultIds = /* @__PURE__ */ new Set();
|
|
4386
|
+
this.toolTrajectoryNamesById = /* @__PURE__ */ new Map();
|
|
4387
|
+
this.toolTrajectoryItemsById = /* @__PURE__ */ new Map();
|
|
4388
|
+
this.transcriptToolProgressCallIds = /* @__PURE__ */ new Set();
|
|
4389
|
+
this.nativeGeneratedMediaItemIds = /* @__PURE__ */ new Set();
|
|
4390
|
+
this.nativeGeneratedMediaUrlsByItemId = /* @__PURE__ */ new Map();
|
|
4391
|
+
this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
|
|
4392
|
+
this.assistantStarted = false;
|
|
4393
|
+
this.reasoningStarted = false;
|
|
4394
|
+
this.reasoningEnded = false;
|
|
4395
|
+
this.streamedPartialAssistantItemReplaceable = false;
|
|
4396
|
+
this.promptErrorSource = null;
|
|
4397
|
+
this.synthesizedMissingToolResultError = null;
|
|
4398
|
+
this.aborted = false;
|
|
4399
|
+
this.guardianReviewCount = 0;
|
|
4400
|
+
this.completedCompactionCount = 0;
|
|
4401
|
+
this.nativeToolLifecycleProjector = new CodexNativeToolLifecycleProjector(params, threadId, turnId, { runAbortSignal: options.runAbortSignal });
|
|
4402
|
+
}
|
|
4403
|
+
getCompletedTurnStatus() {
|
|
4404
|
+
return this.completedTurn?.status;
|
|
4405
|
+
}
|
|
4406
|
+
hasCompletedTerminalAssistantText() {
|
|
4407
|
+
const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
|
|
4408
|
+
if (!latestCompletedItemId) return false;
|
|
4409
|
+
const finalItem = this.resolveFinalAssistantTextItem();
|
|
4410
|
+
return this.latestCompletedItemId === latestCompletedItemId && finalItem?.itemId === latestCompletedItemId && this.completedItemIds.has(latestCompletedItemId);
|
|
4411
|
+
}
|
|
4412
|
+
getLatestTerminalAssistantCandidate() {
|
|
4413
|
+
const itemId = this.latestTerminalAssistantCandidateItemId;
|
|
4414
|
+
if (!itemId) return;
|
|
4415
|
+
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
4416
|
+
return {
|
|
4417
|
+
itemId,
|
|
4418
|
+
hasText: Boolean(text && !this.toolProgressTexts.has(text))
|
|
4419
|
+
};
|
|
4420
|
+
}
|
|
4421
|
+
hasLatestTerminalAssistantCandidateText() {
|
|
4422
|
+
return !this.latestTerminalAssistantCandidateSuperseded && this.getLatestTerminalAssistantCandidate()?.hasText === true;
|
|
4423
|
+
}
|
|
4424
|
+
canReleaseLatestTerminalAssistantAfterToolHandoff() {
|
|
4425
|
+
return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
|
|
4426
|
+
}
|
|
4427
|
+
/** Restores a completed final item after only the enclosing turn timeout fired. */
|
|
4428
|
+
recoverCompletedTerminalAssistantAfterTurnWatchTimeout() {
|
|
4429
|
+
if (!this.aborted || this.promptError !== "codex app-server attempt timed out" || !this.hasCompletedTerminalAssistantText()) return false;
|
|
4430
|
+
this.aborted = false;
|
|
4431
|
+
this.promptError = void 0;
|
|
4432
|
+
this.promptErrorSource = null;
|
|
4433
|
+
return true;
|
|
4434
|
+
}
|
|
4435
|
+
/** Resolves the shared model-order position for a native tool item. */
|
|
4436
|
+
recordNativeToolOutcome(item) {
|
|
4437
|
+
if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
4438
|
+
const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
|
|
4439
|
+
if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
|
|
4440
|
+
}
|
|
4441
|
+
recordNativeToolApprovalFailure(toolCallId, disposition) {
|
|
4442
|
+
this.nativeToolLifecycleProjector.recordApprovalFailureDisposition(toolCallId, disposition);
|
|
4443
|
+
}
|
|
4444
|
+
recordNativeToolPreToolUseFailure(failure) {
|
|
4445
|
+
this.nativeToolLifecycleProjector.recordPreToolUseFailure(failure);
|
|
4446
|
+
}
|
|
4447
|
+
async handleNotification(notification) {
|
|
4448
|
+
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
4449
|
+
if (!params) return;
|
|
4450
|
+
if (isHookNotificationMethod(notification.method)) {
|
|
4451
|
+
if (!this.isHookNotificationForCurrentThread(params)) return;
|
|
4452
|
+
} else if (notification.method === "guardianWarning") {
|
|
4453
|
+
if (readCodexNotificationThreadId(params) !== this.threadId) return;
|
|
4454
|
+
} else if (!this.isNotificationForTurn(params)) return;
|
|
4455
|
+
this.nativeToolLifecycleProjector.handleNotification(notification);
|
|
4456
|
+
switch (notification.method) {
|
|
4457
|
+
case "item/agentMessage/delta":
|
|
4458
|
+
await this.handleAssistantDelta(params);
|
|
4459
|
+
break;
|
|
4460
|
+
case "item/reasoning/summaryTextDelta":
|
|
4461
|
+
case "item/reasoning/textDelta":
|
|
4462
|
+
await this.handleReasoningDelta(notification.method, params);
|
|
4463
|
+
break;
|
|
4464
|
+
case "item/plan/delta":
|
|
4465
|
+
this.handlePlanDelta(params);
|
|
4466
|
+
break;
|
|
4467
|
+
case "turn/plan/updated":
|
|
4468
|
+
this.handleTurnPlanUpdated(params);
|
|
4469
|
+
break;
|
|
4470
|
+
case "item/started":
|
|
4471
|
+
await this.handleItemStarted(params);
|
|
4472
|
+
break;
|
|
4473
|
+
case "item/completed":
|
|
4474
|
+
await this.handleItemCompleted(params);
|
|
4475
|
+
break;
|
|
4476
|
+
case "item/commandExecution/outputDelta":
|
|
4477
|
+
this.handleOutputDelta(params, "bash");
|
|
4478
|
+
break;
|
|
4479
|
+
case "item/autoApprovalReview/started":
|
|
4480
|
+
case "item/autoApprovalReview/completed":
|
|
4481
|
+
this.handleGuardianReviewNotification(notification.method, params);
|
|
4482
|
+
break;
|
|
4483
|
+
case "guardianWarning":
|
|
4484
|
+
this.handleGuardianWarning(params);
|
|
4485
|
+
break;
|
|
4486
|
+
case "hook/started":
|
|
4487
|
+
case "hook/completed":
|
|
4488
|
+
this.handleHookNotification(notification.method, params);
|
|
4489
|
+
break;
|
|
4490
|
+
case "thread/tokenUsage/updated":
|
|
4491
|
+
this.handleTokenUsage(params);
|
|
4492
|
+
break;
|
|
4493
|
+
case "turn/completed":
|
|
4494
|
+
await this.handleTurnCompleted(params);
|
|
4495
|
+
break;
|
|
4496
|
+
case "rawResponseItem/completed":
|
|
4497
|
+
await this.handleRawResponseItemCompleted(params);
|
|
4498
|
+
break;
|
|
4499
|
+
case "error":
|
|
4500
|
+
if (params.willRetry === true) break;
|
|
4501
|
+
this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
|
|
4502
|
+
this.promptErrorSource = "prompt";
|
|
4503
|
+
break;
|
|
4504
|
+
default: break;
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
buildResult(toolTelemetry, options) {
|
|
4508
|
+
this.nativeToolLifecycleProjector.finalizeActive();
|
|
4509
|
+
const assistantTexts = this.collectAssistantTexts();
|
|
4510
|
+
const reasoningText = collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
|
|
4511
|
+
const planText = collectTextValues(this.planTextByItem).join("\n\n");
|
|
4512
|
+
const hasAssistantItemText = this.hasAssistantItemTextForSynthesis();
|
|
4513
|
+
const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
|
|
4514
|
+
const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
|
|
4515
|
+
this.synthesizeMissingToolResults({
|
|
4516
|
+
synthesize: legacyFailClosed,
|
|
4517
|
+
recordPromptError: legacyFailClosed && !hasDeliverableAssistantOnCompletedTurn
|
|
4518
|
+
});
|
|
4519
|
+
const lastAssistant = assistantTexts.length > 0 ? this.createAssistantMessage(assistantTexts.join("\n\n")) : void 0;
|
|
4520
|
+
const currentAttemptAssistant = this.createCurrentAttemptAssistantMessage();
|
|
4521
|
+
const turnId = this.turnId;
|
|
4522
|
+
const messagesSnapshot = this.params.suppressNextUserMessagePersistence ? [] : [attachCodexMirrorIdentity(buildCodexUserPromptMessage(this.params), `${turnId}:prompt`)];
|
|
4523
|
+
if (reasoningText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex reasoning", reasoningText), `${turnId}:reasoning`));
|
|
4524
|
+
if (planText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex plan", planText), `${turnId}:plan`));
|
|
4525
|
+
messagesSnapshot.push(...this.toolTranscriptMessages);
|
|
4526
|
+
if (lastAssistant) messagesSnapshot.push(attachCodexMirrorIdentity(lastAssistant, `${turnId}:assistant`));
|
|
4527
|
+
const turnFailed = this.completedTurn?.status === "failed";
|
|
4528
|
+
const promptError = this.promptError ?? this.synthesizedMissingToolResultError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
|
|
4529
|
+
const agentHarnessResultClassification = classifyAgentHarnessTerminalOutcome({
|
|
4530
|
+
assistantTexts,
|
|
4531
|
+
reasoningText,
|
|
4532
|
+
planText,
|
|
4533
|
+
promptError,
|
|
4534
|
+
turnCompleted: Boolean(this.completedTurn)
|
|
4535
|
+
});
|
|
4536
|
+
const toolMetas = [...this.toolMetas.values()];
|
|
4537
|
+
const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.nativeGeneratedMediaItemIds.size > 0 || this.sideEffectingToolItemIds.size > 0 || this.sideEffectingDynamicToolCallIds.size > 0;
|
|
4538
|
+
return {
|
|
4539
|
+
aborted: this.aborted,
|
|
4540
|
+
externalAbort: false,
|
|
4541
|
+
timedOut: false,
|
|
4542
|
+
idleTimedOut: false,
|
|
4543
|
+
timedOutDuringCompaction: false,
|
|
4544
|
+
timedOutDuringToolExecution: false,
|
|
4545
|
+
promptError,
|
|
4546
|
+
promptErrorSource: promptError ? this.promptErrorSource || "prompt" : null,
|
|
4547
|
+
sessionIdUsed: this.params.sessionId,
|
|
4548
|
+
...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
|
|
4549
|
+
bootstrapPromptWarningSignaturesSeen: this.params.bootstrapPromptWarningSignaturesSeen,
|
|
4550
|
+
bootstrapPromptWarningSignature: this.params.bootstrapPromptWarningSignature,
|
|
4551
|
+
messagesSnapshot,
|
|
4552
|
+
assistantTexts,
|
|
4553
|
+
toolMetas,
|
|
4554
|
+
lastAssistant,
|
|
4555
|
+
currentAttemptAssistant,
|
|
4556
|
+
...this.lastNativeToolError ? { lastToolError: this.lastNativeToolError } : {},
|
|
4557
|
+
didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
|
|
4558
|
+
didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
|
|
4559
|
+
messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
|
|
4560
|
+
messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
|
|
4561
|
+
messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
|
|
4562
|
+
messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
|
|
4563
|
+
heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
|
|
4564
|
+
toolMediaUrls: this.buildToolMediaUrls(toolTelemetry),
|
|
4565
|
+
toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
|
|
4566
|
+
successfulCronAdds: toolTelemetry.successfulCronAdds,
|
|
4567
|
+
cloudCodeAssistFormatError: false,
|
|
4568
|
+
attemptUsage: this.tokenUsage,
|
|
4569
|
+
replayMetadata: {
|
|
4570
|
+
hadPotentialSideEffects,
|
|
4571
|
+
replaySafe: !hadPotentialSideEffects
|
|
4572
|
+
},
|
|
4573
|
+
itemLifecycle: {
|
|
4574
|
+
startedCount: this.activeItemIds.size + this.completedItemIds.size,
|
|
4575
|
+
completedCount: this.completedItemIds.size,
|
|
4576
|
+
activeCount: this.activeItemIds.size,
|
|
4577
|
+
...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {}
|
|
4578
|
+
},
|
|
4579
|
+
yieldDetected: options?.yieldDetected || false,
|
|
4580
|
+
didSendDeterministicApprovalPrompt: this.guardianReviewCount > 0 ? false : void 0
|
|
4581
|
+
};
|
|
4582
|
+
}
|
|
4583
|
+
recordDynamicToolCall(params) {
|
|
4584
|
+
const args = sanitizeCodexToolArguments(params.arguments);
|
|
4585
|
+
this.recordToolTranscriptCall({
|
|
4586
|
+
id: params.callId,
|
|
4587
|
+
name: params.tool,
|
|
4588
|
+
arguments: args
|
|
4589
|
+
});
|
|
4590
|
+
}
|
|
4591
|
+
recordDynamicToolResult(params) {
|
|
4592
|
+
const resultText = collectDynamicToolContentText(params.contentItems);
|
|
4593
|
+
if (params.asyncStarted === true) {
|
|
4594
|
+
const existing = this.toolMetas.get(params.callId);
|
|
4595
|
+
this.toolMetas.set(params.callId, {
|
|
4596
|
+
toolName: existing?.toolName ?? params.tool,
|
|
4597
|
+
...existing?.meta ? { meta: existing.meta } : {},
|
|
4598
|
+
asyncStarted: true
|
|
4599
|
+
});
|
|
4600
|
+
}
|
|
4601
|
+
this.recordToolTranscriptResult({
|
|
4602
|
+
id: params.callId,
|
|
4603
|
+
name: params.tool,
|
|
4604
|
+
text: resultText,
|
|
4605
|
+
isError: !params.success
|
|
4606
|
+
});
|
|
4607
|
+
if (!params.success && params.terminalType === "blocked") this.lastNativeToolError = {
|
|
4608
|
+
toolName: params.tool,
|
|
4609
|
+
error: resultText || "codex dynamic tool blocked"
|
|
4610
|
+
};
|
|
4611
|
+
else if (params.success && this.lastNativeToolError && !this.lastNativeToolError.mutatingAction) this.lastNativeToolError = void 0;
|
|
4612
|
+
if (params.sideEffectEvidence === true) this.sideEffectingDynamicToolCallIds.add(params.callId);
|
|
4613
|
+
}
|
|
4614
|
+
markTimedOut() {
|
|
4615
|
+
this.aborted = true;
|
|
4616
|
+
this.promptError = "codex app-server attempt timed out";
|
|
4617
|
+
this.promptErrorSource = "prompt";
|
|
4618
|
+
}
|
|
4619
|
+
markAborted() {
|
|
4620
|
+
this.aborted = true;
|
|
4621
|
+
}
|
|
4622
|
+
isCompacting() {
|
|
4623
|
+
return this.activeCompactionItemIds.size > 0;
|
|
4624
|
+
}
|
|
4625
|
+
async handleAssistantDelta(params) {
|
|
4626
|
+
const itemId = readString(params, "itemId") ?? "assistant";
|
|
4627
|
+
const delta = readString(params, "delta") ?? "";
|
|
4628
|
+
if (!delta) return;
|
|
4629
|
+
if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4630
|
+
const isCommentary = this.isCommentaryAssistantItem(itemId);
|
|
4631
|
+
if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
|
|
4632
|
+
if (!this.assistantStarted) {
|
|
4633
|
+
this.assistantStarted = true;
|
|
4634
|
+
await this.params.onAssistantMessageStart?.();
|
|
4635
|
+
}
|
|
4636
|
+
this.rememberAssistantItem(itemId);
|
|
4637
|
+
const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
|
|
4638
|
+
this.assistantTextByItem.set(itemId, text);
|
|
4639
|
+
if (isCommentary) this.emitCommentaryProgress({
|
|
4640
|
+
itemId,
|
|
4641
|
+
text
|
|
4642
|
+
});
|
|
4643
|
+
else {
|
|
4644
|
+
const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
|
|
4645
|
+
const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
|
|
4646
|
+
if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
|
|
4647
|
+
else if (this.streamedPartialAssistantItemId === void 0) this.streamedPartialAssistantItemReplaceable = !knownFinalAnswer;
|
|
4648
|
+
this.streamedPartialAssistantItemId = itemId;
|
|
4649
|
+
const replaceable = this.streamedPartialAssistantItemReplaceable;
|
|
4650
|
+
const replacement = replace && replaceable;
|
|
4651
|
+
const streamPayload = {
|
|
4652
|
+
text,
|
|
4653
|
+
delta: replacement ? "" : delta,
|
|
4654
|
+
...replacement ? { replace: true } : {}
|
|
4655
|
+
};
|
|
4656
|
+
this.emitAgentEvent({
|
|
4657
|
+
stream: "assistant",
|
|
4658
|
+
data: {
|
|
4659
|
+
...streamPayload,
|
|
4660
|
+
...replaceable ? { replaceable: true } : {}
|
|
4661
|
+
}
|
|
4662
|
+
});
|
|
4663
|
+
if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
async handleReasoningDelta(method, params) {
|
|
4667
|
+
const itemId = readString(params, "itemId") ?? "reasoning";
|
|
4668
|
+
const delta = readString(params, "delta") ?? "";
|
|
4669
|
+
if (!delta) return;
|
|
4670
|
+
this.reasoningStarted = true;
|
|
4671
|
+
if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
|
|
4672
|
+
const groupIndex = method === "item/reasoning/textDelta" ? readNonNegativeInteger(params, "contentIndex") ?? 0 : readNonNegativeInteger(params, "summaryIndex") ?? 0;
|
|
4673
|
+
const groupKey = `${method}\0${itemId}\0${groupIndex}`;
|
|
4674
|
+
const current = this.reasoningTextByGroup.get(groupKey);
|
|
4675
|
+
this.reasoningTextByGroup.set(groupKey, {
|
|
4676
|
+
itemId,
|
|
4677
|
+
method,
|
|
4678
|
+
index: groupIndex,
|
|
4679
|
+
text: `${current?.text ?? ""}${delta}`
|
|
4680
|
+
});
|
|
4681
|
+
await this.params.onReasoningStream?.({
|
|
4682
|
+
text: collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n"),
|
|
4683
|
+
isReasoningSnapshot: true
|
|
4684
|
+
});
|
|
4685
|
+
}
|
|
4686
|
+
handlePlanDelta(params) {
|
|
4687
|
+
const itemId = readString(params, "itemId") ?? "plan";
|
|
4688
|
+
const delta = readString(params, "delta") ?? "";
|
|
4689
|
+
if (!delta) return;
|
|
4690
|
+
const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
|
|
4691
|
+
this.planTextByItem.set(itemId, text);
|
|
4692
|
+
this.emitPlanUpdate({
|
|
4693
|
+
explanation: void 0,
|
|
4694
|
+
steps: splitPlanText(text)
|
|
4695
|
+
});
|
|
4696
|
+
}
|
|
4697
|
+
handleTurnPlanUpdated(params) {
|
|
4698
|
+
const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
|
|
4699
|
+
if (!isJsonObject(entry)) return [];
|
|
4700
|
+
const step = readString(entry, "step");
|
|
4701
|
+
const status = readString(entry, "status");
|
|
4702
|
+
if (!step) return [];
|
|
4703
|
+
return status ? [`${step} (${status})`] : [step];
|
|
4704
|
+
}) : void 0;
|
|
4705
|
+
this.emitPlanUpdate({
|
|
4706
|
+
explanation: readNullableString(params, "explanation"),
|
|
4707
|
+
steps: plan
|
|
4708
|
+
});
|
|
4709
|
+
}
|
|
4710
|
+
async handleItemStarted(params) {
|
|
4711
|
+
const item = readItem(params.item);
|
|
4712
|
+
const itemId = item?.id ?? readString(params, "itemId");
|
|
4713
|
+
if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4714
|
+
this.rememberAssistantPhase(item);
|
|
4715
|
+
if (itemId) {
|
|
4716
|
+
this.activeItemIds.add(itemId);
|
|
4717
|
+
if (itemId !== this.latestTerminalAssistantCandidateItemId) {
|
|
4718
|
+
this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
|
|
4719
|
+
if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
this.recordNativeToolOutcome(item);
|
|
4723
|
+
if (item?.type === "contextCompaction" && itemId) {
|
|
4724
|
+
this.activeCompactionItemIds.add(itemId);
|
|
4725
|
+
await runAgentHarnessBeforeCompactionHook({
|
|
4726
|
+
sessionFile: this.params.sessionFile,
|
|
4727
|
+
messages: await this.readMirroredSessionMessages(),
|
|
4728
|
+
ctx: {
|
|
4729
|
+
runId: this.params.runId,
|
|
4730
|
+
agentId: this.params.agentId,
|
|
4731
|
+
sessionKey: this.params.sessionKey,
|
|
4732
|
+
sessionId: this.params.sessionId,
|
|
4733
|
+
workspaceDir: this.params.workspaceDir,
|
|
4734
|
+
messageProvider: this.params.messageProvider ?? void 0,
|
|
4735
|
+
trigger: this.params.trigger,
|
|
4736
|
+
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
4737
|
+
}
|
|
4738
|
+
});
|
|
4739
|
+
this.emitAgentEvent({
|
|
4740
|
+
stream: "compaction",
|
|
4741
|
+
data: {
|
|
4742
|
+
phase: "start",
|
|
4743
|
+
backend: "codex-app-server",
|
|
4744
|
+
threadId: this.threadId,
|
|
4745
|
+
turnId: this.turnId,
|
|
4746
|
+
itemId
|
|
4747
|
+
}
|
|
4748
|
+
});
|
|
4749
|
+
}
|
|
4750
|
+
this.recordToolMeta(item);
|
|
4751
|
+
this.emitStandardItemEvent({
|
|
4752
|
+
phase: "start",
|
|
4753
|
+
item
|
|
4754
|
+
});
|
|
4755
|
+
await this.emitNormalizedToolItemEvent({
|
|
4756
|
+
phase: "start",
|
|
4757
|
+
item
|
|
4758
|
+
});
|
|
4759
|
+
this.recordNativeToolTranscriptCall(item);
|
|
4760
|
+
this.emitToolResultSummary(item);
|
|
4761
|
+
this.emitAgentEvent({
|
|
4762
|
+
stream: "codex_app_server.item",
|
|
4763
|
+
data: {
|
|
4764
|
+
phase: "started",
|
|
4765
|
+
itemId,
|
|
4766
|
+
type: item?.type
|
|
4767
|
+
}
|
|
4768
|
+
});
|
|
4769
|
+
}
|
|
4770
|
+
async handleItemCompleted(params) {
|
|
4771
|
+
const item = readItem(params.item);
|
|
4772
|
+
this.recordNativeToolOutcome(item);
|
|
4773
|
+
this.clearTerminalPresentationForNativeItem(item);
|
|
4774
|
+
const itemId = item?.id ?? readString(params, "itemId");
|
|
4775
|
+
if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4776
|
+
if (itemId) {
|
|
4777
|
+
this.activeItemIds.delete(itemId);
|
|
4778
|
+
this.completedItemIds.add(itemId);
|
|
4779
|
+
this.latestCompletedItemId = itemId;
|
|
4780
|
+
}
|
|
4781
|
+
this.rememberAssistantPhase(item);
|
|
4782
|
+
if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
|
|
4783
|
+
this.latestCompletedTerminalAssistantItemId = item.id;
|
|
4784
|
+
this.markLatestTerminalAssistantCandidate(item.id);
|
|
4785
|
+
this.pendingRawTerminalAssistantEchoItemId = item.id;
|
|
4786
|
+
} else if (itemId) {
|
|
4787
|
+
this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
|
|
4788
|
+
if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4789
|
+
}
|
|
4790
|
+
if (item?.type === "agentMessage" && typeof item.text === "string") {
|
|
4791
|
+
this.rememberAssistantItem(item.id);
|
|
4792
|
+
this.assistantTextByItem.set(item.id, item.text);
|
|
4793
|
+
if (item.text && this.isCommentaryAssistantItem(item.id)) {
|
|
4794
|
+
this.emitCommentaryProgress({
|
|
4795
|
+
itemId: item.id,
|
|
4796
|
+
text: item.text
|
|
4797
|
+
});
|
|
4798
|
+
this.pendingRawCommentaryEchoes += 1;
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
this.recordNativeGeneratedMedia(item);
|
|
4802
|
+
if (item?.type === "plan" && typeof item.text === "string" && item.text) {
|
|
4803
|
+
this.planTextByItem.set(item.id, item.text);
|
|
4804
|
+
this.emitPlanUpdate({
|
|
4805
|
+
explanation: void 0,
|
|
4806
|
+
steps: splitPlanText(item.text)
|
|
4807
|
+
});
|
|
4808
|
+
}
|
|
4809
|
+
if (item?.type === "contextCompaction" && itemId) {
|
|
4810
|
+
this.activeCompactionItemIds.delete(itemId);
|
|
4811
|
+
this.completedCompactionCount += 1;
|
|
4812
|
+
await runAgentHarnessAfterCompactionHook({
|
|
4813
|
+
sessionFile: this.params.sessionFile,
|
|
4814
|
+
messages: await this.readMirroredSessionMessages(),
|
|
4815
|
+
compactedCount: -1,
|
|
4816
|
+
ctx: {
|
|
4817
|
+
runId: this.params.runId,
|
|
4818
|
+
agentId: this.params.agentId,
|
|
4819
|
+
sessionKey: this.params.sessionKey,
|
|
4820
|
+
sessionId: this.params.sessionId,
|
|
4821
|
+
workspaceDir: this.params.workspaceDir,
|
|
4822
|
+
messageProvider: this.params.messageProvider ?? void 0,
|
|
4823
|
+
trigger: this.params.trigger,
|
|
4824
|
+
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
4825
|
+
}
|
|
4826
|
+
});
|
|
4827
|
+
this.emitAgentEvent({
|
|
4828
|
+
stream: "compaction",
|
|
4829
|
+
data: {
|
|
4830
|
+
phase: "end",
|
|
4831
|
+
backend: "codex-app-server",
|
|
4832
|
+
completed: true,
|
|
4833
|
+
threadId: this.threadId,
|
|
4834
|
+
turnId: this.turnId,
|
|
4835
|
+
itemId
|
|
4836
|
+
}
|
|
4837
|
+
});
|
|
4838
|
+
}
|
|
4839
|
+
this.recordToolMeta(item);
|
|
4840
|
+
this.emitStandardItemEvent({
|
|
4841
|
+
phase: "end",
|
|
4842
|
+
item
|
|
4843
|
+
});
|
|
4844
|
+
await this.emitNormalizedToolItemEvent({
|
|
4845
|
+
phase: "result",
|
|
4846
|
+
item
|
|
4847
|
+
});
|
|
4848
|
+
this.recordNativeToolTranscriptCall(item);
|
|
4849
|
+
this.recordNativeToolTranscriptResult(item);
|
|
4850
|
+
this.emitToolResultSummary(item);
|
|
4851
|
+
this.emitToolResultOutput(item);
|
|
4852
|
+
this.emitAgentEvent({
|
|
4853
|
+
stream: "codex_app_server.item",
|
|
4854
|
+
data: {
|
|
4855
|
+
phase: "completed",
|
|
4856
|
+
itemId,
|
|
4857
|
+
type: item?.type
|
|
4858
|
+
}
|
|
4859
|
+
});
|
|
4860
|
+
}
|
|
4861
|
+
handleTokenUsage(params) {
|
|
4862
|
+
const tokenUsage = isJsonObject(params.tokenUsage) ? params.tokenUsage : void 0;
|
|
4863
|
+
const last = tokenUsage && isJsonObject(tokenUsage.last) ? tokenUsage.last : void 0;
|
|
4864
|
+
if (!last) return;
|
|
4865
|
+
const usage = normalizeCodexTokenUsage(last);
|
|
4866
|
+
if (usage) this.tokenUsage = usage;
|
|
4867
|
+
}
|
|
4868
|
+
handleGuardianReviewNotification(method, params) {
|
|
4869
|
+
this.guardianReviewCount += 1;
|
|
4870
|
+
const review = isJsonObject(params.review) ? params.review : void 0;
|
|
4871
|
+
const action = isJsonObject(params.action) ? params.action : void 0;
|
|
4872
|
+
this.emitAgentEvent({
|
|
4873
|
+
stream: "codex_app_server.guardian",
|
|
4874
|
+
data: {
|
|
4875
|
+
method,
|
|
4876
|
+
phase: method.endsWith("/started") ? "started" : "completed",
|
|
4877
|
+
reviewId: readString(params, "reviewId"),
|
|
4878
|
+
targetItemId: readNullableString(params, "targetItemId"),
|
|
4879
|
+
decisionSource: readString(params, "decisionSource"),
|
|
4880
|
+
status: review ? readString(review, "status") : void 0,
|
|
4881
|
+
riskLevel: review ? readString(review, "riskLevel") : void 0,
|
|
4882
|
+
userAuthorization: review ? readString(review, "userAuthorization") : void 0,
|
|
4883
|
+
rationale: review ? readNullableString(review, "rationale") : void 0,
|
|
4884
|
+
actionType: action ? readString(action, "type") : void 0
|
|
4885
|
+
}
|
|
4886
|
+
});
|
|
4887
|
+
}
|
|
4888
|
+
handleGuardianWarning(params) {
|
|
4889
|
+
this.emitAgentEvent({
|
|
4890
|
+
stream: "codex_app_server.guardian",
|
|
4891
|
+
data: {
|
|
4892
|
+
phase: "warning",
|
|
4893
|
+
message: readString(params, "message")
|
|
4894
|
+
}
|
|
4895
|
+
});
|
|
4896
|
+
}
|
|
4897
|
+
handleHookNotification(method, params) {
|
|
4898
|
+
const run = isJsonObject(params.run) ? params.run : void 0;
|
|
4899
|
+
if (!run) return;
|
|
4900
|
+
const durationMs = readNumber(run, "durationMs");
|
|
4901
|
+
const entries = readHookOutputEntries(run.entries);
|
|
4902
|
+
const hookTurnId = readNullableString(params, "turnId");
|
|
4903
|
+
this.emitAgentEvent({
|
|
4904
|
+
stream: "codex_app_server.hook",
|
|
4905
|
+
data: {
|
|
4906
|
+
phase: method === "hook/started" ? "started" : "completed",
|
|
4907
|
+
threadId: this.threadId,
|
|
4908
|
+
turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
|
|
4909
|
+
hookRunId: readString(run, "id"),
|
|
4910
|
+
eventName: readString(run, "eventName"),
|
|
4911
|
+
handlerType: readString(run, "handlerType"),
|
|
4912
|
+
executionMode: readString(run, "executionMode"),
|
|
4913
|
+
scope: readString(run, "scope"),
|
|
4914
|
+
source: readString(run, "source"),
|
|
4915
|
+
sourcePath: readString(run, "sourcePath"),
|
|
4916
|
+
status: readString(run, "status"),
|
|
4917
|
+
statusMessage: readNullableString(run, "statusMessage"),
|
|
4918
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
4919
|
+
...entries.length > 0 ? { entries } : {}
|
|
4920
|
+
}
|
|
4921
|
+
});
|
|
4922
|
+
}
|
|
4923
|
+
async handleTurnCompleted(params) {
|
|
4924
|
+
const turn = readCodexTurn(params.turn);
|
|
4925
|
+
if (!turn || turn.id !== this.turnId) return;
|
|
4926
|
+
this.completedTurn = turn;
|
|
4927
|
+
if (turn.status === "failed") {
|
|
4928
|
+
this.promptError = formatCodexUsageLimitErrorMessage({
|
|
4929
|
+
message: turn.error?.message,
|
|
4930
|
+
codexErrorInfo: turn.error?.codexErrorInfo,
|
|
4931
|
+
rateLimits: this.options.readRecentRateLimits?.()
|
|
4932
|
+
}) ?? turn.error?.message ?? "codex app-server turn failed";
|
|
4933
|
+
this.promptErrorSource = "prompt";
|
|
4934
|
+
}
|
|
4935
|
+
const turnItems = turn.items ?? [];
|
|
4936
|
+
for (let index = turnItems.length - 1; index >= 0; index -= 1) {
|
|
4937
|
+
const item = turnItems[index];
|
|
4938
|
+
if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
|
|
4939
|
+
if (item?.type === "dynamicToolCall") break;
|
|
4940
|
+
if (shouldClearTerminalPresentationForNativeItem(item)) {
|
|
4941
|
+
this.clearTerminalPresentationForNativeItem(item);
|
|
4942
|
+
break;
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
for (const item of turnItems) {
|
|
4946
|
+
this.rememberAssistantPhase(item);
|
|
4947
|
+
if (item.type === "agentMessage" && typeof item.text === "string") {
|
|
4948
|
+
this.rememberAssistantItem(item.id);
|
|
4949
|
+
this.assistantTextByItem.set(item.id, item.text);
|
|
4950
|
+
}
|
|
4951
|
+
this.recordNativeGeneratedMedia(item);
|
|
4952
|
+
if (item.type === "plan" && typeof item.text === "string" && item.text) {
|
|
4953
|
+
this.planTextByItem.set(item.id, item.text);
|
|
4954
|
+
this.emitPlanUpdate({
|
|
4955
|
+
explanation: void 0,
|
|
4956
|
+
steps: splitPlanText(item.text)
|
|
4957
|
+
});
|
|
4958
|
+
}
|
|
4959
|
+
this.recordToolMeta(item);
|
|
4960
|
+
await this.emitSnapshotOnlyNativeToolProgress(item);
|
|
4961
|
+
this.recordNativeToolTranscriptCall(item);
|
|
4962
|
+
this.recordNativeToolTranscriptResult(item);
|
|
4963
|
+
this.emitAfterToolCallObservation(item);
|
|
4964
|
+
this.emitToolResultSummary(item);
|
|
4965
|
+
this.emitToolResultOutput(item);
|
|
4966
|
+
}
|
|
4967
|
+
this.activeCompactionItemIds.clear();
|
|
4968
|
+
await this.maybeEndReasoning();
|
|
4969
|
+
}
|
|
4970
|
+
async emitSnapshotOnlyNativeToolProgress(item) {
|
|
4971
|
+
if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
|
|
4972
|
+
if (!this.activeItemIds.has(item.id)) {
|
|
4973
|
+
this.emitStandardItemEvent({
|
|
4974
|
+
phase: "start",
|
|
4975
|
+
item
|
|
4976
|
+
});
|
|
4977
|
+
await this.emitNormalizedToolItemEvent({
|
|
4978
|
+
phase: "start",
|
|
4979
|
+
item
|
|
4980
|
+
});
|
|
4981
|
+
}
|
|
4982
|
+
this.activeItemIds.delete(item.id);
|
|
4983
|
+
this.emitStandardItemEvent({
|
|
4984
|
+
phase: "end",
|
|
4985
|
+
item
|
|
4986
|
+
});
|
|
4987
|
+
await this.emitNormalizedToolItemEvent({
|
|
4988
|
+
phase: "result",
|
|
4989
|
+
item
|
|
4990
|
+
});
|
|
4991
|
+
this.completedItemIds.add(item.id);
|
|
4992
|
+
}
|
|
4993
|
+
isCurrentTurnSnapshotItem(item) {
|
|
4994
|
+
const itemTurnId = readItemString(item, "turnId");
|
|
4995
|
+
return itemTurnId === void 0 || itemTurnId === this.turnId;
|
|
4996
|
+
}
|
|
4997
|
+
handleOutputDelta(params, toolName) {
|
|
4998
|
+
const itemId = readString(params, "itemId");
|
|
4999
|
+
const delta = readString(params, "delta");
|
|
5000
|
+
if (!itemId || !delta) return;
|
|
5001
|
+
appendToolOutputDeltaText(this.toolResultOutputTextByItem, itemId, delta);
|
|
5002
|
+
if (!this.shouldEmitToolOutput()) return;
|
|
5003
|
+
if (this.transcriptToolProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.toolTranscriptArgumentsById.get(itemId))) return;
|
|
5004
|
+
const state = this.toolResultOutputDeltaState.get(itemId) ?? {
|
|
5005
|
+
chars: 0,
|
|
5006
|
+
messages: 0,
|
|
5007
|
+
truncated: false
|
|
5008
|
+
};
|
|
5009
|
+
if (state.truncated) return;
|
|
5010
|
+
const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
|
|
5011
|
+
const remainingMessages = Math.max(0, MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM - state.messages);
|
|
5012
|
+
if (remainingChars === 0 || remainingMessages === 0) {
|
|
5013
|
+
state.truncated = true;
|
|
5014
|
+
this.toolResultOutputDeltaState.set(itemId, state);
|
|
5015
|
+
this.emitToolResultMessage({
|
|
5016
|
+
itemId,
|
|
5017
|
+
text: formatToolOutput(toolName, void 0, "(output truncated)")
|
|
5018
|
+
});
|
|
5019
|
+
return;
|
|
5020
|
+
}
|
|
5021
|
+
const chunk = delta.length > remainingChars ? delta.slice(0, remainingChars) : delta;
|
|
5022
|
+
state.chars += chunk.length;
|
|
5023
|
+
state.messages += 1;
|
|
5024
|
+
const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM;
|
|
5025
|
+
if (reachedLimit) state.truncated = true;
|
|
5026
|
+
this.toolResultOutputDeltaState.set(itemId, state);
|
|
5027
|
+
this.toolResultOutputStreamedItemIds.add(itemId);
|
|
5028
|
+
this.emitToolResultMessage({
|
|
5029
|
+
itemId,
|
|
5030
|
+
text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
|
|
5031
|
+
});
|
|
5032
|
+
}
|
|
5033
|
+
async handleRawResponseItemCompleted(params) {
|
|
5034
|
+
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
5035
|
+
if (!item) return;
|
|
5036
|
+
const role = readString(item, "role");
|
|
5037
|
+
const phase = readString(item, "phase");
|
|
5038
|
+
const rawItemId = readString(item, "id");
|
|
5039
|
+
const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
|
|
5040
|
+
const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
|
|
5041
|
+
const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
|
|
5042
|
+
if (pendingTerminalAssistantEchoItemId !== void 0 && !isPendingTerminalAssistantEcho) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
5043
|
+
if (!isPendingTerminalAssistantEcho) {
|
|
5044
|
+
this.latestCompletedItemId = void 0;
|
|
5045
|
+
this.markTerminalAssistantCandidateSupersededBy(rawItemId);
|
|
5046
|
+
}
|
|
5047
|
+
await this.recordRawGeneratedImageMedia(item);
|
|
5048
|
+
if (role !== "assistant") return;
|
|
5049
|
+
if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
|
|
5050
|
+
this.pendingRawCommentaryEchoes -= 1;
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
const text = extractRawAssistantText(item);
|
|
5054
|
+
if (isPendingTerminalAssistantEcho) {
|
|
5055
|
+
const typedItemId = pendingTerminalAssistantEchoItemId;
|
|
5056
|
+
this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
5057
|
+
if (this.assistantTextByItem.get(typedItemId)?.trim() || !text) return;
|
|
5058
|
+
this.rememberAssistantItem(typedItemId);
|
|
5059
|
+
this.assistantTextByItem.set(typedItemId, text);
|
|
5060
|
+
return;
|
|
5061
|
+
}
|
|
5062
|
+
if (!text) return;
|
|
5063
|
+
const itemId = rawItemId ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
|
|
5064
|
+
const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && this.activeItemIds.size === 0;
|
|
5065
|
+
if (phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
|
|
5066
|
+
if (phase) this.assistantPhaseByItem.set(itemId, phase);
|
|
5067
|
+
this.rememberAssistantItem(itemId);
|
|
5068
|
+
this.assistantTextByItem.set(itemId, text);
|
|
5069
|
+
if (phase === "commentary") this.emitCommentaryProgress({
|
|
5070
|
+
itemId,
|
|
5071
|
+
text
|
|
5072
|
+
});
|
|
5073
|
+
else this.markLatestTerminalAssistantCandidate(itemId, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
|
|
5074
|
+
}
|
|
5075
|
+
markLatestTerminalAssistantCandidate(itemId, options) {
|
|
5076
|
+
this.latestTerminalAssistantCandidateItemId = itemId;
|
|
5077
|
+
this.latestTerminalAssistantCandidateSuperseded = false;
|
|
5078
|
+
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = options?.canReleaseAfterToolHandoff === true;
|
|
5079
|
+
this.terminalAssistantCandidateEarlierActiveItemIds = new Set(this.activeItemIds);
|
|
5080
|
+
}
|
|
5081
|
+
markTerminalAssistantCandidateSupersededBy(itemId, options) {
|
|
5082
|
+
if (!this.latestTerminalAssistantCandidateItemId) return;
|
|
5083
|
+
if (itemId && this.terminalAssistantCandidateEarlierActiveItemIds.has(itemId)) {
|
|
5084
|
+
if (!options?.preserveEarlierActiveItem) this.terminalAssistantCandidateEarlierActiveItemIds.delete(itemId);
|
|
5085
|
+
return;
|
|
5086
|
+
}
|
|
5087
|
+
this.latestTerminalAssistantCandidateSuperseded = true;
|
|
5088
|
+
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
|
|
5089
|
+
this.terminalAssistantCandidateEarlierActiveItemIds.clear();
|
|
5090
|
+
}
|
|
5091
|
+
recordNativeGeneratedMedia(item) {
|
|
5092
|
+
if (item?.type !== "imageGeneration") return;
|
|
5093
|
+
const savedPath = readItemString(item, "savedPath")?.trim();
|
|
5094
|
+
if (savedPath) this.recordNativeGeneratedMediaUrl({
|
|
5095
|
+
itemId: item.id,
|
|
5096
|
+
mediaUrl: savedPath
|
|
5097
|
+
});
|
|
5098
|
+
}
|
|
5099
|
+
async recordRawGeneratedImageMedia(item) {
|
|
5100
|
+
if (readString(item, "type") !== "image_generation_call") return;
|
|
5101
|
+
const result = readString(item, "result");
|
|
5102
|
+
if (!result) return;
|
|
5103
|
+
const itemId = readString(item, "id") ?? `raw-image-${this.nativeGeneratedMediaItemIds.size}`;
|
|
5104
|
+
this.nativeGeneratedMediaItemIds.add(itemId);
|
|
5105
|
+
const maxBytes = resolveGeneratedImageMaxBytes(this.params.config);
|
|
5106
|
+
const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
|
|
5107
|
+
if (estimatedDecodedBytes !== void 0 && estimatedDecodedBytes > maxBytes) {
|
|
5108
|
+
embeddedAgentLog.warn("codex app-server raw image generation result exceeds media limit", {
|
|
5109
|
+
itemId,
|
|
5110
|
+
estimatedDecodedBytes,
|
|
5111
|
+
maxBytes
|
|
5112
|
+
});
|
|
5113
|
+
return;
|
|
5114
|
+
}
|
|
5115
|
+
const asset = generatedImageAssetFromBase64({
|
|
5116
|
+
base64: result,
|
|
5117
|
+
index: this.nativeGeneratedMediaItemIds.size,
|
|
5118
|
+
revisedPrompt: readString(item, "revised_prompt") ?? readString(item, "revisedPrompt"),
|
|
5119
|
+
fileNamePrefix: "codex-image-generation",
|
|
5120
|
+
sniffMimeType: true
|
|
5121
|
+
});
|
|
5122
|
+
if (!asset) return;
|
|
5123
|
+
try {
|
|
5124
|
+
const saved = await saveMediaBuffer(asset.buffer, asset.mimeType, GENERATED_IMAGE_MEDIA_SUBDIR, maxBytes, asset.fileName);
|
|
5125
|
+
this.recordNativeGeneratedMediaUrl({
|
|
5126
|
+
itemId,
|
|
5127
|
+
mediaUrl: saved.path,
|
|
5128
|
+
replaceExisting: true
|
|
5129
|
+
});
|
|
5130
|
+
} catch (error) {
|
|
5131
|
+
embeddedAgentLog.warn("codex app-server raw image generation result save failed", {
|
|
5132
|
+
itemId,
|
|
5133
|
+
error
|
|
5134
|
+
});
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
recordNativeGeneratedMediaUrl(params) {
|
|
5138
|
+
if (this.nativeGeneratedMediaUrlsByItemId.has(params.itemId) && params.replaceExisting !== true) {
|
|
5139
|
+
this.nativeGeneratedMediaItemIds.add(params.itemId);
|
|
5140
|
+
return;
|
|
5141
|
+
}
|
|
5142
|
+
this.nativeGeneratedMediaUrlsByItemId.set(params.itemId, params.mediaUrl);
|
|
5143
|
+
this.nativeGeneratedMediaItemIds.add(params.itemId);
|
|
5144
|
+
}
|
|
5145
|
+
buildToolMediaUrls(toolTelemetry) {
|
|
5146
|
+
const mediaUrls = new Set(toolTelemetry.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
|
|
5147
|
+
if ((toolTelemetry.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.nativeGeneratedMediaUrlsByItemId.values()) mediaUrls.add(mediaUrl);
|
|
5148
|
+
return mediaUrls.size > 0 ? [...mediaUrls] : toolTelemetry.toolMediaUrls;
|
|
5149
|
+
}
|
|
5150
|
+
async maybeEndReasoning() {
|
|
5151
|
+
if (!this.reasoningStarted || this.reasoningEnded) return;
|
|
5152
|
+
this.reasoningEnded = true;
|
|
5153
|
+
await this.params.onReasoningEnd?.();
|
|
5154
|
+
}
|
|
5155
|
+
emitPlanUpdate(params) {
|
|
5156
|
+
if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
|
|
5157
|
+
this.emitAgentEvent({
|
|
5158
|
+
stream: "plan",
|
|
5159
|
+
data: {
|
|
5160
|
+
phase: "update",
|
|
5161
|
+
title: "Plan updated",
|
|
5162
|
+
source: "codex-app-server",
|
|
5163
|
+
...params.explanation ? { explanation: params.explanation } : {},
|
|
5164
|
+
...params.steps && params.steps.length > 0 ? { steps: params.steps } : {}
|
|
5165
|
+
}
|
|
5166
|
+
});
|
|
5167
|
+
}
|
|
5168
|
+
rememberAssistantPhase(item) {
|
|
5169
|
+
if (item?.type !== "agentMessage") return;
|
|
5170
|
+
const phase = readItemString(item, "phase");
|
|
5171
|
+
if (phase) this.assistantPhaseByItem.set(item.id, phase);
|
|
5172
|
+
}
|
|
5173
|
+
isCommentaryAssistantItem(itemId) {
|
|
5174
|
+
return this.assistantPhaseByItem.get(itemId) === "commentary";
|
|
5175
|
+
}
|
|
5176
|
+
shouldStreamAssistantPartial(itemId) {
|
|
5177
|
+
return this.assistantPhaseByItem.get(itemId) === "final_answer";
|
|
5178
|
+
}
|
|
5179
|
+
emitCommentaryProgress(params) {
|
|
5180
|
+
const progressText = params.text.replace(/\s+/g, " ").trim();
|
|
5181
|
+
if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
|
|
5182
|
+
this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
|
|
5183
|
+
this.emitAgentEvent({
|
|
5184
|
+
stream: "item",
|
|
5185
|
+
data: {
|
|
5186
|
+
itemId: params.itemId,
|
|
5187
|
+
kind: "preamble",
|
|
5188
|
+
title: "Preamble",
|
|
5189
|
+
phase: "update",
|
|
5190
|
+
progressText,
|
|
5191
|
+
source: "codex-app-server"
|
|
5192
|
+
}
|
|
5193
|
+
});
|
|
5194
|
+
}
|
|
5195
|
+
emitStandardItemEvent(params) {
|
|
5196
|
+
const { item } = params;
|
|
5197
|
+
if (!item) return;
|
|
5198
|
+
const kind = itemKind(item);
|
|
5199
|
+
if (!kind) return;
|
|
5200
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
5201
|
+
const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
|
|
5202
|
+
this.emitAgentEvent({
|
|
5203
|
+
stream: "item",
|
|
5204
|
+
data: {
|
|
5205
|
+
itemId: item.id,
|
|
5206
|
+
phase: params.phase,
|
|
5207
|
+
kind,
|
|
5208
|
+
title: itemTitle(item),
|
|
5209
|
+
status: params.phase === "start" ? "running" : itemStatus(item),
|
|
5210
|
+
...itemName(item) ? { name: itemName(item) } : {},
|
|
5211
|
+
...meta ? { meta } : {},
|
|
5212
|
+
...suppressChannelProgress ? { suppressChannelProgress: true } : {}
|
|
5213
|
+
}
|
|
5214
|
+
});
|
|
5215
|
+
}
|
|
5216
|
+
async emitNormalizedToolItemEvent(params) {
|
|
5217
|
+
const { item } = params;
|
|
5218
|
+
if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
|
|
5219
|
+
const name = itemName(item);
|
|
5220
|
+
if (!name) return;
|
|
5221
|
+
const status = params.phase === "result" ? itemStatus(item) : "running";
|
|
5222
|
+
const args = itemToolArgs(item);
|
|
5223
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
5224
|
+
this.recordToolTrajectoryEvent({
|
|
5225
|
+
phase: params.phase,
|
|
5226
|
+
item,
|
|
5227
|
+
name,
|
|
5228
|
+
args,
|
|
5229
|
+
status
|
|
5230
|
+
});
|
|
5231
|
+
if (params.phase === "result") this.recordNativeToolError({
|
|
5232
|
+
item,
|
|
5233
|
+
name,
|
|
5234
|
+
meta,
|
|
5235
|
+
status
|
|
5236
|
+
});
|
|
5237
|
+
if (!shouldEmitTranscriptToolProgress(name, args)) {
|
|
5238
|
+
if (params.phase === "result") {
|
|
5239
|
+
this.emitAfterToolCallObservation(item);
|
|
5240
|
+
await this.options.onNativeToolResultRecorded?.();
|
|
5241
|
+
}
|
|
5242
|
+
return;
|
|
5243
|
+
}
|
|
5244
|
+
this.emitAgentEvent({
|
|
5245
|
+
stream: "tool",
|
|
5246
|
+
data: {
|
|
5247
|
+
phase: params.phase,
|
|
5248
|
+
name,
|
|
5249
|
+
itemId: item.id,
|
|
5250
|
+
toolCallId: item.id,
|
|
5251
|
+
...meta ? { meta } : {},
|
|
5252
|
+
...params.phase === "start" && args ? { args } : {},
|
|
5253
|
+
...params.phase === "result" ? {
|
|
5254
|
+
status,
|
|
5255
|
+
isError: isNonSuccessItemStatus(status),
|
|
5256
|
+
...itemToolResult(item)
|
|
5257
|
+
} : {}
|
|
5258
|
+
}
|
|
5259
|
+
});
|
|
5260
|
+
if (params.phase === "result") {
|
|
5261
|
+
this.emitAfterToolCallObservation(item);
|
|
5262
|
+
await this.options.onNativeToolResultRecorded?.();
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
clearTerminalPresentationForNativeItem(item) {
|
|
5266
|
+
if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
5267
|
+
const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
|
|
5268
|
+
this.terminalPresentationClearedItemIds.add(item.id);
|
|
5269
|
+
this.params.onToolOutcome?.({
|
|
5270
|
+
toolName: itemName(item) ?? item.type,
|
|
5271
|
+
argsHash: "",
|
|
5272
|
+
resultHash: "",
|
|
5273
|
+
...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
|
|
5274
|
+
terminalPresentation: void 0,
|
|
5275
|
+
presentationOnly: true
|
|
5276
|
+
});
|
|
5277
|
+
}
|
|
5278
|
+
recordNativeToolError(params) {
|
|
5279
|
+
if (!isNonSuccessItemStatus(params.status)) {
|
|
5280
|
+
if (!this.lastNativeToolError) return;
|
|
5281
|
+
if (!this.lastNativeToolError.mutatingAction) {
|
|
5282
|
+
this.lastNativeToolError = void 0;
|
|
5283
|
+
return;
|
|
5284
|
+
}
|
|
5285
|
+
const actionFingerprint = nativeToolActionFingerprint(params.item);
|
|
5286
|
+
if (this.lastNativeToolError.actionFingerprint && actionFingerprint && this.lastNativeToolError.actionFingerprint === actionFingerprint) this.lastNativeToolError = void 0;
|
|
5287
|
+
return;
|
|
5288
|
+
}
|
|
5289
|
+
const error = itemToolError(params.item, params.status, this.toolResultOutputTextByItem);
|
|
5290
|
+
const actionFingerprint = nativeToolActionFingerprint(params.item);
|
|
5291
|
+
this.lastNativeToolError = {
|
|
5292
|
+
toolName: params.name,
|
|
5293
|
+
...params.meta ? { meta: params.meta } : {},
|
|
5294
|
+
...error ? { error } : {},
|
|
5295
|
+
...isMutatingNativeToolItem(params.item) ? { mutatingAction: true } : {},
|
|
5296
|
+
...actionFingerprint ? { actionFingerprint } : {}
|
|
5297
|
+
};
|
|
5298
|
+
}
|
|
5299
|
+
recordToolTrajectoryEvent(params) {
|
|
5300
|
+
if (params.phase === "start") {
|
|
5301
|
+
this.toolTrajectoryCallIds.add(params.item.id);
|
|
5302
|
+
this.toolTrajectoryNamesById.set(params.item.id, params.name);
|
|
5303
|
+
this.toolTrajectoryItemsById.set(params.item.id, params.item);
|
|
5304
|
+
this.options.trajectoryRecorder?.recordEvent("tool.call", {
|
|
5305
|
+
threadId: this.threadId,
|
|
5306
|
+
turnId: this.turnId,
|
|
5307
|
+
itemId: params.item.id,
|
|
5308
|
+
toolCallId: params.item.id,
|
|
5309
|
+
name: params.name,
|
|
5310
|
+
arguments: params.args
|
|
5311
|
+
});
|
|
5312
|
+
return;
|
|
5313
|
+
}
|
|
5314
|
+
this.toolTrajectoryResultIds.add(params.item.id);
|
|
5315
|
+
const toolResult = itemToolResult(params.item).result;
|
|
5316
|
+
const output = itemOutputText(params.item, this.toolResultOutputTextByItem);
|
|
5317
|
+
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
5318
|
+
threadId: this.threadId,
|
|
5319
|
+
turnId: this.turnId,
|
|
5320
|
+
itemId: params.item.id,
|
|
5321
|
+
toolCallId: params.item.id,
|
|
5322
|
+
name: params.name,
|
|
5323
|
+
status: params.status,
|
|
5324
|
+
isError: isNonSuccessItemStatus(params.status),
|
|
5325
|
+
...toolResult ? { result: toolResult } : {},
|
|
5326
|
+
...output ? { output } : {}
|
|
5327
|
+
});
|
|
5328
|
+
}
|
|
5329
|
+
emitAfterToolCallObservation(item) {
|
|
5330
|
+
if (!this.shouldEmitAfterToolCallObservation(item)) return;
|
|
5331
|
+
const name = itemName(item);
|
|
5332
|
+
if (!name) return;
|
|
5333
|
+
const status = itemStatus(item);
|
|
5334
|
+
if (status === "running") return;
|
|
5335
|
+
this.afterToolCallObservedItemIds.add(item.id);
|
|
5336
|
+
const result = itemToolResult(item).result;
|
|
5337
|
+
const error = itemToolError(item, status, this.toolResultOutputTextByItem);
|
|
5338
|
+
const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
|
|
5339
|
+
const hookParams = {
|
|
5340
|
+
toolName: name,
|
|
5341
|
+
toolCallId: item.id,
|
|
5342
|
+
runId: this.params.runId,
|
|
5343
|
+
agentId: this.params.agentId,
|
|
5344
|
+
sessionId: this.params.sessionId,
|
|
5345
|
+
sessionKey: this.params.sessionKey,
|
|
5346
|
+
startArgs: itemToolArgs(item) ?? {},
|
|
5347
|
+
...result !== void 0 ? { result } : {},
|
|
5348
|
+
...error ? { error } : {},
|
|
5349
|
+
...startedAt !== void 0 ? { startedAt } : {}
|
|
5350
|
+
};
|
|
5351
|
+
setImmediate(() => {
|
|
5352
|
+
runAgentHarnessAfterToolCallHook(hookParams);
|
|
5353
|
+
});
|
|
5354
|
+
}
|
|
5355
|
+
shouldEmitAfterToolCallObservation(item) {
|
|
5356
|
+
if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
|
|
5357
|
+
if (this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item)) return false;
|
|
5358
|
+
return true;
|
|
5359
|
+
}
|
|
5360
|
+
emitToolResultSummary(item) {
|
|
5361
|
+
if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
|
|
5362
|
+
const itemId = item.id;
|
|
5363
|
+
if (this.toolResultSummaryItemIds.has(itemId)) return;
|
|
5364
|
+
const toolName = itemName(item);
|
|
5365
|
+
if (!toolName) return;
|
|
5366
|
+
if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
5367
|
+
this.toolResultSummaryItemIds.add(itemId);
|
|
5368
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
5369
|
+
this.emitToolResultMessage({
|
|
5370
|
+
itemId,
|
|
5371
|
+
text: formatToolSummary(toolName, meta)
|
|
5372
|
+
});
|
|
5373
|
+
}
|
|
5374
|
+
emitToolResultOutput(item) {
|
|
5375
|
+
if (!item || !this.params.onToolResult || !this.shouldEmitToolOutput()) return;
|
|
5376
|
+
const itemId = item.id;
|
|
5377
|
+
if (this.toolResultOutputItemIds.has(itemId)) return;
|
|
5378
|
+
if (this.toolResultOutputStreamedItemIds.has(itemId)) return;
|
|
5379
|
+
const toolName = itemName(item);
|
|
5380
|
+
const output = itemOutputText(item, this.toolResultOutputTextByItem);
|
|
5381
|
+
if (!toolName || !output) return;
|
|
5382
|
+
if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
5383
|
+
this.emitToolResultMessage({
|
|
5384
|
+
itemId,
|
|
5385
|
+
text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
|
|
5386
|
+
finalOutput: true,
|
|
5387
|
+
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
5388
|
+
});
|
|
5389
|
+
}
|
|
5390
|
+
emitToolResultMessage(params) {
|
|
5391
|
+
const text = params.text.trim();
|
|
5392
|
+
if (!text) return;
|
|
5393
|
+
this.toolProgressTexts.add(text);
|
|
5394
|
+
if (params.finalOutput) this.toolResultOutputItemIds.add(params.itemId);
|
|
5395
|
+
try {
|
|
5396
|
+
Promise.resolve(this.params.onToolResult?.({
|
|
5397
|
+
text,
|
|
5398
|
+
...params.isError === true ? { isError: true } : {}
|
|
5399
|
+
})).catch(() => {});
|
|
5400
|
+
} catch {}
|
|
5401
|
+
}
|
|
5402
|
+
shouldEmitToolResult() {
|
|
5403
|
+
return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
|
|
5404
|
+
}
|
|
5405
|
+
shouldEmitToolOutput() {
|
|
5406
|
+
return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
|
|
5407
|
+
}
|
|
5408
|
+
toolProgressDetailMode() {
|
|
5409
|
+
return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
|
|
5410
|
+
}
|
|
5411
|
+
recordToolMeta(item) {
|
|
5412
|
+
if (!item) return;
|
|
5413
|
+
if (isSideEffectingNativeToolItem(item)) this.sideEffectingToolItemIds.add(item.id);
|
|
5414
|
+
else this.sideEffectingToolItemIds.delete(item.id);
|
|
5415
|
+
const toolName = itemName(item);
|
|
5416
|
+
if (!toolName) return;
|
|
5417
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
5418
|
+
const existing = this.toolMetas.get(item.id);
|
|
5419
|
+
this.toolMetas.set(item.id, {
|
|
5420
|
+
toolName,
|
|
5421
|
+
...meta ? { meta } : {},
|
|
5422
|
+
...existing?.asyncStarted ? { asyncStarted: true } : {}
|
|
5423
|
+
});
|
|
5424
|
+
}
|
|
5425
|
+
recordNativeToolTranscriptCall(item) {
|
|
5426
|
+
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
5427
|
+
const name = itemName(item);
|
|
5428
|
+
if (!name) return;
|
|
5429
|
+
this.recordToolTranscriptCall({
|
|
5430
|
+
id: item.id,
|
|
5431
|
+
name,
|
|
5432
|
+
arguments: itemToolArgs(item)
|
|
5433
|
+
});
|
|
5434
|
+
}
|
|
5435
|
+
recordNativeToolTranscriptResult(item) {
|
|
5436
|
+
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
5437
|
+
const name = itemName(item);
|
|
5438
|
+
if (!name) return;
|
|
5439
|
+
this.recordToolTranscriptResult({
|
|
5440
|
+
id: item.id,
|
|
5441
|
+
name,
|
|
5442
|
+
text: itemTranscriptResultText(item, this.toolResultOutputTextByItem),
|
|
5443
|
+
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
5444
|
+
});
|
|
5445
|
+
}
|
|
5446
|
+
recordToolTranscriptCall(params) {
|
|
5447
|
+
if (!params.id || !params.name || this.toolTranscriptCallIds.has(params.id)) return;
|
|
5448
|
+
this.toolTranscriptCallIds.add(params.id);
|
|
5449
|
+
this.toolTranscriptNamesById.set(params.id, params.name);
|
|
5450
|
+
this.toolTranscriptArgumentsById.set(params.id, params.arguments);
|
|
5451
|
+
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptToolProgressSuppressedIds.add(params.id);
|
|
5452
|
+
else this.transcriptToolProgressSuppressedIds.delete(params.id);
|
|
5453
|
+
this.emitTranscriptToolCallProgress(params);
|
|
5454
|
+
this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`));
|
|
5455
|
+
}
|
|
5456
|
+
recordToolTranscriptResult(params) {
|
|
5457
|
+
if (!params.id || !params.name || this.toolTranscriptResultIds.has(params.id)) return;
|
|
5458
|
+
this.toolTranscriptResultIds.add(params.id);
|
|
5459
|
+
this.emitTranscriptToolResultProgress(params);
|
|
5460
|
+
this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`));
|
|
5461
|
+
}
|
|
5462
|
+
synthesizeMissingToolResults(params) {
|
|
5463
|
+
if (!params.synthesize) return;
|
|
5464
|
+
const missingTranscriptIds = [...this.toolTranscriptCallIds].filter((id) => !this.toolTranscriptResultIds.has(id));
|
|
5465
|
+
const missingTrajectoryIds = [...this.toolTrajectoryCallIds].filter((id) => !this.toolTrajectoryResultIds.has(id));
|
|
5466
|
+
if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
|
|
5467
|
+
for (const id of missingTranscriptIds) {
|
|
5468
|
+
const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
|
|
5469
|
+
if (!name) continue;
|
|
5470
|
+
this.recordToolTranscriptResult({
|
|
5471
|
+
id,
|
|
5472
|
+
name,
|
|
5473
|
+
text: formatMissingToolResultError({
|
|
5474
|
+
id,
|
|
5475
|
+
name
|
|
5476
|
+
}),
|
|
5477
|
+
isError: true
|
|
5478
|
+
});
|
|
5479
|
+
}
|
|
5480
|
+
for (const id of missingTrajectoryIds) {
|
|
5481
|
+
const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
|
|
5482
|
+
if (!name) continue;
|
|
5483
|
+
this.toolTrajectoryResultIds.add(id);
|
|
5484
|
+
const text = formatMissingToolResultError({
|
|
5485
|
+
id,
|
|
5486
|
+
name
|
|
5487
|
+
});
|
|
5488
|
+
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
5489
|
+
threadId: this.threadId,
|
|
5490
|
+
turnId: this.turnId,
|
|
5491
|
+
itemId: id,
|
|
5492
|
+
toolCallId: id,
|
|
5493
|
+
name,
|
|
5494
|
+
status: "failed",
|
|
5495
|
+
isError: true,
|
|
5496
|
+
result: {
|
|
5497
|
+
status: "failed",
|
|
5498
|
+
reason: "missing_tool_result"
|
|
5499
|
+
},
|
|
5500
|
+
output: text
|
|
5501
|
+
});
|
|
5502
|
+
}
|
|
5503
|
+
if (!params.recordPromptError) {
|
|
5504
|
+
const firstMissingId = missingTranscriptIds.find((id) => {
|
|
5505
|
+
const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
|
|
5506
|
+
return Boolean(name);
|
|
5507
|
+
}) ?? missingTrajectoryIds.find((id) => {
|
|
5508
|
+
const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
|
|
5509
|
+
return Boolean(name);
|
|
5510
|
+
});
|
|
5511
|
+
if (firstMissingId) {
|
|
5512
|
+
const name = this.toolTranscriptNamesById.get(firstMissingId) ?? this.toolTrajectoryNamesById.get(firstMissingId);
|
|
5513
|
+
if (name) {
|
|
5514
|
+
const item = this.toolTrajectoryItemsById.get(firstMissingId);
|
|
5515
|
+
const meta = item ? itemMeta(item, this.toolProgressDetailMode()) : this.toolMetas.get(firstMissingId)?.meta;
|
|
5516
|
+
const actionFingerprint = item ? nativeToolActionFingerprint(item) : void 0;
|
|
5517
|
+
this.lastNativeToolError = {
|
|
5518
|
+
toolName: name,
|
|
5519
|
+
...meta ? { meta } : {},
|
|
5520
|
+
error: formatMissingToolResultError({
|
|
5521
|
+
id: firstMissingId,
|
|
5522
|
+
name
|
|
5523
|
+
}),
|
|
5524
|
+
...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {},
|
|
5525
|
+
...actionFingerprint ? { actionFingerprint } : {}
|
|
5526
|
+
};
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5529
|
+
return;
|
|
5530
|
+
}
|
|
5531
|
+
const missingCount = (/* @__PURE__ */ new Set([...missingTranscriptIds, ...missingTrajectoryIds])).size;
|
|
5532
|
+
this.synthesizedMissingToolResultError = missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
|
|
5533
|
+
this.promptErrorSource = this.promptErrorSource ?? "prompt";
|
|
5534
|
+
}
|
|
5535
|
+
emitTranscriptToolCallProgress(params) {
|
|
5536
|
+
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
|
|
5537
|
+
this.transcriptToolProgressCallIds.add(params.id);
|
|
5538
|
+
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
5539
|
+
const meta = inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() });
|
|
5540
|
+
if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.toolResultSummaryItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
|
|
5541
|
+
this.toolResultSummaryItemIds.add(params.id);
|
|
5542
|
+
this.emitToolResultMessage({
|
|
5543
|
+
itemId: params.id,
|
|
5544
|
+
text: formatToolSummary(params.name, meta)
|
|
5545
|
+
});
|
|
5546
|
+
}
|
|
5547
|
+
emitTranscriptToolResultProgress(params) {
|
|
5548
|
+
if (this.transcriptToolProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.toolTranscriptArgumentsById.get(params.id))) return;
|
|
5549
|
+
if (!this.transcriptToolProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
|
|
5550
|
+
id: params.id,
|
|
5551
|
+
name: params.name,
|
|
5552
|
+
arguments: {}
|
|
5553
|
+
});
|
|
5554
|
+
if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.toolResultOutputItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
|
|
5555
|
+
const text = params.text?.trim();
|
|
5556
|
+
if (!text) return;
|
|
5557
|
+
this.emitToolResultMessage({
|
|
5558
|
+
itemId: params.id,
|
|
5559
|
+
text: formatToolOutput(params.name, void 0, text),
|
|
5560
|
+
finalOutput: true,
|
|
5561
|
+
isError: params.isError
|
|
5562
|
+
});
|
|
5563
|
+
}
|
|
5564
|
+
formatCodexErrorMessage(params) {
|
|
5565
|
+
const error = isJsonObject(params.error) ? params.error : void 0;
|
|
5566
|
+
return formatCodexUsageLimitErrorMessage({
|
|
5567
|
+
message: error ? readString(error, "message") : void 0,
|
|
5568
|
+
codexErrorInfo: error?.codexErrorInfo,
|
|
5569
|
+
rateLimits: this.options.readRecentRateLimits?.()
|
|
5570
|
+
}) ?? readCodexErrorNotificationMessage(params);
|
|
5571
|
+
}
|
|
5572
|
+
emitAgentEvent(event) {
|
|
5573
|
+
try {
|
|
5574
|
+
emitAgentEvent({
|
|
5575
|
+
runId: this.params.runId,
|
|
5576
|
+
stream: event.stream,
|
|
5577
|
+
data: event.data,
|
|
5578
|
+
...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
|
|
5579
|
+
});
|
|
5580
|
+
} catch (error) {
|
|
5581
|
+
embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
|
|
5582
|
+
}
|
|
5583
|
+
try {
|
|
5584
|
+
const maybePromise = this.params.onAgentEvent?.(event);
|
|
5585
|
+
Promise.resolve(maybePromise).catch((error) => {
|
|
5586
|
+
embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
|
|
5587
|
+
});
|
|
5588
|
+
} catch (error) {
|
|
5589
|
+
embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
|
|
5590
|
+
}
|
|
5591
|
+
}
|
|
5592
|
+
collectAssistantTexts() {
|
|
5593
|
+
const finalText = this.resolveFinalAssistantText();
|
|
5594
|
+
return finalText ? [finalText] : [];
|
|
5595
|
+
}
|
|
5596
|
+
hasAssistantItemTextForSynthesis() {
|
|
5597
|
+
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5598
|
+
const itemId = this.assistantItemOrder[i];
|
|
5599
|
+
if (!itemId) continue;
|
|
5600
|
+
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
5601
|
+
const text = this.assistantTextByItem.get(itemId);
|
|
5602
|
+
if (text && text.length > 0) return true;
|
|
5603
|
+
}
|
|
5604
|
+
return false;
|
|
5605
|
+
}
|
|
5606
|
+
resolveFinalAssistantText() {
|
|
5607
|
+
return this.resolveFinalAssistantTextItem()?.text;
|
|
5608
|
+
}
|
|
5609
|
+
resolveFinalAssistantTextItem() {
|
|
5610
|
+
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5611
|
+
const itemId = this.assistantItemOrder[i];
|
|
5612
|
+
if (!itemId) continue;
|
|
5613
|
+
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
5614
|
+
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
5615
|
+
if (text && !this.toolProgressTexts.has(text)) return {
|
|
5616
|
+
itemId,
|
|
5617
|
+
text
|
|
5618
|
+
};
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
rememberAssistantItem(itemId) {
|
|
5622
|
+
if (!itemId || this.assistantItemOrder.includes(itemId)) return;
|
|
5623
|
+
this.assistantItemOrder.push(itemId);
|
|
5624
|
+
}
|
|
5625
|
+
createCurrentAttemptAssistantMessage() {
|
|
5626
|
+
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5627
|
+
const itemId = this.assistantItemOrder[i];
|
|
5628
|
+
if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
|
|
5629
|
+
const text = this.assistantTextByItem.get(itemId) ?? "";
|
|
5630
|
+
const normalizedText = text.trim();
|
|
5631
|
+
if (normalizedText && this.toolProgressTexts.has(normalizedText)) continue;
|
|
5632
|
+
return this.createAssistantMessage(text);
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
async readMirroredSessionMessages() {
|
|
5636
|
+
return await readCodexMirroredSessionHistoryMessages({
|
|
5637
|
+
agentId: this.params.agentId,
|
|
5638
|
+
sessionFile: this.params.sessionFile,
|
|
5639
|
+
sessionId: this.params.sessionId,
|
|
5640
|
+
sessionKey: this.params.sessionKey
|
|
5641
|
+
}) ?? [];
|
|
5642
|
+
}
|
|
5643
|
+
createAssistantMessage(text) {
|
|
5644
|
+
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5645
|
+
const usage = this.tokenUsage ? {
|
|
5646
|
+
input: this.tokenUsage.input ?? 0,
|
|
5647
|
+
output: this.tokenUsage.output ?? 0,
|
|
5648
|
+
cacheRead: this.tokenUsage.cacheRead ?? 0,
|
|
5649
|
+
cacheWrite: this.tokenUsage.cacheWrite ?? 0,
|
|
5650
|
+
totalTokens: this.tokenUsage.total ?? (this.tokenUsage.input ?? 0) + (this.tokenUsage.output ?? 0) + (this.tokenUsage.cacheRead ?? 0) + (this.tokenUsage.cacheWrite ?? 0),
|
|
5651
|
+
cost: ZERO_USAGE.cost
|
|
5652
|
+
} : ZERO_USAGE;
|
|
5653
|
+
return {
|
|
5654
|
+
role: "assistant",
|
|
5655
|
+
content: [{
|
|
5656
|
+
type: "text",
|
|
5657
|
+
text
|
|
5658
|
+
}],
|
|
5659
|
+
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5660
|
+
provider: attribution.provider,
|
|
5661
|
+
model: this.params.modelId,
|
|
5662
|
+
usage,
|
|
5663
|
+
stopReason: this.aborted ? "aborted" : this.promptError ? "error" : "stop",
|
|
5664
|
+
errorMessage: this.promptError ? formatErrorMessage(this.promptError) : void 0,
|
|
5665
|
+
timestamp: Date.now()
|
|
5666
|
+
};
|
|
5667
|
+
}
|
|
5668
|
+
createAssistantMirrorMessage(title, text) {
|
|
5669
|
+
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5670
|
+
return {
|
|
5671
|
+
role: "assistant",
|
|
5672
|
+
content: [{
|
|
5673
|
+
type: "text",
|
|
5674
|
+
text: `${title}:\n${text}`
|
|
5675
|
+
}],
|
|
5676
|
+
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5677
|
+
provider: attribution.provider,
|
|
5678
|
+
model: this.params.modelId,
|
|
5679
|
+
usage: ZERO_USAGE,
|
|
5680
|
+
stopReason: "stop",
|
|
5681
|
+
timestamp: Date.now()
|
|
5682
|
+
};
|
|
5683
|
+
}
|
|
5684
|
+
createToolCallMessage(params) {
|
|
5685
|
+
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
5686
|
+
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5687
|
+
return {
|
|
5688
|
+
role: "assistant",
|
|
5689
|
+
content: [{
|
|
5690
|
+
type: "toolCall",
|
|
5691
|
+
id: params.id,
|
|
5692
|
+
name: params.name,
|
|
5693
|
+
arguments: args,
|
|
5694
|
+
input: args
|
|
5695
|
+
}],
|
|
5696
|
+
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5697
|
+
provider: attribution.provider,
|
|
5698
|
+
model: this.params.modelId,
|
|
5699
|
+
usage: ZERO_USAGE,
|
|
5700
|
+
stopReason: "toolUse",
|
|
5701
|
+
timestamp: Date.now()
|
|
5702
|
+
};
|
|
5703
|
+
}
|
|
5704
|
+
createToolResultMessage(params) {
|
|
5705
|
+
const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
|
|
5706
|
+
return {
|
|
5707
|
+
role: "toolResult",
|
|
5708
|
+
toolCallId: params.id,
|
|
5709
|
+
toolName: params.name,
|
|
5710
|
+
isError: params.isError,
|
|
5711
|
+
content: [{
|
|
5712
|
+
type: "toolResult",
|
|
5713
|
+
id: params.id,
|
|
5714
|
+
name: params.name,
|
|
5715
|
+
toolName: params.name,
|
|
5716
|
+
toolCallId: params.id,
|
|
5717
|
+
toolUseId: params.id,
|
|
5718
|
+
tool_use_id: params.id,
|
|
5719
|
+
content: text,
|
|
5720
|
+
text
|
|
5721
|
+
}],
|
|
5722
|
+
timestamp: Date.now()
|
|
5723
|
+
};
|
|
5724
|
+
}
|
|
5725
|
+
isNotificationForTurn(params) {
|
|
5726
|
+
const threadId = readCodexNotificationThreadId(params);
|
|
5727
|
+
const turnId = readCodexNotificationTurnId(params);
|
|
5728
|
+
return threadId === this.threadId && turnId === this.turnId;
|
|
5729
|
+
}
|
|
5730
|
+
isHookNotificationForCurrentThread(params) {
|
|
5731
|
+
const threadId = readString(params, "threadId");
|
|
5732
|
+
const turnId = params.turnId;
|
|
5733
|
+
return threadId === this.threadId && (turnId === this.turnId || turnId === null);
|
|
5734
|
+
}
|
|
5735
|
+
};
|
|
5736
|
+
function isHookNotificationMethod(method) {
|
|
5737
|
+
return method === "hook/started" || method === "hook/completed";
|
|
5738
|
+
}
|
|
5739
|
+
function readString(record, key) {
|
|
5740
|
+
const value = record[key];
|
|
5741
|
+
return typeof value === "string" ? value : void 0;
|
|
5742
|
+
}
|
|
5743
|
+
function estimateBase64DecodedBytes(base64) {
|
|
5744
|
+
let nonWhitespaceLength = 0;
|
|
5745
|
+
let previousCode = -1;
|
|
5746
|
+
let lastCode = -1;
|
|
5747
|
+
for (let i = 0; i < base64.length; i += 1) {
|
|
5748
|
+
const code = base64.charCodeAt(i);
|
|
5749
|
+
if (isBase64WhitespaceCode(code)) continue;
|
|
5750
|
+
nonWhitespaceLength += 1;
|
|
5751
|
+
previousCode = lastCode;
|
|
5752
|
+
lastCode = code;
|
|
5753
|
+
}
|
|
5754
|
+
if (nonWhitespaceLength === 0) return;
|
|
5755
|
+
const equalsCode = "=".charCodeAt(0);
|
|
5756
|
+
const padding = lastCode === equalsCode ? previousCode === equalsCode ? 2 : 1 : 0;
|
|
5757
|
+
return Math.max(0, Math.floor(nonWhitespaceLength * 3 / 4) - padding);
|
|
5758
|
+
}
|
|
5759
|
+
function isBase64WhitespaceCode(code) {
|
|
5760
|
+
return code === 32 || code === 9 || code === 10 || code === 13;
|
|
5761
|
+
}
|
|
5762
|
+
function resolveGeneratedImageMaxBytes(config) {
|
|
5763
|
+
const configured = config?.agents?.defaults?.mediaMaxMb;
|
|
5764
|
+
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * BYTES_PER_MB);
|
|
5765
|
+
return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
|
|
5766
|
+
}
|
|
5767
|
+
function normalizeNonEmptyString(value) {
|
|
5768
|
+
if (typeof value !== "string") return;
|
|
5769
|
+
return value.trim() || void 0;
|
|
5770
|
+
}
|
|
5771
|
+
function readNonEmptyString(record, key) {
|
|
5772
|
+
return normalizeNonEmptyString(record[key]);
|
|
5773
|
+
}
|
|
5774
|
+
function readNonEmptyStringArray(record, key) {
|
|
5775
|
+
const value = record[key];
|
|
5776
|
+
if (!Array.isArray(value)) return [];
|
|
5777
|
+
const entries = [];
|
|
5778
|
+
for (const entry of value) {
|
|
5779
|
+
const normalized = normalizeNonEmptyString(entry);
|
|
5780
|
+
if (normalized) entries.push(normalized);
|
|
5781
|
+
}
|
|
5782
|
+
return entries;
|
|
5783
|
+
}
|
|
5784
|
+
function readNullableString(record, key) {
|
|
5785
|
+
const value = record[key];
|
|
5786
|
+
if (value === null) return null;
|
|
5787
|
+
return typeof value === "string" ? value : void 0;
|
|
5788
|
+
}
|
|
5789
|
+
function readNumber(record, key) {
|
|
5790
|
+
const value = record[key];
|
|
5791
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5792
|
+
}
|
|
5793
|
+
function resolveStartedAtFromDurationMs(durationMs) {
|
|
5794
|
+
if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
|
|
5795
|
+
return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
|
|
5796
|
+
}
|
|
5797
|
+
function readNonNegativeInteger(record, key) {
|
|
5798
|
+
const value = readNumber(record, key);
|
|
5799
|
+
return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
5800
|
+
}
|
|
5801
|
+
function readCodexErrorNotificationMessage(record) {
|
|
5802
|
+
const error = record.error;
|
|
5803
|
+
return isJsonObject(error) ? readString(error, "message") : void 0;
|
|
5804
|
+
}
|
|
5805
|
+
function readHookOutputEntries(value) {
|
|
5806
|
+
if (!Array.isArray(value)) return [];
|
|
5807
|
+
return value.flatMap((entry) => {
|
|
5808
|
+
if (!isJsonObject(entry)) return [];
|
|
5809
|
+
const text = readString(entry, "text");
|
|
5810
|
+
if (!text) return [];
|
|
5811
|
+
const kind = readString(entry, "kind");
|
|
5812
|
+
return [{
|
|
5813
|
+
...kind ? { kind } : {},
|
|
5814
|
+
text
|
|
5815
|
+
}];
|
|
5816
|
+
});
|
|
5817
|
+
}
|
|
5818
|
+
function normalizeCodexTokenUsage(record) {
|
|
5819
|
+
const inputTokens = readNumber(record, "inputTokens");
|
|
5820
|
+
const cacheRead = readNumber(record, "cachedInputTokens");
|
|
5821
|
+
return normalizeUsage({
|
|
5822
|
+
input: inputTokens !== void 0 && cacheRead !== void 0 ? Math.max(0, inputTokens - cacheRead) : inputTokens,
|
|
5823
|
+
output: readNumber(record, "outputTokens"),
|
|
5824
|
+
cacheRead,
|
|
5825
|
+
total: readNumber(record, "totalTokens")
|
|
5826
|
+
});
|
|
5827
|
+
}
|
|
5828
|
+
function splitPlanText(text) {
|
|
5829
|
+
return text.split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
|
|
5830
|
+
}
|
|
5831
|
+
function collectTextValues(map) {
|
|
5832
|
+
return [...map.values()].filter((text) => text.trim().length > 0);
|
|
5833
|
+
}
|
|
5834
|
+
function collectReasoningTextValues(groups, itemOrder) {
|
|
5835
|
+
return [...groups.values()].toSorted((left, right) => {
|
|
5836
|
+
const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
|
|
5837
|
+
if (itemDelta !== 0) return itemDelta;
|
|
5838
|
+
const methodDelta = reasoningMethodOrder(left.method) - reasoningMethodOrder(right.method);
|
|
5839
|
+
return methodDelta !== 0 ? methodDelta : left.index - right.index;
|
|
5840
|
+
}).map((group) => group.text).filter((text) => text.trim().length > 0);
|
|
5841
|
+
}
|
|
5842
|
+
function reasoningMethodOrder(method) {
|
|
5843
|
+
return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
|
|
5844
|
+
}
|
|
5845
|
+
function extractRawAssistantText(item) {
|
|
5846
|
+
return (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
|
|
5847
|
+
if (!isJsonObject(entry)) return [];
|
|
5848
|
+
const type = readString(entry, "type");
|
|
5849
|
+
if (type !== "output_text" && type !== "text") return [];
|
|
5850
|
+
const value = readString(entry, "text");
|
|
5851
|
+
return value ? [value] : [];
|
|
5852
|
+
}).join("").trim() || void 0;
|
|
5853
|
+
}
|
|
5854
|
+
function itemKind(item) {
|
|
5855
|
+
switch (item.type) {
|
|
5856
|
+
case "dynamicToolCall":
|
|
5857
|
+
case "mcpToolCall": return "tool";
|
|
5858
|
+
case "commandExecution": return "command";
|
|
5859
|
+
case "fileChange": return "patch";
|
|
5860
|
+
case "webSearch": return "search";
|
|
5861
|
+
case "reasoning":
|
|
5862
|
+
case "contextCompaction": return "analysis";
|
|
5863
|
+
default: return;
|
|
5864
|
+
}
|
|
5865
|
+
}
|
|
5866
|
+
function itemTitle(item) {
|
|
5867
|
+
switch (item.type) {
|
|
5868
|
+
case "commandExecution": return "Command";
|
|
5869
|
+
case "fileChange": return "File change";
|
|
5870
|
+
case "mcpToolCall": return "MCP tool";
|
|
5871
|
+
case "dynamicToolCall": return "Tool";
|
|
5872
|
+
case "webSearch": return "Web search";
|
|
5873
|
+
case "contextCompaction": return "Context compaction";
|
|
5874
|
+
case "reasoning": return "Reasoning";
|
|
5875
|
+
default: return item.type;
|
|
5876
|
+
}
|
|
5877
|
+
}
|
|
5878
|
+
function itemStatus(item) {
|
|
5879
|
+
const status = readItemString(item, "status");
|
|
5880
|
+
if (status === "failed" || status === "error") return "failed";
|
|
5881
|
+
if (status === "declined") return "blocked";
|
|
5882
|
+
if (status === "inProgress" || status === "in_progress" || status === "running") return "running";
|
|
5883
|
+
return "completed";
|
|
5884
|
+
}
|
|
5885
|
+
function auditNativeToolTerminalStatus(item) {
|
|
5886
|
+
if (item.type === "imageView" || item.type === "sleep") return "completed";
|
|
5887
|
+
const status = readItemString(item, "status");
|
|
5888
|
+
if (status === "completed") return "completed";
|
|
5889
|
+
if (status === "failed" || status === "error") return "failed";
|
|
5890
|
+
if (status === "declined") return "blocked";
|
|
5891
|
+
return "unknown";
|
|
5892
|
+
}
|
|
5893
|
+
function auditNativeToolUnfinishedStatus(item) {
|
|
5894
|
+
return item.type === "webSearch" || item.type === "imageGeneration" ? "unknown" : "failed";
|
|
5895
|
+
}
|
|
5896
|
+
function formatMissingToolResultError(params) {
|
|
5897
|
+
return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
|
|
5898
|
+
}
|
|
5899
|
+
function isNonSuccessItemStatus(status) {
|
|
5900
|
+
return status === "failed" || status === "blocked";
|
|
5901
|
+
}
|
|
5902
|
+
function itemName(item) {
|
|
5903
|
+
if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
|
|
5904
|
+
if (item.type === "mcpToolCall" && typeof item.tool === "string") {
|
|
5905
|
+
const server = typeof item.server === "string" ? item.server : void 0;
|
|
5906
|
+
return server ? `${server}.${item.tool}` : item.tool;
|
|
5907
|
+
}
|
|
5908
|
+
if (item.type === "commandExecution") return "bash";
|
|
5909
|
+
if (item.type === "fileChange") return "apply_patch";
|
|
5910
|
+
if (item.type === "webSearch") return "web_search";
|
|
5911
|
+
}
|
|
5912
|
+
function auditNativeToolName(item) {
|
|
5913
|
+
if (item.type === "dynamicToolCall") return;
|
|
5914
|
+
const progressName = itemName(item);
|
|
5915
|
+
if (progressName) return progressName;
|
|
5916
|
+
if (item.type === "collabAgentToolCall") return typeof item.tool === "string" && item.tool.trim() ? `collab.${item.tool.trim()}` : "collab_agent";
|
|
5917
|
+
if (item.type === "imageGeneration") return "image_generation";
|
|
5918
|
+
if (item.type === "imageView") return "image_view";
|
|
5919
|
+
if (item.type === "sleep") return "sleep";
|
|
5920
|
+
}
|
|
5921
|
+
function isSideEffectingNativeToolItem(item) {
|
|
5922
|
+
return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
|
|
5923
|
+
}
|
|
5924
|
+
function shouldSynthesizeToolProgressForItem(item) {
|
|
5925
|
+
switch (item.type) {
|
|
5926
|
+
case "commandExecution":
|
|
5927
|
+
case "fileChange":
|
|
5928
|
+
case "webSearch":
|
|
5929
|
+
case "mcpToolCall": return true;
|
|
5930
|
+
default: return false;
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
function shouldRecordNativeToolTranscript(item) {
|
|
5934
|
+
return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
|
|
5935
|
+
}
|
|
5936
|
+
function isMutatingNativeToolItem(item) {
|
|
5937
|
+
if (item.type === "commandExecution") return true;
|
|
5938
|
+
return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
|
|
5939
|
+
}
|
|
5940
|
+
function shouldClearTerminalPresentationForNativeItem(item) {
|
|
5941
|
+
switch (item.type) {
|
|
5942
|
+
case "collabAgentToolCall":
|
|
5943
|
+
case "commandExecution":
|
|
5944
|
+
case "fileChange":
|
|
5945
|
+
case "imageGeneration":
|
|
5946
|
+
case "imageView":
|
|
5947
|
+
case "mcpToolCall":
|
|
5948
|
+
case "webSearch": return true;
|
|
5949
|
+
default: return false;
|
|
5950
|
+
}
|
|
5951
|
+
}
|
|
5952
|
+
function nativeToolActionFingerprint(item) {
|
|
5953
|
+
if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
|
|
5954
|
+
type: item.type,
|
|
5955
|
+
command: item.command,
|
|
5956
|
+
cwd: typeof item.cwd === "string" ? item.cwd : ""
|
|
5957
|
+
});
|
|
5958
|
+
if (item.type === "fileChange") return JSON.stringify({
|
|
5959
|
+
type: item.type,
|
|
5960
|
+
changes: itemFileChanges(item)
|
|
5961
|
+
});
|
|
5962
|
+
}
|
|
5963
|
+
function isNativePostToolUseRelayItem(item) {
|
|
5964
|
+
switch (item.type) {
|
|
5965
|
+
case "commandExecution":
|
|
5966
|
+
case "fileChange":
|
|
5967
|
+
case "mcpToolCall": return true;
|
|
5968
|
+
default: return false;
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5971
|
+
function shouldSuppressChannelProgressForItem(item) {
|
|
5972
|
+
if (shouldSynthesizeToolProgressForItem(item)) return true;
|
|
5973
|
+
return item.type === "dynamicToolCall";
|
|
5974
|
+
}
|
|
5975
|
+
function itemToolArgs(item) {
|
|
5976
|
+
if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
|
|
5977
|
+
command: item.command,
|
|
5978
|
+
...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
|
|
5979
|
+
});
|
|
5980
|
+
if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChanges(item) });
|
|
5981
|
+
if (item.type === "webSearch") return webSearchToolArgs(item);
|
|
5982
|
+
if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
|
|
5983
|
+
}
|
|
5984
|
+
function webSearchToolArgs(item) {
|
|
5985
|
+
const action = isJsonObject(item.action) ? item.action : void 0;
|
|
5986
|
+
const actionType = action ? readNonEmptyString(action, "type") : void 0;
|
|
5987
|
+
const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
|
|
5988
|
+
const query = normalizeNonEmptyString(item.query) ?? (action && actionType === "search" ? readNonEmptyString(action, "query") : void 0) ?? queries[0];
|
|
5989
|
+
const url = action ? readNonEmptyString(action, "url") : void 0;
|
|
5990
|
+
const pattern = action ? readNonEmptyString(action, "pattern") : void 0;
|
|
5991
|
+
const args = {};
|
|
5992
|
+
if (query) args.query = query;
|
|
5993
|
+
if (queries.length > 0) args.queries = queries;
|
|
5994
|
+
if (actionType && actionType !== "search") args.action = actionType;
|
|
5995
|
+
if (url) args.url = url;
|
|
5996
|
+
if (pattern) args.pattern = pattern;
|
|
5997
|
+
if (!query && !url && !pattern) args.queryUnavailable = true;
|
|
5998
|
+
return sanitizeCodexAgentEventRecord(args);
|
|
5999
|
+
}
|
|
6000
|
+
function itemToolResult(item) {
|
|
6001
|
+
if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
|
|
6002
|
+
status: item.status,
|
|
6003
|
+
exitCode: item.exitCode,
|
|
6004
|
+
durationMs: item.durationMs
|
|
6005
|
+
}) };
|
|
6006
|
+
if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
|
|
6007
|
+
status: item.status,
|
|
6008
|
+
changes: itemFileChanges(item)
|
|
6009
|
+
}) };
|
|
6010
|
+
if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
|
|
6011
|
+
status: item.status,
|
|
6012
|
+
durationMs: item.durationMs,
|
|
6013
|
+
...item.error ? { error: item.error } : {},
|
|
6014
|
+
...item.result ? { result: item.result } : {}
|
|
6015
|
+
}) };
|
|
6016
|
+
if (item.type === "webSearch") return { result: webSearchToolResult(item) };
|
|
6017
|
+
return {};
|
|
6018
|
+
}
|
|
6019
|
+
function webSearchToolResult(item) {
|
|
6020
|
+
return sanitizeCodexAgentEventRecord({
|
|
6021
|
+
status: itemStatus(item),
|
|
6022
|
+
...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
|
|
6023
|
+
...webSearchToolArgs(item)
|
|
6024
|
+
});
|
|
6025
|
+
}
|
|
6026
|
+
function itemFileChanges(item) {
|
|
6027
|
+
return Array.isArray(item.changes) ? item.changes.map((change) => ({
|
|
6028
|
+
path: change.path,
|
|
6029
|
+
kind: change.kind
|
|
6030
|
+
})) : [];
|
|
6031
|
+
}
|
|
6032
|
+
function itemToolError(item, status, outputTextByItem) {
|
|
6033
|
+
if (status === "blocked") return "codex native tool blocked";
|
|
6034
|
+
if (status !== "failed") return;
|
|
6035
|
+
return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
|
|
6036
|
+
}
|
|
6037
|
+
function itemMeta(item, detailMode = "explain") {
|
|
6038
|
+
if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
|
|
6039
|
+
command: item.command,
|
|
6040
|
+
cwd: typeof item.cwd === "string" ? item.cwd : void 0
|
|
6041
|
+
}, { detailMode });
|
|
6042
|
+
if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
|
|
6043
|
+
const toolName = itemName(item);
|
|
6044
|
+
if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
|
|
6045
|
+
}
|
|
6046
|
+
function itemOutputText(item, outputTextByItem) {
|
|
6047
|
+
if (item.type === "commandExecution") return item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim() || void 0;
|
|
6048
|
+
if (item.type === "dynamicToolCall") return collectDynamicToolContentText(item.contentItems).trim() || void 0;
|
|
6049
|
+
if (item.type === "mcpToolCall") {
|
|
6050
|
+
if (item.error) return stringifyJsonValue(item.error);
|
|
6051
|
+
return item.result ? stringifyJsonValue(item.result) : void 0;
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
function itemTranscriptResultText(item, outputTextByItem) {
|
|
6055
|
+
const output = itemOutputText(item, outputTextByItem);
|
|
6056
|
+
if (output) return output;
|
|
6057
|
+
const result = itemToolResult(item).result;
|
|
6058
|
+
return result ? stringifyJsonValue(result) : itemStatus(item);
|
|
6059
|
+
}
|
|
6060
|
+
function appendToolOutputDeltaText(outputTextByItem, itemId, delta) {
|
|
6061
|
+
const current = outputTextByItem.get(itemId) ?? "";
|
|
6062
|
+
if (current.length >= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return;
|
|
6063
|
+
const remaining = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - current.length;
|
|
6064
|
+
const next = current + (delta.length > remaining ? delta.slice(0, remaining) : delta);
|
|
6065
|
+
outputTextByItem.set(itemId, next);
|
|
6066
|
+
}
|
|
6067
|
+
function normalizeToolTranscriptArguments(value) {
|
|
6068
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
6069
|
+
return value;
|
|
6070
|
+
}
|
|
6071
|
+
function collectDynamicToolContentText(contentItems) {
|
|
6072
|
+
if (!Array.isArray(contentItems)) return "";
|
|
6073
|
+
return contentItems.flatMap((entry) => {
|
|
6074
|
+
if (!isJsonObject(entry)) return [];
|
|
6075
|
+
const text = readString(entry, "text");
|
|
6076
|
+
return text ? [text] : [];
|
|
6077
|
+
}).join("\n");
|
|
6078
|
+
}
|
|
6079
|
+
function truncateToolTranscriptText(text) {
|
|
6080
|
+
if (text.length <= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return text;
|
|
6081
|
+
return `${text.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)}\n...(truncated)...`;
|
|
6082
|
+
}
|
|
6083
|
+
function toolResultStatusText(params) {
|
|
6084
|
+
return params.isError ? `${params.name} failed` : `${params.name} completed`;
|
|
6085
|
+
}
|
|
6086
|
+
function stringifyJsonValue(value) {
|
|
6087
|
+
try {
|
|
6088
|
+
return JSON.stringify(value, null, 2);
|
|
6089
|
+
} catch {
|
|
6090
|
+
return;
|
|
6091
|
+
}
|
|
6092
|
+
}
|
|
6093
|
+
function formatToolSummary(toolName, meta) {
|
|
6094
|
+
const trimmedMeta = meta?.trim();
|
|
6095
|
+
return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
|
|
6096
|
+
}
|
|
6097
|
+
function formatToolOutput(toolName, meta, output) {
|
|
6098
|
+
const formattedOutput = formatToolProgressOutput(output);
|
|
6099
|
+
if (!formattedOutput) return formatToolSummary(toolName, meta);
|
|
6100
|
+
const fence = markdownFenceForText(formattedOutput);
|
|
6101
|
+
return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
|
|
6102
|
+
}
|
|
6103
|
+
function markdownFenceForText(text) {
|
|
6104
|
+
return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
|
|
6105
|
+
}
|
|
6106
|
+
function longestBacktickRun(value) {
|
|
6107
|
+
let longest = 0;
|
|
6108
|
+
let current = 0;
|
|
6109
|
+
for (const char of value) {
|
|
6110
|
+
if (char === "`") {
|
|
6111
|
+
current += 1;
|
|
6112
|
+
longest = Math.max(longest, current);
|
|
6113
|
+
continue;
|
|
6114
|
+
}
|
|
6115
|
+
current = 0;
|
|
6116
|
+
}
|
|
6117
|
+
return longest;
|
|
6118
|
+
}
|
|
6119
|
+
function readItemString(item, key) {
|
|
6120
|
+
const value = item[key];
|
|
6121
|
+
return typeof value === "string" ? value : void 0;
|
|
6122
|
+
}
|
|
6123
|
+
function readItem(value) {
|
|
6124
|
+
if (!isJsonObject(value)) return;
|
|
6125
|
+
const type = typeof value.type === "string" ? value.type : void 0;
|
|
6126
|
+
const id = typeof value.id === "string" ? value.id : void 0;
|
|
6127
|
+
if (!type || !id) return;
|
|
6128
|
+
return value;
|
|
6129
|
+
}
|
|
6130
|
+
//#endregion
|
|
6131
|
+
//#region extensions/codex/src/app-server/provider-capabilities.ts
|
|
6132
|
+
async function readConfiguredProviderWebSearchSupport(params) {
|
|
6133
|
+
return (await params.client.request("modelProvider/capabilities/read", {}, {
|
|
6134
|
+
timeoutMs: params.timeoutMs,
|
|
6135
|
+
signal: params.signal
|
|
6136
|
+
})).webSearch ? "supported" : "unsupported";
|
|
6137
|
+
}
|
|
6138
|
+
async function resolveCodexProviderWebSearchSupportForClient(params) {
|
|
6139
|
+
const modelProviderOverride = params.modelProviderOverride?.trim().toLowerCase();
|
|
6140
|
+
if (modelProviderOverride === "openai") return "supported";
|
|
6141
|
+
if (modelProviderOverride) return "unsupported";
|
|
6142
|
+
try {
|
|
6143
|
+
return await readConfiguredProviderWebSearchSupport(params);
|
|
6144
|
+
} catch {
|
|
6145
|
+
return "unknown";
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6148
|
+
async function resolveCodexProviderWebSearchSupport(params) {
|
|
6149
|
+
let client;
|
|
6150
|
+
try {
|
|
6151
|
+
client = await params.clientFactory({
|
|
6152
|
+
startOptions: params.appServer.start,
|
|
6153
|
+
authProfileId: params.authProfileId,
|
|
6154
|
+
agentDir: params.agentDir,
|
|
6155
|
+
config: params.config,
|
|
6156
|
+
timeoutMs: params.appServer.requestTimeoutMs
|
|
6157
|
+
});
|
|
3352
6158
|
return await resolveCodexProviderWebSearchSupportForClient({
|
|
3353
6159
|
client,
|
|
3354
6160
|
timeoutMs: params.appServer.requestTimeoutMs,
|
|
@@ -3362,4 +6168,4 @@ async function resolveCodexProviderWebSearchSupport(params) {
|
|
|
3362
6168
|
}
|
|
3363
6169
|
}
|
|
3364
6170
|
//#endregion
|
|
3365
|
-
export {
|
|
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 };
|