{"_id":"@cargo-cult/pi-agent-core","_rev":"7-62fa6f3e2dabde07664800acb4d25d2c","name":"@cargo-cult/pi-agent-core","dist-tags":{"latest":"0.47.0"},"versions":{"0.40.0":{"name":"@cargo-cult/pi-agent-core","version":"0.40.0","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@cargo-cult/pi-agent-core@0.40.0","maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"homepage":"https://github.com/TUstudents/pi-mono#readme","bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"dist":{"shasum":"1c2e338b619d77018c5dc7d96328d021ba2b3217","tarball":"https://registry.npmjs.org/@cargo-cult/pi-agent-core/-/pi-agent-core-0.40.0.tgz","fileCount":22,"integrity":"sha512-PSiqRwHs5eA2aO9VniNsCegE8ubY3hGWiYhGRZLVn5DZQCpSbXHWPsqgIfqRRENyFszz5BjIrvARiWgTQyZ6fw==","signatures":[{"sig":"MEYCIQCpSlTvpvmS3JMf+pf4RbLvwdrFXTcASvXpzy6CJ2OxzAIhAK8QNRJHWAhcZhtTMlsdVQmCeftVHVYNQ+/ug7uKKNFo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cargo-cult%2fpi-agent-core@0.40.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190421},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"94ba25b6aee13ac8f98935b3536dab5192ed0399","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"tustudents","email":"python@atoms.eu"},"repository":{"url":"git+https://github.com/TUstudents/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.4","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cargo-cult/pi-ai":"^0.40.0","@cargo-cult/pi-tui":"^0.40.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.40.0_1767983647129_0.4321888299973826","host":"s3://npm-registry-packages-npm-production"}},"0.40.1":{"name":"@cargo-cult/pi-agent-core","version":"0.40.1","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@cargo-cult/pi-agent-core@0.40.1","maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"homepage":"https://github.com/TUstudents/pi-mono#readme","bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"dist":{"shasum":"1ba0b6a1276302176ba77d1a78eff0a70956348f","tarball":"https://registry.npmjs.org/@cargo-cult/pi-agent-core/-/pi-agent-core-0.40.1.tgz","fileCount":22,"integrity":"sha512-GsMFErZfgNBVwOpmRSU3aNqpL1mEC2NSJ3lAhgek/U4eWxWBgkA5TNNSEESSgMYo0vp6HJkNjc7wL+pqRULOPQ==","signatures":[{"sig":"MEQCIEPzzgJPVge/IX0pVp80Z94gYKuW2bULJi3PqbeD/os3AiA1wLz1i/68KahO9+l3hDcp3Nd/a3fa6G/l/JQS0PF3DQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cargo-cult%2fpi-agent-core@0.40.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190421},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"4d64ff64d64172c9c7ac87db756bf6044503396f","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"tustudents","email":"python@atoms.eu"},"repository":{"url":"git+https://github.com/TUstudents/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.4","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cargo-cult/pi-ai":"^0.40.1","@cargo-cult/pi-tui":"^0.40.1"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.40.1_1767984127444_0.5026061158652599","host":"s3://npm-registry-packages-npm-production"}},"0.42.0":{"name":"@cargo-cult/pi-agent-core","version":"0.42.0","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@cargo-cult/pi-agent-core@0.42.0","maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"homepage":"https://github.com/TUstudents/pi-mono#readme","bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"dist":{"shasum":"6408c938c79bd302e3d30632e3e36fc7ac30063c","tarball":"https://registry.npmjs.org/@cargo-cult/pi-agent-core/-/pi-agent-core-0.42.0.tgz","fileCount":22,"integrity":"sha512-pw4CeMaovW9TqOjdoUtAtriRz4oU3htDFmCdYl1XsqhW9bqNzv9s/AVka9iHEML7rvKGWhC87dHFpAflnJjhqQ==","signatures":[{"sig":"MEUCIQCggf3yxy/AXaKczQTasN4urp9Wn+YRQCr5YpjwK/0QlwIgTWS1E3wF2kLsj+39VPsUSR1C8RQYaz8XVmPZHRexUoE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cargo-cult%2fpi-agent-core@0.42.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190421},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"84996508517bf91a4cbe8fbaa09d5b12b19f38cf","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"tustudents","email":"python@atoms.eu"},"repository":{"url":"git+https://github.com/TUstudents/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.4","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cargo-cult/pi-ai":"^0.42.0","@cargo-cult/pi-tui":"^0.42.0"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.42.0_1767985870130_0.22413411879889855","host":"s3://npm-registry-packages-npm-production"}},"0.42.1":{"name":"@cargo-cult/pi-agent-core","version":"0.42.1","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@cargo-cult/pi-agent-core@0.42.1","maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"homepage":"https://github.com/TUstudents/pi-mono#readme","bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"dist":{"shasum":"939e8e5aee0dc21afb29322fbcef20d5010b89a9","tarball":"https://registry.npmjs.org/@cargo-cult/pi-agent-core/-/pi-agent-core-0.42.1.tgz","fileCount":22,"integrity":"sha512-GdsyYAj9XFNAYkMTnPfxZZx4ATE+QQvGepPU1zoy2hQ/o2Z+X8pHFXeP/5265I5c9v0yxJ2GikiRqyixWNyHBw==","signatures":[{"sig":"MEYCIQCiBhCikKRjjUt8k9Ifu8VSLij55zWy/TA9ItZ1qexPvgIhAJjlikFXix+dzMJKkFJYhWy8Dhfjd8/HkeM3HfaVVsol","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cargo-cult%2fpi-agent-core@0.42.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190421},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"5198ee434057587f815953b57e154b8f71e1db3c","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"tustudents","email":"python@atoms.eu"},"repository":{"url":"git+https://github.com/TUstudents/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.4","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cargo-cult/pi-ai":"^0.42.1","@cargo-cult/pi-tui":"^0.42.1"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.42.1_1767986261803_0.6019025950377337","host":"s3://npm-registry-packages-npm-production"}},"0.45.3":{"name":"@cargo-cult/pi-agent-core","version":"0.45.3","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@cargo-cult/pi-agent-core@0.45.3","maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"homepage":"https://github.com/TUstudents/pi-mono#readme","bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"dist":{"shasum":"abc8aba4fc3b02efc891a3c0aefecd0ff78b8e14","tarball":"https://registry.npmjs.org/@cargo-cult/pi-agent-core/-/pi-agent-core-0.45.3.tgz","fileCount":22,"integrity":"sha512-vQKpuSS6935mUYJKigN5Ib7hCvrBYKgjRr8CADqP/gSOXEg50PEoqY5oALd2sfiWmQtLbN5RBDqex+56LP8nfg==","signatures":[{"sig":"MEUCIEmb4NbxGj4OJ4rzB+gh67RMsakFmv0KC+1rJLQJkXpIAiEAwC1FXAYHo7Cj5cNNcN0QeRQW+zlrHwdSA27oh4ZSwsE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cargo-cult%2fpi-agent-core@0.45.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190421},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"c565ce93e848a6a833849cebebec6178585abed7","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"tustudents","email":"python@atoms.eu"},"repository":{"url":"git+https://github.com/TUstudents/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.4","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cargo-cult/pi-ai":"^0.45.3","@cargo-cult/pi-tui":"^0.45.3"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.45.3_1768326663578_0.732855669448476","host":"s3://npm-registry-packages-npm-production"}},"0.45.7":{"name":"@cargo-cult/pi-agent-core","version":"0.45.7","keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","_id":"@cargo-cult/pi-agent-core@0.45.7","maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"homepage":"https://github.com/TUstudents/pi-mono#readme","bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"dist":{"shasum":"6f91e8c9c0662cb9bf51d08334ab7f3b3efa8203","tarball":"https://registry.npmjs.org/@cargo-cult/pi-agent-core/-/pi-agent-core-0.45.7.tgz","fileCount":22,"integrity":"sha512-uxNj3xa73RjDIAjFsdniQR9ydUH76+MFAOspqVk/m32Trtw2EABE/tYiCvUB1kyx9bZkO1CiOJv9oVPIfuuzXw==","signatures":[{"sig":"MEUCIQDpnUXlgpYdoA7PYV5B3roRnC1t0hZ9UxP+k+YI0amaTgIgLA5ti7lShyuO9R6FnTPTGtED3jpo3bib8JadDEIrRB4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cargo-cult%2fpi-agent-core@0.45.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190206},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"gitHead":"0854b993f8933023232e238f1e8eb14c6768817f","scripts":{"dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","build":"tsgo -p tsconfig.build.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"tustudents","email":"python@atoms.eu"},"repository":{"url":"git+https://github.com/TUstudents/pi-mono.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.4","description":"General-purpose agent with transport abstraction, state management, and attachment support","directories":{},"_nodeVersion":"22.21.1","dependencies":{"@cargo-cult/pi-ai":"^0.45.7","@cargo-cult/pi-tui":"^0.45.7"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.7.3","@types/node":"^24.3.0"},"_npmOperationalInternal":{"tmp":"tmp/pi-agent-core_0.45.7_1768473916956_0.9414093783307913","host":"s3://npm-registry-packages-npm-production"}},"0.47.0":{"name":"@cargo-cult/pi-agent-core","version":"0.47.0","description":"General-purpose agent with transport abstraction, state management, and attachment support","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"clean":"rm -rf dist","build":"tsgo -p tsconfig.build.json","dev":"tsgo -p tsconfig.build.json --watch --preserveWatchOutput","test":"vitest --run","prepublishOnly":"npm run clean && npm run build"},"dependencies":{"@cargo-cult/pi-ai":"^0.47.0","@cargo-cult/pi-tui":"^0.47.0"},"keywords":["ai","agent","llm","transport","state-management"],"author":{"name":"Mario Zechner"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/TUstudents/pi-mono.git","directory":"packages/agent"},"engines":{"node":">=20.0.0"},"devDependencies":{"@types/node":"^24.3.0","typescript":"^5.7.3","vitest":"^3.2.4"},"_id":"@cargo-cult/pi-agent-core@0.47.0","gitHead":"639988d4bcbe25693775ce39cd905382eb58b7d4","bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"homepage":"https://github.com/TUstudents/pi-mono#readme","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-si5mJr2ceBlQE0wf6YoCBu6e+o6XSvZvH0M8jCFR9MaOscy+vB0dYc5ISnkwh9ymfsCYHKLV1bUpg6sds6+QRA==","shasum":"7dd44e9af4d51050e15589184557487926b19033","tarball":"https://registry.npmjs.org/@cargo-cult/pi-agent-core/-/pi-agent-core-0.47.0.tgz","fileCount":22,"unpackedSize":190206,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cargo-cult%2fpi-agent-core@0.47.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCgI4uUdLbr/RCzhSl0yvmXw6r02mO7leuNZZqiTP6J4QIhAPV2L1YG88EEz8OIVaTyxR3nKf4hSWrWNnkcRqNTR9rF"}]},"_npmUser":{"name":"tustudents","email":"python@atoms.eu"},"directories":{},"maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-agent-core_0.47.0_1768544525242_0.5366145198781456"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-09T18:34:07.035Z","modified":"2026-01-16T06:22:05.680Z","0.40.0":"2026-01-09T18:34:07.275Z","0.40.1":"2026-01-09T18:42:07.581Z","0.42.0":"2026-01-09T19:11:10.280Z","0.42.1":"2026-01-09T19:17:41.951Z","0.45.3":"2026-01-13T17:51:03.742Z","0.45.7":"2026-01-15T10:45:17.098Z","0.47.0":"2026-01-16T06:22:05.380Z"},"bugs":{"url":"https://github.com/TUstudents/pi-mono/issues"},"author":{"name":"Mario Zechner"},"license":"MIT","homepage":"https://github.com/TUstudents/pi-mono#readme","keywords":["ai","agent","llm","transport","state-management"],"repository":{"type":"git","url":"git+https://github.com/TUstudents/pi-mono.git","directory":"packages/agent"},"description":"General-purpose agent with transport abstraction, state management, and attachment support","maintainers":[{"name":"tustudents","email":"python@atoms.eu"}],"readme":"# @cargo-cult/pi-agent\n\nStateful agent with tool execution and event streaming. Built on `@cargo-cult/pi-ai`.\n\n## Installation\n\n```bash\nnpm install @cargo-cult/pi-agent\n```\n\n## Quick Start\n\n```typescript\nimport { Agent } from \"@cargo-cult/pi-agent\";\nimport { getModel } from \"@cargo-cult/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  // How to handle queued messages: \"one-at-a-time\" (default) or \"all\"\n  queueMode: \"one-at-a-time\",\n\n  // Custom stream function (for proxy backends)\n  streamFn: streamProxy,\n\n  // Dynamic API key resolution (for expiring OAuth tokens)\n  getApiKey: async (provider) => refreshToken(),\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### 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## Message Queue\n\nQueue messages to inject during tool execution (for user interruptions):\n\n```typescript\nagent.setQueueMode(\"one-at-a-time\");\n\n// While agent is running tools\nagent.queueMessage({\n  role: \"user\",\n  content: \"Stop! Do this instead.\",\n  timestamp: Date.now(),\n});\n```\n\nWhen queued messages are detected after a tool completes:\n1. Remaining tools are skipped with error results\n2. Queued message is injected\n3. LLM responds to the interruption\n\n## Custom Message Types\n\nExtend `AgentMessage` via declaration merging:\n\n```typescript\ndeclare module \"@cargo-cult/pi-agent\" {\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 \"@cargo-cult/pi-agent\";\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 \"@cargo-cult/pi-agent\";\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"}