diff --git a/AGENTS-CN.md b/AGENTS-CN.md index 730a5c5d7..347d1c375 100644 --- a/AGENTS-CN.md +++ b/AGENTS-CN.md @@ -9,7 +9,7 @@ BitFun 是一个由 Rust workspace 与共享 React 前端组成的项目。 ## 快速开始 1. 在修改架构敏感代码前,先阅读 `README.md` 和 `CONTRIBUTING.md`。 -2. 本地桌面快速验证优先使用 `pnpm run desktop:preview:debug`,而不是 `pnpm run desktop:dev`。 +2. 桌面端开发优先使用 `pnpm run desktop:dev` — 提供完整热更新(Vite HMR + Rust 自动重编译并重启)。仅在需要更快冷启动且只迭代前端时使用 `pnpm run desktop:preview:debug`(Rust 改动不会自动重编译)。 3. 改完后按下方表格执行与改动范围匹配的最小验证。 ## 模块索引 @@ -35,9 +35,10 @@ BitFun 是一个由 Rust workspace 与共享 React 前端组成的项目。 pnpm install # 开发 -pnpm run desktop:preview:debug # 桌面快速迭代 -pnpm run dev:web # 纯浏览器前端 -pnpm run cli:dev # CLI 运行时 +pnpm run desktop:dev # 完整热更新:Vite HMR + Rust 自动重编译并重启 +pnpm run desktop:preview:debug # 复用预构建二进制 + Vite HMR;无 Rust 自动重编译 +pnpm run dev:web # 纯浏览器前端 +pnpm run cli:dev # CLI 运行时 # 检查 pnpm run lint:web diff --git a/AGENTS.md b/AGENTS.md index 5659a427e..180652ac1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,7 +9,7 @@ Repository rule: **keep product logic platform-agnostic, then expose it through ## Quick start 1. Read `README.md` and `CONTRIBUTING.md` before architecture-sensitive changes. -2. For fast local desktop checks, prefer `pnpm run desktop:preview:debug` over `pnpm run desktop:dev`. +2. For desktop development, prefer `pnpm run desktop:dev` — it provides full hot-reload (Vite HMR + Rust auto-rebuild & restart). Use `pnpm run desktop:preview:debug` only when you need a faster cold-start for frontend-only iteration (Rust changes are not auto-rebuilt). 3. After changes, run the smallest matching verification from the table below. ## Module index @@ -35,9 +35,10 @@ Repository rule: **keep product logic platform-agnostic, then expose it through pnpm install # Dev -pnpm run desktop:preview:debug # fast desktop iteration -pnpm run dev:web # browser-only frontend -pnpm run cli:dev # CLI runtime +pnpm run desktop:dev # full hot-reload: Vite HMR + Rust auto-rebuild & restart +pnpm run desktop:preview:debug # reuse pre-built binary + Vite HMR; no Rust auto-rebuild +pnpm run dev:web # browser-only frontend +pnpm run cli:dev # CLI runtime # Check pnpm run lint:web diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c9e3d58f..1b37e17ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,15 +35,21 @@ pnpm install ### Common commands ```bash -# Desktop -pnpm run desktop:dev -pnpm run desktop:preview:debug +# Desktop (recommended for daily development) +pnpm run desktop:dev # full hot-reload: Vite HMR + Rust auto-rebuild & restart + +# Desktop (lightweight preview, no Rust auto-rebuild) +pnpm run desktop:preview:debug # reuse pre-built binary + Vite HMR; Rust changes require manual restart + +# Desktop (production build) pnpm run desktop:build # E2E pnpm run e2e:test ``` +> **`desktop:dev` vs `desktop:preview:debug`**: `desktop:dev` runs `tauri dev`, which provides **full hot-reload** — frontend changes apply instantly via Vite HMR, and Rust/backend changes trigger an incremental rebuild followed by an automatic app restart. This is the recommended workflow for active development. `desktop:preview:debug` launches a pre-built debug binary alongside a Vite dev server; frontend edits still get HMR, but **Rust-side changes are not auto-rebuilt** — you must stop and re-run the command (or use `--force-rebuild`). Use `desktop:preview:debug` when you only need to iterate on frontend code or want a faster cold-start without waiting for `tauri dev` initialization. + > For the full script list, see [`package.json`](package.json). For agent-specific commands, verification, and architecture rules, see [`AGENTS.md`](AGENTS.md). ### Desktop debugging tools diff --git a/CONTRIBUTING_CN.md b/CONTRIBUTING_CN.md index cd5ae6f47..0421c9c02 100644 --- a/CONTRIBUTING_CN.md +++ b/CONTRIBUTING_CN.md @@ -35,15 +35,21 @@ pnpm install ### 常用命令 ```bash -# Desktop -pnpm run desktop:dev -pnpm run desktop:preview:debug +# Desktop(日常开发推荐) +pnpm run desktop:dev # 完整热更新:Vite HMR + Rust 自动重编译并重启 + +# Desktop(轻量预览,无 Rust 自动重编译) +pnpm run desktop:preview:debug # 复用预构建二进制 + Vite HMR;Rust 改动需手动重启 + +# Desktop(生产构建) pnpm run desktop:build # E2E pnpm run e2e:test ``` +> **`desktop:dev` 与 `desktop:preview:debug` 的区别**:`desktop:dev` 运行 `tauri dev`,提供**完整热更新** — 前端改动通过 Vite HMR 即时生效,Rust/后端改动会触发增量重编译并自动重启应用,是日常开发的首选方式。`desktop:preview:debug` 启动预构建的 debug 二进制和 Vite dev server;前端编辑仍可 HMR,但 **Rust 侧改动不会自动重编译** — 需要手动停止并重新运行命令(或使用 `--force-rebuild`)。适合仅需迭代前端代码、或希望跳过 `tauri dev` 初始化以更快冷启动的场景。 + > 完整脚本列表见 [`package.json`](package.json)。agent 专用命令、验证与架构规则见 [`AGENTS.md`](AGENTS.md)。 ### 桌面端调试工具 diff --git a/src/crates/core/src/agentic/agents/prompts/deep_review_agent.md b/src/crates/core/src/agentic/agents/prompts/deep_review_agent.md index b2f0b72f4..810ba6921 100644 --- a/src/crates/core/src/agentic/agents/prompts/deep_review_agent.md +++ b/src/crates/core/src/agentic/agents/prompts/deep_review_agent.md @@ -128,6 +128,7 @@ Each reviewer Task prompt must include: - a request for concrete findings only - a strict output format that is easy to verify later - for split instances: an explicit list of the files this instance is responsible for, and an instruction not to review files outside the assigned group unless a cross-file dependency is critical +- a time-awareness reminder: "You have a strict timeout. Prioritize: (1) Inspect the diff first, then read only files the diff directly references. (2) Confirm or dismiss each hypothesis before opening a new investigation path. (3) Write your findings early — a partial report with confirmed findings is more valuable than no report at all." Strategy guidance (fallback only; the configured `prompt_directive` is the source of truth): @@ -135,6 +136,16 @@ Strategy guidance (fallback only; the configured `prompt_directive` is the sourc - `normal`: brief the reviewer to run the standard role-specific pass with balanced coverage and concrete evidence. - `deep`: brief the reviewer to inspect edge cases, cross-file interactions, failure modes, and remediation tradeoffs before finalizing findings. +Role-specific strategy amplification (append to the reviewer Task prompt when the strategy matches): + +- **ReviewBusinessLogic** + `quick`: "Only trace logic paths directly changed by the diff. Do not follow call chains beyond one hop." +- **ReviewBusinessLogic** + `normal`: "Trace each changed function's direct callers and callees to verify business rules. Stop once you have enough evidence per path." +- **ReviewBusinessLogic** + `deep`: "Map full call chains for changed functions. Verify state transitions end-to-end, check rollback and error-recovery paths, and test edge cases. Prioritize findings by user-facing impact." +- **ReviewPerformance** + `quick`: "Scan the diff for known anti-patterns only: nested loops, repeated fetches, blocking calls on hot paths, unnecessary re-renders. Do not trace call chains." +- **ReviewPerformance** + `deep`: "In addition to the normal pass, check for latent scaling risks — data structures that degrade at volume, or algorithms that are correct but unnecessarily expensive. Only report if you can estimate the impact." +- **ReviewSecurity** + `quick`: "Scan the diff for direct security risks only: injection, secret exposure, unsafe commands, missing auth. Do not trace data flows beyond one hop." +- **ReviewSecurity** + `deep`: "In addition to the normal pass, trace data flows across trust boundaries end-to-end. Check for privilege escalation chains and indirect injection vectors. Report only with a complete threat narrative." + ### Phase 3: Quality gate After the reviewer batch finishes, launch `ReviewJudge` with: @@ -143,6 +154,10 @@ After the reviewer batch finishes, launch `ReviewJudge` with: - the full reviewer outputs from every reviewer that ran, including timeout/cancel/failure notes - if file splitting was used, include outputs from **all** same-role instances and label each by group (e.g. "Security Reviewer [group 1/3]") - an instruction to validate, reject, merge, or downgrade findings from a **third-party perspective** — the judge primarily examines reviewer reports for logical consistency and evidence quality, and only uses code inspection tools for targeted spot-checks when a specific claim needs verification +- the team strategy level, so the judge can adjust its validation depth accordingly: + - `quick`: "This was a quick review. Focus on confirming or rejecting each finding efficiently. If a finding's evidence is thin, reject it rather than spending time verifying." + - `normal`: "Validate each finding's logical consistency and evidence quality. Spot-check code only when a claim needs verification." + - `deep`: "This was a deep review with potentially complex findings. Cross-validate findings across reviewers for consistency. For each finding, verify the evidence supports the conclusion and the suggested fix is safe. Pay extra attention to overlapping findings across reviewers or same-role instances." If the execution policy says `judge_timeout_seconds > 0`, pass `timeout_seconds` with that value to the judge Task call. diff --git a/src/crates/core/src/agentic/agents/prompts/review_business_logic_agent.md b/src/crates/core/src/agentic/agents/prompts/review_business_logic_agent.md index e6a54d64a..e417b6d00 100644 --- a/src/crates/core/src/agentic/agents/prompts/review_business_logic_agent.md +++ b/src/crates/core/src/agentic/agents/prompts/review_business_logic_agent.md @@ -32,11 +32,21 @@ Never modify files or git state. ## Review standards - Confirm before claiming. -- Gather surrounding context before judging unfamiliar code. - Focus on behavior, not style. - Prefer a small number of well-supported issues over broad speculation. - If something is only a weak suspicion, call it out as low-confidence and do not overstate it. +## Efficiency rules + +- Start from the diff. Only read surrounding context when a potential issue in the diff requires it. +- Limit context reads to the minimum needed to confirm or reject a suspicion. Do not read entire modules speculatively. +- If you have checked a file and found no issues, move on. Do not re-read it from different angles. +- When you have enough evidence to support or dismiss a hypothesis, stop investigating that path immediately. +- Prefer a focused review with a few confirmed findings over exhaustive coverage that risks timing out with no output. +- If the strategy is `quick`, restrict your investigation to files and functions directly changed by the diff. Do not trace call chains beyond one hop. +- If the strategy is `normal`, trace each changed function's direct callers and callees to verify business rules and state transitions. Stop investigating a path once you have enough evidence. +- If the strategy is `deep`, map the full call chain for each changed function. Verify state transitions end-to-end, check rollback and error-recovery paths, and test edge cases in data shape and lifecycle assumptions. Prioritize findings by user-facing impact. + ## Output format Return markdown only, using this exact structure: diff --git a/src/crates/core/src/agentic/agents/prompts/review_performance_agent.md b/src/crates/core/src/agentic/agents/prompts/review_performance_agent.md index 5930bfdbc..d3cfc7b2d 100644 --- a/src/crates/core/src/agentic/agents/prompts/review_performance_agent.md +++ b/src/crates/core/src/agentic/agents/prompts/review_performance_agent.md @@ -37,6 +37,17 @@ Never modify files or git state. - When impact is uncertain, lower severity and explain the assumption. - If current code is acceptable for the expected scale, say so. +## Efficiency rules + +- Start from the diff. Scan for known performance anti-patterns first: loops inside loops, repeated fetches, blocking calls on hot paths, unnecessary re-renders, large allocations. +- Only read surrounding code when a potential pattern in the diff needs confirmation of its context (e.g. is this on a hot path? is this called in a loop?). +- Do not read entire modules to speculate about hypothetical scaling problems. +- When you have confirmed or dismissed a performance concern, move on. Do not re-examine the same code from different angles. +- Prefer a focused report with confirmed regressions over a broad survey that risks timing out. +- If the strategy is `quick`, report only issues with direct evidence in the diff. Do not trace call chains or estimate impact beyond what the diff shows. +- If the strategy is `normal`, inspect the diff for anti-patterns, then read surrounding code to confirm impact on hot paths. Report only issues likely to matter at realistic scale. +- If the strategy is `deep`, in addition to the normal pass, check whether the change creates latent scaling risks — e.g. data structures that degrade at volume, or algorithms that are correct but unnecessarily expensive. Only report if you can quantify or estimate the impact. Do not speculate about edge cases or failure modes unrelated to performance. + ## Output format Return markdown only, using this exact structure: diff --git a/src/crates/core/src/agentic/agents/prompts/review_quality_gate_agent.md b/src/crates/core/src/agentic/agents/prompts/review_quality_gate_agent.md index fd620e0ac..96be33e4b 100644 --- a/src/crates/core/src/agentic/agents/prompts/review_quality_gate_agent.md +++ b/src/crates/core/src/agentic/agents/prompts/review_quality_gate_agent.md @@ -33,6 +33,17 @@ Be especially skeptical of: - duplicated findings reported by multiple reviewers or multiple same-role instances - findings where the stated evidence does not logically lead to the stated conclusion +## Efficiency rules + +- Start from the reviewer reports. Only use code inspection tools when a specific claim needs verification or you suspect a false positive. +- Do not broadly re-review the codebase. Your job is to validate reviewer reasoning, not to discover new issues independently. +- Process findings in order of severity. Validate high-severity findings first; if time is limited, lower-severity findings can receive a quicker pass. +- When a finding's evidence is clearly sufficient or clearly insufficient, make your decision quickly. Reserve detailed spot-checks for ambiguous findings only. +- Prefer completing validation of all findings over deep-diving into a single finding. +- If the team strategy was `quick`, focus on confirming or rejecting each finding efficiently. If a finding's evidence is thin, reject it rather than spending time verifying. +- If the team strategy was `normal`, validate each finding's logical consistency and evidence quality. Spot-check code only when a claim needs verification. +- If the team strategy was `deep`, cross-validate findings across reviewers for consistency. For each finding, verify the evidence supports the conclusion and the suggested fix is safe. Pay extra attention to findings that overlap across reviewers or across same-role instances from file splitting. + ## Tools Use read-only investigation when needed: diff --git a/src/crates/core/src/agentic/agents/prompts/review_security_agent.md b/src/crates/core/src/agentic/agents/prompts/review_security_agent.md index 38b2ebb27..b8305458c 100644 --- a/src/crates/core/src/agentic/agents/prompts/review_security_agent.md +++ b/src/crates/core/src/agentic/agents/prompts/review_security_agent.md @@ -37,6 +37,17 @@ Never modify files or git state. - Prefer concrete threat narratives over vague warnings. - If there is insufficient evidence for a real security issue, do not report it. +## Efficiency rules + +- Start from the diff. Scan for direct security risks first: injection, secret exposure, unsafe command/file handling, missing auth checks. +- Only trace data flows beyond the diff when a potential vulnerability needs confirmation of its reachability or exploitability. +- Do not read entire modules to search for hypothetical attack surfaces. +- When you have confirmed or dismissed a security concern, move on. Do not re-examine the same code from different angles. +- Prefer a focused report with confirmed vulnerabilities over a broad survey that risks timing out. +- If the strategy is `quick`, report only issues with a concrete exploit path visible in the diff. Do not trace data flows beyond one hop. +- If the strategy is `normal`, trace each changed input path from entry point to usage. Check trust boundaries, auth assumptions, and data sanitization. Report only issues with a realistic threat narrative. +- If the strategy is `deep`, in addition to the normal pass, trace data flows across trust boundaries end-to-end. Check for privilege escalation chains, indirect injection vectors, and failure modes that expose sensitive data. Report only issues with a complete threat narrative. + ## Output format Return markdown only, using this exact structure: diff --git a/src/crates/core/src/agentic/session/session_manager.rs b/src/crates/core/src/agentic/session/session_manager.rs index 3ac93b213..76bf171cc 100644 --- a/src/crates/core/src/agentic/session/session_manager.rs +++ b/src/crates/core/src/agentic/session/session_manager.rs @@ -70,6 +70,164 @@ pub struct ResolvedSessionTitle { pub method: SessionTitleMethod, } +#[cfg(test)] +mod tests { + use super::{SessionManager, SessionManagerConfig}; + use crate::agentic::core::{ProcessingPhase, Session, SessionConfig, SessionState}; + use crate::agentic::persistence::PersistenceManager; + use crate::agentic::session::SessionContextStore; + use crate::infrastructure::PathManager; + use std::path::{Path, PathBuf}; + use std::sync::Arc; + use std::time::Duration; + use uuid::Uuid; + + struct TestWorkspace { + path: PathBuf, + } + + impl TestWorkspace { + fn new() -> Self { + let path = std::env::temp_dir() + .join(format!("bitfun-session-restore-test-{}", Uuid::new_v4())); + std::fs::create_dir_all(&path).expect("test workspace should be created"); + Self { path } + } + + fn path(&self) -> &Path { + &self.path + } + } + + impl Drop for TestWorkspace { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.path); + } + } + + fn test_manager(persistence_manager: Arc) -> SessionManager { + SessionManager::new( + Arc::new(SessionContextStore::new()), + persistence_manager, + SessionManagerConfig { + max_active_sessions: 100, + session_idle_timeout: Duration::from_secs(3600), + auto_save_interval: Duration::from_secs(300), + enable_persistence: true, + }, + ) + } + + #[tokio::test] + async fn restore_session_resets_processing_state_without_marking_unread_completion() { + let workspace = TestWorkspace::new(); + let persistence_manager = Arc::new( + PersistenceManager::new(Arc::new(PathManager::new().expect("path manager"))) + .expect("persistence manager"), + ); + let session_id = Uuid::new_v4().to_string(); + let mut session = Session::new_with_id( + session_id.clone(), + "Legacy processing session".to_string(), + "agent".to_string(), + SessionConfig { + workspace_path: Some(workspace.path().to_string_lossy().to_string()), + ..Default::default() + }, + ); + session.state = SessionState::Processing { + current_turn_id: "turn-1".to_string(), + phase: ProcessingPhase::Thinking, + }; + + persistence_manager + .save_session(workspace.path(), &session) + .await + .expect("session should save"); + persistence_manager + .save_session_state(workspace.path(), &session_id, &session.state) + .await + .expect("processing state should save"); + + let manager = test_manager(persistence_manager.clone()); + let restored = manager + .restore_session(workspace.path(), &session_id) + .await + .expect("session should restore"); + let metadata = persistence_manager + .load_session_metadata(workspace.path(), &session_id) + .await + .expect("metadata should load") + .expect("metadata should exist"); + + assert!(matches!(restored.state, SessionState::Idle)); + assert_eq!(metadata.unread_completion, None); + } + + #[test] + fn build_messages_from_turns_skips_manual_compaction_turns() { + use crate::service::session::{DialogTurnData, DialogTurnKind, UserMessageData}; + + let turns = vec![ + DialogTurnData::new( + "turn-1".to_string(), + 0, + "session-1".to_string(), + UserMessageData { + id: "user-1".to_string(), + content: "hello".to_string(), + timestamp: 1, + metadata: None, + }, + ), + DialogTurnData::new_with_kind( + DialogTurnKind::ManualCompaction, + "turn-2".to_string(), + 1, + "session-1".to_string(), + UserMessageData { + id: "user-2".to_string(), + content: "/compact".to_string(), + timestamp: 2, + metadata: None, + }, + ), + ]; + + let messages = SessionManager::build_messages_from_turns(&turns); + + assert_eq!(messages.len(), 1); + assert!(messages[0].is_actual_user_message()); + } + + #[test] + fn fallback_session_title_uses_sentence_break_when_available() { + let title = SessionManager::fallback_session_title( + "Fix the flaky integration test. Add logging for retries.", + 20, + ); + + assert_eq!(title, "Fix the flaky..."); + } + + #[test] + fn fallback_session_title_appends_ellipsis_when_truncated_without_sentence_break() { + let title = SessionManager::fallback_session_title( + "Implement session title generation fallback", + 12, + ); + + assert_eq!(title, "Implement..."); + } + + #[test] + fn fallback_session_title_uses_default_for_blank_input() { + let title = SessionManager::fallback_session_title(" ", 20); + + assert_eq!(title, "New Session"); + } +} + /// Session manager pub struct SessionManager { /// Active sessions in memory @@ -1008,13 +1166,14 @@ impl SessionManager { let trimmed = persisted_model_id.trim(); let needs_migration = if trimmed.is_empty() { false - } else if let Ok(ai_config) = get_global_config_service() - .await - .map_err(|e| BitFunError::config(e.to_string()))? - .get_config::(Some("ai")) - .await - { - !Self::is_session_model_id_usable(&ai_config, trimmed) + } else if let Ok(config_service) = get_global_config_service().await { + match config_service + .get_config::(Some("ai")) + .await + { + Ok(ai_config) => !Self::is_session_model_id_usable(&ai_config, trimmed), + Err(_) => false, + } } else { false }; @@ -1160,33 +1319,10 @@ impl SessionManager { context_msg_count ); - // Mark session as having unread completion if it was previously running (not Idle). - // This handles both normal app close and abnormal crash scenarios. - if previous_state_was_not_idle { - if let Ok(Some(mut metadata)) = self - .persistence_manager - .load_session_metadata(&session_storage_path, session_id) - .await - { - if metadata.unread_completion.is_none() { - debug!( - "Marking session as having unread completion (was interrupted during restore): session_id={}", - session_id - ); - metadata.unread_completion = Some("completed".to_string()); - if let Err(e) = self - .persistence_manager - .save_session_metadata(&session_storage_path, &metadata) - .await - { - warn!( - "Failed to save unread_completion metadata: session_id={}, error={}", - session_id, e - ); - } - } - } - } + // Do not infer unread completion from persisted runtime state during restore. + // Older IDE versions could leave sessions in non-idle states on disk; treating those + // as completed would surface misleading unread indicators after an upgrade. + // Unread completion is now written only by runtime completion/persist paths. // 4. Add to memory (will overwrite if already exists) self.sessions @@ -2125,69 +2261,4 @@ impl SessionManager { } } -#[cfg(test)] -mod tests { - use super::SessionManager; - use crate::service::session::{DialogTurnData, DialogTurnKind, UserMessageData}; - #[test] - fn build_messages_from_turns_skips_manual_compaction_turns() { - let turns = vec![ - DialogTurnData::new( - "turn-1".to_string(), - 0, - "session-1".to_string(), - UserMessageData { - id: "user-1".to_string(), - content: "hello".to_string(), - timestamp: 1, - metadata: None, - }, - ), - DialogTurnData::new_with_kind( - DialogTurnKind::ManualCompaction, - "turn-2".to_string(), - 1, - "session-1".to_string(), - UserMessageData { - id: "user-2".to_string(), - content: "/compact".to_string(), - timestamp: 2, - metadata: None, - }, - ), - ]; - - let messages = SessionManager::build_messages_from_turns(&turns); - - assert_eq!(messages.len(), 1); - assert!(messages[0].is_actual_user_message()); - } - - #[test] - fn fallback_session_title_uses_sentence_break_when_available() { - let title = SessionManager::fallback_session_title( - "Fix the flaky integration test. Add logging for retries.", - 20, - ); - - assert_eq!(title, "Fix the flaky..."); - } - - #[test] - fn fallback_session_title_appends_ellipsis_when_truncated_without_sentence_break() { - let title = SessionManager::fallback_session_title( - "Implement session title generation fallback", - 12, - ); - - assert_eq!(title, "Implement..."); - } - - #[test] - fn fallback_session_title_uses_default_for_blank_input() { - let title = SessionManager::fallback_session_title(" ", 20); - - assert_eq!(title, "New Session"); - } -} diff --git a/src/web-ui/src/app/components/NavPanel/NavSearchDialog.tsx b/src/web-ui/src/app/components/NavPanel/NavSearchDialog.tsx index 6535508ce..30c4b286d 100644 --- a/src/web-ui/src/app/components/NavPanel/NavSearchDialog.tsx +++ b/src/web-ui/src/app/components/NavPanel/NavSearchDialog.tsx @@ -18,6 +18,11 @@ import { sessionAPI } from '@/infrastructure/api'; import { WorkspaceKind } from '@/shared/types'; import { i18nService } from '@/infrastructure/i18n'; import { resolvePersistedSessionTitle, resolveSessionTitle } from '@/flow_chat/utils/sessionTitle'; +import { + compareSessionsForDisplay, + getSessionMetadataSortTimestamp, + getSessionSortTimestamp, +} from '@/flow_chat/utils/sessionOrdering'; import './NavSearchDialog.scss'; interface NavSearchDialogProps { @@ -40,9 +45,6 @@ const MAX_PER_GROUP = 20; const getTitle = (session: Session): string => resolveSessionTitle(session, (key, options) => i18nService.t(key, options)); -const sessionRecencyTime = (session: Session): number => - session.updatedAt ?? session.lastActiveAt ?? session.createdAt ?? 0; - const matchesQuery = (query: string, ...fields: (string | undefined | null)[]): boolean => { const q = query.toLowerCase(); return fields.some(f => f && f.toLowerCase().includes(q)); @@ -131,7 +133,7 @@ const NavSearchDialog: React.FC = ({ open, onClose }) => { result.push({ session, workspace }); } } - result.sort((a, b) => sessionRecencyTime(b.session) - sessionRecencyTime(a.session)); + result.sort((a, b) => compareSessionsForDisplay(a.session, b.session)); return result; }, [flowChatState.sessions, openedWorkspacesList, openedWorkspaceIdSet]); @@ -192,13 +194,23 @@ const NavSearchDialog: React.FC = ({ open, onClose }) => { merged.sort((a, b) => { const ta = 'session' in a - ? sessionRecencyTime(a.session) - : a.disk.lastActiveAt ?? a.disk.createdAt ?? 0; + ? getSessionSortTimestamp(a.session) + : getSessionMetadataSortTimestamp(a.disk); const tb = 'session' in b - ? sessionRecencyTime(b.session) - : b.disk.lastActiveAt ?? b.disk.createdAt ?? 0; - return tb - ta; + ? getSessionSortTimestamp(b.session) + : getSessionMetadataSortTimestamp(b.disk); + const timestampDiff = tb - ta; + if (timestampDiff !== 0) return timestampDiff; + + const createdAtA = 'session' in a ? a.session.createdAt : a.disk.createdAt; + const createdAtB = 'session' in b ? b.session.createdAt : b.disk.createdAt; + const createdAtDiff = createdAtB - createdAtA; + if (createdAtDiff !== 0) return createdAtDiff; + + const sessionIdA = 'session' in a ? a.session.sessionId : a.disk.sessionId; + const sessionIdB = 'session' in b ? b.session.sessionId : b.disk.sessionId; + return sessionIdA.localeCompare(sessionIdB); }); for (const entry of merged.slice(0, MAX_PER_GROUP)) { diff --git a/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss b/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss index 3a12b0918..bf485dce1 100644 --- a/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss +++ b/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss @@ -2,7 +2,7 @@ .review-team-page { .bitfun-config-page-content__inner { - max-width: 1280px; + max-width: 1600px; } &__summary-grid { diff --git a/src/web-ui/src/flow_chat/components/ChatInput.tsx b/src/web-ui/src/flow_chat/components/ChatInput.tsx index 818770cc8..962be9142 100644 --- a/src/web-ui/src/flow_chat/components/ChatInput.tsx +++ b/src/web-ui/src/flow_chat/components/ChatInput.tsx @@ -627,6 +627,19 @@ export const ChatInput: React.FC = ({ }; }, [clearPendingLargePastes]); + // Expose current input value for external queries (e.g. deep review fill-back confirmation) + React.useEffect(() => { + const handleGetChatInputState = (request: { getValue?: () => string }) => { + request.getValue = () => inputValueRef.current; + }; + + globalEventBus.on('chat-input:get-state', handleGetChatInputState); + + return () => { + globalEventBus.off('chat-input:get-state', handleGetChatInputState); + }; + }, []); + React.useEffect(() => { if (!slashCommandState.isActive || slashCommandState.kind !== 'all' || derivedState?.isProcessing) { return; diff --git a/src/web-ui/src/flow_chat/components/TaskDetailPanel/TaskDetailPanel.tsx b/src/web-ui/src/flow_chat/components/TaskDetailPanel/TaskDetailPanel.tsx index e55319b6d..b535763bf 100644 --- a/src/web-ui/src/flow_chat/components/TaskDetailPanel/TaskDetailPanel.tsx +++ b/src/web-ui/src/flow_chat/components/TaskDetailPanel/TaskDetailPanel.tsx @@ -145,6 +145,7 @@ export interface TaskDetailPanelProps { export const TaskDetailPanel: React.FC = ({ data }) => { const { t } = useTranslation('flow-chat'); + const { t: tAgents } = useTranslation('scenes/agents'); const { toolItem: initialToolItem, taskInput, sessionId } = data || {}; const parentTaskToolId = initialToolItem?.id; const parentTaskToolCallId = initialToolItem?.toolCall?.id; @@ -502,6 +503,8 @@ export const TaskDetailPanel: React.FC = ({ data }) => { ); } + const rc = taskInput?.reviewerContext; + return (
@@ -546,19 +549,27 @@ export const TaskDetailPanel: React.FC = ({ data }) => { ref={contentRef} className="task-detail-panel__content" > - {taskInput?.reviewerContext ? ( + {rc ? (
{t('toolCards.taskDetailPanel.reviewerContextLabel')}
-
- {taskInput.reviewerContext.roleName} +
+ {tAgents(`reviewTeams.members.${rc.definitionKey}.role`, { + defaultValue: rc.roleName, + })}
- {taskInput.reviewerContext.description} + {tAgents(`reviewTeams.members.${rc.definitionKey}.description`, { + defaultValue: rc.description, + })}
    - {taskInput.reviewerContext.responsibilities.map((resp, idx) => ( -
  • {resp}
  • + {rc.responsibilities.map((resp, idx) => ( +
  • + {tAgents(`reviewTeams.members.${rc.definitionKey}.responsibilities.${idx}`, { + defaultValue: resp, + })} +
  • ))}
diff --git a/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.scss b/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.scss index 32ae26e16..321108725 100644 --- a/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.scss +++ b/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.scss @@ -143,11 +143,7 @@ } &__action-bar-wrapper { - position: absolute; - bottom: 0; - left: 0; - right: 0; - z-index: 10; + flex-shrink: 0; padding: 0 14px 14px; pointer-events: none; diff --git a/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.tsx b/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.tsx index e48b3e978..6fda17a0f 100644 --- a/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.tsx +++ b/src/web-ui/src/flow_chat/components/btw/BtwSessionPanel.tsx @@ -72,8 +72,6 @@ export const BtwSessionPanel: React.FC = ({ const [stoppingReview, setStoppingReview] = useState(false); const [showScrollToBottom, setShowScrollToBottom] = useState(false); const scrollContainerRef = useRef(null); - const actionBarRef = useRef(null); - const [actionBarHeight, setActionBarHeight] = useState(0); const shouldAutoScrollRef = useRef(true); useEffect(() => { @@ -449,32 +447,6 @@ export const BtwSessionPanel: React.FC = ({ }; }, [childSession, childSessionId, parentSessionId, isReviewSession, isDeepReview]); - // Observe action bar height to adjust body padding dynamically - useEffect(() => { - if (!showReviewActionBar) { - setActionBarHeight(0); - return; - } - - const el = actionBarRef.current; - if (!el) return; - - const observer = new ResizeObserver((entries) => { - for (const entry of entries) { - const h = entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height; - setActionBarHeight(h); - } - }); - - observer.observe(el); - // Initial measurement - setActionBarHeight(el.getBoundingClientRect().height); - - return () => { - observer.disconnect(); - }; - }, [showReviewActionBar]); - const btwOrigin = childSession?.btwOrigin; const parentLabel = resolveSessionTitle(parentSession, t('btw.parent')); const backTooltip = btwOrigin?.parentTurnIndex @@ -593,7 +565,6 @@ export const BtwSessionPanel: React.FC = ({
0 ? { paddingBottom: `${actionBarHeight + 20}px` } : undefined} > {virtualItems.length === 0 ? (
{t('session.empty')}
@@ -642,7 +613,7 @@ export const BtwSessionPanel: React.FC = ({ )} {showReviewActionBar && ( -
+
)} diff --git a/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.scss b/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.scss index ae43bbcc3..f5616065d 100644 --- a/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.scss +++ b/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.scss @@ -1,8 +1,8 @@ .deep-review-action-bar { - position: absolute; - bottom: 14px; - left: 14px; - right: 14px; + position: relative; + bottom: auto; + left: auto; + right: auto; z-index: 10; border-radius: 12px; padding: 16px 18px; @@ -124,19 +124,18 @@ align-items: center; gap: 8px; padding: 6px 8px; - border-radius: 8px; + border-radius: 6px; background: transparent; - border: 1px solid transparent; + border: none; color: var(--color-text-primary); font-size: 13px; font-weight: 500; cursor: pointer; - transition: background 0.2s, border-color 0.2s; + transition: background 0.2s; width: fit-content; &:hover { - background: var(--element-bg-soft); - border-color: var(--border-base); + background: var(--element-bg-subtle); } .checkbox { @@ -151,9 +150,11 @@ &__remediation-list { display: flex; flex-direction: column; - gap: 10px; + gap: 4px; max-height: 260px; overflow-y: auto; + overscroll-behavior: contain; + scrollbar-gutter: stable; padding-right: 4px; } @@ -161,18 +162,14 @@ &__remediation-group { display: flex; flex-direction: column; - gap: 2px; - border: 1px solid var(--border-base); - border-radius: 8px; - overflow: hidden; } &__remediation-group-header { display: flex; align-items: center; gap: 8px; - padding: 8px 10px; - background: var(--element-bg-subtle); + padding: 6px 8px; + background: transparent; border: none; cursor: pointer; width: 100%; @@ -180,9 +177,10 @@ transition: background 0.2s; font-size: 13px; color: var(--color-text-primary); + border-radius: 6px; &:hover { - background: var(--element-bg-soft); + background: var(--element-bg-subtle); } .checkbox { @@ -204,20 +202,17 @@ &__remediation-group-items { display: flex; flex-direction: column; + padding-left: 28px; } &__remediation-item { display: flex; align-items: flex-start; gap: 8px; - padding: 7px 10px 7px 38px; + padding: 5px 8px; cursor: pointer; transition: background 0.15s; - border-bottom: 1px solid var(--border-base); - - &:last-child { - border-bottom: none; - } + border-radius: 6px; &:hover { background: var(--element-bg-subtle); @@ -280,12 +275,25 @@ } &__empty-selection { - padding: 8px 10px; - border-radius: 6px; - background: var(--element-bg-subtle); + display: flex; + align-items: center; + gap: 8px; + padding: 9px 11px; + border: 1px solid color-mix(in srgb, var(--color-warning, #f59e0b) 24%, var(--border-base)); + border-radius: 8px; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--color-warning, #f59e0b) 10%, var(--color-bg-secondary)) 0%, + color-mix(in srgb, var(--color-warning, #f59e0b) 4%, var(--color-bg-secondary)) 100% + ); font-size: 12px; - color: var(--color-text-muted); - text-align: center; + color: var(--color-text-secondary); + box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 8%, transparent); + } + + &__empty-selection-icon { + flex-shrink: 0; + color: var(--color-warning, #f59e0b); } /* No issues found */ diff --git a/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.test.tsx b/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.test.tsx index c02df409c..17cd23fb4 100644 --- a/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.test.tsx +++ b/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.test.tsx @@ -4,6 +4,8 @@ import { createRoot, type Root } from 'react-dom/client'; import { useReviewActionBarStore } from '../../store/deepReviewActionBarStore'; const sendMessageMock = vi.hoisted(() => vi.fn()); +const eventBusEmitMock = vi.hoisted(() => vi.fn()); +const confirmWarningMock = vi.hoisted(() => vi.fn()); vi.mock('react-i18next', () => ({ useTranslation: () => ({ @@ -45,10 +47,14 @@ vi.mock('../../services/FlowChatManager', () => ({ vi.mock('@/infrastructure/event-bus', () => ({ globalEventBus: { - emit: vi.fn(), + emit: eventBusEmitMock, }, })); +vi.mock('@/component-library/components/ConfirmDialog/confirmService', () => ({ + confirmWarning: confirmWarningMock, +})); + vi.mock('@/shared/notification-system', () => ({ notificationService: { error: vi.fn(), @@ -98,6 +104,8 @@ describeWithJsdom('DeepReviewActionBar', () => { document.body.appendChild(container); root = createRoot(container); sendMessageMock.mockResolvedValue(undefined); + confirmWarningMock.mockResolvedValue(true); + eventBusEmitMock.mockReturnValue(false); useReviewActionBarStore.getState().reset(); }); @@ -190,7 +198,86 @@ describeWithJsdom('DeepReviewActionBar', () => { expect(agentType).toBe('CodeReview'); }); - it('minimizes action bar when close button is clicked', async () => { + it('asks for confirmation before replacing existing chat input text', async () => { + const { DeepReviewActionBar } = await import('./DeepReviewActionBar'); + + eventBusEmitMock.mockImplementation((event: string, payload: { getValue?: () => string }) => { + if (event === 'chat-input:get-state') { + payload.getValue = () => 'existing draft'; + } + return true; + }); + confirmWarningMock.mockResolvedValue(false); + + useReviewActionBarStore.getState().showActionBar({ + childSessionId: 'child-session', + parentSessionId: 'parent-session', + reviewData: { + summary: { recommended_action: 'request_changes' }, + remediation_plan: ['Fix issue 1'], + }, + phase: 'review_completed', + }); + + await act(async () => { + root.render(); + }); + + const fillButton = Array.from(container.querySelectorAll('button')) + .find((button) => button.textContent?.includes('Fill to input')); + expect(fillButton).toBeTruthy(); + + await act(async () => { + fillButton!.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true })); + await Promise.resolve(); + }); + + expect(confirmWarningMock).toHaveBeenCalledTimes(1); + expect(eventBusEmitMock).not.toHaveBeenCalledWith('fill-chat-input', expect.anything()); + expect(useReviewActionBarStore.getState().dismissed).toBe(false); + }); + + it('fills chat input without confirmation when current input is empty', async () => { + const { DeepReviewActionBar } = await import('./DeepReviewActionBar'); + + eventBusEmitMock.mockImplementation((event: string, payload: { getValue?: () => string }) => { + if (event === 'chat-input:get-state') { + payload.getValue = () => ' '; + } + return true; + }); + + useReviewActionBarStore.getState().showActionBar({ + childSessionId: 'child-session', + parentSessionId: 'parent-session', + reviewData: { + summary: { recommended_action: 'request_changes' }, + remediation_plan: ['Fix issue 1'], + }, + phase: 'review_completed', + }); + + await act(async () => { + root.render(); + }); + + const fillButton = Array.from(container.querySelectorAll('button')) + .find((button) => button.textContent?.includes('Fill to input')); + expect(fillButton).toBeTruthy(); + + await act(async () => { + fillButton!.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true })); + await Promise.resolve(); + }); + + expect(confirmWarningMock).not.toHaveBeenCalled(); + expect(eventBusEmitMock).toHaveBeenCalledWith('fill-chat-input', expect.objectContaining({ + mode: 'replace', + })); + expect(useReviewActionBarStore.getState().dismissed).toBe(true); + }); + + it('dismisses action bar when close button is clicked', async () => { const { DeepReviewActionBar } = await import('./DeepReviewActionBar'); useReviewActionBarStore.getState().showActionBar({ @@ -215,8 +302,7 @@ describeWithJsdom('DeepReviewActionBar', () => { await Promise.resolve(); }); - expect(useReviewActionBarStore.getState().minimized).toBe(true); - expect(useReviewActionBarStore.getState().dismissed).toBe(false); + expect(useReviewActionBarStore.getState().dismissed).toBe(true); }); it('marks completed remediation items when fix completes', async () => { diff --git a/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.tsx b/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.tsx index f483f5a74..6db609a55 100644 --- a/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.tsx +++ b/src/web-ui/src/flow_chat/components/btw/DeepReviewActionBar.tsx @@ -12,7 +12,7 @@ import { MessageSquare, Play, Copy, - Minimize2, + Info, } from 'lucide-react'; import { Button, Checkbox, Tooltip } from '@/component-library'; import { useReviewActionBarStore, type ReviewActionPhase } from '../../store/deepReviewActionBarStore'; @@ -56,6 +56,13 @@ const GROUP_PRIORITY_META: Record = { verification: { color: 'var(--color-success, #22c55e)' }, }; +const stopNestedScrollPropagation = (event: React.WheelEvent | React.TouchEvent) => { + event.stopPropagation(); + if ('nativeEvent' in event && typeof event.nativeEvent.stopImmediatePropagation === 'function') { + event.nativeEvent.stopImmediatePropagation(); + } +}; + export const ReviewActionBar: React.FC = () => { const { t } = useTranslation('flow-chat'); const store = useReviewActionBarStore(); @@ -183,7 +190,7 @@ export const ReviewActionBar: React.FC = () => { } }, [reviewData, childSessionId, selectedRemediationIds, customInstructions, reviewMode, isDeepReview, store, t, completedRemediationIds]); - const handleFillBackInput = useCallback(() => { + const handleFillBackInput = useCallback(async () => { if (!reviewData) return; let prompt = buildSelectedReviewRemediationPrompt({ @@ -199,22 +206,40 @@ export const ReviewActionBar: React.FC = () => { if (!prompt) return; + // Check if chat input already has content — require confirmation before replacing + const currentInputRequest: { getValue?: () => string } = {}; + globalEventBus.emit('chat-input:get-state', currentInputRequest); + const currentInput = currentInputRequest.getValue?.() ?? ''; + + if (currentInput.trim()) { + const confirmed = await confirmWarning( + t('deepReviewActionBar.replaceInputConfirmTitle', { + defaultValue: 'Replace current input?', + }), + t('deepReviewActionBar.replaceInputConfirmMessage', { + defaultValue: 'The chat input already has text. Filling this plan will replace the current draft.', + }), + { + confirmText: t('deepReviewActionBar.replaceInputConfirmAction', { + defaultValue: 'Replace input', + }), + }, + ); + if (!confirmed) return; + } + globalEventBus.emit('fill-chat-input', { content: prompt, mode: 'replace', }); store.dismiss(); - }, [reviewData, selectedRemediationIds, customInstructions, reviewMode, store]); + }, [reviewData, selectedRemediationIds, customInstructions, reviewMode, store, t]); const handleDismiss = useCallback(() => { store.dismiss(); }, [store]); - const handleMinimize = useCallback(() => { - store.minimize(); - }, [store]); - const handleContinueReview = useCallback(async () => { if (!interruption) return; @@ -377,12 +402,16 @@ export const ReviewActionBar: React.FC = () => { } return ( -
+
@@ -424,7 +453,11 @@ export const ReviewActionBar: React.FC = () => { {showRemediationList && ( -
+
{groupOrder.map((groupId) => { const items = groupedItems[groupId]!; const groupSelectedCount = items.filter((i) => selectedRemediationIds.has(i.id)).length; @@ -495,18 +528,15 @@ export const ReviewActionBar: React.FC = () => {
)} - {/* Hint text */} -
- {t('toolCards.codeReview.remediationActions.hint', { - defaultValue: 'Deep Review is read-only by default. Select the remediation items to fix.', - })} -
- + {/* Empty selection hint */} {selectedCount === 0 && ( -
- {t('toolCards.codeReview.remediationActions.noSelectionHint', { - defaultValue: 'Select at least one remediation item to start fixing.', - })} +
+ + + {t('toolCards.codeReview.remediationActions.noSelectionHint', { + defaultValue: 'Select at least one remediation item to start fixing.', + })} +
)}
@@ -582,7 +612,7 @@ export const ReviewActionBar: React.FC = () => { variant="ghost" size="small" disabled={isFixDisabled} - onClick={handleFillBackInput} + onClick={() => void handleFillBackInput()} > {t('deepReviewActionBar.fillBackInput', { defaultValue: 'Fill to input' })} @@ -654,17 +684,6 @@ export const ReviewActionBar: React.FC = () => { {t('deepReviewActionBar.close', { defaultValue: 'Close' })} )} - - {(phase === 'review_completed' || phase === 'fix_interrupted') && ( - - )}
); diff --git a/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.tsx b/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.tsx index fbebb0d6b..e77d89745 100644 --- a/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.tsx +++ b/src/web-ui/src/flow_chat/tool-cards/TaskToolDisplay.tsx @@ -32,6 +32,7 @@ export const TaskToolDisplay: React.FC = ({ sessionId }) => { const { t } = useTranslation('flow-chat'); + const { t: tAgents } = useTranslation('scenes/agents'); const { toolCall, toolResult, status, requiresConfirmation, userConfirmed } = toolItem; const toolId = toolItem.id ?? toolCall?.id; @@ -327,11 +328,13 @@ export const TaskToolDisplay: React.FC = ({ return null; } + const rc = taskInput?.reviewerContext; + if ( !hasInterruptionNote && !hasRealPrompt && !needsConfirmation && - !taskInput?.reviewerContext + !rc ) { return null; } @@ -349,17 +352,25 @@ export const TaskToolDisplay: React.FC = ({ )} )} - {taskInput?.reviewerContext ? ( + {rc ? (
-
- {taskInput.reviewerContext.roleName} +
+ {tAgents(`reviewTeams.members.${rc.definitionKey}.role`, { + defaultValue: rc.roleName, + })}
- {taskInput.reviewerContext.description} + {tAgents(`reviewTeams.members.${rc.definitionKey}.description`, { + defaultValue: rc.description, + })}
    - {taskInput.reviewerContext.responsibilities.map((resp, idx) => ( -
  • {resp}
  • + {rc.responsibilities.map((resp, idx) => ( +
  • + {tAgents(`reviewTeams.members.${rc.definitionKey}.responsibilities.${idx}`, { + defaultValue: resp, + })} +
  • ))}
diff --git a/src/web-ui/src/flow_chat/utils/sessionMetadata.test.ts b/src/web-ui/src/flow_chat/utils/sessionMetadata.test.ts index 3c00674aa..e2e49cedf 100644 --- a/src/web-ui/src/flow_chat/utils/sessionMetadata.test.ts +++ b/src/web-ui/src/flow_chat/utils/sessionMetadata.test.ts @@ -366,4 +366,83 @@ describe('sessionMetadata', () => { canOpenInAuxPane: true, }); }); + + describe('unread completion persistence', () => { + it('persists unreadCompletion from session to metadata', () => { + const session = createSession({ + hasUnreadCompletion: 'completed', + }); + + const metadata = buildSessionMetadata(session); + + expect(metadata.unreadCompletion).toBe('completed'); + }); + + it('persists needsUserAttention from session to metadata', () => { + const session = createSession({ + needsUserAttention: 'ask_user', + }); + + const metadata = buildSessionMetadata(session); + + expect(metadata.needsUserAttention).toBe('ask_user'); + }); + + it('clears unreadCompletion when session has hasUnreadCompletion undefined', () => { + const session = createSession({ + hasUnreadCompletion: undefined, + }); + + const existingMetadata: SessionMetadata = { + sessionId: 'session-1', + sessionName: 'Session Title', + agentType: 'agentic', + modelName: 'gpt-test', + createdAt: 1000, + lastActiveAt: 1000, + turnCount: 0, + messageCount: 0, + toolCallCount: 0, + status: 'active', + tags: [], + customMetadata: {}, + todos: [], + workspacePath: '/workspace', + unreadCompletion: 'completed', + }; + + const metadata = buildSessionMetadata(session, existingMetadata); + + // The cleared value (undefined) must NOT fall back to existingMetadata.unreadCompletion + expect(metadata.unreadCompletion).toBeUndefined(); + }); + + it('clears needsUserAttention when session has needsUserAttention undefined', () => { + const session = createSession({ + needsUserAttention: undefined, + }); + + const existingMetadata: SessionMetadata = { + sessionId: 'session-1', + sessionName: 'Session Title', + agentType: 'agentic', + modelName: 'gpt-test', + createdAt: 1000, + lastActiveAt: 1000, + turnCount: 0, + messageCount: 0, + toolCallCount: 0, + status: 'active', + tags: [], + customMetadata: {}, + todos: [], + workspacePath: '/workspace', + needsUserAttention: 'tool_confirm', + }; + + const metadata = buildSessionMetadata(session, existingMetadata); + + expect(metadata.needsUserAttention).toBeUndefined(); + }); + }); }); diff --git a/src/web-ui/src/flow_chat/utils/sessionMetadata.ts b/src/web-ui/src/flow_chat/utils/sessionMetadata.ts index 5ec0a580e..c969f2a8d 100644 --- a/src/web-ui/src/flow_chat/utils/sessionMetadata.ts +++ b/src/web-ui/src/flow_chat/utils/sessionMetadata.ts @@ -298,9 +298,11 @@ export function buildSessionMetadata( remoteConnectionId: session.remoteConnectionId ?? existingMetadata?.remoteConnectionId, remoteSshHost: session.remoteSshHost ?? existingMetadata?.remoteSshHost, - unreadCompletion: - session.hasUnreadCompletion ?? existingMetadata?.unreadCompletion, - needsUserAttention: - session.needsUserAttention ?? existingMetadata?.needsUserAttention, + // Always use the in-memory session value as the source of truth. + // Previously this used `??` to fall back to existingMetadata, which prevented + // clears from reaching disk: when the store sets `hasUnreadCompletion: undefined`, + // `undefined ?? existingMetadata.unreadCompletion` would restore the old value. + unreadCompletion: session.hasUnreadCompletion, + needsUserAttention: session.needsUserAttention, }; } diff --git a/src/web-ui/src/flow_chat/utils/sessionOrdering.test.ts b/src/web-ui/src/flow_chat/utils/sessionOrdering.test.ts index 70857da56..5da0fff71 100644 --- a/src/web-ui/src/flow_chat/utils/sessionOrdering.test.ts +++ b/src/web-ui/src/flow_chat/utils/sessionOrdering.test.ts @@ -1,8 +1,10 @@ import { describe, expect, it } from 'vitest'; import type { Session } from '../types/flow-chat'; import { + compareSessionMetadataForDisplay, compareSessionsForDisplay, compareSessionsForNavStable, + getSessionMetadataSortTimestamp, getSessionSortTimestamp, sessionBelongsToWorkspaceNavRow, } from './sessionOrdering'; @@ -35,31 +37,26 @@ function createSession(overrides: Partial = {}): Session { } describe('sessionOrdering', () => { - it('uses lastActiveAt when available', () => { - const session = createSession({ createdAt: 1234, lastActiveAt: 5678 }); - expect(getSessionSortTimestamp(session)).toBe(5678); - }); - - it('uses lastFinishedAt when lastActiveAt is missing', () => { - const session = createSession({ createdAt: 1234, lastFinishedAt: 9999 }); + it('uses lastFinishedAt when available', () => { + const session = createSession({ createdAt: 1234, lastActiveAt: 5678, lastFinishedAt: 9999 }); expect(getSessionSortTimestamp(session)).toBe(9999); }); it('uses createdAt as fallback', () => { - const session = createSession({ createdAt: 1234 }); + const session = createSession({ createdAt: 1234, lastActiveAt: 5678 }); expect(getSessionSortTimestamp(session)).toBe(1234); }); - it('sorts sessions by lastActiveAt before lastFinishedAt and createdAt', () => { + it('does not move a switched or streaming session above newer display timestamps', () => { const sessions = [ createSession({ sessionId: 'older-new', createdAt: 1000 }), createSession({ sessionId: 'completed', createdAt: 500, lastFinishedAt: 3000 }), - createSession({ sessionId: 'just-active', createdAt: 200, lastActiveAt: 5000 }), + createSession({ sessionId: 'switched-or-streaming', createdAt: 200, lastActiveAt: 5000 }), createSession({ sessionId: 'newest-new', createdAt: 2000 }), ]; const orderedIds = [...sessions].sort(compareSessionsForDisplay).map(session => session.sessionId); - expect(orderedIds).toEqual(['just-active', 'completed', 'newest-new', 'older-new']); + expect(orderedIds).toEqual(['completed', 'newest-new', 'older-new', 'switched-or-streaming']); }); it('falls back to stable ordering when timestamps are equal', () => { @@ -81,6 +78,18 @@ describe('sessionOrdering', () => { expect(orderedIds).toEqual(['first', 'second']); }); + it('sorts persisted metadata by lastFinishedAt before createdAt without using lastActiveAt', () => { + const metadata = [ + { sessionId: 'older-new', createdAt: 1000, lastActiveAt: 9000 }, + { sessionId: 'completed', createdAt: 500, lastActiveAt: 600, customMetadata: { lastFinishedAt: 3000 } }, + { sessionId: 'newest-new', createdAt: 2000, lastActiveAt: 2500 }, + ]; + + expect(getSessionMetadataSortTimestamp(metadata[0])).toBe(1000); + const orderedIds = [...metadata].sort(compareSessionMetadataForDisplay).map(session => session.sessionId); + expect(orderedIds).toEqual(['completed', 'newest-new', 'older-new']); + }); + it('remote SSH: same host but different remote root does not share nav row', () => { const conn = 'ssh-user@myserver.example.com:22'; const host = 'myserver.example.com'; diff --git a/src/web-ui/src/flow_chat/utils/sessionOrdering.ts b/src/web-ui/src/flow_chat/utils/sessionOrdering.ts index 2bd7618f7..93b921b77 100644 --- a/src/web-ui/src/flow_chat/utils/sessionOrdering.ts +++ b/src/web-ui/src/flow_chat/utils/sessionOrdering.ts @@ -1,4 +1,5 @@ import type { Session } from '../types/flow-chat'; +import type { SessionMetadata } from '@/shared/types/session-history'; import { isSamePath, normalizeRemoteWorkspacePath } from '@/shared/utils/pathUtils'; /** Extract `host` from our saved form `ssh-{user}@{host}:{port}` (used when metadata omits `remoteSshHost`). */ @@ -62,13 +63,13 @@ export function sessionBelongsToWorkspaceNavRow( return true; } -export function getSessionSortTimestamp(session: Pick): number { - return session.lastActiveAt ?? session.lastFinishedAt ?? session.createdAt; +export function getSessionSortTimestamp(session: Pick): number { + return session.lastFinishedAt ?? session.createdAt; } export function compareSessionsForDisplay( - a: Pick, - b: Pick + a: Pick, + b: Pick ): number { const timestampDiff = getSessionSortTimestamp(b) - getSessionSortTimestamp(a); if (timestampDiff !== 0) { @@ -83,6 +84,30 @@ export function compareSessionsForDisplay( return a.sessionId.localeCompare(b.sessionId); } +export function getSessionMetadataSortTimestamp( + session: Pick +): number { + const lastFinishedAt = session.customMetadata?.lastFinishedAt; + return typeof lastFinishedAt === 'number' ? lastFinishedAt : session.createdAt; +} + +export function compareSessionMetadataForDisplay( + a: Pick, + b: Pick +): number { + const timestampDiff = getSessionMetadataSortTimestamp(b) - getSessionMetadataSortTimestamp(a); + if (timestampDiff !== 0) { + return timestampDiff; + } + + const createdAtDiff = b.createdAt - a.createdAt; + if (createdAtDiff !== 0) { + return createdAtDiff; + } + + return a.sessionId.localeCompare(b.sessionId); +} + /** * Left-nav session list order: newest-created first, stable while switching sessions * (does not use `lastActiveAt`, so rows do not jump to the top on click). @@ -95,5 +120,6 @@ export function compareSessionsForNavStable( if (createdAtDiff !== 0) { return createdAtDiff; } + return a.sessionId.localeCompare(b.sessionId); } diff --git a/src/web-ui/src/infrastructure/config/components/common/ConfigPageLayout.scss b/src/web-ui/src/infrastructure/config/components/common/ConfigPageLayout.scss index b94a8ee45..a5d7e01c7 100644 --- a/src/web-ui/src/infrastructure/config/components/common/ConfigPageLayout.scss +++ b/src/web-ui/src/infrastructure/config/components/common/ConfigPageLayout.scss @@ -96,10 +96,11 @@ } .bitfun-config-page-section__body { + min-width: 0; background: var(--element-bg-subtle); border: 1px solid var(--border-subtle); border-radius: var(--size-radius-md, 10px); - overflow: visible; + overflow: hidden; } .bitfun-config-page-row { diff --git a/src/web-ui/src/locales/en-US/flow-chat.json b/src/web-ui/src/locales/en-US/flow-chat.json index 97a6beae4..10bc26bc5 100644 --- a/src/web-ui/src/locales/en-US/flow-chat.json +++ b/src/web-ui/src/locales/en-US/flow-chat.json @@ -383,6 +383,9 @@ "customInstructionsPlaceholder": "Describe additional requirements or context for the fix...", "fillBackInput": "Fill to input", "fillBackInputHint": "Copy selected fix plan to the input box for manual editing", + "replaceInputConfirmTitle": "Replace current input?", + "replaceInputConfirmMessage": "The chat input already has text. Filling this plan will replace the current draft.", + "replaceInputConfirmAction": "Replace input", "reviewInterrupted": "Deep review interrupted", "resumeBlocked": "Action required before continuing", "resumeRunning": "Continuing review...", diff --git a/src/web-ui/src/locales/zh-CN/flow-chat.json b/src/web-ui/src/locales/zh-CN/flow-chat.json index 6cca84c53..33183ce1d 100644 --- a/src/web-ui/src/locales/zh-CN/flow-chat.json +++ b/src/web-ui/src/locales/zh-CN/flow-chat.json @@ -383,6 +383,9 @@ "customInstructionsPlaceholder": "描述修复的额外要求或上下文信息...", "fillBackInput": "填入输入框", "fillBackInputHint": "将选中的修复计划填入输入框,供手动编辑", + "replaceInputConfirmTitle": "替换当前输入?", + "replaceInputConfirmMessage": "输入框中已有文本,填入计划将替换当前草稿内容。", + "replaceInputConfirmAction": "替换输入", "reviewInterrupted": "深度审核已中断", "resumeBlocked": "继续前需要处理问题", "resumeRunning": "正在继续审核...", diff --git a/src/web-ui/src/locales/zh-TW/flow-chat.json b/src/web-ui/src/locales/zh-TW/flow-chat.json index a54530c46..24250f165 100644 --- a/src/web-ui/src/locales/zh-TW/flow-chat.json +++ b/src/web-ui/src/locales/zh-TW/flow-chat.json @@ -374,6 +374,9 @@ "customInstructionsPlaceholder": "描述修復的額外要求或上下文信息...", "fillBackInput": "填入輸入框", "fillBackInputHint": "將選中的修復計劃填入輸入框,供手動編輯", + "replaceInputConfirmTitle": "替換當前輸入?", + "replaceInputConfirmMessage": "輸入框中已有文本,填入計劃將替換當前草稿內容。", + "replaceInputConfirmAction": "替換輸入", "reviewInterrupted": "深度審核已中斷", "resumeBlocked": "繼續前需要處理問題", "resumeRunning": "正在繼續審核...", diff --git a/src/web-ui/src/shared/services/reviewTeamService.test.ts b/src/web-ui/src/shared/services/reviewTeamService.test.ts index 390212cde..9728ec140 100644 --- a/src/web-ui/src/shared/services/reviewTeamService.test.ts +++ b/src/web-ui/src/shared/services/reviewTeamService.test.ts @@ -280,14 +280,14 @@ describe('reviewTeamService', () => { strategyLevel: 'quick', strategySource: 'team', defaultModelSlot: 'fast', - strategyDirective: REVIEW_STRATEGY_DEFINITIONS.quick.promptDirective, + strategyDirective: REVIEW_STRATEGY_DEFINITIONS.quick.roleDirectives.ReviewBusinessLogic, }), expect.objectContaining({ subagentId: 'ReviewPerformance', strategyLevel: 'quick', strategySource: 'team', defaultModelSlot: 'fast', - strategyDirective: REVIEW_STRATEGY_DEFINITIONS.quick.promptDirective, + strategyDirective: REVIEW_STRATEGY_DEFINITIONS.quick.roleDirectives.ReviewPerformance, }), expect.objectContaining({ subagentId: 'ReviewSecurity', @@ -295,7 +295,7 @@ describe('reviewTeamService', () => { strategySource: 'member', model: 'primary', defaultModelSlot: 'primary', - strategyDirective: REVIEW_STRATEGY_DEFINITIONS.deep.promptDirective, + strategyDirective: REVIEW_STRATEGY_DEFINITIONS.deep.roleDirectives.ReviewSecurity, }), ]); expect(manifest.enabledExtraReviewers[0]).toMatchObject({ @@ -311,7 +311,7 @@ describe('reviewTeamService', () => { expect(promptBlock).toContain('subagent_type: ReviewSecurity'); expect(promptBlock).toContain('strategy: deep'); expect(promptBlock).toContain('model_id: primary'); - expect(promptBlock).toContain(`prompt_directive: ${REVIEW_STRATEGY_DEFINITIONS.deep.promptDirective}`); + expect(promptBlock).toContain(`prompt_directive: ${REVIEW_STRATEGY_DEFINITIONS.deep.roleDirectives.ReviewSecurity}`); expect(promptBlock).toContain('pass model_id with that value to the matching Task call'); expect(promptBlock).toContain('Token/time impact: approximately 1.8-2.5x token usage and 1.5-2.5x runtime.'); }); diff --git a/src/web-ui/src/shared/services/reviewTeamService.ts b/src/web-ui/src/shared/services/reviewTeamService.ts index 1faf8a115..f3ef21ab6 100644 --- a/src/web-ui/src/shared/services/reviewTeamService.ts +++ b/src/web-ui/src/shared/services/reviewTeamService.ts @@ -26,6 +26,12 @@ export interface ReviewStrategyCommonRules { reviewerPromptRules: string[]; } +export type ReviewRoleDirectiveKey = + | 'ReviewBusinessLogic' + | 'ReviewPerformance' + | 'ReviewSecurity' + | 'ReviewJudge'; + export interface ReviewStrategyProfile { level: ReviewStrategyLevel; label: string; @@ -34,6 +40,9 @@ export interface ReviewStrategyProfile { runtimeImpact: string; defaultModelSlot: 'fast' | 'primary'; promptDirective: string; + /** Per-role strategy directives. When a role key is present, its directive + * overrides `promptDirective` for that reviewer or the judge. */ + roleDirectives: Partial>; } export const REVIEW_STRATEGY_LEVELS: ReviewStrategyLevel[] = [ @@ -64,6 +73,16 @@ export const REVIEW_STRATEGY_PROFILES: Record< defaultModelSlot: 'fast', promptDirective: 'Prefer a concise diff-focused pass. Report only high-confidence correctness, security, or regression risks and avoid speculative design rewrites.', + roleDirectives: { + ReviewBusinessLogic: + 'Only trace logic paths directly changed by the diff. Do not follow call chains beyond one hop. Report only issues where the diff introduces a provably wrong behavior.', + ReviewPerformance: + 'Scan the diff for known anti-patterns only: nested loops, repeated fetches, blocking calls on hot paths, unnecessary re-renders. Do not trace call chains or estimate impact beyond what the diff shows.', + ReviewSecurity: + 'Scan the diff for direct security risks only: injection, secret exposure, unsafe commands, missing auth. Do not trace data flows beyond one hop.', + ReviewJudge: + 'This was a quick review. Focus on confirming or rejecting each finding efficiently. If a finding\'s evidence is thin, reject it rather than spending time verifying.', + }, }, normal: { level: 'normal', @@ -75,6 +94,16 @@ export const REVIEW_STRATEGY_PROFILES: Record< defaultModelSlot: 'fast', promptDirective: 'Perform the standard role-specific review. Balance coverage with precision and include concrete evidence for each issue.', + roleDirectives: { + ReviewBusinessLogic: + 'Trace each changed function\'s direct callers and callees to verify business rules and state transitions. Stop investigating a path once you have enough evidence to confirm or dismiss it.', + ReviewPerformance: + 'Inspect the diff for anti-patterns, then read surrounding code to confirm impact on hot paths. Report only issues likely to matter at realistic scale.', + ReviewSecurity: + 'Trace each changed input path from entry point to usage. Check trust boundaries, auth assumptions, and data sanitization. Report only issues with a realistic threat narrative.', + ReviewJudge: + 'Validate each finding\'s logical consistency and evidence quality. Spot-check code only when a claim needs verification.', + }, }, deep: { level: 'deep', @@ -86,6 +115,16 @@ export const REVIEW_STRATEGY_PROFILES: Record< defaultModelSlot: 'primary', promptDirective: 'Run a thorough role-specific pass. Inspect edge cases, cross-file interactions, failure modes, and remediation tradeoffs before finalizing findings.', + roleDirectives: { + ReviewBusinessLogic: + 'Map full call chains for changed functions. Verify state transitions end-to-end, check rollback and error-recovery paths, and test edge cases in data shape and lifecycle assumptions. Prioritize findings by user-facing impact.', + ReviewPerformance: + 'In addition to the normal pass, check for latent scaling risks — data structures that degrade at volume, or algorithms that are correct but unnecessarily expensive. Only report if you can estimate the impact. Do not speculate about edge cases or failure modes unrelated to performance.', + ReviewSecurity: + 'In addition to the normal pass, trace data flows across trust boundaries end-to-end. Check for privilege escalation chains, indirect injection vectors, and failure modes that expose sensitive data. Report only issues with a complete threat narrative.', + ReviewJudge: + 'This was a deep review with potentially complex findings. Cross-validate findings across reviewers for consistency. For each finding, verify the evidence supports the conclusion and the suggested fix is safe. Pay extra attention to overlapping findings across reviewers or same-role instances.', + }, }, }; @@ -668,6 +707,7 @@ function buildExtraMember( * of what each reviewer is doing. */ export interface ReviewerContext { + definitionKey: ReviewTeamCoreRoleKey; roleName: string; description: string; responsibilities: string[]; @@ -686,6 +726,7 @@ export function getReviewerContextBySubagentId( ); if (!coreRole) return null; return { + definitionKey: coreRole.key, roleName: coreRole.roleName, description: coreRole.description, responsibilities: coreRole.responsibilities, @@ -810,6 +851,8 @@ function toManifestMember( reason?: ReviewTeamManifestMember['reason'], ): ReviewTeamManifestMember { const strategyProfile = getReviewStrategyProfile(member.strategyLevel); + const roleDirective = + strategyProfile.roleDirectives[member.subagentId as ReviewRoleDirectiveKey]; return { subagentId: member.subagentId, displayName: member.displayName, @@ -820,7 +863,7 @@ function toManifestMember( defaultModelSlot: strategyProfile.defaultModelSlot, strategyLevel: member.strategyLevel, strategySource: member.strategySource, - strategyDirective: strategyProfile.promptDirective, + strategyDirective: roleDirective || strategyProfile.promptDirective, locked: member.locked, source: member.source, subagentSource: member.subagentSource, @@ -967,11 +1010,17 @@ export function buildReviewTeamPromptBlock( ].join('\n'); const strategyRules = REVIEW_STRATEGY_LEVELS.map((level) => { const definition = getReviewStrategyProfile(level); + const roleEntries = Object.entries(definition.roleDirectives) as [ReviewRoleDirectiveKey, string][]; + const roleLines = roleEntries.map( + ([role, directive]) => ` - ${role}: ${directive}`, + ); return [ `- ${level}: ${definition.summary}`, ` - ${formatStrategyImpact(level)}`, ` - Default model slot: ${definition.defaultModelSlot}`, - ` - Prompt directive: ${definition.promptDirective}`, + ` - Prompt directive (fallback): ${definition.promptDirective}`, + ` - Role-specific directives:`, + ...roleLines, ].join('\n'); }).join('\n'); const commonStrategyRules = REVIEW_STRATEGY_COMMON_RULES.reviewerPromptRules diff --git a/src/web-ui/tsconfig.tsbuildinfo b/src/web-ui/tsconfig.tsbuildinfo deleted file mode 100644 index 95cbe2588..000000000 --- a/src/web-ui/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/app/app.tsx","./src/app/index.ts","./src/app/components/apperrorboundary.tsx","./src/app/components/index.ts","./src/app/components/aboutdialog/aboutdialog.tsx","./src/app/components/aboutdialog/index.ts","./src/app/components/gallerylayout/gallerydetailmodal.tsx","./src/app/components/gallerylayout/galleryempty.tsx","./src/app/components/gallerylayout/gallerygrid.tsx","./src/app/components/gallerylayout/gallerylayout.tsx","./src/app/components/gallerylayout/gallerypageheader.tsx","./src/app/components/gallerylayout/galleryskeleton.tsx","./src/app/components/gallerylayout/galleryzone.tsx","./src/app/components/gallerylayout/index.ts","./src/app/components/mcpinteractiondialog/mcpinteractiondialog.tsx","./src/app/components/navbar/navbar.tsx","./src/app/components/navbar/index.ts","./src/app/components/navpanel/mainnav.tsx","./src/app/components/navpanel/navpanel.tsx","./src/app/components/navpanel/navsearchdialog.tsx","./src/app/components/navpanel/config.ts","./src/app/components/navpanel/index.ts","./src/app/components/navpanel/types.ts","./src/app/components/navpanel/components/branchquickswitch.tsx","./src/app/components/navpanel/components/miniappentry.tsx","./src/app/components/navpanel/components/navitem.tsx","./src/app/components/navpanel/components/persistentfooteractions.tsx","./src/app/components/navpanel/components/sectionheader.tsx","./src/app/components/navpanel/sections/sessions/sessionssection.tsx","./src/app/components/navpanel/sections/workspaces/dotmatrixarrowrighticon.tsx","./src/app/components/navpanel/sections/workspaces/workspaceitem.tsx","./src/app/components/navpanel/sections/workspaces/workspacelistsection.tsx","./src/app/components/newprojectdialog/newprojectdialog.tsx","./src/app/components/newprojectdialog/index.ts","./src/app/components/remoteconnectdialog/remoteconnectdialog.tsx","./src/app/components/remoteconnectdialog/remoteconnectdisclaimer.tsx","./src/app/components/remoteconnectdialog/index.ts","./src/app/components/remoteconnectdialog/remoteconnectdisclaimerstorage.ts","./src/app/components/scenebar/scenebar.tsx","./src/app/components/scenebar/scenetab.tsx","./src/app/components/scenebar/index.ts","./src/app/components/scenebar/types.ts","./src/app/components/splashscreen/splashscreen.tsx","./src/app/components/titlebar/notificationbutton.tsx","./src/app/components/titlebar/panelicons.tsx","./src/app/components/panels/branchselectmodal.tsx","./src/app/components/panels/difffullscreenviewer.tsx","./src/app/components/panels/filespanel.tsx","./src/app/components/panels/terminaleditmodal.tsx","./src/app/components/panels/index.ts","./src/app/components/panels/base/flexiblepanel.tsx","./src/app/components/panels/base/panelheader.tsx","./src/app/components/panels/base/index.ts","./src/app/components/panels/base/types.ts","./src/app/components/panels/base/utils.ts","./src/app/components/panels/content-canvas/contentcanvas.tsx","./src/app/components/panels/content-canvas/index.ts","./src/app/components/panels/content-canvas/anchor-zone/anchorzone.tsx","./src/app/components/panels/content-canvas/anchor-zone/index.ts","./src/app/components/panels/content-canvas/context/canvascontext.ts","./src/app/components/panels/content-canvas/context/canvasprovider.tsx","./src/app/components/panels/content-canvas/context/index.ts","./src/app/components/panels/content-canvas/editor-area/dropzone.tsx","./src/app/components/panels/content-canvas/editor-area/editorarea.tsx","./src/app/components/panels/content-canvas/editor-area/editorgroup.tsx","./src/app/components/panels/content-canvas/editor-area/splithandle.tsx","./src/app/components/panels/content-canvas/editor-area/index.ts","./src/app/components/panels/content-canvas/empty-state/emptystate.tsx","./src/app/components/panels/content-canvas/empty-state/index.ts","./src/app/components/panels/content-canvas/hooks/index.ts","./src/app/components/panels/content-canvas/hooks/usekeyboardshortcuts.ts","./src/app/components/panels/content-canvas/hooks/uselayoutstate.ts","./src/app/components/panels/content-canvas/hooks/usepaneltabcoordinator.ts","./src/app/components/panels/content-canvas/hooks/usetablifecycle.ts","./src/app/components/panels/content-canvas/mission-control/missioncontrol.tsx","./src/app/components/panels/content-canvas/mission-control/searchfilter.tsx","./src/app/components/panels/content-canvas/mission-control/thumbnailcard.tsx","./src/app/components/panels/content-canvas/mission-control/index.ts","./src/app/components/panels/content-canvas/quick-look/quicklook.tsx","./src/app/components/panels/content-canvas/quick-look/index.ts","./src/app/components/panels/content-canvas/stores/canvasstore.ts","./src/app/components/panels/content-canvas/stores/index.ts","./src/app/components/panels/content-canvas/tab-bar/tab.tsx","./src/app/components/panels/content-canvas/tab-bar/tabbar.tsx","./src/app/components/panels/content-canvas/tab-bar/taboverflowmenu.tsx","./src/app/components/panels/content-canvas/tab-bar/index.ts","./src/app/components/panels/content-canvas/types/content.ts","./src/app/components/panels/content-canvas/types/index.ts","./src/app/components/panels/content-canvas/types/layout.ts","./src/app/components/panels/content-canvas/types/tab.ts","./src/app/hooks/index.ts","./src/app/hooks/useapp.ts","./src/app/hooks/useassistantbootstrap.ts","./src/app/hooks/usecurrentsessiontitle.ts","./src/app/hooks/usecurrentsettingstabtitle.ts","./src/app/hooks/usedialogcompletionnotify.ts","./src/app/hooks/usegallerysceneautorefresh.ts","./src/app/hooks/useglobalsceneshortcuts.ts","./src/app/hooks/usenavhistory.ts","./src/app/hooks/usescenemanager.ts","./src/app/hooks/usesnapshot.ts","./src/app/hooks/usewindowcontrols.ts","./src/app/layout/applayout.tsx","./src/app/layout/floatingminichat.tsx","./src/app/layout/workspacebody.tsx","./src/app/layout/panelconfig.ts","./src/app/scenes/sceneviewport.tsx","./src/app/scenes/index.ts","./src/app/scenes/nav-registry.ts","./src/app/scenes/registry.ts","./src/app/scenes/agents/agentsscene.tsx","./src/app/scenes/agents/agentvisibility.ts","./src/app/scenes/agents/agentsicons.ts","./src/app/scenes/agents/agentsstore.ts","./src/app/scenes/agents/utils.ts","./src/app/scenes/agents/components/agentcard.tsx","./src/app/scenes/agents/components/coreagentcard.tsx","./src/app/scenes/agents/components/createagentpage.tsx","./src/app/scenes/agents/hooks/useagentslist.ts","./src/app/scenes/assistant/assistantscene.tsx","./src/app/scenes/browser/browserpanel.tsx","./src/app/scenes/browser/browserscene.tsx","./src/app/scenes/browser/browserinspectorscript.ts","./src/app/scenes/browser/browserurlcheck.ts","./src/app/scenes/file-viewer/fileviewernav.tsx","./src/app/scenes/file-viewer/fileviewerscene.tsx","./src/app/scenes/git/gitnav.tsx","./src/app/scenes/git/gitscene.tsx","./src/app/scenes/git/gitscenestore.ts","./src/app/scenes/git/index.ts","./src/app/scenes/git/views/branchesview.tsx","./src/app/scenes/git/views/graphview.tsx","./src/app/scenes/git/views/workingcopyview.tsx","./src/app/scenes/git/views/index.ts","./src/app/scenes/mermaid/mermaideditorscene.tsx","./src/app/scenes/miniapps/miniappgalleryscene.tsx","./src/app/scenes/miniapps/miniappscene.tsx","./src/app/scenes/miniapps/miniappstore.ts","./src/app/scenes/miniapps/components/miniappcard.tsx","./src/app/scenes/miniapps/components/miniapprunner.tsx","./src/app/scenes/miniapps/hooks/useminiappbridge.ts","./src/app/scenes/miniapps/hooks/useminiappcatalogsync.ts","./src/app/scenes/miniapps/utils/buildminiappthemevars.ts","./src/app/scenes/miniapps/utils/miniappicons.tsx","./src/app/scenes/miniapps/views/miniappgalleryview.tsx","./src/app/scenes/my-agent/assistantscheduleview.tsx","./src/app/scenes/my-agent/insightsscene.tsx","./src/app/scenes/my-agent/identitydocument.ts","./src/app/scenes/my-agent/insightsstore.ts","./src/app/scenes/my-agent/myagentstore.ts","./src/app/scenes/my-agent/useagentidentitydocument.ts","./src/app/scenes/panel-view/panelviewscene.tsx","./src/app/scenes/profile/profilescene.tsx","./src/app/scenes/profile/index.ts","./src/app/scenes/profile/nurserystore.ts","./src/app/scenes/profile/views/assistantcard.tsx","./src/app/scenes/profile/views/assistantconfigpage.tsx","./src/app/scenes/profile/views/assistantquickinput.tsx","./src/app/scenes/profile/views/nurserygallery.tsx","./src/app/scenes/profile/views/nurseryview.tsx","./src/app/scenes/profile/views/personaradar.tsx","./src/app/scenes/profile/views/templateconfigpage.tsx","./src/app/scenes/profile/views/index.ts","./src/app/scenes/profile/views/usetokenestimate.ts","./src/app/scenes/session/auxpane.tsx","./src/app/scenes/session/chatpane.tsx","./src/app/scenes/session/sessionscene.tsx","./src/app/scenes/session/index.ts","./src/app/scenes/settings/settingsnav.tsx","./src/app/scenes/settings/settingsscene.tsx","./src/app/scenes/settings/index.ts","./src/app/scenes/settings/settingsconfig.ts","./src/app/scenes/settings/settingsstore.ts","./src/app/scenes/settings/settingstabsearchcontent.ts","./src/app/scenes/settings/components/keyboardshortcutstab.tsx","./src/app/scenes/shell/shellnav.tsx","./src/app/scenes/shell/shellscene.tsx","./src/app/scenes/shell/shellconfig.ts","./src/app/scenes/shell/shellstore.ts","./src/app/scenes/shell/components/shellnaventryitem.tsx","./src/app/scenes/shell/components/shellnavworkspaceswitcher.tsx","./src/app/scenes/shell/hooks/index.ts","./src/app/scenes/shell/hooks/shellentrytypes.ts","./src/app/scenes/shell/hooks/usemanualterminalprofiles.ts","./src/app/scenes/shell/hooks/useshellentries.ts","./src/app/scenes/shell/hooks/useshellnavmenustate.ts","./src/app/scenes/shell/hooks/useterminalsessions.ts","./src/app/scenes/skills/skillsscene.tsx","./src/app/scenes/skills/skillsscenestore.ts","./src/app/scenes/skills/components/skillcard.tsx","./src/app/scenes/skills/hooks/useinstalledskills.ts","./src/app/scenes/skills/hooks/useskillmarket.ts","./src/app/scenes/terminal/terminalscene.tsx","./src/app/scenes/terminal/index.ts","./src/app/scenes/welcome/welcomescene.tsx","./src/app/scenes/welcome/index.ts","./src/app/services/appmanager.ts","./src/app/services/index.ts","./src/app/stores/navscenestore.ts","./src/app/stores/scenestore.ts","./src/app/stores/sessionmodestore.ts","./src/app/stores/terminalscenestore.ts","./src/app/types/index.ts","./src/app/utils/projectsessionworkspace.ts","./src/component-library/index.ts","./src/component-library/components/index.ts","./src/component-library/components/alert/alert.tsx","./src/component-library/components/alert/alertdemo.tsx","./src/component-library/components/alert/index.ts","./src/component-library/components/avatar/avatar.tsx","./src/component-library/components/avatar/index.ts","./src/component-library/components/badge/badge.tsx","./src/component-library/components/badge/index.ts","./src/component-library/components/button/button.tsx","./src/component-library/components/button/index.ts","./src/component-library/components/card/card.tsx","./src/component-library/components/card/index.ts","./src/component-library/components/checkbox/checkbox.tsx","./src/component-library/components/checkbox/index.ts","./src/component-library/components/codeeditor/codeeditor.tsx","./src/component-library/components/codeeditor/index.ts","./src/component-library/components/configpage/configpageloading.tsx","./src/component-library/components/configpage/configpagemessage.tsx","./src/component-library/components/configpage/configpagerefreshbutton.tsx","./src/component-library/components/configpage/index.ts","./src/component-library/components/confirmdialog/confirmdialog.tsx","./src/component-library/components/confirmdialog/confirmdialogrenderer.tsx","./src/component-library/components/confirmdialog/confirmservice.ts","./src/component-library/components/confirmdialog/index.ts","./src/component-library/components/cubeloading/cubeloading.tsx","./src/component-library/components/cubeloading/index.ts","./src/component-library/components/cubelogo/cubelogo.tsx","./src/component-library/components/cubelogo/index.ts","./src/component-library/components/dotmatrixloader/dotmatrixloader.tsx","./src/component-library/components/dotmatrixloader/index.ts","./src/component-library/components/empty/empty.tsx","./src/component-library/components/empty/index.ts","./src/component-library/components/filterpill/filterpill.tsx","./src/component-library/components/filterpill/index.ts","./src/component-library/components/flowchatcards/index.ts","./src/component-library/components/flowchatcards/basetoolcard/basetoolcard.tsx","./src/component-library/components/flowchatcards/basetoolcard/index.ts","./src/component-library/components/flowchatcards/contextcompressioncard/contextcompressioncard.tsx","./src/component-library/components/flowchatcards/contextcompressioncard/index.ts","./src/component-library/components/flowchatcards/searchcard/searchcard.tsx","./src/component-library/components/flowchatcards/searchcard/index.ts","./src/component-library/components/flowchatcards/snapshotcard/snapshotcard.tsx","./src/component-library/components/flowchatcards/snapshotcard/index.ts","./src/component-library/components/flowchatcards/taskcard/taskcard.tsx","./src/component-library/components/flowchatcards/taskcard/index.ts","./src/component-library/components/flowchatcards/todocard/todocard.tsx","./src/component-library/components/flowchatcards/todocard/index.ts","./src/component-library/components/flowchatcards/websearchcard/websearchcard.tsx","./src/component-library/components/flowchatcards/websearchcard/index.ts","./src/component-library/components/iconbutton/iconbutton.tsx","./src/component-library/components/iconbutton/index.ts","./src/component-library/components/input/input.tsx","./src/component-library/components/input/index.ts","./src/component-library/components/inputdialog/inputdialog.tsx","./src/component-library/components/inputdialog/index.ts","./src/component-library/components/markdown/markdown.tsx","./src/component-library/components/markdown/mermaidblock.tsx","./src/component-library/components/markdown/reproductionstepsblock.tsx","./src/component-library/components/markdown/index.ts","./src/component-library/components/modal/modal.tsx","./src/component-library/components/modal/index.ts","./src/component-library/components/numberinput/numberinput.tsx","./src/component-library/components/numberinput/index.ts","./src/component-library/components/search/search.tsx","./src/component-library/components/search/index.ts","./src/component-library/components/select/select.tsx","./src/component-library/components/select/index.ts","./src/component-library/components/streamtext/streamtext.tsx","./src/component-library/components/streamtext/index.ts","./src/component-library/components/switch/switch.tsx","./src/component-library/components/switch/index.ts","./src/component-library/components/tabs/tabs.tsx","./src/component-library/components/tabs/index.ts","./src/component-library/components/tag/tag.tsx","./src/component-library/components/tag/index.ts","./src/component-library/components/textstrokeeffect/textstrokeeffect.tsx","./src/component-library/components/textstrokeeffect/index.ts","./src/component-library/components/textarea/textarea.tsx","./src/component-library/components/textarea/index.ts","./src/component-library/components/tooltip/tooltip.tsx","./src/component-library/components/tooltip/index.ts","./src/component-library/components/windowcontrols/windowcontrols.tsx","./src/component-library/components/windowcontrols/index.ts","./src/component-library/types/index.ts","./src/features/ssh-remote/confirmdialog.tsx","./src/features/ssh-remote/remotefilebrowser.tsx","./src/features/ssh-remote/sshauthpromptdialog.tsx","./src/features/ssh-remote/sshconnectiondialog.tsx","./src/features/ssh-remote/sshremotecontext.ts","./src/features/ssh-remote/sshremoteprovider.tsx","./src/features/ssh-remote/index.ts","./src/features/ssh-remote/picksshprivatekeypath.ts","./src/features/ssh-remote/sshapi.ts","./src/features/ssh-remote/types.ts","./src/features/ssh-remote/usesshremote.ts","./src/flow_chat/index.ts","./src/flow_chat/components/chatemptystate.tsx","./src/flow_chat/components/chatinput.tsx","./src/flow_chat/components/chatinputpixelpet.tsx","./src/flow_chat/components/codepreview.tsx","./src/flow_chat/components/copyoutputbutton.tsx","./src/flow_chat/components/coworkexamplecards.tsx","./src/flow_chat/components/currentsessiontitle.tsx","./src/flow_chat/components/filementionpicker.tsx","./src/flow_chat/components/flowitemrenderer.tsx","./src/flow_chat/components/flowtextblock.tsx","./src/flow_chat/components/flowtoolcard.tsx","./src/flow_chat/components/flowtoolcarderrorboundary.tsx","./src/flow_chat/components/inlinediffpreview.tsx","./src/flow_chat/components/modelselector.tsx","./src/flow_chat/components/richtextinput.tsx","./src/flow_chat/components/scrolltobottombutton.tsx","./src/flow_chat/components/scrolltolatestbar.tsx","./src/flow_chat/components/snapshotrollbackbutton.tsx","./src/flow_chat/components/tokenusageindicator.tsx","./src/flow_chat/components/toolstatusindicator.tsx","./src/flow_chat/components/turnhistorypanel.tsx","./src/flow_chat/components/turnrollbackbutton.tsx","./src/flow_chat/components/usermessage.tsx","./src/flow_chat/components/welcomepanel.tsx","./src/flow_chat/components/codepreviewprismtheme.ts","./src/flow_chat/components/index.ts","./src/flow_chat/components/richtextinputsync.ts","./src/flow_chat/components/taskdetailpanel/taskdetailpanel.tsx","./src/flow_chat/components/taskdetailpanel/index.ts","./src/flow_chat/components/btw/btwsessionpanel.tsx","./src/flow_chat/components/modern/exploregrouprenderer.tsx","./src/flow_chat/components/modern/exportimagebutton.tsx","./src/flow_chat/components/modern/flowchatcontext.tsx","./src/flow_chat/components/modern/flowchatheader.tsx","./src/flow_chat/components/modern/modelrounditem.tsx","./src/flow_chat/components/modern/modernflowchatcontainer.tsx","./src/flow_chat/components/modern/processingindicator.tsx","./src/flow_chat/components/modern/scrollanchor.tsx","./src/flow_chat/components/modern/sessionfilemodificationsbar.tsx","./src/flow_chat/components/modern/sessionfilesbadge.tsx","./src/flow_chat/components/modern/usermessageitem.tsx","./src/flow_chat/components/modern/virtualitemrenderer.tsx","./src/flow_chat/components/modern/virtualmessagelist.tsx","./src/flow_chat/components/modern/index.ts","./src/flow_chat/components/modern/useexploregroupstate.ts","./src/flow_chat/components/modern/useflowchatcopydialog.ts","./src/flow_chat/components/modern/useflowchatfileactions.ts","./src/flow_chat/components/modern/useflowchatfollowoutput.ts","./src/flow_chat/components/modern/useflowchatnavigation.ts","./src/flow_chat/components/modern/useflowchatsessionrelationship.ts","./src/flow_chat/components/modern/useflowchatsync.ts","./src/flow_chat/components/modern/useflowchattoolactions.ts","./src/flow_chat/components/smart-recommendations/recommendationregistry.ts","./src/flow_chat/components/smart-recommendations/smartrecommendations.tsx","./src/flow_chat/components/smart-recommendations/index.ts","./src/flow_chat/components/smart-recommendations/initproviders.ts","./src/flow_chat/components/smart-recommendations/types.ts","./src/flow_chat/components/toolbar-mode/toolbarmode.tsx","./src/flow_chat/components/toolbar-mode/toolbarmodecontext.ts","./src/flow_chat/components/toolbar-mode/toolbarmodeprovider.tsx","./src/flow_chat/components/toolbar-mode/index.ts","./src/flow_chat/constants/chatinputconfig.ts","./src/flow_chat/constants/mermaidexamples.ts","./src/flow_chat/constants/processinghints.ts","./src/flow_chat/events/flowchatnavigation.ts","./src/flow_chat/hooks/index.ts","./src/flow_chat/hooks/useactivesessionstate.ts","./src/flow_chat/hooks/useautoscroll.ts","./src/flow_chat/hooks/usecopydialog.ts","./src/flow_chat/hooks/useflowchat.ts","./src/flow_chat/hooks/usemessagesender.ts","./src/flow_chat/hooks/usesessionstatemachine.ts","./src/flow_chat/hooks/usetypewriter.ts","./src/flow_chat/reducers/inputreducer.ts","./src/flow_chat/reducers/modereducer.ts","./src/flow_chat/services/agenticeventlistener.ts","./src/flow_chat/services/btwthreadservice.ts","./src/flow_chat/services/eventbatcher.ts","./src/flow_chat/services/flowchatmanager.ts","./src/flow_chat/services/processingstatusmanager.ts","./src/flow_chat/services/openbtwsession.ts","./src/flow_chat/services/storesync.ts","./src/flow_chat/services/flow-chat-manager/eventhandlermodule.ts","./src/flow_chat/services/flow-chat-manager/imageanalysismodule.ts","./src/flow_chat/services/flow-chat-manager/messagemodule.ts","./src/flow_chat/services/flow-chat-manager/persistencemodule.ts","./src/flow_chat/services/flow-chat-manager/sessionmodule.ts","./src/flow_chat/services/flow-chat-manager/subagentmodule.ts","./src/flow_chat/services/flow-chat-manager/textchunkmodule.ts","./src/flow_chat/services/flow-chat-manager/tooleventmodule.ts","./src/flow_chat/services/flow-chat-manager/index.ts","./src/flow_chat/services/flow-chat-manager/types.ts","./src/flow_chat/state-machine/sessionstatemachine.ts","./src/flow_chat/state-machine/sessionstatemachinemanager.ts","./src/flow_chat/state-machine/derivedstate.ts","./src/flow_chat/state-machine/index.ts","./src/flow_chat/state-machine/transitions.ts","./src/flow_chat/state-machine/types.ts","./src/flow_chat/store/flowchatstore.ts","./src/flow_chat/store/taskcollapsestatemanager.ts","./src/flow_chat/store/chatinputstatestore.ts","./src/flow_chat/store/inputhistorystore.ts","./src/flow_chat/store/modernflowchatstore.ts","./src/flow_chat/tool-cards/askuserquestioncard.tsx","./src/flow_chat/tool-cards/basetoolcard.tsx","./src/flow_chat/tool-cards/btwmarkercard.tsx","./src/flow_chat/tool-cards/codereviewtoolcard.tsx","./src/flow_chat/tool-cards/compacttoolcard.tsx","./src/flow_chat/tool-cards/contextcompressiondisplay.tsx","./src/flow_chat/tool-cards/createplandisplay.tsx","./src/flow_chat/tool-cards/defaulttoolcard.tsx","./src/flow_chat/tool-cards/fileoperationtoolcard.tsx","./src/flow_chat/tool-cards/generativewidgettoolcard.tsx","./src/flow_chat/tool-cards/getfilediffdisplay.tsx","./src/flow_chat/tool-cards/gittooldisplay.tsx","./src/flow_chat/tool-cards/globsearchdisplay.tsx","./src/flow_chat/tool-cards/grepsearchdisplay.tsx","./src/flow_chat/tool-cards/lsdisplay.tsx","./src/flow_chat/tool-cards/mcptooldisplay.tsx","./src/flow_chat/tool-cards/mermaidinteractivedisplay.tsx","./src/flow_chat/tool-cards/miniapptooldisplay.tsx","./src/flow_chat/tool-cards/modelthinkingdisplay.tsx","./src/flow_chat/tool-cards/readfiledisplay.tsx","./src/flow_chat/tool-cards/sessioncontroltoolcard.tsx","./src/flow_chat/tool-cards/sessionmessagetoolcard.tsx","./src/flow_chat/tool-cards/skilldisplay.tsx","./src/flow_chat/tool-cards/snapshotfullscreendiffviewer.tsx","./src/flow_chat/tool-cards/tasktooldisplay.tsx","./src/flow_chat/tool-cards/terminalcontroldisplay.tsx","./src/flow_chat/tool-cards/terminaltoolcard.tsx","./src/flow_chat/tool-cards/todowritedisplay.tsx","./src/flow_chat/tool-cards/toolcardheaderlayoutcontext.ts","./src/flow_chat/tool-cards/websearchcard.tsx","./src/flow_chat/tool-cards/index.ts","./src/flow_chat/tool-cards/usetoolcardheightcontract.ts","./src/flow_chat/types/flow-chat.ts","./src/flow_chat/types/index.ts","./src/flow_chat/utils/chatinputpetmood.ts","./src/flow_chat/utils/flowchatscrolllayout.ts","./src/flow_chat/utils/imageutils.ts","./src/flow_chat/utils/sessionmetadata.ts","./src/flow_chat/utils/sessionordering.ts","./src/flow_chat/utils/sessionstatusutils.ts","./src/flow_chat/utils/titleutils.ts","./src/flow_chat/utils/workspacescope.ts","./src/generated/version.ts","./src/hooks/index.ts","./src/hooks/usefilesearch.ts","./src/hooks/usemodelconfigs.ts","./src/hooks/useresizer.ts","./src/hooks/usescrollactivity.ts","./src/hooks/usetoolexecution.ts","./src/infrastructure/index.ts","./src/infrastructure/agents/constants.ts","./src/infrastructure/api/aimemoryapi.ts","./src/infrastructure/api/index.ts","./src/infrastructure/api/insightsapi.ts","./src/infrastructure/api/adapters/base.ts","./src/infrastructure/api/adapters/index.ts","./src/infrastructure/api/adapters/tauri-adapter.ts","./src/infrastructure/api/adapters/websocket-adapter.ts","./src/infrastructure/api/errors/tauricommanderror.ts","./src/infrastructure/api/service-api/aiapi.ts","./src/infrastructure/api/service-api/airulesapi.ts","./src/infrastructure/api/service-api/agentapi.ts","./src/infrastructure/api/service-api/apiclient.ts","./src/infrastructure/api/service-api/btwapi.ts","./src/infrastructure/api/service-api/configapi.ts","./src/infrastructure/api/service-api/contextapi.ts","./src/infrastructure/api/service-api/cronapi.ts","./src/infrastructure/api/service-api/diffapi.ts","./src/infrastructure/api/service-api/editoraiapi.ts","./src/infrastructure/api/service-api/gitapi.ts","./src/infrastructure/api/service-api/gitagentapi.ts","./src/infrastructure/api/service-api/gitrepohistoryapi.ts","./src/infrastructure/api/service-api/globalapi.ts","./src/infrastructure/api/service-api/i18napi.ts","./src/infrastructure/api/service-api/imagecontexttypes.ts","./src/infrastructure/api/service-api/mcpapi.ts","./src/infrastructure/api/service-api/miniappapi.ts","./src/infrastructure/api/service-api/projectapi.ts","./src/infrastructure/api/service-api/remoteconnectapi.ts","./src/infrastructure/api/service-api/selfcontrolapi.ts","./src/infrastructure/api/service-api/sessionapi.ts","./src/infrastructure/api/service-api/snapshotapi.ts","./src/infrastructure/api/service-api/startchatagentapi.ts","./src/infrastructure/api/service-api/subagentapi.ts","./src/infrastructure/api/service-api/systemapi.ts","./src/infrastructure/api/service-api/toolapi.ts","./src/infrastructure/api/service-api/workspaceapi.ts","./src/infrastructure/api/service-api/tauri-commands.ts","./src/infrastructure/api/service-api/types.ts","./src/infrastructure/config/index.ts","./src/infrastructure/config/components/aifeaturesconfig.tsx","./src/infrastructure/config/components/aimodelconfig.tsx","./src/infrastructure/config/components/airulesmemoryconfig.tsx","./src/infrastructure/config/components/basicsconfig.tsx","./src/infrastructure/config/components/defaultmodelconfig.tsx","./src/infrastructure/config/components/editorconfig.tsx","./src/infrastructure/config/components/lspconfig.tsx","./src/infrastructure/config/components/mcpresourcebrowser.tsx","./src/infrastructure/config/components/mcptoolsconfig.tsx","./src/infrastructure/config/components/modelselectionradio.tsx","./src/infrastructure/config/components/sessionconfig.tsx","./src/infrastructure/config/components/skillsconfig.tsx","./src/infrastructure/config/components/form-controls.ts","./src/infrastructure/config/components/index.ts","./src/infrastructure/config/components/common/configcollectionitem.tsx","./src/infrastructure/config/components/common/configcollectionsection.tsx","./src/infrastructure/config/components/common/configpageheader.tsx","./src/infrastructure/config/components/common/configpagelayout.tsx","./src/infrastructure/config/components/common/index.ts","./src/infrastructure/config/components/form-controls/configactions.tsx","./src/infrastructure/config/components/form-controls/configcheckbox.tsx","./src/infrastructure/config/components/form-controls/configform.tsx","./src/infrastructure/config/components/form-controls/configinput.tsx","./src/infrastructure/config/components/form-controls/configsection.tsx","./src/infrastructure/config/components/form-controls/configselect.tsx","./src/infrastructure/config/components/form-controls/configstatus.tsx","./src/infrastructure/config/components/form-controls/configtextarea.tsx","./src/infrastructure/config/core/index.ts","./src/infrastructure/config/services/aiexperienceconfigservice.ts","./src/infrastructure/config/services/configmanager.ts","./src/infrastructure/config/services/frontendloglevelsync.ts","./src/infrastructure/config/services/index.ts","./src/infrastructure/config/services/modelconfigs.ts","./src/infrastructure/config/services/providercatalog.ts","./src/infrastructure/config/types/index.ts","./src/infrastructure/config/utils/modelconfighelpers.ts","./src/infrastructure/config/utils/reasoning.ts","./src/infrastructure/contexts/chatcontext.ts","./src/infrastructure/contexts/chatprovider.tsx","./src/infrastructure/contexts/viewmodecontext.ts","./src/infrastructure/contexts/viewmodeprovider.tsx","./src/infrastructure/contexts/workspacecontext.ts","./src/infrastructure/contexts/workspaceprovider.tsx","./src/infrastructure/contexts/index.ts","./src/infrastructure/event-bus/eventbus.ts","./src/infrastructure/event-bus/index.ts","./src/infrastructure/font-preference/index.ts","./src/infrastructure/font-preference/components/fontpreferencepanel.tsx","./src/infrastructure/font-preference/core/fontpreferenceservice.ts","./src/infrastructure/font-preference/hooks/usefontpreference.ts","./src/infrastructure/font-preference/store/fontpreferencestore.ts","./src/infrastructure/font-preference/types/index.ts","./src/infrastructure/hooks/index.ts","./src/infrastructure/hooks/useaiinitialization.ts","./src/infrastructure/hooks/useairules.ts","./src/infrastructure/hooks/useshortcut.ts","./src/infrastructure/hooks/useworkspacemanagersync.ts","./src/infrastructure/i18n/index.ts","./src/infrastructure/i18n/components/languageselector.tsx","./src/infrastructure/i18n/components/index.ts","./src/infrastructure/i18n/core/i18nservice.ts","./src/infrastructure/i18n/hooks/index.ts","./src/infrastructure/i18n/hooks/usei18n.ts","./src/infrastructure/i18n/presets/index.ts","./src/infrastructure/i18n/providers/i18nprovider.tsx","./src/infrastructure/i18n/providers/index.ts","./src/infrastructure/i18n/store/i18nstore.ts","./src/infrastructure/i18n/types/index.ts","./src/infrastructure/language-detection/index.ts","./src/infrastructure/language-detection/core/languagedetector.ts","./src/infrastructure/language-detection/core/languageregistry.ts","./src/infrastructure/language-detection/core/projectdetector.ts","./src/infrastructure/language-detection/plugins/index.ts","./src/infrastructure/language-detection/types/index.ts","./src/infrastructure/language-detection/utils/helpers.ts","./src/infrastructure/mcp/toolname.ts","./src/infrastructure/providers/corecontext.ts","./src/infrastructure/providers/coreprovider.tsx","./src/infrastructure/providers/index.ts","./src/infrastructure/providers/usecore.ts","./src/infrastructure/self-control/selfcontroleventlistener.ts","./src/infrastructure/self-control/selfcontrolservice.ts","./src/infrastructure/self-control/index.ts","./src/infrastructure/services/shortcutmanager.ts","./src/infrastructure/services/index.ts","./src/infrastructure/services/api/aiservice.ts","./src/infrastructure/services/business/agentservice.ts","./src/infrastructure/services/business/workspacemanager.ts","./src/infrastructure/services/business/worktreeworkspaceservice.ts","./src/infrastructure/services/infra/contextmanager.ts","./src/infrastructure/theme/index.ts","./src/infrastructure/theme/core/themeservice.ts","./src/infrastructure/theme/hooks/usetheme.ts","./src/infrastructure/theme/integrations/monacothemesync.ts","./src/infrastructure/theme/presets/china-night-theme.ts","./src/infrastructure/theme/presets/china-style-theme.ts","./src/infrastructure/theme/presets/cyber-theme.ts","./src/infrastructure/theme/presets/dark-theme.ts","./src/infrastructure/theme/presets/index.ts","./src/infrastructure/theme/presets/light-theme.ts","./src/infrastructure/theme/presets/midnight-theme.ts","./src/infrastructure/theme/presets/slate-theme.ts","./src/infrastructure/theme/store/themestore.ts","./src/infrastructure/theme/types/index.ts","./src/infrastructure/theme/utils/themevalidator.ts","./src/shared/index.ts","./src/shared/announcement-system/index.ts","./src/shared/announcement-system/components/announcementprovider.tsx","./src/shared/announcement-system/components/announcementtoastitem.tsx","./src/shared/announcement-system/components/announcementtoaststack.tsx","./src/shared/announcement-system/components/featuremodal.tsx","./src/shared/announcement-system/components/featuremodalpage.tsx","./src/shared/announcement-system/components/mediarenderer.tsx","./src/shared/announcement-system/hooks/useannouncement.ts","./src/shared/announcement-system/hooks/useannouncementi18n.ts","./src/shared/announcement-system/services/announcementservice.ts","./src/shared/announcement-system/store/announcementstore.ts","./src/shared/announcement-system/types/index.ts","./src/shared/constants/app.ts","./src/shared/constants/index.ts","./src/shared/constants/shortcuts.ts","./src/shared/context-menu-system/index.ts","./src/shared/context-menu-system/init.ts","./src/shared/context-menu-system/commands/basecommand.ts","./src/shared/context-menu-system/commands/commandexecutor.ts","./src/shared/context-menu-system/commands/commandregistry.ts","./src/shared/context-menu-system/commands/index.ts","./src/shared/context-menu-system/commands/builtin/copycommand.ts","./src/shared/context-menu-system/commands/builtin/cutcommand.ts","./src/shared/context-menu-system/commands/builtin/pastecommand.ts","./src/shared/context-menu-system/commands/builtin/refreshcommand.ts","./src/shared/context-menu-system/commands/builtin/selectallcommand.ts","./src/shared/context-menu-system/commands/builtin/index.ts","./src/shared/context-menu-system/commands/builtin/file/copypathcommand.ts","./src/shared/context-menu-system/commands/builtin/file/copyrelativepathcommand.ts","./src/shared/context-menu-system/commands/builtin/file/deletecommand.ts","./src/shared/context-menu-system/commands/builtin/file/newfilecommand.ts","./src/shared/context-menu-system/commands/builtin/file/newfoldercommand.ts","./src/shared/context-menu-system/commands/builtin/file/renamecommand.ts","./src/shared/context-menu-system/commands/builtin/file/revealinexplorercommand.ts","./src/shared/context-menu-system/commands/builtin/file/index.ts","./src/shared/context-menu-system/components/contextmenurenderer.tsx","./src/shared/context-menu-system/components/index.ts","./src/shared/context-menu-system/components/ui/contextmenu.tsx","./src/shared/context-menu-system/components/ui/index.ts","./src/shared/context-menu-system/components/ui/types.ts","./src/shared/context-menu-system/config/defaultmenus.ts","./src/shared/context-menu-system/config/index.ts","./src/shared/context-menu-system/config/keybindings.ts","./src/shared/context-menu-system/core/contextmenucontroller.ts","./src/shared/context-menu-system/core/contextmenumanager.ts","./src/shared/context-menu-system/core/contextmenuregistry.ts","./src/shared/context-menu-system/core/contextresolver.ts","./src/shared/context-menu-system/core/menubuilder.ts","./src/shared/context-menu-system/core/index.ts","./src/shared/context-menu-system/providers/editormenuprovider.ts","./src/shared/context-menu-system/providers/fileexplorermenuprovider.ts","./src/shared/context-menu-system/providers/flowchatmenuprovider.ts","./src/shared/context-menu-system/providers/globalmenuprovider.ts","./src/shared/context-menu-system/providers/selectionmenuprovider.ts","./src/shared/context-menu-system/providers/terminalmenuprovider.ts","./src/shared/context-menu-system/providers/index.ts","./src/shared/context-menu-system/store/commandhistorystore.ts","./src/shared/context-menu-system/store/contextmenustore.ts","./src/shared/context-menu-system/types/command.types.ts","./src/shared/context-menu-system/types/context.types.ts","./src/shared/context-menu-system/types/index.ts","./src/shared/context-menu-system/types/menu.types.ts","./src/shared/context-menu-system/types/provider.types.ts","./src/shared/context-menu-system/utils/contextanalyzer.ts","./src/shared/context-menu-system/utils/index.ts","./src/shared/context-menu-system/utils/menubuilder.ts","./src/shared/context-menu-system/utils/menumerger.ts","./src/shared/context-menu-system/utils/positioncalculator.ts","./src/shared/context-system/index.ts","./src/shared/context-system/components/index.ts","./src/shared/context-system/components/contextcard/contextcard.tsx","./src/shared/context-system/components/contextcard/index.ts","./src/shared/context-system/components/contextlist/contextlist.tsx","./src/shared/context-system/components/contextlist/index.ts","./src/shared/context-system/core/index.ts","./src/shared/context-system/core/registerdefaulttypes.ts","./src/shared/context-system/drag-drop/contextdropzone.tsx","./src/shared/context-system/drag-drop/filetreedragsource.ts","./src/shared/context-system/drag-drop/index.ts","./src/shared/helpers/monacohelper.ts","./src/shared/helpers/index.ts","./src/shared/notification-system/index.ts","./src/shared/notification-system/components/loadingnotification.tsx","./src/shared/notification-system/components/notificationcenter.tsx","./src/shared/notification-system/components/notificationcontainer.tsx","./src/shared/notification-system/components/notificationitem.tsx","./src/shared/notification-system/components/progressnotification.tsx","./src/shared/notification-system/hooks/usenotification.ts","./src/shared/notification-system/hooks/usenotificationstate.ts","./src/shared/notification-system/providers/notificationcontextmenuprovider.ts","./src/shared/notification-system/services/notificationservice.ts","./src/shared/notification-system/store/notificationstore.ts","./src/shared/notification-system/types/index.ts","./src/shared/services/contextregistry.ts","./src/shared/services/dragmanager.ts","./src/shared/services/editorjumpservice.ts","./src/shared/services/filetabmanager.ts","./src/shared/services/planbuildstateservice.ts","./src/shared/services/agent-service.ts","./src/shared/services/index.ts","./src/shared/services/openshellsessiontarget.ts","./src/shared/services/pendingtabqueue.ts","./src/shared/services/sceneopentargetresolver.ts","./src/shared/services/tool-execution-service.ts","./src/shared/services/ide-control/idecontroleventbus.ts","./src/shared/services/ide-control/panelcontroller.ts","./src/shared/services/ide-control/api.ts","./src/shared/services/ide-control/index.ts","./src/shared/services/ide-control/types.ts","./src/shared/stores/panelstatemanager.ts","./src/shared/stores/contextstore.ts","./src/shared/types/agent-api.ts","./src/shared/types/base.ts","./src/shared/types/chat.ts","./src/shared/types/code-node.ts","./src/shared/types/context.ts","./src/shared/types/drag.ts","./src/shared/types/global-state.ts","./src/shared/types/index.ts","./src/shared/types/project-view.ts","./src/shared/types/session-history.ts","./src/shared/types/shortcut.ts","./src/shared/types/snapshot.ts","./src/shared/types/tab.ts","./src/shared/types/tool-display.ts","./src/shared/types/tool-events.ts","./src/shared/types/version.ts","./src/shared/utils/aiconnectiontestmessages.ts","./src/shared/utils/cardgradients.ts","./src/shared/utils/chatcontext.ts","./src/shared/utils/configconverter.ts","./src/shared/utils/contextgenerator.ts","./src/shared/utils/debugprobe.ts","./src/shared/utils/eventmanager.ts","./src/shared/utils/format.ts","./src/shared/utils/fserrorutils.ts","./src/shared/utils/index.ts","./src/shared/utils/logger.ts","./src/shared/utils/partialjsonparser.ts","./src/shared/utils/pathcompression.ts","./src/shared/utils/pathutils.ts","./src/shared/utils/reactproductionerror.ts","./src/shared/utils/recentworkspacedisplay.ts","./src/shared/utils/tabutils.ts","./src/shared/utils/textselection.ts","./src/shared/utils/validation.ts","./src/shared/utils/version.ts","./src/tools/index.ts","./src/tools/editor/index.ts","./src/tools/editor/components/codeeditor.tsx","./src/tools/editor/components/diffeditor.tsx","./src/tools/editor/components/editorbreadcrumb.tsx","./src/tools/editor/components/editorstatusbar.tsx","./src/tools/editor/components/imageviewer.tsx","./src/tools/editor/components/markdowneditor.tsx","./src/tools/editor/components/planviewer.tsx","./src/tools/editor/components/index.ts","./src/tools/editor/components/readonlycodeblock/readonlycodeblock.tsx","./src/tools/editor/components/readonlycodeblock/index.ts","./src/tools/editor/components/statusbarpopovers/statusbarpopovers.tsx","./src/tools/editor/components/statusbarpopovers/index.ts","./src/tools/editor/config/defaults.ts","./src/tools/editor/config/index.ts","./src/tools/editor/config/presets.ts","./src/tools/editor/config/types.ts","./src/tools/editor/core/monacodiffcore.tsx","./src/tools/editor/core/monacoeditorcore.tsx","./src/tools/editor/core/index.ts","./src/tools/editor/core/types.ts","./src/tools/editor/extensions/lspextension.ts","./src/tools/editor/extensions/index.ts","./src/tools/editor/extensions/types.ts","./src/tools/editor/hooks/index.ts","./src/tools/editor/hooks/useeditorconfig.ts","./src/tools/editor/hooks/useeditoroptions.ts","./src/tools/editor/hooks/useeditortheme.ts","./src/tools/editor/hooks/uselspintegration.ts","./src/tools/editor/languages/mermaid.language.ts","./src/tools/editor/languages/toml.language.ts","./src/tools/editor/meditor/index.ts","./src/tools/editor/meditor/components/editarea.tsx","./src/tools/editor/meditor/components/inlineaipreviewblock.tsx","./src/tools/editor/meditor/components/inlinemarkdownpreview.tsx","./src/tools/editor/meditor/components/meditor.tsx","./src/tools/editor/meditor/components/preview.tsx","./src/tools/editor/meditor/components/tiptapeditor.tsx","./src/tools/editor/meditor/extensions/blockidextension.ts","./src/tools/editor/meditor/extensions/inlineaipreviewextension.tsx","./src/tools/editor/meditor/extensions/inlineaipreviewpluginkey.ts","./src/tools/editor/meditor/extensions/markdownalignmentextension.ts","./src/tools/editor/meditor/extensions/markdownimageextension.ts","./src/tools/editor/meditor/extensions/markdowntableextensions.ts","./src/tools/editor/meditor/extensions/rawhtmlextensions.ts","./src/tools/editor/meditor/hooks/useeditor.ts","./src/tools/editor/meditor/hooks/useeditorhistory.ts","./src/tools/editor/meditor/types/index.ts","./src/tools/editor/meditor/utils/blockid.ts","./src/tools/editor/meditor/utils/inlineai.ts","./src/tools/editor/meditor/utils/keyboardshortcuts.ts","./src/tools/editor/meditor/utils/loadlocalimages.ts","./src/tools/editor/meditor/utils/markdownblocks.ts","./src/tools/editor/meditor/utils/rehype-local-images.ts","./src/tools/editor/meditor/utils/tiptapmarkdown.ts","./src/tools/editor/services/activeedittargetservice.ts","./src/tools/editor/services/diffservice.ts","./src/tools/editor/services/editorextensionmanager.ts","./src/tools/editor/services/editormanager.ts","./src/tools/editor/services/editoroptionsbuilder.ts","./src/tools/editor/services/editorreadymanager.ts","./src/tools/editor/services/monacoinitmanager.ts","./src/tools/editor/services/monacomodelmanager.ts","./src/tools/editor/services/thememanager.ts","./src/tools/editor/services/index.ts","./src/tools/editor/themes/bitfun-dark.theme.ts","./src/tools/editor/themes/index.ts","./src/tools/editor/types/index.ts","./src/tools/editor/utils/diskfileversion.ts","./src/tools/editor/utils/monacopathhelper.ts","./src/tools/file-explorer/index.ts","./src/tools/file-explorer/controller/explorercontroller.ts","./src/tools/file-explorer/hooks/useexplorercontroller.ts","./src/tools/file-explorer/model/explorermodel.ts","./src/tools/file-explorer/projection/explorerviewprojector.ts","./src/tools/file-explorer/provider/tauriexplorerfilesystemprovider.ts","./src/tools/file-explorer/search/treefilter.ts","./src/tools/file-explorer/search/useexplorersearch.ts","./src/tools/file-explorer/types/explorer.ts","./src/tools/file-system/index.ts","./src/tools/file-system/components/fileexplorer.tsx","./src/tools/file-system/components/filesearchresults.tsx","./src/tools/file-system/components/filetree.tsx","./src/tools/file-system/components/filetreeitem.tsx","./src/tools/file-system/components/filetreenode.tsx","./src/tools/file-system/components/virtualfiletree.tsx","./src/tools/file-system/components/filetreedepth.ts","./src/tools/file-system/components/index.ts","./src/tools/file-system/hooks/index.ts","./src/tools/file-system/hooks/usefilesystem.ts","./src/tools/file-system/hooks/usefiletree.ts","./src/tools/file-system/services/directorycache.ts","./src/tools/file-system/services/filesystemservice.ts","./src/tools/file-system/services/workspacefiletransfer.ts","./src/tools/file-system/types/index.ts","./src/tools/file-system/utils/fileicons.ts","./src/tools/file-system/utils/getnewitemparentpath.ts","./src/tools/file-system/utils/pathcompression.ts","./src/tools/file-system/utils/treeflattening.ts","./src/tools/generative-widget/generativewidgetframe.tsx","./src/tools/generative-widget/generativewidgetpanel.tsx","./src/tools/generative-widget/widgetinteraction.ts","./src/tools/git/index.ts","./src/tools/git/components/index.ts","./src/tools/git/components/createbranchdialog/createbranchdialog.tsx","./src/tools/git/components/createbranchdialog/index.ts","./src/tools/git/components/gitbranchhistoryview/gitbranchhistoryview.tsx","./src/tools/git/components/gitbranchhistoryview/index.ts","./src/tools/git/components/gitdiffeditor/gitdiffeditor.tsx","./src/tools/git/components/gitdiffeditor/index.ts","./src/tools/git/components/gitdiffview/gitdiffview.tsx","./src/tools/git/components/gitdiffview/index.ts","./src/tools/git/components/gitgraphview/gitgraphview.tsx","./src/tools/git/components/gitgraphview/index.ts","./src/tools/git/components/gitsettingsview/gitsettingsview.tsx","./src/tools/git/components/gitsettingsview/index.ts","./src/tools/git/components/pushbutton/pushbutton.tsx","./src/tools/git/components/pushbutton/index.ts","./src/tools/git/hooks/index.ts","./src/tools/git/hooks/usegitadvanced.ts","./src/tools/git/hooks/usegitagent.ts","./src/tools/git/hooks/usegitoperations.ts","./src/tools/git/hooks/usegitstate.ts","./src/tools/git/services/giteventservice.ts","./src/tools/git/services/gitservice.ts","./src/tools/git/services/workspacegitinitializer.ts","./src/tools/git/services/gitdiffservice.ts","./src/tools/git/services/index.ts","./src/tools/git/state/gitstatemanager.ts","./src/tools/git/state/index.ts","./src/tools/git/state/types.ts","./src/tools/git/types/events.ts","./src/tools/git/types/git-agent.types.ts","./src/tools/git/types/graph.ts","./src/tools/git/types/index.ts","./src/tools/git/types/operations.ts","./src/tools/git/types/repository.ts","./src/tools/lsp/index.ts","./src/tools/lsp/components/lsppluginlist/lsppluginlist.tsx","./src/tools/lsp/components/referencespanel/referencespanel.tsx","./src/tools/lsp/components/referencespanel/index.ts","./src/tools/lsp/hooks/uselsp.ts","./src/tools/lsp/hooks/usemonacolsp.ts","./src/tools/lsp/services/hoverpositioncalculator.ts","./src/tools/lsp/services/lspadaptermanager.ts","./src/tools/lsp/services/lspconfigservice.ts","./src/tools/lsp/services/lspdiagnostics.ts","./src/tools/lsp/services/lspdocumentservice.ts","./src/tools/lsp/services/lspextensionregistry.ts","./src/tools/lsp/services/lsprefreshmanager.ts","./src/tools/lsp/services/lspservice.ts","./src/tools/lsp/services/monacolspadapter.ts","./src/tools/lsp/services/monacolspbridge.ts","./src/tools/lsp/services/workspacelspinitializer.ts","./src/tools/lsp/services/workspacelspmanager.ts","./src/tools/lsp/types/index.ts","./src/tools/mermaid-editor/index.ts","./src/tools/mermaid-editor/components/floatingtoolbar.tsx","./src/tools/mermaid-editor/components/mermaidcomponentlibrary.tsx","./src/tools/mermaid-editor/components/mermaideditor.tsx","./src/tools/mermaid-editor/components/mermaideditorheader.tsx","./src/tools/mermaid-editor/components/mermaiderrorboundary.tsx","./src/tools/mermaid-editor/components/mermaidpanel.tsx","./src/tools/mermaid-editor/components/mermaidpreview.tsx","./src/tools/mermaid-editor/components/mermaidsourceeditor.tsx","./src/tools/mermaid-editor/components/mermaidsyntaxhighlighter.tsx","./src/tools/mermaid-editor/components/index.ts","./src/tools/mermaid-editor/data/components.ts","./src/tools/mermaid-editor/hooks/index.ts","./src/tools/mermaid-editor/hooks/usemermaideditor.ts","./src/tools/mermaid-editor/hooks/usepanzoom.ts","./src/tools/mermaid-editor/hooks/usesvginteraction.ts","./src/tools/mermaid-editor/services/mermaidservice.ts","./src/tools/mermaid-editor/theme/index.ts","./src/tools/mermaid-editor/theme/mermaidtheme.ts","./src/tools/mermaid-editor/tools/mermaidinteractivetool.ts","./src/tools/mermaid-editor/types/mermaidpaneltypes.ts","./src/tools/mermaid-editor/types/index.ts","./src/tools/mermaid-editor/utils/mermaidhighlight.ts","./src/tools/mermaid-editor/utils/templates.ts","./src/tools/snapshot_system/index.ts","./src/tools/snapshot_system/components/index.ts","./src/tools/snapshot_system/core/diffdisplayengine.ts","./src/tools/snapshot_system/core/snapshoteventbus.ts","./src/tools/snapshot_system/core/snapshotinitializer.ts","./src/tools/snapshot_system/core/snapshotlazyloader.ts","./src/tools/snapshot_system/core/snapshotstatemanager.ts","./src/tools/snapshot_system/core/index.ts","./src/tools/snapshot_system/hooks/index.ts","./src/tools/snapshot_system/hooks/usesnapshotstate.ts","./src/tools/snapshot_system/services/snapshotsystemservice.ts","./src/tools/snapshot_system/services/index.ts","./src/tools/snapshot_system/types/index.ts","./src/tools/terminal/index.ts","./src/tools/terminal/components/connectedterminal.tsx","./src/tools/terminal/components/terminal.tsx","./src/tools/terminal/components/terminaloutputrenderer.tsx","./src/tools/terminal/components/index.ts","./src/tools/terminal/hooks/index.ts","./src/tools/terminal/hooks/useterminal.ts","./src/tools/terminal/services/terminalactionmanager.ts","./src/tools/terminal/services/terminalservice.ts","./src/tools/terminal/services/index.ts","./src/tools/terminal/services/manualterminalprofileservice.ts","./src/tools/terminal/types/index.ts","./src/tools/terminal/types/session.ts","./src/tools/terminal/utils/terminalresizedebouncer.ts","./src/tools/terminal/utils/index.ts","./src/tools/terminal/utils/xtermtheme.ts","./src/tools/workspace/index.ts","./src/tools/workspace/components/workspacemanager.tsx","./src/tools/workspace/types/index.ts"],"errors":true,"version":"5.8.3"} \ No newline at end of file