@openclaw/codex 2026.5.10-beta.2 → 2026.5.10-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/{client-CTzy3Y6M.js → client-CpksBQ9l.js} +1 -1
  2. package/dist/{client-factory-BJL_efz4.js → client-factory-D2P0KD6r.js} +1 -1
  3. package/dist/{request-DC1Dz3iZ.js → command-formatters-Ttwc_kgX.js} +192 -57
  4. package/dist/{command-handlers-BLZCm3fD.js → command-handlers-iLF1EI-b.js} +306 -15
  5. package/dist/{compact-4FVe6NwI.js → compact-BJ1UYEyA.js} +3 -3
  6. package/dist/{rate-limit-cache-DbZvmrAD.js → computer-use-CVLaKaW3.js} +3 -26
  7. package/dist/{config-CT01BBDc.js → config-C7xdbzrp.js} +1 -1
  8. package/dist/{conversation-binding-FqeYliIk.js → conversation-binding-D4XZ-tvV.js} +5 -4
  9. package/dist/harness.js +8 -4
  10. package/dist/index.js +11 -6
  11. package/dist/media-understanding-provider.js +142 -41
  12. package/dist/{models-H-PTi3W0.js → models-Bg-Qf5s-.js} +2 -2
  13. package/dist/{plugin-activation-CweAZa7r.js → plugin-activation-C0soz2YD.js} +1 -1
  14. package/dist/{protocol-validators-CeCyJaWj.js → protocol-validators-CSY0BFBo.js} +4 -1
  15. package/dist/provider.js +2 -2
  16. package/dist/rate-limit-cache-dvhq-4pi.js +24 -0
  17. package/dist/request-BCAfJvSg.js +41 -0
  18. package/dist/{run-attempt-CefRS5_d.js → run-attempt-DZvighJE.js} +548 -1053
  19. package/dist/{session-binding-B44KIZM2.js → session-binding-UFKjHkKJ.js} +13 -4
  20. package/dist/{shared-client-p-TvEiNL.js → shared-client-K13b0L1X.js} +78 -13
  21. package/dist/side-question-Dh_g53P9.js +548 -0
  22. package/dist/test-api.js +2 -2
  23. package/dist/{thread-lifecycle-CGc-O_5d.js → thread-lifecycle-i42nWiSS.js} +18 -11
  24. package/dist/vision-tools-Bo0P6gwM.js +1008 -0
  25. package/package.json +7 -6
@@ -1,560 +1,28 @@
1
- import { l as resolveCodexPluginsPolicy, o as readCodexPluginConfig, s as resolveCodexAppServerRuntimeOptions, u as withMcpElicitationsApprovalPolicy } from "./config-CT01BBDc.js";
2
- import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-CeCyJaWj.js";
1
+ import { l as resolveCodexPluginsPolicy, o as readCodexPluginConfig, s as resolveCodexAppServerRuntimeOptions, u as withMcpElicitationsApprovalPolicy } from "./config-C7xdbzrp.js";
2
+ import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-CSY0BFBo.js";
3
3
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
4
- import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-CTzy3Y6M.js";
5
- import { d as formatCodexUsageLimitErrorMessage, i as formatCodexDisplayText } from "./request-DC1Dz3iZ.js";
6
- import { a as refreshCodexAppServerAuthTokens, c as resolveCodexAppServerAuthProfileIdForAgent, l as resolveCodexAppServerEnvApiKeyCacheKey, o as resolveCodexAppServerAuthAccountCacheKey, s as resolveCodexAppServerAuthProfileId, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerHomeDir } from "./shared-client-p-TvEiNL.js";
7
- import { i as readCodexAppServerBinding } from "./session-binding-B44KIZM2.js";
8
- import { i as defaultCodexAppInventoryCache, r as buildCodexAppInventoryCacheKey } from "./plugin-activation-CweAZa7r.js";
9
- import { a as buildTurnStartParams, c as buildCodexPluginThreadConfig, d as createCodexDynamicToolBridge, f as filterCodexDynamicTools, l as buildCodexPluginThreadConfigInputFingerprint, n as buildDeveloperInstructions, o as codexDynamicToolsFingerprint, p as normalizeCodexDynamicToolName, s as startOrResumeThread, t as areCodexDynamicToolFingerprintsCompatible, u as shouldBuildCodexPluginThreadConfig } from "./thread-lifecycle-CGc-O_5d.js";
10
- import { n as defaultCodexAppServerClientFactory } from "./client-factory-BJL_efz4.js";
11
- import { n as rememberCodexRateLimits, r as ensureCodexComputerUse, t as readRecentCodexRateLimits } from "./rate-limit-cache-DbZvmrAD.js";
4
+ import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-CpksBQ9l.js";
5
+ import { n as CODEX_CONTROL_METHODS } from "./request-BCAfJvSg.js";
6
+ import { d as resolveCodexUsageLimitResetAtMs, f as shouldRefreshCodexRateLimitsForUsageLimitMessage, r as formatCodexDisplayText, u as formatCodexUsageLimitErrorMessage } from "./command-formatters-Ttwc_kgX.js";
7
+ import { c as resolveCodexAppServerAuthProfileId, d as resolveCodexAppServerHomeDir, l as resolveCodexAppServerAuthProfileIdForAgent, o as refreshCodexAppServerAuthTokens, s as resolveCodexAppServerAuthAccountCacheKey, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerEnvApiKeyCacheKey } from "./shared-client-K13b0L1X.js";
8
+ import { i as readCodexAppServerBinding } from "./session-binding-UFKjHkKJ.js";
9
+ import { i as defaultCodexAppInventoryCache, r as buildCodexAppInventoryCacheKey } from "./plugin-activation-C0soz2YD.js";
10
+ import { d as buildCodexPluginThreadConfig, f as buildCodexPluginThreadConfigInputFingerprint, g as normalizeCodexDynamicToolName, h as filterCodexDynamicTools, m as createCodexDynamicToolBridge, o as buildTurnStartParams, p as shouldBuildCodexPluginThreadConfig, r as buildDeveloperInstructions, s as codexDynamicToolsFingerprint, t as areCodexDynamicToolFingerprintsCompatible, u as startOrResumeThread } from "./thread-lifecycle-i42nWiSS.js";
11
+ import { n as defaultCodexAppServerClientFactory } from "./client-factory-D2P0KD6r.js";
12
+ import { n as handleCodexAppServerElicitationRequest, r as handleCodexAppServerApprovalRequest, t as filterToolsForVisionInputs } from "./vision-tools-Bo0P6gwM.js";
13
+ import { t as ensureCodexComputerUse } from "./computer-use-CVLaKaW3.js";
14
+ import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-dvhq-4pi.js";
12
15
  import { createHash } from "node:crypto";
13
16
  import nodeFs from "node:fs";
14
- import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, callGatewayTool, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatApprovalDisplayPath, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, resolveSessionWriteLockAcquireTimeoutMs, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
17
+ import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveModelAuthMode, resolveSandboxContext, resolveSessionAgentIds, resolveSessionWriteLockAcquireTimeoutMs, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
15
18
  import fs from "node:fs/promises";
16
19
  import path from "node:path";
17
- import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
20
+ import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
18
21
  import { appendRegularFile, pathExists } from "openclaw/plugin-sdk/security-runtime";
19
22
  import { AsyncLocalStorage } from "node:async_hooks";
20
23
  import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
21
- import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "@mariozechner/pi-coding-agent";
22
- import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/text-runtime";
23
- //#region extensions/codex/src/app-server/plugin-approval-roundtrip.ts
24
- const DEFAULT_CODEX_APPROVAL_TIMEOUT_MS = 12e4;
25
- const MAX_PLUGIN_APPROVAL_TITLE_LENGTH = 80;
26
- const MAX_PLUGIN_APPROVAL_DESCRIPTION_LENGTH = 256;
27
- async function requestPluginApproval(params) {
28
- const timeoutMs = DEFAULT_CODEX_APPROVAL_TIMEOUT_MS;
29
- return callGatewayTool("plugin.approval.request", { timeoutMs: timeoutMs + 1e4 }, {
30
- pluginId: "openclaw-codex-app-server",
31
- title: truncateForGateway(params.title, MAX_PLUGIN_APPROVAL_TITLE_LENGTH),
32
- description: truncateForGateway(params.description, MAX_PLUGIN_APPROVAL_DESCRIPTION_LENGTH),
33
- severity: params.severity,
34
- toolName: params.toolName,
35
- toolCallId: params.toolCallId,
36
- agentId: params.paramsForRun.agentId,
37
- sessionKey: params.paramsForRun.sessionKey,
38
- turnSourceChannel: params.paramsForRun.messageChannel ?? params.paramsForRun.messageProvider,
39
- turnSourceTo: params.paramsForRun.currentChannelId,
40
- turnSourceAccountId: params.paramsForRun.agentAccountId,
41
- turnSourceThreadId: params.paramsForRun.currentThreadTs,
42
- timeoutMs,
43
- twoPhase: true
44
- }, { expectFinal: false });
45
- }
46
- function approvalRequestExplicitlyUnavailable(result) {
47
- if (result === null || result === void 0 || typeof result !== "object") return false;
48
- let descriptor;
49
- try {
50
- descriptor = Object.getOwnPropertyDescriptor(result, "decision");
51
- } catch {
52
- return false;
53
- }
54
- return descriptor !== void 0 && "value" in descriptor && descriptor.value === null;
55
- }
56
- async function waitForPluginApprovalDecision(params) {
57
- const waitPromise = callGatewayTool("plugin.approval.waitDecision", { timeoutMs: DEFAULT_CODEX_APPROVAL_TIMEOUT_MS + 1e4 }, { id: params.approvalId });
58
- if (!params.signal) return (await waitPromise)?.decision;
59
- let onAbort;
60
- const abortPromise = new Promise((_, reject) => {
61
- if (params.signal.aborted) {
62
- reject(params.signal.reason);
63
- return;
64
- }
65
- onAbort = () => reject(params.signal.reason);
66
- params.signal.addEventListener("abort", onAbort, { once: true });
67
- });
68
- try {
69
- return (await Promise.race([waitPromise, abortPromise]))?.decision;
70
- } finally {
71
- if (onAbort) params.signal.removeEventListener("abort", onAbort);
72
- }
73
- }
74
- function mapExecDecisionToOutcome(decision) {
75
- if (decision === "allow-once") return "approved-once";
76
- if (decision === "allow-always") return "approved-session";
77
- if (decision === null || decision === void 0) return "unavailable";
78
- return "denied";
79
- }
80
- function truncateForGateway(value, maxLength) {
81
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
82
- }
83
- //#endregion
84
- //#region extensions/codex/src/app-server/approval-bridge.ts
85
- const PERMISSION_DESCRIPTION_MAX_LENGTH = 700;
86
- const PERMISSION_SAMPLE_LIMIT = 2;
87
- const PERMISSION_VALUE_MAX_LENGTH = 48;
88
- const COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH = 80;
89
- const APPROVAL_PREVIEW_SCAN_MAX_LENGTH = 4096;
90
- const APPROVAL_PREVIEW_OMITTED = "[preview truncated or unsafe content omitted]";
91
- const ANSI_OSC_SEQUENCE_RE$1 = new RegExp(String.raw`(?:\u001b]|\u009d)[^\u001b\u009c\u0007]*(?:\u0007|\u001b\\|\u009c)`, "g");
92
- const ANSI_CONTROL_SEQUENCE_RE$1 = new RegExp(String.raw`(?:\u001b\[[0-?]*[ -/]*[@-~]|\u009b[0-?]*[ -/]*[@-~]|\u001b[@-Z\\-_])`, "g");
93
- const CONTROL_CHARACTER_RE$1 = new RegExp(String.raw`[\u0000-\u001f\u007f-\u009f]+`, "g");
94
- const INVISIBLE_FORMATTING_CONTROL_RE$1 = new RegExp(String.raw`[\u00ad\u034f\u061c\u200b-\u200f\u202a-\u202e\u2060-\u206f\ufeff\ufe00-\ufe0f\u{e0100}-\u{e01ef}]`, "gu");
95
- const DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE$1 = new RegExp(String.raw`(?:\u001b\][^\u001b\u009c\u0007]*|\u009d[^\u001b\u009c\u0007]*|\u001b\[[0-?]*[ -/]*|\u009b[0-?]*[ -/]*|\u001b)$`);
96
- async function handleCodexAppServerApprovalRequest(params) {
97
- const requestParams = isJsonObject(params.requestParams) ? params.requestParams : void 0;
98
- if (!matchesCurrentTurn(requestParams, params.threadId, params.turnId)) return;
99
- if (!isSupportedAppServerApprovalMethod(params.method)) return unsupportedApprovalResponse();
100
- const context = buildApprovalContext({
101
- method: params.method,
102
- requestParams,
103
- paramsForRun: params.paramsForRun
104
- });
105
- try {
106
- const requestResult = await requestPluginApproval({
107
- paramsForRun: params.paramsForRun,
108
- title: context.title,
109
- description: context.description,
110
- severity: context.severity,
111
- toolName: context.toolName,
112
- toolCallId: context.itemId
113
- });
114
- const approvalId = requestResult?.id;
115
- if (!approvalId) {
116
- emitApprovalEvent(params.paramsForRun, {
117
- phase: "resolved",
118
- kind: context.kind,
119
- status: "unavailable",
120
- title: context.title,
121
- ...context.eventDetails,
122
- ...approvalEventScope(params.method, "denied"),
123
- message: "Codex app-server approval route unavailable."
124
- });
125
- return buildApprovalResponse(params.method, context.requestParams, "denied");
126
- }
127
- emitApprovalEvent(params.paramsForRun, {
128
- phase: "requested",
129
- kind: context.kind,
130
- status: "pending",
131
- title: context.title,
132
- approvalId,
133
- approvalSlug: approvalId,
134
- ...context.eventDetails,
135
- message: "Codex app-server approval requested."
136
- });
137
- const outcome = mapExecDecisionToOutcome(approvalRequestExplicitlyUnavailable(requestResult) ? null : await waitForPluginApprovalDecision({
138
- approvalId,
139
- signal: params.signal
140
- }));
141
- emitApprovalEvent(params.paramsForRun, {
142
- phase: "resolved",
143
- kind: context.kind,
144
- status: outcome === "denied" ? "denied" : outcome === "unavailable" ? "unavailable" : outcome === "cancelled" ? "failed" : "approved",
145
- title: context.title,
146
- approvalId,
147
- approvalSlug: approvalId,
148
- ...context.eventDetails,
149
- ...approvalEventScope(params.method, outcome),
150
- message: approvalResolutionMessage(outcome)
151
- });
152
- return buildApprovalResponse(params.method, context.requestParams, outcome);
153
- } catch (error) {
154
- const cancelled = params.signal?.aborted === true;
155
- emitApprovalEvent(params.paramsForRun, {
156
- phase: "resolved",
157
- kind: context.kind,
158
- status: cancelled ? "failed" : "unavailable",
159
- title: context.title,
160
- ...context.eventDetails,
161
- ...approvalEventScope(params.method, cancelled ? "cancelled" : "denied"),
162
- message: cancelled ? "Codex app-server approval cancelled because the run stopped." : `Codex app-server approval route failed: ${formatCodexDisplayText(formatErrorMessage$1(error))}`
163
- });
164
- return buildApprovalResponse(params.method, context.requestParams, cancelled ? "cancelled" : "denied");
165
- }
166
- }
167
- function buildApprovalResponse(method, requestParams, outcome) {
168
- if (method === "item/commandExecution/requestApproval") return { decision: commandApprovalDecision(requestParams, outcome) };
169
- if (method === "item/fileChange/requestApproval") return { decision: fileChangeApprovalDecision(outcome) };
170
- if (method === "item/permissions/requestApproval") {
171
- if (outcome === "approved-session" || outcome === "approved-once") return {
172
- permissions: requestedPermissions(requestParams),
173
- scope: outcome === "approved-session" ? "session" : "turn"
174
- };
175
- return {
176
- permissions: {},
177
- scope: "turn"
178
- };
179
- }
180
- return unsupportedApprovalResponse();
181
- }
182
- function matchesCurrentTurn(requestParams, threadId, turnId) {
183
- if (!requestParams) return false;
184
- const requestThreadId = readString$4(requestParams, "threadId") ?? readString$4(requestParams, "conversationId");
185
- const requestTurnId = readString$4(requestParams, "turnId");
186
- return requestThreadId === threadId && requestTurnId === turnId;
187
- }
188
- function buildApprovalContext(params) {
189
- const itemId = readString$4(params.requestParams, "itemId") ?? readString$4(params.requestParams, "callId") ?? readString$4(params.requestParams, "approvalId");
190
- const commandDetailLines = params.method === "item/commandExecution/requestApproval" ? describeCommandApprovalDetails(params.requestParams) : [];
191
- const commandPreview = sanitizeApprovalPreview(readDisplayCommandPreview(params.requestParams), commandDetailLines.length > 0 ? COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH : 180);
192
- const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
193
- const command = commandPreview.text;
194
- const reason = reasonPreview.text;
195
- const kind = approvalKindForMethod(params.method);
196
- const permissionLines = params.method === "item/permissions/requestApproval" ? describeRequestedPermissions(params.requestParams) : [];
197
- const title = kind === "exec" ? "Codex app-server command approval" : params.method === "item/permissions/requestApproval" ? "Codex app-server permission approval" : kind === "plugin" ? "Codex app-server file approval" : "Codex app-server approval";
198
- const subject = permissionLines[0] ?? (command ? `Command: ${formatApprovalPreviewSubject(command, commandPreview.omitted)}` : commandPreview.omitted ? `Command: ${APPROVAL_PREVIEW_OMITTED}` : reason ? `Reason: ${formatApprovalPreviewSubject(reason, reasonPreview.omitted)}` : reasonPreview.omitted ? `Reason: ${APPROVAL_PREVIEW_OMITTED}` : `Request method: ${params.method}`);
199
- return {
200
- kind,
201
- title,
202
- description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [
203
- subject,
204
- ...commandDetailLines,
205
- params.paramsForRun.sessionKey && `Session: ${params.paramsForRun.sessionKey}`
206
- ].filter(Boolean).join("\n"),
207
- severity: kind === "exec" ? "warning" : "info",
208
- toolName: kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
209
- itemId,
210
- requestParams: params.requestParams,
211
- eventDetails: {
212
- ...itemId ? { itemId } : {},
213
- ...command ? { command } : {},
214
- ...commandPreview.omitted ? { commandPreviewOmitted: true } : {},
215
- ...reason ? { reason } : {},
216
- ...reasonPreview.omitted ? { reasonPreviewOmitted: true } : {}
217
- }
218
- };
219
- }
220
- function commandApprovalDecision(requestParams, outcome) {
221
- if (outcome === "cancelled") return commandRejectionDecision(requestParams, "cancel");
222
- if (outcome === "denied" || outcome === "unavailable") return commandRejectionDecision(requestParams, "decline");
223
- if (outcome === "approved-session") {
224
- if (hasAvailableDecision(requestParams, "acceptForSession")) return "acceptForSession";
225
- const amendmentDecision = findAvailableCommandAmendmentDecision(requestParams);
226
- if (amendmentDecision) return amendmentDecision;
227
- }
228
- return hasAvailableDecision(requestParams, "accept") ? "accept" : commandRejectionDecision(requestParams, "decline");
229
- }
230
- function fileChangeApprovalDecision(outcome) {
231
- if (outcome === "cancelled") return "cancel";
232
- if (outcome === "denied" || outcome === "unavailable") return "decline";
233
- return outcome === "approved-session" ? "acceptForSession" : "accept";
234
- }
235
- function requestedPermissions(requestParams) {
236
- const permissions = isJsonObject(requestParams?.permissions) ? requestParams.permissions : {};
237
- const granted = {};
238
- if (isJsonObject(permissions.network)) granted.network = permissions.network;
239
- if (isJsonObject(permissions.fileSystem)) granted.fileSystem = permissions.fileSystem;
240
- return granted;
241
- }
242
- function unsupportedApprovalResponse() {
243
- return {
244
- decision: "decline",
245
- reason: "OpenClaw codex app-server bridge does not grant native approvals yet."
246
- };
247
- }
248
- function describeRequestedPermissions(requestParams) {
249
- return describePermissionProfile(requestedPermissions(requestParams), "Permissions");
250
- }
251
- function describeCommandApprovalDetails(requestParams) {
252
- const lines = [];
253
- const additionalPermissions = isJsonObject(requestParams?.additionalPermissions) ? requestParams.additionalPermissions : void 0;
254
- if (additionalPermissions) lines.push(...describePermissionProfile(additionalPermissions, "Additional permissions"));
255
- const execpolicySummary = summarizeStringArray(requestParams?.proposedExecpolicyAmendment, "Proposed exec policy", sanitizePermissionScalar);
256
- if (execpolicySummary) lines.push(execpolicySummary);
257
- const networkAmendmentSummary = summarizeNetworkPolicyAmendments(requestParams?.proposedNetworkPolicyAmendments);
258
- if (networkAmendmentSummary) lines.push(networkAmendmentSummary);
259
- return lines;
260
- }
261
- function describePermissionProfile(permissions, label) {
262
- const lines = [];
263
- const kinds = [];
264
- const risks = /* @__PURE__ */ new Set();
265
- if (isJsonObject(permissions.network)) kinds.push("network");
266
- if (isJsonObject(permissions.fileSystem)) kinds.push("fileSystem");
267
- if (kinds.length > 0) lines.push(`${label}: ${kinds.join(", ")}`);
268
- let networkSummary;
269
- if (isJsonObject(permissions.network)) {
270
- const summaries = [summarizeNetworkEnabledPermission(permissions.network, risks), summarizePermissionRecord(permissions.network, risks, [{
271
- key: "allowHosts",
272
- label: "allowHosts",
273
- sanitize: sanitizePermissionHostValue,
274
- risksFor: permissionHostRisks
275
- }])].filter((summary) => Boolean(summary));
276
- networkSummary = summaries.length > 0 ? summaries.join("; ") : void 0;
277
- }
278
- let fileSystemSummary;
279
- if (isJsonObject(permissions.fileSystem)) {
280
- const summaries = [summarizePermissionRecord(permissions.fileSystem, risks, [
281
- {
282
- key: "read",
283
- label: "read",
284
- sanitize: sanitizePermissionPathValue,
285
- risksFor: permissionPathRisks
286
- },
287
- {
288
- key: "write",
289
- label: "write",
290
- sanitize: sanitizePermissionPathValue,
291
- risksFor: permissionPathRisks
292
- },
293
- {
294
- key: "roots",
295
- label: "roots",
296
- sanitize: sanitizePermissionPathValue,
297
- risksFor: permissionPathRisks
298
- },
299
- {
300
- key: "readPaths",
301
- label: "readPaths",
302
- sanitize: sanitizePermissionPathValue,
303
- risksFor: permissionPathRisks
304
- },
305
- {
306
- key: "writePaths",
307
- label: "writePaths",
308
- sanitize: sanitizePermissionPathValue,
309
- risksFor: permissionPathRisks
310
- }
311
- ]), summarizeFileSystemEntries(permissions.fileSystem, risks)].filter((summary) => Boolean(summary));
312
- fileSystemSummary = summaries.length > 0 ? summaries.join("; ") : void 0;
313
- }
314
- if (risks.size > 0) lines.push(`High-risk targets: ${[...risks].join(", ")}`);
315
- if (networkSummary) lines.push(`Network ${networkSummary}`);
316
- if (fileSystemSummary) lines.push(`File system ${fileSystemSummary}`);
317
- return lines;
318
- }
319
- function summarizeNetworkEnabledPermission(permission, risks) {
320
- const enabled = permission.enabled;
321
- if (typeof enabled !== "boolean") return;
322
- if (enabled) risks.add("network access");
323
- return `enabled: ${enabled}`;
324
- }
325
- function summarizeFileSystemEntries(permission, risks) {
326
- const entries = permission.entries;
327
- if (!Array.isArray(entries)) return;
328
- const samples = [];
329
- let count = 0;
330
- for (const entry of entries) {
331
- const item = isJsonObject(entry) ? entry : void 0;
332
- const path = typeof item?.path === "string" ? item.path.trim() : "";
333
- const access = typeof item?.access === "string" ? item.access.trim() : "";
334
- if (!path || !access) continue;
335
- count += 1;
336
- if (access !== "none") for (const risk of permissionPathRisks(path)) risks.add(risk);
337
- if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizePermissionScalar(access)} ${sanitizePermissionPathValue(path)}`);
338
- }
339
- if (count === 0) return;
340
- const remaining = count - samples.length;
341
- const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
342
- return `entries: ${samples.join(", ")}${remainderSuffix}`;
343
- }
344
- function summarizePermissionRecord(permission, risks, descriptors) {
345
- const details = [];
346
- for (const descriptor of descriptors) {
347
- const summary = summarizePermissionArray(permission, descriptor, risks);
348
- if (summary) details.push(summary);
349
- }
350
- return details.length > 0 ? details.join("; ") : void 0;
351
- }
352
- function summarizePermissionArray(record, descriptor, risks) {
353
- const values = readStringArray(record, descriptor.key);
354
- if (values.length === 0) return;
355
- for (const value of values) for (const risk of descriptor.risksFor(value)) risks.add(risk);
356
- const sampleValues = values.slice(0, PERMISSION_SAMPLE_LIMIT).map(descriptor.sanitize).filter(Boolean);
357
- if (sampleValues.length === 0) return `${descriptor.label}: ${values.length}`;
358
- const remaining = values.length - sampleValues.length;
359
- const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
360
- return `${descriptor.label}: ${sampleValues.join(", ")}${remainderSuffix}`;
361
- }
362
- function summarizeStringArray(value, label, sanitize) {
363
- if (!Array.isArray(value)) return;
364
- const values = value.filter((entry) => typeof entry === "string").map((entry) => sanitize(entry)).filter(Boolean);
365
- if (values.length === 0) return;
366
- const samples = values.slice(0, PERMISSION_SAMPLE_LIMIT);
367
- const remaining = values.length - samples.length;
368
- const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
369
- return `${label}: ${samples.join(", ")}${remainderSuffix}`;
370
- }
371
- function summarizeNetworkPolicyAmendments(value) {
372
- if (!Array.isArray(value)) return;
373
- const samples = [];
374
- let count = 0;
375
- for (const entry of value) {
376
- const amendment = isJsonObject(entry) ? entry : void 0;
377
- const host = typeof amendment?.host === "string" ? amendment.host : "";
378
- const action = typeof amendment?.action === "string" ? amendment.action : "";
379
- if (!host || !action) continue;
380
- count += 1;
381
- if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizePermissionScalar(action)} ${sanitizePermissionHostValue(host)}`);
382
- }
383
- if (count === 0) return;
384
- const remaining = count - samples.length;
385
- const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
386
- return `Proposed network policy: ${samples.join(", ")}${remainderSuffix}`;
387
- }
388
- function readStringArray(record, key) {
389
- const value = record[key];
390
- return Array.isArray(value) ? value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean) : [];
391
- }
392
- function sanitizePermissionHostValue(value) {
393
- const withoutScheme = sanitizePermissionScalar(value).toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "");
394
- const authority = withoutScheme.split(/[/?#]/, 1)[0] ?? withoutScheme;
395
- return truncate(authority.includes("@") ? authority.slice(authority.lastIndexOf("@") + 1) : authority, PERMISSION_VALUE_MAX_LENGTH);
396
- }
397
- function sanitizePermissionPathValue(value) {
398
- return truncate(formatApprovalDisplayPath(sanitizePermissionScalar(value)), PERMISSION_VALUE_MAX_LENGTH);
399
- }
400
- function sanitizePermissionScalar(value) {
401
- return sanitizeVisibleScalar(value);
402
- }
403
- function permissionHostRisks(value) {
404
- const normalized = value.trim().toLowerCase();
405
- const risks = [];
406
- if (normalized.includes("*")) {
407
- risks.push("wildcard hosts");
408
- if (isPrivateNetworkHostPattern(normalized)) risks.push("private-network wildcards");
409
- }
410
- return risks;
411
- }
412
- function permissionPathRisks(value) {
413
- const normalized = sanitizePermissionScalar(value);
414
- const risks = [];
415
- if (normalized === "/" || normalized === "\\" || /^[A-Za-z]:[\\/]*$/.test(normalized)) risks.push("filesystem root");
416
- return risks;
417
- }
418
- function isPrivateNetworkHostPattern(value) {
419
- const wildcardStripped = value.toLowerCase().replace(/^\*\./, "");
420
- if (wildcardStripped === "localhost" || wildcardStripped === "local" || wildcardStripped === "internal" || wildcardStripped === "lan" || wildcardStripped === "home" || wildcardStripped === "corp" || wildcardStripped === "private" || wildcardStripped.endsWith(".local") || wildcardStripped.endsWith(".internal") || wildcardStripped.endsWith(".lan") || wildcardStripped.endsWith(".home") || wildcardStripped.endsWith(".corp") || wildcardStripped.endsWith(".private")) return true;
421
- if (wildcardStripped.startsWith("10.") || wildcardStripped.startsWith("127.") || wildcardStripped.startsWith("192.168.") || wildcardStripped.startsWith("169.254.")) return true;
422
- return /^172\.(1[6-9]|2\d|3[0-1])\./.test(wildcardStripped);
423
- }
424
- function hasAvailableDecision(requestParams, decision) {
425
- const available = requestParams?.availableDecisions;
426
- if (!Array.isArray(available)) return true;
427
- return available.includes(decision);
428
- }
429
- function findAvailableCommandAmendmentDecision(requestParams) {
430
- const available = requestParams?.availableDecisions;
431
- if (!Array.isArray(available)) return;
432
- return available.find((entry) => isJsonObject(entry) && (isJsonObject(entry.acceptWithExecpolicyAmendment) || isJsonObject(entry.applyNetworkPolicyAmendment)));
433
- }
434
- function commandRejectionDecision(requestParams, preferred) {
435
- const available = requestParams?.availableDecisions;
436
- if (!Array.isArray(available)) return preferred;
437
- if (available.includes(preferred)) return preferred;
438
- const alternate = preferred === "decline" ? "cancel" : "decline";
439
- if (available.includes(alternate)) return alternate;
440
- return preferred;
441
- }
442
- function approvalResolutionMessage(outcome) {
443
- if (outcome === "approved-session") return "Codex app-server approval granted for the session.";
444
- if (outcome === "approved-once") return "Codex app-server approval granted for this turn.";
445
- if (outcome === "cancelled") return "Codex app-server approval cancelled.";
446
- if (outcome === "unavailable") return "Codex app-server approval unavailable.";
447
- return "Codex app-server approval denied.";
448
- }
449
- function approvalScopeForOutcome(outcome) {
450
- return outcome === "approved-session" ? "session" : "turn";
451
- }
452
- function approvalEventScope(method, outcome) {
453
- return method === "item/permissions/requestApproval" ? { scope: approvalScopeForOutcome(outcome) } : {};
454
- }
455
- function approvalKindForMethod(method) {
456
- if (method.includes("commandExecution") || method.includes("execCommand")) return "exec";
457
- if (method.includes("fileChange") || method.includes("Patch") || method.includes("permissions")) return "plugin";
458
- return "unknown";
459
- }
460
- function isSupportedAppServerApprovalMethod(method) {
461
- return method === "item/commandExecution/requestApproval" || method === "item/fileChange/requestApproval" || method === "item/permissions/requestApproval";
462
- }
463
- function emitApprovalEvent(params, data) {
464
- params.onAgentEvent?.({
465
- stream: "approval",
466
- data
467
- });
468
- }
469
- function readDisplayCommandPreview(record) {
470
- const actionCommand = readCommandActionsPreview(record);
471
- if (actionCommand) return actionCommand;
472
- return readCommandPreview(record);
473
- }
474
- function readCommandActionsPreview(record) {
475
- const actions = record?.commandActions;
476
- if (!Array.isArray(actions)) return;
477
- let source;
478
- for (const action of actions) {
479
- const command = isJsonObject(action) ? readString$4(action, "command") : void 0;
480
- if (!command) continue;
481
- source = appendPreviewPart(source, command, " && ");
482
- if (source.clipped) break;
483
- }
484
- return source;
485
- }
486
- function readCommandPreview(record) {
487
- const command = record?.command;
488
- if (typeof command === "string") return previewSource(command);
489
- if (!Array.isArray(command)) return;
490
- let source;
491
- for (const part of command) {
492
- if (typeof part !== "string") return;
493
- source = appendPreviewPart(source, part, " ");
494
- if (source.clipped) break;
495
- }
496
- return source;
497
- }
498
- function readStringPreview(record, key) {
499
- const value = readString$4(record, key);
500
- return value === void 0 ? void 0 : previewSource(value);
501
- }
502
- function readString$4(record, key) {
503
- const value = record?.[key];
504
- return typeof value === "string" ? value : void 0;
505
- }
506
- function truncate(value, maxLength) {
507
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
508
- }
509
- function previewSource(value) {
510
- return {
511
- value: value.slice(0, APPROVAL_PREVIEW_SCAN_MAX_LENGTH),
512
- clipped: value.length > APPROVAL_PREVIEW_SCAN_MAX_LENGTH
513
- };
514
- }
515
- function appendPreviewPart(source, part, separator) {
516
- const value = `${source?.value ? `${source.value}${separator}` : ""}${part}`;
517
- const clipped = source?.clipped === true || value.length > APPROVAL_PREVIEW_SCAN_MAX_LENGTH;
518
- return {
519
- value: value.slice(0, APPROVAL_PREVIEW_SCAN_MAX_LENGTH),
520
- clipped
521
- };
522
- }
523
- function sanitizeApprovalPreview(source, maxLength) {
524
- if (!source || !source.value) return { omitted: false };
525
- const sanitized = sanitizeVisibleScalar(source.value.replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE$1, ""));
526
- if (!sanitized) return { omitted: true };
527
- return {
528
- text: formatCodexDisplayText(truncate(sanitized, maxLength)),
529
- omitted: source.clipped
530
- };
531
- }
532
- function sanitizeVisibleScalar(value) {
533
- return value.replace(ANSI_OSC_SEQUENCE_RE$1, "").replace(ANSI_CONTROL_SEQUENCE_RE$1, "").replace(INVISIBLE_FORMATTING_CONTROL_RE$1, " ").replace(CONTROL_CHARACTER_RE$1, " ").replace(/\s+/g, " ").trim();
534
- }
535
- function formatApprovalPreviewSubject(text, omitted) {
536
- return omitted ? `${text} ${APPROVAL_PREVIEW_OMITTED}` : text;
537
- }
538
- function joinDescriptionLinesWithinLimit(lines, maxLength) {
539
- let description = "";
540
- for (const line of lines) {
541
- const prefix = description ? "\n" : "";
542
- const next = `${description}${prefix}${line}`;
543
- if (next.length <= maxLength) {
544
- description = next;
545
- continue;
546
- }
547
- const remaining = maxLength - description.length - prefix.length;
548
- if (remaining < 3) break;
549
- description += `${prefix}${truncate(line, remaining)}`;
550
- break;
551
- }
552
- return description;
553
- }
554
- function formatErrorMessage$1(error) {
555
- return error instanceof Error ? error.message : String(error);
556
- }
557
- //#endregion
24
+ import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "@earendil-works/pi-coding-agent";
25
+ import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
558
26
  //#region extensions/codex/src/app-server/context-engine-projection.ts
559
27
  const CONTEXT_HEADER = "OpenClaw assembled context for this turn:";
560
28
  const CONTEXT_OPEN = "<conversation_context>";
@@ -632,469 +100,6 @@ function truncateText(text, maxChars) {
632
100
  return text.length > maxChars ? `${text.slice(0, maxChars)}\n[truncated ${text.length - maxChars} chars]` : text;
633
101
  }
634
102
  //#endregion
635
- //#region extensions/codex/src/app-server/elicitation-bridge.ts
636
- const MCP_TOOL_APPROVAL_KIND = "mcp_tool_call";
637
- const MCP_TOOL_APPROVAL_KIND_KEY = "codex_approval_kind";
638
- const MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY = "connector_name";
639
- const MCP_TOOL_APPROVAL_TOOL_TITLE_KEY = "tool_title";
640
- const MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY = "tool_description";
641
- const MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY = "tool_params_display";
642
- const PLUGIN_APP_ID_META_KEYS = [
643
- "app_id",
644
- "appId",
645
- "codex_app_id",
646
- "codexAppId"
647
- ];
648
- const PLUGIN_NAME_META_KEYS = [
649
- "plugin_name",
650
- "pluginName",
651
- "codex_plugin_name",
652
- "codexPluginName"
653
- ];
654
- const PLUGIN_CONFIG_KEY_META_KEYS = [
655
- "config_key",
656
- "configKey",
657
- "codex_config_key"
658
- ];
659
- const PLUGIN_MARKETPLACE_NAME_META_KEYS = [
660
- "marketplace_name",
661
- "marketplaceName",
662
- "codex_marketplace_name",
663
- "codexMarketplaceName"
664
- ];
665
- const MAX_DISPLAY_PARAM_ENTRIES = 8;
666
- const MAX_DISPLAY_PARAM_VALUE_LENGTH = 120;
667
- const MAX_DISPLAY_VALUE_ARRAY_ITEMS = 8;
668
- const MAX_DISPLAY_VALUE_OBJECT_KEYS = 8;
669
- const MAX_DISPLAY_VALUE_DEPTH = 3;
670
- const DISPLAY_TEXT_SCAN_MAX_LENGTH = 4096;
671
- const ANSI_OSC_SEQUENCE_RE = new RegExp(String.raw`(?:\u001b]|\u009d)[^\u001b\u009c\u0007]*(?:\u0007|\u001b\\|\u009c)`, "g");
672
- const ANSI_CONTROL_SEQUENCE_RE = new RegExp(String.raw`(?:\u001b\[[0-?]*[ -/]*[@-~]|\u009b[0-?]*[ -/]*[@-~]|\u001b[@-Z\\-_])`, "g");
673
- const CONTROL_CHARACTER_RE = new RegExp(String.raw`[\u0000-\u001f\u007f-\u009f]+`, "g");
674
- const INVISIBLE_FORMATTING_CONTROL_RE = new RegExp(String.raw`[\u00ad\u034f\u061c\u200b-\u200f\u202a-\u202e\u2060-\u206f\ufeff\ufe00-\ufe0f\u{e0100}-\u{e01ef}]`, "gu");
675
- const DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE = new RegExp(String.raw`(?:\u001b\][^\u001b\u009c\u0007]*|\u009d[^\u001b\u009c\u0007]*|\u001b\[[0-?]*[ -/]*|\u009b[0-?]*[ -/]*|\u001b)$`);
676
- async function handleCodexAppServerElicitationRequest(params) {
677
- const requestParams = isJsonObject(params.requestParams) ? params.requestParams : void 0;
678
- if (!requestParams) return;
679
- if (!matchesCurrentThread(requestParams, params.threadId)) return;
680
- if (turnIdMismatches(requestParams, params.turnId)) return;
681
- const pluginResolution = resolvePluginElicitation({
682
- requestParams,
683
- pluginAppPolicyContext: params.pluginAppPolicyContext
684
- });
685
- if (pluginResolution.kind !== "not_plugin") {
686
- if (pluginResolution.kind === "decline") {
687
- logPluginElicitationDecline(pluginResolution.reason, requestParams);
688
- return declineElicitationResponse();
689
- }
690
- if (!hasExactTurnId(requestParams, params.turnId)) {
691
- logPluginElicitationDecline("missing_active_turn", requestParams);
692
- return declineElicitationResponse();
693
- }
694
- return buildPluginPolicyElicitationResponse(pluginResolution.entry, requestParams);
695
- }
696
- const approvalPrompt = readBridgeableApprovalElicitation(requestParams);
697
- if (!approvalPrompt) return;
698
- const outcome = await requestPluginApprovalOutcome({
699
- paramsForRun: params.paramsForRun,
700
- title: approvalPrompt.title,
701
- description: approvalPrompt.description,
702
- signal: params.signal
703
- });
704
- return buildElicitationResponse(approvalPrompt.requestedSchema, approvalPrompt.meta, outcome);
705
- }
706
- function matchesCurrentThread(requestParams, threadId) {
707
- if (!requestParams) return false;
708
- return readString$3(requestParams, "threadId") === threadId;
709
- }
710
- function turnIdMismatches(requestParams, turnId) {
711
- const rawTurnId = requestParams?.turnId;
712
- return rawTurnId !== null && rawTurnId !== void 0 && rawTurnId !== turnId;
713
- }
714
- function hasExactTurnId(requestParams, turnId) {
715
- return requestParams?.turnId === turnId;
716
- }
717
- function resolvePluginElicitation(params) {
718
- const requestParams = params.requestParams;
719
- if (!requestParams) return { kind: "not_plugin" };
720
- const meta = isJsonObject(requestParams._meta) ? requestParams._meta : {};
721
- const context = params.pluginAppPolicyContext;
722
- const entries = context ? Object.values(context.apps) : [];
723
- const appId = readFirstString(meta, PLUGIN_APP_ID_META_KEYS) ?? readFirstString(requestParams, PLUGIN_APP_ID_META_KEYS);
724
- if (appId) {
725
- if (!context) return {
726
- kind: "decline",
727
- reason: "missing_policy_context"
728
- };
729
- const entry = context.apps[appId];
730
- return uniquePluginMatch(entry ? [entry] : [], "app_id");
731
- }
732
- const serverName = readString$3(requestParams, "serverName");
733
- if (serverName && context) {
734
- const matches = entries.filter((entry) => entry.mcpServerNames.includes(serverName));
735
- if (matches.length > 0) return uniquePluginMatch(matches, "server_name");
736
- }
737
- const metadataResolution = resolvePluginStableMetadataMatch({
738
- meta,
739
- requestParams,
740
- entries,
741
- context
742
- });
743
- if (metadataResolution.kind !== "not_plugin") return metadataResolution;
744
- if (context && hasDisplayNameOnlyPluginMatch(meta, entries)) return {
745
- kind: "decline",
746
- reason: "display_name_only"
747
- };
748
- return { kind: "not_plugin" };
749
- }
750
- function resolvePluginStableMetadataMatch(params) {
751
- const pluginName = readFirstString(params.meta, PLUGIN_NAME_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_NAME_META_KEYS);
752
- const configKey = readFirstString(params.meta, PLUGIN_CONFIG_KEY_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_CONFIG_KEY_META_KEYS);
753
- const marketplaceName = readFirstString(params.meta, PLUGIN_MARKETPLACE_NAME_META_KEYS) ?? readFirstString(params.requestParams, PLUGIN_MARKETPLACE_NAME_META_KEYS);
754
- if (!pluginName && !configKey) return { kind: "not_plugin" };
755
- if (!params.context) return {
756
- kind: "decline",
757
- reason: "missing_policy_context"
758
- };
759
- return uniquePluginMatch(params.entries.filter((entry) => {
760
- if (marketplaceName && entry.marketplaceName !== marketplaceName) return false;
761
- if (pluginName && entry.pluginName !== pluginName) return false;
762
- if (configKey && entry.configKey !== configKey) return false;
763
- return true;
764
- }), "metadata");
765
- }
766
- function uniquePluginMatch(matches, source) {
767
- if (matches.length === 1 && matches[0]) return {
768
- kind: "matched",
769
- entry: matches[0]
770
- };
771
- return {
772
- kind: "decline",
773
- reason: matches.length === 0 ? `${source}_not_enabled` : `${source}_ambiguous`
774
- };
775
- }
776
- function hasDisplayNameOnlyPluginMatch(meta, entries) {
777
- const connectorName = readString$3(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
778
- if (!connectorName) return false;
779
- const normalized = normalizePluginIdentityText(connectorName);
780
- return entries.some((entry) => normalizePluginIdentityText(entry.pluginName) === normalized || normalizePluginIdentityText(entry.configKey) === normalized);
781
- }
782
- function normalizePluginIdentityText(value) {
783
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
784
- }
785
- function buildPluginPolicyElicitationResponse(entry, requestParams) {
786
- if (!entry.allowDestructiveActions) {
787
- logPluginElicitationDecline("destructive_actions_disabled", requestParams);
788
- return declineElicitationResponse();
789
- }
790
- if (readString$3(requestParams, "mode") !== "form" || !isJsonObject(requestParams.requestedSchema)) {
791
- logPluginElicitationDecline("unsupported_schema", requestParams);
792
- return declineElicitationResponse();
793
- }
794
- const meta = isJsonObject(requestParams._meta) ? requestParams._meta : {};
795
- const response = buildElicitationResponse(requestParams.requestedSchema, meta, "approved-once");
796
- if (isJsonObject(response) && response.action === "accept") return response;
797
- logPluginElicitationDecline("unmappable_schema", requestParams);
798
- return declineElicitationResponse();
799
- }
800
- function declineElicitationResponse() {
801
- return {
802
- action: "decline",
803
- content: null,
804
- _meta: null
805
- };
806
- }
807
- function logPluginElicitationDecline(reason, requestParams) {
808
- embeddedAgentLog.debug("codex plugin elicitation declined", {
809
- reason,
810
- serverName: readString$3(requestParams, "serverName"),
811
- mode: readString$3(requestParams, "mode")
812
- });
813
- }
814
- function readBridgeableApprovalElicitation(requestParams) {
815
- if (!requestParams || readString$3(requestParams, "mode") !== "form" || !isJsonObject(requestParams._meta) || requestParams._meta[MCP_TOOL_APPROVAL_KIND_KEY] !== MCP_TOOL_APPROVAL_KIND || !isJsonObject(requestParams.requestedSchema)) return;
816
- const requestedSchema = requestParams.requestedSchema;
817
- if (readString$3(requestedSchema, "type") !== "object" || !isJsonObject(requestedSchema.properties)) return;
818
- const title = sanitizeDisplayText(readString$3(requestParams, "message") ?? "") || "Codex MCP tool approval";
819
- return {
820
- title,
821
- description: buildApprovalDescription({
822
- title,
823
- meta: requestParams._meta,
824
- requestedSchema,
825
- serverName: sanitizeOptionalDisplayText(readString$3(requestParams, "serverName"))
826
- }),
827
- requestedSchema,
828
- meta: requestParams._meta
829
- };
830
- }
831
- function buildApprovalDescription(params) {
832
- const connectorName = sanitizeOptionalDisplayText(readString$3(params.meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY));
833
- const toolTitle = sanitizeOptionalDisplayText(readString$3(params.meta, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY));
834
- const toolDescription = sanitizeOptionalDisplayText(readString$3(params.meta, MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY));
835
- const summaryLines = [
836
- connectorName && `App: ${connectorName}`,
837
- toolTitle && `Tool: ${toolTitle}`,
838
- params.serverName && `MCP server: ${params.serverName}`,
839
- toolDescription
840
- ].filter((line) => Boolean(line));
841
- const paramLines = readDisplayParamLines(params.meta);
842
- const propertyLines = readPropertyDescriptionLines(params.requestedSchema);
843
- return [
844
- params.title,
845
- summaryLines.join("\n"),
846
- paramLines.length > 0 ? ["Parameters:", ...paramLines].join("\n") : "",
847
- propertyLines.length > 0 ? ["Fields:", ...propertyLines].join("\n") : ""
848
- ].filter(Boolean).join("\n\n");
849
- }
850
- function readPropertyDescriptionLines(requestedSchema) {
851
- const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
852
- return Object.entries(properties).map(([name, value]) => {
853
- const schema = isJsonObject(value) ? value : void 0;
854
- if (!schema) return;
855
- const propTitle = sanitizeDisplayText(readString$3(schema, "title") ?? "") || sanitizeDisplayText(name) || "field";
856
- const description = sanitizeOptionalDisplayText(readString$3(schema, "description"));
857
- return description ? `- ${propTitle}: ${description}` : `- ${propTitle}`;
858
- }).filter((line) => Boolean(line));
859
- }
860
- function readDisplayParamLines(meta) {
861
- const displayParams = meta[MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY];
862
- if (!Array.isArray(displayParams)) return [];
863
- const lines = displayParams.slice(0, MAX_DISPLAY_PARAM_ENTRIES).map((entry) => {
864
- const param = isJsonObject(entry) ? entry : void 0;
865
- if (!param) return;
866
- const name = sanitizeOptionalDisplayText(readString$3(param, "display_name")) ?? sanitizeOptionalDisplayText(readString$3(param, "name"));
867
- if (!name) return;
868
- return `- ${name}: ${formatDisplayParamValue(param.value)}`;
869
- }).filter((line) => Boolean(line));
870
- const remaining = displayParams.length - MAX_DISPLAY_PARAM_ENTRIES;
871
- return remaining > 0 ? [...lines, `- Additional parameters: ${remaining} more`] : lines;
872
- }
873
- function formatDisplayParamValue(value) {
874
- return truncateDisplayText(sanitizeDisplayText(typeof value === "string" ? value : formatDisplayJsonValue(value ?? null)), MAX_DISPLAY_PARAM_VALUE_LENGTH);
875
- }
876
- function formatDisplayJsonValue(value, depth = MAX_DISPLAY_VALUE_DEPTH) {
877
- if (value === null) return "null";
878
- if (typeof value === "string") return JSON.stringify(truncateDisplayText(sanitizeDisplayText(value), 80));
879
- if (typeof value === "number" || typeof value === "boolean") return String(value);
880
- if (Array.isArray(value)) {
881
- if (depth <= 0) return "[truncated]";
882
- const parts = [];
883
- const limit = Math.min(value.length, MAX_DISPLAY_VALUE_ARRAY_ITEMS);
884
- for (let i = 0; i < limit; i += 1) parts.push(formatDisplayJsonValue(value[i] ?? null, depth - 1));
885
- if (value.length > MAX_DISPLAY_VALUE_ARRAY_ITEMS) parts.push("...");
886
- return `[${parts.join(",")}]`;
887
- }
888
- if (typeof value === "object") {
889
- if (depth <= 0) return "{truncated}";
890
- const parts = [];
891
- let count = 0;
892
- let truncated = false;
893
- for (const key in value) {
894
- if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
895
- if (count >= MAX_DISPLAY_VALUE_OBJECT_KEYS) {
896
- truncated = true;
897
- break;
898
- }
899
- const safeKey = truncateDisplayText(sanitizeDisplayText(key), 80);
900
- parts.push(`${JSON.stringify(safeKey)}:${formatDisplayJsonValue(value[key] ?? null, depth - 1)}`);
901
- count += 1;
902
- }
903
- if (truncated) parts.push("...");
904
- return `{${parts.join(",")}}`;
905
- }
906
- return "null";
907
- }
908
- function sanitizeOptionalDisplayText(value) {
909
- return (value === void 0 ? "" : sanitizeDisplayText(value)) || void 0;
910
- }
911
- function sanitizeDisplayText(value) {
912
- const scanned = value.slice(0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
913
- const clipped = value.length > DISPLAY_TEXT_SCAN_MAX_LENGTH;
914
- 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();
915
- const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
916
- return clipped && escaped ? `${escaped}...` : escaped;
917
- }
918
- function truncateDisplayText(value, maxLength) {
919
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
920
- }
921
- async function requestPluginApprovalOutcome(params) {
922
- try {
923
- const requestResult = await requestPluginApproval({
924
- paramsForRun: params.paramsForRun,
925
- title: params.title,
926
- description: params.description,
927
- severity: "warning",
928
- toolName: "codex_mcp_tool_approval"
929
- });
930
- const approvalId = requestResult?.id;
931
- if (!approvalId) return "unavailable";
932
- return mapExecDecisionToOutcome(approvalRequestExplicitlyUnavailable(requestResult) ? null : await waitForPluginApprovalDecision({
933
- approvalId,
934
- signal: params.signal
935
- }));
936
- } catch {
937
- return params.signal?.aborted ? "cancelled" : "denied";
938
- }
939
- }
940
- function buildElicitationResponse(requestedSchema, meta, outcome) {
941
- if (outcome === "cancelled") return {
942
- action: "cancel",
943
- content: null,
944
- _meta: null
945
- };
946
- if (outcome === "denied" || outcome === "unavailable") return {
947
- action: "decline",
948
- content: null,
949
- _meta: null
950
- };
951
- const content = buildAcceptedContent(requestedSchema, meta, outcome);
952
- if (!content) {
953
- if (hasNoSchemaProperties(requestedSchema)) return {
954
- action: "accept",
955
- content: null,
956
- _meta: buildAcceptedMeta(meta, outcome)
957
- };
958
- embeddedAgentLog.warn("codex MCP approval elicitation approved without a mappable response", {
959
- approvalKind: meta[MCP_TOOL_APPROVAL_KIND_KEY],
960
- fields: Object.keys(requestedSchema.properties ?? {}),
961
- outcome
962
- });
963
- return {
964
- action: "decline",
965
- content: null,
966
- _meta: null
967
- };
968
- }
969
- return {
970
- action: "accept",
971
- content,
972
- _meta: buildAcceptedMeta(meta, outcome)
973
- };
974
- }
975
- function buildAcceptedContent(requestedSchema, meta, outcome) {
976
- const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : void 0;
977
- if (!properties) return;
978
- const required = Array.isArray(requestedSchema.required) ? new Set(requestedSchema.required.filter((entry) => typeof entry === "string")) : /* @__PURE__ */ new Set();
979
- const content = {};
980
- let sawApprovalField = false;
981
- for (const [name, value] of Object.entries(properties)) {
982
- const schema = isJsonObject(value) ? value : void 0;
983
- if (!schema) continue;
984
- const property = {
985
- name,
986
- schema,
987
- required: required.has(name)
988
- };
989
- const next = readApprovalFieldValue(property, outcome) ?? readPersistFieldValue(property, meta, outcome) ?? readFallbackFieldValue(property, outcome);
990
- if (next === void 0) {
991
- if (isApprovalField(property)) sawApprovalField = true;
992
- if (property.required) return;
993
- continue;
994
- }
995
- if (isApprovalField(property)) sawApprovalField = true;
996
- content[name] = next;
997
- }
998
- return sawApprovalField ? content : void 0;
999
- }
1000
- function readApprovalFieldValue(property, outcome) {
1001
- if (!isApprovalField(property)) return;
1002
- if (readString$3(property.schema, "type") === "boolean") return true;
1003
- const options = readEnumOptions(property.schema);
1004
- if (options.length === 0) return;
1005
- const sessionChoice = options.find((option) => isSessionApprovalOption(option));
1006
- const acceptChoice = options.find((option) => isPositiveApprovalOption(option));
1007
- if (outcome === "approved-session") return sessionChoice?.value ?? acceptChoice?.value;
1008
- return acceptChoice?.value ?? sessionChoice?.value;
1009
- }
1010
- function readPersistFieldValue(property, meta, outcome) {
1011
- if (!isPersistField(property) || outcome !== "approved-session") return;
1012
- const persistHints = readPersistHints(meta);
1013
- const options = readEnumOptions(property.schema);
1014
- if (options.length === 0) return;
1015
- const preferred = choosePersistHint(persistHints);
1016
- if (preferred) return options.find((option) => option.value === preferred || option.label === preferred)?.value;
1017
- }
1018
- function readDefaultValue(schema) {
1019
- return schema.default;
1020
- }
1021
- function readFallbackFieldValue(property, outcome) {
1022
- if (outcome === "approved-once" && isPersistField(property)) return;
1023
- return readDefaultValue(property.schema);
1024
- }
1025
- function isApprovalField(property) {
1026
- const haystack = propertyText(property).toLowerCase();
1027
- return /\b(approve|approval|allow|accept|decision)\b/.test(haystack);
1028
- }
1029
- function isPersistField(property) {
1030
- const haystack = propertyText(property).toLowerCase();
1031
- return /\b(persist|session|always|scope)\b/.test(haystack);
1032
- }
1033
- function propertyText(property) {
1034
- return [
1035
- property.name,
1036
- readString$3(property.schema, "title"),
1037
- readString$3(property.schema, "description")
1038
- ].filter(Boolean).join(" ");
1039
- }
1040
- function readPersistHints(meta) {
1041
- const raw = meta.persist;
1042
- if (typeof raw === "string") return [raw];
1043
- if (Array.isArray(raw)) return raw.filter((entry) => typeof entry === "string");
1044
- return ["session", "always"];
1045
- }
1046
- function buildAcceptedMeta(meta, outcome) {
1047
- if (outcome !== "approved-session") return null;
1048
- const persist = choosePersistHint(readPersistHints(meta));
1049
- return persist ? { persist } : null;
1050
- }
1051
- function choosePersistHint(persistHints) {
1052
- if (persistHints.includes("always")) return "always";
1053
- if (persistHints.includes("session")) return "session";
1054
- }
1055
- function hasNoSchemaProperties(requestedSchema) {
1056
- const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
1057
- return Object.keys(properties).length === 0;
1058
- }
1059
- function readEnumOptions(schema) {
1060
- if (Array.isArray(schema.enum)) {
1061
- const values = schema.enum.filter((entry) => typeof entry === "string");
1062
- const labels = Array.isArray(schema.enumNames) ? schema.enumNames.filter((entry) => typeof entry === "string") : [];
1063
- return values.map((value, index) => ({
1064
- value,
1065
- label: labels[index] ?? value
1066
- }));
1067
- }
1068
- if (Array.isArray(schema.oneOf)) return schema.oneOf.map((entry) => {
1069
- const option = isJsonObject(entry) ? entry : void 0;
1070
- const value = readString$3(option, "const");
1071
- if (!value) return;
1072
- return {
1073
- value,
1074
- label: readString$3(option, "title") ?? value
1075
- };
1076
- }).filter((entry) => Boolean(entry));
1077
- return [];
1078
- }
1079
- function isPositiveApprovalOption(option) {
1080
- const haystack = `${option.value} ${option.label}`.toLowerCase();
1081
- return /\b(allow|approve|accept|yes|continue|proceed|true)\b/.test(haystack);
1082
- }
1083
- function isSessionApprovalOption(option) {
1084
- const haystack = `${option.value} ${option.label}`.toLowerCase();
1085
- return /\b(session|always|persistent)\b/.test(haystack) && /\b(allow|approve|accept)\b/.test(haystack);
1086
- }
1087
- function readString$3(record, key) {
1088
- const value = record?.[key];
1089
- return typeof value === "string" && value.trim() ? value : void 0;
1090
- }
1091
- function readFirstString(record, keys) {
1092
- for (const key of keys) {
1093
- const value = readString$3(record, key);
1094
- if (value) return value;
1095
- }
1096
- }
1097
- //#endregion
1098
103
  //#region extensions/codex/src/app-server/session-history.ts
1099
104
  function isMissingFileError(error) {
1100
105
  return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
@@ -1282,10 +287,11 @@ const CODEX_PROMPT_TOTAL_INPUT_KEYS = [
1282
287
  const MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM = 20;
1283
288
  const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 12e3;
1284
289
  var CodexAppServerEventProjector = class {
1285
- constructor(params, threadId, turnId) {
290
+ constructor(params, threadId, turnId, options = {}) {
1286
291
  this.params = params;
1287
292
  this.threadId = threadId;
1288
293
  this.turnId = turnId;
294
+ this.options = options;
1289
295
  this.assistantTextByItem = /* @__PURE__ */ new Map();
1290
296
  this.assistantItemOrder = [];
1291
297
  this.reasoningTextByItem = /* @__PURE__ */ new Map();
@@ -1301,6 +307,9 @@ var CodexAppServerEventProjector = class {
1301
307
  this.toolTranscriptMessages = [];
1302
308
  this.toolTranscriptCallIds = /* @__PURE__ */ new Set();
1303
309
  this.toolTranscriptResultIds = /* @__PURE__ */ new Set();
310
+ this.nativeGeneratedMediaUrls = /* @__PURE__ */ new Set();
311
+ this.diagnosticToolStartedAtByItem = /* @__PURE__ */ new Map();
312
+ this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
1304
313
  this.assistantStarted = false;
1305
314
  this.reasoningStarted = false;
1306
315
  this.reasoningEnded = false;
@@ -1418,7 +427,7 @@ var CodexAppServerEventProjector = class {
1418
427
  messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
1419
428
  messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
1420
429
  heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
1421
- toolMediaUrls: toolTelemetry.toolMediaUrls,
430
+ toolMediaUrls: this.buildToolMediaUrls(toolTelemetry),
1422
431
  toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
1423
432
  successfulCronAdds: toolTelemetry.successfulCronAdds,
1424
433
  cloudCodeAssistFormatError: false,
@@ -1457,6 +466,9 @@ var CodexAppServerEventProjector = class {
1457
466
  this.promptError = "codex app-server attempt timed out";
1458
467
  this.promptErrorSource = "prompt";
1459
468
  }
469
+ markAborted() {
470
+ this.aborted = true;
471
+ }
1460
472
  isCompacting() {
1461
473
  return this.activeCompactionItemIds.size > 0;
1462
474
  }
@@ -1565,6 +577,7 @@ var CodexAppServerEventProjector = class {
1565
577
  this.rememberAssistantItem(item.id);
1566
578
  this.assistantTextByItem.set(item.id, item.text);
1567
579
  }
580
+ this.recordNativeGeneratedMedia(item);
1568
581
  if (item?.type === "plan" && typeof item.text === "string" && item.text) {
1569
582
  this.planTextByItem.set(item.id, item.text);
1570
583
  this.emitPlanUpdate({
@@ -1694,6 +707,7 @@ var CodexAppServerEventProjector = class {
1694
707
  this.rememberAssistantItem(item.id);
1695
708
  this.assistantTextByItem.set(item.id, item.text);
1696
709
  }
710
+ this.recordNativeGeneratedMedia(item);
1697
711
  if (item.type === "plan" && typeof item.text === "string" && item.text) {
1698
712
  this.planTextByItem.set(item.id, item.text);
1699
713
  this.emitPlanUpdate({
@@ -1704,6 +718,7 @@ var CodexAppServerEventProjector = class {
1704
718
  this.recordToolMeta(item);
1705
719
  this.recordNativeToolTranscriptCall(item);
1706
720
  this.recordNativeToolTranscriptResult(item);
721
+ this.emitAfterToolCallObservation(item);
1707
722
  this.emitToolResultSummary(item);
1708
723
  this.emitToolResultOutput(item);
1709
724
  }
@@ -1752,6 +767,16 @@ var CodexAppServerEventProjector = class {
1752
767
  this.rememberAssistantItem(itemId);
1753
768
  this.assistantTextByItem.set(itemId, text);
1754
769
  }
770
+ recordNativeGeneratedMedia(item) {
771
+ if (item?.type !== "imageGeneration") return;
772
+ const savedPath = readItemString(item, "savedPath")?.trim();
773
+ if (savedPath) this.nativeGeneratedMediaUrls.add(savedPath);
774
+ }
775
+ buildToolMediaUrls(toolTelemetry) {
776
+ const mediaUrls = new Set(toolTelemetry.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
777
+ if ((toolTelemetry.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.nativeGeneratedMediaUrls) mediaUrls.add(mediaUrl);
778
+ return mediaUrls.size > 0 ? [...mediaUrls] : toolTelemetry.toolMediaUrls;
779
+ }
1755
780
  async maybeEndReasoning() {
1756
781
  if (!this.reasoningStarted || this.reasoningEnded) return;
1757
782
  this.reasoningEnded = true;
@@ -1799,6 +824,12 @@ var CodexAppServerEventProjector = class {
1799
824
  const meta = itemMeta(item, this.toolProgressDetailMode());
1800
825
  const args = params.phase === "start" ? itemToolArgs(item) : void 0;
1801
826
  const status = params.phase === "result" ? itemStatus(item) : "running";
827
+ this.emitDiagnosticToolExecutionEvent({
828
+ phase: params.phase,
829
+ item,
830
+ name,
831
+ status
832
+ });
1802
833
  this.emitAgentEvent({
1803
834
  stream: "tool",
1804
835
  data: {
@@ -1815,6 +846,74 @@ var CodexAppServerEventProjector = class {
1815
846
  } : {}
1816
847
  }
1817
848
  });
849
+ if (params.phase === "result") this.emitAfterToolCallObservation(item);
850
+ }
851
+ emitDiagnosticToolExecutionEvent(params) {
852
+ const base = {
853
+ runId: this.params.runId,
854
+ sessionId: this.params.sessionId,
855
+ sessionKey: this.params.sessionKey,
856
+ toolName: params.name,
857
+ toolCallId: params.item.id
858
+ };
859
+ if (params.phase === "start") {
860
+ this.diagnosticToolStartedAtByItem.set(params.item.id, Date.now());
861
+ emitTrustedDiagnosticEvent({
862
+ type: "tool.execution.started",
863
+ ...base
864
+ });
865
+ return;
866
+ }
867
+ const startedAt = this.diagnosticToolStartedAtByItem.get(params.item.id);
868
+ this.diagnosticToolStartedAtByItem.delete(params.item.id);
869
+ const durationMs = (typeof params.item.durationMs === "number" ? params.item.durationMs : void 0) ?? (startedAt === void 0 ? 0 : Math.max(0, Date.now() - startedAt));
870
+ const terminalEvent = params.status === "blocked" ? {
871
+ type: "tool.execution.blocked",
872
+ reason: "codex_native_tool_blocked",
873
+ deniedReason: "codex_native_tool_blocked"
874
+ } : params.status === "failed" ? {
875
+ type: "tool.execution.error",
876
+ durationMs,
877
+ errorCategory: "codex_native_tool_error"
878
+ } : {
879
+ type: "tool.execution.completed",
880
+ durationMs
881
+ };
882
+ emitTrustedDiagnosticEvent({
883
+ ...base,
884
+ ...terminalEvent
885
+ });
886
+ }
887
+ emitAfterToolCallObservation(item) {
888
+ if (!this.shouldEmitAfterToolCallObservation(item)) return;
889
+ const name = itemName(item);
890
+ if (!name) return;
891
+ const status = itemStatus(item);
892
+ if (status === "running") return;
893
+ this.afterToolCallObservedItemIds.add(item.id);
894
+ const result = itemToolResult(item).result;
895
+ const error = itemToolError(item, status);
896
+ const startedAt = typeof item.durationMs === "number" ? Date.now() - Math.max(0, item.durationMs) : void 0;
897
+ const hookParams = {
898
+ toolName: name,
899
+ toolCallId: item.id,
900
+ runId: this.params.runId,
901
+ agentId: this.params.agentId,
902
+ sessionId: this.params.sessionId,
903
+ sessionKey: this.params.sessionKey,
904
+ startArgs: itemToolArgs(item) ?? {},
905
+ ...result !== void 0 ? { result } : {},
906
+ ...error ? { error } : {},
907
+ ...startedAt !== void 0 ? { startedAt } : {}
908
+ };
909
+ setImmediate(() => {
910
+ runAgentHarnessAfterToolCallHook(hookParams);
911
+ });
912
+ }
913
+ shouldEmitAfterToolCallObservation(item) {
914
+ if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
915
+ if (this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item)) return false;
916
+ return true;
1818
917
  }
1819
918
  emitToolResultSummary(item) {
1820
919
  if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
@@ -1986,13 +1085,15 @@ var CodexAppServerEventProjector = class {
1986
1085
  };
1987
1086
  }
1988
1087
  createToolCallMessage(params) {
1088
+ const args = normalizeToolTranscriptArguments(params.arguments);
1989
1089
  return {
1990
1090
  role: "assistant",
1991
1091
  content: [{
1992
1092
  type: "toolCall",
1993
1093
  id: params.id,
1994
1094
  name: params.name,
1995
- arguments: normalizeToolTranscriptArguments(params.arguments)
1095
+ arguments: args,
1096
+ input: args
1996
1097
  }],
1997
1098
  api: this.params.model.api ?? "openai-codex-responses",
1998
1099
  provider: this.params.provider,
@@ -2011,6 +1112,9 @@ var CodexAppServerEventProjector = class {
2011
1112
  isError: params.isError,
2012
1113
  content: [{
2013
1114
  type: "toolResult",
1115
+ id: params.id,
1116
+ name: params.name,
1117
+ toolName: params.name,
2014
1118
  toolCallId: params.id,
2015
1119
  toolUseId: params.id,
2016
1120
  tool_use_id: params.id,
@@ -2193,6 +1297,14 @@ function shouldSynthesizeToolProgressForItem(item) {
2193
1297
  default: return false;
2194
1298
  }
2195
1299
  }
1300
+ function isNativePostToolUseRelayItem(item) {
1301
+ switch (item.type) {
1302
+ case "commandExecution":
1303
+ case "fileChange":
1304
+ case "mcpToolCall": return true;
1305
+ default: return false;
1306
+ }
1307
+ }
2196
1308
  function shouldSuppressChannelProgressForItem(item) {
2197
1309
  if (shouldSynthesizeToolProgressForItem(item)) return true;
2198
1310
  return item.type === "dynamicToolCall";
@@ -2202,6 +1314,7 @@ function itemToolArgs(item) {
2202
1314
  command: item.command,
2203
1315
  ...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
2204
1316
  });
1317
+ if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChanges(item) });
2205
1318
  if (item.type === "webSearch" && typeof item.query === "string") return sanitizeCodexAgentEventRecord({ query: item.query });
2206
1319
  if (item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
2207
1320
  }
@@ -2213,10 +1326,7 @@ function itemToolResult(item) {
2213
1326
  }) };
2214
1327
  if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
2215
1328
  status: item.status,
2216
- changes: item.changes.map((change) => ({
2217
- path: change.path,
2218
- kind: change.kind
2219
- }))
1329
+ changes: itemFileChanges(item)
2220
1330
  }) };
2221
1331
  if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
2222
1332
  status: item.status,
@@ -2227,6 +1337,17 @@ function itemToolResult(item) {
2227
1337
  if (item.type === "webSearch") return { result: sanitizeCodexAgentEventRecord({ status: "completed" }) };
2228
1338
  return {};
2229
1339
  }
1340
+ function itemFileChanges(item) {
1341
+ return Array.isArray(item.changes) ? item.changes.map((change) => ({
1342
+ path: change.path,
1343
+ kind: change.kind
1344
+ })) : [];
1345
+ }
1346
+ function itemToolError(item, status) {
1347
+ if (status === "blocked") return "codex native tool blocked";
1348
+ if (status !== "failed") return;
1349
+ return itemOutputText(item) ?? "codex native tool failed";
1350
+ }
2230
1351
  function itemMeta(item, detailMode = "explain") {
2231
1352
  if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
2232
1353
  command: item.command,
@@ -2733,18 +1854,14 @@ function readRequestId(record) {
2733
1854
  return typeof value === "string" || typeof value === "number" ? value : void 0;
2734
1855
  }
2735
1856
  //#endregion
2736
- //#region extensions/codex/src/app-server/vision-tools.ts
2737
- function filterToolsForVisionInputs(tools, params) {
2738
- if (!params.modelHasVision || !params.hasInboundImages) return tools;
2739
- return tools.filter((tool) => tool.name !== "image");
2740
- }
2741
- //#endregion
2742
1857
  //#region extensions/codex/src/app-server/run-attempt.ts
2743
1858
  const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 3e4;
2744
1859
  const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
2745
1860
  const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
2746
1861
  const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
2747
1862
  const CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS = 100;
1863
+ const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
1864
+ const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
2748
1865
  const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 6e4;
2749
1866
  const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 6e4;
2750
1867
  const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 30 * 6e4;
@@ -2964,7 +2081,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
2964
2081
  config: params.config,
2965
2082
  agentId: params.agentId
2966
2083
  });
2967
- const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, sessionAgentId);
2084
+ const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, sessionAgentId);
2968
2085
  const startupBinding = await readCodexAppServerBinding(params.sessionFile);
2969
2086
  const startupAuthProfileCandidate = params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId;
2970
2087
  const startupAuthProfileId = params.authProfileStore ? resolveCodexAppServerAuthProfileId({
@@ -3047,13 +2164,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
3047
2164
  historyMessages = await readMirroredSessionHistoryMessages(params.sessionFile) ?? historyMessages;
3048
2165
  }
3049
2166
  const baseDeveloperInstructions = buildDeveloperInstructions(params);
3050
- const workspaceBootstrapInstructions = await buildCodexWorkspaceBootstrapInstructions({
2167
+ const workspaceBootstrapContext = await buildCodexWorkspaceBootstrapContext({
3051
2168
  params,
3052
2169
  resolvedWorkspace,
3053
2170
  effectiveWorkspace,
3054
2171
  sessionKey: sandboxSessionKey,
3055
2172
  sessionAgentId
3056
2173
  });
2174
+ const workspaceBootstrapInstructions = workspaceBootstrapContext.instructions;
3057
2175
  let promptText = params.prompt;
3058
2176
  let developerInstructions = joinPresentSections(baseDeveloperInstructions, workspaceBootstrapInstructions);
3059
2177
  let prePromptMessageCount = historyMessages.length;
@@ -3095,12 +2213,21 @@ async function runCodexAppServerAttempt(params, options = {}) {
3095
2213
  promptText = projection.promptText;
3096
2214
  prePromptMessageCount = projection.prePromptMessageCount;
3097
2215
  }
2216
+ promptText = prependCurrentTurnContext(promptText, params.currentTurnContext);
3098
2217
  const promptBuild = await resolveAgentHarnessBeforePromptBuildResult({
3099
2218
  prompt: promptText,
3100
2219
  developerInstructions,
3101
2220
  messages: historyMessages,
3102
2221
  ctx: hookContext
3103
2222
  });
2223
+ const systemPromptReport = buildCodexSystemPromptReport({
2224
+ attempt: params,
2225
+ sessionKey: sandboxSessionKey,
2226
+ workspaceDir: effectiveWorkspace,
2227
+ developerInstructions: promptBuild.developerInstructions,
2228
+ workspaceBootstrapContext,
2229
+ tools: toolBridge.specs
2230
+ });
3104
2231
  const trajectoryRecorder = createCodexTrajectoryRecorder({
3105
2232
  attempt: params,
3106
2233
  cwd: effectiveWorkspace,
@@ -3419,6 +2546,36 @@ async function runCodexAppServerAttempt(params, options = {}) {
3419
2546
  });
3420
2547
  lifecycleTerminalEmitted = true;
3421
2548
  };
2549
+ const executionPhaseKeys = /* @__PURE__ */ new Set();
2550
+ const emitExecutionPhaseOnce = (key, info) => {
2551
+ if (executionPhaseKeys.has(key)) return;
2552
+ executionPhaseKeys.add(key);
2553
+ params.onExecutionPhase?.({
2554
+ provider: params.provider,
2555
+ model: params.modelId,
2556
+ backend: "codex-app-server",
2557
+ ...info
2558
+ });
2559
+ };
2560
+ const reportCodexExecutionNotification = (notification) => {
2561
+ if (notification.method === "turn/started") {
2562
+ emitExecutionPhaseOnce("turn_accepted", { phase: "turn_accepted" });
2563
+ return;
2564
+ }
2565
+ if (notification.method === "item/agentMessage/delta") {
2566
+ emitExecutionPhaseOnce("assistant_output_started", { phase: "assistant_output_started" });
2567
+ return;
2568
+ }
2569
+ if (notification.method !== "item/started") return;
2570
+ const item = readCodexNotificationItem(notification.params);
2571
+ const tool = item ? codexExecutionToolName(item) : void 0;
2572
+ if (!item || !tool) return;
2573
+ emitExecutionPhaseOnce(`tool:${item.id}`, {
2574
+ phase: "tool_execution_started",
2575
+ tool,
2576
+ itemId: item.id
2577
+ });
2578
+ };
3422
2579
  const handleNotification = async (notification) => {
3423
2580
  userInputBridge?.handleNotification(notification);
3424
2581
  if (!projector || !turnId) {
@@ -3426,11 +2583,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
3426
2583
  return;
3427
2584
  }
3428
2585
  const isCurrentTurnNotification = isTurnNotification(notification.params, thread.threadId, turnId);
3429
- if (isCurrentTurnNotification) touchTurnCompletionActivity(`notification:${notification.method}`, { details: describeNotificationActivity(notification) });
2586
+ if (isCurrentTurnNotification) {
2587
+ touchTurnCompletionActivity(`notification:${notification.method}`, { details: describeNotificationActivity(notification) });
2588
+ reportCodexExecutionNotification(notification);
2589
+ }
3430
2590
  if (isCurrentTurnNotification && notification.method === "error") if (isRetryableErrorNotification(notification.params)) disarmTurnCompletionIdleWatch();
3431
2591
  else armTurnCompletionIdleWatch({ pinnedByTerminalError: true });
3432
2592
  else if (turnCompletionIdleWatchArmed && !turnCompletionIdleWatchPinnedByTerminalError && notification.method !== "turn/completed" && isCurrentTurnNotification) disarmTurnCompletionIdleWatch();
3433
2593
  const isTurnCompletion = notification.method === "turn/completed" && isCurrentTurnNotification;
2594
+ const isTurnAbortMarker = isCurrentTurnNotification && isCodexTurnAbortMarkerNotification(notification, { currentPromptText: promptBuild.prompt });
2595
+ const isTurnTerminal = isTurnCompletion || isTurnAbortMarker;
3434
2596
  try {
3435
2597
  await projector.handleNotification(notification);
3436
2598
  } catch (error) {
@@ -3439,7 +2601,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
3439
2601
  error
3440
2602
  });
3441
2603
  } finally {
3442
- if (isTurnCompletion) {
2604
+ if (isTurnTerminal) {
2605
+ if (isTurnAbortMarker) projector.markAborted();
3443
2606
  if (!timedOut && !runAbortController.signal.aborted) await steeringQueue?.flushPending();
3444
2607
  completed = true;
3445
2608
  clearTurnCompletionIdleTimer();
@@ -3512,6 +2675,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
3512
2675
  tool: call.tool,
3513
2676
  arguments: call.arguments
3514
2677
  });
2678
+ emitExecutionPhaseOnce(`tool:${call.callId}`, {
2679
+ phase: "tool_execution_started",
2680
+ tool: call.tool,
2681
+ toolCallId: call.callId
2682
+ });
3515
2683
  const toolMeta = inferCodexDynamicToolMeta(call, resolveCodexToolProgressDetailMode(params.toolProgressDetail));
3516
2684
  const toolArgs = sanitizeCodexToolArguments(call.arguments);
3517
2685
  emitCodexAppServerEvent(params, {
@@ -3586,10 +2754,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
3586
2754
  };
3587
2755
  const turnStartFailureMessages = [...historyMessages, {
3588
2756
  role: "user",
3589
- content: [{
3590
- type: "text",
3591
- text: promptBuild.prompt
3592
- }]
2757
+ content: promptBuild.prompt,
2758
+ timestamp: Date.now()
3593
2759
  }];
3594
2760
  let turn;
3595
2761
  try {
@@ -3614,8 +2780,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
3614
2780
  signal: runAbortController.signal
3615
2781
  }));
3616
2782
  } catch (error) {
3617
- const usageLimitError = formatCodexTurnStartUsageLimitError(error, pendingNotifications);
3618
- const turnStartErrorMessage = usageLimitError ?? formatErrorMessage(error);
2783
+ const usageLimitError = await formatCodexTurnStartUsageLimitError({
2784
+ client,
2785
+ error,
2786
+ pendingNotifications,
2787
+ timeoutMs: appServer.requestTimeoutMs,
2788
+ signal: runAbortController.signal
2789
+ });
2790
+ const turnStartErrorMessage = usageLimitError?.message ?? formatErrorMessage(error);
3619
2791
  emitCodexAppServerEvent(params, {
3620
2792
  stream: "codex_app_server.lifecycle",
3621
2793
  data: {
@@ -3665,11 +2837,24 @@ async function runCodexAppServerAttempt(params, options = {}) {
3665
2837
  }
3666
2838
  });
3667
2839
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
3668
- if (usageLimitError) throw new Error(usageLimitError, { cause: error });
2840
+ if (usageLimitError) {
2841
+ await markCodexAuthProfileBlockedFromRateLimits({
2842
+ params,
2843
+ authProfileId: startupAuthProfileId,
2844
+ rateLimits: usageLimitError.rateLimitsForProfile
2845
+ });
2846
+ return buildCodexTurnStartFailureResult({
2847
+ params,
2848
+ message: usageLimitError.message,
2849
+ messagesSnapshot: turnStartFailureMessages,
2850
+ systemPromptReport
2851
+ });
2852
+ }
3669
2853
  throw error;
3670
2854
  }
3671
2855
  turnId = turn.turn.id;
3672
2856
  const activeTurnId = turn.turn.id;
2857
+ emitExecutionPhaseOnce("turn_accepted", { phase: "turn_accepted" });
3673
2858
  userInputBridge = createCodexUserInputBridge({
3674
2859
  paramsForRun: params,
3675
2860
  threadId: thread.threadId,
@@ -3682,7 +2867,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
3682
2867
  prompt: promptBuild.prompt,
3683
2868
  imagesCount: params.images?.length ?? 0
3684
2869
  });
3685
- projector = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId);
2870
+ projector = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId, { nativePostToolUseRelayEnabled: nativeHookRelay?.allowedEvents.includes("post_tool_use") === true });
3686
2871
  emitLifecycleStart();
3687
2872
  const activeProjector = projector;
3688
2873
  for (const notification of pendingNotifications.splice(0)) await enqueueNotification(notification);
@@ -3719,9 +2904,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
3719
2904
  runAbortController.abort("timeout");
3720
2905
  }, Math.max(100, params.timeoutMs));
3721
2906
  const abortListener = () => {
2907
+ const shouldRetireClient = timedOut;
3722
2908
  interruptCodexTurnBestEffort(client, {
3723
2909
  threadId: thread.threadId,
3724
- turnId: activeTurnId
2910
+ turnId: activeTurnId,
2911
+ timeoutMs: shouldRetireClient ? CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS : void 0
2912
+ });
2913
+ if (shouldRetireClient) retireCodexAppServerClientAfterTimedOutTurn(client, {
2914
+ threadId: thread.threadId,
2915
+ turnId: activeTurnId,
2916
+ reason: String(runAbortController.signal.reason ?? "timeout")
3725
2917
  });
3726
2918
  resolveCompletion?.();
3727
2919
  };
@@ -3731,7 +2923,18 @@ async function runCodexAppServerAttempt(params, options = {}) {
3731
2923
  await completion;
3732
2924
  const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected });
3733
2925
  const finalAborted = result.aborted || runAbortController.signal.aborted;
3734
- const finalPromptError = turnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : timedOut ? "codex app-server attempt timed out" : result.promptError;
2926
+ let finalPromptError = turnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : timedOut ? "codex app-server attempt timed out" : result.promptError;
2927
+ const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
2928
+ if (shouldRefreshCodexRateLimitsForUsageLimitMessage(finalPromptErrorMessage)) finalPromptError = await refreshCodexUsageLimitErrorMessage({
2929
+ client,
2930
+ source: {
2931
+ message: finalPromptErrorMessage,
2932
+ codexErrorInfo: "usageLimitExceeded",
2933
+ rateLimits: readRecentCodexRateLimits()
2934
+ },
2935
+ timeoutMs: appServer.requestTimeoutMs,
2936
+ signal: runAbortController.signal
2937
+ });
3735
2938
  const finalPromptErrorSource = timedOut ? "prompt" : result.promptErrorSource;
3736
2939
  recordCodexTrajectoryCompletion(trajectoryRecorder, {
3737
2940
  attempt: params,
@@ -3825,7 +3028,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
3825
3028
  timedOut,
3826
3029
  aborted: finalAborted,
3827
3030
  promptError: finalPromptError,
3828
- promptErrorSource: finalPromptErrorSource
3031
+ promptErrorSource: finalPromptErrorSource,
3032
+ systemPromptReport
3829
3033
  };
3830
3034
  } finally {
3831
3035
  emitLifecycleTerminal({
@@ -3862,6 +3066,60 @@ async function runCodexAppServerAttempt(params, options = {}) {
3862
3066
  clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
3863
3067
  }
3864
3068
  }
3069
+ async function markCodexAuthProfileBlockedFromRateLimits(params) {
3070
+ const authProfileId = params.authProfileId?.trim();
3071
+ if (!authProfileId || !params.params.authProfileStore) return;
3072
+ const blockedUntil = resolveCodexUsageLimitResetAtMs(params.rateLimits);
3073
+ if (!blockedUntil) return;
3074
+ try {
3075
+ await markAuthProfileBlockedUntil({
3076
+ store: params.params.authProfileStore,
3077
+ profileId: authProfileId,
3078
+ blockedUntil,
3079
+ source: "codex_rate_limits",
3080
+ agentDir: params.params.agentDir,
3081
+ runId: params.params.runId,
3082
+ modelId: params.params.modelId
3083
+ });
3084
+ } catch (error) {
3085
+ embeddedAgentLog.debug("failed to mark Codex auth profile blocked from app-server limits", {
3086
+ authProfileId,
3087
+ error: formatErrorMessage(error)
3088
+ });
3089
+ }
3090
+ }
3091
+ function buildCodexTurnStartFailureResult(params) {
3092
+ return {
3093
+ aborted: false,
3094
+ externalAbort: false,
3095
+ timedOut: false,
3096
+ idleTimedOut: false,
3097
+ timedOutDuringCompaction: false,
3098
+ timedOutDuringToolExecution: false,
3099
+ promptError: params.message,
3100
+ promptErrorSource: "prompt",
3101
+ sessionIdUsed: params.params.sessionId,
3102
+ messagesSnapshot: params.messagesSnapshot,
3103
+ assistantTexts: [],
3104
+ toolMetas: [],
3105
+ lastAssistant: void 0,
3106
+ didSendViaMessagingTool: false,
3107
+ messagingToolSentTexts: [],
3108
+ messagingToolSentMediaUrls: [],
3109
+ messagingToolSentTargets: [],
3110
+ cloudCodeAssistFormatError: false,
3111
+ replayMetadata: {
3112
+ hadPotentialSideEffects: false,
3113
+ replaySafe: true
3114
+ },
3115
+ itemLifecycle: {
3116
+ startedCount: 0,
3117
+ completedCount: 0,
3118
+ activeCount: 0
3119
+ },
3120
+ systemPromptReport: params.systemPromptReport
3121
+ };
3122
+ }
3865
3123
  async function handleDynamicToolCallWithTimeout(params) {
3866
3124
  if (params.signal.aborted) return failedDynamicToolResponse("OpenClaw dynamic tool call aborted before execution.");
3867
3125
  const controller = new AbortController();
@@ -3989,8 +3247,31 @@ function buildCodexNativeHookRelayId(params) {
3989
3247
  return `codex-${hash.digest("hex").slice(0, 40)}`;
3990
3248
  }
3991
3249
  function interruptCodexTurnBestEffort(client, params) {
3992
- Promise.resolve().then(() => client.request("turn/interrupt", params)).catch((error) => {
3250
+ const requestOptions = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? { timeoutMs: params.timeoutMs } : void 0;
3251
+ const requestParams = {
3252
+ threadId: params.threadId,
3253
+ turnId: params.turnId
3254
+ };
3255
+ try {
3256
+ const interrupt = requestOptions ? client.request("turn/interrupt", requestParams, requestOptions) : client.request("turn/interrupt", requestParams);
3257
+ Promise.resolve(interrupt).catch((error) => {
3258
+ embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
3259
+ });
3260
+ } catch (error) {
3993
3261
  embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
3262
+ }
3263
+ }
3264
+ function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
3265
+ const clearedSharedClient = clearSharedCodexAppServerClientIfCurrent(client);
3266
+ if (!clearedSharedClient) {
3267
+ const close = client.close;
3268
+ if (typeof close === "function") close.call(client);
3269
+ }
3270
+ embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
3271
+ threadId: params.threadId,
3272
+ turnId: params.turnId,
3273
+ reason: params.reason,
3274
+ clearedSharedClient
3994
3275
  });
3995
3276
  }
3996
3277
  function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
@@ -4003,7 +3284,7 @@ async function buildDynamicTools(input) {
4003
3284
  const { params } = input;
4004
3285
  if (params.disableTools || !supportsModelTools(params.model)) return [];
4005
3286
  const modelHasVision = params.model.input?.includes("image") ?? false;
4006
- const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
3287
+ const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
4007
3288
  const createOpenClawCodingTools = openClawCodingToolsFactoryForTests ?? (await import("openclaw/plugin-sdk/agent-harness")).createOpenClawCodingTools;
4008
3289
  const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
4009
3290
  const filteredTools = filterCodexDynamicToolsForAllowlist(filterToolsForVisionInputs(filterCodexDynamicTools(createOpenClawCodingTools({
@@ -4142,14 +3423,76 @@ function resolveCodexTurnTerminalIdleTimeoutMs(value) {
4142
3423
  function readDynamicToolCallParams(value) {
4143
3424
  return readCodexDynamicToolCallParams(value);
4144
3425
  }
4145
- function formatCodexTurnStartUsageLimitError(error, pendingNotifications) {
3426
+ async function formatCodexTurnStartUsageLimitError(params) {
3427
+ return refreshCodexUsageLimitError({
3428
+ client: params.client,
3429
+ source: readCodexTurnStartUsageLimitErrorSource(params.error, params.pendingNotifications),
3430
+ timeoutMs: params.timeoutMs,
3431
+ signal: params.signal
3432
+ });
3433
+ }
3434
+ async function refreshCodexUsageLimitErrorMessage(params) {
3435
+ return (await refreshCodexUsageLimitError({
3436
+ client: params.client,
3437
+ source: params.source,
3438
+ timeoutMs: params.timeoutMs,
3439
+ signal: params.signal
3440
+ }))?.message;
3441
+ }
3442
+ async function refreshCodexUsageLimitError(params) {
3443
+ const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
3444
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
3445
+ message: initialMessage,
3446
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
3447
+ } : void 0;
3448
+ const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
3449
+ client: params.client,
3450
+ timeoutMs: params.timeoutMs,
3451
+ signal: params.signal
3452
+ });
3453
+ if (!rateLimits) return initialMessage ? {
3454
+ message: initialMessage,
3455
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
3456
+ } : void 0;
3457
+ const message = formatCodexUsageLimitErrorMessage({
3458
+ message: params.source.message,
3459
+ codexErrorInfo: params.source.codexErrorInfo,
3460
+ rateLimits
3461
+ }) ?? initialMessage;
3462
+ return message ? {
3463
+ message,
3464
+ rateLimitsForProfile: rateLimits
3465
+ } : void 0;
3466
+ }
3467
+ async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
3468
+ if (params.signal?.aborted) return;
3469
+ try {
3470
+ const rateLimits = await params.client.request(CODEX_CONTROL_METHODS.rateLimits, void 0, {
3471
+ timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
3472
+ signal: params.signal
3473
+ });
3474
+ rememberCodexRateLimits(rateLimits);
3475
+ return rateLimits;
3476
+ } catch (error) {
3477
+ embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
3478
+ return;
3479
+ }
3480
+ }
3481
+ function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
3482
+ if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
3483
+ return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
3484
+ }
3485
+ function readCodexTurnStartUsageLimitErrorSource(error, pendingNotifications) {
4146
3486
  const notificationError = readLatestCodexErrorNotification(pendingNotifications);
3487
+ const notificationRateLimits = readLatestRateLimitNotificationPayload(pendingNotifications);
4147
3488
  const errorPayload = readCodexErrorPayload(error);
4148
- return formatCodexUsageLimitErrorMessage({
3489
+ const rateLimits = notificationRateLimits ?? errorPayload.rateLimits ?? readRecentCodexRateLimits();
3490
+ return {
4149
3491
  message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
4150
3492
  codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
4151
- rateLimits: readLatestRateLimitNotificationPayload(pendingNotifications) ?? errorPayload.rateLimits ?? readRecentCodexRateLimits()
4152
- });
3493
+ rateLimits,
3494
+ rateLimitsTrustedForProfile: notificationRateLimits !== void 0 || errorPayload.rateLimits !== void 0
3495
+ };
4153
3496
  }
4154
3497
  function readLatestRateLimitNotificationPayload(notifications) {
4155
3498
  for (let index = notifications.length - 1; index >= 0; index -= 1) {
@@ -4178,10 +3521,12 @@ function readCodexErrorPayload(error) {
4178
3521
  const data = error.data;
4179
3522
  if (!isJsonObject(data)) return { message };
4180
3523
  const nestedError = isJsonObject(data.error) ? data.error : data;
3524
+ const rateLimits = nestedError.rateLimits ?? data.rateLimits;
3525
+ if (rateLimits !== void 0) rememberCodexRateLimits(rateLimits);
4181
3526
  return {
4182
3527
  message: readString(nestedError, "message") ?? message,
4183
3528
  codexErrorInfo: nestedError.codexErrorInfo,
4184
- rateLimits: nestedError.rateLimits ?? data.rateLimits
3529
+ rateLimits
4185
3530
  };
4186
3531
  }
4187
3532
  function describeNotificationActivity(notification) {
@@ -4225,6 +3570,35 @@ function readNestedTurnId(record) {
4225
3570
  const turn = record.turn;
4226
3571
  return isJsonObject(turn) ? readString(turn, "id") : void 0;
4227
3572
  }
3573
+ const CODEX_TURN_ABORT_MARKER_START = "<turn_aborted>";
3574
+ const CODEX_TURN_ABORT_MARKER_END = "</turn_aborted>";
3575
+ const CODEX_INTERRUPTED_USER_GUIDANCE = "The user interrupted the previous turn on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.";
3576
+ const CODEX_INTERRUPTED_DEVELOPER_GUIDANCE = "The previous turn was interrupted on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.";
3577
+ function isCodexTurnAbortMarkerNotification(notification, options = {}) {
3578
+ if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
3579
+ const item = notification.params.item;
3580
+ const role = isJsonObject(item) ? readString(item, "role") : void 0;
3581
+ if (!isJsonObject(item) || role !== "user" && role !== "developer") return false;
3582
+ const text = extractRawResponseItemText(item).trim();
3583
+ if (role === "user" && text === options.currentPromptText?.trim()) return false;
3584
+ const markerBody = readCodexTurnAbortMarkerBody(text);
3585
+ return markerBody === CODEX_INTERRUPTED_USER_GUIDANCE || markerBody === CODEX_INTERRUPTED_DEVELOPER_GUIDANCE;
3586
+ }
3587
+ function readCodexTurnAbortMarkerBody(text) {
3588
+ if (!text.startsWith(CODEX_TURN_ABORT_MARKER_START) || !text.endsWith(CODEX_TURN_ABORT_MARKER_END)) return;
3589
+ return text.slice(14, -15).trim();
3590
+ }
3591
+ function extractRawResponseItemText(item) {
3592
+ const content = item.content;
3593
+ if (!Array.isArray(content)) return "";
3594
+ return content.flatMap((entry) => {
3595
+ if (!isJsonObject(entry)) return [];
3596
+ const type = readString(entry, "type");
3597
+ if (type !== "input_text" && type !== "text") return [];
3598
+ const text = readString(entry, "text");
3599
+ return text ? [text] : [];
3600
+ }).join("");
3601
+ }
4228
3602
  function readString(record, key) {
4229
3603
  const value = record[key];
4230
3604
  return typeof value === "string" ? value : void 0;
@@ -4238,9 +3612,9 @@ async function readMirroredSessionHistoryMessages(sessionFile) {
4238
3612
  if (!messages) embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", { sessionFile });
4239
3613
  return messages;
4240
3614
  }
4241
- async function buildCodexWorkspaceBootstrapInstructions(params) {
3615
+ async function buildCodexWorkspaceBootstrapContext(params) {
4242
3616
  try {
4243
- const { contextFiles } = await resolveBootstrapContextForRun({
3617
+ const bootstrapContext = await resolveBootstrapContextForRun({
4244
3618
  workspaceDir: params.resolvedWorkspace,
4245
3619
  config: params.params.config,
4246
3620
  sessionKey: params.sessionKey,
@@ -4250,16 +3624,118 @@ async function buildCodexWorkspaceBootstrapInstructions(params) {
4250
3624
  contextMode: params.params.bootstrapContextMode,
4251
3625
  runKind: params.params.bootstrapContextRunKind
4252
3626
  });
4253
- return renderCodexWorkspaceBootstrapInstructions(contextFiles.map((file) => remapCodexContextFilePath({
3627
+ const contextFiles = bootstrapContext.contextFiles.map((file) => remapCodexContextFilePath({
4254
3628
  file,
4255
3629
  sourceWorkspaceDir: params.resolvedWorkspace,
4256
3630
  targetWorkspaceDir: params.effectiveWorkspace
4257
- })));
3631
+ }));
3632
+ return {
3633
+ ...bootstrapContext,
3634
+ contextFiles,
3635
+ instructions: renderCodexWorkspaceBootstrapInstructions(contextFiles)
3636
+ };
4258
3637
  } catch (error) {
4259
3638
  embeddedAgentLog.warn("failed to load codex workspace bootstrap instructions", { error });
4260
- return;
3639
+ return {
3640
+ bootstrapFiles: [],
3641
+ contextFiles: []
3642
+ };
4261
3643
  }
4262
3644
  }
3645
+ function buildCodexSystemPromptReport(params) {
3646
+ const toolEntries = params.tools.map(buildCodexToolReportEntry);
3647
+ const schemaChars = toolEntries.reduce((sum, tool) => sum + tool.schemaChars, 0);
3648
+ const projectContextChars = params.workspaceBootstrapContext.instructions?.length ?? 0;
3649
+ const bootstrapMaxChars = readPositiveNumber(params.attempt.config?.agents?.defaults?.bootstrapMaxChars);
3650
+ const bootstrapTotalMaxChars = readPositiveNumber(params.attempt.config?.agents?.defaults?.bootstrapTotalMaxChars);
3651
+ return {
3652
+ source: "run",
3653
+ generatedAt: Date.now(),
3654
+ sessionId: params.attempt.sessionId,
3655
+ sessionKey: params.sessionKey,
3656
+ provider: params.attempt.provider,
3657
+ model: params.attempt.modelId,
3658
+ workspaceDir: params.workspaceDir,
3659
+ ...bootstrapMaxChars ? { bootstrapMaxChars } : {},
3660
+ ...bootstrapTotalMaxChars ? { bootstrapTotalMaxChars } : {},
3661
+ systemPrompt: {
3662
+ chars: params.developerInstructions.length,
3663
+ projectContextChars,
3664
+ nonProjectContextChars: Math.max(0, params.developerInstructions.length - projectContextChars)
3665
+ },
3666
+ injectedWorkspaceFiles: buildCodexBootstrapInjectionStats({
3667
+ bootstrapFiles: params.workspaceBootstrapContext.bootstrapFiles,
3668
+ injectedFiles: params.workspaceBootstrapContext.contextFiles
3669
+ }),
3670
+ skills: {
3671
+ promptChars: 0,
3672
+ entries: []
3673
+ },
3674
+ tools: {
3675
+ listChars: 0,
3676
+ schemaChars,
3677
+ entries: toolEntries
3678
+ }
3679
+ };
3680
+ }
3681
+ function buildCodexToolReportEntry(tool) {
3682
+ const summary = tool.description.trim();
3683
+ if (tool.deferLoading === true) return {
3684
+ name: tool.name,
3685
+ summaryChars: summary.length,
3686
+ schemaChars: 0,
3687
+ propertiesCount: null
3688
+ };
3689
+ return {
3690
+ name: tool.name,
3691
+ summaryChars: summary.length,
3692
+ ...buildCodexToolSchemaStats(tool.inputSchema)
3693
+ };
3694
+ }
3695
+ function buildCodexToolSchemaStats(schema) {
3696
+ const schemaChars = (() => {
3697
+ try {
3698
+ return JSON.stringify(schema).length;
3699
+ } catch {
3700
+ return 0;
3701
+ }
3702
+ })();
3703
+ const properties = isJsonObject(schema) && isJsonObject(schema.properties) ? schema.properties : null;
3704
+ return {
3705
+ schemaChars,
3706
+ propertiesCount: properties ? Object.keys(properties).length : null
3707
+ };
3708
+ }
3709
+ function buildCodexBootstrapInjectionStats(params) {
3710
+ const injectedByPath = /* @__PURE__ */ new Map();
3711
+ const injectedByBaseName = /* @__PURE__ */ new Map();
3712
+ for (const file of params.injectedFiles) {
3713
+ const pathValue = readNonEmptyString(file.path);
3714
+ if (!pathValue) continue;
3715
+ if (!injectedByPath.has(pathValue)) injectedByPath.set(pathValue, file.content);
3716
+ const baseName = path.posix.basename(pathValue.replaceAll("\\", "/"));
3717
+ if (!injectedByBaseName.has(baseName)) injectedByBaseName.set(baseName, file.content);
3718
+ }
3719
+ return params.bootstrapFiles.map((file) => {
3720
+ const pathValue = readNonEmptyString(file.path) ?? file.name;
3721
+ const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
3722
+ const injectedChars = (injectedByPath.get(pathValue) ?? injectedByPath.get(file.name) ?? injectedByBaseName.get(file.name))?.length ?? 0;
3723
+ return {
3724
+ name: file.name,
3725
+ path: pathValue,
3726
+ missing: file.missing,
3727
+ rawChars,
3728
+ injectedChars,
3729
+ truncated: !file.missing && injectedChars < rawChars
3730
+ };
3731
+ });
3732
+ }
3733
+ function readPositiveNumber(value) {
3734
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
3735
+ }
3736
+ function readNonEmptyString(value) {
3737
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
3738
+ }
4263
3739
  function renderCodexWorkspaceBootstrapInstructions(contextFiles) {
4264
3740
  const files = contextFiles.filter((file) => {
4265
3741
  const baseName = getCodexContextFileBasename(file.path);
@@ -4274,7 +3750,7 @@ function renderCodexWorkspaceBootstrapInstructions(contextFiles) {
4274
3750
  "",
4275
3751
  "The following project context files have been loaded:"
4276
3752
  ];
4277
- if (hasSoulFile) lines.push("If SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.");
3753
+ if (hasSoulFile) lines.push("SOUL.md: persona/tone. Follow it unless higher-priority instructions override.");
4278
3754
  lines.push("");
4279
3755
  for (const file of files) lines.push(`## ${file.path}`, "", file.content, "");
4280
3756
  return lines.join("\n").trim();
@@ -4321,9 +3797,28 @@ async function mirrorTranscriptBestEffort(params) {
4321
3797
  function isNonEmptyString(value) {
4322
3798
  return typeof value === "string" && value.length > 0;
4323
3799
  }
3800
+ function readCodexNotificationItem(params) {
3801
+ if (!isJsonObject(params) || !isJsonObject(params.item)) return;
3802
+ const item = params.item;
3803
+ return typeof item.id === "string" && typeof item.type === "string" ? item : void 0;
3804
+ }
3805
+ function codexExecutionToolName(item) {
3806
+ if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
3807
+ if (item.type === "mcpToolCall" && typeof item.tool === "string") {
3808
+ const server = typeof item.server === "string" && item.server ? item.server : void 0;
3809
+ return server ? `${server}.${item.tool}` : item.tool;
3810
+ }
3811
+ if (item.type === "commandExecution") return "bash";
3812
+ if (item.type === "fileChange") return "apply_patch";
3813
+ if (item.type === "webSearch") return "web_search";
3814
+ }
4324
3815
  function joinPresentSections(...sections) {
4325
3816
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
4326
3817
  }
3818
+ function prependCurrentTurnContext(prompt, context) {
3819
+ const text = context?.text.trim();
3820
+ return text ? [text, prompt].filter(Boolean).join("\n\n") : prompt;
3821
+ }
4327
3822
  function handleApprovalRequest(params) {
4328
3823
  return handleCodexAppServerApprovalRequest({
4329
3824
  method: params.method,