@openclaw/codex 2026.7.2-beta.1 → 2026.7.2-beta.3

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 (41) hide show
  1. package/README.md +1 -1
  2. package/dist/{app-server-policy-DVcxdse_.js → app-server-policy-DUMF2Xyr.js} +1 -1
  3. package/dist/{attempt-notifications-DEv9h7iC.js → attempt-notifications-Ecjyzufe.js} +3 -6
  4. package/dist/cli-metadata.js +1 -1
  5. package/dist/{command-formatters-D6ZlGNmb.js → command-formatters-NCTL7A2q.js} +38 -3
  6. package/dist/{command-handlers-Cpl9fUWv.js → command-handlers-DtFbZiba.js} +70 -15
  7. package/dist/{compact-FnJmiPNA.js → compact-Kgy26gIM.js} +4 -5
  8. package/dist/{computer-use-DVzr8OP1.js → computer-use-C7x4ZaqR.js} +4 -4
  9. package/dist/{conversation-control-DtQ-E7mr.js → conversation-control-B0s3aIjT.js} +5 -7
  10. package/dist/doctor-contract-api.js +1 -1
  11. package/dist/{dynamic-tools-BMLoaTeG.js → dynamic-tools-D_vSVmv9.js} +851 -434
  12. package/dist/harness.js +11 -6
  13. package/dist/index.js +52 -31
  14. package/dist/{media-understanding-provider-D580L0P8.js → media-understanding-provider-IPNNQre6.js} +11 -12
  15. package/dist/media-understanding-provider.js +1 -1
  16. package/dist/{models-DuKzZA6G.js → models-DD5zBdX5.js} +5 -9
  17. package/dist/{notification-correlation-DfaCm0mx.js → notification-correlation-C8DVQ_Bq.js} +3 -3
  18. package/dist/{plugin-app-cache-key-BKNjHMEs.js → plugin-app-cache-key-DJbhLYSK.js} +2 -2
  19. package/dist/{rate-limits-Dhp04Rqo.js → rate-limits-CqhWQ2R9.js} +9 -6
  20. package/dist/{request-DborTWgw.js → request-9yf9pZjf.js} +54 -8
  21. package/dist/{run-attempt-VPVJoYDP.js → run-attempt-BIoO8ISr.js} +637 -351
  22. package/dist/{config-c48K5HP9.js → session-binding-Bmt20HfB.js} +788 -3
  23. package/dist/{session-catalog-7H112Tr_.js → session-catalog-CA7R-hX2.js} +82 -45
  24. package/dist/{session-cli-B28RhCyp.js → session-cli-Bxk3-Wqz.js} +2 -2
  25. package/dist/{shared-client-D4mFI9al.js → shared-client-CSXLtCLE.js} +304 -81
  26. package/dist/{side-question-CgJBz52s.js → side-question-DtzquMOb.js} +15 -13
  27. package/dist/{thread-lifecycle-BgLXzjvV.js → thread-lifecycle-CQ_9mAa1.js} +1821 -1582
  28. package/dist/{protocol-validators-dZQ-UTOa.js → transcript-mirror-D9rTxx2P.js} +485 -2
  29. package/dist/usage-CuPpFgTc.js +25 -0
  30. package/dist/{web-search-provider.runtime-Bs-eTn5U.js → web-search-provider.runtime-_x5XTeqh.js} +3 -3
  31. package/npm-shrinkwrap.json +30 -30
  32. package/openclaw.plugin.json +21 -6
  33. package/package.json +6 -6
  34. package/dist/capabilities-42Dfn2TV.js +0 -33
  35. package/dist/prompt-overlay.js +0 -15
  36. package/dist/protocol-BMifTfdW.js +0 -15
  37. package/dist/provider-catalog.js +0 -118
  38. package/dist/provider-discovery.js +0 -34
  39. package/dist/provider.js +0 -307
  40. package/dist/session-binding-BMfX1OX-.js +0 -786
  41. package/dist/transcript-mirror-DhLwFIL4.js +0 -485
@@ -1,30 +1,30 @@
1
- import { i as isJsonObject, n as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE } from "./protocol-BMifTfdW.js";
2
- import { d as readCodexPluginConfig } from "./config-c48K5HP9.js";
3
- import { c as readCodexTurn } from "./protocol-validators-dZQ-UTOa.js";
4
- import { c as releaseLeasedSharedCodexAppServerClient } from "./shared-client-D4mFI9al.js";
5
- import { C as sanitizeInlineImageDataUrl, D as normalizeCodexDynamicToolName, E as isSystemAgentOnlyCodexDynamicToolAllowlist, S as sanitizeCodexHistoryImagePayloads, T as isForcedPrivateQaCodexRuntime, v as resolveCodexWebSearchPlan, w as filterCodexDynamicTools, x as invalidInlineImageText } from "./thread-lifecycle-BgLXzjvV.js";
6
- import { n as formatCodexUsageLimitErrorMessage } from "./rate-limits-Dhp04Rqo.js";
7
- import { c as attachCodexMirrorIdentity, s as promptSnapshot } from "./transcript-mirror-DhLwFIL4.js";
8
- import { r as formatCodexDisplayText } from "./command-formatters-D6ZlGNmb.js";
1
+ import { Y as CODEX_OPENCLAW_DIRECT_DYNAMIC_TOOL_NAMESPACE, Z as isJsonObject, _ as readCodexRateLimitsRevision, c as releaseLeasedSharedCodexAppServerClient, j as resolveCodexGatewayTimeoutWithGraceMs, v as readRecentCodexRateLimits, y as rememberCodexRateLimitsRead } from "./shared-client-CSXLtCLE.js";
2
+ import { y as readCodexPluginConfig } from "./session-binding-Bmt20HfB.js";
3
+ import { c as attachCodexMirrorIdentity, g as readCodexTurn, s as promptSnapshot } from "./transcript-mirror-D9rTxx2P.js";
4
+ import { C as isForcedPrivateQaCodexRuntime, S as filterCodexDynamicToolsWithOpenClawShell, T as normalizeCodexDynamicToolName, a as sanitizeCodexHistoryImagePayloads, d as resolveCodexWebSearchPlan, i as invalidInlineImageText, o as sanitizeInlineImageDataUrl, w as isSystemAgentOnlyCodexDynamicToolAllowlist, x as filterCodexDynamicTools } from "./thread-lifecycle-CQ_9mAa1.js";
5
+ import { d as CODEX_CONTROL_METHODS, r as formatCodexDisplayText } from "./command-formatters-NCTL7A2q.js";
6
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-CqhWQ2R9.js";
9
7
  import { a as resolveCodexNodeExecToolOverrides, i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-D1Wky__S.js";
10
- import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-DfaCm0mx.js";
8
+ import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-C8DVQ_Bq.js";
11
9
  import fs from "node:fs/promises";
12
- import { addTimerTimeoutGraceMs, asDateTimestampMs, finiteSecondsToTimerSafeMilliseconds, parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
10
+ import { addTimerTimeoutGraceMs, asDateTimestampMs, finiteSecondsToTimerSafeMilliseconds, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
13
11
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
14
12
  import { createHash } from "node:crypto";
15
- import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
13
+ import { markAuthProfileBlockedUntil, resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
16
14
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
17
15
  import { asOptionalRecord, isRecord, normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
18
16
  import { HEARTBEAT_RESPONSE_TOOL_NAME, TOOL_PROGRESS_OUTPUT_MAX_CHARS, buildAgentHookContextChannelFields, buildEmbeddedAttemptToolRunContext, callGatewayTool, classifyAgentHarnessTerminalOutcome, consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, emitAgentEvent, extractMessagingToolSend, extractMessagingToolSendResult, extractToolResultMediaArtifact, filterProviderNormalizableTools, filterToolResultMediaUrls, finalizeToolTerminalPresentation, formatApprovalDisplayPath, formatErrorMessage, formatToolAggregate, formatToolExecutionErrorMessage, formatToolProgressOutput, getBeforeToolCallFailureDisposition, getChannelAgentToolMeta, getPluginToolMeta, hasNativeHookRelayInvocation, inferToolMetaFromArgs, invokeNativeHookRelay, isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, isHostScopedAgentToolActive, isMessagingTool, isMessagingToolSendAction, isReplaySafeToolCall, isSubagentSessionKey, isToolResultError, isToolWrappedWithBeforeToolCallHook, normalizeAgentRuntimeTools, normalizeHeartbeatToolResponse, normalizeUsage, projectRuntimeToolInputSchema, registerNativeHookRelay, resolveAttemptSpawnWorkspaceDir, resolveModelAuthMode, resolveNativeHookRelayDeferredToolApproval, resolveToolExecutionErrorKind, resolveToolResultFailureKind, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessBeforeCompactionHook, runBeforeToolCallHook, sanitizeToolResult, setBeforeToolCallDiagnosticsEnabled, supportsModelTools, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
19
17
  import { listSessionEntries, parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
20
18
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
21
- import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
19
+ import { redactSensitiveFieldValue, redactSensitiveText, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
22
20
  import { emitTrustedDiagnosticEvent, hasPendingInternalDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
23
21
  import { readSessionTranscriptEvents } from "openclaw/plugin-sdk/session-transcript-runtime";
24
22
  import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
23
+ import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
25
24
  import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
26
25
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
27
26
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
27
+ import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
28
28
  //#region extensions/codex/src/app-server/local-runtime-attribution.ts
29
29
  const OPENAI_PROVIDER_ID = "openai";
30
30
  const OPENAI_RESPONSES_API = "openai-responses";
@@ -67,6 +67,7 @@ function createAssistantMessage(params, text, options) {
67
67
  output: options.tokenUsage.output ?? 0,
68
68
  cacheRead: options.tokenUsage.cacheRead ?? 0,
69
69
  cacheWrite: options.tokenUsage.cacheWrite ?? 0,
70
+ ...options.tokenUsage.contextUsage ? { contextUsage: options.tokenUsage.contextUsage } : {},
70
71
  totalTokens: options.tokenUsage.total ?? (options.tokenUsage.input ?? 0) + (options.tokenUsage.output ?? 0) + (options.tokenUsage.cacheRead ?? 0) + (options.tokenUsage.cacheWrite ?? 0),
71
72
  cost: ZERO_USAGE$1.cost
72
73
  } : ZERO_USAGE$1;
@@ -103,7 +104,7 @@ function createAssistantMirrorMessage(params, title, text) {
103
104
  }
104
105
  //#endregion
105
106
  //#region extensions/codex/src/app-server/event-projector-values.ts
106
- function readString$2(record, key) {
107
+ function readString$3(record, key) {
107
108
  const value = record[key];
108
109
  return typeof value === "string" ? value : void 0;
109
110
  }
@@ -139,15 +140,15 @@ function readNonNegativeInteger(record, key) {
139
140
  }
140
141
  function readCodexErrorNotificationMessage(record) {
141
142
  const error = record.error;
142
- return isJsonObject(error) ? readString$2(error, "message") : void 0;
143
+ return isJsonObject(error) ? readString$3(error, "message") : void 0;
143
144
  }
144
145
  function readHookOutputEntries(value) {
145
146
  if (!Array.isArray(value)) return [];
146
147
  return value.flatMap((entry) => {
147
148
  if (!isJsonObject(entry)) return [];
148
- const text = readString$2(entry, "text");
149
+ const text = readString$3(entry, "text");
149
150
  if (!text) return [];
150
- const kind = readString$2(entry, "kind");
151
+ const kind = readString$3(entry, "kind");
151
152
  return [{
152
153
  ...kind ? { kind } : {},
153
154
  text
@@ -160,9 +161,9 @@ function splitPlanText(text) {
160
161
  function extractRawAssistantText(item) {
161
162
  return (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
162
163
  if (!isJsonObject(entry)) return [];
163
- const type = readString$2(entry, "type");
164
+ const type = readString$3(entry, "type");
164
165
  if (type !== "output_text" && type !== "text") return [];
165
- const value = readString$2(entry, "text");
166
+ const value = readString$3(entry, "text");
166
167
  return value ? [value] : [];
167
168
  }).join("").trim() || void 0;
168
169
  }
@@ -178,6 +179,117 @@ function readItem(value) {
178
179
  return value;
179
180
  }
180
181
  //#endregion
182
+ //#region extensions/codex/src/app-server/event-projector-items.ts
183
+ function itemKind(item) {
184
+ switch (item.type) {
185
+ case "dynamicToolCall":
186
+ case "mcpToolCall": return "tool";
187
+ case "commandExecution": return "command";
188
+ case "fileChange": return "patch";
189
+ case "webSearch": return "search";
190
+ case "reasoning":
191
+ case "contextCompaction": return "analysis";
192
+ default: return;
193
+ }
194
+ }
195
+ function itemTitle(item) {
196
+ switch (item.type) {
197
+ case "commandExecution": return "Command";
198
+ case "fileChange": return "File change";
199
+ case "mcpToolCall": return "MCP tool";
200
+ case "dynamicToolCall": return "Tool";
201
+ case "webSearch": return "Web search";
202
+ case "contextCompaction": return "Context compaction";
203
+ case "reasoning": return "Reasoning";
204
+ default: return item.type;
205
+ }
206
+ }
207
+ function itemStatus(item) {
208
+ const status = readItemString(item, "status");
209
+ if (status === "failed" || status === "error") return "failed";
210
+ if (status === "declined") return "blocked";
211
+ if (status === "inProgress" || status === "in_progress" || status === "running") return "running";
212
+ return "completed";
213
+ }
214
+ function unknownItemStatus(item) {
215
+ const status = readItemString(item, "status");
216
+ switch (status) {
217
+ case void 0:
218
+ case "completed":
219
+ case "failed":
220
+ case "error":
221
+ case "declined":
222
+ case "inProgress":
223
+ case "in_progress":
224
+ case "running": return;
225
+ default: return status;
226
+ }
227
+ }
228
+ function auditNativeToolTerminalStatus(item) {
229
+ if (item.type === "imageView" || item.type === "sleep") return "completed";
230
+ const status = readItemString(item, "status");
231
+ if (status === "completed") return "completed";
232
+ if (status === "failed" || status === "error") return "failed";
233
+ if (status === "declined") return "blocked";
234
+ return "unknown";
235
+ }
236
+ function auditNativeToolUnfinishedStatus(item) {
237
+ return item.type === "webSearch" || item.type === "imageGeneration" ? "unknown" : "failed";
238
+ }
239
+ function isNonSuccessItemStatus(status) {
240
+ return status === "failed" || status === "blocked";
241
+ }
242
+ function itemName(item) {
243
+ if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
244
+ if (item.type === "mcpToolCall" && typeof item.tool === "string") {
245
+ const server = typeof item.server === "string" ? item.server : void 0;
246
+ return server ? `${server}.${item.tool}` : item.tool;
247
+ }
248
+ if (item.type === "commandExecution") return "bash";
249
+ if (item.type === "fileChange") return "apply_patch";
250
+ if (item.type === "webSearch") return "web_search";
251
+ }
252
+ function auditNativeToolName(item) {
253
+ if (item.type === "dynamicToolCall") return;
254
+ const progressName = itemName(item);
255
+ if (progressName) return progressName;
256
+ if (item.type === "collabAgentToolCall") return typeof item.tool === "string" && item.tool.trim() ? `collab.${item.tool.trim()}` : "collab_agent";
257
+ if (item.type === "imageGeneration") return "image_generation";
258
+ if (item.type === "imageView") return "image_view";
259
+ if (item.type === "sleep") return "sleep";
260
+ }
261
+ function isSideEffectingNativeToolItem(item) {
262
+ return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
263
+ }
264
+ function shouldSynthesizeToolProgressForItem(item) {
265
+ switch (item.type) {
266
+ case "commandExecution":
267
+ case "fileChange":
268
+ case "webSearch":
269
+ case "mcpToolCall": return true;
270
+ default: return false;
271
+ }
272
+ }
273
+ function shouldRecordNativeToolTranscript(item) {
274
+ return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
275
+ }
276
+ function isMutatingNativeToolItem(item) {
277
+ if (item.type === "commandExecution") return true;
278
+ return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
279
+ }
280
+ function shouldClearTerminalPresentationForNativeItem(item) {
281
+ switch (item.type) {
282
+ case "collabAgentToolCall":
283
+ case "commandExecution":
284
+ case "fileChange":
285
+ case "imageGeneration":
286
+ case "imageView":
287
+ case "mcpToolCall":
288
+ case "webSearch": return true;
289
+ default: return false;
290
+ }
291
+ }
292
+ //#endregion
181
293
  //#region extensions/codex/src/app-server/event-projector-assistant.ts
182
294
  var CodexAssistantProjection = class {
183
295
  constructor(params, emitAgentEvent, matchesToolProgressEcho) {
@@ -191,6 +303,7 @@ var CodexAssistantProjection = class {
191
303
  this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
192
304
  this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
193
305
  this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
306
+ this.lastAnswerCandidateEventByItem = /* @__PURE__ */ new Map();
194
307
  this.pendingRawCommentaryEchoes = 0;
195
308
  this.rawPromotedAssistantItemIds = /* @__PURE__ */ new Set();
196
309
  this.assistantStarted = false;
@@ -218,8 +331,8 @@ var CodexAssistantProjection = class {
218
331
  return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
219
332
  }
220
333
  async handleAssistantDelta(params) {
221
- const itemId = readString$2(params, "itemId") ?? "assistant";
222
- const delta = readString$2(params, "delta") ?? "";
334
+ const itemId = readString$3(params, "itemId") ?? "assistant";
335
+ const delta = readString$3(params, "delta") ?? "";
223
336
  if (!delta) return;
224
337
  if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
225
338
  const isCommentary = this.isCommentaryAssistantItem(itemId);
@@ -238,6 +351,7 @@ var CodexAssistantProjection = class {
238
351
  });
239
352
  return;
240
353
  }
354
+ if (this.isFinalAnswerAssistantItem(itemId)) this.emitAnswerCandidate(itemId, "candidate");
241
355
  const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
242
356
  const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
243
357
  if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
@@ -288,7 +402,7 @@ var CodexAssistantProjection = class {
288
402
  text: item.text
289
403
  });
290
404
  this.pendingRawCommentaryEchoes += 1;
291
- }
405
+ } else if (item.text && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
292
406
  }
293
407
  }
294
408
  recordSnapshotItem(item) {
@@ -299,9 +413,9 @@ var CodexAssistantProjection = class {
299
413
  }
300
414
  }
301
415
  handleRawResponseItemCompleted(item, activeItemIds) {
302
- const role = readString$2(item, "role");
303
- const phase = readString$2(item, "phase");
304
- const rawItemId = readString$2(item, "id");
416
+ const role = readString$3(item, "role");
417
+ const phase = readString$3(item, "phase");
418
+ const rawItemId = readString$3(item, "id");
305
419
  const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
306
420
  const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
307
421
  const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
@@ -342,6 +456,26 @@ var CodexAssistantProjection = class {
342
456
  const finalText = this.resolveFinalAssistantTextItem()?.text;
343
457
  return finalText ? [finalText] : [];
344
458
  }
459
+ finalizeAnswerCandidate(turn) {
460
+ if (turn.status !== "completed") {
461
+ this.supersedeVisibleAnswerCandidate();
462
+ return;
463
+ }
464
+ const turnItems = turn.items ?? [];
465
+ const authoritativeIndex = turnItems.findLastIndex((item) => item.type === "agentMessage" && readItemString(item, "phase") === "final_answer" && typeof item.text === "string" && item.text.trim().length > 0);
466
+ const authoritative = authoritativeIndex >= 0 ? turnItems[authoritativeIndex] : void 0;
467
+ if (turnItems.slice(authoritativeIndex + 1).some(shouldClearTerminalPresentationForNativeItem) || authoritative?.id === this.latestTerminalAssistantCandidateItemId && this.latestTerminalAssistantCandidateSuperseded) {
468
+ this.supersedeVisibleAnswerCandidate();
469
+ return;
470
+ }
471
+ const itemId = authoritative?.id ?? this.visibleAnswerCandidateItemId;
472
+ if (!itemId) return;
473
+ if (itemId !== this.visibleAnswerCandidateItemId) {
474
+ this.supersedeVisibleAnswerCandidate();
475
+ this.visibleAnswerCandidateItemId = itemId;
476
+ }
477
+ this.emitAnswerCandidate(itemId, "selected");
478
+ }
345
479
  hasAssistantItemTextForSynthesis() {
346
480
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
347
481
  const itemId = this.assistantItemOrder[i];
@@ -375,6 +509,9 @@ var CodexAssistantProjection = class {
375
509
  isCommentaryAssistantItem(itemId) {
376
510
  return this.assistantPhaseByItem.get(itemId) === "commentary";
377
511
  }
512
+ isFinalAnswerAssistantItem(itemId) {
513
+ return this.assistantPhaseByItem.get(itemId) === "final_answer";
514
+ }
378
515
  shouldStreamAssistantPartial(itemId) {
379
516
  return this.assistantPhaseByItem.get(itemId) === "final_answer";
380
517
  }
@@ -394,6 +531,36 @@ var CodexAssistantProjection = class {
394
531
  }
395
532
  });
396
533
  }
534
+ emitAnswerCandidate(itemId, status) {
535
+ const text = this.assistantTextByItem.get(itemId)?.trim();
536
+ if (!text) return;
537
+ if (status === "candidate" && this.visibleAnswerCandidateItemId !== itemId) {
538
+ this.supersedeVisibleAnswerCandidate();
539
+ this.visibleAnswerCandidateItemId = itemId;
540
+ }
541
+ const signature = `${status}\0${text}`;
542
+ if (this.lastAnswerCandidateEventByItem.get(itemId) === signature) return;
543
+ this.lastAnswerCandidateEventByItem.set(itemId, signature);
544
+ this.emitAgentEvent({
545
+ stream: "item",
546
+ data: {
547
+ itemId,
548
+ kind: "answer_candidate",
549
+ title: "Answer candidate",
550
+ phase: "update",
551
+ status,
552
+ progressText: text,
553
+ source: "codex-app-server",
554
+ hideFromChannelProgress: true
555
+ }
556
+ });
557
+ }
558
+ supersedeVisibleAnswerCandidate() {
559
+ const itemId = this.visibleAnswerCandidateItemId;
560
+ if (!itemId) return;
561
+ this.emitAnswerCandidate(itemId, "superseded");
562
+ this.visibleAnswerCandidateItemId = void 0;
563
+ }
397
564
  markLatestTerminalAssistantCandidate(itemId, activeItemIds, options) {
398
565
  this.latestTerminalAssistantCandidateItemId = itemId;
399
566
  this.latestTerminalAssistantCandidateSuperseded = false;
@@ -409,6 +576,7 @@ var CodexAssistantProjection = class {
409
576
  this.latestTerminalAssistantCandidateSuperseded = true;
410
577
  this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
411
578
  this.terminalAssistantCandidateEarlierActiveItemIds.clear();
579
+ this.supersedeVisibleAnswerCandidate();
412
580
  }
413
581
  resolveFinalAssistantTextItem() {
414
582
  for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
@@ -431,102 +599,52 @@ var CodexAssistantProjection = class {
431
599
  }
432
600
  };
433
601
  //#endregion
434
- //#region extensions/codex/src/app-server/event-projector-items.ts
435
- function itemKind(item) {
436
- switch (item.type) {
437
- case "dynamicToolCall":
438
- case "mcpToolCall": return "tool";
439
- case "commandExecution": return "command";
440
- case "fileChange": return "patch";
441
- case "webSearch": return "search";
442
- case "reasoning":
443
- case "contextCompaction": return "analysis";
444
- default: return;
445
- }
602
+ //#region extensions/codex/src/app-server/event-projector-diagnostics.ts
603
+ function redactCodexEventKind(method) {
604
+ return redactSensitiveText(sanitizeTerminalText(method));
446
605
  }
447
- function itemTitle(item) {
448
- switch (item.type) {
449
- case "commandExecution": return "Command";
450
- case "fileChange": return "File change";
451
- case "mcpToolCall": return "MCP tool";
452
- case "dynamicToolCall": return "Tool";
453
- case "webSearch": return "Web search";
454
- case "contextCompaction": return "Context compaction";
455
- case "reasoning": return "Reasoning";
456
- default: return item.type;
606
+ var CodexProjectionDiagnostics = class {
607
+ constructor(threadId, turnId) {
608
+ this.threadId = threadId;
609
+ this.turnId = turnId;
610
+ this.warningKeys = /* @__PURE__ */ new Set();
457
611
  }
458
- }
459
- function itemStatus(item) {
460
- const status = readItemString(item, "status");
461
- if (status === "failed" || status === "error") return "failed";
462
- if (status === "declined") return "blocked";
463
- if (status === "inProgress" || status === "in_progress" || status === "running") return "running";
464
- return "completed";
465
- }
466
- function auditNativeToolTerminalStatus(item) {
467
- if (item.type === "imageView" || item.type === "sleep") return "completed";
468
- const status = readItemString(item, "status");
469
- if (status === "completed") return "completed";
470
- if (status === "failed" || status === "error") return "failed";
471
- if (status === "declined") return "blocked";
472
- return "unknown";
473
- }
474
- function auditNativeToolUnfinishedStatus(item) {
475
- return item.type === "webSearch" || item.type === "imageGeneration" ? "unknown" : "failed";
476
- }
477
- function isNonSuccessItemStatus(status) {
478
- return status === "failed" || status === "blocked";
479
- }
480
- function itemName(item) {
481
- if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
482
- if (item.type === "mcpToolCall" && typeof item.tool === "string") {
483
- const server = typeof item.server === "string" ? item.server : void 0;
484
- return server ? `${server}.${item.tool}` : item.tool;
612
+ warnUnknownItemStatus(item) {
613
+ if (!item) return;
614
+ const status = unknownItemStatus(item);
615
+ if (!status) return;
616
+ const safeStatus = redactCodexEventKind(status);
617
+ const safeItemType = redactCodexEventKind(item.type);
618
+ this.warnOnce(JSON.stringify([
619
+ "status",
620
+ item.type,
621
+ status
622
+ ]), "codex app-server item reported unknown status; continuing projection", {
623
+ itemId: item.id,
624
+ itemType: safeItemType,
625
+ status: safeStatus
626
+ });
485
627
  }
486
- if (item.type === "commandExecution") return "bash";
487
- if (item.type === "fileChange") return "apply_patch";
488
- if (item.type === "webSearch") return "web_search";
489
- }
490
- function auditNativeToolName(item) {
491
- if (item.type === "dynamicToolCall") return;
492
- const progressName = itemName(item);
493
- if (progressName) return progressName;
494
- if (item.type === "collabAgentToolCall") return typeof item.tool === "string" && item.tool.trim() ? `collab.${item.tool.trim()}` : "collab_agent";
495
- if (item.type === "imageGeneration") return "image_generation";
496
- if (item.type === "imageView") return "image_view";
497
- if (item.type === "sleep") return "sleep";
498
- }
499
- function isSideEffectingNativeToolItem(item) {
500
- return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
501
- }
502
- function shouldSynthesizeToolProgressForItem(item) {
503
- switch (item.type) {
504
- case "commandExecution":
505
- case "fileChange":
506
- case "webSearch":
507
- case "mcpToolCall": return true;
508
- default: return false;
628
+ warnUnknownEvent(notification, params) {
629
+ const notificationThreadId = readCodexNotificationThreadId(params);
630
+ const notificationTurnId = readCodexNotificationTurnId(params);
631
+ const eventKind = redactCodexEventKind(notification.method);
632
+ this.warnOnce(JSON.stringify(["method", notification.method]), `codex app-server projector received unknown event kind; continuing: ${eventKind}`, {
633
+ eventKind,
634
+ activeThreadId: this.threadId,
635
+ activeTurnId: this.turnId,
636
+ threadId: notificationThreadId,
637
+ turnId: notificationTurnId,
638
+ matchesActiveThread: notificationThreadId === this.threadId,
639
+ matchesActiveTurn: notificationTurnId === this.turnId
640
+ });
509
641
  }
510
- }
511
- function shouldRecordNativeToolTranscript(item) {
512
- return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
513
- }
514
- function isMutatingNativeToolItem(item) {
515
- if (item.type === "commandExecution") return true;
516
- return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
517
- }
518
- function shouldClearTerminalPresentationForNativeItem(item) {
519
- switch (item.type) {
520
- case "collabAgentToolCall":
521
- case "commandExecution":
522
- case "fileChange":
523
- case "imageGeneration":
524
- case "imageView":
525
- case "mcpToolCall":
526
- case "webSearch": return true;
527
- default: return false;
642
+ warnOnce(key, message, context) {
643
+ if (this.warningKeys.has(key)) return;
644
+ this.warningKeys.add(key);
645
+ embeddedAgentLog.warn(message, context);
528
646
  }
529
- }
647
+ };
530
648
  //#endregion
531
649
  //#region extensions/codex/src/app-server/event-projector-tool-output.ts
532
650
  const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 1e4;
@@ -607,7 +725,7 @@ function collectDynamicToolContentText(contentItems) {
607
725
  if (!Array.isArray(contentItems)) return "";
608
726
  return contentItems.flatMap((entry) => {
609
727
  if (!isJsonObject(entry)) return [];
610
- const text = readString$2(entry, "text");
728
+ const text = readString$3(entry, "text");
611
729
  return text ? [text] : [];
612
730
  }).join("\n");
613
731
  }
@@ -956,7 +1074,29 @@ var CodexToolProgressProjection = class {
956
1074
  return this.sideEffectingNativeIds.size > 0 || this.sideEffectingDynamicIds.size > 0;
957
1075
  }
958
1076
  setLastToolError(error) {
959
- this.lastNativeToolError = error;
1077
+ if (!error) {
1078
+ this.lastNativeToolError = void 0;
1079
+ return;
1080
+ }
1081
+ const terminalResolution = this.params.observeToolTerminal?.({
1082
+ toolName: error.toolName,
1083
+ ...error.meta ? { meta: error.meta } : {},
1084
+ outcome: "failure",
1085
+ failure: {
1086
+ ...error.errorCode ? { errorCode: error.errorCode } : {},
1087
+ ...error.error ? { error: error.error } : {},
1088
+ ...error.validationErrorSummary ? { validationErrorSummary: error.validationErrorSummary } : {},
1089
+ ...error.timedOut ? { timedOut: true } : {},
1090
+ ...error.middlewareError ? { middlewareError: true } : {}
1091
+ },
1092
+ nativeMutation: {
1093
+ mutatingAction: error.mutatingAction === true,
1094
+ replaySafe: error.mutatingAction !== true,
1095
+ ...error.actionFingerprint ? { actionFingerprint: error.actionFingerprint } : {},
1096
+ ...error.fileTarget ? { fileTarget: error.fileTarget } : {}
1097
+ }
1098
+ });
1099
+ this.lastNativeToolError = terminalResolution?.lastToolError ?? (this.lastNativeToolError?.mutatingAction && error.mutatingAction !== true ? this.lastNativeToolError : error);
960
1100
  }
961
1101
  recordDynamicToolResult(params) {
962
1102
  const resultText = collectDynamicToolContentText(params.contentItems);
@@ -967,16 +1107,17 @@ var CodexToolProgressProjection = class {
967
1107
  ...params.asyncStarted === true ? { asyncStarted: true } : {},
968
1108
  ...!params.success ? { isError: true } : {}
969
1109
  });
970
- if (!params.success && params.terminalType === "blocked") this.lastNativeToolError = {
1110
+ if (params.terminalResolution) this.lastNativeToolError = params.terminalResolution.lastToolError;
1111
+ else if (!params.success) this.lastNativeToolError = {
971
1112
  toolName: params.tool,
972
- error: resultText || "codex dynamic tool blocked"
1113
+ error: resultText || (params.terminalType === "blocked" ? "codex dynamic tool blocked" : "codex dynamic tool failed")
973
1114
  };
974
- else if (params.success && this.lastNativeToolError && !this.lastNativeToolError.mutatingAction) this.lastNativeToolError = void 0;
1115
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
975
1116
  if (params.sideEffectEvidence === true) this.sideEffectingDynamicIds.add(params.callId);
976
1117
  }
977
1118
  handleOutputDelta(params, toolName) {
978
- const itemId = readString$2(params, "itemId");
979
- const delta = readString$2(params, "delta");
1119
+ const itemId = readString$3(params, "itemId");
1120
+ const delta = readString$3(params, "delta");
980
1121
  if (!itemId || !delta) return;
981
1122
  const storedOutput = this.output.append(itemId, delta);
982
1123
  this.rememberEcho(itemId, {
@@ -1017,25 +1158,37 @@ var CodexToolProgressProjection = class {
1017
1158
  });
1018
1159
  }
1019
1160
  recordNativeToolError(params) {
1020
- if (!isNonSuccessItemStatus(params.status)) {
1021
- if (!this.lastNativeToolError) return;
1022
- if (!this.lastNativeToolError.mutatingAction) {
1023
- this.lastNativeToolError = void 0;
1024
- return;
1161
+ const executionStarted = params.status !== "blocked";
1162
+ const mutatingAction = executionStarted && isMutatingNativeToolItem(params.item);
1163
+ const actionFingerprint = mutatingAction ? nativeToolActionFingerprint(params.item) : void 0;
1164
+ const isFailure = isNonSuccessItemStatus(params.status);
1165
+ const error = isFailure ? itemToolError(params.item, params.status, this.output.textByItem) : void 0;
1166
+ const terminalResolution = this.params.observeToolTerminal?.({
1167
+ toolCallId: params.item.id,
1168
+ toolName: params.name,
1169
+ arguments: itemToolArgs(params.item),
1170
+ ...params.meta ? { meta: params.meta } : {},
1171
+ executionStarted,
1172
+ outcome: isFailure ? "failure" : "success",
1173
+ ...isFailure ? { failure: error ? { error } : {} } : {},
1174
+ nativeMutation: {
1175
+ mutatingAction,
1176
+ replaySafe: !mutatingAction,
1177
+ ...actionFingerprint ? { actionFingerprint } : {}
1025
1178
  }
1026
- const actionFingerprint = nativeToolActionFingerprint(params.item);
1027
- if (this.lastNativeToolError.actionFingerprint && actionFingerprint && this.lastNativeToolError.actionFingerprint === actionFingerprint) this.lastNativeToolError = void 0;
1179
+ });
1180
+ if (terminalResolution) {
1181
+ this.lastNativeToolError = terminalResolution.lastToolError;
1028
1182
  return;
1029
1183
  }
1030
- const error = itemToolError(params.item, params.status, this.output.textByItem);
1031
- const actionFingerprint = nativeToolActionFingerprint(params.item);
1032
- this.lastNativeToolError = {
1184
+ if (isFailure) this.lastNativeToolError = {
1033
1185
  toolName: params.name,
1034
1186
  ...params.meta ? { meta: params.meta } : {},
1035
1187
  ...error ? { error } : {},
1036
- ...isMutatingNativeToolItem(params.item) ? { mutatingAction: true } : {},
1188
+ ...mutatingAction ? { mutatingAction: true } : {},
1037
1189
  ...actionFingerprint ? { actionFingerprint } : {}
1038
1190
  };
1191
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
1039
1192
  }
1040
1193
  emitToolResultSummary(item) {
1041
1194
  if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
@@ -1202,7 +1355,8 @@ async function readCodexMirroredSessionHistoryMessages(target) {
1202
1355
  const entries = await readCodexMirroredSessionEntries(target);
1203
1356
  if (entries.length === 0) return [];
1204
1357
  const firstEntry = entries[0];
1205
- if (firstEntry?.type !== "session" || typeof firstEntry.id !== "string") return;
1358
+ if (firstEntry?.type !== "session") return [];
1359
+ if (typeof firstEntry.id !== "string") return;
1206
1360
  migrateSessionEntries(entries);
1207
1361
  return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => {
1208
1362
  return entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.type !== "session";
@@ -1533,14 +1687,14 @@ var CodexEventProjection = class {
1533
1687
  data: {
1534
1688
  method,
1535
1689
  phase: method.endsWith("/started") ? "started" : "completed",
1536
- reviewId: readString$2(params, "reviewId"),
1690
+ reviewId: readString$3(params, "reviewId"),
1537
1691
  targetItemId: readNullableString(params, "targetItemId"),
1538
- decisionSource: readString$2(params, "decisionSource"),
1539
- status: review ? readString$2(review, "status") : void 0,
1540
- riskLevel: review ? readString$2(review, "riskLevel") : void 0,
1541
- userAuthorization: review ? readString$2(review, "userAuthorization") : void 0,
1692
+ decisionSource: readString$3(params, "decisionSource"),
1693
+ status: review ? readString$3(review, "status") : void 0,
1694
+ riskLevel: review ? readString$3(review, "riskLevel") : void 0,
1695
+ userAuthorization: review ? readString$3(review, "userAuthorization") : void 0,
1542
1696
  rationale: review ? readNullableString(review, "rationale") : void 0,
1543
- actionType: action ? readString$2(action, "type") : void 0
1697
+ actionType: action ? readString$3(action, "type") : void 0
1544
1698
  }
1545
1699
  });
1546
1700
  }
@@ -1549,7 +1703,7 @@ var CodexEventProjection = class {
1549
1703
  stream: "codex_app_server.guardian",
1550
1704
  data: {
1551
1705
  phase: "warning",
1552
- message: readString$2(params, "message")
1706
+ message: readString$3(params, "message")
1553
1707
  }
1554
1708
  });
1555
1709
  }
@@ -1565,14 +1719,14 @@ var CodexEventProjection = class {
1565
1719
  phase: method === "hook/started" ? "started" : "completed",
1566
1720
  threadId: this.threadId,
1567
1721
  turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
1568
- hookRunId: readString$2(run, "id"),
1569
- eventName: readString$2(run, "eventName"),
1570
- handlerType: readString$2(run, "handlerType"),
1571
- executionMode: readString$2(run, "executionMode"),
1572
- scope: readString$2(run, "scope"),
1573
- source: readString$2(run, "source"),
1574
- sourcePath: readString$2(run, "sourcePath"),
1575
- status: readString$2(run, "status"),
1722
+ hookRunId: readString$3(run, "id"),
1723
+ eventName: readString$3(run, "eventName"),
1724
+ handlerType: readString$3(run, "handlerType"),
1725
+ executionMode: readString$3(run, "executionMode"),
1726
+ scope: readString$3(run, "scope"),
1727
+ source: readString$3(run, "source"),
1728
+ sourcePath: readString$3(run, "sourcePath"),
1729
+ status: readString$3(run, "status"),
1576
1730
  statusMessage: readNullableString(run, "statusMessage"),
1577
1731
  ...durationMs !== void 0 ? { durationMs } : {},
1578
1732
  ...entries.length > 0 ? { entries } : {}
@@ -1673,10 +1827,10 @@ var CodexGeneratedMediaProjection = class {
1673
1827
  });
1674
1828
  }
1675
1829
  async recordRaw(item) {
1676
- if (readString$2(item, "type") !== "image_generation_call") return;
1677
- const result = readString$2(item, "result");
1830
+ if (readString$3(item, "type") !== "image_generation_call") return;
1831
+ const result = readString$3(item, "result");
1678
1832
  if (!result) return;
1679
- const itemId = readString$2(item, "id") ?? `raw-image-${this.itemIds.size}`;
1833
+ const itemId = readString$3(item, "id") ?? `raw-image-${this.itemIds.size}`;
1680
1834
  this.itemIds.add(itemId);
1681
1835
  const maxBytes = resolveGeneratedImageMaxBytes(this.config);
1682
1836
  const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
@@ -1691,7 +1845,7 @@ var CodexGeneratedMediaProjection = class {
1691
1845
  const asset = generatedImageAssetFromBase64({
1692
1846
  base64: result,
1693
1847
  index: this.itemIds.size,
1694
- revisedPrompt: readString$2(item, "revised_prompt") ?? readString$2(item, "revisedPrompt"),
1848
+ revisedPrompt: readString$3(item, "revised_prompt") ?? readString$3(item, "revisedPrompt"),
1695
1849
  fileNamePrefix: "codex-image-generation",
1696
1850
  sniffMimeType: true
1697
1851
  });
@@ -1749,6 +1903,90 @@ function resolveGeneratedImageMaxBytes(config) {
1749
1903
  return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
1750
1904
  }
1751
1905
  //#endregion
1906
+ //#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
1907
+ function withDynamicToolTerminalResolution(response, terminalResolution) {
1908
+ if (terminalResolution) {
1909
+ Object.defineProperties(response, {
1910
+ terminalResolution: {
1911
+ configurable: true,
1912
+ enumerable: false,
1913
+ value: terminalResolution
1914
+ },
1915
+ executionStarted: {
1916
+ configurable: true,
1917
+ enumerable: false,
1918
+ value: terminalResolution.executionStarted
1919
+ },
1920
+ ...terminalResolution.executedArguments ? { executedArguments: {
1921
+ configurable: true,
1922
+ enumerable: false,
1923
+ value: terminalResolution.executedArguments
1924
+ } } : {}
1925
+ });
1926
+ withDynamicToolSideEffectEvidence(response, terminalResolution.sideEffectEvidence);
1927
+ }
1928
+ return response;
1929
+ }
1930
+ function withDynamicToolExecutionState(response, state) {
1931
+ Object.defineProperties(response, {
1932
+ executedArguments: {
1933
+ configurable: true,
1934
+ enumerable: false,
1935
+ value: state.executedArguments
1936
+ },
1937
+ executionStarted: {
1938
+ configurable: true,
1939
+ enumerable: false,
1940
+ value: state.executionStarted
1941
+ }
1942
+ });
1943
+ return withDynamicToolSideEffectEvidence(response, state.sideEffectEvidence === true);
1944
+ }
1945
+ function withDynamicToolSideEffectEvidence(response, sideEffectEvidence) {
1946
+ if (!sideEffectEvidence) {
1947
+ delete response.sideEffectEvidence;
1948
+ return response;
1949
+ }
1950
+ Object.defineProperty(response, "sideEffectEvidence", {
1951
+ configurable: true,
1952
+ enumerable: false,
1953
+ value: true
1954
+ });
1955
+ return response;
1956
+ }
1957
+ function createFailedDynamicToolResponse(message, options) {
1958
+ const response = {
1959
+ contentItems: [{
1960
+ type: "inputText",
1961
+ text: message
1962
+ }],
1963
+ success: false
1964
+ };
1965
+ Object.defineProperties(response, {
1966
+ diagnosticTerminalReason: {
1967
+ configurable: true,
1968
+ enumerable: false,
1969
+ value: options?.terminalReason ?? "failed"
1970
+ },
1971
+ diagnosticTerminalType: {
1972
+ configurable: true,
1973
+ enumerable: false,
1974
+ value: "error"
1975
+ }
1976
+ });
1977
+ if (options?.executionStarted !== void 0) Object.defineProperty(response, "executionStarted", {
1978
+ configurable: true,
1979
+ enumerable: false,
1980
+ value: options.executionStarted
1981
+ });
1982
+ if (options?.executedArguments !== void 0) Object.defineProperty(response, "executedArguments", {
1983
+ configurable: true,
1984
+ enumerable: false,
1985
+ value: options.executedArguments
1986
+ });
1987
+ return withDynamicToolSideEffectEvidence(response, options?.sideEffectEvidence === true);
1988
+ }
1989
+ //#endregion
1752
1990
  //#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
1753
1991
  /** Leaf helper shared by native and dynamic tool diagnostics. */
1754
1992
  const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
@@ -1846,6 +2084,30 @@ function formatDynamicToolTimeoutDetails(params) {
1846
2084
  */
1847
2085
  async function handleDynamicToolCallWithTimeout(params) {
1848
2086
  let didNotifyAgentToolResult = false;
2087
+ const conservativeRaceResponses = /* @__PURE__ */ new WeakSet();
2088
+ const finalizeTerminal = (response) => {
2089
+ const executionSnapshot = params.toolBridge.consumeToolExecutionSnapshot?.(params.call.callId);
2090
+ const observedExecutionStarted = executionSnapshot?.executionStarted ?? (conservativeRaceResponses.has(response) ? void 0 : response.executionStarted);
2091
+ const terminalResolution = params.observeToolTerminal?.({
2092
+ toolCallId: params.call.callId,
2093
+ toolName: params.call.tool,
2094
+ arguments: response.executedArguments ?? executionSnapshot?.executedArguments ?? params.call.arguments,
2095
+ ...params.toolMeta ? { meta: params.toolMeta } : {},
2096
+ ...observedExecutionStarted !== void 0 ? { executionStarted: observedExecutionStarted } : {},
2097
+ outcome: response.success ? "success" : "failure",
2098
+ ...!response.success ? { failure: { error: readDynamicToolResponseText(response) } } : {}
2099
+ });
2100
+ return withDynamicToolTerminalResolution(response, terminalResolution);
2101
+ };
2102
+ const createFailedAfterPossibleDispatch = (message, terminalReason) => {
2103
+ const response = createFailedDynamicToolResponse(message, {
2104
+ executionStarted: true,
2105
+ sideEffectEvidence: true,
2106
+ terminalReason
2107
+ });
2108
+ conservativeRaceResponses.add(response);
2109
+ return response;
2110
+ };
1849
2111
  const notifyAgentToolResult = (event) => {
1850
2112
  if (didNotifyAgentToolResult) return;
1851
2113
  didNotifyAgentToolResult = true;
@@ -1876,7 +2138,10 @@ async function handleDynamicToolCallWithTimeout(params) {
1876
2138
  const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
1877
2139
  params.onFallbackSelected?.();
1878
2140
  notifyFailedToolResult(message, terminalReason);
1879
- return failedDynamicToolResponse(message, { terminalReason });
2141
+ return finalizeTerminal(createFailedDynamicToolResponse(message, {
2142
+ executionStarted: false,
2143
+ terminalReason
2144
+ }));
1880
2145
  }
1881
2146
  const controller = new AbortController();
1882
2147
  let timeout;
@@ -1888,10 +2153,7 @@ async function handleDynamicToolCallWithTimeout(params) {
1888
2153
  params.onFallbackSelected?.();
1889
2154
  controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
1890
2155
  notifyFailedToolResult(message, terminalReason);
1891
- resolveAbort?.(failedDynamicToolResponse(message, {
1892
- sideEffectEvidence: true,
1893
- terminalReason
1894
- }));
2156
+ resolveAbort?.(createFailedAfterPossibleDispatch(message, terminalReason));
1895
2157
  };
1896
2158
  const abortPromise = new Promise((resolve) => {
1897
2159
  resolveAbort = resolve;
@@ -1912,10 +2174,7 @@ async function handleDynamicToolCallWithTimeout(params) {
1912
2174
  consoleMessage: timeoutDetails.consoleMessage
1913
2175
  });
1914
2176
  notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
1915
- resolve(failedDynamicToolResponse(timeoutDetails.responseMessage, {
1916
- sideEffectEvidence: true,
1917
- terminalReason: "timed_out"
1918
- }));
2177
+ resolve(createFailedAfterPossibleDispatch(timeoutDetails.responseMessage, "timed_out"));
1919
2178
  }, timeoutMs);
1920
2179
  timeout.unref?.();
1921
2180
  });
@@ -1926,21 +2185,19 @@ async function handleDynamicToolCallWithTimeout(params) {
1926
2185
  params.toolBridge.handleToolCall(params.call, {
1927
2186
  signal: controller.signal,
1928
2187
  onAgentToolResult: notifyAgentToolResult,
1929
- toolCallOrdinal: params.toolCallOrdinal
2188
+ toolCallOrdinal: params.toolCallOrdinal,
2189
+ retainExecutionSnapshot: true
1930
2190
  }),
1931
2191
  abortPromise,
1932
2192
  timeoutPromise
1933
2193
  ]);
1934
2194
  if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
1935
- return response;
2195
+ return finalizeTerminal(response);
1936
2196
  } catch (error) {
1937
2197
  const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
1938
2198
  const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
1939
2199
  notifyFailedToolResult(message, terminalReason);
1940
- return failedDynamicToolResponse(message, {
1941
- sideEffectEvidence: true,
1942
- terminalReason
1943
- });
2200
+ return finalizeTerminal(createFailedAfterPossibleDispatch(message, terminalReason));
1944
2201
  } finally {
1945
2202
  if (timeout) clearTimeout(timeout);
1946
2203
  params.signal.removeEventListener("abort", abortFromRun);
@@ -1951,31 +2208,6 @@ async function handleDynamicToolCallWithTimeout(params) {
1951
2208
  function readDynamicToolResponseText(response) {
1952
2209
  return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
1953
2210
  }
1954
- function failedDynamicToolResponse(message, options) {
1955
- const response = {
1956
- contentItems: [{
1957
- type: "inputText",
1958
- text: message
1959
- }],
1960
- success: false
1961
- };
1962
- Object.defineProperty(response, "diagnosticTerminalType", {
1963
- configurable: true,
1964
- enumerable: false,
1965
- value: "error"
1966
- });
1967
- Object.defineProperty(response, "diagnosticTerminalReason", {
1968
- configurable: true,
1969
- enumerable: false,
1970
- value: options?.terminalReason ?? "failed"
1971
- });
1972
- if (options?.sideEffectEvidence === true) Object.defineProperty(response, "sideEffectEvidence", {
1973
- configurable: true,
1974
- enumerable: false,
1975
- value: true
1976
- });
1977
- return response;
1978
- }
1979
2211
  /** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
1980
2212
  function toCodexDynamicToolProtocolResponse(response) {
1981
2213
  return {
@@ -2181,6 +2413,7 @@ function createCodexNativeHookRelay(params) {
2181
2413
  runId: params.runId,
2182
2414
  ...params.channelId ? { channelId: params.channelId } : {},
2183
2415
  allowedEvents: params.events,
2416
+ preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
2184
2417
  ttlMs: resolveCodexNativeHookRelayTtlMs({
2185
2418
  explicitTtlMs: params.options?.ttlMs,
2186
2419
  attemptTimeoutMs: params.attemptTimeoutMs,
@@ -2239,7 +2472,8 @@ function buildCodexNativeHookRelayConfig(params) {
2239
2472
  const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
2240
2473
  const selected = selectedEvents.has(event);
2241
2474
  const shouldRelay = params.relay.shouldRelayEvent(event);
2242
- if (!selected || !shouldRelay && !(selected && event === "pre_tool_use" && !shouldRelay)) {
2475
+ const selectedNoopPreToolUse = selected && event === "pre_tool_use" && !shouldRelay && params.loopDetectionPreToolUseRelay;
2476
+ if (!selected || !shouldRelay && !selectedNoopPreToolUse) {
2243
2477
  if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
2244
2478
  if (params.clearOmittedEvents) for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = { enabled: false };
2245
2479
  continue;
@@ -2382,12 +2616,12 @@ var CodexNativeToolLifecycleProjector = class {
2382
2616
  this.preToolUseFailureByItem.set(failure.toolCallId, record);
2383
2617
  }
2384
2618
  recordRawWebSearchResult(item) {
2385
- if (readString$2(item, "type") !== "web_search_call") return;
2386
- const toolCallId = readString$2(item, "id");
2619
+ if (readString$3(item, "type") !== "web_search_call") return;
2620
+ const toolCallId = readString$3(item, "id");
2387
2621
  if (!toolCallId || this.completedItemIds.has(toolCallId)) return;
2388
2622
  const toolName = "web_search";
2389
2623
  this.recordStarted(toolCallId, toolName, "unknown");
2390
- const rawStatus = readString$2(item, "status");
2624
+ const rawStatus = readString$3(item, "status");
2391
2625
  if (rawStatus === "in_progress" || rawStatus === "running") return;
2392
2626
  const status = rawStatus === "completed" ? "completed" : rawStatus === "cancelled" ? "cancelled" : rawStatus === "failed" || rawStatus === "error" || rawStatus === "incomplete" ? "failed" : "unknown";
2393
2627
  this.recordTerminal(toolCallId, toolName, status, { sourceTimestampMs: this.webSearchCompletionByItem.get(toolCallId)?.sourceTimestampMs });
@@ -2512,8 +2746,8 @@ var CodexReasoningProjection = class {
2512
2746
  this.reasoningEnded = false;
2513
2747
  }
2514
2748
  async handleReasoningDelta(method, params) {
2515
- const itemId = readString$2(params, "itemId") ?? "reasoning";
2516
- const delta = readString$2(params, "delta") ?? "";
2749
+ const itemId = readString$3(params, "itemId") ?? "reasoning";
2750
+ const delta = readString$3(params, "delta") ?? "";
2517
2751
  if (!delta) return;
2518
2752
  this.reasoningStarted = true;
2519
2753
  if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
@@ -2532,27 +2766,35 @@ var CodexReasoningProjection = class {
2532
2766
  });
2533
2767
  }
2534
2768
  handlePlanDelta(params) {
2535
- const itemId = readString$2(params, "itemId") ?? "plan";
2536
- const delta = readString$2(params, "delta") ?? "";
2769
+ const itemId = readString$3(params, "itemId") ?? "plan";
2770
+ const delta = readString$3(params, "delta") ?? "";
2537
2771
  if (!delta) return;
2538
2772
  const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
2539
2773
  this.planTextByItem.set(itemId, text);
2540
2774
  this.emitPlanUpdate({
2541
2775
  explanation: void 0,
2542
- steps: splitPlanText(text)
2776
+ steps: splitPlanText(text).map((step) => ({
2777
+ step,
2778
+ status: "pending"
2779
+ }))
2543
2780
  });
2544
2781
  }
2545
2782
  handleTurnPlanUpdated(params) {
2783
+ const explanation = readNullableString(params, "explanation");
2546
2784
  const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
2547
2785
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
2548
2786
  const record = entry;
2549
- const step = readString$2(record, "step");
2550
- const status = readString$2(record, "status");
2787
+ const step = readString$3(record, "step");
2551
2788
  if (!step) return [];
2552
- return status ? [`${step} (${status})`] : [step];
2789
+ return [{
2790
+ step,
2791
+ status: normalizePlanStepStatus(readString$3(record, "status"))
2792
+ }];
2553
2793
  }) : void 0;
2794
+ const planText = [explanation, ...(plan ?? []).map(({ step, status }) => `- [${status}] ${step}`)].filter((part) => Boolean(part)).join("\n");
2795
+ if (planText) this.turnPlanText = planText;
2554
2796
  this.emitPlanUpdate({
2555
- explanation: readNullableString(params, "explanation"),
2797
+ explanation,
2556
2798
  steps: plan
2557
2799
  });
2558
2800
  }
@@ -2561,7 +2803,10 @@ var CodexReasoningProjection = class {
2561
2803
  this.planTextByItem.set(item.id, item.text);
2562
2804
  this.emitPlanUpdate({
2563
2805
  explanation: void 0,
2564
- steps: splitPlanText(item.text)
2806
+ steps: splitPlanText(item.text).map((step) => ({
2807
+ step,
2808
+ status: "pending"
2809
+ }))
2565
2810
  });
2566
2811
  }
2567
2812
  }
@@ -2574,7 +2819,7 @@ var CodexReasoningProjection = class {
2574
2819
  return collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
2575
2820
  }
2576
2821
  planText() {
2577
- return [...this.planTextByItem.values()].filter((text) => text.trim().length > 0).join("\n\n");
2822
+ return this.turnPlanText ?? [...this.planTextByItem.values()].filter((text) => text.trim().length > 0).join("\n\n");
2578
2823
  }
2579
2824
  emitPlanUpdate(params) {
2580
2825
  if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
@@ -2590,6 +2835,10 @@ var CodexReasoningProjection = class {
2590
2835
  });
2591
2836
  }
2592
2837
  };
2838
+ function normalizePlanStepStatus(status) {
2839
+ if (status === "inProgress" || status === "in_progress") return "in_progress";
2840
+ return status === "completed" ? "completed" : "pending";
2841
+ }
2593
2842
  function collectReasoningTextValues(groups, itemOrder) {
2594
2843
  return [...groups.values()].toSorted((left, right) => {
2595
2844
  const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
@@ -2603,15 +2852,193 @@ function reasoningMethodOrder(method) {
2603
2852
  }
2604
2853
  //#endregion
2605
2854
  //#region extensions/codex/src/app-server/event-projector-usage.ts
2606
- function normalizeCodexTokenUsage(record) {
2855
+ function readTokenCount(record, key) {
2856
+ const value = readNonNegativeInteger(record, key);
2857
+ return value !== void 0 && Number.isSafeInteger(value) ? value : void 0;
2858
+ }
2859
+ function readCodexThreadTokenUsage(params) {
2860
+ const tokenUsage = isJsonObject(params.tokenUsage) ? params.tokenUsage : void 0;
2861
+ const last = tokenUsage && isJsonObject(tokenUsage.last) ? tokenUsage.last : void 0;
2862
+ return last ? normalizeCodexThreadTokenUsage(last) : void 0;
2863
+ }
2864
+ function normalizeCodexThreadTokenUsage(record) {
2607
2865
  const inputTokens = readNumber(record, "inputTokens");
2608
2866
  const cacheRead = readNumber(record, "cachedInputTokens");
2609
- return normalizeUsage({
2867
+ const usage = normalizeUsage({
2610
2868
  input: inputTokens !== void 0 && cacheRead !== void 0 ? Math.max(0, inputTokens - cacheRead) : inputTokens,
2611
2869
  output: readNumber(record, "outputTokens"),
2612
2870
  cacheRead,
2613
2871
  total: readNumber(record, "totalTokens")
2614
2872
  });
2873
+ return usage ? {
2874
+ ...usage,
2875
+ contextUsage: { state: "unavailable" }
2876
+ } : void 0;
2877
+ }
2878
+ function normalizeCodexResponseTokenUsage(record) {
2879
+ const totalTokens = readTokenCount(record, "totalTokens");
2880
+ const inputTokens = readTokenCount(record, "inputTokens");
2881
+ const cacheRead = readTokenCount(record, "cachedInputTokens");
2882
+ const output = readTokenCount(record, "outputTokens");
2883
+ const reasoningOutput = readTokenCount(record, "reasoningOutputTokens");
2884
+ const cacheWrite = record.cacheWriteInputTokens === void 0 ? 0 : readTokenCount(record, "cacheWriteInputTokens");
2885
+ if (totalTokens === void 0 || inputTokens === void 0 || cacheRead === void 0 || cacheWrite === void 0 || output === void 0 || reasoningOutput === void 0 || cacheRead + cacheWrite > inputTokens || totalTokens !== inputTokens + output) return;
2886
+ const usage = normalizeUsage({
2887
+ input: inputTokens - cacheRead - cacheWrite,
2888
+ output,
2889
+ cacheRead,
2890
+ cacheWrite,
2891
+ total: totalTokens
2892
+ });
2893
+ if (!usage) return;
2894
+ return {
2895
+ ...usage,
2896
+ contextUsage: {
2897
+ state: "available",
2898
+ promptTokens: inputTokens,
2899
+ totalTokens
2900
+ }
2901
+ };
2902
+ }
2903
+ //#endregion
2904
+ //#region extensions/codex/src/app-server/usage-limit-error.ts
2905
+ /**
2906
+ * Enriches Codex usage-limit failures with current rate-limit information and
2907
+ * marks blocked auth profiles when Codex exposes a reset time.
2908
+ */
2909
+ const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
2910
+ function createCodexUsageLimitPromptError(message) {
2911
+ return Object.assign(new Error(message), { status: 429 });
2912
+ }
2913
+ function isCodexUsageLimitPromptError(error) {
2914
+ return error instanceof Error && "status" in error && error.status === 429;
2915
+ }
2916
+ /** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
2917
+ async function markCodexAuthProfileBlockedFromRateLimits(params) {
2918
+ const authProfileId = params.authProfileId?.trim();
2919
+ if (!authProfileId || !params.params.authProfileStore) return;
2920
+ const blockedUntil = resolveCodexUsageLimitResetAtMs(params.rateLimits);
2921
+ if (!blockedUntil) return;
2922
+ try {
2923
+ await markAuthProfileBlockedUntil({
2924
+ store: params.params.authProfileStore,
2925
+ profileId: authProfileId,
2926
+ blockedUntil,
2927
+ source: "codex_rate_limits",
2928
+ agentDir: params.params.agentDir,
2929
+ runId: params.params.runId,
2930
+ modelId: params.params.modelId
2931
+ });
2932
+ } catch (error) {
2933
+ embeddedAgentLog.debug("failed to mark Codex auth profile blocked from app-server limits", {
2934
+ authProfileId,
2935
+ error: formatErrorMessage(error)
2936
+ });
2937
+ }
2938
+ }
2939
+ /** Formats a turn-start usage-limit error, refreshing rate limits when needed. */
2940
+ async function formatCodexTurnStartUsageLimitError(params) {
2941
+ return refreshCodexUsageLimitError({
2942
+ client: params.client,
2943
+ source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
2944
+ timeoutMs: params.timeoutMs,
2945
+ signal: params.signal
2946
+ });
2947
+ }
2948
+ /** Refreshes a generic prompt usage-limit message into a reset-aware message. */
2949
+ async function refreshCodexUsageLimitPromptError(params) {
2950
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(params.message)) return;
2951
+ return refreshCodexUsageLimitError({
2952
+ client: params.client,
2953
+ source: {
2954
+ message: params.message,
2955
+ codexErrorInfo: "usageLimitExceeded",
2956
+ rateLimits: readRecentCodexRateLimits(params.client)
2957
+ },
2958
+ timeoutMs: params.timeoutMs,
2959
+ signal: params.signal
2960
+ });
2961
+ }
2962
+ async function refreshCodexUsageLimitError(params) {
2963
+ const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
2964
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
2965
+ message: initialMessage,
2966
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
2967
+ } : void 0;
2968
+ const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
2969
+ client: params.client,
2970
+ timeoutMs: params.timeoutMs,
2971
+ signal: params.signal
2972
+ });
2973
+ if (!rateLimits) return initialMessage ? {
2974
+ message: initialMessage,
2975
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
2976
+ } : void 0;
2977
+ const message = formatCodexUsageLimitErrorMessage({
2978
+ message: params.source.message,
2979
+ codexErrorInfo: params.source.codexErrorInfo,
2980
+ rateLimits,
2981
+ rateLimitsAuthoritative: true
2982
+ }) ?? initialMessage;
2983
+ return message ? {
2984
+ message,
2985
+ rateLimitsForProfile: rateLimits
2986
+ } : void 0;
2987
+ }
2988
+ async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
2989
+ if (params.signal?.aborted) return;
2990
+ try {
2991
+ const rateLimits = await params.client.request(CODEX_CONTROL_METHODS.rateLimits, void 0, {
2992
+ timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
2993
+ signal: params.signal
2994
+ });
2995
+ rememberCodexRateLimitsRead(params.client, rateLimits);
2996
+ return rateLimits;
2997
+ } catch (error) {
2998
+ embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
2999
+ return;
3000
+ }
3001
+ }
3002
+ function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
3003
+ if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
3004
+ return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
3005
+ }
3006
+ function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
3007
+ const notificationError = readCodexErrorNotification(errorNotification);
3008
+ const errorPayload = readCodexErrorPayload(error);
3009
+ const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
3010
+ const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
3011
+ return {
3012
+ message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
3013
+ codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
3014
+ rateLimits,
3015
+ rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
3016
+ };
3017
+ }
3018
+ function readCodexErrorNotification(notification) {
3019
+ if (notification?.method !== "error" || !isJsonObject(notification.params)) return;
3020
+ const error = notification.params.error;
3021
+ return isJsonObject(error) ? {
3022
+ message: readString$2(error, "message"),
3023
+ codexErrorInfo: error.codexErrorInfo
3024
+ } : void 0;
3025
+ }
3026
+ function readCodexErrorPayload(error) {
3027
+ const message = error instanceof Error ? error.message : void 0;
3028
+ if (!error || typeof error !== "object" || !("data" in error)) return { message };
3029
+ const data = error.data;
3030
+ if (!isJsonObject(data)) return { message };
3031
+ const nestedError = isJsonObject(data.error) ? data.error : data;
3032
+ const rateLimits = nestedError.rateLimits ?? data.rateLimits;
3033
+ return {
3034
+ message: readString$2(nestedError, "message") ?? message,
3035
+ codexErrorInfo: nestedError.codexErrorInfo,
3036
+ rateLimits
3037
+ };
3038
+ }
3039
+ function readString$2(record, key) {
3040
+ const value = record[key];
3041
+ return typeof value === "string" ? value : void 0;
2615
3042
  }
2616
3043
  //#endregion
2617
3044
  //#region extensions/codex/src/app-server/event-projector.ts
@@ -2630,6 +3057,7 @@ var CodexAppServerEventProjector = class {
2630
3057
  this.synthesizedMissingToolResultError = null;
2631
3058
  this.aborted = false;
2632
3059
  this.completedCompactionCount = 0;
3060
+ this.diagnostics = new CodexProjectionDiagnostics(threadId, turnId);
2633
3061
  this.nativeToolLifecycleProjector = new CodexNativeToolLifecycleProjector(params, threadId, turnId, { runAbortSignal: options.runAbortSignal });
2634
3062
  this.generatedMediaProjection = new CodexGeneratedMediaProjection(params.config);
2635
3063
  this.toolProgressProjection = new CodexToolProgressProjection(params);
@@ -2720,20 +3148,38 @@ var CodexAppServerEventProjector = class {
2720
3148
  this.eventProjection.handleHook(notification.method, params);
2721
3149
  break;
2722
3150
  case "thread/tokenUsage/updated":
2723
- this.handleTokenUsage(params);
3151
+ this.tokenUsage = readCodexThreadTokenUsage(params) ?? this.tokenUsage;
2724
3152
  break;
2725
3153
  case "turn/completed":
2726
3154
  await this.handleTurnCompleted(params);
2727
3155
  break;
3156
+ case "rawResponse/completed":
3157
+ this.handleRawResponseCompleted(params);
3158
+ break;
2728
3159
  case "rawResponseItem/completed":
2729
3160
  await this.handleRawResponseItemCompleted(params);
2730
3161
  break;
2731
3162
  case "error":
3163
+ this.responseUsage = void 0;
2732
3164
  if (params.willRetry === true) break;
2733
3165
  this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
2734
3166
  this.promptErrorSource = "prompt";
2735
3167
  break;
2736
- default: break;
3168
+ case "thread/compacted":
3169
+ case "turn/started":
3170
+ case "turn/diff/updated":
3171
+ case "item/reasoning/summaryPartAdded":
3172
+ case "item/commandExecution/terminalInteraction":
3173
+ case "item/fileChange/outputDelta":
3174
+ case "item/fileChange/patchUpdated":
3175
+ case "item/mcpToolCall/progress":
3176
+ case "model/rerouted":
3177
+ case "model/verification":
3178
+ case "turn/moderationMetadata":
3179
+ case "model/safetyBuffering/updated": break;
3180
+ default:
3181
+ this.diagnostics.warnUnknownEvent(notification, params);
3182
+ break;
2737
3183
  }
2738
3184
  }
2739
3185
  buildResult(toolTelemetry, options) {
@@ -2741,6 +3187,7 @@ var CodexAppServerEventProjector = class {
2741
3187
  const assistantTexts = this.assistantProjection.collectAssistantTexts();
2742
3188
  const reasoningText = this.reasoningProjection.reasoningText();
2743
3189
  const planText = this.reasoningProjection.planText();
3190
+ const projectedUsage = this.aborted ? this.tokenUsage : this.responseUsage ?? this.tokenUsage;
2744
3191
  const hasAssistantItemText = this.assistantProjection.hasAssistantItemTextForSynthesis();
2745
3192
  const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
2746
3193
  const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
@@ -2753,7 +3200,7 @@ var CodexAppServerEventProjector = class {
2753
3200
  this.promptErrorSource = this.promptErrorSource ?? "prompt";
2754
3201
  }
2755
3202
  const assistantMessageOptions = {
2756
- tokenUsage: this.tokenUsage,
3203
+ tokenUsage: projectedUsage,
2757
3204
  aborted: this.aborted,
2758
3205
  promptError: this.promptError
2759
3206
  };
@@ -2806,7 +3253,8 @@ var CodexAppServerEventProjector = class {
2806
3253
  toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
2807
3254
  successfulCronAdds: toolTelemetry.successfulCronAdds,
2808
3255
  cloudCodeAssistFormatError: false,
2809
- attemptUsage: this.tokenUsage,
3256
+ attemptUsage: projectedUsage,
3257
+ ...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {},
2810
3258
  replayMetadata: {
2811
3259
  hadPotentialSideEffects,
2812
3260
  replaySafe: !hadPotentialSideEffects
@@ -2814,8 +3262,7 @@ var CodexAppServerEventProjector = class {
2814
3262
  itemLifecycle: {
2815
3263
  startedCount: this.activeItemIds.size + this.completedItemIds.size,
2816
3264
  completedCount: this.completedItemIds.size,
2817
- activeCount: this.activeItemIds.size,
2818
- ...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {}
3265
+ activeCount: this.activeItemIds.size
2819
3266
  },
2820
3267
  yieldDetected: options?.yieldDetected || false,
2821
3268
  didSendDeterministicApprovalPrompt: this.eventProjection.guardianReviewCount > 0 ? false : void 0
@@ -2835,13 +3282,14 @@ var CodexAppServerEventProjector = class {
2835
3282
  }
2836
3283
  markAborted() {
2837
3284
  this.aborted = true;
3285
+ this.responseUsage = void 0;
2838
3286
  }
2839
3287
  isCompacting() {
2840
3288
  return this.activeCompactionItemIds.size > 0;
2841
3289
  }
2842
3290
  async handleItemStarted(params) {
2843
3291
  const item = readItem(params.item);
2844
- const itemId = item?.id ?? readString$2(params, "itemId");
3292
+ const itemId = item?.id ?? readString$3(params, "itemId");
2845
3293
  this.assistantProjection.recordItemStarted(item, itemId);
2846
3294
  if (itemId) this.activeItemIds.add(itemId);
2847
3295
  this.recordNativeToolOutcome(item);
@@ -2894,9 +3342,10 @@ var CodexAppServerEventProjector = class {
2894
3342
  }
2895
3343
  async handleItemCompleted(params) {
2896
3344
  const item = readItem(params.item);
3345
+ this.diagnostics.warnUnknownItemStatus(item);
2897
3346
  this.recordNativeToolOutcome(item);
2898
3347
  this.clearTerminalPresentationForNativeItem(item);
2899
- const itemId = item?.id ?? readString$2(params, "itemId");
3348
+ const itemId = item?.id ?? readString$3(params, "itemId");
2900
3349
  if (itemId) {
2901
3350
  this.activeItemIds.delete(itemId);
2902
3351
  this.completedItemIds.add(itemId);
@@ -2958,23 +3407,22 @@ var CodexAppServerEventProjector = class {
2958
3407
  }
2959
3408
  });
2960
3409
  }
2961
- handleTokenUsage(params) {
2962
- const tokenUsage = isJsonObject(params.tokenUsage) ? params.tokenUsage : void 0;
2963
- const last = tokenUsage && isJsonObject(tokenUsage.last) ? tokenUsage.last : void 0;
2964
- if (!last) return;
2965
- const usage = normalizeCodexTokenUsage(last);
2966
- if (usage) this.tokenUsage = usage;
3410
+ handleRawResponseCompleted(params) {
3411
+ const usage = isJsonObject(params.usage) ? params.usage : void 0;
3412
+ this.responseUsage = usage ? normalizeCodexResponseTokenUsage(usage) : void 0;
2967
3413
  }
2968
3414
  async handleTurnCompleted(params) {
2969
3415
  const turn = readCodexTurn(params.turn);
2970
3416
  if (!turn || turn.id !== this.turnId) return;
2971
3417
  this.completedTurn = turn;
3418
+ if (turn.status !== "completed") this.responseUsage = void 0;
2972
3419
  if (turn.status === "failed") {
2973
- this.promptError = formatCodexUsageLimitErrorMessage({
3420
+ const usageLimitMessage = formatCodexUsageLimitErrorMessage({
2974
3421
  message: turn.error?.message,
2975
3422
  codexErrorInfo: turn.error?.codexErrorInfo,
2976
3423
  rateLimits: this.options.readRecentRateLimits?.()
2977
- }) ?? turn.error?.message ?? "codex app-server turn failed";
3424
+ });
3425
+ this.promptError = usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : turn.error?.message ?? "codex app-server turn failed";
2978
3426
  this.promptErrorSource = "prompt";
2979
3427
  }
2980
3428
  const turnItems = turn.items ?? [];
@@ -2988,6 +3436,7 @@ var CodexAppServerEventProjector = class {
2988
3436
  }
2989
3437
  }
2990
3438
  for (const item of turnItems) {
3439
+ this.diagnostics.warnUnknownItemStatus(item);
2991
3440
  this.assistantProjection.recordSnapshotItem(item);
2992
3441
  this.reasoningProjection.recordItem(item);
2993
3442
  this.generatedMediaProjection.recordNative(item);
@@ -3000,6 +3449,7 @@ var CodexAppServerEventProjector = class {
3000
3449
  this.toolProgressProjection.emitToolResultSummary(item);
3001
3450
  this.toolProgressProjection.emitToolResultOutput(item);
3002
3451
  }
3452
+ this.assistantProjection.finalizeAnswerCandidate(turn);
3003
3453
  this.activeCompactionItemIds.clear();
3004
3454
  await this.reasoningProjection.maybeEndReasoning();
3005
3455
  }
@@ -3051,11 +3501,12 @@ var CodexAppServerEventProjector = class {
3051
3501
  }
3052
3502
  formatCodexErrorMessage(params) {
3053
3503
  const error = isJsonObject(params.error) ? params.error : void 0;
3054
- return formatCodexUsageLimitErrorMessage({
3055
- message: error ? readString$2(error, "message") : void 0,
3504
+ const usageLimitMessage = formatCodexUsageLimitErrorMessage({
3505
+ message: error ? readString$3(error, "message") : void 0,
3056
3506
  codexErrorInfo: error?.codexErrorInfo,
3057
3507
  rateLimits: this.options.readRecentRateLimits?.()
3058
- }) ?? readCodexErrorNotificationMessage(params);
3508
+ });
3509
+ return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
3059
3510
  }
3060
3511
  emitAgentEvent(event) {
3061
3512
  try {
@@ -3083,7 +3534,7 @@ var CodexAppServerEventProjector = class {
3083
3534
  return threadId === this.threadId && turnId === this.turnId;
3084
3535
  }
3085
3536
  isHookNotificationForCurrentThread(params) {
3086
- const threadId = readString$2(params, "threadId");
3537
+ const threadId = readString$3(params, "threadId");
3087
3538
  const turnId = params.turnId;
3088
3539
  return threadId === this.threadId && (turnId === this.turnId || turnId === null);
3089
3540
  }
@@ -3121,12 +3572,72 @@ function addCodexMessageToolOnlyFinalParameter(parameters) {
3121
3572
  ...rawProperties,
3122
3573
  final: {
3123
3574
  type: "boolean",
3124
- description: "Set true only when this message is intended to complete the reply to the current source conversation. OpenClaw stops after confirming delivery."
3575
+ description: "Set false for progress or true to complete the current source reply. If omitted, OpenClaw continues and resolves the latest omitted source reply when the turn ends."
3125
3576
  }
3126
3577
  }
3127
3578
  };
3128
3579
  }
3129
3580
  //#endregion
3581
+ //#region extensions/codex/src/app-server/shell-dynamic-tools.ts
3582
+ const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
3583
+ const CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME = "node_process";
3584
+ const CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES = /* @__PURE__ */ new Set([
3585
+ "host",
3586
+ "security",
3587
+ "ask"
3588
+ ]);
3589
+ /** Returns true when plugin config explicitly removes any named dynamic tool. */
3590
+ function isCodexDynamicToolExcluded(config, names) {
3591
+ const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName(name)));
3592
+ return (config.codexDynamicToolsExclude ?? []).some((name) => normalizedNames.has(normalizeCodexDynamicToolName(name)));
3593
+ }
3594
+ function createNodeExecDynamicTool(execTool, configuredNode) {
3595
+ const pinnedNode = configuredNode?.trim();
3596
+ return {
3597
+ ...execTool,
3598
+ name: CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME,
3599
+ description: pinnedNode ? "Run a shell command on the OpenClaw configured remote node for this session. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Use node_process for follow-up on backgrounded node_exec sessions. Use Codex's native shell for local app-server work." : "Run a shell command on an OpenClaw remote node. Select the node by name or id when multiple nodes are available. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Use node_process for follow-up on backgrounded node_exec sessions. Use Codex's native shell for local app-server work.",
3600
+ parameters: hideNodeExecDynamicToolParameters(execTool.parameters, { hideNode: Boolean(pinnedNode) }),
3601
+ execute: async (toolCallId, args, signal, onUpdate) => {
3602
+ const result = await execTool.execute(toolCallId, pinNodeExecDynamicToolArgs(args, pinnedNode), signal, onUpdate);
3603
+ return {
3604
+ ...result,
3605
+ content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace("Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.", "Use node_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.") }) : item)
3606
+ };
3607
+ }
3608
+ };
3609
+ }
3610
+ function createNodeProcessDynamicTool(processTool) {
3611
+ return {
3612
+ ...processTool,
3613
+ name: CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME,
3614
+ description: "Manage node_exec sessions that were started on OpenClaw remote nodes: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use only for node_exec follow-up; use Codex's native shell session handling for local app-server work."
3615
+ };
3616
+ }
3617
+ function pinNodeExecDynamicToolArgs(args, configuredNode) {
3618
+ const { host: _host, security: _security, ask: _ask, node: requestedNode, ...rest } = args && typeof args === "object" && !Array.isArray(args) ? args : {};
3619
+ const node = configuredNode ?? (typeof requestedNode === "string" ? requestedNode.trim() : "");
3620
+ return {
3621
+ ...rest,
3622
+ host: "node",
3623
+ ...node ? { node } : {}
3624
+ };
3625
+ }
3626
+ function hideNodeExecDynamicToolParameters(parameters, options) {
3627
+ if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return parameters;
3628
+ const schema = parameters;
3629
+ const rawProperties = schema.properties;
3630
+ if (!rawProperties || typeof rawProperties !== "object" || Array.isArray(rawProperties)) return parameters;
3631
+ const nextProperties = Object.fromEntries(Object.entries(rawProperties).filter(([name]) => !CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES.has(normalizeCodexDynamicToolName(name)) && !(options.hideNode && normalizeCodexDynamicToolName(name) === "node")));
3632
+ const rawRequired = schema.required;
3633
+ const nextRequired = Array.isArray(rawRequired) ? rawRequired.filter((name) => typeof name !== "string" || !CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES.has(normalizeCodexDynamicToolName(name)) && !(options.hideNode && normalizeCodexDynamicToolName(name) === "node")) : rawRequired;
3634
+ return {
3635
+ ...schema,
3636
+ properties: nextProperties,
3637
+ ...Array.isArray(rawRequired) ? { required: nextRequired } : {}
3638
+ };
3639
+ }
3640
+ //#endregion
3130
3641
  //#region extensions/codex/src/app-server/vision-tools.ts
3131
3642
  /**
3132
3643
  * Filters Codex dynamic tools for turns that already contain image inputs so
@@ -3153,13 +3664,6 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
3153
3664
  "apply_patch"
3154
3665
  ];
3155
3666
  const CODEX_MEMORY_FLUSH_DYNAMIC_TOOL_ALLOW = /* @__PURE__ */ new Set(["read", "write"]);
3156
- const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
3157
- const CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME = "node_process";
3158
- const CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES = /* @__PURE__ */ new Set([
3159
- "host",
3160
- "security",
3161
- "ask"
3162
- ]);
3163
3667
  function preserveRingZeroSystemAgentTool(allTools, filteredTools) {
3164
3668
  const openclaw = allTools.find((tool) => tool.name === "openclaw" && tool.catalogMode === "direct-only");
3165
3669
  if (!openclaw) return filteredTools;
@@ -3248,8 +3752,8 @@ async function buildDynamicTools(input) {
3248
3752
  const toolBuildStages = createCodexDynamicToolBuildStageTracker({ enabled: input.profilerEnabled });
3249
3753
  const modelHasVision = params.model.input?.includes("image") ?? false;
3250
3754
  const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, input.sessionAgentId);
3251
- const agentHarness = await import("openclaw/plugin-sdk/agent-harness");
3252
- const createOpenClawCodingTools = dynamicToolBuildState.openClawCodingToolsFactory ?? agentHarness.createOpenClawCodingTools;
3755
+ const { createOpenClawCodingTools: defaultCreateOpenClawCodingTools, resolveWebSearchToolPolicy } = await import("openclaw/plugin-sdk/agent-harness");
3756
+ const createOpenClawCodingTools = dynamicToolBuildState.openClawCodingToolsFactory ?? defaultCreateOpenClawCodingTools;
3253
3757
  toolBuildStages.mark("load-agent-harness-tools");
3254
3758
  const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
3255
3759
  const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
@@ -3302,6 +3806,7 @@ async function buildDynamicTools(input) {
3302
3806
  modelCompat: params.model.compat && typeof params.model.compat === "object" ? params.model.compat : void 0,
3303
3807
  modelApi: params.model.api,
3304
3808
  modelContextWindowTokens: params.model.contextWindow,
3809
+ delegationCapability: params.delegationCapability,
3305
3810
  modelAuthMode: resolveModelAuthMode(params.model.provider, params.config, params.toolAuthProfileStore ?? params.authProfileStore, { workspaceDir: input.effectiveWorkspace }),
3306
3811
  suppressManagedWebSearch: false,
3307
3812
  currentChannelId: params.currentChannelId,
@@ -3346,7 +3851,7 @@ async function buildDynamicTools(input) {
3346
3851
  nativeProviderWebSearchSupport: input.nativeProviderWebSearchSupport
3347
3852
  });
3348
3853
  const readableAllTools = [...readableAllToolProjection.tools];
3349
- const normallyProfiledTools = filterCodexDynamicTools(readableAllTools, input.pluginConfig);
3854
+ const normallyProfiledTools = shouldKeepOpenClawShellDynamicTools(input, nativeExecutionPolicy) ? filterCodexDynamicToolsWithOpenClawShell(readableAllTools, input.pluginConfig) : filterCodexDynamicTools(readableAllTools, input.pluginConfig);
3350
3855
  const profileFilteredTools = (input.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) ? preserveRingZeroSystemAgentTool(readableAllTools, normallyProfiledTools) : normallyProfiledTools;
3351
3856
  const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input), readableAllTools, input, nativeExecutionPolicy);
3352
3857
  toolBuildStages.mark("codex-filtering");
@@ -3356,7 +3861,7 @@ async function buildDynamicTools(input) {
3356
3861
  });
3357
3862
  toolBuildStages.mark("vision-filtering");
3358
3863
  const webSearchPresent = visionFilteredTools.some((tool) => tool.name === "web_search");
3359
- const webSearchPolicy = agentHarness.resolveWebSearchToolPolicy({
3864
+ const webSearchPolicy = resolveWebSearchToolPolicy({
3360
3865
  config: params.config,
3361
3866
  modelProvider: params.model.provider,
3362
3867
  modelId: params.modelId,
@@ -3570,13 +4075,6 @@ function shouldExposeSandboxExecDynamicTool(input) {
3570
4075
  const backendId = input.sandbox?.enabled ? input.sandbox.backendId.trim().toLowerCase() : "";
3571
4076
  return Boolean(backendId && input.nativeToolSurfaceEnabled === false);
3572
4077
  }
3573
- function isCodexDynamicToolExcluded(config, names) {
3574
- const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName(name)));
3575
- return (config.codexDynamicToolsExclude ?? []).some((name) => {
3576
- const normalized = normalizeCodexDynamicToolName(name);
3577
- return normalizedNames.has(normalized);
3578
- });
3579
- }
3580
4078
  function isSandboxShellDynamicToolExcluded(config) {
3581
4079
  return isCodexDynamicToolExcluded(config, [
3582
4080
  "exec",
@@ -3594,55 +4092,12 @@ function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePo
3594
4092
  const processTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "process");
3595
4093
  if (!execTool || !processTool) return filteredTools;
3596
4094
  const toolsToAppend = [];
3597
- if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME)) toolsToAppend.push(createNodeExecDynamicTool(execTool, nodePolicy.node));
3598
- if (!isCodexDynamicToolExcluded(input.pluginConfig, ["process", CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME)) toolsToAppend.push(createNodeProcessDynamicTool(processTool));
4095
+ if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) toolsToAppend.push(createNodeExecDynamicTool(execTool, nodePolicy.node));
4096
+ if (!isCodexDynamicToolExcluded(input.pluginConfig, ["process", "node_process"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_process")) toolsToAppend.push(createNodeProcessDynamicTool(processTool));
3599
4097
  return toolsToAppend.length > 0 ? [...filteredTools, ...toolsToAppend] : filteredTools;
3600
4098
  }
3601
- function createNodeExecDynamicTool(execTool, configuredNode) {
3602
- const pinnedNode = configuredNode?.trim();
3603
- return {
3604
- ...execTool,
3605
- name: CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME,
3606
- description: pinnedNode ? "Run a shell command on the OpenClaw configured remote node for this session. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Use node_process for follow-up on backgrounded node_exec sessions. Use Codex's native shell for local app-server work." : "Run a shell command on an OpenClaw remote node. Select the node by name or id when multiple nodes are available. This tool always uses OpenClaw host=node internally and follows the existing node exec approval and allowlist policy. Use node_process for follow-up on backgrounded node_exec sessions. Use Codex's native shell for local app-server work.",
3607
- parameters: hideNodeExecDynamicToolParameters(execTool.parameters, { hideNode: Boolean(pinnedNode) }),
3608
- execute: async (toolCallId, args, signal, onUpdate) => {
3609
- const result = await execTool.execute(toolCallId, pinNodeExecDynamicToolArgs(args, pinnedNode), signal, onUpdate);
3610
- return {
3611
- ...result,
3612
- content: result.content.map((item) => item.type === "text" ? Object.assign({}, item, { text: item.text.replace("Use process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.", "Use node_process (list/poll/log/write/send-keys/submit/paste/kill/clear/remove) for follow-up.") }) : item)
3613
- };
3614
- }
3615
- };
3616
- }
3617
- function createNodeProcessDynamicTool(processTool) {
3618
- return {
3619
- ...processTool,
3620
- name: CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME,
3621
- description: "Manage node_exec sessions that were started on OpenClaw remote nodes: list, poll, log, write, send-keys, submit, paste, kill, clear, or remove. Use only for node_exec follow-up; use Codex's native shell session handling for local app-server work."
3622
- };
3623
- }
3624
- function pinNodeExecDynamicToolArgs(args, configuredNode) {
3625
- const { host: _host, security: _security, ask: _ask, node: requestedNode, ...rest } = args && typeof args === "object" && !Array.isArray(args) ? args : {};
3626
- const node = configuredNode ?? (typeof requestedNode === "string" ? requestedNode.trim() : "");
3627
- return {
3628
- ...rest,
3629
- host: "node",
3630
- ...node ? { node } : {}
3631
- };
3632
- }
3633
- function hideNodeExecDynamicToolParameters(parameters, options) {
3634
- if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return parameters;
3635
- const schema = parameters;
3636
- const rawProperties = schema.properties;
3637
- if (!rawProperties || typeof rawProperties !== "object" || Array.isArray(rawProperties)) return parameters;
3638
- const nextProperties = Object.fromEntries(Object.entries(rawProperties).filter(([name]) => !CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES.has(normalizeCodexDynamicToolName(name)) && !(options.hideNode && normalizeCodexDynamicToolName(name) === "node")));
3639
- const rawRequired = schema.required;
3640
- const nextRequired = Array.isArray(rawRequired) ? rawRequired.filter((name) => typeof name !== "string" || !CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES.has(normalizeCodexDynamicToolName(name)) && !(options.hideNode && normalizeCodexDynamicToolName(name) === "node")) : rawRequired;
3641
- return {
3642
- ...schema,
3643
- properties: nextProperties,
3644
- ...Array.isArray(rawRequired) ? { required: nextRequired } : {}
3645
- };
4099
+ function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
4100
+ return !isCodexMemoryFlushRun(input.params) && input.nativeToolSurfaceEnabled === false && input.sandbox?.enabled !== true && nodePolicy.effectiveExecHost !== "node";
3646
4101
  }
3647
4102
  function resolveCodexNativeExecutionPolicyForDynamicTools(input) {
3648
4103
  return resolveCodexNativeExecutionPolicy({
@@ -3663,7 +4118,7 @@ function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
3663
4118
  const allowSet = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)).filter(Boolean));
3664
4119
  return tools.filter((tool) => {
3665
4120
  const normalized = normalizeCodexDynamicToolName(tool.name);
3666
- return allowSet.has(normalized) || normalized === "sandbox_exec" && allowSet.has("exec") || normalized === "sandbox_process" && (allowSet.has("exec") || allowSet.has("process")) || normalized === CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME && allowSet.has("exec") || normalized === CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME && (allowSet.has("exec") || allowSet.has("process"));
4121
+ return allowSet.has(normalized) || normalized === "sandbox_exec" && allowSet.has("exec") || normalized === "sandbox_process" && (allowSet.has("exec") || allowSet.has("process")) || normalized === "node_exec" && allowSet.has("exec") || normalized === "node_process" && (allowSet.has("exec") || allowSet.has("process"));
3667
4122
  });
3668
4123
  }
3669
4124
  /** Detects the wildcard allowlist marker after Codex tool-name normalization. */
@@ -3675,90 +4130,28 @@ function shouldForceMessageTool(params) {
3675
4130
  return params.disableMessageTool !== true && params.sourceReplyDeliveryMode === "message_tool_only";
3676
4131
  }
3677
4132
  //#endregion
3678
- //#region extensions/codex/src/app-server/attempt-timeouts.ts
3679
- /**
3680
- * Timeout defaults and normalizers for Codex app-server startup and turn
3681
- * liveness watches.
3682
- */
3683
- /** Minimum startup timeout accepted by the Codex app-server harness. */
3684
- const CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS = 100;
3685
- /** Default idle timeout while waiting for app-server turn completion. */
3686
- const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 6e4;
3687
- /** Short guard after apparent assistant completion. */
3688
- const CODEX_TURN_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS = 1e4;
3689
- const CODEX_POST_TOOL_RAW_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS = 5 * 6e4;
3690
- /** Guard after reasoning/commentary progress when no tool handoff occurred. */
3691
- const CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS = 5 * 6e4;
3692
- /** Long terminal idle watch for app-server turns that never send completion. */
3693
- const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 6e4;
3694
- function resolvePositiveIntegerTimeoutMs(value, fallbackMs) {
3695
- return resolveTimerTimeoutMs(value, resolveTimerTimeoutMs(fallbackMs, 1));
3696
- }
3697
- /** Runs startup work with abort and timeout handling plus optional cleanup. */
3698
- async function withCodexStartupTimeout(params) {
3699
- if (params.signal.aborted) throw new Error("codex app-server startup aborted");
3700
- let timeout;
3701
- let abortCleanup;
3702
- let timeoutError;
3703
- let timeoutCleanup;
3704
- try {
3705
- return await Promise.race([params.operation(), new Promise((_, reject) => {
3706
- const rejectOnce = (error) => {
3707
- if (timeout) {
3708
- clearTimeout(timeout);
3709
- timeout = void 0;
3710
- }
3711
- reject(error);
3712
- };
3713
- timeout = setTimeout(() => {
3714
- timeoutError = /* @__PURE__ */ new Error("codex app-server startup timed out");
3715
- timeoutCleanup = Promise.resolve(params.onTimeout?.()).then(() => void 0, () => void 0);
3716
- timeoutCleanup.finally(() => {
3717
- rejectOnce(timeoutError);
3718
- });
3719
- }, params.timeoutMs);
3720
- const abortListener = () => rejectOnce(/* @__PURE__ */ new Error("codex app-server startup aborted"));
3721
- params.signal.addEventListener("abort", abortListener, { once: true });
3722
- abortCleanup = () => params.signal.removeEventListener("abort", abortListener);
3723
- })]);
3724
- } catch (error) {
3725
- if (timeoutError) {
3726
- await timeoutCleanup;
3727
- throw timeoutError;
3728
- }
3729
- throw error;
3730
- } finally {
3731
- if (timeout) clearTimeout(timeout);
3732
- abortCleanup?.();
4133
+ //#region extensions/codex/src/app-server/source-reply-finality.ts
4134
+ const sourceReplyDeliveryIntents = /* @__PURE__ */ new WeakMap();
4135
+ /** Retain source-reply intent until the owning Codex turn has an authoritative outcome. */
4136
+ function recordCodexSourceReplyDeliveryIntent(owner, intent) {
4137
+ const intents = sourceReplyDeliveryIntents.get(owner);
4138
+ if (intents) {
4139
+ intents.push(intent);
4140
+ return;
3733
4141
  }
4142
+ sourceReplyDeliveryIntents.set(owner, [intent]);
3734
4143
  }
3735
- /** Resolves startup timeout while honoring the configured floor. */
3736
- function resolveCodexStartupTimeoutMs(params) {
3737
- const timeoutFloorMs = resolvePositiveIntegerTimeoutMs(params.timeoutFloorMs, CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS);
3738
- const timeoutMs = resolvePositiveIntegerTimeoutMs(params.timeoutMs, timeoutFloorMs);
3739
- return Math.max(timeoutFloorMs, timeoutMs);
3740
- }
3741
- /** Resolves the completion-idle timeout for an active turn. */
3742
- function resolveCodexTurnCompletionIdleTimeoutMs(value) {
3743
- return resolvePositiveIntegerTimeoutMs(value, CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS);
3744
- }
3745
- /** Resolves the short assistant-completion release timeout. */
3746
- function resolveCodexTurnAssistantCompletionIdleTimeoutMs(value) {
3747
- return resolvePositiveIntegerTimeoutMs(value, CODEX_TURN_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS);
3748
- }
3749
- /** Resolves the conservative post-tool raw assistant guard timeout. */
3750
- function resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(value, fallbackMs) {
3751
- return resolvePositiveIntegerTimeoutMs(value, Math.max(resolvePositiveIntegerTimeoutMs(void 0, fallbackMs), CODEX_POST_TOOL_RAW_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS));
3752
- }
3753
- /** Resolves the long terminal turn idle timeout. */
3754
- function resolveCodexTurnTerminalIdleTimeoutMs(value, runTimeoutOverrideMs) {
3755
- const explicitRunBudgetMs = resolvePositiveIntegerTimeoutMs(runTimeoutOverrideMs, CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS);
3756
- return resolvePositiveIntegerTimeoutMs(value, Math.max(CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS, explicitRunBudgetMs));
3757
- }
3758
- /** Adds gateway grace time to a caller timeout without overflowing invalid values. */
3759
- function resolveCodexGatewayTimeoutWithGraceMs(timeoutMs, graceMs = 1e4) {
3760
- const timeout = resolvePositiveIntegerTimeoutMs(timeoutMs, 1);
3761
- return addTimerTimeoutGraceMs(timeout, resolveTimerTimeoutMs(graceMs, 0, 0)) ?? timeout;
4144
+ /** Resolve omitted finality without changing explicit progress or final markers. */
4145
+ function settleCodexSourceReplyFinality(owner, turnSucceeded) {
4146
+ const intents = sourceReplyDeliveryIntents.get(owner);
4147
+ if (!intents) return false;
4148
+ const lastIntent = intents.at(-1);
4149
+ for (const intent of intents) {
4150
+ if (intent.final !== void 0) continue;
4151
+ intent.record.sourceReplyFinal = turnSucceeded && intent === lastIntent;
4152
+ }
4153
+ sourceReplyDeliveryIntents.delete(owner);
4154
+ return turnSucceeded && intents.some((intent) => intent.record.sourceReplyFinal === true);
3762
4155
  }
3763
4156
  //#endregion
3764
4157
  //#region extensions/codex/src/app-server/provider-capabilities.ts
@@ -4580,6 +4973,7 @@ async function handleCodexAppServerApprovalRequest(params) {
4580
4973
  paramsForRun: params.paramsForRun,
4581
4974
  context,
4582
4975
  nativeHookRelay: params.nativeHookRelay,
4976
+ autoApprove: params.autoApprove,
4583
4977
  signal: params.signal
4584
4978
  });
4585
4979
  if (policyOutcome?.outcome === "denied") {
@@ -4759,6 +5153,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
4759
5153
  context: params.context,
4760
5154
  policyRequest,
4761
5155
  nativeHookRelay: params.nativeHookRelay,
5156
+ autoApprove: params.autoApprove,
4762
5157
  cwd,
4763
5158
  signal: params.signal
4764
5159
  });
@@ -4867,6 +5262,11 @@ async function runNativeRelayToolPolicyForApprovalRequest(params) {
4867
5262
  };
4868
5263
  return { handled: true };
4869
5264
  } catch (error) {
5265
+ if (params.autoApprove === true && !hasNativeHookRelayInvocation({
5266
+ relayId: params.nativeHookRelay.relayId,
5267
+ event: "pre_tool_use",
5268
+ toolUseId: params.context.approvalId
5269
+ })) return;
4870
5270
  return {
4871
5271
  handled: true,
4872
5272
  blocked: true,
@@ -5539,6 +5939,7 @@ function createCodexDynamicToolBridge(params) {
5539
5939
  return getChannelAgentToolMeta(tool) === void 0;
5540
5940
  };
5541
5941
  const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
5942
+ const executionSnapshotStates = /* @__PURE__ */ new Map();
5542
5943
  const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
5543
5944
  return {
5544
5945
  availableSpecs: createCodexDynamicToolSpecs({
@@ -5552,9 +5953,16 @@ function createCodexDynamicToolBridge(params) {
5552
5953
  directToolNames
5553
5954
  }),
5554
5955
  telemetry,
5956
+ consumeToolExecutionSnapshot: (toolCallId) => {
5957
+ const state = executionSnapshotStates.get(toolCallId);
5958
+ executionSnapshotStates.delete(toolCallId);
5959
+ if (state) state.consumed = true;
5960
+ return state?.snapshot;
5961
+ },
5555
5962
  handleToolCall: async (call, options) => {
5556
5963
  const toolEntry = toolMap.get(call.tool);
5557
5964
  if (!toolEntry) {
5965
+ const executedArguments = jsonObjectToRecord(call.arguments);
5558
5966
  const message = registeredToolNames.has(call.tool) ? `OpenClaw tool is not available for this turn: ${call.tool}` : `Unknown OpenClaw tool: ${call.tool}`;
5559
5967
  finalizeToolTerminalPresentation({
5560
5968
  toolCallId: call.callId,
@@ -5566,28 +5974,34 @@ function createCodexDynamicToolBridge(params) {
5566
5974
  toolCallOrdinal: options?.toolCallOrdinal
5567
5975
  });
5568
5976
  notifyAgentToolResult(options?.onAgentToolResult, call.tool, failedToolResult(message), true);
5569
- if (registeredToolNames.has(call.tool)) return {
5570
- contentItems: [{
5571
- type: "inputText",
5572
- text: message
5573
- }],
5574
- success: false
5575
- };
5576
- return {
5577
- contentItems: [{
5578
- type: "inputText",
5579
- text: message
5580
- }],
5581
- success: false
5582
- };
5977
+ return createFailedDynamicToolResponse(message, {
5978
+ executedArguments,
5979
+ executionStarted: false
5980
+ });
5583
5981
  }
5584
5982
  const { tool, name: toolName } = toolEntry;
5585
5983
  const args = jsonObjectToRecord(call.arguments);
5586
5984
  const startedAt = Date.now();
5587
5985
  const signal = composeAbortSignals(params.signal, options?.signal);
5588
5986
  let didStartExecution = false;
5987
+ let didDispatchExecution = false;
5589
5988
  let executionPrevented = false;
5590
5989
  let executedArgs = structuredClone(args);
5990
+ const executionSnapshotState = {
5991
+ consumed: false,
5992
+ retainAfterCompletion: options?.retainExecutionSnapshot === true
5993
+ };
5994
+ executionSnapshotStates.set(call.callId, executionSnapshotState);
5995
+ const captureExecutionBoundary = () => {
5996
+ didStartExecution ||= didDispatchExecution;
5997
+ executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
5998
+ const adjustedExecutedArgs = consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
5999
+ if (isRecord(adjustedExecutedArgs)) executedArgs = adjustedExecutedArgs;
6000
+ if (!executionSnapshotState.consumed) executionSnapshotState.snapshot = {
6001
+ executedArguments: structuredClone(executedArgs),
6002
+ executionStarted: didStartExecution && !executionPrevented
6003
+ };
6004
+ };
5591
6005
  try {
5592
6006
  const preparedArgs = tool.prepareArguments ? tool.prepareArguments(args) : args;
5593
6007
  const telemetryArgs = isRecord(preparedArgs) ? preparedArgs : args;
@@ -5600,13 +6014,11 @@ function createCodexDynamicToolBridge(params) {
5600
6014
  replyToMode: params.hookContext?.replyToMode,
5601
6015
  hasRepliedRef: params.hookContext?.hasRepliedRef ? { value: params.hookContext.hasRepliedRef.value } : void 0
5602
6016
  };
5603
- didStartExecution = true;
6017
+ didDispatchExecution = true;
5604
6018
  const rawResult = await tool.execute(call.callId, preparedArgs, signal);
5605
- const adjustedExecutedArgs = consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
5606
- if (isRecord(adjustedExecutedArgs)) executedArgs = structuredClone(adjustedExecutedArgs);
5607
- executionPrevented = consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
6019
+ captureExecutionBoundary();
5608
6020
  const telemetryRawResult = sanitizeToolResult(rawResult);
5609
- const rawIsError = isCodexToolResultError(rawResult);
6021
+ const rawIsError = isToolResultError(rawResult);
5610
6022
  const rawResultFailureKind = resolveToolResultFailureKind(rawResult);
5611
6023
  const middlewareResult = await middlewareRunner.applyToolResultMiddleware({
5612
6024
  threadId: call.threadId,
@@ -5625,7 +6037,7 @@ function createCodexDynamicToolBridge(params) {
5625
6037
  args: structuredClone(executedArgs),
5626
6038
  result: middlewareResult
5627
6039
  });
5628
- const resultIsError = rawIsError || isCodexToolResultError(result);
6040
+ const resultIsError = rawIsError || isToolResultError(result);
5629
6041
  const finalResultFailureKind = resolveToolResultFailureKind(result);
5630
6042
  const resultFailureKind = rawResultFailureKind ?? finalResultFailureKind;
5631
6043
  const observerResult = rawResultFailureKind && finalResultFailureKind !== rawResultFailureKind ? {
@@ -5689,8 +6101,9 @@ function createCodexDynamicToolBridge(params) {
5689
6101
  }) && (replyReceiptMatchesCurrentMessage(rawResult, params.hookContext) || replyReceiptMatchesCurrentMessage(result, params.hookContext));
5690
6102
  const toolConfirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && !resultIsError && (rawResult.terminate === true || result.terminate === true);
5691
6103
  const hasExplicitFinalControl = typeof executedArgs.final === "boolean";
5692
- const sourceReplyFinal = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply || receiptConfirmedSourceReply) ? hasExplicitFinalControl ? executedArgs.final === true : true : void 0;
5693
- collectToolTelemetry({
6104
+ const confirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply || receiptConfirmedSourceReply);
6105
+ const sourceReplyFinal = confirmedSourceReply ? hasExplicitFinalControl ? executedArgs.final === true : void 0 : void 0;
6106
+ const sourceReplyRecord = collectToolTelemetry({
5694
6107
  toolName,
5695
6108
  args: executedArgs,
5696
6109
  result,
@@ -5700,17 +6113,26 @@ function createCodexDynamicToolBridge(params) {
5700
6113
  messagingTarget: confirmedMessagingTarget,
5701
6114
  sourceReplyFinal
5702
6115
  });
6116
+ if (confirmedSourceReply && sourceReplyRecord) recordCodexSourceReplyDeliveryIntent(telemetry, {
6117
+ record: sourceReplyRecord,
6118
+ final: sourceReplyFinal
6119
+ });
5703
6120
  if (deliveredSourceReply || receiptConfirmedSourceReply || toolConfirmedSourceReply) telemetry.didDeliverSourceReplyViaMessageTool = true;
5704
- withDynamicToolTermination(response, (rawResult.terminate === true || result.terminate === true) && !(params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && executedArgs.final === false) || isToolResultYield(rawResult) || isToolResultYield(result) || (deliveredSourceReply || receiptConfirmedSourceReply) && executedArgs.final !== false);
6121
+ const defersInferredSourceReplyTermination = confirmedSourceReply && executedArgs.final !== true;
6122
+ withDynamicToolTermination(response, (rawResult.terminate === true || result.terminate === true) && !defersInferredSourceReplyTermination || isToolResultYield(rawResult) || isToolResultYield(result) || confirmedSourceReply && executedArgs.final === true);
5705
6123
  const asyncStarted = isAsyncStartedToolResult(rawResult) || isAsyncStartedToolResult(result);
5706
6124
  withDynamicToolAsyncStarted(response, asyncStarted);
5707
- return withSideEffectEvidence(response, !(executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs)));
6125
+ const replaySafe = executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
6126
+ return withDynamicToolExecutionState(response, {
6127
+ executedArguments: executedArgs,
6128
+ executionStarted: didStartExecution && !executionPrevented,
6129
+ sideEffectEvidence: !replaySafe
6130
+ });
5708
6131
  } catch (error) {
6132
+ captureExecutionBoundary();
5709
6133
  if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId) invalidateComputerFrame(params.computerContextEpoch);
5710
6134
  const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
5711
6135
  const errorMessage = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
5712
- const adjustedExecutedArgs = consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
5713
- if (isRecord(adjustedExecutedArgs)) executedArgs = structuredClone(adjustedExecutedArgs);
5714
6136
  executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
5715
6137
  const failedResult = failedToolResult(errorMessage, executionDisposition);
5716
6138
  finalizeToolTerminalPresentation({
@@ -5743,13 +6165,20 @@ function createCodexDynamicToolBridge(params) {
5743
6165
  startedAt
5744
6166
  });
5745
6167
  const replaySafe = !didStartExecution || executionPrevented || isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
5746
- return withSideEffectEvidence(withDiagnosticFailureDisposition({
6168
+ return withDynamicToolExecutionState(withDiagnosticFailureDisposition({
5747
6169
  contentItems: [{
5748
6170
  type: "inputText",
5749
6171
  text: errorMessage
5750
6172
  }],
5751
6173
  success: false
5752
- }, executionDisposition), didStartExecution && !replaySafe);
6174
+ }, executionDisposition), {
6175
+ executedArguments: executedArgs,
6176
+ executionStarted: didStartExecution && !executionPrevented,
6177
+ sideEffectEvidence: didStartExecution && !replaySafe
6178
+ });
6179
+ } finally {
6180
+ if (executionSnapshotStates.get(call.callId) === executionSnapshotState && (executionSnapshotState.consumed || !executionSnapshotState.retainAfterCompletion)) executionSnapshotStates.delete(call.callId);
6181
+ consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
5753
6182
  }
5754
6183
  }
5755
6184
  };
@@ -5877,7 +6306,12 @@ function projectCodexDynamicTools(tools) {
5877
6306
  quarantinedTools.push(descriptor.diagnostic);
5878
6307
  continue;
5879
6308
  }
5880
- const projection = projectRuntimeToolInputSchema(descriptor.parameters, `${descriptor.name}.inputSchema`);
6309
+ const normalizedParameters = normalizeOpenAIToolSchemas({
6310
+ provider: "openai",
6311
+ modelApi: "openai-chatgpt-responses",
6312
+ tools: [{ parameters: descriptor.parameters }]
6313
+ })[0]?.parameters;
6314
+ const projection = projectRuntimeToolInputSchema(normalizedParameters ?? descriptor.parameters, `${descriptor.name}.inputSchema`);
5881
6315
  if (projection.violations.length > 0) {
5882
6316
  quarantinedTools.push({
5883
6317
  tool: descriptor.name,
@@ -6032,11 +6466,12 @@ function collectToolTelemetry(params) {
6032
6466
  params.telemetry.didSendViaMessagingTool = true;
6033
6467
  const sourceReplyPayload = extractInternalSourceReplyPayload(params.result?.details);
6034
6468
  if (sourceReplyPayload) {
6035
- params.telemetry.messagingToolSourceReplyPayloads.push({
6469
+ const record = {
6036
6470
  ...sourceReplyPayload,
6037
6471
  ...params.sourceReplyFinal !== void 0 ? { sourceReplyFinal: params.sourceReplyFinal } : {}
6038
- });
6039
- return;
6472
+ };
6473
+ params.telemetry.messagingToolSourceReplyPayloads.push(record);
6474
+ return record;
6040
6475
  }
6041
6476
  const text = readFirstString(params.args, [
6042
6477
  "text",
@@ -6047,7 +6482,7 @@ function collectToolTelemetry(params) {
6047
6482
  if (text) params.telemetry.messagingToolSentTexts.push(text);
6048
6483
  const mediaUrls = collectMediaUrls(params.args);
6049
6484
  params.telemetry.messagingToolSentMediaUrls.push(...mediaUrls);
6050
- params.telemetry.messagingToolSentTargets.push({
6485
+ const record = {
6051
6486
  ...params.messagingTarget ?? {
6052
6487
  tool: params.toolName,
6053
6488
  provider: readFirstString(params.args, ["provider", "channel"]) ?? params.toolName,
@@ -6066,7 +6501,9 @@ function collectToolTelemetry(params) {
6066
6501
  ...text ? { text } : {},
6067
6502
  ...mediaUrls.length > 0 ? { mediaUrls } : {},
6068
6503
  ...params.sourceReplyFinal !== void 0 ? { sourceReplyFinal: params.sourceReplyFinal } : {}
6069
- });
6504
+ };
6505
+ params.telemetry.messagingToolSentTargets.push(record);
6506
+ return record;
6070
6507
  }
6071
6508
  function extractInternalSourceReplyPayload(details) {
6072
6509
  if (!isRecord(details) || details.sourceReplySink !== "internal-ui") return;
@@ -6087,17 +6524,6 @@ function extractInternalSourceReplyPayload(details) {
6087
6524
  };
6088
6525
  return text || mediaUrls.length > 0 || payload.presentation || payload.interactive ? payload : void 0;
6089
6526
  }
6090
- function isCodexToolResultError(result) {
6091
- if (isToolResultError(result)) return true;
6092
- const details = result.details;
6093
- if (!isRecord(details)) return false;
6094
- if (details.ok === true || details.success === true) return false;
6095
- if (details.timedOut === true) return true;
6096
- if (typeof details.exitCode === "number" && details.exitCode !== 0) return true;
6097
- if (typeof details.status !== "string") return false;
6098
- const status = details.status.trim().toLowerCase();
6099
- return status !== "" && status !== "0" && status !== "ok" && status !== "success" && status !== "completed" && status !== "recorded" && status !== "created" && status !== "updated" && status !== "accepted" && status !== "found" && status !== "missing" && status !== "pending" && status !== "started" && status !== "running" && status !== "yielded";
6100
- }
6101
6527
  function isToolResultYield(result) {
6102
6528
  const details = result.details;
6103
6529
  if (!isRecord(details) || typeof details.status !== "string") return false;
@@ -6125,15 +6551,6 @@ function withDiagnosticFailureDisposition(response, disposition) {
6125
6551
  });
6126
6552
  return response;
6127
6553
  }
6128
- function withSideEffectEvidence(response, sideEffectEvidence) {
6129
- if (!sideEffectEvidence) return response;
6130
- Object.defineProperty(response, "sideEffectEvidence", {
6131
- configurable: true,
6132
- enumerable: false,
6133
- value: true
6134
- });
6135
- return response;
6136
- }
6137
6554
  function withDynamicToolTermination(response, terminate) {
6138
6555
  if (!terminate) return response;
6139
6556
  Object.defineProperty(response, "terminate", {
@@ -6274,4 +6691,4 @@ function isCronAddAction(args) {
6274
6691
  return typeof action === "string" && action.trim().toLowerCase() === "add";
6275
6692
  }
6276
6693
  //#endregion
6277
- export { inferCodexDynamicToolMeta as $, CodexNativeToolLifecycleProjector as A, handleDynamicToolCallWithTimeout as B, resolveCodexMessageToolProvider as C, shouldWarnCodexDynamicToolBuildStageSummary as D, shouldRequireCodexSandboxExecServerEnvironment as E, createCodexNativeHookRelay as F, resolveTerminalDynamicToolBatchAction as G, isDynamicToolTerminalDiagnosticEvent as H, emitCodexNativePreToolUseFailureDiagnostic as I, toCodexDynamicToolProgressResponse as J, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as K, resolveCodexNativeHookRelayEvents as L, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as M, buildCodexNativeHookRelayConfig as N, filterToolsForVisionInputs as O, buildCodexNativeHookRelayDisabledConfig as P, shouldEmitTranscriptToolProgress as Q, resolveCodexNativeHookRelayTtlMs as R, resolveCodexExternalSandboxPolicyForOpenClawSandbox as S, shouldEnableCodexAppServerNativeToolSurface as T, isMatchingDynamicToolTerminalDiagnostic as U, hasPendingDynamicToolTerminalDiagnostic as V, resolveDynamicToolCallTimeoutMs as W, resolveCodexToolAbortTerminalReason as X, toCodexDynamicToolProtocolResponse as Y, readCodexMirroredSessionHistoryMessages as Z, createCodexDynamicToolBuildStageTracker as _, emitDynamicToolStartedDiagnostic as a, resolveCodexAppServerExecutionCwd as b, resolveCodexProviderWebSearchSupportForClient as c, resolveCodexStartupTimeoutMs as d, resolveCodexToolProgressDetailMode as et, resolveCodexTurnAssistantCompletionIdleTimeoutMs as f, buildDynamicTools as g, withCodexStartupTimeout as h, emitDynamicToolErrorDiagnostic as i, CODEX_NATIVE_HOOK_RELAY_EVENTS as j, CodexAppServerEventProjector as k, CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS as l, resolveCodexTurnTerminalIdleTimeoutMs as m, handleCodexAppServerApprovalRequest as n, sanitizeCodexToolResponse as nt, emitDynamicToolTerminalDiagnostic as o, resolveCodexTurnCompletionIdleTimeoutMs as p, shouldReleaseTurnAfterTerminalDynamicTool as q, handleCodexAppServerElicitationRequest as r, resolveCodexLocalRuntimeAttribution as rt, resolveCodexProviderWebSearchSupport as s, createCodexDynamicToolBridge as t, sanitizeCodexToolArguments as tt, resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs as u, disableCodexPluginThreadConfig as v, resolveCodexSandboxEnvironmentSelection as w, resolveCodexAppServerHookChannelId as x, formatCodexDynamicToolBuildStageSummary as y, scheduleCodexNativeHookRelayUnregister as z };
6694
+ export { resolveCodexToolProgressDetailMode as $, CODEX_NATIVE_HOOK_RELAY_EVENTS as A, hasPendingDynamicToolTerminalDiagnostic as B, CodexAppServerEventProjector as C, markCodexAuthProfileBlockedFromRateLimits as D, isCodexUsageLimitPromptError as E, emitCodexNativePreToolUseFailureDiagnostic as F, shouldBlockTerminalReleaseForNonTerminalDynamicToolResult as G, isMatchingDynamicToolTerminalDiagnostic as H, resolveCodexNativeHookRelayEvents as I, toCodexDynamicToolProtocolResponse as J, shouldReleaseTurnAfterTerminalDynamicTool as K, resolveCodexNativeHookRelayTtlMs as L, buildCodexNativeHookRelayConfig as M, buildCodexNativeHookRelayDisabledConfig as N, refreshCodexUsageLimitPromptError as O, createCodexNativeHookRelay as P, inferCodexDynamicToolMeta as Q, scheduleCodexNativeHookRelayUnregister as R, filterToolsForVisionInputs as S, formatCodexTurnStartUsageLimitError as T, resolveDynamicToolCallTimeoutMs as U, isDynamicToolTerminalDiagnosticEvent as V, resolveTerminalDynamicToolBatchAction as W, readCodexMirroredSessionHistoryMessages as X, resolveCodexToolAbortTerminalReason as Y, shouldEmitTranscriptToolProgress as Z, resolveCodexMessageToolProvider as _, emitDynamicToolStartedDiagnostic as a, shouldRequireCodexSandboxExecServerEnvironment as b, resolveCodexProviderWebSearchSupportForClient as c, createCodexDynamicToolBuildStageTracker as d, sanitizeCodexToolArguments as et, disableCodexPluginThreadConfig as f, resolveCodexExternalSandboxPolicyForOpenClawSandbox as g, resolveCodexAppServerHookChannelId as h, emitDynamicToolErrorDiagnostic as i, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as j, CodexNativeToolLifecycleProjector as k, settleCodexSourceReplyFinality as l, resolveCodexAppServerExecutionCwd as m, handleCodexAppServerApprovalRequest as n, redactCodexEventKind as nt, emitDynamicToolTerminalDiagnostic as o, formatCodexDynamicToolBuildStageSummary as p, toCodexDynamicToolProgressResponse as q, handleCodexAppServerElicitationRequest as r, resolveCodexLocalRuntimeAttribution as rt, resolveCodexProviderWebSearchSupport as s, createCodexDynamicToolBridge as t, sanitizeCodexToolResponse as tt, buildDynamicTools as u, resolveCodexSandboxEnvironmentSelection as v, createCodexUsageLimitPromptError as w, shouldWarnCodexDynamicToolBuildStageSummary as x, shouldEnableCodexAppServerNativeToolSurface as y, handleDynamicToolCallWithTimeout as z };