chainlesschain 0.166.76 → 0.166.78

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 (230) hide show
  1. package/README.md +19 -8
  2. package/completions/_cc +1 -1
  3. package/completions/cc.bash +1 -1
  4. package/completions/cc.fish +1 -1
  5. package/completions/cc.ps1 +1 -1
  6. package/package.json +1 -1
  7. package/src/assets/linux-subreaper/linux-arm64/manifest.json +10 -0
  8. package/src/assets/linux-subreaper/linux-arm64/supervisor +0 -0
  9. package/src/assets/linux-subreaper/linux-x64/manifest.json +10 -0
  10. package/src/assets/linux-subreaper/linux-x64/supervisor +0 -0
  11. package/src/assets/web-panel/assets/{AIOps-CNaejClV.js → AIOps-DSyZEY2G.js} +1 -1
  12. package/src/assets/web-panel/assets/{ActionButton-CF_CVKT_.js → ActionButton-DhNyZ577.js} +1 -1
  13. package/src/assets/web-panel/assets/{Analytics-a3Z6J7gv.js → Analytics-HMMpxmyi.js} +3 -3
  14. package/src/assets/web-panel/assets/{AppLayout-BFtWEnct.js → AppLayout-CwKMM5Ie.js} +5 -5
  15. package/src/assets/web-panel/assets/{Artifacts-BQ8oeMvX.js → Artifacts-CNzDmph7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Audit-NOo9bmgk.js → Audit-DxWpUW3V.js} +1 -1
  17. package/src/assets/web-panel/assets/{BackgroundAgents-QRc7F3Tp.js → BackgroundAgents-C1R2ST-P.js} +1 -1
  18. package/src/assets/web-panel/assets/{Backup-UlVgLn2P.js → Backup-B6biTaEV.js} +1 -1
  19. package/src/assets/web-panel/assets/{BaseInput-BER9LJAr.js → BaseInput-CVnkD2Z5.js} +1 -1
  20. package/src/assets/web-panel/assets/{Chat-Z8z2Fdfz.js → Chat-Dbewl4Je.js} +6 -6
  21. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DdmuBrdQ.js → ChatBubbleRenderer-DVZ6SwC5.js} +1 -1
  22. package/src/assets/web-panel/assets/{Checkbox-CXPgyB8L.js → Checkbox-BqqiF1JE.js} +1 -1
  23. package/src/assets/web-panel/assets/{Codegen-D1vOsLJ7.js → Codegen-BVMkGumc.js} +1 -1
  24. package/src/assets/web-panel/assets/{Col-ucLnUJC7.js → Col-DmiRHy96.js} +1 -1
  25. package/src/assets/web-panel/assets/{Community-COsV9n9t.js → Community-BDlpygqA.js} +1 -1
  26. package/src/assets/web-panel/assets/{Compact-D0EkBEYh.js → Compact-B2NME_m-.js} +1 -1
  27. package/src/assets/web-panel/assets/{Compliance-C8w9SUjt.js → Compliance-BlzDLE-R.js} +1 -1
  28. package/src/assets/web-panel/assets/{Cowork-CmVaKEJS.js → Cowork-C8raPybB.js} +3 -3
  29. package/src/assets/web-panel/assets/{Cron-B_FiMMV2.js → Cron-DA8Py0zP.js} +2 -2
  30. package/src/assets/web-panel/assets/{Crosschain-ByO3YL8N.js → Crosschain-DU2XyHqB.js} +1 -1
  31. package/src/assets/web-panel/assets/{DID-DwpsLM9r.js → DID-Df098fT_.js} +2 -2
  32. package/src/assets/web-panel/assets/{Dashboard-B_8IUvM_.js → Dashboard-D1rT702x.js} +2 -2
  33. package/src/assets/web-panel/assets/{Dropdown-Bo6WQkuX.js → Dropdown-BweXGo1O.js} +1 -1
  34. package/src/assets/web-panel/assets/{EmailListRenderer-C9LiQesx.js → EmailListRenderer-B9ShbgJH.js} +1 -1
  35. package/src/assets/web-panel/assets/EvolutionSettings-D75_iBoc.js +1 -0
  36. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dst6VUnO.js → FamilyGuardDashboard-DrznZFVs.js} +1 -1
  37. package/src/assets/web-panel/assets/{Federation-Ci7CoLkJ.js → Federation-DQZ2adIA.js} +1 -1
  38. package/src/assets/web-panel/assets/{FormItemContext-bdDyUhUM.js → FormItemContext-DN30Dowh.js} +1 -1
  39. package/src/assets/web-panel/assets/{GenericCardRenderer-CrRwOUVi.js → GenericCardRenderer-DRdt0j1e.js} +1 -1
  40. package/src/assets/web-panel/assets/{Git-Dis8h-Z-.js → Git-wwRgStzk.js} +2 -2
  41. package/src/assets/web-panel/assets/{Governance-DX7OH5w2.js → Governance-CI4Uw0Nh.js} +1 -1
  42. package/src/assets/web-panel/assets/{Inference-Cavv_Q_Z.js → Inference-5TuE2AJI.js} +1 -1
  43. package/src/assets/web-panel/assets/{KnowledgeGraph-Cck8tnZn.js → KnowledgeGraph-oXyvmFSR.js} +1 -1
  44. package/src/assets/web-panel/assets/{Logs-BQt3aGMu.js → Logs-ewudSWuv.js} +2 -2
  45. package/src/assets/web-panel/assets/MarkdownRenderer-Cz5nyufm.js +1 -0
  46. package/src/assets/web-panel/assets/{Marketplace-CdmAKNSN.js → Marketplace-DcZeWFrs.js} +1 -1
  47. package/src/assets/web-panel/assets/{McpTools-BUlJdyPY.js → McpTools-BcV9p1RS.js} +3 -3
  48. package/src/assets/web-panel/assets/{Memory-DQRTQDr-.js → Memory-CMhYvugt.js} +2 -2
  49. package/src/assets/web-panel/assets/{MobileBridge-C0dnxgsl.js → MobileBridge-5RP2uwvT.js} +1 -1
  50. package/src/assets/web-panel/assets/{MobileProjects-DQ5Gy7fa.js → MobileProjects-BIuM9Ze4.js} +1 -1
  51. package/src/assets/web-panel/assets/{Mtc-Xl7u2isS.js → Mtc-DmBszTDe.js} +5 -5
  52. package/src/assets/web-panel/assets/{MtcAudit-C3KX0574.js → MtcAudit-Czdp5ZGt.js} +2 -2
  53. package/src/assets/web-panel/assets/{Multisig-CHEhfkn8.js → Multisig-DRWUuNr-.js} +3 -3
  54. package/src/assets/web-panel/assets/{NLProgramming-Ce38mH-8.js → NLProgramming-IRQQN0-c.js} +1 -1
  55. package/src/assets/web-panel/assets/{Notes-C7nuTd7c.js → Notes-CGhD1lW9.js} +4 -4
  56. package/src/assets/web-panel/assets/{NotificationSettings-BcoEcyq8.js → NotificationSettings-BtyPgQAh.js} +1 -1
  57. package/src/assets/web-panel/assets/{OrderTableRenderer-D-t5hKkx.js → OrderTableRenderer-yUaqwCty.js} +1 -1
  58. package/src/assets/web-panel/assets/{Organization-C_BGi4xi.js → Organization-C3kTVn5i.js} +4 -4
  59. package/src/assets/web-panel/assets/{Overflow-BodhraDq.js → Overflow-C2-0iZhq.js} +1 -1
  60. package/src/assets/web-panel/assets/{P2P-BTa3sAIy.js → P2P-CISoEUxA.js} +2 -2
  61. package/src/assets/web-panel/assets/{PdhVaultBrowser-BjZslP9T.js → PdhVaultBrowser-NfTVtvKQ.js} +5 -5
  62. package/src/assets/web-panel/assets/{Permissions-u3UDzGef.js → Permissions-CfK0jJhm.js} +3 -3
  63. package/src/assets/web-panel/assets/{PersonalDataHub-Cb7ybv65.js → PersonalDataHub-OsVxxh_j.js} +4 -4
  64. package/src/assets/web-panel/assets/{Pipeline-CS24k-P7.js → Pipeline-DEmK_Ch2.js} +1 -1
  65. package/src/assets/web-panel/assets/{Privacy-Ddj6Kqs7.js → Privacy-D61s73b0.js} +1 -1
  66. package/src/assets/web-panel/assets/{ProjectInit-Bg5uQIgg.js → ProjectInit-DkLF5gHb.js} +2 -2
  67. package/src/assets/web-panel/assets/{ProjectSettings-AmPR8T12.js → ProjectSettings-BA7odTrI.js} +2 -2
  68. package/src/assets/web-panel/assets/{Projects-DGSbAyTK.js → Projects-CdI1GcAG.js} +1 -1
  69. package/src/assets/web-panel/assets/{Providers-Dc1C3lbB.js → Providers-D9BYyP1r.js} +1 -1
  70. package/src/assets/web-panel/assets/{QrScannerModal-Cv3HXpce.js → QrScannerModal-CXMItUDD.js} +1 -1
  71. package/src/assets/web-panel/assets/{QuickAsk-3dpbXARb.js → QuickAsk-8-tF9ffZ.js} +1 -1
  72. package/src/assets/web-panel/assets/{Recommend-5aGRVnJK.js → Recommend-C2QGWOSi.js} +1 -1
  73. package/src/assets/web-panel/assets/{RemoteSession-CTS2jx0p.js → RemoteSession-B1M_v1lx.js} +2 -2
  74. package/src/assets/web-panel/assets/{Reputation-BxaKI0hY.js → Reputation-D0WZHgv1.js} +1 -1
  75. package/src/assets/web-panel/assets/{Row-C23uQdwd.js → Row-D6nf9-c-.js} +1 -1
  76. package/src/assets/web-panel/assets/{RssFeed-ssX9YOhm.js → RssFeed-ClZ6M-mN.js} +3 -3
  77. package/src/assets/web-panel/assets/{Search-C24GogII.js → Search-ZWkt8vuO.js} +1 -1
  78. package/src/assets/web-panel/assets/{Security-DdAcGKkk.js → Security-BUlQut2m.js} +3 -3
  79. package/src/assets/web-panel/assets/{Services-D0dCiEuq.js → Services-Cd_3xJOc.js} +2 -2
  80. package/src/assets/web-panel/assets/{Skeleton-DEY44o0v.js → Skeleton-C8e2-cHW.js} +1 -1
  81. package/src/assets/web-panel/assets/Skills-DucVRJp1.js +1 -0
  82. package/src/assets/web-panel/assets/{Sla-B_bbo2vB.js → Sla-Csr5pUwI.js} +1 -1
  83. package/src/assets/web-panel/assets/SpeechSettings-CXvyaRAT.js +1 -0
  84. package/src/assets/web-panel/assets/{SyncSettings-CpIXql0Z.js → SyncSettings-KdsIcV5h.js} +2 -2
  85. package/src/assets/web-panel/assets/Tasks-B-eHmJm9.js +1 -0
  86. package/src/assets/web-panel/assets/{Templates-C_44nVPq.js → Templates-BglVeaLK.js} +1 -1
  87. package/src/assets/web-panel/assets/{Tenant-CyZ2ey2P.js → Tenant-BBbQ8qan.js} +1 -1
  88. package/src/assets/web-panel/assets/{Terminal-DwGNHWnp.js → Terminal-Ca_pCVBi.js} +2 -2
  89. package/src/assets/web-panel/assets/{TimelineRenderer-BTAHNkqU.js → TimelineRenderer-YD6XG_vZ.js} +1 -1
  90. package/src/assets/web-panel/assets/{Tokens-DCJWkUwc.js → Tokens-2lRoeVli.js} +1 -1
  91. package/src/assets/web-panel/assets/{Trigger-BCjDO9-E.js → Trigger-C-3XNAU9.js} +1 -1
  92. package/src/assets/web-panel/assets/{Trust-_YS8WbRb.js → Trust-k-1PxdLG.js} +1 -1
  93. package/src/assets/web-panel/assets/{UkeySign-m6R8xGq7.js → UkeySign-FG-qW646.js} +1 -1
  94. package/src/assets/web-panel/assets/{VideoEditing-xOfk29c4.js → VideoEditing-BNOe1cmd.js} +1 -1
  95. package/src/assets/web-panel/assets/{Wallet-B1Gw0awc.js → Wallet-D5C3i2Kw.js} +4 -4
  96. package/src/assets/web-panel/assets/{WebAuthn-CHK5ft18.js → WebAuthn-BOpztqd3.js} +4 -4
  97. package/src/assets/web-panel/assets/{WorkflowEditor-DEp1ukQQ.js → WorkflowEditor-BpVrpUEv.js} +1 -1
  98. package/src/assets/web-panel/assets/{chat-B2sFizun.js → chat-DIuMHb38.js} +1 -1
  99. package/src/assets/web-panel/assets/{colors-DJ17YV1E.js → colors-CTFJPyzQ.js} +1 -1
  100. package/src/assets/web-panel/assets/{compact-item-CBhJJgEC.js → compact-item-CONfmJcW.js} +1 -1
  101. package/src/assets/web-panel/assets/{createContext-BZkpvNJy.js → createContext-RkW5CRrz.js} +1 -1
  102. package/src/assets/web-panel/assets/devWarning-DaEBln_q.js +1 -0
  103. package/src/assets/web-panel/assets/{hasIn-C0ZKo6Ei.js → hasIn-CW4XOjDC.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Bc3vVG2X.js → index-7wMSBCU7.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-vFMwiAN4.js → index-Akmg1WdH.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DOBXNMdh.js → index-B54qoVff.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DLo8Ls9u.js → index-B8r2t0Ch.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C7gv9dvb.js → index-B9XoBoty.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DHtpMr1G.js → index-B9aS2VLu.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Dxe0YlZe.js → index-BFs1Tznc.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DumLgj1t.js → index-BKgaRT0b.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BNp6HKFh.js → index-BM7bOUvu.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DM0rWF6z.js → index-BMg2JFmW.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-4oULtRqE.js → index-BRZsPxNn.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-LPPe0val.js → index-Bf19fKef.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Ctguam0K.js → index-BkAbfBF-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DrU5xVpF.js → index-CBjb2A5R.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CCOr6Q27.js → index-CEZWQthY.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C9gYiACH.js → index-CFGLf88d.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CZW84cEf.js → index-CLUa68JH.js} +3 -3
  121. package/src/assets/web-panel/assets/{index-BoDUYyuo.js → index-CM2X25Er.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DyiCbHeL.js → index-CX4T2uKN.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CW5o1Gar.js → index-CixqfdRk.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-5K2z_tgO.js → index-CkRojEip.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BOfPp5OV.js → index-Cvmc3f25.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CdxJm-HB.js → index-D-lpTPS8.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-8ueG48gl.js → index-D59GbWCN.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BMxOoITz.js → index-DAESljiJ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DLMr5KMd.js → index-DDHUx4Yz.js} +1 -1
  130. package/src/assets/web-panel/assets/index-DG1mcYTa.js +1 -0
  131. package/src/assets/web-panel/assets/{index-BGIvhCPl.js → index-DP5pPedy.js} +1 -1
  132. package/src/assets/web-panel/assets/index-DRs5Pe8u.js +1 -0
  133. package/src/assets/web-panel/assets/{index-DjdSFO0d.js → index-DSlXL6Ju.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Cn5UzvgR.js → index-DWCJnn2O.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-Chct9tL8.js → index-DWY7YwDu.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-BEnd5fiz.js → index-DZTwP364.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-KzNhWykP.js → index-DidrMvOx.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-BpyrhOeh.js → index-Dj3K5kHs.js} +1 -1
  139. package/src/assets/web-panel/assets/{index-CZlkPrxb.js → index-YaeGscSw.js} +1 -1
  140. package/src/assets/web-panel/assets/{index-Dfgxe-fw.js → index-YlMnFNOi.js} +1 -1
  141. package/src/assets/web-panel/assets/{index-IHgzfc1u.js → index-asWsB34i.js} +1 -1
  142. package/src/assets/web-panel/assets/{index-DJkoJzTU.js → index-gkjiFjQd.js} +1 -1
  143. package/src/assets/web-panel/assets/{initDefaultProps-Cqfoq0TJ.js → initDefaultProps-BOr4igh3.js} +1 -1
  144. package/src/assets/web-panel/assets/{motion-CIiVJHNu.js → motion-CK469n2j.js} +1 -1
  145. package/src/assets/web-panel/assets/{move-CFl1o_A8.js → move-DzjudlYQ.js} +1 -1
  146. package/src/assets/web-panel/assets/{mtc-parser-DYoCxcZ1.js → mtc-parser-BQaKUxC6.js} +1 -1
  147. package/src/assets/web-panel/assets/{omit-C1P2q5TY.js → omit-MciovCFD.js} +1 -1
  148. package/src/assets/web-panel/assets/{pickAttrs-BtTRpJtC.js → pickAttrs-DDPJ4zAz.js} +1 -1
  149. package/src/assets/web-panel/assets/{placementArrow-Cst2PcNM.js → placementArrow-Cznyb-jI.js} +1 -1
  150. package/src/assets/web-panel/assets/{responsiveObserve-CF5kalV2.js → responsiveObserve-AJK3SLzJ.js} +1 -1
  151. package/src/assets/web-panel/assets/{slide-CtHGqpXN.js → slide-CPQ28wu7.js} +1 -1
  152. package/src/assets/web-panel/assets/{statusUtils-N__biHXf.js → statusUtils-CFjzvqf_.js} +1 -1
  153. package/src/assets/web-panel/assets/{styleChecker-DR8V_H-i.js → styleChecker-BPyFfTuR.js} +1 -1
  154. package/src/assets/web-panel/assets/{useFlexGapSupport-D274dqun.js → useFlexGapSupport-Dq7N6R05.js} +1 -1
  155. package/src/assets/web-panel/assets/{useFs-BwHLYU5b.js → useFs-C3GS0uTc.js} +1 -1
  156. package/src/assets/web-panel/assets/{usePersonalDataHub-BiA-hVqt.js → usePersonalDataHub-BhgIVzP_.js} +1 -1
  157. package/src/assets/web-panel/assets/{vnode-Bugl94dy.js → vnode-4ZD9-MDr.js} +1 -1
  158. package/src/assets/web-panel/assets/{zoom-DEFl8GCu.js → zoom-Bx4MRVNW.js} +1 -1
  159. package/src/assets/web-panel/index.html +1 -1
  160. package/src/command-help-index.json +1 -1
  161. package/src/command-manifest.json +1 -1
  162. package/src/commands/checkpoint.js +42 -7
  163. package/src/commands/orchestrate.js +85 -25
  164. package/src/commands/session-location.js +5 -0
  165. package/src/commands/session-show.js +109 -1
  166. package/src/commands/session.js +26 -2
  167. package/src/commands/tokens.js +9 -4
  168. package/src/data/changelog.json +26 -0
  169. package/src/generated/cc-agent-protocol.schema.json +71 -0
  170. package/src/harness/jsonl-session-store.js +357 -114
  171. package/src/harness/mcp-client.js +44 -1
  172. package/src/lib/agent-router.js +35 -7
  173. package/src/lib/app-server/server.js +14 -0
  174. package/src/lib/background-agent-supervisor.js +14 -4
  175. package/src/lib/causal-observability.js +64 -1
  176. package/src/lib/checkpoint-summary-projection.js +98 -0
  177. package/src/lib/checkpoint-timeline-authority.js +15 -51
  178. package/src/lib/claude-code-bridge.js +302 -60
  179. package/src/lib/codex-app-server-adapter.js +161 -35
  180. package/src/lib/cost-budget.js +4 -0
  181. package/src/lib/decision-layer/providers.js +84 -9
  182. package/src/lib/decision-layer/runtime.js +86 -49
  183. package/src/lib/direct-model-usage.js +14 -3
  184. package/src/lib/doctor-checkup.js +31 -2
  185. package/src/lib/durable-security-store.js +6 -1
  186. package/src/lib/evolution/evolution-artifact-identity.js +21 -0
  187. package/src/lib/evolution/evolution-artifact-ports.js +5 -11
  188. package/src/lib/evolution/evolution-eval-cohort-receipt-reconciliation.js +389 -0
  189. package/src/lib/execution-location-failure-site.js +63 -0
  190. package/src/lib/execution-location-target.js +102 -8
  191. package/src/lib/external-agent-adapters.js +2 -0
  192. package/src/lib/image-file-boundary.js +154 -0
  193. package/src/lib/image-input.js +16 -3
  194. package/src/lib/llm-pricing.js +114 -8
  195. package/src/lib/mcp-call-ledger-store.js +7 -2
  196. package/src/lib/model-capabilities.js +21 -10
  197. package/src/lib/model-context-catalog.js +60 -1
  198. package/src/lib/openai-responses.js +7 -1
  199. package/src/lib/orchestrator.js +8 -2
  200. package/src/lib/process-execution-broker/index.js +93 -1
  201. package/src/lib/process-execution-broker/linux-subreaper-artifact.js +120 -0
  202. package/src/lib/process-execution-broker/linux-subreaper-helper.js +344 -0
  203. package/src/lib/process-execution-broker/linux-subreaper-process.js +326 -0
  204. package/src/lib/process-execution-broker/linux-subreaper-supervisor.c +360 -0
  205. package/src/lib/process-execution-broker/owned-posix-process-group-worker.mjs +131 -0
  206. package/src/lib/process-execution-broker/owned-posix-process-group.js +267 -0
  207. package/src/lib/process-execution-broker/process-ownership-journal.js +297 -0
  208. package/src/lib/process-execution-broker/process-ownership-quarantine.js +91 -0
  209. package/src/lib/runtime-usage-ledger.js +3 -0
  210. package/src/lib/secure-fs.js +158 -42
  211. package/src/lib/session-branch-history.js +240 -0
  212. package/src/lib/session-history-origins.js +285 -0
  213. package/src/lib/session-input-receipt.js +110 -0
  214. package/src/lib/session-resource-budget.js +8 -8
  215. package/src/lib/session-transcript-history.js +456 -0
  216. package/src/lib/session-transcript-page.js +164 -0
  217. package/src/lib/session-transcript-reference.js +38 -0
  218. package/src/lib/session-usage.js +8 -0
  219. package/src/lib/token-tracker.js +32 -54
  220. package/src/lib/usage-pricing-context.js +93 -0
  221. package/src/runtime/headless-runner.js +8 -0
  222. package/src/runtime/headless-stream.js +202 -4
  223. package/src/assets/web-panel/assets/EvolutionSettings-Pc1IpRfk.js +0 -1
  224. package/src/assets/web-panel/assets/MarkdownRenderer-BIUeeiVA.js +0 -1
  225. package/src/assets/web-panel/assets/Skills-CYt0Mu2W.js +0 -1
  226. package/src/assets/web-panel/assets/SpeechSettings-a2BBjtTL.js +0 -1
  227. package/src/assets/web-panel/assets/Tasks-BYQKU-GL.js +0 -1
  228. package/src/assets/web-panel/assets/devWarning-DZhu13vj.js +0 -1
  229. package/src/assets/web-panel/assets/index-C6iFyQYz.js +0 -1
  230. package/src/assets/web-panel/assets/index-CztThH9f.js +0 -1
@@ -91,6 +91,13 @@ import {
91
91
  } from "../lib/execution-location-result.js";
92
92
  import { normalizeExecutionLocationResultStoreReceipt } from "../lib/execution-location-result-store.js";
93
93
  import skillInvocationReceipt from "@chainlesschain/session-core/skill-invocation-receipt";
94
+ import {
95
+ BRANCH_HISTORY_MESSAGE,
96
+ BRANCH_HISTORY_ORIGIN,
97
+ resolveSessionBranchHistory,
98
+ branchMessageDigest,
99
+ createBranchHistoryImport,
100
+ } from "../lib/session-branch-history.js";
94
101
 
95
102
  const { verifySkillInvocationReceipt } = skillInvocationReceipt;
96
103
 
@@ -723,7 +730,13 @@ function getSessionsDir() {
723
730
  if (claudeProjectDir) {
724
731
  ensureClaudeProjectStorageTree(homeDir, claudeProjectDir);
725
732
  } else {
726
- ensurePrivateDirectory(dir);
733
+ // Another process may have created the directory but still be securing
734
+ // its Windows ACL. Join that check/repair before writing an anchored
735
+ // child; a later directory DACL change also changes child ctime.
736
+ ensurePrivateDirectory(dir, {
737
+ applyWindowsAcl: true,
738
+ failIfUnavailable: true,
739
+ });
727
740
  }
728
741
  securedSessionsDir = dir;
729
742
  const secured = lstatSync(dir);
@@ -2187,10 +2200,36 @@ export function withSessionAuthorityTransaction(
2187
2200
  let appendAttempts = 0;
2188
2201
  let writerActive = true;
2189
2202
  let writerPoisoned = false;
2203
+ let readingProjection = false;
2190
2204
  let transactionRecoveryEvidence = null;
2191
2205
  const writer = Object.freeze({
2192
2206
  initialHeadHash: currentHeadHash,
2193
2207
  currentHeadHash: () => currentHeadHash,
2208
+ readProjection(createProjection) {
2209
+ if (!writerActive || writerPoisoned || readingProjection) {
2210
+ throw new Error(
2211
+ "Session authority reader lease is closed or poisoned",
2212
+ );
2213
+ }
2214
+ readingProjection = true;
2215
+ try {
2216
+ const result = readVerifiedProjectionLocked(
2217
+ sessionId,
2218
+ filePath,
2219
+ createProjection,
2220
+ {
2221
+ expectedHeadHash: currentHeadHash,
2222
+ },
2223
+ );
2224
+ if (result && typeof result.then === "function")
2225
+ throw new TypeError(
2226
+ "Session authority projection must be synchronous",
2227
+ );
2228
+ return result;
2229
+ } finally {
2230
+ readingProjection = false;
2231
+ }
2232
+ },
2194
2233
  retainRecoveryEvidence(evidence) {
2195
2234
  if (!writerActive) {
2196
2235
  const error = new Error(
@@ -2209,6 +2248,10 @@ export function withSessionAuthorityTransaction(
2209
2248
  return transactionRecoveryEvidence;
2210
2249
  },
2211
2250
  appendAuthorityEvent(type, data) {
2251
+ if (readingProjection)
2252
+ throw new Error(
2253
+ "Cannot append during a session authority projection",
2254
+ );
2212
2255
  if (!writerActive) {
2213
2256
  const error = new Error(
2214
2257
  `Session authority transaction is already closed: ${sessionId}`,
@@ -3491,7 +3534,9 @@ function assertVerifiedTranscriptAnchor(
3491
3534
  * event reduction, and the independently persisted head/count check share one
3492
3535
  * forward transcript pass. `finish()` may call `authority.readMessages()` to
3493
3536
  * recover only the active context with bounded reverse IO (latest compact
3494
- * checkpoint plus its suffix).
3537
+ * checkpoint plus its suffix). Its optional synchronous `replayEvents(visitor)`
3538
+ * revalidates the same head in another forward pass under this lock. That
3539
+ * visitor lease expires when finish returns; it cannot escape to later tasks.
3495
3540
  *
3496
3541
  * This removes the mandatory all-event array from resume projections. It does
3497
3542
  * not make verification IO sublinear: authenticating a plain chained JSONL
@@ -3519,54 +3564,11 @@ export function readVerifiedProjection(
3519
3564
  return withFileLock(
3520
3565
  filePath,
3521
3566
  () => {
3522
- const projection = createProjection();
3523
- if (
3524
- !projection ||
3525
- typeof projection.accept !== "function" ||
3526
- typeof projection.finish !== "function"
3527
- ) {
3528
- throw new TypeError(
3529
- "Verified transcript projection must provide accept() and finish()",
3530
- );
3531
- }
3532
- if (!existsSync(filePath)) {
3533
- const presence = getSessionPresence(sessionId);
3534
- if (presence === SESSION_PRESENCE.TOMBSTONED) {
3535
- throw sessionDeletedError(sessionId);
3536
- }
3537
- if (presence === SESSION_PRESENCE.MISSING_TRANSCRIPT) {
3538
- throw unverifiedTranscriptError(
3539
- sessionId,
3540
- missingTranscriptVerification(),
3541
- );
3542
- }
3543
- return projection.finish(
3544
- Object.freeze({
3545
- headHash: null,
3546
- eventCount: 0,
3547
- readMessages: () => [],
3548
- }),
3549
- );
3550
- }
3551
- const structure = createSessionTranscriptStructureProjection(sessionId);
3552
- const verification = verifyTranscriptFile(filePath, {
3553
- ioMetrics: options.ioMetrics,
3554
- onVerifiedEvent: (event) => {
3555
- structure.accept(event);
3556
- projection.accept(event);
3557
- },
3558
- });
3559
- assertVerifiedTranscriptAnchor(sessionId, verification);
3560
- structure.finish({ assertValid: true });
3561
- return projection.finish(
3562
- Object.freeze({
3563
- headHash: verification.lastHash,
3564
- eventCount: verification.chainedEvents,
3565
- readMessages: () =>
3566
- rebuildVerifiedMessagesFromFile(filePath, {
3567
- ioMetrics: options.messageIoMetrics,
3568
- }),
3569
- }),
3567
+ return readVerifiedProjectionLocked(
3568
+ sessionId,
3569
+ filePath,
3570
+ createProjection,
3571
+ options,
3570
3572
  );
3571
3573
  },
3572
3574
  {
@@ -3580,6 +3582,119 @@ export function readVerifiedProjection(
3580
3582
  );
3581
3583
  }
3582
3584
 
3585
+ function readVerifiedProjectionLocked(
3586
+ sessionId,
3587
+ filePath,
3588
+ createProjection,
3589
+ options = {},
3590
+ ) {
3591
+ const projection = createProjection();
3592
+ if (
3593
+ !projection ||
3594
+ typeof projection.accept !== "function" ||
3595
+ typeof projection.finish !== "function"
3596
+ ) {
3597
+ throw new TypeError(
3598
+ "Verified transcript projection must provide accept() and finish()",
3599
+ );
3600
+ }
3601
+ if (!existsSync(filePath)) {
3602
+ const presence = getSessionPresence(sessionId);
3603
+ if (presence === SESSION_PRESENCE.TOMBSTONED) {
3604
+ throw sessionDeletedError(sessionId);
3605
+ }
3606
+ if (presence === SESSION_PRESENCE.MISSING_TRANSCRIPT) {
3607
+ throw unverifiedTranscriptError(
3608
+ sessionId,
3609
+ missingTranscriptVerification(),
3610
+ );
3611
+ }
3612
+ return projection.finish(
3613
+ Object.freeze({
3614
+ headHash: null,
3615
+ eventCount: 0,
3616
+ readMessages: () => [],
3617
+ }),
3618
+ );
3619
+ }
3620
+ const structure = createSessionTranscriptStructureProjection(sessionId);
3621
+ const verification = verifyTranscriptFile(filePath, {
3622
+ ioMetrics: options.ioMetrics,
3623
+ onVerifiedEvent: (event) => {
3624
+ structure.accept(event);
3625
+ projection.accept(event);
3626
+ },
3627
+ });
3628
+ assertVerifiedTranscriptAnchor(sessionId, verification);
3629
+ structure.finish({ assertValid: true });
3630
+ if (
3631
+ options.expectedHeadHash !== undefined &&
3632
+ verification.lastHash !== options.expectedHeadHash
3633
+ ) {
3634
+ throw unverifiedTranscriptError(sessionId, verification);
3635
+ }
3636
+ let replayLeaseActive = true;
3637
+ let replaying = false;
3638
+ try {
3639
+ return projection.finish(
3640
+ Object.freeze({
3641
+ headHash: verification.lastHash,
3642
+ eventCount: verification.chainedEvents,
3643
+ readMessages: () =>
3644
+ rebuildVerifiedMessagesFromFile(filePath, {
3645
+ ioMetrics: options.messageIoMetrics,
3646
+ }),
3647
+ // Rewinds can discard the entire bounded page buffer. A second scan
3648
+ // collects the selected prefix without retaining the archive in RAM.
3649
+ // This lease is synchronous and expires before releasing the lock.
3650
+ replayEvents(accept) {
3651
+ if (!replayLeaseActive || replaying) {
3652
+ throw new Error(
3653
+ "Verified replay lease is closed or already in use",
3654
+ );
3655
+ }
3656
+ if (
3657
+ typeof accept !== "function" ||
3658
+ accept.constructor?.name === "AsyncFunction"
3659
+ ) {
3660
+ throw new TypeError(
3661
+ "Verified replay requires a synchronous visitor",
3662
+ );
3663
+ }
3664
+ replaying = true;
3665
+ try {
3666
+ const replayStructure =
3667
+ createSessionTranscriptStructureProjection(sessionId);
3668
+ const replayVerification = verifyTranscriptFile(filePath, {
3669
+ ioMetrics: options.replayIoMetrics,
3670
+ onVerifiedEvent(event) {
3671
+ replayStructure.accept(event);
3672
+ const result = accept(event);
3673
+ if (result && typeof result.then === "function")
3674
+ throw new TypeError(
3675
+ "Verified replay visitor must be synchronous",
3676
+ );
3677
+ },
3678
+ });
3679
+ assertVerifiedTranscriptAnchor(sessionId, replayVerification);
3680
+ replayStructure.finish({ assertValid: true });
3681
+ if (
3682
+ replayVerification.lastHash !== verification.lastHash ||
3683
+ replayVerification.chainedEvents !== verification.chainedEvents
3684
+ ) {
3685
+ throw unverifiedTranscriptError(sessionId, replayVerification);
3686
+ }
3687
+ } finally {
3688
+ replaying = false;
3689
+ }
3690
+ },
3691
+ }),
3692
+ );
3693
+ } finally {
3694
+ replayLeaseActive = false;
3695
+ }
3696
+ }
3697
+
3583
3698
  /**
3584
3699
  * Compatibility reader for consumers that still require the complete event
3585
3700
  * array. New resume projections should use `readVerifiedProjection()` so their
@@ -6672,6 +6787,8 @@ export function forkSession(sourceId, options = {}) {
6672
6787
  * @param {string|null} [params.parentTurnId]
6673
6788
  * @param {Array<{role:string,content:any}>} [params.messages] pre-branch turns
6674
6789
  * @param {{title?:string,provider?:string,model?:string}} [params.meta]
6790
+ * @param {object|null} [params.history] Scoped display-history capability from
6791
+ * createSessionTranscriptBranchProjection(), consumed under the source lock.
6675
6792
  * @returns {{branchSessionId:string, created:boolean, messages:number}}
6676
6793
  */
6677
6794
  export function createBranchSession({
@@ -6680,6 +6797,7 @@ export function createBranchSession({
6680
6797
  parentTurnId = null,
6681
6798
  messages = [],
6682
6799
  meta = {},
6800
+ history = null,
6683
6801
  } = {}) {
6684
6802
  if (isUnsafeSessionId(branchSessionId)) {
6685
6803
  throw new Error(
@@ -6692,6 +6810,18 @@ export function createBranchSession({
6692
6810
  const canonicalMessages = projectCanonicalResumeMessages(messages, {
6693
6811
  strict: true,
6694
6812
  });
6813
+ const historyPlan = resolveSessionBranchHistory(
6814
+ history,
6815
+ parentSessionId,
6816
+ messages,
6817
+ );
6818
+ if (
6819
+ historyPlan &&
6820
+ (process.platform === "win32"
6821
+ ? branchSessionId.toLowerCase() === parentSessionId.toLowerCase()
6822
+ : branchSessionId === parentSessionId)
6823
+ )
6824
+ throw new Error("A history branch must be independent of its parent");
6695
6825
  if (
6696
6826
  (parentSessionId !== null && typeof parentSessionId !== "string") ||
6697
6827
  (parentTurnId !== null && typeof parentTurnId !== "string")
@@ -6746,36 +6876,10 @@ export function createBranchSession({
6746
6876
  // Host prompts and tool scaffolding are re-established on resume. The
6747
6877
  // canonical projection above retains only explicitly durable systems.
6748
6878
  }
6749
- const inputDigest = `sha256:${createHash("sha256")
6750
- .update(JSON.stringify(digestEvents), "utf8")
6751
- .digest("hex")}`;
6752
6879
  const branchGenerationId = `generation-${createHash("sha256")
6753
6880
  .update(`branch-generation\0${branchSessionId}`, "utf8")
6754
6881
  .digest("hex")
6755
6882
  .slice(0, 32)}`;
6756
- const plannedEvents = digestEvents.map((event, index) =>
6757
- index === 0
6758
- ? {
6759
- ...event,
6760
- data: encodeSessionGenerationData(
6761
- event.data,
6762
- createSessionGenerationAuthority(
6763
- branchSessionId,
6764
- null,
6765
- branchGenerationId,
6766
- ),
6767
- ),
6768
- }
6769
- : event,
6770
- );
6771
- plannedEvents.push({
6772
- type: "session_branch_complete",
6773
- data: {
6774
- schemaVersion: 1,
6775
- inputDigest,
6776
- messageCount: count,
6777
- },
6778
- });
6779
6883
 
6780
6884
  const filePath = sessionPath(branchSessionId);
6781
6885
  const sessionsDir = getSessionsDir();
@@ -6800,8 +6904,22 @@ export function createBranchSession({
6800
6904
  );
6801
6905
  }
6802
6906
  if (existsSync(filePath)) inspectPhysicalTail(filePath);
6907
+ const anchoredMeta = readSessionMeta(sessionsDir, branchSessionId);
6908
+ const anchoredCount = Number(anchoredMeta?.event_count);
6909
+ let anchoredEventHash = null;
6910
+ let existingBranchData = null;
6911
+ let seenEvents = 0;
6912
+ const existingHistoryImport = createBranchHistoryImport();
6803
6913
  let verification = existsSync(filePath)
6804
- ? verifyTranscriptFile(filePath)
6914
+ ? verifyTranscriptFile(filePath, {
6915
+ onVerifiedEvent(event) {
6916
+ existingHistoryImport.accept(event);
6917
+ seenEvents += 1;
6918
+ if (seenEvents === anchoredCount) anchoredEventHash = event.hash;
6919
+ if (seenEvents === 2 && event.type === "session_branch")
6920
+ existingBranchData = event.data;
6921
+ },
6922
+ })
6805
6923
  : {
6806
6924
  status: TRANSCRIPT_CHAIN_STATUS.EMPTY,
6807
6925
  chainedEvents: 0,
@@ -6815,27 +6933,146 @@ export function createBranchSession({
6815
6933
  ) {
6816
6934
  throw unverifiedTranscriptError(branchSessionId, verification);
6817
6935
  }
6818
- const existingEvents = existsSync(filePath)
6819
- ? readEvents(branchSessionId)
6820
- : [];
6821
- const prefixMatches = existingEvents
6822
- .slice(0, Math.min(existingEvents.length, plannedEvents.length))
6823
- .every((event, index) => {
6824
- let plannedData = plannedEvents[index].data;
6825
- // Existing v1 branches predate generation authority. They remain
6826
- // idempotently readable; only newly created branches publish it.
6827
- if (
6828
- index === 0 &&
6829
- event?.data?.[SESSION_GENERATION_AUTHORITY_FIELD] === undefined
6830
- ) {
6831
- plannedData = { ...plannedData };
6832
- delete plannedData[SESSION_GENERATION_AUTHORITY_FIELD];
6833
- }
6834
- return (
6835
- event.type === plannedEvents[index].type &&
6836
- JSON.stringify(event.data) === JSON.stringify(plannedData)
6936
+ const existingCount = verification.chainedEvents;
6937
+ // Keep old deterministic snapshot branches idempotent. Never retrofit
6938
+ // archive data into an already published branch's execution history.
6939
+ const inherited =
6940
+ existingBranchData && existingBranchData.history == null
6941
+ ? null
6942
+ : historyPlan;
6943
+ if (inherited && inherited.contextOrigins.length !== count)
6944
+ throw new Error("Branch history context does not match its snapshot");
6945
+ const visitDigestEvents = (visit) => {
6946
+ visit(digestEvents[0]);
6947
+ visit(
6948
+ inherited
6949
+ ? {
6950
+ ...digestEvents[1],
6951
+ data: {
6952
+ ...digestEvents[1].data,
6953
+ history: inherited.descriptor,
6954
+ },
6955
+ }
6956
+ : digestEvents[1],
6957
+ );
6958
+ if (inherited) {
6959
+ let inheritedRows = 0;
6960
+ inherited.visit((row) => {
6961
+ // Preserve all display text, independently of the page's 200K
6962
+ // rendering cap. Chunks also keep escaped text below record limits.
6963
+ const chunkChars = 128 * 1024;
6964
+ let offset = 0;
6965
+ let part = 0;
6966
+ do {
6967
+ let end = Math.min(row.text.length, offset + chunkChars);
6968
+ const before = row.text.charCodeAt(end - 1);
6969
+ const after = row.text.charCodeAt(end);
6970
+ if (
6971
+ before >= 0xd800 &&
6972
+ before <= 0xdbff &&
6973
+ after >= 0xdc00 &&
6974
+ after <= 0xdfff
6975
+ )
6976
+ end -= 1;
6977
+ visit({
6978
+ type: BRANCH_HISTORY_MESSAGE,
6979
+ data: {
6980
+ ...row,
6981
+ text: row.text.slice(offset, end),
6982
+ part: part++,
6983
+ last: end === row.text.length,
6984
+ },
6985
+ });
6986
+ offset = end;
6987
+ } while (offset < row.text.length);
6988
+ inheritedRows += 1;
6989
+ });
6990
+ if (inheritedRows !== inherited.descriptor.totalMessages)
6991
+ throw new Error("Branch history source changed while streaming");
6992
+ }
6993
+ digestEvents.slice(2).forEach((event, index) => {
6994
+ visit(event);
6995
+ if (inherited)
6996
+ visit({
6997
+ type: BRANCH_HISTORY_ORIGIN,
6998
+ data: {
6999
+ messageDigest: branchMessageDigest(event.data),
7000
+ origin: inherited.contextOrigins[index],
7001
+ },
7002
+ });
7003
+ });
7004
+ };
7005
+ const inputHash = createHash("sha256").update("[");
7006
+ let plannedCount = 0;
7007
+ visitDigestEvents((event) => {
7008
+ if (plannedCount > 0) inputHash.update(",");
7009
+ inputHash.update(JSON.stringify(event));
7010
+ plannedCount += 1;
7011
+ });
7012
+ const inputDigest = `sha256:${inputHash.update("]").digest("hex")}`;
7013
+ plannedCount += 1;
7014
+ const visitPlannedEvents = (visit) => {
7015
+ let index = 0;
7016
+ visitDigestEvents((event) => {
7017
+ visit(
7018
+ index === 0
7019
+ ? {
7020
+ ...event,
7021
+ data: encodeSessionGenerationData(
7022
+ event.data,
7023
+ createSessionGenerationAuthority(
7024
+ branchSessionId,
7025
+ null,
7026
+ branchGenerationId,
7027
+ ),
7028
+ ),
7029
+ }
7030
+ : event,
7031
+ index++,
6837
7032
  );
6838
7033
  });
7034
+ visit(
7035
+ {
7036
+ type: "session_branch_complete",
7037
+ data: {
7038
+ schemaVersion: inherited ? 2 : 1,
7039
+ inputDigest,
7040
+ messageCount: count,
7041
+ },
7042
+ },
7043
+ index,
7044
+ );
7045
+ };
7046
+ const existingIterator =
7047
+ existingCount > 0 ? iterateCanonicalJsonlLinesSync(filePath) : null;
7048
+ let prefixMatches = true;
7049
+ try {
7050
+ if (existingCount > 0)
7051
+ visitPlannedEvents((planned, index) => {
7052
+ if (index >= existingCount) return;
7053
+ const record = existingIterator.next();
7054
+ if (record.done) {
7055
+ prefixMatches = false;
7056
+ return;
7057
+ }
7058
+ const event = parseCanonicalJsonlRecord(record.value.line);
7059
+ let plannedData = planned.data;
7060
+ // Existing v1 branches predate generation authority. They remain
7061
+ // idempotently readable; only newly created branches publish it.
7062
+ if (
7063
+ index === 0 &&
7064
+ event?.data?.[SESSION_GENERATION_AUTHORITY_FIELD] === undefined
7065
+ ) {
7066
+ plannedData = { ...plannedData };
7067
+ delete plannedData[SESSION_GENERATION_AUTHORITY_FIELD];
7068
+ }
7069
+ prefixMatches &&=
7070
+ event.type === planned.type &&
7071
+ JSON.stringify(event.data) === JSON.stringify(plannedData);
7072
+ });
7073
+ } finally {
7074
+ existingIterator?.return?.();
7075
+ }
6839
7076
  if (!prefixMatches) {
6840
7077
  const error = new Error(
6841
7078
  `Branch session conflicts with its deterministic input: ${branchSessionId}`,
@@ -6843,11 +7080,12 @@ export function createBranchSession({
6843
7080
  error.code = "SESSION_BRANCH_CONFLICT";
6844
7081
  throw error;
6845
7082
  }
7083
+ if (existingCount >= plannedCount) existingHistoryImport.finish();
6846
7084
 
6847
7085
  // Once the exact completion marker is anchored, later branch turns are
6848
7086
  // outside the idempotent creation transaction. A replay resolves to that
6849
7087
  // progressed branch, but an unanchored suffix is never re-blessed.
6850
- if (existingEvents.length > plannedEvents.length) {
7088
+ if (existingCount > plannedCount) {
6851
7089
  assertVerifiedTranscriptAnchor(branchSessionId, verification);
6852
7090
  return { branchSessionId, created: false, messages: 0 };
6853
7091
  }
@@ -6856,8 +7094,6 @@ export function createBranchSession({
6856
7094
  // sidecar. Only advance an existing anchor along that exact prefix. Never
6857
7095
  // lower or replace a higher/different anchor: doing so would legitimize a
6858
7096
  // truncated deterministic branch and permanently discard later turns.
6859
- const anchoredMeta = readSessionMeta(sessionsDir, branchSessionId);
6860
- const anchoredCount = Number(anchoredMeta?.event_count);
6861
7097
  const anchorMatchesCurrent =
6862
7098
  anchoredMeta?.deleted !== true &&
6863
7099
  anchoredMeta?.last_hash === verification.lastHash &&
@@ -6867,11 +7103,9 @@ export function createBranchSession({
6867
7103
  Number.isSafeInteger(anchoredCount) &&
6868
7104
  anchoredCount >= 0 &&
6869
7105
  anchoredCount < verification.chainedEvents &&
6870
- verification.chainedEvents === existingEvents.length &&
6871
7106
  (anchoredCount === 0
6872
7107
  ? anchoredMeta?.last_hash === null
6873
- : existingEvents[anchoredCount - 1]?.hash ===
6874
- anchoredMeta?.last_hash);
7108
+ : anchoredEventHash === anchoredMeta?.last_hash);
6875
7109
  if (
6876
7110
  anchoredMeta !== null &&
6877
7111
  !anchorMatchesCurrent &&
@@ -6879,13 +7113,22 @@ export function createBranchSession({
6879
7113
  ) {
6880
7114
  throw unverifiedTranscriptError(branchSessionId, verification);
6881
7115
  }
6882
- if (existsSync(filePath) && !anchorMatchesCurrent) {
7116
+ const unpublishedHistoryPrefix =
7117
+ inherited && existingCount < plannedCount;
7118
+ // A second failed attempt must not make an incomplete history branch
7119
+ // resumable. Its first usable sidecar/witness is published only after the
7120
+ // complete archive, execution snapshot and completion digest validate.
7121
+ if (
7122
+ existsSync(filePath) &&
7123
+ !anchorMatchesCurrent &&
7124
+ !unpublishedHistoryPrefix
7125
+ ) {
6883
7126
  rebuildSessionMetaUnlocked(sessionsDir, branchSessionId, filePath);
6884
7127
  }
6885
- if (existingEvents.length > 0) {
7128
+ if (existingCount > 0 && !unpublishedHistoryPrefix) {
6886
7129
  assertVerifiedTranscriptAnchor(branchSessionId, verification);
6887
7130
  }
6888
- if (existingEvents.length === plannedEvents.length) {
7131
+ if (existingCount === plannedCount) {
6889
7132
  return { branchSessionId, created: false, messages: 0 };
6890
7133
  }
6891
7134
 
@@ -6893,12 +7136,8 @@ export function createBranchSession({
6893
7136
  let expectedTranscriptState = existsSync(filePath)
6894
7137
  ? readPhysicalTranscriptState(filePath)
6895
7138
  : null;
6896
- for (
6897
- let index = existingEvents.length;
6898
- index < plannedEvents.length;
6899
- index += 1
6900
- ) {
6901
- const planned = plannedEvents[index];
7139
+ visitPlannedEvents((planned, index) => {
7140
+ if (index < existingCount) return;
6902
7141
  const core = {
6903
7142
  type: planned.type,
6904
7143
  timestamp: Date.now(),
@@ -6914,12 +7153,16 @@ export function createBranchSession({
6914
7153
  expectedTranscriptState,
6915
7154
  );
6916
7155
  prevHash = hash;
6917
- }
7156
+ });
6918
7157
 
6919
- verification = verifyTranscriptFile(filePath);
7158
+ const completedHistoryImport = createBranchHistoryImport();
7159
+ verification = verifyTranscriptFile(filePath, {
7160
+ onVerifiedEvent: (event) => completedHistoryImport.accept(event),
7161
+ });
7162
+ completedHistoryImport.finish();
6920
7163
  if (
6921
7164
  verification.status !== TRANSCRIPT_CHAIN_STATUS.VERIFIED ||
6922
- verification.chainedEvents !== plannedEvents.length ||
7165
+ verification.chainedEvents !== plannedCount ||
6923
7166
  verification.lastHash !== prevHash ||
6924
7167
  verification.malformedLines > 0 ||
6925
7168
  verification.truncatedTail
@@ -3525,13 +3525,16 @@ export class MCPClient extends EventEmitter {
3525
3525
  * Call a tool on a specific server. If the server has a registered
3526
3526
  * reconnector and the call fails with a connection-shaped error (server
3527
3527
  * restarted / token rotated / entry dropped), re-resolve the config,
3528
- * reconnect, and retry the call exactly once.
3528
+ * reconnect, and retry the call exactly once. HTTP 404 only reinitializes
3529
+ * the connection: an already-dispatched tool has an unknown outcome and
3530
+ * must never be retried automatically just because its session expired.
3529
3531
  * @param {string} serverName - Server name
3530
3532
  * @param {string} toolName - Tool name
3531
3533
  * @param {object} args - Tool arguments
3532
3534
  * @param {{signal?: AbortSignal, dispatchAdmission?: Function}} options - Host-owned cancellation and dispatch authority
3533
3535
  */
3534
3536
  async callTool(serverName, toolName, args = {}, options = {}) {
3537
+ const attemptedEntry = this.servers.get(serverName);
3535
3538
  if (options?.signal?.aborted) {
3536
3539
  throw mcpRequestAbortedError(
3537
3540
  serverName,
@@ -3542,6 +3545,43 @@ export class MCPClient extends EventEmitter {
3542
3545
  try {
3543
3546
  return await this._callToolOnce(serverName, toolName, args, options);
3544
3547
  } catch (err) {
3548
+ if (
3549
+ !isMcpRpcError(err) &&
3550
+ safeRpcProperty(err, "code") === "CC_MCP_HTTP_STATUS" &&
3551
+ safeRpcProperty(err, "status") === 404 &&
3552
+ attemptedEntry?.httpUrl &&
3553
+ !options?.signal?.aborted
3554
+ ) {
3555
+ // Both a stateless endpoint's transient 404 and an expired MCP
3556
+ // session may recover through initialize. The HTTP status alone is
3557
+ // insufficient evidence to replay a possibly effectful tool call.
3558
+ const current = this.servers.get(serverName);
3559
+ const recovered =
3560
+ current && current !== attemptedEntry
3561
+ ? current.state === ServerState.CONNECTED
3562
+ : this._reconnecting.has(serverName) ||
3563
+ (current && !current._httpDiscardStopping)
3564
+ ? await this._tryReconnect(serverName, {
3565
+ connectAuthRetryUsed: true,
3566
+ expectedEntry: attemptedEntry,
3567
+ })
3568
+ : false;
3569
+ const failure = mcpTransportError(
3570
+ "CC_MCP_HTTP_TOOL_OUTCOME_UNKNOWN",
3571
+ recovered
3572
+ ? "MCP HTTP connection was reinitialized after HTTP 404; the tool outcome is unknown and was not replayed"
3573
+ : "MCP HTTP 404 recovery failed; the tool outcome is unknown and was not replayed",
3574
+ {
3575
+ transport: attemptedEntry.transportKind,
3576
+ url: attemptedEntry.config?.url,
3577
+ status: 404,
3578
+ dispatched: true,
3579
+ outcomeUnknown: true,
3580
+ },
3581
+ );
3582
+ failure.connectionRecovered = Boolean(recovered);
3583
+ throw failure;
3584
+ }
3545
3585
  if (
3546
3586
  mcpRpcControlData.has(err) &&
3547
3587
  (await this._resolveRequiredUrlElicitations(serverName, err))
@@ -3734,6 +3774,9 @@ export class MCPClient extends EventEmitter {
3734
3774
  const p = (async () => {
3735
3775
  try {
3736
3776
  const currentEntry = this.servers.get(name) || null;
3777
+ if (options.expectedEntry && currentEntry !== options.expectedEntry) {
3778
+ return currentEntry?.state === ServerState.CONNECTED;
3779
+ }
3737
3780
  const currentConfig = currentEntry?.config || null;
3738
3781
  // Resource subscriptions belong to the logical client/server
3739
3782
  // relationship, not to one transport connection. Snapshot them before