From 9b94d3492c4d184ad41afb4ab11d8994902b5641 Mon Sep 17 00:00:00 2001 From: Simon Davies Date: Thu, 7 May 2026 13:39:53 +0100 Subject: [PATCH 1/2] fix: native module resolution breaks validation loop for ha:pdf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module resolution loop checked only Object.keys(newSources) to decide whether to continue resolving. Native modules (like ha:ziplib) have no .js source — only module.json metadata — so the loop broke early with valid=false and an empty errors array, producing the cryptic 'Validation failed: •' message. Root cause: ha:pdf imports ha:ziplib (native). The resolution loop loaded ziplib.json but not ziplib.js (doesn't exist). Since newSources was empty, it broke out of the loop before passing the JSON metadata to the validator. The validator then couldn't confirm ziplib was resolved, returning valid=false with no errors. Fix: check all three sources (newSources, newModuleJsons, newDtsSources) before deciding nothing was found. This allows native module metadata to flow through and lets the validator recognise them as resolved. Applied to both register_handler and register_module validation loops. Signed-off-by: Simon Davies --- src/agent/index.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/agent/index.ts b/src/agent/index.ts index c23f028..c228337 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -1220,7 +1220,13 @@ async function validateHandlerCode( moduleJsons: newModuleJsons, } = loadModuleFilesForValidator(validation.missingSources, pluginManager); - if (Object.keys(newSources).length === 0) { + if ( + Object.keys(newSources).length === 0 && + Object.keys(newModuleJsons).length === 0 && + Object.keys(newDtsSources).length === 0 + ) { + // No new sources, module metadata, or type definitions found. + // Check if any specifiers are bare (no ha:/host: prefix). const unresolvable = validation.missingSources.filter( (s) => !s.startsWith("ha:") && !s.startsWith("host:"), ); @@ -1401,7 +1407,12 @@ const registerHandlerTool = defineTool("register_handler", { // If we couldn't resolve ANY of the missing sources, generate helpful errors // This happens when imports use wrong prefixes (e.g., "pptx" instead of "ha:pptx") - if (Object.keys(newSources).length === 0) { + // Native modules have module.json but no .js source — check all three. + if ( + Object.keys(newSources).length === 0 && + Object.keys(newModuleJsons).length === 0 && + Object.keys(newDtsSources).length === 0 + ) { const unresolvable = validation.missingSources.filter( (s) => !s.startsWith("ha:") && !s.startsWith("host:"), ); From 44bbe0adba0eda4872782b0f38c5d9d0c4c93763 Mon Sep 17 00:00:00 2001 From: Simon Davies Date: Thu, 7 May 2026 13:50:00 +0100 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20address=20PR=20#111=20review=20feedb?= =?UTF-8?q?ack=20=E2=80=94=20clarify=20comment=20wording?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed 'no ha:/host: prefix' to 'missing ha: or host: prefix' to avoid confusion with the slash character - Note: registerModuleImpl loop doesn't have the early-break pattern so it was never affected by the bug (commit message corrected) Signed-off-by: Simon Davies --- src/agent/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agent/index.ts b/src/agent/index.ts index c228337..5249421 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -1226,7 +1226,7 @@ async function validateHandlerCode( Object.keys(newDtsSources).length === 0 ) { // No new sources, module metadata, or type definitions found. - // Check if any specifiers are bare (no ha:/host: prefix). + // Check if any specifiers are missing a ha: or host: prefix. const unresolvable = validation.missingSources.filter( (s) => !s.startsWith("ha:") && !s.startsWith("host:"), );