{"_id":"@boxiaolanya2008/pi-agent-core","_rev":"9-06b8b3dfa44a29a4fda122752dd075dc","name":"@boxiaolanya2008/pi-agent-core","dist-tags":{"latest":"1.0.0"},"versions":{"0.60.4":{"name":"@boxiaolanya2008/pi-agent-core","version":"0.60.4","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@boxiaolanya2008/pi-agent-core@0.60.4","maintainers":[{"name":"boxiaolanya2008","email":"3520687734@qq.com"}],"homepage":"https://github.com/badlogic/pi-mono#readme","bugs":{"url":"https://github.com/badlogic/pi-mono/issues"},"dist":{"shasum":"fd60fb33f988e759e61446fd7430d5db1cd0f5fe","tarball":"https://registry.npmjs.org/@boxiaolanya2008/pi-agent-core/-/pi-agent-core-0.60.4.tgz","fileCount":22,"integrity":"sha512-HKLPdO4XC1ySC6iuAdVyAzxriOoRtQd0HFyYP3hTqhYBnG0/BWTTjEZ7yPQxYU9CjZBNLYet7DtJ7LNeZfdyTA==","signatures":[{"sig":"MEYCIQDLSdJ2j5j14cr87aU3W+0KrnDlUEx4yIcwAFiQN6M51AIhAOidLFn3DaboXcehjjLgNXRS/nJ1scnkvaZv5qw8t2Y4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":190078},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"d904742779f84e7e3a344711c3fd414d9d7334b3","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"shx rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"boxiaolanya2008","email":"3520687734@qq.com"},"repository":{"url":"git+https://github.com/badlogic/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.6.2","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"25.2.1","dependencies":{"@boxiaolanya2008/pi-ai":"^0.60.4"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.60.4_1773407379402_0.2362073174930326","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"0.60.7":{"name":"@boxiaolanya2008/pi-agent-core","version":"0.60.7","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@boxiaolanya2008/pi-agent-core@0.60.7","maintainers":[{"name":"boxiaolanya2008","email":"3520687734@qq.com"}],"homepage":"https://github.com/badlogic/pi-mono#readme","bugs":{"url":"https://github.com/badlogic/pi-mono/issues"},"dist":{"shasum":"6368ffa9a5eb9911381eca813f6cb51d570e1b46","tarball":"https://registry.npmjs.org/@boxiaolanya2008/pi-agent-core/-/pi-agent-core-0.60.7.tgz","fileCount":22,"integrity":"sha512-TDdGGGHSyuwptJKoMfqoXSIeIcol1pv2L3WDhvW8bAKmmNDBHZfZmnjzr+cUeomunGZGZvOQbQsKXhdvQoOwTg==","signatures":[{"sig":"MEUCIH8Jxbw1fBicvwHfIV8M1uGXncvlFeS07RhzHNtNHpYDAiEAvh1pAX5QxTdyPrMKgA6sADfx0vCD31VvmLEqp/rPbiQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":190078},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"448a40e7f194f1343b78c03f2907d2a2ba33c62f","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"shx rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"boxiaolanya2008","email":"3520687734@qq.com"},"repository":{"url":"git+https://github.com/badlogic/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.6.2","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"25.2.1","dependencies":{"@boxiaolanya2008/pi-ai":"^0.60.7"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.60.7_1773408851639_0.32366728214762674","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"0.62.0":{"name":"@boxiaolanya2008/pi-agent-core","version":"0.62.0","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@boxiaolanya2008/pi-agent-core@0.62.0","maintainers":[{"name":"boxiaolanya2008","email":"3520687734@qq.com"}],"homepage":"https://github.com/badlogic/pi-mono#readme","bugs":{"url":"https://github.com/badlogic/pi-mono/issues"},"dist":{"shasum":"d2d58d1f2908991b7c86f847e7e34acab9550260","tarball":"https://registry.npmjs.org/@boxiaolanya2008/pi-agent-core/-/pi-agent-core-0.62.0.tgz","fileCount":22,"integrity":"sha512-nalnlm7SSOrYeNbGfsuIEqR7zKgFr42TLQdsK9ioIvF7S8m6gSRqDzhzS7tkqVDD5qlnZXmcNqANmwzle18chg==","signatures":[{"sig":"MEQCIEuHReV5J/XoBE99Q3d3uTBJYwEWO0BnzYrlF4DUVhsAAiBEdPpAv4ergcwpBXdbhNANhoA6xVGtrLjlnoXx43IoQA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":190078},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"7c3ac07dc52fa8d047e5349a78f8c2d4252c042f","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"shx rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"boxiaolanya2008","email":"3520687734@qq.com"},"repository":{"url":"git+https://github.com/badlogic/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.6.2","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"25.2.1","dependencies":{"@boxiaolanya2008/pi-ai":"^0.62.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.62.0_1773410960930_0.10804138094339","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"0.63.3":{"name":"@boxiaolanya2008/pi-agent-core","version":"0.63.3","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@boxiaolanya2008/pi-agent-core@0.63.3","maintainers":[{"name":"boxiaolanya2008","email":"3520687734@qq.com"}],"homepage":"https://github.com/badlogic/pi-mono#readme","bugs":{"url":"https://github.com/badlogic/pi-mono/issues"},"dist":{"shasum":"7e43d01bba3b6771ae3dd0752cc6c51ed330d351","tarball":"https://registry.npmjs.org/@boxiaolanya2008/pi-agent-core/-/pi-agent-core-0.63.3.tgz","fileCount":22,"integrity":"sha512-1qH8GeEu2AUSeLKWIKHgrJsa8ZxCEjWoJAaDSiGgBd08Tp96D8gFM6rWTVZutgmqSVq6/kb71ZudsEZp1qWo+w==","signatures":[{"sig":"MEUCIQCoduDS7wG2ZLZy6RxEYu05UDN4tjp3t5ZVvZll7Ue1wAIgPlGrHFeOKYG19zTJY92mTABfglFbBtniuLbcxW6yYT4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":190094},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"879b7098fc93a2a40cb73a61ded2701273a88dbe","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"shx rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"boxiaolanya2008","email":"3520687734@qq.com"},"repository":{"url":"git+https://github.com/badlogic/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.6.2","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"25.2.1","dependencies":{"@boxiaolanya2008/pi-ai":"^0.62.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.63.3_1773741197080_0.8931976753675492","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"0.63.4":{"name":"@boxiaolanya2008/pi-agent-core","version":"0.63.4","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@boxiaolanya2008/pi-agent-core@0.63.4","maintainers":[{"name":"boxiaolanya2008","email":"3520687734@qq.com"}],"homepage":"https://github.com/badlogic/pi-mono#readme","bugs":{"url":"https://github.com/badlogic/pi-mono/issues"},"dist":{"shasum":"5ae8bfb15e91e428dd99ce7a5ccd713992ba3439","tarball":"https://registry.npmjs.org/@boxiaolanya2008/pi-agent-core/-/pi-agent-core-0.63.4.tgz","fileCount":22,"integrity":"sha512-n/FPCMouwXnOg64RDU+ou1+wSqMuZAv0f1NUx65B6/CzbeyCDYQTftbDZ7ai1MXCkoqqBfsRyYmgS8uAWW9AuQ==","signatures":[{"sig":"MEYCIQDMQQSFjdOaCmlpoNAJHuacWcxqqU2Wdro7qZBQN42DugIhALcSq7mhtM760E08c7jZNvkXttbd/auTLnuczeeEUo8J","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":190094},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"a545c2814c30a73c05470213d15e4352bad2c4ed","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"shx rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"boxiaolanya2008","email":"3520687734@qq.com"},"repository":{"url":"git+https://github.com/badlogic/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.6.2","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"25.2.1","dependencies":{"@boxiaolanya2008/pi-ai":"^0.63.4"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.63.4_1773742695875_0.6408665922331314","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.0":{"name":"@boxiaolanya2008/pi-agent-core","version":"1.0.0","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@boxiaolanya2008/pi-agent-core@1.0.0","maintainers":[{"name":"boxiaolanya2008","email":"3520687734@qq.com"}],"homepage":"https://github.com/badlogic/pi-mono#readme","bugs":{"url":"https://github.com/badlogic/pi-mono/issues"},"dist":{"shasum":"38157dd5abd8bd72cd371c9de0ee5b0a1739e78a","tarball":"https://registry.npmjs.org/@boxiaolanya2008/pi-agent-core/-/pi-agent-core-1.0.0.tgz","fileCount":22,"integrity":"sha512-nCJp1vqIhUhSgWcpFypj3EGTZV1N3CpkyR4+hv9btHVopqa5oKUEFdPQ58qOq+9XPUPL/UWFaRFkDfLmC9laMw==","signatures":[{"sig":"MEQCIEbd398VrZxywik81bCcuiMR7jHJdXt2oOhAzLYqcfbZAiApHOftIhfRqSKK8uW4VeI4ZN8yYP4zWGIOK8cwrGIcgA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":190092},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"a1cebcc88966e61c00cb952488f75eecc4d03ed7","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"shx rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"boxiaolanya2008","email":"3520687734@qq.com"},"repository":{"url":"git+https://github.com/badlogic/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.6.2","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"25.2.1","dependencies":{"@boxiaolanya2008/pi-ai":"^1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_1.0.0_1773743947976_0.49037028436207253","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2026-03-13T13:09:39.335Z","modified":"2026-04-28T08:54:34.372Z","0.60.3":"2026-03-13T12:58:01.637Z","0.60.4":"2026-03-13T13:09:39.549Z","0.60.7":"2026-03-13T13:34:11.785Z","0.62.0":"2026-03-13T14:09:21.067Z","0.63.3":"2026-03-17T09:53:17.219Z","0.63.4":"2026-03-17T10:18:16.036Z","1.0.0":"2026-03-17T10:39:08.136Z"},"bugs":{"url":"https://github.com/badlogic/pi-mono/issues"},"author":{"name":"Mario Zechner"},"license":"MIT","homepage":"https://github.com/badlogic/pi-mono#readme","keywords":["ai","agent","llm","transport","state-management"],"repository":{"url":"git+https://github.com/badlogic/pi-mono.git","type":"git","directory":"packages/agent"},"description":"General-purpose agent with transport abstraction, state management, and attachment support","maintainers":[{"name":"boxiaolanya2008","email":"3520687734@qq.com"}],"readme":"# @mariozechner/pi-agent-core\n\nStateful agent with tool execution and event streaming. Built on `@mariozechner/pi-ai`.\n\n## Installation\n\n```bash\nnpm install @mariozechner/pi-agent-core\n```\n\n## Quick Start\n\n```typescript\nimport { Agent } from \"@mariozechner/pi-agent-core\";\nimport { getModel } from \"@mariozechner/pi-ai\";\n\nconst agent = new Agent({\n  initialState: {\n    systemPrompt: \"You are a helpful assistant.\",\n    model: getModel(\"anthropic\", \"claude-sonnet-4-20250514\"),\n  },\n});\n\nagent.subscribe((event) => {\n  if (event.type === \"message_update\" && event.assistantMessageEvent.type === \"text_delta\") {\n    // Stream just the new text chunk\n    process.stdout.write(event.assistantMessageEvent.delta);\n  }\n});\n\nawait agent.prompt(\"Hello!\");\n```\n\n## Core Concepts\n\n### AgentMessage vs LLM Message\n\nThe agent works with `AgentMessage`, a flexible type that can include:\n- Standard LLM messages (`user`, `assistant`, `toolResult`)\n- Custom app-specific message types via declaration merging\n\nLLMs only understand `user`, `assistant`, and `toolResult`. The `convertToLlm` function bridges this gap by filtering and transforming messages before each LLM call.\n\n### Message Flow\n\n```\nAgentMessage[] → transformContext() → AgentMessage[] → convertToLlm() → Message[] → LLM\n                    (optional)                           (required)\n```\n\n1. **transformContext**: Prune old messages, inject external context\n2. **convertToLlm**: Filter out UI-only messages, convert custom types to LLM format\n\n## Event Flow\n\nThe agent emits events for UI updates. Understanding the event sequence helps build responsive interfaces.\n\n### prompt() Event Sequence\n\nWhen you call `prompt(\"Hello\")`:\n\n```\nprompt(\"Hello\")\n├─ agent_start\n├─ turn_start\n├─ message_start   { message: userMessage }      // Your prompt\n├─ message_end     { message: userMessage }\n├─ message_start   { message: assistantMessage } // LLM starts responding\n├─ message_update  { message: partial... }       // Streaming chunks\n├─ message_update  { message: partial... }\n├─ message_end     { message: assistantMessage } // Complete response\n├─ turn_end        { message, toolResults: [] }\n└─ agent_end       { messages: [...] }\n```\n\n### With Tool Calls\n\nIf the assistant calls tools, the loop continues:\n\n```\nprompt(\"Read config.json\")\n├─ agent_start\n├─ turn_start\n├─ message_start/end  { userMessage }\n├─ message_start      { assistantMessage with toolCall }\n├─ message_update...\n├─ message_end        { assistantMessage }\n├─ tool_execution_start  { toolCallId, toolName, args }\n├─ tool_execution_update { partialResult }           // If tool streams\n├─ tool_execution_end    { toolCallId, result }\n├─ message_start/end  { toolResultMessage }\n├─ turn_end           { message, toolResults: [toolResult] }\n│\n├─ turn_start                                        // Next turn\n├─ message_start      { assistantMessage }           // LLM responds to tool result\n├─ message_update...\n├─ message_end\n├─ turn_end\n└─ agent_end\n```\n\n### continue() Event Sequence\n\n`continue()` resumes from existing context without adding a new message. Use it for retries after errors.\n\n```typescript\n// After an error, retry from current state\nawait agent.continue();\n```\n\nThe last message in context must be `user` or `toolResult` (not `assistant`).\n\n### Event Types\n\n| Event | Description |\n|-------|-------------|\n| `agent_start` | Agent begins processing |\n| `agent_end` | Agent completes with all new messages |\n| `turn_start` | New turn begins (one LLM call + tool executions) |\n| `turn_end` | Turn completes with assistant message and tool results |\n| `message_start` | Any message begins (user, assistant, toolResult) |\n| `message_update` | **Assistant only.** Includes `assistantMessageEvent` with delta |\n| `message_end` | Message completes |\n| `tool_execution_start` | Tool begins |\n| `tool_execution_update` | Tool streams progress |\n| `tool_execution_end` | Tool completes |\n\n## Agent Options\n\n```typescript\nconst agent = new Agent({\n  // Initial state\n  initialState: {\n    systemPrompt: string,\n    model: Model<any>,\n    thinkingLevel: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\",\n    tools: AgentTool<any>[],\n    messages: AgentMessage[],\n  },\n\n  // Convert AgentMessage[] to LLM Message[] (required for custom message types)\n  convertToLlm: (messages) => messages.filter(...),\n\n  // Transform context before convertToLlm (for pruning, compaction)\n  transformContext: async (messages, signal) => pruneOldMessages(messages),\n\n  // Steering mode: \"one-at-a-time\" (default) or \"all\"\n  steeringMode: \"one-at-a-time\",\n\n  // Follow-up mode: \"one-at-a-time\" (default) or \"all\"\n  followUpMode: \"one-at-a-time\",\n\n  // Custom stream function (for proxy backends)\n  streamFn: streamProxy,\n\n  // Session ID for provider caching\n  sessionId: \"session-123\",\n\n  // Dynamic API key resolution (for expiring OAuth tokens)\n  getApiKey: async (provider) => refreshToken(),\n\n  // Custom thinking budgets for token-based providers\n  thinkingBudgets: {\n    minimal: 128,\n    low: 512,\n    medium: 1024,\n    high: 2048,\n  },\n});\n```\n\n## Agent State\n\n```typescript\ninterface AgentState {\n  systemPrompt: string;\n  model: Model<any>;\n  thinkingLevel: ThinkingLevel;\n  tools: AgentTool<any>[];\n  messages: AgentMessage[];\n  isStreaming: boolean;\n  streamMessage: AgentMessage | null;  // Current partial during streaming\n  pendingToolCalls: Set<string>;\n  error?: string;\n}\n```\n\nAccess via `agent.state`. During streaming, `streamMessage` contains the partial assistant message.\n\n## Methods\n\n### Prompting\n\n```typescript\n// Text prompt\nawait agent.prompt(\"Hello\");\n\n// With images\nawait agent.prompt(\"What's in this image?\", [\n  { type: \"image\", data: base64Data, mimeType: \"image/jpeg\" }\n]);\n\n// AgentMessage directly\nawait agent.prompt({ role: \"user\", content: \"Hello\", timestamp: Date.now() });\n\n// Continue from current context (last message must be user or toolResult)\nawait agent.continue();\n```\n\n### State Management\n\n```typescript\nagent.setSystemPrompt(\"New prompt\");\nagent.setModel(getModel(\"openai\", \"gpt-4o\"));\nagent.setThinkingLevel(\"medium\");\nagent.setTools([myTool]);\nagent.replaceMessages(newMessages);\nagent.appendMessage(message);\nagent.clearMessages();\nagent.reset();  // Clear everything\n```\n\n### Session and Thinking Budgets\n\n```typescript\nagent.sessionId = \"session-123\";\n\nagent.thinkingBudgets = {\n  minimal: 128,\n  low: 512,\n  medium: 1024,\n  high: 2048,\n};\n```\n\n### Control\n\n```typescript\nagent.abort();           // Cancel current operation\nawait agent.waitForIdle(); // Wait for completion\n```\n\n### Events\n\n```typescript\nconst unsubscribe = agent.subscribe((event) => {\n  console.log(event.type);\n});\nunsubscribe();\n```\n\n## Steering and Follow-up\n\nSteering messages let you interrupt the agent while tools are running. Follow-up messages let you queue work after the agent would otherwise stop.\n\n```typescript\nagent.setSteeringMode(\"one-at-a-time\");\nagent.setFollowUpMode(\"one-at-a-time\");\n\n// While agent is running tools\nagent.steer({\n  role: \"user\",\n  content: \"Stop! Do this instead.\",\n  timestamp: Date.now(),\n});\n\n// After the agent finishes its current work\nagent.followUp({\n  role: \"user\",\n  content: \"Also summarize the result.\",\n  timestamp: Date.now(),\n});\n\nconst steeringMode = agent.getSteeringMode();\nconst followUpMode = agent.getFollowUpMode();\n\nagent.clearSteeringQueue();\nagent.clearFollowUpQueue();\nagent.clearAllQueues();\n```\n\nUse clearSteeringQueue, clearFollowUpQueue, or clearAllQueues to drop queued messages.\n\nWhen steering messages are detected after a tool completes:\n1. Remaining tools are skipped with error results\n2. Steering messages are injected\n3. LLM responds to the interruption\n\nFollow-up messages are checked only when there are no more tool calls and no steering messages. If any are queued, they are injected and another turn runs.\n\n## Custom Message Types\n\nExtend `AgentMessage` via declaration merging:\n\n```typescript\ndeclare module \"@mariozechner/pi-agent-core\" {\n  interface CustomAgentMessages {\n    notification: { role: \"notification\"; text: string; timestamp: number };\n  }\n}\n\n// Now valid\nconst msg: AgentMessage = { role: \"notification\", text: \"Info\", timestamp: Date.now() };\n```\n\nHandle custom types in `convertToLlm`:\n\n```typescript\nconst agent = new Agent({\n  convertToLlm: (messages) => messages.flatMap(m => {\n    if (m.role === \"notification\") return []; // Filter out\n    return [m];\n  }),\n});\n```\n\n## Tools\n\nDefine tools using `AgentTool`:\n\n```typescript\nimport { Type } from \"@sinclair/typebox\";\n\nconst readFileTool: AgentTool = {\n  name: \"read_file\",\n  label: \"Read File\",  // For UI display\n  description: \"Read a file's contents\",\n  parameters: Type.Object({\n    path: Type.String({ description: \"File path\" }),\n  }),\n  execute: async (toolCallId, params, signal, onUpdate) => {\n    const content = await fs.readFile(params.path, \"utf-8\");\n\n    // Optional: stream progress\n    onUpdate?.({ content: [{ type: \"text\", text: \"Reading...\" }], details: {} });\n\n    return {\n      content: [{ type: \"text\", text: content }],\n      details: { path: params.path, size: content.length },\n    };\n  },\n};\n\nagent.setTools([readFileTool]);\n```\n\n### Error Handling\n\n**Throw an error** when a tool fails. Do not return error messages as content.\n\n```typescript\nexecute: async (toolCallId, params, signal, onUpdate) => {\n  if (!fs.existsSync(params.path)) {\n    throw new Error(`File not found: ${params.path}`);\n  }\n  // Return content only on success\n  return { content: [{ type: \"text\", text: \"...\" }] };\n}\n```\n\nThrown errors are caught by the agent and reported to the LLM as tool errors with `isError: true`.\n\n## Proxy Usage\n\nFor browser apps that proxy through a backend:\n\n```typescript\nimport { Agent, streamProxy } from \"@mariozechner/pi-agent-core\";\n\nconst agent = new Agent({\n  streamFn: (model, context, options) =>\n    streamProxy(model, context, {\n      ...options,\n      authToken: \"...\",\n      proxyUrl: \"https://your-server.com\",\n    }),\n});\n```\n\n## Low-Level API\n\nFor direct control without the Agent class:\n\n```typescript\nimport { agentLoop, agentLoopContinue } from \"@mariozechner/pi-agent-core\";\n\nconst context: AgentContext = {\n  systemPrompt: \"You are helpful.\",\n  messages: [],\n  tools: [],\n};\n\nconst config: AgentLoopConfig = {\n  model: getModel(\"openai\", \"gpt-4o\"),\n  convertToLlm: (msgs) => msgs.filter(m => [\"user\", \"assistant\", \"toolResult\"].includes(m.role)),\n};\n\nconst userMessage = { role: \"user\", content: \"Hello\", timestamp: Date.now() };\n\nfor await (const event of agentLoop([userMessage], context, config)) {\n  console.log(event.type);\n}\n\n// Continue from existing context\nfor await (const event of agentLoopContinue(context, config)) {\n  console.log(event.type);\n}\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}