From f98ddcb9f6bc3db3900a17efef631f1b98d0826d Mon Sep 17 00:00:00 2001 From: Nick Ficano Date: Sat, 23 May 2026 23:15:40 -0400 Subject: [PATCH] docs: refresh CLI and conformance references --- docs/architecture.md | 9 ++--- docs/cli.md | 55 ++++++++------------------- docs/conformance.md | 63 ++++++++++++++++--------------- docs/guides/jobs.md | 19 +++++----- docs/guides/leases.md | 4 +- docs/guides/observability.md | 2 +- docs/guides/resume.md | 2 +- docs/recipes/ack-backpressure.md | 2 +- docs/recipes/agent-versions.md | 4 +- docs/recipes/cancel.md | 2 +- docs/recipes/cost-budget.md | 4 +- docs/recipes/custom-auth.md | 2 +- docs/recipes/delegate.md | 2 +- docs/recipes/heartbeat.md | 2 +- docs/recipes/host-tracing.md | 2 +- docs/recipes/idempotent-retry.md | 2 +- docs/recipes/lease-expires-at.md | 2 +- docs/recipes/lease-violation.md | 2 +- docs/recipes/list-jobs.md | 2 +- docs/recipes/progress.md | 2 +- docs/recipes/result-chunk.md | 2 +- docs/recipes/resume.md | 2 +- docs/recipes/submit-and-stream.md | 2 +- docs/recipes/subscribe.md | 2 +- docs/recipes/vendor-extensions.md | 2 +- docs/transports.md | 16 ++++---- docs/troubleshooting.md | 4 +- src/arcp/_envelope.py | 8 +++- 28 files changed, 101 insertions(+), 121 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 2e638a4..efe21ae 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -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 diff --git a/docs/cli.md b/docs/cli.md index e45cc70..311e2c8 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -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. @@ -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. diff --git a/docs/conformance.md b/docs/conformance.md index 96a8956..4a370c6 100644 --- a/docs/conformance.md +++ b/docs/conformance.md @@ -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` diff --git a/docs/guides/jobs.md b/docs/guides/jobs.md index e00deed..3d8d33f 100644 --- a/docs/guides/jobs.md +++ b/docs/guides/jobs.md @@ -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 @@ -47,13 +48,13 @@ 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} ``` @@ -61,9 +62,9 @@ async def my_agent(input, ctx): | 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 | @@ -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 ``` diff --git a/docs/guides/leases.md b/docs/guides/leases.md index fcb2e74..4228c14 100644 --- a/docs/guides/leases.md +++ b/docs/guides/leases.md @@ -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} ``` diff --git a/docs/guides/observability.md b/docs/guides/observability.md index f55e17a..97114c0 100644 --- a/docs/guides/observability.md +++ b/docs/guides/observability.md @@ -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 diff --git a/docs/guides/resume.md b/docs/guides/resume.md index 050e612..f745656 100644 --- a/docs/guides/resume.md +++ b/docs/guides/resume.md @@ -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. diff --git a/docs/recipes/ack-backpressure.md b/docs/recipes/ack-backpressure.md index 0a4b614..1816746 100644 --- a/docs/recipes/ack-backpressure.md +++ b/docs/recipes/ack-backpressure.md @@ -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 ``` diff --git a/docs/recipes/agent-versions.md b/docs/recipes/agent-versions.md index 5766aa3..5d9b6f9 100644 --- a/docs/recipes/agent-versions.md +++ b/docs/recipes/agent-versions.md @@ -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. diff --git a/docs/recipes/cancel.md b/docs/recipes/cancel.md index dd4f91d..10747d2 100644 --- a/docs/recipes/cancel.md +++ b/docs/recipes/cancel.md @@ -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 ``` diff --git a/docs/recipes/cost-budget.md b/docs/recipes/cost-budget.md index 05ff215..72eb2ac 100644 --- a/docs/recipes/cost-budget.md +++ b/docs/recipes/cost-budget.md @@ -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 ``` diff --git a/docs/recipes/custom-auth.md b/docs/recipes/custom-auth.md index dca5842..4db81d0 100644 --- a/docs/recipes/custom-auth.md +++ b/docs/recipes/custom-auth.md @@ -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 ``` diff --git a/docs/recipes/delegate.md b/docs/recipes/delegate.md index 0bbc3e1..cbbe151 100644 --- a/docs/recipes/delegate.md +++ b/docs/recipes/delegate.md @@ -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 ``` diff --git a/docs/recipes/heartbeat.md b/docs/recipes/heartbeat.md index 0ce0b85..74e6db4 100644 --- a/docs/recipes/heartbeat.md +++ b/docs/recipes/heartbeat.md @@ -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 ``` diff --git a/docs/recipes/host-tracing.md b/docs/recipes/host-tracing.md index 4617422..5f204d1 100644 --- a/docs/recipes/host-tracing.md +++ b/docs/recipes/host-tracing.md @@ -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 ``` diff --git a/docs/recipes/idempotent-retry.md b/docs/recipes/idempotent-retry.md index e55e847..1788c47 100644 --- a/docs/recipes/idempotent-retry.md +++ b/docs/recipes/idempotent-retry.md @@ -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 ``` diff --git a/docs/recipes/lease-expires-at.md b/docs/recipes/lease-expires-at.md index 02f8c54..b0235ec 100644 --- a/docs/recipes/lease-expires-at.md +++ b/docs/recipes/lease-expires-at.md @@ -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 ``` diff --git a/docs/recipes/lease-violation.md b/docs/recipes/lease-violation.md index d917398..d96c580 100644 --- a/docs/recipes/lease-violation.md +++ b/docs/recipes/lease-violation.md @@ -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 ``` diff --git a/docs/recipes/list-jobs.md b/docs/recipes/list-jobs.md index 7ff6133..d2fd5bf 100644 --- a/docs/recipes/list-jobs.md +++ b/docs/recipes/list-jobs.md @@ -7,7 +7,7 @@ The client submits three jobs against the same agent, then issues Source: [`../../examples/list_jobs/`](../../examples/list_jobs/). ```sh -uv run python -m examples.list_jobs.runtime & +uv run python -m examples.list_jobs.server & uv run python -m examples.list_jobs.client ``` diff --git a/docs/recipes/progress.md b/docs/recipes/progress.md index 5c6c7cc..aaae471 100644 --- a/docs/recipes/progress.md +++ b/docs/recipes/progress.md @@ -7,7 +7,7 @@ running tally before consuming the terminal `job.result`. Source: [`../../examples/progress/`](../../examples/progress/). ```sh -uv run python -m examples.progress.runtime & +uv run python -m examples.progress.server & uv run python -m examples.progress.client ``` diff --git a/docs/recipes/result-chunk.md b/docs/recipes/result-chunk.md index ed33b8d..55db627 100644 --- a/docs/recipes/result-chunk.md +++ b/docs/recipes/result-chunk.md @@ -7,7 +7,7 @@ asserts the reassembled bytes match the source. Source: [`../../examples/result_chunk/`](../../examples/result_chunk/). ```sh -uv run python -m examples.result_chunk.runtime & +uv run python -m examples.result_chunk.server & uv run python -m examples.result_chunk.client ``` diff --git a/docs/recipes/resume.md b/docs/recipes/resume.md index dfc021a..dc48d29 100644 --- a/docs/recipes/resume.md +++ b/docs/recipes/resume.md @@ -8,7 +8,7 @@ since `last_event_seq` from its event log. Source: [`../../examples/resume/`](../../examples/resume/). ```sh -uv run python -m examples.resume.runtime & +uv run python -m examples.resume.server & uv run python -m examples.resume.client ``` diff --git a/docs/recipes/submit-and-stream.md b/docs/recipes/submit-and-stream.md index a4e7e8e..0e3b8ba 100644 --- a/docs/recipes/submit-and-stream.md +++ b/docs/recipes/submit-and-stream.md @@ -10,7 +10,7 @@ Source: [`../../examples/submit_and_stream/`](../../examples/submit_and_stream/) Run the two-process WebSocket variant: ```sh -uv run python -m examples.submit_and_stream.runtime & +uv run python -m examples.submit_and_stream.server & uv run python -m examples.submit_and_stream.client ``` diff --git a/docs/recipes/subscribe.md b/docs/recipes/subscribe.md index e5eaa69..b6b9f8a 100644 --- a/docs/recipes/subscribe.md +++ b/docs/recipes/subscribe.md @@ -7,7 +7,7 @@ replays the prior events before continuing live. Source: [`../../examples/subscribe/`](../../examples/subscribe/). ```sh -uv run python -m examples.subscribe.runtime & +uv run python -m examples.subscribe.server & uv run python -m examples.subscribe.submitter & uv run python -m examples.subscribe.observer ``` diff --git a/docs/recipes/vendor-extensions.md b/docs/recipes/vendor-extensions.md index 96eedac..15af033 100644 --- a/docs/recipes/vendor-extensions.md +++ b/docs/recipes/vendor-extensions.md @@ -7,7 +7,7 @@ contract: unknown `x-` fields pass through validators untouched. Source: [`../../examples/vendor_extensions/`](../../examples/vendor_extensions/). ```sh -uv run python -m examples.vendor_extensions.runtime & +uv run python -m examples.vendor_extensions.server & uv run python -m examples.vendor_extensions.client ``` diff --git a/docs/transports.md b/docs/transports.md index 000d683..6209ba7 100644 --- a/docs/transports.md +++ b/docs/transports.md @@ -18,12 +18,13 @@ class Transport(Protocol): The fastest option — no sockets, no serialisation overhead. Use it in tests and single-process demos. ```python -from arcp import pair_memory_transports +from arcp import ARCPClient, ClientInfo, pair_memory_transports client_t, server_t = pair_memory_transports() async with asyncio.TaskGroup() as tg: tg.create_task(runtime.accept(server_t)) + client = ARCPClient(client=ClientInfo(name="my-client", version="1.0.0"), token=TOKEN) await client.connect(client_t) ``` @@ -47,11 +48,10 @@ app.add_middleware(ARCPMiddleware, runtime=runtime) **Client**: ```python -from arcp import ARCPClient, ClientInfo -from arcp.transport import WebSocketClientTransport +from arcp import ARCPClient, ClientInfo, WebSocketTransport client = ARCPClient(client=ClientInfo(name="my-client", version="1.0.0"), token=TOKEN) -await client.connect(WebSocketClientTransport("ws://localhost:8000/arcp")) +await client.connect(WebSocketTransport.connect("ws://localhost:8000/arcp")) ``` See the [host-asgi recipe](recipes/host-asgi.md) for a runnable server. @@ -63,19 +63,19 @@ For subprocess-based runtimes. The parent process spawns a child; the child read **Parent (client side)**: ```python -from arcp.transport import StdioClientTransport +from arcp import StdioTransport -transport = await StdioClientTransport.spawn(["python", "-m", "my_agent_server"]) +transport = await StdioTransport.spawn(["python", "-m", "my_agent_server"]) await client.connect(transport) ``` **Child (server side)** — in `my_agent_server/__main__.py`: ```python -from arcp.transport import StdioServerTransport +from arcp import StdioTransport async def main(): - transport = StdioServerTransport() + transport = StdioTransport() await runtime.accept(transport) asyncio.run(main()) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index ddd5115..10e8e14 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -49,7 +49,7 @@ client = ARCPClient(..., token="valid-token") ### `LeaseExceededError: lease.exceeded` -**Cause:** The job called `ctx.report_cost(amount)` and the running total exceeded `lease_request["max_cost_usd"]`. +**Cause:** The job emitted a cost metric via `ctx.metric(...)` and the running total exceeded `lease_request["max_cost_usd"]`. **Fix:** Either raise the budget in `lease_request` or reduce cost reporting in the agent. @@ -57,7 +57,7 @@ client = ARCPClient(..., token="valid-token") **Cause:** The connection dropped and stream resume was not configured. -**Fix:** Pass a `resume_token` from the `job.started` event to a new `submit()` call. See [Stream resume guide](guides/resume.md). +**Fix:** Pass the current `resume_token` from the session handshake to a new `submit()` call. See [Stream resume guide](guides/resume.md). ### `RuntimeError: TaskGroup already finished` diff --git a/src/arcp/_envelope.py b/src/arcp/_envelope.py index ffde4a8..ff45648 100644 --- a/src/arcp/_envelope.py +++ b/src/arcp/_envelope.py @@ -1,4 +1,4 @@ -"""ARCP wire envelope (spec §5.1): 8 fields, `arcp` constant, frozen pydantic model.""" +"""ARCP wire envelope (spec §5.1): 8 fields, `arcp` constant, mutable pydantic model.""" from __future__ import annotations @@ -24,7 +24,11 @@ def _is_valid_trace_id(value: str) -> bool: class Envelope(BaseModel): - """Outer envelope for every wire frame. 8 fields per spec §5.1.""" + """Outer envelope for every wire frame. 8 fields per spec §5.1. + + The model stays mutable so runtime code can stamp routing metadata + and copy envelopes with `model_copy(update=...)` when needed. + """ model_config = ConfigDict(extra="allow", frozen=False, populate_by_name=True)