@openclaw/codex 2026.5.10-beta.3 → 2026.5.10-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,560 +1,28 @@
1
1
  import { l as resolveCodexPluginsPolicy, o as readCodexPluginConfig, s as resolveCodexAppServerRuntimeOptions, u as withMcpElicitationsApprovalPolicy } from "./config-C7xdbzrp.js";
2
- import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-CeCyJaWj.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
4
  import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-CpksBQ9l.js";
5
- import { d as formatCodexUsageLimitErrorMessage, f as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as formatCodexDisplayText, p as CODEX_CONTROL_METHODS } from "./request-Bj4IRDU9.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-BRX9tReb.js";
7
- import { i as readCodexAppServerBinding } from "./session-binding-BAaJ-7s-.js";
8
- import { i as defaultCodexAppInventoryCache, r as buildCodexAppInventoryCacheKey } from "./plugin-activation-Dn14PX1W.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-DRyCVHhL.js";
10
- import { n as defaultCodexAppServerClientFactory } from "./client-factory-BHbKz0nI.js";
11
- import { n as rememberCodexRateLimits, r as ensureCodexComputerUse, t as readRecentCodexRateLimits } from "./rate-limit-cache-BFTJlMhx.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";
24
+ import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "@earendil-works/pi-coding-agent";
22
25
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
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
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();
@@ -1303,6 +309,7 @@ var CodexAppServerEventProjector = class {
1303
309
  this.toolTranscriptResultIds = /* @__PURE__ */ new Set();
1304
310
  this.nativeGeneratedMediaUrls = /* @__PURE__ */ new Set();
1305
311
  this.diagnosticToolStartedAtByItem = /* @__PURE__ */ new Map();
312
+ this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
1306
313
  this.assistantStarted = false;
1307
314
  this.reasoningStarted = false;
1308
315
  this.reasoningEnded = false;
@@ -1459,6 +466,9 @@ var CodexAppServerEventProjector = class {
1459
466
  this.promptError = "codex app-server attempt timed out";
1460
467
  this.promptErrorSource = "prompt";
1461
468
  }
469
+ markAborted() {
470
+ this.aborted = true;
471
+ }
1462
472
  isCompacting() {
1463
473
  return this.activeCompactionItemIds.size > 0;
1464
474
  }
@@ -1708,6 +718,7 @@ var CodexAppServerEventProjector = class {
1708
718
  this.recordToolMeta(item);
1709
719
  this.recordNativeToolTranscriptCall(item);
1710
720
  this.recordNativeToolTranscriptResult(item);
721
+ this.emitAfterToolCallObservation(item);
1711
722
  this.emitToolResultSummary(item);
1712
723
  this.emitToolResultOutput(item);
1713
724
  }
@@ -1835,6 +846,7 @@ var CodexAppServerEventProjector = class {
1835
846
  } : {}
1836
847
  }
1837
848
  });
849
+ if (params.phase === "result") this.emitAfterToolCallObservation(item);
1838
850
  }
1839
851
  emitDiagnosticToolExecutionEvent(params) {
1840
852
  const base = {
@@ -1872,6 +884,37 @@ var CodexAppServerEventProjector = class {
1872
884
  ...terminalEvent
1873
885
  });
1874
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;
917
+ }
1875
918
  emitToolResultSummary(item) {
1876
919
  if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
1877
920
  const itemId = item.id;
@@ -2254,6 +1297,14 @@ function shouldSynthesizeToolProgressForItem(item) {
2254
1297
  default: return false;
2255
1298
  }
2256
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
+ }
2257
1308
  function shouldSuppressChannelProgressForItem(item) {
2258
1309
  if (shouldSynthesizeToolProgressForItem(item)) return true;
2259
1310
  return item.type === "dynamicToolCall";
@@ -2263,6 +1314,7 @@ function itemToolArgs(item) {
2263
1314
  command: item.command,
2264
1315
  ...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
2265
1316
  });
1317
+ if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChanges(item) });
2266
1318
  if (item.type === "webSearch" && typeof item.query === "string") return sanitizeCodexAgentEventRecord({ query: item.query });
2267
1319
  if (item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
2268
1320
  }
@@ -2274,10 +1326,7 @@ function itemToolResult(item) {
2274
1326
  }) };
2275
1327
  if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
2276
1328
  status: item.status,
2277
- changes: item.changes.map((change) => ({
2278
- path: change.path,
2279
- kind: change.kind
2280
- }))
1329
+ changes: itemFileChanges(item)
2281
1330
  }) };
2282
1331
  if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
2283
1332
  status: item.status,
@@ -2288,6 +1337,17 @@ function itemToolResult(item) {
2288
1337
  if (item.type === "webSearch") return { result: sanitizeCodexAgentEventRecord({ status: "completed" }) };
2289
1338
  return {};
2290
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
+ }
2291
1351
  function itemMeta(item, detailMode = "explain") {
2292
1352
  if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
2293
1353
  command: item.command,
@@ -2794,18 +1854,13 @@ function readRequestId(record) {
2794
1854
  return typeof value === "string" || typeof value === "number" ? value : void 0;
2795
1855
  }
2796
1856
  //#endregion
2797
- //#region extensions/codex/src/app-server/vision-tools.ts
2798
- function filterToolsForVisionInputs(tools, params) {
2799
- if (!params.modelHasVision || !params.hasInboundImages) return tools;
2800
- return tools.filter((tool) => tool.name !== "image");
2801
- }
2802
- //#endregion
2803
1857
  //#region extensions/codex/src/app-server/run-attempt.ts
2804
1858
  const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 3e4;
2805
1859
  const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
2806
1860
  const CODEX_DYNAMIC_IMAGE_TOOL_TIMEOUT_MS = 6e4;
2807
1861
  const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
2808
1862
  const CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS = 100;
1863
+ const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
2809
1864
  const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
2810
1865
  const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 6e4;
2811
1866
  const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 6e4;
@@ -3026,7 +2081,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
3026
2081
  config: params.config,
3027
2082
  agentId: params.agentId
3028
2083
  });
3029
- const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, sessionAgentId);
2084
+ const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, sessionAgentId);
3030
2085
  const startupBinding = await readCodexAppServerBinding(params.sessionFile);
3031
2086
  const startupAuthProfileCandidate = params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId;
3032
2087
  const startupAuthProfileId = params.authProfileStore ? resolveCodexAppServerAuthProfileId({
@@ -3491,6 +2546,36 @@ async function runCodexAppServerAttempt(params, options = {}) {
3491
2546
  });
3492
2547
  lifecycleTerminalEmitted = true;
3493
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
+ };
3494
2579
  const handleNotification = async (notification) => {
3495
2580
  userInputBridge?.handleNotification(notification);
3496
2581
  if (!projector || !turnId) {
@@ -3498,11 +2583,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
3498
2583
  return;
3499
2584
  }
3500
2585
  const isCurrentTurnNotification = isTurnNotification(notification.params, thread.threadId, turnId);
3501
- if (isCurrentTurnNotification) touchTurnCompletionActivity(`notification:${notification.method}`, { details: describeNotificationActivity(notification) });
2586
+ if (isCurrentTurnNotification) {
2587
+ touchTurnCompletionActivity(`notification:${notification.method}`, { details: describeNotificationActivity(notification) });
2588
+ reportCodexExecutionNotification(notification);
2589
+ }
3502
2590
  if (isCurrentTurnNotification && notification.method === "error") if (isRetryableErrorNotification(notification.params)) disarmTurnCompletionIdleWatch();
3503
2591
  else armTurnCompletionIdleWatch({ pinnedByTerminalError: true });
3504
2592
  else if (turnCompletionIdleWatchArmed && !turnCompletionIdleWatchPinnedByTerminalError && notification.method !== "turn/completed" && isCurrentTurnNotification) disarmTurnCompletionIdleWatch();
3505
2593
  const isTurnCompletion = notification.method === "turn/completed" && isCurrentTurnNotification;
2594
+ const isTurnAbortMarker = isCurrentTurnNotification && isCodexTurnAbortMarkerNotification(notification, { currentPromptText: promptBuild.prompt });
2595
+ const isTurnTerminal = isTurnCompletion || isTurnAbortMarker;
3506
2596
  try {
3507
2597
  await projector.handleNotification(notification);
3508
2598
  } catch (error) {
@@ -3511,7 +2601,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
3511
2601
  error
3512
2602
  });
3513
2603
  } finally {
3514
- if (isTurnCompletion) {
2604
+ if (isTurnTerminal) {
2605
+ if (isTurnAbortMarker) projector.markAborted();
3515
2606
  if (!timedOut && !runAbortController.signal.aborted) await steeringQueue?.flushPending();
3516
2607
  completed = true;
3517
2608
  clearTurnCompletionIdleTimer();
@@ -3584,6 +2675,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
3584
2675
  tool: call.tool,
3585
2676
  arguments: call.arguments
3586
2677
  });
2678
+ emitExecutionPhaseOnce(`tool:${call.callId}`, {
2679
+ phase: "tool_execution_started",
2680
+ tool: call.tool,
2681
+ toolCallId: call.callId
2682
+ });
3587
2683
  const toolMeta = inferCodexDynamicToolMeta(call, resolveCodexToolProgressDetailMode(params.toolProgressDetail));
3588
2684
  const toolArgs = sanitizeCodexToolArguments(call.arguments);
3589
2685
  emitCodexAppServerEvent(params, {
@@ -3658,10 +2754,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
3658
2754
  };
3659
2755
  const turnStartFailureMessages = [...historyMessages, {
3660
2756
  role: "user",
3661
- content: [{
3662
- type: "text",
3663
- text: promptBuild.prompt
3664
- }]
2757
+ content: promptBuild.prompt,
2758
+ timestamp: Date.now()
3665
2759
  }];
3666
2760
  let turn;
3667
2761
  try {
@@ -3693,7 +2787,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
3693
2787
  timeoutMs: appServer.requestTimeoutMs,
3694
2788
  signal: runAbortController.signal
3695
2789
  });
3696
- const turnStartErrorMessage = usageLimitError ?? formatErrorMessage(error);
2790
+ const turnStartErrorMessage = usageLimitError?.message ?? formatErrorMessage(error);
3697
2791
  emitCodexAppServerEvent(params, {
3698
2792
  stream: "codex_app_server.lifecycle",
3699
2793
  data: {
@@ -3743,11 +2837,24 @@ async function runCodexAppServerAttempt(params, options = {}) {
3743
2837
  }
3744
2838
  });
3745
2839
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
3746
- 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
+ }
3747
2853
  throw error;
3748
2854
  }
3749
2855
  turnId = turn.turn.id;
3750
2856
  const activeTurnId = turn.turn.id;
2857
+ emitExecutionPhaseOnce("turn_accepted", { phase: "turn_accepted" });
3751
2858
  userInputBridge = createCodexUserInputBridge({
3752
2859
  paramsForRun: params,
3753
2860
  threadId: thread.threadId,
@@ -3760,7 +2867,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
3760
2867
  prompt: promptBuild.prompt,
3761
2868
  imagesCount: params.images?.length ?? 0
3762
2869
  });
3763
- projector = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId);
2870
+ projector = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId, { nativePostToolUseRelayEnabled: nativeHookRelay?.allowedEvents.includes("post_tool_use") === true });
3764
2871
  emitLifecycleStart();
3765
2872
  const activeProjector = projector;
3766
2873
  for (const notification of pendingNotifications.splice(0)) await enqueueNotification(notification);
@@ -3797,9 +2904,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
3797
2904
  runAbortController.abort("timeout");
3798
2905
  }, Math.max(100, params.timeoutMs));
3799
2906
  const abortListener = () => {
2907
+ const shouldRetireClient = timedOut;
3800
2908
  interruptCodexTurnBestEffort(client, {
3801
2909
  threadId: thread.threadId,
3802
- 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")
3803
2917
  });
3804
2918
  resolveCompletion?.();
3805
2919
  };
@@ -3952,6 +3066,60 @@ async function runCodexAppServerAttempt(params, options = {}) {
3952
3066
  clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
3953
3067
  }
3954
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
+ }
3955
3123
  async function handleDynamicToolCallWithTimeout(params) {
3956
3124
  if (params.signal.aborted) return failedDynamicToolResponse("OpenClaw dynamic tool call aborted before execution.");
3957
3125
  const controller = new AbortController();
@@ -4079,8 +3247,31 @@ function buildCodexNativeHookRelayId(params) {
4079
3247
  return `codex-${hash.digest("hex").slice(0, 40)}`;
4080
3248
  }
4081
3249
  function interruptCodexTurnBestEffort(client, params) {
4082
- 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) {
4083
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
4084
3275
  });
4085
3276
  }
4086
3277
  function resolveOpenClawCodingToolsSessionKeys(params, sandboxSessionKey) {
@@ -4093,7 +3284,7 @@ async function buildDynamicTools(input) {
4093
3284
  const { params } = input;
4094
3285
  if (params.disableTools || !supportsModelTools(params.model)) return [];
4095
3286
  const modelHasVision = params.model.input?.includes("image") ?? false;
4096
- const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
3287
+ const agentDir = params.agentDir ?? resolveAgentDir$1(params.config ?? {}, input.sessionAgentId);
4097
3288
  const createOpenClawCodingTools = openClawCodingToolsFactoryForTests ?? (await import("openclaw/plugin-sdk/agent-harness")).createOpenClawCodingTools;
4098
3289
  const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
4099
3290
  const filteredTools = filterCodexDynamicToolsForAllowlist(filterToolsForVisionInputs(filterCodexDynamicTools(createOpenClawCodingTools({
@@ -4233,7 +3424,7 @@ function readDynamicToolCallParams(value) {
4233
3424
  return readCodexDynamicToolCallParams(value);
4234
3425
  }
4235
3426
  async function formatCodexTurnStartUsageLimitError(params) {
4236
- return refreshCodexUsageLimitErrorMessage({
3427
+ return refreshCodexUsageLimitError({
4237
3428
  client: params.client,
4238
3429
  source: readCodexTurnStartUsageLimitErrorSource(params.error, params.pendingNotifications),
4239
3430
  timeoutMs: params.timeoutMs,
@@ -4241,19 +3432,37 @@ async function formatCodexTurnStartUsageLimitError(params) {
4241
3432
  });
4242
3433
  }
4243
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) {
4244
3443
  const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
4245
- if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ?? void 0;
3444
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
3445
+ message: initialMessage,
3446
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
3447
+ } : void 0;
4246
3448
  const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
4247
3449
  client: params.client,
4248
3450
  timeoutMs: params.timeoutMs,
4249
3451
  signal: params.signal
4250
3452
  });
4251
- if (!rateLimits) return initialMessage;
4252
- return formatCodexUsageLimitErrorMessage({
3453
+ if (!rateLimits) return initialMessage ? {
3454
+ message: initialMessage,
3455
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
3456
+ } : void 0;
3457
+ const message = formatCodexUsageLimitErrorMessage({
4253
3458
  message: params.source.message,
4254
3459
  codexErrorInfo: params.source.codexErrorInfo,
4255
3460
  rateLimits
4256
3461
  }) ?? initialMessage;
3462
+ return message ? {
3463
+ message,
3464
+ rateLimitsForProfile: rateLimits
3465
+ } : void 0;
4257
3466
  }
4258
3467
  async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
4259
3468
  if (params.signal?.aborted) return;
@@ -4275,11 +3484,14 @@ function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
4275
3484
  }
4276
3485
  function readCodexTurnStartUsageLimitErrorSource(error, pendingNotifications) {
4277
3486
  const notificationError = readLatestCodexErrorNotification(pendingNotifications);
3487
+ const notificationRateLimits = readLatestRateLimitNotificationPayload(pendingNotifications);
4278
3488
  const errorPayload = readCodexErrorPayload(error);
3489
+ const rateLimits = notificationRateLimits ?? errorPayload.rateLimits ?? readRecentCodexRateLimits();
4279
3490
  return {
4280
3491
  message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
4281
3492
  codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
4282
- rateLimits: readLatestRateLimitNotificationPayload(pendingNotifications) ?? errorPayload.rateLimits ?? readRecentCodexRateLimits()
3493
+ rateLimits,
3494
+ rateLimitsTrustedForProfile: notificationRateLimits !== void 0 || errorPayload.rateLimits !== void 0
4283
3495
  };
4284
3496
  }
4285
3497
  function readLatestRateLimitNotificationPayload(notifications) {
@@ -4309,10 +3521,12 @@ function readCodexErrorPayload(error) {
4309
3521
  const data = error.data;
4310
3522
  if (!isJsonObject(data)) return { message };
4311
3523
  const nestedError = isJsonObject(data.error) ? data.error : data;
3524
+ const rateLimits = nestedError.rateLimits ?? data.rateLimits;
3525
+ if (rateLimits !== void 0) rememberCodexRateLimits(rateLimits);
4312
3526
  return {
4313
3527
  message: readString(nestedError, "message") ?? message,
4314
3528
  codexErrorInfo: nestedError.codexErrorInfo,
4315
- rateLimits: nestedError.rateLimits ?? data.rateLimits
3529
+ rateLimits
4316
3530
  };
4317
3531
  }
4318
3532
  function describeNotificationActivity(notification) {
@@ -4356,6 +3570,35 @@ function readNestedTurnId(record) {
4356
3570
  const turn = record.turn;
4357
3571
  return isJsonObject(turn) ? readString(turn, "id") : void 0;
4358
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
+ }
4359
3602
  function readString(record, key) {
4360
3603
  const value = record[key];
4361
3604
  return typeof value === "string" ? value : void 0;
@@ -4554,6 +3797,21 @@ async function mirrorTranscriptBestEffort(params) {
4554
3797
  function isNonEmptyString(value) {
4555
3798
  return typeof value === "string" && value.length > 0;
4556
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
+ }
4557
3815
  function joinPresentSections(...sections) {
4558
3816
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
4559
3817
  }