From 5c42e23305bf5e04075c9a7845f4d51cce8a530b Mon Sep 17 00:00:00 2001 From: Li JiangHeng <1794551825@qq.com> Date: Thu, 3 Sep 2026 15:09:34 +0800 Subject: [PATCH 1/2] fix(cli/doctor-pi): skip unrelated local packages when probing embedding runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit piPluginDirCandidates treated every non-npm: entry in Pi packages[] as a candidate plugin tree, so local dev-path extensions (any package.json, regardless of name) were probed for the embedding runtime. The first broken candidate made doctor report 'native runtime and WASM fallback both unavailable' and stop, even when the real magic-context install was healthy. Now only directories whose package.json names @cortexkit/pi-magic-context qualify as candidates, and broken candidates no longer abort the scan — a stale local dev tree cannot mask a healthy managed install. Repro: register any local-path Pi extension (D:\repo\my-extension) in settings.json packages[], run 'doctor --harness pi' — doctor blamed the extension's package.json for missing onnxruntime-web deps instead of reporting the actual plugin install. --- packages/cli/src/commands/doctor-pi.test.ts | 45 +++++++++++++++++++++ packages/cli/src/commands/doctor-pi.ts | 45 ++++++++++++++++----- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/doctor-pi.test.ts b/packages/cli/src/commands/doctor-pi.test.ts index 06006700b..5d2cd35c1 100644 --- a/packages/cli/src/commands/doctor-pi.test.ts +++ b/packages/cli/src/commands/doctor-pi.test.ts @@ -346,6 +346,51 @@ describe("Pi doctor", () => { expect(output).toContain("WARN 2"); }); + it("skips unrelated local dev-path packages and broken trees when probing the embedding runtime", async () => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + + // Unrelated local extension: has a package.json but is NOT the + // magic-context plugin. Must not be probed as an embedding candidate. + const unrelatedPlugin = makeTempRoot("mc-pi-doctor-unrelated-"); + writeFileSync( + join(unrelatedPlugin, "package.json"), + JSON.stringify({ name: "pi-tree-git-checkpoint", version: "0.0.0" }), + ); + // Local dev tree of the actual plugin that is missing all embedding deps. + const brokenDevTree = makeTempRoot("mc-pi-doctor-dev-"); + writeFileSync( + join(brokenDevTree, "package.json"), + JSON.stringify({ name: "@cortexkit/pi-magic-context", version: "0.0.0-dev" }), + ); + + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ + packages: [ + "npm:@cortexkit/pi-magic-context", + unrelatedPlugin, + brokenDevTree, + ], + }), + ); + createInstalledPiPlugin(agentDir, true); + const prompts = new MockPrompts(); + + const code = await runDoctor(baseOptions(root, cwd, prompts)); + + expect(code).toBe(0); + const output = prompts.messages.join("\n"); + expect(output).toContain( + "PASS Embedding provider: local (native runtime selected and OK)", + ); + expect(output).not.toContain( + "WARN Embedding provider: local — native runtime and WASM fallback both unavailable", + ); + }); + it("reports the WASM fallback when onnxruntime-node is completely absent", async () => { const root = makeTempRoot(); const cwd = makeTempRoot("mc-pi-doctor-cwd-"); diff --git a/packages/cli/src/commands/doctor-pi.ts b/packages/cli/src/commands/doctor-pi.ts index 5f77c823a..743d8f0c0 100644 --- a/packages/cli/src/commands/doctor-pi.ts +++ b/packages/cli/src/commands/doctor-pi.ts @@ -288,18 +288,35 @@ function packagesFrom(settings: Record): unknown[] { * /.pi/npm/node_modules/ (project). We collect every plausible dir * with a package.json; the resolver stays SILENT for any that don't exist. */ +/** True when the directory's package.json declares the magic-context Pi plugin. */ +function isPiMagicContextPackageDir(dir: string): boolean { + const packageJson = join(dir, "package.json"); + if (!existsSync(packageJson)) return false; + try { + const pkg = JSON.parse(readFileSync(packageJson, "utf-8")) as { + name?: unknown; + }; + return typeof pkg.name === "string" && pkg.name === PACKAGE_NAME; + } catch { + return false; + } +} + function piPluginDirCandidates(packages: unknown[], cwd: string): string[] { const dirs: string[] = []; const agentDir = getPiAgentConfigDir(); // Local dev-path entries: a string spec that is NOT an npm: specifier and // resolves to a directory on disk. Relative entries are resolved against the - // Pi agent dir (Pi's settings.packages base). + // Pi agent dir (Pi's settings.packages base). Only directories whose + // package.json names the magic-context plugin itself are candidates — other + // local extensions registered in packages[] must not be probed for the + // embedding runtime. for (const entry of packages) { const spec = typeof entry === "string" ? entry.trim() : ""; if (!spec || spec.startsWith("npm:")) continue; const resolved = isAbsolute(spec) ? spec : join(agentDir, spec); - dirs.push(resolved); + if (isPiMagicContextPackageDir(resolved)) dirs.push(resolved); } // Managed npm install roots (hoisted): /node_modules/. @@ -781,6 +798,9 @@ async function runHealthChecks(options: { // persistence-capable Node WASM fallback. Resolution starts from the // installed plugin dir and stays silent when no tree can be inspected. let runtimeReported = false; + let firstBroken: ReturnType< + typeof checkLocalEmbeddingRuntimeByResolution + > | null = null; let runtimeUnverifiedReason = "no installed plugin tree found to inspect"; for (const pluginDir of piPluginDirCandidates(packages, options.cwd)) { const runtime = checkLocalEmbeddingRuntimeByResolution( @@ -813,18 +833,23 @@ async function runHealthChecks(options: { break; } if (isLocalEmbeddingRuntimeBroken(runtime)) { - add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(runtime)); - runtimeReported = true; - break; + // Keep probing: an earlier broken candidate (e.g. a stale local + // dev-path tree) must not mask a healthy managed install. + firstBroken ??= runtime; + continue; } if (runtime.state === "unknown") runtimeUnverifiedReason = runtime.reason; } if (!runtimeReported) { - add( - results, - "warn", - `Embedding provider ${loadedConfig.config.embedding.provider}: selected runtime unverified (${runtimeUnverifiedReason})`, - ); + if (firstBroken) { + add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(firstBroken)); + } else { + add( + results, + "warn", + `Embedding provider ${loadedConfig.config.embedding.provider}: selected runtime unverified (${runtimeUnverifiedReason})`, + ); + } } } From 296e340ecee6934a62322ee44880f5bcb86a67c5 Mon Sep 17 00:00:00 2001 From: Li JiangHeng <1794551825@qq.com> Date: Thu, 3 Sep 2026 15:38:06 +0800 Subject: [PATCH 2/2] fix(cli/doctor-pi): don't let a WASM fallback mask a native-capable install Address review findings (Greptile P1, cubic-dev-ai P2): the loop still stopped at the first candidate with a working WASM fallback, reporting a degraded runtime even when a later managed install had the native binding. Record the best degraded candidate and keep probing; only report the fallback WARN when no candidate is fully OK. Tests: add regression coverage for (1) unrelated local packages never probed (unverified, not a broken-runtime WARN, when only unrelated packages are registered), and (2) a WASM-only dev tree not masking a later native-capable install. --- packages/cli/src/commands/doctor-pi.test.ts | 87 +++++++++++++++++++++ packages/cli/src/commands/doctor-pi.ts | 15 +++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/doctor-pi.test.ts b/packages/cli/src/commands/doctor-pi.test.ts index 5d2cd35c1..5844e32db 100644 --- a/packages/cli/src/commands/doctor-pi.test.ts +++ b/packages/cli/src/commands/doctor-pi.test.ts @@ -391,6 +391,93 @@ describe("Pi doctor", () => { ); }); + it("prefers a later native-capable install over an earlier WASM fallback", async () => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + + // Local dev tree of the actual plugin with only a WASM fallback + // (no native binding) — probing it alone would report a degraded + // runtime. + const wasmDevTree = makeTempRoot("mc-pi-doctor-wasm-dev-"); + mkdirSync(join(wasmDevTree, "node_modules", "onnxruntime-web"), { + recursive: true, + }); + writeFileSync( + join(wasmDevTree, "node_modules", "onnxruntime-web", "package.json"), + JSON.stringify({ name: "onnxruntime-web", main: "index.js" }), + ); + writeFileSync( + join(wasmDevTree, "node_modules", "onnxruntime-web", "index.js"), + "module.exports = {};\n", + ); + mkdirSync(join(wasmDevTree, "dist"), { recursive: true }); + writeFileSync( + join(wasmDevTree, "dist", "transformers-node-wasm.js"), + "export {};\n", + ); + writeFileSync( + join(wasmDevTree, "package.json"), + JSON.stringify({ name: "@cortexkit/pi-magic-context", version: "0.0.0-dev" }), + ); + + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ + packages: ["npm:@cortexkit/pi-magic-context", wasmDevTree], + }), + ); + createInstalledPiPlugin(agentDir, true); + const prompts = new MockPrompts(); + + const code = await runDoctor(baseOptions(root, cwd, prompts)); + + expect(code).toBe(0); + const output = prompts.messages.join("\n"); + expect(output).toContain( + "PASS Embedding provider: local (native runtime selected and OK)", + ); + expect(output).not.toContain( + "WARN Embedding provider: local — onnxruntime-node native binding failed", + ); + }); + + it("reports unverified, not a broken-runtime WARN, when only unrelated local packages are registered", async () => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + + // Only an unrelated local extension is registered; the magic-context + // managed install tree is absent. The unrelated package must not be + // probed as an embedding candidate, so doctor reports unverified + // instead of blaming it for a missing onnxruntime. + const unrelatedPlugin = makeTempRoot("mc-pi-doctor-unrelated-"); + writeFileSync( + join(unrelatedPlugin, "package.json"), + JSON.stringify({ name: "pi-tree-git-checkpoint", version: "0.0.0" }), + ); + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ + packages: ["npm:@cortexkit/pi-magic-context", unrelatedPlugin], + }), + ); + const prompts = new MockPrompts(); + + const code = await runDoctor(baseOptions(root, cwd, prompts)); + + expect(code).toBe(0); + const output = prompts.messages.join("\n"); + expect(output).toContain( + "selected runtime unverified (no installed plugin tree found to inspect)", + ); + expect(output).not.toContain( + "WARN Embedding provider: local — native runtime and WASM fallback both unavailable", + ); + }); + it("reports the WASM fallback when onnxruntime-node is completely absent", async () => { const root = makeTempRoot(); const cwd = makeTempRoot("mc-pi-doctor-cwd-"); diff --git a/packages/cli/src/commands/doctor-pi.ts b/packages/cli/src/commands/doctor-pi.ts index 743d8f0c0..79084f9dc 100644 --- a/packages/cli/src/commands/doctor-pi.ts +++ b/packages/cli/src/commands/doctor-pi.ts @@ -798,6 +798,9 @@ async function runHealthChecks(options: { // persistence-capable Node WASM fallback. Resolution starts from the // installed plugin dir and stays silent when no tree can be inspected. let runtimeReported = false; + let firstFallback: ReturnType< + typeof checkLocalEmbeddingRuntimeByResolution + > | null = null; let firstBroken: ReturnType< typeof checkLocalEmbeddingRuntimeByResolution > | null = null; @@ -828,9 +831,11 @@ async function runHealthChecks(options: { break; } if (runtime.state === "wasm-fallback") { - add(results, "warn", formatLocalEmbeddingRuntimeWasmFallback(runtime)); - runtimeReported = true; - break; + // Remember the best degraded candidate but keep probing: a WASM + // fallback in an earlier tree must not mask a later native-capable + // install. + firstFallback ??= runtime; + continue; } if (isLocalEmbeddingRuntimeBroken(runtime)) { // Keep probing: an earlier broken candidate (e.g. a stale local @@ -841,7 +846,9 @@ async function runHealthChecks(options: { if (runtime.state === "unknown") runtimeUnverifiedReason = runtime.reason; } if (!runtimeReported) { - if (firstBroken) { + if (firstFallback) { + add(results, "warn", formatLocalEmbeddingRuntimeWasmFallback(firstFallback)); + } else if (firstBroken) { add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(firstBroken)); } else { add(