chainlesschain 0.165.6 → 0.165.8

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 (238) hide show
  1. package/package.json +2 -1
  2. package/src/assets/web-panel/assets/{AIOps-vfoDb6HZ.js → AIOps-B3gWmXjo.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Cm5f7p93.js → ActionButton-G9pdT-0e.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-yRuvgWxa.js → Analytics-Ca30J7m2.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-BROsC4h1.js → AppLayout-BtVTm8PU.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CimHApb1.js → Artifacts-BhViNa6u.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-DI8kr8zI.js → Audit-DhKq-FnY.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DSLzKy-n.js → BackgroundAgents-BL0SVbnE.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DkH_n2r1.js → Backup-BWkblnFi.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CKSrUoRA.js → BaseInput-ClaRNrF8.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-ClwfMXUS.js → Chat-sZE2Z77o.js} +4 -4
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjMaqfL7.js → ChatBubbleRenderer-BAfKOnRe.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-BYTehcQG.js → Checkbox-Cvhay5WM.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-B8AMcp6J.js → Codegen-BpejL-4N.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-BNSogPo3.js → Col-g2Ki6g_1.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-JxPvGb8O.js → Community-D2xKbNo9.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DF7bFAgE.js → Compact-DbL1Pds3.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Bvhqnwrq.js → Compliance-DGtZCV4c.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-BDS0CJvM.js → Cowork-4kQpoNsI.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-DqvRfxoQ.js → Cron-BxTYXXAN.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-CDcIvnqS.js → Crosschain-k0J0LT2N.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-D7jaUxU3.js → DID-BaqhoPgN.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-B5A18wEV.js → Dashboard-T4RVPIM1.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-_6IVkGag.js → Dropdown-DszOToWq.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-C9PeiKCG.js → EmailListRenderer-C9KJU7HF.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DXjuEHrX.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-B4IAR9zv.js → Federation-BI-_2xZX.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CxQeSNx1.js → FormItemContext-DsnDEPsl.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-B8GS-Y3N.js → GenericCardRenderer-BLncAKKK.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BS8SouUX.js → Git-BPQhGXDn.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-Dq9aYb5Q.js → Governance-CyElbUQW.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BrCEe9AJ.js → Inference-CcSntBQD.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DybOpiY2.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-DBpzbRfx.js → Logs-CNEtr-wz.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-Bj9xI0YH.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-Cc7CCSCu.js → Marketplace-Dd2notAJ.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DFaIhOfu.js → McpTools-BBsN2CSW.js} +4 -4
  38. package/src/assets/web-panel/assets/{Memory-KCqlp9ol.js → Memory-Dg2uTvwx.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-Bys84ySN.js → MobileBridge-r1YMRiJV.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-B-6qU-je.js → MobileProjects-C-89kSm4.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-BgY-fvCq.js → Mtc-CrPZcVgf.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-B8pCmqQJ.js → MtcAudit-sxi7hqJb.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-BbOmYutA.js → Multisig-CAgOMfF1.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-BocLQz_M.js → NLProgramming-DafaPts3.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CSwbxEop.js → Notes-CJM-xUW_.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-CqFNSYG3.js → NotificationSettings-L0ZjfAh_.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-C6gMfAk7.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-QrDXVdMJ.js → Organization-DeLtgAD8.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Cxntotjt.js → Overflow-CKC2b10z.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-CIdBOwNq.js → P2P-eyf2rzY1.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CT5aP8rD.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-DH7OM8XU.js → Permissions-B8p3W6JK.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CnJqbWce.js → PersonalDataHub-BnRMxTq3.js} +5 -5
  54. package/src/assets/web-panel/assets/{Pipeline-5C3HG1kH.js → Pipeline-DjCNI1li.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B4yIp7xK.js → Privacy-u6gXLbWT.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-BMvt-2M0.js → ProjectInit-BZAw4tkM.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-CvSTEMRG.js → ProjectSettings-B1Tr72q7.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BKBJxEek.js → Projects-DO7HFuBQ.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-BdwUqFWN.js → Providers-BjCztyhj.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DtnCKlTm.js → QrScannerModal-ZWyCUbR1.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-sTbgwyNr.js → QuickAsk-DhDjeW4G.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Bzr14gSb.js → Recommend-R_CN2MuU.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C_TF5IaJ.js → RemoteSession-uxFcujeu.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-GCAM4H2K.js → Reputation-DX8D7rks.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-Dir0r6OQ.js → Row-BYahQILD.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-CpGnlecX.js → RssFeed-CUSQ2tm-.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DMMamC6u.js → Search-CnN_S8Cs.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-yqay5yG-.js → Security-DcZlh-O4.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-C5i2wh9G.js → Services-ZhBOjzIX.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-vUqk35yj.js → Skeleton-D5Qnq_lI.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DzcSkhot.js → Skills-BC9cZ3Be.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-CB6qbYNr.js → Sla-DauJ3E8_.js} +1 -1
  73. package/src/assets/web-panel/assets/SpeechSettings-DbNeZ_fh.js +1 -0
  74. package/src/assets/web-panel/assets/{SyncSettings-D-RrsxTJ.js → SyncSettings-DUvJbSct.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DFxdOJiq.js → Tasks-BXa-5noQ.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-SKAob_Aq.js → Templates-JxaiUt_3.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-BPfsfHQ-.js → Tenant-BW4ERQN8.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-8iw7FpUf.js → Terminal-DltUbdtv.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-C4Gjj5Su.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-COYhN18e.js → Tokens-D_VEV60K.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-B-JzXcf6.js → Trigger-CCXTCs0R.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BJ5bg0nI.js → Trust-lkGRIPcG.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-BG_ByRMf.js → UkeySign-BfGeQ9HX.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-B3kOnSiU.js → VideoEditing-BEC7pYvc.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-CcoGFnK7.js → Wallet-BZdG24o0.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DdT8vRC_.js → WebAuthn-yoEddedp.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-CZu3M9N8.js → WorkflowEditor-BXZEprOW.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DxJt3Qba.js → chat-gg0bzZal.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DdYfwJaB.js → colors-DjRVRuin.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-BG3SUzwj.js → compact-item-su-dieAc.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-By51z2-1.js → createContext-Ci3S8oKF.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-B0apZ3KJ.js → hasIn-D8F10heH.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DE76D-kA.js → index-11wtYQiF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BkvcEo4p.js → index-1IgxgALJ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-Bos5_aMV.js → index-1agckWSq.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BfWYsB9t.js → index-5JqhujnR.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CAhHirVH.js → index-6Tniqk1s.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BYINoRLK.js → index-8Z-8BIlN.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B5rLiEo0.js → index-BCI5v0yB.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BZ_0qm4g.js → index-BDJntYmJ.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CWEuZWCK.js → index-BGzixOGZ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BLIbGOLX.js → index-BMMOVoLD.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-bP_VlDb4.js → index-BTFpdBtt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-B3rWuPMW.js → index-Ba_HyPnE.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Da_WBdLX.js → index-BbF1IHof.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-RD6O-0C6.js → index-BgT9su_e.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CpjZhgNd.js → index-BzPB6kGZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Q2SCX-MI.js → index-C6ygzplr.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BIrJfqO4.js → index-CC3ZDjzY.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BBPf3TpM.js → index-CEmXRhAE.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
  113. package/src/assets/web-panel/assets/{index-Cp6e-hGw.js → index-CMPqd5Cv.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-kf57LJ93.js → index-COWd8uGx.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CW93tZDv.js → index-CPIYSp4H.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BewKYrX9.js → index-CR0h6seT.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-p1_8SbI1.js → index-CUmDhC4j.js} +1 -1
  118. package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
  119. package/src/assets/web-panel/assets/{index-ZxtP8jG7.js → index-CusgZPtc.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BWF8_mkx.js → index-DBAETXft.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BP94CrBV.js → index-DIJ2qFOC.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CNIxXMmN.js → index-Dbq4NMET.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CzmQElZy.js → index-DkMEF5VT.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-ZFbps9po.js → index-DkfQR3hM.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Dz49VOyQ.js → index-DnLucOn9.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-oxRrmFO9.js → index-DugB_aKa.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Bissefox.js → index-DyLUarhZ.js} +3 -3
  128. package/src/assets/web-panel/assets/{index-DaIz0NAW.js → index-T6_6ygJZ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-SQqOlR3d.js → index-Tu8eWqh-.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DCn8IcxV.js → index-W7XUgdVL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-3jWkIu5j.js → index-dHZinfko.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C1C9i79A.js → index-jyNgp5FI.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-BWJktcg8.js → initDefaultProps-Cv4p8e4w.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BEXBzUFV.js → motion-B8EwmMOw.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BUKwn2sx.js → move-B8R9F8TK.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-FTI7btqQ.js → mtc-parser-DoFYz5Bp.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-CsLtSPH3.js → omit-CHWYLBjI.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DWZxsT_S.js → pickAttrs-CXuPDO77.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-Bcfs43CX.js → placementArrow-BaAtUUon.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-a9bcC1p-.js → responsiveObserve-DCc4zcDD.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-BDTzgOXN.js → slide-B_lx89oZ.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-Iq1j_HUg.js → statusUtils-DE8DPmR0.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-DAZEYxeh.js → styleChecker-1ktXOsL_.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DVkjyDrh.js → useFlexGapSupport-xH89-wjh.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-wXTpNTHt.js → useFs-DscvfW5p.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CtQM_aZr.js → usePersonalDataHub-RdMKQta7.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-hjEoG85v.js → vnode-Z3wTni2T.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-BHVpyKg_.js → zoom-B0iChCsm.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +3 -3
  151. package/src/commands/agent.js +91 -60
  152. package/src/commands/git.js +39 -0
  153. package/src/commands/plugin.js +17 -2
  154. package/src/commands/session-group.js +131 -0
  155. package/src/commands/session-location.js +48 -0
  156. package/src/commands/session.js +11 -0
  157. package/src/commands/sync-providers.js +4 -1
  158. package/src/commands/update.js +1 -0
  159. package/src/data/changelog.json +27 -1
  160. package/src/gateways/ws/ws-agent-handler.js +20 -12
  161. package/src/harness/jsonl-session-store.js +279 -37
  162. package/src/harness/mcp-client.js +701 -91
  163. package/src/harness/session-index.js +15 -4
  164. package/src/lib/agent-worktree.js +8 -1
  165. package/src/lib/auto-mode-safety-classifier.js +3 -17
  166. package/src/lib/background-agent-keeper-protocol.js +26 -6
  167. package/src/lib/background-agent-supervisor.js +17 -1
  168. package/src/lib/browser-evidence.js +1405 -0
  169. package/src/lib/chrome-connector.js +465 -79
  170. package/src/lib/claude-project-auto-memory.js +481 -0
  171. package/src/lib/claude-project-storage-layout.js +104 -0
  172. package/src/lib/cloud/cloud-client.js +40 -0
  173. package/src/lib/execution-location-local-supervisor.mjs +226 -0
  174. package/src/lib/execution-location-runner-lifecycle.js +991 -0
  175. package/src/lib/execution-location-target-preflight.js +333 -0
  176. package/src/lib/execution-location-target.js +783 -13
  177. package/src/lib/file-lines.js +6 -1
  178. package/src/lib/gateway-client-resilience.js +571 -0
  179. package/src/lib/gitlab-mr-worktree.js +176 -0
  180. package/src/lib/hooks-v2-producers.js +119 -15
  181. package/src/lib/hooks-v2-workspace-context.js +44 -1
  182. package/src/lib/host-resource-budget.js +295 -0
  183. package/src/lib/ide-context.js +15 -2
  184. package/src/lib/llm-config-defaults.js +43 -0
  185. package/src/lib/mcp-lifecycle-authority.js +887 -0
  186. package/src/lib/mcp-oauth.js +101 -20
  187. package/src/lib/mcp-tls.js +103 -3
  188. package/src/lib/paths.js +260 -32
  189. package/src/lib/permission-request.js +48 -0
  190. package/src/lib/plugin-runtime/bin.js +3 -1
  191. package/src/lib/plugin-runtime/hooks.js +3 -1
  192. package/src/lib/plugin-runtime/install.js +117 -1
  193. package/src/lib/plugin-runtime/lsp.js +4 -4
  194. package/src/lib/plugin-runtime/marketplace-archive-source.js +40 -5
  195. package/src/lib/plugin-runtime/marketplace-catalog.js +13 -2
  196. package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
  197. package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
  198. package/src/lib/plugin-runtime/marketplace-source-adapter.js +33 -8
  199. package/src/lib/plugin-runtime/mcp.js +3 -1
  200. package/src/lib/plugin-runtime/monitors.js +3 -1
  201. package/src/lib/plugin-runtime/remote-source.js +151 -6
  202. package/src/lib/plugin-runtime/settings.js +3 -1
  203. package/src/lib/plugin-runtime/trust.js +274 -13
  204. package/src/lib/process-execution-broker/credential-agent.js +210 -41
  205. package/src/lib/process-execution-broker/index.js +15 -1
  206. package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
  207. package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
  208. package/src/lib/project-mcp-trust.js +165 -10
  209. package/src/lib/resume-session.js +9 -6
  210. package/src/lib/session-core-singletons.js +26 -11
  211. package/src/lib/session-host-lease.js +52 -16
  212. package/src/lib/session-lifecycle-scope.js +153 -0
  213. package/src/lib/session-message-fabric.js +168 -21
  214. package/src/lib/session-projection.js +244 -4
  215. package/src/lib/session-store-guard.js +3 -1
  216. package/src/lib/session-workbench-store.js +466 -0
  217. package/src/lib/spellcheck.js +211 -0
  218. package/src/lib/sub-agent-context.js +9 -9
  219. package/src/lib/sync-cli-db.js +485 -129
  220. package/src/lib/sync-credentials.js +31 -20
  221. package/src/lib/web-fetch.js +112 -40
  222. package/src/lib/web-search.js +127 -61
  223. package/src/lib/workspace-trust.js +793 -0
  224. package/src/repl/agent-repl.js +124 -5
  225. package/src/repl/permission-prompt.js +10 -12
  226. package/src/runtime/agent-core.js +311 -18
  227. package/src/runtime/bootstrap.js +37 -13
  228. package/src/runtime/coding-agent-contract-shared.cjs +11 -4
  229. package/src/runtime/headless-runner.js +234 -36
  230. package/src/runtime/headless-stream.js +181 -6
  231. package/src/runtime/mcp-config.js +298 -8
  232. package/src/runtime/policies/agent-policy.js +8 -0
  233. package/src/runtime/status-diagnostics-lite.js +9 -1
  234. package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +0 -1
  235. package/src/assets/web-panel/assets/SpeechSettings-BHUI7cox.js +0 -1
  236. package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +0 -1
  237. package/src/assets/web-panel/assets/index-BETiL0a3.js +0 -1
  238. package/src/assets/web-panel/assets/index-BhAVHuW8.js +0 -1
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
  import { TextDecoder } from "node:util";
6
7
  import {
7
8
  EXECUTION_LOCATION_TARGET_ATTESTATION_SCHEMA,
@@ -16,6 +17,9 @@ import {
16
17
  } from "./secure-file-identity.js";
17
18
  import { canonicalJson } from "./scheduler-kernel/contract.js";
18
19
  import { executionBroker } from "./process-execution-broker/index.js";
20
+ import { assertExecutionLocationRunnerLeaseAuthority } from "./execution-location-runner-lifecycle.js";
21
+ import { ensurePrivateDirectory, repairPrivatePaths } from "./secure-fs.js";
22
+ import { getSessionAntiRollbackDirectory } from "./session-anti-rollback-anchor.js";
19
23
  import {
20
24
  MAX_EXECUTION_LOCATION_RESULT_BUNDLE_BYTES,
21
25
  normalizeExecutionLocationResultBundle,
@@ -24,6 +28,8 @@ import {
24
28
 
25
29
  export const EXECUTION_LOCATION_PROFILE_SCHEMA =
26
30
  "cc-execution-location-profile/v1";
31
+ export const EXECUTION_LOCATION_PROFILE_SCHEMA_V2 =
32
+ "cc-execution-location-profile/v2";
27
33
  export { EXECUTION_LOCATION_TARGET_ATTESTATION_SCHEMA };
28
34
  export const EXECUTION_LOCATION_TARGET_RESUME_SCHEMA =
29
35
  "cc-execution-location-target-resume/v1";
@@ -36,13 +42,26 @@ const SESSION_LOCATION_AUTHORITY_SCHEMA =
36
42
  "cc-session-execution-location-authority/v1";
37
43
  const SESSION_LOCATION_HANDOFF_INSTALL_SCHEMA =
38
44
  "chainlesschain.session-execution-location-handoff-install/v1";
39
- const PROFILE_TARGETS = new Set(["wsl", "ssh", "container"]);
45
+ const PROFILE_TARGETS = new Set(["local", "wsl", "ssh", "container"]);
46
+ const RUNNER_LIFECYCLE_STATES = new Set([
47
+ "accepting",
48
+ "draining",
49
+ "parked",
50
+ "reclaiming",
51
+ ]);
40
52
  const SESSION_STORE_MODES = new Set(["replicated", "shared"]);
41
53
  const SHA256_RE = /^sha256:[a-f0-9]{64}$/u;
42
54
  const COMMIT_RE = /^[a-f0-9]{40,64}$/u;
43
55
  const MAX_PROFILE_BYTES = 1024 * 1024;
44
56
  const MAX_PROBE_BYTES = 1024 * 1024;
45
57
  const MAX_REPLICA_BYTES = 64 * 1024 * 1024;
58
+ const DEFAULT_TARGET_COMMAND_TIMEOUT_MS = 30_000;
59
+ const MAX_TARGET_COMMAND_TIMEOUT_MS = 120_000;
60
+ const REPLICA_TRANSFER_TIMEOUT_MS = MAX_TARGET_COMMAND_TIMEOUT_MS;
61
+ const MAX_PROXY_AUTHORITY_AGE_MS = 5 * 60 * 1000;
62
+ const LOCAL_TARGET_SUPERVISOR = fileURLToPath(
63
+ new URL("./execution-location-local-supervisor.mjs", import.meta.url),
64
+ );
46
65
  const RESULT_MEDIA_TYPE_RE =
47
66
  /^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/u;
48
67
 
@@ -87,6 +106,26 @@ function safePath(value, field) {
87
106
  return normalized;
88
107
  }
89
108
 
109
+ function safeInteger(value, field, minimum, maximum) {
110
+ const normalized = Number(value);
111
+ if (
112
+ !Number.isSafeInteger(normalized) ||
113
+ normalized < minimum ||
114
+ normalized > maximum
115
+ ) {
116
+ throw new TypeError(`${field} is invalid`);
117
+ }
118
+ return normalized;
119
+ }
120
+
121
+ function safeTimestamp(value, field) {
122
+ const normalized = safeString(value, field, 64);
123
+ if (!Number.isFinite(Date.parse(normalized))) {
124
+ throw new TypeError(`${field} is invalid`);
125
+ }
126
+ return normalized;
127
+ }
128
+
90
129
  function exactObject(value, fields, field) {
91
130
  if (!value || typeof value !== "object" || Array.isArray(value)) {
92
131
  throw new TypeError(`${field} must be an object`);
@@ -142,10 +181,169 @@ function normalizeSessionStore(value) {
142
181
  return Object.freeze({ mode, targetSessionId, headHash, eventCount });
143
182
  }
144
183
 
184
+ function normalizeRunnerLifecycle(value, profileCwd) {
185
+ const input = exactObject(
186
+ value,
187
+ [
188
+ "runnerId",
189
+ "authorityFile",
190
+ "state",
191
+ "generation",
192
+ "lease",
193
+ "proxyAuthority",
194
+ "baseDir",
195
+ "resources",
196
+ "postSessionHook",
197
+ ],
198
+ "profile.lifecycle",
199
+ );
200
+ const state = safeName(input.state, "profile.lifecycle.state", 32);
201
+ if (!RUNNER_LIFECYCLE_STATES.has(state)) {
202
+ throw new TypeError("profile.lifecycle.state is invalid");
203
+ }
204
+ if (state === "parked" || state === "reclaiming") {
205
+ throw new TypeError("profile lifecycle is not accepting target work");
206
+ }
207
+
208
+ const generation = safeInteger(
209
+ input.generation,
210
+ "profile.lifecycle.generation",
211
+ 1,
212
+ Number.MAX_SAFE_INTEGER,
213
+ );
214
+ const lease = exactObject(
215
+ input.lease,
216
+ ["id", "generation", "expiresAt"],
217
+ "profile.lifecycle.lease",
218
+ );
219
+ const leaseGeneration = safeInteger(
220
+ lease.generation,
221
+ "profile.lifecycle.lease.generation",
222
+ 1,
223
+ generation,
224
+ );
225
+ const leaseExpiresAt = safeTimestamp(
226
+ lease.expiresAt,
227
+ "profile.lifecycle.lease.expiresAt",
228
+ );
229
+
230
+ const proxyAuthority = exactObject(
231
+ input.proxyAuthority,
232
+ ["id", "revision", "issuedAt", "expiresAt"],
233
+ "profile.lifecycle.proxyAuthority",
234
+ );
235
+ const proxyIssuedAt = safeTimestamp(
236
+ proxyAuthority.issuedAt,
237
+ "profile.lifecycle.proxyAuthority.issuedAt",
238
+ );
239
+ const proxyExpiresAt = safeTimestamp(
240
+ proxyAuthority.expiresAt,
241
+ "profile.lifecycle.proxyAuthority.expiresAt",
242
+ );
243
+ if (Date.parse(proxyExpiresAt) <= Date.parse(proxyIssuedAt)) {
244
+ throw new TypeError("profile lifecycle proxy authority is invalid");
245
+ }
246
+
247
+ const baseDir = exactObject(
248
+ input.baseDir,
249
+ ["path", "writableRequired"],
250
+ "profile.lifecycle.baseDir",
251
+ );
252
+ const baseDirPath = safePath(baseDir.path, "profile.lifecycle.baseDir.path");
253
+ if (baseDirPath !== profileCwd || baseDir.writableRequired !== true) {
254
+ throw new TypeError("profile lifecycle base directory is invalid");
255
+ }
256
+
257
+ const resources = exactObject(
258
+ input.resources,
259
+ ["cpuSeconds", "memoryBytes"],
260
+ "profile.lifecycle.resources",
261
+ );
262
+ const memoryBytes = safeInteger(
263
+ resources.memoryBytes,
264
+ "profile.lifecycle.resources.memoryBytes",
265
+ 64 * 1024 * 1024,
266
+ 64 * 1024 * 1024 * 1024,
267
+ );
268
+ const cpuSeconds = safeInteger(
269
+ resources.cpuSeconds,
270
+ "profile.lifecycle.resources.cpuSeconds",
271
+ 1,
272
+ 24 * 60 * 60,
273
+ );
274
+
275
+ const postSessionHook = exactObject(
276
+ input.postSessionHook,
277
+ ["digest", "generation"],
278
+ "profile.lifecycle.postSessionHook",
279
+ );
280
+ const hookDigest = safeString(
281
+ postSessionHook.digest,
282
+ "profile.lifecycle.postSessionHook.digest",
283
+ 80,
284
+ ).toLowerCase();
285
+ if (!SHA256_RE.test(hookDigest)) {
286
+ throw new TypeError("profile lifecycle post-session hook is invalid");
287
+ }
288
+ const hookGeneration = safeInteger(
289
+ postSessionHook.generation,
290
+ "profile.lifecycle.postSessionHook.generation",
291
+ 1,
292
+ generation,
293
+ );
294
+ if (hookGeneration !== leaseGeneration) {
295
+ throw new TypeError("profile lifecycle post-session hook fence is stale");
296
+ }
297
+
298
+ return Object.freeze({
299
+ runnerId: safeName(input.runnerId, "profile.lifecycle.runnerId"),
300
+ authorityFile: path.resolve(
301
+ safePath(input.authorityFile, "profile.lifecycle.authorityFile"),
302
+ ),
303
+ state,
304
+ generation,
305
+ lease: Object.freeze({
306
+ id: safeName(lease.id, "profile.lifecycle.lease.id"),
307
+ generation: leaseGeneration,
308
+ expiresAt: leaseExpiresAt,
309
+ }),
310
+ proxyAuthority: Object.freeze({
311
+ id: safeName(proxyAuthority.id, "profile.lifecycle.proxyAuthority.id"),
312
+ revision: safeInteger(
313
+ proxyAuthority.revision,
314
+ "profile.lifecycle.proxyAuthority.revision",
315
+ 1,
316
+ Number.MAX_SAFE_INTEGER,
317
+ ),
318
+ issuedAt: proxyIssuedAt,
319
+ expiresAt: proxyExpiresAt,
320
+ }),
321
+ baseDir: Object.freeze({ path: baseDirPath, writableRequired: true }),
322
+ resources: Object.freeze({ cpuSeconds, memoryBytes }),
323
+ postSessionHook: Object.freeze({
324
+ digest: hookDigest,
325
+ generation: hookGeneration,
326
+ }),
327
+ });
328
+ }
329
+
145
330
  function normalizeTransport(target, value) {
146
331
  if (!value || typeof value !== "object" || Array.isArray(value)) {
147
332
  throw new TypeError("profile.transport must be an object");
148
333
  }
334
+ if (target === "local") {
335
+ const input = exactObject(
336
+ value,
337
+ ["home", "securityHome"],
338
+ "profile.transport",
339
+ );
340
+ return Object.freeze({
341
+ home: path.resolve(safePath(input.home, "profile.transport.home")),
342
+ securityHome: path.resolve(
343
+ safePath(input.securityHome, "profile.transport.securityHome"),
344
+ ),
345
+ });
346
+ }
149
347
  if (target === "wsl") {
150
348
  const input = exactObject(value, ["distro"], "profile.transport");
151
349
  return Object.freeze({
@@ -228,14 +426,24 @@ export function normalizeExecutionLocationProfile(value) {
228
426
  "sessionStore",
229
427
  ];
230
428
  const suppliedDigest = value?.profileDigest;
429
+ const isV2 = value?.schema === EXECUTION_LOCATION_PROFILE_SCHEMA_V2;
231
430
  const input = exactObject(
232
431
  value,
233
- suppliedDigest == null ? baseFields : [...baseFields, "profileDigest"],
432
+ suppliedDigest == null
433
+ ? isV2
434
+ ? [...baseFields, "lifecycle"]
435
+ : baseFields
436
+ : isV2
437
+ ? [...baseFields, "lifecycle", "profileDigest"]
438
+ : [...baseFields, "profileDigest"],
234
439
  "execution location profile",
235
440
  );
236
- if (input.schema !== EXECUTION_LOCATION_PROFILE_SCHEMA) {
441
+ if (
442
+ input.schema !== EXECUTION_LOCATION_PROFILE_SCHEMA &&
443
+ input.schema !== EXECUTION_LOCATION_PROFILE_SCHEMA_V2
444
+ ) {
237
445
  throw new TypeError(
238
- `execution location profile must use ${EXECUTION_LOCATION_PROFILE_SCHEMA}`,
446
+ `execution location profile must use ${EXECUTION_LOCATION_PROFILE_SCHEMA} or ${EXECUTION_LOCATION_PROFILE_SCHEMA_V2}`,
239
447
  );
240
448
  }
241
449
  if (containsSecret(JSON.stringify(input))) {
@@ -262,13 +470,14 @@ export function normalizeExecutionLocationProfile(value) {
262
470
  if (!COMMIT_RE.test(gitCommit)) {
263
471
  throw new TypeError("profile.expected.gitCommit is invalid");
264
472
  }
473
+ const cwd = safePath(input.cwd, "profile.cwd");
265
474
  const profile = {
266
- schema: EXECUTION_LOCATION_PROFILE_SCHEMA,
475
+ schema: input.schema,
267
476
  id: safeName(input.id, "profile.id"),
268
477
  target,
269
478
  evidenceId: safeName(input.evidenceId, "profile.evidenceId"),
270
479
  cliCommand: safePath(input.cliCommand, "profile.cliCommand"),
271
- cwd: safePath(input.cwd, "profile.cwd"),
480
+ cwd,
272
481
  transport: normalizeTransport(target, input.transport),
273
482
  expected: Object.freeze({
274
483
  platform: safeName(expected.platform, "profile.expected.platform", 64),
@@ -282,9 +491,14 @@ export function normalizeExecutionLocationProfile(value) {
282
491
  tools: Object.freeze(normalizeTools(expected.tools)),
283
492
  }),
284
493
  sessionStore: normalizeSessionStore(input.sessionStore),
494
+ ...(isV2
495
+ ? { lifecycle: normalizeRunnerLifecycle(input.lifecycle, cwd) }
496
+ : {}),
285
497
  };
286
498
  const profileDigest = digest(
287
- "chainlesschain.execution-location.profile.v1\0",
499
+ isV2
500
+ ? "chainlesschain.execution-location.profile.v2\0"
501
+ : "chainlesschain.execution-location.profile.v1\0",
288
502
  profile,
289
503
  );
290
504
  if (suppliedDigest != null && suppliedDigest !== profileDigest) {
@@ -367,6 +581,163 @@ function quotePosix(value) {
367
581
  return `'${String(value).replace(/'/gu, `'"'"'`)}'`;
368
582
  }
369
583
 
584
+ function targetLifecycleEnvironment(profile, deps = {}) {
585
+ if (!profile.lifecycle) return Object.freeze([]);
586
+ const now = Number((deps.now || Date.now)());
587
+ const lifecycle = profile.lifecycle;
588
+ (
589
+ deps.assertRunnerLifecycleAuthority ||
590
+ assertExecutionLocationRunnerLeaseAuthority
591
+ )(lifecycle, profile.target, { now: () => now });
592
+ const leaseExpiry = Date.parse(lifecycle.lease.expiresAt);
593
+ const proxyIssuedAt = Date.parse(lifecycle.proxyAuthority.issuedAt);
594
+ const proxyExpiry = Date.parse(lifecycle.proxyAuthority.expiresAt);
595
+ if (
596
+ !Number.isFinite(now) ||
597
+ leaseExpiry <= now ||
598
+ proxyIssuedAt > now + 30_000 ||
599
+ now - proxyIssuedAt > MAX_PROXY_AUTHORITY_AGE_MS ||
600
+ proxyExpiry <= now
601
+ ) {
602
+ throw new Error("execution location lease or proxy authority is stale");
603
+ }
604
+ const values = {
605
+ CC_EXECUTION_LOCATION_BASE_DIR: lifecycle.baseDir.path,
606
+ CC_EXECUTION_LOCATION_CPU_SECONDS: String(lifecycle.resources.cpuSeconds),
607
+ CC_EXECUTION_LOCATION_GENERATION: String(lifecycle.generation),
608
+ CC_EXECUTION_LOCATION_LEASE_GENERATION: String(lifecycle.lease.generation),
609
+ CC_EXECUTION_LOCATION_LEASE_ID: lifecycle.lease.id,
610
+ CC_EXECUTION_LOCATION_LEASE_EXPIRES_AT: lifecycle.lease.expiresAt,
611
+ CC_EXECUTION_LOCATION_MEMORY_BYTES: String(lifecycle.resources.memoryBytes),
612
+ CC_EXECUTION_LOCATION_POST_SESSION_HOOK_DIGEST:
613
+ lifecycle.postSessionHook.digest,
614
+ CC_EXECUTION_LOCATION_POST_SESSION_HOOK_GENERATION: String(
615
+ lifecycle.postSessionHook.generation,
616
+ ),
617
+ CC_EXECUTION_LOCATION_PROXY_AUTHORITY_ID: lifecycle.proxyAuthority.id,
618
+ CC_EXECUTION_LOCATION_PROXY_EXPIRES_AT: lifecycle.proxyAuthority.expiresAt,
619
+ CC_EXECUTION_LOCATION_PROXY_ISSUED_AT: lifecycle.proxyAuthority.issuedAt,
620
+ CC_EXECUTION_LOCATION_PROXY_REVISION: String(
621
+ lifecycle.proxyAuthority.revision,
622
+ ),
623
+ CC_EXECUTION_LOCATION_RUNNER_ID: lifecycle.runnerId,
624
+ CC_EXECUTION_LOCATION_STATE: lifecycle.state,
625
+ };
626
+ return Object.freeze(
627
+ Object.entries(values)
628
+ .sort(([left], [right]) => left.localeCompare(right))
629
+ .map(([key, value]) => `${key}=${value}`),
630
+ );
631
+ }
632
+
633
+ function localTargetEnvironment(profile, lifecycleEnvironment) {
634
+ const inherited = {};
635
+ for (const key of [
636
+ "ComSpec",
637
+ "FORCE_COLOR",
638
+ "LANG",
639
+ "LC_ALL",
640
+ "NO_COLOR",
641
+ "PATH",
642
+ "PATHEXT",
643
+ "SystemRoot",
644
+ "TEMP",
645
+ "TMP",
646
+ "TMPDIR",
647
+ "WINDIR",
648
+ ]) {
649
+ if (typeof process.env[key] === "string") inherited[key] = process.env[key];
650
+ }
651
+ return Object.freeze({
652
+ ...inherited,
653
+ APPDATA: path.join(profile.transport.home, "AppData", "Roaming"),
654
+ // `home` is the target account's home directory, not the application
655
+ // state directory. Pointing CHAINLESSCHAIN_HOME at it makes the
656
+ // owner-only guard (correctly) refuse to repair a broad user home on
657
+ // Windows. Keep all CLI state beneath a dedicated child directory.
658
+ CHAINLESSCHAIN_HOME: path.join(profile.transport.home, ".chainlesschain"),
659
+ CHAINLESSCHAIN_SECURITY_ANCHOR_HOME: profile.transport.securityHome,
660
+ FORCE_COLOR: "0",
661
+ HOME: profile.transport.home,
662
+ LOCALAPPDATA: path.join(profile.transport.home, "AppData", "Local"),
663
+ NO_COLOR: "1",
664
+ USERPROFILE: profile.transport.home,
665
+ ...Object.fromEntries(
666
+ lifecycleEnvironment.map((entry) => {
667
+ const separator = entry.indexOf("=");
668
+ return [entry.slice(0, separator), entry.slice(separator + 1)];
669
+ }),
670
+ ),
671
+ });
672
+ }
673
+
674
+ function isLocalSessionPrepare(cliArgs) {
675
+ return (
676
+ cliArgs[0] === "session" &&
677
+ cliArgs[1] === "location" &&
678
+ cliArgs[2] === "prepare" &&
679
+ typeof cliArgs[3] === "string"
680
+ );
681
+ }
682
+
683
+ /**
684
+ * Materialize the exact durable directories required by a Local replica
685
+ * handoff before launching its restricted Windows target process. The target
686
+ * is deliberately allowed to write only within these target-owned roots, but
687
+ * starting one PowerShell ACL repair per directory under a restricted token
688
+ * can consume the entire bounded target command window. Create and repair
689
+ * the fixed tree in one host-side batch, then let the target verify and use
690
+ * the already-private paths.
691
+ *
692
+ * @param {Object} profile
693
+ * @param {string[]} cliArgs
694
+ * @param {Object} deps
695
+ */
696
+ export function prepareLocalTargetState(profile, cliArgs, deps = {}) {
697
+ if (!isLocalSessionPrepare(cliArgs)) return null;
698
+ const sessionId = safeName(
699
+ cliArgs[3],
700
+ "Local target replica session id",
701
+ 1024,
702
+ );
703
+ const stateHome = path.join(profile.transport.home, ".chainlesschain");
704
+ const antiRollbackDirectory = getSessionAntiRollbackDirectory({
705
+ homeDir: stateHome,
706
+ anchorBase: profile.transport.securityHome,
707
+ });
708
+ const sessionDigest = createHash("sha256").update(sessionId).digest("hex");
709
+ const directories = [
710
+ stateHome,
711
+ path.join(stateHome, "sessions"),
712
+ profile.transport.securityHome,
713
+ path.join(profile.transport.securityHome, "sessions-v1"),
714
+ antiRollbackDirectory,
715
+ path.join(antiRollbackDirectory, "namespace"),
716
+ path.join(antiRollbackDirectory, "records"),
717
+ path.join(antiRollbackDirectory, "records", sessionDigest.slice(0, 2)),
718
+ ];
719
+ const uniqueDirectories = [...new Set(directories)];
720
+ const ensure = deps.ensurePrivateDirectory || ensurePrivateDirectory;
721
+ const repair = deps.repairPrivatePaths || repairPrivatePaths;
722
+ const platform = deps.platform || process.platform;
723
+ for (const directory of uniqueDirectories) {
724
+ ensure(
725
+ directory,
726
+ platform === "win32"
727
+ ? { applyWindowsAcl: false, failIfUnavailable: true }
728
+ : { failIfUnavailable: true },
729
+ );
730
+ }
731
+ if (platform === "win32") {
732
+ repair(uniqueDirectories, { platform: "win32" });
733
+ }
734
+ return Object.freeze({
735
+ stateHome,
736
+ antiRollbackDirectory,
737
+ directories: Object.freeze(uniqueDirectories),
738
+ });
739
+ }
740
+
370
741
  function materializeKnownHostsAuthority(bytes, deps = {}) {
371
742
  const runtimeFs = deps.fs || fs;
372
743
  const root = runtimeFs.mkdtempSync(
@@ -400,7 +771,103 @@ function materializeKnownHostsAuthority(bytes, deps = {}) {
400
771
  });
401
772
  }
402
773
 
774
+ /**
775
+ * A brokered Local target cannot reliably receive a source-side stdin pipe on
776
+ * Windows: the restricted-token helper owns the process boundary. Stage the
777
+ * bounded replica beneath the already owner-only target state tree instead of
778
+ * using the source process temporary directory, which the target is not
779
+ * authorized to read.
780
+ */
781
+ function materializeLocalTargetInput(profile, input, deps = {}) {
782
+ const bytes = Buffer.isBuffer(input) ? input : Buffer.from(input || "");
783
+ if (bytes.length === 0 || bytes.length > MAX_REPLICA_BYTES) {
784
+ throw new Error("local target input is empty or exceeds its byte limit");
785
+ }
786
+ const runtimeFs = deps.fs || fs;
787
+ const root = runtimeFs.mkdtempSync(
788
+ path.join(profile.transport.home, ".chainlesschain", "target-input-"),
789
+ );
790
+ const inputPath = path.join(root, "stdin.bin");
791
+ let descriptor;
792
+ try {
793
+ descriptor = runtimeFs.openSync(
794
+ inputPath,
795
+ runtimeFs.constants.O_CREAT |
796
+ runtimeFs.constants.O_EXCL |
797
+ runtimeFs.constants.O_WRONLY,
798
+ 0o600,
799
+ );
800
+ runtimeFs.writeFileSync(descriptor, bytes);
801
+ runtimeFs.fsyncSync(descriptor);
802
+ } catch (error) {
803
+ try {
804
+ runtimeFs.rmSync(root, { recursive: true, force: true });
805
+ } catch {
806
+ // Preserve the original materialization failure.
807
+ }
808
+ throw error;
809
+ } finally {
810
+ if (descriptor !== undefined) runtimeFs.closeSync(descriptor);
811
+ }
812
+ return Object.freeze({
813
+ path: inputPath,
814
+ cleanup: () => runtimeFs.rmSync(root, { recursive: true, force: true }),
815
+ });
816
+ }
817
+
403
818
  function targetInvocation(profile, cliArgs, deps = {}, options = {}) {
819
+ const lifecycleEnvironment = targetLifecycleEnvironment(profile, deps);
820
+ if (profile.target === "local") {
821
+ if (!path.isAbsolute(profile.cliCommand)) {
822
+ throw new Error("Local target CLI entry must be an absolute path");
823
+ }
824
+ if (!profile.lifecycle) {
825
+ throw new Error("Local target launch requires a lifecycle profile");
826
+ }
827
+ if (isLocalSessionPrepare(cliArgs)) {
828
+ // Injected spawnSync calls model a target transport in unit tests. They
829
+ // must not mutate a caller's host filesystem merely to exercise argv
830
+ // validation; production always takes the authenticated bootstrap path.
831
+ const bootstrap =
832
+ deps.prepareLocalTargetState ||
833
+ (deps.spawnSync ? null : prepareLocalTargetState);
834
+ bootstrap?.(profile, cliArgs, deps);
835
+ }
836
+ if (options.input != null && !isLocalSessionPrepare(cliArgs)) {
837
+ throw new Error(
838
+ "local target input is only supported for session prepare",
839
+ );
840
+ }
841
+ const inputAuthority =
842
+ options.input == null
843
+ ? null
844
+ : materializeLocalTargetInput(profile, options.input, deps);
845
+ return Object.freeze({
846
+ file: deps.nodeCommand || process.execPath,
847
+ args: Object.freeze([
848
+ LOCAL_TARGET_SUPERVISOR,
849
+ "--cwd",
850
+ profile.cwd,
851
+ "--cpu-seconds",
852
+ String(profile.lifecycle.resources.cpuSeconds),
853
+ "--memory-bytes",
854
+ String(profile.lifecycle.resources.memoryBytes),
855
+ "--entry",
856
+ profile.cliCommand,
857
+ ...(inputAuthority === null
858
+ ? []
859
+ : ["--stdin-file", inputAuthority.path]),
860
+ "--",
861
+ ...cliArgs,
862
+ ]),
863
+ spawnOptions: Object.freeze({
864
+ cwd: profile.cwd,
865
+ env: localTargetEnvironment(profile, lifecycleEnvironment),
866
+ }),
867
+ inputHandled: inputAuthority !== null,
868
+ cleanup: inputAuthority?.cleanup || null,
869
+ });
870
+ }
404
871
  if (profile.target === "wsl") {
405
872
  if ((deps.platform || process.platform) !== "win32") {
406
873
  throw new Error("WSL target launch requires a Windows host");
@@ -413,6 +880,9 @@ function targetInvocation(profile, cliArgs, deps = {}, options = {}) {
413
880
  "--cd",
414
881
  profile.cwd,
415
882
  "--exec",
883
+ ...(lifecycleEnvironment.length > 0
884
+ ? ["env", ...lifecycleEnvironment]
885
+ : []),
416
886
  profile.cliCommand,
417
887
  ...cliArgs,
418
888
  ]),
@@ -427,6 +897,7 @@ function targetInvocation(profile, cliArgs, deps = {}, options = {}) {
427
897
  ...(options.input == null ? [] : ["-i"]),
428
898
  "--workdir",
429
899
  profile.cwd,
900
+ ...lifecycleEnvironment.flatMap((entry) => ["--env", entry]),
430
901
  profile.transport.container,
431
902
  profile.cliCommand,
432
903
  ...cliArgs,
@@ -473,7 +944,13 @@ function targetInvocation(profile, cliArgs, deps = {}, options = {}) {
473
944
  : transport.host;
474
945
  args.push(
475
946
  target,
476
- `cd ${quotePosix(profile.cwd)} && ${[profile.cliCommand, ...cliArgs]
947
+ `cd ${quotePosix(profile.cwd)} && ${[
948
+ ...(lifecycleEnvironment.length > 0
949
+ ? ["env", ...lifecycleEnvironment]
950
+ : []),
951
+ profile.cliCommand,
952
+ ...cliArgs,
953
+ ]
477
954
  .map(quotePosix)
478
955
  .join(" ")}`,
479
956
  );
@@ -499,18 +976,37 @@ function runTargetCommand(profile, cliArgs, deps = {}, options = {}) {
499
976
  ) {
500
977
  throw new Error("target command output boundary is invalid");
501
978
  }
979
+ const timeoutMs = Number(
980
+ options.timeoutMs ?? DEFAULT_TARGET_COMMAND_TIMEOUT_MS,
981
+ );
982
+ if (
983
+ !Number.isSafeInteger(timeoutMs) ||
984
+ timeoutMs < 1 ||
985
+ timeoutMs > MAX_TARGET_COMMAND_TIMEOUT_MS
986
+ ) {
987
+ throw new Error("target command timeout boundary is invalid");
988
+ }
502
989
  const result = spawnSync(invocation.file, invocation.args, {
503
990
  origin: "execution-location:target",
504
991
  scope: "execution-location",
505
992
  policy: "allow",
506
993
  shell: false,
507
994
  encoding: "utf8",
508
- timeout: options.interactive ? undefined : 30000,
995
+ timeout: options.interactive ? undefined : timeoutMs,
509
996
  maxBuffer: options.interactive ? undefined : maxBuffer,
510
- ...(options.input == null ? {} : { input: options.input }),
997
+ ...(options.input == null || invocation.inputHandled
998
+ ? {}
999
+ : { input: options.input }),
511
1000
  stdio: options.interactive
512
1001
  ? "inherit"
513
- : [options.input == null ? "ignore" : "pipe", "pipe", "pipe"],
1002
+ : [
1003
+ options.input == null || invocation.inputHandled
1004
+ ? "ignore"
1005
+ : "pipe",
1006
+ "pipe",
1007
+ "pipe",
1008
+ ],
1009
+ ...(invocation.spawnOptions || {}),
514
1010
  });
515
1011
  if (result?.error) throw result.error;
516
1012
  if (!result || result.status !== 0) {
@@ -548,6 +1044,266 @@ function parseTargetProjection(raw, label) {
548
1044
  return projection;
549
1045
  }
550
1046
 
1047
+ function preflightExecutionLocationTarget(profile, deps = {}) {
1048
+ if (!profile.lifecycle) return null;
1049
+ const raw = runTargetCommand(
1050
+ profile,
1051
+ ["session", "location", "target-preflight", "--json"],
1052
+ deps,
1053
+ );
1054
+ const receipt = exactObject(
1055
+ parseTargetProjection(raw, "target lifecycle preflight"),
1056
+ [
1057
+ "schema",
1058
+ "runnerId",
1059
+ "state",
1060
+ "generation",
1061
+ "lease",
1062
+ "proxyAuthority",
1063
+ "baseDir",
1064
+ "resources",
1065
+ "postSessionHook",
1066
+ "secretTransferCount",
1067
+ "receiptDigest",
1068
+ ],
1069
+ "target lifecycle preflight",
1070
+ );
1071
+ const lifecycle = profile.lifecycle;
1072
+ const reportedEnforcement = receipt.resources?.enforcement;
1073
+ const expectedEnforcement =
1074
+ profile.target === "local"
1075
+ ? "target-supervisor"
1076
+ : reportedEnforcement === "posix-rlimit" ||
1077
+ reportedEnforcement === "target-supervisor"
1078
+ ? reportedEnforcement
1079
+ : "posix-rlimit";
1080
+ const material = {
1081
+ schema: "cc-execution-location-target-preflight/v1",
1082
+ runnerId: lifecycle.runnerId,
1083
+ state: lifecycle.state,
1084
+ generation: lifecycle.generation,
1085
+ lease: {
1086
+ id: lifecycle.lease.id,
1087
+ generation: lifecycle.lease.generation,
1088
+ expiresAt: lifecycle.lease.expiresAt,
1089
+ },
1090
+ proxyAuthority: {
1091
+ id: lifecycle.proxyAuthority.id,
1092
+ revision: lifecycle.proxyAuthority.revision,
1093
+ issuedAt: lifecycle.proxyAuthority.issuedAt,
1094
+ expiresAt: lifecycle.proxyAuthority.expiresAt,
1095
+ },
1096
+ baseDir: {
1097
+ digest: digest(
1098
+ "chainlesschain.execution-location.base-dir.v1\0",
1099
+ lifecycle.baseDir.path,
1100
+ ),
1101
+ writable: true,
1102
+ },
1103
+ resources: {
1104
+ cpuSeconds: lifecycle.resources.cpuSeconds,
1105
+ memoryBytes: lifecycle.resources.memoryBytes,
1106
+ observedCpuSeconds: Number(receipt.resources?.observedCpuSeconds),
1107
+ observedMemoryBytes: Number(receipt.resources?.observedMemoryBytes),
1108
+ targetEnforced: true,
1109
+ enforcement: expectedEnforcement,
1110
+ },
1111
+ postSessionHook: {
1112
+ digest: lifecycle.postSessionHook.digest,
1113
+ generation: lifecycle.postSessionHook.generation,
1114
+ },
1115
+ secretTransferCount: 0,
1116
+ };
1117
+ const expectedReceiptDigest = digest(
1118
+ "chainlesschain.execution-location.target-preflight.v1\0",
1119
+ material,
1120
+ );
1121
+ if (
1122
+ canonicalJson(receipt, "targetLifecyclePreflight") !==
1123
+ canonicalJson(
1124
+ { ...material, receiptDigest: expectedReceiptDigest },
1125
+ "expectedTargetLifecyclePreflight",
1126
+ ) ||
1127
+ material.resources.observedCpuSeconds < 1 ||
1128
+ material.resources.observedCpuSeconds > lifecycle.resources.cpuSeconds ||
1129
+ material.resources.observedMemoryBytes < 1 ||
1130
+ material.resources.observedMemoryBytes > lifecycle.resources.memoryBytes
1131
+ ) {
1132
+ throw new Error("target lifecycle preflight does not match the profile");
1133
+ }
1134
+ return Object.freeze(receipt);
1135
+ }
1136
+
1137
+ export function probeExecutionLocationTargetPreflight(input = {}, deps = {}) {
1138
+ const profile = normalizeExecutionLocationProfile(input.profile);
1139
+ if (!profile.lifecycle) {
1140
+ throw new TypeError("target preflight requires a v2 lifecycle profile");
1141
+ }
1142
+ return preflightExecutionLocationTarget(profile, deps);
1143
+ }
1144
+
1145
+ export function probeExecutionLocationTargetResourceLimit(
1146
+ input = {},
1147
+ deps = {},
1148
+ ) {
1149
+ const profile = normalizeExecutionLocationProfile(input.profile);
1150
+ if (!profile.lifecycle) {
1151
+ throw new TypeError(
1152
+ "resource enforcement probe requires a v2 lifecycle profile",
1153
+ );
1154
+ }
1155
+ const kind = input.kind;
1156
+ if (kind !== "cpu" && kind !== "memory") {
1157
+ throw new TypeError(
1158
+ "resource enforcement probe kind must be cpu or memory",
1159
+ );
1160
+ }
1161
+ const preflight = preflightExecutionLocationTarget(profile, deps);
1162
+ const invocation = targetInvocation(
1163
+ profile,
1164
+ ["session", "location", "resource-probe", kind],
1165
+ deps,
1166
+ );
1167
+ const spawnSync =
1168
+ deps.spawnSync ||
1169
+ ((file, args, spawnOptions) =>
1170
+ executionBroker.spawnSync(file, args, spawnOptions));
1171
+ let result;
1172
+ try {
1173
+ result = spawnSync(invocation.file, invocation.args, {
1174
+ origin: "execution-location:target-resource-probe",
1175
+ scope: "execution-location",
1176
+ policy: "allow",
1177
+ shell: false,
1178
+ encoding: "utf8",
1179
+ timeout: Math.min(
1180
+ 60_000,
1181
+ (profile.lifecycle.resources.cpuSeconds + 30) * 1000,
1182
+ ),
1183
+ maxBuffer: MAX_PROBE_BYTES,
1184
+ stdio: ["ignore", "pipe", "pipe"],
1185
+ ...(invocation.spawnOptions || {}),
1186
+ });
1187
+ } finally {
1188
+ invocation.cleanup?.();
1189
+ }
1190
+ if (result?.error) throw result.error;
1191
+ const expectedMarker = `CC_EXECUTION_LOCATION_RESOURCE_PROBE_ARMED:${kind}`;
1192
+ const status = Number(result?.status);
1193
+ const signal = result?.signal == null ? null : String(result.signal);
1194
+ const signalTerminated = new Set(["SIGABRT", "SIGKILL", "SIGXCPU"]).has(
1195
+ signal,
1196
+ );
1197
+ const exitTerminated = [134, 137, 152].includes(status);
1198
+ if (
1199
+ !result ||
1200
+ String(result.stdout || "").trim() !== expectedMarker ||
1201
+ (!signalTerminated && !exitTerminated)
1202
+ ) {
1203
+ throw new Error("target workload did not terminate at its resource limit");
1204
+ }
1205
+ const material = {
1206
+ schema: "cc-execution-location-target-resource-enforcement/v1",
1207
+ target: profile.target,
1208
+ kind,
1209
+ profileDigest: profile.profileDigest,
1210
+ preflightReceiptDigest: preflight.receiptDigest,
1211
+ runnerId: profile.lifecycle.runnerId,
1212
+ runnerGeneration: profile.lifecycle.generation,
1213
+ leaseId: profile.lifecycle.lease.id,
1214
+ leaseGeneration: profile.lifecycle.lease.generation,
1215
+ limit:
1216
+ kind === "cpu"
1217
+ ? profile.lifecycle.resources.cpuSeconds
1218
+ : profile.lifecycle.resources.memoryBytes,
1219
+ enforcementScope: "target-workload",
1220
+ termination: signalTerminated
1221
+ ? { kind: "signal", value: signal }
1222
+ : { kind: "exit-status", value: status },
1223
+ secretTransferCount: 0,
1224
+ };
1225
+ return Object.freeze({
1226
+ ...material,
1227
+ receiptDigest: digest(
1228
+ "chainlesschain.execution-location.target-resource-enforcement.v1\0",
1229
+ material,
1230
+ ),
1231
+ });
1232
+ }
1233
+
1234
+ export function probeExecutionLocationTargetSigtermDrain(
1235
+ input = {},
1236
+ deps = {},
1237
+ ) {
1238
+ const profile = normalizeExecutionLocationProfile(input.profile);
1239
+ if (!profile.lifecycle || profile.lifecycle.state !== "accepting") {
1240
+ throw new TypeError("SIGTERM drain probe requires an accepting v2 profile");
1241
+ }
1242
+ const preflight = preflightExecutionLocationTarget(profile, deps);
1243
+ const raw = runTargetCommand(
1244
+ profile,
1245
+ ["session", "location", "sigterm-drain-probe", "--json"],
1246
+ deps,
1247
+ );
1248
+ const receipt = exactObject(
1249
+ parseTargetProjection(raw, "target SIGTERM drain probe"),
1250
+ [
1251
+ "schema",
1252
+ "runnerId",
1253
+ "signal",
1254
+ "before",
1255
+ "after",
1256
+ "lease",
1257
+ "preflightReceiptDigest",
1258
+ "signalDeliveryCount",
1259
+ "postSignalLeaseAcceptanceCount",
1260
+ "secretTransferCount",
1261
+ "receiptDigest",
1262
+ ],
1263
+ "target SIGTERM drain probe",
1264
+ );
1265
+ const lifecycle = profile.lifecycle;
1266
+ const material = {
1267
+ schema: "cc-execution-location-target-sigterm-drain/v1",
1268
+ runnerId: lifecycle.runnerId,
1269
+ signal: "SIGTERM",
1270
+ before: {
1271
+ state: "accepting",
1272
+ generation: lifecycle.generation,
1273
+ accepting: true,
1274
+ },
1275
+ after: {
1276
+ state: "draining",
1277
+ generation: lifecycle.generation + 1,
1278
+ accepting: false,
1279
+ },
1280
+ lease: {
1281
+ id: lifecycle.lease.id,
1282
+ generation: lifecycle.lease.generation,
1283
+ expiresAt: lifecycle.lease.expiresAt,
1284
+ continued: true,
1285
+ },
1286
+ preflightReceiptDigest: preflight.receiptDigest,
1287
+ signalDeliveryCount: 1,
1288
+ postSignalLeaseAcceptanceCount: 0,
1289
+ secretTransferCount: 0,
1290
+ };
1291
+ const expected = {
1292
+ ...material,
1293
+ receiptDigest: digest(
1294
+ "chainlesschain.execution-location.target-sigterm-drain.v1\0",
1295
+ material,
1296
+ ),
1297
+ };
1298
+ if (
1299
+ canonicalJson(receipt, "targetSigtermDrain") !==
1300
+ canonicalJson(expected, "expectedTargetSigtermDrain")
1301
+ ) {
1302
+ throw new Error("target SIGTERM drain receipt does not match the profile");
1303
+ }
1304
+ return Object.freeze(receipt);
1305
+ }
1306
+
551
1307
  function validateProfileHandoff(profile, handoff) {
552
1308
  if (!handoff?.allowed) {
553
1309
  throw new Error("execution location handoff is not allowed");
@@ -605,6 +1361,7 @@ function validateCurrentProjection(profile, rawProjection) {
605
1361
  export function attestExecutionLocationTarget(input = {}, deps = {}) {
606
1362
  const profile = normalizeExecutionLocationProfile(input.profile);
607
1363
  validateProfileHandoff(profile, input.handoff);
1364
+ const lifecyclePreflight = preflightExecutionLocationTarget(profile, deps);
608
1365
  const raw = runTargetCommand(
609
1366
  profile,
610
1367
  ["session", "location", "current", "--json"],
@@ -612,7 +1369,7 @@ export function attestExecutionLocationTarget(input = {}, deps = {}) {
612
1369
  );
613
1370
  const current = parseTargetProjection(raw, "target current-location probe");
614
1371
  const binding = validateCurrentProjection(profile, current);
615
- return createExecutionLocationTargetAttestation({
1372
+ const attestation = createExecutionLocationTargetAttestation({
616
1373
  profileDigest: profile.profileDigest,
617
1374
  sourceSessionId: input.handoff.session.sessionId,
618
1375
  sourceHeadHash: input.handoff.session.headHash,
@@ -621,6 +1378,19 @@ export function attestExecutionLocationTarget(input = {}, deps = {}) {
621
1378
  baseCommit: input.handoff.transfer.git.baseCommit,
622
1379
  binding,
623
1380
  });
1381
+ if (!lifecyclePreflight) return attestation;
1382
+ return Object.freeze({
1383
+ ...attestation,
1384
+ lifecyclePreflight,
1385
+ lifecycleAttestationDigest: digest(
1386
+ "chainlesschain.execution-location.lifecycle-attestation.v1\0",
1387
+ {
1388
+ attestationDigest: attestation.attestationDigest,
1389
+ preflightReceiptDigest: lifecyclePreflight.receiptDigest,
1390
+ profileDigest: profile.profileDigest,
1391
+ },
1392
+ ),
1393
+ });
624
1394
  }
625
1395
 
626
1396
  function verifyTargetSessionStore(
@@ -755,7 +1525,7 @@ function transferTargetSessionStore(
755
1525
  "--json",
756
1526
  ],
757
1527
  deps,
758
- { input: transcriptBytes },
1528
+ { input: transcriptBytes, timeoutMs: REPLICA_TRANSFER_TIMEOUT_MS },
759
1529
  );
760
1530
  const receipt = parseTargetProjection(
761
1531
  raw,