@namzu/openrouter 2.0.0 → 2.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,78 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d3bd080: A wrong API key is no longer reported as working
8
+
9
+ Typing a key into the picker ran a check that could not fail for two providers.
10
+ Measured against deliberately invalid keys, both said the key was good.
11
+
12
+ **With an OpenRouter key, any string at all passed.** A typo, the wrong
13
+ clipboard entry, a revoked key — all were accepted and reported as verified. The
14
+ check listed the model catalogue and treated a successful list as a passed
15
+ check, and OpenRouter's catalogue endpoint does not authenticate, so it answered
16
+ the same way whatever was sent. Nothing was wrong with that driver's listing; a
17
+ catalogue was simply never evidence about a key.
18
+
19
+ **With an Anthropic key, a real rejection was discarded.** The listing caught
20
+ the `401` and returned a hardcoded three-model list, which the check read as
21
+ success — so the truth existed, was thrown away, and was replaced by something
22
+ that looked like an answer.
23
+
24
+ A credential check is now a separate, declared capability. A driver that
25
+ declares no probe is reported as **not checked**, never as verified, so a driver
26
+ added in future cannot silently inherit a check it does not perform. Anthropic,
27
+ OpenRouter, OpenAI and Ollama declare one; OpenRouter's asks about the key
28
+ rather than the catalogue.
29
+
30
+ Refusal and doubt stay distinct. A `401` means the key is genuinely refused; a
31
+ timeout or a DNS failure means nothing was learned, and is reported that way —
32
+ telling someone on a broken connection to rotate a working key is a different
33
+ error, not a smaller one.
34
+
35
+ **Anthropic's model listing also never once ran.** The SDK method was pulled out
36
+ of its namespace and called bare, so it lost `this`, threw a `TypeError` on
37
+ every call, and was swallowed by the same catch — the hardcoded models were not
38
+ a fallback but the only answer the method could give. It now calls the live
39
+ endpoint, and falls back only when that genuinely fails.
40
+
41
+ The four driver packages are `minor` rather than `patch`: each gains a method
42
+ it did not have, and added functionality is a minor whatever the size of the
43
+ diff. Anthropic's earns it twice over, because its listing now returns the live
44
+ catalogue where it previously returned the same three hardcoded entries to every
45
+ caller - so the value every existing caller receives changes.
46
+
47
+ ## 2.0.1
48
+
49
+ ### Patch Changes
50
+
51
+ - 48d9d67: Published tarballs no longer contain test files.
52
+
53
+ `files: ["dist", "src", ...]` reads as "the build output and the sources" and
54
+ means "everything the compiler emitted and everything in the tree", so every
55
+ compiled test, its declaration, and both source maps shipped to the registry —
56
+ and for the twelve packages that also ship `src`, the raw test sources went with
57
+ them.
58
+
59
+ Measured on the versions currently published:
60
+
61
+ | package | files | of which tests | unpacked |
62
+ | ------------ | ----------- | -------------- | ------------------ |
63
+ | `@namzu/sdk` | 3879 → 2239 | 1640 (42%) | 12.73 MB → 6.81 MB |
64
+ | `@namzu/cli` | 462 → 282 | 180 (39%) | 1.21 MB → 0.73 MB |
65
+
66
+ Nothing you can import changes. Every package restricts `exports` to `"."`, so
67
+ Node refused a deep subpath into those files already — they were weight in the
68
+ tarball and nothing else. Hence `patch`: there is no consumer-visible surface
69
+ here, only less to download.
70
+
71
+ The exclusions are at the packaging layer, not the compiler. Adding `exclude`
72
+ to `tsconfig.json` would have kept tests out of `dist` and also dropped them
73
+ from `tsc --noEmit`, silently ending type-checking of the entire test suite —
74
+ trading a packaging defect for a much worse one.
75
+
3
76
  ## 2.0.0
4
77
 
5
78
  ### Major Changes
package/dist/client.d.ts CHANGED
@@ -18,6 +18,21 @@ export declare class OpenRouterProvider implements LLMProvider {
18
18
  private formatMessages;
19
19
  private buildRequestBody;
20
20
  chatStream(params: ChatCompletionParams): AsyncIterable<StreamChunk>;
21
+ /**
22
+ * Ask about the KEY, not about the catalogue.
23
+ *
24
+ * `listModels` here is already honest — it has no fallback and returns
25
+ * exactly what the server sent. It is still useless as a credential check,
26
+ * because `/models` does not authenticate: any string whatsoever, including
27
+ * a typo or a revoked key, came back with the full catalogue and was
28
+ * reported as verified. Nothing was wrong with the menu; the menu was simply
29
+ * never evidence about the key.
30
+ *
31
+ * `/key` is the endpoint that answers the question actually being asked. It
32
+ * requires the credential and returns its metadata, so a 401 here means the
33
+ * key is genuinely refused.
34
+ */
35
+ probeCredential(): Promise<void>;
21
36
  listModels(): Promise<ModelInfo[]>;
22
37
  healthCheck(): Promise<boolean>;
23
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,oBAAoB,EACpB,WAAW,EAGX,MAAM,YAAY,CAAA;AASnB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAwClD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,oBAOrC,CAAA;AAED,qBAAa,kBAAmB,YAAW,WAAW;IACrD,QAAQ,CAAC,EAAE,gBAAe;IAC1B,QAAQ,CAAC,IAAI,gBAAe;IAC5B,QAAQ,CAAC,YAAY,uBAA0B;IAE/C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAQ;gBAEX,MAAM,EAAE,gBAAgB;IAQpC,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,gBAAgB;IAqCjB,UAAU,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CAAC,WAAW,CAAC;IAmIrE,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IA+BlC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAWrC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,oBAAoB,EACpB,WAAW,EAGX,MAAM,YAAY,CAAA;AASnB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAwClD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,oBAOrC,CAAA;AAED,qBAAa,kBAAmB,YAAW,WAAW;IACrD,QAAQ,CAAC,EAAE,gBAAe;IAC1B,QAAQ,CAAC,IAAI,gBAAe;IAC5B,QAAQ,CAAC,YAAY,uBAA0B;IAE/C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAQ;gBAEX,MAAM,EAAE,gBAAgB;IAQpC,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,gBAAgB;IAqCjB,UAAU,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CAAC,WAAW,CAAC;IAmI3E;;;;;;;;;;;;;OAaG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAWhC,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IA+BlC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAWrC"}
package/dist/client.js CHANGED
@@ -239,6 +239,28 @@ export class OpenRouterProvider {
239
239
  reader.releaseLock();
240
240
  }
241
241
  }
242
+ /**
243
+ * Ask about the KEY, not about the catalogue.
244
+ *
245
+ * `listModels` here is already honest — it has no fallback and returns
246
+ * exactly what the server sent. It is still useless as a credential check,
247
+ * because `/models` does not authenticate: any string whatsoever, including
248
+ * a typo or a revoked key, came back with the full catalogue and was
249
+ * reported as verified. Nothing was wrong with the menu; the menu was simply
250
+ * never evidence about the key.
251
+ *
252
+ * `/key` is the endpoint that answers the question actually being asked. It
253
+ * requires the credential and returns its metadata, so a 401 here means the
254
+ * key is genuinely refused.
255
+ */
256
+ async probeCredential() {
257
+ const response = await fetch(`${this.baseUrl}/key`, { headers: this.getHeaders() });
258
+ if (!response.ok) {
259
+ const err = new Error(`Credential check failed: ${response.status}`);
260
+ err.status = response.status;
261
+ throw err;
262
+ }
263
+ }
242
264
  async listModels() {
243
265
  const response = await fetch(`${this.baseUrl}/models`, {
244
266
  headers: this.getHeaders(),
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namzu/openrouter",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "OpenRouter LLM provider for @namzu/sdk. Access to 200+ models (Claude, GPT-4, Llama, Mixtral) via OpenRouter's unified API.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "type": "module",
@@ -23,7 +23,17 @@
23
23
  },
24
24
  "files": [
25
25
  "dist",
26
+ "!dist/**/*.test.*",
27
+ "!dist/**/*.proc-test.*",
28
+ "!dist/**/__tests__",
29
+ "!dist/**/__fixtures__",
30
+ "!dist/**/test-setup.*",
26
31
  "src",
32
+ "!src/**/*.test.*",
33
+ "!src/**/*.proc-test.*",
34
+ "!src/**/__tests__",
35
+ "!src/**/__fixtures__",
36
+ "!src/**/test-setup.*",
27
37
  "LICENSE.md",
28
38
  "README.md",
29
39
  "CHANGELOG.md"
@@ -42,7 +52,7 @@
42
52
  "@types/node": "^22.19.17",
43
53
  "typescript": "^5.5.0",
44
54
  "vitest": "^3.2.6",
45
- "@namzu/sdk": "^5.0.0"
55
+ "@namzu/sdk": "^18.1.0"
46
56
  },
47
57
  "scripts": {
48
58
  "build": "tsc --build",
package/src/client.ts CHANGED
@@ -292,6 +292,31 @@ export class OpenRouterProvider implements LLMProvider {
292
292
  }
293
293
  }
294
294
 
295
+ /**
296
+ * Ask about the KEY, not about the catalogue.
297
+ *
298
+ * `listModels` here is already honest — it has no fallback and returns
299
+ * exactly what the server sent. It is still useless as a credential check,
300
+ * because `/models` does not authenticate: any string whatsoever, including
301
+ * a typo or a revoked key, came back with the full catalogue and was
302
+ * reported as verified. Nothing was wrong with the menu; the menu was simply
303
+ * never evidence about the key.
304
+ *
305
+ * `/key` is the endpoint that answers the question actually being asked. It
306
+ * requires the credential and returns its metadata, so a 401 here means the
307
+ * key is genuinely refused.
308
+ */
309
+ async probeCredential(): Promise<void> {
310
+ const response = await fetch(`${this.baseUrl}/key`, { headers: this.getHeaders() })
311
+ if (!response.ok) {
312
+ const err = new Error(`Credential check failed: ${response.status}`) as Error & {
313
+ status?: number
314
+ }
315
+ err.status = response.status
316
+ throw err
317
+ }
318
+ }
319
+
295
320
  async listModels(): Promise<ModelInfo[]> {
296
321
  const response = await fetch(`${this.baseUrl}/models`, {
297
322
  headers: this.getHeaders(),
@@ -1,15 +0,0 @@
1
- /**
2
- * Provider error taxonomy — OpenRouter driver.
3
- *
4
- * Every provider failure must reach the caller as a classified
5
- * `ProviderRequestError` (kind + status + retryAfterMs + providerId), not
6
- * as an opaque vendor string. The message must be built from the status
7
- * line and the classified kind ONLY — never from the response body, which
8
- * is where a credential echoed back by the upstream would leak into a log.
9
- *
10
- * Transport seam: the driver calls the global `fetch` directly, so the
11
- * existing `vi.stubGlobal('fetch', …)` harness from `http.test.ts` applies
12
- * verbatim.
13
- */
14
- export {};
15
- //# sourceMappingURL=error-taxonomy.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-taxonomy.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/error-taxonomy.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
@@ -1,187 +0,0 @@
1
- /**
2
- * Provider error taxonomy — OpenRouter driver.
3
- *
4
- * Every provider failure must reach the caller as a classified
5
- * `ProviderRequestError` (kind + status + retryAfterMs + providerId), not
6
- * as an opaque vendor string. The message must be built from the status
7
- * line and the classified kind ONLY — never from the response body, which
8
- * is where a credential echoed back by the upstream would leak into a log.
9
- *
10
- * Transport seam: the driver calls the global `fetch` directly, so the
11
- * existing `vi.stubGlobal('fetch', …)` harness from `http.test.ts` applies
12
- * verbatim.
13
- */
14
- import { afterEach, describe, expect, it, vi } from 'vitest';
15
- import { OpenRouterProvider } from '../client.js';
16
- /**
17
- * An obviously fake, non-functional token. Present ONLY to prove the
18
- * driver does not interpolate a response body into a thrown message.
19
- */
20
- const FAKE_CREDENTIAL = 'sk-ant-FAKE-DO-NOT-USE-0000';
21
- afterEach(() => {
22
- vi.unstubAllGlobals();
23
- });
24
- function respond(status, body, headers = {}) {
25
- return new Response(body, {
26
- status,
27
- headers: { 'Content-Type': 'application/json', ...headers },
28
- });
29
- }
30
- function sseResponse(frames) {
31
- return new Response(`${frames.join('\n\n')}\n\n`, {
32
- status: 200,
33
- headers: { 'Content-Type': 'text/event-stream' },
34
- });
35
- }
36
- async function captureChatStreamError(provider, signal) {
37
- try {
38
- for await (const _chunk of provider.chatStream({
39
- model: 'anthropic/claude-sonnet-4-5',
40
- messages: [{ role: 'user', content: 'hi' }],
41
- signal,
42
- })) {
43
- // drain
44
- }
45
- }
46
- catch (err) {
47
- return err;
48
- }
49
- throw new Error('expected chatStream to throw');
50
- }
51
- function makeProvider() {
52
- return new OpenRouterProvider({
53
- apiKey: 'test-key',
54
- baseUrl: 'https://example.test/api/v1',
55
- });
56
- }
57
- describe('@namzu/openrouter — provider error taxonomy', () => {
58
- it('(a) 429 with `retry-after: 2` throws a throttle-classified error carrying retryAfterMs', async () => {
59
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue(respond(429, JSON.stringify({ error: { message: 'rate limit exceeded' } }), {
60
- 'retry-after': '2',
61
- })));
62
- const err = await captureChatStreamError(makeProvider());
63
- expect(err).toMatchObject({
64
- name: 'ProviderRequestError',
65
- kind: 'throttle',
66
- status: 429,
67
- retryAfterMs: 2000,
68
- providerId: 'openrouter',
69
- });
70
- });
71
- it("(c) 400 whose body says the prompt is too long classifies as 'context_overflow'", async () => {
72
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue(respond(400, JSON.stringify({
73
- error: {
74
- type: 'invalid_request_error',
75
- message: 'prompt is too long: 305231 tokens > 200000 maximum',
76
- },
77
- }))));
78
- const err = await captureChatStreamError(makeProvider());
79
- expect(err).toMatchObject({
80
- name: 'ProviderRequestError',
81
- kind: 'context_overflow',
82
- status: 400,
83
- providerId: 'openrouter',
84
- });
85
- });
86
- it("(c) 400 that is not an overflow classifies as 'bad_request'", async () => {
87
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue(respond(400, JSON.stringify({
88
- error: {
89
- type: 'invalid_request_error',
90
- message: "unknown field 'temperture'",
91
- },
92
- }))));
93
- const err = await captureChatStreamError(makeProvider());
94
- expect(err).toMatchObject({
95
- name: 'ProviderRequestError',
96
- kind: 'bad_request',
97
- status: 400,
98
- providerId: 'openrouter',
99
- });
100
- });
101
- it("(a/auth) 401 classifies as 'auth'", async () => {
102
- vi.stubGlobal('fetch', vi
103
- .fn()
104
- .mockResolvedValue(respond(401, JSON.stringify({ error: { message: 'No auth credentials found' } }))));
105
- const err = await captureChatStreamError(makeProvider());
106
- expect(err).toMatchObject({
107
- name: 'ProviderRequestError',
108
- kind: 'auth',
109
- status: 401,
110
- providerId: 'openrouter',
111
- });
112
- });
113
- it('(d) SECURITY: a credential echoed back in a 401 body never reaches the thrown message', async () => {
114
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue(respond(401, JSON.stringify({
115
- error: { message: `invalid api key: ${FAKE_CREDENTIAL}` },
116
- }))));
117
- const err = await captureChatStreamError(makeProvider());
118
- expect(err).toBeInstanceOf(Error);
119
- const message = err.message;
120
- expect(message).not.toContain(FAKE_CREDENTIAL);
121
- expect(JSON.stringify(err)).not.toContain(FAKE_CREDENTIAL);
122
- expect('cause' in err).toBe(false);
123
- });
124
- it('(d) SECURITY: a credential echoed back in a 400 body never reaches the thrown message', async () => {
125
- vi.stubGlobal('fetch', vi
126
- .fn()
127
- .mockResolvedValue(respond(400, `bad request for key ${FAKE_CREDENTIAL}: model not permitted`)));
128
- const err = await captureChatStreamError(makeProvider());
129
- expect(err.message).not.toContain(FAKE_CREDENTIAL);
130
- });
131
- it("classifies a fetch rejection as 'network' without retaining its cause", async () => {
132
- const vendor = new Error(`socket failed for ${FAKE_CREDENTIAL}`);
133
- vi.stubGlobal('fetch', vi.fn().mockRejectedValue(vendor));
134
- const err = await captureChatStreamError(makeProvider());
135
- expect(err).toMatchObject({
136
- name: 'ProviderRequestError',
137
- kind: 'network',
138
- providerId: 'openrouter',
139
- });
140
- expect(err.message).not.toContain(FAKE_CREDENTIAL);
141
- expect('cause' in err).toBe(false);
142
- });
143
- it('preserves the caller abort reason when fetch replaces the error object', async () => {
144
- const controller = new AbortController();
145
- const reason = new Error('user stopped');
146
- const fetchAbort = Object.assign(new Error('request aborted'), {
147
- name: 'AbortError',
148
- });
149
- controller.abort(reason);
150
- vi.stubGlobal('fetch', vi.fn().mockRejectedValue(fetchAbort));
151
- const err = await captureChatStreamError(makeProvider(), controller.signal);
152
- expect(fetchAbort).not.toBe(reason);
153
- expect(err).toBe(reason);
154
- });
155
- it('classifies and sanitizes an SSE error after the 200 response', async () => {
156
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue(sseResponse([
157
- `data: ${JSON.stringify({
158
- error: {
159
- type: 'overloaded_error',
160
- message: `echoed ${FAKE_CREDENTIAL}`,
161
- },
162
- })}`,
163
- ])));
164
- const err = await captureChatStreamError(makeProvider());
165
- expect(err).toMatchObject({
166
- name: 'ProviderRequestError',
167
- kind: 'server',
168
- providerId: 'openrouter',
169
- });
170
- expect(err.message).not.toContain(FAKE_CREDENTIAL);
171
- expect(JSON.stringify(err)).not.toContain(FAKE_CREDENTIAL);
172
- expect('cause' in err).toBe(false);
173
- });
174
- it('classifies malformed SSE without retaining the credential-bearing frame', async () => {
175
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue(sseResponse([`data: ${FAKE_CREDENTIAL} not-json`])));
176
- const err = await captureChatStreamError(makeProvider());
177
- expect(err).toMatchObject({
178
- name: 'ProviderRequestError',
179
- kind: 'server',
180
- providerId: 'openrouter',
181
- });
182
- expect(err.message).not.toContain(FAKE_CREDENTIAL);
183
- expect(JSON.stringify(err)).not.toContain(FAKE_CREDENTIAL);
184
- expect('cause' in err).toBe(false);
185
- });
186
- });
187
- //# sourceMappingURL=error-taxonomy.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-taxonomy.test.js","sourceRoot":"","sources":["../../src/__tests__/error-taxonomy.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=openrouter.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"openrouter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/openrouter.test.ts"],"names":[],"mappings":""}
@@ -1,85 +0,0 @@
1
- import { DuplicateProviderError, ProviderRegistry } from '@namzu/sdk';
2
- import { beforeEach, describe, expect, it } from 'vitest';
3
- import { OPENROUTER_CAPABILITIES, OpenRouterProvider, registerOpenRouter } from '../index.js';
4
- // Ensure a clean slate between tests. The sdk pre-registers 'mock' on import
5
- // via its sideEffects whitelist; we only need to clear 'openrouter' to make
6
- // re-registration deterministic across tests.
7
- beforeEach(() => {
8
- if (ProviderRegistry.isSupported('openrouter')) {
9
- ProviderRegistry.unregister('openrouter');
10
- }
11
- });
12
- describe('@namzu/openrouter', () => {
13
- describe('registerOpenRouter()', () => {
14
- it("adds 'openrouter' to the ProviderRegistry", () => {
15
- expect(ProviderRegistry.isSupported('openrouter')).toBe(false);
16
- registerOpenRouter();
17
- expect(ProviderRegistry.isSupported('openrouter')).toBe(true);
18
- expect(ProviderRegistry.listTypes()).toContain('openrouter');
19
- });
20
- it('throws DuplicateProviderError when called twice without options', () => {
21
- registerOpenRouter();
22
- expect(() => registerOpenRouter()).toThrowError(DuplicateProviderError);
23
- });
24
- it('allows re-registration when { replace: true } is passed', () => {
25
- registerOpenRouter();
26
- expect(() => registerOpenRouter({ replace: true })).not.toThrow();
27
- expect(ProviderRegistry.isSupported('openrouter')).toBe(true);
28
- });
29
- it('exposes capabilities through the registry after registration', () => {
30
- registerOpenRouter();
31
- const caps = ProviderRegistry.getCapabilities('openrouter');
32
- expect(caps).toEqual(OPENROUTER_CAPABILITIES);
33
- });
34
- });
35
- describe('OPENROUTER_CAPABILITIES', () => {
36
- it('declares the expected capability flags', () => {
37
- expect(OPENROUTER_CAPABILITIES).toEqual({
38
- supportsTools: true,
39
- supportsStreaming: true,
40
- supportsFunctionCalling: true,
41
- supportsVision: false,
42
- supportsDocuments: false,
43
- });
44
- });
45
- it('is exposed on the provider instance for runtime negotiation', () => {
46
- const provider = new OpenRouterProvider({ apiKey: 'test-key' });
47
- expect(provider.capabilities).toEqual(OPENROUTER_CAPABILITIES);
48
- });
49
- });
50
- describe('ProviderRegistry.create({ type: "openrouter", ... })', () => {
51
- it('narrows the config type via module augmentation and instantiates OpenRouterProvider', () => {
52
- registerOpenRouter();
53
- const { provider, capabilities } = ProviderRegistry.create({
54
- type: 'openrouter',
55
- apiKey: 'test-key',
56
- siteUrl: 'https://example.com',
57
- siteName: 'Test',
58
- });
59
- expect(provider).toBeInstanceOf(OpenRouterProvider);
60
- expect(capabilities).toEqual(OPENROUTER_CAPABILITIES);
61
- });
62
- });
63
- it('keeps the Namzu enforcement hint out of the OpenRouter request body', () => {
64
- const provider = new OpenRouterProvider({ apiKey: 'test-key' });
65
- const tools = [
66
- {
67
- type: 'function',
68
- function: {
69
- name: 'edit',
70
- description: 'Edit',
71
- parameters: { type: 'object' },
72
- },
73
- },
74
- ];
75
- const body = provider.buildRequestBody({
76
- model: 'anthropic/claude-sonnet-5',
77
- messages: [{ role: 'user', content: 'edit' }],
78
- tools,
79
- enforceToolInputSchema: ['edit'],
80
- }, true);
81
- expect(body).not.toHaveProperty('enforceToolInputSchema');
82
- expect(body.tools).toEqual(tools);
83
- });
84
- });
85
- //# sourceMappingURL=openrouter.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"openrouter.test.js","sourceRoot":"","sources":["../../src/__tests__/openrouter.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=thinking-refused.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"thinking-refused.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/thinking-refused.test.ts"],"names":[],"mappings":""}
@@ -1,35 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { OpenRouterProvider } from '../client.js';
3
- /**
4
- * The shared guard's own tests pass whether or not this driver calls it, so
5
- * this drives the real chatStream. The defect being fixed was a declared
6
- * parameter no code path consumed — asserting the helper works would have
7
- * reproduced that shape one level up.
8
- *
9
- * The guard runs before any request, so the unreachable host below is never
10
- * dialled on the throwing cases.
11
- */
12
- const provider = new OpenRouterProvider({ apiKey: 'test-key' });
13
- async function run(thinking) {
14
- for await (const _chunk of provider.chatStream({
15
- model: 'anthropic/claude-sonnet-4.5',
16
- messages: [{ role: 'user', content: 'hi' }],
17
- ...(thinking ? { thinking } : {}),
18
- })) {
19
- // drain
20
- }
21
- }
22
- describe('OpenRouterProvider refuses a thinking request rather than dropping it', () => {
23
- it('throws on a manual thinking request', async () => {
24
- await expect(run({ type: 'enabled', budgetTokens: 10_000 })).rejects.toThrow(/OpenRouterProvider does not implement thinking/);
25
- });
26
- it('throws on an adaptive one', async () => {
27
- await expect(run({ type: 'adaptive' })).rejects.toThrow(/does not implement thinking/);
28
- });
29
- it('does not throw for an explicit disable', async () => {
30
- // Fails on the unreachable host instead, which is the point: the
31
- // guard let it past.
32
- await expect(run({ type: 'disabled' })).rejects.not.toThrow(/does not implement thinking/);
33
- });
34
- });
35
- //# sourceMappingURL=thinking-refused.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"thinking-refused.test.js","sourceRoot":"","sources":["../../src/__tests__/thinking-refused.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEjD;;;;;;;;GAQG;AAEH,MAAM,QAAQ,GAAG,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAW,CAAC,CAAA;AAExE,KAAK,UAAU,GAAG,CAAC,QAA2C;IAC7D,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,QAAQ,CAAC,UAAU,CAAC;QAC9C,KAAK,EAAE,6BAA6B;QACpC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3C,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACT,CAAC,EAAE,CAAC;QAC5B,QAAQ;IACT,CAAC;AACF,CAAC;AAED,QAAQ,CAAC,uEAAuE,EAAE,GAAG,EAAE;IACtF,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC3E,gDAAgD,CAChD,CAAA;IACF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAA;IACvF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACvD,iEAAiE;QACjE,qBAAqB;QACrB,MAAM,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAA;IAC3F,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=vision.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vision.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/vision.test.ts"],"names":[],"mappings":""}
@@ -1,42 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { OPENROUTER_CAPABILITIES } from '../client.js';
3
- /**
4
- * This driver does not map attachments, and says so.
5
- *
6
- * Shipping without a wire shape is legitimate; claiming one you do not have
7
- * is not. The runtime reads these flags and warns — or fails under
8
- * `strictCapabilities` — before the request is built, so the flags are the
9
- * contract, and what this pins is drift in either direction: flipping a
10
- * flag without writing the mapping makes the runtime stop warning about
11
- * images that still vanish, and writing the mapping without flipping the
12
- * flag makes it keep warning about images that now arrive.
13
- */
14
- describe('the declared capabilities are the honest ones', () => {
15
- it('does not claim vision', () => {
16
- expect(OPENROUTER_CAPABILITIES.supportsVision).toBe(false);
17
- });
18
- it('does not claim documents', () => {
19
- expect(OPENROUTER_CAPABILITIES.supportsDocuments).toBe(false);
20
- });
21
- it('claims the tool support it does have', () => {
22
- expect(OPENROUTER_CAPABILITIES.supportsTools).toBe(true);
23
- expect(OPENROUTER_CAPABILITIES.supportsFunctionCalling).toBe(true);
24
- });
25
- it('claims streaming', () => {
26
- expect(OPENROUTER_CAPABILITIES.supportsStreaming).toBe(true);
27
- });
28
- it('answers every flag the negotiation reads', () => {
29
- // An absent flag defaults to permissive, so a missing one is not a
30
- // neutral omission — it is a claim.
31
- for (const flag of [
32
- 'supportsTools',
33
- 'supportsStreaming',
34
- 'supportsFunctionCalling',
35
- 'supportsVision',
36
- 'supportsDocuments',
37
- ]) {
38
- expect(typeof OPENROUTER_CAPABILITIES[flag]).toBe('boolean');
39
- }
40
- });
41
- });
42
- //# sourceMappingURL=vision.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vision.test.js","sourceRoot":"","sources":["../../src/__tests__/vision.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAEtD;;;;;;;;;;GAUG;AAEH,QAAQ,CAAC,+CAA+C,EAAE,GAAG,EAAE;IAC9D,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,uBAAuB,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,uBAAuB,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACxD,MAAM,CAAC,uBAAuB,CAAC,uBAAuB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACnD,mEAAmE;QACnE,oCAAoC;QACpC,KAAK,MAAM,IAAI,IAAI;YAClB,eAAe;YACf,mBAAmB;YACnB,yBAAyB;YACzB,gBAAgB;YAChB,mBAAmB;SACV,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC7D,CAAC;IACF,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
@@ -1,272 +0,0 @@
1
- /**
2
- * Provider error taxonomy — OpenRouter driver.
3
- *
4
- * Every provider failure must reach the caller as a classified
5
- * `ProviderRequestError` (kind + status + retryAfterMs + providerId), not
6
- * as an opaque vendor string. The message must be built from the status
7
- * line and the classified kind ONLY — never from the response body, which
8
- * is where a credential echoed back by the upstream would leak into a log.
9
- *
10
- * Transport seam: the driver calls the global `fetch` directly, so the
11
- * existing `vi.stubGlobal('fetch', …)` harness from `http.test.ts` applies
12
- * verbatim.
13
- */
14
-
15
- import { afterEach, describe, expect, it, vi } from 'vitest'
16
- import { OpenRouterProvider } from '../client.js'
17
-
18
- /**
19
- * An obviously fake, non-functional token. Present ONLY to prove the
20
- * driver does not interpolate a response body into a thrown message.
21
- */
22
- const FAKE_CREDENTIAL = 'sk-ant-FAKE-DO-NOT-USE-0000'
23
-
24
- afterEach(() => {
25
- vi.unstubAllGlobals()
26
- })
27
-
28
- function respond(status: number, body: string, headers: Record<string, string> = {}): Response {
29
- return new Response(body, {
30
- status,
31
- headers: { 'Content-Type': 'application/json', ...headers },
32
- })
33
- }
34
-
35
- function sseResponse(frames: string[]): Response {
36
- return new Response(`${frames.join('\n\n')}\n\n`, {
37
- status: 200,
38
- headers: { 'Content-Type': 'text/event-stream' },
39
- })
40
- }
41
-
42
- async function captureChatStreamError(
43
- provider: OpenRouterProvider,
44
- signal?: AbortSignal,
45
- ): Promise<unknown> {
46
- try {
47
- for await (const _chunk of provider.chatStream({
48
- model: 'anthropic/claude-sonnet-4-5',
49
- messages: [{ role: 'user', content: 'hi' }],
50
- signal,
51
- })) {
52
- // drain
53
- }
54
- } catch (err) {
55
- return err
56
- }
57
- throw new Error('expected chatStream to throw')
58
- }
59
-
60
- function makeProvider(): OpenRouterProvider {
61
- return new OpenRouterProvider({
62
- apiKey: 'test-key',
63
- baseUrl: 'https://example.test/api/v1',
64
- })
65
- }
66
-
67
- describe('@namzu/openrouter — provider error taxonomy', () => {
68
- it('(a) 429 with `retry-after: 2` throws a throttle-classified error carrying retryAfterMs', async () => {
69
- vi.stubGlobal(
70
- 'fetch',
71
- vi.fn().mockResolvedValue(
72
- respond(429, JSON.stringify({ error: { message: 'rate limit exceeded' } }), {
73
- 'retry-after': '2',
74
- }),
75
- ),
76
- )
77
-
78
- const err = await captureChatStreamError(makeProvider())
79
-
80
- expect(err).toMatchObject({
81
- name: 'ProviderRequestError',
82
- kind: 'throttle',
83
- status: 429,
84
- retryAfterMs: 2000,
85
- providerId: 'openrouter',
86
- })
87
- })
88
-
89
- it("(c) 400 whose body says the prompt is too long classifies as 'context_overflow'", async () => {
90
- vi.stubGlobal(
91
- 'fetch',
92
- vi.fn().mockResolvedValue(
93
- respond(
94
- 400,
95
- JSON.stringify({
96
- error: {
97
- type: 'invalid_request_error',
98
- message: 'prompt is too long: 305231 tokens > 200000 maximum',
99
- },
100
- }),
101
- ),
102
- ),
103
- )
104
-
105
- const err = await captureChatStreamError(makeProvider())
106
-
107
- expect(err).toMatchObject({
108
- name: 'ProviderRequestError',
109
- kind: 'context_overflow',
110
- status: 400,
111
- providerId: 'openrouter',
112
- })
113
- })
114
-
115
- it("(c) 400 that is not an overflow classifies as 'bad_request'", async () => {
116
- vi.stubGlobal(
117
- 'fetch',
118
- vi.fn().mockResolvedValue(
119
- respond(
120
- 400,
121
- JSON.stringify({
122
- error: {
123
- type: 'invalid_request_error',
124
- message: "unknown field 'temperture'",
125
- },
126
- }),
127
- ),
128
- ),
129
- )
130
-
131
- const err = await captureChatStreamError(makeProvider())
132
-
133
- expect(err).toMatchObject({
134
- name: 'ProviderRequestError',
135
- kind: 'bad_request',
136
- status: 400,
137
- providerId: 'openrouter',
138
- })
139
- })
140
-
141
- it("(a/auth) 401 classifies as 'auth'", async () => {
142
- vi.stubGlobal(
143
- 'fetch',
144
- vi
145
- .fn()
146
- .mockResolvedValue(
147
- respond(401, JSON.stringify({ error: { message: 'No auth credentials found' } })),
148
- ),
149
- )
150
-
151
- const err = await captureChatStreamError(makeProvider())
152
-
153
- expect(err).toMatchObject({
154
- name: 'ProviderRequestError',
155
- kind: 'auth',
156
- status: 401,
157
- providerId: 'openrouter',
158
- })
159
- })
160
-
161
- it('(d) SECURITY: a credential echoed back in a 401 body never reaches the thrown message', async () => {
162
- vi.stubGlobal(
163
- 'fetch',
164
- vi.fn().mockResolvedValue(
165
- respond(
166
- 401,
167
- JSON.stringify({
168
- error: { message: `invalid api key: ${FAKE_CREDENTIAL}` },
169
- }),
170
- ),
171
- ),
172
- )
173
-
174
- const err = await captureChatStreamError(makeProvider())
175
-
176
- expect(err).toBeInstanceOf(Error)
177
- const message = (err as Error).message
178
- expect(message).not.toContain(FAKE_CREDENTIAL)
179
- expect(JSON.stringify(err)).not.toContain(FAKE_CREDENTIAL)
180
- expect('cause' in (err as object)).toBe(false)
181
- })
182
-
183
- it('(d) SECURITY: a credential echoed back in a 400 body never reaches the thrown message', async () => {
184
- vi.stubGlobal(
185
- 'fetch',
186
- vi
187
- .fn()
188
- .mockResolvedValue(
189
- respond(400, `bad request for key ${FAKE_CREDENTIAL}: model not permitted`),
190
- ),
191
- )
192
-
193
- const err = await captureChatStreamError(makeProvider())
194
-
195
- expect((err as Error).message).not.toContain(FAKE_CREDENTIAL)
196
- })
197
-
198
- it("classifies a fetch rejection as 'network' without retaining its cause", async () => {
199
- const vendor = new Error(`socket failed for ${FAKE_CREDENTIAL}`)
200
- vi.stubGlobal('fetch', vi.fn().mockRejectedValue(vendor))
201
-
202
- const err = await captureChatStreamError(makeProvider())
203
-
204
- expect(err).toMatchObject({
205
- name: 'ProviderRequestError',
206
- kind: 'network',
207
- providerId: 'openrouter',
208
- })
209
- expect((err as Error).message).not.toContain(FAKE_CREDENTIAL)
210
- expect('cause' in (err as object)).toBe(false)
211
- })
212
-
213
- it('preserves the caller abort reason when fetch replaces the error object', async () => {
214
- const controller = new AbortController()
215
- const reason = new Error('user stopped')
216
- const fetchAbort = Object.assign(new Error('request aborted'), {
217
- name: 'AbortError',
218
- })
219
- controller.abort(reason)
220
- vi.stubGlobal('fetch', vi.fn().mockRejectedValue(fetchAbort))
221
-
222
- const err = await captureChatStreamError(makeProvider(), controller.signal)
223
-
224
- expect(fetchAbort).not.toBe(reason)
225
- expect(err).toBe(reason)
226
- })
227
-
228
- it('classifies and sanitizes an SSE error after the 200 response', async () => {
229
- vi.stubGlobal(
230
- 'fetch',
231
- vi.fn().mockResolvedValue(
232
- sseResponse([
233
- `data: ${JSON.stringify({
234
- error: {
235
- type: 'overloaded_error',
236
- message: `echoed ${FAKE_CREDENTIAL}`,
237
- },
238
- })}`,
239
- ]),
240
- ),
241
- )
242
-
243
- const err = await captureChatStreamError(makeProvider())
244
-
245
- expect(err).toMatchObject({
246
- name: 'ProviderRequestError',
247
- kind: 'server',
248
- providerId: 'openrouter',
249
- })
250
- expect((err as Error).message).not.toContain(FAKE_CREDENTIAL)
251
- expect(JSON.stringify(err)).not.toContain(FAKE_CREDENTIAL)
252
- expect('cause' in (err as object)).toBe(false)
253
- })
254
-
255
- it('classifies malformed SSE without retaining the credential-bearing frame', async () => {
256
- vi.stubGlobal(
257
- 'fetch',
258
- vi.fn().mockResolvedValue(sseResponse([`data: ${FAKE_CREDENTIAL} not-json`])),
259
- )
260
-
261
- const err = await captureChatStreamError(makeProvider())
262
-
263
- expect(err).toMatchObject({
264
- name: 'ProviderRequestError',
265
- kind: 'server',
266
- providerId: 'openrouter',
267
- })
268
- expect((err as Error).message).not.toContain(FAKE_CREDENTIAL)
269
- expect(JSON.stringify(err)).not.toContain(FAKE_CREDENTIAL)
270
- expect('cause' in (err as object)).toBe(false)
271
- })
272
- })
@@ -1,104 +0,0 @@
1
- import { DuplicateProviderError, ProviderRegistry } from '@namzu/sdk'
2
- import { beforeEach, describe, expect, it } from 'vitest'
3
- import { OPENROUTER_CAPABILITIES, OpenRouterProvider, registerOpenRouter } from '../index.js'
4
-
5
- // Ensure a clean slate between tests. The sdk pre-registers 'mock' on import
6
- // via its sideEffects whitelist; we only need to clear 'openrouter' to make
7
- // re-registration deterministic across tests.
8
- beforeEach(() => {
9
- if (ProviderRegistry.isSupported('openrouter')) {
10
- ProviderRegistry.unregister('openrouter')
11
- }
12
- })
13
-
14
- describe('@namzu/openrouter', () => {
15
- describe('registerOpenRouter()', () => {
16
- it("adds 'openrouter' to the ProviderRegistry", () => {
17
- expect(ProviderRegistry.isSupported('openrouter')).toBe(false)
18
- registerOpenRouter()
19
- expect(ProviderRegistry.isSupported('openrouter')).toBe(true)
20
- expect(ProviderRegistry.listTypes()).toContain('openrouter')
21
- })
22
-
23
- it('throws DuplicateProviderError when called twice without options', () => {
24
- registerOpenRouter()
25
- expect(() => registerOpenRouter()).toThrowError(DuplicateProviderError)
26
- })
27
-
28
- it('allows re-registration when { replace: true } is passed', () => {
29
- registerOpenRouter()
30
- expect(() => registerOpenRouter({ replace: true })).not.toThrow()
31
- expect(ProviderRegistry.isSupported('openrouter')).toBe(true)
32
- })
33
-
34
- it('exposes capabilities through the registry after registration', () => {
35
- registerOpenRouter()
36
- const caps = ProviderRegistry.getCapabilities('openrouter')
37
- expect(caps).toEqual(OPENROUTER_CAPABILITIES)
38
- })
39
- })
40
-
41
- describe('OPENROUTER_CAPABILITIES', () => {
42
- it('declares the expected capability flags', () => {
43
- expect(OPENROUTER_CAPABILITIES).toEqual({
44
- supportsTools: true,
45
- supportsStreaming: true,
46
- supportsFunctionCalling: true,
47
- supportsVision: false,
48
- supportsDocuments: false,
49
- })
50
- })
51
-
52
- it('is exposed on the provider instance for runtime negotiation', () => {
53
- const provider = new OpenRouterProvider({ apiKey: 'test-key' })
54
- expect(provider.capabilities).toEqual(OPENROUTER_CAPABILITIES)
55
- })
56
- })
57
-
58
- describe('ProviderRegistry.create({ type: "openrouter", ... })', () => {
59
- it('narrows the config type via module augmentation and instantiates OpenRouterProvider', () => {
60
- registerOpenRouter()
61
- const { provider, capabilities } = ProviderRegistry.create({
62
- type: 'openrouter',
63
- apiKey: 'test-key',
64
- siteUrl: 'https://example.com',
65
- siteName: 'Test',
66
- })
67
- expect(provider).toBeInstanceOf(OpenRouterProvider)
68
- expect(capabilities).toEqual(OPENROUTER_CAPABILITIES)
69
- })
70
- })
71
-
72
- it('keeps the Namzu enforcement hint out of the OpenRouter request body', () => {
73
- const provider = new OpenRouterProvider({ apiKey: 'test-key' })
74
- const tools = [
75
- {
76
- type: 'function' as const,
77
- function: {
78
- name: 'edit',
79
- description: 'Edit',
80
- parameters: { type: 'object' },
81
- },
82
- },
83
- ]
84
- const body = (
85
- provider as unknown as {
86
- buildRequestBody(
87
- params: import('@namzu/sdk').ChatCompletionParams,
88
- stream: boolean,
89
- ): Record<string, unknown>
90
- }
91
- ).buildRequestBody(
92
- {
93
- model: 'anthropic/claude-sonnet-5',
94
- messages: [{ role: 'user', content: 'edit' }],
95
- tools,
96
- enforceToolInputSchema: ['edit'],
97
- },
98
- true,
99
- )
100
-
101
- expect(body).not.toHaveProperty('enforceToolInputSchema')
102
- expect(body.tools).toEqual(tools)
103
- })
104
- })
@@ -1,44 +0,0 @@
1
- import type { ChatCompletionParams } from '@namzu/sdk'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- import { OpenRouterProvider } from '../client.js'
5
-
6
- /**
7
- * The shared guard's own tests pass whether or not this driver calls it, so
8
- * this drives the real chatStream. The defect being fixed was a declared
9
- * parameter no code path consumed — asserting the helper works would have
10
- * reproduced that shape one level up.
11
- *
12
- * The guard runs before any request, so the unreachable host below is never
13
- * dialled on the throwing cases.
14
- */
15
-
16
- const provider = new OpenRouterProvider({ apiKey: 'test-key' } as never)
17
-
18
- async function run(thinking?: ChatCompletionParams['thinking']): Promise<void> {
19
- for await (const _chunk of provider.chatStream({
20
- model: 'anthropic/claude-sonnet-4.5',
21
- messages: [{ role: 'user', content: 'hi' }],
22
- ...(thinking ? { thinking } : {}),
23
- } as ChatCompletionParams)) {
24
- // drain
25
- }
26
- }
27
-
28
- describe('OpenRouterProvider refuses a thinking request rather than dropping it', () => {
29
- it('throws on a manual thinking request', async () => {
30
- await expect(run({ type: 'enabled', budgetTokens: 10_000 })).rejects.toThrow(
31
- /OpenRouterProvider does not implement thinking/,
32
- )
33
- })
34
-
35
- it('throws on an adaptive one', async () => {
36
- await expect(run({ type: 'adaptive' })).rejects.toThrow(/does not implement thinking/)
37
- })
38
-
39
- it('does not throw for an explicit disable', async () => {
40
- // Fails on the unreachable host instead, which is the point: the
41
- // guard let it past.
42
- await expect(run({ type: 'disabled' })).rejects.not.toThrow(/does not implement thinking/)
43
- })
44
- })
@@ -1,48 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
-
3
- import { OPENROUTER_CAPABILITIES } from '../client.js'
4
-
5
- /**
6
- * This driver does not map attachments, and says so.
7
- *
8
- * Shipping without a wire shape is legitimate; claiming one you do not have
9
- * is not. The runtime reads these flags and warns — or fails under
10
- * `strictCapabilities` — before the request is built, so the flags are the
11
- * contract, and what this pins is drift in either direction: flipping a
12
- * flag without writing the mapping makes the runtime stop warning about
13
- * images that still vanish, and writing the mapping without flipping the
14
- * flag makes it keep warning about images that now arrive.
15
- */
16
-
17
- describe('the declared capabilities are the honest ones', () => {
18
- it('does not claim vision', () => {
19
- expect(OPENROUTER_CAPABILITIES.supportsVision).toBe(false)
20
- })
21
-
22
- it('does not claim documents', () => {
23
- expect(OPENROUTER_CAPABILITIES.supportsDocuments).toBe(false)
24
- })
25
-
26
- it('claims the tool support it does have', () => {
27
- expect(OPENROUTER_CAPABILITIES.supportsTools).toBe(true)
28
- expect(OPENROUTER_CAPABILITIES.supportsFunctionCalling).toBe(true)
29
- })
30
-
31
- it('claims streaming', () => {
32
- expect(OPENROUTER_CAPABILITIES.supportsStreaming).toBe(true)
33
- })
34
-
35
- it('answers every flag the negotiation reads', () => {
36
- // An absent flag defaults to permissive, so a missing one is not a
37
- // neutral omission — it is a claim.
38
- for (const flag of [
39
- 'supportsTools',
40
- 'supportsStreaming',
41
- 'supportsFunctionCalling',
42
- 'supportsVision',
43
- 'supportsDocuments',
44
- ] as const) {
45
- expect(typeof OPENROUTER_CAPABILITIES[flag]).toBe('boolean')
46
- }
47
- })
48
- })