chainlesschain 0.162.158 → 0.162.159

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 (201) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-BwpTJTN_.js → AIOps-ByIiF5bT.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BtQOoo3l.js → ActionButton-NlgSFZq5.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-Duf_rXZa.js → Analytics-D17cOUcb.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-XQK3wjvB.js → AppLayout-B4mEZcbl.js} +4 -4
  6. package/src/assets/web-panel/assets/Artifacts-CASsmyQw.js +1 -0
  7. package/src/assets/web-panel/assets/{Audit-DNjOGQLh.js → Audit-DYAia2e2.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-FH9RAfB5.js → BackgroundAgents-C1lvYUSu.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-ZIbrR4-U.js → Backup-B6agK6bA.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DSt3NaND.js → BaseInput-DM43R2vy.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CY-wvbjv.js → Chat-Dtdp3eXp.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BAV9V-t-.js → ChatBubbleRenderer-BL5c_OQ1.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-8JVadE_B.js → Checkbox-D2xf7uZt.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-CN83nPYI.js → Codegen-D3j45jJL.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-xPSvqdN8.js → Col-voPJ9fUZ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-D78cnIjG.js → Community-DEsyTESF.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CSw6Csbp.js → Compact-OHV_QXLD.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-E_HhjcYR.js → Compliance-eDTJWC-J.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-DybeQjmm.js → Cowork-BBvy-6d1.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-DS0BJeLY.js → Cron-Dw2eoJxu.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BOwVgkbs.js → Crosschain-DQHms_Bh.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DRUr9YuX.js → DID-Cyu7Id1c.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-Cht-yUKW.js → Dashboard-sIDzkG92.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BLr_EJ9X.js → Dropdown-DUW3T2ey.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-Drexuh5W.js → EmailListRenderer-CECELXW8.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D9Q2Kq63.js → FamilyGuardDashboard-BIZR37ov.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-C-Z1xI-T.js → Federation-BEZIQX84.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-DLHPMkQF.js → FormItemContext-BcdUMznL.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DxxEdn2r.js → GenericCardRenderer-DsN_fr5H.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-syATcaBt.js → Git-63LhjBx2.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CYe_r8Zf.js → Governance-CYtQtIcH.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-GWxErB7k.js → Inference-BwR6UUox.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-qQvVCZoi.js → KnowledgeGraph-D9lVNTUA.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-BenAU2W8.js → Logs-C4q5h7Ut.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-7agE3Or1.js → MarkdownRenderer-DexLlXfN.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-D4Jn5fpD.js → Marketplace-Plksliuw.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DtAwoqiQ.js → McpTools-DZUFfeuG.js} +4 -4
  38. package/src/assets/web-panel/assets/{Memory-BI_O50I8.js → Memory-NoDYt29V.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-CQWE2lqx.js → MobileBridge-CEXGTkdh.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-CVtTqgtg.js → MobileProjects-DnbJkbZo.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-Dnr16qsn.js → Mtc-DTAa0cwy.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-CQ5Xe4-a.js → MtcAudit-CVV7LnNS.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-Bjr8O-wy.js → Multisig-BqqGrjt_.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CEe-KirU.js → NLProgramming-C4WP8DRp.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-spKJIKF9.js → Notes-Zo4DJUVI.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-DRp83LFG.js → NotificationSettings-DBcjRaz2.js} +1 -1
  47. package/src/assets/web-panel/assets/OrderTableRenderer-BCQel9bD.js +1 -0
  48. package/src/assets/web-panel/assets/{Organization-BlgB0IYg.js → Organization-BT9WPBOX.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-a_35HqZI.js → Overflow-D6b4bZ3q.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-CSNHCNPC.js → P2P-Cw3OsYx-.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-C8M1JH-1.js → PdhVaultBrowser-C0nyiK2b.js} +5 -5
  52. package/src/assets/web-panel/assets/{Permissions-DmjJi9Ms.js → Permissions-zVBZEB_C.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-DfuwRyf4.js → PersonalDataHub-BOspsFew.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-B2-S4ekE.js → Pipeline-CQWfZ4XH.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B1dOiBLC.js → Privacy-C2oGJ_WL.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-_pNmc-DX.js → ProjectInit-BZMR4qY1.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DmwG2Ii1.js → ProjectSettings-DAujoP63.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-COATmHxF.js → Projects-DvH_P2Uq.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-C6HSjg1d.js → Providers-D_RGHM-v.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CrDmS7l1.js → QrScannerModal-Mf6KfL2J.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CfKvWt-x.js → QuickAsk-5D8URoI_.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DMa6Kuzv.js → Recommend-Df4kC8Gy.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-DrktUo5x.js → RemoteSession-DGi3f3vO.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BTPQ1vjL.js → Reputation-jmkGFNWb.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-B6Q7QkMG.js → Row-DP6Pcu3m.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-B1IJhAmX.js → RssFeed-D60hSuPO.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-xbea1kGD.js → Search-0mf1BQUK.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-7rs0cO3F.js → Security-CfZtLFYV.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-0fjixK0S.js → Services-D9zpZLWz.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-B_rVq9Cu.js → Skeleton-D1_44eMD.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-C5uM90b9.js → Skills-cVFa8zkz.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-U4pYCIZH.js → Sla-Cinpzc3L.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-Cdz4yTWx.js → SpeechSettings-bqdiJE_P.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-Me3aetHv.js → SyncSettings-Dgl_SLgj.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-Cf6aNkL_.js → Tasks-DawWAEhy.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CYatk4tU.js → Templates-Ca0nY651.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-DIMKLw7o.js → Tenant-CgaGEhLm.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-C_cp-uSJ.js → Terminal-D-9EnpRx.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-cUPVNADp.js → TimelineRenderer-CSOxDHZ6.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CiaEIXVf.js → Tokens-BpwLZpL3.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DB1WU5lQ.js → Trigger-DgowwBK5.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-Cfht9Ijy.js → Trust-BZutapjE.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-K3P4Vt9S.js → UkeySign-QZPt8_Sp.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DWjhjXRh.js → VideoEditing-DlQx5UDJ.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-D9-Q1Kan.js → Wallet-ZfdTgtWA.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-BDszy-oV.js → WebAuthn-CAp0y_Cr.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-DIhij4rA.js → WorkflowEditor-BVITZEmr.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-D7VCiKbh.js → chat-CC2tPaNg.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-BvOd9pVn.js → colors-31brc9CA.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-DoqXBiAO.js → compact-item-DLMkC9C-.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CrKRzUtp.js → createContext-BMtveI6F.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CB8pPOtd.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-BWAev4WC.js → hasIn-DbD6WHW2.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-B30NvR21.js → index-3oBsXZmF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Cx7M7PSF.js → index-ALvoNTRF.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BHOcVYrx.js → index-AaIKG60c.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BXyl8O23.js → index-BFRLmCR3.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-YGDy7nNq.js → index-BKAB6-6V.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BoYrS_L2.js → index-BOM-VFlv.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Bq_ctRpz.js → index-BPpS-lGb.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CDGLdn12.js → index-Bbc2JeQG.js} +1 -1
  102. package/src/assets/web-panel/assets/index-BhXiaiev.js +1 -0
  103. package/src/assets/web-panel/assets/{index-ISvPZ05r.js → index-BiRQUoyE.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B0vGEbJT.js → index-BnFLzejT.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Bj7EgmOp.js → index-BoO4WEJB.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BZGk8AbZ.js → index-C-cGlCDW.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BkgnGX7v.js → index-C1PfeI88.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-HwHdTusY.js → index-C21pdBqs.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bf9YlbDn.js → index-C7LHNw_N.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-oY45XrkG.js → index-CEXa-4zd.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-KR1-Em9C.js → index-CEwjBA9q.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C5kRmj3P.js → index-CFXKbOek.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BN9m4Ux2.js → index-CJhREOlG.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-PDxGjKRu.js → index-COHHQ9cU.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-D23L1V76.js → index-CUmLMFsy.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-zH-tuuGZ.js → index-CWgfipcC.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CmJ6xKBO.js → index-C_eUKGYP.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CEPHMSih.js → index-CcVszn2b.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DV-Rfmfx.js → index-Cod97Urs.js} +3 -3
  120. package/src/assets/web-panel/assets/{index-BWWsaimP.js → index-Cwbqh0KZ.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-8g5TJCZ3.js → index-D67kIagl.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CEXfKSiI.js → index-D7cV0tu0.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CoD31Ndm.js → index-DJvxFyIz.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CSb-tnIo.js → index-DZl-UXB6.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DvDOpsJB.js → index-Dm0byjm1.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DlGu7As6.js → index-DndNnYc0.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-gmxqu5Kk.js → index-WQUO2vvK.js} +1 -1
  128. package/src/assets/web-panel/assets/index-WpaYaE3D.js +1 -0
  129. package/src/assets/web-panel/assets/{index-ww1QtLnX.js → index-bdaJwTXJ.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-JzUmJMvn.js → index-ep6JchNG.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BCyP071m.js → index-jG6h22Qg.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-1poMpMU0.js → index-ntVQqSML.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-XW-OR3mL.js → initDefaultProps-D5UoBRhN.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CeuEHh20.js → motion-DltOw0CR.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Dva79Keg.js → move-_oPkk5zj.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-C0B6bD2j.js → mtc-parser-EES8ROCS.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-AkZvO-a2.js → omit-B-_39x_M.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-B4r55qGc.js → pickAttrs-l6D0yJHz.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BLH-z7YE.js → placementArrow-DIeUK31L.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-ksEGZ2Wf.js → responsiveObserve-dG4EzUQo.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-Cth4OD1Z.js → slide-DWFzYGdH.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-9S83ezxw.js → statusUtils-CECdTA5H.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-7yQmQGZ4.js → styleChecker-D6kP-mMK.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DpGnMmPc.js → useFlexGapSupport-BWfna_rd.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-C4zDZIXH.js → useFs-Bb-j6rzo.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CoLICdUZ.js → usePersonalDataHub-D5OIsYM4.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-CKyH-35X.js → vnode-DNpwzFTM.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DRDGit17.js → zoom-vNLpFQBW.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-manifest.json +17 -3
  151. package/src/commands/agent.js +7 -0
  152. package/src/commands/background-session.js +143 -3
  153. package/src/commands/browse.js +86 -0
  154. package/src/commands/cloud.js +232 -0
  155. package/src/commands/doctor.js +61 -6
  156. package/src/commands/mcp.js +61 -0
  157. package/src/commands/plugin.js +63 -1
  158. package/src/commands/routine.js +367 -0
  159. package/src/commands/session.js +326 -0
  160. package/src/data/changelog.json +7 -2
  161. package/src/harness/jsonl-session-store.js +82 -2
  162. package/src/harness/session-index.js +295 -0
  163. package/src/harness/session-mirror.js +142 -0
  164. package/src/harness/transcript-integrity.js +182 -0
  165. package/src/index.js +8 -0
  166. package/src/lib/background-agent-supervisor.js +126 -2
  167. package/src/lib/channels/channel-manager.js +126 -0
  168. package/src/lib/channels/telegram-channel.js +112 -0
  169. package/src/lib/channels/webhook-channel.js +140 -0
  170. package/src/lib/chrome-connector.js +386 -0
  171. package/src/lib/cloud/bundle.js +110 -0
  172. package/src/lib/cloud/cloud-client.js +152 -0
  173. package/src/lib/computer-use/authorization.js +173 -0
  174. package/src/lib/computer-use/computer-use-server.js +276 -0
  175. package/src/lib/computer-use/control-backend.js +150 -0
  176. package/src/lib/doctor-checkup.js +652 -0
  177. package/src/lib/jsonl-session-store.js +4 -0
  178. package/src/lib/lsp/lsp-server-registry.js +48 -21
  179. package/src/lib/plugin-runtime/governance.js +174 -0
  180. package/src/lib/pr-link-ledger.js +221 -0
  181. package/src/lib/routine-store.js +352 -0
  182. package/src/lib/session-store-guard.js +40 -0
  183. package/src/lib/session-usage.js +318 -2
  184. package/src/lib/sub-agent-context.js +23 -0
  185. package/src/repl/agent-repl.js +361 -3
  186. package/src/repl/bg-dashboard.js +355 -0
  187. package/src/repl/fast-mode.js +103 -0
  188. package/src/repl/fullscreen-tui.js +197 -0
  189. package/src/repl/slash-command-registry.js +11 -3
  190. package/src/repl/voice-dictation.js +132 -0
  191. package/src/runtime/agent-core.js +242 -1
  192. package/src/runtime/coding-agent-contract-shared.cjs +88 -0
  193. package/src/runtime/coding-agent-policy.cjs +17 -0
  194. package/src/runtime/headless-runner.js +71 -7
  195. package/src/runtime/headless-stream.js +14 -8
  196. package/src/runtime/policies/agent-policy.js +3 -0
  197. package/src/assets/web-panel/assets/Artifacts-DQvJoQ6E.js +0 -1
  198. package/src/assets/web-panel/assets/OrderTableRenderer-h1A0XB2a.js +0 -1
  199. package/src/assets/web-panel/assets/devWarning-nDB--2xr.js +0 -1
  200. package/src/assets/web-panel/assets/index-B1_pRTtB.js +0 -1
  201. package/src/assets/web-panel/assets/index-TMM9FuW-.js +0 -1
@@ -31,6 +31,30 @@ function toNumber(v) {
31
31
  return Number.isFinite(n) ? n : 0;
32
32
  }
33
33
 
34
+ /**
35
+ * Extract the optional usage-attribution frame (用量归因) persisted alongside
36
+ * a token_usage event's data: `{ origin, skill?, subagentId?, role?,
37
+ * parentSessionId?, depth? }`. Written by the agent drivers for sub-agent /
38
+ * isolated-skill usage; absent on legacy events and on plain main-loop usage
39
+ * (absence ⇒ origin "main"). Returns a normalized copy or null.
40
+ */
41
+ export function extractAttribution(event) {
42
+ const a = event?.data?.attribution;
43
+ if (!a || typeof a !== "object") return null;
44
+ const origin = typeof a.origin === "string" && a.origin ? a.origin : null;
45
+ if (!origin) return null;
46
+ const out = { origin };
47
+ if (typeof a.skill === "string" && a.skill) out.skill = a.skill;
48
+ if (typeof a.subagentId === "string" && a.subagentId)
49
+ out.subagentId = a.subagentId;
50
+ if (typeof a.role === "string" && a.role) out.role = a.role;
51
+ if (typeof a.parentSessionId === "string" && a.parentSessionId)
52
+ out.parentSessionId = a.parentSessionId;
53
+ const depth = Number(a.depth);
54
+ if (Number.isFinite(depth)) out.depth = depth;
55
+ return out;
56
+ }
57
+
34
58
  /**
35
59
  * Extract a normalized usage record from a single event, or null if none.
36
60
  * Accepts both snake_case (OpenAI/Anthropic) and camelCase variants.
@@ -75,6 +99,9 @@ export function extractUsage(event) {
75
99
  return null;
76
100
  }
77
101
 
102
+ // Attribution rides along ONLY when present, so records built from legacy
103
+ // events stay byte-identical (callers using exact-equality keep passing).
104
+ const attribution = extractAttribution(event);
78
105
  return {
79
106
  provider: d.provider || raw.provider || null,
80
107
  model: d.model || raw.model || null,
@@ -84,6 +111,223 @@ export function extractUsage(event) {
84
111
  cacheReadTokens,
85
112
  cacheCreationTokens,
86
113
  timestamp: event.timestamp || null,
114
+ ...(attribution ? { attribution } : {}),
115
+ };
116
+ }
117
+
118
+ // ── Usage attribution roll-ups (用量归因) ───────────────────────────────────
119
+
120
+ function _newSums() {
121
+ return {
122
+ inputTokens: 0,
123
+ outputTokens: 0,
124
+ totalTokens: 0,
125
+ cacheReadTokens: 0,
126
+ cacheCreationTokens: 0,
127
+ calls: 0,
128
+ };
129
+ }
130
+
131
+ function _addSums(sums, u) {
132
+ sums.inputTokens += u.inputTokens || 0;
133
+ sums.outputTokens += u.outputTokens || 0;
134
+ sums.totalTokens += u.totalTokens || 0;
135
+ sums.cacheReadTokens += u.cacheReadTokens || 0;
136
+ sums.cacheCreationTokens += u.cacheCreationTokens || 0;
137
+ sums.calls += u.calls != null ? u.calls : 1;
138
+ }
139
+
140
+ const _byTokensDesc = (a, b) => b.totalTokens - a.totalTokens;
141
+
142
+ /**
143
+ * Roll usage events up by attribution: origin (main | subagent | skill —
144
+ * events without an attribution frame count as "main", which keeps every
145
+ * pre-attribution transcript aggregating exactly as before), by skill name,
146
+ * and by individual sub-agent.
147
+ */
148
+ export function aggregateUsageAttribution(events) {
149
+ const byOrigin = new Map();
150
+ const bySkill = new Map();
151
+ const bySubagent = new Map();
152
+
153
+ for (const evt of events || []) {
154
+ const u = extractUsage(evt);
155
+ if (!u) continue;
156
+ const attr = u.attribution || null;
157
+ const origin = attr?.origin || "main";
158
+
159
+ const o = byOrigin.get(origin) || { origin, ..._newSums() };
160
+ _addSums(o, u);
161
+ byOrigin.set(origin, o);
162
+
163
+ if (origin === "skill") {
164
+ const key = attr?.skill || "?";
165
+ const s = bySkill.get(key) || { skill: key, ..._newSums() };
166
+ _addSums(s, u);
167
+ bySkill.set(key, s);
168
+ }
169
+
170
+ if (attr?.subagentId) {
171
+ const key = attr.subagentId;
172
+ const r = bySubagent.get(key) || {
173
+ subagentId: key,
174
+ role: attr.role || null,
175
+ origin,
176
+ ..._newSums(),
177
+ };
178
+ _addSums(r, u);
179
+ bySubagent.set(key, r);
180
+ }
181
+ }
182
+
183
+ return {
184
+ byOrigin: Array.from(byOrigin.values()).sort(_byTokensDesc),
185
+ bySkill: Array.from(bySkill.values()).sort(_byTokensDesc),
186
+ bySubagent: Array.from(bySubagent.values()).sort(_byTokensDesc),
187
+ };
188
+ }
189
+
190
+ /**
191
+ * MCP tools reach the loop as `mcp__<server>__<tool>`; returns the server
192
+ * segment or null for a non-MCP tool name.
193
+ */
194
+ export function mcpServerOf(toolName) {
195
+ if (typeof toolName !== "string" || !toolName.startsWith("mcp__"))
196
+ return null;
197
+ const rest = toolName.slice(5);
198
+ const sep = rest.indexOf("__");
199
+ return sep > 0 ? rest.slice(0, sep) : null;
200
+ }
201
+
202
+ /** Lenient tool_result error detection (mirrors session-insights). */
203
+ function _isToolResultError(event) {
204
+ const r = event?.data?.result;
205
+ return Boolean(
206
+ event?.data?.error ||
207
+ (r && typeof r === "object" && (r.error || r.is_error || r.isError)),
208
+ );
209
+ }
210
+
211
+ /**
212
+ * Aggregate the transcript's tool events: per-tool call/error counts, an MCP
213
+ * server bucket (`mcp__<server>__*`), and a turn-associated token figure.
214
+ *
215
+ * `turnTokens` is an APPROXIMATION: turns are delimited by `user_message`
216
+ * events, and every token_usage event inside a turn is attributed to EVERY
217
+ * tool that ran in that turn ("tokens of turns that used tool X"). A turn's
218
+ * tokens therefore count once per distinct tool — do NOT sum turnTokens
219
+ * across tools/servers, it is not a partition. Headless sessions persist one
220
+ * aggregated usage event per run, so there the whole run's tokens associate
221
+ * with each tool the run used.
222
+ *
223
+ * Accepts both the compact `tool_call` shape ({tool, is_error, skill?}) the
224
+ * drivers write now and the legacy `tool_call`({tool, args}) + `tool_result`
225
+ * pair (errors read off the result, calls only off tool_call — no double
226
+ * count). Sessions without tool events aggregate to zeros.
227
+ */
228
+ export function aggregateToolCalls(events) {
229
+ const byTool = new Map();
230
+ const byServer = new Map();
231
+ let totalCalls = 0;
232
+ let totalErrors = 0;
233
+
234
+ const bumpTool = (name) => {
235
+ let t = byTool.get(name);
236
+ if (!t) {
237
+ t = {
238
+ tool: name,
239
+ mcpServer: mcpServerOf(name),
240
+ calls: 0,
241
+ errors: 0,
242
+ turnTokens: 0,
243
+ };
244
+ byTool.set(name, t);
245
+ }
246
+ return t;
247
+ };
248
+ const bumpServer = (server) => {
249
+ let s = byServer.get(server);
250
+ if (!s) {
251
+ s = { server, calls: 0, errors: 0, turnTokens: 0 };
252
+ byServer.set(server, s);
253
+ }
254
+ return s;
255
+ };
256
+
257
+ let turnTools = new Set();
258
+ let turnServers = new Set();
259
+ let turnTokens = 0;
260
+ const flushTurn = () => {
261
+ if (turnTokens > 0) {
262
+ for (const name of turnTools) bumpTool(name).turnTokens += turnTokens;
263
+ for (const server of turnServers)
264
+ bumpServer(server).turnTokens += turnTokens;
265
+ }
266
+ turnTools = new Set();
267
+ turnServers = new Set();
268
+ turnTokens = 0;
269
+ };
270
+
271
+ for (const evt of events || []) {
272
+ if (!evt || typeof evt !== "object") continue;
273
+ if (evt.type === "user_message") {
274
+ flushTurn();
275
+ continue;
276
+ }
277
+ if (evt.type === "tool_call") {
278
+ const name = evt.data?.tool || "?";
279
+ totalCalls++;
280
+ const t = bumpTool(name);
281
+ t.calls++;
282
+ const isErr = evt.data?.is_error === true;
283
+ if (isErr) {
284
+ totalErrors++;
285
+ t.errors++;
286
+ }
287
+ turnTools.add(name);
288
+ const server = mcpServerOf(name);
289
+ if (server) {
290
+ const s = bumpServer(server);
291
+ s.calls++;
292
+ if (isErr) s.errors++;
293
+ turnServers.add(server);
294
+ }
295
+ continue;
296
+ }
297
+ if (evt.type === "tool_result") {
298
+ const name = evt.data?.tool || "?";
299
+ turnTools.add(name);
300
+ const server = mcpServerOf(name);
301
+ if (server) turnServers.add(server);
302
+ if (_isToolResultError(evt)) {
303
+ totalErrors++;
304
+ bumpTool(name).errors++;
305
+ if (server) bumpServer(server).errors++;
306
+ }
307
+ continue;
308
+ }
309
+ const u = extractUsage(evt);
310
+ if (u) turnTokens += u.totalTokens;
311
+ }
312
+ flushTurn();
313
+
314
+ const byCallsDesc = (a, b) => b.calls - a.calls;
315
+ return {
316
+ totalCalls,
317
+ totalErrors,
318
+ byTool: Array.from(byTool.values()).sort(byCallsDesc),
319
+ byMcpServer: Array.from(byServer.values()).sort(byCallsDesc),
320
+ };
321
+ }
322
+
323
+ /**
324
+ * Full attribution section for a transcript: origin/skill/subagent usage
325
+ * roll-ups + the tool/MCP call aggregation. Pure.
326
+ */
327
+ export function sessionAttribution(events) {
328
+ return {
329
+ ...aggregateUsageAttribution(events),
330
+ tools: aggregateToolCalls(events),
87
331
  };
88
332
  }
89
333
 
@@ -141,12 +385,13 @@ export function aggregateUsage(events) {
141
385
  }
142
386
 
143
387
  /**
144
- * Roll up usage for a single JSONL session.
388
+ * Roll up usage for a single JSONL session. The pre-attribution keys
389
+ * (total/byModel) are unchanged; `attribution` is additive.
145
390
  */
146
391
  export function sessionUsage(sessionId) {
147
392
  const events = _deps.readEvents(sessionId);
148
393
  const agg = aggregateUsage(events);
149
- return { sessionId, ...agg };
394
+ return { sessionId, ...agg, attribution: sessionAttribution(events) };
150
395
  }
151
396
 
152
397
  /**
@@ -206,6 +451,66 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
206
451
  }
207
452
  }
208
453
 
454
+ // Global attribution roll-up: merge each session's attribution rows by key.
455
+ const gOrigin = new Map();
456
+ const gSkill = new Map();
457
+ const gSubagent = new Map();
458
+ const gTool = new Map();
459
+ const gServer = new Map();
460
+ let gToolCalls = 0;
461
+ let gToolErrors = 0;
462
+ const mergeRow = (map, key, row, seed) => {
463
+ const entry = map.get(key) || { ...seed, ..._newSums() };
464
+ _addSums(entry, row);
465
+ map.set(key, entry);
466
+ };
467
+ for (const s of perSession) {
468
+ const a = s.attribution;
469
+ if (!a) continue;
470
+ for (const row of a.byOrigin || []) {
471
+ mergeRow(gOrigin, row.origin, row, { origin: row.origin });
472
+ }
473
+ for (const row of a.bySkill || []) {
474
+ mergeRow(gSkill, row.skill, row, { skill: row.skill });
475
+ }
476
+ for (const row of a.bySubagent || []) {
477
+ mergeRow(gSubagent, row.subagentId, row, {
478
+ subagentId: row.subagentId,
479
+ role: row.role || null,
480
+ origin: row.origin,
481
+ });
482
+ }
483
+ const tools = a.tools || {};
484
+ gToolCalls += tools.totalCalls || 0;
485
+ gToolErrors += tools.totalErrors || 0;
486
+ for (const row of tools.byTool || []) {
487
+ const entry = gTool.get(row.tool) || {
488
+ tool: row.tool,
489
+ mcpServer: row.mcpServer || null,
490
+ calls: 0,
491
+ errors: 0,
492
+ turnTokens: 0,
493
+ };
494
+ entry.calls += row.calls || 0;
495
+ entry.errors += row.errors || 0;
496
+ entry.turnTokens += row.turnTokens || 0;
497
+ gTool.set(row.tool, entry);
498
+ }
499
+ for (const row of tools.byMcpServer || []) {
500
+ const entry = gServer.get(row.server) || {
501
+ server: row.server,
502
+ calls: 0,
503
+ errors: 0,
504
+ turnTokens: 0,
505
+ };
506
+ entry.calls += row.calls || 0;
507
+ entry.errors += row.errors || 0;
508
+ entry.turnTokens += row.turnTokens || 0;
509
+ gServer.set(row.server, entry);
510
+ }
511
+ }
512
+ const byCallsDesc = (a, b) => b.calls - a.calls;
513
+
209
514
  return {
210
515
  sessions: perSession,
211
516
  skipped,
@@ -213,6 +518,17 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
213
518
  byModel: Array.from(byKey.values()).sort(
214
519
  (a, b) => b.totalTokens - a.totalTokens,
215
520
  ),
521
+ attribution: {
522
+ byOrigin: Array.from(gOrigin.values()).sort(_byTokensDesc),
523
+ bySkill: Array.from(gSkill.values()).sort(_byTokensDesc),
524
+ bySubagent: Array.from(gSubagent.values()).sort(_byTokensDesc),
525
+ tools: {
526
+ totalCalls: gToolCalls,
527
+ totalErrors: gToolErrors,
528
+ byTool: Array.from(gTool.values()).sort(byCallsDesc),
529
+ byMcpServer: Array.from(gServer.values()).sort(byCallsDesc),
530
+ },
531
+ },
216
532
  };
217
533
  }
218
534
 
@@ -115,6 +115,14 @@ export class SubAgentContext {
115
115
  // Optional progress callback for streaming events to consumers
116
116
  this._onProgress = options.onProgress || null;
117
117
 
118
+ // Usage-attribution seam (用量归因): forwards the child loop's REAL
119
+ // provider-reported token usage to the spawner (the ~4-chars/token
120
+ // estimate kept in _tokenCount below is only a budget heuristic and never
121
+ // leaves this context). Wired by spawn_sub_agent / isolated run_skill so
122
+ // the parent loop can re-yield attributed `token-usage` events.
123
+ this._onUsage =
124
+ typeof options.onUsage === "function" ? options.onUsage : null;
125
+
118
126
  // Optional abort signal for cancellation
119
127
  this._signal = options.signal || null;
120
128
 
@@ -295,6 +303,21 @@ export class SubAgentContext {
295
303
  for await (const event of gen) {
296
304
  this._iterationCount++;
297
305
 
306
+ if (event.type === "token-usage" && this._onUsage) {
307
+ // Forward real usage to the spawner. A nested child's event already
308
+ // carries its own attribution frame — preserve it (deepest wins).
309
+ try {
310
+ this._onUsage({
311
+ provider: event.provider || null,
312
+ model: event.model || null,
313
+ usage: event.usage || null,
314
+ attribution: event.attribution || null,
315
+ });
316
+ } catch (_e) {
317
+ // Usage forwarding is best-effort — never break the child loop
318
+ }
319
+ }
320
+
298
321
  if (event.type === "tool-executing") {
299
322
  this._toolsUsed.push(event.tool);
300
323
  }