Overhaul Feynman harness: streamline agents, prompts, and extensions
Remove legacy chains, skills, and config modules. Add citation agent, SYSTEM.md, modular research-tools extension, and web-access layer. Add ralph-wiggum to Pi package stack for long-running loops. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
import {
|
||||
@@ -18,7 +18,6 @@ export type PiRuntimeOptions = {
|
||||
explicitModelSpec?: string;
|
||||
oneShotPrompt?: string;
|
||||
initialPrompt?: string;
|
||||
systemPrompt: string;
|
||||
};
|
||||
|
||||
export function resolvePiPaths(appRoot: string) {
|
||||
@@ -27,8 +26,8 @@ export function resolvePiPaths(appRoot: string) {
|
||||
piCliPath: resolve(appRoot, "node_modules", "@mariozechner", "pi-coding-agent", "dist", "cli.js"),
|
||||
promisePolyfillPath: resolve(appRoot, "dist", "system", "promise-polyfill.js"),
|
||||
researchToolsPath: resolve(appRoot, "extensions", "research-tools.ts"),
|
||||
skillsPath: resolve(appRoot, "skills"),
|
||||
promptTemplatePath: resolve(appRoot, "prompts"),
|
||||
systemPromptPath: resolve(appRoot, ".pi", "SYSTEM.md"),
|
||||
piWorkspaceNodeModulesPath: resolve(appRoot, ".pi", "npm", "node_modules"),
|
||||
};
|
||||
}
|
||||
@@ -40,7 +39,6 @@ export function validatePiInstallation(appRoot: string): string[] {
|
||||
if (!existsSync(paths.piCliPath)) missing.push(paths.piCliPath);
|
||||
if (!existsSync(paths.promisePolyfillPath)) missing.push(paths.promisePolyfillPath);
|
||||
if (!existsSync(paths.researchToolsPath)) missing.push(paths.researchToolsPath);
|
||||
if (!existsSync(paths.skillsPath)) missing.push(paths.skillsPath);
|
||||
if (!existsSync(paths.promptTemplatePath)) missing.push(paths.promptTemplatePath);
|
||||
|
||||
return missing;
|
||||
@@ -53,14 +51,14 @@ export function buildPiArgs(options: PiRuntimeOptions): string[] {
|
||||
options.sessionDir,
|
||||
"--extension",
|
||||
paths.researchToolsPath,
|
||||
"--skill",
|
||||
paths.skillsPath,
|
||||
"--prompt-template",
|
||||
paths.promptTemplatePath,
|
||||
"--system-prompt",
|
||||
options.systemPrompt,
|
||||
];
|
||||
|
||||
if (existsSync(paths.systemPromptPath)) {
|
||||
args.push("--system-prompt", readFileSync(paths.systemPromptPath, "utf8"));
|
||||
}
|
||||
|
||||
if (options.explicitModelSpec) {
|
||||
args.push("--model", options.explicitModelSpec);
|
||||
}
|
||||
@@ -81,16 +79,13 @@ export function buildPiEnv(options: PiRuntimeOptions): NodeJS.ProcessEnv {
|
||||
|
||||
return {
|
||||
...process.env,
|
||||
PI_CODING_AGENT_DIR: options.feynmanAgentDir,
|
||||
FEYNMAN_CODING_AGENT_DIR: options.feynmanAgentDir,
|
||||
FEYNMAN_VERSION: options.feynmanVersion,
|
||||
FEYNMAN_PI_NPM_ROOT: paths.piWorkspaceNodeModulesPath,
|
||||
FEYNMAN_SESSION_DIR: options.sessionDir,
|
||||
PI_SESSION_DIR: options.sessionDir,
|
||||
FEYNMAN_MEMORY_DIR: resolve(dirname(options.feynmanAgentDir), "memory"),
|
||||
FEYNMAN_NODE_EXECUTABLE: process.execPath,
|
||||
FEYNMAN_BIN_PATH: resolve(options.appRoot, "bin", "feynman.js"),
|
||||
PANDOC_PATH: process.env.PANDOC_PATH ?? resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS),
|
||||
PI_HARDWARE_CURSOR: process.env.PI_HARDWARE_CURSOR ?? "1",
|
||||
PI_SKIP_VERSION_CHECK: process.env.PI_SKIP_VERSION_CHECK ?? "1",
|
||||
MERMAID_CLI_PATH: process.env.MERMAID_CLI_PATH ?? resolveExecutable("mmdc", MERMAID_FALLBACK_PATHS),
|
||||
PUPPETEER_EXECUTABLE_PATH:
|
||||
|
||||
109
src/pi/web-access.ts
Normal file
109
src/pi/web-access.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
export type PiWebSearchProvider = "auto" | "perplexity" | "gemini";
|
||||
|
||||
export type PiWebAccessConfig = Record<string, unknown> & {
|
||||
provider?: PiWebSearchProvider;
|
||||
searchProvider?: PiWebSearchProvider;
|
||||
perplexityApiKey?: string;
|
||||
geminiApiKey?: string;
|
||||
chromeProfile?: string;
|
||||
};
|
||||
|
||||
export type PiWebAccessStatus = {
|
||||
configPath: string;
|
||||
searchProvider: PiWebSearchProvider;
|
||||
requestProvider: PiWebSearchProvider;
|
||||
perplexityConfigured: boolean;
|
||||
geminiApiConfigured: boolean;
|
||||
chromeProfile?: string;
|
||||
routeLabel: string;
|
||||
note: string;
|
||||
};
|
||||
|
||||
export function getPiWebSearchConfigPath(home = process.env.HOME ?? homedir()): string {
|
||||
return resolve(home, ".pi", "web-search.json");
|
||||
}
|
||||
|
||||
function normalizeProvider(value: unknown): PiWebSearchProvider | undefined {
|
||||
return value === "auto" || value === "perplexity" || value === "gemini" ? value : undefined;
|
||||
}
|
||||
|
||||
function normalizeNonEmptyString(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
export function loadPiWebAccessConfig(configPath = getPiWebSearchConfigPath()): PiWebAccessConfig {
|
||||
if (!existsSync(configPath)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(readFileSync(configPath, "utf8")) as PiWebAccessConfig;
|
||||
return parsed && typeof parsed === "object" ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function formatRouteLabel(provider: PiWebSearchProvider): string {
|
||||
switch (provider) {
|
||||
case "perplexity":
|
||||
return "Perplexity";
|
||||
case "gemini":
|
||||
return "Gemini";
|
||||
default:
|
||||
return "Auto";
|
||||
}
|
||||
}
|
||||
|
||||
function formatRouteNote(provider: PiWebSearchProvider): string {
|
||||
switch (provider) {
|
||||
case "perplexity":
|
||||
return "Pi web-access will use Perplexity for search.";
|
||||
case "gemini":
|
||||
return "Pi web-access will use Gemini API or Gemini Browser.";
|
||||
default:
|
||||
return "Pi web-access will try Perplexity, then Gemini API, then Gemini Browser.";
|
||||
}
|
||||
}
|
||||
|
||||
export function getPiWebAccessStatus(
|
||||
config: PiWebAccessConfig = loadPiWebAccessConfig(),
|
||||
configPath = getPiWebSearchConfigPath(),
|
||||
): PiWebAccessStatus {
|
||||
const searchProvider = normalizeProvider(config.searchProvider) ?? "auto";
|
||||
const requestProvider = normalizeProvider(config.provider) ?? searchProvider;
|
||||
const perplexityConfigured = Boolean(normalizeNonEmptyString(config.perplexityApiKey));
|
||||
const geminiApiConfigured = Boolean(normalizeNonEmptyString(config.geminiApiKey));
|
||||
const chromeProfile = normalizeNonEmptyString(config.chromeProfile);
|
||||
const effectiveProvider = searchProvider;
|
||||
|
||||
return {
|
||||
configPath,
|
||||
searchProvider,
|
||||
requestProvider,
|
||||
perplexityConfigured,
|
||||
geminiApiConfigured,
|
||||
chromeProfile,
|
||||
routeLabel: formatRouteLabel(effectiveProvider),
|
||||
note: formatRouteNote(effectiveProvider),
|
||||
};
|
||||
}
|
||||
|
||||
export function formatPiWebAccessDoctorLines(
|
||||
status: PiWebAccessStatus = getPiWebAccessStatus(),
|
||||
): string[] {
|
||||
return [
|
||||
"web access: pi-web-access",
|
||||
` search route: ${status.routeLabel}`,
|
||||
` request route: ${status.requestProvider}`,
|
||||
` perplexity api: ${status.perplexityConfigured ? "configured" : "not configured"}`,
|
||||
` gemini api: ${status.geminiApiConfigured ? "configured" : "not configured"}`,
|
||||
` browser profile: ${status.chromeProfile ?? "default Chromium profile"}`,
|
||||
` config path: ${status.configPath}`,
|
||||
` note: ${status.note}`,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user