@phosphorco/bb-thread-metrics 0.1.0 → 0.2.1
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/CONTRACT.md +213 -1
- package/README.md +28 -1
- package/dist/bb.js +276 -15
- package/dist/index.js +648 -64
- package/dist/testing.js +83 -3
- package/dist/types/bb.d.ts +1490 -10
- package/dist/types/heat-settings.d.ts +413 -0
- package/dist/types/index.d.ts +31 -24
- package/dist/types/recent-session.d.ts +51 -0
- package/dist/types/recent.d.ts +6654 -0
- package/dist/types/testing.d.ts +10 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,28 +1,562 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/recent.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { UsageVectorSchema } from "@phosphorco/bb-usage-algebra/vector";
|
|
4
|
+
var RECENT_LIMITS = Object.freeze({ rows: 1000, bytes: 4 * 1024 * 1024, rowBytes: 8 * 1024, inputBytes: 1024, providers: 32, windowMs: 3 * 24 * 60 * 60 * 1000 });
|
|
5
|
+
var id = z.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
6
|
+
var revision = z.string().min(1).max(256);
|
|
7
|
+
var time = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
8
|
+
var coverage = z.enum(["complete", "partial", "unknown"]);
|
|
9
|
+
var rank = z.number().finite().min(0).max(1).nullable();
|
|
10
|
+
var count = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
11
|
+
var windowShape = (value) => z.object({ m20: value, h24: value, d7: value, lifetime: value }).strip();
|
|
12
|
+
var reason = z.string().min(1).max(64);
|
|
13
|
+
var reasons = z.array(reason).max(16);
|
|
14
|
+
var recentDollarSchema = z.discriminatedUnion("kind", [
|
|
15
|
+
z.object({ kind: z.literal("estimate"), amount: z.number().finite().nonnegative() }).strip(),
|
|
16
|
+
z.object({ kind: z.literal("floor"), amount: z.number().finite().nonnegative() }).strip(),
|
|
17
|
+
z.object({ kind: z.literal("unavailable"), amount: z.null() }).strip()
|
|
18
|
+
]);
|
|
19
|
+
var recentWindowsSchema = windowShape(recentDollarSchema);
|
|
20
|
+
var flags = windowShape(z.boolean());
|
|
21
|
+
var recentAttributionSchema = z.object({ unknownProvider: recentWindowsSchema, attributionComplete: flags, unpriced: flags }).strip();
|
|
22
|
+
var recentPresentationSchema = z.object({ title: z.string().trim().min(1).max(512).nullable(), navigation: z.discriminatedUnion("kind", [
|
|
23
|
+
z.object({ kind: z.literal("personal") }).strip(),
|
|
24
|
+
z.object({ kind: z.literal("project"), projectId: id }).strip()
|
|
25
|
+
]).nullable() }).strip();
|
|
26
|
+
var recentApproximationSchema = z.object({ approximate: z.boolean(), reasons: z.array(z.enum(["closest-model", "provider-default", "model-attribution", "bucket-boundary", "missing-price", "source-gap"])).max(6) }).strip().superRefine((v, c) => {
|
|
27
|
+
if (!v.approximate && v.reasons.length)
|
|
28
|
+
c.addIssue({ code: "custom", message: "Approximation reasons require approximate flag" });
|
|
29
|
+
});
|
|
30
|
+
var provider = z.object({ providerId: id, windows: recentWindowsSchema, approximation: recentApproximationSchema }).strip();
|
|
31
|
+
var recentTokenValueSchema = z.discriminatedUnion("kind", [
|
|
32
|
+
z.object({ kind: z.literal("estimate"), value: UsageVectorSchema }).strip(),
|
|
33
|
+
z.object({ kind: z.literal("floor"), value: UsageVectorSchema }).strip(),
|
|
34
|
+
z.object({ kind: z.literal("unavailable"), value: z.null() }).strip()
|
|
35
|
+
]);
|
|
36
|
+
var recentTokenWindowsSchema = windowShape(recentTokenValueSchema);
|
|
37
|
+
var recentUsageSchema = z.object({
|
|
38
|
+
windows: recentTokenWindowsSchema,
|
|
39
|
+
providers: z.array(z.object({ providerId: id, windows: recentTokenWindowsSchema }).strip()).max(RECENT_LIMITS.providers),
|
|
40
|
+
omittedProviders: count,
|
|
41
|
+
descendants: z.object({ count, exact: z.boolean() }).strip()
|
|
42
|
+
}).strip();
|
|
43
|
+
var recentScopeSchema = z.object({
|
|
44
|
+
memberCount: count,
|
|
45
|
+
coverage,
|
|
46
|
+
coverageReasons: reasons,
|
|
47
|
+
priceVersion: revision,
|
|
48
|
+
approximation: recentApproximationSchema,
|
|
49
|
+
usage: recentUsageSchema.optional(),
|
|
50
|
+
attribution: recentAttributionSchema.optional(),
|
|
51
|
+
windows: recentWindowsSchema,
|
|
52
|
+
scores: recentWindowsSchema,
|
|
53
|
+
providers: z.array(provider).max(RECENT_LIMITS.providers),
|
|
54
|
+
omittedProviders: count,
|
|
55
|
+
dollarRanks: z.object({ m20: rank, h24: rank, d7: rank }).strip(),
|
|
56
|
+
scoreRanks: z.object({ m20: rank, h24: rank, d7: rank }).strip()
|
|
57
|
+
}).strip().superRefine((v, c) => {
|
|
58
|
+
if (new Set(v.providers.map((x) => x.providerId)).size !== v.providers.length)
|
|
59
|
+
c.addIssue({ code: "custom", message: "Duplicate providers" });
|
|
60
|
+
if (v.omittedProviders && v.coverage === "complete")
|
|
61
|
+
c.addIssue({ code: "custom", message: "Omitted providers require partial coverage" });
|
|
62
|
+
if (v.usage) {
|
|
63
|
+
const u = v.usage, fail = (message) => c.addIssue({ code: "custom", path: ["usage"], message });
|
|
64
|
+
const providers = new Set(v.providers.map((p) => p.providerId));
|
|
65
|
+
if (u.providers.length !== providers.size || new Set(u.providers.map((p) => p.providerId)).size !== u.providers.length || u.providers.some((p) => !providers.has(p.providerId)) || u.omittedProviders !== v.omittedProviders)
|
|
66
|
+
fail("Token provider attribution mismatch");
|
|
67
|
+
if (u.descendants.count !== Math.max(0, v.memberCount - 1))
|
|
68
|
+
fail("Descendant/member count mismatch");
|
|
69
|
+
if (u.descendants.exact && v.coverageReasons.some((reason2) => ["member-cap", "depth-cap", "edge-cap"].includes(reason2)))
|
|
70
|
+
fail("Membership caps cannot carry an exact descendant count");
|
|
71
|
+
const near = (a, b) => Math.abs(a - b) <= 4 * Number.EPSILON * Math.max(a, b);
|
|
72
|
+
for (const w of ["m20", "h24", "d7", "lifetime"]) {
|
|
73
|
+
const total = u.windows[w], known = u.providers.filter((p) => p.windows[w].value !== null), all = !u.omittedProviders && known.length === u.providers.length;
|
|
74
|
+
if (total.kind === "estimate" && (u.omittedProviders > 0 || u.providers.some((p) => p.windows[w].kind !== "estimate")))
|
|
75
|
+
fail("Incomplete provider tokens cannot carry an aggregate estimate");
|
|
76
|
+
for (const k of ["inputUncached", "cacheRead", "cacheWrite", "output"]) {
|
|
77
|
+
const sum = known.reduce((n, p) => n + p.windows[w].value[k], 0), value = total.value?.[k];
|
|
78
|
+
if (!Number.isFinite(sum) || value === undefined && sum > 0 || value !== undefined && (sum > value && !near(sum, value) || all && !near(sum, value)))
|
|
79
|
+
fail("Token provider conservation mismatch");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
var recentSnapshotInputSchema = z.object({ ifRevision: revision.optional() }).strict();
|
|
85
|
+
var thresholds = windowShape(z.number().finite().min(0).max(1e6));
|
|
86
|
+
var weights = z.record(id, z.number().finite().min(0).max(3)).refine((v) => Object.keys(v).length <= RECENT_LIMITS.providers, "Too many provider weights");
|
|
87
|
+
var configBase = { schemaVersion: z.literal(2), providerWeights: weights };
|
|
88
|
+
var scoringConfigV2Schema = z.discriminatedUnion("mode", [
|
|
89
|
+
z.object({ ...configBase, mode: z.literal("percentile"), gates: z.object({ m20: z.number().finite().min(0).max(1), h24: z.number().finite().min(0).max(1), d7: z.number().finite().min(0).max(1) }).strip() }).strip(),
|
|
90
|
+
z.object({ ...configBase, mode: z.literal("absolute"), thresholds, lifetimeEnabled: z.boolean().optional() }).strip()
|
|
91
|
+
]);
|
|
92
|
+
var DEFAULT_SCORING_CONFIG_V2 = Object.freeze({ schemaVersion: 2, mode: "percentile", providerWeights: Object.freeze({}), gates: Object.freeze({ m20: 0.9, h24: 0.9, d7: 0.9 }) });
|
|
93
|
+
var ROOT_FAMILY_HEAT_DOMAIN_V1 = Object.freeze({ version: 1, kind: "root-family" });
|
|
94
|
+
var rootFamilyHeatDomainV1Schema = z.object({ version: z.literal(1), kind: z.literal("root-family") }).strip();
|
|
95
|
+
var configV3Base = { schemaVersion: z.literal(3), domain: rootFamilyHeatDomainV1Schema, providerWeights: weights };
|
|
96
|
+
var scoringConfigV3Schema = z.discriminatedUnion("mode", [
|
|
97
|
+
z.object({ ...configV3Base, mode: z.literal("percentile"), gates: z.object({ m20: z.number().finite().min(0).max(1), h24: z.number().finite().min(0).max(1), d7: z.number().finite().min(0).max(1) }).strip() }).strip(),
|
|
98
|
+
z.object({ ...configV3Base, mode: z.literal("absolute"), thresholds, lifetimeEnabled: z.literal(false).optional() }).strip()
|
|
99
|
+
]);
|
|
100
|
+
var storedScoringConfigSchema = z.union([scoringConfigV2Schema, scoringConfigV3Schema]);
|
|
101
|
+
var DEFAULT_SCORING_CONFIG_V3 = Object.freeze({ schemaVersion: 3, domain: ROOT_FAMILY_HEAT_DOMAIN_V1, mode: "percentile", providerWeights: Object.freeze({}), gates: Object.freeze({ m20: 0.9, h24: 0.8, d7: 0.8 }) });
|
|
102
|
+
var HEAT_WINDOWS = ["m20", "h24", "d7"];
|
|
103
|
+
var rankWindows = z.object({ m20: rank, h24: rank, d7: rank }).strip();
|
|
104
|
+
var rootTopologyV1Schema = z.discriminatedUnion("kind", [
|
|
105
|
+
z.object({ kind: z.literal("attested"), parentThreadId: id.nullable(), lifecycleOwnerThreadId: id.nullable(), deleted: z.boolean() }).strip(),
|
|
106
|
+
z.object({ kind: z.literal("unknown"), reasons }).strip()
|
|
107
|
+
]);
|
|
108
|
+
function rootFamilyEligibilityV1(input) {
|
|
109
|
+
const topology = rootTopologyV1Schema.parse(input);
|
|
110
|
+
if (topology.kind === "unknown")
|
|
111
|
+
return "unknown";
|
|
112
|
+
if (topology.deleted)
|
|
113
|
+
return "deleted";
|
|
114
|
+
return topology.parentThreadId === null && topology.lifecycleOwnerThreadId === null ? "eligible-root" : "ineligible-child";
|
|
115
|
+
}
|
|
116
|
+
var coveredPopulation = z.object({ count, coverage, reasons }).strip();
|
|
117
|
+
var rootPopulations = z.object({ m20: coveredPopulation, h24: coveredPopulation, d7: coveredPopulation }).strip();
|
|
118
|
+
var interval = z.object({ start: time, end: time }).strip();
|
|
119
|
+
var rootFamilyHeatContextV1Schema = z.object({
|
|
120
|
+
version: z.literal(1),
|
|
121
|
+
domain: rootFamilyHeatDomainV1Schema,
|
|
122
|
+
contextRevision: revision,
|
|
123
|
+
configRevision: revision,
|
|
124
|
+
topologyRevision: revision,
|
|
125
|
+
rankRevision: revision,
|
|
126
|
+
rankAsOf: time,
|
|
127
|
+
windows: z.object({ m20: interval, h24: interval, d7: interval }).strip(),
|
|
128
|
+
population: z.object({ dollars: rootPopulations, scores: rootPopulations }).strip()
|
|
129
|
+
}).strip().superRefine((v, c) => {
|
|
130
|
+
const duration = { m20: 20 * 60 * 1000, h24: 24 * 60 * 60 * 1000, d7: 7 * 24 * 60 * 60 * 1000 };
|
|
131
|
+
for (const w of HEAT_WINDOWS)
|
|
132
|
+
if (v.windows[w].end !== v.rankAsOf || v.windows[w].start !== Math.max(0, v.rankAsOf - duration[w]))
|
|
133
|
+
c.addIssue({ code: "custom", message: "Invalid root heat window provenance", path: ["windows", w] });
|
|
134
|
+
});
|
|
135
|
+
var decision = z.object({ decision: z.enum(["qualifies", "below", "suppressed", "unavailable"]), reason: z.enum(["threshold-met", "below-threshold", "gate-met", "below-gate", "not-root", "deleted", "topology-unknown", "unsupported-value", "zero", "insufficient-population", "rank-unavailable", "context-unavailable"]) }).strip();
|
|
136
|
+
var rootFamilyDecisionSetV1Schema = z.object({ contextRevision: revision, signals: z.object({ m20: decision, h24: decision, d7: decision }).strip(), winner: z.enum(["on-fire", "hot", "heavy"]).nullable() }).strip();
|
|
137
|
+
var recentRootHeatSchema = z.object({ topology: rootTopologyV1Schema, rootDollarRanks: rankWindows, rootScoreRanks: rankWindows, decisions: rootFamilyDecisionSetV1Schema }).strip();
|
|
138
|
+
function rootFamilyHeatV1(input) {
|
|
139
|
+
const context = rootFamilyHeatContextV1Schema.parse(input.context), config = scoringConfigV3Schema.parse(input.config), family = input.family;
|
|
140
|
+
if (family.configRevision !== context.configRevision || family.contextRevision !== context.contextRevision)
|
|
141
|
+
throw new Error("Root heat configuration/context mismatch");
|
|
142
|
+
const eligibility = rootFamilyEligibilityV1(input.topology), signals = {};
|
|
143
|
+
for (const w of HEAT_WINDOWS) {
|
|
144
|
+
const score = recentDollarSchema.parse(family.scores[w]), r = rank.parse(family.rootScoreRanks[w]), pop = context.population.scores[w];
|
|
145
|
+
if (r !== null && (pop.count === 0 || pop.count === 1 && r !== 0 || pop.coverage === "unknown"))
|
|
146
|
+
throw new Error("Root rank population mismatch");
|
|
147
|
+
if (eligibility !== "eligible-root") {
|
|
148
|
+
signals[w] = { decision: eligibility === "unknown" ? "unavailable" : "suppressed", reason: eligibility === "unknown" ? "topology-unknown" : eligibility === "deleted" ? "deleted" : "not-root" };
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (score.amount === null) {
|
|
152
|
+
signals[w] = { decision: "unavailable", reason: "unsupported-value" };
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (score.amount === 0) {
|
|
156
|
+
signals[w] = score.kind === "floor" ? { decision: "unavailable", reason: "unsupported-value" } : { decision: "suppressed", reason: "zero" };
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (config.mode === "absolute") {
|
|
160
|
+
const hit = score.amount >= config.thresholds[w];
|
|
161
|
+
signals[w] = !hit && score.kind === "floor" ? { decision: "unavailable", reason: "unsupported-value" } : { decision: hit ? "qualifies" : "below", reason: hit ? "threshold-met" : "below-threshold" };
|
|
162
|
+
} else if (pop.coverage === "unknown")
|
|
163
|
+
signals[w] = { decision: "unavailable", reason: "rank-unavailable" };
|
|
164
|
+
else if (pop.count < 2)
|
|
165
|
+
signals[w] = { decision: "suppressed", reason: "insufficient-population" };
|
|
166
|
+
else if (r === null)
|
|
167
|
+
signals[w] = { decision: "unavailable", reason: "rank-unavailable" };
|
|
168
|
+
else {
|
|
169
|
+
const hit = r >= config.gates[w];
|
|
170
|
+
signals[w] = { decision: hit ? "qualifies" : "below", reason: hit ? "gate-met" : "below-gate" };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const winner = signals.m20.decision === "qualifies" ? "on-fire" : signals.h24.decision === "qualifies" ? "hot" : signals.d7.decision === "qualifies" ? "heavy" : null;
|
|
174
|
+
return { contextRevision: context.contextRevision, signals, winner };
|
|
175
|
+
}
|
|
176
|
+
var recentRowSchema = z.object({ threadId: id, revision, configRevision: revision, asOf: time, presentation: recentPresentationSchema.optional(), ownLastFoldAt: time.nullable(), familyLastFoldAt: time.nullable(), own: recentScopeSchema, family: recentScopeSchema, heat: recentRootHeatSchema.optional() }).strip();
|
|
177
|
+
var recentRootInputSchema = z.object({ threadId: id, asOf: time, configRevision: revision, presentation: recentPresentationSchema.optional(), family: recentScopeSchema, heat: recentRootHeatSchema }).strip();
|
|
178
|
+
var recentHeatInputsSchema = z.object({ contextRevision: revision, topologyRevision: revision, cohortComplete: z.boolean(), pending: count, omitted: z.object({ count, exact: z.boolean() }).strip(), rows: z.array(recentRootInputSchema).max(RECENT_LIMITS.rows) }).strip();
|
|
179
|
+
function weightedScoreV2(values, providerWeights) {
|
|
180
|
+
if (values.length > RECENT_LIMITS.providers)
|
|
181
|
+
throw new Error("Too many score providers");
|
|
182
|
+
weights.parse(providerWeights);
|
|
183
|
+
let sum = 0, unknown = false;
|
|
184
|
+
const seen = new Set;
|
|
185
|
+
for (const v of values) {
|
|
186
|
+
id.parse(v.providerId);
|
|
187
|
+
if (seen.has(v.providerId))
|
|
188
|
+
throw new Error("Duplicate score provider");
|
|
189
|
+
seen.add(v.providerId);
|
|
190
|
+
if (v.amount === null) {
|
|
191
|
+
unknown = true;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (!Number.isFinite(v.amount) || v.amount < 0)
|
|
195
|
+
throw new Error("Invalid provider dollars");
|
|
196
|
+
const w = Object.hasOwn(providerWeights, v.providerId) ? providerWeights[v.providerId] : 1;
|
|
197
|
+
sum += w * v.amount;
|
|
198
|
+
if (!Number.isFinite(sum))
|
|
199
|
+
throw new Error("Weighted dollar overflow");
|
|
200
|
+
}
|
|
201
|
+
return unknown ? null : sum;
|
|
202
|
+
}
|
|
203
|
+
var population = z.object({ count, coverage }).strip();
|
|
204
|
+
var populations = z.object({ m20: population, h24: population, d7: population }).strip();
|
|
205
|
+
var scopedPopulations = z.object({ dollars: populations, scores: populations }).strip();
|
|
206
|
+
var recentPublicationIdentitySchema = z.object({ instance: z.string().min(1).max(128), sequence: z.number().int().positive().max(Number.MAX_SAFE_INTEGER) }).strip();
|
|
207
|
+
var recentSnapshotFullSchema = z.object({
|
|
208
|
+
version: z.literal(1),
|
|
209
|
+
revision,
|
|
210
|
+
publication: recentPublicationIdentitySchema.optional(),
|
|
211
|
+
unchanged: z.literal(false),
|
|
212
|
+
currency: z.literal("USD"),
|
|
213
|
+
estimated: z.literal(true),
|
|
214
|
+
cutoffLastFoldAt: time.nullable(),
|
|
215
|
+
asOf: time,
|
|
216
|
+
rankAsOf: time,
|
|
217
|
+
recentWindowMs: z.literal(RECENT_LIMITS.windowMs),
|
|
218
|
+
config: z.object({ revision, value: storedScoringConfigSchema }).strip(),
|
|
219
|
+
heat: rootFamilyHeatContextV1Schema.optional(),
|
|
220
|
+
heatInputs: recentHeatInputsSchema.optional(),
|
|
221
|
+
coverage,
|
|
222
|
+
coverageReasons: reasons,
|
|
223
|
+
materializationPending: count,
|
|
224
|
+
omittedRows: z.object({ count, exact: z.boolean() }).strip(),
|
|
225
|
+
population: z.object({ own: scopedPopulations, family: scopedPopulations }).strip(),
|
|
226
|
+
rows: z.array(recentRowSchema).max(RECENT_LIMITS.rows)
|
|
227
|
+
}).strip();
|
|
228
|
+
var recentSnapshotResultSchema = z.union([z.object({ version: z.literal(1), revision, unchanged: z.literal(true) }).strip(), recentSnapshotFullSchema]);
|
|
229
|
+
function validateRecentSnapshot(input, value) {
|
|
230
|
+
recentSnapshotInputSchema.parse(input);
|
|
231
|
+
const bytes = (v) => new TextEncoder().encode(JSON.stringify(v)).byteLength;
|
|
232
|
+
if (bytes(value) > RECENT_LIMITS.bytes)
|
|
233
|
+
throw new Error("Snapshot byte limit");
|
|
234
|
+
const raw = value;
|
|
235
|
+
let entries = 0;
|
|
236
|
+
for (const rows of [raw?.rows, raw?.heatInputs?.rows])
|
|
237
|
+
if (rows !== undefined) {
|
|
238
|
+
if (!Array.isArray(rows) || (entries += rows.length) > RECENT_LIMITS.rows)
|
|
239
|
+
throw new Error("Snapshot aggregate row limit");
|
|
240
|
+
for (const row of rows)
|
|
241
|
+
if (bytes(row) > RECENT_LIMITS.rowBytes)
|
|
242
|
+
throw new Error("Snapshot row byte limit");
|
|
243
|
+
}
|
|
244
|
+
if (value && typeof value === "object" && value.unchanged === true) {
|
|
245
|
+
for (const field of Object.keys(recentSnapshotFullSchema.shape))
|
|
246
|
+
if (!["version", "revision", "unchanged"].includes(field) && Object.hasOwn(value, field))
|
|
247
|
+
throw new Error("Unchanged snapshot carries recognized full content");
|
|
248
|
+
}
|
|
249
|
+
const out = recentSnapshotResultSchema.parse(value);
|
|
250
|
+
if (out.unchanged) {
|
|
251
|
+
if (input.ifRevision !== out.revision)
|
|
252
|
+
throw new Error("Unmatched snapshot revision");
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
if (out.config.value.schemaVersion === 3 && !out.heat)
|
|
256
|
+
throw new Error("Root-family configuration requires heat context");
|
|
257
|
+
if (out.heat && (out.config.value.schemaVersion !== 3 || out.heat.configRevision !== out.config.revision || out.heat.rankAsOf !== out.rankAsOf))
|
|
258
|
+
throw new Error("Root heat configuration/rank epoch mismatch");
|
|
259
|
+
if (out.rankAsOf > out.asOf)
|
|
260
|
+
throw new Error("Future rank time");
|
|
261
|
+
if ((out.materializationPending || out.omittedRows.count || !out.omittedRows.exact) && out.coverage === "complete")
|
|
262
|
+
throw new Error("Pending/capped snapshot must disclose coverage");
|
|
263
|
+
let previousActivity = Infinity, previousId = "";
|
|
264
|
+
const ids = new Set;
|
|
265
|
+
for (const row of out.rows) {
|
|
266
|
+
if (ids.has(row.threadId))
|
|
267
|
+
throw new Error("Duplicate snapshot thread");
|
|
268
|
+
ids.add(row.threadId);
|
|
269
|
+
if (row.asOf > out.asOf)
|
|
270
|
+
throw new Error("Future row time");
|
|
271
|
+
if (row.configRevision !== out.config.revision)
|
|
272
|
+
throw new Error("Row score configuration mismatch");
|
|
273
|
+
if (row.ownLastFoldAt === null && row.familyLastFoldAt === null)
|
|
274
|
+
throw new Error("Missing fold activity timestamp");
|
|
275
|
+
const activity = Math.max(row.ownLastFoldAt ?? 0, row.familyLastFoldAt ?? 0);
|
|
276
|
+
if (activity > out.asOf || activity < Math.max(0, out.asOf - out.recentWindowMs))
|
|
277
|
+
throw new Error("Row outside recent activity window");
|
|
278
|
+
if (activity > previousActivity || activity === previousActivity && row.threadId < previousId)
|
|
279
|
+
throw new Error("Rows must be newest first with ID tie-break");
|
|
280
|
+
previousActivity = activity;
|
|
281
|
+
previousId = row.threadId;
|
|
282
|
+
for (const scope of ["own", "family"]) {
|
|
283
|
+
validateProjection(row[scope], out.config.value.providerWeights);
|
|
284
|
+
validateRanks(row[scope], out.population[scope], out.rankAsOf >= row.asOf);
|
|
285
|
+
}
|
|
286
|
+
validateRootHeatRow(row, out.heat, out.config.value);
|
|
287
|
+
}
|
|
288
|
+
if (out.heat) {
|
|
289
|
+
for (const [axis, field] of [["dollars", "windows"], ["scores", "scores"]]) {
|
|
290
|
+
for (const w of HEAT_WINDOWS) {
|
|
291
|
+
const pop = out.heat.population[axis][w], supported = out.rows.filter((row) => row.heat && rootFamilyEligibilityV1(row.heat.topology) === "eligible-root" && row.asOf <= out.heat.rankAsOf && (row.family[field][w].amount ?? 0) > 0).length;
|
|
292
|
+
if (pop.coverage === "complete" && pop.count < supported)
|
|
293
|
+
throw new Error("Root population below supported displayed members");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (out.heatInputs)
|
|
298
|
+
validateHeatInputs(out);
|
|
299
|
+
if (out.omittedRows.count && out.rows.length && out.cutoffLastFoldAt !== previousActivity)
|
|
300
|
+
throw new Error("Missing eviction cutoff");
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
function validateProjection(projection, providerWeights) {
|
|
304
|
+
if (projection.coverageReasons.includes("unpriced") && !projection.approximation.reasons.includes("missing-price"))
|
|
305
|
+
throw new Error("Unpriced coverage requires missing-price provenance");
|
|
306
|
+
const observationGap = projection.coverage !== "complete" && !projection.coverageReasons.includes("unpriced") && projection.approximation.reasons.includes("source-gap") && projection.coverageReasons.some((r) => ["source-gap", "root-unattested", "history-unavailable", "missing-thread"].includes(r));
|
|
307
|
+
const unavailableProvenance = (approximation) => approximation.reasons.includes("missing-price") || observationGap && approximation.reasons.includes("source-gap");
|
|
308
|
+
for (const p of projection.providers) {
|
|
309
|
+
if (p.approximation.approximate && (!projection.approximation.approximate || p.approximation.reasons.some((reason2) => !projection.approximation.reasons.includes(reason2))))
|
|
310
|
+
throw new Error("Aggregate must retain provider approximation provenance");
|
|
311
|
+
if (Object.values(p.windows).some((v) => v.kind === "unavailable") && !unavailableProvenance(p.approximation))
|
|
312
|
+
throw new Error("Unavailable provider requires missing-price provenance");
|
|
313
|
+
}
|
|
314
|
+
if ([...Object.values(projection.windows), ...Object.values(projection.scores)].some((v) => v.kind === "unavailable") && !unavailableProvenance(projection.approximation))
|
|
315
|
+
throw new Error("Unavailable total requires missing-price provenance");
|
|
316
|
+
for (const window of ["m20", "h24", "d7", "lifetime"]) {
|
|
317
|
+
const known = projection.providers.filter((p) => p.windows[window].kind !== "unavailable");
|
|
318
|
+
const parts = known.map((p) => ({ providerId: p.providerId, amount: p.windows[window].amount }));
|
|
319
|
+
const dollars = parts.reduce((sum, p) => sum + p.amount, 0), score = weightedScoreV2(parts, providerWeights);
|
|
320
|
+
if (!Number.isFinite(dollars))
|
|
321
|
+
throw new Error("Provider dollar overflow");
|
|
322
|
+
const incomplete = projection.omittedProviders > 0 || known.length !== projection.providers.length;
|
|
323
|
+
const floor = incomplete || known.some((p) => p.windows[window].kind === "floor");
|
|
324
|
+
const equal = (a, b) => Math.abs(a - b) <= 8 * Number.EPSILON * Math.max(a, b);
|
|
325
|
+
const attribution = projection.attribution;
|
|
326
|
+
if (projection.windows[window].kind === "unavailable" && (dollars > 0 || (attribution?.unknownProvider[window].amount ?? 0) > 0))
|
|
327
|
+
throw new Error("Supported provider dollars require an aggregate floor or estimate");
|
|
328
|
+
if (attribution) {
|
|
329
|
+
const unknown = attribution.unknownProvider[window], complete = attribution.attributionComplete[window];
|
|
330
|
+
if (complete && (projection.omittedProviders > 0 || unknown.kind !== "estimate" || unknown.amount !== 0))
|
|
331
|
+
throw new Error("Complete attribution requires no unknown provider remainder");
|
|
332
|
+
if (attribution.unpriced[window] && !projection.approximation.reasons.includes("missing-price"))
|
|
333
|
+
throw new Error("Unpriced attribution requires missing-price provenance");
|
|
334
|
+
if (projection.windows[window].kind === "estimate" && (!complete || attribution.unpriced[window] || unknown.kind !== "estimate" || unknown.amount !== 0))
|
|
335
|
+
throw new Error("Incomplete attribution cannot carry an estimate");
|
|
336
|
+
const total = projection.windows[window], reconciled = dollars + (unknown.amount ?? 0);
|
|
337
|
+
if (!Number.isFinite(reconciled) || total.amount !== null && (reconciled > total.amount && !equal(reconciled, total.amount) || unknown.kind === "estimate" && !equal(reconciled, total.amount)))
|
|
338
|
+
throw new Error("Unknown provider reconciliation mismatch");
|
|
339
|
+
}
|
|
340
|
+
for (const [total, subtotal, label] of [[projection.windows[window], dollars, "dollar"], [projection.scores[window], score, "score"]]) {
|
|
341
|
+
if (total.kind === "unavailable")
|
|
342
|
+
continue;
|
|
343
|
+
if (floor && total.kind === "estimate")
|
|
344
|
+
throw new Error("Partial/provider floor cannot become an exact estimate");
|
|
345
|
+
if (incomplete) {
|
|
346
|
+
if (total.amount < subtotal && !equal(total.amount, subtotal))
|
|
347
|
+
throw new Error(`Known provider ${label} subtotal exceeds total`);
|
|
348
|
+
} else if (!equal(total.amount, subtotal))
|
|
349
|
+
throw new Error(`Provider ${label} total mismatch`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function validateRanks(projection, population2, contemporaneous) {
|
|
354
|
+
for (const [values, ranks, pops] of [[projection.windows, projection.dollarRanks, population2.dollars], [projection.scores, projection.scoreRanks, population2.scores]]) {
|
|
355
|
+
for (const w of ["m20", "h24", "d7"]) {
|
|
356
|
+
const r = ranks[w], n = pops[w].count;
|
|
357
|
+
if (r !== null && (n === 0 || n === 1 && r !== 0))
|
|
358
|
+
throw new Error("Rank contradicts empty/singleton population");
|
|
359
|
+
if (contemporaneous && r !== null && (values[w].amount === null || values[w].amount <= 0))
|
|
360
|
+
throw new Error("Rank requires supported positive contemporaneous amount");
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function recentPercentileHeat(projection, gates = { m20: 0.9, h24: 0.9, d7: 0.9 }, population2) {
|
|
365
|
+
for (const w of ["m20", "h24", "d7"])
|
|
366
|
+
if (!Number.isFinite(gates[w]) || gates[w] < 0 || gates[w] > 1)
|
|
367
|
+
throw new Error("Invalid gate");
|
|
368
|
+
for (const [w, label] of [["m20", "on-fire"], ["h24", "hot"], ["d7", "heavy"]]) {
|
|
369
|
+
const amount = projection.scores[w].amount, r = projection.scoreRanks[w], n = population2?.[w].count ?? (r !== null && r > 0 ? 2 : 0);
|
|
370
|
+
if (n >= 2 && amount !== null && amount > 0 && r !== null && r >= gates[w])
|
|
371
|
+
return label;
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
function isFirstIndexRank(r, n) {
|
|
376
|
+
const nearest = Math.round(r * (n - 1)) / (n - 1);
|
|
377
|
+
return Math.abs(r - nearest) <= 4 * Number.EPSILON * Math.max(r, nearest);
|
|
378
|
+
}
|
|
379
|
+
function validateRootHeatRow(row, context, config) {
|
|
380
|
+
if (!context) {
|
|
381
|
+
if (row.heat)
|
|
382
|
+
throw new Error("Root row requires context");
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!row.heat || config.schemaVersion !== 3)
|
|
386
|
+
throw new Error("Missing root row topology/decisions");
|
|
387
|
+
const heat = row.heat, eligible = rootFamilyEligibilityV1(heat.topology) === "eligible-root";
|
|
388
|
+
for (const [ranks, population2, values] of [[heat.rootDollarRanks, context.population.dollars, row.family.windows], [heat.rootScoreRanks, context.population.scores, row.family.scores]]) {
|
|
389
|
+
for (const w of HEAT_WINDOWS) {
|
|
390
|
+
const r = ranks[w], n = population2[w].count;
|
|
391
|
+
if (!eligible && r !== null)
|
|
392
|
+
throw new Error("Ineligible thread carries root-domain rank");
|
|
393
|
+
if (r !== null && (n === 0 || n === 1 && r !== 0 || population2[w].coverage === "unknown"))
|
|
394
|
+
throw new Error("Root rank population mismatch");
|
|
395
|
+
if (r !== null && n >= 2 && population2[w].coverage === "complete" && !isFirstIndexRank(r, n))
|
|
396
|
+
throw new Error("Root rank outside complete first-index grid");
|
|
397
|
+
if (context.rankAsOf >= row.asOf && r !== null && population2[w].coverage === "complete" && values[w].kind !== "estimate")
|
|
398
|
+
throw new Error("Uncertified value cannot carry a complete root rank");
|
|
399
|
+
if (context.rankAsOf >= row.asOf && r !== null && (values[w].amount === null || values[w].amount <= 0))
|
|
400
|
+
throw new Error("Root rank needs supported positive amount");
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const expected = rootFamilyHeatV1({ topology: heat.topology, family: { contextRevision: heat.decisions.contextRevision, configRevision: row.configRevision, scores: row.family.scores, rootScoreRanks: heat.rootScoreRanks }, context, config });
|
|
404
|
+
if (JSON.stringify(expected) !== JSON.stringify(heat.decisions))
|
|
405
|
+
throw new Error("Root heat decisions/winner mismatch");
|
|
406
|
+
}
|
|
407
|
+
function validateHeatInputs(snapshot) {
|
|
408
|
+
const { heatInputs: input, heat: context } = snapshot;
|
|
409
|
+
if (!context || snapshot.config.value.schemaVersion !== 3 || input.contextRevision !== context.contextRevision || input.topologyRevision !== context.topologyRevision)
|
|
410
|
+
throw new Error("Root input context mismatch");
|
|
411
|
+
if (!input.cohortComplete && snapshot.coverage === "complete")
|
|
412
|
+
throw new Error("Incomplete root inputs require partial snapshot");
|
|
413
|
+
if (input.cohortComplete && (input.pending || input.omitted.count || !input.omitted.exact))
|
|
414
|
+
throw new Error("Incomplete root input cohort");
|
|
415
|
+
const ids = new Set;
|
|
416
|
+
for (const row of input.rows) {
|
|
417
|
+
if (ids.has(row.threadId))
|
|
418
|
+
throw new Error("Duplicate root input");
|
|
419
|
+
ids.add(row.threadId);
|
|
420
|
+
if (row.asOf > context.rankAsOf || row.configRevision !== snapshot.config.revision || rootFamilyEligibilityV1(row.heat.topology) !== "eligible-root" || !row.family.attribution)
|
|
421
|
+
throw new Error("Invalid root input capture");
|
|
422
|
+
validateProjection(row.family, snapshot.config.value.providerWeights);
|
|
423
|
+
validateRootHeatRow(row, context, snapshot.config.value);
|
|
424
|
+
}
|
|
425
|
+
for (const [axis, field, ranks] of [["dollars", "windows", "rootDollarRanks"], ["scores", "scores", "rootScoreRanks"]])
|
|
426
|
+
for (const w of HEAT_WINDOWS) {
|
|
427
|
+
const pop = context.population[axis][w], supported = input.rows.filter((row) => (row.family[field][w].amount ?? 0) > 0);
|
|
428
|
+
if (input.cohortComplete && pop.count > input.rows.length)
|
|
429
|
+
throw new Error("Root cohort below declared population");
|
|
430
|
+
if (pop.coverage === "complete" && pop.count < supported.length)
|
|
431
|
+
throw new Error("Root input population below supported members");
|
|
432
|
+
if (!input.cohortComplete || pop.coverage !== "complete")
|
|
433
|
+
continue;
|
|
434
|
+
if (supported.some((row) => row.family[field][w].kind !== "estimate") || pop.count !== supported.length)
|
|
435
|
+
throw new Error("Complete root input population mismatch");
|
|
436
|
+
const sorted = supported.slice().sort((a, b) => a.family[field][w].amount - b.family[field][w].amount || (a.threadId < b.threadId ? -1 : 1)), expected = new Map;
|
|
437
|
+
let first = 0;
|
|
438
|
+
for (let i = 0;i < sorted.length; i++) {
|
|
439
|
+
if (!i || sorted[i].family[field][w].amount !== sorted[i - 1].family[field][w].amount)
|
|
440
|
+
first = i;
|
|
441
|
+
expected.set(sorted[i].threadId, sorted.length === 1 ? 0 : first / (sorted.length - 1));
|
|
442
|
+
}
|
|
443
|
+
for (const row of input.rows) {
|
|
444
|
+
const actual = row.heat[ranks][w], rank2 = expected.get(row.threadId) ?? null;
|
|
445
|
+
if (actual !== rank2)
|
|
446
|
+
throw new Error("Root input rank/order mismatch");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// src/heat-settings.ts
|
|
451
|
+
import { z as z2 } from "zod";
|
|
452
|
+
var HEAT_SETTINGS_METHODS = Object.freeze({ read: "threadMetricsHeatSettingsReadV1", save: "threadMetricsHeatSettingsSaveV1" });
|
|
453
|
+
var HEAT_SETTINGS_LIMITS = Object.freeze({ configBytes: 16 * 1024, inputBytes: 18 * 1024, outputBytes: 40 * 1024, operationKeys: 128 });
|
|
454
|
+
var version = z2.string().min(1).max(256);
|
|
455
|
+
var key = z2.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
456
|
+
var providerId = z2.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
457
|
+
var weights2 = z2.record(providerId, z2.number().finite().min(0).max(3)).refine((v) => Object.keys(v).length <= 32, "Too many provider weights");
|
|
458
|
+
var gates = z2.object({ m20: z2.number().finite().min(0).max(1), h24: z2.number().finite().min(0).max(1), d7: z2.number().finite().min(0).max(1) }).strict();
|
|
459
|
+
var thresholds2 = z2.object({ m20: z2.number().finite().min(0).max(1e6), h24: z2.number().finite().min(0).max(1e6), d7: z2.number().finite().min(0).max(1e6), lifetime: z2.number().finite().min(0).max(1e6) }).strict();
|
|
460
|
+
var legacyHeatConfigInputSchema = z2.discriminatedUnion("mode", [
|
|
461
|
+
z2.object({ schemaVersion: z2.literal(2), mode: z2.literal("percentile"), providerWeights: weights2, gates }).strict(),
|
|
462
|
+
z2.object({ schemaVersion: z2.literal(2), mode: z2.literal("absolute"), providerWeights: weights2, thresholds: thresholds2, lifetimeEnabled: z2.boolean().optional() }).strict()
|
|
463
|
+
]);
|
|
464
|
+
var domain = z2.object({ version: z2.literal(1), kind: z2.literal("root-family") }).strict();
|
|
465
|
+
var heatConfigInputSchema = z2.discriminatedUnion("mode", [
|
|
466
|
+
z2.object({ schemaVersion: z2.literal(3), domain, mode: z2.literal("percentile"), providerWeights: weights2, gates }).strict(),
|
|
467
|
+
z2.object({ schemaVersion: z2.literal(3), domain, mode: z2.literal("absolute"), providerWeights: weights2, thresholds: thresholds2, lifetimeEnabled: z2.literal(false).optional() }).strict()
|
|
468
|
+
]);
|
|
469
|
+
function canonicalHeatConfig(value) {
|
|
470
|
+
const v = value.schemaVersion === 2 ? legacyHeatConfigInputSchema.parse(value) : heatConfigInputSchema.parse(value);
|
|
471
|
+
return JSON.stringify({ ...v, providerWeights: Object.fromEntries(Object.entries(v.providerWeights).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)), ...v.mode === "absolute" ? { lifetimeEnabled: v.lifetimeEnabled ?? false } : {} });
|
|
472
|
+
}
|
|
473
|
+
function validateHeatConfig(value) {
|
|
474
|
+
const parsed = bounded(z2.union([legacyHeatConfigInputSchema, heatConfigInputSchema]), value, HEAT_SETTINGS_LIMITS.configBytes);
|
|
475
|
+
if (new TextEncoder().encode(canonicalHeatConfig(parsed)).byteLength > HEAT_SETTINGS_LIMITS.configBytes)
|
|
476
|
+
throw new Error("Config byte limit");
|
|
477
|
+
return parsed;
|
|
478
|
+
}
|
|
479
|
+
function validateRootFamilyHeatConfig(value) {
|
|
480
|
+
return bounded(heatConfigInputSchema, value, HEAT_SETTINGS_LIMITS.configBytes);
|
|
481
|
+
}
|
|
482
|
+
function prepareRootFamilyMigration(record, review) {
|
|
483
|
+
const value = validateHeatConfig(record.value);
|
|
484
|
+
if (value.schemaVersion === 3)
|
|
485
|
+
return { kind: "review", base: record, candidate: value };
|
|
486
|
+
if (value.mode === "absolute" && value.lifetimeEnabled && !review?.disableLifetime)
|
|
487
|
+
return { kind: "blocked", reason: "Legacy lifetime heat is enabled. Explicitly review and disable that behavior before root-family migration; stored values were preserved." };
|
|
488
|
+
const candidate = value.mode === "percentile" ? { schemaVersion: 3, domain: ROOT_FAMILY_HEAT_DOMAIN_V1, mode: value.mode, providerWeights: { ...value.providerWeights }, gates: { ...value.gates } } : { schemaVersion: 3, domain: ROOT_FAMILY_HEAT_DOMAIN_V1, mode: value.mode, providerWeights: { ...value.providerWeights }, thresholds: { ...value.thresholds }, lifetimeEnabled: false };
|
|
489
|
+
return { kind: "review", base: record, candidate };
|
|
490
|
+
}
|
|
491
|
+
var heatSettingsReadInputSchema = z2.object({ protocolVersion: z2.literal(1), operationKey: key.optional() }).strict();
|
|
492
|
+
var heatSettingsSaveInputSchema = z2.object({ protocolVersion: z2.literal(1), baseConfigVersion: version, operationKey: key, candidate: heatConfigInputSchema }).strict();
|
|
493
|
+
var heatConfigRecordSchema = z2.object({ version, value: storedScoringConfigSchema }).strip();
|
|
494
|
+
var observation = z2.object({ instance: version, sequence: z2.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER), configVersion: version.nullable() }).strip();
|
|
495
|
+
var outcome = z2.discriminatedUnion("kind", [
|
|
496
|
+
z2.object({ kind: z2.literal("saved"), operationKey: key, record: heatConfigRecordSchema }).strip(),
|
|
497
|
+
z2.object({ kind: z2.literal("conflict"), operationKey: key, record: heatConfigRecordSchema }).strip(),
|
|
498
|
+
z2.object({ kind: z2.literal("unknown"), operationKey: key }).strip()
|
|
499
|
+
]);
|
|
500
|
+
var heatSettingsReadResultSchema = z2.object({ protocolVersion: z2.literal(1), current: heatConfigRecordSchema, published: observation, writable: z2.boolean(), reason: z2.string().max(512).nullable(), migration: z2.object({ kind: z2.literal("domain-required"), target: z2.object({ version: z2.literal(1), kind: z2.literal("root-family") }).strip() }).strip().optional(), outcome: outcome.optional() }).strip();
|
|
501
|
+
var heatSettingsSaveResultSchema = z2.object({ protocolVersion: z2.literal(1), kind: z2.enum(["saved", "conflict"]), operationKey: key, record: heatConfigRecordSchema, publicationPending: z2.boolean() }).strip();
|
|
502
|
+
function scoringHeatV2(values, ranks, population2, config) {
|
|
503
|
+
legacyHeatConfigInputSchema.parse(config);
|
|
504
|
+
for (const w of ["m20", "h24", "d7"])
|
|
505
|
+
if (!Number.isSafeInteger(population2[w]) || population2[w] < 0 || ranks[w] !== null && (!Number.isFinite(ranks[w]) || ranks[w] < 0 || ranks[w] > 1))
|
|
506
|
+
throw new Error("Invalid heat rank population");
|
|
507
|
+
for (const [w, label] of [["m20", "on-fire"], ["h24", "hot"], ["d7", "heavy"]]) {
|
|
508
|
+
const n2 = values[w].amount;
|
|
509
|
+
if (n2 !== null && Number.isFinite(n2) && n2 > 0 && (config.mode === "absolute" ? n2 >= config.thresholds[w] : population2[w] >= 2 && ranks[w] !== null && ranks[w] >= config.gates[w]))
|
|
510
|
+
return label;
|
|
511
|
+
}
|
|
512
|
+
const n = values.lifetime.amount;
|
|
513
|
+
return config.mode === "absolute" && config.lifetimeEnabled && n !== null && Number.isFinite(n) && n > 0 && n >= config.thresholds.lifetime ? "heavy" : null;
|
|
514
|
+
}
|
|
515
|
+
function bounded(schema, value, maxBytes) {
|
|
516
|
+
const json = JSON.stringify(value);
|
|
517
|
+
if (json === undefined || new TextEncoder().encode(json).byteLength > maxBytes)
|
|
518
|
+
throw new Error("Heat settings byte limit");
|
|
519
|
+
return schema.parse(value);
|
|
520
|
+
}
|
|
521
|
+
function validateHeatSaveInput(value) {
|
|
522
|
+
const input = bounded(heatSettingsSaveInputSchema, value, HEAT_SETTINGS_LIMITS.inputBytes);
|
|
523
|
+
validateHeatConfig(input.candidate);
|
|
524
|
+
return input;
|
|
525
|
+
}
|
|
526
|
+
function reconcileHeatSave(input, read) {
|
|
527
|
+
const o = read.outcome;
|
|
528
|
+
if (o?.operationKey === input.operationKey && o.kind === "saved")
|
|
529
|
+
return canonicalHeatConfig(o.record.value) === canonicalHeatConfig(input.candidate) ? "saved" : "conflict";
|
|
530
|
+
if (o?.operationKey === input.operationKey && o.kind === "conflict")
|
|
531
|
+
return "conflict";
|
|
532
|
+
return read.current.version === input.baseConfigVersion ? "unchanged" : "unknown";
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// src/index.ts
|
|
536
|
+
import { z as z3 } from "zod";
|
|
537
|
+
import { UsageVectorSchema as UsageVectorSchema2 } from "@phosphorco/bb-usage-algebra/vector";
|
|
4
538
|
var LIMITS = Object.freeze({ threadIds: 200, seriesPoints: 288, detailRows: 200, inputBytes: 65536, outputBytes: 2097152, describeBytes: 16384, candidates: 128, listedPlugins: 1e4, concurrency: 4, callMs: 2000, discoveryMs: 5000, familyEdges: 50000, familyMembers: 1e4, familyDepth: 256 });
|
|
5
539
|
var METRIC_BUCKET_MS = 300000;
|
|
6
540
|
var WINDOW_BUCKETS = { m20: 4, h24: 288, d7: 2016, lifetime: 2016 };
|
|
7
541
|
function retainedWindowStart(asOf, window) {
|
|
8
|
-
|
|
542
|
+
time2.parse(asOf);
|
|
9
543
|
windowSchema.parse(window);
|
|
10
544
|
return Math.max(0, Math.floor(asOf / METRIC_BUCKET_MS) * METRIC_BUCKET_MS - (WINDOW_BUCKETS[window] - 1) * METRIC_BUCKET_MS);
|
|
11
545
|
}
|
|
12
|
-
var METHODS = Object.freeze({ describe: "threadMetricsDescribe", read: "threadMetricsReadV1", detail: "threadMetricsDetailV1" });
|
|
13
|
-
var
|
|
14
|
-
var text =
|
|
15
|
-
var
|
|
16
|
-
var money =
|
|
17
|
-
var scope =
|
|
18
|
-
var windowSchema =
|
|
19
|
-
var coverageSchema =
|
|
20
|
-
var coverageReasonSchema =
|
|
21
|
-
var
|
|
22
|
-
var approximationSchema =
|
|
23
|
-
approximate:
|
|
24
|
-
reasons:
|
|
25
|
-
rates:
|
|
546
|
+
var METHODS = Object.freeze({ describe: "threadMetricsDescribe", read: "threadMetricsReadV1", detail: "threadMetricsDetailV1", recentSnapshot: "threadMetricsRecentSnapshotV1" });
|
|
547
|
+
var id2 = z3.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/);
|
|
548
|
+
var text = z3.string().min(1).max(256);
|
|
549
|
+
var time2 = z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
550
|
+
var money = z3.number().finite().nonnegative().nullable();
|
|
551
|
+
var scope = z3.enum(["own", "family"]);
|
|
552
|
+
var windowSchema = z3.enum(["m20", "h24", "d7", "lifetime"]);
|
|
553
|
+
var coverageSchema = z3.enum(["complete", "partial", "unknown"]);
|
|
554
|
+
var coverageReasonSchema = z3.enum(["source-gap", "root-unattested", "history-unavailable", "member-cap", "depth-cap", "edge-cap", "missing-thread", "unpriced", "retention", "row-cap", "pagination", "population-incomplete"]);
|
|
555
|
+
var reasons2 = z3.array(coverageReasonSchema).max(16);
|
|
556
|
+
var approximationSchema = z3.object({
|
|
557
|
+
approximate: z3.boolean(),
|
|
558
|
+
reasons: z3.array(z3.enum(["closest-model", "provider-default", "model-attribution", "bucket-boundary", "missing-price", "source-gap"])).max(6),
|
|
559
|
+
rates: z3.array(z3.object({ providerId: id2, modelId: text, rateId: text.nullable(), basis: z3.enum(["matched", "closest-model", "provider-default", "unpriced"]) }).superRefine((rate, ctx) => {
|
|
26
560
|
if (rate.basis === "unpriced" !== (rate.rateId === null))
|
|
27
561
|
ctx.addIssue({ code: "custom", path: ["rateId"], message: "Priced basis requires rateId; unpriced basis requires null" });
|
|
28
562
|
})).max(32).optional()
|
|
@@ -30,40 +564,40 @@ var approximationSchema = z.object({
|
|
|
30
564
|
if (!v.approximate && (v.reasons.length || v.rates?.some((r) => r.basis !== "matched")))
|
|
31
565
|
ctx.addIssue({ code: "custom", message: "Approximation provenance requires approximate flag" });
|
|
32
566
|
});
|
|
33
|
-
var
|
|
34
|
-
var ranksSchema =
|
|
35
|
-
var
|
|
36
|
-
var rankPopulationSchema =
|
|
37
|
-
var windowValueSchema =
|
|
38
|
-
var windowsSchema =
|
|
39
|
-
var bucketSchema =
|
|
567
|
+
var rank2 = z3.number().finite().min(0).max(1).nullable();
|
|
568
|
+
var ranksSchema = z3.object({ m20: rank2, h24: rank2, d7: rank2 }).strip();
|
|
569
|
+
var population2 = z3.object({ asOf: time2, count: z3.number().int().nonnegative(), coverage: coverageSchema }).strip();
|
|
570
|
+
var rankPopulationSchema = z3.object({ m20: population2, h24: population2, d7: population2 }).strip();
|
|
571
|
+
var windowValueSchema = z3.object({ tokens: UsageVectorSchema2, amount: money }).strip();
|
|
572
|
+
var windowsSchema = z3.object({ m20: windowValueSchema, h24: windowValueSchema, d7: windowValueSchema, lifetime: windowValueSchema }).strip();
|
|
573
|
+
var bucketSchema = z3.object({ start: time2, amount: money }).strip();
|
|
40
574
|
var metadata = {
|
|
41
|
-
threadId:
|
|
575
|
+
threadId: id2,
|
|
42
576
|
scope,
|
|
43
|
-
memberCount:
|
|
577
|
+
memberCount: z3.number().int().nonnegative().max(LIMITS.familyMembers),
|
|
44
578
|
coverage: coverageSchema,
|
|
45
|
-
coverageReasons:
|
|
46
|
-
excluded:
|
|
47
|
-
asOf:
|
|
579
|
+
coverageReasons: reasons2,
|
|
580
|
+
excluded: z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
581
|
+
asOf: time2,
|
|
48
582
|
revision: text,
|
|
49
583
|
priceVersion: text,
|
|
50
|
-
currency:
|
|
51
|
-
estimated:
|
|
584
|
+
currency: z3.literal("USD"),
|
|
585
|
+
estimated: z3.literal(true),
|
|
52
586
|
approximation: approximationSchema,
|
|
53
587
|
ranks: ranksSchema,
|
|
54
588
|
rankPopulation: rankPopulationSchema,
|
|
55
|
-
reported:
|
|
589
|
+
reported: z3.object({ amount: z3.number().finite().nonnegative(), currency: z3.string().min(1).max(16), asOf: time2 }).strip().optional()
|
|
56
590
|
};
|
|
57
|
-
var summaryRowSchema =
|
|
58
|
-
|
|
59
|
-
|
|
591
|
+
var summaryRowSchema = z3.discriminatedUnion("changed", [
|
|
592
|
+
z3.object({ ...metadata, changed: z3.literal(true), windows: windowsSchema, buckets: z3.array(bucketSchema).max(LIMITS.seriesPoints).optional() }).strip(),
|
|
593
|
+
z3.object({ ...metadata, changed: z3.literal(false), windows: z3.never().optional(), buckets: z3.never().optional() }).strip()
|
|
60
594
|
]);
|
|
61
|
-
var readInputSchema =
|
|
62
|
-
threadIds:
|
|
595
|
+
var readInputSchema = z3.object({
|
|
596
|
+
threadIds: z3.array(id2).min(1).max(LIMITS.threadIds),
|
|
63
597
|
scope,
|
|
64
|
-
includeArchived:
|
|
65
|
-
knownRevisions:
|
|
66
|
-
series:
|
|
598
|
+
includeArchived: z3.boolean().optional(),
|
|
599
|
+
knownRevisions: z3.record(id2, text).optional(),
|
|
600
|
+
series: z3.object({ activeThreadId: id2, maxPoints: z3.number().int().min(1).max(LIMITS.seriesPoints) }).strict().optional()
|
|
67
601
|
}).strict().superRefine((v, ctx) => {
|
|
68
602
|
if (new Set(v.threadIds).size !== v.threadIds.length)
|
|
69
603
|
ctx.addIssue({ code: "custom", message: "Duplicate thread IDs" });
|
|
@@ -72,17 +606,17 @@ var readInputSchema = z.object({
|
|
|
72
606
|
if (v.knownRevisions && Object.keys(v.knownRevisions).some((k) => !v.threadIds.includes(k)))
|
|
73
607
|
ctx.addIssue({ code: "custom", message: "Unrequested revision key" });
|
|
74
608
|
});
|
|
75
|
-
var readResultSchema =
|
|
76
|
-
var detailInputSchema =
|
|
77
|
-
var detailBase = { amount: money, tokens:
|
|
78
|
-
var detailRowSchema =
|
|
79
|
-
|
|
80
|
-
|
|
609
|
+
var readResultSchema = z3.object({ version: z3.literal(1), rows: z3.array(summaryRowSchema).max(LIMITS.threadIds) }).strip();
|
|
610
|
+
var detailInputSchema = z3.object({ threadId: id2, scope, includeArchived: z3.boolean().optional(), axis: z3.enum(["model", "time"]), window: windowSchema, cursor: z3.string().min(1).max(1024).optional(), limit: z3.number().int().min(1).max(LIMITS.detailRows).optional() }).strict();
|
|
611
|
+
var detailBase = { amount: money, tokens: UsageVectorSchema2, approximation: approximationSchema };
|
|
612
|
+
var detailRowSchema = z3.discriminatedUnion("axis", [
|
|
613
|
+
z3.object({ ...detailBase, axis: z3.literal("model"), providerId: id2, modelId: text }).strip(),
|
|
614
|
+
z3.object({ ...detailBase, axis: z3.literal("time"), start: time2, end: time2 }).strip().refine((v) => v.end > v.start, { message: "Empty time interval" })
|
|
81
615
|
]);
|
|
82
|
-
var detailResultSchema =
|
|
83
|
-
var describeInputSchema =
|
|
84
|
-
var describeEnvelopeSchema =
|
|
85
|
-
var capabilitiesSchema =
|
|
616
|
+
var detailResultSchema = z3.object({ version: z3.literal(1), threadId: id2, scope, axis: z3.enum(["model", "time"]), window: windowSchema, asOf: time2, revision: text, priceVersion: text, currency: z3.literal("USD"), estimated: z3.literal(true), coverage: coverageSchema, coverageReasons: reasons2, excluded: z3.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(), approximation: approximationSchema, total: windowValueSchema, rows: z3.array(detailRowSchema).max(LIMITS.detailRows), nextCursor: z3.string().min(1).max(1024).optional() }).strip();
|
|
617
|
+
var describeInputSchema = z3.object({}).strict();
|
|
618
|
+
var describeEnvelopeSchema = z3.object({ protocol: z3.literal("bb-thread-metrics"), versions: z3.array(z3.number().int().positive()).max(16) }).passthrough();
|
|
619
|
+
var capabilitiesSchema = z3.object({ summary: z3.literal(true), series: z3.boolean(), detail: z3.boolean(), recentSnapshotV1: z3.boolean().optional(), heatSettingsV1: z3.boolean().optional() }).strip();
|
|
86
620
|
var describeV1Schema = describeEnvelopeSchema.extend({ capabilities: capabilitiesSchema });
|
|
87
621
|
function parseBounded(schema, value, maxBytes) {
|
|
88
622
|
const json = JSON.stringify(value);
|
|
@@ -100,29 +634,29 @@ function negotiateVersion(value) {
|
|
|
100
634
|
const v = describeV1Schema.safeParse(value);
|
|
101
635
|
return v.success ? { kind: "ready", version: 1, capabilities: v.data.capabilities } : { kind: "incompatible" };
|
|
102
636
|
}
|
|
103
|
-
function dollarPercentiles(
|
|
637
|
+
function dollarPercentiles(population3) {
|
|
104
638
|
const seen = new Set;
|
|
105
|
-
for (const p of
|
|
639
|
+
for (const p of population3) {
|
|
106
640
|
if (seen.has(p.threadId))
|
|
107
641
|
throw new Error("Duplicate population thread");
|
|
108
642
|
seen.add(p.threadId);
|
|
109
643
|
if (p.amount !== null && (!Number.isFinite(p.amount) || p.amount < 0))
|
|
110
644
|
throw new Error("Invalid dollars");
|
|
111
645
|
}
|
|
112
|
-
const active =
|
|
646
|
+
const active = population3.filter((p) => p.amount !== null && p.amount > 0);
|
|
113
647
|
const sorted = active.map((p) => p.amount).sort((a, b) => a - b);
|
|
114
648
|
const lower = new Map;
|
|
115
649
|
sorted.forEach((x, i) => {
|
|
116
650
|
if (!lower.has(x))
|
|
117
651
|
lower.set(x, i);
|
|
118
652
|
});
|
|
119
|
-
return new Map(
|
|
653
|
+
return new Map(population3.map((p) => [p.threadId, p.amount === null || p.amount === 0 ? null : active.length < 2 ? 0 : lower.get(p.amount) / (active.length - 1)]));
|
|
120
654
|
}
|
|
121
|
-
function heat(ranks,
|
|
655
|
+
function heat(ranks, gates2 = { m20: 0.9, h24: 0.9, d7: 0.9 }) {
|
|
122
656
|
for (const w of ["m20", "h24", "d7"])
|
|
123
|
-
if (!Number.isFinite(
|
|
657
|
+
if (!Number.isFinite(gates2[w]) || gates2[w] < 0 || gates2[w] > 1)
|
|
124
658
|
throw new Error("Invalid gate");
|
|
125
|
-
return ranks.m20 !== null && ranks.m20 >=
|
|
659
|
+
return ranks.m20 !== null && ranks.m20 >= gates2.m20 ? "on-fire" : ranks.h24 !== null && ranks.h24 >= gates2.h24 ? "hot" : ranks.d7 !== null && ranks.d7 >= gates2.d7 ? "heavy" : null;
|
|
126
660
|
}
|
|
127
661
|
function familyClosure(edges, roots, policy = {}) {
|
|
128
662
|
const cap = (n, fallback) => {
|
|
@@ -222,7 +756,7 @@ function validateDetailResult(input, value) {
|
|
|
222
756
|
let priorEnd;
|
|
223
757
|
const keys = new Set;
|
|
224
758
|
for (const row of out.rows) {
|
|
225
|
-
if (row.approximation.approximate && (!out.approximation.approximate || row.approximation.reasons.some((
|
|
759
|
+
if (row.approximation.approximate && (!out.approximation.approximate || row.approximation.reasons.some((reason2) => !out.approximation.reasons.includes(reason2))))
|
|
226
760
|
throw new Error("Aggregate total must retain contributing row approximation provenance");
|
|
227
761
|
if (row.amount === null && !row.approximation.reasons.includes("missing-price"))
|
|
228
762
|
throw new Error("Unknown dollars require missing-price provenance");
|
|
@@ -233,15 +767,15 @@ function validateDetailResult(input, value) {
|
|
|
233
767
|
throw new Error("Invalid time detail range/order/overlap");
|
|
234
768
|
priorEnd = row.end;
|
|
235
769
|
}
|
|
236
|
-
const
|
|
237
|
-
if (keys.has(
|
|
770
|
+
const key2 = row.axis === "model" ? JSON.stringify([row.providerId, row.modelId]) : String(row.start);
|
|
771
|
+
if (keys.has(key2))
|
|
238
772
|
throw new Error("Duplicate detail row");
|
|
239
|
-
keys.add(
|
|
773
|
+
keys.add(key2);
|
|
240
774
|
}
|
|
241
775
|
if (out.coverage === "complete" && !input.cursor && !out.nextCursor) {
|
|
242
|
-
for (const
|
|
243
|
-
const sum = out.rows.reduce((n, r) => n + r.tokens[
|
|
244
|
-
if (!Number.isFinite(sum) || Math.abs(sum - out.total.tokens[
|
|
776
|
+
for (const key2 of ["inputUncached", "cacheRead", "cacheWrite", "output"]) {
|
|
777
|
+
const sum = out.rows.reduce((n, r) => n + r.tokens[key2], 0);
|
|
778
|
+
if (!Number.isFinite(sum) || Math.abs(sum - out.total.tokens[key2]) > 0.00000001 * Math.max(1, sum, out.total.tokens[key2]))
|
|
245
779
|
throw new Error("Detail token total mismatch");
|
|
246
780
|
}
|
|
247
781
|
const hasUnknown = out.rows.some((r) => r.amount === null);
|
|
@@ -273,16 +807,57 @@ export {
|
|
|
273
807
|
windowsSchema,
|
|
274
808
|
windowValueSchema,
|
|
275
809
|
windowSchema,
|
|
810
|
+
weightedScoreV2,
|
|
811
|
+
validateRootFamilyHeatConfig,
|
|
812
|
+
validateRecentSnapshot,
|
|
276
813
|
validateReadResult,
|
|
814
|
+
validateHeatSaveInput,
|
|
815
|
+
validateHeatConfig,
|
|
277
816
|
validateDetailResult,
|
|
278
817
|
summaryRowSchema,
|
|
818
|
+
storedScoringConfigSchema,
|
|
819
|
+
scoringHeatV2,
|
|
820
|
+
scoringConfigV3Schema,
|
|
821
|
+
scoringConfigV2Schema,
|
|
822
|
+
rootTopologyV1Schema,
|
|
823
|
+
rootFamilyHeatV1,
|
|
824
|
+
rootFamilyHeatDomainV1Schema,
|
|
825
|
+
rootFamilyHeatContextV1Schema,
|
|
826
|
+
rootFamilyEligibilityV1,
|
|
827
|
+
rootFamilyDecisionSetV1Schema,
|
|
279
828
|
retainedWindowStart,
|
|
829
|
+
reconcileHeatSave,
|
|
830
|
+
recentWindowsSchema,
|
|
831
|
+
recentUsageSchema,
|
|
832
|
+
recentTokenWindowsSchema,
|
|
833
|
+
recentTokenValueSchema,
|
|
834
|
+
recentSnapshotResultSchema,
|
|
835
|
+
recentSnapshotInputSchema,
|
|
836
|
+
recentSnapshotFullSchema,
|
|
837
|
+
recentScopeSchema,
|
|
838
|
+
recentRowSchema,
|
|
839
|
+
recentRootInputSchema,
|
|
840
|
+
recentRootHeatSchema,
|
|
841
|
+
recentPublicationIdentitySchema,
|
|
842
|
+
recentPresentationSchema,
|
|
843
|
+
recentPercentileHeat,
|
|
844
|
+
recentHeatInputsSchema,
|
|
845
|
+
recentDollarSchema,
|
|
846
|
+
recentAttributionSchema,
|
|
847
|
+
recentApproximationSchema,
|
|
280
848
|
readResultSchema,
|
|
281
849
|
readInputSchema,
|
|
282
850
|
ranksSchema,
|
|
283
851
|
rankPopulationSchema,
|
|
852
|
+
prepareRootFamilyMigration,
|
|
284
853
|
parseBounded,
|
|
285
854
|
negotiateVersion,
|
|
855
|
+
heatSettingsSaveResultSchema,
|
|
856
|
+
heatSettingsSaveInputSchema,
|
|
857
|
+
heatSettingsReadResultSchema,
|
|
858
|
+
heatSettingsReadInputSchema,
|
|
859
|
+
heatConfigRecordSchema,
|
|
860
|
+
heatConfigInputSchema,
|
|
286
861
|
heat,
|
|
287
862
|
familyClosure,
|
|
288
863
|
dollarPercentiles,
|
|
@@ -296,9 +871,18 @@ export {
|
|
|
296
871
|
coverageReasonSchema,
|
|
297
872
|
classifyOwnerError,
|
|
298
873
|
capabilitiesSchema,
|
|
874
|
+
canonicalHeatConfig,
|
|
299
875
|
bucketSchema,
|
|
876
|
+
bounded,
|
|
300
877
|
approximationSchema,
|
|
878
|
+
ROOT_FAMILY_HEAT_DOMAIN_V1,
|
|
879
|
+
RECENT_LIMITS,
|
|
301
880
|
METRIC_BUCKET_MS,
|
|
302
881
|
METHODS,
|
|
303
|
-
LIMITS
|
|
882
|
+
LIMITS,
|
|
883
|
+
HEAT_WINDOWS,
|
|
884
|
+
HEAT_SETTINGS_METHODS,
|
|
885
|
+
HEAT_SETTINGS_LIMITS,
|
|
886
|
+
DEFAULT_SCORING_CONFIG_V3,
|
|
887
|
+
DEFAULT_SCORING_CONFIG_V2
|
|
304
888
|
};
|