@namzu/sdk 18.0.0 → 18.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,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 18.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
+
3
47
  ## 18.0.0
4
48
 
5
49
  ### Major Changes
@@ -32,6 +32,40 @@ export interface LLMProvider {
32
32
  */
33
33
  chatStream(params: ChatCompletionParams): AsyncIterable<StreamChunk>;
34
34
  listModels?(): Promise<ModelInfo[]>;
35
+ /**
36
+ * Establish whether this credential actually works. Resolves if it does,
37
+ * throws if it does not.
38
+ *
39
+ * Separate from `listModels` because the two answer different questions, and
40
+ * conflating them is a defect measured rather than imagined. `listModels`
41
+ * builds a MENU: "what can I offer this operator to choose from?" — a stale
42
+ * hardcoded list is a degraded but legitimate answer, since someone offline
43
+ * still has to pick a model. This builds a PROBE: "did this key work?" — and
44
+ * for that a list is not a degraded answer, it is no answer, because it
45
+ * arrives whether the key is right, wrong, expired or never sent.
46
+ *
47
+ * Two drivers proved a menu cannot stand in for a probe, and they failed
48
+ * differently. One caught a real `401` and returned its hardcoded catalogue,
49
+ * so the truth existed and was thrown away. The other has no fallback at all
50
+ * and is entirely honest about its menu — its listing endpoint simply does
51
+ * not authenticate, so ANY string returned the real catalogue. That second
52
+ * case is why this is a separate method rather than a rule about writing
53
+ * `listModels` more carefully: no amount of care in a menu makes it a probe.
54
+ *
55
+ * The probe is per-driver by nature — one has an authenticated call whose
56
+ * failure is real, another needs a different endpoint than its menu — so it
57
+ * is DECLARED, never inferred. A driver that does not implement this is
58
+ * reported as unverifiable, never as verified, and that has to hold for the
59
+ * driver nobody has written yet: inheriting a generic path silently is how
60
+ * this defect returns.
61
+ *
62
+ * Throw so the caller can tell the two failures apart. A rejection from the
63
+ * server (`401`/`403`) means the credential is genuinely bad; anything else
64
+ * — a timeout, DNS, a proxy — means nothing was learned, and reporting that
65
+ * as a bad key would tell an operator on broken wifi to go and rotate a
66
+ * credential that is fine.
67
+ */
68
+ probeCredential?(): Promise<void>;
35
69
  healthCheck?(): Promise<boolean>;
36
70
  /**
37
71
  * Optional structured health probe used by `runDoctor()`.
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/types/provider/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAA;IAE5C;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CAAC,WAAW,CAAC,CAAA;IAEpE,UAAU,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IAEnC,WAAW,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAEhC;;;;;;;OAOG;IACH,WAAW,CAAC,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,eAAe,CAAC,CACf,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,OAAO,WAAW,EAAE,cAAc,GAC3C,SAAS,OAAO,WAAW,EAAE,eAAe,EAAE,CAAA;CACjD"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/types/provider/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAA;IAE5C;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CAAC,WAAW,CAAC,CAAA;IAEpE,UAAU,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,eAAe,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjC,WAAW,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAEhC;;;;;;;OAOG;IACH,WAAW,CAAC,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,eAAe,CAAC,CACf,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,OAAO,WAAW,EAAE,cAAc,GAC3C,SAAS,OAAO,WAAW,EAAE,eAAe,EAAE,CAAA;CACjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namzu/sdk",
3
- "version": "18.0.0",
3
+ "version": "18.1.0",
4
4
  "description": "Open-source AI agent SDK with a built-in runtime. Nothing between you and your agents.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "type": "module",
@@ -38,6 +38,41 @@ export interface LLMProvider {
38
38
 
39
39
  listModels?(): Promise<ModelInfo[]>
40
40
 
41
+ /**
42
+ * Establish whether this credential actually works. Resolves if it does,
43
+ * throws if it does not.
44
+ *
45
+ * Separate from `listModels` because the two answer different questions, and
46
+ * conflating them is a defect measured rather than imagined. `listModels`
47
+ * builds a MENU: "what can I offer this operator to choose from?" — a stale
48
+ * hardcoded list is a degraded but legitimate answer, since someone offline
49
+ * still has to pick a model. This builds a PROBE: "did this key work?" — and
50
+ * for that a list is not a degraded answer, it is no answer, because it
51
+ * arrives whether the key is right, wrong, expired or never sent.
52
+ *
53
+ * Two drivers proved a menu cannot stand in for a probe, and they failed
54
+ * differently. One caught a real `401` and returned its hardcoded catalogue,
55
+ * so the truth existed and was thrown away. The other has no fallback at all
56
+ * and is entirely honest about its menu — its listing endpoint simply does
57
+ * not authenticate, so ANY string returned the real catalogue. That second
58
+ * case is why this is a separate method rather than a rule about writing
59
+ * `listModels` more carefully: no amount of care in a menu makes it a probe.
60
+ *
61
+ * The probe is per-driver by nature — one has an authenticated call whose
62
+ * failure is real, another needs a different endpoint than its menu — so it
63
+ * is DECLARED, never inferred. A driver that does not implement this is
64
+ * reported as unverifiable, never as verified, and that has to hold for the
65
+ * driver nobody has written yet: inheriting a generic path silently is how
66
+ * this defect returns.
67
+ *
68
+ * Throw so the caller can tell the two failures apart. A rejection from the
69
+ * server (`401`/`403`) means the credential is genuinely bad; anything else
70
+ * — a timeout, DNS, a proxy — means nothing was learned, and reporting that
71
+ * as a bad key would tell an operator on broken wifi to go and rotate a
72
+ * credential that is fine.
73
+ */
74
+ probeCredential?(): Promise<void>
75
+
41
76
  healthCheck?(): Promise<boolean>
42
77
 
43
78
  /**