llm-relay 0.80.0 → 0.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend.d.ts +43 -3
- package/dist/backend.js +0 -0
- package/dist/backend.js.map +1 -1
- package/dist/candidate-runner.d.ts +45 -9
- package/dist/candidate-runner.js +55 -22
- package/dist/candidate-runner.js.map +1 -1
- package/dist/catalog.d.ts +49 -0
- package/dist/catalog.js +18 -0
- package/dist/catalog.js.map +1 -1
- package/dist/circuit-breaker.d.ts +118 -1
- package/dist/circuit-breaker.js +96 -1
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +23 -3
- package/dist/cli.js.map +1 -1
- package/dist/config/routing-parser.d.ts +1 -1
- package/dist/config/routing-parser.js +27 -1
- package/dist/config/routing-parser.js.map +1 -1
- package/dist/config-edit.js +70 -16
- package/dist/config-edit.js.map +1 -1
- package/dist/config-types.d.ts +62 -6
- package/dist/config-types.js +1 -1
- package/dist/config-types.js.map +1 -1
- package/dist/dashboard/.vite/manifest.json +2 -2
- package/dist/dashboard/assets/index-B2UQHoym.js +74 -0
- package/dist/dashboard/assets/index-DFEP3XPr.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dispatch-lane-stats.d.ts +59 -5
- package/dist/dispatch-lane-stats.js +96 -29
- package/dist/dispatch-lane-stats.js.map +1 -1
- package/dist/dispatch.d.ts +143 -1
- package/dist/dispatch.js +260 -98
- package/dist/dispatch.js.map +1 -1
- package/dist/lane-affinity.d.ts +8 -0
- package/dist/lane-affinity.js +10 -0
- package/dist/lane-affinity.js.map +1 -1
- package/dist/log.d.ts +16 -0
- package/dist/log.js +1 -0
- package/dist/log.js.map +1 -1
- package/dist/mcp/agy-quota-log.d.ts +37 -0
- package/dist/mcp/agy-quota-log.js +68 -0
- package/dist/mcp/agy-quota-log.js.map +1 -0
- package/dist/mcp/job-archive.d.ts +55 -0
- package/dist/mcp/job-archive.js +151 -0
- package/dist/mcp/job-archive.js.map +1 -0
- package/dist/mcp/lane-runner.d.ts +108 -5
- package/dist/mcp/lane-runner.js +93 -4
- package/dist/mcp/lane-runner.js.map +1 -1
- package/dist/mcp/readonly-boundary.d.ts +46 -0
- package/dist/mcp/readonly-boundary.js +119 -0
- package/dist/mcp/readonly-boundary.js.map +1 -1
- package/dist/mcp/server.d.ts +132 -8
- package/dist/mcp/server.js +267 -68
- package/dist/mcp/server.js.map +1 -1
- package/dist/openai-request.d.ts +8 -0
- package/dist/openai-request.js +33 -9
- package/dist/openai-request.js.map +1 -1
- package/dist/pacing.d.ts +125 -0
- package/dist/pacing.js +83 -0
- package/dist/pacing.js.map +1 -0
- package/dist/ping/cadence.d.ts +57 -0
- package/dist/ping/cadence.js +58 -0
- package/dist/ping/cadence.js.map +1 -1
- package/dist/refusal-interpretation.d.ts +24 -0
- package/dist/refusal-interpretation.js +10 -1
- package/dist/refusal-interpretation.js.map +1 -1
- package/dist/responses-request.d.ts +14 -1
- package/dist/responses-request.js +21 -4
- package/dist/responses-request.js.map +1 -1
- package/dist/routes/admin.d.ts +8 -0
- package/dist/routes/admin.js +122 -35
- package/dist/routes/admin.js.map +1 -1
- package/dist/routes/messages.d.ts +2 -0
- package/dist/routes/messages.js +7 -3
- package/dist/routes/messages.js.map +1 -1
- package/dist/routes/openai-front.d.ts +1 -0
- package/dist/routes/openai-front.js +7 -3
- package/dist/routes/openai-front.js.map +1 -1
- package/dist/server.d.ts +4 -0
- package/dist/server.js +28 -4
- package/dist/server.js.map +1 -1
- package/dist/setup-claude.d.ts +2 -2
- package/dist/setup-claude.js +3 -3
- package/dist/stream-commit.d.ts +65 -0
- package/dist/stream-commit.js +150 -8
- package/dist/stream-commit.js.map +1 -1
- package/dist/target-facts.js +3 -0
- package/dist/target-facts.js.map +1 -1
- package/dist/telemetry.d.ts +9 -1
- package/dist/telemetry.js +1 -1
- package/dist/telemetry.js.map +1 -1
- package/package.json +1 -1
- package/scripts/install-skill.mjs +2 -2
- package/skills/llm-relay/SKILL.md +5 -3
- package/skills/llm-relay/references/dispatch-lanes.md +8 -2
- package/dist/dashboard/assets/index-Bqtx14ES.css +0 -1
- package/dist/dashboard/assets/index-CetZdLCA.js +0 -74
package/dist/mcp/server.d.ts
CHANGED
|
@@ -22,9 +22,11 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import type { Config } from "../config.js";
|
|
24
24
|
import type { DispatchView } from "../dispatch.js";
|
|
25
|
-
import type { DispatchedTelemetryReport } from "../dispatch-lane-stats.js";
|
|
26
|
-
import { type AnswerFetch, type LaneSpawner, type DispatchedQuotaReport } from "./lane-runner.js";
|
|
25
|
+
import type { DispatchedTelemetryReport, DispatchMode } from "../dispatch-lane-stats.js";
|
|
26
|
+
import { type AnswerFetch, type LaneJob, type LaneSpawner, type DispatchedQuotaReport } from "./lane-runner.js";
|
|
27
|
+
import { type AgyLogSnapshot } from "./agy-quota-log.js";
|
|
27
28
|
import { type JobJournal } from "./job-journal.js";
|
|
29
|
+
import { type JobArchive } from "./job-archive.js";
|
|
28
30
|
export declare const MCP_SERVER_NAME = "llm-relay";
|
|
29
31
|
/** `max_tokens` for an answer-mode call when the caller names none. */
|
|
30
32
|
export declare const DEFAULT_ANSWER_MAX_TOKENS = 4096;
|
|
@@ -37,6 +39,13 @@ export type DispatchViewBuilder = (opts: {
|
|
|
37
39
|
task: string | undefined;
|
|
38
40
|
tier: string | undefined;
|
|
39
41
|
lane: string | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* How this dispatch will run its lanes, so the relay hands back budgets measured in the SAME mode
|
|
44
|
+
* (`DispatchMode`). Absent from `dispatch_lanes`, which runs nothing.
|
|
45
|
+
*/
|
|
46
|
+
mode?: DispatchMode | undefined;
|
|
47
|
+
/** A routing spec to run as its own one-lane view instead of the ladder (`dispatch`'s `model`). */
|
|
48
|
+
model?: string | undefined;
|
|
40
49
|
}) => Promise<DispatchView>;
|
|
41
50
|
export interface McpServerDeps {
|
|
42
51
|
config: Config;
|
|
@@ -74,6 +83,24 @@ export interface McpServerDeps {
|
|
|
74
83
|
* real one, so a host-launched server can report the jobs a previous instance died holding.
|
|
75
84
|
*/
|
|
76
85
|
journal?: JobJournal;
|
|
86
|
+
/**
|
|
87
|
+
* The finished-job archive. Absent ⇒ `nullJobArchive`, which keeps nothing. `cli.ts` passes the
|
|
88
|
+
* real one, so a job that ENDED before a restart still answers `dispatch_result` afterwards and
|
|
89
|
+
* job ids continue past the highest one the previous process minted (`job-archive.ts`).
|
|
90
|
+
*/
|
|
91
|
+
archive?: JobArchive;
|
|
92
|
+
/**
|
|
93
|
+
* The llm-relay version INSTALLED on disk now, read fresh on each call — or null when unknown.
|
|
94
|
+
* When it differs from `version` (the code this process started with), every tool reply says so:
|
|
95
|
+
* the Claude desktop app keeps one MCP process alive across sessions and releases, and on
|
|
96
|
+
* 2026-09-10 two processes from before v0.78.0 were still serving old code with no sign of it.
|
|
97
|
+
*/
|
|
98
|
+
installedVersion?: () => string | null;
|
|
99
|
+
/**
|
|
100
|
+
* AGY's log, read after an AGY lane ends or is stopped, so a quota death AGY stated only in its
|
|
101
|
+
* log still reaches the relay (`agy-quota-log.ts`). Absent ⇒ never read.
|
|
102
|
+
*/
|
|
103
|
+
readAgyLog?: () => AgyLogSnapshot | null;
|
|
77
104
|
write: (chunk: string) => void;
|
|
78
105
|
}
|
|
79
106
|
/**
|
|
@@ -108,6 +135,16 @@ export type ResolvedWaitMs = {
|
|
|
108
135
|
refusal: string;
|
|
109
136
|
};
|
|
110
137
|
export declare function resolveWaitMs(requested: unknown, ceiling: number): ResolvedWaitMs;
|
|
138
|
+
/**
|
|
139
|
+
* A RUNNING spawned attempt's output so far: how long the lane has been silent, or how much it has
|
|
140
|
+
* written and how long ago. Null for anything else — a finished job, or an answer-mode call, which
|
|
141
|
+
* has no output stream and must not read as "silent".
|
|
142
|
+
*
|
|
143
|
+
* ⚠ The zero-output line says why silence alone proves nothing: `claude -p` buffers its whole
|
|
144
|
+
* answer until exit. Without that, a caller reading "silent for 300 s" on the free pool would cancel
|
|
145
|
+
* a healthy run — the false failure the backlog item names as worse than an honest slow status.
|
|
146
|
+
*/
|
|
147
|
+
export declare function describeActivity(job: LaneJob, now: number): string | null;
|
|
111
148
|
/**
|
|
112
149
|
* What a caller is told when the walk tried every lane it had and none of them answered.
|
|
113
150
|
*
|
|
@@ -132,9 +169,24 @@ export declare const LANE_LADDER_EXHAUSTED_ADVICE: string;
|
|
|
132
169
|
* (found by adversarial review, 2026-09-06). Both of its sentences were then false: lanes remained,
|
|
133
170
|
* and "it would pick the same lanes" is wrong precisely because the walk has just DEMOTED every
|
|
134
171
|
* lane it tried, so the next dispatch reorders around them. Telling an autonomous caller to stop
|
|
135
|
-
* delegating, on a false premise, abandons
|
|
172
|
+
* delegating, on a false premise, abandons capacity that was never contacted.
|
|
136
173
|
*/
|
|
137
174
|
export declare const LANE_LADDER_PARTIAL_ADVICE: string;
|
|
175
|
+
/**
|
|
176
|
+
* What a caller is told when the walk STOPPED a lane at its time budget and nothing answered after.
|
|
177
|
+
*
|
|
178
|
+
* ⚠ The stopped lane did not fail: it was still working when the walk moved on. So "every dispatch
|
|
179
|
+
* lane has now been tried" was false there, and it ended the caller's use of dispatch for a task the
|
|
180
|
+
* stopped lane would have finished (`docs/dispatch-giveup-diagnosis-2026-09-10.md` §5). A NAMED lane
|
|
181
|
+
* runs with no budget, so this names the call that lets it finish.
|
|
182
|
+
*/
|
|
183
|
+
export declare function laneStoppedAdvice(laneId: string): string;
|
|
184
|
+
/**
|
|
185
|
+
* What a caller is told when it NAMED the lane or the model and that one lane did not answer. Only
|
|
186
|
+
* that lane ran, so "every dispatch lane has now been tried" would be false — measured 2026-09-10 on
|
|
187
|
+
* jobs 0023 and 0024, which each ran one forced lane and were told to stop delegating.
|
|
188
|
+
*/
|
|
189
|
+
export declare const FORCED_LANE_ADVICE: string;
|
|
138
190
|
export declare class McpDispatchServer {
|
|
139
191
|
private readonly deps;
|
|
140
192
|
private readonly jobs;
|
|
@@ -173,18 +225,51 @@ export declare class McpDispatchServer {
|
|
|
173
225
|
* takes the loop down. Resolves after the source ends AND every handler it started has settled.
|
|
174
226
|
*/
|
|
175
227
|
serve(source: AsyncIterable<string>): Promise<void>;
|
|
176
|
-
/**
|
|
228
|
+
/**
|
|
229
|
+
* Kill every running child, then flush the finished-job archive. Wired to process exit so nothing
|
|
230
|
+
* is orphaned and nothing the archive still owes to disk is lost on a graceful stop (a hard kill
|
|
231
|
+
* is covered by the archive's eager terminal writes — `job-archive.ts`).
|
|
232
|
+
*/
|
|
177
233
|
shutdown(): void;
|
|
178
234
|
private send;
|
|
179
235
|
private handleLine;
|
|
180
236
|
private route;
|
|
181
237
|
private initialize;
|
|
182
238
|
private callTool;
|
|
239
|
+
private runTool;
|
|
240
|
+
/**
|
|
241
|
+
* Append a notice to a tool reply when this process runs OLDER code than the version installed on
|
|
242
|
+
* disk. The Claude desktop app keeps one MCP process alive across sessions and releases, and on
|
|
243
|
+
* 2026-09-10 two processes from before v0.78.0 were still serving old code with nothing to say so.
|
|
244
|
+
* Only a plain one-block text reply is touched; anything else passes through unchanged. A version
|
|
245
|
+
* that cannot be read is unknown, and unknown adds nothing.
|
|
246
|
+
*/
|
|
247
|
+
private withVersionNotice;
|
|
183
248
|
private toolLanes;
|
|
184
249
|
private toolStatus;
|
|
185
250
|
private toolResult;
|
|
186
251
|
private toolCancel;
|
|
187
252
|
private toolDispatch;
|
|
253
|
+
/**
|
|
254
|
+
* The lanes this dispatch will try, best first, and how many selectable lanes it leaves untried.
|
|
255
|
+
*
|
|
256
|
+
* `view.order` is the ONE definition of selection order (`dispatch.ts`). With the walk turned off
|
|
257
|
+
* — or with a view that carries no order — this collapses to EXACTLY the pre-walk behaviour: the
|
|
258
|
+
* single lane the view named as `next`, tried once, with no attempt budget.
|
|
259
|
+
*
|
|
260
|
+
* ⚠ The `Array.isArray` test is a VERSION SKEW guard, not defensive noise. `buildView` reaches the
|
|
261
|
+
* running daemon over HTTP, and a daemon started before this field existed answers without it — an
|
|
262
|
+
* MCP child upgraded ahead of a long-running daemon is the normal state on a machine that starts
|
|
263
|
+
* the relay at logon and leaves it up for days. Reading `.length` off `undefined` there would throw
|
|
264
|
+
* on EVERY dispatch, turning a new optional field into a total outage.
|
|
265
|
+
*
|
|
266
|
+
* ⚠ A second skew guard: a daemon older than `requester=mcp` still offers a pass-through rung as
|
|
267
|
+
* ready, and this process can never run one (`mcpPassThroughReason`). Such a lane is dropped from an
|
|
268
|
+
* UNFORCED walk here; a caller who named it still reaches it and reads why it cannot run.
|
|
269
|
+
*/
|
|
270
|
+
private walkOrder;
|
|
271
|
+
/** A pass-through relay lane — one this process cannot run (`walkOrder`). Never throws. */
|
|
272
|
+
private isPassThroughLane;
|
|
188
273
|
/**
|
|
189
274
|
* Try each lane in turn until one answers. THE JOB IS THE WALK.
|
|
190
275
|
*
|
|
@@ -199,6 +284,16 @@ export declare class McpDispatchServer {
|
|
|
199
284
|
* with it. Only a caller error ends the walk early — see `refusal` on `LaneAttemptOutcome`.
|
|
200
285
|
*/
|
|
201
286
|
private runWalk;
|
|
287
|
+
/**
|
|
288
|
+
* Does the lane at position `i` run with NO attempt budget? The last lane does — there is nowhere
|
|
289
|
+
* to move to. So does a lane whose later lanes are all unlikely to answer: each is on a streak of
|
|
290
|
+
* `LANE_UNRELIABLE_STREAK` own failures or more, or marked `failing`. Stopping a lane that may
|
|
291
|
+
* still answer in order to reach those trades an answer for a near-certain failure — measured
|
|
292
|
+
* 2026-09-10, when the walk stopped `free-pool` at 90 s to try lanes that had answered 0 of 12,
|
|
293
|
+
* 0 of 34 and 0 of 21 runs (`docs/dispatch-giveup-diagnosis-2026-09-10.md` §1). A lane with no
|
|
294
|
+
* record counts as reliable: unmeasured is no opinion, never a failure.
|
|
295
|
+
*/
|
|
296
|
+
private budgetWithheld;
|
|
202
297
|
/**
|
|
203
298
|
* A `cli` rung whose configured `maxConcurrent` this MCP server process has already reached for
|
|
204
299
|
* — SKIP it for this walk rather than starting a competing process. Records the skip as an
|
|
@@ -211,6 +306,18 @@ export declare class McpDispatchServer {
|
|
|
211
306
|
* always `null`, so this returns `false` for one without needing a `kind` check of its own.
|
|
212
307
|
*/
|
|
213
308
|
private skipIfAtConcurrencyCap;
|
|
309
|
+
/**
|
|
310
|
+
* For a read-only dispatch, the invocation this lane will be spawned with — its own CLI's
|
|
311
|
+
* read-only tool binding (`readOnlyInvoke`) — or a SKIP when its CLI offers none. Recorded exactly
|
|
312
|
+
* like a concurrency-cap skip: an attempt in the walk's own record, `lanesNotTried` grown so the
|
|
313
|
+
* terminal advice is PARTIAL rather than EXHAUSTED, nothing spawned, no telemetry, no demotion —
|
|
314
|
+
* a lane the relay declined to run unbound has not failed.
|
|
315
|
+
*
|
|
316
|
+
* Not consulted at all for an ordinary dispatch, for an answer-mode relay call (no harness, so
|
|
317
|
+
* nothing to bind), or for a lane with no invocation (the existing "cannot be run from here" path
|
|
318
|
+
* reports that on its own).
|
|
319
|
+
*/
|
|
320
|
+
private bindReadOnlyOrSkip;
|
|
214
321
|
/**
|
|
215
322
|
* Run ONE lane, optionally bounded by an attempt budget. Returns what happened; never throws.
|
|
216
323
|
*
|
|
@@ -236,10 +343,27 @@ export declare class McpDispatchServer {
|
|
|
236
343
|
*/
|
|
237
344
|
private startLane;
|
|
238
345
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
346
|
+
* Settle a spawned lane's run. A quota death the lane states — in its own output, or for an AGY
|
|
347
|
+
* lane in AGY's own log — is reported to the relay; a content-empty answer is a failure.
|
|
348
|
+
*
|
|
349
|
+
* ⚠ It also runs for a lane the walk STOPPED: the killed child still settles here, so a death AGY
|
|
350
|
+
* stated only in its log reaches the relay although the walk has moved on. The outcome itself is
|
|
351
|
+
* discarded in that case — `runOneLane` already returned `abandoned`.
|
|
352
|
+
*/
|
|
353
|
+
private settleSpawnedRun;
|
|
354
|
+
/**
|
|
355
|
+
* The quota death an AGY lane stated only in AGY's own log (`agy-quota-log.ts`). AGY retries a
|
|
356
|
+
* spent quota in silence, so a lane stopped by the walk or by its own timeout printed nothing, and
|
|
357
|
+
* the death reached the relay only when a run happened to last its whole length
|
|
358
|
+
* (`docs/dispatch-giveup-diagnosis-2026-09-10.md` §4). Undefined unless the lane is AGY, names its
|
|
359
|
+
* model, and the log is provably this run's — `agyQuotaStatement` refuses everything else.
|
|
360
|
+
*/
|
|
361
|
+
private agyLogReport;
|
|
362
|
+
/**
|
|
363
|
+
* Forward one lane-execution report per ATTEMPT, for both lane kinds: the daemon records the
|
|
364
|
+
* lane's stats and routing memory from these, and skips the ledger row for a `relay` lane on its
|
|
365
|
+
* own because the HTTP pipeline already accounts it. Never for a cancelled job, and never for an
|
|
366
|
+
* ad-hoc lane (`DispatchLane.adHoc`), which the daemon has no rung to record against.
|
|
243
367
|
*
|
|
244
368
|
* Fire-and-forget by design: the reporter is never awaited on the response path, and a
|
|
245
369
|
* throwing or rejecting reporter is swallowed after ONE metadata-only stderr line (lane id
|