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

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-Scc-Wevo.js} +1 -1
  3. package/dist/{attempt-notifications-DEv9h7iC.js → attempt-notifications-BFmNhqFl.js} +3 -6
  4. package/dist/cli-metadata.js +1 -1
  5. package/dist/{command-formatters-D6ZlGNmb.js → command-formatters-Dq9InZUK.js} +34 -3
  6. package/dist/{command-handlers-Cpl9fUWv.js → command-handlers-IgqmL_kv.js} +10 -13
  7. package/dist/{compact-FnJmiPNA.js → compact-DCTmg3id.js} +4 -5
  8. package/dist/{computer-use-DVzr8OP1.js → computer-use-CPcU8TjG.js} +4 -4
  9. package/dist/{conversation-control-DtQ-E7mr.js → conversation-control-CGkBlfc5.js} +5 -7
  10. package/dist/doctor-contract-api.js +1 -1
  11. package/dist/{dynamic-tools-BMLoaTeG.js → dynamic-tools-C_1tEs34.js} +608 -330
  12. package/dist/harness.js +9 -5
  13. package/dist/index.js +52 -31
  14. package/dist/{media-understanding-provider-D580L0P8.js → media-understanding-provider-D31dOJwb.js} +11 -12
  15. package/dist/media-understanding-provider.js +1 -1
  16. package/dist/{models-DuKzZA6G.js → models-uh26C8QU.js} +5 -9
  17. package/dist/{notification-correlation-DfaCm0mx.js → notification-correlation-KmfV4EkP.js} +3 -3
  18. package/dist/{plugin-app-cache-key-BKNjHMEs.js → plugin-app-cache-key-Ceb-lm2c.js} +2 -2
  19. package/dist/{rate-limits-Dhp04Rqo.js → rate-limits-DyXaYAxU.js} +7 -6
  20. package/dist/{request-DborTWgw.js → request-B_oQsCXy.js} +54 -8
  21. package/dist/{run-attempt-VPVJoYDP.js → run-attempt-MLzoMe_m.js} +499 -295
  22. package/dist/{config-c48K5HP9.js → session-binding-h1mmCGnl.js} +786 -3
  23. package/dist/{session-catalog-7H112Tr_.js → session-catalog-CEvoXWHA.js} +45 -29
  24. package/dist/{session-cli-B28RhCyp.js → session-cli-XsEuWb_C.js} +1 -1
  25. package/dist/{shared-client-D4mFI9al.js → shared-client-JiAnW6pc.js} +255 -81
  26. package/dist/{side-question-CgJBz52s.js → side-question-DqDvIwSU.js} +15 -13
  27. package/dist/{thread-lifecycle-BgLXzjvV.js → thread-lifecycle-CN_pPtPh.js} +1787 -1579
  28. package/dist/{protocol-validators-dZQ-UTOa.js → transcript-mirror-D9rTxx2P.js} +485 -2
  29. package/dist/usage-D5Rohxoc.js +25 -0
  30. package/dist/{web-search-provider.runtime-Bs-eTn5U.js → web-search-provider.runtime-CKVBn3fP.js} +3 -3
  31. package/npm-shrinkwrap.json +30 -30
  32. package/openclaw.plugin.json +10 -5
  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,18 +1,16 @@
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-JiAnW6pc.js";
2
+ import { y as readCodexPluginConfig } from "./session-binding-h1mmCGnl.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-CN_pPtPh.js";
5
+ import { d as CODEX_CONTROL_METHODS, r as formatCodexDisplayText } from "./command-formatters-Dq9InZUK.js";
6
+ import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-DyXaYAxU.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-KmfV4EkP.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";
@@ -25,6 +23,7 @@ import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
25
23
  import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
26
24
  import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
27
25
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
26
+ import { normalizeOpenAIToolSchemas } from "openclaw/plugin-sdk/provider-tools";
28
27
  //#region extensions/codex/src/app-server/local-runtime-attribution.ts
29
28
  const OPENAI_PROVIDER_ID = "openai";
30
29
  const OPENAI_RESPONSES_API = "openai-responses";
@@ -67,6 +66,7 @@ function createAssistantMessage(params, text, options) {
67
66
  output: options.tokenUsage.output ?? 0,
68
67
  cacheRead: options.tokenUsage.cacheRead ?? 0,
69
68
  cacheWrite: options.tokenUsage.cacheWrite ?? 0,
69
+ ...options.tokenUsage.contextUsage ? { contextUsage: options.tokenUsage.contextUsage } : {},
70
70
  totalTokens: options.tokenUsage.total ?? (options.tokenUsage.input ?? 0) + (options.tokenUsage.output ?? 0) + (options.tokenUsage.cacheRead ?? 0) + (options.tokenUsage.cacheWrite ?? 0),
71
71
  cost: ZERO_USAGE$1.cost
72
72
  } : ZERO_USAGE$1;
@@ -103,7 +103,7 @@ function createAssistantMirrorMessage(params, title, text) {
103
103
  }
104
104
  //#endregion
105
105
  //#region extensions/codex/src/app-server/event-projector-values.ts
106
- function readString$2(record, key) {
106
+ function readString$3(record, key) {
107
107
  const value = record[key];
108
108
  return typeof value === "string" ? value : void 0;
109
109
  }
@@ -139,15 +139,15 @@ function readNonNegativeInteger(record, key) {
139
139
  }
140
140
  function readCodexErrorNotificationMessage(record) {
141
141
  const error = record.error;
142
- return isJsonObject(error) ? readString$2(error, "message") : void 0;
142
+ return isJsonObject(error) ? readString$3(error, "message") : void 0;
143
143
  }
144
144
  function readHookOutputEntries(value) {
145
145
  if (!Array.isArray(value)) return [];
146
146
  return value.flatMap((entry) => {
147
147
  if (!isJsonObject(entry)) return [];
148
- const text = readString$2(entry, "text");
148
+ const text = readString$3(entry, "text");
149
149
  if (!text) return [];
150
- const kind = readString$2(entry, "kind");
150
+ const kind = readString$3(entry, "kind");
151
151
  return [{
152
152
  ...kind ? { kind } : {},
153
153
  text
@@ -160,9 +160,9 @@ function splitPlanText(text) {
160
160
  function extractRawAssistantText(item) {
161
161
  return (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
162
162
  if (!isJsonObject(entry)) return [];
163
- const type = readString$2(entry, "type");
163
+ const type = readString$3(entry, "type");
164
164
  if (type !== "output_text" && type !== "text") return [];
165
- const value = readString$2(entry, "text");
165
+ const value = readString$3(entry, "text");
166
166
  return value ? [value] : [];
167
167
  }).join("").trim() || void 0;
168
168
  }
@@ -218,8 +218,8 @@ var CodexAssistantProjection = class {
218
218
  return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
219
219
  }
220
220
  async handleAssistantDelta(params) {
221
- const itemId = readString$2(params, "itemId") ?? "assistant";
222
- const delta = readString$2(params, "delta") ?? "";
221
+ const itemId = readString$3(params, "itemId") ?? "assistant";
222
+ const delta = readString$3(params, "delta") ?? "";
223
223
  if (!delta) return;
224
224
  if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
225
225
  const isCommentary = this.isCommentaryAssistantItem(itemId);
@@ -299,9 +299,9 @@ var CodexAssistantProjection = class {
299
299
  }
300
300
  }
301
301
  handleRawResponseItemCompleted(item, activeItemIds) {
302
- const role = readString$2(item, "role");
303
- const phase = readString$2(item, "phase");
304
- const rawItemId = readString$2(item, "id");
302
+ const role = readString$3(item, "role");
303
+ const phase = readString$3(item, "phase");
304
+ const rawItemId = readString$3(item, "id");
305
305
  const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
306
306
  const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
307
307
  const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
@@ -607,7 +607,7 @@ function collectDynamicToolContentText(contentItems) {
607
607
  if (!Array.isArray(contentItems)) return "";
608
608
  return contentItems.flatMap((entry) => {
609
609
  if (!isJsonObject(entry)) return [];
610
- const text = readString$2(entry, "text");
610
+ const text = readString$3(entry, "text");
611
611
  return text ? [text] : [];
612
612
  }).join("\n");
613
613
  }
@@ -956,7 +956,29 @@ var CodexToolProgressProjection = class {
956
956
  return this.sideEffectingNativeIds.size > 0 || this.sideEffectingDynamicIds.size > 0;
957
957
  }
958
958
  setLastToolError(error) {
959
- this.lastNativeToolError = error;
959
+ if (!error) {
960
+ this.lastNativeToolError = void 0;
961
+ return;
962
+ }
963
+ const terminalResolution = this.params.observeToolTerminal?.({
964
+ toolName: error.toolName,
965
+ ...error.meta ? { meta: error.meta } : {},
966
+ outcome: "failure",
967
+ failure: {
968
+ ...error.errorCode ? { errorCode: error.errorCode } : {},
969
+ ...error.error ? { error: error.error } : {},
970
+ ...error.validationErrorSummary ? { validationErrorSummary: error.validationErrorSummary } : {},
971
+ ...error.timedOut ? { timedOut: true } : {},
972
+ ...error.middlewareError ? { middlewareError: true } : {}
973
+ },
974
+ nativeMutation: {
975
+ mutatingAction: error.mutatingAction === true,
976
+ replaySafe: error.mutatingAction !== true,
977
+ ...error.actionFingerprint ? { actionFingerprint: error.actionFingerprint } : {},
978
+ ...error.fileTarget ? { fileTarget: error.fileTarget } : {}
979
+ }
980
+ });
981
+ this.lastNativeToolError = terminalResolution?.lastToolError ?? (this.lastNativeToolError?.mutatingAction && error.mutatingAction !== true ? this.lastNativeToolError : error);
960
982
  }
961
983
  recordDynamicToolResult(params) {
962
984
  const resultText = collectDynamicToolContentText(params.contentItems);
@@ -967,16 +989,17 @@ var CodexToolProgressProjection = class {
967
989
  ...params.asyncStarted === true ? { asyncStarted: true } : {},
968
990
  ...!params.success ? { isError: true } : {}
969
991
  });
970
- if (!params.success && params.terminalType === "blocked") this.lastNativeToolError = {
992
+ if (params.terminalResolution) this.lastNativeToolError = params.terminalResolution.lastToolError;
993
+ else if (!params.success) this.lastNativeToolError = {
971
994
  toolName: params.tool,
972
- error: resultText || "codex dynamic tool blocked"
995
+ error: resultText || (params.terminalType === "blocked" ? "codex dynamic tool blocked" : "codex dynamic tool failed")
973
996
  };
974
- else if (params.success && this.lastNativeToolError && !this.lastNativeToolError.mutatingAction) this.lastNativeToolError = void 0;
997
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
975
998
  if (params.sideEffectEvidence === true) this.sideEffectingDynamicIds.add(params.callId);
976
999
  }
977
1000
  handleOutputDelta(params, toolName) {
978
- const itemId = readString$2(params, "itemId");
979
- const delta = readString$2(params, "delta");
1001
+ const itemId = readString$3(params, "itemId");
1002
+ const delta = readString$3(params, "delta");
980
1003
  if (!itemId || !delta) return;
981
1004
  const storedOutput = this.output.append(itemId, delta);
982
1005
  this.rememberEcho(itemId, {
@@ -1017,25 +1040,37 @@ var CodexToolProgressProjection = class {
1017
1040
  });
1018
1041
  }
1019
1042
  recordNativeToolError(params) {
1020
- if (!isNonSuccessItemStatus(params.status)) {
1021
- if (!this.lastNativeToolError) return;
1022
- if (!this.lastNativeToolError.mutatingAction) {
1023
- this.lastNativeToolError = void 0;
1024
- return;
1043
+ const executionStarted = params.status !== "blocked";
1044
+ const mutatingAction = executionStarted && isMutatingNativeToolItem(params.item);
1045
+ const actionFingerprint = mutatingAction ? nativeToolActionFingerprint(params.item) : void 0;
1046
+ const isFailure = isNonSuccessItemStatus(params.status);
1047
+ const error = isFailure ? itemToolError(params.item, params.status, this.output.textByItem) : void 0;
1048
+ const terminalResolution = this.params.observeToolTerminal?.({
1049
+ toolCallId: params.item.id,
1050
+ toolName: params.name,
1051
+ arguments: itemToolArgs(params.item),
1052
+ ...params.meta ? { meta: params.meta } : {},
1053
+ executionStarted,
1054
+ outcome: isFailure ? "failure" : "success",
1055
+ ...isFailure ? { failure: error ? { error } : {} } : {},
1056
+ nativeMutation: {
1057
+ mutatingAction,
1058
+ replaySafe: !mutatingAction,
1059
+ ...actionFingerprint ? { actionFingerprint } : {}
1025
1060
  }
1026
- const actionFingerprint = nativeToolActionFingerprint(params.item);
1027
- if (this.lastNativeToolError.actionFingerprint && actionFingerprint && this.lastNativeToolError.actionFingerprint === actionFingerprint) this.lastNativeToolError = void 0;
1061
+ });
1062
+ if (terminalResolution) {
1063
+ this.lastNativeToolError = terminalResolution.lastToolError;
1028
1064
  return;
1029
1065
  }
1030
- const error = itemToolError(params.item, params.status, this.output.textByItem);
1031
- const actionFingerprint = nativeToolActionFingerprint(params.item);
1032
- this.lastNativeToolError = {
1066
+ if (isFailure) this.lastNativeToolError = {
1033
1067
  toolName: params.name,
1034
1068
  ...params.meta ? { meta: params.meta } : {},
1035
1069
  ...error ? { error } : {},
1036
- ...isMutatingNativeToolItem(params.item) ? { mutatingAction: true } : {},
1070
+ ...mutatingAction ? { mutatingAction: true } : {},
1037
1071
  ...actionFingerprint ? { actionFingerprint } : {}
1038
1072
  };
1073
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
1039
1074
  }
1040
1075
  emitToolResultSummary(item) {
1041
1076
  if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
@@ -1202,7 +1237,8 @@ async function readCodexMirroredSessionHistoryMessages(target) {
1202
1237
  const entries = await readCodexMirroredSessionEntries(target);
1203
1238
  if (entries.length === 0) return [];
1204
1239
  const firstEntry = entries[0];
1205
- if (firstEntry?.type !== "session" || typeof firstEntry.id !== "string") return;
1240
+ if (firstEntry?.type !== "session") return [];
1241
+ if (typeof firstEntry.id !== "string") return;
1206
1242
  migrateSessionEntries(entries);
1207
1243
  return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => {
1208
1244
  return entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.type !== "session";
@@ -1533,14 +1569,14 @@ var CodexEventProjection = class {
1533
1569
  data: {
1534
1570
  method,
1535
1571
  phase: method.endsWith("/started") ? "started" : "completed",
1536
- reviewId: readString$2(params, "reviewId"),
1572
+ reviewId: readString$3(params, "reviewId"),
1537
1573
  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,
1574
+ decisionSource: readString$3(params, "decisionSource"),
1575
+ status: review ? readString$3(review, "status") : void 0,
1576
+ riskLevel: review ? readString$3(review, "riskLevel") : void 0,
1577
+ userAuthorization: review ? readString$3(review, "userAuthorization") : void 0,
1542
1578
  rationale: review ? readNullableString(review, "rationale") : void 0,
1543
- actionType: action ? readString$2(action, "type") : void 0
1579
+ actionType: action ? readString$3(action, "type") : void 0
1544
1580
  }
1545
1581
  });
1546
1582
  }
@@ -1549,7 +1585,7 @@ var CodexEventProjection = class {
1549
1585
  stream: "codex_app_server.guardian",
1550
1586
  data: {
1551
1587
  phase: "warning",
1552
- message: readString$2(params, "message")
1588
+ message: readString$3(params, "message")
1553
1589
  }
1554
1590
  });
1555
1591
  }
@@ -1565,14 +1601,14 @@ var CodexEventProjection = class {
1565
1601
  phase: method === "hook/started" ? "started" : "completed",
1566
1602
  threadId: this.threadId,
1567
1603
  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"),
1604
+ hookRunId: readString$3(run, "id"),
1605
+ eventName: readString$3(run, "eventName"),
1606
+ handlerType: readString$3(run, "handlerType"),
1607
+ executionMode: readString$3(run, "executionMode"),
1608
+ scope: readString$3(run, "scope"),
1609
+ source: readString$3(run, "source"),
1610
+ sourcePath: readString$3(run, "sourcePath"),
1611
+ status: readString$3(run, "status"),
1576
1612
  statusMessage: readNullableString(run, "statusMessage"),
1577
1613
  ...durationMs !== void 0 ? { durationMs } : {},
1578
1614
  ...entries.length > 0 ? { entries } : {}
@@ -1673,10 +1709,10 @@ var CodexGeneratedMediaProjection = class {
1673
1709
  });
1674
1710
  }
1675
1711
  async recordRaw(item) {
1676
- if (readString$2(item, "type") !== "image_generation_call") return;
1677
- const result = readString$2(item, "result");
1712
+ if (readString$3(item, "type") !== "image_generation_call") return;
1713
+ const result = readString$3(item, "result");
1678
1714
  if (!result) return;
1679
- const itemId = readString$2(item, "id") ?? `raw-image-${this.itemIds.size}`;
1715
+ const itemId = readString$3(item, "id") ?? `raw-image-${this.itemIds.size}`;
1680
1716
  this.itemIds.add(itemId);
1681
1717
  const maxBytes = resolveGeneratedImageMaxBytes(this.config);
1682
1718
  const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
@@ -1691,7 +1727,7 @@ var CodexGeneratedMediaProjection = class {
1691
1727
  const asset = generatedImageAssetFromBase64({
1692
1728
  base64: result,
1693
1729
  index: this.itemIds.size,
1694
- revisedPrompt: readString$2(item, "revised_prompt") ?? readString$2(item, "revisedPrompt"),
1730
+ revisedPrompt: readString$3(item, "revised_prompt") ?? readString$3(item, "revisedPrompt"),
1695
1731
  fileNamePrefix: "codex-image-generation",
1696
1732
  sniffMimeType: true
1697
1733
  });
@@ -1749,6 +1785,90 @@ function resolveGeneratedImageMaxBytes(config) {
1749
1785
  return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
1750
1786
  }
1751
1787
  //#endregion
1788
+ //#region extensions/codex/src/app-server/dynamic-tool-response-state.ts
1789
+ function withDynamicToolTerminalResolution(response, terminalResolution) {
1790
+ if (terminalResolution) {
1791
+ Object.defineProperties(response, {
1792
+ terminalResolution: {
1793
+ configurable: true,
1794
+ enumerable: false,
1795
+ value: terminalResolution
1796
+ },
1797
+ executionStarted: {
1798
+ configurable: true,
1799
+ enumerable: false,
1800
+ value: terminalResolution.executionStarted
1801
+ },
1802
+ ...terminalResolution.executedArguments ? { executedArguments: {
1803
+ configurable: true,
1804
+ enumerable: false,
1805
+ value: terminalResolution.executedArguments
1806
+ } } : {}
1807
+ });
1808
+ withDynamicToolSideEffectEvidence(response, terminalResolution.sideEffectEvidence);
1809
+ }
1810
+ return response;
1811
+ }
1812
+ function withDynamicToolExecutionState(response, state) {
1813
+ Object.defineProperties(response, {
1814
+ executedArguments: {
1815
+ configurable: true,
1816
+ enumerable: false,
1817
+ value: state.executedArguments
1818
+ },
1819
+ executionStarted: {
1820
+ configurable: true,
1821
+ enumerable: false,
1822
+ value: state.executionStarted
1823
+ }
1824
+ });
1825
+ return withDynamicToolSideEffectEvidence(response, state.sideEffectEvidence === true);
1826
+ }
1827
+ function withDynamicToolSideEffectEvidence(response, sideEffectEvidence) {
1828
+ if (!sideEffectEvidence) {
1829
+ delete response.sideEffectEvidence;
1830
+ return response;
1831
+ }
1832
+ Object.defineProperty(response, "sideEffectEvidence", {
1833
+ configurable: true,
1834
+ enumerable: false,
1835
+ value: true
1836
+ });
1837
+ return response;
1838
+ }
1839
+ function createFailedDynamicToolResponse(message, options) {
1840
+ const response = {
1841
+ contentItems: [{
1842
+ type: "inputText",
1843
+ text: message
1844
+ }],
1845
+ success: false
1846
+ };
1847
+ Object.defineProperties(response, {
1848
+ diagnosticTerminalReason: {
1849
+ configurable: true,
1850
+ enumerable: false,
1851
+ value: options?.terminalReason ?? "failed"
1852
+ },
1853
+ diagnosticTerminalType: {
1854
+ configurable: true,
1855
+ enumerable: false,
1856
+ value: "error"
1857
+ }
1858
+ });
1859
+ if (options?.executionStarted !== void 0) Object.defineProperty(response, "executionStarted", {
1860
+ configurable: true,
1861
+ enumerable: false,
1862
+ value: options.executionStarted
1863
+ });
1864
+ if (options?.executedArguments !== void 0) Object.defineProperty(response, "executedArguments", {
1865
+ configurable: true,
1866
+ enumerable: false,
1867
+ value: options.executedArguments
1868
+ });
1869
+ return withDynamicToolSideEffectEvidence(response, options?.sideEffectEvidence === true);
1870
+ }
1871
+ //#endregion
1752
1872
  //#region extensions/codex/src/app-server/tool-abort-terminal-reason.ts
1753
1873
  /** Leaf helper shared by native and dynamic tool diagnostics. */
1754
1874
  const CODEX_TIMEOUT_ABORT_REASONS = /* @__PURE__ */ new Set([
@@ -1846,6 +1966,30 @@ function formatDynamicToolTimeoutDetails(params) {
1846
1966
  */
1847
1967
  async function handleDynamicToolCallWithTimeout(params) {
1848
1968
  let didNotifyAgentToolResult = false;
1969
+ const conservativeRaceResponses = /* @__PURE__ */ new WeakSet();
1970
+ const finalizeTerminal = (response) => {
1971
+ const executionSnapshot = params.toolBridge.consumeToolExecutionSnapshot?.(params.call.callId);
1972
+ const observedExecutionStarted = executionSnapshot?.executionStarted ?? (conservativeRaceResponses.has(response) ? void 0 : response.executionStarted);
1973
+ const terminalResolution = params.observeToolTerminal?.({
1974
+ toolCallId: params.call.callId,
1975
+ toolName: params.call.tool,
1976
+ arguments: response.executedArguments ?? executionSnapshot?.executedArguments ?? params.call.arguments,
1977
+ ...params.toolMeta ? { meta: params.toolMeta } : {},
1978
+ ...observedExecutionStarted !== void 0 ? { executionStarted: observedExecutionStarted } : {},
1979
+ outcome: response.success ? "success" : "failure",
1980
+ ...!response.success ? { failure: { error: readDynamicToolResponseText(response) } } : {}
1981
+ });
1982
+ return withDynamicToolTerminalResolution(response, terminalResolution);
1983
+ };
1984
+ const createFailedAfterPossibleDispatch = (message, terminalReason) => {
1985
+ const response = createFailedDynamicToolResponse(message, {
1986
+ executionStarted: true,
1987
+ sideEffectEvidence: true,
1988
+ terminalReason
1989
+ });
1990
+ conservativeRaceResponses.add(response);
1991
+ return response;
1992
+ };
1849
1993
  const notifyAgentToolResult = (event) => {
1850
1994
  if (didNotifyAgentToolResult) return;
1851
1995
  didNotifyAgentToolResult = true;
@@ -1876,7 +2020,10 @@ async function handleDynamicToolCallWithTimeout(params) {
1876
2020
  const terminalReason = resolveCodexToolAbortTerminalReason(params.signal);
1877
2021
  params.onFallbackSelected?.();
1878
2022
  notifyFailedToolResult(message, terminalReason);
1879
- return failedDynamicToolResponse(message, { terminalReason });
2023
+ return finalizeTerminal(createFailedDynamicToolResponse(message, {
2024
+ executionStarted: false,
2025
+ terminalReason
2026
+ }));
1880
2027
  }
1881
2028
  const controller = new AbortController();
1882
2029
  let timeout;
@@ -1888,10 +2035,7 @@ async function handleDynamicToolCallWithTimeout(params) {
1888
2035
  params.onFallbackSelected?.();
1889
2036
  controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
1890
2037
  notifyFailedToolResult(message, terminalReason);
1891
- resolveAbort?.(failedDynamicToolResponse(message, {
1892
- sideEffectEvidence: true,
1893
- terminalReason
1894
- }));
2038
+ resolveAbort?.(createFailedAfterPossibleDispatch(message, terminalReason));
1895
2039
  };
1896
2040
  const abortPromise = new Promise((resolve) => {
1897
2041
  resolveAbort = resolve;
@@ -1912,10 +2056,7 @@ async function handleDynamicToolCallWithTimeout(params) {
1912
2056
  consoleMessage: timeoutDetails.consoleMessage
1913
2057
  });
1914
2058
  notifyFailedToolResult(timeoutDetails.responseMessage, "timed_out");
1915
- resolve(failedDynamicToolResponse(timeoutDetails.responseMessage, {
1916
- sideEffectEvidence: true,
1917
- terminalReason: "timed_out"
1918
- }));
2059
+ resolve(createFailedAfterPossibleDispatch(timeoutDetails.responseMessage, "timed_out"));
1919
2060
  }, timeoutMs);
1920
2061
  timeout.unref?.();
1921
2062
  });
@@ -1926,21 +2067,19 @@ async function handleDynamicToolCallWithTimeout(params) {
1926
2067
  params.toolBridge.handleToolCall(params.call, {
1927
2068
  signal: controller.signal,
1928
2069
  onAgentToolResult: notifyAgentToolResult,
1929
- toolCallOrdinal: params.toolCallOrdinal
2070
+ toolCallOrdinal: params.toolCallOrdinal,
2071
+ retainExecutionSnapshot: true
1930
2072
  }),
1931
2073
  abortPromise,
1932
2074
  timeoutPromise
1933
2075
  ]);
1934
2076
  if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response), response.diagnosticTerminalReason ?? "failed");
1935
- return response;
2077
+ return finalizeTerminal(response);
1936
2078
  } catch (error) {
1937
2079
  const terminalReason = params.signal.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : resolveToolExecutionErrorKind(error);
1938
2080
  const message = formatToolExecutionErrorMessage(error, "OpenClaw dynamic tool call failed.");
1939
2081
  notifyFailedToolResult(message, terminalReason);
1940
- return failedDynamicToolResponse(message, {
1941
- sideEffectEvidence: true,
1942
- terminalReason
1943
- });
2082
+ return finalizeTerminal(createFailedAfterPossibleDispatch(message, terminalReason));
1944
2083
  } finally {
1945
2084
  if (timeout) clearTimeout(timeout);
1946
2085
  params.signal.removeEventListener("abort", abortFromRun);
@@ -1951,31 +2090,6 @@ async function handleDynamicToolCallWithTimeout(params) {
1951
2090
  function readDynamicToolResponseText(response) {
1952
2091
  return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
1953
2092
  }
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
2093
  /** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
1980
2094
  function toCodexDynamicToolProtocolResponse(response) {
1981
2095
  return {
@@ -2181,6 +2295,7 @@ function createCodexNativeHookRelay(params) {
2181
2295
  runId: params.runId,
2182
2296
  ...params.channelId ? { channelId: params.channelId } : {},
2183
2297
  allowedEvents: params.events,
2298
+ preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
2184
2299
  ttlMs: resolveCodexNativeHookRelayTtlMs({
2185
2300
  explicitTtlMs: params.options?.ttlMs,
2186
2301
  attemptTimeoutMs: params.attemptTimeoutMs,
@@ -2239,7 +2354,8 @@ function buildCodexNativeHookRelayConfig(params) {
2239
2354
  const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
2240
2355
  const selected = selectedEvents.has(event);
2241
2356
  const shouldRelay = params.relay.shouldRelayEvent(event);
2242
- if (!selected || !shouldRelay && !(selected && event === "pre_tool_use" && !shouldRelay)) {
2357
+ const selectedNoopPreToolUse = selected && event === "pre_tool_use" && !shouldRelay && params.loopDetectionPreToolUseRelay;
2358
+ if (!selected || !shouldRelay && !selectedNoopPreToolUse) {
2243
2359
  if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
2244
2360
  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
2361
  continue;
@@ -2382,12 +2498,12 @@ var CodexNativeToolLifecycleProjector = class {
2382
2498
  this.preToolUseFailureByItem.set(failure.toolCallId, record);
2383
2499
  }
2384
2500
  recordRawWebSearchResult(item) {
2385
- if (readString$2(item, "type") !== "web_search_call") return;
2386
- const toolCallId = readString$2(item, "id");
2501
+ if (readString$3(item, "type") !== "web_search_call") return;
2502
+ const toolCallId = readString$3(item, "id");
2387
2503
  if (!toolCallId || this.completedItemIds.has(toolCallId)) return;
2388
2504
  const toolName = "web_search";
2389
2505
  this.recordStarted(toolCallId, toolName, "unknown");
2390
- const rawStatus = readString$2(item, "status");
2506
+ const rawStatus = readString$3(item, "status");
2391
2507
  if (rawStatus === "in_progress" || rawStatus === "running") return;
2392
2508
  const status = rawStatus === "completed" ? "completed" : rawStatus === "cancelled" ? "cancelled" : rawStatus === "failed" || rawStatus === "error" || rawStatus === "incomplete" ? "failed" : "unknown";
2393
2509
  this.recordTerminal(toolCallId, toolName, status, { sourceTimestampMs: this.webSearchCompletionByItem.get(toolCallId)?.sourceTimestampMs });
@@ -2512,8 +2628,8 @@ var CodexReasoningProjection = class {
2512
2628
  this.reasoningEnded = false;
2513
2629
  }
2514
2630
  async handleReasoningDelta(method, params) {
2515
- const itemId = readString$2(params, "itemId") ?? "reasoning";
2516
- const delta = readString$2(params, "delta") ?? "";
2631
+ const itemId = readString$3(params, "itemId") ?? "reasoning";
2632
+ const delta = readString$3(params, "delta") ?? "";
2517
2633
  if (!delta) return;
2518
2634
  this.reasoningStarted = true;
2519
2635
  if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
@@ -2532,24 +2648,29 @@ var CodexReasoningProjection = class {
2532
2648
  });
2533
2649
  }
2534
2650
  handlePlanDelta(params) {
2535
- const itemId = readString$2(params, "itemId") ?? "plan";
2536
- const delta = readString$2(params, "delta") ?? "";
2651
+ const itemId = readString$3(params, "itemId") ?? "plan";
2652
+ const delta = readString$3(params, "delta") ?? "";
2537
2653
  if (!delta) return;
2538
2654
  const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
2539
2655
  this.planTextByItem.set(itemId, text);
2540
2656
  this.emitPlanUpdate({
2541
2657
  explanation: void 0,
2542
- steps: splitPlanText(text)
2658
+ steps: splitPlanText(text).map((step) => ({
2659
+ step,
2660
+ status: "pending"
2661
+ }))
2543
2662
  });
2544
2663
  }
2545
2664
  handleTurnPlanUpdated(params) {
2546
2665
  const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
2547
2666
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
2548
2667
  const record = entry;
2549
- const step = readString$2(record, "step");
2550
- const status = readString$2(record, "status");
2668
+ const step = readString$3(record, "step");
2551
2669
  if (!step) return [];
2552
- return status ? [`${step} (${status})`] : [step];
2670
+ return [{
2671
+ step,
2672
+ status: normalizePlanStepStatus(readString$3(record, "status"))
2673
+ }];
2553
2674
  }) : void 0;
2554
2675
  this.emitPlanUpdate({
2555
2676
  explanation: readNullableString(params, "explanation"),
@@ -2561,7 +2682,10 @@ var CodexReasoningProjection = class {
2561
2682
  this.planTextByItem.set(item.id, item.text);
2562
2683
  this.emitPlanUpdate({
2563
2684
  explanation: void 0,
2564
- steps: splitPlanText(item.text)
2685
+ steps: splitPlanText(item.text).map((step) => ({
2686
+ step,
2687
+ status: "pending"
2688
+ }))
2565
2689
  });
2566
2690
  }
2567
2691
  }
@@ -2590,6 +2714,10 @@ var CodexReasoningProjection = class {
2590
2714
  });
2591
2715
  }
2592
2716
  };
2717
+ function normalizePlanStepStatus(status) {
2718
+ if (status === "inProgress" || status === "in_progress") return "in_progress";
2719
+ return status === "completed" ? "completed" : "pending";
2720
+ }
2593
2721
  function collectReasoningTextValues(groups, itemOrder) {
2594
2722
  return [...groups.values()].toSorted((left, right) => {
2595
2723
  const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
@@ -2603,15 +2731,187 @@ function reasoningMethodOrder(method) {
2603
2731
  }
2604
2732
  //#endregion
2605
2733
  //#region extensions/codex/src/app-server/event-projector-usage.ts
2606
- function normalizeCodexTokenUsage(record) {
2734
+ function readTokenCount(record, key) {
2735
+ const value = readNonNegativeInteger(record, key);
2736
+ return value !== void 0 && Number.isSafeInteger(value) ? value : void 0;
2737
+ }
2738
+ function normalizeCodexThreadTokenUsage(record) {
2607
2739
  const inputTokens = readNumber(record, "inputTokens");
2608
2740
  const cacheRead = readNumber(record, "cachedInputTokens");
2609
- return normalizeUsage({
2741
+ const usage = normalizeUsage({
2610
2742
  input: inputTokens !== void 0 && cacheRead !== void 0 ? Math.max(0, inputTokens - cacheRead) : inputTokens,
2611
2743
  output: readNumber(record, "outputTokens"),
2612
2744
  cacheRead,
2613
2745
  total: readNumber(record, "totalTokens")
2614
2746
  });
2747
+ return usage ? {
2748
+ ...usage,
2749
+ contextUsage: { state: "unavailable" }
2750
+ } : void 0;
2751
+ }
2752
+ function normalizeCodexResponseTokenUsage(record) {
2753
+ const totalTokens = readTokenCount(record, "totalTokens");
2754
+ const inputTokens = readTokenCount(record, "inputTokens");
2755
+ const cacheRead = readTokenCount(record, "cachedInputTokens");
2756
+ const output = readTokenCount(record, "outputTokens");
2757
+ const reasoningOutput = readTokenCount(record, "reasoningOutputTokens");
2758
+ const cacheWrite = record.cacheWriteInputTokens === void 0 ? 0 : readTokenCount(record, "cacheWriteInputTokens");
2759
+ 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;
2760
+ const usage = normalizeUsage({
2761
+ input: inputTokens - cacheRead - cacheWrite,
2762
+ output,
2763
+ cacheRead,
2764
+ cacheWrite,
2765
+ total: totalTokens
2766
+ });
2767
+ if (!usage) return;
2768
+ return {
2769
+ ...usage,
2770
+ contextUsage: {
2771
+ state: "available",
2772
+ promptTokens: inputTokens,
2773
+ totalTokens
2774
+ }
2775
+ };
2776
+ }
2777
+ //#endregion
2778
+ //#region extensions/codex/src/app-server/usage-limit-error.ts
2779
+ /**
2780
+ * Enriches Codex usage-limit failures with current rate-limit information and
2781
+ * marks blocked auth profiles when Codex exposes a reset time.
2782
+ */
2783
+ const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
2784
+ function createCodexUsageLimitPromptError(message) {
2785
+ return Object.assign(new Error(message), { status: 429 });
2786
+ }
2787
+ function isCodexUsageLimitPromptError(error) {
2788
+ return error instanceof Error && "status" in error && error.status === 429;
2789
+ }
2790
+ /** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
2791
+ async function markCodexAuthProfileBlockedFromRateLimits(params) {
2792
+ const authProfileId = params.authProfileId?.trim();
2793
+ if (!authProfileId || !params.params.authProfileStore) return;
2794
+ const blockedUntil = resolveCodexUsageLimitResetAtMs(params.rateLimits);
2795
+ if (!blockedUntil) return;
2796
+ try {
2797
+ await markAuthProfileBlockedUntil({
2798
+ store: params.params.authProfileStore,
2799
+ profileId: authProfileId,
2800
+ blockedUntil,
2801
+ source: "codex_rate_limits",
2802
+ agentDir: params.params.agentDir,
2803
+ runId: params.params.runId,
2804
+ modelId: params.params.modelId
2805
+ });
2806
+ } catch (error) {
2807
+ embeddedAgentLog.debug("failed to mark Codex auth profile blocked from app-server limits", {
2808
+ authProfileId,
2809
+ error: formatErrorMessage(error)
2810
+ });
2811
+ }
2812
+ }
2813
+ /** Formats a turn-start usage-limit error, refreshing rate limits when needed. */
2814
+ async function formatCodexTurnStartUsageLimitError(params) {
2815
+ return refreshCodexUsageLimitError({
2816
+ client: params.client,
2817
+ source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
2818
+ timeoutMs: params.timeoutMs,
2819
+ signal: params.signal
2820
+ });
2821
+ }
2822
+ /** Refreshes a generic prompt usage-limit message into a reset-aware message. */
2823
+ async function refreshCodexUsageLimitPromptError(params) {
2824
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(params.message)) return;
2825
+ return refreshCodexUsageLimitError({
2826
+ client: params.client,
2827
+ source: {
2828
+ message: params.message,
2829
+ codexErrorInfo: "usageLimitExceeded",
2830
+ rateLimits: readRecentCodexRateLimits(params.client)
2831
+ },
2832
+ timeoutMs: params.timeoutMs,
2833
+ signal: params.signal
2834
+ });
2835
+ }
2836
+ async function refreshCodexUsageLimitError(params) {
2837
+ const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
2838
+ if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
2839
+ message: initialMessage,
2840
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
2841
+ } : void 0;
2842
+ const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
2843
+ client: params.client,
2844
+ timeoutMs: params.timeoutMs,
2845
+ signal: params.signal
2846
+ });
2847
+ if (!rateLimits) return initialMessage ? {
2848
+ message: initialMessage,
2849
+ ...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
2850
+ } : void 0;
2851
+ const message = formatCodexUsageLimitErrorMessage({
2852
+ message: params.source.message,
2853
+ codexErrorInfo: params.source.codexErrorInfo,
2854
+ rateLimits
2855
+ }) ?? initialMessage;
2856
+ return message ? {
2857
+ message,
2858
+ rateLimitsForProfile: rateLimits
2859
+ } : void 0;
2860
+ }
2861
+ async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
2862
+ if (params.signal?.aborted) return;
2863
+ try {
2864
+ const rateLimits = await params.client.request(CODEX_CONTROL_METHODS.rateLimits, void 0, {
2865
+ timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
2866
+ signal: params.signal
2867
+ });
2868
+ rememberCodexRateLimitsRead(params.client, rateLimits);
2869
+ return rateLimits;
2870
+ } catch (error) {
2871
+ embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
2872
+ return;
2873
+ }
2874
+ }
2875
+ function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
2876
+ if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
2877
+ return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
2878
+ }
2879
+ function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
2880
+ const notificationError = readCodexErrorNotification(errorNotification);
2881
+ const errorPayload = readCodexErrorPayload(error);
2882
+ const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
2883
+ const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
2884
+ return {
2885
+ message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
2886
+ codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
2887
+ rateLimits,
2888
+ rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
2889
+ };
2890
+ }
2891
+ function readCodexErrorNotification(notification) {
2892
+ if (notification?.method !== "error" || !isJsonObject(notification.params)) return;
2893
+ const error = notification.params.error;
2894
+ return isJsonObject(error) ? {
2895
+ message: readString$2(error, "message"),
2896
+ codexErrorInfo: error.codexErrorInfo
2897
+ } : void 0;
2898
+ }
2899
+ function readCodexErrorPayload(error) {
2900
+ const message = error instanceof Error ? error.message : void 0;
2901
+ if (!error || typeof error !== "object" || !("data" in error)) return { message };
2902
+ const data = error.data;
2903
+ if (!isJsonObject(data)) return { message };
2904
+ const nestedError = isJsonObject(data.error) ? data.error : data;
2905
+ const rateLimits = nestedError.rateLimits ?? data.rateLimits;
2906
+ return {
2907
+ message: readString$2(nestedError, "message") ?? message,
2908
+ codexErrorInfo: nestedError.codexErrorInfo,
2909
+ rateLimits
2910
+ };
2911
+ }
2912
+ function readString$2(record, key) {
2913
+ const value = record[key];
2914
+ return typeof value === "string" ? value : void 0;
2615
2915
  }
2616
2916
  //#endregion
2617
2917
  //#region extensions/codex/src/app-server/event-projector.ts
@@ -2725,10 +3025,14 @@ var CodexAppServerEventProjector = class {
2725
3025
  case "turn/completed":
2726
3026
  await this.handleTurnCompleted(params);
2727
3027
  break;
3028
+ case "rawResponse/completed":
3029
+ this.handleRawResponseCompleted(params);
3030
+ break;
2728
3031
  case "rawResponseItem/completed":
2729
3032
  await this.handleRawResponseItemCompleted(params);
2730
3033
  break;
2731
3034
  case "error":
3035
+ this.responseUsage = void 0;
2732
3036
  if (params.willRetry === true) break;
2733
3037
  this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
2734
3038
  this.promptErrorSource = "prompt";
@@ -2741,6 +3045,7 @@ var CodexAppServerEventProjector = class {
2741
3045
  const assistantTexts = this.assistantProjection.collectAssistantTexts();
2742
3046
  const reasoningText = this.reasoningProjection.reasoningText();
2743
3047
  const planText = this.reasoningProjection.planText();
3048
+ const projectedUsage = this.aborted ? this.tokenUsage : this.responseUsage ?? this.tokenUsage;
2744
3049
  const hasAssistantItemText = this.assistantProjection.hasAssistantItemTextForSynthesis();
2745
3050
  const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
2746
3051
  const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
@@ -2753,7 +3058,7 @@ var CodexAppServerEventProjector = class {
2753
3058
  this.promptErrorSource = this.promptErrorSource ?? "prompt";
2754
3059
  }
2755
3060
  const assistantMessageOptions = {
2756
- tokenUsage: this.tokenUsage,
3061
+ tokenUsage: projectedUsage,
2757
3062
  aborted: this.aborted,
2758
3063
  promptError: this.promptError
2759
3064
  };
@@ -2806,7 +3111,7 @@ var CodexAppServerEventProjector = class {
2806
3111
  toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
2807
3112
  successfulCronAdds: toolTelemetry.successfulCronAdds,
2808
3113
  cloudCodeAssistFormatError: false,
2809
- attemptUsage: this.tokenUsage,
3114
+ attemptUsage: projectedUsage,
2810
3115
  replayMetadata: {
2811
3116
  hadPotentialSideEffects,
2812
3117
  replaySafe: !hadPotentialSideEffects
@@ -2835,13 +3140,14 @@ var CodexAppServerEventProjector = class {
2835
3140
  }
2836
3141
  markAborted() {
2837
3142
  this.aborted = true;
3143
+ this.responseUsage = void 0;
2838
3144
  }
2839
3145
  isCompacting() {
2840
3146
  return this.activeCompactionItemIds.size > 0;
2841
3147
  }
2842
3148
  async handleItemStarted(params) {
2843
3149
  const item = readItem(params.item);
2844
- const itemId = item?.id ?? readString$2(params, "itemId");
3150
+ const itemId = item?.id ?? readString$3(params, "itemId");
2845
3151
  this.assistantProjection.recordItemStarted(item, itemId);
2846
3152
  if (itemId) this.activeItemIds.add(itemId);
2847
3153
  this.recordNativeToolOutcome(item);
@@ -2896,7 +3202,7 @@ var CodexAppServerEventProjector = class {
2896
3202
  const item = readItem(params.item);
2897
3203
  this.recordNativeToolOutcome(item);
2898
3204
  this.clearTerminalPresentationForNativeItem(item);
2899
- const itemId = item?.id ?? readString$2(params, "itemId");
3205
+ const itemId = item?.id ?? readString$3(params, "itemId");
2900
3206
  if (itemId) {
2901
3207
  this.activeItemIds.delete(itemId);
2902
3208
  this.completedItemIds.add(itemId);
@@ -2962,19 +3268,25 @@ var CodexAppServerEventProjector = class {
2962
3268
  const tokenUsage = isJsonObject(params.tokenUsage) ? params.tokenUsage : void 0;
2963
3269
  const last = tokenUsage && isJsonObject(tokenUsage.last) ? tokenUsage.last : void 0;
2964
3270
  if (!last) return;
2965
- const usage = normalizeCodexTokenUsage(last);
3271
+ const usage = normalizeCodexThreadTokenUsage(last);
2966
3272
  if (usage) this.tokenUsage = usage;
2967
3273
  }
3274
+ handleRawResponseCompleted(params) {
3275
+ const usage = isJsonObject(params.usage) ? params.usage : void 0;
3276
+ this.responseUsage = usage ? normalizeCodexResponseTokenUsage(usage) : void 0;
3277
+ }
2968
3278
  async handleTurnCompleted(params) {
2969
3279
  const turn = readCodexTurn(params.turn);
2970
3280
  if (!turn || turn.id !== this.turnId) return;
2971
3281
  this.completedTurn = turn;
3282
+ if (turn.status !== "completed") this.responseUsage = void 0;
2972
3283
  if (turn.status === "failed") {
2973
- this.promptError = formatCodexUsageLimitErrorMessage({
3284
+ const usageLimitMessage = formatCodexUsageLimitErrorMessage({
2974
3285
  message: turn.error?.message,
2975
3286
  codexErrorInfo: turn.error?.codexErrorInfo,
2976
3287
  rateLimits: this.options.readRecentRateLimits?.()
2977
- }) ?? turn.error?.message ?? "codex app-server turn failed";
3288
+ });
3289
+ this.promptError = usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : turn.error?.message ?? "codex app-server turn failed";
2978
3290
  this.promptErrorSource = "prompt";
2979
3291
  }
2980
3292
  const turnItems = turn.items ?? [];
@@ -3051,11 +3363,12 @@ var CodexAppServerEventProjector = class {
3051
3363
  }
3052
3364
  formatCodexErrorMessage(params) {
3053
3365
  const error = isJsonObject(params.error) ? params.error : void 0;
3054
- return formatCodexUsageLimitErrorMessage({
3055
- message: error ? readString$2(error, "message") : void 0,
3366
+ const usageLimitMessage = formatCodexUsageLimitErrorMessage({
3367
+ message: error ? readString$3(error, "message") : void 0,
3056
3368
  codexErrorInfo: error?.codexErrorInfo,
3057
3369
  rateLimits: this.options.readRecentRateLimits?.()
3058
- }) ?? readCodexErrorNotificationMessage(params);
3370
+ });
3371
+ return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
3059
3372
  }
3060
3373
  emitAgentEvent(event) {
3061
3374
  try {
@@ -3083,7 +3396,7 @@ var CodexAppServerEventProjector = class {
3083
3396
  return threadId === this.threadId && turnId === this.turnId;
3084
3397
  }
3085
3398
  isHookNotificationForCurrentThread(params) {
3086
- const threadId = readString$2(params, "threadId");
3399
+ const threadId = readString$3(params, "threadId");
3087
3400
  const turnId = params.turnId;
3088
3401
  return threadId === this.threadId && (turnId === this.turnId || turnId === null);
3089
3402
  }
@@ -3121,12 +3434,72 @@ function addCodexMessageToolOnlyFinalParameter(parameters) {
3121
3434
  ...rawProperties,
3122
3435
  final: {
3123
3436
  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."
3437
+ 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
3438
  }
3126
3439
  }
3127
3440
  };
3128
3441
  }
3129
3442
  //#endregion
3443
+ //#region extensions/codex/src/app-server/shell-dynamic-tools.ts
3444
+ const CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME = "node_exec";
3445
+ const CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME = "node_process";
3446
+ const CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES = /* @__PURE__ */ new Set([
3447
+ "host",
3448
+ "security",
3449
+ "ask"
3450
+ ]);
3451
+ /** Returns true when plugin config explicitly removes any named dynamic tool. */
3452
+ function isCodexDynamicToolExcluded(config, names) {
3453
+ const normalizedNames = new Set(names.map((name) => normalizeCodexDynamicToolName(name)));
3454
+ return (config.codexDynamicToolsExclude ?? []).some((name) => normalizedNames.has(normalizeCodexDynamicToolName(name)));
3455
+ }
3456
+ function createNodeExecDynamicTool(execTool, configuredNode) {
3457
+ const pinnedNode = configuredNode?.trim();
3458
+ return {
3459
+ ...execTool,
3460
+ name: CODEX_NODE_EXEC_DYNAMIC_TOOL_NAME,
3461
+ 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.",
3462
+ parameters: hideNodeExecDynamicToolParameters(execTool.parameters, { hideNode: Boolean(pinnedNode) }),
3463
+ execute: async (toolCallId, args, signal, onUpdate) => {
3464
+ const result = await execTool.execute(toolCallId, pinNodeExecDynamicToolArgs(args, pinnedNode), signal, onUpdate);
3465
+ return {
3466
+ ...result,
3467
+ 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)
3468
+ };
3469
+ }
3470
+ };
3471
+ }
3472
+ function createNodeProcessDynamicTool(processTool) {
3473
+ return {
3474
+ ...processTool,
3475
+ name: CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME,
3476
+ 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."
3477
+ };
3478
+ }
3479
+ function pinNodeExecDynamicToolArgs(args, configuredNode) {
3480
+ const { host: _host, security: _security, ask: _ask, node: requestedNode, ...rest } = args && typeof args === "object" && !Array.isArray(args) ? args : {};
3481
+ const node = configuredNode ?? (typeof requestedNode === "string" ? requestedNode.trim() : "");
3482
+ return {
3483
+ ...rest,
3484
+ host: "node",
3485
+ ...node ? { node } : {}
3486
+ };
3487
+ }
3488
+ function hideNodeExecDynamicToolParameters(parameters, options) {
3489
+ if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return parameters;
3490
+ const schema = parameters;
3491
+ const rawProperties = schema.properties;
3492
+ if (!rawProperties || typeof rawProperties !== "object" || Array.isArray(rawProperties)) return parameters;
3493
+ const nextProperties = Object.fromEntries(Object.entries(rawProperties).filter(([name]) => !CODEX_NODE_EXEC_POLICY_PARAMETER_NAMES.has(normalizeCodexDynamicToolName(name)) && !(options.hideNode && normalizeCodexDynamicToolName(name) === "node")));
3494
+ const rawRequired = schema.required;
3495
+ 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;
3496
+ return {
3497
+ ...schema,
3498
+ properties: nextProperties,
3499
+ ...Array.isArray(rawRequired) ? { required: nextRequired } : {}
3500
+ };
3501
+ }
3502
+ //#endregion
3130
3503
  //#region extensions/codex/src/app-server/vision-tools.ts
3131
3504
  /**
3132
3505
  * Filters Codex dynamic tools for turns that already contain image inputs so
@@ -3153,13 +3526,6 @@ const CODEX_NATIVE_SANDBOX_TOOL_REQUIREMENTS = [
3153
3526
  "apply_patch"
3154
3527
  ];
3155
3528
  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
3529
  function preserveRingZeroSystemAgentTool(allTools, filteredTools) {
3164
3530
  const openclaw = allTools.find((tool) => tool.name === "openclaw" && tool.catalogMode === "direct-only");
3165
3531
  if (!openclaw) return filteredTools;
@@ -3248,8 +3614,8 @@ async function buildDynamicTools(input) {
3248
3614
  const toolBuildStages = createCodexDynamicToolBuildStageTracker({ enabled: input.profilerEnabled });
3249
3615
  const modelHasVision = params.model.input?.includes("image") ?? false;
3250
3616
  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;
3617
+ const { createOpenClawCodingTools: defaultCreateOpenClawCodingTools, resolveWebSearchToolPolicy } = await import("openclaw/plugin-sdk/agent-harness");
3618
+ const createOpenClawCodingTools = dynamicToolBuildState.openClawCodingToolsFactory ?? defaultCreateOpenClawCodingTools;
3253
3619
  toolBuildStages.mark("load-agent-harness-tools");
3254
3620
  const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
3255
3621
  const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
@@ -3346,7 +3712,7 @@ async function buildDynamicTools(input) {
3346
3712
  nativeProviderWebSearchSupport: input.nativeProviderWebSearchSupport
3347
3713
  });
3348
3714
  const readableAllTools = [...readableAllToolProjection.tools];
3349
- const normallyProfiledTools = filterCodexDynamicTools(readableAllTools, input.pluginConfig);
3715
+ const normallyProfiledTools = shouldKeepOpenClawShellDynamicTools(input, nativeExecutionPolicy) ? filterCodexDynamicToolsWithOpenClawShell(readableAllTools, input.pluginConfig) : filterCodexDynamicTools(readableAllTools, input.pluginConfig);
3350
3716
  const profileFilteredTools = (input.isHostScopedToolActive?.("openclaw") ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) ? preserveRingZeroSystemAgentTool(readableAllTools, normallyProfiledTools) : normallyProfiledTools;
3351
3717
  const codexFilteredTools = addNodeShellDynamicToolsIfNeeded(addSandboxShellDynamicToolsIfAvailable(isCodexMemoryFlushRun(params) ? filterCodexMemoryFlushDynamicTools(readableAllTools) : profileFilteredTools, readableAllTools, input), readableAllTools, input, nativeExecutionPolicy);
3352
3718
  toolBuildStages.mark("codex-filtering");
@@ -3356,7 +3722,7 @@ async function buildDynamicTools(input) {
3356
3722
  });
3357
3723
  toolBuildStages.mark("vision-filtering");
3358
3724
  const webSearchPresent = visionFilteredTools.some((tool) => tool.name === "web_search");
3359
- const webSearchPolicy = agentHarness.resolveWebSearchToolPolicy({
3725
+ const webSearchPolicy = resolveWebSearchToolPolicy({
3360
3726
  config: params.config,
3361
3727
  modelProvider: params.model.provider,
3362
3728
  modelId: params.modelId,
@@ -3570,13 +3936,6 @@ function shouldExposeSandboxExecDynamicTool(input) {
3570
3936
  const backendId = input.sandbox?.enabled ? input.sandbox.backendId.trim().toLowerCase() : "";
3571
3937
  return Boolean(backendId && input.nativeToolSurfaceEnabled === false);
3572
3938
  }
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
3939
  function isSandboxShellDynamicToolExcluded(config) {
3581
3940
  return isCodexDynamicToolExcluded(config, [
3582
3941
  "exec",
@@ -3594,55 +3953,12 @@ function addNodeShellDynamicToolsIfNeeded(filteredTools, allTools, input, nodePo
3594
3953
  const processTool = allTools.find((tool) => normalizeCodexDynamicToolName(tool.name) === "process");
3595
3954
  if (!execTool || !processTool) return filteredTools;
3596
3955
  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));
3956
+ if (!isCodexDynamicToolExcluded(input.pluginConfig, ["exec", "node_exec"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_exec")) toolsToAppend.push(createNodeExecDynamicTool(execTool, nodePolicy.node));
3957
+ if (!isCodexDynamicToolExcluded(input.pluginConfig, ["process", "node_process"]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === "node_process")) toolsToAppend.push(createNodeProcessDynamicTool(processTool));
3599
3958
  return toolsToAppend.length > 0 ? [...filteredTools, ...toolsToAppend] : filteredTools;
3600
3959
  }
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
- };
3960
+ function shouldKeepOpenClawShellDynamicTools(input, nodePolicy) {
3961
+ return !isCodexMemoryFlushRun(input.params) && input.nativeToolSurfaceEnabled === false && input.sandbox?.enabled !== true && nodePolicy.effectiveExecHost !== "node";
3646
3962
  }
3647
3963
  function resolveCodexNativeExecutionPolicyForDynamicTools(input) {
3648
3964
  return resolveCodexNativeExecutionPolicy({
@@ -3663,7 +3979,7 @@ function filterCodexDynamicToolsForAllowlist(tools, toolsAllow) {
3663
3979
  const allowSet = new Set(toolsAllow.map((name) => normalizeCodexDynamicToolName(name)).filter(Boolean));
3664
3980
  return tools.filter((tool) => {
3665
3981
  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"));
3982
+ 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
3983
  });
3668
3984
  }
3669
3985
  /** Detects the wildcard allowlist marker after Codex tool-name normalization. */
@@ -3675,90 +3991,28 @@ function shouldForceMessageTool(params) {
3675
3991
  return params.disableMessageTool !== true && params.sourceReplyDeliveryMode === "message_tool_only";
3676
3992
  }
3677
3993
  //#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?.();
3994
+ //#region extensions/codex/src/app-server/source-reply-finality.ts
3995
+ const sourceReplyDeliveryIntents = /* @__PURE__ */ new WeakMap();
3996
+ /** Retain source-reply intent until the owning Codex turn has an authoritative outcome. */
3997
+ function recordCodexSourceReplyDeliveryIntent(owner, intent) {
3998
+ const intents = sourceReplyDeliveryIntents.get(owner);
3999
+ if (intents) {
4000
+ intents.push(intent);
4001
+ return;
3733
4002
  }
4003
+ sourceReplyDeliveryIntents.set(owner, [intent]);
3734
4004
  }
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;
4005
+ /** Resolve omitted finality without changing explicit progress or final markers. */
4006
+ function settleCodexSourceReplyFinality(owner, turnSucceeded) {
4007
+ const intents = sourceReplyDeliveryIntents.get(owner);
4008
+ if (!intents) return false;
4009
+ const lastIntent = intents.at(-1);
4010
+ for (const intent of intents) {
4011
+ if (intent.final !== void 0) continue;
4012
+ intent.record.sourceReplyFinal = turnSucceeded && intent === lastIntent;
4013
+ }
4014
+ sourceReplyDeliveryIntents.delete(owner);
4015
+ return turnSucceeded && intents.some((intent) => intent.record.sourceReplyFinal === true);
3762
4016
  }
3763
4017
  //#endregion
3764
4018
  //#region extensions/codex/src/app-server/provider-capabilities.ts
@@ -4580,6 +4834,7 @@ async function handleCodexAppServerApprovalRequest(params) {
4580
4834
  paramsForRun: params.paramsForRun,
4581
4835
  context,
4582
4836
  nativeHookRelay: params.nativeHookRelay,
4837
+ autoApprove: params.autoApprove,
4583
4838
  signal: params.signal
4584
4839
  });
4585
4840
  if (policyOutcome?.outcome === "denied") {
@@ -4759,6 +5014,7 @@ async function runOpenClawToolPolicyForApprovalRequest(params) {
4759
5014
  context: params.context,
4760
5015
  policyRequest,
4761
5016
  nativeHookRelay: params.nativeHookRelay,
5017
+ autoApprove: params.autoApprove,
4762
5018
  cwd,
4763
5019
  signal: params.signal
4764
5020
  });
@@ -4867,6 +5123,11 @@ async function runNativeRelayToolPolicyForApprovalRequest(params) {
4867
5123
  };
4868
5124
  return { handled: true };
4869
5125
  } catch (error) {
5126
+ if (params.autoApprove === true && !hasNativeHookRelayInvocation({
5127
+ relayId: params.nativeHookRelay.relayId,
5128
+ event: "pre_tool_use",
5129
+ toolUseId: params.context.approvalId
5130
+ })) return;
4870
5131
  return {
4871
5132
  handled: true,
4872
5133
  blocked: true,
@@ -5539,6 +5800,7 @@ function createCodexDynamicToolBridge(params) {
5539
5800
  return getChannelAgentToolMeta(tool) === void 0;
5540
5801
  };
5541
5802
  const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
5803
+ const executionSnapshotStates = /* @__PURE__ */ new Map();
5542
5804
  const directToolNames = /* @__PURE__ */ new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
5543
5805
  return {
5544
5806
  availableSpecs: createCodexDynamicToolSpecs({
@@ -5552,9 +5814,16 @@ function createCodexDynamicToolBridge(params) {
5552
5814
  directToolNames
5553
5815
  }),
5554
5816
  telemetry,
5817
+ consumeToolExecutionSnapshot: (toolCallId) => {
5818
+ const state = executionSnapshotStates.get(toolCallId);
5819
+ executionSnapshotStates.delete(toolCallId);
5820
+ if (state) state.consumed = true;
5821
+ return state?.snapshot;
5822
+ },
5555
5823
  handleToolCall: async (call, options) => {
5556
5824
  const toolEntry = toolMap.get(call.tool);
5557
5825
  if (!toolEntry) {
5826
+ const executedArguments = jsonObjectToRecord(call.arguments);
5558
5827
  const message = registeredToolNames.has(call.tool) ? `OpenClaw tool is not available for this turn: ${call.tool}` : `Unknown OpenClaw tool: ${call.tool}`;
5559
5828
  finalizeToolTerminalPresentation({
5560
5829
  toolCallId: call.callId,
@@ -5566,28 +5835,34 @@ function createCodexDynamicToolBridge(params) {
5566
5835
  toolCallOrdinal: options?.toolCallOrdinal
5567
5836
  });
5568
5837
  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
- };
5838
+ return createFailedDynamicToolResponse(message, {
5839
+ executedArguments,
5840
+ executionStarted: false
5841
+ });
5583
5842
  }
5584
5843
  const { tool, name: toolName } = toolEntry;
5585
5844
  const args = jsonObjectToRecord(call.arguments);
5586
5845
  const startedAt = Date.now();
5587
5846
  const signal = composeAbortSignals(params.signal, options?.signal);
5588
5847
  let didStartExecution = false;
5848
+ let didDispatchExecution = false;
5589
5849
  let executionPrevented = false;
5590
5850
  let executedArgs = structuredClone(args);
5851
+ const executionSnapshotState = {
5852
+ consumed: false,
5853
+ retainAfterCompletion: options?.retainExecutionSnapshot === true
5854
+ };
5855
+ executionSnapshotStates.set(call.callId, executionSnapshotState);
5856
+ const captureExecutionBoundary = () => {
5857
+ didStartExecution ||= didDispatchExecution;
5858
+ executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
5859
+ const adjustedExecutedArgs = consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
5860
+ if (isRecord(adjustedExecutedArgs)) executedArgs = adjustedExecutedArgs;
5861
+ if (!executionSnapshotState.consumed) executionSnapshotState.snapshot = {
5862
+ executedArguments: structuredClone(executedArgs),
5863
+ executionStarted: didStartExecution && !executionPrevented
5864
+ };
5865
+ };
5591
5866
  try {
5592
5867
  const preparedArgs = tool.prepareArguments ? tool.prepareArguments(args) : args;
5593
5868
  const telemetryArgs = isRecord(preparedArgs) ? preparedArgs : args;
@@ -5600,13 +5875,11 @@ function createCodexDynamicToolBridge(params) {
5600
5875
  replyToMode: params.hookContext?.replyToMode,
5601
5876
  hasRepliedRef: params.hookContext?.hasRepliedRef ? { value: params.hookContext.hasRepliedRef.value } : void 0
5602
5877
  };
5603
- didStartExecution = true;
5878
+ didDispatchExecution = true;
5604
5879
  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);
5880
+ captureExecutionBoundary();
5608
5881
  const telemetryRawResult = sanitizeToolResult(rawResult);
5609
- const rawIsError = isCodexToolResultError(rawResult);
5882
+ const rawIsError = isToolResultError(rawResult);
5610
5883
  const rawResultFailureKind = resolveToolResultFailureKind(rawResult);
5611
5884
  const middlewareResult = await middlewareRunner.applyToolResultMiddleware({
5612
5885
  threadId: call.threadId,
@@ -5625,7 +5898,7 @@ function createCodexDynamicToolBridge(params) {
5625
5898
  args: structuredClone(executedArgs),
5626
5899
  result: middlewareResult
5627
5900
  });
5628
- const resultIsError = rawIsError || isCodexToolResultError(result);
5901
+ const resultIsError = rawIsError || isToolResultError(result);
5629
5902
  const finalResultFailureKind = resolveToolResultFailureKind(result);
5630
5903
  const resultFailureKind = rawResultFailureKind ?? finalResultFailureKind;
5631
5904
  const observerResult = rawResultFailureKind && finalResultFailureKind !== rawResultFailureKind ? {
@@ -5689,8 +5962,9 @@ function createCodexDynamicToolBridge(params) {
5689
5962
  }) && (replyReceiptMatchesCurrentMessage(rawResult, params.hookContext) || replyReceiptMatchesCurrentMessage(result, params.hookContext));
5690
5963
  const toolConfirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && !resultIsError && (rawResult.terminate === true || result.terminate === true);
5691
5964
  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({
5965
+ const confirmedSourceReply = params.hookContext?.sourceReplyDeliveryMode === "message_tool_only" && toolName === "message" && (toolConfirmedSourceReply || deliveredSourceReply || receiptConfirmedSourceReply);
5966
+ const sourceReplyFinal = confirmedSourceReply ? hasExplicitFinalControl ? executedArgs.final === true : void 0 : void 0;
5967
+ const sourceReplyRecord = collectToolTelemetry({
5694
5968
  toolName,
5695
5969
  args: executedArgs,
5696
5970
  result,
@@ -5700,17 +5974,26 @@ function createCodexDynamicToolBridge(params) {
5700
5974
  messagingTarget: confirmedMessagingTarget,
5701
5975
  sourceReplyFinal
5702
5976
  });
5977
+ if (confirmedSourceReply && sourceReplyRecord) recordCodexSourceReplyDeliveryIntent(telemetry, {
5978
+ record: sourceReplyRecord,
5979
+ final: sourceReplyFinal
5980
+ });
5703
5981
  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);
5982
+ const defersInferredSourceReplyTermination = confirmedSourceReply && executedArgs.final !== true;
5983
+ withDynamicToolTermination(response, (rawResult.terminate === true || result.terminate === true) && !defersInferredSourceReplyTermination || isToolResultYield(rawResult) || isToolResultYield(result) || confirmedSourceReply && executedArgs.final === true);
5705
5984
  const asyncStarted = isAsyncStartedToolResult(rawResult) || isAsyncStartedToolResult(result);
5706
5985
  withDynamicToolAsyncStarted(response, asyncStarted);
5707
- return withSideEffectEvidence(response, !(executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs)));
5986
+ const replaySafe = executionPrevented || !asyncStarted && isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
5987
+ return withDynamicToolExecutionState(response, {
5988
+ executedArguments: executedArgs,
5989
+ executionStarted: didStartExecution && !executionPrevented,
5990
+ sideEffectEvidence: !replaySafe
5991
+ });
5708
5992
  } catch (error) {
5993
+ captureExecutionBoundary();
5709
5994
  if (toolName === "computer" && params.computerContextEpoch?.frameToolCallId === call.callId) invalidateComputerFrame(params.computerContextEpoch);
5710
5995
  const executionDisposition = getBeforeToolCallFailureDisposition(error) ?? (signal.aborted ? resolveCodexToolAbortTerminalReason(signal) : resolveToolExecutionErrorKind(error));
5711
5996
  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
5997
  executionPrevented = executionPrevented || consumePreExecutionBlockedToolCall(call.callId, toolResultHookContext.runId);
5715
5998
  const failedResult = failedToolResult(errorMessage, executionDisposition);
5716
5999
  finalizeToolTerminalPresentation({
@@ -5743,13 +6026,20 @@ function createCodexDynamicToolBridge(params) {
5743
6026
  startedAt
5744
6027
  });
5745
6028
  const replaySafe = !didStartExecution || executionPrevented || isReplaySafeToolInstance(toolEntry.tool) && isReplaySafeToolCall(toolName, executedArgs);
5746
- return withSideEffectEvidence(withDiagnosticFailureDisposition({
6029
+ return withDynamicToolExecutionState(withDiagnosticFailureDisposition({
5747
6030
  contentItems: [{
5748
6031
  type: "inputText",
5749
6032
  text: errorMessage
5750
6033
  }],
5751
6034
  success: false
5752
- }, executionDisposition), didStartExecution && !replaySafe);
6035
+ }, executionDisposition), {
6036
+ executedArguments: executedArgs,
6037
+ executionStarted: didStartExecution && !executionPrevented,
6038
+ sideEffectEvidence: didStartExecution && !replaySafe
6039
+ });
6040
+ } finally {
6041
+ if (executionSnapshotStates.get(call.callId) === executionSnapshotState && (executionSnapshotState.consumed || !executionSnapshotState.retainAfterCompletion)) executionSnapshotStates.delete(call.callId);
6042
+ consumeAdjustedParamsForToolCall(call.callId, toolResultHookContext.runId);
5753
6043
  }
5754
6044
  }
5755
6045
  };
@@ -5877,7 +6167,12 @@ function projectCodexDynamicTools(tools) {
5877
6167
  quarantinedTools.push(descriptor.diagnostic);
5878
6168
  continue;
5879
6169
  }
5880
- const projection = projectRuntimeToolInputSchema(descriptor.parameters, `${descriptor.name}.inputSchema`);
6170
+ const normalizedParameters = normalizeOpenAIToolSchemas({
6171
+ provider: "openai",
6172
+ modelApi: "openai-chatgpt-responses",
6173
+ tools: [{ parameters: descriptor.parameters }]
6174
+ })[0]?.parameters;
6175
+ const projection = projectRuntimeToolInputSchema(normalizedParameters ?? descriptor.parameters, `${descriptor.name}.inputSchema`);
5881
6176
  if (projection.violations.length > 0) {
5882
6177
  quarantinedTools.push({
5883
6178
  tool: descriptor.name,
@@ -6032,11 +6327,12 @@ function collectToolTelemetry(params) {
6032
6327
  params.telemetry.didSendViaMessagingTool = true;
6033
6328
  const sourceReplyPayload = extractInternalSourceReplyPayload(params.result?.details);
6034
6329
  if (sourceReplyPayload) {
6035
- params.telemetry.messagingToolSourceReplyPayloads.push({
6330
+ const record = {
6036
6331
  ...sourceReplyPayload,
6037
6332
  ...params.sourceReplyFinal !== void 0 ? { sourceReplyFinal: params.sourceReplyFinal } : {}
6038
- });
6039
- return;
6333
+ };
6334
+ params.telemetry.messagingToolSourceReplyPayloads.push(record);
6335
+ return record;
6040
6336
  }
6041
6337
  const text = readFirstString(params.args, [
6042
6338
  "text",
@@ -6047,7 +6343,7 @@ function collectToolTelemetry(params) {
6047
6343
  if (text) params.telemetry.messagingToolSentTexts.push(text);
6048
6344
  const mediaUrls = collectMediaUrls(params.args);
6049
6345
  params.telemetry.messagingToolSentMediaUrls.push(...mediaUrls);
6050
- params.telemetry.messagingToolSentTargets.push({
6346
+ const record = {
6051
6347
  ...params.messagingTarget ?? {
6052
6348
  tool: params.toolName,
6053
6349
  provider: readFirstString(params.args, ["provider", "channel"]) ?? params.toolName,
@@ -6066,7 +6362,9 @@ function collectToolTelemetry(params) {
6066
6362
  ...text ? { text } : {},
6067
6363
  ...mediaUrls.length > 0 ? { mediaUrls } : {},
6068
6364
  ...params.sourceReplyFinal !== void 0 ? { sourceReplyFinal: params.sourceReplyFinal } : {}
6069
- });
6365
+ };
6366
+ params.telemetry.messagingToolSentTargets.push(record);
6367
+ return record;
6070
6368
  }
6071
6369
  function extractInternalSourceReplyPayload(details) {
6072
6370
  if (!isRecord(details) || details.sourceReplySink !== "internal-ui") return;
@@ -6087,17 +6385,6 @@ function extractInternalSourceReplyPayload(details) {
6087
6385
  };
6088
6386
  return text || mediaUrls.length > 0 || payload.presentation || payload.interactive ? payload : void 0;
6089
6387
  }
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
6388
  function isToolResultYield(result) {
6102
6389
  const details = result.details;
6103
6390
  if (!isRecord(details) || typeof details.status !== "string") return false;
@@ -6125,15 +6412,6 @@ function withDiagnosticFailureDisposition(response, disposition) {
6125
6412
  });
6126
6413
  return response;
6127
6414
  }
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
6415
  function withDynamicToolTermination(response, terminate) {
6138
6416
  if (!terminate) return response;
6139
6417
  Object.defineProperty(response, "terminate", {
@@ -6274,4 +6552,4 @@ function isCronAddAction(args) {
6274
6552
  return typeof action === "string" && action.trim().toLowerCase() === "add";
6275
6553
  }
6276
6554
  //#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 };
6555
+ 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, resolveCodexLocalRuntimeAttribution as nt, emitDynamicToolTerminalDiagnostic as o, formatCodexDynamicToolBuildStageSummary as p, toCodexDynamicToolProgressResponse as q, handleCodexAppServerElicitationRequest as r, 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 };