@openclaw/codex 2026.5.28 → 2026.5.31-beta.1

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 (28) hide show
  1. package/dist/{client-DMXvboVu.js → client-BnOmn0y-.js} +20 -6
  2. package/dist/client-factory-B8dh39UW.js +20 -0
  3. package/dist/{command-handlers-C2t6-ChO.js → command-handlers-BsK6kZJi.js} +265 -16
  4. package/dist/{compact-Dlnt5G3v.js → compact-BXLjhUHX.js} +4 -4
  5. package/dist/{computer-use-Ba4vW9fO.js → computer-use-D-0EFJBJ.js} +3 -4
  6. package/dist/{config--tW89bHH.js → config-AlzuNKCY.js} +14 -5
  7. package/dist/{conversation-binding-CC9XMAwn.js → conversation-binding-KUDxop-C.js} +69 -92
  8. package/dist/doctor-contract-api.js +1 -1
  9. package/dist/harness.js +6 -10
  10. package/dist/index.js +23 -60
  11. package/dist/media-understanding-provider.js +8 -9
  12. package/dist/{models-QT-XOhE7.js → models-mbjSisZL.js} +2 -2
  13. package/dist/{native-hook-relay-B8qM8ZwR.js → native-hook-relay-DCxPRaBN.js} +77 -9
  14. package/dist/{notification-correlation-BDakP_d3.js → notification-correlation-BykOI_jh.js} +3 -3
  15. package/dist/{protocol-validators-DtjYmUw_.js → protocol-validators-DIt7cXIp.js} +2 -2
  16. package/dist/provider-catalog.js +2 -2
  17. package/dist/provider.js +2 -2
  18. package/dist/{request-CF4f5hWY.js → request-DuFgTwRU.js} +4 -4
  19. package/dist/{run-attempt-CuhGEh0u.js → run-attempt-CgaY9r5u.js} +579 -288
  20. package/dist/{sandbox-guard-mXE4_vE_.js → sandbox-guard-DMCJlzmz.js} +4 -1
  21. package/dist/{session-binding-ueMCn4om.js → session-binding-BXJAV_xy.js} +27 -10
  22. package/dist/{shared-client-Duh1bHaP.js → shared-client-DBlR-rV2.js} +33 -4
  23. package/dist/{side-question-B-Cjb9wm.js → side-question-SjBLGANT.js} +10 -10
  24. package/dist/{thread-lifecycle-Dv9Npl7a.js → thread-lifecycle-Bnlv_Q8u.js} +78 -120
  25. package/npm-shrinkwrap.json +34 -34
  26. package/openclaw.plugin.json +5 -4
  27. package/package.json +6 -6
  28. package/dist/client-factory-qZvhyyXr.js +0 -15
@@ -1,16 +1,17 @@
1
- import { n as defaultLeasedCodexAppServerClientFactory } from "./client-factory-qZvhyyXr.js";
2
- import { c as readCodexPluginConfig, d as resolveCodexPluginsPolicy, f as resolveOpenClawExecPolicyForCodexAppServer, i as isCodexAppServerApprovalPolicyAllowedByRequirements, l as resolveCodexAppServerRuntimeOptions, m as withMcpElicitationsApprovalPolicy, o as isCodexSandboxExecServerEnabled, p as shouldAutoApproveCodexAppServerApprovals, u as resolveCodexComputerUseConfig } from "./config--tW89bHH.js";
3
- import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-DtjYmUw_.js";
4
- import { a as isCodexAppServerConnectionClosedError, i as isCodexAppServerApprovalRequest, l as isJsonObject, r as compareCodexAppServerVersions, s as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./client-DMXvboVu.js";
5
- import { C as sanitizeCodexHistoryImagePayloads, D as resolveCodexContextEngineProjectionReserveTokens, E as resolveCodexContextEngineProjectionMaxChars, T as projectContextEngineAssemblyForCodex, _ as shouldBuildCodexPluginThreadConfig, a as buildDeveloperInstructions, c as codexDynamicToolsFingerprint, f as startOrResumeThread, g as mergeCodexThreadConfigs, h as buildCodexPluginThreadConfigInputFingerprint, i as buildContextEngineBinding, l as isContextEngineBindingCompatible, m as buildCodexPluginThreadConfig, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, p as isCodexAppServerProfilerEnabled, s as buildTurnStartParams, x as defaultCodexAppInventoryCache } from "./thread-lifecycle-Dv9Npl7a.js";
6
- import { i as readCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-ueMCn4om.js";
7
- import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-CF4f5hWY.js";
8
- import { _ as shouldRefreshCodexRateLimitsForUsageLimitMessage, g as resolveCodexUsageLimitResetAtMs, h as formatCodexUsageLimitErrorMessage, i as readCodexNotificationTurnId, n as isCodexNotificationForTurn, r as readCodexNotificationThreadId, s as formatCodexDisplayText, t as describeCodexNotificationCorrelation } from "./notification-correlation-BDakP_d3.js";
9
- import { a as releaseLeasedSharedCodexAppServerClient, d as resolveCodexAppServerAuthProfileId, f as resolveCodexAppServerAuthProfileIdForAgent, l as refreshCodexAppServerAuthTokens, m as resolveCodexAppServerHomeDir, o as retireSharedCodexAppServerClientIfCurrent, p as resolveCodexAppServerFallbackApiKeyCacheKey, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerAuthAccountCacheKey } from "./shared-client-Duh1bHaP.js";
10
- import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-mXE4_vE_.js";
11
- import { _ as normalizeCodexDynamicToolName$1, a as createCodexNativeHookRelay, c as scheduleCodexNativeHookRelayUnregister, d as emitDynamicToolErrorDiagnostic, f as emitDynamicToolStartedDiagnostic, g as isForcedPrivateQaCodexRuntime, h as filterCodexDynamicTools, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerElicitationRequest, m as filterToolsForVisionInputs, n as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, o as resolveCodexNativeHookRelayEvents, p as emitDynamicToolTerminalDiagnostic, r as buildCodexNativeHookRelayConfig, s as resolveCodexNativeHookRelayTtlMs, u as createCodexDynamicToolBridge, v as resolveCodexDynamicToolsLoading, y as handleCodexAppServerApprovalRequest } from "./native-hook-relay-B8qM8ZwR.js";
12
- import { t as ensureCodexComputerUse } from "./computer-use-Ba4vW9fO.js";
1
+ import { a as isCodexAppServerApprovalPolicyAllowedByRequirements, c as isCodexSandboxExecServerEnabled, d as resolveCodexAppServerRuntimeOptions, f as resolveCodexComputerUseConfig, g as withMcpElicitationsApprovalPolicy, h as shouldAutoApproveCodexAppServerApprovals, m as resolveOpenClawExecPolicyForCodexAppServer, p as resolveCodexPluginsPolicy, u as readCodexPluginConfig } from "./config-AlzuNKCY.js";
2
+ import { a as readCodexDynamicToolCallParams, c as readCodexTurn, i as assertCodexTurnStartResponse } from "./protocol-validators-DIt7cXIp.js";
3
+ import { a as isCodexAppServerConnectionClosedError, i as isCodexAppServerApprovalRequest, l as isJsonObject, r as compareCodexAppServerVersions, s as MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./client-BnOmn0y-.js";
4
+ import { C as sanitizeCodexHistoryImagePayloads, D as resolveCodexContextEngineProjectionReserveTokens, E as resolveCodexContextEngineProjectionMaxChars, T as projectContextEngineAssemblyForCodex, _ as shouldBuildCodexPluginThreadConfig, a as buildDeveloperInstructions, c as codexDynamicToolsFingerprint, f as startOrResumeThread, g as mergeCodexThreadConfigs, h as buildCodexPluginThreadConfigInputFingerprint, i as buildContextEngineBinding, l as isContextEngineBindingCompatible, m as buildCodexPluginThreadConfig, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, p as isCodexAppServerProfilerEnabled, s as buildTurnStartParams, x as defaultCodexAppInventoryCache } from "./thread-lifecycle-Bnlv_Q8u.js";
5
+ import { a as readCodexAppServerBinding, n as clearCodexAppServerBindingForThread, s as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-BXJAV_xy.js";
6
+ import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS } from "./request-DuFgTwRU.js";
7
+ import { _ as shouldRefreshCodexRateLimitsForUsageLimitMessage, g as resolveCodexUsageLimitResetAtMs, h as formatCodexUsageLimitErrorMessage, i as readCodexNotificationTurnId, n as isCodexNotificationForTurn, r as readCodexNotificationThreadId, s as formatCodexDisplayText, t as describeCodexNotificationCorrelation } from "./notification-correlation-BykOI_jh.js";
8
+ import { a as releaseLeasedSharedCodexAppServerClient, d as resolveCodexAppServerAuthProfileId, f as resolveCodexAppServerAuthProfileIdForAgent, l as refreshCodexAppServerAuthTokens, m as resolveCodexAppServerHomeDir, o as retireSharedCodexAppServerClientIfCurrent, p as resolveCodexAppServerFallbackApiKeyCacheKey, t as clearSharedCodexAppServerClientIfCurrent, u as resolveCodexAppServerAuthAccountCacheKey } from "./shared-client-DBlR-rV2.js";
9
+ import { i as resolveCodexNativeExecutionPolicy } from "./sandbox-guard-DMCJlzmz.js";
10
+ import { n as defaultLeasedCodexAppServerClientFactory } from "./client-factory-B8dh39UW.js";
11
+ import { C as resolveCodexTurnAssistantCompletionIdleTimeoutMs, E as withCodexStartupTimeout, S as resolveCodexStartupTimeoutMs, T as resolveCodexTurnTerminalIdleTimeoutMs, _ as normalizeCodexDynamicToolName$1, a as createCodexNativeHookRelay, b as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, c as scheduleCodexNativeHookRelayUnregister, d as emitDynamicToolErrorDiagnostic, f as emitDynamicToolStartedDiagnostic, g as isForcedPrivateQaCodexRuntime, h as filterCodexDynamicTools, i as buildCodexNativeHookRelayDisabledConfig, l as handleCodexAppServerElicitationRequest, m as filterToolsForVisionInputs, n as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, o as resolveCodexNativeHookRelayEvents, p as emitDynamicToolTerminalDiagnostic, r as buildCodexNativeHookRelayConfig, s as resolveCodexNativeHookRelayTtlMs, u as createCodexDynamicToolBridge, v as resolveCodexDynamicToolsLoading, w as resolveCodexTurnCompletionIdleTimeoutMs, x as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, y as handleCodexAppServerApprovalRequest } from "./native-hook-relay-DCxPRaBN.js";
12
+ import { t as ensureCodexComputerUse } from "./computer-use-D-0EFJBJ.js";
13
13
  import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-N66I-Rd7.js";
14
+ import { asDateTimestampMs, parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
14
15
  import { createHash, randomUUID } from "node:crypto";
15
16
  import fsSync from "node:fs";
16
17
  import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
@@ -26,9 +27,10 @@ import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent
26
27
  import { markAuthProfileBlockedUntil, resolveAgentDir as resolveAgentDir$1, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
27
28
  import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
28
29
  import { appendRegularFile, pathExists } from "openclaw/plugin-sdk/security-runtime";
29
- import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
30
30
  import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
31
31
  import { isIP } from "node:net";
32
+ import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
33
+ import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
32
34
  import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
33
35
  //#region extensions/codex/src/app-server/app-server-policy.ts
34
36
  function resolveCodexAppServerForOpenClawToolPolicy(params) {
@@ -155,37 +157,6 @@ async function readMirroredSessionHistoryMessages(sessionFile) {
155
157
  if (!messages) embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", { sessionFile });
156
158
  return messages;
157
159
  }
158
- function shouldProjectMirroredHistoryForCodexStart(params) {
159
- if (!params.historyMessages.some((message) => message.role === "user")) return false;
160
- if (params.forceProject) return true;
161
- if (!params.startupBinding?.threadId) return true;
162
- if (hasUserVisibleHistoryAfterCodexBinding({
163
- startupBinding: params.startupBinding,
164
- historyMessages: params.historyMessages
165
- })) return true;
166
- return !areCodexDynamicToolFingerprintsCompatible({
167
- previous: params.startupBinding.dynamicToolsFingerprint,
168
- next: params.dynamicToolsFingerprint
169
- });
170
- }
171
- function hasUserVisibleHistoryAfterCodexBinding(params) {
172
- const bindingUpdatedAt = Date.parse(params.startupBinding.updatedAt);
173
- if (!Number.isFinite(bindingUpdatedAt)) return false;
174
- return params.historyMessages.some((message) => {
175
- if (message.role !== "user" && message.role !== "assistant") return false;
176
- if (isCodexAppServerMirroredTranscriptMessage(message)) return false;
177
- const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
178
- return Number.isFinite(timestamp) && timestamp > bindingUpdatedAt;
179
- });
180
- }
181
- function isCodexAppServerMirroredTranscriptMessage(message) {
182
- const record = message;
183
- const idempotencyKey = record.idempotencyKey;
184
- if (typeof idempotencyKey === "string" && idempotencyKey.startsWith("codex-app-server:")) return true;
185
- const meta = record["__openclaw"];
186
- if (!meta || typeof meta !== "object" || Array.isArray(meta)) return false;
187
- return typeof meta.mirrorIdentity === "string";
188
- }
189
160
  function readContextEngineThreadBootstrapProjection(projection) {
190
161
  if (projection?.mode !== "thread_bootstrap") return;
191
162
  const epoch = projection.epoch?.trim();
@@ -284,6 +255,10 @@ async function buildCodexWorkspaceBootstrapContext(params) {
284
255
  promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
285
256
  developerInstructions: renderCodexWorkspaceThreadDeveloperInstructions(developerInstructionFiles),
286
257
  turnScopedDeveloperInstructions: renderCodexWorkspaceCollaborationDeveloperInstructions(turnScopedDeveloperInstructionFiles),
258
+ memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? renderCodexWorkspaceMemoryReference({
259
+ files: memoryReferenceFiles,
260
+ toolNames: params.memoryToolNames
261
+ }) : void 0,
287
262
  heartbeatCollaborationInstructions: renderCodexWorkspaceHeartbeatReference(heartbeatReferenceFiles)
288
263
  };
289
264
  } catch (error) {
@@ -454,19 +429,11 @@ function readNonEmptyString$1(value) {
454
429
  }
455
430
  function buildCodexOpenClawPromptContext(params) {
456
431
  if (!shouldInjectCodexOpenClawPromptContext(params.params)) return;
457
- const sections = [
458
- params.skillsPrompt?.trim() ? [
459
- "## OpenClaw Skills",
460
- "",
461
- params.skillsPrompt.trim()
462
- ].join("\n") : void 0,
463
- params.workspaceMemoryReference?.trim() ? params.workspaceMemoryReference.trim() : void 0,
464
- params.workspacePromptContext?.trim() ? [
465
- "## OpenClaw Workspace Context",
466
- "",
467
- params.workspacePromptContext.trim()
468
- ].join("\n") : void 0
469
- ].filter(isNonEmptyString$2);
432
+ const sections = [params.workspacePromptContext?.trim() ? [
433
+ "## OpenClaw Workspace Context",
434
+ "",
435
+ params.workspacePromptContext.trim()
436
+ ].join("\n") : void 0].filter(isNonEmptyString$2);
470
437
  if (sections.length === 0) return;
471
438
  return [
472
439
  "OpenClaw runtime context for this turn:",
@@ -478,9 +445,17 @@ function buildCodexOpenClawPromptContext(params) {
478
445
  function shouldInjectCodexOpenClawPromptContext(params) {
479
446
  return !(params.bootstrapContextMode === "lightweight" && params.bootstrapContextRunKind === "cron");
480
447
  }
481
- function prependCodexOpenClawPromptContext(prompt, context) {
482
- if (!context?.trim()) return prompt;
448
+ function renderCodexSkillsCollaborationInstructions(params) {
449
+ if (!shouldInjectCodexOpenClawPromptContext(params.attempt)) return;
450
+ return params.skillsPrompt?.trim() ? [
451
+ "## OpenClaw Skills",
452
+ "",
453
+ params.skillsPrompt.trim()
454
+ ].join("\n") : void 0;
455
+ }
456
+ function prependCodexOpenClawPromptContext(prompt, context, options = {}) {
483
457
  const { deliveryHint, prompt: promptWithoutDeliveryHint } = splitLeadingCodexDeliveryHint(prompt);
458
+ if (!context?.trim() && (!deliveryHint || options.preservePromptWithoutContext)) return prompt;
484
459
  const promptSection = promptWithoutDeliveryHint.startsWith("OpenClaw assembled context for this turn:") ? promptWithoutDeliveryHint : ["Current user request:", promptWithoutDeliveryHint].join("\n");
485
460
  const deliverySection = deliveryHint ? [
486
461
  "OpenClaw delivery metadata:",
@@ -488,7 +463,7 @@ function prependCodexOpenClawPromptContext(prompt, context) {
488
463
  deliveryHint
489
464
  ].join("\n") : void 0;
490
465
  return [
491
- context.trim(),
466
+ context?.trim(),
492
467
  deliverySection,
493
468
  promptSection
494
469
  ].filter(Boolean).join("\n\n");
@@ -550,11 +525,12 @@ function renderCodexWorkspaceCollaborationDeveloperInstructions(files) {
550
525
  return renderCodexWorkspaceDeveloperInstructions({
551
526
  files,
552
527
  header: "## OpenClaw Agent Soul",
553
- preamble: "OpenClaw loaded these workspace instruction files from the active agent workspace. They are the canonical definitions of who you are, how you think and work, and the human you work alongside. Internalize and follow them accordingly."
528
+ preamble: "OpenClaw loaded these workspace instruction files from the active agent workspace. They are the canonical definitions of who you are, how you think and work, and the human you work alongside. Internalize and follow them accordingly.",
529
+ wrapperTag: "AGENT_SOUL"
554
530
  });
555
531
  }
556
532
  function renderCodexWorkspaceDeveloperInstructions(params) {
557
- const { files, header, preamble } = params;
533
+ const { files, header, preamble, wrapperTag } = params;
558
534
  if (files.length === 0) return;
559
535
  const lines = [
560
536
  header,
@@ -562,7 +538,9 @@ function renderCodexWorkspaceDeveloperInstructions(params) {
562
538
  preamble,
563
539
  ""
564
540
  ];
541
+ if (wrapperTag) lines.push(`<${wrapperTag}>`, "");
565
542
  for (const file of files) lines.push(`### ${file.path}`, "", file.content, "");
543
+ if (wrapperTag) lines.push(`</${wrapperTag}>`);
566
544
  return lines.join("\n").trim();
567
545
  }
568
546
  function selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles) {
@@ -594,7 +572,7 @@ function renderCodexWorkspaceMemoryReference(params) {
594
572
  const lines = [
595
573
  "## OpenClaw Workspace Memory",
596
574
  "",
597
- `MEMORY.md exists in the active agent workspace. OpenClaw does not paste its contents into native Codex turns; use ${(params.toolNames?.length ? params.toolNames : Array.from(CODEX_MEMORY_TOOL_NAMES)).join(" or ")} when durable memory is relevant and the tools are available.`,
575
+ `MEMORY.md exists in the active agent workspace as a memory file, not an instruction file. OpenClaw does not paste its contents into native Codex turns; use ${(params.toolNames?.length ? params.toolNames : Array.from(CODEX_MEMORY_TOOL_NAMES)).join(" or ")} when durable memory is relevant and the tools are available.`,
598
576
  ""
599
577
  ];
600
578
  for (const file of params.files) lines.push(`- ${file.path}`);
@@ -825,6 +803,11 @@ function isReasoningItemCompletionNotification(notification) {
825
803
  const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
826
804
  return item ? readString$6(item, "type") === "reasoning" : false;
827
805
  }
806
+ function isAssistantCommentaryCompletionNotification(notification) {
807
+ if (!isJsonObject(notification.params) || notification.method !== "item/completed") return false;
808
+ const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
809
+ return Boolean(item && readString$6(item, "type") === "agentMessage" && readString$6(item, "phase") === "commentary");
810
+ }
828
811
  function isRawReasoningCompletionNotification(notification) {
829
812
  if (!isJsonObject(notification.params) || notification.method !== "rawResponseItem/completed") return false;
830
813
  const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
@@ -870,10 +853,21 @@ function isNativeToolProgressNotification(notification) {
870
853
  default: return false;
871
854
  }
872
855
  }
873
- function isRawAssistantCompletionNotification(notification) {
856
+ function isNativeResponseStreamDeltaNotification(notification) {
857
+ return notification.method.startsWith("response.") && notification.method.endsWith(".delta");
858
+ }
859
+ function isFileChangePatchUpdatedNotification(notification) {
860
+ return notification.method === "item/fileChange/patchUpdated" && isJsonObject(notification.params);
861
+ }
862
+ function isRawAssistantProgressNotification(notification) {
874
863
  if (notification.method !== "rawResponseItem/completed" || !isJsonObject(notification.params)) return false;
875
864
  const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
876
- return Boolean(item && readString$6(item, "type") === "message" && readString$6(item, "role") === "assistant" && readString$6(item, "phase") !== "commentary" && readRawAssistantTextPreview(item));
865
+ return Boolean(item && readString$6(item, "type") === "message" && readString$6(item, "role") === "assistant" && readRawAssistantTextPreview(item));
866
+ }
867
+ function isRawAssistantCompletionNotification(notification) {
868
+ if (!isRawAssistantProgressNotification(notification) || !isJsonObject(notification.params)) return false;
869
+ const item = isJsonObject(notification.params.item) ? notification.params.item : void 0;
870
+ return Boolean(item && readString$6(item, "phase") !== "commentary");
877
871
  }
878
872
  function readRawAssistantTextPreview(item) {
879
873
  if (readString$6(item, "role") !== "assistant" || !Array.isArray(item.content)) return;
@@ -964,67 +958,6 @@ function codexExecutionToolName(item) {
964
958
  function isNonEmptyString$1(value) {
965
959
  return typeof value === "string" && value.length > 0;
966
960
  }
967
- const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 6e4;
968
- const CODEX_TURN_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS = 1e4;
969
- const CODEX_POST_REASONING_SOURCE_REPLY_IDLE_TIMEOUT_MS = 5 * 6e4;
970
- const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 6e4;
971
- function resolvePositiveIntegerTimeoutMs(value, fallbackMs) {
972
- return resolveTimerTimeoutMs(value, resolveTimerTimeoutMs(fallbackMs, 1));
973
- }
974
- async function withCodexStartupTimeout(params) {
975
- if (params.signal.aborted) throw new Error("codex app-server startup aborted");
976
- let timeout;
977
- let abortCleanup;
978
- let timeoutError;
979
- let timeoutCleanup;
980
- try {
981
- return await Promise.race([params.operation(), new Promise((_, reject) => {
982
- const rejectOnce = (error) => {
983
- if (timeout) {
984
- clearTimeout(timeout);
985
- timeout = void 0;
986
- }
987
- reject(error);
988
- };
989
- timeout = setTimeout(() => {
990
- timeoutError = /* @__PURE__ */ new Error("codex app-server startup timed out");
991
- timeoutCleanup = Promise.resolve(params.onTimeout?.()).then(() => void 0, () => void 0);
992
- timeoutCleanup.finally(() => {
993
- rejectOnce(timeoutError);
994
- });
995
- }, params.timeoutMs);
996
- const abortListener = () => rejectOnce(/* @__PURE__ */ new Error("codex app-server startup aborted"));
997
- params.signal.addEventListener("abort", abortListener, { once: true });
998
- abortCleanup = () => params.signal.removeEventListener("abort", abortListener);
999
- })]);
1000
- } catch (error) {
1001
- if (timeoutError) {
1002
- await timeoutCleanup;
1003
- throw timeoutError;
1004
- }
1005
- throw error;
1006
- } finally {
1007
- if (timeout) clearTimeout(timeout);
1008
- abortCleanup?.();
1009
- }
1010
- }
1011
- function resolveCodexStartupTimeoutMs(params) {
1012
- const timeoutFloorMs = resolvePositiveIntegerTimeoutMs(params.timeoutFloorMs, 100);
1013
- const timeoutMs = resolvePositiveIntegerTimeoutMs(params.timeoutMs, timeoutFloorMs);
1014
- return Math.max(timeoutFloorMs, timeoutMs);
1015
- }
1016
- function resolveCodexTurnCompletionIdleTimeoutMs(value) {
1017
- return resolvePositiveIntegerTimeoutMs(value, CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS);
1018
- }
1019
- function resolveCodexTurnAssistantCompletionIdleTimeoutMs(value) {
1020
- return resolvePositiveIntegerTimeoutMs(value, CODEX_TURN_ASSISTANT_COMPLETION_IDLE_TIMEOUT_MS);
1021
- }
1022
- function resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(value, fallbackMs) {
1023
- return resolvePositiveIntegerTimeoutMs(value, fallbackMs);
1024
- }
1025
- function resolveCodexTurnTerminalIdleTimeoutMs(value) {
1026
- return resolvePositiveIntegerTimeoutMs(value, CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS);
1027
- }
1028
961
  //#endregion
1029
962
  //#region extensions/codex/src/app-server/attempt-notification-state.ts
1030
963
  function reportCodexExecutionNotification(params) {
@@ -1055,8 +988,9 @@ function applyCodexTurnNotificationState(params) {
1055
988
  const { notification, turnWatches } = params;
1056
989
  const isCurrentTurnNotification = isTurnNotification(notification.params, params.threadId, params.turnId);
1057
990
  const isTurnCompletion = notification.method === "turn/completed" && isCurrentTurnNotification;
991
+ const isNativeResponseStreamDelta = isNativeResponseStreamDeltaNotification(notification);
1058
992
  let turnCrossedToolHandoff = params.turnCrossedToolHandoff;
1059
- if (isCurrentTurnNotification) {
993
+ if (isCurrentTurnNotification && !isNativeResponseStreamDelta) {
1060
994
  turnWatches.touchActivity(`notification:${notification.method}`, {
1061
995
  details: describeNotificationActivity(notification),
1062
996
  attemptProgress: true
@@ -1070,25 +1004,37 @@ function applyCodexTurnNotificationState(params) {
1070
1004
  const rawToolOutputCompletion = isRawToolOutputCompletionNotification(notification);
1071
1005
  if (isCurrentTurnNotification && (rawToolOutputCompletion || isNativeToolProgressNotification(notification))) turnCrossedToolHandoff = true;
1072
1006
  const assistantCompletionCanRelease = isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff);
1073
- const postToolRawAssistantCompletionNeedsTerminalGuard = isCurrentTurnNotification && turnCrossedToolHandoff && isRawAssistantCompletionNotification(notification) && params.activeTurnItemIds.size === 0;
1074
- const rawResponseItemCompletedWithNoActiveItems = isCurrentTurnNotification && notification.method === "rawResponseItem/completed" && params.activeTurnItemIds.size === 0 && params.activeAppServerTurnRequests === 0 && !assistantCompletionCanRelease && !postToolRawAssistantCompletionNeedsTerminalGuard;
1075
- const shouldArmPostReasoningSourceReplyWatch = isCurrentTurnNotification && isReasoningItemCompletionNotification(notification) && params.activeTurnItemIds.size === 0 && params.sourceReplyDeliveryMode === "message_tool_only";
1076
- const shouldArmPostRawReasoningSourceReplyWatch = rawResponseItemCompletedWithNoActiveItems && isRawReasoningCompletionNotification(notification) && params.sourceReplyDeliveryMode === "message_tool_only";
1077
- const shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem = isCurrentTurnNotification && notification.method === "item/completed" && params.activeTurnItemIds.size === 0 && !trackedDynamicToolCompletion && !assistantCompletionCanRelease && !shouldArmPostReasoningSourceReplyWatch;
1007
+ const postToolRawAssistantCompletionNeedsTerminalGuard = isCurrentTurnNotification && turnCrossedToolHandoff && isRawAssistantProgressNotification(notification) && params.activeTurnItemIds.size === 0;
1008
+ const postToolPatchUpdateNeedsTerminalGuard = isCurrentTurnNotification && turnCrossedToolHandoff && isFileChangePatchUpdatedNotification(notification);
1009
+ const rawResponseItemCompletedWithNoActiveItems = isCurrentTurnNotification && notification.method === "rawResponseItem/completed" && params.activeTurnItemIds.size === 0 && params.activeAppServerTurnRequests === 0 && !assistantCompletionCanRelease && !postToolRawAssistantCompletionNeedsTerminalGuard && !rawToolOutputCompletion;
1010
+ const shouldArmNoToolPostProgressReplyWatch = isCurrentTurnNotification && !turnCrossedToolHandoff && params.activeTurnItemIds.size === 0 && (isReasoningItemCompletionNotification(notification) || isAssistantCommentaryCompletionNotification(notification));
1011
+ const shouldArmNoToolPostRawProgressReplyWatch = !turnCrossedToolHandoff && rawResponseItemCompletedWithNoActiveItems && (isRawReasoningCompletionNotification(notification) || isRawAssistantProgressNotification(notification));
1012
+ const shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem = isCurrentTurnNotification && notification.method === "item/completed" && params.activeTurnItemIds.size === 0 && !trackedDynamicToolCompletion && !assistantCompletionCanRelease && !shouldArmNoToolPostProgressReplyWatch;
1013
+ const shouldUsePostToolContinuationWatch = turnCrossedToolHandoff && (postToolRawAssistantCompletionNeedsTerminalGuard || postToolPatchUpdateNeedsTerminalGuard || rawToolOutputCompletion || trackedDynamicToolCompletion || shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem);
1014
+ const armPostToolContinuationWatch = () => {
1015
+ turnWatches.armCompletionIdleWatch({ timeoutMs: params.postToolRawAssistantCompletionIdleTimeoutMs });
1016
+ turnWatches.extendAttemptIdleWatch(params.postToolRawAssistantCompletionIdleTimeoutMs);
1017
+ };
1018
+ const armPostProgressReplyWatch = () => {
1019
+ turnWatches.armCompletionIdleWatch({ timeoutMs: CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS });
1020
+ turnWatches.extendAttemptIdleWatch(CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS);
1021
+ };
1078
1022
  if (isCurrentTurnNotification && notification.method === "error") {
1079
1023
  if (isRetryableErrorNotification(notification.params)) turnWatches.disarmCompletionIdleWatch();
1080
1024
  else turnWatches.armCompletionIdleWatch({ pinnedByTerminalError: true });
1081
1025
  turnWatches.disarmAssistantCompletionIdleWatch();
1082
1026
  } else if (isTurnCompletion) turnWatches.disarmAssistantCompletionIdleWatch();
1083
1027
  else if (isCurrentTurnNotification && assistantCompletionCanRelease) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
1084
- else if (postToolRawAssistantCompletionNeedsTerminalGuard) turnWatches.armCompletionIdleWatch({ timeoutMs: params.postToolRawAssistantCompletionIdleTimeoutMs });
1085
- else if (shouldArmPostReasoningSourceReplyWatch || shouldArmPostRawReasoningSourceReplyWatch) turnWatches.armCompletionIdleWatch({ timeoutMs: CODEX_POST_REASONING_SOURCE_REPLY_IDLE_TIMEOUT_MS });
1028
+ else if (postToolRawAssistantCompletionNeedsTerminalGuard || postToolPatchUpdateNeedsTerminalGuard) armPostToolContinuationWatch();
1029
+ else if (shouldArmNoToolPostProgressReplyWatch || shouldArmNoToolPostRawProgressReplyWatch) armPostProgressReplyWatch();
1030
+ else if (trackedDynamicToolCompletion) armPostToolContinuationWatch();
1086
1031
  else if (unblockedAssistantCompletionRelease) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
1087
- else if (shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) turnWatches.armCompletionIdleWatch();
1032
+ else if (shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) if (shouldUsePostToolContinuationWatch) armPostToolContinuationWatch();
1033
+ else turnWatches.armCompletionIdleWatch();
1088
1034
  else if (rawResponseItemCompletedWithNoActiveItems) turnWatches.armCompletionIdleWatch();
1089
- else if (isCurrentTurnNotification && rawToolOutputCompletion) turnWatches.armCompletionIdleWatch();
1035
+ else if (isCurrentTurnNotification && rawToolOutputCompletion) armPostToolContinuationWatch();
1090
1036
  else if (isCurrentTurnNotification && shouldDisarmAssistantCompletionIdleWatch(notification)) turnWatches.disarmAssistantCompletionIdleWatch();
1091
- if (turnWatches.isCompletionIdleWatchArmed() && !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && notification.method !== "turn/completed" && isCurrentTurnNotification && !trackedDynamicToolCompletion && !rawToolOutputCompletion && !postToolRawAssistantCompletionNeedsTerminalGuard && !rawResponseItemCompletedWithNoActiveItems && !shouldArmPostReasoningSourceReplyWatch && !shouldArmPostRawReasoningSourceReplyWatch && !shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) turnWatches.disarmCompletionIdleWatch();
1037
+ if (turnWatches.isCompletionIdleWatchArmed() && !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && notification.method !== "turn/completed" && isCurrentTurnNotification && !isNativeResponseStreamDelta && !trackedDynamicToolCompletion && !rawToolOutputCompletion && !postToolRawAssistantCompletionNeedsTerminalGuard && !postToolPatchUpdateNeedsTerminalGuard && !rawResponseItemCompletedWithNoActiveItems && !shouldArmNoToolPostProgressReplyWatch && !shouldArmNoToolPostRawProgressReplyWatch && !shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) turnWatches.disarmCompletionIdleWatch();
1092
1038
  if (trackedDynamicToolCompletion) {
1093
1039
  const itemId = readNotificationItemId(notification);
1094
1040
  if (itemId) {
@@ -1120,7 +1066,8 @@ function hasCodexAppServerPotentialSideEffectEvidence(result) {
1120
1066
  }
1121
1067
  function buildCodexAppServerPromptTimeoutOutcome(params) {
1122
1068
  const completionIdleTimeoutHadPotentialSideEffects = hasCodexAppServerPotentialSideEffectEvidence(params.result);
1123
- if (!params.turnCompletionIdleTimedOut || params.result.itemLifecycle.completedCount === 0 && !completionIdleTimeoutHadPotentialSideEffects) return;
1069
+ const replayBlockedReason = resolveCodexAppServerReplayBlockedReason(params.result);
1070
+ if (!params.turnCompletionIdleTimedOut || params.result.itemLifecycle.completedCount === 0 && !completionIdleTimeoutHadPotentialSideEffects && replayBlockedReason === void 0) return;
1124
1071
  return {
1125
1072
  message: completionIdleTimeoutHadPotentialSideEffects ? CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_SIDE_EFFECT_USER_MESSAGE : CODEX_APP_SERVER_MISSING_TERMINAL_EVENT_USER_MESSAGE,
1126
1073
  ...completionIdleTimeoutHadPotentialSideEffects ? {
@@ -3013,9 +2960,16 @@ function createCodexAttemptTurnWatchController(params) {
3013
2960
  let completionLastActivityAt = Date.now();
3014
2961
  let completionLastActivityReason = "startup";
3015
2962
  let completionLastActivityDetails;
2963
+ let attemptIdleTimeoutOverrideMs;
3016
2964
  let attemptLastProgressAt = Date.now();
3017
2965
  let attemptLastProgressReason = "startup";
3018
2966
  let attemptLastProgressDetails;
2967
+ const turnCompletionIdleTimeoutMs = resolveTimerTimeoutMs(params.turnCompletionIdleTimeoutMs, 1);
2968
+ const turnAssistantCompletionIdleTimeoutMs = resolveTimerTimeoutMs(params.turnAssistantCompletionIdleTimeoutMs, 1);
2969
+ const turnAttemptIdleTimeoutMs = resolveTimerTimeoutMs(params.turnAttemptIdleTimeoutMs, 1);
2970
+ const turnTerminalIdleTimeoutMs = resolveTimerTimeoutMs(params.turnTerminalIdleTimeoutMs, 1);
2971
+ const interruptTimeoutMs = resolveTimerTimeoutMs(params.interruptTimeoutMs, 1);
2972
+ const resolveWatchTimeoutMs = (timeoutMs) => resolveTimerTimeoutMs(timeoutMs, 1);
3019
2973
  const clearCompletionIdleTimer = () => {
3020
2974
  if (completionIdleTimer) {
3021
2975
  clearTimeout(completionIdleTimer);
@@ -3050,8 +3004,7 @@ function createCodexAttemptTurnWatchController(params) {
3050
3004
  clearCompletionIdleTimer();
3051
3005
  if (params.isCompleted() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
3052
3006
  const elapsedMs = Math.max(0, Date.now() - completionLastActivityAt);
3053
- const timeoutMs = completionIdleTimeoutOverrideMs ?? params.turnCompletionIdleTimeoutMs;
3054
- const delayMs = Math.max(1, timeoutMs - elapsedMs);
3007
+ const delayMs = Math.max(1, (completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs) - elapsedMs);
3055
3008
  completionIdleTimer = setTimeout(fireCompletionIdleTimeout, delayMs);
3056
3009
  completionIdleTimer.unref?.();
3057
3010
  }
@@ -3059,7 +3012,7 @@ function createCodexAttemptTurnWatchController(params) {
3059
3012
  clearAssistantCompletionIdleTimer();
3060
3013
  if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed) return;
3061
3014
  const elapsedMs = Math.max(0, Date.now() - assistantCompletionLastActivityAt);
3062
- const delayMs = Math.max(1, params.turnAssistantCompletionIdleTimeoutMs - elapsedMs);
3015
+ const delayMs = Math.max(1, turnAssistantCompletionIdleTimeoutMs - elapsedMs);
3063
3016
  assistantCompletionIdleTimer = setTimeout(fireAssistantCompletionIdleRelease, delayMs);
3064
3017
  assistantCompletionIdleTimer.unref?.();
3065
3018
  }
@@ -3067,7 +3020,7 @@ function createCodexAttemptTurnWatchController(params) {
3067
3020
  clearAttemptIdleTimer();
3068
3021
  if (params.isCompleted() || params.signal.aborted || !attemptIdleWatchArmed) return;
3069
3022
  const elapsedMs = Math.max(0, Date.now() - attemptLastProgressAt);
3070
- const delayMs = Math.max(1, params.turnAttemptIdleTimeoutMs - elapsedMs);
3023
+ const delayMs = Math.max(1, (attemptIdleTimeoutOverrideMs ?? turnAttemptIdleTimeoutMs) - elapsedMs);
3071
3024
  attemptIdleTimer = setTimeout(fireAttemptIdleTimeout, delayMs);
3072
3025
  attemptIdleTimer.unref?.();
3073
3026
  }
@@ -3075,7 +3028,7 @@ function createCodexAttemptTurnWatchController(params) {
3075
3028
  clearTerminalIdleTimer();
3076
3029
  if (params.isCompleted() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
3077
3030
  const elapsedMs = Math.max(0, Date.now() - completionLastActivityAt);
3078
- const delayMs = Math.max(1, params.turnTerminalIdleTimeoutMs - elapsedMs);
3031
+ const delayMs = Math.max(1, turnTerminalIdleTimeoutMs - elapsedMs);
3079
3032
  terminalIdleTimer = setTimeout(fireTerminalIdleTimeout, delayMs);
3080
3033
  terminalIdleTimer.unref?.();
3081
3034
  }
@@ -3084,6 +3037,14 @@ function createCodexAttemptTurnWatchController(params) {
3084
3037
  scheduleCompletionIdleWatch();
3085
3038
  scheduleTerminalIdleWatch();
3086
3039
  }
3040
+ function recordAttemptProgress(reason, options) {
3041
+ attemptIdleTimeoutOverrideMs = options?.attemptTimeoutMs !== void 0 ? resolveWatchTimeoutMs(options.attemptTimeoutMs) : void 0;
3042
+ attemptLastProgressAt = completionLastActivityAt;
3043
+ attemptLastProgressReason = reason;
3044
+ attemptLastProgressDetails = options?.details;
3045
+ params.onAttemptProgress(reason, options?.details);
3046
+ scheduleAttemptIdleWatch();
3047
+ }
3087
3048
  function fireAssistantCompletionIdleRelease() {
3088
3049
  if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed) return;
3089
3050
  if (params.getActiveAppServerTurnRequests() > 0 || params.getActiveTurnItemCount() > 0) {
@@ -3091,7 +3052,7 @@ function createCodexAttemptTurnWatchController(params) {
3091
3052
  return;
3092
3053
  }
3093
3054
  const idleMs = Math.max(0, Date.now() - assistantCompletionLastActivityAt);
3094
- if (idleMs < params.turnAssistantCompletionIdleTimeoutMs) {
3055
+ if (idleMs < turnAssistantCompletionIdleTimeoutMs) {
3095
3056
  scheduleAssistantCompletionIdleWatch();
3096
3057
  return;
3097
3058
  }
@@ -3103,20 +3064,20 @@ function createCodexAttemptTurnWatchController(params) {
3103
3064
  threadId: params.threadId,
3104
3065
  turnId,
3105
3066
  idleMs,
3106
- timeoutMs: params.turnAssistantCompletionIdleTimeoutMs,
3067
+ timeoutMs: turnAssistantCompletionIdleTimeoutMs,
3107
3068
  ...assistantCompletionLastActivityDetails
3108
3069
  });
3109
3070
  embeddedAgentLog.warn("codex app-server turn released after completed assistant item without terminal event", {
3110
3071
  threadId: params.threadId,
3111
3072
  turnId,
3112
3073
  idleMs,
3113
- timeoutMs: params.turnAssistantCompletionIdleTimeoutMs,
3074
+ timeoutMs: turnAssistantCompletionIdleTimeoutMs,
3114
3075
  ...assistantCompletionLastActivityDetails
3115
3076
  });
3116
3077
  if (turnId) params.onInterruptTurn({
3117
3078
  threadId: params.threadId,
3118
3079
  turnId,
3119
- timeoutMs: params.interruptTimeoutMs
3080
+ timeoutMs: interruptTimeoutMs
3120
3081
  });
3121
3082
  params.onCompleted();
3122
3083
  params.onResolveCompletion();
@@ -3124,14 +3085,15 @@ function createCodexAttemptTurnWatchController(params) {
3124
3085
  function fireAttemptIdleTimeout() {
3125
3086
  if (params.isCompleted() || params.signal.aborted || !attemptIdleWatchArmed) return;
3126
3087
  const idleMs = Math.max(0, Date.now() - attemptLastProgressAt);
3127
- if (idleMs < params.turnAttemptIdleTimeoutMs) {
3088
+ const timeoutMs = attemptIdleTimeoutOverrideMs ?? turnAttemptIdleTimeoutMs;
3089
+ if (idleMs < timeoutMs) {
3128
3090
  scheduleAttemptIdleWatch();
3129
3091
  return;
3130
3092
  }
3131
3093
  const timeout = {
3132
3094
  kind: "progress",
3133
3095
  idleMs,
3134
- timeoutMs: params.turnAttemptIdleTimeoutMs,
3096
+ timeoutMs,
3135
3097
  lastActivityReason: attemptLastProgressReason,
3136
3098
  details: attemptLastProgressDetails
3137
3099
  };
@@ -3157,7 +3119,7 @@ function createCodexAttemptTurnWatchController(params) {
3157
3119
  }
3158
3120
  function fireCompletionIdleTimeout() {
3159
3121
  if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !completionIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
3160
- const timeoutMs = completionIdleTimeoutOverrideMs ?? params.turnCompletionIdleTimeoutMs;
3122
+ const timeoutMs = completionIdleTimeoutOverrideMs ?? turnCompletionIdleTimeoutMs;
3161
3123
  const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
3162
3124
  if (idleMs < timeoutMs) {
3163
3125
  scheduleCompletionIdleWatch();
@@ -3193,14 +3155,14 @@ function createCodexAttemptTurnWatchController(params) {
3193
3155
  function fireTerminalIdleTimeout() {
3194
3156
  if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
3195
3157
  const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
3196
- if (idleMs < params.turnTerminalIdleTimeoutMs) {
3158
+ if (idleMs < turnTerminalIdleTimeoutMs) {
3197
3159
  scheduleTerminalIdleWatch();
3198
3160
  return;
3199
3161
  }
3200
3162
  const timeout = {
3201
3163
  kind: "terminal",
3202
3164
  idleMs,
3203
- timeoutMs: params.turnTerminalIdleTimeoutMs,
3165
+ timeoutMs: turnTerminalIdleTimeoutMs,
3204
3166
  lastActivityReason: completionLastActivityReason,
3205
3167
  details: completionLastActivityDetails
3206
3168
  };
@@ -3239,7 +3201,7 @@ function createCodexAttemptTurnWatchController(params) {
3239
3201
  armCompletionIdleWatch: (options) => {
3240
3202
  completionIdleWatchArmed = true;
3241
3203
  completionIdleWatchPinnedByTerminalError = options?.pinnedByTerminalError === true;
3242
- completionIdleTimeoutOverrideMs = options?.timeoutMs !== void 0 ? Math.max(1, Math.floor(options.timeoutMs)) : void 0;
3204
+ completionIdleTimeoutOverrideMs = options?.timeoutMs !== void 0 ? resolveWatchTimeoutMs(options.timeoutMs) : void 0;
3243
3205
  scheduleCompletionIdleWatch();
3244
3206
  },
3245
3207
  disarmCompletionIdleWatch: () => {
@@ -3264,12 +3226,7 @@ function createCodexAttemptTurnWatchController(params) {
3264
3226
  completionLastActivityReason = reason;
3265
3227
  completionLastActivityDetails = options?.details;
3266
3228
  completionIdleTimeoutOverrideMs = void 0;
3267
- if (options?.attemptProgress) {
3268
- attemptLastProgressAt = completionLastActivityAt;
3269
- attemptLastProgressReason = reason;
3270
- attemptLastProgressDetails = options.details;
3271
- params.onAttemptProgress(reason, options.details);
3272
- }
3229
+ if (options?.attemptProgress) recordAttemptProgress(reason, options);
3273
3230
  params.onProgressDiagnostic(reason);
3274
3231
  if (options?.arm) {
3275
3232
  completionIdleWatchArmed = true;
@@ -3277,9 +3234,15 @@ function createCodexAttemptTurnWatchController(params) {
3277
3234
  }
3278
3235
  scheduleProgressWatches();
3279
3236
  },
3280
- noteNotificationReceived: (method) => {
3237
+ noteNotificationReceived: (method, options) => {
3281
3238
  completionLastActivityAt = Date.now();
3282
3239
  completionLastActivityReason = `notification:${method}`;
3240
+ if (options?.details !== void 0) completionLastActivityDetails = options.details;
3241
+ if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
3242
+ },
3243
+ extendAttemptIdleWatch: (timeoutMs) => {
3244
+ attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
3245
+ scheduleAttemptIdleWatch();
3283
3246
  },
3284
3247
  scheduleProgressWatches,
3285
3248
  clearCompletionIdleTimer,
@@ -3348,7 +3311,7 @@ async function handleDynamicToolCallWithTimeout(params) {
3348
3311
  let resolveAbort;
3349
3312
  const abortFromRun = () => {
3350
3313
  const message = "OpenClaw dynamic tool call aborted.";
3351
- controller.abort(params.signal.reason ?? new Error(message));
3314
+ controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
3352
3315
  resolveAbort?.(failedDynamicToolResponse(message, { sideEffectEvidence: true }));
3353
3316
  };
3354
3317
  const abortPromise = new Promise((resolve) => {
@@ -3428,6 +3391,9 @@ function toCodexDynamicToolProgressResponse(response, protocolResponse) {
3428
3391
  function shouldReleaseTurnAfterTerminalDynamicTool(state) {
3429
3392
  return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
3430
3393
  }
3394
+ function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
3395
+ return response.asyncStarted !== true;
3396
+ }
3431
3397
  function resolveTerminalDynamicToolBatchAction(state) {
3432
3398
  if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
3433
3399
  if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
@@ -3486,15 +3452,14 @@ function clampDynamicToolTimeoutMs(timeoutMs) {
3486
3452
  //#region extensions/codex/src/app-server/local-runtime-attribution.ts
3487
3453
  const OPENAI_PROVIDER_ID = "openai";
3488
3454
  const OPENAI_RESPONSES_API = "openai-responses";
3489
- const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
3490
- const OPENAI_CODEX_RESPONSES_API = "openai-codex-responses";
3455
+ const OPENAI_CODEX_RESPONSES_API = "openai-chatgpt-responses";
3491
3456
  function normalizeRuntimeId(value) {
3492
3457
  return value?.trim().toLowerCase() ?? "";
3493
3458
  }
3494
3459
  function resolveCodexLocalRuntimeAttribution(params) {
3495
3460
  const authProfileProvider = normalizeRuntimeId(params.runtimePlan?.auth?.authProfileProviderForAuth);
3496
3461
  if (normalizeRuntimeId(params.runtimePlan?.observability.harnessId) === "codex" && authProfileProvider !== OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.provider) === OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.api) === OPENAI_RESPONSES_API) return {
3497
- provider: OPENAI_CODEX_PROVIDER_ID,
3462
+ provider: OPENAI_PROVIDER_ID,
3498
3463
  api: OPENAI_CODEX_RESPONSES_API
3499
3464
  };
3500
3465
  return {
@@ -3754,6 +3719,7 @@ async function mirrorCodexAppServerTranscript(params) {
3754
3719
  for (const update of appendedUpdates) emitSessionTranscriptUpdate({
3755
3720
  sessionFile: params.sessionFile,
3756
3721
  ...params.sessionKey ? { sessionKey: params.sessionKey } : {},
3722
+ ...params.agentId ? { agentId: params.agentId } : {},
3757
3723
  message: update.message,
3758
3724
  messageId: update.messageId,
3759
3725
  messageSeq: update.messageSeq
@@ -3826,6 +3792,9 @@ const CODEX_PROMPT_TOTAL_INPUT_KEYS = [
3826
3792
  ];
3827
3793
  const MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM = 20;
3828
3794
  const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 12e3;
3795
+ const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
3796
+ const BYTES_PER_MB = 1024 * 1024;
3797
+ const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * BYTES_PER_MB;
3829
3798
  const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = new Set([
3830
3799
  "message",
3831
3800
  "messages",
@@ -3871,6 +3840,8 @@ var CodexAppServerEventProjector = class {
3871
3840
  this.toolTranscriptResultIds = /* @__PURE__ */ new Set();
3872
3841
  this.transcriptToolProgressCallIds = /* @__PURE__ */ new Set();
3873
3842
  this.nativeGeneratedMediaUrls = /* @__PURE__ */ new Set();
3843
+ this.nativeGeneratedMediaItemIds = /* @__PURE__ */ new Set();
3844
+ this.nativeGeneratedMediaUrlsByItemId = /* @__PURE__ */ new Map();
3874
3845
  this.diagnosticToolStartedAtByItem = /* @__PURE__ */ new Map();
3875
3846
  this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
3876
3847
  this.assistantStarted = false;
@@ -3936,7 +3907,7 @@ var CodexAppServerEventProjector = class {
3936
3907
  await this.handleTurnCompleted(params);
3937
3908
  break;
3938
3909
  case "rawResponseItem/completed":
3939
- this.handleRawResponseItemCompleted(params);
3910
+ await this.handleRawResponseItemCompleted(params);
3940
3911
  break;
3941
3912
  case "error":
3942
3913
  if (readBooleanAlias(params, ["willRetry", "will_retry"]) === true) break;
@@ -3967,7 +3938,7 @@ var CodexAppServerEventProjector = class {
3967
3938
  turnCompleted: Boolean(this.completedTurn)
3968
3939
  });
3969
3940
  const toolMetas = [...this.toolMetas.values()];
3970
- const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.sideEffectingToolItemIds.size > 0 || this.sideEffectingDynamicToolCallIds.size > 0;
3941
+ const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.nativeGeneratedMediaItemIds.size > 0 || this.sideEffectingToolItemIds.size > 0 || this.sideEffectingDynamicToolCallIds.size > 0;
3971
3942
  return {
3972
3943
  aborted: this.aborted,
3973
3944
  externalAbort: false,
@@ -4388,9 +4359,11 @@ var CodexAppServerEventProjector = class {
4388
4359
  text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
4389
4360
  });
4390
4361
  }
4391
- handleRawResponseItemCompleted(params) {
4362
+ async handleRawResponseItemCompleted(params) {
4392
4363
  const item = isJsonObject(params.item) ? params.item : void 0;
4393
- if (!item || readString$5(item, "role") !== "assistant") return;
4364
+ if (!item) return;
4365
+ await this.recordRawGeneratedImageMedia(item);
4366
+ if (readString$5(item, "role") !== "assistant") return;
4394
4367
  const text = extractRawAssistantText(item);
4395
4368
  if (!text) return;
4396
4369
  const itemId = readString$5(item, "id") ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
@@ -4406,7 +4379,56 @@ var CodexAppServerEventProjector = class {
4406
4379
  recordNativeGeneratedMedia(item) {
4407
4380
  if (item?.type !== "imageGeneration") return;
4408
4381
  const savedPath = readItemString(item, "savedPath")?.trim();
4409
- if (savedPath) this.nativeGeneratedMediaUrls.add(savedPath);
4382
+ if (savedPath) this.recordNativeGeneratedMediaUrl({
4383
+ itemId: item.id,
4384
+ mediaUrl: savedPath
4385
+ });
4386
+ }
4387
+ async recordRawGeneratedImageMedia(item) {
4388
+ if (readString$5(item, "type") !== "image_generation_call") return;
4389
+ const result = readString$5(item, "result");
4390
+ if (!result) return;
4391
+ const itemId = readString$5(item, "id") ?? `raw-image-${this.nativeGeneratedMediaItemIds.size}`;
4392
+ this.nativeGeneratedMediaItemIds.add(itemId);
4393
+ const maxBytes = resolveGeneratedImageMaxBytes(this.params.config);
4394
+ const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
4395
+ if (estimatedDecodedBytes !== void 0 && estimatedDecodedBytes > maxBytes) {
4396
+ embeddedAgentLog.warn("codex app-server raw image generation result exceeds media limit", {
4397
+ itemId,
4398
+ estimatedDecodedBytes,
4399
+ maxBytes
4400
+ });
4401
+ return;
4402
+ }
4403
+ const asset = generatedImageAssetFromBase64({
4404
+ base64: result,
4405
+ index: this.nativeGeneratedMediaItemIds.size,
4406
+ revisedPrompt: readString$5(item, "revised_prompt") ?? readString$5(item, "revisedPrompt"),
4407
+ fileNamePrefix: "codex-image-generation",
4408
+ sniffMimeType: true
4409
+ });
4410
+ if (!asset) return;
4411
+ try {
4412
+ const saved = await saveMediaBuffer(asset.buffer, asset.mimeType, GENERATED_IMAGE_MEDIA_SUBDIR, maxBytes, asset.fileName);
4413
+ this.recordNativeGeneratedMediaUrl({
4414
+ itemId,
4415
+ mediaUrl: saved.path
4416
+ });
4417
+ } catch (error) {
4418
+ embeddedAgentLog.warn("codex app-server raw image generation result save failed", {
4419
+ itemId,
4420
+ error
4421
+ });
4422
+ }
4423
+ }
4424
+ recordNativeGeneratedMediaUrl(params) {
4425
+ if (this.nativeGeneratedMediaUrlsByItemId.has(params.itemId)) {
4426
+ this.nativeGeneratedMediaItemIds.add(params.itemId);
4427
+ return;
4428
+ }
4429
+ this.nativeGeneratedMediaUrlsByItemId.set(params.itemId, params.mediaUrl);
4430
+ this.nativeGeneratedMediaUrls.add(params.mediaUrl);
4431
+ this.nativeGeneratedMediaItemIds.add(params.itemId);
4410
4432
  }
4411
4433
  buildToolMediaUrls(toolTelemetry) {
4412
4434
  const mediaUrls = new Set(toolTelemetry.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
@@ -4617,7 +4639,7 @@ var CodexAppServerEventProjector = class {
4617
4639
  this.afterToolCallObservedItemIds.add(item.id);
4618
4640
  const result = itemToolResult(item).result;
4619
4641
  const error = itemToolError(item, status, this.toolResultOutputTextByItem);
4620
- const startedAt = typeof item.durationMs === "number" ? Date.now() - Math.max(0, item.durationMs) : void 0;
4642
+ const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
4621
4643
  const hookParams = {
4622
4644
  toolName: name,
4623
4645
  toolCallId: item.id,
@@ -4833,7 +4855,7 @@ var CodexAppServerEventProjector = class {
4833
4855
  type: "text",
4834
4856
  text
4835
4857
  }],
4836
- api: attribution.api ?? "openai-codex-responses",
4858
+ api: attribution.api ?? "openai-chatgpt-responses",
4837
4859
  provider: attribution.provider,
4838
4860
  model: this.params.modelId,
4839
4861
  usage,
@@ -4850,7 +4872,7 @@ var CodexAppServerEventProjector = class {
4850
4872
  type: "text",
4851
4873
  text: `${title}:\n${text}`
4852
4874
  }],
4853
- api: attribution.api ?? "openai-codex-responses",
4875
+ api: attribution.api ?? "openai-chatgpt-responses",
4854
4876
  provider: attribution.provider,
4855
4877
  model: this.params.modelId,
4856
4878
  usage: ZERO_USAGE,
@@ -4870,7 +4892,7 @@ var CodexAppServerEventProjector = class {
4870
4892
  arguments: args,
4871
4893
  input: args
4872
4894
  }],
4873
- api: attribution.api ?? "openai-codex-responses",
4895
+ api: attribution.api ?? "openai-chatgpt-responses",
4874
4896
  provider: attribution.provider,
4875
4897
  model: this.params.modelId,
4876
4898
  usage: ZERO_USAGE,
@@ -4920,6 +4942,30 @@ function readString$5(record, key) {
4920
4942
  const value = record[key];
4921
4943
  return typeof value === "string" ? value : void 0;
4922
4944
  }
4945
+ function estimateBase64DecodedBytes(base64) {
4946
+ let nonWhitespaceLength = 0;
4947
+ let previousCode = -1;
4948
+ let lastCode = -1;
4949
+ for (let i = 0; i < base64.length; i += 1) {
4950
+ const code = base64.charCodeAt(i);
4951
+ if (isBase64WhitespaceCode(code)) continue;
4952
+ nonWhitespaceLength += 1;
4953
+ previousCode = lastCode;
4954
+ lastCode = code;
4955
+ }
4956
+ if (nonWhitespaceLength === 0) return;
4957
+ const equalsCode = "=".charCodeAt(0);
4958
+ const padding = lastCode === equalsCode ? previousCode === equalsCode ? 2 : 1 : 0;
4959
+ return Math.max(0, Math.floor(nonWhitespaceLength * 3 / 4) - padding);
4960
+ }
4961
+ function isBase64WhitespaceCode(code) {
4962
+ return code === 32 || code === 9 || code === 10 || code === 13;
4963
+ }
4964
+ function resolveGeneratedImageMaxBytes(config) {
4965
+ const configured = config?.agents?.defaults?.mediaMaxMb;
4966
+ if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * BYTES_PER_MB);
4967
+ return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
4968
+ }
4923
4969
  function normalizeNonEmptyString(value) {
4924
4970
  if (typeof value !== "string") return;
4925
4971
  return value.trim() || void 0;
@@ -4946,6 +4992,10 @@ function readNumber$1(record, key) {
4946
4992
  const value = record[key];
4947
4993
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
4948
4994
  }
4995
+ function resolveStartedAtFromDurationMs(durationMs) {
4996
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
4997
+ return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
4998
+ }
4949
4999
  function readNonNegativeInteger(record, key) {
4950
5000
  const value = readNumber$1(record, key);
4951
5001
  return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
@@ -6349,6 +6399,9 @@ function unrefTimer(timer) {
6349
6399
  //#endregion
6350
6400
  //#region extensions/codex/src/app-server/startup-binding.ts
6351
6401
  const CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS = 3e5;
6402
+ const CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS = 2e4;
6403
+ const CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_TOKENS = 8e3;
6404
+ const CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_RATIO = .5;
6352
6405
  const CODEX_APP_SERVER_BYTE_UNITS = {
6353
6406
  b: 1,
6354
6407
  k: 1024,
@@ -6364,6 +6417,7 @@ const CODEX_APP_SERVER_BYTE_UNITS = {
6364
6417
  tb: 1024 * 1024 * 1024 * 1024,
6365
6418
  tib: 1024 * 1024 * 1024 * 1024
6366
6419
  };
6420
+ const codexSessionRecordCache = /* @__PURE__ */ new Map();
6367
6421
  function parseCodexAppServerByteLimit(value) {
6368
6422
  if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
6369
6423
  if (typeof value !== "string") return;
@@ -6419,22 +6473,44 @@ async function listCodexAppServerRolloutFilesForThread(agentDir, threadId, codex
6419
6473
  }
6420
6474
  async function readCodexSessionRecordForSessionFile(sessionFile) {
6421
6475
  const sessionsFile = path.join(path.dirname(sessionFile), "sessions.json");
6476
+ const resolvedSessionFile = path.resolve(sessionFile);
6477
+ let stat;
6478
+ try {
6479
+ stat = await fs.stat(sessionsFile);
6480
+ } catch {
6481
+ codexSessionRecordCache.delete(resolvedSessionFile);
6482
+ return;
6483
+ }
6484
+ const cached = codexSessionRecordCache.get(resolvedSessionFile);
6485
+ if (cached?.sessionsFile === sessionsFile && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) return cached.record;
6422
6486
  let store;
6423
6487
  try {
6424
6488
  store = JSON.parse(await fs.readFile(sessionsFile, "utf8"));
6425
6489
  } catch {
6490
+ codexSessionRecordCache.delete(resolvedSessionFile);
6426
6491
  return;
6427
6492
  }
6428
- if (!isJsonObject(store)) return;
6429
- const resolvedSessionFile = path.resolve(sessionFile);
6493
+ if (!isJsonObject(store)) {
6494
+ codexSessionRecordCache.delete(resolvedSessionFile);
6495
+ return;
6496
+ }
6497
+ let found;
6430
6498
  for (const [sessionKey, record] of Object.entries(store)) {
6431
6499
  if (!isJsonObject(record) || typeof record.sessionFile !== "string") continue;
6432
6500
  if (path.resolve(record.sessionFile) !== resolvedSessionFile) continue;
6433
- return {
6501
+ found = {
6434
6502
  sessionKey,
6435
6503
  ...record
6436
6504
  };
6505
+ break;
6437
6506
  }
6507
+ codexSessionRecordCache.set(resolvedSessionFile, {
6508
+ sessionsFile,
6509
+ mtimeMs: stat.mtimeMs,
6510
+ size: stat.size,
6511
+ record: found
6512
+ });
6513
+ return found;
6438
6514
  }
6439
6515
  async function readCodexAppServerRolloutTokenSnapshot(file) {
6440
6516
  let handle;
@@ -6478,52 +6554,75 @@ function readCodexAppServerRolloutTokenSnapshotLine(line) {
6478
6554
  return;
6479
6555
  }
6480
6556
  }
6481
- function resolveCodexAppServerNativeThreadTokenFuse(modelContextWindow) {
6482
- return modelContextWindow ?? CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS;
6557
+ function toNonNegativeInt(value) {
6558
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return;
6559
+ return Math.floor(value);
6560
+ }
6561
+ function readCompactionConfig(config) {
6562
+ return isJsonObject(config?.agents?.defaults?.compaction) ? config.agents.defaults.compaction : void 0;
6563
+ }
6564
+ function resolveCodexAppServerNativeThreadReserveTokens(config) {
6565
+ const compaction = readCompactionConfig(config);
6566
+ const reserveTokens = toNonNegativeInt(compaction?.reserveTokens);
6567
+ const reserveTokensFloor = toNonNegativeInt(compaction?.reserveTokensFloor);
6568
+ if (reserveTokens !== void 0) return Math.max(reserveTokens, reserveTokensFloor ?? CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS);
6569
+ return reserveTokensFloor ?? CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS;
6570
+ }
6571
+ function resolveCodexAppServerNativeThreadTokenFuse(params) {
6572
+ const projectedTurnTokens = typeof params.projectedTurnTokens === "number" && Number.isFinite(params.projectedTurnTokens) && params.projectedTurnTokens > 0 ? Math.floor(params.projectedTurnTokens) : 0;
6573
+ const contextWindow = params.modelContextWindow ?? CODEX_APP_SERVER_NATIVE_THREAD_FALLBACK_MAX_TOKENS;
6574
+ const minPromptBudget = Math.min(CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_TOKENS, Math.max(1, Math.floor(contextWindow * CODEX_APP_SERVER_NATIVE_THREAD_MIN_PROMPT_BUDGET_RATIO)));
6575
+ const effectiveReserveTokens = Math.min(params.reserveTokens, Math.max(0, contextWindow - minPromptBudget));
6576
+ return Math.max(1, contextWindow - effectiveReserveTokens - projectedTurnTokens);
6483
6577
  }
6484
6578
  function maxFiniteNumber(values) {
6485
6579
  const nums = values.filter((value) => typeof value === "number" && Number.isFinite(value));
6486
6580
  if (nums.length === 0) return;
6487
6581
  return Math.max(...nums);
6488
6582
  }
6583
+ function minFiniteNumber(values) {
6584
+ const nums = values.filter((value) => typeof value === "number" && Number.isFinite(value));
6585
+ if (nums.length === 0) return;
6586
+ return Math.min(...nums);
6587
+ }
6489
6588
  function hasContextEngineThreadBootstrapProjection(binding) {
6490
6589
  return binding.contextEngine?.projection?.mode === "thread_bootstrap";
6491
6590
  }
6492
6591
  async function rotateOversizedCodexAppServerStartupBinding(params) {
6493
6592
  const binding = params.binding;
6494
6593
  if (!binding?.threadId) return binding;
6495
- if (params.config?.agents?.defaults?.compaction?.truncateAfterCompaction !== true) return binding;
6496
- if (params.contextEngineActive === true && hasContextEngineThreadBootstrapProjection(binding)) {
6497
- embeddedAgentLog.debug("codex app-server deferring native transcript size guard for context-engine thread bootstrap", {
6498
- threadId: binding.threadId,
6499
- engineId: binding.contextEngine?.engineId,
6500
- epoch: binding.contextEngine?.projection?.epoch,
6501
- fingerprint: binding.contextEngine?.projection?.fingerprint
6502
- });
6503
- return binding;
6504
- }
6505
6594
  const sessionRecord = await readCodexSessionRecordForSessionFile(params.sessionFile);
6506
- const maxBytes = parseCodexAppServerByteLimit(params.config?.agents?.defaults?.compaction?.maxActiveTranscriptBytes);
6507
6595
  const rolloutFiles = await listCodexAppServerRolloutFilesForThread(params.agentDir, binding.threadId, params.codexHome);
6508
- if (maxBytes !== void 0) {
6509
- const oversizedFiles = rolloutFiles.filter((file) => file.bytes >= maxBytes);
6510
- if (oversizedFiles.length > 0) {
6511
- embeddedAgentLog.warn("codex app-server native transcript exceeded active byte limit; starting a fresh thread", {
6512
- threadId: binding.threadId,
6513
- maxBytes,
6514
- files: oversizedFiles.map((file) => ({
6515
- path: file.path,
6516
- bytes: file.bytes
6517
- }))
6518
- });
6519
- await clearCodexAppServerBinding(params.sessionFile);
6520
- return;
6596
+ const compaction = readCompactionConfig(params.config);
6597
+ const shouldDeferByteGuard = compaction?.truncateAfterCompaction === true && params.contextEngineActive === true && hasContextEngineThreadBootstrapProjection(binding);
6598
+ if (compaction?.truncateAfterCompaction === true && !shouldDeferByteGuard) {
6599
+ const maxBytes = parseCodexAppServerByteLimit(compaction.maxActiveTranscriptBytes);
6600
+ if (maxBytes !== void 0) {
6601
+ const oversizedFiles = rolloutFiles.filter((file) => file.bytes >= maxBytes);
6602
+ if (oversizedFiles.length > 0) {
6603
+ embeddedAgentLog.warn("codex app-server native transcript exceeded active byte limit; starting a fresh thread", {
6604
+ threadId: binding.threadId,
6605
+ maxBytes,
6606
+ files: oversizedFiles.map((file) => ({
6607
+ path: file.path,
6608
+ bytes: file.bytes
6609
+ }))
6610
+ });
6611
+ await clearCodexAppServerBinding(params.sessionFile);
6612
+ return;
6613
+ }
6521
6614
  }
6522
6615
  }
6523
6616
  const nativeTokenSnapshots = await Promise.all(rolloutFiles.map(async (file) => readCodexAppServerRolloutTokenSnapshot(file.path)));
6524
6617
  const nativeTokens = maxFiniteNumber(nativeTokenSnapshots.map((snapshot) => snapshot?.totalTokens));
6525
6618
  const nativeModelContextWindow = maxFiniteNumber(nativeTokenSnapshots.map((snapshot) => snapshot?.modelContextWindow));
6526
- const maxTokens = resolveCodexAppServerNativeThreadTokenFuse(nativeModelContextWindow);
6619
+ const sessionModelContextWindow = typeof sessionRecord?.contextTokens === "number" && Number.isFinite(sessionRecord.contextTokens) && sessionRecord.contextTokens > 0 ? Math.floor(sessionRecord.contextTokens) : void 0;
6620
+ const reserveTokens = resolveCodexAppServerNativeThreadReserveTokens(params.config);
6621
+ const maxTokens = resolveCodexAppServerNativeThreadTokenFuse({
6622
+ modelContextWindow: minFiniteNumber([nativeModelContextWindow, sessionModelContextWindow]),
6623
+ reserveTokens,
6624
+ projectedTurnTokens: params.projectedTurnTokens
6625
+ });
6527
6626
  const sessionTokens = sessionRecord?.totalTokensFresh !== false && typeof sessionRecord?.totalTokens === "number" && Number.isFinite(sessionRecord.totalTokens) ? sessionRecord.totalTokens : void 0;
6528
6627
  const tokenCount = maxFiniteNumber([sessionTokens, nativeTokens]);
6529
6628
  if (tokenCount !== void 0 && tokenCount >= maxTokens) {
@@ -6533,11 +6632,24 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
6533
6632
  sessionKey: sessionRecord?.sessionKey,
6534
6633
  sessionTokens,
6535
6634
  nativeTokens,
6536
- nativeModelContextWindow
6635
+ nativeModelContextWindow,
6636
+ sessionModelContextWindow,
6637
+ reserveTokens,
6638
+ projectedTurnTokens: params.projectedTurnTokens
6537
6639
  });
6538
6640
  await clearCodexAppServerBinding(params.sessionFile);
6539
6641
  return;
6540
6642
  }
6643
+ if (compaction?.truncateAfterCompaction !== true) return binding;
6644
+ if (shouldDeferByteGuard) {
6645
+ embeddedAgentLog.debug("codex app-server deferring native transcript byte guard for context-engine thread bootstrap", {
6646
+ threadId: binding.threadId,
6647
+ engineId: binding.contextEngine?.engineId,
6648
+ epoch: binding.contextEngine?.projection?.epoch,
6649
+ fingerprint: binding.contextEngine?.projection?.fingerprint
6650
+ });
6651
+ return binding;
6652
+ }
6541
6653
  return binding;
6542
6654
  }
6543
6655
  //#endregion
@@ -7056,7 +7168,12 @@ function readRequestId(record) {
7056
7168
  //#endregion
7057
7169
  //#region extensions/codex/src/app-server/run-attempt.ts
7058
7170
  const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
7171
+ const CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN = 4;
7059
7172
  const ensuredCodexWorkspaceDirs = /* @__PURE__ */ new Set();
7173
+ function estimateCodexAppServerProjectedTurnTokens(params) {
7174
+ const inputChars = params.prompt.length + (params.developerInstructions?.length ?? 0);
7175
+ return Math.max(1, Math.ceil(inputChars / CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN));
7176
+ }
7060
7177
  async function ensureCodexWorkspaceDirOnce(workspaceDir) {
7061
7178
  const normalized = path.resolve(workspaceDir);
7062
7179
  if (ensuredCodexWorkspaceDirs.has(normalized)) {
@@ -7202,8 +7319,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
7202
7319
  sessionKey: contextSessionKey,
7203
7320
  ...startupAuthProfileId ? { authProfileId: startupAuthProfileId } : {}
7204
7321
  };
7205
- let activeSessionId = params.sessionId;
7206
- let activeSessionFile = params.sessionFile;
7322
+ const activeSessionId = params.sessionId;
7323
+ const activeSessionFile = params.sessionFile;
7207
7324
  const buildActiveRunAttemptParams = () => ({
7208
7325
  ...runtimeParams,
7209
7326
  sessionId: activeSessionId,
@@ -7363,17 +7480,27 @@ async function runCodexAppServerAttempt(params, options = {}) {
7363
7480
  const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(params, { dynamicTools: toolBridge.availableSpecs }), workspaceBootstrapContext.developerInstructions);
7364
7481
  const openClawPromptContext = buildCodexOpenClawPromptContext({
7365
7482
  params,
7366
- skillsPrompt: params.skillsSnapshot?.prompt,
7367
- workspacePromptContext: workspaceBootstrapContext.promptContext,
7368
- workspaceMemoryReference: renderCodexWorkspaceMemoryReference({
7369
- files: workspaceBootstrapContext.memoryReferenceFiles ?? [],
7370
- toolNames: workspaceBootstrapContext.memoryToolNames
7371
- })
7483
+ workspacePromptContext: workspaceBootstrapContext.promptContext
7484
+ });
7485
+ const skillsCollaborationInstructions = renderCodexSkillsCollaborationInstructions({
7486
+ attempt: params,
7487
+ skillsPrompt: params.skillsSnapshot?.prompt
7372
7488
  });
7373
7489
  let promptText = params.prompt;
7374
7490
  let developerInstructions = baseDeveloperInstructions;
7375
7491
  let prePromptMessageCount = historyMessages.length;
7376
7492
  let contextEngineProjection;
7493
+ let precomputedStaleBindingContinuityProjectionApplied = false;
7494
+ let staleBindingContinuityForcedFreshStart = false;
7495
+ const applyFreshThreadContinuityProjection = () => {
7496
+ const projection = projectContextEngineAssemblyForCodex({
7497
+ assembledMessages: historyMessages,
7498
+ originalHistoryMessages: historyMessages,
7499
+ prompt: params.prompt
7500
+ });
7501
+ promptText = projection.promptText;
7502
+ prePromptMessageCount = projection.prePromptMessageCount;
7503
+ };
7377
7504
  const applyActiveContextEngineProjection = async (decisionStartupBinding) => {
7378
7505
  if (!activeContextEngine) return;
7379
7506
  const assembled = await assembleHarnessContextEngine({
@@ -7435,41 +7562,125 @@ async function runCodexAppServerAttempt(params, options = {}) {
7435
7562
  } catch (assembleErr) {
7436
7563
  embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
7437
7564
  }
7438
- else if (shouldProjectMirroredHistoryForCodexStart({
7439
- startupBinding,
7440
- dynamicToolsFingerprint: codexDynamicToolsFingerprint(toolBridge.specs),
7441
- historyMessages,
7442
- forceProject: !nativeToolSurfaceEnabled
7443
- })) {
7444
- const projection = projectContextEngineAssemblyForCodex({
7445
- assembledMessages: historyMessages,
7446
- originalHistoryMessages: historyMessages,
7447
- prompt: params.prompt
7448
- });
7449
- promptText = projection.promptText;
7450
- prePromptMessageCount = projection.prePromptMessageCount;
7451
- }
7565
+ const codexModelInputHistoryMessages = [];
7452
7566
  const buildPromptFromCurrentInputs = () => resolveAgentHarnessBeforePromptBuildResult({
7453
7567
  prompt: prependCurrentInboundContext(promptText, params.currentInboundContext),
7454
7568
  developerInstructions,
7455
- messages: historyMessages,
7569
+ messages: codexModelInputHistoryMessages,
7456
7570
  ctx: hookContext
7457
7571
  });
7458
7572
  let promptBuild = await buildPromptFromCurrentInputs();
7459
- const decorateCodexTurnPromptText = (prompt) => prependCodexOpenClawPromptContext(prompt, openClawPromptContext);
7573
+ const decorateCodexTurnPromptText = (prompt) => prependCodexOpenClawPromptContext(prompt, openClawPromptContext, { preservePromptWithoutContext: params.bootstrapContextMode === "lightweight" && params.bootstrapContextRunKind === "cron" });
7460
7574
  let codexTurnPromptText = decorateCodexTurnPromptText(promptBuild.prompt);
7461
7575
  const buildCodexTurnCollaborationDeveloperInstructions = () => buildTurnCollaborationMode(params, {
7462
7576
  turnScopedDeveloperInstructions: workspaceBootstrapContext.turnScopedDeveloperInstructions,
7577
+ skillsCollaborationInstructions,
7578
+ memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions,
7463
7579
  heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions
7464
7580
  }).settings.developer_instructions ?? void 0;
7465
7581
  const buildRenderedCodexDeveloperInstructions = () => joinPresentSections(promptBuild.developerInstructions, buildCodexTurnCollaborationDeveloperInstructions());
7582
+ const rebuildCodexPromptBuildFromCurrentProjection = async () => {
7583
+ promptBuild = await buildPromptFromCurrentInputs();
7584
+ codexTurnPromptText = decorateCodexTurnPromptText(promptBuild.prompt);
7585
+ };
7586
+ const rebuildCodexTurnPromptTextFromCurrentProjection = async () => {
7587
+ const nextPromptBuild = await buildPromptFromCurrentInputs();
7588
+ promptBuild = {
7589
+ ...promptBuild,
7590
+ prompt: nextPromptBuild.prompt
7591
+ };
7592
+ codexTurnPromptText = decorateCodexTurnPromptText(nextPromptBuild.prompt);
7593
+ };
7594
+ const selectNewerVisibleHistoryAfterBinding = (binding) => {
7595
+ const bindingUpdatedAt = Date.parse(binding.updatedAt);
7596
+ if (!Number.isFinite(bindingUpdatedAt)) return [];
7597
+ return historyMessages.filter((message) => {
7598
+ if (message.role !== "user" && message.role !== "assistant") return false;
7599
+ const record = message;
7600
+ const idempotencyKey = record.idempotencyKey;
7601
+ if (typeof idempotencyKey === "string" && idempotencyKey.startsWith("codex-app-server:")) return false;
7602
+ const meta = record["__openclaw"];
7603
+ const mirrorIdentity = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorIdentity : void 0;
7604
+ if (typeof mirrorIdentity === "string" && mirrorIdentity.startsWith("codex-app-server:")) return false;
7605
+ const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
7606
+ return Number.isFinite(timestamp) && timestamp > bindingUpdatedAt;
7607
+ });
7608
+ };
7609
+ const applyResumeStaleBindingContinuityProjection = (binding) => {
7610
+ const newerVisibleMessages = selectNewerVisibleHistoryAfterBinding(binding);
7611
+ if (newerVisibleMessages.length === 0) return false;
7612
+ const projection = projectContextEngineAssemblyForCodex({
7613
+ assembledMessages: newerVisibleMessages,
7614
+ originalHistoryMessages: historyMessages,
7615
+ prompt: params.prompt
7616
+ });
7617
+ promptText = projection.promptText;
7618
+ prePromptMessageCount = projection.prePromptMessageCount;
7619
+ return true;
7620
+ };
7621
+ const precomputeNoContextEngineStaleBindingProjection = (binding) => {
7622
+ precomputedStaleBindingContinuityProjectionApplied = false;
7623
+ staleBindingContinuityForcedFreshStart = false;
7624
+ if (activeContextEngine || !binding?.threadId) return false;
7625
+ const projected = applyResumeStaleBindingContinuityProjection(binding);
7626
+ precomputedStaleBindingContinuityProjectionApplied = projected;
7627
+ return projected;
7628
+ };
7629
+ const applyNoContextEngineContinuityProjection = (action, binding) => {
7630
+ if (activeContextEngine || !historyMessages.some((message) => message.role === "user")) return false;
7631
+ if (action === "resumed" && precomputedStaleBindingContinuityProjectionApplied) return true;
7632
+ if (action === "started" && staleBindingContinuityForcedFreshStart) return true;
7633
+ if (action === "resumed" && binding) return applyResumeStaleBindingContinuityProjection(binding);
7634
+ if (action === "started") {
7635
+ applyFreshThreadContinuityProjection();
7636
+ return true;
7637
+ }
7638
+ return false;
7639
+ };
7640
+ if (precomputeNoContextEngineStaleBindingProjection(startupBinding)) await rebuildCodexPromptBuildFromCurrentProjection();
7641
+ const rotateStartupBindingForProjectedTurn = async () => {
7642
+ if (!startupBinding?.threadId) return;
7643
+ const previousThreadId = startupBinding.threadId;
7644
+ const projectedTurnTokens = estimateCodexAppServerProjectedTurnTokens({
7645
+ prompt: codexTurnPromptText,
7646
+ developerInstructions: buildRenderedCodexDeveloperInstructions()
7647
+ });
7648
+ startupBinding = await rotateOversizedCodexAppServerStartupBinding({
7649
+ binding: startupBinding,
7650
+ sessionFile: params.sessionFile,
7651
+ agentDir,
7652
+ codexHome: appServer.start.env?.CODEX_HOME,
7653
+ config: params.config,
7654
+ contextEngineActive: Boolean(activeContextEngine),
7655
+ projectedTurnTokens
7656
+ });
7657
+ if (startupBinding?.threadId) return;
7658
+ staleBindingContinuityForcedFreshStart = precomputedStaleBindingContinuityProjectionApplied;
7659
+ if (activeContextEngine) {
7660
+ contextEngineProjection = void 0;
7661
+ try {
7662
+ await applyActiveContextEngineProjection(void 0);
7663
+ } catch (assembleErr) {
7664
+ embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
7665
+ }
7666
+ }
7667
+ await rebuildCodexPromptBuildFromCurrentProjection();
7668
+ embeddedAgentLog.info("codex app-server rebuilt turn prompt after native thread rotation", {
7669
+ sessionId: params.sessionId,
7670
+ sessionKey: contextSessionKey,
7671
+ previousThreadId,
7672
+ promptChars: codexTurnPromptText.length,
7673
+ developerInstructionChars: buildRenderedCodexDeveloperInstructions()?.length ?? 0
7674
+ });
7675
+ };
7676
+ await rotateStartupBindingForProjectedTurn();
7466
7677
  const systemPromptReport = buildCodexSystemPromptReport({
7467
7678
  attempt: params,
7468
7679
  sessionKey: contextSessionKey,
7469
7680
  workspaceDir: effectiveWorkspace,
7470
7681
  developerInstructions: buildRenderedCodexDeveloperInstructions(),
7471
7682
  workspaceBootstrapContext,
7472
- skillsPrompt: openClawPromptContext ? params.skillsSnapshot?.prompt ?? "" : "",
7683
+ skillsPrompt: skillsCollaborationInstructions ? params.skillsSnapshot?.prompt ?? "" : "",
7473
7684
  tools: toolBridge.availableSpecs
7474
7685
  });
7475
7686
  const trajectoryRecorder = createCodexTrajectoryRecorder({
@@ -7582,6 +7793,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
7582
7793
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
7583
7794
  throw error;
7584
7795
  }
7796
+ if (applyNoContextEngineContinuityProjection(thread.lifecycle.action, thread)) await rebuildCodexTurnPromptTextFromCurrentProjection();
7585
7797
  trajectoryRecorder?.recordEvent("session.started", {
7586
7798
  sessionFile: params.sessionFile,
7587
7799
  threadId: thread.threadId,
@@ -7592,19 +7804,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
7592
7804
  recordCodexTrajectoryContext(trajectoryRecorder, {
7593
7805
  attempt: params,
7594
7806
  cwd: effectiveCwd,
7595
- developerInstructions: promptBuild.developerInstructions,
7807
+ developerInstructions: buildRenderedCodexDeveloperInstructions(),
7596
7808
  prompt: codexTurnPromptText,
7597
7809
  tools: toolBridge.availableSpecs
7598
7810
  });
7599
- let projector;
7600
- let turnId;
7601
7811
  const pendingNotifications = [];
7602
- let userInputBridge;
7603
- let steeringQueue;
7604
7812
  let completed = false;
7605
7813
  let terminalTurnNotificationQueued = false;
7606
7814
  let timedOut = false;
7607
7815
  let turnCompletionIdleTimedOut = false;
7816
+ let turnWatchTimeoutKind;
7608
7817
  let turnCompletionIdleTimeoutMessage;
7609
7818
  let clientClosedPromptError;
7610
7819
  let clientClosedAbort = false;
@@ -7629,6 +7838,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
7629
7838
  let pendingTerminalDynamicToolRelease;
7630
7839
  let terminalDynamicToolReleaseCheckScheduled = false;
7631
7840
  let currentTurnHadNonTerminalDynamicToolResult = false;
7841
+ const turnIdRef = {};
7842
+ const projectorRef = {};
7843
+ const userInputBridgeRef = {};
7844
+ const steeringQueueRef = {};
7632
7845
  const renewNativeHookRelayForTurnProgress = () => {
7633
7846
  if (!nativeHookRelay || options.nativeHookRelay?.ttlMs !== void 0) return;
7634
7847
  const now = Date.now();
@@ -7646,7 +7859,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
7646
7859
  const turnWatches = createCodexAttemptTurnWatchController({
7647
7860
  threadId: thread.threadId,
7648
7861
  signal: runAbortController.signal,
7649
- getTurnId: () => turnId,
7862
+ getTurnId: () => turnIdRef.current,
7650
7863
  isCompleted: () => completed,
7651
7864
  isTerminalTurnNotificationQueued: () => terminalTurnNotificationQueued,
7652
7865
  getActiveAppServerTurnRequests: () => activeAppServerTurnRequests,
@@ -7657,12 +7870,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
7657
7870
  turnTerminalIdleTimeoutMs,
7658
7871
  interruptTimeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS,
7659
7872
  onInterruptTurn: (input) => interruptCodexTurnBestEffort(client, input),
7660
- onTimeout: () => {
7873
+ onTimeout: (timeout) => {
7661
7874
  timedOut = true;
7662
7875
  turnCompletionIdleTimedOut = true;
7876
+ turnWatchTimeoutKind = timeout.kind;
7663
7877
  turnCompletionIdleTimeoutMessage = "codex app-server turn idle timed out waiting for turn/completed";
7664
7878
  },
7665
- onMarkTimedOut: () => projector?.markTimedOut(),
7879
+ onMarkTimedOut: () => projectorRef.current?.markTimedOut(),
7666
7880
  onAbort: (reason) => runAbortController.abort(reason),
7667
7881
  onCompleted: () => {
7668
7882
  completed = true;
@@ -7793,6 +8007,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
7793
8007
  currentPromptTexts: [codexTurnPromptText]
7794
8008
  });
7795
8009
  const handleNotification = async (notification) => {
8010
+ const projector = projectorRef.current;
8011
+ const turnId = turnIdRef.current;
8012
+ const userInputBridge = userInputBridgeRef.current;
8013
+ const steeringQueue = steeringQueueRef.current;
7796
8014
  userInputBridge?.handleNotification(notification);
7797
8015
  if (!projector || !turnId) {
7798
8016
  pendingNotifications.push(notification);
@@ -7803,7 +8021,6 @@ async function runCodexAppServerAttempt(params, options = {}) {
7803
8021
  threadId: thread.threadId,
7804
8022
  turnId,
7805
8023
  currentPromptTexts: [codexTurnPromptText],
7806
- sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
7807
8024
  turnWatches,
7808
8025
  activeTurnItemIds,
7809
8026
  activeAppServerTurnRequests,
@@ -7836,6 +8053,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
7836
8053
  }
7837
8054
  };
7838
8055
  const enqueueNotification = (notification) => {
8056
+ const projector = projectorRef.current;
8057
+ const turnId = turnIdRef.current;
8058
+ const userInputBridge = userInputBridgeRef.current;
7839
8059
  const correlation = describeCodexNotificationCorrelation(notification, {
7840
8060
  threadId: thread.threadId,
7841
8061
  ...turnId ? { turnId } : {}
@@ -7850,7 +8070,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
7850
8070
  return Promise.resolve();
7851
8071
  }
7852
8072
  if (isTerminalTurnNotificationForTurn(notification, turnId)) terminalTurnNotificationQueued = true;
7853
- if (correlation.matchesActiveTurn !== false) turnWatches.noteNotificationReceived(notification.method);
8073
+ const isNativeResponseStreamDelta = isNativeResponseStreamDeltaNotification(notification);
8074
+ const nativeResponseStreamDeltaMatchesActiveTurn = isNativeResponseStreamDelta && (correlation.matchesActiveTurn === true || isUnscopedCodexNotification(correlation) && canAttributeUnscopedNativeResponseDeltaToThisTurn(client));
8075
+ if (correlation.matchesActiveTurn === true || !isNativeResponseStreamDelta && correlation.matchesActiveTurn !== false || nativeResponseStreamDeltaMatchesActiveTurn) turnWatches.noteNotificationReceived(notification.method, isNativeResponseStreamDelta ? {
8076
+ attemptProgress: true,
8077
+ ...turnCrossedToolHandoff ? { attemptTimeoutMs: postToolRawAssistantCompletionIdleTimeoutMs } : {},
8078
+ details: { lastNotificationMethod: notification.method }
8079
+ } : void 0);
7854
8080
  notificationQueue = notificationQueue.then(() => handleNotification(notification), () => handleNotification(notification));
7855
8081
  return notificationQueue;
7856
8082
  };
@@ -7864,6 +8090,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
7864
8090
  });
7865
8091
  const notificationCleanup = client.addNotificationHandler(enqueueNotification);
7866
8092
  const requestCleanup = client.addRequestHandler(async (request) => {
8093
+ const turnId = turnIdRef.current;
8094
+ const userInputBridge = userInputBridgeRef.current;
8095
+ const projector = projectorRef.current;
7867
8096
  let armCompletionWatchOnResponse = false;
7868
8097
  let requestCountsAsTurnActivity = false;
7869
8098
  const markCurrentTurnRequestProgress = () => {
@@ -8045,19 +8274,20 @@ async function runCodexAppServerAttempt(params, options = {}) {
8045
8274
  sessionKey: params.sessionKey,
8046
8275
  durationMs: toolDurationMs
8047
8276
  });
8048
- if (response.terminate === true) {
8049
- pendingOpenClawDynamicToolCompletionIds.delete(call.callId);
8050
- scheduleTurnReleaseAfterTerminalDynamicTool({
8051
- call,
8052
- response,
8053
- durationMs: toolDurationMs
8054
- });
8055
- } else {
8277
+ pendingOpenClawDynamicToolCompletionIds.delete(call.callId);
8278
+ if (response.terminate === true) scheduleTurnReleaseAfterTerminalDynamicTool({
8279
+ call,
8280
+ response,
8281
+ durationMs: toolDurationMs
8282
+ });
8283
+ else if (!shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response)) scheduleTerminalDynamicToolReleaseCheck();
8284
+ else {
8056
8285
  currentTurnHadNonTerminalDynamicToolResult = true;
8057
8286
  pendingTerminalDynamicToolRelease = void 0;
8058
8287
  }
8059
8288
  return protocolResponse;
8060
8289
  } catch (error) {
8290
+ pendingOpenClawDynamicToolCompletionIds.delete(call.callId);
8061
8291
  if (!terminalDiagnosticObserved && !hasPendingDynamicToolTerminalDiagnostic({
8062
8292
  call,
8063
8293
  runId: params.runId,
@@ -8077,15 +8307,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
8077
8307
  } finally {
8078
8308
  if (requestCountsAsTurnActivity) {
8079
8309
  activeAppServerTurnRequests = Math.max(0, activeAppServerTurnRequests - 1);
8310
+ const postToolContinuationTimeoutMs = request.method === "item/tool/call" && turnCrossedToolHandoff ? postToolRawAssistantCompletionIdleTimeoutMs : void 0;
8080
8311
  turnWatches.touchActivity(`request:${request.method}:response`, {
8081
8312
  arm: armCompletionWatchOnResponse,
8082
- attemptProgress: true
8313
+ attemptProgress: true,
8314
+ ...postToolContinuationTimeoutMs !== void 0 ? { attemptTimeoutMs: postToolContinuationTimeoutMs } : {}
8083
8315
  });
8316
+ if (armCompletionWatchOnResponse && postToolContinuationTimeoutMs !== void 0) turnWatches.armCompletionIdleWatch({ timeoutMs: postToolContinuationTimeoutMs });
8084
8317
  scheduleTerminalDynamicToolReleaseCheck();
8085
8318
  } else turnWatches.scheduleProgressWatches();
8086
8319
  }
8087
8320
  });
8088
- let closeCleanup;
8089
8321
  const buildLlmInputEvent = () => ({
8090
8322
  runId: params.runId,
8091
8323
  sessionId: params.sessionId,
@@ -8093,11 +8325,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
8093
8325
  model: params.modelId,
8094
8326
  systemPrompt: buildRenderedCodexDeveloperInstructions(),
8095
8327
  prompt: codexTurnPromptText,
8096
- historyMessages,
8328
+ historyMessages: codexModelInputHistoryMessages,
8097
8329
  imagesCount: params.images?.length ?? 0,
8098
8330
  tools
8099
8331
  });
8100
- const buildTurnStartFailureMessages = () => [...historyMessages, buildCodexUserPromptMessage({
8332
+ const buildCodexModelInputMessages = () => [...codexModelInputHistoryMessages, buildCodexUserPromptMessage({
8101
8333
  ...params,
8102
8334
  prompt: codexTurnPromptText
8103
8335
  })];
@@ -8116,7 +8348,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8116
8348
  },
8117
8349
  capture: codexModelContentCapture,
8118
8350
  tools,
8119
- buildInputMessages: buildTurnStartFailureMessages,
8351
+ buildInputMessages: buildCodexModelInputMessages,
8120
8352
  buildSystemPrompt: buildRenderedCodexDeveloperInstructions,
8121
8353
  onErrorDiagnostic: (error) => {
8122
8354
  embeddedAgentLog.debug("codex app-server model call diagnostic ended with error", { error: formatErrorMessage(error) });
@@ -8132,6 +8364,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
8132
8364
  sandboxPolicy: codexSandboxPolicy,
8133
8365
  environmentSelection: codexEnvironmentSelection,
8134
8366
  turnScopedDeveloperInstructions: workspaceBootstrapContext.turnScopedDeveloperInstructions,
8367
+ skillsCollaborationInstructions,
8368
+ memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions,
8135
8369
  heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions
8136
8370
  });
8137
8371
  codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
@@ -8157,7 +8391,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8157
8391
  turn = await startCodexTurn();
8158
8392
  } catch (error) {
8159
8393
  let turnStartError = error;
8160
- if (shouldRetryContextEngineTurnOnFreshCodexThread({
8394
+ if (shouldUseFreshCodexThreadAfterContextEngineOverflow({
8161
8395
  error: turnStartError,
8162
8396
  contextEngineActive: Boolean(activeContextEngine),
8163
8397
  thread
@@ -8168,20 +8402,26 @@ async function runCodexAppServerAttempt(params, options = {}) {
8168
8402
  });
8169
8403
  try {
8170
8404
  const preRetrySessionFile = activeSessionFile;
8171
- await clearCodexAppServerBinding(preRetrySessionFile);
8172
- if (activeSessionFile !== preRetrySessionFile) await clearCodexAppServerBinding(activeSessionFile);
8173
- thread = await restartContextEngineCodexThread();
8174
- emitCodexAppServerEvent(params, {
8175
- stream: "codex_app_server.lifecycle",
8176
- data: {
8177
- phase: "thread_ready_retry",
8178
- threadId: thread.threadId
8179
- }
8405
+ const clearedPreRetryBinding = await clearCodexAppServerBindingForThread(preRetrySessionFile, thread.threadId);
8406
+ const clearedActiveBinding = activeSessionFile !== preRetrySessionFile ? await clearCodexAppServerBindingForThread(activeSessionFile, thread.threadId) : false;
8407
+ if (!clearedPreRetryBinding && !clearedActiveBinding) embeddedAgentLog.warn("codex app-server preserved newer context-engine binding after resume overflow; skipping fresh retry", {
8408
+ threadId: thread.threadId,
8409
+ error: formatErrorMessage(turnStartError)
8180
8410
  });
8181
- try {
8182
- turn = await startCodexTurn();
8183
- } catch (retryError) {
8184
- turnStartError = retryError;
8411
+ else {
8412
+ thread = await restartContextEngineCodexThread();
8413
+ emitCodexAppServerEvent(params, {
8414
+ stream: "codex_app_server.lifecycle",
8415
+ data: {
8416
+ phase: "thread_ready_retry",
8417
+ threadId: thread.threadId
8418
+ }
8419
+ });
8420
+ try {
8421
+ turn = await startCodexTurn();
8422
+ } catch (retryError) {
8423
+ turnStartError = retryError;
8424
+ }
8185
8425
  }
8186
8426
  } catch (retrySetupError) {
8187
8427
  turnStartError = retrySetupError;
@@ -8240,9 +8480,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
8240
8480
  formatError: formatErrorMessage
8241
8481
  });
8242
8482
  codexModelCallDiagnostics.emitError(turnStartErrorMessage, turnStartFailureKind ? { failureKind: turnStartFailureKind } : {});
8483
+ const turnStartFailureMessages = [...historyMessages, buildCodexUserPromptMessage({
8484
+ ...params,
8485
+ prompt: codexTurnPromptText
8486
+ })];
8243
8487
  await runCodexAgentEndHook(params, {
8244
8488
  event: {
8245
- messages: buildTurnStartFailureMessages(),
8489
+ messages: turnStartFailureMessages,
8246
8490
  success: false,
8247
8491
  error: turnStartErrorMessage,
8248
8492
  durationMs: Date.now() - attemptStartedAt
@@ -8279,7 +8523,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8279
8523
  return { ...buildCodexTurnStartFailureResult({
8280
8524
  params,
8281
8525
  message: usageLimitError.message,
8282
- messagesSnapshot: buildTurnStartFailureMessages(),
8526
+ messagesSnapshot: turnStartFailureMessages,
8283
8527
  systemPromptReport
8284
8528
  }) };
8285
8529
  }
@@ -8291,10 +8535,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
8291
8535
  releaseSharedClientLease = void 0;
8292
8536
  throw new Error("codex app-server turn/start failed without an error");
8293
8537
  }
8294
- turnId = turn.turn.id;
8538
+ turnIdRef.current = turn.turn.id;
8295
8539
  const activeTurnId = turn.turn.id;
8296
8540
  emitExecutionPhaseOnce("turn_accepted", { phase: "turn_accepted" });
8297
- userInputBridge = createCodexUserInputBridge({
8541
+ userInputBridgeRef.current = createCodexUserInputBridge({
8298
8542
  paramsForRun: params,
8299
8543
  threadId: thread.threadId,
8300
8544
  turnId: activeTurnId,
@@ -8306,12 +8550,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
8306
8550
  prompt: codexTurnPromptText,
8307
8551
  imagesCount: params.images?.length ?? 0
8308
8552
  });
8309
- projector = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId, {
8553
+ projectorRef.current = new CodexAppServerEventProjector(params, thread.threadId, activeTurnId, {
8310
8554
  nativePostToolUseRelayEnabled: nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && nativeHookRelay.shouldRelayEvent("post_tool_use"),
8311
8555
  trajectoryRecorder
8312
8556
  });
8313
8557
  if (isTerminalTurnStatus(turn.turn.status) || pendingNotifications.some((notification) => isTerminalTurnNotificationForTurn(notification, activeTurnId))) terminalTurnNotificationQueued = true;
8314
- closeCleanup = client.addCloseHandler?.(() => {
8558
+ const closeCleanup = client.addCloseHandler?.(() => {
8315
8559
  if (completed || terminalTurnNotificationQueued || runAbortController.signal.aborted) return;
8316
8560
  clientClosedPromptError = "codex app-server client closed before turn completed";
8317
8561
  trajectoryRecorder?.recordEvent("turn.client_closed", {
@@ -8329,9 +8573,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
8329
8573
  resolveCompletion?.();
8330
8574
  });
8331
8575
  emitLifecycleStart();
8332
- const activeProjector = projector;
8576
+ const activeProjector = projectorRef.current;
8577
+ if (!activeProjector) throw new Error("codex app-server projector was not initialized");
8333
8578
  turnWatches.armTerminalIdleWatch();
8334
8579
  turnWatches.touchActivity("turn:start", { arm: true });
8580
+ turnWatches.armAttemptIdleWatch();
8581
+ turnWatches.touchActivity("turn:start", { attemptProgress: true });
8335
8582
  for (const notification of pendingNotifications.splice(0)) await enqueueNotification(notification);
8336
8583
  if (!completed && isTerminalTurnStatus(turn.turn.status)) await enqueueNotification({
8337
8584
  method: "turn/completed",
@@ -8345,15 +8592,15 @@ async function runCodexAppServerAttempt(params, options = {}) {
8345
8592
  client,
8346
8593
  threadId: thread.threadId,
8347
8594
  turnId: activeTurnId,
8348
- answerPendingUserInput: (text) => userInputBridge?.handleQueuedMessage(text) ?? false,
8595
+ answerPendingUserInput: (text) => userInputBridgeRef.current?.handleQueuedMessage(text) ?? false,
8349
8596
  signal: runAbortController.signal
8350
8597
  });
8351
- steeringQueue = activeSteeringQueue;
8598
+ steeringQueueRef.current = activeSteeringQueue;
8352
8599
  const handle = {
8353
8600
  kind: "embedded",
8354
8601
  queueMessage: async (text, options) => activeSteeringQueue.queue(text, options),
8355
8602
  isStreaming: () => !completed,
8356
- isCompacting: () => projector?.isCompacting() ?? false,
8603
+ isCompacting: () => projectorRef.current?.isCompacting() ?? false,
8357
8604
  sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
8358
8605
  cancel: () => runAbortController.abort("cancelled"),
8359
8606
  abort: () => runAbortController.abort("aborted")
@@ -8369,16 +8616,16 @@ async function runCodexAppServerAttempt(params, options = {}) {
8369
8616
  threadId: thread.threadId,
8370
8617
  turnId: activeTurnId
8371
8618
  });
8372
- turnWatches.armAttemptIdleWatch();
8373
- turnWatches.armTerminalIdleWatch();
8374
- turnWatches.touchActivity("turn:start", { attemptProgress: true });
8375
8619
  const abortListener = () => {
8376
8620
  if (timedOut) {
8377
- retireCodexAppServerClientAfterTimedOutTurn(client, {
8378
- threadId: thread.threadId,
8379
- turnId: activeTurnId,
8380
- reason: String(runAbortController.signal.reason ?? "timeout")
8381
- }).finally(() => {
8621
+ (async () => {
8622
+ await clearCodexAppServerBindingForThread(activeSessionFile, thread.threadId);
8623
+ await retireCodexAppServerClientAfterTimedOutTurn(client, {
8624
+ threadId: thread.threadId,
8625
+ turnId: activeTurnId,
8626
+ reason: String(runAbortController.signal.reason ?? "timeout")
8627
+ });
8628
+ })().finally(() => {
8382
8629
  resolveCompletion?.();
8383
8630
  });
8384
8631
  return;
@@ -8393,6 +8640,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8393
8640
  if (runAbortController.signal.aborted) abortListener();
8394
8641
  try {
8395
8642
  await completion;
8643
+ await notificationQueue;
8396
8644
  const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected });
8397
8645
  const finalAborted = result.aborted || runAbortController.signal.aborted && !clientClosedAbort;
8398
8646
  let finalPromptError = clientClosedPromptError ?? (turnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : timedOut ? "codex app-server attempt timed out" : result.promptError);
@@ -8403,6 +8651,20 @@ async function runCodexAppServerAttempt(params, options = {}) {
8403
8651
  turnId: activeTurnId,
8404
8652
  error: finalPromptErrorMessage
8405
8653
  });
8654
+ if (shouldUseFreshCodexThreadAfterContextEngineOverflow({
8655
+ error: finalPromptError,
8656
+ contextEngineActive: Boolean(activeContextEngine),
8657
+ thread
8658
+ })) {
8659
+ embeddedAgentLog.warn("codex app-server context-engine turn overflowed after resume; clearing thread binding for recovery", {
8660
+ threadId: thread.threadId,
8661
+ turnId: activeTurnId,
8662
+ error: finalPromptErrorMessage
8663
+ });
8664
+ const preClearSessionFile = activeSessionFile;
8665
+ await clearCodexAppServerBindingForThread(preClearSessionFile, thread.threadId);
8666
+ if (activeSessionFile !== preClearSessionFile) await clearCodexAppServerBindingForThread(activeSessionFile, thread.threadId);
8667
+ }
8406
8668
  const refreshedUsageLimitPromptError = await refreshCodexUsageLimitPromptError({
8407
8669
  client,
8408
8670
  message: finalPromptErrorMessage,
@@ -8526,6 +8788,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
8526
8788
  hookRunner
8527
8789
  });
8528
8790
  shouldDelayNativeHookRelayUnregister = activeProjector.getCompletedTurnStatus() === "completed" && !timedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
8791
+ if (shouldDelayNativeHookRelayUnregister) await markCodexAppServerBindingCoveredThroughTurn({
8792
+ sessionFile: params.sessionFile,
8793
+ threadId: thread.threadId,
8794
+ authProfileStore: params.authProfileStore,
8795
+ agentDir: params.agentDir,
8796
+ config: params.config
8797
+ });
8529
8798
  return {
8530
8799
  ...result,
8531
8800
  timedOut,
@@ -8534,6 +8803,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8534
8803
  promptErrorSource: finalPromptErrorSource,
8535
8804
  ...codexAppServerFailureKind ? { codexAppServerFailure: {
8536
8805
  kind: codexAppServerFailureKind,
8806
+ ...codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind ? { turnWatchTimeoutKind } : {},
8537
8807
  transport: appServer.start.transport,
8538
8808
  threadId: thread.threadId,
8539
8809
  turnId: activeTurnId,
@@ -8565,12 +8835,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
8565
8835
  await trajectoryRecorder?.flush();
8566
8836
  }
8567
8837
  });
8568
- if (!timedOut && !runAbortController.signal.aborted) await steeringQueue?.flushPending();
8838
+ if (!timedOut && !runAbortController.signal.aborted) await steeringQueueRef.current?.flushPending();
8569
8839
  if (!timedOut) await unsubscribeCodexThreadBestEffort(client, {
8570
8840
  threadId: thread.threadId,
8571
8841
  timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
8572
8842
  });
8573
- userInputBridge?.cancelPending();
8843
+ userInputBridgeRef.current?.cancelPending();
8574
8844
  turnWatches.clearAllTimers();
8575
8845
  notificationCleanup();
8576
8846
  requestCleanup();
@@ -8584,7 +8854,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
8584
8854
  await releaseSandboxExecEnvironment();
8585
8855
  runAbortController.signal.removeEventListener("abort", abortListener);
8586
8856
  params.abortSignal?.removeEventListener("abort", abortFromUpstream);
8587
- steeringQueue?.cancel();
8857
+ steeringQueueRef.current?.cancel();
8588
8858
  clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
8589
8859
  }
8590
8860
  }
@@ -8603,10 +8873,31 @@ async function clearCodexBindingAfterInvalidImagePayload(sessionFile, fields) {
8603
8873
  embeddedAgentLog.warn("codex app-server image payload error detected; clearing thread binding", fields);
8604
8874
  await clearCodexAppServerBinding(sessionFile);
8605
8875
  }
8876
+ async function markCodexAppServerBindingCoveredThroughTurn(params) {
8877
+ const currentBinding = await readCodexAppServerBinding(params.sessionFile, {
8878
+ authProfileStore: params.authProfileStore,
8879
+ agentDir: params.agentDir,
8880
+ config: params.config
8881
+ });
8882
+ if (!currentBinding || currentBinding.threadId !== params.threadId) return;
8883
+ const { schemaVersion: _schemaVersion, sessionFile: _boundSessionFile, updatedAt: _updatedAt, ...bindingForWrite } = currentBinding;
8884
+ await writeCodexAppServerBinding(params.sessionFile, bindingForWrite, {
8885
+ authProfileStore: params.authProfileStore,
8886
+ agentDir: params.agentDir,
8887
+ config: params.config
8888
+ });
8889
+ }
8606
8890
  function isNonEmptyString(value) {
8607
8891
  return typeof value === "string" && value.length > 0;
8608
8892
  }
8609
- function shouldRetryContextEngineTurnOnFreshCodexThread(params) {
8893
+ function canAttributeUnscopedNativeResponseDeltaToThisTurn(client) {
8894
+ const activeLeases = client.getActiveSharedLeaseCountForUnscopedNotifications?.();
8895
+ return activeLeases === void 0 || activeLeases <= 1;
8896
+ }
8897
+ function isUnscopedCodexNotification(correlation) {
8898
+ return !correlation.threadId && !correlation.turnId && !correlation.nestedTurnThreadId && !correlation.nestedTurnId;
8899
+ }
8900
+ function shouldUseFreshCodexThreadAfterContextEngineOverflow(params) {
8610
8901
  if (!params.contextEngineActive || params.thread.lifecycle.action !== "resumed") return false;
8611
8902
  return isCodexContextWindowError(params.error);
8612
8903
  }