@openagentsy/ledger 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +20 -2
  3. package/data/pricing/litellm.json +8882 -0
  4. package/data/pricing/models.dev.json +12151 -0
  5. package/data/pricing/openrouter.json +1017 -0
  6. package/dist/db.d.ts +44 -0
  7. package/dist/db.d.ts.map +1 -0
  8. package/dist/db.js +97 -0
  9. package/dist/db.js.map +1 -0
  10. package/dist/export.d.ts +71 -0
  11. package/dist/export.d.ts.map +1 -0
  12. package/dist/export.js +78 -0
  13. package/dist/export.js.map +1 -0
  14. package/dist/footprint.d.ts +24 -0
  15. package/dist/footprint.d.ts.map +1 -0
  16. package/dist/footprint.js +33 -0
  17. package/dist/footprint.js.map +1 -0
  18. package/dist/git.d.ts +44 -0
  19. package/dist/git.d.ts.map +1 -0
  20. package/dist/git.js +126 -0
  21. package/dist/git.js.map +1 -0
  22. package/dist/git.test.d.ts +2 -0
  23. package/dist/git.test.d.ts.map +1 -0
  24. package/dist/git.test.js +101 -0
  25. package/dist/git.test.js.map +1 -0
  26. package/dist/index.d.ts +19 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +19 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/ingest.d.ts +133 -0
  31. package/dist/ingest.d.ts.map +1 -0
  32. package/dist/ingest.js +324 -0
  33. package/dist/ingest.js.map +1 -0
  34. package/dist/ingest.test.d.ts +2 -0
  35. package/dist/ingest.test.d.ts.map +1 -0
  36. package/dist/ingest.test.js +245 -0
  37. package/dist/ingest.test.js.map +1 -0
  38. package/dist/outcomes.d.ts +81 -0
  39. package/dist/outcomes.d.ts.map +1 -0
  40. package/dist/outcomes.js +85 -0
  41. package/dist/outcomes.js.map +1 -0
  42. package/dist/outcomes.test.d.ts +2 -0
  43. package/dist/outcomes.test.d.ts.map +1 -0
  44. package/dist/outcomes.test.js +160 -0
  45. package/dist/outcomes.test.js.map +1 -0
  46. package/dist/policy.d.ts +48 -0
  47. package/dist/policy.d.ts.map +1 -0
  48. package/dist/policy.js +99 -0
  49. package/dist/policy.js.map +1 -0
  50. package/dist/policy.test.d.ts +2 -0
  51. package/dist/policy.test.d.ts.map +1 -0
  52. package/dist/policy.test.js +126 -0
  53. package/dist/policy.test.js.map +1 -0
  54. package/dist/pricing.d.ts +92 -0
  55. package/dist/pricing.d.ts.map +1 -0
  56. package/dist/pricing.js +341 -0
  57. package/dist/pricing.js.map +1 -0
  58. package/dist/pricing.test.d.ts +2 -0
  59. package/dist/pricing.test.d.ts.map +1 -0
  60. package/dist/pricing.test.js +167 -0
  61. package/dist/pricing.test.js.map +1 -0
  62. package/dist/query.d.ts +134 -0
  63. package/dist/query.d.ts.map +1 -0
  64. package/dist/query.js +247 -0
  65. package/dist/query.js.map +1 -0
  66. package/dist/query.test.d.ts +2 -0
  67. package/dist/query.test.d.ts.map +1 -0
  68. package/dist/query.test.js +242 -0
  69. package/dist/query.test.js.map +1 -0
  70. package/dist/schema.d.ts +33 -0
  71. package/dist/schema.d.ts.map +1 -0
  72. package/dist/schema.js +139 -0
  73. package/dist/schema.js.map +1 -0
  74. package/dist/schema.test.d.ts +2 -0
  75. package/dist/schema.test.d.ts.map +1 -0
  76. package/dist/schema.test.js +129 -0
  77. package/dist/schema.test.js.map +1 -0
  78. package/dist/waste.d.ts +116 -0
  79. package/dist/waste.d.ts.map +1 -0
  80. package/dist/waste.js +553 -0
  81. package/dist/waste.js.map +1 -0
  82. package/dist/waste.test.d.ts +2 -0
  83. package/dist/waste.test.d.ts.map +1 -0
  84. package/dist/waste.test.js +278 -0
  85. package/dist/waste.test.js.map +1 -0
  86. package/package.json +52 -4
@@ -0,0 +1,48 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { PolicyFiring, PolicyFiringSink } from '@openagentsy/core';
3
+ /**
4
+ * Record one policy firing.
5
+ *
6
+ * The row is written once and is not deduplicated: a policy that fires twice
7
+ * in a session fired twice, and the count of the firings is what the report
8
+ * ranks the policies by.
9
+ */
10
+ export declare function recordPolicyFiring(db: Database, firing: PolicyFiring): void;
11
+ /**
12
+ * A sink that writes the firings into a ledger.
13
+ *
14
+ * The engine writes through the interface and knows nothing of SQLite, so
15
+ * the store the rows go into is the caller's choice.
16
+ */
17
+ export declare function createPolicyFiringSink(db: Database): PolicyFiringSink;
18
+ /** How often one policy was asked, and what it decided. */
19
+ export type PolicyFireCount = {
20
+ /** The stable id of the policy. */
21
+ policyId: string;
22
+ /** How many times the policy was asked. */
23
+ count: number;
24
+ /** How many of those times it blocked. */
25
+ blocks: number;
26
+ /** How many of those times it flagged. */
27
+ flags: number;
28
+ /** The versions the policy carried, newest last. */
29
+ versions: string[];
30
+ };
31
+ /**
32
+ * The firing count of every policy the log holds a row for.
33
+ *
34
+ * The count is of every evaluation, and the block and flag counts are of the
35
+ * evaluations that acted, so a reader can see a policy that was asked often
36
+ * and never acted and one that acted every time.
37
+ */
38
+ export declare function policyFireCounts(db: Database): PolicyFireCount[];
39
+ /**
40
+ * The policies with the most firings, the largest first.
41
+ *
42
+ * The report asks for the ten of them; the number is a limit and not a
43
+ * promise, because a log with fewer policies has fewer to show.
44
+ */
45
+ export declare function topPoliciesByFireCount(db: Database, limit?: number): PolicyFireCount[];
46
+ /** How many firings the log holds in all. */
47
+ export declare function policyFiringTotal(db: Database): number;
48
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAoBxE;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAmB3E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,QAAQ,GAAG,gBAAgB,CAMrE;AAED,2DAA2D;AAC3D,MAAM,MAAM,eAAe,GAAG;IAC5B,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,eAAe,EAAE,CAqBhE;AAoBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,SAAK,GAAG,eAAe,EAAE,CAElF;AAED,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAKtD"}
package/dist/policy.js ADDED
@@ -0,0 +1,99 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { withBusyRetry } from './db.js';
3
+ /**
4
+ * The policy firing log (§21.3).
5
+ *
6
+ * Every policy that was asked for a decision leaves a row here: the policy
7
+ * id, the version it carried at that moment, the session it happened in and
8
+ * what it decided. The version is the point of the row — a regression is
9
+ * attributed to the version that caused it and not to the policy in general.
10
+ *
11
+ * The table is the ledger's, and the rows go into the ledger's database file,
12
+ * so the log survives a restart: it is a store and not a memory.
13
+ */
14
+ const INSERT = `INSERT INTO policy_firing
15
+ (id, session_id, policy_id, policy_version, layer, action, severity, reason,
16
+ finding_id, classification, detail, origin, ts)
17
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)`;
18
+ /**
19
+ * Record one policy firing.
20
+ *
21
+ * The row is written once and is not deduplicated: a policy that fires twice
22
+ * in a session fired twice, and the count of the firings is what the report
23
+ * ranks the policies by.
24
+ */
25
+ export function recordPolicyFiring(db, firing) {
26
+ const ts = firing.ts ?? new Date().toISOString();
27
+ withBusyRetry(() => {
28
+ db.query(INSERT).run(randomUUID(), firing.sessionId, firing.policyId, firing.policyVersion, firing.layer, firing.action, firing.severity ?? null, firing.reason ?? null, firing.findingId ?? null, firing.classification ?? null, firing.detail ?? null, firing.origin ?? 'first-party', ts);
29
+ });
30
+ }
31
+ /**
32
+ * A sink that writes the firings into a ledger.
33
+ *
34
+ * The engine writes through the interface and knows nothing of SQLite, so
35
+ * the store the rows go into is the caller's choice.
36
+ */
37
+ export function createPolicyFiringSink(db) {
38
+ return {
39
+ record: (firing) => {
40
+ recordPolicyFiring(db, firing);
41
+ },
42
+ };
43
+ }
44
+ /**
45
+ * The firing count of every policy the log holds a row for.
46
+ *
47
+ * The count is of every evaluation, and the block and flag counts are of the
48
+ * evaluations that acted, so a reader can see a policy that was asked often
49
+ * and never acted and one that acted every time.
50
+ */
51
+ export function policyFireCounts(db) {
52
+ const rows = db
53
+ .query(`SELECT
54
+ policy_id AS policyId,
55
+ COUNT(*) AS count,
56
+ SUM(CASE WHEN action = 'block' THEN 1 ELSE 0 END) AS blocks,
57
+ SUM(CASE WHEN action = 'flag' THEN 1 ELSE 0 END) AS flags
58
+ FROM policy_firing
59
+ GROUP BY policy_id
60
+ ORDER BY count DESC, policy_id ASC`)
61
+ .all();
62
+ return rows.map((row) => ({
63
+ policyId: row.policyId,
64
+ count: row.count,
65
+ blocks: row.blocks,
66
+ flags: row.flags,
67
+ versions: policyVersions(db, row.policyId),
68
+ }));
69
+ }
70
+ /**
71
+ * The versions one policy carried, in the order they were seen.
72
+ *
73
+ * A policy that carried two versions was changed between the two firings,
74
+ * and the log is where that is visible.
75
+ */
76
+ function policyVersions(db, policyId) {
77
+ const rows = db
78
+ .query(`SELECT DISTINCT policy_version AS version
79
+ FROM policy_firing
80
+ WHERE policy_id = ?
81
+ ORDER BY policy_version ASC`)
82
+ .all(policyId);
83
+ return rows.map((row) => row.version ?? 'unknown');
84
+ }
85
+ /**
86
+ * The policies with the most firings, the largest first.
87
+ *
88
+ * The report asks for the ten of them; the number is a limit and not a
89
+ * promise, because a log with fewer policies has fewer to show.
90
+ */
91
+ export function topPoliciesByFireCount(db, limit = 10) {
92
+ return policyFireCounts(db).slice(0, Math.max(0, limit));
93
+ }
94
+ /** How many firings the log holds in all. */
95
+ export function policyFiringTotal(db) {
96
+ const row = db.query('SELECT COUNT(*) AS total FROM policy_firing').get();
97
+ return row?.total ?? 0;
98
+ }
99
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,GAAG;;;qCAGsB,CAAC;AAEtC;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAY,EAAE,MAAoB;IACnE,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACjD,aAAa,CAAC,GAAG,EAAE;QACjB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAClB,UAAU,EAAE,EACZ,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,aAAa,EACpB,MAAM,CAAC,KAAK,EACZ,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,QAAQ,IAAI,IAAI,EACvB,MAAM,CAAC,MAAM,IAAI,IAAI,EACrB,MAAM,CAAC,SAAS,IAAI,IAAI,EACxB,MAAM,CAAC,cAAc,IAAI,IAAI,EAC7B,MAAM,CAAC,MAAM,IAAI,IAAI,EACrB,MAAM,CAAC,MAAM,IAAI,aAAa,EAC9B,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAY;IACjD,OAAO;QACL,MAAM,EAAE,CAAC,MAAoB,EAAE,EAAE;YAC/B,kBAAkB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC;AAgBD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAY;IAC3C,MAAM,IAAI,GAAG,EAAE;SACZ,KAAK,CACJ;;;;;;;0CAOoC,CACrC;SACA,GAAG,EAA0E,CAAC;IAEjF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC;KAC3C,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,EAAY,EAAE,QAAgB;IACpD,MAAM,IAAI,GAAG,EAAE;SACZ,KAAK,CACJ;;;mCAG6B,CAC9B;SACA,GAAG,CAAC,QAAQ,CAAiC,CAAC;IACjD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,IAAI,SAAS,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAY,EAAE,KAAK,GAAG,EAAE;IAC7D,OAAO,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,iBAAiB,CAAC,EAAY;IAC5C,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC,GAAG,EAE/D,CAAC;IACT,OAAO,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=policy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.test.d.ts","sourceRoot":"","sources":["../src/policy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { mkdtempSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { closeLedger, openLedger } from './db.js';
6
+ import { createPolicyFiringSink, policyFireCounts, policyFiringTotal, recordPolicyFiring, topPoliciesByFireCount, } from './policy.js';
7
+ /**
8
+ * The policy firing log (§21.3).
9
+ *
10
+ * The log is a store and not a memory: the rows are read back after the
11
+ * ledger was closed and opened again.
12
+ */
13
+ /** A ledger in a directory of its own. */
14
+ function ledger() {
15
+ const root = mkdtempSync(join(tmpdir(), 'openagentsy-policy-'));
16
+ return { root, db: openLedger(root) };
17
+ }
18
+ describe('the policy firing log', () => {
19
+ it('reads back what was written after the ledger was closed and opened again', () => {
20
+ const { root, db } = ledger();
21
+ recordPolicyFiring(db, {
22
+ sessionId: 'ses_1',
23
+ policyId: 'oa.anti-sycophancy',
24
+ policyVersion: '1.0.0',
25
+ layer: 4,
26
+ action: 'flag',
27
+ severity: 'warn',
28
+ reason: 'praise',
29
+ origin: 'first-party',
30
+ });
31
+ recordPolicyFiring(db, {
32
+ sessionId: 'ses_1',
33
+ policyId: 'oa.anti-sycophancy',
34
+ policyVersion: '1.0.0',
35
+ layer: 4,
36
+ action: 'block',
37
+ reason: 'high-risk domain',
38
+ origin: 'first-party',
39
+ });
40
+ recordPolicyFiring(db, {
41
+ sessionId: 'ses_2',
42
+ policyId: 'oa.agi-framing',
43
+ policyVersion: '1.2.0',
44
+ layer: 4,
45
+ action: 'allow',
46
+ origin: 'external',
47
+ });
48
+ closeLedger(db);
49
+ const again = openLedger(root);
50
+ try {
51
+ expect(policyFiringTotal(again)).toBe(3);
52
+ const counts = topPoliciesByFireCount(again, 10);
53
+ expect(counts[0]?.policyId).toBe('oa.anti-sycophancy');
54
+ expect(counts[0]?.count).toBe(2);
55
+ expect(counts[0]?.flags).toBe(1);
56
+ expect(counts[0]?.blocks).toBe(1);
57
+ expect(counts[0]?.versions).toEqual(['1.0.0']);
58
+ expect(counts[1]?.policyId).toBe('oa.agi-framing');
59
+ expect(counts[1]?.versions).toEqual(['1.2.0']);
60
+ }
61
+ finally {
62
+ closeLedger(again);
63
+ }
64
+ });
65
+ it('holds the version the policy carried when it fired', () => {
66
+ const { db } = ledger();
67
+ try {
68
+ recordPolicyFiring(db, {
69
+ sessionId: 'ses_1',
70
+ policyId: 'oa.anti-sycophancy',
71
+ policyVersion: '1.1.0',
72
+ layer: 4,
73
+ action: 'allow',
74
+ origin: 'first-party',
75
+ });
76
+ const row = db
77
+ .query('SELECT policy_version, session_id, action FROM policy_firing WHERE policy_id = ?')
78
+ .get('oa.anti-sycophancy');
79
+ expect(row.policy_version).toBe('1.1.0');
80
+ expect(row.session_id).toBe('ses_1');
81
+ expect(row.action).toBe('allow');
82
+ }
83
+ finally {
84
+ closeLedger(db);
85
+ }
86
+ });
87
+ it('marks a module a plugin brought as external', () => {
88
+ const { db } = ledger();
89
+ try {
90
+ const sink = createPolicyFiringSink(db);
91
+ sink.record({
92
+ sessionId: 'ses_1',
93
+ policyId: 'acme.no-warnings',
94
+ policyVersion: '0.1.0',
95
+ layer: 4,
96
+ action: 'allow',
97
+ origin: 'external',
98
+ });
99
+ const row = db
100
+ .query('SELECT origin FROM policy_firing WHERE policy_id = ?')
101
+ .get('acme.no-warnings');
102
+ expect(row.origin).toBe('external');
103
+ }
104
+ finally {
105
+ closeLedger(db);
106
+ }
107
+ });
108
+ it('counts every policy the log holds a row for', () => {
109
+ const { db } = ledger();
110
+ try {
111
+ recordPolicyFiring(db, {
112
+ sessionId: 'ses_1',
113
+ policyId: 'oa.agi-framing',
114
+ policyVersion: '1.0.0',
115
+ layer: 4,
116
+ action: 'allow',
117
+ origin: 'first-party',
118
+ });
119
+ expect(policyFireCounts(db).map((entry) => entry.policyId)).toEqual(['oa.agi-framing']);
120
+ }
121
+ finally {
122
+ closeLedger(db);
123
+ }
124
+ });
125
+ });
126
+ //# sourceMappingURL=policy.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.test.js","sourceRoot":"","sources":["../src/policy.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The pricing chain (§6.3).
3
+ *
4
+ * The reported cost of a message wins, then models.dev, then LiteLLM, then
5
+ * OpenRouter, and then nothing: a model nobody prices stays unpriced and is
6
+ * shown as unpriced, never as zero dollars. A zero would be a lie about
7
+ * money, and a report that says "free" is a report nobody can act on.
8
+ */
9
+ /** The token counts of one message. */
10
+ export type TokenCounts = {
11
+ input: number;
12
+ output: number;
13
+ reasoning?: number;
14
+ cacheRead?: number;
15
+ cacheWrite?: number;
16
+ };
17
+ /** The rates of a band, per million tokens. */
18
+ export type Rates = {
19
+ input: number;
20
+ output: number;
21
+ cacheRead?: number;
22
+ cacheWrite?: number;
23
+ };
24
+ /**
25
+ * A band of the pricing.
26
+ *
27
+ * `above` is the number of input tokens the band starts at. A source with
28
+ * no tiers has one band, from zero upwards.
29
+ */
30
+ export type Tier = Rates & {
31
+ above: number;
32
+ };
33
+ /** Where a price came from. */
34
+ export type PriceSource = 'reported' | 'models.dev' | 'litellm' | 'openrouter' | 'none';
35
+ export type PriceResult = {
36
+ /** The price in dollars, or null when no source knows it. */
37
+ cost: number | null;
38
+ source: PriceSource;
39
+ /** The bands that priced it. */
40
+ tiers?: Tier[];
41
+ };
42
+ export type PriceOptions = {
43
+ /** The cost OpenCode reported, when it reported one. */
44
+ reportedCost?: number | null;
45
+ /** Where the cached sources live. */
46
+ cacheDir?: string;
47
+ /** The fetch to use, so a test does not need the network. */
48
+ fetchImpl?: FetchLike;
49
+ /** How long a fetch may take. */
50
+ timeoutMs?: number;
51
+ /** The clock, so a test can age a cache file. */
52
+ now?: () => number;
53
+ /** Where the bundled snapshot lives. */
54
+ snapshotDir?: string;
55
+ };
56
+ /**
57
+ * The fetch the chain uses.
58
+ *
59
+ * It is not the global `fetch` type, because a test hands in a plain
60
+ * function and the global one carries properties a double does not have.
61
+ */
62
+ export type FetchLike = (url: string, init?: {
63
+ signal?: AbortSignal;
64
+ }) => Promise<Response>;
65
+ /** How long a cached source stays fresh. */
66
+ export declare const CACHE_TTL_MS: number;
67
+ /** How long a fetch may take before the chain moves on. */
68
+ export declare const FETCH_TIMEOUT_MS = 10000;
69
+ /** One source of the chain. */
70
+ export type Source = {
71
+ name: PriceSource;
72
+ /** The file the source is cached in. */
73
+ file: string;
74
+ url: string;
75
+ reduce: (raw: unknown) => unknown;
76
+ lookup: (payload: unknown, provider: string, model: string) => Tier[] | null;
77
+ };
78
+ /** The chain, in order. The first hit wins. */
79
+ export declare const SOURCES: readonly Source[];
80
+ /** Where the cached sources live. */
81
+ export declare function defaultCacheDir(): string;
82
+ /** Where the bundled snapshot lives. */
83
+ export declare function defaultSnapshotDir(): string;
84
+ /**
85
+ * The price of a message.
86
+ *
87
+ * The reported cost of the message wins, because the host knows what it
88
+ * charged. After that the chain runs, and when every source fails the
89
+ * result is a null cost and the source `none`: unpriced, not free.
90
+ */
91
+ export declare function priceMessage(provider: string, model: string, tokens: TokenCounts, options?: PriceOptions): Promise<PriceResult>;
92
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AAEH,uCAAuC;AACvC,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,KAAK,GAAG;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,+BAA+B;AAC/B,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,CAAC;AAExF,MAAM,MAAM,WAAW,GAAG;IACxB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,gCAAgC;IAChC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE5F,4CAA4C;AAC5C,eAAO,MAAM,YAAY,QAAsB,CAAC;AAChD,2DAA2D;AAC3D,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAqMvC,+BAA+B;AAC/B,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IAClC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,EAAE,GAAG,IAAI,CAAC;CAC9E,CAAC;AAEF,+CAA+C;AAC/C,eAAO,MAAM,OAAO,EAAE,SAAS,MAAM,EAwCpC,CAAC;AAEF,qCAAqC;AACrC,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wCAAwC;AACxC,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AA+DD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CAiBtB"}