{"_id":"@apertis/agent","name":"@apertis/agent","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@apertis/agent","version":"0.1.0","description":"Agent runtime for Apertis — multi-step tool loops, stop conditions, streaming, approval, and real measured cost. Drop-in shape for @openrouter/agent.","license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/apertis-ai/apertis-agent.git"},"homepage":"https://apertis.ai?utm_source=apertis-agent&utm_medium=npm&utm_campaign=ecosystem","type":"module","engines":{"node":">=18.0.0"},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","lint":"biome check .","lint:fix":"biome check --write .","test":"vitest","test:run":"vitest run","prepublishOnly":"npm run build"},"dependencies":{"zod":"^3.25.0"},"devDependencies":{"@biomejs/biome":"^1.9.0","@types/node":"^22.0.0","tsup":"^8.0.0","typescript":"^5.6.0","vitest":"^2.0.0"},"keywords":["apertis","agent","ai","llm","tool-calling","agentic","openai-compatible","openrouter"],"_id":"@apertis/agent@0.1.0","gitHead":"d6519787dbc2695aa9fd8056ae263e6bbc59bcd5","bugs":{"url":"https://github.com/apertis-ai/apertis-agent/issues"},"_nodeVersion":"22.15.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-BxEp15grITaBxXjy33Q2QNcC89ojGG6rJDqcSTZg6ju465Iw6y/QdGXGGvwQHEaXsfTUubkZfBEJc7dEygnzYw==","shasum":"72cc569602614c22fafd1bc8157155864ce75067","tarball":"https://registry.npmjs.org/@apertis/agent/-/agent-0.1.0.tgz","fileCount":9,"unpackedSize":255609,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFTupWx/gBlP9e1kR6txZkPNkhLi+qxSMBFDRr272QMcAiEAz+PhmgruFPfMnV2sE8elDElcfP94h/UTEaQPMxz9azQ="}]},"_npmUser":{"name":"apertis","email":"hi@apertis.ai"},"directories":{},"maintainers":[{"name":"apertis","email":"hi@apertis.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agent_0.1.0_1781019014311_0.21032664500771747"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-09T15:30:14.184Z","0.1.0":"2026-06-09T15:30:14.507Z","modified":"2026-06-09T15:30:14.683Z"},"maintainers":[{"name":"apertis","email":"hi@apertis.ai"}],"description":"Agent runtime for Apertis — multi-step tool loops, stop conditions, streaming, approval, and real measured cost. Drop-in shape for @openrouter/agent.","homepage":"https://apertis.ai?utm_source=apertis-agent&utm_medium=npm&utm_campaign=ecosystem","keywords":["apertis","agent","ai","llm","tool-calling","agentic","openai-compatible","openrouter"],"repository":{"type":"git","url":"git+https://github.com/apertis-ai/apertis-agent.git"},"bugs":{"url":"https://github.com/apertis-ai/apertis-agent/issues"},"license":"Apache-2.0","readme":"# @apertis/agent\n\nAgent runtime for [Apertis](https://apertis.ai?utm_source=apertis-agent&utm_medium=npm&utm_campaign=ecosystem) — multi-step tool loops, stop conditions, streaming, human-in-the-loop approval, and **measured** cost control. Drop-in shape for `@openrouter/agent`, over the Apertis OpenAI-compatible API.\n\n## Why\n\n`callModel` runs the whole agent loop for you: send messages → the model calls tools → execute them → feed results back → repeat, until a stop condition fires or the model stops calling tools. You write tools; the SDK handles the loop, validation, streaming, and state.\n\nThe differentiator: `maxCost` stops on **real measured spend**, not a token estimate.\n\n## Install\n\n```bash\nnpm install @apertis/agent zod\n```\n\n```bash\nexport APERTIS_API_KEY=sk-your-key\n```\n\n## Quickstart\n\n```typescript\nimport { callModel, tool, stepCountIs, maxCost, hasToolCall } from \"@apertis/agent\";\nimport { z } from \"zod\";\n\nconst getWeather = tool({\n  name: \"get_weather\",\n  description: \"Get the weather for a city\",\n  inputSchema: z.object({ city: z.string() }),\n  execute: async ({ city }) => ({ city, tempC: 21 }),\n});\n\nconst result = callModel({\n  model: \"claude-sonnet-4-6\",\n  input: \"What's the weather in Taipei? Then say done.\",\n  tools: [getWeather],\n  stopWhen: [stepCountIs(10), maxCost(0.5), hasToolCall(\"done\")], // OR — any one stops the loop\n});\n\nconsole.log(await result.getText());\nconsole.log(\"steps:\", (await result.getResponse()).steps.length);\nconsole.log(\"cost: $\", (await result.getResponse()).cost);\n```\n\n## Streaming\n\n```typescript\nconst result = callModel({ model: \"gpt-5.2\", input: \"Write a haiku.\" });\nfor await (const delta of result.getTextStream()) process.stdout.write(delta);\n```\n\nAlso: `getReasoningStream()`, `getToolCallsStream()`, `getToolStream()`, `getNewMessagesStream()`, `getFullResponsesStream()`.\n\n## Stop conditions\n\n| Condition | Stops when |\n|---|---|\n| `stepCountIs(n)` | the loop has run `n` steps |\n| `maxTokensUsed(n)` | cumulative total tokens reach `n` |\n| `maxCost(usd)` | **measured** cumulative cost reaches `usd` |\n| `hasToolCall(name)` | the model calls the named tool |\n| `finishReasonIs(reason)` | the latest `finish_reason` matches |\n\n`stopWhen` combines conditions with OR. With no `stopWhen`, a `stepCountIs(20)` backstop applies; an absolute 100-step cap always holds.\n\n### How `maxCost` measures cost\n\n1. If the API returns `usage.cost` inline, it is used directly.\n2. Otherwise the SDK reads the `used_quota_usd` delta from `/v1/token/usage` after each step (one lightweight GET; enabled only when `maxCost` is set).\n\nIf cost can't be measured for a step, `maxCost` stops the loop conservatively rather than risk overspend.\n\n## Tool approval (human-in-the-loop)\n\n```typescript\nimport { InMemoryStateAccessor } from \"@apertis/agent\";\n\nconst state = new InMemoryStateAccessor(); // bring your own (Redis/DB/file) for production\n\nconst deleteFile = tool({\n  name: \"delete_file\",\n  inputSchema: z.object({ path: z.string() }),\n  execute: async ({ path }) => `deleted ${path}`,\n  requireApproval: true,\n});\n\nconst run = callModel({ model: \"m\", input: \"clean up /tmp\", tools: [deleteFile], state });\nif (await run.requiresApproval()) {\n  const pending = await run.getPendingToolCalls();\n  // ... ask a human ...\n  const resumed = callModel({\n    model: \"m\", input: \"clean up /tmp\", tools: [deleteFile], state,\n    approveToolCalls: [pending[0].id], // or rejectToolCalls\n  });\n  console.log(await resumed.getText());\n}\n```\n\nState persistence is **client-side**: implement `StateAccessor` (`load`/`save`) over Redis, a database, or files to survive process restarts. Apertis stores no agent state server-side.\n\n## Configuration\n\n```typescript\nimport { createCallModel } from \"@apertis/agent\";\nconst callModel = createCallModel({ apiKey: \"sk-...\", baseURL: \"https://api.apertis.ai/v1\" });\n```\n\nKey precedence: `opts.apiKey` → `APERTIS_API_KEY` → `createCallModel` config.\n\n## Format converters\n\n`fromChatMessages` / `toChatMessage` (native) and `fromClaudeMessages` / `toClaudeMessage` bridge Anthropic Messages-format history into the chat-completions format the loop uses.\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md","_rev":"1-b8970cb521f7b07b30c6e83d3b66e07c"}