Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,19 @@ Clients can pin to a specific agent version:

```python
handle = await client.submit(
agent="summarise",
agent="summarise@2",
input={"url": "https://example.com"},
agent_version="2",
)
```

Runtimes register versioned agents:

```python
runtime.register_agent("summarise", summarise_v1, version="1")
runtime.register_agent("summarise", summarise_v2, version="2")
runtime.register_agent_version("summarise", "1", summarise_v1)
runtime.register_agent_version("summarise", "2", summarise_v2)
```

If the client omits `agent_version`, the runtime uses the latest registered version.
If the client omits the version suffix, the runtime uses the latest registered version.

## Middleware

Expand Down
55 changes: 15 additions & 40 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,47 @@ pip install arcp

### `arcp serve`

Start an ARCP runtime serving agents from a Python module.
Run the built-in demo runtime.

```bash
arcp serve my_module:runtime --host 0.0.0.0 --port 8080
arcp serve --token demo-token --principal cli-user
```

**Arguments:**

| Argument | Default | Description |
|---|---|---|
| `MODULE:ATTR` | required | Python import path to an `ARCPRuntime` instance |
| `--host` | `127.0.0.1` | Bind address |
| `--port` | `8080` | Bind port |
| `--reload` | off | Auto-reload on file changes (dev only) |
| `--port` | `7777` | Bind port |
| `--token` | required | Demo bearer token to accept |
| `--principal` | `cli-user` | Principal bound to the accepted token |
| `--db` | off | Optional SQLite event log path |

### `arcp submit`

Submit a job to a running runtime and print the result.

```bash
arcp submit ws://localhost:8080/arcp summarise '{"url":"https://example.com"}' \
--token my-bearer-token
arcp submit --url ws://localhost:7777/arcp --agent echo \
--input '{"url":"https://example.com"}' --token my-bearer-token
```

**Arguments:**

| Argument | Description |
|---|---|
| `URL` | WebSocket URL of the runtime |
| `AGENT` | Agent name |
| `INPUT` | JSON-encoded input (use `@file.json` to read from file) |
| `--url` | WebSocket URL of the runtime |
| `--agent` | Agent name |
| `--input` | JSON-encoded input |
| `--token` | Bearer token |
| `--lease-max-cost` | Maximum spend in USD (e.g. `0.10`) |
| `--lease-expires-in` | Maximum wall time in seconds |
| `--idempotency-key` | Idempotency key |
| `--stream` | Print result chunks as they arrive |
| `--lease` | Lease JSON object |

### `arcp tail`

Tail the event stream for a job in real time.

```bash
arcp tail ws://localhost:8080/arcp JOB_ID --token my-bearer-token
arcp tail --url ws://localhost:7777/arcp --job-id JOB_ID --token my-bearer-token
```

Prints each `JobEvent` as a JSON line. Press `Ctrl-C` to stop.
Expand All @@ -65,30 +63,7 @@ Prints each `JobEvent` as a JSON line. Press `Ctrl-C` to stop.
Replay a recorded event stream from a JSONL file.

```bash
arcp replay events.jsonl
arcp replay --db events.sqlite --session SESSION_ID
```

Useful for debugging: record a live stream with `arcp tail --output events.jsonl`, then replay it offline.

## Environment variables

All CLI options can also be set via environment variables:

| Variable | CLI equivalent |
|---|---|
| `ARCP_TOKEN` | `--token` |
| `ARCP_HOST` | `arcp serve --host` |
| `ARCP_PORT` | `arcp serve --port` |

## Shell completion

```bash
# bash
arcp --install-completion bash

# zsh
arcp --install-completion zsh

# fish
arcp --install-completion fish
```
Useful for debugging: record a live stream from `tail` and replay it from the SQLite event log.
63 changes: 32 additions & 31 deletions docs/conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,43 @@ This page records which sections of the [ARCP v1.1 specification](https://arcp.d

| Spec section | Title | Status | Source |
|---|---|---|---|
| §4 | Versioning | ✅ Full | `arcp/_version.py` |
| §5 | Transport framing | ✅ Full | `arcp/_transport/` |
| §6 | Sessions | ✅ Full | `arcp/runtime/session.py` |
| §6.1 | Authentication — Bearer | ✅ Full | `arcp/runtime/auth.py` |
| §6.1 | Authentication — custom verifier | ✅ Full | `arcp/runtime/auth.py` |
| §6.2 | Agent versions | ✅ Full | `arcp/runtime/registry.py` |
| §6.3 | Stream resume | ✅ Full | `arcp/runtime/resume.py` |
| §7 | Jobs | ✅ Full | `arcp/runtime/job_runner.py` |
| §7.1 | Idempotency keys | ✅ Full | `arcp/runtime/idempotency.py` |
| §7.2 | Job cancellation | ✅ Full | `arcp/runtime/job_runner.py` |
| §8 | Job events | ✅ Full | `arcp/_envelope.py` |
| §8.1 | `job.queued` | ✅ Full | `arcp/_envelope.py` |
| §8.2 | `job.started` | ✅ Full | `arcp/_envelope.py` |
| §8.3 | `job.log` | ✅ Full | `arcp/runtime/context.py` |
| §8.4 | `job.progress` | ✅ Full | `arcp/runtime/context.py` |
| §8.5 | `job.result_chunk` | ✅ Full | `arcp/runtime/context.py` |
| §8.6 | `job.completed` | ✅ Full | `arcp/runtime/job_runner.py` |
| §8.7 | `job.failed` | ✅ Full | `arcp/runtime/job_runner.py` |
| §8.8 | `job.cancelled` | ✅ Full | `arcp/runtime/job_runner.py` |
| §8.9 | `job.heartbeat` | ✅ Full | `arcp/runtime/heartbeat.py` |
| §9 | Leases | ✅ Full | `arcp/runtime/lease.py` |
| §9.1 | Cost budgets | ✅ Full | `arcp/runtime/lease.py` |
| §9.2 | Time budgets (`expires_in_s`) | ✅ Full | `arcp/runtime/lease.py` |
| §9.3 | `expires_at` (absolute timestamp) | ✅ Full | `arcp/runtime/lease.py` |
| §10 | Delegation | ✅ Full | `arcp/runtime/delegation.py` |
| §11 | Observability | ✅ Full | `arcp/middleware/otel.py` |
| §12 | Errors | ✅ Full | `arcp/_errors.py` |
| §13 | Capability negotiation | ✅ Full | `arcp/runtime/capabilities.py` |
| §14 | List jobs | ✅ Full | `arcp/runtime/job_store.py` |
| §15 | Vendor extensions | ✅ Full | `arcp/_extensions.py` |
| §4 | Versioning | ✅ Full | `src/arcp/_version.py` |
| §5 | Transport framing | ✅ Full | `src/arcp/_transport/` |
| §6 | Sessions | ✅ Full | `src/arcp/_runtime/session.py` |
| §6.1 | Authentication — Bearer | ✅ Full | `src/arcp/_auth/bearer.py` |
| §6.1 | Authentication — custom verifier | ✅ Full | `src/arcp/_auth/jwt.py` |
| §6.2 | Agent versions | ✅ Full | `src/arcp/_runtime/server.py` |
| §6.3 | Stream resume | ✅ Full | `src/arcp/_runtime/session.py` |
| §7 | Jobs | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §7.1 | Idempotency keys | ✅ Full | `src/arcp/_store/idempotency.py` |
| §7.2 | Job cancellation | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §8 | Job events | ✅ Full | `src/arcp/_envelope.py` |
| §8.1 | `job.queued` | ✅ Full | `src/arcp/_envelope.py` |
| §8.2 | `job.started` | ✅ Full | `src/arcp/_envelope.py` |
| §8.3 | `job.log` | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §8.4 | `job.progress` | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §8.5 | `job.result_chunk` | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §8.6 | `job.completed` | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §8.7 | `job.failed` | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §8.8 | `job.cancelled` | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §8.9 | `job.heartbeat` | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §9 | Leases | ✅ Full | `src/arcp/_runtime/lease.py` |
| §9.1 | Cost budgets | ✅ Full | `src/arcp/_runtime/lease.py` |
| §9.2 | Time budgets (`expires_in_s`) | ✅ Full | `src/arcp/_runtime/lease.py` |
| §9.3 | `expires_at` (absolute timestamp) | ✅ Full | `src/arcp/_runtime/lease.py` |
| §10 | Delegation | ✅ Full | `src/arcp/_runtime/_handlers.py` |
| §11 | Observability | ✅ Full | `src/arcp/middleware/otel.py` |
| §12 | Errors | ✅ Full | `src/arcp/_errors.py` |
| §13 | Capability negotiation | ✅ Full | `src/arcp/_runtime/server.py` |
| §14 | List jobs | ✅ Full | `src/arcp/_runtime/_handler_list_jobs.py` |
| §15 | Vendor extensions | ✅ Full | `src/arcp/_extensions.py` |

## Notes

### §6.3 Stream resume

Resume tokens are opaque strings returned in the `job.started` event. Pass the token in a new `submit()` call with `resume_token=...` to replay missed events from that point forward.
Resume remains session-scoped in the current implementation. Treat the older
`resume_token` submit flow as deferred until the runtime exposes it publicly.

### §9.3 `expires_at`

Expand Down
19 changes: 10 additions & 9 deletions docs/guides/jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ print(result.result)
| `input` | `dict` | Arbitrary JSON-serialisable payload |
| `lease_request` | `dict` | Budget constraints (see [Leases](leases.md)) |
| `idempotency_key` | `str` | Deduplicate re-submissions (see below) |
| `agent_version` | `str` | Pin to a specific agent version |
| `resume_token` | `str` | Resume an interrupted stream |
| `resume_from_seq` | `int` | Replay from this event sequence number |
| `lease_constraints` | `dict` | Optional absolute expiry and related constraints |
| `max_runtime_sec` | `int` | Maximum runtime before timeout |
| `trace_id` | `str` | Optional trace correlation id |
| `parent_job_id` | `str` | Parent job for delegation or tracing |

## Registering an agent

Expand All @@ -47,23 +48,23 @@ The `ctx` object gives agents access to logging, progress, streaming, cost repor
```python
async def my_agent(input, ctx):
await ctx.log("info", "starting")
await ctx.progress(0, 100)
await ctx.progress(0, total=100)

for i in range(10):
chunk = await do_work(i)
await ctx.result_chunk(chunk)
await ctx.progress((i + 1) * 10, 100)
await ctx.report_cost(0.001) # USD
await ctx.progress((i + 1) * 10, total=100)
await ctx.metric({"name": "cost.inference", "value": 0.001, "unit": "USD"})

return {"done": True}
```

| Method | Description |
|---|---|
| `ctx.log(level, message)` | Emit a `job.log` event |
| `ctx.progress(done, total)` | Emit a `job.progress` event |
| `ctx.progress(current, total=..., units=..., message=...)` | Emit a `job.progress` event |
| `ctx.result_chunk(chunk)` | Emit a `job.result_chunk` event |
| `ctx.report_cost(usd)` | Accumulate cost against the lease |
| `ctx.metric(body)` | Emit a `metric` event |
| `ctx.principal` | The authenticated client identity |
| `ctx.job_id` | The current job ID |
| `ctx.session_id` | The current session ID |
Expand All @@ -85,7 +86,7 @@ async for event in handle.events():
```python
handle = await client.submit(agent="slow", input={})
await asyncio.sleep(1.0)
await handle.cancel()
await client.cancel_job(handle.job_id)
# handle.done raises JobCancelledError
```

Expand Down
4 changes: 2 additions & 2 deletions docs/guides/leases.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ handle = await client.submit(
)
```

The agent reports costs via `ctx.report_cost(usd)`:
The agent reports costs via `ctx.metric(...)`:

```python
async def gpt_4_summary(input, ctx):
response = await call_openai(input["text"])
cost = response.usage.total_tokens * 0.00003
await ctx.report_cost(cost)
await ctx.metric({"name": "cost.openai", "value": cost, "unit": "USD"})
return {"summary": response.text}
```

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ trace.set_tracer_provider(provider)
| `arcp.jobs.active` | Gauge | Currently running jobs |
| `arcp.jobs.total` | Counter | Total jobs submitted |
| `arcp.job.duration_ms` | Histogram | Job wall-clock duration |
| `arcp.job.cost_usd` | Histogram | Job cost (from `ctx.report_cost`) |
| `arcp.job.cost_usd` | Histogram | Job cost (from `ctx.metric({"name": "cost.*", ...})`) |
| `arcp.events.total` | Counter | Total events emitted |

## Propagating trace context
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/resume.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ If a connection drops while a job is running, the client can **resume** the even

1. The runtime emits a `job.started` event containing a `resume_token`.
2. The client stores the token and the last acknowledged `event_seq`.
3. If the connection drops, the client reconnects and calls `submit()` again with `resume_token=...`.
3. If the connection drops, the client reconnects and calls `client.resume(..., resume=SessionResume(...))`.
4. The runtime replays events from `event_seq + 1` onward.

The job itself is **not** re-executed. Only the event stream is replayed.
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/ack-backpressure.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pauses production and emits a `status` event with
Source: [`../../examples/ack_backpressure/`](../../examples/ack_backpressure/).

```sh
uv run python -m examples.ack_backpressure.runtime &
uv run python -m examples.ack_backpressure.server &
uv run python -m examples.ack_backpressure.client
```

Expand Down
4 changes: 2 additions & 2 deletions docs/recipes/agent-versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ the example asserts both jobs ran the expected handler.
Source: [`../../examples/agent_versions/`](../../examples/agent_versions/).

```sh
uv run python -m examples.agent_versions.runtime &
uv run python -m examples.agent_versions.server &
uv run python -m examples.agent_versions.client
```

## See also

- Guide: [Sessions](../guides/sessions.md) — agent versions (§6.2).
- Guide: [Jobs](../guides/jobs.md) — `agent_version` submit option.
- Guide: [Jobs](../guides/jobs.md) — versioned agent names.
2 changes: 1 addition & 1 deletion docs/recipes/cancel.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ terminal envelope ships.
Source: [`../../examples/cancel/`](../../examples/cancel/).

```sh
uv run python -m examples.cancel.runtime &
uv run python -m examples.cancel.server &
uv run python -m examples.cancel.client
```

Expand Down
4 changes: 2 additions & 2 deletions docs/recipes/cost-budget.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

A client submits a job with `max_cost_usd: 0.05`; the agent
reports successive cost increments until the budget reaches zero,
and the next `ctx.report_cost(...)` call fails with `LeaseExceededError`.
and the next `ctx.metric(...)` call fails with `LeaseExceededError`.

Source: [`../../examples/cost_budget/`](../../examples/cost_budget/).

```sh
uv run python -m examples.cost_budget.runtime &
uv run python -m examples.cost_budget.server &
uv run python -m examples.cost_budget.client
```

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/custom-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ authentication and the rest of the protocol.
Source: [`../../examples/custom_auth/`](../../examples/custom_auth/).

```sh
uv run python -m examples.custom_auth.runtime &
uv run python -m examples.custom_auth.server &
uv run python -m examples.custom_auth.client
```

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/delegate.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ delegation composes the recursive case of jobs / events / leases.
Source: [`../../examples/delegate/`](../../examples/delegate/).

```sh
uv run python -m examples.delegate.runtime &
uv run python -m examples.delegate.server &
uv run python -m examples.delegate.client
```

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/heartbeat.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ client to surface `HEARTBEAT_LOST` and a `session.error` close.
Source: [`../../examples/heartbeat/`](../../examples/heartbeat/).

```sh
uv run python -m examples.heartbeat.runtime &
uv run python -m examples.heartbeat.server &
uv run python -m examples.heartbeat.client
```

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/host-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ without an external collector.
Source: [`../../examples/host_tracing/`](../../examples/host_tracing/).

```sh
uv run python -m examples.host_tracing.runtime &
uv run python -m examples.host_tracing.server &
uv run python -m examples.host_tracing.client
```

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/idempotent-retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ idempotency store.
Source: [`../../examples/idempotent_retry/`](../../examples/idempotent_retry/).

```sh
uv run python -m examples.idempotent_retry.runtime &
uv run python -m examples.idempotent_retry.server &
uv run python -m examples.idempotent_retry.client
```

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/lease-expires-at.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sleeps past the deadline and the next `ctx.authorize(...)` raises
Source: [`../../examples/lease_expires_at/`](../../examples/lease_expires_at/).

```sh
uv run python -m examples.lease_expires_at.runtime &
uv run python -m examples.lease_expires_at.server &
uv run python -m examples.lease_expires_at.client
```

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/lease-violation.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ the lease validator path and the failure surface in `arcp._errors`.
Source: [`../../examples/lease_violation/`](../../examples/lease_violation/).

```sh
uv run python -m examples.lease_violation.runtime &
uv run python -m examples.lease_violation.server &
uv run python -m examples.lease_violation.client
```

Expand Down
Loading