Files
codex/codex-rs/cloud-client
Benjamin Carlsson bc4116e4cd Add paginated history reads to the cloud client (#59075)
## Why

`Attach` delivers live events only. Clients also need access to stored turn metadata and item content.

## What changed

- Add `Client::list_turns` and `Client::list_items` using native gRPC history endpoints, with optional turn filtering for items.
- Expose page limits, sort order, access mode, and forward/backward cursors. Each call returns one page in server order without automatic pagination or retries.
- Preserve raw response and row payloads alongside typed summaries and item timestamps. Retain unknown item variants and command sources without typed summaries.
- Document cursor reuse and history/live overlap: history pages do not provide an atomic boundary with `Attach`, and coordinated reconciliation still requires caller-managed state.

## Testing

Add wire-fixture tests for request encoding, explicit pagination, raw payload preservation, unknown data, malformed rows, empty pages, RPC errors without retries, and diagnostic redaction.

GitOrigin-RevId: f507530b8e431236bc7e19d867517af56f74644f
2026-11-03 06:34:51 +00:00
..

Codex cloud client

Reusable Rust client for native ThreadService gRPC over HTTP/2, with no TUI, conventional app-server or private monorepo dependency. This slice supports Resume and live Attach. It does not use the HTTP /grpc JSON gateway.

The caller supplies a trusted native gRPC origin (scheme/host/port, no path), ChatGPT bearer token, selected account ID and configured HttpClientFactory. HTTPS is required except for direct loopback HTTP with an unrestricted, unmanaged factory. This exception bypasses proxies to keep plaintext credentials local. For HTTPS, the shared HTTP client supplies proxy, custom CA and network policy handling; redirects are rejected. Tonic supplies gRPC framing, protobuf encoding, message bounds (64 MiB) and status/trailer handling. Bind factory policy before loading credentials; rebuild on login/account changes.

ResumeRequest.wait = false acknowledges admission, not readiness. Requests are never retried by this crate: a timeout or disconnect can leave work admitted. Attach has no history snapshot or replay guarantee. Dropping it detaches without stopping the thread or answering approvals. Streams end after their first error. Resume and Attach setup have a 150-second local deadline; live streams have no idle deadline. Setup does not send a gRPC deadline that would expire a healthy stream.

Event::Transcript decodes selected text, command output, patch, and turn updates. It validates the thread ID and exposes the decoded fields. Turn updates include start time, completion time, and duration when the server provides them. Other notifications and server requests remain opaque protobuf payloads, preserving unknown fields. These are not ProtoJSON or app-server JSON-RPC. Native status code, message and binary details are available explicitly; credentials, event and error diagnostics are redacted. Item lifecycle updates report supported message, command, and file-change summaries. Agent message summaries retain structured questions even when their text is empty. Unsupported item kinds stay opaque. Approval replies and reconnect are subsequent slices. The minimal envelopes in wire.rs and ResumeRequest mirror ThreadService field numbers; no monorepo checkout or protobuf compiler is required to build this crate.

Provide CODEX_CLOUD_ACCESS_TOKEN and CODEX_CLOUD_ACCOUNT_ID through your environment, then run this standalone consumer from codex-rs:

cargo run -p codex-cloud-client --example attach -- "$CODEX_CLOUD_GRPC_ENDPOINT" "$CODEX_CLOUD_THREAD_ID"

Use an existing authorized thread and native gRPC origin. No production URL is assumed. Public native ingress is a rollout assumption, not proved by fixture tests. This change does not publish a package to a registry.

History reads use native ListTurns (stored turn metadata) and ListItems (one turn or the whole thread). Calls return one page with forward/backward opaque cursors, preserve server order and retain raw responses and rows. Reuse a cursor with the same account, thread and filters. Keep the sort order for next_cursor and reverse it for backwards_cursor. Calls never paginate or retry automatically. Items with an unknown variant or command source remain in the page with no typed summary.

History pages and Attach have no shared revision or replay cursor. A page is not an atomic history/live boundary, and timestamps are not event offsets. Do not append buffered deltas to a fetched snapshot: it may already include that text. Full item snapshots can replace matching account/host/thread/turn/item identities; they must not create duplicate transcript rows. Disconnects, untyped recovery notifications and uncertain overlap require authoritative history reload. This crate exposes the read primitives; a coordinated resync/display state machine is still required before claiming lossless resume or live/history reconciliation.