chainlesschain 0.166.115 → 0.166.117

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 (180) hide show
  1. package/README.md +20 -4
  2. package/package.json +3 -3
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-BsoSoDNb.js → AIOps-C9tSCImB.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-Dcx7Gm5t.js → ActionButton-CXKExmT_.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-S1hsBIz3.js → Analytics-DE8WBWHo.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-B2j067qM.js → AppLayout-BdpwNM08.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-D5iGV9ic.js → Artifacts-94TQpe38.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-C8ChVvXu.js → Audit-DdDEv5Ej.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-Cg97AAJS.js → BackgroundAgents-TU9nlV7j.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CyxU9IUg.js → Backup-C0GPoUUq.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-CKlWjA8I.js → BaseInput-iyrVn2I6.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-C-EqZza6.js → Chat-Dqo8spwp.js} +5 -5
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BRFiXKPX.js → ChatBubbleRenderer-CDqHSzc4.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-kkI13tSJ.js → Checkbox-BF_XJvQi.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-NlqEDwHK.js → Codegen-5tUWoOa9.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-CfepEu79.js → Col-BQxSeRvG.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-BRo0qLXM.js → Community-B2Ra7w-J.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-BMu8TP0E.js → Compact-GjxJwlr0.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-B7Q3Moef.js → Compliance-BTUP6RTA.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-BVwt7Xmc.js → Cowork-DiwfL16s.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-CgBv20Qy.js → Cron-1ceOo5ux.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-CwcSBJk2.js → Crosschain-aHYxLu0u.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-d20rCiIm.js → DID-eeTjzOLV.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-PpZaUYpS.js → Dashboard-CT3fpkvX.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-DjsLdX_Z.js → Dropdown-B3UpOy-N.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-CaPkrtxn.js → EmailListRenderer-Ba2Rvoru.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-DDY8cQnE.js → EvolutionSettings-CGtn93on.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-VQavMjyj.js → FamilyGuardDashboard-C0IUdHQt.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-D0GvKQgI.js → Federation-1QIDwemn.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-Dl_TWHmb.js → FormItemContext-NGbSsdje.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-Bny2uagG.js → GenericCardRenderer-DzVZc2Ba.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-B01KXq47.js → Git-B_1TDbSr.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-F4c4Vwo1.js → Governance-BDl_ySyI.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-_025Q-8K.js → Inference-CAp-wcCK.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-DwbTmQb1.js → KnowledgeGraph-DRjRcxul.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-CnNo0hCm.js → Logs-qSuwMwk7.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-BgnYTegK.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-Dkz5bv7F.js → Marketplace-DfiXL6UO.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-CBtRwDW_.js → McpTools-D9rIvFjN.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-Y9jnyTb7.js → Memory-D-p7yDzj.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-Br1urkOz.js → MobileBridge-D0XT5B6m.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-DnsyuerX.js → MobileProjects-CL9cHVIk.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-he3ZzieF.js → Mtc-CME4kDmC.js} +4 -4
  46. package/src/assets/web-panel/assets/{MtcAudit-BadphAuN.js → MtcAudit-BjGOXFnn.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-3_pVLymK.js → Multisig-CSzc53e0.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-DmtmHfsI.js → NLProgramming-BYjZnaH8.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-CE4L2O55.js → Notes-BUPOYE0C.js} +4 -4
  50. package/src/assets/web-panel/assets/{NotificationSettings-B7QmfJn9.js → NotificationSettings-BK8YADru.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-O0wRSyJ0.js → OrderTableRenderer-B5F8bnWt.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-BrlMSsyT.js → Organization-B0WNlhMd.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-CY4ULPrZ.js → Overflow-CED6ZO87.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-kIANZYKd.js → P2P-CBdc8fkP.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-DEgB4rvF.js → PdhVaultBrowser-DuauYi03.js} +5 -5
  56. package/src/assets/web-panel/assets/{Permissions-CWxywRp5.js → Permissions-kfgY0ukN.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-DAg7iCbr.js → PersonalDataHub-CDY3I1Do.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-Brc6ONnR.js → Pipeline-B9x0wKSK.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-CcinYTL8.js → Privacy-Cqhcr_Qv.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-Bz8XEsom.js → ProjectInit-CFhoyo57.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-CsOMlSTZ.js → ProjectSettings-DCBjV2Hm.js} +2 -2
  62. package/src/assets/web-panel/assets/Projects-C51w0LnM.js +1 -0
  63. package/src/assets/web-panel/assets/{Providers-CeunOQiH.js → Providers-BuCIWZY1.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-CN2a2_Fb.js → QrScannerModal-CEXNL5CB.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-CVeLif3e.js → QuickAsk-DgK8aLAU.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-1k9WOEGM.js → Recommend-BxEYQRAs.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-Q8UCqIMw.js → RemoteSession-PRsxNsYU.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-CkrCgXKM.js → Reputation-S-tQH70r.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-JFdvuTzi.js → Row-U1Ws3CJl.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-C74JHCmk.js → RssFeed-BftCyeii.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-DrJiVO1T.js → Search-o4gK0cPy.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-BJzNlSJq.js → Security-U2ZRpsbA.js} +3 -3
  73. package/src/assets/web-panel/assets/{Services-D4TLMnJm.js → Services-D0tRjWl_.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-cswTDqhh.js → Skeleton-DzZ8TFsB.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-Bv0rM1BS.js → Skills-B1cC7rju.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-D9rE7hNh.js → Sla-CtIHOR0J.js} +1 -1
  77. package/src/assets/web-panel/assets/SpeechSettings-CD8ZlQpW.js +1 -0
  78. package/src/assets/web-panel/assets/{SyncSettings-BwnMG8kq.js → SyncSettings-BilFQWQk.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-wJvU24cB.js → Tasks-Bmg2qLXl.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-CZbRU3tN.js → Templates-D6yofyC1.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-_jbpFJPZ.js → Tenant-BzttD-Gp.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-BzNm5WKZ.js → Terminal-DyRXeYuH.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-bmybWjuS.js → TimelineRenderer-Bdmkx-Tr.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-o1WDcqVQ.js → Tokens-10RcVAVJ.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-BB1D6sIB.js → Trigger-CKV-9DdV.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-CLxdCfy2.js → Trust-BEc-sW1q.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-B9LS6vS0.js → UkeySign-BSql8piX.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-Bg0-wxjz.js → VideoEditing-C4OH2f4s.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-Bw0G2F6P.js → Wallet-BSSvWKmv.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-irEiNY9b.js → WebAuthn-LxIzXo9d.js} +5 -5
  91. package/src/assets/web-panel/assets/{WorkflowEditor-Bh08FbJs.js → WorkflowEditor-DWwdfcte.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-De-FPjVT.js → chat-DWxYYLD5.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-ygdyvinU.js → colors-BxEcQqtg.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-BQ9jH9KV.js → compact-item-DNbezFOJ.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-BFuVSyox.js → createContext-DaJXc5nx.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-CRyXoGo9.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-i19GtR1P.js → hasIn-BMrw3sg3.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-IXkif6J_.js → index-2c0bL3SE.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-cDJsyAkI.js → index-6W0V5G2t.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B2LieObB.js → index-7zzLqfvI.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BGMxK155.js → index-B9b6UaZl.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DVnFgasM.js → index-BAV07GDK.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BBYiCI1d.js → index-BCCd8Rj6.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-m8waSAyv.js → index-BNZDGV2q.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CpdXhhMY.js → index-BR4nWOmR.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C61w3omz.js → index-B_b9_6wQ.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DlwxUZQL.js → index-BaByEdBj.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Cq9k4-Ws.js → index-BcuytRTa.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BPUCvc8i.js → index-Bs2QyMPQ.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DgjLaB7k.js → index-Bum_aSyd.js} +1 -1
  111. package/src/assets/web-panel/assets/index-C-W0Ntk_.js +1 -0
  112. package/src/assets/web-panel/assets/{index-BaaydJcC.js → index-C25ig6EV.js} +1 -1
  113. package/src/assets/web-panel/assets/index-CCk_qhBx.js +1 -0
  114. package/src/assets/web-panel/assets/{index-_9tUBcLm.js → index-CL-36X3A.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BLMtCKAY.js → index-Ca2hwPMo.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CMNjaSir.js → index-CaxzeSIM.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CgoSgmjK.js → index-CiEdfbB9.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DuuOX5u3.js → index-Cm9DYzhC.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-We2nK3ju.js → index-CpwgF7vh.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CWfgeaG4.js → index-DEvdSKT4.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-4HoFz1MA.js → index-DL0k35f0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CXrk8OYO.js → index-DarVV5yG.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DS1IwwGL.js → index-DpSp97J8.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BVhtukYX.js → index-Dr18agDl.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-28DjsABm.js → index-Dwg38xTG.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-B1IsP_JO.js → index-DzZHBzCb.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DL7ewjD2.js → index-IX0g0s2t.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Bw5pTpnI.js → index-LRb0QX6S.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-l5-J0jHx.js → index-NEW5EN2w.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BKlU5VZy.js → index-T4T84G6X.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BKblSCL9.js → index-VD-dTYQE.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Bhf-MuUh.js → index-cCqCEa9X.js} +3 -3
  133. package/src/assets/web-panel/assets/{index-vXtH3o6O.js → index-cKBpcInt.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DAboGZTw.js → index-hPy_f08G.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DTY20pWu.js → index-pzYLf5qu.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CBVcGaxk.js → index-rFaCnjXi.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-B_EZHeqN.js → initDefaultProps-BWVLgWhF.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-BrVb_Gy1.js → motion-KJZ1xfDE.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-CKxVRL_J.js → move-DGqU6H7M.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-C80rvQ1J.js → mtc-parser-Dg0xkI5J.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-De5vD7BR.js → omit-B6RnMJAQ.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-BTyEDjth.js → pickAttrs-Ctw2i3jb.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-BWR94AjR.js → placementArrow-D3F3x8Jm.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-1g-Vnibk.js → responsiveObserve-W6eHE0nM.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-Dmg6dYuF.js → slide-BlFf8sY5.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-Ddj6rSab.js → statusUtils-Cd6LMwmy.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-DyP1mPDf.js → styleChecker-Bg5-U4Y1.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-D3d_0YuY.js → useFlexGapSupport-DTkLS3tn.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-M3vq-gE2.js → useFs-CwYGzoBt.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-GtP2TgtK.js → usePersonalDataHub-PCgFQXZP.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-BurE2ATQ.js → vnode-CJM7MkrB.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-Ch-ViiDe.js → zoom-CP0mgaFi.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/commands/compact.js +7 -2
  155. package/src/data/changelog.json +14 -1
  156. package/src/harness/jsonl-session-store.js +78 -73
  157. package/src/harness/prompt-compressor.js +57 -3
  158. package/src/lib/context-memory-kernel/context-image-transport.js +248 -0
  159. package/src/lib/context-memory-kernel/jsonl-session-context-port.js +4 -1
  160. package/src/lib/context-memory-kernel/live-compaction.js +40 -10
  161. package/src/lib/context-memory-kernel/message-adapter.js +80 -10
  162. package/src/lib/context-memory-kernel/message-bundle-store.js +1140 -0
  163. package/src/lib/context-memory-kernel/provider-context.js +23 -8
  164. package/src/lib/evolution/agent-evolution-ingress.js +62 -12
  165. package/src/lib/evolution/agent-model-projection.js +90 -18
  166. package/src/lib/evolution/evidence-json-text.js +279 -42
  167. package/src/lib/evolution/evolution-evidence-projector.js +121 -16
  168. package/src/lib/image-token-estimate.js +59 -0
  169. package/src/lib/read-file-loop-guard.js +7 -4
  170. package/src/lib/session-history-origins.js +25 -5
  171. package/src/lib/session-transcript-history.js +5 -1
  172. package/src/lib/tool-result-preview.js +276 -0
  173. package/src/runtime/agent-core.js +57 -59
  174. package/src/runtime/headless-stream.js +32 -17
  175. package/src/assets/web-panel/assets/MarkdownRenderer-D8_iZief.js +0 -1
  176. package/src/assets/web-panel/assets/Projects-BeSOM6wi.js +0 -1
  177. package/src/assets/web-panel/assets/SpeechSettings-Bfhvin8B.js +0 -1
  178. package/src/assets/web-panel/assets/devWarning-c2I2Rn4_.js +0 -1
  179. package/src/assets/web-panel/assets/index-CTs4dYcV.js +0 -1
  180. package/src/assets/web-panel/assets/index-DeuvaW15.js +0 -1
@@ -0,0 +1,276 @@
1
+ import {
2
+ createEvidenceJsonTextBoundary,
3
+ AGENT_CODE_TEXT_RESULT_TOOLS,
4
+ } from "./evolution/evidence-json-text.js";
5
+ import { createHash } from "node:crypto";
6
+
7
+ // Keep host identity outside the JSON evidence object. External JSON results
8
+ // cannot nominate a digest or borrow an earlier command's output identity.
9
+ const HOST_OUTPUT_IDENTITIES = new WeakMap();
10
+ export function registerHostToolOutput(result, output) {
11
+ HOST_OUTPUT_IDENTITIES.set(
12
+ result,
13
+ Object.freeze({
14
+ chars: output.length,
15
+ digest: createHash("sha256").update(output).digest("hex"),
16
+ visibleOutput: result.output,
17
+ }),
18
+ );
19
+ return result;
20
+ }
21
+ export function copyHostToolOutputIdentity(result, source) {
22
+ const identity = HOST_OUTPUT_IDENTITIES.get(source);
23
+ if (identity) HOST_OUTPUT_IDENTITIES.set(result, identity);
24
+ return result;
25
+ }
26
+ export function hostToolOutputIdentity(result) {
27
+ const identity = HOST_OUTPUT_IDENTITIES.get(result);
28
+ return identity?.visibleOutput === result?.output ? identity : undefined;
29
+ }
30
+
31
+ const PREVIEW_HINT =
32
+ "Tool output truncated. Narrow the request: filter/count locally, select fields, search_files, or read_file with offset/limit. Omitted text is not full-output evidence.";
33
+
34
+ /** Check only host-created excerpts; original uncut tool text still reaches ingress. */
35
+ function completeExcerpt(text, tool) {
36
+ const scan = createEvidenceJsonTextBoundary((message) => new Error(message));
37
+ const visit = (value, layer) => {
38
+ if (typeof value === "string") return project(value, layer);
39
+ if (Array.isArray(value)) return value.map((child) => visit(child, layer));
40
+ if (value && typeof value === "object") {
41
+ return Object.fromEntries(
42
+ Object.entries(value).map(([key, child]) => [key, visit(child, layer)]),
43
+ );
44
+ }
45
+ return value;
46
+ };
47
+ const project = (value, layer = 0) =>
48
+ scan(value, {
49
+ layer,
50
+ codeText: AGENT_CODE_TEXT_RESULT_TOOLS.includes(tool),
51
+ visit,
52
+ plain: (plain) => plain,
53
+ equal: (left, right) => JSON.stringify(left) === JSON.stringify(right),
54
+ });
55
+ try {
56
+ project(text);
57
+ return true;
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+
63
+ function omitted(chars) {
64
+ return `Text omitted (${chars} chars); narrow the request to inspect a complete value.`;
65
+ }
66
+
67
+ const STATUS_KEYS = new Set([
68
+ "success",
69
+ "error",
70
+ "exitCode",
71
+ "status",
72
+ "code",
73
+ "signal",
74
+ "path",
75
+ "range",
76
+ "nextRead",
77
+ "duration",
78
+ "language",
79
+ ]);
80
+
81
+ // Native JSON.parse is used only after checking its lossy cases: duplicate
82
+ // decoded keys and non-finite/unsafe numbers must never turn into a safe-looking
83
+ // preview. This pass does not repair syntax or decode text leaves.
84
+ function parsePreviewJson(text) {
85
+ const value = JSON.parse(text);
86
+ const stack = [];
87
+ for (let index = 0; index < text.length; index++) {
88
+ const char = text[index];
89
+ if (char === '"') {
90
+ const start = index;
91
+ while (++index < text.length) {
92
+ if (text[index] === "\\") index++;
93
+ else if (text[index] === '"') break;
94
+ }
95
+ const object = stack.at(-1);
96
+ if (object?.key) {
97
+ const key = JSON.parse(text.slice(start, index + 1));
98
+ if (object.keys.has(key)) throw new TypeError("Ambiguous JSON keys");
99
+ object.keys.add(key);
100
+ object.key = false;
101
+ }
102
+ } else if (char === "{") stack.push({ keys: new Set(), key: true });
103
+ else if (char === "[") stack.push(null);
104
+ else if (char === "}" || char === "]") stack.pop();
105
+ else if (char === "," && stack.at(-1)) stack.at(-1).key = true;
106
+ else if (/[\d-]/u.test(char)) {
107
+ const token = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/u.exec(
108
+ text.slice(index),
109
+ )[0];
110
+ const number = Number(token);
111
+ if (
112
+ !Number.isFinite(number) ||
113
+ (Number.isInteger(number) && !Number.isSafeInteger(number))
114
+ )
115
+ throw new TypeError("Unsafe JSON number");
116
+ index += token.length - 1;
117
+ }
118
+ }
119
+ return value;
120
+ }
121
+
122
+ /** Produce complete JSON values, never prefixes of a JSON leaf. */
123
+ export function boundedToolResultText(text, max) {
124
+ if (text.length <= max) return text;
125
+ let original;
126
+ try {
127
+ original = parsePreviewJson(text);
128
+ } catch {
129
+ // Plain output can retain a prefix only when it cannot contain a sliced
130
+ // structured span. In particular, do not hide broken JSON in a preview.
131
+ if (!/[{}\[\]"']/u.test(text)) {
132
+ const marker = `\nTool output truncated (${text.length} chars). Narrow the request.`;
133
+ if (marker.length < max) {
134
+ let end = max - marker.length;
135
+ if (/[\uD800-\uDBFF]/u.test(text[end - 1] ?? "")) end--;
136
+ return text.slice(0, end) + marker;
137
+ }
138
+ }
139
+ original = omitted(text.length);
140
+ }
141
+
142
+ for (const limit of [8192, 2048, 512, 128, 0]) {
143
+ let nodes = 0;
144
+ const preview = (value, depth = 0) => {
145
+ if (++nodes > 128 || depth > 6)
146
+ return "Value omitted; select a smaller subtree.";
147
+ if (typeof value === "string")
148
+ return JSON.stringify(value).length > limit
149
+ ? omitted(value.length)
150
+ : value;
151
+ if (!value || typeof value !== "object") return value;
152
+ if (Array.isArray(value)) {
153
+ const kept = [];
154
+ for (const child of value) {
155
+ if (kept.length >= 32 || nodes >= 128) break;
156
+ kept.push(preview(child, depth + 1));
157
+ }
158
+ if (kept.length < value.length)
159
+ kept.push(
160
+ `Items omitted (${value.length - kept.length}); narrow the request.`,
161
+ );
162
+ return kept;
163
+ }
164
+ const kept = Object.create(null);
165
+ const entries = Object.entries(value).sort(
166
+ ([a], [b]) => Number(STATUS_KEYS.has(b)) - Number(STATUS_KEYS.has(a)),
167
+ );
168
+ let count = 0;
169
+ for (const [key, child] of entries) {
170
+ if (count >= 32 || nodes >= 128) break;
171
+ if (key.length > 256) continue;
172
+ Object.defineProperty(kept, key, {
173
+ enumerable: true,
174
+ value: preview(child, depth + 1),
175
+ });
176
+ count++;
177
+ }
178
+ if (count < entries.length) {
179
+ let marker = "_omittedFields";
180
+ while (Object.hasOwn(kept, marker)) marker += "_";
181
+ Object.defineProperty(kept, marker, {
182
+ enumerable: true,
183
+ value: entries.length - count,
184
+ });
185
+ }
186
+ return kept;
187
+ };
188
+ const value = preview(original);
189
+ const result =
190
+ value && !Array.isArray(value) && typeof value === "object"
191
+ ? value
192
+ : { preview: value };
193
+ let marker = "_toolOutputPreview";
194
+ while (Object.hasOwn(result, marker)) marker += "_";
195
+ Object.defineProperty(result, marker, {
196
+ enumerable: true,
197
+ value: {
198
+ truncated: true,
199
+ originalChars: text.length,
200
+ hint: PREVIEW_HINT,
201
+ },
202
+ });
203
+ const serialized = JSON.stringify(result);
204
+ if (serialized.length <= max) return serialized;
205
+ }
206
+ const summary = JSON.stringify({
207
+ truncated: true,
208
+ originalChars: text.length,
209
+ hint: PREVIEW_HINT,
210
+ });
211
+ if (summary.length <= max) return summary;
212
+ if (max >= 18) return '{"truncated":true}';
213
+ return max >= 4 ? "null" : max >= 2 ? "{}" : "0";
214
+ }
215
+
216
+ /** Keep cursors/status, but remove JSON fragments created by host paging. */
217
+ export function safeHostToolResultForModel(tool, result) {
218
+ if (!result || typeof result !== "object" || Array.isArray(result))
219
+ return result;
220
+ const fields = [];
221
+ if (
222
+ tool === "read_file" &&
223
+ (result.truncated || result.range || result.nextRead)
224
+ )
225
+ fields.push("content");
226
+ if (tool === "run_code" && result.truncated) fields.push("output");
227
+ if (tool === "check_shell") fields.push("stdout", "stderr");
228
+ if (tool === "run_shell" || tool === "git") {
229
+ for (const field of ["stdout", "stderr"])
230
+ if (result[`${field}_truncated`])
231
+ fields.push(field, `${field}_diagnostics`);
232
+ }
233
+ let safe = result;
234
+ for (const field of fields) {
235
+ const text = result[field];
236
+ if (typeof text !== "string" || completeExcerpt(text, tool)) continue;
237
+ if (safe === result) safe = { ...result };
238
+ safe[field] = omitted(text.length);
239
+ safe[`${field}Omitted`] = true;
240
+ safe[`${field}OmittedChars`] = text.length;
241
+ }
242
+ return safe;
243
+ }
244
+
245
+ /** Prefer a whole JSON tree over slicing stdout through an embedded value. */
246
+ export function boundedCommandOutput(output, max, tool = "run_code") {
247
+ const encoded = JSON.stringify(output);
248
+ if (encoded.length <= max) return output;
249
+ // If stdout itself is JSON, preview that tree rather than its quoted form.
250
+ let json = false;
251
+ try {
252
+ parsePreviewJson(output);
253
+ json = true;
254
+ } catch {
255
+ /* Plain command text. */
256
+ }
257
+ if (json) {
258
+ const preview = boundedToolResultText(
259
+ output,
260
+ Math.max(1, Math.floor(max / 2)),
261
+ );
262
+ if (JSON.stringify(preview).length <= max && completeExcerpt(preview, tool))
263
+ return preview;
264
+ } else if (!/[{}\[\]"']/u.test(output)) {
265
+ let low = 0;
266
+ let high = Math.min(output.length, max);
267
+ while (low < high) {
268
+ const middle = Math.ceil((low + high) / 2);
269
+ if (JSON.stringify(output.slice(0, middle)).length <= max) low = middle;
270
+ else high = middle - 1;
271
+ }
272
+ if (/[\uD800-\uDBFF]/u.test(output[low - 1] ?? "")) low--;
273
+ return output.slice(0, low);
274
+ }
275
+ return omitted(output.length);
276
+ }
@@ -56,6 +56,13 @@ import { ReadFileLoopGuard } from "../lib/read-file-loop-guard.js";
56
56
  const READ_FILE_REQUEST_CONTEXT = Symbol("read-file-request-context");
57
57
  import { gitToolInputError } from "../lib/git-tool-input.js";
58
58
  import { shellOutputPage, shellOutputPreview } from "../lib/shell-output.js";
59
+ import {
60
+ boundedToolResultText,
61
+ boundedCommandOutput,
62
+ safeHostToolResultForModel,
63
+ registerHostToolOutput,
64
+ copyHostToolOutputIdentity,
65
+ } from "../lib/tool-result-preview.js";
59
66
  import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "../lib/diagnostic-workflow.js";
60
67
  import { CODING_TASK_QUALITY_GUIDANCE } from "../lib/coding-task-guidance.js";
61
68
  import { RemoteReadLoopGuard } from "../lib/remote-read-loop-guard.js";
@@ -5741,7 +5748,12 @@ async function executeToolInner(
5741
5748
  const descriptorPayload = buildPayload(runtimeDescriptor);
5742
5749
  const attachDescriptor = (payload, overrideDescriptor = null) => {
5743
5750
  const descriptor = buildPayload(overrideDescriptor || runtimeDescriptor);
5744
- return descriptor ? { ...payload, toolDescriptor: descriptor } : payload;
5751
+ return descriptor
5752
+ ? copyHostToolOutputIdentity(
5753
+ { ...payload, toolDescriptor: descriptor },
5754
+ payload,
5755
+ )
5756
+ : payload;
5745
5757
  };
5746
5758
  const localToolExecutor =
5747
5759
  externalToolExecutors && typeof externalToolExecutors === "object"
@@ -7706,8 +7718,8 @@ async function executeToolInner(
7706
7718
  ) {
7707
7719
  return attachDescriptor({
7708
7720
  success: true,
7709
- stdout: String(res.stdout || "").substring(0, 30000),
7710
- stderr: String(res.stderr || "").substring(0, 2000),
7721
+ ...shellOutputPreview(res.stdout, "stdout", 30000),
7722
+ ...shellOutputPreview(res.stderr, "stderr", 2000),
7711
7723
  exitCode: res.status,
7712
7724
  command: normalizedCommand,
7713
7725
  readOnly: true,
@@ -7716,11 +7728,13 @@ async function executeToolInner(
7716
7728
  });
7717
7729
  }
7718
7730
  if (res.status !== 0) {
7719
- const stderr = String(res.stderr || "").substring(0, 2000);
7731
+ const stderr = String(res.stderr || "");
7720
7732
  return attachDescriptor({
7721
- error: stderr || `git exited with code ${res.status}`,
7722
- stdout: String(res.stdout || "").substring(0, 30000),
7723
- stderr,
7733
+ error:
7734
+ boundedCommandOutput(stderr, 2000) ||
7735
+ `git exited with code ${res.status}`,
7736
+ ...shellOutputPreview(res.stdout, "stdout", 30000),
7737
+ ...shellOutputPreview(stderr, "stderr", 2000),
7724
7738
  exitCode: res.status,
7725
7739
  command: normalizedCommand,
7726
7740
  readOnly,
@@ -7734,7 +7748,7 @@ async function executeToolInner(
7734
7748
  });
7735
7749
  }
7736
7750
  return attachDescriptor({
7737
- stdout: String(res.stdout || "").substring(0, 30000),
7751
+ ...shellOutputPreview(res.stdout, "stdout", 30000),
7738
7752
  exitCode: 0,
7739
7753
  command: normalizedCommand,
7740
7754
  readOnly,
@@ -10332,8 +10346,8 @@ async function _executeRunCode(args, cwd) {
10332
10346
  outcome: "disabled",
10333
10347
  });
10334
10348
  return {
10335
- error: (stderr || message).substring(0, 5000),
10336
- stderr: stderr.substring(0, 5000),
10349
+ error: boundedCommandOutput(stderr || message, 5000),
10350
+ stderr: boundedCommandOutput(stderr, 5000),
10337
10351
  exitCode: err.status ?? null,
10338
10352
  language: lang,
10339
10353
  ...classified,
@@ -10349,8 +10363,8 @@ async function _executeRunCode(args, cwd) {
10349
10363
  outcome: "blocked",
10350
10364
  });
10351
10365
  return {
10352
- error: (stderr || message).substring(0, 5000),
10353
- stderr: stderr.substring(0, 5000),
10366
+ error: boundedCommandOutput(stderr || message, 5000),
10367
+ stderr: boundedCommandOutput(stderr, 5000),
10354
10368
  exitCode: err.status ?? null,
10355
10369
  language: lang,
10356
10370
  ...classified,
@@ -10405,8 +10419,8 @@ async function _executeRunCode(args, cwd) {
10405
10419
  ),
10406
10420
  );
10407
10421
  return {
10408
- error: (stderr || message).substring(0, 5000),
10409
- stderr: stderr.substring(0, 5000),
10422
+ error: boundedCommandOutput(stderr || message, 5000),
10423
+ stderr: boundedCommandOutput(stderr, 5000),
10410
10424
  exitCode: err.status ?? null,
10411
10425
  language: lang,
10412
10426
  ...classified,
@@ -10462,14 +10476,13 @@ async function _executeRunCode(args, cwd) {
10462
10476
  );
10463
10477
  const retryDuration = Date.now() - retryStart;
10464
10478
 
10465
- return {
10479
+ return Object.assign(boundedCodeOutput(retryOutput), {
10466
10480
  success: true,
10467
- ...boundedCodeOutput(retryOutput),
10468
10481
  language: lang,
10469
10482
  duration: `${retryDuration}ms`,
10470
10483
  autoInstalled: [packageName],
10471
10484
  ...(persist ? { scriptPath } : {}),
10472
- };
10485
+ });
10473
10486
  } catch (retryErr) {
10474
10487
  rethrowRunCodeSandboxFailure(retryErr);
10475
10488
  const retryStderr = (retryErr.stderr || "").toString();
@@ -10481,8 +10494,8 @@ async function _executeRunCode(args, cwd) {
10481
10494
  lang,
10482
10495
  );
10483
10496
  return {
10484
- error: (retryStderr || retryMessage).substring(0, 5000),
10485
- stderr: retryStderr.substring(0, 5000),
10497
+ error: boundedCommandOutput(retryStderr || retryMessage, 5000),
10498
+ stderr: boundedCommandOutput(retryStderr, 5000),
10486
10499
  exitCode: retryErr.status ?? null,
10487
10500
  language: lang,
10488
10501
  ...retryClassified,
@@ -10495,8 +10508,8 @@ async function _executeRunCode(args, cwd) {
10495
10508
  }
10496
10509
 
10497
10510
  return {
10498
- error: (stderr || message).substring(0, 5000),
10499
- stderr: stderr.substring(0, 5000),
10511
+ error: boundedCommandOutput(stderr || message, 5000),
10512
+ stderr: boundedCommandOutput(stderr, 5000),
10500
10513
  exitCode: err.status ?? null,
10501
10514
  language: lang,
10502
10515
  ...classified,
@@ -10505,13 +10518,12 @@ async function _executeRunCode(args, cwd) {
10505
10518
  }
10506
10519
 
10507
10520
  const duration = Date.now() - start;
10508
- return {
10521
+ return Object.assign(boundedCodeOutput(output), {
10509
10522
  success: true,
10510
- ...boundedCodeOutput(output),
10511
10523
  language: lang,
10512
10524
  duration: `${duration}ms`,
10513
10525
  ...(persist ? { scriptPath } : {}),
10514
- };
10526
+ });
10515
10527
  } finally {
10516
10528
  // Only clean up if not persisting
10517
10529
  if (!persist) {
@@ -10546,14 +10558,9 @@ export const MAX_SUB_AGENT_DEPTH = 5;
10546
10558
  export const MAX_SUB_AGENTS_PER_RUN = 32;
10547
10559
 
10548
10560
  /**
10549
- * Per-tool-result character cap fed back to the model. One giant tool output (a
10550
- * huge file, a verbose command, an MCP blob) must not blow the context window —
10551
- * but the model is TOLD when output is cut, instead of the old silent
10552
- * `substring(0, 5000)` that sliced mid-content with no marker and undercut even
10553
- * read_file's own 50k self-limit. Tools that self-limit (read_file, notebook
10554
- * render) stay below this; it is the final safety net for the ones that don't
10555
- * (run_shell, search_files, run_code, MCP). CC_MAX_TOOL_RESULT_CHARS may only
10556
- * tighten the host ceiling; it cannot raise or disable it.
10561
+ * Total character cap fed back to the model, including truncation metadata.
10562
+ * Complete JSON previews retain status and small fields while omitting large
10563
+ * leaves. CC_MAX_TOOL_RESULT_CHARS may only tighten the host ceiling.
10557
10564
  */
10558
10565
  export const MAX_TOOL_RESULT_CHARS_HARD_LIMIT = 50000;
10559
10566
 
@@ -10569,47 +10576,38 @@ export const MAX_TOOL_RESULT_CHARS = resolveMaxToolResultChars(
10569
10576
  );
10570
10577
 
10571
10578
  /**
10572
- * Cap a serialized tool result to `max` chars, appending a visible truncation
10573
- * marker (with the original length + how to get the rest) when it overflows —
10574
- * so the model never silently receives a mid-content slice. Pure; exported for
10575
- * tests.
10579
+ * Cap a serialized tool result without splitting JSON values. Plain prefixes
10580
+ * are allowed only when they cannot contain a truncated structured span.
10576
10581
  */
10577
10582
  export function capToolResultString(serialized, max = MAX_TOOL_RESULT_CHARS) {
10578
10583
  const s = String(serialized ?? "");
10579
10584
  const effectiveMax = resolveMaxToolResultChars(max);
10580
- if (s.length <= effectiveMax) return s;
10581
- return (
10582
- s.slice(0, effectiveMax) +
10583
- `\n…[tool output truncated: showing the first ${effectiveMax} of ${s.length} chars` +
10584
- ` — narrow the request (read a line range, grep, or paginate) to see the rest]`
10585
- );
10585
+ return boundedToolResultText(s, effectiveMax);
10586
10586
  }
10587
10587
 
10588
10588
  /** Leave room for JSON escaping and execution metadata, including the hint. */
10589
10589
  export function boundedCodeOutput(value) {
10590
10590
  const output = String(value ?? "");
10591
10591
  const budget = Math.max(0, MAX_TOOL_RESULT_CHARS - 4096);
10592
- let low = 0;
10593
- let high = Math.min(output.length, 50000);
10594
- while (low < high) {
10595
- const mid = Math.ceil((low + high) / 2);
10596
- if (JSON.stringify(output.slice(0, mid)).length <= budget) low = mid;
10597
- else high = mid - 1;
10598
- }
10599
- if (low === output.length) return { output };
10600
- if (low > 0 && /[\uD800-\uDBFF]/.test(output[low - 1])) low--;
10601
- return {
10602
- truncated: true,
10603
- outputChars: output.length,
10604
- returnedChars: low,
10605
- hint: "Only an output prefix is shown. Do not rerun the same full-file print: filter/count locally and print a bounded summary with source line numbers, or use read_file with offset/limit for a missing section. This prefix is not evidence of the entire file.",
10606
- output: output.slice(0, low),
10607
- };
10592
+ if (JSON.stringify(output).length <= budget) return { output };
10593
+ const preview = boundedCommandOutput(output, Math.max(1, budget));
10594
+ return registerHostToolOutput(
10595
+ {
10596
+ truncated: true,
10597
+ outputChars: output.length,
10598
+ returnedChars: preview.length,
10599
+ hint: "Output is incomplete. Do not rerun the same full-file print: filter/count locally and print a bounded summary with source line numbers, or use read_file with offset/limit for a missing section. Omitted text is not evidence of the entire file.",
10600
+ output: preview,
10601
+ },
10602
+ output,
10603
+ );
10608
10604
  }
10609
10605
 
10610
10606
  /** Avoid injecting the same file page twice while its original is still visible. */
10611
10607
  export function toolResultForModel(tool, result, messages) {
10612
- const serialized = capToolResultString(safeStringifyToolResult(result));
10608
+ const serialized = capToolResultString(
10609
+ safeStringifyToolResult(safeHostToolResultForModel(tool, result)),
10610
+ );
10613
10611
  if (
10614
10612
  (tool === "run_code" || tool === "run_shell") &&
10615
10613
  !result?.error &&
@@ -3826,6 +3826,21 @@ async function runAgentHeadlessStreamInWorkspace(
3826
3826
  return false;
3827
3827
  }
3828
3828
  };
3829
+ const repairAndSettleToolRoundsForHost = (phase) => {
3830
+ const originalMessages = new Set(messages);
3831
+ const balanced = sanitizeToolPairs(messages);
3832
+ for (const message of balanced) {
3833
+ if (
3834
+ !originalMessages.has(message) &&
3835
+ message.role === "assistant" &&
3836
+ !message.tool_calls?.length
3837
+ )
3838
+ markRuntimeOnlySessionMessage(message);
3839
+ }
3840
+ messages.length = 0;
3841
+ messages.push(...balanced);
3842
+ return settleToolRoundsForHost(phase);
3843
+ };
3829
3844
  const loopOptionsBase = {
3830
3845
  ...(evolutionIngress === null ? {} : { evolutionIngress }),
3831
3846
  model,
@@ -5637,6 +5652,12 @@ async function runAgentHeadlessStreamInWorkspace(
5637
5652
  const turnWasAborted = currentAbort?.signal.aborted === true;
5638
5653
  currentAbort = null;
5639
5654
  if (err?.code === AGENT_EVOLUTION_INGRESS_FAILED_CODE) {
5655
+ evolutionIngressFailed = true;
5656
+ // Ingress can reject the next model request after a tool round has
5657
+ // already completed. Keep those completed effects in the canonical
5658
+ // session before terminating this Run; incomplete calls are removed
5659
+ // with the same pairing repair used for an interrupted turn.
5660
+ if (!repairAndSettleToolRoundsForHost("after-model")) break;
5640
5661
  emit({
5641
5662
  type: "result",
5642
5663
  subtype: "error_evolution_ingress",
@@ -5646,7 +5667,6 @@ async function runAgentHeadlessStreamInWorkspace(
5646
5667
  turn: turns,
5647
5668
  });
5648
5669
  sawError = true;
5649
- evolutionIngressFailed = true;
5650
5670
  break;
5651
5671
  }
5652
5672
  if (persist) {
@@ -5677,6 +5697,7 @@ async function runAgentHeadlessStreamInWorkspace(
5677
5697
  break;
5678
5698
  }
5679
5699
  if (err?.runtimeLedgerPersistence === true) {
5700
+ if (!repairAndSettleToolRoundsForHost("after-model")) break;
5680
5701
  emit({
5681
5702
  type: "result",
5682
5703
  subtype: "error_persistence",
@@ -5693,6 +5714,7 @@ async function runAgentHeadlessStreamInWorkspace(
5693
5714
  err?.code === "CC_SESSION_BUDGET_USAGE_UNKNOWN" ||
5694
5715
  options.sessionBudget?.signal?.aborted === true
5695
5716
  ) {
5717
+ if (!repairAndSettleToolRoundsForHost("after-model")) break;
5696
5718
  const budgetReason =
5697
5719
  options.sessionBudget?.reason?.() ||
5698
5720
  err.budgetReason ||
@@ -5729,21 +5751,7 @@ async function runAgentHeadlessStreamInWorkspace(
5729
5751
  // the array length unchanged. sanitizeToolPairs is idempotent on an
5730
5752
  // already-balanced array and leaves user/text turns untouched, so the
5731
5753
  // intentional dangling-user state above is preserved.
5732
- const originalMessages = new Set(messages);
5733
- const balanced = sanitizeToolPairs(messages);
5734
- // Only the sanitizer's newly-created commentary for discarded calls
5735
- // is ephemeral. Existing real messages retain their authority.
5736
- for (const message of balanced) {
5737
- if (
5738
- !originalMessages.has(message) &&
5739
- message.role === "assistant" &&
5740
- !message.tool_calls?.length
5741
- )
5742
- markRuntimeOnlySessionMessage(message);
5743
- }
5744
- messages.length = 0;
5745
- messages.push(...balanced);
5746
- if (!settleToolRoundsForHost("after-model")) break;
5754
+ if (!repairAndSettleToolRoundsForHost("after-model")) break;
5747
5755
  emit({
5748
5756
  type: "result",
5749
5757
  subtype: "interrupted",
@@ -5754,7 +5762,12 @@ async function runAgentHeadlessStreamInWorkspace(
5754
5762
  });
5755
5763
  continue;
5756
5764
  }
5757
- if (isAbort) break; // outer options.signal — caller is shutting down
5765
+ if (isAbort) {
5766
+ // Caller shutdown terminates the stream, but completed tool effects
5767
+ // from the in-flight turn still belong in the durable transcript.
5768
+ if (!repairAndSettleToolRoundsForHost("after-model")) break;
5769
+ break;
5770
+ }
5758
5771
  if (!settleToolRoundsForHost("after-model")) break;
5759
5772
  const transportDetails = transportErrorDetails(err);
5760
5773
  emit({
@@ -5771,6 +5784,7 @@ async function runAgentHeadlessStreamInWorkspace(
5771
5784
  persistTurnBindingLog();
5772
5785
  }
5773
5786
  if (turnBindingPersistenceError) {
5787
+ if (!repairAndSettleToolRoundsForHost("after-model")) break;
5774
5788
  emitTurnBindingFailure();
5775
5789
  sawError = true;
5776
5790
  break;
@@ -5792,6 +5806,7 @@ async function runAgentHeadlessStreamInWorkspace(
5792
5806
  }
5793
5807
 
5794
5808
  if (outcome.endReason === "compaction-usage-unknown") {
5809
+ if (!repairAndSettleToolRoundsForHost("after-model")) break;
5795
5810
  sawError = true;
5796
5811
  emit({
5797
5812
  type: "result",
@@ -1 +0,0 @@
1
- import{P as c,J as p,b as a}from"./vendor-Bn1JzzAn.js";import{r as s}from"./markdown-Yo_c6Krv.js";import{_ as d}from"./index-Bhf-MuUh.js";const _={__name:"MarkdownRenderer",props:{content:{type:String,default:""}},setup(t,{expose:o}){o();const e=t,r=a(()=>s(e.content||"")),n={props:e,rendered:r,computed:a,get renderMarkdown(){return s}};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}},i=["innerHTML"];function m(t,o,e,r,n,u){return c(),p("div",{class:"markdown-content",innerHTML:r.rendered},null,8,i)}const w=d(_,[["render",m],["__scopeId","data-v-0b0b9f63"],["__file","/tmp/cc-web-panel-jIIa0G/repo/packages/web-panel/src/components/MarkdownRenderer.vue"]]);export{w as M};