llm-relay 0.35.0 → 0.37.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 (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -0
  3. package/THIRD_PARTY_NOTICES.md +72 -0
  4. package/config.example.json +8 -2
  5. package/dist/accounting-store-io.d.ts +124 -0
  6. package/dist/accounting-store-io.js +1105 -0
  7. package/dist/accounting-store-io.js.map +1 -0
  8. package/dist/accounting-store-schema.d.ts +299 -0
  9. package/dist/accounting-store-schema.js +1021 -0
  10. package/dist/accounting-store-schema.js.map +1 -0
  11. package/dist/accounting-store.d.ts +73 -0
  12. package/dist/accounting-store.js +1523 -0
  13. package/dist/accounting-store.js.map +1 -0
  14. package/dist/accounting.d.ts +177 -0
  15. package/dist/accounting.js +429 -0
  16. package/dist/accounting.js.map +1 -0
  17. package/dist/anthropic.d.ts +2 -2
  18. package/dist/anthropic.js +22 -5
  19. package/dist/anthropic.js.map +1 -1
  20. package/dist/authEnv.d.ts +19 -0
  21. package/dist/authEnv.js +58 -25
  22. package/dist/authEnv.js.map +1 -1
  23. package/dist/backend.d.ts +32 -3
  24. package/dist/backend.js +324 -41
  25. package/dist/backend.js.map +1 -1
  26. package/dist/candidates.d.ts +28 -3
  27. package/dist/candidates.js +86 -13
  28. package/dist/candidates.js.map +1 -1
  29. package/dist/catalog.d.ts +1 -0
  30. package/dist/catalog.js +36 -12
  31. package/dist/catalog.js.map +1 -1
  32. package/dist/circuit-breaker.d.ts +49 -129
  33. package/dist/circuit-breaker.js +166 -223
  34. package/dist/circuit-breaker.js.map +1 -1
  35. package/dist/cli.d.ts +47 -15
  36. package/dist/cli.js +475 -134
  37. package/dist/cli.js.map +1 -1
  38. package/dist/config.d.ts +23 -1
  39. package/dist/config.js +189 -19
  40. package/dist/config.js.map +1 -1
  41. package/dist/context-limits.js +2 -1
  42. package/dist/context-limits.js.map +1 -1
  43. package/dist/control-authorization.js +6 -4
  44. package/dist/control-authorization.js.map +1 -1
  45. package/dist/credential-fleet.d.ts +41 -0
  46. package/dist/credential-fleet.js +78 -0
  47. package/dist/credential-fleet.js.map +1 -0
  48. package/dist/credential-id.d.ts +13 -0
  49. package/dist/credential-id.js +22 -0
  50. package/dist/credential-id.js.map +1 -0
  51. package/dist/credential-select.d.ts +86 -0
  52. package/dist/credential-select.js +303 -0
  53. package/dist/credential-select.js.map +1 -0
  54. package/dist/dashboard/.vite/dashboard-bundle-graph.json +32 -0
  55. package/dist/dashboard/.vite/manifest.json +11 -0
  56. package/dist/dashboard/assets/index-BeZxN4Lx.css +1 -0
  57. package/dist/dashboard/assets/index-RtLgb7Cg.js +74 -0
  58. package/dist/dashboard/index.html +4 -0
  59. package/dist/dashboard-auth.d.ts +68 -0
  60. package/dist/dashboard-auth.js +225 -0
  61. package/dist/dashboard-auth.js.map +1 -0
  62. package/dist/dashboard-contract.d.ts +391 -0
  63. package/dist/dashboard-contract.js +619 -0
  64. package/dist/dashboard-contract.js.map +1 -0
  65. package/dist/dashboard-routes.d.ts +91 -0
  66. package/dist/dashboard-routes.js +688 -0
  67. package/dist/dashboard-routes.js.map +1 -0
  68. package/dist/dashboard-snapshot.d.ts +36 -0
  69. package/dist/dashboard-snapshot.js +1132 -0
  70. package/dist/dashboard-snapshot.js.map +1 -0
  71. package/dist/dashboard-static.d.ts +61 -0
  72. package/dist/dashboard-static.js +326 -0
  73. package/dist/dashboard-static.js.map +1 -0
  74. package/dist/dispatch.js +11 -6
  75. package/dist/dispatch.js.map +1 -1
  76. package/dist/dynamic-pools.js +6 -4
  77. package/dist/dynamic-pools.js.map +1 -1
  78. package/dist/kernel/contracts.d.ts +4 -0
  79. package/dist/kernel/request-lifecycle.js +4 -1
  80. package/dist/kernel/request-lifecycle.js.map +1 -1
  81. package/dist/key-checker.d.ts +3 -1
  82. package/dist/key-checker.js +35 -11
  83. package/dist/key-checker.js.map +1 -1
  84. package/dist/key-import.d.ts +29 -0
  85. package/dist/key-import.js +160 -0
  86. package/dist/key-import.js.map +1 -0
  87. package/dist/log.d.ts +20 -2
  88. package/dist/log.js +53 -2
  89. package/dist/log.js.map +1 -1
  90. package/dist/offload.d.ts +9 -0
  91. package/dist/offload.js +7 -1
  92. package/dist/offload.js.map +1 -1
  93. package/dist/onboarding.d.ts +3 -3
  94. package/dist/onboarding.js +12 -6
  95. package/dist/onboarding.js.map +1 -1
  96. package/dist/openai-dialect.d.ts +42 -0
  97. package/dist/openai-dialect.js +388 -0
  98. package/dist/openai-dialect.js.map +1 -0
  99. package/dist/ping/cadence.d.ts +12 -3
  100. package/dist/ping/cadence.js +68 -15
  101. package/dist/ping/cadence.js.map +1 -1
  102. package/dist/ping/ping.d.ts +7 -3
  103. package/dist/ping/ping.js +11 -35
  104. package/dist/ping/ping.js.map +1 -1
  105. package/dist/ping/probe-cache.d.ts +14 -6
  106. package/dist/ping/probe-cache.js +20 -5
  107. package/dist/ping/probe-cache.js.map +1 -1
  108. package/dist/ping/runtime-telemetry.d.ts +2 -1
  109. package/dist/ping/runtime-telemetry.js +103 -10
  110. package/dist/ping/runtime-telemetry.js.map +1 -1
  111. package/dist/pool-health.d.ts +2 -0
  112. package/dist/pool-health.js +29 -13
  113. package/dist/pool-health.js.map +1 -1
  114. package/dist/quota-observation.d.ts +29 -0
  115. package/dist/quota-observation.js +249 -0
  116. package/dist/quota-observation.js.map +1 -0
  117. package/dist/refusal-interpretation.d.ts +13 -7
  118. package/dist/refusal-interpretation.js +260 -33
  119. package/dist/refusal-interpretation.js.map +1 -1
  120. package/dist/registry.d.ts +13 -2
  121. package/dist/registry.js +12 -4
  122. package/dist/registry.js.map +1 -1
  123. package/dist/repair.d.ts +4 -2
  124. package/dist/repair.js +15 -1
  125. package/dist/repair.js.map +1 -1
  126. package/dist/request-log.d.ts +2 -2
  127. package/dist/request-log.js +9 -1
  128. package/dist/request-log.js.map +1 -1
  129. package/dist/reshaper.d.ts +76 -11
  130. package/dist/reshaper.js +264 -29
  131. package/dist/reshaper.js.map +1 -1
  132. package/dist/resolved-attempt.d.ts +14 -0
  133. package/dist/resolved-attempt.js +21 -0
  134. package/dist/resolved-attempt.js.map +1 -0
  135. package/dist/routes/admin.js +7 -1
  136. package/dist/routes/admin.js.map +1 -1
  137. package/dist/secret-file-acl.d.ts +25 -0
  138. package/dist/secret-file-acl.js +37 -0
  139. package/dist/secret-file-acl.js.map +1 -0
  140. package/dist/server.d.ts +28 -3
  141. package/dist/server.js +1774 -467
  142. package/dist/server.js.map +1 -1
  143. package/dist/session-pin.d.ts +35 -0
  144. package/dist/session-pin.js +136 -0
  145. package/dist/session-pin.js.map +1 -0
  146. package/dist/stream-commit.d.ts +34 -0
  147. package/dist/stream-commit.js +325 -0
  148. package/dist/stream-commit.js.map +1 -0
  149. package/dist/target-facts.d.ts +23 -136
  150. package/dist/target-facts.js +138 -179
  151. package/dist/target-facts.js.map +1 -1
  152. package/dist/telemetry.d.ts +1 -25
  153. package/dist/telemetry.js +52 -45
  154. package/dist/telemetry.js.map +1 -1
  155. package/dist/think-tags.d.ts +34 -0
  156. package/dist/think-tags.js +219 -0
  157. package/dist/think-tags.js.map +1 -0
  158. package/dist/usage-observer.d.ts +34 -0
  159. package/dist/usage-observer.js +322 -0
  160. package/dist/usage-observer.js.map +1 -0
  161. package/dist/winenv.js +26 -4
  162. package/dist/winenv.js.map +1 -1
  163. package/docs/dashboard-bundle-inventory.json +16 -0
  164. package/package.json +29 -6
  165. package/skills/llm-relay/SKILL.md +48 -9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-pin.js","sourceRoot":"","sources":["../src/session-pin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Final-wire streamed response commit probe.
3
+ *
4
+ * A structurally valid SSE preamble is not yet an answer. Keep the downstream head provisional
5
+ * until the client-facing protocol carries meaningful assistant output, then replay every byte
6
+ * exactly as received. Before that point, an in-band error or an empty completion is safe to
7
+ * fail over because the client has observed nothing.
8
+ */
9
+ export type StreamCommitProtocol = "anthropic-messages" | "openai-chat" | "openai-responses";
10
+ export type StreamCommitProbe = {
11
+ kind: "ready";
12
+ body: ReadableStream<Uint8Array>;
13
+ } | {
14
+ kind: "dead";
15
+ reason: string;
16
+ provenance: "upstream" | "local";
17
+ } | {
18
+ kind: "cancelled";
19
+ };
20
+ export interface StreamCommitProbeOptions {
21
+ /** Client cancellation wins races with EOF/read failures and must never start another target. */
22
+ isCancelled?: () => boolean;
23
+ /** Malformed final wire produced by a response mapper is a local defect, not target health. */
24
+ malformedProvenance?: "upstream" | "local";
25
+ /** A rejected body read is normally transport/upstream even when parsing is mapper-local. */
26
+ readFailureProvenance?: "upstream" | "local";
27
+ }
28
+ /** Shared by structural preflight and final-wire commit probing. */
29
+ export declare const STREAM_PREFLIGHT_LIMIT: number;
30
+ /**
31
+ * Read through metadata-only SSE events until the final wire contains meaningful assistant output.
32
+ * The returned stream replays the exact raw chunks and continues on the same locked reader.
33
+ */
34
+ export declare function probeStreamForCommit(body: ReadableStream<Uint8Array>, protocol: StreamCommitProtocol, options?: StreamCommitProbeOptions): Promise<StreamCommitProbe>;
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Final-wire streamed response commit probe.
3
+ *
4
+ * A structurally valid SSE preamble is not yet an answer. Keep the downstream head provisional
5
+ * until the client-facing protocol carries meaningful assistant output, then replay every byte
6
+ * exactly as received. Before that point, an in-band error or an empty completion is safe to
7
+ * fail over because the client has observed nothing.
8
+ */
9
+ /** Shared by structural preflight and final-wire commit probing. */
10
+ export const STREAM_PREFLIGHT_LIMIT = 64 * 1024;
11
+ const HOLD = { kind: "hold" };
12
+ const READY = { kind: "ready" };
13
+ function isRecord(value) {
14
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15
+ }
16
+ function nonWhitespace(value) {
17
+ return typeof value === "string" && value.trim().length > 0;
18
+ }
19
+ function substantive(value) {
20
+ if (nonWhitespace(value))
21
+ return true;
22
+ if (typeof value === "number" || typeof value === "boolean")
23
+ return true;
24
+ if (Array.isArray(value))
25
+ return value.some(substantive);
26
+ if (isRecord(value))
27
+ return Object.values(value).some(substantive);
28
+ return false;
29
+ }
30
+ function substantiveFields(value, ignored) {
31
+ return Object.entries(value).some(([key, field]) => !ignored.has(key) && substantive(field));
32
+ }
33
+ function boundedError(value) {
34
+ const error = isRecord(value.error) ? value.error : value;
35
+ const message = typeof error.message === "string" ? error.message.trim().slice(0, 200) : "";
36
+ return message ? `in-band error before meaningful content: ${message}` : "in-band error before meaningful content";
37
+ }
38
+ function anthropicVerdict(value) {
39
+ const type = typeof value.type === "string" ? value.type : "";
40
+ if (type === "error" || isRecord(value.error)) {
41
+ return { kind: "dead", reason: boundedError(value), provenance: "upstream" };
42
+ }
43
+ if (type === "message_stop") {
44
+ return { kind: "dead", reason: "stream completed without meaningful content", provenance: "upstream" };
45
+ }
46
+ if (type === "content_block_start") {
47
+ if (!isRecord(value.content_block))
48
+ return HOLD;
49
+ const block = value.content_block;
50
+ const blockType = typeof block.type === "string" ? block.type : "";
51
+ if (blockType === "tool_use") {
52
+ return nonWhitespace(block.id) || nonWhitespace(block.name) ? READY : HOLD;
53
+ }
54
+ if (blockType === "text")
55
+ return nonWhitespace(block.text) ? READY : HOLD;
56
+ if (blockType === "thinking")
57
+ return nonWhitespace(block.thinking) ? READY : HOLD;
58
+ // Redacted thinking and future opaque content blocks are output only when they carry an
59
+ // actual payload; a type/index skeleton remains provisional.
60
+ return substantiveFields(block, new Set(["type", "index"])) ? READY : HOLD;
61
+ }
62
+ if (type === "content_block_delta") {
63
+ if (!isRecord(value.delta))
64
+ return HOLD;
65
+ const delta = value.delta;
66
+ const deltaType = typeof delta.type === "string" ? delta.type : "";
67
+ if (deltaType === "text_delta")
68
+ return nonWhitespace(delta.text) ? READY : HOLD;
69
+ if (deltaType === "thinking_delta")
70
+ return nonWhitespace(delta.thinking) ? READY : HOLD;
71
+ // Tool arguments follow a meaningful tool_use start. Signatures are metadata, not content.
72
+ if (deltaType === "input_json_delta" || deltaType === "signature_delta")
73
+ return HOLD;
74
+ return substantiveFields(delta, new Set(["type", "index"])) ? READY : HOLD;
75
+ }
76
+ // Unknown future Anthropic content events are allowed to commit only when their content-shaped
77
+ // payload is substantive. Metadata-only event records stay held.
78
+ if (type.startsWith("content_block_")) {
79
+ if (isRecord(value.content_block) && substantiveFields(value.content_block, new Set(["type", "index"])))
80
+ return READY;
81
+ if (isRecord(value.delta) && substantiveFields(value.delta, new Set(["type", "index"])))
82
+ return READY;
83
+ }
84
+ return HOLD;
85
+ }
86
+ function chatToolCallMeaningful(value) {
87
+ if (!isRecord(value))
88
+ return false;
89
+ if (nonWhitespace(value.id))
90
+ return true;
91
+ if (!isRecord(value.function))
92
+ return false;
93
+ return nonWhitespace(value.function.name) || nonWhitespace(value.function.arguments);
94
+ }
95
+ function openAiChatVerdict(value) {
96
+ if (isRecord(value.error) || value.type === "error") {
97
+ return { kind: "dead", reason: boundedError(value), provenance: "upstream" };
98
+ }
99
+ if (!Array.isArray(value.choices)) {
100
+ return { kind: "dead", reason: "OpenAI stream event is missing choices", provenance: "upstream" };
101
+ }
102
+ if (value.choices.length === 0)
103
+ return HOLD; // usage-only frame
104
+ let finished = false;
105
+ for (const rawChoice of value.choices) {
106
+ if (!isRecord(rawChoice)) {
107
+ return { kind: "dead", reason: "OpenAI stream choice is not an object", provenance: "upstream" };
108
+ }
109
+ if (rawChoice.finish_reason !== null && rawChoice.finish_reason !== undefined)
110
+ finished = true;
111
+ if (!isRecord(rawChoice.delta))
112
+ continue;
113
+ const delta = rawChoice.delta;
114
+ if (nonWhitespace(delta.content) ||
115
+ nonWhitespace(delta.refusal) ||
116
+ nonWhitespace(delta.reasoning_content) ||
117
+ nonWhitespace(delta.reasoning))
118
+ return READY;
119
+ if (Array.isArray(delta.tool_calls) && delta.tool_calls.some(chatToolCallMeaningful))
120
+ return READY;
121
+ if (substantiveFields(delta, new Set(["role", "content", "refusal", "reasoning_content", "reasoning", "tool_calls"])))
122
+ return READY;
123
+ }
124
+ return finished
125
+ ? { kind: "dead", reason: "stream completed without meaningful content", provenance: "upstream" }
126
+ : HOLD;
127
+ }
128
+ function responsesItemMeaningful(value) {
129
+ if (!isRecord(value))
130
+ return false;
131
+ const type = typeof value.type === "string" ? value.type : "";
132
+ if (type === "function_call") {
133
+ return nonWhitespace(value.id) || nonWhitespace(value.call_id) || nonWhitespace(value.name) || nonWhitespace(value.arguments);
134
+ }
135
+ if (type === "output_text" || type === "refusal" || type === "reasoning") {
136
+ return nonWhitespace(value.text) || nonWhitespace(value.refusal) || substantive(value.content);
137
+ }
138
+ if (type === "message" && Array.isArray(value.content))
139
+ return value.content.some(responsesItemMeaningful);
140
+ return substantiveFields(value, new Set(["type", "id", "status", "role", "index"]));
141
+ }
142
+ function openAiResponsesVerdict(value) {
143
+ const type = typeof value.type === "string" ? value.type : "";
144
+ if (type === "error" || type === "response.failed" || isRecord(value.error)) {
145
+ return { kind: "dead", reason: boundedError(value), provenance: "upstream" };
146
+ }
147
+ if (type === "response.incomplete" || type === "response.cancelled") {
148
+ return { kind: "dead", reason: `${type} before meaningful content`, provenance: "upstream" };
149
+ }
150
+ if (type === "response.output_text.delta" ||
151
+ type === "response.refusal.delta" ||
152
+ type === "response.reasoning.delta" ||
153
+ type === "response.reasoning_text.delta")
154
+ return nonWhitespace(value.delta) || nonWhitespace(value.text) ? READY : HOLD;
155
+ if (type === "response.function_call_arguments.delta")
156
+ return nonWhitespace(value.delta) ? READY : HOLD;
157
+ if (type === "response.output_item.added" || type === "response.output_item.done") {
158
+ return responsesItemMeaningful(value.item) ? READY : HOLD;
159
+ }
160
+ if (type === "response.content_part.added" || type === "response.content_part.done") {
161
+ return responsesItemMeaningful(value.part) ? READY : HOLD;
162
+ }
163
+ if (type === "response.completed") {
164
+ const response = isRecord(value.response) ? value.response : {};
165
+ if (response.status === "failed" || response.status === "incomplete" || response.status === "cancelled") {
166
+ return { kind: "dead", reason: "response completed unsuccessfully before meaningful content", provenance: "upstream" };
167
+ }
168
+ if (Array.isArray(response.output) && response.output.some(responsesItemMeaningful))
169
+ return READY;
170
+ return { kind: "dead", reason: "stream completed without meaningful content", provenance: "upstream" };
171
+ }
172
+ if (type.endsWith(".delta")) {
173
+ return substantiveFields(value, new Set(["type", "sequence_number", "output_index", "content_index", "item_id"]))
174
+ ? READY
175
+ : HOLD;
176
+ }
177
+ if (type.endsWith(".done")) {
178
+ return nonWhitespace(value.text) || nonWhitespace(value.refusal) || responsesItemMeaningful(value.item) ? READY : HOLD;
179
+ }
180
+ return HOLD;
181
+ }
182
+ function classifyEvent(event, protocol, malformedProvenance) {
183
+ const lines = event.split(/\r?\n/);
184
+ const eventName = lines.find((line) => line.startsWith("event:"))?.slice(6).trim() ?? "";
185
+ const data = lines
186
+ .filter((line) => line.startsWith("data:"))
187
+ .map((line) => line.slice(5).replace(/^ /, ""))
188
+ .join("\n")
189
+ .trim();
190
+ if (!data)
191
+ return eventName === "error"
192
+ ? { kind: "dead", reason: "in-band error before meaningful content", provenance: "upstream" }
193
+ : HOLD;
194
+ if (data === "[DONE]") {
195
+ return { kind: "dead", reason: "stream completed without meaningful content", provenance: "upstream" };
196
+ }
197
+ let parsed;
198
+ try {
199
+ parsed = JSON.parse(data);
200
+ }
201
+ catch {
202
+ return { kind: "dead", reason: "stream event before meaningful content is not valid JSON", provenance: malformedProvenance };
203
+ }
204
+ if (!isRecord(parsed)) {
205
+ return { kind: "dead", reason: "stream event before meaningful content is not an object", provenance: malformedProvenance };
206
+ }
207
+ if (eventName === "error" && parsed.type !== "error" && !isRecord(parsed.error)) {
208
+ return { kind: "dead", reason: boundedError(parsed), provenance: "upstream" };
209
+ }
210
+ switch (protocol) {
211
+ case "anthropic-messages": return anthropicVerdict(parsed);
212
+ case "openai-chat": return openAiChatVerdict(parsed);
213
+ case "openai-responses": return openAiResponsesVerdict(parsed);
214
+ }
215
+ }
216
+ function firstBoundary(value) {
217
+ const match = /\r?\n\r?\n/.exec(value);
218
+ return match ? { index: match.index, length: match[0].length } : null;
219
+ }
220
+ /**
221
+ * Read through metadata-only SSE events until the final wire contains meaningful assistant output.
222
+ * The returned stream replays the exact raw chunks and continues on the same locked reader.
223
+ */
224
+ export async function probeStreamForCommit(body, protocol, options = {}) {
225
+ const reader = body.getReader();
226
+ const chunks = [];
227
+ const decoder = new TextDecoder();
228
+ const malformedProvenance = options.malformedProvenance ?? "upstream";
229
+ const readFailureProvenance = options.readFailureProvenance ?? "upstream";
230
+ let buffered = "";
231
+ let inspectedBytes = 0;
232
+ const isCancelled = () => options.isCancelled?.() ?? false;
233
+ const cancelled = async () => {
234
+ await reader.cancel("client disconnected before stream commit").catch(() => { });
235
+ return { kind: "cancelled" };
236
+ };
237
+ const dead = async (reason, provenance = "upstream") => {
238
+ if (isCancelled())
239
+ return cancelled();
240
+ await reader.cancel(reason).catch(() => { });
241
+ return { kind: "dead", reason, provenance };
242
+ };
243
+ const ready = () => {
244
+ let prefixIndex = 0;
245
+ return {
246
+ kind: "ready",
247
+ body: new ReadableStream({
248
+ async pull(controller) {
249
+ if (prefixIndex < chunks.length) {
250
+ controller.enqueue(chunks[prefixIndex++]);
251
+ return;
252
+ }
253
+ try {
254
+ const next = await reader.read();
255
+ if (next.done)
256
+ controller.close();
257
+ else
258
+ controller.enqueue(next.value);
259
+ }
260
+ catch (error) {
261
+ controller.error(error);
262
+ }
263
+ },
264
+ async cancel(reason) {
265
+ await reader.cancel(reason).catch(() => { });
266
+ },
267
+ }),
268
+ };
269
+ };
270
+ const inspectCompleteEvents = (final = false) => {
271
+ while (true) {
272
+ const boundary = firstBoundary(buffered);
273
+ if (!boundary)
274
+ break;
275
+ const event = buffered.slice(0, boundary.index);
276
+ buffered = buffered.slice(boundary.index + boundary.length);
277
+ const verdict = classifyEvent(event, protocol, malformedProvenance);
278
+ if (verdict.kind !== "hold")
279
+ return verdict;
280
+ }
281
+ if (final && buffered.trim().length > 0) {
282
+ const event = buffered;
283
+ buffered = "";
284
+ return classifyEvent(event, protocol, malformedProvenance);
285
+ }
286
+ return HOLD;
287
+ };
288
+ while (inspectedBytes < STREAM_PREFLIGHT_LIMIT) {
289
+ let next;
290
+ try {
291
+ next = await reader.read();
292
+ }
293
+ catch {
294
+ if (isCancelled())
295
+ return cancelled();
296
+ return dead("stream failed before meaningful content", readFailureProvenance);
297
+ }
298
+ if (isCancelled())
299
+ return cancelled();
300
+ if (next.done) {
301
+ buffered += decoder.decode();
302
+ const verdict = inspectCompleteEvents(true);
303
+ if (verdict.kind === "ready")
304
+ return ready();
305
+ if (verdict.kind === "dead")
306
+ return dead(verdict.reason, verdict.provenance);
307
+ return dead("stream ended before meaningful content");
308
+ }
309
+ chunks.push(next.value);
310
+ const remaining = STREAM_PREFLIGHT_LIMIT - inspectedBytes;
311
+ const inspectBytes = next.value.byteLength > remaining ? next.value.subarray(0, remaining) : next.value;
312
+ inspectedBytes += inspectBytes.byteLength;
313
+ buffered += decoder.decode(inspectBytes, { stream: true });
314
+ const verdict = inspectCompleteEvents();
315
+ if (verdict.kind === "ready")
316
+ return isCancelled() ? cancelled() : ready();
317
+ if (verdict.kind === "dead")
318
+ return dead(verdict.reason, verdict.provenance);
319
+ if (next.value.byteLength > inspectBytes.byteLength) {
320
+ return dead("no meaningful content within commit probe limit");
321
+ }
322
+ }
323
+ return dead("no meaningful content within commit probe limit");
324
+ }
325
+ //# sourceMappingURL=stream-commit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-commit.js","sourceRoot":"","sources":["../src/stream-commit.ts"],"names":[],"mappings":"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"}
@@ -1,86 +1,28 @@
1
+ import { type CredentialId } from "./credential-id.js";
2
+ /** A learned condition or measurement about a routing target. */
3
+ export type FactKind = "not-servable" | "subscription-required" | "allowance-exhausted" | "credential-invalid" | "rate-limited" | "context-limit";
1
4
  /**
2
- * Learned facts about routing targets, each carrying the SCOPE it actually applies to.
3
- *
4
- * The problem this exists to stop recurring: a fact's natural scope and its storage keying kept
5
- * drifting apart, independently, in every store that learned something. Measured 2026-08-08:
6
- *
7
- * - a HuggingFace credit balance is stated per ACCOUNT, and was being rediscovered once per
8
- * model — six pool members, six round-trips, six expiries, to learn one number;
9
- * - a revoked key is a fact about the CREDENTIAL, and `credentialFaultUntil` is keyed per
10
- * deployment, so every model on that provider must independently discover the same 401;
11
- * - an account-level 429 is the same shape again.
12
- *
13
- * Each was a separate patch waiting to be written. So scope is now part of the fact rather than a
14
- * property of whichever module happened to record it, and every lookup resolves MOST SPECIFIC
15
- * FIRST with its provenance attached — the shape `metadata.ts` already uses for limits and prices
16
- * (serving provider's own figure → another provider's figure for the same id → null).
17
- *
18
- * ⚠ **Scope comes from EVIDENCE, never from inference.** "Three models on this provider returned
19
- * 401" is not proof the key is bad — it is equally three gated models under a working credential,
20
- * which is the false accusation `key-checker.ts` exists to avoid. A provider-scoped fact requires
21
- * something that *states* an account-level condition; when a situation is ambiguous it goes to the
22
- * offline review tier in `refusal-interpretation.ts` and binds only once accepted. Nothing here
23
- * promotes a fact by counting failures.
24
- */
25
- /**
26
- * What was learned. Deliberately one enum across fact kinds, because the consequence of a fact is
27
- * a property of the fact and not of the store it came from.
28
- *
29
- * ⚠ `allowance-exhausted` is TEMPORAL, not a cost verdict — a free lane that has spent this
30
- * period's credits is the normal state of a working free lane. It demotes and expires; it must
31
- * never evict anything from a free pool, or the eviction outlives the exhaustion. `isCostBlocked`
32
- * is the enforcement of that and deliberately cannot see it.
33
- */
34
- export type FactKind =
35
- /** The deployment does not exist (404/400 "does not exist", "not found for account"). */
36
- "not-servable"
37
- /** It exists, but is not covered by the plan we hold (403 naming a subscription). */
38
- | "subscription-required"
39
- /** Free, and spent until the allowance refreshes (402 naming a credit balance). */
40
- | "allowance-exhausted"
41
- /** The credential itself is rejected — stated, e.g. "invalid api key". NOT inferred from 401s. */
42
- | "credential-invalid"
43
- /**
44
- * Rate limited at a scope the response STATED — "your account has exceeded its rate limit".
45
- *
46
- * ⚠ This is not where ordinary 429s go. A bare 429 is one deployment's back-pressure and belongs
47
- * to the breaker's per-target cooldown, which already handles it well. This kind exists only for
48
- * the case the breaker cannot express: a limit the provider says belongs to the ACCOUNT, where
49
- * every sibling is equally throttled and discovering that once per model is the waste.
50
- */
51
- | "rate-limited"
52
- /**
53
- * The context ceiling a deployment STATED when it refused an over-length request, in tokens.
54
- *
55
- * ⚠ A MEASUREMENT, not a condition — and the distinction is load-bearing. Every other kind here
56
- * describes something that is currently true and stops being true: a success disproves it. A
57
- * context window is a property of the deployment, so a successful request says nothing about it
58
- * and must not clear it. See `CONDITIONS` below.
59
- */
60
- | "context-limit";
61
- /**
62
- * Who a fact applies to, specific → general. Resolution walks this order and stops at the first
63
- * live hit, so a deployment's own verdict always beats its provider's.
64
- *
65
- * deployment — this (provider, model) alone.
66
- * group — an explicit set of models on one provider. ⚠ The membership travels WITH the
67
- * fact rather than being inferred from id prefixes: prefix matching is the
68
- * heuristic `authEnv.ts` refuses, and one bad match evicts a working family. A
69
- * group verdict is reviewed with its member list visible before it binds.
70
- * provider — every deployment behind that credential. Balances, revoked keys, account rate
71
- * limits. This is the scope that turns N discoveries into one.
72
- * model — the same model id wherever it is served. Reference-grade only, and never used for
73
- * cost or availability: the same id on two providers is two deployments with
74
- * different ceilings, prices and entitlements (see `metadata.ts`).
5
+ * The evidence scope, in lookup order. `provider` deliberately means every credential for the
6
+ * provider; credential-wide evidence must use `credential` instead.
75
7
  */
76
8
  export type FactScope = {
77
- kind: "deployment";
9
+ kind: "attempt";
78
10
  provider: string;
11
+ credentialId: CredentialId;
79
12
  model: string;
80
13
  } | {
81
14
  kind: "group";
82
15
  provider: string;
16
+ credentialId?: CredentialId;
83
17
  members: string[];
18
+ } | {
19
+ kind: "deployment";
20
+ provider: string;
21
+ model: string;
22
+ } | {
23
+ kind: "credential";
24
+ provider: string;
25
+ credentialId: CredentialId;
84
26
  } | {
85
27
  kind: "provider";
86
28
  provider: string;
@@ -88,45 +30,18 @@ export type FactScope = {
88
30
  kind: "model";
89
31
  model: string;
90
32
  };
91
- /** Scope names in resolution order. Exported so callers cannot invent a different precedence. */
92
33
  export declare const SCOPE_PRECEDENCE: Array<FactScope["kind"]>;
93
- /**
94
- * How long each kind is believed without re-checking. All expire, because all are reversible: a
95
- * de-listed model returns, a plan is upgraded, an allowance refreshes, a key is rotated.
96
- *
97
- * `allowance-exhausted` is deliberately the SHORTEST despite monthly credit cycles — cooling a
98
- * whole provider for a month on one 402 would be catastrophic if the balance were topped up an
99
- * hour later, and one re-probe an hour is cheap and self-correcting. It also matches the breaker's
100
- * `QUOTA_EXHAUSTED_COOLDOWN_MS`, so the two cannot disagree about when to try again.
101
- * `credential-invalid` is short for the same reason from the other direction: a rotated key must
102
- * recover quickly, and any success clears it outright.
103
- */
104
34
  export declare const FACT_TTL_MS: Record<FactKind, number>;
105
- /**
106
- * Every fact kind, for callers that must enumerate them (the review CLI's `--class` validation).
107
- *
108
- * Derived from `FACT_TTL_MS`, which is a `Record<FactKind, …>` and therefore exhaustive by
109
- * compiler enforcement — so this list cannot fall behind the type. A hand-maintained copy already
110
- * did: `rate-limited` existed in the store while the CLI rejected it, which made the review
111
- * command unusable for the one kind it had just gained.
112
- */
113
35
  export declare const FACT_KINDS: FactKind[];
114
- /**
115
- * Record a fact at the scope its evidence supports.
116
- *
117
- * A fresh observation replaces an older one at the SAME scope: the target is the authority on
118
- * itself, and one that changed its answer is telling us so. Facts at different scopes coexist —
119
- * a deployment can be `subscription-required` while its provider is `allowance-exhausted`, and
120
- * resolution decides which applies.
121
- */
36
+ /** Canonical persisted key. Kept exported so persistence tests cannot duplicate it. */
37
+ export declare function keyOf(scope: FactScope): string;
122
38
  export declare function recordFact(kind: FactKind, scope: FactScope, opts?: {
123
39
  path?: string;
124
40
  now?: number;
125
41
  retryAfterMs?: number | null;
126
42
  value?: number;
127
43
  }): void;
128
- /** Every live fact covering a deployment, most specific first. */
129
- export declare function factsFor(provider: string, model: string | null | undefined, opts?: {
44
+ export declare function factsFor(provider: string, credentialId: CredentialId | null, model: string | null | undefined, opts?: {
130
45
  path?: string;
131
46
  now?: number;
132
47
  }): Array<{
@@ -135,43 +50,18 @@ export declare function factsFor(provider: string, model: string | null | undefi
135
50
  until: number;
136
51
  value?: number;
137
52
  }>;
138
- /**
139
- * Has this deployment been PROVEN unfit for a free pool?
140
- *
141
- * ⚠ True only for facts in `COST_BLOCKING`. `allowance-exhausted` is deliberately excluded and
142
- * must stay excluded — see the `FactKind` note. `credential-invalid` is excluded too: a bad key is
143
- * a configuration problem, not a statement that the deployment costs money, and removing members
144
- * from a pool over it would empty the pool on a mistake that a rotation fixes in seconds.
145
- */
146
- export declare function isCostBlocked(provider: string, model: string | null | undefined, opts?: {
53
+ export declare function isCostBlocked(provider: string, credentialId: CredentialId | null, model: string | null | undefined, opts?: {
147
54
  path?: string;
148
55
  now?: number;
149
56
  }): boolean;
150
- /**
151
- * When a temporarily-unusable target is worth trying again, or null when nothing is cooling it.
152
- * Only `COOLING` kinds produce this — the others are exclusions, not cooldowns, and a caller that
153
- * wants those wants `isCostBlocked`.
154
- */
155
- export declare function cooldownUntil(provider: string, model: string | null | undefined, opts?: {
57
+ export declare function cooldownUntil(provider: string, credentialId: CredentialId | null, model: string | null | undefined, opts?: {
156
58
  path?: string;
157
59
  now?: number;
158
60
  }): number | null;
159
- /**
160
- * A success clears every fact covering this deployment — including its PROVIDER-scoped ones.
161
- *
162
- * A served request is first-party proof that the deployment exists, the credential authenticates,
163
- * and the allowance is not spent. That is strictly better evidence than any stored refusal, and it
164
- * covers the cases that matter most: credits topped up, a key rotated, a month rolled over, all
165
- * well before the TTL would have expired. Same contract as the breaker clearing a credential fault
166
- * on success.
167
- *
168
- * ⚠ A group fact is cleared only if this model is IN the group — one member serving says nothing
169
- * about the others, and dropping the whole verdict would re-admit models that are genuinely gated.
170
- */
171
- export declare function clearFacts(provider: string, model: string | null | undefined, opts?: {
61
+ /** A success retracts conditions covering this credential/model cell, never measurements. */
62
+ export declare function clearFacts(provider: string, credentialId: CredentialId | null, model: string | null | undefined, opts?: {
172
63
  path?: string;
173
64
  }): FactKind[];
174
- /** Every live fact, for `llm-relay eligibility`. Expired entries are omitted, not reported. */
175
65
  export declare function allFacts(opts?: {
176
66
  path?: string;
177
67
  now?: number;
@@ -181,11 +71,8 @@ export declare function allFacts(opts?: {
181
71
  at: number;
182
72
  until: number;
183
73
  }>;
184
- /** A human-readable label for a scope — used by the CLI and by nothing that makes decisions. */
185
74
  export declare function describeScope(scope: FactScope): string;
186
- /** Flush pending writes. Called on shutdown, like the other write-behind stores. */
187
75
  export declare function flushFacts(opts?: {
188
76
  path?: string;
189
77
  }): void;
190
- /** Test seam: drop the in-memory store so a suite can point at a fresh path. */
191
78
  export declare function resetFacts(): void;