Reverts violations of the durable rule that current-state docs belong
in LLM.md and history belongs in git log. Removed files were session
handoffs, agent-style "complete success" / "1000%" reports, dated
audit dumps, and stub NOTES.
codex-rs depends on v8 (via code-mode) which has no musl prebuilt,
breaking CI builds. Revert release workflow to hanzo-dev workspace
which doesn't have this dependency.
Also disable the ★ Upgrade Available ★ banner — updates are handled
via CI/CD releases, not in-TUI upgrade prompts.
Review finding: unwrap_or_else(|_| reqwest::Client::new()) silently
reverts to native-tls if rustls builder fails, defeating the TLS fix.
Replace with expect() since rustls builder should never fail in
practice. Also includes linter cleanups.
The assistant bg tint was only painted within the content area (minus
padding), leaving 1-column gaps on the left and right edges. Now paints
the tint across the full history_area width so assistant messages have
edge-to-edge background color.
Zen models (qwen3-based) emit <think>...</think> blocks inline in the
answer stream. These were rendered as raw text in both the TUI and CLI.
Added ThinkTagFilter that handles streaming deltas where tags can span
multiple chunks. Applied to both:
- TUI streaming controller (push_and_maybe_commit + apply_final_answer)
- CLI event processor (AgentMessageDelta + AgentMessage)
Also increased assistant message background tint from 5% to 12% — the
original value was nearly invisible on dark/black terminals (resulted in
RGB ~(6,10,13) which looked identical to pure black).
When both rustls-tls and native-tls features are compiled in (via
chromiumoxide → reqwest default features), reqwest silently defaults to
native-tls. On macOS this is SecureTransport, which fails TLS handshake
against Go crypto/tls servers (Traefik ingress) with "bad protocol
version".
Fix: explicitly call .use_rustls_tls() on every reqwest::Client::builder()
to ensure consistent TLS behavior across all platforms.
Also reverts the earlier native-tls experiment in Cargo.toml files back
to rustls-tls and removes temporary TLS diagnostic logging.
When footer_height is 0 (zen mode), the input area was stretching
to fill all available height. Now it uses input_height and anchors
to the bottom of the available area, giving a compact input bar.
The textarea was painting its rows with the main background() color
instead of input_background(), causing the input area to show as
pure black instead of the subtle gray set by the Block. This broke
the contiguous background appearance of the input area.
Login screen now offers 5 options:
1. Sign in with Hanzo ID (default, via hanzo.id OAuth)
2. Sign in with ChatGPT (OpenAI OAuth)
3. Sign in with Claude (keychain auto-detect + claude.ai OAuth fallback)
4. Provide your own API key
5. Sign in with Device Code (headless)
CLI: `hanzo login --provider hanzo|openai|claude`
Auth error handling:
- 401/403 responses are no longer retryable (stops "Reconnecting..." loop)
- Clear error message: "Authentication failed (401). Run `hanzo login` to sign in."
- Maps to CodexErrorInfo::Unauthorized for frontend handling
Fixes:
- Remove duplicate test functions from merge (chatwidget/tests.rs)
- Fix SessionTelemetry::new() calls missing originator parameter (otel tests)
Auth changes:
- HANZO_API_KEY has highest priority, then CODEX_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY
- Default OAuth issuer changed to hanzo.id (Casdoor)
- Skip API key exchange for non-OpenAI issuers (Casdoor uses access_token directly)
- RefreshRequest includes client_secret for Casdoor compat
- id_token_or_access() fallback for Casdoor token refresh
- Login status detects provider from key prefix (sk-hanzo-, sk-ant-, sk-)
- Hanzo client credentials (app-hanzo) for OAuth flow
Branding:
- Welcome screen: "Hanzo Dev — AI development in the terminal"
- All user-facing "Codex" → "Hanzo Dev"
- Update URLs point to github.com/hanzoai/dev
- npm package: @hanzo/dev
- Windows toast: "Hanzo Dev"
- Disable OpenAI model upgrade prompts (GPT-5.2/5.3 migration banners)
Users choose their own model via /model or config.toml
- Make migration prompt UI monochromatic (no blue/green accents)
- Rebrand /chrome and /browser: remove Chrome-specific strings,
settings section now shows "Browser" instead of "Chrome"
- Add Enso browser recommendation to README
- Bump version to 0.6.67
- Replace EVERY CODE / CODE logo PNGs with Hanzo H logo and HANZO DEV banner
- Fix release.yml version determination to use package.json as authoritative
source instead of chasing highest npm version (was auto-bumping to 3.0.x)
- Rename "Dark - Codex" theme to "Dark - Hanzo" in TUI and theme picker
- Update warning messages from "Codex home" to "config home"
- Handle "hanzo" segment in model name formatting
- Remove "OpenAI Codex" from README highlights
Error cells now render in text_dim instead of the error() color so they
don't compete visually with user text for attention.
A bare 429 error (without retry-limit keywords) was incorrectly treated
as fatal, killing the session instead of letting the core retry loop
handle it. Now any 429 is classified as transient unless retries are
explicitly exhausted.
Zen mode suppresses the " • " bullet gutter on all assistant messages.
Update assertions to verify content rendering without requiring the gutter prefix.
The 429+retry-limit handler rewrote the error message to a
human-friendly string but then re-checked the rewritten text for
"retry limit" keywords, causing retries_exhausted to be false and the
app to get stuck showing the error without properly recovering. Now
track exhaustion state before the rewrite.
User messages now render in text_bright() so they visually stand out
from assistant text (text_dim) and status text (text_dim), giving
clear human / robot / status differentiation.
Replace last 3 hardcoded Borders::ALL with zen_borders() in Chrome
settings dialog, help text box, and auto-drive composer input.
Remove painted cursor overlay (contrast_ratio/relative_luminance) —
the terminal's native cursor is sufficient.
Three locations in chat_composer.rs always subtracted border+padding from
width and added +2 height for borders, even when zen mode has no borders.
This caused:
- Overestimated composer height (extra grey space below text)
- Cursor off-by-one from width mismatch between render and cursor_pos
Now all three (desired_height, cursor_pos, render_ref) conditionally skip
the offsets when show_borders() is false.
Content width calculation now uses show_borders() instead of hardcoded
4-char border+padding offset, and bottom pane horizontal padding uses
content_padding() so both respect zen mode's flush-left layout.
The bottom pane's compute_composer_rect() and render method both
had horizontal_padding hardcoded to 1, causing a 1-column left
inset on the input area even when zen mode was active. Use
theme::content_padding() (0 in zen) instead.
Replace 71 scattered is_zen_mode() calls with data-driven theme
accessors: show_borders(), show_gutter(), gutter_width(),
content_padding(), zen_borders(), zen_left_borders().
Layout decisions now flow through the Theme struct properties
(show_borders, show_gutter, content_padding) set by apply_zen(),
so each dimension is independently configurable.
Also persists zen preference to config.toml on Alt+G toggle.
Spinner animation characters were rendering in the exec overlay header
and auto coordinator approval view even when zen mode was active. Wrap
both render paths with is_zen_mode() checks so they show nothing in zen.
- Add show_borders, show_gutter, content_padding to Theme struct
- Theme.apply_zen() sets layout properties; toggle_zen_mode updates them
- Content area uses theme.content_padding (0 in zen, 1 otherwise)
- Gutter width uses theme.show_gutter
- Status indicator: no leading space in zen mode
- 429 rate limit: minimum 5s backoff, default max retries 4→8
- new_connecting_mcp_status() now accepts server names and shows them:
"Connecting MCP servers: hanzo-mcp, filesystem…"
- When MCP finishes, replace with "MCP connected: hanzo-mcp, filesystem"
instead of silently removing the cell
- new_connected_mcp_status() for the replacement cell
- Needle match updated to prefix-based for robustness
- Zen mode now defaults to true for clean minimal UI out of the box
- Users can toggle with Alt+G if they want gutter icons/borders back
- Assistant text uses text_dim color to contrast with brighter human input
- Replace rotating spinner (✧) with Codex-style shimmer animation on
status header text (sweeping brightness band via cosine interpolation)
- Simple • bullet with blink (accent/dim at 600ms), hidden in zen mode
- Fix wait_status cell leaking ◓ gutter symbol in zen mode
- Make exec overlay error/success/warning status text match icon color
(was always text_dim, now red for errors, green for success, etc.)
- 30fps animation redraws via ScheduleFrameIn for smooth shimmer
Toggle zen mode on/off without restarting. Invalidates height cache
since gutter width changes (2→0) affect layout calculations.
Shows in help panel as "Alt+G: Toggle zen mode (gutter/borders)".
Strip ▶ ⏺ ✖ ✔ ⚙ ◓ and other decorative glyphs from every
gutter_symbol() override, streaming preview, status title,
agent status indicators, chevrons, and auto-review footer.
In zen mode: just text, no bullets or icons on the side.
Hide borders and titles in zen mode for every remaining overlay,
settings panel, history cell, and popup across the TUI. Covers
18 files including agent editor, undo timeline, cloud tasks,
theme/skills/prompts/verbosity selectors, login accounts, exec
cells, assistant code blocks, image placeholders, upgrade banner,
and all chatwidget command windows/dialogs/tab rules.
- Status indicator now uses spinner animation from spinner system instead
of static bullet, with descriptive activity text ("Using read_file",
"Agents coordinating", etc.) and Codex-style format with • separators
- ? as first char in empty composer opens help popup (like / for commands)
- Zen mode hides borders on all settings panels, overlays, and modals
- Hide "Ctrl+A to close" and "Ctrl+B to close" hints from overlay titles
- Improve map_status_message to capitalize multi-word statuses and
simplify tool names ("using tool: X" → "Using X")