From 937d2bfb20df0c20a9ec2b2697d04246c04a4b27 Mon Sep 17 00:00:00 2001 From: Adnan Rashid Hussain Date: Sun, 30 Aug 2026 15:59:42 -0700 Subject: [PATCH 1/2] fix(sdk): classify a missing adapter by structured signal only --- sdks/typescript/src/providers/ai-sdk-provider.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sdks/typescript/src/providers/ai-sdk-provider.ts b/sdks/typescript/src/providers/ai-sdk-provider.ts index 15579b9c..2527c505 100644 --- a/sdks/typescript/src/providers/ai-sdk-provider.ts +++ b/sdks/typescript/src/providers/ai-sdk-provider.ts @@ -14,13 +14,14 @@ const NOT_INSTALLED = new Set(['ERR_MODULE_NOT_FOUND', 'MODULE_NOT_FOUND']); * not-installed case keeps the original as `cause` either way. */ function adapterImportError(error: unknown, vendor: string, pkg: string): unknown { - // Walked, not read off the top: bundlers and test loaders wrap an import failure in their - // own error and keep the real one as `cause`, so the resolution code is often one level down. + // Structured signal only, per SPEC §6.5: wording is not a contract, and matching on it + // would reclassify an adapter that failed to load for some other reason. Walked rather + // than read off the top, because bundlers and test loaders wrap an import failure in their + // own error and keep the real one as `cause`, so the code sits one level down. const missing = (function isMissing(e: unknown, depth = 0): boolean { if (e === null || typeof e !== 'object' || depth > 4) return false; - const { code, message, cause } = e as { code?: unknown; message?: unknown; cause?: unknown }; + const { code, cause } = e as { code?: unknown; cause?: unknown }; if (typeof code === 'string' && NOT_INSTALLED.has(code)) return true; - if (typeof message === 'string' && /cannot find (module|package)/i.test(message)) return true; return isMissing(cause, depth + 1); })(error); From 4c46c3e6c104e3857e7a8dd3de9e2b43763377af Mon Sep 17 00:00:00 2001 From: Adnan Rashid Hussain Date: Sun, 30 Aug 2026 16:03:20 -0700 Subject: [PATCH 2/2] docs(sdk): match the comment to the cause-chain walk --- sdks/typescript/src/providers/ai-sdk-provider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdks/typescript/src/providers/ai-sdk-provider.ts b/sdks/typescript/src/providers/ai-sdk-provider.ts index 2527c505..5bcbab3b 100644 --- a/sdks/typescript/src/providers/ai-sdk-provider.ts +++ b/sdks/typescript/src/providers/ai-sdk-provider.ts @@ -17,7 +17,8 @@ function adapterImportError(error: unknown, vendor: string, pkg: string): unknow // Structured signal only, per SPEC §6.5: wording is not a contract, and matching on it // would reclassify an adapter that failed to load for some other reason. Walked rather // than read off the top, because bundlers and test loaders wrap an import failure in their - // own error and keep the real one as `cause`, so the code sits one level down. + // own error and keep the real one as `cause` — sometimes through more than one layer, hence + // walking the chain rather than checking a fixed depth. const missing = (function isMissing(e: unknown, depth = 0): boolean { if (e === null || typeof e !== 'object' || depth > 4) return false; const { code, cause } = e as { code?: unknown; cause?: unknown };