Skip to content
Open
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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
MessageType::JobAccepted(p) => println!("accepted: {}", p.job_id),
MessageType::JobCompleted(p) => { println!("done: {:?}", p.value); break; }
MessageType::JobFailed(p) => return Err(format!("{}: {}", p.code, p.message).into()),
other => println!("[seq={:?}] {}", env.event_seq, other.type_name()),
other => println!("[msg_id={}] {}", env.id, other.type_name()),
}
}
transport.close().await?;
Expand Down Expand Up @@ -141,7 +141,7 @@ let MessageType::SessionAccepted(welcome) = transport.recv().await?.ok_or("eof")
let session_id = welcome.session_id.clone();
let mut last_seq: u64 = 0;

// ... read envelopes, tracking the highest env.event_seq in `last_seq` ...
// ... read envelopes, tracking the highest countable sequence in `last_seq` ...
// ... transport drops ...

// Reconnect on a fresh transport and resume from `last_seq`:
Expand Down Expand Up @@ -200,9 +200,9 @@ use arcp::Envelope;

let mut last_seq: u64 = 0;
while let Some(env) = transport.recv().await? {
if let Some(seq) = env.event_seq {
last_seq = seq;
}
// Track the highest countable sequence in your own application state.
// let seq = highest_countable_sequence_from_your_runtime();
// last_seq = seq;
match env.payload {
MessageType::Log(p) => println!("[log {:?}] {}", p.level, p.message),
MessageType::Metric(m) => println!("metric[{}] = {} {}", m.name, m.value, m.unit),
Expand Down Expand Up @@ -378,7 +378,7 @@ Full API reference — every type, method, and event payload — is in [`docs/`]

## Versioning and compatibility

This SDK speaks **ARCP v1.1 (draft)**. The SDK follows semantic versioning independently of the protocol; the protocol version it negotiates is shown above and in `session.hello`. 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.
This SDK speaks **ARCP v1.1 (draft)**. The SDK follows semantic versioning independently of the protocol; the protocol version it negotiates is shown above and in `session.accepted`. 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.

## Contributing

Expand Down
12 changes: 6 additions & 6 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ advertises anonymous auth.

## Run examples

Examples are the fastest way to see client/runtime flows. Some are compact
illustrations with setup elided; the integration tests under [`tests/`](../tests/)
show fully exercised paths.
Examples are the fastest way to see client/runtime flows. The commands
below point at runnable demos with real setup; the integration tests under
[`tests/`](../tests/) show fully exercised paths.

```sh
cargo run --example submit_and_stream
cargo run --example resumability
cargo run --example job_subscribe
cargo run --example session_ack
cargo run --example result_chunk
cargo run --example session_list_jobs
cargo run --example cost_budget
cargo run --example provisioned_credentials
```
Expand Down
4 changes: 3 additions & 1 deletion docs/guides/jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Spec reference: [§7](../../../spec/docs/draft-arcp-1.1.md#7-jobs).
submit-and-await flows. It sends a job request, waits for acceptance, and
resolves on the terminal result.

See [`examples/submit_and_stream.rs`](../../examples/submit_and_stream.rs).
See the illustrative [`examples/submit_and_stream.rs`](../../examples/submit_and_stream.rs)
for the submit-and-await shape, and the runnable [`examples/result_chunk/`](../../examples/result_chunk/)
for an end-to-end streaming demo.

## Runtime dispatch

Expand Down
7 changes: 5 additions & 2 deletions docs/guides/resume.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ session.

## Example

[`examples/resumability/`](../../examples/resumability/) demonstrates replaying
events after a session boundary.
[`examples/resumability/`](../../examples/resumability/) sketches the replay
flow after a session boundary, but it is illustrative and still contains
placeholder setup. Use the runnable [`examples/session_ack/`](../../examples/session_ack/)
and [`examples/result_chunk/`](../../examples/result_chunk/) demos for concrete
client/runtime flows.

## Failure modes

Expand Down
23 changes: 13 additions & 10 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
# ARCP Rust examples

Fourteen single-purpose codebases, each named for the protocol primitive
Thirteen single-purpose codebases, each named for the protocol primitive
it demonstrates. Mirrors the Python tree at
[`python-sdk/examples/`](https://github.com/agentruntimecontrolprotocol/python-sdk/tree/main/examples).

> **Illustrative, not runnable.** Each example imports the in-repo `arcp`
> **Mostly illustrative.** Most examples import the in-repo `arcp`
> crate as if it were a published `arcp = "1"`. Setup boilerplate
> (transport URL, identity, auth) is elided with `let client: Client =
> todo!();`. LLM and framework calls live in tiny stub modules
> (`agents.rs`, `steps.rs`, `synth.rs`, ...) so the protocol code in
> `main.rs` is what you read.
> `main.rs` is what you read. The runnable end-to-end examples are the
> concrete demos with real runtime setup, such as `session_ack`,
> `result_chunk`, and `session_list_jobs`.

## The fourteen
## The thirteen

| Example | Demonstrates | Spec |
|---|---|---|
Expand All @@ -23,7 +25,7 @@ it demonstrates. Mirrors the Python tree at
| [`handoff/`](./handoff) | `agent.handoff` with transcript packed as an artifact, runtime fingerprint pinned. | §14, §16, §8.3 |
| [`heartbeats/`](./heartbeats) | Worker federation; heartbeat-loss reroute via `idempotency_key`. | §10.3, §6.4 |
| [`capability_negotiation.rs`](./capability_negotiation.rs) | Capability-driven peer routing; standard `cost.usd` rollups. | §7, §17.3.1, §18.3 |
| [`resumability/`](./resumability) | **Real crash and resume.** `std::process::exit(137)` mid-flight; second invocation picks up at the next step. | §10, §19, §6.4 |
| [`resumability/`](./resumability) | **Illustrative crash and replay.** Shows the two-step flow, but still contains placeholder setup. | §10, §19, §6.4 |
| [`reasoning_streams/`](./reasoning_streams) | `kind: thought` stream + a peer runtime that subscribes and delegates critiques back. | §11.4, §13, §14 |
| [`extensions.rs`](./extensions.rs) | Custom `arcpx.sdr.*.v1` extension namespace + unknown-message handling. | §21 |
| [`cancellation.rs`](./cancellation.rs) | Cooperative `cancel` (terminate) vs `interrupt` (pause and ask). | §10.4–§10.5 |
Expand Down Expand Up @@ -60,11 +62,12 @@ it demonstrates. Mirrors the Python tree at
## Reading order

For a brisk tour: `subscriptions`, `leases`, `delegation`,
`resumability` (this one actually crashes and recovers), `cancellation`,
`extensions`, `mcp`. These seven exercise the bulk of the protocol.
`resumability` (illustrative only), `cancellation`, `extensions`,
`mcp`. These six exercise the bulk of the protocol, plus the
placeholder-heavy `resumability` sketch.

## Numbered companions

`01_minimal_session.rs` and `02_tool_invoke.rs` predate this index;
they're a runnable end-to-end against the in-process runtime + memory
transport. The fourteen above are illustrative.
The examples above are illustrative. The runnable end-to-end demos are
the concrete runtime-backed examples in this tree, including
`session_ack`, `result_chunk`, and `session_list_jobs`.
14 changes: 14 additions & 0 deletions src/client/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,20 @@ impl<T: Transport + 'static> std::fmt::Debug for ARCPClient<T> {

impl<T: Transport + 'static> ARCPClient<T> {
/// Construct over an attached transport.
///
/// ## Examples
///
/// ```rust
/// use arcp::transport::paired;
/// use arcp::ARCPClient;
///
/// # fn main() -> Result<(), arcp::ARCPError> {
/// let (_server_t, client_t) = paired();
/// let client = ARCPClient::new(client_t).open()?;
/// let _ = client;
/// # Ok(())
/// # }
/// ```
#[must_use]
pub const fn new(transport: T) -> Self {
Self {
Expand Down
10 changes: 10 additions & 0 deletions src/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
//!
//! The two are interconvertible via [`Envelope::into_raw`] /
//! [`RawEnvelope::try_into_typed`].
//!
//! ## Examples
//!
//! ```rust
//! use arcp::messages::{MessageType, PingPayload};
//! use arcp::Envelope;
//!
//! let env = Envelope::new(MessageType::Ping(PingPayload::default()));
//! assert_eq!(env.payload.type_name(), "ping");
//! ```

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
Expand Down
39 changes: 39 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,45 @@
//! The public API centers on [`ARCPClient`] for consumers and [`ARCPRuntime`]
//! for runtimes.
//!
//! ## Examples
//!
//! ```rust
//! use arcp::auth::BearerAuthenticator;
//! use arcp::messages::{AuthScheme, Capabilities, ClientIdentity, Credentials};
//! use arcp::runtime::ARCPRuntime;
//! use arcp::transport::paired;
//! use arcp::ARCPClient;
//!
//! # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
//! let runtime = ARCPRuntime::builder()
//! .with_authenticator(Box::new(BearerAuthenticator::new().with_token("tok", "alice")))
//! .build()
//! .await?;
//!
//! let (server_t, client_t) = paired();
//! let _server = runtime.serve_connection(server_t);
//!
//! let client = ARCPClient::new(client_t)
//! .open()?
//! .authenticate(
//! Credentials {
//! scheme: AuthScheme::Bearer,
//! token: Some("tok".into()),
//! },
//! ClientIdentity {
//! kind: "demo-client".into(),
//! version: "1.0.0".into(),
//! fingerprint: None,
//! principal: None,
//! },
//! Capabilities::default(),
//! )
//! .await?;
//! let _ = client;
//! # Ok(())
//! # }
//! ```
//!
//! [rfc]: https://github.com/agentruntimecontrolprotocol/spec/blob/main/docs/draft-arcp-1.1.md

#![deny(unsafe_code)]
Expand Down
34 changes: 34 additions & 0 deletions src/runtime/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,40 @@ impl std::fmt::Debug for ARCPRuntime {

impl ARCPRuntime {
/// Construct via [`RuntimeBuilder`].
///
/// ## Examples
///
/// ```rust
/// use arcp::auth::BearerAuthenticator;
/// use arcp::messages::{AuthScheme, Capabilities, ClientIdentity, Credentials};
/// use arcp::runtime::ARCPRuntime;
/// use arcp::transport::paired;
/// use arcp::ARCPClient;
///
/// # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
/// let runtime = ARCPRuntime::builder()
/// .with_authenticator(Box::new(BearerAuthenticator::new().with_token("tok", "alice")))
/// .build()
/// .await?;
/// let (server_t, client_t) = paired();
/// let _server = runtime.serve_connection(server_t);
///
/// let _session = ARCPClient::new(client_t)
/// .open()?
/// .authenticate(
/// Credentials { scheme: AuthScheme::Bearer, token: Some("tok".into()) },
/// ClientIdentity {
/// kind: "demo-client".into(),
/// version: "1.0.0".into(),
/// fingerprint: None,
/// principal: None,
/// },
/// Capabilities::default(),
/// )
/// .await?;
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn builder() -> RuntimeBuilder {
RuntimeBuilder::new()
Expand Down
16 changes: 16 additions & 0 deletions src/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@
//! minimal: send one envelope, receive the next, close when done. Higher-
//! level concerns (idempotency, ordering, backpressure) live above this
//! layer.
//!
//! ## Examples
//!
//! ```rust
//! use arcp::messages::{MessageType, PingPayload};
//! use arcp::transport::{paired, Transport};
//! use arcp::Envelope;
//!
//! # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
//! let (sender, receiver) = paired();
//! sender.send(Envelope::new(MessageType::Ping(PingPayload::default()))).await?;
//! let env = receiver.recv().await?.expect("envelope");
//! assert_eq!(env.payload.type_name(), "ping");
//! # Ok(())
//! # }
//! ```

use async_trait::async_trait;

Expand Down