Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions packages/cli/src/commands/doctor-pi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,138 @@ 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(
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
"WARN Embedding provider: local — native runtime and WASM fallback both unavailable",
);
});

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-");
Expand Down
58 changes: 45 additions & 13 deletions packages/cli/src/commands/doctor-pi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,18 +288,35 @@ function packagesFrom(settings: Record<string, unknown>): unknown[] {
* <cwd>/.pi/npm/node_modules/<pkg> (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): <root>/node_modules/<pkg>.
Expand Down Expand Up @@ -781,6 +798,12 @@ 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;
let runtimeUnverifiedReason = "no installed plugin tree found to inspect";
for (const pluginDir of piPluginDirCandidates(packages, options.cwd)) {
const runtime = checkLocalEmbeddingRuntimeByResolution(
Expand Down Expand Up @@ -808,23 +831,32 @@ 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)) {
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;
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
}
if (runtime.state === "unknown") runtimeUnverifiedReason = runtime.reason;
}
if (!runtimeReported) {
add(
results,
"warn",
`Embedding provider ${loadedConfig.config.embedding.provider}: selected runtime unverified (${runtimeUnverifiedReason})`,
);
if (firstFallback) {
add(results, "warn", formatLocalEmbeddingRuntimeWasmFallback(firstFallback));
} else if (firstBroken) {
add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(firstBroken));
} else {
add(
results,
"warn",
`Embedding provider ${loadedConfig.config.embedding.provider}: selected runtime unverified (${runtimeUnverifiedReason})`,
);
}
}
}

Expand Down