Commit Graph

25 Commits

  • fix(session): include Codex archived sessions (#2861)
    Scan Codex archived_sessions alongside active sessions so archived conversations appear in the session manager.
    
    Allow deletion from any validated Codex session root while keeping path safety checks in place.
  • feat: support launch warp and execute session (#2466)
    * feat: support launch warp and execute session
    
    Signed-off-by: tison <wander4096@gmail.com>
    
    * other wires
    
    Signed-off-by: tison <wander4096@gmail.com>
    
    * for launch with provider
    
    Signed-off-by: tison <wander4096@gmail.com>
    
    * fixup indirection
    
    Signed-off-by: tison <wander4096@gmail.com>
    
    * clippy
    
    Signed-off-by: tison <wander4096@gmail.com>
    
    * address comments
    
    Signed-off-by: tison <wander4096@gmail.com>
    
    ---------
    
    Signed-off-by: tison <wander4096@gmail.com>
  • Fix/一键配置失效 (#2249)
    * style(FailoverQueueManager): 显示供应商备注信息
    
    * style(FailoverQueueItem): 添加供应商备注字段以支持备注信息显示
    
    * style(FailoverQueueManager): 显示供应商备注信息
    
    * style(FailoverQueueItem): 添加供应商备注字段以支持备注信息显示
    
    * style(FailoverQueueManager): 更新供应商备注信息的显示样式
    
    * style(FailoverQueueItem): 添加条件序列化以优化供应商备注字段
    
    * fix: 优化模型状态管理,确保配置更新时正确引用最新设置
    
    * fix(skill): improve error handling for skill source directory resolution
    
    Co-authored-by: Copilot <copilot@github.com>
    
    * fix(gemini): simplify project directory retrieval in scan_sessions function
    
    * fix(useModelState): optimize latestConfigRef assignment in useModelState hook
    
    * fix(useModelState): remove unnecessary blank line in useModelState hook
    
    ---------
    
    Co-authored-by: Copilot <copilot@github.com>
  • feat: implement Hermes session manager with SQLite + JSONL support (Phase 6)
    - Add hermes.rs session provider with dual-source scanning:
      SQLite (state.db) as primary, JSONL transcripts as fallback
    - Dynamic schema discovery via PRAGMA table_info for SQLite resilience
    - Use read_head_tail_lines for efficient JSONL metadata extraction
      (head 30 lines for metadata, tail 10 for last_active_at)
    - Support both flat and nested JSONL message formats
    - Add SQLite session loading and transactional deletion
    - Register hermes in parallel session scan (thread::scope)
    - Add "hermes" to frontend ProviderFilter type
    - 7 unit tests covering JSONL parsing, SQLite source parsing, deletion
  • feat: add Hermes Agent as 6th supported app type (Phase 1)
    Register AppType::Hermes across the entire Rust backend:
    - Add Hermes variant to AppType enum with additive mode and MCP support
    - Add hermes field to McpApps, SkillApps, CommonConfigSnippets, and all
      per-app structs (McpRoot, PromptRoot, VisibleApps, AppSettings)
    - Create minimal hermes_config.rs with get_hermes_dir() respecting
      settings override, matching the pattern of other app config modules
    - Update all match arms in commands, services, deeplink, proxy, mcp,
      session_manager, and test files
    - Extract shared build_additive_app_settings() to eliminate duplication
      between OpenClaw and Hermes deep link handling
    - Combine identical OpenClaw/Hermes proxy match arms into unified arms
  • Fix Ghostty session restore launch path (#1976)
    * fix: launch Ghostty via shell command
    
    Use Ghostty's shell execution path instead of injecting raw terminal input so Claude resume commands run reliably when opening a session terminal.
    
    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
    
    * fix(ghostty): pass cwd via --working-directory instead of shell string
    
    Use Ghostty's native --working-directory flag to set the working
    directory, matching the pattern used by Alacritty. This avoids shell
    expansion of special characters (e.g. $VAR, spaces) in project paths.
    
    The command is now passed directly to -c without a cd prefix.
    
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
  • fix(proxy): reduce unnecessary Copilot premium interaction consumption
    - Fix request classification: treat messages containing tool_result as
      agent continuation instead of user-initiated, preventing false premium
      charges on every tool call
    - Add subagent detection via __SUBAGENT_MARKER__ and metadata._agent_
      fallback, setting x-interaction-type=conversation-subagent
    - Add deterministic x-interaction-id derived from session ID to group
      requests into a single billing interaction
    - Add orphan tool_result sanitization to prevent upstream API errors
      that could cause retries and duplicate billing
    - Reorder pipeline: classify (on original body) → sanitize → merge →
      warmup, ensuring classification sees raw tool_result semantics
    - Enable warmup downgrade by default with gpt-5-mini model
    - Enhance session ID extraction priority chain for Copilot cache keys
    - Detect infinite whitespace bug in streaming tool call arguments
  • fix(sessions): strip OpenClaw message_id suffix and allow 2-line titles
    OpenClaw gateway injects `[message_id: UUID]` metadata at the end of
    every message, wasting display space. Strip this suffix from both title
    and summary fields.
    
    Also change session title display from single-line truncate to
    line-clamp-2, so longer titles (e.g. OpenClaw's timestamp-prefixed
    messages) can show more meaningful content across two lines.
  • feat(sessions): extract meaningful titles for Codex and OpenClaw sessions
    Previously Codex and OpenClaw sessions only showed the working directory
    basename as the title, making it hard to distinguish sessions in the same
    project. Now both providers extract the first real user message as the
    session title, matching the existing Claude Code behavior.
    
    - Codex: first user message → dir basename (skips AGENTS.md injection)
    - OpenClaw: displayName (sessions.json) → first user message → dir basename
    - Move TITLE_MAX_CHARS constant to shared utils.rs
    - Use Option<&HashMap> for OpenClaw parse_session to avoid leaky abstraction
  • Let Kaku users launch sessions from their chosen terminal (#1954) (#1983)
    Kaku is a WezTerm-derived macOS terminal, so reusing the existing WezTerm-compatible launch path keeps the change small while making it selectable in settings and session resume flows.
    
    Constraint: Kaku support should stay macOS-only and avoid introducing a separate launcher model
    Rejected: Treat Kaku as a silent WezTerm fallback | users could not explicitly choose it in settings
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: Keep Kaku on the shared WezTerm-compatible launch path unless upstream drops the start-compatible CLI
    Tested: pnpm typecheck; pnpm format:check; cargo check --manifest-path src-tauri/Cargo.toml; cargo fmt --manifest-path src-tauri/Cargo.toml --check; cargo test --manifest-path src-tauri/Cargo.toml --lib session_manager::terminal::tests
    Not-tested: End-to-end launch against a locally installed Kaku.app
    Related: #1954
  • feat(session-manager): extract meaningful titles for Claude sessions
    Instead of showing directory basenames for all Claude sessions, extract
    titles from JSONL content with a priority chain:
    1. custom-title metadata (set via /rename in Claude Code)
    2. First real user message (skipping /clear, /compact caveats)
    3. Directory basename (fallback)
  • feat: add bulk delete for session manager (#1693)
    * feat: add bulk delete for session manager
    
    * fix: address batch delete review issues
    
    * fix: keep session list in sync after batch delete
  • refactor(proxy): transparent header forwarding via hyper client (#1714)
    * style(frontend): reformat provider forms, constants and hooks
    
    Apply prettier formatting across 5 frontend files. No logic changes.
    
    Changed files:
    - AddProviderDialog.tsx: reformat generic type annotation and callback
    - ClaudeFormFields.tsx: consolidate multi-line useState and Collapsible props
    - CodexConfigSections.tsx: expand single-line React imports to multi-line,
      collapse removeCodexTopLevelField() call
    - constants.ts: merge TemplateType into single line
    - useSkills.ts: expand single-line TanStack Query imports to multi-line,
      reformat uninstallSkill mutationFn chain
    
    * deps(proxy): add hyper ecosystem crates and manual decompression libs
    
    reqwest internally normalizes all header names to lowercase and does not
    preserve insertion order, causing proxied requests to differ from the
    original client requests. To achieve transparent header forwarding with
    original casing and order, introduce lower-level hyper HTTP client libs.
    
    New dependencies:
    - hyper-util 0.1: TokioExecutor + legacy Client with
      preserve_header_case support for HTTP/1.1
    - hyper-rustls 0.27: rustls-based TLS connector for hyper
    - http 1 / http-body 1 / http-body-util 0.1: HTTP type crates for
      hyper 1.x request/response construction
    - flate2 1: manual gzip/deflate decompression (replaces reqwest auto)
    - brotli 7: manual brotli decompression
    
    Changed dependencies:
    - serde_json: enable preserve_order feature to keep JSON field order
    - reqwest: drop gzip feature to prevent reqwest from overriding the
      client's original accept-encoding header
    
    * refactor(proxy): use hyper client for header-case preserving forwarding
    
    Previously the proxy used reqwest for all upstream requests. reqwest
    normalizes header names to lowercase and reorders them internally,
    making proxied requests distinguishable from direct CLI requests.
    Some upstream providers are sensitive to these differences.
    
    This commit replaces reqwest with a hyper-based HTTP client on the
    default (non-proxy) path, achieving wire-level header fidelity:
    
    Server layer (server.rs):
    - Replace axum::serve with a manual hyper HTTP/1.1 accept loop
    - Enable preserve_header_case(true) so incoming header casing is
      captured in a HeaderCaseMap extension on each request
    - Bridge hyper requests to axum Router via tower::Service
    
    New hyper client module (hyper_client.rs):
    - Lazy-initialized hyper-util Client with preserve_header_case
    - ProxyResponse enum wrapping both hyper::Response and reqwest::Response
      behind a unified interface (status, headers, bytes, bytes_stream)
    - send_request() builds requests with ordered HeaderMap + case map
    
    Request handlers (handlers.rs):
    - Switch from (HeaderMap, Json<Value>) extractors to raw
      axum::extract::Request to preserve Extensions (containing the
      HeaderCaseMap from the accept loop)
    - Pass extensions through the forwarding chain
    
    Forwarder (forwarder.rs):
    - Remove HEADER_BLACKLIST array; replace with ordered header iteration
      that preserves original header sequence and casing
    - Build ordered_headers by iterating client headers, skipping only
      auth/host/content-length, and inserting auth headers at the original
      authorization position to maintain order
    - Handle anthropic-beta (ensure claude-code-20250219 tag) and
      anthropic-version (passthrough or default) inline during iteration
    - Remove should_force_identity_encoding() — accept-encoding is now
      transparently forwarded to upstream
    - Use hyper client by default; fall back to reqwest only when an
      HTTP/SOCKS5 proxy tunnel is configured
    
    Provider adapters (adapter.rs, claude.rs, codex.rs, gemini.rs):
    - Replace add_auth_headers(RequestBuilder) -> RequestBuilder with
      get_auth_headers(AuthInfo) -> Vec<(HeaderName, HeaderValue)>
    - Adapters now return header pairs instead of mutating a reqwest builder
    - Claude adapter: merge Anthropic/ClaudeAuth/Bearer into single branch;
      move Copilot fingerprint headers into get_auth_headers
    
    Response processing (response_processor.rs):
    - Add manual decompression (gzip/deflate/brotli via flate2 + brotli)
      for non-streaming responses, since reqwest auto-decompression is now
      disabled to allow accept-encoding passthrough
    - Add compressed-SSE warning log for streaming responses
    - Accept ProxyResponse instead of reqwest::Response
    
    HTTP client (http_client.rs):
    - Disable reqwest auto-decompression (.no_gzip/.no_brotli/.no_deflate)
      on both global and per-provider clients
    
    Streaming adapters (streaming.rs, streaming_responses.rs):
    - Generalize stream error type from reqwest::Error to generic E: Error
    
    Misc:
    - log_codes.rs: add SRV-005 (ACCEPT_ERR) and SRV-006 (CONN_ERR)
    - stream_check.rs: reformat copilot header lines
    - transform.rs: fix trailing whitespace alignment
    
    * fix(lint): resolve 35 clippy warnings across Rust codebase
    
    Fix all clippy warnings reported by `cargo clippy --lib`:
    
    - codex_config.rs: fix doc_overindented_list_items (3 spaces -> 2)
    - commands/copilot.rs: inline format args in 2 log::error! calls
    - commands/provider.rs: inline format args in 3 map_err closures
    - proxy/hyper_client.rs: inline format arg in log::debug! call
    - proxy/providers/copilot_auth.rs: inline format args in 16 locations
      (log macros, format! in headers, error constructors)
    - proxy/thinking_optimizer.rs: inline format args in 2 log::info! calls
    - services/skill.rs: inline format args in log::debug! call
    - services/webdav_sync.rs: inline format args in 6 format! calls
      (version compat messages, download limit messages)
    - services/webdav_sync/archive.rs: inline format args in 2 format! calls
    - session_manager/providers/opencode.rs: inline format args in
      source_path format!
    
    All fixes use the clippy::uninlined_format_args suggestion pattern:
      format!("msg: {}", var)  ->  format!("msg: {var}")
    
    * deps(proxy): add raw HTTP write and native TLS cert dependencies
    
    Add crates required for the raw TCP/TLS write path that bypasses
    hyper's header encoder to preserve original header name casing:
    
    - httparse: parse raw TCP peek bytes to capture header casings
    - tokio-rustls + rustls: direct TLS connections for raw write path
    - webpki-roots: Mozilla CA bundle baseline
    - rustls-native-certs: load system keychain CAs (trusts proxy MITM
      certificates from Clash, mitmproxy, etc.)
    
    * fix(proxy): address code review feedback on response handling
    
    Fixes from PR #1714 code review:
    
    - Extract `read_decoded_body()` and `strip_entity_headers_for_rebuilt_body()`
      in response_processor to properly clean content-encoding/content-length
      headers after decompression
    - Reuse `read_decoded_body()` in handlers.rs for Claude transform path,
      ensuring compressed responses are decoded before format conversion
    - Make `build_proxy_url_from_config()` public so forwarder can pass proxy
      URL to the hyper raw write path
    - Add `has_system_proxy_env()` utility with test coverage
    - Add 50ms backoff after accept() failures in server.rs to prevent
      tight-loop CPU spin on transient socket errors
    
    * feat(proxy): implement raw TCP/TLS write with HTTP CONNECT tunnel
    
    Rewrite hyper_client with a two-tier strategy for header case preservation:
    
    Primary path (raw write):
    - Peek raw TCP bytes in server.rs to capture OriginalHeaderCases before
      hyper lowercases them
    - Build raw HTTP/1.1 request bytes with exact original header name casing
    - Write directly to TLS stream, then use WriteFilter to let hyper parse
      the response while discarding its duplicate request writes
    - Support HTTP CONNECT tunneling through upstream proxies, so header case
      is preserved even when a proxy (Clash, V2Ray) is configured
    
    Fallback path (hyper-util Client):
    - Used when OriginalHeaderCases is empty or raw write fails
    - Configured with title_case_headers(true) for best-effort casing
    
    TLS improvements:
    - Load native system certificates alongside webpki roots so proxy MITM
      CAs (installed in system keychain) are trusted through CONNECT tunnels
    
    Key types added:
    - OriginalHeaderCases: maps lowercase name → original wire-casing bytes
    - WriteFilter<S>: AsyncRead+AsyncWrite wrapper that discards writes
    - connect_via_proxy(): HTTP CONNECT tunnel establishment
    - ExtensionDebugMarker: diagnostic marker for extension chain debugging
    
    * refactor(proxy): route requests through hyper with proxy-aware forwarding
    
    Rework forwarder request dispatch to always prefer the hyper raw write
    path (header case preservation) over reqwest:
    
    Request routing:
    - HTTP/HTTPS proxy: hyper raw write through CONNECT tunnel (case preserved)
    - SOCKS5 proxy: reqwest fallback (CONNECT not supported for SOCKS5)
    - No proxy: hyper raw write direct connection
    
    Header handling improvements:
    - Replace host header in-place at original position instead of
      skip-and-append, preserving client's header ordering
    - Preserve client's original accept-encoding for transparent passthrough;
      only force identity encoding when transform path needs decompression
    - Add should_force_identity_encoding() to centralize the decision
    - Remove hardcoded 'br, gzip, deflate' override that masked client values
    
    Proxy URL resolution (priority order):
    1. Provider-specific proxy config (if enabled)
    2. Global proxy URL configured in CC Switch
    3. Direct connection (no proxy)
    
    * chore(proxy): remove dead code, redundant tests and debug scaffolding
    
    - Inline should_force_identity_encoding() (was just `needs_transform`)
      and delete its 5 test cases
    - Remove ExtensionDebugMarker diagnostic type
    - Remove unused has_system_proxy_env() and its test
    - Remove strip_entity_headers test
    - Simplify hyper path: remove redundant is_socks_proxy ternary
    - Update hyper_client module doc to reflect CONNECT tunnel support
    
    * fix(proxy): block direct-connect fallback and complete CONNECT tunnel support
    
    * feat(hooks): improve proxy requirement warnings with specific reasons
    
    - Remove redundant OpenAI format hint toast messages
    - Add detailed reason detection for proxy requirements (OpenAI Chat, OpenAI Responses, full URL mode)
    - Update i18n files with new reason-specific keys
    
    * style(*): format code with prettier
    
    - Remove extra whitespace in http_client.rs
    - Fix formatting issues in useProviderActions.ts
    
    * fix(proxy): post-merge fixes for forward return type and clippy warnings
    
    - Restore forward() return type to (ProxyResponse, Option<String>)
      to pass claude_api_format through to callers
    - Inline format args in log::warn! macro (clippy::uninlined_format_args)
    - Suppress too_many_arguments for check_claude_stream
    
    * refactor(proxy): preserve original header wire order and add non-streaming body timeout
    
    - Rewrite build_raw_request to emit headers in original
      client-sent sequence instead of hash-map order
    - Remove unused OriginalHeaderCases::get_all method
    - Add body_timeout to read_decoded_body to prevent
      requests hanging when upstream stalls after headers
  • fix: parse tool_use/tool_result messages and add OpenCode SQLite backend (#1401)
    * fix: parse tool_use/tool_result messages and add OpenCode SQLite backend
    
      - Claude: reclassify user messages containing tool_result as "tool" role
      - Codex: handle function_call and function_call_output payload types
      - Gemini: support array content and toolCalls extraction, filter info/error types
      - OpenCode: add SQLite session scan, load and delete alongside legacy JSON
      - utils: extend parse_timestamp_to_ms for integer timestamps, extract tool_use/tool_result in shared extract_text
    
    * fix: address remaining issues from tool_use/tool_result parsing commit
      - Claude: fix role misclassification for mixed user+tool_result messages (any → all)
      - OpenCode: extract duplicate part text logic into extract_part_text()
      - OpenCode: add path validation for SQLite delete to prevent foreign DB access
      - OpenCode: wrap SQLite deletion in transaction for atomicity
      - openclaw_config: remove redundant as_deref() on Option<&str>
  • feat: add session deletion with per-provider cleanup and path safety
    Add delete_session Tauri command dispatching to provider-specific deletion
    logic for all 5 providers (Claude, Codex, Gemini, OpenCode, OpenClaw).
    Includes path traversal protection via canonicalize + starts_with validation,
    session ID verification against file contents, frontend confirmation dialog
    with optimistic cache updates, i18n keys (zh/en/ja), and component tests.
  • chore: pre-release cleanup — remove debug logs, fix clippy warning, add missing ja translations, and format code
    - Remove 2 console.log statements from DeepLinkImportDialog
    - Fix clippy unnecessary_map_or: use is_some_and in live.rs
    - Add 17 missing Japanese i18n keys (skills, proxy, circuitBreaker, universalProvider)
    - Run prettier and cargo fmt to fix pre-existing formatting drift
  • perf: optimize session panel loading with parallel scan and head-tail JSONL reading
    - Parallelize 5 provider scans using std::thread::scope
    - Add read_head_tail_lines() to read only first 10 + last 30 lines from JSONL files, skipping potentially large middle sections
    - Cache Codex UUID regex with LazyLock to avoid repeated compilation
    - Skip expensive I/O in OpenCode when session title is already available
  • feat(sessions): add session browsing for OpenCode and OpenClaw
    Add two new session providers following the existing convention-based
    pattern. OpenCode reads three-layer JSON storage, OpenClaw parses JSONL
    event streams. Wire up backend dispatch, frontend filter dropdown, icon
    mappings, toolbar button for OpenClaw, and i18n subtitle updates.
  • feat: session manger (#867)
    * feat: init session manger
    
    * feat: persist selected app to localStorage
    
    - Save app selection when switching providers
    - Restore last selected app on page load
    
    * feat: persist current view to localStorage
    
    - Save view selection when switching tabs
    - Restore last selected view on page load
    
    * styles: update ui
    
    * feat: Improve macOS Terminal activation and refactor Kitty launch to use  command with user's default shell.
    
    * fix: session view
    
    * feat: toc
    
    * feat: Implement FlexSearch for improved session search functionality.
    
    * feat: Redesign session manager search and filter UI for a more compact and dynamic experience.
    
    * refactor: modularize session manager by extracting components and utility functions into dedicated files.
    
    * feat: Enhance session terminal launching with support for iTerm2, Ghostty, WezTerm, and Alacritty, including UI and custom configuration options.
    
    * feat: Conditionally render terminal selection and resume session buttons only on macOS.