chainlesschain 0.162.183 → 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-BapS7lz4.js → AIOps-DzAl5fGx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CXYdXEUI.js → ActionButton-DAHtMGeC.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CKR5Lexn.js → Analytics-BHQaexv5.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DATVUOXB.js → AppLayout-Phz_pD0n.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-vb_0ZlV1.js → Artifacts-DHt47kCA.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BI1l9GWt.js → Audit--Y_8FULl.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BFXbaUEf.js → BackgroundAgents-N1zchH7D.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DEYBJJ6H.js → Backup-B_BoyN30.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-COdzeO0M.js → BaseInput-Gl4uQHcT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-oSN6sfM5.js → Chat-Cg-eIyAZ.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BzeUi3JH.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BezEa4tm.js → Checkbox-D0MGR2wA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CNEL0Ife.js → Codegen-BdObJEFn.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Bf2Y2Bn1.js → Col--ghxXT3z.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BfUlu4Hc.js → Community-BUFeXOqz.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BN4xWP9e.js → Compact-DhQO2_Mr.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bt6BKmEP.js → Compliance-3HoTqKYd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DKLp8YxO.js → Cowork-B3zmO4VW.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-o7Hhw6ky.js → Cron-CJDrZOcc.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DaNXWJIw.js → Crosschain-SMl15obd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B-Qa2mha.js → DID-CgTrTWrl.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CMX0Qnxw.js → Dashboard-dXFKaKol.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BLuLyRu_.js → Dropdown-qQHEhCvI.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CFBAeMij.js → EmailListRenderer-C_ej0MWH.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DWzXQ67V.js → FamilyGuardDashboard-D6ajoE7I.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DB1QSLgr.js → Federation-Cio2y49h.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C-OKCToP.js → FormItemContext-ClJz1cYp.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DMJFtFZf.js → GenericCardRenderer-Dr_8Maca.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DsGSujOV.js → Git-CevLwp5u.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B0iS9NEW.js → Governance-CW9YyQUY.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CH004H7c.js → Inference-CQqeAyER.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Q9MTQ83c.js → KnowledgeGraph-cuGVxHrw.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B_fyjZK9.js → Logs-DIuHri5d.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-C0o4FtpD.js → MarkdownRenderer-R91Qw5JA.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BIStF9d1.js → Marketplace-CY1VhFGx.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-pGfal768.js → McpTools-BBAbi1vt.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-xbNqpIqB.js → Memory-BWYGHxJA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-B5EQGrIK.js → MobileBridge-CPIFkW7A.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-DQyWxCho.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-a_rmfxJH.js → Mtc-Bo3GnQbx.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BmKuofUr.js → MtcAudit-C8-c4rSW.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BQbJ-CAN.js → Multisig-ChqMTtoq.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-JtPJKX25.js → NLProgramming-C48TyhvI.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-ZFKznTSG.js → Notes-zogUFWxp.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-B6aHRBBR.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-BFDTAUgS.js → Organization-Boi1dQfR.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-0FoTfbWH.js → Overflow-DM5YWwgA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CWd90etw.js → P2P-C57LKpKK.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DtCGC-W-.js → PdhVaultBrowser-BFkPqu6Q.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CvQ7nr7T.js → Permissions-D6fB9YSp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-rdNPcR-Z.js → PersonalDataHub-Bp7EefEo.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-CmrfiNOs.js → Pipeline-BVln3hZf.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D7ep1h_t.js → Privacy-Bf8P3nF6.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-C0xKINHS.js → ProjectInit-DM6wA2E2.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-AOmZFp-F.js → ProjectSettings-CGbc5Iho.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BWHSkXZP.js → Projects-D0p9EOxm.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Cb7JCTPL.js → Providers-B2kJcxS6.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BDMehcXC.js → QrScannerModal-Cxu7dXYy.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BF6_1nvx.js → QuickAsk-DTlrTPms.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BHfT-A28.js → Recommend-Cm3X-cCj.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-DNb2kjvt.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-CsWHLEcj.js → Reputation-CZUFFjc1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D6srnWhm.js → Row-DAPgloxA.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Xhr-KyVd.js → RssFeed-dsx9S793.js} +3 -3
- package/src/assets/web-panel/assets/{Search-vIbIJvwL.js → Search-DnnC8kz-.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bzi5Aiko.js → Security-VuZknRMK.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BHtVX52P.js → Services-B3XNX8Vx.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C0R8B2jf.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-0B37mxVU.js → Sla-HzU56n1z.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-0V2jE1Bs.js → SpeechSettings-CRle7Jl2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CYedRzaj.js → SyncSettings-DWadz7Sl.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-D-R1x8vm.js → Tasks-CNVTDGg8.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BKoHztGT.js → Templates-BBAgvrlM.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant--17nwVNZ.js → Tenant-BsbbvGN8.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-uscpZI4q.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-C5jx4AE3.js → TimelineRenderer-mTyjNE6r.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DYJKYa9t.js → Tokens-Bz97oBgs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DChpWiQ9.js → Trigger-BTRKC140.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DfWG52EA.js → Trust-B4A_KuyX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CMugYcyA.js → UkeySign-DES9TmLU.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BOSmrHAd.js → VideoEditing-ClXs7Qxp.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-D5g3k3Sa.js → Wallet-IzZqFz-7.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-D-7u4Mkc.js → WebAuthn-BtOP04g_.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-CFzZMSUJ.js → WorkflowEditor-_0cqnopY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-VbR6l6mc.js → chat-ax7193uU.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BSZn8EDS.js → colors-Bu4WGJah.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-y74obMEq.js → compact-item-BX1dlTW0.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C326bjex.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-D_INJQT_.js → hasIn-D5wd817l.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsovTpx9.js → index-5PYYkr8S.js} +1 -1
- package/src/assets/web-panel/assets/{index-9PIVuXqo.js → index-A4uBPQCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjcWKg1S.js → index-BAtBNfkr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMHqs3Jb.js → index-BLLsUNDc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7SrC-On.js → index-BQtoRyTO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUU2606o.js → index-BRfXoRuh.js} +1 -1
- package/src/assets/web-panel/assets/{index-Iw0FfqUz.js → index-Bad2CYeH.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dlxh9D8D.js → index-BcGdL3sG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuIkfnTF.js → index-C3HmzCLt.js} +1 -1
- package/src/assets/web-panel/assets/{index-E_v1ZCTP.js → index-C8DkWyNr.js} +1 -1
- package/src/assets/web-panel/assets/{index-4eyH-eC2.js → index-C8tLR2TG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnSfnSMP.js → index-CP8ELJBc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DwHSZLXg.js → index-CUForzsG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2NyllnW.js → index-CYRE0NJA.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bnua8IX3.js → index-CeVZaJSC.js} +1 -1
- package/src/assets/web-panel/assets/{index-KF9Zmqln.js → index-CfzxR61U.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cr3uuz0C.js → index-Ci3y1ZAv.js} +3 -3
- package/src/assets/web-panel/assets/{index-DBkzrpiL.js → index-CjEJjl5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-abmn_8Yz.js → index-Cv7bVWJ5.js} +1 -1
- package/src/assets/web-panel/assets/{index-qMA4GYdQ.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-jYaogFE8.js → index-D9op8poE.js} +1 -1
- package/src/assets/web-panel/assets/{index-GA80ir_u.js → index-DF1TysIF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzE53HiB.js → index-DYo9S5OT.js} +1 -1
- package/src/assets/web-panel/assets/{index-gINyI1rh.js → index-D_cycOET.js} +1 -1
- package/src/assets/web-panel/assets/{index-FQBsuExX.js → index-Db3CpWTr.js} +1 -1
- package/src/assets/web-panel/assets/{index-IZAWt0H8.js → index-DkD1K-fF.js} +1 -1
- package/src/assets/web-panel/assets/{index-C327pLbw.js → index-DmPJ6T6F.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdyjrCAu.js → index-DmVUeWUk.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfLJ42kv.js → index-Dzdr0zDf.js} +1 -1
- package/src/assets/web-panel/assets/{index-DkH1JvEt.js → index-PBLzwYnu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNMxIsDg.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-C-ZCaUJc.js → index-h3KJZr95.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bfw2hf2s.js → index-iHcPtQ2e.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJi3UqBt.js → index-jzlzm4VK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY8-nCgs.js → index-o14NNnxw.js} +1 -1
- package/src/assets/web-panel/assets/{index-nSn2-C4N.js → index-tYPSfGbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-4PIeTM--.js → index-ytp2H5Zx.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DI0Zaf8-.js → initDefaultProps-D2Iut83B.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BZDKSy3Z.js → motion-BuaJVdK9.js} +1 -1
- package/src/assets/web-panel/assets/{move-DsIW01Vp.js → move-CaEuM92y.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CohkDhsf.js → mtc-parser-wuKNBwys.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DpiN5rzg.js → omit-7a3eklaq.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qbuqni3y.js → pickAttrs-Dn4z7KvJ.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-mBky6714.js → placementArrow-DVn_qT4-.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Bas3eQ2K.js → responsiveObserve-Dodz1OMQ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CBx_SKoK.js → slide-CxoGskbp.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BlYguJSV.js → statusUtils-DBh_epQH.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BCYQmRGv.js → styleChecker-RXQ4gG5P.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Dp5dybLM.js → useFlexGapSupport-1Wxm0HEG.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DHAMBgA2.js → useFs-CI_ySNxJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-W6jsSw76.js → usePersonalDataHub-C6973rZP.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BkI6aayc.js → vnode-CalynwKr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-C_IMGueq.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/background-session.js +11 -1
- package/src/commands/batch.js +160 -5
- package/src/commands/plugin.js +90 -27
- package/src/commands/session.js +31 -1
- package/src/commands/team.js +212 -6
- package/src/data/changelog.json +13 -0
- package/src/harness/jsonl-session-store.js +57 -6
- package/src/harness/mcp-client.js +65 -43
- package/src/lib/agent-batch.js +4 -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/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 +249 -57
- 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/process-execution-broker/index.js +453 -12
- 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 +580 -56
- package/src/lib/session-usage.js +202 -2
- package/src/lib/stream-retry.js +29 -0
- package/src/repl/agent-repl.js +32 -3
- package/src/runtime/agent-core.js +32 -4
- package/src/runtime/headless-runner.js +37 -0
- package/src/runtime/headless-stream.js +34 -2
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CI_jT0ru.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DdcsOqEz.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +0 -5
- package/src/assets/web-panel/assets/Skills-DtT6rWOX.js +0 -1
- package/src/assets/web-panel/assets/Terminal-Dnkos7vW.js +0 -3
- package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +0 -1
- package/src/assets/web-panel/assets/index-5iAFwYx1.js +0 -1
- package/src/assets/web-panel/assets/index-BfnB7LPx.js +0 -1
package/src/lib/session-usage.js
CHANGED
|
@@ -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
|
-
|
|
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 (
|
|
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
|
}
|
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";
|
|
@@ -353,13 +355,25 @@ export async function agentLoop(messages, options) {
|
|
|
353
355
|
chalk.gray(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`),
|
|
354
356
|
);
|
|
355
357
|
} else if (event.type === "tool-executing") {
|
|
356
|
-
_lastExec = {
|
|
358
|
+
_lastExec = {
|
|
359
|
+
tool: event.tool,
|
|
360
|
+
args: event.args,
|
|
361
|
+
startedAt: options.now ? options.now() : Date.now(),
|
|
362
|
+
};
|
|
357
363
|
process.stdout.write(
|
|
358
364
|
chalk.gray(
|
|
359
365
|
` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
|
|
360
366
|
),
|
|
361
367
|
);
|
|
362
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);
|
|
363
377
|
// 用量归因: persist a compact tool_call record (name + error flag +
|
|
364
378
|
// skill/plugin hints — never args, which can carry whole file bodies)
|
|
365
379
|
// so `cc session usage --by tool|mcp|plugin` and `cc insights` can
|
|
@@ -375,6 +389,7 @@ export async function agentLoop(messages, options) {
|
|
|
375
389
|
? _lastExec.args?.skill_name
|
|
376
390
|
: undefined,
|
|
377
391
|
...extractPluginUsageAttribution(event.result),
|
|
392
|
+
durationMs,
|
|
378
393
|
});
|
|
379
394
|
} catch (_e) {
|
|
380
395
|
// persistence is best-effort — never break the turn
|
|
@@ -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
|
}
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
appendAssistantMessage as jsonlAppendAssistantMessage,
|
|
44
44
|
appendTokenUsage as jsonlAppendTokenUsage,
|
|
45
45
|
appendToolCallCompact as jsonlAppendToolCallCompact,
|
|
46
|
+
appendLlmRetryCompact as jsonlAppendLlmRetryCompact,
|
|
46
47
|
appendCompactEvent as jsonlAppendCompactEvent,
|
|
47
48
|
appendEvent as jsonlAppendEvent,
|
|
48
49
|
readEvents as jsonlReadEvents,
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
getLastSessionId as jsonlGetLastSessionId,
|
|
52
53
|
verifySession as jsonlVerifySession,
|
|
53
54
|
} from "../harness/jsonl-session-store.js";
|
|
55
|
+
import { classifyStreamRetryReason } from "../lib/stream-retry.js";
|
|
54
56
|
import {
|
|
55
57
|
SideEffectLedger,
|
|
56
58
|
reconcileSideEffects,
|
|
@@ -503,6 +505,8 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
503
505
|
appendTokenUsage: deps.appendTokenUsage || jsonlAppendTokenUsage,
|
|
504
506
|
appendToolCallCompact:
|
|
505
507
|
deps.appendToolCallCompact || jsonlAppendToolCallCompact,
|
|
508
|
+
appendLlmRetryCompact:
|
|
509
|
+
deps.appendLlmRetryCompact || jsonlAppendLlmRetryCompact,
|
|
506
510
|
appendCompactEvent: deps.appendCompactEvent || jsonlAppendCompactEvent,
|
|
507
511
|
appendEvent: deps.appendEvent || jsonlAppendEvent,
|
|
508
512
|
readEvents: deps.readEvents || jsonlReadEvents,
|
|
@@ -1626,6 +1630,22 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1626
1630
|
// while a human watching a long `cc agent -p` run learns we're still waiting
|
|
1627
1631
|
// and, when a hard inactivity timeout is set, when it will auto-retry. Plain
|
|
1628
1632
|
// text (no chalk) since headless stderr is frequently piped/non-TTY.
|
|
1633
|
+
onStreamRetry: (attempt, error, telemetry = {}) => {
|
|
1634
|
+
writeErr(` ⟳ connection dropped — retrying (attempt ${attempt})…\n`);
|
|
1635
|
+
if (persist) {
|
|
1636
|
+
try {
|
|
1637
|
+
store.appendLlmRetryCompact(sessionId, {
|
|
1638
|
+
attempt,
|
|
1639
|
+
durationMs: telemetry.durationMs,
|
|
1640
|
+
provider: telemetry.provider || provider,
|
|
1641
|
+
model: telemetry.model || model,
|
|
1642
|
+
reason: classifyStreamRetryReason(error),
|
|
1643
|
+
});
|
|
1644
|
+
} catch {
|
|
1645
|
+
/* usage telemetry is best-effort */
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1629
1649
|
onStall: (ms, timeoutMs) => {
|
|
1630
1650
|
const silent = Math.round(ms / 1000);
|
|
1631
1651
|
const retryIn = timeoutMs > ms ? Math.round((timeoutMs - ms) / 1000) : 0;
|
|
@@ -1678,6 +1698,11 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1678
1698
|
|
|
1679
1699
|
const startedAt = deps.now ? deps.now() : Date.now();
|
|
1680
1700
|
const toolCalls = [];
|
|
1701
|
+
// The core normally attaches a precise toolTelemetryRecord on success.
|
|
1702
|
+
// Keep an event-boundary fallback for denials/early throws that settle
|
|
1703
|
+
// before that record is produced. Tools are serial in this loop, so one
|
|
1704
|
+
// timestamp is sufficient and never leaks into the result envelope.
|
|
1705
|
+
let currentToolStartedAt = null;
|
|
1681
1706
|
// Policy denials (blocked tool calls) collected for an end-of-run summary,
|
|
1682
1707
|
// so a non-interactive run surfaces what got blocked the way the REPL's
|
|
1683
1708
|
// `/permissions denials` does (Claude-Code 2.1.193 denial reasons).
|
|
@@ -1970,6 +1995,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1970
1995
|
break;
|
|
1971
1996
|
}
|
|
1972
1997
|
case "tool-executing": {
|
|
1998
|
+
currentToolStartedAt = deps.now ? deps.now() : Date.now();
|
|
1973
1999
|
const line = ` [${event.tool}] ${formatToolArgs(event.tool, event.args)}`;
|
|
1974
2000
|
if (isText) writeErr(line + "\n");
|
|
1975
2001
|
emitStream({
|
|
@@ -2057,11 +2083,21 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
2057
2083
|
if (toolCalls.length > 0) {
|
|
2058
2084
|
const settledCall = toolCalls[toolCalls.length - 1];
|
|
2059
2085
|
settledCall.is_error = Boolean(err);
|
|
2086
|
+
settledCall.durationMs =
|
|
2087
|
+
event.result?.toolTelemetryRecord?.durationMs ??
|
|
2088
|
+
(currentToolStartedAt === null
|
|
2089
|
+
? undefined
|
|
2090
|
+
: Math.max(
|
|
2091
|
+
0,
|
|
2092
|
+
(deps.now ? deps.now() : Date.now()) -
|
|
2093
|
+
currentToolStartedAt,
|
|
2094
|
+
));
|
|
2060
2095
|
Object.assign(
|
|
2061
2096
|
settledCall,
|
|
2062
2097
|
extractPluginUsageAttribution(event.result),
|
|
2063
2098
|
);
|
|
2064
2099
|
}
|
|
2100
|
+
currentToolStartedAt = null;
|
|
2065
2101
|
// Track policy denials (not plain tool failures) for the end-of-run
|
|
2066
2102
|
// summary. The preceding tool-executing pushed the args.
|
|
2067
2103
|
if (err) {
|
|
@@ -2540,6 +2576,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
2540
2576
|
tc.tool === "run_skill" ? tc.args?.skill_name || null : undefined,
|
|
2541
2577
|
plugin: tc.plugin || undefined,
|
|
2542
2578
|
pluginVersion: tc.pluginVersion || undefined,
|
|
2579
|
+
durationMs: tc.durationMs,
|
|
2543
2580
|
});
|
|
2544
2581
|
}
|
|
2545
2582
|
// Attributed child-loop usage first (chronology: it happened during
|