chainlesschain 0.166.77 → 0.166.79

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 (230) hide show
  1. package/README.md +9 -8
  2. package/completions/_cc +1 -1
  3. package/completions/cc.bash +1 -1
  4. package/completions/cc.fish +1 -1
  5. package/completions/cc.ps1 +1 -1
  6. package/package.json +1 -1
  7. package/src/assets/linux-subreaper/linux-arm64/manifest.json +10 -0
  8. package/src/assets/linux-subreaper/linux-arm64/supervisor +0 -0
  9. package/src/assets/linux-subreaper/linux-x64/manifest.json +10 -0
  10. package/src/assets/linux-subreaper/linux-x64/supervisor +0 -0
  11. package/src/assets/web-panel/assets/{AIOps-CpiCBhRh.js → AIOps-CwD6Pm_Y.js} +1 -1
  12. package/src/assets/web-panel/assets/{ActionButton-CnU-Xvx-.js → ActionButton-Du5Rp-O6.js} +1 -1
  13. package/src/assets/web-panel/assets/{Analytics-BJW-CNtf.js → Analytics-DY9l72ma.js} +3 -3
  14. package/src/assets/web-panel/assets/{AppLayout-UFQJL3G9.js → AppLayout-7t1WIaCJ.js} +5 -5
  15. package/src/assets/web-panel/assets/{Artifacts-Dr_IUDoY.js → Artifacts-BLIA5yny.js} +1 -1
  16. package/src/assets/web-panel/assets/{Audit-aglWo6rr.js → Audit-BC_N0QTg.js} +1 -1
  17. package/src/assets/web-panel/assets/{BackgroundAgents-Dm_TCiXC.js → BackgroundAgents-CA7SxDtg.js} +1 -1
  18. package/src/assets/web-panel/assets/{Backup-DUdX4-gA.js → Backup-BZ4AzS1_.js} +1 -1
  19. package/src/assets/web-panel/assets/{BaseInput-BTcLjy1o.js → BaseInput-BhiggGaH.js} +1 -1
  20. package/src/assets/web-panel/assets/{Chat-Bhy9qZrl.js → Chat-CDdBoIBI.js} +5 -5
  21. package/src/assets/web-panel/assets/ChatBubbleRenderer-hsBsrl31.js +1 -0
  22. package/src/assets/web-panel/assets/{Checkbox-CblrPa21.js → Checkbox-asoMKSEK.js} +1 -1
  23. package/src/assets/web-panel/assets/{Codegen-SEAHzOlr.js → Codegen-B0gElLdm.js} +1 -1
  24. package/src/assets/web-panel/assets/{Col-DJz8iFOR.js → Col-DrNzLpF6.js} +1 -1
  25. package/src/assets/web-panel/assets/{Community-RufKhcQt.js → Community-DCzB5qkI.js} +1 -1
  26. package/src/assets/web-panel/assets/{Compact-Dlifx08U.js → Compact-Db-Eua_j.js} +1 -1
  27. package/src/assets/web-panel/assets/{Compliance-DdhQWcHh.js → Compliance-B76NByjl.js} +1 -1
  28. package/src/assets/web-panel/assets/{Cowork-BLNn6oOm.js → Cowork-C-nK6Qpk.js} +3 -3
  29. package/src/assets/web-panel/assets/{Cron-Dv-JWY2d.js → Cron-C-GlQO19.js} +2 -2
  30. package/src/assets/web-panel/assets/{Crosschain-k7Pz7O-b.js → Crosschain-Cuuz9IIh.js} +1 -1
  31. package/src/assets/web-panel/assets/{DID-C_10syUW.js → DID-gwFaT973.js} +2 -2
  32. package/src/assets/web-panel/assets/{Dashboard-CMyH6Szx.js → Dashboard-DHDt5jEf.js} +2 -2
  33. package/src/assets/web-panel/assets/{Dropdown-DjmD2Saa.js → Dropdown-CJ9kog-Y.js} +1 -1
  34. package/src/assets/web-panel/assets/{EmailListRenderer-36_IRUSo.js → EmailListRenderer-DOSXFRiQ.js} +1 -1
  35. package/src/assets/web-panel/assets/{EvolutionSettings-CmtU0XmM.js → EvolutionSettings-CHYMP7rR.js} +1 -1
  36. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DEgf9kyP.js → FamilyGuardDashboard-C1gzG5EK.js} +1 -1
  37. package/src/assets/web-panel/assets/{Federation-DGO1wQls.js → Federation-CEdNVWUw.js} +1 -1
  38. package/src/assets/web-panel/assets/{FormItemContext-Doj1FmKW.js → FormItemContext-CwVOWPX8.js} +1 -1
  39. package/src/assets/web-panel/assets/{GenericCardRenderer-GzOU27iT.js → GenericCardRenderer-CgNUs3kT.js} +1 -1
  40. package/src/assets/web-panel/assets/{Git-Dd7D1EDK.js → Git-DM4MNnQo.js} +2 -2
  41. package/src/assets/web-panel/assets/{Governance-6IY8LcjI.js → Governance-DdOtjkvd.js} +1 -1
  42. package/src/assets/web-panel/assets/{Inference-BB9gl8q1.js → Inference-28PidFfr.js} +1 -1
  43. package/src/assets/web-panel/assets/{KnowledgeGraph-CeCed5wd.js → KnowledgeGraph-Bpj_qNUK.js} +1 -1
  44. package/src/assets/web-panel/assets/{Logs-CYOeHuaI.js → Logs-C6Ix19Ay.js} +2 -2
  45. package/src/assets/web-panel/assets/{MarkdownRenderer-Bk8Zqc2R.js → MarkdownRenderer-Bj6mtPbf.js} +1 -1
  46. package/src/assets/web-panel/assets/{Marketplace-D-_xgHgM.js → Marketplace-DqkhRSMP.js} +1 -1
  47. package/src/assets/web-panel/assets/{McpTools-NnaUTF5t.js → McpTools-1WQ2SjaE.js} +3 -3
  48. package/src/assets/web-panel/assets/{Memory-CndN9pMt.js → Memory-BWTi7v6R.js} +2 -2
  49. package/src/assets/web-panel/assets/{MobileBridge-BXblFmhY.js → MobileBridge-DB4wu3UE.js} +1 -1
  50. package/src/assets/web-panel/assets/{MobileProjects-CC26Dm2k.js → MobileProjects-Bh9pWx2f.js} +1 -1
  51. package/src/assets/web-panel/assets/{Mtc-BhaqWCiW.js → Mtc-BVF1Wrno.js} +4 -4
  52. package/src/assets/web-panel/assets/{MtcAudit-D6fktM2w.js → MtcAudit-DtNfslPf.js} +5 -5
  53. package/src/assets/web-panel/assets/{Multisig-CFvahz7s.js → Multisig-4qcXP0bf.js} +3 -3
  54. package/src/assets/web-panel/assets/{NLProgramming-Co73Ivya.js → NLProgramming-BRJl5Ehw.js} +1 -1
  55. package/src/assets/web-panel/assets/{Notes-B4g33L1o.js → Notes-BRY_bgMk.js} +3 -3
  56. package/src/assets/web-panel/assets/{NotificationSettings-lXd17EbN.js → NotificationSettings-DirXMFz4.js} +1 -1
  57. package/src/assets/web-panel/assets/{OrderTableRenderer-BiOB0GIi.js → OrderTableRenderer-BlrY8lwu.js} +1 -1
  58. package/src/assets/web-panel/assets/{Organization-JQ7_wQIA.js → Organization-CVzQ1vO-.js} +4 -4
  59. package/src/assets/web-panel/assets/{Overflow-gwgJgozQ.js → Overflow-A3rXTPG4.js} +1 -1
  60. package/src/assets/web-panel/assets/{P2P-D9L_0gie.js → P2P-Db9ABWz0.js} +2 -2
  61. package/src/assets/web-panel/assets/{PdhVaultBrowser-CbOPsFE9.js → PdhVaultBrowser-CPv0H2OZ.js} +3 -3
  62. package/src/assets/web-panel/assets/{Permissions-Cz42HNQf.js → Permissions-COnW4sIR.js} +4 -4
  63. package/src/assets/web-panel/assets/{PersonalDataHub-DjorYtrF.js → PersonalDataHub-C1hG7rp7.js} +5 -5
  64. package/src/assets/web-panel/assets/{Pipeline-DyWPhmm5.js → Pipeline---GLRDEe.js} +1 -1
  65. package/src/assets/web-panel/assets/{Privacy-DyfLMl2j.js → Privacy-aaN9R3Lu.js} +1 -1
  66. package/src/assets/web-panel/assets/{ProjectInit-CyqDx1OE.js → ProjectInit-C-kw49hE.js} +2 -2
  67. package/src/assets/web-panel/assets/{ProjectSettings-DsBw0Csy.js → ProjectSettings-y5tTkBTC.js} +2 -2
  68. package/src/assets/web-panel/assets/Projects-DZHlhyPB.js +1 -0
  69. package/src/assets/web-panel/assets/{Providers-CCI_klN-.js → Providers-LqxnV72o.js} +1 -1
  70. package/src/assets/web-panel/assets/{QrScannerModal-DNsnlDdS.js → QrScannerModal-D_q-Y5F0.js} +1 -1
  71. package/src/assets/web-panel/assets/{QuickAsk-B9r2iaee.js → QuickAsk-_E_KB5o8.js} +1 -1
  72. package/src/assets/web-panel/assets/{Recommend-BJDe03GR.js → Recommend-BN8S5WJA.js} +1 -1
  73. package/src/assets/web-panel/assets/{RemoteSession-J7JR3rRo.js → RemoteSession-BFzrY_aW.js} +3 -3
  74. package/src/assets/web-panel/assets/{Reputation-DR39AJeV.js → Reputation-C1HCLaTV.js} +1 -1
  75. package/src/assets/web-panel/assets/{Row-Dh77m3NZ.js → Row-B8T2-Qv7.js} +1 -1
  76. package/src/assets/web-panel/assets/{RssFeed-BUdg28nJ.js → RssFeed-CNQLk8v1.js} +3 -3
  77. package/src/assets/web-panel/assets/{Search-BNC7sGhj.js → Search-BOx720Kc.js} +1 -1
  78. package/src/assets/web-panel/assets/{Security-DISENGI7.js → Security-Ds8z6PF6.js} +4 -4
  79. package/src/assets/web-panel/assets/{Services-BBBGpJ1-.js → Services-C0-YkRSF.js} +2 -2
  80. package/src/assets/web-panel/assets/{Skeleton-DMcIoIv2.js → Skeleton-DUHlSySD.js} +1 -1
  81. package/src/assets/web-panel/assets/{Skills-QAyFbI3r.js → Skills-C8CBbbh0.js} +1 -1
  82. package/src/assets/web-panel/assets/{Sla-CSg5wLH7.js → Sla-lQltyYfB.js} +1 -1
  83. package/src/assets/web-panel/assets/{SpeechSettings-DMPxTc5x.js → SpeechSettings-B5vyOma-.js} +1 -1
  84. package/src/assets/web-panel/assets/{SyncSettings-CPG7l30y.js → SyncSettings-BKDPu_y-.js} +2 -2
  85. package/src/assets/web-panel/assets/{Tasks-Y1kOVvVL.js → Tasks-Bgvf7LTx.js} +1 -1
  86. package/src/assets/web-panel/assets/{Templates-jt9NagL0.js → Templates-BMZyYYS7.js} +1 -1
  87. package/src/assets/web-panel/assets/{Tenant-DauuJICx.js → Tenant-7PYTig8f.js} +1 -1
  88. package/src/assets/web-panel/assets/{Terminal-E9rnKeGq.js → Terminal-C2W0LMdR.js} +2 -2
  89. package/src/assets/web-panel/assets/{TimelineRenderer-Cc7z2APG.js → TimelineRenderer-CqREew_l.js} +1 -1
  90. package/src/assets/web-panel/assets/{Tokens-CV0UthRA.js → Tokens-BhTyxTuD.js} +1 -1
  91. package/src/assets/web-panel/assets/{Trigger-BFcJJGfu.js → Trigger-DxNSSZy_.js} +1 -1
  92. package/src/assets/web-panel/assets/{Trust-BAydbNJA.js → Trust-Dy_wfbIh.js} +1 -1
  93. package/src/assets/web-panel/assets/{UkeySign-DcOS6sqL.js → UkeySign-DJC5ANqx.js} +1 -1
  94. package/src/assets/web-panel/assets/{VideoEditing-BLHJZvif.js → VideoEditing-CNFevrO4.js} +1 -1
  95. package/src/assets/web-panel/assets/{Wallet-4QFLKHwW.js → Wallet-CXzuS1YK.js} +4 -4
  96. package/src/assets/web-panel/assets/{WebAuthn-BocEOOSj.js → WebAuthn-BWS6HP8c.js} +5 -5
  97. package/src/assets/web-panel/assets/{WorkflowEditor-DCfF7D0B.js → WorkflowEditor-swiN_7Vj.js} +1 -1
  98. package/src/assets/web-panel/assets/{chat-CxvfsYGr.js → chat-NwH7qlBs.js} +1 -1
  99. package/src/assets/web-panel/assets/{colors-KtQB9DmQ.js → colors-h6nRhBTw.js} +1 -1
  100. package/src/assets/web-panel/assets/{compact-item-Dx4J6arN.js → compact-item-BQ6B6rP2.js} +1 -1
  101. package/src/assets/web-panel/assets/{createContext-Cq0olgzo.js → createContext-CQkJuRcG.js} +1 -1
  102. package/src/assets/web-panel/assets/devWarning-Bz6vqBGV.js +1 -0
  103. package/src/assets/web-panel/assets/{hasIn-Bf9-h3D-.js → hasIn-Diwl1WyI.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CpO6DKuu.js → index-0ZztPzIm.js} +1 -1
  105. package/src/assets/web-panel/assets/index-1b7DEw-w.js +1 -0
  106. package/src/assets/web-panel/assets/{index-DQDR-_ai.js → index-AzeSQJDK.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BMcC4cbj.js → index-B3QgcUBV.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BO6XvO-C.js → index-B5gj_uCC.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-ClUfSChI.js → index-BI3ALCgN.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B3xfiItB.js → index-BRl3KUj0.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DFdEDvFn.js → index-BYYb8oeV.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CqnggDos.js → index-BcKDUGYf.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B_8UcDan.js → index-BcjlIMP9.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CmQuG0fr.js → index-BheCvudN.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-iE9tqEN5.js → index-BjcARUtr.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-nmyDOh_b.js → index-BnQUin2K.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Br1mDMfO.js → index-Bs6HyCfP.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DZWjYfWt.js → index-BvPz6vZ8.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-0fi8DIVQ.js → index-C3Ye1ISy.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-hSzyoXri.js → index-C7-T-u0L.js} +3 -3
  121. package/src/assets/web-panel/assets/{index-DfrFNMvz.js → index-CRWpixu2.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C9yaUHm9.js → index-CatBQDSQ.js} +1 -1
  123. package/src/assets/web-panel/assets/index-CfD4tMuW.js +1 -0
  124. package/src/assets/web-panel/assets/{index-DMR-keCL.js → index-CqH-oOR3.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Cy865Hhq.js → index-CwsoVDi-.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DI5bRqNG.js → index-CyBtEVLM.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-gt5N6VM_.js → index-D5P498Rk.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-6dDDSCHD.js → index-DKaB4xTf.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BKtQyb2o.js → index-DQYZa1Ci.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CaIclw-E.js → index-DZ9CNcbJ.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-_FIz-0tp.js → index-DZAsByy4.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DBRlZdZj.js → index-D_HY0nyN.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DsCdl2Od.js → index-Da8frO_A.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Cfsn7N18.js → index-DjB8IC9D.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CeYP_kT9.js → index-Dwpthe2b.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-B8WuPI-h.js → index-Fm95dO4u.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-CK2U_AHi.js → index-GTefQSbY.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-DnU-dUWd.js → index-OEWSrqit.js} +1 -1
  139. package/src/assets/web-panel/assets/{index-DDAe1UmZ.js → index-TYMmUsxZ.js} +1 -1
  140. package/src/assets/web-panel/assets/{index-D2BfSIrn.js → index-iHNOr_wL.js} +1 -1
  141. package/src/assets/web-panel/assets/{index-Cz7f4Jaq.js → index-mxxZtsHp.js} +1 -1
  142. package/src/assets/web-panel/assets/{index-BHmdMKYl.js → index-pYwYBVeQ.js} +1 -1
  143. package/src/assets/web-panel/assets/{initDefaultProps-DvzKJ6o5.js → initDefaultProps-BeUrpC_H.js} +1 -1
  144. package/src/assets/web-panel/assets/{motion-D1yTvmxW.js → motion-CHW5EXt1.js} +1 -1
  145. package/src/assets/web-panel/assets/{move-DxtSIF8K.js → move-CWaoWJvF.js} +1 -1
  146. package/src/assets/web-panel/assets/{mtc-parser-BDsQgIHY.js → mtc-parser-CEC6Us1u.js} +1 -1
  147. package/src/assets/web-panel/assets/{omit-UVBJW8kh.js → omit-1D-kP_Qj.js} +1 -1
  148. package/src/assets/web-panel/assets/{pickAttrs-D4QqtTyX.js → pickAttrs-CsS0myLl.js} +1 -1
  149. package/src/assets/web-panel/assets/{placementArrow-QXmu6vBf.js → placementArrow-bMThOvkI.js} +1 -1
  150. package/src/assets/web-panel/assets/{responsiveObserve-D-PmXO9M.js → responsiveObserve-B72knbW8.js} +1 -1
  151. package/src/assets/web-panel/assets/{slide-CKbGxEZJ.js → slide-C3bI_2yy.js} +1 -1
  152. package/src/assets/web-panel/assets/{statusUtils-BbgXPkXm.js → statusUtils-0-FmcCAS.js} +1 -1
  153. package/src/assets/web-panel/assets/{styleChecker-DOr9rk68.js → styleChecker-DXUyqoWY.js} +1 -1
  154. package/src/assets/web-panel/assets/{useFlexGapSupport-B2wO2r36.js → useFlexGapSupport-BSSnL3f1.js} +1 -1
  155. package/src/assets/web-panel/assets/{useFs-SeVGBK34.js → useFs-B0Cvvl0S.js} +1 -1
  156. package/src/assets/web-panel/assets/{usePersonalDataHub-DUJBCxjD.js → usePersonalDataHub-6D-d4WU5.js} +1 -1
  157. package/src/assets/web-panel/assets/{vnode-CF4EYsig.js → vnode-zjIrlxT_.js} +1 -1
  158. package/src/assets/web-panel/assets/{zoom-pRUs5pqI.js → zoom-Hw7NEisu.js} +1 -1
  159. package/src/assets/web-panel/index.html +1 -1
  160. package/src/command-help-index.json +1 -1
  161. package/src/command-manifest.json +1 -1
  162. package/src/commands/checkpoint.js +42 -7
  163. package/src/commands/orchestrate.js +85 -25
  164. package/src/commands/session-location.js +5 -0
  165. package/src/commands/session-show.js +109 -1
  166. package/src/commands/session.js +26 -2
  167. package/src/commands/tokens.js +9 -4
  168. package/src/data/changelog.json +26 -0
  169. package/src/generated/cc-agent-protocol.schema.json +71 -0
  170. package/src/harness/jsonl-session-store.js +357 -114
  171. package/src/harness/mcp-client.js +44 -1
  172. package/src/lib/agent-router.js +35 -7
  173. package/src/lib/agent-sandbox.js +302 -2
  174. package/src/lib/app-server/server.js +14 -0
  175. package/src/lib/background-agent-supervisor.js +14 -4
  176. package/src/lib/causal-observability.js +64 -1
  177. package/src/lib/checkpoint-summary-projection.js +98 -0
  178. package/src/lib/checkpoint-timeline-authority.js +15 -51
  179. package/src/lib/claude-code-bridge.js +302 -60
  180. package/src/lib/codex-app-server-adapter.js +161 -35
  181. package/src/lib/cost-budget.js +4 -0
  182. package/src/lib/doctor-checkup.js +31 -2
  183. package/src/lib/durable-security-store.js +6 -1
  184. package/src/lib/evolution/evolution-artifact-identity.js +21 -0
  185. package/src/lib/evolution/evolution-artifact-ports.js +5 -11
  186. package/src/lib/evolution/evolution-eval-cohort-receipt-reconciliation.js +389 -0
  187. package/src/lib/execution-location-failure-site.js +63 -0
  188. package/src/lib/execution-location-target.js +102 -8
  189. package/src/lib/external-agent-adapters.js +2 -0
  190. package/src/lib/image-file-boundary.js +154 -0
  191. package/src/lib/image-input.js +16 -3
  192. package/src/lib/llm-pricing.js +114 -8
  193. package/src/lib/lsp/code-intelligence.js +7 -1
  194. package/src/lib/mcp-call-ledger-store.js +7 -2
  195. package/src/lib/model-capabilities.js +21 -10
  196. package/src/lib/model-context-catalog.js +60 -1
  197. package/src/lib/openai-responses.js +7 -1
  198. package/src/lib/orchestrator.js +8 -2
  199. package/src/lib/process-execution-broker/index.js +93 -1
  200. package/src/lib/process-execution-broker/linux-subreaper-artifact.js +120 -0
  201. package/src/lib/process-execution-broker/linux-subreaper-helper.js +344 -0
  202. package/src/lib/process-execution-broker/linux-subreaper-process.js +326 -0
  203. package/src/lib/process-execution-broker/linux-subreaper-supervisor.c +360 -0
  204. package/src/lib/process-execution-broker/owned-posix-process-group-worker.mjs +131 -0
  205. package/src/lib/process-execution-broker/owned-posix-process-group.js +267 -0
  206. package/src/lib/process-execution-broker/process-ownership-journal.js +297 -0
  207. package/src/lib/process-execution-broker/process-ownership-quarantine.js +91 -0
  208. package/src/lib/runtime-usage-ledger.js +3 -0
  209. package/src/lib/sandbox-docker-egress.js +507 -0
  210. package/src/lib/sandbox-egress-proxy.js +167 -10
  211. package/src/lib/sandbox-egress-worker-thread.js +38 -0
  212. package/src/lib/sandbox-egress-worker.js +196 -0
  213. package/src/lib/secure-fs.js +158 -42
  214. package/src/lib/session-branch-history.js +240 -0
  215. package/src/lib/session-history-origins.js +285 -0
  216. package/src/lib/session-input-receipt.js +110 -0
  217. package/src/lib/session-resource-budget.js +8 -8
  218. package/src/lib/session-transcript-history.js +456 -0
  219. package/src/lib/session-transcript-page.js +164 -0
  220. package/src/lib/session-transcript-reference.js +38 -0
  221. package/src/lib/session-usage.js +8 -0
  222. package/src/lib/token-tracker.js +32 -54
  223. package/src/lib/usage-pricing-context.js +93 -0
  224. package/src/runtime/agent-core.js +141 -21
  225. package/src/runtime/headless-stream.js +202 -4
  226. package/src/assets/web-panel/assets/ChatBubbleRenderer-D0WrJQaQ.js +0 -1
  227. package/src/assets/web-panel/assets/Projects-CRaSI25Z.js +0 -1
  228. package/src/assets/web-panel/assets/devWarning-CmgDNkoO.js +0 -1
  229. package/src/assets/web-panel/assets/index-BpIZ2S01.js +0 -1
  230. package/src/assets/web-panel/assets/index-C-VmKuyv.js +0 -1
@@ -18,6 +18,8 @@
18
18
  import http from "node:http";
19
19
  import net from "node:net";
20
20
  import dns from "node:dns";
21
+ import fs from "node:fs";
22
+ import path from "node:path";
21
23
  import {
22
24
  evaluateNetworkAccess,
23
25
  isPrivateHost,
@@ -132,15 +134,83 @@ export function proxyEnv(port, host = "127.0.0.1") {
132
134
  /**
133
135
  * Create (but do not yet listen on) an egress-filtering proxy for a policy.
134
136
  * @param {object} policy { allowedDomains?, deniedDomains?, allowPrivate? }
135
- * @param {object} [opts] { onDecision?(info), bindHost? }
136
- * @returns {{ listen():Promise<{port,env,server}>, close():Promise<void>,
137
- * server:import('http').Server, blocked:number, allowed:number }}
137
+ * @param {object} [opts] { onDecision?(info), bindHost?, socketPath? }
138
+ * @returns {{ listen():Promise<{port?,socketPath?,env?,server}>, close():Promise<void>,
139
+ * updatePolicy(nextPolicy:object, expectedRevision:number):number,
140
+ * server:import('http').Server, blocked:number, allowed:number,
141
+ * revision:number }}
138
142
  */
139
143
  export function createEgressProxy(policy = {}, opts = {}) {
140
144
  const bindHost = opts.bindHost || "127.0.0.1";
145
+ const socketPath = opts.socketPath || null;
146
+ if (socketPath) {
147
+ if (
148
+ process.platform !== "linux" ||
149
+ !path.isAbsolute(socketPath) ||
150
+ Buffer.byteLength(socketPath) > 107
151
+ ) {
152
+ throw new Error(
153
+ "A short Linux absolute path is required for the egress broker socket",
154
+ );
155
+ }
156
+ const parent = path.dirname(socketPath);
157
+ const stat = fs.lstatSync(parent);
158
+ if (
159
+ !stat.isDirectory() ||
160
+ stat.mode & 0o077 ||
161
+ stat.uid !== process.getuid()
162
+ ) {
163
+ throw new Error(
164
+ "The egress broker socket requires an owner-only directory",
165
+ );
166
+ }
167
+ }
141
168
  const state = { blocked: 0, allowed: 0 };
169
+ const snapshot = (value) => {
170
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
171
+ throw new TypeError("Egress policy must be an object");
172
+ }
173
+ for (const key of ["allowedDomains", "deniedDomains"]) {
174
+ if (
175
+ value[key] !== undefined &&
176
+ (!Array.isArray(value[key]) ||
177
+ value[key].some((item) => typeof item !== "string"))
178
+ ) {
179
+ throw new TypeError(`${key} must be an array of strings`);
180
+ }
181
+ }
182
+ if (
183
+ value.allowPrivate !== undefined &&
184
+ typeof value.allowPrivate !== "boolean"
185
+ ) {
186
+ throw new TypeError("allowPrivate must be a boolean");
187
+ }
188
+ return Object.freeze({
189
+ allowedDomains: Object.freeze([...(value.allowedDomains || [])]),
190
+ deniedDomains: Object.freeze([...(value.deniedDomains || [])]),
191
+ allowPrivate: value.allowPrivate === true,
192
+ });
193
+ };
194
+ let currentPolicy = snapshot(policy);
195
+ let revision = 0;
196
+ let closed = false;
197
+ const active = new Set();
198
+ const connections = new Set();
199
+ const register = (client) => {
200
+ const lease = { revision, client, upstream: null, cancelled: false };
201
+ active.add(lease);
202
+ return lease;
203
+ };
204
+ const revoke = (lease) => {
205
+ lease.cancelled = true;
206
+ lease.upstream?.destroy();
207
+ lease.client.destroy();
208
+ active.delete(lease);
209
+ };
210
+ const live = (lease) =>
211
+ !closed && !lease.cancelled && lease.revision === revision;
142
212
  const decide = (target, kind) => {
143
- const verdict = evaluateNetworkAccess(target, policy);
213
+ const verdict = evaluateNetworkAccess(target, currentPolicy);
144
214
  if (verdict.allowed) state.allowed += 1;
145
215
  else state.blocked += 1;
146
216
  if (typeof opts.onDecision === "function") {
@@ -162,11 +232,11 @@ export function createEgressProxy(policy = {}, opts = {}) {
162
232
  // exact address to pin the connection to (null → connect by the original
163
233
  // name). When the resolved IP is private the name-based allow is overturned:
164
234
  // fix the counters and re-notify observers with the block reason.
165
- const guard = async (verdict, kind) => {
235
+ const guard = async (verdict, kind, policySnapshot) => {
166
236
  if (!verdict.allowed || verdict.specific || !rebindGuard) {
167
237
  return { verdict, ip: null };
168
238
  }
169
- const res = await guardResolvedTarget(verdict.host, policy, lookup);
239
+ const res = await guardResolvedTarget(verdict.host, policySnapshot, lookup);
170
240
  if (res.ok) return { verdict, ip: res.ip };
171
241
  state.allowed -= 1;
172
242
  state.blocked += 1;
@@ -182,9 +252,20 @@ export function createEgressProxy(policy = {}, opts = {}) {
182
252
  };
183
253
 
184
254
  const server = http.createServer(async (req, res) => {
255
+ const lease = register(res);
256
+ res.once("close", () => {
257
+ active.delete(lease);
258
+ if (!res.writableEnded) lease.upstream?.destroy();
259
+ });
185
260
  try {
186
261
  // A forward-proxy request carries the absolute URL in the request line.
187
- const { verdict, ip } = await guard(decide(req.url, "http"), "http");
262
+ const policySnapshot = currentPolicy;
263
+ const { verdict, ip } = await guard(
264
+ decide(req.url, "http"),
265
+ "http",
266
+ policySnapshot,
267
+ );
268
+ if (!live(lease)) return;
188
269
  if (!verdict.allowed) {
189
270
  res.writeHead(403, { "content-type": "text/plain" });
190
271
  res.end(`egress blocked: ${verdict.host} — ${verdict.reason}\n`);
@@ -198,26 +279,40 @@ export function createEgressProxy(policy = {}, opts = {}) {
198
279
  res.end("bad request target\n");
199
280
  return;
200
281
  }
282
+ if (u.protocol !== "http:" || u.username || u.password) {
283
+ res.writeHead(400, { "content-type": "text/plain" });
284
+ res.end("unsupported proxy target\n");
285
+ return;
286
+ }
201
287
  const upstream = http.request(
202
288
  {
203
289
  hostname: ip || u.hostname, // pin to the validated IP when re-checked
204
290
  port: u.port || 80,
205
291
  path: `${u.pathname}${u.search}`,
206
292
  method: req.method,
207
- headers: req.headers, // preserves the original Host header
293
+ // The URL is the policy authority. Do not forward a conflicting
294
+ // client Host header to a different virtual host on the allowed IP.
295
+ headers: { ...req.headers, host: u.host },
208
296
  },
209
297
  (upRes) => {
298
+ if (!live(lease)) {
299
+ upRes.destroy();
300
+ return;
301
+ }
210
302
  res.writeHead(upRes.statusCode || 502, upRes.headers);
211
303
  upRes.pipe(res);
212
304
  },
213
305
  );
306
+ lease.upstream = upstream;
214
307
  upstream.on("error", () => {
308
+ if (!live(lease)) return;
215
309
  if (!res.headersSent)
216
310
  res.writeHead(502, { "content-type": "text/plain" });
217
311
  res.end("upstream error\n");
218
312
  });
219
313
  req.pipe(upstream);
220
314
  } catch {
315
+ if (!live(lease)) return;
221
316
  if (!res.headersSent)
222
317
  res.writeHead(502, { "content-type": "text/plain" });
223
318
  res.end("proxy error\n");
@@ -226,11 +321,19 @@ export function createEgressProxy(policy = {}, opts = {}) {
226
321
 
227
322
  // HTTPS (and any TLS) goes through CONNECT — tunnel only allowed hosts.
228
323
  server.on("connect", async (req, clientSocket, head) => {
324
+ const lease = register(clientSocket);
325
+ clientSocket.once("close", () => {
326
+ active.delete(lease);
327
+ lease.upstream?.destroy();
328
+ });
229
329
  try {
330
+ const policySnapshot = currentPolicy;
230
331
  const { verdict, ip } = await guard(
231
332
  decide(req.url, "connect"),
232
333
  "connect",
334
+ policySnapshot,
233
335
  ); // req.url = "host:port"
336
+ if (!live(lease)) return;
234
337
  if (!verdict.allowed) {
235
338
  clientSocket.write(
236
339
  "HTTP/1.1 403 Forbidden\r\n" +
@@ -245,11 +348,16 @@ export function createEgressProxy(policy = {}, opts = {}) {
245
348
  // Connect to the pinned validated IP so a rebind at connect time can't
246
349
  // swap in a private address between our check and the socket.
247
350
  const upstream = net.connect(port, ip || host, () => {
351
+ if (!live(lease)) {
352
+ upstream.destroy();
353
+ return;
354
+ }
248
355
  clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
249
356
  if (head && head.length) upstream.write(head);
250
357
  upstream.pipe(clientSocket);
251
358
  clientSocket.pipe(upstream);
252
359
  });
360
+ lease.upstream = upstream;
253
361
  upstream.on("error", () => {
254
362
  try {
255
363
  clientSocket.end();
@@ -265,6 +373,7 @@ export function createEgressProxy(policy = {}, opts = {}) {
265
373
  }
266
374
  });
267
375
  } catch {
376
+ if (!live(lease)) return;
268
377
  try {
269
378
  clientSocket.end();
270
379
  } catch {
@@ -273,6 +382,16 @@ export function createEgressProxy(policy = {}, opts = {}) {
273
382
  }
274
383
  });
275
384
 
385
+ // Raw HTTP Upgrade is not a governed tunnel. WebSocket clients may use a
386
+ // CONNECT tunnel, which remains registered above for revision revocation.
387
+ server.on("upgrade", (_req, socket) => {
388
+ socket.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
389
+ });
390
+ server.on("connection", (socket) => {
391
+ connections.add(socket);
392
+ socket.once("close", () => connections.delete(socket));
393
+ });
394
+
276
395
  return {
277
396
  server,
278
397
  get blocked() {
@@ -281,17 +400,55 @@ export function createEgressProxy(policy = {}, opts = {}) {
281
400
  get allowed() {
282
401
  return state.allowed;
283
402
  },
403
+ get revision() {
404
+ return revision;
405
+ },
406
+ updatePolicy(nextPolicy, expectedRevision) {
407
+ if (closed || expectedRevision !== revision) {
408
+ const error = new Error("Stale or closed egress policy revision");
409
+ error.code = "ERR_EGRESS_POLICY_REVISION";
410
+ throw error;
411
+ }
412
+ const next = snapshot(nextPolicy);
413
+ currentPolicy = next;
414
+ revision += 1;
415
+ // A new revision has no inherited connection authority. Closing every
416
+ // existing lease also covers in-flight DNS and HTTP connect callbacks.
417
+ for (const lease of active) revoke(lease);
418
+ return revision;
419
+ },
284
420
  listen() {
285
421
  return new Promise((resolve, reject) => {
422
+ if (closed) {
423
+ reject(new Error("Egress proxy is closed"));
424
+ return;
425
+ }
286
426
  server.once("error", reject);
287
- server.listen(0, bindHost, () => {
427
+ const onListening = () => {
288
428
  server.removeListener("error", reject);
429
+ if (socketPath) {
430
+ try {
431
+ fs.chmodSync(socketPath, 0o600);
432
+ } catch (error) {
433
+ server.close();
434
+ reject(error);
435
+ return;
436
+ }
437
+ resolve({ socketPath, server });
438
+ return;
439
+ }
289
440
  const port = server.address().port;
290
441
  resolve({ port, env: proxyEnv(port), server });
291
- });
442
+ };
443
+ if (socketPath) server.listen(socketPath, onListening);
444
+ else server.listen(0, bindHost, onListening);
292
445
  });
293
446
  },
294
447
  close() {
448
+ closed = true;
449
+ for (const lease of active) revoke(lease);
450
+ for (const socket of connections) socket.destroy();
451
+ if (!server.listening) return Promise.resolve();
295
452
  return new Promise((resolve) => server.close(() => resolve()));
296
453
  },
297
454
  };
@@ -0,0 +1,38 @@
1
+ import { parentPort, workerData } from "node:worker_threads";
2
+ import { createEgressProxy } from "./sandbox-egress-proxy.js";
3
+
4
+ let proxy;
5
+ try {
6
+ proxy = createEgressProxy(workerData.policy, {
7
+ bindHost: workerData.bindHost,
8
+ socketPath: workerData.socketPath,
9
+ });
10
+ const { port, socketPath } = await proxy.listen();
11
+ parentPort.postMessage({ type: "ready", port, socketPath });
12
+ parentPort.on("message", async (message) => {
13
+ if (!Number.isSafeInteger(message?.id) || message.id < 1) return;
14
+ try {
15
+ if (message.type === "update") {
16
+ const revision = proxy.updatePolicy(
17
+ message.policy,
18
+ message.expectedRevision,
19
+ );
20
+ parentPort.postMessage({ id: message.id, revision });
21
+ } else if (message.type === "close") {
22
+ await proxy.close();
23
+ parentPort.postMessage({ id: message.id, closed: true });
24
+ }
25
+ } catch (error) {
26
+ parentPort.postMessage({
27
+ id: message.id,
28
+ errorCode:
29
+ error?.code === "ERR_EGRESS_POLICY_REVISION"
30
+ ? error.code
31
+ : "ERR_EGRESS_WORKER_REQUEST",
32
+ });
33
+ }
34
+ });
35
+ } catch {
36
+ parentPort.postMessage({ type: "startup-failed" });
37
+ parentPort.close();
38
+ }
@@ -0,0 +1,196 @@
1
+ /** Run the policy proxy on a separate event loop so a synchronous shell wait
2
+ * cannot stall HTTP/CONNECT decisions. This is a host-side building block;
3
+ * callers still need a kernel-enforced network route before allowing a
4
+ * domain-restricted sandbox command to start.
5
+ */
6
+ import { Worker } from "node:worker_threads";
7
+ import path from "node:path";
8
+
9
+ const RESPONSE_TIMEOUT_MS = 10_000;
10
+
11
+ function workerError(code) {
12
+ const error = new Error(code);
13
+ error.code = code;
14
+ return error;
15
+ }
16
+
17
+ export async function startEgressProxyWorker(policy, options = {}) {
18
+ const bindHost = options.bindHost || "127.0.0.1";
19
+ const socketPath = options.socketPath || null;
20
+ if (socketPath) {
21
+ if (process.platform !== "linux" || !path.isAbsolute(socketPath)) {
22
+ throw workerError("ERR_EGRESS_WORKER_SOCKET_PATH");
23
+ }
24
+ } else if (bindHost !== "127.0.0.1" && bindHost !== "::1") {
25
+ throw workerError("ERR_EGRESS_WORKER_BIND_HOST");
26
+ }
27
+ const timeoutMs = options.timeoutMs || RESPONSE_TIMEOUT_MS;
28
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1) {
29
+ throw workerError("ERR_EGRESS_WORKER_TIMEOUT");
30
+ }
31
+ const worker = new Worker(
32
+ new URL("./sandbox-egress-worker-thread.js", import.meta.url),
33
+ { workerData: { policy, bindHost, socketPath } },
34
+ );
35
+ const pending = new Map();
36
+ let nextId = 1;
37
+ let revision = 0;
38
+ let closed = false;
39
+ let terminalError = null;
40
+ let expectedTermination = false;
41
+ let terminationPromise = null;
42
+ let shutdownPromise = null;
43
+ let resolveReady;
44
+ let rejectReady;
45
+ const ready = new Promise((resolve, reject) => {
46
+ resolveReady = resolve;
47
+ rejectReady = reject;
48
+ });
49
+ const startupTimer = setTimeout(() => {
50
+ fail(workerError("ERR_EGRESS_WORKER_START_TIMEOUT"));
51
+ void terminateWorker();
52
+ }, timeoutMs);
53
+
54
+ function terminateWorker() {
55
+ if (!terminationPromise) {
56
+ expectedTermination = true;
57
+ terminationPromise = worker.terminate();
58
+ }
59
+ return terminationPromise;
60
+ }
61
+
62
+ function fail(error) {
63
+ if (terminalError) return;
64
+ terminalError = error;
65
+ clearTimeout(startupTimer);
66
+ rejectReady(error);
67
+ for (const entry of pending.values()) {
68
+ clearTimeout(entry.timer);
69
+ entry.reject(error);
70
+ }
71
+ pending.clear();
72
+ if (!closed) {
73
+ try {
74
+ options.onFailure?.(error);
75
+ } catch {
76
+ // A diagnostic observer cannot keep a failed proxy alive.
77
+ }
78
+ }
79
+ }
80
+
81
+ worker.on("message", (message) => {
82
+ if (message?.type === "startup-failed") {
83
+ fail(workerError("ERR_EGRESS_WORKER_START_FAILED"));
84
+ return;
85
+ }
86
+ if (message?.type === "ready") {
87
+ const validEndpoint = socketPath
88
+ ? message.socketPath === socketPath && message.port === undefined
89
+ : Number.isSafeInteger(message.port) &&
90
+ message.port >= 1 &&
91
+ message.port <= 65535;
92
+ if (!validEndpoint) {
93
+ fail(workerError("ERR_EGRESS_WORKER_PROTOCOL"));
94
+ void terminateWorker();
95
+ return;
96
+ }
97
+ clearTimeout(startupTimer);
98
+ resolveReady(socketPath ? socketPath : message.port);
99
+ return;
100
+ }
101
+ const entry = pending.get(message?.id);
102
+ if (!entry) return;
103
+ pending.delete(message.id);
104
+ clearTimeout(entry.timer);
105
+ if (message.errorCode) {
106
+ entry.reject(
107
+ workerError(
108
+ message.errorCode === "ERR_EGRESS_POLICY_REVISION"
109
+ ? message.errorCode
110
+ : "ERR_EGRESS_WORKER_REQUEST",
111
+ ),
112
+ );
113
+ return;
114
+ }
115
+ entry.resolve(message);
116
+ });
117
+ worker.on("error", () => fail(workerError("ERR_EGRESS_WORKER_FAILURE")));
118
+ worker.on("exit", () => {
119
+ if (!expectedTermination) fail(workerError("ERR_EGRESS_WORKER_EXIT"));
120
+ });
121
+
122
+ function request(type, fields = {}) {
123
+ if (terminalError) return Promise.reject(terminalError);
124
+ if (closed && type !== "close") {
125
+ return Promise.reject(workerError("ERR_EGRESS_WORKER_CLOSED"));
126
+ }
127
+ const id = nextId++;
128
+ return new Promise((resolve, reject) => {
129
+ const timer = setTimeout(() => {
130
+ const error = workerError("ERR_EGRESS_WORKER_RESPONSE_TIMEOUT");
131
+ fail(error);
132
+ void terminateWorker();
133
+ }, timeoutMs);
134
+ pending.set(id, { resolve, reject, timer });
135
+ try {
136
+ worker.postMessage({ id, type, ...fields });
137
+ } catch {
138
+ clearTimeout(timer);
139
+ pending.delete(id);
140
+ reject(workerError("ERR_EGRESS_WORKER_REQUEST"));
141
+ }
142
+ });
143
+ }
144
+
145
+ let endpoint;
146
+ try {
147
+ endpoint = await ready;
148
+ } catch (error) {
149
+ await terminateWorker();
150
+ throw error;
151
+ }
152
+ return {
153
+ port: socketPath ? null : endpoint,
154
+ socketPath: socketPath ? endpoint : null,
155
+ get revision() {
156
+ return revision;
157
+ },
158
+ async updatePolicy(nextPolicy, expectedRevision) {
159
+ const response = await request("update", {
160
+ policy: nextPolicy,
161
+ expectedRevision,
162
+ });
163
+ if (!Number.isSafeInteger(response.revision)) {
164
+ fail(workerError("ERR_EGRESS_WORKER_PROTOCOL"));
165
+ void terminateWorker();
166
+ throw terminalError;
167
+ }
168
+ revision = response.revision;
169
+ return revision;
170
+ },
171
+ close() {
172
+ if (!shutdownPromise) {
173
+ closed = true;
174
+ shutdownPromise = (async () => {
175
+ try {
176
+ if (!terminalError) await request("close");
177
+ } finally {
178
+ await terminateWorker();
179
+ }
180
+ })();
181
+ }
182
+ return shutdownPromise;
183
+ },
184
+ abort() {
185
+ if (!shutdownPromise) {
186
+ fail(workerError("ERR_EGRESS_WORKER_ABORTED"));
187
+ closed = true;
188
+ shutdownPromise = terminateWorker().then(() => {});
189
+ } else if (!expectedTermination) {
190
+ fail(workerError("ERR_EGRESS_WORKER_ABORTED"));
191
+ void terminateWorker();
192
+ }
193
+ return shutdownPromise;
194
+ },
195
+ };
196
+ }