chainlesschain 0.162.183 → 0.162.185

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 (205) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-BapS7lz4.js → AIOps-Co4ipAKx.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CXYdXEUI.js → ActionButton-BpsoDrDq.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CKR5Lexn.js → Analytics-DMUqeBik.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DATVUOXB.js → AppLayout-C6fD_dXr.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-vb_0ZlV1.js → Artifacts-DW0YlQlV.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-BI1l9GWt.js → Audit-DXtHiiFK.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BFXbaUEf.js → BackgroundAgents-BgmF62U-.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DEYBJJ6H.js → Backup-D8vhVLQC.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-COdzeO0M.js → BaseInput-D7nCtY2X.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-oSN6sfM5.js → Chat-CBZLEV2G.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CI_jT0ru.js → ChatBubbleRenderer-CtDb-2Qu.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-BezEa4tm.js → Checkbox-DJLkeD4M.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CNEL0Ife.js → Codegen-Cs6xO8Fd.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-Bf2Y2Bn1.js → Col-Dtx9wxfT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BfUlu4Hc.js → Community-DiigJg4n.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BN4xWP9e.js → Compact-B67oMDDL.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-Bt6BKmEP.js → Compliance-BBDl-Jos.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DKLp8YxO.js → Cowork-Ba98hSsa.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-o7Hhw6ky.js → Cron-DBWQZIz7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DaNXWJIw.js → Crosschain-OcrKzxAt.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-B-Qa2mha.js → DID-mW2eSbjM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CMX0Qnxw.js → Dashboard-BmsDPmKJ.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BLuLyRu_.js → Dropdown-CVzmCyRx.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CFBAeMij.js → EmailListRenderer-vVgqKzhh.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DWzXQ67V.js → FamilyGuardDashboard-CWY5t_ZJ.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-DB1QSLgr.js → Federation-ZGomhjxr.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C-OKCToP.js → FormItemContext-DyaknbLr.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-DMJFtFZf.js → GenericCardRenderer-CmIvN1qr.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-DsGSujOV.js → Git-DQOJIYkG.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-B0iS9NEW.js → Governance-CMiY-EEp.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CH004H7c.js → Inference-D6IXuucf.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-Q9MTQ83c.js → KnowledgeGraph-6jkTL0hV.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-B_fyjZK9.js → Logs-N0jD-349.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-BL1IVXm-.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-BIStF9d1.js → Marketplace-WhYuhBxm.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-pGfal768.js → McpTools-DTeO28xy.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-xbNqpIqB.js → Memory-MSHzPSE-.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-B5EQGrIK.js → MobileBridge-Cyqs3J3W.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-7IN2hWHg.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-a_rmfxJH.js → Mtc-MWBnezNM.js} +5 -5
  43. package/src/assets/web-panel/assets/{MtcAudit-BmKuofUr.js → MtcAudit-DQu35Nu-.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BQbJ-CAN.js → Multisig-CQS_M-DP.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-JtPJKX25.js → NLProgramming-WD16vQvY.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-ZFKznTSG.js → Notes-DqsiSwOq.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-B6aHRBBR.js → NotificationSettings-D3LoxO-P.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DdcsOqEz.js → OrderTableRenderer-C5jPoTd6.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BFDTAUgS.js → Organization-48aMBJso.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-0FoTfbWH.js → Overflow-j-a2XkhR.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-CWd90etw.js → P2P-BYpBNYnh.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-DtCGC-W-.js → PdhVaultBrowser-CYCsrrLg.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-CvQ7nr7T.js → Permissions-ZRXGXDFb.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-rdNPcR-Z.js → PersonalDataHub-6OWUPHyL.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-CmrfiNOs.js → Pipeline-FcjlDK9E.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-D7ep1h_t.js → Privacy-vD_6Tldp.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-C0xKINHS.js → ProjectInit-B_CHOLXp.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-AOmZFp-F.js → ProjectSettings-rEiqQ2da.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-BWHSkXZP.js → Projects-Bg8obRAM.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-Cb7JCTPL.js → Providers-Dgtt97B4.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-BDMehcXC.js → QrScannerModal-DaGVylZO.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BF6_1nvx.js → QuickAsk-DP2_2x3u.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BHfT-A28.js → Recommend-D29yj4ki.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-Ue2wnBLp.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-CsWHLEcj.js → Reputation-CrxIHqeC.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-D6srnWhm.js → Row-BgSzJPCn.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Xhr-KyVd.js → RssFeed-AwhYOrP9.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-vIbIJvwL.js → Search-D2shpEcm.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-Bzi5Aiko.js → Security-BtKzhTbT.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-BHtVX52P.js → Services-BY0DsUSm.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-C0R8B2jf.js → Skeleton-B3Sb0UI9.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-DtT6rWOX.js → Skills-Ne5w0bDs.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-0B37mxVU.js → Sla-C8VkaP4T.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-0V2jE1Bs.js → SpeechSettings-BQdIEYP9.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-CYedRzaj.js → SyncSettings-CwFafrUg.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-yJuKoDlR.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-BKoHztGT.js → Templates-DYn9qCmx.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant--17nwVNZ.js → Tenant-DkKReHvn.js} +1 -1
  79. package/src/assets/web-panel/assets/Terminal-r6bd-owt.js +3 -0
  80. package/src/assets/web-panel/assets/{TimelineRenderer-C5jx4AE3.js → TimelineRenderer-Mq2juUC3.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DYJKYa9t.js → Tokens-BnMw7MRi.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DChpWiQ9.js → Trigger-CTzKp4o9.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DfWG52EA.js → Trust-BBrSZm9U.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CMugYcyA.js → UkeySign-D9fDvEMq.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-BOSmrHAd.js → VideoEditing-DUi5ve-0.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-D5g3k3Sa.js → Wallet-C8Fi1P8_.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-D-7u4Mkc.js → WebAuthn-s1LppYi2.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CFzZMSUJ.js → WorkflowEditor-DJL2C4lj.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-VbR6l6mc.js → chat-D3F2ndOB.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BSZn8EDS.js → colors-B7739-e3.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-y74obMEq.js → compact-item-CcJbFTF4.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-C326bjex.js → createContext-CuP_DEFS.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-Cj_36GHq.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-D_INJQT_.js → hasIn-6YEXfIik.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DuIkfnTF.js → index-4h3lpWJ3.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DBkzrpiL.js → index-BIID4XcP.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B7SrC-On.js → index-BOHUo3aM.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BnSfnSMP.js → index-BYYSLEjn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-KF9Zmqln.js → index-Bh4hcNWW.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C327pLbw.js → index-BhIKnQAW.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-E_v1ZCTP.js → index-Bru_kpZl.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bfw2hf2s.js → index-BsdSnHQE.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-jYaogFE8.js → index-BxSqVMGh.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dlxh9D8D.js → index-C3ZA_h1j.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DdyjrCAu.js → index-C3pctdCb.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-abmn_8Yz.js → index-CRuoRapN.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-qMA4GYdQ.js → index-CY0zd_jc.js} +1 -1
  108. package/src/assets/web-panel/assets/index-CezFrAmD.js +1 -0
  109. package/src/assets/web-panel/assets/index-Cxgguez6.js +1 -0
  110. package/src/assets/web-panel/assets/{index-Cr3uuz0C.js → index-CxradkS6.js} +19 -19
  111. package/src/assets/web-panel/assets/{index-DwHSZLXg.js → index-D0K_rFC1.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CMHqs3Jb.js → index-D0zdPyTD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bnua8IX3.js → index-D3haorUt.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BY8-nCgs.js → index-D6Ij_2uw.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-FQBsuExX.js → index-DDRlBaDt.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C-ZCaUJc.js → index-DGsE7MQz.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BjcWKg1S.js → index-DHvxXXNA.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-GA80ir_u.js → index-DV11jpeO.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CsovTpx9.js → index-DXdnfDVK.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-nSn2-C4N.js → index-D_75jYWk.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BJi3UqBt.js → index-DjI9MkId.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-gINyI1rh.js → index-DlSvpNMf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CfLJ42kv.js → index-Dox44j29.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-C2NyllnW.js → index-DpEKmjoa.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Iw0FfqUz.js → index-Dsy7_x4s.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-4eyH-eC2.js → index-Gxw09e9R.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-4PIeTM--.js → index-LfRlSmqe.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-IZAWt0H8.js → index-Nz-MUHD1.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DUU2606o.js → index-b9cuvFEv.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CzE53HiB.js → index-eR6H6NR7.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-9PIVuXqo.js → index-fWgUQZ9d.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DkH1JvEt.js → index-mfWQRSLn.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DNMxIsDg.js → index-yOZYSNmG.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DI0Zaf8-.js → initDefaultProps-CJ1Bq2L3.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BZDKSy3Z.js → motion-rYJN_vzf.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DsIW01Vp.js → move-DnDK2gTP.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-CohkDhsf.js → mtc-parser-CKnXy7ES.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-DpiN5rzg.js → omit-3nRc3hWT.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-qbuqni3y.js → pickAttrs-Ddjj3sLu.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-mBky6714.js → placementArrow-CbsbphQY.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Bas3eQ2K.js → responsiveObserve-D9aZ4VDJ.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-CBx_SKoK.js → slide-D4pvdFj0.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-BlYguJSV.js → statusUtils-C2W-vY-S.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BCYQmRGv.js → styleChecker-Cbq5150d.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-Dp5dybLM.js → useFlexGapSupport-BA2B7R82.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-DHAMBgA2.js → useFs-CLrwg3xg.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-W6jsSw76.js → usePersonalDataHub-DQeAI5qs.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BkI6aayc.js → vnode-8BDGy-YU.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-C_IMGueq.js → zoom-B2SzFprl.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agenda.js +74 -1
  152. package/src/commands/background-session.js +11 -1
  153. package/src/commands/batch.js +160 -5
  154. package/src/commands/plugin.js +90 -27
  155. package/src/commands/session.js +31 -1
  156. package/src/commands/team.js +212 -6
  157. package/src/data/changelog.json +26 -0
  158. package/src/gateways/terminal/PtyManager.js +45 -15
  159. package/src/gateways/ws/ws-agent-handler.js +15 -1
  160. package/src/gateways/ws/ws-session-gateway.js +88 -0
  161. package/src/harness/background-task-command-runner.js +234 -0
  162. package/src/harness/background-task-manager.js +150 -20
  163. package/src/harness/background-task-worker.js +17 -11
  164. package/src/harness/jsonl-session-store.js +57 -6
  165. package/src/harness/mcp-client.js +65 -43
  166. package/src/lib/agent-batch.js +4 -1
  167. package/src/lib/agent-team/team-worktree.js +3 -0
  168. package/src/lib/async-hook-supervisor.cjs +82 -10
  169. package/src/lib/async-hook-supervisor.js +50 -4
  170. package/src/lib/background-agent-supervisor.js +46 -0
  171. package/src/lib/collaboration-run-store.js +362 -0
  172. package/src/lib/event-runtime-host.js +12 -10
  173. package/src/lib/hook-runner.cjs +105 -18
  174. package/src/lib/hook-runner.js +79 -6
  175. package/src/lib/hook-shell-command.cjs +148 -0
  176. package/src/lib/hooks-v2-runtime.js +280 -81
  177. package/src/lib/hooks-v2-workspace-context.js +258 -0
  178. package/src/lib/jsonl-session-store.js +2 -0
  179. package/src/lib/lsp/__tests__/lsp-client.test.js +15 -0
  180. package/src/lib/lsp/lsp-client.js +12 -1
  181. package/src/lib/plugin-monitor-supervisor.js +53 -16
  182. package/src/lib/plugin-runtime/bin.js +21 -5
  183. package/src/lib/plugin-runtime/install.js +249 -57
  184. package/src/lib/plugin-runtime/mcp.js +21 -2
  185. package/src/lib/plugin-runtime/monitors.js +19 -3
  186. package/src/lib/plugin-runtime/sandbox-policy.js +100 -4
  187. package/src/lib/process-execution-broker/index.js +1151 -44
  188. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1557 -0
  189. package/src/lib/process-execution-broker/linux-generic-bwrap.js +1378 -0
  190. package/src/lib/process-execution-broker/platform-sandbox.js +586 -58
  191. package/src/lib/session-usage.js +202 -2
  192. package/src/lib/stream-retry.js +29 -0
  193. package/src/repl/agent-repl.js +40 -3
  194. package/src/runtime/agent-core.js +304 -42
  195. package/src/runtime/headless-runner.js +48 -1
  196. package/src/runtime/headless-stream.js +42 -2
  197. package/src/workers/background-agent-worker.js +5 -11
  198. package/src/assets/web-panel/assets/MarkdownRenderer-C0o4FtpD.js +0 -1
  199. package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +0 -1
  200. package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +0 -5
  201. package/src/assets/web-panel/assets/Tasks-D-R1x8vm.js +0 -1
  202. package/src/assets/web-panel/assets/Terminal-Dnkos7vW.js +0 -3
  203. package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +0 -1
  204. package/src/assets/web-panel/assets/index-5iAFwYx1.js +0 -1
  205. package/src/assets/web-panel/assets/index-BfnB7LPx.js +0 -1
@@ -32,6 +32,33 @@ function toNumber(v) {
32
32
  return Number.isFinite(n) ? n : 0;
33
33
  }
34
34
 
35
+ const MAX_OBSERVED_DURATION_MS = 7 * 24 * 60 * 60 * 1000;
36
+
37
+ function observedDuration(value) {
38
+ if (value === null || value === undefined || value === "") return null;
39
+ const number = Number(value);
40
+ if (!Number.isFinite(number) || number < 0) return null;
41
+ return Math.min(MAX_OBSERVED_DURATION_MS, Math.round(number));
42
+ }
43
+
44
+ function eventDuration(event) {
45
+ return observedDuration(
46
+ event?.data?.duration_ms ??
47
+ event?.data?.durationMs ??
48
+ event?.data?.result?.toolTelemetryRecord?.durationMs,
49
+ );
50
+ }
51
+
52
+ function addObservedDuration(row, durationMs) {
53
+ if (durationMs === null) return;
54
+ row.durationMs = (row.durationMs || 0) + durationMs;
55
+ row.timedCalls = (row.timedCalls || 0) + 1;
56
+ }
57
+
58
+ function addObservedRetry(row) {
59
+ row.retries = (row.retries || 0) + 1;
60
+ }
61
+
35
62
  /**
36
63
  * Extract the optional usage-attribution frame (用量归因) persisted alongside
37
64
  * a token_usage event's data: `{ origin, skill?, subagentId?, role?,
@@ -209,6 +236,56 @@ function _isToolResultError(event) {
209
236
  );
210
237
  }
211
238
 
239
+ /**
240
+ * Aggregate compact `llm_retry` events. Each event represents one failed
241
+ * streaming attempt that the runtime actually retried and includes only a
242
+ * controlled reason plus its elapsed time — never the raw provider error.
243
+ */
244
+ export function aggregateLlmRetries(events) {
245
+ const byReason = new Map();
246
+ const byModel = new Map();
247
+ let totalRetries = 0;
248
+ let durationMs = 0;
249
+
250
+ const add = (map, key, seed, elapsed) => {
251
+ const row = map.get(key) || { ...seed, retries: 0, durationMs: 0 };
252
+ row.retries++;
253
+ row.durationMs += elapsed;
254
+ map.set(key, row);
255
+ };
256
+
257
+ for (const event of events || []) {
258
+ if (event?.type !== "llm_retry") continue;
259
+ const data = event.data || {};
260
+ const elapsed = eventDuration(event) ?? 0;
261
+ const reason =
262
+ typeof data.reason === "string" && data.reason ? data.reason : "unknown";
263
+ const provider =
264
+ typeof data.provider === "string" && data.provider ? data.provider : null;
265
+ const model =
266
+ typeof data.model === "string" && data.model ? data.model : null;
267
+ totalRetries++;
268
+ durationMs += elapsed;
269
+ add(byReason, reason, { reason }, elapsed);
270
+ add(
271
+ byModel,
272
+ `${provider || "?"}/${model || "?"}`,
273
+ { provider, model },
274
+ elapsed,
275
+ );
276
+ }
277
+
278
+ if (totalRetries === 0) return null;
279
+ const byRetriesDesc = (a, b) =>
280
+ b.retries - a.retries || b.durationMs - a.durationMs;
281
+ return {
282
+ totalRetries,
283
+ durationMs,
284
+ byReason: Array.from(byReason.values()).sort(byRetriesDesc),
285
+ byModel: Array.from(byModel.values()).sort(byRetriesDesc),
286
+ };
287
+ }
288
+
212
289
  /**
213
290
  * Aggregate the transcript's tool events: per-tool call/error counts, an MCP
214
291
  * server bucket (`mcp__<server>__*`), and a turn-associated token figure.
@@ -232,6 +309,9 @@ export function aggregateToolCalls(events) {
232
309
  const byPlugin = new Map();
233
310
  let totalCalls = 0;
234
311
  let totalErrors = 0;
312
+ let totalDurationMs = 0;
313
+ let timedCalls = 0;
314
+ let retryCalls = 0;
235
315
 
236
316
  const bumpTool = (name) => {
237
317
  let t = byTool.get(name);
@@ -276,6 +356,8 @@ export function aggregateToolCalls(events) {
276
356
  let turnPlugins = new Set();
277
357
  let turnTokens = 0;
278
358
  let pendingPlugin = null;
359
+ let pendingObservation = null;
360
+ let failedToolsInTurn = new Set();
279
361
  const flushTurn = () => {
280
362
  if (turnTokens > 0) {
281
363
  for (const name of turnTools) bumpTool(name).turnTokens += turnTokens;
@@ -291,6 +373,8 @@ export function aggregateToolCalls(events) {
291
373
  turnPlugins = new Set();
292
374
  turnTokens = 0;
293
375
  pendingPlugin = null;
376
+ pendingObservation = null;
377
+ failedToolsInTurn = new Set();
294
378
  };
295
379
 
296
380
  for (const evt of events || []) {
@@ -301,20 +385,35 @@ export function aggregateToolCalls(events) {
301
385
  }
302
386
  if (evt.type === "tool_call") {
303
387
  const name = evt.data?.tool || "?";
388
+ const durationMs = eventDuration(evt);
389
+ const isRetry = failedToolsInTurn.has(name);
304
390
  totalCalls++;
305
391
  const t = bumpTool(name);
306
392
  t.calls++;
307
393
  const isErr = evt.data?.is_error === true;
394
+ if (durationMs !== null) {
395
+ totalDurationMs += durationMs;
396
+ timedCalls++;
397
+ addObservedDuration(t, durationMs);
398
+ }
399
+ if (isRetry) {
400
+ retryCalls++;
401
+ addObservedRetry(t);
402
+ }
308
403
  if (isErr) {
309
404
  totalErrors++;
310
405
  t.errors++;
311
406
  }
407
+ if (isErr) failedToolsInTurn.add(name);
408
+ else failedToolsInTurn.delete(name);
312
409
  turnTools.add(name);
313
410
  const server = mcpServerOf(name);
314
411
  if (server) {
315
412
  const s = bumpServer(server);
316
413
  s.calls++;
317
414
  if (isErr) s.errors++;
415
+ addObservedDuration(s, durationMs);
416
+ if (isRetry) addObservedRetry(s);
318
417
  turnServers.add(server);
319
418
  }
320
419
  const plugin =
@@ -326,24 +425,43 @@ export function aggregateToolCalls(events) {
326
425
  ? evt.data.plugin_version
327
426
  : null;
328
427
  pendingPlugin = { tool: name, plugin, version: pluginVersion };
428
+ pendingObservation = {
429
+ tool: name,
430
+ durationCounted: durationMs !== null,
431
+ errorCounted: isErr,
432
+ };
329
433
  if (plugin) {
330
434
  const p = bumpPlugin(plugin, pluginVersion);
331
435
  p.calls++;
332
436
  if (isErr) p.errors++;
437
+ addObservedDuration(p, durationMs);
438
+ if (isRetry) addObservedRetry(p);
333
439
  turnPlugins.add(`${plugin}@${pluginVersion || "?"}`);
334
440
  }
335
441
  continue;
336
442
  }
337
443
  if (evt.type === "tool_result") {
338
444
  const name = evt.data?.tool || "?";
445
+ const durationMs = eventDuration(evt);
339
446
  turnTools.add(name);
340
447
  const server = mcpServerOf(name);
341
448
  if (server) turnServers.add(server);
342
- if (_isToolResultError(evt)) {
449
+ const isErr = _isToolResultError(evt);
450
+ const pairedObservation =
451
+ pendingObservation?.tool === name ? pendingObservation : null;
452
+ if (isErr && !pairedObservation?.errorCounted) {
343
453
  totalErrors++;
344
454
  bumpTool(name).errors++;
345
455
  if (server) bumpServer(server).errors++;
346
456
  }
457
+ if (isErr) failedToolsInTurn.add(name);
458
+ else failedToolsInTurn.delete(name);
459
+ if (durationMs !== null && !pairedObservation?.durationCounted) {
460
+ totalDurationMs += durationMs;
461
+ timedCalls++;
462
+ addObservedDuration(bumpTool(name), durationMs);
463
+ if (server) addObservedDuration(bumpServer(server), durationMs);
464
+ }
347
465
  const extracted = extractPluginUsageAttribution(evt.data?.result);
348
466
  const plugin =
349
467
  typeof evt.data?.plugin === "string" && evt.data.plugin
@@ -360,10 +478,14 @@ export function aggregateToolCalls(events) {
360
478
  pendingPlugin?.plugin === plugin &&
361
479
  (pendingPlugin?.version || null) === (pluginVersion || null);
362
480
  if (!countedByCall) p.calls++;
363
- if (_isToolResultError(evt) && !countedByCall) p.errors++;
481
+ if (isErr && !countedByCall) p.errors++;
482
+ if (!countedByCall || !pairedObservation?.durationCounted) {
483
+ addObservedDuration(p, durationMs);
484
+ }
364
485
  turnPlugins.add(`${plugin}@${pluginVersion || "?"}`);
365
486
  }
366
487
  pendingPlugin = null;
488
+ pendingObservation = null;
367
489
  continue;
368
490
  }
369
491
  const u = extractUsage(evt);
@@ -375,6 +497,8 @@ export function aggregateToolCalls(events) {
375
497
  return {
376
498
  totalCalls,
377
499
  totalErrors,
500
+ ...(timedCalls > 0 ? { totalDurationMs, timedCalls } : {}),
501
+ ...(retryCalls > 0 ? { retryCalls } : {}),
378
502
  byTool: Array.from(byTool.values()).sort(byCallsDesc),
379
503
  byMcpServer: Array.from(byServer.values()).sort(byCallsDesc),
380
504
  byPlugin: Array.from(byPlugin.values()).sort(byCallsDesc),
@@ -386,9 +510,11 @@ export function aggregateToolCalls(events) {
386
510
  * roll-ups + the tool/MCP call aggregation. Pure.
387
511
  */
388
512
  export function sessionAttribution(events) {
513
+ const retries = aggregateLlmRetries(events);
389
514
  return {
390
515
  ...aggregateUsageAttribution(events),
391
516
  tools: aggregateToolCalls(events),
517
+ ...(retries ? { retries } : {}),
392
518
  };
393
519
  }
394
520
 
@@ -519,8 +645,15 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
519
645
  const gTool = new Map();
520
646
  const gServer = new Map();
521
647
  const gPlugin = new Map();
648
+ const gRetryReason = new Map();
649
+ const gRetryModel = new Map();
522
650
  let gToolCalls = 0;
523
651
  let gToolErrors = 0;
652
+ let gToolDurationMs = 0;
653
+ let gTimedToolCalls = 0;
654
+ let gToolRetryCalls = 0;
655
+ let gLlmRetries = 0;
656
+ let gLlmRetryDurationMs = 0;
524
657
  const mergeRow = (map, key, row, seed) => {
525
658
  const entry = map.get(key) || { ...seed, ..._newSums() };
526
659
  _addSums(entry, row);
@@ -545,6 +678,9 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
545
678
  const tools = a.tools || {};
546
679
  gToolCalls += tools.totalCalls || 0;
547
680
  gToolErrors += tools.totalErrors || 0;
681
+ gToolDurationMs += tools.totalDurationMs || 0;
682
+ gTimedToolCalls += tools.timedCalls || 0;
683
+ gToolRetryCalls += tools.retryCalls || 0;
548
684
  for (const row of tools.byTool || []) {
549
685
  const entry = gTool.get(row.tool) || {
550
686
  tool: row.tool,
@@ -556,6 +692,12 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
556
692
  entry.calls += row.calls || 0;
557
693
  entry.errors += row.errors || 0;
558
694
  entry.turnTokens += row.turnTokens || 0;
695
+ if (row.durationMs !== undefined) {
696
+ entry.durationMs = (entry.durationMs || 0) + (row.durationMs || 0);
697
+ entry.timedCalls = (entry.timedCalls || 0) + (row.timedCalls || 0);
698
+ }
699
+ if (row.retries !== undefined)
700
+ entry.retries = (entry.retries || 0) + (row.retries || 0);
559
701
  gTool.set(row.tool, entry);
560
702
  }
561
703
  for (const row of tools.byMcpServer || []) {
@@ -568,6 +710,12 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
568
710
  entry.calls += row.calls || 0;
569
711
  entry.errors += row.errors || 0;
570
712
  entry.turnTokens += row.turnTokens || 0;
713
+ if (row.durationMs !== undefined) {
714
+ entry.durationMs = (entry.durationMs || 0) + (row.durationMs || 0);
715
+ entry.timedCalls = (entry.timedCalls || 0) + (row.timedCalls || 0);
716
+ }
717
+ if (row.retries !== undefined)
718
+ entry.retries = (entry.retries || 0) + (row.retries || 0);
571
719
  gServer.set(row.server, entry);
572
720
  }
573
721
  for (const row of tools.byPlugin || []) {
@@ -582,10 +730,45 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
582
730
  entry.calls += row.calls || 0;
583
731
  entry.errors += row.errors || 0;
584
732
  entry.turnTokens += row.turnTokens || 0;
733
+ if (row.durationMs !== undefined) {
734
+ entry.durationMs = (entry.durationMs || 0) + (row.durationMs || 0);
735
+ entry.timedCalls = (entry.timedCalls || 0) + (row.timedCalls || 0);
736
+ }
737
+ if (row.retries !== undefined)
738
+ entry.retries = (entry.retries || 0) + (row.retries || 0);
585
739
  gPlugin.set(key, entry);
586
740
  }
741
+ const retries = a.retries || null;
742
+ if (retries) {
743
+ gLlmRetries += retries.totalRetries || 0;
744
+ gLlmRetryDurationMs += retries.durationMs || 0;
745
+ for (const row of retries.byReason || []) {
746
+ const entry = gRetryReason.get(row.reason) || {
747
+ reason: row.reason,
748
+ retries: 0,
749
+ durationMs: 0,
750
+ };
751
+ entry.retries += row.retries || 0;
752
+ entry.durationMs += row.durationMs || 0;
753
+ gRetryReason.set(row.reason, entry);
754
+ }
755
+ for (const row of retries.byModel || []) {
756
+ const key = `${row.provider || "?"}/${row.model || "?"}`;
757
+ const entry = gRetryModel.get(key) || {
758
+ provider: row.provider || null,
759
+ model: row.model || null,
760
+ retries: 0,
761
+ durationMs: 0,
762
+ };
763
+ entry.retries += row.retries || 0;
764
+ entry.durationMs += row.durationMs || 0;
765
+ gRetryModel.set(key, entry);
766
+ }
767
+ }
587
768
  }
588
769
  const byCallsDesc = (a, b) => b.calls - a.calls;
770
+ const byRetriesDesc = (a, b) =>
771
+ b.retries - a.retries || b.durationMs - a.durationMs;
589
772
 
590
773
  return {
591
774
  sessions: perSession,
@@ -601,10 +784,27 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
601
784
  tools: {
602
785
  totalCalls: gToolCalls,
603
786
  totalErrors: gToolErrors,
787
+ ...(gTimedToolCalls > 0
788
+ ? {
789
+ totalDurationMs: gToolDurationMs,
790
+ timedCalls: gTimedToolCalls,
791
+ }
792
+ : {}),
793
+ ...(gToolRetryCalls > 0 ? { retryCalls: gToolRetryCalls } : {}),
604
794
  byTool: Array.from(gTool.values()).sort(byCallsDesc),
605
795
  byMcpServer: Array.from(gServer.values()).sort(byCallsDesc),
606
796
  byPlugin: Array.from(gPlugin.values()).sort(byCallsDesc),
607
797
  },
798
+ ...(gLlmRetries > 0
799
+ ? {
800
+ retries: {
801
+ totalRetries: gLlmRetries,
802
+ durationMs: gLlmRetryDurationMs,
803
+ byReason: Array.from(gRetryReason.values()).sort(byRetriesDesc),
804
+ byModel: Array.from(gRetryModel.values()).sort(byRetriesDesc),
805
+ },
806
+ }
807
+ : {}),
608
808
  },
609
809
  };
610
810
  }
@@ -81,3 +81,32 @@ export function isRetryableStreamError(err, signal) {
81
81
  msg,
82
82
  );
83
83
  }
84
+
85
+ /**
86
+ * Reduce a retryable stream failure to a small, secret-free reason vocabulary
87
+ * suitable for persisted usage telemetry. Never persist the provider's raw
88
+ * error message: it can contain a request URL, tenant id, or proxy details.
89
+ */
90
+ export function classifyStreamRetryReason(err) {
91
+ const code = String(err?.code || err?.cause?.code || "").toUpperCase();
92
+ if (code === "ETIMEDOUT") return "timeout";
93
+ if (["EAI_AGAIN", "ENOTFOUND"].includes(code)) return "dns";
94
+ if (code === "ECONNREFUSED") return "connection_refused";
95
+ if (code === "ENETUNREACH") return "network_unreachable";
96
+ if (["ECONNRESET", "EPIPE", "UND_ERR_SOCKET"].includes(code)) {
97
+ return "connection_reset";
98
+ }
99
+ const msg = String(err?.message || err || "").toLowerCase();
100
+ if (/timed?\s*out|etimedout/.test(msg)) return "timeout";
101
+ if (/eai_again|enotfound|dns/.test(msg)) return "dns";
102
+ if (/econnrefused/.test(msg)) return "connection_refused";
103
+ if (/network unreachable|enetunreach/.test(msg)) return "network_unreachable";
104
+ if (
105
+ /econnreset|socket hang ?up|terminated|fetch failed|network error|premature close/.test(
106
+ msg,
107
+ )
108
+ ) {
109
+ return "connection_reset";
110
+ }
111
+ return "unknown";
112
+ }
@@ -48,10 +48,12 @@ import {
48
48
  appendCompactEvent,
49
49
  appendTokenUsage,
50
50
  appendToolCallCompact,
51
+ appendLlmRetryCompact,
51
52
  appendEvent,
52
53
  rebuildMessages,
53
54
  sessionExists,
54
55
  } from "../harness/jsonl-session-store.js";
56
+ import { classifyStreamRetryReason } from "../lib/stream-retry.js";
55
57
  import { CLISkillLoader } from "../lib/skill-loader.js";
56
58
  import { storeMemory, consolidateMemory } from "../lib/hierarchical-memory.js";
57
59
  import { CLIContextEngineering } from "../lib/cli-context-engineering.js";
@@ -119,6 +121,7 @@ import {
119
121
  questionWithIdleTimeout,
120
122
  } from "./permission-prompt.js";
121
123
  import { describeAskContext, raceLocalAndRemote } from "./remote-approval.js";
124
+ import { runWithHostHooksV2Workspace } from "../lib/hooks-v2-workspace-context.js";
122
125
  import {
123
126
  parsePermissionTier,
124
127
  parsePermissionModeArg,
@@ -353,13 +356,25 @@ export async function agentLoop(messages, options) {
353
356
  chalk.gray(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`),
354
357
  );
355
358
  } else if (event.type === "tool-executing") {
356
- _lastExec = { tool: event.tool, args: event.args };
359
+ _lastExec = {
360
+ tool: event.tool,
361
+ args: event.args,
362
+ startedAt: options.now ? options.now() : Date.now(),
363
+ };
357
364
  process.stdout.write(
358
365
  chalk.gray(
359
366
  ` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
360
367
  ),
361
368
  );
362
369
  } else if (event.type === "tool-result") {
370
+ const durationMs =
371
+ event.result?.toolTelemetryRecord?.durationMs ??
372
+ (_lastExec?.tool === event.tool
373
+ ? Math.max(
374
+ 0,
375
+ (options.now ? options.now() : Date.now()) - _lastExec.startedAt,
376
+ )
377
+ : undefined);
363
378
  // 用量归因: persist a compact tool_call record (name + error flag +
364
379
  // skill/plugin hints — never args, which can carry whole file bodies)
365
380
  // so `cc session usage --by tool|mcp|plugin` and `cc insights` can
@@ -375,6 +390,7 @@ export async function agentLoop(messages, options) {
375
390
  ? _lastExec.args?.skill_name
376
391
  : undefined,
377
392
  ...extractPluginUsageAttribution(event.result),
393
+ durationMs,
378
394
  });
379
395
  } catch (_e) {
380
396
  // persistence is best-effort — never break the turn
@@ -462,6 +478,13 @@ export async function agentLoop(messages, options) {
462
478
  * Start the agentic REPL
463
479
  */
464
480
  export async function startAgentRepl(options = {}) {
481
+ const trustedWorkspaceRoot = process.cwd();
482
+ return runWithHostHooksV2Workspace(trustedWorkspaceRoot, () =>
483
+ startAgentReplInWorkspace(options),
484
+ );
485
+ }
486
+
487
+ async function startAgentReplInWorkspace(options = {}) {
465
488
  // EPIPE guard: if the REPL's stdout is piped and the consumer closes (e.g.
466
489
  // `cc agent | head`), the async stream `error` would otherwise crash the
467
490
  // process. Route a broken pipe into the REPL's own graceful shutdown (the
@@ -5570,12 +5593,26 @@ export async function startAgentRepl(options = {}) {
5570
5593
  // streaming call hits a transient connection drop and retries, tell the
5571
5594
  // user instead of leaving them staring at a silent pause. To stderr so
5572
5595
  // it never corrupts the streamed answer on stdout.
5573
- onStreamRetry: (attempt) =>
5596
+ onStreamRetry: (attempt, error, telemetry = {}) => {
5574
5597
  process.stderr.write(
5575
5598
  chalk.dim(
5576
5599
  ` ⟳ connection dropped — retrying (attempt ${attempt})…\n`,
5577
5600
  ),
5578
- ),
5601
+ );
5602
+ if (useJsonl && sessionId) {
5603
+ try {
5604
+ appendLlmRetryCompact(sessionId, {
5605
+ attempt,
5606
+ durationMs: telemetry.durationMs,
5607
+ provider: telemetry.provider || provider,
5608
+ model: telemetry.model || activeModel,
5609
+ reason: classifyStreamRetryReason(error),
5610
+ });
5611
+ } catch {
5612
+ /* usage telemetry is best-effort */
5613
+ }
5614
+ }
5615
+ },
5579
5616
  // Stream-stall hint (Claude-Code 2.1.185): the connection is alive but
5580
5617
  // the API has gone silent mid-response — tell the user we're still
5581
5618
  // waiting instead of leaving a frozen spinner. stderr so it never