{"id":49865906,"url":"https://github.com/pivanov/agents-wire","last_synced_at":"2026-05-15T03:00:01.155Z","repository":{"id":355752267,"uuid":"1228663625","full_name":"pivanov/agents-wire","owner":"pivanov","description":"One TypeScript SDK for every local coding agent - Claude, Codex, Cursor, Copilot, Gemini, OpenCode, Droid, Pi, Cline, Kilo,   Qwen, Auggie. ACP transport, Vercel AI SDK provider, cost budgets, multi-agent orchestration.","archived":false,"fork":false,"pushed_at":"2026-05-13T12:14:38.000Z","size":833,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-15T02:59:57.261Z","etag":null,"topics":["acp","agent-client-protocol","ai-sdk","claude","claude-code","codex","coding-agent","copilot","cursor","gemini","llm","multi-agent","opencode","sdk","streaming","structured-output","typescript","vercel-ai-sdk"],"latest_commit_sha":null,"homepage":"http://agents-wire.dev/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pivanov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-04T08:42:02.000Z","updated_at":"2026-05-13T12:14:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pivanov/agents-wire","commit_stats":null,"previous_names":["pivanov/agents-wire"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/pivanov/agents-wire","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pivanov%2Fagents-wire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pivanov%2Fagents-wire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pivanov%2Fagents-wire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pivanov%2Fagents-wire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pivanov","download_url":"https://codeload.github.com/pivanov/agents-wire/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pivanov%2Fagents-wire/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33051875,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-15T02:00:06.351Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acp","agent-client-protocol","ai-sdk","claude","claude-code","codex","coding-agent","copilot","cursor","gemini","llm","multi-agent","opencode","sdk","streaming","structured-output","typescript","vercel-ai-sdk"],"created_at":"2026-05-15T02:59:54.425Z","updated_at":"2026-05-15T03:00:01.146Z","avatar_url":"https://github.com/pivanov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @pivanov/agents-wire\n\n[![npm](https://img.shields.io/npm/v/@pivanov/agents-wire)](https://www.npmjs.com/package/@pivanov/agents-wire)\n[![license](https://img.shields.io/npm/l/@pivanov/agents-wire)](./LICENSE)\n\nOne TypeScript SDK for **every local coding agent**. Drives Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Factory Droid, Pi, Cline, Kilo, Qwen Code, and Augment Code (Auggie) over the [Agent Client Protocol](https://agentclientprotocol.com), with cost budgets, structured JSON, tool middleware, multi-agent orchestration, and a Vercel AI SDK provider (LanguageModelV3, `ai@^6`).\n\n```ts\nimport { agents } from \"@pivanov/agents-wire\";\n\nconst result = await agents.ask(\"claude\", \"Refactor src/auth.ts\", {\n  permission: \"auto-allow\",\n  maxCostUsd: 0.5,\n});\n\nconsole.log(result.text, result.cost?.totalUsd);\n```\n\n## Why\n\nLocal coding agents are powerful but awkward to drive programmatically. Each one ships a different CLI; their output formats drift; cost tracking is bring-your-own; structured outputs are a prompt-engineering project; and combining several agents takes a lot of glue. `agents-wire` fixes all of that with one transport and a small, focused API.\n\n## Features\n\n- **Twelve agents, one API** - Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Factory Droid, Pi, Cline, Kilo, Qwen Code, Augment Code (Auggie)\n- **`ask` / `stream` / `session`** - one-shot, streaming async-iterable, multi-turn with shared subprocess\n- **`askJson` with Standard Schema** - Zod 4 / Valibot / ArkType auto-derived to JSON Schema. Strict CLI channel for Claude (via `@pivanov/claude-wire`), post-hoc validation for the rest.\n- **Cost tracker + budgets** - per-agent breakdown, runtime pricing table, auto-abort when over budget\n- **Tool middleware** - `allowed` / `blocked` / `onToolUse` decision pipeline plumbed through ACP permission requests\n- **Permission policies** - `auto-allow`, `auto-allow-once`, `auto-reject`, `stream` (HITL), or custom function\n- **Orchestration** - `failover` / `race` / `cascade` / `pool` for multi-agent workflows\n- **Vercel AI SDK provider** (LanguageModelV3, `ai@^6`) - `agentModel(\"claude\")` drops into `streamText` / `generateText`\n- **Typed errors** - `WireError` with `code` field plus `BudgetExceededError`, `JsonValidationError`, `AbortError`, `CapabilityNotSupportedError`\n- **Fully typed** - discriminated `TAgentEvent` union, full IntelliSense, no `any`\n- **Mock + transcript replay** - `@pivanov/agents-wire/testing` for deterministic tests\n- **CLI** - `agents-wire ask | ask-json | stream | detect | agents`\n\n## Install\n\n```bash\nbun add @pivanov/agents-wire\n# or\nnpm install @pivanov/agents-wire\n```\n\nYou also need the agent's CLI installed and authenticated. Run `npx @pivanov/agents-wire detect` to see which agents are available on your machine.\n\n| Agent       | How it speaks ACP                                         | Install                                                                                  |\n| ----------- | --------------------------------------------------------- | ---------------------------------------------------------------------------------------- |\n| `claude`    | bridge (`@agentclientprotocol/claude-agent-acp`, bundled) | [Claude Code](https://docs.claude.com/en/docs/claude-code/setup) + `claude /login`       |\n| `codex`     | bridge (`@zed-industries/codex-acp`, bundled)             | OpenAI Codex CLI on PATH                                                                 |\n| `cursor`    | native (`agent acp`)                                      | [Cursor Agent CLI](https://cursor.com/docs/cli/acp)                                      |\n| `copilot`   | bridge (`@github/copilot --acp`, peer install)            | `npm i -g @github/copilot` + `gh auth login`                                             |\n| `gemini`    | bridge (`@google/gemini-cli --acp`, peer install)         | `npm i -g @google/gemini-cli` + `gemini auth login`                                      |\n| `opencode`  | native (`opencode acp`)                                   | `npm i -g opencode-ai`                                                                   |\n| `droid`     | native (`droid exec --output-format acp`)                 | `npm i -g droid` + `FACTORY_API_KEY`                                                     |\n| `pi`        | native (`pi acp`)                                         | `npm i -g @mariozechner/pi-coding-agent`                                                 |\n| `cline`     | native (`cline --acp`)                                    | `npm i -g cline` + `cline auth` or provider keys                                         |\n| `kilo`      | native (`kilo acp`)                                       | `npm i -g @kilocode/cli` + `kilo auth login --provider \u003cid\u003e` or `KILO_API_KEY`           |\n| `qwen`      | native (`qwen --acp --experimental-skills`)               | `npm i -g @qwen-code/qwen-code` + `qwen auth qwen-oauth` or `BAILIAN_CODING_PLAN_API_KEY` |\n| `auggie`    | native (`auggie --acp`)                                   | `npm i -g @augmentcode/auggie` + `auggie login` (subscription required)                  |\n\n## Quick start\n\n### One-shot\n\n```ts\nimport { agents } from \"@pivanov/agents-wire\";\n\nconst result = await agents.ask(\"claude\", \"Summarize README.md in 3 bullets\", {\n  cwd: process.cwd(),\n  permission: \"auto-allow\",\n  maxCostUsd: 0.25,\n});\n\nconsole.log(result.text);\nconsole.log(result.usage);          // { contextSize, contextUsed, costUsd }\nconsole.log(result.cost?.totalUsd); // SDK-side cumulative cost\n```\n\n### Streaming\n\n```ts\nimport { agents } from \"@pivanov/agents-wire\";\n\nconst stream = agents.stream(\"claude\", \"Refactor src/auth.ts to use the new session API\");\n\nfor await (const event of stream) {\n  if (event.type === \"text-delta\") {\n    process.stdout.write(event.text);\n  }\n  if (event.type === \"tool-call\") {\n    console.log(\"\\n[tool]\", event.tool, event.input);\n  }\n}\n\nconst final = await stream.result();\nconsole.log(\"\\nfinished:\", final.stopReason);\n```\n\n### Multi-turn session\n\n```ts\nimport { agents } from \"@pivanov/agents-wire\";\n\nawait using session = await agents.session(\"codex\", { permission: \"auto-allow\" });\n\nawait session.ask(\"List all TODOs in the repo\");\nconst fix = await session.ask(\"Now fix the highest-priority one\");\nconsole.log(fix.text);\n\nconsole.log(\"session cost:\", session.cost.snapshot.totalUsd);\n```\n\n### Structured JSON with Standard Schema\n\n```ts\nimport { agents } from \"@pivanov/agents-wire\";\nimport { z } from \"zod\";\n\nconst Issue = z.object({\n  title: z.string(),\n  severity: z.enum([\"low\", \"medium\", \"high\"]),\n});\n\nconst { data } = await agents.askJson(\"claude\", \"Read src/auth.ts and report issues\", Issue);\nconsole.log(data.title, data.severity);\n```\n\nWorks with **Zod 4**, **Valibot** (with `@valibot/to-json-schema` installed), and **ArkType**. You can pass a raw JSON Schema string for **prompt guidance only** — `askJson` will steer the agent toward that shape but cannot validate the response. For validated output, pass a Standard Schema instance.\n\n### Tool middleware\n\n```ts\nconst result = await agents.ask(\"claude\", \"Fix the build\", {\n  toolHandler: {\n    blocked: [\"Bash\"],                              // hard block\n    onToolUse: async (event) =\u003e {\n      if (event.tool === \"Write\" \u0026\u0026 String(event.input).includes(\"secrets\")) {\n        return { decision: \"deny\", reason: \"no secrets\" };\n      }\n      return \"allow\";\n    },\n  },\n});\n```\n\n## Orchestration\n\nFour primitives for combining agents.\n\n### `failover`\n\nTry candidates in order; skip on transient errors, return the first success.\n\n```ts\nconst result = await agents.failover(\"Classify this ticket\", [\"claude\", \"codex\", \"gemini\"]);\nconsole.log(result.winner, result.text);\n```\n\n### `race`\n\nAll candidates in parallel; first to finish wins, losers get cancelled.\n\n```ts\nconst result = await agents.race(\"Classify this ticket\", [\"claude\", \"gemini\"]);\nconsole.log(result.winner, \"lost:\", result.losers.map((l) =\u003e l.agent));\n```\n\n### `cascade`\n\nEscalation chain. Try cheaper/faster first; fall through if the result fails an `accept` predicate.\n\n```ts\nconst result = await agents.cascade(\"Triage this issue\", [\n  { agent: \"claude\", options: { model: \"haiku\" }, accept: (r) =\u003e r.text.length \u003e 20 },\n  { agent: \"claude\", options: { model: \"sonnet\" }, accept: (r) =\u003e r.text.length \u003e 50 },\n  { agent: \"claude\", options: { model: \"opus\" } },\n]);\nconsole.log(\"won at stage\", result.winningStageIndex);\n```\n\n### `pool`\n\nWarm subprocess pool with capacity limit. Concurrent prompts share the pool.\n\n```ts\nawait using pool = await agents.pool({ agents: [\"claude\"], capacity: 4 });\n\nconst replies = await Promise.all(\n  prompts.map((p) =\u003e pool.ask(p)),\n);\nconsole.log(\"total cost:\", pool.cost.snapshot.totalUsd);\n```\n\n## Vercel AI SDK provider\n\nUse any agent as a `LanguageModelV3` for `streamText` / `generateText`:\n\n```ts\nimport { streamText } from \"ai\";\nimport { agentModel } from \"@pivanov/agents-wire/ai-sdk\";\n\nconst { textStream } = streamText({\n  model: agentModel(\"claude\", { permission: \"auto-allow\" }),\n  prompt: \"Refactor src/auth.ts\",\n});\n\nfor await (const chunk of textStream) {\n  process.stdout.write(chunk);\n}\n```\n\nFor multi-turn sharing one subprocess across `streamText` calls:\n\n```ts\nimport { createAgentModelSession } from \"@pivanov/agents-wire/ai-sdk\";\n\nawait using s = await createAgentModelSession(\"codex\");\nawait streamText({ model: s.model, prompt: \"list TODOs\" });\nawait streamText({ model: s.model, prompt: \"now fix the highest-priority one\" });\n```\n\n## CLI\n\n```bash\nnpx @pivanov/agents-wire ask claude --prompt \"explain this repo\"\nnpx @pivanov/agents-wire ask-json claude --prompt \"extract metadata\" --schema-file ./schema.json\nnpx @pivanov/agents-wire stream gemini --prompt \"summarize this PR\"\nnpx @pivanov/agents-wire detect      # list available agents on this machine\nnpx @pivanov/agents-wire agents      # list all built-in agents\n```\n\n## Testing\n\n`@pivanov/agents-wire/testing` ships a mock agent and transcript record/replay so you can test consumers without spawning real processes.\n\n```ts\nimport { createMockAgent, createRecorder, replayTranscript } from \"@pivanov/agents-wire/testing\";\n\nconst mock = createMockAgent({\n  agent: \"claude\",\n  turns: [\n    { text: \"ok\" },\n    { text: \"porcupine\" },\n  ],\n});\n\nconst turn1 = await mock.ask(\"remember 'porcupine'\");  // → \"ok\"\nconst turn2 = await mock.ask(\"what was it?\");          // → \"porcupine\"\n```\n\n## Subpath exports\n\n| Subpath                              | What's there                                               |\n| ------------------------------------ | ---------------------------------------------------------- |\n| `@pivanov/agents-wire`               | the main facade and types                                  |\n| `@pivanov/agents-wire/errors`        | typed `WireError` + subclasses + `KNOWN_ERROR_CODES`       |\n| `@pivanov/agents-wire/ai-sdk`        | Vercel AI SDK provider                                     |\n| `@pivanov/agents-wire/testing`       | mock agent + transcript replay                             |\n| `@pivanov/agents-wire/catalog`       | individual agent definitions + registry                    |\n| `@pivanov/agents-wire/orchestrate`   | `failover`, `race`, `cascade`, `pool`                      |\n\n## Requirements\n\n- Bun ≥ 1.0 or Node ≥ 22\n- POSIX (macOS, Linux, WSL). Native Windows isn't supported.\n- The agent CLIs you want to drive must be installed and authenticated on the host machine.\n\n## Troubleshooting\n\n### Global bin resolution\n\nWhen agent bridges (`@agentclientprotocol/claude-agent-acp`, `@github/copilot`, `@google/gemini-cli`) are installed globally rather than as workspace deps, agents-wire walks `npm root -g`, your Node prefix, and conventional system roots (`/usr/local`, `/opt/homebrew`, `~/.npm-global`, `~/.local`). If your global root is in a non-standard location (custom `npm config prefix`, isolated Volta/asdf install), set:\n\n```bash\nexport AGENTS_WIRE_GLOBAL_NODE_MODULES=/path/to/your/global/node_modules\n```\n\nThe override is consulted first; resolution falls through to the standard search if the override doesn't contain the requested package.\n\n## Development\n\n```bash\nbun install\nbun run typecheck\nbun run lint\nbun run --filter '@pivanov/agents-wire' build\n\n# Run the smoke test against your installed Claude\nbun packages/agents-wire/tests/smoke.ts\nbun packages/agents-wire/tests/smoke-orchestrate.ts\n```\n\n## Sponsors\n\nSupported by [LogicStar AI](https://logicstar.ai/).\n\n## License\n\nMIT - © Pavel Ivanov\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpivanov%2Fagents-wire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpivanov%2Fagents-wire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpivanov%2Fagents-wire/lists"}