Deep directories (beyond the workspace snapshot's depth limit) relied on
a lazy-loaded override cache that was never invalidated, so agent-created
files, in-app deletes / renames / rollbacks, and external changes inside
expanded deep folders stayed invisible until the folder was reopened.
Propagate watcher `changed_paths` through the delta envelope and fire a
Meta event whenever FS activity doesn't alter the tree/git snapshots, so
the frontend can surgically invalidate affected cache entries and
re-fetch. Manual refresh (Reload from disk) clears the cache and
re-hydrates still-expanded deep dirs through the same path. Replayed
deltas after reconnect are forwarded to the same listeners.
Also split the combined workspace-state effect into tree / git / status
slices so unrelated state transitions (e.g. the 'resyncing' flip during
a refresh) no longer rebuild the entire node tree and cause a flash.
Reintroduce a local not-a-git-repo fallback state in the git log panel so non-repo errors still render the dedicated empty state when workspace state streaming is degraded.
Improve git repository preflight classification by distinguishing missing paths, permission issues, and non-directory targets before checking .git presence.
Add not_a_git_repository to the frontend AppErrorCode union for explicit typed handling.
Consolidate `.git` presence detection into a shared `git_repo` module used by both the workspace state watcher and the command preflight helper, replacing duplicated local definitions.
Introduce `AppErrorCode::NotAGitRepository` (HTTP 422) and preflight eleven frontend-callable git commands (log, status, list-branches, diff, diff-with-branch, show-diff, show-file, push-info, list-remotes, list-all-branches, commit-branches) so non-git folders short-circuit with a structured error instead of leaking locale-dependent git stderr.
Frontend `isNotAGitRepoError` checks the error code first and falls back to a multi-language regex list centralized in `src/i18n/git-error-patterns.ts`, covering the nine languages git actually translates into.
Wire the git log panel to `workspaceState.isGitRepo` rather than a local cached flag, so running `git init` or deleting `.git` externally propagates through the watcher and refreshes the panel automatically.
Gate git refresh on .git presence so file churn in non-git workspaces no longer produces endless resync_hint events, and silently log tree/git refresh errors during watch flushing instead of flagging requires_resync, which turned transient failures into self-reinforcing loops.
Degrade gracefully when the filesystem watcher fails to attach (e.g. permission denied, inotify quota): keep the initial snapshot, surface a degraded flag, and expose a store-level restart that the banner uses to retry attachment after the root cause is fixed.
Propagate is_git_repo through the snapshot so the git log and changes tabs render a dedicated "Not a Git repository" empty state instead of raw git stderr with a useless retry button.
Stop polling get_git_branch from the title bar once it returns null and re-arm on visibility change.
Add translations for the new banner, empty-state, and retry keys across all ten locales.
- Persist user-supplied access token and last-used port in app_metadata, falling back to defaults when unset
- Atomically guard concurrent starts via compare_exchange with RAII rollback of the running flag
- Wrap token and port persistence in a single SeaORM transaction to prevent partial writes
- Classify bind errors (port in use, permission denied, address unavailable, invalid address) into stable i18n keys
- Localize start-failure messages across all 10 supported languages
Forward the previously-dropped `locations` and `meta` fields from ACP
SDK ToolCall/ToolCallUpdate events through to the frontend. The meta
field carries `claudeCode.parentToolUseId` which enables precise
parent-child matching for concurrent Agent tool calls during streaming.
- Forward locations/meta in Rust AcpEvent types and connection handlers
- Use parentToolUseId for exact agent→child mapping, with position-based
fallback for agents that don't provide it (Codex, OpenCode)
- Replace `any` types with proper ToolCallMeta / unknown types
- Add runtime guards for meta field parsing (defensive against
unexpected shapes from different agents)
- Cache inferLiveToolName results per tool_call_id to avoid redundant
computation across Phase 1 and Phase 2
- Lazy-construct agentStats only when children exist
Support Agent card rendering during live streaming responses for Claude
Code, Codex CLI, and OpenCode. Previously Agent cards only rendered for
loaded/historical messages parsed from the DB.
- Fix tool name inference: subagent_type in input now returns "agent"
instead of "task"; add spawn_agent/wait_agent/close_agent aliases
- Group sub-agent tool calls inside parent Agent cards during streaming
using position-based heuristic (ACP SDK lacks parent_id tracking)
- Clean raw Agent output (JSON content blocks, XML task_result wrappers)
- Emit agent_stats with nested tool calls so AgentToolCallPart renders
child execution inline, matching the loaded message appearance
Render Agent/Explore/Plan tool calls in a visually distinct collapsible
container with colored left border, replacing the generic tool card. Parse
subagent JSONL transcripts from {sessionId}/subagents/ to extract and
display the actual tool calls (Bash, Read, Grep, etc.) the subagent
executed, reusing the existing ToolCallPart for consistent appearance.
- Add AgentToolCallPart component with collapsible body, prompt section,
execution stats, and nested tool call list via render prop injection
- Add AgentExecutionStats and AgentToolCall types (Rust + TypeScript)
- Parse toolUseResult.agentId to locate and read subagent JSONL files
- Validate agentId against path traversal before filesystem access
- Pass agentStats through adapter for both ID-matched and positional
tool result pairing
- Strip agentStats in nested render to prevent recursive Agent expansion
- Add i18n keys for agent UI labels across all 10 languages
Add OAuth device code flow for Codex CLI official subscription auth,
allowing users to log in with their ChatGPT account directly from the
agent settings page without using the terminal.
- Backend: two new endpoints (codex_request_device_code, codex_poll_device_code)
that handle the OpenAI OAuth device code flow and return tokens to frontend
- Frontend: login UI with verification URL, copyable user code, polling status,
15-minute timeout, and auto-save via existing persistEnv/persistConfig path
- Auth.json written in Codex CLI compatible format (nested tokens, account_id,
last_refresh) so codex-acp can use OAuth tokens directly
- Show logged-in status and re-login option when tokens are present
- Remove auth.json textarea from Codex settings UI
- i18n: all 10 languages updated with new login-related keys
Live tool calls that produce output while running (e.g. streaming bash
stdout from Codex) now render their partial output in real time instead
of appearing blank until the tool completes. The tool card keeps its
running visual state — spinner and 24KB tail truncation — while chunks
arrive, and transitions to the completed state once the final status
lands. A WeakMap keyed on the ACP reducer's chunks-array identity
memoizes the joined output so repeated renders don't re-run O(n) string
concatenation.
Add a "Reset to Here" context action for git log commit items in the folder page.
Show a reset dialog with branch, target commit, commit message, and reset mode details for soft, mixed, hard, and keep.
Disable reset when viewing a non-current branch filter and keep the action ordering under commit diff.
Add git_reset support across Rust commands, Tauri invoke registration, web handlers/routes, and frontend API/type bindings.
Add localized reset labels, mode descriptions, and toast messages across all supported languages.
Introduce a shared workspace-state backend stream with snapshot/delta APIs for file tree and git changes.
Migrate both aux panels to a common frontend workspace store with lifecycle-safe stream handling.
Apply batched watch throttling, path-aware git refresh gating, no-op delta suppression, and bounded history compaction to improve runtime stability.
Enable raw Claude SDK forwarding for ACP sessions and emit only system/api_retry events to the frontend.
Show a localized single-line retry banner with loading under the conversation input, including error details and retry progress.
Detect dark/light mode before React hydrates to eliminate the visible
white-to-dark flash when opening windows in dark mode.
Frontend:
- Inline script now reads next-themes localStorage key and applies
.dark class, colorScheme, and backgroundColor on <html> before first
paint
- Add CSS-only fallback via prefers-color-scheme media query in an
inline <style> tag that fires before any JS executes
macOS backend:
- Detect system dark mode via `defaults read -g AppleInterfaceStyle`
(cached with OnceLock) and set native window background color to
match dark theme in apply_platform_window_style
- Persist user appearance mode preference (dark/light/system) to DB
alongside zoom level so new windows use the correct background
- Add update_appearance_mode Tauri command; frontend syncs on mount,
on settings change, and on cross-window storage events
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Leverage the Tauri updater plugin's DownloadEvent callback to display
a progress bar with downloaded/total bytes during app updates, replacing
the previous spinner-only feedback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the spinner-only UX with live log output during agent SDK
operations, matching the existing OpenCode plugin install experience.
Backend: emit structured events (started/log/completed/failed) via
EventEmitter during npm install and binary download. npm commands now
run with piped stdio for line-by-line streaming; binary downloads
report chunked progress every 1 MB.
Frontend: subscribe to `app://agent-install` events through a new
`useAgentInstallStream` hook and render a theme-aware log terminal
below the preflight checks panel.
Also fixes the install log container in both agent settings and the
OpenCode plugins modal: auto-scroll no longer shifts the outer page,
and colours now follow the active light/dark theme.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Tauri's native `traffic_light_position()` builder method to position
macOS window controls instead of runtime objc2 calls. A global AtomicU32
tracks the current zoom level so newly created windows reflect the latest
zoom. The frontend syncs zoom changes to the backend via a new
`update_traffic_light_position` command.
- Add `traffic_light_position()` to `apply_platform_window_style` builder
- Add `CURRENT_ZOOM` atomic and `traffic_light_position()` helper
- Register `update_traffic_light_position` Tauri command
- Add `syncTrafficLightPosition` in appearance-provider to sync on zoom
change, mount, and cross-tab storage events
- Consolidate `ensure_windows_undecorated` calls into `post_window_setup`
- Remove dead `on_window_resized` no-op and its Resized event listener
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Session-page connect never triggers download/install; returns
SdkNotInstalled immediately and prompts the user to install from
Agent Settings instead
- Binary agents now accept any cached version via
find_best_cached_binary_for_agent so stale caches still connect
- Bound Initialize handshake with a 60s timeout and convert it to
AcpError::InitializeTimeout via a sentinel in run_connection
- Spawn background task owns ConnectionManager map insertion and
removes the entry on exit through an RAII guard that survives
panics, preventing leaked stale entries
- AcpError gains SdkNotInstalled and InitializeTimeout variants plus
a stable code() identifier; AcpEvent::Error carries code so the
frontend can render localized messages by key
- Frontend preflight now runs for all connect sources; error event
handler switches on code to show translated text for
initialize_timeout, sdk_not_installed, platform_not_supported,
process_exited, spawn_failed and download_failed
- Remove ConnectionStatus::Downloading enum variant, all frontend
branches, and i18n strings; drop obsolete autoLinkFailedTitle,
autoLinkPreflightFailed, preflightCheckFailedDefault and
preflightFailedTitle keys across 10 locales
- Add backendErrors.* translations in 10 languages
- Diagnostic logging: always log agent stderr plus binary
path/size/args/env keys and Initialize timing; gate stdin/stdout
JSON-RPC tracing behind CODEG_ACP_DEBUG to avoid persisting user
content into OS log files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expert skills in the chat session were derived by intersecting built-in
experts with ACP availableCommands, which caused Codex experts to never
appear since Codex does not advertise skills through ACP.
- Add `experts_list_for_agent` backend API that checks symlink status
across all global skill dirs for the given agent type
- Replace availableCommands-based expert filtering with symlink-based
query, making the settings page the single source of truth
- Use `$` prefix for Codex expert skills while keeping `/` for slash
commands and other agents' experts
- Disable the expert button when no experts are linked for the agent
- Invalidate per-agent expert cache after link/unlink in settings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Upgrade @lobehub/icons from v4 to v5.3.0, adopt new icon components (ClaudeCode, Codex, GeminiCLI) with Color variants, and align fallback AGENT_COLORS with official brand colors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restrict supported agent types to Claude Code, Codex CLI, and Gemini CLI. Reposition agent type badges to be vertically centered beside the name and API URL block in provider cards.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Split env vars and config file persistence into separate save operations
- Add model_provider_id field to agent_setting for tracking selected provider
- Add "Model Provider" auth mode for Claude Code, Codex CLI, and Gemini CLI
- Add "Custom Endpoint" auth mode for Claude Code (previously only official subscription)
- Unify auth mode labels across all three agents (official subscription / custom endpoint / model provider)
- When model provider is selected, fill api_url and api_key into env and config automatically
- Resolve model provider credentials at ACP connect time as a backend fallback
- Clear provider deletion cascades to agent_setting.model_provider_id
- Claude Code writes API credentials to config.env (ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN)
- Codex: switching auth modes patches config.toml instead of clearing it
- Add i18n keys for new auth modes in all 10 supported languages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a new settings page for managing API model providers (name, API URL,
API key, applicable agent types). Includes database migration, SeaORM
entity, backend CRUD commands/handlers, frontend settings UI with agent
type filter, add/edit/delete dialogs, and i18n support for all 10 locales.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Support typing "@" in the chat input to trigger a file list popup with
filtering, keyboard navigation, and file/directory attachment. Directories
from the sidebar file tree now attach as resource links instead of injecting
text into the input.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add split button with dropdown in commit dialog supporting both
"Commit" and "Commit and Push" actions
- Pass folderId through gitCommit/gitPush API calls so backend emits
events that the folder window can receive for toast notifications
- Update Tauri git_commit and git_push commands to accept folder_id
parameter with window label fallback for cross-window compatibility
- Wrap commit page with GitCredentialProvider for push authentication
- Keep commit window open when push fails so user can see the error
- Add i18n translations for all 10 locales
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move PTY spawn from context layer to view layer so event subscription
happens before spawn, preventing loss of initial terminal output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Generate QR code server-side when iLink API returns SPA page URL
(added qrcode + image crates for PNG generation)
- Strip bot_token from frontend response (new WeixinQrcodeStatusPublic type)
- Add request timeouts and shared HTTP client for QR code endpoints
- Fix TOCTOU race on reply_context double-lock (single lock scope)
- Extract do_send() helper to deduplicate sendmessage logic; resend now
checks ret field for context expiry instead of HTTP status only
- Cap pending_messages buffer at 50 to prevent unbounded memory growth
- Generate stable X-WECHAT-UIN per backend instance instead of per request
- Extract ILINK_CHANNEL_VERSION constant (was hardcoded in 4 places)
- Add 5-minute client-side QR expiry timeout in frontend dialog
- Track consecutive polling errors and show warning after 3 failures
- Stabilise onAuthSuccess/onClose callback refs to prevent polling restarts
- Replace dead i18n key weixinOpenQrcode with weixinPollError
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>