{"_id":"@openrouter/agent","_rev":"23-6b0951db4066a53186756108be1c5468","name":"@openrouter/agent","dist-tags":{"latest":"0.11.0"},"versions":{"0.1.0":{"name":"@openrouter/agent","version":"0.1.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.1.0","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"johncolanduoni","email":"john@colanduoni.com"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"6855b30b375562fef329c1cecba55adc9a8cdbe6","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.1.0.tgz","fileCount":98,"integrity":"sha512-wYshH/rrh+3gr1RowF5DoYNbhozGGnVeJ8BBJvNpovUY+a+OJay6bOouJtOvW/KYjlJbPFPdtQdgAkjCnFTDYQ==","signatures":[{"sig":"MEUCIHYwzQYF1K4k1NlJyQtm9+Viu36WgucL/c0j5jlAkM+BAiEAqfQyK8e/vRNFhkf9xi16xL8zh6R6FnfyqLZ6pRdVB38=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":516508},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.1.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"eslint --cache --max-warnings=0 src","test":"vitest --run --project unit","build":"tsc","compile":"tsc","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/6b1a01be5468388a552cd5c83f54feee/openrouter-agent-0.1.0.tgz","_integrity":"sha512-wYshH/rrh+3gr1RowF5DoYNbhozGGnVeJ8BBJvNpovUY+a+OJay6bOouJtOvW/KYjlJbPFPdtQdgAkjCnFTDYQ==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git"},"_npmVersion":"10.9.4","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.1","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"git+https://github.com/OpenRouterTeam/typescript-sdk.git#add-prepare-script"},"_hasShrinkwrap":false,"devDependencies":{"dotenv":"^16.4.7","eslint":"^9.19.0","vitest":"^3.2.4","globals":"^15.14.0","@eslint/js":"^9.19.0","typescript":"~5.8.3","@types/node":"^22.13.12","@changesets/cli":"^2.30.0","typescript-eslint":"^8.26.0","@changesets/changelog-github":"^0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/agent_0.1.0_1775061796600_0.6449257620269375","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@openrouter/agent","version":"0.1.1","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.1.1","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"b18496ca94c821d5d1c1e59cb8eb815eaec9fae6","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.1.1.tgz","fileCount":102,"integrity":"sha512-IIZXZ8yffTv4htBEujd5+W9D9SDXsvgAHqDarV1F1vBtXF2SATcPRVGRlgo1SpK40deiO7VWtCGKZPm9fpluXA==","signatures":[{"sig":"MEYCIQD5Up5aGqKiFDeSAB2ZBPt5B//ILLRAvOXamxwoDu6TZAIhAIKiRRGQ2KwNMtUgbF1X9v4PuaoeipKlHQBsIfEpLcHZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":524022},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.1.1.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"eslint --cache --max-warnings=0 src","test":"vitest --run --project unit","build":"tsc","compile":"tsc","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/cb785cb4b163fd9442bb92c5ed23c8fd/openrouter-agent-0.1.1.tgz","_integrity":"sha512-IIZXZ8yffTv4htBEujd5+W9D9SDXsvgAHqDarV1F1vBtXF2SATcPRVGRlgo1SpK40deiO7VWtCGKZPm9fpluXA==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git"},"_npmVersion":"10.9.4","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.1","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"*"},"_hasShrinkwrap":false,"devDependencies":{"dotenv":"^16.4.7","eslint":"^9.19.0","vitest":"^3.2.4","globals":"^15.14.0","@eslint/js":"^9.19.0","typescript":"~5.8.3","@types/node":"^22.13.12","@changesets/cli":"^2.30.0","typescript-eslint":"^8.26.0","@changesets/changelog-github":"^0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/agent_0.1.1_1775173324141_0.08620435208384736","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@openrouter/agent","version":"0.1.2","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.1.2","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"894a3630cecdcc16866b57c27b0e652af24a5e93","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.1.2.tgz","fileCount":106,"integrity":"sha512-nlbatensa2OpQTVJHKAEnzkfcO/aBOIz+ao9YZ/gh6R3cxCXBHejp0sH7ZC3afXMNKtxUikF+gLfxHyrIky2Ng==","signatures":[{"sig":"MEQCIHgfJ7NVw/PwlLJa2RiZRcF6GoKgBto2r6FCTRlCSrlmAiAHS4GKMpNbHmog8YOSazOna2ING3Vgo8D6R4T9wFGNXA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":529181},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.1.2.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"eslint --cache --max-warnings=0 src","test":"vitest --run --project unit","build":"tsc","compile":"tsc","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/6ffd84b5fa8dcfcaff48d34b4d5e3307/openrouter-agent-0.1.2.tgz","_integrity":"sha512-nlbatensa2OpQTVJHKAEnzkfcO/aBOIz+ao9YZ/gh6R3cxCXBHejp0sH7ZC3afXMNKtxUikF+gLfxHyrIky2Ng==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"*"},"_hasShrinkwrap":false,"devDependencies":{"dotenv":"^16.4.7","eslint":"^9.19.0","vitest":"^3.2.4","globals":"^15.14.0","@eslint/js":"^9.19.0","typescript":"~5.8.3","@types/node":"^22.13.12","@changesets/cli":"^2.30.0","typescript-eslint":"^8.26.0","@changesets/changelog-github":"^0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/agent_0.1.2_1775583002376_0.2381813529486092","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@openrouter/agent","version":"0.3.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.3.0","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"690ea6ff6635c677761e00857316b45f1b7fe3e1","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.3.0.tgz","fileCount":110,"integrity":"sha512-u+AXwOfjb6rh1Kx9pnNDlqtX9FBvAD76bFAzT+ppPvob+wrA3gxKxu/f6MAkMGmkNBNe4b1839dYQUSviuXOfg==","signatures":[{"sig":"MEUCIQDePVhiYwG1BtlzZcvt36Vr7Ll6dpH3f08EootU3iqdbQIgHa+yfz1JRKkwbLFdNEfx1tEiojvrAtiBqenxcov7Ocg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":537280},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.3.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/253793aeafa2ff2b933ac65d6c90642c/openrouter-agent-0.3.0.tgz","_integrity":"sha512-u+AXwOfjb6rh1Kx9pnNDlqtX9FBvAD76bFAzT+ppPvob+wrA3gxKxu/f6MAkMGmkNBNe4b1839dYQUSviuXOfg==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"*"},"_hasShrinkwrap":false,"devDependencies":{"dotenv":"^16.4.7","vitest":"^3.2.4","typescript":"~5.8.3","@types/node":"^22.13.12","@biomejs/biome":"^2.4.10","@changesets/cli":"^2.30.0","@changesets/changelog-github":"^0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/agent_0.3.0_1775747146686_0.4690948452852022","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@openrouter/agent","version":"0.3.1","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.3.1","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"a1aca65a25872b95ca05753c517d60bbc0a8342b","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.3.1.tgz","fileCount":110,"integrity":"sha512-pKAi02SFsgs2W4RPCzZemqzguzjvneoj4+oQ4dnaZnwj9sBQBIoEy9mbpinCYnsF4Xwa/4P05g4ZPDlgpaVZ2w==","signatures":[{"sig":"MEQCIEgzzWAXkQsz40TZkElaf8WMI1cYbdds+DkokNr7I/CqAiB0VORmx2z7KeTDWOLFemNQNH5BanmGMKdRYjN+StBH9w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":537792},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.3.1.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/c013441e00f098977132c9d1a2c41835/openrouter-agent-0.3.1.tgz","_integrity":"sha512-pKAi02SFsgs2W4RPCzZemqzguzjvneoj4+oQ4dnaZnwj9sBQBIoEy9mbpinCYnsF4Xwa/4P05g4ZPDlgpaVZ2w==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"*"},"_hasShrinkwrap":false,"devDependencies":{"husky":"^9.1.7","dotenv":"^16.4.7","vitest":"^3.2.4","typescript":"~5.8.3","@types/node":"^22.13.12","@biomejs/biome":"^2.4.10","@changesets/cli":"^2.30.0","@changesets/changelog-github":"^0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/agent_0.3.1_1776005037018_0.5058623757772869","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@openrouter/agent","version":"0.3.2","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.3.2","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"4b972da6c73eda13fb897eda1907f54ad816b6e0","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.3.2.tgz","fileCount":110,"integrity":"sha512-ovC9OJLWHdhCozg12Y/PC7+mUuQP+uwC1pxpY+nUzor428sNOixxmHZtcZAv8WIjwJdda5o+4OKe0yqiQRfkcA==","signatures":[{"sig":"MEQCIFwTLF3dhlWaqCARHUckbg1+k7bsmRmDVxqJcS88m7onAiBGj5ijj/piY1pA93jkH930gj78+D0CkS7IA/Dcaf0POw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.3.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":544784},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.3.2.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/8844d35b910493da5dba3ebaaeabbcba/openrouter-agent-0.3.2.tgz","_integrity":"sha512-ovC9OJLWHdhCozg12Y/PC7+mUuQP+uwC1pxpY+nUzor428sNOixxmHZtcZAv8WIjwJdda5o+4OKe0yqiQRfkcA==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"*"},"_hasShrinkwrap":false,"devDependencies":{"husky":"^9.1.7","dotenv":"^16.4.7","vitest":"^3.2.4","typescript":"~5.8.3","@types/node":"^22.13.12","@biomejs/biome":"^2.4.10","@changesets/cli":"^2.30.0","@changesets/changelog-github":"^0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/agent_0.3.2_1776198336902_0.30449076073465275","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@openrouter/agent","version":"0.4.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.4.0","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"b79cbfdd198d52c8761d59b8f4bb7f0e59a8ce5a","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.4.0.tgz","fileCount":110,"integrity":"sha512-at/vbDFdWm2+iUYEw63gVMc1dt3zMW+WWOUofrMmouxi6GdY05pR3gyUGLAIgQYjeAnfK2gWIop/idt/tBCw4A==","signatures":[{"sig":"MEUCICjWFZ6sRoYVCRIdTM/w4i9BY7BoeVhh3tzFEWPwFU4/AiEAiONNR9I1jEGzSem7chCTs4df0u5ptI3nufR9JILgPdA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":585139},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.4.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/8c57b4f9f6859cb779082822d31379a5/openrouter-agent-0.4.0.tgz","_integrity":"sha512-at/vbDFdWm2+iUYEw63gVMc1dt3zMW+WWOUofrMmouxi6GdY05pR3gyUGLAIgQYjeAnfK2gWIop/idt/tBCw4A==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.12.12"},"_hasShrinkwrap":false,"devDependencies":{"husky":"^9.1.7","dotenv":"^16.4.7","vitest":"^3.2.4","typescript":"~5.8.3","@types/node":"^22.13.12","@biomejs/biome":"^2.4.10","@changesets/cli":"^2.30.0","@changesets/changelog-github":"^0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/agent_0.4.0_1776711477399_0.7350925083931328","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@openrouter/agent","version":"0.5.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.5.0","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"44c8abd4e943be87a0681ad1bf19a4dc3e548355","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.5.0.tgz","fileCount":110,"integrity":"sha512-BW5YnCTAblUsyquf4dPnK+qrmv7ba7flNmAoujFvz6pgZvAQ7BtYetGzd3YWFmkuMQiBHCdh51JkTD/MXzNnkQ==","signatures":[{"sig":"MEUCIH6mZ1wIx+Gj/ygkRPMbNKweoM5QU/6ewbbo+dpyQq3mAiEAuLQQu6Wn3DZApR/ZvGQAYA0iC6O0pyMUvHXX4QJPA4U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":633676},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.5.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/2eded3644e547222e90f1b1c5debea24/openrouter-agent-0.5.0.tgz","_integrity":"sha512-BW5YnCTAblUsyquf4dPnK+qrmv7ba7flNmAoujFvz6pgZvAQ7BtYetGzd3YWFmkuMQiBHCdh51JkTD/MXzNnkQ==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.12.12"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent_0.5.0_1778028777118_0.6850208803570463","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@openrouter/agent","version":"0.6.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.6.0","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"aab3faa1f5b1dc9775bf9a7bf4efb221d54eaee9","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.6.0.tgz","fileCount":110,"integrity":"sha512-uJpyHhNn0R15CXYWpUOYYQ85IN755LmJL5dE/hqgbbpBHslQrNbIM+UPeb9JivQYgQh1X37bB7cv2FWvfn94VQ==","signatures":[{"sig":"MEQCIB1p5Qwp1hddagQAO69ssBUHAHxnvV2pGxU/A0V8aOoIAiAMSnXAt5vg+JCyWsiLDw/QXuBWkxAPx0GeeuQQ7myc5A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":634053},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.6.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/600e2132a0d4840ea0b0679e2cd41680/openrouter-agent-0.6.0.tgz","_integrity":"sha512-uJpyHhNn0R15CXYWpUOYYQ85IN755LmJL5dE/hqgbbpBHslQrNbIM+UPeb9JivQYgQh1X37bB7cv2FWvfn94VQ==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.12.12"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent_0.6.0_1778774385629_0.36841661624238586","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@openrouter/agent","version":"0.7.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.7.0","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"61a69d8659f236e2fbc55bd47299f47a4f49d236","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.7.0.tgz","fileCount":110,"integrity":"sha512-jBamX9n5tdFj85jshQLu76zObjIWCqWlfm3clH8qisPJdTIhyV57AUsAehz+zcD35chCzF1IRCaWVs7Ka3SZng==","signatures":[{"sig":"MEYCIQDHNqJQp0fhz/WSQfOaAoOL2iPYaHfl3lM6F7vdAa8cwAIhAIzS+88qpFr/CcufZSclMrYVYvoEKjulzrM1iYuIjtdf","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":648190},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.7.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"},"_resolved":"/tmp/6c15f2cc380ce72c14adc1fa4473c473/openrouter-agent-0.7.0.tgz","_integrity":"sha512-jBamX9n5tdFj85jshQLu76zObjIWCqWlfm3clH8qisPJdTIhyV57AUsAehz+zcD35chCzF1IRCaWVs7Ka3SZng==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.7","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.12.12"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent_0.7.0_1779140716051_0.6006290904901628","host":"s3://npm-registry-packages-npm-production"}},"0.7.1":{"name":"@openrouter/agent","version":"0.7.1","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.7.1","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"18a96b97d2140d1c75dcdeb5e1d97b95c8ce688c","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.7.1.tgz","fileCount":110,"integrity":"sha512-NrxszvWG/C2biUjKva4qDgA/DyIIiKaa0AoKLLVq1PPfClHb4GIdOREMIne3rL+IIRsshWVTXDKL4SdEF4Gw6w==","signatures":[{"sig":"MEUCIQDR8I2TpgFLpJ/e3qiKTwWXf06H8dr/F9yTBKoYMnlryAIgByd5uXjO+W+F8B3p0QnSge76wDiOSNGtnvG7TLxggS4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.7.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":650301},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.7.1.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},"_resolved":"/tmp/bb1a0ac54dc6bd85155fb21eaae7346b/openrouter-agent-0.7.1.tgz","_integrity":"sha512-NrxszvWG/C2biUjKva4qDgA/DyIIiKaa0AoKLLVq1PPfClHb4GIdOREMIne3rL+IIRsshWVTXDKL4SdEF4Gw6w==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.8","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.3","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.12.12"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent_0.7.1_1780506633400_0.08759546228701165","host":"s3://npm-registry-packages-npm-production"}},"0.7.2":{"name":"@openrouter/agent","version":"0.7.2","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.7.2","maintainers":[{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"03c60a9237f13541cbf999f17649955064d2a654","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.7.2.tgz","fileCount":110,"integrity":"sha512-G3Rp4iPKP7RQSJ+k7ea2+fDOJXUhsZNmiKoz8Ddlo7fY9KDdNiP4xKA0IA/9Y27FJjb2kagHvP9Dw2ycEwlzmA==","signatures":[{"sig":"MEYCIQCA8AlcBSc3tlzSihWdjw7b8Ya9Wrb/eheHbrwAi7fmYgIhAKFo3e+2OX83T77wZnIFPiNs5x6ymu/YFugP/d5Kk/+S","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.7.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":650300},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.7.2.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},"_resolved":"/tmp/63383bba6312368b9cf7b076502031ad/openrouter-agent-0.7.2.tgz","_integrity":"sha512-G3Rp4iPKP7RQSJ+k7ea2+fDOJXUhsZNmiKoz8Ddlo7fY9KDdNiP4xKA0IA/9Y27FJjb2kagHvP9Dw2ycEwlzmA==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.8","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.3","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.13.7"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent_0.7.2_1781704193958_0.6535235692780674","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@openrouter/agent","version":"0.8.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.8.0","maintainers":[{"name":"christineschen","email":"christinechensd@gmail.com"},{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"c2aac9fe249d216b8898a9520a0d89451e3d578f","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.8.0.tgz","fileCount":134,"integrity":"sha512-QpKy/DgYIWxKfE+sflTFSIze1ubdTcRHmQCbkvGl+j8WEBl48s/ZNaAhsxkrXZimPhs+KVSJh2v3BMY4zSYsKw==","signatures":[{"sig":"MEUCIQDck2B8LmkP37XbGW6OsRw1Gx45+xjtjoykvdJr6RAizQIgbmmprc2o/KGIZFnl1A07FGhdBzAO+/3eoyYrLbLD9w4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":868319},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.8.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./hooks-manager":{"types":"./esm/lib/hooks-manager.d.ts","default":"./esm/lib/hooks-manager.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit -p tsconfig.typecheck.json","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},"_resolved":"/tmp/f682cfc7c20fa899c231ef6978bb40dc/openrouter-agent-0.8.0.tgz","_integrity":"sha512-QpKy/DgYIWxKfE+sflTFSIze1ubdTcRHmQCbkvGl+j8WEBl48s/ZNaAhsxkrXZimPhs+KVSJh2v3BMY4zSYsKw==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"10.9.8","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"22.23.1","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.13.7"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent_0.8.0_1784753429849_0.7794830873698899","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"@openrouter/agent","version":"0.9.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.9.0","maintainers":[{"name":"christineschen","email":"christinechensd@gmail.com"},{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},{"name":"lukehagar","email":"lukeslakemail@gmail.com"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"8bfaa8c2f167035b540153b0e9adae714d526f22","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.9.0.tgz","fileCount":166,"integrity":"sha512-VYXRbMBbvLFwqzySZF6f52EtzMpRO356A8pasijGheNaoOgkHmOcDa6Xr4STpdoySMxCleilTvDp4AvD17+bFA==","signatures":[{"sig":"MEUCIQCuy3O1yy9Iz+LAl2mUdYwpuq5uRJCqqYSd65U5amOZdQIgPhKv8n2aTnHmmV+ni9b3SNxd21Q+i9tePO4cz/qgXX4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1483217},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.9.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./doom-loop":{"types":"./esm/lib/doom-loop.d.ts","default":"./esm/lib/doom-loop.js"},"./tool-task":{"types":"./esm/lib/tool-task.d.ts","default":"./esm/lib/tool-task.js"},"./agent-tool":{"types":"./esm/lib/agent-tool.d.ts","default":"./esm/lib/agent-tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-check":{"types":"./esm/lib/tool-check.d.ts","default":"./esm/lib/tool-check.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./hooks-manager":{"types":"./esm/lib/hooks-manager.d.ts","default":"./esm/lib/hooks-manager.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./tool-concurrency":{"types":"./esm/lib/tool-concurrency.d.ts","default":"./esm/lib/tool-concurrency.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./async-tool-registry":{"types":"./esm/lib/async-tool-registry.d.ts","default":"./esm/lib/async-tool-registry.js"},"./resume-tool-results":{"types":"./esm/inner-loop/resume-tool-results.d.ts","default":"./esm/inner-loop/resume-tool-results.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"tsc","compile":"tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e","typecheck":"tsc --noEmit -p tsconfig.typecheck.json","test:watch":"vitest --watch --project unit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:59127aa2-8d66-4ee6-b7ec-f2e48194db22"}},"_resolved":"/tmp/bd3b54e58ef92f08d46d39dc04bf8fc4/openrouter-agent-0.9.0.tgz","_integrity":"sha512-VYXRbMBbvLFwqzySZF6f52EtzMpRO356A8pasijGheNaoOgkHmOcDa6Xr4STpdoySMxCleilTvDp4AvD17+bFA==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.16.0","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.13.7"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent_0.9.0_1786386183057_0.5652580912294225","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"@openrouter/agent","version":"0.10.0","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"author":{"name":"OpenRouter"},"license":"Apache-2.0","_id":"@openrouter/agent@0.10.0","maintainers":[{"name":"christineschen","email":"christinechensd@gmail.com"},{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},{"name":"davidlbowman-npm","email":"david@theinnovationlab.dev"},{"name":"lukehagar","email":"lukeslakemail@gmail.com"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"dist":{"shasum":"9dc3383d8c822477f77ae45ec3bd411d112f4d3f","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.10.0.tgz","fileCount":267,"integrity":"sha512-rpEJ3dCz17ud6HItdNKOHMUyHLBMRpTbvkBJ/JbYNkSEtm4HecZzOTGTGbWIdEURJJbbpQnTGO2jW3nASBFpOg==","signatures":[{"sig":"MEUCIGI8b02qXbxi28j1ibRLSB9HMY2eHvsAuBaRZ/0wJbUbAiEAsezMDlXlwrHmry/skecrdJgnA/CtB8IpHr/wF9f6C4c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.10.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1804327},"main":"./esm/index.js","type":"module","_from":"file:openrouter-agent-0.10.0.tgz","types":"./esm/index.d.ts","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./mcp":{"types":"./esm/mcp/index.d.ts","default":"./esm/mcp/index.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./tool-set":{"types":"./esm/lib/tool-set.d.ts","default":"./esm/lib/tool-set.js"},"./doom-loop":{"types":"./esm/lib/doom-loop.d.ts","default":"./esm/lib/doom-loop.js"},"./mcp/cache":{"types":"./esm/mcp/cache/cache-store.d.ts","default":"./esm/mcp/cache/cache-store.js"},"./mcp/types":{"types":"./esm/mcp/types.d.ts","default":"./esm/mcp/types.js"},"./tool-task":{"types":"./esm/lib/tool-task.d.ts","default":"./esm/lib/tool-task.js"},"./agent-tool":{"types":"./esm/lib/agent-tool.d.ts","default":"./esm/lib/agent-tool.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./mcp/schema":{"types":"./esm/mcp/schema/json-schema-to-zod.d.ts","default":"./esm/mcp/schema/json-schema-to-zod.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./tool-check":{"types":"./esm/lib/tool-check.d.ts","default":"./esm/lib/tool-check.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./package.json":"./package.json","./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./hooks-manager":{"types":"./esm/lib/hooks-manager.d.ts","default":"./esm/lib/hooks-manager.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./tool-concurrency":{"types":"./esm/lib/tool-concurrency.d.ts","default":"./esm/lib/tool-concurrency.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./async-tool-registry":{"types":"./esm/lib/async-tool-registry.d.ts","default":"./esm/lib/async-tool-registry.js"},"./resume-tool-results":{"types":"./esm/inner-loop/resume-tool-results.d.ts","default":"./esm/inner-loop/resume-tool-results.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./mcp/create-mcp-tools":{"types":"./esm/mcp/create-mcp-tools.d.ts","default":"./esm/mcp/create-mcp-tools.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"}},"scripts":{"lint":"biome check src tests","test":"vitest --run --project unit","build":"node scripts/gen-version.mjs && tsc","compile":"node scripts/gen-version.mjs && tsc","lint:fix":"biome check --write src tests","test:e2e":"vitest --run --project e2e --coverage.enabled=false","typecheck":"tsc --noEmit -p tsconfig.typecheck.json","test:watch":"vitest --watch --project unit","gen:version":"node scripts/gen-version.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:59127aa2-8d66-4ee6-b7ec-f2e48194db22"}},"_resolved":"/tmp/bb4085bd43863e02496732af6dda5fd9/openrouter-agent-0.10.0.tgz","_integrity":"sha512-rpEJ3dCz17ud6HItdNKOHMUyHLBMRpTbvkBJ/JbYNkSEtm4HecZzOTGTGbWIdEURJJbbpQnTGO2jW3nASBFpOg==","repository":{"url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","type":"git","directory":"packages/agent"},"_npmVersion":"11.17.0","description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","dependencies":{"zod":"^4.0.0","@openrouter/sdk":"^0.13.7"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"@modelcontextprotocol/client":"^2.0.0"},"peerDependencies":{"@modelcontextprotocol/client":"^2.0.0"},"peerDependenciesMeta":{"@modelcontextprotocol/client":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/agent_0.10.0_1787331598519_0.45723409428831685","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"name":"@openrouter/agent","version":"0.11.0","author":{"name":"OpenRouter"},"description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"license":"Apache-2.0","type":"module","main":"./esm/index.js","exports":{".":{"types":"./esm/index.d.ts","default":"./esm/index.js"},"./call-model":{"types":"./esm/inner-loop/call-model.d.ts","default":"./esm/inner-loop/call-model.js"},"./tool-types":{"types":"./esm/lib/tool-types.d.ts","default":"./esm/lib/tool-types.js"},"./model-result":{"types":"./esm/lib/model-result.d.ts","default":"./esm/lib/model-result.js"},"./hooks-manager":{"types":"./esm/lib/hooks-manager.d.ts","default":"./esm/lib/hooks-manager.js"},"./async-params":{"types":"./esm/lib/async-params.d.ts","default":"./esm/lib/async-params.js"},"./stop-conditions":{"types":"./esm/lib/stop-conditions.d.ts","default":"./esm/lib/stop-conditions.js"},"./doom-loop":{"types":"./esm/lib/doom-loop.d.ts","default":"./esm/lib/doom-loop.js"},"./tool":{"types":"./esm/lib/tool.d.ts","default":"./esm/lib/tool.js"},"./anthropic-compat":{"types":"./esm/lib/anthropic-compat.d.ts","default":"./esm/lib/anthropic-compat.js"},"./chat-compat":{"types":"./esm/lib/chat-compat.d.ts","default":"./esm/lib/chat-compat.js"},"./claude-constants":{"types":"./esm/lib/claude-constants.d.ts","default":"./esm/lib/claude-constants.js"},"./claude-type-guards":{"types":"./esm/lib/claude-type-guards.d.ts","default":"./esm/lib/claude-type-guards.js"},"./conversation-state":{"types":"./esm/lib/conversation-state.d.ts","default":"./esm/lib/conversation-state.js"},"./next-turn-params":{"types":"./esm/lib/next-turn-params.d.ts","default":"./esm/lib/next-turn-params.js"},"./stream-transformers":{"types":"./esm/lib/stream-transformers.d.ts","default":"./esm/lib/stream-transformers.js"},"./tool-context":{"types":"./esm/lib/tool-context.d.ts","default":"./esm/lib/tool-context.js"},"./tool-event-broadcaster":{"types":"./esm/lib/tool-event-broadcaster.d.ts","default":"./esm/lib/tool-event-broadcaster.js"},"./reusable-stream":{"types":"./esm/lib/reusable-stream.d.ts","default":"./esm/lib/reusable-stream.js"},"./turn-context":{"types":"./esm/lib/turn-context.d.ts","default":"./esm/lib/turn-context.js"},"./tool-set":{"types":"./esm/lib/tool-set.d.ts","default":"./esm/lib/tool-set.js"},"./openrouter":{"types":"./esm/openrouter.d.ts","default":"./esm/openrouter.js"},"./mcp":{"types":"./esm/mcp/index.d.ts","default":"./esm/mcp/index.js"},"./mcp/create-mcp-tools":{"types":"./esm/mcp/create-mcp-tools.d.ts","default":"./esm/mcp/create-mcp-tools.js"},"./mcp/types":{"types":"./esm/mcp/types.d.ts","default":"./esm/mcp/types.js"},"./mcp/schema":{"types":"./esm/mcp/schema/json-schema-to-zod.d.ts","default":"./esm/mcp/schema/json-schema-to-zod.js"},"./mcp/cache":{"types":"./esm/mcp/cache/cache-store.d.ts","default":"./esm/mcp/cache/cache-store.js"},"./tool-concurrency":{"types":"./esm/lib/tool-concurrency.d.ts","default":"./esm/lib/tool-concurrency.js"},"./async-tool-registry":{"types":"./esm/lib/async-tool-registry.d.ts","default":"./esm/lib/async-tool-registry.js"},"./resume-tool-results":{"types":"./esm/inner-loop/resume-tool-results.d.ts","default":"./esm/inner-loop/resume-tool-results.js"},"./tool-task":{"types":"./esm/lib/tool-task.d.ts","default":"./esm/lib/tool-task.js"},"./tool-check":{"types":"./esm/lib/tool-check.d.ts","default":"./esm/lib/tool-check.js"},"./agent-tool":{"types":"./esm/lib/agent-tool.d.ts","default":"./esm/lib/agent-tool.js"},"./package.json":"./package.json"},"sideEffects":false,"repository":{"type":"git","url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","directory":"packages/agent"},"publishConfig":{"access":"public","provenance":true},"dependencies":{"@openrouter/sdk":"^0.13.7","zod":"^4.0.0"},"peerDependencies":{"@modelcontextprotocol/client":"^2.0.0"},"peerDependenciesMeta":{"@modelcontextprotocol/client":{"optional":true}},"devDependencies":{"@modelcontextprotocol/client":"^2.0.0"},"scripts":{"lint":"biome check src tests","lint:fix":"biome check --write src tests","build":"node scripts/gen-version.mjs && tsc","test":"vitest --run --project unit","test:e2e":"vitest --run --project e2e --coverage.enabled=false","test:watch":"vitest --watch --project unit","typecheck":"tsc --noEmit -p tsconfig.typecheck.json","compile":"node scripts/gen-version.mjs && tsc","gen:version":"node scripts/gen-version.mjs"},"types":"./esm/index.d.ts","_id":"@openrouter/agent@0.11.0","bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","_integrity":"sha512-8idTYNZ4MjQyNJ2HKMTSmocVgQYajB43xG3rqa7ronK8Xfqx6XnIKvfhtqmAeJTPkhYppVuuQ+DKkMXlVEA+SA==","_resolved":"/tmp/39c1c55753395f051c030476ef6b0750/openrouter-agent-0.11.0.tgz","_from":"file:openrouter-agent-0.11.0.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-8idTYNZ4MjQyNJ2HKMTSmocVgQYajB43xG3rqa7ronK8Xfqx6XnIKvfhtqmAeJTPkhYppVuuQ+DKkMXlVEA+SA==","shasum":"9503ac385824628c21b47bc4d0c856e2b82e5f2b","tarball":"https://registry.npmjs.org/@openrouter/agent/-/agent-0.11.0.tgz","fileCount":267,"unpackedSize":1849950,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@openrouter%2fagent@0.11.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGEjs7ASykefmAGCYb+rUpIxfZEMLAbDFcFFPvIGqcVRAiBT+azDGbfL3jr8RsHYYjmt0Nt2HKZ/k/mGDGcyWutRSA=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:59127aa2-8d66-4ee6-b7ec-f2e48194db22"}},"directories":{},"maintainers":[{"name":"christineschen","email":"christinechensd@gmail.com"},{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},{"name":"davidlbowman-npm","email":"david@theinnovationlab.dev"},{"name":"lukehagar","email":"lukeslakemail@gmail.com"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agent_0.11.0_1787410191137_0.9259882400794548"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-01T16:43:16.521Z","modified":"2026-08-22T14:49:51.600Z","0.1.0":"2026-04-01T16:43:16.750Z","0.1.1":"2026-04-02T23:42:04.329Z","0.1.2":"2026-04-07T17:30:02.539Z","0.3.0":"2026-04-09T15:05:46.885Z","0.3.1":"2026-04-12T14:43:57.201Z","0.3.2":"2026-04-14T20:25:37.053Z","0.4.0":"2026-04-20T18:57:57.540Z","0.5.0":"2026-05-06T00:52:57.319Z","0.6.0":"2026-05-14T15:59:45.787Z","0.7.0":"2026-05-18T21:45:16.217Z","0.7.1":"2026-06-03T17:10:33.661Z","0.7.2":"2026-06-17T13:49:54.106Z","0.8.0":"2026-07-22T20:50:29.987Z","0.9.0":"2026-08-10T18:23:03.215Z","0.10.0":"2026-08-21T16:59:58.669Z","0.11.0":"2026-08-22T14:49:51.295Z"},"bugs":{"url":"https://github.com/OpenRouterTeam/typescript-agent/issues"},"author":{"name":"OpenRouter"},"license":"Apache-2.0","homepage":"https://github.com/OpenRouterTeam/typescript-agent#readme","keywords":["openrouter","agent","typescript","ai","tools","streaming","llm"],"repository":{"type":"git","url":"git+https://github.com/OpenRouterTeam/typescript-agent.git","directory":"packages/agent"},"description":"Agent toolkit for building AI applications with OpenRouter — tool orchestration, streaming, multi-turn conversations, and format compatibility.","maintainers":[{"name":"christineschen","email":"christinechensd@gmail.com"},{"name":"louisgv","email":"lgvichy@gmail.com"},{"name":"alexanderatallah","email":"alexander.atallah@gmail.com"},{"name":"sambarnes","email":"mistersamuelbarnes@gmail.com"},{"name":"shashankgoyal-or","email":"shashank.goyal@openrouter.ai"},{"name":"robertyeakel","email":"robert.yeakel@openrouter.ai"},{"name":"davidlbowman-npm","email":"david@theinnovationlab.dev"},{"name":"lukehagar","email":"lukeslakemail@gmail.com"},{"name":"mattapperson-or","email":"matt.apperson@openrouter.ai"}],"readme":"# OpenRouter Agent (Beta)\n\nAgent toolkit for building AI applications with [OpenRouter](https://openrouter.ai) — tool orchestration, streaming, multi-turn conversations, and format compatibility.\n\n> [!IMPORTANT]\n> This SDK is currently in beta. There may be breaking changes between versions.\n> We recommend pinning to a specific version in your `package.json`.\n\n## Installation\n\n```bash\n# npm\nnpm install @openrouter/agent\n\n# pnpm\npnpm add @openrouter/agent\n\n# bun\nbun add @openrouter/agent\n\n# yarn\nyarn add @openrouter/agent\n```\n\n> [!NOTE]\n> This package is ESM-only. If you are using CommonJS, you can use `await import('@openrouter/agent')`.\n\n## Quick Start\n\n```typescript\nimport OpenRouter from '@openrouter/sdk';\nimport { callModel, tool } from '@openrouter/agent';\nimport { z } from 'zod';\n\nconst client = new OpenRouter({ apiKey: 'YOUR_API_KEY' });\n\nconst weatherTool = tool({\n  name: 'get_weather',\n  description: 'Get the current weather for a location',\n  inputSchema: z.object({ location: z.string() }),\n  execute: async ({ location }) => ({\n    temperature: 72,\n    condition: 'sunny',\n    location,\n  }),\n});\n\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'What is the weather in San Francisco?',\n  tools: [weatherTool] as const,\n});\n\n// Get the final text response (tools are auto-executed)\nconst text = await result.getText();\nconsole.log(text);\n```\n\n## Optional subpaths\n\n- `@openrouter/agent/tool-set` provides declarative tool activation.\n- `@openrouter/agent/mcp` provides MCP discovery, caching, and tool wrapping. Install its optional client when using MCP:\n\n```bash\npnpm add @openrouter/agent @modelcontextprotocol/client\n```\n\nExisting `@openrouter/mcp` imports remain supported as compatibility facades.\n\n## Features\n\n### Multiple Response Consumption Patterns\n\n`callModel` returns a `ModelResult` that supports many ways to consume the response — all usable concurrently on the same result:\n\n```typescript\nconst result = callModel(client, { model, input, tools });\n\n// Await the final text\nconst text = await result.getText();\n\n// Await the full response with usage data (the FINAL round only)\nconst response = await result.getResponse();\nconsole.log(response.usage); // { inputTokens, outputTokens, cost, ... }\n\n// Await aggregate usage across EVERY round of the tool loop\nconst usage = await result.getUsage();\nconsole.log(usage); // { modelCalls, inputTokens, outputTokens, totalTokens, cachedTokens, reasoningTokens, cost? }\n\n// Stream text deltas\nfor await (const delta of result.getTextStream()) {\n  process.stdout.write(delta);\n}\n\n// Stream reasoning deltas\nfor await (const delta of result.getReasoningStream()) {\n  process.stdout.write(delta);\n}\n\n// Stream tool-call argument deltas (plus preliminary results from generator tools)\nfor await (const event of result.getToolStream()) {\n  console.log(event); // { type: 'delta', content: '...' } | { type: 'preliminary_result', ... }\n}\n\n// Stream all response events, including tool execution results\nfor await (const event of result.getFullResponsesStream()) {\n  // includes tool.result / tool.call_output events\n}\n\n// Stream structured tool calls\nfor await (const toolCall of result.getToolCallsStream()) {\n  console.log(toolCall.name, toolCall.input);\n}\n\n// Get all tool calls after completion\nconst toolCalls = await result.getToolCalls();\n```\n\nWhat each stream emits:\n\n| Method | Emits |\n|---|---|\n| `getTextStream()` | assistant text deltas |\n| `getReasoningStream()` | reasoning deltas |\n| `getToolStream()` | tool-call **argument deltas**; `preliminary_result` events for generator tools — *not* execution results |\n| `getToolCallsStream()` | parsed tool calls as they complete |\n| `getItemsStream()` | all output items (messages, function calls, …) — output items **only**, no usage/response metadata |\n| `getFullResponsesStream()` | every response event, including `tool.result` / `tool.call_output` execution events, and each round's `response.completed` (with that round's usage block) |\n\n#### Replay history for stream consumers\n\nEvery stream getter above can start from event zero, so by default a result\nretains its full event history for the lifetime of the call. Long generator-tool\nstreams make that history expensive in a constrained runtime. When all consumers\nattach before draining, `streamReplay: 'active-consumers'` releases buffered\nevents once every attached consumer has advanced past them:\n\n```typescript\nconst result = callModel(client, {\n  model,\n  input,\n  // Default 'full' retains complete replay history for delayed and\n  // sequential consumers; 'active-consumers' trades that for bounded memory.\n  streamReplay: 'active-consumers',\n});\n```\n\n#### Usage across a multi-round tool loop\n\n`getResponse()` resolves to the **final** round's response, so in a\nmulti-round tool loop the tokens spent on the intermediate `tool_calls`\ngenerations are not in `response.usage`. `getItemsStream()` carries output\nitems only and never surfaces `response.completed`, so usage is not reachable\nfrom that stream either.\n\n`getUsage()` closes the gap with aggregate totals across every model call the\nrun made — the initial request, each tool-round follow-up, the empty-final\nretry, the `allowFinalResponse` final turn, and approval-resume requests:\n\n```typescript\nconst result = callModel(client, { model, input, tools });\n\nfor await (const item of result.getItemsStream()) {\n  render(item);\n}\n\nconst usage = await result.getUsage();\nconsole.log(usage.modelCalls, usage.totalTokens, usage.cost);\n```\n\nIt gates on run completion like `getResponse()` does, so the totals are final\nwhether you await it directly, after `getResponse()`, or after draining any of\nthe streaming getters. Unlike `getResponse()` it never rejects — a failed run\nstill consumed tokens — and returns the totals accrued so far, with\n`modelCalls: 0` and zeroed tokens when no model call completed. `cost` is\npresent only when the server reported cost accounting.\n\nSame `SessionUsageTotals` shape and numbers as the `SessionEnd` hook's\n`totalUsage`. For **per-call** granularity use the `PostModelCall` hook (one\nemit per model call, with `turnType`/`turnNumber`) or read each round's\n`response.completed` off `getFullResponsesStream()`.\n\n### Tool Types\n\nThe `tool()` factory creates type-safe tools with full Zod schema inference. In addition to the legacy kinds below, the unified `run` interface with `lifecycle: 'sync' | 'background' | 'deferred'` covers [async tools](#async-tools) whose results arrive after the tool round, and `tool.agent()` creates [subagent tools](#agent-tools-subagents).\n\n**Regular tools** — automatically executed by the agent loop:\n\n```typescript\nconst searchTool = tool({\n  name: 'search',\n  description: 'Search the web',\n  inputSchema: z.object({ query: z.string() }),\n  outputSchema: z.object({ results: z.array(z.string()) }),\n  execute: async ({ query }) => {\n    const results = await performSearch(query);\n    return { results };\n  },\n});\n```\n\n**Generator tools** — stream intermediate events during execution:\n\n```typescript\nconst analysisTool = tool({\n  name: 'analyze',\n  inputSchema: z.object({ data: z.string() }),\n  eventSchema: z.object({ progress: z.number() }),\n  outputSchema: z.object({ summary: z.string() }),\n  execute: async function* ({ data }) {\n    yield { progress: 0.5 };\n    // ... processing ...\n    return { summary: 'Analysis complete' };\n  },\n});\n```\n\n**Manual tools** — reported to the model but not auto-executed (for human-in-the-loop flows):\n\n```typescript\nconst confirmTool = tool({\n  name: 'confirm_action',\n  inputSchema: z.object({ action: z.string() }),\n  execute: false,\n});\n```\n\n**Forced tool choices** are one-shot for each resolved semantic value. After a\nforced choice produces a tool call, unchanged follow-up choices are relaxed to\n`auto` so the model can either call another tool or answer in text. A dynamic\nchoice re-arms when it resolves to a different value (or after an unforced\nturn):\n\n```typescript\ncallModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Plan, research, then submit.',\n  tools: [planTool, searchTool, submitTool] as const,\n  toolChoice: ({ numberOfTurns }) =>\n    numberOfTurns === 0\n      ? { type: 'function', name: 'plan' }\n      : numberOfTurns === 3\n        ? { type: 'function', name: 'submit' }\n        : 'auto',\n});\n```\n\n### Stop Conditions\n\nControl when the agent loop stops executing tools:\n\n```typescript\nimport { callModel, stepCountIs, hasToolCall, maxTokensUsed, maxCost } from '@openrouter/agent';\n\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Research this topic thoroughly',\n  tools: [searchTool, summarizeTool] as const,\n  // Single condition\n  stopWhen: stepCountIs(10),\n  // Or combine multiple (stops when ANY condition is met)\n  stopWhen: [stepCountIs(10), maxCost(0.50), hasToolCall('summarize')],\n});\n```\n\nBuilt-in stop conditions:\n\n| Condition | Description |\n|---|---|\n| `stepCountIs(n)` | Stop after `n` tool execution steps (default: 5) |\n| `hasToolCall(name)` | Stop when a specific tool is called |\n| `maxTokensUsed(n)` | Stop when total tokens exceed a threshold |\n| `maxCost(dollars)` | Stop when total cost exceeds a dollar amount |\n| `finishReasonIs(reason)` | Stop on a specific finish reason |\n\n**Final response after stop**\n\nWhen `stopWhen` fires while the model is still emitting tool calls, the\nloop makes one more model turn with `toolChoice: 'none'` so the run ends\nwith a natural-language answer instead of a half-finished tool call.\nTools stay in the request — only calling is forbidden — which preserves\nthe prompt-cache prefix. **This is on by default**; `allowFinalResponse`\ntunes it:\n\n```typescript\ncallModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Research this topic',\n  tools: [searchTool] as const,\n  stopWhen: stepCountIs(5),\n  // default (omitted or `true`): appends DEFAULT_FINAL_RESPONSE_DIRECTIVE\n  // as a final user message so the model writes an answer instead of\n  // attempting another tool call\n\n  // override the directive wording:\n  // allowFinalResponse: 'Please summarize what you found.',\n  // append no message (turn still happens, calls still forbidden):\n  // allowFinalResponse: '',\n  // disable the final turn entirely:\n  // allowFinalResponse: false,\n});\n```\n\nThe pending tool calls from the halted turn are executed first so they\nhave real outputs in the input, then the full conversation and the\noriginal `instructions` are sent with `toolChoice: 'none'`. Any\nnon-executable (manual) tool calls in the halted turn are paired with\nsynthesized stub `function_call_output` items so the input is well-formed.\n\n### Cancellation & Request Timeouts\n\nA `callModel` run is a *sequence* of API requests (initial, one per tool\nround, plus final/retry turns). Two independent bounds compose:\n\n```typescript\nconst controller = new AbortController();\n\nconst result = callModel(\n  client,\n  {\n    model: 'openai/gpt-4o',\n    input: 'Research this topic',\n    tools: [searchTool] as const,\n    // Cancel the WHOLE run: stops the loop at the next turn boundary and\n    // aborts the in-flight request/stream. Promises reject with the\n    // signal's abort reason.\n    signal: controller.signal,\n  },\n  {\n    // Bound EACH request: a provider that stalls fails that request after\n    // 90s instead of hanging until an outer timeout kills the process.\n    timeoutMs: 90000,\n  },\n);\n\n// later, e.g. on user navigation:\ncontroller.abort(new Error('user cancelled'));\n```\n\n`timeoutMs` is per-request (each dispatch gets a fresh budget), not\nper-run — bound the run with `stopWhen` (`maxCost`, `maxTokensUsed`,\n`stepCountIs`) and/or `signal`. The two compose: when both are set, each\nrequest is bounded by whichever fires first. Prefer the `signal` option\nover passing a raw `signal` through `RequestOptions` — the underlying SDK\nskips its `timeoutMs` wiring whenever a request already carries a signal,\nso the engine re-composes them for you only on the `signal` option path.\n\n### Doom-Loop Detection\n\nCatch runs that stop making progress while continuing to spend: the model\nre-issuing the same tool call with identical arguments (including repeated\n*empty* calls and repeated invalid-JSON calls), repeating identical\nserver-tool requests, or emitting the same text tokens over and over. Off by\ndefault; opt in with `doomLoop: true`:\n\n```typescript\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Research this topic',\n  tools: [searchTool, bashTool] as const,\n  doomLoop: true, // recommended defaults: observe@2, block@3, stop@6\n  // or tune it:\n  // doomLoop: {\n  //   ladder: { observe: 2, steer: false, block: 3, stop: 6 },\n  //   text: { minRepeats: 4 }, // or `false` to disable text detection\n  // },\n});\n\n// Was the run stopped by detection?\nconst verdict = await result.getDoomLoopVerdict();\nif (verdict) console.warn(verdict.message);\n```\n\nDetection is **deterministic** — a verdict is a pure function of the\ntranscript, so the same sequence of calls/text always fires at the same\npoint. Repeated **rounds** build a per-tool streak: interleaved calls to\n*other* tools don't reset it, and N identical calls fanned out in parallel\nwithin ONE round count once (a streak measures the model re-issuing a call\n*after seeing its result*, which requires a round trip).\n\nTwo kinds of evidence accumulate side by side, and the stronger one decides:\n\n- **Round-set streaks.** A round's identity for one tool is the **set** of\n  calls it made, so a fan-out of *distinct* arguments reissued verbatim\n  counts: `read(a), read(b), read(c)` every round accumulates. Ordering\n  within the round is irrelevant, and a round whose membership changes — in\n  either direction — resets this streak, since adding or dropping work is\n  progress for the round as a unit.\n- **Per-call streaks.** Each `(tool, arguments)` identity also counts its own\n  consecutive rounds, whatever its round-mates did. A call repeating inside\n  varying company (`[a,b]`, `[a,c]`, `[a,d]` — `a` is a 3-peat) is flagged\n  even though every round's set differs, and a repeat spanning an approval\n  pause keeps counting when the paused member drops from the resumed round.\n  For an exactly-repeating round both counts are equal, so nothing\n  double-fires.\n\nWhen a repeating fan-out crosses a rung, every call in the round gets the\nverdict (so `block` stops the whole fan-out, not just one member), and calls\ncarrying the SAME evidence share byte-identical text — the `steer` rung\ndedupes on exact text, so one piece of evidence injects one correction. A\nround can carry two pieces of evidence at once (`[a]`, `[a,b]`, `[a,b]`: by\nround 3, `a` is a 3-peat call while `{a,b}` is a 2-peat set), in which case\neach renders its own message — at most two per tool per round, each stating\na distinct fact. When the per-call count alone crosses a rung, only that\ncall is refused and genuinely new round-mates run free. The streak crosses\na graduated ladder — strongest crossed rung wins:\n\n| Action | Effect |\n|---|---|\n| `observe` | Emit the `DoomLoopDetected` hook only |\n| `steer` | Inject a corrective user message before the next turn (off by default). Guidance queued right before a pause persists in `ConversationState.doomLoop.pendingSteer` and is delivered on resume |\n| `escalate` | **Recover by throwing more intelligence at the next turn** (off by default; requires an `escalation` config). One-turn overrides — a stronger model and/or a forced `openrouter:advisor` consult — then automatic revert. Bounded by `escalation.maxEscalations` (default 2, persisted across resumes); exhausted or unconfigured escalations fall through to weaker rungs |\n| `block` | Refuse the call and return an explanatory error as the tool output — the model sees why and can change course. Not applicable to text or server-tool verdicts (already emitted/executed); those downgrade to `observe` |\n| `stop` | Halt the loop before any further model request (`SessionEnd.reason: 'doom_loop'`). Unresolved tool calls in the final turn get synthesized halt-error outputs so persisted history stays well-formed and resumable |\n\n**Escalation recovery** — instead of (or before) blocking, unblock the loop\nby escalating the next turn, then return to the cheap model:\n\n```typescript\ncallModel(client, {\n  model: 'z-ai/glm-5.2', // the everyday executor\n  input: 'Research this topic',\n  tools: [searchTool] as const,\n  doomLoop: {\n    ladder: { observe: 2, escalate: 3, block: 5, stop: 8 },\n    escalation: {\n      // Either or both:\n      model: 'anthropic/claude-opus-4.6',  // run the NEXT turn on a stronger model\n      advisor: true,                        // and/or force an openrouter:advisor consult\n      maxEscalations: 2,                    // spend cap for the whole conversation\n    },\n  },\n});\n```\n\nOn an `escalate` verdict the engine (1) injects a user notice naming the\ndetected loop, and (2) applies one-turn request overrides: `model` is\nswapped for that dispatch only, and/or the `openrouter:advisor` server tool\nis appended with `forwardTranscript: true`, loop-diagnosing instructions,\nand `toolChoice` pinned to it (`allowed_tools`/`required`) so the stuck\nmodel must consult the advisor before doing anything else. `advisor` may\nalso be an object passed through as the advisor tool's parameters\n(`model`, `instructions`, `maxToolCalls`, ...). The following turn reverts\nautomatically. Budget is consumed when a recovery is *applied* (not at\nverdict time) and `escalationsUsed` persists in `ConversationState.doomLoop`\nso a resumed run cannot reset it; concurrent detector verdicts in one window\nescalate once.\n\nLadder configs are sanity-checked at resolve time: enabling `block` with\n`stop: false` warns (a model that keeps re-issuing a blocked call loops\nuntil `stopWhen` fires — and `stopWhen` defaults to unbounded), dead\nrungs (a weaker threshold at or past an enabled stronger one) warn, and so\ndoes an `escalate` rung without an `escalation` config (or vice versa).\n\n**Tools declare what identifies a call** via `loopKey` on the tool\ndefinition — a computed function over the call's validated arguments, like\nevery other tool hook, or `false` to exempt:\n\n```typescript\n// Compute the identity — a web-search tool normalizes its query.\ntool({\n  name: 'web_search',\n  inputSchema: z.object({ query: z.string() }),\n  loopKey: ({ query }) => query.trim().toLowerCase(),\n  execute: async ({ query }) => search(query),\n});\n\n// Return the subset of fields that matter. A bash call is identified by\n// the command AND where it runs; other fields (e.g. verbose) don't count.\ntool({\n  name: 'bash',\n  inputSchema: z.object({ command: z.string(), cwd: z.string(), verbose: z.boolean() }),\n  loopKey: ({ command, cwd }) => ({ command, cwd }),\n  execute: async ({ command, cwd }) => run(command, cwd),\n});\n\n// false: statically exempt — repetition is this tool's job.\ntool({\n  name: 'check_status',\n  inputSchema: z.object({ jobId: z.string() }),\n  loopKey: false,\n  execute: async ({ jobId }) => poll(jobId),\n});\n```\n\nA function-form `loopKey` may return `null` to exempt an individual call.\nReturning `undefined`, throwing, or returning unhashable material (bigint,\ncircular, >64 levels deep) falls back to the full-arguments identity with a\nwarning — detection never fails a run. Without any `loopKey`, the full\nvalidated arguments object is the identity. A field-name array\n(`loopKey: ['command', 'cwd']`) is also accepted — data rather than code,\nso it can be serialized into MCP tool caches and advertised over the wire\nvia `_meta['openrouter/loopKey']`. MCP-wrapped tools accept a `loopKey`\nvia `markMcp(tool, { loopKey })` or the `loopKeys` map on\n`createMCPTools`.\n\n> **Exempt tools that repeat by design — including repeating *fan-outs*.**\n> The detector compares arguments, not results, so a call whose arguments are\n> stable while its results change is indistinguishable from a loop. Since a\n> round's identity is now the whole *set* of a tool's calls, this covers\n> parallel shapes too: an agent that re-reads the same context files at the\n> start of every turn, or fans out a fixed set of pollers, accumulates a streak\n> and is refused at the default `block` rung from round 3 — and because every\n> call in the round gets the verdict, that is N synthesized error outputs per\n> round, not one. These shapes were invisible before this behavior existed, so\n> `loopKey: false` (or a `loopKey` returning `null`) is the opt-out for any\n> tool whose repetition is legitimate.\n\n**Fingerprints are a cross-port contract**: key material is canonicalized\nper RFC 8785 (JCS) and hashed with SHA-256 over the UTF-8 bytes, so the\nPython/Go ports produce identical fingerprints — they MUST use an RFC 8785\nimplementation (pip `jcs`, `cyberphone/json-canonicalization`), not their\nstdlib JSON serializer. The conformance vectors live in\n`tests/vectors/doom-loop-fingerprints.json`. Key order never defeats\ndetection; malformed calls count too (a model stuck emitting the same\ninvalid JSON trips the detector instead of bouncing off the parse error\nforever). Detector state is plain JSON inside `ConversationState.doomLoop`,\nso streaks survive serialize → resume **when the resuming call also passes\n`doomLoop`**. A `stop` verdict persists across decision-only resumes\n(`approveToolCalls`/`rejectToolCalls`); a fresh conversational turn clears\nit (streaks are kept, so renewed repetition re-condemns quickly).\n\nThe `DoomLoopDetected` hook observes every verdict and can override the\naction per event (`overrideAction`, last handler wins) — de-escalate a\nblock to observe for a known-chatty tool, or escalate straight to stop.\n\n**What this does NOT catch** (documented limits, locked by negative tests):\n\n- **Varying-input loops.** The fingerprint is identity-based: a model that\n  invents a fresh nonce/timestamp field each call evades the default\n  whole-arguments identity entirely. A `loopKey` that returns the meaningful\n  fields closes this per tool; the structural fix (outcome hashing — the\n  progress-ledger detector from the design doc) is planned, not shipped.\n- **Paraphrased repetition.** Text detectors require exact repeated token\n  blocks (within a response) or byte-identical whitespace-normalized text\n  (across steps). Semantically-identical rephrasings do not trip.\n- **Pre-mutation identity.** Fingerprints are computed on the arguments the\n  MODEL issued, before any `PreToolUse` mutation — a hook that rewrites\n  varying inputs into identical ones does not make them count as repeats\n  (and a hook that injects a nonce cannot mask real repetition).\n- **Manual/client-executed calls** pause the loop for the caller and are\n  not recorded (only executed, blocked, and parse-error calls are\n  evidence).\n- **Cross-tool round patterns.** Streaks are per tool: a loop alternating\n  BETWEEN tools with no per-tool repetition (`read(a)` one round, `grep(a)`\n  the next, forever) shows each tool a sparse pattern its own evidence\n  cannot condemn. Interleaved calls to other tools never *reset* a tool's\n  streak, so an every-other-round repeat still accumulates — slowly.\n\n### Async Tools\n\nOne `tool()` shape covers every execution lifecycle. Write an ordinary `run` — an async function or an async generator — and say what kind it is with `lifecycle`:\n\n- **`'sync'`** (default): awaited in the round, exactly like `execute`.\n- **`'background'`**: the loop keeps going. Work settling within the grace window (`graceMs`, default 250ms) behaves like a plain sync call; otherwise the model immediately receives a pending placeholder and the return value is injected as a `tool_task_result` message when it settles.\n- **`'deferred'`**: `run` returns `ctx.defer(taskId)` to park the call on durable external work — the run pauses (`status: 'awaiting_async_tools'`) until the task is completed from any process. Returning a plain value resolves immediately.\n\nGenerator `run` yields become the task's **log** (feeding check-ins, `tool.preliminary_result` events, and transcripts); the generator's **return** is the result, validated against `outputSchema`. Non-generator bodies log with `ctx.log()`.\n\n```typescript\nconst renderVideo = tool({\n  name: 'render_video',\n  lifecycle: 'background',\n  inputSchema: z.object({ script: z.string() }),\n  outputSchema: z.object({ url: z.string() }),\n  ack: 'Rendering started.',\n  timeoutMs: 300_000,\n  run: async function* ({ script }, ctx) {\n    const job = await renderer.start(script, { signal: ctx?.signal });\n    ctx?.onMessage((msg) => job.reprioritize(msg));       // steering opt-in\n    for await (const p of job.progress()) yield { pct: p };\n    return job.result();\n  },\n});\n\nconst legalReview = tool({\n  name: 'request_legal_review',\n  lifecycle: 'deferred',\n  inputSchema: z.object({ contractId: z.string() }),\n  outputSchema: z.object({ approved: z.boolean() }),\n  run: async ({ contractId }, ctx) => {\n    const ticket = await legal.open(contractId, { conversationId: ctx?.conversationId });\n    return ctx!.defer(ticket.id);\n  },\n});\n```\n\nDeferred completion is typed and lives on the tool — callable from any process holding the `StateAccessor`:\n\n```typescript\n// webhook handler — hours later, different process\nawait legalReview.resolve(client, {\n  state: makeAccessor(conversationId),\n  taskId: ticketId,\n  output: { approved: true },        // ← typechecked against outputSchema\n  run: { model: 'openai/gpt-4o' },   // continue immediately (omit to record-only)\n});\n```\n\n`legalReview.fail(...)` / `legalReview.cancel(...)` complete the surface; double resolution throws `ToolTaskAlreadySettledError`. The low-level `resumeToolResults()` handles batches. When a run would end with background work in flight, `asyncTools.onRunEnd` decides: `'drain'` (default), `'detach'`, or `'cancel'`.\n\n> **Security:** `.resolve()` injects a value the model treats as a tool result. Authenticate the webhook before calling it — the SDK cannot do that for you. Outputs are validated against `outputSchema` at runtime as well as compile time.\n\n### Checking On Long-Running Tasks\n\nWhen any long-running tool is registered, the SDK appends **one universal `task` tool** to the request — a single static wire definition regardless of how many async tools exist (per-tool schemas are never augmented, so context cost stays constant). The pending placeholder tells the model to use it:\n\n```typescript\ntask({ taskId: \"task_7f3\" })                          // status: state, elapsed, last log\ntask({ taskId, view: \"logs\", tail: 5 })               // recent progress entries\ntask({ taskId, view: \"transcript\" })                  // full detail (agents: child conversation)\ntask({ taskId, action: \"steer\", message: \"...\" })     // send guidance to the running task\ntask({ taskId, action: \"result\" })                    // final result if settled, else status\ntask({ taskId, action: \"cancel\", reason: \"...\" })     // stop the task\n```\n\nCalls are engine-intercepted and dispatched to the **owning tool's** `check` config — the wire surface is universal, the handling stays tool-specific:\n\n```typescript\nconst renderVideo = tool({\n  name: 'render_video',\n  lifecycle: 'background',\n  // ... as above ...\n  check: {                                     // optional — SDK default when absent\n    schema: z.object({ focus: z.string().optional() }),   // validates task({ params })\n    execute: async (params, turnContext) => {\n      // turnContext.toolCallStatus           → 'working' | 'completed' | ...\n      // turnContext.accumulatedYieldedEvents → every run yield so far\n      // turnContext.task                     → { statusView, tailLogs, transcript, send, cancel }\n      if (params.focus) turnContext.task?.send(params.focus);\n      return turnContext.task?.statusView();\n    },\n  },\n});\n```\n\nWithout a custom `check`, the SDK default answers the three views (`status` / `logs` / `transcript`, truncated to `asyncTools.maxTranscriptChars`, default 20k). Task-tool calls are doom-loop-exempt, bypass per-tool concurrency/timeout gates, and never fire Pre/PostToolUse hooks — but a `PermissionRequest` hook denial recorded for the call IS honored, so a policy layer can veto `cancel`/`steer`. Disable entirely with `asyncTools: { checkins: false }` (placeholders then revert to \"do not call this tool again\"). The name `task` is reserved: `tool()` and `tool.agent()` reject it at definition time; a dynamically-built tool list that bypasses `tool()` and claims the name suppresses the built-in with a warning (and the engine routes `task` calls to that user tool instead of intercepting them).\n\nAfter a process restart, deferred tasks answer `status` from persisted state (including a bounded `lastLog`); full logs and transcripts are in-memory only and report an explanatory note instead.\n\n### Steering Running Tasks\n\n- **From code:** `result.sendToTask(taskId, message)` delivers into the run body's `ctx.onMessage` handler (queued until one registers). Deferred tasks throw — their work runs in an external system.\n- **From the model:** `task({ taskId, action: 'steer', message })` delivers directly, or expose custom `params` handled by `check.execute` with `turnContext.task.send(...)`.\n- **Agent tools** forward steering messages into the child conversation automatically (as user messages at the child's next turn boundary).\n\n### Agent Tools (Subagents)\n\n`tool.agent()` creates a tool whose work IS a child `callModel` conversation, running as a background task:\n\n```typescript\nconst researcher = tool.agent({\n  name: 'research_topic',\n  description: 'Deep-research a topic in the background.',\n  inputSchema: z.object({ topic: z.string() }),\n  outputSchema: z.object({ text: z.string() }),\n  agent: ({ topic }) => ({\n    model: 'openai/gpt-4o',\n    input: `Research: ${topic}`,\n    tools: [searchTool, fetchTool] as const,\n    stopWhen: stepCountIs(15),\n  }),\n  // default result mapper — Dennis-style last_message outcome:\n  result: async (child) => ({ text: await child.getText() }),\n});\n```\n\nThe parent keeps working while children run (several can run concurrently under the background pool). The child's conversation is the check-in **transcript**; each child turn is a **log** entry; `status` reports `turnsCompleted` and `currentActivity`. `cancelTask(taskId)` (or parent abort / `timeoutMs`) cancels the child; `sendToTask` steers it mid-run. Children run **in-memory** (no `StateAccessor`) and do not inherit the parent's hooks — pass child hooks explicitly in the `agent` spec if needed. A child that pauses (HITL/manual/approval/deferred tools inside it) fails the task with a clear error.\n\n### Strict Tool Schemas\n\nEvery client tool kind, including `tool.agent()`, accepts `strict: true` to\nrequest provider-enforced schema adherence for generated tool-call arguments.\nThe SDK faithfully converts the caller's `inputSchema`; it does not rewrite\nthe runtime Zod contract.\n\nOpenAI-style strict function calling requires every declared object property\nto appear in JSON Schema's `required` list. Use `.nullable()` for a value that\nmay be absent conceptually, because `.optional()` omits the property from\n`required`:\n\n```typescript\nconst weatherTool = tool({\n  name: 'get_weather',\n  inputSchema: z.object({\n    location: z.string(),\n    // The key is required, but the model may return null.\n    units: z.enum(['celsius', 'fahrenheit']).nullable(),\n  }),\n  strict: true,\n  execute: async ({ location, units }) => getWeather(location, units),\n});\n```\n\nThe SDK sends the generated schema unchanged. Providers validate it according\nto their own strict-mode dialect and the SDK propagates any API error. Use\n`.nullable()`, or set `strict: false` when omission is part of the tool's\ncontract. Provider support and strict-schema restrictions can vary.\n\n### Per-Tool Timeouts & Concurrency\n\nEvery tool kind accepts `timeoutMs` (per-execution deadline; the run-level `toolTimeoutMs` sets a default) and `maxConcurrency` (max simultaneous executions of that tool). On timeout the round stops waiting — the model receives `{ error, code: 'tool_timeout' }` and the tool's `ctx.signal` aborts; the timeout bounds the round's *wait*, not the tool body, so signal-ignoring bodies can't hang the run. `ctx.signal` also fires on run abort (`signal` option) and `ModelResult.cancel()`.\n\nRound-level parallelism (unbounded by default, matching previous behavior) is capped with `toolConcurrency`:\n\n```typescript\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'fan out',\n  tools: [searchTool] as const,\n  toolTimeoutMs: 30_000,\n  toolConcurrency: { round: 4, background: 8 },  // or a bare number for { round: n }\n});\n```\n\nExecution order may change under a cap; output order never does (results stay in call order for prompt-cache stability).\n\n### Tool Approval\n\nGate tool execution with approval checks for sensitive operations:\n\n```typescript\nconst deleteTool = tool({\n  name: 'delete_record',\n  inputSchema: z.object({ id: z.string() }),\n  requireApproval: true, // Always require approval\n  execute: async ({ id }) => { /* ... */ },\n});\n\n// Or use a function for conditional approval\nconst writeTool = tool({\n  name: 'write_file',\n  inputSchema: z.object({ path: z.string(), content: z.string() }),\n  requireApproval: ({ path }) => path.startsWith('/etc'),\n  execute: async ({ path, content }) => { /* ... */ },\n});\n\n// Handle approvals at the callModel level\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Delete record abc-123',\n  tools: [deleteTool] as const,\n  approveToolCalls: async (toolCalls) => {\n    // Return IDs of approved tool calls\n    return toolCalls.map(tc => tc.id);\n  },\n});\n```\n\n### Lifecycle Hooks\n\nObserve and control the agent loop with typed lifecycle hooks — inspect or\nblock tool calls, mutate inputs, gate approvals programmatically, intercept\nprompts, and run audit/telemetry work. Inspired by the Claude Agent SDK hooks\npattern.\n\n> [!NOTE]\n> Lifecycle hooks are distinct from the SDK **transport** hooks\n> (`SDKHooks`, `BeforeRequestHook`, `HookContext`, …), which intercept\n> HTTP requests. Lifecycle hooks fire on agent-loop events.\n\n**Two usage modes.** Pass a plain object for quick setup, or a `HooksManager`\ninstance for custom hooks, dynamic registration, and programmatic emit:\n\n```typescript\n// Inline config — built-in hooks only\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Clean up the temp directory',\n  tools: [shellTool] as const,\n  hooks: {\n    PreToolUse: [\n      {\n        matcher: 'run_shell', // string | RegExp | (toolName) => boolean\n        handler: ({ toolName, toolInput }) => {\n          if (String(toolInput.command).includes('rm -rf /')) {\n            return { block: 'Refusing to run a destructive command' };\n          }\n        },\n      },\n    ],\n    PostToolUse: [\n      { handler: ({ toolName, durationMs }) => console.log(toolName, durationMs) },\n    ],\n  },\n});\n```\n\n```typescript\n// HooksManager — full control\nimport { HooksManager } from '@openrouter/agent';\n\nconst hooks = new HooksManager();\n\nconst unsubscribe = hooks.on('PreToolUse', {\n  matcher: /^db_/,\n  filter: (payload) => Object.keys(payload.toolInput).length > 0, // optional predicate on the payload\n  handler: ({ toolInput }, ctx) => {\n    console.log(`[${ctx.sessionId}] intercepting db tool`); // session id lives on the context\n    return {\n      // Replace the tool's input before execution (mutation piping)\n      mutatedInput: { ...toolInput, dryRun: true },\n    };\n  },\n});\n\nconst result = callModel(client, { model, input, tools, hooks });\n\n// Later: unsubscribe(), hooks.off(...), hooks.removeAll(...)\n```\n\n**Built-in hooks**\n\n| Hook | Fires | Result fields |\n|---|---|---|\n| `PreToolUse` | Before every client-tool execution (auto, approval-resume, and hook-allowed paths) | `mutatedInput` replaces the tool's arguments; `block: true \\| string` skips execution and reports the reason as the tool's error output |\n| `PostToolUse` | After a successful tool execution (payload includes `toolOutput`, `durationMs`) | none (void) |\n| `PostToolUseFailure` | After a tool execution throws or returns an error. Not fired when a tool never ran (`PermissionRequest` deny, user rejection, `PreToolUse` block) — observe those via the gating hooks themselves | none (void) |\n| `UserPromptSubmit` | Before the initial API request, with the user prompt string | `mutatedPrompt` replaces the prompt; `reject: true \\| string` aborts the call with an error |\n| `PermissionRequest` | When a tool requires approval, before pausing for the human gate | `decision: 'allow'` skips the gate (the tool runs once via the normal round), `'deny'` synthesizes a rejected result without executing, `'ask_user'` (default) falls through to the approval flow. Last handler wins. Payload includes a `riskLevel` derived from the approval gate's shape (`'high'` for tool- or call-level functions, `'medium'` for blanket `true`) |\n| `Stop` | When a `stopWhen` condition halts the loop (`reason: 'max_turns'`) | `forceResume: true` continues the loop (capped at 3 consecutive overrides without tool progress — a bare `forceResume` that changes no state will typically re-trigger the stop condition immediately and burn through the cap, so pair it with `appendPrompt` or external state the stop condition observes); `appendPrompt` injects a user message for the next turn (honored independently of `forceResume`). Blocked/rejected tool outputs count as progress for the cap: the model receives that feedback, and each round costs a full request, so the loop cannot spin hot |\n| `SessionStart` | Once per run, before the initial request. `config` summarizes the session (`hasTools`, `hasApproval`, `hasState`) | none (void) |\n| `SessionEnd` | Once per run, on every exit path — completion, approval pause, interruption, error, and the no-tools streaming paths. `reason` is `'complete' \\| 'error' \\| 'max_turns' \\| 'user' \\| 'doom_loop'`. When at least one model call completed, `totalUsage` aggregates tokens/cost across all of them (`modelCalls`, `inputTokens`, `outputTokens`, `totalTokens`, `cachedTokens`, `reasoningTokens`, and `cost` when the server reported it) | none (void) |\n| `PostModelCall` | Once per completed model response, on **every** request the loop makes — initial, each tool-round follow-up, the empty-final retry, the `allowFinalResponse` final turn, and approval-resume requests. Payload: `responseId` (the OpenRouter generation id), `model`, `durationMs` (dispatch → fully materialized response, including stream consumption), `turnType` (`'initial' \\| 'resume' \\| 'tool_round' \\| 'final' \\| 'retry'`), `turnNumber`, and `usage` (`inputTokens`, `outputTokens`, `totalTokens`, `cachedTokens`, `reasoningTokens`, `cost?`) when the server reported usage accounting. Purely observational — the telemetry primitive for tracing/benchmark consumers: one span per model call | none (void) |\n| `DoomLoopDetected` | Every time doom-loop detection crosses a ladder rung, once per `(tool, fingerprint)` per round — *identical* parallel duplicates in one round share the event, but a repeating fan-out of DISTINCT arguments emits one event per member, since each is its own `(tool, fingerprint)` (requires the `doomLoop` option). Payload: `detector` (`'tool-fingerprint' \\| 'server-tool-fingerprint' \\| 'text-repetition' \\| 'text-streak'`), the resolved `action` (`'observe' \\| 'steer' \\| 'escalate' \\| 'block' \\| 'stop'`), the `streak`, the `fingerprint`, `toolName`/`toolInput` for tool verdicts, and the explanatory `message` | `overrideAction` replaces the engine's resolved action for this event (last handler wins); `block` on a text or server-tool verdict downgrades to `observe`; `escalate` without an `escalation` config or remaining budget downgrades to `observe` |\n\nNotes on lifecycle pairing: `SessionEnd` only fires when a matching\n`SessionStart` succeeded, and at most once per run. Pending async hook work is\nalways drained on teardown — including on paths that skip `SessionStart`,\nsuch as resuming from a tool approval. A throwing `SessionEnd` handler never\nmasks the run's original error (teardown failures are logged as warnings).\n\nOn no-tools streaming paths the initial response is only materialized when the\nstream is consumed, so `PostModelCall` for that response fires during session\nteardown (before `SessionEnd`). A stream that fails or errors before producing\na materialized response emits no `PostModelCall`; a `response.incomplete`\nresponse (e.g. truncated at `max_output_tokens`) **does** emit — it carries a\nreal generation id and consumed tokens. Note `usage.cost` is only present when\nthe request had usage accounting enabled server-side.\n\n`SessionEnd.totalUsage` is push-based; for the same totals without registering\na hook, await [`getUsage()`](#usage-across-a-multi-round-tool-loop).\nEvery handler receives `(payload, context)` — `context` carries the\n`sessionId` (the single source of session identity; payloads do not repeat\nit), the `hookName`, and an `AbortSignal` for cooperative cancellation. The\nengine threads the session id into each emit's context, so a single\n`HooksManager` instance can be shared safely across concurrent `callModel`\nruns — each run's handlers see that run's id. (If you call `emit()` yourself\non a shared manager, pass `{ sessionId }` in the emit context; the\n`setSessionId()` default is a single mutable field and is last-writer-wins.)\n\n**Handler chain semantics**\n\nHandlers for a hook run sequentially in registration order.\n\n- **Matchers** (`matcher`) scope a handler to tool names — exact string,\n  `RegExp` (stateful `/g`//`/y` flags are handled safely), or a predicate\n  function (truthy/falsy returns are coerced to boolean). Matchers fail\n  closed: a matcher-scoped handler is skipped when the emit has no tool name.\n- **Filters** (`filter`) are arbitrary predicates on the payload.\n- **Mutation piping**: a handler's `mutatedInput`/`mutatedPrompt` replaces the\n  corresponding payload field for all subsequent handlers in the chain, and\n  for the tool/request itself. A blocking handler's mutation still lands\n  before the short-circuit.\n- **Short-circuit**: `block`/`reject` with `true` or a non-empty string stops\n  the chain (empty strings do not block).\n- **Error policy**: by default a throwing handler — or a throwing\n  matcher/filter — is logged as a warning and skipped, and the chain\n  continues. Construct the manager with\n  `new HooksManager(custom, { throwOnHandlerError: true })` to propagate\n  errors instead (useful in tests).\n\n**Async fire-and-forget handlers**\n\nA handler can detach background work (telemetry, audit writes) without\nblocking the loop by returning an `AsyncOutput` signal:\n\n```typescript\nhooks.on('PostToolUse', {\n  handler: (payload, ctx) => ({\n    async: true,\n    work: sendTelemetry(payload, { signal: ctx.signal }),\n    asyncTimeout: 5_000, // default 30_000\n  }),\n});\n\n// On shutdown: abort in-flight handlers, then wait for detached work\nhooks.abortInflight('shutdown');\nawait hooks.drain();\n```\n\n`drain()` waits for all detached work, bounded per-handler by `asyncTimeout`.\nOn timeout the emit's `ctx.signal` is aborted and a warning is logged — the\nwork itself cannot be forcibly cancelled, so handlers should observe\n`ctx.signal` to stop cooperatively. `abortInflight()` reaches detached work\neven after the originating `emit()` has returned. The signal object must have\n**exactly** the `AsyncOutput` shape (`async: true` plus optional `work`/\n`asyncTimeout`); a return value carrying any other field is treated as a\nregular result so mutations/blocks are never silently discarded. The\n`isAsyncOutput` type guard is exported.\n\n**Custom hooks**\n\nDefine your own hooks with Zod schema pairs and full type inference, then emit\nthem from your own code:\n\n```typescript\nimport { HooksManager } from '@openrouter/agent';\nimport { z } from 'zod/v4';\n\nconst hooks = new HooksManager({\n  DeploymentGate: {\n    payload: z.object({ environment: z.string(), version: z.string() }),\n    result: z.object({ approved: z.boolean() }),\n  },\n  AuditLog: {\n    payload: z.object({ event: z.string() }),\n    result: z.void(), // side-effect only: results are not validated\n  },\n});\n\nhooks.on('DeploymentGate', {\n  handler: ({ environment }) => ({ approved: environment !== 'production' }),\n});\n\nconst { results } = await hooks.emit('DeploymentGate', {\n  environment: 'staging',\n  version: '1.2.3',\n});\n```\n\nPayloads and results are validated against the schemas on every `emit`.\nSchemas with `.transform()`, `.default()`, or `.coerce` are honored: handlers\nreceive the parsed **output** values, matching the inferred TypeScript types.\nCustom hook names must be non-empty and must not collide with built-in names.\nCustom hooks do not participate in mutation piping or blocking (those are\nbuilt-in-only behaviors); the inline config surface only accepts built-in\nhooks — unknown names are warned about and skipped.\n\nA payload validation failure follows the same error policy as handlers:\nlogged and skipped by default, thrown in strict mode.\n\n### Tool Context\n\nProvide typed context data to tools without passing it through the model:\n\n```typescript\nconst dbTool = tool({\n  name: 'query_db',\n  inputSchema: z.object({ sql: z.string() }),\n  contextSchema: z.object({ connectionString: z.string() }),\n  execute: async ({ sql }, ctx) => {\n    // Tool context is available on ctx.local\n    const db = connect(ctx?.local.connectionString);\n    return db.query(sql);\n  },\n});\n\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'List all users',\n  tools: [dbTool] as const,\n  context: {\n    query_db: { connectionString: 'postgres://localhost/mydb' },\n  },\n});\n```\n\n### Shared Context\n\nShare mutable state across all tools in a conversation. When typing\n`ctx.shared` explicitly, use the curried `tool<SharedContext>()({...})` form:\n\n```typescript\ntype SharedContext = {\n  processedIds: string[];\n};\n\nconst processItem = tool<SharedContext>()({\n  name: 'process_item',\n  inputSchema: z.object({ id: z.string() }),\n  execute: async ({ id }, ctx) => {\n    ctx?.setSharedContext({ processedIds: [...ctx.shared.processedIds, id] });\n    return { processed: id };\n  },\n});\n\nconst result = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Process these items',\n  tools: [processItem] as const,\n  sharedContextSchema: z.object({ processedIds: z.array(z.string()) }),\n  context: {\n    shared: { processedIds: [] },\n  },\n});\n```\n\nThe direct `tool<SharedContext>({...})` syntax remains supported for backward\ncompatibility, but its returned tool name is typed as `string`. TypeScript\ncannot partially infer trailing generics after an explicit `TShared`, so the\ncurried form is required when event types must correlate on a literal tool name.\nCalls without an explicit shared-context type, such as `tool({...})`, continue\nto infer literal names normally.\n\n### Conversation State Management\n\nPersist multi-turn conversations with full state tracking. The `state`\noption takes a `StateAccessor` — a `{ load, save }` pair over any storage\nbackend (memory, SQLite, Redis, …). The loop calls `load` before the run and\n`save` as the conversation progresses:\n\n```typescript\nimport { callModel, type ConversationState, type StateAccessor } from '@openrouter/agent';\n\n// Any storage backend — here, a simple in-memory holder\nlet stored: ConversationState | null = null;\nconst state: StateAccessor = {\n  load: async () => stored,\n  save: async (s) => {\n    stored = s;\n  },\n};\n\n// First turn\nconst result1 = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Search for TypeScript best practices',\n  tools: [searchTool] as const,\n  state,\n});\nawait result1.getText();\n\n// Read the updated state (messages, tool calls, status, metadata)\nconst snapshot = await result1.getState();\n\n// Continue the conversation — the accessor loads the saved history\nconst result2 = callModel(client, {\n  model: 'openai/gpt-4o',\n  input: 'Now summarize what you found',\n  tools: [searchTool] as const,\n  state,\n});\n```\n\n`ConversationState` is plain JSON — `JSON.stringify`/`JSON.parse` it into any\nstore (this is how serverless/cold-start resume works).\n\n### Dynamic Parameters Between Turns\n\nAdjust model parameters dynamically based on tool execution:\n\n```typescript\nconst searchTool = tool({\n  name: 'search',\n  inputSchema: z.object({ query: z.string() }),\n  nextTurnParams: {\n    temperature: (input) => input.query.includes('creative') ? 0.9 : 0.1,\n    maxOutputTokens: () => 2000,\n  },\n  execute: async ({ query }) => { /* ... */ },\n});\n```\n\n### Format Compatibility\n\nConvert between OpenRouter and other message formats:\n\n```typescript\nimport { toClaudeMessage, fromClaudeMessages } from '@openrouter/agent';\nimport { toChatMessage, fromChatMessages } from '@openrouter/agent';\n\n// Anthropic Claude format\nconst claudeMsg = toClaudeMessage(openRouterMessage);\nconst orMessages = fromClaudeMessages(claudeMessages);\n\n// Standard Chat format\nconst chatMsg = toChatMessage(openRouterMessage);\nconst orMessages2 = fromChatMessages(chatMessages);\n```\n\n# Tool Sets\n\nDeclarative, state-aware activation and deactivation for tools used with `@openrouter/agent`.\n\nPort of [`ai-tool-set`](https://github.com/zirkelc/ai-tool-set) (MIT © Chris Cook), adapted for this SDK's ordered `Tool[]` / `callModel` model. See [`THIRD_PARTY_NOTICES.md`](./THIRD_PARTY_NOTICES.md).\n\n## What it adds\n\n- **Stable tool-set IDs** for every addressable tool:\n  - client tools → `function.name`\n  - server tools → `server:${config.type}` by default (overridable via `serverTool(config, { id })`)\n- A **typed three-way partition** of those IDs: definitely enabled, definitely disabled, conditional.\n- **Exhaustive runtime snapshots** from `resolve()` / `resolveSituation()` — every ID appears in `statusByTool`.\n- **Named declarative situations** with compile-time exact tool tuples when the situation is fully static.\n- Integration with `callModel`'s `activeTools` option via the snapshot's spread-safe `.callModel` input.\n\n## Install\n\n```bash\npnpm add @openrouter/agent\n```\n\n## Usage\n\n```ts\nimport { OpenRouter, tool, serverTool, callModel } from '@openrouter/agent';\nimport {\n  createToolSet,\n  type InferEnabledIds,\n  type InferDisabledIds,\n  type InferConditionalIds,\n  type InferAllIds,\n} from '@openrouter/agent/tool-set';\nimport { z } from 'zod/v4';\n\ntype AppContext = {\n  isAuthenticated: boolean;\n  isAdmin: boolean;\n};\n\nconst listOrders = tool({\n  name: 'list_orders',\n  inputSchema: z.object({}),\n  execute: async () => ({ orders: [] }),\n});\n\nconst cancelOrder = tool({\n  name: 'cancel_order',\n  inputSchema: z.object({ id: z.string() }),\n  execute: async () => ({ ok: true }),\n});\n\nconst login = tool({\n  name: 'login',\n  inputSchema: z.object({}),\n  execute: async () => ({ token: '…' }),\n});\n\nconst webSearch = serverTool({ type: 'web_search_2025_08_26' });\n// id defaults to 'server:web_search_2025_08_26'\n\nconst allTools = [listOrders, cancelOrder, login, webSearch] as const;\n\nconst toolSet = createToolSet<typeof allTools, AppContext>({ tools: allTools })\n  .deactivate('cancel_order')\n  .activateWhen('list_orders', ({ context }) => context?.isAuthenticated === true)\n  .defineSituations({\n    guest: {\n      enabled: ['login', 'server:web_search_2025_08_26'],\n      disabled: ['list_orders', 'cancel_order'],\n    },\n    authenticated: {\n      enabled: ['list_orders', 'server:web_search_2025_08_26'],\n      disabled: ['login'],\n      conditional: {\n        cancel_order: ({ context }) => context?.isAdmin === true,\n      },\n    },\n  });\n\n// Compile-time partition of the *base* set (before a situation overlay):\ntype All = InferAllIds<typeof toolSet>;\n// 'list_orders' | 'cancel_order' | 'login' | 'server:web_search_2025_08_26'\ntype Enabled = InferEnabledIds<typeof toolSet>; // excludes cancel_order + list_orders (conditional)\ntype Disabled = InferDisabledIds<typeof toolSet>; // 'cancel_order'\ntype Conditional = InferConditionalIds<typeof toolSet>; // 'list_orders'\n\nconst client = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });\n\n// Named static situation → exact tool tuple at compile time\nconst guest = toolSet.resolveSituation('guest');\n// guest.tools is exactly [login, webSearch]\n// guest.enabled / guest.disabled / guest.statusByTool are exhaustive\n\nconst authenticated = toolSet.resolveSituation('authenticated', {\n  context: { isAuthenticated: true, isAdmin: false },\n});\n\nconst result = callModel(client, {\n  model: 'openai/gpt-4o-mini',\n  input: 'List my orders.',\n  ...authenticated.callModel,\n});\n```\n\n## Identity\n\n| Kind | Tool-set ID |\n| --- | --- |\n| Client `tool({ name: 'x' })` | `'x'` |\n| `serverTool({ type: 'web_search_2025_08_26' })` | `'server:web_search_2025_08_26'` |\n| `serverTool(config, { id: 'server:public_search' })` | `'server:public_search'` |\n\nDuplicate IDs throw at `createToolSet` construction. Activation methods accept only known IDs.\n\n## Compile-time vs runtime exactness\n\n| Resolution style | Developer-time knowledge | Runtime knowledge |\n| --- | --- | --- |\n| Static `activate` / `deactivate` | Exact partition | Exact snapshot |\n| Named static situation (`enabled`/`disabled` only) | Exact filtered tool tuple | Exact snapshot |\n| `activateWhen` / `deactivateWhen` / situation `conditional` | Upper bound (`enabled ∪ conditional`) | Exact snapshot after predicates |\n| Mutable `ToolSet` | Partition types may widen | Exact snapshot |\n\nThe type system cannot execute predicates. Conditional IDs therefore expand the compile-time upper bound of active tools; after `resolve`, the returned arrays and `statusByTool` are always exhaustive and exact.\n\n## API\n\n### `createToolSet<T, TShared?>({ tools, mutable? })`\n\nBuild a set from an ordered tool array. Optional `TShared` types the `context` argument on predicates. Defaults to immutable.\n\n### `.tools`\n\nConcrete tools tuple in construction order (client + server), regardless of activation.\n\n### `.activate(id | id[])` / `.deactivate(id | id[])`\n\nStatic flip (last-call-wins). Accepts client names **and** server IDs. Updates the compile-time partition.\n\n### `.activateWhen(id, predicate)` / `.activateWhen({ [id]: predicate })`\n\nConditional activation — defaults inactive, becomes active when predicate returns `true`. Moves the ID into the conditional partition.\n\n### `.deactivateWhen(id, predicate)` / `.deactivateWhen({ [id]: predicate })`\n\nConditional deactivation — defaults active, becomes inactive when predicate returns `true`. Also moves the ID into the conditional partition.\n\nPredicate input: `{ state?: ConversationState; context?: TShared }`.\n\n### `.defineSituations({ [name]: config })`\n\nDeclarative named situations. Each config may include:\n\n- `enabled?: readonly Id[]` — statically on\n- `disabled?: readonly Id[]` — statically off\n- `conditional?: { [id]: predicate | { mode?, predicate } }` — runtime rules\n\nSituation overlays the base partition for every ID it mentions; unmentioned IDs keep the base state. Unknown, duplicate, or conflicting IDs within one situation throw.\n\n### `.resolve(input?)` → snapshot\n\n```ts\n{\n  tools: /* active tools, construction order, concrete types */;\n  activeTools: /* active *client* names for callModel */;\n  callModel: { tools, activeTools }; // safe to spread into callModel()\n  enabled: /* every active ID (client + server) */;\n  disabled: /* every inactive ID */;\n  statusByTool: {\n    [id]: {\n      enabled: boolean;\n      reason: 'default' | 'activate' | 'deactivate' | 'activateWhen' | 'deactivateWhen' | 'situation';\n      directive?: 'activate' | 'deactivate' | 'activateWhen' | 'deactivateWhen';\n      predicate?: boolean; // true when a runtime predicate decided the result\n    };\n  };\n}\n```\n\n### `.resolveSituation(name, input?)` → snapshot\n\nSame shape as `resolve`, with the named situation overlay applied first.\n\n### `.inferTools(input?)`\n\nBack-compat alias for `resolve`. Prefer `resolve` in new code.\n\n### `.clone({ mutable? })`\n\nCopy state, optionally flipping mode.\n\n### Inference utilities\n\n```ts\ntype All = InferAllIds<typeof toolSet>;\ntype Enabled = InferEnabledIds<typeof toolSet>;\ntype Disabled = InferDisabledIds<typeof toolSet>;\ntype Conditional = InferConditionalIds<typeof toolSet>;\n```\n\n### `InferToolSet<TTools>`\n\nAlias of the agent's `CorrelatedToolEventUnion<TTools>` — name-correlated preliminary/result stream events based on a tools tuple.\n\n## Notes\n\n- Immutable by default (every mutator returns a new `ToolSet` with refined partition types).\n- `mutable: true` mutates in place. Partition type parameters may widen for soundness; runtime state is still exact.\n- Last-call-wins: each directive on a given ID replaces any prior one for that ID.\n- Server tools participate fully in activation once they have an ID. When active they appear in `tools` (and `enabled` / `statusByTool`) but **not** in `activeTools`, which remains the client-name list expected by `callModel`.\n- Keep a snapshot's `tools` and `activeTools` together by spreading `.callModel`; `callModel` cannot verify `activeTools` against an unrelated tools array.\n- `callModel` ignores names in `activeTools` that are not present in `tools`. Tool-set snapshots avoid stale names by deriving both arrays from the same set.\n\n\n## Subpath Exports\n\nFor tree-shaking or targeted imports, the package provides granular subpath exports:\n\n```typescript\nimport { callModel } from '@openrouter/agent/call-model';\nimport { tool } from '@openrouter/agent/tool';\nimport { ModelResult } from '@openrouter/agent/model-result';\nimport { HooksManager } from '@openrouter/agent/hooks-manager';\nimport { stepCountIs, maxCost } from '@openrouter/agent/stop-conditions';\nimport { DoomLoopMonitor, fingerprintToolCall } from '@openrouter/agent/doom-loop';\nimport { toClaudeMessage } from '@openrouter/agent/anthropic-compat';\nimport { toChatMessage } from '@openrouter/agent/chat-compat';\nimport { ToolContextStore } from '@openrouter/agent/tool-context';\nimport { ToolEventBroadcaster } from '@openrouter/agent/tool-event-broadcaster';\nimport { createInitialState } from '@openrouter/agent/conversation-state';\nimport { resumeToolResults } from '@openrouter/agent/resume-tool-results';\nimport { Semaphore } from '@openrouter/agent/tool-concurrency';\nimport { AsyncToolRegistry } from '@openrouter/agent/async-tool-registry';\nimport { ToolTask } from '@openrouter/agent/tool-task';\nimport { TaskToolInputSchema } from '@openrouter/agent/tool-check';\nimport { AgentTranscriptSource } from '@openrouter/agent/agent-tool';\n```\n\n## Development\n\n```bash\n# Install dependencies\npnpm install\n\n# Build\npnpm build\n\n# Run unit tests\npnpm test\n\n# Run end-to-end tests (requires OPENROUTER_API_KEY in .env)\npnpm test:e2e\n\n# Type check\npnpm typecheck\n\n# Lint\npnpm lint\n```\n\n### Running Tests\n\nCreate a `.env` file with your OpenRouter API key:\n\n```env\nOPENROUTER_API_KEY=sk-or-...\n```\n\nThen run:\n\n```bash\npnpm test        # Unit tests\npnpm test:e2e    # Integration tests (requires API key)\n```\n\n## Documentation\n\nFull `callModel` documentation is available at [openrouter.ai/docs/sdks/typescript/call-model](https://openrouter.ai/docs/sdks/typescript/call-model/overview).\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md"}