@trazum/core 1.43.0 → 1.45.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.
package/README.md CHANGED
@@ -171,6 +171,29 @@ vanishing, because "we alerted about this" and "this is fine now" are
171
171
  different sentences. The unwatched stretch between cycles is returned as
172
172
  `gap`.
173
173
 
174
+ ## Answering before the call
175
+
176
+ `answerCost(request, { catalogue })` answers the two questions asked at call
177
+ time. The budget consumed is `measured`, the cost of the described call is
178
+ `estimated`, and the two never merge: the composed `afterCall.usd` carries its
179
+ halves beside it, and `restsOn` says whether the verdict needed the estimate
180
+ (`measured` when the budget is already past its limit, `measured+estimated`
181
+ when it takes this call to cross). Three outcomes rather than two — `within`,
182
+ `over`, `cannot-tell` — and the reasons are kept apart because their fixes
183
+ differ: no budget configured, nothing measured, or a model the catalogue
184
+ cannot price. Pure and synchronous, because a function that reads a file
185
+ cannot promise an answer in milliseconds.
186
+
187
+ ## The guard
188
+
189
+ `guardSpend(request, { catalogue })` wraps `answerCost` with what to do
190
+ instead. A refusal carries alternatives — route, batch, or both combined the
191
+ way `billLevers` combines them rather than summed — each priced for the single
192
+ call being decided, each carrying its typed assumption, and each filtered to
193
+ models whose context window the prompt actually fits. `cannot-tell` never
194
+ becomes `yes`: a guard that permits whatever it cannot judge permits
195
+ everything the moment its inputs go missing.
196
+
174
197
  ## Comparing two versions
175
198
 
176
199
  ```ts
@@ -0,0 +1,117 @@
1
+ /**
2
+ * "What will this call cost, and is there budget?" — answered before it is
3
+ * sent.
4
+ *
5
+ * Everything Trazum knows sits behind a process launch, a config walk and a
6
+ * log parse. That is fine for a report and useless for a decision being made
7
+ * right now: by the time a report exists, the call has been paid for.
8
+ *
9
+ * **This is where the temptation to merge halves is strongest**, which is why
10
+ * the shape below refuses to. The budget consumed is *measured* — it comes
11
+ * from the provider's own billed counts. The cost of the call being asked
12
+ * about is *estimated* — nobody has sent it yet, and the token count is a
13
+ * heuristic. A single "you have $38 left after this" would be a number that is
14
+ * neither, handed to a caller with no way to tell.
15
+ *
16
+ * So the answer carries both halves separately, and the composed figure — which
17
+ * callers genuinely need — arrives with its two halves broken out beside it.
18
+ * The verdict names what it rests on: `measured` when the budget is already
19
+ * blown without help from any estimate, and `measured+estimated` when it takes
20
+ * the described call to cross. A caller reading only the verdict still cannot
21
+ * mistake one for the other.
22
+ */
23
+ import type { PricingCatalogue } from './pricing.js';
24
+ export type AnswerVerdict = 'within' | 'over' | 'cannot-tell';
25
+ /** Why the question cannot be answered, when it cannot. */
26
+ export type CannotTellReasonAnswer =
27
+ /** No budget is configured, so "is there budget left" has no subject. */
28
+ 'no-budget-configured'
29
+ /** Nothing has been measured, so the consumed half is unknown. */
30
+ | 'nothing-measured'
31
+ /** The model is not in the catalogue, so the call cannot be priced. */
32
+ | 'model-unpriced';
33
+ export interface CallEstimate {
34
+ model: string;
35
+ inputTokens: number;
36
+ outputTokens: number;
37
+ estimatedUsd: number;
38
+ /** Always `estimated`: this call has not happened. */
39
+ provenance: 'estimated';
40
+ /**
41
+ * What the estimate rests on, so a caller can weigh it.
42
+ *
43
+ * `token-count` means the caller handed over counts it had already made;
44
+ * `heuristic` means Trazum counted the text itself, with the ±10% band the
45
+ * estimator has published since 1.9.
46
+ */
47
+ basis: 'token-count' | 'heuristic';
48
+ }
49
+ export interface BudgetPosition {
50
+ limitUsd: number;
51
+ /** Spent so far, from the provider's own billed counts. */
52
+ consumedUsd: number;
53
+ remainingUsd: number;
54
+ /** Always `measured`: this is a bill, not a projection. */
55
+ provenance: 'measured';
56
+ /** The period the consumed figure covers. */
57
+ window: {
58
+ fromMs: number;
59
+ toMs: number;
60
+ } | null;
61
+ }
62
+ export interface CostAnswer {
63
+ schemaVersion: 1;
64
+ /** The call the caller described, priced. Null when none was described. */
65
+ call: CallEstimate | null;
66
+ /** Where the budget stands. Null when there is no budget or nothing measured. */
67
+ budget: BudgetPosition | null;
68
+ verdict: AnswerVerdict;
69
+ /**
70
+ * What the verdict rests on — the field that keeps this honest.
71
+ *
72
+ * `measured` means the budget is already past its limit and the estimate
73
+ * played no part. `measured+estimated` means it takes the described call to
74
+ * cross, so the verdict is only as good as the token count behind it. A
75
+ * caller that reads nothing else can still tell those apart.
76
+ */
77
+ restsOn: 'measured' | 'measured+estimated' | null;
78
+ reason: CannotTellReasonAnswer | null;
79
+ /**
80
+ * Where the budget would stand after this call — the figure callers actually
81
+ * want, with its halves kept visible so the composition cannot be mistaken
82
+ * for a measurement.
83
+ */
84
+ afterCall: {
85
+ usd: number;
86
+ halves: {
87
+ measuredUsd: number;
88
+ estimatedUsd: number;
89
+ };
90
+ } | null;
91
+ }
92
+ export interface AnswerRequest {
93
+ model?: string;
94
+ inputTokens?: number;
95
+ outputTokens?: number;
96
+ /** Measured spend so far, when there is any. */
97
+ consumedUsd?: number;
98
+ limitUsd?: number;
99
+ window?: {
100
+ fromMs: number;
101
+ toMs: number;
102
+ } | null;
103
+ /** How the token counts were arrived at. */
104
+ basis?: 'token-count' | 'heuristic';
105
+ }
106
+ /**
107
+ * Answers the two questions, from figures the caller already holds.
108
+ *
109
+ * Pure and synchronous on purpose: the whole point of this release is an
110
+ * answer in single-digit milliseconds, and a function that reads a file cannot
111
+ * promise that. The server hands it a store total it read once and keeps.
112
+ */
113
+ export declare function answerCost(request: AnswerRequest, options: {
114
+ catalogue: PricingCatalogue;
115
+ on?: Date;
116
+ }): CostAnswer;
117
+ //# sourceMappingURL=answer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D,2DAA2D;AAC3D,MAAM,MAAM,sBAAsB;AAChC,yEAAyE;AACvE,sBAAsB;AACxB,kEAAkE;GAChE,kBAAkB;AACpB,uEAAuE;GACrE,gBAAgB,CAAC;AAErB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,UAAU,EAAE,WAAW,CAAC;IACxB;;;;;;OAMG;IACH,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,UAAU,EAAE,UAAU,CAAC;IACvB,6CAA6C;IAC7C,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,CAAC,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,aAAa,CAAC;IACvB;;;;;;;OAOG;IACH,OAAO,EAAE,UAAU,GAAG,oBAAoB,GAAG,IAAI,CAAC;IAClD,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;KACvD,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,4CAA4C;IAC5C,KAAK,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;IAAE,SAAS,EAAE,gBAAgB,CAAC;IAAC,EAAE,CAAC,EAAE,IAAI,CAAA;CAAE,GAClD,UAAU,CA8GZ"}
package/dist/answer.js ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * "What will this call cost, and is there budget?" — answered before it is
3
+ * sent.
4
+ *
5
+ * Everything Trazum knows sits behind a process launch, a config walk and a
6
+ * log parse. That is fine for a report and useless for a decision being made
7
+ * right now: by the time a report exists, the call has been paid for.
8
+ *
9
+ * **This is where the temptation to merge halves is strongest**, which is why
10
+ * the shape below refuses to. The budget consumed is *measured* — it comes
11
+ * from the provider's own billed counts. The cost of the call being asked
12
+ * about is *estimated* — nobody has sent it yet, and the token count is a
13
+ * heuristic. A single "you have $38 left after this" would be a number that is
14
+ * neither, handed to a caller with no way to tell.
15
+ *
16
+ * So the answer carries both halves separately, and the composed figure — which
17
+ * callers genuinely need — arrives with its two halves broken out beside it.
18
+ * The verdict names what it rests on: `measured` when the budget is already
19
+ * blown without help from any estimate, and `measured+estimated` when it takes
20
+ * the described call to cross. A caller reading only the verdict still cannot
21
+ * mistake one for the other.
22
+ */
23
+ import { effectivePricing } from './pricing.js';
24
+ /**
25
+ * Answers the two questions, from figures the caller already holds.
26
+ *
27
+ * Pure and synchronous on purpose: the whole point of this release is an
28
+ * answer in single-digit milliseconds, and a function that reads a file cannot
29
+ * promise that. The server hands it a store total it read once and keeps.
30
+ */
31
+ export function answerCost(request, options) {
32
+ const { catalogue, on = new Date() } = options;
33
+ let call = null;
34
+ let unpriced = false;
35
+ if (request.model !== undefined) {
36
+ const model = catalogue.byId.get(request.model);
37
+ if (model === undefined) {
38
+ unpriced = true;
39
+ }
40
+ else {
41
+ const { inputPerMTok, outputPerMTok } = effectivePricing(model, on);
42
+ const inputTokens = request.inputTokens ?? 0;
43
+ const outputTokens = request.outputTokens ?? 0;
44
+ call = {
45
+ model: request.model,
46
+ inputTokens,
47
+ outputTokens,
48
+ estimatedUsd: (inputTokens / 1_000_000) * inputPerMTok + (outputTokens / 1_000_000) * outputPerMTok,
49
+ provenance: 'estimated',
50
+ basis: request.basis ?? 'token-count',
51
+ };
52
+ }
53
+ }
54
+ const hasBudget = request.limitUsd !== undefined;
55
+ const hasMeasurement = request.consumedUsd !== undefined;
56
+ let budget = null;
57
+ if (hasBudget && hasMeasurement) {
58
+ budget = {
59
+ limitUsd: request.limitUsd,
60
+ consumedUsd: request.consumedUsd,
61
+ remainingUsd: request.limitUsd - request.consumedUsd,
62
+ provenance: 'measured',
63
+ window: request.window ?? null,
64
+ };
65
+ }
66
+ /**
67
+ * The order of these refusals matters. A missing budget and a missing
68
+ * measurement are different problems with different fixes — configure a
69
+ * limit, or connect a source — and collapsing them into one message sends
70
+ * half the readers to the wrong place.
71
+ */
72
+ if (budget === null) {
73
+ return {
74
+ schemaVersion: 1,
75
+ call,
76
+ budget: null,
77
+ verdict: 'cannot-tell',
78
+ restsOn: null,
79
+ reason: !hasBudget ? 'no-budget-configured' : 'nothing-measured',
80
+ afterCall: null,
81
+ };
82
+ }
83
+ if (unpriced) {
84
+ // The budget half is known and the call half is not. Answering "within"
85
+ // would be answering a question nobody asked: whether the *current* spend
86
+ // fits, rather than whether this call does.
87
+ return {
88
+ schemaVersion: 1,
89
+ call: null,
90
+ budget,
91
+ verdict: 'cannot-tell',
92
+ restsOn: null,
93
+ reason: 'model-unpriced',
94
+ afterCall: null,
95
+ };
96
+ }
97
+ const estimatedUsd = call?.estimatedUsd ?? 0;
98
+ const afterUsd = budget.consumedUsd + estimatedUsd;
99
+ /**
100
+ * Already over without any help from the estimate: the verdict is a
101
+ * measurement, and saying so lets a caller act on it with full confidence
102
+ * rather than wondering how good the token count was.
103
+ */
104
+ if (budget.consumedUsd > budget.limitUsd) {
105
+ return {
106
+ schemaVersion: 1,
107
+ call,
108
+ budget,
109
+ verdict: 'over',
110
+ restsOn: 'measured',
111
+ reason: null,
112
+ afterCall: {
113
+ usd: afterUsd,
114
+ halves: { measuredUsd: budget.consumedUsd, estimatedUsd },
115
+ },
116
+ };
117
+ }
118
+ return {
119
+ schemaVersion: 1,
120
+ call,
121
+ budget,
122
+ verdict: afterUsd > budget.limitUsd ? 'over' : 'within',
123
+ // It takes the described call to cross, so the verdict is only as good as
124
+ // the token count behind it — and it says so rather than implying a
125
+ // measurement.
126
+ restsOn: 'measured+estimated',
127
+ reason: null,
128
+ afterCall: {
129
+ usd: afterUsd,
130
+ halves: { measuredUsd: budget.consumedUsd, estimatedUsd },
131
+ },
132
+ };
133
+ }
134
+ //# sourceMappingURL=answer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer.js","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The thing spending the money can finally ask, and be told no.
3
+ *
4
+ * 1.44 gave an endpoint that *answers*. An agent may consult it and ignore it,
5
+ * which is fine — advice an implementation can skip is still advice worth
6
+ * having. What was missing is the shape of a refusal an agent can act on.
7
+ *
8
+ * **A guard that only says no teaches a caller to stop asking.** A model told
9
+ * "denied" with no alternative has exactly two moves: send it anyway, or fail
10
+ * the user's request. Both are worse than the call it wanted to make. So every
11
+ * refusal here arrives with the levers that exist — this work routes to a
12
+ * cheaper model that still fits, a batch window would halve it — each with
13
+ * what it is worth *for this call*, and the assumption it rests on.
14
+ *
15
+ * **The guard never spends to answer.** No provider call, no LLM pass, no
16
+ * pull. The answer comes from the store and the catalogue, or it says it
17
+ * cannot tell. A cost guard that costs money to consult is a joke with a bill
18
+ * attached.
19
+ *
20
+ * **An alternative the prompt does not fit in is not an alternative.** A
21
+ * cheaper model with a smaller context window does not make this call cheaper;
22
+ * it makes it impossible. Those are filtered out here rather than offered and
23
+ * blamed later.
24
+ */
25
+ import type { AnswerRequest, CostAnswer } from './answer.js';
26
+ import type { PricingCatalogue } from './pricing.js';
27
+ import type { PlanAssumption } from './plan.js';
28
+ export type GuardVerdict = 'yes' | 'no' | 'cannot-tell';
29
+ export interface GuardAlternative {
30
+ kind: 'route' | 'batch' | 'route+batch';
31
+ /** The model this moves to, when it moves. */
32
+ model: {
33
+ id: string;
34
+ displayName: string;
35
+ } | null;
36
+ /**
37
+ * What this alternative saves **on this call** — not per month.
38
+ *
39
+ * The caller is deciding one call, right now. A monthly figure would be the
40
+ * right number at the wrong moment, and an agent has no way to act on it.
41
+ */
42
+ savingUsd: number;
43
+ /** What the log cannot confirm, typed as everywhere since 1.38. */
44
+ assumes: PlanAssumption[];
45
+ /**
46
+ * Whether the described call fits this alternative's context window.
47
+ *
48
+ * Only `true` ever reaches a caller — the false ones are dropped before
49
+ * they are offered. The field exists so the rule is visible in the type
50
+ * rather than buried in a filter nobody reads.
51
+ */
52
+ fits: true;
53
+ }
54
+ export interface GuardAnswer {
55
+ schemaVersion: 1;
56
+ verdict: GuardVerdict;
57
+ /** The full cost answer, halves and provenance intact. */
58
+ cost: CostAnswer;
59
+ /**
60
+ * What to do instead, dearest saving first. Present on a refusal, and on a
61
+ * yes as well: an agent that can spend less while still being allowed to
62
+ * spend should be told so.
63
+ */
64
+ alternatives: GuardAlternative[];
65
+ /**
66
+ * A one-line reason a human will read in a log. The fields above are what a
67
+ * machine acts on; this is never the only place a fact appears.
68
+ */
69
+ because: string;
70
+ }
71
+ export interface GuardRequest extends AnswerRequest {
72
+ /** Whether the caller says this work can wait for a batch window. */
73
+ batchEligible?: boolean;
74
+ }
75
+ export declare function guardSpend(request: GuardRequest, options: {
76
+ catalogue: PricingCatalogue;
77
+ on?: Date;
78
+ }): GuardAnswer;
79
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,IAAI,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,aAAa,CAAC;IACxC,8CAA8C;IAC9C,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClD;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B;;;;;;OAMG;IACH,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,YAAY,CAAC;IACtB,0DAA0D;IAC1D,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAuBD,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE;IAAE,SAAS,EAAE,gBAAgB,CAAC;IAAC,EAAE,CAAC,EAAE,IAAI,CAAA;CAAE,GAClD,WAAW,CA8Eb"}
package/dist/guard.js ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * The thing spending the money can finally ask, and be told no.
3
+ *
4
+ * 1.44 gave an endpoint that *answers*. An agent may consult it and ignore it,
5
+ * which is fine — advice an implementation can skip is still advice worth
6
+ * having. What was missing is the shape of a refusal an agent can act on.
7
+ *
8
+ * **A guard that only says no teaches a caller to stop asking.** A model told
9
+ * "denied" with no alternative has exactly two moves: send it anyway, or fail
10
+ * the user's request. Both are worse than the call it wanted to make. So every
11
+ * refusal here arrives with the levers that exist — this work routes to a
12
+ * cheaper model that still fits, a batch window would halve it — each with
13
+ * what it is worth *for this call*, and the assumption it rests on.
14
+ *
15
+ * **The guard never spends to answer.** No provider call, no LLM pass, no
16
+ * pull. The answer comes from the store and the catalogue, or it says it
17
+ * cannot tell. A cost guard that costs money to consult is a joke with a bill
18
+ * attached.
19
+ *
20
+ * **An alternative the prompt does not fit in is not an alternative.** A
21
+ * cheaper model with a smaller context window does not make this call cheaper;
22
+ * it makes it impossible. Those are filtered out here rather than offered and
23
+ * blamed later.
24
+ */
25
+ import { answerCost } from './answer.js';
26
+ import { effectivePricing, multipliersFor } from './pricing.js';
27
+ /** Models cheaper than this one, in the same family, that the prompt fits in. */
28
+ function cheaperThan(model, catalogue, inputTokens, on) {
29
+ const here = effectivePricing(model, on);
30
+ return [...catalogue.byId.values()]
31
+ .filter((candidate) => {
32
+ if (candidate.id === model.id)
33
+ return false;
34
+ if (candidate.provider !== model.provider)
35
+ return false;
36
+ const there = effectivePricing(candidate, on);
37
+ if (there.inputPerMTok >= here.inputPerMTok)
38
+ return false;
39
+ // A model the prompt does not fit in is not a cheaper way to make this
40
+ // call; it is a way not to make it.
41
+ return candidate.contextWindow >= inputTokens;
42
+ })
43
+ .sort((a, b) => effectivePricing(b, on).inputPerMTok - effectivePricing(a, on).inputPerMTok);
44
+ }
45
+ export function guardSpend(request, options) {
46
+ const { catalogue, on = new Date() } = options;
47
+ const cost = answerCost(request, { catalogue, on });
48
+ const alternatives = [];
49
+ const model = request.model === undefined ? undefined : catalogue.byId.get(request.model);
50
+ if (model !== undefined && cost.call !== null) {
51
+ const inputTokens = cost.call.inputTokens;
52
+ const outputTokens = cost.call.outputTokens;
53
+ const here = effectivePricing(model, on);
54
+ const priceOf = (candidate) => {
55
+ const rates = effectivePricing(candidate, on);
56
+ return (inputTokens / 1_000_000) * rates.inputPerMTok + (outputTokens / 1_000_000) * rates.outputPerMTok;
57
+ };
58
+ const mine = (inputTokens / 1_000_000) * here.inputPerMTok + (outputTokens / 1_000_000) * here.outputPerMTok;
59
+ const batchRate = multipliersFor(model).batch;
60
+ for (const candidate of cheaperThan(model, catalogue, inputTokens, on)) {
61
+ const routed = priceOf(candidate);
62
+ const candidateBatch = multipliersFor(candidate).batch;
63
+ const both = candidateBatch === null ? null : routed * candidateBatch;
64
+ /**
65
+ * Route and batch on the same call combine the way `billLevers` has
66
+ * combined them since 1.23: the batch discount applies to the *cheaper*
67
+ * model's price, never as a second subtraction from this one. Adding the
68
+ * two savings is the arithmetic `plan` exists to kill.
69
+ */
70
+ if (request.batchEligible === true && both !== null) {
71
+ alternatives.push({
72
+ kind: 'route+batch',
73
+ model: { id: candidate.id, displayName: candidate.displayName },
74
+ savingUsd: mine - both,
75
+ assumes: [
76
+ { kind: 'model-capability', model: candidate.displayName },
77
+ { kind: 'batch-window' },
78
+ ],
79
+ fits: true,
80
+ });
81
+ }
82
+ alternatives.push({
83
+ kind: 'route',
84
+ model: { id: candidate.id, displayName: candidate.displayName },
85
+ savingUsd: mine - routed,
86
+ assumes: [{ kind: 'model-capability', model: candidate.displayName }],
87
+ fits: true,
88
+ });
89
+ }
90
+ if (request.batchEligible === true && batchRate !== null) {
91
+ alternatives.push({
92
+ kind: 'batch',
93
+ model: null,
94
+ savingUsd: mine - mine * batchRate,
95
+ assumes: [{ kind: 'batch-window' }],
96
+ fits: true,
97
+ });
98
+ }
99
+ }
100
+ alternatives.sort((a, b) => b.savingUsd - a.savingUsd);
101
+ /**
102
+ * The verdict maps the cost answer's three outcomes onto the three an agent
103
+ * can act on. `cannot-tell` stays `cannot-tell` rather than defaulting to
104
+ * yes: a guard that permits whatever it cannot judge is a guard that permits
105
+ * everything the moment its inputs go missing.
106
+ */
107
+ const verdict = cost.verdict === 'cannot-tell' ? 'cannot-tell' : cost.verdict === 'over' ? 'no' : 'yes';
108
+ return {
109
+ schemaVersion: 1,
110
+ verdict,
111
+ cost,
112
+ alternatives,
113
+ because: reasonFor(verdict, cost, alternatives),
114
+ };
115
+ }
116
+ function reasonFor(verdict, cost, alternatives) {
117
+ if (verdict === 'cannot-tell') {
118
+ return cost.reason === 'no-budget-configured'
119
+ ? 'No budget is configured, so there is nothing to judge this against.'
120
+ : cost.reason === 'nothing-measured'
121
+ ? 'Nothing has been measured yet, so how much of the budget is gone is unknown.'
122
+ : 'This model is not in the price catalogue, so the call cannot be priced.';
123
+ }
124
+ if (verdict === 'no') {
125
+ const lead = cost.restsOn === 'measured'
126
+ ? 'The budget is already spent, measured.'
127
+ : 'This call would take the budget past its limit, on an estimate of the call.';
128
+ return alternatives.length === 0
129
+ ? `${lead} No cheaper way to make this call exists in the catalogue.`
130
+ : `${lead} The cheapest alternative below saves the most.`;
131
+ }
132
+ return alternatives.length === 0
133
+ ? 'Within budget, and no cheaper way to make this call exists in the catalogue.'
134
+ : 'Within budget — and there is still a cheaper way to make this call.';
135
+ }
136
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -17,6 +17,10 @@ export { buildPlan, planLabelName } from './plan.js';
17
17
  export type { PlanAction, PlanActionKind, PlanAssumption, PlanDocument } from './plan.js';
18
18
  export { verifyPlan } from './verify.js';
19
19
  export { buildHistory, storedReportFrom, MIN_RUN } from './history.js';
20
+ export { answerCost } from './answer.js';
21
+ export { guardSpend } from './guard.js';
22
+ export type { GuardAlternative, GuardAnswer, GuardRequest, GuardVerdict } from './guard.js';
23
+ export type { AnswerRequest, AnswerVerdict, BudgetPosition, CallEstimate, CannotTellReasonAnswer, CostAnswer, } from './answer.js';
20
24
  export { evaluateWatch, firedKey, COVERAGE_FLOOR, DAY_MS } from './watch.js';
21
25
  export type { NotJudgeable, WatchAbstention, WatchCrossing, WatchGate, WatchOptions, WatchResult, WatchThresholds, } from './watch.js';
22
26
  export { STORE_SCHEMA_VERSION, identityOf, resolveStore, recordsFromBuckets, bucketsFromRecords, storeInventory, pruneRecords, } from './store.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -21,6 +21,8 @@ export { assignSources, fleetRollup } from './fleet.js';
21
21
  export { buildPlan, planLabelName } from './plan.js';
22
22
  export { verifyPlan } from './verify.js';
23
23
  export { buildHistory, storedReportFrom, MIN_RUN } from './history.js';
24
+ export { answerCost } from './answer.js';
25
+ export { guardSpend } from './guard.js';
24
26
  export { evaluateWatch, firedKey, COVERAGE_FLOOR, DAY_MS } from './watch.js';
25
27
  export { STORE_SCHEMA_VERSION, identityOf, resolveStore, recordsFromBuckets, bucketsFromRecords, storeInventory, pruneRecords, } from './store.js';
26
28
  export { CONNECTORS, connectorFor, normalizeAnthropicUsage, normalizeOpenAIUsage, bucketedProfile, bucketedCacheEconomics, } from './connector.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trazum/core",
3
- "version": "1.43.0",
3
+ "version": "1.45.0",
4
4
  "description": "Trazum core: priced advisories for LLM prompts (caching, model tier, batching, schemas), plus deterministic trimming, token counting and pricing.",
5
5
  "license": "MIT",
6
6
  "author": "David Mu\u00f1oz Rey",
package/src/answer.ts ADDED
@@ -0,0 +1,226 @@
1
+ /**
2
+ * "What will this call cost, and is there budget?" — answered before it is
3
+ * sent.
4
+ *
5
+ * Everything Trazum knows sits behind a process launch, a config walk and a
6
+ * log parse. That is fine for a report and useless for a decision being made
7
+ * right now: by the time a report exists, the call has been paid for.
8
+ *
9
+ * **This is where the temptation to merge halves is strongest**, which is why
10
+ * the shape below refuses to. The budget consumed is *measured* — it comes
11
+ * from the provider's own billed counts. The cost of the call being asked
12
+ * about is *estimated* — nobody has sent it yet, and the token count is a
13
+ * heuristic. A single "you have $38 left after this" would be a number that is
14
+ * neither, handed to a caller with no way to tell.
15
+ *
16
+ * So the answer carries both halves separately, and the composed figure — which
17
+ * callers genuinely need — arrives with its two halves broken out beside it.
18
+ * The verdict names what it rests on: `measured` when the budget is already
19
+ * blown without help from any estimate, and `measured+estimated` when it takes
20
+ * the described call to cross. A caller reading only the verdict still cannot
21
+ * mistake one for the other.
22
+ */
23
+
24
+ import { effectivePricing } from './pricing.js';
25
+ import type { PricingCatalogue } from './pricing.js';
26
+
27
+ export type AnswerVerdict = 'within' | 'over' | 'cannot-tell';
28
+
29
+ /** Why the question cannot be answered, when it cannot. */
30
+ export type CannotTellReasonAnswer =
31
+ /** No budget is configured, so "is there budget left" has no subject. */
32
+ | 'no-budget-configured'
33
+ /** Nothing has been measured, so the consumed half is unknown. */
34
+ | 'nothing-measured'
35
+ /** The model is not in the catalogue, so the call cannot be priced. */
36
+ | 'model-unpriced';
37
+
38
+ export interface CallEstimate {
39
+ model: string;
40
+ inputTokens: number;
41
+ outputTokens: number;
42
+ estimatedUsd: number;
43
+ /** Always `estimated`: this call has not happened. */
44
+ provenance: 'estimated';
45
+ /**
46
+ * What the estimate rests on, so a caller can weigh it.
47
+ *
48
+ * `token-count` means the caller handed over counts it had already made;
49
+ * `heuristic` means Trazum counted the text itself, with the ±10% band the
50
+ * estimator has published since 1.9.
51
+ */
52
+ basis: 'token-count' | 'heuristic';
53
+ }
54
+
55
+ export interface BudgetPosition {
56
+ limitUsd: number;
57
+ /** Spent so far, from the provider's own billed counts. */
58
+ consumedUsd: number;
59
+ remainingUsd: number;
60
+ /** Always `measured`: this is a bill, not a projection. */
61
+ provenance: 'measured';
62
+ /** The period the consumed figure covers. */
63
+ window: { fromMs: number; toMs: number } | null;
64
+ }
65
+
66
+ export interface CostAnswer {
67
+ schemaVersion: 1;
68
+ /** The call the caller described, priced. Null when none was described. */
69
+ call: CallEstimate | null;
70
+ /** Where the budget stands. Null when there is no budget or nothing measured. */
71
+ budget: BudgetPosition | null;
72
+ verdict: AnswerVerdict;
73
+ /**
74
+ * What the verdict rests on — the field that keeps this honest.
75
+ *
76
+ * `measured` means the budget is already past its limit and the estimate
77
+ * played no part. `measured+estimated` means it takes the described call to
78
+ * cross, so the verdict is only as good as the token count behind it. A
79
+ * caller that reads nothing else can still tell those apart.
80
+ */
81
+ restsOn: 'measured' | 'measured+estimated' | null;
82
+ reason: CannotTellReasonAnswer | null;
83
+ /**
84
+ * Where the budget would stand after this call — the figure callers actually
85
+ * want, with its halves kept visible so the composition cannot be mistaken
86
+ * for a measurement.
87
+ */
88
+ afterCall: {
89
+ usd: number;
90
+ halves: { measuredUsd: number; estimatedUsd: number };
91
+ } | null;
92
+ }
93
+
94
+ export interface AnswerRequest {
95
+ model?: string;
96
+ inputTokens?: number;
97
+ outputTokens?: number;
98
+ /** Measured spend so far, when there is any. */
99
+ consumedUsd?: number;
100
+ limitUsd?: number;
101
+ window?: { fromMs: number; toMs: number } | null;
102
+ /** How the token counts were arrived at. */
103
+ basis?: 'token-count' | 'heuristic';
104
+ }
105
+
106
+ /**
107
+ * Answers the two questions, from figures the caller already holds.
108
+ *
109
+ * Pure and synchronous on purpose: the whole point of this release is an
110
+ * answer in single-digit milliseconds, and a function that reads a file cannot
111
+ * promise that. The server hands it a store total it read once and keeps.
112
+ */
113
+ export function answerCost(
114
+ request: AnswerRequest,
115
+ options: { catalogue: PricingCatalogue; on?: Date },
116
+ ): CostAnswer {
117
+ const { catalogue, on = new Date() } = options;
118
+
119
+ let call: CallEstimate | null = null;
120
+ let unpriced = false;
121
+ if (request.model !== undefined) {
122
+ const model = catalogue.byId.get(request.model);
123
+ if (model === undefined) {
124
+ unpriced = true;
125
+ } else {
126
+ const { inputPerMTok, outputPerMTok } = effectivePricing(model, on);
127
+ const inputTokens = request.inputTokens ?? 0;
128
+ const outputTokens = request.outputTokens ?? 0;
129
+ call = {
130
+ model: request.model,
131
+ inputTokens,
132
+ outputTokens,
133
+ estimatedUsd:
134
+ (inputTokens / 1_000_000) * inputPerMTok + (outputTokens / 1_000_000) * outputPerMTok,
135
+ provenance: 'estimated',
136
+ basis: request.basis ?? 'token-count',
137
+ };
138
+ }
139
+ }
140
+
141
+ const hasBudget = request.limitUsd !== undefined;
142
+ const hasMeasurement = request.consumedUsd !== undefined;
143
+
144
+ let budget: BudgetPosition | null = null;
145
+ if (hasBudget && hasMeasurement) {
146
+ budget = {
147
+ limitUsd: request.limitUsd!,
148
+ consumedUsd: request.consumedUsd!,
149
+ remainingUsd: request.limitUsd! - request.consumedUsd!,
150
+ provenance: 'measured',
151
+ window: request.window ?? null,
152
+ };
153
+ }
154
+
155
+ /**
156
+ * The order of these refusals matters. A missing budget and a missing
157
+ * measurement are different problems with different fixes — configure a
158
+ * limit, or connect a source — and collapsing them into one message sends
159
+ * half the readers to the wrong place.
160
+ */
161
+ if (budget === null) {
162
+ return {
163
+ schemaVersion: 1,
164
+ call,
165
+ budget: null,
166
+ verdict: 'cannot-tell',
167
+ restsOn: null,
168
+ reason: !hasBudget ? 'no-budget-configured' : 'nothing-measured',
169
+ afterCall: null,
170
+ };
171
+ }
172
+
173
+ if (unpriced) {
174
+ // The budget half is known and the call half is not. Answering "within"
175
+ // would be answering a question nobody asked: whether the *current* spend
176
+ // fits, rather than whether this call does.
177
+ return {
178
+ schemaVersion: 1,
179
+ call: null,
180
+ budget,
181
+ verdict: 'cannot-tell',
182
+ restsOn: null,
183
+ reason: 'model-unpriced',
184
+ afterCall: null,
185
+ };
186
+ }
187
+
188
+ const estimatedUsd = call?.estimatedUsd ?? 0;
189
+ const afterUsd = budget.consumedUsd + estimatedUsd;
190
+
191
+ /**
192
+ * Already over without any help from the estimate: the verdict is a
193
+ * measurement, and saying so lets a caller act on it with full confidence
194
+ * rather than wondering how good the token count was.
195
+ */
196
+ if (budget.consumedUsd > budget.limitUsd) {
197
+ return {
198
+ schemaVersion: 1,
199
+ call,
200
+ budget,
201
+ verdict: 'over',
202
+ restsOn: 'measured',
203
+ reason: null,
204
+ afterCall: {
205
+ usd: afterUsd,
206
+ halves: { measuredUsd: budget.consumedUsd, estimatedUsd },
207
+ },
208
+ };
209
+ }
210
+
211
+ return {
212
+ schemaVersion: 1,
213
+ call,
214
+ budget,
215
+ verdict: afterUsd > budget.limitUsd ? 'over' : 'within',
216
+ // It takes the described call to cross, so the verdict is only as good as
217
+ // the token count behind it — and it says so rather than implying a
218
+ // measurement.
219
+ restsOn: 'measured+estimated',
220
+ reason: null,
221
+ afterCall: {
222
+ usd: afterUsd,
223
+ halves: { measuredUsd: budget.consumedUsd, estimatedUsd },
224
+ },
225
+ };
226
+ }
package/src/guard.ts ADDED
@@ -0,0 +1,204 @@
1
+ /**
2
+ * The thing spending the money can finally ask, and be told no.
3
+ *
4
+ * 1.44 gave an endpoint that *answers*. An agent may consult it and ignore it,
5
+ * which is fine — advice an implementation can skip is still advice worth
6
+ * having. What was missing is the shape of a refusal an agent can act on.
7
+ *
8
+ * **A guard that only says no teaches a caller to stop asking.** A model told
9
+ * "denied" with no alternative has exactly two moves: send it anyway, or fail
10
+ * the user's request. Both are worse than the call it wanted to make. So every
11
+ * refusal here arrives with the levers that exist — this work routes to a
12
+ * cheaper model that still fits, a batch window would halve it — each with
13
+ * what it is worth *for this call*, and the assumption it rests on.
14
+ *
15
+ * **The guard never spends to answer.** No provider call, no LLM pass, no
16
+ * pull. The answer comes from the store and the catalogue, or it says it
17
+ * cannot tell. A cost guard that costs money to consult is a joke with a bill
18
+ * attached.
19
+ *
20
+ * **An alternative the prompt does not fit in is not an alternative.** A
21
+ * cheaper model with a smaller context window does not make this call cheaper;
22
+ * it makes it impossible. Those are filtered out here rather than offered and
23
+ * blamed later.
24
+ */
25
+
26
+ import { answerCost } from './answer.js';
27
+ import type { AnswerRequest, CostAnswer } from './answer.js';
28
+ import { effectivePricing, multipliersFor } from './pricing.js';
29
+ import type { PricingCatalogue } from './pricing.js';
30
+ import type { ModelPricing } from './types.js';
31
+ import type { PlanAssumption } from './plan.js';
32
+
33
+ export type GuardVerdict = 'yes' | 'no' | 'cannot-tell';
34
+
35
+ export interface GuardAlternative {
36
+ kind: 'route' | 'batch' | 'route+batch';
37
+ /** The model this moves to, when it moves. */
38
+ model: { id: string; displayName: string } | null;
39
+ /**
40
+ * What this alternative saves **on this call** — not per month.
41
+ *
42
+ * The caller is deciding one call, right now. A monthly figure would be the
43
+ * right number at the wrong moment, and an agent has no way to act on it.
44
+ */
45
+ savingUsd: number;
46
+ /** What the log cannot confirm, typed as everywhere since 1.38. */
47
+ assumes: PlanAssumption[];
48
+ /**
49
+ * Whether the described call fits this alternative's context window.
50
+ *
51
+ * Only `true` ever reaches a caller — the false ones are dropped before
52
+ * they are offered. The field exists so the rule is visible in the type
53
+ * rather than buried in a filter nobody reads.
54
+ */
55
+ fits: true;
56
+ }
57
+
58
+ export interface GuardAnswer {
59
+ schemaVersion: 1;
60
+ verdict: GuardVerdict;
61
+ /** The full cost answer, halves and provenance intact. */
62
+ cost: CostAnswer;
63
+ /**
64
+ * What to do instead, dearest saving first. Present on a refusal, and on a
65
+ * yes as well: an agent that can spend less while still being allowed to
66
+ * spend should be told so.
67
+ */
68
+ alternatives: GuardAlternative[];
69
+ /**
70
+ * A one-line reason a human will read in a log. The fields above are what a
71
+ * machine acts on; this is never the only place a fact appears.
72
+ */
73
+ because: string;
74
+ }
75
+
76
+ /** Models cheaper than this one, in the same family, that the prompt fits in. */
77
+ function cheaperThan(
78
+ model: ModelPricing,
79
+ catalogue: PricingCatalogue,
80
+ inputTokens: number,
81
+ on: Date,
82
+ ): ModelPricing[] {
83
+ const here = effectivePricing(model, on);
84
+ return [...catalogue.byId.values()]
85
+ .filter((candidate) => {
86
+ if (candidate.id === model.id) return false;
87
+ if (candidate.provider !== model.provider) return false;
88
+ const there = effectivePricing(candidate, on);
89
+ if (there.inputPerMTok >= here.inputPerMTok) return false;
90
+ // A model the prompt does not fit in is not a cheaper way to make this
91
+ // call; it is a way not to make it.
92
+ return candidate.contextWindow >= inputTokens;
93
+ })
94
+ .sort((a, b) => effectivePricing(b, on).inputPerMTok - effectivePricing(a, on).inputPerMTok);
95
+ }
96
+
97
+ export interface GuardRequest extends AnswerRequest {
98
+ /** Whether the caller says this work can wait for a batch window. */
99
+ batchEligible?: boolean;
100
+ }
101
+
102
+ export function guardSpend(
103
+ request: GuardRequest,
104
+ options: { catalogue: PricingCatalogue; on?: Date },
105
+ ): GuardAnswer {
106
+ const { catalogue, on = new Date() } = options;
107
+ const cost = answerCost(request, { catalogue, on });
108
+
109
+ const alternatives: GuardAlternative[] = [];
110
+ const model = request.model === undefined ? undefined : catalogue.byId.get(request.model);
111
+
112
+ if (model !== undefined && cost.call !== null) {
113
+ const inputTokens = cost.call.inputTokens;
114
+ const outputTokens = cost.call.outputTokens;
115
+ const here = effectivePricing(model, on);
116
+ const priceOf = (candidate: ModelPricing): number => {
117
+ const rates = effectivePricing(candidate, on);
118
+ return (inputTokens / 1_000_000) * rates.inputPerMTok + (outputTokens / 1_000_000) * rates.outputPerMTok;
119
+ };
120
+ const mine = (inputTokens / 1_000_000) * here.inputPerMTok + (outputTokens / 1_000_000) * here.outputPerMTok;
121
+ const batchRate = multipliersFor(model).batch;
122
+
123
+ for (const candidate of cheaperThan(model, catalogue, inputTokens, on)) {
124
+ const routed = priceOf(candidate);
125
+ const candidateBatch = multipliersFor(candidate).batch;
126
+ const both = candidateBatch === null ? null : routed * candidateBatch;
127
+ /**
128
+ * Route and batch on the same call combine the way `billLevers` has
129
+ * combined them since 1.23: the batch discount applies to the *cheaper*
130
+ * model's price, never as a second subtraction from this one. Adding the
131
+ * two savings is the arithmetic `plan` exists to kill.
132
+ */
133
+ if (request.batchEligible === true && both !== null) {
134
+ alternatives.push({
135
+ kind: 'route+batch',
136
+ model: { id: candidate.id, displayName: candidate.displayName },
137
+ savingUsd: mine - both,
138
+ assumes: [
139
+ { kind: 'model-capability', model: candidate.displayName },
140
+ { kind: 'batch-window' },
141
+ ],
142
+ fits: true,
143
+ });
144
+ }
145
+ alternatives.push({
146
+ kind: 'route',
147
+ model: { id: candidate.id, displayName: candidate.displayName },
148
+ savingUsd: mine - routed,
149
+ assumes: [{ kind: 'model-capability', model: candidate.displayName }],
150
+ fits: true,
151
+ });
152
+ }
153
+
154
+ if (request.batchEligible === true && batchRate !== null) {
155
+ alternatives.push({
156
+ kind: 'batch',
157
+ model: null,
158
+ savingUsd: mine - mine * batchRate,
159
+ assumes: [{ kind: 'batch-window' }],
160
+ fits: true,
161
+ });
162
+ }
163
+ }
164
+
165
+ alternatives.sort((a, b) => b.savingUsd - a.savingUsd);
166
+
167
+ /**
168
+ * The verdict maps the cost answer's three outcomes onto the three an agent
169
+ * can act on. `cannot-tell` stays `cannot-tell` rather than defaulting to
170
+ * yes: a guard that permits whatever it cannot judge is a guard that permits
171
+ * everything the moment its inputs go missing.
172
+ */
173
+ const verdict: GuardVerdict =
174
+ cost.verdict === 'cannot-tell' ? 'cannot-tell' : cost.verdict === 'over' ? 'no' : 'yes';
175
+
176
+ return {
177
+ schemaVersion: 1,
178
+ verdict,
179
+ cost,
180
+ alternatives,
181
+ because: reasonFor(verdict, cost, alternatives),
182
+ };
183
+ }
184
+
185
+ function reasonFor(verdict: GuardVerdict, cost: CostAnswer, alternatives: GuardAlternative[]): string {
186
+ if (verdict === 'cannot-tell') {
187
+ return cost.reason === 'no-budget-configured'
188
+ ? 'No budget is configured, so there is nothing to judge this against.'
189
+ : cost.reason === 'nothing-measured'
190
+ ? 'Nothing has been measured yet, so how much of the budget is gone is unknown.'
191
+ : 'This model is not in the price catalogue, so the call cannot be priced.';
192
+ }
193
+ if (verdict === 'no') {
194
+ const lead = cost.restsOn === 'measured'
195
+ ? 'The budget is already spent, measured.'
196
+ : 'This call would take the budget past its limit, on an estimate of the call.';
197
+ return alternatives.length === 0
198
+ ? `${lead} No cheaper way to make this call exists in the catalogue.`
199
+ : `${lead} The cheapest alternative below saves the most.`;
200
+ }
201
+ return alternatives.length === 0
202
+ ? 'Within budget, and no cheaper way to make this call exists in the catalogue.'
203
+ : 'Within budget — and there is still a cheaper way to make this call.';
204
+ }
package/src/index.ts CHANGED
@@ -48,6 +48,17 @@ export { buildPlan, planLabelName } from './plan.js';
48
48
  export type { PlanAction, PlanActionKind, PlanAssumption, PlanDocument } from './plan.js';
49
49
  export { verifyPlan } from './verify.js';
50
50
  export { buildHistory, storedReportFrom, MIN_RUN } from './history.js';
51
+ export { answerCost } from './answer.js';
52
+ export { guardSpend } from './guard.js';
53
+ export type { GuardAlternative, GuardAnswer, GuardRequest, GuardVerdict } from './guard.js';
54
+ export type {
55
+ AnswerRequest,
56
+ AnswerVerdict,
57
+ BudgetPosition,
58
+ CallEstimate,
59
+ CannotTellReasonAnswer,
60
+ CostAnswer,
61
+ } from './answer.js';
51
62
  export { evaluateWatch, firedKey, COVERAGE_FLOOR, DAY_MS } from './watch.js';
52
63
  export type {
53
64
  NotJudgeable,