llm-relay 0.71.1 → 0.72.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.
@@ -154,16 +154,37 @@ function collectLocallyDeclaredFunctionNames(sourceFile) {
154
154
  visit(sourceFile);
155
155
  return names;
156
156
  }
157
- function containsCallTo(node, names) {
158
- if (names.size === 0)
157
+ function rootIdentifierOf(expr) {
158
+ let curr = expr;
159
+ while (ts.isPropertyAccessExpression(curr)) {
160
+ curr = curr.expression;
161
+ }
162
+ return ts.isIdentifier(curr) ? curr.text : null;
163
+ }
164
+ function containsLocalReplicaCall(node, localNonImportedFunctions, importedNames) {
165
+ if (localNonImportedFunctions.size === 0)
159
166
  return false;
160
167
  let found = false;
161
168
  const visit = (n) => {
162
169
  if (found)
163
170
  return;
164
- if (ts.isCallExpression(n) && ts.isIdentifier(n.expression) && names.has(n.expression.text)) {
165
- found = true;
166
- return;
171
+ if (ts.isCallExpression(n)) {
172
+ if (ts.isIdentifier(n.expression)) {
173
+ const callee = n.expression.text;
174
+ if (localNonImportedFunctions.has(callee)) {
175
+ found = true;
176
+ return;
177
+ }
178
+ if (importedNames.has(callee)) {
179
+ return;
180
+ }
181
+ }
182
+ else if (ts.isPropertyAccessExpression(n.expression)) {
183
+ const root = rootIdentifierOf(n.expression);
184
+ if (root !== null && importedNames.has(root)) {
185
+ return;
186
+ }
187
+ }
167
188
  }
168
189
  ts.forEachChild(n, visit);
169
190
  };
@@ -177,7 +198,7 @@ function describeLocalReplicaCall(chain, importedNames, localFunctionNames) {
177
198
  if (importedNames.size === 0)
178
199
  return null;
179
200
  const nonImportedLocalFunctions = new Set([...localFunctionNames].filter((n) => !importedNames.has(n)));
180
- if (!containsCallTo(actual, nonImportedLocalFunctions))
201
+ if (!containsLocalReplicaCall(actual, nonImportedLocalFunctions, importedNames))
181
202
  return null;
182
203
  return `expect(${actual.getText().slice(0, 80)}).${matcherName}(...) calls a function declared locally in this test file, never imported — it may assert against a hand-copied replica rather than the code under test`;
183
204
  }
@@ -1 +1 @@
1
- {"version":3,"file":"test-assertions.js","sourceRoot":"","sources":["../../src/delegate-gate/test-assertions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test-assertions.js","sourceRoot":"","sources":["../../src/delegate-gate/test-assertions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ import type { Config } from "./config.js";
2
+ import type { LadderRung } from "./config-types.js";
3
+ /**
4
+ * The lane KIND travels as data, never as two hand-copies: the `as const` array is the ONE
5
+ * declaration — the type is indexed from it and the parser validates against it, so a new kind
6
+ * is a compile error at the classifier rather than a silent fall-through (the closed-union
7
+ * gotcha in CLAUDE.md).
8
+ */
9
+ export declare const DISPATCH_LANE_KINDS: readonly ["cli", "relay"];
10
+ export type DispatchLaneKind = (typeof DISPATCH_LANE_KINDS)[number];
11
+ /** Terminal lane states worth recording. A `cancelled` job is discarded, never reported. */
12
+ export declare const DISPATCH_LANE_STATUSES: readonly ["completed", "failed", "timed_out"];
13
+ export type DispatchLaneStatus = (typeof DISPATCH_LANE_STATUSES)[number];
14
+ /**
15
+ * What `llm-relay mcp` reports to the daemon when an agent-mode lane run settles. Counts and
16
+ * lengths only — never the task text, never the lane's output (logs-are-metadata-only).
17
+ *
18
+ * ⚠ No `providerKey`/`modelId`: an agent-mode lane's serving member is unknowable to the
19
+ * reporter (a `cli` lane runs its own tool loop against its own credentials; a `relay` lane's
20
+ * HTTP traffic is already metered by the daemon's own pipeline), so carrying them would label
21
+ * a guess as a measurement.
22
+ */
23
+ export interface DispatchedTelemetryReport {
24
+ jobId: string;
25
+ laneId: string;
26
+ kind: DispatchLaneKind;
27
+ spec?: string;
28
+ wallClockMs: number;
29
+ exitCode: number | null;
30
+ status: DispatchLaneStatus;
31
+ estimatedInputTokens: number;
32
+ estimatedOutputTokens: number;
33
+ }
34
+ /**
35
+ * Fail-closed validation of one report body. Anything unexpected — unknown keys, a missing
36
+ * field, a bad kind/status, a negative or fractional token count, an over-long id — yields
37
+ * null rather than a guess. The 400 reason never echoes the body.
38
+ */
39
+ export declare function parseTelemetryReport(value: unknown): DispatchedTelemetryReport | null;
40
+ export interface LaneStats {
41
+ calls: number;
42
+ successes: number;
43
+ failures: number;
44
+ timeouts: number;
45
+ /** Bounded wall-clock sample window, oldest dropped — advisory percentiles, not scoring. */
46
+ wallClockMs: number[];
47
+ lastAt: number | null;
48
+ }
49
+ /** Rolling wall-clock window per lane; bound keeps one chatty lane from growing the file. */
50
+ export declare const MAX_LANE_STAT_SAMPLES = 25;
51
+ /**
52
+ * Median of one lane's rolling wall-clock window, in milliseconds. Null when the window is
53
+ * empty — unknown stays null, never 0, so a surface cannot print a measured-looking figure for
54
+ * a lane that never ran here. Pure: `buildDispatch` (`dispatch.ts`) calls this rather than
55
+ * re-implementing the percentile, so the view and the tests share the one definition.
56
+ */
57
+ export declare function medianWallClockMs(samples: readonly number[]): number | null;
58
+ /**
59
+ * The CLOSED list of env names that can route a lane's harness traffic back through this
60
+ * relay. Any other name — however credential-shaped — never counts: an open list would let
61
+ * a future env var silently reclassify a lane's accounting.
62
+ */
63
+ export declare const RELAY_ROUTED_ENV_NAMES: readonly ["ANTHROPIC_BASE_URL", "OPENAI_BASE_URL"];
64
+ /**
65
+ * Whether this rung's declared env routes its harness traffic back through the relay
66
+ * listener at `listener` — i.e. the daemon's own HTTP pipeline already meters the run, so
67
+ * a telemetry accounting row would double count it (finding C1).
68
+ *
69
+ * A labelled fact about the lane's own wiring, config-derived, never guessed: true only
70
+ * when a closed-list env name holds a parseable URL whose origin (scheme://host:port,
71
+ * IPv6-bracket-aware, default ports applied) equals the listener's own origin, built with
72
+ * the same IPv6 bracketing rule `proxyUrl` applies. A `null` env value (unset), an absent
73
+ * name, an unparseable URL, or any other name ⇒ false. Pure, so the daemon route and the
74
+ * suite share the one definition.
75
+ */
76
+ export declare function laneRoutesThroughRelay(rung: Pick<LadderRung, "env">, listener: {
77
+ host: string;
78
+ port: number;
79
+ }): boolean;
80
+ /**
81
+ * Record one settled lane run. `completed` counts a success; `failed` a failure; `timed_out`
82
+ * a timeout AND a failure (a timeout did not succeed). Stats never reorder the ladder.
83
+ */
84
+ export declare function recordLaneRun(cfg: Config, report: DispatchedTelemetryReport, now?: number): void;
85
+ /** One lane's stats for this config, or undefined when the lane never ran here. A copy. */
86
+ export declare function laneStatsFor(cfg: Config, laneId: string): LaneStats | undefined;
87
+ /** Every lane with stats for this config, sorted by lane id so surfaces render stably. Copies. */
88
+ export declare function allLaneStats(cfg: Config): Array<{
89
+ laneId: string;
90
+ } & LaneStats>;
91
+ export declare function onLaneStatsChanged(cfg: Config, listener: () => void): void;
92
+ /** One exported/persisted lane row: the lane id plus its counters and sample window. */
93
+ export interface LaneStatsRow {
94
+ laneId: string;
95
+ calls: number;
96
+ successes: number;
97
+ failures: number;
98
+ timeouts: number;
99
+ wallClockMs: number[];
100
+ lastAt: number | null;
101
+ }
102
+ /** Still-live rows for persistence and ladder surfaces. */
103
+ export declare function exportLaneStatsRows(cfg: Config): LaneStatsRow[];
104
+ /**
105
+ * Restore persisted rows into this config's live map. It NEVER overwrites stats the live
106
+ * process already learned — the `restoreExhaustedRows` contract. Returns the count restored.
107
+ */
108
+ export declare function restoreLaneStatsRows(cfg: Config, rows: readonly LaneStatsRow[]): number;
109
+ /** Bumped when the row shape changes; a mismatch restores nothing rather than guessing. */
110
+ export declare const CURRENT_DISPATCH_LANE_STATS_VERSION = 1;
111
+ export interface DispatchLaneStatsFile {
112
+ version: number;
113
+ rows: LaneStatsRow[];
114
+ }
115
+ export declare function getDispatchLaneStatsPath(): string;
116
+ /**
117
+ * Read the persisted rows. Absent file, unreadable file, wrong version, or an unrecognized
118
+ * envelope all yield an empty list — never a throw, never a partial row from a shape we did
119
+ * not recognize. One malformed row is dropped alone.
120
+ */
121
+ export declare function loadLaneStatsRows(opts?: {
122
+ path?: string;
123
+ }): LaneStatsRow[];
124
+ export declare function saveLaneStatsRows(rows: readonly LaneStatsRow[], opts?: {
125
+ path?: string;
126
+ }): void;
127
+ /**
128
+ * Wire a config's lane stats to the file: restore what was recorded, then flush on every
129
+ * change, debounced through the shared `WriteBehindTimer`. Returns the number restored.
130
+ */
131
+ export declare function installDispatchLaneStatsPersistence(cfg: Config, opts?: {
132
+ path?: string;
133
+ }): number;
@@ -0,0 +1,274 @@
1
+ import { join } from "node:path";
2
+ import { tmpdir } from "node:os";
3
+ import { relayStatePath } from "./state-paths.js";
4
+ import { WriteBehindTimer } from "./write-behind.js";
5
+ import { atomicWriteJsonSync, safeReadJsonSync } from "./storage/json-store.js";
6
+ import { isDashboardSafeId } from "./dashboard-contract.js";
7
+ export const DISPATCH_LANE_KINDS = Object.freeze(["cli", "relay"]);
8
+ export const DISPATCH_LANE_STATUSES = Object.freeze(["completed", "failed", "timed_out"]);
9
+ const REPORT_KEYS = Object.freeze([
10
+ "jobId",
11
+ "laneId",
12
+ "kind",
13
+ "spec",
14
+ "wallClockMs",
15
+ "exitCode",
16
+ "status",
17
+ "estimatedInputTokens",
18
+ "estimatedOutputTokens",
19
+ ]);
20
+ const _REPORT_KEYS_COMPLETE = true;
21
+ const REPORT_KEY_SET = new Set(REPORT_KEYS);
22
+ const MAX_JOB_ID_CHARS = 64;
23
+ const MAX_LANE_ID_CHARS = 200;
24
+ const MAX_SPEC_CHARS = 200;
25
+ function isBoundedId(value, maxChars) {
26
+ return typeof value === "string"
27
+ && value.length >= 1
28
+ && value.length <= maxChars
29
+ && isDashboardSafeId(value);
30
+ }
31
+ function isTokenCount(value) {
32
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
33
+ }
34
+ export function parseTelemetryReport(value) {
35
+ if (value === null || typeof value !== "object" || Array.isArray(value))
36
+ return null;
37
+ const body = value;
38
+ for (const key of Object.keys(body)) {
39
+ if (!REPORT_KEY_SET.has(key))
40
+ return null;
41
+ }
42
+ const { jobId, laneId, kind, spec, wallClockMs, exitCode, status, estimatedInputTokens, estimatedOutputTokens } = body;
43
+ if (!isBoundedId(jobId, MAX_JOB_ID_CHARS))
44
+ return null;
45
+ if (!isBoundedId(laneId, MAX_LANE_ID_CHARS))
46
+ return null;
47
+ if (spec !== undefined && !isBoundedId(spec, MAX_SPEC_CHARS))
48
+ return null;
49
+ if (!DISPATCH_LANE_KINDS.includes(kind))
50
+ return null;
51
+ if (!DISPATCH_LANE_STATUSES.includes(status))
52
+ return null;
53
+ if (typeof wallClockMs !== "number" || !Number.isFinite(wallClockMs) || wallClockMs < 0)
54
+ return null;
55
+ if (exitCode !== null && (typeof exitCode !== "number" || !Number.isInteger(exitCode)))
56
+ return null;
57
+ if (!isTokenCount(estimatedInputTokens) || !isTokenCount(estimatedOutputTokens))
58
+ return null;
59
+ return {
60
+ jobId,
61
+ laneId,
62
+ kind: kind,
63
+ ...(spec === undefined ? {} : { spec }),
64
+ wallClockMs,
65
+ exitCode,
66
+ status: status,
67
+ estimatedInputTokens,
68
+ estimatedOutputTokens,
69
+ };
70
+ }
71
+ export const MAX_LANE_STAT_SAMPLES = 25;
72
+ export function medianWallClockMs(samples) {
73
+ if (samples.length === 0)
74
+ return null;
75
+ const sorted = [...samples].sort((a, b) => a - b);
76
+ const mid = Math.floor(sorted.length / 2);
77
+ if (sorted.length % 2 === 1)
78
+ return sorted[mid] ?? null;
79
+ const lo = sorted[mid - 1] ?? null;
80
+ const hi = sorted[mid] ?? null;
81
+ if (lo === null || hi === null)
82
+ return null;
83
+ return (lo + hi) / 2;
84
+ }
85
+ function freshLaneStats() {
86
+ return { calls: 0, successes: 0, failures: 0, timeouts: 0, wallClockMs: [], lastAt: null };
87
+ }
88
+ export const RELAY_ROUTED_ENV_NAMES = ["ANTHROPIC_BASE_URL", "OPENAI_BASE_URL"];
89
+ export function laneRoutesThroughRelay(rung, listener) {
90
+ const env = rung.env;
91
+ if (env === undefined)
92
+ return false;
93
+ let listenerOrigin;
94
+ try {
95
+ const host = listener.host.includes(":") ? `[${listener.host}]` : listener.host;
96
+ listenerOrigin = new URL(`http://${host}:${listener.port}`).origin;
97
+ }
98
+ catch {
99
+ return false;
100
+ }
101
+ for (const name of RELAY_ROUTED_ENV_NAMES) {
102
+ const value = env[name];
103
+ if (typeof value !== "string")
104
+ continue;
105
+ let origin;
106
+ try {
107
+ origin = new URL(value).origin;
108
+ }
109
+ catch {
110
+ continue;
111
+ }
112
+ if (origin === listenerOrigin)
113
+ return true;
114
+ }
115
+ return false;
116
+ }
117
+ const laneStats = new WeakMap();
118
+ function laneStatsForWrite(cfg) {
119
+ let m = laneStats.get(cfg);
120
+ if (!m) {
121
+ m = new Map();
122
+ laneStats.set(cfg, m);
123
+ }
124
+ return m;
125
+ }
126
+ function copyStats(stats) {
127
+ return { ...stats, wallClockMs: [...stats.wallClockMs] };
128
+ }
129
+ export function recordLaneRun(cfg, report, now = Date.now()) {
130
+ const map = laneStatsForWrite(cfg);
131
+ let entry = map.get(report.laneId);
132
+ if (!entry) {
133
+ entry = freshLaneStats();
134
+ map.set(report.laneId, entry);
135
+ }
136
+ entry.calls += 1;
137
+ switch (report.status) {
138
+ case "completed":
139
+ entry.successes += 1;
140
+ break;
141
+ case "failed":
142
+ entry.failures += 1;
143
+ break;
144
+ case "timed_out":
145
+ entry.timeouts += 1;
146
+ entry.failures += 1;
147
+ break;
148
+ default: {
149
+ const _never = report.status;
150
+ throw new Error(`unhandled dispatch lane status: ${String(_never)}`);
151
+ }
152
+ }
153
+ entry.wallClockMs.push(report.wallClockMs);
154
+ if (entry.wallClockMs.length > MAX_LANE_STAT_SAMPLES) {
155
+ entry.wallClockMs.splice(0, entry.wallClockMs.length - MAX_LANE_STAT_SAMPLES);
156
+ }
157
+ entry.lastAt = now;
158
+ notifyLaneStats(cfg);
159
+ }
160
+ export function laneStatsFor(cfg, laneId) {
161
+ const entry = laneStats.get(cfg)?.get(laneId);
162
+ return entry ? copyStats(entry) : undefined;
163
+ }
164
+ export function allLaneStats(cfg) {
165
+ return [...(laneStats.get(cfg) ?? new Map()).entries()]
166
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
167
+ .map(([laneId, stats]) => ({ laneId, ...copyStats(stats) }));
168
+ }
169
+ const laneStatsListeners = new WeakMap();
170
+ export function onLaneStatsChanged(cfg, listener) {
171
+ let set = laneStatsListeners.get(cfg);
172
+ if (!set) {
173
+ set = new Set();
174
+ laneStatsListeners.set(cfg, set);
175
+ }
176
+ set.add(listener);
177
+ }
178
+ function notifyLaneStats(cfg) {
179
+ for (const listener of laneStatsListeners.get(cfg) ?? []) {
180
+ try {
181
+ listener();
182
+ }
183
+ catch {
184
+ }
185
+ }
186
+ }
187
+ export function exportLaneStatsRows(cfg) {
188
+ return allLaneStats(cfg).map(({ laneId, calls, successes, failures, timeouts, wallClockMs, lastAt }) => ({
189
+ laneId,
190
+ calls,
191
+ successes,
192
+ failures,
193
+ timeouts,
194
+ wallClockMs: [...wallClockMs],
195
+ lastAt,
196
+ }));
197
+ }
198
+ function isLaneStatsRow(value) {
199
+ if (value === null || typeof value !== "object" || Array.isArray(value))
200
+ return false;
201
+ const row = value;
202
+ if (!isBoundedId(row["laneId"], MAX_LANE_ID_CHARS))
203
+ return false;
204
+ for (const key of ["calls", "successes", "failures", "timeouts"]) {
205
+ if (!isTokenCount(row[key]))
206
+ return false;
207
+ }
208
+ const wallClockMs = row["wallClockMs"];
209
+ if (!Array.isArray(wallClockMs))
210
+ return false;
211
+ if (wallClockMs.length > MAX_LANE_STAT_SAMPLES)
212
+ return false;
213
+ for (const sample of wallClockMs) {
214
+ if (typeof sample !== "number" || !Number.isFinite(sample) || sample < 0)
215
+ return false;
216
+ }
217
+ const lastAt = row["lastAt"];
218
+ if (lastAt !== null && (typeof lastAt !== "number" || !Number.isFinite(lastAt) || lastAt < 0))
219
+ return false;
220
+ return true;
221
+ }
222
+ export function restoreLaneStatsRows(cfg, rows) {
223
+ const map = laneStatsForWrite(cfg);
224
+ let restored = 0;
225
+ for (const row of rows) {
226
+ if (map.has(row.laneId))
227
+ continue;
228
+ map.set(row.laneId, {
229
+ calls: row.calls,
230
+ successes: row.successes,
231
+ failures: row.failures,
232
+ timeouts: row.timeouts,
233
+ wallClockMs: row.wallClockMs.slice(-MAX_LANE_STAT_SAMPLES),
234
+ lastAt: row.lastAt,
235
+ });
236
+ restored++;
237
+ }
238
+ if (restored > 0)
239
+ notifyLaneStats(cfg);
240
+ return restored;
241
+ }
242
+ export const CURRENT_DISPATCH_LANE_STATS_VERSION = 1;
243
+ export function getDispatchLaneStatsPath() {
244
+ if (process.env.VITEST)
245
+ return join(tmpdir(), "llm-relay-vitest", "dispatch-lane-stats.json");
246
+ return join(relayStatePath("cache"), "dispatch-lane-stats.json");
247
+ }
248
+ export function loadLaneStatsRows(opts = {}) {
249
+ const target = opts.path ?? getDispatchLaneStatsPath();
250
+ const parsed = safeReadJsonSync(target);
251
+ if (parsed === null || typeof parsed !== "object")
252
+ return [];
253
+ if (parsed["version"] !== CURRENT_DISPATCH_LANE_STATS_VERSION)
254
+ return [];
255
+ const rows = parsed["rows"];
256
+ if (!Array.isArray(rows))
257
+ return [];
258
+ return rows.filter((row) => isLaneStatsRow(row));
259
+ }
260
+ export function saveLaneStatsRows(rows, opts = {}) {
261
+ const target = opts.path ?? getDispatchLaneStatsPath();
262
+ const file = { version: CURRENT_DISPATCH_LANE_STATS_VERSION, rows: [...rows] };
263
+ atomicWriteJsonSync(target, file, { space: 2 });
264
+ }
265
+ export function installDispatchLaneStatsPersistence(cfg, opts = {}) {
266
+ const path = opts.path ?? getDispatchLaneStatsPath();
267
+ const restored = restoreLaneStatsRows(cfg, loadLaneStatsRows({ path }));
268
+ const timer = new WriteBehindTimer();
269
+ onLaneStatsChanged(cfg, () => {
270
+ timer.touch(() => saveLaneStatsRows(exportLaneStatsRows(cfg), { path }));
271
+ });
272
+ return restored;
273
+ }
274
+ //# sourceMappingURL=dispatch-lane-stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch-lane-stats.js","sourceRoot":"","sources":["../src/dispatch-lane-stats.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Config } from "./config-types.js";
1
+ import type { Config, LadderRung } from "./config-types.js";
2
2
  import type { HostRoutingState } from "./host-routing.js";
3
3
  import type { ContextWindowSource, ResolvedContextWindow } from "./metadata.js";
4
4
  import { type LaneManifest } from "./lane-manifest.js";
@@ -41,6 +41,29 @@ export declare const MAX_EXHAUSTED_MS: number;
41
41
  export type DispatchOutcome = "rate_limited" | "quota_exhausted";
42
42
  export declare const OUTCOME_DEFAULT_MS: Record<DispatchOutcome, number>;
43
43
  export type LaneState = "ready" | "exhausted" | "disabled" | "not-servable";
44
+ /**
45
+ * Advisory per-lane execution stats for one ladder rung: how often this config took the lane
46
+ * and how long it took. The median is over the rolling wall-clock window held by
47
+ * `dispatch-lane-stats.ts` (null when the window is empty — unknown, never 0) and `lastAt`
48
+ * is epoch ms of the last recorded run (null when the lane never ran here). Present only
49
+ * when the rung HAS a stats entry; omitted otherwise. Advisory columns only: stats never
50
+ * change a lane's `state`, never change `next`, and never reorder the ladder.
51
+ */
52
+ export interface DispatchLaneStats {
53
+ calls: number;
54
+ successes: number;
55
+ failures: number;
56
+ timeouts: number;
57
+ medianWallClockMs: number | null;
58
+ lastAt: number | null;
59
+ }
60
+ /**
61
+ * One-line advisory rendering of a lane's stats, shared by `dispatch_lanes` and
62
+ * `llm-relay dispatch` so the wording cannot drift between the two surfaces:
63
+ * `stats: 4 calls, 3 ok, 1 failed, 0 timed out, median 24s` (`median n/a` when unknown).
64
+ * Median seconds are rounded to one decimal.
65
+ */
66
+ export declare function formatLaneStats(stats: DispatchLaneStats): string;
44
67
  export interface DispatchLane {
45
68
  id: string;
46
69
  kind: "cli" | "relay";
@@ -117,6 +140,12 @@ export interface DispatchLane {
117
140
  * never auto-selected as `next` — offering a lane known not to work is the defect being fixed.
118
141
  */
119
142
  unreachable?: string;
143
+ /**
144
+ * Advisory execution stats for this rung (`DispatchLaneStats`), filled from
145
+ * `dispatch-lane-stats.ts` for every rung that ran under this config and OMITTED otherwise.
146
+ * Never changes `state`, `next`, or the ladder order — a column, not an input.
147
+ */
148
+ stats?: DispatchLaneStats;
120
149
  }
121
150
  export interface DispatchView {
122
151
  /** Selected tier-specific ladder, or null when using the legacy single ladder. */
@@ -136,6 +165,10 @@ export interface DispatchView {
136
165
  next: DispatchLane | null;
137
166
  /** Why `next` is what it is — including why it is null. */
138
167
  reason: string;
168
+ /** Dispatched task text when one was given. */
169
+ task?: string;
170
+ /** Source of this dispatch view: live daemon, or local fallback when daemon is unreachable. */
171
+ source?: "daemon" | "local-fallback";
139
172
  }
140
173
  export interface DispatchOptions {
141
174
  /** Originating harness (`claude`, `codex`, or a future configured client). */
@@ -263,6 +296,21 @@ export declare function specContextWindow(spec: string, cfg: Config, published:
263
296
  * deliberately narrow: explicit paths and every other configured command remain authoritative.
264
297
  */
265
298
  export declare function normalizeCliCommand(command: string, platform?: NodeJS.Platform): string;
299
+ /**
300
+ * Every rung across EVERY ladder the config declares — each tiered ladder, then the legacy
301
+ * single ladder. The ONE ladder walk: `POST /dispatch/telemetry` (unknown-lane 400) and the
302
+ * CLI's `--by model` lane-id check both read through here or through `findLadderRung`, so a
303
+ * new ladder shape is fixed once. (`markExhausted` stays tier-scoped via `selectLadder` — an
304
+ * exhaustion report arrives on a tier's dispatch view.)
305
+ */
306
+ export declare function allLadderRungs(cfg: Config): LadderRung[];
307
+ /**
308
+ * Find one rung by id across every ladder the config declares, whatever tier holds it.
309
+ * Tier-agnostic on purpose: a telemetry report names a lane, not a tier. `markExhausted`
310
+ * stays tier-scoped (an exhaustion report arrives on a tier's dispatch view); this is the
311
+ * shared lookup both branches mean — one walk, not two.
312
+ */
313
+ export declare function findLadderRung(cfg: Config, laneId: string): LadderRung | undefined;
266
314
  export declare function buildDispatch(cfg: Config, rawOpts?: DispatchOptions, platform?: NodeJS.Platform): DispatchView;
267
315
  export declare const AUTO_TIERS: readonly ["low", "medium", "high", "xhigh"];
268
316
  export type AutoTier = (typeof AUTO_TIERS)[number];
package/dist/dispatch.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { expandPoolSpecs, offloadRule, splitSpec, POOL_PREFIX } from "./config.js";
2
2
  import { unsupportedArgValues, verifyModel } from "./lane-manifest.js";
3
+ import { allLaneStats, medianWallClockMs } from "./dispatch-lane-stats.js";
3
4
  export const DEFAULT_EXHAUSTED_MS = 15 * 60 * 1000;
4
5
  export const MAX_EXHAUSTED_MS = 30 * 24 * 60 * 60 * 1000;
5
6
  export const OUTCOME_DEFAULT_MS = {
@@ -7,6 +8,10 @@ export const OUTCOME_DEFAULT_MS = {
7
8
  quota_exhausted: 60 * 60 * 1000,
8
9
  };
9
10
  const MAX_ECHOED_ID = 120;
11
+ export function formatLaneStats(stats) {
12
+ const median = stats.medianWallClockMs === null ? "n/a" : `${Math.round(stats.medianWallClockMs / 100) / 10}s`;
13
+ return `stats: ${stats.calls} calls, ${stats.successes} ok, ${stats.failures} failed, ${stats.timeouts} timed out, median ${median}`;
14
+ }
10
15
  const cooldowns = new WeakMap();
11
16
  function cooldownsFor(cfg) {
12
17
  let m = cooldowns.get(cfg);
@@ -248,6 +253,16 @@ function selectLadder(cfg, requested) {
248
253
  return { tier: tier ?? null, rungs: [], ...(tier ? { missing: tier } : {}) };
249
254
  return { tier, rungs: cfg.routing.ladders[tier] };
250
255
  }
256
+ export function allLadderRungs(cfg) {
257
+ const ladders = cfg.routing.ladders;
258
+ const tiered = ladders ? Object.values(ladders).flat() : [];
259
+ return [...tiered, ...(cfg.routing.ladder ?? [])];
260
+ }
261
+ export function findLadderRung(cfg, laneId) {
262
+ if (typeof laneId !== "string" || laneId.length === 0)
263
+ return undefined;
264
+ return allLadderRungs(cfg).find((rung) => rung.id === laneId);
265
+ }
251
266
  function stripControlCharacters(value) {
252
267
  let out = "";
253
268
  for (let i = 0; i < value.length; i++) {
@@ -409,6 +424,20 @@ export function buildDispatch(cfg, rawOpts = {}, platform = process.platform) {
409
424
  const selected = selectLadder(cfg, opts.tier);
410
425
  const rungs = selected.rungs;
411
426
  const ladder = rungs.map((r, i) => toLane(r, i + 1, cfg, opts, now, client, platform, opts.host, opts.entrypoint));
427
+ const statsByLane = new Map(allLaneStats(cfg).map((row) => [row.laneId, row]));
428
+ for (const lane of ladder) {
429
+ const row = statsByLane.get(lane.id);
430
+ if (row === undefined)
431
+ continue;
432
+ lane.stats = {
433
+ calls: row.calls,
434
+ successes: row.successes,
435
+ failures: row.failures,
436
+ timeouts: row.timeouts,
437
+ medianWallClockMs: medianWallClockMs(row.wallClockMs),
438
+ lastAt: row.lastAt,
439
+ };
440
+ }
412
441
  const offload = offloadRule(cfg, client).enabled;
413
442
  const base = { tier: selected.tier, offload, client, host, ladder };
414
443
  if (selected.missing) {