chainlesschain 0.162.182 → 0.162.184
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.
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DzZ5_xoj.js → AIOps-DzAl5fGx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DeLXddIg.js → ActionButton-DAHtMGeC.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-rvDCmet1.js → Analytics-BHQaexv5.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Yt6QVDnp.js → AppLayout-Phz_pD0n.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BS112K5L.js → Artifacts-DHt47kCA.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-0wJuQrNu.js → Audit--Y_8FULl.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-B90PMgbn.js → BackgroundAgents-N1zchH7D.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-TkgMglQQ.js → Backup-B_BoyN30.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CQVS9p1l.js → BaseInput-Gl4uQHcT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-YTjQHoC7.js → Chat-Cg-eIyAZ.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BzeUi3JH.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-5bwaOabq.js → Checkbox-D0MGR2wA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BFmNQ_Xe.js → Codegen-BdObJEFn.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BT_u4Dzp.js → Col--ghxXT3z.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CJP52u5b.js → Community-BUFeXOqz.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BpzhJE9i.js → Compact-DhQO2_Mr.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Cg7rfslp.js → Compliance-3HoTqKYd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-JisxL6Yy.js → Cowork-B3zmO4VW.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Bu7Ug_MC.js → Cron-CJDrZOcc.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CvuqoLdj.js → Crosschain-SMl15obd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C2u9G8Pu.js → DID-CgTrTWrl.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BFt5Yv3d.js → Dashboard-dXFKaKol.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DIaT9Wxq.js → Dropdown-qQHEhCvI.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D_A601Y4.js → EmailListRenderer-C_ej0MWH.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard--IyQ8I8w.js → FamilyGuardDashboard-D6ajoE7I.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BihWHxuY.js → Federation-Cio2y49h.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BB8dOXc_.js → FormItemContext-ClJz1cYp.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DQYhj1HH.js → GenericCardRenderer-Dr_8Maca.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B-2QZxGf.js → Git-CevLwp5u.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CusSb-nG.js → Governance-CW9YyQUY.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BAjmvPPL.js → Inference-CQqeAyER.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DoNYWKYq.js → KnowledgeGraph-cuGVxHrw.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B8fKWD-n.js → Logs-DIuHri5d.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BlJm1J7E.js → MarkdownRenderer-R91Qw5JA.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CyOscbSL.js → Marketplace-CY1VhFGx.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Ex6Om5da.js → McpTools-BBAbi1vt.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-CDcD8l8r.js → Memory-BWYGHxJA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-xfBqoFDg.js → MobileBridge-CPIFkW7A.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BwObMMA2.js → MobileProjects-DQyWxCho.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CIWd36OV.js → Mtc-Bo3GnQbx.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CPX0ouuA.js → MtcAudit-C8-c4rSW.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BDJsIUuW.js → Multisig-ChqMTtoq.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DEkbuD3N.js → NLProgramming-C48TyhvI.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DJpHRhH1.js → Notes-zogUFWxp.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C1UZ-Qd5.js → NotificationSettings-v-K9XpSB.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-P94JGGNz.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Bbhj0fac.js → Organization-Boi1dQfR.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-1H04x3kx.js → Overflow-DM5YWwgA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CDpPe3et.js → P2P-C57LKpKK.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-MUX0Bpvw.js → PdhVaultBrowser-BFkPqu6Q.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-A07-0Ci1.js → Permissions-D6fB9YSp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DhhDWxAZ.js → PersonalDataHub-Bp7EefEo.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-CyfbJfe-.js → Pipeline-BVln3hZf.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CNFps3ED.js → Privacy-Bf8P3nF6.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit--T-u3Dr5.js → ProjectInit-DM6wA2E2.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DJmrK2fC.js → ProjectSettings-CGbc5Iho.js} +2 -2
- package/src/assets/web-panel/assets/Projects-D0p9EOxm.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DLzzbOZd.js → Providers-B2kJcxS6.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CexOomKS.js → QrScannerModal-Cxu7dXYy.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-QklGi19_.js → QuickAsk-DTlrTPms.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bb9kwSw8.js → Recommend-Cm3X-cCj.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DZuVNNN5.js → RemoteSession-DNb2kjvt.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Dr5cLP0N.js → Reputation-CZUFFjc1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-oCQHYv5t.js → Row-DAPgloxA.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-IFDEykIR.js → RssFeed-dsx9S793.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CspVIxDc.js → Search-DnnC8kz-.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bm66o_DH.js → Security-VuZknRMK.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DvanNqXl.js → Services-B3XNX8Vx.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CHjEe9_O.js → Skeleton-Dl1nVtCj.js} +1 -1
- package/src/assets/web-panel/assets/Skills-CY09CZ_W.js +1 -0
- package/src/assets/web-panel/assets/{Sla-DxZm1491.js → Sla-HzU56n1z.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DWxEEclC.js → SpeechSettings-CRle7Jl2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-FIvOFw5p.js → SyncSettings-DWadz7Sl.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CNVTDGg8.js +1 -0
- package/src/assets/web-panel/assets/{Templates-CaNjzV_t.js → Templates-BBAgvrlM.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DjlP6TmH.js → Tenant-BsbbvGN8.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-uscpZI4q.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-DoZ8y7y8.js → TimelineRenderer-mTyjNE6r.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-390cg2TC.js → Tokens-Bz97oBgs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CLrtCj5_.js → Trigger-BTRKC140.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DpESfSPn.js → Trust-B4A_KuyX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CN6279O0.js → UkeySign-DES9TmLU.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DPtAUlSe.js → VideoEditing-ClXs7Qxp.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-nKOMV_1n.js → Wallet-IzZqFz-7.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-HuXlSOc4.js → WebAuthn-BtOP04g_.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CUfFjARi.js → WorkflowEditor-_0cqnopY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-6Eq0PcZJ.js → chat-ax7193uU.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BP_Sf6Iy.js → colors-Bu4WGJah.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BufMKIh5.js → compact-item-BX1dlTW0.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BBMXxWSW.js → createContext-CktA_Dj7.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-x877iKKS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-fUK_tRO2.js → hasIn-D5wd817l.js} +1 -1
- package/src/assets/web-panel/assets/{index-t8N1tSeX.js → index-5PYYkr8S.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLpIs_P_.js → index-A4uBPQCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVsqvI4M.js → index-BAtBNfkr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bkg4ZePE.js → index-BLLsUNDc.js} +1 -1
- package/src/assets/web-panel/assets/{index-cpEli44X.js → index-BQtoRyTO.js} +1 -1
- package/src/assets/web-panel/assets/{index-wKTLbKyc.js → index-BRfXoRuh.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7Etk0Te.js → index-Bad2CYeH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwSf5pnf.js → index-BcGdL3sG.js} +1 -1
- package/src/assets/web-panel/assets/{index-3FuwBewR.js → index-C3HmzCLt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHy6yNiF.js → index-C8DkWyNr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgiECHu2.js → index-C8tLR2TG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C44atv4k.js → index-CP8ELJBc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW_G-MXw.js → index-CUForzsG.js} +1 -1
- package/src/assets/web-panel/assets/{index-pum7HYBA.js → index-CYRE0NJA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWKoBU8R.js → index-CeVZaJSC.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4_MuGkd.js → index-CfzxR61U.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dyngbd29.js → index-Ci3y1ZAv.js} +19 -19
- package/src/assets/web-panel/assets/{index-fzaMtSJ8.js → index-CjEJjl5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp8vhyZy.js → index-Cv7bVWJ5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPdUNk1i.js → index-D-o4Rwjk.js} +1 -1
- package/src/assets/web-panel/assets/index-D7pfPouB.js +1 -0
- package/src/assets/web-panel/assets/{index-CeASi4fr.js → index-D9op8poE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DASluSqY.js → index-DF1TysIF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DocdRcbd.js → index-DYo9S5OT.js} +1 -1
- package/src/assets/web-panel/assets/{index-2A5ph8b2.js → index-D_cycOET.js} +1 -1
- package/src/assets/web-panel/assets/{index-wV6iGdpY.js → index-Db3CpWTr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMLHv389.js → index-DkD1K-fF.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9WYSn4m.js → index-DmPJ6T6F.js} +1 -1
- package/src/assets/web-panel/assets/{index-uCbEN7cY.js → index-DmVUeWUk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmkMXwnR.js → index-Dzdr0zDf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZ6Yt17L.js → index-PBLzwYnu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE2DdTP2.js → index-bFCaM7Eq.js} +1 -1
- package/src/assets/web-panel/assets/index-e4i-VLBd.js +1 -0
- package/src/assets/web-panel/assets/{index-GwdRUZ-x.js → index-h3KJZr95.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2vS66eI.js → index-iHcPtQ2e.js} +1 -1
- package/src/assets/web-panel/assets/{index-6GmBa4Kg.js → index-jzlzm4VK.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9vU25ij.js → index-o14NNnxw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGjmILDm.js → index-tYPSfGbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-LvCBfTLo.js → index-ytp2H5Zx.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-V5Fo95FM.js → initDefaultProps-D2Iut83B.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BYWU0hUT.js → motion-BuaJVdK9.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cr1M-J2h.js → move-CaEuM92y.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BysQKi2N.js → mtc-parser-wuKNBwys.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D1zHU8GC.js → omit-7a3eklaq.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CUArxtfi.js → pickAttrs-Dn4z7KvJ.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ChoYEys0.js → placementArrow-DVn_qT4-.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BQRs3Mo7.js → responsiveObserve-Dodz1OMQ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B8zbsCkg.js → slide-CxoGskbp.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C2U8Gs6h.js → statusUtils-DBh_epQH.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Dd8MgL_V.js → styleChecker-RXQ4gG5P.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-hknqY5NH.js → useFlexGapSupport-1Wxm0HEG.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CLTAmcfw.js → useFs-CI_ySNxJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-ByegHbiB.js → usePersonalDataHub-C6973rZP.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DqlAy5DP.js → vnode-CalynwKr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DiXiztQn.js → zoom-CM1cd-Q0.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +74 -1
- package/src/commands/agent.js +9 -0
- package/src/commands/background-session.js +60 -1
- package/src/commands/batch.js +160 -5
- package/src/commands/plugin.js +318 -43
- package/src/commands/session.js +44 -1
- package/src/commands/team.js +212 -6
- package/src/data/changelog.json +26 -0
- package/src/harness/jsonl-session-store.js +59 -6
- package/src/harness/mcp-client.js +65 -43
- package/src/lib/agent-batch.js +4 -1
- package/src/lib/agent-team/team-runner.js +19 -1
- package/src/lib/agent-team/team-worktree.js +3 -0
- package/src/lib/async-hook-supervisor.cjs +82 -10
- package/src/lib/async-hook-supervisor.js +50 -4
- package/src/lib/background-agent-supervisor.js +49 -0
- package/src/lib/collaboration-run-store.js +362 -0
- package/src/lib/hook-runner.cjs +98 -17
- package/src/lib/hook-runner.js +77 -6
- package/src/lib/hook-shell-command.cjs +148 -0
- package/src/lib/hooks-v2-runtime.js +188 -77
- package/src/lib/jsonl-session-store.js +2 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +18 -0
- package/src/lib/lsp/lsp-client.js +12 -1
- package/src/lib/plugin-monitor-supervisor.js +53 -16
- package/src/lib/plugin-runtime/bin.js +1 -1
- package/src/lib/plugin-runtime/install.js +465 -54
- package/src/lib/plugin-runtime/mcp.js +21 -2
- package/src/lib/plugin-runtime/monitors.js +19 -3
- package/src/lib/plugin-runtime/sandbox-policy.js +99 -0
- package/src/lib/plugin-runtime/scopes.js +13 -2
- package/src/lib/plugin-usage-attribution.js +44 -0
- package/src/lib/process-execution-broker/index.js +705 -13
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1347 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +1260 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +1831 -157
- package/src/lib/session-usage.js +278 -1
- package/src/lib/stream-retry.js +29 -0
- package/src/repl/agent-repl.js +38 -9
- package/src/runtime/agent-core.js +32 -4
- package/src/runtime/headless-runner.js +46 -1
- package/src/runtime/headless-stream.js +54 -3
- package/src/runtime/mcp-config.js +15 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-GlJtqLjT.js +0 -1
- package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +0 -1
- package/src/assets/web-panel/assets/Skills-Cb8jWjMX.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +0 -1
- package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +0 -3
- package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +0 -1
- package/src/assets/web-panel/assets/index-DOZTSoS7.js +0 -1
- package/src/assets/web-panel/assets/index-DhpvjpqI.js +0 -1
package/src/lib/session-usage.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
readEvents,
|
|
13
13
|
listJsonlSessions,
|
|
14
14
|
} from "../harness/jsonl-session-store.js";
|
|
15
|
+
import { extractPluginUsageAttribution } from "./plugin-usage-attribution.js";
|
|
15
16
|
|
|
16
17
|
// Injectable disk seams (tests override). readEvents skips malformed JSON lines
|
|
17
18
|
// but does NOT guard readFileSync, so an unreadable session file (EACCES /
|
|
@@ -31,6 +32,33 @@ function toNumber(v) {
|
|
|
31
32
|
return Number.isFinite(n) ? n : 0;
|
|
32
33
|
}
|
|
33
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
|
+
|
|
34
62
|
/**
|
|
35
63
|
* Extract the optional usage-attribution frame (用量归因) persisted alongside
|
|
36
64
|
* a token_usage event's data: `{ origin, skill?, subagentId?, role?,
|
|
@@ -208,6 +236,56 @@ function _isToolResultError(event) {
|
|
|
208
236
|
);
|
|
209
237
|
}
|
|
210
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
|
+
|
|
211
289
|
/**
|
|
212
290
|
* Aggregate the transcript's tool events: per-tool call/error counts, an MCP
|
|
213
291
|
* server bucket (`mcp__<server>__*`), and a turn-associated token figure.
|
|
@@ -228,8 +306,12 @@ function _isToolResultError(event) {
|
|
|
228
306
|
export function aggregateToolCalls(events) {
|
|
229
307
|
const byTool = new Map();
|
|
230
308
|
const byServer = new Map();
|
|
309
|
+
const byPlugin = new Map();
|
|
231
310
|
let totalCalls = 0;
|
|
232
311
|
let totalErrors = 0;
|
|
312
|
+
let totalDurationMs = 0;
|
|
313
|
+
let timedCalls = 0;
|
|
314
|
+
let retryCalls = 0;
|
|
233
315
|
|
|
234
316
|
const bumpTool = (name) => {
|
|
235
317
|
let t = byTool.get(name);
|
|
@@ -253,19 +335,46 @@ export function aggregateToolCalls(events) {
|
|
|
253
335
|
}
|
|
254
336
|
return s;
|
|
255
337
|
};
|
|
338
|
+
const bumpPlugin = (plugin, version = null) => {
|
|
339
|
+
const key = `${plugin}@${version || "?"}`;
|
|
340
|
+
let row = byPlugin.get(key);
|
|
341
|
+
if (!row) {
|
|
342
|
+
row = {
|
|
343
|
+
plugin,
|
|
344
|
+
version: version || null,
|
|
345
|
+
calls: 0,
|
|
346
|
+
errors: 0,
|
|
347
|
+
turnTokens: 0,
|
|
348
|
+
};
|
|
349
|
+
byPlugin.set(key, row);
|
|
350
|
+
}
|
|
351
|
+
return row;
|
|
352
|
+
};
|
|
256
353
|
|
|
257
354
|
let turnTools = new Set();
|
|
258
355
|
let turnServers = new Set();
|
|
356
|
+
let turnPlugins = new Set();
|
|
259
357
|
let turnTokens = 0;
|
|
358
|
+
let pendingPlugin = null;
|
|
359
|
+
let pendingObservation = null;
|
|
360
|
+
let failedToolsInTurn = new Set();
|
|
260
361
|
const flushTurn = () => {
|
|
261
362
|
if (turnTokens > 0) {
|
|
262
363
|
for (const name of turnTools) bumpTool(name).turnTokens += turnTokens;
|
|
263
364
|
for (const server of turnServers)
|
|
264
365
|
bumpServer(server).turnTokens += turnTokens;
|
|
366
|
+
for (const key of turnPlugins) {
|
|
367
|
+
const row = byPlugin.get(key);
|
|
368
|
+
if (row) row.turnTokens += turnTokens;
|
|
369
|
+
}
|
|
265
370
|
}
|
|
266
371
|
turnTools = new Set();
|
|
267
372
|
turnServers = new Set();
|
|
373
|
+
turnPlugins = new Set();
|
|
268
374
|
turnTokens = 0;
|
|
375
|
+
pendingPlugin = null;
|
|
376
|
+
pendingObservation = null;
|
|
377
|
+
failedToolsInTurn = new Set();
|
|
269
378
|
};
|
|
270
379
|
|
|
271
380
|
for (const evt of events || []) {
|
|
@@ -276,34 +385,107 @@ export function aggregateToolCalls(events) {
|
|
|
276
385
|
}
|
|
277
386
|
if (evt.type === "tool_call") {
|
|
278
387
|
const name = evt.data?.tool || "?";
|
|
388
|
+
const durationMs = eventDuration(evt);
|
|
389
|
+
const isRetry = failedToolsInTurn.has(name);
|
|
279
390
|
totalCalls++;
|
|
280
391
|
const t = bumpTool(name);
|
|
281
392
|
t.calls++;
|
|
282
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
|
+
}
|
|
283
403
|
if (isErr) {
|
|
284
404
|
totalErrors++;
|
|
285
405
|
t.errors++;
|
|
286
406
|
}
|
|
407
|
+
if (isErr) failedToolsInTurn.add(name);
|
|
408
|
+
else failedToolsInTurn.delete(name);
|
|
287
409
|
turnTools.add(name);
|
|
288
410
|
const server = mcpServerOf(name);
|
|
289
411
|
if (server) {
|
|
290
412
|
const s = bumpServer(server);
|
|
291
413
|
s.calls++;
|
|
292
414
|
if (isErr) s.errors++;
|
|
415
|
+
addObservedDuration(s, durationMs);
|
|
416
|
+
if (isRetry) addObservedRetry(s);
|
|
293
417
|
turnServers.add(server);
|
|
294
418
|
}
|
|
419
|
+
const plugin =
|
|
420
|
+
typeof evt.data?.plugin === "string" && evt.data.plugin
|
|
421
|
+
? evt.data.plugin
|
|
422
|
+
: null;
|
|
423
|
+
const pluginVersion =
|
|
424
|
+
typeof evt.data?.plugin_version === "string"
|
|
425
|
+
? evt.data.plugin_version
|
|
426
|
+
: null;
|
|
427
|
+
pendingPlugin = { tool: name, plugin, version: pluginVersion };
|
|
428
|
+
pendingObservation = {
|
|
429
|
+
tool: name,
|
|
430
|
+
durationCounted: durationMs !== null,
|
|
431
|
+
errorCounted: isErr,
|
|
432
|
+
};
|
|
433
|
+
if (plugin) {
|
|
434
|
+
const p = bumpPlugin(plugin, pluginVersion);
|
|
435
|
+
p.calls++;
|
|
436
|
+
if (isErr) p.errors++;
|
|
437
|
+
addObservedDuration(p, durationMs);
|
|
438
|
+
if (isRetry) addObservedRetry(p);
|
|
439
|
+
turnPlugins.add(`${plugin}@${pluginVersion || "?"}`);
|
|
440
|
+
}
|
|
295
441
|
continue;
|
|
296
442
|
}
|
|
297
443
|
if (evt.type === "tool_result") {
|
|
298
444
|
const name = evt.data?.tool || "?";
|
|
445
|
+
const durationMs = eventDuration(evt);
|
|
299
446
|
turnTools.add(name);
|
|
300
447
|
const server = mcpServerOf(name);
|
|
301
448
|
if (server) turnServers.add(server);
|
|
302
|
-
|
|
449
|
+
const isErr = _isToolResultError(evt);
|
|
450
|
+
const pairedObservation =
|
|
451
|
+
pendingObservation?.tool === name ? pendingObservation : null;
|
|
452
|
+
if (isErr && !pairedObservation?.errorCounted) {
|
|
303
453
|
totalErrors++;
|
|
304
454
|
bumpTool(name).errors++;
|
|
305
455
|
if (server) bumpServer(server).errors++;
|
|
306
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
|
+
}
|
|
465
|
+
const extracted = extractPluginUsageAttribution(evt.data?.result);
|
|
466
|
+
const plugin =
|
|
467
|
+
typeof evt.data?.plugin === "string" && evt.data.plugin
|
|
468
|
+
? evt.data.plugin
|
|
469
|
+
: extracted.plugin || null;
|
|
470
|
+
const pluginVersion =
|
|
471
|
+
typeof evt.data?.plugin_version === "string"
|
|
472
|
+
? evt.data.plugin_version
|
|
473
|
+
: extracted.pluginVersion || null;
|
|
474
|
+
if (plugin) {
|
|
475
|
+
const p = bumpPlugin(plugin, pluginVersion);
|
|
476
|
+
const countedByCall =
|
|
477
|
+
pendingPlugin?.tool === name &&
|
|
478
|
+
pendingPlugin?.plugin === plugin &&
|
|
479
|
+
(pendingPlugin?.version || null) === (pluginVersion || null);
|
|
480
|
+
if (!countedByCall) p.calls++;
|
|
481
|
+
if (isErr && !countedByCall) p.errors++;
|
|
482
|
+
if (!countedByCall || !pairedObservation?.durationCounted) {
|
|
483
|
+
addObservedDuration(p, durationMs);
|
|
484
|
+
}
|
|
485
|
+
turnPlugins.add(`${plugin}@${pluginVersion || "?"}`);
|
|
486
|
+
}
|
|
487
|
+
pendingPlugin = null;
|
|
488
|
+
pendingObservation = null;
|
|
307
489
|
continue;
|
|
308
490
|
}
|
|
309
491
|
const u = extractUsage(evt);
|
|
@@ -315,8 +497,11 @@ export function aggregateToolCalls(events) {
|
|
|
315
497
|
return {
|
|
316
498
|
totalCalls,
|
|
317
499
|
totalErrors,
|
|
500
|
+
...(timedCalls > 0 ? { totalDurationMs, timedCalls } : {}),
|
|
501
|
+
...(retryCalls > 0 ? { retryCalls } : {}),
|
|
318
502
|
byTool: Array.from(byTool.values()).sort(byCallsDesc),
|
|
319
503
|
byMcpServer: Array.from(byServer.values()).sort(byCallsDesc),
|
|
504
|
+
byPlugin: Array.from(byPlugin.values()).sort(byCallsDesc),
|
|
320
505
|
};
|
|
321
506
|
}
|
|
322
507
|
|
|
@@ -325,9 +510,11 @@ export function aggregateToolCalls(events) {
|
|
|
325
510
|
* roll-ups + the tool/MCP call aggregation. Pure.
|
|
326
511
|
*/
|
|
327
512
|
export function sessionAttribution(events) {
|
|
513
|
+
const retries = aggregateLlmRetries(events);
|
|
328
514
|
return {
|
|
329
515
|
...aggregateUsageAttribution(events),
|
|
330
516
|
tools: aggregateToolCalls(events),
|
|
517
|
+
...(retries ? { retries } : {}),
|
|
331
518
|
};
|
|
332
519
|
}
|
|
333
520
|
|
|
@@ -457,8 +644,16 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
457
644
|
const gSubagent = new Map();
|
|
458
645
|
const gTool = new Map();
|
|
459
646
|
const gServer = new Map();
|
|
647
|
+
const gPlugin = new Map();
|
|
648
|
+
const gRetryReason = new Map();
|
|
649
|
+
const gRetryModel = new Map();
|
|
460
650
|
let gToolCalls = 0;
|
|
461
651
|
let gToolErrors = 0;
|
|
652
|
+
let gToolDurationMs = 0;
|
|
653
|
+
let gTimedToolCalls = 0;
|
|
654
|
+
let gToolRetryCalls = 0;
|
|
655
|
+
let gLlmRetries = 0;
|
|
656
|
+
let gLlmRetryDurationMs = 0;
|
|
462
657
|
const mergeRow = (map, key, row, seed) => {
|
|
463
658
|
const entry = map.get(key) || { ...seed, ..._newSums() };
|
|
464
659
|
_addSums(entry, row);
|
|
@@ -483,6 +678,9 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
483
678
|
const tools = a.tools || {};
|
|
484
679
|
gToolCalls += tools.totalCalls || 0;
|
|
485
680
|
gToolErrors += tools.totalErrors || 0;
|
|
681
|
+
gToolDurationMs += tools.totalDurationMs || 0;
|
|
682
|
+
gTimedToolCalls += tools.timedCalls || 0;
|
|
683
|
+
gToolRetryCalls += tools.retryCalls || 0;
|
|
486
684
|
for (const row of tools.byTool || []) {
|
|
487
685
|
const entry = gTool.get(row.tool) || {
|
|
488
686
|
tool: row.tool,
|
|
@@ -494,6 +692,12 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
494
692
|
entry.calls += row.calls || 0;
|
|
495
693
|
entry.errors += row.errors || 0;
|
|
496
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);
|
|
497
701
|
gTool.set(row.tool, entry);
|
|
498
702
|
}
|
|
499
703
|
for (const row of tools.byMcpServer || []) {
|
|
@@ -506,10 +710,65 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
506
710
|
entry.calls += row.calls || 0;
|
|
507
711
|
entry.errors += row.errors || 0;
|
|
508
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);
|
|
509
719
|
gServer.set(row.server, entry);
|
|
510
720
|
}
|
|
721
|
+
for (const row of tools.byPlugin || []) {
|
|
722
|
+
const key = `${row.plugin}@${row.version || "?"}`;
|
|
723
|
+
const entry = gPlugin.get(key) || {
|
|
724
|
+
plugin: row.plugin,
|
|
725
|
+
version: row.version || null,
|
|
726
|
+
calls: 0,
|
|
727
|
+
errors: 0,
|
|
728
|
+
turnTokens: 0,
|
|
729
|
+
};
|
|
730
|
+
entry.calls += row.calls || 0;
|
|
731
|
+
entry.errors += row.errors || 0;
|
|
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);
|
|
739
|
+
gPlugin.set(key, entry);
|
|
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
|
+
}
|
|
511
768
|
}
|
|
512
769
|
const byCallsDesc = (a, b) => b.calls - a.calls;
|
|
770
|
+
const byRetriesDesc = (a, b) =>
|
|
771
|
+
b.retries - a.retries || b.durationMs - a.durationMs;
|
|
513
772
|
|
|
514
773
|
return {
|
|
515
774
|
sessions: perSession,
|
|
@@ -525,9 +784,27 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
525
784
|
tools: {
|
|
526
785
|
totalCalls: gToolCalls,
|
|
527
786
|
totalErrors: gToolErrors,
|
|
787
|
+
...(gTimedToolCalls > 0
|
|
788
|
+
? {
|
|
789
|
+
totalDurationMs: gToolDurationMs,
|
|
790
|
+
timedCalls: gTimedToolCalls,
|
|
791
|
+
}
|
|
792
|
+
: {}),
|
|
793
|
+
...(gToolRetryCalls > 0 ? { retryCalls: gToolRetryCalls } : {}),
|
|
528
794
|
byTool: Array.from(gTool.values()).sort(byCallsDesc),
|
|
529
795
|
byMcpServer: Array.from(gServer.values()).sort(byCallsDesc),
|
|
796
|
+
byPlugin: Array.from(gPlugin.values()).sort(byCallsDesc),
|
|
530
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
|
+
: {}),
|
|
531
808
|
},
|
|
532
809
|
};
|
|
533
810
|
}
|
package/src/lib/stream-retry.js
CHANGED
|
@@ -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
|
+
}
|
package/src/repl/agent-repl.js
CHANGED
|
@@ -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";
|
|
@@ -108,6 +110,7 @@ import {
|
|
|
108
110
|
import { resolveSlashMacro } from "./slash-macro.js";
|
|
109
111
|
import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
|
|
110
112
|
import { newCostStore, addUsage } from "./session-cost.js";
|
|
113
|
+
import { extractPluginUsageAttribution } from "../lib/plugin-usage-attribution.js";
|
|
111
114
|
import { parseThinkCommand, parseEffortCommand } from "./think-command.js";
|
|
112
115
|
import { parseBtwCommand, buildAsideBlock, applyAside } from "./btw-command.js";
|
|
113
116
|
import { shouldStreamLive } from "./stream-decision.js";
|
|
@@ -352,17 +355,29 @@ export async function agentLoop(messages, options) {
|
|
|
352
355
|
chalk.gray(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`),
|
|
353
356
|
);
|
|
354
357
|
} else if (event.type === "tool-executing") {
|
|
355
|
-
_lastExec = {
|
|
358
|
+
_lastExec = {
|
|
359
|
+
tool: event.tool,
|
|
360
|
+
args: event.args,
|
|
361
|
+
startedAt: options.now ? options.now() : Date.now(),
|
|
362
|
+
};
|
|
356
363
|
process.stdout.write(
|
|
357
364
|
chalk.gray(
|
|
358
365
|
` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
|
|
359
366
|
),
|
|
360
367
|
);
|
|
361
368
|
} else if (event.type === "tool-result") {
|
|
369
|
+
const durationMs =
|
|
370
|
+
event.result?.toolTelemetryRecord?.durationMs ??
|
|
371
|
+
(_lastExec?.tool === event.tool
|
|
372
|
+
? Math.max(
|
|
373
|
+
0,
|
|
374
|
+
(options.now ? options.now() : Date.now()) - _lastExec.startedAt,
|
|
375
|
+
)
|
|
376
|
+
: undefined);
|
|
362
377
|
// 用量归因: persist a compact tool_call record (name + error flag +
|
|
363
|
-
// skill
|
|
364
|
-
// `cc session usage --by tool|mcp` and `cc insights` can
|
|
365
|
-
// tool use for REPL sessions. Best-effort; opt out with
|
|
378
|
+
// skill/plugin hints — never args, which can carry whole file bodies)
|
|
379
|
+
// so `cc session usage --by tool|mcp|plugin` and `cc insights` can
|
|
380
|
+
// aggregate tool use for REPL sessions. Best-effort; opt out with
|
|
366
381
|
// options.persistToolCalls === false.
|
|
367
382
|
if (options.sessionId && options.persistToolCalls !== false) {
|
|
368
383
|
try {
|
|
@@ -373,6 +388,8 @@ export async function agentLoop(messages, options) {
|
|
|
373
388
|
event.tool === "run_skill" && _lastExec?.tool === "run_skill"
|
|
374
389
|
? _lastExec.args?.skill_name
|
|
375
390
|
: undefined,
|
|
391
|
+
...extractPluginUsageAttribution(event.result),
|
|
392
|
+
durationMs,
|
|
376
393
|
});
|
|
377
394
|
} catch (_e) {
|
|
378
395
|
// persistence is best-effort — never break the turn
|
|
@@ -1844,9 +1861,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
1844
1861
|
" Open this secure page? [y/N] ",
|
|
1845
1862
|
),
|
|
1846
1863
|
async (answer) => {
|
|
1847
|
-
const approved = /^(y|yes)$/i.test(
|
|
1848
|
-
String(answer || "").trim(),
|
|
1849
|
-
);
|
|
1864
|
+
const approved = /^(y|yes)$/i.test(String(answer || "").trim());
|
|
1850
1865
|
if (!approved) {
|
|
1851
1866
|
resolve({ action: "decline" });
|
|
1852
1867
|
rl.prompt();
|
|
@@ -5570,12 +5585,26 @@ export async function startAgentRepl(options = {}) {
|
|
|
5570
5585
|
// streaming call hits a transient connection drop and retries, tell the
|
|
5571
5586
|
// user instead of leaving them staring at a silent pause. To stderr so
|
|
5572
5587
|
// it never corrupts the streamed answer on stdout.
|
|
5573
|
-
onStreamRetry: (attempt) =>
|
|
5588
|
+
onStreamRetry: (attempt, error, telemetry = {}) => {
|
|
5574
5589
|
process.stderr.write(
|
|
5575
5590
|
chalk.dim(
|
|
5576
5591
|
` ⟳ connection dropped — retrying (attempt ${attempt})…\n`,
|
|
5577
5592
|
),
|
|
5578
|
-
)
|
|
5593
|
+
);
|
|
5594
|
+
if (useJsonl && sessionId) {
|
|
5595
|
+
try {
|
|
5596
|
+
appendLlmRetryCompact(sessionId, {
|
|
5597
|
+
attempt,
|
|
5598
|
+
durationMs: telemetry.durationMs,
|
|
5599
|
+
provider: telemetry.provider || provider,
|
|
5600
|
+
model: telemetry.model || activeModel,
|
|
5601
|
+
reason: classifyStreamRetryReason(error),
|
|
5602
|
+
});
|
|
5603
|
+
} catch {
|
|
5604
|
+
/* usage telemetry is best-effort */
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
},
|
|
5579
5608
|
// Stream-stall hint (Claude-Code 2.1.185): the connection is alive but
|
|
5580
5609
|
// the API has gone silent mid-response — tell the user we're still
|
|
5581
5610
|
// waiting instead of leaving a frozen spinner. stderr so it never
|
|
@@ -6742,7 +6742,15 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
6742
6742
|
options.streamStallMs,
|
|
6743
6743
|
options.streamStallTimeoutMs,
|
|
6744
6744
|
),
|
|
6745
|
-
{
|
|
6745
|
+
{
|
|
6746
|
+
signal,
|
|
6747
|
+
onRetry: (attempt, error, telemetry) =>
|
|
6748
|
+
options.onStreamRetry?.(attempt, error, {
|
|
6749
|
+
...telemetry,
|
|
6750
|
+
provider: "ollama",
|
|
6751
|
+
model: model || null,
|
|
6752
|
+
}),
|
|
6753
|
+
},
|
|
6746
6754
|
);
|
|
6747
6755
|
}
|
|
6748
6756
|
const response = await fetch(apiUrl, {
|
|
@@ -6868,7 +6876,15 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
6868
6876
|
options.streamStallMs,
|
|
6869
6877
|
options.streamStallTimeoutMs,
|
|
6870
6878
|
),
|
|
6871
|
-
{
|
|
6879
|
+
{
|
|
6880
|
+
signal,
|
|
6881
|
+
onRetry: (attempt, error, telemetry) =>
|
|
6882
|
+
options.onStreamRetry?.(attempt, error, {
|
|
6883
|
+
...telemetry,
|
|
6884
|
+
provider: "anthropic",
|
|
6885
|
+
model: effModel,
|
|
6886
|
+
}),
|
|
6887
|
+
},
|
|
6872
6888
|
);
|
|
6873
6889
|
}
|
|
6874
6890
|
|
|
@@ -6972,7 +6988,15 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
6972
6988
|
options.streamStallMs,
|
|
6973
6989
|
options.streamStallTimeoutMs,
|
|
6974
6990
|
),
|
|
6975
|
-
{
|
|
6991
|
+
{
|
|
6992
|
+
signal,
|
|
6993
|
+
onRetry: (attempt, error, telemetry) =>
|
|
6994
|
+
options.onStreamRetry?.(attempt, error, {
|
|
6995
|
+
...telemetry,
|
|
6996
|
+
provider,
|
|
6997
|
+
model: model || defaultModels[provider] || "gpt-4o-mini",
|
|
6998
|
+
}),
|
|
6999
|
+
},
|
|
6976
7000
|
);
|
|
6977
7001
|
}
|
|
6978
7002
|
|
|
@@ -7171,8 +7195,10 @@ export async function _retryStreamingChat(streamFn, opts = {}) {
|
|
|
7171
7195
|
const base = opts.baseDelayMs ?? STREAM_RETRY_BASE_MS;
|
|
7172
7196
|
const signal = opts.signal;
|
|
7173
7197
|
const sleep = opts.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
7198
|
+
const now = typeof opts.now === "function" ? opts.now : Date.now;
|
|
7174
7199
|
let attempt = 0;
|
|
7175
7200
|
for (;;) {
|
|
7201
|
+
const attemptStartedAt = now();
|
|
7176
7202
|
try {
|
|
7177
7203
|
return await streamFn();
|
|
7178
7204
|
} catch (err) {
|
|
@@ -7181,7 +7207,9 @@ export async function _retryStreamingChat(streamFn, opts = {}) {
|
|
|
7181
7207
|
attempt++;
|
|
7182
7208
|
if (typeof opts.onRetry === "function") {
|
|
7183
7209
|
try {
|
|
7184
|
-
opts.onRetry(attempt, err
|
|
7210
|
+
opts.onRetry(attempt, err, {
|
|
7211
|
+
durationMs: Math.max(0, now() - attemptStartedAt),
|
|
7212
|
+
});
|
|
7185
7213
|
} catch {
|
|
7186
7214
|
/* the retry notice is best-effort; never let it mask the retry */
|
|
7187
7215
|
}
|