Skip to content

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 21 May 07:29
· 130 commits to refs/heads/main since this release

Highlights

WebSocket codex-parity sweep — all 8 deepwiki audit gaps closed. The chatgpt.com Codex backend's WebSocket transport now ships at full wire-level parity with upstream openai/codex for the things that affect production behavior. Together with everything from v0.1.9, this is a substantial minor release.

What's new vs v0.1.9

  • Wrapped WebSocket errors now map to typed errors (#117): when the chatgpt.com Codex backend sends a type: "error" text frame instead of a proper HTTP-level status (429 plan-usage, 401 token-expiry, 5xx, websocket_connection_limit_reached), we now decode the envelope and surface the same Error::UsageLimitReached / Error::InvalidStatusCode / forge_domain::Error::Retryable the HTTP path would have. Before: the orchestrator retried with generic EmptyCompletion. Now: existing retry / refresh / fallback policies fire identically across transports.
  • x-codex-beta-features (#118): comma-separated session-scoped beta-feature opt-ins via openai_responses_beta_features = ["foo", "bar"] in .forge.toml.
  • x-codex-turn-metadata (#125): per-turn observability metadata via openai_responses_turn_metadata = { repo = "...", env = "..." }. JSON-encoded, BTreeMap-stable key ordering.
  • x-openai-attestation (#125): client attestation token forwarding via openai_responses_attestation_token = "v1.<opaque>". Static-token model (upstream uses rotating tokens via JSON-RPC; followed-up separately if needed).
  • x-codex-turn-state (#125): sticky backend-routing token captured from WS upgrade response headers and replayed on subsequent reconnects within the conversation. Mirrors ModelClientSession::turn_state's capture-then-replay pattern.
  • Handshake probe + close-frame diagnostic (#125): 50ms post-upgrade poll for an immediate Close frame, surfacing the close code + reason as a typed ConnectError so policy rejections (rate-limited, auth-invalid) come with actionable context instead of an opaque "stream ended".
  • Structured WebSocket telemetry (#125): tracing::info!/warn! events tagged event.kind = "codex.websocket_{connect,request,event}" with duration_ms, success, error.message, sub-kinds for idle_timeout / transport_error / stream_end / response_completed / response_failed. Wire your own OTel / JSON / stdout subscriber.
  • Connection-only WebSocket prewarm (#125): opt-in via openai_responses_prewarm = true; the new OpenAIResponsesProvider::preconnect_websocket(conv_id) API opens + stashes the socket so the first real turn skips TLS+upgrade latency. Connection-only for now (no generate=false stub roundtrip yet).

Carried over from v0.1.9

  • Codex Responses-API parity rounds 1–3 (#66#106): wire-level parity for parallel_tool_calls, client_metadata body fields incl. W3C trace context, SSE output_item.done + reasoning_summary_part.added, structured 429 UsageLimitReached / UsageNotIncluded, reactive 401→refresh + proactive refresh-before-expiry.
  • ReadWithoutWriteDetector (#109, closes #27): orchestrator hook for the analysis-paralysis loop pattern.
  • macOS screenshot drag-drop (#52, closes #51).
  • MCP completeness (#108, closes #26): Audio / ResourceLink / Resource / structuredContent / output_schema variant coverage.
  • Subagent trajectory recording (#112/#114, closes #33): /trace <root_conversation_id> walks the whole subagent tree with parent_agent_id linkage. Live-verified.
  • Credentials hardening (#69): ~/forge/credentials.json is now chmod 0o600.
  • WS protocol pin (#66): OpenAI-Beta: responses_websockets=2026-02-06 on the upgrade.

Install

Recommended (POSIX shell installer, auto-detects OS + arch + libc):

curl -fsSL https://github.com/justrach/codegraff/releases/download/v0.2.0/install.sh | sh

Supported binary downloads

Platform graff codegraff
macOS arm64 (Apple Silicon) graff-aarch64-apple-darwin codegraff-aarch64-apple-darwin
Linux x86_64 (glibc) graff-x86_64-unknown-linux-gnu codegraff-x86_64-unknown-linux-gnu
Linux x86_64 (musl, static) graff-x86_64-unknown-linux-musl codegraff-x86_64-unknown-linux-musl
Linux aarch64 (glibc) graff-aarch64-unknown-linux-gnu codegraff-aarch64-unknown-linux-gnu
Linux aarch64 (musl, static) graff-aarch64-unknown-linux-musl codegraff-aarch64-unknown-linux-musl
Windows x86_64 (MSVC) graff-x86_64-pc-windows-msvc.exe codegraff-x86_64-pc-windows-msvc.exe
Windows aarch64 (MSVC) graff-aarch64-pc-windows-msvc.exe codegraff-aarch64-pc-windows-msvc.exe

CodeDB-bundled tarballs for the Linux-x86_64 line are available as graff-x86_64-unknown-linux-{gnu,musl}-bundle.tar.gz.

Signing / build provenance

Platform Status
macOS arm64 Codesigned (Developer ID WWP9DLJ27P, hardened runtime, RFC 3161 timestamp) and notarized via Apple notary service. Built locally on the maintainer's workstation from the v0.2.0 tag.
Linux + Windows Unsigned. Built by this CI run via the tag-driven Multi Channel Release workflow on ubuntu-latest / windows-latest runners.
macOS x86_64 (Intel) Not shipped in this release — pending CODEDB_LOCAL_APPLE_* GitHub Secrets configuration for CI signing.

Verification

graff --version  # → graff 0.2.0
codesign -dvv $(which graff)  # macOS only → Authority=Developer ID Application: Rachit Pradhan (WWP9DLJ27P)

Known gaps (not shipped, deferred follow-ups)

  • aarch64-linux-android — build fails because arboard (clipboard library in forge_main) does not compile on Android. Needs #[cfg(not(target_os = "android"))] guards.
  • x86_64-apple-darwin (Intel mac) binary — needs the Apple CI secrets so it can land in the same release pipeline as the other platforms.
  • TUI startup wiring for preconnect_websocket() — building blocks shipped (#125), but no caller fires it from session-start yet. Suggested follow-up: tokio::spawn it after auth resolves, before the TUI's main event loop blocks on input.
  • HTTP/SSE-side x-codex-turn-state capture — WS-only today (#125); SSE response-header extraction needs an eventsource-stream plumbing change.
  • prewarm_websocket generate=false stub — the additional roundtrip that primes previous_response_id. Benchmark first.
  • Rotating attestation tokens — openai_responses_attestation_token is static today. If rotation is needed, add openai_responses_attestation_command that shells out.