chainlesschain 0.162.197 → 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 (208) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-CSN-D2xb.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-CBSMlGzv.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-3yr3Nt2t.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-1b-o3lMH.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-Dr8rJi17.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-2V689HC2.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DIxkNrD_.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Pg6BcwrE.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-CLUpqyfH.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-qVS8rD4G.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-ti49X12Q.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-Bs9AaEay.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-Dvc1mq8q.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C4_biO7A.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-al25XQUV.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-D-ZDB8M1.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-6lIfDz1W.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-DdI48XMJ.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-DF5HZd2g.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-DuxeSvt3.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-B9yHkUTd.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-Z5EuI3IM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-vuRfO3dY.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-QFLCc1uL.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-BZ7EXkFJ.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DycIEg2s.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-Ba0H_7yG.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer--JgSTK-C.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-DEvTbldE.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-BJIljy18.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-CVyW3G3q.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-v8-borge.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-BCdS7gGo.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-CU-JB2Ns.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-AN6tGA2B.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-DGViXASN.js} +4 -4
  38. package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Bpvmx9X4.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-CTP-DOTC.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-C8qfktxn.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-ndYr0M1s.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-Ci0-YK7W.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-BehSTFW7.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B91_YBLv.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-BQlJ0-gq.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-CDgYMjYK.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-BJkSlKeH.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B-b3cWCp.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CILUL4nL.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-BTCFBkF0.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-BAR6AUx5.js} +4 -4
  52. package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-RRf6HbWJ.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-BGTMiICa.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-945qY9YF.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-DPZwI6lJ.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-BPH2NRKc.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-BZCRNCvb.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-BTI__tBt.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-uO-10aP1.js → Providers-DDCf_nIw.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-N6rbqt1r.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-DrwjPS7M.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-Cog9Wd99.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-DC9pynjM.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-D4th4gKS.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-ogiWR6v9.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-uYlk-2Oj.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-DYzzwvtM.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-Da7YOyX6.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-CLI1VM1X.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-DK77dzDC.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-C_z7Lmhl.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-D6413Q8L.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-C07ydbj2.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-DGOm2W22.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-Cr_PVyLM.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BhL2mZ0t.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-CPk5oyUr.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-ClhqUsqV.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-BCC7MV5a.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-DD1Jt4oO.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-BCydFvN2.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-DDHT1noa.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-CWPZC7oI.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-9Ea0nJR0.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-BjxhLxTf.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-C6uCye67.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-d2g4reYo.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-frTc6ScX.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-B5K6lG3v.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-Cdn46r8D.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-DU1jqVQi.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-DdNH-aW_.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-32ajXmTb.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-7ehytj1K.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-BC4eGkar.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-BVLL-mq_.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-BXKhhx9V.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-BcGwGIaR.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-Be4OtL-N.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-BoFak7le.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BwlIxAmX.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-CGiwfwB9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-CHmkkdfT.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-CQorP1Ng.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-CUoNAmE8.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Cj6P9Nst.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-CtRPi4YC.js} +1 -1
  109. package/src/assets/web-panel/assets/index-CzrCHrFy.js +1 -0
  110. package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-D1qIC4GR.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-D23xGYjM.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index-D4l82Me1.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CLc19alH.js → index-D4miNkWv.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DIVyTmZ3.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index-DM5FmPZU.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DRzBBdQ0.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-Dca_i7H8.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CpumS98o.js → index-DftZuyA7.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-DtBCCkC4.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-HjCvXHf5.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-LaivuXyF.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-MM4Zz2OE.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-NmEXVjzt.js} +1 -1
  124. package/src/assets/web-panel/assets/index-OJsGskZw.js +1 -0
  125. package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-PjAS0sIZ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-VDM2KKo9.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-cYaWV4Mq.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-gg3QPC3i.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-kVO_MDm-.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-mgU3Q-lN.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-t_OBzIP4.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-yZRuZpwK.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-DwLTRNdo.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-BchZDXnh.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-e8M-zZFl.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-Ci40fQb3.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-C7110_f2.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-skteMgnM.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-MGZ-lh2B.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-N4Sd7uy-.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-C7HDGRaa.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-BhRexlgy.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-ChEoQkN2.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CDbG1s2Y.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-CUIx0tcJ.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-bkO7L7ec.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-B_ZvABIG.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-D5shjpS8.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +4 -4
  151. package/src/commands/agent.js +3 -3
  152. package/src/commands/background-session.js +145 -0
  153. package/src/commands/mcp.js +125 -7
  154. package/src/commands/serve.js +1 -1
  155. package/src/commands/session-show.js +131 -0
  156. package/src/commands/session.js +50 -106
  157. package/src/commands/skill.js +87 -50
  158. package/src/data/changelog.json +14 -1
  159. package/src/gateways/ws/ws-agent-handler.js +8 -1
  160. package/src/harness/jsonl-session-store.js +428 -25
  161. package/src/harness/mcp-client.js +2652 -404
  162. package/src/harness/session-list-index.js +65 -7
  163. package/src/lazy-dispatch.js +76 -8
  164. package/src/lib/abort-utils.js +68 -0
  165. package/src/lib/agent-core.js +1 -59
  166. package/src/lib/chat-core.js +24 -6
  167. package/src/lib/cowork-mcp-tools.js +47 -2
  168. package/src/lib/cowork-task-runner.js +11 -3
  169. package/src/lib/doctor-checkup.js +9 -7
  170. package/src/lib/learning/skill-improver.js +21 -2
  171. package/src/lib/mcp-call-ledger-store.js +116 -3
  172. package/src/lib/mcp-call-ledger.js +59 -14
  173. package/src/lib/mcp-client.js +1 -0
  174. package/src/lib/mcp-stdio-executable-identity.js +598 -0
  175. package/src/lib/mcp-stdio-execution-authority.js +363 -0
  176. package/src/lib/mcp-tool-metadata.js +436 -0
  177. package/src/lib/mcp-tool-result.js +367 -0
  178. package/src/lib/observability/index.js +71 -0
  179. package/src/lib/pr-link-ledger.js +8 -27
  180. package/src/lib/pr-link-store.js +26 -0
  181. package/src/lib/process-execution-broker/index.js +14 -0
  182. package/src/lib/process-tree-termination.js +253 -0
  183. package/src/lib/session-host-snapshot.js +9 -5
  184. package/src/lib/session-projection.js +5 -1
  185. package/src/lib/session-tail.js +31 -1
  186. package/src/lib/skill-budget.js +222 -0
  187. package/src/lib/skill-execution-authority.js +381 -0
  188. package/src/lib/skill-execution-identity.js +39 -2
  189. package/src/lib/skill-loader.js +322 -86
  190. package/src/lib/skill-mcp.js +43 -1
  191. package/src/repl/agent-repl.js +174 -38
  192. package/src/repl/bg-dashboard.js +81 -28
  193. package/src/repl/chat-repl.js +77 -12
  194. package/src/runtime/agent-core.js +340 -108
  195. package/src/runtime/agent-runtime.js +26 -3
  196. package/src/runtime/headless-runner.js +150 -23
  197. package/src/runtime/headless-stream.js +309 -59
  198. package/src/runtime/mcp-config.js +157 -10
  199. package/src/runtime/mcp-tool-search.js +0 -0
  200. package/src/runtime/output-backpressure.js +366 -0
  201. package/src/runtime/pipe-safety.js +56 -21
  202. package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
  203. package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
  204. package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
  205. package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
  206. package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
  207. package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
  208. package/src/lib/skill-process-broker.js +0 -85
@@ -0,0 +1,253 @@
1
+ import { executionBroker } from "./process-execution-broker/index.js";
2
+
3
+ export const PROCESS_TREE_CLEANUP_MAX_MS = 2_000;
4
+ export const PROCESS_TREE_GRACE_MAX_MS = 500;
5
+ const TREE_MODES = new Set(["sandbox", "posix-group", "windows-tree"]);
6
+
7
+ function boundedMilliseconds(value, fallback, maximum) {
8
+ const parsed = Number(value);
9
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
10
+ return Math.max(1, Math.min(maximum, Math.floor(parsed)));
11
+ }
12
+
13
+ function validChildPid(child) {
14
+ const pid = Number(child?.pid);
15
+ return Number.isInteger(pid) && pid > 0 && pid !== process.pid ? pid : null;
16
+ }
17
+
18
+ function childHasClosed(child, observedClose) {
19
+ return (
20
+ observedClose() ||
21
+ (child?.exitCode !== null && child?.exitCode !== undefined) ||
22
+ (child?.signalCode !== null && child?.signalCode !== undefined)
23
+ );
24
+ }
25
+
26
+ function posixGroupIsGone(pid, kill) {
27
+ try {
28
+ kill(-pid, 0);
29
+ return false;
30
+ } catch (error) {
31
+ return error?.code === "ESRCH";
32
+ }
33
+ }
34
+
35
+ function requestDirectSignal(child, signal) {
36
+ try {
37
+ return child?.kill?.(signal) !== false;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ function requestPosixGroupSignal(child, pid, signal, kill) {
44
+ try {
45
+ kill(-pid, signal);
46
+ return { requested: true, treeRequested: true };
47
+ } catch {
48
+ return {
49
+ requested: requestDirectSignal(child, signal),
50
+ treeRequested: false,
51
+ };
52
+ }
53
+ }
54
+
55
+ function requestWindowsTreeSignal(child, pid, force, spawnSync, timeoutMs) {
56
+ try {
57
+ const args = ["/PID", String(pid), "/T"];
58
+ if (force) args.push("/F");
59
+ const result = spawnSync("taskkill", args, {
60
+ shell: false,
61
+ windowsHide: true,
62
+ encoding: "utf8",
63
+ timeout: Math.max(1, timeoutMs),
64
+ origin: "process-tree:taskkill",
65
+ policy: "allow",
66
+ scope: "process-tree",
67
+ });
68
+ if (!result?.error && result?.status === 0) {
69
+ return { requested: true, treeRequested: true };
70
+ }
71
+ } catch {
72
+ // Fall through to the hard-phase fallback below.
73
+ }
74
+ // Never kill only the Windows root during the soft phase: doing so can
75
+ // orphan its descendants and make the later `/T /F` walk impossible.
76
+ if (!force) return { requested: false, treeRequested: false };
77
+ return {
78
+ requested: requestDirectSignal(child, "SIGKILL"),
79
+ treeRequested: false,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Retire one Broker-owned child tree inside a host-capped deadline.
85
+ *
86
+ * `treeMode` describes the ownership established at spawn time:
87
+ * - `sandbox`: a PID namespace / Windows Job close fence owns descendants;
88
+ * - `posix-group`: the child is the leader of a detached process group;
89
+ * - `windows-tree`: taskkill walks the live root's descendant tree.
90
+ *
91
+ * The function never throws and never treats a direct-child fallback as proof
92
+ * that descendants are gone. Callers can preserve their primary error while
93
+ * attaching the structured result, or fail closed on an unconfirmed explicit
94
+ * disconnect.
95
+ */
96
+ export async function terminateOwnedProcessTree(child, options = {}) {
97
+ const platform = options.platform || process.platform;
98
+ const cleanupTimeoutMs = boundedMilliseconds(
99
+ options.cleanupTimeoutMs,
100
+ PROCESS_TREE_CLEANUP_MAX_MS,
101
+ PROCESS_TREE_CLEANUP_MAX_MS,
102
+ );
103
+ const graceMs = Math.min(
104
+ cleanupTimeoutMs,
105
+ boundedMilliseconds(options.graceMs, 250, PROCESS_TREE_GRACE_MAX_MS),
106
+ );
107
+ const now = options.now || Date.now;
108
+ const sleep =
109
+ options.sleep ||
110
+ ((milliseconds) =>
111
+ new Promise((resolve) => setTimeout(resolve, milliseconds)));
112
+ const kill = options.kill || process.kill.bind(process);
113
+ const spawnSync =
114
+ options.spawnSync || executionBroker.spawnSync.bind(executionBroker);
115
+ const pid = validChildPid(child);
116
+ const inferredTreeMode =
117
+ options.treeMode ||
118
+ (options.sandboxManagedTree === true
119
+ ? "sandbox"
120
+ : platform === "win32"
121
+ ? "windows-tree"
122
+ : "posix-group");
123
+ const treeMode = TREE_MODES.has(inferredTreeMode)
124
+ ? inferredTreeMode
125
+ : platform === "win32"
126
+ ? "windows-tree"
127
+ : "posix-group";
128
+ const startedAt = now();
129
+ const deadline = startedAt + cleanupTimeoutMs;
130
+ let closeObserved = options.alreadyClosed === true;
131
+ let softRequested = false;
132
+ let hardRequested = false;
133
+ let treeRequested = false;
134
+ let escalated = false;
135
+
136
+ const onClose = () => {
137
+ closeObserved = true;
138
+ };
139
+ child?.once?.("close", onClose);
140
+
141
+ const remaining = () => Math.max(0, deadline - now());
142
+ const closed = () => childHasClosed(child, () => closeObserved);
143
+ const treeGone = () => {
144
+ if (!pid) return false;
145
+ if (treeMode === "sandbox") return closed();
146
+ if (treeMode === "posix-group") return posixGroupIsGone(pid, kill);
147
+ return treeRequested;
148
+ };
149
+ const confirmed = () => closed() && treeGone();
150
+ const waitForConfirmation = async (maximumMs) => {
151
+ const phaseDeadline = Math.min(deadline, now() + Math.max(0, maximumMs));
152
+ while (!confirmed() && now() < phaseDeadline) {
153
+ await sleep(Math.max(1, Math.min(10, phaseDeadline - now())));
154
+ }
155
+ return confirmed();
156
+ };
157
+ const request = (force) => {
158
+ if (!pid) {
159
+ return {
160
+ requested: requestDirectSignal(child, force ? "SIGKILL" : "SIGTERM"),
161
+ treeRequested: false,
162
+ };
163
+ }
164
+ if (treeMode === "sandbox") {
165
+ return {
166
+ requested: requestDirectSignal(child, force ? "SIGKILL" : "SIGTERM"),
167
+ treeRequested: true,
168
+ };
169
+ }
170
+ if (treeMode === "windows-tree") {
171
+ return requestWindowsTreeSignal(
172
+ child,
173
+ pid,
174
+ force,
175
+ spawnSync,
176
+ remaining(),
177
+ );
178
+ }
179
+ return requestPosixGroupSignal(
180
+ child,
181
+ pid,
182
+ force ? "SIGKILL" : "SIGTERM",
183
+ kill,
184
+ );
185
+ };
186
+
187
+ try {
188
+ if (!pid) {
189
+ // Test doubles and partial spawn failures have no verifiable tree
190
+ // identity. Preserve legacy best-effort direct-child cleanup without
191
+ // upgrading it to a whole-tree claim.
192
+ const attempt = request(true);
193
+ hardRequested = attempt.requested;
194
+ return {
195
+ pid: null,
196
+ treeMode,
197
+ verifiable: false,
198
+ softRequested,
199
+ hardRequested,
200
+ treeRequested: false,
201
+ escalated: true,
202
+ closed: closed(),
203
+ treeTerminated: false,
204
+ confirmed: false,
205
+ deadlineExceeded: false,
206
+ elapsedMs: Math.max(0, now() - startedAt),
207
+ };
208
+ }
209
+
210
+ // A managed sandbox close fence already proves its namespace/job is empty.
211
+ // A closed unmanaged POSIX root does not: descendants in the owned group
212
+ // must still be signalled and probed.
213
+ if (treeMode === "sandbox" && closed()) {
214
+ treeRequested = true;
215
+ } else if (!closed()) {
216
+ try {
217
+ child?.stdin?.end?.();
218
+ } catch {
219
+ // EOF is only an additional graceful hint; SIGTERM remains authoritative.
220
+ }
221
+ const attempt = request(false);
222
+ softRequested = attempt.requested;
223
+ treeRequested ||= attempt.treeRequested;
224
+ await waitForConfirmation(graceMs);
225
+ }
226
+
227
+ if (!confirmed()) {
228
+ escalated = true;
229
+ const attempt = request(true);
230
+ hardRequested = attempt.requested;
231
+ treeRequested ||= attempt.treeRequested;
232
+ if (remaining() > 0) await waitForConfirmation(remaining());
233
+ }
234
+
235
+ const didConfirm = confirmed();
236
+ return {
237
+ pid,
238
+ treeMode,
239
+ verifiable: true,
240
+ softRequested,
241
+ hardRequested,
242
+ treeRequested,
243
+ escalated,
244
+ closed: closed(),
245
+ treeTerminated: treeGone(),
246
+ confirmed: didConfirm,
247
+ deadlineExceeded: !didConfirm && remaining() === 0,
248
+ elapsedMs: Math.max(0, now() - startedAt),
249
+ };
250
+ } finally {
251
+ child?.removeListener?.("close", onClose);
252
+ }
253
+ }
@@ -14,7 +14,7 @@ import {
14
14
  projectWsTurnMessages,
15
15
  readVerifiedEvents as storeReadVerifiedEvents,
16
16
  readVerifiedProjection as storeReadVerifiedProjection,
17
- sessionExists as storeSessionExists,
17
+ sessionHasPersistedEvidence as storeSessionHasPersistedEvidence,
18
18
  } from "../harness/jsonl-session-store.js";
19
19
  import {
20
20
  createMcpLedgerEventReducer,
@@ -344,12 +344,16 @@ function createStreamingSessionHostProjection(sessionId) {
344
344
  /**
345
345
  * Read one real JSONL session for a host resume/attach boundary.
346
346
  *
347
- * `null` means no JSONL session exists and callers may use their legacy store.
348
- * A present but damaged/unanchored transcript returns a fail-closed snapshot
349
- * and no raw messages, so a host cannot silently fall back to stale history.
347
+ * `null` means no canonical JSONL evidence exists and callers may use their
348
+ * legacy store. A tombstoned, missing, damaged, or unanchored transcript
349
+ * returns a fail-closed snapshot and no raw messages, so a host cannot silently
350
+ * fall back to stale history or implicitly recreate a deleted generation.
350
351
  */
351
352
  export function readSessionHostResumeState(sessionId, dependencies = {}) {
352
- const exists = dependencies.sessionExists || storeSessionExists;
353
+ const exists =
354
+ dependencies.sessionHasPersistedEvidence ||
355
+ dependencies.sessionExists ||
356
+ storeSessionHasPersistedEvidence;
353
357
  const hasCustomLegacyReader =
354
358
  typeof dependencies.readVerifiedEvents === "function" &&
355
359
  dependencies.readVerifiedEvents !== storeReadVerifiedEvents;
@@ -260,7 +260,11 @@ function actionPreviews(kind, state, source, sourceId, linkedSessionId) {
260
260
  );
261
261
  }
262
262
  if (active && interactive) {
263
- routes.reply = preview("terminal", ["attach", sourceId]);
263
+ routes.reply = preview(
264
+ "cli",
265
+ ["daemon", "reply", sourceId, PROJECTION_PROMPT_PLACEHOLDER, "--json"],
266
+ { mutates: true, input: "prompt" },
267
+ );
264
268
  routes.attach = preview("terminal", ["attach", sourceId]);
265
269
  }
266
270
  if (active) {
@@ -10,7 +10,31 @@
10
10
 
11
11
  import { promises as fsp } from "node:fs";
12
12
  import { existsSync, statSync } from "node:fs";
13
- import { sessionPath } from "../harness/jsonl-session-store.js";
13
+ import {
14
+ getSessionPresence,
15
+ SESSION_PRESENCE,
16
+ sessionPath,
17
+ } from "../harness/jsonl-session-store.js";
18
+
19
+ function assertLiveTranscriptReadable(sessionId) {
20
+ const presence = getSessionPresence(sessionId);
21
+ if (
22
+ presence === SESSION_PRESENCE.PRESENT ||
23
+ presence === SESSION_PRESENCE.ABSENT
24
+ ) {
25
+ return;
26
+ }
27
+ const error = new Error(
28
+ presence === SESSION_PRESENCE.TOMBSTONED
29
+ ? `Session ${sessionId} has been deleted`
30
+ : `Session ${sessionId} transcript cannot be verified for live tailing`,
31
+ );
32
+ error.code =
33
+ presence === SESSION_PRESENCE.TOMBSTONED
34
+ ? "SESSION_DELETED"
35
+ : "SESSION_TRANSCRIPT_UNVERIFIED";
36
+ throw error;
37
+ }
14
38
 
15
39
  function parseLine(line) {
16
40
  const trimmed = line.trim();
@@ -53,6 +77,7 @@ function matchesFilter(event, { types, sinceMs }) {
53
77
  * - fromEnd (default): current EOF, so only new events are yielded
54
78
  */
55
79
  export function initialOffset(sessionId, { fromStart = false } = {}) {
80
+ assertLiveTranscriptReadable(sessionId);
56
81
  const p = sessionPath(sessionId);
57
82
  if (!existsSync(p)) return 0;
58
83
  if (fromStart) return 0;
@@ -98,6 +123,11 @@ export async function* followSession(sessionId, options = {}) {
98
123
  while (true) {
99
124
  if (signal?.aborted) return;
100
125
 
126
+ // A long-lived tail is a live canonical reader. Re-check the durable
127
+ // sidecar witness on every poll so external deletion or a tombstoned path
128
+ // restoration cannot silently turn into empty output or stale replay.
129
+ assertLiveTranscriptReadable(sessionId);
130
+
101
131
  if (existsSync(filePath)) {
102
132
  const stat = await fsp.stat(filePath);
103
133
  if (stat.size < offset) {
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Host-owned admission limits for file-backed CLI skills.
3
+ *
4
+ * These are security ceilings, not defaults that callers may raise. Loader
5
+ * options can only tighten them through resolveSkillLimits().
6
+ */
7
+ export const HOST_SKILL_LIMITS = Object.freeze({
8
+ maxSkillFileBytes: 256 * 1024,
9
+ maxSkillTotalBytes: 512 * 1024,
10
+ maxSkillDiscoveryEntries: 16_384,
11
+ maxSkillDiscoveryFiles: 4096,
12
+ maxSkillDiscoveryBytes: 64 * 1024 * 1024,
13
+ maxSkillNestDepth: 5,
14
+ maxSkillPromptBytes: 48_000,
15
+ // Provider-neutral conservative ceiling: one token cannot require fewer
16
+ // than zero bytes, and byte-level tokenizers cannot produce more tokens than
17
+ // the UTF-8 byte sequence. Hosts with a smaller context may tighten this.
18
+ maxSkillPromptTokens: 48_000,
19
+ maxSkillPromptTotalBytes: 192_000,
20
+ maxSkillPromptTotalTokens: 192_000,
21
+ });
22
+
23
+ const LIMIT_KEYS = Object.freeze(Object.keys(HOST_SKILL_LIMITS));
24
+
25
+ function tighteningLimit(value, hardLimit) {
26
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
27
+ return hardLimit;
28
+ }
29
+ return Math.min(hardLimit, Math.max(1, Math.floor(value)));
30
+ }
31
+
32
+ export function resolveSkillLimits(options = {}) {
33
+ const limits = {};
34
+ for (const key of LIMIT_KEYS) {
35
+ limits[key] = tighteningLimit(options?.[key], HOST_SKILL_LIMITS[key]);
36
+ }
37
+ return Object.freeze(limits);
38
+ }
39
+
40
+ export function skillBudgetError(code, message, details = {}) {
41
+ const error = new Error(message);
42
+ error.name = "SkillBudgetError";
43
+ error.code = code;
44
+ if (Number.isFinite(details.limit)) error.limit = details.limit;
45
+ if (Number.isFinite(details.actual)) error.actual = details.actual;
46
+ if (details.component) error.component = details.component;
47
+ return error;
48
+ }
49
+
50
+ export function isSkillBudgetError(error) {
51
+ return error?.name === "SkillBudgetError";
52
+ }
53
+
54
+ export function assertSkillFileSize(component, size, limitsInput = {}) {
55
+ const limits = resolveSkillLimits(limitsInput);
56
+ if (!Number.isSafeInteger(size) || size < 0) {
57
+ throw skillBudgetError(
58
+ "CC_SKILL_FILE_SIZE_INVALID",
59
+ `Skill component size is invalid: ${component}`,
60
+ { component },
61
+ );
62
+ }
63
+ if (size > limits.maxSkillFileBytes) {
64
+ throw skillBudgetError(
65
+ "CC_SKILL_FILE_TOO_LARGE",
66
+ `Skill component exceeds the host byte limit: ${component}`,
67
+ { component, limit: limits.maxSkillFileBytes, actual: size },
68
+ );
69
+ }
70
+ return size;
71
+ }
72
+
73
+ export function assertSkillTotalSize(size, limitsInput = {}) {
74
+ const limits = resolveSkillLimits(limitsInput);
75
+ if (!Number.isSafeInteger(size) || size < 0) {
76
+ throw skillBudgetError(
77
+ "CC_SKILL_TOTAL_SIZE_INVALID",
78
+ "Skill component total is invalid.",
79
+ );
80
+ }
81
+ if (size > limits.maxSkillTotalBytes) {
82
+ throw skillBudgetError(
83
+ "CC_SKILL_TOTAL_BYTES_EXCEEDED",
84
+ "Skill components exceed the host aggregate byte limit.",
85
+ { limit: limits.maxSkillTotalBytes, actual: size },
86
+ );
87
+ }
88
+ return size;
89
+ }
90
+
91
+ export function createSkillDiscoveryBudget(limitsInput = {}) {
92
+ return {
93
+ limits: resolveSkillLimits(limitsInput),
94
+ entries: 0,
95
+ files: 0,
96
+ bytes: 0,
97
+ };
98
+ }
99
+
100
+ export function debitSkillDiscoveryEntry(budget) {
101
+ const nextEntries = budget.entries + 1;
102
+ if (
103
+ !Number.isSafeInteger(nextEntries) ||
104
+ nextEntries > budget.limits.maxSkillDiscoveryEntries
105
+ ) {
106
+ throw skillBudgetError(
107
+ "CC_SKILL_DISCOVERY_ENTRIES_EXCEEDED",
108
+ "Recursive skill discovery exceeds the host directory-entry limit.",
109
+ {
110
+ limit: budget.limits.maxSkillDiscoveryEntries,
111
+ actual: nextEntries,
112
+ },
113
+ );
114
+ }
115
+ budget.entries = nextEntries;
116
+ return budget;
117
+ }
118
+
119
+ export function debitSkillDiscoveryBudget(budget, fileCount, byteCount) {
120
+ const nextFiles = budget.files + fileCount;
121
+ const nextBytes = budget.bytes + byteCount;
122
+ if (
123
+ !Number.isSafeInteger(nextFiles) ||
124
+ nextFiles > budget.limits.maxSkillDiscoveryFiles
125
+ ) {
126
+ throw skillBudgetError(
127
+ "CC_SKILL_DISCOVERY_FILES_EXCEEDED",
128
+ "Recursive skill discovery exceeds the host file-count limit.",
129
+ {
130
+ limit: budget.limits.maxSkillDiscoveryFiles,
131
+ actual: nextFiles,
132
+ },
133
+ );
134
+ }
135
+ if (
136
+ !Number.isSafeInteger(nextBytes) ||
137
+ nextBytes > budget.limits.maxSkillDiscoveryBytes
138
+ ) {
139
+ throw skillBudgetError(
140
+ "CC_SKILL_DISCOVERY_BYTES_EXCEEDED",
141
+ "Recursive skill discovery exceeds the host aggregate byte limit.",
142
+ {
143
+ limit: budget.limits.maxSkillDiscoveryBytes,
144
+ actual: nextBytes,
145
+ },
146
+ );
147
+ }
148
+ budget.files = nextFiles;
149
+ budget.bytes = nextBytes;
150
+ return budget;
151
+ }
152
+
153
+ export function measureSkillPrompt(body) {
154
+ if (typeof body !== "string") {
155
+ throw skillBudgetError(
156
+ "CC_SKILL_PROMPT_INVALID",
157
+ "Skill instructions must be a string before model projection.",
158
+ );
159
+ }
160
+ const bytes = Buffer.byteLength(body, "utf8");
161
+ return Object.freeze({
162
+ bytes,
163
+ // Conservative provider-neutral upper bound for text-derived tokens. This
164
+ // intentionally does not trust a configurable estimator that could return
165
+ // zero or NaN.
166
+ tokenUpperBound: bytes,
167
+ });
168
+ }
169
+
170
+ export function admitSkillPrompt(body, limitsInput = {}) {
171
+ const limits = resolveSkillLimits(limitsInput);
172
+ const measured = measureSkillPrompt(body);
173
+ if (measured.bytes > limits.maxSkillPromptBytes) {
174
+ throw skillBudgetError(
175
+ "CC_SKILL_PROMPT_BYTES_EXCEEDED",
176
+ "Skill instructions exceed the host model-projection byte limit.",
177
+ { limit: limits.maxSkillPromptBytes, actual: measured.bytes },
178
+ );
179
+ }
180
+ if (measured.tokenUpperBound > limits.maxSkillPromptTokens) {
181
+ throw skillBudgetError(
182
+ "CC_SKILL_PROMPT_TOKENS_EXCEEDED",
183
+ "Skill instructions exceed the host model-projection token limit.",
184
+ { limit: limits.maxSkillPromptTokens, actual: measured.tokenUpperBound },
185
+ );
186
+ }
187
+ return measured;
188
+ }
189
+
190
+ export function debitSkillPromptBudget(budget, measured) {
191
+ const nextBytes = budget.bytes + measured.bytes;
192
+ const nextTokens = budget.tokens + measured.tokenUpperBound;
193
+ if (
194
+ !Number.isSafeInteger(nextBytes) ||
195
+ nextBytes > budget.limits.maxSkillPromptTotalBytes
196
+ ) {
197
+ throw skillBudgetError(
198
+ "CC_SKILL_PROMPT_TOTAL_BYTES_EXCEEDED",
199
+ "Skill instructions exceed the host aggregate model-projection byte limit.",
200
+ {
201
+ limit: budget.limits.maxSkillPromptTotalBytes,
202
+ actual: nextBytes,
203
+ },
204
+ );
205
+ }
206
+ if (
207
+ !Number.isSafeInteger(nextTokens) ||
208
+ nextTokens > budget.limits.maxSkillPromptTotalTokens
209
+ ) {
210
+ throw skillBudgetError(
211
+ "CC_SKILL_PROMPT_TOTAL_TOKENS_EXCEEDED",
212
+ "Skill instructions exceed the host aggregate model-projection token limit.",
213
+ {
214
+ limit: budget.limits.maxSkillPromptTotalTokens,
215
+ actual: nextTokens,
216
+ },
217
+ );
218
+ }
219
+ budget.bytes = nextBytes;
220
+ budget.tokens = nextTokens;
221
+ return budget;
222
+ }