import{createAgentSession,ModelRuntime,SessionManager}from"@earendil-works/pi-coding-agent";constmodelRuntime=awaitModelRuntime.create();const{session}=awaitcreateAgentSession({sessionManager: SessionManager.inMemory(),modelRuntime,});session.subscribe((event)=>{if(event.type==="message_update"&&event.assistantMessageEvent.type==="text_delta"){process.stdout.write(event.assistantMessageEvent.delta);}});awaitsession.prompt("What files are in the current directory?");
import{createAgentSession,SessionManager}from"@earendil-works/pi-coding-agent";// Minimal: defaults with DefaultResourceLoader
const{session}=awaitcreateAgentSession();// Custom: override specific options
const{session}=awaitcreateAgentSession({model: myModel,tools:["read","bash"],sessionManager: SessionManager.inMemory(),});
AgentSession
会话负责管理智能体生命周期、消息历史、模型状态、上下文压缩和事件流。
interfaceAgentSession{// Send a prompt and wait for completion
prompt(text: string,options?: PromptOptions):Promise<void>;// Queue messages during streaming
steer(text: string):Promise<void>;followUp(text: string):Promise<void>;// Subscribe to events (returns unsubscribe function)
subscribe(listener:(event: AgentSessionEvent)=>void):()=>void;// Session info
sessionFile: string|undefined;sessionId: string;// Model control
setModel(model: Model):Promise<void>;setThinkingLevel(level: ThinkingLevel):void;cycleModel():Promise<ModelCycleResult|undefined>;cycleThinkingLevel():ThinkingLevel|undefined;// State access
agent: Agent;model: Model|undefined;thinkingLevel: ThinkingLevel;messages: AgentMessage[];isStreaming: boolean;// In-place tree navigation within the current session file
navigateTree(targetId: string,options?:{summarize?: boolean;customInstructions?: string;replaceInstructions?: boolean;label?: string}):Promise<{editorText?: string;cancelled: boolean}>;// Compaction
compact(customInstructions?: string):Promise<CompactionResult>;abortCompaction():void;// Abort current operation
abort():Promise<void>;// Cleanup
dispose():void;}
// Basic prompt (when not streaming)
awaitsession.prompt("What files are here?");// With images
awaitsession.prompt("What's in this image?",{images:[{type:"image",source:{type:"base64",mediaType:"image/png",data:"..."}}]});// During streaming: must specify how to queue the message
awaitsession.prompt("Stop and do this instead",{streamingBehavior:"steer"});awaitsession.prompt("After you're done, also check X",{streamingBehavior:"followUp"});
// Queue a steering message for delivery after the current assistant turn finishes its tool calls
awaitsession.steer("New instruction");// Wait for agent to finish (delivered only when agent stops)
awaitsession.followUp("After you're done, also do this");
// Access current state
conststate=session.agent.state;// state.messages: AgentMessage[] - conversation history
// state.model: Model - current model
// state.thinkingLevel: ThinkingLevel - current thinking level
// state.systemPrompt: string - system prompt
// state.tools: AgentTool[] - available tools
// state.streamingMessage?: AgentMessage - current partial assistant message
// state.errorMessage?: string - latest assistant error
// Replace messages (useful for branching or restoration)
session.agent.state.messages=messages;// copies the top-level array
// Replace tools
session.agent.state.tools=tools;// copies the top-level array
// Wait for agent to finish processing
awaitsession.agent.waitForIdle();
import{getModel}from"@earendil-works/pi-ai";import{ModelRuntime}from"@earendil-works/pi-coding-agent";constmodelRuntime=awaitModelRuntime.create();// create() restores cached catalogs but does not refresh them from pi.dev by default.
// Opt in to a create-time network refresh and bound how long it may take:
constrefreshedRuntime=awaitModelRuntime.create({allowModelNetwork: true,modelRefreshTimeoutMs: 15_000,});// Find specific built-in model (doesn't check if API key exists)
constopus=getModel("anthropic","claude-opus-4-5");if(!opus)thrownewError("Model not found");// Find any model by provider/id, including custom models from models.json
// (doesn't check if API key exists)
constcustomModel=modelRuntime.getModel("my-provider","my-model");// Get only models that have valid authentication configured
constavailable=awaitmodelRuntime.getAvailable();const{session}=awaitcreateAgentSession({model: opus,thinkingLevel:"medium",// off, minimal, low, medium, high, xhigh, max
// Models for cycling (Ctrl+P in interactive mode)
scopedModels:[{model: opus,thinkingLevel:"high"},{model: haiku,thinkingLevel:"off"},],modelRuntime,});
import{InMemoryCredentialStore}from"@earendil-works/pi-ai";import{createAgentSession,ModelRuntime}from"@earendil-works/pi-coding-agent";// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
constmodelRuntime=awaitModelRuntime.create();// Provider-owned auth methods and current status
for(constproviderofmodelRuntime.getProviders()){conststatus=awaitmodelRuntime.checkAuth(provider.id);console.log(provider.name,provider.auth,status);}// Runtime API key override (not persisted to disk)
awaitmodelRuntime.setRuntimeApiKey("anthropic","sk-my-temp-key");// Custom credential and model locations
constcustomRuntime=awaitModelRuntime.create({authPath:"/my/app/auth.json",modelsPath:"/my/app/models.json",});// Or inject any pi-ai CredentialStore
constcredentials=newInMemoryCredentialStore();constinMemoryRuntime=awaitModelRuntime.create({credentials});const{session}=awaitcreateAgentSession({modelRuntime: customRuntime,});
login()、logout()、setRuntimeApiKey() 和 removeRuntimeApiKey() 会在受影响提供商的缓存/内置目录、组合信息以及可用性快照在本地达成一致后再完成,不会等待远程目录刷新到最新。如果凭据已提交但本地同步失败,这些方法会以导出的 CredentialSynchronizationError 拒绝;此时应检查其 providerId、operation、credential 和 cause 字段,而不是盲目重试凭据变更。
公开的模型/认证操作以及 ModelRuntime.create({ signal }) 都接受可选的中止信号,省略时不设时限。远程目录新鲜度的截止时间策略由 SDK 应用自己掌握:
constsignal=AbortSignal.timeout(15_000);constresult=awaitmodelRuntime.refresh({providers:["anthropic"],signal,});if(result.aborted)console.warn("Catalog refresh timed out; using cached models");for(const[providerId,error]ofresult.errors){console.warn(`Could not refresh ${providerId}:`,error);}
import{createAgentSession,DefaultResourceLoader}from"@earendil-works/pi-coding-agent";constloader=newDefaultResourceLoader({systemPromptOverride:()=>"You are a helpful assistant.",});awaitloader.reload();const{session}=awaitcreateAgentSession({resourceLoader: loader});
import{createAgentSession}from"@earendil-works/pi-coding-agent";// Read-only mode
const{session}=awaitcreateAgentSession({tools:["read","grep","find","ls"],});// Pick specific tools
const{session}=awaitcreateAgentSession({tools:["read","bash","grep"],});// Use PowerShell instead of Bash on Windows
const{session}=awaitcreateAgentSession({tools:["read","powershell","edit","write"],});// Disable one tool while keeping the rest available
const{session}=awaitcreateAgentSession({excludeTools:["ask_question"],});
import{createAgentSession,SessionManager}from"@earendil-works/pi-coding-agent";constcwd="/path/to/project";// Use default tools for custom cwd
const{session}=awaitcreateAgentSession({cwd,sessionManager: SessionManager.inMemory(cwd),});// Or pick specific tools for custom cwd
const{session}=awaitcreateAgentSession({cwd,tools:["read","bash","grep"],sessionManager: SessionManager.inMemory(cwd),});
import{createAgentSession,DefaultResourceLoader}from"@earendil-works/pi-coding-agent";constloader=newDefaultResourceLoader({agentsFilesOverride:(current)=>({agentsFiles:[...current.agentsFiles,{path:"/virtual/AGENTS.md",content:"# Guidelines\n\n- Be concise"},],}),});awaitloader.reload();const{session}=awaitcreateAgentSession({resourceLoader: loader});
import{typeCreateAgentSessionRuntimeFactory,createAgentSession,createAgentSessionFromServices,createAgentSessionRuntime,createAgentSessionServices,getAgentDir,SessionManager,}from"@earendil-works/pi-coding-agent";// In-memory (no persistence)
const{session}=awaitcreateAgentSession({sessionManager: SessionManager.inMemory(),});// New persistent session
const{session: persisted}=awaitcreateAgentSession({sessionManager: SessionManager.create(process.cwd()),});// Continue most recent
const{session: continued,modelFallbackMessage}=awaitcreateAgentSession({sessionManager: SessionManager.continueRecent(process.cwd()),});if(modelFallbackMessage){console.log("Note:",modelFallbackMessage);}// Open specific file
const{session: opened}=awaitcreateAgentSession({sessionManager: SessionManager.open("/path/to/session.jsonl"),});// Resume a session kept outside the filesystem, e.g. in a database
const{session: restored}=awaitcreateAgentSession({sessionManager: SessionManager.inMemory(process.cwd(),{id: sessionId},entries),});// List sessions
constcurrentProjectSessions=awaitSessionManager.list(process.cwd());constallSessions=awaitSessionManager.listAll(process.cwd());// Session replacement API for /new, /resume, /fork, /clone, and import flows.
constcreateRuntime: CreateAgentSessionRuntimeFactory=async({cwd,sessionManager,sessionStartEvent})=>{constservices=awaitcreateAgentSessionServices({cwd});return{...(awaitcreateAgentSessionFromServices({services,sessionManager,sessionStartEvent,})),services,diagnostics: services.diagnostics,};};construntime=awaitcreateAgentSessionRuntime(createRuntime,{cwd: process.cwd(),agentDir: getAgentDir(),sessionManager: SessionManager.create(process.cwd()),});// Replace the active session with a fresh one
awaitruntime.newSession();// Replace the active session with another saved session
awaitruntime.switchSession("/path/to/session.jsonl");// Replace the active session with a fork from a specific user entry
awaitruntime.fork("entry-id");// Clone the active path through a specific entry
awaitruntime.fork("entry-id",{position:"at"});
SessionManager 树 API:
constsm=SessionManager.open("/path/to/session.jsonl");// Session listing
constcurrentProjectSessions=awaitSessionManager.list(process.cwd());constallSessions=awaitSessionManager.listAll(process.cwd());// Tree traversal
constentries=sm.getEntries();// All entries (excludes header)
consttree=sm.getTree();// Full tree structure
constpath=sm.getPath();// Path from root to current leaf
constleaf=sm.getLeafEntry();// Current leaf entry
constentry=sm.getEntry(id);// Get entry by ID
constchildren=sm.getChildren(id);// Direct children of entry
// Labels
constlabel=sm.getLabel(id);// Get label for entry
sm.appendLabelChange(id,"checkpoint");// Set label
// Branching
sm.branch(entryId);// Move leaf to earlier entry
sm.branchWithSummary(id,"Summary...");// Branch with context summary
sm.createBranchedSession(leafId);// Extract path to new file
interfaceCreateAgentSessionResult{// The session
session: AgentSession;// Extensions result (for runner setup)
extensionsResult: LoadExtensionsResult;// Warning if session model couldn't be restored
modelFallbackMessage?: string;}interfaceLoadExtensionsResult{extensions: Extension[];errors: Array<{path: string;error: string}>;runtime: ExtensionRuntime;}
完整示例
import{getModel}from"@earendil-works/pi-ai";import{Type}from"typebox";import{createAgentSession,DefaultResourceLoader,defineTool,ModelRuntime,SessionManager,SettingsManager,}from"@earendil-works/pi-coding-agent";constmodelRuntime=awaitModelRuntime.create({authPath:"/custom/agent/auth.json",modelsPath:"/custom/agent/models.json",});if(process.env.MY_KEY){awaitmodelRuntime.setRuntimeApiKey("anthropic",process.env.MY_KEY);}// Inline tool
conststatusTool=defineTool({name:"status",label:"Status",description:"Get system status",parameters: Type.Object({}),execute: async()=>({content:[{type:"text",text:`Uptime: ${process.uptime()}s`}],details:{},}),});constmodel=getModel("anthropic","claude-opus-4-5");if(!model)thrownewError("Model not found");// In-memory settings with overrides
constsettingsManager=SettingsManager.inMemory({compaction:{enabled: false},retry:{enabled: true,maxRetries: 2},});constloader=newDefaultResourceLoader({cwd: process.cwd(),agentDir:"/custom/agent",settingsManager,systemPromptOverride:()=>"You are a minimal assistant. Be concise.",});awaitloader.reload();const{session}=awaitcreateAgentSession({cwd: process.cwd(),agentDir:"/custom/agent",model,thinkingLevel:"off",modelRuntime,tools:["read","bash","status"],customTools:[statusTool],resourceLoader: loader,sessionManager: SessionManager.inMemory(),settingsManager,});session.subscribe((event)=>{if(event.type==="message_update"&&event.assistantMessageEvent.type==="text_delta"){process.stdout.write(event.assistantMessageEvent.delta);}});awaitsession.prompt("Get status and list files.");