Key resolution cannot express the configured judgment backend
Summary
pi-typesafe ships multiple judgment backends — DECISIONS_BACKENDS (client.js:11-14) maps
each to its own keyEnv, and createTypeSafe consumes it at :93 and :96-110. Requests
work. But the key reporting and login surfaces cannot express a backend, so with
typesafeBackend: "openrouter" they report the wrong state and verify against the wrong host.
Filed as the library-side half of a defect observed through pi-warden, which carries the
end-to-end reproduction.
Versions
| Package |
Version |
Published |
| pi-typesafe |
0.6.2 (latest) |
2026-09-22T05:54:44Z |
| pi-warden |
0.38.3 |
2026-09-22T03:39:04Z |
| Node |
v26.7.0 |
— |
Line numbers are from 0.6.2.
What works and what does not
| Function |
Knows the backend? |
createTypeSafe(options) — client.js:88, client.d.ts:17 |
yes (backend?) |
keySituation() — credentials.js:50 / .d.ts:32 |
no — takes no arguments |
authState(options) — auth.js:53 / .d.ts:37-39 |
no — options carries only path |
describeAuth(state) — auth.js:98 / .d.ts:56 |
no |
ensureApiKey(ctx) — login.js:30 / .d.ts:27 |
no |
Observed with OPENROUTER_API_KEY set and TYPESAFE_API_KEY unset:
keySituation() // { kind: "missing" }
authState() // { kind: "missing", usable: false, keyName: "no key" }
describeAuth().text // "TypeSafe key: missing — every Jev judgment is skipped …"
Judgments in fact run in that same process.
Note for anyone reproducing: do not check arity with fn.length — default parameters make
keySituation.length, authState.length and describeAuth.length all report 0. Read the
.d.ts.
The four relevant dist files are byte-identical between 0.6.1 and 0.6.2 (sha256 edd7057a…
credentials.js, 78f3986f… auth.js, 8580cd21… login.js, 9c9170d8… client.js). The
0.6.2 changes in extension.js and schema.js are unrelated to this path.
Scope of the fix
Threading the backend into the four signatures is necessary but not sufficient. Three more
places are backend-hardcoded or unpartitioned:
loginWithPrompt — login.js:22 calls createTypeSafe({ apiKey: key }).listModels()
with no backend, so client.js:85 defaults baseURL to https://api.typesafe.ai. An
OpenRouter key is verified against the wrong service. Threading ensureApiKey without this
leaves the login flow broken.
- Labels and copy —
keySourceLabel (credentials.js:67) returns the literal
"TYPESAFE_API_KEY" / "/typesafe login", surfaced as keyName at auth.js:65;
credentials.js:21 and auth.js:98-104 name console.typesafe.ai and /typesafe login.
- Storage —
storeApiKey (credentials.js:87) writes a single auth.json at
credentialsPath(), reported at auth.js:63. A backend-aware environment variable over a
single unpartitioned store is an inconsistent model.
resolveApiKey (credentials.js:81) is a second frozen public entry point and needs the same
treatment.
Two notes for the maintainer
The capability is shipped but undocumented. client.js:11-14 implements openrouter with
keyEnv: "OPENROUTER_API_KEY" and path: "/api/alpha/decisions", while README.md contains no
mention of "openrouter" or "backend". A reader cannot discover that the backend exists, or that
it changes which credential is required.
Part of this may be intended. auth.d.ts:26 defines verified as "accepted by
api.typesafe.ai", and credentials.d.ts:29-31 scopes keySituation to the environment, the
login store, and file permissions. Under that reading pi-typesafe reports its own credential
by design, and the only backend-aware surface is createTypeSafe. If so, this is a
documentation gap rather than a defect — worth stating either way, because consumers currently
cannot tell which surfaces are backend-aware.
Unverified
Whether listModels() succeeds against OpenRouter once the backend is forwarded.
DECISIONS_BACKENDS.openrouter carries no models endpoint, and I did not make an authenticated
request. If it fails, the login flow needs a backend-specific verification path rather than just
a forwarded argument.
Key resolution cannot express the configured judgment backend
Summary
pi-typesafeships multiple judgment backends —DECISIONS_BACKENDS(client.js:11-14) mapseach to its own
keyEnv, andcreateTypeSafeconsumes it at:93and:96-110. Requestswork. But the key reporting and login surfaces cannot express a backend, so with
typesafeBackend: "openrouter"they report the wrong state and verify against the wrong host.Filed as the library-side half of a defect observed through
pi-warden, which carries theend-to-end reproduction.
Versions
Line numbers are from 0.6.2.
What works and what does not
createTypeSafe(options)—client.js:88,client.d.ts:17backend?)keySituation()—credentials.js:50/.d.ts:32authState(options)—auth.js:53/.d.ts:37-39optionscarries onlypathdescribeAuth(state)—auth.js:98/.d.ts:56ensureApiKey(ctx)—login.js:30/.d.ts:27Observed with
OPENROUTER_API_KEYset andTYPESAFE_API_KEYunset:Judgments in fact run in that same process.
Note for anyone reproducing: do not check arity with
fn.length— default parameters makekeySituation.length,authState.lengthanddescribeAuth.lengthall report0. Read the.d.ts.The four relevant dist files are byte-identical between 0.6.1 and 0.6.2 (sha256
edd7057a…credentials.js,78f3986f…auth.js,8580cd21…login.js,9c9170d8…client.js). The0.6.2 changes in
extension.jsandschema.jsare unrelated to this path.Scope of the fix
Threading the backend into the four signatures is necessary but not sufficient. Three more
places are backend-hardcoded or unpartitioned:
loginWithPrompt—login.js:22callscreateTypeSafe({ apiKey: key }).listModels()with no backend, so
client.js:85defaultsbaseURLtohttps://api.typesafe.ai. AnOpenRouter key is verified against the wrong service. Threading
ensureApiKeywithout thisleaves the login flow broken.
keySourceLabel(credentials.js:67) returns the literal"TYPESAFE_API_KEY"/"/typesafe login", surfaced askeyNameatauth.js:65;credentials.js:21andauth.js:98-104nameconsole.typesafe.aiand/typesafe login.storeApiKey(credentials.js:87) writes a singleauth.jsonatcredentialsPath(), reported atauth.js:63. A backend-aware environment variable over asingle unpartitioned store is an inconsistent model.
resolveApiKey(credentials.js:81) is a second frozen public entry point and needs the sametreatment.
Two notes for the maintainer
The capability is shipped but undocumented.
client.js:11-14implementsopenrouterwithkeyEnv: "OPENROUTER_API_KEY"andpath: "/api/alpha/decisions", whileREADME.mdcontains nomention of "openrouter" or "backend". A reader cannot discover that the backend exists, or that
it changes which credential is required.
Part of this may be intended.
auth.d.ts:26definesverifiedas "accepted byapi.typesafe.ai", and
credentials.d.ts:29-31scopeskeySituationto the environment, thelogin store, and file permissions. Under that reading
pi-typesafereports its own credentialby design, and the only backend-aware surface is
createTypeSafe. If so, this is adocumentation gap rather than a defect — worth stating either way, because consumers currently
cannot tell which surfaces are backend-aware.
Unverified
Whether
listModels()succeeds against OpenRouter once the backend is forwarded.DECISIONS_BACKENDS.openroutercarries no models endpoint, and I did not make an authenticatedrequest. If it fails, the login flow needs a backend-specific verification path rather than just
a forwarded argument.