chainlesschain 0.163.6 → 0.163.7
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/assets/{AIOps-DDIUofA8.js → AIOps-axqQfwv8.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Bm-5xYRn.js → ActionButton-DV-Ro0By.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D5UH4ljI.js → Analytics-Bt72WEVu.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-D_ifjOOP.js → AppLayout-BtepG55E.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BY1mXdC7.js → Artifacts-B7KaJ7S_.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bujr4Ml3.js → Audit-FNFaUg7A.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-sioKSTcC.js → BackgroundAgents-DFb6J2wQ.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DDOAK8RK.js → Backup-CC7YBhyj.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CtCsVVke.js → BaseInput-RkgTNVtj.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DpNj0vnd.js → Chat-hHEmGZy-.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CZta3Ftr.js → ChatBubbleRenderer-CAP9YhHv.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DoHVNSXq.js → Checkbox-z5wXOQP1.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DlVl7Qmv.js → Codegen-DVaDJnxy.js} +1 -1
- package/src/assets/web-panel/assets/{Col-GIqoPYty.js → Col-CJmaRDR5.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CIAPEVji.js → Community-DF29_2bI.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BN7JiI_h.js → Compact-DtkQkBzn.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CqUWCM9B.js → Compliance-xoHDxp7I.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-A4iPp7A4.js → Cowork-BUm-R7ht.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-B2zeXXWb.js → Cron-Cgl14ZkB.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BR2xmWr8.js → Crosschain-BNcbmoqh.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BzFDjrIv.js → DID-Bwja0Zo9.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DKtDo3yA.js → Dashboard-Bk5XjEdJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-RNx43_YG.js → Dropdown-Ci36Oitq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-SwMt4Rcy.js → EmailListRenderer--U04ZN4q.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CsYebZ4X.js → FamilyGuardDashboard-B2gNPR2s.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BmRKvBTy.js → Federation-C4KghYn7.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CVxGTA-0.js → FormItemContext-QL7x_Q_0.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-x2iXpyB7.js → GenericCardRenderer-F3Ryltwm.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DfqbkiOP.js → Git-2HassK5k.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-RuHyyj5O.js → Governance-DoBIp7oe.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Bn7uZ7rI.js → Inference--W01QhZ-.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DlP7XefZ.js → KnowledgeGraph-DmvwqGpT.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BYNdOiCd.js → Logs-BBJ4yAel.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-aMtgN-R6.js → MarkdownRenderer-BLCZMzhJ.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-JQYmDqM8.js → Marketplace-D5lNngSI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Cyx4yZ-9.js → McpTools-CkEVOMKH.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-VDuGZTN2.js → Memory-B1Ow1xVE.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-_yKo97gi.js → MobileBridge-SgYbUfb6.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CXRXW3q7.js → MobileProjects-BWdOvQAj.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-acTGtfLH.js → Mtc-DcTXSSma.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-ClpjXfkY.js → MtcAudit-Dt3WG4oj.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-_6Tn3o96.js → Multisig-B9B38UZx.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CK7pbZzP.js → NLProgramming-B0S4GGvG.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Czy6V_P8.js → Notes-DZ5U58Ng.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Cqx-UVv3.js → NotificationSettings-PjL5ZW34.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C69WDxGy.js → OrderTableRenderer-DrAX2ESt.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DFnA9vLQ.js → Organization-Cv1GYs-c.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-ReZkk2yT.js → Overflow-Bbkp_PL3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DzjSI0aI.js → P2P-BD8QUgoZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DKTf5xIP.js → PdhVaultBrowser-BO-2PeaB.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DhQ5klhR.js → Permissions-D_OPVa02.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CUL1iHrW.js → PersonalDataHub-mh-pEmaB.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DU88iu4N.js → Pipeline-6JC2EDdb.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Bw-sYfyK.js → Privacy-CUshzUAG.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Bq3Q5y0z.js → ProjectInit-Cs2Aalri.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C8spzpho.js → ProjectSettings-1ak49tcj.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BV4oApsm.js +1 -0
- package/src/assets/web-panel/assets/{Providers-Bf4eTP9W.js → Providers-CXXxvafH.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Dme9BDU1.js → QrScannerModal-PTcDsF58.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CIXfv1rE.js → QuickAsk-CDwucrJw.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CIxvdeZ-.js → Recommend-CJlLVRXR.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C6k7MExI.js → RemoteSession-DlN-ETsO.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CISDz2bl.js → Reputation-DI23LR6m.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CdnDxXyf.js → Row-Cl7UQMrM.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C-QpRHsl.js → RssFeed-CexHNfh8.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DBjaGzSn.js → Search-BneBWvRN.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CQKukOeE.js → Security-DX7Phgrp.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DZz86jWb.js → Services-BWXO8Mte.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-jb8RMK0d.js → Skeleton-DCyOjKPJ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CdY76fZG.js → Skills-CXqERsTB.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C_CSiZX9.js → Sla-bhbrbz8E.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-3IPU0HBZ.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-qvimGO0O.js → SyncSettings-DBNTQLbM.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Dng5LPJC.js → Tasks-quP0bkN-.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Bwqq5Wll.js → Templates-BouWmgho.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DNWuwP7Z.js → Tenant-qv0D9s7z.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B-6Othpp.js → Terminal-BxvbE8BB.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DKpKZfzx.js → TimelineRenderer-D-IeLnJ2.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DzfGMd2J.js → Tokens-CFMKKyVG.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BXwUi74J.js → Trigger-L8as3UuJ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BNzUq4ui.js → Trust-MxmA4VZG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BYugxww4.js → UkeySign-C0VvJYRi.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DdauhtSJ.js → VideoEditing-Bf5QZj8N.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CSC9bCe_.js → Wallet-BIJ9riRz.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Q8AN3bTO.js → WebAuthn-B8p9Mhkw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-d-LBGCuA.js → WorkflowEditor-BVsuaPen.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BLVDCgsZ.js → chat-BfkjoKpV.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BZlPSEHl.js → colors-DeAlsWK8.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-IWseP6R6.js → compact-item-Cs97bz5j.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DnVRV3Ao.js → createContext-BqbG60LH.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DPty0mGN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLQZA_Wn.js → hasIn-i_RY0b1X.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_N4-Gq9.js → index-0-agYlWL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DB0MJgF_.js → index-79V_zv1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOZJBTWZ.js → index-B-3tEpTc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUq0c7gq.js → index-B-JVTrpr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dy0R5ZED.js → index-B1CbOH1i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bxp7uDD8.js → index-B5UkyA3c.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpVrK6fr.js → index-BESLFt4q.js} +1 -1
- package/src/assets/web-panel/assets/{index-CT9vVN-X.js → index-BOVkf1K3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOoQpuQY.js → index-BYUVuwKt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CINJQEj6.js → index-BcUXAB2s.js} +1 -1
- package/src/assets/web-panel/assets/{index-CarMTIyQ.js → index-BkBi5TmC.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5dgnilC.js → index-Bm7lLR4c.js} +1 -1
- package/src/assets/web-panel/assets/index-Bt7g0Vq_.js +1 -0
- package/src/assets/web-panel/assets/{index-CbQNL0ue.js → index-BuLQobzW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpBaJ3TD.js → index-CE2VtTlp.js} +1 -1
- package/src/assets/web-panel/assets/{index-jWb70ZgZ.js → index-CED2PL_y.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEu2lMBy.js → index-CG3PRyAL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DocEhinm.js → index-CIG8fQ2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_LQodeJ.js → index-COdGA-8n.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZpYeqrh.js → index-CeWoNHau.js} +1 -1
- package/src/assets/web-panel/assets/{index-gt8bNefW.js → index-Cf3WCLmy.js} +1 -1
- package/src/assets/web-panel/assets/{index-N_GoWZX4.js → index-CgsqdnRV.js} +1 -1
- package/src/assets/web-panel/assets/index-ChVT0p9u.js +1 -0
- package/src/assets/web-panel/assets/{index-CZDekj7T.js → index-D2B4VPyP.js} +1 -1
- package/src/assets/web-panel/assets/{index-YSw_gxlZ.js → index-D9evtvg2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTbxp4UH.js → index-DUUrv141.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bbj2NS_I.js → index-DiSkx4UB.js} +3 -3
- package/src/assets/web-panel/assets/{index-BZs5bBnk.js → index-Dk2qouAz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DalA0TKC.js → index-Dk7-v4hP.js} +1 -1
- package/src/assets/web-panel/assets/{index-1jeAudHd.js → index-DmbfhJUP.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1ifZtXo.js → index-DqBgBpgp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhA3QEni.js → index-PT_5sBMA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSjilZ9L.js → index-U4f5izHz.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2FqfMvU.js → index-YXgLng0K.js} +1 -1
- package/src/assets/web-panel/assets/{index-COev0W2g.js → index-ZxO_-E8a.js} +1 -1
- package/src/assets/web-panel/assets/{index-M7wxfAbm.js → index-dIHr4B_u.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjWnONdy.js → index-gyFpvL7l.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dkwsi1Kn.js → index-s1opAL-M.js} +1 -1
- package/src/assets/web-panel/assets/{index-hwVDeoC0.js → index-yObj1O4n.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Co67Kqmb.js → initDefaultProps-C9h9yKL3.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BMOxQSF_.js → motion-BvmLsPdu.js} +1 -1
- package/src/assets/web-panel/assets/{move-C5fWDw-N.js → move-CoL4ekU1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BJevpL0e.js → mtc-parser-C4pMy6xV.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BCzNU8s6.js → omit-DPuJfNxx.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CybWvDPk.js → pickAttrs-D0KEeaK2.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BMWS9HnW.js → placementArrow-BpkyINxt.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BoL2UnGY.js → responsiveObserve-DQI_wOq8.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DWNfXraU.js → slide-DYfdDiRg.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BquTM3tX.js → statusUtils-B5dtvb0D.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BEkCleim.js → styleChecker-CF310mSj.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Oecnq7EA.js → useFlexGapSupport-D1Qp4BLp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CkDffJ4F.js → useFs-C7_iyvJK.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-qbvKXesB.js → usePersonalDataHub-DZUt_fIm.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C6v8n6mA.js → vnode-D3dsi0lZ.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-4CTeFILV.js → zoom-WdbOlvdQ.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agent.js +90 -1
- package/src/commands/chat.js +11 -2
- package/src/commands/compact.js +106 -23
- package/src/commands/loop.js +28 -10
- package/src/commands/routine.js +1 -1
- package/src/commands/scheduler-daemon.js +359 -2
- package/src/commands/session-observability.js +305 -0
- package/src/commands/session.js +38 -0
- package/src/data/changelog.json +14 -1
- package/src/gateways/ws/ws-agent-handler.js +439 -5
- package/src/harness/jsonl-session-store.js +463 -125
- package/src/harness/prompt-compressor.js +86 -13
- package/src/harness/provider-backed-compaction.js +100 -27
- package/src/lib/advisor-runtime.js +3 -1
- package/src/lib/agent-schedule-store.js +414 -4
- package/src/lib/agent-team/team-adjudication.js +40 -1
- package/src/lib/automation-engine.js +494 -0
- package/src/lib/background-agent-supervisor.js +793 -215
- package/src/lib/background-command-argv.js +24 -0
- package/src/lib/background-interaction-journal.js +361 -52
- package/src/lib/background-interaction-resolver.js +32 -0
- package/src/lib/background-launch-profile.js +2 -0
- package/src/lib/causal-observability.js +2661 -0
- package/src/lib/chat-session-admission.js +76 -0
- package/src/lib/cost-budget.js +5 -7
- package/src/lib/cowork/ab-comparator-cli.js +11 -0
- package/src/lib/cowork/debate-review-cli.js +12 -0
- package/src/lib/cowork-adapter.js +159 -38
- package/src/lib/cowork-cron.js +357 -1
- package/src/lib/cowork-task-runner.js +112 -17
- package/src/lib/delivery-coordinator.js +142 -0
- package/src/lib/direct-model-usage.js +121 -0
- package/src/lib/file-lines.js +183 -47
- package/src/lib/llm-pricing.js +12 -17
- package/src/lib/loop.js +26 -2
- package/src/lib/routine-store.js +380 -21
- package/src/lib/runtime-usage-ledger.js +182 -0
- package/src/lib/scheduler-kernel/agenda-adapter.js +308 -3
- package/src/lib/scheduler-kernel/automation-adapter.js +410 -8
- package/src/lib/scheduler-kernel/automation-event-adapter.js +36 -0
- package/src/lib/scheduler-kernel/cowork-cron-adapter.js +285 -3
- package/src/lib/scheduler-kernel/loop-adapter.js +541 -6
- package/src/lib/scheduler-kernel/migration-admin.js +346 -0
- package/src/lib/scheduler-kernel/migration-rollback-admin.js +932 -0
- package/src/lib/scheduler-kernel/routine-adapter.js +394 -20
- package/src/lib/scheduler-kernel/runtime.js +63 -1
- package/src/lib/scheduler-kernel/source-locator-path.js +142 -0
- package/src/lib/scheduler-kernel/store.js +2586 -85
- package/src/lib/session-host-lease.js +171 -2
- package/src/lib/session-host-snapshot.js +14 -3
- package/src/lib/session-persistence-failure.js +1 -0
- package/src/lib/session-tail.js +179 -34
- package/src/lib/session-transcript-structure.js +96 -0
- package/src/lib/sub-agent-context.js +328 -22
- package/src/lib/ws-agent-handler.js +4 -1
- package/src/repl/agent-repl.js +732 -91
- package/src/repl/chat-repl.js +5 -1
- package/src/runtime/agent-core.js +1272 -207
- package/src/runtime/fallback-model.js +5 -0
- package/src/runtime/headless-runner.js +324 -82
- package/src/runtime/headless-stream.js +232 -26
- package/src/runtime/policies/agent-policy.js +4 -0
- package/src/workers/background-agent-worker.js +403 -96
- package/src/assets/web-panel/assets/Projects-Dd-xuPIc.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-C7tT9iad.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Jzu62CLC.js +0 -1
- package/src/assets/web-panel/assets/index-Zb2MeDSy.js +0 -1
- package/src/assets/web-panel/assets/index-fikqml4k.js +0 -1
|
@@ -66,7 +66,10 @@ const ACTION_TO_METHOD = Object.freeze({
|
|
|
66
66
|
|
|
67
67
|
const EXACT_COMMIT_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
|
|
68
68
|
const SHA256_RE = /^sha256:[0-9a-f]{64}$/i;
|
|
69
|
+
const TRANSCRIPT_HEAD_RE = /^[0-9a-f]{64}$/i;
|
|
69
70
|
const DEFAULT_BLOCKING_SEVERITIES = Object.freeze(["Critical", "High"]);
|
|
71
|
+
const MAX_CAUSAL_ID_LENGTH = 256;
|
|
72
|
+
const MAX_CAUSAL_SESSIONS = 128;
|
|
70
73
|
|
|
71
74
|
function hash(value) {
|
|
72
75
|
return `sha256:${crypto
|
|
@@ -104,6 +107,126 @@ function boundedInteger(value, fallback, min, max) {
|
|
|
104
107
|
: fallback;
|
|
105
108
|
}
|
|
106
109
|
|
|
110
|
+
function hasAsciiControlCharacter(value) {
|
|
111
|
+
for (let index = 0; index < value.length; index++) {
|
|
112
|
+
const code = value.charCodeAt(index);
|
|
113
|
+
if (code <= 0x1f || code === 0x7f) return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function compareCodeUnits(left, right) {
|
|
119
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeCausalId(value, field, { required = false } = {}) {
|
|
123
|
+
if (value == null) {
|
|
124
|
+
if (required) throw new Error(`${field} is required`);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
if (typeof value !== "string") throw new Error(`${field} must be a string`);
|
|
128
|
+
const normalized = value.trim();
|
|
129
|
+
if (!normalized) {
|
|
130
|
+
if (required) throw new Error(`${field} is required`);
|
|
131
|
+
throw new Error(`${field} must not be blank`);
|
|
132
|
+
}
|
|
133
|
+
if (
|
|
134
|
+
normalized.length > MAX_CAUSAL_ID_LENGTH ||
|
|
135
|
+
hasAsciiControlCharacter(normalized)
|
|
136
|
+
) {
|
|
137
|
+
throw new Error(`${field} is invalid`);
|
|
138
|
+
}
|
|
139
|
+
return normalized;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function normalizeCausality(value) {
|
|
143
|
+
if (value != null && (typeof value !== "object" || Array.isArray(value))) {
|
|
144
|
+
throw new Error("causality must be an object");
|
|
145
|
+
}
|
|
146
|
+
const source = value || {};
|
|
147
|
+
if (Object.hasOwn(source, "sessionIds")) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
"causality.sessionIds is unsupported; bind sessions with headHash and eventCount",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
if (
|
|
153
|
+
source.scope != null &&
|
|
154
|
+
(typeof source.scope !== "object" || Array.isArray(source.scope))
|
|
155
|
+
) {
|
|
156
|
+
throw new Error("causality.scope must be an object");
|
|
157
|
+
}
|
|
158
|
+
if (source.sessions != null && !Array.isArray(source.sessions)) {
|
|
159
|
+
throw new Error("causality.sessions must be an array");
|
|
160
|
+
}
|
|
161
|
+
const scope = source.scope || {};
|
|
162
|
+
if ((source.sessions || []).length > MAX_CAUSAL_SESSIONS) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`causality.sessions exceeds the ${MAX_CAUSAL_SESSIONS} session limit`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const sessions = (source.sessions || [])
|
|
168
|
+
.map((binding) => {
|
|
169
|
+
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
170
|
+
throw new Error("causality.sessions entries must be objects");
|
|
171
|
+
}
|
|
172
|
+
const sessionId = normalizeCausalId(
|
|
173
|
+
binding.sessionId,
|
|
174
|
+
"causality.sessions.sessionId",
|
|
175
|
+
{ required: true },
|
|
176
|
+
);
|
|
177
|
+
if (
|
|
178
|
+
sessionId.includes("/") ||
|
|
179
|
+
sessionId.includes("\\") ||
|
|
180
|
+
sessionId.includes("..")
|
|
181
|
+
) {
|
|
182
|
+
throw new Error("causality.sessions.sessionId is unsafe");
|
|
183
|
+
}
|
|
184
|
+
const headHash = String(binding.headHash || "").toLowerCase();
|
|
185
|
+
if (!TRANSCRIPT_HEAD_RE.test(headHash)) {
|
|
186
|
+
throw new Error("causality.sessions.headHash is invalid");
|
|
187
|
+
}
|
|
188
|
+
const eventCount = Number(binding.eventCount);
|
|
189
|
+
if (!Number.isSafeInteger(eventCount) || eventCount <= 0) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
"causality.sessions.eventCount must be a positive safe integer",
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return { sessionId, headHash, eventCount };
|
|
195
|
+
})
|
|
196
|
+
.sort((a, b) => compareCodeUnits(a.sessionId, b.sessionId));
|
|
197
|
+
if (
|
|
198
|
+
new Set(sessions.map((binding) => binding.sessionId)).size !==
|
|
199
|
+
sessions.length
|
|
200
|
+
) {
|
|
201
|
+
throw new Error("causality.sessions contains duplicate sessionId values");
|
|
202
|
+
}
|
|
203
|
+
const normalizedScope = {
|
|
204
|
+
workspaceId: normalizeCausalId(
|
|
205
|
+
scope.workspaceId,
|
|
206
|
+
"causality.scope.workspaceId",
|
|
207
|
+
),
|
|
208
|
+
teamId: normalizeCausalId(scope.teamId, "causality.scope.teamId"),
|
|
209
|
+
policyId: normalizeCausalId(scope.policyId, "causality.scope.policyId"),
|
|
210
|
+
};
|
|
211
|
+
if (
|
|
212
|
+
sessions.length > 0 &&
|
|
213
|
+
!Object.values(normalizedScope).some((scopeId) => scopeId != null)
|
|
214
|
+
) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
"causality.scope requires at least one dimension when sessions are bound",
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return { scope: normalizedScope, sessions };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function hasCausality(causality) {
|
|
223
|
+
return Boolean(
|
|
224
|
+
causality &&
|
|
225
|
+
(causality.sessions.length > 0 ||
|
|
226
|
+
Object.values(causality.scope).some((value) => value != null)),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
107
230
|
function stateMaterial(state) {
|
|
108
231
|
const material = jsonClone(state);
|
|
109
232
|
delete material.stateDigest;
|
|
@@ -533,6 +656,19 @@ export function verifyDeliveryFlowState(snapshot) {
|
|
|
533
656
|
unmet.push("schema-unsupported");
|
|
534
657
|
if (snapshot.version !== DELIVERY_FLOW_VERSION)
|
|
535
658
|
unmet.push("version-unsupported");
|
|
659
|
+
if (Object.hasOwn(snapshot, "causality")) {
|
|
660
|
+
try {
|
|
661
|
+
const normalized = normalizeCausality(snapshot.causality);
|
|
662
|
+
if (
|
|
663
|
+
canonicalDeliveryJson(normalized) !==
|
|
664
|
+
canonicalDeliveryJson(snapshot.causality)
|
|
665
|
+
) {
|
|
666
|
+
unmet.push("causality-noncanonical");
|
|
667
|
+
}
|
|
668
|
+
} catch {
|
|
669
|
+
unmet.push("causality-invalid");
|
|
670
|
+
}
|
|
671
|
+
}
|
|
536
672
|
const actualDigest = String(snapshot.stateDigest || "");
|
|
537
673
|
const expectedDigest = hash(stateMaterial(snapshot));
|
|
538
674
|
if (!actualDigest || actualDigest !== expectedDigest) {
|
|
@@ -580,6 +716,10 @@ export function restoreDeliveryFlow(snapshot) {
|
|
|
580
716
|
export function createDeliveryFlow(config = {}, { now } = {}) {
|
|
581
717
|
const createdAt = isoNow(now);
|
|
582
718
|
const requiredGates = normalizeGateDefinitions(config.requiredGates);
|
|
719
|
+
const normalizedCausality = normalizeCausality(config.causality);
|
|
720
|
+
const causality = hasCausality(normalizedCausality)
|
|
721
|
+
? normalizedCausality
|
|
722
|
+
: null;
|
|
583
723
|
const gateSelection = selectImpactedGates({
|
|
584
724
|
changedFiles: config.diff?.changedFiles || config.changedFiles,
|
|
585
725
|
requiredGates: requiredGates.map((gate) => ({
|
|
@@ -646,6 +786,7 @@ export function createDeliveryFlow(config = {}, { now } = {}) {
|
|
|
646
786
|
commitSha: config.commitSha,
|
|
647
787
|
diff: config.diff,
|
|
648
788
|
requiredGateIds: requiredGates.map((gate) => gate.id),
|
|
789
|
+
...(causality ? { causality } : {}),
|
|
649
790
|
};
|
|
650
791
|
const state = {
|
|
651
792
|
schema: DELIVERY_FLOW_SCHEMA,
|
|
@@ -661,6 +802,7 @@ export function createDeliveryFlow(config = {}, { now } = {}) {
|
|
|
661
802
|
stopReason: preflightReason,
|
|
662
803
|
preflightUnmet,
|
|
663
804
|
commitSha,
|
|
805
|
+
...(causality ? { causality } : {}),
|
|
664
806
|
diff: jsonClone(config.diff || {}),
|
|
665
807
|
environment: jsonClone(config.environment || {}),
|
|
666
808
|
requiredGates,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
markRuntimeLedgerPersistenceError,
|
|
4
|
+
projectRuntimeTokenUsage,
|
|
5
|
+
projectRuntimeUsageBoundary,
|
|
6
|
+
runtimeUsageEventType,
|
|
7
|
+
} from "./runtime-usage-ledger.js";
|
|
8
|
+
|
|
9
|
+
function asRuntimeLedgerPersistenceError(error) {
|
|
10
|
+
if (error && (typeof error === "object" || typeof error === "function")) {
|
|
11
|
+
try {
|
|
12
|
+
const marked = markRuntimeLedgerPersistenceError(error);
|
|
13
|
+
if (marked?.runtimeLedgerPersistence === true) return marked;
|
|
14
|
+
} catch {
|
|
15
|
+
// Frozen/non-extensible errors are wrapped below.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const wrapped = new Error("runtime usage ledger persistence failed", {
|
|
19
|
+
cause: error,
|
|
20
|
+
});
|
|
21
|
+
return markRuntimeLedgerPersistenceError(wrapped);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function persistUsageEvent(persist, type, event) {
|
|
25
|
+
try {
|
|
26
|
+
await persist(type, event);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw asRuntimeLedgerPersistenceError(error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function unknownUsageEvent({ callId, provider, model, source, code }) {
|
|
33
|
+
return projectRuntimeUsageBoundary(
|
|
34
|
+
{ callId, provider, model, source, code },
|
|
35
|
+
"unknown",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Meter one direct provider call that runs outside agentLoop. The started row is
|
|
41
|
+
* durable before the callback resumes into provider work; every outcome then
|
|
42
|
+
* settles known or unknown without persisting prompts, responses, or errors.
|
|
43
|
+
*/
|
|
44
|
+
export async function runMeteredDirectModelCall({
|
|
45
|
+
sessionId,
|
|
46
|
+
persist,
|
|
47
|
+
provider,
|
|
48
|
+
model,
|
|
49
|
+
source = "model",
|
|
50
|
+
call,
|
|
51
|
+
}) {
|
|
52
|
+
if (typeof call !== "function") {
|
|
53
|
+
throw new TypeError("metered direct model call requires a callback");
|
|
54
|
+
}
|
|
55
|
+
if (!sessionId) return call();
|
|
56
|
+
if (typeof persist !== "function") {
|
|
57
|
+
throw new TypeError("metered direct model call requires persistence");
|
|
58
|
+
}
|
|
59
|
+
const callId = `direct-${randomUUID()}`;
|
|
60
|
+
await persistUsageEvent(
|
|
61
|
+
persist,
|
|
62
|
+
runtimeUsageEventType("started"),
|
|
63
|
+
projectRuntimeUsageBoundary({ callId, provider, model, source }, "started"),
|
|
64
|
+
);
|
|
65
|
+
let result;
|
|
66
|
+
try {
|
|
67
|
+
result = await call();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
await persistUsageEvent(
|
|
70
|
+
persist,
|
|
71
|
+
runtimeUsageEventType("unknown"),
|
|
72
|
+
unknownUsageEvent({
|
|
73
|
+
callId,
|
|
74
|
+
provider,
|
|
75
|
+
model,
|
|
76
|
+
source,
|
|
77
|
+
code: "provider_call_failed",
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
if (result?.usage && typeof result.usage === "object") {
|
|
83
|
+
let event;
|
|
84
|
+
try {
|
|
85
|
+
event = projectRuntimeTokenUsage({
|
|
86
|
+
callId,
|
|
87
|
+
provider,
|
|
88
|
+
model,
|
|
89
|
+
source,
|
|
90
|
+
usage: result.usage,
|
|
91
|
+
});
|
|
92
|
+
} catch {
|
|
93
|
+
await persistUsageEvent(
|
|
94
|
+
persist,
|
|
95
|
+
runtimeUsageEventType("unknown"),
|
|
96
|
+
unknownUsageEvent({
|
|
97
|
+
callId,
|
|
98
|
+
provider,
|
|
99
|
+
model,
|
|
100
|
+
source,
|
|
101
|
+
code: "provider_usage_missing",
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
await persistUsageEvent(persist, "token_usage", event);
|
|
107
|
+
} else {
|
|
108
|
+
await persistUsageEvent(
|
|
109
|
+
persist,
|
|
110
|
+
runtimeUsageEventType("unknown"),
|
|
111
|
+
unknownUsageEvent({
|
|
112
|
+
callId,
|
|
113
|
+
provider,
|
|
114
|
+
model,
|
|
115
|
+
source,
|
|
116
|
+
code: "provider_usage_missing",
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
package/src/lib/file-lines.js
CHANGED
|
@@ -1,7 +1,64 @@
|
|
|
1
1
|
import { closeSync, fstatSync, openSync, readSync } from "node:fs";
|
|
2
|
-
import { StringDecoder } from "node:string_decoder";
|
|
3
2
|
|
|
4
3
|
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
|
4
|
+
export const DEFAULT_MAX_FILE_LINE_BYTES = 16 * 1024 * 1024;
|
|
5
|
+
export const FILE_LINE_TOO_LARGE_CODE = "CC_FILE_LINE_TOO_LARGE";
|
|
6
|
+
|
|
7
|
+
function normalizeMaxLineBytes(value) {
|
|
8
|
+
const parsed = Number(value);
|
|
9
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
10
|
+
throw new TypeError("maxLineBytes must be a positive safe integer");
|
|
11
|
+
}
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeChunkSize(value) {
|
|
16
|
+
const parsed = Number(value);
|
|
17
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
18
|
+
throw new TypeError("chunkSize must be a positive safe integer");
|
|
19
|
+
}
|
|
20
|
+
return parsed;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function assertFileLineByteLength(
|
|
24
|
+
byteLength,
|
|
25
|
+
{
|
|
26
|
+
maxLineBytes = DEFAULT_MAX_FILE_LINE_BYTES,
|
|
27
|
+
code = FILE_LINE_TOO_LARGE_CODE,
|
|
28
|
+
lineNo = null,
|
|
29
|
+
direction = null,
|
|
30
|
+
label = "File line",
|
|
31
|
+
} = {},
|
|
32
|
+
) {
|
|
33
|
+
const actualBytes = Number(byteLength);
|
|
34
|
+
const maxBytes = normalizeMaxLineBytes(maxLineBytes);
|
|
35
|
+
if (!Number.isSafeInteger(actualBytes) || actualBytes < 0) {
|
|
36
|
+
throw new TypeError("byteLength must be a non-negative safe integer");
|
|
37
|
+
}
|
|
38
|
+
if (actualBytes <= maxBytes) return actualBytes;
|
|
39
|
+
const error = new Error(`${label} exceeded the ${maxBytes}-byte limit`);
|
|
40
|
+
error.code = code;
|
|
41
|
+
error.actualBytes = actualBytes;
|
|
42
|
+
error.maxBytes = maxBytes;
|
|
43
|
+
if (Number.isSafeInteger(lineNo) && lineNo > 0) error.lineNo = lineNo;
|
|
44
|
+
if (direction) error.direction = direction;
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function assertLineByteLength(byteLength, options, details) {
|
|
49
|
+
return assertFileLineByteLength(byteLength, {
|
|
50
|
+
maxLineBytes: options.maxLineBytes,
|
|
51
|
+
code: options.lineTooLargeCode,
|
|
52
|
+
label: options.lineLabel,
|
|
53
|
+
...details,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function joinedLineBuffer(parts, byteLength) {
|
|
58
|
+
if (parts.length === 0) return Buffer.alloc(0);
|
|
59
|
+
if (parts.length === 1) return parts[0];
|
|
60
|
+
return Buffer.concat(parts, byteLength);
|
|
61
|
+
}
|
|
5
62
|
|
|
6
63
|
function recordRead(ioMetrics, bytes) {
|
|
7
64
|
if (!ioMetrics || typeof ioMetrics !== "object") return;
|
|
@@ -13,7 +70,8 @@ function recordRead(ioMetrics, bytes) {
|
|
|
13
70
|
/**
|
|
14
71
|
* Stream UTF-8 lines from a file without loading the whole file. The final
|
|
15
72
|
* unterminated fragment is yielded with `terminated:false`, which lets callers
|
|
16
|
-
* distinguish a crash tail from a normal record.
|
|
73
|
+
* distinguish a crash tail from a normal record. Records are assembled as raw
|
|
74
|
+
* byte slices and checked before any Buffer.concat or UTF-8 decode.
|
|
17
75
|
*/
|
|
18
76
|
export function* iterateFileLinesSync(
|
|
19
77
|
filePath,
|
|
@@ -21,35 +79,81 @@ export function* iterateFileLinesSync(
|
|
|
21
79
|
chunkSize = DEFAULT_CHUNK_SIZE,
|
|
22
80
|
includeEmpty = false,
|
|
23
81
|
ioMetrics = null,
|
|
82
|
+
maxLineBytes = DEFAULT_MAX_FILE_LINE_BYTES,
|
|
83
|
+
lineTooLargeCode = FILE_LINE_TOO_LARGE_CODE,
|
|
84
|
+
lineLabel = "File line",
|
|
24
85
|
} = {},
|
|
25
86
|
) {
|
|
87
|
+
const lineOptions = {
|
|
88
|
+
maxLineBytes: normalizeMaxLineBytes(maxLineBytes),
|
|
89
|
+
lineTooLargeCode,
|
|
90
|
+
lineLabel,
|
|
91
|
+
};
|
|
92
|
+
const buffer = Buffer.allocUnsafe(
|
|
93
|
+
Math.min(normalizeChunkSize(chunkSize), lineOptions.maxLineBytes),
|
|
94
|
+
);
|
|
26
95
|
const fd = openSync(filePath, "r");
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let pending = "";
|
|
96
|
+
let pendingParts = [];
|
|
97
|
+
let pendingBytes = 0;
|
|
30
98
|
let lineNo = 0;
|
|
31
99
|
try {
|
|
32
100
|
for (;;) {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
recordRead(ioMetrics,
|
|
36
|
-
|
|
37
|
-
let newline;
|
|
38
|
-
while (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
pending = pending.slice(newline + 1);
|
|
101
|
+
const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
|
|
102
|
+
if (bytesRead === 0) break;
|
|
103
|
+
recordRead(ioMetrics, bytesRead);
|
|
104
|
+
let start = 0;
|
|
105
|
+
let newline = buffer.indexOf(0x0a, start);
|
|
106
|
+
while (newline >= 0 && newline < bytesRead) {
|
|
107
|
+
const segment = buffer.subarray(start, newline);
|
|
108
|
+
const lineByteLength = pendingBytes + segment.length;
|
|
42
109
|
lineNo += 1;
|
|
43
|
-
|
|
44
|
-
|
|
110
|
+
assertLineByteLength(lineByteLength, lineOptions, {
|
|
111
|
+
lineNo,
|
|
112
|
+
direction: "forward",
|
|
113
|
+
});
|
|
114
|
+
let lineBytes = joinedLineBuffer(
|
|
115
|
+
segment.length > 0 ? [...pendingParts, segment] : pendingParts,
|
|
116
|
+
lineByteLength,
|
|
117
|
+
);
|
|
118
|
+
if (lineBytes.length > 0 && lineBytes[lineBytes.length - 1] === 0x0d) {
|
|
119
|
+
lineBytes = lineBytes.subarray(0, lineBytes.length - 1);
|
|
120
|
+
}
|
|
121
|
+
if (includeEmpty || lineBytes.length > 0) {
|
|
122
|
+
yield {
|
|
123
|
+
line: lineBytes.toString("utf8"),
|
|
124
|
+
lineNo,
|
|
125
|
+
terminated: true,
|
|
126
|
+
};
|
|
45
127
|
}
|
|
128
|
+
pendingParts = [];
|
|
129
|
+
pendingBytes = 0;
|
|
130
|
+
start = newline + 1;
|
|
131
|
+
newline = buffer.indexOf(0x0a, start);
|
|
46
132
|
}
|
|
133
|
+
|
|
134
|
+
const trailing = buffer.subarray(start, bytesRead);
|
|
135
|
+
const nextPendingBytes = pendingBytes + trailing.length;
|
|
136
|
+
assertLineByteLength(nextPendingBytes, lineOptions, {
|
|
137
|
+
lineNo: lineNo + 1,
|
|
138
|
+
direction: "forward",
|
|
139
|
+
});
|
|
140
|
+
if (trailing.length > 0) pendingParts.push(Buffer.from(trailing));
|
|
141
|
+
pendingBytes = nextPendingBytes;
|
|
47
142
|
}
|
|
48
|
-
|
|
49
|
-
if (
|
|
143
|
+
|
|
144
|
+
if (pendingBytes > 0 || includeEmpty) {
|
|
50
145
|
lineNo += 1;
|
|
51
|
-
|
|
52
|
-
|
|
146
|
+
assertLineByteLength(pendingBytes, lineOptions, {
|
|
147
|
+
lineNo,
|
|
148
|
+
direction: "forward",
|
|
149
|
+
});
|
|
150
|
+
if (includeEmpty || pendingBytes > 0) {
|
|
151
|
+
const pending = joinedLineBuffer(pendingParts, pendingBytes);
|
|
152
|
+
yield {
|
|
153
|
+
line: pending.toString("utf8"),
|
|
154
|
+
lineNo,
|
|
155
|
+
terminated: false,
|
|
156
|
+
};
|
|
53
157
|
}
|
|
54
158
|
}
|
|
55
159
|
} finally {
|
|
@@ -68,36 +172,57 @@ export function* iterateFileLinesReverseSync(
|
|
|
68
172
|
chunkSize = DEFAULT_CHUNK_SIZE,
|
|
69
173
|
includeEmpty = false,
|
|
70
174
|
ioMetrics = null,
|
|
175
|
+
maxLineBytes = DEFAULT_MAX_FILE_LINE_BYTES,
|
|
176
|
+
lineTooLargeCode = FILE_LINE_TOO_LARGE_CODE,
|
|
177
|
+
lineLabel = "File line",
|
|
71
178
|
} = {},
|
|
72
179
|
) {
|
|
180
|
+
const lineOptions = {
|
|
181
|
+
maxLineBytes: normalizeMaxLineBytes(maxLineBytes),
|
|
182
|
+
lineTooLargeCode,
|
|
183
|
+
lineLabel,
|
|
184
|
+
};
|
|
185
|
+
const readChunkSize = Math.min(
|
|
186
|
+
normalizeChunkSize(chunkSize),
|
|
187
|
+
lineOptions.maxLineBytes,
|
|
188
|
+
);
|
|
73
189
|
const fd = openSync(filePath, "r");
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
if (size > 0) {
|
|
77
|
-
const last = Buffer.allocUnsafe(1);
|
|
78
|
-
const bytes = readSync(fd, last, 0, 1, size - 1);
|
|
79
|
-
recordRead(ioMetrics, bytes);
|
|
80
|
-
endsWithNewline = last[0] === 0x0a;
|
|
81
|
-
}
|
|
82
|
-
let position = size;
|
|
83
|
-
let carry = Buffer.alloc(0);
|
|
190
|
+
let carryParts = [];
|
|
191
|
+
let carryBytes = 0;
|
|
84
192
|
let isPhysicalTail = true;
|
|
85
193
|
try {
|
|
194
|
+
const size = fstatSync(fd).size;
|
|
195
|
+
let endsWithNewline = false;
|
|
196
|
+
if (size > 0) {
|
|
197
|
+
const last = Buffer.allocUnsafe(1);
|
|
198
|
+
const bytesRead = readSync(fd, last, 0, 1, size - 1);
|
|
199
|
+
recordRead(ioMetrics, bytesRead);
|
|
200
|
+
endsWithNewline = last[0] === 0x0a;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let position = size;
|
|
86
204
|
while (position > 0) {
|
|
87
|
-
const length = Math.min(
|
|
205
|
+
const length = Math.min(readChunkSize, position);
|
|
88
206
|
position -= length;
|
|
89
207
|
const chunk = Buffer.allocUnsafe(length);
|
|
90
|
-
const
|
|
91
|
-
recordRead(ioMetrics,
|
|
92
|
-
|
|
93
|
-
let
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
208
|
+
const bytesRead = readSync(fd, chunk, 0, length, position);
|
|
209
|
+
recordRead(ioMetrics, bytesRead);
|
|
210
|
+
let end = bytesRead;
|
|
211
|
+
for (let index = bytesRead - 1; index >= 0; index -= 1) {
|
|
212
|
+
if (chunk[index] !== 0x0a) continue;
|
|
213
|
+
const segment = chunk.subarray(index + 1, end);
|
|
214
|
+
const lineByteLength = segment.length + carryBytes;
|
|
215
|
+
assertLineByteLength(lineByteLength, lineOptions, {
|
|
216
|
+
direction: "reverse",
|
|
217
|
+
});
|
|
218
|
+
let lineBytes = joinedLineBuffer(
|
|
219
|
+
segment.length > 0 ? [segment, ...carryParts] : carryParts,
|
|
220
|
+
lineByteLength,
|
|
221
|
+
);
|
|
222
|
+
if (lineBytes.length > 0 && lineBytes[lineBytes.length - 1] === 0x0d) {
|
|
223
|
+
lineBytes = lineBytes.subarray(0, lineBytes.length - 1);
|
|
99
224
|
}
|
|
100
|
-
const line =
|
|
225
|
+
const line = lineBytes.toString("utf8");
|
|
101
226
|
const terminated = isPhysicalTail ? endsWithNewline : true;
|
|
102
227
|
// A file ending in "\n" produces one empty physical-tail slice. Skip
|
|
103
228
|
// it without changing the termination status of the real last line.
|
|
@@ -105,16 +230,27 @@ export function* iterateFileLinesReverseSync(
|
|
|
105
230
|
yield { line, terminated };
|
|
106
231
|
isPhysicalTail = false;
|
|
107
232
|
}
|
|
233
|
+
carryParts = [];
|
|
234
|
+
carryBytes = 0;
|
|
108
235
|
end = index;
|
|
109
236
|
}
|
|
110
|
-
|
|
237
|
+
|
|
238
|
+
const prefix = chunk.subarray(0, end);
|
|
239
|
+
const nextCarryBytes = prefix.length + carryBytes;
|
|
240
|
+
assertLineByteLength(nextCarryBytes, lineOptions, {
|
|
241
|
+
direction: "reverse",
|
|
242
|
+
});
|
|
243
|
+
if (prefix.length > 0) carryParts.unshift(Buffer.from(prefix));
|
|
244
|
+
carryBytes = nextCarryBytes;
|
|
111
245
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
246
|
+
|
|
247
|
+
if (carryBytes > 0 || includeEmpty) {
|
|
248
|
+
assertLineByteLength(carryBytes, lineOptions, { direction: "reverse" });
|
|
249
|
+
let lineBytes = joinedLineBuffer(carryParts, carryBytes);
|
|
250
|
+
if (lineBytes.length > 0 && lineBytes[lineBytes.length - 1] === 0x0d) {
|
|
251
|
+
lineBytes = lineBytes.subarray(0, lineBytes.length - 1);
|
|
116
252
|
}
|
|
117
|
-
const line =
|
|
253
|
+
const line = lineBytes.toString("utf8");
|
|
118
254
|
if (includeEmpty || line.length > 0) {
|
|
119
255
|
yield { line, terminated: !isPhysicalTail };
|
|
120
256
|
}
|
package/src/lib/llm-pricing.js
CHANGED
|
@@ -84,11 +84,6 @@ export const PRICE_TABLE = Object.freeze({
|
|
|
84
84
|
],
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
const round = (n, dp = 6) => {
|
|
88
|
-
const f = Math.pow(10, dp);
|
|
89
|
-
return Math.round((Number(n) + Number.EPSILON) * f) / f;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
87
|
/**
|
|
93
88
|
* Merge user-supplied price overrides (typically `config.llm.pricing`) onto the
|
|
94
89
|
* built-in table. Override shape mirrors PRICE_TABLE:
|
|
@@ -221,22 +216,22 @@ export function estimateCost({
|
|
|
221
216
|
rate: null,
|
|
222
217
|
};
|
|
223
218
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
);
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
219
|
+
// Keep full IEEE-754 precision throughout authority and budget arithmetic.
|
|
220
|
+
// Rounding each component to six decimals lets a real sub-microdollar call
|
|
221
|
+
// become zero and can make a hard `maxUsd` gate pass incorrectly. Display
|
|
222
|
+
// surfaces may format these values, but safety decisions consume the raw sum.
|
|
223
|
+
const inputCost = (Number(inputTokens) / 1e6) * rate.in;
|
|
224
|
+
const outputCost = (Number(outputTokens) / 1e6) * rate.out;
|
|
225
|
+
const cacheReadCost =
|
|
226
|
+
(Number(cacheReadTokens) / 1e6) * rate.in * cacheReadMultiplier(provider);
|
|
227
|
+
const cacheCreationCost =
|
|
228
|
+
(Number(cacheCreationTokens) / 1e6) * rate.in * CACHE_WRITE_MULTIPLIER;
|
|
232
229
|
return {
|
|
233
230
|
inputCost,
|
|
234
231
|
outputCost,
|
|
235
232
|
cacheReadCost,
|
|
236
233
|
cacheCreationCost,
|
|
237
|
-
totalCost:
|
|
238
|
-
inputCost + outputCost + cacheReadCost + cacheCreationCost,
|
|
239
|
-
),
|
|
234
|
+
totalCost: inputCost + outputCost + cacheReadCost + cacheCreationCost,
|
|
240
235
|
currency: "USD",
|
|
241
236
|
matched: true,
|
|
242
237
|
free: rate.pattern === "free",
|
|
@@ -280,7 +275,7 @@ export function priceRollup(aggregate, { table } = {}) {
|
|
|
280
275
|
const unpriced = [];
|
|
281
276
|
for (const row of byModel) {
|
|
282
277
|
if (row.matched) {
|
|
283
|
-
totalCost
|
|
278
|
+
totalCost += row.cost;
|
|
284
279
|
} else if (row.totalTokens > 0) {
|
|
285
280
|
unpriced.push({
|
|
286
281
|
provider: row.provider,
|