@openclaw/codex 2026.9.4 → 2026.9.6

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 (160) hide show
  1. package/assets/activity.svg +30 -0
  2. package/assets/icon.png +0 -0
  3. package/dist/.setup/account-usage-runtime-Eem_WGqY.mjs +75 -0
  4. package/dist/{apply-CvT8tW5i.js → .setup/apply-DDOi8uNx.mjs} +24 -30
  5. package/dist/.setup/async-questions-DdhOCwGi.mjs +27 -0
  6. package/dist/{attempt-client-cleanup-DKHzbwt5.js → .setup/attempt-client-cleanup-CEv1cKwF.mjs} +79 -49
  7. package/dist/.setup/attempt-terminal-gZrxQ35N.mjs +5 -0
  8. package/dist/.setup/attempt-workspace-context-DUVEn_CS.mjs +331 -0
  9. package/dist/{auth-binding-CWlKVccJ.js → .setup/auth-binding-DhLWa_7K.mjs} +1 -1
  10. package/dist/{auth-cache-key-7orQae_j.js → .setup/auth-cache-key-B2mpaI9D.mjs} +9 -81
  11. package/dist/{auth-profile-CwzO4_RG.js → .setup/auth-profile-WqZtZfXN.mjs} +1 -1
  12. package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
  13. package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
  14. package/dist/.setup/binding-connection-ThoatDcb.mjs +50 -0
  15. package/dist/.setup/binding-connection.runtime-yoHrABt-.mjs +48 -0
  16. package/dist/{bounded-turn-C_Hneydj.js → .setup/bounded-turn-B2HScEgT.mjs} +143 -32
  17. package/dist/{build-state-BCmg9-5p.js → .setup/build-state-C7EnDVgr.mjs} +1 -1
  18. package/dist/{capabilities-8vx68WLH.js → .setup/capabilities-CDXOOFdZ.mjs} +1 -1
  19. package/dist/{client-B57KWPQx.js → .setup/client-Cs08OXVQ.mjs} +705 -512
  20. package/dist/.setup/client-catalog-response-BMgt_4q4.mjs +285 -0
  21. package/dist/{command-dispatch-BpduNGVf.js → .setup/command-dispatch-B4JuOsOk.mjs} +8 -4
  22. package/dist/{command-formatters-lxlkNZgU.js → .setup/command-formatters-Bmqvr9tO.mjs} +8 -4
  23. package/dist/{command-handlers-tiL9iWj7.js → .setup/command-handlers-DW7Lx9oH.mjs} +137 -186
  24. package/dist/{command-rpc-CR5EOEZX.js → .setup/command-rpc-DixIj3HW.mjs} +47 -23
  25. package/dist/{compact-B_H3AOd7.js → .setup/compact-Du5UvtxA.mjs} +29 -51
  26. package/dist/{config-oIORaQ5T.js → .setup/config-BoTP_mrL.mjs} +5 -4
  27. package/dist/{config-options-CXMq1T-C.js → .setup/config-options-BvaRs51b.mjs} +50 -446
  28. package/dist/{protocol-5bh1G-H7.js → .setup/config-parsing-CcB9iPoq.mjs} +9 -51
  29. package/dist/{config-runtime-D0zGNIHu.js → .setup/config-runtime-C8Rw1m1m.mjs} +2 -2
  30. package/dist/.setup/config-security-BEReZ6go.mjs +380 -0
  31. package/dist/{conversation-binding-DsB4-O7U.js → .setup/conversation-binding-DP-gMrBP.mjs} +29 -38
  32. package/dist/{conversation-binding-data-BiodpIzq.js → .setup/conversation-binding-data-CrcyZk6O.mjs} +1 -1
  33. package/dist/{conversation-control-CtUu1b3m.js → .setup/conversation-control-uK9Jomlc.mjs} +32 -67
  34. package/dist/.setup/cyber-failover-DYwbmf1o.mjs +123 -0
  35. package/dist/.setup/cyber-failover-notice-e9G3J9-e.mjs +17 -0
  36. package/dist/{dynamic-tools-CmN5RAip.js → .setup/dynamic-tools-BDXl-8Cw.mjs} +936 -620
  37. package/dist/{effective-mcp-catalog-Bd73bqA8.js → .setup/effective-mcp-catalog-CvnvnGIe.mjs} +3 -3
  38. package/dist/{ephemeral-turn-D78_VmzO.js → .setup/ephemeral-turn-poidXgek.mjs} +9 -7
  39. package/dist/{event-projector-assistant-message-BmBM4qBV.js → .setup/event-projector-assistant-message-BsruvNrH.mjs} +8 -7
  40. package/dist/{event-projector-items-5VhsECCO.js → .setup/event-projector-items-C6L_5ZiT.mjs} +42 -8
  41. package/dist/{attempt-notifications-C9XufG6l.js → .setup/event-projector-usage-CnBS8Wa3.mjs} +102 -9
  42. package/dist/{harness-C7rRa7bm.js → .setup/harness-a_MsE4-_.mjs} +81 -21
  43. package/dist/{inference-context-Dr_mzvk4.js → .setup/inference-context-D96l9-ew.mjs} +1 -1
  44. package/dist/.setup/inference-proxy-b-aJMSxm.mjs +601 -0
  45. package/dist/{isolated-completion-DWVb5U2J.js → .setup/isolated-completion-BIZFhTNG.mjs} +18 -11
  46. package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
  47. package/dist/{managed-binary-D22rNEcR.js → .setup/managed-binary-BnshlFag.mjs} +14 -3
  48. package/dist/{managed-thread-store-D1t5jh3G.js → .setup/managed-thread-store-BMqgThVL.mjs} +48 -14
  49. package/dist/{model-catalog-Dq2n8f_p.js → .setup/model-catalog-CfXJf9Ay.mjs} +36 -12
  50. package/dist/{models-PCKOim-h.js → .setup/models-KnqBv0hE.mjs} +3 -3
  51. package/dist/.setup/native-auth-rWbGbMn3.mjs +57 -0
  52. package/dist/{native-subagent-history-DjtPloaU.js → .setup/native-subagent-history-0nYYgo0k.mjs} +43 -35
  53. package/dist/.setup/native-subagent-monitor-D3filLY2.mjs +4859 -0
  54. package/dist/{native-subagent-task-ids-BzTdT0Uw.js → .setup/native-subagent-task-ids-wb8CKZGN.mjs} +98 -44
  55. package/dist/{node-cli-sessions-DKaOmVUh.js → .setup/node-cli-sessions-r3avj02l.mjs} +86 -68
  56. package/dist/.setup/node-exec-server.runtime-BDwWXVpK.mjs +247 -0
  57. package/dist/{plan-7uG-Fp36.js → .setup/plan-CN3VwfNU.mjs} +133 -119
  58. package/dist/{plugin-activation-BYQculil.js → .setup/plugin-activation-DT3MNtqj.mjs} +7 -16
  59. package/dist/{plugin-app-cache-key-d0eKxy7b.js → .setup/plugin-app-cache-key-B2CsSdnV.mjs} +32 -44
  60. package/dist/{plugin-approval-roundtrip-Bz3FT8W5.js → .setup/plugin-approval-roundtrip-C0MZzCuP.mjs} +1 -1
  61. package/dist/{plugin-inventory-oL4Na1ZG.js → .setup/plugin-inventory-BoRei8Z4.mjs} +21 -11
  62. package/dist/{plugin-marketplace-discovery-P-Vndfqq.js → .setup/plugin-marketplace-discovery-CDOi8IFs.mjs} +4 -1
  63. package/dist/{plugin-metadata-cache-ClPJpRio.js → .setup/plugin-metadata-cache-Ctmo83k_.mjs} +226 -141
  64. package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
  65. package/dist/{provider-MQ8PVkuE.js → .setup/provider-BOsxQHOM.mjs} +9 -5
  66. package/dist/{provider-capabilities-BvCUL69A.js → .setup/provider-capabilities-BfE88FZ5.mjs} +31 -3
  67. package/dist/{rate-limits-CBMmWBr9.js → .setup/rate-limits-CMU3DUD0.mjs} +174 -151
  68. package/dist/{request-B7hEkBGq.js → .setup/request-D2L0zMrq.mjs} +130 -42
  69. package/dist/{run-attempt-CvsAAGa0.js → .setup/run-attempt-BHu11r7h.mjs} +1628 -966
  70. package/dist/.setup/run-attempt-lifecycle-B7JbY4-F.mjs +71 -0
  71. package/dist/{runtime-artifact-BJ0rXBZ_.js → .setup/runtime-artifact-CLZ8udh-.mjs} +29 -45
  72. package/dist/{sandbox-guard-BxgyoM8M.js → .setup/sandbox-guard-C2sRMOEY.mjs} +3 -3
  73. package/dist/{session-binding-CI8UuilT.js → .setup/session-binding-Cm0apEbd.mjs} +36 -83
  74. package/dist/{session-binding-record-Bcvslnhf.js → .setup/session-binding-record-BGoz8wOK.mjs} +202 -5
  75. package/dist/.setup/session-binding-sidecars-BlB5EvIS.mjs +546 -0
  76. package/dist/.setup/session-catalog-Pno1xGcl.mjs +2764 -0
  77. package/dist/{session-catalog-adoption-tcZi1IfX.js → .setup/session-catalog-adoption-BI6UTDZT.mjs} +58 -26
  78. package/dist/{session-catalog-archive-CwKg8Xz2.js → .setup/session-catalog-archive-lHBqzhaO.mjs} +6 -6
  79. package/dist/.setup/session-catalog-events-Bj6j94E_.mjs +347 -0
  80. package/dist/.setup/session-catalog-index-B9_VQIUz.mjs +1170 -0
  81. package/dist/.setup/session-catalog-index-order-hcdz07yN.mjs +48 -0
  82. package/dist/.setup/session-catalog-index-state-Dgh8ORJm.mjs +183 -0
  83. package/dist/{thread-history-page-D0JIp48V.js → .setup/session-catalog-native-projection-DowriLid.mjs} +223 -95
  84. package/dist/{session-catalog-node-adoption-B1tgDW-m.js → .setup/session-catalog-node-adoption-ITddWx_X.mjs} +25 -13
  85. package/dist/{session-catalog-node-continue-Ci-DXyJP.js → .setup/session-catalog-node-continue-6g6qi4A9.mjs} +24 -15
  86. package/dist/.setup/session-catalog-projection-zK_PUsiE.mjs +422 -0
  87. package/dist/{session-cli-DNN_jhGc.js → .setup/session-cli-irrD-uo7.mjs} +3 -3
  88. package/dist/{session-history-Cz-tOOYj.js → .setup/session-history-BrqhSCf9.mjs} +19 -19
  89. package/dist/{session-history-import-DwyijFI6.js → .setup/session-history-import-D-3wAgOP.mjs} +4 -4
  90. package/dist/{session-history-read-cxPUxyVV.js → .setup/session-history-read-B3ZfSUbF.mjs} +2 -2
  91. package/dist/{session-history.worker-BeK1YOyT.js → .setup/session-history.worker-CaCh0xF_.mjs} +10 -24
  92. package/dist/{session-initialization-Ct0_Hzmg.js → .setup/session-initialization-rcgbZiid.mjs} +2 -2
  93. package/dist/{session-permission-policy-BBmXA8Eg.js → .setup/session-permission-policy-BI1QK_98.mjs} +2 -2
  94. package/dist/{session-retirement-B2YfbAAT.js → .setup/session-retirement-BH-S6qkJ.mjs} +26 -7
  95. package/dist/{settled-turn-context-Du6gdUDd.js → .setup/settled-turn-context-fmwouT5I.mjs} +2 -2
  96. package/dist/{settled-turn-finalizer-Dn91stmc.js → .setup/settled-turn-finalizer-DiAi6Kx6.mjs} +12 -12
  97. package/dist/{shared-client-CscigXXL.js → .setup/shared-client-DA4VR4Eb.mjs} +332 -221
  98. package/dist/{side-question-BGMJQf4U.js → .setup/side-question-BJ1wSDcB.mjs} +183 -230
  99. package/dist/{source-CElXU0Oy.js → .setup/source-CUzJq5WN.mjs} +11 -10
  100. package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
  101. package/dist/{thread-archive-guard-DfpBr-Sz.js → .setup/thread-archive-guard-C9frICzX.mjs} +10 -3
  102. package/dist/.setup/thread-history-page-DoVpnHim.mjs +83 -0
  103. package/dist/{thread-lifecycle-DXl1-I6b.js → .setup/thread-lifecycle-B3mfz1TG.mjs} +347 -2318
  104. package/dist/{thread-ownership-BsDRTsyp.js → .setup/thread-ownership-DcTtAcXj.mjs} +6 -4
  105. package/dist/{thread-policy-BuEQfE0g.js → .setup/thread-policy-DGuQsuFC.mjs} +19 -3
  106. package/dist/.setup/thread-requests-BLvGkP2R.mjs +2205 -0
  107. package/dist/{thread-resume-DsDOZpFg.js → .setup/thread-resume-DIBXwOpE.mjs} +8 -5
  108. package/dist/.setup/timeout-C910MdAB.mjs +80 -0
  109. package/dist/{transcript-history-projection-v17wXgdK.js → .setup/transcript-history-projection-ZZpkFkJ_.mjs} +96 -61
  110. package/dist/{transcript-mirror-C5CKgNy6.js → .setup/transcript-mirror-BtHDBp1G.mjs} +224 -195
  111. package/dist/{transcript-mirror-attestation-DibE0krH.js → .setup/transcript-mirror-attestation-CzM-94NC.mjs} +4 -1
  112. package/dist/{transport-stdio-BGjHoYLr.js → .setup/transport-stdio-h2VHOskC.mjs} +274 -89
  113. package/dist/{upstream-session-fork-BZerMte_.js → .setup/upstream-session-fork-BUr4OUbC.mjs} +51 -42
  114. package/dist/{usage-tCk-mg_x.js → .setup/usage-BML0r-ua.mjs} +5 -4
  115. package/dist/.setup/usage-limit-error-DUMbefVY.mjs +166 -0
  116. package/dist/{web-search-provider.runtime-B7bGbGWq.js → .setup/web-search-provider.runtime-CDKlN-Rv.mjs} +4 -3
  117. package/dist/api.js +23 -12
  118. package/dist/catalog-page-worker-entrypoint.js +12 -0
  119. package/dist/catalog-page.worker.js +9 -0
  120. package/dist/cli-metadata.js +1 -1
  121. package/dist/doctor-contract-api.js +8 -528
  122. package/dist/harness.js +1 -1
  123. package/dist/index.js +306 -215
  124. package/dist/media-understanding-provider.js +3 -3
  125. package/dist/migration-provider-api.js +1 -1
  126. package/dist/native-tool-policy.js +12 -0
  127. package/dist/provider-discovery.js +18 -0
  128. package/dist/session-history-worker-runtime.js +36 -35
  129. package/dist/session-history.worker.js +2 -2
  130. package/dist/web-search-contract-api.js +1 -1
  131. package/openclaw.plugin.json +354 -69
  132. package/package.json +14 -8
  133. package/dist/attempt-context-D44yeDII.js +0 -636
  134. package/dist/binding-connection-D3udKbIE.js +0 -60
  135. package/dist/event-projector-usage-BfXmpOec.js +0 -102
  136. package/dist/inference-proxy-HGXKUIre.js +0 -311
  137. package/dist/launch-args-RXQwn8zV.js +0 -90
  138. package/dist/native-subagent-history-owner-Pcp7JuVF.js +0 -34
  139. package/dist/native-subagent-monitor-fQ7eIXEf.js +0 -2978
  140. package/dist/node-exec-server.runtime-Cl2vapEi.js +0 -226
  141. package/dist/rpc-error-ttN_QjEm.js +0 -30
  142. package/dist/session-catalog-ct-AH01I.js +0 -1477
  143. package/dist/session-catalog-home-id-CwhLdC03.js +0 -18
  144. package/dist/session-catalog-provenance-D-dIfRAH.js +0 -96
  145. package/dist/timeout-BVw8aaM1.js +0 -33
  146. package/dist/version-omm6IPI3.js +0 -12
  147. /package/dist/{auth-profile-selection-DFu9e167.js → .setup/auth-profile-selection-DFu9e167.mjs} +0 -0
  148. /package/dist/{config-utils-DujwEnhg.js → .setup/config-utils-DujwEnhg.mjs} +0 -0
  149. /package/dist/{context-compaction-activity-CMla5FPJ.js → .setup/context-compaction-activity-CMla5FPJ.mjs} +0 -0
  150. /package/dist/{elicitation-input-DPoK8F7w.js → .setup/elicitation-input-DPoK8F7w.mjs} +0 -0
  151. /package/dist/{history-rejection-B4nOXNXF.js → .setup/history-rejection-B4nOXNXF.mjs} +0 -0
  152. /package/dist/{image-payload-sanitizer-tupAr-2o.js → .setup/image-payload-sanitizer-tupAr-2o.mjs} +0 -0
  153. /package/dist/{incognito-session-uhrBF6wJ.js → .setup/incognito-session-uhrBF6wJ.mjs} +0 -0
  154. /package/dist/{local-runtime-attribution-B_2mnqhb.js → .setup/local-runtime-attribution-B_2mnqhb.mjs} +0 -0
  155. /package/dist/{native-execution-policy-umd0tgh4.js → .setup/native-execution-policy-umd0tgh4.mjs} +0 -0
  156. /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
  157. /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
  158. /package/dist/{session-upstream-marker-D15C9NHp.js → .setup/session-upstream-marker-D15C9NHp.mjs} +0 -0
  159. /package/dist/{upstream-prompt-provenance-LphB8slG.js → .setup/upstream-prompt-provenance-LphB8slG.mjs} +0 -0
  160. /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
@@ -0,0 +1,4859 @@
1
+ import { C as readCodexNativeSubagentHistoryOwner, S as matchesCodexNativeSubagentHistoryOwner, y as assertHistoryOwnerMatchesRegistration } from "./session-binding-record-BGoz8wOK.mjs";
2
+ import { i as isJsonObject } from "./protocol-CANUwXJ3.mjs";
3
+ import { D as readNullableString$1, S as readItem, a as isNonSuccessItemStatus, c as itemName, d as matchesCodexSnapshotTurn, h as unknownItemStatus, i as isMutatingNativeToolItem, l as itemStatus, m as shouldSynthesizeToolProgressForItem, n as auditNativeToolTerminalStatus, o as isSideEffectingNativeToolItem, p as shouldRecordNativeToolTranscript, s as itemKind, u as itemTitle, x as readHookOutputEntries } from "./event-projector-items-C6L_5ZiT.mjs";
4
+ import { t as attachCodexMirrorIdentity } from "./upstream-prompt-provenance-LphB8slG.mjs";
5
+ import { t as readCodexMirroredSessionHistoryMessages } from "./session-history-BrqhSCf9.mjs";
6
+ import { I as hasCodexAppServerLiveThread, M as claimCodexAppServerLiveThread, W as retainCodexAppServerLiveThread } from "./shared-client-DA4VR4Eb.mjs";
7
+ import { t as resolveCodexLocalRuntimeAttribution } from "./local-runtime-attribution-B_2mnqhb.mjs";
8
+ import { A as formatToolSummary, C as sanitizeCodexToolArguments, D as ToolOutputAccumulator, E as TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS, M as readCodexResponseOutput, N as toolOutputRawEchoSignature, O as collectDynamicToolContentText, P as truncateToolTranscriptText, S as resolveCodexToolProgressDetailMode, _ as readCodeModeNativePatchInput, a as readCodexNativeSubagentRunId, c as isCommandBearingToolItem, d as itemOutputText, f as itemToolArgs, g as projectCodexToolActivity, h as itemTranscriptResultText, i as codexNativeSubagentRunId, j as normalizeToolTranscriptArguments, k as formatToolOutput, l as isNativePostToolUseRelayItem, m as itemToolResult, n as CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX, o as readNativeSubagentThreadIds, p as itemToolError, r as CODEX_NATIVE_SUBAGENT_TASK_KIND, s as readNativeTaskAssignment, t as CODEX_NATIVE_SUBAGENT_RUNTIME, u as itemMeta, v as readInterceptedNativePatchInput, x as isCodexCommandBearingToolCall, y as shouldSuppressChannelProgressForItem } from "./native-subagent-task-ids-wb8CKZGN.mjs";
9
+ import { n as codexApprovalTimeoutText } from "./plugin-approval-roundtrip-C0MZzCuP.mjs";
10
+ import { asFiniteNumber, normalizeOptionalString, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
11
+ import { randomUUID } from "node:crypto";
12
+ import { isDeepStrictEqual } from "node:util";
13
+ import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
14
+ import path from "node:path";
15
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
16
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
17
+ import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, embeddedAgentLog, emitAgentEvent, formatErrorMessage, inferToolMetaFromArgs, projectAgentActivityItem, runAgentHarnessAfterToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
18
+ import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
19
+ //#region extensions/codex/src/app-server/event-projector-tool-progress.ts
20
+ const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = /* @__PURE__ */ new Set([
21
+ "message",
22
+ "messages",
23
+ "reply",
24
+ "send",
25
+ "reaction",
26
+ "react",
27
+ "typing"
28
+ ]);
29
+ function shouldEmitTranscriptToolProgress(toolName) {
30
+ const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
31
+ return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
32
+ }
33
+ var CodexToolProgressProjection = class {
34
+ constructor(params) {
35
+ this.params = params;
36
+ this.echoesByItem = /* @__PURE__ */ new Map();
37
+ this.resultSummaryItemIds = /* @__PURE__ */ new Set();
38
+ this.resultOutputItemIds = /* @__PURE__ */ new Set();
39
+ this.resultOutputStreamedItemIds = /* @__PURE__ */ new Set();
40
+ this.transcriptProgressSuppressedIds = /* @__PURE__ */ new Set();
41
+ this.resultOutputDeltaState = /* @__PURE__ */ new Map();
42
+ this.output = new ToolOutputAccumulator();
43
+ this.metas = /* @__PURE__ */ new Map();
44
+ this.sideEffectingNativeIds = /* @__PURE__ */ new Set();
45
+ this.sideEffectingDynamicIds = /* @__PURE__ */ new Set();
46
+ this.transcriptProgressCallIds = /* @__PURE__ */ new Set();
47
+ this.approvalTimeoutKinds = /* @__PURE__ */ new Map();
48
+ }
49
+ get outputTextByItem() {
50
+ return this.output.textByItem;
51
+ }
52
+ isOutputTruncated(itemId) {
53
+ return this.output.isTruncated(itemId);
54
+ }
55
+ get toolMetas() {
56
+ return [...this.metas.values()];
57
+ }
58
+ getToolMeta(itemId) {
59
+ return this.metas.get(itemId);
60
+ }
61
+ get lastToolError() {
62
+ return this.lastNativeToolError;
63
+ }
64
+ get hasPotentialSideEffects() {
65
+ return this.sideEffectingNativeIds.size > 0 || this.sideEffectingDynamicIds.size > 0;
66
+ }
67
+ approvalTimeoutExplanation(itemId, status) {
68
+ const kind = isNonSuccessItemStatus(status) && this.approvalTimeoutKinds.get(itemId);
69
+ return kind ? codexApprovalTimeoutText(kind) : void 0;
70
+ }
71
+ setLastToolError(error) {
72
+ if (!error) {
73
+ this.lastNativeToolError = void 0;
74
+ return;
75
+ }
76
+ const terminalResolution = this.params.observeToolTerminal?.({
77
+ toolName: error.toolName,
78
+ ...error.meta ? { meta: error.meta } : {},
79
+ outcome: "failure",
80
+ failure: {
81
+ ...error.errorCode ? { errorCode: error.errorCode } : {},
82
+ ...error.error ? { error: error.error } : {},
83
+ ...error.validationErrorSummary ? { validationErrorSummary: error.validationErrorSummary } : {},
84
+ ...error.timedOut ? { timedOut: true } : {},
85
+ ...error.middlewareError ? { middlewareError: true } : {}
86
+ },
87
+ nativeMutation: {
88
+ mutatingAction: error.mutatingAction === true,
89
+ replaySafe: error.mutatingAction !== true
90
+ }
91
+ });
92
+ this.lastNativeToolError = terminalResolution?.lastToolError ?? (this.lastNativeToolError?.mutatingAction && error.mutatingAction !== true ? this.lastNativeToolError : error);
93
+ }
94
+ recordDynamicToolResult(params) {
95
+ const resultText = collectDynamicToolContentText(params.contentItems);
96
+ const existing = this.metas.get(params.callId);
97
+ this.metas.set(params.callId, {
98
+ toolName: existing?.toolName ?? params.tool,
99
+ ...existing?.meta ? { meta: existing.meta } : {},
100
+ ...params.asyncStarted === true ? { asyncStarted: true } : {},
101
+ isError: !params.success
102
+ });
103
+ if (params.terminalResolution) this.lastNativeToolError = params.terminalResolution.lastToolError;
104
+ else if (!params.success) this.lastNativeToolError = {
105
+ toolName: params.tool,
106
+ error: resultText || (params.terminalType === "blocked" ? "codex dynamic tool blocked" : "codex dynamic tool failed")
107
+ };
108
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
109
+ if (params.sideEffectEvidence === true) this.sideEffectingDynamicIds.add(params.callId);
110
+ }
111
+ handleOutputDelta(params, toolName) {
112
+ const itemId = readStringField(params, "itemId");
113
+ const delta = readStringField(params, "delta");
114
+ if (!itemId || !delta) return;
115
+ const storedOutput = this.output.append(itemId, delta);
116
+ this.rememberEcho(itemId, {
117
+ displayText: storedOutput.text,
118
+ rawLength: storedOutput.normalizedLength,
119
+ rawPrefix: storedOutput.rawPrefix,
120
+ streamedDisplay: true
121
+ });
122
+ if (!this.shouldEmitToolOutput()) return;
123
+ if (this.transcriptProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName)) return;
124
+ const state = this.resultOutputDeltaState.get(itemId) ?? {
125
+ chars: 0,
126
+ messages: 0,
127
+ truncated: false
128
+ };
129
+ if (state.truncated) return;
130
+ const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
131
+ const chunk = delta.length > remainingChars ? truncateUtf16Safe(delta, remainingChars) : delta;
132
+ state.chars += chunk.length;
133
+ state.messages += 1;
134
+ const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= 20;
135
+ if (reachedLimit) state.truncated = true;
136
+ this.resultOutputDeltaState.set(itemId, state);
137
+ this.resultOutputStreamedItemIds.add(itemId);
138
+ this.emitToolResultMessage({
139
+ itemId,
140
+ text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
141
+ });
142
+ }
143
+ recordNativeToolError(params) {
144
+ const executionStarted = params.status !== "blocked";
145
+ const mutatingAction = executionStarted && isMutatingNativeToolItem(params.item);
146
+ const isFailure = isNonSuccessItemStatus(params.status);
147
+ const approvalTimeoutExplanation = this.approvalTimeoutExplanation(params.item.id, params.status);
148
+ const error = isFailure ? approvalTimeoutExplanation ?? itemToolError(params.item, params.status, this.output.textByItem) : void 0;
149
+ const failure = error ? {
150
+ ...approvalTimeoutExplanation ? { errorCode: "approval_timeout" } : {},
151
+ error,
152
+ ...approvalTimeoutExplanation ? { timedOut: true } : {}
153
+ } : {};
154
+ const terminalResolution = this.params.observeToolTerminal?.({
155
+ toolCallId: params.item.id,
156
+ toolName: params.name,
157
+ arguments: itemToolArgs(params.item),
158
+ ...params.meta ? { meta: params.meta } : {},
159
+ executionStarted,
160
+ outcome: isFailure ? "failure" : "success",
161
+ ...isFailure ? { failure } : {},
162
+ nativeMutation: {
163
+ mutatingAction,
164
+ replaySafe: !mutatingAction
165
+ }
166
+ });
167
+ if (terminalResolution) {
168
+ this.lastNativeToolError = terminalResolution.lastToolError;
169
+ return;
170
+ }
171
+ if (isFailure) this.lastNativeToolError = {
172
+ toolName: params.name,
173
+ ...params.meta ? { meta: params.meta } : {},
174
+ ...failure,
175
+ ...mutatingAction ? { mutatingAction: true } : {}
176
+ };
177
+ else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
178
+ }
179
+ emitToolResultSummary(item) {
180
+ if (!item || item.type === "dynamicToolCall") return;
181
+ if (!this.params.onToolResult || !this.shouldEmitToolResult()) return;
182
+ if (this.resultSummaryItemIds.has(item.id)) return;
183
+ const toolName = itemName(item);
184
+ const args = itemToolArgs(item);
185
+ if (!toolName || !shouldEmitTranscriptToolProgress(toolName)) return;
186
+ this.resultSummaryItemIds.add(item.id);
187
+ const meta = this.shouldIncludeFormattedMeta(isCommandBearingToolItem(item, args)) ? itemMeta(item, this.toolProgressDetailMode()) : void 0;
188
+ this.emitToolResultMessage({
189
+ itemId: item.id,
190
+ text: formatToolSummary(toolName, meta)
191
+ });
192
+ }
193
+ emitToolResultOutput(item) {
194
+ if (!item || item.type === "dynamicToolCall") return;
195
+ if (!this.params.onToolResult || !this.shouldEmitToolOutput()) return;
196
+ if (this.resultOutputItemIds.has(item.id) || this.resultOutputStreamedItemIds.has(item.id)) return;
197
+ const toolName = itemName(item);
198
+ const output = itemOutputText(item, this.output.textByItem);
199
+ if (!toolName || !output || !shouldEmitTranscriptToolProgress(toolName)) return;
200
+ const meta = this.shouldIncludeFormattedMeta(isCommandBearingToolItem(item, itemToolArgs(item))) ? itemMeta(item, this.toolProgressDetailMode()) : void 0;
201
+ this.emitToolResultMessage({
202
+ itemId: item.id,
203
+ text: formatToolOutput(toolName, meta, output),
204
+ finalOutput: true,
205
+ isError: isNonSuccessItemStatus(itemStatus(item))
206
+ });
207
+ }
208
+ recordToolMeta(item) {
209
+ if (!item) return;
210
+ if (isSideEffectingNativeToolItem(item)) this.sideEffectingNativeIds.add(item.id);
211
+ else this.sideEffectingNativeIds.delete(item.id);
212
+ const toolName = itemName(item);
213
+ if (!toolName) return;
214
+ const meta = itemMeta(item, this.toolProgressDetailMode());
215
+ const existing = this.metas.get(item.id);
216
+ const terminalStatus = auditNativeToolTerminalStatus(item);
217
+ const isError = typeof existing?.isError === "boolean" ? existing.isError : terminalStatus === "completed" ? false : terminalStatus === "failed" || terminalStatus === "blocked" ? true : void 0;
218
+ this.metas.set(item.id, {
219
+ toolName,
220
+ ...meta ? { meta } : {},
221
+ ...existing?.asyncStarted ? { asyncStarted: true } : {},
222
+ ...isError === void 0 ? {} : { isError }
223
+ });
224
+ }
225
+ recordTranscriptCall(params) {
226
+ if (!shouldEmitTranscriptToolProgress(params.name)) this.transcriptProgressSuppressedIds.add(params.id);
227
+ else this.transcriptProgressSuppressedIds.delete(params.id);
228
+ this.emitTranscriptToolCallProgress(params);
229
+ }
230
+ recordTranscriptResult(params) {
231
+ this.emitTranscriptToolResultProgress(params);
232
+ }
233
+ matchesEcho(text) {
234
+ for (const state of this.echoesByItem.values()) {
235
+ if (state.streamedDisplayText === text || state.displayTexts.includes(text)) return true;
236
+ if (state.streamedRawSignature && text.length === state.streamedRawSignature.length && text.startsWith(state.streamedRawSignature.prefix)) return true;
237
+ for (const signature of state.rawSignatures) if (text.length === signature.length && text.startsWith(signature.prefix)) return true;
238
+ }
239
+ return false;
240
+ }
241
+ rememberCommandAggregateOutputEcho(item) {
242
+ if (item?.type !== "commandExecution" || typeof item.aggregatedOutput !== "string") return;
243
+ const signature = toolOutputRawEchoSignature(item.aggregatedOutput);
244
+ if (signature) this.rememberEcho(item.id, signature);
245
+ }
246
+ toolProgressDetailMode() {
247
+ return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
248
+ }
249
+ emitToolResultMessage(params) {
250
+ const rawText = params.text.trim();
251
+ const text = truncateToolTranscriptText(rawText);
252
+ if (!text) return;
253
+ this.rememberEcho(params.itemId, {
254
+ displayText: text,
255
+ rawText
256
+ });
257
+ if (params.finalOutput) this.resultOutputItemIds.add(params.itemId);
258
+ try {
259
+ Promise.resolve(this.params.onToolResult?.({
260
+ text,
261
+ ...(this.params.messageChannel || this.params.messageProvider) && { channelData: { openclawToolProgressId: `tool:${params.itemId}` } },
262
+ ...params.isError === true ? { isError: true } : {}
263
+ })).catch(() => {});
264
+ } catch {}
265
+ }
266
+ shouldEmitToolResult() {
267
+ return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
268
+ }
269
+ shouldEmitToolOutput() {
270
+ return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
271
+ }
272
+ shouldIncludeFormattedMeta(commandBearing) {
273
+ const channel = this.params.messageChannel ?? this.params.messageProvider;
274
+ return !commandBearing || !channel && this.shouldEmitToolOutput();
275
+ }
276
+ emitTranscriptToolCallProgress(params) {
277
+ if (params.name === "progress_card" || !shouldEmitTranscriptToolProgress(params.name)) return;
278
+ this.transcriptProgressCallIds.add(params.id);
279
+ const args = normalizeToolTranscriptArguments(params.arguments);
280
+ const meta = this.shouldIncludeFormattedMeta(isCodexCommandBearingToolCall(params.name, args)) ? inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() }) : void 0;
281
+ if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.resultSummaryItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
282
+ this.resultSummaryItemIds.add(params.id);
283
+ this.emitToolResultMessage({
284
+ itemId: params.id,
285
+ text: formatToolSummary(params.name, meta)
286
+ });
287
+ }
288
+ emitTranscriptToolResultProgress(params) {
289
+ if (params.name === "progress_card" && !params.isError || this.transcriptProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name)) return;
290
+ if (params.name === "progress_card" && this.shouldEmitToolResult()) this.emitToolResultMessage({
291
+ itemId: params.id,
292
+ text: formatToolSummary(params.name),
293
+ isError: true
294
+ });
295
+ if (!this.transcriptProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
296
+ id: params.id,
297
+ name: params.name,
298
+ arguments: {}
299
+ });
300
+ if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.resultOutputItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
301
+ const text = params.text?.trim();
302
+ if (text) this.emitToolResultMessage({
303
+ itemId: params.id,
304
+ text: formatToolOutput(params.name, void 0, text),
305
+ finalOutput: true,
306
+ isError: params.isError
307
+ });
308
+ }
309
+ rememberEcho(itemId, signature) {
310
+ if (!itemId) return;
311
+ const existing = this.echoesByItem.get(itemId) ?? {
312
+ displayTexts: [],
313
+ rawSignatures: []
314
+ };
315
+ const displayText = signature.displayText?.trim();
316
+ if (displayText) {
317
+ if (signature.streamedDisplay) existing.streamedDisplayText = displayText;
318
+ else if (!existing.displayTexts.includes(displayText)) {
319
+ if (existing.displayTexts.length >= 24) existing.displayTexts.shift();
320
+ existing.displayTexts.push(displayText);
321
+ }
322
+ }
323
+ const rawText = signature.rawText?.trim();
324
+ const rawLength = signature.rawLength ?? rawText?.length;
325
+ const rawPrefix = signature.rawPrefix?.trim() ?? rawText;
326
+ if (rawLength !== void 0 && rawPrefix && rawPrefix.length >= 1024) {
327
+ const next = {
328
+ length: rawLength,
329
+ prefix: rawPrefix.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
330
+ };
331
+ if (signature.streamedDisplay) existing.streamedRawSignature = next;
332
+ else {
333
+ const matchIndex = existing.rawSignatures.findIndex((entry) => entry.prefix === next.prefix);
334
+ if (matchIndex >= 0) existing.rawSignatures[matchIndex] = next;
335
+ else {
336
+ if (existing.rawSignatures.length >= 24) existing.rawSignatures.shift();
337
+ existing.rawSignatures.push(next);
338
+ }
339
+ }
340
+ }
341
+ this.echoesByItem.set(itemId, existing);
342
+ }
343
+ };
344
+ //#endregion
345
+ //#region extensions/codex/src/app-server/event-projector-tool-transcript.ts
346
+ const ZERO_USAGE = {
347
+ input: 0,
348
+ output: 0,
349
+ cacheRead: 0,
350
+ cacheWrite: 0,
351
+ totalTokens: 0,
352
+ cost: {
353
+ input: 0,
354
+ output: 0,
355
+ cacheRead: 0,
356
+ cacheWrite: 0,
357
+ total: 0
358
+ }
359
+ };
360
+ const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
361
+ const NATIVE_PATCH_REJECTION_RE = /^\s*patch rejected:\s*writing outside of the project;\s*rejected by user approval settings\s*$/iu;
362
+ const CODE_MODE_RESULT_RE = /^\s*Script (completed|failed)\s*\r?\nWall time\s+\d+(?:\.\d+)?\s+seconds\s*\r?\nOutput:\s*([\s\S]*?)\s*$/iu;
363
+ const MAX_TOOL_APPROVAL_REVIEWS = 16;
364
+ function toolApprovalReviewOutcome(state) {
365
+ return state.denied ? "denied" : state.unresolvedReviewIds === null || state.unresolvedReviewIds.size > 0 ? "reviewing" : "approved";
366
+ }
367
+ var CodexToolTranscriptProjection = class {
368
+ constructor(params, threadId, turnId, progress, nextTranscriptTimestamp, options = {}) {
369
+ this.params = params;
370
+ this.threadId = threadId;
371
+ this.turnId = turnId;
372
+ this.progress = progress;
373
+ this.nextTranscriptTimestamp = nextTranscriptTimestamp;
374
+ this.options = options;
375
+ this.messages = [];
376
+ this.callIds = /* @__PURE__ */ new Set();
377
+ this.resultIds = /* @__PURE__ */ new Set();
378
+ this.namesById = /* @__PURE__ */ new Map();
379
+ this.trajectoryCallIds = /* @__PURE__ */ new Set();
380
+ this.trajectoryResultIds = /* @__PURE__ */ new Set();
381
+ this.trajectoryNamesById = /* @__PURE__ */ new Map();
382
+ this.trajectoryItemsById = /* @__PURE__ */ new Map();
383
+ this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
384
+ this.nativeMcpAppResultDetails = /* @__PURE__ */ new Map();
385
+ this.nativeMcpAppResultDetailsAttempted = /* @__PURE__ */ new Set();
386
+ this.approvalReviewsByCallId = /* @__PURE__ */ new Map();
387
+ this.rawNativeToolOutputByCallId = /* @__PURE__ */ new Map();
388
+ this.pendingRawOutputIds = /* @__PURE__ */ new Set();
389
+ this.rawCallsById = /* @__PURE__ */ new Map();
390
+ this.codeModeNativePatchInputsByCallId = /* @__PURE__ */ new Map();
391
+ }
392
+ get transcriptMessages() {
393
+ return this.messages;
394
+ }
395
+ recordToolApprovalReview(toolCallId, reviewId, status, review) {
396
+ const state = this.approvalReviewsByCallId.get(toolCallId) ?? {
397
+ reviews: [],
398
+ denied: false,
399
+ unresolvedReviewIds: /* @__PURE__ */ new Set()
400
+ };
401
+ state.reviews = [...state.reviews.filter((candidate) => candidate.id !== reviewId), review].slice(-16);
402
+ state.denied ||= [
403
+ "denied",
404
+ "timed_out",
405
+ "aborted"
406
+ ].includes(status);
407
+ const unresolved = state.unresolvedReviewIds;
408
+ if (status === "in_progress") state.unresolvedReviewIds = unresolved && (unresolved.size < MAX_TOOL_APPROVAL_REVIEWS || unresolved.has(reviewId)) ? unresolved.add(reviewId) : null;
409
+ else unresolved?.delete(reviewId);
410
+ this.approvalReviewsByCallId.set(toolCallId, state);
411
+ return toolApprovalReviewOutcome(state);
412
+ }
413
+ finalizeToolApprovalReviews(toolCallId) {
414
+ const state = this.approvalReviewsByCallId.get(toolCallId);
415
+ if (!state) return;
416
+ state.unresolvedReviewIds = /* @__PURE__ */ new Set();
417
+ return toolApprovalReviewOutcome(state);
418
+ }
419
+ recordDynamicToolCall(params) {
420
+ this.recordToolCall({
421
+ id: params.callId,
422
+ name: params.tool,
423
+ arguments: sanitizeCodexToolArguments(params.arguments)
424
+ });
425
+ }
426
+ recordDynamicToolResult(params, resultContentSource) {
427
+ this.recordToolResult({
428
+ id: params.callId,
429
+ name: params.tool,
430
+ text: collectDynamicToolContentText(params.contentItems),
431
+ isError: !params.success,
432
+ details: params.details,
433
+ ...resultContentSource ? { resultContentSource } : {}
434
+ });
435
+ }
436
+ recordNativeToolCall(item) {
437
+ if (!item || !shouldRecordNativeToolTranscript(item)) return;
438
+ const name = itemName(item);
439
+ if (name) this.recordToolCall({
440
+ id: item.id,
441
+ name,
442
+ arguments: itemToolArgs(item)
443
+ });
444
+ }
445
+ recordNativeToolResult(item, details) {
446
+ if (!item || !shouldRecordNativeToolTranscript(item) || this.resultIds.has(item.id)) return;
447
+ const name = itemName(item);
448
+ if (name) {
449
+ const status = itemStatus(item);
450
+ const approvalTimeoutExplanation = this.progress.approvalTimeoutExplanation(item.id, status);
451
+ this.recordToolResult({
452
+ id: item.id,
453
+ name,
454
+ text: approvalTimeoutExplanation ?? this.rawNativeToolOutputByCallId.get(item.id) ?? itemTranscriptResultText(item, this.progress.outputTextByItem),
455
+ isError: isNonSuccessItemStatus(status),
456
+ ...item.type === "commandExecution" && item.aggregatedOutput == null && this.progress.isOutputTruncated(item.id) ? { captureTruncated: true } : {},
457
+ details,
458
+ ...item.type === "webSearch" ? { resultContentSource: "network" } : {}
459
+ });
460
+ this.progress.approvalTimeoutKinds.delete(item.id);
461
+ }
462
+ }
463
+ recordRawNativeToolItem(item) {
464
+ const type = typeof item.type === "string" ? item.type : void 0;
465
+ const callId = typeof item.call_id === "string" ? item.call_id : typeof item.callId === "string" ? item.callId : void 0;
466
+ if (!callId) return;
467
+ if ((type === "custom_tool_call" || type === "function_call") && typeof item.name === "string") {
468
+ this.rawCallsById.set(callId, {
469
+ id: callId,
470
+ name: item.name,
471
+ arguments: type === "custom_tool_call" ? { input: item.input } : { arguments: item.arguments }
472
+ });
473
+ this.pendingRawOutputIds.add(callId);
474
+ }
475
+ if ((type === "custom_tool_call" || type === "function_call") && (item.name === "apply_patch" || item.name === "exec_command" || item.name === "exec")) {
476
+ let args;
477
+ if (type === "custom_tool_call" && item.name === "apply_patch" && typeof item.input === "string") args = { input: item.input };
478
+ else if (type === "custom_tool_call" && item.name === "exec") {
479
+ const input = readCodeModeNativePatchInput(item.input);
480
+ if (input) this.codeModeNativePatchInputsByCallId.set(callId, input);
481
+ return;
482
+ } else if (type === "function_call" && typeof item.arguments === "string") try {
483
+ const parsed = JSON.parse(item.arguments);
484
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
485
+ const parsedArguments = parsed;
486
+ if (item.name === "apply_patch") args = parsedArguments;
487
+ else {
488
+ const command = typeof parsedArguments.cmd === "string" ? parsedArguments.cmd : typeof parsedArguments.command === "string" ? parsedArguments.command : void 0;
489
+ const patch = readInterceptedNativePatchInput(command);
490
+ if (patch) {
491
+ const workdir = typeof parsedArguments.workdir === "string" ? parsedArguments.workdir : typeof parsedArguments.cwd === "string" ? parsedArguments.cwd : void 0;
492
+ const cwd = patch.cwd ? workdir && !path.isAbsolute(patch.cwd) ? path.join(workdir, patch.cwd) : patch.cwd : workdir;
493
+ args = {
494
+ input: patch.input,
495
+ ...cwd ? { cwd } : {}
496
+ };
497
+ }
498
+ }
499
+ }
500
+ } catch {
501
+ return;
502
+ }
503
+ if (args) {
504
+ this.pendingRawOutputIds.add(callId);
505
+ this.recordToolCall({
506
+ id: callId,
507
+ name: "apply_patch",
508
+ arguments: args
509
+ });
510
+ }
511
+ return;
512
+ }
513
+ if (type !== "custom_tool_call_output" && type !== "function_call_output") return;
514
+ this.pendingRawOutputIds.delete(callId);
515
+ const text = readCodexResponseOutput(item);
516
+ if (text === void 0) return;
517
+ this.rawNativeToolOutputByCallId.set(callId, text);
518
+ const rawCall = this.rawCallsById.get(callId);
519
+ const responseText = typeof item.output === "string" ? item.output : collectDynamicToolContentText(item.output);
520
+ const execution = rawCall?.name === "exec" ? CODE_MODE_RESULT_RE.exec(responseText) : null;
521
+ const codeModePatchInput = this.codeModeNativePatchInputsByCallId.get(callId);
522
+ if (codeModePatchInput) {
523
+ this.codeModeNativePatchInputsByCallId.delete(callId);
524
+ if (execution?.[1]?.toLowerCase() === "failed") {
525
+ const failure = execution[2]?.replace(/^Script error:\s*/iu, "").trim() || text;
526
+ this.recordToolCall({
527
+ id: callId,
528
+ name: "apply_patch",
529
+ arguments: { input: codeModePatchInput }
530
+ });
531
+ this.recordToolResult({
532
+ id: callId,
533
+ name: "apply_patch",
534
+ text: failure,
535
+ isError: true
536
+ });
537
+ return;
538
+ }
539
+ }
540
+ const result = this.messages.find((message) => message.role === "toolResult" && message.toolCallId === callId);
541
+ if (!result) {
542
+ if (!this.callIds.has(callId) && rawCall) {
543
+ this.recordToolCall(rawCall);
544
+ this.recordToolResult({
545
+ id: callId,
546
+ name: rawCall.name,
547
+ text,
548
+ isError: execution?.[1]?.toLowerCase() === "failed",
549
+ ...!execution ? { outcomeUnknown: true } : {}
550
+ });
551
+ } else if (this.namesById.get(callId) === "apply_patch" && NATIVE_PATCH_REJECTION_RE.test(text)) this.recordToolResult({
552
+ id: callId,
553
+ name: "apply_patch",
554
+ text,
555
+ isError: true
556
+ });
557
+ return;
558
+ }
559
+ result.content = this.createToolResultMessage({
560
+ id: callId,
561
+ name: result.toolName,
562
+ text,
563
+ isError: result.isError
564
+ }).content;
565
+ const metadata = Reflect.get(result, "__openclaw");
566
+ Reflect.set(result, "__openclaw", {
567
+ ...isJsonObject(metadata) ? metadata : {},
568
+ toolOutput: {
569
+ source: "provider-response",
570
+ modelInput: "unverified"
571
+ }
572
+ });
573
+ }
574
+ async prepareNativeToolResultDetails(item) {
575
+ const preparedDetails = await this.prepareNativeMcpAppResultDetails(item);
576
+ const approvalReviewState = item ? this.approvalReviewsByCallId.get(item.id) : void 0;
577
+ const reviewDetails = approvalReviewState ? {
578
+ approvalReviews: approvalReviewState.reviews,
579
+ approvalReviewOutcome: toolApprovalReviewOutcome(approvalReviewState)
580
+ } : void 0;
581
+ return reviewDetails ? isJsonObject(preparedDetails) ? {
582
+ ...preparedDetails,
583
+ ...reviewDetails
584
+ } : {
585
+ ...preparedDetails !== void 0 ? { toolDetails: preparedDetails } : {},
586
+ ...reviewDetails
587
+ } : preparedDetails;
588
+ }
589
+ async prepareNativeMcpAppResultDetails(item) {
590
+ if (!item || item.type !== "mcpToolCall" || itemStatus(item) === "running") return;
591
+ if (this.nativeMcpAppResultDetails.has(item.id)) return this.nativeMcpAppResultDetails.get(item.id);
592
+ if (this.nativeMcpAppResultDetailsAttempted.has(item.id) || !this.options.prepareNativeMcpAppResultDetails) return;
593
+ this.nativeMcpAppResultDetailsAttempted.add(item.id);
594
+ try {
595
+ const details = await this.options.prepareNativeMcpAppResultDetails(item);
596
+ if (details !== void 0) this.nativeMcpAppResultDetails.set(item.id, details);
597
+ return details;
598
+ } catch (error) {
599
+ embeddedAgentLog.debug("codex native MCP App preview preparation failed", {
600
+ itemId: item.id,
601
+ error
602
+ });
603
+ return;
604
+ }
605
+ }
606
+ recordTrajectoryEvent(params) {
607
+ if (params.phase === "start") {
608
+ this.trajectoryCallIds.add(params.item.id);
609
+ this.trajectoryNamesById.set(params.item.id, params.name);
610
+ this.trajectoryItemsById.set(params.item.id, params.item);
611
+ this.options.trajectoryRecorder?.recordEvent("tool.call", {
612
+ threadId: this.threadId,
613
+ turnId: this.turnId,
614
+ itemId: params.item.id,
615
+ toolCallId: params.item.id,
616
+ name: params.name,
617
+ arguments: params.args
618
+ });
619
+ return;
620
+ }
621
+ this.trajectoryResultIds.add(params.item.id);
622
+ const toolResult = itemToolResult(params.item).result;
623
+ const output = this.progress.approvalTimeoutExplanation(params.item.id, params.status) ?? itemOutputText(params.item, this.progress.outputTextByItem);
624
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
625
+ threadId: this.threadId,
626
+ turnId: this.turnId,
627
+ itemId: params.item.id,
628
+ toolCallId: params.item.id,
629
+ name: params.name,
630
+ status: params.status,
631
+ isError: isNonSuccessItemStatus(params.status),
632
+ ...toolResult ? { result: toolResult } : {},
633
+ ...output ? { output } : {}
634
+ });
635
+ }
636
+ emitAfterToolCallObservation(item) {
637
+ if (!this.shouldEmitAfterToolCallObservation(item)) return;
638
+ const name = itemName(item);
639
+ const status = itemStatus(item);
640
+ if (!name || status === "running") return;
641
+ this.afterToolCallObservedItemIds.add(item.id);
642
+ const result = itemToolResult(item).result;
643
+ const error = this.progress.approvalTimeoutExplanation(item.id, status) ?? itemToolError(item, status, this.progress.outputTextByItem);
644
+ const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
645
+ const hookParams = {
646
+ toolName: name,
647
+ toolCallId: item.id,
648
+ runId: this.params.runId,
649
+ agentId: this.params.agentId,
650
+ sessionId: this.params.sessionId,
651
+ sessionKey: this.params.sessionKey,
652
+ startArgs: itemToolArgs(item) ?? {},
653
+ ...result !== void 0 ? { result } : {},
654
+ ...error ? { error } : {},
655
+ ...startedAt !== void 0 ? { startedAt } : {}
656
+ };
657
+ setImmediate(() => {
658
+ runAgentHarnessAfterToolCallHook(hookParams);
659
+ });
660
+ }
661
+ synthesizeMissingToolResults(params) {
662
+ if (!params.synthesize) return;
663
+ const missingTranscriptIds = [...this.callIds].filter((id) => !this.resultIds.has(id));
664
+ const missingTrajectoryIds = [...this.trajectoryCallIds].filter((id) => !this.trajectoryResultIds.has(id));
665
+ if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
666
+ for (const id of missingTranscriptIds) {
667
+ const name = this.namesById.get(id) ?? this.trajectoryNamesById.get(id);
668
+ if (name) this.recordToolResult({
669
+ id,
670
+ name,
671
+ text: formatMissingToolResultError({
672
+ id,
673
+ name
674
+ }),
675
+ isError: true,
676
+ details: { reason: "missing_tool_result" }
677
+ });
678
+ }
679
+ for (const id of missingTrajectoryIds) {
680
+ const name = this.trajectoryNamesById.get(id) ?? this.namesById.get(id);
681
+ if (!name) continue;
682
+ this.trajectoryResultIds.add(id);
683
+ const text = formatMissingToolResultError({
684
+ id,
685
+ name
686
+ });
687
+ this.options.trajectoryRecorder?.recordEvent("tool.result", {
688
+ threadId: this.threadId,
689
+ turnId: this.turnId,
690
+ itemId: id,
691
+ toolCallId: id,
692
+ name,
693
+ status: "failed",
694
+ isError: true,
695
+ result: {
696
+ status: "failed",
697
+ reason: "missing_tool_result"
698
+ },
699
+ output: text
700
+ });
701
+ }
702
+ if (params.terminalDisposition === "tool_error") {
703
+ this.recordMissingToolError(missingTranscriptIds, missingTrajectoryIds);
704
+ return;
705
+ }
706
+ if (params.terminalDisposition === "diagnostic_only") return;
707
+ const missingCount = (/* @__PURE__ */ new Set([...missingTranscriptIds, ...missingTrajectoryIds])).size;
708
+ return missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
709
+ }
710
+ async readMirroredSessionMessages(signal) {
711
+ return await readCodexMirroredSessionHistoryMessages({
712
+ agentId: this.params.agentId,
713
+ sessionFile: this.params.sessionFile,
714
+ sessionId: this.params.sessionId,
715
+ sessionKey: this.params.sessionKey,
716
+ sessionTarget: this.params.sessionTarget
717
+ }, void 0, signal, this.params.contextTokenBudget) ?? [];
718
+ }
719
+ recordToolCall(params) {
720
+ if (!params.id || !params.name || this.callIds.has(params.id)) return;
721
+ this.callIds.add(params.id);
722
+ this.namesById.set(params.id, params.name);
723
+ this.progress.recordTranscriptCall(params);
724
+ const message = attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`);
725
+ this.messages.push(message);
726
+ this.options.checkpointMessage?.({ read: () => message });
727
+ }
728
+ recordToolResult(params) {
729
+ if (!params.id || !params.name || this.resultIds.has(params.id)) return;
730
+ this.resultIds.add(params.id);
731
+ this.progress.recordTranscriptResult(params);
732
+ const message = attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`);
733
+ this.messages.push(message);
734
+ this.options.checkpointMessage?.({
735
+ read: () => message,
736
+ ready: () => !this.pendingRawOutputIds.has(params.id)
737
+ });
738
+ }
739
+ recordMissingToolError(missingTranscriptIds, missingTrajectoryIds) {
740
+ const firstMissingId = missingTranscriptIds.find((id) => Boolean(this.namesById.get(id))) ?? missingTrajectoryIds.find((id) => Boolean(this.trajectoryNamesById.get(id) ?? this.namesById.get(id)));
741
+ if (!firstMissingId) return;
742
+ const name = this.namesById.get(firstMissingId) ?? this.trajectoryNamesById.get(firstMissingId);
743
+ if (!name) return;
744
+ const item = this.trajectoryItemsById.get(firstMissingId);
745
+ const meta = item ? itemMeta(item, this.progress.toolProgressDetailMode()) : this.progress.getToolMeta(firstMissingId)?.meta;
746
+ this.progress.setLastToolError({
747
+ toolName: name,
748
+ ...meta ? { meta } : {},
749
+ error: formatMissingToolResultError({
750
+ id: firstMissingId,
751
+ name
752
+ }),
753
+ ...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {}
754
+ });
755
+ }
756
+ shouldEmitAfterToolCallObservation(item) {
757
+ if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
758
+ return !(this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item));
759
+ }
760
+ createToolCallMessage(params) {
761
+ const args = normalizeToolTranscriptArguments(params.arguments);
762
+ const attribution = resolveCodexLocalRuntimeAttribution(this.params);
763
+ return {
764
+ role: "assistant",
765
+ content: [{
766
+ type: "toolCall",
767
+ id: params.id,
768
+ name: params.name,
769
+ arguments: args
770
+ }],
771
+ api: attribution.api ?? "openai-chatgpt-responses",
772
+ provider: attribution.provider,
773
+ model: this.params.modelId,
774
+ usage: ZERO_USAGE,
775
+ stopReason: "toolUse",
776
+ timestamp: this.nextTranscriptTimestamp()
777
+ };
778
+ }
779
+ createToolResultMessage(params) {
780
+ const response = this.rawNativeToolOutputByCallId.get(params.id);
781
+ const text = response ?? params.text ?? toolResultStatusText(params);
782
+ return {
783
+ role: "toolResult",
784
+ toolCallId: params.id,
785
+ toolName: params.name,
786
+ isError: params.isError,
787
+ content: [{
788
+ type: "text",
789
+ text
790
+ }],
791
+ ...params.details !== void 0 ? { details: params.details } : {},
792
+ timestamp: this.nextTranscriptTimestamp(),
793
+ __openclaw: {
794
+ ...params.resultContentSource ? { resultContentSource: params.resultContentSource } : {},
795
+ toolOutput: {
796
+ source: response === void 0 ? "execution" : "provider-response",
797
+ modelInput: "unverified",
798
+ ...params.outcomeUnknown ? { outcome: "unknown" } : {},
799
+ ...response === void 0 && params.captureTruncated ? { captureTruncated: true } : {}
800
+ }
801
+ }
802
+ };
803
+ }
804
+ };
805
+ function formatMissingToolResultError(params) {
806
+ return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
807
+ }
808
+ function toolResultStatusText(params) {
809
+ return params.isError ? `${params.name} failed` : `${params.name} completed`;
810
+ }
811
+ function resolveStartedAtFromDurationMs(durationMs) {
812
+ if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
813
+ return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
814
+ }
815
+ //#endregion
816
+ //#region extensions/codex/src/app-server/event-projector-events.ts
817
+ /** Downstream event consumers must never corrupt the canonical Codex turn projection. */
818
+ function emitCodexAgentEvent(params, event) {
819
+ try {
820
+ emitAgentEvent({
821
+ runId: params.runId,
822
+ stream: event.stream,
823
+ data: event.data,
824
+ ...params.sessionKey ? { sessionKey: params.sessionKey } : {}
825
+ });
826
+ } catch (error) {
827
+ embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
828
+ }
829
+ try {
830
+ const maybePromise = params.onAgentEvent?.(event);
831
+ Promise.resolve(maybePromise).catch((error) => {
832
+ embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
833
+ });
834
+ } catch (error) {
835
+ embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
836
+ }
837
+ }
838
+ function guardianActionCommand(action) {
839
+ if (!action) return;
840
+ const directLabel = readStringField(action, "command") ?? readStringField(action, "target") ?? readStringField(action, "toolTitle") ?? readStringField(action, "reason");
841
+ if (directLabel) return directLabel;
842
+ const server = readStringField(action, "connectorName") ?? readStringField(action, "server");
843
+ const tool = readStringField(action, "toolName");
844
+ if (server && tool) return `${server}/${tool}`;
845
+ const argv = Array.isArray(action.argv) ? action.argv.filter((value) => typeof value === "string") : [];
846
+ return argv.length > 0 ? argv.join(" ") : readStringField(action, "program");
847
+ }
848
+ function normalizeApprovalReviewStatus(status) {
849
+ return status === "inProgress" ? "in_progress" : status === "timedOut" ? "timed_out" : status;
850
+ }
851
+ const GUARDIAN_TIMEOUT_WARNING = "Automatic approval review timed out while evaluating the requested approval.";
852
+ const LOG_ONLY_CODEX_WARNING_PATTERNS = [/^Configured service tier `[^`\r\n]+` is not advertised as supported for model `[^`\r\n]+` and will be omitted from requests\.$/, /^Code Mode is enabled in configuration, but model `[^`\r\n]+` does not advertise Code Mode support\. This may degrade model performance\. Disable `features\.code_mode` and `features\.code_mode_only`, or select a model whose metadata enables Code Mode\.$/];
853
+ function projectNormalizedToolItem(params) {
854
+ const { item } = params;
855
+ if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
856
+ const name = itemName(item);
857
+ if (!name) return;
858
+ const status = params.phase === "result" ? itemStatus(item) : "running";
859
+ const args = itemToolArgs(item);
860
+ const commandBearing = isCommandBearingToolItem(item, args);
861
+ const meta = itemMeta(item, params.detailMode);
862
+ return {
863
+ name,
864
+ status,
865
+ args,
866
+ meta,
867
+ event: shouldEmitTranscriptToolProgress(name) ? {
868
+ stream: "tool",
869
+ data: {
870
+ phase: params.phase,
871
+ name,
872
+ itemId: item.id,
873
+ toolCallId: item.id,
874
+ ...meta ? { meta } : {},
875
+ ...commandBearing ? { commandBearing: true } : {},
876
+ ...params.phase === "start" && args ? { args } : {},
877
+ ...params.phase === "result" ? {
878
+ status,
879
+ isError: isNonSuccessItemStatus(status),
880
+ ...itemToolResult(item)
881
+ } : {}
882
+ }
883
+ } : void 0
884
+ };
885
+ }
886
+ var CodexEventProjection = class {
887
+ constructor(provider, threadId, turnId, emitAgentEvent, toolProgress, toolTranscript, onNativeToolResultRecorded) {
888
+ this.provider = provider;
889
+ this.threadId = threadId;
890
+ this.turnId = turnId;
891
+ this.emitAgentEvent = emitAgentEvent;
892
+ this.toolProgress = toolProgress;
893
+ this.toolTranscript = toolTranscript;
894
+ this.onNativeToolResultRecorded = onNativeToolResultRecorded;
895
+ this.reviewCount = 0;
896
+ this.safetyBufferingEnded = false;
897
+ }
898
+ get guardianReviewCount() {
899
+ return this.reviewCount;
900
+ }
901
+ emitCompactionEnd(itemId, completed) {
902
+ this.emitAgentEvent({
903
+ stream: "compaction",
904
+ data: {
905
+ phase: "end",
906
+ backend: "codex-app-server",
907
+ completed,
908
+ threadId: this.threadId,
909
+ turnId: this.turnId,
910
+ itemId
911
+ }
912
+ });
913
+ }
914
+ handleGuardianReview(method, params) {
915
+ this.reviewCount += 1;
916
+ const review = isJsonObject(params.review) ? params.review : void 0;
917
+ const action = isJsonObject(params.action) ? params.action : void 0;
918
+ const reviewId = readStringField(params, "reviewId");
919
+ const targetItemId = readNullableString$1(params, "targetItemId");
920
+ const command = guardianActionCommand(action);
921
+ const reviewStatus = review ? readStringField(review, "status") : void 0;
922
+ const status = normalizeApprovalReviewStatus(reviewStatus);
923
+ const riskLevel = review ? readStringField(review, "riskLevel") : void 0;
924
+ const userAuthorization = review ? readStringField(review, "userAuthorization") : void 0;
925
+ const rationale = review ? readNullableString$1(review, "rationale") : void 0;
926
+ const expectedWarning = status === "timed_out" ? GUARDIAN_TIMEOUT_WARNING : rationale && riskLevel && userAuthorization && (status === "approved" || status === "denied") ? `Automatic approval review ${status} (risk: ${riskLevel}, authorization: ${userAuthorization}): ${rationale}` : void 0;
927
+ if (Boolean(targetItemId) && Boolean(reviewId) && this.pendingGuardianWarning === expectedWarning) this.pendingGuardianWarning = void 0;
928
+ else this.flushPendingGuardianWarning();
929
+ const threadId = readStringField(params, "threadId") ?? this.threadId;
930
+ const turnId = readStringField(params, "turnId") ?? this.turnId;
931
+ if (method.endsWith("/started")) this.activeGuardianReview = {
932
+ reviewId,
933
+ targetItemId,
934
+ command,
935
+ threadId,
936
+ turnId
937
+ };
938
+ this.emitAgentEvent({
939
+ stream: "codex_app_server.guardian",
940
+ data: {
941
+ method,
942
+ phase: method.endsWith("/started") ? "started" : "completed",
943
+ threadId,
944
+ turnId,
945
+ reviewId,
946
+ targetItemId,
947
+ decisionSource: readStringField(params, "decisionSource"),
948
+ status: reviewStatus,
949
+ riskLevel,
950
+ userAuthorization,
951
+ rationale,
952
+ actionType: action ? readStringField(action, "type") : void 0,
953
+ command
954
+ }
955
+ });
956
+ if (reviewId && targetItemId && status) {
957
+ const approvalReview = {
958
+ id: reviewId,
959
+ label: "Guardian",
960
+ status,
961
+ ...riskLevel ? { riskLevel } : {},
962
+ ...userAuthorization ? { userAuthorization } : {},
963
+ ...rationale ? { rationale } : {}
964
+ };
965
+ const approvalReviewOutcome = this.toolTranscript.recordToolApprovalReview(targetItemId, reviewId, status, approvalReview);
966
+ this.emitAgentEvent({
967
+ stream: "tool",
968
+ data: {
969
+ phase: "review",
970
+ toolCallId: targetItemId,
971
+ hideFromChannelProgress: true,
972
+ approvalReviewOutcome,
973
+ review: approvalReview
974
+ }
975
+ });
976
+ }
977
+ if (method.endsWith("/completed") && this.activeGuardianReview?.reviewId === reviewId) this.activeGuardianReview = void 0;
978
+ }
979
+ handleGuardianWarning(params) {
980
+ this.flushPendingGuardianWarning();
981
+ const message = readStringField(params, "message");
982
+ if (message) {
983
+ this.pendingGuardianWarning = message;
984
+ return;
985
+ }
986
+ this.emitAgentEvent({
987
+ stream: "codex_app_server.guardian",
988
+ data: {
989
+ phase: "warning",
990
+ message
991
+ }
992
+ });
993
+ }
994
+ handleWarning(params) {
995
+ const message = [readStringField(params, "summary") ?? readStringField(params, "message"), readStringField(params, "details")].filter(Boolean).join("\n");
996
+ if (LOG_ONLY_CODEX_WARNING_PATTERNS.some((pattern) => pattern.test(message))) embeddedAgentLog.warn(message);
997
+ else if (message) this.emitAgentEvent({
998
+ stream: "notice",
999
+ data: {
1000
+ phase: "warning",
1001
+ message
1002
+ }
1003
+ });
1004
+ }
1005
+ handleModelRerouted(params) {
1006
+ const fromModel = readStringField(params, "fromModel");
1007
+ const toModel = readStringField(params, "toModel");
1008
+ const reason = readStringField(params, "reason");
1009
+ this.responseModel = toModel ?? this.responseModel;
1010
+ if (fromModel && toModel && fromModel !== toModel) {
1011
+ this.emitAgentEvent({
1012
+ stream: "lifecycle",
1013
+ data: {
1014
+ phase: "model",
1015
+ provider: this.provider,
1016
+ model: toModel
1017
+ }
1018
+ });
1019
+ this.emitAgentEvent({
1020
+ stream: "fallback",
1021
+ data: {
1022
+ fromModel,
1023
+ toModel,
1024
+ ...reason ? { reason } : {}
1025
+ }
1026
+ });
1027
+ if (reason === "highRiskCyberActivity") {
1028
+ this.cyberNoticeState = "fallback";
1029
+ this.emitCyberNotice("fallback", {
1030
+ model: fromModel,
1031
+ fallbackModel: toModel
1032
+ });
1033
+ }
1034
+ }
1035
+ }
1036
+ handleSafetyBuffering(params) {
1037
+ if (params.showBufferingUi === false) {
1038
+ this.clearSafetyBuffering();
1039
+ return;
1040
+ }
1041
+ if (this.safetyBufferingEnded || this.cyberNoticeState === "blocked" || this.cyberNoticeState === "fallback" || params.showBufferingUi !== true || !Array.isArray(params.useCases) || !params.useCases.includes("cyber")) return;
1042
+ this.cyberNoticeState = "buffering";
1043
+ const model = readStringField(params, "model");
1044
+ const fallbackModel = readStringField(params, "fasterModel");
1045
+ this.emitCyberNotice("buffering", {
1046
+ ...model ? { model } : {},
1047
+ ...fallbackModel ? { fallbackModel } : {}
1048
+ });
1049
+ }
1050
+ handleCyberPolicyError(codexErrorInfo, model) {
1051
+ if (codexErrorInfo !== "cyberPolicy" || this.cyberNoticeState === "blocked") return;
1052
+ this.cyberNoticeState = "blocked";
1053
+ this.emitCyberNotice("blocked", { model: this.responseModel ?? model });
1054
+ }
1055
+ endSafetyBuffering() {
1056
+ this.safetyBufferingEnded = true;
1057
+ this.clearSafetyBuffering();
1058
+ }
1059
+ markSafetyBufferingAssistantStarted() {
1060
+ if (this.cyberNoticeState === "buffering") this.endSafetyBuffering();
1061
+ }
1062
+ clearSafetyBuffering() {
1063
+ if (this.cyberNoticeState !== "buffering") return;
1064
+ this.cyberNoticeState = void 0;
1065
+ this.emitCyberNotice("cleared");
1066
+ }
1067
+ emitCyberNotice(state, models = {}) {
1068
+ if (this.provider !== "openai") return;
1069
+ this.emitAgentEvent({
1070
+ stream: "notice",
1071
+ data: {
1072
+ phase: "provider_policy",
1073
+ category: "cyber",
1074
+ state,
1075
+ provider: "openai",
1076
+ ...models
1077
+ }
1078
+ });
1079
+ }
1080
+ handleRetry(params) {
1081
+ const rateLimited = isJsonObject(params.error) && params.error.codexErrorInfo === "rateLimitExceeded";
1082
+ this.emitAgentEvent({
1083
+ stream: "run_status",
1084
+ data: {
1085
+ phase: "retrying",
1086
+ message: rateLimited ? "Rate limited. The provider is retrying." : "Connection interrupted. The provider is retrying."
1087
+ }
1088
+ });
1089
+ }
1090
+ flushPendingGuardianWarning() {
1091
+ const pending = this.pendingGuardianWarning;
1092
+ if (!pending) return;
1093
+ this.pendingGuardianWarning = void 0;
1094
+ this.emitAgentEvent({
1095
+ stream: "codex_app_server.guardian",
1096
+ data: {
1097
+ phase: "warning",
1098
+ message: pending
1099
+ }
1100
+ });
1101
+ }
1102
+ handleStrictReviewRequired(params) {
1103
+ this.emitAgentEvent({
1104
+ stream: "codex_app_server.guardian",
1105
+ data: {
1106
+ method: "autoApprovalReview/strictReviewRequired",
1107
+ phase: "strict_review_required",
1108
+ threadId: readStringField(params, "threadId") ?? this.activeGuardianReview?.threadId ?? this.threadId,
1109
+ turnId: readStringField(params, "turnId") ?? this.activeGuardianReview?.turnId ?? this.turnId,
1110
+ reviewId: this.activeGuardianReview?.reviewId,
1111
+ targetItemId: this.activeGuardianReview?.targetItemId,
1112
+ command: this.activeGuardianReview?.command,
1113
+ startedAtMs: asFiniteNumber(params.startedAtMs)
1114
+ }
1115
+ });
1116
+ }
1117
+ handleHook(method, params) {
1118
+ const run = isJsonObject(params.run) ? params.run : void 0;
1119
+ if (!run) return;
1120
+ const durationMs = asFiniteNumber(run.durationMs);
1121
+ const entries = readHookOutputEntries(run.entries);
1122
+ const hookTurnId = readNullableString$1(params, "turnId");
1123
+ this.emitAgentEvent({
1124
+ stream: "codex_app_server.hook",
1125
+ data: {
1126
+ phase: method === "hook/started" ? "started" : "completed",
1127
+ threadId: this.threadId,
1128
+ turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
1129
+ hookRunId: readStringField(run, "id"),
1130
+ eventName: readStringField(run, "eventName"),
1131
+ handlerType: readStringField(run, "handlerType"),
1132
+ executionMode: readStringField(run, "executionMode"),
1133
+ scope: readStringField(run, "scope"),
1134
+ source: readStringField(run, "source"),
1135
+ sourcePath: readStringField(run, "sourcePath"),
1136
+ status: readStringField(run, "status"),
1137
+ statusMessage: readNullableString$1(run, "statusMessage"),
1138
+ ...durationMs !== void 0 ? { durationMs } : {},
1139
+ ...entries.length > 0 ? { entries } : {}
1140
+ }
1141
+ });
1142
+ }
1143
+ emitStandardItemEvent(params) {
1144
+ const { item } = params;
1145
+ if (!item) return;
1146
+ const activity = item.type === "subAgentActivity";
1147
+ if (activity && params.phase === "start") return;
1148
+ const subagent = activity || item.type === "collabAgentToolCall";
1149
+ const kind = subagent ? "tool" : itemKind(item);
1150
+ if (!kind) return;
1151
+ const name = subagent ? "subagents" : itemName(item);
1152
+ const args = itemToolArgs(item);
1153
+ const commandBearing = isCommandBearingToolItem(item, args);
1154
+ const subagentStatus = readStringField(item, activity ? "kind" : "status");
1155
+ const interaction = activity && subagentStatus === "interacted";
1156
+ const status = subagent && subagentStatus === "interrupted" ? "failed" : activity ? subagentStatus === "completed" || interaction ? "completed" : "running" : params.phase === "start" ? "running" : kind === "analysis" ? "completed" : unknownItemStatus(item) ? void 0 : itemStatus(item);
1157
+ const meta = subagent ? [interaction ? "message sent" : activity ? subagentStatus : status, readStringField(item, activity ? "agentPath" : "tool")].filter(Boolean).join(": ") : itemMeta(item, this.toolProgress.toolProgressDetailMode());
1158
+ const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
1159
+ this.emitAgentEvent({
1160
+ stream: "item",
1161
+ data: projectAgentActivityItem({
1162
+ itemId: activity && !interaction ? `subagent:${readStringField(item, "agentThreadId") ?? item.id}` : item.id,
1163
+ phase: params.phase,
1164
+ kind,
1165
+ title: itemTitle(item),
1166
+ ...status ? { status } : {},
1167
+ ...status === void 0 ? {
1168
+ summary: "Outcome unknown",
1169
+ title: `${itemTitle(item)} — outcome unknown`
1170
+ } : {},
1171
+ toolCallId: item.id,
1172
+ ...name ? { name } : {},
1173
+ ...meta ? { meta } : {},
1174
+ ...commandBearing ? { commandBearing: true } : {},
1175
+ ...suppressChannelProgress ? { suppressChannelProgress: true } : {}
1176
+ }, {
1177
+ args: itemToolArgs(item),
1178
+ ...item.type === "collabAgentToolCall" && item.tool === "wait" ? { nativeOperation: "wait" } : {}
1179
+ })
1180
+ });
1181
+ }
1182
+ async emitSnapshotOnlyNativeToolProgress(params) {
1183
+ const { item, activeItemIds, completedItemIds, isActive } = params;
1184
+ if (!shouldSynthesizeToolProgressForItem(item) || !matchesCodexSnapshotTurn(item, this.turnId) || completedItemIds.has(item.id) || itemStatus(item) === "running") return;
1185
+ if (!activeItemIds.has(item.id)) {
1186
+ this.emitStandardItemEvent({
1187
+ phase: "start",
1188
+ item
1189
+ });
1190
+ await this.emitNormalizedToolItemEvent({
1191
+ phase: "start",
1192
+ item
1193
+ });
1194
+ }
1195
+ if (!isActive()) return;
1196
+ activeItemIds.delete(item.id);
1197
+ this.emitStandardItemEvent({
1198
+ phase: "end",
1199
+ item
1200
+ });
1201
+ await this.emitNormalizedToolItemEvent({
1202
+ phase: "result",
1203
+ item
1204
+ });
1205
+ completedItemIds.add(item.id);
1206
+ }
1207
+ async emitNormalizedToolItemEvent(params) {
1208
+ const projection = projectNormalizedToolItem({
1209
+ ...params,
1210
+ detailMode: this.toolProgress.toolProgressDetailMode()
1211
+ });
1212
+ if (!projection || !params.item) return;
1213
+ const { item } = params;
1214
+ const { name, status, args, meta, event } = projection;
1215
+ const approvalReviewOutcome = params.phase === "result" ? this.toolTranscript.finalizeToolApprovalReviews(item.id) : void 0;
1216
+ if (event && approvalReviewOutcome) event.data.approvalReviewOutcome = approvalReviewOutcome;
1217
+ this.toolTranscript.recordTrajectoryEvent({
1218
+ phase: params.phase,
1219
+ item,
1220
+ name,
1221
+ args,
1222
+ status
1223
+ });
1224
+ if (params.phase === "result") this.toolProgress.recordNativeToolError({
1225
+ item,
1226
+ name,
1227
+ meta,
1228
+ status
1229
+ });
1230
+ if (!event) {
1231
+ if (params.phase === "result") {
1232
+ this.toolTranscript.emitAfterToolCallObservation(item);
1233
+ await this.onNativeToolResultRecorded?.();
1234
+ }
1235
+ return;
1236
+ }
1237
+ const activity = projectCodexToolActivity(item, params.phase, meta);
1238
+ if (activity && params.phase === "start") this.emitAgentEvent({
1239
+ stream: "item",
1240
+ data: activity
1241
+ });
1242
+ this.emitAgentEvent(event);
1243
+ if (activity && params.phase !== "start") this.emitAgentEvent({
1244
+ stream: "item",
1245
+ data: activity
1246
+ });
1247
+ if (params.phase === "result") {
1248
+ this.toolTranscript.emitAfterToolCallObservation(item);
1249
+ await this.onNativeToolResultRecorded?.();
1250
+ }
1251
+ }
1252
+ };
1253
+ //#endregion
1254
+ //#region extensions/codex/src/app-server/native-subagent-history-recovery.ts
1255
+ const THREAD_READ_TIMEOUT_MS = 3e4;
1256
+ const RECENT_TERMINAL_TASK_RECONCILE_GRACE_MS = 6e4;
1257
+ var CodexNativeSubagentHistoryRecovery = class {
1258
+ constructor(client, queries) {
1259
+ this.client = client;
1260
+ this.queries = queries;
1261
+ this.recoveredParentSources = /* @__PURE__ */ new Map();
1262
+ }
1263
+ retainRecoveryParents(recovered, source) {
1264
+ for (const parent of recovered) {
1265
+ if (!parent || parent === source) continue;
1266
+ const sources = this.recoveredParentSources.get(parent) ?? /* @__PURE__ */ new Set();
1267
+ sources.add(source);
1268
+ this.recoveredParentSources.set(parent, sources);
1269
+ }
1270
+ }
1271
+ forgetRecoveredParent(state) {
1272
+ const ancestors = this.recoveredParentSources.get(state);
1273
+ if (ancestors) {
1274
+ for (const sources of this.recoveredParentSources.values()) if (sources.has(state)) for (const ancestor of ancestors) sources.add(ancestor);
1275
+ }
1276
+ this.recoveredParentSources.delete(state);
1277
+ }
1278
+ parentsForRetirement(parentThreadId, parents) {
1279
+ const current = parents.get(parentThreadId);
1280
+ const retiring = new Set(current ? [current] : []);
1281
+ for (const sources of this.recoveredParentSources.values()) for (const source of sources) if (source.parentThreadId === parentThreadId && (!current || source === current || source.historyOwner && current.historyOwner && this.acceptsParent(source, current))) retiring.add(source);
1282
+ for (const source of retiring) for (const [recovered, sources] of this.recoveredParentSources) if (sources.has(source)) retiring.add(recovered);
1283
+ return [...retiring].filter((state) => parents.get(state.parentThreadId) === state);
1284
+ }
1285
+ selectTaskRecords(state, records = state.taskRuntime?.listTaskRecords() ?? []) {
1286
+ return records.filter((task) => this.acceptsTask(task, state)).toSorted((a, b) => (b.startedAt ?? b.createdAt) - (a.startedAt ?? a.createdAt));
1287
+ }
1288
+ acceptsParent(stored, current) {
1289
+ return stored.requesterSessionKey === current.requesterSessionKey && (!stored.historyOwner || current.historyOwner !== void 0 && matchesCodexNativeSubagentHistoryOwner(stored.historyOwner, current.historyOwner));
1290
+ }
1291
+ acceptsTask(task, state) {
1292
+ if (task.requesterSessionKey !== state.requesterSessionKey) return false;
1293
+ return this.acceptsParent({
1294
+ requesterSessionKey: task.requesterSessionKey,
1295
+ historyOwner: readCodexNativeSubagentHistoryOwner(task.detail)
1296
+ }, state);
1297
+ }
1298
+ canRestoreTask(task, state) {
1299
+ const history = readCodexNativeSubagentHistoryOwner(task.detail);
1300
+ try {
1301
+ assertHistoryOwnerMatchesRegistration(history, state.historyOwner, history?.parentThreadId ?? state.parentThreadId, true);
1302
+ return this.acceptsTask(task, state);
1303
+ } catch {
1304
+ return false;
1305
+ }
1306
+ }
1307
+ readReceiverTask(state, childThreadId) {
1308
+ const records = state.taskRuntime?.listTaskRecords() ?? [];
1309
+ const task = records.toSorted((a, b) => (b.startedAt ?? b.createdAt) - (a.startedAt ?? a.createdAt)).find((record) => readNativeTaskAssignment(record)?.childThreadId === childThreadId);
1310
+ const assignment = task && readNativeTaskAssignment(task);
1311
+ const history = task && readCodexNativeSubagentHistoryOwner(task.detail);
1312
+ if (!task) return;
1313
+ if (!assignment || !history || history.parentThreadId !== state.parentThreadId && ![
1314
+ "succeeded",
1315
+ "failed",
1316
+ "cancelled"
1317
+ ].includes(task.status) || !this.canRestoreTask(task, state)) return { restorable: false };
1318
+ return {
1319
+ restorable: true,
1320
+ assignment,
1321
+ nativeParentThreadId: history.parentThreadId,
1322
+ records: this.selectTaskRecords(state, records)
1323
+ };
1324
+ }
1325
+ readChildAssignments(state, assignment, tasks) {
1326
+ let current = assignment;
1327
+ let latestAt = -Infinity;
1328
+ let terminal = false;
1329
+ let nativeParentThreadId = state.parentThreadId;
1330
+ const storedTurnIds = /* @__PURE__ */ new Set();
1331
+ const completedRunIds = [];
1332
+ for (const task of tasks) {
1333
+ const candidate = readNativeTaskAssignment(task);
1334
+ if (!this.acceptsTask(task, state) || candidate?.childThreadId !== assignment.childThreadId) continue;
1335
+ const history = readCodexNativeSubagentHistoryOwner(task.detail);
1336
+ if (history && !this.canRestoreTask(task, state)) continue;
1337
+ const taskTerminal = task.status === "succeeded" || task.status === "failed" || task.status === "cancelled";
1338
+ if (taskTerminal) completedRunIds.push(candidate.runId);
1339
+ for (const turnId of [candidate.nativeTurnId, candidate.initialTurnId]) if (turnId) storedTurnIds.add(turnId);
1340
+ const startedAt = task.startedAt ?? task.createdAt;
1341
+ if (startedAt > latestAt) {
1342
+ current = {
1343
+ ...candidate,
1344
+ nativeTurnId: candidate.nativeTurnId ?? (candidate.runId === assignment.runId ? assignment.nativeTurnId : void 0)
1345
+ };
1346
+ terminal = taskTerminal;
1347
+ nativeParentThreadId = history?.parentThreadId ?? state.parentThreadId;
1348
+ latestAt = startedAt;
1349
+ }
1350
+ }
1351
+ return {
1352
+ current,
1353
+ found: latestAt !== -Infinity,
1354
+ terminal,
1355
+ nativeParentThreadId,
1356
+ storedTurnIds,
1357
+ completedRunIds
1358
+ };
1359
+ }
1360
+ shouldReconcileTask(task, now) {
1361
+ if (task.status === "queued" || task.status === "running" || task.deliveryStatus === "pending") return true;
1362
+ if (task.deliveryStatus !== "not_applicable" || task.endedAt === void 0) return false;
1363
+ return task.endedAt >= now - RECENT_TERMINAL_TASK_RECONCILE_GRACE_MS;
1364
+ }
1365
+ prepareTaskRead(candidate, child, now) {
1366
+ const tasks = candidate.taskRuntime.listTaskRecords().filter((record) => record.runId === candidate.runId);
1367
+ const task = tasks[0];
1368
+ if (tasks.length !== 1 || !task || task.taskId !== candidate.taskId || !this.acceptsTask(task, candidate.parentState) || !this.shouldReconcileTask(task, now) || child?.completionTaskId && child.completionTaskId !== candidate.taskId) return;
1369
+ const assignment = child ?? readNativeTaskAssignment(task);
1370
+ if (!assignment) return;
1371
+ return {
1372
+ task,
1373
+ historyOwner: readCodexNativeSubagentHistoryOwner(task.detail),
1374
+ assignment,
1375
+ terminal: task.status === "succeeded" || task.status === "failed" || task.status === "cancelled"
1376
+ };
1377
+ }
1378
+ isCurrentTask(candidate, task, history, parentThreadId, now) {
1379
+ const currentTasks = candidate.taskRuntime.listTaskRecords().filter((record) => record.runId === candidate.runId);
1380
+ const current = currentTasks[0];
1381
+ if (currentTasks.length !== 1 || !current || current.taskId !== candidate.taskId || task.taskId !== candidate.taskId) return false;
1382
+ try {
1383
+ assertHistoryOwnerMatchesRegistration(readCodexNativeSubagentHistoryOwner(current.detail), candidate.parentState.historyOwner, parentThreadId, true);
1384
+ } catch {
1385
+ return false;
1386
+ }
1387
+ return current && current.taskId === task.taskId && this.acceptsTask(current, candidate.parentState) && this.shouldReconcileTask(current, now) && isDeepStrictEqual(readCodexNativeSubagentHistoryOwner(current.detail), history) && parentThreadId === (history?.parentThreadId ?? candidate.parentState.parentThreadId);
1388
+ }
1389
+ requestThreadRead(childThreadId, includeTurns) {
1390
+ return this.client.request("thread/read", {
1391
+ threadId: childThreadId,
1392
+ includeTurns
1393
+ }, { timeoutMs: THREAD_READ_TIMEOUT_MS });
1394
+ }
1395
+ requestLatestThreadTurn(childThreadId) {
1396
+ return this.client.request("thread/turns/list", {
1397
+ threadId: childThreadId,
1398
+ limit: 1,
1399
+ sortDirection: "desc",
1400
+ itemsView: "full"
1401
+ }, { timeoutMs: THREAD_READ_TIMEOUT_MS });
1402
+ }
1403
+ readTask(assignment, task, candidate) {
1404
+ const recordedStatus = task.status === "succeeded" || task.status === "failed" || task.status === "cancelled" ? task.status : void 0;
1405
+ return this.read(assignment, {
1406
+ resumeInterrupted: task.status === "queued" || task.status === "running",
1407
+ getTaskRecords: () => this.selectTaskRecords(candidate.parentState),
1408
+ observedTurns: candidate.observedTurns,
1409
+ ...recordedStatus && task.terminalSummary ? { recordedCompletion: {
1410
+ childThreadId: candidate.childThreadId,
1411
+ status: recordedStatus,
1412
+ statusLabel: "recorded_task_result",
1413
+ result: task.terminalSummary,
1414
+ completedAt: task.endedAt
1415
+ } } : {}
1416
+ });
1417
+ }
1418
+ async read(assignment, options) {
1419
+ const { childThreadId } = assignment;
1420
+ const { recordedCompletion } = options;
1421
+ const response = await this.requestThreadRead(childThreadId, true).catch(() => this.requestThreadRead(childThreadId, false));
1422
+ const thread = isJsonObject(response.thread) ? response.thread : void 0;
1423
+ if (!thread || readStringField(thread, "id")?.trim() !== childThreadId) return {
1424
+ resumable: false,
1425
+ threadState: "unavailable",
1426
+ observedPendingTurns: []
1427
+ };
1428
+ const firstObserved = options.observedTurns?.[0];
1429
+ const observedPredecessor = options.resumeInterrupted && firstObserved?.state && firstObserved.state !== "active" && !firstObserved.startObserved ? firstObserved.turnId : void 0;
1430
+ const turnId = assignment.nativeTurnId ?? observedPredecessor;
1431
+ const pendingTurnIds = /* @__PURE__ */ new Set([...this.queries.getPendingTurnIds(childThreadId), ...options.observedTurns?.map((turn) => turn.turnId) ?? []]);
1432
+ const unresolvedAssignment = options.resumeInterrupted && !turnId && pendingTurnIds.size > 0;
1433
+ const observedPendingTurns = [];
1434
+ for (const turn of Array.isArray(thread.turns) ? thread.turns : []) {
1435
+ const pendingTurnId = readStringField(turn, "id");
1436
+ if (pendingTurnId && pendingTurnIds.has(pendingTurnId)) observedPendingTurns.push({
1437
+ turnId: pendingTurnId,
1438
+ state: readNativeTurnState(turn)
1439
+ });
1440
+ }
1441
+ const threadStatus = isJsonObject(thread.status) ? normalizeIdentifier(readStringField(thread.status, "type")) : void 0;
1442
+ let completion;
1443
+ let fallbackCompletion;
1444
+ let nativeTurnId;
1445
+ let nativeTurnState;
1446
+ let resumable = false;
1447
+ let threadState = threadStatus === "active" ? "active" : threadStatus === "systemerror" ? "system_error" : threadStatus ? "other" : "unavailable";
1448
+ if (unresolvedAssignment) threadState = "unavailable";
1449
+ else if (turnId) {
1450
+ const turns = Array.isArray(thread.turns) ? thread.turns.filter(isJsonObject) : [];
1451
+ let index = turns.findIndex((turn) => readStringField(turn, "id") === turnId);
1452
+ while (options.resumeInterrupted && index >= 0 && index + 1 < turns.length && normalizeIdentifier(readStringField(turns[index], "status")) === "interrupted") index += 1;
1453
+ const turn = turns[index];
1454
+ const turnStatus = normalizeIdentifier(readStringField(turn, "status"));
1455
+ nativeTurnId = readStringField(turn, "id");
1456
+ nativeTurnState = readNativeTurnState(turn);
1457
+ completion = isJsonObject(turn) ? readTurnCompletion(turn, childThreadId) : void 0;
1458
+ resumable = turnStatus === "interrupted";
1459
+ threadState = turnStatus === "inprogress" ? "active" : turnStatus ? "other" : "unavailable";
1460
+ } else if (threadStatus === "active") {
1461
+ const turn = Array.isArray(thread.turns) ? thread.turns.at(-1) : void 0;
1462
+ if (normalizeIdentifier(readStringField(turn, "status")) === "inprogress") {
1463
+ nativeTurnId = readStringField(turn, "id");
1464
+ nativeTurnState = "active";
1465
+ }
1466
+ } else if (threadStatus !== "systemerror") {
1467
+ const turnRecovery = readThreadTurnRecovery(thread, childThreadId);
1468
+ nativeTurnId = turnRecovery.nativeTurnId;
1469
+ nativeTurnState = turnRecovery.nativeTurnState;
1470
+ completion = turnRecovery.completion;
1471
+ resumable = turnRecovery.resumable;
1472
+ }
1473
+ if (!unresolvedAssignment && threadStatus === "systemerror" && (!turnId || threadState === "unavailable" && !completion)) {
1474
+ const turnsResponse = await this.requestLatestThreadTurn(childThreadId).catch(() => void 0);
1475
+ const data = isJsonObject(turnsResponse) && Array.isArray(turnsResponse.data) ? turnsResponse.data : [];
1476
+ const latestTurn = isJsonObject(data[0]) ? data[0] : void 0;
1477
+ const latestTurnId = readStringField(latestTurn, "id");
1478
+ if (latestTurnId && pendingTurnIds.has(latestTurnId)) observedPendingTurns.push({
1479
+ turnId: latestTurnId,
1480
+ state: readNativeTurnState(latestTurn)
1481
+ });
1482
+ const latestTurnStatus = normalizeIdentifier(readStringField(latestTurn, "status"));
1483
+ const matchesAssignment = !turnId || readStringField(latestTurn, "id") === turnId;
1484
+ if (latestTurn && matchesAssignment) {
1485
+ if (turnId) {
1486
+ const turnRecovery = readThreadTurnRecovery({ turns: [latestTurn] }, childThreadId);
1487
+ nativeTurnId = turnRecovery.nativeTurnId;
1488
+ nativeTurnState = turnRecovery.nativeTurnState;
1489
+ completion = turnRecovery.completion;
1490
+ resumable = turnRecovery.resumable;
1491
+ } else if (latestTurnStatus === "failed") completion = readTurnCompletion(latestTurn, childThreadId);
1492
+ }
1493
+ const current = !latestTurn ? this.queries.getCurrentAssignmentState(childThreadId) : void 0;
1494
+ const taskRecords = !latestTurn ? options.getTaskRecords() : [];
1495
+ const task = taskRecords.find((record) => record.runId === assignment.runId);
1496
+ const hasSuccessor = task && taskRecords.some((record) => record.requesterSessionKey === task.requesterSessionKey && (record.startedAt ?? record.createdAt) > (task.startedAt ?? task.createdAt) && readNativeTaskAssignment(record)?.childThreadId === childThreadId);
1497
+ const unresolvedCurrentAssignment = current?.runId === assignment.runId && current?.nativeTurnState !== "completed" && !hasSuccessor;
1498
+ if (latestTurnStatus === "inprogress" && matchesAssignment) {
1499
+ nativeTurnId = readStringField(latestTurn, "id");
1500
+ nativeTurnState = "active";
1501
+ threadState = "active";
1502
+ } else if (!completion && !resumable && latestTurnStatus !== "inprogress" && (!turnId || !latestTurn && unresolvedCurrentAssignment)) fallbackCompletion = systemErrorFallbackCompletion(childThreadId);
1503
+ }
1504
+ if (recordedCompletion && (!turnId || !completion || completion.status !== recordedCompletion.status)) {
1505
+ completion = recordedCompletion;
1506
+ if (!turnId) {
1507
+ nativeTurnId = void 0;
1508
+ nativeTurnState = void 0;
1509
+ }
1510
+ fallbackCompletion = void 0;
1511
+ resumable = false;
1512
+ threadState = "other";
1513
+ }
1514
+ const lineage = {
1515
+ parentThreadId: readThreadParentThreadId(thread),
1516
+ agentPath: normalizeOptionalString(readStringField(readThreadSpawnSource(thread), "agent_path"))
1517
+ };
1518
+ if (!turnId && !recordedCompletion && hasSavedSuccessor(assignment, options.getTaskRecords())) return {
1519
+ ...lineage,
1520
+ assignmentUnresolved: true,
1521
+ observedPendingTurns,
1522
+ resumable: false,
1523
+ threadState: "unavailable"
1524
+ };
1525
+ return {
1526
+ ...lineage,
1527
+ assignmentTurnId: turnId,
1528
+ nativeTurnId,
1529
+ nativeTurnState,
1530
+ observedPendingTurns,
1531
+ completion,
1532
+ fallbackCompletion,
1533
+ resumable,
1534
+ threadState
1535
+ };
1536
+ }
1537
+ };
1538
+ function hasSavedSuccessor(assignment, records) {
1539
+ const task = records.find((record) => record.runId === assignment.runId);
1540
+ return Boolean(task && records.some((record) => {
1541
+ const successor = readNativeTaskAssignment(record);
1542
+ return record.requesterSessionKey === task.requesterSessionKey && record.runId !== assignment.runId && successor?.childThreadId === assignment.childThreadId && Boolean(successor.initialTurnId);
1543
+ }));
1544
+ }
1545
+ function readThreadTurnRecovery(thread, childThreadId) {
1546
+ const turns = Array.isArray(thread.turns) ? thread.turns : [];
1547
+ for (let index = turns.length - 1; index >= 0; index -= 1) {
1548
+ const turn = turns[index];
1549
+ if (!isJsonObject(turn)) continue;
1550
+ const status = normalizeIdentifier(readStringField(turn, "status"));
1551
+ return {
1552
+ nativeTurnId: readStringField(turn, "id"),
1553
+ nativeTurnState: readNativeTurnState(turn),
1554
+ completion: readTurnCompletion(turn, childThreadId),
1555
+ resumable: status === "interrupted"
1556
+ };
1557
+ }
1558
+ return { resumable: false };
1559
+ }
1560
+ function readNativeTurnEnd(turn) {
1561
+ const status = normalizeIdentifier(readStringField(turn, "status"));
1562
+ return status === "completed" || status === "failed" || status === "interrupted" ? status : void 0;
1563
+ }
1564
+ function readNativeTurnState(turn) {
1565
+ return normalizeIdentifier(readStringField(turn, "status")) === "inprogress" ? "active" : readNativeTurnEnd(turn);
1566
+ }
1567
+ function readTurnErrorMessage(turn) {
1568
+ const error = isJsonObject(turn.error) ? turn.error : void 0;
1569
+ return normalizeOptionalString(readStringField(error, "message")) ?? normalizeOptionalString(isJsonObject(error?.codexErrorInfo) ? readStringField(error.codexErrorInfo, "message") : void 0);
1570
+ }
1571
+ function systemErrorFallbackCompletion(childThreadId) {
1572
+ return {
1573
+ childThreadId,
1574
+ status: "failed",
1575
+ statusLabel: "system_error",
1576
+ result: "Subagent runtime reported a system error."
1577
+ };
1578
+ }
1579
+ function readTurnCompletion(turn, childThreadId) {
1580
+ const status = normalizeIdentifier(readStringField(turn, "status"));
1581
+ if (status === "inprogress" || !status) return;
1582
+ const result = readLastAgentMessage(turn);
1583
+ const completedAtSeconds = asFiniteNumber(turn.completedAt);
1584
+ const completedAt = completedAtSeconds === void 0 ? void 0 : Math.round(completedAtSeconds * 1e3);
1585
+ if (status === "completed") return {
1586
+ childThreadId,
1587
+ status: "succeeded",
1588
+ statusLabel: result ? "task_complete" : "completed_without_final_message",
1589
+ result: result ?? "Subagent completed without a final assistant message.",
1590
+ completedAt
1591
+ };
1592
+ if (status === "interrupted") return;
1593
+ if (status === "failed") return {
1594
+ childThreadId,
1595
+ status: "failed",
1596
+ statusLabel: "task_failed",
1597
+ result: readTurnErrorMessage(turn) ?? result ?? "Subagent failed.",
1598
+ completedAt
1599
+ };
1600
+ }
1601
+ function readLastAgentMessage(turn) {
1602
+ const items = Array.isArray(turn.items) ? turn.items : [];
1603
+ let legacyResult;
1604
+ for (let index = items.length - 1; index >= 0; index -= 1) {
1605
+ const item = items[index];
1606
+ if (!isJsonObject(item)) continue;
1607
+ if (normalizeIdentifier(readStringField(item, "type")) !== "agentmessage") continue;
1608
+ const text = readStringField(item, "text")?.trim();
1609
+ if (!text) continue;
1610
+ const phase = normalizeIdentifier(readStringField(item, "phase"));
1611
+ if (phase === "finalanswer") return text;
1612
+ if (!phase) legacyResult ??= text;
1613
+ }
1614
+ return legacyResult;
1615
+ }
1616
+ function isNoFinalCompletion(completion) {
1617
+ return completion.status === "succeeded" && completion.statusLabel === "completed_without_final_message";
1618
+ }
1619
+ function readThreadParentThreadId(thread) {
1620
+ return readStringField(thread, "parentThreadId")?.trim() ?? readStringField(readThreadSpawnSource(thread), "parent_thread_id")?.trim();
1621
+ }
1622
+ function readThreadSpawnSource(thread) {
1623
+ const source = isJsonObject(thread?.source) ? thread.source : void 0;
1624
+ const subAgent = isJsonObject(source?.subAgent) ? source.subAgent : void 0;
1625
+ return isJsonObject(subAgent?.thread_spawn) ? subAgent.thread_spawn : void 0;
1626
+ }
1627
+ function normalizeIdentifier(value) {
1628
+ return value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
1629
+ }
1630
+ //#endregion
1631
+ //#region extensions/codex/src/app-server/native-subagent-retry.ts
1632
+ function delayForAttempt(delays, attempt) {
1633
+ return Math.max(1, delays[Math.min(attempt, delays.length - 1)] ?? 1);
1634
+ }
1635
+ //#endregion
1636
+ //#region extensions/codex/src/app-server/native-subagent-recovery-coordinator.ts
1637
+ const DEFAULT_RECOVERY_POLL_DELAYS_MS = [
1638
+ 2e3,
1639
+ 5e3,
1640
+ 1e4,
1641
+ 15e3,
1642
+ 3e4,
1643
+ 6e4,
1644
+ 12e4,
1645
+ 3e5
1646
+ ];
1647
+ var CodexNativeSubagentRecoveryCoordinator = class {
1648
+ constructor(dependencies) {
1649
+ this.dependencies = dependencies;
1650
+ this.taskReconciliations = /* @__PURE__ */ new Map();
1651
+ this.taskReconciliationTimers = /* @__PURE__ */ new Map();
1652
+ this.threadStatusRevisions = /* @__PURE__ */ new Map();
1653
+ this.recoveryPollDelaysMs = dependencies.recoveryPollDelaysMs ?? DEFAULT_RECOVERY_POLL_DELAYS_MS;
1654
+ }
1655
+ allCandidates() {
1656
+ return [...this.taskReconciliations.values(), ...this.taskReconciliationTimers.values()].map(({ candidate }) => candidate);
1657
+ }
1658
+ observeUnregisteredTurn(threadId, turnId, started, end) {
1659
+ const candidates = [...new Set(this.allCandidates())].filter((candidate) => candidate.childThreadId === threadId && !this.dependencies.isRetiredParent(candidate.parentState));
1660
+ for (const turns of new Set(candidates.map((candidate) => candidate.observedTurns))) {
1661
+ const observed = turns.find((entry) => entry.turnId === turnId);
1662
+ if (!started) {
1663
+ if (observed) observed.state = end;
1664
+ else turns.push({
1665
+ turnId,
1666
+ state: end
1667
+ });
1668
+ } else if (!observed) {
1669
+ const previous = turns.at(-1);
1670
+ if (previous?.state === "active") previous.state = void 0;
1671
+ turns.push({
1672
+ turnId,
1673
+ state: "active",
1674
+ startObserved: true
1675
+ });
1676
+ }
1677
+ }
1678
+ return candidates;
1679
+ }
1680
+ hasRevision(threadId) {
1681
+ return this.threadStatusRevisions.has(threadId);
1682
+ }
1683
+ observeRevision(threadId) {
1684
+ const revision = this.threadStatusRevisions.get(threadId);
1685
+ if (revision) revision.value += 1;
1686
+ }
1687
+ isTerminalRevision(threadId) {
1688
+ return this.threadStatusRevisions.get(threadId)?.terminal === true;
1689
+ }
1690
+ markTerminalRevision(threadId) {
1691
+ const revision = this.threadStatusRevisions.get(threadId);
1692
+ if (revision) revision.terminal = true;
1693
+ }
1694
+ seedRevision(threadId, parentThreadId) {
1695
+ this.threadStatusRevisions.set(threadId, this.threadStatusRevisions.get(threadId) ?? {
1696
+ value: 0,
1697
+ readers: 0,
1698
+ parentThreadId
1699
+ });
1700
+ }
1701
+ dispose() {
1702
+ for (const { timer } of this.taskReconciliationTimers.values()) clearTimeout(timer);
1703
+ this.taskReconciliationTimers.clear();
1704
+ }
1705
+ async reconcileRegisteredChild(childState) {
1706
+ if (childState.terminal || this.dependencies.isDisposed() || !this.dependencies.isRegisteredChild(childState)) return false;
1707
+ if (childState.recoveryInFlight) return await childState.recoveryInFlight;
1708
+ const recovery = this.dependencies.reconcileChildState(childState);
1709
+ childState.recoveryInFlight = recovery;
1710
+ try {
1711
+ return await recovery;
1712
+ } finally {
1713
+ if (childState.recoveryInFlight === recovery) childState.recoveryInFlight = void 0;
1714
+ }
1715
+ }
1716
+ pendingChildRecoveries(state, threadId) {
1717
+ return [...new Set(this.allCandidates())].filter((candidate) => candidate.childThreadId === threadId && candidate.requesterSessionKey === state.requesterSessionKey && candidate.parentState.parentThreadId === state.parentThreadId && !this.dependencies.isRetiredParent(candidate.parentState));
1718
+ }
1719
+ resolveChildTurnBuffer(state, threadId) {
1720
+ return this.pendingChildRecoveries(state, threadId)[0]?.observedTurns ?? [];
1721
+ }
1722
+ clearTerminalRevisionsForParent(parentThreadId) {
1723
+ for (const [threadId, revision] of this.threadStatusRevisions) if (revision.parentThreadId === parentThreadId) this.collectThreadStatusRevision(threadId, revision);
1724
+ }
1725
+ collectThreadStatusRevision(threadId, revision = this.threadStatusRevisions.get(threadId)) {
1726
+ if (!revision || revision.readers > 0 || Boolean(this.dependencies.currentChild(threadId))) return;
1727
+ if ((revision.parentThreadId ? this.dependencies.parentState(revision.parentThreadId) : void 0)?.owners.size) return;
1728
+ if (this.threadStatusRevisions.get(threadId) === revision) this.threadStatusRevisions.delete(threadId);
1729
+ }
1730
+ scheduleRecoveryPoll(childState) {
1731
+ if (childState.terminal || childState.settledWithoutCompletion || childState.recoveryTimer || this.dependencies.isDisposed() || this.recoveryPollDelaysMs.length === 0) return;
1732
+ const delayMs = delayForAttempt(this.recoveryPollDelaysMs, childState.recoveryAttempt++);
1733
+ childState.recoveryTimer = setTimeout(() => {
1734
+ childState.recoveryTimer = void 0;
1735
+ this.reconcileRegisteredChild(childState).catch((error) => {
1736
+ logRecoveryFailure(childState.childThreadId, error);
1737
+ return false;
1738
+ }).then(async (reconciled) => {
1739
+ if (reconciled || !this.dependencies.isRegisteredChild(childState)) return;
1740
+ const fallback = childState.fallbackCompletion;
1741
+ const state = this.dependencies.parentState(childState.parentThreadId);
1742
+ if (fallback && state && childState.recoveryAttempt >= 2) {
1743
+ await this.dependencies.processCompletion(state, childState, fallback, fallback.completedAt ?? this.dependencies.now());
1744
+ return;
1745
+ }
1746
+ this.scheduleRecoveryPoll(childState);
1747
+ });
1748
+ }, delayMs);
1749
+ childState.recoveryTimer.unref();
1750
+ }
1751
+ setRecoveryFallback(childState, completion, eventAt) {
1752
+ if (childState.terminal) return;
1753
+ const current = childState.fallbackCompletion;
1754
+ if (current?.status === completion.status && current.statusLabel === completion.statusLabel && current.result === completion.result) return;
1755
+ if (childState.recoveryTimer) {
1756
+ clearTimeout(childState.recoveryTimer);
1757
+ childState.recoveryTimer = void 0;
1758
+ }
1759
+ childState.recoveryAttempt = 0;
1760
+ childState.fallbackCompletion = {
1761
+ ...completion,
1762
+ completedAt: eventAt
1763
+ };
1764
+ this.scheduleRecoveryPoll(childState);
1765
+ }
1766
+ clearSystemErrorFallback(childState) {
1767
+ if (childState.fallbackCompletion?.statusLabel !== "system_error") return;
1768
+ childState.fallbackCompletion = void 0;
1769
+ }
1770
+ retainThreadStatusRevision(threadId) {
1771
+ const revision = this.threadStatusRevisions.get(threadId) ?? {
1772
+ value: 0,
1773
+ readers: 0
1774
+ };
1775
+ this.threadStatusRevisions.set(threadId, revision);
1776
+ revision.readers += 1;
1777
+ const capturedValue = revision.value;
1778
+ let retained = true;
1779
+ return {
1780
+ isCurrent: () => this.threadStatusRevisions.get(threadId) === revision && revision.value === capturedValue,
1781
+ release: () => {
1782
+ if (!retained) return;
1783
+ retained = false;
1784
+ revision.readers -= 1;
1785
+ this.collectThreadStatusRevision(threadId, revision);
1786
+ }
1787
+ };
1788
+ }
1789
+ clearRecoveryTimers(childState) {
1790
+ if (childState.recoveryTimer) {
1791
+ clearTimeout(childState.recoveryTimer);
1792
+ childState.recoveryTimer = void 0;
1793
+ }
1794
+ }
1795
+ async reconcileTaskCandidate(candidate, after) {
1796
+ const key = `${candidate.requesterSessionKey}\0${candidate.runId}`;
1797
+ const scheduled = this.taskReconciliationTimers.get(key);
1798
+ if (scheduled) {
1799
+ clearTimeout(scheduled.timer);
1800
+ this.taskReconciliationTimers.delete(key);
1801
+ }
1802
+ const existing = this.taskReconciliations.get(key);
1803
+ if (existing) {
1804
+ await existing.promise;
1805
+ return;
1806
+ }
1807
+ const reconciliation = after ? after.then(() => this.dependencies.reconcileTaskCandidateOnce(candidate)) : this.dependencies.reconcileTaskCandidateOnce(candidate);
1808
+ this.taskReconciliations.set(key, {
1809
+ candidate,
1810
+ promise: reconciliation
1811
+ });
1812
+ try {
1813
+ await reconciliation;
1814
+ } finally {
1815
+ if (this.taskReconciliations.get(key)?.promise === reconciliation) this.taskReconciliations.delete(key);
1816
+ this.dependencies.onCandidateSettled(candidate.parentState);
1817
+ }
1818
+ }
1819
+ scheduleTaskCandidateReconciliation(candidate) {
1820
+ const key = `${candidate.requesterSessionKey}\0${candidate.runId}`;
1821
+ if (this.dependencies.isDisposed() || this.dependencies.isRetiredParent(candidate.parentState) || this.recoveryPollDelaysMs.length === 0 || this.taskReconciliationTimers.has(key)) return;
1822
+ const delayMs = delayForAttempt(this.recoveryPollDelaysMs, candidate.recoveryAttempt++);
1823
+ const timer = setTimeout(() => {
1824
+ this.taskReconciliationTimers.delete(key);
1825
+ this.reconcileTaskCandidate(candidate).catch((error) => {
1826
+ logRecoveryFailure(candidate.childThreadId, error);
1827
+ this.scheduleTaskCandidateReconciliation(candidate);
1828
+ });
1829
+ }, delayMs);
1830
+ this.taskReconciliationTimers.set(key, {
1831
+ candidate,
1832
+ timer
1833
+ });
1834
+ timer.unref();
1835
+ }
1836
+ };
1837
+ function logRecoveryFailure(childThreadId, error) {
1838
+ embeddedAgentLog.debug("Codex native subagent history is not ready", {
1839
+ childThreadId,
1840
+ error: formatErrorMessage(error)
1841
+ });
1842
+ }
1843
+ //#endregion
1844
+ //#region extensions/codex/src/app-server/native-subagent-close-owner.ts
1845
+ var CodexNativeSubagentCloseOwner = class {
1846
+ constructor(client, callbacks) {
1847
+ this.client = client;
1848
+ this.callbacks = callbacks;
1849
+ this.calls = /* @__PURE__ */ new WeakMap();
1850
+ }
1851
+ bind(state, turnId) {
1852
+ this.prune(state);
1853
+ for (const [key, call] of this.calls.get(state) ?? []) if (call.completionObserved && call.turnId === turnId) this.completeChildClose(state, key, call);
1854
+ }
1855
+ clear(state) {
1856
+ this.calls.delete(state);
1857
+ }
1858
+ hasPending(state) {
1859
+ return [...this.calls.get(state)?.values() ?? []].some((call) => call.completing && !call.settled);
1860
+ }
1861
+ settlements(state) {
1862
+ return [...this.calls.get(state)?.values() ?? []].flatMap((call) => call.settlement ? [call.settlement] : []);
1863
+ }
1864
+ async observe(notification, state) {
1865
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
1866
+ const item = isJsonObject(params?.item) ? params.item : void 0;
1867
+ const turnId = readStringField(params, "turnId");
1868
+ const itemId = readStringField(item, "id");
1869
+ const senderThreadId = readStringField(item, "senderThreadId");
1870
+ if (!turnId || !itemId || readStringField(params, "threadId") !== state.parentThreadId || senderThreadId !== void 0 && senderThreadId !== state.parentThreadId || this.callbacks.isParentRetired(state)) return;
1871
+ let calls = this.calls.get(state);
1872
+ if (!calls) {
1873
+ calls = /* @__PURE__ */ new Map();
1874
+ this.calls.set(state, calls);
1875
+ }
1876
+ const key = `${turnId}\0${itemId}`;
1877
+ const childThreadIds = new Set(readNativeSubagentThreadIds(item?.receiverThreadIds));
1878
+ if (notification.method === "item/started") {
1879
+ if (calls.has(key)) return;
1880
+ const owners = new Set([...state.owners.values()].filter((owner) => !owner.turnId || owner.turnId === turnId));
1881
+ if (owners.size === 0) return;
1882
+ const targets = [];
1883
+ for (const childThreadId of childThreadIds) {
1884
+ const known = this.callbacks.knownChild(childThreadId);
1885
+ if (known?.parent !== state || known.pendingTurns.length > 0) continue;
1886
+ targets.push({
1887
+ childThreadId,
1888
+ runId: known.assignment.runId,
1889
+ nativeTurnId: known.turnId,
1890
+ childState: this.callbacks.currentChild(childThreadId),
1891
+ forget: this.callbacks.captureForget?.(childThreadId).catch((error) => {
1892
+ logRecoveryFailure(childThreadId, error);
1893
+ })
1894
+ });
1895
+ }
1896
+ calls.set(key, {
1897
+ turnId,
1898
+ owners,
1899
+ targets
1900
+ });
1901
+ return;
1902
+ }
1903
+ const call = calls.get(key);
1904
+ if (!call || call.targets.some((target) => !childThreadIds.has(target.childThreadId)) || childThreadIds.size !== call.targets.length) return;
1905
+ call.completionObserved = true;
1906
+ await this.completeChildClose(state, key, call);
1907
+ }
1908
+ prune(state) {
1909
+ const calls = this.calls.get(state);
1910
+ if (!calls) return;
1911
+ for (const [key, call] of calls) {
1912
+ if (call.completing && !call.settled) continue;
1913
+ if (![...state.owners.values()].some((owner) => call.owners.has(owner) && (!owner.turnId || owner.turnId === call.turnId))) calls.delete(key);
1914
+ }
1915
+ }
1916
+ retireChild(state, childState, summary, releaseSubscription) {
1917
+ if (childState.pendingCompletion && !this.callbacks.isParentRetired(state)) {
1918
+ childState.subscriptionClosed = true;
1919
+ this.callbacks.releaseDirectChild(childState);
1920
+ this.callbacks.clearRecoveryTimers(childState);
1921
+ releaseSubscription?.();
1922
+ this.callbacks.releaseClientRetentionIfIdle();
1923
+ return;
1924
+ }
1925
+ if (!childState.terminal) {
1926
+ childState.terminal = true;
1927
+ const known = this.callbacks.knownChild(childState.childThreadId);
1928
+ if (known?.assignment.runId === childState.runId) {
1929
+ known.assignment.terminal = true;
1930
+ known.assignment.nativeTurnId = childState.nativeTurnId;
1931
+ }
1932
+ this.callbacks.markTerminalRevision(childState.childThreadId);
1933
+ const eventAt = this.callbacks.now();
1934
+ state.mirror?.markAuthoritativeCompletion(childState.childThreadId);
1935
+ state.taskRuntime?.finalizeTaskRunByRunId({
1936
+ runId: childState.runId,
1937
+ status: "cancelled",
1938
+ endedAt: eventAt,
1939
+ lastEventAt: eventAt,
1940
+ error: summary,
1941
+ progressSummary: summary,
1942
+ terminalSummary: summary
1943
+ });
1944
+ }
1945
+ if (childState.pendingCompletion) {
1946
+ childState.pendingCompletion = void 0;
1947
+ state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
1948
+ runId: childState.runId,
1949
+ deliveryStatus: "failed",
1950
+ error: summary
1951
+ });
1952
+ }
1953
+ this.callbacks.unregisterChild(childState);
1954
+ releaseSubscription?.();
1955
+ }
1956
+ retireReceiver(receiver, releaseSubscription) {
1957
+ const threadId = receiver.assignment.childThreadId;
1958
+ if (this.callbacks.isParentRetired(receiver.parent) && this.callbacks.knownChild(threadId) === receiver && !this.callbacks.currentChild(threadId)) releaseSubscription();
1959
+ }
1960
+ completeChildClose(state, key, call) {
1961
+ if (call.settlement) return call.settlement;
1962
+ const settlement = this.confirmChildClose(state, key, call);
1963
+ if (call.completing) call.settlement = settlement;
1964
+ return settlement;
1965
+ }
1966
+ async confirmChildClose(state, key, call) {
1967
+ const isCurrent = () => this.callbacks.isParentCurrent(state) && this.calls.get(state)?.get(key) === call;
1968
+ const isTargetCurrent = (target) => {
1969
+ const known = this.callbacks.knownChild(target.childThreadId);
1970
+ const childState = this.callbacks.currentChild(target.childThreadId);
1971
+ return known?.parent === state && known.assignment.runId === target.runId && known.turnId === target.nativeTurnId && known.pendingTurns.length === 0 && (childState === void 0 || childState === target.childState);
1972
+ };
1973
+ const recordUnconfirmedClose = () => {
1974
+ if (!isCurrent()) return;
1975
+ for (const target of call.targets) {
1976
+ const known = this.callbacks.knownChild(target.childThreadId);
1977
+ const childState = this.callbacks.currentChild(target.childThreadId);
1978
+ if (!isTargetCurrent(target) || known?.assignment.terminal || childState?.terminal || childState?.pendingCompletion) continue;
1979
+ state.taskRuntime?.recordTaskRunProgressByRunId({
1980
+ runId: target.runId,
1981
+ lastEventAt: this.callbacks.now(),
1982
+ progressSummary: "Could not confirm that the subagent closed. Retry the close request."
1983
+ });
1984
+ }
1985
+ };
1986
+ if (call.completing || !isCurrent() || ![...state.owners.values()].some((owner) => call.owners.has(owner) && owner.turnId === call.turnId)) return;
1987
+ call.completing = true;
1988
+ try {
1989
+ const forgetters = await Promise.all(call.targets.map((target) => Promise.resolve(target.forget)));
1990
+ if (!isCurrent()) return;
1991
+ const loaded = await this.client.request("thread/loaded/list", {}, { timeoutMs: 1e4 });
1992
+ if (!isCurrent()) return;
1993
+ if (!isJsonObject(loaded) || loaded.nextCursor !== null || !Array.isArray(loaded.data) || !loaded.data.every((id) => typeof id === "string" && id.trim() !== "")) {
1994
+ recordUnconfirmedClose();
1995
+ return;
1996
+ }
1997
+ for (const [index, target] of call.targets.entries()) {
1998
+ const childState = this.callbacks.currentChild(target.childThreadId);
1999
+ if (loaded.data.includes(target.childThreadId) || !isTargetCurrent(target)) continue;
2000
+ const forget = forgetters[index];
2001
+ if (childState) this.retireChild(state, childState, "Subagent was closed.", forget);
2002
+ else forget?.();
2003
+ }
2004
+ } catch (error) {
2005
+ embeddedAgentLog.warn("Failed to confirm Codex native subagent close", {
2006
+ parentThreadId: state.parentThreadId,
2007
+ error: formatErrorMessage(error)
2008
+ });
2009
+ recordUnconfirmedClose();
2010
+ } finally {
2011
+ call.settled = true;
2012
+ if (this.calls.get(state)?.get(key) === call) call.targets = [];
2013
+ this.prune(state);
2014
+ this.callbacks.pruneParent(state);
2015
+ }
2016
+ }
2017
+ };
2018
+ function isCodexNativeSubagentCloseNotification(notification) {
2019
+ if (notification.method !== "item/started" && notification.method !== "item/completed") return false;
2020
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2021
+ const item = isJsonObject(params?.item) ? params.item : void 0;
2022
+ return readStringField(item, "type") === "collabAgentToolCall" && normalizeIdentifier(readStringField(item, "tool")) === "closeagent";
2023
+ }
2024
+ //#endregion
2025
+ //#region extensions/codex/src/app-server/native-subagent-completion-delivery.ts
2026
+ const DEFAULT_COMPLETION_DELIVERY_RETRY_DELAYS_MS = [
2027
+ 5e3,
2028
+ 15e3,
2029
+ 3e4,
2030
+ 6e4,
2031
+ 12e4,
2032
+ 3e5
2033
+ ];
2034
+ const completionDeliveryOwners = /* @__PURE__ */ new Map();
2035
+ var CodexNativeSubagentCompletionDelivery = class {
2036
+ constructor(dependencies) {
2037
+ this.dependencies = dependencies;
2038
+ this.retryDelaysMs = dependencies.retryDelaysMs ?? DEFAULT_COMPLETION_DELIVERY_RETRY_DELAYS_MS;
2039
+ this.maxRetries = dependencies.maxRetries ?? this.retryDelaysMs.length;
2040
+ }
2041
+ async deliverPending(state, childState) {
2042
+ const completion = childState.pendingCompletion;
2043
+ if (!completion || !this.dependencies.isCurrentChild(childState) || !this.dependencies.isCurrentParent(state) || this.dependencies.isRetiredParent(state)) return;
2044
+ if (childState.deliveringCompletion || childState.completionDeliveryTimer) return;
2045
+ childState.deliveringCompletion = true;
2046
+ try {
2047
+ if (!this.persistPending(state, childState)) return;
2048
+ if (state.owners.size > 0 || !state.taskRuntimeScope) return;
2049
+ const task = state.taskRuntime?.listTaskRecords().find((record) => record.runId === childState.runId);
2050
+ const historyOwner = readCodexNativeSubagentHistoryOwner(task?.detail);
2051
+ const delivery = await this.dependencies.deliver({
2052
+ scope: state.taskRuntimeScope,
2053
+ ...historyOwner ? { expectedRequester: {
2054
+ sessionId: historyOwner.sessionId,
2055
+ lifecycleRevision: historyOwner.lifecycleRevision
2056
+ } } : {},
2057
+ isSourceSessionAdmissionAllowed: () => this.dependencies.isCurrentChild(childState) && this.dependencies.isCurrentParent(state) && !this.dependencies.isRetiredParent(state) && this.claim(state, childState),
2058
+ childSessionKey: childState.runId,
2059
+ childSessionId: completion.childThreadId,
2060
+ announceId: `codex-native:${childState.nativeParentThreadId}:${readCodexNativeSubagentRunId(childState.runId)?.turnId ? childState.runId : completion.childThreadId}:${completion.status}`,
2061
+ announceType: "Subagent",
2062
+ taskLabel: "Subagent",
2063
+ status: completion.status,
2064
+ statusLabel: completion.statusLabel,
2065
+ result: completion.result,
2066
+ replyInstruction: "Use the Codex native subagent result to continue or wrap up the parent task. If this is a Discord/channel session, send the visible response with the message tool instead of only writing a transcript final answer. Reply in your normal assistant voice and do not expose internal notification markup."
2067
+ });
2068
+ if (!this.dependencies.isCurrentChild(childState) || !this.dependencies.isCurrentParent(state)) return;
2069
+ if (!this.claim(state, childState)) {
2070
+ this.dependencies.unregisterChild(childState);
2071
+ return;
2072
+ }
2073
+ if (isDurableAgentHarnessCompletionDelivery(delivery)) {
2074
+ childState.nativeCompletionDelivered = true;
2075
+ childState.completionTaskPhase = "delivery";
2076
+ this.persistPending(state, childState);
2077
+ return;
2078
+ }
2079
+ if (delivery.recoveryBlocked) {
2080
+ this.dependencies.unregisterChild(childState);
2081
+ return;
2082
+ }
2083
+ if (delivery.recoveryPending) {
2084
+ this.scheduleRetry(childState, delivery.error ?? "requester recovery owns completion", false);
2085
+ return;
2086
+ }
2087
+ const error = delivery.error ?? "completion delivery did not produce a parent response";
2088
+ state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2089
+ runId: childState.runId,
2090
+ deliveryStatus: "pending",
2091
+ error
2092
+ });
2093
+ this.scheduleRetry(childState, error);
2094
+ } catch (error) {
2095
+ if (!this.dependencies.isCurrentChild(childState) || !this.dependencies.isCurrentParent(state)) return;
2096
+ if (!this.claim(state, childState)) {
2097
+ this.dependencies.unregisterChild(childState);
2098
+ return;
2099
+ }
2100
+ const message = formatErrorMessage(error);
2101
+ if (!childState.completionTaskPhase) state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2102
+ runId: childState.runId,
2103
+ deliveryStatus: "pending",
2104
+ error: message
2105
+ });
2106
+ this.scheduleRetry(childState, message);
2107
+ embeddedAgentLog.warn("Failed to deliver Codex native subagent completion", {
2108
+ parentThreadId: state.parentThreadId,
2109
+ childThreadId: completion.childThreadId,
2110
+ error: message
2111
+ });
2112
+ } finally {
2113
+ childState.deliveringCompletion = false;
2114
+ }
2115
+ }
2116
+ finish(state, child) {
2117
+ child.completionTaskPhase ??= "delivery";
2118
+ if (child.completionDeliveryTimer) {
2119
+ clearTimeout(child.completionDeliveryTimer);
2120
+ child.completionDeliveryTimer = void 0;
2121
+ }
2122
+ this.deliverPending(state, child);
2123
+ }
2124
+ applyReceipts(state, runIds, children) {
2125
+ for (const runId of runIds) {
2126
+ const child = children.get(runId);
2127
+ const deliveryParent = child && this.dependencies.getParent(child.parentThreadId);
2128
+ if (!child || !deliveryParent || !this.dependencies.isCurrentChild(child) || !this.dependencies.isCurrentParent(state) || this.dependencies.isRetiredParent(state) || !this.dependencies.isCurrentParent(deliveryParent) || this.dependencies.isRetiredParent(deliveryParent)) continue;
2129
+ if (deliveryParent !== state) {
2130
+ if (!state.requesterSessionKey?.trim() || state.requesterSessionKey !== deliveryParent.requesterSessionKey) continue;
2131
+ const task = deliveryParent.taskRuntime?.listTaskRecords().find((record) => record.runId === runId);
2132
+ try {
2133
+ assertHistoryOwnerMatchesRegistration(readCodexNativeSubagentHistoryOwner(task?.detail), state.historyOwner, child.nativeParentThreadId, true);
2134
+ } catch {
2135
+ continue;
2136
+ }
2137
+ if (!this.claim(deliveryParent, child)) continue;
2138
+ }
2139
+ child.nativeCompletionDelivered = true;
2140
+ if (child.pendingCompletion && !child.deliveringCompletion) this.finish(deliveryParent, child);
2141
+ }
2142
+ }
2143
+ deliverDetached(state, children) {
2144
+ for (const child of children) if (child.parentThreadId === state.parentThreadId && child.pendingCompletion) this.deliverPending(state, child);
2145
+ }
2146
+ release(childState) {
2147
+ if (childState.completionDeliveryTimer) clearTimeout(childState.completionDeliveryTimer);
2148
+ const deliveryOwnerKey = childState.deliveryOwnerKey;
2149
+ if (deliveryOwnerKey && completionDeliveryOwners.get(deliveryOwnerKey) === childState) completionDeliveryOwners.delete(deliveryOwnerKey);
2150
+ childState.deliveryOwnerKey = void 0;
2151
+ }
2152
+ persistPending(state, child) {
2153
+ const completion = child.pendingCompletion;
2154
+ if (!completion) return false;
2155
+ const runId = child.runId;
2156
+ if (!this.claim(state, child)) {
2157
+ this.dependencies.unregisterChild(child);
2158
+ return false;
2159
+ }
2160
+ if (child.completionTaskPhase === "finalize") {
2161
+ const eventAt = completion.completedAt ?? this.dependencies.now();
2162
+ const currentRecord = state.taskRuntime?.listTaskRecords().find((record) => record.runId === runId);
2163
+ const updated = state.taskRuntime?.finalizeTaskRunByRunId({
2164
+ runId,
2165
+ status: completion.status,
2166
+ endedAt: eventAt,
2167
+ lastEventAt: eventAt,
2168
+ ...completion.status === "succeeded" ? {} : { error: completion.result },
2169
+ progressSummary: completion.result,
2170
+ terminalSummary: completion.result,
2171
+ ...child.nativeTurnId ? { detail: {
2172
+ ...isJsonObject(currentRecord?.detail) ? currentRecord.detail : {},
2173
+ nativeTurnId: child.nativeTurnId
2174
+ } } : {}
2175
+ });
2176
+ if (state.taskRuntime && !updated?.some((task) => task.runId === runId && (!child.completionTaskId || task.taskId === child.completionTaskId))) {
2177
+ const current = state.taskRuntime.listTaskRecords().find((task) => task.runId === runId);
2178
+ if (!current || current.status !== completion.status && current.status !== "queued" && current.status !== "running") {
2179
+ this.dependencies.unregisterChild(child);
2180
+ return false;
2181
+ }
2182
+ throw new Error("Codex native subagent task finalization was not persisted.");
2183
+ }
2184
+ child.completionTaskPhase = "delivery";
2185
+ }
2186
+ if (!state.requesterSessionKey || !state.taskRuntimeScope) {
2187
+ this.dependencies.unregisterChild(child);
2188
+ return false;
2189
+ }
2190
+ if (child.completionTaskPhase === "delivery") {
2191
+ const updated = state.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2192
+ runId,
2193
+ deliveryStatus: child.nativeCompletionDelivered ? "delivered" : "pending"
2194
+ });
2195
+ if (state.taskRuntime && !updated?.some((task) => task.runId === runId && (!child.completionTaskId || task.taskId === child.completionTaskId))) {
2196
+ if (!state.taskRuntime.listTaskRecords().some((task) => task.runId === runId)) {
2197
+ this.dependencies.unregisterChild(child);
2198
+ return false;
2199
+ }
2200
+ throw new Error("Codex native subagent task delivery status was not persisted.");
2201
+ }
2202
+ child.completionTaskPhase = void 0;
2203
+ child.completionDeliveryAttempt = 0;
2204
+ }
2205
+ if (child.nativeCompletionDelivered) {
2206
+ child.pendingCompletion = void 0;
2207
+ this.dependencies.unregisterChild(child);
2208
+ return false;
2209
+ }
2210
+ this.dependencies.releaseClientRetentionIfIdle();
2211
+ return true;
2212
+ }
2213
+ scheduleRetry(childState, error, chargeAttempt = true) {
2214
+ if (!childState.pendingCompletion || childState.completionDeliveryTimer || !this.dependencies.isCurrentChild(childState)) return;
2215
+ if (chargeAttempt && !childState.completionTaskPhase && childState.completionDeliveryAttempt >= this.maxRetries) {
2216
+ this.dependencies.getParent(childState.parentThreadId)?.taskRuntime?.setDetachedTaskDeliveryStatusByRunId({
2217
+ runId: childState.runId,
2218
+ deliveryStatus: "failed",
2219
+ error
2220
+ });
2221
+ this.dependencies.unregisterChild(childState);
2222
+ return;
2223
+ }
2224
+ const delayMs = delayForAttempt(this.retryDelaysMs, chargeAttempt ? childState.completionDeliveryAttempt++ : childState.completionDeliveryAttempt);
2225
+ childState.completionDeliveryTimer = setTimeout(() => {
2226
+ childState.completionDeliveryTimer = void 0;
2227
+ if (!this.dependencies.isCurrentChild(childState)) return;
2228
+ const state = this.dependencies.getParent(childState.parentThreadId);
2229
+ if (state) this.deliverPending(state, childState);
2230
+ }, delayMs);
2231
+ childState.completionDeliveryTimer.unref();
2232
+ }
2233
+ claim(state, childState) {
2234
+ const requesterSessionKey = state.requesterSessionKey?.trim();
2235
+ if (!requesterSessionKey) return true;
2236
+ const key = `${requesterSessionKey}\0${childState.runId}`;
2237
+ const runId = childState.runId;
2238
+ const tasks = state.taskRuntime?.listTaskRecords().filter((record) => record.runId === runId) ?? [];
2239
+ const task = tasks[0];
2240
+ if (tasks.length > 1 || childState.completionTaskId && task?.taskId !== childState.completionTaskId) return false;
2241
+ if (task?.deliveryStatus === "delivered") return false;
2242
+ try {
2243
+ assertHistoryOwnerMatchesRegistration(readCodexNativeSubagentHistoryOwner(task?.detail), state.historyOwner, childState.nativeParentThreadId, childState.requiresHistoryOwner === true);
2244
+ } catch (error) {
2245
+ embeddedAgentLog.warn("Holding native completion with unresolved history owner", {
2246
+ childThreadId: childState.childThreadId,
2247
+ error: formatErrorMessage(error)
2248
+ });
2249
+ return false;
2250
+ }
2251
+ const owner = completionDeliveryOwners.get(key);
2252
+ if (owner) return owner === childState;
2253
+ childState.completionTaskId = task?.taskId;
2254
+ completionDeliveryOwners.set(key, childState);
2255
+ childState.deliveryOwnerKey = key;
2256
+ return true;
2257
+ }
2258
+ };
2259
+ //#endregion
2260
+ //#region extensions/codex/src/app-server/native-subagent-notification.ts
2261
+ /**
2262
+ * Extracts native Codex subagent completion notifications from trusted
2263
+ * contextual and inter-agent messages emitted by the app-server.
2264
+ */
2265
+ const CODEX_SUBAGENT_NOTIFICATION_START = "<subagent_notification>";
2266
+ const CODEX_SUBAGENT_NOTIFICATION_END = "</subagent_notification>";
2267
+ /** Extracts trusted subagent completion payloads from a Codex server notification. */
2268
+ function extractCodexNativeSubagentCompletions(notification) {
2269
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2270
+ if (!params) return [];
2271
+ const item = isJsonObject(params.item) ? params.item : void 0;
2272
+ if (!item) return [];
2273
+ if (notification.method === "rawResponseItem/completed" && item.role === "user") return readTrustedContextualCompletions(item);
2274
+ return [];
2275
+ }
2276
+ function readTrustedContextualCompletions(item) {
2277
+ const content = item.content;
2278
+ const metadata = item.internal_chat_message_metadata_passthrough;
2279
+ const kinds = isJsonObject(metadata) ? metadata.content_item_kinds : void 0;
2280
+ if (item.type !== "message" || !Array.isArray(content) || !Array.isArray(kinds) || content.length !== kinds.length) return [];
2281
+ return content.flatMap((entry, index) => {
2282
+ if (kinds[index] !== "multi_agent.subagent_notification" || !isJsonObject(entry) || entry.type !== "input_text") return [];
2283
+ const text = readStringField(entry, "text")?.trim();
2284
+ if (!text?.startsWith(CODEX_SUBAGENT_NOTIFICATION_START) || !text.endsWith(CODEX_SUBAGENT_NOTIFICATION_END)) return [];
2285
+ const completion = parseCodexNativeSubagentNotificationBody(text.slice(23, -24));
2286
+ return completion ? [completion] : [];
2287
+ });
2288
+ }
2289
+ const codexNativeSubagentNotifications = {
2290
+ fromNotification: extractCodexNativeSubagentCompletions,
2291
+ deliveredAgentPaths: readDeliveredNativeCompletionPaths
2292
+ };
2293
+ /** Reads native delivery receipts, leaving status and result ownership with the child lifecycle. */
2294
+ function readDeliveredNativeCompletionPaths(notification) {
2295
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2296
+ const item = isJsonObject(params?.item) ? params.item : void 0;
2297
+ if (notification.method === "item/completed" && item?.type === "collabAgentToolCall" && item.tool === "wait" && (item.status === "completed" || item.status === "failed") && item.senderThreadId === params?.threadId && Array.isArray(item.receiverThreadIds) && isJsonObject(item.agentsStates)) {
2298
+ const receivers = new Set(item.receiverThreadIds);
2299
+ return Object.entries(item.agentsStates).flatMap(([threadId, state]) => receivers.has(threadId) && isJsonObject(state) && [
2300
+ "completed",
2301
+ "errored",
2302
+ "shutdown",
2303
+ "notFound"
2304
+ ].includes(readStringField(state, "status") ?? "") ? [threadId] : []);
2305
+ }
2306
+ if (notification.method !== "rawResponseItem/completed") return [];
2307
+ if (!item || readStringField(item, "type") !== "agent_message") return extractCodexNativeSubagentCompletions(notification).map((completion) => completion.agentPath);
2308
+ const author = readStringField(item, "author");
2309
+ const recipient = readStringField(item, "recipient");
2310
+ const content = item.content;
2311
+ if (!author || !recipient || !Array.isArray(content) || content.length !== 1) return [];
2312
+ const part = content[0];
2313
+ if (!isJsonObject(part) || readStringField(part, "type") !== "input_text") return [];
2314
+ return readStringField(part, "text")?.startsWith(`Message Type: FINAL_ANSWER\nTask name: ${recipient}\nSender: ${author}\nPayload:\n`) ? [author] : [];
2315
+ }
2316
+ function parseCodexNativeSubagentNotificationBody(body) {
2317
+ let payload;
2318
+ try {
2319
+ payload = JSON.parse(body.trim());
2320
+ } catch {
2321
+ return;
2322
+ }
2323
+ if (!isJsonObject(payload)) return;
2324
+ const agentPath = readStringField(payload, "agent_path")?.trim();
2325
+ const completion = readCompletionStatus(payload.status);
2326
+ return agentPath && completion ? {
2327
+ agentPath,
2328
+ ...completion
2329
+ } : void 0;
2330
+ }
2331
+ function readCompletionStatus(status) {
2332
+ if (status === "shutdown" || status === "not_found") return {
2333
+ status: status === "shutdown" ? "cancelled" : "failed",
2334
+ statusLabel: status,
2335
+ result: "(no output)"
2336
+ };
2337
+ if (!isJsonObject(status)) return;
2338
+ const completed = status.completed;
2339
+ if (completed === null || typeof completed === "string") {
2340
+ const result = completed?.trim();
2341
+ return {
2342
+ status: "succeeded",
2343
+ statusLabel: result ? "completed" : "completed_without_final_message",
2344
+ result: result || "Subagent completed without a final assistant message."
2345
+ };
2346
+ }
2347
+ const error = readStringField(status, "errored");
2348
+ return error === void 0 ? void 0 : {
2349
+ status: "failed",
2350
+ statusLabel: "errored",
2351
+ result: error.trim() || "(no output)"
2352
+ };
2353
+ }
2354
+ //#endregion
2355
+ //#region extensions/codex/src/app-server/native-subagent-delivery-receipts.ts
2356
+ function buildCodexNativeSubagentAgentPathKey(parentThreadId, agentPath) {
2357
+ return `${parentThreadId}\0${agentPath}`;
2358
+ }
2359
+ function resolveCodexNativeSubagentReceiptOwner(params) {
2360
+ const { state, childThreadId, known, candidates, isRetiredParent } = params;
2361
+ if (known?.parent === state) return known.deliveryReceipts;
2362
+ for (const candidate of candidates) if (candidate.childThreadId === childThreadId && candidate.parentState.parentThreadId === state.parentThreadId && candidate.requesterSessionKey === state.requesterSessionKey && !isRetiredParent(candidate.parentState)) return candidate.deliveryReceipts;
2363
+ return state.deliveryReceipts;
2364
+ }
2365
+ function registerCodexNativeSubagentReceiptAlias(params) {
2366
+ const { state, childThreadId, agentPath, known, aliases } = params;
2367
+ if (known?.parent !== state) return [];
2368
+ const key = buildCodexNativeSubagentAgentPathKey(state.parentThreadId, agentPath);
2369
+ const existingChild = aliases.get(key);
2370
+ if (existingChild && existingChild !== childThreadId) {
2371
+ embeddedAgentLog.warn("Ignoring conflicting Codex native subagent agent path", {
2372
+ parentThreadId: state.parentThreadId,
2373
+ agentPath,
2374
+ existingChildThreadId: existingChild,
2375
+ attemptedChildThreadId: childThreadId
2376
+ });
2377
+ return [];
2378
+ }
2379
+ aliases.set(key, childThreadId);
2380
+ known.agentPaths.add(agentPath);
2381
+ return known.deliveryReceipts.addAlias(childThreadId, agentPath);
2382
+ }
2383
+ /** Correlates native receipts with immutable assignments while a parent is registered. */
2384
+ var CodexNativeSubagentDeliveryReceipts = class {
2385
+ constructor() {
2386
+ this.seen = /* @__PURE__ */ new Set();
2387
+ this.pending = [];
2388
+ this.outcomes = /* @__PURE__ */ new Map();
2389
+ }
2390
+ observe(notification) {
2391
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
2392
+ const item = isJsonObject(params?.item) ? params.item : void 0;
2393
+ if (!item) return [];
2394
+ const nativeResults = codexNativeSubagentNotifications.fromNotification(notification);
2395
+ for (const agentPath of codexNativeSubagentNotifications.deliveredAgentPaths(notification)) {
2396
+ const id = `${notification.method}:${readStringField(item, "id") ?? JSON.stringify(item)}:${agentPath}`;
2397
+ if (this.seen.has(id)) continue;
2398
+ this.seen.add(id);
2399
+ let result = nativeResults.find((value) => value.agentPath === agentPath)?.result;
2400
+ if (item.type === "agent_message" && Array.isArray(item.content)) {
2401
+ const part = item.content[0];
2402
+ result = (isJsonObject(part) ? readStringField(part, "text") : void 0)?.split("\nPayload:\n").slice(1).join("\nPayload:\n");
2403
+ } else if (isJsonObject(item.agentsStates)) {
2404
+ const child = item.agentsStates[agentPath];
2405
+ result = isJsonObject(child) ? readStringField(child, "message") : result;
2406
+ }
2407
+ this.pending.push({
2408
+ agentPath,
2409
+ result: receiptResultKey(result)
2410
+ });
2411
+ }
2412
+ return this.match();
2413
+ }
2414
+ record(runId, paths, result) {
2415
+ const outcome = this.outcomes.get(runId) ?? {
2416
+ paths: new Set(paths),
2417
+ received: false,
2418
+ receiptResults: /* @__PURE__ */ new Set()
2419
+ };
2420
+ outcome.result = receiptResultKey(result);
2421
+ this.outcomes.set(runId, outcome);
2422
+ const matched = this.match();
2423
+ return outcome.received ? [.../* @__PURE__ */ new Set([...matched, runId])] : matched;
2424
+ }
2425
+ track(runId, paths) {
2426
+ const outcome = this.outcomes.get(runId) ?? {
2427
+ paths: /* @__PURE__ */ new Set(),
2428
+ received: false,
2429
+ receiptResults: /* @__PURE__ */ new Set()
2430
+ };
2431
+ for (const path of paths) outcome.paths.add(path);
2432
+ this.outcomes.set(runId, outcome);
2433
+ const matched = this.match();
2434
+ return outcome.received ? [.../* @__PURE__ */ new Set([...matched, runId])] : matched;
2435
+ }
2436
+ restore(assignments) {
2437
+ const restored = /* @__PURE__ */ new Map();
2438
+ for (const assignment of assignments) {
2439
+ const outcome = this.outcomes.get(assignment.runId) ?? {
2440
+ paths: /* @__PURE__ */ new Set(),
2441
+ received: false,
2442
+ receiptResults: /* @__PURE__ */ new Set()
2443
+ };
2444
+ for (const path of assignment.paths) outcome.paths.add(path);
2445
+ outcome.result ??= receiptResultKey(assignment.result);
2446
+ restored.set(assignment.runId, outcome);
2447
+ }
2448
+ for (const [runId, outcome] of this.outcomes) if (!restored.has(runId)) restored.set(runId, outcome);
2449
+ this.outcomes = restored;
2450
+ const matched = this.match();
2451
+ return [.../* @__PURE__ */ new Set([...matched, ...[...restored].filter(([, value]) => value.received).map(([id]) => id)])];
2452
+ }
2453
+ resumeAssignment(runId, pendingRunIds) {
2454
+ for (const pendingRunId of pendingRunIds) this.outcomes.delete(pendingRunId);
2455
+ return this.track(runId, []);
2456
+ }
2457
+ addAlias(threadId, agentPath) {
2458
+ for (const outcome of this.outcomes.values()) if (outcome.paths.has(threadId)) outcome.paths.add(agentPath);
2459
+ return this.match();
2460
+ }
2461
+ match() {
2462
+ const received = [];
2463
+ for (let index = 0; index < this.pending.length;) {
2464
+ const receipt = this.pending[index];
2465
+ const matches = [...this.outcomes].filter(([, outcome]) => outcome.paths.has(receipt.agentPath));
2466
+ let match;
2467
+ for (const candidate of matches) {
2468
+ const outcome = candidate[1];
2469
+ if (outcome.result !== void 0 && outcome.result === receipt.result || outcome.receiptResults.has(receipt.result)) {
2470
+ match = candidate;
2471
+ break;
2472
+ }
2473
+ if (outcome.result === void 0) {
2474
+ match = matches.length === 1 ? candidate : void 0;
2475
+ break;
2476
+ }
2477
+ }
2478
+ if (!match) {
2479
+ index += 1;
2480
+ continue;
2481
+ }
2482
+ match[1].receiptResults.add(receipt.result);
2483
+ if (!match[1].received) {
2484
+ match[1].received = true;
2485
+ received.push(match[0]);
2486
+ }
2487
+ this.pending.splice(index, 1);
2488
+ }
2489
+ return received;
2490
+ }
2491
+ };
2492
+ function restoreCodexNativeSubagentTaskReceipts(params) {
2493
+ const { state, taskRecords, knownChildren, applyReceipts } = params;
2494
+ const snapshots = /* @__PURE__ */ new Map();
2495
+ for (const task of taskRecords.toReversed().toSorted((a, b) => (a.startedAt ?? a.createdAt) - (b.startedAt ?? b.createdAt))) {
2496
+ if (task.requesterSessionKey !== state.requesterSessionKey) continue;
2497
+ const assignment = readNativeTaskAssignment(task);
2498
+ if (!assignment) continue;
2499
+ const known = knownChildren.get(assignment.childThreadId);
2500
+ const history = readCodexNativeSubagentHistoryOwner(task.detail);
2501
+ if (known && known.parent !== state || (history ? history.parentThreadId !== (known?.nativeParentThreadId ?? state.parentThreadId) : known?.parent !== state)) continue;
2502
+ const receipts = known?.deliveryReceipts ?? state.deliveryReceipts;
2503
+ const snapshot = snapshots.get(receipts) ?? [];
2504
+ snapshot.push({
2505
+ runId: assignment.runId,
2506
+ paths: known?.agentPaths ?? [assignment.childThreadId],
2507
+ ...task.terminalSummary ? { result: task.terminalSummary } : {}
2508
+ });
2509
+ snapshots.set(receipts, snapshot);
2510
+ }
2511
+ for (const [threadId, known] of knownChildren) {
2512
+ if (known.parent !== state || known.pendingTurns.length === 0) continue;
2513
+ const snapshot = snapshots.get(known.deliveryReceipts) ?? [];
2514
+ for (const turn of known.pendingTurns) snapshot.push({
2515
+ runId: codexNativeSubagentRunId(threadId, turn.turnId),
2516
+ paths: known.agentPaths
2517
+ });
2518
+ snapshots.set(known.deliveryReceipts, snapshot);
2519
+ }
2520
+ for (const [receipts, snapshot] of snapshots) applyReceipts(receipts.restore(snapshot));
2521
+ }
2522
+ function observeCodexNativeSubagentDeliveryReceipts(params) {
2523
+ const { state, notification, knownChildren, candidates, isRetiredParent, applyReceipts } = params;
2524
+ const trackers = /* @__PURE__ */ new Set([state.deliveryReceipts]);
2525
+ for (const known of knownChildren) if (known.parent === state) trackers.add(known.deliveryReceipts);
2526
+ for (const candidate of candidates) if (candidate.parentState.parentThreadId === state.parentThreadId && candidate.requesterSessionKey === state.requesterSessionKey && !isRetiredParent(candidate.parentState)) trackers.add(candidate.deliveryReceipts);
2527
+ for (const tracker of trackers) applyReceipts(tracker.observe(notification));
2528
+ }
2529
+ function receiptResultKey(result) {
2530
+ return result?.replace(/\s+/g, " ").trim();
2531
+ }
2532
+ //#endregion
2533
+ //#region extensions/codex/src/app-server/native-subagent-monitor-runtime.ts
2534
+ const defaultNativeSubagentMonitorRuntime = {
2535
+ createAgentHarnessTaskRuntime,
2536
+ deliverAgentHarnessTaskCompletion
2537
+ };
2538
+ function createCodexNativeSubagentMonitorRuntime(Monitor) {
2539
+ const monitors = /* @__PURE__ */ new WeakMap();
2540
+ function registerMonitor(params) {
2541
+ let monitor = monitors.get(params.client);
2542
+ if (!monitor) {
2543
+ const childThreadOwnership = /* @__PURE__ */ new Map();
2544
+ const childThreadTransitions = new KeyedAsyncQueue();
2545
+ const releaseOwnership = async (threadId, ownership) => {
2546
+ if (!ownership) return;
2547
+ await ownership.release(threadId);
2548
+ if (childThreadOwnership.get(threadId) === ownership) childThreadOwnership.delete(threadId);
2549
+ };
2550
+ monitor = new Monitor(params.client, params.runtime ?? defaultNativeSubagentMonitorRuntime, {
2551
+ retainClient: params.retainClient,
2552
+ retainParentThread: params.retainParentThread,
2553
+ hasObservationBacking: (parentThreadId, childThreadId) => hasCodexAppServerLiveThread(params.client, parentThreadId) || hasCodexAppServerLiveThread(params.client, childThreadId),
2554
+ claimChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
2555
+ let ownership;
2556
+ let invalidated = false;
2557
+ ownership = await claimCodexAppServerLiveThread(params.client, threadId, () => {
2558
+ invalidated = true;
2559
+ if (childThreadOwnership.get(threadId) === ownership) childThreadOwnership.delete(threadId);
2560
+ ownership = void 0;
2561
+ });
2562
+ if (ownership && !invalidated) childThreadOwnership.set(threadId, ownership);
2563
+ return invalidated ? void 0 : ownership;
2564
+ }),
2565
+ retainChildThread: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
2566
+ const ownership = childThreadOwnership.get(threadId);
2567
+ if (!ownership) return false;
2568
+ let retained = false;
2569
+ try {
2570
+ retained = await retainCodexAppServerLiveThread(params.client, threadId, ownership.release);
2571
+ return retained;
2572
+ } finally {
2573
+ if (!retained) {
2574
+ await ownership.release(threadId);
2575
+ if (childThreadOwnership.get(threadId) === ownership) childThreadOwnership.delete(threadId);
2576
+ }
2577
+ }
2578
+ }),
2579
+ releaseChildThread: (threadId) => childThreadTransitions.enqueue(threadId, () => releaseOwnership(threadId, childThreadOwnership.get(threadId))),
2580
+ captureChildThreadForget: (threadId) => childThreadTransitions.enqueue(threadId, async () => {
2581
+ return childThreadOwnership.get(threadId)?.forget;
2582
+ })
2583
+ });
2584
+ monitors.set(params.client, monitor);
2585
+ }
2586
+ return monitor.registerParent({
2587
+ parentThreadId: params.parentThreadId,
2588
+ requesterSessionKey: params.requesterSessionKey,
2589
+ taskRuntimeScope: params.taskRuntimeScope,
2590
+ historyOwner: params.historyOwner,
2591
+ submissionStore: params.submissionStore,
2592
+ agentId: params.agentId,
2593
+ claimDirectChild: params.claimDirectChild,
2594
+ rejectPendingDirectChild: params.rejectPendingDirectChild,
2595
+ onDirectChildAccepted: params.onDirectChildAccepted
2596
+ });
2597
+ }
2598
+ return {
2599
+ Monitor,
2600
+ register: registerMonitor,
2601
+ retireParent: (client, parentThreadId) => {
2602
+ monitors.get(client)?.retireParent(parentThreadId);
2603
+ }
2604
+ };
2605
+ }
2606
+ //#endregion
2607
+ //#region extensions/codex/src/app-server/native-subagent-submission-call.ts
2608
+ function hasSubmissionCallCustody(state, call, hasObservationBacking) {
2609
+ return Boolean(call.accepted && call.targets.some(({ childThreadId }) => state.owners.size > 0 || hasObservationBacking?.(state.parentThreadId, childThreadId)));
2610
+ }
2611
+ function captureSubmissionPredecessor(params) {
2612
+ const { state, known, child } = params;
2613
+ if (known?.parent !== state) return;
2614
+ if (!known.assignment.nativeTurnId) return child && child.runId === known.assignment.runId && !child.terminal && !known.assignment.terminal && !known.assignment.unanchored ? { child } : void 0;
2615
+ return {
2616
+ runId: known.assignment.runId,
2617
+ nativeTurnId: known.assignment.nativeTurnId,
2618
+ terminal: known.assignment.terminal || child?.nativeTurnState === "completed" || child?.nativeTurnState === "failed"
2619
+ };
2620
+ }
2621
+ function observeSubmissionPredecessor(params) {
2622
+ const { state, call, threadId, turn, known } = params;
2623
+ const submissionId = call.submissionId;
2624
+ if (!call.accepted || !submissionId) return;
2625
+ call.targets = call.targets.filter(({ childThreadId, predecessor }) => {
2626
+ if (childThreadId !== threadId || !("child" in predecessor)) return true;
2627
+ const child = predecessor.child;
2628
+ if (known?.parent !== state || known.assignment.runId !== child.runId || state.owners.size === 0 && !params.hasObservationBacking?.(state.parentThreadId, threadId)) return false;
2629
+ const nativeTurnId = child.nativeTurnId;
2630
+ if (!nativeTurnId || nativeTurnId === submissionId || known.assignment.nativeTurnId !== nativeTurnId) return true;
2631
+ const nativeState = (readStringField(turn, "id") === nativeTurnId ? readNativeTurnEnd(turn) : void 0) ?? child.nativeTurnState;
2632
+ const owner = call.owner && [...state.owners.values()].includes(call.owner) ? call.owner : void 0;
2633
+ if (nativeState === "interrupted") {
2634
+ if (owner) params.acceptContinuation(owner);
2635
+ return false;
2636
+ }
2637
+ if (!known.assignment.terminal && nativeState !== "completed" && nativeState !== "failed") return true;
2638
+ params.capture({
2639
+ parentTurnId: call.parentTurnId,
2640
+ callId: call.callId,
2641
+ childThreadId,
2642
+ submissionId,
2643
+ predecessorRunId: child.runId,
2644
+ predecessorNativeTurnId: nativeTurnId
2645
+ }, owner);
2646
+ return false;
2647
+ });
2648
+ if (call.targets.length === 0) call.owner = void 0;
2649
+ }
2650
+ //#endregion
2651
+ //#region extensions/codex/src/app-server/native-subagent-submission-owner.ts
2652
+ /** Captures successful native submissions before their turn notifications arrive. */
2653
+ var CodexNativeSubagentSubmissionOwner = class {
2654
+ constructor(dependencies) {
2655
+ this.dependencies = dependencies;
2656
+ this.calls = /* @__PURE__ */ new Map();
2657
+ this.pending = /* @__PURE__ */ new Map();
2658
+ this.writes = /* @__PURE__ */ new Map();
2659
+ this.disposed = false;
2660
+ this.pollDelays = dependencies.recoveryPollDelaysMs ?? DEFAULT_RECOVERY_POLL_DELAYS_MS;
2661
+ }
2662
+ isCurrent(state) {
2663
+ if (this.dependencies.isCurrent(state)) return true;
2664
+ this.retire(state);
2665
+ this.dependencies.onSettled(state);
2666
+ return false;
2667
+ }
2668
+ nativeParentThreadId(state, childThreadId) {
2669
+ const known = this.dependencies.knownChildren.get(childThreadId);
2670
+ return known?.parent === state ? known.nativeParentThreadId : state.parentThreadId;
2671
+ }
2672
+ isObserving(state, custody) {
2673
+ if (this.disposed || custody.phase === "promoting" || custody.phase === "settled" || !this.isCurrent(state)) return false;
2674
+ if (custody.phase === "detached" && !this.dependencies.hasObservationBacking?.(state.parentThreadId, custody.receipt.childThreadId)) {
2675
+ this.finishCustody(state, custody);
2676
+ return false;
2677
+ }
2678
+ return true;
2679
+ }
2680
+ observedTurn(state, threadId, turnId) {
2681
+ const known = this.dependencies.knownChildren.get(threadId);
2682
+ if (known?.parent !== state) return;
2683
+ const pending = known.pendingTurns.find((turn) => turn.turnId === turnId);
2684
+ const child = this.dependencies.currentChild(threadId);
2685
+ const status = pending?.state ?? (child?.nativeTurnId === turnId ? child.nativeTurnState : void 0);
2686
+ return status ? {
2687
+ id: turnId,
2688
+ status: status === "active" ? "inProgress" : status
2689
+ } : void 0;
2690
+ }
2691
+ observeCall(state, turnId, item) {
2692
+ const callId = readStringField(item, "id");
2693
+ if (!turnId || !callId || !this.isCurrent(state)) return;
2694
+ const owner = this.dependencies.parentOwner(state, turnId);
2695
+ if (!owner && ![...state.owners.values()].some((candidate) => !candidate.turnId)) return;
2696
+ const calls = this.calls.get(state) ?? /* @__PURE__ */ new Map();
2697
+ const key = `${turnId}\0${callId}`;
2698
+ if (calls.has(key) || !owner && calls.size >= 32) return;
2699
+ const targets = (Array.isArray(item.receiverThreadIds) ? item.receiverThreadIds : []).flatMap((id) => {
2700
+ if (typeof id !== "string") return [];
2701
+ this.dependencies.prepareReceiver(state, id);
2702
+ const predecessor = captureSubmissionPredecessor({
2703
+ state,
2704
+ known: this.dependencies.knownChildren.get(id),
2705
+ child: this.dependencies.currentChild(id)
2706
+ });
2707
+ return predecessor ? [{
2708
+ childThreadId: id,
2709
+ predecessor
2710
+ }] : [];
2711
+ });
2712
+ if (!targets.length) return;
2713
+ calls.set(key, {
2714
+ parentTurnId: turnId,
2715
+ callId,
2716
+ targets,
2717
+ owner
2718
+ });
2719
+ this.calls.set(state, calls);
2720
+ }
2721
+ observeOutput(state, turnId, item) {
2722
+ if (item.type !== "function_call_output" || !turnId) return;
2723
+ const key = `${turnId}\0${readStringField(item, "call_id") ?? ""}`;
2724
+ const call = this.calls.get(state)?.get(key);
2725
+ if (!call || call.closed) return;
2726
+ let output;
2727
+ try {
2728
+ output = JSON.parse(readStringField(item, "output") ?? "");
2729
+ } catch {}
2730
+ const submissionId = isJsonObject(output) ? readStringField(output, "submission_id")?.trim() : void 0;
2731
+ if (!submissionId) {
2732
+ call.closed = true;
2733
+ return;
2734
+ }
2735
+ call.submissionId = submissionId;
2736
+ this.accept(state, call);
2737
+ }
2738
+ bind(state, turnId) {
2739
+ for (const call of this.calls.get(state)?.values() ?? []) if (call.parentTurnId === turnId && call.submissionId) this.accept(state, call);
2740
+ }
2741
+ restore(state) {
2742
+ try {
2743
+ for (const receipt of state.submissionStore?.read() ?? []) this.capture(state, receipt);
2744
+ } catch (error) {
2745
+ embeddedAgentLog.warn("Cannot recover native follow-up submission receipts", { error: formatErrorMessage(error) });
2746
+ }
2747
+ }
2748
+ observeTurn(threadId, turn) {
2749
+ const turnId = readStringField(turn, "id");
2750
+ if (!turnId) return;
2751
+ this.observeKnownChild(threadId, turn);
2752
+ for (const [state, entries] of this.pending) for (const custody of entries.values()) if (custody.receipt.childThreadId === threadId && custody.receipt.submissionId === turnId) this.promote(state, custody, turn);
2753
+ }
2754
+ hasCustody(state) {
2755
+ return Boolean(this.pending.get(state)?.size || this.writes.get(state)?.size) || [...this.calls.get(state)?.values() ?? []].some((call) => hasSubmissionCallCustody(state, call, this.dependencies.hasObservationBacking));
2756
+ }
2757
+ hasChildCustody(state, childThreadId) {
2758
+ return [...this.pending.get(state)?.values() ?? []].some((entry) => entry.receipt.childThreadId === childThreadId) || [...this.calls.get(state)?.values() ?? []].some((call) => call.targets.some((target) => target.childThreadId === childThreadId) && hasSubmissionCallCustody(state, call, this.dependencies.hasObservationBacking));
2759
+ }
2760
+ async drain(state) {
2761
+ const calls = this.calls.get(state);
2762
+ const hasUnboundOwner = [...state.owners.values()].some((owner) => !owner.turnId);
2763
+ for (const [key, call] of calls ?? []) {
2764
+ if (hasSubmissionCallCustody(state, call, this.dependencies.hasObservationBacking)) {
2765
+ if (!call.owner || ![...state.owners.values()].includes(call.owner)) call.owner = void 0;
2766
+ continue;
2767
+ }
2768
+ if (call.owner && ![...state.owners.values()].includes(call.owner) || !this.dependencies.parentOwner(state, call.parentTurnId) && !hasUnboundOwner) calls.delete(key);
2769
+ }
2770
+ if (!calls?.size) this.calls.delete(state);
2771
+ while (this.writes.get(state)?.size) await Promise.allSettled(this.writes.get(state));
2772
+ if (state.owners.size === 0) for (const custody of this.pending.get(state)?.values() ?? []) {
2773
+ if (custody.phase === "captured") {
2774
+ custody.phase = "detached";
2775
+ custody.owner = void 0;
2776
+ custody.release();
2777
+ }
2778
+ this.isObserving(state, custody);
2779
+ }
2780
+ }
2781
+ retire(state) {
2782
+ this.calls.delete(state);
2783
+ for (const custody of this.pending.get(state)?.values() ?? []) {
2784
+ custody.phase = "settled";
2785
+ if (custody.timer) clearTimeout(custody.timer);
2786
+ custody.release();
2787
+ }
2788
+ this.pending.delete(state);
2789
+ }
2790
+ dispose() {
2791
+ this.disposed = true;
2792
+ for (const state of this.pending.keys()) this.retire(state);
2793
+ this.calls.clear();
2794
+ }
2795
+ accept(state, call) {
2796
+ const owner = this.dependencies.parentOwner(state, call.parentTurnId);
2797
+ if (call.closed || !owner || call.owner && owner !== call.owner || !call.submissionId || !this.isCurrent(state)) return;
2798
+ call.closed = true;
2799
+ call.accepted = true;
2800
+ call.owner = owner;
2801
+ const targets = call.targets;
2802
+ call.targets = [];
2803
+ for (const target of targets) {
2804
+ const { childThreadId, predecessor } = target;
2805
+ if ("child" in predecessor) {
2806
+ call.targets.push(target);
2807
+ continue;
2808
+ }
2809
+ if (!predecessor.terminal) {
2810
+ this.dependencies.acceptContinuation(state, owner, childThreadId, call);
2811
+ continue;
2812
+ }
2813
+ this.capture(state, {
2814
+ parentTurnId: call.parentTurnId,
2815
+ callId: call.callId,
2816
+ childThreadId,
2817
+ submissionId: call.submissionId,
2818
+ predecessorRunId: predecessor.runId,
2819
+ predecessorNativeTurnId: predecessor.nativeTurnId
2820
+ }, owner, true);
2821
+ }
2822
+ for (const { childThreadId } of call.targets) this.observeKnownChild(childThreadId);
2823
+ }
2824
+ observeKnownChild(threadId, turn) {
2825
+ for (const [state, calls] of this.calls) {
2826
+ if (!this.isCurrent(state)) continue;
2827
+ for (const call of calls.values()) observeSubmissionPredecessor({
2828
+ state,
2829
+ call,
2830
+ threadId,
2831
+ turn,
2832
+ known: this.dependencies.knownChildren.get(threadId),
2833
+ hasObservationBacking: this.dependencies.hasObservationBacking,
2834
+ acceptContinuation: (owner) => this.dependencies.acceptContinuation(state, owner, threadId, call),
2835
+ capture: (receipt, owner) => this.capture(state, receipt, owner, true)
2836
+ });
2837
+ this.dependencies.onSettled(state);
2838
+ }
2839
+ }
2840
+ capture(state, receipt, owner, persist = false) {
2841
+ if (this.disposed || !this.isCurrent(state)) return;
2842
+ const entries = this.pending.get(state) ?? /* @__PURE__ */ new Map();
2843
+ const key = `${receipt.parentTurnId}\0${receipt.callId}\0${receipt.childThreadId}`;
2844
+ if (entries.has(key)) return;
2845
+ const foreground = state.owners.size > 0;
2846
+ const custody = {
2847
+ receipt: Object.freeze({ ...receipt }),
2848
+ owner,
2849
+ release: foreground ? this.dependencies.retain(state, receipt.childThreadId) : () => {},
2850
+ recorded: Promise.resolve(),
2851
+ phase: foreground ? "captured" : "detached",
2852
+ attempt: 0
2853
+ };
2854
+ entries.set(key, custody);
2855
+ this.pending.set(state, entries);
2856
+ if (persist && state.submissionStore) custody.recorded = this.track(state, (async () => {
2857
+ try {
2858
+ if (!await state.submissionStore.record(receipt, () => this.dependencies.assertPersistenceCurrent(state))) throw new Error("Native submission binding changed before receipt persistence.");
2859
+ } catch (error) {
2860
+ embeddedAgentLog.warn("Accepted native follow-up lost restart protection; retaining local observation", { error: formatErrorMessage(error) });
2861
+ }
2862
+ })());
2863
+ const observed = this.observedTurn(state, receipt.childThreadId, receipt.submissionId);
2864
+ if (observed) this.promote(state, custody, observed);
2865
+ if (custody.phase !== "promoting") this.reconcile(state, custody);
2866
+ }
2867
+ promote(state, custody, turn, historyValidated = false) {
2868
+ if (!this.isObserving(state, custody) || !this.admitTurn(state, custody.receipt, turn, custody.owner, historyValidated)) return;
2869
+ custody.phase = "promoting";
2870
+ if (custody.timer) clearTimeout(custody.timer);
2871
+ const consumed = (async () => {
2872
+ await custody.recorded;
2873
+ if (state.submissionStore) try {
2874
+ await state.submissionStore.consume(custody.receipt, () => this.dependencies.assertPersistenceCurrent(state));
2875
+ } catch (error) {
2876
+ embeddedAgentLog.warn("Native follow-up task is persisted but its receipt remains for reconciliation", { error: formatErrorMessage(error) });
2877
+ }
2878
+ this.finishCustody(state, custody);
2879
+ })();
2880
+ this.track(state, consumed);
2881
+ }
2882
+ async reconcile(state, custody) {
2883
+ if (!this.isObserving(state, custody)) return;
2884
+ try {
2885
+ const turn = await this.readTurn(state, custody);
2886
+ if (turn) this.promote(state, custody, turn, true);
2887
+ } catch (error) {
2888
+ embeddedAgentLog.warn("Failed to reconcile an accepted native follow-up receipt", { error: formatErrorMessage(error) });
2889
+ }
2890
+ if (!this.isObserving(state, custody) || !this.pollDelays.length) return;
2891
+ custody.timer = setTimeout(() => {
2892
+ custody.timer = void 0;
2893
+ this.reconcile(state, custody);
2894
+ }, delayForAttempt(this.pollDelays, custody.attempt++));
2895
+ custody.timer.unref();
2896
+ }
2897
+ async readTurn(state, custody) {
2898
+ const { receipt } = custody;
2899
+ if (!this.dependencies.prepareReceiver(state, receipt.childThreadId)) return;
2900
+ const revision = this.dependencies.recovery.retainThreadStatusRevision(receipt.childThreadId);
2901
+ try {
2902
+ const response = await this.dependencies.client.request("thread/read", {
2903
+ threadId: receipt.childThreadId,
2904
+ includeTurns: true
2905
+ }, { timeoutMs: 3e4 });
2906
+ if (!revision.isCurrent() || !this.isObserving(state, custody)) return;
2907
+ const thread = isJsonObject(response.thread) ? response.thread : void 0;
2908
+ if (readStringField(thread, "id") !== receipt.childThreadId || readThreadParentThreadId(thread) !== this.nativeParentThreadId(state, receipt.childThreadId)) return;
2909
+ const turns = [];
2910
+ for (const turn of Array.isArray(thread?.turns) ? thread.turns : []) if (isJsonObject(turn)) turns.push(turn);
2911
+ const predecessorIndex = turns.findIndex((turn) => readStringField(turn, "id") === receipt.predecessorNativeTurnId);
2912
+ const turnIndex = turns.findIndex((turn) => readStringField(turn, "id") === receipt.submissionId);
2913
+ if (predecessorIndex < 0 || turnIndex <= predecessorIndex || !["completed", "failed"].includes(readStringField(turns[predecessorIndex], "status") ?? "")) return;
2914
+ const previous = this.dependencies.currentChild(receipt.childThreadId);
2915
+ if (previous && !previous.terminal && previous.nativeTurnId !== receipt.submissionId) await this.dependencies.recovery.reconcileRegisteredChild(previous);
2916
+ return this.isObserving(state, custody) ? turns[turnIndex] : void 0;
2917
+ } finally {
2918
+ revision.release();
2919
+ }
2920
+ }
2921
+ finishCustody(state, custody) {
2922
+ custody.phase = "settled";
2923
+ if (custody.timer) clearTimeout(custody.timer);
2924
+ const entries = this.pending.get(state);
2925
+ for (const [key, entry] of entries ?? []) if (entry === custody) entries.delete(key);
2926
+ if (!entries?.size) this.pending.delete(state);
2927
+ custody.release();
2928
+ this.dependencies.onSettled(state);
2929
+ }
2930
+ admitTurn(state, receipt, turn, owner, historyValidated) {
2931
+ if (readStringField(turn, "id") !== receipt.submissionId || this.disposed || !this.isCurrent(state)) return false;
2932
+ const runId = codexNativeSubagentRunId(receipt.childThreadId, receipt.submissionId);
2933
+ const records = state.taskRuntime?.listTaskRecords() ?? [];
2934
+ const existing = records.find((task) => task.runId === runId);
2935
+ if (existing) {
2936
+ const assignment = readNativeTaskAssignment(existing);
2937
+ const history = readCodexNativeSubagentHistoryOwner(existing.detail);
2938
+ if (assignment?.nativeTurnId !== receipt.submissionId || history && history.parentThreadId !== this.nativeParentThreadId(state, receipt.childThreadId)) return false;
2939
+ if ((existing.status === "succeeded" || existing.status === "failed" || existing.status === "cancelled") && existing.deliveryStatus === "delivered") return true;
2940
+ }
2941
+ let known = this.dependencies.knownChildren.get(receipt.childThreadId);
2942
+ if (!known) {
2943
+ if (!historyValidated) return false;
2944
+ this.dependencies.restoreKnownChild(state, {
2945
+ runId: receipt.predecessorRunId,
2946
+ childThreadId: receipt.childThreadId,
2947
+ nativeTurnId: receipt.predecessorNativeTurnId
2948
+ }, records);
2949
+ known = this.dependencies.knownChildren.get(receipt.childThreadId);
2950
+ if (known && known.assignment.runId === receipt.predecessorRunId && !records.some((task) => task.runId === receipt.predecessorRunId)) known.assignment.terminal = true;
2951
+ }
2952
+ if (known?.parent !== state) return false;
2953
+ if (known.assignment.runId !== receipt.predecessorRunId && known.assignment.runId !== runId && !known.pendingTurns.some((pending) => pending.turnId === receipt.submissionId)) return false;
2954
+ if (known.assignment.runId === receipt.predecessorRunId && known.assignment.nativeTurnId !== receipt.predecessorNativeTurnId) return false;
2955
+ const nativeState = readStringField(turn, "status") === "inProgress" ? "active" : readNativeTurnEnd(turn);
2956
+ if (!nativeState) return false;
2957
+ if (known.assignment.runId === runId) {
2958
+ const child = this.dependencies.currentChild(receipt.childThreadId) ?? this.dependencies.registerChild(state, {
2959
+ runId,
2960
+ childThreadId: receipt.childThreadId,
2961
+ nativeTurnId: receipt.submissionId
2962
+ }, { admitAssignment: true });
2963
+ if (!child) return false;
2964
+ child.nativeTurnState = nativeState;
2965
+ } else {
2966
+ let pending = known.pendingTurns.find((candidate) => candidate.turnId === receipt.submissionId);
2967
+ if (!pending) {
2968
+ pending = {
2969
+ turnId: receipt.submissionId,
2970
+ state: nativeState
2971
+ };
2972
+ known.pendingTurns.push(pending);
2973
+ known.observedTurns.set(receipt.submissionId, {});
2974
+ }
2975
+ pending.state = nativeState;
2976
+ pending.admittedSubmission = receipt;
2977
+ if (owner && pending.admittedOwner !== owner && [...state.owners.values()].includes(owner)) {
2978
+ pending.admittedOwner = owner;
2979
+ owner.onDirectChildAccepted?.();
2980
+ }
2981
+ this.dependencies.admitFollowup(known, receipt.childThreadId);
2982
+ }
2983
+ const child = this.dependencies.currentChild(receipt.childThreadId);
2984
+ if (child?.runId !== runId) return false;
2985
+ if (!state.taskRuntime?.listTaskRecords().some((task) => task.runId === runId)) return false;
2986
+ if (nativeState === "active") this.dependencies.resumeChild(child);
2987
+ else if (Array.isArray(turn.items)) this.dependencies.completeChild({
2988
+ method: "turn/completed",
2989
+ params: {
2990
+ threadId: receipt.childThreadId,
2991
+ turn
2992
+ }
2993
+ }, child).catch((error) => logRecoveryFailure(receipt.childThreadId, error));
2994
+ return true;
2995
+ }
2996
+ track(state, operation) {
2997
+ const writes = this.writes.get(state) ?? /* @__PURE__ */ new Set();
2998
+ writes.add(operation);
2999
+ this.writes.set(state, writes);
3000
+ const remove = () => {
3001
+ writes.delete(operation);
3002
+ if (!writes.size) this.writes.delete(state);
3003
+ this.dependencies.onSettled(state);
3004
+ };
3005
+ operation.then(remove, remove);
3006
+ return operation;
3007
+ }
3008
+ };
3009
+ //#endregion
3010
+ //#region extensions/codex/src/app-server/native-subagent-task-mirror.ts
3011
+ /** Projects Codex thread and collab-agent notifications into task lifecycle updates. */
3012
+ var CodexNativeSubagentTaskMirror = class {
3013
+ constructor(params, runtime) {
3014
+ this.params = params;
3015
+ this.runtime = runtime;
3016
+ this.mirrorStateByThreadId = /* @__PURE__ */ new Map();
3017
+ this.terminalRunIds = /* @__PURE__ */ new Set();
3018
+ this.authoritativeRunIds = /* @__PURE__ */ new Set();
3019
+ this.runIdsByThreadId = /* @__PURE__ */ new Map();
3020
+ this.now = params.now ?? Date.now;
3021
+ }
3022
+ markAuthoritativeCompletion(childThreadId, runId = this.runId(childThreadId)) {
3023
+ this.authoritativeRunIds.add(runId);
3024
+ this.terminalRunIds.add(runId);
3025
+ }
3026
+ restoreCurrentTaskRun(threadId, runId) {
3027
+ this.runIdsByThreadId.set(threadId, runId);
3028
+ this.mirrorStateByThreadId.set(threadId, "mirrored");
3029
+ }
3030
+ startFollowupTurn(threadId, turnId, nativeParentThreadId) {
3031
+ const previousRunId = this.runId(threadId);
3032
+ const previous = this.runtime.listTaskRecords().find((task) => task.runId === previousRunId);
3033
+ const runId = codexNativeSubagentRunId(threadId, turnId);
3034
+ this.runIdsByThreadId.set(threadId, runId);
3035
+ this.mirrorStateByThreadId.delete(threadId);
3036
+ this.createRunningTask({
3037
+ threadId,
3038
+ turnId,
3039
+ nativeParentThreadId,
3040
+ label: previous?.label ?? "Subagent",
3041
+ task: previous?.task ?? "Subagent follow-up",
3042
+ startedAt: this.now(),
3043
+ progressSummary: "Subagent started follow-up work."
3044
+ });
3045
+ }
3046
+ recordNativeTurn(runId, turnId) {
3047
+ const task = this.runtime.listTaskRecords().find((record) => record.runId === runId);
3048
+ const detail = isJsonObject(task?.detail) ? task.detail : {};
3049
+ if (!task || detail.nativeTurnId === turnId) return;
3050
+ this.runtime.tryCreateRunningTaskRun({
3051
+ runId,
3052
+ sourceId: task.sourceId,
3053
+ label: task.label,
3054
+ task: task.task,
3055
+ notifyPolicy: task.notifyPolicy,
3056
+ deliveryStatus: task.deliveryStatus,
3057
+ detail: {
3058
+ ...detail,
3059
+ nativeTurnId: turnId
3060
+ }
3061
+ });
3062
+ }
3063
+ runId(threadId) {
3064
+ return this.runIdsByThreadId.get(threadId) ?? codexNativeSubagentRunId(threadId);
3065
+ }
3066
+ handleNotification(notification) {
3067
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
3068
+ if (!params) return;
3069
+ if (notification.method === "thread/started") {
3070
+ this.handleThreadStarted(params);
3071
+ return;
3072
+ }
3073
+ if (notification.method === "thread/status/changed") {
3074
+ this.handleThreadStatusChanged(params);
3075
+ return;
3076
+ }
3077
+ if (notification.method === "item/started" || notification.method === "item/completed") {
3078
+ const item = isJsonObject(params.item) ? params.item : void 0;
3079
+ if (notification.method === "item/completed" && item && readStringField(item, "type") === "subAgentActivity") {
3080
+ this.handleSubagentActivityItem(params);
3081
+ return;
3082
+ }
3083
+ this.handleCollabAgentItem(params);
3084
+ }
3085
+ }
3086
+ handleThreadStarted(params) {
3087
+ const notification = readThreadStartedNotification(params);
3088
+ if (!notification) return;
3089
+ const thread = notification.thread;
3090
+ const spawn = readSubagentThreadSpawnSource(thread.source, this.params.parentThreadId);
3091
+ if (!spawn) return;
3092
+ const threadId = thread.id.trim();
3093
+ const label = normalizeOptionalString(spawn.agent_nickname) ?? normalizeOptionalString(thread.agentNickname) ?? normalizeOptionalString(spawn.agent_role) ?? normalizeOptionalString(thread.agentRole) ?? "Subagent";
3094
+ const task = normalizeOptionalString(thread.preview) ?? `Subagent${label === "Subagent" ? "" : ` ${label}`}`;
3095
+ const createdAt = secondsToMillis(thread.createdAt) ?? this.now();
3096
+ if (!this.createRunningTask({
3097
+ threadId,
3098
+ label,
3099
+ task,
3100
+ startedAt: createdAt,
3101
+ progressSummary: "Subagent started."
3102
+ })) return;
3103
+ this.applyStatus(threadId, thread.status);
3104
+ }
3105
+ handleThreadStatusChanged(params) {
3106
+ const notification = readThreadStatusChangedNotification(params);
3107
+ if (!notification) return;
3108
+ this.applyStatus(notification.threadId, notification.status);
3109
+ }
3110
+ applyStatus(threadId, status) {
3111
+ if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
3112
+ const statusType = status?.type;
3113
+ if (!statusType) return;
3114
+ const runId = this.runId(threadId);
3115
+ if (this.authoritativeRunIds.has(runId)) return;
3116
+ if (this.terminalRunIds.has(runId) && statusType !== "systemError") return;
3117
+ const eventAt = this.now();
3118
+ if (statusType === "active") {
3119
+ this.runtime.recordTaskRunProgressByRunId({
3120
+ runId,
3121
+ lastEventAt: eventAt,
3122
+ progressSummary: "Subagent is active."
3123
+ });
3124
+ return;
3125
+ }
3126
+ if (statusType === "idle") {
3127
+ this.runtime.recordTaskRunProgressByRunId({
3128
+ runId,
3129
+ lastEventAt: eventAt,
3130
+ progressSummary: "Subagent is idle."
3131
+ });
3132
+ return;
3133
+ }
3134
+ if (statusType === "systemError") {
3135
+ this.terminalRunIds.delete(runId);
3136
+ this.runtime.recordTaskRunProgressByRunId({
3137
+ runId,
3138
+ lastEventAt: eventAt,
3139
+ progressSummary: "Subagent hit a system error; awaiting recovery."
3140
+ });
3141
+ return;
3142
+ }
3143
+ if (statusType === "notLoaded") this.runtime.recordTaskRunProgressByRunId({
3144
+ runId,
3145
+ lastEventAt: eventAt,
3146
+ progressSummary: "Subagent is not loaded."
3147
+ });
3148
+ }
3149
+ handleCollabAgentItem(params) {
3150
+ const item = isJsonObject(params.item) ? params.item : void 0;
3151
+ if (!item || readStringField(item, "type") !== "collabAgentToolCall") return;
3152
+ if ((readStringField(item, "senderThreadId") ?? readStringField(params, "threadId")) !== this.params.parentThreadId) return;
3153
+ const isSpawnAgentTool = normalizeToolName(readStringField(item, "tool")) === "spawnagent";
3154
+ const receiverThreadIds = readNativeSubagentThreadIds(item.receiverThreadIds);
3155
+ const agentsStates = readAgentsStates(item.agentsStates);
3156
+ const spawnChildThreadIds = /* @__PURE__ */ new Set([...receiverThreadIds, ...agentsStates.keys()]);
3157
+ if (isSpawnAgentTool) for (const childThreadId of spawnChildThreadIds) this.createTaskFromCollabSpawnItem(childThreadId, item);
3158
+ const toolCallStatus = normalizeCollabToolCallStatus(readStringField(item, "status"));
3159
+ const terminalToolCallThreadIds = /* @__PURE__ */ new Set();
3160
+ if (isSpawnAgentTool && isBlockedOrFailedCollabToolCallStatus(toolCallStatus)) {
3161
+ for (const threadId of spawnChildThreadIds) terminalToolCallThreadIds.add(threadId);
3162
+ for (const threadId of agentsStates.keys()) terminalToolCallThreadIds.add(threadId);
3163
+ }
3164
+ const terminalAgentStateThreadIds = /* @__PURE__ */ new Set();
3165
+ for (const [threadId, state] of agentsStates) {
3166
+ const normalizedStatus = normalizeAgentStateStatus(state.status);
3167
+ if (terminalToolCallThreadIds.has(threadId) && isNonTerminalAgentStateStatus(normalizedStatus)) continue;
3168
+ this.applyCollabAgentStatus(threadId, normalizedStatus, state.message);
3169
+ if (isTerminalAgentStateStatus(normalizedStatus)) terminalAgentStateThreadIds.add(threadId);
3170
+ }
3171
+ if (isBlockedOrFailedCollabToolCallStatus(toolCallStatus)) for (const threadId of terminalToolCallThreadIds) {
3172
+ if (terminalAgentStateThreadIds.has(threadId)) continue;
3173
+ const state = agentsStates.get(threadId);
3174
+ this.applyCollabAgentStatus(threadId, toolCallStatus, state?.message);
3175
+ }
3176
+ }
3177
+ handleSubagentActivityItem(params) {
3178
+ const item = isJsonObject(params.item) ? params.item : void 0;
3179
+ if (!item || readStringField(item, "type") !== "subAgentActivity" || readStringField(params, "threadId") !== this.params.parentThreadId) return;
3180
+ const threadId = normalizeOptionalString(readStringField(item, "agentThreadId"));
3181
+ const kind = normalizeSubagentActivityKind(readStringField(item, "kind"));
3182
+ if (!threadId || !kind) return;
3183
+ if (kind === "started") {
3184
+ this.createTaskFromSubagentActivity(threadId, normalizeOptionalString(readStringField(item, "agentPath")));
3185
+ return;
3186
+ }
3187
+ if (this.mirrorStateByThreadId.get(threadId) !== "mirrored") return;
3188
+ const message = kind === "interacted" ? "Subagent received more input." : "Subagent was interrupted.";
3189
+ this.applyCollabAgentStatus(threadId, kind === "interacted" ? "running" : "interrupted", message);
3190
+ }
3191
+ createTaskFromSubagentActivity(threadId, agentPath) {
3192
+ const eventAt = this.now();
3193
+ this.createRunningTask({
3194
+ threadId,
3195
+ label: "Subagent",
3196
+ task: agentPath ? `Subagent ${agentPath}` : "Subagent",
3197
+ startedAt: eventAt,
3198
+ progressSummary: "Subagent started."
3199
+ });
3200
+ }
3201
+ createTaskFromCollabSpawnItem(threadId, item) {
3202
+ const prompt = normalizeOptionalString(readStringField(item, "prompt"));
3203
+ const createdAt = this.now();
3204
+ this.createRunningTask({
3205
+ threadId,
3206
+ label: "Subagent",
3207
+ task: prompt ?? "Subagent",
3208
+ startedAt: createdAt,
3209
+ progressSummary: "Subagent spawned."
3210
+ });
3211
+ }
3212
+ createRunningTask(params) {
3213
+ const threadId = params.threadId.trim();
3214
+ if (!threadId || this.mirrorStateByThreadId.get(threadId) === "mirrored") return false;
3215
+ this.mirrorStateByThreadId.set(threadId, "mirrored");
3216
+ const runId = this.runId(threadId);
3217
+ const historyOwner = this.params.historyOwner && params.nativeParentThreadId ? {
3218
+ ...this.params.historyOwner,
3219
+ parentThreadId: params.nativeParentThreadId
3220
+ } : this.params.historyOwner;
3221
+ const existing = this.runtime.listTaskRecords().find((task) => task.runId === runId);
3222
+ const stampHistoryOwner = historyOwner && !existing;
3223
+ const detail = {
3224
+ ...isJsonObject(existing?.detail) ? existing.detail : {},
3225
+ ...stampHistoryOwner ? { nativeHistory: { ...historyOwner } } : {},
3226
+ ...params.turnId ? { nativeTurnId: params.turnId } : {}
3227
+ };
3228
+ if (!this.runtime.tryCreateRunningTaskRun({
3229
+ sourceId: runId,
3230
+ agentId: this.params.agentId,
3231
+ runId,
3232
+ label: params.label,
3233
+ task: params.task,
3234
+ notifyPolicy: "silent",
3235
+ deliveryStatus: "not_applicable",
3236
+ preferMetadata: true,
3237
+ startedAt: params.startedAt,
3238
+ lastEventAt: this.now(),
3239
+ progressSummary: params.progressSummary,
3240
+ ...stampHistoryOwner || params.turnId ? { detail } : {}
3241
+ })) {
3242
+ this.mirrorStateByThreadId.set(threadId, "failed");
3243
+ return false;
3244
+ }
3245
+ this.terminalRunIds.delete(runId);
3246
+ this.authoritativeRunIds.delete(runId);
3247
+ return true;
3248
+ }
3249
+ applyCollabAgentStatus(threadId, status, message) {
3250
+ if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
3251
+ const normalizedStatus = normalizeAgentStateStatus(status);
3252
+ if (!normalizedStatus) return;
3253
+ const runId = this.runId(threadId);
3254
+ if (this.authoritativeRunIds.has(runId)) return;
3255
+ if (this.terminalRunIds.has(runId) && isNonTerminalAgentStateStatus(normalizedStatus)) return;
3256
+ const eventAt = this.now();
3257
+ if (isNonTerminalAgentStateStatus(normalizedStatus)) {
3258
+ this.runtime.recordTaskRunProgressByRunId({
3259
+ runId,
3260
+ lastEventAt: eventAt,
3261
+ progressSummary: normalizeOptionalString(message) ?? (normalizedStatus === "pendingInit" ? "Subagent is initializing." : normalizedStatus === "interrupted" ? "Subagent was interrupted." : "Subagent is running.")
3262
+ });
3263
+ return;
3264
+ }
3265
+ if (normalizedStatus === "completed") {
3266
+ this.terminalRunIds.add(runId);
3267
+ const summary = normalizeOptionalString(message) ?? "Subagent completed.";
3268
+ this.runtime.recordTaskRunProgressByRunId({
3269
+ runId,
3270
+ lastEventAt: eventAt,
3271
+ progressSummary: summary
3272
+ });
3273
+ return;
3274
+ }
3275
+ if (normalizedStatus === "blocked") {
3276
+ this.terminalRunIds.add(runId);
3277
+ this.runtime.finalizeTaskRunByRunId({
3278
+ runId,
3279
+ status: "succeeded",
3280
+ endedAt: eventAt,
3281
+ lastEventAt: eventAt,
3282
+ progressSummary: normalizeOptionalString(message) ?? "Subagent blocked.",
3283
+ terminalSummary: normalizeOptionalString(message) ?? "Subagent blocked.",
3284
+ terminalOutcome: "blocked"
3285
+ });
3286
+ return;
3287
+ }
3288
+ this.terminalRunIds.add(runId);
3289
+ this.runtime.finalizeTaskRunByRunId({
3290
+ runId,
3291
+ status: normalizedStatus === "shutdown" ? "cancelled" : "failed",
3292
+ endedAt: eventAt,
3293
+ lastEventAt: eventAt,
3294
+ error: normalizeOptionalString(message) ?? `Subagent status: ${normalizedStatus}`,
3295
+ progressSummary: normalizeOptionalString(message) ?? `Subagent ${normalizedStatus}.`,
3296
+ terminalSummary: normalizeOptionalString(message) ?? "Subagent did not complete."
3297
+ });
3298
+ }
3299
+ };
3300
+ /** Reads a subagent thread-spawn source only when it belongs to the expected parent thread. */
3301
+ function readSubagentThreadSpawnSource(source, parentThreadId) {
3302
+ if (!source || typeof source !== "object" || !("subAgent" in source)) return;
3303
+ const subAgent = source.subAgent;
3304
+ if (!subAgent || typeof subAgent !== "object" || !("thread_spawn" in subAgent)) return;
3305
+ const spawn = subAgent.thread_spawn;
3306
+ if (!spawn || typeof spawn !== "object") return;
3307
+ return spawn.parent_thread_id === parentThreadId ? spawn : void 0;
3308
+ }
3309
+ function readThreadStartedNotification(params) {
3310
+ const thread = params.thread;
3311
+ if (!isJsonObject(thread) || typeof thread.id !== "string") return;
3312
+ return { thread };
3313
+ }
3314
+ function readThreadStatusChangedNotification(params) {
3315
+ if (typeof params.threadId !== "string") return;
3316
+ const status = params.status;
3317
+ if (!isJsonObject(status) || !isCodexThreadStatusType(status.type)) return;
3318
+ return {
3319
+ threadId: params.threadId,
3320
+ status
3321
+ };
3322
+ }
3323
+ function isCodexThreadStatusType(value) {
3324
+ return value === "notLoaded" || value === "idle" || value === "systemError" || value === "active";
3325
+ }
3326
+ function readAgentsStates(value) {
3327
+ const states = /* @__PURE__ */ new Map();
3328
+ if (!isJsonObject(value)) return states;
3329
+ for (const [threadId, rawState] of Object.entries(value)) {
3330
+ if (!isJsonObject(rawState)) continue;
3331
+ const status = readStringField(rawState, "status");
3332
+ const message = readNullableString(rawState, "message");
3333
+ states.set(threadId, {
3334
+ status,
3335
+ message
3336
+ });
3337
+ }
3338
+ return states;
3339
+ }
3340
+ function readNullableString(value, key) {
3341
+ const entry = value[key];
3342
+ return typeof entry === "string" || entry === null ? entry : void 0;
3343
+ }
3344
+ function normalizeToolName(value) {
3345
+ return value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
3346
+ }
3347
+ function normalizeSubagentActivityKind(value) {
3348
+ const key = value?.replace(/[^a-z]/giu, "").toLowerCase();
3349
+ return key === "started" || key === "interacted" || key === "interrupted" ? key : void 0;
3350
+ }
3351
+ function normalizeCollabToolCallStatus(value) {
3352
+ const key = value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
3353
+ if (key === "completed" || key === "succeeded" || key === "success") return "completed";
3354
+ if (key === "failed" || key === "error" || key === "errored") return "failed";
3355
+ if (key === "blocked" || key === "declined") return "blocked";
3356
+ if (key === "inprogress" || key === "running") return "running";
3357
+ return value?.trim();
3358
+ }
3359
+ function isBlockedOrFailedCollabToolCallStatus(value) {
3360
+ return value === "failed" || value === "blocked";
3361
+ }
3362
+ function isNonTerminalAgentStateStatus(value) {
3363
+ return value === "pendingInit" || value === "running" || value === "interrupted";
3364
+ }
3365
+ function isTerminalAgentStateStatus(value) {
3366
+ return value !== void 0 && !isNonTerminalAgentStateStatus(value);
3367
+ }
3368
+ function normalizeAgentStateStatus(value) {
3369
+ const key = value?.replace(/[^a-z0-9]/giu, "").toLowerCase();
3370
+ if (!key) return;
3371
+ if (key === "pendinginit") return "pendingInit";
3372
+ if (key === "inprogress" || key === "running") return "running";
3373
+ if (key === "completed" || key === "succeeded" || key === "success") return "completed";
3374
+ if (key === "interrupted" || key === "cancelled" || key === "canceled" || key === "shutdown") return key === "shutdown" ? "shutdown" : "interrupted";
3375
+ if (key === "failed" || key === "error" || key === "systemerror") return "failed";
3376
+ if (key === "blocked" || key === "declined") return "blocked";
3377
+ return value?.trim();
3378
+ }
3379
+ function secondsToMillis(value) {
3380
+ if (typeof value !== "number" || !Number.isFinite(value)) return;
3381
+ return value * 1e3;
3382
+ }
3383
+ //#endregion
3384
+ //#region extensions/codex/src/app-server/native-subagent-turn-observation.ts
3385
+ var CodexNativeSubagentTurnObservation = class {
3386
+ constructor(callbacks) {
3387
+ this.callbacks = callbacks;
3388
+ this.observationSourceId = randomUUID();
3389
+ this.projectedActivityWaits = /* @__PURE__ */ new WeakSet();
3390
+ }
3391
+ invalidate(childState) {
3392
+ this.projectedActivityWaits.delete(childState);
3393
+ if (!childState.terminal && childState.activityObserved) emitAgentEvent({
3394
+ runId: childState.runId,
3395
+ ...childState.agentId ? { agentId: childState.agentId } : {},
3396
+ stream: "execution",
3397
+ data: {
3398
+ state: "unknown",
3399
+ sourceId: this.observationSourceId,
3400
+ invalidate: true
3401
+ }
3402
+ });
3403
+ }
3404
+ markActivityUnknown(childState) {
3405
+ this.projectedActivityWaits.delete(childState);
3406
+ childState.activityObserved = true;
3407
+ emitAgentEvent({
3408
+ runId: childState.runId,
3409
+ ...childState.agentId ? { agentId: childState.agentId } : {},
3410
+ stream: "execution",
3411
+ data: {
3412
+ state: "unknown",
3413
+ sourceId: this.observationSourceId,
3414
+ executionId: childState.nativeTurnId
3415
+ }
3416
+ });
3417
+ }
3418
+ refreshWaitDependency(childState, receiverThreadId) {
3419
+ const activityWait = childState.activityWait;
3420
+ if (childState.terminal || childState.nativeTurnState && childState.nativeTurnState !== "active" || this.callbacks.currentChild(childState.childThreadId) !== childState || activityWait?.wait.kind !== "children") return;
3421
+ const runId = this.callbacks.dependencyRunId(childState.parentThreadId, receiverThreadId);
3422
+ if (!runId) return;
3423
+ let changed = false;
3424
+ const dependencies = activityWait.wait.dependencies?.map((dependency) => {
3425
+ if (dependency.runId === runId || readCodexNativeSubagentRunId(dependency.runId)?.threadId !== receiverThreadId) return dependency;
3426
+ changed = true;
3427
+ return { runId };
3428
+ });
3429
+ if (!changed) return;
3430
+ const wait = {
3431
+ ...activityWait.wait,
3432
+ dependencies
3433
+ };
3434
+ childState.activityWait = {
3435
+ ...activityWait,
3436
+ wait
3437
+ };
3438
+ if (this.projectedActivityWaits.has(childState)) this.observeActivity(childState, "waiting", wait);
3439
+ }
3440
+ observeActivity(childState, state, wait) {
3441
+ if (wait && wait === childState.activityWait?.wait) this.projectedActivityWaits.add(childState);
3442
+ else this.projectedActivityWaits.delete(childState);
3443
+ childState.activityObserved = true;
3444
+ emitAgentEvent({
3445
+ runId: childState.runId,
3446
+ ...childState.agentId ? { agentId: childState.agentId } : {},
3447
+ stream: "execution",
3448
+ data: {
3449
+ state,
3450
+ sourceId: this.observationSourceId,
3451
+ ...childState.nativeTurnId ? { executionId: childState.nativeTurnId } : {},
3452
+ ...wait ? { wait } : {}
3453
+ }
3454
+ });
3455
+ }
3456
+ emitChildTaskActivity(notification, childState) {
3457
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
3458
+ if (!params) return;
3459
+ const owner = {
3460
+ runId: childState.runId,
3461
+ ...childState.agentId ? { agentId: childState.agentId } : {}
3462
+ };
3463
+ const turn = isJsonObject(params.turn) ? params.turn : void 0;
3464
+ const turnId = readStringField(params, "turnId") ?? readStringField(turn, "id");
3465
+ if (notification.method === "turn/started") {
3466
+ childState.nativeTurnId = turnId;
3467
+ childState.nativeTurnState = "active";
3468
+ childState.activityWait = void 0;
3469
+ } else if (turnId && childState.nativeTurnId && turnId !== childState.nativeTurnId) return;
3470
+ else if (turnId && childState.nativeTurnState && childState.nativeTurnState !== "active") return;
3471
+ else if (turnId) childState.nativeTurnId ??= turnId;
3472
+ const observe = (state, wait) => this.observeActivity(childState, state, wait);
3473
+ if (notification.method === "turn/started") {
3474
+ observe("running");
3475
+ return;
3476
+ }
3477
+ if (notification.method === "turn/completed") {
3478
+ childState.nativeTurnState = readNativeTurnEnd(turn);
3479
+ childState.activityWait = void 0;
3480
+ observe("unknown");
3481
+ const current = this.callbacks.onTurnEnded(childState);
3482
+ if (current?.nativeTurnId !== turnId && current?.nativeTurnState === "active") this.emitChildTaskActivity({
3483
+ method: "turn/started",
3484
+ params: {
3485
+ threadId: current.childThreadId,
3486
+ turn: { id: current.nativeTurnId }
3487
+ }
3488
+ }, current);
3489
+ return;
3490
+ }
3491
+ if (notification.method === "thread/status/changed") {
3492
+ const status = isJsonObject(params.status) ? params.status : void 0;
3493
+ if (status?.type === "active") {
3494
+ const flags = Array.isArray(status.activeFlags) ? status.activeFlags : [];
3495
+ const wait = flags.includes("waitingOnApproval") ? { kind: "approval" } : flags.includes("waitingOnUserInput") ? { kind: "user_input" } : childState.activityWait?.wait;
3496
+ observe(wait ? "waiting" : "running", wait);
3497
+ } else if (status?.type === "idle" || status?.type === "notLoaded" || status?.type === "systemError") {
3498
+ childState.activityWait = void 0;
3499
+ observe("unknown");
3500
+ }
3501
+ return;
3502
+ }
3503
+ if (notification.method === "item/agentMessage/delta" || notification.method === "item/reasoning/summaryTextDelta") {
3504
+ const delta = readStringField(params, "delta");
3505
+ if (delta) {
3506
+ if (!childState.activityObserved) observe("running");
3507
+ emitAgentEvent({
3508
+ ...owner,
3509
+ stream: notification.method === "item/agentMessage/delta" ? "assistant" : "thinking",
3510
+ data: { delta }
3511
+ });
3512
+ }
3513
+ return;
3514
+ }
3515
+ if (notification.method !== "item/started" && notification.method !== "item/completed") return;
3516
+ const item = readItem(params.item);
3517
+ if (item?.type === "collabAgentToolCall" && normalizeIdentifier(item.tool ?? void 0) === "wait" && Array.isArray(item.receiverThreadIds)) {
3518
+ if (notification.method === "item/started") {
3519
+ const receivers = [...new Set(item.receiverThreadIds.flatMap((id) => typeof id === "string" && id.trim() ? [id.trim()] : []))];
3520
+ const wait = receivers.length > 0 ? {
3521
+ kind: "children",
3522
+ dependencies: receivers.slice(0, 32).map((id) => ({ runId: this.callbacks.dependencyRunId(childState.parentThreadId, id) ?? codexNativeSubagentRunId(id) })),
3523
+ pendingCount: receivers.length
3524
+ } : { kind: "agent_messages" };
3525
+ childState.activityWait = {
3526
+ itemId: item.id,
3527
+ wait
3528
+ };
3529
+ observe("waiting", wait);
3530
+ } else if (childState.activityWait?.itemId === item.id) {
3531
+ childState.activityWait = void 0;
3532
+ observe("running");
3533
+ }
3534
+ return;
3535
+ }
3536
+ if (item?.type === "agentMessage" && notification.method === "item/completed" && item.text) {
3537
+ if (!childState.activityObserved) observe("running");
3538
+ emitAgentEvent({
3539
+ ...owner,
3540
+ stream: "assistant",
3541
+ data: { text: item.text }
3542
+ });
3543
+ }
3544
+ const projection = projectNormalizedToolItem({
3545
+ phase: notification.method === "item/started" ? "start" : "result",
3546
+ item
3547
+ });
3548
+ if (projection?.event) {
3549
+ if (!childState.activityObserved) observe("running");
3550
+ emitAgentEvent({
3551
+ ...owner,
3552
+ ...projection.event
3553
+ });
3554
+ }
3555
+ }
3556
+ toChildTurnCompletion(childState, turn) {
3557
+ const status = normalizeIdentifier(readStringField(turn, "status"));
3558
+ if (status === "completed") {
3559
+ const result = readLastAgentMessage(turn);
3560
+ return {
3561
+ childThreadId: childState.childThreadId,
3562
+ status: "succeeded",
3563
+ statusLabel: result ? "turn_completed" : "completed_without_final_message",
3564
+ result: result ?? "Subagent completed without a final assistant message."
3565
+ };
3566
+ }
3567
+ if (status === "failed") return {
3568
+ childThreadId: childState.childThreadId,
3569
+ status: "failed",
3570
+ statusLabel: "turn_failed",
3571
+ result: readTurnErrorMessage(turn) ?? "Subagent failed."
3572
+ };
3573
+ }
3574
+ };
3575
+ //#endregion
3576
+ //#region extensions/codex/src/app-server/native-subagent-monitor.ts
3577
+ const NATIVE_SUBAGENT_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
3578
+ "thread/started",
3579
+ "thread/status/changed",
3580
+ "turn/started",
3581
+ "turn/completed",
3582
+ "item/agentMessage/delta",
3583
+ "item/reasoning/summaryTextDelta",
3584
+ "item/started",
3585
+ "item/completed",
3586
+ "rawResponseItem/completed"
3587
+ ]);
3588
+ const RECOVERY_REVISION_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
3589
+ "thread/started",
3590
+ "thread/status/changed",
3591
+ "turn/started",
3592
+ "turn/completed"
3593
+ ]);
3594
+ const MAX_PENDING_CHILD_ADMISSION_EVIDENCE = 32;
3595
+ var Monitor = class {
3596
+ constructor(client, runtime = defaultNativeSubagentMonitorRuntime, options = {}) {
3597
+ this.client = client;
3598
+ this.runtime = runtime;
3599
+ this.parentStates = /* @__PURE__ */ new Map();
3600
+ this.pendingChildAdmissionEvidence = /* @__PURE__ */ new Map();
3601
+ this.retiredParentStates = /* @__PURE__ */ new WeakSet();
3602
+ this.childStates = /* @__PURE__ */ new Map();
3603
+ this.knownChildren = /* @__PURE__ */ new Map();
3604
+ this.childThreadIdsByAgentPath = /* @__PURE__ */ new Map();
3605
+ this.parentThreadRetentions = /* @__PURE__ */ new Map();
3606
+ this.disposed = false;
3607
+ this.now = options.now ?? Date.now;
3608
+ this.retainClient = options.retainClient;
3609
+ this.retainParentThread = options.retainParentThread;
3610
+ this.claimChildThread = options.claimChildThread;
3611
+ this.retainChildThread = options.retainChildThread;
3612
+ this.releaseChildThread = options.releaseChildThread;
3613
+ this.childCloses = new CodexNativeSubagentCloseOwner(client, {
3614
+ isParentCurrent: (state) => !this.disposed && !this.retiredParentStates.has(state) && this.parentStates.get(state.parentThreadId) === state,
3615
+ isParentRetired: (state) => this.retiredParentStates.has(state),
3616
+ knownChild: (id) => this.knownChildren.get(id),
3617
+ currentChild: (id) => this.currentChild(id),
3618
+ captureForget: options.captureChildThreadForget,
3619
+ releaseDirectChild: (child) => this.releaseDirectChild(child),
3620
+ clearRecoveryTimers: (child) => this.recovery.clearRecoveryTimers(child),
3621
+ markTerminalRevision: (id) => this.recovery.markTerminalRevision(id),
3622
+ unregisterChild: (child) => this.unregisterChild(child, { retainSubscription: false }),
3623
+ releaseClientRetentionIfIdle: () => this.releaseClientRetentionIfIdle(),
3624
+ now: () => this.now(),
3625
+ pruneParent: (state) => this.pruneParentIfUnused(state)
3626
+ });
3627
+ this.historyRecovery = new CodexNativeSubagentHistoryRecovery(client, {
3628
+ getPendingTurnIds: (id) => this.knownChildren.get(id)?.pendingTurns.map((turn) => turn.turnId) ?? [],
3629
+ getCurrentAssignmentState: (id) => {
3630
+ return {
3631
+ runId: this.knownChildren.get(id)?.assignment.runId,
3632
+ nativeTurnState: this.currentChild(id)?.nativeTurnState
3633
+ };
3634
+ }
3635
+ });
3636
+ this.completionDelivery = new CodexNativeSubagentCompletionDelivery({
3637
+ deliver: (params) => runtime.deliverAgentHarnessTaskCompletion(params),
3638
+ now: this.now,
3639
+ retryDelaysMs: options.completionDeliveryRetryDelaysMs,
3640
+ maxRetries: options.completionDeliveryMaxRetries,
3641
+ isCurrentChild: (child) => this.childStates.get(child.runId) === child,
3642
+ isCurrentParent: (state) => this.parentStates.get(state.parentThreadId) === state,
3643
+ isRetiredParent: (state) => this.retiredParentStates.has(state),
3644
+ getParent: (id) => this.parentStates.get(id),
3645
+ unregisterChild: (child) => this.unregisterChild(child),
3646
+ releaseClientRetentionIfIdle: () => this.releaseClientRetentionIfIdle()
3647
+ });
3648
+ this.turnObservation = new CodexNativeSubagentTurnObservation({
3649
+ currentChild: (id) => this.currentChild(id),
3650
+ dependencyRunId: (parentThreadId, childThreadId) => {
3651
+ const receiver = this.knownChildren.get(childThreadId);
3652
+ return receiver?.parent.parentThreadId === parentThreadId ? receiver.assignment.runId : void 0;
3653
+ },
3654
+ onTurnEnded: (child) => {
3655
+ if (child.nativeTurnState) this.releaseDirectChild(child);
3656
+ const state = this.parentStates.get(child.parentThreadId);
3657
+ return state ? this.recordObservedChildTurn(state, child) : void 0;
3658
+ }
3659
+ });
3660
+ this.recovery = new CodexNativeSubagentRecoveryCoordinator({
3661
+ isDisposed: () => this.disposed,
3662
+ isRegisteredChild: (child) => this.childStates.get(child.runId) === child,
3663
+ currentChild: (id) => this.currentChild(id),
3664
+ parentState: (id) => this.parentStates.get(id),
3665
+ isRetiredParent: (state) => this.retiredParentStates.has(state),
3666
+ reconcileChildState: (child) => this.reconcileChildState(child),
3667
+ reconcileTaskCandidateOnce: (candidate) => this.reconcileTaskCandidateOnce(candidate),
3668
+ processCompletion: (state, child, completion, eventAt) => this.processCompletion(state, child, completion, eventAt),
3669
+ onCandidateSettled: (state) => {
3670
+ this.clearUnconsumablePendingChildAdmissionEvidence();
3671
+ this.pruneParentIfUnused(state);
3672
+ },
3673
+ now: this.now,
3674
+ recoveryPollDelaysMs: options.recoveryPollDelaysMs
3675
+ });
3676
+ this.removeNotificationHandler = client.addNotificationHandler(async (notification) => {
3677
+ if (!NATIVE_SUBAGENT_NOTIFICATION_METHODS.has(notification.method)) return;
3678
+ await this.handleNotification(notification);
3679
+ });
3680
+ this.submissions = new CodexNativeSubagentSubmissionOwner({
3681
+ isCurrent: (state) => {
3682
+ if (this.disposed || this.parentStates.get(state.parentThreadId) !== state || this.retiredParentStates.has(state)) return false;
3683
+ try {
3684
+ state.submissionStore?.assertCurrent();
3685
+ return true;
3686
+ } catch {
3687
+ return false;
3688
+ }
3689
+ },
3690
+ assertPersistenceCurrent: (state) => {
3691
+ if (this.retiredParentStates.has(state) || !this.disposed && this.parentStates.get(state.parentThreadId) !== state) throw new Error("Native submission parent generation is no longer current.");
3692
+ state.submissionStore?.assertCurrent();
3693
+ },
3694
+ parentOwner: (state, turnId) => this.resolveParentOwner(state, turnId),
3695
+ client: this.client,
3696
+ recovery: this.recovery,
3697
+ knownChildren: this.knownChildren,
3698
+ currentChild: (id) => this.currentChild(id),
3699
+ restoreKnownChild: (state, assignment, records) => this.restoreKnownChild(state, assignment, records),
3700
+ prepareReceiver: (state, threadId) => this.prepareReceiverChild(state, threadId),
3701
+ registerChild: (state, assignment, childOptions) => this.registerChildThread(state, assignment, childOptions),
3702
+ admitFollowup: (known, id) => this.admitFollowupChild(known, id),
3703
+ resumeChild: (child) => this.resumeChild(child),
3704
+ completeChild: (notification, child) => this.handleChildTurnCompletion(notification, child),
3705
+ retain: (state, threadId) => {
3706
+ const releases = [
3707
+ this.retainClient?.(),
3708
+ this.retainParentThread?.(state.parentThreadId),
3709
+ this.retainParentThread?.(threadId)
3710
+ ];
3711
+ let retained = true;
3712
+ return () => {
3713
+ if (!retained) return;
3714
+ retained = false;
3715
+ for (const release of releases) release?.();
3716
+ };
3717
+ },
3718
+ hasObservationBacking: options.hasObservationBacking,
3719
+ acceptContinuation: (state, owner, threadId, call) => this.observeParentInteraction(state, owner, threadId, void 0, {
3720
+ parentTurnId: call.parentTurnId,
3721
+ itemId: call.callId
3722
+ }),
3723
+ onSettled: (state) => this.pruneParentIfUnused(state),
3724
+ recoveryPollDelaysMs: options.recoveryPollDelaysMs
3725
+ });
3726
+ this.removeCloseHandler = client.addCloseHandler(() => this.dispose());
3727
+ }
3728
+ dispose() {
3729
+ if (this.disposed) return;
3730
+ this.disposed = true;
3731
+ this.submissions.dispose();
3732
+ this.removeNotificationHandler();
3733
+ this.removeCloseHandler();
3734
+ this.recovery.dispose();
3735
+ for (const childState of this.childStates.values()) {
3736
+ this.turnObservation.invalidate(childState);
3737
+ this.releaseDirectChild(childState);
3738
+ if (childState.terminal && childState.pendingCompletion) {
3739
+ this.recovery.clearRecoveryTimers(childState);
3740
+ continue;
3741
+ }
3742
+ this.unregisterChild(childState);
3743
+ }
3744
+ this.releaseRetainedClient();
3745
+ for (const release of this.parentThreadRetentions.values()) release();
3746
+ this.parentThreadRetentions.clear();
3747
+ for (const state of this.parentStates.values()) {
3748
+ state.owners.clear();
3749
+ state.turnIds.clear();
3750
+ this.childCloses.clear(state);
3751
+ this.completionDelivery.deliverDetached(state, this.childStates.values());
3752
+ }
3753
+ this.pendingChildAdmissionEvidence.clear();
3754
+ for (const [parentThreadId] of this.parentStates) if (![...this.childStates.values()].some((childState) => childState.parentThreadId === parentThreadId)) this.parentStates.delete(parentThreadId);
3755
+ this.knownChildren.clear();
3756
+ this.childThreadIdsByAgentPath.clear();
3757
+ }
3758
+ registerParent(params) {
3759
+ const parentThreadId = params.parentThreadId.trim();
3760
+ if (!parentThreadId) throw new Error("Codex native subagent monitor requires a parent thread id");
3761
+ if (this.disposed) throw new Error("Codex native subagent monitor is closed");
3762
+ let state = this.parentStates.get(parentThreadId);
3763
+ if (state?.requesterSessionKey && params.requesterSessionKey && state.requesterSessionKey !== params.requesterSessionKey) throw new Error(`Codex thread ${parentThreadId} is already bound to another session`);
3764
+ if (!state) {
3765
+ state = {
3766
+ parentThreadId,
3767
+ owners: /* @__PURE__ */ new Map(),
3768
+ turnIds: /* @__PURE__ */ new Set(),
3769
+ deliveryReceipts: new CodexNativeSubagentDeliveryReceipts()
3770
+ };
3771
+ this.parentStates.set(parentThreadId, state);
3772
+ }
3773
+ state.requesterSessionKey ??= params.requesterSessionKey;
3774
+ state.taskRuntimeScope ??= params.taskRuntimeScope;
3775
+ state.historyOwner ??= params.historyOwner;
3776
+ state.submissionStore ??= params.submissionStore;
3777
+ state.agentId ??= params.agentId;
3778
+ const owner = Symbol("codex-native-subagent-owner");
3779
+ state.owners.set(owner, {
3780
+ claimDirectChild: params.claimDirectChild,
3781
+ rejectPendingDirectChild: params.rejectPendingDirectChild,
3782
+ onDirectChildAccepted: params.onDirectChildAccepted
3783
+ });
3784
+ this.prepareParentTaskRuntime(state);
3785
+ for (const childState of this.childStates.values()) if (childState.parentThreadId === parentThreadId && childState.pendingCompletion) this.completionDelivery.deliverPending(state, childState);
3786
+ let registered = true;
3787
+ let settlement;
3788
+ const registeredState = state;
3789
+ this.reconcileTaskRowsForParent(registeredState).catch((error) => {
3790
+ embeddedAgentLog.warn("Failed to reconcile Codex native subagent task rows", {
3791
+ parentThreadId,
3792
+ error: formatErrorMessage(error)
3793
+ });
3794
+ });
3795
+ this.submissions.restore(registeredState);
3796
+ return {
3797
+ bindTurn: (turnIdInput) => {
3798
+ const turnId = turnIdInput.trim();
3799
+ if (!turnId || this.parentStates.get(parentThreadId) !== registeredState) return;
3800
+ const current = registeredState.owners.get(owner);
3801
+ if (!current || [...registeredState.owners.values()].some((other) => other !== current && other.turnId === turnId)) return;
3802
+ current.turnId = turnId;
3803
+ this.submissions.bind(registeredState, turnId);
3804
+ registeredState.turnIds.add(turnId);
3805
+ this.childCloses.bind(registeredState, turnId);
3806
+ this.drainPendingChildAdmissionEvidence(registeredState, current, turnId, true);
3807
+ this.clearUnconsumablePendingChildAdmissionEvidence();
3808
+ },
3809
+ unregister: () => {
3810
+ if (!registered) return settlement ?? Promise.resolve();
3811
+ registered = false;
3812
+ const current = this.parentStates.get(parentThreadId);
3813
+ if (current === registeredState) {
3814
+ const turnId = current.owners.get(owner)?.turnId;
3815
+ current.owners.delete(owner);
3816
+ this.childCloses.prune(current);
3817
+ if (turnId) current.turnIds.delete(turnId);
3818
+ if (current.owners.size === 0) {
3819
+ current.turnIds.clear();
3820
+ current.deliveryReceipts = new CodexNativeSubagentDeliveryReceipts();
3821
+ }
3822
+ this.clearUnconsumablePendingChildAdmissionEvidence();
3823
+ this.completionDelivery.deliverDetached(current, this.childStates.values());
3824
+ this.pruneParentIfUnused(current);
3825
+ }
3826
+ settlement = Promise.allSettled([this.submissions.drain(registeredState), ...this.childCloses.settlements(registeredState)]).then(() => {});
3827
+ return settlement;
3828
+ }
3829
+ };
3830
+ }
3831
+ retireParent(parentThreadIdInput) {
3832
+ const states = this.historyRecovery.parentsForRetirement(parentThreadIdInput.trim(), this.parentStates);
3833
+ for (const state of states) this.retiredParentStates.add(state);
3834
+ for (const state of states) {
3835
+ const parentThreadId = state.parentThreadId;
3836
+ this.submissions.retire(state);
3837
+ state.owners.clear();
3838
+ this.childCloses.clear(state);
3839
+ this.clearPendingChildAdmissionEvidenceForParent(parentThreadId);
3840
+ for (const childState of Array.from(this.childStates.values())) if (childState.parentThreadId === parentThreadId) this.childCloses.retireChild(state, childState, "Subagent parent session ended.");
3841
+ this.pruneParentIfUnused(state);
3842
+ }
3843
+ }
3844
+ prepareParentTaskRuntime(state) {
3845
+ if (!state.requesterSessionKey || !state.taskRuntimeScope) return;
3846
+ state.taskRuntime ??= this.runtime.createAgentHarnessTaskRuntime({
3847
+ runtime: CODEX_NATIVE_SUBAGENT_RUNTIME,
3848
+ taskKind: CODEX_NATIVE_SUBAGENT_TASK_KIND,
3849
+ scope: state.taskRuntimeScope,
3850
+ runIdPrefix: CODEX_NATIVE_SUBAGENT_RUN_ID_PREFIX,
3851
+ executionPid: this.client.getTransportPid()
3852
+ });
3853
+ state.mirror ??= new CodexNativeSubagentTaskMirror({
3854
+ parentThreadId: state.parentThreadId,
3855
+ requesterSessionKey: state.requesterSessionKey,
3856
+ historyOwner: state.historyOwner,
3857
+ agentId: state.agentId
3858
+ }, state.taskRuntime);
3859
+ }
3860
+ /** Handles one notification from the client-wide router observer. */
3861
+ async handleNotification(notification) {
3862
+ if (this.disposed) return;
3863
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
3864
+ this.captureUnregisteredChildTurn(notification, params);
3865
+ if (notification.method === "turn/started" && params && !this.observeNativeChildTurnStart(params)) return;
3866
+ if (notification.method === "turn/completed" && params) {
3867
+ const known = this.knownChildren.get(readStringField(params, "threadId") ?? "");
3868
+ const turn = isJsonObject(params.turn) ? params.turn : void 0;
3869
+ const turnId = readStringField(turn, "id");
3870
+ const pending = known?.pendingTurns.find((candidate) => candidate.turnId === turnId);
3871
+ if (pending) pending.state = readNativeTurnEnd(turn);
3872
+ }
3873
+ if (params && (notification.method === "turn/started" || notification.method === "turn/completed") && isJsonObject(params.turn)) this.submissions.observeTurn(readStringField(params, "threadId") ?? "", params.turn);
3874
+ if (notification.method === "rawResponseItem/completed" && params && isJsonObject(params.item)) {
3875
+ const parent = this.parentStates.get(readStringField(params, "threadId") ?? "");
3876
+ if (parent) this.submissions.observeOutput(parent, readStringField(params, "turnId"), params.item);
3877
+ }
3878
+ const mirrorState = this.resolveMirrorState(notification);
3879
+ const startedThread = isJsonObject(params?.thread) ? params.thread : void 0;
3880
+ const threadId = readStringField(params, "threadId")?.trim() ?? readStringField(startedThread, "id")?.trim();
3881
+ const threadStatus = isJsonObject(params?.status) ? normalizeIdentifier(readStringField(params.status, "type")) : void 0;
3882
+ const parent = threadId ? this.parentStates.get(threadId) : void 0;
3883
+ if (parent && parent.owners.size > 0 && notification.method === "turn/started") {
3884
+ const turnId = isJsonObject(params?.turn) ? readStringField(params.turn, "id") : void 0;
3885
+ if (turnId) parent.turnIds.add(turnId);
3886
+ }
3887
+ const tracksRecoveryRevision = Boolean(threadId && this.recovery.hasRevision(threadId));
3888
+ if (RECOVERY_REVISION_NOTIFICATION_METHODS.has(notification.method) && threadId && tracksRecoveryRevision) this.recovery.observeRevision(threadId);
3889
+ if (!mirrorState && (!threadId || !this.parentStates.has(threadId) && !this.currentChild(threadId) && !tracksRecoveryRevision)) return;
3890
+ const notificationTurnId = readStringField(params, "turnId") ?? (isJsonObject(params?.turn) ? readStringField(params.turn, "id") : void 0);
3891
+ const pendingTurns = threadId ? this.knownChildren.get(threadId)?.pendingTurns : void 0;
3892
+ const pendingNativeTurn = pendingTurns?.some((pending) => !notificationTurnId || pending.turnId === notificationTurnId);
3893
+ if (pendingNativeTurn && threadId) {
3894
+ const previous = this.currentChild(threadId);
3895
+ if (previous) this.recovery.reconcileRegisteredChild(previous).catch((error) => {
3896
+ logRecoveryFailure(threadId, error);
3897
+ this.recovery.scheduleRecoveryPoll(previous);
3898
+ });
3899
+ }
3900
+ const isChildClose = isCodexNativeSubagentCloseNotification(notification);
3901
+ if (mirrorState && isChildClose) await this.childCloses.observe(notification, mirrorState);
3902
+ if (mirrorState?.mirror && !pendingNativeTurn && !isChildClose) try {
3903
+ mirrorState.mirror.handleNotification(notification);
3904
+ } catch (error) {
3905
+ embeddedAgentLog.warn("Failed to mirror Codex native subagent lifecycle event", {
3906
+ method: notification.method,
3907
+ error: formatErrorMessage(error)
3908
+ });
3909
+ }
3910
+ const childState = threadId && !pendingNativeTurn ? this.currentChild(threadId) : void 0;
3911
+ if (notification.method === "turn/started" && childState) {
3912
+ childState.nativeCompletionDelivered = false;
3913
+ this.resumeChild(childState);
3914
+ }
3915
+ if (parent && parent.turnIds.has(readStringField(params, "turnId") ?? "")) observeCodexNativeSubagentDeliveryReceipts({
3916
+ state: parent,
3917
+ notification,
3918
+ knownChildren: this.knownChildren.values(),
3919
+ candidates: this.recovery.allCandidates(),
3920
+ isRetiredParent: (state) => this.retiredParentStates.has(state),
3921
+ applyReceipts: (runIds) => this.applyNativeReceipts(parent, runIds)
3922
+ });
3923
+ if (childState && !childState.terminal && (!pendingTurns?.length || notification.method === "turn/completed")) this.turnObservation.emitChildTaskActivity(notification, childState);
3924
+ if (!pendingNativeTurn) await this.handleChildTurnCompletion(notification, childState);
3925
+ if (!pendingNativeTurn && notification.method === "thread/status/changed" && threadId && threadStatus) {
3926
+ if (threadStatus !== "systemerror") {
3927
+ if (childState) this.recovery.clearSystemErrorFallback(childState);
3928
+ } else {
3929
+ if (childState) {
3930
+ this.resumeChild(childState, { scheduleRecovery: false });
3931
+ this.recovery.setRecoveryFallback(childState, systemErrorFallbackCompletion(childState.childThreadId), this.now());
3932
+ }
3933
+ this.reconcileChildThread(threadId).catch((error) => {
3934
+ logRecoveryFailure(threadId, error);
3935
+ return false;
3936
+ }).then((reconciled) => {
3937
+ if (!reconciled && childState && this.currentChild(threadId) === childState) this.recovery.scheduleRecoveryPoll(childState);
3938
+ });
3939
+ }
3940
+ }
3941
+ await this.handleCompletionNotification(notification);
3942
+ }
3943
+ resumeChild(childState, options = {}) {
3944
+ if (childState.terminal) return;
3945
+ this.observeActiveChild(childState);
3946
+ this.recovery.clearRecoveryTimers(childState);
3947
+ childState.recoveryAttempt = 0;
3948
+ if (options.scheduleRecovery !== false) this.recovery.scheduleRecoveryPoll(childState);
3949
+ }
3950
+ observeActiveChild(childState) {
3951
+ childState.settledWithoutCompletion = false;
3952
+ childState.fallbackCompletion = void 0;
3953
+ this.releaseClientRetention ??= this.retainClient?.();
3954
+ }
3955
+ settleResumableChild(childState) {
3956
+ if (childState.terminal) return;
3957
+ childState.settledWithoutCompletion = true;
3958
+ childState.fallbackCompletion = void 0;
3959
+ this.releaseDirectChild(childState);
3960
+ this.recovery.clearRecoveryTimers(childState);
3961
+ this.releaseClientRetentionIfIdle();
3962
+ }
3963
+ async handleChildTurnCompletion(notification, childState) {
3964
+ if (notification.method !== "turn/completed") return;
3965
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
3966
+ const childThreadId = readStringField(params, "threadId")?.trim();
3967
+ const state = childState ? this.parentStates.get(childState.parentThreadId) : void 0;
3968
+ const turn = isJsonObject(params?.turn) ? params.turn : void 0;
3969
+ if (!state || !childState || childState.childThreadId !== childThreadId || !turn || childState.terminal) return;
3970
+ const turnId = readStringField(turn, "id");
3971
+ if (childState.nativeTurnId && turnId !== childState.nativeTurnId) return;
3972
+ const status = normalizeIdentifier(readStringField(turn, "status"));
3973
+ if (status === "interrupted") {
3974
+ this.removePendingSpawnAdmissionEvidenceForChild(childState.childThreadId);
3975
+ this.rejectPendingDirectChild(state, childState.childThreadId, "Codex child turn interrupted");
3976
+ this.settleResumableChild(childState);
3977
+ return;
3978
+ }
3979
+ if (status === "completed" || status === "failed") {
3980
+ const latestTurnId = this.knownChildren.get(childState.childThreadId)?.turnId;
3981
+ if (!latestTurnId || latestTurnId === turnId) {
3982
+ this.recovery.markTerminalRevision(childState.childThreadId);
3983
+ this.rejectPendingDirectChild(state, childState.childThreadId, "Codex child turn completed");
3984
+ this.removePendingSpawnAdmissionEvidenceForChild(childState.childThreadId);
3985
+ }
3986
+ this.releaseDirectChild(childState);
3987
+ }
3988
+ const completion = this.turnObservation.toChildTurnCompletion(childState, turn);
3989
+ if (!completion) return;
3990
+ await this.processObservedCompletion(state, childState, completion);
3991
+ }
3992
+ /** Reads one child through app-server history and delivers a terminal result when present. */
3993
+ async reconcileChildThread(childThreadIdInput) {
3994
+ const childState = this.currentChild(childThreadIdInput.trim());
3995
+ return childState ? this.recovery.reconcileRegisteredChild(childState) : false;
3996
+ }
3997
+ resolveMirrorState(notification) {
3998
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
3999
+ if (!params) return;
4000
+ if (notification.method === "thread/started") {
4001
+ const thread = isJsonObject(params.thread) ? params.thread : void 0;
4002
+ const parentThreadId = readThreadParentThreadId(thread);
4003
+ const childThreadId = thread ? readStringField(thread, "id")?.trim() : void 0;
4004
+ const agentPath = readStringField(readThreadSpawnSource(thread), "agent_path")?.trim();
4005
+ const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
4006
+ if (state && childThreadId && parentThreadId) return this.registerChildThread(state, childThreadId, agentPath === void 0 ? {} : { agentPath }) ? state : void 0;
4007
+ return state;
4008
+ }
4009
+ if (notification.method === "thread/status/changed" || notification.method === "turn/started" || notification.method === "turn/completed" || notification.method === "item/agentMessage/delta") {
4010
+ const childThreadId = readStringField(params, "threadId")?.trim();
4011
+ const parentThreadId = childThreadId ? this.currentChild(childThreadId)?.parentThreadId : void 0;
4012
+ return parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
4013
+ }
4014
+ if (notification.method === "item/started" || notification.method === "item/completed") {
4015
+ const item = isJsonObject(params.item) ? params.item : void 0;
4016
+ const parentThreadId = item ? (readStringField(item, "senderThreadId") ?? readStringField(params, "threadId"))?.trim() : void 0;
4017
+ const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
4018
+ if (state && parentThreadId) {
4019
+ const turnId = readStringField(params, "turnId");
4020
+ const owner = this.resolveParentOwner(state, turnId);
4021
+ if (notification.method === "item/completed") {
4022
+ if (readStringField(item, "type") === "subAgentActivity" && readStringField(item, "kind") === "interacted") {
4023
+ const childThreadId = readStringField(item, "agentThreadId");
4024
+ if (childThreadId) this.observeParentInteraction(state, owner, childThreadId, readStringField(item, "agentPath"), {
4025
+ parentTurnId: turnId,
4026
+ itemId: readStringField(item, "id")
4027
+ });
4028
+ return state;
4029
+ }
4030
+ if (readStringField(item, "type") === "collabAgentToolCall" && readStringField(item, "tool") === "sendInput" && readStringField(item, "status") === "completed") {
4031
+ this.submissions.observeCall(state, turnId, item);
4032
+ return;
4033
+ }
4034
+ }
4035
+ if (notification.method === "item/completed" && readStringField(item, "type") === "subAgentActivity" && normalizeIdentifier(readStringField(item, "kind")) === "started") {
4036
+ const childThreadId = readStringField(item, "agentThreadId")?.trim();
4037
+ const agentPath = readStringField(item, "agentPath");
4038
+ if (childThreadId) this.registerDirectSpawnChild(state, turnId, {
4039
+ parentThreadId,
4040
+ childThreadId,
4041
+ ...agentPath === void 0 ? {} : { agentPath }
4042
+ }, owner);
4043
+ return state;
4044
+ }
4045
+ const isCompletedSpawnAgentTool = notification.method === "item/completed" && readStringField(item, "type") === "collabAgentToolCall" && normalizeIdentifier(readStringField(item, "tool")) === "spawnagent" && normalizeIdentifier(readStringField(item, "status")) === "completed";
4046
+ if (normalizeIdentifier(readStringField(item, "tool")) === "closeagent") return state;
4047
+ const childThreadIds = new Set(readNativeSubagentThreadIds(item?.receiverThreadIds));
4048
+ let accepted = true;
4049
+ for (const childThreadId of childThreadIds) accepted = Boolean(isCompletedSpawnAgentTool ? this.registerDirectSpawnChild(state, turnId, {
4050
+ parentThreadId,
4051
+ childThreadId
4052
+ }, owner) : this.registerChildThread(state, childThreadId)) && accepted;
4053
+ if (!accepted) return;
4054
+ }
4055
+ return state;
4056
+ }
4057
+ }
4058
+ async handleCompletionNotification(notification) {
4059
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
4060
+ const parentThreadId = params ? readStringField(params, "threadId")?.trim() : void 0;
4061
+ const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
4062
+ if (!state) return;
4063
+ for (const nativeCompletion of codexNativeSubagentNotifications.fromNotification(notification)) {
4064
+ const childThreadId = this.childThreadIdsByAgentPath.get(buildCodexNativeSubagentAgentPathKey(state.parentThreadId, nativeCompletion.agentPath));
4065
+ const childState = childThreadId ? this.currentChild(childThreadId) : void 0;
4066
+ if (!childState || childState.parentThreadId !== state.parentThreadId || childState.terminal || this.knownChildren.get(childState.childThreadId)?.pendingTurns.length || readCodexNativeSubagentRunId(childState.runId)?.turnId) {
4067
+ embeddedAgentLog.warn("Ignoring Codex native subagent completion for unknown child thread", {
4068
+ parentThreadId: state.parentThreadId,
4069
+ agentPath: nativeCompletion.agentPath
4070
+ });
4071
+ continue;
4072
+ }
4073
+ const completion = {
4074
+ childThreadId: childState.childThreadId,
4075
+ status: nativeCompletion.status,
4076
+ statusLabel: nativeCompletion.statusLabel,
4077
+ result: nativeCompletion.result
4078
+ };
4079
+ await this.processObservedCompletion(state, childState, completion);
4080
+ }
4081
+ }
4082
+ async processObservedCompletion(state, childState, completion) {
4083
+ if (!isNoFinalCompletion(completion)) {
4084
+ await this.processCompletion(state, childState, completion);
4085
+ return;
4086
+ }
4087
+ this.resumeChild(childState, { scheduleRecovery: false });
4088
+ this.recovery.setRecoveryFallback(childState, completion, this.now());
4089
+ await this.recovery.reconcileRegisteredChild(childState).catch((error) => {
4090
+ logRecoveryFailure(childState.childThreadId, error);
4091
+ return false;
4092
+ });
4093
+ }
4094
+ async reconcileChildState(childState) {
4095
+ const state = this.parentStates.get(childState.parentThreadId);
4096
+ if (!state) return false;
4097
+ const statusRead = this.recovery.retainThreadStatusRevision(childState.childThreadId);
4098
+ try {
4099
+ const recovery = await this.historyRecovery.read(childState, {
4100
+ resumeInterrupted: !childState.terminal,
4101
+ getTaskRecords: () => this.historyRecovery.selectTaskRecords(state),
4102
+ recordedCompletion: childState.pendingCompletion
4103
+ });
4104
+ if (!statusRead.isCurrent() || this.childStates.get(childState.runId) !== childState) return false;
4105
+ if (recovery.parentThreadId && recovery.parentThreadId !== childState.nativeParentThreadId) {
4106
+ embeddedAgentLog.warn("Codex native subagent parent did not match monitor state", {
4107
+ childThreadId: childState.childThreadId,
4108
+ expectedParentThreadId: childState.nativeParentThreadId,
4109
+ actualParentThreadId: recovery.parentThreadId
4110
+ });
4111
+ this.unregisterChild(childState);
4112
+ return false;
4113
+ }
4114
+ if (recovery.agentPath) this.registerAgentPath(state, childState.childThreadId, recovery.agentPath);
4115
+ this.recordRecoveredChildTurn(state, childState, recovery);
4116
+ if (recovery.threadState === "active") {
4117
+ this.observeActiveChild(childState);
4118
+ return false;
4119
+ }
4120
+ if (recovery.threadState === "other") this.recovery.clearSystemErrorFallback(childState);
4121
+ if (recovery.resumable) {
4122
+ this.settleResumableChild(childState);
4123
+ return false;
4124
+ }
4125
+ const completion = this.processRecoveredCompletion(state, childState, recovery);
4126
+ if (!completion) return false;
4127
+ await completion;
4128
+ return true;
4129
+ } finally {
4130
+ statusRead.release();
4131
+ }
4132
+ }
4133
+ processRecoveredCompletion(state, child, recovery) {
4134
+ const completion = recovery.completion;
4135
+ if (completion && !isNoFinalCompletion(completion)) return this.processCompletion(state, child, completion, completion.completedAt);
4136
+ const fallback = completion ?? recovery.fallbackCompletion;
4137
+ if (fallback) this.recovery.setRecoveryFallback(child, fallback, fallback.completedAt ?? this.now());
4138
+ }
4139
+ recordRecoveredChildTurn(state, child, recovery) {
4140
+ if (recovery.assignmentUnresolved) child.fallbackCompletion = void 0;
4141
+ const known = this.knownChildren.get(child.childThreadId);
4142
+ if (known?.parent === state) for (const observed of recovery.observedPendingTurns) {
4143
+ const pending = known.pendingTurns.find((candidate) => candidate.turnId === observed.turnId);
4144
+ if (pending && observed.state) pending.state = observed.state === "active" && pending !== known.pendingTurns.at(-1) ? void 0 : observed.state;
4145
+ }
4146
+ const turnId = recovery.nativeTurnId;
4147
+ if (!turnId) return;
4148
+ const observedTurn = Boolean(child.nativeTurnId && child.nativeTurnId !== turnId);
4149
+ if (child.nativeTurnId !== turnId) {
4150
+ child.nativeTurnId = turnId;
4151
+ child.nativeTurnState = void 0;
4152
+ child.activityWait = void 0;
4153
+ state.mirror?.recordNativeTurn(child.runId, turnId);
4154
+ }
4155
+ child.nativeTurnState = recovery.nativeTurnState;
4156
+ if (child.nativeTurnState && child.nativeTurnState !== "active") this.releaseDirectChild(child);
4157
+ this.recordObservedChildTurn(state, child, observedTurn);
4158
+ }
4159
+ recordObservedChildTurn(state, child, observedTurn = false) {
4160
+ const known = this.knownChildren.get(child.childThreadId);
4161
+ if (!child.nativeTurnId || known?.parent !== state || known.assignment.runId !== child.runId) return child;
4162
+ known.assignment.nativeTurnId = child.nativeTurnId;
4163
+ known.assignment.unanchored = void 0;
4164
+ if (!known.observedTurns.has(child.nativeTurnId)) known.observedTurns.set(child.nativeTurnId, observedTurn ? { awaitingInteraction: true } : {});
4165
+ if (observedTurn && known.pendingTurns.length === 0) this.associatePendingChildInteraction(known, child.childThreadId, child.nativeTurnId);
4166
+ this.submissions.observeKnownChild(child.childThreadId);
4167
+ return this.admitFollowupChild(known, child.childThreadId);
4168
+ }
4169
+ async processCompletion(state, childState, completion, eventAt = this.now()) {
4170
+ if (childState.terminal) return;
4171
+ childState.terminal = true;
4172
+ const known = this.knownChildren.get(childState.childThreadId);
4173
+ if (known?.assignment.runId === childState.runId) {
4174
+ known.assignment.terminal = true;
4175
+ known.assignment.unanchored = void 0;
4176
+ known.assignment.nativeTurnId = childState.nativeTurnId;
4177
+ }
4178
+ childState.pendingCompletion = {
4179
+ ...completion,
4180
+ completedAt: eventAt
4181
+ };
4182
+ childState.completionTaskPhase = "finalize";
4183
+ this.recovery.markTerminalRevision(childState.childThreadId);
4184
+ this.releaseDirectChild(childState);
4185
+ this.recovery.clearRecoveryTimers(childState);
4186
+ state.mirror?.markAuthoritativeCompletion(completion.childThreadId, childState.runId);
4187
+ this.applyNativeReceipts(state, childState.deliveryReceipts.record(childState.runId, this.knownChildren.get(childState.childThreadId)?.agentPaths ?? [childState.childThreadId], completion.result));
4188
+ await this.completionDelivery.deliverPending(state, childState);
4189
+ }
4190
+ applyNativeReceipts(state, runIds) {
4191
+ this.completionDelivery.applyReceipts(state, runIds, this.childStates);
4192
+ }
4193
+ resolveChildReceiptOwner(state, childThreadId) {
4194
+ return resolveCodexNativeSubagentReceiptOwner({
4195
+ state,
4196
+ childThreadId,
4197
+ known: this.knownChildren.get(childThreadId),
4198
+ candidates: this.recovery.allCandidates(),
4199
+ isRetiredParent: (parent) => this.retiredParentStates.has(parent)
4200
+ });
4201
+ }
4202
+ captureUnregisteredChildTurn(notification, params) {
4203
+ if (notification.method !== "turn/started" && notification.method !== "turn/completed") return;
4204
+ const threadId = readStringField(params, "threadId");
4205
+ const turn = isJsonObject(params?.turn) ? params.turn : void 0;
4206
+ const turnId = readStringField(turn, "id");
4207
+ if (!threadId || !turnId) return;
4208
+ const known = this.knownChildren.get(threadId);
4209
+ if (known && (known.assignment.terminal || known.assignment.nativeTurnId || this.currentChild(threadId))) return;
4210
+ const candidates = this.recovery.observeUnregisteredTurn(threadId, turnId, notification.method === "turn/started", readNativeTurnEnd(turn));
4211
+ for (const candidate of candidates) {
4212
+ const state = this.parentStates.get(candidate.parentState.parentThreadId);
4213
+ if (state) this.associateUnregisteredChildInteractions(state, threadId);
4214
+ }
4215
+ if (known && notification.method === "turn/started") this.applyNativeReceipts(known.parent, known.deliveryReceipts.track(codexNativeSubagentRunId(threadId, turnId), known.agentPaths));
4216
+ }
4217
+ associateUnregisteredChildInteractions(state, threadId) {
4218
+ const known = this.knownChildren.get(threadId);
4219
+ if (known && (known.assignment.terminal || known.assignment.nativeTurnId)) return;
4220
+ const candidate = this.recovery.pendingChildRecoveries(state, threadId)[0];
4221
+ if (!candidate) return;
4222
+ const currentTurnId = candidate.nativeTurnId ?? (!candidate.terminal ? candidate.observedTurns[0]?.turnId : void 0);
4223
+ const interactions = [...this.pendingChildAdmissionEvidence.values()].flat().filter((evidence) => evidence.kind === "interaction" && evidence.parentThreadId === state.parentThreadId && evidence.childThreadId === threadId);
4224
+ for (const turn of candidate.observedTurns) {
4225
+ if (turn.turnId === currentTurnId) continue;
4226
+ const interaction = interactions.find((entry) => entry.kind === "interaction" && entry.nativeTurnId === turn.turnId) ?? interactions.find((entry) => entry.kind === "interaction" && !entry.nativeTurnId);
4227
+ if (interaction?.kind !== "interaction") continue;
4228
+ interaction.nativeTurnId = turn.turnId;
4229
+ if (interaction.owner && [...state.owners.values()].includes(interaction.owner)) interaction.admittedOwner = interaction.owner;
4230
+ }
4231
+ }
4232
+ prepareReceiverChild(state, threadId) {
4233
+ const known = this.knownChildren.get(threadId);
4234
+ if (known?.parent === state) return true;
4235
+ if (this.disposed || this.parentStates.get(state.parentThreadId) !== state || this.retiredParentStates.has(state) || known && (!known.assignment.terminal || known.pendingTurns.length > 0 || this.submissions.hasChildCustody(known.parent, threadId))) return false;
4236
+ const saved = this.historyRecovery.readReceiverTask(state, threadId);
4237
+ if (!saved) return !known;
4238
+ if (!saved.restorable) return false;
4239
+ const nativeParent = this.parentStates.get(saved.nativeParentThreadId);
4240
+ for (const owner of [known?.parent, nativeParent]) if (owner && (this.retiredParentStates.has(owner) || !this.historyRecovery.acceptsParent(owner, state))) return false;
4241
+ try {
4242
+ state.submissionStore?.assertCurrent();
4243
+ } catch {
4244
+ return false;
4245
+ }
4246
+ this.restoreKnownChild(state, saved.assignment, saved.records);
4247
+ this.historyRecovery.retainRecoveryParents([known?.parent, nativeParent], state);
4248
+ restoreCodexNativeSubagentTaskReceipts({
4249
+ state,
4250
+ taskRecords: saved.records,
4251
+ knownChildren: this.knownChildren,
4252
+ applyReceipts: (runIds) => this.applyNativeReceipts(state, runIds)
4253
+ });
4254
+ for (const agentPath of this.knownChildren.get(threadId)?.agentPaths ?? []) this.registerAgentPath(state, threadId, agentPath);
4255
+ return true;
4256
+ }
4257
+ registerChildThread(state, childInput, options = {}) {
4258
+ const parentThreadId = state.parentThreadId;
4259
+ const preparedAssignment = typeof childInput === "string" ? void 0 : childInput;
4260
+ const childThreadId = typeof childInput === "string" ? childInput.trim() : childInput.childThreadId;
4261
+ if (!parentThreadId || !childThreadId || this.disposed) return;
4262
+ const claimDirectChild = options.directOwner?.claimDirectChild;
4263
+ if (claimDirectChild && this.recovery.isTerminalRevision(childThreadId)) return;
4264
+ if (!preparedAssignment && !this.prepareReceiverChild(state, childThreadId)) return;
4265
+ const known = this.knownChildren.get(childThreadId);
4266
+ const observedTurns = options.observedTurns ?? (!known ? this.recovery.resolveChildTurnBuffer(state, childThreadId) : []);
4267
+ if (known && known.parent !== state && !options.historicalAssignment) {
4268
+ embeddedAgentLog.warn("Ignoring Codex native subagent child reparenting", {
4269
+ childThreadId,
4270
+ existingParentThreadId: known.parent.parentThreadId,
4271
+ attemptedParentThreadId: parentThreadId
4272
+ });
4273
+ return;
4274
+ }
4275
+ const assignment = preparedAssignment ?? {
4276
+ runId: known?.assignment.runId ?? codexNativeSubagentRunId(childThreadId),
4277
+ childThreadId,
4278
+ nativeTurnId: void 0
4279
+ };
4280
+ const { runId } = assignment;
4281
+ let childState = this.childStates.get(runId);
4282
+ if (childState && childState.parentThreadId !== parentThreadId) return;
4283
+ if (!childState && known && !preparedAssignment) return;
4284
+ if (!childState) {
4285
+ this.updateChildThreadOwnership("claim", childThreadId, this.claimChildThread);
4286
+ this.releaseClientRetention ??= this.retainClient?.();
4287
+ if (!this.parentThreadRetentions.has(parentThreadId)) {
4288
+ const releaseParentThread = this.retainParentThread?.(parentThreadId);
4289
+ if (releaseParentThread) this.parentThreadRetentions.set(parentThreadId, releaseParentThread);
4290
+ }
4291
+ childState = {
4292
+ runId,
4293
+ nativeTurnId: assignment.nativeTurnId,
4294
+ nativeTurnState: observedTurns.find((turn) => turn.turnId === assignment.nativeTurnId)?.state,
4295
+ deliveryReceipts: this.resolveChildReceiptOwner(state, childThreadId),
4296
+ childThreadId,
4297
+ parentThreadId,
4298
+ nativeParentThreadId: known?.nativeParentThreadId ?? parentThreadId,
4299
+ agentId: state.agentId,
4300
+ recoveryAttempt: 0,
4301
+ terminal: false,
4302
+ nativeCompletionDelivered: false,
4303
+ settledWithoutCompletion: false,
4304
+ completionDeliveryAttempt: 0,
4305
+ deliveringCompletion: false
4306
+ };
4307
+ this.childStates.set(runId, childState);
4308
+ if (!known || !known.assignment.nativeTurnId && assignment.nativeTurnId && observedTurns.length > 0) {
4309
+ const taskRecords = this.historyRecovery.selectTaskRecords(state);
4310
+ this.restoreKnownChild(state, assignment, taskRecords, observedTurns);
4311
+ restoreCodexNativeSubagentTaskReceipts({
4312
+ state,
4313
+ taskRecords,
4314
+ knownChildren: this.knownChildren,
4315
+ applyReceipts: (runIds) => this.applyNativeReceipts(state, runIds)
4316
+ });
4317
+ }
4318
+ this.recovery.seedRevision(childThreadId, parentThreadId);
4319
+ }
4320
+ if (known && options.admitAssignment) {
4321
+ const previousRunId = known.assignment.runId;
4322
+ known.assignment = {
4323
+ ...assignment,
4324
+ terminal: childState.terminal || known.assignment.runId === runId && known.assignment.terminal
4325
+ };
4326
+ if (previousRunId !== runId) this.refreshWaitDependency(state, childThreadId);
4327
+ }
4328
+ if (claimDirectChild && !childState.terminal && !childState.settledWithoutCompletion && !childState.releaseDirectChild) {
4329
+ childState.directOwner = options.directOwner;
4330
+ childState.releaseDirectChild = claimDirectChild(childThreadId);
4331
+ }
4332
+ this.registerAgentPath(state, childThreadId, childThreadId);
4333
+ const agentPath = normalizeOptionalString(options.agentPath);
4334
+ if (agentPath) this.registerAgentPath(state, childThreadId, agentPath);
4335
+ for (const path of this.knownChildren.get(childThreadId)?.agentPaths ?? []) this.registerAgentPath(state, childThreadId, path);
4336
+ this.applyNativeReceipts(state, childState.deliveryReceipts.track(runId, this.knownChildren.get(childThreadId)?.agentPaths ?? [childThreadId]));
4337
+ const restored = this.knownChildren.get(childThreadId);
4338
+ if (observedTurns.length > 0 && restored?.parent === state) {
4339
+ if (!restored.assignment.terminal && !this.currentChild(childThreadId)) this.registerChildThread(state, restored.assignment, { observedTurns });
4340
+ const pendingAdmissions = [...this.pendingChildAdmissionEvidence];
4341
+ for (const [parentTurnId, pending] of pendingAdmissions) {
4342
+ const owners = /* @__PURE__ */ new Set();
4343
+ for (const entry of pending) {
4344
+ if (entry.kind !== "interaction" || entry.parentThreadId !== state.parentThreadId || entry.childThreadId !== childThreadId) continue;
4345
+ const owner = entry.admittedOwner ?? entry.owner;
4346
+ if (owner) owners.add(owner);
4347
+ }
4348
+ for (const owner of owners) this.drainPendingChildAdmissionEvidence(state, owner, parentTurnId, true);
4349
+ }
4350
+ for (const candidate of this.recovery.pendingChildRecoveries(state, childThreadId)) candidate.observedTurns.length = 0;
4351
+ }
4352
+ this.recovery.scheduleRecoveryPoll(childState);
4353
+ return childState;
4354
+ }
4355
+ currentChild(threadId) {
4356
+ const runId = this.knownChildren.get(threadId)?.assignment.runId;
4357
+ return runId ? this.childStates.get(runId) : void 0;
4358
+ }
4359
+ refreshWaitDependency(state, receiverThreadId) {
4360
+ if (this.disposed || this.parentStates.get(state.parentThreadId) !== state || this.retiredParentStates.has(state)) return;
4361
+ for (const child of this.childStates.values()) if (child.parentThreadId === state.parentThreadId) this.turnObservation.refreshWaitDependency(child, receiverThreadId);
4362
+ }
4363
+ observeNativeChildTurnStart(params) {
4364
+ const threadId = readStringField(params, "threadId");
4365
+ const turn = isJsonObject(params.turn) ? params.turn : void 0;
4366
+ const turnId = readStringField(turn, "id");
4367
+ const known = threadId ? this.knownChildren.get(threadId) : void 0;
4368
+ if (!threadId || !turnId || !known || this.parentStates.get(known.parent.parentThreadId) !== known.parent) return true;
4369
+ let previous = this.currentChild(threadId);
4370
+ if (!previous && !known.assignment.terminal && !known.assignment.nativeTurnId && this.recovery.pendingChildRecoveries(known.parent, threadId).length > 0) return false;
4371
+ if (!previous && !known.assignment.terminal) previous = this.registerChildThread(known.parent, known.assignment);
4372
+ const pending = known.pendingTurns.find((candidate) => candidate.turnId === turnId);
4373
+ if (known.observedTurns.has(turnId) && (known.turnId !== turnId || known.assignment.terminal || pending || previous?.nativeTurnState !== void 0)) return false;
4374
+ const observedTurn = !known.observedTurns.has(turnId);
4375
+ const startsPendingTurn = !pending && (known.pendingTurns.length > 0 || known.assignment.unanchored || !previous || known.assignment.terminal || previous.terminal || previous.nativeTurnState === "completed" || previous.nativeTurnState === "failed" || previous.nativeTurnId && previous.nativeTurnId !== turnId);
4376
+ if (observedTurn) known.observedTurns.set(turnId, startsPendingTurn ? { awaitingInteraction: true } : {});
4377
+ if (startsPendingTurn) {
4378
+ const previousPending = known.pendingTurns.at(-1);
4379
+ if (previousPending?.state === "active") previousPending.state = void 0;
4380
+ known.pendingTurns.push({
4381
+ turnId,
4382
+ state: "active"
4383
+ });
4384
+ if (!previousPending && previous && !known.assignment.terminal && !previous.terminal && (!previous.nativeTurnState || previous.nativeTurnState === "active")) {
4385
+ previous.nativeTurnState = void 0;
4386
+ previous.activityWait = void 0;
4387
+ this.releaseDirectChild(previous);
4388
+ this.turnObservation.markActivityUnknown(previous);
4389
+ }
4390
+ this.applyNativeReceipts(known.parent, known.deliveryReceipts.track(codexNativeSubagentRunId(threadId, turnId), known.agentPaths));
4391
+ }
4392
+ known.turnId = turnId;
4393
+ if (previous && !previous.terminal && known.pendingTurns.length === 0) {
4394
+ previous.nativeTurnId = turnId;
4395
+ known.assignment.nativeTurnId = turnId;
4396
+ known.assignment.unanchored = void 0;
4397
+ previous.nativeTurnState = "active";
4398
+ known.parent.mirror?.recordNativeTurn(previous.runId, turnId);
4399
+ }
4400
+ if (observedTurn) this.associatePendingChildInteraction(known, threadId, turnId);
4401
+ this.admitFollowupChild(known, threadId);
4402
+ return true;
4403
+ }
4404
+ associatePendingChildInteraction(known, threadId, nativeTurnId) {
4405
+ for (const [parentTurnId, pending] of this.pendingChildAdmissionEvidence) {
4406
+ const interaction = pending.find((evidence) => evidence.kind === "interaction" && evidence.parentThreadId === known.parent.parentThreadId && evidence.childThreadId === threadId && !evidence.nativeTurnId);
4407
+ if (interaction?.kind !== "interaction") continue;
4408
+ interaction.nativeTurnId = nativeTurnId;
4409
+ const observed = known.observedTurns.get(nativeTurnId);
4410
+ if (observed) observed.awaitingInteraction = void 0;
4411
+ if (interaction.owner) this.drainPendingChildAdmissionEvidence(known.parent, interaction.owner, parentTurnId);
4412
+ return;
4413
+ }
4414
+ }
4415
+ observeParentInteraction(state, owner, threadId, agentPath, interaction = {}) {
4416
+ this.prepareReceiverChild(state, threadId);
4417
+ const known = this.knownChildren.get(threadId);
4418
+ if (known && known.parent !== state || !known && this.recovery.pendingChildRecoveries(state, threadId).length === 0) return;
4419
+ if (known && agentPath) this.registerAgentPath(state, threadId, agentPath);
4420
+ const parentTurnId = interaction.parentTurnId ?? owner?.turnId;
4421
+ this.bufferPendingChildAdmissionEvidence(parentTurnId, {
4422
+ kind: "interaction",
4423
+ parentThreadId: state.parentThreadId,
4424
+ childThreadId: threadId,
4425
+ ...agentPath ? { agentPath } : {},
4426
+ ...interaction.itemId ? { itemId: interaction.itemId } : {},
4427
+ ...owner ? { owner } : {}
4428
+ });
4429
+ if (!known || !known.assignment.terminal && !known.assignment.nativeTurnId) this.associateUnregisteredChildInteractions(state, threadId);
4430
+ if (owner && parentTurnId) this.drainPendingChildAdmissionEvidence(state, owner, parentTurnId, true);
4431
+ }
4432
+ admitFollowupChild(known, threadId, owner) {
4433
+ if (this.parentStates.get(known.parent.parentThreadId) !== known.parent || this.retiredParentStates.has(known.parent)) return;
4434
+ let child = this.currentChild(threadId);
4435
+ if (!child && !known.assignment.terminal) return;
4436
+ let claimOwner = owner;
4437
+ let transitioned = false;
4438
+ while (known.pendingTurns.length > 0) {
4439
+ const pending = known.pendingTurns[0];
4440
+ const currentTurnId = child?.nativeTurnId;
4441
+ const recoveredIndex = currentTurnId ? known.pendingTurns.findIndex((candidate) => candidate.turnId === currentTurnId) : -1;
4442
+ if (child && !child.terminal && !known.assignment.terminal && (recoveredIndex >= 0 || child.nativeTurnState === "interrupted")) {
4443
+ let continuationCount = Math.max(1, recoveredIndex + 1);
4444
+ if (recoveredIndex < 0) while (continuationCount < known.pendingTurns.length && known.pendingTurns[continuationCount - 1]?.state === "interrupted") continuationCount += 1;
4445
+ const continuations = known.pendingTurns.splice(0, continuationCount);
4446
+ const resumed = continuations.at(-1);
4447
+ if (recoveredIndex < 0) {
4448
+ child.nativeTurnId = resumed.turnId;
4449
+ child.nativeTurnState = resumed.state;
4450
+ child.activityWait = void 0;
4451
+ known.parent.mirror?.recordNativeTurn(child.runId, resumed.turnId);
4452
+ }
4453
+ claimOwner = resumed.admittedOwner;
4454
+ transitioned = true;
4455
+ this.applyNativeReceipts(known.parent, known.deliveryReceipts.resumeAssignment(child.runId, continuations.map((turn) => codexNativeSubagentRunId(threadId, turn.turnId))));
4456
+ continue;
4457
+ }
4458
+ if (child && !child.terminal && !known.assignment.terminal && child.nativeTurnState !== "completed" && child.nativeTurnState !== "failed") {
4459
+ child.nativeTurnState = void 0;
4460
+ return;
4461
+ }
4462
+ if (!pending.admittedOwner && !pending.admittedSubmission) return;
4463
+ const runId = codexNativeSubagentRunId(threadId, pending.turnId);
4464
+ known.parent.mirror?.startFollowupTurn(threadId, pending.turnId, known.nativeParentThreadId);
4465
+ child = this.registerChildThread(known.parent, {
4466
+ runId,
4467
+ childThreadId: threadId,
4468
+ nativeTurnId: pending.turnId
4469
+ }, { admitAssignment: true });
4470
+ if (!child) return;
4471
+ child.nativeTurnId = pending.turnId;
4472
+ child.nativeTurnState = pending.state;
4473
+ claimOwner = pending.admittedOwner;
4474
+ transitioned = true;
4475
+ known.pendingTurns.shift();
4476
+ }
4477
+ if (!child || child.terminal || known.assignment.terminal || !child.nativeTurnId) return;
4478
+ known.assignment.nativeTurnId = child.nativeTurnId;
4479
+ known.turnId = child.nativeTurnId;
4480
+ if (child.nativeTurnState !== "active") return child;
4481
+ const currentInteraction = [...this.pendingChildAdmissionEvidence.values()].flat().findLast((evidence) => evidence.kind === "interaction" && evidence.parentThreadId === known.parent.parentThreadId && evidence.childThreadId === threadId && !evidence.nativeTurnId && evidence.owner && [...known.parent.owners.values()].includes(evidence.owner));
4482
+ if (currentInteraction?.kind === "interaction" && currentInteraction.owner) claimOwner = currentInteraction.owner;
4483
+ if (claimOwner && [...known.parent.owners.values()].includes(claimOwner)) {
4484
+ if (child.directOwner !== claimOwner) {
4485
+ this.releaseDirectChild(child);
4486
+ child.directOwner = claimOwner;
4487
+ child.releaseDirectChild = claimOwner.claimDirectChild?.(child.childThreadId);
4488
+ }
4489
+ if (!transitioned) claimOwner.onDirectChildAccepted?.();
4490
+ }
4491
+ return child;
4492
+ }
4493
+ resolveParentOwner(state, turnIdInput) {
4494
+ const turnId = turnIdInput?.trim();
4495
+ if (!turnId) return;
4496
+ const owners = [...state.owners.values()].filter((owner) => owner.turnId === turnId);
4497
+ return owners.length === 1 ? owners[0] : void 0;
4498
+ }
4499
+ registerDirectSpawnChild(state, turnIdInput, evidence, owner) {
4500
+ const childState = this.registerChildThread(state, evidence.childThreadId, {
4501
+ ...evidence.agentPath === void 0 ? {} : { agentPath: evidence.agentPath },
4502
+ ...owner?.claimDirectChild ? { directOwner: owner } : {}
4503
+ });
4504
+ if (!owner) this.bufferPendingChildAdmissionEvidence(turnIdInput, {
4505
+ ...evidence,
4506
+ kind: "spawn"
4507
+ });
4508
+ else if (childState) owner.onDirectChildAccepted?.();
4509
+ return childState;
4510
+ }
4511
+ bufferPendingChildAdmissionEvidence(turnIdInput, evidence) {
4512
+ const turnId = turnIdInput?.trim();
4513
+ const requiresUnboundOwner = evidence.kind !== "interaction" || !evidence.owner;
4514
+ if (!turnId || requiresUnboundOwner && !this.hasUnboundParentOwner(evidence.parentThreadId)) return;
4515
+ const pending = this.pendingChildAdmissionEvidence.get(turnId) ?? [];
4516
+ if (evidence.kind === "interaction") {
4517
+ const nativeTurn = [...this.knownChildren.get(evidence.childThreadId)?.observedTurns ?? []].find(([nativeTurnId, observed]) => observed.awaitingInteraction && ![...this.pendingChildAdmissionEvidence.values()].flat().some((candidate) => candidate.kind === "interaction" && candidate.parentThreadId === evidence.parentThreadId && candidate.childThreadId === evidence.childThreadId && candidate.nativeTurnId === nativeTurnId));
4518
+ if (nativeTurn) evidence.nativeTurnId = nativeTurn[0];
4519
+ }
4520
+ if (pending.some((candidate) => candidate.parentThreadId === evidence.parentThreadId && candidate.kind === evidence.kind && candidate.childThreadId === evidence.childThreadId && candidate.agentPath === evidence.agentPath && (candidate.kind === "spawn" || evidence.kind === "spawn" || candidate.nativeTurnId !== void 0 && candidate.nativeTurnId === evidence.nativeTurnId || evidence.itemId !== void 0 && candidate.itemId === evidence.itemId)) || requiresUnboundOwner && [...this.pendingChildAdmissionEvidence.values()].reduce((count, entries) => count + entries.length, 0) >= MAX_PENDING_CHILD_ADMISSION_EVIDENCE) return;
4521
+ pending.push(evidence);
4522
+ this.pendingChildAdmissionEvidence.set(turnId, pending);
4523
+ }
4524
+ drainPendingChildAdmissionEvidence(state, owner, turnId, observeActivity = false) {
4525
+ const pending = this.pendingChildAdmissionEvidence.get(turnId);
4526
+ const ownerIsCurrent = [...state.owners.values()].includes(owner);
4527
+ if (!pending || this.parentStates.get(state.parentThreadId) !== state || this.retiredParentStates.has(state) || !ownerIsCurrent && !pending.some((entry) => entry.kind === "interaction" && entry.admittedOwner === owner)) return;
4528
+ const remaining = [];
4529
+ const affectedChildren = /* @__PURE__ */ new Set();
4530
+ const unknownChildren = /* @__PURE__ */ new Set();
4531
+ for (const evidence of pending) {
4532
+ if (evidence.parentThreadId !== state.parentThreadId) {
4533
+ remaining.push(evidence);
4534
+ continue;
4535
+ }
4536
+ if (evidence.kind === "interaction") {
4537
+ if (!ownerIsCurrent && evidence.admittedOwner !== owner) {
4538
+ remaining.push(evidence);
4539
+ continue;
4540
+ }
4541
+ if (ownerIsCurrent) evidence.owner = owner;
4542
+ const known = this.knownChildren.get(evidence.childThreadId);
4543
+ if (known && known.parent !== state) continue;
4544
+ if (!known || !known.assignment.terminal && !known.assignment.nativeTurnId && !this.currentChild(evidence.childThreadId) && this.recovery.pendingChildRecoveries(state, evidence.childThreadId).length > 0) {
4545
+ remaining.push(evidence);
4546
+ unknownChildren.add(evidence.childThreadId);
4547
+ continue;
4548
+ }
4549
+ if (evidence.agentPath && !known.agentPaths.has(evidence.agentPath)) this.registerAgentPath(state, evidence.childThreadId, evidence.agentPath);
4550
+ if (evidence.nativeTurnId) {
4551
+ const observed = known.observedTurns.get(evidence.nativeTurnId);
4552
+ if (observed) observed.awaitingInteraction = void 0;
4553
+ const nativeTurn = known.pendingTurns.find((turn) => turn.turnId === evidence.nativeTurnId);
4554
+ if (nativeTurn) {
4555
+ if (!nativeTurn.admittedOwner) {
4556
+ nativeTurn.admittedOwner = ownerIsCurrent ? owner : evidence.admittedOwner;
4557
+ if (ownerIsCurrent) owner.onDirectChildAccepted?.();
4558
+ }
4559
+ } else if (this.currentChild(evidence.childThreadId)?.nativeTurnId !== evidence.nativeTurnId) continue;
4560
+ } else if (ownerIsCurrent) remaining.push(evidence);
4561
+ else continue;
4562
+ affectedChildren.add(evidence.childThreadId);
4563
+ continue;
4564
+ }
4565
+ if (!ownerIsCurrent || !owner.claimDirectChild) continue;
4566
+ if (this.registerChildThread(state, evidence.childThreadId, {
4567
+ ...evidence.agentPath === void 0 ? {} : { agentPath: evidence.agentPath },
4568
+ directOwner: owner
4569
+ })) owner.onDirectChildAccepted?.();
4570
+ }
4571
+ if (remaining.length) this.pendingChildAdmissionEvidence.set(turnId, remaining);
4572
+ else this.pendingChildAdmissionEvidence.delete(turnId);
4573
+ for (const threadId of unknownChildren) this.associateUnregisteredChildInteractions(state, threadId);
4574
+ for (const threadId of affectedChildren) {
4575
+ const known = this.knownChildren.get(threadId);
4576
+ if (known?.parent !== state) continue;
4577
+ const previous = this.currentChild(threadId);
4578
+ const child = this.admitFollowupChild(known, threadId, ownerIsCurrent ? owner : void 0);
4579
+ if (observeActivity && child && child !== previous && child.nativeTurnState === "active") this.turnObservation.emitChildTaskActivity({
4580
+ method: "turn/started",
4581
+ params: {
4582
+ threadId,
4583
+ turn: { id: child.nativeTurnId }
4584
+ }
4585
+ }, child);
4586
+ }
4587
+ }
4588
+ hasUnboundParentOwner(parentThreadId) {
4589
+ return [...this.parentStates.get(parentThreadId)?.owners.values() ?? []].some((owner) => owner.turnId === void 0);
4590
+ }
4591
+ clearUnconsumablePendingChildAdmissionEvidence() {
4592
+ this.filterPendingChildAdmissionEvidence((evidence) => {
4593
+ const known = this.knownChildren.get(evidence.childThreadId);
4594
+ if (known && known.parent.parentThreadId !== evidence.parentThreadId) return false;
4595
+ if (evidence.kind === "interaction" && evidence.owner) {
4596
+ const state = this.parentStates.get(evidence.parentThreadId);
4597
+ if (state && [...state.owners.values()].includes(evidence.owner)) return true;
4598
+ return Boolean(evidence.admittedOwner && state && this.recovery.pendingChildRecoveries(state, evidence.childThreadId).length > 0);
4599
+ }
4600
+ return this.hasUnboundParentOwner(evidence.parentThreadId);
4601
+ });
4602
+ }
4603
+ clearPendingChildAdmissionEvidenceForParent(parentThreadId) {
4604
+ this.filterPendingChildAdmissionEvidence((evidence) => evidence.parentThreadId !== parentThreadId);
4605
+ }
4606
+ removePendingSpawnAdmissionEvidenceForChild(childThreadId) {
4607
+ this.filterPendingChildAdmissionEvidence((evidence) => evidence.childThreadId !== childThreadId || evidence.kind === "interaction");
4608
+ }
4609
+ filterPendingChildAdmissionEvidence(keep) {
4610
+ for (const [turnId, pending] of this.pendingChildAdmissionEvidence) {
4611
+ const remaining = pending.filter(keep);
4612
+ if (remaining.length) this.pendingChildAdmissionEvidence.set(turnId, remaining);
4613
+ else this.pendingChildAdmissionEvidence.delete(turnId);
4614
+ }
4615
+ }
4616
+ registerAgentPath(state, childThreadId, agentPath) {
4617
+ this.applyNativeReceipts(state, registerCodexNativeSubagentReceiptAlias({
4618
+ state,
4619
+ childThreadId,
4620
+ agentPath,
4621
+ known: this.knownChildren.get(childThreadId),
4622
+ aliases: this.childThreadIdsByAgentPath
4623
+ }));
4624
+ }
4625
+ unregisterChild(childState, options = {}) {
4626
+ this.releaseDirectChild(childState);
4627
+ const known = this.knownChildren.get(childState.childThreadId);
4628
+ if (childState.terminal && !childState.subscriptionClosed && options.retainSubscription !== false && !this.disposed && known?.parent.parentThreadId === childState.parentThreadId && known.assignment.runId === childState.runId) this.updateChildThreadOwnership("retain", childState.childThreadId, this.retainChildThread);
4629
+ this.recovery.clearRecoveryTimers(childState);
4630
+ this.completionDelivery.release(childState);
4631
+ if (this.childStates.get(childState.runId) === childState) this.childStates.delete(childState.runId);
4632
+ if (![...this.childStates.values()].some((remainingChild) => remainingChild.parentThreadId === childState.parentThreadId)) {
4633
+ const releaseParentThread = this.parentThreadRetentions.get(childState.parentThreadId);
4634
+ this.parentThreadRetentions.delete(childState.parentThreadId);
4635
+ releaseParentThread?.();
4636
+ }
4637
+ this.recovery.collectThreadStatusRevision(childState.childThreadId);
4638
+ this.releaseClientRetentionIfIdle();
4639
+ const state = this.parentStates.get(childState.parentThreadId);
4640
+ if (state) this.pruneParentIfUnused(state);
4641
+ if (known && known.parent !== state) this.pruneParentIfUnused(known.parent);
4642
+ }
4643
+ releaseDirectChild(childState) {
4644
+ const release = childState.releaseDirectChild;
4645
+ childState.releaseDirectChild = void 0;
4646
+ childState.directOwner = void 0;
4647
+ release?.();
4648
+ }
4649
+ rejectPendingDirectChild(state, childThreadId, reason) {
4650
+ if ([...this.pendingChildAdmissionEvidence.values()].flat().some((evidence) => evidence.kind === "interaction" && evidence.parentThreadId === state.parentThreadId && evidence.childThreadId === childThreadId)) return;
4651
+ for (const owner of state.owners.values()) owner.rejectPendingDirectChild?.(childThreadId, reason);
4652
+ }
4653
+ updateChildThreadOwnership(operation, childThreadId, update) {
4654
+ if (!update) return;
4655
+ update(childThreadId).catch((error) => {
4656
+ embeddedAgentLog.warn("Failed to update Codex native subagent thread ownership", {
4657
+ operation,
4658
+ childThreadId,
4659
+ error: formatErrorMessage(error)
4660
+ });
4661
+ });
4662
+ }
4663
+ releaseClientRetentionIfIdle() {
4664
+ if ([...this.childStates.values()].some((childState) => !childState.terminal && !childState.settledWithoutCompletion)) return;
4665
+ this.releaseRetainedClient();
4666
+ }
4667
+ releaseRetainedClient() {
4668
+ const release = this.releaseClientRetention;
4669
+ this.releaseClientRetention = void 0;
4670
+ release?.();
4671
+ }
4672
+ pruneParentIfUnused(state) {
4673
+ if (this.submissions.hasCustody(state)) return;
4674
+ if (state.owners.size > 0) return;
4675
+ if (this.childCloses.hasPending(state)) return;
4676
+ for (const childState of this.childStates.values()) if (childState.parentThreadId === state.parentThreadId) return;
4677
+ for (const known of this.knownChildren.values()) if (known.parent === state && this.currentChild(known.assignment.childThreadId)) return;
4678
+ if (!this.retiredParentStates.has(state) && this.recovery.allCandidates().some((candidate) => candidate.parentState === state)) return;
4679
+ if (this.parentStates.get(state.parentThreadId) === state) {
4680
+ this.submissions.retire(state);
4681
+ this.childCloses.clear(state);
4682
+ this.recovery.clearTerminalRevisionsForParent(state.parentThreadId);
4683
+ this.historyRecovery.forgetRecoveredParent(state);
4684
+ this.parentStates.delete(state.parentThreadId);
4685
+ for (const [threadId, known] of this.knownChildren) if (known.parent === state) {
4686
+ this.childCloses.retireReceiver(known, () => this.updateChildThreadOwnership("release", threadId, this.releaseChildThread));
4687
+ for (const path of known.agentPaths) this.childThreadIdsByAgentPath.delete(buildCodexNativeSubagentAgentPathKey(state.parentThreadId, path));
4688
+ this.knownChildren.delete(threadId);
4689
+ }
4690
+ }
4691
+ }
4692
+ async reconcileTaskRowsForParent(state) {
4693
+ if (this.disposed || this.parentStates.get(state.parentThreadId) !== state || !state.taskRuntime || !state.requesterSessionKey || !state.taskRuntimeScope) return;
4694
+ const candidates = /* @__PURE__ */ new Map();
4695
+ const turnBuffers = /* @__PURE__ */ new Map();
4696
+ const taskRecords = this.historyRecovery.selectTaskRecords(state);
4697
+ for (const task of taskRecords) {
4698
+ const assignment = readNativeTaskAssignment(task);
4699
+ if (assignment && this.historyRecovery.canRestoreTask(task, state) && (task.deliveryStatus === "delivered" || readCodexNativeSubagentHistoryOwner(task.detail)?.parentThreadId === state.parentThreadId) && !this.knownChildren.has(assignment.childThreadId)) this.restoreKnownChild(state, assignment, taskRecords);
4700
+ if (!this.historyRecovery.shouldReconcileTask(task, this.now())) continue;
4701
+ if (!assignment) continue;
4702
+ const childThreadId = assignment.childThreadId;
4703
+ const observedTurns = turnBuffers.get(childThreadId) ?? this.recovery.resolveChildTurnBuffer(state, childThreadId);
4704
+ turnBuffers.set(childThreadId, observedTurns);
4705
+ candidates.set(assignment.runId, {
4706
+ taskId: task.taskId,
4707
+ runId: assignment.runId,
4708
+ nativeTurnId: assignment.nativeTurnId,
4709
+ terminal: task.status === "succeeded" || task.status === "failed" || task.status === "cancelled",
4710
+ observedTurns,
4711
+ parentState: state,
4712
+ deliveryReceipts: this.resolveChildReceiptOwner(state, childThreadId),
4713
+ requesterSessionKey: state.requesterSessionKey,
4714
+ childThreadId,
4715
+ recoveryAttempt: 0,
4716
+ taskRuntimeScope: state.taskRuntimeScope,
4717
+ agentId: state.agentId,
4718
+ taskRuntime: state.taskRuntime
4719
+ });
4720
+ }
4721
+ restoreCodexNativeSubagentTaskReceipts({
4722
+ state,
4723
+ taskRecords,
4724
+ knownChildren: this.knownChildren,
4725
+ applyReceipts: (runIds) => this.applyNativeReceipts(state, runIds)
4726
+ });
4727
+ let previous;
4728
+ for (const candidate of candidates.values()) previous = this.recovery.reconcileTaskCandidate(candidate, previous).catch((error) => {
4729
+ logRecoveryFailure(candidate.childThreadId, error);
4730
+ });
4731
+ await previous;
4732
+ }
4733
+ restoreKnownChild(state, assignment, taskRecords, observedTurns = []) {
4734
+ const { current, found, terminal, nativeParentThreadId, storedTurnIds, completedRunIds } = this.historyRecovery.readChildAssignments(state, assignment, taskRecords);
4735
+ for (const runId of completedRunIds) state.mirror?.markAuthoritativeCompletion(assignment.childThreadId, runId);
4736
+ if (found) state.mirror?.restoreCurrentTaskRun(assignment.childThreadId, current.runId);
4737
+ const currentIndex = observedTurns.findIndex((turn) => turn.turnId === current.nativeTurnId);
4738
+ const pendingTurns = observedTurns.filter((turn, index) => index > currentIndex && turn.turnId !== current.nativeTurnId && !storedTurnIds.has(turn.turnId)).map((turn, index, turns) => ({
4739
+ turnId: turn.turnId,
4740
+ state: turn.state === "active" && index < turns.length - 1 ? void 0 : turn.state
4741
+ }));
4742
+ const previousRunId = this.knownChildren.get(assignment.childThreadId)?.assignment.runId;
4743
+ this.knownChildren.set(assignment.childThreadId, {
4744
+ parent: state,
4745
+ nativeParentThreadId,
4746
+ deliveryReceipts: this.resolveChildReceiptOwner(state, assignment.childThreadId),
4747
+ assignment: {
4748
+ ...current,
4749
+ terminal,
4750
+ unanchored: !terminal && !current.nativeTurnId && found ? true : void 0
4751
+ },
4752
+ turnId: pendingTurns.at(-1)?.turnId ?? current.nativeTurnId,
4753
+ observedTurns: new Map([...new Set([
4754
+ ...storedTurnIds,
4755
+ current.nativeTurnId,
4756
+ current.initialTurnId,
4757
+ ...observedTurns.map((turn) => turn.turnId)
4758
+ ].filter((id) => Boolean(id)))].map((turnId) => [turnId, pendingTurns.some((turn) => turn.turnId === turnId) ? { awaitingInteraction: true } : {}])),
4759
+ pendingTurns,
4760
+ agentPaths: this.knownChildren.get(assignment.childThreadId)?.agentPaths ?? /* @__PURE__ */ new Set([assignment.childThreadId])
4761
+ });
4762
+ if (previousRunId !== current.runId) this.refreshWaitDependency(state, assignment.childThreadId);
4763
+ }
4764
+ async reconcileTaskCandidateOnce(candidate) {
4765
+ if (this.disposed || this.retiredParentStates.has(candidate.parentState)) return;
4766
+ const childBeforeRead = this.childStates.get(candidate.runId);
4767
+ const prepared = this.historyRecovery.prepareTaskRead(candidate, childBeforeRead, this.now());
4768
+ if (!prepared) return;
4769
+ const { task, historyOwner } = prepared;
4770
+ let { assignment } = prepared;
4771
+ candidate.terminal = prepared.terminal;
4772
+ candidate.nativeTurnId = assignment.nativeTurnId;
4773
+ const statusRead = this.recovery.retainThreadStatusRevision(assignment.childThreadId);
4774
+ try {
4775
+ let recovery;
4776
+ try {
4777
+ recovery = await this.historyRecovery.readTask(assignment, task, candidate);
4778
+ } catch (error) {
4779
+ logRecoveryFailure(candidate.childThreadId, error);
4780
+ this.recovery.scheduleTaskCandidateReconciliation(candidate);
4781
+ return;
4782
+ }
4783
+ if (this.disposed || this.retiredParentStates.has(candidate.parentState) || this.parentStates.get(candidate.parentState.parentThreadId) !== candidate.parentState) return;
4784
+ if (!statusRead.isCurrent() || this.childStates.get(candidate.runId) !== childBeforeRead) {
4785
+ this.recovery.scheduleTaskCandidateReconciliation(candidate);
4786
+ return;
4787
+ }
4788
+ const parentThreadId = recovery.parentThreadId;
4789
+ if (!parentThreadId) {
4790
+ this.recovery.scheduleTaskCandidateReconciliation(candidate);
4791
+ return;
4792
+ }
4793
+ if (!this.historyRecovery.isCurrentTask(candidate, task, historyOwner, parentThreadId, this.now())) return;
4794
+ if (!candidate.terminal && !assignment.nativeTurnId && candidate.observedTurns.length > 0) {
4795
+ if (!recovery.assignmentTurnId) {
4796
+ this.recovery.scheduleTaskCandidateReconciliation(candidate);
4797
+ return;
4798
+ }
4799
+ assignment = {
4800
+ ...assignment,
4801
+ nativeTurnId: recovery.assignmentTurnId
4802
+ };
4803
+ candidate.nativeTurnId = recovery.assignmentTurnId;
4804
+ }
4805
+ const nativeParent = this.parentStates.get(parentThreadId);
4806
+ const controller = this.knownChildren.get(assignment.childThreadId)?.parent;
4807
+ for (const owner of [nativeParent, controller]) if (owner && (this.retiredParentStates.has(owner) || !this.historyRecovery.acceptsParent(owner, candidate.parentState))) return;
4808
+ const historicalAssignment = candidate.terminal && task.deliveryStatus !== "delivered";
4809
+ let state = historicalAssignment ? nativeParent : controller ?? nativeParent;
4810
+ if (!state) {
4811
+ state = {
4812
+ parentThreadId,
4813
+ owners: /* @__PURE__ */ new Map(),
4814
+ turnIds: /* @__PURE__ */ new Set(),
4815
+ deliveryReceipts: parentThreadId === candidate.parentState.parentThreadId ? candidate.deliveryReceipts : new CodexNativeSubagentDeliveryReceipts(),
4816
+ requesterSessionKey: candidate.requesterSessionKey,
4817
+ taskRuntimeScope: candidate.taskRuntimeScope,
4818
+ agentId: candidate.agentId,
4819
+ historyOwner: historyOwner ?? candidate.parentState.historyOwner,
4820
+ taskRuntime: candidate.taskRuntime
4821
+ };
4822
+ this.prepareParentTaskRuntime(state);
4823
+ this.parentStates.set(parentThreadId, state);
4824
+ }
4825
+ this.historyRecovery.retainRecoveryParents([state], candidate.parentState);
4826
+ const observedTurns = parentThreadId === candidate.parentState.parentThreadId ? candidate.observedTurns.map((turn) => ({
4827
+ turnId: turn.turnId,
4828
+ state: turn.state && turn.state !== "active" ? turn.state : recovery.observedPendingTurns.find((observed) => observed.turnId === turn.turnId)?.state ?? turn.state
4829
+ })) : [];
4830
+ const childState = this.registerChildThread(state, assignment, {
4831
+ ...historicalAssignment ? { historicalAssignment: true } : {},
4832
+ ...recovery.agentPath ? { agentPath: recovery.agentPath } : {},
4833
+ observedTurns
4834
+ });
4835
+ if (!childState) {
4836
+ this.pruneParentIfUnused(state);
4837
+ return;
4838
+ }
4839
+ childState.requiresHistoryOwner = true;
4840
+ childState.completionTaskId ??= candidate.taskId;
4841
+ candidate.observedTurns.length = 0;
4842
+ this.recordRecoveredChildTurn(state, childState, recovery);
4843
+ if (recovery.threadState === "active") this.observeActiveChild(childState);
4844
+ if (recovery.threadState === "other") this.recovery.clearSystemErrorFallback(childState);
4845
+ if (recovery.resumable) {
4846
+ this.settleResumableChild(childState);
4847
+ return;
4848
+ }
4849
+ const completion = this.processRecoveredCompletion(state, childState, recovery);
4850
+ if (completion) await completion;
4851
+ else if (!recovery.completion && !recovery.fallbackCompletion) this.recovery.scheduleRecoveryPoll(childState);
4852
+ } finally {
4853
+ statusRead.release();
4854
+ }
4855
+ }
4856
+ };
4857
+ const codexNativeSubagentMonitorRuntime = createCodexNativeSubagentMonitorRuntime(Monitor);
4858
+ //#endregion
4859
+ export { CodexToolProgressProjection as a, CodexToolTranscriptProjection as i, CodexEventProjection as n, shouldEmitTranscriptToolProgress as o, emitCodexAgentEvent as r, codexNativeSubagentMonitorRuntime as t };