@danypops/jittor 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/package.json +4 -3
  2. package/src/adapters/artificial-analysis-direct-source.ts +42 -11
  3. package/src/adapters/lmarena-hf-source.ts +37 -15
  4. package/src/adapters/metric-benchmark-store.ts +29 -18
  5. package/src/adapters/openrouter-benchmark-source.ts +75 -19
  6. package/src/adapters/openrouter-design-arena-source.ts +36 -19
  7. package/src/adapters/sqlite-metric-store.ts +48 -25
  8. package/src/adapters/sqlite-session-identity-store.ts +13 -7
  9. package/src/cli-commands/benchmarks.ts +87 -22
  10. package/src/cli-commands/compaction.ts +7 -2
  11. package/src/cli-commands/context.ts +10 -2
  12. package/src/cli-commands/metrics.ts +128 -31
  13. package/src/cli-commands/op.ts +6 -1
  14. package/src/cli-commands/route-args.ts +5 -1
  15. package/src/cli-commands/router.ts +61 -18
  16. package/src/cli-commands/service-daemon.ts +31 -13
  17. package/src/cli-commands/session.ts +15 -4
  18. package/src/cli-commands/support.ts +1 -1
  19. package/src/cli.ts +30 -23
  20. package/src/client.ts +1 -1
  21. package/src/constants.ts +1 -1
  22. package/src/daemon.ts +44 -24
  23. package/src/domain/benchmark.ts +72 -40
  24. package/src/domain/codex-recovery.ts +34 -25
  25. package/src/domain/context-hub.ts +35 -26
  26. package/src/domain/context-telemetry.ts +106 -35
  27. package/src/domain/metric.ts +11 -11
  28. package/src/domain/model-observation.ts +139 -55
  29. package/src/domain/model-ranking-service.ts +13 -3
  30. package/src/domain/model-ranking.ts +126 -60
  31. package/src/domain/task-cost.ts +52 -11
  32. package/src/domain/task-focus.ts +11 -8
  33. package/src/domain/usage.ts +2 -2
  34. package/src/index.ts +69 -69
  35. package/src/log.ts +7 -2
  36. package/src/operations/benchmark-operations.ts +1 -1
  37. package/src/operations/context-operations.ts +15 -6
  38. package/src/operations/metrics-operations.ts +63 -28
  39. package/src/operations/model-ranking-operations.ts +9 -2
  40. package/src/operations/router-operations.ts +8 -4
  41. package/src/operations/session-identity-operations.ts +1 -1
  42. package/src/operations/session-scope.ts +5 -3
  43. package/src/policy.ts +22 -17
  44. package/src/ports/benchmark-controller.ts +1 -5
  45. package/src/ports/metric-store.ts +1 -1
  46. package/src/providers/anthropic-contracts.ts +13 -3
  47. package/src/providers/codex-contracts.ts +60 -52
  48. package/src/providers/codex.ts +16 -19
  49. package/src/providers/google-vertex-budget-contracts.ts +24 -14
  50. package/src/providers/google-vertex-budget.ts +15 -13
  51. package/src/providers/google-vertex-contracts.ts +36 -24
  52. package/src/providers/openrouter-contracts.ts +49 -51
  53. package/src/providers/openrouter.ts +21 -15
  54. package/src/providers/telemetry-sources.ts +13 -10
  55. package/src/router.ts +92 -43
  56. package/src/service.ts +93 -32
  57. package/src/session-identity-service.ts +10 -2
  58. package/src/state.ts +4 -10
  59. package/src/vehicle-registration.ts +154 -0
@@ -6,10 +6,10 @@ import {
6
6
  BENCHMARK_MAX_TEXT_CHARACTERS,
7
7
  BENCHMARK_REFRESH_INTERVAL_MS,
8
8
  } from "../constants.ts";
9
- import { METRIC_UNITS, type MetricUnit } from "./metric.ts";
10
9
  import type { BenchmarkController } from "../ports/benchmark-controller.ts";
11
10
  import type { BenchmarkSource as BenchmarkSourcePort } from "../ports/benchmark-source.ts";
12
11
  import type { BenchmarkStore } from "../ports/benchmark-store.ts";
12
+ import { METRIC_UNITS, type MetricUnit } from "./metric.ts";
13
13
 
14
14
  export type BenchmarkSourceType = "creator" | "marketplace" | "independent" | "operational" | "preference" | "local";
15
15
 
@@ -112,7 +112,9 @@ export function normalizeModelIdentity(provider: string, model: string, aliases:
112
112
  const normalizedModel = identityPart(model, "model", true);
113
113
  const version = VERSION_SUFFIX.exec(normalizedModel)?.[1] ?? null;
114
114
  const canonical = `${normalizedProvider}/${normalizedModel}`;
115
- const normalizedAliases = [...new Set(aliases.map((alias) => boundedText(alias, "alias", BENCHMARK_IDENTITY_MAX_CHARACTERS).toLowerCase()))]
115
+ const normalizedAliases = [
116
+ ...new Set(aliases.map((alias) => boundedText(alias, "alias", BENCHMARK_IDENTITY_MAX_CHARACTERS).toLowerCase())),
117
+ ]
116
118
  .filter((alias) => alias !== canonical)
117
119
  .sort();
118
120
  return { provider: normalizedProvider, model: normalizedModel, version, canonical, aliases: normalizedAliases };
@@ -121,9 +123,10 @@ export function normalizeModelIdentity(provider: string, model: string, aliases:
121
123
  function validateIdentity(value: unknown): ModelIdentity {
122
124
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("model identity is required");
123
125
  const input = value as Record<string, unknown>;
124
- if (!Array.isArray(input["aliases"]) || !input["aliases"].every((alias) => typeof alias === "string")) throw new Error("model aliases are invalid");
125
- const normalized = normalizeModelIdentity(String(input["provider"] ?? ""), String(input["model"] ?? ""), input["aliases"] as string[]);
126
- if (input["canonical"] !== normalized.canonical || input["version"] !== normalized.version) throw new Error("model identity is not normalized");
126
+ if (!Array.isArray(input.aliases) || !input.aliases.every((alias) => typeof alias === "string"))
127
+ throw new Error("model aliases are invalid");
128
+ const normalized = normalizeModelIdentity(String(input.provider ?? ""), String(input.model ?? ""), input.aliases as string[]);
129
+ if (input.canonical !== normalized.canonical || input.version !== normalized.version) throw new Error("model identity is not normalized");
127
130
  return normalized;
128
131
  }
129
132
 
@@ -136,26 +139,31 @@ function validateTimestamp(value: unknown, name: string, nullable = false): numb
136
139
  function validateProvenance(value: unknown): BenchmarkProvenance {
137
140
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("provenance is required");
138
141
  const input = value as Record<string, unknown>;
139
- const sourceId = identityPart(String(input["sourceId"] ?? ""), "source id");
140
- if (!SOURCE_TYPES.has(input["sourceType"] as BenchmarkSourceType)) throw new Error("source type is invalid");
142
+ const sourceId = identityPart(String(input.sourceId ?? ""), "source id");
143
+ if (!SOURCE_TYPES.has(input.sourceType as BenchmarkSourceType)) throw new Error("source type is invalid");
141
144
  let url: URL;
142
- try { url = new URL(boundedText(input["url"], "source URL")); } catch { throw new Error("source URL is invalid"); }
145
+ try {
146
+ url = new URL(boundedText(input.url, "source URL"));
147
+ } catch {
148
+ throw new Error("source URL is invalid");
149
+ }
143
150
  if (url.protocol !== "https:") throw new Error("source URL must use HTTPS");
144
- const confidence = input["confidence"];
145
- if (typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1) throw new Error("confidence must be between zero and one");
146
- const retrievedAt = validateTimestamp(input["retrievedAt"], "retrieval time") as number;
147
- const freshUntil = validateTimestamp(input["freshUntil"], "freshness deadline") as number;
151
+ const confidence = input.confidence;
152
+ if (typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1)
153
+ throw new Error("confidence must be between zero and one");
154
+ const retrievedAt = validateTimestamp(input.retrievedAt, "retrieval time") as number;
155
+ const freshUntil = validateTimestamp(input.freshUntil, "freshness deadline") as number;
148
156
  if (freshUntil < retrievedAt) throw new Error("freshness deadline precedes retrieval");
149
157
  return {
150
158
  sourceId,
151
- sourceType: input["sourceType"] as BenchmarkSourceType,
152
- publisher: boundedText(input["publisher"], "publisher"),
159
+ sourceType: input.sourceType as BenchmarkSourceType,
160
+ publisher: boundedText(input.publisher, "publisher"),
153
161
  url: url.toString(),
154
- revision: boundedText(input["revision"], "revision"),
155
- publishedAt: validateTimestamp(input["publishedAt"], "publication time", true),
162
+ revision: boundedText(input.revision, "revision"),
163
+ publishedAt: validateTimestamp(input.publishedAt, "publication time", true),
156
164
  retrievedAt,
157
165
  freshUntil,
158
- license: boundedText(input["license"], "license"),
166
+ license: boundedText(input.license, "license"),
159
167
  confidence,
160
168
  };
161
169
  }
@@ -170,16 +178,16 @@ function validateMethodology(value: unknown): BenchmarkObservation["methodology"
170
178
  export function validateBenchmarkObservation(value: unknown): BenchmarkObservation {
171
179
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("benchmark observation must be an object");
172
180
  const input = value as Record<string, unknown>;
173
- const numericValue = input["value"];
181
+ const numericValue = input.value;
174
182
  if (typeof numericValue !== "number" || !Number.isFinite(numericValue)) throw new Error("benchmark value must be finite");
175
- if (!METRIC_UNITS.includes(input["unit"] as MetricUnit)) throw new Error("benchmark unit is not supported");
183
+ if (!METRIC_UNITS.includes(input.unit as MetricUnit)) throw new Error("benchmark unit is not supported");
176
184
  return {
177
- model: validateIdentity(input["model"]),
178
- dimension: identityPart(String(input["dimension"] ?? ""), "dimension"),
185
+ model: validateIdentity(input.model),
186
+ dimension: identityPart(String(input.dimension ?? ""), "dimension"),
179
187
  value: numericValue,
180
- unit: input["unit"] as MetricUnit,
181
- provenance: validateProvenance(input["provenance"]),
182
- methodology: validateMethodology(input["methodology"]),
188
+ unit: input.unit as MetricUnit,
189
+ provenance: validateProvenance(input.provenance),
190
+ methodology: validateMethodology(input.methodology),
183
191
  };
184
192
  }
185
193
 
@@ -188,9 +196,12 @@ function validateSourceSnapshot(value: BenchmarkSourceSnapshot, expectedSourceId
188
196
  if (sourceId !== expectedSourceId) throw new Error("source snapshot identity mismatch");
189
197
  const snapshotId = boundedText(value.snapshotId, "snapshot id", BENCHMARK_IDENTITY_MAX_CHARACTERS);
190
198
  const retrievedAt = validateTimestamp(value.retrievedAt, "retrieval time") as number;
191
- if (!Array.isArray(value.observations) || value.observations.length > BENCHMARK_MAX_OBSERVATIONS_PER_SNAPSHOT) throw new Error("source snapshot exceeds the observation limit");
199
+ if (!Array.isArray(value.observations) || value.observations.length > BENCHMARK_MAX_OBSERVATIONS_PER_SNAPSHOT)
200
+ throw new Error("source snapshot exceeds the observation limit");
192
201
  const observations = value.observations.map(validateBenchmarkObservation);
193
- if (observations.some((observation) => observation.provenance.sourceId !== sourceId || observation.provenance.retrievedAt !== retrievedAt)) {
202
+ if (
203
+ observations.some((observation) => observation.provenance.sourceId !== sourceId || observation.provenance.retrievedAt !== retrievedAt)
204
+ ) {
194
205
  throw new Error("source snapshot provenance mismatch");
195
206
  }
196
207
  return { sourceId, snapshotId, retrievedAt, observations };
@@ -223,19 +234,36 @@ export class BenchmarkCatalog implements BenchmarkController {
223
234
 
224
235
  async refresh(force = false): Promise<BenchmarkRefreshResult> {
225
236
  const now = this.clock();
226
- await Promise.all(this.sources.map(async (source) => {
227
- const prior = this.states.get(source.id)!;
228
- if (!force && prior.lastAttemptAt !== null && now - prior.lastAttemptAt < this.refreshIntervalMs) return;
229
- this.states.set(source.id, { ...prior, lastAttemptAt: now });
230
- try {
231
- const snapshot = validateSourceSnapshot(await source.fetch(), source.id);
232
- const published = this.store.publish(snapshot.sourceId, snapshot.snapshotId, snapshot.observations);
233
- this.states.set(source.id, { id: source.id, ok: true, hasEvidence: true, lastAttemptAt: now, lastSuccessAt: published.retrievedAt, observations: published.observations.length });
234
- } catch {
235
- const evidence = this.store.latest(source.id);
236
- this.states.set(source.id, { id: source.id, ok: false, hasEvidence: evidence !== null, lastAttemptAt: now, lastSuccessAt: evidence?.retrievedAt ?? prior.lastSuccessAt, observations: evidence?.observations.length ?? prior.observations, error: "source refresh failed" });
237
- }
238
- }));
237
+ await Promise.all(
238
+ this.sources.map(async (source) => {
239
+ const prior = this.states.get(source.id)!;
240
+ if (!force && prior.lastAttemptAt !== null && now - prior.lastAttemptAt < this.refreshIntervalMs) return;
241
+ this.states.set(source.id, { ...prior, lastAttemptAt: now });
242
+ try {
243
+ const snapshot = validateSourceSnapshot(await source.fetch(), source.id);
244
+ const published = this.store.publish(snapshot.sourceId, snapshot.snapshotId, snapshot.observations);
245
+ this.states.set(source.id, {
246
+ id: source.id,
247
+ ok: true,
248
+ hasEvidence: true,
249
+ lastAttemptAt: now,
250
+ lastSuccessAt: published.retrievedAt,
251
+ observations: published.observations.length,
252
+ });
253
+ } catch {
254
+ const evidence = this.store.latest(source.id);
255
+ this.states.set(source.id, {
256
+ id: source.id,
257
+ ok: false,
258
+ hasEvidence: evidence !== null,
259
+ lastAttemptAt: now,
260
+ lastSuccessAt: evidence?.retrievedAt ?? prior.lastSuccessAt,
261
+ observations: evidence?.observations.length ?? prior.observations,
262
+ error: "source refresh failed",
263
+ });
264
+ }
265
+ }),
266
+ );
239
267
  return { observedAt: now, sources: this.status().sources };
240
268
  }
241
269
 
@@ -251,7 +279,11 @@ export class BenchmarkCatalog implements BenchmarkController {
251
279
  const limit = Math.max(1, Math.min(BENCHMARK_MAX_QUERY_LIMIT, requestedLimit));
252
280
  const model = input.model?.trim().toLowerCase();
253
281
  const dimension = input.dimension?.trim().toLowerCase();
254
- const matched = snapshot.observations.filter((observation) => (!model || observation.model.canonical === model || observation.model.aliases.includes(model)) && (!dimension || observation.dimension === dimension));
282
+ const matched = snapshot.observations.filter(
283
+ (observation) =>
284
+ (!model || observation.model.canonical === model || observation.model.aliases.includes(model)) &&
285
+ (!dimension || observation.dimension === dimension),
286
+ );
255
287
  const freshUntil = Math.min(...snapshot.observations.map((observation) => observation.provenance.freshUntil));
256
288
  return {
257
289
  ...structuredClone(snapshot),
@@ -1,8 +1,4 @@
1
- import {
2
- CODEX_ERROR_MESSAGE_LIMIT,
3
- CODEX_RETRY_AFTER_MAX_MS,
4
- MILLISECONDS_PER_SECOND,
5
- } from "../constants.ts";
1
+ import { CODEX_ERROR_MESSAGE_LIMIT, CODEX_RETRY_AFTER_MAX_MS, MILLISECONDS_PER_SECOND } from "../constants.ts";
6
2
 
7
3
  export type CodexFailureKind =
8
4
  | "concurrency"
@@ -57,10 +53,12 @@ export class CodexRecoveryPolicy {
57
53
  private readonly random: () => number = Math.random,
58
54
  ) {
59
55
  if (!Number.isFinite(options.baseDelayMs) || options.baseDelayMs < 0) throw new Error("baseDelayMs must be non-negative");
60
- if (!Number.isFinite(options.maxDelayMs) || options.maxDelayMs < options.baseDelayMs) throw new Error("maxDelayMs must be at least baseDelayMs");
56
+ if (!Number.isFinite(options.maxDelayMs) || options.maxDelayMs < options.baseDelayMs)
57
+ throw new Error("maxDelayMs must be at least baseDelayMs");
61
58
  if (!Number.isInteger(options.maxAttempts) || options.maxAttempts < 1) throw new Error("maxAttempts must be a positive integer");
62
59
  if (!Number.isFinite(options.attemptWindowMs) || options.attemptWindowMs <= 0) throw new Error("attemptWindowMs must be positive");
63
- if (!Number.isFinite(options.jitterRatio) || options.jitterRatio < 0 || options.jitterRatio > 1) throw new Error("jitterRatio must be between 0 and 1");
60
+ if (!Number.isFinite(options.jitterRatio) || options.jitterRatio < 0 || options.jitterRatio > 1)
61
+ throw new Error("jitterRatio must be between 0 and 1");
64
62
  }
65
63
 
66
64
  observeFailure(failure: CodexFailure, now: number): void {
@@ -88,13 +86,15 @@ export class CodexRecoveryPolicy {
88
86
  this.normalizeWindow(now);
89
87
  if (!this.pendingFailure) return { action: "wait", reason: "no transient Codex failure is pending" };
90
88
  if (this.attempts >= this.options.maxAttempts) {
91
- return { action: "exhausted", reason: `${this.options.maxAttempts} recovery attempts reached within ${this.options.attemptWindowMs}ms` };
89
+ return {
90
+ action: "exhausted",
91
+ reason: `${this.options.maxAttempts} recovery attempts reached within ${this.options.attemptWindowMs}ms`,
92
+ };
92
93
  }
93
- const base = this.pendingFailure.retryAfterMs
94
- ?? this.options.baseDelayMs * (2 ** this.attempts);
94
+ const base = this.pendingFailure.retryAfterMs ?? this.options.baseDelayMs * 2 ** this.attempts;
95
95
  const sample = this.random();
96
96
  const unit = Number.isFinite(sample) ? Math.min(1, Math.max(0, sample)) : 0;
97
- const multiplier = 1 + ((unit * 2) - 1) * this.options.jitterRatio;
97
+ const multiplier = 1 + (unit * 2 - 1) * this.options.jitterRatio;
98
98
  const jittered = Math.max(0, Math.round(base * multiplier));
99
99
  const delayMs = this.pendingFailure.retryAfterMs === undefined ? jittered : Math.max(base, jittered);
100
100
  return {
@@ -135,9 +135,7 @@ export class CodexRecoveryPolicy {
135
135
  }
136
136
 
137
137
  function asRecord(value: unknown): Record<string, unknown> | undefined {
138
- return typeof value === "object" && value !== null && !Array.isArray(value)
139
- ? value as Record<string, unknown>
140
- : undefined;
138
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
141
139
  }
142
140
 
143
141
  function firstString(...values: unknown[]): string | undefined {
@@ -160,15 +158,15 @@ function matches(value: string, patterns: readonly string[]): boolean {
160
158
 
161
159
  export function classifyCodexFailure(value: unknown, metadata: CodexFailureMetadata = {}): CodexFailure {
162
160
  const root = asRecord(value);
163
- const detail = asRecord(root?.["detail"]);
164
- const nestedError = asRecord(root?.["error"]);
165
- const code = firstString(detail?.["code"], detail?.["error_code"], nestedError?.["code"], root?.["code"]);
166
- const source = firstString(detail?.["source"], nestedError?.["source"], root?.["source"]);
161
+ const detail = asRecord(root?.detail);
162
+ const nestedError = asRecord(root?.error);
163
+ const code = firstString(detail?.code, detail?.error_code, nestedError?.code, root?.code);
164
+ const source = firstString(detail?.source, nestedError?.source, root?.source);
167
165
  const rawMessage = firstString(
168
- detail?.["message"],
169
- typeof root?.["error"] === "string" ? root["error"] : undefined,
170
- nestedError?.["message"],
171
- root?.["message"],
166
+ detail?.message,
167
+ typeof root?.error === "string" ? root.error : undefined,
168
+ nestedError?.message,
169
+ root?.message,
172
170
  typeof value === "string" ? value : undefined,
173
171
  );
174
172
  const message = rawMessage?.slice(0, CODEX_ERROR_MESSAGE_LIMIT);
@@ -183,16 +181,27 @@ export function classifyCodexFailure(value: unknown, metadata: CodexFailureMetad
183
181
  if (matches(evidence, ["insufficient_quota", "quota exceeded", "out of credits", "billing"])) {
184
182
  return { kind: "quota", transient: false, ...base };
185
183
  }
186
- if (metadata.status === 401 || metadata.status === 403 || matches(evidence, ["invalid_api_key", "authentication", "unauthorized", "permission_denied"])) {
184
+ if (
185
+ metadata.status === 401 ||
186
+ metadata.status === 403 ||
187
+ matches(evidence, ["invalid_api_key", "authentication", "unauthorized", "permission_denied"])
188
+ ) {
187
189
  return { kind: "authentication", transient: false, ...base };
188
190
  }
189
- if (matches(evidence, ["invalid_prompt", "invalid_request", "context_length_exceeded"]) || metadata.status === 400 || metadata.status === 422) {
191
+ if (
192
+ matches(evidence, ["invalid_prompt", "invalid_request", "context_length_exceeded"]) ||
193
+ metadata.status === 400 ||
194
+ metadata.status === 422
195
+ ) {
190
196
  return { kind: "invalid-request", transient: false, ...base };
191
197
  }
192
198
  if (matches(evidence, ["concurrency_limit", "too many concurrent requests", "throttled"])) {
193
199
  return { kind: "concurrency", transient: true, ...base };
194
200
  }
195
- if (matches(evidence, ["server_is_overloaded", "slow_down", "service unavailable", "overloaded"]) || (metadata.status !== undefined && metadata.status >= 500 && metadata.status <= 599)) {
201
+ if (
202
+ matches(evidence, ["server_is_overloaded", "slow_down", "service unavailable", "overloaded"]) ||
203
+ (metadata.status !== undefined && metadata.status >= 500 && metadata.status <= 599)
204
+ ) {
196
205
  return { kind: "overload", transient: true, ...base };
197
206
  }
198
207
  if (metadata.status === 429 || matches(evidence, ["rate_limit_exceeded", "rate limit", "too many requests"])) {
@@ -17,8 +17,8 @@ import {
17
17
  CONTEXT_HUB_CONTRIBUTION_MAX_AGE_MS,
18
18
  CONTEXT_HUB_CONTRIBUTION_SCHEMA,
19
19
  CONTEXT_HUB_ITEM_LABEL_MAX_CHARACTERS,
20
- CONTEXT_HUB_MAX_ITEMS_PER_SEGMENT,
21
20
  CONTEXT_HUB_MAX_ITEM_DEPTH,
21
+ CONTEXT_HUB_MAX_ITEMS_PER_SEGMENT,
22
22
  CONTEXT_HUB_PRODUCER_NAME_MAX_CHARACTERS,
23
23
  CONTEXT_HUB_SEGMENT_KEY_MAX_CHARACTERS,
24
24
  CONTEXT_HUB_SEGMENT_LABEL_MAX_CHARACTERS,
@@ -58,12 +58,14 @@ export interface ContextContribution {
58
58
  }
59
59
 
60
60
  function nonEmptyString(value: unknown, name: string, maxLength: number): string {
61
- if (typeof value !== "string" || value.length === 0 || value.length > maxLength) throw new Error(`${name} must be a non-empty string of at most ${maxLength} characters`);
61
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLength)
62
+ throw new Error(`${name} must be a non-empty string of at most ${maxLength} characters`);
62
63
  return value;
63
64
  }
64
65
 
65
66
  function boundedInteger(value: unknown, name: string): number {
66
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new Error(`${name} must be a bounded non-negative integer`);
67
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
68
+ throw new Error(`${name} must be a bounded non-negative integer`);
67
69
  return value;
68
70
  }
69
71
 
@@ -76,15 +78,16 @@ function validateSegmentItem(value: unknown, depth: number): ContextSegmentItem
76
78
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("context segment item must be an object");
77
79
  const input = value as Record<string, unknown>;
78
80
  for (const key of Object.keys(input)) {
79
- if (key !== "label" && key !== "estimatedTokens" && key !== "children") throw new Error(`context segment item contains unexpected field: ${key}`);
81
+ if (key !== "label" && key !== "estimatedTokens" && key !== "children")
82
+ throw new Error(`context segment item contains unexpected field: ${key}`);
80
83
  }
81
84
  const item: ContextSegmentItem = {
82
- label: nonEmptyString(input["label"], "item.label", CONTEXT_HUB_ITEM_LABEL_MAX_CHARACTERS),
83
- estimatedTokens: boundedInteger(input["estimatedTokens"], "item.estimatedTokens"),
85
+ label: nonEmptyString(input.label, "item.label", CONTEXT_HUB_ITEM_LABEL_MAX_CHARACTERS),
86
+ estimatedTokens: boundedInteger(input.estimatedTokens, "item.estimatedTokens"),
84
87
  };
85
- if (input["children"] !== undefined) {
86
- if (!Array.isArray(input["children"])) throw new Error("item.children must be an array");
87
- item.children = input["children"].map((child) => validateSegmentItem(child, depth + 1));
88
+ if (input.children !== undefined) {
89
+ if (!Array.isArray(input.children)) throw new Error("item.children must be an array");
90
+ item.children = input.children.map((child) => validateSegmentItem(child, depth + 1));
88
91
  }
89
92
  return item;
90
93
  }
@@ -98,22 +101,23 @@ export function validateContextSegment(value: unknown): ContextSegment {
98
101
  throw new Error(`context segment contains unexpected field: ${key}`);
99
102
  }
100
103
  }
101
- const confidence = input["confidence"];
104
+ const confidence = input.confidence;
102
105
  if (typeof confidence !== "string" || !CONTEXT_HUB_CONFIDENCE_TIERS.includes(confidence as ContextConfidenceTier)) {
103
106
  throw new Error(`confidence must be one of ${CONTEXT_HUB_CONFIDENCE_TIERS.join(", ")}`);
104
107
  }
105
- if (input["unknown"] !== undefined && typeof input["unknown"] !== "boolean") throw new Error("segment.unknown must be a boolean");
108
+ if (input.unknown !== undefined && typeof input.unknown !== "boolean") throw new Error("segment.unknown must be a boolean");
106
109
  const segment: ContextSegment = {
107
- key: nonEmptyString(input["key"], "segment.key", CONTEXT_HUB_SEGMENT_KEY_MAX_CHARACTERS),
108
- label: nonEmptyString(input["label"], "segment.label", CONTEXT_HUB_SEGMENT_LABEL_MAX_CHARACTERS),
109
- estimatedTokens: boundedInteger(input["estimatedTokens"], "segment.estimatedTokens"),
110
+ key: nonEmptyString(input.key, "segment.key", CONTEXT_HUB_SEGMENT_KEY_MAX_CHARACTERS),
111
+ label: nonEmptyString(input.label, "segment.label", CONTEXT_HUB_SEGMENT_LABEL_MAX_CHARACTERS),
112
+ estimatedTokens: boundedInteger(input.estimatedTokens, "segment.estimatedTokens"),
110
113
  confidence: confidence as ContextConfidenceTier,
111
- ...(input["unknown"] !== undefined ? { unknown: input["unknown"] as boolean } : {}),
114
+ ...(input.unknown !== undefined ? { unknown: input.unknown as boolean } : {}),
112
115
  };
113
- if (input["items"] !== undefined) {
114
- if (!Array.isArray(input["items"])) throw new Error("segment.items must be an array");
115
- const items = input["items"].map((item) => validateSegmentItem(item, 1));
116
- if (countItems(items) > CONTEXT_HUB_MAX_ITEMS_PER_SEGMENT) throw new Error(`segment.items exceeds ${CONTEXT_HUB_MAX_ITEMS_PER_SEGMENT} total items`);
116
+ if (input.items !== undefined) {
117
+ if (!Array.isArray(input.items)) throw new Error("segment.items must be an array");
118
+ const items = input.items.map((item) => validateSegmentItem(item, 1));
119
+ if (countItems(items) > CONTEXT_HUB_MAX_ITEMS_PER_SEGMENT)
120
+ throw new Error(`segment.items exceeds ${CONTEXT_HUB_MAX_ITEMS_PER_SEGMENT} total items`);
117
121
  segment.items = items;
118
122
  }
119
123
  return segment;
@@ -125,16 +129,17 @@ const CONTRIBUTION_FIELDS = new Set(["schema", "observedAt", "sequence", "produc
125
129
  export function validateContextContribution(value: unknown, now = Date.now()): ContextContribution {
126
130
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("context contribution must be an object");
127
131
  const input = value as Record<string, unknown>;
128
- for (const key of Object.keys(input)) if (!CONTRIBUTION_FIELDS.has(key)) throw new Error(`context contribution contains unexpected field: ${key}`);
129
- if (input["schema"] !== CONTEXT_HUB_CONTRIBUTION_SCHEMA) throw new Error("context contribution schema is not supported");
130
- const observedAt = boundedInteger(input["observedAt"], "observedAt");
132
+ for (const key of Object.keys(input))
133
+ if (!CONTRIBUTION_FIELDS.has(key)) throw new Error(`context contribution contains unexpected field: ${key}`);
134
+ if (input.schema !== CONTEXT_HUB_CONTRIBUTION_SCHEMA) throw new Error("context contribution schema is not supported");
135
+ const observedAt = boundedInteger(input.observedAt, "observedAt");
131
136
  if (Math.abs(now - observedAt) > CONTEXT_HUB_CONTRIBUTION_MAX_AGE_MS) throw new Error("context contribution is stale");
132
137
  return {
133
138
  schema: CONTEXT_HUB_CONTRIBUTION_SCHEMA,
134
139
  observedAt,
135
- sequence: boundedInteger(input["sequence"], "sequence"),
136
- producerName: nonEmptyString(input["producerName"], "producerName", CONTEXT_HUB_PRODUCER_NAME_MAX_CHARACTERS),
137
- segment: validateContextSegment(input["segment"]),
140
+ sequence: boundedInteger(input.sequence, "sequence"),
141
+ producerName: nonEmptyString(input.producerName, "producerName", CONTEXT_HUB_PRODUCER_NAME_MAX_CHARACTERS),
142
+ segment: validateContextSegment(input.segment),
138
143
  };
139
144
  }
140
145
 
@@ -199,7 +204,11 @@ export function computeToolSchemaLedger(tools: readonly ToolLedgerEntry[]): Tool
199
204
  for (const tool of tools) {
200
205
  const source = tool.sourceInfo?.source && tool.sourceInfo.source.length > 0 ? tool.sourceInfo.source : "unknown";
201
206
  const characters = toolCharacters(tool);
202
- const usage: ToolLedgerToolUsage = { name: tool.name, characters, estimatedTokens: Math.ceil(characters / CONTEXT_ESTIMATE_CHARACTERS_PER_TOKEN) };
207
+ const usage: ToolLedgerToolUsage = {
208
+ name: tool.name,
209
+ characters,
210
+ estimatedTokens: Math.ceil(characters / CONTEXT_ESTIMATE_CHARACTERS_PER_TOKEN),
211
+ };
203
212
  const existing = bySource.get(source);
204
213
  if (existing) existing.push(usage);
205
214
  else bySource.set(source, [usage]);