* Bundled agent definitions.
*
* Agents are embedded at build time via Bun's import with { type: "text" }.
*/
import { Effort } from "@oh-my-pi/pi-ai";
import { parseFrontmatter, prompt } from "@oh-my-pi/pi-utils";
import { parseAgentFields } from "../discovery/helpers";
import designerMd from "../prompts/agents/designer.md" with { type: "text" };
import exploreMd from "../prompts/agents/explore.md" with { type: "text" };
import agentFrontmatterTemplate from "../prompts/agents/frontmatter.md" with { type: "text" };
import librarianMd from "../prompts/agents/librarian.md" with { type: "text" };
import oracleMd from "../prompts/agents/oracle.md" with { type: "text" };
import planMd from "../prompts/agents/plan.md" with { type: "text" };
import reviewerMd from "../prompts/agents/reviewer.md" with { type: "text" };
import taskMd from "../prompts/agents/task.md" with { type: "text" };
import type { AgentDefinition, AgentSource } from "./types";
interface AgentFrontmatter {
name: string;
description: string;
tools?: string[];
spawns?: string;
model?: string | string[];
thinkingLevel?: string;
blocking?: boolean;
}
interface EmbeddedAgentDef {
fileName: string;
frontmatter?: AgentFrontmatter;
template: string;
}
function buildAgentContent(def: EmbeddedAgentDef): string {
const body = prompt.render(def.template);
if (!def.frontmatter) return body;
return prompt.render(agentFrontmatterTemplate, { ...def.frontmatter, body });
}
const EMBEDDED_AGENT_DEFS: EmbeddedAgentDef[] = [
{ fileName: "explore.md", template: exploreMd },
{ fileName: "plan.md", template: planMd },
{ fileName: "designer.md", template: designerMd },
{ fileName: "reviewer.md", template: reviewerMd },
{ fileName: "librarian.md", template: librarianMd },
{ fileName: "oracle.md", template: oracleMd },
{
fileName: "task.md",
frontmatter: {
name: "task",
description: "General-purpose subagent with full capabilities for delegated multi-step tasks",
spawns: "*",
model: "pi/task",
thinkingLevel: Effort.Medium,
},
template: taskMd,
},
{
fileName: "quick_task.md",
frontmatter: {
name: "quick_task",
description: "Low-reasoning agent for strictly mechanical updates or data collection only",
model: "pi/smol",
thinkingLevel: Effort.Minimal,
},
template: taskMd,
},
];
export class AgentParsingError extends Error {
constructor(
error: Error,
readonly source?: unknown,
) {
super(`Failed to parse agent: ${error.message}`, { cause: error });
this.name = "AgentParsingError";
}
toString(): string {
const details: string[] = [this.message];
if (this.source !== undefined) {
details.push(`Source: ${JSON.stringify(this.source)}`);
}
if (this.cause && typeof this.cause === "object" && "stack" in this.cause && this.cause.stack) {
details.push(`Stack:\n${this.cause.stack}`);
} else if (this.stack) {
details.push(`Stack:\n${this.stack}`);
}
return details.join("\n\n");
}
}
* Parse an agent from embedded content.
*/
export function parseAgent(
filePath: string,
content: string,
source: AgentSource,
level: "fatal" | "warn" | "off" = "fatal",
): AgentDefinition {
const { frontmatter, body } = parseFrontmatter(content, {
location: filePath,
level,
});
const fields = parseAgentFields(frontmatter);
if (!fields) {
throw new AgentParsingError(new Error(`Invalid agent field: ${filePath}\n${content}`), filePath);
}
return {
...fields,
systemPrompt: body,
source,
filePath,
};
}
let bundledAgentsCache: AgentDefinition[] | null = null;
* Load all bundled agents from embedded content.
* Results are cached after first load.
*/
export function loadBundledAgents(): AgentDefinition[] {
if (bundledAgentsCache !== null) {
return bundledAgentsCache;
}
bundledAgentsCache = EMBEDDED_AGENT_DEFS.map(def =>
parseAgent(`embedded:${def.fileName}`, buildAgentContent(def), "bundled"),
);
return bundledAgentsCache;
}
* Get a bundled agent by name.
*/
export function getBundledAgent(name: string): AgentDefinition | undefined {
return loadBundledAgents().find(a => a.name === name);
}
* Get all bundled agents as a map keyed by name.
*/
export function getBundledAgentsMap(): Map<string, AgentDefinition> {
const map = new Map<string, AgentDefinition>();
for (const agent of loadBundledAgents()) {
map.set(agent.name, agent);
}
return map;
}
* Clear the bundled agents cache (for testing).
*/
export function clearBundledAgentsCache(): void {
bundledAgentsCache = null;
}
export const BUNDLED_AGENTS = loadBundledAgents;