{"id":50532919,"url":"https://github.com/runloopai/remote-agents-sdk","last_synced_at":"2026-06-03T15:01:51.268Z","repository":{"id":348651336,"uuid":"1199167816","full_name":"runloopai/remote-agents-sdk","owner":"runloopai","description":"Axon-backed ACP and Claude agent clients for Runloop broker agents","archived":false,"fork":false,"pushed_at":"2026-05-07T00:54:55.000Z","size":1030,"stargazers_count":0,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-15T19:51:29.115Z","etag":null,"topics":["acp","agent","axon","claude","runloop","sdk","typescript"],"latest_commit_sha":null,"homepage":"https://runloopai.github.io/remote-agents-sdk/","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/runloopai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"runloopai"}},"created_at":"2026-04-02T05:25:48.000Z","updated_at":"2026-05-06T23:23:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"4a56c240-6959-4121-bedb-ce50cd08e3af","html_url":"https://github.com/runloopai/remote-agents-sdk","commit_stats":null,"previous_names":["runloopai/agent-axon-client-ts","runloopai/remote-agents-sdk"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/runloopai/remote-agents-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runloopai%2Fremote-agents-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runloopai%2Fremote-agents-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runloopai%2Fremote-agents-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runloopai%2Fremote-agents-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/runloopai","download_url":"https://codeload.github.com/runloopai/remote-agents-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runloopai%2Fremote-agents-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33870026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"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","axon","claude","runloop","sdk","typescript"],"created_at":"2026-06-03T15:01:50.247Z","updated_at":"2026-06-03T15:01:51.248Z","avatar_url":"https://github.com/runloopai.png","language":"TypeScript","funding_links":["https://github.com/sponsors/runloopai"],"categories":[],"sub_categories":[],"readme":"# @runloop/remote-agents-sdk\n\n[![CI](https://github.com/runloopai/remote-agents-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/runloopai/remote-agents-sdk/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@runloop/remote-agents-sdk)](https://www.npmjs.com/package/@runloop/remote-agents-sdk)\n[![Docs](https://img.shields.io/badge/docs-TypeDoc-blue)](https://runloopai.github.io/remote-agents-sdk/)\n[![codecov](https://codecov.io/gh/runloopai/remote-agents-sdk/branch/main/graph/badge.svg)](https://codecov.io/gh/runloopai/remote-agents-sdk)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nTypeScript SDK for connecting applications to Runloop-hosted remote agents (Claude Code, OpenCode, etc.) via the Axon event bus.\n\n## Key Concepts\n\nBefore getting started, it's helpful to understand these core concepts:\n\n- **Runloop** — A cloud platform that provides on-demand development environments (devboxes) where coding agents can run.\n- **Devbox** — An isolated Linux container/environment running in Runloop's cloud where coding agents execute. It has a filesystem, can run commands, and persists for the duration of your session.\n- **Axon** — A bidirectional message bus that enables real-time communication between your application and an agent running in a Runloop devbox. Think of it as a WebSocket-like channel for agent control.\n- **Broker Mount** — A devbox configuration that connects an Axon channel to an agent binary, specifying which agent to run (opencode, claude, etc.), the protocol to use (acp, claude_json), and launch arguments.\n\nIn short: **Runloop** hosts **devboxes** where agents run; a **broker mount** connects that agent to **Axon**; and **Axon** is the message bus your app uses to control it.\n\n## Prerequisites\n\n- [Node.js](https://nodejs.org) \u003e= 22.0.0\n- A [Runloop](https://runloop.ai) API key\n  - Sign up for free at [platform.runloop.ai](https://platform.runloop.ai) (includes $50 in credits)\n  - Navigate to [Settings](https://platform.runloop.ai/settings) → API Keys\n  - Create an API key (starts with `ak`\\_)\n  - Set environment variable: `export RUNLOOP_API_KEY=ak_your_key`\n- An [Anthropic](https://console.anthropic.com) API key (**only required for Claude module examples**)\n  - Sign up at [console.anthropic.com](https://console.anthropic.com)\n  - Go to API Keys section\n  - Create new key (starts with `sk-ant-`)\n  - Set environment variable: `export ANTHROPIC_API_KEY=sk-ant-your_key`\n\n## Installation\n\n```bash\nnpm install @runloop/remote-agents-sdk @runloop/api-client\n```\n\n`@runloop/api-client` is a required peer dependency — it provides the `RunloopSDK` instance and Axon types used by both modules.\n\nIf you're using the Claude module, also install:\n\n```bash\nnpm install @anthropic-ai/claude-agent-sdk\n```\n\n## Status \u0026 Roadmap\n\n### Supported Features by Protocol\n\n| Capability                                   | Claude | ACP |\n| -------------------------------------------- | ------ | --- |\n| Send prompts / messages                      | ✅     | ✅  |\n| Streaming responses                          | ✅     | ✅  |\n| Tool use / tool results                      | ✅     | ✅  |\n| Cancel / interrupt turns                     | ✅     | ✅  |\n| Permission / control requests (auto-approve) | ✅     | ✅  |\n\n\\*Auto-approve only for now, permission request flow pending\n\n### Coming Soon\n\n| Status     | Description                                                                                                                                                     |\n| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| 🚧 Planned | **Devbox state-transition events** — expose transitional lifecycle states (`suspending`, `resuming`, `creating`) as first-class Axon events (terminal states like `running`, `suspended`, `shutdown`, `failed` are already supported) |\n| 🚧 Planned | **Axon subscribe over WebSockets** — WebSocket transport for Axon subscriptions, enabling browser clients without a backend proxy                               |\n\n## Modules\n\nThe SDK has two independent modules — pick the one that matches your agent's protocol:\n\n| Module     | Import path                         | Protocol                                                                | Use when                                        |\n| ---------- | ----------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------- |\n| **ACP**    | `@runloop/remote-agents-sdk/acp`    | [Agent Client Protocol](https://agentclientprotocol.com) (JSON-RPC 2.0) | Using OpenCode, or Claude via ACP               |\n| **Claude** | `@runloop/remote-agents-sdk/claude` | Claude Code SDK wire format                                             | Using Claude Code with native SDK message types |\n\n### Which module should I use?\n\n**Use the ACP module when:**\n\n- You want agent-agnostic code that works with multiple agents (OpenCode, Claude via ACP, future agents)\n- You need a standardized JSON-RPC 2.0 protocol\n- You want maximum compatibility and flexibility\n\n**Use the Claude module when:**\n\n- You're specifically using Claude Code\n- You want native Claude SDK message types\n- You need Claude-specific features\n\n**Note:** The modules have different APIs and are not directly interchangeable. Choose based on your agent and requirements.\n\n## Usage\n\n### ACP module\n\n```typescript\nimport {\n  ACPAxonConnection,\n  PROTOCOL_VERSION,\n  isAgentMessageChunk,\n} from \"@runloop/remote-agents-sdk/acp\";\nimport { RunloopSDK } from \"@runloop/api-client\";\n\nconst sdk = new RunloopSDK({ bearerToken: process.env.RUNLOOP_API_KEY });\n\nconst axon = await sdk.axon.create({ name: \"acp-transport\" });\nconst devbox = await sdk.devbox.create({\n  mounts: [\n    {\n      type: \"broker_mount\",\n      axon_id: axon.id,\n      protocol: \"acp\",\n      agent_binary: \"opencode\",\n      launch_args: [\"acp\"],\n    },\n  ],\n});\nconst agent = new ACPAxonConnection(axon, devbox, {\n  onDisconnect: async () =\u003e {\n    await devbox.shutdown();\n  },\n});\n\nawait agent.connect();\nawait agent.initialize({\n  protocolVersion: PROTOCOL_VERSION,\n  clientInfo: { name: \"my-app\", version: \"1.0.0\" },\n});\n\nagent.onSessionUpdate((sessionId, update) =\u003e {\n  if (isAgentMessageChunk(update)) process.stdout.write(update.message);\n});\n\nconst session = await agent.newSession({ cwd: \"/home/user\", mcpServers: [] });\nawait agent.prompt({\n  sessionId: session.sessionId,\n  prompt: [{ type: \"text\", text: \"Hello!\" }],\n});\n\nawait agent.disconnect();\n```\n\n### Claude module\n\n```typescript\nimport { ClaudeAxonConnection } from \"@runloop/remote-agents-sdk/claude\";\nimport { RunloopSDK } from \"@runloop/api-client\";\n\nconst sdk = new RunloopSDK({ bearerToken: process.env.RUNLOOP_API_KEY });\n\nconst axon = await sdk.axon.create({ name: \"claude-transport\" });\nconst devbox = await sdk.devbox.create({\n  mounts: [\n    {\n      type: \"broker_mount\",\n      axon_id: axon.id,\n      protocol: \"claude_json\",\n      agent_binary: \"claude\",\n    },\n  ],\n});\n\nconst conn = new ClaudeAxonConnection(axon, devbox, {\n  model: \"claude-sonnet-4-5\",\n});\nawait conn.connect();\nawait conn.initialize();\n\nawait conn.send(\"What files are in this directory?\");\n\nfor await (const msg of conn.receiveAgentResponse()) {\n  console.log(msg.type, msg);\n}\n\nawait conn.disconnect();\n```\n\n### Timeline events\n\nBoth modules provide a unified timeline event stream — the recommended way to build chat UIs that need a single chronological view of protocol messages, system events (turn start/end), and custom events.\n\nEvery timeline event has `{ kind, data, axonEvent }` where `kind` is a discriminant (`\"acp_protocol\"` / `\"claude_protocol\"`, `\"system\"`, or `\"unknown\"`), `data` is the typed payload, and `axonEvent` is the raw Axon event with full metadata.\n\n**ACP — handling protocol events:**\n\n```typescript\nimport { SYSTEM_EVENT_TYPES } from \"@runloop/remote-agents-sdk/shared\";\nimport {\n  isAgentMessageChunk,\n  isToolCall,\n  isToolCallProgress,\n} from \"@runloop/remote-agents-sdk/acp\";\n\nagent.onTimelineEvent((event) =\u003e {\n  switch (event.kind) {\n    case \"acp_protocol\":\n      if (event.eventType === \"session/update\") {\n        const update = event.data.update;\n        if (isAgentMessageChunk(update)) {\n          process.stdout.write(update.text);\n        } else if (isToolCall(update)) {\n          console.log(`Tool: ${update.name} (${update.status})`);\n        } else if (isToolCallProgress(update)) {\n          console.log(`Tool output: ${update.content}`);\n        }\n      }\n      break;\n    case \"unknown\":\n      console.log(\n        `Unrecognized event: ${event.axonEvent.event_type}`,\n        event.axonEvent.payload,\n      );\n      break;\n  }\n});\n```\n\n**Claude:**\n\n```typescript\nconn.onTimelineEvent((event) =\u003e {\n  switch (event.kind) {\n    case \"claude_protocol\":\n      if (event.eventType === \"assistant\") {\n        process.stdout.write(event.data.content);\n      } else if (event.eventType === \"result\") {\n        console.log(\"Result:\", event.data.content);\n      }\n      break;\n    case \"unknown\":\n      break;\n  }\n});\n```\n\n**Custom events** — use `publish()` to push your own events to the channel. They arrive as `kind: \"unknown\"` timeline events:\n\n```typescript\nimport { tryParseTimelinePayload } from \"@runloop/remote-agents-sdk/acp\";\n\n// Publish a custom event\nawait conn.publish({\n  event_type: \"build_status\",\n  origin: \"EXTERNAL_EVENT\",\n  source: \"ci-pipeline\",\n  payload: JSON.stringify({ step: \"compile\", progress: 75 }),\n});\n\n// Consume it on the other side\nconn.onTimelineEvent((event) =\u003e {\n  if (\n    event.kind === \"unknown\" \u0026\u0026\n    event.axonEvent.event_type === \"build_status\"\n  ) {\n    const status = tryParseTimelinePayload\u003c{ step: string; progress: number }\u003e(\n      event,\n    );\n    if (status) console.log(`${status.step}: ${status.progress}%`);\n  }\n});\n```\n\nBoth modules also support pull-based consumption via an async generator:\n\n```typescript\nfor await (const event of agent.receiveTimelineEvents()) {\n  console.log(event.kind, event.data);\n}\n```\n\nSee the [SDK documentation](sdk/README.md#custom-events-via-publish-and-tryparsetimelinepayload) for more on custom events, and the [full timeline API reference](sdk/README.md#timeline-events) for replay behavior and `afterSequence`.\n\nSee the [SDK documentation](sdk/README.md) for the full API reference, or browse the [hosted API docs](https://runloopai.github.io/remote-agents-sdk/).\n\n## Getting Agents onto the Devbox\n\nThere are two ways to ensure your agent binary is available on the devbox before execution starts:\n\n- **Agent mounts (late-binding)** — Install the agent at devbox creation time via a mount. The agent lands on the box just before the broker mount connects it to Axon. This works with any standard Runloop image, so you can pick or customize the base environment independently from the agent.\n- **Blueprints (pre-baked)** — Bake the agent (and any other tooling) directly into a custom devbox image. Subsequent devbox creations skip the install step entirely, giving you the fastest cold-start and a reproducible, versioned environment.\n\nThe standalone examples (hello-world, CLI, combined-app) use a pre-baked **blueprint** for the fastest cold-start; the [feature-examples](examples/feature-examples/) default to **agent mounts** so they work with any standard image. Pick whichever fits your workflow — for example, blueprints when you want reproducible images, or mounts when you need to swap agent versions frequently or avoid maintaining custom images.\n\n## Repository Structure\n\n```\nsdk/                      → @runloop/remote-agents-sdk (the published npm package)\nexamples/\n  blueprint/              → Builds the shared `axon-agents` blueprint (run this first)\n  acp-hello-world/        → Minimal ACP single-prompt script\n  acp-cli/                → Interactive ACP REPL\n  claude-hello-world/     → Minimal Claude single-prompt script\n  claude-cli/             → Interactive Claude REPL\n  combined-app/           → Full-stack combined demo (Claude + ACP, Express + React)\n  feature-examples/       → Runnable SDK recipes (single-prompt, elicitation, etc.)\n```\n\n### Running the examples\n\n\u003e **Run the blueprint example first.** Every other example creates a devbox with `blueprint_name: \"axon-agents\"`. That blueprint is built by [`examples/blueprint`](examples/blueprint/) and must exist on your Runloop account before any other example will succeed.\n\u003e\n\u003e ```bash\n\u003e bun install\n\u003e bun run build\n\u003e bun run build-blueprint   # one-time — builds the axon-agents blueprint\n\u003e ```\n\u003e\n\u003e After the blueprint reports `build_complete`, you can run any of the other examples. See [`examples/blueprint/README.md`](examples/blueprint/README.md) for details and [`examples/README.md`](examples/README.md) for the full example index.\n\n## Development\n\n**Prerequisites:**\n\n- [Node.js](https://nodejs.org) \u003e= 22.0.0\n- [Bun](https://bun.sh) (package manager and task runner)\n\n```bash\nbun install\nbun run build\nbun run test\nbun run check    # lint + format verification\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow, commit conventions, and PR guidelines.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunloopai%2Fremote-agents-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frunloopai%2Fremote-agents-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunloopai%2Fremote-agents-sdk/lists"}