@danypops/jittor 0.16.2 → 0.17.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 (94) hide show
  1. package/README.md +38 -13
  2. package/docs/COMPACTION_EFFECTIVENESS.md +34 -0
  3. package/docs/CONTEXT_SNAPSHOTS.md +53 -0
  4. package/docs/MODEL_CATALOG.md +43 -0
  5. package/docs/OTLP_EXPORT.md +63 -0
  6. package/docs/PROVIDER_RESEARCH.md +1 -1
  7. package/docs/TOKEN_MEASUREMENT.md +53 -0
  8. package/docs/USAGE_BACKFILL.md +35 -0
  9. package/package.json +4 -3
  10. package/src/{providers/anthropic-contracts.ts → anthropic/rate-limits.ts} +1 -1
  11. package/src/{adapters/artificial-analysis-direct-source.ts → artificial-analysis/benchmark-source.ts} +3 -3
  12. package/src/cli-commands/backfill.ts +51 -0
  13. package/src/cli-commands/benchmarks.ts +4 -4
  14. package/src/cli-commands/catalog.ts +123 -0
  15. package/src/cli-commands/compaction.ts +1 -1
  16. package/src/cli-commands/context.ts +77 -4
  17. package/src/cli-commands/export.ts +30 -0
  18. package/src/cli-commands/metrics.ts +8 -2
  19. package/src/cli-commands/op.ts +1 -1
  20. package/src/cli-commands/route-args.ts +2 -2
  21. package/src/cli-commands/router.ts +2 -2
  22. package/src/cli-commands/support.ts +2 -2
  23. package/src/cli.ts +54 -21
  24. package/src/{providers/codex-contracts.ts → codex/contracts.ts} +1 -1
  25. package/src/codex/source.ts +53 -0
  26. package/src/{providers/codex.ts → codex/telemetry.ts} +2 -2
  27. package/src/constants.ts +18 -0
  28. package/src/daemon.ts +53 -23
  29. package/src/{providers/google-vertex-budget-contracts.ts → google-vertex/budget-contracts.ts} +3 -3
  30. package/src/{providers/google-vertex-budget.ts → google-vertex/budget-telemetry.ts} +6 -6
  31. package/src/{providers/google-vertex-contracts.ts → google-vertex/failures.ts} +1 -1
  32. package/src/google-vertex/source.ts +35 -0
  33. package/src/index.ts +135 -55
  34. package/src/{adapters/lmarena-hf-source.ts → lmarena/benchmark-source.ts} +3 -3
  35. package/src/observability/budget.ts +14 -0
  36. package/src/observability/context-delta.ts +309 -0
  37. package/src/{domain → observability}/context-hub.ts +19 -2
  38. package/src/observability/context-snapshot-history.ts +281 -0
  39. package/src/{domain → observability}/context-telemetry.ts +232 -4
  40. package/src/observability/model-identity.ts +42 -0
  41. package/src/{domain → observability}/model-observation.ts +26 -11
  42. package/src/{ports/metric-store.ts → observability/store.ts} +2 -2
  43. package/src/{ports → observability}/telemetry-source.ts +2 -2
  44. package/src/observability/token-measurement.ts +194 -0
  45. package/src/observability/usage-import.ts +122 -0
  46. package/src/openai/token-counter.ts +63 -0
  47. package/src/{adapters/openrouter-benchmark-source.ts → openrouter/benchmark-source.ts} +3 -3
  48. package/src/{providers/openrouter-contracts.ts → openrouter/contracts.ts} +1 -1
  49. package/src/{adapters/openrouter-design-arena-source.ts → openrouter/design-arena-source.ts} +4 -4
  50. package/src/openrouter/source.ts +20 -0
  51. package/src/{providers/openrouter.ts → openrouter/telemetry.ts} +2 -2
  52. package/src/{domain → optimization/model-selection}/benchmark.ts +9 -27
  53. package/src/optimization/model-selection/catalog-store.ts +97 -0
  54. package/src/optimization/model-selection/catalog.ts +478 -0
  55. package/src/{ports/benchmark-controller.ts → optimization/model-selection/controller.ts} +1 -1
  56. package/src/{adapters/metric-benchmark-store.ts → optimization/model-selection/observation-store.ts} +5 -5
  57. package/src/{domain/model-ranking-service.ts → optimization/model-selection/ranker.ts} +6 -12
  58. package/src/{domain/model-ranking.ts → optimization/model-selection/ranking.ts} +8 -2
  59. package/src/{ports/benchmark-source.ts → optimization/model-selection/source.ts} +1 -1
  60. package/src/{ports/benchmark-store.ts → optimization/model-selection/store.ts} +1 -1
  61. package/src/{domain/codex-recovery.ts → optimization/recovery/codex.ts} +1 -1
  62. package/src/{ports/router-controller.ts → optimization/routing/controller.ts} +1 -1
  63. package/src/{policy.ts → optimization/routing/policy.ts} +4 -14
  64. package/src/{router.ts → optimization/routing/router.ts} +4 -10
  65. package/src/otlp/exporter.ts +354 -0
  66. package/src/pi/session-usage-source.ts +203 -0
  67. package/src/{session-identity-service.ts → sessions/identity.ts} +2 -2
  68. package/src/{operations/session-scope.ts → sessions/router-authorization.ts} +1 -1
  69. package/src/{db.ts → sqlite/database.ts} +14 -1
  70. package/src/{adapters/sqlite-metric-store.ts → sqlite/metric-store.ts} +4 -4
  71. package/src/{adapters/sqlite-session-identity-store.ts → sqlite/session-store.ts} +1 -1
  72. package/src/sqlite/usage-import-store.ts +133 -0
  73. package/src/telemetry-export/exporter.ts +84 -0
  74. package/src/{operations → vehicle}/benchmark-operations.ts +3 -3
  75. package/src/vehicle/catalog-operations.ts +10 -0
  76. package/src/{client.ts → vehicle/client.ts} +1 -1
  77. package/src/{operations → vehicle}/context-operations.ts +13 -5
  78. package/src/vehicle/export-operations.ts +12 -0
  79. package/src/{operations/metrics-operations.ts → vehicle/metric-operations.ts} +4 -4
  80. package/src/{operations → vehicle}/model-ranking-operations.ts +3 -3
  81. package/src/{vehicle-registration.ts → vehicle/registration.ts} +14 -4
  82. package/src/{operations/router-operations.ts → vehicle/routing-operations.ts} +4 -4
  83. package/src/{service.ts → vehicle/service.ts} +108 -26
  84. package/src/{operations/session-identity-operations.ts → vehicle/session-operations.ts} +3 -3
  85. package/src/vehicle/usage-import-operations.ts +10 -0
  86. package/src/providers/telemetry-sources.ts +0 -108
  87. /package/src/{providers/google-adc-auth.ts → google-vertex/auth.ts} +0 -0
  88. /package/src/{domain → observability}/metric.ts +0 -0
  89. /package/src/{domain → observability}/task-cost.ts +0 -0
  90. /package/src/{domain → observability}/task-focus.ts +0 -0
  91. /package/src/{domain → observability}/usage.ts +0 -0
  92. /package/src/{config.ts → optimization/routing/config.ts} +0 -0
  93. /package/src/{ports/session-identity-store.ts → sessions/store.ts} +0 -0
  94. /package/src/{operations/types.ts → vehicle/operation-types.ts} +0 -0
@@ -0,0 +1,123 @@
1
+ import { MODEL_CATALOG_MAX_QUERY_LIMIT } from "../constants.ts";
2
+ import type { ModelCatalogQuery, ModelCatalogQueryResult, ModelCatalogStatus } from "../optimization/model-selection/catalog.ts";
3
+ import { type CliDependencies, humanField } from "./support.ts";
4
+
5
+ export const CATALOG_USAGE_LINES = [
6
+ " catalog <status|refresh|list> [--provider <id>] [--model <id>] [--limit <n>] [--context-tokens <n>] [--input-tokens <n>] [--output-tokens <n>] [--input-price <usd-per-million>] [--output-price <usd-per-million>] [--force] [--json]",
7
+ ];
8
+
9
+ interface CatalogArgs {
10
+ action: "status" | "refresh" | "list";
11
+ json: boolean;
12
+ force: boolean;
13
+ query: ModelCatalogQuery;
14
+ }
15
+
16
+ function parsePositive(raw: string, integer: boolean): number | null {
17
+ const value = Number(raw);
18
+ if (!Number.isFinite(value) || value < 0 || (integer && !Number.isSafeInteger(value))) return null;
19
+ return value;
20
+ }
21
+
22
+ function parseCatalogArgs(action: string | undefined, args: string[]): CatalogArgs | null {
23
+ if (action !== "status" && action !== "refresh" && action !== "list") return null;
24
+ let json = false;
25
+ let force = false;
26
+ const query: ModelCatalogQuery = {};
27
+ for (let index = 0; index < args.length; index += 1) {
28
+ const argument = args[index];
29
+ if (argument === "--json") {
30
+ json = true;
31
+ continue;
32
+ }
33
+ if (argument === "--force" && action === "refresh") {
34
+ force = true;
35
+ continue;
36
+ }
37
+ if (action !== "list") return null;
38
+ if (
39
+ !argument ||
40
+ ![
41
+ "--provider",
42
+ "--model",
43
+ "--limit",
44
+ "--context-tokens",
45
+ "--input-tokens",
46
+ "--output-tokens",
47
+ "--input-price",
48
+ "--output-price",
49
+ ].includes(argument)
50
+ )
51
+ return null;
52
+ const raw = args[++index];
53
+ if (!raw) return null;
54
+ if (argument === "--provider") query.provider = raw;
55
+ else if (argument === "--model") query.model = raw;
56
+ else if (argument === "--limit") {
57
+ const value = parsePositive(raw, true);
58
+ if (value === null || value < 1 || value > MODEL_CATALOG_MAX_QUERY_LIMIT) return null;
59
+ query.limit = value;
60
+ } else {
61
+ const integer = argument.endsWith("-tokens");
62
+ const value = parsePositive(raw, integer);
63
+ if (value === null) return null;
64
+ const key = {
65
+ "--context-tokens": "contextTokens",
66
+ "--input-tokens": "inputTokens",
67
+ "--output-tokens": "outputTokens",
68
+ "--input-price": "inputPrice",
69
+ "--output-price": "outputPrice",
70
+ }[argument] as "contextTokens" | "inputTokens" | "outputTokens" | "inputPrice" | "outputPrice";
71
+ if (!query.overrides) query.overrides = {};
72
+ query.overrides[key] = value;
73
+ }
74
+ }
75
+ return { action, json, force, query };
76
+ }
77
+
78
+ export function formatCatalogStatus(status: ModelCatalogStatus): string {
79
+ if (!status.configured && !status.hasSnapshot) return "Model catalog: not configured (set JITTOR_MODELS_DEV_CATALOG=1)";
80
+ const state = status.ok === null ? "not refreshed" : status.ok ? "ready" : "refresh failed; last-good snapshot retained";
81
+ return `Model catalog: ${state} · ${status.entries.toLocaleString()} entries · revision ${status.revision ?? "unknown"}`;
82
+ }
83
+
84
+ export function formatCatalogQuery(result: ModelCatalogQueryResult): string {
85
+ return [
86
+ `Model catalog: ${result.completeness} · ${result.freshness} · ${result.entries.length.toLocaleString()} entries · ${humanField(result.provenance.revision)}`,
87
+ ...result.entries.map((entry) => {
88
+ const cost = entry.pricing
89
+ ? ` · $${entry.pricing.input ?? "unknown"}/$${entry.pricing.output ?? "unknown"} input/output per 1M`
90
+ : " · pricing unknown";
91
+ return `- ${humanField(entry.canonical)} · context ${entry.limits.context.toLocaleString()} · output ${entry.limits.output.toLocaleString()}${cost} · ${entry.status}`;
92
+ }),
93
+ ].join("\n");
94
+ }
95
+
96
+ export async function runCatalogCommand(
97
+ action: string | undefined,
98
+ rest: string[],
99
+ deps: CliDependencies,
100
+ usage: () => number,
101
+ ): Promise<number> {
102
+ const parsed = parseCatalogArgs(action, rest);
103
+ if (!parsed) return usage();
104
+ try {
105
+ const result =
106
+ parsed.action === "list"
107
+ ? await deps.client.call("catalog.query", parsed.query)
108
+ : parsed.action === "refresh"
109
+ ? await deps.client.call("catalog.refresh", { force: parsed.force })
110
+ : await deps.client.call("catalog.status", {});
111
+ deps.stdout(
112
+ parsed.json
113
+ ? JSON.stringify(result)
114
+ : parsed.action === "list"
115
+ ? formatCatalogQuery(result as ModelCatalogQueryResult)
116
+ : formatCatalogStatus(result as ModelCatalogStatus),
117
+ );
118
+ return 0;
119
+ } catch (error) {
120
+ deps.stderr(error instanceof Error ? error.message : String(error));
121
+ return 1;
122
+ }
123
+ }
@@ -1,4 +1,4 @@
1
- import type { CompactionDurationEstimate } from "../domain/context-telemetry.ts";
1
+ import type { CompactionDurationEstimate } from "../observability/context-telemetry.ts";
2
2
  import { parseJsonOnlyArgs } from "./router.ts";
3
3
  import { type CliDependencies, callAndPrint } from "./support.ts";
4
4
 
@@ -1,7 +1,12 @@
1
- import type { ContextAssessment } from "../domain/context-telemetry.ts";
1
+ import type { ContextDelta, ContextSnapshot } from "../observability/context-delta.ts";
2
+ import type { ContextAssessment } from "../observability/context-telemetry.ts";
2
3
  import type { CliDependencies } from "./support.ts";
3
4
 
4
- export const CONTEXT_USAGE_LINES = [" context [--since <ms>] [--until <ms>] [--json]"];
5
+ export const CONTEXT_USAGE_LINES = [
6
+ " context [--since <ms>] [--until <ms>] [--json]",
7
+ " context delta --session-id <opaque-id> [--json]",
8
+ " context snapshot --snapshot <json> [--json]",
9
+ ];
5
10
 
6
11
  function parseContextArgs(args: string[]): { input: { since?: number; until?: number }; json: boolean } | null {
7
12
  const input: { since?: number; until?: number } = {};
@@ -34,19 +39,87 @@ export function formatContextAssessment(summary: ContextAssessment): string {
34
39
  `Injection mix: rules ${summary.injection.ruleCharacters.toLocaleString()} chars · tasks ${summary.injection.taskCharacters.toLocaleString()} chars · estimated ${summary.injection.estimatedTokens.toLocaleString()} tokens · unchanged ${summary.injection.unchangedRate === null ? "unknown" : `${(summary.injection.unchangedRate * 100).toFixed(1)}%`}`,
35
40
  `Compactions: ${summary.compaction.completed} completed · ${summary.compaction.aborted} aborted · avg ${value(summary.compaction.averageDurationMs, "ms")} · ${summary.compaction.perRun === null ? "unknown" : summary.compaction.perRun.toFixed(3)} per agent run · ${summary.compaction.perTurn === null ? "unknown" : summary.compaction.perTurn.toFixed(3)} per turn`,
36
41
  `Between compactions: ${value(summary.compaction.averageTurnsBetween, " turns")} · ${value(summary.compaction.averageProviderTokensBetween, " provider tokens")} · ${value(summary.compaction.averageCacheReadTokensBetween, " cache-read tokens")}`,
42
+ `Effectiveness: ${summary.compaction.effectivenessSamples} samples · avg reduction ${value(summary.compaction.averageReductionRatio === null ? null : summary.compaction.averageReductionRatio * 100, "%")} · pre ${value(summary.compaction.averagePreContextTokens, " tokens")} · post ${value(summary.compaction.averagePostContextTokens, " tokens")}`,
43
+ `Regrowth: 50% ${summary.compaction.regrowth["50"] ? `${value(summary.compaction.regrowth["50"].averageTurns, " turns")} / ${value(summary.compaction.regrowth["50"].averageElapsedMs, "ms")}` : "unknown"} · 80% ${summary.compaction.regrowth["80"] ? `${value(summary.compaction.regrowth["80"].averageTurns, " turns")} / ${value(summary.compaction.regrowth["80"].averageElapsedMs, "ms")}` : "unknown"} · 100% ${summary.compaction.regrowth["100"] ? `${value(summary.compaction.regrowth["100"].averageTurns, " turns")} / ${value(summary.compaction.regrowth["100"].averageElapsedMs, "ms")}` : "unknown"}`,
44
+ `Mechanisms: Pi ${summary.compaction.mechanisms["pi-native"]} · provider ${summary.compaction.mechanisms["provider-side"]} · extension ${summary.compaction.mechanisms.extension}`,
37
45
  `Reasons: threshold ${summary.compaction.reasons.threshold} · overflow ${summary.compaction.reasons.overflow} · manual ${summary.compaction.reasons.manual}`,
38
46
  ].join("\n");
39
47
  }
40
48
 
49
+ export function formatContextDelta(delta: ContextDelta | null): string {
50
+ if (!delta) return "Context delta: unavailable (no snapshot recorded for this session)";
51
+ const first = delta.firstChangedSegment;
52
+ const lifecycle = new Map<string, number>();
53
+ for (const change of delta.changes) lifecycle.set(change.lifecycle, (lifecycle.get(change.lifecycle) ?? 0) + 1);
54
+ const lifecycleText = [...lifecycle.entries()].map(([name, count]) => `${name} ${count}`).join(" · ") || "none";
55
+ const growth = delta.growthBySource
56
+ .filter((item) => item.deltaTokens !== 0)
57
+ .map((item) => `${item.source} ${item.deltaTokens > 0 ? "+" : ""}${item.deltaTokens.toLocaleString()} tokens`)
58
+ .join(" · ");
59
+ return [
60
+ `Context delta: ${new Date(delta.capturedAt).toISOString()}`,
61
+ `Stable prefix: ${delta.stablePrefixTokens.toLocaleString()} tokens${
62
+ first ? ` · first change ${first.source} at request position ${first.requestPosition ?? "historical"}` : " · unchanged"
63
+ }`,
64
+ `Lifecycle: ${lifecycleText}`,
65
+ `Growth: ${growth || "none"}`,
66
+ "Stable-prefix correlation is structural evidence, not proof of provider cache behavior.",
67
+ ].join("\n");
68
+ }
69
+
70
+ function parseDeltaArgs(args: string[]): { input: { session_id: string }; json: boolean } | null {
71
+ let sessionId: string | undefined;
72
+ let json = false;
73
+ for (let index = 0; index < args.length; index++) {
74
+ const argument = args[index];
75
+ if (argument === "--json") json = true;
76
+ else if (argument === "--session-id") sessionId = args[++index];
77
+ else return null;
78
+ }
79
+ if (!sessionId || !/^[A-Za-z0-9_-]{32,64}$/.test(sessionId)) return null;
80
+ return { input: { session_id: sessionId }, json };
81
+ }
82
+
83
+ function parseSnapshotArgs(args: string[]): { input: ContextSnapshot; json: boolean } | null {
84
+ let snapshotValue: string | undefined;
85
+ let json = false;
86
+ for (let index = 0; index < args.length; index++) {
87
+ const argument = args[index];
88
+ if (argument === "--json") json = true;
89
+ else if (argument === "--snapshot") snapshotValue = args[++index];
90
+ else return null;
91
+ }
92
+ if (!snapshotValue) return null;
93
+ try {
94
+ return { input: JSON.parse(snapshotValue) as ContextSnapshot, json };
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+
41
100
  export async function runContextCommand(
42
101
  action: string | undefined,
43
102
  rest: string[],
44
103
  deps: CliDependencies,
45
104
  usage: () => number,
46
105
  ): Promise<number> {
47
- const parsed = parseContextArgs([...(action === undefined ? [] : [action]), ...rest]);
48
- if (!parsed) return usage();
49
106
  try {
107
+ if (action === "delta") {
108
+ const parsed = parseDeltaArgs(rest);
109
+ if (!parsed) return usage();
110
+ const delta = await deps.client.call("context.delta", parsed.input);
111
+ deps.stdout(parsed.json ? JSON.stringify(delta) : formatContextDelta(delta));
112
+ return 0;
113
+ }
114
+ if (action === "snapshot") {
115
+ const parsed = parseSnapshotArgs(rest);
116
+ if (!parsed) return usage();
117
+ const delta = await deps.client.call("context.snapshot", parsed.input);
118
+ deps.stdout(parsed.json ? JSON.stringify(delta) : formatContextDelta(delta));
119
+ return 0;
120
+ }
121
+ const parsed = parseContextArgs([...(action === undefined ? [] : [action]), ...rest]);
122
+ if (!parsed) return usage();
50
123
  const summary = await deps.client.call("context.assess", parsed.input);
51
124
  deps.stdout(parsed.json ? JSON.stringify(summary) : formatContextAssessment(summary));
52
125
  return 0;
@@ -0,0 +1,30 @@
1
+ import type { ObservationExportStatus } from "../telemetry-export/exporter.ts";
2
+ import type { CliDependencies } from "./support.ts";
3
+
4
+ export const EXPORT_USAGE_LINES = [" export <status|flush> [--json]"];
5
+
6
+ export function formatExportStatus(status: ObservationExportStatus): string {
7
+ if (!status.enabled) return "OTLP export: disabled";
8
+ return [
9
+ `OTLP export: enabled · semantic conventions ${status.semanticConventions}`,
10
+ `Queue: ${status.queued.toLocaleString()} queued · ${status.exported.toLocaleString()} exported · ${status.dropped.toLocaleString()} dropped · ${status.failures.toLocaleString()} failed batches`,
11
+ ].join("\n");
12
+ }
13
+
14
+ export async function runExportCommand(
15
+ action: string | undefined,
16
+ rest: string[],
17
+ deps: CliDependencies,
18
+ usage: () => number,
19
+ ): Promise<number> {
20
+ if ((action !== "status" && action !== "flush") || rest.some((argument) => argument !== "--json")) return usage();
21
+ const json = rest.includes("--json");
22
+ try {
23
+ const status = await deps.client.call(action === "flush" ? "export.flush" : "export.status", {});
24
+ deps.stdout(json ? JSON.stringify(status) : formatExportStatus(status));
25
+ return 0;
26
+ } catch (error) {
27
+ deps.stderr(error instanceof Error ? error.message : String(error));
28
+ return 1;
29
+ }
30
+ }
@@ -5,8 +5,14 @@ import {
5
5
  METRIC_BATCH_MAX_OBSERVATIONS,
6
6
  USAGE_MAX_DISTINCT_SCOPES,
7
7
  } from "../constants.ts";
8
- import { METRIC_UNITS, type MetricObservation, type MetricQuery, type MetricUnit, type StoredMetricObservation } from "../domain/metric.ts";
9
- import type { TaskCostSummary } from "../domain/task-cost.ts";
8
+ import {
9
+ METRIC_UNITS,
10
+ type MetricObservation,
11
+ type MetricQuery,
12
+ type MetricUnit,
13
+ type StoredMetricObservation,
14
+ } from "../observability/metric.ts";
15
+ import type { TaskCostSummary } from "../observability/task-cost.ts";
10
16
  import { type CliDependencies, callAndPrint, humanField } from "./support.ts";
11
17
 
12
18
  export const METRICS_USAGE_LINES = [
@@ -1,4 +1,4 @@
1
- import { EXPECTED_OPERATION_NAMES, type OperationName } from "../service.ts";
1
+ import { EXPECTED_OPERATION_NAMES, type OperationName } from "../vehicle/service.ts";
2
2
  import type { CliDependencies } from "./support.ts";
3
3
 
4
4
  export const OP_USAGE_LINES = [" op <operation> [--input <json>]"];
@@ -1,5 +1,5 @@
1
- import type { ModelCandidate } from "../domain/model-ranking.ts";
2
- import type { Route } from "../policy.ts";
1
+ import type { ModelCandidate } from "../optimization/model-selection/ranking.ts";
2
+ import type { Route } from "../optimization/routing/policy.ts";
3
3
 
4
4
  /** Shared `provider/model@thinking` parsing for router and benchmark CLI arguments -- a Route and a ModelCandidate are structurally identical at this boundary. */
5
5
  export function parseCandidate(raw: string): ModelCandidate | null {
@@ -1,6 +1,6 @@
1
1
  import { CLI_AVAILABLE_ROUTES_MAX } from "../constants.ts";
2
- import type { PolicyDecision, Route } from "../policy.ts";
3
- import type { RouteOverride, RouterStatus, TelemetryPollResult } from "../ports/router-controller.ts";
2
+ import type { RouteOverride, RouterStatus, TelemetryPollResult } from "../optimization/routing/controller.ts";
3
+ import type { PolicyDecision, Route } from "../optimization/routing/policy.ts";
4
4
  import { parseRoute } from "./route-args.ts";
5
5
  import { type CliDependencies, callAndPrint, humanField } from "./support.ts";
6
6
 
@@ -1,6 +1,6 @@
1
- import type { JittorClient } from "../client.ts";
2
1
  import { HUMAN_TEXT_FIELD_MAX_CHARACTERS } from "../constants.ts";
3
- import type { OperationInputs, OperationName, OperationOutputs } from "../service.ts";
2
+ import type { JittorClient } from "../vehicle/client.ts";
3
+ import type { OperationInputs, OperationName, OperationOutputs } from "../vehicle/service.ts";
4
4
 
5
5
  export interface CliDependencies {
6
6
  client: Pick<JittorClient, "call">;
package/src/cli.ts CHANGED
@@ -1,21 +1,36 @@
1
1
  #!/usr/bin/env bun
2
2
  import { fileURLToPath } from "node:url";
3
+ import { BACKFILL_USAGE_LINES, formatUsageImportResult, formatUsageImportStatus, runBackfillCommand } from "./cli-commands/backfill.ts";
3
4
  import { BENCHMARKS_USAGE_LINES, runBenchmarksCommand } from "./cli-commands/benchmarks.ts";
5
+ import { CATALOG_USAGE_LINES, formatCatalogQuery, formatCatalogStatus, runCatalogCommand } from "./cli-commands/catalog.ts";
4
6
  import { runCompactionCommand } from "./cli-commands/compaction.ts";
5
- import { CONTEXT_USAGE_LINES, formatContextAssessment, runContextCommand } from "./cli-commands/context.ts";
7
+ import { CONTEXT_USAGE_LINES, formatContextAssessment, formatContextDelta, runContextCommand } from "./cli-commands/context.ts";
8
+ import { EXPORT_USAGE_LINES, formatExportStatus, runExportCommand } from "./cli-commands/export.ts";
6
9
  import { formatCostByTask, formatMetricsQuery, METRICS_USAGE_LINES, runMetricsCommand } from "./cli-commands/metrics.ts";
7
10
  import { OP_USAGE_LINES, runOpCommand } from "./cli-commands/op.ts";
8
11
  import { formatRouterStatus, ROUTER_USAGE_LINES, runRouterCommand, runTelemetryCommand } from "./cli-commands/router.ts";
9
12
  import { installService, renderSystemdUnit, runServiceCommand, SERVICE_USAGE_LINES, systemctl } from "./cli-commands/service-daemon.ts";
10
13
  import { runSessionCommand, SESSION_USAGE_LINES } from "./cli-commands/session.ts";
11
14
  import type { CliDependencies } from "./cli-commands/support.ts";
12
- import { connectJittorClient } from "./client.ts";
13
15
  import { serveMain } from "./daemon.ts";
16
+ import { connectJittorClient } from "./vehicle/client.ts";
14
17
 
15
18
  // Re-exported for external callers (tests, daemon.ts's systemd-unit test) that import these
16
19
  // directly from cli.ts rather than reaching into src/cli-commands/*.
17
20
  export type { CliDependencies };
18
- export { formatContextAssessment, formatCostByTask, formatMetricsQuery, formatRouterStatus, renderSystemdUnit };
21
+ export {
22
+ formatCatalogQuery,
23
+ formatCatalogStatus,
24
+ formatContextAssessment,
25
+ formatContextDelta,
26
+ formatCostByTask,
27
+ formatExportStatus,
28
+ formatMetricsQuery,
29
+ formatRouterStatus,
30
+ formatUsageImportResult,
31
+ formatUsageImportStatus,
32
+ renderSystemdUnit,
33
+ };
19
34
 
20
35
  const DEFAULT_DEPENDENCIES: CliDependencies = {
21
36
  get client() {
@@ -34,8 +49,11 @@ function usage(stderr: (line: string) => void): number {
34
49
  "Usage: jittor <command> [options]",
35
50
  " serve",
36
51
  ...SERVICE_USAGE_LINES,
52
+ ...BACKFILL_USAGE_LINES,
53
+ ...EXPORT_USAGE_LINES,
37
54
  ...CONTEXT_USAGE_LINES,
38
55
  ...BENCHMARKS_USAGE_LINES,
56
+ ...CATALOG_USAGE_LINES,
39
57
  ...METRICS_USAGE_LINES,
40
58
  ...ROUTER_USAGE_LINES,
41
59
  ...SESSION_USAGE_LINES,
@@ -47,29 +65,44 @@ function usage(stderr: (line: string) => void): number {
47
65
 
48
66
  /**
49
67
  * Composes one command dispatcher per capability module (metrics, router+telemetry+compaction,
50
- * session identity, benchmarks, context, service lifecycle, the raw op escape hatch) instead of
51
- * a single switch that used to combine every command's own argument parsing, validation, and
52
- * presentation in one place. Each module owns its own usage lines and command handler; this
53
- * function only routes the top-level command word and forwards `usage` for the shared "not
54
- * recognized" fallback.
68
+ * session identity, benchmarks, context, service lifecycle, the raw op escape hatch). Each module
69
+ * owns its own argument parsing, validation, presentation, and usage lines; this function's switch
70
+ * only routes the top-level command word and forwards `usage` for the shared fallback.
55
71
  */
56
72
  export async function runCli(args: string[], deps: CliDependencies = DEFAULT_DEPENDENCIES): Promise<number> {
57
73
  const [command, action, ...rest] = args;
58
74
  const fail = () => usage(deps.stderr);
59
- if (command === "serve") {
60
- await deps.serve();
61
- return 0;
75
+ switch (command) {
76
+ case "serve":
77
+ await deps.serve();
78
+ return 0;
79
+ case "session":
80
+ return runSessionCommand(action, rest, deps, fail);
81
+ case "backfill":
82
+ return runBackfillCommand(action, rest, deps, fail);
83
+ case "export":
84
+ return runExportCommand(action, rest, deps, fail);
85
+ case "metrics":
86
+ return runMetricsCommand(action, rest, deps, fail);
87
+ case "telemetry":
88
+ return runTelemetryCommand(action, rest, deps, fail);
89
+ case "compaction":
90
+ return runCompactionCommand(action, rest, deps, fail);
91
+ case "router":
92
+ return runRouterCommand(action, rest, deps, fail);
93
+ case "op":
94
+ return runOpCommand(action, rest, deps, fail);
95
+ case "benchmarks":
96
+ return runBenchmarksCommand(action, rest, deps, fail);
97
+ case "catalog":
98
+ return runCatalogCommand(action, rest, deps, fail);
99
+ case "context":
100
+ return runContextCommand(action, rest, deps, fail);
101
+ case "service":
102
+ return runServiceCommand(action, rest, deps, fail);
103
+ default:
104
+ return fail();
62
105
  }
63
- if (command === "session") return runSessionCommand(action, rest, deps, fail);
64
- if (command === "metrics") return runMetricsCommand(action, rest, deps, fail);
65
- if (command === "telemetry") return runTelemetryCommand(action, rest, deps, fail);
66
- if (command === "compaction") return runCompactionCommand(action, rest, deps, fail);
67
- if (command === "router") return runRouterCommand(action, rest, deps, fail);
68
- if (command === "op") return runOpCommand(action, rest, deps, fail);
69
- if (command === "benchmarks") return runBenchmarksCommand(action, rest, deps, fail);
70
- if (command === "context") return runContextCommand(action, rest, deps, fail);
71
- if (command === "service") return runServiceCommand(action, rest, deps, fail);
72
- return fail();
73
106
  }
74
107
 
75
108
  export async function main(args: string[] = process.argv.slice(2)): Promise<void> {
@@ -1,4 +1,4 @@
1
- import type { MetricObservation } from "../domain/metric.ts";
1
+ import type { MetricObservation } from "../observability/metric.ts";
2
2
 
3
3
  export interface CodexWindow {
4
4
  usedPercent: number;
@@ -0,0 +1,53 @@
1
+ import type { BudgetWindow } from "../observability/budget.ts";
2
+ import type { TelemetryBatch, TelemetrySource } from "../observability/telemetry-source.ts";
3
+ import {
4
+ type CodexRateLimitSnapshot,
5
+ CodexSubscriptionTelemetryAdapter,
6
+ type CodexTransport,
7
+ type CodexWindow,
8
+ loadCodexFileCredentials,
9
+ } from "./telemetry.ts";
10
+
11
+ function budgetWindow(limit: CodexRateLimitSnapshot, name: "primary" | "secondary", window: CodexWindow | null): BudgetWindow | null {
12
+ if (!window || window.windowSeconds === null || window.resetsAt === null) return null;
13
+ return {
14
+ id: `${limit.limitId}:${name}@${limit.observedAt}`,
15
+ source: "codex-subscription",
16
+ scope: limit.limitId === "codex" ? `codex:${name}` : `${limit.limitId}:${name}`,
17
+ usedFraction: window.usedPercent / 100,
18
+ windowSeconds: window.windowSeconds,
19
+ resetsAt: window.resetsAt * 1_000,
20
+ observedAt: limit.observedAt,
21
+ freshness: "fresh",
22
+ confidence: 0.8,
23
+ };
24
+ }
25
+
26
+ function windowsFromLimit(limit: CodexRateLimitSnapshot): BudgetWindow[] {
27
+ return [budgetWindow(limit, "primary", limit.primary), budgetWindow(limit, "secondary", limit.secondary)].filter(
28
+ (window): window is BudgetWindow => window !== null,
29
+ );
30
+ }
31
+
32
+ export class CodexTelemetrySource implements TelemetrySource {
33
+ readonly id = "codex-subscription";
34
+ readonly provider = "openai-codex";
35
+ readonly required = true;
36
+
37
+ constructor(
38
+ private readonly authFile: string,
39
+ private readonly transport: CodexTransport = fetch,
40
+ private readonly clock: () => number = Date.now,
41
+ ) {}
42
+
43
+ async poll(): Promise<TelemetryBatch> {
44
+ const observedAt = this.clock();
45
+ const adapter = new CodexSubscriptionTelemetryAdapter(loadCodexFileCredentials(this.authFile), this.transport);
46
+ const snapshot = await adapter.readUsage(observedAt);
47
+ return {
48
+ observedAt,
49
+ metrics: snapshot.metrics,
50
+ windows: [snapshot.defaultLimit, ...snapshot.additionalLimits].flatMap(windowsFromLimit),
51
+ };
52
+ }
53
+ }
@@ -1,5 +1,5 @@
1
1
  import { readFileSync, statSync } from "node:fs";
2
- import { type CodexRateLimitSnapshot, type CodexUsageSnapshot, parseCodexRateLimitHeaders, parseCodexUsage } from "./codex-contracts.ts";
2
+ import { type CodexRateLimitSnapshot, type CodexUsageSnapshot, parseCodexRateLimitHeaders, parseCodexUsage } from "./contracts.ts";
3
3
 
4
4
  export {
5
5
  type CodexCredits,
@@ -11,7 +11,7 @@ export {
11
11
  parseCodexRateLimitHeaders,
12
12
  parseCodexUsage,
13
13
  rateLimitMetrics,
14
- } from "./codex-contracts.ts";
14
+ } from "./contracts.ts";
15
15
 
16
16
  const CHATGPT_BACKEND_BASE_URL = "https://chatgpt.com/backend-api";
17
17
 
package/src/constants.ts CHANGED
@@ -18,6 +18,19 @@ export const BENCHMARK_MAX_QUERY_LIMIT = 500;
18
18
  export const BENCHMARK_SOURCE_MAX_RESPONSE_BYTES = 2_097_152;
19
19
  export const BENCHMARK_SOURCE_MAX_TOTAL_RESPONSE_BYTES = 6_291_456;
20
20
  export const BENCHMARK_REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1_000;
21
+ export const MODEL_CATALOG_MAX_RESPONSE_BYTES = 8_388_608;
22
+ export const MODEL_CATALOG_MAX_ENTRIES = 8_000;
23
+ export const MODEL_CATALOG_STORE_QUERY_LIMIT = MODEL_CATALOG_MAX_ENTRIES + 1;
24
+ export const MODEL_CATALOG_DEFAULT_QUERY_LIMIT = 100;
25
+ export const MODEL_CATALOG_MAX_QUERY_LIMIT = 500;
26
+ export const MODEL_CATALOG_REQUEST_TIMEOUT_MS = 10_000;
27
+ export const MODEL_CATALOG_FRESHNESS_MS = 24 * 60 * 60 * 1_000;
28
+ export const PI_USAGE_IMPORT_MAX_FILES = 200;
29
+ export const PI_USAGE_IMPORT_MAX_FILE_BYTES = 16_777_216;
30
+ export const PI_USAGE_IMPORT_MAX_TOTAL_BYTES = 67_108_864;
31
+ export const PI_USAGE_IMPORT_MAX_ENTRIES = 20_000;
32
+ export const PI_USAGE_IMPORT_MAX_RECORDS = 5_000;
33
+ export const PI_USAGE_IMPORT_MAX_DURATION_MS = 30_000;
21
34
  export const MODEL_OBSERVATION_IDENTITY_MAX_CHARACTERS = 160;
22
35
  export const MODEL_AGGREGATE_MAX_ROWS = 10_000;
23
36
  export const MODEL_AGGREGATE_MAX_GROUPS = 500;
@@ -31,6 +44,11 @@ export const MODEL_OBSERVATION_FRESH_MS = 7 * 24 * 60 * 60 * 1_000;
31
44
  export const MAINTENANCE_INTERVAL_MS = 15 * 60 * 1_000;
32
45
  export const TELEMETRY_POLL_INTERVAL_MS = 60_000;
33
46
  export const TELEMETRY_STALE_AFTER_MS = 120_000;
47
+ export const OTLP_EXPORT_MAX_QUEUE_SIZE = 1_000;
48
+ export const OTLP_EXPORT_BATCH_SIZE = 100;
49
+ export const OTLP_EXPORT_INTERVAL_MS = 5_000;
50
+ export const OTLP_EXPORT_TIMEOUT_MS = 3_000;
51
+ export const OTLP_EXPORT_MAX_RETRIES = 1;
34
52
  export const FOOTER_CONTEXT_ACCENT_FRACTION = 0.5;
35
53
  export const FOOTER_CONTEXT_WARNING_FRACTION = 0.7;
36
54
  export const FOOTER_CONTEXT_ERROR_FRACTION = 0.9;