{"id":51244590,"url":"https://github.com/agentruntimecontrolprotocol/ruby-sdk","last_synced_at":"2026-06-29T03:03:07.288Z","repository":{"id":356978808,"uuid":"1234794663","full_name":"agentruntimecontrolprotocol/ruby-sdk","owner":"agentruntimecontrolprotocol","description":"Ruby reference SDK for ARCP (Agent Runtime Control Protocol).","archived":false,"fork":false,"pushed_at":"2026-06-23T15:02:59.000Z","size":735,"stargazers_count":2,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-23T15:08:07.444Z","etag":null,"topics":["agent-protocol","agent-runtime-control-protocol","agents","ai-agents","arcp","durable-execution","llm","mcp","ruby","sdk","streaming"],"latest_commit_sha":null,"homepage":"https://github.com/agentruntimecontrolprotocol/spec","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/agentruntimecontrolprotocol.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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-10T16:45:32.000Z","updated_at":"2026-06-23T13:04:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/agentruntimecontrolprotocol/ruby-sdk","commit_stats":null,"previous_names":["agentruntimecontrolprotocol/ruby-sdk"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/agentruntimecontrolprotocol/ruby-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentruntimecontrolprotocol%2Fruby-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentruntimecontrolprotocol%2Fruby-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentruntimecontrolprotocol%2Fruby-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentruntimecontrolprotocol%2Fruby-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agentruntimecontrolprotocol","download_url":"https://codeload.github.com/agentruntimecontrolprotocol/ruby-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentruntimecontrolprotocol%2Fruby-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34911135,"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-29T02:00:05.398Z","response_time":58,"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":["agent-protocol","agent-runtime-control-protocol","agents","ai-agents","arcp","durable-execution","llm","mcp","ruby","sdk","streaming"],"created_at":"2026-06-29T03:03:01.233Z","updated_at":"2026-06-29T03:03:07.277Z","avatar_url":"https://github.com/agentruntimecontrolprotocol.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch3 align=\"center\"\u003eARCP Ruby SDK\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eRuby SDK for the Agent Runtime Control Protocol (ARCP) — submit, observe, and control long-running agent jobs from Ruby.\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://rubygems.org/gems/arcp\"\u003e\u003cimg alt=\"gem\" src=\"https://img.shields.io/gem/v/arcp.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/agentruntimecontrolprotocol/ruby-sdk/actions/workflows/test.yml\"\u003e\u003cimg alt=\"CI\" src=\"https://github.com/agentruntimecontrolprotocol/ruby-sdk/actions/workflows/test.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/agentruntimecontrolprotocol/ruby-sdk\"\u003e\u003cimg alt=\"codecov\" src=\"https://codecov.io/gh/agentruntimecontrolprotocol/ruby-sdk/graph/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md\"\u003e\u003cimg alt=\"ARCP\" src=\"https://img.shields.io/badge/ARCP-v1.1%20draft-blue\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/badge/license-Apache--2.0-lightgrey\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md\"\u003eSpecification\u003c/a\u003e ·\n  \u003ca href=\"#concepts\"\u003eConcepts\u003c/a\u003e ·\n  \u003ca href=\"#installation\"\u003eInstall\u003c/a\u003e ·\n  \u003ca href=\"#quick-start\"\u003eQuick start\u003c/a\u003e ·\n  \u003ca href=\"docs/\"\u003eGuides\u003c/a\u003e ·\n  \u003ca href=\"docs/api/\"\u003eAPI reference\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n`arcp` is the Ruby reference implementation of [ARCP](https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md), the Agent Runtime Control Protocol. It covers both sides of the wire — `Arcp::Client` for submitting and observing jobs, `Arcp::Runtime::Runtime` for hosting agents — so either side can talk to any conformant peer in any language without hand-rolling the envelope, sequencing, or the lease, budget, and model enforcement primitives.\n\nARCP itself is a transport-agnostic wire protocol for long-running AI agent jobs. It owns the parts of agent infrastructure that don't change between products — sessions, durable event streams, capability leases, budgets, resume — and stays out of the parts that do. ARCP wraps the agent function; it does not define how agents are built, how tools are exposed (that's MCP), or how telemetry is exported (that's OpenTelemetry).\n\n## Installation\n\nRequires Ruby 3.3 or later. The gem runs on the `socketry/async` reactor and pulls in `async-websocket` for the default networked transport. The runtime currently buffers events in memory for replay; durable persistence is not shipped yet. Add it to a `Gemfile`:\n\n```ruby\ngem 'arcp', '~\u003e 1.0'\n```\n\n```sh\nbundle install\n```\n\n## Quick start\n\nConnect to a runtime, submit a job, stream its events to completion:\n\n```ruby\nrequire 'async'\nrequire 'arcp'\n\nECHO = lambda do |ctx|\n  ctx.log(level: 'info', message: \"echoing #{ctx.input.inspect}\")\n  ctx.progress(current: 1, total: 1, units: 'message')\n  ctx.finish(result: { 'echoed' =\u003e ctx.input })\nend\n\nSync do\n  runtime = Arcp::Runtime::Runtime.new(\n    auth_verifier: Arcp::Auth::Bearer.from_token('demo', principal_id: 'alice'),\n    heartbeat_interval_sec: nil\n  )\n  runtime.register_agent(name: 'echo', versions: ['1.0.0'], default: '1.0.0', handler: ECHO)\n\n  server_t, client_t = Arcp::Transport::MemoryTransport.pair\n  server = Async { runtime.accept(server_t) }\n\n  client = Arcp::Client.open(\n    transport: client_t,\n    auth: { 'scheme' =\u003e 'bearer', 'token' =\u003e 'demo' },\n    client_name: 'quickstart'\n  )\n\n  handle = client.submit_job(agent: 'echo', input: { 'msg' =\u003e 'hi' })\n  handle.subscribe(client: client).each { |event| puts \"#{event.kind}: #{event.body.to_h}\" }\n  result = handle.get_result(client: client)\n  puts \"final: #{result.final_status} #{result.result.inspect}\"\n\n  client.close\n  server.stop\nend\n```\n\nThis is the whole shape of the SDK: open a session, submit work, consume an ordered event stream, get a terminal result or error. Everything below is detail on those four moves.\n\n## Concepts\n\nARCP organizes everything around four concerns — **identity**, **durability**, **authority**, and **observability** — expressed through five core objects:\n\n- **Session** — a connection between a client and a runtime. A session carries identity (a bearer token), negotiates a feature set in a `hello`/`welcome` handshake, and keeps a replay window in the runtime's in-memory event log. Transparent reconnect resume is not wired through yet; use `history: true` and `from_event_seq` when you need to replay events. Jobs outlive the session that started them. See [§6](https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md).\n- **Job** — one unit of agent work submitted into a session. A job has an identity, an optional idempotency key, a resolved agent version, and a lifecycle that ends in exactly one terminal state: `success`, `error`, `cancelled`, or `timed_out`. See [§7](https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md).\n- **Event** — the ordered, session-scoped stream a job emits: logs, thoughts, tool calls and results, status, metrics, artifact references, progress, and streamed result chunks. Events carry strictly monotonic sequence numbers so the stream survives reconnects gap-free. See [§8](https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md).\n- **Lease** — the authority a job runs under, expressed as capability grants (`fs.read`, `fs.write`, `net.fetch`, `tool.call`, `agent.delegate`, `cost.budget`, `model.use`). The SDK provides synchronous enforcement seams — `JobContext#authorize!(capability)`, `#use_model!(model_id)`, and the guarded `#tool_call` — that check the lease, budget, and permitted model set *before* an authority-bearing operation runs, raising `PERMISSION_DENIED` or `BUDGET_EXHAUSTED`. An agent routes its operations through these seams so it cannot act outside the lease; `cost.*` metrics decrement the budget automatically. Leases may carry a budget and an expiry, and may be subset and handed to sub-agents via delegation. See [§9](https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md).\n- **Subscription** — read-only attachment to a job started elsewhere (e.g. a dashboard watching a job a CLI submitted). A subscriber observes the live event stream but cannot cancel or mutate the job. Distinct from *resume*, which continues the original session and carries cancel authority. See [§7.6](https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md).\n\nThe SDK models each of these as first-class objects; the rest of this README shows how.\n\n## Guides\n\n### Sessions and replay\n\nOpen a session, submit work, and replay buffered events from the retained log window when you need to recover missed history.\n\n```ruby\nrequire 'async'\nrequire 'arcp'\n\nSync do\n  client = Arcp::Client.open(\n    transport: transport,\n    auth: { 'scheme' =\u003e 'bearer', 'token' =\u003e ENV.fetch('ARCP_TOKEN') },\n    client_name: 'resumable'\n  )\n\n  handle = client.submit_job(agent: 'long-runner')\n  handle.subscribe(client: client).each do |event|\n    puts \"#{event.kind}: #{event.body.to_h}\"\n  end\n\n  replay = client.subscribe_job(job_id: handle.job_id, from_event_seq: 0, history: true)\n  replay.each do |event|\n    puts \"[replay] #{event.kind}\"\n  end\nend\n```\n\n### Submitting jobs\n\nSubmit a job with an agent (optionally version-pinned as `name@version`), an input, and an optional lease request, idempotency key, and runtime limit.\n\n```ruby\nhandle = client.submit_job(\n  agent: 'weekly-report@2.1.0',\n  input: { 'week' =\u003e '2026-W19' },\n  lease_request: Arcp::Lease::LeaseRequest.new(\n    capabilities: ['net.fetch'],\n    expires_at: (Time.now.utc + 60).iso8601\n  ),\n  lease_constraints: Arcp::Lease::LeaseConstraints.new(\n    expires_at: (Time.now.utc + 300).iso8601,\n    max_budget: nil\n  ),\n  idempotency_key: 'weekly-report-2026-W19',\n  max_runtime_sec: 300\n)\n\nputs \"job_id           = #{handle.job_id}\"\nputs \"resolved agent   = #{handle.agent.inspect}\"\nputs \"effective lease  = #{handle.lease\u0026.to_h.inspect}\"\n```\n\n### Consuming events\n\nIterate the ordered event stream — `log`, `thought`, `tool_call`, `tool_result`, `status`, `metric`, `artifact_ref`, `progress`, `result_chunk` — and optionally acknowledge progress so the runtime can release buffered events early.\n\n```ruby\nlast_seq = 0\nhandle.subscribe(client: client).each do |event|\n  case event.kind\n  when Arcp::Job::EventKind::LOG\n    puts event.body.message\n  when Arcp::Job::EventKind::TOOL_CALL\n    puts \"-\u003e tool #{event.body.tool}(#{event.body.args.inspect})\"\n  when Arcp::Job::EventKind::METRIC\n    puts \"metric #{event.body.name}=#{event.body.value}#{event.body.unit}\"\n  when Arcp::Job::EventKind::PROGRESS\n    puts \"progress #{event.body.current}/#{event.body.total} #{event.body.units}\"\n  end\n  last_seq += 1\n  client.ack(last_seq) if (last_seq % 32).zero?  # coalesced session.ack\nend\n```\n\n### Leases and budgets\n\nRequest capabilities, a budget, and an expiry; read budget-remaining metrics as they arrive; handle the runtime's enforcement decisions.\n\n```ruby\nhandle = client.submit_job(\n  agent: 'web-research',\n  input: { 'iterations' =\u003e 8 },\n  lease_request: Arcp::Lease::LeaseRequest.new(\n    capabilities: ['tool.call', 'net.fetch'],\n    budget: Arcp::Lease::CostBudget.parse(['USD:1.00']),\n    expires_at: (Time.now.utc + 600).iso8601\n  )\n)\n\nputs \"initial budget = #{handle.lease\u0026.budget\u0026.to_a.inspect}\"\n\nhandle.subscribe(client: client).each do |event|\n  next unless event.kind == Arcp::Job::EventKind::METRIC\n  next unless event.body.name == 'cost.budget.remaining'\n\n  puts \"budget remaining: #{event.body.value} #{event.body.unit}\"\nend\n\nbegin\n  handle.get_result(client: client)\nrescue Arcp::Errors::BudgetExhausted, Arcp::Errors::LeaseExpired =\u003e e\n  # Never retryable — resubmit with a fresh lease/budget instead.\n  warn \"job ended: #{e.code} #{e.message}\"\nend\n```\n\n### Subscribing to jobs\n\nAttach read-only to a job submitted elsewhere and observe its live stream (with optional history replay) without cancel authority.\n\n```ruby\nSync do\n  observer = Arcp::Client.open(\n    transport: dashboard_transport,\n    auth: { 'scheme' =\u003e 'bearer', 'token' =\u003e ENV.fetch('ARCP_TOKEN') },\n    client_name: 'dashboard'\n  )\n\n  running = observer.list_jobs(status: 'running', limit: 1).first\n  stream  = observer.subscribe_job(job_id: running.job_id, from_event_seq: 0, history: true)\n\n  stream.each do |event|\n    puts \"[#{running.job_id}] #{event.kind}\"\n  end\n\n  observer.close\nend\n```\n\n### Error handling\n\nCatch the typed error taxonomy and respect the `retryable` flag — `LEASE_EXPIRED` and `BUDGET_EXHAUSTED` are never retryable; a naive retry fails identically.\n\n```ruby\nbegin\n  handle = client.submit_job(agent: 'flaky', input: {})\n  handle.get_result(client: client)\nrescue Arcp::Errors::LeaseExpired, Arcp::Errors::BudgetExhausted =\u003e e\n  raise e  # resubmit with a fresh lease / budget instead\nrescue Arcp::Error =\u003e e\n  if e.retryable?\n    # safe to retry with backoff (e.g. INTERNAL_ERROR, RATE_LIMITED, TIMEOUT)\n    retry_with_backoff(e)\n  else\n    raise\n  end\nend\n```\n\n## Feature support\n\nARCP features this SDK negotiates during the `hello`/`welcome` handshake:\n\n| Feature flag | Status |\n|---|---|\n| `heartbeat` | Supported |\n| `ack` | Supported |\n| `list_jobs` | Supported |\n| `subscribe` | Supported |\n| `lease_expires_at` | Supported |\n| `cost.budget` | Supported |\n| `model.use` | Supported |\n| `provisioned_credentials` | Supported |\n| `progress` | Supported |\n| `result_chunk` | Supported |\n| `agent_versions` | Supported |\n\n## Transport\n\nARCP is transport-agnostic. This SDK ships a WebSocket transport (default), a stdio transport for in-process child runtimes, and an in-memory transport for tests. WebSocket is the default for networked runtimes; stdio is used for in-process child runtimes. Select one by constructing the corresponding transport object and passing it to `Arcp::Client.open(transport:, ...)`: `Arcp::Transport::WebSocketTransport.new(connection: ws)` for production (wrap an open `Async::WebSocket::Connection`, typically hosted under `falcon`), `Arcp::Transport::StdioTransport` for co-process agents, or `Arcp::Transport::MemoryTransport.pair` for in-process tests and embedded clients.\n\n## API reference\n\nFull API reference — every type, method, and event payload — is in [`docs/`](docs/) (YARD-generated reference under [`docs/api/`](docs/api/), rebuilt with `bundle exec rake docs`).\n\n## Versioning and compatibility\n\nThis SDK speaks **ARCP v1.1 (draft)**. The SDK follows semantic versioning independently of the protocol; the negotiated runtime version is available on `client.session.runtime_version`. A runtime advertising a different ARCP MAJOR is not guaranteed compatible. Feature mismatches degrade gracefully: the effective feature set is the intersection of what the client and runtime advertise, and the SDK will not use a feature outside it.\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md). Protocol questions and proposed changes belong in the [spec repository](https://github.com/agentruntimecontrolprotocol/spec); SDK bugs and feature requests belong here.\n\n## License\n\nApache-2.0 — see [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagentruntimecontrolprotocol%2Fruby-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagentruntimecontrolprotocol%2Fruby-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagentruntimecontrolprotocol%2Fruby-sdk/lists"}