chainlesschain 0.166.77 → 0.166.78

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 (222) hide show
  1. package/README.md +16 -7
  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-DSyZEY2G.js} +1 -1
  12. package/src/assets/web-panel/assets/{ActionButton-CnU-Xvx-.js → ActionButton-DhNyZ577.js} +1 -1
  13. package/src/assets/web-panel/assets/{Analytics-BJW-CNtf.js → Analytics-HMMpxmyi.js} +3 -3
  14. package/src/assets/web-panel/assets/{AppLayout-UFQJL3G9.js → AppLayout-CwKMM5Ie.js} +5 -5
  15. package/src/assets/web-panel/assets/{Artifacts-Dr_IUDoY.js → Artifacts-CNzDmph7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Audit-aglWo6rr.js → Audit-DxWpUW3V.js} +1 -1
  17. package/src/assets/web-panel/assets/{BackgroundAgents-Dm_TCiXC.js → BackgroundAgents-C1R2ST-P.js} +1 -1
  18. package/src/assets/web-panel/assets/{Backup-DUdX4-gA.js → Backup-B6biTaEV.js} +1 -1
  19. package/src/assets/web-panel/assets/{BaseInput-BTcLjy1o.js → BaseInput-CVnkD2Z5.js} +1 -1
  20. package/src/assets/web-panel/assets/{Chat-Bhy9qZrl.js → Chat-Dbewl4Je.js} +6 -6
  21. package/src/assets/web-panel/assets/{ChatBubbleRenderer-D0WrJQaQ.js → ChatBubbleRenderer-DVZ6SwC5.js} +1 -1
  22. package/src/assets/web-panel/assets/{Checkbox-CblrPa21.js → Checkbox-BqqiF1JE.js} +1 -1
  23. package/src/assets/web-panel/assets/{Codegen-SEAHzOlr.js → Codegen-BVMkGumc.js} +1 -1
  24. package/src/assets/web-panel/assets/{Col-DJz8iFOR.js → Col-DmiRHy96.js} +1 -1
  25. package/src/assets/web-panel/assets/{Community-RufKhcQt.js → Community-BDlpygqA.js} +1 -1
  26. package/src/assets/web-panel/assets/{Compact-Dlifx08U.js → Compact-B2NME_m-.js} +1 -1
  27. package/src/assets/web-panel/assets/{Compliance-DdhQWcHh.js → Compliance-BlzDLE-R.js} +1 -1
  28. package/src/assets/web-panel/assets/{Cowork-BLNn6oOm.js → Cowork-C8raPybB.js} +3 -3
  29. package/src/assets/web-panel/assets/{Cron-Dv-JWY2d.js → Cron-DA8Py0zP.js} +2 -2
  30. package/src/assets/web-panel/assets/{Crosschain-k7Pz7O-b.js → Crosschain-DU2XyHqB.js} +1 -1
  31. package/src/assets/web-panel/assets/{DID-C_10syUW.js → DID-Df098fT_.js} +2 -2
  32. package/src/assets/web-panel/assets/{Dashboard-CMyH6Szx.js → Dashboard-D1rT702x.js} +2 -2
  33. package/src/assets/web-panel/assets/{Dropdown-DjmD2Saa.js → Dropdown-BweXGo1O.js} +1 -1
  34. package/src/assets/web-panel/assets/{EmailListRenderer-36_IRUSo.js → EmailListRenderer-B9ShbgJH.js} +1 -1
  35. package/src/assets/web-panel/assets/EvolutionSettings-D75_iBoc.js +1 -0
  36. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DEgf9kyP.js → FamilyGuardDashboard-DrznZFVs.js} +1 -1
  37. package/src/assets/web-panel/assets/{Federation-DGO1wQls.js → Federation-DQZ2adIA.js} +1 -1
  38. package/src/assets/web-panel/assets/{FormItemContext-Doj1FmKW.js → FormItemContext-DN30Dowh.js} +1 -1
  39. package/src/assets/web-panel/assets/{GenericCardRenderer-GzOU27iT.js → GenericCardRenderer-DRdt0j1e.js} +1 -1
  40. package/src/assets/web-panel/assets/{Git-Dd7D1EDK.js → Git-wwRgStzk.js} +2 -2
  41. package/src/assets/web-panel/assets/{Governance-6IY8LcjI.js → Governance-CI4Uw0Nh.js} +1 -1
  42. package/src/assets/web-panel/assets/{Inference-BB9gl8q1.js → Inference-5TuE2AJI.js} +1 -1
  43. package/src/assets/web-panel/assets/{KnowledgeGraph-CeCed5wd.js → KnowledgeGraph-oXyvmFSR.js} +1 -1
  44. package/src/assets/web-panel/assets/{Logs-CYOeHuaI.js → Logs-ewudSWuv.js} +2 -2
  45. package/src/assets/web-panel/assets/{MarkdownRenderer-Bk8Zqc2R.js → MarkdownRenderer-Cz5nyufm.js} +1 -1
  46. package/src/assets/web-panel/assets/{Marketplace-D-_xgHgM.js → Marketplace-DcZeWFrs.js} +1 -1
  47. package/src/assets/web-panel/assets/{McpTools-NnaUTF5t.js → McpTools-BcV9p1RS.js} +3 -3
  48. package/src/assets/web-panel/assets/{Memory-CndN9pMt.js → Memory-CMhYvugt.js} +2 -2
  49. package/src/assets/web-panel/assets/{MobileBridge-BXblFmhY.js → MobileBridge-5RP2uwvT.js} +1 -1
  50. package/src/assets/web-panel/assets/{MobileProjects-CC26Dm2k.js → MobileProjects-BIuM9Ze4.js} +1 -1
  51. package/src/assets/web-panel/assets/{Mtc-BhaqWCiW.js → Mtc-DmBszTDe.js} +4 -4
  52. package/src/assets/web-panel/assets/{MtcAudit-D6fktM2w.js → MtcAudit-Czdp5ZGt.js} +5 -5
  53. package/src/assets/web-panel/assets/{Multisig-CFvahz7s.js → Multisig-DRWUuNr-.js} +3 -3
  54. package/src/assets/web-panel/assets/{NLProgramming-Co73Ivya.js → NLProgramming-IRQQN0-c.js} +1 -1
  55. package/src/assets/web-panel/assets/{Notes-B4g33L1o.js → Notes-CGhD1lW9.js} +4 -4
  56. package/src/assets/web-panel/assets/{NotificationSettings-lXd17EbN.js → NotificationSettings-BtyPgQAh.js} +1 -1
  57. package/src/assets/web-panel/assets/{OrderTableRenderer-BiOB0GIi.js → OrderTableRenderer-yUaqwCty.js} +1 -1
  58. package/src/assets/web-panel/assets/{Organization-JQ7_wQIA.js → Organization-C3kTVn5i.js} +4 -4
  59. package/src/assets/web-panel/assets/{Overflow-gwgJgozQ.js → Overflow-C2-0iZhq.js} +1 -1
  60. package/src/assets/web-panel/assets/{P2P-D9L_0gie.js → P2P-CISoEUxA.js} +2 -2
  61. package/src/assets/web-panel/assets/{PdhVaultBrowser-CbOPsFE9.js → PdhVaultBrowser-NfTVtvKQ.js} +5 -5
  62. package/src/assets/web-panel/assets/{Permissions-Cz42HNQf.js → Permissions-CfK0jJhm.js} +4 -4
  63. package/src/assets/web-panel/assets/{PersonalDataHub-DjorYtrF.js → PersonalDataHub-OsVxxh_j.js} +5 -5
  64. package/src/assets/web-panel/assets/{Pipeline-DyWPhmm5.js → Pipeline-DEmK_Ch2.js} +1 -1
  65. package/src/assets/web-panel/assets/{Privacy-DyfLMl2j.js → Privacy-D61s73b0.js} +1 -1
  66. package/src/assets/web-panel/assets/{ProjectInit-CyqDx1OE.js → ProjectInit-DkLF5gHb.js} +2 -2
  67. package/src/assets/web-panel/assets/{ProjectSettings-DsBw0Csy.js → ProjectSettings-BA7odTrI.js} +2 -2
  68. package/src/assets/web-panel/assets/{Projects-CRaSI25Z.js → Projects-CdI1GcAG.js} +1 -1
  69. package/src/assets/web-panel/assets/{Providers-CCI_klN-.js → Providers-D9BYyP1r.js} +1 -1
  70. package/src/assets/web-panel/assets/{QrScannerModal-DNsnlDdS.js → QrScannerModal-CXMItUDD.js} +1 -1
  71. package/src/assets/web-panel/assets/{QuickAsk-B9r2iaee.js → QuickAsk-8-tF9ffZ.js} +1 -1
  72. package/src/assets/web-panel/assets/{Recommend-BJDe03GR.js → Recommend-C2QGWOSi.js} +1 -1
  73. package/src/assets/web-panel/assets/{RemoteSession-J7JR3rRo.js → RemoteSession-B1M_v1lx.js} +3 -3
  74. package/src/assets/web-panel/assets/{Reputation-DR39AJeV.js → Reputation-D0WZHgv1.js} +1 -1
  75. package/src/assets/web-panel/assets/{Row-Dh77m3NZ.js → Row-D6nf9-c-.js} +1 -1
  76. package/src/assets/web-panel/assets/{RssFeed-BUdg28nJ.js → RssFeed-ClZ6M-mN.js} +3 -3
  77. package/src/assets/web-panel/assets/{Search-BNC7sGhj.js → Search-ZWkt8vuO.js} +1 -1
  78. package/src/assets/web-panel/assets/{Security-DISENGI7.js → Security-BUlQut2m.js} +4 -4
  79. package/src/assets/web-panel/assets/{Services-BBBGpJ1-.js → Services-Cd_3xJOc.js} +2 -2
  80. package/src/assets/web-panel/assets/{Skeleton-DMcIoIv2.js → Skeleton-C8e2-cHW.js} +1 -1
  81. package/src/assets/web-panel/assets/{Skills-QAyFbI3r.js → Skills-DucVRJp1.js} +1 -1
  82. package/src/assets/web-panel/assets/{Sla-CSg5wLH7.js → Sla-Csr5pUwI.js} +1 -1
  83. package/src/assets/web-panel/assets/{SpeechSettings-DMPxTc5x.js → SpeechSettings-CXvyaRAT.js} +1 -1
  84. package/src/assets/web-panel/assets/{SyncSettings-CPG7l30y.js → SyncSettings-KdsIcV5h.js} +2 -2
  85. package/src/assets/web-panel/assets/{Tasks-Y1kOVvVL.js → Tasks-B-eHmJm9.js} +1 -1
  86. package/src/assets/web-panel/assets/{Templates-jt9NagL0.js → Templates-BglVeaLK.js} +1 -1
  87. package/src/assets/web-panel/assets/{Tenant-DauuJICx.js → Tenant-BBbQ8qan.js} +1 -1
  88. package/src/assets/web-panel/assets/{Terminal-E9rnKeGq.js → Terminal-Ca_pCVBi.js} +2 -2
  89. package/src/assets/web-panel/assets/{TimelineRenderer-Cc7z2APG.js → TimelineRenderer-YD6XG_vZ.js} +1 -1
  90. package/src/assets/web-panel/assets/{Tokens-CV0UthRA.js → Tokens-2lRoeVli.js} +1 -1
  91. package/src/assets/web-panel/assets/{Trigger-BFcJJGfu.js → Trigger-C-3XNAU9.js} +1 -1
  92. package/src/assets/web-panel/assets/{Trust-BAydbNJA.js → Trust-k-1PxdLG.js} +1 -1
  93. package/src/assets/web-panel/assets/{UkeySign-DcOS6sqL.js → UkeySign-FG-qW646.js} +1 -1
  94. package/src/assets/web-panel/assets/{VideoEditing-BLHJZvif.js → VideoEditing-BNOe1cmd.js} +1 -1
  95. package/src/assets/web-panel/assets/{Wallet-4QFLKHwW.js → Wallet-D5C3i2Kw.js} +4 -4
  96. package/src/assets/web-panel/assets/{WebAuthn-BocEOOSj.js → WebAuthn-BOpztqd3.js} +5 -5
  97. package/src/assets/web-panel/assets/{WorkflowEditor-DCfF7D0B.js → WorkflowEditor-BpVrpUEv.js} +1 -1
  98. package/src/assets/web-panel/assets/{chat-CxvfsYGr.js → chat-DIuMHb38.js} +1 -1
  99. package/src/assets/web-panel/assets/{colors-KtQB9DmQ.js → colors-CTFJPyzQ.js} +1 -1
  100. package/src/assets/web-panel/assets/{compact-item-Dx4J6arN.js → compact-item-CONfmJcW.js} +1 -1
  101. package/src/assets/web-panel/assets/{createContext-Cq0olgzo.js → createContext-RkW5CRrz.js} +1 -1
  102. package/src/assets/web-panel/assets/devWarning-DaEBln_q.js +1 -0
  103. package/src/assets/web-panel/assets/{hasIn-Bf9-h3D-.js → hasIn-CW4XOjDC.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CK2U_AHi.js → index-7wMSBCU7.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DMR-keCL.js → index-Akmg1WdH.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-6dDDSCHD.js → index-B54qoVff.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CmQuG0fr.js → index-B8r2t0Ch.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-ClUfSChI.js → index-B9XoBoty.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B3xfiItB.js → index-B9aS2VLu.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DQDR-_ai.js → index-BFs1Tznc.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DZWjYfWt.js → index-BKgaRT0b.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-nmyDOh_b.js → index-BM7bOUvu.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B_8UcDan.js → index-BMg2JFmW.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C9yaUHm9.js → index-BRZsPxNn.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cz7f4Jaq.js → index-Bf19fKef.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CpO6DKuu.js → index-BkAbfBF-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DFdEDvFn.js → index-CBjb2A5R.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-gt5N6VM_.js → index-CEZWQthY.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-0fi8DIVQ.js → index-CFGLf88d.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-hSzyoXri.js → index-CLUa68JH.js} +3 -3
  121. package/src/assets/web-panel/assets/{index-Cy865Hhq.js → index-CM2X25Er.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DDAe1UmZ.js → index-CX4T2uKN.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BMcC4cbj.js → index-CixqfdRk.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DsCdl2Od.js → index-CkRojEip.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DnU-dUWd.js → index-Cvmc3f25.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Br1mDMfO.js → index-D-lpTPS8.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CaIclw-E.js → index-D59GbWCN.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DBRlZdZj.js → index-DAESljiJ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-B8WuPI-h.js → index-DDHUx4Yz.js} +1 -1
  130. package/src/assets/web-panel/assets/index-DG1mcYTa.js +1 -0
  131. package/src/assets/web-panel/assets/{index-D2BfSIrn.js → index-DP5pPedy.js} +1 -1
  132. package/src/assets/web-panel/assets/index-DRs5Pe8u.js +1 -0
  133. package/src/assets/web-panel/assets/{index-CqnggDos.js → index-DSlXL6Ju.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-_FIz-0tp.js → index-DWCJnn2O.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-BKtQyb2o.js → index-DWY7YwDu.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-DfrFNMvz.js → index-DZTwP364.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-BHmdMKYl.js → index-DidrMvOx.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-BO6XvO-C.js → index-Dj3K5kHs.js} +1 -1
  139. package/src/assets/web-panel/assets/{index-iE9tqEN5.js → index-YaeGscSw.js} +1 -1
  140. package/src/assets/web-panel/assets/{index-DI5bRqNG.js → index-YlMnFNOi.js} +1 -1
  141. package/src/assets/web-panel/assets/{index-Cfsn7N18.js → index-asWsB34i.js} +1 -1
  142. package/src/assets/web-panel/assets/{index-CeYP_kT9.js → index-gkjiFjQd.js} +1 -1
  143. package/src/assets/web-panel/assets/{initDefaultProps-DvzKJ6o5.js → initDefaultProps-BOr4igh3.js} +1 -1
  144. package/src/assets/web-panel/assets/{motion-D1yTvmxW.js → motion-CK469n2j.js} +1 -1
  145. package/src/assets/web-panel/assets/{move-DxtSIF8K.js → move-DzjudlYQ.js} +1 -1
  146. package/src/assets/web-panel/assets/{mtc-parser-BDsQgIHY.js → mtc-parser-BQaKUxC6.js} +1 -1
  147. package/src/assets/web-panel/assets/{omit-UVBJW8kh.js → omit-MciovCFD.js} +1 -1
  148. package/src/assets/web-panel/assets/{pickAttrs-D4QqtTyX.js → pickAttrs-DDPJ4zAz.js} +1 -1
  149. package/src/assets/web-panel/assets/{placementArrow-QXmu6vBf.js → placementArrow-Cznyb-jI.js} +1 -1
  150. package/src/assets/web-panel/assets/{responsiveObserve-D-PmXO9M.js → responsiveObserve-AJK3SLzJ.js} +1 -1
  151. package/src/assets/web-panel/assets/{slide-CKbGxEZJ.js → slide-CPQ28wu7.js} +1 -1
  152. package/src/assets/web-panel/assets/{statusUtils-BbgXPkXm.js → statusUtils-CFjzvqf_.js} +1 -1
  153. package/src/assets/web-panel/assets/{styleChecker-DOr9rk68.js → styleChecker-BPyFfTuR.js} +1 -1
  154. package/src/assets/web-panel/assets/{useFlexGapSupport-B2wO2r36.js → useFlexGapSupport-Dq7N6R05.js} +1 -1
  155. package/src/assets/web-panel/assets/{useFs-SeVGBK34.js → useFs-C3GS0uTc.js} +1 -1
  156. package/src/assets/web-panel/assets/{usePersonalDataHub-DUJBCxjD.js → usePersonalDataHub-BhgIVzP_.js} +1 -1
  157. package/src/assets/web-panel/assets/{vnode-CF4EYsig.js → vnode-4ZD9-MDr.js} +1 -1
  158. package/src/assets/web-panel/assets/{zoom-pRUs5pqI.js → zoom-Bx4MRVNW.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 +13 -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/app-server/server.js +14 -0
  174. package/src/lib/background-agent-supervisor.js +14 -4
  175. package/src/lib/causal-observability.js +64 -1
  176. package/src/lib/checkpoint-summary-projection.js +98 -0
  177. package/src/lib/checkpoint-timeline-authority.js +15 -51
  178. package/src/lib/claude-code-bridge.js +302 -60
  179. package/src/lib/codex-app-server-adapter.js +161 -35
  180. package/src/lib/cost-budget.js +4 -0
  181. package/src/lib/doctor-checkup.js +31 -2
  182. package/src/lib/durable-security-store.js +6 -1
  183. package/src/lib/evolution/evolution-artifact-identity.js +21 -0
  184. package/src/lib/evolution/evolution-artifact-ports.js +5 -11
  185. package/src/lib/evolution/evolution-eval-cohort-receipt-reconciliation.js +389 -0
  186. package/src/lib/execution-location-failure-site.js +63 -0
  187. package/src/lib/execution-location-target.js +102 -8
  188. package/src/lib/external-agent-adapters.js +2 -0
  189. package/src/lib/image-file-boundary.js +154 -0
  190. package/src/lib/image-input.js +16 -3
  191. package/src/lib/llm-pricing.js +114 -8
  192. package/src/lib/mcp-call-ledger-store.js +7 -2
  193. package/src/lib/model-capabilities.js +21 -10
  194. package/src/lib/model-context-catalog.js +60 -1
  195. package/src/lib/openai-responses.js +7 -1
  196. package/src/lib/orchestrator.js +8 -2
  197. package/src/lib/process-execution-broker/index.js +93 -1
  198. package/src/lib/process-execution-broker/linux-subreaper-artifact.js +120 -0
  199. package/src/lib/process-execution-broker/linux-subreaper-helper.js +344 -0
  200. package/src/lib/process-execution-broker/linux-subreaper-process.js +326 -0
  201. package/src/lib/process-execution-broker/linux-subreaper-supervisor.c +360 -0
  202. package/src/lib/process-execution-broker/owned-posix-process-group-worker.mjs +131 -0
  203. package/src/lib/process-execution-broker/owned-posix-process-group.js +267 -0
  204. package/src/lib/process-execution-broker/process-ownership-journal.js +297 -0
  205. package/src/lib/process-execution-broker/process-ownership-quarantine.js +91 -0
  206. package/src/lib/runtime-usage-ledger.js +3 -0
  207. package/src/lib/secure-fs.js +158 -42
  208. package/src/lib/session-branch-history.js +240 -0
  209. package/src/lib/session-history-origins.js +285 -0
  210. package/src/lib/session-input-receipt.js +110 -0
  211. package/src/lib/session-resource-budget.js +8 -8
  212. package/src/lib/session-transcript-history.js +456 -0
  213. package/src/lib/session-transcript-page.js +164 -0
  214. package/src/lib/session-transcript-reference.js +38 -0
  215. package/src/lib/session-usage.js +8 -0
  216. package/src/lib/token-tracker.js +32 -54
  217. package/src/lib/usage-pricing-context.js +93 -0
  218. package/src/runtime/headless-stream.js +202 -4
  219. package/src/assets/web-panel/assets/EvolutionSettings-CmtU0XmM.js +0 -1
  220. package/src/assets/web-panel/assets/devWarning-CmgDNkoO.js +0 -1
  221. package/src/assets/web-panel/assets/index-BpIZ2S01.js +0 -1
  222. package/src/assets/web-panel/assets/index-C-VmKuyv.js +0 -1
@@ -0,0 +1,456 @@
1
+ import { canonicalDigest } from "@chainlesschain/context-memory-kernel";
2
+ import {
3
+ readVerifiedProjection,
4
+ projectWsTurnMessages,
5
+ } from "../harness/jsonl-session-store.js";
6
+ import { contextItemsToMessages } from "./context-memory-kernel/message-adapter.js";
7
+ import {
8
+ displayTranscriptText,
9
+ transcriptTextParts,
10
+ boundTranscriptRow,
11
+ } from "./session-transcript-page.js";
12
+ import { createSessionHistoryOrigins } from "./session-history-origins.js";
13
+ import { getDurableSystemMessageProvenance } from "./session-message-provenance.js";
14
+ import {
15
+ BRANCH_HISTORY_SCHEMA,
16
+ BRANCH_HISTORY_SYSTEM_ORIGINS_SCHEMA,
17
+ BRANCH_HISTORY_MESSAGE,
18
+ branchContextDigest,
19
+ withSessionBranchHistory,
20
+ createBranchHistoryImport,
21
+ } from "./session-branch-history.js";
22
+
23
+ const HASH = /^[a-f0-9]{64}$/u;
24
+ const SCHEMA = "chainlesschain.session-transcript-page/v2";
25
+ const CHANGES_SCHEMA = "chainlesschain.session-transcript-changes/v1";
26
+ const MAX_PAGE_BYTES = 1024 * 1024;
27
+ const MAX_HISTORY_RANGES = 16384;
28
+
29
+ // Enumerate all possible display sources, including replacement snapshots.
30
+ // Their physical sequence never changes when the selected history rewinds.
31
+ function eventMessages(event, imported = null) {
32
+ if (event.type === BRANCH_HISTORY_MESSAGE)
33
+ return imported?.kind === "archive" ? [imported.message] : [];
34
+ if (
35
+ ["compact", "checkpoint_timeline_commit"].includes(event.type) &&
36
+ Array.isArray(event.data?.messages)
37
+ )
38
+ return event.data.messages;
39
+ if (["user_message", "assistant_message", "system"].includes(event.type))
40
+ return [event.data];
41
+ if (event.type === "ws_turn") {
42
+ const pair = projectWsTurnMessages(event);
43
+ if (pair) return [pair.user, pair.assistant];
44
+ }
45
+ return [];
46
+ }
47
+ const displayable = (message) =>
48
+ ["user", "assistant", "tool"].includes(message?.role);
49
+
50
+ // compact also historically meant import, rewind or a session-end snapshot.
51
+ // Only a verified Kernel compaction payload proves this is a context update.
52
+ // A copied fork retains the source payload, so do not compare its sessionId to
53
+ // the destination namespace (the store independently verifies fork authority).
54
+ function isContextCompaction(data) {
55
+ const canonical = data?.canonical;
56
+ if (
57
+ canonical?.schema !== "chainlesschain.context-compaction/v1" ||
58
+ canonical.schemaVersion !== 1 ||
59
+ !["committed", "degraded"].includes(canonical.status) ||
60
+ !Array.isArray(canonical.outputItems)
61
+ )
62
+ return false;
63
+ try {
64
+ const { digest, ...body } = canonical;
65
+ if (canonicalDigest(body, "chainlesschain.compaction-event/v1") !== digest)
66
+ return false;
67
+ const content = (messages) =>
68
+ messages
69
+ .filter((message) => message?.role !== "system")
70
+ .map(({ role, content, tool_calls, tool_call_id }) => ({
71
+ role,
72
+ content,
73
+ tool_calls,
74
+ tool_call_id,
75
+ }));
76
+ return (
77
+ canonicalDigest(
78
+ content(contextItemsToMessages(canonical.outputItems)),
79
+ ) === canonicalDigest(content(data.messages))
80
+ );
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+
86
+ function staleCursor() {
87
+ const error = new Error("Transcript history changed; reload the latest page");
88
+ error.code = "SESSION_TRANSCRIPT_CURSOR_STALE";
89
+ return error;
90
+ }
91
+
92
+ export function createSessionTranscriptHistoryProjection(
93
+ sessionId,
94
+ { limit = 50, cursor = null, after = null } = {},
95
+ branch = null,
96
+ ) {
97
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
98
+ throw new TypeError("transcript page limit must be 1–100");
99
+ if (cursor !== null && after !== null)
100
+ throw new TypeError(
101
+ "History before and after cursors are mutually exclusive",
102
+ );
103
+ const incremental = after !== null;
104
+ const encodedCursor = incremental ? after : cursor;
105
+ let anchor = null;
106
+ if (encodedCursor !== null) {
107
+ if (
108
+ typeof encodedCursor !== "string" ||
109
+ !/^[\w-]{1,1024}$/u.test(encodedCursor)
110
+ )
111
+ throw new TypeError("invalid transcript history cursor");
112
+ try {
113
+ anchor = JSON.parse(
114
+ Buffer.from(encodedCursor, "base64url").toString("utf8"),
115
+ );
116
+ } catch {
117
+ throw new TypeError("invalid transcript history cursor");
118
+ }
119
+ if (
120
+ !anchor ||
121
+ anchor.sessionId !== sessionId ||
122
+ !HASH.test(anchor.generation) ||
123
+ !HASH.test(anchor.revision) ||
124
+ !Number.isSafeInteger(anchor.eventCount) ||
125
+ anchor.eventCount < 1 ||
126
+ (incremental
127
+ ? anchor.v !== 1 ||
128
+ anchor.view !== "history-sync" ||
129
+ !Number.isSafeInteger(anchor.offset) ||
130
+ anchor.offset < 0
131
+ : anchor.v !== 2 ||
132
+ !Number.isSafeInteger(anchor.before) ||
133
+ anchor.before < 1)
134
+ )
135
+ throw new TypeError("invalid transcript history cursor");
136
+ }
137
+ let generation = null;
138
+ let eventCount = 0;
139
+ let count = 0;
140
+ let bytes = 0;
141
+ let rows = [];
142
+ let incrementalFull = false;
143
+ let anchored = !anchor;
144
+ let coverage = { kind: "from-origin", boundaryEvent: null, reason: null };
145
+ const origins = createSessionHistoryOrigins();
146
+ const branchImport = createBranchHistoryImport();
147
+ let physical = 0;
148
+ let ranges = [];
149
+ let needsReplay = false;
150
+
151
+ function displayRow(message, event, index, ordinal) {
152
+ return boundTranscriptRow({
153
+ id: `${sessionId}:${event.hash}:${index}`,
154
+ eventId: event.hash,
155
+ itemIndex: index,
156
+ ordinal,
157
+ role: message.role,
158
+ ...displayTranscriptText(message.content),
159
+ });
160
+ }
161
+ function collect(message, event, index, ordinal) {
162
+ if (incremental) {
163
+ if (ordinal < anchor.offset || incrementalFull) return;
164
+ } else if (anchor && ordinal >= anchor.before) return;
165
+ const row = displayRow(message, event, index, ordinal);
166
+ const rowBytes = Buffer.byteLength(JSON.stringify(row));
167
+ if (
168
+ incremental &&
169
+ (rows.length >= limit || bytes + rowBytes > MAX_PAGE_BYTES)
170
+ ) {
171
+ // Keep the FIRST contiguous prefix after the cursor. Skipping an oversized
172
+ // next row and admitting later small rows would permanently omit messages.
173
+ incrementalFull = true;
174
+ return;
175
+ }
176
+ rows.push(row);
177
+ bytes += rowBytes;
178
+ while (rows.length > limit || bytes > MAX_PAGE_BYTES)
179
+ bytes -= Buffer.byteLength(JSON.stringify(rows.shift()));
180
+ }
181
+ function append(message, event, index) {
182
+ if (!displayable(message)) return;
183
+ if (ranges.at(-1)?.end === physical) ranges.at(-1).end += 1;
184
+ else {
185
+ if (ranges.length >= MAX_HISTORY_RANGES) {
186
+ const error = new Error(
187
+ "Transcript history has too many disjoint ranges",
188
+ );
189
+ error.code = "SESSION_TRANSCRIPT_HISTORY_LIMIT";
190
+ throw error;
191
+ }
192
+ ranges.push({ start: physical, end: physical + 1 });
193
+ }
194
+ physical += 1;
195
+ collect(message, event, index, count++);
196
+ }
197
+ function rewind(cutoff) {
198
+ let remaining = cutoff;
199
+ ranges = ranges.filter((range) => {
200
+ if (remaining === 0) return false;
201
+ const kept = Math.min(remaining, range.end - range.start);
202
+ range.end = range.start + kept;
203
+ remaining -= kept;
204
+ return true;
205
+ });
206
+ count = cutoff;
207
+ rows = [];
208
+ bytes = 0;
209
+ incrementalFull = false;
210
+ needsReplay = true;
211
+ }
212
+ function boundary(event, reason) {
213
+ coverage = { kind: "snapshot-boundary", boundaryEvent: event.hash, reason };
214
+ }
215
+ function visitSelected(authority, visit) {
216
+ let source = 0;
217
+ let rangeIndex = 0;
218
+ let ordinal = 0;
219
+ const replayImport = createBranchHistoryImport();
220
+ authority.replayEvents((event) => {
221
+ const imported = replayImport.accept(event);
222
+ eventMessages(event, imported).forEach((message, index) => {
223
+ if (!displayable(message)) return;
224
+ while (ranges[rangeIndex] && source >= ranges[rangeIndex].end)
225
+ rangeIndex += 1;
226
+ const range = ranges[rangeIndex];
227
+ if (range && source >= range.start)
228
+ visit(message, event, index, ordinal++);
229
+ source += 1;
230
+ });
231
+ });
232
+ replayImport.finish();
233
+ if (ordinal !== count || source !== physical)
234
+ throw new Error("Transcript history replay did not match its selection");
235
+ }
236
+ return {
237
+ accept(event) {
238
+ if (!HASH.test(event.hash))
239
+ throw new TypeError("verified event hash required");
240
+ eventCount += 1;
241
+ generation ||= event.hash;
242
+ const imported = branchImport.accept(event);
243
+ const messages = eventMessages(event, imported);
244
+ if (imported?.kind === "start") {
245
+ coverage = { ...imported.coverage };
246
+ } else if (imported?.kind === "archive") {
247
+ append(messages[0], event, 0);
248
+ } else if (imported?.kind === "archive-part") {
249
+ // A logical row may span several bounded persistence records.
250
+ } else if (imported?.kind === "context") {
251
+ physical += messages.filter(displayable).length;
252
+ } else if (imported?.kind === "origin") {
253
+ origins.appendMappedPersisted(imported.message, imported.origin);
254
+ } else if (imported?.kind === "complete") {
255
+ // The complete import is independently anchored in this session.
256
+ } else if (
257
+ ["compact", "checkpoint_timeline_commit"].includes(event.type) &&
258
+ Array.isArray(event.data?.messages)
259
+ ) {
260
+ const cutoff =
261
+ event.type === "checkpoint_timeline_commit"
262
+ ? origins.rewind(event, count)
263
+ : null;
264
+ if (cutoff !== null) {
265
+ rewind(cutoff);
266
+ generation = event.hash;
267
+ physical += messages.filter(displayable).length;
268
+ } else if (
269
+ event.type === "checkpoint_timeline_commit" &&
270
+ origins.summarize(event)
271
+ ) {
272
+ // The verified rewrite changes model context only. Original rows,
273
+ // coverage and old display cursors continue to describe the archive.
274
+ physical += messages.filter(displayable).length;
275
+ } else if (
276
+ event.type === "compact" &&
277
+ count > 0 &&
278
+ isContextCompaction(event.data)
279
+ ) {
280
+ origins.compact(event.data);
281
+ physical += messages.filter(displayable).length;
282
+ } else {
283
+ // Legacy or unverifiable lineage can authorize only its snapshot.
284
+ generation = event.hash;
285
+ count = 0;
286
+ bytes = 0;
287
+ rows = [];
288
+ incrementalFull = false;
289
+ ranges = [];
290
+ needsReplay = false;
291
+ origins.snapshot(messages);
292
+ boundary(
293
+ event,
294
+ event.type === "checkpoint_timeline_commit"
295
+ ? "timeline-replacement"
296
+ : "context-snapshot",
297
+ );
298
+ messages.forEach((message, index) => append(message, event, index));
299
+ }
300
+ } else if (event.type === "session_branch") {
301
+ boundary(event, "branch-snapshot");
302
+ } else {
303
+ messages.forEach((message, index) => {
304
+ origins.appendPersisted(message, count);
305
+ append(message, event, index);
306
+ });
307
+ }
308
+ if (anchor && eventCount === anchor.eventCount) {
309
+ anchored =
310
+ event.hash === anchor.revision &&
311
+ (incremental ? anchor.offset : anchor.before) <= count &&
312
+ generation === anchor.generation;
313
+ }
314
+ },
315
+ finish(authority) {
316
+ branchImport.finish();
317
+ if (branch) {
318
+ const prefix = origins.branchPrefix(
319
+ branch.messages,
320
+ authority.headHash,
321
+ count,
322
+ );
323
+ if (!prefix) return branch.consume(null);
324
+ rewind(prefix.cutoff);
325
+ const contextEntries = prefix.entries.filter(
326
+ ({ message }) =>
327
+ ["user", "assistant"].includes(message.role) ||
328
+ (message.role === "system" &&
329
+ getDurableSystemMessageProvenance(message)),
330
+ );
331
+ const contextOrigins = contextEntries.map(({ origin }) => origin);
332
+ return withSessionBranchHistory(
333
+ {
334
+ parentSessionId: sessionId,
335
+ contextDigest: branchContextDigest(branch.messages),
336
+ contextOrigins,
337
+ descriptor: {
338
+ schema: contextEntries.some(
339
+ ({ message, origin }) => message.role === "system" && origin,
340
+ )
341
+ ? BRANCH_HISTORY_SYSTEM_ORIGINS_SCHEMA
342
+ : BRANCH_HISTORY_SCHEMA,
343
+ sourceGeneration: generation,
344
+ totalMessages: count,
345
+ contextMessageCount: contextOrigins.length,
346
+ coverage: { ...coverage },
347
+ },
348
+ visit(visitor) {
349
+ visitSelected(authority, (message, event, index) => {
350
+ visitor({
351
+ role: message.role,
352
+ text: transcriptTextParts(message.content).join(""),
353
+ sourceEventId: event.hash,
354
+ sourceItemIndex: index,
355
+ });
356
+ });
357
+ },
358
+ },
359
+ branch.consume,
360
+ );
361
+ }
362
+ if (anchor && (!anchored || generation !== anchor.generation))
363
+ throw staleCursor();
364
+ if (needsReplay) {
365
+ // Scan under the same store lock and revalidate the same exact head.
366
+ // Even a rewind into a prefix older than the page buffer stays bounded.
367
+ rows = [];
368
+ bytes = 0;
369
+ incrementalFull = false;
370
+ visitSelected(authority, collect);
371
+ }
372
+ const first = rows[0]?.ordinal ?? 0;
373
+ const snapshot = {
374
+ sessionId,
375
+ generation,
376
+ revision: authority.headHash,
377
+ eventCount: authority.eventCount,
378
+ totalMessages: count,
379
+ messages: rows,
380
+ contextOnly: false,
381
+ coverage,
382
+ };
383
+ const syncCursor = (offset) =>
384
+ Buffer.from(
385
+ JSON.stringify({
386
+ v: 1,
387
+ view: "history-sync",
388
+ sessionId,
389
+ generation,
390
+ revision: authority.headHash,
391
+ eventCount: authority.eventCount,
392
+ offset,
393
+ }),
394
+ ).toString("base64url");
395
+ if (incremental) {
396
+ const nextOffset = rows.length
397
+ ? rows.at(-1).ordinal + 1
398
+ : anchor.offset;
399
+ return {
400
+ schema: CHANGES_SCHEMA,
401
+ ...snapshot,
402
+ from: anchor.offset,
403
+ nextCursor: syncCursor(nextOffset),
404
+ hasMore: nextOffset < count,
405
+ };
406
+ }
407
+ return {
408
+ schema: SCHEMA,
409
+ ...snapshot,
410
+ // Only the latest page establishes an update baseline. Older navigation
411
+ // must not advance it past messages the latest view has not received.
412
+ syncCursor:
413
+ !anchor && authority.eventCount > 0 ? syncCursor(count) : null,
414
+ nextCursor:
415
+ first > 0
416
+ ? Buffer.from(
417
+ JSON.stringify({
418
+ v: 2,
419
+ sessionId,
420
+ generation,
421
+ revision: authority.headHash,
422
+ eventCount: authority.eventCount,
423
+ before: first,
424
+ }),
425
+ ).toString("base64url")
426
+ : null,
427
+ };
428
+ },
429
+ };
430
+ }
431
+
432
+ export function createSessionTranscriptBranchProjection(
433
+ sessionId,
434
+ messages,
435
+ consume,
436
+ ) {
437
+ if (
438
+ !Array.isArray(messages) ||
439
+ typeof consume !== "function" ||
440
+ consume.constructor?.name === "AsyncFunction"
441
+ )
442
+ throw new TypeError(
443
+ "Branch history requires messages and a synchronous consumer",
444
+ );
445
+ return createSessionTranscriptHistoryProjection(
446
+ sessionId,
447
+ {},
448
+ { messages, consume },
449
+ );
450
+ }
451
+
452
+ export function readSessionTranscriptHistory(sessionId, options = {}) {
453
+ return readVerifiedProjection(sessionId, () =>
454
+ createSessionTranscriptHistoryProjection(sessionId, options),
455
+ );
456
+ }
@@ -0,0 +1,164 @@
1
+ import { createHash } from "node:crypto";
2
+ import {
3
+ readVerifiedProjection,
4
+ projectWsTurnMessages,
5
+ } from "../harness/jsonl-session-store.js";
6
+
7
+ const MAX_PAGE_BYTES = 1024 * 1024;
8
+ const MAX_TEXT_CHARS = 200000;
9
+
10
+ export function transcriptTextParts(content) {
11
+ return typeof content === "string"
12
+ ? [content]
13
+ : Array.isArray(content)
14
+ ? content.map((part) =>
15
+ typeof part?.text === "string"
16
+ ? part.text
17
+ : ["image", "image_url", "input_image"].includes(part?.type)
18
+ ? "[image]"
19
+ : "",
20
+ )
21
+ : [];
22
+ }
23
+
24
+ export function displayTranscriptText(content) {
25
+ const parts = transcriptTextParts(content);
26
+ let text = "";
27
+ let truncated = false;
28
+ for (const part of parts) {
29
+ const remaining = MAX_TEXT_CHARS - text.length;
30
+ text += part.slice(0, remaining);
31
+ if (part.length > remaining) truncated = true;
32
+ }
33
+ return { text, truncated };
34
+ }
35
+
36
+ // Escaped control characters can exceed the JSON byte budget before the
37
+ // character limit. Keep a shortened row rather than silently dropping it.
38
+ export function boundTranscriptRow(row) {
39
+ if (Buffer.byteLength(JSON.stringify(row)) <= MAX_PAGE_BYTES) return row;
40
+ const text = row.text;
41
+ row.truncated = true;
42
+ let low = 0;
43
+ let high = text.length - 1;
44
+ while (low < high) {
45
+ const middle = Math.ceil((low + high) / 2);
46
+ row.text = text.slice(0, middle);
47
+ if (Buffer.byteLength(JSON.stringify(row)) <= MAX_PAGE_BYTES) low = middle;
48
+ else high = middle - 1;
49
+ }
50
+ row.text = text.slice(0, low);
51
+ return row;
52
+ }
53
+
54
+ export function createSessionTranscriptPageProjection(
55
+ sessionId,
56
+ { limit = 50, cursor = null } = {},
57
+ ) {
58
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
59
+ throw new TypeError("transcript page limit must be 1–100");
60
+ let before = Infinity;
61
+ let expectedGeneration = null;
62
+ if (cursor != null) {
63
+ if (
64
+ typeof cursor !== "string" ||
65
+ cursor.length > 1024 ||
66
+ !/^[\w-]+$/u.test(cursor)
67
+ )
68
+ throw new TypeError("invalid transcript cursor");
69
+ const value = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
70
+ if (
71
+ value.sessionId !== sessionId ||
72
+ !Number.isSafeInteger(value.before) ||
73
+ value.before < 0 ||
74
+ !/^[a-f0-9]{64}$/u.test(value.generation)
75
+ )
76
+ throw new TypeError("invalid transcript cursor");
77
+ before = value.before;
78
+ expectedGeneration = value.generation;
79
+ }
80
+ let generation = null;
81
+ let count = 0;
82
+ let bytes = 0;
83
+ let rows = [];
84
+ const identity = (event) =>
85
+ /^[a-f0-9]{64}$/u.test(event.hash || "")
86
+ ? event.hash
87
+ : createHash("sha256").update(JSON.stringify(event)).digest("hex");
88
+ function append(message) {
89
+ if (!["user", "assistant", "tool"].includes(message?.role)) return;
90
+ const ordinal = count++;
91
+ if (ordinal >= before) return;
92
+ const content = displayTranscriptText(message.content);
93
+ const row = boundTranscriptRow({
94
+ id: `${generation}:${ordinal}`,
95
+ ordinal,
96
+ role: message.role,
97
+ ...content,
98
+ });
99
+ rows.push(row);
100
+ bytes += Buffer.byteLength(JSON.stringify(row));
101
+ while (rows.length > limit || bytes > MAX_PAGE_BYTES)
102
+ bytes -= Buffer.byteLength(JSON.stringify(rows.shift()));
103
+ }
104
+ return {
105
+ accept(event) {
106
+ if (!generation) generation = identity(event);
107
+ if (
108
+ (event.type === "compact" ||
109
+ event.type === "checkpoint_timeline_commit") &&
110
+ Array.isArray(event.data?.messages)
111
+ ) {
112
+ // Match canonical resume semantics. A rewind or replacement checkpoint
113
+ // invalidates every old cursor; never revive discarded messages.
114
+ generation = identity(event);
115
+ count = 0;
116
+ bytes = 0;
117
+ rows = [];
118
+ for (const message of event.data.messages) append(message);
119
+ } else if (
120
+ ["user_message", "assistant_message", "system"].includes(event.type)
121
+ )
122
+ append(event.data);
123
+ else if (event.type === "ws_turn") {
124
+ const pair = projectWsTurnMessages(event);
125
+ if (pair) {
126
+ append(pair.user);
127
+ append(pair.assistant);
128
+ }
129
+ }
130
+ },
131
+ finish(authority) {
132
+ if (expectedGeneration && generation !== expectedGeneration) {
133
+ const error = new Error(
134
+ "Transcript generation changed; reload the latest page",
135
+ );
136
+ error.code = "SESSION_TRANSCRIPT_CURSOR_STALE";
137
+ throw error;
138
+ }
139
+ const first = rows[0]?.ordinal ?? 0;
140
+ return {
141
+ schema: "chainlesschain.session-transcript-page/v1",
142
+ sessionId,
143
+ generation,
144
+ revision: authority.headHash,
145
+ eventCount: authority.eventCount,
146
+ totalMessages: count,
147
+ messages: rows,
148
+ nextCursor:
149
+ first > 0
150
+ ? Buffer.from(
151
+ JSON.stringify({ sessionId, generation, before: first }),
152
+ ).toString("base64url")
153
+ : null,
154
+ contextOnly: true,
155
+ };
156
+ },
157
+ };
158
+ }
159
+
160
+ export function readSessionTranscriptPage(sessionId, options = {}) {
161
+ return readVerifiedProjection(sessionId, () =>
162
+ createSessionTranscriptPageProjection(sessionId, options),
163
+ );
164
+ }
@@ -0,0 +1,38 @@
1
+ const HASH = /^[a-f0-9]{64}$/u;
2
+ const hash = (value) => typeof value === "string" && HASH.test(value);
3
+
4
+ /**
5
+ * Display association only. Message writers return a synchronous append receipt,
6
+ * not the event body. The caller determines the role from the writer it invoked;
7
+ * consumers still need the history reader's full chain/anchor verification.
8
+ */
9
+ export function transcriptMessageEventId(receipt) {
10
+ if (!receipt || receipt.commitState !== "committed" || !hash(receipt.hash))
11
+ return null;
12
+ return receipt.hash;
13
+ }
14
+
15
+ export function transcriptResultReferences({
16
+ sessionId,
17
+ userEventId = null,
18
+ assistantEventId = null,
19
+ clientMessageId = null,
20
+ }) {
21
+ if (
22
+ typeof sessionId !== "string" ||
23
+ !sessionId ||
24
+ sessionId.length > 256 ||
25
+ !hash(assistantEventId)
26
+ )
27
+ return null;
28
+ return {
29
+ schema: "chainlesschain.session-transcript-references/v1",
30
+ sessionId,
31
+ assistantEventId,
32
+ ...(hash(userEventId) ? { userEventId } : {}),
33
+ ...(typeof clientMessageId === "string" &&
34
+ /^[a-zA-Z0-9_-]{1,80}$/u.test(clientMessageId)
35
+ ? { clientMessageId }
36
+ : {}),
37
+ };
38
+ }
@@ -13,6 +13,10 @@ import {
13
13
  listJsonlSessions,
14
14
  } from "../harness/jsonl-session-store.js";
15
15
  import { extractPluginUsageAttribution } from "./plugin-usage-attribution.js";
16
+ import {
17
+ projectUsagePricingContext,
18
+ mergeUsagePricingBucket as mergePricingBucket,
19
+ } from "./usage-pricing-context.js";
16
20
 
17
21
  // Injectable disk seams (tests override). readEvents skips malformed JSON lines
18
22
  // but does NOT guard readFileSync, so an unreadable session file (EACCES /
@@ -140,6 +144,7 @@ export function extractUsage(event) {
140
144
  cacheCreationTokens,
141
145
  timestamp: event.timestamp || null,
142
146
  ...(attribution ? { attribution } : {}),
147
+ ...projectUsagePricingContext(raw),
143
148
  };
144
149
  }
145
150
 
@@ -560,6 +565,7 @@ export function aggregateUsage(events) {
560
565
  entry.cacheReadTokens += u.cacheReadTokens || 0;
561
566
  entry.cacheCreationTokens += u.cacheCreationTokens || 0;
562
567
  entry.calls += 1;
568
+ mergePricingBucket(entry, u);
563
569
  byKey.set(key, entry);
564
570
  }
565
571
 
@@ -634,6 +640,8 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
634
640
  entry.cacheReadTokens += row.cacheReadTokens || 0;
635
641
  entry.cacheCreationTokens += row.cacheCreationTokens || 0;
636
642
  entry.calls += row.calls;
643
+ for (const bucket of row.pricingBuckets || [])
644
+ mergePricingBucket(entry, bucket);
637
645
  byKey.set(key, entry);
638
646
  }
639
647
  }