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
@@ -228,12 +228,14 @@ export class AgentRouter extends EventEmitter {
228
228
  */
229
229
  static autoDetect(options = {}) {
230
230
  const backends = [];
231
+ const claude = options.cliDetections?.claude || detectClaudeCode();
232
+ const codex = options.cliDetections?.codex || detectCodex();
231
233
 
232
- if (detectClaudeCode().found) {
233
- backends.push({ type: BACKEND_TYPE.CLAUDE, weight: 3 });
234
+ if (claude.found) {
235
+ backends.push({ type: BACKEND_TYPE.CLAUDE, weight: 3, installed: true });
234
236
  }
235
- if (detectCodex().found) {
236
- backends.push({ type: BACKEND_TYPE.CODEX, weight: 2 });
237
+ if (codex.found) {
238
+ backends.push({ type: BACKEND_TYPE.CODEX, weight: 2, installed: true });
237
239
  }
238
240
  if (process.env.GEMINI_API_KEY) {
239
241
  backends.push({ type: BACKEND_TYPE.GEMINI, weight: 2 });
@@ -263,7 +265,7 @@ export class AgentRouter extends EventEmitter {
263
265
  const { cwd = process.cwd() } = options;
264
266
  if (this._backends.length === 0) {
265
267
  throw new Error(
266
- "No agent backends available. Install Claude Code: npm i -g @anthropic-ai/claude-code",
268
+ "No agent backends available. Use cc agent for tool execution; orchestrate API backends produce text only.",
267
269
  );
268
270
  }
269
271
  const evolutionIngress =
@@ -277,8 +279,7 @@ export class AgentRouter extends EventEmitter {
277
279
  error.code = "CC_AGENT_EVOLUTION_INGRESS_FAILED";
278
280
  throw error;
279
281
  }
280
- const backends =
281
- this._backends.filter((backend) => !backend.isCLI);
282
+ const backends = this._backends.filter((backend) => !backend.isCLI);
282
283
 
283
284
  if (backends.length === 0) {
284
285
  const error = new Error(
@@ -475,6 +476,7 @@ export class AgentRouter extends EventEmitter {
475
476
  return {
476
477
  type,
477
478
  isCLI: true,
479
+ installed: cfg.installed ?? null,
478
480
  weight: cfg.weight || 1,
479
481
  _pool: pool,
480
482
  timeout: cfg.timeout || 300_000,
@@ -530,8 +532,34 @@ export class AgentRouter extends EventEmitter {
530
532
  kind: b.isCLI ? "cli" : "api",
531
533
  provider: b.provider || b.type,
532
534
  weight: b.weight,
535
+ installed: b.isCLI ? (b.installed ?? null) : null,
536
+ configured: true,
537
+ governanceAdmitted: b.isCLI ? false : null,
538
+ requiresAuthenticatedRun: true,
539
+ dispatchable: !b.isCLI,
540
+ runnable: false,
541
+ executionMode: b.isCLI ? "blocked" : "text-only",
542
+ blockedReason: b.isCLI
543
+ ? AGENT_ROUTER_ERROR.EXTERNAL_MODEL_INGRESS_UNATTESTED
544
+ : "API_TEXT_ONLY_NO_TOOL_EXECUTION",
545
+ alternative: "cc agent",
533
546
  }));
534
547
  }
548
+
549
+ /** Configuration preflight only; dispatch still authenticates each Run. */
550
+ assertDispatchAvailable() {
551
+ if (this._backends.some((backend) => !backend.isCLI)) return;
552
+ const error = new Error(
553
+ this._backends.length
554
+ ? "External Claude/Codex CLIs are blocked: per-request model governance is unattested. Use cc agent for tool execution."
555
+ : "No agent backends available. Use cc agent for tool execution.",
556
+ );
557
+ error.code = this._backends.length
558
+ ? AGENT_ROUTER_ERROR.EXTERNAL_MODEL_INGRESS_UNATTESTED
559
+ : "AGENT_ROUTER_NO_BACKENDS";
560
+ error.backends = this.summary();
561
+ throw error;
562
+ }
535
563
  }
536
564
 
537
565
  // ===== V2 Surface: Agent Router governance overlay (CLI v0.132.0) =====
@@ -13,6 +13,9 @@ export const AGENT_SANDBOX_MODES = Object.freeze([
13
13
  ]);
14
14
  export const AGENT_SANDBOX_CAPABILITY_SCHEMA =
15
15
  "chainlesschain.agent-sandbox-capabilities/v1";
16
+ const dockerEgressReceipts = new WeakMap();
17
+ const PINNED_DOCKER_IMAGE =
18
+ /^[a-zA-Z0-9][a-zA-Z0-9./:_-]*@sha256:[a-f0-9]{64}$/;
16
19
  export const _deps = {
17
20
  spawnSync: (...args) => executionBroker.spawnSync(...args),
18
21
  host: () => ({
@@ -249,12 +252,21 @@ export function normalizeAgentSandbox(value, options = {}) {
249
252
  return {
250
253
  engine: effectiveSettings.engine || "docker",
251
254
  image: effectiveSettings.image || image,
255
+ ...(effectiveSettings.engine === "docker-egress"
256
+ ? { relayImage: effectiveSettings.relayImage || null }
257
+ : {}),
252
258
  cwd: path.resolve(options.cwd || process.cwd()),
253
259
  network: managedNetworkDisabled
254
260
  ? false
255
261
  : options.network === true || effectiveSettings.network === true,
256
262
  policy: normalizeSandboxPolicy(
257
- effectiveSettings,
263
+ effectiveSettings.engine === "docker-egress"
264
+ ? {
265
+ ...effectiveSettings,
266
+ allowUnsandboxedCommands: false,
267
+ failIfUnavailable: true,
268
+ }
269
+ : effectiveSettings,
258
270
  options.cwd || process.cwd(),
259
271
  ),
260
272
  };
@@ -340,6 +352,149 @@ function addUniqueCapability(target, entry) {
340
352
  }
341
353
  }
342
354
 
355
+ function egressBinding(sandbox) {
356
+ return JSON.stringify({
357
+ engine: sandbox.engine,
358
+ image: sandbox.image,
359
+ relayImage: sandbox.relayImage,
360
+ cwd: sandbox.cwd,
361
+ network: sandbox.network,
362
+ mode: sandbox.mode || null,
363
+ policy: sandbox.policy || {},
364
+ });
365
+ }
366
+
367
+ function assessDockerEgressCapabilities(sandbox, options, host) {
368
+ const policy = sandbox.policy || {};
369
+ const domainRules =
370
+ (policy.allowedDomains?.length || 0) + (policy.deniedDomains?.length || 0);
371
+ const requested = [
372
+ capability("isolation.container"),
373
+ capability("filesystem.workspace-read-write"),
374
+ capability("network.domain-policy", { entries: domainRules }),
375
+ ];
376
+ const unsupported = [];
377
+ const reject = (id, reason, message) =>
378
+ unsupported.push(capability(id, { reason, message }));
379
+ if (policy.excludedCommands?.length)
380
+ reject(
381
+ "execution.excluded-commands",
382
+ "docker_egress_exclusions_prohibited",
383
+ "Docker egress does not allow commands to bypass its execution route.",
384
+ );
385
+ if (host.platform !== "linux")
386
+ reject(
387
+ "backend.platform",
388
+ "docker_egress_requires_linux",
389
+ "Docker egress requires a Linux host.",
390
+ );
391
+ if (!["x64", "arm64"].includes(host.arch))
392
+ reject(
393
+ "backend.architecture",
394
+ "docker_egress_architecture_unsupported",
395
+ "Docker egress requires x64 or arm64.",
396
+ );
397
+ if (sandbox.mode === "strict" || sandbox.network !== true)
398
+ reject(
399
+ "network.domain-policy",
400
+ "docker_egress_network_disabled",
401
+ "Docker egress requires network=true and is prohibited in strict mode.",
402
+ );
403
+ if (!domainRules)
404
+ reject(
405
+ "network.domain-policy",
406
+ "docker_egress_domain_policy_required",
407
+ "Docker egress requires explicit domain rules.",
408
+ );
409
+ for (const field of ["image", "relayImage"]) {
410
+ if (
411
+ typeof sandbox[field] !== "string" ||
412
+ !PINNED_DOCKER_IMAGE.test(sandbox[field])
413
+ )
414
+ reject(
415
+ `backend.${field}`,
416
+ "docker_egress_image_digest_required",
417
+ `Docker egress ${field} must be pinned to a sha256 digest.`,
418
+ );
419
+ }
420
+ for (const field of ["allowRead", "denyRead", "allowWrite", "denyWrite"]) {
421
+ if (policy[field]?.length)
422
+ reject(
423
+ `filesystem.${field}`,
424
+ "docker_fine_grained_filesystem_unsupported",
425
+ "Docker egress does not support additional or masked filesystem paths.",
426
+ );
427
+ }
428
+ const enforceable = unsupported.length
429
+ ? []
430
+ : [
431
+ capability("isolation.container", { enforcement: "docker-container" }),
432
+ capability("filesystem.workspace-read-write", {
433
+ enforcement: "docker-bind-mount",
434
+ }),
435
+ capability("network.domain-policy", {
436
+ enforcement: "docker-none-network-uds-proxy-seccomp",
437
+ }),
438
+ ];
439
+ const receipt = options.execution?.receipt;
440
+ const registered =
441
+ receipt && typeof receipt === "object"
442
+ ? dockerEgressReceipts.get(receipt)
443
+ : null;
444
+ const started =
445
+ unsupported.length === 0 &&
446
+ registered?.binding === egressBinding(sandbox) &&
447
+ registered.host.platform === host.platform &&
448
+ registered.host.arch === host.arch;
449
+ const attempted = started || options.execution?.attempted === true;
450
+ const outcomeUnknown = options.execution?.outcomeUnknown === true;
451
+ const checked = started || options.availability != null;
452
+ const available = started
453
+ ? true
454
+ : checked
455
+ ? options.availability?.available === true
456
+ : null;
457
+ return {
458
+ schema: AGENT_SANDBOX_CAPABILITY_SCHEMA,
459
+ host: {
460
+ platform: String(host.platform),
461
+ release: String(host.release),
462
+ arch: String(host.arch),
463
+ },
464
+ backend: {
465
+ engine: "docker-egress",
466
+ isolationLevel: "container",
467
+ availabilityChecked: checked,
468
+ available,
469
+ reason: started
470
+ ? null
471
+ : outcomeUnknown
472
+ ? "docker_egress_outcome_unknown"
473
+ : options.availability?.reason || null,
474
+ },
475
+ status: unsupported.length
476
+ ? "unsupported"
477
+ : started
478
+ ? "applied"
479
+ : outcomeUnknown
480
+ ? "outcome-unknown"
481
+ : attempted
482
+ ? "failed-to-start"
483
+ : checked && !available
484
+ ? "unavailable"
485
+ : "ready",
486
+ execution: {
487
+ observed: Boolean(started || options.execution),
488
+ attempted,
489
+ started: Boolean(started),
490
+ },
491
+ requested,
492
+ enforceable,
493
+ applied: started ? enforceable.map((entry) => ({ ...entry })) : [],
494
+ unsupported,
495
+ };
496
+ }
497
+
343
498
  /**
344
499
  * Describe the exact legacy agent-shell sandbox request without claiming that
345
500
  * a command has run. `enforceable` is backend capability; `applied` is only
@@ -351,6 +506,8 @@ export function assessAgentSandboxCapabilities(sandbox, options = {}) {
351
506
  release: os.release(),
352
507
  arch: process.arch,
353
508
  };
509
+ if (sandbox?.engine === "docker-egress")
510
+ return assessDockerEgressCapabilities(sandbox, options, host);
354
511
  const execution = options.execution || null;
355
512
  const availability = options.availability || null;
356
513
  const requested = [];
@@ -594,6 +751,22 @@ function attachSandboxCapabilityReport(
594
751
  }
595
752
 
596
753
  export function executeSandboxedShell(command, sandbox, options = {}) {
754
+ if (sandbox?.engine === "docker-egress") {
755
+ return {
756
+ stdout: "",
757
+ stderr: "Docker egress requires the asynchronous execution route",
758
+ exitCode: 1,
759
+ failedToStart: true,
760
+ sandboxCapabilities: assessAgentSandboxCapabilities(sandbox, {
761
+ host: _deps.host(),
762
+ execution: { attempted: true },
763
+ availability: {
764
+ available: false,
765
+ reason: "docker_egress_async_execution_required",
766
+ },
767
+ }),
768
+ };
769
+ }
597
770
  if (!sandbox || !["docker", "bubblewrap"].includes(sandbox.engine)) {
598
771
  throw new Error("A supported agent sandbox configuration is required");
599
772
  }
@@ -783,7 +956,7 @@ function executeBubblewrapShell(command, sandbox, options, hostCwd, policy) {
783
956
  export function isolationLevel(sandbox) {
784
957
  if (!sandbox) return "policy-only";
785
958
  if (sandbox.engine === "bubblewrap") return "os-sandbox";
786
- if (sandbox.engine === "docker") return "container";
959
+ if (["docker", "docker-egress"].includes(sandbox.engine)) return "container";
787
960
  return "policy-only";
788
961
  }
789
962
 
@@ -836,6 +1009,11 @@ export function assertSandboxAvailable(sandbox, deps = _deps) {
836
1009
  if (!sandbox || sandbox.policy?.failIfUnavailable !== true) return;
837
1010
  const probe = probeSandboxAvailability(sandbox, deps);
838
1011
  if (!probe.available) {
1012
+ if (sandbox.engine === "docker-egress") {
1013
+ throw new Error(
1014
+ "Docker egress is unavailable; this backend never falls back to unsandboxed execution.",
1015
+ );
1016
+ }
839
1017
  throw new Error(
840
1018
  `sandbox.failIfUnavailable: ${sandbox.engine} sandbox is unavailable (${probe.reason}) — refusing to start. Install/start ${sandbox.engine === "bubblewrap" ? "bubblewrap" : "Docker"}, or unset failIfUnavailable to allow per-command degradation.`,
841
1019
  );
@@ -860,5 +1038,127 @@ export function sandboxSummary(sandbox) {
860
1038
  },
861
1039
  };
862
1040
  if (sandbox.mode) summary.mode = sandbox.mode;
1041
+ if (sandbox.engine === "docker-egress")
1042
+ summary.relayImage = sandbox.relayImage;
863
1043
  return summary;
864
1044
  }
1045
+
1046
+ /** Own the asynchronous session and mint execution evidence only after its
1047
+ * real implementation reports a terminal result and cleanup completes. */
1048
+ export async function executeDockerEgressShell(command, sandbox, options = {}) {
1049
+ if (sandbox?.engine !== "docker-egress")
1050
+ throw new TypeError("docker-egress configuration is required");
1051
+ const host = _deps.host();
1052
+ const snapshot = JSON.parse(JSON.stringify(sandbox));
1053
+ const preflight = assessAgentSandboxCapabilities(snapshot, { host });
1054
+ let startAuthorized = false;
1055
+ let observedResult;
1056
+ const failure = () => ({
1057
+ stdout:
1058
+ startAuthorized && typeof observedResult?.stdout === "string"
1059
+ ? observedResult.stdout
1060
+ : "",
1061
+ stderr: startAuthorized
1062
+ ? "Docker egress execution outcome or cleanup could not be verified; do not retry automatically."
1063
+ : preflight.unsupported.length
1064
+ ? preflight.unsupported.map((entry) => entry.message).join("; ")
1065
+ : "Docker egress execution is unavailable or did not complete safely.",
1066
+ exitCode: 1,
1067
+ ...(startAuthorized
1068
+ ? {
1069
+ executionOutcome: observedResult
1070
+ ? "completed-cleanup-unknown"
1071
+ : "unknown",
1072
+ retrySafe: false,
1073
+ }
1074
+ : { failedToStart: true }),
1075
+ sandboxCapabilities: assessAgentSandboxCapabilities(snapshot, {
1076
+ host,
1077
+ execution: { attempted: true, outcomeUnknown: startAuthorized },
1078
+ ...(startAuthorized
1079
+ ? {}
1080
+ : {
1081
+ availability: {
1082
+ available: false,
1083
+ reason: "docker_egress_execution_unavailable",
1084
+ },
1085
+ }),
1086
+ }),
1087
+ });
1088
+ if (preflight.unsupported.length) return failure();
1089
+ let session;
1090
+ let authorityError;
1091
+ try {
1092
+ const { startDockerEgressSession } =
1093
+ await import("./sandbox-docker-egress.js");
1094
+ session = await startDockerEgressSession({
1095
+ brokerSocketPath: options.brokerSocketPath,
1096
+ relayImage: snapshot.relayImage,
1097
+ targetImage: snapshot.image,
1098
+ workspaceRoot: options.cwd || snapshot.cwd,
1099
+ auditContext: options.auditContext,
1100
+ });
1101
+ try {
1102
+ options.onSession?.(session);
1103
+ } catch (error) {
1104
+ authorityError = error;
1105
+ throw error;
1106
+ }
1107
+ const result = await session.run(command, {
1108
+ timeoutMs: options.timeoutMs || options.timeout,
1109
+ auditContext: options.auditContext,
1110
+ env: options.env,
1111
+ async beforeStart() {
1112
+ try {
1113
+ if (egressBinding(sandbox) !== egressBinding(snapshot))
1114
+ throw Object.assign(
1115
+ new Error("Sandbox policy changed during Docker startup"),
1116
+ { code: "CC_SHELL_POLICY_AUTHORITY_CHANGED" },
1117
+ );
1118
+ await options.beforeStart?.();
1119
+ if (egressBinding(sandbox) !== egressBinding(snapshot))
1120
+ throw Object.assign(
1121
+ new Error("Sandbox policy changed during Docker startup"),
1122
+ { code: "CC_SHELL_POLICY_AUTHORITY_CHANGED" },
1123
+ );
1124
+ startAuthorized = true;
1125
+ } catch (error) {
1126
+ authorityError = error;
1127
+ throw error;
1128
+ }
1129
+ },
1130
+ });
1131
+ observedResult = result;
1132
+ await session.close();
1133
+ if (
1134
+ !startAuthorized ||
1135
+ !Number.isInteger(result?.exitCode) ||
1136
+ typeof result.stdout !== "string" ||
1137
+ typeof result.stderr !== "string"
1138
+ ) {
1139
+ throw new Error("Docker egress execution proof is incomplete");
1140
+ }
1141
+ const receipt = Object.freeze({ kind: "docker-egress-execution/v1" });
1142
+ dockerEgressReceipts.set(receipt, {
1143
+ binding: egressBinding(snapshot),
1144
+ host: { ...host },
1145
+ });
1146
+ return {
1147
+ ...result,
1148
+ sandboxCapabilities: assessAgentSandboxCapabilities(snapshot, {
1149
+ host,
1150
+ execution: { attempted: true, receipt },
1151
+ }),
1152
+ };
1153
+ } catch (error) {
1154
+ if (session) {
1155
+ try {
1156
+ await session.close();
1157
+ } catch (cleanupError) {
1158
+ error.cleanupError = cleanupError;
1159
+ }
1160
+ }
1161
+ if (authorityError) throw authorityError;
1162
+ return failure();
1163
+ }
1164
+ }
@@ -1463,6 +1463,20 @@ export class CcAppServer {
1463
1463
  contextRevision: Number.isSafeInteger(event.context_revision)
1464
1464
  ? event.context_revision
1465
1465
  : null,
1466
+ // Preserve the MCP form/URL vocabulary for native host review. This is
1467
+ // question metadata, not an approval decision or host execution authority.
1468
+ ...(event.metadata?.kind === "mcp_elicitation"
1469
+ ? {
1470
+ metadata: {
1471
+ kind: "mcp_elicitation",
1472
+ mode: event.metadata.mode || "form",
1473
+ server: event.metadata.server ?? null,
1474
+ requestedSchema: event.metadata.requestedSchema ?? null,
1475
+ url: event.metadata.url ?? null,
1476
+ elicitationId: event.metadata.elicitationId ?? null,
1477
+ },
1478
+ }
1479
+ : {}),
1466
1480
  expiresAt: new Date(this.now() + this.requestTimeoutMs).toISOString(),
1467
1481
  };
1468
1482
  }
@@ -2174,14 +2174,22 @@ function isBackgroundAgentAfterCursor(state, cursor) {
2174
2174
  return String(state.id).localeCompare(cursor.id) > 0;
2175
2175
  }
2176
2176
 
2177
+ function observeBackgroundAgentList(options, source, entryCount) {
2178
+ // Optional in-process measurement only; telemetry failures cannot change a read.
2179
+ try {
2180
+ options.indexObserver?.(Object.freeze({ source, entryCount }));
2181
+ } catch {
2182
+ // Keep observers outside the authoritative result.
2183
+ }
2184
+ }
2185
+
2177
2186
  /**
2178
2187
  * Return one stable, bounded page from the newest-first background session
2179
2188
  * feed. The cursor is an opaque ordering fence, not an authority token.
2180
2189
  *
2181
- * State files are still read to preserve the current authoritative lifecycle
2182
- * reconciliation. A future read-only index may reduce that I/O, but this API
2183
- * immediately bounds the object graph and protocol response retained by a
2184
- * caller.
2190
+ * The index still enumerates and stats every authority file; selected rows are
2191
+ * read again for lifecycle reconciliation. It reduces content reads, not the
2192
+ * O(N) inventory fence. indexObserver receives the chosen read path and count.
2185
2193
  */
2186
2194
  export function listBackgroundAgentsPage(options = {}) {
2187
2195
  const limit = normalizeBackgroundAgentPageLimit(options.limit);
@@ -2223,6 +2231,7 @@ export function listBackgroundAgentsPage(options = {}) {
2223
2231
  projected.length > sessions.length
2224
2232
  ? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
2225
2233
  : null;
2234
+ observeBackgroundAgentList(options, indexed.source, indexed.entries.length);
2226
2235
  return Object.freeze({
2227
2236
  sessions: Object.freeze(sessions),
2228
2237
  nextCursor,
@@ -2238,6 +2247,7 @@ export function listBackgroundAgentsPage(options = {}) {
2238
2247
  eligible.length > sessions.length
2239
2248
  ? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
2240
2249
  : null;
2250
+ observeBackgroundAgentList(options, "full-scan", eligible.length);
2241
2251
  return Object.freeze({
2242
2252
  sessions: Object.freeze(sessions),
2243
2253
  nextCursor,
@@ -8,6 +8,10 @@
8
8
 
9
9
  import crypto from "node:crypto";
10
10
  import { PRICE_TABLE, priceRollup } from "./llm-pricing.js";
11
+ import {
12
+ projectUsagePricingContext,
13
+ mergeUsagePricingBucket,
14
+ } from "./usage-pricing-context.js";
11
15
  import { canonicalDeliveryJson } from "./delivery-evidence.js";
12
16
  import { createSessionTranscriptStructureProjection } from "./session-transcript-structure.js";
13
17
 
@@ -251,7 +255,64 @@ function normalizePricingTable(table = PRICE_TABLE) {
251
255
  ) {
252
256
  throw new Error("pricing rates must be finite non-negative numbers");
253
257
  }
254
- return { match, in: input, out: output };
258
+ const result = { match, in: input, out: output };
259
+ if (Object.hasOwn(entry, "explicitOverride")) {
260
+ if (typeof entry.explicitOverride !== "boolean")
261
+ throw new Error("pricing explicitOverride must be boolean");
262
+ result.explicitOverride = entry.explicitOverride;
263
+ }
264
+ if (Object.hasOwn(entry, "exact")) {
265
+ if (typeof entry.exact !== "boolean")
266
+ throw new Error("pricing exact must be boolean");
267
+ result.exact = entry.exact;
268
+ }
269
+ if (Object.hasOwn(entry, "terms")) {
270
+ const terms = entry.terms;
271
+ const nonNegative = (value) => {
272
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
273
+ throw new Error("invalid pricing multiplier");
274
+ return value;
275
+ };
276
+ assertKnownOwnKeys(
277
+ terms,
278
+ new Set([
279
+ "cacheReadMultiplier",
280
+ "cacheWriteMultiplier",
281
+ "longContext",
282
+ "serviceMultipliers",
283
+ ]),
284
+ "pricing terms",
285
+ );
286
+ assertKnownOwnKeys(
287
+ terms.longContext,
288
+ new Set(["threshold", "inputMultiplier", "outputMultiplier"]),
289
+ "pricing long context",
290
+ );
291
+ const threshold = nonNegative(terms.longContext.threshold);
292
+ if (!Number.isSafeInteger(threshold))
293
+ throw new Error("invalid pricing threshold");
294
+ assertKnownOwnKeys(
295
+ terms.serviceMultipliers,
296
+ new Set(["standard", "default", "auto", "batch", "flex", "fast"]),
297
+ "pricing service multipliers",
298
+ );
299
+ result.terms = {
300
+ cacheReadMultiplier: nonNegative(terms.cacheReadMultiplier),
301
+ cacheWriteMultiplier: nonNegative(terms.cacheWriteMultiplier),
302
+ longContext: {
303
+ threshold,
304
+ inputMultiplier: nonNegative(terms.longContext.inputMultiplier),
305
+ outputMultiplier: nonNegative(terms.longContext.outputMultiplier),
306
+ },
307
+ serviceMultipliers: Object.fromEntries(
308
+ Object.entries(terms.serviceMultipliers).map(([key, value]) => [
309
+ key,
310
+ nonNegative(value),
311
+ ]),
312
+ ),
313
+ };
314
+ }
315
+ return result;
255
316
  });
256
317
  normalized.set(providerKey, normalizedEntries);
257
318
  }
@@ -672,6 +733,7 @@ function strictUsage(event) {
672
733
  totalTokens: expectedTotal,
673
734
  cacheReadTokens,
674
735
  cacheCreationTokens,
736
+ ...projectUsagePricingContext(raw),
675
737
  };
676
738
  }
677
739
 
@@ -1101,6 +1163,7 @@ export function createVerifiedSessionObservabilityProjection(
1101
1163
  }),
1102
1164
  );
1103
1165
  addUsageSums(row, usage, "session usage model");
1166
+ mergeUsagePricingBucket(row, usage);
1104
1167
  }
1105
1168
 
1106
1169
  if (event?.type === "tool_call") {
@@ -0,0 +1,98 @@
1
+ import {
2
+ buildExtractiveHandoff,
3
+ formatStructuredHandoff,
4
+ } from "../harness/structured-handoff.js";
5
+ import {
6
+ DURABLE_SYSTEM_MESSAGE_KINDS,
7
+ getDurableSystemMessageProvenance,
8
+ markDurableSystemMessage,
9
+ projectCanonicalResumeMessages,
10
+ } from "./session-message-provenance.js";
11
+
12
+ export const HISTORY_SUMMARY_SCHEMA =
13
+ "chainlesschain.session-history-summary/v1";
14
+
15
+ function summaryMessage(messages, action, turnId) {
16
+ const handoff = buildExtractiveHandoff(messages, {
17
+ maxFallbackSourceChars: 24_000,
18
+ });
19
+ return markDurableSystemMessage(
20
+ {
21
+ role: "system",
22
+ content:
23
+ `[Conversation Summary: ${action} ${turnId}]\n` +
24
+ formatStructuredHandoff(handoff),
25
+ },
26
+ DURABLE_SYSTEM_MESSAGE_KINDS.CHECKPOINT_SUMMARY,
27
+ );
28
+ }
29
+
30
+ /**
31
+ * Deterministic v1 rewrite shared by the planner and verified history reader.
32
+ * Source indexes describe the conservative dependency of each output, not a
33
+ * claim that the bounded extractive summary quotes every source. Changes to
34
+ * this transform/formatter require a new history schema or a v1 reader.
35
+ */
36
+ export function projectCheckpointSummary({
37
+ messages,
38
+ action,
39
+ turnId,
40
+ start,
41
+ end,
42
+ }) {
43
+ if (
44
+ !Array.isArray(messages) ||
45
+ typeof turnId !== "string" ||
46
+ !turnId ||
47
+ !Number.isSafeInteger(start) ||
48
+ !Number.isSafeInteger(end) ||
49
+ start < 0 ||
50
+ start >= end ||
51
+ end > messages.length ||
52
+ !["summary-from", "summary-to"].includes(action)
53
+ )
54
+ throw new TypeError("Invalid checkpoint summary source range");
55
+ const copied = (begin, finish) =>
56
+ messages.slice(begin, finish).map((message, index) => ({
57
+ message,
58
+ sourceIndexes: [begin + index],
59
+ }));
60
+ const summarized = (entries) => ({
61
+ message: summaryMessage(
62
+ entries.map((entry) => entry.message),
63
+ action,
64
+ turnId,
65
+ ),
66
+ sourceIndexes: entries.flatMap((entry) => entry.sourceIndexes),
67
+ });
68
+ if (action === "summary-from") {
69
+ if (end !== messages.length || messages[start]?.role !== "user")
70
+ throw new TypeError("Invalid checkpoint summary suffix");
71
+ return [...copied(0, start), summarized(copied(start, end))];
72
+ }
73
+ if (
74
+ start !== (messages[0]?.role === "system" ? 1 : 0) ||
75
+ (end < messages.length && messages[end]?.role !== "user")
76
+ )
77
+ throw new TypeError("Invalid checkpoint summary prefix");
78
+
79
+ const prefix = messages.slice(start, end);
80
+ // Validate/sanitize before inspecting roles. Unmarked systems must neither
81
+ // survive the rewrite nor be quoted into a newly authorized summary.
82
+ const canonical = projectCanonicalResumeMessages(prefix, { strict: true });
83
+ const indexes = prefix.flatMap((message, index) =>
84
+ message.role !== "system" || getDurableSystemMessageProvenance(message)
85
+ ? [start + index]
86
+ : [],
87
+ );
88
+ const entries = canonical.map((message, index) => ({
89
+ message,
90
+ sourceIndexes: [indexes[index]],
91
+ }));
92
+ return [
93
+ ...copied(0, start),
94
+ ...entries.filter((entry) => entry.message.role === "system"),
95
+ summarized(entries.filter((entry) => entry.message.role !== "system")),
96
+ ...copied(end, messages.length),
97
+ ];
98
+ }