chainlesschain 0.162.193 → 0.162.198

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 (215) hide show
  1. package/README.md +29 -6
  2. package/completions/_cc +2 -2
  3. package/completions/cc.bash +2 -2
  4. package/completions/cc.fish +2 -2
  5. package/completions/cc.ps1 +2 -2
  6. package/package.json +1 -1
  7. package/src/assets/web-panel/assets/{AIOps-DBLkaTje.js → AIOps-CSN-D2xb.js} +1 -1
  8. package/src/assets/web-panel/assets/{ActionButton-BAy-Qebo.js → ActionButton-CBSMlGzv.js} +1 -1
  9. package/src/assets/web-panel/assets/{Analytics-Dfb1Mlau.js → Analytics-3yr3Nt2t.js} +3 -3
  10. package/src/assets/web-panel/assets/{AppLayout-BbFkN8m0.js → AppLayout-1b-o3lMH.js} +4 -4
  11. package/src/assets/web-panel/assets/{Artifacts-kM4-cEEH.js → Artifacts-Dr8rJi17.js} +1 -1
  12. package/src/assets/web-panel/assets/{Audit-BSQfYWf3.js → Audit-2V689HC2.js} +1 -1
  13. package/src/assets/web-panel/assets/{BackgroundAgents-CWDbI_fG.js → BackgroundAgents-DIxkNrD_.js} +1 -1
  14. package/src/assets/web-panel/assets/{Backup-BxLPbq_Q.js → Backup-Pg6BcwrE.js} +1 -1
  15. package/src/assets/web-panel/assets/{BaseInput-DuIbdrp6.js → BaseInput-CLUpqyfH.js} +1 -1
  16. package/src/assets/web-panel/assets/{Chat-NEmjGahU.js → Chat-qVS8rD4G.js} +5 -5
  17. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DfJEo-5_.js → ChatBubbleRenderer-ti49X12Q.js} +1 -1
  18. package/src/assets/web-panel/assets/{Checkbox-CaoeG3Ns.js → Checkbox-Bs9AaEay.js} +1 -1
  19. package/src/assets/web-panel/assets/{Codegen-BFf_g3xY.js → Codegen-Dvc1mq8q.js} +1 -1
  20. package/src/assets/web-panel/assets/{Col-CNOd8mdQ.js → Col-C4_biO7A.js} +1 -1
  21. package/src/assets/web-panel/assets/{Community-BhpDqz-i.js → Community-al25XQUV.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compact-CQ42T7Z6.js → Compact-D-ZDB8M1.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compliance-DI5PcLMN.js → Compliance-6lIfDz1W.js} +1 -1
  24. package/src/assets/web-panel/assets/{Cowork-wYYZe0wR.js → Cowork-DdI48XMJ.js} +3 -3
  25. package/src/assets/web-panel/assets/{Cron-Dlq03DD_.js → Cron-DF5HZd2g.js} +2 -2
  26. package/src/assets/web-panel/assets/{Crosschain-BzqS8PP3.js → Crosschain-DuxeSvt3.js} +1 -1
  27. package/src/assets/web-panel/assets/{DID-It55Yem7.js → DID-B9yHkUTd.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dashboard-9HkH6oxn.js → Dashboard-Z5EuI3IM.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dropdown-00e2YWRB.js → Dropdown-vuRfO3dY.js} +1 -1
  30. package/src/assets/web-panel/assets/{EmailListRenderer-By-ugRDL.js → EmailListRenderer-QFLCc1uL.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C396yu5m.js → FamilyGuardDashboard-BZ7EXkFJ.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-BME5sbZY.js → Federation-DycIEg2s.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-B4TEZCr-.js → FormItemContext-Ba0H_7yG.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-_9CX9ieY.js → GenericCardRenderer--JgSTK-C.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-BrS6WVSr.js → Git-DEvTbldE.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-BMdCWnKF.js → Governance-BJIljy18.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-Bcp3vzcA.js → Inference-CVyW3G3q.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-DlLuqGY5.js → KnowledgeGraph-v8-borge.js} +1 -1
  39. package/src/assets/web-panel/assets/{Logs-D-W_6x09.js → Logs-BCdS7gGo.js} +2 -2
  40. package/src/assets/web-panel/assets/{MarkdownRenderer-DElGPxCZ.js → MarkdownRenderer-CU-JB2Ns.js} +1 -1
  41. package/src/assets/web-panel/assets/{Marketplace-Dzn2guvI.js → Marketplace-AN6tGA2B.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-uPict-KL.js → McpTools-DGViXASN.js} +6 -6
  43. package/src/assets/web-panel/assets/{Memory-BCw43kOb.js → Memory-Bpvmx9X4.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-DmV8Wv7F.js → MobileBridge-CTP-DOTC.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-C-HaCmSM.js → MobileProjects-C8qfktxn.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-C6jxJ34Y.js → Mtc-ndYr0M1s.js} +5 -5
  47. package/src/assets/web-panel/assets/{MtcAudit-Za8ExwhQ.js → MtcAudit-Ci0-YK7W.js} +5 -5
  48. package/src/assets/web-panel/assets/{Multisig-KKDbuEgb.js → Multisig-BehSTFW7.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-Dj97zbCk.js → NLProgramming-B91_YBLv.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-DBXvMraU.js → Notes-BQlJ0-gq.js} +4 -4
  51. package/src/assets/web-panel/assets/{NotificationSettings-DOxITLYl.js → NotificationSettings-CDgYMjYK.js} +1 -1
  52. package/src/assets/web-panel/assets/{OrderTableRenderer-7DVjZwMM.js → OrderTableRenderer-BJkSlKeH.js} +1 -1
  53. package/src/assets/web-panel/assets/{Organization-D1i73mjb.js → Organization-B-b3cWCp.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-kW6Q3rvg.js → Overflow-CILUL4nL.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-C-v_8p2v.js → P2P-BTCFBkF0.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-tUbbY64W.js → PdhVaultBrowser-BAR6AUx5.js} +5 -5
  57. package/src/assets/web-panel/assets/{Permissions-DkHxEZ58.js → Permissions-RRf6HbWJ.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-DWuMPGrK.js → PersonalDataHub-BGTMiICa.js} +4 -4
  59. package/src/assets/web-panel/assets/{Pipeline-30IYXWWm.js → Pipeline-945qY9YF.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-DZNQ4RrH.js → Privacy-DPZwI6lJ.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-De2MCElf.js → ProjectInit-BPH2NRKc.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-5uHk9wsC.js → ProjectSettings-BZCRNCvb.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-XjDfrFLb.js → Projects-BTI__tBt.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-Bgwj6gcE.js → Providers-DDCf_nIw.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-gKql9umv.js → QrScannerModal-N6rbqt1r.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-BWvmwkh7.js → QuickAsk-DrwjPS7M.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-Uok4hZPd.js → Recommend-Cog9Wd99.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-DLJzTk9a.js → RemoteSession-DC9pynjM.js} +3 -3
  69. package/src/assets/web-panel/assets/{Reputation-ZVwIPMSO.js → Reputation-D4th4gKS.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-CN04HxWR.js → Row-ogiWR6v9.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BdSrEG4g.js → RssFeed-uYlk-2Oj.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-5PwVd0IR.js → Search-DYzzwvtM.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-DdBo4zjN.js → Security-Da7YOyX6.js} +4 -4
  74. package/src/assets/web-panel/assets/{Services-D3letU7M.js → Services-CLI1VM1X.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton--Jb83f1v.js → Skeleton-DK77dzDC.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-Bg-CulHX.js → Skills-C_z7Lmhl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-7QOa7S-X.js → Sla-D6413Q8L.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-DQwppjRw.js → SpeechSettings-C07ydbj2.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-dNPQGL-s.js → SyncSettings-DGOm2W22.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-Ggs-Z48q.js → Tasks-Cr_PVyLM.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-78TYHgRO.js → Templates-BhL2mZ0t.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-xHwwv9vO.js → Tenant-CPk5oyUr.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-BJXSJfm4.js → Terminal-ClhqUsqV.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-fLbjRhJw.js → TimelineRenderer-BCC7MV5a.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-oMSBSBmB.js → Tokens-DD1Jt4oO.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-CfnY_wX_.js → Trigger-BCydFvN2.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-AdyTgYKO.js → Trust-DDHT1noa.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-C5K_81dS.js → UkeySign-CWPZC7oI.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-vcGVDqki.js → VideoEditing-9Ea0nJR0.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-hhOc3hlY.js → Wallet-BjxhLxTf.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CVloC11s.js → WebAuthn-C6uCye67.js} +4 -4
  92. package/src/assets/web-panel/assets/{WorkflowEditor-CYeE32R5.js → WorkflowEditor-d2g4reYo.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-CNROR5bN.js → chat-frTc6ScX.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-Cjx-cnwU.js → colors-B5K6lG3v.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-DOIfelvD.js → compact-item-Cdn46r8D.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-DfA7UZ8u.js → createContext-DU1jqVQi.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-BMSCzcfM.js → hasIn-DdNH-aW_.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CAnvhwg6.js → index-32ajXmTb.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BEMwBBHk.js → index-7ehytj1K.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CVL27kXC.js → index-BC4eGkar.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-FBc_CJBd.js → index-BVLL-mq_.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-dCd8cSUq.js → index-BXKhhx9V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BemJ2u_E.js → index-BcGwGIaR.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CVm1_i3t.js → index-Be4OtL-N.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Cs6l7uRu.js → index-BoFak7le.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DPAWBrMf.js → index-BwlIxAmX.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DLmn0BoK.js → index-CGiwfwB9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B7gf77CJ.js → index-CHmkkdfT.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-m2vG9gG_.js → index-CQorP1Ng.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-ClNMrqEY.js → index-CUoNAmE8.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DHtpHZAQ.js → index-Cj6P9Nst.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-C2K9SNVX.js → index-CtRPi4YC.js} +1 -1
  114. package/src/assets/web-panel/assets/index-CzrCHrFy.js +1 -0
  115. package/src/assets/web-panel/assets/{index-BolaOQxx.js → index-D1qIC4GR.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BM1y_9hM.js → index-D23xGYjM.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Cyweh0Hy.js → index-D4l82Me1.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-By61ojUx.js → index-D4miNkWv.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BbQ2eQ8H.js → index-DIVyTmZ3.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Icdoqmzt.js → index-DM5FmPZU.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DjEsxO5h.js → index-DRzBBdQ0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B4iYzpkL.js → index-Dca_i7H8.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C3L5gdiD.js → index-DftZuyA7.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CdYBye8B.js → index-DtBCCkC4.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-AWg-0M1S.js → index-HjCvXHf5.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CEKMcOyU.js → index-LaivuXyF.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BxOk9vkG.js → index-MM4Zz2OE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CNC5onm4.js → index-NmEXVjzt.js} +1 -1
  129. package/src/assets/web-panel/assets/index-OJsGskZw.js +1 -0
  130. package/src/assets/web-panel/assets/{index-BUonmKSE.js → index-PjAS0sIZ.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B5Udeb_6.js → index-VDM2KKo9.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-5-HXgOX5.js → index-cYaWV4Mq.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DBpWXFed.js → index-gg3QPC3i.js} +3 -3
  134. package/src/assets/web-panel/assets/{index-C7xn0Nub.js → index-kVO_MDm-.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-ti_lnTWl.js → index-mgU3Q-lN.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CshQbilz.js → index-t_OBzIP4.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-mTpHoEzr.js → index-yZRuZpwK.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-Bu8k7H1Y.js → initDefaultProps-DwLTRNdo.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-s3iVK41k.js → motion-BchZDXnh.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-B6B-E5Jz.js → move-e8M-zZFl.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BiYgTidA.js → mtc-parser-Ci40fQb3.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-pU1RdUDA.js → omit-C7110_f2.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-Cdoq_SeM.js → pickAttrs-skteMgnM.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-DIuvAn2K.js → placementArrow-MGZ-lh2B.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-8w16YR2D.js → responsiveObserve-N4Sd7uy-.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-VZ01cZe3.js → slide-C7HDGRaa.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-lh8YMN8Z.js → statusUtils-BhRexlgy.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-CmDyreec.js → styleChecker-ChEoQkN2.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-hiJWMU5y.js → useFlexGapSupport-CDbG1s2Y.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-Thnu-Iaj.js → useFs-CUIx0tcJ.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-LBdOxjwY.js → usePersonalDataHub-bkO7L7ec.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-Cg6lFY_X.js → vnode-B_ZvABIG.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-DVfsR1YO.js → zoom-D5shjpS8.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-help-index.json +4 -4
  156. package/src/command-manifest.json +214 -50
  157. package/src/command-surface-policy.js +115 -0
  158. package/src/commands/agent.js +3 -3
  159. package/src/commands/background-session.js +145 -0
  160. package/src/commands/mcp.js +125 -7
  161. package/src/commands/serve.js +1 -1
  162. package/src/commands/session-show.js +131 -0
  163. package/src/commands/session.js +50 -106
  164. package/src/commands/skill.js +87 -50
  165. package/src/data/changelog.json +18 -5
  166. package/src/gateways/ws/ws-agent-handler.js +8 -1
  167. package/src/harness/jsonl-session-store.js +428 -25
  168. package/src/harness/mcp-client.js +2652 -404
  169. package/src/harness/session-list-index.js +65 -7
  170. package/src/lazy-dispatch.js +76 -8
  171. package/src/lib/abort-utils.js +68 -0
  172. package/src/lib/agent-core.js +1 -59
  173. package/src/lib/chat-core.js +24 -6
  174. package/src/lib/cowork-mcp-tools.js +47 -2
  175. package/src/lib/cowork-task-runner.js +11 -3
  176. package/src/lib/doctor-checkup.js +9 -7
  177. package/src/lib/learning/skill-improver.js +21 -2
  178. package/src/lib/mcp-call-ledger-store.js +116 -3
  179. package/src/lib/mcp-call-ledger.js +59 -14
  180. package/src/lib/mcp-client.js +1 -0
  181. package/src/lib/mcp-stdio-executable-identity.js +598 -0
  182. package/src/lib/mcp-stdio-execution-authority.js +363 -0
  183. package/src/lib/mcp-tool-metadata.js +436 -0
  184. package/src/lib/mcp-tool-result.js +367 -0
  185. package/src/lib/observability/index.js +71 -0
  186. package/src/lib/pr-link-ledger.js +8 -27
  187. package/src/lib/pr-link-store.js +26 -0
  188. package/src/lib/process-execution-broker/index.js +14 -0
  189. package/src/lib/process-tree-termination.js +253 -0
  190. package/src/lib/public-ide-capability-manifest.js +6 -0
  191. package/src/lib/session-host-snapshot.js +9 -5
  192. package/src/lib/session-projection.js +5 -1
  193. package/src/lib/session-tail.js +31 -1
  194. package/src/lib/skill-budget.js +222 -0
  195. package/src/lib/skill-execution-authority.js +381 -0
  196. package/src/lib/skill-execution-identity.js +39 -2
  197. package/src/lib/skill-loader.js +322 -86
  198. package/src/lib/skill-mcp.js +43 -1
  199. package/src/repl/agent-repl.js +349 -46
  200. package/src/repl/bg-dashboard.js +81 -28
  201. package/src/repl/chat-repl.js +77 -12
  202. package/src/repl/prompt-interactions.js +67 -7
  203. package/src/repl/slash-command-registry.js +3 -4
  204. package/src/runtime/agent-core.js +340 -108
  205. package/src/runtime/agent-runtime.js +26 -3
  206. package/src/runtime/headless-runner.js +150 -23
  207. package/src/runtime/headless-stream.js +309 -59
  208. package/src/runtime/mcp-config.js +157 -10
  209. package/src/runtime/mcp-tool-search.js +0 -0
  210. package/src/runtime/output-backpressure.js +366 -0
  211. package/src/runtime/pipe-safety.js +56 -21
  212. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +0 -1
  213. package/src/assets/web-panel/assets/index-Q2IajL54.js +0 -1
  214. package/src/assets/web-panel/assets/index-bRkHgOMc.js +0 -1
  215. package/src/lib/skill-process-broker.js +0 -85
@@ -11,6 +11,7 @@ import {
11
11
  appendFileSync,
12
12
  existsSync,
13
13
  readFileSync,
14
+ rmSync,
14
15
  writeFileSync,
15
16
  } from "node:fs";
16
17
  import { join } from "node:path";
@@ -19,6 +20,7 @@ import { ensurePrivateFile } from "../lib/secure-fs.js";
19
20
 
20
21
  export const SESSION_INDEX_SCHEMA = 2;
21
22
  export const SESSION_INDEX_FILE = ".sessions-index-v2.ndjson";
23
+ export const SESSION_TOMBSTONE_MARKER_SUFFIX = ".tombstone";
22
24
 
23
25
  // Used only by an opted-in child in the independent session-scale crash gate.
24
26
  // Keeping the injection immediately around the production sidecar/journal
@@ -41,6 +43,10 @@ export function sessionIndexPath(dir) {
41
43
  return join(dir, SESSION_INDEX_FILE);
42
44
  }
43
45
 
46
+ export function sessionTombstoneMarkerPath(dir, sessionId) {
47
+ return join(dir, `${sessionId}${SESSION_TOMBSTONE_MARKER_SUFFIX}`);
48
+ }
49
+
44
50
  export function emptySessionMeta(sessionId) {
45
51
  return {
46
52
  schema: SESSION_INDEX_SCHEMA,
@@ -108,6 +114,18 @@ export function applyEventToSessionMeta(meta, event, lastHash) {
108
114
 
109
115
  function writeMetaSnapshot(dir, meta) {
110
116
  const filePath = sessionMetaPath(dir, meta.id);
117
+ const tombstoneMarker = sessionTombstoneMarkerPath(dir, meta.id);
118
+ // The marker is the durable per-session namespace witness that makes a
119
+ // tombstoned identity discoverable without reading every live sidecar on
120
+ // `--continue`; the meta snapshot remains the content/status authority.
121
+ // Publish the marker before the tombstone snapshot, and fail the delete if
122
+ // that publication fails. Remove it only after a live generation snapshot
123
+ // commits, so crashes can cause an extra candidate read but never hide a
124
+ // successfully committed tombstone.
125
+ if (meta.deleted === true) {
126
+ writeFileSync(tombstoneMarker, "", { encoding: "utf8", mode: 0o600 });
127
+ ensurePrivateFile(tombstoneMarker);
128
+ }
111
129
  // The owning transcript lock serializes this derived snapshot. It need not
112
130
  // use a second temp+rename protocol: interruption can only corrupt a cache,
113
131
  // which readSessionMeta rejects and rebuildSessionMeta regenerates.
@@ -116,6 +134,7 @@ function writeMetaSnapshot(dir, meta) {
116
134
  mode: 0o600,
117
135
  });
118
136
  ensurePrivateFile(filePath);
137
+ if (meta.deleted !== true) rmSync(tombstoneMarker, { force: true });
119
138
  }
120
139
 
121
140
  function appendActivity(dir, meta) {
@@ -135,9 +154,20 @@ function appendActivity(dir, meta) {
135
154
  }
136
155
 
137
156
  /** Called while the owning transcript lock is held. */
138
- export function recordSessionEvent(dir, sessionId, event, lastHash) {
139
- const current =
140
- readSessionMeta(dir, sessionId) || emptySessionMeta(sessionId);
157
+ export function recordSessionEvent(
158
+ dir,
159
+ sessionId,
160
+ event,
161
+ lastHash,
162
+ { resetGeneration = false } = {},
163
+ ) {
164
+ const existing = readSessionMeta(dir, sessionId);
165
+ // Only the transcript writer may authorize a new generation after checking
166
+ // the tombstone and physical file under its lock. The index cannot infer
167
+ // that authority from an attacker-controlled genesis-shaped event alone.
168
+ const current = resetGeneration
169
+ ? emptySessionMeta(sessionId)
170
+ : existing || emptySessionMeta(sessionId);
141
171
  current.id = sessionId;
142
172
  const next = applyEventToSessionMeta(current, event, lastHash);
143
173
  writeMetaSnapshot(dir, next);
@@ -224,13 +254,29 @@ export function publicSessionMeta(meta) {
224
254
  */
225
255
  export function listIndexedSessions(
226
256
  dir,
227
- { limit = 20, hasSession = () => true } = {},
257
+ {
258
+ limit = 20,
259
+ hasSession = () => true,
260
+ includeDeleted = () => false,
261
+ onScanComplete = null,
262
+ } = {},
228
263
  ) {
229
264
  const filePath = sessionIndexPath(dir);
230
- if (!existsSync(filePath)) return [];
265
+ if (!existsSync(filePath)) {
266
+ if (typeof onScanComplete === "function") {
267
+ onScanComplete({
268
+ exhausted: true,
269
+ seenSessionIds: new Set(),
270
+ latestBySessionId: new Map(),
271
+ });
272
+ }
273
+ return [];
274
+ }
231
275
  const wanted = Math.max(0, Number(limit) || 0);
232
276
  const seen = new Set();
277
+ const latestBySessionId = new Map();
233
278
  const result = [];
279
+ let exhausted = true;
234
280
  for (const { line } of iterateFileLinesReverseSync(filePath)) {
235
281
  let meta;
236
282
  try {
@@ -246,9 +292,21 @@ export function listIndexedSessions(
246
292
  continue;
247
293
  }
248
294
  seen.add(meta.id);
249
- if (meta.deleted || !hasSession(meta.id)) continue;
295
+ latestBySessionId.set(meta.id, meta);
296
+ if (
297
+ (meta.deleted && !includeDeleted(meta.id, meta)) ||
298
+ !hasSession(meta.id)
299
+ ) {
300
+ continue;
301
+ }
250
302
  result.push(publicSessionMeta(meta));
251
- if (wanted > 0 && result.length >= wanted) break;
303
+ if (wanted > 0 && result.length >= wanted) {
304
+ exhausted = false;
305
+ break;
306
+ }
307
+ }
308
+ if (typeof onScanComplete === "function") {
309
+ onScanComplete({ exhausted, seenSessionIds: seen, latestBySessionId });
252
310
  }
253
311
  return result;
254
312
  }
@@ -337,6 +337,43 @@ export function resolveCommandLifecycleInvocation(
337
337
  };
338
338
  }
339
339
 
340
+ /**
341
+ * Build the bounded, content-free lifecycle usage dimensions for a migrated
342
+ * command. The original argv is inspected only to distinguish the retained
343
+ * top-level spelling from its virtual namespace replacement; arguments and
344
+ * nested command names are never copied into telemetry.
345
+ */
346
+ export function resolveCommandLifecycleTelemetry(
347
+ argv,
348
+ manifestData = manifest,
349
+ ) {
350
+ const invocation = resolveCommandLifecycleInvocation(argv, manifestData);
351
+ let entry = null;
352
+ let route = null;
353
+ if (invocation.kind === "namespace-rewrite") {
354
+ entry = invocation.entry;
355
+ route = "replacement";
356
+ } else if (invocation.kind === "passthrough") {
357
+ const commandName = resolveCommandToken(argv);
358
+ entry = findManifestEntry(commandName, manifestData);
359
+ route = "legacy";
360
+ }
361
+ if (
362
+ entry?.lifecycle?.state !== "deprecated" ||
363
+ typeof entry.name !== "string" ||
364
+ !entry.replacement
365
+ ) {
366
+ return null;
367
+ }
368
+ return Object.freeze({
369
+ command: entry.name,
370
+ route,
371
+ version: packageMetadata.version,
372
+ deprecatedSince: entry.lifecycle.deprecatedSince,
373
+ removalNotBefore: entry.lifecycle.removalNotBefore,
374
+ });
375
+ }
376
+
340
377
  export function formatCommandDeprecationWarning(entry, invokedName) {
341
378
  const lifecycle = entry?.lifecycle;
342
379
  if (lifecycle?.state !== "deprecated" || !entry.replacement) return null;
@@ -691,7 +728,7 @@ export async function dispatchCli(
691
728
  { manifestData = manifest, ...dispatchOptions } = {},
692
729
  ) {
693
730
  const commandName = resolveCommandToken(argv);
694
- const entry = findManifestEntry(commandName, manifestData);
731
+ let entry = findManifestEntry(commandName, manifestData);
695
732
  if (!entry) {
696
733
  const program = await (
697
734
  dispatchOptions.loadFullProgram || defaultLoadFullProgram
@@ -699,20 +736,35 @@ export async function dispatchCli(
699
736
  await program.parseAsync(argv);
700
737
  return;
701
738
  }
739
+ const commandLocation = findCommandTokenLocation(argv);
740
+ if (entry.name === "session" && argv[commandLocation?.index + 1] === "show") {
741
+ entry = {
742
+ ...entry,
743
+ module: "./commands/session-show.js",
744
+ register: "registerSessionShowCommand",
745
+ };
746
+ }
702
747
  await dispatchManifestEntry(argv, entry, dispatchOptions);
703
748
  }
704
749
 
705
750
  export function isFastReadOnlyInvocation(argv, env = process.env) {
706
751
  const args = argv.slice(2);
707
- return (
752
+ const explicitOtlp =
753
+ args.includes("--otlp-endpoint") ||
754
+ args.some((token) => token.startsWith("--otlp-endpoint=")) ||
755
+ env.OTEL_EXPORTER_OTLP_ENDPOINT ||
756
+ env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
757
+ env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT;
758
+ const quickStatus =
708
759
  resolveCommandToken(argv) === "status" &&
709
760
  !args.includes("--deep") &&
710
- !args.includes("--otlp-endpoint") &&
711
- !args.some((token) => token.startsWith("--otlp-endpoint=")) &&
712
- !env.OTEL_EXPORTER_OTLP_ENDPOINT &&
713
- !env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT &&
714
- !env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
715
- );
761
+ !explicitOtlp;
762
+ const commandLocation = findCommandTokenLocation(argv);
763
+ const sessionShow =
764
+ commandLocation?.token === "session" &&
765
+ argv[commandLocation.index + 1] === "show" &&
766
+ !explicitOtlp;
767
+ return quickStatus || sessionShow;
716
768
  }
717
769
 
718
770
  let processHandlersInstalled = false;
@@ -767,6 +819,10 @@ async function withInvocationOutputContext(argv, task) {
767
819
  }
768
820
 
769
821
  export async function runCli(argv, options = {}) {
822
+ const lifecycleTelemetry = resolveCommandLifecycleTelemetry(
823
+ argv,
824
+ options.manifestData || manifest,
825
+ );
770
826
  const prepared = await prepareInvocation(argv, options);
771
827
  if (prepared.handled) {
772
828
  if (prepared.exitCode) {
@@ -799,12 +855,24 @@ export async function runCli(argv, options = {}) {
799
855
  }
800
856
 
801
857
  const observability = await initializeCommandRuntime(dispatchArgv);
858
+ const lifecycleStartedAt = performance.now();
859
+ let lifecycleOutcome = "completed";
802
860
  try {
803
861
  return await withDefaultEventRuntimeLifecycle(
804
862
  () => dispatchCli(dispatchArgv, options),
805
863
  options,
806
864
  );
865
+ } catch (error) {
866
+ lifecycleOutcome = "error";
867
+ throw error;
807
868
  } finally {
869
+ if (lifecycleTelemetry) {
870
+ observability.exportCommandLifecycleInvocation({
871
+ ...lifecycleTelemetry,
872
+ outcome: lifecycleOutcome,
873
+ durationMs: performance.now() - lifecycleStartedAt,
874
+ });
875
+ }
808
876
  await observability.shutdown().catch(() => {});
809
877
  }
810
878
  });
@@ -18,3 +18,71 @@ export function throwIfAborted(signal, message = "Agent loop interrupted") {
18
18
  throw signal.reason || createAbortError(message);
19
19
  }
20
20
  }
21
+
22
+ /**
23
+ * Await a value while making a host AbortSignal authoritative over settlement.
24
+ * The source promise is always observed after cancellation, so a late rejection
25
+ * cannot become unhandled and a late success cannot revive the cancelled path.
26
+ */
27
+ export function raceWithAbort(
28
+ value,
29
+ signal,
30
+ message = "Agent loop interrupted",
31
+ ) {
32
+ if (!signal) return Promise.resolve(value);
33
+ try {
34
+ throwIfAborted(signal, message);
35
+ } catch (error) {
36
+ return Promise.reject(error);
37
+ }
38
+ if (typeof signal.addEventListener !== "function") {
39
+ return Promise.resolve(value);
40
+ }
41
+
42
+ const source = Promise.resolve(value);
43
+ return new Promise((resolve, reject) => {
44
+ let settled = false;
45
+ const cleanup = () => {
46
+ try {
47
+ signal.removeEventListener?.("abort", onAbort);
48
+ } catch {
49
+ // Listener cleanup cannot change the already-authoritative settlement.
50
+ }
51
+ };
52
+ const onAbort = () => {
53
+ if (settled) return;
54
+ settled = true;
55
+ cleanup();
56
+ reject(signal.reason || createAbortError(message));
57
+ };
58
+ try {
59
+ signal.addEventListener("abort", onAbort, { once: true });
60
+ } catch (error) {
61
+ settled = true;
62
+ // Observe a later source rejection even though the signal contract was
63
+ // malformed and this wrapper must fail immediately.
64
+ source.catch(() => {});
65
+ reject(error);
66
+ return;
67
+ }
68
+ source.then(
69
+ (result) => {
70
+ if (settled) return;
71
+ settled = true;
72
+ cleanup();
73
+ resolve(result);
74
+ },
75
+ (error) => {
76
+ if (settled) return;
77
+ settled = true;
78
+ cleanup();
79
+ reject(error);
80
+ },
81
+ );
82
+ // AbortSignal does not replay an event that fired immediately before the
83
+ // listener was attached. Re-check after both observers are installed so
84
+ // that narrow registration race still settles as cancellation, while the
85
+ // source remains observed if it finishes later.
86
+ if (signal.aborted) onAbort();
87
+ });
88
+ }
@@ -1,60 +1,2 @@
1
1
  /** @deprecated Thin re-export shim; canonical impl is `../runtime/agent-core.js` (CLI Runtime Convergence, Phase 6b) — import from there in new code. */
2
- export {
3
- AGENT_TOOLS,
4
- AGENT_TOOL_REGISTRY,
5
- MAX_SUB_AGENT_DEPTH,
6
- MAX_SUB_AGENTS_PER_RUN,
7
- MAX_TOOL_RESULT_CHARS,
8
- capToolResultString,
9
- safeStringifyToolResult,
10
- tokenizeShellWords,
11
- reloadSkills,
12
- getAgentToolDefinitions,
13
- getAgentToolDescriptors,
14
- getCachedPython,
15
- _resetCachedPythonForTests,
16
- getEnvironmentInfo,
17
- _environmentProcessDeps,
18
- _agentToolProcessDeps,
19
- getBaseSystemPrompt,
20
- buildSystemPrompt,
21
- executeTool,
22
- computeProposedEdit,
23
- editNotebookCell,
24
- renderNotebook,
25
- classifyError,
26
- isValidPackageName,
27
- chatWithTools,
28
- agentLoop,
29
- formatToolArgs,
30
- getActiveMcpServers,
31
- listBackgroundShellTasks,
32
- killBackgroundShellTask,
33
- killAllBackgroundShellTasks,
34
- killAllBackgroundShellTasksSync,
35
- _backgroundProcessDeps,
36
- _runBackgroundTaskkill,
37
- _gitProcessDeps,
38
- reapIdleBackgroundShellTasks,
39
- _ingestSearchOutput,
40
- writeFileVerified,
41
- formatProviderHttpError,
42
- _accumulateOllamaStream,
43
- _accumulateOpenAIStream,
44
- _accumulateAnthropicStream,
45
- _streamErrorDisposition,
46
- _isRetryableStreamError,
47
- _retryStreamingChat,
48
- _iterateStreamWithStall,
49
- _toAnthropicMessages,
50
- _anthropicThinkingParams,
51
- _normalizeAnthropicResponse,
52
- _resetFileFreshness,
53
- _withPostEditDiagnostics,
54
- _getSharedCodeIntel,
55
- disposeSharedCodeIntel,
56
- emitToolHookLifecycle,
57
- emitToolBatchHookLifecycle,
58
- buildSubAgentHandoffContext,
59
- _resetSubtreeInstructionLoaders,
60
- } from "../runtime/agent-core.js";
2
+ export * from "../runtime/agent-core.js";
@@ -90,6 +90,21 @@ function _cachedPromptTokens(usage) {
90
90
  return 0;
91
91
  }
92
92
 
93
+ async function _deliverToken(onToken, token, guard) {
94
+ if (typeof onToken !== "function") return;
95
+ // Terminal backpressure is not provider silence. Suspend the network stall
96
+ // timer while the UI sink drains, then re-arm it before reading more bytes.
97
+ guard.stop();
98
+ try {
99
+ await onToken(token);
100
+ } catch (error) {
101
+ if (error?.isOutputBackpressureFailure === true) throw error;
102
+ // Generic UI callbacks retain their historical best-effort semantics.
103
+ } finally {
104
+ guard.bump();
105
+ }
106
+ }
107
+
93
108
  /**
94
109
  * Stream a response from Ollama.
95
110
  * If `onUsage` is provided, it's called with `{inputTokens, outputTokens}`
@@ -156,7 +171,7 @@ export async function streamOllama(
156
171
  const json = JSON.parse(line);
157
172
  if (json.message?.content) {
158
173
  fullResponse += json.message.content;
159
- onToken(json.message.content);
174
+ await _deliverToken(onToken, json.message.content, guard);
160
175
  }
161
176
  if (json.done && onUsage) {
162
177
  const inputTokens = Number(json.prompt_eval_count) || 0;
@@ -165,7 +180,8 @@ export async function streamOllama(
165
180
  onUsage({ inputTokens, outputTokens });
166
181
  }
167
182
  }
168
- } catch {
183
+ } catch (error) {
184
+ if (error?.isOutputBackpressureFailure === true) throw error;
169
185
  // Partial JSON, skip
170
186
  }
171
187
  }
@@ -257,7 +273,7 @@ export async function streamOpenAI(
257
273
  const content = json.choices?.[0]?.delta?.content;
258
274
  if (content) {
259
275
  fullResponse += content;
260
- onToken(content);
276
+ await _deliverToken(onToken, content, guard);
261
277
  }
262
278
  if (json.usage && onUsage) {
263
279
  // Split cached prompt tokens out of prompt_tokens so cc cost
@@ -270,7 +286,8 @@ export async function streamOpenAI(
270
286
  onUsage({ inputTokens, outputTokens, cacheReadTokens });
271
287
  }
272
288
  }
273
- } catch {
289
+ } catch (error) {
290
+ if (error?.isOutputBackpressureFailure === true) throw error;
274
291
  // Partial data
275
292
  }
276
293
  }
@@ -377,7 +394,7 @@ export async function streamAnthropic(
377
394
  const delta = obj.delta?.text;
378
395
  if (delta) {
379
396
  fullResponse += delta;
380
- onToken(delta);
397
+ await _deliverToken(onToken, delta, guard);
381
398
  }
382
399
  } else if (obj.type === "message_start") {
383
400
  const u = obj.message?.usage || {};
@@ -390,7 +407,8 @@ export async function streamAnthropic(
390
407
  } else if (obj.type === "message_delta") {
391
408
  outputTokens = Number(obj.usage?.output_tokens) || outputTokens;
392
409
  }
393
- } catch {
410
+ } catch (error) {
411
+ if (error?.isOutputBackpressureFailure === true) throw error;
394
412
  /* skip malformed */
395
413
  }
396
414
  }
@@ -21,6 +21,10 @@
21
21
  */
22
22
  import { validateMcpServerConfig } from "./skill-mcp.js";
23
23
  import { mcpEffectDescriptorFields } from "./mcp-effect-contract.js";
24
+ import {
25
+ MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE,
26
+ issueMcpStdioExecutionAuthority,
27
+ } from "./mcp-stdio-execution-authority.js";
24
28
 
25
29
  export const _deps = {
26
30
  importMcpClient: async () => {
@@ -90,6 +94,7 @@ export function toAgentTool(serverName, tool) {
90
94
  * @param {{ mcpServers?: Array<object> }} template
91
95
  * @param {object} [opts]
92
96
  * @param {(msg: string, err?: Error) => void} [opts.onWarn]
97
+ * @param {(request: object) => boolean|Promise<boolean>} [opts.approveLocalCodeExecution]
93
98
  * @returns {Promise<{
94
99
  * mcpClient: object|null,
95
100
  * mounted: string[],
@@ -121,15 +126,55 @@ export async function mountTemplateMcpTools(template, opts = {}) {
121
126
  .filter(Boolean);
122
127
  if (validated.length === 0) return empty;
123
128
 
129
+ const skipped = [];
130
+ const authorized = [];
131
+ for (const server of validated) {
132
+ let approved = false;
133
+ try {
134
+ approved =
135
+ typeof opts.approveLocalCodeExecution === "function" &&
136
+ (await opts.approveLocalCodeExecution({
137
+ templateId: template?.id || null,
138
+ serverName: server.name,
139
+ command: server.command,
140
+ args: [...server.args],
141
+ cwd: server.cwd || null,
142
+ envKeys: Object.keys(server.env || {}).sort(),
143
+ })) === true;
144
+ } catch (error) {
145
+ const message = error?.message || String(error);
146
+ skipped.push({ name: server.name, error: message });
147
+ opts.onWarn?.(
148
+ `[cowork-mcp] Local-code approval failed for "${server.name}": ${message}`,
149
+ error,
150
+ );
151
+ continue;
152
+ }
153
+ if (!approved) {
154
+ const message = `${MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE}: template MCP server requires explicit local-code approval`;
155
+ skipped.push({ name: server.name, error: message });
156
+ opts.onWarn?.(`[cowork-mcp] Skipped "${server.name}": ${message}`);
157
+ continue;
158
+ }
159
+ const config = { ...server };
160
+ config.mcpStdioExecutionAuthority = issueMcpStdioExecutionAuthority({
161
+ serverName: server.name,
162
+ config,
163
+ approvalKind: "explicit-config",
164
+ approvalSource: `cowork-template:${template?.id || "unknown"}:${server.name}`,
165
+ });
166
+ authorized.push(config);
167
+ }
168
+ if (authorized.length === 0) return { ...empty, skipped };
169
+
124
170
  const MCPClient = await _deps.importMcpClient();
125
171
  const mcpClient = new MCPClient();
126
172
  const mounted = [];
127
- const skipped = [];
128
173
  const extraToolDefinitions = [];
129
174
  const externalToolDescriptors = {};
130
175
  const externalToolExecutors = {};
131
176
 
132
- for (const server of validated) {
177
+ for (const server of authorized) {
133
178
  try {
134
179
  await mcpClient.connect(server.name, server);
135
180
  mounted.push(server.name);
@@ -313,6 +313,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
313
313
  }
314
314
  let recoveryNotice = null;
315
315
  let recoveryState = null;
316
+ let recoveryAuthority = null;
316
317
  let bindingAllowed = false;
317
318
  let expectedHeadHash = null;
318
319
  try {
@@ -321,6 +322,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
321
322
  String(options.templateId || "free"),
322
323
  );
323
324
  const recovery = projected.recovery;
325
+ recoveryAuthority = recovery;
324
326
  expectedHeadHash = projected.expectedHeadHash;
325
327
  bindingAllowed = projected.bindingAllowed;
326
328
  recoveryNotice = formatMcpLedgerRecoveryNotice(recovery);
@@ -361,9 +363,12 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
361
363
  }
362
364
  }
363
365
 
364
- const sink = createSessionMcpLedgerSink(sessionId, {
365
- appendEvent: _deps.appendSessionEvent,
366
- });
366
+ const sink = createSessionMcpLedgerSink(
367
+ sessionId,
368
+ _deps.appendSessionEvent === appendSessionEvent
369
+ ? { recovery: recoveryAuthority }
370
+ : { appendEvent: _deps.appendSessionEvent },
371
+ );
367
372
  const recoveryController =
368
373
  createMcpRecoveryAdmissionController(recoveryState);
369
374
  return {
@@ -395,6 +400,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
395
400
  * @param {number} [options.maxIterations] - Override iteration limit
396
401
  * @param {number} [options.tokenBudget] - Override token budget
397
402
  * @param {string} [options.mcpSessionId] - Stable MCP ledger session for resume
403
+ * @param {(request: object) => boolean|Promise<boolean>} [options.approveMcpLocalCodeExecution]
398
404
  * @returns {Promise<{ taskId: string, status: string, result: object }>}
399
405
  */
400
406
  export async function runCoworkTask(options = {}) {
@@ -410,6 +416,7 @@ export async function runCoworkTask(options = {}) {
410
416
  onProgress = null,
411
417
  signal = null,
412
418
  mcpSessionId = null,
419
+ approveMcpLocalCodeExecution = null,
413
420
  } = options;
414
421
 
415
422
  if (!userMessage || typeof userMessage !== "string") {
@@ -458,6 +465,7 @@ export async function runCoworkTask(options = {}) {
458
465
 
459
466
  // Mount template-declared MCP servers (best-effort, failures are tolerated)
460
467
  const mcp = await mountTemplateMcpTools(template, {
468
+ approveLocalCodeExecution: approveMcpLocalCodeExecution,
461
469
  onWarn: (msg) => {
462
470
  if (onProgress) onProgress({ type: "mcp-warning", message: msg });
463
471
  },
@@ -604,22 +604,24 @@ async function transcriptSection(_opts, _deps2) {
604
604
  const { verifyAllSessions } =
605
605
  await import("../harness/jsonl-session-store.js");
606
606
  const results = verifyAllSessions({ limit: 200 });
607
- const tampered = results.filter((r) => r.status === "tampered");
608
- if (tampered.length > 0) {
607
+ const integrityFailures = results.filter((result) =>
608
+ ["tampered", "missing", "conflict"].includes(result.status),
609
+ );
610
+ if (integrityFailures.length > 0) {
609
611
  checks.push(
610
612
  check(
611
613
  "transcript-tamper",
612
614
  "Transcript integrity",
613
615
  CHECK_LEVELS.ERR,
614
- `${tampered.length} tampered transcript(s): ${tampered
616
+ `${integrityFailures.length} transcript integrity failure(s): ${integrityFailures
615
617
  .slice(0, 5)
616
- .map((t) => t.sessionId)
617
- .join(", ")}${tampered.length > 5 ? ", …" : ""}`,
618
+ .map((result) => `${result.sessionId} [${result.status}]`)
619
+ .join(", ")}${integrityFailures.length > 5 ? ", …" : ""}`,
618
620
  {
619
621
  id: "transcript-tamper",
620
622
  safe: false,
621
623
  description: "Inspect the flagged transcripts",
622
- command: `cc session verify ${tampered[0].sessionId}`,
624
+ command: `cc session verify ${integrityFailures[0].sessionId}`,
623
625
  },
624
626
  ),
625
627
  );
@@ -629,7 +631,7 @@ async function transcriptSection(_opts, _deps2) {
629
631
  "transcript-tamper",
630
632
  "Transcript integrity",
631
633
  CHECK_LEVELS.OK,
632
- `${results.length} session(s) checked, no tamper`,
634
+ `${results.length} session(s) checked, no integrity failures`,
633
635
  ),
634
636
  );
635
637
  }