deepdebug-local-agent 1.9.0 → 1.10.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.
@@ -0,0 +1,380 @@
1
+ // F3-10 (1): the Local Agent's LLM client against a FAKE OpenAI-compatible server (real HTTP, real SSE). What it proves: the DLP masks before anything
2
+ // leaves, the key travels only in the Authorization header and is never in a body, an audit record, a stat or an error, limits and timeouts and retries
3
+ // behave, errors are codes only, and with the flag off there is no client at all. Not proven here: the real LiteLLM (only a fake server).
4
+ import { test, after } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import util from "node:util";
7
+ import { createLlmKeyHolder } from "../../src/la-protocol-v2/llm-key/store.js";
8
+ import { createLlmClient, LlmClientError, LLM_ERROR_CODES } from "../../src/la-protocol-v2/llm/client.js";
9
+ import { resolveLlmClientConfig } from "../../src/la-protocol-v2/llm/config.js";
10
+ import { startFakeLlm, closeAllFakes, okJson, okSse, status, mkKey, keySet } from "./helpers/fake-llm.js";
11
+
12
+ after(closeAllFakes);
13
+
14
+ const noop = () => {};
15
+ const GH = "ghp_" + "A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8";
16
+ const PEM = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n-----END RSA PRIVATE KEY-----";
17
+ const ENFORCE = { LOCAL_AGENT_DLP_MODE: "enforce" };
18
+ const TENANT = "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f";
19
+ const AGENT = "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d";
20
+ const JOB = "9d1f6a2c-1111-4222-8333-444455556666";
21
+ const msg = (content, role = "user") => ({ role, content });
22
+
23
+ async function rig({ behavior, env = ENFORCE, cfg = {}, key = mkKey(), holderOpts = {}, setKey = true } = {}) {
24
+ const fake = await startFakeLlm(behavior);
25
+ const holder = createLlmKeyHolder({ log: { warn: noop }, ...holderOpts });
26
+ if (setKey) assert.equal(holder.set(keySet(key)).ok, true);
27
+ const config = { ...resolveLlmClientConfig({ LOCAL_AGENT_LLM_CLIENT: "true" }, noop), backoffMs: 0, ...cfg };
28
+ const client = createLlmClient({ holder: { request: (p, i, o) => holder.request(p, i, o), describe: () => holder.describe(), get held() { return holder.held; } },
29
+ config, tenantId: TENANT, agentId: AGENT, env, fetchImpl: fake.fetchImpl, sleep: async () => {} });
30
+ return { fake, holder, client, key, config, close: () => fake.close() };
31
+ }
32
+
33
+ async function codeOf(p) {
34
+ try { await p; } catch (e) { assert.ok(e instanceof LlmClientError, "only LlmClientError escapes"); assert.ok(LLM_ERROR_CODES.includes(e.code)); assert.equal(e.message, e.code, "the message is the code, nothing else"); return e.code; }
35
+ return null;
36
+ }
37
+
38
+ // ------------------------------------------------------------------------------------------------------ config
39
+
40
+ test("config: off by default (unset, empty, unknown value -> off, unknown warns); on only with true/1/yes/on", () => {
41
+ for (const v of [undefined, "", "false", "0", "off", "nope"]) {
42
+ const warns = [];
43
+ assert.deepEqual(resolveLlmClientConfig({ LOCAL_AGENT_LLM_CLIENT: v }, (m) => warns.push(m)), { enabled: false });
44
+ assert.equal(warns.length, v === "nope" ? 1 : 0);
45
+ }
46
+ for (const v of ["true", "1", "YES", " on "]) assert.equal(resolveLlmClientConfig({ LOCAL_AGENT_LLM_CLIENT: v }, noop).enabled, true);
47
+ });
48
+
49
+ test("config: every number is clamped, a bad one falls back to the default (never unlimited); the path must be a plain absolute path", () => {
50
+ const c = resolveLlmClientConfig({ LOCAL_AGENT_LLM_CLIENT: "true", LOCAL_AGENT_LLM_TIMEOUT_MS: "1", LOCAL_AGENT_LLM_MAX_RETRIES: "99", LOCAL_AGENT_LLM_MAX_PROMPT_BYTES: "abc",
51
+ LOCAL_AGENT_LLM_MAX_OUTPUT_TOKENS: "0", LOCAL_AGENT_LLM_MAX_CONCURRENT: "1000", LOCAL_AGENT_LLM_MAX_RESPONSE_BYTES: "-1" }, noop);
52
+ assert.equal(c.timeoutMs, 60_000); assert.equal(c.maxRetries, 2); assert.equal(c.maxPromptBytes, 256 * 1024); assert.equal(c.maxOutputTokens, 4096);
53
+ assert.equal(c.maxConcurrent, 4); assert.equal(c.maxResponseBytes, 1024 * 1024); assert.equal(c.tags, true); assert.equal(c.path, "/v1/chat/completions");
54
+ const warns = [];
55
+ for (const bad of ["v1/chat", "/../admin", "/a b", "//x", "https://evil/x", "/a?b=1"]) {
56
+ assert.equal(resolveLlmClientConfig({ LOCAL_AGENT_LLM_CLIENT: "true", LOCAL_AGENT_LLM_PATH: bad }, (m) => warns.push(m)).path, "/v1/chat/completions", bad);
57
+ }
58
+ assert.equal(warns.length, 6);
59
+ assert.equal(resolveLlmClientConfig({ LOCAL_AGENT_LLM_CLIENT: "true", LOCAL_AGENT_LLM_PATH: "/chat/completions", LOCAL_AGENT_LLM_TAGS: "false" }, noop).tags, false);
60
+ });
61
+
62
+ // ------------------------------------------------------------------------------------------------------ the call
63
+
64
+ test("chat: OpenAI-compatible request (model, masked messages, max_tokens, tags), key ONLY in the Authorization header, usage and cost parsed", async () => {
65
+ const r = await rig({ behavior: okJson("a summary", { promptTokens: 30, completionTokens: 9, cost: "0.0123" }) });
66
+ const out = await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("be brief", "system"), msg("what is this?")], maxTokens: 100, temperature: 0, job: { jobId: JOB, agent: "smoke" } });
67
+ assert.equal(out.text, "a summary");
68
+ assert.deepEqual(out.usage, { promptTokens: 30, completionTokens: 9, estimated: false });
69
+ assert.equal(out.costUsd, 0.0123);
70
+ assert.equal(out.finishReason, "stop");
71
+ assert.equal(out.attempts, 1);
72
+ const q = r.fake.requests[0];
73
+ assert.equal(q.method, "POST");
74
+ assert.equal(q.url, "/litellm/v1/chat/completions", "endpoint of the key + the configured path");
75
+ assert.equal(q.headers.authorization, `Bearer ${r.key}`);
76
+ assert.equal(q.headers["content-type"], "application/json");
77
+ assert.deepEqual(q.body, { model: "vh-claude-sonnet", messages: [msg("be brief", "system"), msg("what is this?")], max_tokens: 100, stream: false, temperature: 0,
78
+ metadata: { tags: [`vh-tenant:${TENANT}`, `vh-la:${AGENT}`, `vh-job:${JOB}`, "vh-agent:smoke"] } });
79
+ assert.ok(!q.raw.includes(r.key), "the key is never in a body");
80
+ await r.close();
81
+ });
82
+
83
+ test("chat: tags off (LOCAL_AGENT_LLM_TAGS=false) = no metadata; the output is capped by LOCAL_AGENT_LLM_MAX_OUTPUT_TOKENS", async () => {
84
+ const r = await rig({ cfg: { tags: false, maxOutputTokens: 50 } });
85
+ await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], maxTokens: 99_999 });
86
+ assert.equal(r.fake.requests[0].body.metadata, undefined);
87
+ assert.equal(r.fake.requests[0].body.max_tokens, 50);
88
+ await r.close();
89
+ });
90
+
91
+ test("chat: without usage in the answer the usage is ESTIMATED from the bytes (a budget must still count)", async () => {
92
+ const r = await rig({ behavior: (q, s) => { s.writeHead(200, { "content-type": "application/json" }); s.end(JSON.stringify({ choices: [{ message: { content: "x".repeat(300) } }] })); } });
93
+ const out = await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("y".repeat(90))] });
94
+ assert.deepEqual(out.usage, { promptTokens: 30, completionTokens: 100, estimated: true });
95
+ assert.equal(out.costUsd, null);
96
+ await r.close();
97
+ });
98
+
99
+ test("chat: streaming - deltas in order, usage from the last chunk (stream_options.include_usage asked), text assembled", async () => {
100
+ const r = await rig({ behavior: okSse(["Hel", "lo ", "wor", "ld"], { promptTokens: 21, completionTokens: 4 }) });
101
+ const seen = [];
102
+ const out = await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], stream: true, onDelta: (d) => seen.push(d) });
103
+ assert.deepEqual(seen, ["Hel", "lo ", "wor", "ld"]);
104
+ assert.equal(out.text, "Hello world");
105
+ assert.deepEqual(out.usage, { promptTokens: 21, completionTokens: 4, estimated: false });
106
+ assert.equal(r.fake.requests[0].body.stream, true);
107
+ assert.deepEqual(r.fake.requests[0].body.stream_options, { include_usage: true });
108
+ assert.equal(r.fake.requests[0].headers.accept, "text/event-stream");
109
+ await r.close();
110
+ });
111
+
112
+ test("chat: streaming - a consumer that throws in onDelta does not break the call; a half line / CRLF framing still parses", async () => {
113
+ const r = await rig({ behavior: (q, s) => {
114
+ s.writeHead(200, { "content-type": "text/event-stream" });
115
+ s.write('data: {"choices":[{"delta":{"content":"ab"}}]}\r\n\r\nda');
116
+ setTimeout(() => { s.write('ta: {"choices":[{"delta":{"content":"cd"}}]}\r\n\r\n'); s.end("data: [DONE]\n\n"); }, 20);
117
+ } });
118
+ const out = await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], stream: true, onDelta: () => { throw new Error("consumer bug"); } });
119
+ assert.equal(out.text, "abcd");
120
+ assert.equal(out.usage.estimated, true);
121
+ await r.close();
122
+ });
123
+
124
+ // ------------------------------------------------------------------------------------------------------ DLP
125
+
126
+ test("DLP: a secret in ANY message is masked BEFORE the request leaves; the server never sees it; the audit has counts only", async () => {
127
+ const r = await rig();
128
+ await r.client.chat({ model: "vh-claude-sonnet", messages: [msg(`use this token ${GH} to push`, "system"), msg(`and again ${GH}`)], job: { jobId: JOB } });
129
+ const sent = r.fake.requests[0].raw;
130
+ assert.ok(!sent.includes(GH), "the secret never reaches the wire");
131
+ assert.match(sent, /\[REDACTED:[a-z0-9_]+\]/);
132
+ const rec = r.client.audit.tail(5).find((a) => a.event === "llm_call");
133
+ assert.equal(rec.ok, true);
134
+ assert.ok(Object.values(rec.redactions).reduce((a, b) => a + b, 0) >= 2, "counted per rule");
135
+ assert.ok(!JSON.stringify(r.client.audit.tail(50)).includes(GH));
136
+ await r.close();
137
+ });
138
+
139
+ test("DLP: a hit of a blocked class refuses the call (dlp_blocked) and NOTHING is sent", async () => {
140
+ const r = await rig({ env: { ...ENFORCE, LOCAL_AGENT_DLP_BLOCK_CLASSES: "private-key" } });
141
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg(`here: ${PEM}`)] })), "dlp_blocked");
142
+ assert.equal(r.fake.requests.length, 0);
143
+ await r.close();
144
+ });
145
+
146
+ test("DLP: off / warn / unset = dlp_not_enforced (per call, no opt-out) and nothing is sent; an oversize input is dlp_denied", async () => {
147
+ for (const mode of [undefined, "off", "warn"]) {
148
+ const r = await rig({ env: mode ? { LOCAL_AGENT_DLP_MODE: mode } : {} });
149
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg(`x ${GH}`)] })), "dlp_not_enforced", String(mode));
150
+ assert.equal(r.fake.requests.length, 0);
151
+ await r.close();
152
+ }
153
+ const r = await rig({ env: { ...ENFORCE, LOCAL_AGENT_DLP_MAX_BYTES: "100" } });
154
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("z".repeat(500))] })), "dlp_denied");
155
+ assert.equal(r.fake.requests.length, 0);
156
+ await r.close();
157
+ });
158
+
159
+ test("DLP: the mode is read at every call (switching it off mid-life closes the door)", async () => {
160
+ const env = { ...ENFORCE };
161
+ const r = await rig({ env });
162
+ await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] });
163
+ env.LOCAL_AGENT_DLP_MODE = "off";
164
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "dlp_not_enforced");
165
+ assert.equal(r.fake.requests.length, 1);
166
+ await r.close();
167
+ });
168
+
169
+ // ------------------------------------------------------------------------------------------------------ key and model
170
+
171
+ test("no key / expired key / alias not in the key's models: refused locally with a code, nothing is sent", async () => {
172
+ let t = 1_700_000_000_000;
173
+ const r = await rig({ setKey: false, holderOpts: { now: () => t } });
174
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "no_key");
175
+ r.holder.set(keySet(r.key, { expiresAt: new Date(t + 60_000).toISOString() }));
176
+ assert.equal(await codeOf(r.client.chat({ model: "vh-drael", messages: [msg("hi")] })), "model_not_allowed");
177
+ t += 120_000;
178
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "no_key", "an expired key is dropped by the holder: no key");
179
+ assert.equal(r.fake.requests.length, 0);
180
+ await r.close();
181
+ });
182
+
183
+ test("bad request shapes: model name, roles, non-string content, too many messages, bad temperature -> bad_request, nothing sent", async () => {
184
+ const r = await rig();
185
+ const bad = [
186
+ { model: "x y", messages: [msg("hi")] }, { model: "vh-claude-sonnet", messages: [] }, { model: "vh-claude-sonnet", messages: [{ role: "tool", content: "x" }] },
187
+ { model: "vh-claude-sonnet", messages: [{ role: "user", content: 42 }] }, { model: "vh-claude-sonnet", messages: Array.from({ length: 65 }, () => msg("x")) },
188
+ { model: "vh-claude-sonnet", messages: [msg("x")], temperature: 9 }, {},
189
+ ];
190
+ for (const o of bad) assert.equal(await codeOf(r.client.chat(o)), "bad_request");
191
+ assert.equal(r.fake.requests.length, 0);
192
+ await r.close();
193
+ });
194
+
195
+ // ------------------------------------------------------------------------------------------------------ errors, retries, timeouts
196
+
197
+ test("HTTP errors map to codes; 401/403 and other 4xx are NOT retried; the body of an error response is never read or echoed", async () => {
198
+ const CANARY = "PROMPT-ECHO-CANARY-7731";
199
+ for (const [st, code, calls] of [[401, "key_rejected", 1], [403, "key_rejected", 1], [400, "bad_request", 1], [404, "bad_request", 1], [413, "prompt_too_large", 1]]) {
200
+ const r = await rig({ behavior: status(st, JSON.stringify({ error: { message: `you sent ${CANARY}` } })) });
201
+ let err;
202
+ try { await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] }); } catch (e) { err = e; }
203
+ assert.equal(err.code, code, String(st));
204
+ assert.equal(r.fake.requests.length, calls, `no retry on ${st}`);
205
+ assert.ok(!util.inspect(err, { depth: 5 }).includes(CANARY) && !err.stack.includes(CANARY));
206
+ assert.ok(!JSON.stringify(r.client.audit.tail(10)).includes(CANARY));
207
+ await r.close();
208
+ }
209
+ });
210
+
211
+ test("retries: 5xx / 429 / 408 / dropped connection are retried up to maxRetries (then the code); Retry-After is honoured and capped; success after a retry", async () => {
212
+ const r = await rig({ behavior: [status(503), status(500), okJson("finally")], cfg: { maxRetries: 2 } });
213
+ const waits = [];
214
+ const client = createLlmClient({ holder: { request: (p, i, o) => r.holder.request(p, i, o), describe: () => r.holder.describe(), get held() { return r.holder.held; } },
215
+ config: { ...r.config, backoffMs: 100 }, tenantId: TENANT, agentId: AGENT, env: ENFORCE, fetchImpl: r.fake.fetchImpl, sleep: async (ms) => { waits.push(ms); } });
216
+ const out = await client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] });
217
+ assert.equal(out.text, "finally"); assert.equal(out.attempts, 3); assert.deepEqual(waits, [100, 200], "exponential backoff");
218
+ assert.equal(client.stats.retries, 2);
219
+ await r.close();
220
+
221
+ const r2 = await rig({ behavior: status(500), cfg: { maxRetries: 2 } });
222
+ assert.equal(await codeOf(r2.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "upstream_error");
223
+ assert.equal(r2.fake.requests.length, 3, "1 + maxRetries, never more");
224
+ await r2.close();
225
+
226
+ const r3 = await rig({ behavior: [(q, s) => { s.writeHead(429, { "retry-after": "999" }); s.end(); }, okJson("ok")] });
227
+ const w3 = [];
228
+ const c3 = createLlmClient({ holder: { request: (p, i, o) => r3.holder.request(p, i, o), describe: () => r3.holder.describe(), get held() { return r3.holder.held; } },
229
+ config: r3.config, tenantId: TENANT, agentId: AGENT, env: ENFORCE, fetchImpl: r3.fake.fetchImpl, sleep: async (ms) => { w3.push(ms); } });
230
+ assert.equal((await c3.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })).text, "ok");
231
+ assert.deepEqual(w3, [10_000], "Retry-After capped");
232
+ await r3.close();
233
+
234
+ const r4 = await rig({ behavior: [(q, s) => s.destroy(), (q, s) => s.destroy(), (q, s) => s.destroy()], cfg: { maxRetries: 2 } });
235
+ assert.equal(await codeOf(r4.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "network_error");
236
+ assert.equal(r4.fake.requests.length, 3);
237
+ await r4.close();
238
+
239
+ const r5 = await rig({ behavior: [status(408), status(429), okJson("fine")], cfg: { maxRetries: 2 } });
240
+ assert.equal((await r5.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })).text, "fine");
241
+ await r5.close();
242
+ });
243
+
244
+ test("timeouts: a server that never answers -> timeout (retried, then the code); the total deadline stops the retries", async () => {
245
+ const r = await rig({ behavior: () => new Promise(() => {}), cfg: { timeoutMs: 80, maxRetries: 1 } });
246
+ const t0 = Date.now();
247
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "timeout");
248
+ assert.equal(r.fake.requests.length, 2);
249
+ assert.ok(Date.now() - t0 < 2000);
250
+ await r.close();
251
+ const r2 = await rig({ behavior: () => new Promise(() => {}), cfg: { timeoutMs: 80, maxRetries: 5, totalMs: 100, backoffMs: 60 } });
252
+ assert.equal(await codeOf(r2.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "timeout");
253
+ assert.ok(r2.fake.requests.length <= 2, "the deadline of the whole call cut the retries");
254
+ await r2.close();
255
+ });
256
+
257
+ test("streaming: idle timeout between chunks -> timeout; no retry once a delta was delivered (it would repeat output)", async () => {
258
+ const stall = (q, s) => { s.writeHead(200, { "content-type": "text/event-stream" }); s.write('data: {"choices":[{"delta":{"content":"par"}}]}\n\n'); /* then silence */ };
259
+ const r = await rig({ behavior: stall, cfg: { streamIdleMs: 80, maxRetries: 3 } });
260
+ const seen = [];
261
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], stream: true, onDelta: (d) => seen.push(d) })), "timeout");
262
+ assert.deepEqual(seen, ["par"]);
263
+ assert.equal(r.fake.requests.length, 1, "no retry after output was delivered");
264
+ await r.close();
265
+ });
266
+
267
+ test("a redirect is refused (a 3xx would carry the key elsewhere): network_error, and the second host is never called", async () => {
268
+ const other = await startFakeLlm(okJson("leak"));
269
+ const r = await rig({ behavior: (q, s) => { s.writeHead(302, { location: `http://127.0.0.1:${other.port}/steal` }); s.end(); }, cfg: { maxRetries: 0 } });
270
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "network_error");
271
+ assert.equal(other.requests.length, 0);
272
+ await r.close(); await other.close();
273
+ });
274
+
275
+ test("cancellation: an aborted signal ends the call with `cancelled` (before and during the request), no retry", async () => {
276
+ const r = await rig({ behavior: () => new Promise(() => {}) });
277
+ const ac = new AbortController();
278
+ const p = r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], signal: ac.signal });
279
+ setTimeout(() => ac.abort(), 40);
280
+ assert.equal(await codeOf(p), "cancelled");
281
+ assert.equal(r.fake.requests.length, 1);
282
+ const pre = new AbortController(); pre.abort();
283
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], signal: pre.signal })), "cancelled");
284
+ assert.equal(r.fake.requests.length, 1);
285
+ assert.equal(r.client.active, 0, "the slot is released");
286
+ await r.close();
287
+ });
288
+
289
+ // ------------------------------------------------------------------------------------------------------ limits
290
+
291
+ test("limits: prompt above maxPromptBytes (after masking) -> prompt_too_large; response above maxResponseBytes -> response_too_large (JSON and SSE); nothing partial returned", async () => {
292
+ const r = await rig({ behavior: okJson("fine"), cfg: { maxPromptBytes: 1024 } });
293
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("a".repeat(600)), msg("b".repeat(600))] })), "prompt_too_large");
294
+ assert.equal(r.fake.requests.length, 0);
295
+ await r.close();
296
+ const big = await rig({ behavior: okJson("x".repeat(20_000)), cfg: { maxResponseBytes: 4096 } });
297
+ assert.equal(await codeOf(big.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), "response_too_large");
298
+ await big.close();
299
+ const bigSse = await rig({ behavior: okSse(Array.from({ length: 40 }, () => "y".repeat(500))), cfg: { maxResponseBytes: 4096 } });
300
+ const seen = [];
301
+ assert.equal(await codeOf(bigSse.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], stream: true, onDelta: (d) => seen.push(d) })), "response_too_large");
302
+ assert.equal(bigSse.fake.requests.length, 1, "not retried");
303
+ await bigSse.close();
304
+ });
305
+
306
+ test("token budget: the prompt estimate + max_tokens must fit tokensLeft (max_tokens is shrunk to what is left; none left -> budget_exceeded, nothing sent)", async () => {
307
+ const r = await rig();
308
+ await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("x".repeat(30))], maxTokens: 1000, tokensLeft: 60 });
309
+ assert.equal(r.fake.requests[0].body.max_tokens, 50, "60 left - 10 estimated prompt tokens");
310
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("x".repeat(30))], tokensLeft: 10 })), "budget_exceeded");
311
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("x")], tokensLeft: 0 })), "budget_exceeded");
312
+ assert.equal(r.fake.requests.length, 1);
313
+ await r.close();
314
+ });
315
+
316
+ test("concurrency: above maxConcurrent in flight -> busy at once (no queue, no unbounded fan-out); the slot is released after each call", async () => {
317
+ let release;
318
+ const gate = new Promise((r) => { release = r; });
319
+ const r = await rig({ behavior: async (q, s, e) => { await gate; okJson("late")(q, s, e); }, cfg: { maxConcurrent: 2 } });
320
+ const a = r.client.chat({ model: "vh-claude-sonnet", messages: [msg("1")] });
321
+ const b = r.client.chat({ model: "vh-claude-sonnet", messages: [msg("2")] });
322
+ await new Promise((x) => setTimeout(x, 30));
323
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("3")] })), "busy");
324
+ release();
325
+ await Promise.all([a, b]);
326
+ assert.equal(r.client.active, 0);
327
+ await r.client.chat({ model: "vh-claude-sonnet", messages: [msg("4")] });
328
+ await r.close();
329
+ });
330
+
331
+ test("bad responses: not JSON, no choices, an error event in the stream -> bad_response / upstream_error (codes only)", async () => {
332
+ for (const [behavior, code] of [
333
+ [(q, s) => { s.writeHead(200); s.end("<html>nope</html>"); }, "bad_response"],
334
+ [(q, s) => { s.writeHead(200); s.end(JSON.stringify({ choices: [] })); }, "bad_response"],
335
+ [(q, s) => { s.writeHead(200); s.end(JSON.stringify({ choices: [{ message: { content: 5 } }] })); }, "bad_response"],
336
+ ]) {
337
+ const r = await rig({ behavior });
338
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")] })), code);
339
+ await r.close();
340
+ }
341
+ const r = await rig({ behavior: (q, s) => { s.writeHead(200, { "content-type": "text/event-stream" }); s.end('data: {"error":{"message":"boom"}}\n\n'); } });
342
+ assert.equal(await codeOf(r.client.chat({ model: "vh-claude-sonnet", messages: [msg("hi")], stream: true })), "upstream_error");
343
+ await r.close();
344
+ });
345
+
346
+ // ------------------------------------------------------------------------------------------------------ nothing leaks
347
+
348
+ test("SWEEP: across success, every error and streaming, the key, its id, the prompt and the answer are in NO audit record, stat, error or inspected object", async () => {
349
+ const key = mkKey("SWEEP");
350
+ const PROMPT = "PROMPT-CANARY-5521";
351
+ const ANSWER = "ANSWER-CANARY-8842";
352
+ const r = await rig({ key, behavior: [okJson(ANSWER), okSse([ANSWER]), status(401), status(500), status(400), (q, s) => s.destroy()], cfg: { maxRetries: 0 } });
353
+ const errors = [];
354
+ for (const o of [{}, { stream: true }, {}, {}, {}, {}]) {
355
+ try { await r.client.chat({ model: "vh-claude-sonnet", messages: [msg(`${PROMPT} ${GH}`)], job: { jobId: JOB }, ...o }); } catch (e) { errors.push(e); }
356
+ }
357
+ assert.equal(errors.length, 4);
358
+ const dump = JSON.stringify([r.client.audit.tail(100), r.client.stats, errors.map((e) => [e.message, e.code, e.stack, util.inspect(e, { depth: 6 })]), util.inspect(r.client, { depth: 6, showHidden: true })]);
359
+ for (const secret of [key, (await import("./helpers/fake-llm.js")).sha(key), PROMPT, ANSWER, GH]) assert.ok(!dump.includes(secret), `leaked: ${secret.slice(0, 12)}`);
360
+ for (const q of r.fake.requests) { assert.ok(!q.raw.includes(key) && !q.raw.includes(GH)); assert.equal(q.headers.authorization, `Bearer ${key}`); }
361
+ for (const rec of r.client.audit.tail(100)) assert.ok(Object.keys(rec).every((k) => ["ts", "event", "ok", "code", "model", "jobShort", "promptBytes", "outBytes", "tokensIn", "tokensOut", "estimated", "attempts", "redactions", "ms", "stream"].includes(k)), `unexpected audit field in ${Object.keys(rec)}`);
362
+ await r.close();
363
+ });
364
+
365
+ test("the error codes are a closed list (an unknown code can not be created)", () => {
366
+ assert.equal(new LlmClientError("whatever text with a secret").code, "internal");
367
+ assert.equal(new LlmClientError("whatever text with a secret").message, "internal");
368
+ for (const c of LLM_ERROR_CODES) assert.match(c, /^[a-z_]+$/);
369
+ });
370
+
371
+ test("holder.matches(ref): true only for the held key's id or its 8..64-hex prefix; a boolean, never the id; false without a key or for a non-hex / short ref", async () => {
372
+ const r = await rig();
373
+ const id = (await import("./helpers/fake-llm.js")).sha(r.key);
374
+ assert.equal(r.holder.matches(id), true);
375
+ assert.equal(r.holder.matches(id.slice(0, 8)), true);
376
+ for (const bad of [id.slice(0, 7), id.slice(0, 7) + "z", "0".repeat(64), "", undefined, 5, id.toUpperCase()]) assert.equal(r.holder.matches(bad), false, String(bad));
377
+ r.holder.revoke(id);
378
+ assert.equal(r.holder.matches(id), false);
379
+ await r.close();
380
+ });