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
@@ -0,0 +1,804 @@
1
+ /**
2
+ * Canonical, read-only session projection consumed by every IDE surface.
3
+ *
4
+ * The CLI/control plane owns lifecycle and mutations. IDEs receive this
5
+ * deliberately small JSON document and must never infer extra capabilities
6
+ * from local state files. Unsupported checkpoint/archive/detach actions stay
7
+ * explicit and unavailable.
8
+ */
9
+
10
+ import { createHash } from "node:crypto";
11
+ import {
12
+ deriveSessionState,
13
+ normalizeSessionState,
14
+ SESSION_STATES,
15
+ } from "./session-lifecycle.js";
16
+
17
+ export const SESSION_PROJECTION_SCHEMA = "chainlesschain.session-projection/v1";
18
+ export const SESSION_PROJECTION_VERSION = 1;
19
+
20
+ export const PROJECTION_KINDS = Object.freeze([
21
+ "local",
22
+ "background",
23
+ "remote",
24
+ "team",
25
+ "workflow",
26
+ ]);
27
+
28
+ export const PROJECTION_STATES = Object.freeze([
29
+ "working",
30
+ "needs_input",
31
+ "blocked",
32
+ "done",
33
+ "failed",
34
+ "stopped",
35
+ ]);
36
+
37
+ export const PROJECTION_ACTIONS = Object.freeze([
38
+ "dispatch",
39
+ "peek",
40
+ "reply",
41
+ "attach",
42
+ "detach",
43
+ "stop",
44
+ "checkpoint",
45
+ "archive",
46
+ ]);
47
+
48
+ export const PROJECTION_ACTION_EXECUTORS = Object.freeze([
49
+ "cli",
50
+ "terminal",
51
+ "host",
52
+ ]);
53
+
54
+ export const PROJECTION_PROMPT_PLACEHOLDER = "$prompt";
55
+
56
+ const TERMINAL_PROJECTION_STATES = new Set(["done", "failed", "stopped"]);
57
+
58
+ function stableValue(value) {
59
+ if (Array.isArray(value)) return value.map(stableValue);
60
+ if (!value || typeof value !== "object") return value;
61
+ return Object.fromEntries(
62
+ Object.keys(value)
63
+ .sort()
64
+ .map((key) => [key, stableValue(value[key])]),
65
+ );
66
+ }
67
+
68
+ export function projectionRevision(value) {
69
+ return `sha256:${createHash("sha256")
70
+ .update(JSON.stringify(stableValue(value)))
71
+ .digest("hex")}`;
72
+ }
73
+
74
+ export function canonicalSessionId(kind, sourceId) {
75
+ const normalizedKind = PROJECTION_KINDS.includes(kind) ? kind : "local";
76
+ return `${normalizedKind}:${encodeURIComponent(String(sourceId || "unknown"))}`;
77
+ }
78
+
79
+ function asText(value) {
80
+ return typeof value === "string" && value.trim() ? value.trim() : null;
81
+ }
82
+
83
+ function asCount(value) {
84
+ const number = Number(value);
85
+ return Number.isFinite(number) && number > 0 ? Math.floor(number) : 0;
86
+ }
87
+
88
+ function toIso(value) {
89
+ if (value == null || value === "") return null;
90
+ let epoch = null;
91
+ if (typeof value === "number" || /^\d{10,}$/.test(String(value))) {
92
+ const number = Number(value);
93
+ if (Number.isFinite(number) && number > 0) {
94
+ epoch = number < 1e12 ? number * 1000 : number;
95
+ }
96
+ } else {
97
+ const raw = String(value).trim();
98
+ const normalized =
99
+ raw.includes(" ") && !raw.includes("T")
100
+ ? `${raw.replace(" ", "T")}Z`
101
+ : raw;
102
+ const parsed = Date.parse(normalized);
103
+ if (Number.isFinite(parsed)) epoch = parsed;
104
+ }
105
+ if (epoch == null) return null;
106
+ try {
107
+ return new Date(epoch).toISOString();
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+
113
+ function sourceSessionId(source, fallback = null) {
114
+ return (
115
+ asText(source?.sessionId) ||
116
+ asText(source?.agentSessionId) ||
117
+ asText(source?.remoteSessionId) ||
118
+ fallback
119
+ );
120
+ }
121
+
122
+ function sourceSessionIds(kind, source, fallback = null) {
123
+ const ids = [];
124
+ const add = (value) => {
125
+ const id = asText(value);
126
+ if (id && !ids.includes(id)) ids.push(id);
127
+ };
128
+ add(sourceSessionId(source, fallback));
129
+ if (kind === "team") {
130
+ for (const unit of Array.isArray(source?.units) ? source.units : []) {
131
+ add(unit?.sessionId);
132
+ }
133
+ }
134
+ return ids;
135
+ }
136
+
137
+ function mapLifecycleState(source) {
138
+ const phase = String(source?.phase || "")
139
+ .trim()
140
+ .toLowerCase();
141
+ const lifecycle =
142
+ normalizeSessionState(source?.lifecycleState) ||
143
+ deriveSessionState({
144
+ status: source?.status,
145
+ phase: source?.phase,
146
+ connection: source?.connection,
147
+ recovering: source?.recovering,
148
+ cancelRequested: source?.cancelRequested,
149
+ pendingApprovals: source?.pendingApprovals,
150
+ });
151
+
152
+ // Supervisor terminal verdicts are absorbing even when a stale worker phase
153
+ // still says that the previous turn needed input.
154
+ if (lifecycle === SESSION_STATES.COMPLETED) return "done";
155
+ if (lifecycle === SESSION_STATES.FAILED) return "failed";
156
+ if (lifecycle === SESSION_STATES.STOPPED) return "stopped";
157
+
158
+ if (
159
+ (source?.pendingQuestion != null && source.pendingQuestion !== false) ||
160
+ source?.needsInput === true ||
161
+ ["needs_input", "needs-input", "awaiting_input", "question"].includes(phase)
162
+ ) {
163
+ return "needs_input";
164
+ }
165
+
166
+ switch (lifecycle) {
167
+ case SESSION_STATES.WAITING_APPROVAL:
168
+ case SESSION_STATES.CANCELLING:
169
+ case SESSION_STATES.DISCONNECTED:
170
+ case SESSION_STATES.RECOVERING:
171
+ return "blocked";
172
+ default:
173
+ return "working";
174
+ }
175
+ }
176
+
177
+ function mapTeamState(source) {
178
+ const status = String(source?.status || "")
179
+ .trim()
180
+ .toLowerCase();
181
+ if (status === "completed") return "done";
182
+ if (status === "failed") return "failed";
183
+ if (["cancelled", "canceled", "stopped"].includes(status)) return "stopped";
184
+
185
+ const units = Array.isArray(source?.units) ? source.units : [];
186
+ if (
187
+ units.some(
188
+ (unit) =>
189
+ asCount(unit?.sideEffects?.unknown) > 0 ||
190
+ asCount(unit?.sideEffects?.unsettled) > 0,
191
+ )
192
+ ) {
193
+ return "blocked";
194
+ }
195
+ return "working";
196
+ }
197
+
198
+ function mapWorkflowState(source) {
199
+ const stage = String(source?.stage || source?.mode?.stage || "")
200
+ .trim()
201
+ .toLowerCase();
202
+ if (["complete", "completed", "done"].includes(stage)) return "done";
203
+ if (["failed", "error", "errored"].includes(stage)) return "failed";
204
+ if (["cancelled", "canceled", "stopped"].includes(stage)) return "stopped";
205
+ if (
206
+ stage === "plan" &&
207
+ source?.hasPlan === true &&
208
+ source?.approved !== true
209
+ ) {
210
+ return "needs_input";
211
+ }
212
+ // A bare directory is durable workflow residue, not proof of a live writer.
213
+ if (!stage) return "stopped";
214
+ return "working";
215
+ }
216
+
217
+ function preview(executor, argv, { mutates = false, input = null } = {}) {
218
+ return {
219
+ executor,
220
+ argv: argv.map((value) => String(value)),
221
+ mutates: mutates === true,
222
+ input,
223
+ };
224
+ }
225
+
226
+ function actionPreviews(kind, state, source, sourceId, linkedSessionId) {
227
+ const active = !TERMINAL_PROJECTION_STATES.has(state);
228
+ const interactive = source?.interactive === true;
229
+ const linked = Boolean(linkedSessionId);
230
+ const port = Number(source?.port);
231
+ const cwd =
232
+ asText(source?.checkpointCwd) ||
233
+ asText(source?.cwd) ||
234
+ asText(source?.workspace) ||
235
+ asText(source?.repoRoot);
236
+ const checkpointSessionId =
237
+ asText(source?.checkpointSessionId) || linkedSessionId || sourceId;
238
+ const checkpointAvailable =
239
+ source?.checkpointAvailable === true &&
240
+ Boolean(cwd) &&
241
+ Boolean(checkpointSessionId) &&
242
+ ["local", "background", "workflow"].includes(kind);
243
+
244
+ const routes = Object.fromEntries(
245
+ PROJECTION_ACTIONS.map((action) => [action, null]),
246
+ );
247
+
248
+ if (kind === "local") {
249
+ routes.dispatch = preview("host", ["session", "resume", sourceId], {
250
+ mutates: true,
251
+ });
252
+ routes.peek = preview("cli", ["session", "show", sourceId, "--json"]);
253
+ } else if (kind === "background") {
254
+ routes.peek = preview("cli", ["daemon", "view", sourceId, "--json"]);
255
+ if (!active && linked) {
256
+ routes.dispatch = preview(
257
+ "cli",
258
+ ["daemon", "resume", sourceId, PROJECTION_PROMPT_PLACEHOLDER, "--json"],
259
+ { mutates: true, input: "prompt" },
260
+ );
261
+ }
262
+ if (active && interactive) {
263
+ routes.reply = preview("terminal", ["attach", sourceId]);
264
+ routes.attach = preview("terminal", ["attach", sourceId]);
265
+ }
266
+ if (active) {
267
+ routes.stop = preview("cli", ["daemon", "stop", sourceId, "--json"], {
268
+ mutates: true,
269
+ });
270
+ }
271
+ } else if (kind === "remote") {
272
+ routes.peek = preview("cli", ["remote-control", "status", "--json"]);
273
+ if (state === "working" && port > 0) {
274
+ routes.stop = preview(
275
+ "cli",
276
+ ["remote-control", "stop", "--port", String(port), "--json"],
277
+ { mutates: true },
278
+ );
279
+ }
280
+ } else if (kind === "workflow" && cwd) {
281
+ routes.peek = preview("cli", [
282
+ "session",
283
+ "workflow",
284
+ sourceId,
285
+ "--json",
286
+ "--cwd",
287
+ cwd,
288
+ ]);
289
+ }
290
+
291
+ if (checkpointAvailable) {
292
+ routes.checkpoint = preview(
293
+ "cli",
294
+ [
295
+ "checkpoint",
296
+ "create",
297
+ "--dir",
298
+ cwd,
299
+ "--session",
300
+ checkpointSessionId,
301
+ "--json",
302
+ ],
303
+ { mutates: true },
304
+ );
305
+ }
306
+
307
+ return routes;
308
+ }
309
+
310
+ function actionAvailability(kind, state, source, sourceId, linkedSessionId) {
311
+ const active = !TERMINAL_PROJECTION_STATES.has(state);
312
+ const interactive = source?.interactive === true;
313
+ const routes = actionPreviews(kind, state, source, sourceId, linkedSessionId);
314
+
315
+ const reasons = {
316
+ dispatch:
317
+ kind === "background"
318
+ ? active
319
+ ? "background session is already active"
320
+ : "no linked transcript can be dispatched"
321
+ : "this session kind has no dispatch route",
322
+ peek:
323
+ kind === "team"
324
+ ? "team collaboration runs have no run-level CLI detail route"
325
+ : kind === "workflow"
326
+ ? "workflow project root is unavailable"
327
+ : "this session kind has no read-only detail route",
328
+ reply:
329
+ kind === "background"
330
+ ? interactive
331
+ ? "background session is not active"
332
+ : "background session has no interactive transport"
333
+ : "reply is only supported by interactive background sessions",
334
+ attach:
335
+ kind === "background"
336
+ ? interactive
337
+ ? "background session is not active"
338
+ : "background session has no interactive transport"
339
+ : "attach is only supported by interactive background sessions",
340
+ detach:
341
+ "no control-plane attachment id exists; detach is local to the attach client",
342
+ stop: "session is not an active stoppable control-plane process",
343
+ checkpoint:
344
+ kind === "team"
345
+ ? "team checkpoints are task-scoped; no run-level checkpoint route exists"
346
+ : kind === "remote"
347
+ ? "remote sessions have no local checkpoint route"
348
+ : "no git-backed session checkpoint capability is available",
349
+ archive:
350
+ kind === "team"
351
+ ? "the collaboration run store has no non-destructive archive route"
352
+ : kind === "workflow"
353
+ ? "coding workflow state has no non-destructive archive route"
354
+ : "no non-destructive session archive capability is available",
355
+ };
356
+
357
+ return PROJECTION_ACTIONS.map((id) => {
358
+ const available = routes[id] != null;
359
+ return {
360
+ id,
361
+ available,
362
+ reason: available ? null : reasons[id],
363
+ preview: routes[id],
364
+ };
365
+ });
366
+ }
367
+
368
+ function ownerSummary(source, kind) {
369
+ const governance =
370
+ source?.governance && typeof source.governance === "object"
371
+ ? source.governance
372
+ : {};
373
+ const id =
374
+ asText(governance.owner) ||
375
+ asText(source?.owner) ||
376
+ asText(source?.ownerId) ||
377
+ (kind === "remote" ? asText(source?.peerId) : null);
378
+ return {
379
+ type:
380
+ kind === "remote"
381
+ ? "remote-peer"
382
+ : kind === "team"
383
+ ? "team-coordinator"
384
+ : "local-user",
385
+ id,
386
+ };
387
+ }
388
+
389
+ function environmentSummary(source, kind) {
390
+ return {
391
+ cwd:
392
+ asText(source?.cwd) ||
393
+ asText(source?.workspace) ||
394
+ asText(source?.repoRoot),
395
+ host: kind === "remote" ? asText(source?.host) : null,
396
+ port:
397
+ kind === "remote" && Number.isFinite(Number(source?.port))
398
+ ? Number(source.port)
399
+ : null,
400
+ mode: kind === "remote" ? asText(source?.mode) : null,
401
+ };
402
+ }
403
+
404
+ function worktreeSummary(source) {
405
+ if (Array.isArray(source?.units)) {
406
+ const worktrees = source.units.filter(
407
+ (unit) => asText(unit?.worktreePath) || asText(unit?.branch),
408
+ );
409
+ if (worktrees.length > 0 || asText(source?.repoRoot)) {
410
+ const only = worktrees.length === 1 ? worktrees[0] : null;
411
+ return {
412
+ repoRoot: asText(source?.repoRoot),
413
+ path: only ? asText(only.worktreePath) : null,
414
+ branch: only ? asText(only.branch) : null,
415
+ baseSha: null,
416
+ count: worktrees.length,
417
+ };
418
+ }
419
+ }
420
+ const worktree =
421
+ source?.worktree && typeof source.worktree === "object"
422
+ ? source.worktree
423
+ : source;
424
+ const result = {
425
+ repoRoot: asText(worktree?.repoRoot),
426
+ path: asText(worktree?.worktreePath) || asText(worktree?.path),
427
+ branch: asText(worktree?.branch),
428
+ baseSha: asText(worktree?.baseSha),
429
+ };
430
+ return Object.values(result).some(Boolean) ? result : null;
431
+ }
432
+
433
+ function latestBy(rows, field) {
434
+ return [...rows].sort((left, right) => {
435
+ const leftAt = Date.parse(toIso(left?.[field]) || "") || 0;
436
+ const rightAt = Date.parse(toIso(right?.[field]) || "") || 0;
437
+ return rightAt - leftAt;
438
+ })[0];
439
+ }
440
+
441
+ function artifactSummary(sessionIds, artifacts) {
442
+ const ids = new Set(
443
+ (Array.isArray(sessionIds) ? sessionIds : [sessionIds]).filter(Boolean),
444
+ );
445
+ if (ids.size === 0) return { count: 0, latest: null };
446
+ const linked = (Array.isArray(artifacts) ? artifacts : []).filter((entry) =>
447
+ ids.has(entry?.sessionId),
448
+ );
449
+ const latest = linked.length ? latestBy(linked, "createdAt") : null;
450
+ return {
451
+ count: linked.length,
452
+ latest: latest
453
+ ? {
454
+ id: asText(latest.id),
455
+ title: asText(latest.title),
456
+ kind: asText(latest.kind),
457
+ createdAt: toIso(latest.createdAt),
458
+ }
459
+ : null,
460
+ };
461
+ }
462
+
463
+ function prSummary(sessionIds, prLinks) {
464
+ const linked = [];
465
+ for (const sessionId of Array.isArray(sessionIds)
466
+ ? sessionIds
467
+ : [sessionIds]) {
468
+ if (sessionId && Array.isArray(prLinks?.[sessionId])) {
469
+ linked.push(...prLinks[sessionId]);
470
+ }
471
+ }
472
+ const latest = linked.length ? latestBy(linked, "updatedAt") : null;
473
+ return {
474
+ count: linked.length,
475
+ latest: latest
476
+ ? {
477
+ number: Number.isFinite(Number(latest.number))
478
+ ? Number(latest.number)
479
+ : null,
480
+ repo: asText(latest.repo),
481
+ url: asText(latest.url),
482
+ state: asText(latest.state),
483
+ updatedAt: toIso(latest.updatedAt),
484
+ }
485
+ : null,
486
+ };
487
+ }
488
+
489
+ function approvalSummary(source, state) {
490
+ let count = asCount(source?.pendingApprovals);
491
+ if (Array.isArray(source?.units)) {
492
+ count += source.units.reduce(
493
+ (total, unit) =>
494
+ total +
495
+ asCount(unit?.sideEffects?.unknown) +
496
+ asCount(unit?.sideEffects?.unsettled),
497
+ 0,
498
+ );
499
+ }
500
+ const needsInput = state === "needs_input";
501
+ return {
502
+ pending: needsInput || state === "blocked" || count > 0,
503
+ type: needsInput
504
+ ? "input"
505
+ : Array.isArray(source?.units) && count > 0
506
+ ? "adjudication"
507
+ : count > 0
508
+ ? "approval"
509
+ : null,
510
+ count,
511
+ };
512
+ }
513
+
514
+ function lastEventFor(source, state) {
515
+ const at =
516
+ toIso(source?.lastEvent?.at) ||
517
+ toIso(source?.updatedAt) ||
518
+ toIso(source?.updated_at) ||
519
+ toIso(source?.endedAt) ||
520
+ toIso(source?.heartbeatAt) ||
521
+ toIso(source?.startedAt);
522
+ return {
523
+ type: asText(source?.lastEvent?.type) || `state:${state}`,
524
+ at,
525
+ };
526
+ }
527
+
528
+ function projectOne(kind, source, { artifacts, prLinks }) {
529
+ let sourceId;
530
+ if (kind === "remote") {
531
+ const stateFile = asText(source?.stateFile);
532
+ sourceId =
533
+ asText(source?.remoteSessionId) ||
534
+ (Number(source?.port) > 0 ? `port:${Number(source.port)}` : null) ||
535
+ (stateFile
536
+ ? `invalid:${createHash("sha256").update(stateFile).digest("hex").slice(0, 16)}`
537
+ : null) ||
538
+ "invalid";
539
+ } else {
540
+ sourceId =
541
+ asText(source?.id) ||
542
+ asText(source?.runId) ||
543
+ asText(source?.sessionId) ||
544
+ "unknown";
545
+ }
546
+
547
+ let state;
548
+ if (kind === "local") state = "stopped";
549
+ else if (kind === "team") state = mapTeamState(source);
550
+ else if (kind === "workflow") state = mapWorkflowState(source);
551
+ else if (kind === "remote") {
552
+ state = source?.invalid ? "failed" : source?.alive ? "working" : "stopped";
553
+ } else state = mapLifecycleState(source);
554
+
555
+ const linkedSessionIds = sourceSessionIds(
556
+ kind,
557
+ source,
558
+ kind === "local" || kind === "workflow" ? sourceId : null,
559
+ );
560
+ const linkedSessionId =
561
+ linkedSessionIds.length === 1 ? linkedSessionIds[0] : null;
562
+ const actions = actionAvailability(
563
+ kind,
564
+ state,
565
+ source,
566
+ sourceId,
567
+ linkedSessionId,
568
+ );
569
+ const projected = {
570
+ id: canonicalSessionId(kind, sourceId),
571
+ sourceId,
572
+ kind,
573
+ state,
574
+ title:
575
+ asText(source?.title) ||
576
+ (kind === "remote"
577
+ ? `Remote control ${sourceId}`
578
+ : kind === "team"
579
+ ? `Team run ${sourceId}`
580
+ : kind === "workflow"
581
+ ? `Workflow ${sourceId}`
582
+ : `${kind} ${sourceId}`),
583
+ capabilities: actions
584
+ .filter((action) => action.available)
585
+ .map((action) => action.id),
586
+ actions,
587
+ linkedSessionId,
588
+ owner: ownerSummary(source, kind),
589
+ environment: environmentSummary(source, kind),
590
+ worktree: worktreeSummary(source),
591
+ artifact: artifactSummary(linkedSessionIds, artifacts),
592
+ approval: approvalSummary(source, state),
593
+ pr: prSummary(linkedSessionIds, prLinks),
594
+ lastEvent: lastEventFor(source, state),
595
+ };
596
+ return { ...projected, revision: projectionRevision(projected) };
597
+ }
598
+
599
+ function sourceStatus(count, error) {
600
+ return {
601
+ ok: !error,
602
+ count,
603
+ error: error ? String(error).slice(0, 240) : null,
604
+ };
605
+ }
606
+
607
+ /** Build one deterministic projection envelope from already-read sources. */
608
+ export function buildSessionProjection({
609
+ local = [],
610
+ background = [],
611
+ remote = [],
612
+ team = [],
613
+ workflow = [],
614
+ artifacts = [],
615
+ prLinks = {},
616
+ sourceErrors = {},
617
+ generatedAt = new Date().toISOString(),
618
+ } = {}) {
619
+ const context = { artifacts, prLinks };
620
+ const sessions = [
621
+ ...(Array.isArray(local) ? local : []).map((source) =>
622
+ projectOne("local", source, context),
623
+ ),
624
+ ...(Array.isArray(background) ? background : []).map((source) =>
625
+ projectOne("background", source, context),
626
+ ),
627
+ ...(Array.isArray(remote) ? remote : []).map((source) =>
628
+ projectOne("remote", source, context),
629
+ ),
630
+ ...(Array.isArray(team) ? team : []).map((source) =>
631
+ projectOne("team", source, context),
632
+ ),
633
+ ...(Array.isArray(workflow) ? workflow : []).map((source) =>
634
+ projectOne("workflow", source, context),
635
+ ),
636
+ ].sort((left, right) => {
637
+ const priority = { needs_input: 0, blocked: 1, working: 2 };
638
+ const rank = (item) => priority[item.state] ?? 3;
639
+ return (
640
+ rank(left) - rank(right) ||
641
+ (Date.parse(right.lastEvent.at || "") || 0) -
642
+ (Date.parse(left.lastEvent.at || "") || 0) ||
643
+ left.id.localeCompare(right.id)
644
+ );
645
+ });
646
+
647
+ const sources = {
648
+ local: sourceStatus(
649
+ Array.isArray(local) ? local.length : 0,
650
+ sourceErrors.local,
651
+ ),
652
+ background: sourceStatus(
653
+ Array.isArray(background) ? background.length : 0,
654
+ sourceErrors.background,
655
+ ),
656
+ remote: sourceStatus(
657
+ Array.isArray(remote) ? remote.length : 0,
658
+ sourceErrors.remote,
659
+ ),
660
+ team: sourceStatus(
661
+ Array.isArray(team) ? team.length : 0,
662
+ sourceErrors.team,
663
+ ),
664
+ workflow: sourceStatus(
665
+ Array.isArray(workflow) ? workflow.length : 0,
666
+ sourceErrors.workflow,
667
+ ),
668
+ };
669
+ const revision = projectionRevision({
670
+ schema: SESSION_PROJECTION_SCHEMA,
671
+ sources,
672
+ sessions: sessions.map((session) => ({
673
+ id: session.id,
674
+ revision: session.revision,
675
+ })),
676
+ });
677
+ return {
678
+ schema: SESSION_PROJECTION_SCHEMA,
679
+ schemaVersion: SESSION_PROJECTION_VERSION,
680
+ authority: "cli",
681
+ connected: true,
682
+ generatedAt: toIso(generatedAt) || new Date().toISOString(),
683
+ revision,
684
+ sources,
685
+ sessions,
686
+ };
687
+ }
688
+
689
+ function actionRequestError(code, reason, details = {}) {
690
+ return { ok: false, code, reason, ...details };
691
+ }
692
+
693
+ /** Resolve the exact safe action preview shown by one projection revision. */
694
+ export function previewSessionProjectionAction(projection, request = {}) {
695
+ if (
696
+ !projection ||
697
+ projection.authority !== "cli" ||
698
+ projection.connected !== true ||
699
+ typeof projection.revision !== "string"
700
+ ) {
701
+ return actionRequestError(
702
+ "SESSION_PROJECTION_DISCONNECTED",
703
+ "session projection is disconnected",
704
+ );
705
+ }
706
+ if (request.revision !== projection.revision) {
707
+ return actionRequestError(
708
+ "SESSION_PROJECTION_STALE",
709
+ "session projection revision changed",
710
+ { currentRevision: projection.revision },
711
+ );
712
+ }
713
+ const item = projection.sessions?.find((entry) => entry?.id === request.id);
714
+ if (!item || request.itemRevision !== item.revision) {
715
+ return actionRequestError(
716
+ "SESSION_PROJECTION_STALE",
717
+ "session item revision changed",
718
+ { currentItemRevision: item?.revision || null },
719
+ );
720
+ }
721
+ const action = item.actions?.find((entry) => entry?.id === request.action);
722
+ if (!action?.available || !action.preview) {
723
+ return actionRequestError(
724
+ "SESSION_ACTION_UNAVAILABLE",
725
+ action?.reason || "session action is unavailable",
726
+ );
727
+ }
728
+ return {
729
+ ok: true,
730
+ id: item.id,
731
+ action: action.id,
732
+ expectedRevision: projection.revision,
733
+ expectedItemRevision: item.revision,
734
+ preview: stableValue(action.preview),
735
+ };
736
+ }
737
+
738
+ /**
739
+ * Re-check one rendered action against a freshly read authority projection.
740
+ * Envelope churn caused by unrelated rows is allowed; the target item is a
741
+ * strict content-revision CAS.
742
+ */
743
+ export function recheckSessionProjectionAction(
744
+ renderedProjection,
745
+ currentProjection,
746
+ request = {},
747
+ ) {
748
+ const rendered = previewSessionProjectionAction(renderedProjection, request);
749
+ if (!rendered.ok) return rendered;
750
+ if (
751
+ !currentProjection ||
752
+ currentProjection.authority !== "cli" ||
753
+ currentProjection.connected !== true
754
+ ) {
755
+ return actionRequestError(
756
+ "SESSION_PROJECTION_DISCONNECTED",
757
+ "current session projection is disconnected",
758
+ );
759
+ }
760
+ const currentItem = currentProjection.sessions?.find(
761
+ (entry) => entry?.id === request.id,
762
+ );
763
+ if (!currentItem || currentItem.revision !== request.itemRevision) {
764
+ return actionRequestError(
765
+ "SESSION_PROJECTION_STALE",
766
+ "session item changed before dispatch",
767
+ { currentItemRevision: currentItem?.revision || null },
768
+ );
769
+ }
770
+ const currentAction = currentItem.actions?.find(
771
+ (entry) => entry?.id === request.action,
772
+ );
773
+ if (!currentAction?.available || !currentAction.preview) {
774
+ return actionRequestError(
775
+ "SESSION_ACTION_UNAVAILABLE",
776
+ currentAction?.reason || "session action is no longer available",
777
+ );
778
+ }
779
+ return {
780
+ ok: true,
781
+ id: currentItem.id,
782
+ action: currentAction.id,
783
+ expectedRevision: request.revision,
784
+ expectedItemRevision: request.itemRevision,
785
+ currentRevision: currentProjection.revision,
786
+ preview: stableValue(currentAction.preview),
787
+ };
788
+ }
789
+
790
+ /** A fail-closed transport envelope. No stale session actions survive. */
791
+ export function disconnectedSessionProjection(reasonText = "CLI unavailable") {
792
+ const reason = String(reasonText || "CLI unavailable").slice(0, 240);
793
+ return {
794
+ schema: SESSION_PROJECTION_SCHEMA,
795
+ schemaVersion: SESSION_PROJECTION_VERSION,
796
+ authority: "cli",
797
+ connected: false,
798
+ generatedAt: new Date().toISOString(),
799
+ revision: projectionRevision({ connected: false, reason }),
800
+ reason,
801
+ sources: {},
802
+ sessions: [],
803
+ };
804
+ }