chainlesschain 0.162.188 → 0.162.193

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 (337) hide show
  1. package/README.md +61 -11
  2. package/bin/chainlesschain.js +7 -52
  3. package/completions/_cc +209 -0
  4. package/completions/cc.bash +18 -0
  5. package/completions/cc.fish +13 -0
  6. package/completions/cc.ps1 +226 -0
  7. package/package.json +10 -2
  8. package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
  9. package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
  10. package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
  11. package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
  12. package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
  13. package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
  14. package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
  16. package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
  17. package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
  18. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
  19. package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
  20. package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
  21. package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
  22. package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
  24. package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
  25. package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
  26. package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
  27. package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
  28. package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
  30. package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
  31. package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
  32. package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
  33. package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
  34. package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
  35. package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
  36. package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
  37. package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
  39. package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
  40. package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
  41. package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
  42. package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
  43. package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
  44. package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
  45. package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
  46. package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
  47. package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
  48. package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
  49. package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
  50. package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
  51. package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
  52. package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
  53. package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
  54. package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
  55. package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
  56. package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
  57. package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
  58. package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
  59. package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
  60. package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
  61. package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
  62. package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
  63. package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
  64. package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
  65. package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
  66. package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
  67. package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
  68. package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
  69. package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
  70. package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
  72. package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
  73. package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
  74. package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
  75. package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
  76. package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
  77. package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
  78. package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
  79. package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
  80. package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
  81. package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
  82. package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
  84. package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
  85. package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
  86. package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
  88. package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
  89. package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
  90. package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
  91. package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
  92. package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
  93. package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
  94. package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
  127. package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
  133. package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
  134. package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
  135. package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
  140. package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
  141. package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
  142. package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
  143. package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
  144. package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
  145. package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
  146. package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
  147. package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
  148. package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
  149. package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
  151. package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
  153. package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
  154. package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.js} +1 -1
  155. package/src/assets/web-panel/index.html +1 -1
  156. package/src/cli-help.js +181 -0
  157. package/src/command-help-index.json +181 -0
  158. package/src/command-manifest.json +1107 -175
  159. package/src/command-surface-policy.js +197 -0
  160. package/src/commands/agent.js +98 -33
  161. package/src/commands/artifacts.js +400 -2
  162. package/src/commands/background-session.js +41 -3
  163. package/src/commands/checkpoint-managed.js +1059 -0
  164. package/src/commands/checkpoint-restore-recovery.js +1476 -0
  165. package/src/commands/checkpoint.js +950 -22
  166. package/src/commands/compact.js +101 -25
  167. package/src/commands/config.js +506 -20
  168. package/src/commands/doctor.js +4 -1
  169. package/src/commands/hub.js +1 -1
  170. package/src/commands/mcp.js +688 -24
  171. package/src/commands/pack.js +16 -2
  172. package/src/commands/plugin.js +20 -12
  173. package/src/commands/session-mcp-recovery.js +218 -0
  174. package/src/commands/session.js +470 -110
  175. package/src/commands/setup.js +36 -10
  176. package/src/commands/skill.js +181 -110
  177. package/src/commands/status.js +21 -5
  178. package/src/commands/team-distributed.js +3324 -0
  179. package/src/commands/team.js +1130 -74
  180. package/src/commands/update.js +68 -6
  181. package/src/constants.js +11 -0
  182. package/src/data/changelog.json +26 -0
  183. package/src/gateways/ws/action-protocol.js +10 -0
  184. package/src/gateways/ws/session-protocol.js +555 -27
  185. package/src/gateways/ws/ws-agent-handler.js +541 -14
  186. package/src/gateways/ws/ws-session-gateway.js +129 -22
  187. package/src/gateways/ws/ws-session-state.js +431 -0
  188. package/src/harness/background-task-manager.js +233 -36
  189. package/src/harness/jsonl-session-store.js +2551 -167
  190. package/src/harness/mcp-client.js +802 -61
  191. package/src/harness/prompt-compressor.js +227 -27
  192. package/src/harness/session-index.js +89 -38
  193. package/src/harness/session-list-index.js +254 -0
  194. package/src/harness/structured-handoff.js +410 -0
  195. package/src/lazy-dispatch.js +747 -84
  196. package/src/lib/__tests__/logger.test.js +50 -2
  197. package/src/lib/advisor-runtime.js +1047 -0
  198. package/src/lib/agent-core.js +2 -0
  199. package/src/lib/agent-sandbox.js +120 -26
  200. package/src/lib/agent-session-export.js +20 -0
  201. package/src/lib/agent-team/task-lease.js +271 -3
  202. package/src/lib/agent-team/team-adjudication.js +1501 -0
  203. package/src/lib/agent-team/team-budget.js +4 -1
  204. package/src/lib/agent-team/team-control-store.js +582 -0
  205. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  206. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  207. package/src/lib/agent-team/team-runner.js +543 -24
  208. package/src/lib/agent-team/team-worktree.js +1403 -95
  209. package/src/lib/artifact-store.js +131 -8
  210. package/src/lib/background-agent-supervisor.js +185 -13
  211. package/src/lib/background-interaction-journal.js +37 -9
  212. package/src/lib/background-launch-profile.js +1058 -0
  213. package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
  214. package/src/lib/checkpoint-restore-orchestrator.js +987 -0
  215. package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
  216. package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
  217. package/src/lib/checkpoint-restore-recovery.js +884 -0
  218. package/src/lib/checkpoint-restore-retention.js +126 -0
  219. package/src/lib/checkpoint-restore-saga.js +5079 -0
  220. package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
  221. package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
  222. package/src/lib/checkpoint-store.js +1688 -97
  223. package/src/lib/checkpoint-timeline-authority.js +522 -0
  224. package/src/lib/checkpoint-timeline.js +642 -0
  225. package/src/lib/config-keys.js +18 -119
  226. package/src/lib/config-manager.js +446 -27
  227. package/src/lib/config-redaction.js +57 -0
  228. package/src/lib/config-schema.js +853 -0
  229. package/src/lib/cowork-mcp-tools.js +5 -0
  230. package/src/lib/cowork-task-runner.js +458 -63
  231. package/src/lib/delivery-coordinator.js +1234 -0
  232. package/src/lib/delivery-evidence.js +450 -0
  233. package/src/lib/doctor-checkup.js +199 -14
  234. package/src/lib/executable-path.js +34 -0
  235. package/src/lib/fatal-handler.js +16 -0
  236. package/src/lib/file-checkpoint.js +4909 -186
  237. package/src/lib/file-lines.js +125 -0
  238. package/src/lib/headless-config-command.js +16 -5
  239. package/src/lib/hook-runner.cjs +139 -38
  240. package/src/lib/hooks-v2-workspace-context.js +10 -0
  241. package/src/lib/impacted-gate-selector.js +424 -0
  242. package/src/lib/interaction-adapter.js +62 -0
  243. package/src/lib/jsonl-session-store.js +1 -35
  244. package/src/lib/logger.js +66 -14
  245. package/src/lib/managed-checkpoint-render.js +60 -0
  246. package/src/lib/managed-tool-checkpoint.js +194 -0
  247. package/src/lib/mcp-call-ledger-store.js +1069 -0
  248. package/src/lib/mcp-call-ledger.js +747 -0
  249. package/src/lib/mcp-client.js +5 -0
  250. package/src/lib/mcp-conflict-scheduler.js +468 -0
  251. package/src/lib/mcp-effect-contract.js +102 -0
  252. package/src/lib/mcp-headers-helper-trust.js +145 -0
  253. package/src/lib/mcp-headers-helper.js +616 -0
  254. package/src/lib/mcp-host-recovery-runtime.js +131 -0
  255. package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
  256. package/src/lib/mcp-recovery-adjudication.js +443 -0
  257. package/src/lib/output-context.js +86 -0
  258. package/src/lib/packer/file-identity.js +170 -0
  259. package/src/lib/packer/index.js +3 -0
  260. package/src/lib/packer/native-update-state.js +277 -0
  261. package/src/lib/packer/pack-update-applier.js +1983 -136
  262. package/src/lib/packer/pack-update-checker.js +34 -0
  263. package/src/lib/packer/pack-update-downloader.js +875 -86
  264. package/src/lib/packer/pack-update-signature.js +86 -0
  265. package/src/lib/packer/pkg-config-generator.js +6 -0
  266. package/src/lib/paths.js +65 -10
  267. package/src/lib/plan-mode.js +831 -14
  268. package/src/lib/plan-persistence.js +540 -0
  269. package/src/lib/plugin-runtime/hooks.js +227 -69
  270. package/src/lib/plugin-runtime/plugin-options.js +189 -44
  271. package/src/lib/pr-automation-policy.js +146 -8
  272. package/src/lib/process-execution-broker/credential-agent.js +7 -0
  273. package/src/lib/process-execution-broker/index.js +491 -13
  274. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  275. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  276. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  277. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  278. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  279. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  280. package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
  281. package/src/lib/project-instructions.js +165 -29
  282. package/src/lib/project-mcp-trust.js +67 -0
  283. package/src/lib/public-ide-capability-manifest.js +272 -0
  284. package/src/lib/public-ide-capability-validation.js +351 -0
  285. package/src/lib/secret-store.js +267 -43
  286. package/src/lib/secure-file-identity.js +433 -0
  287. package/src/lib/secure-fs.js +1089 -0
  288. package/src/lib/security-audit.js +58 -0
  289. package/src/lib/service-manager.js +34 -13
  290. package/src/lib/session-budget-runtime.js +1452 -0
  291. package/src/lib/session-host-snapshot.js +388 -0
  292. package/src/lib/session-message-provenance.js +429 -0
  293. package/src/lib/session-projection.js +804 -0
  294. package/src/lib/session-resource-budget.js +1495 -0
  295. package/src/lib/settings-hooks.cjs +138 -22
  296. package/src/lib/settings-loader.cjs +25 -2
  297. package/src/lib/side-effect-ledger-store.js +45 -23
  298. package/src/lib/skill-execution-identity.js +217 -0
  299. package/src/lib/skill-loader.js +299 -18
  300. package/src/lib/sub-agent-context.js +236 -28
  301. package/src/lib/sub-agent-registry.js +5 -0
  302. package/src/lib/subagent-contract.js +94 -0
  303. package/src/lib/todo-manager.js +185 -18
  304. package/src/lib/todo-persistence.js +417 -0
  305. package/src/lib/turn-binding-store.js +41 -23
  306. package/src/lib/turn-binding.js +160 -2
  307. package/src/lib/with-file-lock.js +165 -24
  308. package/src/lib/ws-chat-handler.js +21 -0
  309. package/src/program-base.js +18 -0
  310. package/src/repl/advisor-command.js +103 -0
  311. package/src/repl/agent-repl.js +2175 -146
  312. package/src/repl/btw-command.js +93 -10
  313. package/src/repl/clipboard-image.js +119 -0
  314. package/src/repl/config-summary.js +64 -108
  315. package/src/repl/prompt-editor.js +233 -0
  316. package/src/repl/prompt-interactions.js +344 -0
  317. package/src/repl/prompt-stash.js +354 -0
  318. package/src/repl/prompt-suggestions.js +312 -0
  319. package/src/repl/repl-keybindings.js +227 -0
  320. package/src/repl/session-recap.js +217 -0
  321. package/src/repl/slash-command-registry.js +22 -0
  322. package/src/repl/terminal-layout.js +88 -0
  323. package/src/runtime/agent-core.js +1630 -447
  324. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  325. package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
  326. package/src/runtime/diagnostics.js +32 -13
  327. package/src/runtime/headless-runner.js +310 -118
  328. package/src/runtime/headless-stream.js +356 -95
  329. package/src/runtime/mcp-config.js +148 -14
  330. package/src/runtime/policies/agent-policy.js +14 -0
  331. package/src/runtime/status-diagnostics-lite.js +137 -0
  332. package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
  333. package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
  334. package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
  335. package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
  336. package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
  337. package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
@@ -32,6 +32,38 @@ import {
32
32
  runWithHostHooksV2Workspace,
33
33
  } from "../../lib/hooks-v2-workspace-context.js";
34
34
  import { createWsApprovalGate } from "./ws-approval-gate.js";
35
+ import { createSessionMcpLedgerSink } from "../../lib/mcp-call-ledger-store.js";
36
+ import { createMcpHostRecoveryRuntime } from "../../lib/mcp-host-recovery-runtime.js";
37
+ import {
38
+ claimWsTurnIfHead,
39
+ computeWsTurnInputDigest,
40
+ createWsTurnClaimId,
41
+ normalizeWsTurnRequestId,
42
+ readVerifiedWsTurnState,
43
+ settleWsTurnClaim,
44
+ } from "../../harness/jsonl-session-store.js";
45
+
46
+ const CANONICAL_WS_TURN_QUEUES = new Map();
47
+ const CANONICAL_WS_CLAIM_CAS_ATTEMPTS = 4;
48
+
49
+ function runCanonicalWsTurnExclusive(sessionId, task) {
50
+ const previous = CANONICAL_WS_TURN_QUEUES.get(sessionId) || Promise.resolve();
51
+ let release;
52
+ const barrier = new Promise((resolve) => {
53
+ release = resolve;
54
+ });
55
+ const tail = previous.catch(() => undefined).then(() => barrier);
56
+ CANONICAL_WS_TURN_QUEUES.set(sessionId, tail);
57
+ return previous
58
+ .catch(() => undefined)
59
+ .then(task)
60
+ .finally(() => {
61
+ release();
62
+ if (CANONICAL_WS_TURN_QUEUES.get(sessionId) === tail) {
63
+ CANONICAL_WS_TURN_QUEUES.delete(sessionId);
64
+ }
65
+ });
66
+ }
35
67
 
36
68
  export class WSAgentHandler {
37
69
  /**
@@ -40,7 +72,14 @@ export class WSAgentHandler {
40
72
  * @param {import("../../lib/interaction-adapter.js").WebSocketInteractionAdapter} options.interaction
41
73
  * @param {object} [options.db]
42
74
  */
43
- constructor({ session, interaction, db, approvalGate }) {
75
+ constructor({
76
+ session,
77
+ interaction,
78
+ db,
79
+ approvalGate,
80
+ agentLoop: runAgentLoop,
81
+ canonicalSessionStore,
82
+ }) {
44
83
  this.session = session;
45
84
  this.interaction = interaction;
46
85
  this.db = db || null;
@@ -58,6 +97,325 @@ export class WSAgentHandler {
58
97
  // injected): approvalGate stays null and loopOptions are byte-identical.
59
98
  this._approvalGate = approvalGate || null;
60
99
  this._approvalGateInit = Boolean(approvalGate);
100
+ this._mcpRecoveryRuntime = null;
101
+ this._mcpRecoveryRevision = null;
102
+ this._mcpRecoveryClient = null;
103
+ this._agentLoop = runAgentLoop || agentLoop;
104
+ this._canonicalSessionStore = {
105
+ claimWsTurnIfHead,
106
+ computeWsTurnInputDigest,
107
+ createWsTurnClaimId,
108
+ normalizeWsTurnRequestId,
109
+ readVerifiedWsTurnState,
110
+ settleWsTurnClaim,
111
+ ...(canonicalSessionStore || {}),
112
+ };
113
+ const explicitHostPrefix = Array.isArray(
114
+ session?._canonicalHostSystemPrefix,
115
+ )
116
+ ? session._canonicalHostSystemPrefix
117
+ : null;
118
+ const hostSystemPrefix = explicitHostPrefix || [];
119
+ if (!explicitHostPrefix) {
120
+ for (const message of Array.isArray(session?.messages)
121
+ ? session.messages
122
+ : []) {
123
+ if (message?.role !== "system") break;
124
+ hostSystemPrefix.push(Object.freeze({ ...message }));
125
+ }
126
+ }
127
+ this._canonicalHostSystemPrefix = explicitHostPrefix
128
+ ? explicitHostPrefix
129
+ : Object.freeze(hostSystemPrefix);
130
+ }
131
+
132
+ _applyCanonicalMessages(messages) {
133
+ const canonicalMessages = Array.isArray(messages) ? messages : [];
134
+ this.session.messages = [
135
+ ...this._canonicalHostSystemPrefix,
136
+ ...canonicalMessages,
137
+ ];
138
+
139
+ // Resume recovery notices are host-owned runtime context rather than
140
+ // canonical transcript content. Preserve the current notice separately,
141
+ // after the leading system block, on every authoritative refresh.
142
+ const recoveryNotice = this.session._resumeRecoveryNoticeMessage || null;
143
+ if (recoveryNotice) {
144
+ let insertionIndex = 0;
145
+ while (this.session.messages[insertionIndex]?.role === "system") {
146
+ insertionIndex += 1;
147
+ }
148
+ this.session.messages.splice(insertionIndex, 0, recoveryNotice);
149
+ }
150
+ return this.session.messages;
151
+ }
152
+
153
+ _canonicalTurnFromAuthority(state, requestId, userMessage, inputDigest) {
154
+ const status = state?.status || (state?.turn ? "completed" : "none");
155
+ this._applyCanonicalMessages(state?.messages || []);
156
+ if (status === "completed" && state.turn) {
157
+ return {
158
+ requestId,
159
+ userMessage,
160
+ inputDigest,
161
+ status,
162
+ settled: true,
163
+ replayed: true,
164
+ response: state.turn.assistant.content,
165
+ };
166
+ }
167
+ if (status === "failed") {
168
+ return {
169
+ requestId,
170
+ userMessage,
171
+ inputDigest,
172
+ status,
173
+ settled: true,
174
+ replayed: true,
175
+ failureCode: state.settlement?.failure?.code || "CC_WS_TURN_FAILED",
176
+ };
177
+ }
178
+ if (status === "pending") {
179
+ return {
180
+ requestId,
181
+ userMessage,
182
+ inputDigest,
183
+ status,
184
+ settled: false,
185
+ pending: true,
186
+ replayed: true,
187
+ };
188
+ }
189
+ return null;
190
+ }
191
+
192
+ _claimCanonicalTurn(userMessage, requestId) {
193
+ const inputDigest =
194
+ this._canonicalSessionStore.computeWsTurnInputDigest(userMessage);
195
+ let state = this._canonicalSessionStore.readVerifiedWsTurnState(
196
+ this.session.id,
197
+ requestId,
198
+ { inputDigest },
199
+ );
200
+ let terminal = this._canonicalTurnFromAuthority(
201
+ state,
202
+ requestId,
203
+ userMessage,
204
+ inputDigest,
205
+ );
206
+ if (terminal) return terminal;
207
+
208
+ const opaqueClaimId = this._canonicalSessionStore.createWsTurnClaimId();
209
+ let lastConflict = null;
210
+ for (
211
+ let attempt = 1;
212
+ attempt <= CANONICAL_WS_CLAIM_CAS_ATTEMPTS;
213
+ attempt++
214
+ ) {
215
+ try {
216
+ const claimed = this._canonicalSessionStore.claimWsTurnIfHead(
217
+ this.session.id,
218
+ { requestId, user: userMessage, inputDigest, opaqueClaimId },
219
+ state.headHash,
220
+ );
221
+ if (!claimed?.acquired) {
222
+ terminal = this._canonicalTurnFromAuthority(
223
+ claimed,
224
+ requestId,
225
+ userMessage,
226
+ inputDigest,
227
+ );
228
+ if (terminal) return terminal;
229
+ throw Object.assign(
230
+ new Error("Canonical WebSocket request claim was not acquired"),
231
+ { code: "CC_WS_TURN_CLAIM_NOT_ACQUIRED" },
232
+ );
233
+ }
234
+ this._applyCanonicalMessages(claimed.messages);
235
+ const baseMessages = [...this.session.messages];
236
+ this.session.messages.push({ role: "user", content: userMessage });
237
+ return {
238
+ requestId,
239
+ userMessage,
240
+ inputDigest,
241
+ opaqueClaimId,
242
+ status: "pending",
243
+ claimAcquired: true,
244
+ settled: false,
245
+ settlementAttempted: false,
246
+ failureSettlementAttempted: false,
247
+ replayed: false,
248
+ response: null,
249
+ baseMessages,
250
+ };
251
+ } catch (error) {
252
+ if (error?.code !== "SESSION_REVISION_STALE") throw error;
253
+ lastConflict = error;
254
+ state = this._canonicalSessionStore.readVerifiedWsTurnState(
255
+ this.session.id,
256
+ requestId,
257
+ { inputDigest },
258
+ );
259
+ terminal = this._canonicalTurnFromAuthority(
260
+ state,
261
+ requestId,
262
+ userMessage,
263
+ inputDigest,
264
+ );
265
+ if (terminal) return terminal;
266
+ }
267
+ }
268
+ const error = new Error(
269
+ "Canonical WebSocket claim could not settle after revision conflicts",
270
+ { cause: lastConflict },
271
+ );
272
+ error.code = "CC_WS_TURN_CLAIM_CAS_RETRY_EXHAUSTED";
273
+ throw error;
274
+ }
275
+
276
+ _settleCanonicalSuccess(turn, assistantContent) {
277
+ if (typeof assistantContent !== "string" || !assistantContent.trim()) {
278
+ const error = new Error(
279
+ "Canonical WebSocket turn produced no assistant response",
280
+ );
281
+ error.code = "CC_WS_EMPTY_ASSISTANT_RESPONSE";
282
+ throw error;
283
+ }
284
+ turn.settlementAttempted = true;
285
+ const settled = this._canonicalSessionStore.settleWsTurnClaim(
286
+ this.session.id,
287
+ {
288
+ requestId: turn.requestId,
289
+ inputDigest: turn.inputDigest,
290
+ opaqueClaimId: turn.opaqueClaimId,
291
+ outcome: "completed",
292
+ user: turn.userMessage,
293
+ assistant: assistantContent,
294
+ },
295
+ );
296
+ if (settled?.status !== "completed" || !settled.turn) {
297
+ const error = new Error("Canonical WebSocket success was not settled");
298
+ error.code = "CC_WS_TURN_SETTLEMENT_UNKNOWN";
299
+ error.commitState = "unknown";
300
+ throw error;
301
+ }
302
+ this._applyCanonicalMessages(settled.messages);
303
+ turn.settled = true;
304
+ turn.status = "completed";
305
+ turn.response = settled.turn.assistant.content;
306
+ turn.replayed = settled.deduplicated === true;
307
+ return { content: turn.response, replayed: turn.replayed };
308
+ }
309
+
310
+ _settleCanonicalFailure(turn, failureCode) {
311
+ if (
312
+ !turn?.claimAcquired ||
313
+ turn.settled ||
314
+ turn.settlementAttempted ||
315
+ turn.failureSettlementAttempted
316
+ ) {
317
+ return false;
318
+ }
319
+ turn.failureSettlementAttempted = true;
320
+ const settled = this._canonicalSessionStore.settleWsTurnClaim(
321
+ this.session.id,
322
+ {
323
+ requestId: turn.requestId,
324
+ inputDigest: turn.inputDigest,
325
+ opaqueClaimId: turn.opaqueClaimId,
326
+ outcome: "failed",
327
+ failureCode,
328
+ },
329
+ );
330
+ if (settled?.status !== "failed") {
331
+ const error = new Error("Canonical WebSocket failure was not settled");
332
+ error.code = "CC_WS_TURN_SETTLEMENT_UNKNOWN";
333
+ error.commitState = "unknown";
334
+ throw error;
335
+ }
336
+ turn.settled = true;
337
+ turn.status = "failed";
338
+ this._applyCanonicalMessages(settled.messages);
339
+ return true;
340
+ }
341
+
342
+ _ensureMcpRecoveryRuntime() {
343
+ const { session } = this;
344
+ const revision = Number(session.mcpLedgerRecoveryRevision || 0);
345
+ const rawClient = session.mcpClient || null;
346
+ if (!this._mcpRecoveryRuntime || this._mcpRecoveryClient !== rawClient) {
347
+ const controller = this._mcpRecoveryRuntime?.controller || null;
348
+ // Only a newly verified resume revision may lower an existing runtime
349
+ // latch. A transport/client replacement by itself preserves settlement
350
+ // outcome-unknown authority across the new wrapper.
351
+ if (
352
+ controller &&
353
+ this._mcpRecoveryRevision !== revision &&
354
+ typeof controller.replaceVerifiedRecovery === "function"
355
+ ) {
356
+ controller.replaceVerifiedRecovery(
357
+ session.mcpLedgerRecovery || {
358
+ incidents: [],
359
+ unsettled: [],
360
+ replayDenied: [],
361
+ },
362
+ );
363
+ }
364
+ this._mcpRecoveryRuntime = createMcpHostRecoveryRuntime({
365
+ bundle: {
366
+ mcpClient: rawClient,
367
+ externalToolDescriptors: session.externalToolDescriptors || {},
368
+ externalToolExecutors: session.externalToolExecutors || {},
369
+ },
370
+ rawClient,
371
+ sessionId: session.id,
372
+ sink: createSessionMcpLedgerSink(session.id),
373
+ recovery: session.mcpLedgerRecovery || null,
374
+ controller,
375
+ });
376
+ this._mcpRecoveryClient = rawClient;
377
+ this._mcpRecoveryRevision = revision;
378
+ } else if (this._mcpRecoveryRevision !== revision) {
379
+ this._mcpRecoveryRuntime.controller.replaceVerifiedRecovery(
380
+ session.mcpLedgerRecovery || {
381
+ incidents: [],
382
+ unsettled: [],
383
+ replayDenied: [],
384
+ },
385
+ );
386
+ this._mcpRecoveryRevision = revision;
387
+ }
388
+ return this._mcpRecoveryRuntime;
389
+ }
390
+
391
+ refreshMcpRecoveryRuntime() {
392
+ try {
393
+ return this._ensureMcpRecoveryRuntime();
394
+ } catch (cause) {
395
+ const error = new Error("WS MCP recovery runtime refresh failed", {
396
+ cause,
397
+ });
398
+ try {
399
+ error.code = cause?.code || "CC_MCP_RECOVERY_REFRESH_FAILED";
400
+ } catch {
401
+ error.code = "CC_MCP_RECOVERY_REFRESH_FAILED";
402
+ }
403
+ // If an earlier runtime exists, make it unusable immediately. The
404
+ // session protocol also attaches an ALL-blocking projection so a future
405
+ // runtime cannot silently recover as clean.
406
+ this._mcpRecoveryRuntime?.controller?.latchAll?.(error.code);
407
+ throw error;
408
+ }
409
+ }
410
+
411
+ _recordSessionState(type, payload = {}) {
412
+ if (typeof this.session?._recordSessionStateEvent !== "function") return;
413
+ try {
414
+ this.session._recordSessionStateEvent(type, payload);
415
+ } catch (_err) {
416
+ // Recovery persistence is best-effort for the live turn. A persisted
417
+ // running marker is reconciled fail-closed when the session resumes.
418
+ }
61
419
  }
62
420
 
63
421
  /** Resolve the session's approval gate (lazy, opt-in; null when disabled). */
@@ -80,6 +438,27 @@ export class WSAgentHandler {
80
438
  * @param {string} [requestId] - id from ws message for response correlation
81
439
  */
82
440
  async handleMessage(userMessage, requestId) {
441
+ if (this.session?.canonicalJsonlSession === true) {
442
+ let canonicalRequestId;
443
+ try {
444
+ canonicalRequestId =
445
+ this._canonicalSessionStore.normalizeWsTurnRequestId(requestId);
446
+ } catch (error) {
447
+ this.interaction.emit("error", {
448
+ requestId: null,
449
+ code: error?.code || "CC_WS_REQUEST_ID_INVALID",
450
+ message: error?.message || "WebSocket request id is invalid",
451
+ });
452
+ return;
453
+ }
454
+ return runCanonicalWsTurnExclusive(this.session.id, () =>
455
+ this._handleMessage(userMessage, canonicalRequestId),
456
+ );
457
+ }
458
+ return this._handleMessage(userMessage, requestId);
459
+ }
460
+
461
+ async _handleMessage(userMessage, requestId) {
83
462
  if (this._processing) {
84
463
  this.interaction.emit("error", {
85
464
  requestId,
@@ -95,12 +474,48 @@ export class WSAgentHandler {
95
474
  this._activeRequestId = requestId || null;
96
475
  let sideEffectLedger = null;
97
476
  let diffReviewFollowUps = null;
477
+ let runRecorded = false;
478
+ let canonicalTurn = null;
98
479
 
99
480
  try {
100
481
  const { session } = this;
101
482
 
102
- // Add user message
103
- session.messages.push({ role: "user", content: userMessage });
483
+ if (session.canonicalJsonlSession === true) {
484
+ canonicalTurn = this._claimCanonicalTurn(userMessage, requestId);
485
+ if (canonicalTurn.status === "completed") {
486
+ this.interaction.emit("response-complete", {
487
+ requestId,
488
+ content: canonicalTurn.response,
489
+ replayed: true,
490
+ });
491
+ return;
492
+ }
493
+ if (canonicalTurn.status === "failed") {
494
+ this.interaction.emit("error", {
495
+ requestId,
496
+ code: canonicalTurn.failureCode,
497
+ message: "WebSocket request previously settled as failed",
498
+ replayed: true,
499
+ });
500
+ return;
501
+ }
502
+ if (canonicalTurn.pending) {
503
+ this.interaction.emit("response-pending", {
504
+ requestId,
505
+ status: "pending",
506
+ code: "CC_WS_TURN_PENDING",
507
+ remediation: "adjudicate_or_use_new_request_id",
508
+ });
509
+ return;
510
+ }
511
+ } else {
512
+ session.messages.push({ role: "user", content: userMessage });
513
+ }
514
+ this._recordSessionState("run.started", {
515
+ requestId: requestId || null,
516
+ startedAt: new Date().toISOString(),
517
+ });
518
+ runRecorded = true;
104
519
 
105
520
  // Auto-select model based on task type — runnable-first: never switch
106
521
  // onto a provider with no usable key (you'd just 401). Keep the
@@ -134,6 +549,7 @@ export class WSAgentHandler {
134
549
  interaction: this.interaction,
135
550
  db: this.db,
136
551
  });
552
+ const mcpRecoveryRuntime = this._ensureMcpRecoveryRuntime();
137
553
 
138
554
  // Run agent loop
139
555
  const loopOptions = {
@@ -152,6 +568,11 @@ export class WSAgentHandler {
152
568
  externalToolDescriptors: session.externalToolDescriptors || {},
153
569
  externalToolExecutors: session.externalToolExecutors || {},
154
570
  mcpClient: session.mcpClient || null,
571
+ mcpHostClient: mcpRecoveryRuntime.client || session.mcpClient || null,
572
+ // MCP calls must use the same durable prewrite/settlement contract as
573
+ // headless and REPL sessions. For unknown/write/destructive effects a
574
+ // failed prewrite blocks the network call inside agent-core.
575
+ mcpCallLedger: mcpRecoveryRuntime.ledger,
155
576
  shellPolicyOverrides: session.shellPolicyOverrides || null,
156
577
  slotFiller,
157
578
  interaction: this.interaction,
@@ -169,9 +590,13 @@ export class WSAgentHandler {
169
590
  sideEffectLedger = loadSideEffectLedger(session.id);
170
591
  diffReviewFollowUps = new DiffReviewFollowUpTracker(sideEffectLedger);
171
592
  let currentSideEffectOpId = null;
593
+ let currentTodoWrite = null;
172
594
 
173
595
  const executeAgentTurn = async () => {
174
- for await (const event of agentLoop(session.messages, loopOptions)) {
596
+ for await (const event of this._agentLoop(
597
+ session.messages,
598
+ loopOptions,
599
+ )) {
175
600
  switch (event.type) {
176
601
  case "slot-filling":
177
602
  this.interaction.emit("slot-filling", {
@@ -189,6 +614,14 @@ export class WSAgentHandler {
189
614
  display: formatToolArgs(event.tool, event.args),
190
615
  });
191
616
  currentSideEffectOpId = null;
617
+ currentTodoWrite =
618
+ event.tool === "todo_write" && Array.isArray(event.args?.todos)
619
+ ? event.args.todos.map((todo) => ({
620
+ id: todo.id,
621
+ content: todo.content,
622
+ status: todo.status,
623
+ }))
624
+ : null;
192
625
  {
193
626
  const se = classifyToolSideEffect(event.tool, event.args);
194
627
  if (se) {
@@ -219,6 +652,23 @@ export class WSAgentHandler {
219
652
  result: event.result,
220
653
  error: event.error,
221
654
  });
655
+ if (
656
+ event.tool === "todo_write" &&
657
+ currentTodoWrite &&
658
+ !event.error &&
659
+ !event.result?.error &&
660
+ event.result?.success === true &&
661
+ Number.isSafeInteger(event.result.revision)
662
+ ) {
663
+ this._recordSessionState("todo.snapshot", {
664
+ todo: {
665
+ sessionId: session.id,
666
+ revision: event.result.revision,
667
+ todos: currentTodoWrite,
668
+ },
669
+ });
670
+ }
671
+ currentTodoWrite = null;
222
672
  if (currentSideEffectOpId) {
223
673
  const err = event.error || event.result?.error || null;
224
674
  if (event.result?._diffReviewAudit) {
@@ -240,16 +690,28 @@ export class WSAgentHandler {
240
690
  break;
241
691
 
242
692
  case "response-complete":
243
- if (event.content) {
244
- session.messages.push({
245
- role: "assistant",
693
+ if (session.canonicalJsonlSession === true) {
694
+ const settled = this._settleCanonicalSuccess(
695
+ canonicalTurn,
696
+ event.content,
697
+ );
698
+ this.interaction.emit("response-complete", {
699
+ requestId,
700
+ content: settled.content,
701
+ replayed: settled.replayed,
702
+ });
703
+ } else {
704
+ if (event.content) {
705
+ session.messages.push({
706
+ role: "assistant",
707
+ content: event.content,
708
+ });
709
+ }
710
+ this.interaction.emit("response-complete", {
711
+ requestId,
246
712
  content: event.content,
247
713
  });
248
714
  }
249
- this.interaction.emit("response-complete", {
250
- requestId,
251
- content: event.content,
252
- });
253
715
  break;
254
716
  }
255
717
  }
@@ -273,6 +735,13 @@ export class WSAgentHandler {
273
735
  } else {
274
736
  await runAgentTurn();
275
737
  }
738
+ if (session.canonicalJsonlSession === true && !canonicalTurn?.settled) {
739
+ const error = new Error(
740
+ "Canonical WebSocket turn completed without an assistant response",
741
+ );
742
+ error.code = "CC_WS_EMPTY_ASSISTANT_RESPONSE";
743
+ throw error;
744
+ }
276
745
  if (
277
746
  diffReviewFollowUps.complete(sideEffectLedger, {
278
747
  status: "completed-without-reproposal",
@@ -284,6 +753,40 @@ export class WSAgentHandler {
284
753
  // Update last activity
285
754
  session.lastActivity = new Date().toISOString();
286
755
  } catch (err) {
756
+ let surfacedError = err;
757
+ if (
758
+ canonicalTurn?.claimAcquired &&
759
+ !canonicalTurn.settled &&
760
+ !canonicalTurn.settlementAttempted
761
+ ) {
762
+ const failureCode = isAbortError(err)
763
+ ? "CC_WS_TURN_INTERRUPTED"
764
+ : err?.code === "CC_WS_EMPTY_ASSISTANT_RESPONSE"
765
+ ? "CC_WS_EMPTY_ASSISTANT_RESPONSE"
766
+ : "CC_WS_TURN_FAILED";
767
+ try {
768
+ this._settleCanonicalFailure(canonicalTurn, failureCode);
769
+ } catch (settlementError) {
770
+ surfacedError = new Error(
771
+ "WebSocket turn settlement is outcome-unknown; explicit adjudication or a new request id is required",
772
+ { cause: settlementError },
773
+ );
774
+ surfacedError.code = "CC_WS_TURN_SETTLEMENT_UNKNOWN";
775
+ }
776
+ } else if (
777
+ canonicalTurn?.claimAcquired &&
778
+ canonicalTurn.settlementAttempted &&
779
+ !canonicalTurn.settled
780
+ ) {
781
+ surfacedError = new Error(
782
+ "WebSocket turn settlement is outcome-unknown; explicit adjudication or a new request id is required",
783
+ { cause: err },
784
+ );
785
+ surfacedError.code = "CC_WS_TURN_SETTLEMENT_UNKNOWN";
786
+ }
787
+ if (canonicalTurn?.baseMessages && canonicalTurn.status !== "completed") {
788
+ this.session.messages = [...canonicalTurn.baseMessages];
789
+ }
287
790
  if (
288
791
  sideEffectLedger &&
289
792
  diffReviewFollowUps?.complete(sideEffectLedger, {
@@ -299,18 +802,27 @@ export class WSAgentHandler {
299
802
 
300
803
  this.interaction.emit("error", {
301
804
  requestId,
302
- code: "AGENT_ERROR",
303
- message: err.message,
805
+ code: surfacedError?.code || "AGENT_ERROR",
806
+ message: surfacedError.message,
304
807
  });
305
808
 
306
809
  // Record error in context engine
307
810
  if (this.session.contextEngine) {
308
811
  this.session.contextEngine.recordError({
309
812
  step: "ws-agent-loop",
310
- message: err.message,
813
+ message: surfacedError.message,
311
814
  });
312
815
  }
313
816
  } finally {
817
+ if (canonicalTurn?.baseMessages && canonicalTurn.status !== "completed") {
818
+ this.session.messages = [...canonicalTurn.baseMessages];
819
+ }
820
+ if (runRecorded) {
821
+ this._recordSessionState("run.settled", {
822
+ requestId: requestId || null,
823
+ settledAt: new Date().toISOString(),
824
+ });
825
+ }
314
826
  this._processing = false;
315
827
  if (this._abortController === abortController) {
316
828
  this._abortController = null;
@@ -334,6 +846,12 @@ export class WSAgentHandler {
334
846
  this.interaction.rejectAllPending(reason);
335
847
  }
336
848
 
849
+ this._recordSessionState("run.interrupted", {
850
+ requestId: interruptedRequestId,
851
+ interruptedAt: new Date().toISOString(),
852
+ reason: "client_interrupt",
853
+ });
854
+
337
855
  return {
338
856
  sessionId: this.session?.id || null,
339
857
  interrupted: true,
@@ -343,6 +861,8 @@ export class WSAgentHandler {
343
861
  }
344
862
 
345
863
  destroy() {
864
+ const wasProcessing = this._processing;
865
+ const interruptedRequestId = this._activeRequestId || null;
346
866
  const reason = createAbortError("Session closed");
347
867
  if (this._abortController && !this._abortController.signal.aborted) {
348
868
  this._abortController.abort(reason);
@@ -350,6 +870,13 @@ export class WSAgentHandler {
350
870
  if (typeof this.interaction?.rejectAllPending === "function") {
351
871
  this.interaction.rejectAllPending(reason);
352
872
  }
873
+ if (wasProcessing) {
874
+ this._recordSessionState("run.interrupted", {
875
+ requestId: interruptedRequestId,
876
+ interruptedAt: new Date().toISOString(),
877
+ reason: "session_closed",
878
+ });
879
+ }
353
880
  }
354
881
 
355
882
  /**