chore(vendor): import pi 0.85.1
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
This commit is contained in:
26
third_party/pi/packages/coding-agent/examples/sdk/01-minimal.ts
vendored
Normal file
26
third_party/pi/packages/coding-agent/examples/sdk/01-minimal.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Minimal SDK Usage
|
||||
*
|
||||
* Uses all defaults: discovers skills, extensions, tools, context files
|
||||
* from cwd and ~/.pi/agent. Model chosen from settings or first available.
|
||||
*/
|
||||
|
||||
import { createAgentSession } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const { session } = await createAgentSession();
|
||||
|
||||
try {
|
||||
session.subscribe((event) => {
|
||||
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
});
|
||||
|
||||
await session.prompt("What files are in the current directory?");
|
||||
session.state.messages.forEach((msg) => {
|
||||
console.log(msg);
|
||||
});
|
||||
console.log();
|
||||
} finally {
|
||||
session.dispose();
|
||||
}
|
||||
49
third_party/pi/packages/coding-agent/examples/sdk/02-custom-model.ts
vendored
Normal file
49
third_party/pi/packages/coding-agent/examples/sdk/02-custom-model.ts
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Custom Model Selection
|
||||
*
|
||||
* Shows how to select a specific model and thinking level.
|
||||
*/
|
||||
|
||||
import { createAgentSession, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const modelRuntime = await ModelRuntime.create();
|
||||
|
||||
// Option 1: Find a specific built-in model by provider/id
|
||||
const opus = modelRuntime.getModel("anthropic", "claude-opus-4-5");
|
||||
if (opus) {
|
||||
console.log(`Found model: ${opus.provider}/${opus.id}`);
|
||||
}
|
||||
|
||||
// Option 2: Find model via registry (includes custom models from models.json)
|
||||
const customModel = modelRuntime.getModel("my-provider", "my-model");
|
||||
if (customModel) {
|
||||
console.log(`Found custom model: ${customModel.provider}/${customModel.id}`);
|
||||
}
|
||||
|
||||
// Option 3: Pick from available models (have valid API keys)
|
||||
const available = await modelRuntime.getAvailable();
|
||||
console.log(
|
||||
"Available models:",
|
||||
available.map((m) => `${m.provider}/${m.id}`),
|
||||
);
|
||||
|
||||
if (available.length > 0) {
|
||||
const { session } = await createAgentSession({
|
||||
model: available[0],
|
||||
thinkingLevel: "medium", // off, low, medium, high
|
||||
modelRuntime,
|
||||
});
|
||||
|
||||
try {
|
||||
session.subscribe((event) => {
|
||||
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
});
|
||||
|
||||
await session.prompt("Say hello in one sentence.");
|
||||
console.log();
|
||||
} finally {
|
||||
session.dispose();
|
||||
}
|
||||
}
|
||||
75
third_party/pi/packages/coding-agent/examples/sdk/03-custom-prompt.ts
vendored
Normal file
75
third_party/pi/packages/coding-agent/examples/sdk/03-custom-prompt.ts
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Custom System Prompt
|
||||
*
|
||||
* Shows how to replace or modify the default system prompt.
|
||||
*/
|
||||
|
||||
import {
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
getAgentDir,
|
||||
SessionManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const cwd = process.cwd();
|
||||
const agentDir = getAgentDir();
|
||||
|
||||
// Option 1: Replace prompt entirely
|
||||
const loader1 = new DefaultResourceLoader({
|
||||
cwd,
|
||||
agentDir,
|
||||
systemPromptOverride: () => `You are a helpful assistant that speaks like a pirate.
|
||||
Always end responses with "Arrr!"`,
|
||||
// Needed to avoid DefaultResourceLoader appending APPEND_SYSTEM.md from ~/.pi/agent or <cwd>/.pi.
|
||||
appendSystemPromptOverride: () => [],
|
||||
});
|
||||
await loader1.reload();
|
||||
|
||||
const { session: session1 } = await createAgentSession({
|
||||
resourceLoader: loader1,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
|
||||
try {
|
||||
session1.subscribe((event) => {
|
||||
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("=== Replace prompt ===");
|
||||
await session1.prompt("What is 2 + 2?");
|
||||
console.log("\n");
|
||||
} finally {
|
||||
session1.dispose();
|
||||
}
|
||||
|
||||
// Option 2: Append instructions to the default prompt
|
||||
const loader2 = new DefaultResourceLoader({
|
||||
cwd,
|
||||
agentDir,
|
||||
appendSystemPromptOverride: (base) => [
|
||||
...base,
|
||||
"## Additional Instructions\n- Always be concise\n- Use bullet points when listing things",
|
||||
],
|
||||
});
|
||||
await loader2.reload();
|
||||
|
||||
const { session: session2 } = await createAgentSession({
|
||||
resourceLoader: loader2,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
|
||||
try {
|
||||
session2.subscribe((event) => {
|
||||
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("=== Modify prompt ===");
|
||||
await session2.prompt("List 3 benefits of TypeScript.");
|
||||
console.log();
|
||||
} finally {
|
||||
session2.dispose();
|
||||
}
|
||||
55
third_party/pi/packages/coding-agent/examples/sdk/04-skills.ts
vendored
Normal file
55
third_party/pi/packages/coding-agent/examples/sdk/04-skills.ts
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Skills Configuration
|
||||
*
|
||||
* Skills provide specialized instructions loaded into the system prompt.
|
||||
* Discover, filter, merge, or replace them.
|
||||
*/
|
||||
|
||||
import {
|
||||
createAgentSession,
|
||||
createSyntheticSourceInfo,
|
||||
DefaultResourceLoader,
|
||||
getAgentDir,
|
||||
SessionManager,
|
||||
type Skill,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Or define custom skills inline
|
||||
const customSkill: Skill = {
|
||||
name: "my-skill",
|
||||
description: "Custom project instructions",
|
||||
filePath: "/virtual/SKILL.md",
|
||||
baseDir: "/virtual",
|
||||
sourceInfo: createSyntheticSourceInfo("/virtual/SKILL.md", { source: "sdk" }),
|
||||
disableModelInvocation: false,
|
||||
};
|
||||
|
||||
const loader = new DefaultResourceLoader({
|
||||
cwd: process.cwd(),
|
||||
agentDir: getAgentDir(),
|
||||
skillsOverride: (current) => {
|
||||
const filteredSkills = current.skills.filter((s) => s.name.includes("browser") || s.name.includes("search"));
|
||||
return {
|
||||
skills: [...filteredSkills, customSkill],
|
||||
diagnostics: current.diagnostics,
|
||||
};
|
||||
},
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
// Discover all skills from cwd/.pi/skills, ~/.pi/agent/skills, etc.
|
||||
const { skills: allSkills, diagnostics } = loader.getSkills();
|
||||
console.log(
|
||||
"Discovered skills:",
|
||||
allSkills.map((s) => s.name),
|
||||
);
|
||||
if (diagnostics.length > 0) {
|
||||
console.log("Warnings:", diagnostics);
|
||||
}
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
resourceLoader: loader,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log("Session created with filtered skills");
|
||||
session.dispose();
|
||||
48
third_party/pi/packages/coding-agent/examples/sdk/05-tools.ts
vendored
Normal file
48
third_party/pi/packages/coding-agent/examples/sdk/05-tools.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Tools Configuration
|
||||
*
|
||||
* Use tool names to choose which built-in tools are enabled.
|
||||
*
|
||||
* Tool names are matched against all available tools. If you use a custom `cwd`,
|
||||
* createAgentSession() applies that cwd when it builds the actual built-in tools.
|
||||
*
|
||||
* For custom tools, see 06-extensions.ts - custom tools are registered via the
|
||||
* extensions system using pi.registerTool().
|
||||
*/
|
||||
|
||||
import { createAgentSession, SessionManager } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Read-only mode (no edit/write)
|
||||
const { session: readOnlySession } = await createAgentSession({
|
||||
tools: ["read", "grep", "find", "ls"],
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log("Read-only session created");
|
||||
readOnlySession.dispose();
|
||||
|
||||
// Custom tool selection
|
||||
const { session: customToolsSession } = await createAgentSession({
|
||||
tools: ["read", "bash", "grep"],
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log("Custom tools session created");
|
||||
customToolsSession.dispose();
|
||||
|
||||
// With custom cwd
|
||||
const customCwd = "/path/to/project";
|
||||
const { session: customCwdSession } = await createAgentSession({
|
||||
cwd: customCwd,
|
||||
tools: ["read", "bash", "edit", "write"],
|
||||
sessionManager: SessionManager.inMemory(customCwd),
|
||||
});
|
||||
console.log("Custom cwd session created");
|
||||
customCwdSession.dispose();
|
||||
|
||||
// Or pick specific tools for custom cwd
|
||||
const { session: specificToolsSession } = await createAgentSession({
|
||||
cwd: customCwd,
|
||||
tools: ["read", "bash", "grep"],
|
||||
sessionManager: SessionManager.inMemory(customCwd),
|
||||
});
|
||||
console.log("Specific tools with custom cwd session created");
|
||||
specificToolsSession.dispose();
|
||||
99
third_party/pi/packages/coding-agent/examples/sdk/06-extensions.ts
vendored
Normal file
99
third_party/pi/packages/coding-agent/examples/sdk/06-extensions.ts
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Extensions Configuration
|
||||
*
|
||||
* Extensions intercept agent events and can register custom tools.
|
||||
* They provide a unified system for extensions, custom tools, commands, and more.
|
||||
*
|
||||
* By default, extension files are discovered from:
|
||||
* - ~/.pi/agent/extensions/
|
||||
* - <cwd>/.pi/extensions/
|
||||
* - Paths specified in settings.json "extensions" array
|
||||
*
|
||||
* An extension is a TypeScript file that exports a default function:
|
||||
* export default function (pi: ExtensionAPI) { ... }
|
||||
*/
|
||||
|
||||
import {
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
getAgentDir,
|
||||
SessionManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Extensions are discovered automatically from standard locations.
|
||||
// You can also add paths via settings.json or DefaultResourceLoader options.
|
||||
|
||||
const resourceLoader = new DefaultResourceLoader({
|
||||
cwd: process.cwd(),
|
||||
agentDir: getAgentDir(),
|
||||
additionalExtensionPaths: ["./my-logging-extension.ts", "./my-safety-extension.ts"],
|
||||
extensionFactories: [
|
||||
(pi) => {
|
||||
pi.on("agent_start", () => {
|
||||
console.log("[Inline Extension] Agent starting");
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
await resourceLoader.reload();
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
resourceLoader,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
|
||||
try {
|
||||
session.subscribe((event) => {
|
||||
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
});
|
||||
|
||||
await session.prompt("List files in the current directory.");
|
||||
console.log();
|
||||
} finally {
|
||||
session.dispose();
|
||||
}
|
||||
|
||||
// Example extension file (./my-logging-extension.ts):
|
||||
/*
|
||||
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
pi.on("agent_start", async () => {
|
||||
console.log("[Extension] Agent starting");
|
||||
});
|
||||
|
||||
pi.on("tool_call", async (event) => {
|
||||
console.log(\`[Extension] Tool: \${event.toolName}\`);
|
||||
// Return { block: true, reason: "..." } to block execution
|
||||
return undefined;
|
||||
});
|
||||
|
||||
pi.on("agent_end", async (event) => {
|
||||
console.log(\`[Extension] Low-level run ended, \${event.messages.length} messages\`);
|
||||
});
|
||||
|
||||
// Register a custom tool
|
||||
pi.registerTool({
|
||||
name: "my_tool",
|
||||
label: "My Tool",
|
||||
description: "Does something useful",
|
||||
parameters: Type.Object({
|
||||
input: Type.String(),
|
||||
}),
|
||||
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => ({
|
||||
content: [{ type: "text", text: \`Processed: \${params.input}\` }],
|
||||
details: {},
|
||||
}),
|
||||
});
|
||||
|
||||
// Register a command
|
||||
pi.registerCommand("mycommand", {
|
||||
description: "Do something",
|
||||
handler: async (args, ctx) => {
|
||||
ctx.ui.notify(\`Command executed with: \${args}\`);
|
||||
},
|
||||
});
|
||||
}
|
||||
*/
|
||||
47
third_party/pi/packages/coding-agent/examples/sdk/07-context-files.ts
vendored
Normal file
47
third_party/pi/packages/coding-agent/examples/sdk/07-context-files.ts
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Context Files (AGENTS.md)
|
||||
*
|
||||
* Context files provide project-specific instructions loaded into the system prompt.
|
||||
*/
|
||||
|
||||
import {
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
getAgentDir,
|
||||
SessionManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Disable context files entirely by returning an empty list in agentsFilesOverride.
|
||||
const loader = new DefaultResourceLoader({
|
||||
cwd: process.cwd(),
|
||||
agentDir: getAgentDir(),
|
||||
agentsFilesOverride: (current) => ({
|
||||
agentsFiles: [
|
||||
...current.agentsFiles,
|
||||
{
|
||||
path: "/virtual/AGENTS.md",
|
||||
content: `# Project Guidelines
|
||||
|
||||
## Code Style
|
||||
- Use TypeScript strict mode
|
||||
- No any types
|
||||
- Prefer const over let`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
// Discover AGENTS.md files walking up from cwd
|
||||
const discovered = loader.getAgentsFiles().agentsFiles;
|
||||
console.log("Discovered context files:");
|
||||
for (const file of discovered) {
|
||||
console.log(` - ${file.path} (${file.content.length} chars)`);
|
||||
}
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
resourceLoader: loader,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log(`Session created with ${discovered.length + 1} context files`);
|
||||
session.dispose();
|
||||
51
third_party/pi/packages/coding-agent/examples/sdk/08-prompt-templates.ts
vendored
Normal file
51
third_party/pi/packages/coding-agent/examples/sdk/08-prompt-templates.ts
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Prompt Templates
|
||||
*
|
||||
* File-based templates that inject content when invoked with /templatename.
|
||||
*/
|
||||
|
||||
import {
|
||||
createAgentSession,
|
||||
createSyntheticSourceInfo,
|
||||
DefaultResourceLoader,
|
||||
getAgentDir,
|
||||
type PromptTemplate,
|
||||
SessionManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// Define custom templates
|
||||
const deployTemplate: PromptTemplate = {
|
||||
name: "deploy",
|
||||
description: "Deploy the application",
|
||||
filePath: "/virtual/prompts/deploy.md",
|
||||
sourceInfo: createSyntheticSourceInfo("/virtual/prompts/deploy.md", { source: "sdk" }),
|
||||
content: `# Deploy Instructions
|
||||
|
||||
1. Build: npm run build
|
||||
2. Test: npm test
|
||||
3. Deploy: npm run deploy`,
|
||||
};
|
||||
|
||||
const loader = new DefaultResourceLoader({
|
||||
cwd: process.cwd(),
|
||||
agentDir: getAgentDir(),
|
||||
promptsOverride: (current) => ({
|
||||
prompts: [...current.prompts, deployTemplate],
|
||||
diagnostics: current.diagnostics,
|
||||
}),
|
||||
});
|
||||
await loader.reload();
|
||||
|
||||
// Discover templates from cwd/.pi/prompts/ and ~/.pi/agent/prompts/
|
||||
const discovered = loader.getPrompts().prompts;
|
||||
console.log("Discovered prompt templates:");
|
||||
for (const template of discovered) {
|
||||
console.log(` /${template.name}: ${template.description}`);
|
||||
}
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
resourceLoader: loader,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log(`Session created with ${discovered.length + 1} prompt templates`);
|
||||
session.dispose();
|
||||
34
third_party/pi/packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts
vendored
Normal file
34
third_party/pi/packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* API Keys and OAuth
|
||||
*
|
||||
* Configure provider auth through ModelRuntime.
|
||||
*/
|
||||
|
||||
import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const modelRuntime = await ModelRuntime.create();
|
||||
const { session: defaultAuthSession } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
modelRuntime,
|
||||
});
|
||||
console.log("Session with default model runtime");
|
||||
defaultAuthSession.dispose();
|
||||
|
||||
const customRuntime = await ModelRuntime.create({
|
||||
authPath: "/tmp/my-app/auth.json",
|
||||
modelsPath: "/tmp/my-app/models.json",
|
||||
});
|
||||
const { session: customAuthSession } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
modelRuntime: customRuntime,
|
||||
});
|
||||
console.log("Session with custom auth and models locations");
|
||||
customAuthSession.dispose();
|
||||
|
||||
await modelRuntime.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
||||
const { session: runtimeKeySession } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
modelRuntime,
|
||||
});
|
||||
console.log("Session with runtime API key override");
|
||||
runtimeKeySession.dispose();
|
||||
53
third_party/pi/packages/coding-agent/examples/sdk/10-settings.ts
vendored
Normal file
53
third_party/pi/packages/coding-agent/examples/sdk/10-settings.ts
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Settings Configuration
|
||||
*
|
||||
* Override settings using SettingsManager.
|
||||
*/
|
||||
|
||||
import { createAgentSession, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
// Load current settings (merged global + project)
|
||||
const settingsManagerFromDisk = SettingsManager.create(cwd);
|
||||
console.log("Current settings:", JSON.stringify(settingsManagerFromDisk.getGlobalSettings(), null, 2));
|
||||
|
||||
// Override specific settings
|
||||
const settingsManager = SettingsManager.create(cwd);
|
||||
settingsManager.applyOverrides({
|
||||
compaction: { enabled: false },
|
||||
retry: { enabled: true, maxRetries: 5, baseDelayMs: 1000 },
|
||||
});
|
||||
|
||||
const { session: customSettingsSession } = await createAgentSession({
|
||||
settingsManager,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log("Session created with custom settings");
|
||||
customSettingsSession.dispose();
|
||||
|
||||
// Setters update memory immediately and queue persistence writes.
|
||||
// Call flush() when you need a durability boundary.
|
||||
settingsManager.setDefaultThinkingLevel("low");
|
||||
await settingsManager.flush();
|
||||
|
||||
// Surface settings I/O errors at the app layer.
|
||||
const settingsErrors = settingsManager.drainErrors();
|
||||
if (settingsErrors.length > 0) {
|
||||
for (const { scope, error } of settingsErrors) {
|
||||
console.warn(`Warning (${scope} settings): ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// For testing without file I/O:
|
||||
const inMemorySettings = SettingsManager.inMemory({
|
||||
compaction: { enabled: false },
|
||||
retry: { enabled: false },
|
||||
});
|
||||
|
||||
const { session: testSession } = await createAgentSession({
|
||||
settingsManager: inMemorySettings,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log("Test session created with in-memory settings");
|
||||
testSession.dispose();
|
||||
52
third_party/pi/packages/coding-agent/examples/sdk/11-sessions.ts
vendored
Normal file
52
third_party/pi/packages/coding-agent/examples/sdk/11-sessions.ts
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Session Management
|
||||
*
|
||||
* Control session persistence: in-memory, new file, continue, or open specific.
|
||||
*/
|
||||
|
||||
import { createAgentSession, SessionManager } from "@earendil-works/pi-coding-agent";
|
||||
|
||||
// In-memory (no persistence)
|
||||
const { session: inMemory } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
});
|
||||
console.log("In-memory session:", inMemory.sessionFile ?? "(none)");
|
||||
inMemory.dispose();
|
||||
|
||||
// New persistent session
|
||||
const { session: newSession } = await createAgentSession({
|
||||
sessionManager: SessionManager.create(process.cwd()),
|
||||
});
|
||||
console.log("New session file:", newSession.sessionFile);
|
||||
newSession.dispose();
|
||||
|
||||
// Continue most recent session (or create new if none)
|
||||
const { session: continued, modelFallbackMessage } = await createAgentSession({
|
||||
sessionManager: SessionManager.continueRecent(process.cwd()),
|
||||
});
|
||||
if (modelFallbackMessage) console.log("Note:", modelFallbackMessage);
|
||||
console.log("Continued session:", continued.sessionFile);
|
||||
continued.dispose();
|
||||
|
||||
// List and open specific session
|
||||
const sessions = await SessionManager.list(process.cwd());
|
||||
console.log(`\nFound ${sessions.length} sessions:`);
|
||||
for (const info of sessions.slice(0, 3)) {
|
||||
console.log(` ${info.id.slice(0, 8)}... - "${info.firstMessage.slice(0, 30)}..."`);
|
||||
}
|
||||
|
||||
if (sessions.length > 0) {
|
||||
const { session: opened } = await createAgentSession({
|
||||
sessionManager: SessionManager.open(sessions[0].path),
|
||||
});
|
||||
console.log(`\nOpened: ${opened.sessionId}`);
|
||||
opened.dispose();
|
||||
}
|
||||
|
||||
// Custom session directory (no cwd encoding)
|
||||
// const customDir = "/path/to/my-sessions";
|
||||
// const { session } = await createAgentSession({
|
||||
// sessionManager: SessionManager.create(process.cwd(), customDir),
|
||||
// });
|
||||
// SessionManager.list(process.cwd(), customDir);
|
||||
// SessionManager.continueRecent(process.cwd(), customDir);
|
||||
74
third_party/pi/packages/coding-agent/examples/sdk/12-full-control.ts
vendored
Normal file
74
third_party/pi/packages/coding-agent/examples/sdk/12-full-control.ts
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Full Control
|
||||
*
|
||||
* Replace everything - no discovery, explicit configuration.
|
||||
*/
|
||||
|
||||
import { getModel } from "@earendil-works/pi-ai/compat";
|
||||
import {
|
||||
createAgentSession,
|
||||
createExtensionRuntime,
|
||||
ModelRuntime,
|
||||
type ResourceLoader,
|
||||
SessionManager,
|
||||
SettingsManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const modelRuntime = await ModelRuntime.create({
|
||||
authPath: "/tmp/my-agent/auth.json",
|
||||
modelsPath: "/tmp/my-agent/models.json",
|
||||
});
|
||||
if (process.env.MY_ANTHROPIC_KEY) {
|
||||
await modelRuntime.setRuntimeApiKey("anthropic", process.env.MY_ANTHROPIC_KEY);
|
||||
}
|
||||
|
||||
const model = getModel("anthropic", "claude-sonnet-4-5");
|
||||
if (!model) throw new Error("Model not found");
|
||||
|
||||
// In-memory settings with overrides
|
||||
const settingsManager = SettingsManager.inMemory({
|
||||
compaction: { enabled: false },
|
||||
retry: { enabled: true, maxRetries: 2 },
|
||||
});
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
const resourceLoader: ResourceLoader = {
|
||||
getExtensions: () => ({ extensions: [], errors: [], runtime: createExtensionRuntime() }),
|
||||
getSkills: () => ({ skills: [], diagnostics: [] }),
|
||||
getPrompts: () => ({ prompts: [], diagnostics: [] }),
|
||||
getThemes: () => ({ themes: [], diagnostics: [] }),
|
||||
getAgentsFiles: () => ({ agentsFiles: [] }),
|
||||
getSystemPrompt: () => `You are a minimal assistant.
|
||||
Available: read, bash. Be concise.`,
|
||||
getSystemPromptSource: () => undefined,
|
||||
getAppendSystemPrompt: () => [],
|
||||
getAppendSystemPromptSources: () => [],
|
||||
extendResources: () => {},
|
||||
reload: async () => {},
|
||||
};
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
cwd,
|
||||
agentDir: "/tmp/my-agent",
|
||||
model,
|
||||
thinkingLevel: "off",
|
||||
modelRuntime,
|
||||
resourceLoader,
|
||||
tools: ["read", "bash"],
|
||||
sessionManager: SessionManager.inMemory(cwd),
|
||||
settingsManager,
|
||||
});
|
||||
|
||||
try {
|
||||
session.subscribe((event) => {
|
||||
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
});
|
||||
|
||||
await session.prompt("List files in the current directory.");
|
||||
console.log();
|
||||
} finally {
|
||||
session.dispose();
|
||||
}
|
||||
67
third_party/pi/packages/coding-agent/examples/sdk/13-session-runtime.ts
vendored
Normal file
67
third_party/pi/packages/coding-agent/examples/sdk/13-session-runtime.ts
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Session runtime
|
||||
*
|
||||
* Use AgentSessionRuntime when you need to replace the active AgentSession,
|
||||
* for example for new-session, resume, fork, or import flows.
|
||||
*
|
||||
* The important pattern is: after the runtime replaces the active session,
|
||||
* rebind any session-local subscriptions and extension bindings to `runtime.session`.
|
||||
*/
|
||||
|
||||
import {
|
||||
type CreateAgentSessionRuntimeFactory,
|
||||
createAgentSessionFromServices,
|
||||
createAgentSessionRuntime,
|
||||
createAgentSessionServices,
|
||||
getAgentDir,
|
||||
SessionManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
||||
const services = await createAgentSessionServices({ cwd });
|
||||
return {
|
||||
...(await createAgentSessionFromServices({
|
||||
services,
|
||||
sessionManager,
|
||||
sessionStartEvent,
|
||||
})),
|
||||
services,
|
||||
diagnostics: services.diagnostics,
|
||||
};
|
||||
};
|
||||
const runtime = await createAgentSessionRuntime(createRuntime, {
|
||||
cwd: process.cwd(),
|
||||
agentDir: getAgentDir(),
|
||||
sessionManager: SessionManager.create(process.cwd()),
|
||||
});
|
||||
|
||||
let unsubscribe: (() => void) | undefined;
|
||||
|
||||
async function bindSession() {
|
||||
unsubscribe?.();
|
||||
const session = runtime.session;
|
||||
await session.bindExtensions({});
|
||||
unsubscribe = session.subscribe((event) => {
|
||||
if (event.type === "queue_update") {
|
||||
console.log("Queued:", event.steering.length + event.followUp.length);
|
||||
}
|
||||
});
|
||||
return session;
|
||||
}
|
||||
|
||||
let session = await bindSession();
|
||||
const originalSessionFile = session.sessionFile;
|
||||
console.log("Initial session:", originalSessionFile);
|
||||
|
||||
await runtime.newSession();
|
||||
session = await bindSession();
|
||||
console.log("After newSession():", session.sessionFile);
|
||||
|
||||
if (originalSessionFile) {
|
||||
await runtime.switchSession(originalSessionFile);
|
||||
session = await bindSession();
|
||||
console.log("After switchSession():", session.sessionFile);
|
||||
}
|
||||
|
||||
unsubscribe?.();
|
||||
await runtime.dispose();
|
||||
140
third_party/pi/packages/coding-agent/examples/sdk/README.md
vendored
Normal file
140
third_party/pi/packages/coding-agent/examples/sdk/README.md
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
# SDK Examples
|
||||
|
||||
Programmatic usage of pi-coding-agent via `createAgentSession()` and `createAgentSessionRuntime()`.
|
||||
|
||||
The runtime example shows how to build a recreate function that closes over process-global fixed inputs and recreates cwd-bound services and sessions as the active session cwd changes.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `01-minimal.ts` | Simplest usage with all defaults |
|
||||
| `02-custom-model.ts` | Select model and thinking level |
|
||||
| `03-custom-prompt.ts` | Replace or modify system prompt |
|
||||
| `04-skills.ts` | Discover, filter, or replace skills |
|
||||
| `05-tools.ts` | Built-in tool allowlists |
|
||||
| `06-extensions.ts` | Logging, blocking, result modification |
|
||||
| `07-context-files.ts` | AGENTS.md context files |
|
||||
| `08-slash-commands.ts` | File-based slash commands |
|
||||
| `09-api-keys-and-oauth.ts` | API key resolution, OAuth config |
|
||||
| `10-settings.ts` | Override compaction, retry, terminal settings |
|
||||
| `11-sessions.ts` | In-memory, persistent, continue, list sessions |
|
||||
| `12-full-control.ts` | Replace everything, no discovery |
|
||||
| `13-session-runtime.ts` | Manage runtime-backed session replacement |
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
cd packages/coding-agent
|
||||
npx tsx examples/sdk/01-minimal.ts
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```typescript
|
||||
import { getModel } from "@earendil-works/pi-ai";
|
||||
import {
|
||||
createAgentSession,
|
||||
DefaultResourceLoader,
|
||||
ModelRuntime,
|
||||
SessionManager,
|
||||
SettingsManager,
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
|
||||
const modelRuntime = await ModelRuntime.create();
|
||||
|
||||
// Minimal
|
||||
const { session } = await createAgentSession({ modelRuntime });
|
||||
|
||||
// Custom model
|
||||
const model = getModel("anthropic", "claude-opus-4-5");
|
||||
const { session } = await createAgentSession({ model, thinkingLevel: "high", modelRuntime });
|
||||
|
||||
// Modify prompt
|
||||
const loader = new DefaultResourceLoader({
|
||||
systemPromptOverride: (base) => `${base}\n\nBe concise.`,
|
||||
});
|
||||
await loader.reload();
|
||||
const { session } = await createAgentSession({ resourceLoader: loader, modelRuntime });
|
||||
|
||||
// Read-only
|
||||
const { session } = await createAgentSession({ tools: ["read", "grep", "find", "ls"], modelRuntime });
|
||||
|
||||
// In-memory
|
||||
const { session } = await createAgentSession({
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
modelRuntime,
|
||||
});
|
||||
|
||||
// Full control
|
||||
const customRuntime = await ModelRuntime.create({
|
||||
authPath: "/my/app/auth.json",
|
||||
modelsPath: "/my/app/models.json",
|
||||
});
|
||||
await customRuntime.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
|
||||
|
||||
const resourceLoader = new DefaultResourceLoader({
|
||||
systemPromptOverride: () => "You are helpful.",
|
||||
extensionFactories: [myExtension],
|
||||
skillsOverride: () => ({ skills: [], diagnostics: [] }),
|
||||
agentsFilesOverride: () => ({ agentsFiles: [] }),
|
||||
promptsOverride: () => ({ prompts: [], diagnostics: [] }),
|
||||
});
|
||||
await resourceLoader.reload();
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
model,
|
||||
modelRuntime: customRuntime,
|
||||
resourceLoader,
|
||||
tools: ["read", "bash", "my_tool"],
|
||||
customTools: [myTool],
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
settingsManager: SettingsManager.inMemory(),
|
||||
});
|
||||
|
||||
// Run prompts
|
||||
session.subscribe((event) => {
|
||||
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
});
|
||||
await session.prompt("Hello");
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `modelRuntime` | Runtime using `agentDir/auth.json` and `models.json` | Canonical model and authentication runtime |
|
||||
| `cwd` | `process.cwd()` | Working directory |
|
||||
| `agentDir` | `~/.pi/agent` | Config directory |
|
||||
| `model` | From settings/first available | Model to use |
|
||||
| `thinkingLevel` | From settings/"off" | off, low, medium, high |
|
||||
| `tools` | `["read", "bash", "edit", "write"]` built-ins | Allowlist tool names across built-in, extension, and custom tools |
|
||||
| `customTools` | `[]` | Additional tool definitions |
|
||||
| `resourceLoader` | DefaultResourceLoader | Resource loader for extensions, skills, prompts, themes, and context files |
|
||||
| `sessionManager` | `SessionManager.create(cwd)` | Persistence |
|
||||
| `settingsManager` | `SettingsManager.create(cwd, agentDir)` | Settings overrides |
|
||||
|
||||
## Events
|
||||
|
||||
```typescript
|
||||
session.subscribe((event) => {
|
||||
switch (event.type) {
|
||||
case "message_update":
|
||||
if (event.assistantMessageEvent.type === "text_delta") {
|
||||
process.stdout.write(event.assistantMessageEvent.delta);
|
||||
}
|
||||
break;
|
||||
case "tool_execution_start":
|
||||
console.log(`Tool: ${event.toolName}`);
|
||||
break;
|
||||
case "tool_execution_end":
|
||||
console.log(`Result: ${event.result}`);
|
||||
break;
|
||||
case "agent_settled":
|
||||
console.log("Done");
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user