fix startup packaging and content guardrails

This commit is contained in:
Advait Paliwal
2026-04-09 10:09:05 -07:00
parent 554350cc0e
commit 3148f2e62b
39 changed files with 518 additions and 43 deletions

View File

@@ -0,0 +1,110 @@
import test from "node:test";
import assert from "node:assert/strict";
import { buildModelStatusSnapshotFromRecords } from "../src/model/catalog.js";
test("buildModelStatusSnapshotFromRecords returns empty guidance when model is set and valid", () => {
const snapshot = buildModelStatusSnapshotFromRecords(
[{ provider: "anthropic", id: "claude-opus-4-6" }],
[{ provider: "anthropic", id: "claude-opus-4-6" }],
"anthropic/claude-opus-4-6",
);
assert.equal(snapshot.currentValid, true);
assert.equal(snapshot.current, "anthropic/claude-opus-4-6");
assert.equal(snapshot.guidance.length, 0);
});
test("buildModelStatusSnapshotFromRecords emits guidance when no models are available", () => {
const snapshot = buildModelStatusSnapshotFromRecords([], [], undefined);
assert.equal(snapshot.currentValid, false);
assert.equal(snapshot.current, undefined);
assert.equal(snapshot.recommended, undefined);
assert.ok(snapshot.guidance.some((line) => line.includes("No authenticated Pi models")));
});
test("buildModelStatusSnapshotFromRecords emits guidance when no default model is set", () => {
const snapshot = buildModelStatusSnapshotFromRecords(
[{ provider: "openai", id: "gpt-5.4" }],
[{ provider: "openai", id: "gpt-5.4" }],
undefined,
);
assert.equal(snapshot.currentValid, false);
assert.equal(snapshot.current, undefined);
assert.ok(snapshot.guidance.some((line) => line.includes("No default research model")));
});
test("buildModelStatusSnapshotFromRecords marks provider as configured only when it has available models", () => {
const snapshot = buildModelStatusSnapshotFromRecords(
[
{ provider: "anthropic", id: "claude-opus-4-6" },
{ provider: "openai", id: "gpt-5.4" },
],
[{ provider: "openai", id: "gpt-5.4" }],
"openai/gpt-5.4",
);
const anthropicProvider = snapshot.providers.find((provider) => provider.id === "anthropic");
const openaiProvider = snapshot.providers.find((provider) => provider.id === "openai");
assert.ok(anthropicProvider);
assert.equal(anthropicProvider!.configured, false);
assert.equal(anthropicProvider!.supportedModels, 1);
assert.equal(anthropicProvider!.availableModels, 0);
assert.ok(openaiProvider);
assert.equal(openaiProvider!.configured, true);
assert.equal(openaiProvider!.supportedModels, 1);
assert.equal(openaiProvider!.availableModels, 1);
});
test("buildModelStatusSnapshotFromRecords marks provider as current when selected model belongs to it", () => {
const snapshot = buildModelStatusSnapshotFromRecords(
[
{ provider: "anthropic", id: "claude-opus-4-6" },
{ provider: "openai", id: "gpt-5.4" },
],
[
{ provider: "anthropic", id: "claude-opus-4-6" },
{ provider: "openai", id: "gpt-5.4" },
],
"anthropic/claude-opus-4-6",
);
const anthropicProvider = snapshot.providers.find((provider) => provider.id === "anthropic");
const openaiProvider = snapshot.providers.find((provider) => provider.id === "openai");
assert.equal(anthropicProvider!.current, true);
assert.equal(openaiProvider!.current, false);
});
test("buildModelStatusSnapshotFromRecords returns available models sorted by research preference", () => {
const snapshot = buildModelStatusSnapshotFromRecords(
[
{ provider: "openai", id: "gpt-5.4" },
{ provider: "anthropic", id: "claude-opus-4-6" },
],
[
{ provider: "openai", id: "gpt-5.4" },
{ provider: "anthropic", id: "claude-opus-4-6" },
],
undefined,
);
assert.equal(snapshot.availableModels[0], "anthropic/claude-opus-4-6");
assert.equal(snapshot.availableModels[1], "openai/gpt-5.4");
assert.equal(snapshot.recommended, "anthropic/claude-opus-4-6");
});
test("buildModelStatusSnapshotFromRecords sets currentValid false when current model is not in available list", () => {
const snapshot = buildModelStatusSnapshotFromRecords(
[{ provider: "anthropic", id: "claude-opus-4-6" }],
[],
"anthropic/claude-opus-4-6",
);
assert.equal(snapshot.currentValid, false);
assert.equal(snapshot.current, "anthropic/claude-opus-4-6");
});

View File

@@ -0,0 +1,92 @@
import test from "node:test";
import assert from "node:assert/strict";
import { existsSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import {
ensureFeynmanHome,
getBootstrapStatePath,
getDefaultSessionDir,
getFeynmanAgentDir,
getFeynmanHome,
getFeynmanMemoryDir,
getFeynmanStateDir,
} from "../src/config/paths.js";
test("getFeynmanHome uses FEYNMAN_HOME env var when set", () => {
const previous = process.env.FEYNMAN_HOME;
try {
process.env.FEYNMAN_HOME = "/custom/home";
assert.equal(getFeynmanHome(), resolve("/custom/home", ".feynman"));
} finally {
if (previous === undefined) {
delete process.env.FEYNMAN_HOME;
} else {
process.env.FEYNMAN_HOME = previous;
}
}
});
test("getFeynmanHome falls back to homedir when FEYNMAN_HOME is unset", () => {
const previous = process.env.FEYNMAN_HOME;
try {
delete process.env.FEYNMAN_HOME;
const home = getFeynmanHome();
assert.ok(home.endsWith(".feynman"), `expected path ending in .feynman, got: ${home}`);
assert.ok(!home.includes("undefined"), `expected no 'undefined' in path, got: ${home}`);
} finally {
if (previous === undefined) {
delete process.env.FEYNMAN_HOME;
} else {
process.env.FEYNMAN_HOME = previous;
}
}
});
test("getFeynmanAgentDir resolves to <home>/agent", () => {
assert.equal(getFeynmanAgentDir("/some/home"), resolve("/some/home", "agent"));
});
test("getFeynmanMemoryDir resolves to <home>/memory", () => {
assert.equal(getFeynmanMemoryDir("/some/home"), resolve("/some/home", "memory"));
});
test("getFeynmanStateDir resolves to <home>/.state", () => {
assert.equal(getFeynmanStateDir("/some/home"), resolve("/some/home", ".state"));
});
test("getDefaultSessionDir resolves to <home>/sessions", () => {
assert.equal(getDefaultSessionDir("/some/home"), resolve("/some/home", "sessions"));
});
test("getBootstrapStatePath resolves to <home>/.state/bootstrap.json", () => {
assert.equal(getBootstrapStatePath("/some/home"), resolve("/some/home", ".state", "bootstrap.json"));
});
test("ensureFeynmanHome creates all required subdirectories", () => {
const root = mkdtempSync(join(tmpdir(), "feynman-paths-"));
try {
const home = join(root, "home");
ensureFeynmanHome(home);
assert.ok(existsSync(home), "home dir should exist");
assert.ok(existsSync(join(home, "agent")), "agent dir should exist");
assert.ok(existsSync(join(home, "memory")), "memory dir should exist");
assert.ok(existsSync(join(home, ".state")), ".state dir should exist");
assert.ok(existsSync(join(home, "sessions")), "sessions dir should exist");
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("ensureFeynmanHome is idempotent when dirs already exist", () => {
const root = mkdtempSync(join(tmpdir(), "feynman-paths-"));
try {
const home = join(root, "home");
ensureFeynmanHome(home);
assert.doesNotThrow(() => ensureFeynmanHome(home));
} finally {
rmSync(root, { recursive: true, force: true });
}
});

View File

@@ -0,0 +1,32 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readdirSync, readFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const bannedPatterns = [/ValiChord/i, /Harmony Record/i, /harmony_record_/i];
function collectMarkdownFiles(root: string): string[] {
const files: string[] = [];
for (const entry of readdirSync(root, { withFileTypes: true })) {
const fullPath = join(root, entry.name);
if (entry.isDirectory()) {
files.push(...collectMarkdownFiles(fullPath));
continue;
}
if (entry.isFile() && fullPath.endsWith(".md")) {
files.push(fullPath);
}
}
return files;
}
test("bundled prompts and skills do not contain blocked promotional product content", () => {
for (const filePath of [...collectMarkdownFiles(join(repoRoot, "prompts")), ...collectMarkdownFiles(join(repoRoot, "skills"))]) {
const content = readFileSync(filePath, "utf8");
for (const pattern of bannedPatterns) {
assert.doesNotMatch(content, pattern, `${filePath} contains blocked promotional pattern ${pattern}`);
}
}
});

View File

@@ -1,7 +1,8 @@
import test from "node:test";
import assert from "node:assert/strict";
import { pathToFileURL } from "node:url";
import { applyFeynmanPackageManagerEnv, buildPiArgs, buildPiEnv, resolvePiPaths } from "../src/pi/runtime.js";
import { applyFeynmanPackageManagerEnv, buildPiArgs, buildPiEnv, resolvePiPaths, toNodeImportSpecifier } from "../src/pi/runtime.js";
test("buildPiArgs includes configured runtime paths and prompt", () => {
const args = buildPiArgs({
@@ -106,3 +107,11 @@ test("resolvePiPaths includes the Promise.withResolvers polyfill path", () => {
assert.equal(paths.promisePolyfillPath, "/repo/feynman/dist/system/promise-polyfill.js");
});
test("toNodeImportSpecifier converts absolute preload paths to file URLs", () => {
assert.equal(
toNodeImportSpecifier("/repo/feynman/dist/system/promise-polyfill.js"),
pathToFileURL("/repo/feynman/dist/system/promise-polyfill.js").href,
);
assert.equal(toNodeImportSpecifier("tsx"), "tsx");
});

View File

@@ -0,0 +1,48 @@
import test from "node:test";
import assert from "node:assert/strict";
import { patchPiWebAccessSource } from "../scripts/lib/pi-web-access-patch.mjs";
test("patchPiWebAccessSource rewrites legacy Pi web-search config paths", () => {
const input = [
'import { join } from "node:path";',
'import { homedir } from "node:os";',
'const CONFIG_PATH = join(homedir(), ".pi", "web-search.json");',
"",
].join("\n");
const patched = patchPiWebAccessSource("perplexity.ts", input);
assert.match(patched, /FEYNMAN_WEB_SEARCH_CONFIG/);
assert.match(patched, /PI_WEB_SEARCH_CONFIG/);
});
test("patchPiWebAccessSource updates index.ts directory handling", () => {
const input = [
'import { existsSync, mkdirSync } from "node:fs";',
'import { join } from "node:path";',
'import { homedir } from "node:os";',
'const WEB_SEARCH_CONFIG_PATH = join(homedir(), ".pi", "web-search.json");',
'const dir = join(homedir(), ".pi");',
"",
].join("\n");
const patched = patchPiWebAccessSource("index.ts", input);
assert.match(patched, /import \{ dirname, join \} from "node:path";/);
assert.match(patched, /const dir = dirname\(WEB_SEARCH_CONFIG_PATH\);/);
});
test("patchPiWebAccessSource is idempotent", () => {
const input = [
'import { join } from "node:path";',
'import { homedir } from "node:os";',
'const CONFIG_PATH = join(homedir(), ".pi", "web-search.json");',
"",
].join("\n");
const once = patchPiWebAccessSource("perplexity.ts", input);
const twice = patchPiWebAccessSource("perplexity.ts", once);
assert.equal(twice, once);
});

View File

@@ -67,6 +67,17 @@ test("getPiWebAccessStatus reads Gemini routes directly", () => {
assert.equal(status.chromeProfile, "Profile 2");
});
test("getPiWebAccessStatus supports the legacy route key", () => {
const status = getPiWebAccessStatus({
route: "perplexity",
perplexityApiKey: "pplx_...",
});
assert.equal(status.routeLabel, "Perplexity");
assert.equal(status.requestProvider, "perplexity");
assert.equal(status.perplexityConfigured, true);
});
test("formatPiWebAccessDoctorLines reports Pi-managed web access", () => {
const lines = formatPiWebAccessDoctorLines(
getPiWebAccessStatus({

28
tests/skill-paths.test.ts Normal file
View File

@@ -0,0 +1,28 @@
import test from "node:test";
import assert from "node:assert/strict";
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const skillsRoot = join(repoRoot, "skills");
const markdownPathPattern = /`((?:\.\.?\/)(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+\.md)`/g;
const simulatedInstallRoot = join(repoRoot, "__skill-install-root__");
test("all local markdown references in bundled skills resolve in the installed skill layout", () => {
for (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const skillPath = join(skillsRoot, entry.name, "SKILL.md");
if (!existsSync(skillPath)) continue;
const content = readFileSync(skillPath, "utf8");
for (const match of content.matchAll(markdownPathPattern)) {
const reference = match[1];
const installedSkillDir = join(simulatedInstallRoot, entry.name);
const installedTarget = resolve(installedSkillDir, reference);
const repoTarget = installedTarget.replace(simulatedInstallRoot, repoRoot);
assert.ok(existsSync(repoTarget), `${skillPath} references missing installed markdown file ${reference}`);
}
}
});