chainlesschain 0.166.97 → 0.166.99
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/README.md +20 -23
- package/package.json +3 -3
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-BB9kKNVw.js → AIOps-BUgaCgVU.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DTg-lWE0.js → ActionButton-b0MpUSSZ.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BWUZ0Tpc.js → Analytics-hbXTvHqO.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Dj8WDnCf.js → AppLayout-CLQoAVu4.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-x8NiiPTg.js → Artifacts-ytHzl2L2.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-q7otRAqO.js → Audit-BuhkLm0C.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-f6qlO2au.js → BackgroundAgents-CS81hxZV.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BbuX9qQS.js → Backup-DzR-_Qvi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-amgV-Y0y.js → BaseInput-r8Ku_lYw.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CZW3DW7j.js → Chat-C3Uxtoaa.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-RvblDu77.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-DXHXC52i.js → Checkbox-BnG2P5yc.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DQh47r4e.js → Codegen-BdgU6HZy.js} +1 -1
- package/src/assets/web-panel/assets/{Col-lBOOXCD7.js → Col-DT8t9wmf.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BNFPnBu9.js → Community-ChkxR5oe.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-7MOWfBGb.js → Compact-DIwLeTk3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-x0UA-rxk.js → Compliance-CAqzGLvW.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-C_eLfk_Q.js → Cowork-Bb3jejMG.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BP2-_Fja.js → Cron-BYRkZLta.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Dn-e1217.js → Crosschain-BD6z227t.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D_jverif.js → DID-DA3G5Wgo.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BysweBms.js → Dashboard-yaZePHOu.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-osJi0Pci.js → Dropdown-BqY5MBx2.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C72k1SKQ.js → EmailListRenderer-cIWwOgHm.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-CAtA1F9s.js → EvolutionSettings-zvV6ymca.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bi0luzvO.js → FamilyGuardDashboard-CAmh1U2b.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CIwntDLI.js → Federation-5qd2LCg8.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DRfwhPW-.js → FormItemContext-Bdkj1sRJ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-COJTzZ61.js → GenericCardRenderer-B-gWN2P3.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CwsR46lj.js → Git-BEMh_NGd.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B1U-u9BN.js → Governance-qLGvA2Hw.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-vPsdYTS_.js → Inference-DblBwYuW.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CVDRZM5h.js → KnowledgeGraph-BISegVD_.js} +1 -1
- package/src/assets/web-panel/assets/Logs-BD2UR7kE.js +2 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-B-TlDCmr.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CtxYHchd.js → Marketplace-CHKXMOkH.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BkfCbpAk.js → McpTools-B5G1elqd.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-COp-8Z20.js → Memory-O5Ib0uTR.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-7fX-n_xn.js → MobileBridge-BaGYL7rT.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-Dsctbob8.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-CLXFZ5dV.js → Mtc-CNjJ97jg.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-yJ4eP1wB.js → MtcAudit-DjBN-Wqz.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-Cwb_qTh0.js → Multisig-B850R9lO.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CkuojSSJ.js → NLProgramming-Bn8sbFfV.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-ju-QtUhJ.js → Notes-BpLmMp3Q.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DjryXIC3.js → NotificationSettings-Dbk97rjb.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-B4KNF8xr.js → OrderTableRenderer-BOBX_I72.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-6-EtcVaO.js → Organization-B45DpOuE.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BKja8nqr.js → Overflow-DmKH7eer.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BX20f9uL.js → P2P-CC4FaXTl.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-C9zwOEUo.js → PdhVaultBrowser-C2SgP4PI.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-YQjQVrfE.js → Permissions-BQB4QFj_.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-yZYclXxq.js → PersonalDataHub-Dy6GGpn4.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-ChZCCXAI.js → Pipeline-B9HB5pRc.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Ds7R-a0H.js → Privacy-D7pOmg-p.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B3qz_xab.js → ProjectInit-DglBqvec.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BlwNctdF.js → ProjectSettings-Bho22znM.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BxtYpNl7.js → Projects-C0bSuM8U.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BE_1SS_-.js → Providers-BgAgo1Yr.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CMQ25oLb.js → QrScannerModal-C2fBjCC8.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DQRFLDeP.js → QuickAsk-C8MiF-rk.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-ChVra4Pk.js → Recommend-D_TzHdQf.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-9vPBIowJ.js → RemoteSession-BHO7b6xE.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-MdbWbyQQ.js → Reputation-BPxNmNhD.js} +1 -1
- package/src/assets/web-panel/assets/{Row-JLcBncLR.js → Row-fwhot5Bk.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CnMXnpFv.js → RssFeed-CZbD1B-5.js} +2 -2
- package/src/assets/web-panel/assets/{Search-Cf5ISVUe.js → Search-BRs6YpAE.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CywFo2EM.js → Security-CB344tKC.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CLYnSCG3.js → Services-Beo-7fEl.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BtbhOlo1.js → Skeleton-CDXx96vD.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DPeqcIo2.js → Skills-b-mQTJK4.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BRb4SqDn.js → Sla-BvjP-IS9.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-0Wuxts-7.js → SpeechSettings-C6wsV-cO.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BXtsOjXL.js → SyncSettings-BQZoko-e.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-D455Wfhx.js → Tasks-6Z0rJJVO.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-LGc4O0fj.js → Templates-Cc9IXtEQ.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DA--0GFz.js → Tenant-BLc7AjOI.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DQsA0xCq.js → Terminal-CUN49v85.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-VH8AuSLp.js → TimelineRenderer-CX-SksHN.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-B8RyzFRN.js → Tokens-Btrm0-rK.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CA7lVVEf.js → Trigger-CIboUvKe.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-wUoRRLg9.js → Trust-Rv9jEs5v.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-ByoT_alW.js → UkeySign-B_xomdD5.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D9fgAxJV.js → VideoEditing-CrvWeMow.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CRkTndvj.js → Wallet-BjvWgk_7.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-VwmHOxJK.js → WebAuthn-CwfpFw92.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-Z0bPZc2O.js → WorkflowEditor-Dkk2QsOe.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CfX_5qUm.js → chat-Dah1DVes.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BK7mXUnR.js → colors-JEpgQ10k.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Uq755CeO.js → compact-item--DuG_cxf.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DWXq8Qnk.js → createContext-BJ_GZvon.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B6kQEceX.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BXgy5JZA.js → hasIn-yZRLDmWK.js} +1 -1
- package/src/assets/web-panel/assets/{index-LsBPnXHh.js → index-1QZASeW1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSb7SKp5.js → index-9L3Pg2tc.js} +1 -1
- package/src/assets/web-panel/assets/{index-H13lvPme.js → index-BKl40wrO.js} +1 -1
- package/src/assets/web-panel/assets/{index-TDPkVyse.js → index-BM7xJKGD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRLOeoFE.js → index-BUjyj7qR.js} +1 -1
- package/src/assets/web-panel/assets/{index-JX7mXB5a.js → index-BWkKE8Oj.js} +3 -3
- package/src/assets/web-panel/assets/{index-VPLA-GO7.js → index-Bdg_nWD1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bt3tjvj-.js → index-C56b8e9s.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3VnhfYU.js → index-C6ohEAEV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuhlVXh4.js → index-C8nXkCMf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZD1My0S.js → index-CFj7ylAJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxSClhg5.js → index-CJLz3SpM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGy7b2rd.js → index-CQedFuDw.js} +1 -1
- package/src/assets/web-panel/assets/index-CSeiH6HA.js +1 -0
- package/src/assets/web-panel/assets/index-CayGTz6d.js +1 -0
- package/src/assets/web-panel/assets/{index-CqJw5WsH.js → index-CjuD8smQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-9XsaovMH.js → index-Cmr0AzcK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dg9P9EDV.js → index-CxB49Mow.js} +1 -1
- package/src/assets/web-panel/assets/{index-QXKuGlOS.js → index-D6Le2m3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-CarQiRSA.js → index-DFJbJ8rs.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRQSkXde.js → index-DQ4Fch3e.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8e4SYX4.js → index-DZBesaCN.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ait-_UUh.js → index-DaDLWIWo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbjUfyVq.js → index-DbMGGcpZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-p3BlIre2.js → index-DddwZCT9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdwC1i3O.js → index-DemMuw7n.js} +1 -1
- package/src/assets/web-panel/assets/{index-_I4nSJFc.js → index-DhX48E81.js} +1 -1
- package/src/assets/web-panel/assets/{index-wYTro42F.js → index-Dq-8PFhL.js} +1 -1
- package/src/assets/web-panel/assets/{index-XdoslBhl.js → index-DroaQypF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiVH_NiY.js → index-DxnkldGo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfYb8FjC.js → index-IjOa5lYn.js} +1 -1
- package/src/assets/web-panel/assets/{index-B09haI0M.js → index-L9CFyRIi.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y77S-ZC0.js → index-VIgVth92.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4yvdQCj.js → index-asia_JPc.js} +1 -1
- package/src/assets/web-panel/assets/{index-VrR8eMCk.js → index-cHx5KzUt.js} +1 -1
- package/src/assets/web-panel/assets/{index-D63nsr0a.js → index-kZE-K3ZO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ct41B4y4.js → index-o2qO_dYf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUR7dnMg.js → index-rkvSOceQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-8lN1rG.js → index-vdTrEOj7.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DTuF67Yy.js → initDefaultProps-anKyyjGc.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DZasLgsl.js → motion-Cqp0nx5A.js} +1 -1
- package/src/assets/web-panel/assets/{move-pPR4O1Du.js → move-Ca3mgkM2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Dc7fidhm.js → mtc-parser-BmwuhJ3z.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CZ1IcubN.js → omit-C9Mf05WJ.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BMtJilhr.js → pickAttrs-2PbxB9Ds.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-D0EXaZA7.js → placementArrow-CTVbOFHt.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-HftdThc5.js → responsiveObserve-Nrm8Vbmr.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CT1JEPWV.js → slide-Dh4r1zqR.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-rwE-IPZB.js → statusUtils-vzNoQL-r.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B2B8XHVq.js → styleChecker-BXpGfNkI.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-TXNvIFVa.js → useFlexGapSupport-CVWBRdhs.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BcWgJKvf.js → useFs-BJWMLffI.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DWBDnOSR.js → usePersonalDataHub-CWCe5VHG.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Cf_LjUuu.js → vnode-D-NBEb8m.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DM2Bfuyl.js → zoom-C0mnhfGf.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/eval.js +79 -3
- package/src/commands/llm.js +40 -5
- package/src/data/changelog.json +26 -0
- package/src/lib/context-memory-kernel/message-adapter.js +41 -24
- package/src/lib/context-memory-kernel/segmented-memory-port.js +18 -0
- package/src/lib/cost-budget.js +8 -0
- package/src/lib/eval/runner.js +22 -1
- package/src/lib/eval/verify01-contracts.js +106 -51
- package/src/lib/eval/verify01-execution.js +148 -166
- package/src/lib/eval/verify01-host-evidence.js +34 -2
- package/src/lib/eval/verify01-workspace.js +302 -0
- package/src/lib/execution-location-failure-site.js +18 -0
- package/src/lib/execution-location-target.js +10 -0
- package/src/lib/llm-connection-probe.js +147 -6
- package/src/lib/llm-pricing.js +61 -7
- package/src/lib/model-capabilities.js +10 -2
- package/src/lib/model-catalog-review.js +1 -0
- package/src/lib/model-context-catalog.js +26 -1
- package/src/lib/provider-http-error.js +30 -11
- package/src/lib/provider-options.js +38 -1
- package/src/lib/search-command.js +4 -1
- package/src/lib/stream-retry.js +14 -1
- package/src/lib/task-progress-tracker.js +1 -0
- package/src/lib/usage-pricing-context.js +100 -10
- package/src/runtime/agent-core.js +95 -18
- package/src/runtime/headless-runner.js +47 -20
- package/src/runtime/headless-stream.js +32 -10
- package/src/runtime/provider-stream-state.js +7 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DgMYDmBW.js +0 -1
- package/src/assets/web-panel/assets/Logs-YRslKbRL.js +0 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CUBuc97a.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DUyTVgId.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CbtBfs7a.js +0 -1
- package/src/assets/web-panel/assets/index-CicoHC0s.js +0 -1
- package/src/assets/web-panel/assets/index-DttyUwDx.js +0 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/** Shared file boundaries and snapshots; no process broker or npm dependencies. */
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { evalDigest } from "./evidence.js";
|
|
6
|
+
const MAX_BYTES = 16 * 1024 * 1024;
|
|
7
|
+
export const MAX_SNAPSHOT_BYTES = 512 * 1024 * 1024;
|
|
8
|
+
const MAX_DIFF_BYTES = 32 * 1024 * 1024;
|
|
9
|
+
const EMPTY_DELIVERABLE_DIGEST = evalDigest(Buffer.alloc(0));
|
|
10
|
+
|
|
11
|
+
export function requireValue(condition, message) {
|
|
12
|
+
if (!condition) throw new Error(message);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function relativeFile(value) {
|
|
16
|
+
requireValue(
|
|
17
|
+
typeof value === "string" &&
|
|
18
|
+
value.length > 0 &&
|
|
19
|
+
!value.includes("\0") &&
|
|
20
|
+
!/[\\:*?<>|"]/u.test(value) &&
|
|
21
|
+
!value.startsWith("/") &&
|
|
22
|
+
value.split("/").every((part) => part && !/[. ]$/u.test(part)),
|
|
23
|
+
"review paths must be exact relative files",
|
|
24
|
+
);
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
export function readBytes(file) {
|
|
28
|
+
const descriptor = fs.openSync(file, "r");
|
|
29
|
+
try {
|
|
30
|
+
const before = fs.fstatSync(descriptor, { bigint: true });
|
|
31
|
+
requireValue(
|
|
32
|
+
before.isFile() && before.size <= BigInt(MAX_BYTES),
|
|
33
|
+
"input must be a regular file within 16 MiB",
|
|
34
|
+
);
|
|
35
|
+
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
36
|
+
let count = 0;
|
|
37
|
+
while (count < bytes.length) {
|
|
38
|
+
const size = fs.readSync(
|
|
39
|
+
descriptor,
|
|
40
|
+
bytes,
|
|
41
|
+
count,
|
|
42
|
+
bytes.length - count,
|
|
43
|
+
null,
|
|
44
|
+
);
|
|
45
|
+
if (!size) break;
|
|
46
|
+
count += size;
|
|
47
|
+
}
|
|
48
|
+
const after = fs.fstatSync(descriptor, { bigint: true });
|
|
49
|
+
requireValue(
|
|
50
|
+
count === Number(before.size) &&
|
|
51
|
+
["dev", "ino", "size", "mtimeNs", "ctimeNs"].every(
|
|
52
|
+
(key) => before[key] === after[key],
|
|
53
|
+
),
|
|
54
|
+
"input changed during read",
|
|
55
|
+
);
|
|
56
|
+
return bytes.subarray(0, count);
|
|
57
|
+
} finally {
|
|
58
|
+
fs.closeSync(descriptor);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function canonicalDirectory(directory) {
|
|
63
|
+
const absolute = path.resolve(directory);
|
|
64
|
+
const canonical = (fs.realpathSync.native || fs.realpathSync)(absolute);
|
|
65
|
+
const same =
|
|
66
|
+
process.platform === "win32"
|
|
67
|
+
? canonical.toLowerCase() === absolute.toLowerCase()
|
|
68
|
+
: canonical === absolute;
|
|
69
|
+
const stat = fs.lstatSync(absolute);
|
|
70
|
+
requireValue(
|
|
71
|
+
same && stat.isDirectory() && !stat.isSymbolicLink(),
|
|
72
|
+
"task directory must be canonical and unaliased",
|
|
73
|
+
);
|
|
74
|
+
return canonical;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Trusted preparation only: a seed contains no answer and never overwrites
|
|
78
|
+
* a project file. Existing source paths are not passed to this helper. */
|
|
79
|
+
export function seedVerify01Deliverables(directory, expectedFiles, budget) {
|
|
80
|
+
const root = canonicalDirectory(directory);
|
|
81
|
+
requireValue(
|
|
82
|
+
Array.isArray(expectedFiles) &&
|
|
83
|
+
expectedFiles.length > 0 &&
|
|
84
|
+
new Set(expectedFiles).size === expectedFiles.length,
|
|
85
|
+
"deliverable paths must be distinct",
|
|
86
|
+
);
|
|
87
|
+
expectedFiles.forEach(relativeFile);
|
|
88
|
+
const seeds = [];
|
|
89
|
+
for (const relative of expectedFiles) {
|
|
90
|
+
budget.remainingMs("deliverable-preparation");
|
|
91
|
+
let current = root;
|
|
92
|
+
for (const part of relative.split("/").slice(0, -1)) {
|
|
93
|
+
current = path.join(current, part);
|
|
94
|
+
within(root, current);
|
|
95
|
+
try {
|
|
96
|
+
fs.mkdirSync(current, { mode: 0o700 });
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (error.code !== "EEXIST") throw error;
|
|
99
|
+
}
|
|
100
|
+
canonicalDirectory(current);
|
|
101
|
+
}
|
|
102
|
+
const target = path.join(root, relative);
|
|
103
|
+
within(root, target);
|
|
104
|
+
const fd = fs.openSync(
|
|
105
|
+
target,
|
|
106
|
+
fs.constants.O_WRONLY |
|
|
107
|
+
fs.constants.O_CREAT |
|
|
108
|
+
fs.constants.O_EXCL |
|
|
109
|
+
(fs.constants.O_NOFOLLOW || 0),
|
|
110
|
+
0o600,
|
|
111
|
+
);
|
|
112
|
+
try {
|
|
113
|
+
const stat = fs.fstatSync(fd);
|
|
114
|
+
requireValue(
|
|
115
|
+
stat.isFile() && stat.nlink === 1 && stat.size === 0,
|
|
116
|
+
"seed must be an empty unaliased regular file",
|
|
117
|
+
);
|
|
118
|
+
regularPath(root, relative);
|
|
119
|
+
const named = fs.lstatSync(target);
|
|
120
|
+
requireValue(
|
|
121
|
+
named.dev === stat.dev && named.ino === stat.ino,
|
|
122
|
+
"seed path changed identity during preparation",
|
|
123
|
+
);
|
|
124
|
+
canonicalDirectory(path.dirname(target));
|
|
125
|
+
seeds.push(
|
|
126
|
+
Object.freeze({
|
|
127
|
+
path: relative,
|
|
128
|
+
before: null,
|
|
129
|
+
origin: "trusted-empty-deliverable-preparation",
|
|
130
|
+
digest: EMPTY_DELIVERABLE_DIGEST,
|
|
131
|
+
bytes: 0,
|
|
132
|
+
requestedMode: 0o600,
|
|
133
|
+
mode: stat.mode & 0o777,
|
|
134
|
+
preparedAt: new Date().toISOString(),
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
} finally {
|
|
138
|
+
fs.closeSync(fd);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze(seeds);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function within(root, file) {
|
|
145
|
+
const relative = path.relative(root, file);
|
|
146
|
+
requireValue(
|
|
147
|
+
relative &&
|
|
148
|
+
relative !== ".." &&
|
|
149
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
150
|
+
!path.isAbsolute(relative),
|
|
151
|
+
"path escapes its declared root",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function regularPath(root, relative) {
|
|
156
|
+
relativeFile(relative);
|
|
157
|
+
const file = path.join(root, relative);
|
|
158
|
+
within(root, file);
|
|
159
|
+
let current = root;
|
|
160
|
+
for (const segment of relative.split("/")) {
|
|
161
|
+
current = path.join(current, segment);
|
|
162
|
+
requireValue(
|
|
163
|
+
!fs.lstatSync(current).isSymbolicLink(),
|
|
164
|
+
"review/project paths must not traverse links",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
requireValue(
|
|
168
|
+
fs.lstatSync(file).isFile(),
|
|
169
|
+
"review/project artifact must be a regular file",
|
|
170
|
+
);
|
|
171
|
+
return file;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function snapshotVerify01Workspace(directory, budget, stage) {
|
|
175
|
+
const files = new Map();
|
|
176
|
+
let total = 0;
|
|
177
|
+
let dependencyBytes = 0;
|
|
178
|
+
let dependencyFiles = 0;
|
|
179
|
+
const dependencyHash = createHash("sha256");
|
|
180
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
181
|
+
const visit = (current) => {
|
|
182
|
+
for (const entry of fs
|
|
183
|
+
.readdirSync(current, { withFileTypes: true })
|
|
184
|
+
.sort((a, b) => a.name.localeCompare(b.name, "en"))) {
|
|
185
|
+
budget.remainingMs(stage);
|
|
186
|
+
const full = path.join(current, entry.name);
|
|
187
|
+
const relative = path.relative(directory, full).split(path.sep).join("/");
|
|
188
|
+
if (entry.isDirectory()) visit(full);
|
|
189
|
+
else {
|
|
190
|
+
requireValue(
|
|
191
|
+
entry.isFile() || entry.isSymbolicLink(),
|
|
192
|
+
"task snapshot contains an unsupported filesystem object",
|
|
193
|
+
);
|
|
194
|
+
// Preserve link identities without following them into another root.
|
|
195
|
+
const dependency = relative.split("/").includes("node_modules");
|
|
196
|
+
if (dependency && entry.isSymbolicLink()) {
|
|
197
|
+
// A dependency linked outside the isolated workspace cannot be
|
|
198
|
+
// fingerprinted by this capture contract; never silently omit it.
|
|
199
|
+
within(directory, fs.realpathSync(full));
|
|
200
|
+
}
|
|
201
|
+
let bytes = entry.isSymbolicLink()
|
|
202
|
+
? Buffer.from(fs.readlinkSync(full))
|
|
203
|
+
: dependency
|
|
204
|
+
? null
|
|
205
|
+
: readBytes(full);
|
|
206
|
+
let digest, size;
|
|
207
|
+
if (dependency && bytes === null) {
|
|
208
|
+
const hash = createHash("sha256");
|
|
209
|
+
const fd = fs.openSync(full, "r");
|
|
210
|
+
size = 0;
|
|
211
|
+
try {
|
|
212
|
+
let count;
|
|
213
|
+
while (
|
|
214
|
+
(count = fs.readSync(fd, buffer, 0, buffer.length, null)) > 0
|
|
215
|
+
) {
|
|
216
|
+
budget.remainingMs(stage);
|
|
217
|
+
size += count;
|
|
218
|
+
requireValue(
|
|
219
|
+
dependencyBytes + size <= 16 * 1024 ** 3,
|
|
220
|
+
"dependency fingerprint exceeds 16 GiB",
|
|
221
|
+
);
|
|
222
|
+
hash.update(buffer.subarray(0, count));
|
|
223
|
+
}
|
|
224
|
+
} finally {
|
|
225
|
+
fs.closeSync(fd);
|
|
226
|
+
}
|
|
227
|
+
digest = `sha256:${hash.digest("hex")}`;
|
|
228
|
+
} else {
|
|
229
|
+
size = bytes.length;
|
|
230
|
+
digest = evalDigest(bytes);
|
|
231
|
+
}
|
|
232
|
+
if (dependency) {
|
|
233
|
+
dependencyBytes += size;
|
|
234
|
+
dependencyFiles++;
|
|
235
|
+
bytes = null;
|
|
236
|
+
} else total += size;
|
|
237
|
+
requireValue(
|
|
238
|
+
total <= MAX_SNAPSHOT_BYTES &&
|
|
239
|
+
files.size - dependencyFiles < 100000 &&
|
|
240
|
+
dependencyFiles <= 1000000,
|
|
241
|
+
"task snapshot exceeds its capture bound",
|
|
242
|
+
);
|
|
243
|
+
const value = {
|
|
244
|
+
type: entry.isSymbolicLink() ? "link" : "file",
|
|
245
|
+
mode: fs.lstatSync(full).mode & 0o777,
|
|
246
|
+
bytes,
|
|
247
|
+
size,
|
|
248
|
+
digest,
|
|
249
|
+
dependency,
|
|
250
|
+
};
|
|
251
|
+
files.set(relative, value);
|
|
252
|
+
if (dependency)
|
|
253
|
+
dependencyHash.update(
|
|
254
|
+
JSON.stringify([relative, value.type, value.mode, size, digest]) +
|
|
255
|
+
"\n",
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
visit(directory);
|
|
261
|
+
files.dependencyBoundary = {
|
|
262
|
+
contract:
|
|
263
|
+
"all node_modules files and link identities streamed with SHA-256; links never followed; dependency bodies omitted; every change rejected",
|
|
264
|
+
files: dependencyFiles,
|
|
265
|
+
bytes: dependencyBytes,
|
|
266
|
+
digest: `sha256:${dependencyHash.digest("hex")}`,
|
|
267
|
+
};
|
|
268
|
+
return files;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function diffVerify01Workspace(before, after) {
|
|
272
|
+
const result = [];
|
|
273
|
+
let total = 0;
|
|
274
|
+
for (const file of [...new Set([...before.keys(), ...after.keys()])].sort()) {
|
|
275
|
+
const old = before.get(file),
|
|
276
|
+
next = after.get(file);
|
|
277
|
+
if (
|
|
278
|
+
old?.digest === next?.digest &&
|
|
279
|
+
old?.type === next?.type &&
|
|
280
|
+
old?.mode === next?.mode
|
|
281
|
+
)
|
|
282
|
+
continue;
|
|
283
|
+
total += (old?.bytes?.length || 0) + (next?.bytes?.length || 0);
|
|
284
|
+
requireValue(
|
|
285
|
+
total <= MAX_DIFF_BYTES,
|
|
286
|
+
"task diff exceeds 32 MiB; preserve workspace for independent inspection",
|
|
287
|
+
);
|
|
288
|
+
const describe = (value) =>
|
|
289
|
+
value
|
|
290
|
+
? {
|
|
291
|
+
type: value.type,
|
|
292
|
+
mode: value.mode,
|
|
293
|
+
digest: value.digest,
|
|
294
|
+
bytes: value.size,
|
|
295
|
+
dependency: value.dependency,
|
|
296
|
+
bytesBase64: value.bytes?.toString("base64") ?? null,
|
|
297
|
+
}
|
|
298
|
+
: null;
|
|
299
|
+
result.push({ path: file, before: describe(old), after: describe(next) });
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
@@ -27,6 +27,24 @@ export function readExecutionLocationStorageFailure(stderr) {
|
|
|
27
27
|
return match ? `${match[1]}${match[2] || ""}` : null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export function readExecutionLocationStorageLaunchFailure(stderr) {
|
|
31
|
+
const match = String(stderr || "")
|
|
32
|
+
.slice(0, MAX_DIAGNOSTIC_CHARS)
|
|
33
|
+
.match(
|
|
34
|
+
/\[windows-acl-launch:(inspect|repair|preflight):(spawn|timeout):(startup|initialize|traversal|lookup|repair-lock|repair-inspect|repair-write|verify):(EACCES|EPERM|ENOENT|ETIMEDOUT|EINVAL|ENOSYS|ENOMEM|EMFILE|ENFILE|ENOBUFS|EBUSY|UNKNOWN):(unknown|-?(?:0|[1-9][0-9]{0,4})):(spawnSync|spawn|unknown)\]/u,
|
|
35
|
+
);
|
|
36
|
+
if (!match) return null;
|
|
37
|
+
const [, operation, failureStage, progressStage, code, rawErrno, syscall] =
|
|
38
|
+
match;
|
|
39
|
+
const errno = rawErrno === "unknown" ? null : Number(rawErrno);
|
|
40
|
+
if (
|
|
41
|
+
(errno !== null && Math.abs(errno) > 65535) ||
|
|
42
|
+
(failureStage === "timeout") !== (code === "ETIMEDOUT")
|
|
43
|
+
)
|
|
44
|
+
return null;
|
|
45
|
+
return { operation, failureStage, progressStage, code, errno, syscall };
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
export function executionLocationFailureSite(error) {
|
|
31
49
|
const stack = String(error?.stack || "").slice(0, MAX_DIAGNOSTIC_CHARS);
|
|
32
50
|
for (const frame of stack.split(/\r?\n/u).slice(1)) {
|
|
@@ -7,6 +7,7 @@ import { TextDecoder } from "node:util";
|
|
|
7
7
|
import {
|
|
8
8
|
readExecutionLocationFailureSite,
|
|
9
9
|
readExecutionLocationStorageFailure,
|
|
10
|
+
readExecutionLocationStorageLaunchFailure,
|
|
10
11
|
} from "./execution-location-failure-site.js";
|
|
11
12
|
import {
|
|
12
13
|
EXECUTION_LOCATION_TARGET_ATTESTATION_SCHEMA,
|
|
@@ -1039,6 +1040,10 @@ function targetProcessFailure(result) {
|
|
|
1039
1040
|
error.failureCategory = failureCategory;
|
|
1040
1041
|
if (failureSite) error.failureSite = failureSite;
|
|
1041
1042
|
if (storageFailure) error.storageFailure = storageFailure;
|
|
1043
|
+
const storageLaunchFailure = readExecutionLocationStorageLaunchFailure(
|
|
1044
|
+
result.stderr,
|
|
1045
|
+
);
|
|
1046
|
+
if (storageLaunchFailure) error.storageLaunchFailure = storageLaunchFailure;
|
|
1042
1047
|
return error;
|
|
1043
1048
|
}
|
|
1044
1049
|
|
|
@@ -1108,6 +1113,11 @@ function runTargetCommand(profile, cliArgs, deps = {}, options = {}) {
|
|
|
1108
1113
|
error.failureCategory = failureCategory;
|
|
1109
1114
|
if (failureSite) error.failureSite = failureSite;
|
|
1110
1115
|
if (storageFailure) error.storageFailure = storageFailure;
|
|
1116
|
+
const storageLaunchFailure = readExecutionLocationStorageLaunchFailure(
|
|
1117
|
+
result?.stderr,
|
|
1118
|
+
);
|
|
1119
|
+
if (storageLaunchFailure)
|
|
1120
|
+
error.storageLaunchFailure = storageLaunchFailure;
|
|
1111
1121
|
throw error;
|
|
1112
1122
|
}
|
|
1113
1123
|
return options.interactive ? null : result.stdout;
|
|
@@ -1,13 +1,141 @@
|
|
|
1
1
|
import { formatProviderResponseError } from "./provider-http-error.js";
|
|
2
|
+
import { estimateCost } from "./llm-pricing.js";
|
|
2
3
|
|
|
3
4
|
// Reasoning models spend the same output allowance on thinking before the
|
|
4
5
|
// final answer. A 16-token probe can therefore receive HTTP 200 with only
|
|
5
6
|
// reasoning_content and finish_reason=length from an otherwise usable model.
|
|
6
7
|
const PROBE_OUTPUT_TOKENS = 1024;
|
|
8
|
+
export const LLM_PROBE_SCHEMA = "chainlesschain.llm-connection-probe/v1";
|
|
9
|
+
|
|
10
|
+
const count = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
11
|
+
|
|
12
|
+
/** Preserve missing/malformed usage as unknown, including cached prompt tokens. */
|
|
13
|
+
export function normalizeLlmProbeUsage(provider, data) {
|
|
14
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return null;
|
|
15
|
+
let usage;
|
|
16
|
+
if (provider === "ollama") {
|
|
17
|
+
usage = {
|
|
18
|
+
input_tokens: data.prompt_eval_count,
|
|
19
|
+
output_tokens: data.eval_count,
|
|
20
|
+
};
|
|
21
|
+
} else if (provider === "anthropic") {
|
|
22
|
+
usage = data.usage;
|
|
23
|
+
} else if (provider === "gemini") {
|
|
24
|
+
const raw = data.usageMetadata;
|
|
25
|
+
if (
|
|
26
|
+
!raw ||
|
|
27
|
+
!count(raw.candidatesTokenCount) ||
|
|
28
|
+
!count(raw.thoughtsTokenCount === undefined ? 0 : raw.thoughtsTokenCount)
|
|
29
|
+
)
|
|
30
|
+
return null;
|
|
31
|
+
const cached =
|
|
32
|
+
raw.cachedContentTokenCount === undefined
|
|
33
|
+
? 0
|
|
34
|
+
: raw.cachedContentTokenCount;
|
|
35
|
+
if (
|
|
36
|
+
!count(raw.promptTokenCount) ||
|
|
37
|
+
!count(cached) ||
|
|
38
|
+
cached > raw.promptTokenCount
|
|
39
|
+
)
|
|
40
|
+
return null;
|
|
41
|
+
usage = {
|
|
42
|
+
input_tokens: raw.promptTokenCount - cached,
|
|
43
|
+
output_tokens: raw.candidatesTokenCount + (raw.thoughtsTokenCount ?? 0),
|
|
44
|
+
cache_read_input_tokens: cached,
|
|
45
|
+
};
|
|
46
|
+
} else {
|
|
47
|
+
const raw = data.usage;
|
|
48
|
+
if (!raw || !count(raw.prompt_tokens) || !count(raw.completion_tokens))
|
|
49
|
+
return null;
|
|
50
|
+
const detailed = raw.prompt_tokens_details?.cached_tokens;
|
|
51
|
+
const legacy = raw.prompt_cache_hit_tokens;
|
|
52
|
+
const cached =
|
|
53
|
+
detailed === undefined ? (legacy === undefined ? 0 : legacy) : detailed;
|
|
54
|
+
if (
|
|
55
|
+
!count(cached) ||
|
|
56
|
+
cached > raw.prompt_tokens ||
|
|
57
|
+
(detailed !== undefined && legacy !== undefined && detailed !== legacy)
|
|
58
|
+
)
|
|
59
|
+
return null;
|
|
60
|
+
usage = {
|
|
61
|
+
input_tokens: raw.prompt_tokens - cached,
|
|
62
|
+
output_tokens: raw.completion_tokens,
|
|
63
|
+
cache_read_input_tokens: cached,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) return null;
|
|
67
|
+
const normalized = {
|
|
68
|
+
input_tokens: usage.input_tokens,
|
|
69
|
+
output_tokens: usage.output_tokens,
|
|
70
|
+
cache_read_input_tokens:
|
|
71
|
+
usage.cache_read_input_tokens === undefined
|
|
72
|
+
? 0
|
|
73
|
+
: usage.cache_read_input_tokens,
|
|
74
|
+
cache_creation_input_tokens:
|
|
75
|
+
usage.cache_creation_input_tokens === undefined
|
|
76
|
+
? 0
|
|
77
|
+
: usage.cache_creation_input_tokens,
|
|
78
|
+
};
|
|
79
|
+
return Object.values(normalized).every(count) ? normalized : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function priceLlmProbeUsage(target, usage) {
|
|
83
|
+
if (
|
|
84
|
+
!usage ||
|
|
85
|
+
![
|
|
86
|
+
usage.input_tokens,
|
|
87
|
+
usage.output_tokens,
|
|
88
|
+
usage.cache_read_input_tokens,
|
|
89
|
+
usage.cache_creation_input_tokens,
|
|
90
|
+
].every(count)
|
|
91
|
+
)
|
|
92
|
+
return null;
|
|
93
|
+
const estimate = estimateCost({
|
|
94
|
+
provider: target.provider,
|
|
95
|
+
model: target.model,
|
|
96
|
+
inputTokens: usage.input_tokens,
|
|
97
|
+
outputTokens: usage.output_tokens,
|
|
98
|
+
cacheReadTokens: usage.cache_read_input_tokens,
|
|
99
|
+
cacheCreationTokens: usage.cache_creation_input_tokens,
|
|
100
|
+
});
|
|
101
|
+
return estimate.matched && Number.isFinite(estimate.totalCost)
|
|
102
|
+
? estimate.totalCost
|
|
103
|
+
: null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function llmProbeReceipt(
|
|
107
|
+
target,
|
|
108
|
+
{ ok = false, reply = null, usage = null } = {},
|
|
109
|
+
) {
|
|
110
|
+
const normalizedUsage =
|
|
111
|
+
usage === null
|
|
112
|
+
? null
|
|
113
|
+
: {
|
|
114
|
+
input_tokens: usage.input_tokens,
|
|
115
|
+
output_tokens: usage.output_tokens,
|
|
116
|
+
cache_read_input_tokens: usage.cache_read_input_tokens,
|
|
117
|
+
cache_creation_input_tokens: usage.cache_creation_input_tokens,
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
schema: LLM_PROBE_SCHEMA,
|
|
121
|
+
ok,
|
|
122
|
+
provider: target.provider,
|
|
123
|
+
model: target.model,
|
|
124
|
+
reply:
|
|
125
|
+
target.apiKey && typeof reply === "string"
|
|
126
|
+
? reply.replaceAll(target.apiKey, "[REDACTED]")
|
|
127
|
+
: reply,
|
|
128
|
+
usage: normalizedUsage,
|
|
129
|
+
totalCostUsd: priceLlmProbeUsage(target, normalizedUsage),
|
|
130
|
+
currency: "USD",
|
|
131
|
+
costBasis: "token-estimate",
|
|
132
|
+
billingVerified: false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
7
135
|
|
|
8
136
|
export async function probeLlmConnection(
|
|
9
137
|
target,
|
|
10
|
-
{ fetchImpl = fetch, timeoutMs = 20_000 } = {},
|
|
138
|
+
{ fetchImpl = fetch, timeoutMs = 20_000, includeUsage = false } = {},
|
|
11
139
|
) {
|
|
12
140
|
const base = target.baseUrl.replace(/\/+$/, "");
|
|
13
141
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -50,6 +178,7 @@ export async function probeLlmConnection(
|
|
|
50
178
|
}
|
|
51
179
|
const controller = new AbortController();
|
|
52
180
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
181
|
+
let usage = null;
|
|
53
182
|
try {
|
|
54
183
|
const response = await fetchImpl(url, {
|
|
55
184
|
method: "POST",
|
|
@@ -60,9 +189,12 @@ export async function probeLlmConnection(
|
|
|
60
189
|
});
|
|
61
190
|
if (!response.ok)
|
|
62
191
|
throw new Error(
|
|
63
|
-
await formatProviderResponseError(target.provider, response
|
|
192
|
+
await formatProviderResponseError(target.provider, response, {
|
|
193
|
+
secrets: [target.apiKey],
|
|
194
|
+
}),
|
|
64
195
|
);
|
|
65
196
|
const data = await response.json();
|
|
197
|
+
usage = normalizeLlmProbeUsage(target.provider, data);
|
|
66
198
|
const text =
|
|
67
199
|
target.provider === "ollama"
|
|
68
200
|
? data.response
|
|
@@ -91,11 +223,20 @@ export async function probeLlmConnection(
|
|
|
91
223
|
"The endpoint returned no model text; check the protocol and model",
|
|
92
224
|
);
|
|
93
225
|
}
|
|
94
|
-
|
|
226
|
+
const reply = (
|
|
227
|
+
target.apiKey ? text.replaceAll(target.apiKey, "[REDACTED]") : text
|
|
228
|
+
)
|
|
229
|
+
.trim()
|
|
230
|
+
.slice(0, 100);
|
|
231
|
+
return includeUsage
|
|
232
|
+
? llmProbeReceipt(target, { ok: true, reply, usage })
|
|
233
|
+
: reply;
|
|
95
234
|
} catch (error) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
235
|
+
const failure = controller.signal.aborted
|
|
236
|
+
? new Error(`Connection test timed out after ${timeoutMs / 1000}s`)
|
|
237
|
+
: error;
|
|
238
|
+
if (includeUsage) failure.probe = llmProbeReceipt(target, { usage });
|
|
239
|
+
throw failure;
|
|
99
240
|
} finally {
|
|
100
241
|
clearTimeout(timer);
|
|
101
242
|
}
|
package/src/lib/llm-pricing.js
CHANGED
|
@@ -216,13 +216,18 @@ export function normalizePricingTerms(value) {
|
|
|
216
216
|
const allowed = new Set([
|
|
217
217
|
"cacheReadMultiplier",
|
|
218
218
|
"cacheWriteMultiplier",
|
|
219
|
+
"cacheWrite1hMultiplier",
|
|
219
220
|
"longContext",
|
|
220
221
|
"serviceMultipliers",
|
|
221
222
|
]);
|
|
222
223
|
if (!object(value) || Reflect.ownKeys(value).some((key) => !allowed.has(key)))
|
|
223
224
|
fail();
|
|
224
225
|
const result = {};
|
|
225
|
-
for (const key of [
|
|
226
|
+
for (const key of [
|
|
227
|
+
"cacheReadMultiplier",
|
|
228
|
+
"cacheWriteMultiplier",
|
|
229
|
+
"cacheWrite1hMultiplier",
|
|
230
|
+
]) {
|
|
226
231
|
if (!Object.hasOwn(value, key)) continue;
|
|
227
232
|
if (!finite(value[key])) fail();
|
|
228
233
|
result[key] = value[key];
|
|
@@ -289,8 +294,15 @@ export function lookupRate(provider, model, table = PRICE_TABLE) {
|
|
|
289
294
|
// An unrecognized new generation must not inherit an older family price.
|
|
290
295
|
// Explicit operator overrides remain authoritative.
|
|
291
296
|
if (
|
|
292
|
-
|
|
293
|
-
|
|
297
|
+
e.match === "haiku" &&
|
|
298
|
+
/(?:^|[.:/])(?:claude-)?haiku-(?:[5-9]|\d{2,})(?:[.-]|$)/u.test(m) &&
|
|
299
|
+
!e.explicitOverride
|
|
300
|
+
)
|
|
301
|
+
continue;
|
|
302
|
+
if (
|
|
303
|
+
["opus", "sonnet", "haiku"].includes(e.match) &&
|
|
304
|
+
Number(/^claude-(?:opus|sonnet|haiku)-(\d+)(?:[.-]|$)/u.exec(m)?.[1]) >=
|
|
305
|
+
5 &&
|
|
294
306
|
!e.explicitOverride
|
|
295
307
|
)
|
|
296
308
|
continue;
|
|
@@ -337,18 +349,42 @@ export function estimateCost({
|
|
|
337
349
|
outputTokens = 0,
|
|
338
350
|
cacheReadTokens = 0,
|
|
339
351
|
cacheCreationTokens = 0,
|
|
352
|
+
cacheCreation5mTokens,
|
|
353
|
+
cacheCreation1hTokens,
|
|
354
|
+
cacheCreationInvalid = false,
|
|
355
|
+
pricingContextInvalid = false,
|
|
340
356
|
requestInputTokens,
|
|
341
357
|
serviceTier = "standard",
|
|
342
358
|
regionalProcessing = false,
|
|
343
359
|
table,
|
|
344
360
|
} = {}) {
|
|
345
361
|
const rate = lookupRate(provider, model, table);
|
|
362
|
+
const splitPresent =
|
|
363
|
+
cacheCreation5mTokens != null || cacheCreation1hTokens != null;
|
|
364
|
+
const splitValid =
|
|
365
|
+
splitPresent &&
|
|
366
|
+
Number.isSafeInteger(cacheCreation5mTokens) &&
|
|
367
|
+
cacheCreation5mTokens >= 0 &&
|
|
368
|
+
Number.isSafeInteger(cacheCreation1hTokens) &&
|
|
369
|
+
cacheCreation1hTokens >= 0 &&
|
|
370
|
+
cacheCreation5mTokens + cacheCreation1hTokens === cacheCreationTokens;
|
|
371
|
+
const missingCacheEvidence =
|
|
372
|
+
cacheCreationInvalid ||
|
|
373
|
+
(splitPresent && !splitValid) ||
|
|
374
|
+
(rate?.terms?.cacheWrite1hMultiplier != null &&
|
|
375
|
+
cacheCreationTokens > 0 &&
|
|
376
|
+
!splitValid);
|
|
346
377
|
const tierMultiplier = rate?.terms?.serviceMultipliers
|
|
347
378
|
? Object.hasOwn(rate.terms.serviceMultipliers, serviceTier)
|
|
348
379
|
? rate.terms.serviceMultipliers[serviceTier]
|
|
349
380
|
: null
|
|
350
381
|
: 1;
|
|
351
|
-
if (
|
|
382
|
+
if (
|
|
383
|
+
!rate ||
|
|
384
|
+
tierMultiplier == null ||
|
|
385
|
+
missingCacheEvidence ||
|
|
386
|
+
pricingContextInvalid
|
|
387
|
+
) {
|
|
352
388
|
return {
|
|
353
389
|
inputCost: 0,
|
|
354
390
|
outputCost: 0,
|
|
@@ -384,9 +420,14 @@ export function estimateCost({
|
|
|
384
420
|
inputMultiplier *
|
|
385
421
|
multiplier;
|
|
386
422
|
const cacheCreationCost =
|
|
387
|
-
(
|
|
423
|
+
((splitValid
|
|
424
|
+
? cacheCreation5mTokens *
|
|
425
|
+
(rate.terms?.cacheWriteMultiplier ?? CACHE_WRITE_MULTIPLIER) +
|
|
426
|
+
cacheCreation1hTokens * (rate.terms?.cacheWrite1hMultiplier ?? 2)
|
|
427
|
+
: Number(cacheCreationTokens) *
|
|
428
|
+
(rate.terms?.cacheWriteMultiplier ?? CACHE_WRITE_MULTIPLIER)) /
|
|
429
|
+
1e6) *
|
|
388
430
|
rate.in *
|
|
389
|
-
(rate.terms?.cacheWriteMultiplier ?? CACHE_WRITE_MULTIPLIER) *
|
|
390
431
|
inputMultiplier *
|
|
391
432
|
multiplier;
|
|
392
433
|
return {
|
|
@@ -419,6 +460,15 @@ export function priceRollup(aggregate, { table } = {}) {
|
|
|
419
460
|
outputTokens: row.outputTokens,
|
|
420
461
|
cacheReadTokens: row.cacheReadTokens,
|
|
421
462
|
cacheCreationTokens: row.cacheCreationTokens,
|
|
463
|
+
cacheCreation5mTokens: row.cacheCreation5mTokens,
|
|
464
|
+
cacheCreation1hTokens: row.cacheCreation1hTokens,
|
|
465
|
+
cacheCreationInvalid: row.cacheCreationInvalid,
|
|
466
|
+
pricingContextInvalid:
|
|
467
|
+
row.pricingContextInvalid === true ||
|
|
468
|
+
(String(row.provider).toLowerCase() === "anthropic" &&
|
|
469
|
+
String(row.model).toLowerCase() === "claude-haiku-5-5" &&
|
|
470
|
+
row.regionalProcessing == null &&
|
|
471
|
+
row.regional_processing == null),
|
|
422
472
|
serviceTier: row.serviceTier ?? row.service_tier ?? "standard",
|
|
423
473
|
regionalProcessing:
|
|
424
474
|
row.regionalProcessing ?? row.regional_processing ?? false,
|
|
@@ -479,7 +529,11 @@ export function priceRollup(aggregate, { table } = {}) {
|
|
|
479
529
|
for (const row of byModel) {
|
|
480
530
|
if (row.matched) {
|
|
481
531
|
totalCost += row.cost;
|
|
482
|
-
} else if (
|
|
532
|
+
} else if (
|
|
533
|
+
row.totalTokens > 0 ||
|
|
534
|
+
row.cacheReadTokens > 0 ||
|
|
535
|
+
row.cacheCreationTokens > 0
|
|
536
|
+
) {
|
|
483
537
|
unpriced.push({
|
|
484
538
|
provider: row.provider,
|
|
485
539
|
model: row.model,
|