v0.2.0
·
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 sameError::UsageLimitReached/Error::InvalidStatusCode/forge_domain::Error::Retryablethe HTTP path would have. Before: the orchestrator retried with genericEmptyCompletion. Now: existing retry / refresh / fallback policies fire identically across transports. x-codex-beta-features(#118): comma-separated session-scoped beta-feature opt-ins viaopenai_responses_beta_features = ["foo", "bar"]in.forge.toml.x-codex-turn-metadata(#125): per-turn observability metadata viaopenai_responses_turn_metadata = { repo = "...", env = "..." }. JSON-encoded,BTreeMap-stable key ordering.x-openai-attestation(#125): client attestation token forwarding viaopenai_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. MirrorsModelClientSession::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+reasonas a typedConnectErrorso policy rejections (rate-limited, auth-invalid) come with actionable context instead of an opaque "stream ended". - Structured WebSocket telemetry (#125):
tracing::info!/warn!events taggedevent.kind = "codex.websocket_{connect,request,event}"withduration_ms,success,error.message, sub-kinds foridle_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 newOpenAIResponsesProvider::preconnect_websocket(conv_id)API opens + stashes the socket so the first real turn skips TLS+upgrade latency. Connection-only for now (nogenerate=falsestub 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_metadatabody fields incl. W3C trace context, SSEoutput_item.done+reasoning_summary_part.added, structured 429UsageLimitReached/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 withparent_agent_idlinkage. Live-verified. - Credentials hardening (#69):
~/forge/credentials.jsonis nowchmod 0o600. - WS protocol pin (#66):
OpenAI-Beta: responses_websockets=2026-02-06on 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 | shSupported 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 becausearboard(clipboard library inforge_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::spawnit after auth resolves, before the TUI's main event loop blocks on input. - HTTP/SSE-side
x-codex-turn-statecapture — WS-only today (#125); SSE response-header extraction needs an eventsource-stream plumbing change. prewarm_websocketgenerate=falsestub — the additional roundtrip that primesprevious_response_id. Benchmark first.- Rotating attestation tokens —
openai_responses_attestation_tokenis static today. If rotation is needed, addopenai_responses_attestation_commandthat shells out.