chainlesschain 0.166.115 → 0.166.118

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 (207) hide show
  1. package/README.md +43 -5
  2. package/package.json +4 -4
  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/.build-hash +1 -1
  6. package/src/assets/web-panel/assets/{AIOps-BsoSoDNb.js → AIOps-BGySwMIP.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-Dcx7Gm5t.js → ActionButton-Chn7fBLr.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-S1hsBIz3.js → Analytics-ORH7UkGa.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-B2j067qM.js → AppLayout-8xV4jWjP.js} +5 -5
  10. package/src/assets/web-panel/assets/{Artifacts-D5iGV9ic.js → Artifacts-26SBK-x-.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-C8ChVvXu.js → Audit-CtXpqbXg.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-Cg97AAJS.js → BackgroundAgents-C5zLvQis.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-CyxU9IUg.js → Backup-C93xY4vO.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-CKlWjA8I.js → BaseInput-DpVCHfAn.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-C-EqZza6.js → Chat-DM0LblR2.js} +5 -5
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BRFiXKPX.js → ChatBubbleRenderer-unlQUF1T.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-kkI13tSJ.js → Checkbox-BsAt3cKR.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-NlqEDwHK.js → Codegen-E_kp4KFd.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-CfepEu79.js → Col-6msR3T6I.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-BRo0qLXM.js → Community-PyX9c5D-.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-BMu8TP0E.js → Compact-7G1bVDSH.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-B7Q3Moef.js → Compliance-DcHN7GlD.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-BVwt7Xmc.js → Cowork-WUIHJYDx.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-CgBv20Qy.js → Cron-CWgCbptg.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-CwcSBJk2.js → Crosschain-BzKZ6roD.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-d20rCiIm.js → DID-r7zxeItn.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-PpZaUYpS.js → Dashboard-D3pBiN2H.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-DjsLdX_Z.js → Dropdown-C4otRJlZ.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-CaPkrtxn.js → EmailListRenderer-CzDTC8lm.js} +1 -1
  30. package/src/assets/web-panel/assets/{EvolutionSettings-DDY8cQnE.js → EvolutionSettings-DGkXiIs-.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard-VQavMjyj.js → FamilyGuardDashboard-BKdEyuiR.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-D0GvKQgI.js → Federation-dsI-xcpf.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-Dl_TWHmb.js → FormItemContext-CEEb1fMR.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-Bny2uagG.js → GenericCardRenderer-D_mfO3Ts.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-B01KXq47.js → Git-CBwr2ZI3.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-F4c4Vwo1.js → Governance-D-Wy2Jjr.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-_025Q-8K.js → Inference-B4Yk_IRx.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-DwbTmQb1.js → KnowledgeGraph-DoI7bi6m.js} +1 -1
  39. package/src/assets/web-panel/assets/{Logs-CnNo0hCm.js → Logs-Ce1IiKAj.js} +2 -2
  40. package/src/assets/web-panel/assets/MarkdownRenderer-GND_8y0V.js +1 -0
  41. package/src/assets/web-panel/assets/{Marketplace-Dkz5bv7F.js → Marketplace-CsIEByzK.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-CBtRwDW_.js → McpTools-_DRzjcIn.js} +3 -3
  43. package/src/assets/web-panel/assets/{Memory-Y9jnyTb7.js → Memory-zvLpyi__.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-Br1urkOz.js → MobileBridge-DXd1ik2Z.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-DnsyuerX.js → MobileProjects-DKh2q-Y9.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-he3ZzieF.js → Mtc-Ct-fhtIx.js} +4 -4
  47. package/src/assets/web-panel/assets/{MtcAudit-BadphAuN.js → MtcAudit-J4zY3Oqk.js} +2 -2
  48. package/src/assets/web-panel/assets/{Multisig-3_pVLymK.js → Multisig-B2HJMCKe.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-DmtmHfsI.js → NLProgramming-CwcSmU4q.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-CE4L2O55.js → Notes-Da53L6wb.js} +4 -4
  51. package/src/assets/web-panel/assets/{NotificationSettings-B7QmfJn9.js → NotificationSettings-BxhufdrA.js} +1 -1
  52. package/src/assets/web-panel/assets/{OrderTableRenderer-O0wRSyJ0.js → OrderTableRenderer-BoFwohAi.js} +1 -1
  53. package/src/assets/web-panel/assets/{Organization-BrlMSsyT.js → Organization-MIsMrUes.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-CY4ULPrZ.js → Overflow-Dy2P4v3q.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-kIANZYKd.js → P2P-CD9Sxn2y.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-DEgB4rvF.js → PdhVaultBrowser-BvBWgfG0.js} +5 -5
  57. package/src/assets/web-panel/assets/{Permissions-CWxywRp5.js → Permissions-CwOlDRis.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-DAg7iCbr.js → PersonalDataHub-CZYaxEpv.js} +4 -4
  59. package/src/assets/web-panel/assets/{Pipeline-Brc6ONnR.js → Pipeline-kLU7PUbB.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-CcinYTL8.js → Privacy-BVRycSiI.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-Bz8XEsom.js → ProjectInit-DP3pr7uJ.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-CsOMlSTZ.js → ProjectSettings-CACaDYjs.js} +2 -2
  63. package/src/assets/web-panel/assets/Projects-CB1k4zTC.js +1 -0
  64. package/src/assets/web-panel/assets/{Providers-CeunOQiH.js → Providers-D7Y9WHRE.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-CN2a2_Fb.js → QrScannerModal-CeP8xzs1.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-CVeLif3e.js → QuickAsk-iJVFrhZ2.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-1k9WOEGM.js → Recommend-DmTpnagw.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-Q8UCqIMw.js → RemoteSession-C6ek8Uni.js} +2 -2
  69. package/src/assets/web-panel/assets/{Reputation-CkrCgXKM.js → Reputation-CPfwc9de.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-JFdvuTzi.js → Row-C1wwtSMJ.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-C74JHCmk.js → RssFeed-BiDJsqaH.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-DrJiVO1T.js → Search-BF6Aon95.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-BJzNlSJq.js → Security-Bps0XnV4.js} +3 -3
  74. package/src/assets/web-panel/assets/{Services-D4TLMnJm.js → Services-uf92smGR.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-cswTDqhh.js → Skeleton-DQd4eVWW.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-Bv0rM1BS.js → Skills-DgamiYa5.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-D9rE7hNh.js → Sla-CF1IVIrm.js} +1 -1
  78. package/src/assets/web-panel/assets/SpeechSettings-Bzxmjbpc.js +1 -0
  79. package/src/assets/web-panel/assets/{SyncSettings-BwnMG8kq.js → SyncSettings-B8zKauiq.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-wJvU24cB.js → Tasks-DPKV_xSt.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-CZbRU3tN.js → Templates-BikeSZIF.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-_jbpFJPZ.js → Tenant-CEaUaDkJ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-BzNm5WKZ.js → Terminal-ZPgOSQ_q.js} +3 -3
  84. package/src/assets/web-panel/assets/{TimelineRenderer-bmybWjuS.js → TimelineRenderer-DmyB4Udk.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-o1WDcqVQ.js → Tokens-g85Pm5TO.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-BB1D6sIB.js → Trigger-D202hx9a.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-CLxdCfy2.js → Trust-DzvQYVFy.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-B9LS6vS0.js → UkeySign-KRzwddVg.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-Bg0-wxjz.js → VideoEditing-IRBXLO2z.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-Bw0G2F6P.js → Wallet-DSf5KPgQ.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-irEiNY9b.js → WebAuthn-DNPxLTCc.js} +5 -5
  92. package/src/assets/web-panel/assets/{WorkflowEditor-Bh08FbJs.js → WorkflowEditor-C3wAtVcK.js} +1 -1
  93. package/src/assets/web-panel/assets/{addon-fit-CK6X9sAG.js → addon-fit-CbkjSUJl.js} +1 -1
  94. package/src/assets/web-panel/assets/{chat-De-FPjVT.js → chat-q3XXUtv2.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-ygdyvinU.js → colors-CF54SNei.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-BQ9jH9KV.js → compact-item-BaRkvZuT.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-BFuVSyox.js → createContext-CDR9hsfs.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-1LO-NplX.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-i19GtR1P.js → hasIn-DlC--S8R.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DS1IwwGL.js → index-B0Ptc7yu.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B1IsP_JO.js → index-B0V9rgdu.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CWfgeaG4.js → index-BEiUlFKb.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DTY20pWu.js → index-BJO0n1LQ.js} +1 -1
  104. package/src/assets/web-panel/assets/index-BLaJsev4.js +1 -0
  105. package/src/assets/web-panel/assets/index-BLmviChG.js +1 -0
  106. package/src/assets/web-panel/assets/{index-IXkif6J_.js → index-BU5DJqk7.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BVhtukYX.js → index-BY-Tf3OM.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BKlU5VZy.js → index-BZrE0Go8.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bw5pTpnI.js → index-BizdX2X5.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BGMxK155.js → index-C87Yr3h1.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DVnFgasM.js → index-C9Wypz9e.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C61w3omz.js → index-CAoG_E-q.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-l5-J0jHx.js → index-CAxSyMrF.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-28DjsABm.js → index-CC5HjNM6.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BPUCvc8i.js → index-CNZQEZ9g.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CpdXhhMY.js → index-CPvIhha6.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DAboGZTw.js → index-CR_wqMGy.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DL7ewjD2.js → index-CSClXoB3.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Bhf-MuUh.js → index-CZ8azVgB.js} +3 -3
  120. package/src/assets/web-panel/assets/{index-BaaydJcC.js → index-Ccc5z8eJ.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-B2LieObB.js → index-CeiKbM3a.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-We2nK3ju.js → index-Cl6av3X7.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-m8waSAyv.js → index-CtGfeD7E.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-cDJsyAkI.js → index-Ctid12Gq.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DlwxUZQL.js → index-D0H-N0uG.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BBYiCI1d.js → index-D0vIgprF.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-_9tUBcLm.js → index-D4wUDFRg.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-4HoFz1MA.js → index-DJFipyK7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Cq9k4-Ws.js → index-DX2REKNp.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DuuOX5u3.js → index-Dc3gD0cw.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DgjLaB7k.js → index-DiQzer2R.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CMNjaSir.js → index-DntKLOs9.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-vXtH3o6O.js → index-Dplhl6gh.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BLMtCKAY.js → index-Dq8q_PWB.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CXrk8OYO.js → index-IVQdOBNN.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CBVcGaxk.js → index-Kerduyao.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-BKblSCL9.js → index-W6cLGjpW.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-CgoSgmjK.js → index-qZDNFll9.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-B_EZHeqN.js → initDefaultProps-Ccz7v71u.js} +1 -1
  140. package/src/assets/web-panel/assets/{markdown-Yo_c6Krv.js → markdown-Bq2x0XsR.js} +1 -1
  141. package/src/assets/web-panel/assets/markdown-DUKxbhE4.js +51 -0
  142. package/src/assets/web-panel/assets/{motion-BrVb_Gy1.js → motion-BlCLpVDp.js} +1 -1
  143. package/src/assets/web-panel/assets/{move-CKxVRL_J.js → move-B80G7fLU.js} +1 -1
  144. package/src/assets/web-panel/assets/{mtc-parser-C80rvQ1J.js → mtc-parser-Djw08NJh.js} +1 -1
  145. package/src/assets/web-panel/assets/{omit-De5vD7BR.js → omit-Bg_G1eUr.js} +1 -1
  146. package/src/assets/web-panel/assets/{pickAttrs-BTyEDjth.js → pickAttrs-Df9zYUg2.js} +1 -1
  147. package/src/assets/web-panel/assets/{placementArrow-BWR94AjR.js → placementArrow-DjCDLhJ4.js} +1 -1
  148. package/src/assets/web-panel/assets/{responsiveObserve-1g-Vnibk.js → responsiveObserve--Naz7NgY.js} +1 -1
  149. package/src/assets/web-panel/assets/{slide-Dmg6dYuF.js → slide-CHn3ald-.js} +1 -1
  150. package/src/assets/web-panel/assets/{statusUtils-Ddj6rSab.js → statusUtils-BiGwoIK_.js} +1 -1
  151. package/src/assets/web-panel/assets/{styleChecker-DyP1mPDf.js → styleChecker-Zf2dQx1j.js} +1 -1
  152. package/src/assets/web-panel/assets/{useFlexGapSupport-D3d_0YuY.js → useFlexGapSupport-sLES8nyD.js} +1 -1
  153. package/src/assets/web-panel/assets/{useFs-M3vq-gE2.js → useFs-DV1Hl4qu.js} +1 -1
  154. package/src/assets/web-panel/assets/{usePersonalDataHub-GtP2TgtK.js → usePersonalDataHub-CZXr2Qxg.js} +1 -1
  155. package/src/assets/web-panel/assets/{vnode-BurE2ATQ.js → vnode-e8Eo3Oyl.js} +1 -1
  156. package/src/assets/web-panel/assets/{xterm-BZcWGsqw.js → xterm-zbskxxGA.js} +1 -1
  157. package/src/assets/web-panel/assets/{zoom-Ch-ViiDe.js → zoom-B8A6xKMF.js} +1 -1
  158. package/src/assets/web-panel/index.html +1 -1
  159. package/src/commands/compact.js +7 -2
  160. package/src/data/changelog.json +27 -1
  161. package/src/harness/jsonl-session-store.js +80 -74
  162. package/src/harness/prompt-compressor.js +57 -3
  163. package/src/lib/artifact-store.js +129 -20
  164. package/src/lib/bounded-artifact-file-read.js +128 -46
  165. package/src/lib/context-memory-kernel/context-image-transport.js +248 -0
  166. package/src/lib/context-memory-kernel/jsonl-session-context-port.js +4 -1
  167. package/src/lib/context-memory-kernel/live-compaction.js +40 -10
  168. package/src/lib/context-memory-kernel/message-adapter.js +80 -10
  169. package/src/lib/context-memory-kernel/message-bundle-store.js +1140 -0
  170. package/src/lib/context-memory-kernel/provider-context.js +23 -8
  171. package/src/lib/evolution/agent-evolution-ingress.js +103 -19
  172. package/src/lib/evolution/agent-evolution-runtime-composition.js +65 -27
  173. package/src/lib/evolution/agent-model-projection.js +90 -18
  174. package/src/lib/evolution/evidence-json-text.js +341 -42
  175. package/src/lib/evolution/evolution-artifact-ports.js +1005 -83
  176. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +59 -15
  177. package/src/lib/evolution/evolution-evidence-projector.js +153 -16
  178. package/src/lib/evolution/evolution-evidence-storage-layout.js +115 -0
  179. package/src/lib/evolution/evolution-file-witness.js +7 -3
  180. package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +6 -2
  181. package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +6 -2
  182. package/src/lib/evolution/evolution-ledger.js +226 -55
  183. package/src/lib/evolution/evolution-private-storage.js +20 -0
  184. package/src/lib/evolution/evolution-run-ledger-adapter.js +298 -11
  185. package/src/lib/evolution/evolution-windows-directory-durability.js +215 -32
  186. package/src/lib/evolution/local-ui-evolution-runtime.js +3 -38
  187. package/src/lib/evolution/local-ui-signing-authority.js +69 -0
  188. package/src/lib/image-token-estimate.js +59 -0
  189. package/src/lib/ollama-provider-fetch.js +33 -0
  190. package/src/lib/provider-stream.js +14 -13
  191. package/src/lib/read-file-loop-guard.js +7 -4
  192. package/src/lib/session-history-origins.js +94 -35
  193. package/src/lib/session-message-provenance.js +26 -2
  194. package/src/lib/session-runtime-retention.js +10 -1
  195. package/src/lib/session-transcript-history.js +5 -1
  196. package/src/lib/session-transcript-migration.js +67 -12
  197. package/src/lib/storage-failure-cause-codes.js +449 -0
  198. package/src/lib/tool-result-preview.js +276 -0
  199. package/src/runtime/agent-core.js +111 -92
  200. package/src/runtime/headless-stream.js +121 -27
  201. package/src/assets/web-panel/assets/MarkdownRenderer-D8_iZief.js +0 -1
  202. package/src/assets/web-panel/assets/Projects-BeSOM6wi.js +0 -1
  203. package/src/assets/web-panel/assets/SpeechSettings-Bfhvin8B.js +0 -1
  204. package/src/assets/web-panel/assets/devWarning-c2I2Rn4_.js +0 -1
  205. package/src/assets/web-panel/assets/index-CTs4dYcV.js +0 -1
  206. package/src/assets/web-panel/assets/index-DeuvaW15.js +0 -1
  207. package/src/assets/web-panel/assets/markdown-CsiA8-E5.js +0 -50
@@ -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
+ }
@@ -4,6 +4,7 @@ import {
4
4
  } from "../lib/formal-budget-provider-scope.js";
5
5
  import { isFormalBudgetError } from "../lib/formal-budget-errors.js";
6
6
  import { normalizeOllamaBaseUrl } from "../lib/ollama-base-url.js";
7
+ import { ollamaProviderFetch } from "../lib/ollama-provider-fetch.js";
7
8
  import {
8
9
  normalizeProviderBaseUrl,
9
10
  openAICompatibleBaseUrl,
@@ -56,6 +57,13 @@ import { ReadFileLoopGuard } from "../lib/read-file-loop-guard.js";
56
57
  const READ_FILE_REQUEST_CONTEXT = Symbol("read-file-request-context");
57
58
  import { gitToolInputError } from "../lib/git-tool-input.js";
58
59
  import { shellOutputPage, shellOutputPreview } from "../lib/shell-output.js";
60
+ import {
61
+ boundedToolResultText,
62
+ boundedCommandOutput,
63
+ safeHostToolResultForModel,
64
+ registerHostToolOutput,
65
+ copyHostToolOutputIdentity,
66
+ } from "../lib/tool-result-preview.js";
59
67
  import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "../lib/diagnostic-workflow.js";
60
68
  import { CODING_TASK_QUALITY_GUIDANCE } from "../lib/coding-task-guidance.js";
61
69
  import { RemoteReadLoopGuard } from "../lib/remote-read-loop-guard.js";
@@ -5741,7 +5749,12 @@ async function executeToolInner(
5741
5749
  const descriptorPayload = buildPayload(runtimeDescriptor);
5742
5750
  const attachDescriptor = (payload, overrideDescriptor = null) => {
5743
5751
  const descriptor = buildPayload(overrideDescriptor || runtimeDescriptor);
5744
- return descriptor ? { ...payload, toolDescriptor: descriptor } : payload;
5752
+ return descriptor
5753
+ ? copyHostToolOutputIdentity(
5754
+ { ...payload, toolDescriptor: descriptor },
5755
+ payload,
5756
+ )
5757
+ : payload;
5745
5758
  };
5746
5759
  const localToolExecutor =
5747
5760
  externalToolExecutors && typeof externalToolExecutors === "object"
@@ -7706,8 +7719,8 @@ async function executeToolInner(
7706
7719
  ) {
7707
7720
  return attachDescriptor({
7708
7721
  success: true,
7709
- stdout: String(res.stdout || "").substring(0, 30000),
7710
- stderr: String(res.stderr || "").substring(0, 2000),
7722
+ ...shellOutputPreview(res.stdout, "stdout", 30000),
7723
+ ...shellOutputPreview(res.stderr, "stderr", 2000),
7711
7724
  exitCode: res.status,
7712
7725
  command: normalizedCommand,
7713
7726
  readOnly: true,
@@ -7716,11 +7729,13 @@ async function executeToolInner(
7716
7729
  });
7717
7730
  }
7718
7731
  if (res.status !== 0) {
7719
- const stderr = String(res.stderr || "").substring(0, 2000);
7732
+ const stderr = String(res.stderr || "");
7720
7733
  return attachDescriptor({
7721
- error: stderr || `git exited with code ${res.status}`,
7722
- stdout: String(res.stdout || "").substring(0, 30000),
7723
- stderr,
7734
+ error:
7735
+ boundedCommandOutput(stderr, 2000) ||
7736
+ `git exited with code ${res.status}`,
7737
+ ...shellOutputPreview(res.stdout, "stdout", 30000),
7738
+ ...shellOutputPreview(stderr, "stderr", 2000),
7724
7739
  exitCode: res.status,
7725
7740
  command: normalizedCommand,
7726
7741
  readOnly,
@@ -7734,7 +7749,7 @@ async function executeToolInner(
7734
7749
  });
7735
7750
  }
7736
7751
  return attachDescriptor({
7737
- stdout: String(res.stdout || "").substring(0, 30000),
7752
+ ...shellOutputPreview(res.stdout, "stdout", 30000),
7738
7753
  exitCode: 0,
7739
7754
  command: normalizedCommand,
7740
7755
  readOnly,
@@ -10332,8 +10347,8 @@ async function _executeRunCode(args, cwd) {
10332
10347
  outcome: "disabled",
10333
10348
  });
10334
10349
  return {
10335
- error: (stderr || message).substring(0, 5000),
10336
- stderr: stderr.substring(0, 5000),
10350
+ error: boundedCommandOutput(stderr || message, 5000),
10351
+ stderr: boundedCommandOutput(stderr, 5000),
10337
10352
  exitCode: err.status ?? null,
10338
10353
  language: lang,
10339
10354
  ...classified,
@@ -10349,8 +10364,8 @@ async function _executeRunCode(args, cwd) {
10349
10364
  outcome: "blocked",
10350
10365
  });
10351
10366
  return {
10352
- error: (stderr || message).substring(0, 5000),
10353
- stderr: stderr.substring(0, 5000),
10367
+ error: boundedCommandOutput(stderr || message, 5000),
10368
+ stderr: boundedCommandOutput(stderr, 5000),
10354
10369
  exitCode: err.status ?? null,
10355
10370
  language: lang,
10356
10371
  ...classified,
@@ -10405,8 +10420,8 @@ async function _executeRunCode(args, cwd) {
10405
10420
  ),
10406
10421
  );
10407
10422
  return {
10408
- error: (stderr || message).substring(0, 5000),
10409
- stderr: stderr.substring(0, 5000),
10423
+ error: boundedCommandOutput(stderr || message, 5000),
10424
+ stderr: boundedCommandOutput(stderr, 5000),
10410
10425
  exitCode: err.status ?? null,
10411
10426
  language: lang,
10412
10427
  ...classified,
@@ -10462,14 +10477,13 @@ async function _executeRunCode(args, cwd) {
10462
10477
  );
10463
10478
  const retryDuration = Date.now() - retryStart;
10464
10479
 
10465
- return {
10480
+ return Object.assign(boundedCodeOutput(retryOutput), {
10466
10481
  success: true,
10467
- ...boundedCodeOutput(retryOutput),
10468
10482
  language: lang,
10469
10483
  duration: `${retryDuration}ms`,
10470
10484
  autoInstalled: [packageName],
10471
10485
  ...(persist ? { scriptPath } : {}),
10472
- };
10486
+ });
10473
10487
  } catch (retryErr) {
10474
10488
  rethrowRunCodeSandboxFailure(retryErr);
10475
10489
  const retryStderr = (retryErr.stderr || "").toString();
@@ -10481,8 +10495,8 @@ async function _executeRunCode(args, cwd) {
10481
10495
  lang,
10482
10496
  );
10483
10497
  return {
10484
- error: (retryStderr || retryMessage).substring(0, 5000),
10485
- stderr: retryStderr.substring(0, 5000),
10498
+ error: boundedCommandOutput(retryStderr || retryMessage, 5000),
10499
+ stderr: boundedCommandOutput(retryStderr, 5000),
10486
10500
  exitCode: retryErr.status ?? null,
10487
10501
  language: lang,
10488
10502
  ...retryClassified,
@@ -10495,8 +10509,8 @@ async function _executeRunCode(args, cwd) {
10495
10509
  }
10496
10510
 
10497
10511
  return {
10498
- error: (stderr || message).substring(0, 5000),
10499
- stderr: stderr.substring(0, 5000),
10512
+ error: boundedCommandOutput(stderr || message, 5000),
10513
+ stderr: boundedCommandOutput(stderr, 5000),
10500
10514
  exitCode: err.status ?? null,
10501
10515
  language: lang,
10502
10516
  ...classified,
@@ -10505,13 +10519,12 @@ async function _executeRunCode(args, cwd) {
10505
10519
  }
10506
10520
 
10507
10521
  const duration = Date.now() - start;
10508
- return {
10522
+ return Object.assign(boundedCodeOutput(output), {
10509
10523
  success: true,
10510
- ...boundedCodeOutput(output),
10511
10524
  language: lang,
10512
10525
  duration: `${duration}ms`,
10513
10526
  ...(persist ? { scriptPath } : {}),
10514
- };
10527
+ });
10515
10528
  } finally {
10516
10529
  // Only clean up if not persisting
10517
10530
  if (!persist) {
@@ -10546,14 +10559,9 @@ export const MAX_SUB_AGENT_DEPTH = 5;
10546
10559
  export const MAX_SUB_AGENTS_PER_RUN = 32;
10547
10560
 
10548
10561
  /**
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.
10562
+ * Total character cap fed back to the model, including truncation metadata.
10563
+ * Complete JSON previews retain status and small fields while omitting large
10564
+ * leaves. CC_MAX_TOOL_RESULT_CHARS may only tighten the host ceiling.
10557
10565
  */
10558
10566
  export const MAX_TOOL_RESULT_CHARS_HARD_LIMIT = 50000;
10559
10567
 
@@ -10569,47 +10577,38 @@ export const MAX_TOOL_RESULT_CHARS = resolveMaxToolResultChars(
10569
10577
  );
10570
10578
 
10571
10579
  /**
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.
10580
+ * Cap a serialized tool result without splitting JSON values. Plain prefixes
10581
+ * are allowed only when they cannot contain a truncated structured span.
10576
10582
  */
10577
10583
  export function capToolResultString(serialized, max = MAX_TOOL_RESULT_CHARS) {
10578
10584
  const s = String(serialized ?? "");
10579
10585
  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
- );
10586
+ return boundedToolResultText(s, effectiveMax);
10586
10587
  }
10587
10588
 
10588
10589
  /** Leave room for JSON escaping and execution metadata, including the hint. */
10589
10590
  export function boundedCodeOutput(value) {
10590
10591
  const output = String(value ?? "");
10591
10592
  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
- };
10593
+ if (JSON.stringify(output).length <= budget) return { output };
10594
+ const preview = boundedCommandOutput(output, Math.max(1, budget));
10595
+ return registerHostToolOutput(
10596
+ {
10597
+ truncated: true,
10598
+ outputChars: output.length,
10599
+ returnedChars: preview.length,
10600
+ 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.",
10601
+ output: preview,
10602
+ },
10603
+ output,
10604
+ );
10608
10605
  }
10609
10606
 
10610
10607
  /** Avoid injecting the same file page twice while its original is still visible. */
10611
10608
  export function toolResultForModel(tool, result, messages) {
10612
- const serialized = capToolResultString(safeStringifyToolResult(result));
10609
+ const serialized = capToolResultString(
10610
+ safeStringifyToolResult(safeHostToolResultForModel(tool, result)),
10611
+ );
10613
10612
  if (
10614
10613
  (tool === "run_code" || tool === "run_shell") &&
10615
10614
  !result?.error &&
@@ -12215,7 +12214,7 @@ export async function chatWithTools(rawMessages, options) {
12215
12214
  },
12216
12215
  );
12217
12216
  }
12218
- const response = await providerFetch(apiUrl, {
12217
+ const response = await ollamaProviderFetch(apiUrl, {
12219
12218
  method: "POST",
12220
12219
  headers: { "Content-Type": "application/json" },
12221
12220
  signal,
@@ -12624,7 +12623,12 @@ export async function chatWithTools(rawMessages, options) {
12624
12623
  throw new TypeError("Invalid API response: unsupported assistant message");
12625
12624
  }
12626
12625
  // Provider reasoning text is not part of the governed replay protocol.
12627
- const message = { role: "assistant", content: providerMessage.content };
12626
+ // Tool-only responses may omit content. Keep the live message JSON-safe for
12627
+ // canonical tool-round settlement instead of introducing an undefined field.
12628
+ const message = {
12629
+ role: "assistant",
12630
+ content: providerMessage.content ?? null,
12631
+ };
12628
12632
  if (Object.hasOwn(providerMessage, "tool_calls")) {
12629
12633
  message.tool_calls = providerMessage.tool_calls;
12630
12634
  }
@@ -13055,7 +13059,7 @@ async function _chatOllamaStreaming(
13055
13059
  stallMs,
13056
13060
  stallTimeoutMs,
13057
13061
  ) {
13058
- const response = await providerFetch(apiUrl, {
13062
+ const response = await ollamaProviderFetch(apiUrl, {
13059
13063
  method: "POST",
13060
13064
  headers: { "Content-Type": "application/json" },
13061
13065
  signal,
@@ -15284,38 +15288,53 @@ export async function* agentLoop(messages, options) {
15284
15288
  typeof options.runtimeResultRetention === "object"
15285
15289
  ? options.runtimeResultRetention
15286
15290
  : options.runtimeResultRetentionOptions;
15287
- const retained = releaseOldLiveSessionResults(messages, retentionConfig);
15291
+ const candidate = releaseOldLiveSessionResults(messages, retentionConfig);
15288
15292
  if (
15289
- retained.stats.released > 0 &&
15290
- !_sameMessageSnapshot(retained.messages, messages)
15293
+ candidate.stats.released > 0 &&
15294
+ !_sameMessageSnapshot(candidate.messages, messages)
15291
15295
  ) {
15292
- const authorityExpectedMessages = [...messages];
15293
- const liveExpectedMessages = [...messages];
15294
- try {
15295
- await _settleAutomaticCompaction({
15296
- messages,
15297
- liveExpectedMessages,
15298
- authorityExpectedMessages,
15299
- compacted: retained.messages,
15300
- stats: retained.stats,
15301
- options,
15302
- trigger: "runtime-retention",
15303
- });
15304
- yield {
15305
- type: "session-runtime-retention",
15306
- runId,
15307
- stats: retained.stats,
15308
- };
15309
- } catch (error) {
15310
- yield {
15311
- type: "session-runtime-retention-degraded",
15312
- runId,
15313
- reason:
15314
- error?.code === "SESSION_REVISION_STALE"
15315
- ? "session_messages_changed_during_retention"
15316
- : "canonical_retention_settlement_failed",
15317
- code: error?.code || "CC_SESSION_RETENTION_SETTLEMENT_FAILED",
15318
- };
15296
+ // Complete tool effects must be durable before a retention checkpoint
15297
+ // can compare against them. Let a host settlement failure propagate
15298
+ // before another provider call, just as semantic compaction does.
15299
+ options.onBeforeCompaction?.();
15300
+ // The host hook may update live context. Derive both the candidate and
15301
+ // the expected snapshots from the state after that boundary.
15302
+ const retained = releaseOldLiveSessionResults(
15303
+ messages,
15304
+ retentionConfig,
15305
+ );
15306
+ if (
15307
+ retained.stats.released > 0 &&
15308
+ !_sameMessageSnapshot(retained.messages, messages)
15309
+ ) {
15310
+ const authorityExpectedMessages = [...messages];
15311
+ const liveExpectedMessages = [...messages];
15312
+ try {
15313
+ await _settleAutomaticCompaction({
15314
+ messages,
15315
+ liveExpectedMessages,
15316
+ authorityExpectedMessages,
15317
+ compacted: retained.messages,
15318
+ stats: retained.stats,
15319
+ options,
15320
+ trigger: "runtime-retention",
15321
+ });
15322
+ yield {
15323
+ type: "session-runtime-retention",
15324
+ runId,
15325
+ stats: retained.stats,
15326
+ };
15327
+ } catch (error) {
15328
+ yield {
15329
+ type: "session-runtime-retention-degraded",
15330
+ runId,
15331
+ reason:
15332
+ error?.code === "SESSION_REVISION_STALE"
15333
+ ? "session_messages_changed_during_retention"
15334
+ : "canonical_retention_settlement_failed",
15335
+ code: error?.code || "CC_SESSION_RETENTION_SETTLEMENT_FAILED",
15336
+ };
15337
+ }
15319
15338
  }
15320
15339
  }
15321
15340
  }