From 404be2250c529ac2b4a3bf035a0ca1fc2fed00a7 Mon Sep 17 00:00:00 2001
From: zerob13
Date: Wed, 27 May 2026 11:31:01 +0800
Subject: [PATCH 01/12] docs(skills): add data import skill
---
.agents/skills/deepchat-data-import/SKILL.md | 51 ++++
.../deepchat-data-import/agents/openai.yaml | 4 +
.../references/data-locations.md | 87 +++++++
.../references/import-recipes.md | 153 ++++++++++++
.../references/schema-reference.md | 225 ++++++++++++++++++
.../references/sqlite-access.md | 130 ++++++++++
.../deepchat-data-import-skill/plan.md | 35 +++
.../deepchat-data-import-skill/spec.md | 38 +++
.../deepchat-data-import-skill/tasks.md | 9 +
9 files changed, 732 insertions(+)
create mode 100644 .agents/skills/deepchat-data-import/SKILL.md
create mode 100644 .agents/skills/deepchat-data-import/agents/openai.yaml
create mode 100644 .agents/skills/deepchat-data-import/references/data-locations.md
create mode 100644 .agents/skills/deepchat-data-import/references/import-recipes.md
create mode 100644 .agents/skills/deepchat-data-import/references/schema-reference.md
create mode 100644 .agents/skills/deepchat-data-import/references/sqlite-access.md
create mode 100644 docs/features/deepchat-data-import-skill/plan.md
create mode 100644 docs/features/deepchat-data-import-skill/spec.md
create mode 100644 docs/features/deepchat-data-import-skill/tasks.md
diff --git a/.agents/skills/deepchat-data-import/SKILL.md b/.agents/skills/deepchat-data-import/SKILL.md
new file mode 100644
index 000000000..aff6d361c
--- /dev/null
+++ b/.agents/skills/deepchat-data-import/SKILL.md
@@ -0,0 +1,51 @@
+---
+name: deepchat-data-import
+description: Help developers build third-party tools that import, inspect, migrate, or analyze DeepChat data. Use when Codex needs to work with DeepChat provider configuration, model configuration, MCP/app settings, sessions, messages, legacy chat data, `agent.db`, `chat.db`, SQLCipher encrypted SQLite, Electron safeStorage wrapped passwords, Tauri importers, or native macOS/Windows/Linux data access.
+---
+
+# DeepChat Data Import
+
+## Overview
+
+Use this skill to design or implement importers for DeepChat local data. Treat DeepChat's SQLite
+schema as an internal but documentable contract: inspect the current schema when precision matters,
+prefer read-only snapshots, and avoid writing to a live profile.
+
+## Workflow
+
+1. Identify the source: live DeepChat profile, copied profile, sync backup, exported `agent.db`, or
+ legacy `chat.db`.
+2. Read [references/data-locations.md](references/data-locations.md) to locate `agent.db`,
+ sidecar files, encryption metadata, and backup paths.
+3. Read [references/sqlite-access.md](references/sqlite-access.md) before opening SQLite. Decide
+ whether the database is unencrypted, can be unlocked through Electron safeStorage, or must ask
+ the user for the SQLite password.
+4. Read [references/schema-reference.md](references/schema-reference.md) for provider config,
+ settings, session, message, and legacy table relationships.
+5. Read [references/import-recipes.md](references/import-recipes.md) when writing extractor code,
+ mapping DeepChat data to another app, or creating a compatibility import.
+
+## Safety Rules
+
+- Get explicit user consent before reading local DeepChat data. Provider keys, OAuth tokens, MCP
+ env vars, prompt text, message traces, and chat content may be sensitive.
+- Never open the active `agent.db` read-write from a third-party tool. Copy `agent.db`,
+ `agent.db-wal`, and `agent.db-shm`, or use SQLite backup APIs through DeepChat itself.
+- If DeepChat is running, either ask the user to quit it or make a WAL-aware snapshot before import.
+- Prefer parameterized key APIs for SQLCipher passwords. Do not interpolate passwords into SQL.
+- Redact secrets by default in logs, telemetry, previews, and generated sample output.
+- When the schema has changed, inspect `schema_versions`, `sqlite_master`, and the table classes
+ under `src/main/presenter/sqlitePresenter/tables/` before assuming column availability.
+
+## Source Files
+
+Use these repository files as the current source of truth when updating the skill or answering
+version-sensitive questions:
+
+- `src/main/presenter/sqlitePresenter/index.ts`
+- `src/main/presenter/sqlitePresenter/connectionConfig.ts`
+- `src/main/presenter/databaseSecurityPresenter/index.ts`
+- `src/main/presenter/sqlitePresenter/tables/*.ts`
+- `src/main/presenter/agentRuntimePresenter/messageStore.ts`
+- `src/main/presenter/agentRuntimePresenter/sessionStore.ts`
+- `src/main/presenter/configPresenter/**`
diff --git a/.agents/skills/deepchat-data-import/agents/openai.yaml b/.agents/skills/deepchat-data-import/agents/openai.yaml
new file mode 100644
index 000000000..c172f4ad3
--- /dev/null
+++ b/.agents/skills/deepchat-data-import/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "DeepChat Data Import"
+ short_description: "Import DeepChat config and chat data"
+ default_prompt: "Use $deepchat-data-import to help a third-party tool import DeepChat provider config, sessions, messages, and encrypted or unencrypted agent.db data."
diff --git a/.agents/skills/deepchat-data-import/references/data-locations.md b/.agents/skills/deepchat-data-import/references/data-locations.md
new file mode 100644
index 000000000..bd30197d2
--- /dev/null
+++ b/.agents/skills/deepchat-data-import/references/data-locations.md
@@ -0,0 +1,87 @@
+# Data Locations
+
+Use this reference when an importer must locate DeepChat data on disk or inside a backup.
+
+## Primary Files
+
+DeepChat stores the current main database at:
+
+```text
+/app_db/agent.db
+/app_db/agent.db-wal
+/app_db/agent.db-shm
+```
+
+The legacy database, when present, is:
+
+```text
+/app_db/chat.db
+```
+
+Database encryption metadata is outside SQLite:
+
+```text
+/database-security.json
+```
+
+The file is an ElectronStore JSON file. The relevant shape is:
+
+```json
+{
+ "metadata": {
+ "version": 1,
+ "enabled": true,
+ "cipher": "sqlcipher",
+ "passwordStorage": "safeStorage",
+ "wrappedPassword": "base64-electron-safeStorage-blob",
+ "safeStorageBackend": "basic_text",
+ "lastMigrationAt": 1770000000000,
+ "lastMigrationDirection": "enable"
+ }
+}
+```
+
+`passwordStorage` can be `safeStorage`, `manual`, or `none`. If `enabled` is false, open
+`agent.db` as normal SQLite.
+
+## Default UserData Paths
+
+Electron derives the profile path from the packaged product name `DeepChat` unless the runtime
+overrides `app.getPath('userData')`.
+
+```text
+macOS: ~/Library/Application Support/DeepChat
+Windows: %APPDATA%\DeepChat
+Linux: ~/.config/DeepChat
+```
+
+Treat these as defaults. Portable builds, development builds, tests, or user overrides can point
+elsewhere.
+
+## Sync Backup Layout
+
+DeepChat sync backups use `database/agent.db` as the primary database payload in current backup
+versions. Some compatibility backups may also contain `database/chat.db` or old JSON settings. When
+both `agent.db` and `chat.db` exist, prefer `agent.db`.
+
+## Snapshot Rules
+
+- If DeepChat is running, copy `agent.db`, `agent.db-wal`, and `agent.db-shm` together.
+- If DeepChat is closed, `agent.db` alone is usually enough, but copying sidecars is still harmless.
+- For high-integrity import, open a read-only source connection and run a SQLite backup into a temp
+ file, then import from the temp file.
+- Do not delete `*.migration-tmp`, `*.migration-rollback`, `agent.db-wal`, or `agent.db-shm` from a
+ user profile. DeepChat owns those lifecycle decisions.
+
+## Related Files Still Outside Agent.db
+
+Most sensitive configuration has moved into SQLite. Some lightweight or compatibility JSON files may
+still exist in userData, including:
+
+- `app-settings.json`
+- `custom_prompts.json`
+- `system_prompts.json`
+- `mcp-settings.json`
+
+Prefer SQLite tables for current provider, MCP, app setting, prompt, and knowledge config imports.
+Use JSON files only as legacy fallback.
diff --git a/.agents/skills/deepchat-data-import/references/import-recipes.md b/.agents/skills/deepchat-data-import/references/import-recipes.md
new file mode 100644
index 000000000..6854ebaf4
--- /dev/null
+++ b/.agents/skills/deepchat-data-import/references/import-recipes.md
@@ -0,0 +1,153 @@
+# Import Recipes
+
+Use this reference when turning DeepChat data into an importer or migration tool.
+
+## Minimal Read-Only Export
+
+1. Locate and snapshot `agent.db`.
+2. Open it with the flow in `sqlite-access.md`.
+3. Read provider config from `providers`, `provider_models`, `model_status`, and `model_configs`.
+4. Read sessions with:
+
+```sql
+SELECT ns.*, ds.*
+FROM new_sessions ns
+LEFT JOIN deepchat_sessions ds ON ds.id = ns.id
+WHERE ns.session_kind = 'regular'
+ORDER BY ns.updated_at DESC, ns.id DESC;
+```
+
+5. Read messages per session:
+
+```sql
+SELECT *
+FROM deepchat_messages
+WHERE session_id = ?
+ORDER BY order_seq ASC, id ASC;
+```
+
+6. Hydrate user messages from `deepchat_user_messages`, `deepchat_user_message_files`, and
+ `deepchat_user_message_links`.
+7. Hydrate assistant messages from `deepchat_assistant_blocks`; fall back to parsing
+ `deepchat_messages.content`.
+8. Export to the target app's format, redacting secrets unless the user explicitly chooses to
+ include them.
+
+## Provider Config Import
+
+When importing providers into another tool, preserve:
+
+- provider id, name, API type, base URL, enabled flag, custom flag.
+- API key and OAuth token only with explicit user consent.
+- capability provider id for model capability lookup.
+- model rows from `provider_models`, split by `source`.
+- enabled/disabled state from `model_status`.
+- model config from `model_configs.config_json`.
+
+Do not rely only on `provider_json`; DeepChat deliberately stores common scalar fields in columns
+for queryability and migration.
+
+## Session And Message Import
+
+Recommended target shape:
+
+```json
+{
+ "session": {
+ "id": "session-id",
+ "title": "Session title",
+ "agentId": "deepchat",
+ "projectDir": "/path/to/project",
+ "providerId": "openai",
+ "modelId": "gpt-4.1",
+ "createdAt": 1770000000000,
+ "updatedAt": 1770000000000
+ },
+ "messages": [
+ {
+ "id": "message-id",
+ "orderSeq": 1,
+ "role": "user",
+ "status": "sent",
+ "content": {
+ "text": "hello",
+ "files": [],
+ "links": [],
+ "search": false,
+ "think": false
+ },
+ "metadata": {},
+ "createdAt": 1770000000000,
+ "updatedAt": 1770000000000
+ }
+ ]
+}
+```
+
+For assistant messages, keep the assistant block array when possible instead of flattening to text.
+Tool calls, tool responses, reasoning blocks, image data, action prompts, and error blocks may all
+be represented as assistant blocks.
+
+## Handling Partial Or Old Rows
+
+- If structured user rows are missing, parse `deepchat_messages.content`.
+- If structured assistant blocks are missing, parse `deepchat_messages.content`.
+- If `new_sessions` is missing but `conversations` exists, import through the legacy path.
+- If `agent.db` is missing and `chat.db` exists, open `chat.db` as legacy data.
+- If a column is missing, check `schema_versions` and use the nearest fallback rather than failing
+ the whole import.
+
+## Writing Back Into DeepChat
+
+Avoid third-party direct writes to a user's live DeepChat database.
+
+If a tool must generate data for DeepChat:
+
+- Prefer creating an export file or backup package that DeepChat can import through its own code.
+- If implementing inside DeepChat, use Presenter/table helpers instead of raw SQL.
+- If writing a copied database for controlled migration tests, use one transaction per session and
+ keep table groups consistent:
+ - `new_sessions`
+ - `deepchat_sessions`
+ - `deepchat_messages`
+ - structured user or assistant tables
+ - optional search, trace, usage, pending input, and tape rows
+- Keep `deepchat_messages.content` compatible even when structured tables are populated, because it
+ remains the fallback path.
+- Do not update `database-security.json` manually after rekeying; use DeepChat's migration flow.
+
+## Secret Handling Checklist
+
+Redact or require explicit opt-in for:
+
+- `providers.api_key`
+- OAuth tokens in `providers.provider_json`
+- MCP server `env` and custom headers
+- `app_settings` rows marked `sensitive = 1`
+- `deepchat_message_traces.headers_json` and `body_json`
+- file paths in user message files
+- chat content, system prompts, summaries, and project paths
+
+## Useful Consistency Checks
+
+Run these after import from a copied database:
+
+```sql
+PRAGMA quick_check;
+
+SELECT COUNT(*) FROM new_sessions;
+SELECT COUNT(*) FROM deepchat_sessions;
+SELECT COUNT(*) FROM deepchat_messages;
+
+SELECT m.session_id
+FROM deepchat_messages m
+LEFT JOIN new_sessions s ON s.id = m.session_id
+WHERE s.id IS NULL
+LIMIT 20;
+```
+
+For encrypted databases, validate the password before any import work:
+
+```sql
+SELECT name FROM sqlite_master LIMIT 1;
+```
diff --git a/.agents/skills/deepchat-data-import/references/schema-reference.md b/.agents/skills/deepchat-data-import/references/schema-reference.md
new file mode 100644
index 000000000..73222c092
--- /dev/null
+++ b/.agents/skills/deepchat-data-import/references/schema-reference.md
@@ -0,0 +1,225 @@
+# Schema Reference
+
+Use this reference when extracting provider config, settings, sessions, and messages from
+`agent.db`. Verify against `sqlite_master` for user databases created by newer DeepChat versions.
+
+## Core Version Tables
+
+- `schema_versions`: applied migration versions. Read `MAX(version)` to understand how far the DB
+ has migrated.
+- `config_migrations`: config storage migrations, including the SQLite config migration marker.
+
+## Provider And Config Tables
+
+### providers
+
+Primary provider rows.
+
+Important columns:
+
+- `id`: provider id.
+- `name`: display name.
+- `api_type`: provider API adapter type.
+- `api_key`: sensitive API key. Redact by default.
+- `base_url`: configured endpoint.
+- `enabled`: `1` when provider is enabled.
+- `custom`: `1` for custom providers.
+- `capability_provider_id`: catalog provider used for capabilities, nullable.
+- `sort_order`, `last_used_at`, `created_at`, `updated_at`: ordering and timestamps.
+- `provider_json`: JSON for the rest of `LLM_PROVIDER`, excluding model arrays and enabled/disabled
+ model lists.
+
+To reconstruct a provider object, parse `provider_json`, then overlay scalar columns:
+
+```ts
+{
+ ...JSON.parse(row.provider_json || '{}'),
+ id: row.id,
+ name: row.name,
+ apiType: row.api_type,
+ apiKey: row.api_key,
+ baseUrl: row.base_url,
+ enable: row.enabled === 1,
+ custom: row.custom === 1,
+ capabilityProviderId: row.capability_provider_id
+}
+```
+
+### provider_models
+
+Provider and custom model catalog rows.
+
+- Primary key: `(provider_id, model_id, source)`.
+- `source`: `provider` or `custom`.
+- `model_json`: JSON for `MODEL_META`; overlay `model_id`, `provider_id`, `name`, `group_name`,
+ and `isCustom`.
+
+### model_status
+
+Per-model enabled state. `status_key` is the primary key; rows also include `provider_id`,
+`model_id`, `enabled`, and `updated_at`.
+
+### model_configs
+
+Per-model generation config.
+
+- `cache_key`: primary key used by DeepChat config helpers.
+- `provider_id`, `model_id`, `source`: denormalized lookup fields.
+- `config_json`: JSON for model config values such as temperature, context length, reasoning,
+ search, image generation, video generation, or TTS options.
+
+### mcp_servers, mcp_settings, agent_settings, app_settings
+
+- `mcp_servers`: MCP server configs by `name`, with `config_json`, `sort_order`, and timestamps.
+- `mcp_settings`: JSON key/value settings for MCP behavior.
+- `agent_settings`: JSON key/value settings for agent behavior.
+- `app_settings`: JSON key/value settings, with `sensitive` flag. Current sensitive config such as
+ prompts, knowledge config, hooks, remote control, and API-like settings may live here.
+
+`mcp_servers.config_json`, MCP env values, and `app_settings.value_json` can contain secrets.
+
+## Current Session And Message Tables
+
+DeepChat's current mainline session model is split across a thin registry and agent-specific data.
+
+### new_sessions
+
+One row per visible session or subagent session.
+
+Key columns:
+
+- `id`: session id.
+- `agent_id`: agent implementation id. DeepChat chat sessions normally use the DeepChat agent id;
+ ACP sessions use ACP-oriented ids.
+- `title`: sidebar title.
+- `project_dir`: nullable project/workspace path.
+- `is_pinned`, `is_draft`: booleans as integers.
+- `active_skills`, `disabled_agent_tools`: JSON arrays retained for compatibility.
+- `subagent_enabled`: boolean as integer.
+- `session_kind`: `regular` or `subagent`.
+- `parent_session_id`, `subagent_meta_json`: subagent relationship data.
+- `created_at`, `updated_at`: epoch milliseconds.
+
+Related tables:
+
+- `new_projects`: project path, name, optional icon, last access timestamp.
+- `new_session_active_skills`: structured active skill rows.
+- `new_session_disabled_agent_tools`: structured disabled tool rows.
+
+### deepchat_sessions
+
+DeepChat-specific session config. `id` matches `new_sessions.id`.
+
+Important columns:
+
+- `provider_id`, `model_id`: selected model.
+- `permission_mode`: `default` or `full_access`.
+- `system_prompt`, `temperature`, `context_length`, `max_tokens`, `timeout_ms`.
+- `thinking_budget`, `reasoning_effort`, `reasoning_visibility`, `verbosity`.
+- `force_interleaved_thinking_compat`: nullable boolean as integer.
+- `image_generation_options_json`, `video_generation_options_json`: nullable JSON.
+- `summary_text`, `summary_cursor_order_seq`, `summary_updated_at`: compaction summary state.
+
+### deepchat_messages
+
+Message timeline for a session.
+
+- `id`: message id.
+- `session_id`: references the session id.
+- `order_seq`: monotonic ordering within session. Sort ascending for conversation order.
+- `role`: `user` or `assistant`.
+- `content`: JSON string fallback/materialized content.
+- `status`: `pending`, `sent`, or `error`.
+- `is_context_edge`: boolean as integer.
+- `metadata`: JSON string.
+- `created_at`, `updated_at`: epoch milliseconds.
+
+Basic query:
+
+```sql
+SELECT *
+FROM deepchat_messages
+WHERE session_id = ?
+ORDER BY order_seq ASC;
+```
+
+### Structured User Message Tables
+
+Use these first for current rows; fall back to `deepchat_messages.content` if missing.
+
+- `deepchat_user_messages`: `message_id`, `text`, `search_enabled`, `think_enabled`.
+- `deepchat_user_message_files`: `message_id`, `ordinal`, `name`, `path`, `mime_type`, `size`,
+ `metadata_json`.
+- `deepchat_user_message_links`: `message_id`, `ordinal`, `url`.
+
+Materialized user content:
+
+```json
+{
+ "text": "user text",
+ "files": [],
+ "links": [],
+ "search": false,
+ "think": false
+}
+```
+
+### Structured Assistant Blocks
+
+Use `deepchat_assistant_blocks` first for assistant messages. It is especially important for
+pending or recently streamed messages.
+
+Columns:
+
+- `message_id`, `block_index`: primary key.
+- `block_type`, `status`, `text_content`.
+- `tool_call_id`, `tool_name`, `tool_params`, `tool_response`.
+- `action_type`.
+- `image_mime_type`.
+- `reasoning_start_at`, `reasoning_end_at`.
+- `extra_json`: includes block id, timestamp, image data, tool call extras, and reasoning time.
+- `updated_at`.
+
+Sort by `(message_id, block_index)`. If no structured blocks exist, parse
+`deepchat_messages.content` as the fallback assistant block array.
+
+### Event, Search, Trace, And Usage Tables
+
+These are useful for richer import but optional for basic chat history.
+
+- `deepchat_tape_entries`: append-only reconstruction/event facts per session.
+- `deepchat_pending_inputs`: queued or steer-mode pending input payloads.
+- `deepchat_search_documents` and FTS shadow tables: derived search index.
+- `deepchat_message_search_results`: web/search results associated with messages.
+- `deepchat_message_traces`: provider request traces. Treat as highly sensitive.
+- `deepchat_usage_stats`: token/cost usage by message, provider, model, and date.
+
+## Legacy Compatibility Tables
+
+Current DeepChat keeps legacy tables for compatibility and import.
+
+### conversations
+
+Legacy conversation metadata. The business id is `conv_id`; `id` is an autoincrement row id.
+
+Important columns include `title`, `provider_id`, `model_id`, generation settings, search settings,
+`context_chain`, `active_skills`, parent fork fields, `created_at`, and `updated_at`.
+
+### messages
+
+Legacy message timeline.
+
+- `msg_id`: business id.
+- `conversation_id`: references `conversations.conv_id`.
+- `parent_id`: tree/variant parent.
+- `role`: `user`, `assistant`, `system`, or `function`.
+- `content`: message content.
+- `order_seq`: timeline order.
+- `metadata`, `token_count`, `status`, `is_context_edge`, `is_variant`.
+
+### message_attachments
+
+Legacy attachments by `message_id`, `type`, and serialized `content`.
+
+Prefer `new_sessions` and `deepchat_*` for current imports. Use legacy tables or `chat.db` only when
+`agent.db` is missing, an old backup is imported, or the user explicitly wants legacy data.
diff --git a/.agents/skills/deepchat-data-import/references/sqlite-access.md b/.agents/skills/deepchat-data-import/references/sqlite-access.md
new file mode 100644
index 000000000..33a813cb4
--- /dev/null
+++ b/.agents/skills/deepchat-data-import/references/sqlite-access.md
@@ -0,0 +1,130 @@
+# SQLite Access And Encryption
+
+Use this reference before opening `agent.db` or `chat.db`.
+
+## Decision Tree
+
+1. Locate `database-security.json`.
+2. If the file is absent or `metadata.enabled !== true`, open `agent.db` as plain SQLite.
+3. If `metadata.enabled === true`, open with SQLCipher using the DeepChat SQLite password.
+4. If `metadata.passwordStorage === "safeStorage"` and `wrappedPassword` exists, first try an
+ Electron safeStorage helper.
+5. If safeStorage is unavailable, decryption fails, or the importing runtime is not Electron, ask the
+ user for the SQLite password and validate it before reading.
+
+Legacy `chat.db` is normally unencrypted. If a user supplies an encrypted database explicitly, treat
+it with the same SQLCipher path.
+
+## Opening Unencrypted SQLite
+
+Use a read-only connection when possible:
+
+```sql
+SELECT name FROM sqlite_master LIMIT 1;
+PRAGMA quick_check;
+```
+
+If the importer sees `file is not a database`, `SQLITE_NOTADB`, or `SQLITE_CORRUPT` against
+`agent.db`, check `database-security.json` before treating the file as corrupt.
+
+## Opening Encrypted SQLite
+
+DeepChat uses `better-sqlite3-multiple-ciphers` and configures SQLCipher compatibility before
+applying the key:
+
+```ts
+db.pragma("cipher='sqlcipher'")
+db.pragma('legacy=4')
+db.key(Buffer.from(password, 'utf8'))
+```
+
+Then validate with:
+
+```sql
+SELECT name FROM sqlite_master LIMIT 1;
+PRAGMA quick_check;
+```
+
+For other SQLCipher bindings, choose SQLCipher 4 compatible settings that match the binding's
+equivalent of the `legacy=4` mode. Use parameterized or native key APIs when the library supports
+them.
+
+## Electron Importer
+
+An Electron-based third-party importer has the best chance of using DeepChat's wrapped password.
+Read the metadata JSON manually, then try `safeStorage.decryptString`.
+
+```ts
+import { app, safeStorage } from 'electron'
+import fs from 'node:fs'
+import path from 'node:path'
+
+async function readDeepChatPassword(deepChatUserData: string): Promise {
+ await app.whenReady()
+ const metadataPath = path.join(deepChatUserData, 'database-security.json')
+ const raw = JSON.parse(fs.readFileSync(metadataPath, 'utf8')) as {
+ metadata?: {
+ enabled?: boolean
+ passwordStorage?: string
+ wrappedPassword?: string
+ }
+ }
+
+ const metadata = raw.metadata
+ if (!metadata?.enabled) return undefined
+ if (metadata.passwordStorage !== 'safeStorage' || !metadata.wrappedPassword) return null
+ if (!safeStorage.isEncryptionAvailable()) return null
+
+ try {
+ return safeStorage.decryptString(Buffer.from(metadata.wrappedPassword, 'base64'))
+ } catch {
+ return null
+ }
+}
+```
+
+If this returns `null`, fall back to a password prompt. SafeStorage blobs are tied to the user's OS
+security context and Electron's underlying implementation; cross-app or cross-machine decrypt is not
+a stable public contract.
+
+## Tauri Importer
+
+Tauri cannot directly call Electron safeStorage. Prefer this flow:
+
+1. Locate `agent.db` and `database-security.json`.
+2. If unencrypted, open with a normal SQLite crate or plugin.
+3. If encrypted, ask the user for the SQLite password.
+4. Open through a SQLCipher-capable SQLite binding. Standard SQLite drivers will not open encrypted
+ `agent.db`.
+5. Optionally spawn a small Electron helper only for safeStorage unwrap, then pass the password back
+ through a local, user-consented channel.
+
+Use Tauri or OS keyring APIs only to store the importer's own remembered password. Do not assume
+they can unwrap DeepChat's Electron safeStorage blob.
+
+## Native macOS, Windows, And Linux
+
+For unencrypted databases, use the platform's normal SQLite library in read-only mode.
+
+For encrypted databases, use a SQLCipher-capable library and ask the user for the SQLite password
+unless you deliberately ship an Electron helper.
+
+Platform notes:
+
+- macOS: Electron safeStorage depends on Keychain-backed OS crypto. Native Keychain access can be
+ app-permission dependent and should not be treated as a stable DeepChat import API.
+- Windows: Electron safeStorage commonly relies on current-user OS protection. Native DPAPI
+ experiments may work for some blobs, but the blob format and Electron behavior are implementation
+ details. Prefer manual password fallback.
+- Linux: safeStorage may use libsecret, KWallet, or a weaker backend reported as
+ `safeStorageBackend`. If the user's desktop secret service is unavailable, DeepChat stores
+ metadata in manual mode and the importer must ask for the password.
+
+## Validation Errors
+
+- Wrong password usually surfaces as `file is not a database`, `SQLITE_NOTADB`, or a failure reading
+ `sqlite_master`.
+- A missing WAL file can make recent rows disappear from a copied live database. Re-copy sidecars or
+ ask the user to close DeepChat.
+- Do not run rekey or migration operations from an importer. DeepChat's own migration flow copies
+ through an attached temp database and updates metadata only after validation.
diff --git a/docs/features/deepchat-data-import-skill/plan.md b/docs/features/deepchat-data-import-skill/plan.md
new file mode 100644
index 000000000..96481b3a3
--- /dev/null
+++ b/docs/features/deepchat-data-import-skill/plan.md
@@ -0,0 +1,35 @@
+# DeepChat Data Import Skill Plan
+
+## Skill Shape
+
+- Create `.agents/skills/deepchat-data-import/`.
+- Keep `SKILL.md` as the trigger and workflow guide.
+- Put detailed data and encryption documentation in one-level `references/` files:
+ - `data-locations.md`
+ - `sqlite-access.md`
+ - `schema-reference.md`
+ - `import-recipes.md`
+
+## Source Of Truth
+
+Derive the documented schema from:
+
+- `src/main/presenter/sqlitePresenter/index.ts`
+- `src/main/presenter/sqlitePresenter/connectionConfig.ts`
+- `src/main/presenter/databaseSecurityPresenter/index.ts`
+- `src/main/presenter/sqlitePresenter/tables/*.ts`
+- `src/main/presenter/agentRuntimePresenter/messageStore.ts`
+- `src/main/presenter/agentRuntimePresenter/sessionStore.ts`
+- `src/main/presenter/configPresenter/**`
+
+## Compatibility
+
+- Treat `agent.db` as the primary database.
+- Mention legacy `chat.db` and `conversations/messages` only as compatibility paths.
+- Avoid promising that internal table schemas are permanent; tell developers to inspect
+ `schema_versions` and `sqlite_master`.
+
+## Validation
+
+- Run the skill creator quick validator.
+- Run repository formatting, i18n check, and lint after editing.
diff --git a/docs/features/deepchat-data-import-skill/spec.md b/docs/features/deepchat-data-import-skill/spec.md
new file mode 100644
index 000000000..9556249a9
--- /dev/null
+++ b/docs/features/deepchat-data-import-skill/spec.md
@@ -0,0 +1,38 @@
+# DeepChat Data Import Skill Specification
+
+## Goal
+
+Create a repository-local Codex skill that helps third-party developers import DeepChat data safely
+and accurately.
+
+## User Stories
+
+- As an integration developer, I can locate DeepChat's SQLite database and configuration metadata
+ across supported platforms.
+- As an integration developer, I can read provider, model, session, and message data from the
+ current `agent.db` schema.
+- As an integration developer, I can handle both unencrypted SQLite databases and SQLCipher
+ encrypted databases.
+- As an integration developer, I can decide whether to use Electron safeStorage, a user-provided
+ SQLite password, or a platform-native credential path.
+
+## Acceptance Criteria
+
+- The skill is stored under `.agents/skills/` with valid `SKILL.md` frontmatter and
+ `agents/openai.yaml` metadata.
+- The skill links to focused reference files instead of embedding all schema and platform details in
+ `SKILL.md`.
+- The references cover provider config, model config, MCP/app settings, current session/message
+ tables, legacy compatibility tables, and optional database encryption.
+- The security guidance explains Electron, Tauri, and native macOS/Windows/Linux import options.
+- The guidance favors read-only import, user consent, WAL-safe copies, and redaction of secrets.
+
+## Non-Goals
+
+- Do not add runtime import/export code to DeepChat in this change.
+- Do not create sample applications for every framework.
+- Do not define a stable public API beyond documenting the current database contract.
+
+## Open Questions
+
+None.
diff --git a/docs/features/deepchat-data-import-skill/tasks.md b/docs/features/deepchat-data-import-skill/tasks.md
new file mode 100644
index 000000000..ac0f5881b
--- /dev/null
+++ b/docs/features/deepchat-data-import-skill/tasks.md
@@ -0,0 +1,9 @@
+# DeepChat Data Import Skill Tasks
+
+- [x] Inspect current DeepChat schema, config storage, and encryption implementation.
+- [x] Create SDD artifacts for the skill.
+- [x] Initialize `.agents/skills/deepchat-data-import`.
+- [x] Write the skill workflow and focused references.
+- [x] Generate or verify `agents/openai.yaml`.
+- [x] Validate the skill folder.
+- [x] Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`.
From 8e8550b5f4529a675369c522eea1867effd37e8f Mon Sep 17 00:00:00 2001
From: xiaomo
Date: Wed, 27 May 2026 15:00:40 +0800
Subject: [PATCH 02/12] feat(model): add top p generation setting (#1680)
* feat(model): add top p generation setting
* fix(chat): replace top-p inline description with hover tooltip
---------
Co-authored-by: zhangmo8
---
docs/features/model-top-p-settings/plan.md | 38 +
docs/features/model-top-p-settings/spec.md | 36 +
docs/features/model-top-p-settings/tasks.md | 10 +
docs/issues/chat-top-p-tooltip/plan.md | 22 +
docs/issues/chat-top-p-tooltip/spec.md | 28 +
docs/issues/chat-top-p-tooltip/tasks.md | 5 +
.../presenter/agentRuntimePresenter/index.ts | 31 +-
.../presenter/configPresenter/modelConfig.ts | 3 +
.../llmProviderPresenter/aiSdk/runtime.ts | 6 +-
.../sqlitePresenter/schemaCatalog.ts | 5 +-
.../tables/deepchatSessions.ts | 25 +-
.../src/components/chat/ChatStatusBar.vue | 1077 ++++++++++-------
.../components/settings/ModelConfigDialog.vue | 80 +-
src/renderer/src/i18n/da-DK/chat.json | 5 +-
src/renderer/src/i18n/da-DK/settings.json | 7 +-
src/renderer/src/i18n/de-DE/chat.json | 5 +-
src/renderer/src/i18n/de-DE/settings.json | 7 +-
src/renderer/src/i18n/en-US/chat.json | 5 +-
src/renderer/src/i18n/en-US/settings.json | 7 +-
src/renderer/src/i18n/es-ES/chat.json | 5 +-
src/renderer/src/i18n/es-ES/settings.json | 7 +-
src/renderer/src/i18n/fa-IR/chat.json | 5 +-
src/renderer/src/i18n/fa-IR/settings.json | 7 +-
src/renderer/src/i18n/fr-FR/chat.json | 5 +-
src/renderer/src/i18n/fr-FR/settings.json | 7 +-
src/renderer/src/i18n/he-IL/chat.json | 5 +-
src/renderer/src/i18n/he-IL/settings.json | 7 +-
src/renderer/src/i18n/id-ID/chat.json | 5 +-
src/renderer/src/i18n/id-ID/settings.json | 7 +-
src/renderer/src/i18n/it-IT/chat.json | 5 +-
src/renderer/src/i18n/it-IT/settings.json | 7 +-
src/renderer/src/i18n/ja-JP/chat.json | 5 +-
src/renderer/src/i18n/ja-JP/settings.json | 7 +-
src/renderer/src/i18n/ko-KR/chat.json | 5 +-
src/renderer/src/i18n/ko-KR/settings.json | 7 +-
src/renderer/src/i18n/ms-MY/chat.json | 5 +-
src/renderer/src/i18n/ms-MY/settings.json | 7 +-
src/renderer/src/i18n/pl-PL/chat.json | 5 +-
src/renderer/src/i18n/pl-PL/settings.json | 7 +-
src/renderer/src/i18n/pt-BR/chat.json | 5 +-
src/renderer/src/i18n/pt-BR/settings.json | 7 +-
src/renderer/src/i18n/ru-RU/chat.json | 5 +-
src/renderer/src/i18n/ru-RU/settings.json | 7 +-
src/renderer/src/i18n/tr-TR/chat.json | 5 +-
src/renderer/src/i18n/tr-TR/settings.json | 7 +-
src/renderer/src/i18n/vi-VN/chat.json | 5 +-
src/renderer/src/i18n/vi-VN/settings.json | 7 +-
src/renderer/src/i18n/zh-CN/chat.json | 5 +-
src/renderer/src/i18n/zh-CN/settings.json | 7 +-
src/renderer/src/i18n/zh-HK/chat.json | 5 +-
src/renderer/src/i18n/zh-HK/settings.json | 7 +-
src/renderer/src/i18n/zh-TW/chat.json | 5 +-
src/renderer/src/i18n/zh-TW/settings.json | 7 +-
src/renderer/src/pages/NewThreadPage.vue | 1 +
src/renderer/src/stores/ui/draft.ts | 7 +
src/shared/contracts/common.ts | 1 +
src/shared/contracts/domainSchemas.ts | 1 +
src/shared/types/agent-interface.d.ts | 1 +
.../types/presenters/legacy.presenters.d.ts | 1 +
.../utils/generationSettingsValidation.ts | 9 +
src/types/i18n.d.ts | 3 +
.../deepchatSessionsTable.test.ts | 15 +-
.../generationSettingsValidation.test.ts | 17 +
test/renderer/stores/draft.test.ts | 34 +
64 files changed, 1196 insertions(+), 500 deletions(-)
create mode 100644 docs/features/model-top-p-settings/plan.md
create mode 100644 docs/features/model-top-p-settings/spec.md
create mode 100644 docs/features/model-top-p-settings/tasks.md
create mode 100644 docs/issues/chat-top-p-tooltip/plan.md
create mode 100644 docs/issues/chat-top-p-tooltip/spec.md
create mode 100644 docs/issues/chat-top-p-tooltip/tasks.md
create mode 100644 test/renderer/stores/draft.test.ts
diff --git a/docs/features/model-top-p-settings/plan.md b/docs/features/model-top-p-settings/plan.md
new file mode 100644
index 000000000..393230587
--- /dev/null
+++ b/docs/features/model-top-p-settings/plan.md
@@ -0,0 +1,38 @@
+# Implementation Plan
+
+## Approach
+
+Implement `topP` as an optional generation setting, mirroring the existing session generation settings pipeline while avoiding a forced default.
+
+## Affected Interfaces
+
+- `src/shared/types/agent-interface.d.ts`: add optional `topP` to `SessionGenerationSettings`.
+- `src/shared/contracts/common.ts`: add optional `topP` to route schemas.
+- `src/shared/utils/generationSettingsValidation.ts`: validate `topP` as a finite number in `[0.1, 1]`.
+- `src/main/presenter/sqlitePresenter/tables/deepchatSessions.ts`: add `top_p` storage and migration.
+- `src/main/presenter/agentRuntimePresenter/index.ts`: sanitize, persist, and pass `topP` through runtime model config.
+- `src/main/presenter/llmProviderPresenter/aiSdk/runtime.ts`: include `topP` in AI SDK `generateText` and `streamText` calls and request traces when defined.
+- `src/renderer/src/stores/ui/draft.ts`: carry draft `topP` overrides for new sessions.
+- `src/renderer/src/components/chat/ChatStatusBar.vue`: show and persist the compact `topP` control.
+- i18n `chat.json` and `settings.json` files: add `topP` label, hover description, and validation.
+- `topP` number inputs use min `0.1`, max `1`, and step `0.1` so values align with common AI SDK/provider constraints.
+
+## Data Flow
+
+1. User edits `topP` in ChatStatusBar.
+2. Draft sessions store it in Pinia; active sessions call `sessions.updateGenerationSettings`.
+3. Main process validates and stores `topP` as part of session generation settings.
+4. Agent runtime adds `topP` to runtime `ModelConfig`.
+5. AI SDK runtime includes `topP` only when defined.
+
+## Compatibility
+
+- Existing databases migrate by adding nullable `top_p`.
+- Existing sessions return no `topP` unless previously set.
+- Requests without `topP` preserve current behavior.
+
+## Test Strategy
+
+- Run formatting and i18n generation required by repository guidelines.
+- Run lint to catch type/schema/template issues.
+- Prefer focused type/lint validation over provider integration tests because this is a pass-through parameter.
diff --git a/docs/features/model-top-p-settings/spec.md b/docs/features/model-top-p-settings/spec.md
new file mode 100644
index 000000000..006540853
--- /dev/null
+++ b/docs/features/model-top-p-settings/spec.md
@@ -0,0 +1,36 @@
+# Model Top P Settings
+
+## User Need
+
+Users need to adjust `top_p` for chat models because many upstream model APIs support nucleus sampling and expose it as a generation parameter.
+
+## Goal
+
+Add an optional per-session `topP` generation setting for text chat requests and pass it through to AI SDK text generation when the user explicitly sets it.
+
+## Acceptance Criteria
+
+- Users can set `topP` from the chat model advanced settings panel for regular text chat models.
+- `topP` accepts values greater than or equal to 0.1 and less than or equal to 1.
+- The Top P UI uses the plain label "Top P" and moves explanatory copy into a hover help icon.
+- Existing conversations and new sessions continue to work when no `topP` is set.
+- `topP` persists with DeepChat session generation settings and survives app restart.
+- Text `generateText` and streaming requests pass `topP` to AI SDK only when it is defined.
+- Existing Voice.ai TTS `topP` configuration remains independent.
+
+## Constraints
+
+- Follow current typed route/contracts and presenter boundaries.
+- Use `topP` internally and let SDK/provider layers map provider payload details.
+- Do not default-send `topP: 1`; omit the parameter when unset to preserve provider defaults.
+- Use i18n keys for all user-facing strings.
+- SQLite schema migration must be backward compatible.
+
+## Non-Goals
+
+- Provider-specific `top_p` compatibility matrices.
+- Building a full Provider DB `top_p` capability matrix.
+
+## Open Questions
+
+- None.
diff --git a/docs/features/model-top-p-settings/tasks.md b/docs/features/model-top-p-settings/tasks.md
new file mode 100644
index 000000000..af489579a
--- /dev/null
+++ b/docs/features/model-top-p-settings/tasks.md
@@ -0,0 +1,10 @@
+# Tasks
+
+- [x] Create SDD artifacts for optional model `topP` setting.
+- [x] Add shared types, contracts, and validation support for `topP`.
+- [x] Add SQLite session persistence and migration for `topP`.
+- [x] Propagate `topP` through runtime model config and AI SDK calls.
+- [x] Add renderer draft state and advanced settings UI for `topP`.
+- [x] Add i18n strings for `topP` controls and validation.
+- [x] Refine Top P UI to use a help tooltip and `[0.1, 1]` number input range.
+- [x] Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`.
diff --git a/docs/issues/chat-top-p-tooltip/plan.md b/docs/issues/chat-top-p-tooltip/plan.md
new file mode 100644
index 000000000..bb78faca8
--- /dev/null
+++ b/docs/issues/chat-top-p-tooltip/plan.md
@@ -0,0 +1,22 @@
+# Plan
+
+## Approach
+
+The current nested `Tooltip` is fragile inside the model settings `Popover` and can render beneath or outside the visible stacking context. Replace it with a controlled lightweight hover/focus panel anchored next to the Top P label. This keeps tooltip-style interaction while avoiding portal stacking conflicts.
+
+## Affected Interfaces
+
+- `src/renderer/src/components/chat/ChatStatusBar.vue`
+
+## Data Flow
+
+No data flow changes. The same `chat.advancedSettings.topPDescription` i18n string is displayed.
+
+## Compatibility
+
+No persisted configuration changes. The chat settings Top P clamp behavior remains unchanged.
+
+## Test Strategy
+
+- Static verification of template/script changes.
+- Run project formatting/lint commands if package tooling is available.
diff --git a/docs/issues/chat-top-p-tooltip/spec.md b/docs/issues/chat-top-p-tooltip/spec.md
new file mode 100644
index 000000000..27c9e2e3a
--- /dev/null
+++ b/docs/issues/chat-top-p-tooltip/spec.md
@@ -0,0 +1,28 @@
+# Chat Top P Tooltip
+
+## User Need
+
+The chat settings panel Top P help icon must show explanatory text reliably. The settings dialog should keep inline description text, while the chat status bar should keep a hover tooltip interaction.
+
+## Goal
+
+Fix the Top P help content in `ChatStatusBar.vue` so it is visible above the settings popover and has a readable width.
+
+## Acceptance Criteria
+
+- Hovering the Top P help icon in the chat settings panel shows the Top P description.
+- The tooltip is not hidden behind the model settings popover.
+- The tooltip content has a readable constrained width for the long description.
+- The settings dialog Top P description remains inline text.
+- The existing Top P range clamp behavior remains aligned between chat and settings pages.
+
+## Constraints
+
+- Keep changes focused to the chat settings Top P help behavior.
+- Preserve existing i18n keys and shadcn/reka UI styling patterns where practical.
+- Do not introduce new dependencies.
+
+## Non-goals
+
+- Redesign the full chat settings panel.
+- Change Top P semantics or persisted data shape.
diff --git a/docs/issues/chat-top-p-tooltip/tasks.md b/docs/issues/chat-top-p-tooltip/tasks.md
new file mode 100644
index 000000000..6df0aa9e0
--- /dev/null
+++ b/docs/issues/chat-top-p-tooltip/tasks.md
@@ -0,0 +1,5 @@
+# Tasks
+
+- [x] Inspect current Top P tooltip implementation and popover stacking.
+- [x] Add a layer-safe tooltip-style hover/focus panel for Top P in chat settings.
+- [ ] Verify formatting and report any unavailable validation tooling.
diff --git a/src/main/presenter/agentRuntimePresenter/index.ts b/src/main/presenter/agentRuntimePresenter/index.ts
index 2a16a546c..756b05eb8 100644
--- a/src/main/presenter/agentRuntimePresenter/index.ts
+++ b/src/main/presenter/agentRuntimePresenter/index.ts
@@ -154,6 +154,11 @@ type PackageJsonManifest = {
scripts?: Record
}
+function normalizeTopP(value: unknown): number | undefined {
+ const numeric = parseFiniteNumericValue(value)
+ return numeric !== undefined && numeric >= 0.1 && numeric <= 1 ? numeric : undefined
+}
+
function readPackageJsonManifest(workdir: string): PackageJsonManifest | null {
try {
const packageJsonPath = path.join(workdir, 'package.json')
@@ -202,6 +207,7 @@ type PersistedSessionGenerationRow = {
permission_mode: PermissionMode
system_prompt: string | null
temperature: number | null
+ top_p: number | null
context_length: number | null
max_tokens: number | null
timeout_ms: number | null
@@ -1237,7 +1243,6 @@ export class AgentRuntimePresenter implements IAgentImplementation {
if (!state && !dbSession) {
throw new Error(`Session ${sessionId} not found`)
}
-
const providerId = state?.providerId ?? dbSession?.provider_id
const modelId = state?.modelId ?? dbSession?.model_id
if (!providerId || !modelId) {
@@ -1987,6 +1992,7 @@ export class AgentRuntimePresenter implements IAgentImplementation {
const modelConfig: ModelConfig = {
...baseModelConfig,
temperature: generationSettings.temperature,
+ topP: generationSettings.topP,
contextLength: generationSettings.contextLength,
maxTokens: capAgentRequestMaxTokens(generationSettings.maxTokens, contextBudgetLength),
timeout: generationSettings.timeout,
@@ -3328,6 +3334,9 @@ export class AgentRuntimePresenter implements IAgentImplementation {
if (sessionRow.temperature !== null) {
patch.temperature = sessionRow.temperature
}
+ if (sessionRow.top_p !== null) {
+ patch.topP = sessionRow.top_p
+ }
if (sessionRow.context_length !== null) {
patch.contextLength = sessionRow.context_length
}
@@ -3375,6 +3384,9 @@ export class AgentRuntimePresenter implements IAgentImplementation {
if (Object.prototype.hasOwnProperty.call(requestedPatch, 'temperature')) {
patch.temperature = sanitized.temperature
}
+ if (Object.prototype.hasOwnProperty.call(requestedPatch, 'topP')) {
+ patch.topP = sanitized.topP
+ }
if (Object.prototype.hasOwnProperty.call(requestedPatch, 'contextLength')) {
patch.contextLength = sanitized.contextLength
}
@@ -3402,6 +3414,9 @@ export class AgentRuntimePresenter implements IAgentImplementation {
if (Object.prototype.hasOwnProperty.call(requestedPatch, 'imageGeneration')) {
patch.imageGeneration = sanitized.imageGeneration
}
+ if (Object.prototype.hasOwnProperty.call(requestedPatch, 'videoGeneration')) {
+ patch.videoGeneration = sanitized.videoGeneration
+ }
return patch
}
@@ -3412,6 +3427,7 @@ export class AgentRuntimePresenter implements IAgentImplementation {
return {
systemPrompt: settings.systemPrompt,
temperature: settings.temperature,
+ topP: settings.topP,
contextLength: settings.contextLength,
maxTokens: settings.maxTokens,
timeout: settings.timeout,
@@ -3420,7 +3436,8 @@ export class AgentRuntimePresenter implements IAgentImplementation {
reasoningVisibility: settings.reasoningVisibility,
verbosity: settings.verbosity,
forceInterleavedThinkingCompat: settings.forceInterleavedThinkingCompat,
- imageGeneration: settings.imageGeneration
+ imageGeneration: settings.imageGeneration,
+ videoGeneration: settings.videoGeneration
}
}
@@ -3466,6 +3483,7 @@ export class AgentRuntimePresenter implements IAgentImplementation {
fixedTemperatureKimi?.temperature ??
parseFiniteNumericValue(modelConfig.temperature) ??
0.7,
+ topP: normalizeTopP(modelConfig.topP),
contextLength: contextLengthDefault,
timeout:
timeoutDefault >= MODEL_TIMEOUT_MIN_MS && timeoutDefault <= MODEL_TIMEOUT_MAX_MS
@@ -3607,6 +3625,15 @@ export class AgentRuntimePresenter implements IAgentImplementation {
}
}
+ if (Object.prototype.hasOwnProperty.call(patch, 'topP')) {
+ const normalizedTopP = normalizeTopP(patch.topP)
+ if (normalizedTopP !== undefined) {
+ next.topP = normalizedTopP
+ } else {
+ delete next.topP
+ }
+ }
+
if (Object.prototype.hasOwnProperty.call(patch, 'timeout')) {
const error = validateGenerationNumericField('timeout', patch.timeout)
const numeric = toValidNonNegativeInteger(parseFiniteNumericValue(patch.timeout))
diff --git a/src/main/presenter/configPresenter/modelConfig.ts b/src/main/presenter/configPresenter/modelConfig.ts
index fcd2c709c..119f207f8 100644
--- a/src/main/presenter/configPresenter/modelConfig.ts
+++ b/src/main/presenter/configPresenter/modelConfig.ts
@@ -184,6 +184,7 @@ export class ModelConfigHelper {
contextLength: resolveModelContextLength(model.limit?.context),
timeout: DEFAULT_MODEL_TIMEOUT,
temperature: 0.6,
+ topP: undefined,
vision: isImageInputSupported(model),
speechRecognition: false,
functionCall: resolveModelFunctionCall(model.tool_call),
@@ -488,6 +489,7 @@ export class ModelConfigHelper {
...DEFAULT_MODEL_CAPABILITY_FALLBACKS,
timeout: DEFAULT_MODEL_TIMEOUT,
temperature: 0.6,
+ topP: undefined,
type: ModelType.Chat,
apiEndpoint: ApiEndpointType.Chat,
endpointType: undefined,
@@ -512,6 +514,7 @@ export class ModelConfigHelper {
contextLength: storedConfig.contextLength ?? finalConfig.contextLength,
timeout: storedConfig.timeout ?? finalConfig.timeout,
temperature: storedConfig.temperature ?? finalConfig.temperature,
+ topP: storedConfig.topP ?? finalConfig.topP,
vision: storedConfig.vision ?? finalConfig.vision,
speechRecognition: storedConfig.speechRecognition ?? finalConfig.speechRecognition,
functionCall: storedConfig.functionCall ?? finalConfig.functionCall,
diff --git a/src/main/presenter/llmProviderPresenter/aiSdk/runtime.ts b/src/main/presenter/llmProviderPresenter/aiSdk/runtime.ts
index 6c3db6f9e..e143c38ba 100644
--- a/src/main/presenter/llmProviderPresenter/aiSdk/runtime.ts
+++ b/src/main/presenter/llmProviderPresenter/aiSdk/runtime.ts
@@ -1194,7 +1194,8 @@ export async function runAiSdkGenerateText(
maxOutputTokens: maxTokens,
...(shouldSendTemperature && resolvedTemperature !== undefined
? { temperature: resolvedTemperature }
- : {})
+ : {}),
+ ...(normalizedModelConfig.topP !== undefined ? { topP: normalizedModelConfig.topP } : {})
}
await context.emitRequestTrace?.(normalizedModelConfig, {
@@ -1211,6 +1212,7 @@ export async function runAiSdkGenerateText(
...(shouldSendTemperature && resolvedTemperature !== undefined
? { temperature: resolvedTemperature }
: {}),
+ ...(normalizedModelConfig.topP !== undefined ? { topP: normalizedModelConfig.topP } : {}),
maxOutputTokens: maxTokens
})
@@ -1396,6 +1398,7 @@ export async function* runAiSdkCoreStream(
...(shouldSendTemperature && resolvedTemperature !== undefined
? { temperature: resolvedTemperature }
: {}),
+ ...(normalizedModelConfig.topP !== undefined ? { topP: normalizedModelConfig.topP } : {}),
tools: tools.map((tool) => tool.function.name)
}
@@ -1414,6 +1417,7 @@ export async function* runAiSdkCoreStream(
...(shouldSendTemperature && resolvedTemperature !== undefined
? { temperature: resolvedTemperature }
: {}),
+ ...(normalizedModelConfig.topP !== undefined ? { topP: normalizedModelConfig.topP } : {}),
maxOutputTokens: maxTokens
})
diff --git a/src/main/presenter/sqlitePresenter/schemaCatalog.ts b/src/main/presenter/sqlitePresenter/schemaCatalog.ts
index ce55a8fe9..914f36bd0 100644
--- a/src/main/presenter/sqlitePresenter/schemaCatalog.ts
+++ b/src/main/presenter/sqlitePresenter/schemaCatalog.ts
@@ -117,6 +117,7 @@ const CATALOG_DEFINITIONS: CatalogDefinition[] = [
repairableColumns: {
system_prompt: 'ALTER TABLE deepchat_sessions ADD COLUMN system_prompt TEXT;',
temperature: 'ALTER TABLE deepchat_sessions ADD COLUMN temperature REAL;',
+ top_p: 'ALTER TABLE deepchat_sessions ADD COLUMN top_p REAL;',
context_length: 'ALTER TABLE deepchat_sessions ADD COLUMN context_length INTEGER;',
max_tokens: 'ALTER TABLE deepchat_sessions ADD COLUMN max_tokens INTEGER;',
thinking_budget: 'ALTER TABLE deepchat_sessions ADD COLUMN thinking_budget INTEGER;',
@@ -131,7 +132,9 @@ const CATALOG_DEFINITIONS: CatalogDefinition[] = [
'ALTER TABLE deepchat_sessions ADD COLUMN force_interleaved_thinking_compat INTEGER;',
reasoning_visibility: 'ALTER TABLE deepchat_sessions ADD COLUMN reasoning_visibility TEXT;',
image_generation_options_json:
- 'ALTER TABLE deepchat_sessions ADD COLUMN image_generation_options_json TEXT;'
+ 'ALTER TABLE deepchat_sessions ADD COLUMN image_generation_options_json TEXT;',
+ video_generation_options_json:
+ 'ALTER TABLE deepchat_sessions ADD COLUMN video_generation_options_json TEXT;'
},
typeCheckedColumns: [
'summary_cursor_order_seq',
diff --git a/src/main/presenter/sqlitePresenter/tables/deepchatSessions.ts b/src/main/presenter/sqlitePresenter/tables/deepchatSessions.ts
index 7993f9742..0d0235693 100644
--- a/src/main/presenter/sqlitePresenter/tables/deepchatSessions.ts
+++ b/src/main/presenter/sqlitePresenter/tables/deepchatSessions.ts
@@ -21,6 +21,7 @@ type DeepChatSessionGenerationSettings = Pick<
SessionGenerationSettings,
| 'systemPrompt'
| 'temperature'
+ | 'topP'
| 'contextLength'
| 'maxTokens'
| 'timeout'
@@ -40,6 +41,7 @@ export interface DeepChatSessionRow {
permission_mode: 'default' | 'full_access'
system_prompt: string | null
temperature: number | null
+ top_p: number | null
context_length: number | null
max_tokens: number | null
timeout_ms: number | null
@@ -119,6 +121,10 @@ export class DeepChatSessionsTable extends BaseTable {
columns.push('video_generation_options_json TEXT')
}
+ if (version >= 29) {
+ columns.push('top_p REAL')
+ }
+
if (version >= 14) {
columns.push(
'summary_text TEXT',
@@ -158,6 +164,9 @@ export class DeepChatSessionsTable extends BaseTable {
if (!this.hasColumn('context_length')) {
statements.push('ALTER TABLE deepchat_sessions ADD COLUMN context_length INTEGER;')
}
+ if (!this.hasColumn('top_p')) {
+ statements.push('ALTER TABLE deepchat_sessions ADD COLUMN top_p REAL;')
+ }
if (!this.hasColumn('max_tokens')) {
statements.push('ALTER TABLE deepchat_sessions ADD COLUMN max_tokens INTEGER;')
}
@@ -248,11 +257,14 @@ export class DeepChatSessionsTable extends BaseTable {
if (version === 28) {
return 'ALTER TABLE deepchat_sessions ADD COLUMN video_generation_options_json TEXT;'
}
+ if (version === 29) {
+ return 'ALTER TABLE deepchat_sessions ADD COLUMN top_p REAL;'
+ }
return null
}
getLatestVersion(): number {
- return 28
+ return 29
}
private serializeImageGenerationOptions(
@@ -311,6 +323,7 @@ export class DeepChatSessionsTable extends BaseTable {
permission_mode,
system_prompt,
temperature,
+ top_p,
context_length,
max_tokens,
timeout_ms,
@@ -325,7 +338,7 @@ export class DeepChatSessionsTable extends BaseTable {
summary_cursor_order_seq,
summary_updated_at
)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
)
.run(
id,
@@ -334,6 +347,7 @@ export class DeepChatSessionsTable extends BaseTable {
permissionMode,
generationSettings?.systemPrompt ?? null,
generationSettings?.temperature ?? null,
+ generationSettings?.topP ?? null,
generationSettings?.contextLength ?? null,
generationSettings?.maxTokens ?? null,
generationSettings?.timeout ?? null,
@@ -374,6 +388,9 @@ export class DeepChatSessionsTable extends BaseTable {
if (row.temperature !== null) {
settings.temperature = row.temperature
}
+ if (row.top_p !== null) {
+ settings.topP = row.top_p
+ }
if (row.context_length !== null) {
settings.contextLength = row.context_length
}
@@ -432,6 +449,10 @@ export class DeepChatSessionsTable extends BaseTable {
updates.push('temperature = ?')
params.push(settings.temperature ?? null)
}
+ if (Object.prototype.hasOwnProperty.call(settings, 'topP')) {
+ updates.push('top_p = ?')
+ params.push(settings.topP ?? null)
+ }
if (Object.prototype.hasOwnProperty.call(settings, 'contextLength')) {
updates.push('context_length = ?')
params.push(settings.contextLength ?? null)
diff --git a/src/renderer/src/components/chat/ChatStatusBar.vue b/src/renderer/src/components/chat/ChatStatusBar.vue
index 77817d13a..3323f6b37 100644
--- a/src/renderer/src/components/chat/ChatStatusBar.vue
+++ b/src/renderer/src/components/chat/ChatStatusBar.vue
@@ -263,489 +263,579 @@
-
-
{{
- t('chat.advancedSettings.temperature')
- }}
-
-
-
-
-
-
-
-
-
-
- {{ moonshotKimiTemperatureHint }}
-
-
- {{ getNumericInputErrorMessage('temperature') }}
-
-
-
-
-
{{
- t('chat.advancedSettings.contextLength')
- }}
-
-
-
-
-
-
-
-
-
-
+
- {{ getNumericInputErrorMessage('contextLength') }}
-
-
-
-
-
{{
- t('chat.advancedSettings.maxTokens')
- }}
-
-
{{
+ t('chat.advancedSettings.temperature')
+ }}
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
-
-
+ {{ getNumericInputErrorMessage('temperature') }}
+
-
- {{ getNumericInputErrorMessage('maxTokens') }}
-
-
-
-
{{
- t('settings.model.modelConfig.timeout.label')
- }}
-
-
-
-
-
-
+
+ {{
+ t('chat.advancedSettings.topP')
+ }}
+
+
+
+
+
+
+
+ {{ t('chat.advancedSettings.topPDescription') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+ {{ getNumericInputErrorMessage('topP') }}
+
-
- {{ getNumericInputErrorMessage('timeout') }}
-
-
-
-
-
-
-
-
{{
- t('settings.model.modelConfig.reasoningEffort.label')
- }}
-
-
-
+ {{
+ t('chat.advancedSettings.contextLength')
+ }}
+
+
+
+
+
-
-
-
- {{ option.label }}
-
-
-
-
+
+
+
+
+ {{ getNumericInputErrorMessage('contextLength') }}
+
+
-
-
{{
- t('settings.model.modelConfig.reasoningVisibility.label')
- }}
-
-
-
+ {{
+ t('chat.advancedSettings.maxTokens')
+ }}
+
+
-
-
-
- {{ option.label }}
-
-
-
-
-
-
- {{
- t('settings.model.modelConfig.verbosity.label')
- }}
-
-
-
+
+
-
-
-
- {{ option.label }}
-
-
-
-
+
+
+
+
+ {{ getNumericInputErrorMessage('maxTokens') }}
+
+
-
-
+
{{
- t('chat.advancedSettings.thinkingBudget')
+ t('settings.model.modelConfig.timeout.label')
}}
-
- {{ thinkingBudgetHint }}
-
-
+
+
+
+
+
+
+
+ {{ getNumericInputErrorMessage('timeout') }}
+
-
-
+
+
+
+
+ {{
+ t('settings.model.modelConfig.reasoningEffort.label')
+ }}
+
-
-
-
-
+
+
+
+
+ {{ option.label }}
+
+
+
+
+
+
+ {{
+ t('settings.model.modelConfig.reasoningVisibility.label')
+ }}
+
-
-
+
+
+
+
+
+ {{ option.label }}
+
+
+
-
- {{ getNumericInputErrorMessage('thinkingBudget') }}
-
-
+
{{
+ t('settings.model.modelConfig.verbosity.label')
+ }}
+
+
+
+
+
+
+ {{ option.label }}
+
+
+
+
-
-
-
-
- {{ t('chat.advancedSettings.forceInterleavedThinkingCompat') }}
-
-
- {{
- t('chat.advancedSettings.forceInterleavedThinkingCompatDescription')
- }}
-
+
+
+
{{
+ t('chat.advancedSettings.thinkingBudget')
+ }}
+
+
+ {{ thinkingBudgetHint }}
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+ {{ getNumericInputErrorMessage('thinkingBudget') }}
+
-
+
+
+
+
+
+ {{ t('chat.advancedSettings.forceInterleavedThinkingCompat') }}
+
+
+ {{
+ t('chat.advancedSettings.forceInterleavedThinkingCompatDescription')
+ }}
+
+
+
+
+
+
@@ -902,6 +992,12 @@ import {
} from '@shadcn/components/ui/dropdown-menu'
import { Input } from '@shadcn/components/ui/input'
import { Popover, PopoverContent, PopoverTrigger } from '@shadcn/components/ui/popover'
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger
+} from '@shadcn/components/ui/tooltip'
import {
Select,
SelectContent,
@@ -1016,6 +1112,9 @@ type GroupedModelList = {
}
const TEMPERATURE_STEP = 0.1
+const TOP_P_STEP = 0.1
+const TOP_P_MIN = 0.1
+const TOP_P_MAX = 1
const CONTEXT_LENGTH_STEP = 1024
const MAX_TOKENS_STEP = 128
const TIMEOUT_STEP = 1000
@@ -1058,6 +1157,7 @@ let modelSettingsTargetConfigToken = 0
const activeNumericInput = ref(null)
const numericInputDrafts = ref>({
temperature: '',
+ topP: '',
contextLength: '',
maxTokens: '',
timeout: '',
@@ -1067,6 +1167,7 @@ const numericInputErrors = ref<
Record
>({
temperature: null,
+ topP: null,
contextLength: null,
maxTokens: null,
timeout: null,
@@ -1413,6 +1514,10 @@ const getCommittedNumericInputValue = (field: GenerationNumericField): string =>
switch (field) {
case 'temperature':
return String(localSettings.value.temperature)
+ case 'topP': {
+ const value = localSettings.value.topP
+ return value === undefined ? '' : String(value)
+ }
case 'contextLength':
return String(localSettings.value.contextLength)
case 'maxTokens':
@@ -1449,6 +1554,7 @@ const resetNumericInputFieldState = (field: GenerationNumericField): void => {
const resetNumericInputState = (): void => {
activeNumericInput.value = null
resetNumericInputFieldState('temperature')
+ resetNumericInputFieldState('topP')
resetNumericInputFieldState('contextLength')
resetNumericInputFieldState('maxTokens')
resetNumericInputFieldState('timeout')
@@ -1509,6 +1615,8 @@ const getNumericInputErrorMessage = (field: GenerationNumericField): string => {
return t('settings.model.modelConfig.validation.timeoutMin')
case 'timeout_too_large':
return t('settings.model.modelConfig.validation.timeoutMax')
+ case 'top_p_out_of_range':
+ return t('chat.advancedSettings.validation.topPRange')
}
}
@@ -1716,6 +1824,14 @@ const invalidateGenerationPersistResponses = () => {
}
const temperatureInputValue = computed(() => getNumericInputValue('temperature'))
+const topPInputValue = computed(() => getNumericInputValue('topP'))
+const topPCommittedValue = computed(() => localSettings.value?.topP ?? TOP_P_MAX)
+const topPDecreaseDisabled = computed(
+ () => localSettings.value?.topP === undefined || topPCommittedValue.value <= TOP_P_MIN
+)
+const topPIncreaseDisabled = computed(
+ () => localSettings.value?.topP !== undefined && topPCommittedValue.value >= TOP_P_MAX
+)
const contextLengthInputValue = computed(() => getNumericInputValue('contextLength'))
const maxTokensInputValue = computed(() => getNumericInputValue('maxTokens'))
const timeoutInputValue = computed(() => getNumericInputValue('timeout'))
@@ -1747,6 +1863,9 @@ const showTemperatureControl = computed(
(capabilitySupportsTemperature.value !== false || isMoonshotKimiTemperatureLocked.value) &&
Boolean(localSettings.value)
)
+const showTopPControl = computed(
+ () => !showOpenAIMediaGenerationSettings.value && Boolean(localSettings.value)
+)
const showVerbosity = computed(
() =>
@@ -2035,6 +2154,7 @@ const resolveDefaultGenerationSettings = async (
systemPrompt: agentConfig.systemPrompt ?? '',
temperature:
fixedTemperatureKimi?.temperature ?? parseFiniteNumericValue(modelConfig.temperature) ?? 0.7,
+ topP: normalizeTopP(modelConfig.topP),
contextLength: contextLengthDefault,
timeout:
timeoutDefault >= TIMEOUT_MIN && timeoutDefault <= TIMEOUT_MAX
@@ -2241,6 +2361,10 @@ const updateLocalGenerationSettings = (patch: Partial
if (Object.prototype.hasOwnProperty.call(nextPatch, 'temperature')) {
normalizedPatch.temperature = next.temperature
}
+ if (Object.prototype.hasOwnProperty.call(nextPatch, 'topP')) {
+ normalizedPatch.topP = normalizeTopP(next.topP)
+ next.topP = normalizedPatch.topP
+ }
if (Object.prototype.hasOwnProperty.call(nextPatch, 'contextLength')) {
normalizedPatch.contextLength = next.contextLength
}
@@ -2587,6 +2711,7 @@ async function changeModelSelection(
const previousDraftGenerationSettings = {
systemPrompt: draftStore.systemPrompt,
temperature: draftStore.temperature,
+ topP: draftStore.topP,
contextLength: draftStore.contextLength,
maxTokens: draftStore.maxTokens,
timeout: draftStore.timeout,
@@ -2600,6 +2725,7 @@ async function changeModelSelection(
} as Partial
const clearedDraftModelOverrides = {
temperature: undefined,
+ topP: undefined,
contextLength: undefined,
maxTokens: undefined,
timeout: undefined,
@@ -2748,6 +2874,59 @@ function stepTemperature(direction: -1 | 1) {
resetNumericInputFieldState('temperature')
}
+const roundTopPStepValue = (value: number): number => Number(value.toFixed(10))
+
+function normalizeTopP(value: unknown): number | undefined {
+ const numeric = parseFiniteNumericValue(value)
+ return numeric !== undefined && numeric >= 0.1 && numeric <= 1 ? numeric : undefined
+}
+
+function stepTopP(direction: -1 | 1) {
+ if (!localSettings.value) {
+ return
+ }
+ if (hasNumericInputError('topP')) {
+ return
+ }
+ if (direction === -1 && localSettings.value.topP === undefined) {
+ return
+ }
+ const current = localSettings.value.topP ?? TOP_P_MAX
+ const next = Math.min(TOP_P_MAX, Math.max(TOP_P_MIN, current + direction * TOP_P_STEP))
+ updateLocalGenerationSettings({ topP: roundTopPStepValue(next) })
+ resetNumericInputFieldState('topP')
+}
+
+function onTopPInput(value: string | number) {
+ setNumericInputDraft('topP', value)
+}
+
+function commitTopPInput() {
+ if (numericInputDrafts.value.topP.trim() === '') {
+ stopNumericInputEdit('topP')
+ clearNumericInputError('topP')
+ updateLocalGenerationSettings({ topP: undefined })
+ resetNumericInputFieldState('topP')
+ return
+ }
+
+ const draftNum = parseFiniteNumericValue(numericInputDrafts.value.topP)
+ if (draftNum !== undefined) {
+ if (draftNum < TOP_P_MIN) {
+ numericInputDrafts.value.topP = String(TOP_P_MIN)
+ } else if (draftNum > TOP_P_MAX) {
+ numericInputDrafts.value.topP = String(TOP_P_MAX)
+ }
+ }
+
+ const next = commitNumericField('topP', numericInputDrafts.value.topP)
+ if (next === undefined) {
+ return
+ }
+ updateLocalGenerationSettings({ topP: next })
+ resetNumericInputFieldState('topP')
+}
+
function onTemperatureInput(value: string | number) {
if (isMoonshotKimiTemperatureLocked.value) {
return
diff --git a/src/renderer/src/components/settings/ModelConfigDialog.vue b/src/renderer/src/components/settings/ModelConfigDialog.vue
index b5671b0b2..ab5e4953a 100644
--- a/src/renderer/src/components/settings/ModelConfigDialog.vue
+++ b/src/renderer/src/components/settings/ModelConfigDialog.vue
@@ -158,6 +158,24 @@
+
+
{{ t('settings.model.modelConfig.topP.label') }}
+
+
+ {{ t('settings.model.modelConfig.topP.description') }}
+
+
+ {{ errors.topP }}
+
+
+
import { ref, computed, watch, onMounted } from 'vue'
+import { Icon } from '@iconify/vue'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import {
@@ -593,6 +612,12 @@ import { Button } from '@shadcn/components/ui/button'
import { Input } from '@shadcn/components/ui/input'
import { Label } from '@shadcn/components/ui/label'
import { Switch } from '@shadcn/components/ui/switch'
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger
+} from '@shadcn/components/ui/tooltip'
import {
Select,
SelectContent,
@@ -683,6 +708,7 @@ const createDefaultConfig = (): ModelConfig => ({
contextLength: DEFAULT_MODEL_CONTEXT_LENGTH,
timeout: DEFAULT_MODEL_TIMEOUT,
temperature: 0.7,
+ topP: undefined,
vision: DEFAULT_MODEL_VISION,
speechRecognition: DEFAULT_MODEL_SPEECH_RECOGNITION,
functionCall: DEFAULT_MODEL_FUNCTION_CALL,
@@ -700,6 +726,7 @@ const DEFAULT_VERBOSITY_OPTIONS: Array<'low' | 'medium' | 'high'> = ['low', 'med
// 配置数据
const config = ref
(createDefaultConfig())
+const topPDraft = ref('')
const modelNameField = ref(props.modelName ?? '')
const modelIdField = ref(props.modelId ?? '')
const originalModelId = ref(props.modelId ?? '')
@@ -749,6 +776,33 @@ const showOpenAIMediaGenerationRouteControls = computed(
)
const showTtsSettings = computed(() => config.value.type === ModelType.TTS)
+const syncTopPDraftFromConfig = () => {
+ topPDraft.value = typeof config.value.topP === 'number' ? String(config.value.topP) : ''
+}
+
+const parseTopPDraft = (): number | undefined => {
+ const raw = topPDraft.value.trim()
+ if (!raw) {
+ return undefined
+ }
+
+ return Number(raw)
+}
+
+const clampTopPDraft = () => {
+ const raw = topPDraft.value.trim()
+ if (!raw) return
+
+ const num = Number(raw)
+ if (!Number.isFinite(num)) return
+
+ if (num < 0.1) {
+ topPDraft.value = '0.1'
+ } else if (num > 1) {
+ topPDraft.value = '1'
+ }
+}
+
// 重置确认对话框
const showResetConfirm = ref(false)
@@ -1128,6 +1182,7 @@ const loadConfig = async () => {
if (isCreateMode.value) {
config.value = createDefaultConfig()
+ syncTopPDraftFromConfig()
syncNewApiDerivedFields()
await fetchCapabilities()
return
@@ -1137,7 +1192,8 @@ const loadConfig = async () => {
try {
const modelConfig = await modelConfigStore.getModelConfig(props.modelId, props.providerId)
- config.value = { ...modelConfig }
+ config.value = { ...createDefaultConfig(), ...modelConfig }
+ syncTopPDraftFromConfig()
if (showEndpointTypeSelector.value && !isNewApiEndpointType(config.value.endpointType)) {
config.value.endpointType =
@@ -1160,6 +1216,7 @@ const loadConfig = async () => {
} catch (error) {
console.error('Failed to load model config:', error)
config.value = createDefaultConfig()
+ syncTopPDraftFromConfig()
}
await fetchCapabilities()
@@ -1275,6 +1332,18 @@ const validateForm = () => {
}
}
+ if (!showOpenAIMediaGenerationSettings.value) {
+ const parsedTopP = parseTopPDraft()
+ config.value.topP = parsedTopP
+ if (parsedTopP !== undefined) {
+ if (!Number.isFinite(parsedTopP)) {
+ errors.value.topP = t('chat.advancedSettings.validation.finiteNumber')
+ } else if (parsedTopP < 0.1 || parsedTopP > 1) {
+ errors.value.topP = t('settings.model.modelConfig.validation.topPRange')
+ }
+ }
+ }
+
if (config.value.timeout !== undefined && config.value.timeout !== null) {
const timeout = Number(config.value.timeout)
if (!Number.isFinite(timeout) || timeout < MODEL_TIMEOUT_MIN_MS) {
@@ -1308,9 +1377,17 @@ const handleSave = async () => {
const timeout = Number(config.value.timeout)
const normalizedTimeout =
Number.isFinite(timeout) && timeout > 0 ? Math.round(timeout) : undefined
+ const parsedTopP = parseTopPDraft()
const configToSave: ModelConfig = {
...config.value,
...(normalizedTimeout !== undefined ? { timeout: normalizedTimeout } : {}),
+ topP:
+ typeof parsedTopP === 'number' &&
+ Number.isFinite(parsedTopP) &&
+ parsedTopP >= 0.1 &&
+ parsedTopP <= 1
+ ? parsedTopP
+ : undefined,
imageGeneration: showOpenAIImageGenerationSettings.value
? normalizeImageGenerationOptions(config.value.imageGeneration)
: undefined,
@@ -1384,6 +1461,7 @@ const confirmReset = async () => {
try {
if (isCreateMode.value) {
config.value = createDefaultConfig()
+ syncTopPDraftFromConfig()
modelNameField.value = ''
modelIdField.value = ''
showResetConfirm.value = false
diff --git a/src/renderer/src/i18n/da-DK/chat.json b/src/renderer/src/i18n/da-DK/chat.json
index 9afd6c544..ffe03c76a 100644
--- a/src/renderer/src/i18n/da-DK/chat.json
+++ b/src/renderer/src/i18n/da-DK/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "Vælg en forudindstilling",
"temperature": "Temperatur",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 låser temperaturen til {enabled}, når tænkning er aktiveret, og til {disabled}, når den er deaktiveret.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus-sampling; lad feltet stå tomt for at bruge udbyderens standard",
"contextLength": "Kontekstlængde",
"maxTokens": "Maks. tokens",
"thinkingBudget": "Tænkebudget",
@@ -182,7 +184,8 @@
"finiteNumber": "Skal være et endeligt tal",
"nonNegativeInteger": "Skal være et ikke-negativt heltal",
"contextLengthAtLeastMaxTokens": "Skal være større end eller lig med maksimumtokens",
- "maxTokensWithinContextLength": "Skal være mindre end eller lig med kontekstlængden"
+ "maxTokensWithinContextLength": "Skal være mindre end eller lig med kontekstlængden",
+ "topPRange": "Skal være større end 0 og mindre end eller lig med 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/da-DK/settings.json b/src/renderer/src/i18n/da-DK/settings.json
index 5ba20e796..65931ae39 100644
--- a/src/renderer/src/i18n/da-DK/settings.json
+++ b/src/renderer/src/i18n/da-DK/settings.json
@@ -557,6 +557,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 låser temperaturen til {enabled}, når tænkning er aktiveret, og til {disabled}, når den er deaktiveret.",
"label": "Temperatur"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Styrer nucleus-sampling. Lad feltet stå tomt for at bruge model- eller udbyderstandarden."
+ },
"title": "Brugerdefinerede modelparametre",
"type": {
"description": "Vælg modeltype",
@@ -580,7 +584,8 @@
"temperatureMin": "Temperaturen skal være større end eller lig 0",
"temperatureRequired": "Temperaturen må ikke være tom",
"timeoutMin": "Tidsgrænsen for anmodning skal være mindst 1000ms",
- "timeoutMax": "Tidsgrænsen for anmodning må ikke overstige 3600000ms"
+ "timeoutMax": "Tidsgrænsen for anmodning må ikke overstige 3600000ms",
+ "topPRange": "Top P skal være større end 0 og mindre end eller lig med 1"
},
"vision": {
"description": "Understøtter modellen visuelle funktioner?",
diff --git a/src/renderer/src/i18n/de-DE/chat.json b/src/renderer/src/i18n/de-DE/chat.json
index 3f16bc454..f63f5486b 100644
--- a/src/renderer/src/i18n/de-DE/chat.json
+++ b/src/renderer/src/i18n/de-DE/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Voreinstellung auswählen",
"temperature": "Temperatur",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ist bei aktiviertem Denken fest auf {enabled} und bei deaktiviertem Denken fest auf {disabled} gesetzt.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus Sampling; leer lassen, um den Anbieterstandard zu verwenden",
"contextLength": "Kontextlänge",
"maxTokens": "Maximale Ausgabe",
"thinkingBudget": "Denkbudget",
@@ -193,7 +195,8 @@
"finiteNumber": "Muss eine endliche Zahl sein",
"nonNegativeInteger": "Muss eine nicht negative ganze Zahl sein",
"contextLengthAtLeastMaxTokens": "Muss größer oder gleich der maximalen Ausgabe sein",
- "maxTokensWithinContextLength": "Muss kleiner oder gleich der Kontextlänge sein"
+ "maxTokensWithinContextLength": "Muss kleiner oder gleich der Kontextlänge sein",
+ "topPRange": "Muss größer als 0 und kleiner oder gleich 1 sein"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/de-DE/settings.json b/src/renderer/src/i18n/de-DE/settings.json
index f5254e8d9..1842ed21c 100644
--- a/src/renderer/src/i18n/de-DE/settings.json
+++ b/src/renderer/src/i18n/de-DE/settings.json
@@ -623,6 +623,10 @@
"description": "Steuert die Zufälligkeit der Ausgabe. Die meisten Modelle nutzen 0-1, manche unterstützen 0-2; je höher, desto zufälliger.",
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ist bei aktiviertem Denken fest auf {enabled} und bei deaktiviertem Denken fest auf {disabled} gesetzt."
},
+ "topP": {
+ "label": "Top P",
+ "description": "Steuert Nucleus Sampling. Leer lassen, um den Modell- oder Anbieterstandard zu verwenden."
+ },
"vision": {
"label": "Vision-Fähigkeit",
"description": "Ob das Modell Vision-Funktionen unterstützt"
@@ -723,7 +727,8 @@
"temperatureMin": "Temperatur muss größer oder gleich 0 sein",
"temperatureMax": "Temperatur muss kleiner oder gleich 2 sein",
"timeoutMin": "Anfrage-Timeout darf nicht kleiner als 1000 Millisekunden sein",
- "timeoutMax": "Anfrage-Timeout darf 3600000 Millisekunden nicht überschreiten"
+ "timeoutMax": "Anfrage-Timeout darf 3600000 Millisekunden nicht überschreiten",
+ "topPRange": "Top P muss größer als 0 und kleiner oder gleich 1 sein"
},
"endpointType": {
"label": "Protokolltyp",
diff --git a/src/renderer/src/i18n/en-US/chat.json b/src/renderer/src/i18n/en-US/chat.json
index a1724717f..e6d92b4d7 100644
--- a/src/renderer/src/i18n/en-US/chat.json
+++ b/src/renderer/src/i18n/en-US/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Select a preset",
"temperature": "Temperature",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fixes temperature to {enabled} when thinking is enabled and {disabled} when it is disabled.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "Context Length",
"maxTokens": "Max Tokens",
"thinkingBudget": "Thinking Budget",
@@ -193,7 +195,8 @@
"finiteNumber": "Must be a finite number",
"nonNegativeInteger": "Must be a non-negative integer",
"contextLengthAtLeastMaxTokens": "Must be greater than or equal to Max Tokens",
- "maxTokensWithinContextLength": "Must be less than or equal to Context Length"
+ "maxTokensWithinContextLength": "Must be less than or equal to Context Length",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/en-US/settings.json b/src/renderer/src/i18n/en-US/settings.json
index e2bf89246..2749ef0e1 100644
--- a/src/renderer/src/i18n/en-US/settings.json
+++ b/src/renderer/src/i18n/en-US/settings.json
@@ -687,6 +687,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fixes temperature to {enabled} when thinking is enabled and {disabled} when it is disabled.",
"label": "Temperature"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"title": "Custom model parameters",
"type": {
"description": "Select the type of model",
@@ -710,7 +714,8 @@
"temperatureMin": "The temperature must be greater than or equal to 0",
"temperatureRequired": "The temperature cannot be empty",
"timeoutMin": "Request timeout must be at least 1000ms",
- "timeoutMax": "Request timeout cannot exceed 3600000ms"
+ "timeoutMax": "Request timeout cannot exceed 3600000ms",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"vision": {
"description": "Does the model support visual ability?",
diff --git a/src/renderer/src/i18n/es-ES/chat.json b/src/renderer/src/i18n/es-ES/chat.json
index 449d42bd8..c937b77b9 100644
--- a/src/renderer/src/i18n/es-ES/chat.json
+++ b/src/renderer/src/i18n/es-ES/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Seleccionar preajuste",
"temperature": "Temperatura",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fija la temperatura en {enabled} cuando el razonamiento está activado y en {disabled} cuando está desactivado.",
+ "topP": "Top P",
+ "topPDescription": "Muestreo núcleo; déjalo vacío para usar el valor predeterminado del proveedor",
"contextLength": "Longitud del contexto",
"maxTokens": "Tokens máximos",
"thinkingBudget": "Presupuesto de razonamiento",
@@ -193,7 +195,8 @@
"finiteNumber": "Debe ser un número finito",
"nonNegativeInteger": "Debe ser un número entero no negativo",
"contextLengthAtLeastMaxTokens": "Debe ser mayor o igual que Tokens máximos",
- "maxTokensWithinContextLength": "Debe ser menor o igual que la longitud del contexto"
+ "maxTokensWithinContextLength": "Debe ser menor o igual que la longitud del contexto",
+ "topPRange": "Debe ser mayor que 0 y menor o igual que 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/es-ES/settings.json b/src/renderer/src/i18n/es-ES/settings.json
index cabf0baa5..a630e301f 100644
--- a/src/renderer/src/i18n/es-ES/settings.json
+++ b/src/renderer/src/i18n/es-ES/settings.json
@@ -623,6 +623,10 @@
"description": "Controlar la aleatoriedad de la salida. La mayoría de los modelos son 0-1 y algunos admiten entre 0-2. Los valores más altos aumentan la aleatoriedad.",
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fija la temperatura en {enabled} cuando el pensamiento está habilitado y en {disabled} cuando está deshabilitado."
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controla el muestreo núcleo. Déjalo vacío para usar el valor predeterminado del modelo o proveedor."
+ },
"vision": {
"label": "Habilidad visual",
"description": "¿El modelo admite la capacidad visual?"
@@ -723,7 +727,8 @@
"temperatureMin": "La temperatura debe ser mayor o igual a 0",
"temperatureMax": "La temperatura debe ser menor o igual a 2",
"timeoutMin": "El tiempo de espera de la solicitud debe ser de al menos 1000 ms.",
- "timeoutMax": "El tiempo de espera de la solicitud no puede exceder los 3600000 ms"
+ "timeoutMax": "El tiempo de espera de la solicitud no puede exceder los 3600000 ms",
+ "topPRange": "Top P debe ser mayor que 0 y menor o igual que 1"
},
"endpointType": {
"label": "Tipo de punto final",
diff --git a/src/renderer/src/i18n/fa-IR/chat.json b/src/renderer/src/i18n/fa-IR/chat.json
index 0be5a7791..401388eb9 100644
--- a/src/renderer/src/i18n/fa-IR/chat.json
+++ b/src/renderer/src/i18n/fa-IR/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "یک پیشتنظیم را انتخاب کنید",
"temperature": "دما",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 وقتی تفکر فعال باشد دما را روی {enabled} و وقتی غیرفعال باشد روی {disabled} ثابت نگه میدارد.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "طول زمینه",
"maxTokens": "حداکثر توکن",
"thinkingBudget": "بودجه تفکر",
@@ -182,7 +184,8 @@
"finiteNumber": "باید یک عدد متناهی باشد",
"nonNegativeInteger": "باید یک عدد صحیح نامنفی باشد",
"contextLengthAtLeastMaxTokens": "باید بزرگتر یا مساوی حداکثر توکنها باشد",
- "maxTokensWithinContextLength": "باید کوچکتر یا مساوی طول زمینه باشد"
+ "maxTokensWithinContextLength": "باید کوچکتر یا مساوی طول زمینه باشد",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/fa-IR/settings.json b/src/renderer/src/i18n/fa-IR/settings.json
index 9643caffe..16a2a6bcd 100644
--- a/src/renderer/src/i18n/fa-IR/settings.json
+++ b/src/renderer/src/i18n/fa-IR/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 وقتی تفکر فعال باشد دما را روی {enabled} و وقتی غیرفعال باشد روی {disabled} ثابت نگه میدارد.",
"label": "درجه حرارت"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"title": "پارامترهای مدل سفارشی",
"type": {
"description": "نوع مدل را انتخاب کنید",
@@ -647,7 +651,8 @@
"temperatureMin": "دما باید بیشتر یا برابر با 0 باشد",
"temperatureRequired": "درجه حرارت نمی تواند خالی باشد",
"timeoutMin": "تایماوت باید حداقل 1000ms باشد",
- "timeoutMax": "تایماوت نمیتواند از 3600000ms بیشتر باشد"
+ "timeoutMax": "تایماوت نمیتواند از 3600000ms بیشتر باشد",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"vision": {
"description": "آیا مدل از توانایی بصری پشتیبانی می کند؟",
diff --git a/src/renderer/src/i18n/fr-FR/chat.json b/src/renderer/src/i18n/fr-FR/chat.json
index 6e17a6e87..fe390aa44 100644
--- a/src/renderer/src/i18n/fr-FR/chat.json
+++ b/src/renderer/src/i18n/fr-FR/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "Sélectionner un préréglage",
"temperature": "Température",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fixe la température à {enabled} lorsque la réflexion est activée et à {disabled} lorsqu’elle est désactivée.",
+ "topP": "Top P",
+ "topPDescription": "Échantillonnage nucleus ; laissez vide pour utiliser la valeur par défaut du fournisseur",
"contextLength": "Longueur du contexte",
"maxTokens": "Jetons max",
"thinkingBudget": "Budget de réflexion",
@@ -182,7 +184,8 @@
"finiteNumber": "Doit être un nombre fini",
"nonNegativeInteger": "Doit être un entier positif ou nul",
"contextLengthAtLeastMaxTokens": "Doit être supérieur ou égal au nombre maximal de tokens",
- "maxTokensWithinContextLength": "Doit être inférieur ou égal à la longueur de contexte"
+ "maxTokensWithinContextLength": "Doit être inférieur ou égal à la longueur de contexte",
+ "topPRange": "Doit être supérieur à 0 et inférieur ou égal à 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/fr-FR/settings.json b/src/renderer/src/i18n/fr-FR/settings.json
index 455161a5b..ce856188e 100644
--- a/src/renderer/src/i18n/fr-FR/settings.json
+++ b/src/renderer/src/i18n/fr-FR/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fixe la température à {enabled} lorsque la réflexion est activée et à {disabled} lorsqu’elle est désactivée.",
"label": "Température"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Contrôle l’échantillonnage nucleus. Laissez vide pour utiliser la valeur par défaut du modèle ou du fournisseur."
+ },
"title": "Paramètres du modèle personnalisé",
"type": {
"description": "Sélectionnez le type de modèle",
@@ -647,7 +651,8 @@
"temperatureMin": "La température doit être supérieure ou égale à 0",
"temperatureRequired": "La température ne peut pas être vide",
"timeoutMin": "Le délai d'attente doit être d'au moins 1000ms",
- "timeoutMax": "Le délai d'attente ne peut pas dépasser 3600000ms"
+ "timeoutMax": "Le délai d'attente ne peut pas dépasser 3600000ms",
+ "topPRange": "Top P doit être supérieur à 0 et inférieur ou égal à 1"
},
"vision": {
"description": "Le modèle soutient-il la capacité visuelle?",
diff --git a/src/renderer/src/i18n/he-IL/chat.json b/src/renderer/src/i18n/he-IL/chat.json
index 3ff221677..390d50a3e 100644
--- a/src/renderer/src/i18n/he-IL/chat.json
+++ b/src/renderer/src/i18n/he-IL/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "בחר תבנית",
"temperature": "טמפרטורה",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 מקבע את הטמפרטורה ל-{enabled} כשהחשיבה מופעלת ול-{disabled} כשהיא כבויה.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "אורך הקשר",
"maxTokens": "מקסימום טוקנים",
"thinkingBudget": "תקציב חשיבה",
@@ -182,7 +184,8 @@
"finiteNumber": "חייב להיות מספר סופי",
"nonNegativeInteger": "חייב להיות מספר שלם לא שלילי",
"contextLengthAtLeastMaxTokens": "חייב להיות גדול או שווה למספר הטוקנים המרבי",
- "maxTokensWithinContextLength": "חייב להיות קטן או שווה לאורך ההקשר"
+ "maxTokensWithinContextLength": "חייב להיות קטן או שווה לאורך ההקשר",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/he-IL/settings.json b/src/renderer/src/i18n/he-IL/settings.json
index f2fd530dd..1bb7fdb80 100644
--- a/src/renderer/src/i18n/he-IL/settings.json
+++ b/src/renderer/src/i18n/he-IL/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 מקבע את הטמפרטורה ל-{enabled} כשהחשיבה מופעלת ול-{disabled} כשהיא כבויה.",
"label": "טמפרטורה"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"title": "פרמטרים מותאמים למודל",
"type": {
"description": "בחר את סוג המודל",
@@ -647,7 +651,8 @@
"temperatureMin": "הטמפרטורה חייבת להיות גדולה או שווה ל-0",
"temperatureRequired": "הטמפרטורה אינה יכולה להיות ריקה",
"timeoutMin": "הטיימאוט חייב להיות לפחות 1000ms",
- "timeoutMax": "הטיימאוט אינו יכול לעלות על 3600000ms"
+ "timeoutMax": "הטיימאוט אינו יכול לעלות על 3600000ms",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"vision": {
"description": "האם המודל תומך ביכולת חזותית?",
diff --git a/src/renderer/src/i18n/id-ID/chat.json b/src/renderer/src/i18n/id-ID/chat.json
index 2eda9bb3a..52af0998a 100644
--- a/src/renderer/src/i18n/id-ID/chat.json
+++ b/src/renderer/src/i18n/id-ID/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Pilih preset",
"temperature": "suhu",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ditetapkan ke {enabled} saat pemikiran diaktifkan, dan ditetapkan ke {disabled} saat pemikiran dimatikan.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "panjang konteks",
"maxTokens": "keluaran maksimal",
"thinkingBudget": "Pikirkan tentang anggaran",
@@ -193,7 +195,8 @@
"finiteNumber": "Harus berupa angka yang terbatas",
"nonNegativeInteger": "Harus berupa bilangan bulat non-negatif",
"contextLengthAtLeastMaxTokens": "Harus lebih besar atau sama dengan output maksimum",
- "maxTokensWithinContextLength": "Harus kurang dari atau sama dengan panjang konteks"
+ "maxTokensWithinContextLength": "Harus kurang dari atau sama dengan panjang konteks",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/id-ID/settings.json b/src/renderer/src/i18n/id-ID/settings.json
index b40fe2bbe..deef01812 100644
--- a/src/renderer/src/i18n/id-ID/settings.json
+++ b/src/renderer/src/i18n/id-ID/settings.json
@@ -623,6 +623,10 @@
"description": "Kontrol keacakan output. Kebanyakan model mendukung 0-1, dan beberapa mendukung 0-2. Semakin tinggi nilainya, semakin acak.",
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ditetapkan ke {enabled} saat pemikiran diaktifkan, dan ditetapkan ke {disabled} saat pemikiran dimatikan."
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"vision": {
"label": "kemampuan visual",
"description": "Apakah model tersebut mendukung kemampuan visual?"
@@ -723,7 +727,8 @@
"temperatureMin": "Suhu harus lebih besar dari atau sama dengan 0",
"temperatureMax": "Suhu harus kurang dari atau sama dengan 2",
"timeoutMin": "Batas waktu permintaan tidak boleh kurang dari 1000 milidetik",
- "timeoutMax": "Batas waktu permintaan tidak boleh melebihi 3600000 milidetik"
+ "timeoutMax": "Batas waktu permintaan tidak boleh melebihi 3600000 milidetik",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"endpointType": {
"label": "jenis protokol",
diff --git a/src/renderer/src/i18n/it-IT/chat.json b/src/renderer/src/i18n/it-IT/chat.json
index 075ab8e72..1a2ea0fbd 100644
--- a/src/renderer/src/i18n/it-IT/chat.json
+++ b/src/renderer/src/i18n/it-IT/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Seleziona preset",
"temperature": "Temperatura",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 usa {enabled} quando il ragionamento è attivo e {disabled} quando è disattivo.",
+ "topP": "Top P",
+ "topPDescription": "Campionamento nucleus; lascia vuoto per usare il valore predefinito del provider",
"contextLength": "Lunghezza contesto",
"maxTokens": "Output massimo",
"thinkingBudget": "Budget ragionamento",
@@ -193,7 +195,8 @@
"finiteNumber": "Deve essere un numero finito",
"nonNegativeInteger": "Deve essere un intero non negativo",
"contextLengthAtLeastMaxTokens": "Deve essere maggiore o uguale all'output massimo",
- "maxTokensWithinContextLength": "Deve essere minore o uguale alla lunghezza contesto"
+ "maxTokensWithinContextLength": "Deve essere minore o uguale alla lunghezza contesto",
+ "topPRange": "Deve essere maggiore di 0 e minore o uguale a 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/it-IT/settings.json b/src/renderer/src/i18n/it-IT/settings.json
index 1407f5bc3..48cee6ed6 100644
--- a/src/renderer/src/i18n/it-IT/settings.json
+++ b/src/renderer/src/i18n/it-IT/settings.json
@@ -623,6 +623,10 @@
"description": "Controlla la casualità dell'output. La maggior parte dei modelli usa 0-1, alcuni supportano 0-2; più alto significa più casuale",
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 usa {enabled} quando il ragionamento è attivo e {disabled} quando è disattivo."
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controlla il campionamento nucleus. Lascia vuoto per usare il valore predefinito del modello o del provider."
+ },
"vision": {
"label": "Capacità visiva",
"description": "Indica se il modello supporta capacità visive"
@@ -723,7 +727,8 @@
"temperatureMin": "La temperatura deve essere maggiore o uguale a 0",
"temperatureMax": "La temperatura deve essere minore o uguale a 2",
"timeoutMin": "Il timeout richiesta non può essere inferiore a 1000 millisecondi",
- "timeoutMax": "Il timeout richiesta non può superare 3600000 millisecondi"
+ "timeoutMax": "Il timeout richiesta non può superare 3600000 millisecondi",
+ "topPRange": "Top P deve essere maggiore di 0 e minore o uguale a 1"
},
"endpointType": {
"label": "Tipo protocollo",
diff --git a/src/renderer/src/i18n/ja-JP/chat.json b/src/renderer/src/i18n/ja-JP/chat.json
index 2dd76bdcd..eeeeb8150 100644
--- a/src/renderer/src/i18n/ja-JP/chat.json
+++ b/src/renderer/src/i18n/ja-JP/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "プリセットを選択",
"temperature": "温度",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 は、思考を有効にすると温度が {enabled} に固定され、無効にすると {disabled} に固定されます。",
+ "topP": "Top P",
+ "topPDescription": "核サンプリング。空欄でプロバイダーの既定値を使用",
"contextLength": "コンテキスト長",
"maxTokens": "最大トークン",
"thinkingBudget": "思考予算",
@@ -182,7 +184,8 @@
"finiteNumber": "有限の数値である必要があります",
"nonNegativeInteger": "0以上の整数である必要があります",
"contextLengthAtLeastMaxTokens": "最大トークン数以上である必要があります",
- "maxTokensWithinContextLength": "コンテキスト長以下である必要があります"
+ "maxTokensWithinContextLength": "コンテキスト長以下である必要があります",
+ "topPRange": "0 より大きく 1 以下である必要があります"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/ja-JP/settings.json b/src/renderer/src/i18n/ja-JP/settings.json
index 342320882..1bab9cf3c 100644
--- a/src/renderer/src/i18n/ja-JP/settings.json
+++ b/src/renderer/src/i18n/ja-JP/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 は、思考を有効にすると温度が {enabled} に固定され、無効にすると {disabled} に固定されます。",
"label": "温度"
},
+ "topP": {
+ "label": "Top P",
+ "description": "核サンプリングを制御します。空欄でモデルまたはプロバイダーの既定値を使用します。"
+ },
"title": "カスタムモデルパラメーター",
"type": {
"description": "モデルのタイプを選択します",
@@ -647,7 +651,8 @@
"temperatureMin": "温度は0以上でなければなりません",
"temperatureRequired": "温度を空にすることはできません",
"timeoutMin": "タイムアウトは最低1000msでなければなりません",
- "timeoutMax": "タイムアウトは3600000msを超えることはできません"
+ "timeoutMax": "タイムアウトは3600000msを超えることはできません",
+ "topPRange": "Top P は 0 より大きく 1 以下である必要があります"
},
"vision": {
"description": "モデルは視覚能力をサポートしていますか?",
diff --git a/src/renderer/src/i18n/ko-KR/chat.json b/src/renderer/src/i18n/ko-KR/chat.json
index 65249f0f2..0339ce4d1 100644
--- a/src/renderer/src/i18n/ko-KR/chat.json
+++ b/src/renderer/src/i18n/ko-KR/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "프리셋 선택",
"temperature": "온도",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6은 생각이 활성화되면 온도를 {enabled}로, 비활성화되면 {disabled}로 고정합니다.",
+ "topP": "Top P",
+ "topPDescription": "핵 샘플링입니다. 비워 두면 제공업체 기본값을 사용합니다",
"contextLength": "컨텍스트 길이",
"maxTokens": "최대 토큰",
"thinkingBudget": "사고 예산",
@@ -182,7 +184,8 @@
"finiteNumber": "유한한 숫자여야 합니다",
"nonNegativeInteger": "0 이상의 정수여야 합니다",
"contextLengthAtLeastMaxTokens": "최대 출력 이상이어야 합니다",
- "maxTokensWithinContextLength": "컨텍스트 길이 이하여야 합니다"
+ "maxTokensWithinContextLength": "컨텍스트 길이 이하여야 합니다",
+ "topPRange": "0보다 크고 1 이하여야 합니다"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/ko-KR/settings.json b/src/renderer/src/i18n/ko-KR/settings.json
index 43dca7d00..819ad7536 100644
--- a/src/renderer/src/i18n/ko-KR/settings.json
+++ b/src/renderer/src/i18n/ko-KR/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6은 생각이 활성화되면 온도를 {enabled}로, 비활성화되면 {disabled}로 고정합니다.",
"label": "온도"
},
+ "topP": {
+ "label": "Top P",
+ "description": "핵 샘플링 범위를 제어합니다. 비워 두면 모델 또는 제공업체 기본값을 사용합니다."
+ },
"title": "사용자 정의 모델 매개 변수",
"type": {
"description": "모델 유형을 선택하십시오",
@@ -647,7 +651,8 @@
"temperatureMin": "온도는 0보다 크거나 같아야합니다.",
"temperatureRequired": "온도는 비어있을 수 없습니다",
"timeoutMin": "요청 시간 초과는 최소 1000ms여야 합니다",
- "timeoutMax": "요청 시간 초과는 3600000ms를 초과할 수 없습니다"
+ "timeoutMax": "요청 시간 초과는 3600000ms를 초과할 수 없습니다",
+ "topPRange": "Top P는 0보다 크고 1 이하여야 합니다"
},
"vision": {
"description": "모델이 시각적 능력을 지원합니까?",
diff --git a/src/renderer/src/i18n/ms-MY/chat.json b/src/renderer/src/i18n/ms-MY/chat.json
index d75044087..7ae4d8b87 100644
--- a/src/renderer/src/i18n/ms-MY/chat.json
+++ b/src/renderer/src/i18n/ms-MY/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Pilih pratetap",
"temperature": "suhu",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ditetapkan pada {enabled} apabila pemikiran dihidupkan dan {disabled} apabila pemikiran dimatikan.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "panjang konteks",
"maxTokens": "keluaran maksimum",
"thinkingBudget": "Fikirkan tentang bajet",
@@ -193,7 +195,8 @@
"finiteNumber": "Mesti nombor terhingga",
"nonNegativeInteger": "Mestilah integer bukan negatif",
"contextLengthAtLeastMaxTokens": "Mesti lebih besar daripada atau sama dengan keluaran maksimum",
- "maxTokensWithinContextLength": "Mesti kurang daripada atau sama dengan panjang konteks"
+ "maxTokensWithinContextLength": "Mesti kurang daripada atau sama dengan panjang konteks",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/ms-MY/settings.json b/src/renderer/src/i18n/ms-MY/settings.json
index 65dfc0de4..dbba327a5 100644
--- a/src/renderer/src/i18n/ms-MY/settings.json
+++ b/src/renderer/src/i18n/ms-MY/settings.json
@@ -623,6 +623,10 @@
"description": "Kawal rawak keluaran. Kebanyakan model menyokong 0-1, dan beberapa menyokong 0-2. Semakin tinggi nilai, semakin rawak.",
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ditetapkan pada {enabled} apabila pemikiran dihidupkan dan {disabled} apabila pemikiran dimatikan."
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"vision": {
"label": "keupayaan visual",
"description": "Adakah model menyokong keupayaan visual?"
@@ -723,7 +727,8 @@
"temperatureMin": "Suhu mestilah lebih besar daripada atau sama dengan 0",
"temperatureMax": "Suhu mestilah kurang daripada atau sama dengan 2",
"timeoutMin": "Tamat masa permintaan tidak boleh kurang daripada 1000 milisaat",
- "timeoutMax": "Tamat masa permintaan tidak boleh melebihi 3600000 milisaat"
+ "timeoutMax": "Tamat masa permintaan tidak boleh melebihi 3600000 milisaat",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"endpointType": {
"label": "jenis protokol",
diff --git a/src/renderer/src/i18n/pl-PL/chat.json b/src/renderer/src/i18n/pl-PL/chat.json
index 0f5f52ae6..3b8f461f1 100644
--- a/src/renderer/src/i18n/pl-PL/chat.json
+++ b/src/renderer/src/i18n/pl-PL/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Wybierz ustawienie wstępne",
"temperature": "Temperatura",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ustala temperaturę na {enabled}, gdy myślenie jest włączone, i {disabled}, gdy jest wyłączone.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "Długość kontekstu",
"maxTokens": "Maks. Tokens",
"thinkingBudget": "Myślący budżet",
@@ -193,7 +195,8 @@
"finiteNumber": "Musi to być liczba skończona",
"nonNegativeInteger": "Musi być nieujemną liczbą całkowitą",
"contextLengthAtLeastMaxTokens": "Musi być większy lub równy Max Tokens",
- "maxTokensWithinContextLength": "Musi być mniejsza lub równa Długość kontekstu"
+ "maxTokensWithinContextLength": "Musi być mniejsza lub równa Długość kontekstu",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/pl-PL/settings.json b/src/renderer/src/i18n/pl-PL/settings.json
index 4d0721b8d..86a69046e 100644
--- a/src/renderer/src/i18n/pl-PL/settings.json
+++ b/src/renderer/src/i18n/pl-PL/settings.json
@@ -687,6 +687,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 ustala temperaturę na {enabled}, gdy myślenie jest włączone, i {disabled}, gdy jest wyłączone.",
"label": "Temperatura"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"title": "Niestandardowe parametry modelu",
"type": {
"description": "Wybierz typ modelu",
@@ -710,7 +714,8 @@
"temperatureMin": "Temperatura musi być większa lub równa 0",
"temperatureRequired": "Temperatura nie może być pusta",
"timeoutMin": "Limit czasu żądania musi wynosić co najmniej 1000 ms",
- "timeoutMax": "Limit czasu żądania nie może przekroczyć 3600000 ms"
+ "timeoutMax": "Limit czasu żądania nie może przekroczyć 3600000 ms",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"vision": {
"description": "Czy model obsługuje zdolności wizualne?",
diff --git a/src/renderer/src/i18n/pt-BR/chat.json b/src/renderer/src/i18n/pt-BR/chat.json
index 97b3eef12..c41f4463a 100644
--- a/src/renderer/src/i18n/pt-BR/chat.json
+++ b/src/renderer/src/i18n/pt-BR/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "Selecione um preset",
"temperature": "Temperatura",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fixa a temperatura em {enabled} quando o modo de pensamento está ativado e em {disabled} quando está desativado.",
+ "topP": "Top P",
+ "topPDescription": "Amostragem nucleus; deixe em branco para usar o padrão do provedor",
"contextLength": "Tamanho do contexto",
"maxTokens": "Máx. de tokens",
"thinkingBudget": "Orçamento de raciocínio",
@@ -182,7 +184,8 @@
"finiteNumber": "Deve ser um número finito",
"nonNegativeInteger": "Deve ser um inteiro não negativo",
"contextLengthAtLeastMaxTokens": "Deve ser maior ou igual ao Máximo de saída",
- "maxTokensWithinContextLength": "Deve ser menor ou igual ao Tamanho de contexto"
+ "maxTokensWithinContextLength": "Deve ser menor ou igual ao Tamanho de contexto",
+ "topPRange": "Deve ser maior que 0 e menor ou igual a 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/pt-BR/settings.json b/src/renderer/src/i18n/pt-BR/settings.json
index df2c402dd..4dabec081 100644
--- a/src/renderer/src/i18n/pt-BR/settings.json
+++ b/src/renderer/src/i18n/pt-BR/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 fixa a temperatura em {enabled} quando o modo de pensamento está ativado e em {disabled} quando está desativado.",
"label": "Temperatura"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controla a amostragem nucleus. Deixe em branco para usar o padrão do modelo ou provedor."
+ },
"title": "Parâmetros personalizados do modelo",
"type": {
"description": "Selecionar o tipo de modelo",
@@ -647,7 +651,8 @@
"temperatureMin": "A temperatura deve ser maior ou igual a 0",
"temperatureRequired": "A temperatura não pode estar vazia",
"timeoutMin": "O tempo limite da solicitação deve ser de pelo menos 1000ms",
- "timeoutMax": "O tempo limite da solicitação não pode exceder 3600000ms"
+ "timeoutMax": "O tempo limite da solicitação não pode exceder 3600000ms",
+ "topPRange": "Top P deve ser maior que 0 e menor ou igual a 1"
},
"vision": {
"description": "O modelo suporta capacidade visual?",
diff --git a/src/renderer/src/i18n/ru-RU/chat.json b/src/renderer/src/i18n/ru-RU/chat.json
index 167c4c329..e24d42d65 100644
--- a/src/renderer/src/i18n/ru-RU/chat.json
+++ b/src/renderer/src/i18n/ru-RU/chat.json
@@ -166,6 +166,8 @@
"systemPromptPlaceholder": "Выберите пресет",
"temperature": "Температура",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 фиксирует температуру на уровне {enabled}, когда мышление включено, и на уровне {disabled}, когда оно отключено.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; оставьте пустым, чтобы использовать значение провайдера по умолчанию",
"contextLength": "Длина контекста",
"maxTokens": "Макс. токенов",
"thinkingBudget": "Бюджет размышления",
@@ -182,7 +184,8 @@
"finiteNumber": "Должно быть конечным числом",
"nonNegativeInteger": "Должно быть неотрицательным целым числом",
"contextLengthAtLeastMaxTokens": "Должно быть больше или равно максимальному числу токенов",
- "maxTokensWithinContextLength": "Должно быть меньше или равно длине контекста"
+ "maxTokensWithinContextLength": "Должно быть меньше или равно длине контекста",
+ "topPRange": "Должно быть больше 0 и меньше либо равно 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/ru-RU/settings.json b/src/renderer/src/i18n/ru-RU/settings.json
index 0a49ec7af..02ab64a7a 100644
--- a/src/renderer/src/i18n/ru-RU/settings.json
+++ b/src/renderer/src/i18n/ru-RU/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 фиксирует температуру на уровне {enabled}, когда мышление включено, и на уровне {disabled}, когда оно отключено.",
"label": "температура"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Управляет nucleus sampling. Оставьте пустым, чтобы использовать значение модели или провайдера по умолчанию."
+ },
"title": "Пользовательские параметры модели",
"type": {
"description": "Выберите тип модели",
@@ -647,7 +651,8 @@
"temperatureMin": "Температура должна быть больше или равна 0",
"temperatureRequired": "Температура не может быть пустой",
"timeoutMin": "Тайм-аут запроса должен быть не менее 1000ms",
- "timeoutMax": "Тайм-аут запроса не может превышать 3600000ms"
+ "timeoutMax": "Тайм-аут запроса не может превышать 3600000ms",
+ "topPRange": "Top P должно быть больше 0 и меньше либо равно 1"
},
"vision": {
"description": "Поддерживает ли модель визуальные способности?",
diff --git a/src/renderer/src/i18n/tr-TR/chat.json b/src/renderer/src/i18n/tr-TR/chat.json
index 65a3ab7c4..e2245b1bb 100644
--- a/src/renderer/src/i18n/tr-TR/chat.json
+++ b/src/renderer/src/i18n/tr-TR/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Bir ön ayar seçin",
"temperature": "Sıcaklık",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6, düşünme etkinleştirildiğinde sıcaklığı {enabled}'ye ve devre dışı bırakıldığında {disabled}'ye sabitler.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus örnekleme; sağlayıcı varsayılanını kullanmak için boş bırakın",
"contextLength": "Bağlam Uzunluğu",
"maxTokens": "Maksimum token",
"thinkingBudget": "Thinking Budget",
@@ -193,7 +195,8 @@
"finiteNumber": "Sonlu bir sayı olmalı",
"nonNegativeInteger": "Negatif olmayan bir tam sayı olmalıdır",
"contextLengthAtLeastMaxTokens": "Maksimum token değerinden büyük veya ona eşit olmalıdır",
- "maxTokensWithinContextLength": "Bağlam Uzunluğundan küçük veya ona eşit olmalıdır"
+ "maxTokensWithinContextLength": "Bağlam Uzunluğundan küçük veya ona eşit olmalıdır",
+ "topPRange": "0’dan büyük ve 1’e eşit ya da küçük olmalıdır"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/tr-TR/settings.json b/src/renderer/src/i18n/tr-TR/settings.json
index ccc6d226e..335b5f8e1 100644
--- a/src/renderer/src/i18n/tr-TR/settings.json
+++ b/src/renderer/src/i18n/tr-TR/settings.json
@@ -623,6 +623,10 @@
"description": "Çıktının rastgeleliğini kontrol edin. Çoğu model 0-1'dir ve bazıları 0-2 arasındadır. Daha yüksek değerler rastgeleliği artırır.",
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6, düşünme etkinleştirildiğinde sıcaklığı {enabled}'ye ve devre dışı bırakıldığında {disabled}'ye sabitler."
},
+ "topP": {
+ "label": "Top P",
+ "description": "Nucleus örneklemeyi kontrol eder. Model veya sağlayıcı varsayılanını kullanmak için boş bırakın."
+ },
"vision": {
"label": "Görsel yetenek",
"description": "Model görsel yeteneği destekliyor mu?"
@@ -723,7 +727,8 @@
"temperatureMin": "Sıcaklık 0'dan büyük veya ona eşit olmalıdır",
"temperatureMax": "Sıcaklık 2'ye eşit veya daha az olmalıdır",
"timeoutMin": "İstek zaman aşımı en az 1000 ms olmalıdır",
- "timeoutMax": "İstek zaman aşımı 3600000 ms'yi aşamaz"
+ "timeoutMax": "İstek zaman aşımı 3600000 ms'yi aşamaz",
+ "topPRange": "Top P 0’dan büyük ve 1’e eşit ya da küçük olmalıdır"
},
"endpointType": {
"label": "Uç Nokta Türü",
diff --git a/src/renderer/src/i18n/vi-VN/chat.json b/src/renderer/src/i18n/vi-VN/chat.json
index f045d7318..0dc9765d6 100644
--- a/src/renderer/src/i18n/vi-VN/chat.json
+++ b/src/renderer/src/i18n/vi-VN/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "Chọn một cài đặt trước",
"temperature": "Nhiệt độ",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 sửa nhiệt độ thành {enabled} khi tính năng suy nghĩ được bật và {disabled} khi tính năng này bị tắt.",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "Độ dài ngữ cảnh",
"maxTokens": "Tối đa Tokens",
"thinkingBudget": "Suy nghĩ về ngân sách",
@@ -193,7 +195,8 @@
"finiteNumber": "Phải là số hữu hạn",
"nonNegativeInteger": "Phải là số nguyên không âm",
"contextLengthAtLeastMaxTokens": "Phải lớn hơn hoặc bằng Max Tokens",
- "maxTokensWithinContextLength": "Phải nhỏ hơn hoặc bằng Độ dài ngữ cảnh"
+ "maxTokensWithinContextLength": "Phải nhỏ hơn hoặc bằng Độ dài ngữ cảnh",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/vi-VN/settings.json b/src/renderer/src/i18n/vi-VN/settings.json
index b5039e538..1499387a9 100644
--- a/src/renderer/src/i18n/vi-VN/settings.json
+++ b/src/renderer/src/i18n/vi-VN/settings.json
@@ -687,6 +687,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 sửa nhiệt độ thành {enabled} khi tính năng suy nghĩ được bật và {disabled} khi tính năng này bị tắt.",
"label": "Nhiệt độ"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"title": "Thông số mô hình tùy chỉnh",
"type": {
"description": "Chọn loại mô hình",
@@ -710,7 +714,8 @@
"temperatureMin": "Nhiệt độ phải lớn hơn hoặc bằng 0",
"temperatureRequired": "Nhiệt độ không thể trống",
"timeoutMin": "Thời gian chờ yêu cầu phải ít nhất là 1000ms",
- "timeoutMax": "Thời gian chờ yêu cầu không thể vượt quá 3600000ms"
+ "timeoutMax": "Thời gian chờ yêu cầu không thể vượt quá 3600000ms",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"vision": {
"description": "Mô hình có hỗ trợ khả năng thị giác không?",
diff --git a/src/renderer/src/i18n/zh-CN/chat.json b/src/renderer/src/i18n/zh-CN/chat.json
index 9d624d5f8..0388b6f15 100644
--- a/src/renderer/src/i18n/zh-CN/chat.json
+++ b/src/renderer/src/i18n/zh-CN/chat.json
@@ -177,6 +177,8 @@
"systemPromptPlaceholder": "选择预设",
"temperature": "温度",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 在开启思考时固定为 {enabled},关闭思考时固定为 {disabled}。",
+ "topP": "Top P(核采样)",
+ "topPDescription": "留空使用服务商默认值",
"contextLength": "上下文长度",
"maxTokens": "最大输出",
"thinkingBudget": "思考预算",
@@ -193,7 +195,8 @@
"finiteNumber": "必须是有限数字",
"nonNegativeInteger": "必须是非负整数",
"contextLengthAtLeastMaxTokens": "必须大于或等于最大输出",
- "maxTokensWithinContextLength": "必须小于或等于上下文长度"
+ "maxTokensWithinContextLength": "必须小于或等于上下文长度",
+ "topPRange": "必须大于 0 且小于或等于 1"
}
},
"modelPicker": {
diff --git a/src/renderer/src/i18n/zh-CN/settings.json b/src/renderer/src/i18n/zh-CN/settings.json
index 703163ca3..1879035a3 100644
--- a/src/renderer/src/i18n/zh-CN/settings.json
+++ b/src/renderer/src/i18n/zh-CN/settings.json
@@ -623,6 +623,10 @@
"description": "控制输出的随机性,大部分模型0-1,部分支持0-2之间,越高越随机",
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 在开启思考时固定为 {enabled},关闭思考时固定为 {disabled}。"
},
+ "topP": {
+ "label": "Top P(核采样)",
+ "description": "控制核采样范围。留空使用模型或服务商默认值。"
+ },
"vision": {
"label": "视觉能力",
"description": "模型是否支持视觉能力"
@@ -723,7 +727,8 @@
"temperatureMin": "温度必须大于等于0",
"temperatureMax": "温度必须小于等于2",
"timeoutMin": "请求超时时间不能小于1000毫秒",
- "timeoutMax": "请求超时时间不能超过3600000毫秒"
+ "timeoutMax": "请求超时时间不能超过3600000毫秒",
+ "topPRange": "Top P 必须大于 0 且小于或等于 1"
},
"endpointType": {
"label": "协议类型",
diff --git a/src/renderer/src/i18n/zh-HK/chat.json b/src/renderer/src/i18n/zh-HK/chat.json
index a7e95e462..fa1236f26 100644
--- a/src/renderer/src/i18n/zh-HK/chat.json
+++ b/src/renderer/src/i18n/zh-HK/chat.json
@@ -174,6 +174,8 @@
"systemPromptPlaceholder": "選擇預設",
"temperature": "溫度",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 在開啟思考時固定為 {enabled},關閉思考時固定為 {disabled}。",
+ "topP": "Top P",
+ "topPDescription": "Nucleus sampling; leave blank to use provider default",
"contextLength": "上下文長度",
"maxTokens": "最大輸出",
"thinkingBudget": "思考預算",
@@ -190,7 +192,8 @@
"finiteNumber": "必須是有限數字",
"nonNegativeInteger": "必須是非負整數",
"contextLengthAtLeastMaxTokens": "必須大於或等於最大輸出",
- "maxTokensWithinContextLength": "必須小於或等於上下文長度"
+ "maxTokensWithinContextLength": "必須小於或等於上下文長度",
+ "topPRange": "Must be greater than 0 and less than or equal to 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/zh-HK/settings.json b/src/renderer/src/i18n/zh-HK/settings.json
index 7f0ee152a..07d58bbcb 100644
--- a/src/renderer/src/i18n/zh-HK/settings.json
+++ b/src/renderer/src/i18n/zh-HK/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 在開啟思考時固定為 {enabled},關閉思考時固定為 {disabled}。",
"label": "溫度"
},
+ "topP": {
+ "label": "Top P",
+ "description": "Controls nucleus sampling. Leave blank to use the model or provider default."
+ },
"title": "自定義模型參數",
"type": {
"description": "選擇模型的類型",
@@ -647,7 +651,8 @@
"temperatureMin": "溫度必須大於等於0",
"temperatureRequired": "溫度不能為空",
"timeoutMin": "請求逾時時間不能小於1000毫秒",
- "timeoutMax": "請求逾時時間不能超過3600000毫秒"
+ "timeoutMax": "請求逾時時間不能超過3600000毫秒",
+ "topPRange": "Top P must be greater than 0 and less than or equal to 1"
},
"vision": {
"description": "模型是否支持視覺能力",
diff --git a/src/renderer/src/i18n/zh-TW/chat.json b/src/renderer/src/i18n/zh-TW/chat.json
index b27fa206d..2dac31591 100644
--- a/src/renderer/src/i18n/zh-TW/chat.json
+++ b/src/renderer/src/i18n/zh-TW/chat.json
@@ -174,6 +174,8 @@
"systemPromptPlaceholder": "選擇預設",
"temperature": "溫度",
"temperatureFixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 在開啟思考時固定為 {enabled},關閉思考時固定為 {disabled}。",
+ "topP": "Top P(核心採樣)",
+ "topPDescription": "留空使用服務商預設值",
"contextLength": "上下文長度",
"maxTokens": "最大輸出",
"thinkingBudget": "思考預算",
@@ -190,7 +192,8 @@
"finiteNumber": "必須是有限數字",
"nonNegativeInteger": "必須是非負整數",
"contextLengthAtLeastMaxTokens": "必須大於或等於最大輸出",
- "maxTokensWithinContextLength": "必須小於或等於上下文長度"
+ "maxTokensWithinContextLength": "必須小於或等於上下文長度",
+ "topPRange": "必須大於 0 且小於或等於 1"
}
},
"workspace": {
diff --git a/src/renderer/src/i18n/zh-TW/settings.json b/src/renderer/src/i18n/zh-TW/settings.json
index a539e49ed..195bdf724 100644
--- a/src/renderer/src/i18n/zh-TW/settings.json
+++ b/src/renderer/src/i18n/zh-TW/settings.json
@@ -624,6 +624,10 @@
"fixedMoonshotKimi": "Moonshot Kimi K2.5/K2.6 在開啟思考時固定為 {enabled},關閉思考時固定為 {disabled}。",
"label": "溫度"
},
+ "topP": {
+ "label": "Top P(核心採樣)",
+ "description": "控制核心採樣範圍。留空使用模型或服務商預設值。"
+ },
"title": "自定義模型參數",
"type": {
"description": "選擇模型的類型",
@@ -647,7 +651,8 @@
"temperatureMin": "溫度必須大於等於0",
"temperatureRequired": "溫度不能為空",
"timeoutMin": "請求逾時時間不能小於1000毫秒",
- "timeoutMax": "請求逾時時間不能超過3600000毫秒"
+ "timeoutMax": "請求逾時時間不能超過3600000毫秒",
+ "topPRange": "Top P 必須大於 0 且小於或等於 1"
},
"vision": {
"description": "模型是否支持視覺能力",
diff --git a/src/renderer/src/pages/NewThreadPage.vue b/src/renderer/src/pages/NewThreadPage.vue
index 08f11dac9..f4749aca2 100644
--- a/src/renderer/src/pages/NewThreadPage.vue
+++ b/src/renderer/src/pages/NewThreadPage.vue
@@ -861,6 +861,7 @@ const applyDraftDefaultsForSelectedAgent = async (): Promise => {
draftStore.subagentEnabled = false
draftStore.systemPrompt = undefined
draftStore.temperature = undefined
+ draftStore.topP = undefined
draftStore.contextLength = undefined
draftStore.maxTokens = undefined
draftStore.timeout = undefined
diff --git a/src/renderer/src/stores/ui/draft.ts b/src/renderer/src/stores/ui/draft.ts
index e958dd863..1efe15eb2 100644
--- a/src/renderer/src/stores/ui/draft.ts
+++ b/src/renderer/src/stores/ui/draft.ts
@@ -27,6 +27,7 @@ export const useDraftStore = defineStore('draft', () => {
const agentId = ref('deepchat')
const systemPrompt = ref(undefined)
const temperature = ref(undefined)
+ const topP = ref(undefined)
const contextLength = ref(undefined)
const maxTokens = ref(undefined)
const timeout = ref(undefined)
@@ -68,6 +69,7 @@ export const useDraftStore = defineStore('draft', () => {
if (systemPrompt.value !== undefined) settings.systemPrompt = systemPrompt.value
if (temperature.value !== undefined) settings.temperature = temperature.value
+ if (topP.value !== undefined) settings.topP = topP.value
if (contextLength.value !== undefined) settings.contextLength = contextLength.value
if (maxTokens.value !== undefined) settings.maxTokens = maxTokens.value
if (timeout.value !== undefined) settings.timeout = timeout.value
@@ -113,6 +115,9 @@ export const useDraftStore = defineStore('draft', () => {
if (Object.prototype.hasOwnProperty.call(settings, 'temperature')) {
temperature.value = settings.temperature
}
+ if (Object.prototype.hasOwnProperty.call(settings, 'topP')) {
+ topP.value = settings.topP
+ }
if (Object.prototype.hasOwnProperty.call(settings, 'contextLength')) {
contextLength.value = settings.contextLength
}
@@ -148,6 +153,7 @@ export const useDraftStore = defineStore('draft', () => {
function resetGenerationSettings(): void {
systemPrompt.value = undefined
temperature.value = undefined
+ topP.value = undefined
contextLength.value = undefined
maxTokens.value = undefined
timeout.value = undefined
@@ -193,6 +199,7 @@ export const useDraftStore = defineStore('draft', () => {
agentId,
systemPrompt,
temperature,
+ topP,
contextLength,
maxTokens,
timeout,
diff --git a/src/shared/contracts/common.ts b/src/shared/contracts/common.ts
index 147032287..429a89cac 100644
--- a/src/shared/contracts/common.ts
+++ b/src/shared/contracts/common.ts
@@ -130,6 +130,7 @@ export const DeepChatSubagentMetaSchema = z
export const SessionGenerationSettingsSchema = z.object({
systemPrompt: z.string(),
temperature: z.number(),
+ topP: z.number().min(0.1).max(1).optional(),
contextLength: z.number().int(),
maxTokens: z.number().int(),
timeout: z.number().int(),
diff --git a/src/shared/contracts/domainSchemas.ts b/src/shared/contracts/domainSchemas.ts
index 3bd230d8a..f0b024ad4 100644
--- a/src/shared/contracts/domainSchemas.ts
+++ b/src/shared/contracts/domainSchemas.ts
@@ -234,6 +234,7 @@ export const ModelConfigSchema = z
maxTokens: z.number().int(),
contextLength: z.number().int(),
temperature: z.number().optional(),
+ topP: z.number().min(0.1).max(1).optional(),
vision: z.boolean(),
speechRecognition: z.boolean().optional(),
functionCall: z.boolean(),
diff --git a/src/shared/types/agent-interface.d.ts b/src/shared/types/agent-interface.d.ts
index 5b9fe9118..782e22d80 100644
--- a/src/shared/types/agent-interface.d.ts
+++ b/src/shared/types/agent-interface.d.ts
@@ -24,6 +24,7 @@ export interface SessionCompactionState {
export interface SessionGenerationSettings {
systemPrompt: string
temperature: number
+ topP?: number
contextLength: number
maxTokens: number
timeout: number
diff --git a/src/shared/types/presenters/legacy.presenters.d.ts b/src/shared/types/presenters/legacy.presenters.d.ts
index 41541b997..5d75a4311 100644
--- a/src/shared/types/presenters/legacy.presenters.d.ts
+++ b/src/shared/types/presenters/legacy.presenters.d.ts
@@ -161,6 +161,7 @@ export interface ModelConfig {
contextLength: number
timeout?: number
temperature?: number
+ topP?: number
vision: boolean
speechRecognition?: boolean
functionCall: boolean
diff --git a/src/shared/utils/generationSettingsValidation.ts b/src/shared/utils/generationSettingsValidation.ts
index 10da40de8..64097d4e2 100644
--- a/src/shared/utils/generationSettingsValidation.ts
+++ b/src/shared/utils/generationSettingsValidation.ts
@@ -3,6 +3,7 @@ import type { SessionGenerationSettings } from '../types/agent-interface'
export type GenerationNumericField =
| 'temperature'
+ | 'topP'
| 'contextLength'
| 'maxTokens'
| 'timeout'
@@ -15,6 +16,7 @@ export type GenerationNumericValidationCode =
| 'max_tokens_exceed_context_length'
| 'timeout_too_small'
| 'timeout_too_large'
+ | 'top_p_out_of_range'
type GenerationRelationContext = Pick
@@ -58,6 +60,13 @@ export const validateGenerationNumericField = (
return numeric === undefined ? 'finite_number' : null
}
+ if (field === 'topP') {
+ if (numeric === undefined) {
+ return 'finite_number'
+ }
+ return numeric >= 0.1 && numeric <= 1 ? null : 'top_p_out_of_range'
+ }
+
if (!isNonNegativeInteger(numeric)) {
return 'non_negative_integer'
}
diff --git a/src/types/i18n.d.ts b/src/types/i18n.d.ts
index 8615ae91e..873043bdc 100644
--- a/src/types/i18n.d.ts
+++ b/src/types/i18n.d.ts
@@ -281,6 +281,8 @@ declare module 'vue-i18n' {
systemPromptPlaceholder: string
temperature: string
temperatureFixedMoonshotKimi: string
+ topP: string
+ topPDescription: string
contextLength: string
maxTokens: string
thinkingBudget: string
@@ -298,6 +300,7 @@ declare module 'vue-i18n' {
nonNegativeInteger: string
contextLengthAtLeastMaxTokens: string
maxTokensWithinContextLength: string
+ topPRange: string
}
}
modelPicker: {
diff --git a/test/main/presenter/sqlitePresenter/deepchatSessionsTable.test.ts b/test/main/presenter/sqlitePresenter/deepchatSessionsTable.test.ts
index f7e5c5345..170f88c16 100644
--- a/test/main/presenter/sqlitePresenter/deepchatSessionsTable.test.ts
+++ b/test/main/presenter/sqlitePresenter/deepchatSessionsTable.test.ts
@@ -106,10 +106,11 @@ describe('DeepChatSessionsTable.updateSummaryStateIfMatches', () => {
throw new Error(`Unexpected SQL: ${sql}`)
})
- expect(table.getLatestVersion()).toBe(28)
+ expect(table.getLatestVersion()).toBe(29)
expect(table.getMigrationSQL(23)).toBe(
[
+ 'ALTER TABLE deepchat_sessions ADD COLUMN top_p REAL;',
'ALTER TABLE deepchat_sessions ADD COLUMN timeout_ms INTEGER;',
'ALTER TABLE deepchat_sessions ADD COLUMN force_interleaved_thinking_compat INTEGER;',
'ALTER TABLE deepchat_sessions ADD COLUMN reasoning_visibility TEXT;',
@@ -127,6 +128,11 @@ describe('DeepChatSessionsTable.updateSummaryStateIfMatches', () => {
expect(table.getMigrationSQL(28)).toBe(
'ALTER TABLE deepchat_sessions ADD COLUMN video_generation_options_json TEXT;'
)
+ expect(table.getMigrationSQL(29)).toBe('ALTER TABLE deepchat_sessions ADD COLUMN top_p REAL;')
+
+ expect(table.getMigrationSQL(23)).toContain(
+ 'ALTER TABLE deepchat_sessions ADD COLUMN top_p REAL;'
+ )
})
it('reads image generation settings from persisted JSON', () => {
@@ -140,6 +146,7 @@ describe('DeepChatSessionsTable.updateSummaryStateIfMatches', () => {
permission_mode: 'full_access',
system_prompt: null,
temperature: null,
+ top_p: null,
context_length: null,
max_tokens: null,
timeout_ms: null,
@@ -227,7 +234,7 @@ describe('DeepChatSessionsTable.updateSummaryStateIfMatches', () => {
if (sql === 'SELECT MAX(version) as version FROM schema_versions') {
return {
- get: () => ({ version: 29 })
+ get: () => ({ version: 30 })
}
}
@@ -239,11 +246,11 @@ describe('DeepChatSessionsTable.updateSummaryStateIfMatches', () => {
const guardedTable = new DeepChatSessionsTable(guardedDb)
expect(() => guardedTable.createTable()).toThrow(
- 'Recorded deepchat_sessions schema version 29 exceeds supported version 28.'
+ 'Recorded deepchat_sessions schema version 30 exceeds supported version 29.'
)
expect(exec).not.toHaveBeenCalled()
expect(errorSpy).toHaveBeenCalledWith(
- 'Recorded deepchat_sessions schema version 29 exceeds supported version 28. Refusing to create table from a downgraded schema.'
+ 'Recorded deepchat_sessions schema version 30 exceeds supported version 29. Refusing to create table from a downgraded schema.'
)
})
})
diff --git a/test/main/shared/generationSettingsValidation.test.ts b/test/main/shared/generationSettingsValidation.test.ts
index ebf26c45a..562d76d23 100644
--- a/test/main/shared/generationSettingsValidation.test.ts
+++ b/test/main/shared/generationSettingsValidation.test.ts
@@ -17,3 +17,20 @@ describe('validateGenerationNumericField timeout bounds', () => {
)
})
})
+
+describe('validateGenerationNumericField topP bounds', () => {
+ it('accepts finite topP values in the supported range', () => {
+ expect(validateGenerationNumericField('topP', 0.1)).toBeNull()
+ expect(validateGenerationNumericField('topP', 1)).toBeNull()
+ expect(validateGenerationNumericField('topP', '0.5')).toBeNull()
+ })
+
+ it('rejects non-finite and out-of-range topP values', () => {
+ expect(validateGenerationNumericField('topP', '')).toBe('finite_number')
+ expect(validateGenerationNumericField('topP', Number.NaN)).toBe('finite_number')
+ expect(validateGenerationNumericField('topP', 0)).toBe('top_p_out_of_range')
+ expect(validateGenerationNumericField('topP', 0.01)).toBe('top_p_out_of_range')
+ expect(validateGenerationNumericField('topP', -0.1)).toBe('top_p_out_of_range')
+ expect(validateGenerationNumericField('topP', 1.01)).toBe('top_p_out_of_range')
+ })
+})
diff --git a/test/renderer/stores/draft.test.ts b/test/renderer/stores/draft.test.ts
new file mode 100644
index 000000000..3cbe9173e
--- /dev/null
+++ b/test/renderer/stores/draft.test.ts
@@ -0,0 +1,34 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
+describe('draft store generation settings', () => {
+ beforeEach(() => {
+ vi.resetModules()
+ vi.doMock('pinia', async () => {
+ const actual = await vi.importActual('pinia')
+ return actual
+ })
+ })
+
+ it('includes topP overrides in new session input', async () => {
+ const { setActivePinia, createPinia } = await import('pinia')
+ setActivePinia(createPinia())
+ const { useDraftStore } = await import('@/stores/ui/draft')
+ const draftStore = useDraftStore()
+
+ draftStore.updateGenerationSettings({ topP: 0.72 })
+
+ expect(draftStore.toCreateInput('hello').generationSettings).toEqual({ topP: 0.72 })
+ })
+
+ it('omits topP after clearing the override', async () => {
+ const { setActivePinia, createPinia } = await import('pinia')
+ setActivePinia(createPinia())
+ const { useDraftStore } = await import('@/stores/ui/draft')
+ const draftStore = useDraftStore()
+
+ draftStore.updateGenerationSettings({ topP: 0.72 })
+ draftStore.updateGenerationSettings({ topP: undefined })
+
+ expect(draftStore.toCreateInput('hello').generationSettings).toBeUndefined()
+ })
+})
From 61f8c8e5d456c42f39fab78535137f381407c9d3 Mon Sep 17 00:00:00 2001
From: duskzhen
Date: Thu, 28 May 2026 11:14:51 +0800
Subject: [PATCH 03/12] docs: clean stale sdd docs (#1688)
---
docs/ARCHITECTURE.md | 134 +-
docs/FLOWS.md | 207 +-
docs/README.md | 158 +-
docs/architecture/acp-client-runtime/plan.md | 32 -
docs/architecture/acp-client-runtime/spec.md | 47 -
docs/architecture/acp-client-runtime/tasks.md | 16 -
.../agent-provider-simplification/plan.md | 14 -
.../agent-provider-simplification/spec.md | 43 -
.../agent-provider-simplification/tasks.md | 7 -
docs/architecture/agent-refactor/plan.md | 29 -
docs/architecture/agent-refactor/spec.md | 39 -
docs/architecture/agent-refactor/tasks.md | 14 -
.../agent-refactor/tool-result-envelope.md | 112 -
docs/architecture/agent-system.md | 20 +-
.../architecture-simplification/plan.md | 37 -
.../architecture-simplification/spec.md | 40 -
.../architecture-simplification/tasks.md | 31 -
.../chat-store-zero-migration/plan.md | 14 -
.../chat-store-zero-migration/spec.md | 21 -
.../chat-store-zero-migration/tasks.md | 7 -
.../config-sqlite-storage/plan.md | 34 -
.../config-sqlite-storage/spec.md | 32 -
.../config-sqlite-storage/tasks.md | 9 -
docs/architecture/event-system.md | 3 +-
.../main-kernel-refactor/acceptance.md | 216 --
.../main-kernel-refactor/build-vs-buy.md | 136 -
.../eventbus-migration.md | 134 -
.../migration-governance.md | 189 --
.../architecture/main-kernel-refactor/plan.md | 236 --
.../ports-and-scheduler.md | 221 --
.../route-schema-catalog.md | 208 --
.../architecture/main-kernel-refactor/spec.md | 216 --
.../main-kernel-refactor/tasks.md | 84 -
.../main-kernel-refactor/test-plan.md | 264 --
.../new-ui-implementation-plan.md | 619 ----
docs/architecture/remove-rebrand-tool/plan.md | 20 -
docs/architecture/remove-rebrand-tool/spec.md | 27 -
.../architecture/remove-rebrand-tool/tasks.md | 7 -
.../renderer-main-single-track/plan.md | 468 ---
.../renderer-main-single-track/spec.md | 198 --
.../renderer-main-single-track/tasks.md | 122 -
docs/architecture/session-management.md | 11 +
.../skill-runtime-hardening/plan.md | 32 -
.../skill-runtime-hardening/spec.md | 48 -
.../skill-runtime-hardening/tasks.md | 9 -
.../sqlite-database-encryption/plan.md | 247 --
.../sqlite-database-encryption/spec.md | 117 -
.../sqlite-database-encryption/tasks.md | 79 -
.../startup-orchestration/acceptance.md | 126 -
.../startup-orchestration/plan.md | 213 --
.../startup-orchestration/spec.md | 219 --
.../startup-orchestration/tasks.md | 98 -
.../sync-config-import-versioning/plan.md | 24 -
.../sync-config-import-versioning/spec.md | 22 -
.../sync-config-import-versioning/tasks.md | 8 -
docs/archives/agent-cleanup/plan.md | 43 -
docs/archives/agent-cleanup/spec.md | 68 -
docs/archives/agent-cleanup/tasks.md | 45 -
docs/archives/agent-tooling-v2/plan.md | 398 ---
docs/archives/agent-tooling-v2/spec.md | 300 --
docs/archives/agent-tooling-v2/tasks.md | 60 -
.../agentpresenter-mvp-replacement/plan.md | 151 -
.../agentpresenter-mvp-replacement/spec.md | 163 -
.../agentpresenter-mvp-replacement/tasks.md | 247 --
docs/archives/ai-sdk-runtime/plan.md | 13 -
docs/archives/ai-sdk-runtime/spec.md | 96 -
docs/archives/ai-sdk-runtime/tasks.md | 20 -
docs/archives/chat-audio-tts-routing/plan.md | 21 -
docs/archives/chat-audio-tts-routing/spec.md | 26 -
docs/archives/chat-audio-tts-routing/tasks.md | 8 -
.../cua-runtime-plugin/non-macos-handoff.md | 55 -
docs/archives/cua-runtime-plugin/plan.md | 546 ----
docs/archives/cua-runtime-plugin/spec.md | 444 ---
docs/archives/cua-runtime-plugin/tasks.md | 332 --
docs/archives/default-model-settings/plan.md | 130 -
docs/archives/default-model-settings/spec.md | 92 -
docs/archives/default-model-settings/tasks.md | 60 -
.../i18n-missing-translations/plan.md | 25 -
.../i18n-missing-translations/spec.md | 22 -
.../i18n-missing-translations/tasks.md | 8 -
.../legacy-agentpresenter-architecture.md | 320 --
docs/archives/legacy-agentpresenter-flows.md | 654 ----
.../legacy-agentpresenter-retirement/plan.md | 30 -
.../legacy-agentpresenter-retirement/spec.md | 56 -
.../legacy-agentpresenter-retirement/tasks.md | 41 -
.../plan.md | 23 -
.../spec.md | 48 -
.../tasks.md | 20 -
docs/archives/legacy-llm-provider-runtime.md | 41 -
docs/archives/mac-computer-use/README.md | 40 -
docs/archives/mac-computer-use/packaging.md | 277 --
.../mac-computer-use/permissions-ux.md | 155 -
docs/archives/mac-computer-use/plan.md | 231 --
docs/archives/mac-computer-use/references.md | 91 -
docs/archives/mac-computer-use/spec.md | 89 -
docs/archives/mac-computer-use/tasks.md | 117 -
docs/archives/mac-computer-use/test-plan.md | 106 -
docs/archives/multi-window-cleanup/plan.md | 10 -
docs/archives/multi-window-cleanup/spec.md | 51 -
docs/archives/multi-window-cleanup/tasks.md | 14 -
docs/archives/new-agent/plan.md | 414 ---
docs/archives/new-agent/spec.md | 216 --
docs/archives/new-agent/tasks.md | 116 -
docs/archives/new-agent/v1-spec.md | 62 -
docs/archives/new-agent/v2-spec.md | 135 -
docs/archives/new-agent/v3-spec.md | 275 --
docs/archives/new-ui-agent-session/spec.md | 31 -
docs/archives/new-ui-agent-store/spec.md | 158 -
docs/archives/new-ui-chat-components/spec.md | 217 --
docs/archives/new-ui-implementation/todo.md | 314 --
.../new-ui-markdown-rendering/spec.md | 45 -
docs/archives/new-ui-page-state/spec.md | 140 -
docs/archives/new-ui-pages/spec.md | 275 --
docs/archives/new-ui-project-store/spec.md | 133 -
docs/archives/new-ui-session-store/spec.md | 365 ---
docs/archives/new-ui-sidebar/spec.md | 206 --
docs/archives/new-ui-status-bar/spec.md | 42 -
.../provider-layer-simplification/plan.md | 18 -
.../provider-layer-simplification/spec.md | 70 -
.../provider-layer-simplification/tasks.md | 16 -
docs/archives/remove-chat-mode/plan.md | 288 --
docs/archives/remove-chat-mode/spec.md | 179 --
docs/archives/skills-system/design.md | 647 ----
docs/archives/skills-system/research.md | 477 ---
.../skills-syncing-antigravity.md | 237 --
.../skills-syncing-claude-code.md | 203 --
.../skills-system/skills-syncing-copilot.md | 247 --
.../skills-system/skills-syncing-cursor.md | 184 --
.../skills-system/skills-syncing-kiro.md | 286 --
.../skills-system/skills-syncing-tasks.md | 430 ---
.../skills-system/skills-syncing-windsurf.md | 231 --
docs/archives/skills-system/skills-syncing.md | 721 -----
docs/archives/skills-system/ui-design.md | 549 ----
docs/archives/skills-ux-redesign/analysis.md | 223 --
docs/archives/skills-ux-redesign/spec.md | 777 -----
docs/archives/skills-ux-redesign/tasks.md | 450 ---
docs/archives/specs-migration-2026-05-02.md | 39 -
docs/archives/telegram-remote-control/plan.md | 235 --
docs/archives/telegram-remote-control/spec.md | 176 --
.../archives/telegram-remote-control/tasks.md | 112 -
.../thread-presenter-migration-plan.md | 2727 -----------------
.../workspace-agent-refactoring-summary.md | 323 --
docs/features/acp-agent-uninstall/plan.md | 21 -
docs/features/acp-agent-uninstall/spec.md | 25 -
docs/features/acp-agent-uninstall/tasks.md | 7 -
.../acp-session-config-options/plan.md | 127 -
.../acp-session-config-options/spec.md | 93 -
.../acp-session-config-options/tasks.md | 52 -
docs/features/active-input-routing/plan.md | 40 -
docs/features/active-input-routing/spec.md | 32 -
docs/features/active-input-routing/tasks.md | 10 -
docs/features/agent-db-legacy-import/plan.md | 46 -
docs/features/agent-db-legacy-import/spec.md | 32 -
docs/features/agent-db-legacy-import/tasks.md | 7 -
.../agent-input-advanced-config/plan.md | 63 -
.../agent-input-advanced-config/spec.md | 80 -
.../agent-input-advanced-config/tasks.md | 62 -
docs/features/agent-progress-todo/plan.md | 175 --
docs/features/agent-progress-todo/spec.md | 194 --
docs/features/agent-progress-todo/tasks.md | 79 -
docs/features/app-spotlight-search/plan.md | 300 --
docs/features/app-spotlight-search/spec.md | 178 --
docs/features/app-spotlight-search/tasks.md | 84 -
.../cc-switch-provider-import/plan.md | 27 -
.../cc-switch-provider-import/spec.md | 30 -
.../cc-switch-provider-import/tasks.md | 11 -
.../chat-input-hero-transition/plan.md | 32 -
.../chat-input-hero-transition/spec.md | 28 -
.../chat-input-hero-transition/tasks.md | 7 -
docs/features/chat-settings-control/plan.md | 14 -
docs/features/chat-settings-control/spec.md | 85 -
docs/features/chat-settings-control/tasks.md | 7 -
.../chat-sidebar-input-polish/plan.md | 14 -
.../chat-sidebar-input-polish/spec.md | 21 -
.../chat-sidebar-input-polish/tasks.md | 7 -
.../context-engineering-label/plan.md | 10 -
.../context-engineering-label/spec.md | 15 -
.../context-engineering-label/tasks.md | 5 -
.../data-settings-danger-zone-entry/plan.md | 19 -
.../data-settings-danger-zone-entry/spec.md | 19 -
.../data-settings-danger-zone-entry/tasks.md | 7 -
docs/features/data-sync-card-header/plan.md | 12 -
docs/features/data-sync-card-header/spec.md | 16 -
docs/features/data-sync-card-header/tasks.md | 6 -
.../deepchat-data-import-skill/plan.md | 35 -
.../deepchat-data-import-skill/spec.md | 38 -
.../deepchat-data-import-skill/tasks.md | 9 -
docs/features/edit-file-tool/plan.md | 14 -
docs/features/edit-file-tool/spec.md | 77 -
docs/features/edit-file-tool/tasks.md | 7 -
docs/features/electron-vite-5-upgrade/plan.md | 14 -
docs/features/electron-vite-5-upgrade/spec.md | 32 -
.../features/electron-vite-5-upgrade/tasks.md | 7 -
docs/features/file-attachment-support/plan.md | 14 -
docs/features/file-attachment-support/spec.md | 19 -
.../features/file-attachment-support/tasks.md | 7 -
docs/features/floating-agent-widget/plan.md | 91 -
docs/features/floating-agent-widget/spec.md | 52 -
docs/features/floating-agent-widget/tasks.md | 9 -
.../high-priority-i18n-languages/plan.md | 27 -
.../high-priority-i18n-languages/spec.md | 29 -
.../high-priority-i18n-languages/tasks.md | 8 -
docs/features/hooks-notifications/plan.md | 43 -
docs/features/hooks-notifications/spec.md | 175 --
docs/features/hooks-notifications/tasks.md | 7 -
.../manual-compaction-command/plan.md | 28 -
.../manual-compaction-command/spec.md | 39 -
.../manual-compaction-command/tasks.md | 8 -
docs/features/mcp-footer-alignment/plan.md | 12 -
docs/features/mcp-footer-alignment/spec.md | 19 -
docs/features/mcp-footer-alignment/tasks.md | 7 -
docs/features/mcp-settings-status-bar/plan.md | 13 -
docs/features/mcp-settings-status-bar/spec.md | 18 -
.../features/mcp-settings-status-bar/tasks.md | 7 -
docs/features/message-toolbar-actions/plan.md | 90 -
docs/features/message-toolbar-actions/spec.md | 102 -
.../features/message-toolbar-actions/tasks.md | 41 -
docs/features/message-trace-storage/plan.md | 112 -
docs/features/message-trace-storage/spec.md | 96 -
docs/features/message-trace-storage/tasks.md | 55 -
.../features/mistral-provider-support/plan.md | 19 -
.../features/mistral-provider-support/spec.md | 20 -
.../mistral-provider-support/tasks.md | 8 -
docs/features/model-top-p-settings/plan.md | 38 -
docs/features/model-top-p-settings/spec.md | 36 -
docs/features/model-top-p-settings/tasks.md | 10 -
docs/features/ollama-model-selection/plan.md | 48 -
docs/features/ollama-model-selection/spec.md | 71 -
docs/features/ollama-model-selection/tasks.md | 11 -
.../plan.md | 42 -
.../spec.md | 32 -
.../tasks.md | 25 -
.../openai-image-generation-settings/plan.md | 28 -
.../openai-image-generation-settings/spec.md | 27 -
.../openai-image-generation-settings/tasks.md | 10 -
docs/features/privacy-mode/plan.md | 26 -
docs/features/privacy-mode/spec.md | 51 -
docs/features/privacy-mode/tasks.md | 9 -
docs/features/process-tool/plan.md | 14 -
docs/features/process-tool/spec.md | 109 -
docs/features/process-tool/tasks.md | 7 -
docs/features/provider-config-import/plan.md | 89 -
docs/features/provider-config-import/spec.md | 99 -
docs/features/provider-config-import/tasks.md | 14 -
.../features/provider-deeplink-import/plan.md | 180 --
.../features/provider-deeplink-import/spec.md | 136 -
.../provider-deeplink-import/tasks.md | 59 -
.../provider-detail-simplification/plan.md | 18 -
.../provider-detail-simplification/spec.md | 18 -
.../provider-detail-simplification/tasks.md | 7 -
docs/features/remote-acp-control/plan.md | 106 -
docs/features/remote-acp-control/spec.md | 50 -
docs/features/remote-acp-control/tasks.md | 67 -
docs/features/remote-block-streaming/plan.md | 65 -
docs/features/remote-block-streaming/spec.md | 41 -
docs/features/remote-block-streaming/tasks.md | 37 -
docs/features/remote-discord-lark/plan.md | 63 -
docs/features/remote-discord-lark/spec.md | 41 -
docs/features/remote-discord-lark/tasks.md | 14 -
docs/features/remote-multi-channel/plan.md | 255 --
docs/features/remote-multi-channel/spec.md | 88 -
docs/features/remote-multi-channel/tasks.md | 125 -
docs/features/remote-process-log/plan.md | 14 -
docs/features/remote-process-log/spec.md | 31 -
docs/features/remote-process-log/tasks.md | 7 -
docs/features/remote-system-group/plan.md | 9 -
docs/features/remote-system-group/spec.md | 15 -
docs/features/remote-system-group/tasks.md | 5 -
.../features/remote-tool-interactions/plan.md | 125 -
.../features/remote-tool-interactions/spec.md | 47 -
.../remote-tool-interactions/tasks.md | 103 -
docs/features/right-sidepanel/plan.md | 199 --
docs/features/right-sidepanel/spec.md | 133 -
docs/features/right-sidepanel/tasks.md | 52 -
docs/features/scheduled-tasks/plan.md | 63 -
docs/features/scheduled-tasks/spec.md | 59 -
docs/features/scheduled-tasks/tasks.md | 16 -
.../settings-agent-capability-groups/plan.md | 11 -
.../settings-agent-capability-groups/spec.md | 18 -
.../settings-agent-capability-groups/tasks.md | 7 -
docs/features/settings-control-center/plan.md | 27 -
docs/features/settings-control-center/spec.md | 47 -
.../features/settings-control-center/tasks.md | 13 -
docs/features/settings-dashboard/plan.md | 46 -
docs/features/settings-dashboard/spec.md | 33 -
docs/features/settings-dashboard/tasks.md | 8 -
docs/features/settings-environments/plan.md | 67 -
docs/features/settings-environments/spec.md | 50 -
docs/features/settings-environments/tasks.md | 7 -
.../settings-overview-agent-card/plan.md | 11 -
.../settings-overview-agent-card/spec.md | 17 -
.../settings-overview-agent-card/tasks.md | 6 -
docs/features/settings-overview-cards/plan.md | 20 -
docs/features/settings-overview-cards/spec.md | 27 -
.../features/settings-overview-cards/tasks.md | 8 -
.../plan.md | 12 -
.../spec.md | 20 -
.../tasks.md | 7 -
docs/features/shortcut-system-group/plan.md | 9 -
docs/features/shortcut-system-group/spec.md | 15 -
docs/features/shortcut-system-group/tasks.md | 5 -
.../sidebar-session-context-menu/plan.md | 14 -
.../sidebar-session-context-menu/spec.md | 29 -
.../sidebar-session-context-menu/tasks.md | 7 -
.../sidebar-workspace-shortcuts/plan.md | 14 -
.../sidebar-workspace-shortcuts/spec.md | 25 -
.../sidebar-workspace-shortcuts/tasks.md | 7 -
docs/features/subagent-orchestrator/plan.md | 219 --
docs/features/subagent-orchestrator/spec.md | 312 --
docs/features/subagent-orchestrator/tasks.md | 55 -
docs/features/tool-call-image-preview/plan.md | 14 -
docs/features/tool-call-image-preview/spec.md | 27 -
.../features/tool-call-image-preview/tasks.md | 7 -
docs/features/unified-tts-provider/plan.md | 37 -
docs/features/unified-tts-provider/spec.md | 34 -
docs/features/unified-tts-provider/tasks.md | 22 -
docs/features/user-message-collapse/plan.md | 114 -
docs/features/user-message-collapse/spec.md | 42 -
docs/features/user-message-collapse/tasks.md | 60 -
docs/features/v1-0-4-stable-release/plan.md | 20 -
docs/features/v1-0-4-stable-release/spec.md | 22 -
docs/features/v1-0-4-stable-release/tasks.md | 8 -
docs/features/v1-0-5-beta-1-release/plan.md | 22 -
docs/features/v1-0-5-beta-1-release/spec.md | 22 -
docs/features/v1-0-5-beta-1-release/tasks.md | 9 -
.../voice-input-transcription/plan.md | 44 -
.../voice-input-transcription/spec.md | 45 -
.../voice-input-transcription/tasks.md | 16 -
docs/features/workspace-lifecycle/plan.md | 45 -
docs/features/workspace-lifecycle/spec.md | 26 -
docs/features/workspace-lifecycle/tasks.md | 7 -
.../yobrowser-activity-visualization/plan.md | 28 -
.../yobrowser-activity-visualization/spec.md | 29 -
.../yobrowser-activity-visualization/tasks.md | 9 -
docs/features/yobrowser-optimization/plan.md | 42 -
docs/features/yobrowser-optimization/spec.md | 67 -
docs/features/yobrowser-optimization/tasks.md | 9 -
docs/guides/code-navigation.md | 8 +-
docs/guides/getting-started.md | 17 +-
docs/issues/acp-context-budget-bypass/plan.md | 26 -
docs/issues/acp-context-budget-bypass/spec.md | 36 -
.../issues/acp-context-budget-bypass/tasks.md | 9 -
.../issues/acp-initialization-logging/plan.md | 29 -
.../issues/acp-initialization-logging/spec.md | 26 -
.../acp-initialization-logging/tasks.md | 8 -
docs/issues/acp-pr-1614-review-fixes/plan.md | 16 -
docs/issues/acp-pr-1614-review-fixes/spec.md | 21 -
docs/issues/acp-pr-1614-review-fixes/tasks.md | 7 -
docs/issues/acp-workdir-npx-recovery/plan.md | 21 -
docs/issues/acp-workdir-npx-recovery/spec.md | 26 -
docs/issues/acp-workdir-npx-recovery/tasks.md | 8 -
docs/issues/agent-tool-context-budget/plan.md | 81 -
docs/issues/agent-tool-context-budget/spec.md | 87 -
.../issues/agent-tool-context-budget/tasks.md | 20 -
.../background-exec-shell-fallback/plan.md | 27 -
.../background-exec-shell-fallback/spec.md | 25 -
.../background-exec-shell-fallback/tasks.md | 10 -
docs/issues/beta-7-release-test-gate/plan.md | 15 -
docs/issues/beta-7-release-test-gate/spec.md | 21 -
docs/issues/beta-7-release-test-gate/tasks.md | 8 -
.../chat-history-pagination-stability/plan.md | 29 -
.../chat-history-pagination-stability/spec.md | 35 -
.../tasks.md | 8 -
docs/issues/cua-driver-v0-1-5-sync/plan.md | 30 -
docs/issues/cua-driver-v0-1-5-sync/spec.md | 36 -
docs/issues/cua-driver-v0-1-5-sync/tasks.md | 9 -
docs/issues/cua-driver-v014-sync/plan.md | 25 -
docs/issues/cua-driver-v014-sync/spec.md | 29 -
docs/issues/cua-driver-v014-sync/tasks.md | 9 -
.../issues/cua-settings-empty-message/plan.md | 23 -
.../issues/cua-settings-empty-message/spec.md | 22 -
.../cua-settings-empty-message/tasks.md | 6 -
.../danger-zone-button-overflow/plan.md | 11 -
.../danger-zone-button-overflow/spec.md | 16 -
.../danger-zone-button-overflow/tasks.md | 6 -
docs/issues/e2e-smoke-regression/plan.md | 355 ---
docs/issues/e2e-smoke-regression/spec.md | 140 -
docs/issues/e2e-smoke-regression/tasks.md | 139 -
docs/issues/failed-message-context/plan.md | 19 -
docs/issues/failed-message-context/spec.md | 22 -
docs/issues/failed-message-context/tasks.md | 7 -
.../feishu-post-message-payload/plan.md | 12 -
.../feishu-post-message-payload/spec.md | 21 -
.../feishu-post-message-payload/tasks.md | 7 -
.../markdown-smooth-streaming-control/plan.md | 19 -
.../markdown-smooth-streaming-control/spec.md | 17 -
.../tasks.md | 8 -
docs/issues/mcp-toggle-opens-detail/plan.md | 16 -
docs/issues/mcp-toggle-opens-detail/spec.md | 20 -
docs/issues/mcp-toggle-opens-detail/tasks.md | 6 -
docs/issues/merge-dev-into-gen-video/plan.md | 20 -
docs/issues/merge-dev-into-gen-video/spec.md | 23 -
docs/issues/merge-dev-into-gen-video/tasks.md | 8 -
.../permission-flow-stabilization/plan.md | 14 -
.../permission-flow-stabilization/spec.md | 119 -
.../permission-flow-stabilization/tasks.md | 7 -
.../plugin-mcp-lifecycle-isolation/plan.md | 32 -
.../plugin-mcp-lifecycle-isolation/spec.md | 26 -
.../plugin-mcp-lifecycle-isolation/tasks.md | 9 -
.../plugin-settings-surface-isolation/plan.md | 67 -
.../plugin-settings-surface-isolation/spec.md | 52 -
.../tasks.md | 17 -
.../issues/plugin-skill-tool-guidance/plan.md | 28 -
.../issues/plugin-skill-tool-guidance/spec.md | 34 -
.../plugin-skill-tool-guidance/tasks.md | 6 -
docs/issues/pr-review-followups/plan.md | 13 -
docs/issues/pr-review-followups/spec.md | 20 -
docs/issues/pr-review-followups/tasks.md | 7 -
.../provider-validation-disabled/plan.md | 21 -
.../provider-validation-disabled/spec.md | 22 -
.../provider-validation-disabled/tasks.md | 7 -
.../question-tool-prompt-optimization/plan.md | 14 -
.../question-tool-prompt-optimization/spec.md | 28 -
.../tasks.md | 7 -
docs/issues/tool-call-path-summary/plan.md | 17 -
docs/issues/tool-call-path-summary/spec.md | 25 -
docs/issues/tool-call-path-summary/tasks.md | 7 -
docs/issues/tool-output-guardrails/plan.md | 30 -
docs/issues/tool-output-guardrails/spec.md | 77 -
docs/issues/tool-output-guardrails/tasks.md | 7 -
.../windows-exec-output-encoding/plan.md | 21 -
.../windows-exec-output-encoding/spec.md | 26 -
.../windows-exec-output-encoding/tasks.md | 8 -
docs/spec-driven-dev.md | 9 +-
.../sqlite-mainline-normalization/plan.md | 182 --
.../sqlite-mainline-normalization/spec.md | 123 -
.../sqlite-mainline-normalization/tasks.md | 80 -
427 files changed, 304 insertions(+), 35608 deletions(-)
delete mode 100644 docs/architecture/acp-client-runtime/plan.md
delete mode 100644 docs/architecture/acp-client-runtime/spec.md
delete mode 100644 docs/architecture/acp-client-runtime/tasks.md
delete mode 100644 docs/architecture/agent-provider-simplification/plan.md
delete mode 100644 docs/architecture/agent-provider-simplification/spec.md
delete mode 100644 docs/architecture/agent-provider-simplification/tasks.md
delete mode 100644 docs/architecture/agent-refactor/plan.md
delete mode 100644 docs/architecture/agent-refactor/spec.md
delete mode 100644 docs/architecture/agent-refactor/tasks.md
delete mode 100644 docs/architecture/agent-refactor/tool-result-envelope.md
delete mode 100644 docs/architecture/architecture-simplification/plan.md
delete mode 100644 docs/architecture/architecture-simplification/spec.md
delete mode 100644 docs/architecture/architecture-simplification/tasks.md
delete mode 100644 docs/architecture/chat-store-zero-migration/plan.md
delete mode 100644 docs/architecture/chat-store-zero-migration/spec.md
delete mode 100644 docs/architecture/chat-store-zero-migration/tasks.md
delete mode 100644 docs/architecture/config-sqlite-storage/plan.md
delete mode 100644 docs/architecture/config-sqlite-storage/spec.md
delete mode 100644 docs/architecture/config-sqlite-storage/tasks.md
delete mode 100644 docs/architecture/main-kernel-refactor/acceptance.md
delete mode 100644 docs/architecture/main-kernel-refactor/build-vs-buy.md
delete mode 100644 docs/architecture/main-kernel-refactor/eventbus-migration.md
delete mode 100644 docs/architecture/main-kernel-refactor/migration-governance.md
delete mode 100644 docs/architecture/main-kernel-refactor/plan.md
delete mode 100644 docs/architecture/main-kernel-refactor/ports-and-scheduler.md
delete mode 100644 docs/architecture/main-kernel-refactor/route-schema-catalog.md
delete mode 100644 docs/architecture/main-kernel-refactor/spec.md
delete mode 100644 docs/architecture/main-kernel-refactor/tasks.md
delete mode 100644 docs/architecture/main-kernel-refactor/test-plan.md
delete mode 100644 docs/architecture/new-ui-implementation-plan.md
delete mode 100644 docs/architecture/remove-rebrand-tool/plan.md
delete mode 100644 docs/architecture/remove-rebrand-tool/spec.md
delete mode 100644 docs/architecture/remove-rebrand-tool/tasks.md
delete mode 100644 docs/architecture/renderer-main-single-track/plan.md
delete mode 100644 docs/architecture/renderer-main-single-track/spec.md
delete mode 100644 docs/architecture/renderer-main-single-track/tasks.md
delete mode 100644 docs/architecture/skill-runtime-hardening/plan.md
delete mode 100644 docs/architecture/skill-runtime-hardening/spec.md
delete mode 100644 docs/architecture/skill-runtime-hardening/tasks.md
delete mode 100644 docs/architecture/sqlite-database-encryption/plan.md
delete mode 100644 docs/architecture/sqlite-database-encryption/spec.md
delete mode 100644 docs/architecture/sqlite-database-encryption/tasks.md
delete mode 100644 docs/architecture/startup-orchestration/acceptance.md
delete mode 100644 docs/architecture/startup-orchestration/plan.md
delete mode 100644 docs/architecture/startup-orchestration/spec.md
delete mode 100644 docs/architecture/startup-orchestration/tasks.md
delete mode 100644 docs/architecture/sync-config-import-versioning/plan.md
delete mode 100644 docs/architecture/sync-config-import-versioning/spec.md
delete mode 100644 docs/architecture/sync-config-import-versioning/tasks.md
delete mode 100644 docs/archives/agent-cleanup/plan.md
delete mode 100644 docs/archives/agent-cleanup/spec.md
delete mode 100644 docs/archives/agent-cleanup/tasks.md
delete mode 100644 docs/archives/agent-tooling-v2/plan.md
delete mode 100644 docs/archives/agent-tooling-v2/spec.md
delete mode 100644 docs/archives/agent-tooling-v2/tasks.md
delete mode 100644 docs/archives/agentpresenter-mvp-replacement/plan.md
delete mode 100644 docs/archives/agentpresenter-mvp-replacement/spec.md
delete mode 100644 docs/archives/agentpresenter-mvp-replacement/tasks.md
delete mode 100644 docs/archives/ai-sdk-runtime/plan.md
delete mode 100644 docs/archives/ai-sdk-runtime/spec.md
delete mode 100644 docs/archives/ai-sdk-runtime/tasks.md
delete mode 100644 docs/archives/chat-audio-tts-routing/plan.md
delete mode 100644 docs/archives/chat-audio-tts-routing/spec.md
delete mode 100644 docs/archives/chat-audio-tts-routing/tasks.md
delete mode 100644 docs/archives/cua-runtime-plugin/non-macos-handoff.md
delete mode 100644 docs/archives/cua-runtime-plugin/plan.md
delete mode 100644 docs/archives/cua-runtime-plugin/spec.md
delete mode 100644 docs/archives/cua-runtime-plugin/tasks.md
delete mode 100644 docs/archives/default-model-settings/plan.md
delete mode 100644 docs/archives/default-model-settings/spec.md
delete mode 100644 docs/archives/default-model-settings/tasks.md
delete mode 100644 docs/archives/i18n-missing-translations/plan.md
delete mode 100644 docs/archives/i18n-missing-translations/spec.md
delete mode 100644 docs/archives/i18n-missing-translations/tasks.md
delete mode 100644 docs/archives/legacy-agentpresenter-architecture.md
delete mode 100644 docs/archives/legacy-agentpresenter-flows.md
delete mode 100644 docs/archives/legacy-agentpresenter-retirement/plan.md
delete mode 100644 docs/archives/legacy-agentpresenter-retirement/spec.md
delete mode 100644 docs/archives/legacy-agentpresenter-retirement/tasks.md
delete mode 100644 docs/archives/legacy-llm-provider-runtime-retirement/plan.md
delete mode 100644 docs/archives/legacy-llm-provider-runtime-retirement/spec.md
delete mode 100644 docs/archives/legacy-llm-provider-runtime-retirement/tasks.md
delete mode 100644 docs/archives/legacy-llm-provider-runtime.md
delete mode 100644 docs/archives/mac-computer-use/README.md
delete mode 100644 docs/archives/mac-computer-use/packaging.md
delete mode 100644 docs/archives/mac-computer-use/permissions-ux.md
delete mode 100644 docs/archives/mac-computer-use/plan.md
delete mode 100644 docs/archives/mac-computer-use/references.md
delete mode 100644 docs/archives/mac-computer-use/spec.md
delete mode 100644 docs/archives/mac-computer-use/tasks.md
delete mode 100644 docs/archives/mac-computer-use/test-plan.md
delete mode 100644 docs/archives/multi-window-cleanup/plan.md
delete mode 100644 docs/archives/multi-window-cleanup/spec.md
delete mode 100644 docs/archives/multi-window-cleanup/tasks.md
delete mode 100644 docs/archives/new-agent/plan.md
delete mode 100644 docs/archives/new-agent/spec.md
delete mode 100644 docs/archives/new-agent/tasks.md
delete mode 100644 docs/archives/new-agent/v1-spec.md
delete mode 100644 docs/archives/new-agent/v2-spec.md
delete mode 100644 docs/archives/new-agent/v3-spec.md
delete mode 100644 docs/archives/new-ui-agent-session/spec.md
delete mode 100644 docs/archives/new-ui-agent-store/spec.md
delete mode 100644 docs/archives/new-ui-chat-components/spec.md
delete mode 100644 docs/archives/new-ui-implementation/todo.md
delete mode 100644 docs/archives/new-ui-markdown-rendering/spec.md
delete mode 100644 docs/archives/new-ui-page-state/spec.md
delete mode 100644 docs/archives/new-ui-pages/spec.md
delete mode 100644 docs/archives/new-ui-project-store/spec.md
delete mode 100644 docs/archives/new-ui-session-store/spec.md
delete mode 100644 docs/archives/new-ui-sidebar/spec.md
delete mode 100644 docs/archives/new-ui-status-bar/spec.md
delete mode 100644 docs/archives/provider-layer-simplification/plan.md
delete mode 100644 docs/archives/provider-layer-simplification/spec.md
delete mode 100644 docs/archives/provider-layer-simplification/tasks.md
delete mode 100644 docs/archives/remove-chat-mode/plan.md
delete mode 100644 docs/archives/remove-chat-mode/spec.md
delete mode 100644 docs/archives/skills-system/design.md
delete mode 100644 docs/archives/skills-system/research.md
delete mode 100644 docs/archives/skills-system/skills-syncing-antigravity.md
delete mode 100644 docs/archives/skills-system/skills-syncing-claude-code.md
delete mode 100644 docs/archives/skills-system/skills-syncing-copilot.md
delete mode 100644 docs/archives/skills-system/skills-syncing-cursor.md
delete mode 100644 docs/archives/skills-system/skills-syncing-kiro.md
delete mode 100644 docs/archives/skills-system/skills-syncing-tasks.md
delete mode 100644 docs/archives/skills-system/skills-syncing-windsurf.md
delete mode 100644 docs/archives/skills-system/skills-syncing.md
delete mode 100644 docs/archives/skills-system/ui-design.md
delete mode 100644 docs/archives/skills-ux-redesign/analysis.md
delete mode 100644 docs/archives/skills-ux-redesign/spec.md
delete mode 100644 docs/archives/skills-ux-redesign/tasks.md
delete mode 100644 docs/archives/specs-migration-2026-05-02.md
delete mode 100644 docs/archives/telegram-remote-control/plan.md
delete mode 100644 docs/archives/telegram-remote-control/spec.md
delete mode 100644 docs/archives/telegram-remote-control/tasks.md
delete mode 100644 docs/archives/thread-presenter-migration-plan.md
delete mode 100644 docs/archives/workspace-agent-refactoring-summary.md
delete mode 100644 docs/features/acp-agent-uninstall/plan.md
delete mode 100644 docs/features/acp-agent-uninstall/spec.md
delete mode 100644 docs/features/acp-agent-uninstall/tasks.md
delete mode 100644 docs/features/acp-session-config-options/plan.md
delete mode 100644 docs/features/acp-session-config-options/spec.md
delete mode 100644 docs/features/acp-session-config-options/tasks.md
delete mode 100644 docs/features/active-input-routing/plan.md
delete mode 100644 docs/features/active-input-routing/spec.md
delete mode 100644 docs/features/active-input-routing/tasks.md
delete mode 100644 docs/features/agent-db-legacy-import/plan.md
delete mode 100644 docs/features/agent-db-legacy-import/spec.md
delete mode 100644 docs/features/agent-db-legacy-import/tasks.md
delete mode 100644 docs/features/agent-input-advanced-config/plan.md
delete mode 100644 docs/features/agent-input-advanced-config/spec.md
delete mode 100644 docs/features/agent-input-advanced-config/tasks.md
delete mode 100644 docs/features/agent-progress-todo/plan.md
delete mode 100644 docs/features/agent-progress-todo/spec.md
delete mode 100644 docs/features/agent-progress-todo/tasks.md
delete mode 100644 docs/features/app-spotlight-search/plan.md
delete mode 100644 docs/features/app-spotlight-search/spec.md
delete mode 100644 docs/features/app-spotlight-search/tasks.md
delete mode 100644 docs/features/cc-switch-provider-import/plan.md
delete mode 100644 docs/features/cc-switch-provider-import/spec.md
delete mode 100644 docs/features/cc-switch-provider-import/tasks.md
delete mode 100644 docs/features/chat-input-hero-transition/plan.md
delete mode 100644 docs/features/chat-input-hero-transition/spec.md
delete mode 100644 docs/features/chat-input-hero-transition/tasks.md
delete mode 100644 docs/features/chat-settings-control/plan.md
delete mode 100644 docs/features/chat-settings-control/spec.md
delete mode 100644 docs/features/chat-settings-control/tasks.md
delete mode 100644 docs/features/chat-sidebar-input-polish/plan.md
delete mode 100644 docs/features/chat-sidebar-input-polish/spec.md
delete mode 100644 docs/features/chat-sidebar-input-polish/tasks.md
delete mode 100644 docs/features/context-engineering-label/plan.md
delete mode 100644 docs/features/context-engineering-label/spec.md
delete mode 100644 docs/features/context-engineering-label/tasks.md
delete mode 100644 docs/features/data-settings-danger-zone-entry/plan.md
delete mode 100644 docs/features/data-settings-danger-zone-entry/spec.md
delete mode 100644 docs/features/data-settings-danger-zone-entry/tasks.md
delete mode 100644 docs/features/data-sync-card-header/plan.md
delete mode 100644 docs/features/data-sync-card-header/spec.md
delete mode 100644 docs/features/data-sync-card-header/tasks.md
delete mode 100644 docs/features/deepchat-data-import-skill/plan.md
delete mode 100644 docs/features/deepchat-data-import-skill/spec.md
delete mode 100644 docs/features/deepchat-data-import-skill/tasks.md
delete mode 100644 docs/features/edit-file-tool/plan.md
delete mode 100644 docs/features/edit-file-tool/spec.md
delete mode 100644 docs/features/edit-file-tool/tasks.md
delete mode 100644 docs/features/electron-vite-5-upgrade/plan.md
delete mode 100644 docs/features/electron-vite-5-upgrade/spec.md
delete mode 100644 docs/features/electron-vite-5-upgrade/tasks.md
delete mode 100644 docs/features/file-attachment-support/plan.md
delete mode 100644 docs/features/file-attachment-support/spec.md
delete mode 100644 docs/features/file-attachment-support/tasks.md
delete mode 100644 docs/features/floating-agent-widget/plan.md
delete mode 100644 docs/features/floating-agent-widget/spec.md
delete mode 100644 docs/features/floating-agent-widget/tasks.md
delete mode 100644 docs/features/high-priority-i18n-languages/plan.md
delete mode 100644 docs/features/high-priority-i18n-languages/spec.md
delete mode 100644 docs/features/high-priority-i18n-languages/tasks.md
delete mode 100644 docs/features/hooks-notifications/plan.md
delete mode 100644 docs/features/hooks-notifications/spec.md
delete mode 100644 docs/features/hooks-notifications/tasks.md
delete mode 100644 docs/features/manual-compaction-command/plan.md
delete mode 100644 docs/features/manual-compaction-command/spec.md
delete mode 100644 docs/features/manual-compaction-command/tasks.md
delete mode 100644 docs/features/mcp-footer-alignment/plan.md
delete mode 100644 docs/features/mcp-footer-alignment/spec.md
delete mode 100644 docs/features/mcp-footer-alignment/tasks.md
delete mode 100644 docs/features/mcp-settings-status-bar/plan.md
delete mode 100644 docs/features/mcp-settings-status-bar/spec.md
delete mode 100644 docs/features/mcp-settings-status-bar/tasks.md
delete mode 100644 docs/features/message-toolbar-actions/plan.md
delete mode 100644 docs/features/message-toolbar-actions/spec.md
delete mode 100644 docs/features/message-toolbar-actions/tasks.md
delete mode 100644 docs/features/message-trace-storage/plan.md
delete mode 100644 docs/features/message-trace-storage/spec.md
delete mode 100644 docs/features/message-trace-storage/tasks.md
delete mode 100644 docs/features/mistral-provider-support/plan.md
delete mode 100644 docs/features/mistral-provider-support/spec.md
delete mode 100644 docs/features/mistral-provider-support/tasks.md
delete mode 100644 docs/features/model-top-p-settings/plan.md
delete mode 100644 docs/features/model-top-p-settings/spec.md
delete mode 100644 docs/features/model-top-p-settings/tasks.md
delete mode 100644 docs/features/ollama-model-selection/plan.md
delete mode 100644 docs/features/ollama-model-selection/spec.md
delete mode 100644 docs/features/ollama-model-selection/tasks.md
delete mode 100644 docs/features/openai-compatible-video-generation/plan.md
delete mode 100644 docs/features/openai-compatible-video-generation/spec.md
delete mode 100644 docs/features/openai-compatible-video-generation/tasks.md
delete mode 100644 docs/features/openai-image-generation-settings/plan.md
delete mode 100644 docs/features/openai-image-generation-settings/spec.md
delete mode 100644 docs/features/openai-image-generation-settings/tasks.md
delete mode 100644 docs/features/privacy-mode/plan.md
delete mode 100644 docs/features/privacy-mode/spec.md
delete mode 100644 docs/features/privacy-mode/tasks.md
delete mode 100644 docs/features/process-tool/plan.md
delete mode 100644 docs/features/process-tool/spec.md
delete mode 100644 docs/features/process-tool/tasks.md
delete mode 100644 docs/features/provider-config-import/plan.md
delete mode 100644 docs/features/provider-config-import/spec.md
delete mode 100644 docs/features/provider-config-import/tasks.md
delete mode 100644 docs/features/provider-deeplink-import/plan.md
delete mode 100644 docs/features/provider-deeplink-import/spec.md
delete mode 100644 docs/features/provider-deeplink-import/tasks.md
delete mode 100644 docs/features/provider-detail-simplification/plan.md
delete mode 100644 docs/features/provider-detail-simplification/spec.md
delete mode 100644 docs/features/provider-detail-simplification/tasks.md
delete mode 100644 docs/features/remote-acp-control/plan.md
delete mode 100644 docs/features/remote-acp-control/spec.md
delete mode 100644 docs/features/remote-acp-control/tasks.md
delete mode 100644 docs/features/remote-block-streaming/plan.md
delete mode 100644 docs/features/remote-block-streaming/spec.md
delete mode 100644 docs/features/remote-block-streaming/tasks.md
delete mode 100644 docs/features/remote-discord-lark/plan.md
delete mode 100644 docs/features/remote-discord-lark/spec.md
delete mode 100644 docs/features/remote-discord-lark/tasks.md
delete mode 100644 docs/features/remote-multi-channel/plan.md
delete mode 100644 docs/features/remote-multi-channel/spec.md
delete mode 100644 docs/features/remote-multi-channel/tasks.md
delete mode 100644 docs/features/remote-process-log/plan.md
delete mode 100644 docs/features/remote-process-log/spec.md
delete mode 100644 docs/features/remote-process-log/tasks.md
delete mode 100644 docs/features/remote-system-group/plan.md
delete mode 100644 docs/features/remote-system-group/spec.md
delete mode 100644 docs/features/remote-system-group/tasks.md
delete mode 100644 docs/features/remote-tool-interactions/plan.md
delete mode 100644 docs/features/remote-tool-interactions/spec.md
delete mode 100644 docs/features/remote-tool-interactions/tasks.md
delete mode 100644 docs/features/right-sidepanel/plan.md
delete mode 100644 docs/features/right-sidepanel/spec.md
delete mode 100644 docs/features/right-sidepanel/tasks.md
delete mode 100644 docs/features/scheduled-tasks/plan.md
delete mode 100644 docs/features/scheduled-tasks/spec.md
delete mode 100644 docs/features/scheduled-tasks/tasks.md
delete mode 100644 docs/features/settings-agent-capability-groups/plan.md
delete mode 100644 docs/features/settings-agent-capability-groups/spec.md
delete mode 100644 docs/features/settings-agent-capability-groups/tasks.md
delete mode 100644 docs/features/settings-control-center/plan.md
delete mode 100644 docs/features/settings-control-center/spec.md
delete mode 100644 docs/features/settings-control-center/tasks.md
delete mode 100644 docs/features/settings-dashboard/plan.md
delete mode 100644 docs/features/settings-dashboard/spec.md
delete mode 100644 docs/features/settings-dashboard/tasks.md
delete mode 100644 docs/features/settings-environments/plan.md
delete mode 100644 docs/features/settings-environments/spec.md
delete mode 100644 docs/features/settings-environments/tasks.md
delete mode 100644 docs/features/settings-overview-agent-card/plan.md
delete mode 100644 docs/features/settings-overview-agent-card/spec.md
delete mode 100644 docs/features/settings-overview-agent-card/tasks.md
delete mode 100644 docs/features/settings-overview-cards/plan.md
delete mode 100644 docs/features/settings-overview-cards/spec.md
delete mode 100644 docs/features/settings-overview-cards/tasks.md
delete mode 100644 docs/features/settings-overview-quickstart-dashboard/plan.md
delete mode 100644 docs/features/settings-overview-quickstart-dashboard/spec.md
delete mode 100644 docs/features/settings-overview-quickstart-dashboard/tasks.md
delete mode 100644 docs/features/shortcut-system-group/plan.md
delete mode 100644 docs/features/shortcut-system-group/spec.md
delete mode 100644 docs/features/shortcut-system-group/tasks.md
delete mode 100644 docs/features/sidebar-session-context-menu/plan.md
delete mode 100644 docs/features/sidebar-session-context-menu/spec.md
delete mode 100644 docs/features/sidebar-session-context-menu/tasks.md
delete mode 100644 docs/features/sidebar-workspace-shortcuts/plan.md
delete mode 100644 docs/features/sidebar-workspace-shortcuts/spec.md
delete mode 100644 docs/features/sidebar-workspace-shortcuts/tasks.md
delete mode 100644 docs/features/subagent-orchestrator/plan.md
delete mode 100644 docs/features/subagent-orchestrator/spec.md
delete mode 100644 docs/features/subagent-orchestrator/tasks.md
delete mode 100644 docs/features/tool-call-image-preview/plan.md
delete mode 100644 docs/features/tool-call-image-preview/spec.md
delete mode 100644 docs/features/tool-call-image-preview/tasks.md
delete mode 100644 docs/features/unified-tts-provider/plan.md
delete mode 100644 docs/features/unified-tts-provider/spec.md
delete mode 100644 docs/features/unified-tts-provider/tasks.md
delete mode 100644 docs/features/user-message-collapse/plan.md
delete mode 100644 docs/features/user-message-collapse/spec.md
delete mode 100644 docs/features/user-message-collapse/tasks.md
delete mode 100644 docs/features/v1-0-4-stable-release/plan.md
delete mode 100644 docs/features/v1-0-4-stable-release/spec.md
delete mode 100644 docs/features/v1-0-4-stable-release/tasks.md
delete mode 100644 docs/features/v1-0-5-beta-1-release/plan.md
delete mode 100644 docs/features/v1-0-5-beta-1-release/spec.md
delete mode 100644 docs/features/v1-0-5-beta-1-release/tasks.md
delete mode 100644 docs/features/voice-input-transcription/plan.md
delete mode 100644 docs/features/voice-input-transcription/spec.md
delete mode 100644 docs/features/voice-input-transcription/tasks.md
delete mode 100644 docs/features/workspace-lifecycle/plan.md
delete mode 100644 docs/features/workspace-lifecycle/spec.md
delete mode 100644 docs/features/workspace-lifecycle/tasks.md
delete mode 100644 docs/features/yobrowser-activity-visualization/plan.md
delete mode 100644 docs/features/yobrowser-activity-visualization/spec.md
delete mode 100644 docs/features/yobrowser-activity-visualization/tasks.md
delete mode 100644 docs/features/yobrowser-optimization/plan.md
delete mode 100644 docs/features/yobrowser-optimization/spec.md
delete mode 100644 docs/features/yobrowser-optimization/tasks.md
delete mode 100644 docs/issues/acp-context-budget-bypass/plan.md
delete mode 100644 docs/issues/acp-context-budget-bypass/spec.md
delete mode 100644 docs/issues/acp-context-budget-bypass/tasks.md
delete mode 100644 docs/issues/acp-initialization-logging/plan.md
delete mode 100644 docs/issues/acp-initialization-logging/spec.md
delete mode 100644 docs/issues/acp-initialization-logging/tasks.md
delete mode 100644 docs/issues/acp-pr-1614-review-fixes/plan.md
delete mode 100644 docs/issues/acp-pr-1614-review-fixes/spec.md
delete mode 100644 docs/issues/acp-pr-1614-review-fixes/tasks.md
delete mode 100644 docs/issues/acp-workdir-npx-recovery/plan.md
delete mode 100644 docs/issues/acp-workdir-npx-recovery/spec.md
delete mode 100644 docs/issues/acp-workdir-npx-recovery/tasks.md
delete mode 100644 docs/issues/agent-tool-context-budget/plan.md
delete mode 100644 docs/issues/agent-tool-context-budget/spec.md
delete mode 100644 docs/issues/agent-tool-context-budget/tasks.md
delete mode 100644 docs/issues/background-exec-shell-fallback/plan.md
delete mode 100644 docs/issues/background-exec-shell-fallback/spec.md
delete mode 100644 docs/issues/background-exec-shell-fallback/tasks.md
delete mode 100644 docs/issues/beta-7-release-test-gate/plan.md
delete mode 100644 docs/issues/beta-7-release-test-gate/spec.md
delete mode 100644 docs/issues/beta-7-release-test-gate/tasks.md
delete mode 100644 docs/issues/chat-history-pagination-stability/plan.md
delete mode 100644 docs/issues/chat-history-pagination-stability/spec.md
delete mode 100644 docs/issues/chat-history-pagination-stability/tasks.md
delete mode 100644 docs/issues/cua-driver-v0-1-5-sync/plan.md
delete mode 100644 docs/issues/cua-driver-v0-1-5-sync/spec.md
delete mode 100644 docs/issues/cua-driver-v0-1-5-sync/tasks.md
delete mode 100644 docs/issues/cua-driver-v014-sync/plan.md
delete mode 100644 docs/issues/cua-driver-v014-sync/spec.md
delete mode 100644 docs/issues/cua-driver-v014-sync/tasks.md
delete mode 100644 docs/issues/cua-settings-empty-message/plan.md
delete mode 100644 docs/issues/cua-settings-empty-message/spec.md
delete mode 100644 docs/issues/cua-settings-empty-message/tasks.md
delete mode 100644 docs/issues/danger-zone-button-overflow/plan.md
delete mode 100644 docs/issues/danger-zone-button-overflow/spec.md
delete mode 100644 docs/issues/danger-zone-button-overflow/tasks.md
delete mode 100644 docs/issues/e2e-smoke-regression/plan.md
delete mode 100644 docs/issues/e2e-smoke-regression/spec.md
delete mode 100644 docs/issues/e2e-smoke-regression/tasks.md
delete mode 100644 docs/issues/failed-message-context/plan.md
delete mode 100644 docs/issues/failed-message-context/spec.md
delete mode 100644 docs/issues/failed-message-context/tasks.md
delete mode 100644 docs/issues/feishu-post-message-payload/plan.md
delete mode 100644 docs/issues/feishu-post-message-payload/spec.md
delete mode 100644 docs/issues/feishu-post-message-payload/tasks.md
delete mode 100644 docs/issues/markdown-smooth-streaming-control/plan.md
delete mode 100644 docs/issues/markdown-smooth-streaming-control/spec.md
delete mode 100644 docs/issues/markdown-smooth-streaming-control/tasks.md
delete mode 100644 docs/issues/mcp-toggle-opens-detail/plan.md
delete mode 100644 docs/issues/mcp-toggle-opens-detail/spec.md
delete mode 100644 docs/issues/mcp-toggle-opens-detail/tasks.md
delete mode 100644 docs/issues/merge-dev-into-gen-video/plan.md
delete mode 100644 docs/issues/merge-dev-into-gen-video/spec.md
delete mode 100644 docs/issues/merge-dev-into-gen-video/tasks.md
delete mode 100644 docs/issues/permission-flow-stabilization/plan.md
delete mode 100644 docs/issues/permission-flow-stabilization/spec.md
delete mode 100644 docs/issues/permission-flow-stabilization/tasks.md
delete mode 100644 docs/issues/plugin-mcp-lifecycle-isolation/plan.md
delete mode 100644 docs/issues/plugin-mcp-lifecycle-isolation/spec.md
delete mode 100644 docs/issues/plugin-mcp-lifecycle-isolation/tasks.md
delete mode 100644 docs/issues/plugin-settings-surface-isolation/plan.md
delete mode 100644 docs/issues/plugin-settings-surface-isolation/spec.md
delete mode 100644 docs/issues/plugin-settings-surface-isolation/tasks.md
delete mode 100644 docs/issues/plugin-skill-tool-guidance/plan.md
delete mode 100644 docs/issues/plugin-skill-tool-guidance/spec.md
delete mode 100644 docs/issues/plugin-skill-tool-guidance/tasks.md
delete mode 100644 docs/issues/pr-review-followups/plan.md
delete mode 100644 docs/issues/pr-review-followups/spec.md
delete mode 100644 docs/issues/pr-review-followups/tasks.md
delete mode 100644 docs/issues/provider-validation-disabled/plan.md
delete mode 100644 docs/issues/provider-validation-disabled/spec.md
delete mode 100644 docs/issues/provider-validation-disabled/tasks.md
delete mode 100644 docs/issues/question-tool-prompt-optimization/plan.md
delete mode 100644 docs/issues/question-tool-prompt-optimization/spec.md
delete mode 100644 docs/issues/question-tool-prompt-optimization/tasks.md
delete mode 100644 docs/issues/tool-call-path-summary/plan.md
delete mode 100644 docs/issues/tool-call-path-summary/spec.md
delete mode 100644 docs/issues/tool-call-path-summary/tasks.md
delete mode 100644 docs/issues/tool-output-guardrails/plan.md
delete mode 100644 docs/issues/tool-output-guardrails/spec.md
delete mode 100644 docs/issues/tool-output-guardrails/tasks.md
delete mode 100644 docs/issues/windows-exec-output-encoding/plan.md
delete mode 100644 docs/issues/windows-exec-output-encoding/spec.md
delete mode 100644 docs/issues/windows-exec-output-encoding/tasks.md
delete mode 100644 docs/specs/sqlite-mainline-normalization/plan.md
delete mode 100644 docs/specs/sqlite-mainline-normalization/spec.md
delete mode 100644 docs/specs/sqlite-mainline-normalization/tasks.md
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 01f2b92cf..51cb0c1de 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -1,6 +1,7 @@
# DeepChat 当前架构概览
-本文档描述 `2026-04-20` 完成 main kernel refactor phase5 收口后的主架构。
+本文档描述 `2026-05-28` 的主架构。当前目标不是再做一次全量 main-kernel rewrite,
+而是维持 typed renderer-main boundary,并把新增能力接到既有 route/runtime owner 上。
## 主链路
@@ -10,111 +11,97 @@ flowchart LR
Client --> Bridge["window.deepchat / preload bridge"]
Bridge --> Contracts["shared/contracts routes + events"]
Contracts --> Routes["src/main/routes dispatcher"]
- Routes --> Settings["settings handler"]
- Routes --> Sessions["SessionService"]
- Routes --> Chat["ChatService"]
- Routes --> Providers["ProviderService"]
- Settings --> Config["configPresenter"]
- Sessions --> Ports["presenter-backed hot path ports"]
- Chat --> Ports
- Providers --> Ports
+ Routes --> Services["route services / handlers"]
+ Services --> Ports["presenter-backed ports"]
Ports --> AgentSession["agentSessionPresenter"]
- Ports --> Llm["llmProviderPresenter"]
- Ports --> Window["windowPresenter"]
AgentSession --> Runtime["agentRuntimePresenter"]
Runtime --> Tool["toolPresenter"]
Runtime --> SQLite["sqlitePresenter"]
Tool --> Mcp["mcpPresenter"]
Tool --> AgentTools["toolPresenter/agentTools"]
- Llm --> Acp["llmProviderPresenter/acp"]
+ Ports --> Provider["llmProviderPresenter"]
+ Provider --> Acp["llmProviderPresenter/acp"]
```
主结论:
-- migrated renderer boundary 先经过 `renderer/api`、`window.deepchat` 和 shared contracts,再进入 main。
-- `src/main/routes/index.ts` 是 settings / sessions / chat / providers 这些 migrated path 的 typed dispatcher 和装配入口。
-- 现有 presenter 仍然存在,但主要通过 `hotPathPorts` / `runtimePorts` 这样的窄 port 暴露给 route services。
-- `SessionPresenter` 继续保留为 legacy 数据平面和导出边界,不再是 migrated chat hot path 的 owner。
+- renderer 业务代码优先经过 `renderer/api/*Client`、`window.deepchat` 和 shared contracts。
+- `src/main/routes/index.ts` 是 typed route dispatcher,并装配 settings、sessions、chat、
+ providers、models、config、MCP、plugins、skills、sync、browser、database security、
+ scheduled tasks 等 route。
+- presenter 仍是 runtime owner,但 route services 只通过窄 port 或明确 client 依赖使用它们。
+- `SessionPresenter` 仍保留为 legacy 数据访问、导出和兼容边界,不再是当前聊天主链路 owner。
## 模块职责
| 模块 | 位置 | 职责 |
| --- | --- | --- |
-| `renderer/api` | `src/renderer/api/` | 为 settings / sessions / chat / providers 提供 typed renderer client |
-| shared contracts | `src/shared/contracts/` | 维护 route registry、schema、typed event catalog |
-| preload bridge | `src/preload/createBridge.ts` / `src/preload/index.ts` | 统一暴露 `window.deepchat.invoke/on` |
-| main route runtime | `src/main/routes/` | typed route dispatch、settings handler、session/chat/provider services |
-| presenter-backed ports | `src/main/routes/hotPathPorts.ts` / `src/main/presenter/runtimePorts.ts` | 把现有 presenter 收敛成 route service 可依赖的窄接口 |
+| `renderer/api` | `src/renderer/api/` | typed renderer clients,吸收 bridge/channel 细节 |
+| shared contracts | `src/shared/contracts/` | route registry、schema、typed event catalog |
+| preload bridge | `src/preload/createBridge.ts` / `src/preload/index.ts` | 暴露 `window.deepchat.invoke/on` |
+| main routes | `src/main/routes/` | typed route dispatch、services、handlers |
+| hot path ports | `src/main/routes/hotPathPorts.ts` / `src/main/presenter/runtimePorts.ts` | route runtime 到 presenter 的最小接口 |
| `AgentSessionPresenter` | `src/main/presenter/agentSessionPresenter/` | session registry、window binding、legacy import、runtime delegation |
-| `AgentRuntimePresenter` | `src/main/presenter/agentRuntimePresenter/` | 聊天 runtime、stream loop、tool interaction、message persistence |
-| `ToolPresenter` | `src/main/presenter/toolPresenter/` | 工具定义聚合、调用路由、权限预检查 |
-| `LLMProviderPresenter` | `src/main/presenter/llmProviderPresenter/` | provider 实例、stream state、model 管理、ACP provider helper |
-| `SessionPresenter` | `src/main/presenter/sessionPresenter/` | legacy 会话数据访问、导出、历史兼容边界 |
+| `AgentRuntimePresenter` | `src/main/presenter/agentRuntimePresenter/` | 聊天 loop、stream、tool interaction、message/session persistence |
+| `ToolPresenter` | `src/main/presenter/toolPresenter/` | MCP tools 与本地 agent tools 聚合、权限预检查、调用路由 |
+| `LLMProviderPresenter` | `src/main/presenter/llmProviderPresenter/` | provider 实例、model/runtime 管理、ACP helper、AI SDK runtime |
+| `StartupWorkloadCoordinator` | `src/main/presenter/startupWorkloadCoordinator/` | startup/settings/floating 等目标的分阶段后台任务调度 |
+| `RemoteControlPresenter` | `src/main/presenter/remoteControlPresenter/` | Telegram、Feishu/Lark、QQBot、Discord、WeChat iLink 远程控制 |
+| `ScheduledTasksService` | `src/main/presenter/scheduledTasks/` | 一次性、每日、每周任务调度和 prompt/notify action dispatch |
+| `DatabaseSecurityPresenter` | `src/main/presenter/databaseSecurityPresenter/` | SQLCipher 启用、改密、关闭、safeStorage/manual unlock |
+| Spotlight search | `src/renderer/src/stores/ui/spotlight.ts` | 全局搜索、会话/消息跳转、设置导航和非破坏性 action |
## 当前分层
-### 1. Renderer-main boundary
+### 1. Renderer-Main Boundary
-- `src/shared/contracts/routes*.ts` 与 `events*.ts` 是 migrated path 的唯一契约真源。
-- `src/preload/createBridge.ts` 负责把 route invoke 和 typed event subscribe 统一暴露到 `window.deepchat`。
-- `src/renderer/api/*Client.ts` 吸收 bridge 细节,组件和 store 不直接拼新的 raw channel 字符串。
+- `src/shared/contracts/routes*.ts` 与 `events*.ts` 是 migrated path 的契约真源。
+- `src/preload/createBridge.ts` 统一 route invoke 和 typed event subscribe。
+- `src/renderer/api/*Client.ts` 是组件和 store 的默认入口。
+- `src/renderer/api/legacy/**` 是唯一 legacy quarantine。当前保留 `presenters.ts`、
+ `presenterTransport.ts`、`runtime.ts` 三个兼容文件;新业务模块不应直接导入 legacy transport。
-### 2. Main route runtime
+### 2. Main Route Runtime
- `src/main/routes/index.ts` 根据 route registry 分发请求。
-- settings 走 `settings handler + adapter`。
-- sessions、chat、providers 分别走 `SessionService`、`ChatService`、`ProviderService`。
-- `Scheduler` 统一承接 migrated chat path 上的 timeout、retry、cancel 语义。
+- `SessionService`、`ChatService`、`ProviderService` 负责 migrated chat/session/provider hot path。
+- `ProviderImportService` 负责外部 provider 配置扫描与应用。
+- models routes 提供 model catalog、runtime list、config import/export、audio transcription。
+- database security 与 scheduled tasks 已经是 typed route,renderer 通过专用 client 调用。
-### 3. Presenter-backed runtime
+### 3. Agent Runtime
-- `createPresenterHotPathPorts()` 把 `agentSessionPresenter`、`configPresenter`、`llmProviderPresenter` 收敛成 route service 依赖的最小 port。
-- `AgentSessionPresenter` 仍持有 session registry、window 绑定和 legacy import helper。
-- `AgentRuntimePresenter` 继续拥有消息主循环、工具暂停恢复、消息持久化和增量 stream echo。
-- `ToolPresenter`、`LLMProviderPresenter` 和 `mcpPresenter` 继续服务未迁移能力与 runtime 内部协作。
+- `AgentSessionPresenter` 创建/恢复/激活 session,并把执行交给 `AgentRuntimePresenter`。
+- `AgentRuntimePresenter` 拥有 stream loop、tool loop、pending input、manual/auto compaction、
+ message trace 和结构化消息持久化。
+- `DeepChatMessageStore` 采用头表 + 结构化子表模型,并在读路径缺行时回退旧 JSON。
+- 历史搜索使用 `deepchat_search_documents` 与 FTS5,FTS 不可用时回退 `LIKE`。
+- Agent progress 使用 `agent-core/update_plan`、`chat.plan.updated` 和 renderer 浮层展示任务计划。
-### 4. Compatibility boundary
+### 4. Provider And Media Runtime
-仍然保留但已降级为兼容职责的边界:
+- `ModelType` 当前包含 chat、embedding、rerank、imageGeneration、videoGeneration、tts。
+- OpenAI-compatible image/video generation 和 TTS 通过 model config、provider route meta、
+ AI SDK runtime 与消息渲染协作。
+- 本地录音转写走 `ModelClient.transcribeAudio()` / `models.transcribeAudio`,由 provider runtime 完成。
+- provider deeplink 与 provider config import 都会在写入前做 preview、校验、冲突处理和脱敏展示。
+
+### 5. Compatibility Boundary
+
+仍保留但只承担兼容职责:
- `src/main/presenter/agentSessionPresenter/legacyImportService.ts`
- 旧 `conversations/messages` 数据域,作为 import-only 与导出数据源
- `src/main/presenter/sessionPresenter/`,作为 main 内部 compatibility/data facade
-- `src/main/eventbus.ts`,继续服务未迁移路径,但 migrated UI 通知优先走 typed events
-
-## Phase 5 结论
-
-- 本轮已经完成“边界稳定化 + 热路径减耦 + 可测试性提升”的目标。
-- 当前不继续发起一次性全量 `main kernel` rewrite。
-- 后续只在出现新的明确 hot path 收益时,再继续做 slice-driven typed-boundary migration。
-
-## Post-P5 执行规则
-
-`phase5` 收口不等于 renderer-main 已经完成单轨化。
+- `src/main/eventbus.ts`,继续服务未迁移路径;migrated UI 通知优先走 typed events
-当前后续工作的默认规则是:
+## 防回归规则
-- renderer 新功能优先走 `renderer/api/*Client` + `window.deepchat` + shared contracts
-- `useLegacyPresenter()`、`window.electron`、`window.api` 只视为兼容路径,不再作为业务层默认入口
-- `src/renderer/api/legacy/presenters.ts` 已退役,剩余 quarantine-only 入口固定为
- `src/renderer/api/legacy/presenters.ts`
-- renderer 业务模块不应再混用 typed client 与 legacy transport
-- renderer legacy quarantine 目录固定为 `src/renderer/api/legacy/**`,不再允许创建第二个 quarantine 路径
-
-单轨化的目标、阶段和 merge gate 见:
-
-- [architecture/renderer-main-single-track/spec.md](./architecture/renderer-main-single-track/spec.md)
-- [architecture/renderer-main-single-track/plan.md](./architecture/renderer-main-single-track/plan.md)
-- [architecture/renderer-main-single-track/tasks.md](./architecture/renderer-main-single-track/tasks.md)
-
-## 历史对照与防回归
-
-- 历史架构文档见 [archives/legacy-agentpresenter-architecture.md](./archives/legacy-agentpresenter-architecture.md)
-- 历史流程文档见 [archives/legacy-agentpresenter-flows.md](./archives/legacy-agentpresenter-flows.md)
-- main kernel 边界回归由 `scripts/architecture-guard.mjs` 和 `docs/architecture/baselines/main-kernel-*.{md,json}` 追踪
-- `scripts/architecture-guard.mjs` 负责固定 `src/renderer/api/legacy/**`、禁止业务层新增 direct legacy transport,并把 typed boundary 外的 legacy access 视为违规
-- legacy agent cleanup 回归由 `scripts/agent-cleanup-guard.mjs` 追踪
-- renderer-main 单轨化后续治理由 `docs/architecture/renderer-main-single-track/` 追踪
+- 新 renderer-main 能力默认走 `renderer/api/*Client` + `window.deepchat` + shared contracts。
+- legacy transport 只能留在 `src/renderer/api/legacy/**`,不新增第二个 quarantine 目录。
+- `scripts/architecture-guard.mjs` 固定 quarantine 文件数、检测 direct legacy transport、
+ 并读取 `docs/architecture/baselines/main-kernel-bridge-register.json`。
+- `scripts/agent-cleanup-guard.mjs` 用于防止已退休 agent runtime 入口回流。
## 推荐阅读顺序
@@ -124,4 +111,3 @@ flowchart LR
4. [architecture/agent-system.md](./architecture/agent-system.md)
5. [architecture/tool-system.md](./architecture/tool-system.md)
6. [architecture/session-management.md](./architecture/session-management.md)
-
diff --git a/docs/FLOWS.md b/docs/FLOWS.md
index eb5c295cd..28961bf35 100644
--- a/docs/FLOWS.md
+++ b/docs/FLOWS.md
@@ -1,31 +1,34 @@
# DeepChat 当前核心流程
-本文档只描述 retirement 后仍然有效的主流程。旧 `AgentPresenter` 流程已移到
-[archives/legacy-agentpresenter-flows.md](./archives/legacy-agentpresenter-flows.md)。
+本文档只描述当前代码仍在使用的流程。旧 `AgentPresenter` / `startStreamCompletion`
+等历史流程不再作为仓库内长期文档保留,需要追溯时用 `git log` / `git show` 查看历史提交。
## 1. 创建会话并发送消息
```mermaid
sequenceDiagram
participant R as Renderer
+ participant C as SessionClient/ChatClient
+ participant Route as src/main/routes
participant N as AgentSessionPresenter
- participant A as AgentRegistry
participant D as AgentRuntimePresenter
participant S as NewSessionManager
- R->>N: createSession(input, webContentsId)
- N->>A: resolve agent implementation
- N->>S: create session record
- N->>D: initSession(sessionId, config)
- N->>S: bindWindow(webContentsId, sessionId)
- N-->>R: SessionWithState
- N->>D: queuePendingInput()/processMessage()
+ R->>C: create/send/restore
+ C->>Route: window.deepchat.invoke(route)
+ Route->>N: createSession/restore/listMessagesPage
+ N->>S: create/bind/read session
+ N->>D: initSession/processMessage
+ D-->>R: chat.stream.* typed events
```
关键文件:
+- `src/renderer/api/SessionClient.ts`
+- `src/renderer/api/ChatClient.ts`
+- `src/main/routes/sessions/sessionService.ts`
+- `src/main/routes/chat/chatService.ts`
- `src/main/presenter/agentSessionPresenter/index.ts`
-- `src/main/presenter/agentSessionPresenter/sessionManager.ts`
- `src/main/presenter/agentRuntimePresenter/index.ts`
## 2. DeepChat 消息处理主循环
@@ -43,53 +46,79 @@ flowchart TD
Resume -->|no| Continue["append tool results"]
Wait --> Continue
Continue --> Context
- Finalize --> Persist["messageStore / sessionStore"]
+ Finalize --> Persist["messageStore / sessionStore / trace"]
```
-关键文件:
+关键语义:
-- `src/main/presenter/agentRuntimePresenter/process.ts`
-- `src/main/presenter/agentRuntimePresenter/dispatch.ts`
-- `src/main/presenter/agentRuntimePresenter/contextBuilder.ts`
-- `src/main/presenter/agentRuntimePresenter/messageStore.ts`
+- `generationSettings` 在 session 创建、草稿和 active session 中统一传递,覆盖 system prompt、
+ temperature、topP、max tokens、reasoning effort、verbosity 等运行时设置。
+- `sessions.compact` 触发手动上下文压缩;自动压缩设置保存在 agent/session 配置中。
+- message trace 独立落库,renderer 通过 `sessions.listMessageTraces` 查询。
+- 失败消息会保留恢复上下文,tool output guard 会限制过大的工具输出进入后续上下文。
+- `agent-core/update_plan` 工具只更新 plan state 和 `chat.plan.updated` event,不把内部 tool call
+ 暴露成普通消息块。
-## 3. 工具调用与权限
+## 3. 工具调用、权限和 Subagents
```mermaid
sequenceDiagram
participant D as AgentRuntimePresenter
participant T as ToolPresenter
participant M as MCP Presenter
- participant G as AgentToolManager
+ participant A as AgentToolManager
participant P as Permission Services
participant R as Renderer
D->>T: getAllToolDefinitions()
D->>T: preCheckToolPermission()/callTool()
- alt agent tool
- T->>G: callTool(name, args, conversationId)
- G->>P: check/consume approvals
- G-->>T: tool result
- else mcp tool
+ alt MCP tool
T->>M: callTool(request)
- M-->>T: tool result
+ M-->>T: result
+ else local agent tool
+ T->>A: callTool(name, args, conversationId)
+ A->>P: check/consume approvals
+ A-->>T: result
end
alt requires interaction
- D-->>R: emit paused interaction
+ D-->>R: paused interaction event
R->>D: respondToolInteraction()
end
```
-关键文件:
+当前本地 agent tools 包括文件系统、命令执行、chat settings、subagent orchestration 等能力。
+Subagent 会话以 `sessionKind='subagent'` 存储,父会话通过 tape merge/discard 处理子会话结果。
+
+## 4. 会话恢复、分页和搜索
+
+```mermaid
+sequenceDiagram
+ participant R as Renderer messageStore
+ participant S as SessionClient
+ participant Route as SessionService
+ participant N as AgentSessionPresenter
+ participant DB as DeepChatMessageStore
+
+ R->>S: restore(sessionId, limit=100)
+ S->>Route: sessions.restore
+ Route->>N: restoreSession
+ N->>DB: listPageBySession
+ DB-->>R: latest page + nextCursor
+ R->>S: listMessagesPage(cursor)
+ S->>Route: sessions.listMessagesPage
+```
-- `src/main/presenter/toolPresenter/index.ts`
-- `src/main/presenter/toolPresenter/agentTools/agentToolManager.ts`
-- `src/main/presenter/toolPresenter/agentTools/agentFileSystemHandler.ts`
-- `src/main/presenter/mcpPresenter/toolManager.ts`
+结构化持久化当前模型:
-## 4. ACP draft session / runtime preparation
+- `deepchat_messages` 存消息头和稳定 JSON fallback。
+- `deepchat_user_messages`、`deepchat_user_message_files`、`deepchat_user_message_links`
+ 存 user message 热字段。
+- `deepchat_assistant_blocks` 存 assistant block 增量。
+- `deepchat_search_documents` / `_fts` 存历史搜索索引。
+
+## 5. ACP Session / Runtime Preparation
```mermaid
sequenceDiagram
@@ -100,39 +129,109 @@ sequenceDiagram
participant A as ACP helpers
R->>N: ensureAcpDraftSession(agentId, projectDir)
- N->>D: initSession(... providerId='acp')
+ N->>D: initSession(providerId='acp')
N->>L: prepareAcpSession(sessionId, agentId, projectDir)
- L->>A: process/session persistence + config helpers
+ L->>A: process/session persistence + config options
L-->>N: ACP session ready
N-->>R: SessionWithState
```
-关键文件:
+ACP 配置选项走 `sessions.getAcpSessionConfigOptions` /
+`sessions.setAcpSessionConfigOption`;远程控制创建 ACP session 时会使用 channel
+`defaultWorkdir` 或全局默认项目路径,并拒绝没有 workdir 的 ACP 默认 agent。
-- `src/main/presenter/agentSessionPresenter/index.ts`
-- `src/main/presenter/llmProviderPresenter/index.ts`
-- `src/main/presenter/llmProviderPresenter/acp/`
+## 6. Spotlight Search
+
+```mermaid
+sequenceDiagram
+ participant UI as Spotlight overlay
+ participant Store as spotlight store
+ participant Session as AgentSessionPresenter
+ participant Settings as settings navigation registry
+
+ UI->>Store: open/query/select
+ Store->>Session: searchHistory(query)
+ Session-->>Store: sessions/messages hits
+ Store->>Settings: merge settings/actions/agents
+ Store-->>UI: mixed results
+```
+
+Spotlight 默认由 `CommandOrControl+P` 打开,混排 recent sessions、agents、settings、actions
+和历史消息。消息命中会写入 pending jump,`ChatPage` 在目标消息加载完成后滚动并高亮。
-## 5. Legacy 数据导入
+## 7. Provider Import And Deeplinks
```mermaid
sequenceDiagram
- participant Hook as lifecycle import hook
- participant N as AgentSessionPresenter
- participant I as LegacyChatImportService
- participant DB as SQLite / legacy tables
-
- Hook->>N: startLegacyImport()
- N->>I: importLegacyChats()
- I->>DB: read legacy conversations/messages
- I->>DB: write new_sessions / deepchat_messages
- I->>DB: backfill normalized message/session hot-path tables
- I-->>N: import status
+ participant OS as deepchat:// URL
+ participant D as DeeplinkPresenter
+ participant W as Settings window
+ participant P as ProviderImportService
+ participant C as ConfigPresenter
+
+ OS->>D: deepchat://provider/install?v=1&data=...
+ D->>W: provider install preview event
+ W->>P: validate/apply preview
+ P->>C: update builtin provider or create custom provider
```
-这个流程仍然保留,但只负责历史数据迁移,不再恢复旧 runtime。
+当前支持:
-关键文件:
+- `deepchat://start`
+- `deepchat://mcp/install`
+- `deepchat://provider/install`
+- provider config import scan/apply,包括 Codex、Claude Code、Cherry Studio、CC Switch 等来源
+- model config import/export,以及 built-in/custom provider 的 credential-only import
+
+## 8. Scheduled Tasks
+
+```mermaid
+sequenceDiagram
+ participant UI as Settings Scheduled Tasks
+ participant Client as ScheduledTasksClient
+ participant Service as ScheduledTasksService
+ participant Notify as NotificationPresenter
+ participant Session as Session creator
+
+ UI->>Client: list/upsert/toggle/delete/fireNow
+ Client->>Service: scheduledTasks.* route
+ Service->>Service: compute next fire time
+ alt notify action
+ Service->>Notify: showNotification
+ else prompt action
+ Service->>Session: create session, optional autoSend
+ end
+```
+
+Triggers 支持 once、daily、weekly;actions 支持 notification 和 prompt。Prompt action 可指定
+agent、provider、model、system prompt,并通过 route runtime 创建会话。
+
+## 9. Remote Control
+
+```mermaid
+flowchart LR
+ Telegram["Telegram"] --> Remote["RemoteControlPresenter"]
+ Feishu["Feishu/Lark"] --> Remote
+ QQ["QQBot"] --> Remote
+ Discord["Discord"] --> Remote
+ WeChat["WeChat iLink"] --> Remote
+ Remote --> Auth["channel auth / binding store"]
+ Remote --> Runner["remote conversation runner"]
+ Runner --> Agent["AgentSessionPresenter"]
+```
+
+统一远程控制支持绑定、默认 agent、默认 workdir、`/sessions`、`/model`、状态输出、媒体/Markdown
+渲染和工具交互提示。各 channel 的协议差异留在 `remoteControlPresenter//`
+和 `remoteControlPresenter/services/*CommandRouter.ts`。
+
+## 10. Local Data Security
+
+SQLite 数据库加密由 `DatabaseSecurityPresenter` 管理:
+
+- `databaseSecurity.getStatus`
+- `databaseSecurity.enable`
+- `databaseSecurity.changePassword`
+- `databaseSecurity.disable`
-- `src/main/presenter/agentSessionPresenter/legacyImportService.ts`
-- `src/main/presenter/lifecyclePresenter/hooks/after-start/legacyImportHook.ts`
+启用后使用 SQLCipher 迁移 `agent.db`,密码优先通过 Electron `safeStorage` 包装保存;
+safeStorage 不可用或解包失败时进入 manual unlock。
diff --git a/docs/README.md b/docs/README.md
index f83e0b63b..d16216fff 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,104 +1,54 @@
# DeepChat 文档索引
-本文档反映 `2026-04-20` 完成 main kernel refactor phase5 自动化收口后的代码结构。
+本文档反映 `2026-05-28` 的当前代码结构。历史 SDD 已清理为“活跃目标才保留”的模型:
+已经落地的实现只在当前项目文档中保留维护信息,不再保留一次性 `spec/plan/tasks`
+过程文档。
-当前仓库的后续治理重点不是再发起一次新的 `main kernel` 全量重写,而是把 renderer-main 边界继续收成
-single-track。换句话说,typed client / typed event 现在已经是默认方向,`useLegacyPresenter()`、
-`window.electron`、`window.api` 只应被视为兼容路径,而不是新功能入口。
-唯一允许的 quarantine 目录固定为 `src/renderer/api/legacy/**`,原先位于
-`src/renderer/api/legacy/presenters.ts` 的 shim 已在 `P5` 退役,剩余 legacy transport
-只允许从 quarantine adapter 引用。
-
-当前 migrated 聊天热路径已经收敛为:
+当前 renderer-main 默认路径是 typed client / typed event:
```text
Renderer
- -> renderer/api (SettingsClient / SessionClient / ChatClient / ProviderClient)
+ -> renderer/api clients
-> window.deepchat
-> shared/contracts/routes + shared/contracts/events
- -> main route runtime (settings handler / SessionService / ChatService / ProviderService)
- -> presenter-backed hot path ports
- -> agentSessionPresenter / configPresenter / llmProviderPresenter / windowPresenter
- -> agentRuntimePresenter / toolPresenter / mcpPresenter / sqlitePresenter
+ -> src/main/routes dispatcher
+ -> route services / presenter-backed ports
+ -> agentSessionPresenter / agentRuntimePresenter / toolPresenter / llmProviderPresenter
```
-`SessionPresenter` 和旧 `conversations/messages` 数据域仍然保留,但只承担兼容、导出和历史数据访问职责,不再是 migrated chat/session hot path 的主入口。`phase5` 的结论也已经固定:当前不继续发起一次性全量 `main kernel` rewrite,后续只在明确 hot path 需要时继续做增量 typed-boundary migration。
+`useLegacyPresenter()`、`window.electron`、`window.api` 只允许作为兼容路径留在
+`src/renderer/api/legacy/**` quarantine 中。业务模块的新能力应从 `renderer/api/*Client`
+和 shared contracts 进入。
## 当前必读
| 文档 | 用途 |
| --- | --- |
-| [ARCHITECTURE.md](./ARCHITECTURE.md) | 当前主架构总览 |
-| [FLOWS.md](./FLOWS.md) | 当前消息、工具、ACP、导入流程 |
+| [ARCHITECTURE.md](./ARCHITECTURE.md) | 当前主架构、能力 owner、typed boundary 规则 |
+| [FLOWS.md](./FLOWS.md) | 当前消息、工具、ACP、导入、定时任务、远程控制流程 |
| [architecture/agent-system.md](./architecture/agent-system.md) | `agentSessionPresenter` / `agentRuntimePresenter` 细节 |
| [architecture/tool-system.md](./architecture/tool-system.md) | `ToolPresenter`、agent tools、ACP helper 分层 |
-| [architecture/session-management.md](./architecture/session-management.md) | 新会话管理与 legacy 数据平面边界 |
+| [architecture/session-management.md](./architecture/session-management.md) | 新会话管理、分页恢复、legacy 数据平面边界 |
+| [architecture/event-system.md](./architecture/event-system.md) | EventBus 与 typed events 的当前分工 |
| [guides/code-navigation.md](./guides/code-navigation.md) | 当前代码导航入口 |
| [guides/getting-started.md](./guides/getting-started.md) | 新开发者快速上手 |
| [guides/plugin-packaging.md](./guides/plugin-packaging.md) | `.dcplugin` 打包、内置分发和 release 规则 |
-| [spec-driven-dev.md](./spec-driven-dev.md) | SDD 目录规则与变更前置流程 |
-| [architecture/baselines/dependency-report.md](./architecture/baselines/dependency-report.md) | 当前依赖与耦合基线 |
-| [architecture/baselines/main-kernel-boundary-baseline.md](./architecture/baselines/main-kernel-boundary-baseline.md) | main kernel refactor 当前阶段的边界指标与 hot path 快照 |
-| [architecture/baselines/main-kernel-bridge-register.md](./architecture/baselines/main-kernel-bridge-register.md) | main kernel refactor 的临时 bridge 登记表 |
-| [architecture/baselines/main-kernel-migration-scoreboard.md](./architecture/baselines/main-kernel-migration-scoreboard.md) | main kernel refactor 的轻量 migration scoreboard |
-| [architecture/renderer-main-single-track/spec.md](./architecture/renderer-main-single-track/spec.md) | `phase5` 之后 renderer-main 单轨化目标与验收标准 |
-| [architecture/renderer-main-single-track/plan.md](./architecture/renderer-main-single-track/plan.md) | 单轨化阶段计划、family 优先级与 merge gate |
-| [architecture/renderer-main-single-track/tasks.md](./architecture/renderer-main-single-track/tasks.md) | 单轨化执行清单 |
-| [architecture/baselines/test-failure-groups.md](./architecture/baselines/test-failure-groups.md) | 当前测试失败分组基线 |
-
-## 本次清理落库
-
-| 位置 | 内容 |
-| --- | --- |
-| [docs/archives/specs-migration-2026-05-02.md](./archives/specs-migration-2026-05-02.md) | 旧 SDD 目录拆分到 features / issues / architecture / archives 的迁移记录 |
-| [docs/archives/legacy-agentpresenter-retirement/spec.md](./archives/legacy-agentpresenter-retirement/spec.md) | 本次 retirement 的目标、范围、兼容边界 |
-| [docs/archives/legacy-agentpresenter-retirement/plan.md](./archives/legacy-agentpresenter-retirement/plan.md) | 迁移/归档/验证计划 |
-| [docs/archives/legacy-agentpresenter-retirement/tasks.md](./archives/legacy-agentpresenter-retirement/tasks.md) | 已执行清单 |
-| [docs/archives/legacy-llm-provider-runtime-retirement/spec.md](./archives/legacy-llm-provider-runtime-retirement/spec.md) | legacy provider runtime retirement 规格 |
-| [docs/archives/legacy-llm-provider-runtime-retirement/plan.md](./archives/legacy-llm-provider-runtime-retirement/plan.md) | provider runtime 收口与依赖清理计划 |
-| [docs/archives/legacy-llm-provider-runtime-retirement/tasks.md](./archives/legacy-llm-provider-runtime-retirement/tasks.md) | provider runtime 退役执行清单 |
-| [docs/archives/provider-layer-simplification/spec.md](./archives/provider-layer-simplification/spec.md) | provider layer 第二轮内部收口规格 |
-| [docs/archives/provider-layer-simplification/plan.md](./archives/provider-layer-simplification/plan.md) | registry + generic provider 合并计划 |
-| [docs/archives/provider-layer-simplification/tasks.md](./archives/provider-layer-simplification/tasks.md) | provider layer 第二轮执行清单 |
-| [docs/archives/ai-sdk-runtime/spec.md](./archives/ai-sdk-runtime/spec.md) | AI SDK runtime 规格,现已更新为 retired 状态 |
-| [docs/architecture/architecture-simplification/spec.md](./architecture/architecture-simplification/spec.md) | 整体减负治理规格 |
-| [docs/architecture/architecture-simplification/plan.md](./architecture/architecture-simplification/plan.md) | 分层/基线/guard 计划 |
-| [docs/architecture/architecture-simplification/tasks.md](./architecture/architecture-simplification/tasks.md) | 首期实施清单 |
-| [docs/archives/agent-cleanup/spec.md](./archives/agent-cleanup/spec.md) | cleanup 主规格,已更新到 retirement 完成态 |
-
-## 主内核边界稳定化计划记录
+| [spec-driven-dev.md](./spec-driven-dev.md) | SDD 目录规则、保留期限与清理规则 |
-以下文档记录的是本轮已经完成的边界稳定化计划、验收证据和后续治理结论。
+## 仍有运行时用途的基线
-重点不再是一次性交付完整 `Clean Main Kernel`,而是优先解决 renderer-main 边界、hot path
-减耦、lifecycle owner 和可测试性问题。当前这些文档既描述实施路径,也记录 `phase5` 收口后的最终状态:
-
-| 位置 | 内容 |
+| 文档 | 用途 |
| --- | --- |
-| [docs/architecture/main-kernel-refactor/spec.md](./architecture/main-kernel-refactor/spec.md) | 收敛后方案的目标、范围、非目标与成功标准 |
-| [docs/architecture/main-kernel-refactor/plan.md](./architecture/main-kernel-refactor/plan.md) | 以边界稳定和热路径减耦为主的阶段计划 |
-| [docs/architecture/main-kernel-refactor/tasks.md](./architecture/main-kernel-refactor/tasks.md) | 项目级任务清单与阶段状态 |
-| [docs/architecture/main-kernel-refactor/acceptance.md](./architecture/main-kernel-refactor/acceptance.md) | 阶段验收口径与本轮最终收口标准 |
-| [docs/architecture/main-kernel-refactor/test-plan.md](./architecture/main-kernel-refactor/test-plan.md) | 围绕 migrated path 的测试分层与 smoke matrix |
-| [docs/architecture/main-kernel-refactor/migration-governance.md](./architecture/main-kernel-refactor/migration-governance.md) | 防止双轨失控的实施纪律、bridge 规则与 scoreboard |
-| [docs/architecture/main-kernel-refactor/build-vs-buy.md](./architecture/main-kernel-refactor/build-vs-buy.md) | 本轮哪些能力引库、哪些能力保持本地实现 |
-| [docs/architecture/main-kernel-refactor/ports-and-scheduler.md](./architecture/main-kernel-refactor/ports-and-scheduler.md) | 最小必要 port 集合与 `Scheduler` 的定位 |
-| [docs/architecture/main-kernel-refactor/route-schema-catalog.md](./architecture/main-kernel-refactor/route-schema-catalog.md) | migrated path 的 route registry、schema 和 typed event 目录 |
-| [docs/architecture/main-kernel-refactor/eventbus-migration.md](./architecture/main-kernel-refactor/eventbus-migration.md) | 本轮对 typed UI event 和 legacy EventBus 的收敛策略 |
-
-## Renderer-Main 单轨化计划记录
-
-以下文档描述的是 `phase5` 收口之后的新执行规则:不再接受 renderer 业务层双轨并存,而是继续把
-`renderer/api + window.deepchat + shared contracts` 固化成唯一默认路径。
-`src/renderer/api/legacy/**` 是唯一允许暂存 legacy transport 的 quarantine 路径。
+| [architecture/baselines/dependency-report.md](./architecture/baselines/dependency-report.md) | 当前依赖与耦合基线 |
+| [architecture/baselines/main-kernel-boundary-baseline.md](./architecture/baselines/main-kernel-boundary-baseline.md) | renderer-main 边界指标与 hot path 快照 |
+| [architecture/baselines/main-kernel-bridge-register.md](./architecture/baselines/main-kernel-bridge-register.md) | legacy bridge 登记表 |
+| [architecture/baselines/main-kernel-migration-scoreboard.md](./architecture/baselines/main-kernel-migration-scoreboard.md) | typed-boundary migration scoreboard |
+| [architecture/baselines/test-failure-groups.md](./architecture/baselines/test-failure-groups.md) | 测试失败分组基线 |
-| 位置 | 内容 |
-| --- | --- |
-| [docs/architecture/renderer-main-single-track/spec.md](./architecture/renderer-main-single-track/spec.md) | 为什么当前分支还不能在双轨状态下直接合并,以及 single-track 的验收标准 |
-| [docs/architecture/renderer-main-single-track/plan.md](./architecture/renderer-main-single-track/plan.md) | 单轨化阶段划分、quarantine 模型、family 迁移顺序与最终 merge gate |
-| [docs/architecture/renderer-main-single-track/tasks.md](./architecture/renderer-main-single-track/tasks.md) | 具体执行清单 |
+这些基线由 `scripts/generate-architecture-baseline.mjs` 生成,`scripts/architecture-guard.mjs`
+会读取其中的 JSON 文件。它们不是历史 SDD,不应随 completed feature 文档一起删除。
-## 活跃架构地图
+## 当前代码地图
```text
docs/
@@ -107,55 +57,37 @@ docs/
├── FLOWS.md
├── architecture/
│ ├── agent-system.md
-│ ├── baselines/
-│ ├── main-kernel-refactor/
-│ ├── renderer-main-single-track/
+│ ├── event-system.md
│ ├── session-management.md
│ ├── tool-system.md
-│ ├── event-system.md
-│ └── mcp-integration.md
+│ └── baselines/
├── features/
-│ └── /
-│ ├── spec.md
-│ ├── plan.md
-│ └── tasks.md
+│ └── /
├── issues/
-│ └── /
-│ ├── spec.md
-│ ├── plan.md
-│ └── tasks.md
+│ └── /
├── guides/
│ ├── getting-started.md
│ ├── code-navigation.md
-│ ├── plugin-packaging.md
-│ └── debugging.md
-└── archives/
- ├── specs-migration-2026-05-02.md
- ├── legacy-agentpresenter-architecture.md
- ├── legacy-agentpresenter-flows.md
- ├── legacy-llm-provider-runtime.md
- ├── thread-presenter-migration-plan.md
- └── workspace-agent-refactoring-summary.md
+│ ├── debugging.md
+│ └── plugin-packaging.md
+└── spec-driven-dev.md
```
-## 历史文档
+## SDD 保留规则
-以下文档只用于追溯 legacy runtime,不再描述当前实现:
-
-| 文档 | 说明 |
-| --- | --- |
-| [archives/legacy-agentpresenter-architecture.md](./archives/legacy-agentpresenter-architecture.md) | 旧 `AgentPresenter` 架构快照 |
-| [archives/legacy-agentpresenter-flows.md](./archives/legacy-agentpresenter-flows.md) | 旧 `startStreamCompletion` / permission / loop 流程 |
-| [archives/legacy-llm-provider-runtime.md](./archives/legacy-llm-provider-runtime.md) | 旧 provider runtime 的历史归档与提交锚点 |
-| [archives/thread-presenter-migration-plan.md](./archives/thread-presenter-migration-plan.md) | 历史迁移设计 |
-| [archives/workspace-agent-refactoring-summary.md](./archives/workspace-agent-refactoring-summary.md) | 历史工作区改造总结 |
+- `docs/features/**`、`docs/issues/**`、`docs/architecture/**` 下的 goal folder 只表示活跃目标。
+- 已实现能力要把当前维护事实并入 `README.md`、`ARCHITECTURE.md`、`FLOWS.md` 或对应 guide,
+ 然后删除旧 SDD 文件夹。
+- bug 修复类 issue SDD 超过两周即清理;按当前日期 `2026-05-28`,本次清理 cutoff 为
+ `2026-05-14` 之前。
+- 过期、未开工、只描述旧实现或旧分支的 SDD 直接删除。
## 阅读建议
1. 先读 [ARCHITECTURE.md](./ARCHITECTURE.md) 建立当前主链路心智模型。
-2. 再读 [FLOWS.md](./FLOWS.md) 看发送消息、工具调用和 ACP 会话的时序。
+2. 再读 [FLOWS.md](./FLOWS.md) 看发送消息、工具调用、导入和远程控制时序。
3. 深入实现时,按模块进入:
- - 聊天执行链路: [architecture/agent-system.md](./architecture/agent-system.md)
- - 工具与权限: [architecture/tool-system.md](./architecture/tool-system.md)
- - 会话与兼容边界: [architecture/session-management.md](./architecture/session-management.md)
-4. 如果需要对照旧实现,再去看 `archives/` 历史文档,不要依赖已经移除的历史源码快照。
+ - 聊天执行链路:[architecture/agent-system.md](./architecture/agent-system.md)
+ - 工具与权限:[architecture/tool-system.md](./architecture/tool-system.md)
+ - 会话与兼容边界:[architecture/session-management.md](./architecture/session-management.md)
+4. 如果需要理解已退休设计,优先用 `git log` / `git show` 追历史提交,不再依赖仓库内长期归档文档。
diff --git a/docs/architecture/acp-client-runtime/plan.md b/docs/architecture/acp-client-runtime/plan.md
deleted file mode 100644
index bb6a7d9d7..000000000
--- a/docs/architecture/acp-client-runtime/plan.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# ACP Client Runtime Plan
-
-## Runtime Boundary
-
-Add `src/main/presenter/acpClientPresenter/` as the internal ACP runtime boundary. The first implementation keeps the current provider-facing API stable by making `AcpProvider` a compatibility adapter over the runtime.
-
-The runtime owns:
-
-- connection/process lifecycle and debug backlog;
-- session lifecycle and MCP forwarding;
-- prompt concurrency;
-- client-side handlers for permissions, filesystem, terminal, and auth;
-- event mapping from ACP updates to DeepChat stream/workspace events.
-
-## Implementation Sequence
-
-1. Create SDD docs and conformance checklist.
-2. Add the ACP runtime facade and prompt controller.
-3. Move provider construction to the runtime facade while keeping public behavior compatible.
-4. Remove warmup `session/new` probing and let real session responses drive config/mode/model state.
-5. Register persisted load-session handlers before `session/load` so replayed updates are not dropped.
-6. Route debug actions through the initialized connection state and real MCP selections.
-7. Harden fs and terminal workdir guards.
-8. Refresh settings on ACP agent change events and release running processes before repair.
-9. Add focused regression tests, then run formatting, i18n, and lint checks.
-
-## Compatibility
-
-- Existing ACP registry/manual agent configuration remains valid.
-- Existing ACP conversation/session records remain valid.
-- Provider id `acp` and model ids remain unchanged.
-- No renderer IPC contract changes are required for this slice.
diff --git a/docs/architecture/acp-client-runtime/spec.md b/docs/architecture/acp-client-runtime/spec.md
deleted file mode 100644
index c03ef8a51..000000000
--- a/docs/architecture/acp-client-runtime/spec.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# ACP Client Runtime
-
-## Problem
-
-DeepChat's ACP integration currently behaves like a provider helper instead of a full ACP client runtime. This causes protocol drift and product bugs:
-
-- warmup can create a temporary `session/new` with empty MCP servers, so agent configuration is probed with a different context than real turns;
-- debug `initialize` can be invoked on a connection that was already initialized;
-- settings updates for install/repair/enable are not always reflected automatically;
-- ACP sessions, file-system access, terminal access, permissions, and session updates do not have a single runtime boundary.
-
-## Goals
-
-- Introduce an internal ACP client runtime boundary that owns connection, session, handler, mapping, workspace, registry, and debug concerns.
-- Align DeepChat with ACP's client responsibilities: spawn stdio agents, initialize once, create/load sessions with the current cwd and MCP servers, forward prompts, handle client-side fs/terminal/permission calls, and map `session/update`.
-- Preserve DeepChat's existing public model selector, Workspace, MCP, Skills, Remote Control, IPC, and renderer route surfaces.
-- Match Zed's configuration boundary: DeepChat forwards cwd/env/MCP/model/mode/config options; ACP agents read their own native configuration directly.
-
-## Non-Goals
-
-- Do not add a Claude-specific or Codex-specific local configuration resolver.
-- Do not reset or migrate already persisted ACP summary/session state beyond compatible schema additions.
-- Do not redesign the model selector UI in this change.
-
-## Protocol Matrix
-
-| ACP area | DeepChat behavior |
-| --- | --- |
-| `initialize` | Sent once per process connection after stdio spawn. Capabilities and auth methods are cached on the connection handle. |
-| `authenticate` | Routed through the ACP auth/terminal handler when required by the agent. |
-| `session/new` | Uses the resolved conversation workdir and DeepChat MCP selections filtered by agent transport capabilities. |
-| `session/load` | Used only when the agent declares load support and DeepChat has a persisted ACP session id. |
-| `session/prompt` | One active prompt per ACP session; response `stopReason` is persisted as turn completion state. |
-| `session/update` | Mapped to DeepChat message/content/tool/plan/diff/terminal/permission events. |
-| `session/request_permission` | Routed through DeepChat permission UI/policy and remote-control-safe resolution. |
-| `fs/read_text_file` and `fs/write_text_file` | Guarded by realpath validation against registered workdirs. |
-| Terminal lifecycle | Bound to the session workdir and routed through DeepChat terminal management. |
-| Debug | Shows lifecycle, request, response, notification, permission, stderr, and error entries without re-initializing an existing connection. |
-
-## Acceptance Criteria
-
-- Warmup never creates a throwaway session solely to fetch config state.
-- Debug initialize starts or reports the initialized connection and does not send a second ACP initialize request.
-- Real sessions are the source of models/modes/config options.
-- Settings refresh after enable/install/repair/uninstall without requiring a manual reopen.
-- ACP fs and terminal operations are scoped to the registered workdir.
-- Non-ACP providers keep their existing context-budget behavior.
diff --git a/docs/architecture/acp-client-runtime/tasks.md b/docs/architecture/acp-client-runtime/tasks.md
deleted file mode 100644
index 008a4a71b..000000000
--- a/docs/architecture/acp-client-runtime/tasks.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# ACP Client Runtime Tasks
-
-- [x] Add SDD spec, plan, and tasks documents.
-- [x] Add internal ACP runtime facade and prompt controller.
-- [x] Stop warmup from creating temporary sessions with empty MCP servers.
-- [x] Use real MCP selections for debug `session/new` and `session/load`.
-- [x] Make debug `initialize` report initialized connection state instead of sending a second initialize request.
-- [x] Register load-session handlers before `session/load` replay can emit updates.
-- [x] Enforce realpath workspace guards for ACP fs reads/writes.
-- [x] Bind terminal cwd to the registered ACP session workdir.
-- [x] Refresh ACP settings on agent-change events.
-- [x] Release running ACP processes before repair.
-- [x] Add targeted ACP regression coverage.
-- [x] Run `pnpm run format`.
-- [x] Run `pnpm run i18n`.
-- [x] Run `pnpm run lint`.
diff --git a/docs/architecture/agent-provider-simplification/plan.md b/docs/architecture/agent-provider-simplification/plan.md
deleted file mode 100644
index 86358985c..000000000
--- a/docs/architecture/agent-provider-simplification/plan.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Agent Provider Simplification (ACP-only) Plan
-
-## Implementation Direction
-
-- Use [spec.md](./spec.md) as the source of requirements and acceptance criteria.
-- Identify the smallest implementation slice that satisfies the documented architecture change goal.
-- Keep renderer-main changes on typed contracts, typed clients, and existing presenter boundaries.
-- Preserve compatibility for stored user data, settings, and exported artifacts unless the spec explicitly defines a migration.
-
-## Validation
-
-- Add or update focused Vitest coverage for changed main, renderer, or shared behavior.
-- Run targeted tests for the touched subsystem before the repository quality gates.
-- Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint` before handoff.
diff --git a/docs/architecture/agent-provider-simplification/spec.md b/docs/architecture/agent-provider-simplification/spec.md
deleted file mode 100644
index 2c6aa6579..000000000
--- a/docs/architecture/agent-provider-simplification/spec.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Agent Provider Simplification (ACP-only)
-
-## Background
-
-DeepChat currently distinguishes between:
-
-- **LLM providers**: network-backed providers that implement `BaseLLMProvider` (OpenAI/Anthropic/etc).
-- **Agent providers**: providers that manage local agent sessions/processes (currently only `acp` via `AcpProvider`).
-
-The codebase implements this distinction via a dedicated base class (`BaseAgentProvider`) and a runtime/type-detection API (`isAgentProvider`), which is then consumed from the renderer via IPC.
-
-## Problem
-
-- `BaseAgentProvider` is only used by `AcpProvider`, so the abstraction adds indirection without real reuse.
-- Provider type detection is over-engineered (`isAgentConstructor` + prototype checks) and duplicates existing ACP-specific branching.
-- The renderer calls `llmproviderPresenter.isAgentProvider(providerId)` over IPC, but the only “agent provider” is `providerId === 'acp'`. This creates unnecessary main↔renderer coupling and call complexity.
-
-## Goals
-
-- Treat **ACP as the only agent provider** and identify it **only by `providerId === 'acp'`**.
-- Remove the generic “agent provider type detection” path and the renderer IPC dependency for this decision.
-- Keep user-visible behavior unchanged:
- - ACP agents still appear as selectable models when ACP is enabled.
- - Non-ACP providers keep the standard model/custom-model refresh behavior.
- - Shutdown and provider disable still clean up ACP resources.
-
-## Non-goals
-
-- Supporting multiple agent providers beyond ACP.
-- Redesigning ACP model derivation (agents-as-models) or session/workspace semantics.
-- Changing persisted provider IDs or stored settings schemas.
-
-## Acceptance Criteria
-
-- Renderer no longer calls `llmproviderPresenter.isAgentProvider(...)`; ACP decision is local (`providerId === 'acp'`).
-- Main process no longer needs `isAgentConstructor` / prototype-based provider classification.
-- No remaining runtime dependency on `BaseAgentProvider` for correctness (ACP cleanup remains correct).
-- `pnpm run typecheck`, `pnpm test`, `pnpm run lint` pass.
-
-## Open Questions
-
-- None.
-
diff --git a/docs/architecture/agent-provider-simplification/tasks.md b/docs/architecture/agent-provider-simplification/tasks.md
deleted file mode 100644
index 283fd4cf7..000000000
--- a/docs/architecture/agent-provider-simplification/tasks.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Agent Provider Simplification (ACP-only) Tasks
-
-- [ ] Confirm [spec.md](./spec.md) has no unresolved `[NEEDS CLARIFICATION]` markers.
-- [ ] Map affected contracts, presenters, renderer clients, stores, and UI components.
-- [ ] Implement the smallest complete slice that satisfies the acceptance criteria.
-- [ ] Add or update focused tests for the changed behavior.
-- [ ] Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`.
diff --git a/docs/architecture/agent-refactor/plan.md b/docs/architecture/agent-refactor/plan.md
deleted file mode 100644
index bd7654a3e..000000000
--- a/docs/architecture/agent-refactor/plan.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Agent Refactor Plan
-
-## P0 Baseline
-
-1. Default DeepChat subagent config:
- - Enable subagents by default.
- - Add self-based `explorer`, `implementer`, and `reviewer` slots.
-2. Prompt composition:
- - Build a single system prompt string.
- - Keep section order stable.
- - Add permission and verification policy sections.
-3. Tool execution:
- - Parallelize all-read-only canonical Agent tool rounds.
- - Preserve result writeback order.
- - Keep writes, edits, commands, and process operations serialized.
-
-## P1 Stable Tool Profile
-
-1. Add a main-layer session tool-profile cache.
-2. Select `code` profile when a project directory is present; otherwise use `general`.
-3. Refresh the profile when project directory, model, disabled tools, or active skills change.
-4. Keep schemas stable across ordinary user messages.
-
-## P2 Follow-Up
-
-1. Expand profile types for `research` and `analysis`.
-2. Add UI affordances for weak agent capability where only legacy tool fallback is available.
-3. Implement the documented tool-result envelope after review.
-4. Extend subagent lifecycle commands for background management.
diff --git a/docs/architecture/agent-refactor/spec.md b/docs/architecture/agent-refactor/spec.md
deleted file mode 100644
index 86eee5e77..000000000
--- a/docs/architecture/agent-refactor/spec.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Agent Refactor Spec
-
-## Goal
-
-Raise the default DeepChat agent baseline without rewriting the presenter architecture. P0 must be useful on its own for code changes, requirement analysis, repository inspection, and structured research.
-
-## Requirements
-
-1. DeepChat agents default to subagents enabled unless explicitly disabled.
-2. Default subagent slots include self-based `explorer`, `implementer`, and `reviewer` roles.
-3. Provider requests receive one composed system message, not multiple system messages.
-4. The composed system prompt order is stable:
- - user/base prompt
- - runtime capabilities
- - environment and `AGENTS.md`
- - skills metadata and pinned skills
- - tooling rules
- - permission rules
- - verification policy
-5. Read-only canonical Agent tools may execute in parallel when a tool-call round contains only read-only calls.
-6. Mutating and runtime canonical tools remain serialized or permission-gated.
-7. Tool schemas are loaded through a stable session profile and cache. Ordinary user messages must not by themselves change the tool profile.
-8. Code-agent verification policy requires final answers to account for verification after code changes.
-
-## Non-Goals
-
-1. Do not implement a per-turn tool router.
-2. Do not rewrite IPC, renderer message flow, or provider runtime architecture.
-3. Do not change the tool-result envelope implementation before the protocol document is reviewed.
-4. Do not remove legacy function-call fallback in this phase.
-
-## Acceptance
-
-1. New or resolved DeepChat agent configs expose enabled subagents and the three default self slots.
-2. Agent runtime requests include at most one `system` role message.
-3. Prompt section order follows the requirement above and includes `AGENTS.md` via the environment section.
-4. A batch of only `read`/`ls`/`find`/`grep` Agent tool calls starts concurrently and writes tool results back in model call order.
-5. Any batch containing `write`/`edit`/`exec`/`process` stays serialized.
-6. Repeated ordinary messages in the same session reuse the stable tool profile unless project directory, disabled tools, model, or active skills change.
diff --git a/docs/architecture/agent-refactor/tasks.md b/docs/architecture/agent-refactor/tasks.md
deleted file mode 100644
index 516652deb..000000000
--- a/docs/architecture/agent-refactor/tasks.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Agent Refactor Tasks
-
-- [x] Add default self-based `explorer`, `implementer`, and `reviewer` subagent slots.
-- [x] Enable subagents by default for DeepChat configs unless explicitly disabled.
-- [x] Update DeepChat agent settings defaults to use the new slot set.
-- [x] Compose one system prompt with stable section ordering.
-- [x] Add permission and verification policy prompt sections.
-- [x] Add a session tool-profile cache in the main runtime.
-- [x] Parallelize all-read-only canonical Agent tool batches.
-- [x] Keep mixed or mutating tool batches serialized.
-- [x] Document the tool-result envelope before implementation.
-- [x] Add UI labeling for weak legacy-only tool-calling models.
-- [x] Implement the tool-result envelope after protocol review.
-- [x] Extend background subagent lifecycle operations.
diff --git a/docs/architecture/agent-refactor/tool-result-envelope.md b/docs/architecture/agent-refactor/tool-result-envelope.md
deleted file mode 100644
index 2dd229de8..000000000
--- a/docs/architecture/agent-refactor/tool-result-envelope.md
+++ /dev/null
@@ -1,112 +0,0 @@
-# Tool Result Envelope
-
-This document defines the target tool-result protocol. The current implementation adds the envelope to Agent tool `rawData.toolResult` at the ToolPresenter boundary while keeping legacy `rawData.content` unchanged for provider-facing model context.
-
-## Shape
-
-```ts
-type AgentToolResult = {
- ok: boolean
- summary: string
- data?: unknown
- meta?: {
- truncated?: boolean
- nextOffset?: number
- offloadPath?: string
- tokenEstimate?: number
- resultCount?: number
- }
- error?: {
- code: string
- message: string
- recoverable?: boolean
- }
-}
-```
-
-## Success
-
-Successful tools set `ok: true`, provide a short model-readable `summary`, and put structured payloads in `data`.
-
-Examples:
-
-```json
-{
- "ok": true,
- "summary": "Found 12 matches in 3 files.",
- "data": {
- "matches": []
- },
- "meta": {
- "resultCount": 12,
- "truncated": false
- }
-}
-```
-
-## Errors
-
-Failed tools set `ok: false`, provide a short summary, and include a stable `error.code`.
-
-```json
-{
- "ok": false,
- "summary": "Invalid arguments for edit.",
- "error": {
- "code": "INVALID_ARGUMENT",
- "message": "oldText is required.",
- "recoverable": true
- }
-}
-```
-
-## Truncation
-
-Tools that return partial data set `meta.truncated: true`. If the same tool can continue from a position, it also sets `meta.nextOffset`.
-
-Renderer display:
-
-- Show the `summary` in compact cards.
-- Show truncation and next-page affordances from `meta`.
-- Keep `data` available for rich views, but do not require the renderer to parse provider-facing prose.
-
-Model-readable behavior:
-
-- Always include the `summary`.
-- Include enough `data` for the next likely model step.
-- Prefer pagination over returning huge blobs.
-
-## Offload
-
-Large outputs may be written to an offload file and represented by `meta.offloadPath`.
-
-Rules:
-
-- `summary` must explain what was offloaded.
-- `data` may contain a preview.
-- The offload path must be readable by the canonical `read` tool when the session has access.
-
-## Batch Results
-
-When multiple tool calls are executed in one model round:
-
-- Preserve the model's original tool-call order in returned tool messages.
-- Each tool message contains one envelope.
-- Parallel execution must not reorder renderer updates or provider-facing tool result messages.
-
-## Renderer Contract
-
-The renderer should treat the envelope as the stable display protocol:
-
-- `ok` controls success/error styling.
-- `summary` powers the collapsed text.
-- `data` feeds rich tool-specific rendering.
-- `meta` handles pagination, truncation, and offload UI.
-- `error` provides retry hints and diagnostics.
-
-## Migration Notes
-
-1. Canonical Agent tools now receive an envelope through `rawData.toolResult` unless a tool already provides a specialized `toolResult`.
-2. Legacy raw tool output remains available through `rawData.content`.
-3. Renderer cards can migrate to the envelope without changing provider-facing tool messages.
-4. MCP passthrough tools and external servers can be extended after renderer support lands.
diff --git a/docs/architecture/agent-system.md b/docs/architecture/agent-system.md
index 2a86dc472..fafe8bb3b 100644
--- a/docs/architecture/agent-system.md
+++ b/docs/architecture/agent-system.md
@@ -1,7 +1,7 @@
# Agent 系统架构详解
-本文档描述 retirement 后仍然有效的 agent system。旧 `AgentPresenter` 细节已归档到
-[../archives/legacy-agentpresenter-architecture.md](../archives/legacy-agentpresenter-architecture.md)。
+本文档描述 retirement 后仍然有效的 agent system。旧 `AgentPresenter` 细节不再作为仓库内
+长期文档保留;需要对照时用 `git log` / `git show` 查看历史提交。
## 当前运行时所有权
@@ -81,6 +81,8 @@ agentRuntimePresenter/
| Tool dispatch | `src/main/presenter/agentRuntimePresenter/dispatch.ts` | 调用 `ToolPresenter`、暂停交互、生成 tool 结果 |
| Context build | `src/main/presenter/agentRuntimePresenter/contextBuilder.ts` | 历史裁剪、resume context、token budget |
| Persistence | `src/main/presenter/agentRuntimePresenter/messageStore.ts` | 消息持久化、分页读取、结构化内容重组与故障恢复 |
+| Compaction | `src/main/presenter/agentRuntimePresenter/compactionService.ts` | 手动/自动上下文压缩与压缩状态消息 |
+| Pending input | `src/main/presenter/agentRuntimePresenter/pendingInputStore.ts` | queued input、steer、重排与恢复 |
## 持久化热路径
@@ -96,6 +98,18 @@ agentRuntimePresenter/
- streaming 期间只增量更新 `deepchat_assistant_blocks`
- 最终进入 `sent/error` 时才写回稳定的 `deepchat_messages.content`
- 读路径优先从结构化表重组 `ChatMessageRecord.content`,缺行时再回退旧 JSON
+- `sessions.restore` 默认只恢复最近一页消息,历史继续通过 `sessions.listMessagesPage` 翻页
+- `deepchat_search_documents` / `_fts` 提供历史搜索索引,FTS 不可用时回退 `LIKE`
+
+## 运行时能力
+
+- Session generation settings 随 session 创建和更新持久化,覆盖 system prompt、temperature、
+ topP、max tokens、reasoning effort、verbosity 等设置。
+- Message trace 独立落库,供消息工具栏查看运行时 trace。
+- Subagent 会话以 `sessionKind='subagent'` 进入同一套 session/message store,父会话通过
+ tape merge/discard 吸收或丢弃子会话结果。
+- 本地录音转写、TTS、image generation、video generation 都复用 provider/model capability 判定,
+ 不再绕开 provider runtime。
## 兼容边界
@@ -132,4 +146,4 @@ agentRuntimePresenter/
- `permissionHandler`
- `startStreamCompletion`
-需要对照旧实现时,只看归档文档,不再把历史源码快照当作活跃导航入口。
+需要对照旧实现时,从历史提交中查看旧源码快照,不再把已经删除的历史设计当作活跃导航入口。
diff --git a/docs/architecture/architecture-simplification/plan.md b/docs/architecture/architecture-simplification/plan.md
deleted file mode 100644
index 7008d31e1..000000000
--- a/docs/architecture/architecture-simplification/plan.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Architecture Simplification Plan
-
-## Workstreams
-
-### 1. Quality Baseline
-
-- 添加 `scripts/generate-architecture-baseline.mjs`,把依赖环、零入边候选、归档引用报告落到 `docs/architecture/baselines/`。
-- 维护一份失败测试分组文档,区分真实行为回归、测试陈旧、环境问题。
-- 把这批基线纳入文档入口,后续每轮治理都基于同一套报告更新。
-
-### 2. Main Composition / Lifecycle
-
-- 新增 `ConfigQueryPort`、`SessionRuntimePort`、`WindowRoutingPort` 等窄接口。
-- 让 `Presenter` 只在 composition root 组装 port;`agentSessionPresenter`、`agentRuntimePresenter` 只依赖 port。
-- 首批迁移目标:
- - 会话 UI 刷新
- - 权限批准 / 清理
- - model catalog 查询
-
-### 3. Renderer Transport / State
-
-- 增加 window context helper,避免各处各自读 `window.api`。
-- 增加 IPC subscription helper,把 `App.vue`、`stores/ui/session.ts`、`stores/ui/message.ts` 的监听注册收口。
-- 把流式状态拆到独立 `stream` store,消息缓存仍保留在 `message` store。
-
-### 4. Archive / Docs
-
-- 首先把开发者导航从历史源码快照切到稳定历史文档。
-- 在文档脱钩和 guard 到位后删除历史源码实体。
-
-## Validation
-
-- `pnpm run format`
-- `pnpm run i18n`
-- `pnpm run lint`
-- `pnpm run typecheck`
-- 针对本次改动的主链路测试与 composable/store 测试
diff --git a/docs/architecture/architecture-simplification/spec.md b/docs/architecture/architecture-simplification/spec.md
deleted file mode 100644
index e18998936..000000000
--- a/docs/architecture/architecture-simplification/spec.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Architecture Simplification
-
-## Summary
-
-本规格定义首期“整体减负治理”的目标:不引入新功能,优先降低 `main` / `renderer` 的心智负担,清理无用代码,减少隐式调用链,并把生命周期、边界与兼容层显式化。
-
-首期只做四类工作:
-
-- 基线收敛
-- 分层与依赖收口
-- 历史归档脱钩
-- 测试可信度恢复
-
-## Goals
-
-- 为 `main` 和 `renderer` 建立可持续更新的结构基线,而不是一次性口头结论。
-- 限制活跃路径继续依赖全局 `presenter`、分散 IPC 监听和历史归档代码。
-- 让 `agentSessionPresenter`、`agentRuntimePresenter`、`App.vue`、`stores/ui/*` 的职责边界更清楚。
-- 为后续删除历史源码归档目录和死文件清理建立前置条件。
-
-## Non-Goals
-
-- 不做新功能。
-- 不在首期重命名对外 IPC channel。
-- 不在首期移除 `SessionPresenter`、legacy import、旧表结构这些兼容边界。
-- 不要求一次性解决全部失败测试,只要求把失败分类、责任边界和修复入口理清。
-
-## Acceptance Criteria
-
-- 仓库存在一组可更新的基线报告,至少覆盖依赖环、零入边候选、归档引用、失败测试分组。
-- 活跃主链路新增 anti-regression guard,阻止历史源码重新进入运行时依赖,阻止首批主链路继续回跳全局 `presenter`。
-- `agentSessionPresenter` / `agentRuntimePresenter` 首批关键路径改为依赖窄接口 port,而不是直接 import `presenter/index.ts`。
-- `renderer` 至少将 `App`、`session`、`message` 这条活跃链路的 IPC 监听收口到 helper / subscription hub,并把流式状态独立成单独状态面。
-- 文档中存在本次治理的 `spec.md`、`plan.md`、`tasks.md`,并且 `docs/README.md` 能导航到这些内容。
-
-## Constraints
-
-- 行为兼容优先,优先保留用户可见行为和现有 IPC surface。
-- 只有经过人工分类的“真正死文件”可以删除。
-- 任何 archive 清理都要先完成文档脱钩。
diff --git a/docs/architecture/architecture-simplification/tasks.md b/docs/architecture/architecture-simplification/tasks.md
deleted file mode 100644
index 9c1421fad..000000000
--- a/docs/architecture/architecture-simplification/tasks.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Architecture Simplification Tasks
-
-## Baseline
-
-- [x] 建立 architecture simplification spec / plan / tasks
-- [x] 新增依赖 / 死代码 / archive 引用基线生成脚本
-- [x] 新增失败测试分组基线文档
-
-## Main
-
-- [x] 引入窄接口 ports
-- [x] 将 `agentSessionPresenter` 的会话 UI 刷新与权限清理改成依赖 port
-- [x] 将 `agentRuntimePresenter` 的权限批准与 env prompt model lookup 改成依赖 port
-- [x] 让 `Presenter` 成为 port 的唯一组装入口
-
-## Renderer
-
-- [x] 引入 window context helper
-- [x] 引入 IPC subscription helper
-- [x] 收口 `App.vue`、`session`、`message` 的活跃监听链路
-- [x] 拆出独立 stream state store
-- [x] 修复 `session store` 对缺失 `sessionKind` 的兼容分类
-- [x] 补齐 `useSearchConfig` 与 search capability 组合逻辑
-
-## Guardrails / Docs
-
-- [x] 新增 architecture guard
-- [x] 将 lint 串上 architecture guard
-- [x] 更新 `docs/README.md`、`guides/*`、架构文档的治理入口
-- [x] 清理剩余 specs 对历史源码快照的直接文件级引用
-- [x] 删除历史源码归档实体
diff --git a/docs/architecture/chat-store-zero-migration/plan.md b/docs/architecture/chat-store-zero-migration/plan.md
deleted file mode 100644
index b845524c2..000000000
--- a/docs/architecture/chat-store-zero-migration/plan.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Chat Store Zero Migration Plan
-
-## Implementation Direction
-
-- Use [spec.md](./spec.md) as the source of requirements and acceptance criteria.
-- Identify the smallest implementation slice that satisfies the documented architecture change goal.
-- Keep renderer-main changes on typed contracts, typed clients, and existing presenter boundaries.
-- Preserve compatibility for stored user data, settings, and exported artifacts unless the spec explicitly defines a migration.
-
-## Validation
-
-- Add or update focused Vitest coverage for changed main, renderer, or shared behavior.
-- Run targeted tests for the touched subsystem before the repository quality gates.
-- Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint` before handoff.
diff --git a/docs/architecture/chat-store-zero-migration/spec.md b/docs/architecture/chat-store-zero-migration/spec.md
deleted file mode 100644
index 2cfeeabfa..000000000
--- a/docs/architecture/chat-store-zero-migration/spec.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Chat Store Zero Migration
-
-## Goal
-
-Retire legacy renderer `chatStore` usage and switch runtime chat flow to `ui/*` stores only.
-
-## Decisions
-
-1. Renderer old chain components depending on `useChatStore` are removed.
-2. New chat flow keeps `ui/session` + `ui/message` event listeners unchanged.
-3. Backup import auto-detects DB file in zip:
- - Prefer `database/agent.db`
- - Fallback to `database/chat.db`
-4. `chat.db` import behavior:
- - `increment`: legacy migration import
- - `overwrite`: clear new session/message domain tables, then migrate legacy data
-
-## Notes
-
-- Startup `legacyImportHook` remains enabled for local migration compatibility.
-- Backup packaging format remains unchanged (`database/agent.db` as primary output).
diff --git a/docs/architecture/chat-store-zero-migration/tasks.md b/docs/architecture/chat-store-zero-migration/tasks.md
deleted file mode 100644
index b16d74043..000000000
--- a/docs/architecture/chat-store-zero-migration/tasks.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Chat Store Zero Migration Tasks
-
-- [ ] Confirm [spec.md](./spec.md) has no unresolved `[NEEDS CLARIFICATION]` markers.
-- [ ] Map affected contracts, presenters, renderer clients, stores, and UI components.
-- [ ] Implement the smallest complete slice that satisfies the acceptance criteria.
-- [ ] Add or update focused tests for the changed behavior.
-- [ ] Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`.
diff --git a/docs/architecture/config-sqlite-storage/plan.md b/docs/architecture/config-sqlite-storage/plan.md
deleted file mode 100644
index 76e8a19c7..000000000
--- a/docs/architecture/config-sqlite-storage/plan.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Config SQLite Storage Plan
-
-## Architecture
-
-- Add SQLite tables using direct business names: `providers`, `provider_models`, `model_status`,
- `model_configs`, `mcp_servers`, `mcp_settings`, `agent_mcp_selections`, and `agent_settings`.
-- Keep the existing `agents` table and `AgentRepository` flow. Only residual ACP settings move out
- of the old `acp_agents` store.
-- Add DB-backed store adapters that mimic the small ElectronStore surface used by existing helpers.
- This lets `ProviderHelper`, `ProviderModelHelper`, `ModelStatusHelper`, `ModelConfigHelper`,
- `McpConfHelper`, and `AcpConfHelper` keep most existing behavior.
-- Attach SQLite-backed config after database and agent repository initialization. Until then,
- ConfigPresenter uses JSON for startup-critical settings.
-
-## Migration
-
-- Read legacy provider/model/MCP/ACP data from current ElectronStore-backed helpers before switching
- helpers to DB-backed stores.
-- Import legacy data into the new tables inside an idempotent migration guarded by
- `config_migrations`.
-- Preserve legacy JSON files after successful import.
-
-## Compatibility
-
-- Keep existing route contracts and presenter method signatures unchanged.
-- Return `LLM_PROVIDER[]`, `MODEL_META[]`, `MCPServerConfig`, and model config exports in their
- current shapes.
-- Keep old backup import support for legacy archives that still contain `mcp-settings.json`.
-
-## Backup
-
-- Continue backing up `agent.db`.
-- Back up app settings as lightweight JSON with migrated provider/model keys filtered out.
-- Continue backing up prompts and system prompts until their second-phase migration.
diff --git a/docs/architecture/config-sqlite-storage/spec.md b/docs/architecture/config-sqlite-storage/spec.md
deleted file mode 100644
index 52b8e68d8..000000000
--- a/docs/architecture/config-sqlite-storage/spec.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Config SQLite Storage
-
-## User Story
-
-DeepChat configuration has outgrown JSON storage. Provider, model, MCP, and agent-adjacent
-configuration should move to SQLite so frequent updates and larger collections remain structured,
-queryable, and backed up with the main application database.
-
-## Acceptance Criteria
-
-- Provider and model configuration reads/writes keep the existing presenter and route behavior.
-- MCP server configuration keeps the existing presenter and route behavior.
-- Existing `agents` table remains unchanged in name and remains the source for AgentRepository data.
-- Lightweight startup settings remain in JSON so logging, proxy, language, theme, sync path, and
- similar environment settings can be read before SQLite-backed config is attached.
-- Legacy JSON/electron-store data is imported into SQLite exactly once and importing is idempotent.
-- Sync backups include SQLite-backed configuration through `agent.db`; JSON backups exclude migrated
- provider/model keys.
-
-## Non-Goals
-
-- Do not change renderer route contracts or legacy presenter method names.
-- Do not rename existing SQLite tables.
-- Do not migrate prompts, system prompts, knowledge configs, or Nowledge-mem settings in this
- increment.
-- Do not introduce secret encryption changes for API keys in this increment.
-
-## Constraints
-
-- The current startup sequence reads logging and proxy settings before the database is attached.
-- The existing `ConfigPresenter` must continue to work as a compatibility facade.
-- Old JSON files are retained after migration for rollback safety.
diff --git a/docs/architecture/config-sqlite-storage/tasks.md b/docs/architecture/config-sqlite-storage/tasks.md
deleted file mode 100644
index a9589d828..000000000
--- a/docs/architecture/config-sqlite-storage/tasks.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Config SQLite Storage Tasks
-
-- [x] Add SQLite schema and table helpers for provider/model/MCP/ACP residual config.
-- [x] Add DB-backed store adapters for existing ConfigPresenter helpers.
-- [x] Wire ConfigPresenter to attach SQLite-backed config after database initialization.
-- [x] Add idempotent migration from legacy JSON/electron-store stores.
-- [x] Update SyncPresenter backup filtering for migrated app-settings keys.
-- [x] Add focused tests for repositories, migration, route compatibility, startup, and sync.
-- [x] Run format, i18n, lint, and targeted tests.
diff --git a/docs/architecture/event-system.md b/docs/architecture/event-system.md
index b3a131279..23c937b92 100644
--- a/docs/architecture/event-system.md
+++ b/docs/architecture/event-system.md
@@ -6,7 +6,7 @@
- 当前 active renderer-main boundary 已经优先走 `renderer/api/*Client` + `window.deepchat` + typed contracts
- 下文中涉及 `useLegacyPresenter()`、`window.api`、raw `window.electron` 的内容,应视为 legacy / compatibility 背景
-- `phase5` 之后的 single-track 规则见 `docs/architecture/renderer-main-single-track/`
+- 当前 single-track 规则见 `docs/ARCHITECTURE.md` 的 renderer-main boundary 章节
## 📋 核心组件
@@ -850,4 +850,3 @@ export const useChatStore = defineStore('chat', {
- [Agent 系统详解](./agent-system.md)
- [工具系统详解](./tool-system.md)
- [核心流程](../FLOWS.md)
-
diff --git a/docs/architecture/main-kernel-refactor/acceptance.md b/docs/architecture/main-kernel-refactor/acceptance.md
deleted file mode 100644
index be9c018a9..000000000
--- a/docs/architecture/main-kernel-refactor/acceptance.md
+++ /dev/null
@@ -1,216 +0,0 @@
-# Main Kernel Refactor Acceptance
-
-## Acceptance Model
-
-本项目采用双层验收:
-
-- 阶段验收:每个 phase 都必须独立通过
-- 最终验收:所有阶段完成后,再检查本轮“边界稳定化”目标是否达成
-
-本轮验收重点不是“新目录是否漂亮”,而是:
-
-- migrated path 是否更稳定
-- owner 是否更清楚
-- 测试是否更容易写
-
-## Phase Gate Rules
-
-每个阶段都必须同时满足以下条件:
-
-1. 该阶段定义的真实 slice 或主路径切换已经完成
-2. 该阶段引入的 bridge 已登记 `deleteByPhase`
-3. 该阶段要求的自动化验证已通过
-4. 该阶段要求的 smoke 验证已通过
-5. legacy 指标相对上一阶段净下降,或至少没有反弹
-
-## Governance Hard Requirements
-
-以下要求来自 [migration-governance.md](./migration-governance.md),属于硬门槛:
-
-- 同一条用户路径只能有一个 active owner
-- bridge 只能单向 `old -> new`
-- foundation 工作不能连续多轮脱离真实 slice
-- 旧实现一旦进入迁移阶段即冻结
-- migrated path 完成后必须看到可证明的耦合净下降
-
-## Phase Acceptance Criteria
-
-### Phase 0: Guardrails & Baseline
-
-- 能阻止新增 `useLegacyPresenter()`、新增 raw renderer IPC、新增 migrated path raw channel
-- 能输出 renderer / preload / hot path 相关趋势基线
-- bridge register 和 scoreboard 模板可用
-
-### Phase 1: Typed Boundary Foundation
-
-- 存在统一的 shared route registry
-- 存在 typed event catalog,至少覆盖 settings / sessions / chat 首批事件
-- preload bridge 和 `renderer/api` client 已能驱动首批主路径
-- 新增功能不再依赖 `useLegacyPresenter()` 接入
-
-### Phase 2: Settings Pilot Slice
-
-- settings 主读写链路走新 contract + client + handler
-- settings 相关 renderer/store 不再依赖旧 presenter 作为主入口
-- settings 变更通知通过 typed event 可追踪
-
-### Phase 3: Chat & Session Hot Path
-
-- session create / restore / activate 中至少主路径 owner 已明确
-- 发送消息、停止流主链路走显式 orchestration,而不是继续靠 presenter 互调
-- timeout / retry / cancel 通过 `Scheduler` 管理
-- migrated path 上的 cleanup 行为可测
-
-### Phase 4: Provider / Tool Boundary
-
-- provider query / execution / session 配置边界具备明确 port 或 adapter
-- permission / tool response 通过明确 contract 或 typed event 处理
-- migrated path 上 presenter 对 provider 的直接依赖已降到可解释范围
-
-### Phase 5: Consolidation & Re-evaluation
-
-- 本轮新增 bridge 已删除
-- 文档、baseline、scoreboard、smoke 记录已同步
-- 已形成“是否继续做下一轮更彻底 kernel 重构”的结论
-
-## Phase Status
-
-- [x] Phase 0 completed on `2026-04-19`
-- Phase 0 evidence lives in `scripts/architecture-guard.mjs` and `scripts/generate-architecture-baseline.mjs`
-- Phase 0 artifacts are `docs/architecture/baselines/main-kernel-boundary-baseline.md`
-- Phase 0 artifacts are `docs/architecture/baselines/main-kernel-bridge-register.md`
-- Phase 0 artifacts are `docs/architecture/baselines/main-kernel-migration-scoreboard.md`
-- Phase 0 establishes the baseline checkpoint, so later phases compare against this snapshot instead of a prior phase delta
-- [x] Phase 1 completed on `2026-04-19`
-- Phase 1 evidence lives in `src/shared/contracts/`, `src/preload/createBridge.ts`, `src/main/routes/`, and `src/renderer/api/`
-- Phase 1 automated verification covers `test/main/routes/contracts.test.ts`
-- Phase 1 automated verification covers `test/main/routes/dispatcher.test.ts`
-- Phase 1 automated verification covers `test/renderer/api/createBridge.test.ts`
-- Phase 1 automated verification covers `test/renderer/api/clients.test.ts`
-- Phase 1 artifacts are the refreshed `docs/architecture/baselines/main-kernel-*.{md,json}` reports at current phase `P1`
-- Phase 2 implementation and automated verification completed on `2026-04-19`
-- Phase 2 evidence lives in `src/main/routes/settings/`, `src/main/routes/index.ts`, `src/renderer/api/SettingsClient.ts`, and `src/renderer/src/stores/uiSettingsStore.ts`
-- Phase 2 automated verification covers `test/main/routes/contracts.test.ts`
-- Phase 2 automated verification covers `test/main/routes/dispatcher.test.ts`
-- Phase 2 automated verification covers `test/main/routes/settingsHandler.test.ts`
-- Phase 2 automated verification covers `test/renderer/api/clients.test.ts`
-- Phase 2 automated verification covers `test/renderer/stores/uiSettingsStore.test.ts`
-- Phase 2 artifacts are the refreshed `docs/architecture/baselines/main-kernel-*.{md,json}` reports at current phase `P2`
-- Phase 2 manual smoke handoff is documented in `docs/architecture/main-kernel-refactor/test-plan.md`
-- [x] Phase 3 completed on `2026-04-19`
-- Phase 3 evidence lives in `src/main/routes/chat/`, `src/main/routes/sessions/`, `src/main/routes/hotPathPorts.ts`, `src/main/routes/scheduler.ts`, and the migrated renderer hot path under `src/renderer/src/stores/ui/` plus `src/renderer/src/pages/ChatPage.vue`
-- Phase 3 migrated renderer boundary now uses `SessionClient` / `ChatClient` for `sessions.restore`, `sessions.activate`, `sessions.deactivate`, `sessions.getActive`, `chat.sendMessage`, and `chat.stopStream`
-- Phase 3 automated verification covers `test/main/routes/contracts.test.ts`
-- Phase 3 automated verification covers `test/main/routes/dispatcher.test.ts`
-- Phase 3 automated verification covers `test/main/routes/sessionService.test.ts`
-- Phase 3 automated verification covers `test/main/routes/chatService.test.ts`
-- Phase 3 automated verification covers `test/main/routes/scheduler.test.ts`
-- Phase 3 automated verification covers `test/renderer/api/clients.test.ts`
-- Phase 3 automated verification covers `test/renderer/stores/pageRouter.test.ts`
-- Phase 3 automated verification covers `test/renderer/stores/messageStore.test.ts`
-- Phase 3 automated verification covers `test/renderer/stores/sessionStore.test.ts`
-- Phase 3 follow-up verification on `2026-04-19` restored typed incremental stream delivery in `src/main/presenter/agentRuntimePresenter/echo.ts`, aligned rate-limit and deferred terminal-error branches with typed `chat.stream.*` events in `src/main/presenter/agentRuntimePresenter/index.ts`, and reintroduced renderer-side persisted refresh fallback in `src/renderer/src/stores/ui/messageIpc.ts`
-- Phase 3 follow-up verification covers `test/main/presenter/agentRuntimePresenter/echo.test.ts`
-- Phase 3 follow-up verification covers `test/renderer/components/ChatPage.test.ts`
-- Phase 3 artifacts are the refreshed `docs/architecture/baselines/main-kernel-*.{md,json}` reports at current phase `P3`
-- Phase 3 legacy metrics confirm net reduction versus the Phase 0 checkpoint: `renderer.usePresenter.count` `89 -> 87`, `renderer.windowApi.count` `34 -> 33`, `hotpath.presenterEdge.count` `11 -> 10`, and `bridge.active.count` remains `0`
-- Phase 3 manual smoke handoff is documented in `docs/architecture/main-kernel-refactor/test-plan.md`
-- [x] Phase 4 completed on `2026-04-20`
-- Phase 4 evidence lives in `src/main/presenter/runtimePorts.ts`, `src/main/presenter/index.ts`, `src/main/presenter/agentRuntimePresenter/index.ts`, `src/main/presenter/agentSessionPresenter/index.ts`, `src/main/routes/providers/providerService.ts`, `src/main/routes/hotPathPorts.ts`, `src/main/routes/index.ts`, `src/shared/contracts/routes/providers.routes.ts`, `src/shared/contracts/routes/chat.routes.ts`, `src/renderer/api/ProviderClient.ts`, `src/renderer/api/ChatClient.ts`, `src/renderer/src/stores/providerStore.ts`, and `src/renderer/src/pages/ChatPage.vue`
-- Phase 4 migrated renderer boundary now uses `ChatClient.respondToolInteraction` for permission/question responses and `ProviderClient.testConnection` for provider verification
-- Phase 4 automated verification covers `test/main/routes/contracts.test.ts`
-- Phase 4 automated verification covers `test/main/routes/dispatcher.test.ts`
-- Phase 4 automated verification covers `test/main/routes/chatService.test.ts`
-- Phase 4 automated verification covers `test/main/routes/providerService.test.ts`
-- Phase 4 automated verification covers `test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts`
-- Phase 4 automated verification covers `test/main/presenter/agentSessionPresenter/agentSessionPresenter.test.ts`
-- Phase 4 automated verification covers `test/main/presenter/agentSessionPresenter/integration.test.ts`
-- Phase 4 automated verification covers `test/main/presenter/agentSessionPresenter/usageDashboard.test.ts`
-- Phase 4 automated verification covers `test/renderer/api/clients.test.ts`
-- Phase 4 automated verification covers `test/renderer/components/ChatPage.test.ts`
-- Phase 4 automated verification covers `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, `pnpm run typecheck`
-- Phase 4 automated verification covers `node scripts/architecture-guard.mjs`
-- Phase 4 automated verification covers `pnpm run architecture:baseline`
-- Phase 4 artifacts are the refreshed `docs/architecture/baselines/main-kernel-*.{md,json}` reports at current phase `P4`
-- Phase 4 legacy metrics confirm no rebound versus the Phase 3 checkpoint while continuing the renderer boundary reduction: `renderer.usePresenter.count` `87 -> 86`, `renderer.windowElectron.count` `95 -> 95`, `renderer.windowApi.count` `33 -> 33`, `hotpath.presenterEdge.count` `10 -> 10`, and `bridge.active.count` remains `0`
-- Phase 4 manual smoke handoff is documented in `docs/architecture/main-kernel-refactor/test-plan.md`
-- [x] Phase 5 completed on `2026-04-20`
-- Phase 5 audit confirmed the bridge register remains empty, so no main-kernel temporary bridge survived into `P5`
-- Phase 5 automated verification covers the targeted migrated-path regression pack in `test/main/routes/*.test.ts`, `test/main/presenter/agentRuntimePresenter/*.test.ts`, `test/main/presenter/agentSessionPresenter/*.test.ts`, `test/renderer/api/*.test.ts`, `test/renderer/stores/*.test.ts`, and `test/renderer/components/ChatPage.test.ts`
-- Phase 5 automated verification covers `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, `pnpm run typecheck`
-- Phase 5 automated verification covers `pnpm run architecture:baseline`
-- Phase 5 artifacts are the refreshed `docs/architecture/baselines/main-kernel-*.{md,json}` reports at current phase `P5`
-- Phase 5 documentation refresh covers `docs/README.md`, `docs/ARCHITECTURE.md`, and `docs/guides/code-navigation.md`
-- Phase 5 legacy metrics confirm the Phase 4 checkpoint held through final consolidation: `renderer.usePresenter.count` `86 -> 86`, `renderer.windowElectron.count` `95 -> 95`, `renderer.windowApi.count` `33 -> 33`, `hotpath.presenterEdge.count` `10 -> 10`, `migrated.rawChannel.count` `5 -> 5`, and `bridge.active.count` remains `0`
-- Phase 5 final smoke handoff is documented in `docs/architecture/main-kernel-refactor/test-plan.md`
-- Phase 5 conclusion: do not start a full `main kernel` rewrite now; keep using slice-driven typed boundary migrations only when a concrete hot path still justifies it
-
-## Final Acceptance Checklist
-
-结构性签收项已根据 `2026-04-20` 的自动化验证结果勾选;用户可见行为项保留到最终手工 smoke 后确认。
-
-### Boundary
-
-- [x] migrated path 的 renderer 调用统一走 `renderer/api` + `window.deepchat`
-- [x] migrated path 不再新增 `useLegacyPresenter()`、`window.electron`、`window.api` 依赖
-- [x] migrated path 的 route 和 typed event 都能在共享 registry / catalog 中追踪
-- [x] 组件和 store 不直接拼新的 raw channel 字符串
-
-### Runtime Ownership
-
-- [x] settings、chat、session、provider 这些 migrated path 都有明确 owner
-- [x] `AgentSessionPresenter -> AgentRuntimePresenter` 不再是 migrated chat path 的主 owner 链
-- [x] provider query / execution / permission 边界可解释,不依赖全局隐式协作
-- [x] session / stream / permission cleanup 有明确 owner
-
-### Lifecycle and Scheduling
-
-- [x] cancel、timeout、retry 在 migrated chat path 上走 `Scheduler`
-- [x] window/session 相关 listener、subscription、abort controller 的清理可验证
-- [x] 现有 `LifecycleManager` 或等价 setup 模块中,migrated path 的装配关系是可读的
-
-### Cleanup
-
-- [x] 本轮涉及的临时 bridge 已按计划删除
-- [x] 对应 slice 的旧 owner 已冻结,不再继续长新逻辑
-- [x] hot path 直连依赖相较基线净下降
-- [x] 本轮不要求 `src/main/presenter` 目录归零,但 migrated path 不再依赖它的旧协作方式
-
-### Quality
-
-- [x] route / client / service / scheduler / provider boundary 具备对应测试
-- [x] `pnpm run format`、`pnpm run i18n`、`pnpm run lint`、`pnpm run typecheck` 通过
-- [x] baseline、tasks、test-plan、README 已同步更新
-
-### User-Visible Behavior
-
-- [ ] 修改设置正常
-- [ ] 创建会话正常
-- [ ] 恢复会话正常
-- [ ] 发送消息正常
-- [ ] 流式回复正常
-- [ ] 停止流正常
-- [ ] provider 相关关键交互正常
-- [ ] 权限交互正常
-
-## What Is Not Required For Sign-Off
-
-以下内容不属于本轮最终签收硬门槛:
-
-- `src/main/presenter` 整体删除
-- `ServiceLocator` / singleton 在全仓归零
-- 完整 clean architecture 目录搬迁
-- EventBus 全量重写
-- 明显内存下降
-
-## Evidence Required Before Final Sign-Off
-
-- 最新基线报告
-- route registry 与 typed event catalog 摘要
-- bridge register
-- migration scoreboard
-- 自动化测试结果摘要
-- smoke 记录
-- 文档更新记录
-- 对“是否继续做更彻底 kernel 重构”的结论说明
-
diff --git a/docs/architecture/main-kernel-refactor/build-vs-buy.md b/docs/architecture/main-kernel-refactor/build-vs-buy.md
deleted file mode 100644
index 3cf9d9f71..000000000
--- a/docs/architecture/main-kernel-refactor/build-vs-buy.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# Main Kernel Refactor Build vs Buy
-
-## Purpose
-
-本文件定义本轮“边界稳定化”工作的引库策略。
-
-本轮原则不是尽量多造轮子,也不是尽量多引框架,而是:
-
-- 用成熟库解决通用问题
-- 把 DeepChat 的边界、生命周期语义和 Electron 安全面留在仓库内掌控
-
-## Decision Rule
-
-采用以下判断标准:
-
-- 如果问题是通用基础设施问题,并且能明显减少重复劳动,优先考虑引库
-- 如果问题直接定义 renderer-main 能力边界、owner 关系或生命周期语义,优先自写
-- 新引入的第三方库必须能被本地接口或 facade 包裹,不能直接成为业务层心智模型
-
-## Decisions
-
-| Area | Decision | Why |
-| --- | --- | --- |
-| schema/contracts | 继续使用 `zod` | 已有基础最好,统一性高 |
-| preload bridge / route registry | 自写 | 属于产品能力边界和 Electron 安全边界 |
-| renderer client | 自写 | 需要贴合现有 store / composable 用法 |
-| 架构 guard/baseline | 先用现有脚本,必要时再引 `dependency-cruiser` | 当前已有脚本,先控制复杂度 |
-| retry/backoff | `p-retry` 可批准 | 适合放在 `Scheduler.retry` 内部 |
-| queue/backpressure | `p-queue` 仅在明确证据下评估 | 不是第一批刚需 |
-| DI container | 本轮不引入 | 本轮重点是热路径 owner,而不是容器框架 |
-| lifecycle helper / scope helper | 按需小自写 | 如果 cleanup 语义需要,再补最小 helper |
-| EventBus 全量重写 | 本轮不作为前置要求 | 先解决 migrated path 的 typed event 与 owner |
-
-## Approved Third-Party Libraries
-
-默认可批准:
-
-- `zod`
-- `p-retry`
-
-条件成立时再评估:
-
-- `dependency-cruiser`
-- `p-queue`
-
-当前不作为第一批依赖:
-
-- `Awilix`
-- `tsyringe`
-- `Inversify`
-- `Emittery`
-- `neverthrow`
-
-## Self-Owned Components
-
-以下组件明确由仓库内自写并长期掌控:
-
-- shared route registry
-- typed event catalog
-- preload bridge builder
-- `renderer/api/*Client`
-- `Scheduler` interface
-- provider / session / permission 的窄 port
-
-这些组件直接定义:
-
-- renderer-main 边界
-- 迁移期 owner 关系
-- 测试替身注入方式
-
-## DI and Composition Policy
-
-本轮不单独引入 DI container。
-
-默认做法:
-
-- 用显式 factory / setup function 装配依赖
-- 用局部 interface 或 port 缩窄依赖面
-- 只有当 session / window cleanup 真的需要一致 dispose 语义时,再补最小 helper
-
-一句话:
-
-- 先把 owner 讲清楚
-- 再决定是否真的需要 container
-
-## Event Policy
-
-本轮先做两件事:
-
-- 冻结 legacy `eventBus` 的错误扩张
-- 为 migrated path 建立 typed UI event
-
-本轮不要求:
-
-- 先把整个 EventBus 系统重写
-- 先把全部字符串事件搬迁完
-
-如果未来确实需要 internal typed bus,再在下一轮评估。
-
-## Scheduler Policy
-
-`Scheduler` 仍建议作为本地接口存在,原因是:
-
-- 业务层要表达 timeout / retry / sleep 的语义
-- 测试需要 fake scheduler
-- cancel 行为需要统一 owner
-
-实施要求:
-
-- 业务层依赖 `Scheduler`,而不是直接依赖 `setTimeout` 或 `p-retry`
-- `p-retry` 只允许出现在 scheduler adapter 内部
-
-## Error Model Policy
-
-`neverthrow` 暂不进入本轮。
-
-原因:
-
-- 本轮已经同时在切边界、owner 和测试结构
-- 现在再切错误模型,变量太多
-
-建议:
-
-- 先把 migrated path 稳住
-- 等 hot path 稳定后,再评估是否值得试点
-
-## Adoption Rules
-
-新增第三方库前必须回答:
-
-1. 它解决的是通用基础设施问题,还是 DeepChat 特有边界问题?
-2. 它是否会改变 renderer-main 或 hot path owner 的心智模型?
-3. 它能否被本地接口包住?
-4. 它是否直接减少当前迁移成本?
-
-如果以上答案不够明确,本轮默认不引。
diff --git a/docs/architecture/main-kernel-refactor/eventbus-migration.md b/docs/architecture/main-kernel-refactor/eventbus-migration.md
deleted file mode 100644
index 7bf026993..000000000
--- a/docs/architecture/main-kernel-refactor/eventbus-migration.md
+++ /dev/null
@@ -1,134 +0,0 @@
-# Main Kernel Refactor EventBus Migration
-
-## Purpose
-
-本文件定义本轮对事件系统的收敛策略。
-
-关键点只有一句话:
-
-- 本轮不先重写整个 EventBus
-- 本轮先为 migrated path 建立 typed UI event,并冻结 legacy event 的继续扩张
-
-## Current State
-
-当前事件系统的核心问题:
-
-- `src/main/eventbus.ts` 同时承担 main 内部通知和 main -> renderer 广播
-- `eventBus` 直接依赖 `windowPresenter`
-- `src/main/events.ts` 中字符串事件常量混合了多种语义
-- renderer 仍习惯直接监听 `window.electron.ipcRenderer.on(...)`
-
-这说明现在的问题是“职责混杂”,但并不意味着本轮必须把整套事件系统一次性改完。
-
-## Target State For This Program
-
-本轮只要求达到以下状态:
-
-```text
-shared/contracts/events # migrated path 的 typed UI event 定义
-preload bridge # 对 typed event 的统一订阅入口
-renderer/api clients # 吸收 bridge 细节
-main publisher/adapters # migrated path 通过明确 publisher 发事件
-legacy eventBus # 继续服务于未迁移路径,但冻结新增错误用法
-```
-
-## Event Taxonomy For This Program
-
-### Typed UI Events
-
-本轮优先处理:
-
-- `settings.changed`
-- `sessions.updated`
-- `chat.stream.updated`
-- `chat.stream.completed`
-- `chat.stream.failed`
-
-这些事件直接服务于 migrated path 的 renderer 刷新。
-
-### Legacy Internal Events
-
-现有 `eventBus` 仍可继续承接未迁移路径的内部通知,但:
-
-- 不再给 migrated path 新增依赖
-- 不再把新 UI 通知继续挂在 raw string event 上
-
-## Old To New Mapping
-
-### Migrated Path Mapping
-
-| Old pattern | New pattern |
-| --- | --- |
-| `eventBus.sendToRenderer(...)` | typed UI event publisher |
-| `window.electron.ipcRenderer.on(...)` | preload bridge subscription + renderer client/store adapter |
-| renderer 直接监听 raw event 名 | renderer 通过 typed event client / helper 订阅 |
-
-### Legacy Path Policy
-
-对于未迁移路径:
-
-- 保持现状可接受
-- 但禁止继续扩张错误用法
-
-## Migration Strategy
-
-### Phase 1
-
-- 定义 typed UI event catalog
-- 冻结 migrated path 上新增 raw renderer event 监听
-- 为 preload bridge 准备统一订阅面
-
-### Phase 2
-
-- settings slice 改为 typed `settings.changed`
-- settings store 不再依赖 raw IPC 监听
-
-### Phase 3
-
-- chat/session migrated path 改为 typed `sessions.updated`、`chat.stream.*`
-- 新的 stop / restore / send 主链路不再直接依赖 `eventBus.sendToRenderer`
-
-### Phase 4
-
-- provider / permission 相关 migrated path 如需通知 renderer,优先走 typed event
-- 若发现 legacy eventBus 成为 hot path 阻塞点,再评估是否值得拆 internal bus
-
-### Phase 5
-
-- 回顾本轮迁移范围内是否还存在 legacy raw event 依赖
-- 决定下一轮是否真的需要全量 EventBus 重写
-
-## Proposed Publisher Shape
-
-本轮只需要一个足够简单的发布能力:
-
-```ts
-export interface WindowEventPort {
- publish(eventName: string, payload: TPayload): Promise
- publishToWindow?(windowId: number, eventName: string, payload: TPayload): Promise
- publishToWebContents?(
- webContentsId: number,
- eventName: string,
- payload: TPayload
- ): Promise
-}
-```
-
-重点不是接口有多复杂,而是:
-
-- migrated path 不再隐式依赖全局 `eventBus`
-- renderer 收到的是 typed contract,而不是内部常量泄漏
-
-## Completion Criteria
-
-本轮在事件系统上的完成标准是:
-
-- migrated path 的 UI 通知具备 typed event
-- renderer 不再为 migrated path 新增 raw event listener
-- 新主路径不再继续依赖 `eventBus.sendToRenderer`
-
-本轮不要求:
-
-- 删除 `src/main/eventbus.ts`
-- 删除 `src/main/events.ts`
-- 完成全量事件分类重写
diff --git a/docs/architecture/main-kernel-refactor/migration-governance.md b/docs/architecture/main-kernel-refactor/migration-governance.md
deleted file mode 100644
index 89666e609..000000000
--- a/docs/architecture/main-kernel-refactor/migration-governance.md
+++ /dev/null
@@ -1,189 +0,0 @@
-# Main Kernel Refactor Migration Governance
-
-## Purpose
-
-本文件定义本轮“边界稳定化”实施纪律。
-
-它解决的不是“方案是否足够理想”,而是:
-
-- 如何避免一边加新层,一边让旧耦合继续长大
-- 如何确保每一轮迁移都真的减少复杂度
-
-## Core Risk
-
-最大风险不是技术写不出来,而是出现下面这种失控状态:
-
-- 新 contract、新 client、新 service 都建了
-- 旧 presenter 仍在承载同一条主路径
-- bridge 没写删除时点
-- 基线数字没有下降
-
-一旦如此,项目就会同时承担两套心智模型,维护成本比现在更高。
-
-## Operating Principles
-
-### 1. Single Active Owner
-
-同一条用户路径在任一时刻只能有一个主 owner。
-
-例子:
-
-- settings 主读写只能由新 typed boundary 或旧 presenter 之一承载
-- send / stop / restore 这类 chat/session 主链路不能同时由新 orchestration 和旧 presenter 都算主入口
-
-### 2. One-Way Bridge Only
-
-迁移期间允许 bridge,但 bridge 只能是:
-
-```text
-old entry -> new implementation
-```
-
-禁止:
-
-```text
-new client -> legacy direct IPC
-new service -> old presenter
-new typed route -> presenter reflection dispatcher
-```
-
-### 3. Boundary Before Expansion
-
-本轮新增功能如果涉及 renderer-main 能力,必须先进入 typed boundary。
-
-不允许一边写“未来会迁移”,一边继续新增 `useLegacyPresenter()` 或 raw IPC。
-
-### 4. One Real Slice Per Phase
-
-每个 phase 都必须切一个真实 slice。
-
-不合格的 phase 例子:
-
-- 只建 route registry,不迁任何页面或 store
-- 只建 service,不替换任何主路径 owner
-- 只加 helper,不删任何旧桥接
-
-### 5. Legacy Freeze For Migrated Slices
-
-某个 slice 一旦进入迁移阶段,旧实现立即冻结。
-
-冻结含义:
-
-- 可以修阻塞性 bug
-- 可以做最小限度转发
-- 不允许继续长新主逻辑
-- 不允许继续扩张 API surface
-
-### 6. Net Reduction Over Net Addition
-
-阶段是否完成,要看 legacy 指标是否净下降。
-
-至少观察:
-
-- `renderer.usePresenter.count`
-- `renderer.windowElectron.count`
-- `renderer.windowApi.count`
-- hot path direct dependency 数量
-- `bridge.active.count`
-
-如果这些数字没有下降,通常说明只是多加了一层新实现。
-
-## Hard Rules
-
-### Red Lines
-
-以下行为在本轮实施期间视为红线:
-
-1. 在 renderer 新增 `useLegacyPresenter()`
-2. 在 renderer 新增 `window.electron.ipcRenderer.*`
-3. 在 migrated path 新增 raw channel 字符串
-4. 新 orchestration 反向依赖旧 presenter
-5. 同一条用户路径长期保持双 owner
-
-### Phase Hard Stops
-
-出现以下任一情况时,该 phase 不得标记为完成:
-
-1. 该 phase 没切真实 slice
-2. 新增 bridge 没写 `deleteByPhase`
-3. smoke 或自动化验证未通过
-4. legacy 指标没有下降或明显反弹
-5. 超期 bridge 仍存在
-
-## Bridge Register
-
-所有临时 bridge 都必须登记。每条 bridge 至少包含:
-
-| Field | Meaning |
-| --- | --- |
-| `id` | bridge 唯一标识 |
-| `owner` | 负责人 |
-| `legacyEntry` | 旧入口 |
-| `newTarget` | 新目标 |
-| `introducedIn` | 引入 PR / 提交 |
-| `deleteByPhase` | 最晚删除阶段 |
-| `notes` | 限制和风险 |
-
-默认规则:
-
-- bridge 最多存活 1 个 phase
-- bridge 只允许单向转发
-- bridge 不承载新增业务逻辑
-
-## PR Policy
-
-实施期间优先允许三类 PR:
-
-1. `guardrail PR`
-2. `boundary foundation + immediate slice PR`
-3. `slice cutover + legacy cleanup PR`
-
-每个 PR 应明确回答:
-
-- 这次切的是哪条真实路径?
-- 谁是新的 active owner?
-- 删掉了什么旧入口或旧桥接?
-
-如果答案全部是“没有”,通常不应合并。
-
-## Review Checklist
-
-每个迁移 PR 至少检查以下问题:
-
-1. 这条路径现在谁是唯一 active owner?
-2. renderer 是否仍直接感知 presenter 或 raw IPC?
-3. 新代码是否反向依赖旧 presenter?
-4. 有没有新增 bridge?是否写明 `deleteByPhase`?
-5. 这次是否真的降低了 hot path 耦合?
-6. cleanup / cancel / timeout 的 owner 是否清楚?
-7. 对应测试和 smoke 是否覆盖了切换点?
-
-## Migration Scoreboard
-
-每个 phase 结束时至少追踪:
-
-| Metric | Meaning |
-| --- | --- |
-| `renderer.usePresenter.count` | renderer 直接依赖 presenter 的数量 |
-| `renderer.windowElectron.count` | renderer 对旧 Electron bridge 的依赖 |
-| `renderer.windowApi.count` | renderer 对旧 preload 多入口的依赖 |
-| `hotpath.presenterEdge.count` | hot path presenter 直接依赖数量 |
-| `runtime.rawTimer.count` | raw timer 数量 |
-| `bridge.active.count` | 当前存活 bridge 数量 |
-| `bridge.expired.count` | 已超期未删 bridge 数量 |
-
-Scoreboard 目标不是绝对精确,而是持续证明:
-
-- 旧耦合在收缩
-- 双轨没有扩散
-- bridge 没有失控
-
-## Completion Rule
-
-本轮完成标准不是“新框架搭好了”,而是:
-
-- migrated path 的 owner 已切清楚
-- renderer 边界稳定了
-- hot path 耦合净下降
-- bridge register 对本轮范围归零
-
diff --git a/docs/architecture/main-kernel-refactor/plan.md b/docs/architecture/main-kernel-refactor/plan.md
deleted file mode 100644
index 977885a7e..000000000
--- a/docs/architecture/main-kernel-refactor/plan.md
+++ /dev/null
@@ -1,236 +0,0 @@
-# Main Kernel Refactor Plan
-
-## Planning Goal
-
-本计划将原来的“大而全内核重建”收敛为一条更实际的执行路线:
-
-- 先稳住 renderer-main 边界
-- 再切一个低风险 pilot slice
-- 再拆 chat/session/provider 热路径 owner
-- 最后清理迁移过程中产生的桥接和回流
-
-核心原则不是“目录先重排”,而是“每一步都减少真实耦合”。
-
-## Planning Assumptions
-
-- 当前运行时代码仍然是唯一生产实现。
-- 现有 `LifecycleManager` 保留,不单独开一个“重写生命周期系统”的 phase。
-- 第一轮默认不用 DI container;优先用显式 factory、setup function 和窄 port。
-- 只有当 session / window cleanup 真的需要时,才引入最小生命周期 helper。
-- 每个 phase 必须伴随真实 slice 切换,避免连续 foundation-only PR。
-
-## Current Hotspots
-
-当前优先级最高的热点:
-
-- `src/renderer/api/legacy/presenters.ts` 使 renderer 直接感知 presenter 名称与反射调用协议
-- `src/preload/index.ts` 同时承载多入口桥接和旧兼容 API
-- `src/main/presenter/index.ts` 同时承担 composition root、service locator、IPC dispatcher
-- `SessionPresenter` 仍直接回拿全局 `presenter`
-- `AgentSessionPresenter`、`AgentRuntimePresenter`、`LLMProviderPresenter` 之间仍存在主路径直连
-- `eventBus.ts` 同时承担 main 内部通知和 main -> renderer 广播
-
-## Target State For This Program
-
-本轮结束时,目标不是完整 clean architecture,而是让主链路至少具备以下形态:
-
-```text
-renderer component / store
- -> renderer/api/*Client
- -> window.deepchat
- -> shared/contracts/routes + events
- -> main handlers / orchestrators for migrated slices
- -> narrow ports / adapters around provider, session, permission, scheduler
-```
-
-允许保留:
-
-- `src/main/presenter/` 目录
-- 现有 `LifecycleManager`
-- 非 migrated path 的 legacy presenter
-
-但不允许:
-
-- 在 migrated path 上继续新增 `useLegacyPresenter()` / raw renderer IPC
-- 在新 hot path 上继续通过 presenter 互相找彼此
-- 继续把 cleanup / cancel / timeout 塞进匿名回调和散落 timer
-
-## Migration Rules
-
-### Allowed
-
-- 先定义 contract / client / typed event,再迁一条真实调用链
-- 保留旧 owner 作为过渡入口,但只允许单向转发到新实现
-- 为测试目的先引入 fake scheduler、fake provider adapter、fake route registry
-- 用现有 presenter 包住旧依赖,只要新逻辑不反向依赖它
-
-### Forbidden
-
-- 为了“未来可能会用”而提前大搬目录
-- 引入完整 DI container 再慢慢找 slice 迁移
-- 用新的 IPC 包装 main 内部互调
-- 在 hot path 上继续新增 presenter-to-presenter 直接依赖
-- 同一条用户路径长期保留新旧双 owner
-
-## Phase Map
-
-```text
-P0 Guardrails & Baseline
- -> P1 Typed Boundary Foundation
- -> P2 Settings Pilot Slice
- -> P3 Chat & Session Hot Path
- -> P4 Provider / Tool Boundary
- -> P5 Consolidation & Re-evaluation
-```
-
-## Phase Details
-
-### Phase 0: Guardrails & Baseline
-
-目标:
-
-- 冻结错误方向
-- 让后续阶段能证明“legacy 指标净下降”
-
-交付物:
-
-- 扩展 `architecture-guard`
-- 扩展 baseline 脚本
-- 追踪 legacy presenter helper(metric id 仍为 `renderer.usePresenter.count`)、`window.electron`、`window.api`、hot path direct dependency、raw timer、raw channel
-- 定义 bridge register 和轻量 scoreboard
-
-退出条件:
-
-- 能阻止新增 `useLegacyPresenter()`、新增 raw renderer IPC、新增 migrated path raw channel
-- 能重复生成基线
-- 能看出 hot path direct dependency 是否下降
-
-### Phase 1: Typed Boundary Foundation
-
-目标:
-
-- 让 renderer 调用 main 的方式先稳定下来
-
-交付物:
-
-- `shared/contracts/routes`
-- `shared/contracts/events`
-- preload bridge builder 或统一 bridge registration 机制
-- `renderer/api/*Client`
-- 第一批 typed route:settings、sessions/chat 必需入口、系统窗口入口
-
-退出条件:
-
-- 新增功能不再通过 `useLegacyPresenter()` 接入
-- migrated path 的 renderer 调用能从 route registry 追踪
-- typed event 能承接 settings / sessions / chat 的首批 UI 通知
-
-### Phase 2: Settings Pilot Slice
-
-目标:
-
-- 选择低风险、行为可观察的 slice 验证新边界是否真的好用
-
-交付物:
-
-- `SettingsClient`
-- settings contract / handler / adapter
-- settings store 调整为 client-first
-- typed `settings.changed` event
-
-退出条件:
-
-- settings 主读写链路不再依赖 `useLegacyPresenter()` 或 raw IPC
-- 设置持久化和变更通知具备独立测试
-- 对应 bridge 无超期残留
-
-### Phase 3: Chat & Session Hot Path
-
-目标:
-
-- 拆掉最关键的 owner 混杂点
-- 让发送消息、停止流、恢复会话的执行链更可解释
-
-交付物:
-
-- `ChatService` / orchestrator
-- `SessionService` 或等价 session orchestration 层
-- 最小必要 port:`SessionRepository`、`MessageRepository`、`ProviderExecutionPort`、
- `ProviderCatalogPort`、`SessionPermissionPort`、`WindowEventPort`、`Scheduler`
-- typed chat/session events
-- cancel / timeout / retry 通过 `Scheduler` 管理
-
-退出条件:
-
-- 发送消息、停止流、恢复会话的 migrated path owner 明确
-- `AgentSessionPresenter -> AgentRuntimePresenter` 不再是 migrated path 的主 owner 链
-- migrated path 的 cleanup / cancel 行为可测试
-
-### Phase 4: Provider / Tool Boundary
-
-目标:
-
-- 把 provider/tool 相关主协作从 presenter 互调中抽出来
-
-交付物:
-
-- `ProviderExecutionPort`
-- `ProviderCatalogPort`
-- `ProviderSessionPort` 或仅限 ACP/会话配置所需的 provider session adapter
-- `SessionPermissionPort`
-- tool / permission 的明确响应 contract
-
-退出条件:
-
-- migrated path 上 `SessionPresenter` / `AgentSessionPresenter` / `AgentRuntimePresenter`
- 不再直接把 provider 当作全局协作者乱用
-- provider query / execution / permission 边界可单测
-- 相关 renderer 交互走 typed route / typed event
-
-### Phase 5: Consolidation & Re-evaluation
-
-目标:
-
-- 清理本轮新增 bridge
-- 更新基线和文档
-- 判断是否还需要下一轮更彻底的 kernel 重构
-
-交付物:
-
-- 删除 migrated path 临时桥接
-- 收敛 docs、baseline、scoreboard、smoke 记录
-- 形成“是否继续做全量 kernel 重构”的结论
-
-退出条件:
-
-- 本轮涉及 slice 的 bridge register 归零
-- migrated path 的 legacy 指标较起点净下降
-- 本轮目标路径具备稳定测试和 smoke 证据
-
-## Cross-Cutting Streams
-
-所有阶段都同步处理:
-
-- guardrail 更新
-- baseline 更新
-- 文档同步
-- bridge register / scoreboard 维护
-- 针对 migrated path 的测试补齐
-- 对旧 owner 的冻结和清理
-
-## Recommended PR Sequence
-
-1. `chore(architecture): tighten guards and baselines for boundary stabilization`
-2. `refactor(ipc): add typed route registry and renderer clients`
-3. `refactor(settings): migrate settings to typed boundary`
-4. `refactor(chat): introduce chat/session orchestration and scheduler`
-5. `refactor(providers): narrow provider and permission boundaries`
-6. `refactor(architecture): remove temporary bridges and refresh docs`
-
-## Risk Notes
-
-- 最大风险仍然是“多搭一层新框架,但旧 owner 一点没退”。
-- 另一类风险是把 phase 2 之前的基础工作拖太久,导致迟迟不切真实 slice。
-- `Chat / Session / Provider` 三块最耦合,不应并行乱切;建议按 owner 顺序推进。
-- 如果某个阶段不得不引入桥接,必须在下一阶段优先删除,而不是留到“最后统一收尾”。
-
diff --git a/docs/architecture/main-kernel-refactor/ports-and-scheduler.md b/docs/architecture/main-kernel-refactor/ports-and-scheduler.md
deleted file mode 100644
index b0c857700..000000000
--- a/docs/architecture/main-kernel-refactor/ports-and-scheduler.md
+++ /dev/null
@@ -1,221 +0,0 @@
-# Main Kernel Refactor Ports and Scheduler
-
-## Purpose
-
-本文件回答三个问题:
-
-1. 本轮到底需要哪些 port
-2. 哪些能力现在不要急着抽象
-3. `Scheduler` 为什么是本轮少数值得提前引入的接口
-
-本轮原则不是“把一切都 port 化”,而是只抽出能直接减少 hot path 耦合的最小集合。
-
-## Decision Rule
-
-只有同时满足以下条件时,能力才进入 port:
-
-1. 它是 orchestration 层依赖的外部能力或跨 slice 能力
-2. 现有直接依赖已经造成 owner 不清、测试困难或 presenter 互调
-3. 抽出来后能立刻替换一条真实主路径
-
-如果只是为了“以后可能更优雅”,本轮不要抽。
-
-## Minimum Port Set
-
-### 1. SessionRepository
-
-责任:
-
-- session 元数据读取、创建、恢复、状态更新
-
-为什么现在就需要:
-
-- chat/session orchestration 不能继续直接掺着 presenter 和 sqlite 管理 session owner
-
-### 2. MessageRepository
-
-责任:
-
-- message append、查询、状态更新、重试相关读取
-
-为什么现在就需要:
-
-- send / stop / retry 这些路径要有可替换的数据面,方便做单测和集成测试
-
-### 3. ProviderCatalogPort
-
-责任:
-
-- provider、model、custom model 查询
-
-为什么现在就需要:
-
-- 这是 `ConfigQueryPort` 中最容易外溢的一部分,也是 session/chat 在选 provider、校验 model 时的明确依赖
-
-### 4. ProviderExecutionPort
-
-责任:
-
-- completion、stream、provider 级执行入口
-
-为什么现在就需要:
-
-- migrated chat path 不应该继续直接知道 `LLMProviderPresenter` 的完整接口面
-
-### 5. ProviderSessionPort
-
-责任:
-
-- 仅限会话级 provider runtime 配置,如 ACP session / workdir / mode
-
-注意:
-
-- 只有当 Phase 4 真切到这些路径时才落地
-- 不要求在本轮一开始就完整抽出
-
-### 6. SessionPermissionPort
-
-责任:
-
-- 权限请求响应、权限批准、权限清理
-
-为什么现在就需要:
-
-- `SessionRuntimePort` 当前把 UI 刷新、权限清理、权限批准混在一起,边界过胖
-
-### 7. WindowEventPort
-
-责任:
-
-- 向 renderer 发布 typed UI event
-
-为什么现在就需要:
-
-- migrated path 不能继续依赖 `eventBus.sendToRenderer()` 这种混合语义
-
-### 8. Scheduler
-
-责任:
-
-- `sleep`
-- `timeout`
-- `retry`
-
-为什么现在就需要:
-
-- send / stop / provider failure 这些路径的可测性和 cleanup 可靠性都强依赖统一时序语义
-
-## What We Intentionally Do Not Port Yet
-
-以下能力本轮不急着抽:
-
-- browser / yoBrowser 全量能力
-- workspace 全量能力
-- exporter
-- remote control
-- dialog/file picker 的全量系统能力
-- 完整事件总线抽象
-
-这些能力不是不重要,而是当前不是 hot path 的最大耦合来源。
-
-## Legacy To Port Mapping
-
-### `ConfigQueryPort`
-
-本轮只拆最必要的部分:
-
-| Legacy capability | New target |
-| --- | --- |
-| `getProviderModels` | `ProviderCatalogPort` |
-| `getCustomModels` | `ProviderCatalogPort` |
-| `getAgentType` | 先保留在现有 adapter 或单独 `AgentCatalogPort`,仅在 chat/session 需要时再抽 |
-
-### `SessionRuntimePort`
-
-本轮拆成:
-
-| Legacy capability | New target |
-| --- | --- |
-| `refreshSessionUi` | `WindowEventPort` |
-| `clearSessionPermissions` | `SessionPermissionPort` |
-| `approvePermission` | `SessionPermissionPort` |
-
-### Direct `LLMProviderPresenter` Usage
-
-本轮拆法:
-
-| Current usage | New target |
-| --- | --- |
-| model / custom model query | `ProviderCatalogPort` |
-| completion / stream / rate-limit-wrapped execution | `ProviderExecutionPort` |
-| ACP session / workdir / mode | `ProviderSessionPort` |
-| permission-related collaboration | `SessionPermissionPort` |
-
-## Scheduler
-
-### Suggested Interface
-
-```ts
-export interface Scheduler {
- sleep(input: SleepInput): Promise
- timeout(input: TimeoutInput): Promise
- retry(input: RetryInput): Promise
-}
-
-export interface SleepInput {
- ms: number
- reason: string
- signal?: AbortSignal
-}
-
-export interface TimeoutInput {
- task: Promise
- ms: number
- reason: string
- signal?: AbortSignal
-}
-
-export interface RetryInput {
- task: () => Promise
- maxAttempts: number
- initialDelayMs: number
- backoff: number
- reason: string
- signal?: AbortSignal
-}
-```
-
-### Why It Must Exist
-
-当前仓库里的 raw timer 混杂了:
-
-- 业务等待
-- 超时保护
-- 重试退避
-- 清理延迟
-- watcher / polling
-
-如果继续散落使用,会带来三个直接问题:
-
-1. 无法稳定测试
-2. 无法统一取消
-3. cleanup owner 不清楚
-
-因此,本轮虽然不追求全量新架构,但 `Scheduler` 仍值得提前抽出来。
-
-### Implementation Policy
-
-- 接口自写
-- `retry` 内部可用 `p-retry`
-- 业务层不直接依赖任何第三方重试库
-
-## Port Design Guardrails
-
-实施时必须检查:
-
-1. 这个 port 是否直接替换了一个真实 hard dependency?
-2. 它是否缩小了 orchestration 层需要知道的实现细节?
-3. 它是否有明确的 fake / stub 测试价值?
-4. 它是否按能力分组,而不是按旧 presenter 名称分组?
-
-如果答案不明确,本轮就不要抽。
diff --git a/docs/architecture/main-kernel-refactor/route-schema-catalog.md b/docs/architecture/main-kernel-refactor/route-schema-catalog.md
deleted file mode 100644
index 3ec5aa6ce..000000000
--- a/docs/architecture/main-kernel-refactor/route-schema-catalog.md
+++ /dev/null
@@ -1,208 +0,0 @@
-# Main Kernel Refactor Route Schema Catalog
-
-## Purpose
-
-本文件锁定本轮真正需要的两类契约:
-
-1. renderer 发起的 typed route
-2. main -> renderer 的 typed event
-
-目标不是一次性冻结所有未来 route,而是先把 migrated path 的边界稳定下来。
-
-## Design Rules
-
-### 1. Route Only Solves Renderer-Main Boundary
-
-route registry 只收 renderer 发起、必须跨 renderer-main 边界的能力。
-
-main 内部协作不进 route registry。
-
-### 2. Capability Naming
-
-route 名称按用户意图命名,不按内部类命名:
-
-- `settings.getSnapshot`
-- `settings.update`
-- `sessions.create`
-- `chat.sendMessage`
-- `chat.stopStream`
-
-不要出现:
-
-- `configPresenter.get`
-- `llmProviderPresenter.generate`
-- `sessionRepository.insert`
-
-### 3. Coarse-Grained Surface
-
-renderer 调用 use case,不调用内部编排步骤。
-
-合理:
-
-- `chat.sendMessage`
-- `chat.stopStream`
-- `providers.listModels`
-
-不合理:
-
-- `messages.insertUserMessage`
-- `provider.executeWithRateLimit`
-- `sessionRuntime.refreshUi`
-
-### 4. Registry Is The Source Of Truth
-
-同一份 registry 负责:
-
-- route 名称
-- input/output schema
-- preload bridge 类型来源
-- renderer client 类型来源
-
-### 5. Typed Events For Migrated Paths
-
-只要 migrated path 需要 main -> renderer 通知,就必须定义 typed event。
-
-不再允许为 migrated path 新增随意字符串事件。
-
-## Target Layout
-
-```text
-src/shared/contracts/
- routes/
- settings.routes.ts
- sessions.routes.ts
- chat.routes.ts
- providers.routes.ts
- system.routes.ts
- events/
- settings.events.ts
- sessions.events.ts
- chat.events.ts
- routes.ts
- events.ts
-```
-
-本轮不要求一开始就有更多分类文件。
-
-## Common Schemas
-
-第一批建议统一这些基础 schema:
-
-```ts
-import { z } from 'zod'
-
-export const EntityIdSchema = z.string().min(1)
-export const TimestampMsSchema = z.number().int().nonnegative()
-
-export const JsonValueSchema: z.ZodType = z.lazy(() =>
- z.union([
- z.string(),
- z.number(),
- z.boolean(),
- z.null(),
- z.array(JsonValueSchema),
- z.record(JsonValueSchema)
- ])
-)
-
-export const AppErrorSchema = z.object({
- code: z.string(),
- message: z.string(),
- retriable: z.boolean().default(false),
- details: z.record(JsonValueSchema).optional()
-})
-```
-
-## Initial Route Catalog
-
-### Phase 1 and Phase 2
-
-| Route | Input summary | Output summary | Notes |
-| --- | --- | --- | --- |
-| `settings.getSnapshot` | optional key filter | settings snapshot | settings pilot 主入口 |
-| `settings.listSystemFonts` | none | normalized system font list | settings pilot 的字体选择辅助入口 |
-| `settings.update` | array of key/value changes | version + changed keys | 取代散落设置调用 |
-| `system.openSettings` | optional section | `{ windowId }` | 取代直接窗口调用 |
-
-### Phase 3
-
-| Route | Input summary | Output summary | Notes |
-| --- | --- | --- | --- |
-| `sessions.create` | title / provider / model / agent / projectDir | session summary | 会话创建主入口 |
-| `sessions.restore` | `{ sessionId, limit? }` | `{ session, messages, nextCursor, hasMore }` | 恢复最新一页消息 |
-| `sessions.listMessagesPage` | `{ sessionId, cursor?, limit? }` | `{ messages, nextCursor, hasMore }` | 向更老消息翻页 |
-| `sessions.list` | optional filter | session summaries | 列表主入口 |
-| `sessions.activate` | `{ sessionId }` | `{ activated: true }` | 当前窗口激活会话 |
-| `sessions.deactivate` | none | `{ deactivated: true }` | 当前窗口关闭活跃会话 |
-| `sessions.getActive` | none | `{ session }` | 当前窗口读取活跃会话 |
-| `chat.sendMessage` | `sessionId`, content, attachments | `{ accepted, requestId, messageId }` | 聊天发送主入口 |
-| `chat.stopStream` | `sessionId` or `requestId` | `{ stopped: boolean }` | 停止流语义固定 |
-
-### Phase 4
-
-| Route | Input summary | Output summary | Notes |
-| --- | --- | --- | --- |
-| `providers.listModels` | `{ providerId }` | provider + custom model summaries | provider 查询边界 |
-| `providers.testConnection` | `{ providerId, modelId? }` | `{ isOk, errorMsg }` | 当前配置页验证入口 |
-| `chat.respondToolInteraction` | session / message / tool / typed response | `{ accepted, resumed?, waitingForUserMessage? }` | 统一权限/问题响应入口 |
-
-## Initial Typed Event Catalog
-
-本轮最先冻结以下事件:
-
-| Event | Payload summary | Why |
-| --- | --- | --- |
-| `settings.changed` | changed keys + version | settings store 刷新 |
-| `sessions.updated` | affected session ids + reason | 会话列表与当前会话刷新 |
-| `chat.stream.updated` | requestId/sessionId + delta/tool/update | 流式主路径 |
-| `chat.stream.completed` | requestId/sessionId/messageId | 结束信号 |
-| `chat.stream.failed` | requestId/sessionId/error | 错误信号 |
-
-### Example Stream Event Schema
-
-```ts
-export const ChatStreamEventSchema = z.discriminatedUnion('kind', [
- z.object({
- kind: z.literal('delta'),
- requestId: EntityIdSchema,
- sessionId: EntityIdSchema,
- messageId: EntityIdSchema,
- textDelta: z.string()
- }),
- z.object({
- kind: z.literal('tool-call'),
- requestId: EntityIdSchema,
- sessionId: EntityIdSchema,
- toolCallId: EntityIdSchema,
- toolName: z.string(),
- status: z.enum(['start', 'running', 'end', 'error'])
- }),
- z.object({
- kind: z.literal('permission-required'),
- requestId: EntityIdSchema,
- sessionId: EntityIdSchema,
- permissionRequestId: EntityIdSchema,
- permissionType: z.enum(['read', 'write', 'all', 'command'])
- })
-])
-```
-
-## What We Intentionally Do Not Freeze Yet
-
-为了避免过度设计,以下内容本轮不提前固定:
-
-- main 内部 provider orchestration protocol
-- repository-level CRUD route
-- file / browser / workspace 的全量系统 route
-- plugin 相关 route
-- 低频页面的边角能力
-
-## Implementation Guardrails
-
-新增 route 或 event 时必须回答:
-
-1. 这是否真的是 renderer-main 边界?
-2. 名称是否表达用户意图,而不是内部实现细节?
-3. 是否已经有更粗粒度的 route 可承接?
-4. 是否能通过 schema 清楚描述?
-5. 是否会替代一条现有 raw IPC 或 presenter 直调?
diff --git a/docs/architecture/main-kernel-refactor/spec.md b/docs/architecture/main-kernel-refactor/spec.md
deleted file mode 100644
index 511974ef8..000000000
--- a/docs/architecture/main-kernel-refactor/spec.md
+++ /dev/null
@@ -1,216 +0,0 @@
-# Main Kernel Refactor
-
-## Summary
-
-本轮重构在原计划基础上收敛为一套“稳定性优先”的方案。
-
-目标不是一次性把 `src/main` 改造成完整的 `main/app + domain + infra` 新世界,也不是在本轮强行把
-`src/main/presenter/` 清零;目标是先解决当前最影响稳定性、可维护性和可测试性的几类问题:
-
-- renderer 通过 `useLegacyPresenter()`、`window.electron`、`window.api` 直接知道 main 内部实现
-- main hot path 通过 presenter-to-presenter 直接协作,owner 不清楚
-- session / window 生命周期和 stream / timer cleanup 缺少明确 owner
-- 新代码继续回流到旧边界,导致耦合只增不减
-
-这是一轮“边界稳定化 + 热路径减耦”工程,而不是全量目录重建工程。
-
-## Program Reset
-
-原方案中保留的高收益部分:
-
-- typed renderer-main boundary
-- hot path presenter coupling 拆解
-- lifecycle owner 明确化
-- scheduler / cancel / timeout / retry 收口
-- 分阶段迁移与净减 legacy 指标
-
-本轮主动砍掉的目标:
-
-- 不要求一次性交付完整 clean architecture 目录骨架
-- 不要求本轮删除整个 `src/main/presenter/`
-- 不要求先引入完整 DI container 或自写大 Scope 系统
-- 不要求先重写整个 EventBus 体系
-- 不把“内存下降”作为主成功指标
-
-## Baseline
-
-以下基线来自 `2026-04-19` 的最新扫描,用于说明问题集中区域:
-
-| Signal | Baseline | Why it matters |
-| --- | --- | --- |
-| `src/main/presenter/index.ts` 行数 | 769 | 组合根过重,依赖装配和运行时 owner 混在一起 |
-| main dependency cycles | 30 | presenter 互相引用和全局入口回流仍明显 |
-| renderer `useLegacyPresenter(` 命中 | 90 | renderer 仍依赖 presenter naming |
-| renderer `window.electron*` 命中 | 111 | renderer 仍深度感知 Electron / IPC 实现 |
-| renderer `window.api` 命中 | 34 | preload 仍是多入口兼容面 |
-| `setTimeout` / `setInterval` 命中 | 123 | cleanup、超时和轮询语义散落 |
-
-这些数字说明,真正的问题是:
-
-- 边界不稳定
-- owner 不清楚
-- 热路径依赖过粗
-- 可测性差
-
-而不是“目录名字不够先进”。
-
-## Detailed Design Docs
-
-本轮仍保留以下设计文档,但都按“最小必要抽象”执行:
-
-- [ports-and-scheduler.md](./ports-and-scheduler.md)
-- [route-schema-catalog.md](./route-schema-catalog.md)
-- [eventbus-migration.md](./eventbus-migration.md)
-
-## Goals
-
-- 让 renderer-main 主边界收敛到 typed route registry、typed event 和 `renderer/api` client。
-- 阻止新增 `useLegacyPresenter()`、新增 raw renderer IPC、新增旧桥接依赖。
-- 拆掉 chat / session / provider hot path 上最深的 presenter-to-presenter 直接耦合。
-- 让 session / window / stream cleanup 的 owner 明确且可测试。
-- 让关键流程可以通过 fake port、stub provider、fake scheduler 做稳定测试。
-- 每个阶段结束时,legacy 指标净下降,而不是只新增第二套实现。
-
-## Non-Goals
-
-- 不交付完整 clean architecture 目录重排。
-- 不替换现有 `LifecycleManager`。
-- 不要求删除所有 presenter 或所有 singleton。
-- 不要求一次性清理所有历史事件常量和所有旧桥接。
-- 不把内存或启动速度优化作为本轮核心目标。
-
-## User Stories
-
-- 作为维护者,我可以看清 renderer 究竟通过哪些能力边界调用 main,而不是继续猜测 presenter 方法名。
-- 作为功能开发者,我可以在 hot path 上新增逻辑,而不用沿着全局 `Presenter` 和隐式事件链继续扩散耦合。
-- 作为测试编写者,我可以对发送消息、停止流、provider 查询、设置读写分别做独立测试。
-- 作为 QA,我可以验证迁移后的主路径行为没有回退,而不是只验证“代码还能跑”。
-
-## In Scope
-
-- `renderer/api` client、typed route registry、typed preload bridge
-- `settings` 作为第一个 pilot slice
-- `chat/session/provider` 热路径 owner 收口
-- `Scheduler` 接口与 cancel / timeout / retry 语义统一
-- session / window 相关 lifecycle cleanup 明确化
-- 迁移治理、guardrail、baseline、smoke 和测试方案
-
-## Out of Scope
-
-- 全量 presenter 退役
-- 全量 EventBus 重写
-- 全量 repository / domain model 重命名或搬目录
-- 新增插件系统
-- 以性能优化名义大规模重写运行时
-
-## Constraints
-
-- 现有用户可见行为优先保持兼容,尤其是会话创建、恢复、发送消息、流式回复、停止流、provider 切换和权限交互。
-- 新抽象必须替代真实耦合点,不能只增加一层新名字。
-- renderer-main 边界和 main 内部协作边界必须分开设计,不能混为一谈。
-- 每个阶段都必须切一个真实路径,不接受连续多轮纯基础设施 PR。
-
-## Architectural Principles
-
-### 1. Boundary First
-
-先稳住 renderer-main 边界,再谈内部结构继续细化。
-
-如果 renderer 仍直接依赖 presenter 名称,main 内部怎么拆都很难真正稳定。
-
-### 2. Hot Path First
-
-优先处理最深的主链路耦合:
-
-- `useLegacyPresenter()` / raw IPC
-- `AgentSessionPresenter -> AgentRuntimePresenter`
-- `SessionPresenter` / `AgentSessionPresenter` / `AgentRuntimePresenter` 对 provider 运行时的直接索取
-
-不先从低频边角模块开始。
-
-### 3. Explicit Owner
-
-每条主路径都必须能回答:
-
-- 谁创建资源
-- 谁发起执行
-- 谁负责取消
-- 谁负责 cleanup
-
-如果回答仍然是“某个 presenter 顺手处理一下”,说明边界还不够清楚。
-
-### 4. Minimal New Infrastructure
-
-不为“看起来更像架构”而先造完整 container、完整 event framework、完整 domain taxonomy。
-
-只有当抽象能直接替换一个现有 hard dependency,或者显著提升测试稳定性时,才进入本轮。
-
-### 5. Keep Working Code, Freeze Bad Growth
-
-现有生命周期、presenter 和兼容层允许继续存在,但迁移覆盖到的 slice 上:
-
-- 旧 owner 冻结
-- 只允许 `old -> new` 单向转发
-- 不允许继续长新逻辑
-
-## Target Snapshot
-
-本轮目标不是完整新目录,而是把活跃热路径调整成以下形态:
-
-```text
-renderer
- -> renderer/api/*Client
- -> window.deepchat
- -> shared/contracts/routes + shared/contracts/events
- -> migrated main handlers / services / adapters
- -> existing presenters or managers only behind explicit ports on non-migrated paths
-```
-
-main 侧允许在过渡期保持混合结构,但必须满足:
-
-- migrated path 不再由 renderer 直呼 presenter 名称
-- migrated path 不再依赖 presenter-to-presenter 直接 owner 链
-- lifecycle cleanup 和 scheduler 语义可单独测试
-
-## Success Metrics
-
-本轮重点看以下指标是否下降:
-
-- `renderer.usePresenter.count`
-- `renderer.windowElectron.count`
-- `renderer.windowApi.count`
-- hot path 上的 presenter direct dependency 数量
-- migrated path 的 raw channel / raw timer 数量
-- 为 migrated path 建立的 contract / unit / integration 测试数量
-
-## Memory Position
-
-内存不是本轮主成功指标。
-
-这轮工作只有在以下情况才可能带来可观的内存收益:
-
-- session / stream / permission 状态在结束时能被更可靠地释放
-- window / webContents listener 能按 owner 清理
-- 不再长期保留无主的 abort controller、timer、subscription
-
-如果只是把 presenter 换成 service 或 kernel 命名,而对象生命周期并未变化,则不应期待明显内存下降。
-
-## Acceptance Direction
-
-最终验收以以下三类结果为核心:
-
-- renderer 边界更稳定
-- hot path 更容易解释和测试
-- cleanup / cancel / timeout 行为更可靠
-
-不以“新目录是否看起来更纯粹”作为主要标准。
-
-## Open Questions
-
-本轮不阻塞实施的决策如下:
-
-- 现有 `LifecycleManager` 保留,除非后续 hot path 证明它成为阻塞点。
-- `Presenter` 可作为过渡期 composition shell 保留,但不再继续吞新主链路。
-- `EventBus` 先冻结新增错误用法,优先给 migrated path 建 typed UI event;是否全量重写,等本轮收敛后再评估。
-- 本轮结束后再决定是否还有必要推进更彻底的 `main kernel` 目录重构。
-
diff --git a/docs/architecture/main-kernel-refactor/tasks.md b/docs/architecture/main-kernel-refactor/tasks.md
deleted file mode 100644
index dc2ba0d0f..000000000
--- a/docs/architecture/main-kernel-refactor/tasks.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# Main Kernel Refactor Tasks
-
-## Program Reset
-
-- [x] 保留 `docs/architecture/main-kernel-refactor/` 目录作为本轮主规格入口
-- [x] 将原“大而全 kernel 重构”收敛为“边界稳定 + 热路径减耦 + 可测试性提升”方案
-- [x] 重写 `spec.md`
-- [x] 重写 `plan.md`
-- [x] 重写 `acceptance.md`
-- [x] 重写 `tasks.md`
-- [x] 重写 `test-plan.md`
-- [x] 重写 `migration-governance.md`
-- [x] 重写 `build-vs-buy.md`
-- [x] 重写 `ports-and-scheduler.md`
-- [x] 重写 `route-schema-catalog.md`
-- [x] 重写 `eventbus-migration.md`
-- [x] 更新 `docs/README.md` 中对本计划的描述
-
-## Phase 0: Guardrails & Baseline
-
-- [x] 扩展 `scripts/architecture-guard.mjs`,阻止新增 `useLegacyPresenter()` 调用点
-- [x] 扩展 `scripts/architecture-guard.mjs`,阻止新增 `window.electron.ipcRenderer.*` 监听
-- [x] 扩展 `scripts/architecture-guard.mjs`,阻止在 migrated path 中新增 raw channel 字符串
-- [x] 为 hot path direct dependency 增加趋势检查
-- [x] 扩展 baseline 脚本,输出 legacy presenter helper(metric id 保持 `renderer.usePresenter.count`)/ `window.electron` / `window.api` / raw timer / bridge 数量
-- [x] 建立 bridge register
-- [x] 建立轻量 migration scoreboard
-
-## Phase 1: Typed Boundary Foundation
-
-- [x] 新建或收敛 `src/shared/contracts/` 目录
-- [x] 定义 route registry,覆盖 settings 与 chat/session 首批入口
-- [x] 定义 typed event catalog,覆盖 `settings.changed`、`sessions.updated`、`chat.stream.*`
-- [x] 新建 `src/preload/createBridge.ts` 或统一 bridge builder
-- [x] 新建 `src/renderer/api/SettingsClient.ts`
-- [x] 新建 `src/renderer/api/SessionClient.ts`
-- [x] 新建 `src/renderer/api/ChatClient.ts`
-- [x] 约束后续新增 renderer-main 能力必须先落 route registry
-
-## Phase 2: Settings Pilot Slice
-
-- [x] 设计 settings contract / handler / adapter
-- [x] 将 settings renderer/store 迁移到 `SettingsClient`
-- [x] 清理 settings 主路径上的 `useLegacyPresenter()` / raw IPC 依赖
-- [x] 补齐 settings 单测、集成测试和 smoke
-- [x] 删除 settings 迁移过程中的临时桥接
-
-## Phase 3: Chat & Session Hot Path
-
-- [x] 设计 `ChatService` 或等价 orchestration 层
-- [x] 设计 `SessionService` 或等价 session orchestration 层
-- [x] 抽出最小必要 port:`SessionRepository`、`MessageRepository`、`ProviderExecutionPort`
-- [x] 抽出最小必要 port:`ProviderCatalogPort`、`SessionPermissionPort`、`WindowEventPort`
-- [x] 引入 `Scheduler` 接口并承接 cancel / timeout / retry
-- [x] 迁移发送消息、停止流、恢复会话主链路
-- [x] 清理 `AgentSessionPresenter -> AgentRuntimePresenter` 在 migrated path 上的主 owner 角色
-- [x] 补齐 chat/session 主路径测试
-
-## Phase 4: Provider / Tool Boundary
-
-- [x] 明确 provider query / execution / session 配置的边界
-- [x] 将 migrated path 上 presenter 对 provider 的直接调用改为 port / adapter
-- [x] 收口 permission response、tool response 的 contract
-- [x] 如确有需要,再补 `ProviderSessionPort`
-- [x] 为 provider / permission / tool 关键交互补测试
-- [x] 删除本阶段引入的临时兼容桥
-
-## Phase 5: Consolidation & Re-evaluation
-
-- [x] 删除本轮仍存活的 bridge
-- [x] 重跑 baseline 并更新 scoreboard
-- [x] 更新 active docs 和实现说明
-- [x] 记录最终 smoke 结果
-- [x] 形成“是否继续推进更彻底 kernel 重构”的结论
-
-## Phase Exit Discipline
-
-- [x] 每个阶段完成时更新本文件状态
-- [x] 每个阶段完成时更新 `acceptance.md`
-- [x] 每个阶段完成时更新 `test-plan.md`
-- [x] 每个阶段完成时更新 `docs/architecture/baselines/*`
-- [x] 每个阶段完成时更新 bridge register 与 scoreboard
-- [x] 每个阶段完成时确认 legacy 指标净下降
-
diff --git a/docs/architecture/main-kernel-refactor/test-plan.md b/docs/architecture/main-kernel-refactor/test-plan.md
deleted file mode 100644
index 4ca862f46..000000000
--- a/docs/architecture/main-kernel-refactor/test-plan.md
+++ /dev/null
@@ -1,264 +0,0 @@
-# Main Kernel Refactor Test Plan
-
-## Test Goal
-
-本测试方案服务于“边界稳定化 + 热路径减耦”。
-
-测试目标不是一轮内把整个仓库历史问题清零,而是确保:
-
-- migrated path 的新边界可独立验证
-- owner 切换点有明确测试
-- cleanup / cancel / timeout 行为可证明
-- 旧路径在被替换时不会静默回退
-
-## Test Layers
-
-### 1. Static Guard Tests
-
-作用:
-
-- 阻止错误依赖方向继续进入仓库
-- 让结构回退尽早失败
-
-覆盖内容:
-
-- `architecture-guard`
-- baseline 生成脚本
-- grep 型 guard:`useLegacyPresenter`、`window.electron`、`window.api`
-- migrated path 的 raw channel / raw timer 检查
-- hot path direct dependency 趋势检查
-- bridge register / scoreboard 一致性检查
-
-### 2. Contract Tests
-
-作用:
-
-- 验证 route registry、schema、typed event、preload bridge、renderer client 的一致性
-
-建议对象:
-
-- `shared/contracts/routes`
-- `shared/contracts/events`
-- `createBridge`
-- `renderer/api/*Client`
-
-### 3. Orchestration Unit Tests
-
-作用:
-
-- 验证新引入的 orchestration 层与 port 协作逻辑
-
-建议对象:
-
-- `SettingsService`
-- `SessionService`
-- `ChatService`
-- `Scheduler`
-- provider / permission adapter
-
-### 4. Main Integration Tests
-
-作用:
-
-- 验证 `route -> handler -> orchestration -> adapter` 这一整段主链路
-
-建议对象:
-
-- settings 读写
-- session create / restore
-- chat send / stop
-- provider query / permission response
-
-### 5. Renderer Integration Tests
-
-作用:
-
-- 验证 store / composable 在切换到 `renderer/api` 后状态仍保持一致
-
-建议对象:
-
-- settings store
-- session store
-- message / stream store
-- provider store
-
-### 6. Electron Smoke Tests
-
-作用:
-
-- 验证最终用户可见的核心行为未回退
-
-建议路径:
-
-- 启动应用
-- 修改设置
-- 创建会话
-- 发送消息
-- 观察 stream
-- 停止流
-- 恢复会话
-- 切换 provider 或完成一次 provider 相关交互
-
-## Required Harnesses
-
-随着阶段推进,逐步补齐:
-
-- fake `Scheduler`
-- route registry fixture
-- typed event fixture
-- fake provider adapter
-- fake permission adapter
-- preload bridge test double
-
-## Phase-by-Phase Test Matrix
-
-| Phase | Automated Coverage | Manual Smoke |
-| --- | --- | --- |
-| P0 | guard script、baseline script | 确认脚本输出可复现 |
-| P1 | route registry tests、typed event tests、bridge/client tests | 通过新 client 调 settings / session / chat 首批 route |
-| P2 | settings contract/integration/store tests | 修改设置并重启确认持久化 |
-| P3 | chat/session orchestration tests、scheduler tests | 创建会话、发消息、停止流、恢复会话 |
-| P4 | provider/permission/tool boundary tests | 完成 provider 相关交互与权限响应 |
-| P5 | full regression pack for migrated paths | 冷启动、重启恢复、一轮完整主路径 smoke |
-
-## Current Phase Evidence
-
-- Phase 0 completed on `2026-04-19`
-- Automated verification for Phase 0 uses `node scripts/architecture-guard.mjs`
-- Automated verification for Phase 0 uses `pnpm run architecture:baseline`
-- Automated verification for Phase 0 uses `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, `pnpm run typecheck`
-- Repro smoke for Phase 0 is rerunning guard and baseline scripts and confirming committed reports regenerate without drift
-- Phase 0 artifacts are `docs/architecture/baselines/main-kernel-boundary-baseline.md`
-- Phase 0 artifacts are `docs/architecture/baselines/main-kernel-bridge-register.md`
-- Phase 0 artifacts are `docs/architecture/baselines/main-kernel-migration-scoreboard.md`
-- Phase 1 completed on `2026-04-19`
-- Automated verification for Phase 1 uses `pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts`
-- Automated verification for Phase 1 uses `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/api/createBridge.test.ts test/renderer/api/clients.test.ts`
-- Automated verification for Phase 1 uses `pnpm run typecheck`
-- Automated verification for Phase 1 uses `pnpm run format`, `pnpm run i18n`, `pnpm run lint`
-- Repro smoke for Phase 1 is invoking `window.deepchat` routes through the new bridge and confirming typed event subscriptions receive `settings.changed`, `sessions.updated`, and `chat.stream.*`
-- Phase 1 artifacts are `src/shared/contracts/`, `src/preload/createBridge.ts`, `src/main/routes/`, and `src/renderer/api/`
-- Phase 2 implementation and automated verification completed on `2026-04-19`
-- Automated verification for Phase 2 uses `pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts test/main/routes/settingsHandler.test.ts`
-- Automated verification for Phase 2 uses `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/api/clients.test.ts test/renderer/stores/uiSettingsStore.test.ts`
-- Automated verification for Phase 2 uses `pnpm run typecheck`
-- Automated verification for Phase 2 uses `pnpm run format`, `pnpm run i18n`, `pnpm run lint`
-- Automated verification for Phase 2 uses `pnpm run architecture:baseline`
-- Repro smoke for Phase 2 is modifying font size, notifications, and font family through the settings UI, then restarting the app to confirm persistence and cross-window refresh
-- Phase 2 artifacts are `src/main/routes/settings/`, `src/main/routes/index.ts`, `src/renderer/api/SettingsClient.ts`, and `src/renderer/src/stores/uiSettingsStore.ts`
-- Phase 3 implementation and automated verification completed on `2026-04-19`
-- Automated verification for Phase 3 uses `pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts test/main/routes/sessionService.test.ts test/main/routes/chatService.test.ts test/main/routes/scheduler.test.ts`
-- Automated verification for Phase 3 uses `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/api/clients.test.ts test/renderer/stores/pageRouter.test.ts test/renderer/stores/messageStore.test.ts test/renderer/stores/sessionStore.test.ts`
-- Automated verification for Phase 3 follow-up uses `pnpm exec vitest --config vitest.config.ts test/main/presenter/agentRuntimePresenter/echo.test.ts`
-- Automated verification for Phase 3 follow-up uses `pnpm exec vitest --config vitest.config.ts test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts`
-- Automated verification for Phase 3 follow-up uses `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/components/ChatPage.test.ts test/renderer/stores/messageStore.test.ts test/renderer/stores/sessionStore.test.ts`
-- Automated verification for Phase 3 uses `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, `pnpm run typecheck`
-- Automated verification for Phase 3 uses `node scripts/architecture-guard.mjs`
-- Automated verification for Phase 3 uses `pnpm run architecture:baseline`
-- Repro smoke for Phase 3 is: cold start the app, create a regular session, send one message, confirm incremental stream updates arrive before completion, send a second message and confirm user/assistant ordering stays correct, stop the stream before completion, reopen the session, and confirm the active session is restored after switching away and back
-- Phase 3 artifacts are `src/main/routes/chat/`, `src/main/routes/sessions/`, `src/main/routes/hotPathPorts.ts`, `src/main/routes/scheduler.ts`, `src/renderer/api/ChatClient.ts`, `src/renderer/api/SessionClient.ts`, `src/renderer/src/stores/ui/session.ts`, `src/renderer/src/stores/ui/message.ts`, and `src/renderer/src/stores/ui/pageRouter.ts`
-- Phase 4 implementation and automated verification completed on `2026-04-20`
-- Automated verification for Phase 4 uses `pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts test/main/routes/chatService.test.ts test/main/routes/providerService.test.ts`
-- Automated verification for Phase 4 uses `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/api/clients.test.ts test/renderer/components/ChatPage.test.ts`
-- Automated verification for Phase 4 uses `pnpm exec vitest --config vitest.config.ts test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts test/main/presenter/agentSessionPresenter/agentSessionPresenter.test.ts`
-- Automated verification for Phase 4 uses `pnpm exec vitest --config vitest.config.ts test/main/presenter/agentSessionPresenter/integration.test.ts test/main/presenter/agentSessionPresenter/usageDashboard.test.ts`
-- Automated verification for Phase 4 uses `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, `pnpm run typecheck`
-- Automated verification for Phase 4 uses `node scripts/architecture-guard.mjs`
-- Automated verification for Phase 4 uses `pnpm run architecture:baseline`
-- Repro smoke for Phase 4 is: cold start the app, open provider settings and run one provider verification, create or open a session that triggers a permission/question overlay, approve or answer it through the overlay, confirm the overlay disappears and the message list refreshes, then stop any in-flight stream and confirm the session remains usable
-- Phase 4 artifacts are `src/main/presenter/runtimePorts.ts`, `src/main/presenter/index.ts`, `src/main/presenter/agentRuntimePresenter/index.ts`, `src/main/presenter/agentSessionPresenter/index.ts`, `src/main/routes/providers/providerService.ts`, `src/main/routes/hotPathPorts.ts`, `src/main/routes/index.ts`, `src/shared/contracts/routes/providers.routes.ts`, `src/shared/contracts/routes/chat.routes.ts`, `src/renderer/api/ProviderClient.ts`, `src/renderer/api/ChatClient.ts`, `src/renderer/src/stores/providerStore.ts`, and `src/renderer/src/pages/ChatPage.vue`
-- Phase 5 implementation and automated verification completed on `2026-04-20`
-- Automated verification for Phase 5 uses `pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts test/main/routes/settingsHandler.test.ts test/main/routes/sessionService.test.ts test/main/routes/chatService.test.ts test/main/routes/providerService.test.ts test/main/routes/scheduler.test.ts test/main/presenter/agentRuntimePresenter/echo.test.ts test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts test/main/presenter/agentSessionPresenter/agentSessionPresenter.test.ts test/main/presenter/agentSessionPresenter/integration.test.ts test/main/presenter/agentSessionPresenter/usageDashboard.test.ts`
-- Automated verification for Phase 5 uses `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/api/createBridge.test.ts test/renderer/api/clients.test.ts test/renderer/stores/uiSettingsStore.test.ts test/renderer/stores/pageRouter.test.ts test/renderer/stores/messageStore.test.ts test/renderer/stores/sessionStore.test.ts test/renderer/components/ChatPage.test.ts`
-- The Phase 5 migrated-path regression pack passed with `19` test files and `269` tests
-- Automated verification for Phase 5 uses `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, `pnpm run typecheck`
-- Automated verification for Phase 5 uses `pnpm run architecture:baseline`
-- Repro smoke for Phase 5 is: cold start the app, change one persisted setting, create a regular session, send one message, confirm stream updates render before completion, stop one in-flight reply, restart the app, restore the same session, run one provider verification, and complete one permission or question interaction
-- Phase 5 final smoke handoff result is `pending manual validation before commit`; use the runbook below to record pass/fail per step
-- Phase 5 artifacts are the refreshed `docs/architecture/baselines/main-kernel-*.{md,json}` reports at current phase `P5`, plus the updated `docs/README.md`, `docs/ARCHITECTURE.md`, and `docs/guides/code-navigation.md`
-
-## Standard Verification Commands
-
-每个阶段结束至少执行:
-
-- `pnpm run format`
-- `pnpm run i18n`
-- `pnpm run lint`
-- `pnpm run typecheck`
-
-按改动范围补充:
-
-- `pnpm run test:main`
-- `pnpm run test:renderer`
-- 受影响模块的 targeted Vitest suites
-- baseline / guard 相关脚本
-
-## Slice-Specific Test Notes
-
-### Settings Slice
-
-- 验证读、写、变更通知、重启持久化
-- 验证 settings store 不再依赖 `useLegacyPresenter()` 主入口
-- 验证 settings 通知走 typed event
-
-### Chat & Session Slice
-
-- 验证 session create / restore / activate
-- 验证发送消息成功路径
-- 验证停止流
-- 验证 provider 失败路径和超时路径
-- 验证 cancel / timeout / retry 的 owner 清楚且不会残留无主状态
-
-### Provider / Permission Slice
-
-- 验证 provider query 与执行能力边界
-- 验证 permission request / response contract
-- 验证 migrated path 不再通过 presenter 直接协商 provider 行为
-
-## Manual Smoke Matrix
-
-每轮阶段验收至少执行以下手工验证:
-
-1. 冷启动应用,确认主窗口正常打开。
-2. 修改一个设置并确认界面状态更新。
-3. 创建一个新会话。
-4. 发送一条普通消息。
-5. 在 stream 进行中执行一次 stop/cancel。
-6. 关闭并重新打开相关页面或重启应用,确认会话与设置可恢复。
-7. 完成一次 provider 相关交互,确认结果正常返回。
-
-### Phase 5 Final Smoke Handoff
-
-1. 冷启动应用,确认主窗口打开且没有启动报错。
-2. 打开设置页,修改一个可持久化项,例如字体大小或通知开关,确认界面立即刷新。
-3. 完全退出应用后重新启动,确认刚才的设置仍然保留。
-4. 创建一个普通会话,发送一条短消息,确认用户消息和助手消息顺序正确。
-5. 在回答流式生成过程中确认增量内容持续追加,然后执行一次 stop/cancel。
-6. 重新打开刚才的会话,确认会话列表、消息历史和当前活跃会话恢复正常。
-7. 打开 provider 设置并执行一次 `test connection`,确认成功或失败信息能返回到界面。
-8. 触发一次权限或问题交互,确认通过 overlay 响应后 overlay 消失,消息列表继续刷新。
-
-## Exit Evidence Per Phase
-
-每个阶段完成时,应附带:
-
-- 通过的命令列表
-- 新增或修改的测试列表
-- 手工 smoke 结论
-- 更新后的 baseline 摘要
-- bridge register 更新结果
-- scoreboard 更新结果
-
-## Final Regression Gate
-
-在 Phase 5 之前,必须至少完成一次覆盖以下能力的综合回归:
-
-- settings read/write
-- create session
-- restore session
-- send message
-- stream reply
-- stop stream
-- provider or permission interaction
-- This regression gate was satisfied on `2026-04-20` by the Phase 5 targeted migrated-path suites listed above.
-
diff --git a/docs/architecture/new-ui-implementation-plan.md b/docs/architecture/new-ui-implementation-plan.md
deleted file mode 100644
index b03c83387..000000000
--- a/docs/architecture/new-ui-implementation-plan.md
+++ /dev/null
@@ -1,619 +0,0 @@
-# New UI Feature Implementation Plan
-
-**Status:** ✅ **IMPLEMENTATION COMPLETE**
-**Completion Date:** 2026-03-09
-
----
-
-> **Note:** This plan has been fully implemented. The new UI architecture is now the primary interface. This document is retained for historical reference.
-
----
-
-This document defines the technical plan for implementing complete functionality on the new UI architecture, without considering legacy compatibility migration, based on entirely new code implementation.
-
----
-
-## 1. Architecture Overview
-
-### 1.1 Target Architecture
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ ChatTabView (Entry) │
-│ ┌─────────────────────────────────────────────────────────┐│
-│ │ WelcomePage │ Displayed when no Provider config ││
-│ ├─────────────────────────────────────────────────────────┤│
-│ │ NewThreadPage │ Displayed when creating new session││
-│ ├─────────────────────────────────────────────────────────┤│
-│ │ ChatPage │ Displayed during active session ││
-│ └─────────────────────────────────────────────────────────┘│
-│ ┌─────────────────────────────────────────────────────────┐│
-│ │ WindowSideBar │ Agent filter + Session list ││
-│ └─────────────────────────────────────────────────────────┘│
-└─────────────────────────────────────────────────────────────┘
-```
-
-### 1.2 Core Principles
-
-1. **Unidirectional Data Flow**: Store → Composable → Component
-2. **State Machine Driven**: Page transitions controlled by PageState state machine
-3. **Presenter Pattern**: All business logic handled through Main process Presenters
-4. **Reactive Design**: Components only handle UI rendering, state managed by Stores
-
----
-
-## 2. Directory Structure
-
-```
-src/renderer/src/
-├── views/
-│ └── ChatTabView.vue # Page entry, state machine logic
-├── pages/ # Page components (new directory)
-│ ├── WelcomePage.vue # Welcome page
-│ ├── NewThreadPage.vue # New thread page
-│ └── ChatPage.vue # Chat page
-├── components/
-│ ├── sidebar/ # Sidebar components (new directory)
-│ │ ├── AgentFilter.vue # Agent filter
-│ │ ├── SessionGroup.vue # Session group
-│ │ ├── SessionItem.vue # Session item
-│ │ └── SidebarActions.vue # Sidebar action buttons
-│ ├── chat/ # Chat related components (new directory)
-│ │ ├── ChatTopBar.vue # Chat top bar
-│ │ ├── MessageList.vue # Message list
-│ │ ├── MessageItem.vue # Message item
-│ │ ├── ChatInput.vue # Input box
-│ │ ├── InputToolbar.vue # Input toolbar
-│ │ └── ChatStatusBar.vue # Status bar
-│ └── common/ # Common components
-│ ├── ProjectSelector.vue # Project selector
-│ ├── ModelSelector.vue # Model selector
-│ └── PermissionSelector.vue # Permission selector
-├── stores/
-│ ├── ui/ # UI related Stores (new directory)
-│ │ ├── pageState.ts # Page state machine
-│ │ ├── agent.ts # Agent management
-│ │ ├── session.ts # Session management
-│ │ └── project.ts # Project management
-│ └── chat.ts # Retained, message related logic
-├── composables/
-│ ├── usePageState.ts # Page state management
-│ ├── useAgentFilter.ts # Agent filter logic
-│ ├── useSessionGroup.ts # Session grouping logic
-│ └── useProjectRecent.ts # Recent projects logic
-└── types/
- └── ui.ts # UI type definitions
-```
-
----
-
-## 3. State Management Layer
-
-### 3.1 Page State Machine (pageState.ts)
-
-**Responsibility**: Manage global page state, control page transition logic
-
-**State Definition**:
-```typescript
-type PageState =
- | { type: 'welcome' }
- | { type: 'newThread' }
- | { type: 'chat'; sessionId: string }
-```
-
-**State Transition Triggers**:
-- On startup, check Provider config → welcome / newThread
-- Create session → chat
-- Switch session → chat (update sessionId)
-- Close session → newThread
-- Delete last Provider → welcome
-
-**Implementation Points**:
-- Use Pinia store to manage state
-- Provide `transitionTo(state)` method
-- Provide `initialize()` method for startup initialization
-
-### 3.2 Agent Store (agent.ts)
-
-**Responsibility**: Manage Agent list and Agent filter state
-
-**Data Structure**:
-```typescript
-interface Agent {
- id: string
- name: string
- type: 'deepchat' | 'acp'
- enabled: boolean
- icon?: string
-}
-
-interface AgentState {
- agents: Agent[]
- selectedAgentId: string | null // null means "All Agents"
- loading: boolean
-}
-```
-
-**Data Sources**:
-- DeepChat Agent: Always exists
-- ACP Agents: Get from `configPresenter.getAcpAgents()`
-
-### 3.3 Session Store (session.ts)
-
-**Responsibility**: Manage session list and session grouping
-
-**Data Structure**:
-```typescript
-interface Session {
- id: string
- title: string
- agentId: string
- status: 'completed' | 'working' | 'error' | 'none'
- projectDir: string
- providerId?: string
- modelId?: string
- activeSkills?: string[]
- createdAt: number
- updatedAt: number
-}
-
-type SessionGroup =
- | { type: 'time'; label: string; sessions: Session[] }
- | { type: 'project'; project: Project; sessions: Session[] }
-
-interface SessionState {
- sessions: Session[]
- activeSessionId: string | null
- groupByProject: boolean
- loading: boolean
-}
-```
-
-**Key Actions**:
-- `fetchSessions()`: Get session list from sessionPresenter
-- `createSession(settings)`: Create new session
-- `selectSession(id)`: Select session
-- `closeSession()`: Close current session
-- `toggleGroupMode()`: Toggle grouping mode
-
-### 3.4 Project Store (project.ts)
-
-**Responsibility**: Manage recent project list
-
-**Data Structure**:
-```typescript
-interface Project {
- path: string
- name: string
- lastAccessedAt: number
- sessionCount: number
-}
-
-interface ProjectState {
- projects: Project[]
- loading: boolean
-}
-```
-
-**Data Sources**:
-- Aggregated from session list
-- Support manual addition via folder picker
-
----
-
-## 4. Page Component Layer
-
-### 4.1 ChatTabView.vue
-
-**Responsibility**: Page entry, render corresponding page based on PageState
-
-**Implementation**:
-```vue
-
-
-
-```
-
-**Initialization Flow**:
-1. Call `pageState.initialize()` on component mount
-2. Check Provider configuration
-3. Check if there's an active session
-4. Determine initial page state
-
-### 4.2 WelcomePage.vue
-
-**Responsibility**: Guide user to configure first Provider
-
-**Data Dependencies**:
-- ProviderStore: Get recommended Provider list
-
-**Interactions**:
-- Click Provider → Call `windowPresenter.openOrFocusSettingsTab()`
-- Click ACP Agent entry → Same as above
-
-### 4.3 NewThreadPage.vue
-
-**Responsibility**: New session entry
-
-**Data Dependencies**:
-- ProjectStore: Recent project list
-- AgentStore: Agent selection
-- ModelStore: Model selection (DeepChat Agent only)
-
-**Subcomponents**:
-- ProjectSelector: Project/folder selection
-- ChatInput: Message input
-- ChatStatusBar: Model/permission configuration
-
-**Interactions**:
-- Send message → Call `sessionStore.createSession()` → Page transitions to ChatPage
-
-### 4.4 ChatPage.vue
-
-**Responsibility**: Main interface during active session
-
-**Data Dependencies**:
-- SessionStore: Current session info
-- ChatStore: Message list
-
-**Subcomponents**:
-- ChatTopBar: Title, project, share, more actions
-- MessageList: Message rendering
-- ChatInput: Message input
-- ChatStatusBar: Current configuration display
-
----
-
-## 5. Sidebar Component Layer
-
-### 5.1 WindowSideBar.vue Refactoring
-
-**Structure**:
-```
-┌─────────────────────────────────┐
-│ AgentFilter (icon column) │
-│ - All Agents │
-│ - DeepChat │
-│ - Claude Code │
-│ - ... │
-├─────────────────────────────────┤
-│ SessionList │
-│ - SessionGroup (Today) │
-│ - SessionItem │
-│ - SessionItem │
-│ - SessionGroup (Yesterday) │
-│ - SessionItem │
-├─────────────────────────────────┤
-│ SidebarActions │
-│ - New Chat │
-│ - Toggle Group Mode │
-│ - Collapse │
-│ - Browser │
-│ - Settings │
-└─────────────────────────────────┘
-```
-
-**Implementation**:
-- Split into independent subcomponents
-- Share state via composables
-- Support collapsed/expanded modes
-
-### 5.2 AgentFilter.vue
-
-**Responsibility**: Agent icon list, filter sessions
-
-**Props**: None (get data from AgentStore)
-
-**Events**:
-- `@select`: Triggered when Agent is selected
-
-### 5.3 SessionGroup.vue
-
-**Responsibility**: Session group display
-
-**Props**:
-- `group: SessionGroup`: Group data
-
-**Slots**:
-- `default`: SessionItem rendering
-
-### 5.4 SessionItem.vue
-
-**Responsibility**: Single session item rendering
-
-**Props**:
-- `session: Session`: Session data
-- `active: boolean`: Whether active
-
-**Events**:
-- `@click`: Click session
-
----
-
-## 6. Chat Component Layer
-
-### 6.1 ChatTopBar.vue
-
-**Responsibility**: Chat top info bar
-
-**Props**:
-- `title: string`: Session title
-- `projectPath: string`: Project path
-
-**Slots**:
-- Right action buttons area
-
-### 6.2 MessageList.vue
-
-**Responsibility**: Message list rendering
-
-**Key Features**:
-- Virtual scrolling support (large message count optimization)
-- Scroll to specific message
-- Message preloading
-
-**Implementation Points**:
-- Reuse core logic from existing `MessageList.vue`
-- Adapt to new data structures
-
-### 6.3 ChatInput.vue
-
-**Responsibility**: Message input area
-
-**Key Features**:
-- @ mention files
-- / commands
-- Multi-line input
-- Attachment upload
-
-**Implementation Points**:
-- Reuse existing `ChatInput.vue` component
-- Adapt to new page structure
-
----
-
-## 7. Development Phases
-
-### Phase 1: Basic Framework (1-2 weeks)
-
-**Goal**: Build page skeleton and state management
-
-**Tasks**:
-1. Create directory structure
-2. Implement `pageState.ts` state machine
-3. Implement `agent.ts` Store
-4. Implement basic `session.ts` Store structure
-5. Refactor `ChatTabView.vue` page switching logic
-6. Create `WelcomePage.vue` static page
-
-**Acceptance Criteria**:
-- Pages correctly display Welcome / NewThread / Chat states
-- State transition logic is correct
-
-### Phase 2: Sidebar Functionality (1-2 weeks)
-
-**Goal**: Complete sidebar interaction
-
-**Tasks**:
-1. Refactor `WindowSideBar.vue`
-2. Implement `AgentFilter.vue`
-3. Implement `SessionGroup.vue` / `SessionItem.vue`
-4. Implement `useSessionGroup.ts` grouping logic
-5. Implement `project.ts` Store
-6. Implement `SidebarActions.vue`
-
-**Acceptance Criteria**:
-- Sidebar correctly displays Agent filter
-- Session list grouped by time/project
-- Clicking session correctly switches page
-
-### Phase 3: NewThread Page (1 week)
-
-**Goal**: Complete new session functionality
-
-**Tasks**:
-1. Implement `ProjectSelector.vue`
-2. Implement permission selector
-3. Integrate ChatInput component
-4. Implement session creation logic
-
-**Acceptance Criteria**:
-- Can select project/folder
-- Can select Agent and model
-- Session is created and navigates correctly after sending message
-
-### Phase 4: Chat Page (1-2 weeks)
-
-**Goal**: Complete session interaction
-
-**Tasks**:
-1. Implement `ChatTopBar.vue`
-2. Integrate existing `MessageList.vue`
-3. Integrate existing `ChatInput.vue`
-4. Implement `ChatStatusBar.vue`
-5. Implement message send/receive logic
-
-**Acceptance Criteria**:
-- Chat page correctly displays title and project
-- Message list renders correctly
-- Can send and receive messages
-
-### Phase 5: Optimization & Polish (1 week)
-
-**Goal**: Performance optimization and detail refinement
-
-**Tasks**:
-1. Performance optimization (virtual scrolling, lazy loading)
-2. Animation transition effects
-3. Error handling and edge cases
-4. Internationalization support
-
----
-
-## 8. Data Flow Design
-
-### 8.1 Initialization Flow
-
-```
-App Mounted
- │
- ▼
-pageState.initialize()
- │
- ├── providerStore.hasEnabledProviders()?
- │ │
- │ ├── No → transitionTo('welcome')
- │ │
- │ └── Yes ↓
- │
- ├── sessionStore.hasActiveSession()?
- │ │
- │ ├── Yes → transitionTo('chat', sessionId)
- │ │
- │ └── No → transitionTo('newThread')
- │
- └── agentStore.fetchAgents()
- projectStore.fetchProjects()
-```
-
-### 8.2 Create Session Flow
-
-```
-NewThreadPage: User sends message
- │
- ▼
-sessionStore.createSession(settings)
- │
- ├── Call sessionPresenter.createConversation()
- │
- ├── Update local sessions list
- │
- └── pageState.transitionTo('chat', newSessionId)
- │
- ▼
- ChatPage renders
- │
- ▼
- agentPresenter.sendMessage()
-```
-
-### 8.3 Session Switch Flow
-
-```
-Sidebar: Click session item
- │
- ▼
-sessionStore.selectSession(id)
- │
- ├── Call sessionPresenter.setActiveConversation()
- │
- └── pageState.transitionTo('chat', id)
-```
-
----
-
-## 9. Relationship with Existing Code
-
-### 9.1 Reusable Components
-
-| Component | Reuse Level | Notes |
-|-----------|-------------|-------|
-| ChatInput.vue | High reuse | Core input logic unchanged, adapt to new structure |
-| MessageList.vue | High reuse | Message rendering logic unchanged |
-| MessageItem*.vue | High reuse | Message item components unchanged |
-| MarkdownRenderer.vue | Full reuse | No modification needed |
-| Artifact*.vue | Full reuse | No modification needed |
-
-### 9.2 Reusable Stores
-
-| Store | Reuse Level | Notes |
-|-------|-------------|-------|
-| chat.ts | Partial reuse | Message logic retained, session management migrated to new session.ts |
-| providerStore.ts | Full reuse | No modification needed |
-| modelStore.ts | Full reuse | No modification needed |
-
-### 9.3 New vs Modified
-
-**New**:
-- `stores/ui/pageState.ts`
-- `stores/ui/agent.ts`
-- `stores/ui/session.ts`
-- `stores/ui/project.ts`
-- `pages/*.vue`
-- `components/sidebar/*.vue`
-- `components/chat/*.vue` (partial)
-
-**Modified**:
-- `ChatTabView.vue`: Refactor page switching logic
-- `WindowSideBar.vue`: Refactor sidebar structure
-- `chat.ts`: Remove session management logic, keep message logic
-
-**Deprecated**:
-- `components/mock/*.vue`: Removed after the new UI rollout, only historical docs remain
-- `composables/useMockViewState.ts`: Removed after stores took over the state flow
-
----
-
-## 10. Testing Strategy
-
-### 10.1 Unit Tests
-
-**Store Tests**:
-- `pageState.ts`: State transition logic
-- `session.ts`: Session CRUD operations
-- `agent.ts`: Agent filter logic
-
-**Composable Tests**:
-- `useSessionGroup.ts`: Grouping calculation logic
-
-### 10.2 Component Tests
-
-- `WelcomePage.vue`: Snapshot test
-- `NewThreadPage.vue`: Interaction test
-- `ChatPage.vue`: Interaction test
-- `SessionItem.vue`: Render test
-
-### 10.3 Integration Tests
-
-- Complete session creation flow
-- Session switch flow
-- Page state transition flow
-
----
-
-## 11. Risks and Considerations
-
-### 11.1 Risk Points
-
-| Risk | Impact | Mitigation |
-|------|--------|------------|
-| Increased state management complexity | Medium | Use Pinia devtools for debugging |
-| Page switching performance | Low | Use keep-alive to cache page state |
-| Conflict with existing code | Medium | Create new files, gradual migration |
-| Missing i18n | Low | Add i18n keys during development |
-
-### 11.2 Considerations
-
-1. **Progressive Migration**: Build new structure first, then gradually migrate functionality
-2. **Maintain Compatibility**: Keep old UI available until new UI is complete
-3. **Performance First**: Use virtual scrolling for large lists
-4. **Type Safety**: Use TypeScript strict mode for all new code
-
----
-
-## 12. Summary
-
-This plan is based on the product architecture defined in `ui-architecture.md`, using a clear layered design:
-
-1. **State Layer**: 4 core Stores managing page, Agent, session, and project states
-2. **Page Layer**: 3 page states, driven by state machine transitions
-3. **Component Layer**: Fine-grained component splitting for sidebar and chat areas
-
-Development cycle estimated at 6-8 weeks, implemented progressively in 5 phases.
diff --git a/docs/architecture/remove-rebrand-tool/plan.md b/docs/architecture/remove-rebrand-tool/plan.md
deleted file mode 100644
index 7a42049ec..000000000
--- a/docs/architecture/remove-rebrand-tool/plan.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Remove Rebrand Tool Plan
-
-## Approach
-
-- Keep this SDD folder as the decision record for removing the deprecated tooling.
-- Delete the orphaned rebrand script, brand config template, example brand config, and brand asset
- placeholder.
-- Leave `package.json`, Electron builder configuration, runtime resources, and application metadata
- untouched because no package script currently exposes the rebrand path.
-
-## Compatibility
-
-- The only removed interface is direct ad hoc execution of `node scripts/rebrand.js`.
-- No stored user data, app configuration, IPC contract, or build artifact schema changes.
-
-## Validation
-
-- Verify no references remain outside this SDD record with a repository search for `rebrand`,
- `brand-assets`, and `brand-config`.
-- Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`.
diff --git a/docs/architecture/remove-rebrand-tool/spec.md b/docs/architecture/remove-rebrand-tool/spec.md
deleted file mode 100644
index bcef04271..000000000
--- a/docs/architecture/remove-rebrand-tool/spec.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Remove Rebrand Tool
-
-## Goal
-
-Remove the deprecated repository-local rebrand tooling so the project no longer carries an
-unmaintained brand replacement path.
-
-## Acceptance Criteria
-
-- `scripts/rebrand.js` is removed.
-- `brand-config.template.json` and `brand-config.example-banana.json` are removed.
-- `scripts/brand-assets/` no longer has a tracked placeholder file.
-- No code, build, package script, or documentation outside this SDD record references the removed
- rebrand assets.
-- Runtime app behavior, branding, build configuration, IPC, config storage, and i18n output remain
- unchanged.
-
-## Non-Goals
-
-- Do not replace the rebrand tool with a new white-labeling mechanism.
-- Do not alter current DeepChat product metadata, icons, logos, updater settings, or application
- resources.
-- Do not add migrations or compatibility shims for direct `node scripts/rebrand.js` use.
-
-## Open Questions
-
-None.
diff --git a/docs/architecture/remove-rebrand-tool/tasks.md b/docs/architecture/remove-rebrand-tool/tasks.md
deleted file mode 100644
index f5ed76e92..000000000
--- a/docs/architecture/remove-rebrand-tool/tasks.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Remove Rebrand Tool Tasks
-
-- [x] Confirm current references are limited to the rebrand assets.
-- [x] Add minimal SDD documentation for the removal.
-- [x] Remove the rebrand script, brand configs, and brand asset placeholder.
-- [x] Verify no non-SDD references remain.
-- [x] Run required formatting, i18n, and lint checks.
diff --git a/docs/architecture/renderer-main-single-track/plan.md b/docs/architecture/renderer-main-single-track/plan.md
deleted file mode 100644
index 246ddaba0..000000000
--- a/docs/architecture/renderer-main-single-track/plan.md
+++ /dev/null
@@ -1,468 +0,0 @@
-# Renderer-Main Single Track Plan
-
-## Planning Goal
-
-本计划解决的是一个非常具体的问题:
-
-`main kernel refactor` 已经把 renderer-main 主边界做成了可迁移、可测试、可扩展的 typed path,
-但 renderer 业务层还没有真正切到单轨。
-
-因此,这一轮计划的目标不是继续“重构 main”,而是:
-
-- 先把 renderer 业务层的 transport 心智模型收成一条
-- 再按 capability family 分批迁掉剩余 legacy 调用
-- 最后用 guard + docs + merge gate 固化规则
-
-## Baseline Snapshot
-
-以下基线来自 `2026-04-20` 当前仓库扫描:
-
-| Metric | Value | Meaning |
-| --- | --- | --- |
-| `renderer.usePresenter.count` | `86` | 业务代码仍直接知道 presenter naming |
-| `renderer.windowElectron.count` | `95` | 业务代码仍直接知道 Electron IPC |
-| `renderer.windowApi.count` | `33` | 业务代码仍直接知道 legacy preload multi-entry |
-| `hotpath.presenterEdge.count` | `10` | main hot path 已明显收口,但 renderer 入口仍不单轨 |
-| `bridge.active.count` | `0` | `main kernel refactor` 已经没有过渡 bridge 残留 |
-
-这说明当前分支的主要风险已经不在 main hot path,而在 renderer 开发入口仍然模糊。
-
-说明:`renderer.usePresenter.count` 保留旧 metric id,用于连续追踪当前
-legacy presenter helper 的剩余触点。
-
-## Current Presenter Hotspots
-
-按 `useLegacyPresenter()` 名称分布,当前主要热点为:
-
-| Legacy Surface | Current Hits | Target Single-Track Surface | Priority |
-| --- | --- | --- | --- |
-| `configPresenter` | `26` | 扩展 `SettingsClient`,并补 `ConfigClient` / provider-model typed contracts | P2 |
-| `agentSessionPresenter` | `13` | 扩展 `SessionClient` 覆盖 session action / pending-input / export 等能力 | P4 |
-| `windowPresenter` | `9` | `WindowClient` + typed window/system events | P3 |
-| `devicePresenter` | `8` | `DeviceClient` + runtime wrappers | P3 |
-| `workspacePresenter` | `5` | `WorkspaceClient` | P3 |
-| `llmproviderPresenter` | `4` | 扩展 `ProviderClient` 或补 `ModelClient` | P2 / P4 |
-| `skillPresenter` | `4` | `SkillClient` | P4 |
-| `filePresenter` | `2` | `FileClient` | P3 |
-| `mcpPresenter` | `2` | `McpClient` + typed events | P4 |
-| `projectPresenter` | `2` | `ProjectClient` | P3 |
-| `tabPresenter` | `2` | `TabClient` 或并入 window runtime layer | P3 |
-| `yoBrowserPresenter` | `2` | `BrowserClient` | P3 |
-| others | `1` each | 对应 typed client / runtime wrapper | P4 |
-
-这决定了迁移顺序不应该是“86 个点逐个改”,而应该按 capability family 收口。
-
-## Handoff Decision
-
-本计划的关键决策如下:
-
-### 1. Merge Gate Before Branch Merge
-
-当前双轨状态不作为最终可合并状态。
-
-合并前必须先完成 renderer 业务层单轨化,而不是把“后面继续慢慢迁”当作默认路径。
-
-### 2. `useLegacyPresenter()` Downgraded To Internal Compatibility Utility
-
-`useLegacyPresenter()` 不再是新功能入口。
-
-在计划完成前,它最多只能存在于 quarantine adapter 内部,不能再被 `src/renderer/src/**` 业务模块直接 import。
-
-### 3. Business Layer Must Not See Raw Electron IPC
-
-`window.electron` 和 `window.api` 只能存在于极少数 bridge / runtime wrapper。
-
-业务模块只允许看到:
-
-- typed client
-- typed event subscription helper
-- 明确命名的 runtime service
-
-### 4. No Mixed Transport Per Module
-
-如果某个模块已经开始用 typed client,就不能再保留 presenter / raw IPC 调用。
-
-允许短期 mixed mode 的唯一位置,是显式 quarantine adapter。
-
-## Target State
-
-目标态如下:
-
-```text
-renderer component / store / page / composable
- -> domain-level client or runtime wrapper
- -> src/renderer/api/*Client
- -> window.deepchat
- -> shared/contracts/routes + shared/contracts/events
- -> src/main/routes/*
- -> hot path ports / presenters / runtime internals
-```
-
-legacy transport 的唯一允许形态:
-
-```text
-temporary quarantine adapter
- -> useLegacyPresenter() or raw window.electron / window.api
-```
-
-并且 quarantine adapter 不允许被视为“长期公共 API”。
-
-## Allowed Public Surfaces
-
-计划完成后,renderer 对 main 的公开入口只允许是:
-
-- `src/renderer/api/*Client`
-- typed event subscription API
-- 明确命名的 runtime wrapper,例如 window context / device context / shell integration wrapper
-
-以下实现细节只能留在 wrapper / adapter 层:
-
-- `window.deepchat`
-- `window.electron`
-- `window.api`
-- presenter reflection transport
-
-## Forbidden Surfaces
-
-以下行为在本计划中视为禁止:
-
-1. 在 `src/renderer/src/**` 新增 `useLegacyPresenter()` import
-2. 在 `src/renderer/src/**` 新增 `window.electron.*`
-3. 在 `src/renderer/src/**` 新增 `window.api.*`
-4. 在同一个业务模块内混用 typed client 与 legacy transport
-5. 用新的 generic helper 再包一层 presenter reflection,表面看像 typed helper,实质仍走旧协议
-
-## Quarantine Model
-
-为了避免“一边迁,一边到处混用”,本计划要求先建立 quarantine 规则:
-
-- 业务层:`src/renderer/src/**`
-- typed boundary 层:`src/renderer/api/**`
-- temporary quarantine 层:固定为 `src/renderer/api/legacy/**`
-
-规则:
-
-- 业务层只能 import typed boundary 层
-- quarantine 层可以暂时调用 legacy transport
-- 任何 legacy transport 都不得继续散落在业务层
-
-补充规则:
-
-- 不允许再创建第二个 quarantine 目录,例如 `compat/`、`legacy2/`、`v1/`
-- 任何 quarantine 文件都必须是 capability adapter,不允许在其中继续长业务状态管理逻辑
-- `P0` 完成前不进入 `P1`
-
-## Client Boundary Decision Rules
-
-为避免后续围绕“该扩展旧 client 还是新建 client”反复争论,本计划定义以下规则:
-
-### Rule 1: Same Capability Family, Same Owner, Same Event Domain -> Extend
-
-如果某能力同时满足:
-
-- 属于同一 capability family
-- 主要由同一 main route/service owner 承载
-- 使用同一组 typed route / typed event 契约
-
-则优先扩展现有 client。
-
-例子:
-
-- `SessionClient` 扩展 session rename / delete / export / pending-input
-- `ProviderClient` 扩展 provider query / validation / provider-level mutations
-
-### Rule 2: New Capability Family Or Different Event Semantics -> New Client
-
-如果某能力满足以下任一条件,则优先新建 client:
-
-- 与现有 client 不属于同一 capability family
-- 需要独立的 typed event contract family
-- 生命周期、权限模型或调用频率与现有 client 明显不同
-- 将其塞进现有 client 会让该 client 同时承担多个不相干 owner
-
-例子:
-
-- window/system navigation 与 provider config 不应共用同一个 client
-- workspace file tree 与 chat/session action 不应共用同一个 client
-
-### Rule 3: Config Is Not A Dumping Ground
-
-`SettingsClient` 只承载真正属于 settings/settings snapshot 的能力。
-
-如果某能力虽然历史上挂在 `configPresenter` 上,但语义上属于 provider/model/agent/workspace 域,
-应迁移到对应 domain client,而不是继续往 `SettingsClient` 塞。
-
-### Rule 4: Transport Wrapper Is Not A Client
-
-如果某模块只是为了封装 `window.deepchat` / `window.electron` / `window.api` 的调用细节,
-但没有 capability-level 语义,就应视为 runtime wrapper 或 adapter,不应伪装成 domain client。
-
-## Typed Event Ownership
-
-typed event contract 的建立不是隐含工作,而是每个 phase 的显式交付物。
-
-规则如下:
-
-- 迁移哪个 capability family,哪个 phase 就负责定义该 family 的 typed events
-- 不允许先删 raw listener,再把 typed event 留给“后面顺手补”
-- client、event contract、store subscription 改造必须同 phase 收口
-
-归属划分:
-
-| Phase | Typed Event Ownership |
-| --- | --- |
-| `P2` | config / provider / model family typed events |
-| `P3` | window / device / workspace / browser / project family typed events |
-| `P4` | session residual / skill / mcp / sync / upgrade / dialog family typed events |
-
-## Rollback / Fallback Rule
-
-如果某个 capability family 在迁移中被证明无法在当前 phase 完成 cutover:
-
-1. 不允许把半完成 mixed transport 直接留在业务模块
-2. 必须回退到:
- - 原有单一 legacy 路径,或
- - quarantine adapter 中的单一路径
-3. 必须在 `tasks.md` 中记录 blocked reason 和下一阶段 owner
-4. 不允许以“先保留双轨,后面再说”作为默认 fallback
-
-## Phase Map
-
-```text
-P0 Rules & Guard Hardening
- -> P1 Transport Consolidation
- -> P2 Config / Provider / Model Family
- -> P3 Window / Device / Workspace Family
- -> P4 Session Residual / MCP / Skill / Misc Family
- -> P5 Retirement, Docs, Merge Gate
-```
-
-## Phase Details
-
-### P0: Rules & Guard Hardening
-
-目标:
-
-- 把“单轨化”从目标口号变成强约束
-- 让后续改动不能再回流到业务层 legacy transport
-
-交付物:
-
-- single-track spec / plan / tasks
-- 更新 `docs/README.md`、`docs/ARCHITECTURE.md`、`docs/spec-driven-dev.md`、`docs/guides/getting-started.md`
-- `architecture-guard` 从“防增长”升级为“业务层禁用 + quarantine 白名单”
-- baseline 报告增加 business-layer / quarantine-layer 维度
-- 固定唯一 quarantine 目录:`src/renderer/api/legacy/**`
-
-退出条件:
-
-- 新功能无法再在业务层直接新增 `useLegacyPresenter()` 或 raw IPC
-- 入口文档已经明确 single-track 规则
-- `P1` 所需的 quarantine 输出已经存在且路径固定
-
-### P1: Transport Consolidation
-
-目标:
-
-- 先把 transport helper 自己做成单轨
-- 移除“helper 名字变了,但底层还是 presenter reflection”的伪单轨
-
-交付物:
-
-- `useLegacyPresenter()` 迁入 internal compatibility transport,或降级为 quarantine-only utility
-- `src/renderer/api/legacy/` 目录实际建立并承接 legacy transport
-- `useIpcQuery` / `useIpcMutation` 改为:
- - 面向 typed client 的 helper,或
- - 直接退役
-- `window.api` / `window.electron` 相关 runtime access 收口到专用 wrapper
-- 业务层停止直接 import transport primitive
-
-退出条件:
-
-- `src/renderer/src/**` 不再 direct import `@api/legacy/presenters`
-- mixed transport module 被消除
-
-### P2: Config / Provider / Model Family
-
-目标:
-
-- 先清掉最大头的 `configPresenter` 系列调用
-- 把 provider / model / config 相关能力全部收成 typed client
-
-当前状态(`2026-04-20`):
-
-- 已完成
-- `src/renderer/src/**` 中 `configPresenter` / `llmproviderPresenter` 的 P2 业务命中已清零
-- provider / model / config family 已补齐 typed contracts、typed events、typed clients,并完成相关 store / page / component 迁移
-- 相关 renderer/main 定向测试,以及 `pnpm run typecheck`、`pnpm run format`、`pnpm run i18n`、`pnpm run lint` 已通过
-- 验收补丁已对齐 `config.resolveDeepChatAgentConfig` 的 nullable agent config contract,避免 legacy persisted config 在单轨 route 出口被误拒绝
-
-交付物:
-
-- 扩展 `SettingsClient`
-- 补 `ConfigClient`、`ProviderClient`、必要时补 `ModelClient`
-- provider / model / theme / language / system prompt / floating button / shortcut 相关 typed contracts 和 typed events
-- 清理 `providerStore`、`modelStore`、`modelConfigStore`、`systemPromptStore`、`themeStore`、`languageStore`、`shortcutKey`、`floatingButton`、`agentModelStore`
-
-退出条件:
-
-- `configPresenter` 和 `llmproviderPresenter` 不再出现在 `src/renderer/src/**` 业务代码
-- provider family 的事件监听改为 typed event subscription
-
-### P3: Window / Device / Workspace Family
-
-目标:
-
-- 清掉第二批最容易让开发者“顺手继续写 raw IPC”的能力
-
-当前状态(`2026-04-20`):
-
-- 已完成
-- 已补齐 `WindowClient`、`DeviceClient`、`WorkspaceClient`、`ProjectClient`、`FileClient`、`BrowserClient`、`TabClient`,并通过 `src/renderer/api/runtime.ts` 收口唯一 `window.api` typed runtime wrapper
-- 已补齐 window / workspace / browser family typed events,以及对应 shared route / event contracts 和 main route runtime 分发
-- `App.vue`、`AppBar.vue`、`stores/ui/project.ts`、workspace / browser / project / file / device 相关组件与 composables 已完成 cutover
-- `WelcomePage.vue` 与 `NewThreadPage.vue` 已完成 P3 范围审计:前者无需新增改动,后者仅残留 `agentSessionPresenter` 的 P4 scope 调用,不再包含 P3 family transport
-- 业务层 `windowPresenter` / `devicePresenter` / `workspacePresenter` / `projectPresenter` / `filePresenter` / `yoBrowserPresenter` / `tabPresenter` 命中已清零,`src/renderer/src/**` 中不再 direct 使用 window/window-tab 相关 raw IPC
-- P3 定向 main / renderer 自动回归,以及 `pnpm run format`、`pnpm run i18n`、`pnpm run lint`、`pnpm run typecheck` 已通过;后续仅剩 P4 residual family 清理
-
-交付物:
-
-- `WindowClient`
-- `DeviceClient`
-- `WorkspaceClient`
-- `ProjectClient`
-- `FileClient`
-- `BrowserClient`
-- 必要时补 `TabClient` 或 window runtime adapter
-- window / device / workspace family typed events
-- 清理 `App.vue`、`AppBar.vue`、`WelcomePage.vue`、`NewThreadPage.vue`、workspace / browser / project 相关 stores 与组件
-
-退出条件:
-
-- `windowPresenter`、`devicePresenter`、`workspacePresenter`、`projectPresenter`、`filePresenter`、`yoBrowserPresenter`、`tabPresenter`
- 不再出现在业务代码
-- `src/renderer/src/**` 不再 direct 使用 window/window-tab 相关 raw IPC
-
-### P4: Session Residual / MCP / Skill / Misc Family
-
-目标:
-
-- 收掉剩余 presenter family
-- 把“已经有 typed session/chat 主路径,但残余动作还走 presenter”这种半迁移状态补齐
-
-当前状态(`2026-04-20`):
-
-- 已完成
-- 已扩展 `SessionClient` 覆盖 rename / delete / export / pending-input / search / translate / session setting / ACP session config 等 residual 动作,并补齐 `SkillClient`、`McpClient`、`SyncClient`、`UpgradeClient`、`DialogClient`、`ToolClient`
-- 已补齐 skill / mcp / sync / upgrade / dialog / session residual family 的 typed route、typed event、main dispatcher/runtime 接线,以及 direct `sendToRenderer` 源的 typed event publish
-- `stores/ui/session.ts`、`stores/ui/pendingInput.ts`、`stores/skillsStore.ts`、`stores/mcp.ts`、`stores/mcpSampling.ts`、`stores/sync.ts`、`stores/upgrade.ts`、`stores/dialog.ts`、`stores/ollamaStore.ts` 与相关 pages/components/composables 已完成 cutover
-- 业务层 `agentSessionPresenter`、`skillPresenter`、`mcpPresenter`、`syncPresenter`、`upgradePresenter`、`dialogPresenter`、`toolPresenter` 命中已清零,P4 family raw listeners 已改为 typed event subscription
-- `pnpm run format`、`pnpm run i18n`、`pnpm run lint`、`pnpm run typecheck` 与 P4 定向 main/renderer 自动回归已通过
-
-交付物:
-
-- 扩展 `SessionClient` 覆盖 rename / delete / export / session settings / pending input 等残余动作
-- `SkillClient`
-- `McpClient`
-- `SyncClient`
-- `UpgradeClient`
-- `DialogClient`
-- 其他低频 capability 的 typed route / typed event
-- 清理 `skillsStore`、`mcp.ts`、`mcpSampling.ts`、`sync.ts`、`upgrade.ts`、`dialog.ts`、`ollamaStore` 及相关组件
-
-退出条件:
-
-- `agentSessionPresenter`、`skillPresenter`、`mcpPresenter`、`syncPresenter`、`upgradePresenter`、
- `dialogPresenter`、`toolPresenter` 等残余业务层调用清零
-
-### P5: Retirement, Docs, Merge Gate
-
-目标:
-
-- 从“迁移进行中”切换到“规则落地完成”
-
-当前状态(`2026-04-20`):
-
-- 已完成
-- 旧的通用 `usePresenter()` naming 已退役;remaining legacy presenter entry 仅保留在 `src/renderer/api/legacy/presenters.ts`
-- settings compatibility surfaces 继续从 quarantine adapter import;`src/renderer/src/**` 业务层已改为通过明确命名的 runtime wrapper 使用 residual legacy capability,不再直接 import `@api/legacy/presenters`
-- `scripts/architecture-guard.mjs` 已补 `renderer-retired-legacy-entry` 与 quarantine `<= 3` source files gate,稳定阻止 shim 回流和 quarantine 膨胀
-- 未使用的 convenience exports 已从 `src/renderer/api/legacy/presenters.ts` 删除,剩余 legacy presenter helper metric 已降到 `renderer.usePresenter = renderer.quarantine.usePresenter = 1`
-- architecture baseline / scoreboard 已刷新,`P5` gate 现为 `ready`:business legacy signal `0/0/0`,quarantine source files `3/3`
-- active docs 已补充 `P5` 最终状态与 quarantine 导航入口;剩余 quarantine 仅保留 `presenters.ts`、`presenterTransport.ts`、`runtime.ts`
-
-交付物:
-
-- `useLegacyPresenter()` internal-only 或完全删除
-- `window.electron` / `window.api` 只存在于文档明确列出的 bridge / runtime wrapper
-- 刷新基线、任务状态、代码导航和 onboarding 文档
-- 最终 merge gate checklist
-
-退出条件:
-
-- 业务层 single-track 达成
-- quarantine 范围可审计且足够小,或已经清零
-- reviewer 可以不靠口头说明,只看文档和 guard 就判定合规性
-
-## Verification Strategy
-
-所有 phase 都同步执行:
-
-- guard 校验
-- baseline 报告刷新
-- typed client / typed event 单测
-- 关键 store / page 回归测试
-- 迁移 slice 的 smoke 验证
-
-重点不是“迁了多少文件”,而是:
-
-- 有没有把错误入口真正封死
-- 有没有把业务层 transport 真的收成一条
-
-## Parallel Work Policy
-
-`P0` 与 `P1` 必须串行执行。
-
-原因很简单:
-
-- `P0` 固定规则、guard 和 quarantine 目录
-- `P1` 固定 transport helper 与 quarantine 收口方式
-
-这两步没锁定之前,并行切 family 只会制造第二套约定。
-
-`P2`、`P3`、`P4` 允许并行,但只能按 capability family 并行,且必须满足:
-
-1. 每个 family 有唯一 owner
-2. 并行 PR 不得同时修改 `architecture-guard`、baseline schema、shared transport primitive
-3. 并行 PR 不得争用同一个 typed route / typed event contract 文件
-4. 每个 PR 必须声明自己负责的 capability family 和禁止触碰范围
-
-推荐并行粒度:
-
-- 一条 PR = 一个 capability family
-- 一条 PR 同时完成 routes/contracts + client + store cutover + raw listener cleanup
-
-## Final Merge Gate
-
-该分支进入主线前,至少满足:
-
-1. `src/renderer/src/**` direct legacy presenter helper import = `0`
-2. `src/renderer/src/**` direct `window.electron` access = `0`
-3. `src/renderer/src/**` direct `window.api` access = `0`
-4. `useIpcQuery` / `useIpcMutation` 不再依赖 presenter reflection
-5. active docs 已经把 typed client / typed event 写成默认路径
-6. `architecture-guard` 可以稳定阻止回退
-
-## Risk Notes
-
-- 最大风险不是迁不完,而是“表面建了 client,业务层还是继续碰 legacy transport”。
-- 第二类风险是把 single-track 做成“把 86 个点一个个改名”,却没有建立 quarantine 和 merge gate。
-- 第三类风险是过于强调一次性清零,反而不先建立 guard,导致迁移中途继续长新 legacy 点。
-
-因此,本计划的顺序是:
-
-1. 先锁规则
-2. 再锁 transport helper
-3. 再按 family 清理
-4. 最后再 merge
-
diff --git a/docs/architecture/renderer-main-single-track/spec.md b/docs/architecture/renderer-main-single-track/spec.md
deleted file mode 100644
index d6af0e21f..000000000
--- a/docs/architecture/renderer-main-single-track/spec.md
+++ /dev/null
@@ -1,198 +0,0 @@
-# Renderer-Main Single Track
-
-## Summary
-
-`2026-04-20` main kernel refactor `phase5` 收口之后,项目已经完成了“边界稳定化 + 热路径减耦”的第一轮目标,
-但 renderer 侧仍然处于明显的双轨状态:
-
-- 一部分能力已经通过 `renderer/api/*Client` + `window.deepchat` + shared contracts 进入 main
-- 另一部分能力仍然通过 `useLegacyPresenter()`、`window.electron`、`window.api` 直接触达旧兼容面
-- 同一个 store / page / composable 内同时混用两种 transport 和两套 owner 语义
-
-这会直接导致后续开发者在实现功能时继续复制“typed client + legacy presenter + raw IPC”混搭模式,
-从而让这次重构的收益逐步回退。
-
-本计划的目标不是再发起一次全量 `main kernel rewrite`,而是把 renderer-main 边界正式收束成单轨模型,
-让后续开发只有一条默认正确路径可走。
-
-## Why This Program Exists
-
-`2026-04-20` 的当前基线仍然显示:
-
-| Signal | Baseline | Meaning |
-| --- | --- | --- |
-| `renderer.usePresenter.count` | `86` | renderer 业务代码仍直接依赖 presenter naming |
-| `renderer.windowElectron.count` | `95` | renderer 业务代码仍直接感知 Electron IPC |
-| `renderer.windowApi.count` | `33` | preload legacy multi-entry surface 仍暴露在业务层 |
-
-这些数字说明当前分支已经“可运行”,但还没有“可长期维护”。
-
-说明:`renderer.usePresenter.count` 保留旧 metric id,用于连续追踪当前
-`useLegacyPresenter()` 与其他 legacy presenter helper 的剩余触点。
-
-如果现在在双轨状态下直接合并,后续新功能大概率会继续沿着旧路径长,最终形成:
-
-- typed client 继续存在
-- `useLegacyPresenter()` 继续存在
-- raw `window.electron` / `window.api` 继续存在
-- 文档、测试和代码审查标准继续模糊
-
-这正是本计划要阻止的结果。
-
-## Goals
-
-- 让 `src/renderer/src/**` 的业务代码只面对单一、明确、可追踪的 renderer-main 能力入口。
-- 把 renderer-main 能力边界定义为:
- - typed route contracts
- - typed event contracts
- - `renderer/api/*Client`
- - 明确命名的 runtime wrapper
-- 把 `useLegacyPresenter()` 从“通用开发入口”降级为“迁移期间的内部兼容工具”,并最终退出业务层。
-- 把 `window.electron` / `window.api` 从业务层清退到极小、可审计、可删除的 wrapper / adapter 范围。
-- 把“新功能如何接入 main”这件事写成强规则,而不是口头共识。
-
-## Non-Goals
-
-- 不再发起一次新的全量 `main kernel` 目录重写。
-- 不要求本轮删除所有 main presenters。
-- 不要求本轮重写全部 EventBus 或全部 preload 兼容层。
-- 不要求所有 legacy 能力在第一阶段立刻消失。
-- 不以“单纯把指标数字降到 0”作为唯一目标;更关键的是消除业务层双轨。
-
-## User Stories
-
-- 作为后续功能开发者,我只需要沿着一条默认边界接入 main,而不是先猜“这个能力该走 client、presenter 还是 raw IPC”。
-- 作为 reviewer,我可以明确判断某个 PR 是否引入了错误 transport,而不是接受“先这样,后面再迁”。
-- 作为维护者,我可以从文档和 guard 直接看出哪些路径已经是 single-track,哪些还处于 quarantine。
-- 作为测试编写者,我可以围绕 typed client、typed event 和 domain adapter 写测试,而不是围绕 presenter 名称反射写测试。
-
-## In Scope
-
-- `src/renderer/src/**` 的 renderer-main 调用方式
-- `renderer/api/*Client`
-- typed route / event contract 的继续扩展
-- `useLegacyPresenter()` / `window.electron` / `window.api` 的 quarantine 与退役路径
-- guard、baseline、文档和 merge gate
-- 现有 store / page / composable 的 transport 单轨化
-
-## Out of Scope
-
-- main 内部 presenter 是否全部退役
-- main 内部所有 service / port / adapter 的再次大改名
-- 全量 EventBus 重写
-- 不相关的 UI redesign
-- 与单轨化无关的性能优化
-
-## Definition Of Single Track
-
-单轨化在本计划中有明确含义,不是“尽量多用 typed client”。
-
-### 1. Single Public Surface Per Capability
-
-同一个 renderer capability 只能有一个公开入口。
-
-例子:
-
-- provider 查询 / 校验统一走 `ProviderClient`
-- session 创建 / 激活 / 删除 / 导出统一走 `SessionClient` 的扩展能力或同域 typed client
-- window / device / workspace 相关能力统一走对应 typed client 或 runtime wrapper
-
-### 2. No Mixed Transport In Business Modules
-
-同一个 `src/renderer/src/**` 模块内,不允许同时出现:
-
-- typed client
-- `useLegacyPresenter()`
-- raw `window.electron`
-- raw `window.api`
-
-如果某个能力还没迁完,也必须先收口到单独的 adapter,而不是继续在业务模块里混用。
-
-### 3. Legacy Quarantine Only
-
-legacy transport 只允许存在于显式 quarantine 区域。
-
-唯一允许的 quarantine 路径固定为:
-
-- `src/renderer/api/legacy/**`
-
-业务代码不能直接 import / 调用 legacy transport helper。
-
-### 4. Typed-First For New Work
-
-后续新增 renderer-main 能力时,必须先定义:
-
-- route contract 或 event contract
-- 对应 client / runtime wrapper
-- 对应测试
-
-不允许新增 “先走 `useLegacyPresenter()`,以后再迁” 的临时实现。
-
-## Acceptance Criteria
-
-- `src/renderer/src/**` 业务模块中直接 import `@api/legacy/presenters` 的数量降为 `0`。
-- `src/renderer/src/**` 业务模块中 direct `window.electron` 的数量降为 `0`,仅允许文档明确列出的 bridge / runtime wrapper 保留。
-- `src/renderer/src/**` 业务模块中 direct `window.api` 的数量降为 `0`,仅允许文档明确列出的 bridge / runtime wrapper 保留。
-- `useIpcQuery` / `useIpcMutation` 不再建立在 presenter-name / method-name reflection 之上,或被更明确的 typed helper 替代。
-- 已迁移 slice 的业务模块中不再混用 typed client 和 legacy transport。
-- `scripts/architecture-guard.mjs` 能按“业务层禁用 + quarantine 白名单”而不是单纯“防增长”执行检查。
-- `docs/README.md`、`docs/ARCHITECTURE.md`、`docs/spec-driven-dev.md`、`docs/guides/getting-started.md` 等高频入口明确声明 single-track 规则。
-- 合并前存在一份清晰的 merge gate,能让 reviewer 判定“是否允许进入主线”。
-
-## Phase Gates
-
-为了避免 merge gate 只剩最终口号,本计划为每个阶段定义中间达标线:
-
-| Phase | Gate |
-| --- | --- |
-| `P0` | quarantine 路径、guard 规则、baseline 维度和 merge gate 已固定成文档与脚本任务 |
-| `P1` | `src/renderer/src/**` direct import `@api/legacy/presenters` = `0`,业务层 direct `window.electron` / `window.api` 新增点 = `0`,legacy transport 已收口到 `src/renderer/api/legacy/**` 或 typed runtime wrapper |
-| `P2` | business layer `configPresenter` hits = `0`,business layer `llmproviderPresenter` hits = `0`,config/provider/model family 的 raw event listeners 清零 |
-| `P3` | business layer `windowPresenter` / `devicePresenter` / `workspacePresenter` / `projectPresenter` / `filePresenter` / `yoBrowserPresenter` / `tabPresenter` hits = `0` |
-| `P4` | business layer remaining presenter family hits = `0`,包括 `agentSessionPresenter` / `skillPresenter` / `mcpPresenter` / `syncPresenter` / `upgradePresenter` / `dialogPresenter` / `toolPresenter` 等 |
-| `P5` | `src/renderer/src/**` business layer direct legacy presenter helper / direct `window.electron` / direct `window.api` 全部为 `0`,quarantine 目录为空或满足量化退出标准 |
-
-`2026-04-20` 进度更新:P3 已完成。window / device / workspace / project / file / browser / tab family 已完成 typed contract、typed event、typed client cutover;业务层 P3 presenter hits 已清零,window/window-tab raw IPC 业务直连已清零,相关定向测试与 `format/i18n/lint/typecheck` 已通过。
-`2026-04-20` 审计备注:`WelcomePage.vue` 与 `NewThreadPage.vue` 已在 P3 范围内完成复核,前者无需变更,后者仅保留 `agentSessionPresenter` 的 P4 residual 调用,不再阻塞 P3 gate。
-`2026-04-20` 进度更新:P4 已完成。session residual / skill / mcp / sync / upgrade / dialog / tool family 已完成 typed contract、typed event、typed client cutover;业务层 P4 presenter hits 已清零,相关 raw listeners 已切换到 typed event subscription,且定向 main/renderer 自动回归与 `format/i18n/lint/typecheck` 已通过。
-`2026-04-20` 进度更新:P5 已完成。旧的通用 `usePresenter()` naming 已退役;remaining legacy presenter entry 仅保留在 `src/renderer/api/legacy/presenters.ts`,并通过明确命名的 quarantine helper / runtime wrapper 暴露给兼容路径。`2026-04-20` 补充清理后,未使用的 convenience exports 已从该入口移除,baseline 现显示 `renderer.business.usePresenter/windowElectron/windowApi = 0/0/0`、`renderer.usePresenter = renderer.quarantine.usePresenter = 1`,且 quarantine source files 满足 `3/3` 退出标准,`P5` gate 已转为 `ready`。
-
-## Success Metrics
-
-重点不只是总数下降,而是“业务层清零,兼容层收口”。
-
-至少跟踪:
-
-- `renderer.usePresenter.count`
-- `renderer.windowElectron.count`
-- `renderer.windowApi.count`
-- business-layer direct import / direct access count
-- quarantine-layer count
-- typed client / typed event 覆盖的 capability 数量
-
-## Quarantine Exit Standard
-
-本计划的理想终点是 quarantine 清零。
-
-如果在 `P5` 合并前仍因阻塞性兼容约束需要保留 quarantine,则必须同时满足:
-
-- `src/renderer/src/**` 业务层 direct legacy access 仍为 `0`
-- `src/renderer/api/legacy/**` 文件数 `<= 3`
-- 剩余 quarantine 只允许覆盖 `<= 1` 个 capability family
-- 每个剩余文件都必须在 `tasks.md` 或后续 follow-up 规格中写明 owner、删除条件和最晚退出阶段
-
-如果做不到以上条件,则不满足 single-track merge gate。
-
-`2026-04-20` P5 审计结果:
-
-- 剩余 quarantine source files 为 `presenters.ts`、`presenterTransport.ts`、`runtime.ts`
-- 剩余 quarantine capability family 为单一的 `renderer legacy transport`
-- 删除条件:settings compatibility surfaces 不再 import `@api/legacy/presenters` / `@api/legacy/runtime`
-
-## Open Questions
-
-当前无阻塞性 `[NEEDS CLARIFICATION]` 项。
-
-实现层面的命名差异,例如某些能力最终是扩展现有 `Client` 还是拆成新 `Client`,
-由 `plan.md` 在不破坏 single-track 原则的前提下决定。
-
diff --git a/docs/architecture/renderer-main-single-track/tasks.md b/docs/architecture/renderer-main-single-track/tasks.md
deleted file mode 100644
index 7ae271b6d..000000000
--- a/docs/architecture/renderer-main-single-track/tasks.md
+++ /dev/null
@@ -1,122 +0,0 @@
-# Renderer-Main Single Track Tasks
-
-## Program Setup
-
-- [x] 新建 `docs/architecture/renderer-main-single-track/spec.md`
-- [x] 新建 `docs/architecture/renderer-main-single-track/plan.md`
-- [x] 新建 `docs/architecture/renderer-main-single-track/tasks.md`
-- [x] 在 `docs/README.md` 增加 single-track 计划入口
-- [x] 在 `docs/ARCHITECTURE.md` 增加 `phase5` 之后的执行规则
-- [x] 更新 `docs/spec-driven-dev.md`,把 renderer-main 推荐模式从 `useLegacyPresenter()` 改为 typed client
-- [x] 更新 `docs/guides/getting-started.md`,把 onboarding 心智模型改为 typed boundary first
-
-## P0: Rules & Guard Hardening
-
-- [x] 定义业务层 / typed boundary / quarantine 三层目录规则
-- [x] 固定唯一 quarantine 目录为 `src/renderer/api/legacy/**`
-- [x] 在仓库中实际创建 `src/renderer/api/legacy/` 目录与说明文件或首个 adapter
-- [x] 为 `scripts/architecture-guard.mjs` 增加 business-layer direct legacy presenter helper 禁止规则
-- [x] 为 `scripts/architecture-guard.mjs` 增加 business-layer direct `window.electron` 禁止规则
-- [x] 为 `scripts/architecture-guard.mjs` 增加 business-layer direct `window.api` 禁止规则
-- [x] 为 `scripts/generate-architecture-baseline.mjs` 增加 business-layer / quarantine-layer 分维度统计
-- [x] 定义 single-track merge gate
-- [x] 定义阶段性 phase gate 指标并写入 baseline / guard 说明
-
-## P1: Transport Consolidation
-
-- [x] 依赖 P0 已固定 `src/renderer/api/legacy/**` 后再开始本阶段
-- [x] 把 `useLegacyPresenter()` 降级为 quarantine-only utility
-- [x] 在 renderer 层建立显式 legacy quarantine adapter 目录
-- [x] 重写或退役 `useIpcQuery`
-- [x] 重写或退役 `useIpcMutation`
-- [x] 收口 `window.electron` / `window.api` 的 runtime wrapper
-- [x] 清理 `src/renderer/src/**` 中对 transport primitive 的直接 import
-- [x] 为 transport consolidation 补验证:业务层 direct legacy presenter helper import = `0`
-- [x] 为 transport consolidation 补验证:业务层 mixed transport module = `0`
-
-## P2: Config / Provider / Model Family
-
-- [x] 扩展 `SettingsClient` 覆盖仍属于 settings/config 域的基础读写
-- [x] 为 provider / model / config 能力补 typed contracts
-- [x] 为 provider / model / config family 补 typed event contracts
-- [x] 为 provider / model / config 能力补 typed clients
-- [x] 迁移 `providerStore.ts`
-- [x] 迁移 `modelStore.ts`
-- [x] 迁移 `modelConfigStore.ts`
-- [x] 迁移 `systemPromptStore.ts`
-- [x] 迁移 `theme.ts`
-- [x] 迁移 `language.ts`
-- [x] 迁移 `floatingButton.ts`
-- [x] 迁移 `shortcutKey.ts`
-- [x] 迁移 `agentModelStore.ts`
-- [x] 清理 config/provider/model family 的 raw event listeners
-
-`2026-04-20` 进度更新:P2 已完成,相关 renderer/main 定向测试、`typecheck`、`format`、`i18n`、`lint` 已通过。
-`2026-04-20` 验收修复:补齐 `config.resolveDeepChatAgentConfig` 的 nullable agent config contract 兼容,自动回归已重新通过。
-
-## P3: Window / Device / Workspace Family
-
-- [x] 为 window / device / workspace / project / file / browser / tab 能力补 typed clients 或 runtime wrappers
-- [x] 为 window / device / workspace / project / file / browser / tab family 补 typed event contracts
-- [x] 迁移 `App.vue`
-- [x] 迁移 `AppBar.vue`
-- [x] 迁移 `WelcomePage.vue`
-- [x] 迁移 `NewThreadPage.vue`
-- [x] 迁移 `stores/ui/project.ts`
-- [x] 迁移 workspace/browser 相关组件与 composables
-- [x] 清理 window/device/workspace family 的 raw event listeners
-
-`2026-04-20` 进度更新:P3 已完成。新增 `WindowClient`、`DeviceClient`、`WorkspaceClient`、`ProjectClient`、`FileClient`、`BrowserClient`、`TabClient` 与 `src/renderer/api/runtime.ts`,并完成 window / workspace / browser typed event cutover。
-`2026-04-20` 范围备注:`WelcomePage.vue` 为已满足 P3 gate 的既有 typed path,无需改动;`NewThreadPage.vue` 的 P3 范围已完成审计,剩余 legacy 调用仅属于 P4 的 `agentSessionPresenter` residual。
-`2026-04-20` 自动回归:`pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts test/main/presenter/workspacePresenter.test.ts` 与 `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/api/clients.test.ts test/renderer/stores/projectStore.test.ts test/renderer/components/WorkspacePanel.test.ts test/renderer/components/WindowSideBar.test.ts test/renderer/stores/spotlight.test.ts test/renderer/components/SvgArtifact.test.ts test/renderer/components/AgentWelcomePage.test.ts test/renderer/components/WelcomePage.test.ts test/renderer/components/NewThreadPage.test.ts test/renderer/pages/NewThreadPage.test.ts` 已通过。
-`2026-04-20` 静态检查:`pnpm run format`、`pnpm run i18n`、`pnpm run lint`、`pnpm run typecheck` 已通过。
-
-## P4: Session Residual / MCP / Skill / Misc Family
-
-- [x] 扩展 `SessionClient` 覆盖 rename / delete / export / pending input / session setting 类动作
-- [x] 为 skill / mcp / sync / upgrade / dialog 等能力补 typed contracts
-- [x] 为 skill / mcp / sync / upgrade / dialog 等 family 补 typed event contracts
-- [x] 为 skill / mcp / sync / upgrade / dialog 等能力补 typed clients
-- [x] 迁移 `stores/ui/session.ts` 的 residual presenter calls
-- [x] 迁移 `stores/ui/pendingInput.ts`
-- [x] 迁移 `stores/skillsStore.ts`
-- [x] 迁移 `stores/mcp.ts`
-- [x] 迁移 `stores/mcpSampling.ts`
-- [x] 迁移 `stores/sync.ts`
-- [x] 迁移 `stores/upgrade.ts`
-- [x] 迁移 `stores/dialog.ts`
-- [x] 迁移 `stores/ollamaStore.ts`
-- [x] 清理 residual family 的 raw event listeners
-
-`2026-04-20` 进度更新:P4 已完成。已补齐 session residual / skill / mcp / sync / upgrade / dialog / tool family 的 typed route 与 typed event contracts、typed clients,以及 main route dispatcher/runtime 接线。
-`2026-04-20` 迁移结果:`src/renderer/src/**` 内 `agentSessionPresenter` / `skillPresenter` / `mcpPresenter` / `syncPresenter` / `upgradePresenter` / `dialogPresenter` / `toolPresenter` 的 P4 业务命中已清零,相关 raw event listeners 已切到 typed subscriptions。
-`2026-04-20` 自动回归:`pnpm run format`、`pnpm run i18n`、`pnpm run lint`、`pnpm run typecheck` 已通过;`pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts test/main/presenter/upgradePresenter.test.ts test/main/presenter/mcpPresenter.test.ts test/main/presenter/mcpPresenter/toolManager.test.ts test/main/presenter/agentRuntimePresenter/pendingInputStore.test.ts test/main/routes/sessionService.test.ts` 与 `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/api/clients.test.ts test/renderer/stores/mcpStore.test.ts test/renderer/stores/mcpSampling.test.ts test/renderer/stores/upgradeStore.test.ts test/renderer/stores/pendingInputStore.test.ts test/renderer/stores/sessionStore.test.ts test/renderer/stores/ollamaStore.test.ts test/renderer/pages/NewThreadPage.test.ts test/renderer/components/ChatStatusBar.test.ts test/renderer/components/message/MessageBlockContent.test.ts test/renderer/components/MarkdownRenderer.test.ts test/renderer/components/TranslatePopup.test.ts test/renderer/components/trace/TraceDialog.test.ts test/renderer/components/McpIndicator.test.ts test/renderer/components/ChatPage.test.ts` 已通过。
-
-## P5: Retirement & Merge Gate
-
-- [x] 清理 `src/renderer/src/**` 剩余 direct legacy presenter helper import
-- [x] 清理 `src/renderer/src/**` 剩余 direct `window.electron` access
-- [x] 清理 `src/renderer/src/**` 剩余 direct `window.api` access
-- [x] 将 `useLegacyPresenter()` internal-only 或删除
-- [x] 更新 `docs/README.md` / `docs/ARCHITECTURE.md` / `docs/guides/code-navigation.md` 的最终状态
-- [x] 修正文档 / 测试 / guard / baseline 中残留的旧 `usePresenter` 命名与扫描规则
-- [x] 刷新 architecture baseline / scoreboard
-- [x] 运行 `pnpm run format`
-- [x] 运行 `pnpm run i18n`
-- [x] 运行 `pnpm run lint`
-- [x] 跑针对性 renderer/main 测试并记录结果
-
-`2026-04-20` 进度更新:P5 已完成。旧的通用 `usePresenter()` naming 已删除;settings compatibility surfaces 继续从 `src/renderer/api/legacy/presenters.ts` 获取 quarantine-only legacy presenter entry,业务层 residual capability 则改为通过显式 runtime wrapper 访问。
-`2026-04-20` merge gate 更新:`scripts/architecture-guard.mjs` 已新增 retired shim guard 与 quarantine `<= 3` source files 限制;`docs/architecture/baselines/main-kernel-boundary-baseline.md` 现显示 `P5` gate = `ready`,business legacy signal = `0/0/0`,quarantine source files = `3/3`。
-`2026-04-20` 文档刷新:已移除 `src/renderer/api/legacy/presenters.ts` 中未使用的 convenience exports,并重跑 `pnpm run architecture:baseline`;当前 baseline 已更新为 `renderer.usePresenter = renderer.quarantine.usePresenter = 1`。
-`2026-04-20` quarantine 审计:剩余文件固定为 `src/renderer/api/legacy/presenters.ts`、`src/renderer/api/legacy/presenterTransport.ts`、`src/renderer/api/legacy/runtime.ts`;owner 为 renderer legacy transport quarantine,删除条件为 settings compatibility surfaces 不再 import `@api/legacy/presenters` / `@api/legacy/runtime`。
-`2026-04-20` 自动回归:`pnpm run format`、`pnpm run i18n`、`pnpm run lint`、`pnpm run typecheck` 已通过;`pnpm exec vitest --config vitest.config.renderer.ts test/renderer/composables/useLegacyPresenter.test.ts test/renderer/components/SettingsApp.test.ts test/renderer/components/SettingsApp.providerDeeplink.test.ts test/renderer/components/RemoteSettings.test.ts` 与 `pnpm exec vitest --config vitest.config.ts test/main/routes/contracts.test.ts test/main/routes/dispatcher.test.ts` 已通过。
-`2026-04-20` 全量套件尝试:`pnpm test` 当前仍失败,失败面为既有 renderer 测试问题,包含聚合运行中的 `window.deepchat is not available` 测试环境/串扰错误,以及独立存在的 `test/renderer/components/BrowserPanel.test.ts` 断言失败;单独重跑 `pnpm exec vitest --config vitest.config.renderer.ts test/renderer/pages/NewThreadPage.test.ts` 已通过,说明至少 `NewThreadPage` 这类 `window.deepchat` 失败并非本次 P5 改动引入。
-
-## Final Checklist
-
-- [x] renderer 业务层 single-track 达成
-- [x] quarantine 范围明确、可审计
-- [x] 新功能接入规则写入 active docs
-- [x] reviewer 无需口头背景即可判定是否合规
-
diff --git a/docs/architecture/session-management.md b/docs/architecture/session-management.md
index 375a2a961..69e7dd4cc 100644
--- a/docs/architecture/session-management.md
+++ b/docs/architecture/session-management.md
@@ -80,3 +80,14 @@ sequenceDiagram
- renderer `messageStore` 首屏只加载第一页,`ChatPage` 在接近顶部时再拉旧历史
这样可以让大会话恢复保持稳定首屏时间,也把“历史很长”和“首屏可用”两个目标解耦开。
+
+## 当前会话能力
+
+- 会话列表支持 lightweight 分页、按 agent/project/subagent 过滤、固定字母排序和置顶优先。
+- `generationSettings` 保存在 session runtime 中,renderer 可通过 `sessions.getGenerationSettings`
+ / `sessions.updateGenerationSettings` 读取和更新。
+- `sessions.compact` 提供手动上下文压缩;自动压缩默认值来自 agent/settings 配置。
+- `sessions.listMessageTraces` 提供消息 trace 查询,不再把 trace 混在消息正文里。
+- `sessions.searchHistory` 通过结构化搜索文档表优先走 FTS5,失败时回退 `LIKE`。
+- Subagent session 与普通 session 共用表结构,但通过 `sessionKind`、`parentSessionId`、
+ `subagentMeta` 区分生命周期和展示。
diff --git a/docs/architecture/skill-runtime-hardening/plan.md b/docs/architecture/skill-runtime-hardening/plan.md
deleted file mode 100644
index 1d227471c..000000000
--- a/docs/architecture/skill-runtime-hardening/plan.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Skill Runtime Hardening Plan
-
-## Data Model
-- Add `SkillExtensionConfig` with `version`, `env`, `runtimePolicy`, and `scriptOverrides`.
-- Add `SkillScriptDescriptor` generated from `scripts/**/*.{py,js,mjs,cjs,sh}` and merged with sidecar overrides.
-- Store sidecars in `/.deepchat-meta/.json`.
-
-## Runtime Flow
-- `SkillPresenter` owns sidecar read/write and script discovery.
-- `SkillExecutionService` validates active skill access, resolves scripts, merges env, selects runtime, and executes scripts.
-- `skill_run` becomes the preferred skill-local execution entrypoint.
-
-## Read Guardrails
-- Add shared binary-read helpers for ACP and main agent reads.
-- ACP rejects non-text files through `fs/read_text_file`.
-- Main agent keeps image OCR fallback, but rejects unsupported binary reads with guidance.
-
-## Process Output Reliability
-- Move foreground and background completion semantics from `exit` to `close`.
-- Await output flush before returning completed process results.
-- Offload large foreground output to session files when possible.
-
-## UI
-- Extend the skill editor with runtime policy, env rows, and discovered scripts.
-- Show script/env/runtime summary badges on skill cards.
-
-## Tests
-- Presenter: sidecar lifecycle, script discovery, overwrite/uninstall behavior.
-- Runtime: active skill enforcement, runtime fallback, script path validation.
-- Agent tooling: prompt injection, `skill_run`, binary read rejection.
-- ACP: `read_text_file` binary rejection.
-- Process handling: `close`-based flush behavior.
diff --git a/docs/architecture/skill-runtime-hardening/spec.md b/docs/architecture/skill-runtime-hardening/spec.md
deleted file mode 100644
index fc6f156a2..000000000
--- a/docs/architecture/skill-runtime-hardening/spec.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Skill Runtime Hardening
-
-## Summary
-
-Add runtime-aware skill extensions without changing external `SKILL.md` formats. DeepChat stores skill-only runtime settings in a sidecar directory and uses them to execute bundled scripts safely and predictably.
-
-## User Stories
-
-### US-1: Configure skill environment variables
-- As a user, I can define env vars for a skill in Settings.
-- Acceptance:
- - Env vars are stored outside the skill folder as plaintext sidecar metadata.
- - Editing a skill does not rewrite external `SKILL.md` frontmatter to persist env vars.
-
-### US-2: Run skill scripts reliably
-- As an agent, I can run scripts bundled in an active skill without guessing relative paths.
-- Acceptance:
- - Only scripts under `/scripts/` can be executed.
- - The runtime picks system `uv`/`node` first, then falls back to DeepChat bundled runtimes.
- - Skill scripts execute from the current session workdir when available, while Python still honors `pyproject.toml` via `uv run --project `.
-
-### US-3: Prevent prompt pollution from binary file reads
-- As an agent, reading an image or binary file through text-file APIs does not inject raw bytes into prompt context.
-- Acceptance:
- - ACP `fs/read_text_file` rejects image/PDF/common binary files with remediation guidance.
- - Main agent `read` keeps image OCR fallback, but rejects unsupported binary formats instead of returning raw bytes.
-
-### US-4: Guide the model toward stable skill execution
-- As an agent, active skill instructions clearly include absolute paths, script inventory, and the preferred execution tool.
-- Acceptance:
- - Active skill prompt includes `skillRoot`, skill root env vars, recommended `base_directory`, runnable scripts, and explicit guardrails against inline `python -c` / `node -e`.
-
-### US-5: Keep process output after process exit
-- As an agent, command output is still available when a child process writes a large payload right before exiting.
-- Acceptance:
- - Foreground exec waits for child `close`.
- - Background exec sessions are considered complete only after `close` and log flush.
- - Large foreground output is offloaded to a session log file instead of being silently truncated away.
-
-## Non-Goals
-- Secret encryption or OS keychain storage for skill env vars.
-- Extending external skill formats with DeepChat-only frontmatter fields.
-- General workflow orchestration across multiple skills.
-
-## Constraints
-- Keep existing skills compatible.
-- Ignore `.deepchat-meta` in skill discovery and sync/export flows.
-- Reuse the existing `process` tool for background session management.
diff --git a/docs/architecture/skill-runtime-hardening/tasks.md b/docs/architecture/skill-runtime-hardening/tasks.md
deleted file mode 100644
index 2beb3394a..000000000
--- a/docs/architecture/skill-runtime-hardening/tasks.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Skill Runtime Hardening Tasks
-
-1. Add shared types for skill runtime config and script descriptors.
-2. Extend `SkillPresenter` with sidecar persistence and script discovery.
-3. Add `SkillExecutionService` and wire `skill_run` into agent tools.
-4. Harden binary read behavior in ACP and main agent reads.
-5. Switch exec completion logic from `exit` to `close` and await output flush.
-6. Extend skills settings UI to edit runtime config and show summaries.
-7. Add or update tests for presenter, ACP, agent tooling, and process output behavior.
diff --git a/docs/architecture/sqlite-database-encryption/plan.md b/docs/architecture/sqlite-database-encryption/plan.md
deleted file mode 100644
index 0cfd732c4..000000000
--- a/docs/architecture/sqlite-database-encryption/plan.md
+++ /dev/null
@@ -1,247 +0,0 @@
-# SQLite Database Encryption Plan
-
-## Architecture
-
-- Add a main-process database security layer responsible for encryption metadata, safeStorage
- wrapping, unlock, password validation, migration orchestration, and status reporting.
-- Store non-secret encryption metadata outside SQLite in a small ElectronStore file, for example
- `database-security.json`.
-- Keep `ConfigPresenter` startup behavior unchanged until SQLite is attached. After attach, route
- sensitive config keys through SQLite-backed storage.
-- Extend `ConfigTables` with generic app configuration storage for sensitive settings that do not
- need dedicated relational tables.
-- Keep splash as the only startup unlock UI. The main window is created only after DB unlock and DB
- initialization succeed.
-- Expose settings operations through typed route contracts and `SettingsClient` or a dedicated
- `DatabaseSecurityClient`.
-
-## Data Model
-
-Unencrypted metadata store:
-
-```ts
-type DatabaseSecurityMetadata = {
- version: 1
- enabled: boolean
- cipher: 'sqlcipher'
- passwordStorage: 'safeStorage' | 'manual' | 'none'
- wrappedPassword?: string
- safeStorageBackend?: string
- lastMigrationAt?: number
- lastMigrationDirection?: 'enable' | 'change-password' | 'disable'
-}
-```
-
-SQLite additions:
-
-- `app_settings`: `key TEXT PRIMARY KEY`, `value_json TEXT NOT NULL`, `sensitive INTEGER NOT NULL`,
- `updated_at INTEGER NOT NULL`.
-- Store migrated values under existing logical keys: `remoteControl`, `mcprouterApiKey`,
- `nowledgeMemConfig`, `hooksNotifications`, `knowledgeConfigs`, `customPrompts`, and
- `systemPrompts`.
-- Keep existing provider/model/MCP/agent SQLite tables unchanged.
-
-Password handling:
-
-- The user-provided SQLite password is the database password for v1.
-- If `safeStorage.isEncryptionAvailable()` is true, store only `safeStorage.encryptString(password)`
- as base64 in metadata. This uses the OS credential store, such as macOS Keychain, Windows
- Credential Vault, or a Linux secret store, through Electron.
-- If safeStorage is unavailable, do not persist the password; metadata records `passwordStorage:
- 'manual'`.
-- If a manual unlock succeeds later on a system where safeStorage is available, re-wrap the password
- for future launches. This covers restored/imported data where the encrypted database exists but
- the local OS credential store does not have a usable wrapped password.
-
-## Startup Unlock Flow
-
-1. `configInitHook` creates `ConfigPresenter` and applies startup settings as today.
-2. `databaseInitHook` creates or retrieves the database security service before `DatabaseInitializer`.
-3. If metadata says encryption is disabled, initialize SQLite without a password.
-4. If encryption is enabled and a wrapped password exists, try `safeStorage.decryptString()`.
-5. Validate the decrypted password by opening the database with the normal SQLite open helper and a
- lightweight schema query.
-6. If validation fails, if the local OS credential store entry is missing, or if safeStorage is
- unavailable, ask `SplashWindowManager` to enter unlock mode and await a password submission.
-7. On wrong password, return an unlock error to splash and keep waiting.
-8. On cancel, quit startup before main window creation.
-9. On success, pass the password into `DatabaseInitializer({ password })`.
-
-Splash unlock UI:
-
-```text
-+----------------------------------------+
-| DeepChat |
-| Local database is encrypted |
-| |
-| SQLite password |
-| [ ******************************** ] |
-| |
-| Wrong password. Try again. |
-| |
-| [ Unlock ] [ Quit ] |
-| |
-| System unlock is unavailable on this |
-| device, so manual unlock is required. |
-+----------------------------------------+
-```
-
-System unlock progress state:
-
-```text
-+----------------------------------------+
-| DeepChat |
-| Unlocking local database |
-| |
-| Use the system unlock prompt if one |
-| appears. |
-| |
-| Opening local database... |
-+----------------------------------------+
-```
-
-## SQLite Open And Validation
-
-- Keep `cipher='sqlcipher'` and `legacy=4` configured before applying the key so newly encrypted
- databases use SQLCipher 4 compatibility mode and can be opened by tools such as DB Browser for
- SQLite with SQLCipher 4 defaults.
-- Replace SQL string key interpolation with the native `db.key(Buffer.from(password, 'utf8'))` API
- or an equivalent parameter-safe binding path.
-- Validate encrypted opens with a read against `sqlite_master`, then use the existing transaction
- validation in `DatabaseInitializer`.
-- Ensure migration and unlock errors are normalized before reaching logs or renderer state.
-
-## Migration Flow
-
-Primary migration primitive:
-
-- Use one source connection plus an attached temporary target database, then copy normal schema
- tables and rows into the target.
-- `better-sqlite3-multiple-ciphers` backup cannot copy between incompatible encrypted/plaintext
- database states, and the current binding does not treat `VACUUM INTO` filenames as SQLite URI
- parameters. The attach/copy path is therefore the project-compatible migration primitive.
-- Apply target passwords through `ATTACH DATABASE ? AS migration_target KEY ?` binding parameters
- after configuring SQLCipher 4 compatibility mode, not by interpolating a password into SQL text.
- For encrypted-to-plaintext disable, attach the target with an explicit empty key.
-- Do not run native rekey against the active `agent.db`.
-
-Runtime migration sequence:
-
-1. Acquire a process-wide migration lock so settings, sync backup/import, and reset flows cannot run
- concurrently.
-2. Close the active `SQLitePresenter` connection.
-3. Open the source DB with the current password if encrypted.
-4. Run WAL checkpoint and switch the source connection to a non-WAL journal mode for the export.
-5. Create a temp destination DB in the same `app_db` directory by attaching it to the source
- connection.
-6. Copy normal tables and row data into the attached target. Skip FTS virtual/shadow tables; the
- existing table initialization rebuilds those derived search indexes after reopen.
-7. Open the temp DB through the normal SQLite helper using the target password.
-8. Run `PRAGMA quick_check`, schema version checks, and key table row count checks.
-9. Rename the active database to a short-lived rollback file, move the temp DB to `agent.db`, and
- remove sidecar files.
-10. Reopen `SQLitePresenter` with the target password and reattach SQLite-backed config stores.
-11. Update metadata only after reopen succeeds.
-12. Delete the rollback file after successful reopen; on failure, restore it and reopen the original
- DB.
-
-Status results returned to settings:
-
-```ts
-type DatabaseSecurityStatus = {
- enabled: boolean
- cipher: 'sqlcipher'
- safeStorageAvailable: boolean
- safeStorageBackend?: string
- passwordStorage: 'safeStorage' | 'manual' | 'none'
- manualUnlockRequired: boolean
- migrationInProgress: boolean
- lastMigrationAt?: number
-}
-```
-
-## Settings UI
-
-Add the section to the current Data settings page near privacy/data management controls.
-
-```text
-+------------------------------------------------------------------+
-| SQLite database encryption Enabled |
-| Protects local chat history, provider keys, MCP config, prompts, |
-| and other sensitive settings stored in agent.db. |
-| |
-| Cipher SQLCipher |
-| System unlock Available via OS secure storage |
-| Startup unlock System unlock |
-| Last migration 2026-05-22 14:30 |
-| |
-| Current password [ ************************ ] |
-| New password [ ************************ ] |
-| Confirm password [ ************************ ] |
-| |
-| [ Change password ] [ Disable encryption ] |
-+------------------------------------------------------------------+
-```
-
-Disabled state:
-
-```text
-+------------------------------------------------------------------+
-| SQLite database encryption Disabled |
-| |
-| System unlock Unavailable |
-| Startup unlock Manual password required after enabling |
-| |
-| New password [ ************************ ] |
-| Confirm password [ ************************ ] |
-| |
-| [ Enable encryption ] |
-| |
-| This system cannot use Electron safeStorage. DeepChat can still |
-| encrypt the database, but you must enter the password on every |
-| startup. |
-+------------------------------------------------------------------+
-```
-
-## Sensitive Config Migration
-
-- Add DB-backed read/write adapters for sensitive app settings keys after SQLite attach.
-- Migrate legacy values into `app_settings` once, guarded by a `config_migrations` marker such as
- `sensitive-config-sqlite-v1`.
-- After successful migration and verification, remove migrated sensitive keys from `app-settings`
- JSON and replace prompt/knowledge JSON stores with empty defaults or remove the files.
-- Keep non-sensitive startup settings in JSON.
-- Update sync backup filtering so migrated sensitive settings are not copied into JSON backup paths.
-- Import legacy backups by reading old JSON values and writing them into SQLite when config storage
- migration is active.
-
-## Failure Modes
-
-- SafeStorage unavailable: allow encryption, set metadata to manual mode, show manual prompt on
- startup.
-- SafeStorage decrypt fails: show manual splash unlock; if manual unlock succeeds and safeStorage is
- available, re-wrap the password.
-- Wrong password: keep splash open and do not initialize SQLite presenters.
-- Migration fails before replacement: delete temp DB and reopen original DB.
-- Migration fails after replacement: restore rollback DB and old metadata.
-- App exits during migration: next startup detects temp/rollback files, restores the last complete
- database, and asks the user to retry migration.
-
-## Testing
-
-- Main tests cover safeStorage support states, password wrapping, unlock success/failure/cancel,
- migration success/rollback, WAL cleanup, and metadata updates.
-- Main integration tests use temp databases for plaintext-to-encrypted, encrypted password change,
- encrypted-to-plaintext, and legacy sensitive config migration.
-- Renderer tests cover Data settings states, validation errors, action disabling during migration,
- and splash unlock states.
-- Manual QA covers macOS system unlock, Linux/manual unlock fallback, wrong password retry, cancel
- behavior, sync backup/import, and reset flows.
-
-## Quality Gates
-
-- `pnpm run format`
-- `pnpm run i18n`
-- `pnpm run lint`
-- `pnpm run typecheck`
-- Focused Vitest suites for main encryption/migration and renderer settings/splash behavior.
diff --git a/docs/architecture/sqlite-database-encryption/spec.md b/docs/architecture/sqlite-database-encryption/spec.md
deleted file mode 100644
index 0828e5dc6..000000000
--- a/docs/architecture/sqlite-database-encryption/spec.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# SQLite Database Encryption
-
-## User Story
-
-DeepChat users can store provider credentials, remote-control tokens, prompts, knowledge
-configuration, and conversation history locally. Users need an optional database encryption mode so
-the main SQLite database is encrypted at rest, can be unlocked during startup, and can be migrated
-safely when the SQLite password changes.
-
-## Goals
-
-- Let users enable, change, and disable SQLite database encryption from the data settings screen.
-- Require an unlock step before any SQLite-backed presenter opens an encrypted `agent.db`.
-- Use Electron `safeStorage` to store the SQLite password when available.
-- Fall back to manual unlock on every startup when `safeStorage` is unavailable or cannot decrypt.
-- Migrate the existing database by writing a new database file, verifying it, and replacing the old
- file only after successful validation.
-- Move remaining sensitive JSON/ElectronStore configuration into SQLite so encryption covers it.
-
-## Acceptance Criteria
-
-- A new data settings section shows database encryption state, selected cipher, safeStorage support,
- last migration time, and whether manual startup unlock is required.
-- Enabling encryption requires a non-empty SQLite password and confirmation. On success, `agent.db`
- is replaced by an encrypted database and opens only with that password.
-- Changing the password requires the current password, writes a new encrypted database using the new
- password, verifies the result, then replaces the active database.
-- Disabling encryption requires the current password, writes a verified plaintext database, then
- replaces the active encrypted database.
-- A failed migration leaves the original database and sidecar files usable.
-- Successful migration removes temporary files and old `agent.db` sidecar files (`-wal`, `-shm`).
-- Startup does not initialize SQLite-backed presenters until the encrypted database is unlocked.
-- When `safeStorage` decrypts the stored password successfully, startup proceeds without showing a
- manual password prompt. In normal use this means the user unlocks once, and later launches open
- automatically from the OS credential store.
-- When `safeStorage` is unavailable, the OS credential store entry is missing, or decryption fails
- after imported data is restored on a different device, the splash window shows an unlock form.
- After a successful manual unlock on a system with safeStorage, DeepChat stores a fresh wrapped
- password for future launches.
-- Wrong passwords keep the user on the splash unlock form and do not open the main window.
-- Canceling unlock exits startup without creating the main window.
-- User-facing strings are localized through the renderer i18n system.
-- No SQLite password or derived key is written to logs, route activity, migration errors, telemetry,
- or renderer-visible state.
-- Legacy plaintext config copies for migrated sensitive keys are removed or redacted after successful
- SQLite migration.
-
-## Data To Encrypt
-
-Already covered once `agent.db` is encrypted:
-
-- Conversations, messages, tool traces, assistant blocks, pending inputs, usage stats, search
- documents, search indexes, attachment metadata, projects, sessions, and agents.
-- Provider credentials and provider metadata already stored in SQLite, including API keys, OAuth
- tokens, AWS Bedrock secrets, and Vertex credentials.
-- MCP server config, MCP env/custom headers, MCP settings, agent settings, and agent MCP selections
- already stored in SQLite.
-
-Move into SQLite in this feature:
-
-- `remoteControl`: Telegram/Discord bot tokens, Feishu/Lark app secrets, verification tokens,
- encrypt keys, QQBot client secrets, Weixin iLink bot tokens, account state, and bindings.
-- `mcprouterApiKey`.
-- `nowledgeMemConfig`, including `apiKey`.
-- `hooksNotifications`, especially hook commands and webhook-like values.
-- `custom_prompts` and `system_prompts`.
-- `knowledge-configs`, including RAGFlow, Dify, FastGPT, and built-in knowledge config metadata.
-- Legacy provider/model/MCP keys that remain in plaintext JSON after the previous config-to-SQLite
- migration.
-
-Keep outside encrypted SQLite for startup:
-
-- Language, theme, logging, proxy mode, sync folder path, update channel, window/startup flags, and
- other settings needed before the database is opened.
-- `customProxyUrl` stays in startup config for compatibility, but credentials embedded in proxy URLs
- should be rejected or split into encrypted storage in a follow-up.
-
-## Non-Goals
-
-- No cloud key backup or password recovery.
-- No guaranteed secure deletion on SSDs or filesystems with snapshots.
-- No encryption of external files already exported by the user, old sync backups, crash dumps, or OS
- backups.
-- No guarantee that every supported OS will show biometric UI; Electron delegates to the platform
- password manager.
-- No change to the core SQL schema names for existing conversation, provider, MCP, or agent tables.
-
-## Constraints
-
-- `configInitHook` must continue to read logging and proxy settings before SQLite opens.
-- `databaseInitHook` is the earliest point where encrypted SQLite can be unlocked.
-- The unlock UI belongs to the splash renderer so the main app is not shown before DB unlock.
-- New renderer-main APIs should use typed route contracts and renderer API clients, not new direct
- `useLegacyPresenter()` usage.
-- Migration SQL must not include raw passwords in any logged SQL string.
-- The implementation must support both safeStorage-backed startup unlock and manual startup unlock.
-
-## Security Notes
-
-- The SQLite password is present in main-process memory while the app is running.
-- `safeStorage` protects stored password material at rest for the current OS user by using the OS
- credential store, such as macOS Keychain, Windows Credential Vault, or a Linux secret store. It
- does not defend against malware running as that user.
-- Deleting old database files is ordinary file deletion, not cryptographic erasure.
-- WAL/SHM sidecar files must be checkpointed and removed during migration because they may contain
- plaintext data from before encryption.
-- Sync backups created before encryption may still contain plaintext data and remain the user's
- responsibility to delete.
-
-## References
-
-- Electron safeStorage: https://www.electronjs.org/docs/latest/api/safe-storage
-- SQLite3 Multiple Ciphers overview: https://utelle.github.io/SQLite3MultipleCiphers/
-- SQLite3 Multiple Ciphers SQL pragmas:
- https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_sql_pragmas/
-- SQLite3 Multiple Ciphers URI parameters:
- https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_uri/
diff --git a/docs/architecture/sqlite-database-encryption/tasks.md b/docs/architecture/sqlite-database-encryption/tasks.md
deleted file mode 100644
index 694ee41bb..000000000
--- a/docs/architecture/sqlite-database-encryption/tasks.md
+++ /dev/null
@@ -1,79 +0,0 @@
-# SQLite Database Encryption Tasks
-
-## SDD
-
-- [x] Draft encryption spec, implementation plan, and task breakdown.
-
-## Database Security Infrastructure
-
-- [x] Add database security metadata store outside SQLite.
-- [x] Add safeStorage wrapper with support detection, backend reporting, password wrap/unwrap, and
- normalized errors.
-- [x] Add database password validation using the project SQLite open helper.
-- [x] Replace SQL string password interpolation with the native `db.key(Buffer)` path or equivalent
- parameter-safe key application.
-- [x] Configure SQLCipher 4 compatibility mode before keying encrypted database connections.
-- [x] Add tests for metadata defaults, safeStorage unavailable, safeStorage decrypt failure, and
- password validation.
-
-## Startup And Splash Unlock
-
-- [x] Extend `SplashWindowManager` with an awaitable database unlock request API.
-- [x] Add typed splash IPC channels for unlock submit and cancel.
-- [x] Update `databaseInitHook` to resolve the SQLite password before creating `DatabaseInitializer`.
-- [x] Add splash renderer unlock and system-unlock-progress states.
-- [ ] Add tests for successful system unlock, manual unlock, wrong password retry, and cancel before
- main window creation.
-
-## Migration Engine
-
-- [x] Verify backup-plus-temp-`db.rekey(Buffer)` behavior against the project Electron/SQLite ABI
- and replace it with attach/copy migration after incompatibility was confirmed.
-- [ ] Add a migration lock shared by encryption changes, sync backup/import, DB repair, and reset
- flows.
-- [x] Implement plaintext-to-encrypted migration with temp DB validation and rollback.
-- [x] Implement encrypted password change with temp DB validation and rollback.
-- [x] Implement encrypted-to-plaintext disable flow with temp DB validation and rollback.
-- [x] Add sidecar cleanup for `agent.db-wal` and `agent.db-shm`.
-- [x] Add startup recovery for leftover temp/rollback migration files.
-- [ ] Add tests for migration success, validation failure, replacement failure, rollback, and sidecar
- cleanup.
-
-## Typed Routes And Settings UI
-
-- [x] Add shared route contracts for status, enable encryption, change password, and disable
- encryption.
-- [x] Add renderer API client methods for the new database security routes.
-- [x] Add Data settings encryption section with i18n text and password validation.
-- [x] Explain OS credential-store unlock behavior and when manual password input is required.
-- [x] Disable encryption actions while migration is running or when required password fields are
- invalid.
-- [x] Add settings activity records without storing raw passwords.
-- [ ] Add renderer tests for enabled, disabled, safeStorage unavailable, validation, and migration
- progress states.
-
-## Sensitive Config Into SQLite
-
-- [x] Add SQLite-backed generic `app_settings` storage for sensitive app config.
-- [x] Migrate `remoteControl` into SQLite and redact its legacy JSON copy.
-- [x] Migrate `mcprouterApiKey` into SQLite and redact its legacy JSON copy.
-- [x] Migrate `nowledgeMemConfig` into SQLite and redact its legacy JSON copy.
-- [x] Migrate `hooksNotifications` into SQLite and redact its legacy JSON copy.
-- [x] Migrate `knowledge-configs` into SQLite and clear the legacy ElectronStore file.
-- [x] Migrate `custom_prompts` and `system_prompts` into SQLite and clear legacy prompt files.
-- [x] Remove legacy provider/model/MCP sensitive leftovers from JSON once SQLite-backed config is
- verified.
-- [x] Remove the legacy `providers` JSON copy when database encryption or password migration runs
- after provider config has moved to SQLite.
-- [x] Update sync backup/import filtering and legacy backup import for migrated sensitive settings.
-- [ ] Add tests for idempotent sensitive config migration and legacy JSON redaction.
-
-## Verification
-
-- [x] Run focused main tests for encryption, migration, unlock, and sensitive config migration.
-- [x] Run focused renderer tests for Data settings encryption controls.
-- [ ] Run focused renderer tests for splash unlock UI.
-- [x] Run `pnpm run format`.
-- [x] Run `pnpm run i18n`.
-- [x] Run `pnpm run lint`.
-- [x] Run `pnpm run typecheck`.
diff --git a/docs/architecture/startup-orchestration/acceptance.md b/docs/architecture/startup-orchestration/acceptance.md
deleted file mode 100644
index 64f9e80a9..000000000
--- a/docs/architecture/startup-orchestration/acceptance.md
+++ /dev/null
@@ -1,126 +0,0 @@
-# Startup Orchestration 验收方案
-
-## 验收目标
-
-本轮验收聚焦六件事:
-
-1. 主窗口 interactive 是否先于 provider/model background warmup
-2. settings 是否先显示 shell + summaries,再按 section 渐进完成
-3. workload 是否具备 target 级取消与状态回推
-4. `providers.listSummaries` / `models.getProviderCatalog` 是否走新的轻量与内存路径
-5. skills discover / sync scan 是否已离开 main
-6. 打包后 worker 路径是否仍然可用
-
-## 必备观测点
-
-日志或事件中至少应能观察到:
-
-1. `startup.bootstrap.ready`
-2. `startup.session.first-page.ready`
-3. `startup.provider.warmup.deferred`
-4. `startup.workload.changed`
-5. settings heavy section 的 skeleton -> ready 过渡
-
-## P0 通过条件
-
-### P0-1 主窗口首屏响应
-
-- 冷启动时主窗口在 session 首批返回前已经可交互
-- interactive 后不会立刻自动扫所有 provider models
-- provider/model 相关区域按需或 idle 后再补齐
-
-### P0-2 Settings staged hydration
-
-- 打开 settings 时先出现 shell/nav/provider summaries
-- `modelStore.initialize()` / `ollamaStore.initialize()` 不在 settings-open 默认链路上
-- 进入 provider / skills / MCP / remote 前,不会提前触发对应 heavy hydration
-- heavy 页面在 ready 前展示 section skeleton,而不是空白页或整窗冻结
-
-### P0-3 Workload cancellation
-
-- settings window 关闭后,`target = settings` 的 pending/running task 被取消
-- 再次打开 settings 时可以 replay 可见 workload 状态
-
-### P0-4 Main hotspot control
-
-- `providers.listSummaries` 不返回模型数组
-- `models.getProviderCatalog` route 重复命中时以内存快照为主路径
-- `ModelStatusHelper` 首次构建 snapshot 后,批量查询不再重复打持久层读取
-
-### P0-5 Off-main workers
-
-- skills discover / sync scan 正常返回结果
-- worker 抛错时自动回退主线程路径,应用仍可用
-
-### P0-6 Packaging compatibility
-
-- `electron-vite build` 可以产出包含 worker 逻辑的 main bundle
-- `electron-builder --dir` 可以产出 unpacked 包
-- worker 在 asar 环境下不依赖 `process.cwd()` 解析第三方依赖
-
-## 手工验收步骤
-
-### 1. 冷启动主窗口
-
-1. 冷启动应用
-2. 观察:
- - shell / new thread 可交互
- - session sidebar 先 skeleton 再首批
- - provider/model 相关 UI 不会在 interactive 后立刻卡住 main
-
-### 2. 冷启动后立刻打开 settings
-
-1. 冷启动后马上打开 settings
-2. 观察:
- - shell/nav 先出现
- - provider list 先出现 summaries
- - 不出现整窗白屏或冻结
-
-### 3. 进入 heavy settings routes
-
-1. 依次进入 provider / skills / MCP / remote
-2. 观察:
- - 进入前不提前拉起对应数据
- - 进入后先 skeleton,再 section-ready
-
-### 4. 关闭 settings 中断任务
-
-1. 打开 settings 后迅速切到 heavy 页面
-2. 在加载过程中关闭 settings
-3. 观察:
- - settings 任务取消
- - main 窗口继续保持响应
-
-### 5. 大量 provider / models / skills
-
-1. 准备多 provider、多 models、多 skills 数据
-2. 冷启动应用并操作主窗口输入、切换 settings
-3. 观察:
- - main 输入与窗口交互保持响应
- - skills discover / sync scan 不再明显阻塞 main
-
-## 自动化校验
-
-每轮收口至少执行:
-
-1. `pnpm run format`
-2. `pnpm run i18n`
-3. `pnpm run lint`
-4. `pnpm run typecheck`
-5. 关键 vitest:
- - coordinator
- - provider summaries route
- - model status snapshot
- - settings startup
- - worker direct tests
- - worker fallback tests
-
-## 不通过条件
-
-以下任一条件触发即判定不通过:
-
-1. 主窗口 interactive 后立即无条件扫所有 provider/models
-2. settings 打开时仍拉起全量 model / ollama warmup
-3. settings heavy route ready 前出现空白页或整窗卡住
-4. `ModelStatusHelper` 批量查询仍重复命中持久层
-5. worker 在打包产物环境下因依赖解析失败不可用
diff --git a/docs/architecture/startup-orchestration/plan.md b/docs/architecture/startup-orchestration/plan.md
deleted file mode 100644
index 755cfee76..000000000
--- a/docs/architecture/startup-orchestration/plan.md
+++ /dev/null
@@ -1,213 +0,0 @@
-# Startup Orchestration 实施计划
-
-## 规划结论
-
-本轮实现拆成两阶段推进,但都沿用同一套 `startup-orchestration` 规格,不再另起一套启动方案:
-
-1. phase 1:coordinator、typed event、summary route、on-demand hydration、chunked main tasks
-2. phase 2:skills discover / sync scan worker 化,补齐 main-side 热点优化
-
-当前分支的交付目标是:
-
-```text
-main: 先可交互,再渐进补
-settings: summary first,再分 section hydration
-background: coordinator 调度
-cpu parse/scan: off-main
-```
-
-## 当前实现架构
-
-```mermaid
-sequenceDiagram
- participant Main as Main Process
- participant Coord as StartupWorkloadCoordinator
- participant Renderer as Renderer
- participant Settings as Settings Renderer
- participant Worker as worker_threads
-
- Main->>Coord: createRun('main')
- Main->>Coord: register floating/skills/mcp/remote tasks
- Renderer->>Main: startup.getBootstrap
- Main-->>Renderer: bootstrap shell
- Renderer-->>Renderer: interactive shell ready
- Renderer->>Main: sessions.listLightweight
- Main-->>Renderer: first page
- Renderer-->>Renderer: sidebar skeleton -> real list
- Settings->>Main: providers.listSummaries
- Main-->>Settings: provider summaries
- Settings-->>Settings: shell/nav/provider list ready
- Settings->>Main: models.getProviderCatalog (on route entry)
- Main-->>Settings: provider catalog snapshot
- Main->>Worker: skill discovery / sync scan
- Worker-->>Main: JSON result or error
- Main-->>Renderer: startup.workload.changed
-```
-
-## Phase 1
-
-### 1. Coordinator 与 workload contract
-
-落地项:
-
-1. `StartupWorkloadCoordinator`
-2. `startup.workload.changed`
-3. shared task id / phase / state schema
-4. settings target cancel / replay
-
-结果:
-
-1. startup task 不再是松散 fire-and-forget
-2. renderer 可以按 target/task 感知 readiness
-3. settings 关闭后后台 warmup 不再继续偷偷跑
-
-### 2. Route-level startup tracking
-
-落地项:
-
-1. `startup.getBootstrap` -> `main.bootstrap`
-2. `sessions.listLightweight` -> `main.session.firstPage`
-3. `providers.listSummaries` -> `settings.providers.summary`
-4. `models.getProviderCatalog` -> `settings.provider.models` / `main.provider.warmup`
-5. Ollama / skills / MCP route tracking
-
-结果:
-
-1. route 级 workload 可观测
-2. 主要 settings section readiness 有统一事件源
-
-### 3. Settings staged hydration
-
-落地项:
-
-1. settings 首屏只跑 cheap snapshot + provider summaries
-2. `modelStore.initialize()` / `ollamaStore.initialize()` 退出 settings-open 默认链路
-3. provider detail / skills / MCP / remote 页面按路由进入后再 hydration
-4. heavy settings section 统一给 skeleton
-
-结果:
-
-1. settings 打开不再整窗拉满
-2. heavy 页面 ready 前有明确占位态
-
-### 4. Main window warmup policy
-
-落地项:
-
-1. 去掉主窗口 interactive 后立即全量 provider/model warmup
-2. `ChatStatusBar` / `NewThreadPage` 改为 likely-provider on-demand warmup
-3. coordinator idle 后增加低优先级 provider snapshot backfill
-
-结果:
-
-1. provider/model 数据只在需要时优先补齐
-2. 用户不操作相关 UI 时,不再立刻扫全量 provider
-3. 空闲窗口下可以渐进预热缓存
-
-### 5. Model status / provider catalog hotspot 修复
-
-落地项:
-
-1. `providers.listSummaries`
-2. `models.getProviderCatalog` memory-snapshot-first
-3. `ModelStatusHelper` persisted snapshot
-
-结果:
-
-1. settings 首屏不再携带大模型数组
-2. route 重复命中时优先走内存而不是反复持久层读取
-
-## Phase 2
-
-### 1. Skill discovery worker
-
-落地项:
-
-1. `runInlineJsonWorker`
-2. `discoverSkillMetadataInWorker(...)`
-3. worker warning logging
-4. worker failure fallback 到旧主线程路径
-
-结果:
-
-1. skill manifest discover / parse 脱离 main
-2. 仍保持错误可回退
-
-### 2. Skill sync worker
-
-落地项:
-
-1. `scanExternalToolsInWorker(...)`
-2. `scanAndDetectDiscoveriesInWorker(...)`
-3. `SkillSyncPresenter` worker-first / main-thread fallback
-
-结果:
-
-1. external tool scan / compare 脱离 main
-2. settings skills section 与后台扫描不再直接占用 main CPU
-
-## Worker 边界决策
-
-本轮明确采用:
-
-1. `worker_threads` 处理短生命周期、纯 Node/FS/parse 的 CPU/scan 工作
-2. main 保留 Electron 绑定任务的调度与事件桥接
-
-不在本轮切换为 `utilityProcess` 的原因:
-
-1. 当前技能 discover / sync scan 更像短任务,不需要独立进程生命周期管理
-2. 现有 `electron-vite + electron-builder + asar` 对当前 inline worker 路径已经可验证打包通过
-3. 更适合 `utilityProcess` 的是长驻、隔离性要求更强、失败域更大的 runtime/service 类任务
-
-## 风险与缓解
-
-### 风险 1:idle warmup 又变成新的隐性阻塞
-
-缓解:
-
-1. idle warmup 只在 coordinator idle 后触发
-2. 每个 provider 之间显式 yield
-3. 仍使用 background phase
-
-### 风险 2:worker 在打包后找不到依赖
-
-缓解:
-
-1. worker 采用 inline eval 模式,避免额外 worker 入口文件打包问题
-2. 通过 `createRequire(...)` 把 worker 的依赖解析锚到 bundle 路径,而不是 `process.cwd()`
-
-### 风险 3:settings 关闭后任务残留
-
-缓解:
-
-1. settings workload 全部归属 `target = settings`
-2. window close 时统一 `cancelTarget('settings')`
-
-## 验证策略
-
-主进程:
-
-1. coordinator 优先级 / 并发限制 / 取消
-2. provider summaries route
-3. model status snapshot
-4. worker success / fallback
-
-renderer:
-
-1. settings open 仅 cheap init + summaries
-2. main window 不再 eager provider warmup
-3. heavy settings section skeleton
-
-打包:
-
-1. `electron-vite build`
-2. `electron-builder --dir`
-3. worker 路径在 asar 环境下可解析依赖
-
-## 后续 follow-up
-
-以下不再算本轮 blocker,但适合作为下一轮:
-
-1. 更细粒度的 main/splash unified trace
-2. `utilityProcess` 在 long-lived runtime 任务上的替换评估
-3. 更强的 renderer route-level skeleton 自动化测试覆盖
diff --git a/docs/architecture/startup-orchestration/spec.md b/docs/architecture/startup-orchestration/spec.md
deleted file mode 100644
index 284ada384..000000000
--- a/docs/architecture/startup-orchestration/spec.md
+++ /dev/null
@@ -1,219 +0,0 @@
-# Startup Orchestration 规格
-
-## 概述
-
-本轮规格把启动优化从“继续多挪一些 deferred”收敛成“把 deferred 背后的 main 负载拆成可调度、可取消、可观测、必要时 off-main 的工作流”。
-
-目标固定为两条主线:
-
-1. 主窗口和 settings 都使用 staged hydration,先给用户可交互骨架,再按 section 渐进补齐。
-2. main 进程中会持续占满事件循环的初始化逻辑统一接入 coordinator;纯 Node/FS/parse 的重活迁到 `worker_threads`;仍必须留在 main 的 Electron 绑定任务改为低并发、分批让出事件循环。
-
-## 当前决策
-
-本规格以 2026-04-21 的 Startup Main-Unblocking V3 决策为准:
-
-1. main-side `StartupWorkloadCoordinator` 统一管理启动任务。
-2. workload phase 固定为 `interactive -> deferred -> background`。
-3. workload resource 并发固定为 `cpu = 1`、`io = 2`。
-4. main window 保持 `bootstrap + staged session hydration`,provider/model warmup 只在 on-demand 或 coordinator idle 后执行。
-5. settings window 首屏只加载 cheap snapshot、router ready、provider summaries;heavy tabs 按路由进入再 hydration。
-6. `models.getProviderCatalog` 改为内存快照优先,`ModelStatusHelper` 改为 snapshot-first。
-7. `SkillPresenter` manifest discover 和 `SkillSyncPresenter` scan/compare 迁到 `worker_threads`,失败时回落主线程路径。
-8. `startup.workload.changed` 作为统一 typed event 回推 renderer readiness。
-
-## 背景
-
-上一轮 deferred 已经减少了首屏直接阻塞,但 main 进程依然存在两个核心问题:
-
-1. deferred 任务虽然“晚一点跑”,但仍可能在一个时间窗内把 main 事件循环打满。
-2. settings 打开时仍然会顺手拉起过多 provider/model/ollama/skills/mcp/remote 相关初始化,导致整窗冻结感。
-
-实际问题不再是“有没有 deferred”,而是:
-
-1. deferred 是否被拆成可调度、可取消、可观测的 workload。
-2. CPU/parse/scan 类工作是否已经离开 main。
-3. 仍在 main 的工作是否低并发、可中断、分批 yield。
-
-## 目标体验
-
-```text
-Main Window
-+ chat shell ready
-+ agent/bootstrap ready
-+ session sidebar skeleton
-+ provider/model regions hydrate on demand or during idle
-
-Settings Window
-+ shell/nav ready
-+ cheap settings snapshot ready
-+ provider summaries ready
-+ provider/models/skills/mcp/remote hydrate by route task events
-```
-
-## 用户故事
-
-### US-1:主窗口先交互,再补内容
-
-作为用户,我希望主窗口出现后立刻可以进入 new thread 或 active chat,而不是等待 provider/model warmup 抢占 main。
-
-### US-2:settings 先出框架,再补 section
-
-作为用户,我希望打开 settings 时先看到可用的 shell、导航和 summaries,而不是整窗白屏或冻结。
-
-### US-3:后台任务有明确进度与取消边界
-
-作为用户,我希望重任务能渐进完成;关闭 settings 时,settings 专属 warmup 不应继续偷偷占用 main。
-
-### US-4:重 CPU/parse 工作不再阻塞 main
-
-作为用户,我希望 skills discover / sync scan 这类纯 Node 工作在后台线程里完成,不影响主窗口输入和切换。
-
-## 功能范围
-
-本轮范围覆盖:
-
-1. `StartupWorkloadCoordinator` 与 typed startup workload event。
-2. 主窗口 provider/model warmup 的 on-demand + idle backfill 策略。
-3. settings staged hydration 与 summary-first 路径。
-4. `providers.listSummaries` 精简 route。
-5. `models.getProviderCatalog` memory-snapshot-first 路径。
-6. `ModelStatusHelper` persisted snapshot。
-7. `SkillPresenter` discovery worker。
-8. `SkillSyncPresenter` scan/discovery worker。
-9. `docs/architecture/startup-orchestration/` 同步到 V3。
-
-## 功能要求
-
-### A. Main-side Startup Coordinator
-
-- main 侧必须存在统一的 `StartupWorkloadCoordinator`。
-- coordinator 负责:
- - 任务注册
- - phase 优先级调度
- - `cpu/io` 并发限制
- - yield 控制
- - 按 target/run 取消
- - workload 状态事件推送
-- phase 固定为:
- - `interactive`
- - `deferred`
- - `background`
-- 资源并发固定为:
- - `cpu = 1`
- - `io = 2`
-
-### B. Presenter.init 注册方式
-
-- `Presenter.init()` 不再散射式 `void initializeXxx()` fire-and-forget。
-- 以下任务统一注册到 coordinator:
- - floating button
- - yo browser
- - skills init
- - skill sync scan
- - MCP init
- - remote runtime init
- - idle provider warmup
-
-### C. Window/Target 取消边界
-
-- settings window 创建时创建或复用 `settings` startup run。
-- settings window ready 时可以 replay 现有 workload snapshot。
-- settings window 关闭时,`target = settings` 的未完成任务必须取消。
-- main window task 与 settings window task 必须在 coordinator 里分 target 管理。
-
-### D. Startup Workload Typed Event
-
-- 新增 typed event:`startup.workload.changed`
-- payload 固定包含:
- - `startupRunId`
- - `target`
- - `tasks`
-- shared task ids 固定覆盖:
- - `main.bootstrap`
- - `main.session.firstPage`
- - `main.provider.warmup`
- - `settings.providers.summary`
- - `settings.provider.models`
- - `settings.ollama`
- - `settings.skills.catalog`
- - `settings.skills.syncScan`
- - `settings.mcp.runtime`
- - `settings.remote.runtime`
-
-### E. Settings Summary-first Hydration
-
-- settings `onMounted` 只做:
- - `uiSettingsStore.loadSettings()`
- - router ready
- - `providers.listSummaries`
- - startup workload subscription
-- settings open 时移除 eager:
- - `modelStore.initialize()`
- - `ollamaStore.initialize()`
-- `settings-provider` 路由进入后再 `ensureProviderModelsReady(providerId)`。
-- Ollama 仅在命中对应 provider detail 时加载。
-- `skills/mcp/remote` 仅在进入各自页面后才触发 hydration。
-
-### F. Main Window Provider Warmup Policy
-
-- `startup.provider.warmup.deferred` 不得在 interactive 后立刻无条件触发全量 sweep。
-- provider/model warmup 允许两个入口:
- - 用户进入依赖 provider/model 的具体 UI
- - coordinator idle 后进行低优先级 backfill
-- idle backfill 必须:
- - low priority
- - 可取消
- - 每个 provider 之间显式 yield
-
-### G. Summary Route 与 Memory Snapshot
-
-- 新增 `providers.listSummaries` route。
-- settings 首屏只依赖 provider summaries,不带模型数组。
-- `models.getProviderCatalog` 保持存在,但改为:
- - provider models
- - custom models
- - db provider models
- - model status map
- 的 memory-snapshot-first 返回。
-
-### H. Model Status Snapshot
-
-- `ModelStatusHelper.getBatchModelStatus(...)` 不再频繁直接命中持久层。
-- 首次访问时构建内存 status snapshot。
-- 后续批量读取走内存查表。
-- `setModelStatus/deleteModelStatus` 同时更新:
- - 内存 snapshot
- - 持久层
-
-### I. Chunked Main Tasks
-
-- 仍在 main 的批量任务禁止长时间同步 for-loop 跑完整表。
-- 批量任务必须分批推进,并在批次间 yield。
-- 本轮至少保证:
- - provider idle warmup 逐 provider yield
- - MCP / remote 等 background init 不和 interactive 抢同一时段资源
-
-### J. Off-main Workers
-
-- 明确迁到 `worker_threads` 的任务:
- - `SkillPresenter` manifest discover / parse
- - `SkillSyncPresenter` external tool scan / compare
-- worker 输入输出必须是 JSON serializable 数据。
-- worker 失败时必须回落主线程路径,不影响应用可用性。
-- 本轮不把 MCP runtime / remote runtime 迁出 main;它们仍走 coordinator。
-
-## 非目标
-
-1. 本轮不新增新的 legacy IPC。
-2. 本轮不把 MCP runtime / remote runtime 切到 `utilityProcess`。
-3. 本轮不重写 provider store 整体架构。
-4. 本轮不追求“完全无 loading”,而是明确 skeleton / section-ready。
-
-## 约束
-
-1. 继续保持 typed route / typed event 方向一致。
-2. settings 与 main 必须共享同一套 startup workload contract,而不是两套启动协议。
-3. 允许显式 section skeleton,禁止“表面无 loading、实际整页卡住”。
-4. `worker_threads` 落地必须兼容 `electron-vite + electron-builder + asar` 打包环境。
-5. 仍留在 main 的任务必须在 workload、日志和取消边界上可解释。
diff --git a/docs/architecture/startup-orchestration/tasks.md b/docs/architecture/startup-orchestration/tasks.md
deleted file mode 100644
index 5575cdda8..000000000
--- a/docs/architecture/startup-orchestration/tasks.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# Startup Orchestration 任务拆分
-
-## Phase 1
-
-### T1 Coordinator / Contract
-
-- [x] 新增 `StartupWorkloadCoordinator`
-- [x] 固定 phase 优先级为 `interactive / deferred / background`
-- [x] 固定资源并发为 `cpu = 1 / io = 2`
-- [x] 支持 target run 创建、复用、取消、replay
-- [x] 新增 typed event `startup.workload.changed`
-- [x] 固定 shared startup task ids
-
-### T2 Presenter / Window Wiring
-
-- [x] `Presenter.init()` 改成 coordinator 注册 startup task
-- [x] floating button 接入 coordinator
-- [x] skills init 接入 coordinator
-- [x] skill sync background scan 接入 coordinator
-- [x] MCP init 接入 coordinator
-- [x] remote runtime init 接入 coordinator
-- [x] settings window create / ready / close 接入 run create / replay / cancel
-
-### T3 Main Window Hydration
-
-- [x] 保持 `startup.getBootstrap` + staged session path
-- [x] 去掉 interactive 后立刻无条件 provider/model warmup
-- [x] `ChatStatusBar` 改为 likely-provider on-demand warmup
-- [x] `NewThreadPage` 改为 likely-provider on-demand warmup
-- [x] coordinator idle 后增加低优先级 provider snapshot backfill
-
-### T4 Settings Hydration
-
-- [x] settings `onMounted` 只做 cheap init + router ready + `providers.listSummaries`
-- [x] 移除 settings open 时的 eager `modelStore.initialize()`
-- [x] 移除 settings open 时的 eager `ollamaStore.initialize()`
-- [x] `settings-provider` 路由进入后再 `ensureProviderModelsReady(providerId)`
-- [x] Ollama 仅在命中对应 provider detail 时加载
-- [x] skills / MCP / remote 页面进入后再各自 hydration
-- [x] heavy settings section 增加 skeleton
-
-### T5 Summary Route / Memory Snapshot
-
-- [x] 新增 `providers.listSummaries`
-- [x] settings 首屏改走 provider summaries
-- [x] `models.getProviderCatalog` 改成 memory-snapshot-first
-- [x] `ModelStatusHelper` 改为 snapshot-first
-- [x] `setModelStatus/deleteModelStatus` 同步更新 snapshot + persistent store
-
-### T6 Route-level Tracking / Tests
-
-- [x] route runtime 接入 workload task tracking
-- [x] coordinator 优先级 / 并发 / 取消单测
-- [x] `providers.listSummaries` route 单测
-- [x] `ModelStatusHelper` snapshot 单测
-- [x] renderer 侧去 eager warmup 的测试更新
-
-## Phase 2
-
-### T7 Worker: Skill Discovery
-
-- [x] 新增 inline JSON worker runner
-- [x] `SkillPresenter` discover / parse 迁到 worker
-- [x] worker warning logging
-- [x] worker failure fallback 到主线程路径
-- [x] direct worker 单测
-- [x] presenter fallback 单测
-
-### T8 Worker: Skill Sync
-
-- [x] `SkillSyncPresenter` scan / compare 迁到 worker
-- [x] worker failure fallback 到主线程路径
-- [x] direct worker 单测
-- [x] presenter fallback 单测
-- [x] worker dependency resolution 固定锚到 bundle path,而不是 `process.cwd()`
-
-## 文档同步
-
-- [x] 更新 `spec.md`
-- [x] 更新 `plan.md`
-- [x] 更新 `tasks.md`
-- [x] 更新 `acceptance.md`
-
-## 最终校验
-
-- [x] `pnpm run format`
-- [x] `pnpm run i18n`
-- [x] `pnpm run lint`
-- [x] `pnpm run typecheck`
-- [x] 处理与本轮无关的自动拉新资源变更
-
-## 后续 Follow-up
-
-以下不算本轮 blocker,但保留给下一轮:
-
-- [ ] main / splash 统一 startup trace
-- [ ] `utilityProcess` 在 long-lived runtime 上的替换评估
-- [ ] 更完整的 heavy settings route skeleton 自动化测试
diff --git a/docs/architecture/sync-config-import-versioning/plan.md b/docs/architecture/sync-config-import-versioning/plan.md
deleted file mode 100644
index 116cf2b81..000000000
--- a/docs/architecture/sync-config-import-versioning/plan.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Sync Config Import Versioning Plan
-
-## Architecture
-
-- Add a small sync config import service that parses legacy JSON config files and writes them to
- `ConfigTables`.
-- Keep `agent.db` as the source of truth for v2 config backups.
-- Use the backup manifest for version routing: missing/v1 is legacy, v2 is current, future versions
- are rejected.
-
-## Import Behavior
-
-- For v2 backups, import SQLite tables using the existing database import flow.
-- For legacy backups, import conversations as before, then import old config files into SQLite.
-- Increment mode only fills missing config rows. Overwrite mode replaces the corresponding config
- tables from backup data.
-- Imported legacy config marks the config migration as applied so startup does not re-import stale
- JSON over the restored SQLite tables.
-
-## Export Behavior
-
-- Write `manifest.version = 2`, `configStorage = sqlite`, and `configSchemaVersion = 1`.
-- Export `agent.db`, lightweight app settings, custom prompts, and system prompts.
-- Do not export `mcp-settings.json` or migrated app-settings keys.
diff --git a/docs/architecture/sync-config-import-versioning/spec.md b/docs/architecture/sync-config-import-versioning/spec.md
deleted file mode 100644
index d0c8ed760..000000000
--- a/docs/architecture/sync-config-import-versioning/spec.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Sync Config Import Versioning
-
-## User Story
-
-DeepChat sync backups must preserve the SQLite-backed Provider, Model, MCP, and Agent-adjacent
-configuration while still importing old backups that stored those settings in JSON files.
-
-## Acceptance Criteria
-
-- New backups use manifest version 2 and declare SQLite-backed config storage.
-- New backups do not export migrated configuration in legacy JSON files.
-- Old backups import legacy Provider, Model, MCP, and ACP residual settings into SQLite config
- tables.
-- Increment imports preserve local config when IDs conflict.
-- Overwrite imports replace config with backup data while preserving local sync settings.
-- Backups with unsupported future versions fail before local data is changed.
-
-## Non-Goals
-
-- Do not change renderer routes or sync APIs.
-- Do not add a separate config-v2 JSON export.
-- Do not migrate prompts or knowledge configs into SQLite in this change.
diff --git a/docs/architecture/sync-config-import-versioning/tasks.md b/docs/architecture/sync-config-import-versioning/tasks.md
deleted file mode 100644
index fe7342eb9..000000000
--- a/docs/architecture/sync-config-import-versioning/tasks.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Sync Config Import Versioning Tasks
-
-- [x] Add manifest v2 parsing and unsupported-version handling.
-- [x] Add legacy config parser/importer for app-settings, MCP, model config, provider models, and ACP residual settings.
-- [x] Update SyncPresenter import/export flow to call the importer and keep JSON lightweight.
-- [x] Add sync/config import tests and unsupported-version coverage.
-- [x] Add i18n key for unsupported backup versions.
-- [x] Run format, i18n, lint, typecheck, and targeted tests.
diff --git a/docs/archives/agent-cleanup/plan.md b/docs/archives/agent-cleanup/plan.md
deleted file mode 100644
index 9e6bd3b8a..000000000
--- a/docs/archives/agent-cleanup/plan.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Agent Cleanup Final Plan State
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Summary
-
-The cleanup is no longer paused. The runtime retirement slice completed on March 23, 2026.
-
-## Completed Order
-
-1. moved shared runtime helpers out of legacy presenter folders
-2. moved active renderer chat path off legacy message protocol
-3. archived dead renderer path code
-4. persisted new-session skills in `new_sessions.active_skills`
-5. removed legacy global presenter access
-6. retired legacy `AgentPresenter` runtime and public exposure
-7. migrated retained ACP/agent-tool helpers into current live modules
-8. archived retired source/tests and refreshed active docs
-
-## Keep For Now
-
-- `LegacyChatImportService`
-- legacy import hook / status tracking
-- old `conversations/messages` tables as import-only or export-facing sources
-- `SessionPresenter` as internal compatibility/data adapter
-- `scripts/agent-cleanup-guard.mjs`
-
-## Follow-up Order
-
-When cleanup resumes, use this order:
-
-1. clear remaining export-only / non-active-path type coupling
-2. inventory and reduce adjacent provider globals
-3. normalize older specs/docs that still mention retired paths where useful
-4. only then consider deeper removal of legacy import-era tables
-
-## Default Rules
-
-1. Do not reintroduce retired runtime entrypoints.
-2. Prefer archiving dead code before hard deletion.
-3. Keep import-only compatibility separate from active runtime design.
-4. Update SDD documents in the same slice when retiring architecture.
diff --git a/docs/archives/agent-cleanup/spec.md b/docs/archives/agent-cleanup/spec.md
deleted file mode 100644
index d23d7fcc4..000000000
--- a/docs/archives/agent-cleanup/spec.md
+++ /dev/null
@@ -1,68 +0,0 @@
-# Agent Cleanup
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Summary
-
-The cleanup reached final runtime retirement on March 23, 2026.
-
-Current primary flow:
-
-- renderer active chat pages/stores/components
-- `agentSessionPresenter`
-- `agentRuntimePresenter`
-- `toolPresenter`
-- `llmProviderPresenter`
-
-Completed in this retirement slice:
-
-- retired live legacy `AgentPresenter` runtime wiring
-- removed public `agentPresenter` / `sessionPresenter` IPC exposure
-- removed `ILlmProviderPresenter.startStreamCompletion()`
-- migrated retained ACP helpers into `src/main/presenter/llmProviderPresenter/acp/`
-- migrated retained agent tools into `src/main/presenter/toolPresenter/agentTools/`
-- moved retained user message formatting helpers into `src/main/presenter/sessionPresenter/`
-- removed retired source and tests from the active tree, with history preserved in docs
-
-## Compatibility Boundary
-
-The supported compatibility boundary is now:
-
-- keep `LegacyChatImportService`
-- keep legacy import hook / status tracking
-- keep old `conversations/messages` tables as import-only or export-facing sources
-- keep `SessionPresenter` as a main-internal compatibility/data facade only
-
-The new primary flow must not regain runtime ownership from retired `AgentPresenter`.
-
-## Guardrails
-
-`scripts/agent-cleanup-guard.mjs` remains the anti-regression guard.
-
-It now protects these invariants:
-
-1. new main-path modules must not import retired legacy presenter runtime paths
-2. active renderer chat path must not reintroduce `@shared/chat`
-3. provider-layer code must not regain retired legacy fallbacks
-4. `SkillPresenter` and MCP gating must not regain retired legacy global access
-5. retired runtime code stays archived rather than silently re-entering the live tree
-
-## Completed Milestones
-
-- shared helper ownership moved to `src/main/lib/agentRuntime`
-- active renderer chat path moved off legacy message protocol
-- dead renderer and mock/orphan code removed from the active tree
-- new-session skills persisted in `new_sessions.active_skills`
-- legacy `agentPresenter/**` removed from global presenter access
-- provider-layer MCP global access removed
-- final legacy runtime retirement completed and documented
-
-## Remaining Backlog
-
-The remaining work is no longer runtime-retirement work. It is adjacent cleanup only:
-
-- export-only `@shared/chat` coupling in `agentSessionPresenter`
-- non-active renderer residual import in `PromptEditorSheet`
-- adjacent provider globals such as `devicePresenter` / `oauthPresenter`
-- optional archival/normalization of older specs that still mention retired paths
diff --git a/docs/archives/agent-cleanup/tasks.md b/docs/archives/agent-cleanup/tasks.md
deleted file mode 100644
index 65aaf1c28..000000000
--- a/docs/archives/agent-cleanup/tasks.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Agent Cleanup Final Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Completed
-
-- [x] Added cleanup docs and static guardrails
-- [x] Moved shared runtime helpers out of legacy presenter folders
-- [x] Moved active renderer chat path off `@shared/chat`
-- [x] Removed dead renderer path code from the active tree
-- [x] Removed renderer mock/orphan code from the active tree
-- [x] Persisted new-session skills in `new_sessions.active_skills`
-- [x] Retired old-session skill fallback to legacy conversation settings
-- [x] Removed global `presenter.*` access from legacy runtime modules
-- [x] Removed provider-layer `presenter.mcpPresenter` access
-- [x] Removed live legacy `AgentPresenter` runtime wiring
-- [x] Removed public `agentPresenter` / `sessionPresenter` IPC exposure
-- [x] Removed `ILlmProviderPresenter.startStreamCompletion()`
-- [x] Migrated retained ACP helpers to `src/main/presenter/llmProviderPresenter/acp/`
-- [x] Migrated retained agent tools to `src/main/presenter/toolPresenter/agentTools/`
-- [x] Migrated retained message formatting helper to `src/main/presenter/sessionPresenter/`
-- [x] Removed retired source and tests from the active tree and preserved history in docs
-- [x] Refreshed active architecture / flow / navigation docs
-
-## Kept Intentionally
-
-- [x] `LegacyChatImportService`
-- [x] legacy import hook / status tracking
-- [x] old `conversations/messages` tables
-- [x] `SessionPresenter` as internal compatibility/data adapter
-- [x] `scripts/agent-cleanup-guard.mjs`
-
-## Remaining Backlog
-
-- [ ] remove export-only `@shared/chat` coupling in `src/main/presenter/agentSessionPresenter/index.ts`
-- [ ] remove non-active renderer residual import in `PromptEditorSheet`
-- [ ] review adjacent provider globals such as `devicePresenter` / `oauthPresenter`
-- [ ] normalize older historical specs that still mention retired legacy paths
-
-## Historical Cleanup Batches
-
-- [x] dead renderer batch
-- [x] mock / orphan UI batch
-- [x] legacy agent runtime batch
diff --git a/docs/archives/agent-tooling-v2/plan.md b/docs/archives/agent-tooling-v2/plan.md
deleted file mode 100644
index 3fe692d54..000000000
--- a/docs/archives/agent-tooling-v2/plan.md
+++ /dev/null
@@ -1,398 +0,0 @@
-# Agent Tooling V2 实施计划(Main Loop 优先)
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 当前实现基线
-
-### 1.1 工具路由
-
-主路由为:
-
-1. `ToolPresenter` 统一汇总和路由工具
- `src/main/presenter/toolPresenter/index.ts`
-2. Agent 本地工具由 `AgentToolManager` 管理
- `src/main/presenter/agentPresenter/acp/agentToolManager.ts`
-3. 文件能力由 `AgentFileSystemHandler` 执行
- `src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts`
-
-### 1.2 Loop 与事件
-
-1. 生成与工具调度:`AgentLoopHandler` + `ToolCallProcessor`
- `src/main/presenter/agentPresenter/loop/agentLoopHandler.ts`
- `src/main/presenter/agentPresenter/loop/toolCallProcessor.ts`
-2. 主事件类型:`LLMAgentEventData`
- `src/shared/types/core/agent-events.ts`
-3. 对 renderer 推送由 `StreamUpdateScheduler` 聚合
- `src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts`
-
-### 1.3 Skills
-
-1. active skills 与 allowedTools 来源:`SkillPresenter`
- `src/main/presenter/skillPresenter/index.ts`
-2. 当前 allowedTools 未做统一 canonical 归一化。
-
-### 1.4 实施边界(补充)
-
-本计划仅涉及:
-
-1. `src/main/presenter/agentPresenter/acp/agentToolManager.ts`
-2. `src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts`
-3. `src/main/presenter/agentPresenter/loop/toolCallProcessor.ts`
-4. `src/main/presenter/skillPresenter/index.ts`(仅 allowedTools 归一化接入)
-5. `src/main/presenter/toolPresenter/index.ts`(tool prompt 与路由提示)
-6. `src/main/presenter/agentPresenter/message/messageBuilder.ts`(system prompt 拼接)
-7. `src/main/presenter/agentPresenter/message/skillsPromptBuilder.ts`(skills allowedTools 接入)
-8. `src/main/lib/agentRuntime/systemEnvPromptBuilder.ts`(env prompt 生成)
-9. 与上述模块直接相关的测试与文档
-
-明确不改动:
-
-1. `src/main/presenter/browser/**`
-2. `src/main/presenter/agentPresenter/acp/chatSettingsTools.ts`
-3. `src/main/lib/agentRuntime/questionTool.ts`
-4. `src/main/presenter/mcpPresenter/**`
-
-## 2. 设计决策
-
-### 2.1 工具命名策略
-
-决策:使用固定 canonical 工具名,不保留旧名兼容调用。
-
-canonical:
-
-1. `read`
-2. `write`
-3. `edit`
-4. `find`
-5. `grep`
-6. `ls`
-7. `exec`
-8. `process`
-
-实现点:
-
-1. `AgentToolManager.fileSystemSchemas` 直接改名。
-2. `isFileSystemTool()`、`callFileSystemTool()`、`collectWriteTargets()` 全量切换新名。
-3. `ToolCallProcessor.TOOLS_REQUIRING_OFFLOAD` 同步新名。
-
-旧工具删除清单(本次落地必须删除):
-
-1. `read_file`
-2. `write_file`
-3. `list_directory`
-4. `create_directory`
-5. `move_files`
-6. `edit_text`
-7. `glob_search`
-8. `directory_tree`
-9. `get_file_info`
-10. `grep_search`
-11. `text_replace`
-12. `edit_file`
-13. `execute_command`
-
-### 2.1.1 Canonical 参数收敛
-
-决策:参数名统一,删除旧参数别名;一处校验,多处复用。
-
-参数标准:
-
-1. `read`: `path`, `offset?`, `limit?`
-2. `write`: `path`, `content`
-3. `edit`: `path`, `oldText`, `newText`, `replaceAll?`
-4. `find`: `pattern`, `path?`, `maxResults?`, `exclude?`
-5. `grep`: `pattern`, `path?`, `filePattern?`, `caseSensitive?`, `contextLines?`, `maxResults?`
-6. `ls`: `path`, `depth?`
-7. `exec`: `command`, `cwd?`, `timeoutMs?`, `background?`, `yieldMs?`
-8. `process`: `action`, `sessionId?`, `offset?`, `limit?`, `data?`, `eof?`
-
-落地原则:
-
-1. schema 校验失败直接返回 `INVALID_ARGUMENT`,不执行工具。
-2. 不再接受 `old_string/new_string` 等 alias。
-3. skills 映射只做工具名映射,不改写工具参数。
-4. 不引入 `allowParallel` 等并行开关参数,避免工具语义分裂。
-
-### 2.1.2 工具返回 envelope
-
-决策:工具返回统一为“可读摘要 + 结构化数据”。
-
-约定:
-
-1. `content`: 给模型看的短摘要。
-2. `rawData.toolResult`: 结构化对象,至少包含 `ok`。
-3. 查询类工具(`read/find/grep/ls/process(log)`)补充 `meta`(截断、分页)。
-4. 写入类工具(`write/edit/exec/process(write|kill...)`)补充 `affectedPaths` 或 `sessionId` 等执行结果元信息。
-
-### 2.2 Skills 工具映射层
-
-决策:新增“skills allowedTools canonicalizer”,在 skills 到运行时工具过滤的边界做归一化。
-
-建议新增模块:
-
-`src/main/presenter/skillPresenter/toolNameMapping.ts`
-
-提供:
-
-1. `normalizeSkillToolName(toolName: string): { canonical: string; mapped: boolean }`
-2. `normalizeSkillAllowedTools(tools: string[]): { tools: string[]; warnings: string[] }`
-
-首批映射(Claude Code 优先):
-
-1. `Read -> read`
-2. `Write -> write`
-3. `Edit -> edit`
-4. `MultiEdit -> edit`
-5. `Glob -> find`
-6. `Grep -> grep`
-7. `LS -> ls`
-8. `Bash -> exec`
-
-接入点:
-
-1. `SkillPresenter.getActiveSkillsAllowedTools()` 返回前归一化。
-2. 归一化 warning 通过日志输出,避免静默丢失。
-
-### 2.3 rg 增强策略
-
-决策:`find/grep` 均采用 “rg 优先,fallback 次级实现”。
-
-1. `find`
- - 优先:`rg --files` + `-g` include/exclude
- - 回退:`glob`
-2. `grep`
- - 优先:现有 `runRipgrepSearch` 路径继续强化结构化输出
- - 回退:现有 JS grep 路径
-
-约束:
-
-1. 保持 `maxResults` 生效,返回截断信息。
-2. 统一默认 ignore 集。
-3. 当 `rg` 调用异常时必须记录告警并稳定回退。
-
-### 2.4 事件与消息格式定版(main 导出)
-
-决策:不在本阶段改事件通道,但冻结字段约束。
-
-事件:
-
-1. `stream:response`
-2. `stream:error`
-3. `stream:end`
-
-`stream:response` 内 tool 事件规范:
-
-1. 公共字段:`eventId`, `stream_kind`, `seq`
-2. tool 事件必备:
- - `tool_call`
- - `tool_call_id`
- - `tool_call_name`
-3. permission 事件附加:
- - `permission_request.toolName`
- - `permission_request.serverName`
- - `permission_request.permissionType`
- - `permission_request.description`
-
-字段样例:
-
-1. 文本增量:
-
-```json
-{
- "eventId": "evt_123",
- "stream_kind": "delta",
- "seq": 7,
- "content": "partial text"
-}
-```
-
-2. 工具执行中:
-
-```json
-{
- "eventId": "evt_123",
- "tool_call": "running",
- "tool_call_id": "call_1",
- "tool_call_name": "grep",
- "tool_call_params": "{\"pattern\":\"TODO\",\"path\":\"src\"}",
- "tool_call_server_name": "agent-filesystem"
-}
-```
-
-3. 工具执行结束:
-
-```json
-{
- "eventId": "evt_123",
- "tool_call": "end",
- "tool_call_id": "call_1",
- "tool_call_name": "grep",
- "tool_call_response": "Found 3 matches in 2 files",
- "tool_call_response_raw": {
- "toolResult": {
- "ok": true,
- "summary": "Found 3 matches in 2 files",
- "data": {
- "matches": []
- },
- "meta": {
- "truncated": false
- }
- }
- }
-}
-```
-
-4. 权限请求:
-
-```json
-{
- "eventId": "evt_123",
- "tool_call": "permission-required",
- "tool_call_id": "call_2",
- "tool_call_name": "write",
- "permission_request": {
- "toolName": "write",
- "serverName": "agent-filesystem",
- "permissionType": "write",
- "description": "Write access requires approval."
- }
-}
-```
-
-5. 提问请求:
-
-```json
-{
- "eventId": "evt_123",
- "tool_call": "question-required",
- "tool_call_id": "call_3",
- "tool_call_name": "deepchat_question",
- "question_request": {
- "question": "Select environment",
- "choices": ["dev", "prod"]
- }
-}
-```
-
-说明:renderer 暂不改,仅作为后续改造输入契约。
-
-### 2.5 Prompt 管线与调用策略更新(main)
-
-决策:固定 system prompt 管线,避免动态状态碎片化与缓存抖动。
-
-改动点:
-
-1. `src/main/presenter/toolPresenter/index.ts`
- - `buildToolSystemPrompt()` 增加 canonical 工具清单与“意图到工具”选择规则。
-2. `src/main/presenter/agentPresenter/message/messageBuilder.ts`
- - 保证 system prompt 固定顺序拼接。
-3. `src/main/presenter/agentPresenter/message/skillsPromptBuilder.ts`
- - `getSkillsAllowedTools()` 使用 canonicalized allowed tools。
-4. `src/main/lib/agentRuntime/systemEnvPromptBuilder.ts`
- - 统一生成 env prompt(模型、系统、仓库、AGENTS.md)。
-
-拼接顺序(固定):
-
-1. conversation `systemPrompt`
-2. Runtime 简要说明段(YoBrowser/后台进程能力说明,静态)
-3. Skills Prompt(metadata + active skills)
-4. Env Prompt(模型名/模型 ID/工作目录/git/platform/date/AGENTS.md 全文)
-5. Tooling Prompt(canonical 规则)
-
-约束:
-
-1. 不在 system prompt 注入 YoBrowser 当前 tab 或后台进程实时列表。
-2. `enhanceSystemPromptWithDateTime` 的运行态信息迁移至统一 env prompt。
-3. Tooling Prompt 保留独立段,不并入 env prompt。
-
-### 2.6 Loop 执行顺序收敛
-
-统一流程:
-
-1. LLM 产出 tool calls
-2. `batchPreCheckPermissions()` 批量权限预检
-3. 发出 `permission-required`(若有)并暂停 loop
-4. 逐个 tool 执行,发 `running -> end/error`
-5. 大输出按 offload 规则写入 session 文件并返回 stub
-6. 继续下一轮推理或结束
-
-## 3. 实施阶段
-
-### Phase 1:工具面切换(无兼容)
-
-1. 重命名并收敛 tool schemas + definitions。
-2. 切换 handler 分派逻辑。
-3. 删除旧工具名引用(包括测试、文档、skills 默认示例)。
-4. 回归确认 browser/skills/settings/question 工具定义与行为无变更。
-
-### Phase 2:Skills 映射
-
-1. 新增映射模块与单元测试。
-2. 接入 `getActiveSkillsAllowedTools()`。
-3. 在 skill sync 适配器层保持原始值,运行时归一化。
-
-### Phase 3:rg 增强
-
-1. `find` 引入 rg 分支(若尚未实现)。
-2. `grep` 完善 rg 结果结构化(命中文件/行号/上下文/截断)。
-3. 完善 fallback 与错误日志。
-
-### Phase 4:Prompt 与协议导出
-
-1. 新增统一 env prompt builder 并接入 messageBuilder。
-2. 调整 system prompt 固定拼接顺序(含 runtime 静态说明与 skills)。
-3. 更新 main 侧 tool prompt(仅 canonical + 调用规则)。
-4. 增加消息/事件契约测试,确保字段不回归。
-5. 回退 `allowParallel` 相关参数/逻辑与测试预期。
-
-## 4. 数据与配置影响
-
-1. **破坏性变化(明确)**:旧工具名不可再调用。
-2. 不涉及数据库 schema 迁移。
-3. Skills 的 `allowedTools` 原始存储不强制改写,仅在运行时归一化。
-
-## 5. 测试策略
-
-### 5.1 单元测试
-
-1. `AgentToolManager`:
- - 工具定义仅包含 canonical 名。
- - 旧工具名调用报错。
- - canonical 参数 schema 校验正确。
-2. Skills 映射:
- - Claude Code 常见工具名映射正确。
- - 未知工具产生 warning。
-3. `AgentFileSystemHandler`:
- - `find/grep` 在 rg 可用与不可用分支均可运行。
-4. Prompt 组装:
- - tool prompt 仅包含 canonical 工具名。
- - 不包含旧工具名提示。
- - system prompt 顺序固定且可断言。
- - env prompt 包含 AGENTS.md 全文与关键环境字段。
-
-### 5.2 集成测试
-
-1. loop 工具调用事件序列:start/running/end。
-2. permission-required 负载完整性。
-3. offload 与大输出行为在新工具名下仍生效。
-4. toolResult envelope 字段(`ok/summary/data/meta`)在关键工具路径可观测。
-
-## 6. 风险与缓解
-
-1. 风险:旧 prompt 或 skill 仍调用旧工具名导致失败。
- 缓解:在系统 prompt 与 skills metadata prompt 中明确仅 canonical 名。
-
-2. 风险:不同平台 rg 参数兼容性差异。
- 缓解:统一封装 rg 参数构造,Windows/Linux/macOS 加测试样例。
-
-3. 风险:skills 映射冲突导致过度归并。
- 缓解:映射表版本化,保留原值告警,必要时支持精细映射策略。
-
-## 7. 质量门槛(DoD)
-
-1. `pnpm run format`
-2. `pnpm run lint`
-3. `pnpm run typecheck`
-4. 关键 main 测试通过(tool/loop/permission/skills 映射相关)
diff --git a/docs/archives/agent-tooling-v2/spec.md b/docs/archives/agent-tooling-v2/spec.md
deleted file mode 100644
index 44fc64097..000000000
--- a/docs/archives/agent-tooling-v2/spec.md
+++ /dev/null
@@ -1,300 +0,0 @@
-# Agent Tooling V2(Main Loop 优先)
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 背景
-
-当前 main 层 agent 工具体系存在以下问题:
-
-1. 工具命名与主流 Agent 生态不一致(大量下划线命名、语义重叠)。
-2. 文件工具数量偏多且参数风格不统一,模型选工具与组参成本高。
-3. Skills 来源多样(尤其 Claude Code),`allowedTools` 与 DeepChat 当前工具名不直接兼容。
-4. 文件匹配/检索能力未充分利用 `rg`,在大型仓库下性能和结果质量不稳定。
-5. loop 已基本可运行,但“对 renderer 输出的消息/事件格式”尚未形成明确、稳定的主协议。
-
-本规格聚焦 main 层:先让 loop + tool 协议稳定、清晰、可推理,再推进 renderer 适配。
-
-## 目标
-
-1. **工具面收敛**:仅保留 8 个主工具(不兼容旧名)。
-2. **Skills 工具映射**:引入标准映射层,重点支持 Claude Code 工具名。
-3. **文件匹配增强**:`find/grep` 优先使用 `rg`,大仓库性能可预期。
-4. **协议清晰**:导出 main 层稳定的消息格式与事件格式,供 renderer 后续接入。
-5. **Prompt 管线稳定**:system prompt 拼接顺序固定,避免随意动态段影响缓存命中。
-6. **环境信息统一**:模型/系统/仓库/AGENTS.md 信息统一由 `env prompt` 生成。
-
-## 非目标
-
-1. 本阶段不改 renderer 逻辑与 UI 交互。
-2. 不保留旧工具名的后向兼容(不做 alias call)。
-3. 不重构 MCP 协议与第三方 provider 的底层实现。
-4. 不引入新的权限类型(仍为 `read|write|all|command`)。
-5. 不在 system prompt 中注入 YoBrowser 当前 tab 明细或后台进程实时列表。
-
-## 范围边界(补充)
-
-本次优化仅覆盖:
-
-1. 文件操作工具(fs)
-2. runtime 工具(命令执行与后台进程管理)
-3. 上述工具在 main loop 中的调用、权限与事件输出
-
-本次**不改动**:
-
-1. browser 相关工具(如 `yo_browser_*`)
-2. skills 管理工具(如 `skill_list`、`skill_control`)
-3. DeepChat 设置类工具(如 `deepchat_settings_*`)
-4. 提问工具(`deepchat_question`)
-5. MCP 工具本体及其服务端协议
-
-## 工具集合(V2 Canonical)
-
-固定为以下 8 个工具:
-
-1. `read`
-2. `write`
-3. `edit`
-4. `find`
-5. `grep`
-6. `ls`
-7. `exec`
-8. `process`
-
-## Canonical 参数定义(V2)
-
-### A. 文件工具(fs)
-
-| 工具 | 必填参数 | 可选参数 | 说明 |
-|---|---|---|---|
-| `read` | `path: string` | `offset?: number`, `limit?: number` | 单文件读取;大文件分页读取。 |
-| `write` | `path: string`, `content: string` | 无 | 创建或覆盖文件。 |
-| `edit` | `path: string`, `oldText: string`, `newText: string` | `replaceAll?: boolean` | 精确文本替换;仅接受 canonical 参数名。 |
-| `find` | `pattern: string` | `path?: string`, `maxResults?: number`, `exclude?: string[]` | 文件匹配,优先 `rg --files`。 |
-| `grep` | `pattern: string` | `path?: string`, `filePattern?: string`, `caseSensitive?: boolean`, `contextLines?: number`, `maxResults?: number` | 内容搜索,优先 `rg`。 |
-| `ls` | `path: string` | `depth?: number` | 列目录(默认浅层)。 |
-
-### B. 运行时工具(runtime)
-
-| 工具 | 必填参数 | 可选参数 | 说明 |
-|---|---|---|---|
-| `exec` | `command: string` | `cwd?: string`, `timeoutMs?: number`, `background?: boolean`, `yieldMs?: number` | 命令执行;前台仅等待 yield 窗口,超时后自动转后台并返回 `sessionId`。 |
-| `process` | `action: enum` | `sessionId?: string`, `offset?: number`, `limit?: number`, `data?: string`, `eof?: boolean` | 后台会话管理(list/poll/log/write/kill/clear/remove)。 |
-
-约束:
-
-1. 不再接受旧参数别名(如 `old_string/new_string`),只保留 canonical 参数名。
-2. 参数校验失败必须返回结构化错误,不进入工具执行。
-3. `exec` 不引入 `allowParallel` 参数。
-
-## 工具返回格式(统一)
-
-所有 canonical 工具返回统一 envelope(`content` 可读摘要 + `rawData.toolResult` 结构化数据):
-
-```json
-{
- "ok": true,
- "summary": "Found 12 matches in 3 files",
- "data": {},
- "meta": {
- "truncated": false
- }
-}
-```
-
-错误格式:
-
-```json
-{
- "ok": false,
- "error": {
- "code": "INVALID_ARGUMENT",
- "message": "path is required"
- }
-}
-```
-
-说明:
-
-1. `content` 供模型快速理解,`rawData.toolResult` 供 loop/renderer 稳定消费。
-2. `find/grep/read` 需包含分页或截断信息(如 `returned`, `total`, `nextOffset`)。
-
-## 待删除旧工具清单(用于边界确认)
-
-以下旧工具将从 agent 文件/运行时工具定义中移除:
-
-1. `read_file`
-2. `write_file`
-3. `list_directory`
-4. `create_directory`
-5. `move_files`
-6. `edit_text`
-7. `glob_search`
-8. `directory_tree`
-9. `get_file_info`
-10. `grep_search`
-11. `text_replace`
-12. `edit_file`
-13. `execute_command`
-
-说明:
-
-1. `process` 不删除,保留并归入 runtime canonical 集合。
-2. 上述删除仅针对本地 Agent 工具定义层,不影响外部 MCP server 自带同名工具。
-
-## Prompt 约束(main)
-
-目标:降低模型选错工具/组参错误率,减少多余调用。
-
-1. 系统提示中只暴露 canonical 工具名与参数摘要,不出现旧名。
-2. 增加工具选择规则(按意图):
- - 定位文件:`find` / `ls`
- - 搜索内容:`grep`
- - 读取内容:`read`
- - 精确修改:`edit`
- - 整体写入:`write`
- - 执行命令:`exec` + `process`
-3. 增加调用策略:
- - 先查找再读取,再修改(`find/grep -> read -> edit/write`)
- - 优先小步调用,避免一次返回超大输出
-4. 对无效工具名统一返回:`Unknown Agent tool: `,不做自动别名纠正。
-
-## System Prompt 组装顺序(V2.1)
-
-`conversation.settings.systemPrompt` 之后固定拼接顺序:
-
-1. **Runtime 简要说明段**(静态说明)
- - 仅说明 YoBrowser 能力和后台进程能力。
- - 不注入当前 tab 列表、当前 active tab、当前运行进程明细等动态快照。
-2. **Skills Prompt 段**
- - 含 skills metadata + active skills 内容。
-3. **Env Prompt 段**
- - 统一封装环境信息,格式稳定。
- - 包含:模型名、模型 ID、工作目录、是否 git 仓库、平台、日期。
- - 包含 `AGENTS.md` 的具体内容(全文)。
-4. **Tooling Prompt 段**
- - 继续保留独立工具调用规则段(canonical 工具名 + 推荐调用顺序)。
-
-说明:
-
-1. Env Prompt 统一由独立 builder 生成,不在多个模块分散拼接。
-2. 运行态动态信息不进 system prompt,避免提示词频繁变化影响缓存。
-
-Env Prompt 参考格式:
-
-```text
-You are powered by the model named .
-The exact model ID is /
-Here is some useful information about the environment you are running in:
-
-Working directory:
-Is directory a git repo: yes|no
-Platform:
-Today's date:
-
-
-
-
-Instructions from: /AGENTS.md
-
-```
-
-## 用户故事
-
-### US-1:模型能更稳定选对工具
-作为 agent 用户,我希望模型面对文件任务时优先在 6 个文件工具中做确定选择,而不是在多个重叠工具之间摇摆。
-
-### US-2:Claude Code Skills 可直接复用
-作为多工具用户,我希望导入 Claude Code skills 后,`allowed-tools` 能自动映射到 DeepChat 的 canonical 工具,不需要手工改名。
-
-### US-3:大型仓库下检索稳定
-作为 agent 用户,我希望 `find/grep` 在大仓库下保持高性能和一致结果,优先走 `rg`。
-
-### US-4:事件协议可作为 renderer 改造输入
-作为开发者,我希望 main 层先产出稳定的事件与消息格式,后续 renderer 可按协议接入,不再反复追 main 内部状态细节。
-
-## Skills 工具映射(重点:Claude Code)
-
-定义 canonical 映射(首批):
-
-| 外部工具名(Claude Code 常见) | DeepChat Canonical |
-|---|---|
-| `Read` | `read` |
-| `Write` | `write` |
-| `Edit` | `edit` |
-| `MultiEdit` | `edit` |
-| `Glob` | `find` |
-| `Grep` | `grep` |
-| `LS` | `ls` |
-| `Bash` | `exec` |
-
-说明:
-
-1. 该映射用于 `skills allowedTools` 归一化,不影响 MCP 原生工具名。
-2. 无法映射的工具名保留原值并标记 warning(不静默丢失)。
-3. 映射后再参与“工具可用性过滤”。
-
-## 文件匹配增强(rg 优先)
-
-1. `find`:优先 `rg --files` + `-g` 模式过滤(含排除规则),不可用时回退 `glob`。
-2. `grep`:优先 `rg`(支持行号、上下文、max results),不可用时回退 JS 扫描。
-3. 输出必须包含可消费的结构化元信息(命中数、文件数、截断标识)。
-4. 默认忽略目录保持统一:`.git`、`node_modules`、`dist`、`build`、`.next`(可扩展)。
-
-## 消息与事件格式(main 导出)
-
-本阶段定义并冻结 main 输出契约(renderer 暂不改):
-
-1. 仍通过 `STREAM_EVENTS.RESPONSE/ERROR/END` 发送。
-2. `tool_call` 状态集合保持:`start|running|update|end|error|permission-required|question-required`。
-3. 明确字段稳定性:
- - 必带:`eventId`
- - tool 事件必带:`tool_call_id`, `tool_call_name`
- - 权限事件必带:`permission_request.toolName/serverName/permissionType/description`
-4. 在 `docs/archives/agent-tooling-v2/plan.md` 给出字段级 schema 约束与 JSON 样例(text/reasoning/tool/permission/question/end)。
-
-## 约束
-
-1. 安全边界:文件访问必须受 workspace/approved paths/conversation session 限制。
-2. 权限门闩:遇到 permission-required 必须暂停,遵循已落地的 permission stabilization 语义。
-3. 输出控制:工具大输出继续遵循 offload guardrails,不向模型直接注入超大文本。
-
-## 验收标准
-
-### A. 工具面
-
-1. agent 工具定义列表只包含 8 个 canonical 工具名(加业务工具如 `deepchat_question`、skills 管理工具不在本条约束内)。
-2. 旧文件工具名(如 `read_file/write_file/edit_file/...`)不再出现在工具定义中。
-3. 调用旧工具名返回明确错误:`Unknown Agent tool`。
-4. `yo_browser_*`、`skill_*`、`deepchat_settings_*` 的可见性与行为保持不变。
-
-### B. Skills 映射
-
-1. Claude Code `allowed-tools` 输入可映射到 canonical 工具。
-2. `MultiEdit -> edit`、`Bash -> exec`、`Glob -> find` 等关键映射可通过测试验证。
-3. 未知工具名不会被静默吞掉,存在可观测 warning。
-
-### C. rg 增强
-
-1. `find` 在可用 `rg` 时走 `rg` 分支;`rg` 不可用时自动回退 `glob`。
-2. `grep` 在可用 `rg` 时走 `rg` 分支;`rg` 不可用时自动回退 JS。
-3. 大仓库场景下 `find/grep` 都有 `maxResults` 截断行为并返回截断信息。
-
-### D. 协议
-
-1. `STREAM_EVENTS.RESPONSE` 中 tool 相关字段满足 plan 中定义的 schema。
-2. `permission-required` 事件负载完整且可用于恢复执行链路。
-3. 主流程可导出标准消息样例(text/reasoning/tool/permission/question/end)。
-
-### E. 参数与 Prompt
-
-1. 所有 canonical 工具参数满足本 spec 的统一定义。
-2. 系统提示仅出现 canonical 工具名,不出现旧工具名或旧参数别名。
-3. 工具参数校验失败可观测(明确错误码/错误信息),且不执行工具副作用操作。
-4. system prompt 拼接顺序严格符合“V2.1 顺序”。
-5. system prompt 中不包含 YoBrowser/后台进程动态状态快照。
-6. env prompt 中包含模型名/模型 ID/工作目录/git 检测/platform/date/AGENTS.md 全文。
-
-## 开放问题
-
-无。
diff --git a/docs/archives/agent-tooling-v2/tasks.md b/docs/archives/agent-tooling-v2/tasks.md
deleted file mode 100644
index 30de05f6b..000000000
--- a/docs/archives/agent-tooling-v2/tasks.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Agent Tooling V2 Tasks(Prompt 管线与 Env Prompt)
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## T0 文档先行
-
-- [x] 更新 `spec.md`:增加 V2.1 system prompt 固定顺序与 env prompt 约束。
-- [x] 更新 `plan.md`:补充实现边界、顺序、回退 `allowParallel` 要求。
-- [x] 新建 `tasks.md`:形成可执行任务清单。
-
-## T1 回退并清理 `allowParallel`
-
-- [ ] 从 `src/main/presenter/agentPresenter/acp/agentToolManager.ts` 的 `exec` schema 删除 `allowParallel`。
-- [ ] 删除 `exec` description 中关于 `allowParallel` 的文案。
-- [ ] 删除 `exec` 前置并行守卫逻辑和相关辅助方法。
-- [ ] 清理/更新相关测试断言(若涉及)。
-
-## T2 新增统一 Env Prompt Builder
-
-- [ ] 新增 `src/main/lib/agentRuntime/systemEnvPromptBuilder.ts`。
-- [ ] 实现 `buildSystemEnvPrompt(...)`,输出固定格式:
- - 模型名 + 模型 ID
- - ``:workdir、git repo、platform、date
- - `` 空块
- - `Instructions from: /AGENTS.md`
- - AGENTS.md 全文(读取失败时输出可观测 fallback)
-- [ ] 实现 runtime 静态能力说明 builder(YoBrowser + terminal background)。
-
-## T3 调整 Message Builder 拼接顺序
-
-- [ ] 在 `src/main/presenter/agentPresenter/message/messageBuilder.ts` 按固定顺序拼接:
- 1. conversation `systemPrompt`
- 2. runtime 静态能力说明
- 3. skills prompt
- 4. env prompt
- 5. tooling prompt
-- [ ] 移除 YoBrowser 动态状态注入(tab/active tab 实时快照)。
-- [ ] 确认不注入后台进程动态列表。
-
-## T4 收敛 Prompt Enhancer 责任
-
-- [ ] 更新 `src/main/presenter/agentPresenter/utility/promptEnhancer.ts`,避免重复拼接 runtime/date/platform/workdir 信息。
-- [ ] runtime 环境信息统一由 `systemEnvPromptBuilder` 提供。
-
-## T5 测试
-
-- [ ] 新增/更新 `messageBuilder` 测试:断言 system prompt 段落顺序。
-- [ ] 新增 `systemEnvPromptBuilder` 测试:
- - git yes/no
- - AGENTS.md 存在/不存在
- - model name/model id 回退逻辑
-- [ ] 更新 `agentToolManager` 测试:确保 `allowParallel` 不可用。
-
-## T6 验证
-
-- [ ] `pnpm run format`
-- [ ] `pnpm run lint`
-- [ ] `pnpm run typecheck`
-- [ ] 跑关键 main 测试集并记录结果
diff --git a/docs/archives/agentpresenter-mvp-replacement/plan.md b/docs/archives/agentpresenter-mvp-replacement/plan.md
deleted file mode 100644
index 7a69dc7d6..000000000
--- a/docs/archives/agentpresenter-mvp-replacement/plan.md
+++ /dev/null
@@ -1,151 +0,0 @@
-# AgentPresenter 全量替换(MVP)实施计划
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 当前基线(Updated 2026-02-28)
-
-1. 新旧双栈并存:`agentSessionPresenter/agentRuntimePresenter` 与旧 `sessionPresenter/useChatStore` 同时存在。
-2. 新 loop 已可运行,**streaming 和 message persistence 已完成**,但**权限流程完全缺失**。
-3. 产品方向已确定:MVP 先替换核心能力,再完成 chat 模式彻底移除。
-4. **关键发现**:`agentRuntimePresenter/dispatch.ts` 的 `executeTools()` 直接调用工具,**无任何权限检查**。
-
-## 2. 核心架构决策
-
-1. 会话真源:`new_sessions + deepchat_sessions`。
-2. 消息真源:`deepchat_messages`。
-3. 主执行链路:`agentSessionPresenter -> agentRuntimePresenter`。
-4. 新 UI 页面不再依赖 `useChatStore` 与旧 `sessionPresenter` 主流程。
-5. variants 本轮下线,fork 保留为唯一分叉能力。
-
-## 3. 权限与数据模型决策
-
-### 3.1 Session 权限模式
-
-- `new_sessions` 增加 `permission_mode`:`default | full`,默认 `default`。
-- `permission_mode` 为 session 级别配置,不是全局配置。
-
-### 3.2 Full access 规则
-
-- 启用前置条件:`session.projectDir` 必须非空。
-- 若 `projectDir` 为空,UI 禁用 `Full access` 并提示先绑定 workspace。
-- `Full access` 仅自动通过 `projectDir` 内请求,越界请求统一拒绝。
-
-### 3.3 Default 规则
-
-- 走显式权限确认流程。
-- 白名单粒度:`sessionId + toolName + pathPattern`。
-- 判定顺序:先 `projectDir` 边界校验,再执行白名单匹配。
-
-## 4. 关键能力设计
-
-### 4.1 Workspace 绑定
-
-1. 工具执行上下文绑定 `session.projectDir`。
-2. 工具链路统一传递 `conversationId = sessionId`。
-3. 权限消息、审批、执行回执必须在同一 `sessionId` 闭环。
-
-### 4.2 编辑历史 user 消息
-
-1. API:`editUserMessage(sessionId, messageId, newContent)`。
-2. 行为:编辑后截断目标消息后的全部消息。
-3. 随后自动 regenerate,生成新的 assistant 消息。
-
-### 4.3 Retry/Regenerate(无 variants)
-
-1. API:`retryAssistantMessage(sessionId, messageId)`。
-2. 行为:不创建 variants;仅追加 assistant 消息。
-3. 使用消息边界标记避免旧分支内容污染上下文。
-
-### 4.4 Fork
-
-1. API:`forkSessionFromMessage(sessionId, messageId)`。
-2. 切点定义:从“当前 assistant 消息(含它)”截取。
-3. 新 session 继承必要上下文后可继续对话。
-
-## 5. 分阶段迁移
-
-### Phase 0:稳定主链路
-
-1. 清理新 UI 对 `useChatStore` 的依赖点。
-2. active session 查询与事件分发统一到 `agentSessionPresenter`。
-3. 建立最小回归测试基线。
-
-### Phase 1:权限 + Workspace(MVP 核心)
-
-1. 打通 `ChatStatusBar` 权限模式选择到 session 持久化。
-2. 实现 `Default` 权限审批与白名单命中。
-3. 实现 `Full access` 自动放行 + `projectDir` 越界拒绝。
-
-### Phase 2:消息操作(MVP 核心)
-
-1. 实现历史 user 消息编辑(截断+再生成)。
-2. 实现 retry/regenerate 追加 assistant(无 variants)。
-3. 实现 fork(含当前 assistant)。
-
-### Phase 3:设置收敛
-
-1. conversation settings UI 与逻辑下线。
-2. agent 默认配置在 session 中落地。
-3. 清理 legacy settings 读取/写入路径。
-
-### Phase 4:chat 模式清理
-
-1. 类型层移除 `chat`。
-2. UI 与 presenter 中移除 chat 分支。
-3. 完成兼容迁移后删除残留代码。
-
-## 6. IPC 与类型面
-
-1. `IAgentSessionPresenter` 扩展:
- - `setSessionPermissionMode`
- - `editUserMessage`
- - `retryAssistantMessage`
- - `forkSessionFromMessage`
-2. shared types 补充:
- - `PermissionMode`
- - `PermissionWhitelistRule`(含 `toolName` 与 `pathPattern`)
-3. preload 暴露新增方法,保持 typed IPC。
-
-## 7. 测试策略
-
-1. Main 单测:权限边界、白名单匹配、编辑/重试/fork 行为。
-2. 集成测试:从 UI 发起到 tool 执行完整链路。
-3. 迁移回归:旧 session 与旧 chat 数据可正常打开并升级。
-
-## 8. 风险与缓解
-
-1. 权限误放行风险:main 进程集中校验,默认 deny。
-2. 上下文污染风险:编辑/重试后强制消息边界重算。
-3. 双栈耦合风险:每阶段明确“唯一真源”,禁止双写。
-
-## 9. 质量门槛
-
-1. `pnpm run format`
-2. `pnpm run lint`
-3. `pnpm run typecheck`
-4. 关键单测与集成测试通过后进入下一阶段
-
----
-
-## 10. Implementation Notes (2026-02-28)
-
-**Critical Discovery**: Permission flow is completely missing from new architecture.
-
-**Current State**:
-- ✅ Streaming infrastructure: COMPLETE
-- ✅ Message persistence: COMPLETE
-- ✅ Session management: COMPLETE
-- ❌ Permission flow: NOT STARTED (CRITICAL)
-- ❌ Message operations (edit/retry/fork): NOT STARTED
-- ❌ Session configuration: PARTIAL (missing advanced options)
-
-**Immediate Next Steps**:
-1. Create `PermissionChecker` class in `agentRuntimePresenter/`
-2. Modify `executeTools()` in `dispatch.ts` to check permissions before tool calls
-3. Add `handlePermissionResponse()` IPC method to `agentSessionPresenter`
-4. Update `ChatStatusBar.vue` to show permission mode dropdown
-5. Add `permission_mode` column to `new_sessions` table
-6. Create `permission_whitelists` table for session-scoped whitelists
-
-See `gap-analysis.md` for complete details.
diff --git a/docs/archives/agentpresenter-mvp-replacement/spec.md b/docs/archives/agentpresenter-mvp-replacement/spec.md
deleted file mode 100644
index 1f67a2405..000000000
--- a/docs/archives/agentpresenter-mvp-replacement/spec.md
+++ /dev/null
@@ -1,163 +0,0 @@
-# AgentPresenter 全量替换(MVP)规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 概述
-
-以 `agentRuntimePresenter` 新 loop 为唯一核心,分阶段替换旧 chat 体系。MVP 先完成权限、workspace 绑定、消息编辑、retry/regenerate、fork 五个核心能力,再推进设置收敛与 chat 模式清理。
-
-## 背景与目标
-
-1. 当前新旧链路并存,存在行为不一致与维护成本。
-2. 权限模型需要与新 UI 对齐,并提供明确边界。
-3. conversation settings 需要收敛到 agent 默认配置 + session 落地。
-4. 最终目标是彻底移除 chat 模式,仅保留 agent 路径。
-
-## 用户故事
-
-### US-1:权限模式选择
-
-作为用户,我希望在状态栏选择 `Default` 或 `Full access`,并且配置在当前 session 生效。
-
-### US-2:Full access 边界
-
-作为用户,我希望 `Full access` 仍受限制,只允许在当前 session 的 `projectDir` 内执行敏感操作。
-
-### US-3:编辑历史 user 消息
-
-作为用户,我编辑历史 user 消息后,系统应截断后续消息并基于新内容重新生成。
-
-### US-4:Retry/Regenerate(无 variants)
-
-作为用户,我执行 retry/regenerate 时,系统应追加新的 assistant 消息,不走 variants 分支。
-
-### US-5:Fork
-
-作为用户,我希望从“当前 assistant 消息(含它)”切 fork,创建可继续对话的新 session。
-
-### US-6:统一 agent 体验
-
-作为用户,我希望不再感知 chat 模式,所有会话统一使用 agent 能力。
-
-## 验收标准
-
-### A. 权限模式
-
-- [x] `ChatStatusBar` 可选择 `Default` 与 `Full access`。
-- [x] 权限模式持久化在 session 维度。
-- [x] 当 `session.projectDir` 为空时,`Full access` 不可选并提示先绑定 workspace。
-- [x] `Default` 走权限确认流程,白名单按 `session` 维度隔离。
-- [x] `Default` 白名单匹配粒度为 `toolName + pathPattern`。
-- [x] `Full access` 自动通过请求,但任何越出 `projectDir` 的操作必须拒绝。
-
-**Implementation Notes** (added 2026-02-28):
-- Add `permission_mode TEXT DEFAULT 'default'` to `new_sessions` table
-- ChatStatusBar currently shows read-only "Default permissions" button - must convert to dropdown
-- NewThreadPage must pass `permissionMode` to `createSession()`
-- Backend must enforce `projectDir` boundary in tool execution
-
-### B. Workspace 绑定与上下文
-
-- [x] 工具执行上下文绑定 `session.projectDir`。
-- [x] 工具调用链路统一传递 `conversationId = sessionId`。
-- [x] 权限判定与消息归属基于同一 `sessionId`。
-
-**Implementation Notes** (added 2026-02-28):
-- `agentSessionPresenter.createSession()` already passes `projectDir` to session manager
-- `agentRuntimePresenter.processStream()` uses `sessionId` throughout
-- CRITICAL GAP: `executeTools()` in dispatch.ts does NOT check permissions before calling tools
-- Must add `PermissionChecker` class and integrate before tool execution
-
-### C. 编辑历史 user 消息
-
-- [ ] 仅允许编辑 user 消息。
-- [ ] 编辑后删除该消息之后的所有消息。
-- [ ] 自动触发 regenerate,生成新的 assistant 结果。
-
-**Implementation Notes** (added 2026-02-28):
-- NOT YET IMPLEMENTED in new architecture
-- Requires new IPC: `editUserMessage(sessionId, messageId, newContent)`
-- Must delete messages with `orderSeq > editedMessage.orderSeq`
-- Must trigger `processMessage()` to regenerate
-- Frontend: add edit action to user message context menu
-
-### D. Retry/Regenerate(无 variants)
-
-- [ ] 不提供 variants 路径。
-- [ ] 每次 retry/regenerate 追加新的 assistant 消息。
-- [ ] 上下文边界正确,避免被替代消息污染后续生成。
-
-**Implementation Notes** (added 2026-02-28):
-- NOT YET IMPLEMENTED in new architecture
-- Requires new IPC: `retryMessage(sessionId, messageId)`
-- Must create new assistant message (not replace)
-- Must manage message boundaries correctly
-- Old architecture: `agentPresenter.retryMessage()` exists but uses variants
-- New implementation must skip variants path
-
-### E. Fork
-
-- [ ] 支持从 assistant 消息发起 fork。
-- [ ] fork 切点包含当前 assistant 消息本身。
-- [ ] fork 后新 session 消息序列可继续生成。
-
-**Implementation Notes** (added 2026-02-28):
-- NOT YET IMPLEMENTED in new architecture
-- Requires new IPC: `forkSessionFromMessage(sessionId, messageId)`
-- Must copy messages up to and including the fork point
-- Must create new session with copied context
-- Old architecture had fork in sessionPresenter
-
-### F. 设置收敛
-
-- [ ] conversation settings 入口下线。
-- [ ] agent 默认配置生效。
-- [ ] 运行时具体配置数据落到 session。
-
-**Implementation Notes** (added 2026-02-28):
-- PARTIALLY IMPLEMENTED: new architecture uses agent defaults
-- MISSING: session-level configuration (temperature, contextLength, maxTokens)
-- `agentSessionPresenter.createSession()` only accepts providerId/modelId
-- Must extend CreateSessionInput to include all config options
-- Old architecture: CONVERSATION_SETTINGS had 12+ fields
-- New architecture: must decide which settings to persist at session level
-
-### G. 架构替换
-
-- [x] 新 UI 主链路不依赖 `useChatStore` 与旧 `sessionPresenter`。
-- [x] `agentSessionPresenter + agentRuntimePresenter` 成为唯一主执行链路。
-
-**Implementation Notes** (added 2026-02-28):
-- MOSTLY COMPLETE: New UI uses sessionStore/messageStore
-- ChatStatusBar still imports `useChatStore` for config updates (line 11-12)
-- Should migrate ChatStatusBar to use sessionStore config instead
-- Core streaming/persistence fully migrated to new architecture
-
-### H. chat 模式清理
-
-- [ ] 类型、UI、主流程中不再暴露 `chat` 模式。
-- [ ] 旧 chat 数据有明确兼容迁移策略(静默升级或等价兼容)。
-
-**Implementation Notes** (added 2026-02-28):
-- NOT YET STARTED
-- Old tables (conversations, messages) still exist
-- No migration path defined
-- Must decide: migrate old data to new tables? Or maintain dual-read compatibility?
-- Recommended: dual-read during transition, migrate on-demand when old session opened
-
-## 非目标
-
-1. 本轮不恢复 variants。
-2. 本轮不扩展与替换目标无关的 MCP 新能力。
-3. 本轮不做大规模视觉改版。
-
-## 约束
-
-1. MVP 优先,分阶段替换,避免一次性重构。
-2. 任何自动放行都必须受 `projectDir` 边界约束。
-3. 不引入新链路反向依赖旧 chat store。
-
-## 开放问题
-
-无。本轮关键决策已确认。
diff --git a/docs/archives/agentpresenter-mvp-replacement/tasks.md b/docs/archives/agentpresenter-mvp-replacement/tasks.md
deleted file mode 100644
index d3d5c1efa..000000000
--- a/docs/archives/agentpresenter-mvp-replacement/tasks.md
+++ /dev/null
@@ -1,247 +0,0 @@
-# AgentPresenter 全量替换(MVP)任务清单
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-**Updated**: 2026-02-28 - Added implementation notes and priority markers
-
-## T0 规格冻结
-
-- [x] 移除并确认无 `[NEEDS CLARIFICATION]`。
-- [x] 锁定 MVP 范围:权限、workspace、编辑、retry/regenerate、fork。
-- [x] 明确本轮不做 variants。
-
-**Status**: COMPLETE - See `gap-analysis.md` for full specification
-
-## T1 Session 权限模型 🔴 P0 CRITICAL
-
-- [ ] 为 `new_sessions` 增加 `permission_mode` 字段(默认 `default`)。
- - **SQL**: `ALTER TABLE new_sessions ADD COLUMN permission_mode TEXT DEFAULT 'default'`
- - **File**: `src/main/presenter/sqlitePresenter/tables/newSessionsTable.ts`
-- [ ] session manager 增加读写 `permission_mode` 能力。
- - **File**: `src/main/presenter/agentSessionPresenter/sessionManager.ts`
- - Add `permissionMode` to `create()` method
- - Add getter/setter methods
-- [ ] 补齐迁移与回填策略测试。
- - Existing sessions default to 'default' mode
-
-**Priority**: P0 - MVP Blocker
-**Estimated**: 2-3 hours
-**Status**: NOT STARTED
-
-## T2 ChatStatusBar 权限接入 🔴 P0 CRITICAL
-
-- [ ] `ChatStatusBar` 接入 `Default/Full access` 选择与展示。
- - **Current**: Line 91 shows read-only button "Default permissions"
- - **Required**: Convert to DropdownMenu with Default/Full options
- - **File**: `src/renderer/src/components/chat/ChatStatusBar.vue`
-- [ ] session `projectDir` 为空时禁用 `Full access`。
- - Check `sessionStore.activeSession.projectDir`
- - Disable dropdown option when empty
-- [ ] `Full access` 禁用态提示“先绑定 workspace"。
- - Add tooltip on disabled option
-- [ ] 选择结果写回 session 并可恢复。
- - Call `agentSessionPresenter.updateSessionPermissionMode()` (NEW IPC)
- - Load on session activation
-
-**Priority**: P0 - MVP Blocker
-**Estimated**: 4-6 hours
-**Status**: NOT STARTED
-**Dependencies**: T1 (session permission_mode field)
-
-## T3 Default 权限流程 🔴 P0 CRITICAL
-
-- [ ] 新链路接入权限请求消息块与审批动作。
- - **File**: `src/main/presenter/agentRuntimePresenter/dispatch.ts`
- - Modify `executeTools()` to check permissions BEFORE calling tools
- - Create permission request block: `{ type: 'action', action_type: 'tool_call_permission', ... }`
- - Emit `STREAM_EVENTS.RESPONSE` with permission block
- - PAUSE stream processing (set session status to 'paused')
-- [ ] 实现 session 级白名单存储与查询。
- - **CREATE**: `src/main/presenter/agentRuntimePresenter/permissionChecker.ts`
- - Create `permission_whitelists` table: `{ sessionId, toolName, pathPattern, permissionType, createdAt }`
- - Query: `SELECT * FROM permission_whitelists WHERE sessionId = ? AND toolName = ?`
-- [ ] 白名单匹配规则为 `toolName + pathPattern`。
- - Match exact tool name
- - Match path pattern (glob or regex)
- - Check `remember: true` from previous approvals
-- [ ] 补齐白名单命中与未命中测试。
- - Unit tests for PermissionChecker
- - Integration tests for full permission flow
-
-**Priority**: P0 - MVP Blocker
-**Estimated**: 2-3 days
-**Status**: NOT STARTED
-**Dependencies**: T1
-**Key Files**:
-- CREATE: `src/main/presenter/agentRuntimePresenter/permissionChecker.ts`
-- MODIFY: `src/main/presenter/agentRuntimePresenter/dispatch.ts`
-- MODIFY: `src/main/presenter/agentSessionPresenter/index.ts`
-
-## T4 Full access 边界控制 🔴 P0 CRITICAL
-
-- [ ] 实现自动通过逻辑(仅对 `projectDir` 内操作)。
- - **File**: `src/main/presenter/agentRuntimePresenter/permissionChecker.ts`
- - Check `session.permission_mode === 'full'`
- - If full access: auto-approve tools within projectDir
-- [ ] 实现路径归一化与越界检测。
- - Normalize paths (resolve `..`, `.`, symlinks)
- - Check if resolved path starts with `session.projectDir`
- - Reject if outside boundary
-- [ ] 越界请求返回拒绝事件与可见反馈。
- - Add error block to message
- - Emit `STREAM_EVENTS.ERROR` with "Operation outside project directory" message
-- [ ] 补齐越界绕过测试(相对路径、软链接、`..`)。
- - Test cases: `../secret.txt`, `./../../etc/passwd`, symlink escapes
-
-**Priority**: P0 - MVP Blocker
-**Estimated**: 1-2 days
-**Status**: NOT STARTED
-**Dependencies**: T1, T3
-
-## T5 Workspace 与会话绑定 ✅ P0 COMPLETE
-
-- [x] 工具执行上下文绑定 `session.projectDir`。
- - **Status**: COMPLETE - `agentSessionPresenter.createSession()` passes projectDir
-- [x] 统一传递 `conversationId = sessionId`。
- - **Status**: COMPLETE - `agentRuntimePresenter.processStream()` uses sessionId throughout
-- [x] 权限与消息归属链路统一按 `sessionId` 路由。
- - **Status**: COMPLETE - All new architecture uses sessionId
-
-**Priority**: P0 - COMPLETE
-**Status**: ✅ DONE
-**Notes**: Infrastructure is solid, just needs permission integration (T3, T4)
-
-## T6 编辑历史 user 消息 🟡 P1 HIGH
-
-- [ ] 实现 `editUserMessage(sessionId, messageId, newContent)`。
- - **File**: `src/main/presenter/agentSessionPresenter/index.ts`
- - **IPC**: Add `editUserMessage(sessionId, messageId, newContent)` method
- - Validate: only user messages can be edited
-- [ ] 执行"编辑点后消息截断"。
- - **File**: `src/main/presenter/agentRuntimePresenter/messageStore.ts`
- - Add `deleteMessagesAfter(messageId)` method
- - Delete all messages with `orderSeq > editedMessage.orderSeq`
-- [ ] 自动触发 regenerate 并同步状态。
- - Call `processMessage(sessionId, newContent)` after edit
- - Set session status to 'generating'
-- [ ] 补齐编辑后上下文正确性测试。
- - Test: edit message, verify subsequent messages deleted
- - Test: regenerate produces new assistant response
- - Test: message boundaries correct
-
-**Priority**: P1 - High (Core functionality)
-**Estimated**: 1-2 days
-**Status**: NOT STARTED
-**Dependencies**: T5 (session binding)
-**Frontend**: Add edit action to user message context menu in MessageList
-
-## T7 Retry/Regenerate(无 variants) 🟡 P1 HIGH
-
-- [ ] 移除或短路 variants 路径。
- - **Note**: New architecture doesn't have variants - already clean
- - Old: `agentPresenter.retryMessage()` created variants
- - New: Must create NEW assistant message (not replace)
-- [ ] 实现 retry/regenerate 追加 assistant 消息。
- - **File**: `src/main/presenter/agentSessionPresenter/index.ts`
- - **IPC**: Add `retryMessage(sessionId, messageId)` method
- - Find the assistant message by messageId
- - Create new assistant message with same context
- - Call `processStream()` to generate new response
-- [ ] 使用消息边界控制上下文收敛。
- - Mark message boundary to avoid context pollution
- - Ensure only messages up to retry point are used
-- [ ] 补齐多次 retry 的上下文一致性测试。
- - Test: retry 3 times → 3 assistant messages created
- - Test: context uses correct message history
- - Test: each retry independent
-
-**Priority**: P1 - High (Core functionality)
-**Estimated**: 1-2 days
-**Status**: NOT STARTED
-**Dependencies**: T5 (session binding)
-**Frontend**: Add retry action to assistant message context menu
-
-## T8 Fork 🟡 P1 HIGH
-
-- [ ] 实现 `forkSessionFromMessage(sessionId, messageId)`。
- - **File**: `src/main/presenter/agentSessionPresenter/index.ts`
- - **IPC**: Add `forkSessionFromMessage(sessionId, messageId)` method
- - Get all messages up to and including messageId
- - Create new session with copied messages
- - Copy session config (provider, model, permissionMode, etc.)
-- [ ] 切点包含当前 assistant 消息本身。
- - Include the assistant message at fork point
- - New session can continue from that point
-- [ ] fork 后新 session 可继续发送与生成。
- - Initialize new session with agentRuntimePresenter
- - Enable sending messages and generating responses
-- [ ] 补齐 fork 前后消息隔离测试。
- - Test: fork creates independent session
- - Test: messages in forked session don't affect original
- - Test: can continue conversation in forked session
-
-**Priority**: P1 - High (Core functionality)
-**Estimated**: 1-2 days
-**Status**: NOT STARTED
-**Dependencies**: T1 (session model), T5 (session binding)
-**Frontend**: Add fork action to assistant message context menu
-
-## T9 设置收敛 🟢 P2 MEDIUM
-
-- [ ] 下线 conversation settings 入口与依赖逻辑。
- - **Status**: PARTIAL - old settings still exist in parallel
- - Remove conversation settings UI from ChatPage
- - Migrate to session-level config
-- [ ] 将 agent 默认配置下沉到 session 存储。
- - **File**: `src/main/presenter/agentSessionPresenter/index.ts`
- - Extend `CreateSessionInput` to include:
- - `temperature?: number`
- - `contextLength?: number`
- - `maxTokens?: number`
- - `systemPrompt?: string`
- - Store in session record or separate config table
-- [ ] 清理 legacy settings 读取/写入路径。
- - Remove references to old CONVERSATION_SETTINGS
- - Migrate to new session config
-
-**Priority**: P2 - Medium (Can use defaults for MVP)
-**Estimated**: 3-5 days
-**Status**: PARTIALLY COMPLETE - defaults work, advanced config missing
-**Dependencies**: T1 (session model extension)
-
-## T10 移除 chat 模式 🟢 P2 MEDIUM
-
-- [ ] 类型层移除 `chat`。
- - Remove `chatMode` from types
- - Remove legacy chat-specific code paths
-- [ ] presenter/UI 中移除 chat 分支。
- - Clean up agentPresenter/sessionPresenter
- - Remove chat mode conditionals
-- [ ] 旧 chat 数据兼容迁移验证通过。
- - **Strategy**: Dual-read during transition
- - When old session opened, migrate on-demand to new tables
- - Or maintain read-only compatibility layer
-
-**Priority**: P2 - Medium (Can coexist for now)
-**Estimated**: 1 week
-**Status**: NOT STARTED
-**Dependencies**: T6, T7, T8 (message operations)
-**Risk**: Breaking old session compatibility - must test thoroughly
-
-## T11 质量门槛
-
-- [ ] `pnpm run format`
-- [ ] `pnpm run lint`
-- [ ] `pnpm run typecheck`
-- [ ] 关键单测与集成测试通过
-
-**Required Tests**:
-1. Permission flow integration test
-2. Whitelist matching test
-3. Full access boundary test
-4. Edit message test
-5. Retry/regenerate test
-6. Fork session test
-
-**Status**: PENDING - depends on T1-T10 completion
diff --git a/docs/archives/ai-sdk-runtime/plan.md b/docs/archives/ai-sdk-runtime/plan.md
deleted file mode 100644
index 343880413..000000000
--- a/docs/archives/ai-sdk-runtime/plan.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# AI SDK Runtime Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-Status: completed, rollback retired. See [../legacy-llm-provider-runtime-retirement/plan.md](../legacy-llm-provider-runtime-retirement/plan.md).
-
-1. Introduce shared AI SDK runtime modules without changing upper-layer interfaces.
-2. Migrate OpenAI-compatible and OpenAI responses providers first.
-3. Migrate Anthropic / Gemini / Vertex / Bedrock / Ollama to the shared runtime.
-4. Keep routing providers (`new-api`, `zenmux`) as thin delegates over migrated providers.
-5. Freeze `LLMCoreStreamEvent` behavior with adapter-focused tests.
-6. Retire the rollback path and delete legacy state machines.
diff --git a/docs/archives/ai-sdk-runtime/spec.md b/docs/archives/ai-sdk-runtime/spec.md
deleted file mode 100644
index d25ba1c2a..000000000
--- a/docs/archives/ai-sdk-runtime/spec.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# AI SDK Runtime Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Status
-
-Completed in commit `4c8345a7`.
-
-As of `2026-04-11`, the rollback path is retired. See [../legacy-llm-provider-runtime-retirement/spec.md](../legacy-llm-provider-runtime-retirement/spec.md).
-
-## Goal
-
-Unify DeepChat's low-level LLM request pipeline on Vercel AI SDK while keeping the upper-layer contracts unchanged:
-
-- `BaseLLMProvider`
-- `LLMProviderPresenter`
-- `LLMCoreStreamEvent`
-- existing provider IDs, model configs, and conversation history
-
-The AI SDK runtime is the only remaining implementation.
-
-## Non-Negotiable Compatibility
-
-- No functional regression in text streaming, reasoning streaming, tool call streaming, image output, prompt cache, proxy handling, request tracing, routing, and embeddings.
-- `LLMCoreStreamEvent` event names, field names, and stop reasons remain unchanged.
-- Existing `function_call_record` history must stay reusable across providers.
-- Existing provider list / model list / provider check / key status responsibilities remain in provider classes.
-
-## Runtime Mode
-
-- Single runtime: `ai-sdk`
-- `DEEPCHAT_LLM_RUNTIME` has been removed
-- config setting `llmRuntimeMode` has been removed
-
-## Scope
-
-Shared runtime under `src/main/presenter/llmProviderPresenter/aiSdk/` provides:
-
-- provider factory
-- model / message mapper
-- MCP tool mapper
-- streaming adapter
-- image runtime
-- embedding runtime
-- provider-options mapper
-- reasoning middleware
-- legacy function-call compatibility middleware
-
-## Provider Rollout
-
-Phase 1:
-
-- `OpenAICompatibleProvider`
-- `OpenAIResponsesProvider`
-- all `extends OpenAICompatibleProvider` providers
-
-Phase 2:
-
-- `AnthropicProvider`
-- `GeminiProvider`
-- `VertexProvider`
-- `AwsBedrockProvider`
-- `OllamaProvider`
-
-Phase 3:
-
-- `NewApiProvider`
-- `ZenmuxProvider`
-
-Out of scope for first unification pass:
-
-- `AcpProvider`
-- `VoiceAIProvider`
-
-## Validation Matrix
-
-- pure text
-- reasoning native
-- reasoning via ``
-- native tool streaming
-- legacy `` fallback
-- multi-tool history replay
-- image input
-- image output
-- usage mapping
-- prompt cache mapping
-- proxy / trace / abort
-- embeddings
-- retired rollback path verification
-
-## Legacy Removal Exit Criteria
-
-- AI SDK runtime passes the provider regression matrix
-- duplicated legacy stream parsers / tool parsers have no remaining callers
-- retirement is documented in [../legacy-llm-provider-runtime-retirement/spec.md](../legacy-llm-provider-runtime-retirement/spec.md)
diff --git a/docs/archives/ai-sdk-runtime/tasks.md b/docs/archives/ai-sdk-runtime/tasks.md
deleted file mode 100644
index 98e3f7da0..000000000
--- a/docs/archives/ai-sdk-runtime/tasks.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# AI SDK Runtime Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-- [x] Freeze migration scope in SDD docs.
-- [x] Add shared AI SDK runtime modules. (implemented in 4c8345a7)
-- [x] Integrate OpenAI-compatible runtime path. (implemented in 4c8345a7)
-- [x] Integrate OpenAI responses runtime path. (implemented in 4c8345a7)
-- [x] Integrate Anthropic runtime path. (implemented in 4c8345a7)
-- [x] Integrate Gemini runtime path. (implemented in 4c8345a7)
-- [x] Integrate Vertex runtime path. (implemented in 4c8345a7)
-- [x] Integrate Bedrock runtime path. (implemented in 4c8345a7)
-- [x] Integrate Ollama runtime path. (implemented in 4c8345a7)
-- [x] Add regression tests for runtime adapter behavior. (implemented in 4c8345a7)
-- [x] Retire `DEEPCHAT_LLM_RUNTIME` and `llmRuntimeMode`.
-- [x] Remove legacy provider SDK fallback branches.
-- [x] Remove provider-specific MCP conversion interfaces from presenter ports.
-- [x] Record retirement history in [../legacy-llm-provider-runtime-retirement/tasks.md](../legacy-llm-provider-runtime-retirement/tasks.md).
-- [x] Run format, i18n, lint, and targeted tests.
diff --git a/docs/archives/chat-audio-tts-routing/plan.md b/docs/archives/chat-audio-tts-routing/plan.md
deleted file mode 100644
index e711ff914..000000000
--- a/docs/archives/chat-audio-tts-routing/plan.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Chat Audio TTS Routing Plan
-
-## Implementation
-
-- Tighten `isChatAudioTtsModel` so MiMo IDs must match the known MiMo prefixes and include a standalone `tts` segment.
-- Update `executeTtsPatternB` to treat `message.content` as unknown response data.
-- Extract audio parts only after checking `Array.isArray(message.content)`.
-- Keep `message.audio.data` as the first-preference extraction path.
-- Leave the existing missing-audio error path in place for responses that contain no audio data.
-
-## Test Strategy
-
-- Add shared helper coverage for MiMo TTS and non-TTS model IDs.
-- Extend `test/main/presenter/llmProviderPresenter/aiSdkRuntime.test.ts`.
-- Cover `mimo-v2.5-pro` using normal chat streaming instead of direct TTS `fetch`.
-- Cover a successful HTTP response with string `message.content` and no audio payload.
-- Assert the runtime rejects with the expected missing-audio error, not `content.find is not a function`.
-
-## Compatibility
-
-This change is backward-compatible for actual MiMo TTS models. Non-TTS MiMo chat models stop being routed through TTS handling, while providers returning `message.audio.data` or array content audio parts keep the same behavior.
diff --git a/docs/archives/chat-audio-tts-routing/spec.md b/docs/archives/chat-audio-tts-routing/spec.md
deleted file mode 100644
index bfbbca2c5..000000000
--- a/docs/archives/chat-audio-tts-routing/spec.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Chat Audio TTS Routing
-
-## User Story
-
-When a MiMo chat model is selected, DeepChat should only enter chat-audio TTS handling for model IDs that are actually TTS variants. Regular MiMo chat models such as `MiMo-V2.5-Pro` should use the normal chat streaming runtime.
-
-## Acceptance Criteria
-
-- `mimo-v2.5-pro` and provider-prefixed variants are not classified as TTS models.
-- MiMo model IDs with a `tts` segment, such as `mimo-v2.5-tts`, continue to use chat-audio TTS Pattern B.
-- Chat-audio TTS responses with `choices[0].message.audio.data` continue to emit cached audio.
-- Chat-audio TTS responses with array `choices[0].message.content` can still extract an audio content part.
-- Chat-audio TTS responses with string `choices[0].message.content` do not throw a `TypeError`.
-- If no audio payload exists, DeepChat raises the existing missing-audio error instead of a response-shape crash.
-
-## Non-Goals
-
-- No changes to renderer audio display behavior.
-- No changes to request body construction for chat-audio TTS models.
-
-## Constraints
-
-- Keep the fix localized to the AI SDK runtime.
-- Keep TTS model classification in shared helpers so provider and agent runtime checks agree.
-- Preserve current OpenAI-compatible chat-audio behavior.
-- Add focused regression coverage for the reported MiMo Pro misrouting and response shape.
diff --git a/docs/archives/chat-audio-tts-routing/tasks.md b/docs/archives/chat-audio-tts-routing/tasks.md
deleted file mode 100644
index 9ff9742f3..000000000
--- a/docs/archives/chat-audio-tts-routing/tasks.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Chat Audio TTS Routing Tasks
-
-- [x] Create SDD issue artifacts.
-- [x] Guard chat-audio TTS content audio extraction by response shape.
-- [x] Add a regression test for string `message.content`.
-- [x] Tighten MiMo chat-audio TTS classification.
-- [x] Add regression coverage for MiMo Pro chat routing.
-- [x] Run focused test coverage and quality checks.
diff --git a/docs/archives/cua-runtime-plugin/non-macos-handoff.md b/docs/archives/cua-runtime-plugin/non-macos-handoff.md
deleted file mode 100644
index e7d3bb1b3..000000000
--- a/docs/archives/cua-runtime-plugin/non-macos-handoff.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Non-macOS Handoff Notes
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-This implementation was completed on Windows, so the architecture and TypeScript integration were
-validated locally, while macOS runtime behavior still needs a Mac pass.
-
-## Implemented
-
-- Official-source-only plugin installation path with GitHub Release download and local
- `.dcplugin` selection.
-- Generic plugin resources for runtimes, MCP servers, skills, settings contributions, and tool
- policies.
-- Plugin-owned MCP registration with `ownerPluginId`.
-- Plugin-owned skill contribution support.
-- Isolated plugin settings renderer preload API exposed as `window.deepchatPlugin`.
-- Official `plugins/cua` package with a bundled macOS `DeepChat Computer Use.app` helper runtime.
-- CI/package scripts for architecture-specific
- `deepchat-plugin-cua--darwin-.dcplugin` artifacts.
-- App packaging no longer embeds the CUA helper inside the DeepChat app bundle.
-- App packaging no longer embeds the CUA plugin source; the app and plugin artifacts are built
- independently.
-
-## Manual Install Flow
-
-1. Build the plugin package for the current Mac:
- `pnpm run plugin:cua:package`
- - Apple Silicon explicit package: `pnpm run plugin:cua:package:mac:arm64`
- - Intel explicit package: `pnpm run plugin:cua:package:mac:x64`
-2. Open Settings > Plugins.
-3. Click Install on `com.deepchat.plugins.cua`.
- - DeepChat first downloads
- `https://github.com/ThinkInAIXYZ/deepchat/releases/download/v/deepchat-plugin-cua--darwin-.dcplugin`.
- - If the asset is missing, DeepChat opens the matching GitHub Release page.
-4. Click Choose `.dcplugin` and select the downloaded package when automatic download is not
- available.
-5. Enable `com.deepchat.plugins.cua`, then verify runtime status, permission check, MCP
- registration, skill visibility, and tool policy prompts.
-
-## Requires macOS validation
-
-- `cua-driver --version` output shape.
-- `cua-driver check_permissions` output shape and permission parsing.
-- `cua-driver mcp` startup under DeepChat MCP stdio management.
-- TCC ownership remains with the bundled `DeepChat Computer Use.app` helper inside the installed
- plugin.
-- Plugin settings window can open the CUA permission guide and refresh status.
-- Signed `.dcplugin` packaging and official source distribution metadata.
-
-## Legacy Demo Code
-
-The prior built-in Computer Use implementation is removed from startup, routes, renderer API,
-settings, helper packaging, vendored source, and tests. The remaining CUA-specific source of truth is
-`plugins/cua`.
diff --git a/docs/archives/cua-runtime-plugin/plan.md b/docs/archives/cua-runtime-plugin/plan.md
deleted file mode 100644
index da221ae22..000000000
--- a/docs/archives/cua-runtime-plugin/plan.md
+++ /dev/null
@@ -1,546 +0,0 @@
-# CUA Runtime Plugin Implementation Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-Feature: `cua-runtime-plugin`
-Spec: [spec.md](./spec.md)
-
-## Summary
-
-The implementation should land as two independent lines:
-
-1. Generic plugin runtime infrastructure in DeepChat core.
-2. First-party `deepchat-plugin-cua` packaged as an installable runtime plugin.
-
-The current built-in Computer Use branch proves that CUA can drive DeepChat through MCP and skills.
-It should not be used as the final architecture because it makes CUA a core app packaging, signing,
-permission, route, renderer, and agent prompt concern.
-
-## Architecture
-
-```text
-DeepChat Core
-+-- PluginHost
-+-- PluginResourceStore
-+-- RuntimeRegistry
-+-- ManagedMcpRegistry
-+-- SkillRegistry
-+-- ToolPolicyRegistry
-+-- SettingsContributionHost
- |
- | enable / disable / delete
- v
-Installed Plugin: com.deepchat.plugins.cua
-+-- plugin.json
-+-- dist/main.js
-+-- settings/index.html
-+-- types/settings-preload.d.ts
-+-- skills/cua-driver/SKILL.md
-+-- mcp/cua-driver.json
-+-- policies/tool-policy.json
- |
- | detects / opens / executes declared helper commands
- v
-Bundled Plugin Helper
-+-- /runtime/darwin//DeepChat Computer Use.app
- +-- Contents/MacOS/cua-driver
-```
-
-Core owns the registries, official-source trust policy, lifecycle, and renderer isolation. The
-plugin owns CUA-specific runtime detection, bundled helper runtime, status, settings web bundle,
-typed preload API, MCP contribution, skill content, tool policy, and helper guidance.
-
-## Key Decisions
-
-### 1. First-Party Signed Plugin Scope
-
-The first production increment should install plugins only from the DeepChat official plugin source.
-That is enough for `deepchat-plugin-cua` and deliberately avoids opening a dangerous third-party
-execution surface before the trust model, review process, and isolation story are mature.
-
-Required boundaries:
-
-- Production builds reject arbitrary local `.dcplugin` sideloading.
-- Production builds verify official source URL, plugin id reservation, checksums, and DeepChat
- signature metadata before install or update.
-- Local plugin development is available only in development builds or explicit developer mode.
-- A plugin cannot register arbitrary IPC routes.
-- A plugin cannot write raw MCP config or raw skill cache.
-- A plugin can only contribute through `PluginContext` registries.
-- Process execution is only available through declared command IDs.
-- Plugin packages require checksums and signature metadata before installation.
-
-Longer-term untrusted plugin isolation is a separate feature.
-
-### 2. Declarative First, Activation Code Second
-
-The CUA plugin should use manifest-declared contributions wherever possible:
-
-- runtime id and detection candidates
-- MCP server shape
-- skill path
-- settings contribution id
-- tool policy
-
-Activation code should only handle runtime detection, version checks, permission checks, and helper
-install/uninstall actions that need runtime state.
-
-### 3. Owner-Aware Resources
-
-Add a `PluginResourceStore` as the single source of truth for plugin-owned runtime resources. It may
-start as an ElectronStore-backed store to match existing MCP settings, but it should be represented
-behind an interface so it can move to SQLite later if needed.
-
-Suggested records:
-
-```typescript
-interface PluginInstallationRecord {
- pluginId: string
- version: string
- path: string
- enabled: boolean
- trusted: boolean
- installedAt: number
- updatedAt: number
-}
-
-interface PluginResourceRecord {
- pluginId: string
- kind: 'runtime' | 'mcpServer' | 'skill' | 'settings' | 'toolPolicy'
- key: string
- payloadJson: string
- enabled: boolean
- createdAt: number
- updatedAt: number
-}
-
-interface RuntimeDependencyRecord {
- pluginId: string
- runtimeId: string
- provider: string
- command?: string
- version?: string
- installSource?: string
- state: 'missing' | 'installed' | 'running' | 'error'
-}
-```
-
-### 4. MCP Ownership Wrapper
-
-Do not let plugins call `configPresenter.addMcpServer` directly.
-
-Add `ManagedMcpRegistry`:
-
-- validates plugin ownership
-- writes MCP server config with `ownerPluginId`
-- maps plugin server id to a stable internal key
-- stops server before unregistering it
-- refreshes MCP client and tool caches after changes
-
-Existing `source` / `sourceId` can remain for external catalogs. Add `ownerPluginId` for plugin
-ownership instead of overloading those fields.
-
-### 5. Skill Contributions
-
-Extend SkillPresenter discovery to merge:
-
-- user skill directory
-- built-in resource skills
-- plugin-owned skill roots from `SkillRegistry`
-
-Plugin-owned skills should not be copied into `resources/skills` or `~/.deepchat/skills`.
-
-Required behavior:
-
-- hidden when plugin disabled
-- unavailable to `skill_view` when disabled
-- removed from prompt loading and active skill validation
-- hot reloaded in development mode if plugin path changes
-
-### 6. Tool Policies
-
-Add `ToolPolicyRegistry` and call it from MCP tool permission pre-check and execution paths before
-fallback name heuristics.
-
-Policy evaluation order:
-
-1. Session-scoped permission cache.
-2. Plugin-owned exact tool policy.
-3. Server `autoApprove`.
-4. Existing read/write heuristic.
-
-Decision mapping:
-
-- `allow` returns no permission request.
-- `ask` returns a permission request.
-- `deny` returns a clear blocked response.
-
-CUA action tools should use `ask` by default. Read/status tools can use `allow`, but should still be
-overridable by stricter global settings later.
-
-### 7. Settings Contribution Host
-
-Add a generic Settings > Plugins surface before adding CUA UI.
-
-Plugin settings contribution contract:
-
-```typescript
-interface SettingsContribution {
- id: string
- ownerPluginId: string
- title: string
- entry: string // standalone HTML entry inside the plugin package
- preloadTypes: string
- placement: 'plugins'
-}
-```
-
-DeepChat should not render plugin settings inside the normal Vue renderer tree. The contribution
-host should create an isolated renderer/webContents for the plugin settings page:
-
-```text
-DeepChat Settings > Plugins
- |
- | owns container and lifecycle only
- v
-Isolated plugin settings renderer
- |
- | contextIsolation + no Node integration
- v
-Dedicated plugin preload
- |
- | plugin-scoped typed API
- v
-PluginHost / Plugin main module
-```
-
-The plugin settings web bundle can be implemented with any frontend stack that compiles to static
-assets. It talks to DeepChat only through the dedicated preload API. Plugin developers should code
-against the shipped `.d.ts` file, not against DeepChat renderer internals.
-
-Generic host APIs available through preload:
-
-- `plugins.list`
-- `plugins.get`
-- `plugins.enable`
-- `plugins.disable`
-- `plugins.invokeAction`
-
-Plugin-specific APIs available through preload are declared by the plugin and handled through the
-plugin host, for example:
-
-```typescript
-interface CuaSettingsApi {
- getRuntimeStatus(): Promise
- checkPermissions(): Promise
- openPermissionGuide(): Promise
- uninstallHelper(): Promise
-}
-```
-
-No CUA-specific typed route should exist in core.
-
-### 8. Minimal Main SDK
-
-Plugins should provide agent-facing capabilities through MCP and skills first. If a plugin needs
-DeepChat capabilities outside MCP/skills, add a small main-process SDK method to `PluginContext`.
-
-Rules:
-
-- SDK methods require manifest capability declarations.
-- SDK methods expose stable typed functions, not presenters or stores.
-- SDK methods are reviewed as core API surface.
-- SDK methods should be narrow enough that a future untrusted-plugin host can mediate them.
-
-CUA expected SDK needs are limited to:
-
-- declared process execution for `cua-driver` status/version/permission checks
-- opening official external URLs or macOS permission panes
-- plugin storage
-- managed runtime/MCP/skill/settings/tool-policy registries
-
-### 9. CUA Helper Integration
-
-The CUA plugin should detect official helper candidates:
-
-```text
-/Applications/CuaDriver.app/Contents/MacOS/cua-driver
-~/.local/bin/cua-driver
-PATH:cua-driver
-```
-
-Runtime actions:
-
-- `cua-driver.version`: run detected command with `--version`
-- `cua-driver.status`: run detected command with status/check command when available
-- `cua-driver.permissions`: run helper permission check command
-- `cua-driver.open-permissions`: open official permission guide or helper-driven prompt
-- `cua-driver.uninstall`: run official uninstall flow only after user confirmation
-
-The plugin should prefer the app-bundled binary path when present so macOS TCC grants are owned by
-`CuaDriver.app`.
-
-### 10. Plugin Package Layout
-
-Source:
-
-```text
-plugins/
- cua/
- plugin.json
- package.json
- tsconfig.json
- src/
- main.ts
- runtime/locator.ts
- runtime/permissions.ts
- runtime/actions.ts
- settings/
- index.html
- src/CuaRuntimeSettings.ts
- src/style.css
- types/
- settings-preload.d.ts
- skills/
- cua-driver/
- SKILL.md
- README.md
- RECORDING.md
- TESTS.md
- WEB_APPS.md
- mcp/
- cua-driver.json
- policies/
- tool-policy.json
-```
-
-Packaged artifact:
-
-```text
-deepchat-plugin-cua.dcplugin
-+-- plugin.json
-+-- dist/main.js
-+-- settings/index.html
-+-- settings/assets/index.js
-+-- settings/assets/index.css
-+-- types/settings-preload.d.ts
-+-- skills/cua-driver/SKILL.md
-+-- skills/cua-driver/README.md
-+-- skills/cua-driver/RECORDING.md
-+-- skills/cua-driver/TESTS.md
-+-- skills/cua-driver/WEB_APPS.md
-+-- mcp/cua-driver.json
-+-- policies/tool-policy.json
-+-- checksums.json
-+-- signature.sig
-```
-
-### 11. Build And CI
-
-Add plugin scripts without coupling them to core app packaging:
-
-```text
-pnpm run plugin:cua:build
-pnpm run plugin:cua:package
-pnpm run plugin:cua:validate
-```
-
-Release shape:
-
-- Core app jobs build Windows, Linux, and macOS artifacts without CUA helper steps.
-- Plugin job builds `deepchat-plugin-cua-.dcplugin`.
-- Plugin job validates official-source metadata, checksums, and signature metadata.
-- Release job uploads plugin artifact separately from app artifacts.
-- No Swift build, helper entitlements, nested helper signing, or CUA notarization path is required
- for DeepChat app release.
-
-### 12. Demo Branch Migration
-
-Remove CUA-specific core changes from the final app:
-
-```text
-remove:
- src/main/presenter/computerUsePresenter
- src/renderer/api/ComputerUseClient.ts
- src/shared/contracts/routes/computerUse.routes.ts
- src/shared/types/computerUse.ts
- src/renderer/settings/components/ComputerUseSettingsCard.vue
- resources/skills/cua-driver
- vendor/cua-driver
- scripts/build-cua-driver.mjs
- scripts/update-cua-driver.mjs
- build/entitlements.computer-use.plist
- runtime/computer-use
-
-refactor into generic infrastructure:
- typed route pattern -> plugin routes
- MCP source/sourceId usage -> ownerPluginId-aware managed MCP
- skill visibility gate -> plugin-owned skill contribution visibility
- CUA tool buckets -> ToolPolicyRegistry
- CUA settings card -> isolated SettingsContributionHost renderer
- helper path/status -> RuntimeRegistry
-```
-
-Also remove CUA helper steps from macOS build and release workflows.
-
-## Implementation Milestones
-
-### M1 Core Plugin Contracts
-
-Add shared plugin types, manifest schema, official-source trust policy, package validation,
-installation records, and lifecycle routes.
-
-Exit criteria:
-
-- Core can list installed plugins.
-- A fixture plugin manifest validates.
-- Invalid package paths, ids, versions, and unsupported platforms are rejected.
-- Production installation rejects non-official source URLs and untrusted signatures.
-
-### M2 Plugin Resource Store And Registries
-
-Add owner-aware resource records plus runtime, MCP, skill, settings, and tool policy registry shells.
-
-Exit criteria:
-
-- `disableByOwner(pluginId)` disables every owned resource type.
-- `removeByOwner(pluginId)` removes every owned record.
-- Startup repair removes resources for missing plugin installations.
-
-### M3 Managed MCP And Tool Policy Integration
-
-Wire managed MCP registration into existing MCP presenter/config flow. Wire tool policies into MCP
-permission pre-check and execution.
-
-Exit criteria:
-
-- Plugin-owned MCP server appears in MCP runtime state when plugin enabled.
-- Plugin-owned MCP server disappears on disable.
-- Exact tool policy overrides fallback name heuristic.
-- User-owned MCP servers are unaffected.
-
-### M4 Skill And Settings Contributions
-
-Wire plugin-owned skills into SkillPresenter and plugin settings into isolated settings renderers.
-
-Exit criteria:
-
-- Plugin-owned skill appears only while plugin enabled.
-- `skill_view` cannot read disabled plugin-owned skills.
-- Settings > Plugins lists plugin cards through contribution metadata.
-- Plugin settings load in a separate renderer with a dedicated preload.
-
-### M5 Plugin Packaging Toolchain
-
-Add packaging and validation scripts for `.dcplugin`.
-
-Exit criteria:
-
-- Package contains required files.
-- Checksums cover all package files except signature.
-- Invalid manifests or missing files fail validation.
-- Missing settings HTML or preload type declarations fail validation when a settings contribution is
- declared.
-- CI can build a plugin artifact without building DeepChat app.
-
-### M6 CUA Plugin
-
-Move CUA-specific integration into `plugins/cua`.
-
-Exit criteria:
-
-- Missing helper state is visible.
-- Installed helper version and path are detected.
-- MCP server `cua-driver` starts through normal MCP.
-- CUA skill is contributed by the plugin.
-- CUA action tools trigger approval prompts.
-- CUA read/status tools follow plugin policy.
-
-### M7 Demo Core Removal
-
-Remove the built-in Computer Use branch changes and keep only generic infrastructure plus plugin
-source.
-
-Exit criteria:
-
-- Core grep acceptance passes.
-- macOS app package contains no CUA Driver helper or vendored CUA source.
-- DeepChat build workflows no longer run CUA helper build steps.
-
-### M8 Release Validation
-
-Validate end-to-end behavior on macOS with and without official Cua Driver installed.
-
-Exit criteria:
-
-- Fresh app install has no CUA capability until plugin installation.
-- Plugin enable/disable/delete acceptance passes.
-- App release and plugin release artifacts are independent.
-
-## Test Strategy
-
-Main process tests:
-
-- manifest schema validation
-- package path safety
-- plugin lifecycle
-- resource store owner disable/delete
-- managed MCP register/unregister
-- tool policy exact match and fallback behavior
-- plugin skill visibility and prompt injection
-- stale resource startup repair
-
-Renderer tests:
-
-- Settings > Plugins plugin list
-- plugin enable/disable actions
-- isolated settings contribution host lifecycle
-- plugin preload API availability
-- blocked access to DeepChat renderer globals
-- CUA settings states: missing helper, installed helper, permission missing, permission granted
-
-Script tests:
-
-- `.dcplugin` package validation
-- checksum generation and validation
-- missing file failure
-- unsupported platform manifest failure
-- non-official source rejection
-- missing preload type declarations
-
-Manual macOS checks:
-
-- Cua Driver official install
-- Cua Driver permission ownership in System Settings
-- `cua-driver mcp` tool discovery
-- click/type/hotkey approval prompt
-- plugin disable removes tools from current session after refresh
-- plugin delete survives restart without stale resources
-
-## Rollout
-
-1. Land the generic plugin foundation without CUA plugin.
-2. Add CUA plugin package and CI artifact.
-3. Remove built-in CUA demo code from core.
-4. Validate macOS external helper flow.
-5. Publish DeepChat app and CUA plugin as separate artifacts.
-
-## Risks And Mitigations
-
-- Risk: plugin code becomes an unbounded execution surface.
- - Mitigation: production installs only official-source signed plugins, settings run in isolated
- renderers, APIs are capability-declared, and there is no public marketplace scope.
-- Risk: settings UI can pierce DeepChat renderer privileges.
- - Mitigation: standalone settings web bundle in an isolated renderer with dedicated preload,
- context isolation, no Node integration, and plugin-scoped typed APIs only.
-- Risk: plugin-owned MCP config leaves stale tools after disable/delete.
- - Mitigation: owner-aware resource records, stop-before-unregister, and startup repair.
-- Risk: skills stay pinned after plugin disable.
- - Mitigation: skill validation must filter disabled owner resources before prompt composition.
-- Risk: user already has an MCP server named `cua-driver`.
- - Mitigation: use an internal owner-aware key and a user-facing display name, or block enable with
- a clear conflict message.
-- Risk: official Cua Driver install paths change.
- - Mitigation: detect declared canonical paths plus PATH, keep install guidance in the plugin, and
- update plugin independently from core.
diff --git a/docs/archives/cua-runtime-plugin/spec.md b/docs/archives/cua-runtime-plugin/spec.md
deleted file mode 100644
index 88dce51d3..000000000
--- a/docs/archives/cua-runtime-plugin/spec.md
+++ /dev/null
@@ -1,444 +0,0 @@
-# CUA Runtime Plugin Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Summary
-
-DeepChat should move macOS Computer Use out of the core application bundle and into an
-installable runtime plugin. The core app should provide reusable plugin infrastructure:
-Plugin Host, Runtime Registry, Managed MCP Registry, Skill Registry, Tool Policy Registry, and
-Settings Contribution Host. The CUA integration should live in a separate plugin package that
-registers an external Cua Driver helper, MCP server, skill, settings card, and tool permission
-policy only while the plugin is enabled.
-
-This replaces the current `codex/mac-computer-use` branch direction where DeepChat bundles a
-source-built helper app, vendored `cua-driver` source, CUA-specific settings UI, CUA typed routes,
-CUA-specific skill visibility, and CUA-specific MCP permission rules directly in core.
-
-## Current Findings
-
-Treat the current built-in Computer Use implementation as a technical feasibility demo:
-
-- `src/main/presenter/computerUsePresenter/index.ts` owns helper discovery, helper path layout,
- permission checks, enable state, and MCP registration.
-- `src/shared/contracts/routes/computerUse.routes.ts`,
- `src/shared/types/computerUse.ts`, and `src/renderer/api/ComputerUseClient.ts` expose a
- CUA-specific renderer-main API surface.
-- `src/renderer/settings/components/ComputerUseSettingsCard.vue` puts CUA settings directly inside
- core MCP settings.
-- `src/main/presenter/mcpPresenter/toolManager.ts` contains CUA-only read/write tool buckets.
-- `src/main/presenter/skillPresenter/index.ts` hides the built-in `cua-driver` skill with a
- `computer-use` feature flag.
-- `src/main/presenter/agentRuntimePresenter/index.ts` auto-pins the `cua-driver` skill based on the
- built-in CUA MCP server.
-- `scripts/build-cua-driver.mjs`, `scripts/update-cua-driver.mjs`, `scripts/afterPack.js`,
- `electron-builder.yml`, GitHub workflows, `build/entitlements.computer-use.plist`,
- `resources/skills/cua-driver`, and `vendor/cua-driver` make the helper a core packaging concern.
-
-Treat `origin/dev` as the production baseline:
-
-- There is no general Plugin Host for runtime plugins.
-- MCP servers are persisted in the current MCP config store and can carry `source` / `sourceId`
- metadata, but there is no owner-aware managed resource registry.
-- Skills are file-based and already support metadata, platform filters, scripts, and activation, but
- they do not yet support plugin-owned contribution roots.
-- Tool permission is currently based on broad read/write heuristics plus server `autoApprove`.
- There is no declarative per-plugin tool policy registry.
-- Remote channel code has a narrow channel plugin manifest shape, but it is not a general runtime
- plugin system.
-
-## Goals
-
-- Keep DeepChat core free of `cua`, `computerUse`, and `CuaDriver` product-specific code.
-- Add generic plugin infrastructure that can support CUA and future runtime integrations.
-- Ship CUA as `deepchat-plugin-cua`, a separately packaged `.dcplugin` artifact.
-- Allow production plugin installation only from DeepChat official sources for the first release.
-- Let CI build DeepChat app artifacts and CUA plugin artifacts separately.
-- Use the official Cua Driver helper externally, rather than vendoring and notarizing driver source
- inside DeepChat app packages.
-- Let the CUA plugin own its skills, settings UI, runtime detection, MCP contribution, and tool
- permission policy.
-- Ensure disabling or deleting the plugin removes all DeepChat-side CUA capabilities.
-- Keep the CUA helper runtime and macOS TCC grants outside DeepChat core.
-
-## Non-Goals
-
-- Do not merge the current built-in CUA helper as a core feature.
-- Do not package `vendor/cua-driver` or `DeepChat Computer Use.app` inside DeepChat app artifacts.
-- Package the signed `DeepChat Computer Use.app` helper inside the CUA `.dcplugin` artifact.
-- Do not add DeepChat-owned Accessibility or Screen Recording permissions for CUA.
-- Do not add Windows or Linux Computer Use support in this feature.
-- Do not create a public third-party plugin marketplace in the first increment.
-- Do not allow arbitrary local `.dcplugin` sideloading in production builds.
-- Do not let plugins mutate arbitrary core state outside declared contribution APIs.
-
-## User Stories
-
-- As a DeepChat user, I can install the CUA runtime plugin and see it in Settings > Plugins.
-- As a macOS user, I can enable the CUA plugin only after seeing its requested capabilities.
-- As a macOS user without Cua Driver installed, I see a clear install guide and no active CUA tools.
-- As a macOS user with Cua Driver installed, I see helper version, detected command path,
- Accessibility status, Screen Recording status, MCP status, and skill status.
-- As a privacy-sensitive user, I can disable or delete the plugin and have all CUA MCP tools,
- skills, settings cards, and policies disappear from DeepChat.
-- As a release engineer, I can build and publish the DeepChat app without building, signing,
- notarizing, or vendoring CUA Driver.
-- As a plugin maintainer, I can update `deepchat-plugin-cua` independently from DeepChat core.
-
-## Functional Requirements
-
-### Plugin Host
-
-- Core must discover installed plugins from the DeepChat plugin install directory.
-- Core must install plugins only from the DeepChat official plugin source in production builds.
-- Core must parse a signed plugin manifest before activation.
-- Core must reject packages whose publisher, source URL, signature, or checksum chain is not
- trusted by the official source policy.
-- Core must support enable, disable, update, and delete plugin lifecycle operations.
-- Core must expose a stable `PluginContext` with only declared contribution APIs.
-- Core must persist plugin installation state separately from MCP, skills, and runtime state.
-- Core must emit plugin lifecycle events so renderer settings can refresh plugin state.
-- Development sideloading, if needed, must be gated behind a development build or explicit developer
- mode and must never be available as the normal production install path.
-
-### Official Source Policy
-
-- Production plugin discovery and update metadata must come from a DeepChat-owned source.
-- Plugin package URLs must match the official source allowlist.
-- Plugin signatures must chain to a DeepChat-controlled signing identity.
-- Plugin ids must be reserved by the official source registry; a package cannot claim another
- official plugin id.
-- The installer must show plugin capabilities before enabling a newly installed plugin.
-- Plugin updates must preserve the same plugin id and trusted publisher identity.
-- Local file install is allowed only for development builds or an explicit developer-mode workflow.
-
-### Manifest
-
-The CUA plugin manifest must be enough to describe first-party runtime integration:
-
-```json
-{
- "id": "com.deepchat.plugins.cua",
- "name": "CUA Computer Use Runtime",
- "version": "1.0.0",
- "publisher": "DeepChat",
- "engines": {
- "deepchat": ">=1.1.0",
- "platforms": ["darwin"]
- },
- "activationEvents": ["onEnable"],
- "capabilities": [
- "runtime.manage",
- "mcp.register",
- "skills.register",
- "settings.contribute",
- "shell.openExternal",
- "process.execDeclared"
- ],
- "settings": {
- "entry": "settings/index.html",
- "preloadTypes": "types/settings-preload.d.ts"
- }
-}
-```
-
-The manifest may contain declarative runtime, MCP, skill, settings, and tool-policy contributions.
-Activation code may refine those contributions after runtime detection.
-
-### Plugin APIs
-
-Plugins should primarily add agent capabilities through MCP servers and skills. If a plugin needs
-DeepChat app capabilities that cannot be expressed through MCP or skills, core may expose a very
-small main-process SDK through `PluginContext`.
-
-Rules:
-
-- Main SDK methods must be capability-gated by manifest declarations.
-- Main SDK methods must be stable, typed, and reviewed as core API surface.
-- Main SDK methods must not expose raw presenters, raw stores, arbitrary IPC, or Electron objects.
-- Renderer-side plugin APIs must be defined by plugin-specific preload `.d.ts` files.
-- Plugin developers should build settings pages against the preload type definitions, not against
- DeepChat renderer internals.
-
-### Resource Ownership
-
-Every resource registered by a plugin must have an owner:
-
-```typescript
-type ManagedResourceKind = 'runtime' | 'mcpServer' | 'skill' | 'settings' | 'toolPolicy'
-
-type ManagedResource = {
- ownerPluginId: string
- kind: ManagedResourceKind
- key: string
- payload: unknown
- enabled: boolean
- createdAt: number
- updatedAt: number
-}
-```
-
-Core must support:
-
-- `disableByOwner(pluginId)` to unregister runtime-visible resources without deleting plugin files.
-- `removeByOwner(pluginId)` to remove all resource records and stop owned runtime services.
-- Owner-aware startup repair so stale plugin-owned MCP, skill, settings, and policies do not survive
- a deleted plugin.
-
-### Runtime Registry
-
-- Core must support plugin-owned external-helper runtimes.
-- Runtime detection must be allowed only through declared commands and declared paths.
-- Runtime records must store detected command, version, status, install source, last check time, and
- last error.
-- CUA plugin runtime detection must check, at minimum:
- - `/Applications/CuaDriver.app/Contents/MacOS/cua-driver`
- - `~/.local/bin/cua-driver`
- - `PATH:cua-driver`
-- The registry should tolerate additional official install paths if upstream changes them.
-- Runtime install and uninstall actions must require user confirmation.
-
-### Managed MCP Registry
-
-- Plugins must be able to register MCP servers without writing raw MCP config directly.
-- Plugin-owned MCP servers must be identifiable by `ownerPluginId`.
-- Disabling the plugin must stop and unregister its MCP servers.
-- Deleting the plugin must remove plugin-owned MCP config records.
-- User-created MCP servers must never be removed by plugin disable/delete.
-- CUA plugin must register a stdio MCP server equivalent to:
-
-```json
-{
- "id": "cua-driver",
- "displayName": "CUA Driver",
- "transport": "stdio",
- "command": "${runtime.cua-driver.command}",
- "args": ["mcp"],
- "autoApprove": []
-}
-```
-
-### Skill Registry
-
-- Plugins must be able to contribute skill roots without copying files into built-in resources or
- the user's personal skills directory.
-- Plugin-owned skills must be visible only while the owning plugin is enabled.
-- Plugin-owned skills must be removed from available skill metadata, `skill_view`, pinned prompt
- content, and active skill validation when the plugin is disabled or deleted.
-- The CUA plugin must contribute `skills/cua-driver/SKILL.md`.
-- The CUA plugin may copy upstream CUA skill content into the plugin package and maintain
- DeepChat-specific additions in plugin-owned files.
-
-### Tool Policy Registry
-
-- Core must replace CUA-specific permission logic with generic plugin-owned tool policy entries.
-- Policy must be evaluated before fallback read/write name heuristics.
-- Supported decisions:
- - `allow`
- - `ask`
- - `deny`
-- CUA plugin default policy:
-
-```json
-{
- "cua-driver.check_permissions": "allow",
- "cua-driver.list_apps": "allow",
- "cua-driver.list_windows": "allow",
- "cua-driver.get_screen_size": "allow",
- "cua-driver.get_window_state": "allow",
- "cua-driver.get_accessibility_tree": "allow",
- "cua-driver.get_cursor_position": "allow",
- "cua-driver.screenshot": "allow",
- "cua-driver.launch_app": "ask",
- "cua-driver.click": "ask",
- "cua-driver.right_click": "ask",
- "cua-driver.double_click": "ask",
- "cua-driver.scroll": "ask",
- "cua-driver.move_cursor": "ask",
- "cua-driver.type_text": "ask",
- "cua-driver.type_text_chars": "ask",
- "cua-driver.press_key": "ask",
- "cua-driver.hotkey": "ask",
- "cua-driver.set_value": "ask",
- "cua-driver.set_config": "ask",
- "cua-driver.set_recording": "ask",
- "cua-driver.replay_trajectory": "ask",
- "cua-driver.zoom": "ask"
-}
-```
-
-`autoApprove` must remain empty by default.
-
-### Settings Contribution
-
-- Core must provide a generic Settings > Plugins page.
-- Plugins must package settings as a standalone web bundle.
-- DeepChat must load plugin settings in an isolated renderer/webContents, not inside the main
- DeepChat renderer component tree.
-- The isolated settings renderer must use `contextIsolation`, no Node integration, and a dedicated
- plugin preload.
-- The dedicated preload must expose only plugin-scoped typed APIs declared for that plugin.
-- Plugin settings UI must not access DeepChat renderer stores, global IPC, Vue app state, or raw
- Electron APIs.
-- Plugin settings UI must not depend on CUA-specific core routes.
-- Plugin settings data/config APIs must be defined as plugin-owned typed APIs and routed through the
- plugin host.
-- CUA status text, permission guidance, install guidance, and helper uninstall actions belong to the
- plugin.
-
-ASCII layout:
-
-```text
-Settings
-+-- Plugins
- +-- CUA Computer Use Runtime
- +------------------------------------------------+
- | CUA Computer Use Runtime |
- | Runtime CuaDriver 0.x |
- | Helper /Applications/CuaDriver.app |
- | MCP Registered / Running |
- | Skill Active |
- | Permissions |
- | Accessibility Granted |
- | Screen Recording Missing |
- | |
- | [Open Permission Guide] [Check Again] |
- | [Disable Plugin] [Uninstall Helper] |
- +------------------------------------------------+
-```
-
-### Packaging
-
-- Add a top-level plugin source directory for first-party plugins, starting with CUA:
-
-```text
-plugins/
- cua/
- plugin.json
- package.json
- src/
- settings/
- index.html
- src/
- types/
- settings-preload.d.ts
- skills/cua-driver/
- mcp/cua-driver.json
- policies/tool-policy.json
-```
-
-- Add plugin packaging scripts that produce bundled macOS plugin packages:
-
-```text
-build/bundled-plugins/deepchat-plugin-cua--darwin-.dcplugin
-```
-
-- The `.dcplugin` package must contain manifest, compiled plugin code, settings bundle, skills,
- preload type definitions, bundled helper runtime, checksums, and signature metadata.
-- DeepChat app packaging must include the bundled CUA plugin package inside the macOS app resources.
-- Release CI must upload DeepChat app artifacts without publishing `.dcplugin` files separately.
-
-### Migration
-
-- The current built-in branch should be split:
- - Generic plugin infrastructure PR.
- - `deepchat-plugin-cua` PR.
-- Current CUA-specific core files should not survive in final core.
-- Existing `deepchat/computer-use` MCP config entries created by the demo branch should be removed
- or migrated to plugin-owned `cua-driver` entries only if the CUA plugin is installed and enabled.
-- Existing user-created MCP servers and skills must not be affected.
-
-## Acceptance Criteria
-
-### Core Grep
-
-- `src/main`, `src/renderer`, and `src/shared` contain no CUA-specific symbols:
- - `computerUse`
- - `ComputerUse`
- - `cua-driver`
- - `CuaDriver`
-- Allowed generic symbols include:
- - `PluginHost`
- - `RuntimeRegistry`
- - `ManagedMcpRegistry`
- - `SkillRegistry`
- - `SettingsContribution`
- - `ToolPolicy`
- - `ownerPluginId`
-
-### Official Source
-
-- Production builds can install/update plugins only from the official DeepChat plugin source.
-- A local `.dcplugin` chosen by a user is rejected in production builds.
-- A plugin with an untrusted publisher, unknown signature, mismatched checksum, or non-official
- source URL is rejected before activation.
-- Developer sideloading works only in development builds or explicit developer mode.
-
-### Plugin Enable
-
-- CUA plugin appears in Settings > Plugins.
-- CUA plugin settings are rendered in an isolated plugin renderer with its own preload.
-- CUA settings code can call only the CUA plugin typed preload API and generic plugin host APIs.
-- Missing helper state shows install guidance and registers no running CUA tools.
-- Installed helper state shows detected path and version.
-- Accessibility and Screen Recording state is read from Cua Driver helper behavior.
-- `cua-driver` MCP contribution registers and can start through the normal MCP flow.
-- `cua-driver` skill appears in available skills and can be injected into agent context.
-- `click`, `type_text`, and `hotkey` default to a tool approval prompt.
-
-### Plugin Disable
-
-- CUA MCP server disappears from DeepChat MCP runtime state.
-- CUA skill disappears from available skills and active prompt context.
-- CUA tool policies are not evaluated.
-- CUA settings card disappears or changes to disabled plugin state.
-- Existing sessions cannot call CUA tools after registry refresh.
-- External `CuaDriver.app` remains installed.
-
-### Plugin Delete
-
-- Plugin files are removed from the plugin install directory.
-- Plugin storage is removed.
-- Plugin-owned resource records are removed.
-- DeepChat restart does not restore stale CUA MCP, skill, settings, or policy records.
-- Optional helper uninstall only runs after explicit user confirmation.
-
-### External Helper
-
-- Cua Driver macOS permissions are granted to `CuaDriver.app`, not DeepChat.
-- DeepChat app does not request Accessibility or Screen Recording for CUA.
-- DeepChat build and notarization do not depend on Swift, CUA source, or CUA helper entitlements.
-
-### CI
-
-- DeepChat core app build succeeds without CUA plugin build steps.
-- CUA plugin build produces a `.dcplugin` artifact.
-- Release CI publishes app artifacts and plugin artifacts separately.
-- Plugin package validation verifies manifest schema, checksums, signature metadata, and required
- files.
-
-### Settings Isolation
-
-- Plugin settings cannot import or access DeepChat renderer stores/components.
-- Plugin settings cannot call arbitrary DeepChat IPC channels.
-- Plugin settings cannot access Node APIs.
-- Plugin settings preload API has generated or checked TypeScript declarations.
-- Plugin development docs point plugin authors at the preload `.d.ts` API surface.
-
-## Assumptions
-
-- First production increment supports only signed official-source plugins.
-- Local plugin development is available only through development builds or an explicit developer
- mode that is not part of normal production installation.
-- Broader third-party plugin execution requires a stronger isolated extension host, review process,
- and marketplace policy, and is outside this CUA migration scope.
-- Official Cua Driver continues to expose a stdio MCP mode through `cua-driver mcp`.
-- Official Cua Driver remains responsible for macOS TCC permission ownership and helper lifecycle.
-
-## References
-
-- Existing demo spec: `docs/archives/mac-computer-use/`
-- Existing SDD guide: `docs/spec-driven-dev.md`
-- Cua Driver introduction: https://cua.ai/docs/cua-driver/guide/getting-started/introduction
-- Cua Driver installation and MCP registration: https://cua.ai/docs/cua-driver/guide/getting-started/installation
diff --git a/docs/archives/cua-runtime-plugin/tasks.md b/docs/archives/cua-runtime-plugin/tasks.md
deleted file mode 100644
index b83c2a119..000000000
--- a/docs/archives/cua-runtime-plugin/tasks.md
+++ /dev/null
@@ -1,332 +0,0 @@
-# CUA Runtime Plugin Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-Feature: `cua-runtime-plugin`
-Spec: [spec.md](./spec.md)
-Plan: [plan.md](./plan.md)
-
-## Status Legend
-
-- `[ ]` Not started
-- `[~]` In progress
-- `[x]` Complete
-
-## M0 Specification
-
-- [x] Create SDD artifacts under `docs/archives/cua-runtime-plugin/`.
-- [x] Capture current built-in CUA demo coupling points.
-- [x] Define final target as generic Plugin Host plus installable CUA runtime plugin.
-- [x] Define production plugin installation as official-source only for the first increment.
-- [x] Define plugin settings isolation through a standalone web bundle, isolated renderer, and
- plugin-specific typed preload API.
-- [x] Keep implementation code untouched during the specification step.
-
-## M1 Core Plugin Contracts
-
-- [x] Add shared plugin manifest types and schema.
-- [~] Add official plugin source trust policy:
- - source URL allowlist
- - publisher identity
- - plugin id reservation
- - checksum verification
- - signature metadata verification
-- [x] Add plugin lifecycle route contracts:
- - `plugins.list`
- - `plugins.get`
- - `plugins.enable`
- - `plugins.disable`
- - `plugins.invokeAction`
-- [x] Add renderer API client for generic plugin routes.
-- [~] Add package path validation that rejects absolute paths, `..`, drive-letter paths, and unsafe
- symlinks inside plugin packages.
-- [ ] Reject arbitrary local `.dcplugin` install in production builds.
-- [ ] Gate local plugin development behind development builds or explicit developer mode.
-- [ ] Add tests for valid and invalid plugin manifests.
-- [ ] Add tests for unsupported `engines.deepchat` and unsupported platform manifests.
-- [ ] Add tests for non-official source, untrusted publisher, bad signature, and checksum mismatch.
-
-Validation:
-
-- [ ] `pnpm test -- test/main/routes/contracts.test.ts`
-- [ ] Focused plugin manifest test suite passes.
-- [ ] Production install path rejects non-official plugin packages before activation.
-
-## M2 Plugin Installation And Resource Store
-
-- [x] Add `PluginHost` initialization in main presenter startup.
-- [x] Add `PluginInstallationRecord` persistence.
-- [x] Add `PluginResourceRecord` persistence.
-- [x] Add `RuntimeDependencyRecord` persistence or store abstraction.
-- [x] Implement `disableByOwner(pluginId)`.
-- [x] Implement `removeByOwner(pluginId)`.
-- [x] Implement startup repair for resources whose owning plugin is missing.
-- [ ] Add tests for disable, delete, update, and startup repair.
-
-Validation:
-
-- [ ] Disabling a fixture plugin disables all owned resources.
-- [ ] Deleting a fixture plugin removes all owned resources and plugin storage.
-- [ ] Restart simulation does not revive stale owned resources.
-
-## M3 Runtime Registry
-
-- [ ] Add `RuntimeRegistry.register`.
-- [ ] Add `RuntimeRegistry.unregisterByOwner`.
-- [x] Add runtime status refresh support.
-- [ ] Add declared command execution API:
- - command id
- - executable/path arguments
- - timeout
- - stdout/stderr size limit
- - environment allowlist
-- [~] Add shell open API for declared external URLs or system settings URLs.
-- [ ] Add tests for declared command allow/deny behavior.
-- [ ] Add minimal main SDK capability gate for plugin storage, declared process execution, shell
- opening, and registry access.
-- [ ] Add tests that plugins cannot access raw presenters, raw stores, arbitrary IPC, or Electron
- objects through the SDK.
-
-Validation:
-
-- [ ] Plugins cannot execute undeclared commands.
-- [ ] Runtime records carry owner, status, command, version, and last error.
-- [ ] Plugin main code can use only declared SDK capabilities.
-
-## M4 Managed MCP Registry
-
-- [x] Add `ownerPluginId` to plugin-owned MCP server records without breaking user MCP config.
-- [ ] Add `ManagedMcpRegistry.register`.
-- [ ] Add `ManagedMcpRegistry.unregisterByOwner`.
-- [x] Stop running plugin-owned MCP servers before unregistering them.
-- [ ] Refresh MCP clients and tool caches after managed changes.
-- [x] Add conflict handling for user-owned server names.
-- [ ] Add tests that user-owned MCP servers survive plugin disable/delete.
-
-Validation:
-
-- [ ] Enabling a fixture plugin registers its MCP server.
-- [ ] Disabling the fixture plugin removes only its MCP server.
-- [ ] User-created MCP servers with similar names are untouched.
-
-## M5 Tool Policy Registry
-
-- [x] Add `ToolPolicyRegistry.register`.
-- [x] Add `ToolPolicyRegistry.unregisterByOwner`.
-- [x] Add exact policy lookup by server id and original tool name.
-- [x] Evaluate policy in MCP permission pre-check before fallback heuristics.
-- [x] Evaluate policy in MCP execution path before fallback heuristics.
-- [ ] Add `allow`, `ask`, and `deny` behavior tests.
-- [x] Remove CUA-specific read/write tool sets from core.
-
-Validation:
-
-- [ ] `allow` skips permission prompt.
-- [ ] `ask` returns a permission request.
-- [ ] `deny` blocks execution with a clear message.
-- [ ] Unknown tools still use existing fallback heuristics.
-
-## M6 Skill Registry Contributions
-
-- [x] Add `SkillRegistry.register`.
-- [x] Add `SkillRegistry.unregisterByOwner`.
-- [x] Extend SkillPresenter discovery to merge plugin-owned skill roots.
-- [x] Hide plugin-owned skills when owner plugin is disabled.
-- [x] Filter disabled plugin-owned skills from:
- - metadata list
- - `skill_view`
- - active skill validation
- - prompt content loading
- - allowed tools lookup
-- [x] Remove CUA-specific skill visibility and auto-pin logic from core.
-- [x] Add tests for plugin-owned skill enable/disable behavior.
-
-Validation:
-
-- [ ] Plugin-owned skill appears while plugin enabled.
-- [ ] Plugin-owned skill disappears while plugin disabled.
-- [ ] Existing user skills continue to work.
-
-## M7 Isolated Settings Contribution Host
-
-- [x] Add Settings > Plugins page or section.
-- [x] Add generic plugin list UI.
-- [x] Add settings contribution registry.
-- [ ] Define settings contribution metadata:
- - standalone HTML entry
- - settings asset root
- - dedicated preload path
- - preload type declaration path
-- [x] Load plugin settings as a standalone web bundle in an isolated renderer/webContents.
-- [x] Enable `contextIsolation` for plugin settings renderers.
-- [x] Disable Node integration for plugin settings renderers.
-- [x] Add a dedicated plugin settings preload that exposes only plugin-scoped typed APIs.
-- [x] Add generated or checked `settings-preload.d.ts` support for plugin developers.
-- [x] Add generic plugin action bridge for settings UI actions.
-- [x] Add plugin-owned typed API bridge for CUA settings actions.
-- [ ] Block plugin settings access to DeepChat renderer stores, global IPC, Vue app state, and raw
- Electron APIs.
-- [x] Remove CUA-specific settings card from core MCP settings.
-- [ ] Add renderer tests for plugin list and settings contribution states.
-
-Validation:
-
-- [ ] A fixture plugin settings web bundle appears when plugin enabled.
-- [ ] The settings renderer is destroyed or shows disabled state when plugin disabled.
-- [ ] Plugin settings can call only its preload API and generic plugin host APIs.
-- [ ] Plugin settings cannot call arbitrary DeepChat IPC channels.
-- [ ] Plugin developers can compile against the preload `.d.ts` without importing DeepChat renderer
- internals.
-- [ ] No CUA-specific renderer route or component remains in core.
-
-## M8 Plugin Packaging Toolchain
-
-- [x] Add top-level `plugins/` workspace support if needed.
-- [x] Add `.dcplugin` package builder.
-- [x] Add `.dcplugin` validator.
-- [x] Add checksum generation.
-- [x] Package the macOS CUA helper binary into the plugin artifact.
-- [ ] Add signature metadata handling.
-- [x] Add official-source metadata validation.
-- [x] Require settings HTML and preload type declarations when `settings.contribute` is declared.
-- [ ] Add package fixture tests.
-- [x] Add scripts:
- - `plugin:cua:build`
- - `plugin:cua:package`
- - `plugin:cua:validate`
- - `plugin:cua:package:mac:arm64`
- - `plugin:cua:package:mac:x64`
-
-Validation:
-
-- [ ] Valid fixture package passes.
-- [ ] Missing `plugin.json` fails.
-- [ ] Missing declared skill file fails.
-- [ ] Missing declared settings entry fails.
-- [ ] Missing declared preload `.d.ts` fails.
-- [ ] Checksum mismatch fails.
-- [ ] Non-official source metadata fails in production validation.
-
-## M9 CUA Plugin Source
-
-- [x] Add `plugins/cua/plugin.json`.
-- [x] Add CUA runtime locator.
-- [x] Add CUA helper version/status checks.
-- [x] Add CUA permission status checks.
-- [x] Add CUA install guide action.
-- [ ] Add optional CUA helper uninstall action with confirmation.
-- [x] Resolve bundled helper app paths from plugin runtime records.
-- [x] Add `mcp/cua-driver.json`.
-- [x] Inject CUA MCP env from plugin runtime template values.
-- [x] Add `policies/tool-policy.json`.
-- [x] Add `skills/cua-driver/SKILL.md` and related upstream skill docs.
-- [x] Rewrite CUA skill docs to guide model actions through MCP tools only.
-- [x] Inject plugin root, process architecture, and owner plugin context into plugin-owned skills.
-- [x] Keep CUA driver MCP-mode cache errors focused on `get_window_state` ordering.
-- [x] Add CUA settings standalone web bundle for missing helper, installed helper, permissions, MCP,
- and skill state.
-- [x] Add CUA plugin settings preload `.d.ts`.
-- [x] Add GitHub Release install fallback and local `.dcplugin` picker in Settings > Plugins.
-- [x] Add CUA plugin-specific typed settings API implementation:
- - `getRuntimeStatus`
- - `checkPermissions`
- - `openPermissionGuide`
- - `uninstallHelper`
-- [~] Add CUA plugin tests with mocked runtime detection and static package assertions.
-
-Validation:
-
-- [ ] Missing helper shows install guidance.
-- [x] Installed helper shows version and path.
-- [x] Plugin enable registers CUA MCP, skill, settings, and policy resources.
-- [x] Plugin enable starts the CUA MCP server when global MCP is ready and enabled.
-- [x] Plugin disable unregisters CUA MCP, skill, settings, and policy resources.
-- [ ] CUA settings UI works through only the plugin-specific preload API.
-
-## M10 Remove Built-In CUA Demo Code
-
-- [x] Remove `src/main/presenter/computerUsePresenter`.
-- [x] Remove `src/renderer/api/ComputerUseClient.ts`.
-- [x] Remove `src/shared/contracts/routes/computerUse.routes.ts`.
-- [x] Remove `src/shared/types/computerUse.ts`.
-- [x] Remove `src/renderer/settings/components/ComputerUseSettingsCard.vue`.
-- [x] Remove `resources/skills/cua-driver`.
-- [x] Remove `vendor/cua-driver`.
-- [x] Remove `scripts/build-cua-driver.mjs`.
-- [x] Remove `scripts/update-cua-driver.mjs`.
-- [x] Remove `build/entitlements.computer-use.plist`.
-- [x] Remove CUA helper handling from `scripts/afterPack.js`.
-- [x] Remove CUA helper steps from build and release workflows.
-- [x] Remove CUA-specific tests or convert them into plugin/generic infrastructure tests.
-
-Validation:
-
-- [x] Core grep acceptance passes.
-- [x] macOS app packaging no longer contains CUA helper or vendored source.
-- [ ] Existing MCP, skills, and settings tests still pass.
-
-## M11 CI And Release
-
-- [x] Add plugin build job to build workflow.
-- [x] Add plugin build job to release workflow.
-- [x] Upload `.dcplugin` as a separate artifact.
-- [x] Require both macOS CUA plugin architecture assets before release upload.
-- [x] Keep each CUA `.dcplugin` limited to its target runtime architecture.
-- [ ] Publish or attach official-source metadata for the CUA plugin artifact.
-- [x] Keep core app artifact naming unchanged.
-- [ ] Ensure app release jobs do not depend on plugin jobs unless publishing requires all artifacts.
-- [x] Document plugin artifact installation path and manual QA flow.
-
-Validation:
-
-- [ ] Core app CI succeeds without CUA helper build.
-- [x] Plugin CI produces `deepchat-plugin-cua--darwin-x64.dcplugin`.
-- [x] Plugin CI produces `deepchat-plugin-cua--darwin-arm64.dcplugin`.
-- [x] Release assets include app artifacts and plugin artifacts separately.
-- [ ] Production install validates the released CUA plugin as official-source trusted.
-
-## M12 End-To-End QA
-
-- [ ] macOS without Cua Driver:
- - plugin installs from official source
- - plugin enables
- - missing helper state appears
- - no CUA MCP tools run
-- [ ] macOS with Cua Driver:
- - helper version detected
- - permissions detected
- - MCP starts
- - skill appears
- - read/status tools work
- - action tools ask for approval
-- [ ] Settings isolation:
- - CUA settings load in isolated renderer
- - CUA settings preload exposes only CUA typed API
- - CUA settings cannot access DeepChat renderer globals or arbitrary IPC
-- [ ] Disable:
- - MCP stops
- - skill disappears
- - settings contribution disappears or becomes disabled
- - policies disappear
-- [ ] Delete:
- - plugin directory removed
- - resource records removed
- - restart has no stale CUA records
-- [ ] External helper:
- - `CuaDriver.app` remains installed after plugin disable/delete
- - DeepChat has no CUA TCC permission ownership
-
-Validation:
-
-- [ ] Acceptance criteria from [spec.md](./spec.md) are checked and recorded in the PR.
-
-## Quality Gates
-
-- [ ] `pnpm run format`
-- [ ] `pnpm run i18n`
-- [ ] `pnpm run lint`
-- [ ] `pnpm run typecheck`
-- [ ] Focused main process tests
-- [ ] Focused renderer tests
-- [ ] Manual macOS CUA plugin QA
diff --git a/docs/archives/default-model-settings/plan.md b/docs/archives/default-model-settings/plan.md
deleted file mode 100644
index e295751b6..000000000
--- a/docs/archives/default-model-settings/plan.md
+++ /dev/null
@@ -1,130 +0,0 @@
-# 默认模型与默认视觉模型实施计划
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 当前实现基线
-
-### 1.1 新建会话模型来源(现状)
-
-1. `src/renderer/src/components/NewThread.vue` 初始化模型时,优先“最近会话/偏好模型/第一个可用模型”。
-2. `src/main/presenter/sessionPresenter/managers/conversationManager.ts` 在 `createConversation()` 中默认继承最近会话 `settings`。
-3. 因此当前“新建会话默认模型”不稳定,会受最近会话影响。
-
-### 1.2 imageServer 模型来源(现状)
-
-1. `src/main/presenter/mcpPresenter/inMemoryServers/imageServer.ts` 构造函数接收 `provider/model`。
-2. `src/main/presenter/mcpPresenter/inMemoryServers/builder.ts` 通过 `new ImageServer(args[0], args[1])` 传入。
-3. `src/renderer/src/components/mcp-config/mcpServerForm.vue` 存在 `imageServer` 专属模型选择 UI,并把选择写入 server `args`。
-
-## 2. 设计决策
-
-### 2.1 设置数据结构
-
-新增两个设置键(存储于 `app-settings`):
-
-1. `defaultModel: { providerId: string; modelId: string }`
-2. `defaultVisionModel: { providerId: string; modelId: string }`
-
-说明:
-
-1. 两者均通过现有 `configPresenter.getSetting/setSetting` 访问。
-2. 不新增独立 store 文件,先沿用现有配置存储体系。
-
-### 2.2 新建会话默认模型决策
-
-会话创建链路分两层处理:
-
-1. **Renderer 层(UI 体验)**:`NewThread.vue` 初始化时优先读 `defaultModel`(非 ACP)。
-2. **Main 层(最终兜底)**:`conversationManager.createConversation` 在调用方未显式传 `providerId/modelId` 时应用 `defaultModel`(非 ACP)。
-
-规则:
-
-1. 显式传入 `providerId/modelId` 时不覆盖。
-2. `chatMode === 'acp agent'` 或目标 provider 为 `acp` 时不应用 `defaultModel`。
-3. `defaultModel` 未配置或无效时,回退到现有逻辑(保持兼容)。
-
-### 2.3 默认视觉模型决策
-
-1. `defaultVisionModel` 选择器只展示 `vision=true` 的已启用模型。
-2. 保存时做前置校验(非视觉模型不可保存)。
-3. `imageServer` 运行时读取 `defaultVisionModel`;不再依赖 `args`。
-
-### 2.4 imageServer 架构调整
-
-目标模块:
-
-1. `src/main/presenter/mcpPresenter/inMemoryServers/imageServer.ts`
-2. `src/main/presenter/mcpPresenter/inMemoryServers/builder.ts`
-3. `src/renderer/src/components/mcp-config/mcpServerForm.vue`
-
-调整方式:
-
-1. `ImageServer` 构造函数去掉 provider/model 参数。
-2. 每次视觉调用时动态读取 `defaultVisionModel` 并校验可用性。
-3. `mcpServerForm.vue` 删除 `imageServer` 专属模型选择与 args 反解析逻辑。
-4. `builder.ts` 改为 `new ImageServer()`。
-
-### 2.5 兼容与迁移策略
-
-1. 保留旧 `imageServer.args` 数据但不再使用(兼容读取,不破坏旧配置文件结构)。
-2. 不做强制迁移脚本;缺失 `defaultVisionModel` 时由运行时错误提示引导用户配置。
-
-## 3. 实施阶段
-
-### Phase 1:配置与类型接入
-
-1. 新增 `defaultModel/defaultVisionModel` 的读写与默认空值处理。
-2. 补充必要类型定义(若现有类型未覆盖)。
-
-### Phase 2:新建会话默认模型
-
-1. 调整 `NewThread.vue` 初始化优先级(`defaultModel` 优先)。
-2. 调整 `conversationManager.createConversation` 的兜底模型决策。
-3. 校验 `fork` 路径未被覆盖。
-
-### Phase 3:默认视觉模型与 imageServer
-
-1. 设置页新增 `defaultVisionModel` 选择项(vision-only)。
-2. 移除 `mcpServerForm.vue` 中 `imageServer` 模型配置 UI 与 args 绑定逻辑。
-3. `imageServer` 改为全局读取 `defaultVisionModel`。
-4. `builder.ts` 去除 `args[0]/args[1]` 注入。
-
-### Phase 4:验证与收尾
-
-1. 回归新建会话路径(UI 创建、主进程创建)。
-2. 回归 `imageServer` 调用成功与失败场景。
-3. 统一补 i18n 文案与错误提示。
-
-## 4. 测试策略
-
-### 4.1 Main 测试
-
-1. `createConversation`:无显式模型时应用 `defaultModel`。
-2. `createConversation`:ACP 模式不应用 `defaultModel`。
-3. `forkConversation`:继承行为不变。
-4. `imageServer`:读取 `defaultVisionModel` 成功/缺失/无效分支。
-
-### 4.2 Renderer 测试
-
-1. `NewThread` 初始化模型优先级验证(`defaultModel` 优先)。
-2. 设置页视觉模型选择仅展示 vision 模型。
-3. `mcpServerForm` 不再展示 `imageServer` 模型选择控件。
-
-## 5. 风险与缓解
-
-1. 风险:部分隐式创建会话路径未经过 UI,仍可能走旧默认。
-缓解:在 `conversationManager.createConversation` 做主进程兜底。
-
-2. 风险:用户升级后未配置 `defaultVisionModel` 导致 imageServer 报错。
-缓解:统一错误文案,明确引导至设置页。
-
-3. 风险:`defaultModel` 与 `preferredModel` 语义冲突。
-缓解:明确优先级为 `defaultModel > preferredModel`(仅非 ACP)。
-
-## 6. 质量门槛
-
-1. `pnpm run format`
-2. `pnpm run lint`
-3. `pnpm run typecheck`
-4. 关键 main/renderer 测试通过
diff --git a/docs/archives/default-model-settings/spec.md b/docs/archives/default-model-settings/spec.md
deleted file mode 100644
index 4588143cd..000000000
--- a/docs/archives/default-model-settings/spec.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# 默认模型与默认视觉模型规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 概述
-
-新增两个全局设置项:
-
-1. `默认模型`(`defaultModel`)
-2. `默认视觉模型`(`defaultVisionModel`)
-
-其中:
-
-1. `默认模型`用于所有“新建会话”默认模型选择(`fork` 例外,`acp` 模式例外)。
-2. `默认视觉模型`用于视觉场景,当前仅供内置 `imageServer` 使用。
-3. `imageServer` 现有的“按服务器 args 配置模型”能力移除,统一改为读取全局 `defaultVisionModel`。
-4. `默认视觉模型`只能选择具备 `vision` 能力的模型。
-
-## 背景与动机
-
-1. 当前新建会话模型会受到“最近会话/偏好模型”影响,缺少稳定的全局默认入口。
-2. `imageServer` 以 MCP 服务器局部参数维护模型,配置分散,和全局模型管理不一致。
-3. 视觉模型应统一做能力约束(`vision=true`),避免运行时才发现模型不支持图像输入。
-
-## 用户故事
-
-### US-1:新建会话统一默认模型
-
-作为用户,我希望设置一次“默认模型”,以后新建会话时自动使用它,而不是被最近会话模型影响。
-
-### US-2:ACP 模式不受影响
-
-作为用户,我希望 ACP 会话仍按 ACP 机制选模型,不被“默认模型”覆盖。
-
-### US-3:视觉能力统一入口
-
-作为用户,我希望设置一个“默认视觉模型”,内置图片工具直接使用它,不再在 `imageServer` 里重复配置。
-
-## 功能需求
-
-### A. 新增全局设置项
-
-- [ ] 新增 `defaultModel` 配置,数据结构为 `{ providerId: string, modelId: string }`
-- [ ] 新增 `defaultVisionModel` 配置,数据结构为 `{ providerId: string, modelId: string }`
-- [ ] 两项配置均通过 `configPresenter.getSetting/setSetting` 读写并持久化
-
-### B. 新建会话默认模型规则
-
-- [ ] 适用范围:所有“新建会话”路径(即调用 `createConversation` 创建新会话)
-- [ ] 排除范围:`forkConversation`(以及基于分支语义的会话继承路径)不改,继续继承源会话模型
-- [ ] ACP 例外:当会话处于 `acp agent` 模式时,不应用 `defaultModel`
-- [ ] 优先级:当调用方未显式传入 `providerId/modelId` 时,`defaultModel` 优先于“最近会话/旧偏好模型”逻辑
-- [ ] 当 `defaultModel` 未配置或已失效时,回退到当前现有兜底策略
-
-### C. 默认视觉模型规则
-
-- [ ] `defaultVisionModel` 的候选列表仅允许 `vision=true` 的已启用模型
-- [ ] 若用户尝试保存非视觉模型,需阻止并给出明确提示
-- [ ] 若 `defaultVisionModel` 未配置或失效,视觉调用返回可读错误并引导去设置页配置
-
-### D. imageServer 统一使用全局视觉模型
-
-- [ ] `imageServer` 不再从 MCP server `args` 读取 provider/model
-- [ ] `imageServer` 每次视觉调用前从全局配置读取 `defaultVisionModel`
-- [ ] `inMemoryServers/builder.ts` 中 `imageServer` 构造不再依赖 `args[0]/args[1]`
-- [ ] MCP 配置表单中针对 `imageServer` 的模型选择 UI 移除
-
-### E. 验收标准
-
-- [ ] 在非 ACP 新建会话中,未手动改模型时默认使用 `defaultModel`
-- [ ] `fork` 新会话继续继承原会话模型,不受 `defaultModel` 干预
-- [ ] ACP 新建会话不受 `defaultModel` 影响
-- [ ] `imageServer` 在已配置 `defaultVisionModel` 时可正常调用视觉能力
-- [ ] `imageServer` 在未配置/配置无效时给出明确错误(非静默失败)
-- [ ] `imageServer` 相关 MCP args 模型配置入口已移除
-
-## 非目标
-
-1. 不改标题生成链路的模型选择策略(本次仅新增会话默认模型与视觉默认模型)。
-2. 不新增“按工具分别配置视觉模型”的能力(仅一个全局视觉模型)。
-3. 不修改 ACP 模型管理机制。
-
-## 约束
-
-1. 保持现有 Presenter 架构与 IPC 类型边界,不引入新通信通道。
-2. 保持设置持久化兼容,旧配置文件可继续加载。
-3. UI 文案必须走 i18n。
-
-## 开放问题
-
-无。
diff --git a/docs/archives/default-model-settings/tasks.md b/docs/archives/default-model-settings/tasks.md
deleted file mode 100644
index a4e221b2c..000000000
--- a/docs/archives/default-model-settings/tasks.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# 默认模型与默认视觉模型 Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## T0 规格确认
-
-- [x] 完成 `spec.md`
-- [x] 完成 `plan.md`
-- [x] 完成 `tasks.md`
-
-## T1 配置层
-
-- [ ] 在配置体系新增 `defaultModel` 设置读写(`providerId/modelId`)。
-- [ ] 在配置体系新增 `defaultVisionModel` 设置读写(`providerId/modelId`)。
-- [ ] 为设置页提供读取/保存接口(复用 `configPresenter.getSetting/setSetting`)。
-
-## T2 新建会话默认模型(Renderer + Main)
-
-- [ ] 调整 `src/renderer/src/components/NewThread.vue` 初始化模型优先级:非 ACP 时优先 `defaultModel`。
-- [ ] 保持手动选模可覆盖默认值(仅默认初始化受影响)。
-- [ ] 在 `src/main/presenter/sessionPresenter/managers/conversationManager.ts` 中补主进程兜底:未显式模型且非 ACP 时应用 `defaultModel`。
-- [ ] 验证主进程自动建会话入口(如 in-memory server 调用 `createConversation`)同样生效。
-- [ ] 验证 `forkConversation` 路径不受影响。
-
-## T3 设置页 UI
-
-- [ ] 在设置页新增“默认模型”选择项(全模型,排除 ACP provider)。
-- [ ] 在设置页新增“默认视觉模型”选择项(仅 `vision=true`)。
-- [ ] 补齐 i18n 文案(至少 `zh-CN` + `en-US`)。
-- [ ] 视觉模型保存时增加校验与错误提示。
-
-## T4 imageServer 改造
-
-- [ ] 修改 `src/main/presenter/mcpPresenter/inMemoryServers/imageServer.ts`:移除构造注入 provider/model,改为运行时读取 `defaultVisionModel`。
-- [ ] 修改 `src/main/presenter/mcpPresenter/inMemoryServers/builder.ts`:`imageServer` 改为无参构造。
-- [ ] 修改 `src/renderer/src/components/mcp-config/mcpServerForm.vue`:删除 `imageServer` 模型选择 UI、args 反解析与写回逻辑。
-- [ ] 保持其他 inmemory server 的 args 行为不变。
-
-## T5 失败处理与提示
-
-- [ ] `defaultVisionModel` 缺失时,`imageServer` 返回可读错误。
-- [ ] `defaultVisionModel` 指向非视觉或不可用模型时,`imageServer` 返回可读错误。
-- [ ] 错误提示文案包含“去设置中配置默认视觉模型”。
-
-## T6 测试
-
-- [ ] Main:`createConversation` 非 ACP 默认模型应用测试。
-- [ ] Main:ACP 场景不应用默认模型测试。
-- [ ] Main:`forkConversation` 不受影响测试。
-- [ ] Main:`imageServer` 读取 `defaultVisionModel` 成功/失败测试。
-- [ ] Renderer:默认视觉模型只展示 vision 模型测试。
-- [ ] Renderer:`mcpServerForm` 不再出现 `imageServer` 专属模型配置测试。
-
-## T7 质量检查
-
-- [ ] `pnpm run format`
-- [ ] `pnpm run lint`
-- [ ] `pnpm run typecheck`
-- [ ] 跑相关测试并记录结果
diff --git a/docs/archives/i18n-missing-translations/plan.md b/docs/archives/i18n-missing-translations/plan.md
deleted file mode 100644
index 94b1e096d..000000000
--- a/docs/archives/i18n-missing-translations/plan.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Plan
-
-## Scope
-
-The scan found four real missing key paths that are referenced from renderer code:
-
-- `mcp.errors.loadClientsFailed`
-- `mcp.prompts.required`
-- `promptSetting.uploadFailed`
-- `settings.mcp.noServersDescription`
-
-`searchDisclaimer` is supplied from each locale `index.ts`, and `settings.display.*` is dynamically built from existing `text-sm`, `text-base`, `text-lg`, `text-xl`, and `text-2xl` keys, so those are not changed.
-
-## Implementation
-
-- Add the missing keys to every locale JSON file in the matching namespace.
-- Re-run the i18n type generator so `src/types/i18n.d.ts` reflects the source locale.
-- Validate with format, i18n check, and lint.
-
-## Test Strategy
-
-- Run `pnpm run i18n:types`.
-- Run `pnpm run format`.
-- Run `pnpm run i18n`.
-- Run `pnpm run lint`.
diff --git a/docs/archives/i18n-missing-translations/spec.md b/docs/archives/i18n-missing-translations/spec.md
deleted file mode 100644
index e33478126..000000000
--- a/docs/archives/i18n-missing-translations/spec.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Missing i18n Translations
-
-## User Story
-
-As a DeepChat user, I want every supported locale to provide translations for UI keys that are currently used by the app so that the interface never falls back to raw i18n key strings.
-
-## Acceptance Criteria
-
-- All statically used i18n keys found missing from the active locale bundles are added to every supported locale.
-- `pnpm run i18n` reports no missing or invalid translations.
-- The generated i18n type definitions include the restored source-locale keys.
-
-## Non-Goals
-
-- Do not rewrite existing translations unrelated to missing keys.
-- Do not remove stale extra keys that are not currently used by the UI.
-- Do not change runtime i18n loading behavior.
-
-## Constraints
-
-- Keep the existing locale file layout under `src/renderer/src/i18n//`.
-- Preserve interpolation placeholders such as `{count}` and `{serverName}` exactly where needed.
diff --git a/docs/archives/i18n-missing-translations/tasks.md b/docs/archives/i18n-missing-translations/tasks.md
deleted file mode 100644
index 1438e7925..000000000
--- a/docs/archives/i18n-missing-translations/tasks.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Tasks
-
-- [x] Create SDD notes for the missing translation fix.
-- [x] Add `mcp.errors.loadClientsFailed` and `mcp.prompts.required` in all locales.
-- [x] Add `promptSetting.uploadFailed` in all locales.
-- [x] Add `settings.mcp.noServersDescription` in all locales.
-- [x] Regenerate i18n types and run validation commands.
-- [x] Archive the completed SDD notes.
diff --git a/docs/archives/legacy-agentpresenter-architecture.md b/docs/archives/legacy-agentpresenter-architecture.md
deleted file mode 100644
index 2f710276e..000000000
--- a/docs/archives/legacy-agentpresenter-architecture.md
+++ /dev/null
@@ -1,320 +0,0 @@
-# DeepChat 整体架构概览
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-本文档从高层视角介绍 DeepChat 的系统架构,帮助开发者快速理解项目结构和组件关系。
-
-> **Note (2026-03-09):** 本文档描述的是原始 AgentPresenter 架构。当前主入口是 `agentSessionPresenter` + `agentRuntimePresenter`(formerly `newAgentPresenter` + `deepchatAgentPresenter`),详见 [P0 Implementation Summary](./P0_IMPLEMENTATION_SUMMARY.md)。
-
-## 🏗️ 核心组件关系
-
-```mermaid
-graph TB
- subgraph "Main Process - 主进程"
- EventBus[EventBus 事件总线]
-
- subgraph "会话管理层"
- SessionP[SessionPresenter 会话生命周期]
- SessionMgr[SessionManager 会话上下文]
- MsgMgr[MessageManager 消息管理]
- ConvMgr[ConversationManager 会话管理]
- end
-
- subgraph "Agent 编排器层"
- AgentP[AgentPresenter Agent编排入口]
- AgentLoop[agentLoopHandler Agent Loop主循环]
- StreamGen[streamGenerationHandler 流生成协调]
- LoopOrch[loopOrchestrator 循环编排]
- ToolCallProc[toolCallProcessor 工具调用处理]
- LLMEvent[llmEventHandler LLM事件处理]
- PermHandler[permissionHandler 权限协调]
- end
-
- subgraph "工具路由层"
- ToolP[ToolPresenter 统一工具定义]
- ToolMapper[ToolMapper 工具名称路由]
- McpP[McpPresenter MCP集成]
- AgentToolMgr[AgentToolManager Agent工具管理]
- end
-
- subgraph "其他 Presenter"
- LLMProviderP[LLMProviderPresenter LLM提供商]
- ConfigP[ConfigPresenter 配置管理]
- WindowP[WindowPresenter 窗口管理]
- TabP[TabPresenter 标签管理]
- SQLiteP[SQLitePresenter 数据库]
- end
-
- subgraph "渲染进程通信"
- Renderer[渲染进程 Vue 3 + Pinia]
- end
- end
-
- EventBus --> SessionP
- EventBus --> AgentP
- EventBus --> McpP
- EventBus --> ConfigP
- EventBus --> LLMProviderP
-
- SessionP --> SessionMgr
- SessionP --> MsgMgr
- SessionP --> ConvMgr
-
- AgentP --> AgentLoop
- AgentP --> StreamGen
- AgentP --> PermHandler
- AgentP --> SessionP
-
- AgentLoop --> LoopOrch
- AgentLoop --> ToolCallProc
-
- StreamGen --> LLMEvent
- StreamGen --> AgentLoop
-
- ToolCallProc --> ToolP
-
- ToolP --> ToolMapper
- ToolP --> McpP
- ToolP --> AgentToolMgr
-
- AgentLoop --> LLMProviderP
- StreamGen --> LLMProviderP
-
- SessionMgr -.状态分享.-> AgentP
-
- EventBus -.事件推送.-> Renderer
- Renderer -.IPC调用.-> SessionP
- Renderer -.IPC调用.-> AgentP
-
- classDef layer1 fill:#e3f2fd
- classDef layer2 fill:#fff3e0
- classDef layer3 fill:#f3e5f5
- classDef layer4 fill:#e8f5e9
-
- class EventBus layer1
- class SessionP,SessionMgr,MsgMgr,ConvMgr layer2
- class AgentP,AgentLoop,StreamGen,LoopOrch,ToolCallProc,LLMEvent,PermHandler layer3
- class ToolP,ToolMapper,McpP,AgentToolMgr layer4
- class LLMProviderP,ConfigP,WindowP,TabP,SQLiteP layer1
-```
-
-## 📐 分层架构
-
-### 1. 会话管理层
-
-**职责**:管理对话会话的完整生命周期、消息持久化、标签页绑定
-
-| 组件 | 文件位置 | 行数 | 核心职责 |
-|------|---------|------|---------|
-| SessionPresenter | `src/main/presenter/sessionPresenter/index.ts` | 900 | 会话 CRD、消息 CRD、标签绑定、分支 |
-| SessionManager | `src/main/presenter/sessionPresenter/session/sessionManager.ts` | 245 | 会话上下文解析、运行时状态、workspace 路径解析 |
-| MessageManager | `src/main/presenter/sessionPresenter/managers/messageManager.ts` | ~400 | 消息持久化、变体处理、上下文获取 |
-| ConversationManager | `src/main/presenter/sessionPresenter/managers/conversationManager.ts` | ~500 | 会话 CRUD、fork、子会话、标签绑定 |
-
-**关键数据结构**:
-- `SessionContext` - 会话运行时状态(status, resolved, runtime)
-- `SessionContextResolved` - 已解析的会话配置(chatMode, providerId, modelId, workspace)
-- `SessionStatus` - 'idle' \| 'generating' \| 'paused' \| 'waiting_permission' \| 'error'
-
-### 2. Agent 编排器层
-
-**职责**:管理 Agent Loop、LLM 流式响应、工具调用、权限协调
-
-| 组件 | 历史模块标识 | 行数 | 核心职责 |
-|------|---------|------|---------|
-| AgentPresenter | `legacy agent runtime / agentPresenter/index.ts` | 472 | Agent 编排入口,sendMessage/cancelLoop/continueLoop |
-| agentLoopHandler | `legacy agent runtime / loop/agentLoopHandler.ts` | 670 | Agent Loop 主循环(while 循环) |
-| streamGenerationHandler | `legacy agent runtime / streaming/streamGenerationHandler.ts` | 645 | 流生成协调,准备上下文、启动 Stream |
-| loopOrchestrator | `legacy agent runtime / loop/loopOrchestrator.ts` | ~30 | Loop 状态管理 |
-| toolCallProcessor | `legacy agent runtime / loop/toolCallProcessor.ts` | 445 | 工具调用执行、结果处理 |
-| llmEventHandler | `legacy agent runtime / streaming/llmEventHandler.ts` | ~400 | 标准化 LLM 事件到内部格式 |
-| permissionHandler | `legacy agent runtime / permission/permissionHandler.ts` | ~600 | 权限请求响应协调 |
-| messageBuilder | `legacy agent runtime / message/messageBuilder.ts` | ~285 | 提示词构建、上下文压缩 |
-
-**关键流程**:
-1. 用户发送消息 → `AgentPresenter.sendMessage()`
-2. 创建助手消息 → `SessionManager.startLoop()` 状态设为 `generating`
-3. `StreamGenerationHandler` 准备上下文 → 启动 LLM Stream
-4. `AgentLoopHandler` 的主 while 循环处理:
- - 调用 `provider.coreStream()` 获取标准化事件流
- - 处理 text/reasoning/tool_call_start/tool_call_chunk/tool_call_end 事件
- - 遇到 tool_call_end 时执行 `ToolCallProcessor`
- - 执行工具后继续循环或结束
-
-### 3. 工具路由层
-
-**职责**:统一管理所有工具(MCP + Agent)、工具名称解析、路由分发
-
-| 组件 | 历史模块标识 | 行数 | 核心职责 |
-|------|---------|------|---------|
-| ToolPresenter | `src/main/presenter/toolPresenter/index.ts` | 161 | 统一工具定义接口、工具调用路由 |
-| ToolMapper | `src/main/presenter/toolPresenter/toolMapper.ts` | ~100 | 工具名→来源映射(mcp/agent) |
-| McpPresenter | `src/main/presenter/mcpPresenter/index.ts` | ~500 | MCP 服务器管理、工具定义、工具调用 |
-| AgentToolManager | `legacy agent runtime / acp/agentToolManager.ts` | 577 | Agent 文件系统 + Browser 工具 |
-| AgentFileSystemHandler | `legacy agent runtime / acp/agentFileSystemHandler.ts` | 960 | 文件系统工具实现 |
-
-**工具来源**:
-1. **MCP 工具**:外部 MCP 服务器提供,通过 `McpPresenter` 管理
-2. **Agent 工具**:
- - 文件系统工具(read_file, write_file, list_directory 等)
- - Yo Browser 工具
-
-**路由机制**:
-- `ToolPresenter.getAllToolDefinitions()` 收集所有工具
-- `ToolMapper.registerTools()` 按工具名注册来源(mcp/agent)
-- 名称冲突时优先 MCP
-- `ToolPresenter.callTool()` 根据 `ToolMapper` 路由到对应处理器
-
-### 4. 事件通信层
-
-**职责**:主进程内事件广播、主进程→渲染进程事件推送
-
-| 组件 | 文件位置 | 行数 | 核心职责 |
-|------|---------|------|---------|
-| EventBus | `src/main/eventbus.ts` | 152 | 统一事件发射和接收 |
-| events.ts | `src/main/events.ts` | 263 | 事件常量定义 |
-
-**通信模式**:
-- `sendToMain(eventName, ...args)` - 仅主进程内部
-- `sendToRenderer(eventName, SendTarget, ...args)` - 主→渲染进程
-- `sendToTab(tabId, eventName, ...args)` - 精确到特定标签
-- `sendToWindow(windowId, eventName, ...args)` - 窗口级别
-
-**关键事件类别**:
-- `STREAM_EVENTS` - 流生成事件(response, end, error)
-- `CONVERSATION_EVENTS` - 会话事件(list_updated, activated, message_generated)
-- `CONFIG_EVENTS` - 配置变更(setting_changed, provider_changed)
-- `MCP_EVENTS` - MCP 状态(server_started, tool_call_result)
-- `TAB_EVENTS` - 标签页事件(closed, renderer-ready)
-
-### 5. 多窗口管理层
-
-| 组件 | 文件位置 | 行数 | 核心职责 |
-|------|---------|------|---------|
-| WindowPresenter | `src/main/presenter/windowPresenter/index.ts` | ~300 | BrowserWindow 生命周期 |
-| TabPresenter | `src/main/presenter/tabPresenter/index.ts` | ~400 | WebContentsView 管理、跨窗口拖拽 |
-
-## 🔄 关键数据流
-
-### 消息发送流程
-
-```mermaid
-sequenceDiagram
- participant User as 用户
- participant Renderer as 渲染进程
- participant AgentP as AgentPresenter
- participant StreamGen as StreamGenerationHandler
- participant SessionMgr as SessionManager
- participant AgentLoop as agentLoopHandler
- participant LLMProvider as LLMProviderPresenter
- participant ToolP as ToolPresenter
-
- User->>Renderer: 发送消息
- Renderer->>AgentP: sendMessage(agentId, content)
- AgentP->>AgentP: 创建用户消息到数据库
- AgentP->>SessionMgr: startLoop(agentId, messageId)
- Note over SessionMgr: status = 'generating'
- AgentP->>AgentP: 创建助手消息
- AgentP->>StreamGen: startStreamCompletion()
- StreamGen->>StreamGen: 准备上下文(用户消息、历史消息、搜索结果)
- StreamGen->>AgentLoop: startStreamCompletion()
- AgentLoop->>LLMProvider: provider.coreStream(messages, tools)
-
- loop Agent Loop
- LLMProvider-->>AgentLoop: 流式 LLMCoreStreamEvent
- AgentLoop->>AgentLoop: 处理 text/tool_call_start/tool_call_end
- alt 有工具调用
- AgentLoop->>ToolP: callTool(toolCall)
- ToolP-->>AgentLoop: 工具执行结果
- AgentLoop->>AgentLoop: 添加 tool_result 到上下文
- AgentLoop->>LLMProvider: 继续下一次 LLM 调用
- else 无工具调用
- AgentLoop->>AgentLoop: 提示 completion
- end
- end
-
- AgentLoop-->>Renderer: 通过 EventBus 发送流事件
-```
-
-### 会话上下文解析
-
-```typescript
-// SessionManager.getSession(conversationId)
-// → SessionManager.resolveSession(conversationId)
-// → resolveSessionContext({
-// settings: conversation.settings,
-// fallbackChatMode: 'chat',
-// modelConfig: modelConfig
-// })
-
-// 返回 SessionContextResolved:
-{
- chatMode: 'chat' | 'agent' | 'acp agent',
- providerId: string,
- modelId: string,
- supportsVision: boolean,
- supportsFunctionCall: boolean,
- agentWorkspacePath: string | null, // agent 模式才有
- enabledMcpTools?: string[],
- acpWorkdirMap?: Record // acp agent 模式
-}
-```
-
-### 工具调用路由
-
-```typescript
-// agentLoopHandler 获取工具定义
-const toolDefs = await toolPresenter.getAllToolDefinitions({
- enabledMcpTools,
- chatMode,
- supportsVision,
- agentWorkspacePath
-})
-// → 组合 MCP 工具 + Agent 文件系统工具 + Browser 工具
-
-// LLM 返回 tool_call 后
-const response = await toolPresenter.callTool({
- id: toolCallId,
- type: 'function',
- function: { name, arguments: string },
- server: { name, icons, description }
-})
-// → ToolMapper.getToolSource(name)
-// → 若 'mcp' → mcpPresenter.callTool()
-// → 若 'agent' → agentToolManager.callTool()
-```
-
-## 📁 核心文件位置速查
-
-**会话管理**:
-- SessionPresenter: `src/main/presenter/sessionPresenter/index.ts:1-900`
-- SessionManager: `src/main/presenter/sessionPresenter/session/sessionManager.ts:1-245`
-- MessageManager: `src/main/presenter/sessionPresenter/managers/messageManager.ts`
-- ConversationManager: `src/main/presenter/sessionPresenter/managers/conversationManager.ts`
-
-**Agent 系统**:
-- AgentPresenter: `legacy agent runtime / agentPresenter/index.ts`
-- Agent Loop: `legacy agent runtime / loop/agentLoopHandler.ts`
-- Stream Generation: `legacy agent runtime / streaming/streamGenerationHandler.ts`
-- Message Builder: `legacy agent runtime / message/messageBuilder.ts`
-
-**工具系统**:
-- ToolPresenter: `src/main/presenter/toolPresenter/index.ts:1-161`
-- ToolMapper: `src/main/presenter/toolPresenter/toolMapper.ts`
-- AgentToolManager: `legacy agent runtime / acp/agentToolManager.ts`
-- AgentFileSystemHandler: `legacy agent runtime / acp/agentFileSystemHandler.ts`
-- McpPresenter: `src/main/presenter/mcpPresenter/index.ts`
-
-**事件系统**:
-- EventBus: `src/main/eventbus.ts:1-152`
-- 事件常量: `src/main/events.ts:1-263`
-
-## 📚 深入阅读
-
-- **会话管理详情**: [architecture/session-management.md](../architecture/session-management.md)
-- **Agent 系统详解**: [architecture/agent-system.md](../architecture/agent-system.md)
-- **工具系统详解**: [architecture/tool-system.md](../architecture/tool-system.md)
-- **事件系统详解**: [architecture/event-system.md](../architecture/event-system.md)
-- **核心流程**: [FLOWS.md](../FLOWS.md)
-- **MCP 集成**: [architecture/mcp-integration.md](../architecture/mcp-integration.md)
diff --git a/docs/archives/legacy-agentpresenter-flows.md b/docs/archives/legacy-agentpresenter-flows.md
deleted file mode 100644
index b4e5249e4..000000000
--- a/docs/archives/legacy-agentpresenter-flows.md
+++ /dev/null
@@ -1,654 +0,0 @@
-# DeepChat 核心流程
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-本文档使用时序图详细描述 DeepChat 的关键业务流程,帮助开发者理解运行时行为。
-
-> **Note (2026-03-09):** 本文档描述的是原始 AgentPresenter 流程。当前主流程入口是 `agentSessionPresenter` + `agentRuntimePresenter`(formerly `newAgentPresenter` + `deepchatAgentPresenter`);核心流程类似但入口不同。详见 [P0 Implementation Summary](./P0_IMPLEMENTATION_SUMMARY.md)。
-
-## 1. 发送消息完整流程
-
-```mermaid
-sequenceDiagram
- autonumber
- participant User as 用户
- participant UI as ChatInput/ChatView.vue
- participant Store as chatStore.sendMessage()
- participant IPC as presenter:call (IPC)
- participant AgentP as AgentPresenter.sendMessage()
- participant MsgMgr as MessageManager
- participant StreamGen as StreamGenerationHandler
- participant SessionMgr as SessionManager
- participant AgentLoop as agentLoopHandler
- participant ToolP as ToolPresenter
- participant LLM as LLMProviderPresenter
- participant EventBus as EventBus
-
- User->>UI: 输入内容并点击发送
- UI->>Store: handleSend(message)
- Store->>IPC: presenter:call(agentPresenter.sendMessage)
- IPC->>AgentP: sendMessage(agentId, content)
-
- Note over AgentP,MsgMgr: 1. 创建用户消息
- AgentP->>MsgMgr: sendMessage(agentId, content, 'user')
- MsgMgr-->>AgentP: userMessage
-
- Note over AgentP,MsgMgr: 2. 创建助手消息(初始为空)
- AgentP->>MsgMgr: sendMessage(agentId, '[]', 'assistant')
- MsgMgr-->>AgentP: assistantMessage
-
- Note over AgentP,SessionMgr: 3. 启动 Agent Loop
- AgentP->>SessionMgr: startLoop(agentId, assistantMessage.id)
- SessionMgr->>SessionMgr: status = 'generating'
-
- Note over AgentP,StreamGen: 4. 启动流生成
- AgentP->>StreamGen: startStreamCompletion(agentId)
- StreamGen->>StreamGen: prepareConversationContext()
- StreamGen->>StreamGen: processUserMessageContent()
- alt 启用搜索
- StreamGen->>StreamGen: 执行搜索获取相关信息
- end
- StreamGen->>StreamGen: preparePromptContent(上下文+搜索+图片)
-
- Note over StreamGen,AgentLoop: 5. 启动 Agent Loop
- StreamGen->>AgentLoop: startStreamCompletion()
- AgentLoop->>ToolP: getAllToolDefinitions()
- ToolP-->>AgentLoop: toolDefs (MCP + Agent)
-
- AgentLoop->>LLM: provider.coreStream(messages, tools, modelConfig)
-
- loop Agent Loop 主循环
- Note over AgentLoop: 循环状态: toolCallCount < MAX_TOOL_CALLS
-
- LLM-->>AgentLoop: stream event (text/reasoning/tool_call/permission)
-
- alt text 事件
- AgentLoop->>EventBus: send STREAM_EVENTS.RESPONSE { content }
- else reasoning 事件
- AgentLoop->>EventBus: send STREAM_EVENTS.RESPONSE { reasoning_content }
- else tool_call_start 事件
- AgentLoop->>EventBus: send { tool_call: 'start', name, id }
- else tool_call_chunk 事件
- AgentLoop->>EventBus: send { tool_call: 'update', params增量 }
- else tool_call_end 事件
- Note over AgentLoop: 工具参数完整
- AgentLoop->>EventBus: send { tool_call: 'update', 完整params }
-
- alt ACP Provider
- Note over AgentLoop: ACP 直接返回执行结果
- AgentLoop->>EventBus: send { tool_call: 'end', response }
- else 非 ACP
- Note over AgentLoop: 需要本地执行工具
- AgentLoop->>AgentLoop: currentToolCalls.push({id, name, arguments})
- end
- else permission 事件
- AgentLoop->>EventBus: send { tool_call: 'permission-required' }
- AgentLoop->>AgentLoop: needContinue = false (等待用户响应)
- Note over AgentLoop: 退出循环等待用户批准
- end
-
- alt stop event
- AgentLoop->>AgentLoop: 检查 stop_reason
- alt tool_use
- Note over AgentLoop: 继续循环
- else end/max_tokens
- Note over AgentLoop: 结束循环
- Note over AgentLoop: 需要 break
- end
- end
- end
-
- alt 有工具调用需要执行
- Note over AgentLoop,ToolP: 执行工具调用
- AgentLoop->>ToolP: callTool(toolCall[0])
- ToolP->>ToolP: ToolMapper 路由
- ToolP->>ToolP: 执行工具 (MCP 或 Agent)
- ToolP-->>AgentLoop: toolResponse
- AgentLoop->>EventBus: send { tool_call: 'running' }
- AgentLoop->>EventBus: send { tool_call: 'end', response }
-
- Note over AgentLoop,AgentLoop: 添加工具结果到上下文
- AgentLoop->>AgentLoop: conversationMessages.push(tool_result)
- AgentLoop->>AgentLoop: toolCallCount++
- AgentLoop->>AgentLoop: 继续下一次 LLM 调用
- end
-
- loop 继续循环
- AgentLoop->>LLM: coreStream (带工具结果)
- end
-
- AgentLoop->>EventBus: send STREAM_EVENTS.END
- AgentLoop->>SessionMgr: status = 'idle'
-```
-
-**关键文件位置**:
-- AgentPresenter.sendMessage: `src/main/presenter/agentPresenter/index.ts:139-176`
-- SessionManager.startLoop: `src/main/presenter/sessionPresenter/session/sessionManager.ts:140-150`
-- StreamGenerationHandler.startStreamCompletion: `src/main/presenter/agentPresenter/streaming/streamGenerationHandler.ts:54-179`
-- agentLoopHandler.startStreamCompletion: `src/main/presenter/agentPresenter/loop/agentLoopHandler.ts:145-668`
-
-## 2. 渲染与流式更新流程(含 Minimap)
-
-```mermaid
-sequenceDiagram
- autonumber
- participant UI as ChatInput/ChatView (Renderer)
- participant Store as chatStore (Renderer)
- participant IPC as presenter:call (IPC)
- participant AgentP as AgentPresenter (Main)
- participant StreamGen as StreamGenerationHandler (Main)
- participant LLM as LLMProviderPresenter (Main)
- participant LLMH as LLMEventHandler (Main)
- participant Sched as StreamUpdateScheduler (Main)
- participant List as MessageList (Renderer)
-
- UI->>Store: send(message)
- Store->>IPC: presenter:call(agentPresenter.sendMessage)
- IPC->>AgentP: sendMessage(agentId, content)
- AgentP->>StreamGen: generateAIResponse + startStreamCompletion
- StreamGen->>LLM: startStreamCompletion()
- LLM-->>LLMH: stream chunks
- LLMH->>Sched: enqueueDelta(content/tool_call/usage)
- Sched-->>Store: STREAM_EVENTS.RESPONSE (init/delta)
- Store-->>List: update messageItems
- LLMH-->>Sched: flushAll(final)
- Sched-->>Store: STREAM_EVENTS.RESPONSE (final)
- LLMH-->>Store: STREAM_EVENTS.END/ERROR
-```
-
-**关键文件位置**:
-- chatStore.sendMessage + stream handlers: `src/renderer/src/stores/chat.ts`
-- Presenter IPC: `src/renderer/api/legacy/presenters.ts`, `src/main/presenter/index.ts`
-- AgentPresenter.sendMessage: `src/main/presenter/agentPresenter/index.ts`
-- StreamGenerationHandler.startStreamCompletion: `src/main/presenter/agentPresenter/streaming/streamGenerationHandler.ts`
-- LLMEventHandler + StreamUpdateScheduler: `src/main/presenter/agentPresenter/streaming/llmEventHandler.ts`, `src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts`
-- MessageList: `src/renderer/src/components/chat/MessageList.vue`
-
-## 3. Agent Loop 详细流程
-
-```mermaid
-sequenceDiagram
- autonumber
- participant StreamGen as StreamGenerationHandler
- participant AgentLoop as agentLoopHandler
- participant LLM as LLMProvider
- participant ToolP as ToolPresenter
- participant EventBus as EventBus
-
- StreamGen->>AgentLoop: startStreamCompletion()
-
- activate AgentLoop
- AgentLoop->>AgentLoop: 初始化循环变量
- Note right of AgentLoop: conversationMessages, needContinue, toolCallCount
-
- loop while (needContinueConversation)
- AgentLoop->>AgentLoop: 获取工具定义 (getAllToolDefinitions)
- AgentLoop->>ToolP: getAllToolDefinitions({chatMode, workspace})
- ToolP-->>AgentLoop: toolDefs[]
-
- AgentLoop->>LLM: coreStream(conversationMessages, filteredToolDefs)
-
- loop 处理流事件
- LLM-->>AgentLoop: event (LLMCoreStreamEvent)
-
- alt event.type == 'text'
- AgentLoop->>EventBus: send { content }
- AgentLoop->>AgentLoop: currentContent += event.content
- else event.type == 'reasoning'
- AgentLoop->>EventBus: send { reasoning_content }
- AgentLoop->>AgentLoop: currentReasoning += event.reasoning_content
- else event.type == 'tool_call_start'
- AgentLoop->>EventBus: send { tool_call: 'start', name, id }
- AgentLoop->>AgentLoop: currentToolChunks[id] = {name, arguments_chunk: ''}
- else event.type == 'tool_call_chunk'
- AgentLoop->>EventLoop: send { tool_call: 'update', args }
- AgentLoop->>AgentLoop: currentToolChunks[id].arguments_chunk += chunk
- else event.type == 'tool_call_end'
- AgentLoop->>AgentLoop: 完整合并参数
- alt providerId == 'acp'
- Note over AgentLoop: ACP 已执行,直接返回结果
- AgentLoop->>EventBus: send { tool_call: 'end', response }
- else 非 ACP
- Note over AgentLoop: 需要执行工具
- AgentLoop->>AgentLoop: currentToolCalls.push({id, name, arguments})
- end
- else event.type == 'permission'
- AgentLoop->>EventBus: send { tool_call: 'permission-required' }
- AgentLoop->>AgentLoop: 循环退出,等待用户响应
- else event.type == 'stop'
- AgentLoop->>AgentLoop: 检查 stop_reason
- alt stop_reason == 'tool_use'
- Note over AgentLoop: needContinue = true
- else 其他
- Note over AgentLoop: needContinue = false
- end
- end
- end
-
- Note over AgentLoop: 添加 assistant 消息到上下文
- AgentLoop->>AgentLoop: conversationMessages.push({role: 'assistant', content: currentContent})
-
- alt needContinue && currentToolCalls.length > 0
- Note over AgentLoop: 执行工具调用
- AgentLoop->>ToolP: 批量调用工具
- loop 执行每个工具
- ToolP-->>AgentLoop: toolResult
- AgentLoop->>EventBus: 发送工具执行事件
- AgentLoop->>AgentLoop: conversationMessages.push(tool_result)
- end
- AgentLoop->>AgentLoop: toolCallCount++
- end
- end
- deactivate AgentLoop
-
- AgentLoop->>EventBus: send STREAM_EVENTS.END {userStop}
-```
-
-**关键代码位置**:
-- agentLoopHandler 主循环: `src/main/presenter/agentPresenter/loop/agentLoopHandler.ts:223-626`
-
-## 4. 工具调用路由流程
-
-```mermaid
-sequenceDiagram
- autonumber
- participant AgentLoop as agentLoopHandler
- participant ToolP as ToolPresenter
- participant Mapper as ToolMapper
- participant McpP as McpPresenter
- participant AgentToolMgr as AgentToolManager
- participant FsHandler as AgentFileSystemHandler
-
- AgentLoop->>ToolP: callTool({id, function: {name, arguments}, server})
-
- ToolP->>Mapper: getToolSource(name)
- Mapper-->>ToolP: source ('mcp' or 'agent')
-
- alt source == 'mcp'
- ToolP->>McpP: callTool(request)
- Note over McpP: MCP 工具执行
- McpP-->>McpP: 获取工具定义
- McpP-->>McpP: 权限检查
- McpP->>McpP: 调用 MCP 服务器
- McpP-->>ToolP: toolResponse
- else source == 'agent'
- ToolP->>AgentToolMgr: callTool(name, args, conversationId)
-
- alt 工具名以 filesystem 开头
- AgentToolMgr->>FsHandler: read_file/write_file/list_directory
- Note over FsHandler: 路径安全检查 执行文件操作
- FsHandler-->>AgentToolMgr: fileResult
- else 工具是 browser 相关
- AgentToolMgr->>AgentToolMgr: 调用 Browser 工具
- AgentToolMgr-->>AgentToolMgr: browserResult
- end
-
- AgentToolMgr-->>ToolP: toolResponse
- end
-
- ToolP-->>AgentLoop: {content, rawData}
-```
-
-**工具定义收集流程**:
-
-```typescript
-// 1. ToolPresenter.getAllToolDefinitions()
-async getAllToolDefinitions({chatMode, supportsVision, agentWorkspacePath}) {
- // 2. 获取 MCP 工具
- const mcpDefs = await mcpPresenter.getAllToolDefinitions()
- this.mapper.registerTools(mcpDefs, 'mcp')
-
- // 3. chatMode != 'chat' 时获取 Agent 工具
- if (chatMode !== 'chat') {
- const agentDefs = await agentToolManager.getAllToolDefinitions()
-
- // 4. 过滤名称冲突(优先 MCP)
- const filtered = agentDefs.filter(t => !mapper.hasTool(t.name))
- this.mapper.registerTools(filtered, 'agent')
-
- return [...mcpDefs, ...filtered]
- }
-
- return mcpDefs
-}
-```
-
-**关键文件位置**:
-- ToolPresenter: `src/main/presenter/toolPresenter/index.ts:49-99`
-- ToolMapper: `src/main/presenter/toolPresenter/toolMapper.ts`
-- AgentToolManager: `src/main/presenter/agentPresenter/acp/agentToolManager.ts`
-- AgentFileSystemHandler: `src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts`
-
-## 5. 权限请求与响应流程(Batch-level Permission + Resume Lock)
-
-### 完整流程
-
-```mermaid
-sequenceDiagram
- autonumber
- participant AgentLoop as agentLoopHandler
- participant ToolProc as toolCallProcessor
- participant EventBus as EventBus
- participant UI as PermissionDialog.vue
- participant PermHandler as permissionHandler
- participant SessionMgr as SessionManager
- participant ToolP as ToolPresenter
- participant McpP as McpPresenter
-
- Note over AgentLoop: Agent Loop 遇到权限请求
- AgentLoop->>ToolProc: process(toolCalls)
-
- Note over ToolProc: Step 1: 批量预检查权限
- ToolProc->>ToolProc: batchPreCheckPermissions()
-
- loop 遍历每个 toolCall
- ToolProc->>ToolP: callTool(request)
- ToolP->>McpP: callTool(request)
- McpP->>McpP: checkToolPermission()
-
- alt 需要权限请求
- McpP-->>ToolP: requiresPermission: true
- ToolP-->>ToolProc: permission required
- ToolProc->>EventBus: send {tool_call: 'permission-required', ...}
-
- Note over SessionMgr: 添加到 pendingPermissions 队列
- ToolProc->>SessionMgr: addPendingPermission({messageId, toolCallId, ...})
- else 权限已授予
- McpP->>McpP: 执行工具
- McpP-->>ToolP: toolResult
- ToolP-->>ToolProc: toolResult
- end
- end
-
- alt 有待处理权限
- ToolProc->>AgentLoop: 暂停,等待用户响应
- EventBus->>UI: 显示权限请求对话框
- UI->>User: 显示权限请求
-
- User->>UI: 点击"允许"或"拒绝"
- UI->>PermHandler: handlePermissionResponse(messageId, toolCallId, granted, permissionType)
-
- Note over PermHandler: Step 2: 批量更新权限块
- PermHandler->>PermHandler: updatePermissionBlocks()
- Note over PermHandler: canBatchUpdate: 相同 tool_call.id 的权限批量更新
-
- Note over SessionMgr: Step 3: 从队列移除
- PermHandler->>SessionMgr: removePendingPermission(conversationId, messageId, toolCallId)
-
- Note over PermHandler: Step 4: 获取 Resume Lock
- PermHandler->>SessionMgr: acquirePermissionResumeLock(conversationId, messageId)
-
- Note over PermHandler: Step 5: 批准权限
- alt permissionType == 'command'
- PermHandler->>PermHandler: CommandPermissionService.approve()
- else agent-filesystem
- PermHandler->>PermHandler: FilePermissionService.approve()
- else deepchat-settings
- PermHandler->>PermHandler: SettingsPermissionService.approve()
- else MCP 权限
- PermHandler->>McpP: grantPermission(serverName, permissionType, remember)
- else ACP 权限
- PermHandler->>PermHandler: handleAcpPermissionFlow()
- end
-
- Note over PermHandler: Step 6: 恢复工具执行(CRITICAL SECTION)
- PermHandler->>PermHandler: resumeToolExecutionAfterPermissions()
-
- Note over PermHandler: 6a: 验证 Resume Lock
- PermHandler->>SessionMgr: getPermissionResumeLock(conversationId)
- SessionMgr-->>PermHandler: currentLock
-
- alt Lock 无效或过期
- PermHandler->>SessionMgr: releasePermissionResumeLock(conversationId)
- PermHandler->>PermHandler: 跳过执行
- else Lock 有效
- Note over PermHandler: 6b: 重新加载消息状态
- PermHandler->>PermHandler: 从 DB 刷新 generating state
-
- Note over PermHandler: 6c: SYNCHRONOUS FLUSH
- PermHandler->>PermHandler: flushStreamUpdates(messageId)
-
- Note over PermHandler: 6d: 执行工具(Lock 保持)
- loop 遍历已授权工具
- PermHandler->>ToolP: callTool()
- ToolP->>McpP: callTool()
- McpP-->>ToolP: toolResult
- ToolP-->>PermHandler: toolResult
- end
-
- Note over PermHandler: 6e: 再次 FLUSH
- PermHandler->>PermHandler: flushStreamUpdates(messageId)
-
- Note over PermHandler: 6f: 检查是否还有更多权限
- PermHandler->>PermHandler: hasPendingPermissionsInMessage()
-
- alt 还有更多权限
- PermHandler->>SessionMgr: releasePermissionResumeLock(conversationId)
- PermHandler->>UI: 通知前端更新
- else 所有权限已处理
- PermHandler->>PermHandler: continueAfterToolsExecuted()
- PermHandler->>SessionMgr: releasePermissionResumeLock(conversationId)
- PermHandler->>AgentLoop: 继续 Agent Loop
- end
- end
- end
-```
-
-### 关键机制说明
-
-#### 1. Batch-level Permission Update
-
-```typescript
-// 同一个 tool_call 的多个权限块可以批量更新
-function canBatchUpdate(target, granted, grantedType): boolean {
- // 必须相同状态: pending
- // 必须相同类型: tool_call_permission
- // 必须相同 server
- // CRITICAL: 必须相同 tool_call.id(防止误批准其他工具)
- // 权限层级必须满足: grantedType >= targetType
-}
-```
-
-#### 2. Resume Lock(MessageId-level)
-
-```typescript
-// 获取锁
-acquirePermissionResumeLock(conversationId: string, messageId: string): boolean
-
-// 验证锁(防止过期/错误的恢复)
-getPermissionResumeLock(conversationId: string): {messageId, timestamp} | null
-
-// 释放锁(单一出口点)
-releasePermissionResumeLock(conversationId: string): void
-
-// CRITICAL SECTION 保证:
-// - Early-exit checks prevent stale execution
-// - Synchronous flush before executing tools
-// - Lock released only at single exit point
-// - All tools executed atomically (no lock release between tools)
-```
-
-#### 3. Pending Permissions Queue
-
-```typescript
-// 支持多个并发权限请求
-interface PendingPermission {
- messageId: string
- toolCallId: string
- permissionType: string
- serverName: string
- timestamp: number
-}
-
-// SessionManager 管理队列
-pendingPermissions: PendingPermission[]
-
-// 队列操作
-addPendingPermission(conversationId, permission)
-removePendingPermission(conversationId, messageId, toolCallId)
-getNextPendingPermission(conversationId): PendingPermission | undefined
-```
-
-#### 4. Synchronous Flush
-
-```typescript
-// 工具执行前同步刷新 UI 状态
-await llmEventHandler.flushStreamUpdates(messageId)
-
-// 保证:
-// - 所有 tool_call 块已持久化到 DB
-// - 前端 UI 状态已同步
-// - 断点恢复时状态一致
-```
-
-### 权限类型层级
-
-| 类型 | 层级 | 适用场景 |
-|------|------|---------|
-| `all` | 3 | 授予全部权限 |
-| `write` | 2 | 写入操作(write_file, delete_file) |
-| `read` | 1 | 读取操作(read_file, list_directory) |
-| `command` | 0 | 命令执行(精确匹配) |
-
-**权限升级规则**:`all` > `write` > `read`,授予高级权限自动满足低级权限需求。
-
-**关键文件位置**:
-- PermissionHandler: `src/main/presenter/agentPresenter/permission/permissionHandler.ts`
-- ToolCallProcessor: `src/main/presenter/agentPresenter/loop/toolCallProcessor.ts`
-- SessionManager: `src/main/presenter/agentPresenter/session/sessionManager.ts`
-
-## 6. 会话生命周期
-
-```mermaid
-stateDiagram-v2
- [*] --> 未创建: 用户打开聊天界面
-
- 未创建 --> 激活: 创建会话 (createConversation)
- 未创建 --> 激活: 从列表选择会话
-
- 激活 --> 生成中: 用户发送消息 (sendMessage)
-
- 生成中 --> 生成中: Agent Loop 循环执行工具
- 生成中 --> 等待权限: 工具需要权限 (permission-required)
- 生成中 --> 已完成: LLM 完成(无工具或达到最大调用次数)
- 生成中 --> 已取消: 用户停止生成
-
- 等待权限 --> 生成中: 用户批准权限
- 等待权限 --> 已取消: 用户拒绝权限
-
- 已完成 --> 激活: 用户继续对话
- 已完成 --> 已完成: 用户查看历史
-
- 已取消 --> 激活: 用户重新发送消息
-
- 激活 --> 暂停: 切换到其他 Tab
- 暂停 --> 激活: 切换回该 Tab
-
- 激活 --> 分支: 用户选择分支 (forkConversation)
-
- 分支 --> 激活: 新建子会话(部分历史)
-
- 激活 --> 已删除: 用户删除会话
-
- 已删除 --> [*]
-```
-
-**会话创建与绑定流程**:
-
-```mermaid
-sequenceDiagram
- participant UI as 聊天界面
- participant SessionP as SessionPresenter
- participant ConvMgr as ConversationManager
- participant SessionMgr as SessionManager
-
- UI->>SessionP: createConversation(title, settings, tabId)
- SessionP->>ConvMgr: createConversation(title, settings, tabId)
- ConvMgr->>ConvMgr: 持久化到 SQLite
- ConvMgr-->>SessionP: conversationId
- ConvMgr->>ConvMgr: setActiveConversation(conversationId, tabId)
- Note over ConvMgr: 绑定到 tab
-
- UI->>SessionP: getActiveConversation(tabId)
- SessionP->>ConvMgr: getActiveConversation(tabId)
- ConvMgr-->>UI: conversation
-
- Note over UI,SessionP: 首次发送消息时
- UI->>SessionP: sendMessage(conversationId, content)
- SessionP->>SessionMgr: getSession(conversationId)
- Note over SessionMgr: 解析 SessionContextResolved
- Note over SessionMgr: chatMode, providerId, modelId, workspace
-```
-
-**会话分支(Fork)流程**:
-
-```mermaid
-sequenceDiagram
- participant UI as 聊天界面
- participant SessionP as SessionPresenter
- participant ConvMgr as ConversationManager
- participant MsgMgr as MessageManager
-
- UI->>SessionP: forkConversation(conversationId, messageId, newTitle)
- SessionP->>ConvMgr: forkConversation()
- ConvMgr->>ConvMgr: 创建新会话
- ConvMgr->>MsgMgr: 复制消息到 targetMessageId(含变体选择)
- Note over ConvMgr,MsgMgr: 只复制到目标消息及其父消息
- ConvMgr->>ConvMgr: 更新父会话关系 (parentConversationId, parentMessageId)
- ConvMgr-->>UI: newConversationId
-```
-
-**关键文件位置**:
-- ConversationManager: `src/main/presenter/sessionPresenter/managers/conversationManager.ts`
-- forkConversation: `src/main/presenter/sessionPresenter/managers/conversationManager.ts:818-861`
-- SessionManager.getSession: `src/main/presenter/sessionPresenter/session/sessionManager.ts:35-61`
-
-## 7. 继续生成(Continue)流程
-
-```mermaid
-sequenceDiagram
- autonumber
- participant UI as ChatView
- participant AgentP as AgentPresenter
- participant PermHandler as PermissionHandler
- participant StreamGen as StreamGenerationHandler
- participant AgentLoop as agentLoopHandler
- participant McpP as McpPresenter
-
- UI->>AgentP: continueLoop(messageId)
-
- Note over AgentP,AgentP: 1. 检查是否是 maximum_tool_calls_reached
- AgentP->>AgentP: createContinueMessage(agentId)
- AgentP->>AgentP: sendMessage(agentId, '{"text":"continue"}', 'user')
- AgentP->>AgentP: generateAIResponse 创建空助手消息
-
- AgentP->>PermHandler: 继续之前的工具调用执行
-
- alt 有待执行的工具调用
- PermHandler->>PermHandler: 解析最后 action block
- PermHandler->>McpP: callTool(toolCall)
- McpP-->>PermHandler: toolResponse
- PermHandler->>EventBus: 发送 tool_call 事件 (running, end)
- end
-
- AgentP->>PermHandler: 从断点继续
- PermHandler->>StreamGen: continueStreamCompletion(conversationId, messageId)
-
- Note over StreamGen: 2. 准备上下文
- StreamGen->>StreamGen: 准备历史消息(含工具执行结果)
- StreamGen->>StreamGen: preparePromptContent(userContent='continue')
-
- StreamGen->>AgentLoop: startStreamCompletion(continue)
- AgentLoop->>AgentLoop: 继续正常 LLM 调用流程
- AgentLoop->>UI: 流式返回内容
-```
-
-**关键文件位置**:
-- AgentPresenter.continueLoop: `src/main/presenter/agentPresenter/index.ts:178-204`
-- StreamGenerationHandler.continueStreamCompletion: `src/main/presenter/agentPresenter/streaming/streamGenerationHandler.ts:181-350`
-
----
-
-> 💡 **提示**:所有时序图均基于当前实际代码结构绘制,代码位置标注了文件的 approximate 行数,方便快速定位。
-
diff --git a/docs/archives/legacy-agentpresenter-retirement/plan.md b/docs/archives/legacy-agentpresenter-retirement/plan.md
deleted file mode 100644
index c784e44b2..000000000
--- a/docs/archives/legacy-agentpresenter-retirement/plan.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Legacy AgentPresenter Retirement Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Execution Order
-
-1. migrate retained ACP helpers to `src/main/presenter/llmProviderPresenter/acp/`
-2. migrate retained agent tools to `src/main/presenter/toolPresenter/agentTools/`
-3. migrate retained message-formatting helpers to `src/main/presenter/sessionPresenter/`
-4. remove legacy presenter/public type exposure and runtime wiring
-5. archive retired source/tests
-6. refresh active docs and cleanup specs
-7. run format, i18n, lint, typecheck, tests, and cleanup guard
-
-## Design Rules
-
-1. keep compatibility-only data paths separate from active runtime
-2. do not leave migration shims in the old folder if a live module owns the code now
-3. prefer neutral/internal helper names over `legacy*` names when the function remains active
-4. archive before delete when historical traceability matters
-
-## Verification
-
-- `pnpm run format`
-- `pnpm run i18n`
-- `pnpm run lint`
-- `pnpm run typecheck`
-- targeted Vitest suites for provider/tools/presenter changes
-- `node scripts/agent-cleanup-guard.mjs`
diff --git a/docs/archives/legacy-agentpresenter-retirement/spec.md b/docs/archives/legacy-agentpresenter-retirement/spec.md
deleted file mode 100644
index c1390e3ba..000000000
--- a/docs/archives/legacy-agentpresenter-retirement/spec.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Legacy AgentPresenter Retirement
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Summary
-
-Retire the live legacy `AgentPresenter -> SessionManager -> streaming/permission/loop` runtime
-chain and make the current `agentSessionPresenter + agentRuntimePresenter` path the only active chat
-execution flow.
-
-## Scope
-
-In scope:
-
-- remove live runtime wiring to legacy `AgentPresenter`
-- remove public `agentPresenter` / `sessionPresenter` presenter exposure
-- remove legacy `startStreamCompletion()` provider bridge
-- migrate still-needed ACP helpers into `llmProviderPresenter/acp/`
-- migrate still-needed agent tools into `toolPresenter/agentTools/`
-- migrate retained message-formatting helpers into `sessionPresenter/`
-- retire source and tests from the active tree and preserve history in docs
-- document the retirement in active docs and specs
-
-Out of scope:
-
-- deleting legacy import support
-- deleting old `conversations/messages` tables
-- removing all historical specs that mention retired paths
-
-## Compatibility Boundary
-
-The boundary kept after retirement:
-
-- `LegacyChatImportService`
-- legacy import hook / status tracking
-- old `conversations/messages` tables as import-only or export-facing sources
-- `SessionPresenter` as a main-internal compatibility/data adapter
-
-## Historical Preservation Policy
-
-Retired live code must leave the active tree.
-
-Historical structure is preserved in `docs/archives/` and related cleanup specs. Files still used by
-active runtime are migrated into live modules instead of being kept as dormant source snapshots.
-
-## Acceptance
-
-The retirement is done when:
-
-1. live `src/main` / `src/shared` / `src/renderer` code no longer imports legacy runtime folders
-2. renderer no longer has public `agentPresenter` / `sessionPresenter` dependency
-3. `ILlmProviderPresenter.startStreamCompletion()` is removed
-4. migrated ACP / agent tool helpers compile and tests pass from their new locations
-5. retired source/tests no longer exist in the active tree, and their historical shape is preserved in docs
-6. active docs describe the current architecture and link historical docs as archive material
diff --git a/docs/archives/legacy-agentpresenter-retirement/tasks.md b/docs/archives/legacy-agentpresenter-retirement/tasks.md
deleted file mode 100644
index c0570335a..000000000
--- a/docs/archives/legacy-agentpresenter-retirement/tasks.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Legacy AgentPresenter Retirement Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Runtime Retirement
-
-- [x] remove live `AgentPresenter` runtime ownership from `src/main/presenter/index.ts`
-- [x] remove public `agentPresenter` / `sessionPresenter` presenter exposure
-- [x] remove `ILlmProviderPresenter.startStreamCompletion()`
-- [x] stop `SessionPresenter` from reading legacy runtime in-memory session state
-
-## Migration
-
-- [x] move ACP helpers to `src/main/presenter/llmProviderPresenter/acp/`
-- [x] move agent tools to `src/main/presenter/toolPresenter/agentTools/`
-- [x] move retained message formatter helper to `src/main/presenter/sessionPresenter/messageFormatter.ts`
-- [x] update imports and tests to the new paths
-
-## Archive
-
-- [x] archive retired `src/main/presenter/agentPresenter/`
-- [x] archive retired legacy presenter type definitions
-- [x] archive retired legacy tests
-- [x] add archive README for provenance and intent
-
-## Docs
-
-- [x] add retirement spec/plan/tasks
-- [x] replace active architecture and flow docs with current runtime descriptions
-- [x] link legacy architecture/flows as archive-only docs
-- [x] update `agent-cleanup` checkpoint docs to final state
-
-## Verification
-
-- [x] `pnpm run format`
-- [x] `pnpm run i18n`
-- [x] `pnpm run lint`
-- [x] `pnpm run typecheck`
-- [x] targeted Vitest suites
-- [x] `node scripts/agent-cleanup-guard.mjs`
diff --git a/docs/archives/legacy-llm-provider-runtime-retirement/plan.md b/docs/archives/legacy-llm-provider-runtime-retirement/plan.md
deleted file mode 100644
index 4372ec8d6..000000000
--- a/docs/archives/legacy-llm-provider-runtime-retirement/plan.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Legacy Provider Runtime Retirement Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Outcome
-
-Legacy provider runtime retirement is complete and no rollback path remains in the codebase.
-
-## Executed Plan
-
-1. Remove runtime selection and make AI SDK the only request runtime.
-2. Collapse provider implementations onto shared AI SDK helpers.
-3. Remove legacy MCP tool conversion surface from presenter interfaces.
-4. Delete obsolete provider SDK dependencies and refresh lockfiles.
-5. Archive the migration history and point readers to the final legacy-code commit.
-
-## Exit Conditions
-
-- No remaining source imports of `openai`, `@anthropic-ai/sdk`, `@google/genai`, `together-ai`, or `@aws-sdk/client-bedrock-runtime`
-- No `DEEPCHAT_LLM_RUNTIME` or `llmRuntimeMode` references remain
-- Main-process provider tests validate AI SDK-only behavior
-- Documentation explicitly marks the rollback path as retired
diff --git a/docs/archives/legacy-llm-provider-runtime-retirement/spec.md b/docs/archives/legacy-llm-provider-runtime-retirement/spec.md
deleted file mode 100644
index 7f4d0f6b5..000000000
--- a/docs/archives/legacy-llm-provider-runtime-retirement/spec.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Legacy Provider Runtime Retirement Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Status
-
-Completed on `2026-04-11`.
-
-The hidden rollback window is closed. `llmProviderPresenter` now runs on AI SDK only.
-
-## Goal
-
-Retire the legacy provider runtime and remove obsolete provider SDK dependencies without changing upper-layer contracts:
-
-- `BaseLLMProvider`
-- `LLMProviderPresenter`
-- `LLMCoreStreamEvent`
-- existing provider IDs, model configs, conversation history, and `function_call_record` compatibility
-
-## Scope
-
-- Remove `DEEPCHAT_LLM_RUNTIME`
-- Remove config key `llmRuntimeMode`
-- Delete `src/main/presenter/llmProviderPresenter/aiSdk/runtimeMode.ts`
-- Remove legacy-only provider branches, stream parsers, and MCP conversion ports
-- Keep provider-managed responsibilities that still matter:
- - `ollama` local model management
- - `@aws-sdk/client-bedrock` model discovery
-
-## Runtime State After Retirement
-
-- Single runtime: `ai-sdk`
-- No hidden fallback
-- No provider-specific MCP conversion APIs exposed from presenters
-- Vendor-specific request body customization is handled via AI SDK provider options mapping
-
-## Historical Anchors
-
-- AI SDK migration landed in commit `4c8345a7`
-- Legacy provider implementation can be inspected at commit `3add4093b46f15072d5ec3a65c8097e23b4907c4`
-
-## Compatibility Commitments
-
-- `LLMCoreStreamEvent` names and fields remain unchanged
-- Provider IDs and provider settings remain unchanged
-- Existing message history and `function_call_record` remain reusable
-- Routing providers (`new-api`, `zenmux`) stay as thin delegates over migrated providers
diff --git a/docs/archives/legacy-llm-provider-runtime-retirement/tasks.md b/docs/archives/legacy-llm-provider-runtime-retirement/tasks.md
deleted file mode 100644
index b9bda380a..000000000
--- a/docs/archives/legacy-llm-provider-runtime-retirement/tasks.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Legacy Provider Runtime Retirement Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-- [x] Delete `src/main/presenter/llmProviderPresenter/aiSdk/runtimeMode.ts`
-- [x] Remove `DEEPCHAT_LLM_RUNTIME` and `llmRuntimeMode`
-- [x] Convert provider request paths to AI SDK-only implementations
-- [x] Remove provider-specific MCP tool conversion interfaces from presenter ports
-- [x] Replace legacy SDK type imports with local neutral types where still needed
-- [x] Remove obsolete provider SDK dependencies from `package.json`
-- [x] Rewrite provider tests around AI SDK runtime helpers and delegate routing
-- [x] Archive legacy runtime history and document the last legacy-code commit
-- [x] Run `pnpm install`
-- [x] Run `pnpm run format`
-- [x] Run `pnpm run i18n`
-- [x] Run `pnpm run lint`
-- [x] Run `pnpm run typecheck`
-- [x] Run targeted provider tests for the migrated AI SDK-only paths
-- [ ] Run `pnpm run test:main`
diff --git a/docs/archives/legacy-llm-provider-runtime.md b/docs/archives/legacy-llm-provider-runtime.md
deleted file mode 100644
index cb806a075..000000000
--- a/docs/archives/legacy-llm-provider-runtime.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Legacy LLM Provider Runtime Archive
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Summary
-
-DeepChat previously maintained two low-level provider runtimes under `llmProviderPresenter`:
-
-- the original provider-specific SDK implementations
-- the newer shared AI SDK runtime
-
-That rollback window is now closed. The active codebase only keeps the AI SDK runtime.
-
-## Timeline
-
-- AI SDK migration landed in commit `4c8345a7`
-- Legacy runtime retirement and dependency cleanup landed after the migration stabilized
-
-## Where To Find The Old Provider Implementation
-
-Use commit `3add4093b46f15072d5ec3a65c8097e23b4907c4` to inspect the historical provider implementation and legacy runtime code.
-
-That commit is the canonical source for:
-
-- legacy provider request code
-- legacy stream parsing branches
-- provider-specific MCP conversion APIs
-- legacy rollback-path wiring
-
-## Current State
-
-- no `DEEPCHAT_LLM_RUNTIME`
-- no `llmRuntimeMode`
-- no legacy provider SDK fallback branches in active providers
-- no provider-specific MCP conversion APIs exposed from presenters
-
-For current implementation details, read:
-
-- [docs/archives/ai-sdk-runtime/spec.md](../archives/ai-sdk-runtime/spec.md)
-- [docs/archives/legacy-llm-provider-runtime-retirement/spec.md](../archives/legacy-llm-provider-runtime-retirement/spec.md)
diff --git a/docs/archives/mac-computer-use/README.md b/docs/archives/mac-computer-use/README.md
deleted file mode 100644
index 55921bc4f..000000000
--- a/docs/archives/mac-computer-use/README.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# macOS Computer Use Handoff
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-本文档集描述 DeepChat 集成 macOS Computer Use 的规格、架构和实施计划。目标是让后续
-macOS 开发人员可以在 Mac 机器上继续实现,不需要重新调研 `trycua/cua` 和
-`zats/permiso` 的集成边界。
-
-调研日期:2026-04-26。
-
-## Current Decisions
-
-- 从 `vendor/cua-driver/source` 的 DeepChat-owned CUA driver fork 源码集成。
-- 上游基线和 cherry-pick policy 记录在 `vendor/cua-driver/upstream.json`;`trycua/cua` 只作为按需吸收 bug fix、macOS compatibility fix、security fix 的来源。
-- macOS-only;Windows/Linux 不打包、不注册、不展示可用能力。
-- 默认 opt-in;用户在设置中显式开启后才注册并启动 Computer Use。
-- 权限身份使用 DeepChat 自己的 helper:
- - Bundle display name: `DeepChat Computer Use`
- - Bundle identifier: `com.wefonk.deepchat.computeruse`
-- 最终交付仍然是一个 DeepChat Electron app,helper 作为 nested app 打进
- `DeepChat.app`。
-- CUA MCP 入口使用 `cua-driver mcp`。
-- 权限引导参考 `zats/permiso`:打开 System Settings 隐私面板,并在系统设置窗口上方显示
- 非激活 overlay 指引。
-
-## Document Map
-
-- [spec.md](spec.md): 用户目标、范围、验收标准、非目标。
-- [plan.md](plan.md): DeepChat 主进程、renderer、MCP、权限状态的数据流和接口设计。
-- [packaging.md](packaging.md): CUA 源码 vendoring、Swift build、架构区分、codesign、
- notarization、CI 接入。
-- [permissions-ux.md](permissions-ux.md): permiso 风格权限引导、设置页 UI、ASCII UX 草图。
-- [tasks.md](tasks.md): 建议 PR 拆分、依赖顺序、验收点。
-- [references.md](references.md): 外部项目链接、当前版本事实、关键源码依据。
-
-## Implementation Rule
-
-本批文档不实现功能代码。后续实现时应按 SDD 流程保持 spec -> plan -> tasks -> PR 的可追踪性,
-并在每个 PR 中更新对应文档。
diff --git a/docs/archives/mac-computer-use/packaging.md b/docs/archives/mac-computer-use/packaging.md
deleted file mode 100644
index 3eb452b4e..000000000
--- a/docs/archives/mac-computer-use/packaging.md
+++ /dev/null
@@ -1,277 +0,0 @@
-# Packaging, Codesign, and CI Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Source Layout
-
-Recommended source layout:
-
-```text
-vendor/
- cua-driver/
- upstream.json
- source/
- Package.swift
- Sources/...
-runtime/
- computer-use/
- cua-driver/
- current/
- DeepChat Computer Use.app/
-resources/
- skills/
- cua-driver/
- SKILL.md
-```
-
-The implementation uses a DeepChat-owned vendored source snapshot. `vendor/cua-driver/source` is the
-build source of truth, with DeepChat changes committed directly in Swift source.
-`vendor/cua-driver/upstream.json` records the upstream base and cherry-pick policy:
-
-```json
-{
- "sourceKind": "deepchat-owned-fork",
- "upstreamRepo": "https://github.com/trycua/cua.git",
- "upstreamSubdir": "libs/cua-driver",
- "tag": "cua-driver-v0.0.15",
- "commit": "8a4c51337cfdc91a1818ee2f92ceb427272a6247",
- "version": "0.0.15",
- "updatedAt": "2026-05-01",
- "forkPolicy": "Build from the DeepChat-maintained local source snapshot. Cherry-pick upstream fixes only when they directly improve the bundled DeepChat Computer Use helper.",
- "lastCherryPick": {
- "sourceTag": "cua-driver-v0.0.15",
- "sourceCommit": "8a4c51337cfdc91a1818ee2f92ceb427272a6247",
- "appliedAt": "2026-05-01"
- }
-}
-```
-
-Build requirements:
-
-- The source snapshot is present in `vendor/cua-driver/source`.
-- The upstream commit/tag, fork policy, and last cherry-pick source are recorded in `upstream.json`.
-- DeepChat source changes are reviewed as normal repository diffs.
-- Release builds depend on local source and never on upstream binary release assets.
-- The bundled plugin skill lives in `plugins/cua/skills/cua-driver`; upstream skill files under the
- vendor snapshot are reference material.
-- The `cua-driver` skill declares `platforms: [darwin]`; built-in skill installation skips it on
- other platforms.
-- The `cua-driver` skill declares `metadata.deepchatFeature = computer-use`; SkillPresenter hides
- that managed skill while Computer Use is disabled.
-
-## Build Script
-
-Add a build script such as:
-
-```text
-node scripts/build-cua-driver.mjs --arch arm64
-node scripts/build-cua-driver.mjs --arch x64
-```
-
-Responsibilities:
-
-- Verify host platform is macOS.
-- Verify Xcode/Swift toolchain is available.
-- Map `arm64 -> arm64`, `x64 -> x86_64`.
-- Build CUA Driver with Swift release configuration:
- - `swift build -c release --arch --product cua-driver --package-path vendor/cua-driver/source --scratch-path `
-- Wrap binary into `DeepChat Computer Use.app`.
-- Patch `Info.plist`:
- - `CFBundleIdentifier = com.wefonk.deepchat.computeruse`
- - `CFBundleName = DeepChat Computer Use`
- - `CFBundleDisplayName = DeepChat Computer Use`
- - `CFBundleExecutable = cua-driver`
- - `LSUIElement = true`
- - `LSMinimumSystemVersion = 14.0`
-- Copy app resources/icons.
-- Remove stale staged helper before writing new one.
-- Verify binary architecture with `lipo -archs` or `file`.
-- Avoid `git clone`, `git fetch`, source copying, and runtime patching during normal builds.
-- Keep Windows and Linux packages clean by removing both the Computer Use runtime and the bundled
- `cua-driver` skill during `afterPack`.
-
-## Skill Packaging
-
-When macOS Computer Use is enabled and the managed MCP server is enabled, DeepChat auto-pins the
-bundled `cua-driver` skill into the agent system prompt. When Computer Use is disabled, the managed
-skill is hidden from skill listing, skill viewing, and prompt loading. This gives the model the CUA
-workflow, snapshot-before-action rules, and visual fallback guidance only while the feature is
-active.
-
-The source of truth for DeepChat skill content is `plugins/cua/skills/cua-driver`. Keep it MCP-only and
-tailored to DeepChat's plugin runtime; do not replace it with upstream CLI-oriented skill text.
-
-## Upstream CUA Patch Requirements
-
-CUA Driver currently assumes `CuaDriver.app` in some relaunch paths. DeepChat maintains the vendored
-source with these local changes:
-
-- App name and bundle id used by LaunchServices relaunch.
-- Any hardcoded `CuaDriver` app lookup.
-- First-run permission UI labels.
-- Self-update command behavior.
-- Telemetry defaults.
-- DeepChat-specific permission probe command.
-- Non-blocking permission startup.
-- Background click dispatch behavior verified for DeepChat integration.
-
-The target behavior is that all macOS TCC prompts and System Settings rows display
-`DeepChat Computer Use`.
-
-## Upstream Updates
-
-Move the upstream base only when a developer intentionally cherry-picks a fix that improves the
-bundled DeepChat Computer Use helper.
-
-Update flow:
-
-1. Read the current base from `vendor/cua-driver/upstream.json`.
-2. Fetch the old base and target commit from upstream into a temporary clone.
-3. Generate a binary patch from old upstream `libs/cua-driver` to current
- `vendor/cua-driver/source`.
-4. Build a temporary candidate repository with old upstream as the first commit and new upstream as
- the second commit.
-5. Apply the DeepChat delta with `git apply --3way --whitespace=nowarn`.
-6. On success, copy the merged candidate source into `vendor/cua-driver/source` and update the
- upstream base plus `lastCherryPick` in `upstream.json`.
-7. On conflict, copy conflicted source files with conflict markers into `vendor/cua-driver/source`
- during real update runs, print the conflict list, and leave manual resolution to the reviewer.
-
-Conflict handling rules:
-
-- Resolve conflicts in `vendor/cua-driver/source` as normal source conflicts.
-- Keep DeepChat identity, permission probe, non-blocking permission startup, telemetry defaults, and
- background click dispatch behavior unless upstream adds an equivalent implementation.
-- Keep DeepChat's MCP-only skill and policy surface unless the change explicitly updates DeepChat
- plugin behavior.
-- Run the helper build and package validation after resolving conflicts.
-
-## Runtime Placement
-
-DeepChat already copies `runtime/` into:
-
-```text
-DeepChat.app/Contents/Resources/app.asar.unpacked/runtime
-```
-
-Stage the helper at:
-
-```text
-runtime/computer-use/cua-driver/current/DeepChat Computer Use.app
-```
-
-The packaged binary path becomes:
-
-```text
-DeepChat.app/Contents/Resources/app.asar.unpacked/runtime/computer-use/cua-driver/current/DeepChat Computer Use.app/Contents/MacOS/cua-driver
-```
-
-Only the target arch helper should exist in `current/` for a given build artifact.
-
-## Electron Builder Hooks
-
-Extend `scripts/afterPack.js` for macOS:
-
-- Locate staged nested helper inside the app output.
-- Sign nested helper before outer app signing completes.
-- Use a dedicated helper entitlements plist.
-- Fail fast if helper is missing on macOS release builds.
-- Skip helper validation on Windows/Linux.
-- Exclude `vendor/**` from Electron app files; packaged apps should receive the staged helper from
- `runtime/` only.
-
-Recommended helper entitlements:
-
-```xml
-com.apple.security.automation.apple-events
-
-```
-
-Do not add unrelated entitlements unless a failing macOS validation proves they are required.
-
-DeepChat outer app entitlements should remain minimal. Add `com.apple.security.automation.apple-events`
-to the outer app only if DeepChat itself sends Apple Events outside the helper.
-
-## Codesign
-
-Release signing should use the same Developer ID Application identity already used for DeepChat.
-
-Recommended validation commands in CI:
-
-```text
-codesign --verify --deep --strict --verbose=2 ""
-codesign --verify --deep --strict --verbose=2 ""
-spctl -a -vvv -t exec ""
-```
-
-Expected signing order:
-
-```text
-build helper source
-stage helper under runtime
-electron-builder afterPack signs nested helper
-electron-builder signs DeepChat.app
-afterSign notarizes DeepChat.app
-```
-
-## CI Changes
-
-Current GitHub mac build matrix already separates `x64` and `arm64`. Add helper build before
-`pnpm run build` or before `electron-builder`:
-
-```text
-pnpm run installRuntime:mac:${{ matrix.arch }}
-pnpm run build:cua-driver:mac:${{ matrix.arch }}
-pnpm run build
-pnpm exec electron-builder --mac --${{ matrix.arch }} --publish=never
-```
-
-Local package scripts should mirror CI:
-
-```text
-build:mac:arm64 -> build helper arm64 -> build renderer/main -> electron-builder --mac --arm64
-build:mac:x64 -> build helper x64 -> build renderer/main -> electron-builder --mac --x64
-```
-
-## Architecture Validation
-
-For arm64:
-
-```text
-lipo -archs ".../DeepChat Computer Use.app/Contents/MacOS/cua-driver"
-# expected: arm64
-```
-
-For x64:
-
-```text
-lipo -archs ".../DeepChat Computer Use.app/Contents/MacOS/cua-driver"
-# expected: x86_64
-```
-
-Fail the build if:
-
-- The helper is missing.
-- The helper contains the wrong architecture.
-- The helper is unsigned in release mode.
-- Codesign verification fails.
-- Notarization fails.
-
-## Local Development
-
-On non-macOS machines:
-
-- Build script should print a clear unsupported message and exit without modifying runtime artifacts.
-- Unit tests for path resolution can run with platform mocks.
-- Full helper build, TCC permission flow, and codesign validation require macOS.
-
-On macOS development machines:
-
-- Allow ad-hoc signing for local debug builds.
-- Keep release signing strict.
-- Document how to reset TCC grants during manual testing:
- - `tccutil reset Accessibility com.wefonk.deepchat.computeruse`
- - `tccutil reset ScreenCapture com.wefonk.deepchat.computeruse`
-
-Use the reset commands only in manual developer docs, never inside app code.
diff --git a/docs/archives/mac-computer-use/permissions-ux.md b/docs/archives/mac-computer-use/permissions-ux.md
deleted file mode 100644
index 00c33ebf7..000000000
--- a/docs/archives/mac-computer-use/permissions-ux.md
+++ /dev/null
@@ -1,155 +0,0 @@
-# Permissions and UX Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Permission Model
-
-Computer Use needs two macOS TCC grants:
-
-- Accessibility: required for input control and accessibility tree access.
-- Screen Recording: required for screenshots, screen/window inspection, and ScreenCaptureKit probes.
-
-The app must not imply these permissions can be bypassed. Missing permissions should be treated as normal
-setup state, not as a generic error.
-
-## Permission Identity
-
-System Settings must show:
-
-```text
-DeepChat Computer Use
-```
-
-This requires the helper bundle identity to be stable:
-
-```text
-CFBundleIdentifier: com.wefonk.deepchat.computeruse
-CFBundleDisplayName: DeepChat Computer Use
-```
-
-Do not keep upstream `com.trycua.driver` for the integrated helper, because users would see `CuaDriver`
-instead of DeepChat in System Settings.
-
-## Permission Guide Behavior
-
-Use the permiso interaction model:
-
-- Open the target System Settings privacy pane via `x-apple.systempreferences`.
-- Locate the visible System Settings window.
-- Display a passive, non-activating overlay near the relevant settings content.
-- Poll permission status until granted.
-- Advance from Accessibility to Screen Recording automatically when possible.
-- Close the overlay after both grants are detected.
-
-Recommended panels:
-
-```text
-Accessibility:
-x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Accessibility
-
-Screen Recording:
-x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ScreenCapture
-```
-
-## Settings Card
-
-Initial macOS disabled state:
-
-```text
-Settings > MCP
-+--------------------------------------------------+
-| Computer Use (macOS) [Off] |
-| Let DeepChat operate local apps after approval. |
-| Helper: Not running |
-| Accessibility: Not requested |
-| Screen Recording: Not requested |
-| |
-| [Open Permission Guide] [Check Again] |
-+--------------------------------------------------+
-```
-
-Enabled but missing permissions:
-
-```text
-Settings > MCP
-+--------------------------------------------------+
-| Computer Use (macOS) [On] |
-| Helper: Ready |
-| Accessibility: Missing |
-| Screen Recording: Granted |
-| MCP: Waiting for permissions |
-| |
-| [Open Permission Guide] [Check Again] |
-+--------------------------------------------------+
-```
-
-Enabled and ready:
-
-```text
-Settings > MCP
-+--------------------------------------------------+
-| Computer Use (macOS) [On] |
-| Helper: Ready |
-| Accessibility: Granted |
-| Screen Recording: Granted |
-| MCP: Running |
-| |
-| [Restart Service] [Check Again] |
-+--------------------------------------------------+
-```
-
-Unsupported platform:
-
-```text
-Settings > MCP
-+--------------------------------------------------+
-| Computer Use |
-| Available on macOS only. |
-+--------------------------------------------------+
-```
-
-## Permission Overlay Sketch
-
-```text
-System Settings > Privacy & Security > Accessibility
-+--------------------------------------------------------------+
-| Accessibility |
-| |
-| [ ] DeepChat Computer Use |
-| |
-| +---------------------------------------------+ |
-| | Enable DeepChat Computer Use in this list. | |
-| | DeepChat will continue when permission is on.| |
-| +---------------------------------------------+ |
-+--------------------------------------------------------------+
-```
-
-For Screen Recording, use the same shape and text adapted to screen capture.
-
-## Copy Guidelines
-
-User-facing copy should be short and direct:
-
-- "Computer Use lets DeepChat inspect and operate local apps after you approve tool calls."
-- "Accessibility is required for clicks, typing, shortcuts, and accessibility tree access."
-- "Screen Recording is required for screenshots and screen state."
-- "DeepChat cannot use Computer Use until both permissions are granted."
-
-Avoid claiming the feature is always safe. Instead, make the permission boundaries visible.
-
-## Interaction Rules
-
-- Opening the guide should not auto-enable action tools.
-- Toggling Computer Use off should stop/restart the MCP server so tools disappear from active sessions.
-- If global MCP is disabled, the card should say Computer Use is configured but MCP is off.
-- If the helper exits because permissions are missing, show the permission guide action rather than a raw stderr block.
-- If System Settings cannot be found for overlay positioning, fall back to opening the pane and showing an in-app checklist.
-
-## Accessibility of the UI
-
-- Status labels must not rely only on color.
-- Buttons must have clear labels and keyboard focus states.
-- The overlay should be passive and should not intercept System Settings interaction.
-- Renderer strings must use i18n keys.
-
diff --git a/docs/archives/mac-computer-use/plan.md b/docs/archives/mac-computer-use/plan.md
deleted file mode 100644
index 36cadd9cb..000000000
--- a/docs/archives/mac-computer-use/plan.md
+++ /dev/null
@@ -1,231 +0,0 @@
-# macOS Computer Use Implementation Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Architecture Overview
-
-DeepChat 通过一个 macOS nested helper app 承载 CUA Driver。Electron main process 负责:
-
-- 发现 helper 路径和架构。
-- 查询 helper 权限状态。
-- 维护 Computer Use enable 状态。
-- 在启用时注册/更新内置 stdio MCP server。
-- 把 renderer 设置页操作映射到 typed route。
-
-Renderer 只展示状态和触发操作,不直接拼接 helper 路径或执行系统命令。
-
-```text
---------------------+ typed route +--------------------------+
-| Renderer Settings | -------------------------> | ComputerUsePresenter |
-+--------------------+ +-----------+--------------+
- |
- | helper path/status
- v
- +--------------------------+
- | DeepChat Computer Use.app |
- | Contents/MacOS/cua-driver |
- +-------------+------------+
- |
- stdio MCP: cua-driver mcp |
- v
- +--------------------------+
- | McpPresenter/McpClient |
- +--------------------------+
-```
-
-## Main Process Components
-
-### ComputerUsePresenter
-
-Add a mac-only presenter under `src/main/presenter/computerUsePresenter`.
-
-Responsibilities:
-
-- Return unsupported status on non-macOS.
-- Resolve packaged helper path:
- - dev: `runtime/computer-use/cua-driver/current/DeepChat Computer Use.app`
- - packaged: `app.asar.unpacked/runtime/computer-use/cua-driver/current/DeepChat Computer Use.app`
-- Resolve binary path:
- - `DeepChat Computer Use.app/Contents/MacOS/cua-driver`
-- Read persisted enable state from config.
-- Query permissions by invoking a helper status/check command or a small DeepChat wrapper command.
-- Register or unregister `deepchat/computer-use` MCP server when enabled state changes.
-- Restart MCP server after permission changes or helper updates.
-
-Do not let renderer provide arbitrary executable paths.
-
-### Typed Routes
-
-Add a new typed route group, for example `computerUse.routes.ts`.
-
-Recommended methods:
-
-- `computerUse.getStatus(): Promise`
-- `computerUse.setEnabled(enabled: boolean): Promise`
-- `computerUse.openPermissionGuide(target?: ComputerUsePermissionTarget): Promise`
-- `computerUse.checkPermissions(): Promise`
-- `computerUse.restartMcpServer(): Promise`
-
-Recommended shared types:
-
-```typescript
-type ComputerUsePlatform = 'darwin' | 'unsupported'
-type ComputerUsePermissionName = 'accessibility' | 'screenRecording'
-type ComputerUsePermissionState = 'granted' | 'missing' | 'unknown'
-type ComputerUseMcpState = 'notRegistered' | 'registered' | 'running' | 'error'
-
-interface ComputerUseStatus {
- platform: ComputerUsePlatform
- available: boolean
- enabled: boolean
- arch: 'arm64' | 'x64' | 'unknown'
- helperPath?: string
- helperVersion?: string
- permissions: Record
- mcpServer: ComputerUseMcpState
- lastError?: string
-}
-```
-
-The exact file names can follow the current route/client naming conventions.
-
-## MCP Integration
-
-Add a built-in server only on macOS and only when Computer Use is enabled:
-
-```json
-{
- "deepchat/computer-use": {
- "type": "stdio",
- "command": "",
- "args": ["mcp"],
- "env": {
- "DEEPCHAT_COMPUTER_USE": "1",
- "CUA_DRIVER_AUTO_UPDATE": "0",
- "CUA_DRIVER_TELEMETRY": "0"
- },
- "descriptions": "DeepChat built-in macOS computer use service",
- "icons": "computer-use",
- "autoApprove": [],
- "enabled": true,
- "source": "deepchat",
- "sourceId": "computer-use"
- }
-}
-```
-
-The existing MCP config model does not distinguish built-in stdio servers from user-added stdio servers.
-Implementation should add a stable built-in marker using `source: 'deepchat'` and `sourceId`, then update
-renderer grouping logic if needed.
-
-## Tool Permission Policy
-
-CUA tools need explicit classification instead of relying only on generic name heuristics.
-
-Recommended read/status tools:
-
-- `check_permissions`
-- `list_apps`
-- `list_windows`
-- `get_screen_size`
-- `get_window_state`
-- `get_accessibility_tree`
-- `get_cursor_position`
-- `screenshot`
-
-Recommended action/write tools:
-
-- `launch_app`
-- `click`
-- `right_click`
-- `scroll`
-- `type_text`
-- `type_text_chars`
-- `press_key`
-- `hotkey`
-- `set_value`
-- `set_agent_cursor_enabled`
-- `set_agent_cursor_motion`
-- `set_agent_cursor_style`
-- recording/config mutation tools
-- `zoom`
-- `page`
-
-Default server `autoApprove` stays empty. If the user selects DeepChat `full_access`, DeepChat can skip
-its own per-tool prompts, but macOS Accessibility and Screen Recording grants are still required.
-
-Rejected design: do not add a public `set_electron_accessibility` tool. The driver already attempts
-Electron/Chromium AX enablement inside `get_window_state` by setting `AXManualAccessibility`,
-`AXEnhancedUserInterface`, registering an AXObserver, and pumping the run loop before walking the tree.
-If Electron still returns a sparse AX tree, the agent should use `page`, `electron_debugging_port`,
-`screenshot(window_id)`, and window-local pixel actions.
-
-## State Flow
-
-Enable flow:
-
-```text
-User toggles Computer Use on
- -> renderer calls computerUse.setEnabled(true)
- -> presenter resolves helper and checks platform
- -> presenter persists enabled=true
- -> presenter ensures MCP server config
- -> presenter starts/restarts MCP server if global MCP is enabled
- -> presenter returns current status
-```
-
-Permission flow:
-
-```text
-User clicks Open Permission Guide
- -> renderer calls computerUse.openPermissionGuide('all')
- -> presenter launches helper permission UI
- -> helper opens System Settings pane
- -> helper shows permiso-style overlay
- -> presenter/renderer polls checkPermissions
- -> UI updates to granted/missing
-```
-
-MCP startup flow:
-
-```text
-McpClient spawns packaged cua-driver with args ['mcp']
- -> CUA checks TCC permissions
- -> if missing, process exits with clear stderr
- -> presenter maps failure into ComputerUseStatus.lastError
- -> UI offers permission guide
-```
-
-## Renderer UI Placement
-
-Preferred placement: existing MCP settings area, because Computer Use is implemented as an MCP server and
-already depends on MCP enablement. If product wants stronger discoverability, add a dedicated
-`Settings > Computer Use` subsection later; do not start with two separate settings pages.
-
-Renderer should:
-
-- Show card only on macOS, or show a disabled unsupported message on other platforms.
-- Use i18n keys for all user-facing strings.
-- Never expose command/path editing for this built-in server.
-- Link to MCP state if global MCP is disabled.
-
-## Error Handling
-
-Surface these states distinctly:
-
-- `unsupported`: platform is not macOS.
-- `missingHelper`: helper app is not packaged or cannot be found.
-- `archMismatch`: packaged binary does not match expected arch.
-- `missingPermissions`: one or more TCC grants missing.
-- `mcpStartFailed`: helper exists but `cua-driver mcp` exits or fails handshake.
-- `codesignInvalid`: dev/release build validation found an invalid helper signature.
-
-## Security Notes
-
-- Do not auto-enable Computer Use.
-- Do not auto-approve action tools by default.
-- Do not allow model-generated input to change helper command path.
-- Do not bypass macOS TCC or store private permission workarounds.
-- Disable helper self-update. Updates should come from DeepChat release packages.
-- Keep permission identity stable across releases so TCC grants persist.
diff --git a/docs/archives/mac-computer-use/references.md b/docs/archives/mac-computer-use/references.md
deleted file mode 100644
index b964fa5a6..000000000
--- a/docs/archives/mac-computer-use/references.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# External References and Research Notes
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-Research date: 2026-05-01.
-
-## CUA
-
-- Repository: https://github.com/trycua/cua
-- CUA Driver docs: https://cua.ai/docs/cua-driver/guide/getting-started/introduction
-- Latest GitHub release observed: https://github.com/trycua/cua/releases/tag/cua-driver-v0.0.15
-
-Observed release facts:
-
-- Latest release tag: `cua-driver-v0.0.15`
-- Published: 2026-04-30
-- Assets observed:
- - `cua-driver-0.0.15-darwin-arm64.pkg.tar.gz`
- - `cua-driver-0.0.15-darwin-arm64.tar.gz`
- - generic darwin aliases
-- No x64 release asset was observed, so DeepChat must build from source for x64 support.
-
-Key source facts from `libs/cua-driver`:
-
-- `Package.swift` declares macOS 14 minimum.
-- Products include:
- - executable `cua-driver`
- - library `CuaDriverCore`
- - library `CuaDriverServer`
-- `cua-driver mcp` runs the stdio MCP server.
-- `cua-driver serve` runs a long-lived daemon over a Unix socket.
-- `cua-driver mcp-config` prints an MCP config that uses command `` and args `['mcp']`.
-- Permissions use:
- - `AXIsProcessTrusted()`
- - `AXIsProcessTrustedWithOptions(...)`
- - `CGRequestScreenCaptureAccess()`
- - `SCShareableContent.current`
-- Upstream helper entitlement includes `com.apple.security.automation.apple-events`.
-- Upstream app wrapper uses a stable bundle id because TCC grants are tied to app identity.
-
-Integration implication:
-
-- DeepChat should use `cua-driver mcp` for MCP.
-- DeepChat should maintain a local DeepChat-owned driver fork with DeepChat identity and ship the branded
- `DeepChat Computer Use.app`.
-- DeepChat should build helper source for both `arm64` and `x86_64`.
-- DeepChat should disable helper self-update so app updates remain controlled by DeepChat releases.
-- DeepChat should cherry-pick upstream changes only when they directly improve bundled helper
- reliability, macOS compatibility, or security.
-
-## Permiso
-
-- Repository: https://github.com/zats/permiso
-
-Key source facts:
-
-- `PermisoPanel` uses System Settings URLs:
- - `Privacy_Accessibility`
- - `Privacy_ScreenCapture`
- - `x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?`
-- `SettingsWindowLocator` finds System Settings with bundle id `com.apple.systempreferences`.
-- Window location uses `CGWindowListCopyWindowInfo`.
-- `OverlayWindowController` creates a passive `NSPanel` with `.nonactivatingPanel`.
-- Overlay window level uses `.statusBar`.
-
-Integration implication:
-
-- DeepChat can copy the UX pattern without copying unrelated app structure.
-- Permission guide should open System Settings, locate its window, and show a passive overlay.
-- If overlay placement fails, fallback to regular in-app checklist.
-
-## DeepChat Existing Architecture
-
-Relevant current facts:
-
-- App id: `com.wefonk.deepchat`.
-- mac artifact naming already includes `${arch}`.
-- `electron-builder.yml` already copies `runtime/` to `app.asar.unpacked/runtime`.
-- mac builds already run separate `x64` and `arm64` matrix jobs.
-- `afterPack` is `scripts/afterPack.js`.
-- `afterSign` is `scripts/notarize.js`.
-- Existing MCP config supports `stdio`, `sse`, `http`, and `inmemory`.
-- Built-in mac server `deepchat/apple-server` already exists as an `inmemory` platform-specific server.
-- New renderer-main capabilities should use typed route/client instead of new direct legacy presenter calls.
-
-Integration implication:
-
-- Runtime placement can reuse existing `runtime/` packaging behavior.
-- Signing should be added to the existing `afterPack` mac branch.
-- Computer Use should be represented as a DeepChat-owned built-in MCP server, not a user-editable custom server.
diff --git a/docs/archives/mac-computer-use/spec.md b/docs/archives/mac-computer-use/spec.md
deleted file mode 100644
index 9b110e86f..000000000
--- a/docs/archives/mac-computer-use/spec.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# macOS Computer Use Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Summary
-
-DeepChat 需要在 macOS 上提供 Computer Use 能力,让模型可以通过内置工具读取屏幕状态、
-枚举窗口、截屏,并在用户授权后执行点击、输入、快捷键等本机操作。该能力仅在 macOS 有效,
-并通过 DeepChat 内置的 `DeepChat Computer Use.app` helper 实现。
-
-用户不需要单独安装 CUA Driver。DeepChat macOS 安装包需要包含匹配当前架构的 helper:
-`arm64` 构建包含 arm64 helper,`x64` 构建包含 x86_64 helper。
-
-## Goals
-
-- 在 macOS 上内置 source-built CUA Driver,并通过 MCP 接入 DeepChat agent tool flow。
-- 让用户明确开启 Computer Use 后才启用该能力。
-- 用 DeepChat 品牌身份请求 macOS 权限,避免系统设置中出现不明来源的 `CuaDriver`。
-- 提供清晰的 Accessibility 和 Screen Recording 权限引导。
-- 保持 Windows/Linux 行为不变。
-- 保持最终交付物为一个 DeepChat app,不要求用户安装额外 app 或命令行工具。
-
-## User Stories
-
-- 作为 macOS 用户,我可以在设置中看到 Computer Use 入口,并理解它需要本机控制权限。
-- 作为 macOS 用户,我可以一键打开权限引导,按步骤授予 Accessibility 和 Screen Recording。
-- 作为 macOS 用户,我开启 Computer Use 后,agent 可以使用屏幕读取和本机操作工具。
-- 作为隐私敏感用户,我不启用 Computer Use 时,DeepChat 不启动 helper、不注册相关 MCP 工具。
-- 作为 Windows/Linux 用户,我不会看到可误用的 Computer Use 启用入口。
-- 作为发布工程师,我可以分别构建 macOS x64 和 arm64 包,并确认 helper 架构正确。
-
-## Functional Requirements
-
-- Settings:
- - macOS 显示 Computer Use 设置卡片。
- - 默认状态为 disabled。
- - 卡片展示 helper 状态、MCP 状态、Accessibility 权限、Screen Recording 权限。
- - 卡片提供 enable/disable、open permission guide、check again 操作。
-- Permissions:
- - 权限身份必须是 `DeepChat Computer Use`。
- - 缺少任一必需权限时,MCP server 不应静默失败;UI 应展示明确状态。
- - 权限引导必须覆盖 Accessibility 和 Screen Recording。
-- MCP:
- - DeepChat 内置 MCP server key 使用 `deepchat/computer-use`。
- - server type 使用 `stdio`。
- - command 指向 packaged helper binary。
- - args 使用 `['mcp']`。
- - `autoApprove` 默认空数组。
- - CUA action tools 需要走 DeepChat 现有 permission flow。
-- Packaging:
- - macOS build 必须从 vendored source 构建 helper。
- - macOS release artifact 只包含目标架构 helper。
- - helper 必须被签名并随 DeepChat app 一起 notarized。
-
-## Non-Goals
-
-- 不支持 Windows/Linux Computer Use。
-- 不提供独立的 CUA Driver 安装器。
-- 不允许通过下载上游 release binary 完成正式构建。
-- 不在本阶段实现 remote desktop、远程控制或多人协作。
-- 不绕过 macOS TCC;用户必须在系统设置中授予权限。
-
-## Acceptance Criteria
-
-- On macOS:
- - Fresh install 后 Computer Use disabled,helper 未启动,MCP 工具未注册。
- - 用户启用后,如果权限缺失,状态显示 missing,并能打开权限引导。
- - 用户授予 Accessibility 和 Screen Recording 后,状态变为 granted。
- - `deepchat/computer-use` server 可以启动,工具列表包含 CUA MCP tools。
- - `check_permissions`、`list_windows`、`screenshot` 可用。
- - action tool 如 `click`、`type_text` 在默认权限模式下触发 DeepChat permission prompt。
-- On non-macOS:
- - 不打包 helper。
- - 不注册 `deepchat/computer-use`。
- - 设置页不显示可启用的 Computer Use 卡片,或只显示 unavailable 状态。
-- Packaging:
- - mac arm64 artifact 内 helper binary 为 arm64。
- - mac x64 artifact 内 helper binary 为 x86_64。
- - `codesign --verify --deep --strict` 对 helper 和外层 `DeepChat.app` 通过。
- - release notarization 仍通过。
-
-## Constraints
-
-- DeepChat app id 当前为 `com.wefonk.deepchat`。
-- Helper bundle id 固定为 `com.wefonk.deepchat.computeruse`,避免 TCC grant 随版本变化丢失。
-- CUA Driver 当前 `Package.swift` 声明最低 macOS 14;本功能最低 macOS 14。
-- 新 renderer-main 能力应使用 typed route/client,不新增 `useLegacyPresenter()` 调用。
-
diff --git a/docs/archives/mac-computer-use/tasks.md b/docs/archives/mac-computer-use/tasks.md
deleted file mode 100644
index f68d92833..000000000
--- a/docs/archives/mac-computer-use/tasks.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# Implementation Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-This task list is intended for macOS developers taking over the implementation. Prefer small PRs in this
-order so packaging, permissions, and UI can be reviewed independently.
-
-## Phase 1: Source and Build Scaffold
-
-- Add vendored CUA Driver source under `vendor/cua-driver/source`, pinned to `cua-driver-v0.0.13`
- in `vendor/cua-driver/upstream.json`.
-- Commit DeepChat helper bundle identity, app name, relaunch behavior, self-update disablement,
- permission probe, non-blocking permission startup, background click dispatch, and telemetry default
- changes directly in vendored Swift source.
-- Add upstream update script that exports the DeepChat delta and applies it to a requested upstream
- tag/commit with `git apply --3way`.
-- Add mac-only helper build script with `--arch arm64|x64`.
-- Stage helper into `runtime/computer-use/cua-driver/current/DeepChat Computer Use.app`.
-- Add architecture validation for staged helper.
-
-Done when:
-
-- A macOS developer can build both helper architectures from source.
-- The staged helper has correct app name, bundle id, and binary architecture.
-
-## Phase 2: Packaging and Signing
-
-- Update mac package scripts to build the helper before Electron packaging.
-- Update GitHub mac build matrix to call the helper build script.
-- Extend `scripts/afterPack.js` to sign and validate the nested helper on macOS.
-- Add helper entitlements plist.
-- Add CI validation for helper presence, architecture, codesign, and outer app codesign.
-
-Done when:
-
-- mac arm64 artifact contains only arm64 helper.
-- mac x64 artifact contains only x86_64 helper.
-- Release signing and notarization still pass.
-
-## Phase 3: Main Process Integration
-
-- Add `ComputerUsePresenter`.
-- Add typed routes and renderer API client.
-- Add shared status and permission types.
-- Implement helper path resolution for dev and packaged modes.
-- Implement mac-only enable state persistence.
-- Implement permission status checks.
-- Register/unregister `deepchat/computer-use` MCP server when enabled changes.
-
-Done when:
-
-- Unit tests cover platform gating, path resolution, missing helper, enable persistence, and MCP config.
-- Non-mac platforms return unsupported and never register the server.
-
-## Phase 4: MCP Permission Hardening
-
-- Add CUA-specific tool classification for read/status vs action/write tools.
-- Ensure `autoApprove` defaults to empty for `deepchat/computer-use`.
-- Ensure built-in stdio server cannot be edited into an arbitrary command by renderer UI.
-- Surface MCP startup failures as Computer Use status errors.
-
-Done when:
-
-- Default permission mode prompts before action tools.
-- `full_access` behavior is consistent with existing DeepChat agent permission semantics.
-- Missing macOS TCC grants are surfaced as setup state.
-
-## Phase 5: Permission Guide
-
-- Implement or integrate a small Swift permission guide layer based on permiso concepts.
-- Open Accessibility and Screen Recording panes with `x-apple.systempreferences` URLs.
-- Locate System Settings window and show a passive overlay.
-- Poll permission status and auto-advance between required grants.
-- Add fallback in-app checklist when overlay placement is unavailable.
-
-Done when:
-
-- Fresh macOS install can complete both grants through the guide.
-- System Settings shows `DeepChat Computer Use`.
-- Closing the guide leaves clear status in DeepChat.
-
-## Phase 6: Renderer Settings UI
-
-- Add Computer Use card to MCP settings on macOS.
-- Add unsupported state for non-mac if the settings surface needs it.
-- Add i18n keys for all user-facing strings.
-- Wire enable/disable, open guide, check again, and restart actions.
-- Hide raw executable path from normal UI.
-
-Done when:
-
-- UI matches the states in `permissions-ux.md`.
-- Renderer tests cover disabled, missing permissions, ready, MCP disabled, and unsupported states.
-
-## Phase 7: End-to-End Validation
-
-- Manual test on macOS arm64.
-- Manual test on macOS x64 or Rosetta-backed Intel environment.
-- Validate:
- - enable Computer Use
- - grant permissions
- - run `check_permissions`
- - run `list_windows`
- - run `screenshot`
- - run one controlled action tool with a prompt
-- Run repo quality gates:
- - `pnpm run format`
- - `pnpm run i18n`
- - `pnpm run lint`
- - targeted Vitest suites
- - mac package build
-
-Done when:
-
-- All acceptance criteria in `spec.md` pass.
-- Known macOS limitations are documented in release notes or user docs.
diff --git a/docs/archives/mac-computer-use/test-plan.md b/docs/archives/mac-computer-use/test-plan.md
deleted file mode 100644
index 39fedd6d7..000000000
--- a/docs/archives/mac-computer-use/test-plan.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# Test Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Unit Tests
-
-Main process:
-
-- `ComputerUsePresenter` returns unsupported on non-macOS.
-- Helper path resolution maps dev and packaged app paths correctly.
-- Architecture detection maps `process.arch` to expected helper architecture.
-- Missing helper produces `available=false` and a specific error.
-- Enable/disable persists state and updates MCP server config.
-- Permission parser maps helper output to `granted`, `missing`, or `unknown`.
-- MCP server config uses `command=` and `args=['mcp']`.
-- Built-in stdio server is marked as DeepChat-owned and not treated as a user custom server.
-
-Tool permissions:
-
-- CUA read/status tools are classified as read.
-- CUA action tools are classified as write/action.
-- Default `autoApprove=[]` does not silently approve action tools.
-- Existing non-CUA MCP permission behavior is unchanged.
-
-Packaging script tests:
-
-- Build script reads `vendor/cua-driver/source` and does not run upstream clone/fetch during builds.
-- `vendor/cua-driver/upstream.json` missing required fields fails with a clear error.
-- Update script `--dry-run` generates the DeepChat delta and applies it to a local upstream test repo.
-- Update script `--dry-run` reports conflict files when upstream and DeepChat edit the same source.
-- `--arch arm64` maps to Swift arch `arm64`.
-- `--arch x64` maps to Swift arch `x86_64`.
-- Non-macOS build exits with clear unsupported message.
-- Wrong or missing architecture validation fails.
-
-## Renderer Tests
-
-- macOS disabled card renders correct status and actions.
-- Enabled with missing permissions renders permission guide action.
-- Ready state renders granted permissions and running MCP status.
-- Global MCP disabled state explains that Computer Use is configured but inactive.
-- Non-macOS state does not offer an enable action.
-- User actions call the typed client, not legacy presenter APIs.
-- i18n keys exist for all Computer Use strings.
-
-## Package Validation
-
-For each macOS artifact:
-
-- Confirm helper exists in:
- - `DeepChat.app/Contents/Resources/app.asar.unpacked/runtime/computer-use/cua-driver/current/DeepChat Computer Use.app`
-- Confirm architecture:
- - arm64 build: helper binary reports `arm64`
- - x64 build: helper binary reports `x86_64`
-- Confirm signing:
- - helper passes `codesign --verify --deep --strict`
- - outer app passes `codesign --verify --deep --strict`
- - outer app passes `spctl -a -vvv -t exec`
-- Confirm notarization completes in release flow.
-
-## Manual macOS Acceptance
-
-Fresh install:
-
-- Install DeepChat to `/Applications`.
-- Open Settings > MCP.
-- Confirm Computer Use is off.
-- Confirm no `deepchat/computer-use` tools are active.
-
-Permission setup:
-
-- Turn Computer Use on.
-- Open permission guide.
-- Grant Accessibility for `DeepChat Computer Use`.
-- Grant Screen Recording for `DeepChat Computer Use`.
-- Restart app if macOS requires it.
-- Confirm status becomes granted for both permissions.
-
-Tool behavior:
-
-- Run an agent task that calls `check_permissions`.
-- Run an agent task that calls `list_windows`.
-- Run an agent task that calls `screenshot`.
-- Run a controlled action such as clicking a known harmless UI target.
-- In default permission mode, confirm action tool prompt appears before execution.
-
-Disable behavior:
-
-- Turn Computer Use off.
-- Confirm MCP server stops or is unregistered.
-- Confirm active tool list no longer includes CUA tools.
-
-TCC reset regression:
-
-- Reset Accessibility and ScreenCapture grants manually with `tccutil`.
-- Relaunch DeepChat.
-- Confirm UI returns to missing permission state.
-
-## Risks to Watch During Testing
-
-- macOS TCC may attribute permissions to the parent Electron app if helper is launched incorrectly.
-- Upstream CUA relaunch logic may still search for `CuaDriver.app` unless patched.
-- Screen Recording permission may require app restart on some macOS versions.
-- Intel/x64 build may fail if Swift package or dependencies assume arm64-only paths.
-- Nested helper signing order can break notarization if helper is modified after signing.
diff --git a/docs/archives/multi-window-cleanup/plan.md b/docs/archives/multi-window-cleanup/plan.md
deleted file mode 100644
index b710b7356..000000000
--- a/docs/archives/multi-window-cleanup/plan.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# Multi-Window Cleanup Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-1. Rename renderer entry from `shell` to `browser` and delete tooltip overlay.
-2. Convert YoBrowser from single-window multi-tab to multi-window single-page.
-3. Remove tab shortcuts and tab UI, keep only multi-window behavior.
-4. Short-circuit tab-dependent legacy MCP helpers.
-5. Run format, i18n, lint, typecheck, targeted tests, and build.
diff --git a/docs/archives/multi-window-cleanup/spec.md b/docs/archives/multi-window-cleanup/spec.md
deleted file mode 100644
index ae0d5aa48..000000000
--- a/docs/archives/multi-window-cleanup/spec.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Multi-Window Cleanup Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Goal
-
-Remove the last shell and multi-tab browser architecture remnants and converge on a clean
-multi-window model:
-
-- app windows render the chat UI directly
-- browser windows render dedicated browser chrome directly
-- each browser window owns exactly one page `WebContentsView`
-- tooltip overlay is removed completely
-
-## Decisions
-
-### Window model
-
-- `WindowPresenter` exposes explicit app-window and browser-window creation APIs.
-- `createShellWindow` remains only as a deprecated compatibility wrapper.
-- Browser windows load `src/renderer/browser/index.html`.
-- Chat windows load `src/renderer/index.html#/chat`.
-
-### YoBrowser model
-
-- `YoBrowserPresenter` manages multiple browser windows.
-- Each browser window has one browser chrome renderer and one page view.
-- There is no tab list, no tab activation, no tab reordering, and no tab shortcuts.
-- Browser APIs use `windowId` for addressing.
-
-### Renderer model
-
-- The old `src/renderer/shell` entry is renamed to `src/renderer/browser`.
-- Browser chrome keeps only:
- - window controls
- - address bar
- - navigation controls
- - create-new-browser-window action
-- Tooltip overlay entry/runtime is deleted.
-
-### Legacy handling
-
-- `conversationSearchServer` and `meetingServer` are intentionally disabled until they are rebuilt
- against the window-native architecture.
-- Deprecated browser tool names remain as thin aliases only in the handler.
-
-## Non-Goals
-
-- Rebuilding every legacy session/thread abstraction in this pass.
-- Introducing new UI entities beyond the required browser-window state.
diff --git a/docs/archives/multi-window-cleanup/tasks.md b/docs/archives/multi-window-cleanup/tasks.md
deleted file mode 100644
index 610663895..000000000
--- a/docs/archives/multi-window-cleanup/tasks.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Multi-Window Cleanup Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-- [x] Move renderer entry from `shell` to `browser`
-- [x] Delete tooltip overlay entry from renderer tree
-- [ ] Update build aliases and inputs
-- [ ] Simplify browser renderer chrome
-- [ ] Rewrite YoBrowser window model
-- [ ] Split app-window and browser-window creation
-- [ ] Remove tab shortcuts from main/settings
-- [ ] Disable legacy MCP tab helpers
-- [ ] Verify format, i18n, lint, typecheck, tests, build
diff --git a/docs/archives/new-agent/plan.md b/docs/archives/new-agent/plan.md
deleted file mode 100644
index d1d3b36f4..000000000
--- a/docs/archives/new-agent/plan.md
+++ /dev/null
@@ -1,414 +0,0 @@
-# New Agent Architecture v0 — Implementation Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. Current Implementation Baseline
-
-### 1.1 LLM Provider (reuse as-is)
-
-- `src/main/presenter/llmProviderPresenter/baseProvider.ts` — `BaseLLMProvider` with `coreStream()` returning `AsyncGenerator`
-- `src/shared/types/core/llm-events.ts` — `LLMCoreStreamEvent` discriminated union (text, reasoning, tool_call_start, error, usage, stop, etc.)
-- Provider instances managed by `LLMProviderPresenter.getProviderInstance(providerId)`
-
-### 1.2 Event System (extend, not replace)
-
-- `src/main/eventbus.ts` — `EventBus` singleton with `sendToRenderer(event, target, payload)`
-- `src/main/events.ts` — existing event constants (CONVERSATION_EVENTS, STREAM_EVENTS)
-- `src/renderer/src/events.ts` — renderer-side event constant mirrors
-
-### 1.3 Presenter Registration
-
-- `src/main/presenter/index.ts` — singleton `Presenter` class, IPC handler at line ~358 does `presenter[name as keyof Presenter]`
-- `src/shared/types/presenters/legacy.presenters.d.ts` — `IPresenter` interface
-- `src/renderer/api/legacy/presenters.ts` — proxy-based IPC caller
-
-### 1.4 SQLite
-
-- `src/main/presenter/sqlitePresenter/index.ts` — shared SQLite instance
-- `src/main/presenter/sqlitePresenter/tables/` — table definitions with migration support
-- Migration pattern: each table file exports `createTable()` + `migrations` array
-
-### 1.5 Config
-
-- `src/main/presenter/configPresenter/index.ts` — `getSetting()`, `getEnabledProviders()`, model defaults
-- Default provider/model resolved via `configPresenter.getSetting('DEFAULT_PROVIDER_ID')` and `configPresenter.getSetting('DEFAULT_MODEL_ID')`
-
-## 2. Design Decisions
-
-### 2.1 Naming: `agentSessionPresenter` not `agentPresenter`
-
-The old `agentPresenter` still exists and must keep working. The new one is registered as `agentSessionPresenter` in IPresenter to avoid name collision. Renderer calls `useLegacyPresenter('agentSessionPresenter')`. When old UI is removed, rename to `agentPresenter`.
-
-### 2.2 Database: new tables in same chat.db
-
-No separate DB file. New tables coexist with old tables in `chat.db`:
-- `new_sessions` — agentPresenter's thin session registry
-- `new_projects` — project directory history
-- `deepchat_sessions` — agentRuntimePresenter's session config
-- `deepchat_messages` — agentRuntimePresenter's messages
-
-Prefix `new_` on `sessions` and `projects` to avoid any conflict with potential future SQLite reserved words or collisions.
-
-### 2.3 Stream handling: reuse LLMCoreStreamEvent, transform to LLMAgentEventData
-
-agentRuntimePresenter consumes `LLMCoreStreamEvent` from `coreStream()` and:
-1. Accumulates content into `AssistantMessageBlock[]` structure
-2. Persists structured JSON content to `deepchat_messages` (batched DB writes every 600ms)
-3. Transforms to `LLMAgentEventData` format with `conversationId` + `eventId` for routing
-4. Emits via EventBus (batched renderer flush every 120ms) — agentPresenter relays to renderer
-
-This means the renderer's stream handling code receives the same event format. Same `LLMAgentEventData` structure.
-
-### 2.4 v0 message content: structured JSON from the start
-
-Even in v0, messages use structured JSON content — not plain text. This avoids a migration when adding tool calls and thinking in later versions.
-
-**User messages** are stored as serialized `UserMessageContent`:
-```
-{ text: "user input", files: [], links: [], search: false, think: false }
-```
-
-**Assistant messages** are stored as serialized `AssistantMessageBlock[]`:
-```
-[{ type: "content", content: "LLM response text", status: "success", timestamp: 1234567890 }]
-```
-
-v0 only produces `content` and `reasoning_content` block types. Later versions add `tool_call`, `search`, `error`, etc.
-
-### 2.5 v0 message assembly: single user message, no context
-
-v0 sends exactly one message to the LLM:
-
-```
-messages = [
- { role: 'user', content: input.message }
-]
-```
-
-No system prompt, no conversation history, no tool definitions. Just raw user message → LLM → streamed response. Multi-turn context assembly is v1.
-
-### 2.6 v0 model resolution
-
-Use the global default provider/model from configPresenter unless CreateSessionInput specifies overrides:
-
-1. `input.providerId ?? configPresenter.getSetting('DEFAULT_PROVIDER_ID')`
-2. `input.modelId ?? configPresenter.getSetting('DEFAULT_MODEL_ID')`
-
-Per-session model config (temperature, system prompt, etc.) is not planned. Sessions use provider/model defaults from configPresenter.
-
-### 2.7 Message status lifecycle
-
-Messages use a three-state lifecycle: `pending` → `sent` | `error`
-
-- `pending` — message is being generated (stream in progress)
-- `sent` — generation completed successfully
-- `error` — generation failed or app crashed during generation
-
-**Crash recovery**: On app startup, any `deepchat_messages` rows with `status = 'pending'` are updated to `status = 'error'`. This handles the case where the app was killed during streaming.
-
-### 2.8 Stream batching
-
-Two independent batching intervals to balance responsiveness and write pressure:
-
-- **Renderer flush**: every 120ms — accumulate stream deltas and flush to renderer via EventBus
-- **DB flush**: every 600ms — batch-write accumulated content to `deepchat_messages` table
-
-On stream end, both flush immediately with final content.
-
-## 3. Architecture: Module Breakdown
-
-### 3.1 Shared Types (`src/shared/types/`)
-
-**`agent-interface.d.ts`** — the protocol every agent implements:
-
-- `IAgentImplementation` interface with: `initSession`, `destroySession`, `getSessionState`, `processMessage`, `cancelGeneration`, `getMessages`, `getMessageIds`, `getMessage`
-- v0 subset only — permissions, retry, edit added in later versions
-
-**`chat-types.d.ts`** — data model types:
-
-- `Agent`, `Session`, `SessionStatus`, `CreateSessionInput`, `ChatMessage`, `UserMessageContent`, `AssistantMessageBlock`, `MessageMetadata`, `Project`
-
-**`presenters/agent-session.presenter.d.ts`** — IPC-facing interface:
-
-- `IAgentSessionPresenter` — what the renderer can call: `createSession`, `sendMessage`, `getSessionList`, `getSession`, `getMessages`, `getMessageIds`, `getMessage`, `activateSession`, `deactivateSession`, `getActiveSession`, `getAgents`, `deleteSession`
-
-**`presenters/project.presenter.d.ts`** — IPC-facing interface:
-
-- `IProjectPresenter` — `getProjects`, `getRecentProjects`, `selectDirectory`
-
-### 3.2 New DB Tables (`src/main/presenter/sqlitePresenter/tables/`)
-
-**`newSessions.ts`** — thin session registry
-
-| Column | Type | Notes |
-|--------|------|-------|
-| id | TEXT PRIMARY KEY | UUID |
-| agent_id | TEXT NOT NULL | `'deepchat'` for v0 |
-| title | TEXT NOT NULL | |
-| project_dir | TEXT | nullable |
-| is_pinned | INTEGER DEFAULT 0 | |
-| created_at | INTEGER NOT NULL | epoch ms |
-| updated_at | INTEGER NOT NULL | epoch ms |
-
-**`newProjects.ts`** — project directory history
-
-| Column | Type | Notes |
-|--------|------|-------|
-| path | TEXT PRIMARY KEY | filesystem path |
-| name | TEXT NOT NULL | directory basename |
-| icon | TEXT DEFAULT NULL | base64 icon data |
-| last_accessed_at | INTEGER NOT NULL | epoch ms |
-
-**`deepchatSessions.ts`** — deepchat agent session config
-
-| Column | Type | Notes |
-|--------|------|-------|
-| id | TEXT PRIMARY KEY | same ID as new_sessions |
-| provider_id | TEXT NOT NULL | |
-| model_id | TEXT NOT NULL | |
-
-**`deepchatMessages.ts`** — deepchat agent messages
-
-| Column | Type | Notes |
-|--------|------|-------|
-| id | TEXT PRIMARY KEY | UUID |
-| session_id | TEXT NOT NULL | FK to new_sessions |
-| order_seq | INTEGER NOT NULL | monotonic ordering within session |
-| role | TEXT NOT NULL | `'user'` or `'assistant'` |
-| content | TEXT NOT NULL | JSON string: `UserMessageContent` or `AssistantMessageBlock[]` |
-| status | TEXT DEFAULT 'pending' | `'pending'`, `'sent'`, `'error'` |
-| is_context_edge | INTEGER DEFAULT 0 | marks context window boundary (unused in v0) |
-| metadata | TEXT DEFAULT '{}' | JSON string: token usage, timing, model info |
-| created_at | INTEGER NOT NULL | epoch ms |
-| updated_at | INTEGER NOT NULL | epoch ms |
-
-Index: `CREATE INDEX idx_deepchat_messages_session ON deepchat_messages(session_id, order_seq)`
-
-### 3.3 agentPresenter (`src/main/presenter/agentSessionPresenter/`)
-
-**`index.ts`** — main presenter class implementing `IAgentSessionPresenter`
-
-Owns:
-- `sessionManager` — thin CRUD over `new_sessions` table
-- `messageManager` — proxy that resolves agentId then delegates to agent
-- `agentRegistry` — `Map`, populated in constructor with agentRuntimePresenter
-
-Methods (IPC-facing):
-- `createSession(input, webContentsId)` → sessionManager.create() + agent.initSession() + agent.processMessage() + emit ACTIVATED
-- `sendMessage(sessionId, content)` → resolve agent → agent.processMessage()
-- `getSessionList(params)` → sessionManager.list() + enrich with agent.getSessionState()
-- `getSession(sessionId)` → sessionManager.get() + agent.getSessionState()
-- `getMessages(sessionId)` → resolve agent → agent.getMessages()
-- `getMessageIds(sessionId)` → resolve agent → agent.getMessageIds()
-- `getMessage(messageId)` → resolve agent → agent.getMessage() (needs sessionId lookup)
-- `activateSession(webContentsId, sessionId)` → update window binding + emit ACTIVATED
-- `deactivateSession(webContentsId)` → clear window binding + emit DEACTIVATED
-- `getActiveSession(webContentsId)` → return bound session
-- `getAgents()` → agentRegistry.getAll()
-- `deleteSession(sessionId)` → agent.destroySession() + sessionManager.delete()
-
-Event relay:
-- Listen to agent events (callback or EventEmitter pattern)
-- Re-emit as SESSION_EVENTS / STREAM_EVENTS via EventBus to renderer
-- All STREAM_EVENTS carry `conversationId` for renderer routing
-
-**`sessionManager.ts`** — thin session registry
-
-- `create(id, agentId, title, projectDir)` → INSERT into `new_sessions`
-- `get(id)` → SELECT from `new_sessions`, returns thin record
-- `list(filters)` → SELECT with optional WHERE agent_id / project_dir, ORDER BY updated_at DESC
-- `update(id, fields)` → UPDATE `new_sessions`
-- `delete(id)` → DELETE from `new_sessions`
-- Window bindings: in-memory `Map` for webContentsId → sessionId
-
-**`messageManager.ts`** — proxy
-
-- `getMessages(sessionId)` → resolve agentId from sessionManager → agentRegistry.get(agentId).getMessages(sessionId)
-- Same pattern for `getMessageIds`, `getMessage`
-
-**`agentRegistry.ts`** — agent discovery
-
-- `register(agentId, implementation)` — called in constructor
-- `resolve(agentId)` → returns `IAgentImplementation` or throws
-- `getAll()` → returns `Agent[]` list (for v0: just deepchat)
-
-### 3.4 agentRuntimePresenter (`src/main/presenter/agentRuntimePresenter/`)
-
-**`index.ts`** — implements `IAgentImplementation`
-
-Constructor receives: `llmProviderPresenter`, `configPresenter`, `sqlitePresenter`
-
-Initialization:
-- Run crash recovery: `UPDATE deepchat_messages SET status = 'error' WHERE status = 'pending'`
-
-State: in-memory `Map` for runtime status
-
-Methods:
-- `initSession(sessionId, config)` → INSERT into `deepchat_sessions`, set runtime status to `'idle'`
-- `destroySession(sessionId)` → DELETE from `deepchat_sessions` + `deepchat_messages`, remove from in-memory map
-- `getSessionState(sessionId)` → return `{ status, providerId, modelId }` from in-memory + DB
-- `processMessage(sessionId, content)` → persist user message (status `'sent'`) → create assistant message (status `'pending'`) → call LLM → stream with batching → finalize assistant message (status `'sent'`) → emit events
-- `cancelGeneration(sessionId)` → abort stream (v0: basic abort signal support)
-- `getMessages(sessionId)` → SELECT from `deepchat_messages` WHERE session_id ORDER BY order_seq
-- `getMessageIds(sessionId)` → SELECT id from `deepchat_messages` WHERE session_id ORDER BY order_seq
-- `getMessage(messageId)` → SELECT from `deepchat_messages` WHERE id
-
-**`streamHandler.ts`** — LLM stream consumer
-
-Responsibility: consume `AsyncGenerator`, build structured `AssistantMessageBlock[]` content, persist with batching, emit events.
-
-Flow:
-1. Get provider instance via `llmProviderPresenter.getProviderInstance(providerId)`
-2. Build messages array (v0: just the user message as plain text)
-3. Call `provider.coreStream(messages, modelId, modelConfig, temperature, maxTokens, tools=[])`
-4. Iterate the async generator, accumulating into `AssistantMessageBlock[]`:
- - On `text` event → append to current `content` block, queue renderer flush
- - On `reasoning` event → append to current `reasoning_content` block, queue renderer flush
- - On `usage` event → store in metadata
- - On `stop` event → finalize all blocks, flush DB, set message status to `'sent'`, emit `stream:end`
- - On `error` event → add `error` block, set message status to `'error'`, emit `stream:error`
-5. Renderer batching: flush accumulated deltas every 120ms as `stream:response` with `LLMAgentEventData` (includes `conversationId`, `eventId`)
-6. DB batching: write accumulated content JSON to `deepchat_messages` every 600ms
-7. On stream end or error, flush both immediately
-
-**`sessionStore.ts`** — deepchat session persistence
-
-- CRUD over `deepchat_sessions` table (columns: id, provider_id, model_id)
-
-**`messageStore.ts`** — deepchat message persistence
-
-- CRUD over `deepchat_messages` table
-- `createUserMessage(sessionId, orderSeq, content)` → INSERT with role='user', status='sent', content=JSON string of `UserMessageContent`
-- `createAssistantMessage(sessionId, orderSeq)` → INSERT with role='assistant', status='pending', content='[]'
-- `updateAssistantContent(messageId, contentJson)` → UPDATE content (batched)
-- `finalizeAssistantMessage(messageId, contentJson, metadataJson)` → UPDATE content, status='sent', metadata
-- `setMessageError(messageId, errorJson)` → UPDATE status='error', content with error block appended
-- `recoverPendingMessages()` → UPDATE status='error' WHERE status='pending' (called on startup)
-- `getNextOrderSeq(sessionId)` → SELECT MAX(order_seq) + 1
-
-### 3.5 projectPresenter (`src/main/presenter/projectPresenter/`)
-
-**`index.ts`** — implements `IProjectPresenter`
-
-Constructor receives: `sqlitePresenter`, `devicePresenter`
-
-Methods:
-- `getProjects()` → SELECT * FROM new_projects ORDER BY last_accessed_at DESC
-- `getRecentProjects(limit)` → SELECT * FROM new_projects ORDER BY last_accessed_at DESC LIMIT ?
-- `selectDirectory()` → `devicePresenter.selectDirectory()`, if selected upsert into new_projects, return path
-
-### 3.6 Presenter Registration (`src/main/presenter/index.ts`)
-
-3 touchpoints:
-
-1. Import and add to IPresenter: `agentSessionPresenter: IAgentSessionPresenter`, `projectPresenter: IProjectPresenter`
-2. Add class properties
-3. Instantiate in constructor:
- - `this.agentRuntimePresenter = new AgentRuntimePresenter(this.llmProviderPresenter, this.configPresenter, this.sqlitePresenter)`
- - `this.agentSessionPresenter = new AgentSessionPresenter(this.agentRuntimePresenter, this.configPresenter, this.sqlitePresenter, this.eventBus)`
- - `this.projectPresenter = new ProjectPresenter(this.sqlitePresenter, this.devicePresenter)`
-
-Note: `agentRuntimePresenter` is NOT exposed on IPresenter — it's internal. Only `agentSessionPresenter` and `projectPresenter` are IPC-accessible.
-
-### 3.7 Events (`src/main/events.ts`)
-
-Add:
-
-```
-SESSION_EVENTS = {
- LIST_UPDATED: 'session:list-updated',
- ACTIVATED: 'session:activated',
- DEACTIVATED: 'session:deactivated',
- STATUS_CHANGED: 'session:status-changed',
-}
-```
-
-STREAM_EVENTS reused as-is — same event names, same payload format. All stream events include `conversationId` for renderer-side routing.
-
-### 3.8 Renderer Stores (`src/renderer/src/stores/ui/`)
-
-**`session.ts`** — rewrite
-
-- Uses `useLegacyPresenter('agentSessionPresenter')`
-- State: `sessions: Session[]`, `activeSessionId`, `groupMode`
-- Actions: `fetchSessions()`, `createSession(input)`, `selectSession(id)`, `closeSession()`
-- Listens to: `SESSION_EVENTS.LIST_UPDATED`, `SESSION_EVENTS.ACTIVATED`, `SESSION_EVENTS.DEACTIVATED`, `SESSION_EVENTS.STATUS_CHANGED`
-
-**`message.ts`** — new
-
-- Uses `useLegacyPresenter('agentSessionPresenter')`
-- State: `messageIds: string[]`, `messageCache: Map`, `isStreaming: boolean`, `streamingBlocks: AssistantMessageBlock[]`
-- Actions: `loadMessages(sessionId)`, `getMessage(id)`
-- Listens to: `STREAM_EVENTS.RESPONSE` (update streaming blocks from `LLMAgentEventData`), `STREAM_EVENTS.END` (finalize), `STREAM_EVENTS.ERROR`
-- Filters events by `conversationId` matching active session
-
-**`agent.ts`** — rewrite
-
-- Uses `useLegacyPresenter('agentSessionPresenter')`
-- State: `agents: Agent[]`, `selectedAgentId`
-- Actions: `fetchAgents()`, `selectAgent(id)`
-
-**`project.ts`** — rewrite
-
-- Uses `useLegacyPresenter('projectPresenter')`
-- State: `projects: Project[]`, `selectedProjectPath`
-- Actions: `fetchProjects()`, `selectProject(path)`, `openFolderPicker()`
-
-**`draft.ts`** — new
-
-- State: `providerId`, `modelId`, `projectDir`, `agentId`, `reasoningEffort`
-- Actions: `toCreateInput(message)` → `CreateSessionInput`, `reset()`
-
-### 3.9 NewThreadPage Integration
-
-- Imports `useSessionStore`, `useDraftStore`, `useMessageStore`
-- On submit: `draftStore.toCreateInput(text)` → `sessionStore.createSession(input)`
-- Session ACTIVATED event triggers message loading
-- messageStore receives STREAM_EVENTS, filters by `conversationId`, and displays streaming blocks
-
-## 4. Test Strategy
-
-### 4.1 Unit Tests
-
-**agentPresenter:**
-- `sessionManager.create/get/list/update/delete` — CRUD against in-memory SQLite
-- `agentRegistry.register/resolve/getAll` — correct routing
-- `createSession` → calls sessionManager.create + agent.initSession + agent.processMessage
-
-**agentRuntimePresenter:**
-- `processMessage` → creates user message (JSON content), calls LLM, creates assistant message (JSON blocks)
-- `streamHandler` — given mock `AsyncGenerator`, verify: block accumulation, batched DB writes at 600ms, renderer flush at 120ms, final flush on stop
-- `messageStore` — CRUD operations against in-memory SQLite, verify JSON content round-trip
-- `messageStore.recoverPendingMessages()` — pending rows updated to error
-- `messageStore.getNextOrderSeq()` — correct sequence calculation
-
-**projectPresenter:**
-- `getRecentProjects` — returns correct order and limit
-- `selectDirectory` — upserts on new selection
-
-### 4.2 Integration Tests
-
-- End-to-end: `agentSessionPresenter.createSession()` → verify new_sessions row + deepchat_sessions row + deepchat_messages rows (with valid JSON content) + events emitted with conversationId
-- Coexistence: old `sessionPresenter.createSession()` still works — old tables unaffected
-- Crash recovery: insert pending message, reinitialize presenter, verify status changed to error
-
-## 5. Risks & Mitigation
-
-| Risk | Impact | Mitigation |
-|------|--------|------------|
-| LLM provider internal API changes | Cannot call `coreStream()` directly | Check `BaseLLMProvider` is stable; if not, use `llmProviderPresenter` public method |
-| Stream event format mismatch | Renderer can't display response | Reuse exact `LLMAgentEventData` format — same as old agentPresenter emits |
-| DB migration conflicts with old tables | Data corruption | New tables use distinct names (`new_sessions`, `deepchat_*`) — zero overlap |
-| Performance of batched message writes | Streaming feels laggy | DB flush every 600ms, renderer flush every 120ms — matches old `StreamUpdateScheduler` intervals |
-| JSON content parsing errors | Messages unreadable | Validate JSON on write, wrap parse in try/catch on read, store raw text as fallback error block |
-
-## 6. Quality Gate
-
-- [ ] `pnpm run format`
-- [ ] `pnpm run lint`
-- [ ] `pnpm run typecheck`
-- [ ] Unit tests pass for all new modules
-- [ ] Integration test: create session + stream response end-to-end
-- [ ] Old UI regression: `sessionPresenter.getSessionList()` returns same results as before
-
diff --git a/docs/archives/new-agent/spec.md b/docs/archives/new-agent/spec.md
deleted file mode 100644
index 2334eb871..000000000
--- a/docs/archives/new-agent/spec.md
+++ /dev/null
@@ -1,216 +0,0 @@
-# New Agent Architecture v0 — Minimal Single-Turn Chat
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-Replace the old sessionPresenter + agentPresenter architecture with an agent-centric model. v0 delivers the minimum working system: a user can create a session, send one message, and see a streamed LLM response — all through the new architecture with new DB tables and new presenters.
-
-v0 is the foundation. It proves the architecture end-to-end. Subsequent versions (v1–v5) add multi-turn, tool calling, permissions, config, and ACP support incrementally.
-
-## Background
-
-See [Full-Stack Mismatch Analysis](../../architecture/new-ui-store-presenter-mismatch.md) for the complete problem analysis and architectural decisions, including:
-- Why agents are the organizing principle (not sessions)
-- Why new presenters coexist with old ones (not wrap/translate)
-- The ownership model: agents own their data, agentPresenter owns the registry
-- The "build new, grow forward" strategy
-
-## Goals
-
-1. **Agent interface protocol** — define the unified contract all agents implement
-2. **agentPresenter** — router, thin session registry, event relay
-3. **agentRuntimePresenter** — single-turn chat: message → LLM → streamed response → persist
-4. **projectPresenter** — thin project directory CRUD
-5. **New DB tables** — `new_sessions`, `new_projects`, `deepchat_sessions`, `deepchat_messages`
-6. **New renderer stores** — sessionStore, messageStore, agentStore, projectStore, draftStore
-7. **NewThreadPage integration** — create session and see streamed response through new architecture
-
-## Non-Goals (deferred to later versions)
-
-- Multi-turn conversation context assembly (v1)
-- Tool calling / MCP integration (v2)
-- Permission and question flows (v3)
-- ACP agent support (v5)
-- Migration / backfill from old tables (separate task)
-- Modifying or removing any old presenter or old UI code
-- i18n for new components (follow-up)
-
-## Scope Boundary
-
-### New files to create
-
-**Main process:**
-- `src/shared/types/agent-interface.d.ts` — agent interface protocol
-- `src/shared/types/chat-types.d.ts` — Agent, Session, Project, CreateSessionInput, ChatMessage, MessageBlock types
-- `src/main/presenter/agentSessionPresenter/index.ts` — agentPresenter (router)
-- `src/main/presenter/agentSessionPresenter/sessionManager.ts` — thin session registry
-- `src/main/presenter/agentSessionPresenter/messageManager.ts` — message proxy
-- `src/main/presenter/agentSessionPresenter/agentRegistry.ts` — agent discovery + routing
-- `src/main/presenter/agentRuntimePresenter/index.ts` — deepchat agent implementation
-- `src/main/presenter/agentRuntimePresenter/sessionStore.ts` — agent-owned session persistence
-- `src/main/presenter/agentRuntimePresenter/messageStore.ts` — agent-owned message persistence
-- `src/main/presenter/agentRuntimePresenter/streamHandler.ts` — LLM stream → message persistence + event emission
-- `src/main/presenter/projectPresenter/index.ts` — project CRUD
-- `src/main/presenter/sqlitePresenter/tables/` — new table definitions
-- `src/main/events.ts` — add SESSION_EVENTS
-
-**Renderer:**
-- `src/renderer/src/stores/ui/session.ts` — rewrite
-- `src/renderer/src/stores/ui/message.ts` — new
-- `src/renderer/src/stores/ui/agent.ts` — rewrite
-- `src/renderer/src/stores/ui/project.ts` — rewrite
-- `src/renderer/src/stores/ui/draft.ts` — new
-
-### Existing files to modify
-
-- `src/shared/types/presenters/legacy.presenters.d.ts` — add to IPresenter interface
-- `src/main/presenter/index.ts` — register new presenters (3 touchpoints)
-- `src/renderer/src/pages/NewThreadPage.vue` — wire to new stores
-- `src/renderer/src/events.ts` — add SESSION_EVENTS mirror
-
-### Explicitly NOT modified
-
-- All old presenters (sessionPresenter, old agentPresenter, threadPresenter)
-- Old DB tables (conversations, messages)
-- Legacy chatStore (`stores/chat.ts`)
-- Old UI components that use legacy stores (ChatPage stays on old stores for now)
-- LLM provider implementations (reused as-is)
-- mcpPresenter (reused as-is, wired in v2)
-
-## Acceptance Criteria
-
-### Functional Requirements
-
-- [ ] Agent interface protocol defined as TypeScript types in `src/shared/types/`
-- [ ] agentPresenter registered in Presenter class, accessible via `useLegacyPresenter('agentSessionPresenter')`
-- [ ] agentPresenter.sessionManager creates session records in new `new_sessions` table
-- [ ] agentPresenter.agentRegistry returns `[{ id: 'deepchat', name: 'DeepChat', type: 'deepchat', enabled: true }]`
-- [ ] agentPresenter routes `sendMessage()` to agentRuntimePresenter based on session's agentId
-- [ ] agentRuntimePresenter calls LLM provider's `coreStream()` and receives `LLMCoreStreamEvent` stream
-- [ ] agentRuntimePresenter persists user message and assistant message in `deepchat_messages` table with structured JSON content
-- [ ] agentRuntimePresenter emits stream events (response/end/error) via EventBus with `conversationId` for routing
-- [ ] agentPresenter relays all stream events to renderer
-- [ ] Stream events batched: 120ms flush to renderer, 600ms flush to DB
-- [ ] On app restart, any messages with `status = 'pending'` are marked as `'error'` (crash recovery)
-- [ ] projectPresenter reads/writes `new_projects` table
-- [ ] NewThreadPage creates a session and displays streamed response through new architecture
-- [ ] sessionStore displays session list in sidebar from new `new_sessions` table
-- [ ] messageStore displays messages from new `deepchat_messages` table
-- [ ] Old UI continues to work via old presenters — zero regression
-
-### Non-Functional Requirements
-
-- [ ] New tables created alongside old tables in same chat.db (no separate DB file)
-- [ ] All new types in `src/shared/types/` — no type definitions in presenter files
-- [ ] `pnpm run typecheck` passes
-- [ ] `pnpm run lint` passes
-- [ ] `pnpm run format` passes
-- [ ] Unit tests for agentPresenter routing, agentRuntimePresenter stream handling, sessionManager CRUD
-
-## Constraints
-
-- LLM provider HTTP clients are reused directly — agentRuntimePresenter calls `BaseLLMProvider.coreStream()` and consumes the `AsyncGenerator`
-- New tables live in the same SQLite database (`chat.db`) alongside old tables — no separate DB file
-- IPC routing is dynamic (`presenter[name]`) — no route registration needed beyond the 3 touchpoints
-- v0 sends a single user message and gets a single assistant response — no multi-turn context, no tool use
-- Sessions use default model config from `configPresenter` — no per-session config overrides (by design, not deferred)
-
-## Data Model (v0 subset)
-
-### Agent
-
-- `id: string` — `'deepchat'` (only agent in v0)
-- `name: string` — `'DeepChat'`
-- `type: 'deepchat' | 'acp'` — `'deepchat'`
-- `enabled: boolean` — `true`
-
-### Session (UI-facing)
-
-- `id: string`
-- `title: string`
-- `status: 'idle' | 'generating' | 'error'` — (`'waiting'` deferred to v3)
-- `agentId: string`
-- `projectDir: string | null`
-- `providerId: string`
-- `modelId: string`
-- `isPinned: boolean`
-- `createdAt: number`
-- `updatedAt: number`
-
-### CreateSessionInput
-
-- `agentId: string`
-- `message: string`
-- `projectDir?: string`
-- `providerId?: string`
-- `modelId?: string`
-
-### Message (UI-facing, v0 subset)
-
-- `id: string`
-- `sessionId: string`
-- `orderSeq: number` — monotonic ordering within a session
-- `role: 'user' | 'assistant'`
-- `content: string` — JSON string in DB. For user: serialized `UserMessageContent`. For assistant: serialized `AssistantMessageBlock[]`
-- `status: 'pending' | 'sent' | 'error'` — `pending` = generation in progress, `sent` = complete, `error` = failed or crash recovery
-- `metadata: string` — JSON string for token usage, timing, model info
-- `createdAt: number`
-- `updatedAt: number`
-
-### UserMessageContent (stored as JSON)
-
-- `text: string` — the user's input text
-- `files: MessageFile[]` — attached files (empty in v0)
-- `links: string[]` — attached links (empty in v0)
-- `search: boolean` — whether web search was requested
-- `think: boolean` — whether thinking/reasoning was requested
-
-### AssistantMessageBlock (stored as JSON array)
-
-Each block has a `type` discriminator. v0 uses these block types:
-
-- `content` — text content from LLM response. Fields: `type`, `content`, `status`, `timestamp`
-- `reasoning_content` — thinking/reasoning output. Fields: `type`, `content`, `status`, `timestamp`, `reasoning_time`
-- `error` — error information. Fields: `type`, `content`, `status`, `timestamp`
-
-Additional block types added in later versions: `tool_call` (v2), `search` (v2), `action` (v2), `image` (v2)
-
-### MESSAGE_METADATA (stored in metadata JSON field)
-
-- `totalTokens: number`
-- `inputTokens: number`
-- `outputTokens: number`
-- `generationTime: number`
-- `firstTokenTime: number`
-- `tokensPerSecond: number`
-- `model?: string`
-- `provider?: string`
-
-### Project
-
-- `path: string`
-- `name: string`
-- `icon: string | null` — base64 icon data
-- `lastAccessedAt: number`
-
-## Event System (v0 subset)
-
-### SESSION_EVENTS (emitted by agentPresenter)
-
-- `session:list-updated` — session list changed
-- `session:activated` — `{ webContentsId, sessionId }`
-- `session:deactivated` — `{ webContentsId }`
-- `session:status-changed` — `{ sessionId, status }`
-
-### STREAM_EVENTS (relayed from agents, unchanged format)
-
-- `stream:response` — streaming chunk, carries `LLMAgentEventData` with `conversationId` and `eventId` for routing
-- `stream:end` — streaming complete, carries `{ conversationId }`
-- `stream:error` — streaming failed, carries `{ conversationId, error }`
-
-## Open Questions
-
-None. All architectural decisions resolved in the mismatch analysis document.
-
diff --git a/docs/archives/new-agent/tasks.md b/docs/archives/new-agent/tasks.md
deleted file mode 100644
index 6dc12d250..000000000
--- a/docs/archives/new-agent/tasks.md
+++ /dev/null
@@ -1,116 +0,0 @@
-# New Agent Architecture — Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## T0 Shared Types & Events
-
-- [x] Create `src/shared/types/agent-interface.d.ts` — `IAgentImplementation`, `Agent`, `Session`, `SessionStatus`, `CreateSessionInput`, `ChatMessageRecord`, `UserMessageContent`, `AssistantMessageBlock`, `MessageMetadata`, `Project` (merged chat-types into this file)
-- [x] Create `src/shared/types/presenters/agent-session.presenter.d.ts` — `IAgentSessionPresenter` interface
-- [x] Create `src/shared/types/presenters/project.presenter.d.ts` — `IProjectPresenter` interface
-- [x] Export new types from `src/shared/types/presenters/index.d.ts`
-- [x] Add `SESSION_EVENTS` to `src/main/events.ts` (list-updated, activated, deactivated, status-changed)
-- [x] Add `SESSION_EVENTS` mirror to `src/renderer/src/events.ts`
-
-## T1 New DB Tables
-
-- [x] Create `src/main/presenter/sqlitePresenter/tables/newSessions.ts` — `new_sessions` table (no provider_id/model_id — agent owns those)
-- [x] Create `src/main/presenter/sqlitePresenter/tables/newProjects.ts` — `new_projects` table with `icon` column
-- [x] Create `src/main/presenter/sqlitePresenter/tables/deepchatSessions.ts` — `deepchat_sessions` table (id, provider_id, model_id only — no per-session config columns)
-- [x] Create `src/main/presenter/sqlitePresenter/tables/deepchatMessages.ts` — `deepchat_messages` table with order_seq, JSON content, status (pending/sent/error), is_context_edge, metadata; index on (session_id, order_seq)
-- [x] Register new tables in `sqlitePresenter/index.ts` (initTables + migrate array)
-
-## T2 agentRuntimePresenter
-
-- [x] Create `messageStore.ts` — CRUD over `deepchat_messages`
-- [x] Create `sessionStore.ts` — CRUD over `deepchat_sessions`
-- [x] Create `index.ts` — implements `IAgentImplementation`, wires sessionStore + messageStore + llmProviderPresenter, runs crash recovery on init
-- [x] Unit tests: processMessage, recoverPendingMessages (`agentRuntimePresenter.test.ts`)
-
-## T3 agentPresenter (agentSessionPresenter)
-
-- [x] Create `src/main/presenter/agentSessionPresenter/agentRegistry.ts` — register/resolve/getAll
-- [x] Create `src/main/presenter/agentSessionPresenter/sessionManager.ts` — CRUD over `new_sessions`, in-memory window bindings (webContentsId → sessionId)
-- [x] Create `src/main/presenter/agentSessionPresenter/messageManager.ts` — proxy resolves agentId then delegates to agent
-- [x] Create `src/main/presenter/agentSessionPresenter/index.ts` — implements `IAgentSessionPresenter`, wires sessionManager + messageManager + agentRegistry + event relay (all stream events carry conversationId)
-- [x] Unit tests: sessionManager CRUD + window bindings (`test/main/presenter/agentSessionPresenter/sessionManager.test.ts`)
-- [x] Unit tests: agentRegistry register/resolve/getAll/has (`test/main/presenter/agentSessionPresenter/agentRegistry.test.ts`)
-- [x] Unit tests: messageManager delegation (`test/main/presenter/agentSessionPresenter/messageManager.test.ts`)
-- [x] Unit tests: createSession → verify sessionManager.create + agent.initSession + agent.processMessage called (`test/main/presenter/agentSessionPresenter/agentSessionPresenter.test.ts`)
-- [x] Unit tests: sendMessage → verify agent routing (`test/main/presenter/agentSessionPresenter/agentSessionPresenter.test.ts`)
-
-## T4 projectPresenter
-
-- [x] Create `src/main/presenter/projectPresenter/index.ts` — implements `IProjectPresenter`, CRUD over `new_projects`, selectDirectory via devicePresenter
-- [x] Unit tests: getProjects, getRecentProjects (order + limit), selectDirectory (`test/main/presenter/projectPresenter/projectPresenter.test.ts`)
-
-## T5 Presenter Registration
-
-- [x] Add `IAgentSessionPresenter` and `IProjectPresenter` to `IPresenter` interface in `src/shared/types/presenters/legacy.presenters.d.ts`
-- [x] Add properties and constructor instantiation in `src/main/presenter/index.ts`
-- [x] Verify: `useLegacyPresenter('agentSessionPresenter')` and `useLegacyPresenter('projectPresenter')` callable from renderer
-
-## T6 Renderer Stores
-
-- [x] Rewrite `src/renderer/src/stores/ui/session.ts` — uses `agentSessionPresenter`, listens to `SESSION_EVENTS`, uses `webContentsId` for activation
-- [x] Create `src/renderer/src/stores/ui/message.ts` — uses `agentSessionPresenter`, listens to `STREAM_EVENTS`, filters by conversationId, maintains streamingBlocks as AssistantMessageBlock[]
-- [x] Rewrite `src/renderer/src/stores/ui/agent.ts` — uses `agentSessionPresenter.getAgents()`
-- [x] Rewrite `src/renderer/src/stores/ui/project.ts` — uses `projectPresenter`
-- [x] Create `src/renderer/src/stores/ui/draft.ts` — pre-session config, toCreateInput()
-
-## T7 NewThreadPage Integration
-
-- [x] Update `src/renderer/src/pages/NewThreadPage.vue` — wire to new stores (removed `title` from CreateSessionInput, title derived from message in presenter)
-- [x] Update `src/renderer/src/views/ChatTabView.vue` — `deriveFromSessions` → `fetchProjects`
-- [x] Verify: type message → submit → session created → streaming response displayed with structured blocks
-- [x] Verify: session appears in sidebar via sessionStore
-
-## T8 Quality Gate & Verification
-
-- [x] `pnpm run typecheck` — passes
-- [x] `pnpm run lint` — passes (0 warnings, 0 errors)
-- [x] `pnpm run format` — passes
-- [x] Unit tests: all new modules passing
-- [x] Integration test: createSession end-to-end — new_sessions row + deepchat_sessions row + deepchat_messages rows (valid JSON content) + events with conversationId (`test/main/presenter/agentSessionPresenter/integration.test.ts`)
-- [x] Integration test: crash recovery — insert pending message, reinit, verify status = error (`test/main/presenter/agentSessionPresenter/integration.test.ts`)
-- [x] Verify old UI regression: old `sessionPresenter` / `chatStore` still functional — zero impact
-- [x] Manual verify: run `pnpm run dev`, create session via NewThreadPage, see streamed response
-
----
-
-## v1: Multi-Turn Context Assembly (complete)
-
-- [x] Create `contextBuilder.ts` — context assembly + truncation
-- [x] Modify `processMessage` in `index.ts` — wire context builder
-- [x] Unit tests for context builder (`contextBuilder.test.ts`)
-- [x] Update `agentRuntimePresenter.test.ts` — mock `getDefaultSystemPrompt`, verify multi-turn messages
-- [x] Update `integration.test.ts` — verify multi-turn flow end-to-end
-- [x] Quality gate: typecheck, lint, format, tests
-
-## v2: Tool Calling / MCP Integration (complete)
-
-- [x] Fetch MCP tool definitions via `ToolPresenter.getAllToolDefinitions()` and pass to `coreStream`
-- [x] `tool_call_start/chunk/end` events create `tool_call` blocks in the stream
-- [x] `stop_reason: 'tool_use'` triggers tool execution via `ToolPresenter.callTool()`
-- [x] Tool results appended as `role: 'tool'` messages, loop re-invokes `coreStream`
-- [x] Multi-turn tool loop works (multiple rounds of tool calls)
-- [x] Max tool calls limit (128) stops the loop
-- [x] Abort signal cancels the loop mid-execution
-- [x] Tool call blocks rendered with name, params, and response
-- [x] Interleaved thinking support for deepseek-reasoner / kimi-k2-thinking / glm-4.7
-- [x] Quality gate: typecheck, lint, format, tests
-- [x] Fix: stop passing sessionId as conversationId to tool definitions (new agent doesn't use skills)
-
-## v3: Stream Processing Refactor (complete)
-
-- [x] Create `src/shared/utils/throttle.ts` — reusable trailing-edge throttle utility
-- [x] Create `types.ts` — `StreamState`, `IoParams`, `ProcessParams`, `createState()`
-- [x] Create `accumulator.ts` — pure `accumulate(state, event)` block mutations
-- [x] Create `echo.ts` — interval-based flush to renderer (120ms) + DB (600ms) with throttle
-- [x] Create `dispatch.ts` — `executeTools()`, `finalize()`, `finalizeError()`
-- [x] Create `process.ts` — unified `processStream()` loop, single code path for tools and no-tools
-- [x] Update `index.ts` — replace `handleStream`/`agentLoop` with single `processStream()` call
-- [x] Delete `streamHandler.ts` and `agentLoop.ts`
-- [x] Tests: `throttle.test.ts` (7), `accumulator.test.ts` (14), `echo.test.ts` (5), `dispatch.test.ts` (14), `process.test.ts` (9), updated `agentRuntimePresenter.test.ts` (19)
-- [x] Quality gate: typecheck, lint, format, 89 tests passing
-
diff --git a/docs/archives/new-agent/v1-spec.md b/docs/archives/new-agent/v1-spec.md
deleted file mode 100644
index 20dbd9a3e..000000000
--- a/docs/archives/new-agent/v1-spec.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# New Agent Architecture v1 — Multi-Turn Context Assembly
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-v0 proved the new agent architecture end-to-end but sends only the latest user message to the LLM — no conversation history, no system prompt. Every message starts a fresh, context-free conversation. v1 adds multi-turn context assembly so the LLM sees the full conversation history within token limits.
-
-## Goals
-
-1. **System prompt injection** — prepend the user's configured default system prompt to every LLM call
-2. **Conversation history** — include all prior sent messages in the LLM context
-3. **Context window truncation** — drop oldest user+assistant pairs when history exceeds available tokens
-
-## Non-Goals (deferred)
-
-- Tool calling context (v2)
-- `is_context_edge` usage (future)
-- Per-session system prompts (future)
-- Vision/image content in messages (future)
-- Streaming context or partial messages
-
-## Data Model
-
-No new DB tables or columns. Reuses existing:
-- `deepchat_messages` table — fetch prior messages by session
-- `configPresenter.getDefaultSystemPrompt()` — system prompt retrieval
-- `ModelConfig.contextLength` — context window size
-
-## Context Assembly Algorithm
-
-1. Fetch all messages for session via `messageStore.getMessages(sessionId)`
-2. Filter to `status === 'sent'` only (skip pending/error messages)
-3. Exclude the just-inserted new user message (it hasn't been marked sent yet — it's the latest with status 'sent' but we pass `newUserContent` explicitly)
-4. Convert each `ChatMessageRecord` → `ChatMessage`:
- - **User**: parse JSON `UserMessageContent`, extract `.text`
- - **Assistant**: parse JSON `AssistantMessageBlock[]`, concatenate text from `content` and `reasoning_content` blocks
-5. Apply truncation to fit within token budget
-6. Prepend system prompt (if non-empty): `{ role: 'system', content }`
-7. Append new user message: `{ role: 'user', content: newUserContent }`
-
-## Truncation Strategy
-
-1. Calculate: `available = contextLength - systemPromptTokens - newUserMessageTokens - reserveForOutput`
-2. Sum tokens of all history messages using `approximateTokenSize` from `tokenx`
-3. If total exceeds `available`, drop oldest messages from the front until it fits
-4. Return trimmed history
-
-Reserve for output: use `maxTokens` from model config to ensure the model has room to respond.
-
-## Acceptance Criteria
-
-- [ ] Multi-turn works: LLM sees prior messages in conversation
-- [ ] System prompt injected as first message when non-empty
-- [ ] System prompt omitted when empty string
-- [ ] Truncation drops oldest messages when history exceeds available tokens
-- [ ] Error/pending messages excluded from context
-- [ ] Assistant blocks concatenated correctly (content + reasoning_content)
-- [ ] `pnpm run typecheck` passes
-- [ ] All tests pass
-- [ ] `pnpm run lint && pnpm run format` passes
diff --git a/docs/archives/new-agent/v2-spec.md b/docs/archives/new-agent/v2-spec.md
deleted file mode 100644
index ba1687075..000000000
--- a/docs/archives/new-agent/v2-spec.md
+++ /dev/null
@@ -1,135 +0,0 @@
-# New Agent Architecture v2 — Tool Calling / MCP Integration
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Status: Complete (superseded by v3 refactor)
-
-> The v2 implementation has been refactored into the v3 module structure. See `v3-spec.md` for the current architecture. This spec is retained for historical context on design decisions.
-> Note: the historical MCP UI resource exploration mentioned below is no longer supported in the current codebase.
-
-## Overview
-
-v0 proved single-turn chat, v1 added multi-turn context assembly. The LLM currently receives `tools: []` — no tool definitions, no tool execution. v2 adds MCP tool calling so the LLM can invoke tools and receive results in an agent loop.
-
-## Goals
-
-1. **Tool definition discovery** — fetch MCP tool definitions via `ToolPresenter.getAllToolDefinitions()` and pass to `coreStream`
-2. **Agent loop** — when `coreStream` stops with `stop_reason: 'tool_use'`, execute tools and re-call `coreStream` with results
-3. **Tool execution** — call tools via `ToolPresenter.callTool()`, format results as `role: 'tool'` messages
-4. **Tool call rendering** — emit `tool_call` blocks so the renderer displays tool invocations and results
-5. **Safety limit** — cap tool calls at MAX_TOOL_CALLS per processMessage invocation
-
-## Non-Goals (deferred)
-
-- Permission pre-checking / user approval
-- Question tool — halting the loop for user input
-- ACP agent tool routing — ACP handles tools internally
-- Search result extraction from tool responses
-- Tool system prompt injection (`ToolPresenter.buildToolSystemPrompt`)
-
-## Data Model
-
-No new DB tables. Changes to existing types:
-
-- `AssistantBlockType` gains `'tool_call'` variant
-- `AssistantMessageBlock` gains optional `tool_call` field for tool metadata
-- Tool results are transient within the agent loop's `conversationMessages` array — not persisted as separate DB records (tool_call blocks in the assistant message capture the tool name, params, and response for display)
-
-## Architecture (current — v3 module structure)
-
-The v2 goals are implemented in the v3 module structure. The original `streamHandler.ts` + `agentLoop.ts` were refactored into five focused modules:
-
-```
-agentRuntimePresenter/
- index.ts — session lifecycle + single processStream() call
- process.ts — unified loop: stream → accumulate → echo → dispatch
- accumulator.ts — accumulate(state, event): pure block mutations
- echo.ts — interval-based flush to renderer + DB
- dispatch.ts — executeTools() + finalize() + finalizeError()
- types.ts — StreamState, IoParams, ProcessParams
- contextBuilder.ts — DB records to ChatMessage[], truncation
- messageStore.ts — SQLite wrapper
- sessionStore.ts — SQLite wrapper
-```
-
-### Tool Call Flow
-
-```
-processMessage(sessionId, content)
- ├── buildContext(...) → ChatMessage[]
- ├── toolPresenter.getAllToolDefinitions(...) → MCPToolDefinition[]
- └── processStream(params)
- │
- ├── LOOP:
- │ ├── coreStream(conversation, model, config, temp, maxTokens, tools)
- │ ├── for await (event of stream): accumulate(state, event)
- │ │
- │ ├── if stopReason !== 'tool_use' → BREAK
- │ │
- │ ├── executeTools(state, conversation, prevBlockCount, ...)
- │ │ ├── build assistant message (content + tool_calls + reasoning_content)
- │ │ ├── for each tool call:
- │ │ │ callTool() → push tool result to conversation → update block
- │ │ └── enrich blocks with server info
- │ ├── echo.flush()
- │ │
- │ └── if toolCallCount > MAX_TOOL_CALLS → BREAK
- │
- ├── finalize(state, io)
- └── (catch) finalizeError(state, io, err)
-```
-
-### Stream Event Mapping
-
-| LLMCoreStreamEvent | Action |
-|---|---|
-| `tool_call_start` | Create `tool_call` block with `status: 'pending'`, record id + name |
-| `tool_call_chunk` | Accumulate arguments into pending tool call |
-| `tool_call_end` | Finalize arguments, move to completedToolCalls |
-| `stop` with `stop_reason: 'tool_use'` | Break out of stream, enter tool execution |
-| `stop` with other reason | Break out of loop, finalize |
-
-### Tool Call Block Format
-
-```typescript
-{
- type: 'tool_call',
- content: '', // unused for tool_call type
- status: 'pending' | 'success' | 'error',
- timestamp: number,
- tool_call: {
- id: string,
- name: string,
- params: string, // JSON arguments
- response: string, // tool result text
- server_name?: string,
- server_icons?: string,
- server_description?: string
- }
-}
-```
-
-## Key Dependencies
-
-- `IToolPresenter` — `src/shared/types/presenters/tool.presenter.d.ts`
- - `getAllToolDefinitions(context)` → `MCPToolDefinition[]`
- - `callTool(request: MCPToolCall)` → `{ content, rawData: MCPToolResponse }`
-- `MCPToolDefinition`, `MCPToolCall`, `MCPToolResponse` — `src/shared/types/core/mcp.ts`
-- `ChatMessage` with `tool_calls` and `tool_call_id` — `src/shared/types/core/chat-message.ts`
-- `StopStreamEvent.stop_reason: 'tool_use'` — `src/shared/types/core/llm-events.ts`
-- `ToolPresenter` already instantiated at `src/main/presenter/index.ts:191`
-
-## Acceptance Criteria
-
-- [x] Tool definitions passed to `coreStream` when tools are available
-- [x] `tool_call_start/chunk/end` events create `tool_call` blocks in the stream
-- [x] `stop_reason: 'tool_use'` triggers tool execution via `ToolPresenter.callTool()`
-- [x] Tool results appended as `role: 'tool'` messages and loop re-invokes `coreStream`
-- [x] Multi-turn tool loop works: LLM calls tools, gets results, calls more tools or produces final answer
-- [x] Max tool calls limit (128) stops the loop
-- [x] Abort signal cancels the loop mid-execution
-- [x] Tool call blocks rendered in the UI with name, params, and response
-- [x] `pnpm run typecheck` passes
-- [x] All tests pass
-- [x] `pnpm run lint && pnpm run format` passes
diff --git a/docs/archives/new-agent/v3-spec.md b/docs/archives/new-agent/v3-spec.md
deleted file mode 100644
index e894202ad..000000000
--- a/docs/archives/new-agent/v3-spec.md
+++ /dev/null
@@ -1,275 +0,0 @@
-# New Agent Architecture v3 — Stream Processing Refactor
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Status: Complete
-
-## Overview
-
-v2 added MCP tool calling via an agent loop. The implementation worked but had tangled responsibilities: `streamHandler.ts` (336 lines) mixed stream parsing, block accumulation, flush scheduling, and message finalization. `agentLoop.ts` shared ownership of blocks and finalization with `streamHandler`, leading to the `initialBlocks` hack and conditional `!context.initialBlocks` branching. Two code paths diverged in `index.ts` (tools vs no-tools).
-
-v3 refactored the stream processing into five focused pieces with clear boundaries, no shared ownership, and a single code path.
-
-## Goals
-
-1. **Separate stream accumulation from side effects** — event handling is pure block mutation, flush is independent
-2. **Single loop, single code path** — no tools-vs-no-tools branching (zero tools = loop runs once)
-3. **Throttled flush as a utility** — reusable throttle function, not embedded timers
-4. **Each module does one thing** — accumulate, echo, dispatch, process, types
-5. **No `initialBlocks` hack** — the loop owns the state, passes it down, nobody round-trips it
-
-## Non-Goals
-
-- Changing the block data model or DB schema
-- Changing renderer event contracts (STREAM_EVENTS.RESPONSE/END/ERROR)
-- Changing the LLMCoreStreamEvent types or provider coreStream interface
-- Adding new features (permissions, retry, parallel tool exec)
-
-## Current Structure
-
-```
-agentRuntimePresenter/
- index.ts — session lifecycle only (init, destroy, getState, cancel, getMessages)
- process.ts — the loop: stream → accumulate → echo → dispatch
- accumulator.ts — accumulate(state, event): mutate blocks by event type
- echo.ts — start/stop throttled flush to renderer + DB
- dispatch.ts — executeTools() + finalize() + finalizeError()
- contextBuilder.ts — (unchanged from v1)
- messageStore.ts — (unchanged)
- sessionStore.ts — (unchanged)
- types.ts — StreamState, IoParams, ProcessParams
-
-src/shared/utils/
- throttle.ts — createThrottle(fn, interval): reusable throttle utility
-```
-
-## Architecture
-
-### Shared State
-
-All modules operate on a single mutable `StreamState` object owned by `process.ts`:
-
-```typescript
-// types.ts
-interface ToolCallResult {
- id: string
- name: string
- arguments: string
- serverName?: string
- serverIcons?: string
- serverDescription?: string
-}
-
-interface StreamState {
- blocks: AssistantMessageBlock[]
- metadata: MessageMetadata
- startTime: number
- firstTokenTime: number | null
- pendingToolCalls: Map
- completedToolCalls: ToolCallResult[]
- stopReason: 'complete' | 'tool_use' | 'error' | 'abort' | 'max_tokens'
- dirty: boolean
-}
-
-interface IoParams {
- sessionId: string
- messageId: string
- messageStore: DeepChatMessageStore
- abortSignal: AbortSignal
-}
-
-interface ProcessParams {
- io: IoParams
- coreStream: (...) => AsyncGenerator
- tools: MCPToolDefinition[]
- toolPresenter: IToolPresenter | null
- modelId: string
- modelConfig: ModelConfig
- temperature: number
- maxTokens: number
- messages: ChatMessage[]
-}
-```
-
-### Module Responsibilities
-
-#### `accumulator.ts` — event → block mutation
-
-Pure block mutation. No DB, no renderer, no control flow decisions.
-
-```typescript
-function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
-```
-
-| Event | Action |
-|---|---|
-| `text` | Append to current content block (coalesce consecutive) |
-| `reasoning` | Append to current reasoning_content block |
-| `tool_call_start` | Push new tool_call block, add to pendingToolCalls map |
-| `tool_call_chunk` | Append to pending args + update block params |
-| `tool_call_end` | Finalize args, move to completedToolCalls, remove from pending |
-| `usage` | Set metadata fields |
-| `stop` | Set stopReason |
-| `error` | Push error block (status `'error'`) |
-
-New blocks are always created with `status: 'pending'`. The accumulator never transitions existing blocks to `'success'` or `'error'` — that's `dispatch.ts`'s job. The only exception is the `error` event handler, which creates a *new* error block with `status: 'error'`.
-
-Sets `state.dirty = true` on any block mutation. Sets `state.firstTokenTime` on the first `text` or `reasoning` event if not already set.
-
-#### `echo.ts` — interval-based flush to renderer + DB
-
-Two `setInterval` timers drive periodic flushing. Each interval callback checks `state.dirty` before doing work. The shared `createThrottle` utility wraps each callback to prevent overlapping flushes if a write takes longer than the interval.
-
-```typescript
-interface EchoHandle {
- flush(): void // immediate flush (after tool results)
- stop(): void // clear intervals + cancel pending throttles
-}
-
-function startEcho(state: StreamState, io: IoParams): EchoHandle
-```
-
-- Renderer interval: 120ms — when `state.dirty`, emit `STREAM_EVENTS.RESPONSE` with deep-cloned blocks
-- DB interval: 600ms — when `state.dirty`, call `messageStore.updateAssistantContent()`
-- `flush()`: immediate renderer + DB write, clears `state.dirty`
-- `stop()`: clears both intervals, cancels pending throttles (called in `finally` block of process)
-
-#### `dispatch.ts` — tool execution + finalization
-
-```typescript
-async function executeTools(
- state: StreamState,
- conversation: ChatMessage[],
- prevBlockCount: number,
- tools: MCPToolDefinition[],
- toolPresenter: IToolPresenter,
- modelId: string,
- io: IoParams
-): Promise
-
-function finalize(state: StreamState, io: IoParams): void
-function finalizeError(state: StreamState, io: IoParams, error: unknown): void
-```
-
-Three independent functions in one module. They share no internal state — all coordination goes through `StreamState` and the arguments passed in.
-
-`executeTools` responsibilities:
-- Use `prevBlockCount` to slice `state.blocks` and extract only the current iteration's content/reasoning/tool_call blocks
-- Build assistant message from current iteration blocks (content + tool_calls)
-- Include `reasoning_content` for interleaved thinking models (deepseek-reasoner, kimi-k2-thinking, glm-4.7)
-- Push assistant message to conversation
-- For each tool call: check `abortSignal` → call `toolPresenter.callTool()` → push tool result to conversation → update tool_call block response + status
-- Enrich tool_call blocks with server info from tool definitions
-- Flush to renderer + DB after each tool execution
-- Returns the number of tools executed
-- Does NOT enforce MAX_TOOL_CALLS (that's the loop's job in `process.ts`)
-
-`finalize` responsibilities:
-- Mark all pending blocks as `'success'`
-- Compute metadata using `state.startTime` and `state.firstTokenTime` (generationTime, firstTokenTime, tokensPerSecond)
-- Call `messageStore.finalizeAssistantMessage()`
-- Emit `STREAM_EVENTS.RESPONSE` (final blocks) + `STREAM_EVENTS.END`
-
-`finalizeError` responsibilities:
-- Push error block
-- Mark all pending blocks as `'error'`
-- Call `messageStore.setMessageError()`
-- Emit `STREAM_EVENTS.ERROR`
-
-#### `process.ts` — the loop
-
-Single entry point, single code path. No tools-vs-no-tools branching.
-
-```typescript
-async function processStream(params: ProcessParams): Promise
-```
-
-```
-processStream(params)
- state = createState()
- conversation = [...params.messages]
- echo = startEcho(state, params.io)
- toolCallCount = 0
-
- try {
- LOOP:
- prevBlockCount = state.blocks.length
- stream = coreStream(conversation, ...)
- reset completedToolCalls + pendingToolCalls
-
- for await (event of stream):
- if aborted → mark blocks error, setMessageError, emit ERROR, return
- accumulate(state, event)
-
- if aborted → break LOOP
- if stopReason ≠ 'tool_use' → break LOOP
- if no completedToolCalls → break LOOP
- if toolCallCount + completedToolCalls > MAX_TOOL_CALLS → break LOOP
-
- executed = executeTools(state, conversation, prevBlockCount, ...)
- toolCallCount += executed
- echo.flush()
-
- if aborted → break LOOP
-
- finalize(state, params.io)
- catch (err):
- finalizeError(state, params.io, err)
- finally:
- echo.stop()
-```
-
-MAX_TOOL_CALLS = 128.
-
-#### `index.ts` — session lifecycle
-
-`processMessage` is thin: build context, resolve provider, construct `ProcessParams`, call `processStream`.
-
-```
-processMessage(sessionId, content)
- ├── resolve provider + model config
- ├── buildContext(...)
- ├── persist user message + create assistant placeholder
- ├── fetch tool definitions (no conversationId — new agent doesn't use skills)
- └── processStream(params) ← single call, no branching
-```
-
-### Throttle Utility
-
-```typescript
-// src/shared/utils/throttle.ts
-interface ThrottledFn {
- (): void // invoke (throttled — skips if called within interval of last execution)
- flush(): void // invoke immediately, reset interval
- cancel(): void // cancel pending invocation
-}
-
-function createThrottle(fn: () => void, interval: number): ThrottledFn
-```
-
-`echo.ts` uses `createThrottle` to wrap its flush callbacks, then drives them via `setInterval`. The throttle prevents overlapping flushes — if a DB write takes >600ms, the next interval tick is a no-op instead of stacking. Available for reuse elsewhere in the codebase.
-
-## Migration (completed)
-
-This was a pure refactor. External contracts unchanged:
-- `IAgentImplementation` interface: unchanged
-- Renderer events (`STREAM_EVENTS.RESPONSE/END/ERROR`): unchanged
-- DB schema: unchanged
-- Block format (`AssistantMessageBlock`): unchanged
-- `contextBuilder.ts`, `messageStore.ts`, `sessionStore.ts`: unchanged
-
-Files created: `throttle.ts`, `types.ts`, `accumulator.ts`, `echo.ts`, `dispatch.ts`, `process.ts`
-Files deleted: `streamHandler.ts`, `agentLoop.ts`
-Files modified: `index.ts`
-
-## Acceptance Criteria
-
-- [x] All existing behavior preserved (tool calling, reasoning, abort, error handling)
-- [x] No `initialBlocks` or conditional caller-detection in any module
-- [x] Single code path in `processMessage` (no tools-vs-no-tools branch)
-- [x] `streamHandler.ts` and `agentLoop.ts` deleted
-- [x] Throttle utility in `src/shared/utils/` and used by `echo.ts`
-- [x] All existing tests pass (updated for new module structure)
-- [x] `pnpm run typecheck` passes
-- [x] `pnpm run lint && pnpm run format` passes
diff --git a/docs/archives/new-ui-agent-session/spec.md b/docs/archives/new-ui-agent-session/spec.md
deleted file mode 100644
index 6c6f5b72e..000000000
--- a/docs/archives/new-ui-agent-session/spec.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Agent-Aware Session Creation
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Problem
-
-`NewThreadPage.onSubmit()` calls `sessionStore.createSession()` without passing `agentId`. The `createSession` action already supports `agentId` in `CreateSessionInput` and handles ACP agent mode, but it is never provided from the UI.
-
-## Solution
-
-Import `useAgentStore` in `NewThreadPage.vue` and pass the selected agent to `createSession()`.
-
-### Flow
-
-1. User selects an agent in the sidebar (sets `agentStore.selectedAgentId`)
-2. User opens NewThreadPage and types a message
-3. On submit, `NewThreadPage` reads `agentStore.selectedAgentId`
-4. Passes `agentId` to `sessionStore.createSession()`
-5. `createSession()` already handles ACP mode:
- - If `agentId !== 'deepchat'`: sets `chatMode: 'acp agent'`, `acpWorkdirMap: { [agentId]: projectDir }`
- - If `agentId === 'deepchat'` or undefined: standard DeepChat session
-
-### Key Types
-
-- `CreateSessionInput.agentId?: string` — from `stores/ui/session.ts`
-- `agentStore.selectedAgentId: string | null` — `null` means "All Agents" filter (default to 'deepchat')
-
-### Files Modified
-
-- `src/renderer/src/pages/NewThreadPage.vue` — add `useAgentStore` import, pass `agentId` and `providerId`/`modelId` for ACP agents
diff --git a/docs/archives/new-ui-agent-store/spec.md b/docs/archives/new-ui-agent-store/spec.md
deleted file mode 100644
index 421f5c893..000000000
--- a/docs/archives/new-ui-agent-store/spec.md
+++ /dev/null
@@ -1,158 +0,0 @@
-# Agent Store Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-Agent Store manages the agent list (DeepChat built-in + ACP agents) and the sidebar agent filter selection.
-
-## File Location
-
-`src/renderer/src/stores/ui/agent.ts`
-
-## Type Definitions
-
-```typescript
-interface UIAgent {
- id: string // 'deepchat', 'claude-code-acp', 'codex-acp', custom id
- name: string // Display name
- type: 'deepchat' | 'builtin-acp' | 'custom-acp'
- enabled: boolean
-}
-```
-
-### Mapping from Presenter Types
-
-DeepChat agent is hardcoded. ACP agents come from two presenter calls:
-
-```typescript
-// Built-in ACP agents (claude-code, codex, kimi)
-const builtinAgents: AcpBuiltinAgent[] = await configPresenter.getAcpBuiltinAgents()
-
-// Custom ACP agents (user-defined)
-const customAgents: AcpCustomAgent[] = await configPresenter.getAcpCustomAgents()
-```
-
-Mapping:
-
-```typescript
-function mapBuiltinAgent(agent: AcpBuiltinAgent): UIAgent {
- return {
- id: agent.id,
- name: agent.name,
- type: 'builtin-acp',
- enabled: agent.enabled
- }
-}
-
-function mapCustomAgent(agent: AcpCustomAgent): UIAgent {
- return {
- id: agent.id,
- name: agent.name,
- type: 'custom-acp',
- enabled: agent.enabled
- }
-}
-```
-
-## Store Design
-
-```typescript
-export const useAgentStore = defineStore('agent', () => {
- const configPresenter = useLegacyPresenter('configPresenter')
-
- // --- State ---
- const agents = ref([])
- const selectedAgentId = ref(null) // null = "All Agents"
- const loading = ref(false)
- const error = ref(null)
-
- // --- Getters ---
- const enabledAgents = computed(() => agents.value.filter(a => a.enabled))
- const selectedAgent = computed(() => agents.value.find(a => a.id === selectedAgentId.value))
- const selectedAgentName = computed(() => selectedAgent.value?.name ?? 'All Agents')
-
- // --- Actions ---
- async function fetchAgents(): Promise
- function selectAgent(id: string | null): void
-
- return {
- agents, selectedAgentId, loading, error,
- enabledAgents, selectedAgent, selectedAgentName,
- fetchAgents, selectAgent
- }
-})
-```
-
-## Actions
-
-### `fetchAgents(): Promise`
-
-```typescript
-async function fetchAgents() {
- loading.value = true
- error.value = null
- try {
- const deepchatAgent: UIAgent = {
- id: 'deepchat',
- name: 'DeepChat',
- type: 'deepchat',
- enabled: true // Always enabled
- }
-
- const builtinAgents = await configPresenter.getAcpBuiltinAgents()
- const customAgents = await configPresenter.getAcpCustomAgents()
-
- agents.value = [
- deepchatAgent,
- ...builtinAgents.map(mapBuiltinAgent),
- ...customAgents.map(mapCustomAgent)
- ]
- } catch (e) {
- error.value = `Failed to load agents: ${e}`
- } finally {
- loading.value = false
- }
-}
-```
-
-### `selectAgent(id: string | null): void`
-
-Toggle agent filter. Passing the currently selected id deselects it (back to "All").
-
-```typescript
-function selectAgent(id: string | null) {
- selectedAgentId.value = selectedAgentId.value === id ? null : id
-}
-```
-
-## IPC Call Mapping
-
-| Action | Presenter Call |
-|--------|---------------|
-| Get built-in ACP agents | `configPresenter.getAcpBuiltinAgents()` |
-| Get custom ACP agents | `configPresenter.getAcpCustomAgents()` |
-
-## Event Listeners
-
-| Event | Handler |
-|-------|---------|
-| `CONFIG_EVENTS.SETTING_CHANGED` | Re-fetch agents (ACP config may have changed) |
-
-Note: There are no dedicated ACP_EVENTS.AGENT_ADDED/REMOVED events in the codebase. Agent changes propagate through `CONFIG_EVENTS.SETTING_CHANGED`.
-
-## Error Handling
-
-Errors are caught in `fetchAgents` and stored in `error` ref. The DeepChat agent is always present even if ACP agent fetching fails.
-
-## Test Points
-
-1. DeepChat agent is always present in agent list
-2. Built-in ACP agents are mapped correctly from `getAcpBuiltinAgents()`
-3. Custom ACP agents are mapped correctly from `getAcpCustomAgents()`
-4. Disabled agents appear in list but `enabledAgents` filters them out
-5. `selectAgent` toggles selection (same id deselects)
-6. `selectedAgentName` returns 'All Agents' when nothing selected
-7. Error during fetch sets `error` and still includes DeepChat agent
-
diff --git a/docs/archives/new-ui-chat-components/spec.md b/docs/archives/new-ui-chat-components/spec.md
deleted file mode 100644
index 9af0cf90e..000000000
--- a/docs/archives/new-ui-chat-components/spec.md
+++ /dev/null
@@ -1,217 +0,0 @@
-# Chat Components Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-Chat components handle message display, input, and status configuration during active sessions. Each component's visual design must match its mock counterpart exactly.
-
-## Historical Reference Map
-
-| Component | Historical Mock |
-|-----------|-----------|
-| ChatTopBar | `MockTopBar` |
-| MessageList | `MockMessageList` |
-| InputBox | `MockInputBox` |
-| InputToolbar | `MockInputToolbar` |
-| StatusBar | `MockStatusBar` |
-
-## File Locations
-
-```
-src/renderer/src/components/chat/
- ChatTopBar.vue
- MessageList.vue
- ChatInputBox.vue
- ChatInputToolbar.vue
- ChatStatusBar.vue
-```
-
----
-
-## 1. ChatTopBar
-
-**Historical mock reference**: `MockTopBar` (removed from repo)
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ [folder] project-name > Session Title [Share][...]│
-└─────────────────────────────────────────────────────┘
-```
-
-**Props**:
-```typescript
-interface Props {
- title: string
- project: string
-}
-```
-
-**Data flow**: Props passed from ChatPage, which reads from `sessionStore.activeSession`.
-
-**Key behavior**:
-- `projectName` computed as `project.split('/').pop()`
-- Sticky positioning: `sticky top-0 z-10`
-- Window drag region with no-drag on buttons
-
----
-
-## 2. MessageList
-
-**Historical mock reference**: `MockMessageList` (removed from repo)
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ [User message - right-aligned] │
-│ │
-│ [Avatar] [Assistant message] │
-│ [Model name label] │
-│ [Message content] │
-└─────────────────────────────────────────────────────┘
-```
-
-**Data flow**: Reads messages from the existing `useChatStore()`.
-
-```typescript
-const chatStore = useChatStore()
-const messages = computed(() => chatStore.getCurrentThreadMessages())
-```
-
-**IPC call mapping**:
-
-| Operation | Presenter Call |
-|-----------|---------------|
-| Load messages | `sessionPresenter.getMessages(conversationId, page, pageSize)` |
-| Load message IDs | `sessionPresenter.getMessageIds(conversationId)` |
-
-Note: The existing `useChatStore` already handles message fetching and caching via `STREAM_EVENTS.RESPONSE` and `STREAM_EVENTS.END`. The new MessageList component consumes from that store.
-
-**Key behavior**:
-- User messages: right-aligned, `bg-muted rounded-2xl`
-- Assistant messages: left-aligned with model icon, model name label
-- Container: `max-w-3xl mx-auto px-4 py-6 space-y-6`
-- Scrollable overflow
-
----
-
-## 3. ChatInputBox
-
-**Historical mock reference**: `MockInputBox` (removed from repo)
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ Ask DeepChat anything, @ to mention files... │
-│ │
-├─────────────────────────────────────────────────────┤
-│ [toolbar slot] │
-└─────────────────────────────────────────────────────┘
-```
-
-**Props/Events**:
-```typescript
-interface Props {
- modelValue?: string
- placeholder?: string
-}
-
-interface Emits {
- (e: 'update:modelValue', value: string): void
- (e: 'submit', message: string): void
-}
-```
-
-**Slots**:
-- `toolbar`: Slot for InputToolbar
-
-**Key behavior**:
-- Textarea with `min-h-[80px] resize-none border-0 bg-transparent`
-- Container: `rounded-xl border bg-card/30 backdrop-blur-lg shadow-sm`
-- Enter to submit (shift+enter for newline)
-
----
-
-## 4. ChatInputToolbar
-
-**Historical mock reference**: `MockInputToolbar` (removed from repo)
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ [+] [mic] [send] │
-└─────────────────────────────────────────────────────┘
-```
-
-**Events**:
-```typescript
-interface Emits {
- (e: 'send'): void
- (e: 'attach'): void
-}
-```
-
-**Key behavior**:
-- Attach button: `lucide:plus` icon, ghost variant
-- Mic button: `lucide:mic` icon, ghost variant
-- Send button: `lucide:arrow-up` icon, `rounded-full`, primary style
-
----
-
-## 5. ChatStatusBar
-
-**Historical mock reference**: `MockStatusBar` (removed from repo)
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ [ModelIcon Model ▼] [Effort ▼] [Permissions ▼] │
-└─────────────────────────────────────────────────────┘
-```
-
-**Data flow**:
-
-| Selector | Read from | Write to |
-|----------|-----------|----------|
-| Model | `chatStore.chatConfig.modelId` + `modelStore` for list | `sessionPresenter.updateSessionSettings()` |
-| Effort (reasoningEffort) | `chatStore.chatConfig.reasoningEffort` | `sessionPresenter.updateSessionSettings()` |
-| Permissions | TBD (permission system) | TBD |
-
-**IPC call mapping**:
-
-| Operation | Presenter Call |
-|-----------|---------------|
-| Change model | `sessionPresenter.updateSessionSettings(sessionId, { providerId, modelId })` |
-| Change effort | `sessionPresenter.updateSessionSettings(sessionId, { reasoningEffort })` |
-| Get model list | `configPresenter.getProviderDb()` or existing `modelStore` |
-
-**Key behavior**:
-- All buttons: `h-6 px-2 gap-1 text-xs text-muted-foreground`
-- Model selector shows ModelIcon + model name + chevron-down
-- Effort selector shows gauge icon + level + chevron-down
-- Permissions selector shows shield icon + level name
-
----
-
-## Shared Input Flow (NewThread + Chat)
-
-Both NewThreadPage and ChatPage use the same InputBox + InputToolbar + StatusBar combination. The submit behavior differs:
-
-| Context | On Submit |
-|---------|-----------|
-| NewThreadPage | `sessionStore.createSession({ title, message, projectDir, ... })` |
-| ChatPage | `agentPresenter.chat(sessionId, message, tabId)` |
-
-The parent page component handles the submit event and dispatches accordingly.
-
-## Test Points
-
-1. ChatTopBar displays project name and title correctly
-2. MessageList renders user messages right-aligned and assistant messages left-aligned
-3. ChatInputBox emits `submit` on Enter, `update:modelValue` on typing
-4. ChatInputToolbar emits `send` on send button click
-5. ChatStatusBar model dropdown shows available models
-6. ChatStatusBar effort dropdown shows effort levels
-7. All component styles match their mock counterparts exactly
diff --git a/docs/archives/new-ui-implementation/todo.md b/docs/archives/new-ui-implementation/todo.md
deleted file mode 100644
index 950e88c26..000000000
--- a/docs/archives/new-ui-implementation/todo.md
+++ /dev/null
@@ -1,314 +0,0 @@
-# New UI Implementation Development Tracker
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-This document tracks the development progress of new UI feature implementation. All implementations must match the mock interface exactly.
-
-**Architecture Design**: [new-ui-implementation-plan.md](../../architecture/new-ui-implementation-plan.md)
-
-## Historical Reference Map
-
-| Historical Mock | Purpose | Target Replacement |
-|-----------|---------|-------------------|
-| `MockWelcomePage` | Welcome page | `pages/WelcomePage.vue` |
-| `NewThreadMock` | NewThread page | `pages/NewThreadPage.vue` |
-| `MockChatPage` | Chat page | `pages/ChatPage.vue` |
-| `MockTopBar` | Top bar | `components/chat/ChatTopBar.vue` |
-| `MockMessageList` | Message list | `components/chat/MessageList.vue` |
-| `MockInputBox` | Input box | `components/chat/ChatInputBox.vue` |
-| `MockInputToolbar` | Input toolbar | `components/chat/ChatInputToolbar.vue` |
-| `MockStatusBar` | Status bar | `components/chat/ChatStatusBar.vue` |
-| `components/WindowSideBar.vue` | Sidebar | (refactored in place) |
-| `useMockViewState` | State management | Replaced by stores |
-
----
-
-## Specs List
-
-| Spec | File |
-|------|------|
-| Page Router | [spec.md](../new-ui-page-state/spec.md) |
-| Agent Store | [spec.md](../new-ui-agent-store/spec.md) |
-| Session Store | [spec.md](../new-ui-session-store/spec.md) |
-| Project Store | [spec.md](../new-ui-project-store/spec.md) |
-| Sidebar Components | [spec.md](../new-ui-sidebar/spec.md) |
-| Chat Components | [spec.md](../new-ui-chat-components/spec.md) |
-| Page Components | [spec.md](../new-ui-pages/spec.md) |
-| Agent-Aware Sessions | [spec.md](../new-ui-agent-session/spec.md) |
-| Status Bar (Model/Effort) | [spec.md](../new-ui-status-bar/spec.md) |
-| Markdown Rendering | [spec.md](../new-ui-markdown-rendering/spec.md) |
-
----
-
-## Phase 1: Store Layer
-
-### 1.1 Page Router Store
-
-- [x] Create `stores/ui/pageRouter.ts`
-- [x] Define `PageRoute` type (welcome / newThread / chat)
-- [x] Implement `initialize()` — check providers, check active session
-- [x] Implement `goToWelcome()`, `goToNewThread()`, `goToChat(sessionId)`
-- [x] Implement `currentRoute` and `chatSessionId` getters
-- [x] Listen to `CONFIG_EVENTS.PROVIDER_CHANGED`
-- [x] Error handling with fallback to newThread
-
-### 1.2 Session Store
-
-- [x] Create `stores/ui/session.ts`
-- [x] Define `UISession` type with `resolveAgentId()` mapping
-- [x] Define `mapSessionStatus()` mapping
-- [x] Implement `fetchSessions()` via `sessionPresenter.getSessionList()`
-- [x] Implement `createSession()` — create session + send message + navigate
-- [x] Implement `selectSession()` — activate session + navigate
-- [x] Implement `closeSession()` — unbind tab + navigate to newThread
-- [x] Implement `groupByTime()` and `groupByProject()` grouping
-- [x] Implement `getFilteredGroups(agentId)` for sidebar
-- [x] Implement `toggleGroupMode()`
-- [x] Listen to `CONVERSATION_EVENTS.LIST_UPDATED`, `ACTIVATED`, `DEACTIVATED`
-- [x] Error handling on all async actions
-
-### 1.3 Agent Store
-
-- [x] Create `stores/ui/agent.ts`
-- [x] Define `UIAgent` type
-- [x] Implement `fetchAgents()` — DeepChat + `getAcpBuiltinAgents()` + `getAcpCustomAgents()`
-- [x] Implement `selectAgent(id)` toggle
-- [x] Implement `enabledAgents`, `selectedAgent`, `selectedAgentName` getters
-- [x] Listen to `CONFIG_EVENTS.SETTING_CHANGED`
-- [x] Error handling
-
-### 1.4 Project Store
-
-- [x] Create `stores/ui/project.ts`
-- [x] Define `UIProject` type
-- [x] Implement `deriveFromSessions(sessions)` — aggregate unique projects
-- [x] Implement `selectProject(path)`
-- [x] Implement `openFolderPicker()` via `devicePresenter.selectDirectory()`
-- [x] Error handling
-
-**Acceptance Criteria**:
-- [x] All stores manage state correctly
-- [x] IPC calls map to correct presenter methods
-- [x] Errors are caught and exposed via `error` ref
-- [ ] Unit tests pass
-
----
-
-## Phase 2: Page Components
-
-### 2.1 ChatTabView Refactor
-
-- [x] Remove all legacy ChatView/NewThread/Mock imports
-- [x] Remove `useMockViewState` usage
-- [x] Route based on `pageRouter.currentRoute`
-- [x] Initialize stores on mount (parallel)
-- [x] Remove ArtifactDialog margin calculation (move to ChatPage if needed)
-
-### 2.2 WelcomePage
-
-- [x] Create `pages/WelcomePage.vue`
-- [x] Copy exact layout/classes from `MockWelcomePage.vue`
-- [x] Static provider grid (6 items)
-- [x] All clicks → `windowPresenter.openOrFocusSettingsTab()`
-- [x] Window drag region support
-
-### 2.3 NewThreadPage
-
-- [x] Create `pages/NewThreadPage.vue`
-- [x] Copy exact layout/classes from `NewThreadMock.vue`
-- [x] Project selector from `projectStore`
-- [x] Integrate ChatInputBox + ChatInputToolbar
-- [x] Integrate ChatStatusBar
-- [x] Submit → `sessionStore.createSession()`
-
-### 2.4 ChatPage
-
-- [x] Create `pages/ChatPage.vue`
-- [x] Copy exact layout/classes from `MockChatPage.vue`
-- [x] Props: `sessionId`
-- [x] Read session data from `sessionStore.activeSession`
-- [x] Integrate ChatTopBar, MessageList, ChatInputBox, ChatInputToolbar, ChatStatusBar
-- [x] Submit → `agentPresenter.sendMessage()`
-
-**Acceptance Criteria**:
-- [x] Page layouts match mocks exactly
-- [x] Page routing works correctly
-- [x] No fallback to legacy ChatView
-
----
-
-## Phase 3: Chat Components
-
-### 3.1 ChatTopBar
-
-- [x] Create `components/chat/ChatTopBar.vue`
-- [x] Copy exact layout/classes from `MockTopBar.vue`
-- [x] Props: `title`, `project`
-- [x] Computed: `projectName`
-- [x] Share + More buttons (placeholder actions)
-
-### 3.2 MessageList
-
-- [x] Create `components/chat/MessageList.vue`
-- [x] Copy exact layout/classes from `MockMessageList.vue`
-- [x] Read messages via props (parent provides from store)
-- [x] User message style: right-aligned, `bg-muted rounded-2xl`
-- [x] Assistant message style: left with avatar + model label
-
-### 3.3 ChatInputBox
-
-- [x] Create `components/chat/ChatInputBox.vue`
-- [x] Copy exact layout/classes from `MockInputBox.vue`
-- [x] v-model support
-- [x] Toolbar slot
-- [x] Enter to submit, Shift+Enter for newline
-
-### 3.4 ChatInputToolbar
-
-- [x] Create `components/chat/ChatInputToolbar.vue`
-- [x] Copy exact layout/classes from `MockInputToolbar.vue`
-- [x] Attach (+), Mic, Send buttons
-- [x] Emit events: `send`, `attach`
-
-### 3.5 ChatStatusBar
-
-- [x] Create `components/chat/ChatStatusBar.vue`
-- [x] Copy exact layout/classes from `MockStatusBar.vue`
-- [x] Model selector → placeholder with mock data (real integration deferred)
-- [x] Effort selector → placeholder with mock data
-- [x] Permissions selector → placeholder for now
-
-**Acceptance Criteria**:
-- [x] All component styles match mocks exactly
-- [x] Components emit correct events
-- [ ] StatusBar dropdowns read/write session settings (deferred — uses placeholder data)
-
----
-
-## Phase 4: Sidebar Data Integration
-
-### 4.1 Replace Mock Data
-
-- [x] `mockAgents` → `agentStore.enabledAgents`
-- [x] `allSessions` / `mockSessionsByDate` → `sessionStore.getFilteredGroups()`
-- [x] `selectedAgentId` → `agentStore.selectedAgentId`
-- [x] `groupByProject` → `sessionStore.groupMode`
-
-### 4.2 Replace Mock State
-
-- [x] Remove `useMockViewState` import
-- [x] `handleNewChat` → `sessionStore.closeSession()`
-- [x] `handleSessionClick` → `sessionStore.selectSession(id)`
-- [x] Remove debug toggle (welcome page toggle button)
-
-### 4.3 Sidebar-Specific
-
-- [x] Keep `collapsed` as local state
-- [x] `filteredGroups` computed from `sessionStore.getFilteredGroups(agentStore.selectedAgentId)`
-
-**Acceptance Criteria**:
-- [x] Sidebar displays real session data from stores
-- [x] Agent filter works end-to-end
-- [x] Time/project grouping toggle works
-- [x] Session click navigates to chat page
-
----
-
-## Phase 5: Integration and Cleanup
-
-### 5.1 End-to-End Flows
-
-- [ ] Full session creation flow: NewThread → submit → ChatPage renders with messages
-- [ ] Session switching: sidebar click → ChatPage updates
-- [ ] New chat: sidebar + button → NewThreadPage
-- [ ] Welcome → Settings → Provider added → NewThreadPage
-
-### 5.2 Error Handling
-
-- [ ] Display `sessionStore.error` in UI
-- [ ] Display `agentStore.error` in UI
-- [ ] Handle presenter call failures gracefully
-
-### 5.3 Performance
-
-- [ ] Lazy load page components in ChatTabView
-- [ ] Virtual scrolling for message list (if needed)
-
-### 5.4 Internationalization
-
-- [ ] Add i18n keys for all user-facing strings
-
-### 5.5 Cleanup
-
-- [x] Remove `components/mock/MockWelcomePage.vue` from the active tree
-- [x] Remove `components/mock/MockChatPage.vue` from the active tree
-- [x] Remove `components/mock/MockTopBar.vue` from the active tree
-- [x] Remove `components/mock/MockMessageList.vue` from the active tree
-- [x] Remove `components/mock/MockInputBox.vue` from the active tree
-- [x] Remove `components/mock/MockInputToolbar.vue` from the active tree
-- [x] Remove `components/mock/MockStatusBar.vue` from the active tree
-- [x] Remove `components/NewThreadMock.vue` from the active tree
-- [x] Remove `composables/useMockViewState.ts` from the active tree
-
----
-
-## Test Coverage
-
-| Module | Status |
-|--------|--------|
-| Page Router Store | ⬜ |
-| Session Store | ⬜ |
-| Agent Store | ⬜ |
-| Project Store | ⬜ |
-| ChatTabView routing | ⬜ |
-| WelcomePage | ⬜ |
-| NewThreadPage | ⬜ |
-| ChatPage | ⬜ |
-| ChatTopBar | ⬜ |
-| MessageList | ⬜ |
-| ChatInputBox | ⬜ |
-| ChatStatusBar | ⬜ |
-| Sidebar integration | ⬜ |
-
----
-
-## Phase 6: Agent-Aware Sessions, Working Settings, Markdown Rendering
-
-### 6.1 Agent-Aware Session Creation ([spec](../new-ui-agent-session/spec.md))
-
-- [x] Import `useAgentStore` in `NewThreadPage.vue`
-- [x] Pass `agentId: agentStore.selectedAgentId ?? 'deepchat'` to `sessionStore.createSession()`
-- [x] Pass `providerId: 'acp'` and `modelId: agentId` when ACP agent selected
-- [ ] Verify: select ACP agent → create session → session has `chatMode: 'acp agent'`
-
-### 6.2 Working ChatStatusBar ([spec](../new-ui-status-bar/spec.md))
-
-- [x] Replace hardcoded model dropdown with `modelStore.enabledModels` data
-- [x] Display current model from `chatStore.chatConfig.modelId` via `modelStore.findModelByIdOrName()`
-- [x] On model select: `chatStore.updateChatConfig({ providerId, modelId })`
-- [x] Replace hardcoded effort dropdown with real `reasoningEffort` / `verbosity` data
-- [x] On effort select: `chatStore.updateChatConfig({ reasoningEffort: value })`
-- [x] Keep permissions as read-only indicator
-- [x] Add `modelStore.initialize()` to `ChatTabView.onMounted`
-- [ ] Verify: model dropdown shows real enabled models
-- [ ] Verify: changing model updates `chatConfig` and persists
-
-### 6.3 Markdown Message Rendering ([spec](../new-ui-markdown-rendering/spec.md))
-
-- [x] Replace plain-text rendering with `MessageItemAssistant` / `MessageItemUser`
-- [x] Remove `getUserText()`, `getAssistantText()` helpers
-- [x] Remove direct `ModelIcon` and `useThemeStore` imports (handled by message components)
-- [ ] Verify: messages render with full markdown, code blocks, tool calls, etc.
-
----
-
-## Changelog
-
-| Date | Update |
-|------|--------|
-| 2025-02-18 | v2: Rewrote all specs — page router decoupled from session, IPC mapping added, error handling, no legacy fallback |
-| 2026-02-18 | v3: Phase 1–4 implemented — 4 stores, 5 chat components, 3 pages, ChatTabView refactored, sidebar integrated with stores. Typecheck/lint/format pass. Phase 5 (E2E testing, error display, i18n, mock cleanup) pending. |
-| 2026-02-18 | v4: Phase 6 implemented — agent-aware session creation, working ChatStatusBar (model/effort selectors wired to chatStore + modelStore), markdown rendering via existing MessageItemAssistant/MessageItemUser components. |
diff --git a/docs/archives/new-ui-markdown-rendering/spec.md b/docs/archives/new-ui-markdown-rendering/spec.md
deleted file mode 100644
index 595175782..000000000
--- a/docs/archives/new-ui-markdown-rendering/spec.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Markdown Message Rendering
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Problem
-
-`MessageList.vue` manually extracts text with `getAssistantText()` and renders it in a plain `` with `whitespace-pre-wrap`. The existing codebase has a full rendering pipeline that handles markdown, code blocks, tool calls, thinking blocks, permissions, images, and more.
-
-## Solution
-
-Replace the plain-text rendering with existing message components:
-
-- `MessageItemAssistant.vue` — renders all assistant block types (content/markdown, reasoning, tool_call, permission, image, audio, error, plan, question)
-- `MessageItemUser.vue` — renders user content with file attachments, edit support, structured content rendering
-
-## Component Props
-
-- `MessageItemAssistant`: `:message="(msg as AssistantMessage)"`, `:is-capturing-image="false"`
-- `MessageItemUser`: `:message="(msg as UserMessage)"`
-
-Both work with `useChatStore` internally for `getActiveThreadId()`, variant maps, etc. Since `ChatPage` already activates the session in `chatStore`, these should work.
-
-## Type References
-
-From `@shared/chat`:
-- `Message` — union type with `role: 'user' | 'assistant'`
-- `UserMessage` — Message with `role: 'user'`, `content: UserMessageContent`
-- `AssistantMessage` — Message with `role: 'assistant'`, `content: AssistantMessageBlock[]`
-
-## Rendering Capabilities Unlocked
-
-- Markdown formatting (headers, lists, bold, italic, links)
-- Syntax-highlighted code blocks
-- Thinking/reasoning blocks (collapsible)
-- Tool call blocks with results
-- Permission request blocks
-- Image and audio blocks
-- Error blocks
-- Variant navigation
-- Message toolbar (copy, retry, delete)
-
-## Files Modified
-
-- `src/renderer/src/components/chat/MessageList.vue` — rewrite to use existing message components
diff --git a/docs/archives/new-ui-page-state/spec.md b/docs/archives/new-ui-page-state/spec.md
deleted file mode 100644
index 7edf607c2..000000000
--- a/docs/archives/new-ui-page-state/spec.md
+++ /dev/null
@@ -1,140 +0,0 @@
-# Page Router Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-The page router controls which page is displayed in the main content area. It is a pure routing mechanism — it holds no session data, no titles, no project paths. Those belong to the Session Store.
-
-## File Location
-
-`src/renderer/src/stores/ui/pageRouter.ts`
-
-## Route Definitions
-
-```typescript
-type PageRoute =
- | { name: 'welcome' }
- | { name: 'newThread' }
- | { name: 'chat'; sessionId: string }
-```
-
-Three routes, each with clear entry/exit conditions:
-
-| Route | Condition |
-|-------|-----------|
-| `welcome` | No enabled providers configured |
-| `newThread` | Providers exist, no active session |
-| `chat` | Active session selected |
-
-## Store Design
-
-```typescript
-export const usePageRouterStore = defineStore('pageRouter', () => {
- const route = ref
({ name: 'newThread' })
- const error = ref(null)
-
- // --- Actions ---
-
- async function initialize(): Promise
- function goToWelcome(): void
- function goToNewThread(): void
- function goToChat(sessionId: string): void
-
- // --- Getters ---
-
- const currentRoute = computed(() => route.value.name)
- const chatSessionId = computed(() =>
- route.value.name === 'chat' ? route.value.sessionId : null
- )
-
- return { route, error, initialize, goToWelcome, goToNewThread, goToChat, currentRoute, chatSessionId }
-})
-```
-
-## Actions
-
-### `initialize(): Promise`
-
-Called once on ChatTabView mount. Determines the initial route.
-
-```typescript
-async function initialize() {
- try {
- // 1. Check if any provider is enabled
- const hasProviders = await configPresenter.hasEnabledProviders()
- if (!hasProviders) {
- route.value = { name: 'welcome' }
- return
- }
-
- // 2. Check for active session on this tab
- const tabId = window.api.getWebContentsId()
- const activeSession = await sessionPresenter.getActiveSession(tabId)
- if (activeSession) {
- route.value = { name: 'chat', sessionId: activeSession.sessionId }
- return
- }
-
- // 3. Default to new thread
- route.value = { name: 'newThread' }
- } catch (e) {
- error.value = String(e)
- route.value = { name: 'newThread' }
- }
-}
-```
-
-### `goToWelcome(): void`
-
-```typescript
-function goToWelcome() {
- route.value = { name: 'welcome' }
-}
-```
-
-### `goToNewThread(): void`
-
-```typescript
-function goToNewThread() {
- route.value = { name: 'newThread' }
-}
-```
-
-### `goToChat(sessionId: string): void`
-
-```typescript
-function goToChat(sessionId: string) {
- route.value = { name: 'chat', sessionId }
-}
-```
-
-## IPC Call Mapping
-
-| Action | Presenter Call |
-|--------|---------------|
-| Check providers | `configPresenter.hasEnabledProviders()` (or check provider list length) |
-| Get active session | `sessionPresenter.getActiveSession(tabId)` |
-
-## Event Listeners
-
-| Event | Handler |
-|-------|---------|
-| `CONFIG_EVENTS.PROVIDER_CHANGED` | Re-check provider state; if none left → `goToWelcome()` |
-
-## Relationship to Other Stores
-
-- **Page Router does NOT read session titles, project paths, or any session detail.** That data is consumed directly by components from the Session Store.
-- **Session Store calls `pageRouter.goToChat(id)`** after creating or selecting a session.
-- **Session Store calls `pageRouter.goToNewThread()`** after closing a session.
-
-## Test Points
-
-1. `initialize()` routes to `welcome` when no providers exist
-2. `initialize()` routes to `chat` when an active session exists on this tab
-3. `initialize()` routes to `newThread` as default fallback
-4. `goToChat` sets route with correct sessionId
-5. `goToNewThread` clears route to newThread
-6. `goToWelcome` sets route to welcome
-7. Error during `initialize()` falls back to `newThread`
diff --git a/docs/archives/new-ui-pages/spec.md b/docs/archives/new-ui-pages/spec.md
deleted file mode 100644
index fa4738571..000000000
--- a/docs/archives/new-ui-pages/spec.md
+++ /dev/null
@@ -1,275 +0,0 @@
-# Page Components Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-Three page components driven by the Page Router. No fallback to old ChatView — this is a full replacement.
-
-## Historical Reference Map
-
-| Page | Historical Mock |
-|------|-----------|
-| WelcomePage | `MockWelcomePage` |
-| NewThreadPage | `NewThreadMock` |
-| ChatPage | `MockChatPage` |
-
-## File Locations
-
-```
-src/renderer/src/pages/
- WelcomePage.vue
- NewThreadPage.vue
- ChatPage.vue
-```
-
----
-
-## 1. ChatTabView.vue (Refactored)
-
-**File**: `src/renderer/src/views/ChatTabView.vue`
-
-Page entry. Routes to the correct page based on Page Router state. No legacy ChatView fallback.
-
-```vue
-
-
-
-
-
-```
-
-**IPC calls on mount**:
-
-| Call | Purpose |
-|------|---------|
-| `pageRouter.initialize()` | Determine initial route |
-| `sessionStore.fetchSessions()` | Load session list for sidebar |
-| `agentStore.fetchAgents()` | Load agent list for sidebar |
-
----
-
-## 2. WelcomePage
-
-**Historical mock reference**: `MockWelcomePage` (removed from repo)
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ [Logo 16x16] │
-│ │
-│ Welcome to DeepChat Agent │
-│ Connect a model provider to start build │
-│ │
-│ ┌───────┐ ┌───────┐ ┌───────┐ │
-│ │Claude │ │OpenAI │ │DeepSeek│ │
-│ └───────┘ └───────┘ └───────┘ │
-│ ┌───────┐ ┌───────┐ ┌────────┐ │
-│ │Gemini │ │Ollama │ │OpenRouter│ │
-│ └───────┘ └───────┘ └────────┘ │
-│ │
-│ Browse all providers... │
-│ │
-│ ─────────── or connect an agent ─────────── │
-│ │
-│ ┌─────────────────────────────────────────────┐ │
-│ │ [Terminal] Set up an ACP agent │ │
-│ │ Claude Code, Codex, Kimi... │ │
-│ └─────────────────────────────────────────────┘ │
-└─────────────────────────────────────────────────────┘
-```
-
-**Data**: Static provider list (hardcoded, matching mock).
-
-**IPC**:
-
-| Action | Presenter Call |
-|--------|---------------|
-| Click any provider / "Browse all" / ACP agent | `windowPresenter.openOrFocusSettingsTab(windowId)` |
-
-**Key classes** (from mock):
-- Container: `h-full w-full flex flex-col window-drag-region`
-- Content: `flex-1 flex flex-col items-center justify-center px-6`
-- Logo: `w-16 h-16`
-- Title: `text-3xl font-semibold text-foreground mb-2`
-- Subtitle: `text-sm text-muted-foreground text-center max-w-md mb-10`
-- Provider grid: `grid grid-cols-3 gap-2 w-full max-w-sm mb-4`
-- Provider button: `rounded-xl border border-border/60 bg-card/40 px-3 py-4 hover:bg-accent/50`
-
----
-
-## 3. NewThreadPage
-
-**Historical mock reference**: `NewThreadMock` (removed from repo)
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ [Logo 14x14] │
-│ │
-│ Build and explore │
-│ │
-│ [folder deepchat v] │
-│ ┌─────────────────────────────────┐ │
-│ │ Ask DeepChat anything... │ │
-│ │ │ │
-│ ├─────────────────────────────────┤ │
-│ │ [+] [mic][send] │ │
-│ └─────────────────────────────────┘ │
-│ │
-│ [Model v] [Effort v] [Permissions v] │
-└─────────────────────────────────────────────────────┘
-```
-
-**Data sources**:
-
-| UI Element | Store |
-|------------|-------|
-| Project list | `projectStore.projects` |
-| Selected project | `projectStore.selectedProjectName` |
-| Model/Effort | `chatStore.chatConfig` or defaults from `configPresenter` |
-
-**Submit flow**:
-
-```typescript
-const handleSubmit = (message: string) => {
- sessionStore.createSession({
- title: message.slice(0, 50),
- message,
- projectDir: projectStore.selectedProject?.path,
- providerId: selectedProviderId.value,
- modelId: selectedModelId.value,
- reasoningEffort: selectedEffort.value
- })
-}
-```
-
-**IPC**:
-
-| Action | Presenter Call |
-|--------|---------------|
-| Open folder | `filePresenter.selectDirectory()` (via projectStore) |
-| Submit message | `sessionStore.createSession()` → `sessionPresenter.createSession()` + `agentPresenter.chat()` |
-
----
-
-## 4. ChatPage
-
-**Historical mock reference**: `MockChatPage` (removed from repo)
-
-**Props**:
-```typescript
-interface Props {
- sessionId: string
-}
-```
-
-**Layout**:
-```
-┌─────────────────────────────────────────────────────┐
-│ ChatTopBar (sticky top) │
-├─────────────────────────────────────────────────────┤
-│ │
-│ MessageList (scroll) │
-│ │
-├─────────────────────────────────────────────────────┤
-│ ChatInputBox + ChatInputToolbar (sticky bottom) │
-│ ChatStatusBar │
-└─────────────────────────────────────────────────────┘
-```
-
-**Data sources**:
-
-```typescript
-const sessionStore = useSessionStore()
-const chatStore = useChatStore()
-
-const session = computed(() => sessionStore.activeSession)
-const title = computed(() => session.value?.title ?? 'Chat')
-const project = computed(() => session.value?.projectDir ?? '')
-```
-
-**Submit flow**:
-
-```typescript
-const handleSubmit = (message: string) => {
- const tabId = window.api.getWebContentsId()
- agentPresenter.chat(sessionStore.activeSessionId!, message, tabId)
-}
-```
-
-**IPC**:
-
-| Action | Presenter Call |
-|--------|---------------|
-| Send message | `agentPresenter.chat(sessionId, message, tabId)` |
-| Update settings | `sessionPresenter.updateSessionSettings(sessionId, settings)` |
-
-**Key classes** (from mock):
-- Container: `h-full overflow-y-auto`
-- Input area: `sticky bottom-0 z-10 px-6 pt-3 pb-3`
-- Input wrapper: `flex flex-col items-center`
-
----
-
-## Route Transitions
-
-```
-┌─────────────┐ Provider configured ┌─────────────┐
-│ Welcome │ ─────────────────────────► │ NewThread │
-└─────────────┘ └──────┬──────┘
- ▲ │
- │ │ Submit message
- │ All providers │ (createSession)
- │ removed ▼
- │ ┌─────────────┐
- └──────────────────────────────────── │ Chat │
- closeSession └─────────────┘
-```
-
-## Test Points
-
-1. ChatTabView renders correct page based on `pageRouter.currentRoute`
-2. All stores initialize on mount
-3. WelcomePage provider grid renders 6 providers
-4. WelcomePage clicks open settings tab
-5. NewThreadPage project selector shows projects from store
-6. NewThreadPage submit creates session and navigates to chat
-7. ChatPage displays title and project from active session
-8. ChatPage submit sends message via agentPresenter
diff --git a/docs/archives/new-ui-project-store/spec.md b/docs/archives/new-ui-project-store/spec.md
deleted file mode 100644
index 2181590aa..000000000
--- a/docs/archives/new-ui-project-store/spec.md
+++ /dev/null
@@ -1,133 +0,0 @@
-# Project Store Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-Project Store manages the recent projects list for the NewThread page project selector dropdown. Kept intentionally simple.
-
-## File Location
-
-`src/renderer/src/stores/ui/project.ts`
-
-## Type Definitions
-
-```typescript
-interface UIProject {
- name: string // Folder name (last segment of path)
- path: string // Full path
-}
-```
-
-## Store Design
-
-```typescript
-export const useProjectStore = defineStore('project', () => {
- const filePresenter = useLegacyPresenter('filePresenter')
-
- // --- State ---
- const projects = ref([])
- const selectedProjectPath = ref(null)
- const error = ref(null)
-
- // --- Getters ---
- const selectedProject = computed(() =>
- projects.value.find(p => p.path === selectedProjectPath.value)
- )
- const selectedProjectName = computed(() =>
- selectedProject.value?.name ?? 'Select project'
- )
-
- // --- Actions ---
- function deriveFromSessions(sessions: UISession[]): void
- function selectProject(path: string): void
- async function openFolderPicker(): Promise
-
- return {
- projects, selectedProjectPath, error,
- selectedProject, selectedProjectName,
- deriveFromSessions, selectProject, openFolderPicker
- }
-})
-```
-
-## Actions
-
-### `deriveFromSessions(sessions: UISession[]): void`
-
-Extract unique project directories from the session list. Called by the Session Store after fetching sessions.
-
-```typescript
-function deriveFromSessions(sessions: UISession[]) {
- const seen = new Map()
- for (const s of sessions) {
- if (s.projectDir && !seen.has(s.projectDir)) {
- seen.set(s.projectDir, {
- name: s.projectDir.split('/').pop() ?? s.projectDir,
- path: s.projectDir
- })
- }
- }
- projects.value = Array.from(seen.values())
-
- // Auto-select first project if nothing selected
- if (!selectedProjectPath.value && projects.value.length > 0) {
- selectedProjectPath.value = projects.value[0].path
- }
-}
-```
-
-### `selectProject(path: string): void`
-
-```typescript
-function selectProject(path: string) {
- selectedProjectPath.value = path
-}
-```
-
-### `openFolderPicker(): Promise`
-
-Open native folder picker dialog to add a custom project.
-
-```typescript
-async function openFolderPicker() {
- try {
- const result = await filePresenter.selectDirectory()
- if (result) {
- const name = result.split('/').pop() ?? result
- // Add to list if not already present
- if (!projects.value.some(p => p.path === result)) {
- projects.value.unshift({ name, path: result })
- }
- selectedProjectPath.value = result
- }
- } catch (e) {
- error.value = `Failed to open folder picker: ${e}`
- }
-}
-```
-
-## IPC Call Mapping
-
-| Action | Presenter Call |
-|--------|---------------|
-| Open folder picker | `filePresenter.selectDirectory()` |
-
-## Data Flow
-
-```
-sessionStore.fetchSessions()
- └── projectStore.deriveFromSessions(sessions)
- └── projects list updated
- └── NewThreadPage project dropdown reflects changes
-```
-
-## Test Points
-
-1. `deriveFromSessions` extracts unique projects from sessions
-2. `deriveFromSessions` auto-selects first project when none selected
-3. `selectProject` updates selectedProjectPath
-4. `openFolderPicker` adds new project and selects it
-5. Duplicate paths are not added
-
diff --git a/docs/archives/new-ui-session-store/spec.md b/docs/archives/new-ui-session-store/spec.md
deleted file mode 100644
index d7e33fb4f..000000000
--- a/docs/archives/new-ui-session-store/spec.md
+++ /dev/null
@@ -1,365 +0,0 @@
-# Session Store Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-Session Store is the central owner of all session state: the session list, the active session, grouping/filtering, and session CRUD operations. It coordinates with the Page Router for navigation.
-
-## File Location
-
-`src/renderer/src/stores/ui/session.ts`
-
-## Type Definitions
-
-### UI Session (derived from presenter Session)
-
-The presenter returns a rich `Session` object. The UI store maps it to a flattened structure for display:
-
-```typescript
-interface UISession {
- id: string
- title: string
- agentId: string // Derived: see "Agent ID Resolution" below
- status: UISessionStatus
- projectDir: string // From session.context.agentWorkspacePath
- providerId: string
- modelId: string
- createdAt: number
- updatedAt: number
-}
-
-type UISessionStatus = 'completed' | 'working' | 'error' | 'none'
-```
-
-### Agent ID Resolution
-
-The presenter `Session` does not have a top-level `agentId`. It is derived:
-
-```typescript
-function resolveAgentId(session: Session): string {
- // ACP agent sessions have chatMode 'acp agent' and an acpWorkdirMap
- if (session.config.chatMode === 'acp agent') {
- const acpMap = session.context.acpWorkdirMap
- if (acpMap) {
- // The first (or only) key in acpWorkdirMap is the agentId
- const agentIds = Object.keys(acpMap)
- if (agentIds.length > 0) return agentIds[0]
- }
- }
- return 'deepchat'
-}
-```
-
-### Session Status Mapping
-
-Map presenter `SessionStatus` to UI display status:
-
-```typescript
-function mapSessionStatus(status: SessionStatus): UISessionStatus {
- switch (status) {
- case 'generating':
- case 'waiting_permission':
- case 'waiting_question':
- return 'working'
- case 'error':
- return 'error'
- case 'idle':
- case 'paused':
- return 'none'
- default:
- return 'none'
- }
-}
-```
-
-### Session Group
-
-```typescript
-interface SessionGroup {
- label: string // 'Today', 'Yesterday', 'Last Week', or project name
- sessions: UISession[]
-}
-
-type GroupMode = 'time' | 'project'
-```
-
-## Store Design
-
-```typescript
-export const useSessionStore = defineStore('session', () => {
- const sessionPresenter = useLegacyPresenter('sessionPresenter')
- const pageRouter = usePageRouterStore()
-
- // --- State ---
- const sessions = ref([])
- const activeSessionId = ref(null)
- const groupMode = ref('time')
- const loading = ref(false)
- const error = ref(null)
-
- // --- Getters ---
- const activeSession: ComputedRef
- const sessionGroups: ComputedRef
- const hasActiveSession: ComputedRef
-
- // --- Actions ---
- async function fetchSessions(): Promise
- async function createSession(params: CreateSessionInput): Promise
- async function selectSession(sessionId: string): Promise
- async function closeSession(): Promise
- function toggleGroupMode(): void
- function getFilteredGroups(agentId: string | null): SessionGroup[]
-
- return {
- sessions, activeSessionId, groupMode, loading, error,
- activeSession, sessionGroups, hasActiveSession,
- fetchSessions, createSession, selectSession, closeSession,
- toggleGroupMode, getFilteredGroups
- }
-})
-```
-
-## Actions
-
-### `fetchSessions(): Promise`
-
-Load all sessions from the presenter.
-
-```typescript
-async function fetchSessions() {
- loading.value = true
- error.value = null
- try {
- const result = await sessionPresenter.getSessionList(1, 200)
- sessions.value = result.sessions.map(mapToUISession)
- } catch (e) {
- error.value = `Failed to load sessions: ${e}`
- } finally {
- loading.value = false
- }
-}
-```
-
-### `createSession(params): Promise`
-
-Create a new session and navigate to it.
-
-```typescript
-interface CreateSessionInput {
- title: string
- message: string
- projectDir?: string
- providerId?: string
- modelId?: string
- agentId?: string // 'deepchat' or ACP agent id
- reasoningEffort?: string
-}
-
-async function createSession(params: CreateSessionInput) {
- error.value = null
- try {
- const tabId = window.api.getWebContentsId()
- const settings: Partial = {}
-
- if (params.providerId) settings.providerId = params.providerId
- if (params.modelId) settings.modelId = params.modelId
- if (params.projectDir) settings.agentWorkspacePath = params.projectDir
- if (params.reasoningEffort) settings.reasoningEffort = params.reasoningEffort
-
- // Determine chat mode from agent
- if (params.agentId && params.agentId !== 'deepchat') {
- settings.chatMode = 'acp agent'
- settings.acpWorkdirMap = { [params.agentId]: params.projectDir ?? null }
- }
-
- const sessionId = await sessionPresenter.createSession({
- title: params.title || 'New Thread',
- settings,
- tabId
- })
-
- // Refresh session list and activate
- await fetchSessions()
- activeSessionId.value = sessionId
- pageRouter.goToChat(sessionId)
-
- // Send the initial message
- const agentPresenter = useLegacyPresenter('agentPresenter')
- await agentPresenter.chat(sessionId, params.message, tabId)
- } catch (e) {
- error.value = `Failed to create session: ${e}`
- }
-}
-```
-
-### `selectSession(sessionId: string): Promise`
-
-Switch to an existing session.
-
-```typescript
-async function selectSession(sessionId: string) {
- error.value = null
- try {
- const tabId = window.api.getWebContentsId()
- await sessionPresenter.activateSession(tabId, sessionId)
- activeSessionId.value = sessionId
- pageRouter.goToChat(sessionId)
- } catch (e) {
- error.value = `Failed to select session: ${e}`
- }
-}
-```
-
-### `closeSession(): Promise`
-
-Deactivate the current session and return to NewThread.
-
-```typescript
-async function closeSession() {
- error.value = null
- try {
- const tabId = window.api.getWebContentsId()
- await sessionPresenter.unbindFromTab(tabId)
- activeSessionId.value = null
- pageRouter.goToNewThread()
- } catch (e) {
- error.value = `Failed to close session: ${e}`
- }
-}
-```
-
-### `toggleGroupMode(): void`
-
-```typescript
-function toggleGroupMode() {
- groupMode.value = groupMode.value === 'time' ? 'project' : 'time'
-}
-```
-
-### `getFilteredGroups(agentId: string | null): SessionGroup[]`
-
-Returns grouped sessions, optionally filtered by agent. Used by the sidebar.
-
-```typescript
-function getFilteredGroups(agentId: string | null): SessionGroup[] {
- const grouped = groupMode.value === 'time'
- ? groupByTime(sessions.value)
- : groupByProject(sessions.value)
-
- if (agentId === null) return grouped
-
- return grouped
- .map(group => ({
- label: group.label,
- sessions: group.sessions.filter(s => s.agentId === agentId)
- }))
- .filter(group => group.sessions.length > 0)
-}
-```
-
-## Getters
-
-```typescript
-const activeSession = computed(() =>
- sessions.value.find(s => s.id === activeSessionId.value)
-)
-
-const hasActiveSession = computed(() => activeSessionId.value !== null)
-
-const sessionGroups = computed(() => getFilteredGroups(null))
-```
-
-## Grouping Logic
-
-### groupByTime
-
-```typescript
-function groupByTime(sessions: UISession[]): SessionGroup[] {
- const now = Date.now()
- const today = startOfDay(now)
- const yesterday = startOfDay(now - 86400000)
- const lastWeek = startOfDay(now - 7 * 86400000)
-
- const groups: Record = {
- 'Today': [],
- 'Yesterday': [],
- 'Last Week': [],
- 'Older': []
- }
-
- for (const s of sessions) {
- if (s.updatedAt >= today) groups['Today'].push(s)
- else if (s.updatedAt >= yesterday) groups['Yesterday'].push(s)
- else if (s.updatedAt >= lastWeek) groups['Last Week'].push(s)
- else groups['Older'].push(s)
- }
-
- return Object.entries(groups)
- .filter(([, sessions]) => sessions.length > 0)
- .map(([label, sessions]) => ({ label, sessions }))
-}
-```
-
-### groupByProject
-
-```typescript
-function groupByProject(sessions: UISession[]): SessionGroup[] {
- const projectMap = new Map()
- for (const session of sessions) {
- const dir = session.projectDir || 'No Project'
- if (!projectMap.has(dir)) projectMap.set(dir, [])
- projectMap.get(dir)!.push(session)
- }
- return Array.from(projectMap.entries()).map(([dir, sessions]) => ({
- label: dir.split('/').pop() ?? dir,
- sessions
- }))
-}
-```
-
-## IPC Call Mapping
-
-| Action | Presenter Call |
-|--------|---------------|
-| Fetch sessions | `sessionPresenter.getSessionList(page, pageSize)` |
-| Create session | `sessionPresenter.createSession({ title, settings, tabId })` |
-| Activate session | `sessionPresenter.activateSession(tabId, sessionId)` |
-| Deactivate session | `sessionPresenter.unbindFromTab(tabId)` |
-| Send message | `agentPresenter.chat(sessionId, message, tabId)` |
-| Get active session | `sessionPresenter.getActiveSession(tabId)` |
-
-## Event Listeners
-
-| Event | Handler |
-|-------|---------|
-| `CONVERSATION_EVENTS.LIST_UPDATED` | Call `fetchSessions()` to refresh list |
-| `CONVERSATION_EVENTS.ACTIVATED` | Update `activeSessionId` |
-| `CONVERSATION_EVENTS.DEACTIVATED` | Clear `activeSessionId`, `goToNewThread()` |
-
-## Error Handling
-
-All async actions catch errors and set `error` ref. Components can display errors via:
-
-```vue
-
- {{ sessionStore.error }}
-
-```
-
-The `error` state is cleared at the start of each action.
-
-## Test Points
-
-1. `fetchSessions` maps presenter Sessions to UISession correctly
-2. `resolveAgentId` returns 'deepchat' for agent-mode sessions
-3. `resolveAgentId` returns ACP agent id for acp-agent-mode sessions
-4. `createSession` creates session, refreshes list, navigates to chat
-5. `selectSession` activates session and navigates to chat
-6. `closeSession` unbinds tab and navigates to newThread
-7. `groupByTime` correctly buckets sessions into Today/Yesterday/Last Week/Older
-8. `groupByProject` correctly groups by projectDir
-9. `getFilteredGroups` filters by agentId when provided
-10. Error states are set on failure and cleared on retry
-
diff --git a/docs/archives/new-ui-sidebar/spec.md b/docs/archives/new-ui-sidebar/spec.md
deleted file mode 100644
index 8f47a8e7d..000000000
--- a/docs/archives/new-ui-sidebar/spec.md
+++ /dev/null
@@ -1,206 +0,0 @@
-# Sidebar Component Spec
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Overview
-
-The sidebar displays agent filter icons, session list with grouping, and quick action buttons. All data comes from stores — no mock data.
-
-## File Location
-
-`src/renderer/src/components/WindowSideBar.vue`
-
-## Visual Design (must match mock exactly)
-
-```
-┌─────────────────────────────────────────────────────┐
-│ Agent Icons (48px) │ Session List (240px) │
-│ ┌─────────────────┐ │ ┌───────────────────────────┐ │
-│ │ [All Agents] │ │ │ Header: Agent Name │ │
-│ │ ──────────── │ │ │ [Group] [+ New] │ │
-│ │ [DeepChat] │ │ ├───────────────────────────┤ │
-│ │ [Claude Code] │ │ │ Today │ │
-│ │ [Codex] │ │ │ - Fix login bug [v] │ │
-│ │ [Kimi] │ │ │ - Refactor auth [~] │ │
-│ │ [My Bot] │ │ ├───────────────────────────┤ │
-│ │ │ │ │ Yesterday │ │
-│ │ │ │ │ - Add dark mode │ │
-│ │ ──────────── │ │ │ - API integration [!] │ │
-│ │ [Collapse] │ │ └───────────────────────────┘ │
-│ │ [Browser] │ │ │
-│ │ [Settings] │ │ │
-│ └─────────────────┘ │ │
-└─────────────────────────────────────────────────────┘
-```
-
-### Widths
-
-- Expanded: `w-[288px]` (48px agent column + 240px session column)
-- Collapsed: `w-12` (agent column only)
-
-## Data Sources (all from stores)
-
-| UI Element | Store Source |
-|------------|-------------|
-| Agent icon list | `agentStore.enabledAgents` |
-| Selected agent | `agentStore.selectedAgentId` |
-| Agent name in header | `agentStore.selectedAgentName` |
-| Session groups | `sessionStore.getFilteredGroups(agentStore.selectedAgentId)` |
-| Active session | `sessionStore.activeSessionId` |
-| Group mode toggle | `sessionStore.groupMode` |
-
-## Local State
-
-Only UI-specific state is local to the component:
-
-```typescript
-const collapsed = ref(false)
-```
-
-Everything else comes from stores.
-
-## Component Implementation
-
-### Agent Icons Column
-
-```vue
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### Session List Column
-
-```vue
-
-
-
-
{{ agentStore.selectedAgentName }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
No conversations yet
-
Start a new chat to begin
-
-
-
-
-
- {{ group.label }}
-
-
- {{ session.title }}
-
-
-
-
-
-
-
-
-```
-
-## Computed Properties
-
-```typescript
-const filteredGroups = computed(() =>
- sessionStore.getFilteredGroups(agentStore.selectedAgentId)
-)
-```
-
-## Event Handlers
-
-```typescript
-const handleNewChat = () => {
- sessionStore.closeSession()
- // closeSession internally calls pageRouter.goToNewThread()
-}
-
-const handleSessionClick = (session: UISession) => {
- sessionStore.selectSession(session.id)
- // selectSession internally calls pageRouter.goToChat(id)
-}
-
-const openSettings = () => {
- const windowId = window.api.getWindowId()
- if (windowId != null) {
- windowPresenter.openOrFocusSettingsTab(windowId)
- }
-}
-
-const onBrowserClick = async () => {
- try {
- await yoBrowserPresenter.show(true)
- } catch (e) {
- console.warn('Failed to open browser window.', e)
- }
-}
-```
-
-## Styling Reference
-
-All CSS classes must match the existing `WindowSideBar.vue` mock implementation exactly. Key classes:
-
-- Agent button selected: `bg-card/50 border-white/70 dark:border-white/20 ring-1 ring-black/10`
-- Agent button default: `bg-transparent border-none hover:bg-white/30 dark:hover:bg-white/10`
-- Session item active: `bg-accent text-accent-foreground`
-- Session item hover: `text-foreground/80 hover:bg-accent/50`
-- Status working: `text-primary animate-spin` (loader-2 icon)
-- Status completed: `text-green-500` (check icon)
-- Status error: `text-destructive` (alert-circle icon)
-- Window drag region: `-webkit-app-region: drag` on container, `no-drag` on buttons
-
-## Test Points
-
-1. Agent icons render from `agentStore.enabledAgents`
-2. Clicking agent icon calls `agentStore.selectAgent(id)`
-3. Session list renders from `sessionStore.getFilteredGroups()`
-4. Clicking session calls `sessionStore.selectSession(id)`
-5. New Chat button calls `sessionStore.closeSession()`
-6. Group toggle calls `sessionStore.toggleGroupMode()`
-7. Collapse toggle hides session column
-8. Empty state shows when no sessions match filter
-9. Status indicators display correctly per session status
diff --git a/docs/archives/new-ui-status-bar/spec.md b/docs/archives/new-ui-status-bar/spec.md
deleted file mode 100644
index feb68e352..000000000
--- a/docs/archives/new-ui-status-bar/spec.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Working ChatStatusBar (Model + Effort Selectors)
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Problem
-
-ChatStatusBar shows hardcoded "Claude 4 Sonnet" / "High" / "Default permissions". It needs to read from and write to the active session's config via `chatStore.chatConfig` and `chatStore.updateChatConfig()`.
-
-## Model Selector
-
-- **Read**: `chatStore.chatConfig.providerId` + `chatStore.chatConfig.modelId`
-- **Display**: Resolve model name via `modelStore.findModelByIdOrName(modelId)`
-- **List**: Flatten `modelStore.enabledModels` (array of `{ providerId, models[] }`)
-- **Write**: `chatStore.updateChatConfig({ providerId, modelId })` on selection
-
-## Effort Selector
-
-- **Read**: `chatStore.chatConfig.reasoningEffort` (Anthropic/others), `chatStore.chatConfig.thinkingBudget` (Google), `chatStore.chatConfig.verbosity` (OpenAI)
-- **Display**: Unified effort label from the provider-appropriate field
-- **Options**: Low, Medium, High (map to `reasoningEffort` values)
-- **Write**: `chatStore.updateChatConfig({ reasoningEffort: value })` — the backend normalizes per provider
-
-### Effort Type Reference
-
-From `CONVERSATION_SETTINGS`:
-- `reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high'`
-- `verbosity?: 'low' | 'medium' | 'high'`
-- `thinkingBudget?: number`
-
-## Permissions Indicator
-
-Stream-driven (permissions are requested per tool call). Kept as read-only indicator. Actual permission handling is done by `MessageBlockPermissionRequest` in the message list.
-
-## Initialization
-
-`modelStore.initialize()` must be called during `ChatTabView.onMounted` to ensure models are loaded before the status bar renders.
-
-## Files Modified
-
-- `src/renderer/src/components/chat/ChatStatusBar.vue` — major rewrite with real data
-- `src/renderer/src/views/ChatTabView.vue` — add `modelStore.initialize()` to onMounted
diff --git a/docs/archives/provider-layer-simplification/plan.md b/docs/archives/provider-layer-simplification/plan.md
deleted file mode 100644
index a9f6ab837..000000000
--- a/docs/archives/provider-layer-simplification/plan.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Provider Layer Simplification Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-1. Add a provider definition registry that captures runtime kind, model source, health check, and
- hook strategy per provider.
-2. Introduce a generic `AiSdkProvider` that owns AI SDK-backed text, stream, summary, image,
- embeddings, and provider check flows.
-3. Switch `ProviderInstanceManager` from vendor constructor maps to:
- - special providers for `acp`, `github-copilot`, `voiceai`, `ollama`
- - generic `AiSdkProvider` for all other AI SDK-backed providers
-4. Move ModelScope MCP sync HTTP logic into shared helpers so `ModelscopeProvider` is no longer
- required.
-5. Adapt provider tests to assert behavior through the generic provider instead of vendor classes.
-6. Delete obsolete vendor provider classes after import scans confirm they have no remaining
- callers.
-7. Run format, i18n, lint, typecheck, and targeted provider tests.
diff --git a/docs/archives/provider-layer-simplification/spec.md b/docs/archives/provider-layer-simplification/spec.md
deleted file mode 100644
index b96b57f57..000000000
--- a/docs/archives/provider-layer-simplification/spec.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Provider Layer Simplification
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## Status
-
-Completed on `2026-04-11`.
-
-## Goal
-
-Collapse the AI SDK-backed provider layer into one internal implementation while keeping all
-user-visible provider contracts unchanged.
-
-The simplified structure is:
-
-- registry-driven provider resolution
-- one generic `AiSdkProvider` for AI SDK-backed providers
-- special-case providers kept only when they own non-generic responsibilities
-
-## In Scope
-
-- replace vendor class selection in `ProviderInstanceManager` with a registry lookup
-- move runtime choice, routing, model source, health-check strategy, and provider-specific hooks
- into provider definitions
-- keep `AcpProvider`, `GithubCopilotProvider`, `VoiceAIProvider`, and `OllamaProvider` as
- independent classes
-- keep `AiSdkProvider` as the single generic implementation for AI SDK-backed providers
-- move ModelScope MCP sync helpers out of `ModelscopeProvider`
-- delete obsolete vendor provider classes once they have no remaining callers
-
-## Out of Scope
-
-- removing providers from the settings UI or changing the default provider list
-- changing persisted provider IDs, model configs, or conversation history
-- refactoring `OllamaProvider` local model management into a different subsystem
-- redesigning prompts or harmonizing provider-specific output behavior
-
-## Compatibility Constraints
-
-The following must remain stable:
-
-- `providerId`
-- `apiType`
-- provider configuration schema
-- model configuration schema
-- history and `function_call_record` compatibility
-- `LLMProviderPresenter.getProviderInstance()` behavior
-- `LLMCoreStreamEvent` names and payload structure
-
-## Result
-
-After this simplification:
-
-- `ProviderInstanceManager` acts as a registry-backed factory
-- AI SDK-backed providers no longer rely on vendor-specific provider classes
-- `providers/` retains only:
- - `acpProvider.ts`
- - `aiSdkProvider.ts`
- - `githubCopilotProvider.ts`
- - `ollamaProvider.ts`
- - `voiceAIProvider.ts`
-
-## Acceptance Criteria
-
-- registry resolution honors `providerId` first and `apiType` second
-- routing providers (`new-api`, `zenmux`, `grok`) continue to route by model capability
-- ModelScope MCP sync works without `ModelscopeProvider` instance methods
-- no remaining runtime imports of deleted vendor provider classes
-- targeted provider tests pass on the generic path
diff --git a/docs/archives/provider-layer-simplification/tasks.md b/docs/archives/provider-layer-simplification/tasks.md
deleted file mode 100644
index c390afe04..000000000
--- a/docs/archives/provider-layer-simplification/tasks.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Provider Layer Simplification Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-- [x] Add `providerRegistry.ts` as the single source of AI SDK-backed provider definitions.
-- [x] Add generic `AiSdkProvider` and move shared provider behavior into it.
-- [x] Route `ProviderInstanceManager` through registry definitions plus special providers.
-- [x] Decouple ModelScope MCP sync from `ModelscopeProvider` instance methods.
-- [x] Update provider-layer tests to target the generic provider behavior.
-- [x] Delete obsolete vendor provider classes from `src/main/presenter/llmProviderPresenter/providers/`.
-- [x] Run `pnpm run format`.
-- [x] Run `pnpm run i18n`.
-- [x] Run `pnpm run lint`.
-- [x] Run `pnpm run typecheck`.
-- [x] Run targeted provider-layer tests.
diff --git a/docs/archives/remove-chat-mode/plan.md b/docs/archives/remove-chat-mode/plan.md
deleted file mode 100644
index 208734d1c..000000000
--- a/docs/archives/remove-chat-mode/plan.md
+++ /dev/null
@@ -1,288 +0,0 @@
-# 移除 Chat 模式实施计划
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 当前实现基线
-
-### 1.1 模式类型定义
-
-```typescript
-// src/renderer/src/components/chat-input/composables/useChatMode.ts:9
-export type ChatMode = 'chat' | 'agent' | 'acp agent'
-```
-
-共享类型分布在:
-- `src/shared/types/presenters/thread.presenter.d.ts:24`
-- `src/shared/types/presenters/session.presenter.d.ts:24,51`
-- `src/main/presenter/sessionPresenter/types.ts:14,42`
-
-### 1.2 Chat 模式特点
-
-1. **无 Agent 工具** - `toolPresenter/index.ts:100` 判断 `chatMode !== 'chat'` 才加载
-2. **支持 Web 搜索** - `ChatInput.vue:583` 的 `canUseWebSearch` 仅 chat 模式可用
-3. **简单 LLM 对话** - 无工具调用循环
-
-### 1.3 Web 搜索架构
-
-```
-Main Process:
-├── searchPresenter/
-│ ├── index.ts - ISearchPresenter 实现
-│ ├── interface.ts - 搜索接口定义
-│ ├── managers/
-│ │ └── searchManager.ts - 搜索逻辑
-│ └── handlers/
-│ ├── baseHandler.ts - 基础处理器
-│ └── searchHandler.ts - 搜索处理器
-
-Renderer Process:
-├── components/
-│ ├── SearchStatusIndicator.vue
-│ ├── SearchResultsDrawer.vue
-│ └── message/MessageBlockSearch.vue
-├── stores/
-│ ├── searchAssistantStore.ts
-│ ├── searchEngineStore.ts
-│ └── reference.ts
-
-Shared:
-└── types/presenters/search.presenter.d.ts
-```
-
-### 1.4 涉及文件统计
-
-| 类别 | 数量 |
-|------|------|
-| 待删除文件 | 11个 |
-| 待修改文件 | ~25个 |
-| 类型定义修改 | 7处 |
-| i18n 文件 | 12个 |
-
-## 2. 设计决策
-
-### 2.1 类型简化
-
-**决策**:`ChatMode` 类型从三元改为二元
-
-```typescript
-// Before
-export type ChatMode = 'chat' | 'agent' | 'acp agent'
-
-// After
-export type ChatMode = 'agent' | 'acp agent'
-```
-
-**影响范围**:
-- 7处类型定义
-- 所有 `chatMode === 'chat'` 判断改为始终加载 agent 能力
-- 默认值统一改为 `'agent'`
-
-### 2.2 旧数据迁移策略
-
-**决策**:静默升级,无通知
-
-```typescript
-// sessionResolver.ts
-if (settings.chatMode === 'chat') {
- settings.chatMode = 'agent'
- // 不持久化到数据库(运行时升级即可)
- // 如果后续有 updateConversationSettings 调用会自动保存
-}
-```
-
-**原则**:
-1. 不破坏数据库结构
-2. 不显示任何 toast/notification
-3. 用户无感知
-
-### 2.3 搜索功能移除策略
-
-**决策**:完全删除,不保留任何代码
-
-**删除清单**:
-1. `searchPresenter/` 整个目录(5个文件)
-2. 搜索相关组件(3个 Vue 文件)
-3. 搜索相关 stores(3个文件)
-4. 搜索类型定义(1个文件)
-
-**配置清理**:
-1. `enableSearch`、`forcedSearch`、`searchStrategy` 从 CONVERSATION_SETTINGS 移除
-2. `searchPreviewEnabled`、`customSearchEngines` 从 configPresenter 移除
-3. 搜索相关方法从 configPresenter 移除
-
-### 2.4 工具加载简化
-
-**决策**:移除 chat 模式判断,始终加载 Agent 工具
-
-```typescript
-// toolPresenter/index.ts - Before
-if (chatMode !== 'chat') {
- // Load Agent tools
-}
-
-// After
-// Always load Agent tools (no condition)
-```
-
-### 2.5 UI 简化
-
-**决策**:
-1. 模式选择器只显示 Agent / ACP Agent 两个选项
-2. 移除 web 搜索按钮
-3. 移除 `variant === 'chat'` 的特殊样式
-
-## 3. 实施阶段
-
-### Phase 1:删除搜索 Presenter(影响最小)
-
-1. 删除 `src/main/presenter/searchPresenter/` 目录
-2. 删除 `src/shared/types/presenters/search.presenter.d.ts`
-3. 从 `src/main/presenter/index.ts` 移除 searchPresenter 注册
-4. 从 `src/preload/index.ts` 移除 searchPresenter 暴露
-5. 运行 typecheck 确认编译通过
-
-### Phase 2:删除搜索组件和 Stores
-
-1. 删除 `SearchStatusIndicator.vue`
-2. 删除 `SearchResultsDrawer.vue`
-3. 删除 `MessageBlockSearch.vue`
-4. 删除 `searchAssistantStore.ts`
-5. 删除 `searchEngineStore.ts`
-6. 删除 `reference.ts`
-7. 从 `MessageItemAssistant.vue` 移除 MessageBlockSearch 引用
-
-### Phase 3:修改类型定义
-
-1. 更新 7 处 `ChatMode` 类型定义
-2. 更新 `useChatMode.ts`:
- - 移除 chat 图标
- - 移除 modes 数组中的 chat 选项
- - 默认值改为 `'agent'`
-
-### Phase 4:核心逻辑修改
-
-1. `sessionResolver.ts` - 添加旧数据升级逻辑
-2. `sessionManager.ts` - fallback 默认值改为 `'agent'`
-3. `toolPresenter/index.ts` - 移除 chat 模式判断
-4. `mcpPresenter/toolManager.ts` - fallback 默认值改为 `'agent'`
-
-### Phase 5:清理搜索配置
-
-1. `CONVERSATION_SETTINGS` 类型移除 `enableSearch`、`forcedSearch`、`searchStrategy`
-2. `chat.ts` store 移除相关配置
-3. `modelStore.ts` 移除 `enableSearch`
-4. `configPresenter` 移除搜索配置和方法
-5. `ChatInput.vue` 移除 web 搜索按钮和 `canUseWebSearch`
-6. `MessageBlockQuestionRequest.vue` 移除搜索相关代码
-
-### Phase 6:国际化清理
-
-1. 所有 `chat.json` 文件移除 `mode.chat`
-2. 所有 `chat.json` 文件移除 `features.webSearch`
-3. 所有 `chat.json` 文件移除 `search` 块
-
-### Phase 7:测试与验证
-
-1. 更新相关测试用例
-2. 删除搜索相关测试文件
-3. 运行完整测试套件
-
-## 4. 数据与配置影响
-
-### 4.1 数据库
-
-| 影响项 | 处理方式 |
-|--------|----------|
-| `conversations.settings.chatMode` | 保留字段,运行时升级 `'chat'` → `'agent'` |
-| `conversations.settings.enableSearch` | 保留字段,运行时忽略 |
-| `conversations.settings.forcedSearch` | 保留字段,运行时忽略 |
-| `conversations.settings.searchStrategy` | 保留字段,运行时忽略 |
-
-**结论**:无 schema 迁移,保持向后兼容
-
-### 4.2 用户配置
-
-| 配置项 | 处理方式 |
-|--------|----------|
-| `input_chatMode` | 运行时升级 `'chat'` → `'agent'` |
-| `searchPreviewEnabled` | 删除(不再使用) |
-| `customSearchEngines` | 删除(不再使用) |
-
-### 4.3 破坏性变化
-
-| 变化 | 影响范围 |
-|------|----------|
-| Chat 模式不可用 | 所有用户 |
-| Web 搜索功能移除 | Chat 模式用户 |
-| 搜索引擎配置丢失 | 配置过搜索引擎的用户 |
-
-## 5. 测试策略
-
-### 5.1 单元测试
-
-1. **useChatMode**
- - 默认模式为 `'agent'`
- - modes 数组不包含 `'chat'`
- - 设置 `'chat'` 模式被忽略或转为 `'agent'`
-
-2. **sessionResolver**
- - `chatMode === 'chat'` 时自动升级为 `'agent'`
-
-3. **toolPresenter**
- - Agent 工具始终加载
-
-### 5.2 集成测试
-
-1. **旧对话打开**
- - chat 模式的对话可以正常打开
- - 自动升级为 agent 模式
- - 无错误或警告
-
-2. **新对话创建**
- - 默认为 agent 模式
- - 工具可正常调用
-
-### 5.3 删除的测试文件
-
-```
-test/main/presenter/searchPresenter/ (如果存在)
-test/renderer/components/message/MessageBlockSearch.test.ts (如果存在)
-```
-
-## 6. 风险与缓解
-
-| 风险 | 影响 | 缓解措施 |
-|------|------|----------|
-| 旧对话打开失败 | 高 | 升级逻辑充分测试,添加 fallback |
-| 残留搜索引用 | 中 | 全局搜索 `search`、`Search`、`enableSearch` 等 |
-| i18n key 丢失 | 低 | 检查运行时是否有 missing key 警告 |
-| 配置迁移失败 | 低 | 运行时升级,不依赖持久化 |
-
-## 7. 质量门槛
-
-```bash
-# 代码格式
-pnpm run format
-
-# 代码检查
-pnpm run lint
-
-# 类型检查
-pnpm run typecheck
-
-# 测试
-pnpm test
-```
-
-## 8. 实施检查清单
-
-- [ ] Phase 1: 删除搜索 Presenter
-- [ ] Phase 2: 删除搜索组件和 Stores
-- [ ] Phase 3: 修改类型定义
-- [ ] Phase 4: 核心逻辑修改
-- [ ] Phase 5: 清理搜索配置
-- [ ] Phase 6: 国际化清理
-- [ ] Phase 7: 测试与验证
-- [ ] 运行 `pnpm run format && pnpm run lint && pnpm run typecheck`
-- [ ] 运行 `pnpm test`
diff --git a/docs/archives/remove-chat-mode/spec.md b/docs/archives/remove-chat-mode/spec.md
deleted file mode 100644
index 6546e846e..000000000
--- a/docs/archives/remove-chat-mode/spec.md
+++ /dev/null
@@ -1,179 +0,0 @@
-# 移除 Chat 模式规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 概述
-
-移除 DeepChat 中的 "chat" 模式,仅保留 "agent" 和 "acp agent" 两种模式。同时完全移除 Web 搜索功能(该功能仅在 chat 模式下可用)。旧的 chat 模式对话将静默升级为 agent 模式。
-
-## 背景与动机
-
-1. Agent 模式已成熟完善,内置工具调用能力已成为核心特性
-2. Chat 模式功能有限(无工具调用),与 Agent 模式形成冗余
-3. Web 搜索功能仅 Chat 模式可用,用户实际使用率低
-4. 简化模式选择,降低用户决策成本
-
-## 用户故事
-
-### US-1:模式选择简化
-
-作为 DeepChat 用户,我希望不再需要在 chat 和 agent 模式之间选择,直接使用功能更强大的 agent 模式。
-
-### US-2:旧对话无缝迁移
-
-作为已有 chat 模式对话的用户,我希望打开旧对话时无需任何操作,系统能自动将其升级为 agent 模式并继续正常使用。
-
-### US-3:一致的 Agent 体验
-
-作为 DeepChat 用户,我希望所有对话都具备完整的工具调用能力,无需关心"这个对话是否支持某个功能"。
-
-## 验收标准
-
-### A. 类型定义
-
-- [ ] `ChatMode` 类型改为 `'agent' | 'acp agent'`
-- [ ] 所有涉及 `chatMode` 的类型定义更新
-- [ ] 默认值从 `'chat'` 改为 `'agent'`
-
-### B. 模式选择 UI
-
-- [ ] 模式选择器仅显示 "Agent" 和 "ACP Agent" 两个选项
-- [ ] 移除 `MODE_ICONS` 中的 chat 图标
-- [ ] `useChatMode.ts` 的 `modes` 数组不再包含 chat 选项
-
-### C. Web 搜索功能移除
-
-- [ ] 删除 `src/main/presenter/searchPresenter/` 整个目录
-- [ ] 删除 `src/renderer/src/components/SearchStatusIndicator.vue`
-- [ ] 删除 `src/renderer/src/components/SearchResultsDrawer.vue`
-- [ ] 删除 `src/renderer/src/components/message/MessageBlockSearch.vue`
-- [ ] 删除 `src/renderer/src/stores/searchAssistantStore.ts`
-- [ ] 删除 `src/renderer/src/stores/searchEngineStore.ts`
-- [ ] 删除 `src/renderer/src/stores/reference.ts`
-- [ ] 删除 `src/shared/types/presenters/search.presenter.d.ts`
-- [ ] `ChatInput.vue` 移除 web 搜索按钮和相关逻辑
-- [ ] `MessageBlockQuestionRequest.vue` 移除搜索相关代码
-
-### D. 配置清理
-
-- [ ] `CONVERSATION_SETTINGS` 移除 `enableSearch`、`forcedSearch`、`searchStrategy`
-- [ ] `chat.ts` store 移除搜索相关配置
-- [ ] `modelStore.ts` 移除 `enableSearch` 属性
-- [ ] `configPresenter` 移除 `searchPreviewEnabled`、`customSearchEngines` 等配置
-
-### E. 工具加载逻辑
-
-- [ ] `toolPresenter/index.ts` 移除 `chatMode !== 'chat'` 判断
-- [ ] Agent 工具始终加载(无需模式判断)
-
-### F. 旧数据迁移
-
-- [ ] `sessionResolver.ts` 检测 `chatMode === 'chat'` 时静默升级为 `'agent'`
-- [ ] 升级过程无任何用户通知或中断
-
-### G. Presenter 注册清理
-
-- [ ] `src/main/presenter/index.ts` 移除 searchPresenter 注册
-- [ ] `src/preload/index.ts` 移除 searchPresenter 暴露
-- [ ] `src/shared/types/presenters/index.d.ts` 移除 ISearchPresenter 引用
-
-### H. 国际化
-
-- [ ] 所有语言文件移除 `mode.chat` 翻译
-- [ ] 所有语言文件移除 `features.webSearch` 翻译
-- [ ] 所有语言文件移除 `search` 块
-
-### I. 消息组件
-
-- [ ] `MessageItemAssistant.vue` 移除 `MessageBlockSearch` 组件引用
-
-## 非目标
-
-1. **不修改数据库 schema** - `chatMode` 字段保留,仅运行时逻辑改变
-2. **不保留任何 chat 模式兼容代码** - 彻底移除,不做 fallback
-3. **不保留搜索功能给 agent 模式使用** - 完全移除
-4. **不修改 MCP 相关工具** - 仅移除 DeepChat 内置搜索
-
-## 约束
-
-1. **数据迁移必须静默** - 不显示任何 toast 或通知
-2. **向后兼容** - 旧对话数据不丢失,仅升级模式标记
-3. **测试覆盖** - 关键路径必须有测试验证
-4. **代码格式** - 必须通过 `format`、`lint`、`typecheck`
-
-## 待删除文件清单
-
-### Main 进程
-
-```
-src/main/presenter/searchPresenter/
-├── index.ts
-├── interface.ts
-├── managers/searchManager.ts
-└── handlers/
- ├── baseHandler.ts
- └── searchHandler.ts
-```
-
-### Renderer 进程
-
-```
-src/renderer/src/components/SearchStatusIndicator.vue
-src/renderer/src/components/SearchResultsDrawer.vue
-src/renderer/src/components/message/MessageBlockSearch.vue
-src/renderer/src/stores/searchAssistantStore.ts
-src/renderer/src/stores/searchEngineStore.ts
-src/renderer/src/stores/reference.ts
-```
-
-### Shared
-
-```
-src/shared/types/presenters/search.presenter.d.ts
-```
-
-## 待修改文件清单
-
-### 类型定义 (7处)
-
-| 文件 | 修改内容 |
-|------|----------|
-| `src/shared/types/presenters/thread.presenter.d.ts:24` | `chatMode?: 'agent' \| 'acp agent'` |
-| `src/shared/types/presenters/session.presenter.d.ts:24,51` | 同上 |
-| `src/shared/types/presenters/tool.presenter.d.ts:19` | 同上 |
-| `src/shared/types/presenters/legacy.presenters.d.ts:1091` | 同上 |
-| `src/main/presenter/sessionPresenter/types.ts:14,42` | 同上 |
-| `src/renderer/src/components/chat-input/composables/useChatMode.ts:9` | 同上 |
-| `src/renderer/src/components/chat-input/composables/useWorkspaceMention.ts:9` | 同上 |
-
-### 核心逻辑
-
-| 文件 | 修改内容 |
-|------|----------|
-| `useChatMode.ts` | 移除 chat 模式选项,默认改为 agent |
-| `sessionResolver.ts` | 添加旧数据升级逻辑 |
-| `sessionManager.ts` | fallback 默认值改为 agent |
-| `toolPresenter/index.ts` | 移除 chat 模式判断 |
-| `chat.ts` store | 移除搜索配置 |
-| `modelStore.ts` | 移除 enableSearch |
-| `configPresenter/index.ts` | 移除搜索配置和方法 |
-
-### UI 组件
-
-| 文件 | 修改内容 |
-|------|----------|
-| `ChatInput.vue` | 移除 web 搜索按钮、canUseWebSearch |
-| `MessageBlockQuestionRequest.vue` | 移除搜索相关代码 |
-| `MessageItemAssistant.vue` | 移除 MessageBlockSearch 引用 |
-
-### i18n (12个文件)
-
-- `src/renderer/src/i18n/*/chat.json` - 移除 mode.chat、features.webSearch、search 块
-
-## 开放问题
-
-无。所有问题已澄清:
-
-- **搜索功能处理**:完全移除
-- **旧数据升级通知**:静默升级
diff --git a/docs/archives/skills-system/design.md b/docs/archives/skills-system/design.md
deleted file mode 100644
index 9369f415a..000000000
--- a/docs/archives/skills-system/design.md
+++ /dev/null
@@ -1,647 +0,0 @@
-# DeepChat Skills 系统设计文档
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 概述
-
-### 1.1 核心理念
-
-Skills 是一个**文件体系**,为 AI Agent 提供可激活的专业知识和行为指导。
-
-**关键特征**:
-- **文件驱动**:Skills 以文件形式存在于文件系统中,用户在设置中管理
-- **模型自主**:由 LLM 通过工具决定何时激活/停用 Skill
-- **渐进加载**:Metadata 始终在 Context,完整内容仅激活后加载
-- **工具复用**:Skill 内的 scripts/references 通过现有工具(Read/Bash)访问
-- **热加载**:监控 Skill 文件变化,自动更新 Metadata
-
-### 1.2 工作流程
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 启动时:扫描文件系统,提取所有 Skill 的 Metadata │
-└─────────────────────────────────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 每轮对话:Metadata 列表始终存在于 Context │
-│ "Available skills: code-review, refactor, ..." │
-└─────────────────────────────────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 模型决策:根据用户意图,调用 skill_control 工具 │
-│ skill_control({ action: "activate", skill_name: "..." })│
-└─────────────────────────────────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 下一轮 Loop:加载已激活 Skill 的完整 SKILL.md 内容 │
-│ 注入到系统提示中 │
-└─────────────────────────────────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 模型执行:根据 SKILL.md 指令,用现有工具访问资源 │
-│ - Read 工具读取 references/ │
-│ - Bash 工具执行 scripts/ │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### 1.3 设计目标
-
-| 目标 | 描述 | 优先级 |
-|------|------|--------|
-| **文件体系** | Skills 以文件夹形式存在,用户在设置中管理 | P0 |
-| **模型自主激活** | LLM 通过工具控制 Skill 激活状态 | P0 |
-| **渐进加载** | Metadata 常驻,完整内容按需加载 | P0 |
-| **热加载** | 监控文件变化,自动更新 Metadata | P0 |
-| **工具扩展** | Skill 可声明额外需要的工具 | P1 |
-
-### 1.4 非目标
-
-- Skill 市场和在线分发
-- Skill 版本管理和依赖解析
-- 多 Skill 编排和工作流
-- 与 Custom Prompts 合并
-- 项目级/用户级分层(当前仅用户级)
-
----
-
-## 2. 文件体系
-
-### 2.1 目录结构
-
-```
-~/.deepchat/skills/ # Skills 目录
-├── code-review/ # Skill 文件夹
-│ ├── SKILL.md # 必需:元数据 + 完整指令
-│ ├── references/ # 可选:参考文档
-│ │ ├── style-guide.md
-│ │ └── checklist.md
-│ └── scripts/ # 可选:辅助脚本
-│ └── lint.sh
-├── refactor/
-│ └── SKILL.md
-└── my-skill/
- └── SKILL.md
-```
-
-**说明**:
-- 内置 Skills 首次启动时自动安装到同一目录
-- 用户在设置界面中管理 Skills(启用/禁用/编辑)
-- 用户可删除或修改内置 Skills
-
-### 2.2 Skill 安装
-
-支持三种安装方式:
-
-| 方式 | 说明 | 使用场景 |
-|------|------|----------|
-| 本地文件夹 | 选择包含 SKILL.md 的文件夹 | 从其他客户端拷贝(如 ~/.claude/skills/) |
-| 本地 zip | 选择 zip 文件 | 下载的 Skill 包 |
-| URL | 输入 zip 下载地址 | 在线安装 |
-
-**文件夹/zip 结构要求**:
-
-```
-skill-name/ # Skill 文件夹
-├── SKILL.md # 必需
-├── references/ # 可选
-└── scripts/ # 可选
-```
-
-**安装流程**:
-
-```
-用户选择文件夹 / zip 文件 / 输入 URL
- │
- ▼
-读取文件夹 / 解压 zip / 下载并解压
- │
- ▼
-验证结构(SKILL.md 必须存在)
- │
- ▼
-解析 SKILL.md frontmatter,获取 name
- │
- ▼
-验证 name 与目录名一致(不一致则自动重命名目录为 name)
- │
- ▼
-检查同名 Skill 是否存在
- │
- ├── 存在 → 提示用户确认覆盖
- │
- ▼
-拷贝到 ~/.deepchat/skills/{name}/
- │
- ▼
-触发热加载,更新 Metadata
-```
-
-**冲突处理**:
-- 同名 Skill 已存在时,提示用户选择:覆盖 / 取消
-- 覆盖时先备份原 Skill 文件夹
-
-### 2.3 SKILL.md 格式
-
-```markdown
----
-name: code-review
-description: 按照团队规范进行代码审查,检查代码质量、安全性和可维护性
-allowedTools: # 可选:额外需要的工具(与用户已启用工具取并集)
- - Read
- - Grep
- - Glob
- - Bash(git:*)
----
-
-# Code Review Skill
-
-当前 Skill 根目录: ${SKILL_ROOT}
-
-## 你的角色
-
-你是一个代码审查专家,负责按照团队规范审查代码变更。
-
-## 审查流程
-
-1. 首先使用 `git diff` 查看变更范围
-2. 阅读 ${SKILL_ROOT}/references/checklist.md 了解检查项
-3. 逐个检查代码变更
-4. 输出结构化的审查报告
-
-## 资源位置
-
-- 检查清单: ${SKILL_ROOT}/references/checklist.md
-- 代码规范: ${SKILL_ROOT}/references/style-guide.md
-- Lint 脚本: ${SKILL_ROOT}/scripts/lint.sh
-
-## 输出格式
-
-对于每个发现,使用以下格式:
-- **位置**: 文件:行号
-- **级别**: 🔴 严重 | 🟡 建议 | 🟢 优化
-- **描述**: 问题描述
-- **建议**: 修复建议
-```
-
-### 2.4 Frontmatter 字段
-
-| 字段 | 类型 | 必需 | 说明 |
-|------|------|------|------|
-| `name` | string | 是 | Skill 唯一标识符,**必须与目录名一致** |
-| `description` | string | 是 | 简短描述,用于模型理解何时使用 |
-| `allowedTools` | string[] | 否 | 额外需要的工具,与用户已启用工具取并集 |
-
-### 2.5 Name 与目录名一致性
-
-`name` 是 Skill 的唯一标识符,**必须与目录名保持一致**。
-
-**安装时验证**:
-```
-解析 SKILL.md frontmatter 获取 name
- │
- ▼
-比较 name 与源目录名
- │
- ├── 一致 → 直接拷贝到 ~/.deepchat/skills/{name}/
- │
- └── 不一致 → 自动重命名目录为 name,拷贝到 ~/.deepchat/skills/{name}/
-```
-
-**持久化**:
-- `activeSkills` 持久化 `name`(而非目录路径)
-- 恢复会话时通过 `name` 查找 Skill
-
-### 2.6 路径变量
-
-Skill 内容中支持以下变量替换:
-
-| 变量 | 说明 |
-|------|------|
-| `${SKILL_ROOT}` | 当前 Skill 的根目录路径 |
-| `${SKILLS_DIR}` | Skills 总目录路径 (~/.deepchat/skills/) |
-
-**示例**:
-```markdown
-读取检查清单: ${SKILL_ROOT}/references/checklist.md
-执行脚本: ${SKILL_ROOT}/scripts/lint.sh
-```
-
----
-
-## 3. 数据模型
-
-### 3.1 SkillMetadata(元数据)
-
-启动时从 SKILL.md frontmatter 提取,始终保留在内存中。
-
-```typescript
-interface SkillMetadata {
- name: string // 唯一标识符(来自 frontmatter,与目录名一致)
- description: string // 描述文本
- path: string // SKILL.md 完整路径
- skillRoot: string // Skill 根目录路径
- allowedTools?: string[] // 额外需要的工具(可选)
-}
-```
-
-### 3.2 SkillState(会话状态)
-
-与 Conversation 关联的激活状态,需要持久化。
-
-```typescript
-interface SkillState {
- conversationId: string // 关联的会话 ID
- activeSkills: Set // 已激活的 Skill 名称集合
-}
-```
-
-**持久化方案**:
-
-存储在 Conversation 记录中(SQLite chat.db):
-
-```typescript
-// conversations 表扩展字段
-interface Conversation {
- // ... 现有字段
- activeSkills?: string[] // JSON 序列化的激活 Skill 名称数组
-}
-```
-
-**生命周期**:
-- 新建会话:`activeSkills = []`
-- 激活/停用 Skill:更新内存状态 + 持久化到数据库
-- 恢复会话:从数据库加载 `activeSkills`,过滤掉已不存在的 Skill
-
-### 3.3 SkillContent(完整内容)
-
-激活后加载,注入到系统提示。
-
-```typescript
-interface SkillContent {
- name: string
- content: string // SKILL.md 完整内容(含 frontmatter 后的正文)
-}
-```
-
----
-
-## 4. 工具定义
-
-### 4.1 skill_list
-
-列出所有可用 Skills 及其激活状态。
-
-```typescript
-{
- name: "skill_list",
- description: "列出所有可用的 skills 及其当前激活状态",
- parameters: {
- type: "object",
- properties: {},
- required: []
- }
-}
-
-// 返回示例
-{
- "skills": [
- { "name": "code-review", "description": "代码审查", "active": true },
- { "name": "refactor", "description": "代码重构", "active": false }
- ]
-}
-```
-
-### 4.2 skill_control
-
-激活或停用指定 Skill。
-
-```typescript
-{
- name: "skill_control",
- description: "激活或停用一个 skill。激活后,该 skill 的完整指令将在下一轮对话中生效",
- parameters: {
- type: "object",
- properties: {
- action: {
- type: "string",
- enum: ["activate", "deactivate"],
- description: "操作类型"
- },
- skill_name: {
- type: "string",
- description: "skill 名称"
- }
- },
- required: ["action", "skill_name"]
- }
-}
-
-// 返回示例
-{
- "success": true,
- "message": "Skill 'code-review' activated. Instructions will be loaded in next turn."
-}
-```
-
----
-
-## 5. Agent Loop 集成
-
-### 5.1 Context 构建流程
-
-```
-每轮 Loop 开始前:
-
-1. 构建基础系统提示
- │
-2. 附加 Skills Metadata 列表(含 Skills 根目录路径)
- │ "## Available Skills
- │ Skills directory: ~/.deepchat/skills/
- │ You can activate these skills using skill_control tool:
- │ - code-review: 代码审查
- │ - refactor: 代码重构"
- │
-3. 检查 SkillState.activeSkills
- │
-4. 对每个激活的 Skill,加载完整内容
- │ 读取 SKILL.md 正文部分,替换 ${SKILL_ROOT} 等变量
- │
-5. 附加到系统提示
- │ "## Active Skills
- │ [所有激活 Skill 的内容依次列出]"
- │
-6. 继续正常的 Agent Loop
-```
-
-### 5.2 状态生命周期
-
-```
-新建会话
- │
- ▼
-SkillState = { conversationId, activeSkills: new Set() }
- │
- ▼
-持久化空状态到 Conversation.activeSkills
- │
- ▼
-用户消息 → Agent Loop
- │
- ▼
-模型调用 skill_control({ action: "activate", skill_name: "code-review" })
- │
- ▼
-SkillState.activeSkills.add("code-review")
- │
- ▼
-持久化更新到 Conversation.activeSkills
- │
- ▼
-当前轮继续执行(Skill 内容尚未加载)
- │
- ▼
-下一轮 Loop 开始
- │
- ▼
-检测到 "code-review" 激活 → 加载 SKILL.md 内容 → 注入系统提示
- │
- ▼
-模型看到完整 Skill 指令,按指令行动
-
-
-恢复会话(用户重新打开历史会话)
- │
- ▼
-从 Conversation.activeSkills 加载持久化状态
- │
- ▼
-过滤掉已不存在的 Skill 名称
- │
- ▼
-SkillState = { conversationId, activeSkills: 过滤后的集合 }
- │
- ▼
-继续正常的 Agent Loop(已激活 Skill 内容会被加载)
-```
-
-### 5.3 工具列表构建
-
-当 Skill 定义了 `allowedTools` 时,与用户已启用的工具取**并集**:
-
-```
-LLM 请求时工具列表构建:
- │
- ▼
-获取用户已启用的工具列表 (userEnabledTools)
- │
- ▼
-遍历所有激活的 Skills
- │
- ▼
-对每个 Skill,获取 allowedTools
- │
- ▼
-最终工具列表 = userEnabledTools ∪ skill1.allowedTools ∪ skill2.allowedTools ∪ ...
-```
-
-**说明**:
-- `allowedTools` 是**扩展**工具列表,而非限制
-- 提示词中提到的工具和 tools 属性中暴露的工具是两个概念
-- 提示词可以引导模型使用某些工具,但实际可用工具由 tools 列表决定
-
----
-
-## 6. 组件设计
-
-### 6.1 SkillPresenter
-
-核心协调器,负责 Skill 的发现、管理和内容加载。
-
-| 方法 | 职责 |
-|------|------|
-| `installBuiltinSkills()` | 首次启动时安装内置 Skills 到用户目录 |
-| `installFromFolder(folderPath)` | 从本地文件夹安装 Skill |
-| `installFromZip(zipPath)` | 从本地 zip 文件安装 Skill |
-| `installFromUrl(url)` | 从 URL 下载并安装 Skill |
-| `uninstallSkill(name)` | 卸载指定 Skill(删除文件夹) |
-| `discoverSkills()` | 扫描 skills 目录,提取 Metadata |
-| `getMetadataList()` | 返回所有 Skill 的 Metadata 列表 |
-| `getMetadataPrompt()` | 生成 Metadata 列表的文本(注入 Context) |
-| `loadSkillContent(name)` | 读取指定 Skill 的完整 SKILL.md 内容,替换路径变量 |
-| `getSkillsDir()` | 获取 Skills 根目录路径 |
-| `watchSkillFiles()` | 监控 Skill 文件变化,触发热加载 |
-| `getActiveSkillsAllowedTools(conversationId)` | 获取会话中激活 Skills 声明的额外工具列表 |
-| `getActiveSkills(conversationId)` | 获取会话的激活 Skill 列表(从持久化加载) |
-| `setActiveSkills(conversationId, skills)` | 更新会话的激活 Skill 列表(持久化) |
-| `validateSkillNames(names)` | 过滤掉已不存在的 Skill 名称 |
-
-### 6.2 SkillTools
-
-暴露给模型的工具实现。
-
-| 方法 | 职责 |
-|------|------|
-| `handleSkillList(state)` | 处理 skill_list 工具调用 |
-| `handleSkillControl(state, action, name)` | 处理 skill_control 工具调用 |
-
-### 6.3 职责边界
-
-```
-SkillPresenter SkillTools AgentLoop
- │ │ │
- │ 提供数据 │ 提供工具 │ 管理状态
- │ - Metadata │ - skill_list │ - SkillState
- │ - Content │ - skill_control │ - Context 构建
- │ │ │
-```
-
----
-
-## 7. 与现有系统集成
-
-### 7.1 与 ConfigPresenter
-
-```
-ConfigPresenter
- │
- └── getSkillSettings()
- ├── skillsPath: string // Skills 目录路径 (~/.deepchat/skills/)
- └── enableSkills: boolean // 全局开关
-```
-
-### 7.2 enableSkills 开关行为
-
-`enableSkills` 是 Skills 系统的全局开关,控制整个功能的启用/禁用。
-
-**enableSkills = true(启用)**:
-- 注册 `skill_list` / `skill_control` 工具
-- 注入 Skills Metadata 列表到 Context
-- 注入激活 Skill 的完整内容到 Context
-- 合并激活 Skills 的 `allowedTools` 到工具列表
-- 正常处理 Skill 激活/停用
-
-**enableSkills = false(禁用)**:
-- **不注册** `skill_list` / `skill_control` 工具(模型无法看到和调用)
-- **不注入** Metadata 列表到 Context
-- **不注入** 激活 Skill 内容到 Context
-- **不合并** `allowedTools` 到工具列表
-- **保留**持久化的 `activeSkills` 状态(视为挂起,重新启用后恢复)
-
-```
-用户关闭 enableSkills
- │
- ▼
-Skills 功能挂起(非清除)
- │
- ├── 工具不可见
- ├── 提示词无 Skills 相关内容
- ├── 工具列表不含 Skills 的 allowedTools
- │
- └── Conversation.activeSkills 保持不变
- │
- ▼
- 用户重新开启 enableSkills
- │
- ▼
- 恢复之前的激活状态
-```
-
-### 7.3 热加载机制
-
-```
-启动时:
- │
- ├── discoverSkills() → 扫描并加载所有 Metadata
- │
- └── watchSkillFiles() → 启动文件监控
- │
- ▼
- 监控 ~/.deepchat/skills/ 目录
- │
- ▼
- 文件变化事件 (add/change/unlink)
- │
- ▼
- 重新解析受影响的 SKILL.md
- │
- ▼
- 更新内存中的 Metadata
- │
- ▼
- 发送 SKILL_EVENTS.METADATA_UPDATED 事件
-```
-
-### 7.4 与 McpPresenter
-
-```
-构建 LLM 请求时的工具列表:
-
-SkillPresenter McpPresenter
- │ │
- │ getActiveSkillsAllowedTools() │
- │ ─────────────────────────────► │
- │ │
- │ 返回 ["Read", "Bash(git:*)"] │
- │ ◄───────────────────────────── │
- │ │
- │ mergeTools(userEnabled, skillAllowed)
- │ │
- │ 返回合并后的工具列表 │
- │ ◄───────────────────────────── │
-```
-
-### 7.5 与 AgentLoopHandler
-
-```
-AgentLoopHandler
- │
- ├── 检查 enableSkills 配置
- │ │
- │ └── 仅当 enableSkills = true 时:
- │ │
- │ ├── 初始化 SkillState
- │ │
- │ ├── 注册 skill_list / skill_control 工具
- │ │
- │ ├── preparePromptContent() 中:
- │ │ ├── 附加 Metadata 列表
- │ │ └── 附加激活 Skill 的完整内容
- │ │
- │ └── 处理工具调用时:
- │ └── 路由到 SkillTools
- │
- └── enableSkills = false 时:
- └── 跳过所有 Skills 相关逻辑
-```
-
-### 7.6 与 EventBus
-
-```typescript
-const SKILL_EVENTS = {
- DISCOVERED: 'skill:discovered', // Skills 发现完成
- METADATA_UPDATED: 'skill:metadata-updated', // Metadata 热加载更新
- INSTALLED: 'skill:installed', // Skill 安装完成
- UNINSTALLED: 'skill:uninstalled', // Skill 卸载完成
- ACTIVATED: 'skill:activated', // Skill 被激活
- DEACTIVATED: 'skill:deactivated' // Skill 被停用
-}
-```
-
----
-
-## 8. 多 Skill 激活
-
-多个 Skill 同时激活时,内容在同一个板块中依次列出:
-
-```
-## Active Skills
-
-[code-review 的 SKILL.md 内容]
-
-[refactor 的 SKILL.md 内容]
-```
diff --git a/docs/archives/skills-system/research.md b/docs/archives/skills-system/research.md
deleted file mode 100644
index 84cb3cff4..000000000
--- a/docs/archives/skills-system/research.md
+++ /dev/null
@@ -1,477 +0,0 @@
-# Skills 系统调研报告
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 概述
-
-### 1.1 什么是 Skills 系统
-
-Skills 系统是一种 AI 助手的可扩展性机制,允许 AI Agent 动态发现和应用专业知识、工作流程和能力。与传统的 Tools(执行动作)不同,Skills 提供**专业知识**和**程序性知识**,教会 AI Agent 如何处理特定领域或任务。
-
-### 1.2 核心特征
-
-| 特征 | 说明 |
-|------|------|
-| **Model-invoked** | AI Agent 基于语义匹配自动决定何时使用 Skill |
-| **Progressive Disclosure** | 启动时仅加载元数据,激活时才加载完整内容 |
-| **Domain Expertise** | 编码专业知识和行为模式,而非仅仅是可执行函数 |
-| **Token Efficient** | 仅元数据(名称+描述)在启动时加载,完整内容按需加载 |
-| **Composable** | 将通用 Agent 转换为特定领域的专家 |
-
-### 1.3 Skills vs Tools vs MCP 对比
-
-| 维度 | Skills | MCP Tools | Custom Prompts |
-|------|--------|-----------|----------------|
-| **提供内容** | "怎么做" - 专业知识、方法论 | "做什么" - 能力、数据访问 | 模板化的提示词 |
-| **焦点** | 程序性知识 | 外部集成 | 文本生成 |
-| **加载方式** | 渐进式(元数据 → 完整内容) | 预先加载(完整 Schema) | 按需加载 |
-| **Token 成本** | 低(~50 元数据 + 2-5K 激活时) | 高(90+ 工具可达 50K+) | 中等 |
-| **调用方式** | 语义匹配描述 | 函数调用按名称 | @ 提及或 LLM 调用 |
-| **内容类型** | 指令、工作流、模式 | 可执行函数 | 模板文本 |
-
----
-
-## 2. 业界实现模式分析
-
-### 2.1 Claude Code Skills 系统
-
-Claude Code 实现了最完整的 Skills 系统,是本次调研的主要参考。
-
-#### 2.1.1 文件结构
-
-```
-.claude/skills/
-├── skill-name/
-│ ├── SKILL.md (必需) # 元数据 + 指令
-│ ├── scripts/ # 可执行脚本
-│ ├── references/ # 按需加载的文档
-│ └── assets/ # 输出模板/资源
-```
-
-#### 2.1.2 SKILL.md 格式
-
-```yaml
----
-name: skill-name
-description: 描述 Skill 的用途和触发条件(用于语义匹配)
-allowed-tools: [可选] Read, Bash, Grep # 工具限制
-model: [可选] claude-sonnet-4
----
-
-# Skill 指令
-[Markdown 格式的 AI Agent 指令]
-```
-
-#### 2.1.3 发现流程
-
-```
-1. 启动: 扫描 ~/.claude/skills/ 和 .claude/skills/
-2. 加载: 仅解析 YAML frontmatter(名称 + 描述)
-3. 呈现: Skills 可用于语义匹配
-4. 匹配: 用户请求 → LLM 匹配描述
-5. 确认: 用户看到确认提示
-6. 激活: 加载完整 SKILL.md 正文
-7. 执行: 遵循指令,按需加载 references
-```
-
-#### 2.1.4 多级作用域
-
-```typescript
-const skillPaths = [
- '/enterprise/skills/', // 优先级 1: 企业级
- '~/.claude/skills/', // 优先级 2: 个人
- '.claude/skills/', // 优先级 3: 项目
- 'plugins/*/skills/' // 优先级 4: 插件提供
-];
-```
-
-### 2.2 LangChain Skills 模式
-
-LangChain 的 Skills 模式结合了渐进式加载和动态工具注册:
-
-```typescript
-class SkillManager {
- async loadSkill(skillName: string) {
- // 先加载元数据
- const metadata = await loadMetadata(skillName);
-
- // Skill 激活时动态注册工具
- if (metadata.tools) {
- this.registerTools(metadata.tools);
- }
-
- // 加载完整指令
- const instructions = await loadInstructions(skillName);
- return { metadata, instructions };
- }
-}
-```
-
-**适用场景**:
-- 单个 Agent 有多个专业化领域
-- 不同技能之间没有严格约束
-- 不同团队独立开发能力
-
-### 2.3 VS Code Extension 模式
-
-VS Code 的扩展架构提供了隔离和安全性的参考:
-
-```
-主进程 (应用管理)
- ↓
-渲染进程 (UI)
- ↓
-Extension Host 进程 (隔离)
- ↓ (Extension API)
-扩展 (用户代码)
-```
-
-**关键原则**:
-- 扩展不直接访问 DOM
-- 通过消息传递通信 (IPC)
-- 定义良好、稳定的 Extension API
-- 激活事件触发按需加载
-
-### 2.4 工具注册表模式 (OpenAI/LangChain)
-
-传统的工具重型架构:
-
-```typescript
-interface Tool {
- name: string;
- description: string;
- parameters: JSONSchema;
- execute: (args: any) => Promise;
-}
-
-// 所有工具预先加载到上下文
-const toolRegistry = new ToolRegistry();
-toolRegistry.register(databaseTool);
-// ...50+ 工具 = 50K+ tokens
-```
-
-**问题**:Token 成本高,所有工具定义预先加载。
-
----
-
-## 3. Token 经济与性能
-
-### 3.1 传统工具重型架构
-
-```
-预先成本: 50,000 tokens (90+ 工具完整 Schema)
-每次请求: 所有工具在上下文中
-总计: 50K + 消息 tokens
-```
-
-### 3.2 Skills 架构
-
-```
-启动: 2,500 tokens (50 skills × 50 tokens 元数据)
-空闲: 2,500 tokens
-激活 (1 skill): 2,500 + 3,000 = 5,500 tokens
-节省: 空闲时 90%+ 减少
-```
-
-### 3.3 最佳实践
-
-1. **SKILL.md 保持在 500 行以下**
-2. **使用渐进式加载**(拆分到 references/)
-3. **执行脚本不加载**(0 token 成本)
-4. **按需加载 references**
-5. **避免重复**(信息只在 SKILL.md 或 references 中出现一次)
-
----
-
-## 4. 调用模式
-
-### 4.1 自动调用(基于上下文)
-
-- Agent 持续评估上下文与 Skill 描述的匹配度
-- 超过阈值时激活 Skill
-- 用户在完整加载前看到确认提示
-
-### 4.2 显式调用(斜杠命令)
-
-```bash
-# 手动调用
-> /review-code --strict
-
-# 背后可能激活 "code-review" skill
-```
-
-### 4.3 混合调用(Tool-Skill 桥接)
-
-```typescript
-// SlashCommand 工具允许程序化调用 Skill
-{
- name: "SlashCommand",
- description: "执行自定义斜杠命令",
- parameters: {
- command: "/skill-name",
- args: "附加参数"
- }
-}
-```
-
----
-
-## 5. 安全考虑
-
-### 5.1 沙箱隔离模式
-
-| 模式 | 描述 | 适用场景 |
-|------|------|----------|
-| **容器 (Docker/LXC)** | 标准隔离,无性能惩罚 | 受信代码 |
-| **MicroVM (Firecracker)** | 强隔离,独立内核 | 不受信代码 |
-| **用户模式内核 (gVisor)** | 应用与 OS 之间的安全屏障 | Kubernetes Agent |
-| **WebAssembly** | 默认拒绝权限模型 | 轻量级插件 |
-| **V8 Isolates** | 隔离的 V8 引擎实例 | 高密度、低启动 |
-
-### 5.2 权限控制模式
-
-#### Skill 级工具限制
-```yaml
----
-name: read-only-analysis
-description: 分析代码但不修改
-allowed-tools: Read, Grep, Glob # 禁止 Write, Edit, Bash
----
-```
-
-#### 白名单模式
-```yaml
-allowed-tools:
- - Bash(git status:*)
- - Bash(git add:*)
- - Bash(npm test:*)
-```
-
-#### 深度防御
-```
-层 1: 显式命令白名单
-层 2: 最小权限,时间限制 Token
-层 3: 沙箱隔离 (gVisor/Firecracker)
-层 4: 关键操作的人机协作
-层 5: 审计日志和监控
-```
-
----
-
-## 6. DeepChat 现有架构分析
-
-### 6.1 现有类似 Skill 的功能
-
-#### Custom Prompts 系统
-
-DeepChat 已有的 Custom Prompts 系统与 Skills 有 80% 的相似度:
-
-```typescript
-interface Prompt {
- id: string
- name: string
- description: string
- content?: string // 模板文本
- parameters?: Array<{ // 动态参数
- name: string
- description: string
- required: boolean
- }>
- files?: FileItem[]
- enabled?: boolean
- source?: 'local' | 'imported' | 'builtin'
- createdAt?: number
- updatedAt?: number
-}
-```
-
-**已有功能**:
-- ✅ 命名、可重用操作
-- ✅ 参数支持
-- ✅ 启用/禁用开关
-- ✅ 用户管理 UI
-- ✅ LLM 集成(AutoPromptingServer)
-- ✅ 缓存优化
-- ✅ 事件驱动架构
-
-#### AutoPromptingServer
-
-DeepChat 通过内置的 MCP 服务器暴露 Custom Prompts:
-
-```typescript
-// 服务器名称: deepchat-inmemory/custom-prompts-server
-// 暴露的工具:
-- get_template_parameters // 列出 Prompt 参数
-- fill_template // 用参数填充 Prompt
-```
-
-### 6.2 MCP 架构
-
-DeepChat 的 MCP 系统已经非常完善:
-
-```
-McpPresenter (核心协调器)
- ├── ServerManager (服务器生命周期)
- ├── ToolManager (工具执行和权限)
- ├── McpClient (单个服务器连接)
- └── InMemoryServers (内置服务器)
- ├── ArtifactsServer
- ├── SearchServers
- ├── KnowledgeServers
- ├── DeepResearchServer
- ├── AutoPromptingServer ← 已有 Skill-like 功能
- ├── ConversationSearchServer
- ├── MeetingServer
- └── AppleServer
-```
-
-### 6.3 工具执行数据流
-
-```
-用户消息 (Chat/Agent)
- ↓
-Agent Loop (llmProviderPresenter)
- ↓
-LLM 调用 agent.stream() 带工具定义
- ↓
-LLM 返回 tool_call 事件
- ↓
-ToolPresenter.callTool() 路由到:
- - MCP via McpPresenter.callTool()
- - Agent tools via AgentToolManager.callTool()
- ↓
-ToolManager 解析工具名 → 客户端映射
- ↓
-检查权限 (read/write/all)
- ↓
-在目标客户端执行
- ↓
-格式化响应
- ↓
-返回 Agent Loop → LLM 整合
-```
-
-### 6.4 配置系统
-
-DeepChat 使用 ElectronStore 持久化配置:
-
-```
-~/.../userData/
-├── app-settings.json # 主设置
-├── custom_prompts.json # Custom Prompts
-├── system_prompts.json # 系统提示词
-├── mcp-settings.json # MCP 配置
-├── model-config.json # 模型配置
-└── ...
-```
-
-**配置模式**:
-- Helper 模式:每个配置域有专门的 Helper 类
-- 代理模式:Renderer → Main 通过 IPC 代理通信
-- 缓存模式:高频读取的内存缓存
-- 事件驱动更新:通过 EventBus 广播到所有窗口
-
----
-
-## 7. Skills 系统与现有架构的集成点
-
-### 7.1 与 MCP 的关系
-
-Skills 可以**指导**工具使用方法论,而 MCP Tools 提供实际能力:
-
-```
-用户请求
- ↓
-AI Agent (带 Skills)
- ↓
-Skill 激活 → 提供领域专业知识
- ↓ ↓
-Skill 指令指导 → MCP Tool 执行
- ↓
- 外部系统
-```
-
-**示例**:
-- **Skill**: "按公司标准进行代码审查"(教方法论)
-- **MCP Tools**: Git 操作、文件读取、Lint(提供能力)
-- **结果**: Skill 指导 Agent 如何审查;Tools 提供代码访问
-
-### 7.2 自然扩展点
-
-1. **存储层**
- - 扩展 Prompt 接口或创建 SkillDefinition
- - 添加元数据(标签、分类、版本、依赖)
- - 存储在独立的 skills store 或扩展 prompts store
-
-2. **注册**
- - 使用类似 in-memory servers 的模式
- - 创建 skills builder/registry
- - 可能作为 MCP 服务器暴露
-
-3. **执行**
- - 如果基于 MCP,通过 ToolPresenter 路由
- - 如需更深集成,可直接调用
- - 通过现有系统进行权限检查
-
-4. **UI 集成**
- - 设置中的 Skills 面板
- - 专门的 Skills 浏览器
- - Skill 市场/发现
- - Skill 链式/组合 UI
-
-5. **事件广播**
- - 新增 SKILL_EVENTS 常量
- - 广播 Skill 注册/执行事件
- - 与现有 EventBus 集成
-
----
-
-## 8. 关键发现与建议
-
-### 8.1 DeepChat 的优势
-
-1. **模块化 Presenter 模式** - 清晰的关注点分离
-2. **事件驱动** - 通过 EventBus 松耦合
-3. **动态工具加载** - MCP 服务器运行时加载
-4. **多格式支持** - 工具可与任何 LLM 提供商配合
-5. **权限框架** - 内置访问控制
-6. **缓存** - 优化性能
-7. **统一工具接口** - 所有工具来源的单一入口
-
-### 8.2 Skills 系统的自然演进路径
-
-1. **起点**:Custom Prompts(已有 80%)
-2. **添加**:Skill 元数据和分类
-3. **暴露**:通过现有 MCP 基础设施
-4. **增强**:UI 专门的 Skills 管理
-5. **可选**:LLM 上下文外的直接 Skill 执行
-
-### 8.3 推荐的实现策略
-
-基于调研结果,建议采用**渐进式增强**策略:
-
-1. **Phase 1**: 扩展现有 Custom Prompts 为 Skills
-2. **Phase 2**: 添加文件系统发现机制
-3. **Phase 3**: 实现渐进式加载和语义匹配
-4. **Phase 4**: 添加权限控制和工具限制
-5. **Phase 5**: UI 增强和市场功能
-
----
-
-## 9. 参考资源
-
-### Skills 系统
-- [Claude Code Skills Documentation](https://code.claude.com/docs/en/skills)
-- [Claude Skills vs. MCP](https://intuitionlabs.ai/articles/claude-skills-vs-mcp)
-- [LangChain Skills](https://docs.langchain.com/oss/python/langchain/multi-agent/skills)
-
-### 架构模式
-- [Google Cloud: Agentic AI Design Patterns](https://cloud.google.com/architecture/choose-design-pattern-agentic-ai-system)
-- [VS Code Architecture](https://franz-ajit.medium.com/understanding-visual-studio-code-architecture-5fc411fca07)
-- [Plugin Architecture Pattern](https://www.devleader.ca/2023/09/07/plugin-architecture-design-pattern-a-beginners-guide-to-modularity/)
-
-### 安全与沙箱
-- [Sandboxes for AI](https://www.luiscardoso.dev/blog/sandboxes-for-ai)
-- [Google Agent Sandbox](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/agent-sandbox)
-- [E2B Code Interpreter](https://e2b.dev/)
diff --git a/docs/archives/skills-system/skills-syncing-antigravity.md b/docs/archives/skills-system/skills-syncing-antigravity.md
deleted file mode 100644
index 09dea0804..000000000
--- a/docs/archives/skills-system/skills-syncing-antigravity.md
+++ /dev/null
@@ -1,237 +0,0 @@
-# Google Antigravity Workflows 格式规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> 本文档是 [skills-syncing.md](./skills-syncing.md) 的子文档,描述 Google Antigravity Workflows 的格式规格和转换规则。
-
-## 1. 基本信息
-
-| 属性 | 值 |
-|------|-----|
-| 工具名称 | Antigravity (Google Project IDX) |
-| Workflows 目录 | `.agent/workflows/` |
-| 文件模式 | `*.md` |
-| 格式类型 | YAML frontmatter + Markdown |
-| Frontmatter | **有** (仅 description) |
-
-## 2. 目录结构
-
-```
-.agent/workflows/
-├── code-review.md
-├── generate-tests.md
-├── deploy.md
-└── refactor.md
-```
-
-**特点**:
-- 每个 Workflow 是**单个 Markdown 文件**
-- 文件名即 Workflow 名称
-- 不支持子文件夹结构
-- 使用 `## Steps` + 编号步骤结构
-
-## 3. Workflow 格式
-
-### 3.1 完整示例
-
-```markdown
----
-description: Generate unit tests for existing code
----
-
-## Steps
-
-### 1. Analyze Target File
-
-- Read the specified code file
-- Identify all exported functions and classes
-- Note any existing test patterns in the codebase
-
-### 2. Generate Test Cases
-
-For each exported function:
-- Create at least 3 test cases
-- Cover edge cases and error conditions
-- Use clear, descriptive test names
-
-### 3. Write Test File
-
-```javascript
-import { describe, it, expect } from 'vitest'
-import { functionName } from './target-file'
-
-describe('functionName', () => {
- it('should handle normal case', () => {
- // test implementation
- })
-})
-```
-
-### 4. Verify Tests
-
-Run the generated tests:
-```bash
-npm test
-```
-```
-
-### 3.2 结构说明
-
-| 部分 | 必需 | 说明 |
-|------|------|------|
-| YAML frontmatter | ❌ 否 | 可选,仅支持 `description` |
-| `description` | ❌ 否 | workflow 描述 |
-| `## Steps` | ✅ 是 | 步骤容器 |
-| `### N. Step Name` | ✅ 是 | 编号步骤 |
-
-### 3.3 Frontmatter 字段
-
-| 字段 | 类型 | 必需 | 说明 |
-|------|------|------|------|
-| `description` | string | ❌ 否 | Workflow 描述 |
-
-**注意**:Antigravity 的 frontmatter 仅支持 `description` 字段,不支持 `name`、`tools`、`model` 等。
-
-### 3.4 调用方式
-
-在 Project IDX / Antigravity 中:
-1. 打开命令面板
-2. 选择 "Run Workflow"
-3. 选择要运行的 workflow
-
-或在 AI 对话中提及 workflow 名称。
-
-## 4. 发现机制
-
-Antigravity 从以下位置发现 Workflows:
-
-1. **项目目录**:`.agent/workflows/`
-2. **全局配置**:用户级 workflows(如有)
-
-## 5. 与 DeepChat 的转换
-
-### 5.1 兼容性
-
-| 能力 | Antigravity | DeepChat | 转换 |
-|------|:-----------:|:--------:|------|
-| name | ✅ (从文件名) | ✅ | 提取文件名 |
-| description | ✅ | ✅ | 直接映射 |
-| instructions | ✅ (Steps) | ✅ | Steps 作为 instructions |
-| allowedTools | ❌ | ✅ | 导出时丢失 |
-| model | ❌ | ✅ | 导出时丢失 |
-| references/ | ❌ | ✅ | 导出时丢失 |
-| scripts/ | ❌ | ✅ | 导出时丢失 |
-
-### 5.2 导入转换 (Antigravity → DeepChat)
-
-```typescript
-function convertFromAntigravity(content: string, filename: string): DeepChatSkill {
- const { data, body } = grayMatter(content)
-
- // 从文件名提取 name
- const name = filename.replace('.md', '')
-
- return {
- name,
- description: data.description || '',
- instructions: body,
- allowedTools: undefined,
- model: undefined,
- references: undefined,
- scripts: undefined
- }
-}
-```
-
-### 5.3 导出转换 (DeepChat → Antigravity)
-
-```typescript
-function convertToAntigravity(skill: DeepChatSkill): string {
- let output = ''
-
- // 添加 frontmatter(如果有 description)
- if (skill.description) {
- output += `---\ndescription: ${skill.description}\n---\n\n`
- }
-
- // 检查 instructions 是否已有步骤结构
- if (skill.instructions.includes('## Steps') ||
- skill.instructions.includes('### 1.') ||
- skill.instructions.includes('### Step 1')) {
- output += skill.instructions
- } else {
- // 将整个 instructions 包装为步骤结构
- output += `## Steps\n\n### 1. Execute\n\n${skill.instructions}`
- }
-
- return output
-}
-```
-
-### 5.4 转换警告
-
-导出到 Antigravity 时:
-
-| 丢失内容 | 处理方式 |
-|----------|----------|
-| `allowedTools` | 静默丢失 |
-| `model` | 静默丢失 |
-| `references/` | 静默丢失 |
-| `scripts/` | 静默丢失 |
-
-导入到 DeepChat 时:
-
-| 转换内容 | 处理方式 |
-|----------|----------|
-| 步骤结构 | 保持原样作为 instructions |
-| 无额外信息丢失 | - |
-
-### 5.5 步骤结构智能检测
-
-在导出时,检测现有步骤结构:
-
-```typescript
-function hasStepsStructure(content: string): boolean {
- const patterns = [
- /^## Steps/m,
- /^### \d+\./m,
- /^### Step \d+/m
- ]
- return patterns.some(p => p.test(content))
-}
-```
-
-如果原 instructions 已有步骤结构,保持原样;否则包装为单一步骤。
-
-## 6. 与 Windsurf 的比较
-
-Antigravity 和 Windsurf 的格式非常相似:
-
-| 特性 | Antigravity | Windsurf |
-|------|-------------|----------|
-| 目录 | `.agent/workflows/` | `.windsurf/workflows/` |
-| Frontmatter | 有 (仅 description) | 无 |
-| 步骤结构 | `## Steps` + `### N.` | `## Steps` + `### N.` |
-| 描述位置 | frontmatter | 标题后首段 |
-
-### 6.1 通用转换
-
-由于格式相似,可以创建通用的步骤式 Markdown 适配器:
-
-```typescript
-class StepsMarkdownAdapter {
- parse(content: string, hasDescription: boolean): ParsedWorkflow {
- // 通用解析逻辑
- }
-
- serialize(workflow: ParsedWorkflow, includeDescription: boolean): string {
- // 通用序列化逻辑
- }
-}
-```
-
-## 7. 参考资源
-
-- [Customize Antigravity Rules and Workflows](https://atamel.dev/posts/2025/11-25_customize_antigravity_rules_workflows/)
-- [Project IDX Documentation](https://idx.dev/docs)
diff --git a/docs/archives/skills-system/skills-syncing-claude-code.md b/docs/archives/skills-system/skills-syncing-claude-code.md
deleted file mode 100644
index f1507839c..000000000
--- a/docs/archives/skills-system/skills-syncing-claude-code.md
+++ /dev/null
@@ -1,203 +0,0 @@
-# Claude Code Skills 格式规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> 本文档是 [skills-syncing.md](./skills-syncing.md) 的子文档,描述 Claude Code Skills 的格式规格和转换规则。
-
-## 1. 基本信息
-
-| 属性 | 值 |
-|------|-----|
-| 工具名称 | Claude Code |
-| Skills 目录 | `~/.claude/skills/` (用户级) 或 `.claude/skills/` (项目级) |
-| 文件模式 | `*/SKILL.md` |
-| 格式类型 | YAML frontmatter + Markdown |
-| Frontmatter | **必需** |
-
-## 2. 目录结构
-
-```
-~/.claude/skills/
-├── code-review/
-│ ├── SKILL.md # 必需:元数据 + 指令
-│ ├── references/ # 可选:参考文档(按需加载)
-│ │ ├── style-guide.md
-│ │ └── checklist.md
-│ ├── scripts/ # 可选:可执行脚本
-│ │ └── lint.sh
-│ └── assets/ # 可选:输出模板/资源
-│ └── report-template.md
-├── refactor/
-│ └── SKILL.md
-└── my-skill/
- └── SKILL.md
-```
-
-**特点**:
-- 每个 Skill 是一个**文件夹**,而非单个文件
-- 文件夹名应与 `name` 字段一致
-- 支持 `references/`、`scripts/`、`assets/` 子文件夹
-
-## 3. SKILL.md 格式
-
-### 3.1 完整示例
-
-```markdown
----
-name: code-review
-description: Reviews code changes according to team standards. Use when the user asks for a code review, PR review, or wants feedback on their changes.
-allowed-tools: Read, Grep, Glob, Bash(git:*)
-license: MIT
----
-
-# Code Review
-
-## Your Role
-
-You are a code review expert responsible for reviewing code changes according to team standards.
-
-## Review Process
-
-1. First use `git diff` to see the scope of changes
-2. Read ${SKILL_ROOT}/references/checklist.md to understand check items
-3. Review each code change
-4. Output a structured review report
-
-## Resources
-
-- Checklist: ${SKILL_ROOT}/references/checklist.md
-- Style Guide: ${SKILL_ROOT}/references/style-guide.md
-- Lint Script: ${SKILL_ROOT}/scripts/lint.sh
-
-## Output Format
-
-For each finding, use the following format:
-- **Location**: file:line
-- **Level**: 🔴 Critical | 🟡 Suggestion | 🟢 Optimization
-- **Description**: Issue description
-- **Suggestion**: Fix suggestion
-```
-
-### 3.2 Frontmatter 字段
-
-| 字段 | 类型 | 必需 | 约束 | 说明 |
-|------|------|------|------|------|
-| `name` | string | ✅ 是 | 最长 64 字符,仅小写字母/数字/连字符 | Skill 唯一标识符 |
-| `description` | string | ✅ 是 | 最长 1024 字符,非空 | 描述用途和触发条件,用于语义匹配 |
-| `allowed-tools` | string/array | ❌ 否 | - | 限制可用工具,省略则不限制 |
-| `license` | string | ❌ 否 | - | 许可证类型 |
-
-### 3.3 allowed-tools 格式
-
-支持两种写法:
-
-**字符串格式**:
-```yaml
-allowed-tools: Read, Grep, Glob, Bash(git:*)
-```
-
-**数组格式**:
-```yaml
-allowed-tools:
- - Read
- - Grep
- - Glob
- - Bash(git:*)
-```
-
-**工具限制语法**:
-- `Bash` - 允许所有 Bash 命令
-- `Bash(git:*)` - 仅允许 git 开头的命令
-- `Bash(npm test:*)` - 仅允许 npm test 开头的命令
-
-### 3.4 路径变量
-
-Skill 内容中支持以下变量替换:
-
-| 变量 | 说明 |
-|------|------|
-| `${SKILL_ROOT}` | 当前 Skill 的根目录路径 |
-
-## 4. 发现机制
-
-Claude Code 按以下顺序扫描 Skills:
-
-1. `~/.config/claude/skills/` - 用户配置目录
-2. `~/.claude/skills/` - 用户 HOME 目录
-3. `.claude/skills/` - 项目目录
-4. 插件提供的 Skills
-5. 内置 Skills
-
-**优先级**:后发现的同名 Skill 覆盖先发现的。
-
-## 5. 已知问题
-
-根据 [Issue #9817](https://github.com/anthropics/claude-code/issues/9817):
-
-- Frontmatter 格式敏感,多行 description 可能导致发现失败
-- 发现失败时无错误提示(静默失败)
-- YAML 对缩进敏感,建议使用 2 空格缩进
-
-**建议**:
-```yaml
-# ✅ 推荐:单行 description
-description: Reviews code changes according to team standards.
-
-# ❌ 避免:多行 description
-description: |
- Reviews code changes according to team standards.
- Use when the user asks for a code review.
-```
-
-## 6. 与 DeepChat 的转换
-
-### 6.1 兼容性
-
-| 能力 | Claude Code | DeepChat | 转换 |
-|------|:-----------:|:--------:|------|
-| name | ✅ | ✅ | 直接映射 |
-| description | ✅ | ✅ | 直接映射 |
-| allowed-tools | ✅ | ✅ | 字段名转换 `allowed-tools` ↔ `allowedTools` |
-| references/ | ✅ | ✅ | 直接复制 |
-| scripts/ | ✅ | ✅ | 直接复制 |
-| assets/ | ✅ | ⚠️ | 复制到 references/ 或忽略 |
-| license | ✅ | ❌ | 忽略 |
-
-### 6.2 导入转换 (Claude Code → DeepChat)
-
-```typescript
-function convertFromClaudeCode(skill: ClaudeCodeSkill): DeepChatSkill {
- return {
- name: skill.name,
- description: skill.description,
- // 字段名转换
- allowedTools: skill['allowed-tools'],
- instructions: skill.body,
- // 子文件夹直接复制
- references: skill.references,
- scripts: skill.scripts
- }
-}
-```
-
-### 6.3 导出转换 (DeepChat → Claude Code)
-
-```typescript
-function convertToClaudeCode(skill: DeepChatSkill): string {
- const frontmatter = {
- name: skill.name,
- description: skill.description,
- // 字段名转换
- ...(skill.allowedTools && { 'allowed-tools': skill.allowedTools })
- }
-
- return `---\n${yaml.dump(frontmatter)}---\n\n${skill.instructions}`
-}
-```
-
-## 7. 参考资源
-
-- [Agent Skills - Claude Code Docs](https://code.claude.com/docs/en/skills)
-- [Skill authoring best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices)
-- [Anthropic Skills Repository](https://github.com/anthropics/skills)
diff --git a/docs/archives/skills-system/skills-syncing-copilot.md b/docs/archives/skills-system/skills-syncing-copilot.md
deleted file mode 100644
index d400bc49e..000000000
--- a/docs/archives/skills-system/skills-syncing-copilot.md
+++ /dev/null
@@ -1,247 +0,0 @@
-# GitHub Copilot Prompt Files 格式规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> 本文档是 [skills-syncing.md](./skills-syncing.md) 的子文档,描述 GitHub Copilot Prompt Files 的格式规格和转换规则。
-
-## 1. 基本信息
-
-| 属性 | 值 |
-|------|-----|
-| 工具名称 | GitHub Copilot |
-| Prompts 目录 | `.github/prompts/` |
-| 文件模式 | `*.prompt.md` |
-| 格式类型 | YAML frontmatter + Markdown |
-| Frontmatter | **可选** |
-
-## 2. 目录结构
-
-```
-.github/prompts/
-├── code-review.prompt.md
-├── refactor.prompt.md
-├── generate-tests.prompt.md
-└── explain-code.prompt.md
-```
-
-**特点**:
-- 每个 Prompt 是**单个 Markdown 文件**
-- 文件名必须以 `.prompt.md` 结尾
-- 文件名(不含扩展名)即 Prompt 名称
-- 不支持子文件夹结构
-- 支持 `#file` 引用语法
-
-## 3. Prompt File 格式
-
-### 3.1 完整示例
-
-```markdown
----
-description: Generate a comprehensive code review for the selected code
-agent: agent
-model: GPT-4o
-tools: ['githubRepo', 'search/codebase', 'read', 'edit']
----
-
-# Code Review
-
-Review the code changes and provide feedback on:
-- Code quality and readability
-- Potential bugs and edge cases
-- Performance implications
-- Security vulnerabilities
-
-## Context
-
-Use #file:'${file}' to reference the current file context.
-
-## Output Format
-
-Provide a structured review with:
-1. Summary of changes
-2. Issues found (by severity)
-3. Suggested improvements
-4. Overall assessment
-```
-
-### 3.2 Frontmatter 字段
-
-| 字段 | 类型 | 必需 | 说明 |
-|------|------|------|------|
-| `description` | string | ❌ 否 | 描述用途,用于提示列表显示 |
-| `agent` | string | ❌ 否 | 通常设为 `agent` |
-| `model` | string | ❌ 否 | 指定使用的模型(如 `GPT-4o`) |
-| `tools` | string[] | ❌ 否 | 可用工具列表 |
-
-### 3.3 tools 数组
-
-支持的工具包括:
-
-| 工具名 | 说明 |
-|--------|------|
-| `githubRepo` | GitHub 仓库访问 |
-| `search/codebase` | 代码库搜索 |
-| `read` | 读取文件 |
-| `edit` | 编辑文件 |
-| `terminalLastCommand` | 终端最后命令 |
-| `runCommands` | 运行命令 |
-
-### 3.4 文件引用语法
-
-Copilot 支持特殊的 `#file` 引用语法:
-
-```markdown
-# Using file references
-Use #file:'src/utils.ts' to include file context.
-
-# Using variable
-Check #file:'${file}' for the current file.
-```
-
-### 3.5 调用方式
-
-在 VS Code 中:
-1. 打开命令面板 (`Ctrl/Cmd + Shift + P`)
-2. 输入 `@workspace /prompt-name`
-3. 或在 Chat 中直接输入 `/prompt-name`
-
-## 4. 发现机制
-
-GitHub Copilot 从以下位置发现 Prompt Files:
-
-1. **工作区目录**:`.github/prompts/`
-2. **用户全局目录**:`~/.github/prompts/`
-
-**注意**:Copilot 按字母顺序列出 prompts。
-
-## 5. 与 DeepChat 的转换
-
-### 5.1 兼容性
-
-| 能力 | GitHub Copilot | DeepChat | 转换 |
-|------|:--------------:|:--------:|------|
-| name | ✅ (从文件名) | ✅ | 提取文件名 |
-| description | ✅ | ✅ | 直接映射 |
-| instructions | ✅ | ✅ | Markdown body |
-| allowedTools | ✅ (tools) | ✅ | 工具名称映射 |
-| model | ✅ | ✅ | 直接映射 |
-| references/ | ⚠️ (#file 引用) | ✅ | 转换 #file 或内联 |
-| scripts/ | ❌ | ✅ | 导出时丢失 |
-
-### 5.2 工具名称映射
-
-DeepChat 与 Copilot 的工具名称需要映射:
-
-| DeepChat | GitHub Copilot |
-|----------|----------------|
-| Read | read |
-| Edit | edit |
-| Bash | runCommands |
-| Grep | search/codebase |
-| Glob | search/codebase |
-
-### 5.3 导入转换 (Copilot → DeepChat)
-
-```typescript
-function convertFromCopilot(content: string, filename: string): DeepChatSkill {
- const { data, body } = grayMatter(content)
-
- // 从文件名提取 name
- const name = filename.replace('.prompt.md', '')
-
- // 映射工具名称
- const allowedTools = data.tools?.map((tool: string) => {
- const mapping: Record = {
- 'read': 'Read',
- 'edit': 'Edit',
- 'runCommands': 'Bash',
- 'search/codebase': 'Grep'
- }
- return mapping[tool] || tool
- })
-
- // 处理 #file 引用 - 转换为 ${SKILL_ROOT}/references/
- const processedBody = body.replace(
- /#file:'([^']+)'/g,
- '${SKILL_ROOT}/references/$1'
- )
-
- return {
- name,
- description: data.description || '',
- instructions: processedBody,
- allowedTools,
- model: data.model,
- references: undefined,
- scripts: undefined
- }
-}
-```
-
-### 5.4 导出转换 (DeepChat → Copilot)
-
-```typescript
-function convertToCopilot(skill: DeepChatSkill): string {
- const frontmatter: Record = {}
-
- if (skill.description) {
- frontmatter.description = skill.description
- }
-
- frontmatter.agent = 'agent'
-
- if (skill.model) {
- frontmatter.model = skill.model
- }
-
- // 映射工具名称
- if (skill.allowedTools?.length) {
- const mapping: Record = {
- 'Read': 'read',
- 'Edit': 'edit',
- 'Bash': 'runCommands',
- 'Grep': 'search/codebase',
- 'Glob': 'search/codebase'
- }
- frontmatter.tools = skill.allowedTools.map(t => mapping[t] || t)
- }
-
- // 处理 references - 转换为 #file 引用
- let instructions = skill.instructions
- if (skill.references?.length) {
- instructions += '\n\n## References\n\n'
- for (const ref of skill.references) {
- instructions += `See #file:'${ref.relativePath}' for ${ref.name}\n`
- }
- }
-
- const yaml = Object.keys(frontmatter).length > 0
- ? `---\n${yamlDump(frontmatter)}---\n\n`
- : ''
-
- return yaml + instructions
-}
-```
-
-### 5.5 转换警告
-
-导出到 GitHub Copilot 时:
-
-| 丢失内容 | 处理方式 |
-|----------|----------|
-| `scripts/` | 静默丢失,无法映射 |
-| `references/` | 转换为 `#file` 引用(内容不会复制) |
-| 部分工具 | 映射到最接近的 Copilot 工具 |
-
-导入到 DeepChat 时:
-
-| 转换内容 | 处理方式 |
-|----------|----------|
-| `#file` 引用 | 转换为 `${SKILL_ROOT}/references/` 路径 |
-| 工具名称 | 映射到 DeepChat 工具名 |
-
-## 6. 参考资源
-
-- [Prompt Files - VS Code Docs](https://code.visualstudio.com/docs/copilot/customization/prompt-files)
-- [GitHub Copilot Chat Documentation](https://docs.github.com/en/copilot/github-copilot-chat)
diff --git a/docs/archives/skills-system/skills-syncing-cursor.md b/docs/archives/skills-system/skills-syncing-cursor.md
deleted file mode 100644
index 1bfccfc34..000000000
--- a/docs/archives/skills-system/skills-syncing-cursor.md
+++ /dev/null
@@ -1,184 +0,0 @@
-# Cursor Commands 格式规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> 本文档是 [skills-syncing.md](./skills-syncing.md) 的子文档,描述 Cursor Commands 的格式规格和转换规则。
-
-## 1. 基本信息
-
-| 属性 | 值 |
-|------|-----|
-| 工具名称 | Cursor |
-| Commands 目录 | `.cursor/commands/` (项目级) |
-| 文件模式 | `*.md` |
-| 格式类型 | 纯结构化 Markdown |
-| Frontmatter | **无** |
-
-## 2. 目录结构
-
-```
-.cursor/commands/
-├── code-review.md
-├── refactor.md
-├── lint-suite.md
-├── create-pr.md
-└── optimize-performance.md
-```
-
-**特点**:
-- 每个 Command 是**单个 Markdown 文件**
-- 文件名即 Command 名称
-- 不支持子文件夹或附属资源
-
-## 3. Command 格式
-
-### 3.1 完整示例
-
-```markdown
-# Code Review
-
-Brief description of what this command does - reviews code changes according to team standards.
-
-## Objective
-
-Perform a thorough code review of the current changes, checking for:
-- Code quality and readability
-- Potential bugs and edge cases
-- Performance implications
-- Security vulnerabilities
-
-## Requirements
-
-- Follow the project's coding standards
-- Check for proper error handling
-- Verify test coverage for new code
-- Ensure documentation is updated
-
-## Output
-
-Provide a structured review report with:
-1. Summary of changes reviewed
-2. List of issues found (categorized by severity)
-3. Suggested improvements
-4. Overall assessment
-```
-
-### 3.2 结构说明
-
-| 部分 | 必需 | 说明 |
-|------|------|------|
-| `# Title` | ✅ 是 | 一级标题,Command 显示名称 |
-| 首段描述 | ✅ 是 | 标题后的首段,简短描述用途 |
-| `## Objective` | ❌ 否 | 详细说明任务目标和预期结果 |
-| `## Requirements` | ❌ 否 | 具体要求、约束、编码标准 |
-| `## Output` | ❌ 否 | 描述 AI 应该产出什么 |
-
-### 3.3 调用方式
-
-在 Cursor 的 Agent 输入框中输入 `/` 触发命令选择:
-
-```
-> /code-review
-> /refactor
-> /lint-suite
-```
-
-## 4. 发现机制
-
-Cursor 从以下位置发现 Commands:
-
-1. `.cursor/commands/` - 项目目录
-2. 全局 Commands 库(用户级)
-3. Team Commands(通过 Cursor Dashboard 配置)
-
-## 5. 与 DeepChat 的转换
-
-### 5.1 兼容性
-
-| 能力 | Cursor | DeepChat | 转换 |
-|------|:------:|:--------:|------|
-| name | ✅ (从标题/文件名) | ✅ | 提取标题或文件名 |
-| description | ✅ (从首段) | ✅ | 提取首段 |
-| instructions | ✅ | ✅ | 合并所有 sections |
-| allowedTools | ❌ | ✅ | 导出时丢失 |
-| references/ | ❌ | ✅ | 可选择内联 |
-| scripts/ | ❌ | ✅ | 导出时丢失 |
-
-### 5.2 导入转换 (Cursor → DeepChat)
-
-```typescript
-function convertFromCursor(content: string, filename: string): DeepChatSkill {
- const lines = content.split('\n')
-
- // 提取标题
- const titleMatch = lines.find(l => l.startsWith('# '))
- const name = titleMatch
- ? titleMatch.replace('# ', '').toLowerCase().replace(/\s+/g, '-')
- : filename.replace('.md', '')
-
- // 提取首段作为 description
- const titleIndex = lines.findIndex(l => l.startsWith('# '))
- let description = ''
- for (let i = titleIndex + 1; i < lines.length; i++) {
- const line = lines[i].trim()
- if (line === '') continue
- if (line.startsWith('#')) break
- description = line
- break
- }
-
- // 剩余内容作为 instructions
- const instructions = content
-
- return {
- name,
- description,
- instructions,
- allowedTools: undefined, // Cursor 不支持
- references: undefined,
- scripts: undefined
- }
-}
-```
-
-### 5.3 导出转换 (DeepChat → Cursor)
-
-```typescript
-function convertToCursor(skill: DeepChatSkill): string {
- const title = skill.name
- .split('-')
- .map(w => w.charAt(0).toUpperCase() + w.slice(1))
- .join(' ')
-
- let output = `# ${title}\n\n`
- output += `${skill.description}\n\n`
- output += `## Objective\n\n`
- output += skill.instructions
-
- // 如果有 references,可选择内联
- if (skill.references?.length) {
- output += `\n\n## References\n\n`
- for (const ref of skill.references) {
- output += `### ${ref.name}\n\n${ref.content}\n\n`
- }
- }
-
- return output
-}
-```
-
-### 5.4 转换警告
-
-导出到 Cursor 时,以下内容会丢失:
-
-| 丢失内容 | 处理方式 |
-|----------|----------|
-| `allowedTools` | 静默丢失,无法映射 |
-| `scripts/` | 静默丢失,无法映射 |
-| `references/` | 可选择内联到 `## References` 或丢失 |
-
-## 6. 参考资源
-
-- [Commands - Cursor Docs](https://cursor.com/docs/agent/chat/commands)
-- [Cursor Custom Slash Commands](https://github.com/hamzafer/cursor-commands)
diff --git a/docs/archives/skills-system/skills-syncing-kiro.md b/docs/archives/skills-system/skills-syncing-kiro.md
deleted file mode 100644
index 4e8c1e1c2..000000000
--- a/docs/archives/skills-system/skills-syncing-kiro.md
+++ /dev/null
@@ -1,286 +0,0 @@
-# Kiro Steering Files 格式规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> 本文档是 [skills-syncing.md](./skills-syncing.md) 的子文档,描述 Kiro Steering Files 的格式规格和转换规则。
-
-## 1. 基本信息
-
-| 属性 | 值 |
-|------|-----|
-| 工具名称 | Kiro |
-| Steering 目录 | `.kiro/steering/` |
-| 文件模式 | `*.md` |
-| 格式类型 | YAML frontmatter + Markdown |
-| Frontmatter | **可选** |
-
-## 2. 目录结构
-
-```
-.kiro/steering/
-├── product.md # 产品概述(always included)
-├── tech.md # 技术栈(always included)
-├── structure.md # 项目结构(always included)
-├── react-components.md # 条件包含
-├── api-patterns.md # 按需引用
-└── testing-guidelines.md # 按需引用
-```
-
-**特点**:
-- 每个 Steering File 是**单个 Markdown 文件**
-- 文件名即 Steering File 名称
-- 不支持子文件夹结构
-- 支持三种**包含模式**(inclusion modes)
-- 支持 `#filename` 引用语法
-
-## 3. Steering File 格式
-
-### 3.1 完整示例
-
-**Always Inclusion(始终包含)**:
-```markdown
----
-title: Product Overview
-inclusion: always
----
-
-# Product Overview
-
-This is a React-based e-commerce platform with the following features:
-- User authentication
-- Product catalog
-- Shopping cart
-- Order management
-```
-
-**Conditional Inclusion(条件包含)**:
-```markdown
----
-title: React Component Guidelines
-inclusion: conditional
-file_patterns: ["*.jsx", "*.tsx", "src/components/**/*"]
----
-
-# React Component Guidelines
-
-## Naming Conventions
-- Use PascalCase for component names
-- Use camelCase for props
-
-## Structure
-- One component per file
-- Co-locate styles and tests
-```
-
-**On-Demand(按需引用)**:
-```markdown
----
-title: API Patterns
----
-
-# API Patterns
-
-## REST Endpoints
-- Use plural nouns for resources
-- Use HTTP verbs correctly
-
-## Error Handling
-- Return consistent error format
-- Include error codes
-```
-
-### 3.2 Frontmatter 字段
-
-| 字段 | 类型 | 必需 | 说明 |
-|------|------|------|------|
-| `title` | string | ❌ 否 | 显示名称,用于 `#filename` 引用 |
-| `inclusion` | string | ❌ 否 | 包含模式:`always` 或 `conditional` |
-| `file_patterns` | string[] | ❌ 否 | 仅在 `conditional` 模式下使用 |
-
-### 3.3 包含模式(Inclusion Modes)
-
-| 模式 | 触发条件 | 说明 |
-|------|----------|------|
-| `always` | 每次对话 | 始终包含在 AI 上下文中 |
-| `conditional` | 文件匹配 | 当操作的文件匹配 `file_patterns` 时包含 |
-| 无 `inclusion` | 手动引用 | 通过 `#filename` 显式引用 |
-
-### 3.4 file_patterns 语法
-
-支持 glob 模式:
-
-```yaml
-file_patterns:
- - "*.jsx" # 所有 JSX 文件
- - "*.tsx" # 所有 TSX 文件
- - "src/components/**/*" # components 目录下所有文件
- - "**/*.test.ts" # 所有测试文件
-```
-
-### 3.5 调用方式
-
-**自动包含**:
-- `always` 模式的文件自动包含
-- `conditional` 模式根据当前文件自动触发
-
-**手动引用**:
-在对话中使用 `#` 加文件名(不含 .md):
-```
-Check #api-patterns for the REST conventions
-Follow #react-components guidelines
-```
-
-## 4. 发现机制
-
-Kiro 从以下位置发现 Steering Files:
-
-1. **项目目录**:`.kiro/steering/`
-2. 递归扫描工作区内的 `.kiro/steering/` 目录
-
-**注意**:
-- 同名文件可能产生冲突
-- `always` 模式文件会增加每次对话的 token 消耗
-
-## 5. 与 DeepChat 的转换
-
-### 5.1 兼容性
-
-| 能力 | Kiro | DeepChat | 转换 |
-|------|:----:|:--------:|------|
-| name | ✅ (title/filename) | ✅ | 提取 title 或文件名 |
-| description | ❌ | ✅ | 导出时丢失 |
-| instructions | ✅ | ✅ | Markdown body |
-| allowedTools | ❌ | ✅ | 导出时丢失 |
-| model | ❌ | ✅ | 导出时丢失 |
-| references/ | ❌ | ✅ | 导出时丢失 |
-| scripts/ | ❌ | ✅ | 导出时丢失 |
-| inclusion | ✅ | ❌ | 导入时记录,导出时设置 |
-| file_patterns | ✅ | ❌ | 导入时记录,导出时设置 |
-
-### 5.2 导入转换 (Kiro → DeepChat)
-
-```typescript
-function convertFromKiro(content: string, filename: string): DeepChatSkill {
- const { data, body } = grayMatter(content)
-
- // 从 title 或文件名提取 name
- const name = data.title
- ? data.title.toLowerCase().replace(/\s+/g, '-')
- : filename.replace('.md', '')
-
- // 将 inclusion 信息嵌入到 instructions 开头
- let instructions = body
- if (data.inclusion) {
- const inclusionNote = `\n`
- if (data.file_patterns?.length) {
- instructions = `${inclusionNote}\n\n${body}`
- } else {
- instructions = `${inclusionNote}\n${body}`
- }
- }
-
- return {
- name,
- description: '', // Kiro 没有专门的 description 字段
- instructions,
- allowedTools: undefined,
- model: undefined,
- references: undefined,
- scripts: undefined,
- // 保存 Kiro 特有信息用于后续导出
- _kiro: {
- inclusion: data.inclusion,
- filePatterns: data.file_patterns
- }
- }
-}
-```
-
-### 5.3 导出转换 (DeepChat → Kiro)
-
-```typescript
-interface KiroExportOptions {
- inclusion?: 'always' | 'conditional'
- filePatterns?: string[]
-}
-
-function convertToKiro(skill: DeepChatSkill, options?: KiroExportOptions): string {
- const frontmatter: Record = {}
-
- // 转换 name 为 title
- const title = skill.name
- .split('-')
- .map(w => w.charAt(0).toUpperCase() + w.slice(1))
- .join(' ')
-
- frontmatter.title = title
-
- // 设置 inclusion 模式
- if (options?.inclusion) {
- frontmatter.inclusion = options.inclusion
- if (options.inclusion === 'conditional' && options.filePatterns?.length) {
- frontmatter.file_patterns = options.filePatterns
- }
- }
-
- // 如果有 description,嵌入到 instructions 开头
- let instructions = skill.instructions
- if (skill.description) {
- instructions = `> ${skill.description}\n\n${instructions}`
- }
-
- const yaml = Object.keys(frontmatter).length > 0
- ? `---\n${yamlDump(frontmatter)}---\n\n`
- : ''
-
- return yaml + instructions
-}
-```
-
-### 5.4 转换警告
-
-导出到 Kiro 时:
-
-| 丢失内容 | 处理方式 |
-|----------|----------|
-| `description` | 嵌入到 instructions 开头(作为引用块) |
-| `allowedTools` | 静默丢失 |
-| `model` | 静默丢失 |
-| `references/` | 静默丢失 |
-| `scripts/` | 静默丢失 |
-
-导入到 DeepChat 时:
-
-| 转换内容 | 处理方式 |
-|----------|----------|
-| `inclusion` 模式 | 作为注释嵌入 instructions |
-| `file_patterns` | 作为注释嵌入 instructions |
-| 无 `description` | 设为空字符串 |
-
-### 5.5 导出选项 UI
-
-由于 Kiro 支持 inclusion 模式,导出时应提供选项:
-
-```
-┌────────────────────────────────────────────────────────────┐
-│ Kiro 导出选项 │
-├────────────────────────────────────────────────────────────┤
-│ │
-│ 包含模式: │
-│ ○ 按需引用 (默认,通过 #name 手动引用) │
-│ ○ 始终包含 (每次对话自动包含) │
-│ ○ 条件包含 (根据文件模式自动包含) │
-│ │
-│ 文件模式 (仅条件包含): │
-│ ┌──────────────────────────────────────────────────────┐ │
-│ │ *.tsx, *.jsx, src/components/**/* │ │
-│ └──────────────────────────────────────────────────────┘ │
-│ │
-└────────────────────────────────────────────────────────────┘
-```
-
-## 6. 参考资源
-
-- [Kiro Steering Documentation](https://kiro.dev/docs/steering/)
-- [Kiro Getting Started](https://kiro.dev/docs/getting-started/)
diff --git a/docs/archives/skills-system/skills-syncing-tasks.md b/docs/archives/skills-system/skills-syncing-tasks.md
deleted file mode 100644
index 7486949c4..000000000
--- a/docs/archives/skills-system/skills-syncing-tasks.md
+++ /dev/null
@@ -1,430 +0,0 @@
-# Skills 跨工具同步开发任务清单
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 概述
-
-本文档基于 [skills-syncing.md](./skills-syncing.md) 及其子文档制定,用于开发跟踪。
-
-**预计工作量**:中等复杂度功能,涉及 Main/Renderer 双端开发
-
-**支持的工具**:
-- Claude Code - YAML frontmatter + Markdown(子文件夹结构)
-- Cursor - 纯 Markdown(单文件)
-- Windsurf - 步骤式 Markdown(单文件)
-- GitHub Copilot - YAML frontmatter + Markdown(单文件)
-- Kiro - YAML frontmatter + Markdown(单文件,支持 inclusion 模式)
-- Antigravity - YAML frontmatter + Markdown(单文件,步骤式)
-
----
-
-## Phase 1: 类型定义与核心数据模型
-
-### 1.1 共享类型定义
-
-- [x] **1.1.1** 创建 `src/shared/types/skillSync.ts` 文件骨架
- ```typescript
- // 包含:CanonicalSkill, SkillReference, SkillScript
- // ExternalToolConfig, FormatCapabilities
- // ScanResult, ExternalSkillInfo
- // ImportPreview, ExportPreview
- // ConflictStrategy, SyncResult
- ```
-
-- [x] **1.1.2** 定义 `CanonicalSkill` 中间格式接口
- - name, description, instructions
- - allowedTools, model, tags
- - references, scripts
- - source 来源信息
-
-- [x] **1.1.3** 定义 `ExternalToolConfig` 工具配置接口
- - id, name, skillsDir, filePattern, format
- - capabilities (FormatCapabilities)
-
-- [x] **1.1.4** 定义 `FormatCapabilities` 格式能力接口
- - hasFrontmatter, supportsName, supportsDescription
- - supportsTools, supportsModel
- - supportsSubfolders, supportsReferences, supportsScripts
-
-- [x] **1.1.5** 定义同步操作相关类型
- - ScanResult, ExternalSkillInfo
- - ImportPreview, ExportPreview
- - ConflictStrategy (enum)
- - SyncResult
-
-- [x] **1.1.6** 定义 `ISkillSyncPresenter` 接口
- - scanExternalTools(), scanTool()
- - previewImport(), executeImport()
- - previewExport(), executeExport()
- - getRegisteredTools(), isToolAvailable()
-
-- [x] **1.1.7** 在 `src/shared/types/index.d.ts` 中导出 skillSync 类型
-
----
-
-## Phase 2: Format Adapters 实现(插件化)
-
-### 2.1 适配器基础架构
-
-- [x] **2.1.1** 创建 `src/main/presenter/skillSyncPresenter/adapters/` 目录结构
-- [x] **2.1.2** 定义 `IFormatAdapter` 接口
- ```typescript
- interface IFormatAdapter {
- id: string
- name: string
- parse(content: string, context: ParseContext): CanonicalSkill
- serialize(skill: CanonicalSkill): string
- detect(content: string): boolean
- getCapabilities(): FormatCapabilities
- }
- ```
-- [x] **2.1.3** 创建 `adapters/index.ts` 适配器注册表
-
-### 2.2 Claude Code 适配器
-
-- [x] **2.2.1** 创建 `adapters/claudeCodeAdapter.ts`
-- [x] **2.2.2** 实现 parse() - YAML frontmatter 解析
- - 处理 `allowed-tools` ↔ `allowedTools` 字段名转换
- - 支持字符串和数组两种 allowed-tools 格式
-- [x] **2.2.3** 实现 serialize() - 生成 SKILL.md 格式
-- [x] **2.2.4** 实现 references/ 和 scripts/ 子文件夹处理
-- [x] **2.2.5** 单元测试
-
-### 2.3 Cursor 适配器
-
-- [x] **2.3.1** 创建 `adapters/cursorAdapter.ts`
-- [x] **2.3.2** 实现 parse() - 纯 Markdown 解析
- - 从 `# Title` 提取 name
- - 从首段提取 description
-- [x] **2.3.3** 实现 serialize() - 生成 Cursor 格式
- - 可选将 references 内联到 `## References`
-- [x] **2.3.4** 单元测试
-
-### 2.4 Windsurf 适配器
-
-- [x] **2.4.1** 创建 `adapters/windsurfAdapter.ts`
-- [x] **2.4.2** 实现 parse() - 步骤式 Markdown 解析
- - 从标题提取 name(去除 " Workflow" 后缀)
- - 提取 `## Steps` 之前的描述
-- [x] **2.4.3** 实现 serialize() - 生成步骤式格式
- - 智能检测是否已有步骤结构
-- [x] **2.4.4** 单元测试
-
-### 2.5 GitHub Copilot 适配器
-
-- [x] **2.5.1** 创建 `adapters/copilotAdapter.ts`
-- [x] **2.5.2** 实现 parse() - YAML frontmatter 解析
- - 工具名称映射(read → Read, runCommands → Bash 等)
- - 处理 `#file:` 引用语法
-- [x] **2.5.3** 实现 serialize() - 生成 `.prompt.md` 格式
- - 反向工具名称映射
- - 将 references 转换为 `#file:` 引用
-- [x] **2.5.4** 单元测试
-
-### 2.6 Kiro 适配器
-
-- [x] **2.6.1** 创建 `adapters/kiroAdapter.ts`
-- [x] **2.6.2** 实现 parse() - 处理 inclusion 模式
- - 保存 inclusion 和 file_patterns 信息
-- [x] **2.6.3** 实现 serialize() - 生成 Kiro 格式
- - 支持设置 inclusion 模式
- - 将 description 嵌入为引用块
-- [x] **2.6.4** 定义 Kiro 导出选项接口
-- [x] **2.6.5** 单元测试
-
-### 2.7 Antigravity 适配器
-
-- [x] **2.7.1** 创建 `adapters/antigravityAdapter.ts`
-- [x] **2.7.2** 实现 parse() - 与 Windsurf 类似,但有 frontmatter
-- [x] **2.7.3** 实现 serialize() - 生成步骤式格式
-- [x] **2.7.4** 单元测试
-
----
-
-## Phase 3: 核心服务实现
-
-### 3.1 ToolScanner 工具扫描器
-
-- [x] **3.1.1** 创建 `src/main/presenter/skillSyncPresenter/toolScanner.ts`
-- [x] **3.1.2** 实现工具配置注册表
- ```typescript
- const EXTERNAL_TOOLS: ExternalToolConfig[] = [
- { id: 'claude-code', name: 'Claude Code', skillsDir: '~/.claude/skills/', ... },
- { id: 'cursor', name: 'Cursor', skillsDir: '.cursor/commands/', ... },
- // ...
- ]
- ```
-- [x] **3.1.3** 实现 `scanTool(toolId)` - 扫描单个工具目录
- - 检查目录是否存在
- - 根据 filePattern 匹配文件
- - 提取基本元信息
-- [x] **3.1.4** 实现 `scanExternalTools()` - 扫描所有已注册工具
-- [x] **3.1.5** 实现路径安全验证(防止路径遍历)
-- [x] **3.1.6** 单元测试
-
-### 3.2 FormatConverter 格式转换引擎
-
-- [x] **3.2.1** 创建 `src/main/presenter/skillSyncPresenter/formatConverter.ts`
-- [x] **3.2.2** 实现 `parseExternal()` - 根据格式选择适配器解析
-- [x] **3.2.3** 实现 `serializeToExternal()` - 序列化为外部格式
-- [x] **3.2.4** 实现 `serializeToSkillMd()` - 序列化为 DeepChat SKILL.md
-- [x] **3.2.5** 实现 `getConversionWarnings()` - 获取转换警告
- - 检测功能丢失(如 allowedTools 导出到 Cursor)
-- [x] **3.2.6** 单元测试
-
-### 3.3 SkillSyncPresenter 主类
-
-- [x] **3.3.1** 创建 `src/main/presenter/skillSyncPresenter/index.ts` 骨架
-- [x] **3.3.2** 创建 `src/main/presenter/skillSyncPresenter/types.ts` 内部类型
-
-- [x] **3.3.3** 实现扫描功能
- - `scanExternalTools()` - 扫描所有外部工具
- - `scanTool(toolId)` - 扫描指定工具
-
-- [x] **3.3.4** 实现导入功能
- - `previewImport()` - 预览导入,检测冲突
- - `executeImport()` - 执行导入,处理冲突策略
- - 调用 SkillPresenter.installFromFolder()
-
-- [x] **3.3.5** 实现导出功能
- - `previewExport()` - 预览导出,检测冲突
- - `executeExport()` - 执行导出,写入目标目录
- - 调用 SkillPresenter.loadSkillContent()
-
-- [x] **3.3.6** 实现工具查询
- - `getRegisteredTools()` - 获取所有已注册工具配置
- - `isToolAvailable()` - 检查工具目录是否存在
-
-- [x] **3.3.7** 实现冲突处理逻辑
- - SKIP - 跳过
- - OVERWRITE - 覆盖
- - RENAME - 重命名(添加后缀)
-
-- [x] **3.3.8** 集成测试
-
-### 3.4 Presenter 注册与 IPC
-
-- [x] **3.4.1** 在 `src/main/presenter/index.ts` 中注册 SkillSyncPresenter
-- [x] **3.4.2** ~~在 `src/preload/presenter.ts` 中暴露 API~~ (不需要,使用动态路由)
-- [x] **3.4.3** 更新 `src/shared/presenter.d.ts` 类型定义
-
----
-
-## Phase 4: 事件系统
-
-### 4.1 事件定义
-
-- [x] **4.1.1** 在 `src/main/events.ts` 中添加 SKILL_SYNC_EVENTS
- ```typescript
- const SKILL_SYNC_EVENTS = {
- SCAN_STARTED: 'skill-sync:scan-started',
- SCAN_COMPLETED: 'skill-sync:scan-completed',
- IMPORT_STARTED: 'skill-sync:import-started',
- IMPORT_PROGRESS: 'skill-sync:import-progress',
- IMPORT_COMPLETED: 'skill-sync:import-completed',
- EXPORT_STARTED: 'skill-sync:export-started',
- EXPORT_PROGRESS: 'skill-sync:export-progress',
- EXPORT_COMPLETED: 'skill-sync:export-completed'
- }
- ```
-
-- [x] **4.1.2** 在相应操作时发送事件
-
----
-
-## Phase 5: UI 实现
-
-### 5.1 组件目录结构
-
-- [x] **5.1.1** 创建 `src/renderer/settings/components/skills/SkillSyncDialog/` 目录
-- [x] **5.1.2** 规划组件文件
- ```
- SkillSyncDialog/
- ├── SkillSyncDialog.vue # 同步向导主组件
- ├── ImportWizard.vue # 导入向导
- ├── ExportWizard.vue # 导出向导
- ├── ToolSelector.vue # 工具选择器
- ├── SkillSelector.vue # Skill 选择器
- ├── ConflictResolver.vue # 冲突处理
- └── SyncResult.vue # 结果展示
- ```
-
-### 5.2 入口集成
-
-- [x] **5.2.1** 修改 `SkillsHeader.vue`,添加"同步"下拉菜单
- - "从其他工具导入..."
- - "导出到其他工具..."
-
-### 5.3 主对话框组件
-
-- [x] **5.3.1** 创建 `SkillSyncDialog.vue`
- - 管理导入/导出模式切换
- - 控制向导步骤流程
-
-### 5.4 导入向导
-
-- [x] **5.4.1** 创建 `ImportWizard.vue`
- - Step 1: 选择来源工具
- - Step 2: 选择 Skills
- - Step 3: 预览与冲突处理
-
-- [x] **5.4.2** 创建 `ToolSelector.vue`
- - 显示已检测到的工具列表
- - 显示每个工具的 Skills 数量
- - 支持自定义路径
-
-- [x] **5.4.3** 创建 `SkillSelector.vue`
- - 复选框列表选择 Skills
- - 显示冲突警告标识
- - 支持全选/取消全选
-
-- [x] **5.4.4** 创建 `ConflictResolver.vue`
- - 单个冲突的处理选项(覆盖/跳过/重命名)
- - 批量冲突处理
-
-- [x] **5.4.5** 导入流程进度展示
-
-### 5.5 导出向导
-
-- [x] **5.5.1** 创建 `ExportWizard.vue`
- - Step 1: 选择要导出的 Skills
- - Step 2: 选择目标工具
- - Step 3: 预览与确认
-
-- [x] **5.5.2** 实现转换警告展示
- - 显示功能丢失警告
- - 显示转换后内容预览
-
-- [x] **5.5.3** 实现 Kiro 特殊导出选项
- - inclusion 模式选择
- - file_patterns 输入
-
-### 5.6 结果展示
-
-- [x] **5.6.1** 创建 `SyncResult.vue`
- - 成功/跳过/失败统计
- - 详细列表展示
-
----
-
-## Phase 6: 国际化
-
-### 6.1 i18n 键值
-
-- [x] **6.1.1** 添加中文 i18n keys (`zh-CN`)
- ```json
- {
- "settings.skills.sync": "同步",
- "settings.skills.sync.import": "从其他工具导入...",
- "settings.skills.sync.export": "导出到其他工具...",
- // ... 完整键值见 skills-syncing.md 第 6 节
- }
- ```
-
-- [x] **6.1.2** 添加英文 i18n keys (`en-US`)
-
-- [x] **6.1.3** 运行 `pnpm run i18n` 检查完整性
-
----
-
-## Phase 7: 安全与测试
-
-### 7.1 安全验证
-
-- [x] **7.1.1** 实现路径安全验证
- - 防止路径遍历攻击(../)
- - 验证目录在预期范围内
-
-- [x] **7.1.2** 实现内容安全
- - 限制文件大小
- - YAML 解析使用安全选项
-
-- [x] **7.1.3** 实现权限检查
- - 导出时检查目标目录写权限
- - 导入时检查源目录读权限
-
-### 7.2 单元测试
-
-- [x] **7.2.1** Format Adapters 测试(各适配器)
-- [x] **7.2.2** ToolScanner 测试
-- [x] **7.2.3** FormatConverter 测试
-- [x] **7.2.4** SkillSyncPresenter 测试
-
-### 7.3 集成测试
-
-- [x] **7.3.1** 完整导入流程测试
-- [x] **7.3.2** 完整导出流程测试
-- [x] **7.3.3** 冲突处理测试
-
-### 7.4 代码质量
-
-- [x] **7.4.1** 运行 `pnpm run format && pnpm run lint && pnpm run typecheck`
-- [ ] **7.4.2** 代码审查
-
----
-
-## 依赖关系
-
-```
-Phase 1 (类型定义)
- │
- └── 1.1 共享类型 ───────────────────────────────────────────────┐
- │
-Phase 2 (Format Adapters) │
- │ │
- ├── 2.1 适配器基础架构 ◄─────────────────────────────────────────┤
- │ │
- ├── 2.2 Claude Code 适配器 ◄────────────────────────────────────┤
- ├── 2.3 Cursor 适配器 ◄─────────────────────────────────────────┤
- ├── 2.4 Windsurf 适配器 ◄───────────────────────────────────────┤
- ├── 2.5 Copilot 适配器 ◄────────────────────────────────────────┤
- ├── 2.6 Kiro 适配器 ◄───────────────────────────────────────────┤
- └── 2.7 Antigravity 适配器 ◄────────────────────────────────────┘
- │
- ▼
-Phase 3 (核心服务)
- │
- ├── 3.1 ToolScanner ◄── Phase 2
- ├── 3.2 FormatConverter ◄── Phase 2
- ├── 3.3 SkillSyncPresenter ◄── 3.1, 3.2
- └── 3.4 IPC 注册 ◄── 3.3
- │
- ▼
-Phase 4 (事件系统) ◄── Phase 3
- │
- ▼
-Phase 5 (UI 实现)
- │
- ├── 5.1-5.2 目录结构与入口
- ├── 5.3 主对话框
- ├── 5.4 导入向导
- ├── 5.5 导出向导
- └── 5.6 结果展示
- │
- ▼
-Phase 6 (国际化) ◄── Phase 5
- │
- ▼
-Phase 7 (安全与测试) ◄── All Phases
-```
-
----
-
-## 里程碑
-
-| 里程碑 | 完成标准 |
-|--------|----------|
-| **M1: 类型与适配器** | 所有 Format Adapters 实现并通过测试 |
-| **M2: 核心服务** | SkillSyncPresenter 完成,能扫描、导入、导出 |
-| **M3: UI 完成** | 导入/导出向导可用,冲突处理完善 |
-| **M4: 发布就绪** | i18n 完成,安全验证通过,测试覆盖 |
-
----
-
-## 备注
-
-- 开发过程中如有设计变更,及时更新 skills-syncing.md 和相关子文档
-- 每个任务完成后在本文档标记 `[x]`
-- 建议按 Phase 顺序推进,Phase 内可并行
-- 适配器开发可并行进行,由不同开发者负责
diff --git a/docs/archives/skills-system/skills-syncing-windsurf.md b/docs/archives/skills-system/skills-syncing-windsurf.md
deleted file mode 100644
index 85a24ef6f..000000000
--- a/docs/archives/skills-system/skills-syncing-windsurf.md
+++ /dev/null
@@ -1,231 +0,0 @@
-# Windsurf Workflows 格式规格
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> 本文档是 [skills-syncing.md](./skills-syncing.md) 的子文档,描述 Windsurf Workflows 的格式规格和转换规则。
-
-## 1. 基本信息
-
-| 属性 | 值 |
-|------|-----|
-| 工具名称 | Windsurf |
-| Workflows 目录 | `.windsurf/workflows/` |
-| 文件模式 | `*.md` |
-| 格式类型 | 步骤式 Markdown |
-| Frontmatter | **无** |
-
-## 2. 目录结构
-
-```
-.windsurf/workflows/
-├── code-review.md
-├── deploy.md
-├── pr-review.md
-└── run-tests.md
-```
-
-**特点**:
-- 每个 Workflow 是**单个 Markdown 文件**
-- 文件名即 Workflow 名称(用于 `/` 调用)
-- 不支持子文件夹或附属资源
-- 支持多级目录发现
-
-## 3. Workflow 格式
-
-### 3.1 完整示例
-
-```markdown
-# Code Review Workflow
-
-A workflow to systematically review code changes and provide structured feedback.
-
-## Steps
-
-### 1. Get the Changes
-
-Run the following command to see all changes:
-```bash
-git diff HEAD~1
-```
-
-### 2. Analyze Each File
-
-For each changed file:
-- Check code style and formatting
-- Look for potential bugs
-- Verify error handling
-- Check for security issues
-
-### 3. Check Test Coverage
-
-Ensure new code has appropriate tests:
-```bash
-npm run test:coverage
-```
-
-### 4. Generate Review Report
-
-Output a structured report with:
-- Summary of changes
-- Issues found (by severity)
-- Recommendations
-- Overall assessment
-```
-
-### 3.2 结构说明
-
-| 部分 | 必需 | 说明 |
-|------|------|------|
-| `# Title` | ✅ 是 | 一级标题,Workflow 显示名称 |
-| 描述段落 | ✅ 是 | 标题后的描述文本 |
-| `## Steps` | ✅ 是 | 步骤容器 |
-| `### N. Step Name` | ✅ 是 | 编号步骤,按顺序执行 |
-
-### 3.3 调用方式
-
-在 Cascade 中输入 `/` 加 workflow 名称:
-
-```
-> /code-review
-> /deploy
-> /pr-review
-```
-
-## 4. 发现机制
-
-Windsurf 自动从多个位置发现 Workflows:
-
-1. **当前工作区**:`.windsurf/workflows/`
-2. **子目录**:递归搜索所有 `.windsurf/workflows/`
-3. **Git 仓库根目录**:向上搜索到 git root
-4. **多工作区**:去重并显示最短相对路径
-
-**企业功能**:
-- 系统级 Workflows(全局可用,不可修改)
-- 管理员权限控制
-
-## 5. 高级特性
-
-### 5.1 Workflow 链式调用
-
-Workflows 可以调用其他 Workflows:
-
-```markdown
-# Full CI Pipeline
-
-## Steps
-
-### 1. Run Linting
-Call /lint-check
-
-### 2. Run Tests
-Call /run-tests
-
-### 3. Deploy
-Call /deploy-staging
-```
-
-### 5.2 常见用例
-
-- 代码格式化(Prettier, Black)
-- Linting(ESLint, Flake8)
-- 单元测试和 E2E 测试
-- 部署流程
-- 安全漏洞扫描
-
-## 6. 与 DeepChat 的转换
-
-### 6.1 兼容性
-
-| 能力 | Windsurf | DeepChat | 转换 |
-|------|:--------:|:--------:|------|
-| name | ✅ (从标题/文件名) | ✅ | 提取标题或文件名 |
-| description | ✅ (从首段) | ✅ | 提取首段 |
-| instructions | ✅ (Steps) | ✅ | Steps 作为 instructions |
-| allowedTools | ❌ | ✅ | 导出时丢失 |
-| model | ❌ | ✅ | 导出时丢失 |
-| references/ | ❌ | ✅ | 可选择内联 |
-| scripts/ | ❌ | ✅ | 导出时丢失 |
-
-### 6.2 导入转换 (Windsurf → DeepChat)
-
-```typescript
-function convertFromWindsurf(content: string, filename: string): DeepChatSkill {
- const lines = content.split('\n')
-
- // 提取标题
- const titleMatch = lines.find(l => l.startsWith('# '))
- const name = titleMatch
- ? titleMatch.replace('# ', '').replace(' Workflow', '').toLowerCase().replace(/\s+/g, '-')
- : filename.replace('.md', '')
-
- // 提取描述(标题和 ## Steps 之间的内容)
- const titleIndex = lines.findIndex(l => l.startsWith('# '))
- const stepsIndex = lines.findIndex(l => l.startsWith('## Steps'))
-
- let description = ''
- for (let i = titleIndex + 1; i < stepsIndex; i++) {
- const line = lines[i].trim()
- if (line && !line.startsWith('#')) {
- description = line
- break
- }
- }
-
- // Steps 内容作为 instructions
- const instructions = stepsIndex >= 0
- ? lines.slice(stepsIndex).join('\n')
- : content
-
- return {
- name,
- description,
- instructions,
- allowedTools: undefined,
- references: undefined,
- scripts: undefined
- }
-}
-```
-
-### 6.3 导出转换 (DeepChat → Windsurf)
-
-```typescript
-function convertToWindsurf(skill: DeepChatSkill): string {
- const title = skill.name
- .split('-')
- .map(w => w.charAt(0).toUpperCase() + w.slice(1))
- .join(' ')
-
- let output = `# ${title} Workflow\n\n`
- output += `${skill.description}\n\n`
- output += `## Steps\n\n`
-
- // 检查 instructions 是否已有步骤结构
- if (skill.instructions.includes('### 1.') || skill.instructions.includes('### Step 1')) {
- output += skill.instructions
- } else {
- // 整体作为单个步骤
- output += `### 1. Execute\n\n${skill.instructions}`
- }
-
- return output
-}
-```
-
-### 6.4 转换警告
-
-导出到 Windsurf 时,以下内容会丢失:
-
-| 丢失内容 | 处理方式 |
-|----------|----------|
-| `allowedTools` | 静默丢失 |
-| `model` | 静默丢失 |
-| `scripts/` | 静默丢失 |
-| `references/` | 可内联到步骤中或丢失 |
-
-## 7. 参考资源
-
-- [Workflows - Windsurf Docs](https://docs.windsurf.com/windsurf/cascade/workflows)
-- [Windsurf Workflows Guide](https://www.kzsoftworks.com/blog/windsurf-workflows-from-prompt-chaos-to-productive-focus)
diff --git a/docs/archives/skills-system/skills-syncing.md b/docs/archives/skills-system/skills-syncing.md
deleted file mode 100644
index b90647c70..000000000
--- a/docs/archives/skills-system/skills-syncing.md
+++ /dev/null
@@ -1,721 +0,0 @@
-# Skills 跨工具同步设计文档
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 概述
-
-### 1.1 背景
-
-不同的 AI Agent 工具都有自己的 Skills/Commands/Workflows 系统,但格式和存储位置各不相同。用户在多个工具之间切换时,希望能够复用已有的 Skills。
-
-### 1.2 设计目标
-
-| 目标 | 描述 | 优先级 |
-|------|------|--------|
-| **双向同步** | 支持从其他工具导入和导出到其他工具 | P0 |
-| **格式转换** | 自动处理不同工具间的格式差异 | P0 |
-| **冲突处理** | 提供清晰的冲突解决机制 | P0 |
-| **批量操作** | 支持一次性同步多个 Skills | P1 |
-| **增量同步** | 仅同步有变化的 Skills | P2 |
-
-### 1.3 非目标
-
-- 实时双向自动同步(需用户主动触发)
-- 跨工具的 Skill 依赖解析
-- 云端同步服务
-- 工具间的功能完全对等(部分工具功能可能无法完全转换)
-
-### 1.4 支持的工具
-
-| 工具 | 规格文档 |
-|------|----------|
-| Claude Code | [skills-syncing-claude-code.md](./skills-syncing-claude-code.md) |
-| Cursor | [skills-syncing-cursor.md](./skills-syncing-cursor.md) |
-| Windsurf | [skills-syncing-windsurf.md](./skills-syncing-windsurf.md) |
-| Antigravity | [skills-syncing-antigravity.md](./skills-syncing-antigravity.md) |
-| GitHub Copilot | [skills-syncing-copilot.md](./skills-syncing-copilot.md) |
-| Kiro | [skills-syncing-kiro.md](./skills-syncing-kiro.md) |
-
----
-
-## 2. 数据模型
-
-### 2.1 统一中间格式 (Canonical Skill Format)
-
-所有工具的 Skill 在转换时,先转为统一的中间格式:
-
-```typescript
-/**
- * 统一的 Skill 中间格式
- * 用于在不同工具格式之间转换
- */
-interface CanonicalSkill {
- // 基础元数据
- name: string // 唯一标识符
- description: string // 描述文本
-
- // 内容
- instructions: string // 主要指令内容(Markdown)
-
- // 可选元数据
- allowedTools?: string[] // 工具限制
- model?: string // 指定模型
- tags?: string[] // 标签分类
-
- // 附属资源
- references?: SkillReference[] // 参考文档
- scripts?: SkillScript[] // 脚本文件
-
- // 来源信息
- source?: {
- tool: string // 来源工具标识
- originalPath: string // 原始路径
- originalFormat: string // 原始格式
- }
-}
-
-interface SkillReference {
- name: string // 文件名
- content: string // 文件内容
- relativePath: string // 相对路径
-}
-
-interface SkillScript {
- name: string // 脚本名
- content: string // 脚本内容
- relativePath: string // 相对路径
-}
-```
-
-### 2.2 外部工具配置
-
-```typescript
-/**
- * 外部工具配置接口
- * 每个工具的具体配置在对应的子文档中定义
- */
-interface ExternalToolConfig {
- id: string // 工具唯一标识
- name: string // 显示名称
- skillsDir: string // 相对于 HOME 的路径
- filePattern: string // 文件匹配模式 (glob)
- format: string // 文件格式类型
- capabilities: FormatCapabilities // 格式能力
-}
-
-/**
- * 格式能力定义
- */
-interface FormatCapabilities {
- hasFrontmatter: boolean // 是否有 YAML frontmatter
- supportsName: boolean // 支持 name 字段
- supportsDescription: boolean // 支持 description 字段
- supportsTools: boolean // 支持工具限制
- supportsModel: boolean // 支持模型指定
- supportsSubfolders: boolean // 支持子文件夹结构
- supportsReferences: boolean // 支持 references/
- supportsScripts: boolean // 支持 scripts/
-}
-```
-
-### 2.3 同步操作类型
-
-```typescript
-/**
- * 扫描结果
- */
-interface ScanResult {
- toolId: string
- toolName: string
- available: boolean // 目录是否存在
- skillsDir: string // 完整路径
- skills: ExternalSkillInfo[] // 发现的 Skills
- error?: string // 扫描错误
-}
-
-interface ExternalSkillInfo {
- name: string
- description?: string
- path: string // 文件/文件夹路径
- format: string // 检测到的格式
- lastModified: Date // 最后修改时间
-}
-
-/**
- * 导入预览
- */
-interface ImportPreview {
- skill: CanonicalSkill // 转换后的 Skill
- source: ExternalSkillInfo // 来源信息
- conflict?: {
- existingSkill: SkillMetadata // 已存在的同名 Skill
- strategy: ConflictStrategy // 建议的处理策略
- }
- warnings: string[] // 转换警告(如丢失功能)
-}
-
-/**
- * 导出预览
- */
-interface ExportPreview {
- skill: SkillMetadata // 要导出的 Skill
- targetTool: string // 目标工具 ID
- targetPath: string // 目标路径
- convertedContent: string // 转换后的内容预览
- warnings: string[] // 转换警告
- conflict?: {
- existingPath: string // 已存在的文件
- strategy: ConflictStrategy
- }
-}
-
-/**
- * 冲突处理策略
- */
-enum ConflictStrategy {
- SKIP = 'skip', // 跳过
- OVERWRITE = 'overwrite', // 覆盖
- RENAME = 'rename', // 重命名(添加后缀)
- MERGE = 'merge' // 合并(仅适用于部分场景)
-}
-
-/**
- * 同步操作结果
- */
-interface SyncResult {
- success: boolean
- imported: number // 成功导入数量
- exported: number // 成功导出数量
- skipped: number // 跳过数量
- failed: Array<{
- skill: string
- reason: string
- }>
-}
-```
-
----
-
-## 3. 架构设计
-
-### 3.1 模块架构
-
-```
-┌─────────────────────────────────────────────────────────────────────┐
-│ SkillSyncPresenter │
-│ 统一的同步协调器,管理导入/导出流程 │
-├─────────────────────────────────────────────────────────────────────┤
-│ │
-│ ┌───────────────────┐ ┌───────────────────┐ │
-│ │ ToolScanner │ │ FormatConverter │ │
-│ │ 扫描外部工具目录 │ │ 格式转换引擎 │ │
-│ └─────────┬─────────┘ └─────────┬─────────┘ │
-│ │ │ │
-│ ▼ ▼ │
-│ ┌─────────────────────────────────────────────────────────────┐ │
-│ │ Format Adapters (插件化) │ │
-│ │ 每个外部工具一个 Adapter,负责解析和序列化该工具的格式 │ │
-│ └─────────────────────────────────────────────────────────────┘ │
-│ │
-└─────────────────────────────────────────────────────────────────────┘
- │
- │ 调用
- ▼
-┌─────────────────────────────────────────────────────────────────────┐
-│ SkillPresenter │
-│ 现有的 Skill 管理器(安装/卸载/读写) │
-└─────────────────────────────────────────────────────────────────────┘
-```
-
-### 3.2 核心组件接口
-
-#### SkillSyncPresenter
-
-同步功能的主入口,协调扫描、转换和写入:
-
-```typescript
-interface ISkillSyncPresenter {
- // 扫描
- scanExternalTools(): Promise
- scanTool(toolId: string): Promise
-
- // 导入(从外部工具 → DeepChat)
- previewImport(toolId: string, skillNames: string[]): Promise
- executeImport(previews: ImportPreview[], strategies: Record): Promise
-
- // 导出(从 DeepChat → 外部工具)
- previewExport(skillNames: string[], targetToolId: string): Promise
- executeExport(previews: ExportPreview[], strategies: Record): Promise
-
- // 工具配置
- getRegisteredTools(): ExternalToolConfig[]
- isToolAvailable(toolId: string): Promise
-}
-```
-
-#### FormatConverter
-
-格式转换引擎,处理不同格式间的转换:
-
-```typescript
-interface IFormatConverter {
- // 解析外部格式 → CanonicalSkill
- parseExternal(content: string, format: string, context: ParseContext): CanonicalSkill
-
- // CanonicalSkill → 外部格式
- serializeToExternal(skill: CanonicalSkill, targetToolId: string): string
-
- // CanonicalSkill → DeepChat SKILL.md
- serializeToSkillMd(skill: CanonicalSkill): string
-
- // 获取转换警告
- getConversionWarnings(skill: CanonicalSkill, targetToolId: string): string[]
-}
-
-interface ParseContext {
- toolId: string
- filePath: string
- folderPath?: string // 对于支持子文件夹的工具
-}
-```
-
-#### Format Adapter
-
-每种格式的具体转换实现(插件化):
-
-```typescript
-interface IFormatAdapter {
- // 适配器标识
- readonly id: string
- readonly name: string
-
- // 解析
- parse(content: string, context: ParseContext): CanonicalSkill
-
- // 序列化
- serialize(skill: CanonicalSkill): string
-
- // 检测格式
- detect(content: string): boolean
-
- // 获取功能限制
- getCapabilities(): FormatCapabilities
-}
-```
-
-### 3.3 数据流
-
-#### 导入流程
-
-```
-┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
-│ 用户选择 │ │ 扫描外部 │ │ 格式转换 │ │ 冲突检测 │
-│ 外部工具 │────▶│ 工具目录 │────▶│ → Canonical │────▶│ & 预览 │
-└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
- │
- ▼
-┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
-│ 刷新 UI │◀────│ 写入到 │◀────│ Canonical │◀────│ 用户确认 │
-│ 显示结果 │ │ DeepChat │ │ → SKILL.md │ │ 冲突策略 │
-└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
-```
-
-#### 导出流程
-
-```
-┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
-│ 用户选择 │ │ 读取 │ │ 格式转换 │ │ 冲突检测 │
-│ Skills & │────▶│ DeepChat │────▶│ → 目标格式 │────▶│ & 预览 │
-│ 目标工具 │ │ Skills │ │ │ │ │
-└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
- │
- ▼
-┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
-│ 显示结果 │◀────│ 写入到 │◀────│ 用户确认 │◀────│ 显示转换 │
-│ │ │ 目标目录 │ │ 冲突策略 │ │ 警告 │
-└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
-```
-
----
-
-## 4. 交互设计
-
-### 4.1 UI 入口
-
-在 Skills 设置页面添加同步功能:
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│ Skills Settings │
-├─────────────────────────────────────────────────────────────────┤
-│ ┌─────────────────────────────────────────────────────────────┐ │
-│ │ Header │ │
-│ │ ┌─────────────────┐ ┌──────────┐ ┌──────────┐ ┌───────┐ │ │
-│ │ │ 🔍 Search... │ │ 同步 ▾ │ │ 导入 ▾ │ │+ 安装 │ │ │
-│ │ └─────────────────┘ └──────────┘ └──────────┘ └───────┘ │ │
-│ │ ┌───────────────────────┐ │ │
-│ │ │ 从其他工具导入... │ │ │
-│ │ │ 导出到其他工具... │ │ │
-│ │ └───────────────────────┘ │ │
-│ └─────────────────────────────────────────────────────────────┘ │
-│ │
-│ [Skills 卡片网格...] │
-└─────────────────────────────────────────────────────────────────┘
-```
-
-### 4.2 导入向导流程
-
-**Step 1: 选择来源工具**
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ 从其他工具导入 Skills ✕ │
-├──────────────────────────────────────────────────────────────────┤
-│ │
-│ 选择要导入的工具: │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ○ [工具名称] [目录路径] │ │
-│ │ ✓ 已检测到 N 个 Skills │ │
-│ ├────────────────────────────────────────────────────────────┤ │
-│ │ ○ [工具名称] [目录路径] │ │
-│ │ ⚠ 目录不存在 │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ○ 自定义路径... │ │
-│ │ 选择包含 Skills 的文件夹 │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-├──────────────────────────────────────────────────────────────────┤
-│ [取消] [下一步] │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-**Step 2: 选择 Skills**
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ 从 [工具名称] 导入 ✕ │
-├──────────────────────────────────────────────────────────────────┤
-│ │
-│ ┌────────────────────────┐ │
-│ │ ☐ 全选 (N) │ │
-│ └────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ☑ [skill-name] │ │
-│ │ [skill description] │ │
-│ │ ⚠ 已存在同名 Skill (如有冲突) │ │
-│ ├────────────────────────────────────────────────────────────┤ │
-│ │ ☑ [skill-name] │ │
-│ │ [skill description] │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-│ 已选择 N 个,其中 M 个存在冲突 │
-│ │
-├──────────────────────────────────────────────────────────────────┤
-│ [上一步] [取消] [下一步] │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-**Step 3: 预览与冲突处理**
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ 确认导入 ✕ │
-├──────────────────────────────────────────────────────────────────┤
-│ │
-│ 即将导入 N 个 Skills: │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ✓ [skill-name] │ │
-│ │ ┌──────────────────────────────────────────────────────┐ │ │
-│ │ │ ⚠ 冲突: 已存在同名 Skill │ │ │
-│ │ │ │ │ │
-│ │ │ 处理方式: ○ 覆盖 ○ 跳过 ● 重命名为 xxx-1 │ │ │
-│ │ └──────────────────────────────────────────────────────┘ │ │
-│ ├────────────────────────────────────────────────────────────┤ │
-│ │ ✓ [skill-name] │ │
-│ │ 无冲突,将直接导入 │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ℹ️ 转换说明 │ │
-│ │ • [转换说明列表,根据源工具动态生成] │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-├──────────────────────────────────────────────────────────────────┤
-│ [上一步] [取消] [导入] │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-### 4.3 导出向导流程
-
-**Step 1: 选择要导出的 Skills**
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ 导出 Skills 到其他工具 ✕ │
-├──────────────────────────────────────────────────────────────────┤
-│ │
-│ 选择要导出的 Skills: │
-│ │
-│ ┌────────────────────────┐ │
-│ │ ☐ 全选 (N) │ │
-│ └────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ☑ [skill-name] │ │
-│ │ [skill description] │ │
-│ │ 📁 包含 references/, scripts/ (如有) │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-│ 已选择 N 个 Skills │
-│ │
-├──────────────────────────────────────────────────────────────────┤
-│ [取消] [下一步] │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-**Step 2: 选择目标工具**
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ 选择目标工具 ✕ │
-├──────────────────────────────────────────────────────────────────┤
-│ │
-│ 导出到: │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ○ [工具名称] [目录路径] │ │
-│ │ ✓ 完全兼容 / ⚠ [丢失功能列表] │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ ○ 自定义路径... │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-├──────────────────────────────────────────────────────────────────┤
-│ [上一步] [取消] [下一步] │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-**Step 3: 预览与确认**
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ 确认导出到 [工具名称] ✕ │
-├──────────────────────────────────────────────────────────────────┤
-│ │
-│ 即将导出 N 个 Skills 到 [目标路径] │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ [skill-name] → [目标文件名] │ │
-│ │ ┌──────────────────────────────────────────────────────┐ │ │
-│ │ │ ℹ️ 转换说明: │ │ │
-│ │ │ • [转换说明列表] │ │ │
-│ │ └──────────────────────────────────────────────────────┘ │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ 预览: [目标文件名] │ │
-│ │ ┌──────────────────────────────────────────────────────┐ │ │
-│ │ │ [转换后的内容预览] │ │ │
-│ │ └──────────────────────────────────────────────────────┘ │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-├──────────────────────────────────────────────────────────────────┤
-│ [上一步] [取消] [导出] │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-### 4.4 结果反馈
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ [导入/导出]完成 ✕ │
-├──────────────────────────────────────────────────────────────────┤
-│ │
-│ ✓ [操作]成功 │
-│ │
-│ ┌────────────────────────────────────────────────────────────┐ │
-│ │ 成功: N │ │
-│ │ • [skill-name] │ │
-│ │ • [skill-name] │ │
-│ │ │ │
-│ │ 跳过: M │ │
-│ │ • [skill-name] (原因) │ │
-│ │ │ │
-│ │ 失败: K │ │
-│ │ • [skill-name] (错误原因) │ │
-│ └────────────────────────────────────────────────────────────┘ │
-│ │
-├──────────────────────────────────────────────────────────────────┤
-│ [完成] │
-└──────────────────────────────────────────────────────────────────┘
-```
-
----
-
-## 5. 与现有系统集成
-
-### 5.1 与 SkillPresenter 集成
-
-```
-SkillSyncPresenter SkillPresenter
- │ │
- │ scanExternalTools() │
- │ ──────────────────────────────────▶│ (不调用)
- │ │
- │ executeImport() │
- │ ──────────────────────────────────▶│ installFromFolder()
- │ │
- │ executeExport() │
- │ ──────────────────────────────────▶│ loadSkillContent()
- │ │ getSkillFolderTree()
- │ │
-```
-
-### 5.2 事件定义
-
-```typescript
-const SKILL_SYNC_EVENTS = {
- SCAN_STARTED: 'skill-sync:scan-started',
- SCAN_COMPLETED: 'skill-sync:scan-completed',
- IMPORT_STARTED: 'skill-sync:import-started',
- IMPORT_PROGRESS: 'skill-sync:import-progress',
- IMPORT_COMPLETED: 'skill-sync:import-completed',
- EXPORT_STARTED: 'skill-sync:export-started',
- EXPORT_PROGRESS: 'skill-sync:export-progress',
- EXPORT_COMPLETED: 'skill-sync:export-completed'
-}
-```
-
-### 5.3 文件结构
-
-```
-src/main/presenter/
-├── skillPresenter/
-│ └── index.ts # 现有 Skill 管理
-└── skillSyncPresenter/
- ├── index.ts # SkillSyncPresenter 主类
- ├── types.ts # 类型定义
- ├── toolScanner.ts # 工具扫描器
- ├── formatConverter.ts # 格式转换引擎
- └── adapters/ # 格式适配器(插件化)
- └── index.ts # 适配器注册表
-
-src/renderer/settings/components/skills/
-├── SkillsSettings.vue # 添加同步按钮
-├── SkillSyncDialog/
-│ ├── SkillSyncDialog.vue # 同步向导主组件
-│ ├── ImportWizard.vue # 导入向导
-│ ├── ExportWizard.vue # 导出向导
-│ ├── ToolSelector.vue # 工具选择器
-│ ├── SkillSelector.vue # Skill 选择器
-│ ├── ConflictResolver.vue # 冲突处理
-│ └── SyncResult.vue # 结果展示
-
-src/shared/types/
-└── skillSync.ts # 共享类型定义
-```
-
----
-
-## 6. 国际化
-
-```json
-{
- "settings.skills.sync": "同步",
- "settings.skills.sync.import": "从其他工具导入...",
- "settings.skills.sync.export": "导出到其他工具...",
-
- "settings.skills.sync.import.title": "从其他工具导入 Skills",
- "settings.skills.sync.import.selectTool": "选择要导入的工具:",
- "settings.skills.sync.import.detected": "已检测到 {count} 个 Skills",
- "settings.skills.sync.import.notFound": "目录不存在",
- "settings.skills.sync.import.customPath": "自定义路径...",
- "settings.skills.sync.import.selectSkills": "选择要导入的 Skills",
- "settings.skills.sync.import.selectAll": "全选 ({count})",
- "settings.skills.sync.import.conflict": "已存在同名 Skill",
- "settings.skills.sync.import.selected": "已选择 {count} 个,其中 {conflicts} 个存在冲突",
-
- "settings.skills.sync.export.title": "导出 Skills 到其他工具",
- "settings.skills.sync.export.selectSkills": "选择要导出的 Skills",
- "settings.skills.sync.export.selectTarget": "选择目标工具",
- "settings.skills.sync.export.compatible": "完全兼容",
- "settings.skills.sync.export.warning": "{feature} 将丢失",
-
- "settings.skills.sync.conflict.title": "冲突处理",
- "settings.skills.sync.conflict.overwrite": "覆盖",
- "settings.skills.sync.conflict.skip": "跳过",
- "settings.skills.sync.conflict.rename": "重命名为 {name}",
-
- "settings.skills.sync.preview.title": "确认{action}",
- "settings.skills.sync.preview.converting": "转换说明",
- "settings.skills.sync.preview.warnings": "警告",
-
- "settings.skills.sync.result.title": "{action}完成",
- "settings.skills.sync.result.success": "成功: {count}",
- "settings.skills.sync.result.skipped": "跳过: {count}",
- "settings.skills.sync.result.failed": "失败: {count}",
-
- "settings.skills.sync.action.import": "导入",
- "settings.skills.sync.action.export": "导出",
- "settings.skills.sync.button.next": "下一步",
- "settings.skills.sync.button.prev": "上一步",
- "settings.skills.sync.button.done": "完成"
-}
-```
-
----
-
-## 7. 安全考虑
-
-### 7.1 路径安全
-
-- 所有路径操作需验证在预期目录范围内
-- 防止路径遍历攻击(../)
-- 验证外部工具目录确实存在且可读
-
-### 7.2 内容安全
-
-- 解析外部文件时限制文件大小
-- YAML 解析使用安全选项(禁用不安全的类型)
-- 不执行任何脚本内容,仅复制
-
-### 7.3 权限
-
-- 导出时检查目标目录写权限
-- 导入时检查源目录读权限
-- 失败时提供明确错误信息
-
----
-
-## 8. 未来扩展
-
-### 8.1 增量同步
-
-- 基于文件修改时间检测变化
-- 仅同步有更新的 Skills
-- 提供"上次同步时间"记录
-
-### 8.2 自动同步
-
-- 可选的文件监控模式
-- 检测到外部工具 Skills 变化时通知用户
-- 提供一键更新
-
-### 8.3 更多工具支持
-
-- 插件化的适配器架构
-- 用户可添加自定义工具配置
-- 社区贡献的适配器
-
-### 8.4 云同步
-
-- 可选的云端备份
-- 跨设备同步
-- 团队共享
diff --git a/docs/archives/skills-system/ui-design.md b/docs/archives/skills-system/ui-design.md
deleted file mode 100644
index f682ba5bc..000000000
--- a/docs/archives/skills-system/ui-design.md
+++ /dev/null
@@ -1,549 +0,0 @@
-# DeepChat Skills 系统 UI 设计文档
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 1. 概述
-
-本文档描述 Skills 系统的用户界面设计,基于 DeepChat 现有 UI 模式和组件库。
-
-### 1.1 设计原则
-
-- **一致性**:遵循现有 MCP、Custom Prompts 等功能的 UI 模式
-- **简洁性**:卡片网格展示,侧边栏编辑
-- **复用性**:使用 shadcn/ui 组件库
-
-### 1.2 UI 入口
-
-Skills 管理页面作为设置窗口的新页面,位于 MCP 和 Prompt 之间:
-
-| 路由 | 组件 | 图标 | 位置 |
-|------|------|------|------|
-| `/skills` | SkillsSettings | `lucide:sparkles` | 5 (MCP Market 之后) |
-
----
-
-## 2. 页面结构
-
-### 2.1 整体布局
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│ Skills Settings │
-├─────────────────────────────────────────────────────────────────┤
-│ ┌─────────────────────────────────────────────────────────────┐ │
-│ │ Header │ │
-│ │ ┌─────────────────┐ ┌──────────┐ ┌──────────┐ │ │
-│ │ │ 🔍 Search... │ │ 导入 ▾ │ │ + 安装 │ │ │
-│ │ └─────────────────┘ └──────────┘ └──────────┘ │ │
-│ └─────────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌─────────────────────────────────────────────────────────────┐ │
-│ │ ScrollArea │ │
-│ │ │ │
-│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
-│ │ │ Skill │ │ Skill │ │ Skill │ │ Skill │ │ │
-│ │ │ Card │ │ Card │ │ Card │ │ Card │ │ │
-│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
-│ │ │ │
-│ │ ┌──────────┐ ┌──────────┐ │ │
-│ │ │ Skill │ │ Skill │ │ │
-│ │ │ Card │ │ Card │ │ │
-│ │ └──────────┘ └──────────┘ │ │
-│ │ │ │
-│ └─────────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌─────────────────────────────────────────────────────────────┐ │
-│ │ Footer: 共 6 个 Skills | 打开 Skills 文件夹 │ │
-│ └─────────────────────────────────────────────────────────────┘ │
-└─────────────────────────────────────────────────────────────────┘
-```
-
-### 2.2 组件层次
-
-```
-SkillsSettings.vue
-├── SkillsHeader.vue
-│ ├── Input (搜索)
-│ ├── DropdownMenu (导入)
-│ │ ├── 从文件夹安装
-│ │ ├── 从 ZIP 安装
-│ │ └── 从 URL 安装
-│ └── Button (安装)
-├── ScrollArea
-│ ├── Empty State (无 Skills 时)
-│ └── Grid
-│ └── SkillCard.vue (循环)
-├── Footer
-│ ├── 统计信息
-│ └── 打开文件夹按钮
-└── Dialogs
- ├── SkillInstallDialog.vue (安装对话框)
- ├── SkillEditorSheet.vue (编辑侧边栏)
- └── AlertDialog (删除确认)
-```
-
----
-
-## 3. 组件设计
-
-### 3.1 SkillCard
-
-Skill 卡片组件,展示单个 Skill 信息。
-
-```
-┌─────────────────────────────────────┐
-│ ┌───┐ code-review ⋮ │
-│ │ ✨ │ 按照团队规范进行代码审查 │
-│ └───┘ │
-│ │
-│ ┌────────────────────────────────┐ │
-│ │ allowedTools: Read, Grep, ... │ │
-│ └────────────────────────────────┘ │
-│ │
-│ ┌────────┐ ┌────────┐ │
-│ │ 编辑 │ │ 删除 │ │
-│ └────────┘ └────────┘ │
-└─────────────────────────────────────┘
-```
-
-**状态变体**:
-- 正常状态
-- Hover 状态(显示操作按钮)
-- 加载状态(安装/删除中)
-
-**Props**:
-```typescript
-interface SkillCardProps {
- skill: SkillMetadata
- onEdit: () => void
- onDelete: () => void
-}
-```
-
-### 3.2 SkillEditorSheet
-
-右侧滑出的编辑面板,用于查看和编辑 Skill 详情。
-
-```
-┌──────────────────────────────────────┐
-│ ← 编辑 Skill │
-├──────────────────────────────────────┤
-│ │
-│ 名称 │
-│ ┌──────────────────────────────────┐ │
-│ │ code-review │ │
-│ └──────────────────────────────────┘ │
-│ │
-│ 描述 │
-│ ┌──────────────────────────────────┐ │
-│ │ 按照团队规范进行代码审查... │ │
-│ └──────────────────────────────────┘ │
-│ │
-│ 额外工具 (allowedTools) │
-│ ┌──────────────────────────────────┐ │
-│ │ Read, Grep, Glob, Bash(git:*) │ │
-│ └──────────────────────────────────┘ │
-│ │
-│ ─────────────────────────────────── │
-│ │
-│ Skill 内容 │
-│ ┌──────────────────────────────────┐ │
-│ │ # Code Review Skill │ │
-│ │ │ │
-│ │ ## 你的角色 │ │
-│ │ 你是一个代码审查专家... │ │
-│ │ │ │
-│ │ │ │
-│ └──────────────────────────────────┘ │
-│ │
-│ ─────────────────────────────────── │
-│ │
-│ 文件夹内容 │
-│ ┌──────────────────────────────────┐ │
-│ │ 📁 references/ │ │
-│ │ 📄 style-guide.md │ │
-│ │ 📄 checklist.md │ │
-│ │ 📁 scripts/ │ │
-│ │ 📄 lint.sh │ │
-│ └──────────────────────────────────┘ │
-│ │
-├──────────────────────────────────────┤
-│ [取消] [保存] │
-└──────────────────────────────────────┘
-```
-
-**功能**:
-- 编辑 frontmatter 字段(name, description, allowedTools)
-- 编辑 SKILL.md 正文内容(Markdown 编辑器)
-- 查看 Skill 文件夹结构(只读树形展示)
-- 保存修改(写回 SKILL.md 文件)
-
-### 3.3 SkillInstallDialog
-
-安装 Skill 的对话框,支持三种安装方式。
-
-```
-┌──────────────────────────────────────┐
-│ 安装 Skill ✕ │
-├──────────────────────────────────────┤
-│ │
-│ ┌────────┐ ┌────────┐ ┌────────┐ │
-│ │ 文件夹 │ │ ZIP │ │ URL │ │
-│ └────────┘ └────────┘ └────────┘ │
-│ │
-│ ┌──────────────────────────────────┐ │
-│ │ │ │
-│ │ 拖拽文件夹到此处 │ │
-│ │ 或点击选择 │ │
-│ │ │ │
-│ └──────────────────────────────────┘ │
-│ │
-│ 提示:支持从 ~/.claude/skills/ │
-│ 等其他客户端直接导入 Skill 文件夹 │
-│ │
-├──────────────────────────────────────┤
-│ [取消] [安装] │
-└──────────────────────────────────────┘
-```
-
-**Tab 切换内容**:
-
-1. **文件夹 Tab**:文件夹选择区域
-2. **ZIP Tab**:ZIP 文件选择区域
-3. **URL Tab**:URL 输入框
-
-**冲突处理对话框**:
-```
-┌──────────────────────────────────────┐
-│ Skill 已存在 ✕ │
-├──────────────────────────────────────┤
-│ │
-│ 名为 "code-review" 的 Skill 已存在。│
-│ 是否要覆盖现有 Skill? │
-│ │
-├──────────────────────────────────────┤
-│ [取消] [覆盖] │
-└──────────────────────────────────────┘
-```
-
----
-
-## 4. 交互流程
-
-### 4.1 查看 Skills 列表
-
-```
-用户打开 Settings → Skills
- │
- ▼
-加载 Skills Metadata 列表
- │
- ▼
-渲染 SkillCard 网格
- │
- ├── 有 Skills → 显示卡片网格
- │
- └── 无 Skills → 显示空状态
- "还没有安装任何 Skill"
- [安装第一个 Skill]
-```
-
-### 4.2 安装 Skill
-
-```
-用户点击 "安装" 按钮
- │
- ▼
-打开 SkillInstallDialog
- │
- ▼
-用户选择安装方式(文件夹/ZIP/URL)
- │
- ▼
-选择/输入安装源
- │
- ▼
-点击 "安装"
- │
- ▼
-验证 Skill 结构
- │
- ├── 无效 → 显示错误提示
- │
- ▼
-检查是否存在同名 Skill
- │
- ├── 存在 → 显示覆盖确认对话框
- │ │
- │ ├── 取消 → 返回
- │ │
- │ └── 覆盖 → 继续安装
- │
- ▼
-执行安装
- │
- ▼
-显示成功提示(Toast)
- │
- ▼
-刷新 Skills 列表
-```
-
-### 4.3 编辑 Skill
-
-```
-用户点击 SkillCard 的 "编辑" 按钮
- │
- ▼
-打开 SkillEditorSheet
- │
- ▼
-加载 Skill 完整内容
- │
- ▼
-用户编辑字段
- │
- ▼
-点击 "保存"
- │
- ▼
-验证表单
- │
- ├── 无效 → 显示错误提示
- │
- ▼
-写回 SKILL.md 文件
- │
- ▼
-显示成功提示(Toast)
- │
- ▼
-刷新 Skills 列表
-```
-
-### 4.4 删除 Skill
-
-```
-用户点击 SkillCard 的 "删除" 按钮
- │
- ▼
-显示 AlertDialog 确认
- │
- ├── 取消 → 关闭对话框
- │
- └── 确认 → 执行删除
- │
- ▼
- 删除 Skill 文件夹
- │
- ▼
- 显示成功提示(Toast)
- │
- ▼
- 刷新 Skills 列表
-```
-
----
-
-## 5. 文件结构
-
-```
-src/renderer/settings/
-├── main.ts # 添加 /skills 路由
-└── components/
- └── skills/
- ├── SkillsSettings.vue # 主页面
- ├── SkillsHeader.vue # 头部(搜索、导入、安装)
- ├── SkillCard.vue # Skill 卡片
- ├── SkillEditorSheet.vue # 编辑侧边栏
- ├── SkillInstallDialog.vue # 安装对话框
- └── SkillFolderTree.vue # 文件夹树形展示
-
-src/renderer/src/stores/
-└── skills.ts # Skills Pinia Store
-```
-
----
-
-## 6. Store 设计
-
-```typescript
-// src/renderer/src/stores/skills.ts
-import { defineStore } from 'pinia'
-
-interface SkillMetadata {
- name: string
- description: string
- path: string
- skillRoot: string
- allowedTools?: string[]
-}
-
-export const useSkillsStore = defineStore('skills', {
- state: () => ({
- skills: [] as SkillMetadata[],
- loading: false,
- error: null as string | null
- }),
-
- actions: {
- // 加载 Skills 列表
- async loadSkills() {
- this.loading = true
- try {
- const presenter = useLegacyPresenter('skillPresenter')
- this.skills = await presenter.getMetadataList()
- } finally {
- this.loading = false
- }
- },
-
- // 安装 Skill
- async installFromFolder(folderPath: string) { /* ... */ },
- async installFromZip(zipPath: string) { /* ... */ },
- async installFromUrl(url: string) { /* ... */ },
-
- // 卸载 Skill
- async uninstall(name: string) { /* ... */ },
-
- // 更新 Skill
- async updateSkill(name: string, content: string) { /* ... */ }
- }
-})
-```
-
----
-
-## 7. 国际化
-
-添加以下 i18n key:
-
-```json
-{
- "settings.skills": "Skills",
- "settings.skills.title": "Skills 管理",
- "settings.skills.search": "搜索 Skills...",
- "settings.skills.install": "安装",
- "settings.skills.import": "导入",
- "settings.skills.import.folder": "从文件夹安装",
- "settings.skills.import.zip": "从 ZIP 安装",
- "settings.skills.import.url": "从 URL 安装",
- "settings.skills.empty": "还没有安装任何 Skill",
- "settings.skills.empty.action": "安装第一个 Skill",
- "settings.skills.count": "共 {count} 个 Skills",
- "settings.skills.openFolder": "打开 Skills 文件夹",
- "settings.skills.card.edit": "编辑",
- "settings.skills.card.delete": "删除",
- "settings.skills.card.allowedTools": "额外工具",
- "settings.skills.editor.title": "编辑 Skill",
- "settings.skills.editor.name": "名称",
- "settings.skills.editor.description": "描述",
- "settings.skills.editor.allowedTools": "额外工具 (allowedTools)",
- "settings.skills.editor.content": "Skill 内容",
- "settings.skills.editor.files": "文件夹内容",
- "settings.skills.editor.save": "保存",
- "settings.skills.editor.cancel": "取消",
- "settings.skills.install.title": "安装 Skill",
- "settings.skills.install.tab.folder": "文件夹",
- "settings.skills.install.tab.zip": "ZIP",
- "settings.skills.install.tab.url": "URL",
- "settings.skills.install.folder.hint": "拖拽文件夹到此处或点击选择",
- "settings.skills.install.folder.tip": "支持从 ~/.claude/skills/ 等其他客户端直接导入",
- "settings.skills.install.zip.hint": "拖拽 ZIP 文件到此处或点击选择",
- "settings.skills.install.url.placeholder": "输入 ZIP 下载地址",
- "settings.skills.install.confirm": "安装",
- "settings.skills.install.cancel": "取消",
- "settings.skills.conflict.title": "Skill 已存在",
- "settings.skills.conflict.message": "名为 \"{name}\" 的 Skill 已存在。是否要覆盖现有 Skill?",
- "settings.skills.conflict.overwrite": "覆盖",
- "settings.skills.conflict.cancel": "取消",
- "settings.skills.delete.title": "删除 Skill",
- "settings.skills.delete.message": "确定要删除 Skill \"{name}\" 吗?此操作不可撤销。",
- "settings.skills.delete.confirm": "删除",
- "settings.skills.delete.cancel": "取消",
- "settings.skills.toast.installed": "Skill \"{name}\" 安装成功",
- "settings.skills.toast.updated": "Skill \"{name}\" 更新成功",
- "settings.skills.toast.deleted": "Skill \"{name}\" 已删除",
- "settings.skills.toast.error": "操作失败: {error}"
-}
-```
-
----
-
-## 8. 路由配置
-
-```typescript
-// src/renderer/settings/main.ts
-{
- path: '/skills',
- name: 'skills',
- component: () => import('./components/skills/SkillsSettings.vue'),
- meta: {
- titleKey: 'settings.skills',
- icon: 'lucide:sparkles',
- position: 5
- }
-}
-```
-
----
-
-## 9. Presenter 接口
-
-UI 层通过 `useLegacyPresenter('skillPresenter')` 调用以下方法:
-
-```typescript
-interface SkillPresenter {
- // 列表
- getMetadataList(): Promise
-
- // 安装
- installFromFolder(folderPath: string): Promise<{ success: boolean; error?: string }>
- installFromZip(zipPath: string): Promise<{ success: boolean; error?: string }>
- installFromUrl(url: string): Promise<{ success: boolean; error?: string }>
-
- // 卸载
- uninstallSkill(name: string): Promise<{ success: boolean; error?: string }>
-
- // 读取完整内容
- loadSkillContent(name: string): Promise
-
- // 更新 SKILL.md
- updateSkillFile(name: string, content: string): Promise<{ success: boolean; error?: string }>
-
- // 获取 Skill 文件夹结构
- getSkillFolderTree(name: string): Promise
-
- // 打开 Skills 目录
- openSkillsFolder(): Promise
-
- // 获取 Skills 目录路径
- getSkillsDir(): Promise
-}
-```
-
----
-
-## 10. 事件监听
-
-UI 需要监听以下事件以实时更新:
-
-```typescript
-// 在 SkillsSettings.vue 中
-onMounted(() => {
- // 监听 Skill 变化事件(热加载触发)
- eventBus.on(SKILL_EVENTS.METADATA_UPDATED, () => {
- skillsStore.loadSkills()
- })
-
- eventBus.on(SKILL_EVENTS.INSTALLED, () => {
- skillsStore.loadSkills()
- })
-
- eventBus.on(SKILL_EVENTS.UNINSTALLED, () => {
- skillsStore.loadSkills()
- })
-})
-```
-
diff --git a/docs/archives/skills-ux-redesign/analysis.md b/docs/archives/skills-ux-redesign/analysis.md
deleted file mode 100644
index 02fcd0ec9..000000000
--- a/docs/archives/skills-ux-redesign/analysis.md
+++ /dev/null
@@ -1,223 +0,0 @@
-# Skills 用户体验重设计 - 产品分析文档
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> 文档版本: v1.0
-> 创建日期: 2025-01-11
-> 状态: 分析阶段
-
----
-
-## 一、核心问题
-
-**Skills 功能强大,但用户感知不到、不会用。**
-
-| 问题 | 表现 | 影响程度 |
-|------|------|---------|
-| 对话界面零曝光 | 主界面无任何 Skills 入口,用户无法感知其存在 | ⚠️ 严重 |
-| 激活状态不可见 | 当前对话启用了哪些 Skills,用户无从得知 | ⚠️ 严重 |
-| 设置入口太深 | 设置窗口第 7 项,同步藏在下拉菜单里 | 中等 |
-| 与 MCP 关系模糊 | MCP 默认关闭,但 Skills 同步依赖 MCP 生态 | 中等 |
-
----
-
-## 二、设计决策
-
-### 决策 1: 触发符号分离
-
-**`@` 用于引用,`/` 用于调用能力**
-
-| 触发符 | 语义 | 包含内容 |
-|--------|------|---------|
-| `@` | 引用上下文 | context、files、resources、workspace files |
-| `/` | 调用能力 | **skills**、prompts、tools |
-
-**理由**: `@` 在社交产品中用于"提及",`/` 在 CLI/Slack 中用于"命令",语义更清晰。
-
-### 决策 2: 取消二级菜单
-
-**无论 `@` 还是 `/`,直接匹配全量内容,不再需要先选类别**
-
-- 减少点击次数,提高效率
-- 用户通常已知要找什么,直接输入更快
-- 通过图标区分类别,不影响识别
-
-### 决策 3: 输入框增加 Skills 状态入口
-
-**在工具栏增加 Skills 指示器,展示当前激活状态**
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ [Mode ▾] [📁] [📎] [🌐] [MCP ▾] [✨ 2] [Model ▾] [⚙️] [↑]│
-│ ↑ │
-│ Skills 指示器,点击展开面板 │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-### 决策 4: 优化同步流程与状态展示
-
-- 检测到外部 AI 工具时,主动提示是否导入 skills
-- 设置页增加"同步状态"区域,展示各工具同步时间和 skill 数量
-- 同步入口从下拉菜单提升为独立区域
-
----
-
-## 三、具体需求
-
-### 3.1 输入框内
-
-| 需求 | 描述 | 优先级 |
-|------|------|--------|
-| `/` 触发 Skills 选择 | 输入 `/` 显示 skills + prompts + tools 列表 | P0 |
-| 扁平化列表 | `@` 和 `/` 都直接显示匹配结果,无二级菜单 | P0 |
-| 模糊搜索 | 根据输入实时过滤匹配项 | P0 |
-| 类别图标 | 用图标区分(✨ skill / 💬 prompt / 🔧 tool) | P1 |
-
-### 3.2 输入框外
-
-| 需求 | 描述 | 优先级 |
-|------|------|--------|
-| Skills 指示器 | 工具栏显示当前激活 skills 数量 | P0 |
-| Skills 面板 | 点击指示器展开,显示激活/可用 skills | P0 |
-| 快速切换 | 面板内可直接激活/停用 skill | P1 |
-| 跳转管理 | 面板提供入口跳转到设置页 | P2 |
-
-### 3.3 同步与引导
-
-| 需求 | 描述 | 优先级 |
-|------|------|--------|
-| 首次导入引导 | 检测到外部工具时提示导入 | P1 |
-| 同步状态展示 | 设置页显示各工具同步时间和数量 | P1 |
-| MCP 依赖提示 | 激活依赖 MCP 的 skill 时提示启用 | P2 |
-
----
-
-## 四、交互示意
-
-### 4.1 `/` 触发交互
-
-```
-用户输入: /
-┌─────────────────────────────────────────────┐
-│ ✨ commit 生成规范的 Git 提交信息 │
-│ ✨ review 代码审查助手 │
-│ 💬 summarize 总结内容 (prompt) │
-│ 🔧 web_search 搜索网页 (tool) │
-└─────────────────────────────────────────────┘
-
-用户输入: /rev
-┌─────────────────────────────────────────────┐
-│ ✨ review 代码审查助手 │
-└─────────────────────────────────────────────┘
-```
-
-### 4.2 Skills 面板
-
-```
-点击 [✨ 2] 后:
-┌─────────────────────────────────────┐
-│ ✨ Active Skills [管理] │
-├─────────────────────────────────────┤
-│ ● commit 生成提交信息 [✕] │
-│ ● review 代码审查 [✕] │
-├─────────────────────────────────────┤
-│ ○ explain 解释代码 [+] │
-│ ○ refactor 重构建议 [+] │
-└─────────────────────────────────────┘
-```
-
-### 4.3 设置页同步状态
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 同步状态 │
-├─────────────────────────────────────────────────────────────┤
-│ 🔵 Claude Code 12 skills 上次: 2小时前 [同步] │
-│ 🟢 Cursor 5 skills 上次: 1天前 [同步] │
-│ ⚪ Copilot 未连接 [设置连接] │
-└─────────────────────────────────────────────────────────────┘
-```
-
----
-
-## 五、待确认事项
-
-| 问题 | 选项 | 建议 |
-|------|------|------|
-| `/` 列表排序 | A) Skills 优先 B) 按使用频率 C) 按字母 | A |
-| 自动同步 | A) 支持后台自动 B) 仅手动 | 待讨论 |
-| 同名冲突 | A) 覆盖 B) 重命名 C) 让用户选择 | C |
-
----
-
-## 附录 A: 现状调研
-
-### A.1 界面结构
-
-主界面仅有侧边栏(Chat/设置)和对话区域。输入框支持 @ 提及 (context/files/resources/tools/prompts),但没有 Skills 相关入口。
-
-### A.2 设置窗口导航
-
-Skills 位于第 7 位(共 12 项):通用 → 显示 → 模型 → MCP → MCP市场 → ACP → **Skills** → 提示词 → 知识库 → 数据 → 快捷键 → 关于
-
-### A.3 当前 @ 提及系统
-
-位置: `src/renderer/src/components/editor/mention/suggestion.ts`
-
-采用二级菜单:先选类别 → 再选具体项。类别包括 context/files/resources/tools/prompts,**不包含 skills**。
-
-### A.4 Skills 同步入口
-
-位置: `src/renderer/settings/components/skills/SkillsHeader.vue`
-
-同步隐藏在右上角下拉菜单: `[同步 ▾] → 导入 / 导出`
-
-### A.5 AI 工具调用
-
-AI 可通过 `skill_list` 和 `skill_control` 工具管理 Skills,但用户不可见。
-
-### A.6 支持同步的 12 个外部工具
-
-**用户级**: Claude Code、Cursor、OpenCode、Goose、Kilo Code、GitHub Copilot
-
-**项目级**: Cursor、Windsurf、GitHub Copilot、Kiro、Antigravity、Codex
-
----
-
-## 附录 B: 代码改动范围
-
-### B.1 输入框
-
-| 文件 | 改动 |
-|------|------|
-| `src/renderer/src/components/editor/mention/suggestion.ts` | 拆分 @ 和 / 逻辑 |
-| `src/renderer/src/components/editor/mention/MentionList.vue` | 扁平化 UI |
-| `src/renderer/src/components/chat-input/ChatInput.vue` | 增加 Skills 指示器 |
-| 新增 `SkillsIndicator.vue` / `SkillsPanel.vue` | Skills 组件 |
-
-### B.2 状态管理
-
-| 文件 | 改动 |
-|------|------|
-| 新增 `src/renderer/src/stores/skillsActiveStore.ts` | 对话级 skills 状态 |
-
-### B.3 设置页
-
-| 文件 | 改动 |
-|------|------|
-| `src/renderer/settings/components/skills/SkillsSettings.vue` | 同步状态区 |
-| 新增 `SyncStatusCard.vue` | 同步状态卡片 |
-
----
-
-## 附录 C: 关键文件索引
-
-| 模块 | 路径 |
-|------|------|
-| Skills 核心 | `src/main/presenter/skillPresenter/index.ts` |
-| Skills 同步 | `src/main/presenter/skillSyncPresenter/index.ts` |
-| Skills 设置页 | `src/renderer/settings/components/skills/SkillsSettings.vue` |
-| 输入框 | `src/renderer/src/components/chat-input/ChatInput.vue` |
-| @ 提及 | `src/renderer/src/components/editor/mention/suggestion.ts` |
-| 类型定义 | `src/shared/types/skill.ts` |
diff --git a/docs/archives/skills-ux-redesign/spec.md b/docs/archives/skills-ux-redesign/spec.md
deleted file mode 100644
index 6a3064907..000000000
--- a/docs/archives/skills-ux-redesign/spec.md
+++ /dev/null
@@ -1,777 +0,0 @@
-# Skills UX Redesign - Technical Specification
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> Version: 1.0
-> Date: 2025-01-11
-> Status: Draft
-
----
-
-## Overview
-
-This document specifies the technical implementation for the Skills UX redesign based on the 4 design decisions outlined in [analysis.md](./analysis.md).
-
----
-
-## Decision 1: Trigger Symbol Separation
-
-### Goal
-
-Separate `@` (reference context) from `/` (invoke capabilities) to provide clearer mental models.
-
-### Current State
-
-- Single `@` trigger in [suggestion.ts](../../../src/renderer/src/components/editor/mention/suggestion.ts)
-- Categories: `context`, `files`, `resources`, `tools`, `prompts`
-- No skills integration
-- TipTap Mention extension with `char: '@'`
-
-### Target State
-
-| Trigger | Semantic | Content |
-|---------|----------|---------|
-| `@` | Reference context | context, files, resources, workspace files |
-| `/` | Invoke capability | **skills**, prompts, tools |
-
-### Technical Approach
-
-#### 1.1 Create Slash Suggestion Module
-
-Create new file: `src/renderer/src/components/editor/mention/slashSuggestion.ts`
-
-```typescript
-// Similar structure to suggestion.ts but with char: '/'
-export default {
- char: '/',
- allowedPrefixes: null,
- items: ({ query }) => {
- // Return skills + prompts + tools
- },
- render: () => { /* Same popup renderer */ }
-}
-```
-
-#### 1.2 Create Slash Extension
-
-Create new file: `src/renderer/src/components/editor/mention/slashMention.ts`
-
-```typescript
-import TipTMention from '@tiptap/extension-mention'
-
-export const SlashMention = TipTMention.extend({
- name: 'slashMention', // Different name to coexist with @mention
- // ... same attributes as mention.ts
-})
-```
-
-#### 1.3 Update ChatInput.vue
-
-Add SlashMention extension to editor:
-
-```typescript
-// In editor extensions array
-SlashMention.configure({
- HTMLAttributes: {
- class: 'slash-mention px-1.5 py-0.5 text-xs rounded-md bg-primary/10 ...'
- },
- suggestion: slashSuggestion,
- deleteTriggerWithBackspace: true
-})
-```
-
-#### 1.4 Update suggestion.ts
-
-Remove `tools` and `prompts` from `@` categories:
-
-```typescript
-const categorizedData: CategorizedData[] = [
- { label: 'context', icon: 'lucide:quote', type: 'category' },
- { label: 'files', icon: 'lucide:files', type: 'category' },
- { label: 'resources', icon: 'lucide:swatch-book', type: 'category' }
- // tools and prompts moved to /
-]
-```
-
-#### 1.5 Add Skills Data Source
-
-Create composable: `src/renderer/src/components/chat-input/composables/useSkillsData.ts`
-
-```typescript
-export function useSkillsData(conversationId: Ref) {
- const skillPresenter = useLegacyPresenter('skillPresenter')
- const skills = ref([])
- const activeSkills = ref([])
-
- // Fetch skills metadata
- const loadSkills = async () => {
- skills.value = await skillPresenter.getMetadataList()
- }
-
- // Fetch active skills for current conversation
- const loadActiveSkills = async () => {
- if (!conversationId.value) return
- activeSkills.value = await skillPresenter.getActiveSkills(conversationId.value)
- }
-
- return { skills, activeSkills, loadSkills, loadActiveSkills }
-}
-```
-
-### Files Changed
-
-| File | Change |
-|------|--------|
-| `src/renderer/src/components/editor/mention/slashSuggestion.ts` | **New** - `/` trigger logic |
-| `src/renderer/src/components/editor/mention/slashMention.ts` | **New** - TipTap extension |
-| `src/renderer/src/components/editor/mention/suggestion.ts` | Remove tools/prompts categories |
-| `src/renderer/src/components/chat-input/ChatInput.vue` | Add SlashMention extension |
-| `src/renderer/src/components/chat-input/composables/useSkillsData.ts` | **New** - Skills data source |
-| `src/renderer/src/components/chat-input/composables/useMentionData.ts` | Split @ and / data |
-
----
-
-## Decision 2: Flatten Menus
-
-### Goal
-
-Remove secondary category selection, show flat filtered list directly.
-
-### Current State
-
-- [MentionList.vue](../../../src/renderer/src/components/editor/mention/MentionList.vue) uses `isCategoryView` state
-- First shows categories, then items within selected category
-- `displayItems` computed filters by `currentCategory`
-
-### Target State
-
-- Single flat list showing all matching items
-- Icons distinguish item types
-- No category navigation needed
-
-### Technical Approach
-
-#### 2.1 Simplify MentionList.vue
-
-Remove category navigation logic:
-
-```typescript
-// Remove these:
-const currentCategory = ref(null)
-const isCategoryView = computed(...)
-const backHandler = () => {...}
-
-// Simplify displayItems:
-const displayItems = computed(() => {
- if (props.query) {
- return props.items.filter(item =>
- item.label.toLowerCase().includes(props.query.toLowerCase())
- ).slice(0, 10)
- }
- return props.items.slice(0, 10)
-})
-```
-
-#### 2.2 Add Type Icons
-
-Update template to show type-specific icons:
-
-```vue
-
-
-
- ✨
- 💬
- 🔧
-
-
- {{ item.label }}
-
- {{ item.description }}
-
-
-
-```
-
-#### 2.3 Update Data Structure
-
-Ensure all items have proper `category` and `description`:
-
-```typescript
-interface CategorizedData {
- label: string
- icon?: string
- id?: string
- type: 'item' // No more 'category' type
- category: 'context' | 'files' | 'resources' | 'skills' | 'prompts' | 'tools'
- description?: string
- // ... other fields
-}
-```
-
-### Files Changed
-
-| File | Change |
-|------|--------|
-| `src/renderer/src/components/editor/mention/MentionList.vue` | Remove category navigation, flatten UI |
-| `src/renderer/src/components/editor/mention/suggestion.ts` | Update CategorizedData type |
-
----
-
-## Decision 3: Skills Indicator
-
-### Goal
-
-Add visible Skills indicator in chat input toolbar showing active skills count and quick management panel.
-
-### Current State
-
-- No Skills UI in chat input
-- Toolbar has: Mode, Folder, Attach, Web, MCP
-- Skills only manageable via settings page
-
-### Target State
-
-```
-┌──────────────────────────────────────────────────────────────────┐
-│ [Mode ▾] [📁] [📎] [🌐] [MCP ▾] [✨ 2] [Model ▾] [⚙️] [↑]│
-│ ↑ │
-│ Skills indicator │
-└──────────────────────────────────────────────────────────────────┘
-```
-
-### Technical Approach
-
-#### 3.1 Create SkillsIndicator Component
-
-New file: `src/renderer/src/components/chat-input/SkillsIndicator.vue`
-
-```vue
-
-
-
-
-
-
-
- {{ activeCount }}
-
-
-
- {{ activeCount > 0
- ? t('skills.indicator.active', { count: activeCount })
- : t('skills.indicator.none')
- }}
-
-
-
-
-
-
-
-
-
-
-
-```
-
-#### 3.2 Create SkillsPanel Component
-
-New file: `src/renderer/src/components/chat-input/SkillsPanel.vue`
-
-```vue
-
-
-
-
-
-
- {{ t('skills.panel.title') }}
-
-
- {{ t('skills.panel.manage') }}
-
-
-
-
-
-
- {{ t('skills.panel.active') }}
-
-
-
- ●
- {{ skill.name }}
-
-
-
-
-
-
-
-
-
-
- {{ t('skills.panel.available') }}
-
-
-
- ○
- {{ skill.name }}
-
-
-
-
-
-
-
-
-
- {{ t('skills.panel.empty') }}
-
-
-
-```
-
-#### 3.3 Update ChatInput.vue
-
-Add SkillsIndicator to toolbar:
-
-```vue
-
-
-
-```
-
-#### 3.4 Create Skills Store
-
-New file: `src/renderer/src/stores/skillsActiveStore.ts`
-
-```typescript
-import { defineStore } from 'pinia'
-import { ref, computed } from 'vue'
-import type { SkillMetadata } from '@shared/types/skill'
-
-export const useSkillsActiveStore = defineStore('skillsActive', () => {
- const skills = ref([])
- const activeSkillsByConversation = ref>(new Map())
-
- const getActiveSkills = (conversationId: string) => {
- return activeSkillsByConversation.value.get(conversationId) || []
- }
-
- const setActiveSkills = (conversationId: string, skillNames: string[]) => {
- activeSkillsByConversation.value.set(conversationId, skillNames)
- }
-
- const toggleSkill = async (conversationId: string, skillName: string) => {
- const current = getActiveSkills(conversationId)
- const isActive = current.includes(skillName)
- const updated = isActive
- ? current.filter(s => s !== skillName)
- : [...current, skillName]
- setActiveSkills(conversationId, updated)
-
- // Sync to backend
- await window.api.skillPresenter.setActiveSkills(conversationId, updated)
- }
-
- return {
- skills,
- getActiveSkills,
- setActiveSkills,
- toggleSkill
- }
-})
-```
-
-### Files Changed
-
-| File | Change |
-|------|--------|
-| `src/renderer/src/components/chat-input/SkillsIndicator.vue` | **New** |
-| `src/renderer/src/components/chat-input/SkillsPanel.vue` | **New** |
-| `src/renderer/src/components/chat-input/ChatInput.vue` | Add SkillsIndicator |
-| `src/renderer/src/stores/skillsActiveStore.ts` | **New** |
-| `src/renderer/src/components/chat-input/composables/useSkillsData.ts` | **New** |
-
-### i18n Keys
-
-```yaml
-skills:
- indicator:
- active: "{count} skills active"
- none: "No skills active"
- panel:
- title: "Skills"
- manage: "Manage"
- active: "Active"
- available: "Available"
- empty: "No skills installed"
-```
-
----
-
-## Decision 4: Sync Flow Optimization
-
-### Goal
-
-Improve skills sync discoverability with proactive detection and clear status display.
-
-### Current State
-
-- Sync hidden in dropdown: `[同步 ▾] → 导入 / 导出`
-- No detection of external AI tools
-- No sync status visibility
-- Located in [SkillsHeader.vue](../../../src/renderer/settings/components/skills/SkillsHeader.vue)
-
-### Target State
-
-1. Proactive detection of external AI tools on first launch
-2. Dedicated sync status section in settings
-3. Clear last-sync timestamps and skill counts per tool
-
-### Technical Approach
-
-#### 4.1 Create SyncStatusSection Component
-
-New file: `src/renderer/settings/components/skills/SyncStatusSection.vue`
-
-```vue
-
-
-
-
{{ t('skills.sync.title') }}
-
-
- {{ t('skills.sync.refreshAll') }}
-
-
-
-
-
-
-
-
- {{ t('skills.sync.noToolsDetected') }}
-
-
-
-```
-
-#### 4.2 Create SyncStatusCard Component
-
-New file: `src/renderer/settings/components/skills/SyncStatusCard.vue`
-
-```vue
-
-
-
-
-
-
-
-
-
{{ tool.name }}
-
-
- {{ tool.skillCount }} skills · {{ formatLastSync(tool.lastSync) }}
-
-
- {{ t('skills.sync.notConnected') }}
-
-
-
-
-
-
-
-
- {{ t('skills.sync.sync') }}
-
-
- {{ t('skills.sync.setup') }}
-
-
-
-
-
-```
-
-#### 4.3 Add External Tool Detection
-
-Create: `src/main/presenter/skillSyncPresenter/toolDetection.ts`
-
-```typescript
-import fs from 'fs'
-import path from 'path'
-import os from 'os'
-
-interface DetectedTool {
- id: string
- name: string
- configPath: string
- exists: boolean
-}
-
-const TOOL_PATHS = {
- 'claude-code': {
- win32: path.join(os.homedir(), '.claude', 'settings.json'),
- darwin: path.join(os.homedir(), '.claude', 'settings.json'),
- linux: path.join(os.homedir(), '.claude', 'settings.json')
- },
- 'cursor': {
- win32: path.join(os.homedir(), 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage'),
- darwin: path.join(os.homedir(), 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage'),
- linux: path.join(os.homedir(), '.config', 'Cursor', 'User', 'globalStorage')
- },
- // ... other tools
-}
-
-export function detectExternalTools(): DetectedTool[] {
- const platform = process.platform as 'win32' | 'darwin' | 'linux'
- const detected: DetectedTool[] = []
-
- for (const [id, paths] of Object.entries(TOOL_PATHS)) {
- const configPath = paths[platform]
- if (configPath) {
- detected.push({
- id,
- name: formatToolName(id),
- configPath,
- exists: fs.existsSync(configPath)
- })
- }
- }
-
- return detected.filter(t => t.exists)
-}
-
-function formatToolName(id: string): string {
- const names: Record = {
- 'claude-code': 'Claude Code',
- 'cursor': 'Cursor',
- 'windsurf': 'Windsurf',
- 'github-copilot': 'GitHub Copilot',
- // ...
- }
- return names[id] || id
-}
-```
-
-#### 4.4 Add First-Launch Detection Prompt
-
-In settings initialization or first app launch, check for external tools:
-
-```typescript
-// In renderer settings initialization
-const checkFirstLaunch = async () => {
- const hasShownSyncPrompt = await configPresenter.getSetting('skills.syncPromptShown')
- if (hasShownSyncPrompt) return
-
- const detectedTools = await skillSyncPresenter.detectExternalTools()
- if (detectedTools.length > 0) {
- // Show prompt dialog
- showSyncPromptDialog(detectedTools)
- }
-
- await configPresenter.setSetting('skills.syncPromptShown', true)
-}
-```
-
-#### 4.5 Update SkillsSettings.vue
-
-Add sync status section:
-
-```vue
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### Files Changed
-
-| File | Change |
-|------|--------|
-| `src/renderer/settings/components/skills/SyncStatusSection.vue` | **New** |
-| `src/renderer/settings/components/skills/SyncStatusCard.vue` | **New** |
-| `src/renderer/settings/components/skills/SkillsSettings.vue` | Add SyncStatusSection |
-| `src/main/presenter/skillSyncPresenter/toolDetection.ts` | **New** |
-| `src/main/presenter/skillSyncPresenter/index.ts` | Add detection methods |
-
-### i18n Keys
-
-```yaml
-skills:
- sync:
- title: "Sync Status"
- refreshAll: "Refresh All"
- sync: "Sync"
- setup: "Setup"
- notConnected: "Not connected"
- noToolsDetected: "No external AI tools detected"
- lastSync: "Last synced {time}"
- skillCount: "{count} skills"
-```
-
----
-
-## Implementation Order
-
-| Phase | Components | Priority |
-|-------|------------|----------|
-| 1 | Flatten menus (Decision 2) | P0 |
-| 2 | Skills Indicator (Decision 3) | P0 |
-| 3 | Slash trigger (Decision 1) | P0 |
-| 4 | Sync optimization (Decision 4) | P1 |
-
-### Rationale
-
-1. **Flatten menus first** - Simplest change, improves existing UX immediately
-2. **Skills indicator second** - Core visibility improvement, relatively self-contained
-3. **Slash trigger third** - Depends on having skills data accessible, more complex
-4. **Sync optimization last** - Enhancement layer, not blocking core functionality
-
----
-
-## Testing Considerations
-
-### Unit Tests
-
-- `useSkillsData` composable: skill loading, activation toggling
-- `slashSuggestion`: filtering, ordering
-- Flatten logic in `MentionList.vue`
-
-### Integration Tests
-
-- `/` trigger shows skills + prompts + tools
-- `@` trigger shows context + files + resources only
-- Skills indicator updates on activation/deactivation
-- Sync status reflects actual tool states
-
-### E2E Tests
-
-- User types `/` → sees skills list → selects skill → skill activated
-- User clicks skills indicator → panel opens → can toggle skills
-- User opens settings → sees sync status → can sync tools
-
----
-
-## Migration Notes
-
-### Breaking Changes
-
-None. All changes are additive or modify internal behavior.
-
-### Backward Compatibility
-
-- Existing `@` mentions continue to work (subset of categories)
-- Existing skill activation via AI tools unchanged
-- Settings page structure preserved
-
-### Data Migration
-
-None required. Skills state already stored per-conversation in database.
-
diff --git a/docs/archives/skills-ux-redesign/tasks.md b/docs/archives/skills-ux-redesign/tasks.md
deleted file mode 100644
index 3af3cb8c5..000000000
--- a/docs/archives/skills-ux-redesign/tasks.md
+++ /dev/null
@@ -1,450 +0,0 @@
-# Skills UX Redesign - Development Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-> Version: 1.1
-> Date: 2025-01-11
-> Related: [analysis.md](./analysis.md) | [spec.md](./spec.md)
-
----
-
-## Task Overview
-
-| Phase | Description | Tasks | Priority | Status |
-|-------|-------------|-------|----------|--------|
-| 1 | Flatten Menus | 4 | P0 | ✅ Done |
-| 2 | Skills Indicator | 7 | P0 | ✅ Done |
-| 3 | Slash Trigger | 6 | P0 | ✅ Done |
-| 4 | Sync Optimization | 5 | P1 | ✅ Done |
-
-**Total: 22 tasks**
-
----
-
-## Phase 1: Flatten Menus (Decision 2) ✅
-
-> Goal: Remove secondary category selection, show flat filtered list directly.
-> Status: **Completed**
-
-### 1.1 Remove Category Navigation Logic ✅
-
-**File:** `src/renderer/src/components/editor/mention/MentionList.vue`
-
-- [x] Remove `currentCategory` ref and `isCategoryView` computed
-- [x] Remove `backHandler()` function
-- [x] Remove `saveCurrentIndexForCategory()` and `getLastIndexForCategory()` helpers
-- [x] Remove `lastIndexMap` ref
-- [x] Simplify `onKeyDown` to remove Backspace category navigation
-
-**Acceptance Criteria:**
-- ✅ No category state management in component
-- ✅ Backspace key no longer triggers category back navigation
-
----
-
-### 1.2 Simplify displayItems Logic ✅
-
-**File:** `src/renderer/src/components/editor/mention/MentionList.vue`
-
-- [x] Update `displayItems` computed to return flat filtered list
-
-**Acceptance Criteria:**
-- ✅ List shows all matching items without category grouping
-- ✅ Maximum 10 items displayed
-- ✅ Fuzzy search works on item labels
-
----
-
-### 1.3 Update Template for Flat Display ✅
-
-**File:** `src/renderer/src/components/editor/mention/MentionList.vue`
-
-- [x] Remove category view header (`v-if="isCategoryView"`)
-- [x] Remove chevron-right icon for category items
-- [x] Add type indicator icons based on `item.category`:
- - `skills` → ✨ (amber)
- - `prompts` → 💬 (blue)
- - `tools` → 🔧 (green)
- - Others → use `item.icon`
-- [x] Add description display (truncated, right-aligned)
-
-**Acceptance Criteria:**
-- ✅ Each item shows type icon + label + description
-- ✅ Visual distinction between different item types
-- ✅ No category rows in the list
-
----
-
-### 1.4 Update CategorizedData Type ✅
-
-**File:** `src/renderer/src/components/editor/mention/suggestion.ts`
-
-- [x] Keep `'category'` in type union for backward compatibility but not displayed
-- [x] Clear category entries from initial `categorizedData` array
-- [x] Items now populated by `useMentionData` composable
-
-**Acceptance Criteria:**
-- ✅ All mention items are direct items with category metadata
-- ✅ Category entries no longer rendered
-
----
-
-## Phase 2: Skills Indicator (Decision 3) ✅
-
-> Goal: Add visible Skills indicator in chat input toolbar.
-> Status: **Completed**
-
-### 2.1 Create useSkillsData Composable ✅
-
-**File:** `src/renderer/src/components/chat-input/composables/useSkillsData.ts` (NEW)
-
-- [x] Create composable with:
- - `skills: Ref` - all available skills
- - `activeSkills: Ref` - currently active skill names
- - `loadActiveSkills()` - fetch active for conversation
- - `toggleSkill(skillName)` - activate/deactivate skill
- - `activeCount: ComputedRef` - count of active skills
- - `activeSkillItems` / `availableSkills` - computed filtered lists
-- [x] Add event listener for `skill:activated` / `skill:deactivated`
-- [x] Add watcher for conversationId changes
-
-**Acceptance Criteria:**
-- ✅ Skills data reactive and auto-updates
-- ✅ Toggle correctly calls `skillPresenter.setActiveSkills`
-- ✅ Responds to external skill state changes
-
----
-
-### 2.2 Create SkillsPanel Component ✅
-
-**File:** `src/renderer/src/components/chat-input/SkillsPanel.vue` (NEW)
-
-- [x] Create component with props:
- - `skills: SkillMetadata[]`
- - `activeSkills: string[]`
-- [x] Create emits:
- - `toggle(skillName: string)`
- - `manage()`
-- [x] Implement template:
- - Header with title and "Manage" button
- - Active skills section with deactivate buttons
- - Available skills section with activate buttons
- - Empty state when no skills
-
-**Acceptance Criteria:**
-- ✅ Shows active skills at top
-- ✅ Shows available (inactive) skills below
-- ✅ Click toggle emits event
-- ✅ Manage button emits event
-
----
-
-### 2.3 Create SkillsIndicator Component ✅
-
-**File:** `src/renderer/src/components/chat-input/SkillsIndicator.vue` (NEW)
-
-- [x] Create component with props:
- - `conversationId: string | null`
-- [x] Integrate `useSkillsData` composable
-- [x] Implement Popover with:
- - Trigger: Button showing ✨ icon + active count
- - Content: SkillsPanel component
-- [x] Add Tooltip for button hover
-- [x] Style: highlight when skills active (primary color border)
-
-**Acceptance Criteria:**
-- ✅ Shows sparkles icon
-- ✅ Shows count badge when skills active
-- ✅ Opens panel on click
-- ✅ Tooltip shows summary
-
----
-
-### 2.4 Add SkillsIndicator to ChatInput ✅
-
-**File:** `src/renderer/src/components/chat-input/ChatInput.vue`
-
-- [x] Import SkillsIndicator component
-- [x] Add to toolbar after ` `:
- ```vue
-
-
- ```
-- [x] Ensure conversationId is available (already exists as computed)
-
-**Acceptance Criteria:**
-- ✅ SkillsIndicator visible in chat input toolbar
-- ✅ Positioned between MCP tools and right-side actions
-- ✅ Works in both chat and newThread variants
-
----
-
-### 2.5 Create skillsActiveStore ⏭️ (Skipped)
-
-**Note:** Decided to use `useSkillsData` composable instead of a separate Pinia store. The composable leverages the existing `skillsStore` for metadata and manages per-conversation state directly via `skillPresenter`.
-
----
-
-### 2.6 Add Settings Navigation ✅
-
-**File:** `src/renderer/src/components/chat-input/SkillsIndicator.vue`
-
-- [x] Implement `openSettings()` method using `windowPresenter.openOrFocusSettingsTab()`
-- [x] Wire to SkillsPanel's `@manage` event
-
-**Acceptance Criteria:**
-- ✅ Clicking "Manage" opens settings window
-
----
-
-### 2.7 Add i18n Keys ✅
-
-**Files:** `src/renderer/src/i18n/{en-US,zh-CN}/chat.json`
-
-- [x] Add `chat.skills.indicator.active` / `chat.skills.indicator.none`
-- [x] Add `chat.skills.panel.title` / `manage` / `active` / `available` / `empty`
-- [x] Added to en-US and zh-CN
-
-**Acceptance Criteria:**
-- ✅ All UI text uses i18n keys
-- ✅ Keys exist in en-US and zh-CN locales
-
----
-
-## Phase 3: Slash Trigger (Decision 1) ✅
-
-> Goal: Separate `/` trigger for skills, prompts, tools from `@` trigger.
-> Status: **Completed**
-
-### 3.1 Create SlashMention Extension ✅
-
-**File:** `src/renderer/src/components/editor/mention/slashMention.ts` (NEW)
-
-- [x] Extend TipTap Mention with `name: 'slashMention'`
-- [x] Copy attribute definitions from mention.ts
-- [x] Add `trigger` attribute to distinguish from @ mentions
-
-**Acceptance Criteria:**
-- ✅ Extension can coexist with existing Mention
-- ✅ Unique node name prevents conflicts
-
----
-
-### 3.2 Create slashSuggestion Module ✅
-
-**File:** `src/renderer/src/components/editor/mention/slashSuggestion.ts` (NEW)
-
-- [x] Create with `char: '/'`
-- [x] Create `slashMentionData` ref for skills + prompts + tools
-- [x] Implement `items({ query })`:
- - Filter skills, prompts, tools by query
- - Sort: skills first, then prompts, then tools
- - Limit to 10 items
-- [x] Reuse render logic from suggestion.ts
-
-**Acceptance Criteria:**
-- ✅ `/` triggers popup
-- ✅ Shows skills, prompts, tools only
-- ✅ Query filters results
-
----
-
-### 3.3 Create useSlashMentionData Composable ✅
-
-**File:** `src/renderer/src/components/chat-input/composables/useSlashMentionData.ts` (NEW)
-
-- [x] Watch skills from skillsStore
-- [x] Watch prompts from useAgentMcpData
-- [x] Watch tools from useAgentMcpData
-- [x] Aggregate into `slashMentionData` with proper category tags
-- [x] Sort: skills → prompts → tools
-
-**Acceptance Criteria:**
-- ✅ Data updates reactively from all sources
-- ✅ Each item has correct category for icon display
-
----
-
-### 3.4 Update suggestion.ts ✅
-
-**File:** `src/renderer/src/components/editor/mention/suggestion.ts`
-
-- [x] Remove `tools` and `prompts` from categorizedData
-- [x] Keep only: context, files, resources (+ workspace files)
-- [x] Update useMentionData to remove tools/prompts watchers
-
-**Acceptance Criteria:**
-- ✅ `@` trigger only shows context, files, resources
-- ✅ No tools or prompts in `@` menu
-
----
-
-### 3.5 Integrate SlashMention in ChatInput ✅
-
-**File:** `src/renderer/src/components/chat-input/ChatInput.vue`
-
-- [x] Import SlashMention and slashSuggestion
-- [x] Add to editor extensions with appropriate styling
-- [x] Initialize useSlashMentionData composable
-- [x] Wire skill activation handler via `setSkillActivationHandler`
-
-**Acceptance Criteria:**
-- ✅ Both `@` and `/` triggers work in editor
-- ✅ Different visual styles for @ vs / mentions
-
----
-
-### 3.6 Handle Skill Selection ✅
-
-**File:** `src/renderer/src/components/editor/mention/slashSuggestion.ts`
-
-- [x] On skill selection:
- - Insert mention node
- - Activate skill for current conversation via handler
-- [x] On prompt selection:
- - Handle same as current @ prompts (with params dialog if needed)
-- [x] On tool selection:
- - Insert mention node (current behavior)
-
-**Acceptance Criteria:**
-- ✅ Selecting skill activates it
-- ✅ Prompt param dialog works
-- ✅ Tool insertion works
-
----
-
-## Phase 4: Sync Optimization (Decision 4) ✅
-
-> Goal: Improve skills sync discoverability and status visibility.
-> Status: **Completed**
-
-### 4.1 Create Tool Detection Logic ✅
-
-**Note:** Tool detection logic already exists in `src/main/presenter/skillSyncPresenter/toolScanner.ts`
-
-- [x] `EXTERNAL_TOOLS` registry defines all supported tools
-- [x] `isToolAvailable()` checks if tool's directory exists
-- [x] `scanExternalTools()` scans all registered tools
-- [x] Already integrated in skillSyncPresenter interface
-
-**Acceptance Criteria:**
-- ✅ Detects installed AI tools on all platforms
-- ✅ Returns tool id, name, config path
-
----
-
-### 4.2 Create SyncStatusCard Component ✅
-
-**File:** `src/renderer/settings/components/skills/SyncStatusCard.vue` (NEW)
-
-- [x] Create component with props:
- - `tool: ScanResult`
- - `syncing: boolean`
-- [x] Create emits:
- - `sync(toolId)`
-- [x] Implement template:
- - Status indicator (colored dot)
- - Tool name and skill count
- - Import button for available tools
-
-**Acceptance Criteria:**
-- ✅ Shows connection status visually
-- ✅ Shows skill count
-- ✅ Appropriate action button based on state
-
----
-
-### 4.3 Create SyncStatusSection Component ✅
-
-**File:** `src/renderer/settings/components/skills/SyncStatusSection.vue` (NEW)
-
-- [x] Create component with:
- - Detected tools list from presenter
- - Sync states tracking
- - Refresh functionality
-- [x] Implement sync handler:
- - Emits import event for parent to open sync dialog
-- [x] Add empty state for no detected tools
-- [x] Add show more/less toggle for long lists
-
-**Acceptance Criteria:**
-- ✅ Lists all detected external tools
-- ✅ Each tool shows SyncStatusCard
-- ✅ Refresh updates detection
-
----
-
-### 4.4 Update SkillsSettings Layout ✅
-
-**File:** `src/renderer/settings/components/skills/SkillsSettings.vue`
-
-- [x] Import SyncStatusSection
-- [x] Add before skills list with separator
-- [x] Adjust spacing/layout
-
-**Acceptance Criteria:**
-- ✅ Sync section visible at top of Skills settings
-- ✅ Clean visual hierarchy with skills list below
-
----
-
-### 4.5 Add First-Launch Detection Dialog ✅
-
-**File:** `src/renderer/settings/components/skills/SyncPromptDialog.vue` (NEW)
-
-- [x] Create dialog component:
- - Shows list of detected tools with skill counts
- - Checkbox to select tools for import
- - "Don't show again" checkbox
-- [x] Wire to first launch check via `configPresenter.getSetting('skills.syncPromptShown')`
-- [x] Pre-select all available tools by default
-
-**Acceptance Criteria:**
-- Dialog appears on first launch if tools detected
-- User can import or dismiss
-- Preference persisted
-
----
-
-## Testing Checklist
-
-### Phase 1 Tests ✅
-- [x] Typing `@` shows flat list of context/files/resources
-- [x] Arrow keys navigate flat list
-- [x] Enter selects item
-- [x] Backspace deletes characters (no category navigation)
-- [x] Query filters items correctly
-
-### Phase 2 Tests ✅
-- [x] Skills indicator visible in toolbar
-- [x] Count updates when skills activated/deactivated
-- [x] Panel opens on click
-- [x] Can toggle skills from panel
-- [x] Manage button opens settings
-
-### Phase 3 Tests ✅
-- [x] Typing `/` shows skills/prompts/tools
-- [x] `@` no longer shows tools/prompts
-- [x] Skill selection activates the skill
-- [x] Prompt params dialog works
-- [x] Both @ and / mentions render correctly
-
-### Phase 4 Tests ✅
-- [x] Sync section shows in settings
-- [x] Detected tools display correctly
-- [x] Sync button triggers import dialog
-- [x] Status updates after refresh
-- [x] First launch dialog appears appropriately
-
----
-
-## Definition of Done
-
-- [x] All tasks completed and tested
-- [x] `pnpm run lint` passes
-- [x] `pnpm run typecheck` passes
-- [ ] `pnpm test` passes
-- [x] i18n keys added to all locales (en-US, zh-CN)
-- [ ] No regressions in existing functionality
-- [ ] Code reviewed and approved
diff --git a/docs/archives/specs-migration-2026-05-02.md b/docs/archives/specs-migration-2026-05-02.md
deleted file mode 100644
index 09863df5f..000000000
--- a/docs/archives/specs-migration-2026-05-02.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Specs Migration 2026-05-02
-
-> Archive note: This document records the SDD directory migration performed on 2026-05-02. Historical paths can reference code that has since moved or been removed.
-
-## Summary
-
-The legacy SDD tree was split into goal-based directories:
-
-- `docs/features`: 35 active feature and capability targets
-- `docs/issues`: 5 active issue, regression, and reliability targets
-- `docs/architecture`: 8 active architecture and migration targets
-- `docs/archives`: 27 stale, completed, retired, or superseded target folders
-
-Active goal folders now contain `spec.md`, `plan.md`, and `tasks.md`. Generated placeholder plan/task files preserve SDD shape for older spec-only folders and should be replaced with detailed content when those targets are resumed.
-
-## Active Features
-
-`acp-agent-uninstall`, `acp-session-config-options`, `active-input-routing`, `agent-db-legacy-import`, `agent-input-advanced-config`, `app-spotlight-search`, `chat-settings-control`, `chat-sidebar-input-polish`, `edit-file-tool`, `electron-vite-5-upgrade`, `file-attachment-support`, `floating-agent-widget`, `hooks-notifications`, `message-toolbar-actions`, `message-trace-storage`, `ollama-model-selection`, `privacy-mode`, `process-tool`, `provider-deeplink-import`, `remote-acp-control`, `remote-block-streaming`, `remote-discord-lark`, `remote-multi-channel`, `remote-process-log`, `remote-tool-interactions`, `right-sidepanel`, `settings-dashboard`, `settings-environments`, `sidebar-session-context-menu`, `sidebar-workspace-shortcuts`, `subagent-orchestrator`, `tool-call-image-preview`, `user-message-collapse`, `workspace-lifecycle`, `yobrowser-optimization`.
-
-## Active Issues
-
-`agent-tool-context-budget`, `e2e-smoke-regression`, `permission-flow-stabilization`, `question-tool-prompt-optimization`, `tool-output-guardrails`.
-
-## Active Architecture Targets
-
-`agent-provider-simplification`, `agent-refactor`, `architecture-simplification`, `chat-store-zero-migration`, `main-kernel-refactor`, `renderer-main-single-track`, `skill-runtime-hardening`, `startup-orchestration`.
-
-## Archived Targets
-
-`agent-cleanup`, `agent-tooling-v2`, `agentpresenter-mvp-replacement`, `ai-sdk-runtime`, `cua-runtime-plugin`, `default-model-settings`, `legacy-agentpresenter-retirement`, `legacy-llm-provider-runtime-retirement`, `mac-computer-use`, `multi-window-cleanup`, `new-agent`, `new-ui-agent-session`, `new-ui-agent-store`, `new-ui-chat-components`, `new-ui-implementation`, `new-ui-markdown-rendering`, `new-ui-page-state`, `new-ui-pages`, `new-ui-project-store`, `new-ui-session-store`, `new-ui-sidebar`, `new-ui-status-bar`, `provider-layer-simplification`, `remove-chat-mode`, `skills-system`, `skills-ux-redesign`, `telegram-remote-control`.
-
-## Deleted Stale Files
-
-- `agentpresenter-mvp-replacement/gap-analysis.md`
-- `skills-system/code-review.md`
-- `skills-system/create-skill-prompt.md`
-- `skills-system/create-skill-spec.md`
-
-These files were removed because they only described superseded implementation paths or skill scaffolding plans that no longer match the current project-level skill model.
diff --git a/docs/archives/telegram-remote-control/plan.md b/docs/archives/telegram-remote-control/plan.md
deleted file mode 100644
index a5cbf30a2..000000000
--- a/docs/archives/telegram-remote-control/plan.md
+++ /dev/null
@@ -1,235 +0,0 @@
-# Telegram Remote Control Plan
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-Feature: `telegram-remote-control`
-Spec: [spec.md](./spec.md)
-Tasks: [tasks.md](./tasks.md)
-
-## Summary
-
-This plan updates the original Telegram-only implementation notes to match the shipped
-multi-channel `remoteControlPresenter` surface. Telegram-specific runtime behavior still
-matters, but all renderer and preload work now lands on a shared presenter contract that also
-serves Feishu/Lark, QQBot, Discord, and Weixin iLink.
-
-## Goals
-
-- Keep the `telegram-remote-control` feature docs aligned with the current shared presenter
- surface.
-- Document the generic per-channel settings, status, bindings, pairing, and account-login
- flows that renderer and preload callers must use.
-- Preserve Telegram runtime behavior while preventing follow-up work from regressing the IPC
- contract back to a Telegram-only shape.
-- Make channel-specific differences explicit for Telegram, QQBot, Discord, and Weixin iLink.
-
-## Readiness
-
-- Companion artifacts exist in `docs/archives/telegram-remote-control/`:
- - `spec.md`
- - `plan.md`
- - `tasks.md`
-- No open `[NEEDS CLARIFICATION]` markers remain in [spec.md](./spec.md).
-- The current source of truth for the contract is
- [remote-control.presenter.d.ts](../../../src/shared/types/presenters/remote-control.presenter.d.ts).
-
-## Presenter And IPC Contract
-
-The preferred renderer and preload surface is the shared `IRemoteControlPresenter` contract:
-
-- Discovery
- - `listRemoteChannels()`
-- Settings
- - `getChannelSettings(channel: T)`
- - `saveChannelSettings(channel: T, input: ChannelSettingsMap[T])`
-- Runtime status
- - `getChannelStatus(channel: RemoteChannel)`
-- Bindings
- - `getChannelBindings(channel: RemoteChannel)`
- - `removeChannelBinding(channel: RemoteChannel, endpointKey: string)`
- - `clearChannelBindings(channel: RemoteChannel)`
-- Pairable-channel principal and pair-code flow
- - `removeChannelPrincipal(channel: PairableRemoteChannel, principalId: string)`
- - `getChannelPairingSnapshot(channel: PairableRemoteChannel)`
- - `createChannelPairCode(channel: PairableRemoteChannel)`
- - `clearChannelPairCode(channel: PairableRemoteChannel)`
-- Weixin iLink account flow
- - `startWeixinIlinkLogin(input?: { force?: boolean })`
- - `waitForWeixinIlinkLogin({ sessionKey, timeoutMs? })`
- - `removeWeixinIlinkAccount(accountId)`
- - `restartWeixinIlinkAccount(accountId)`
-
-Compatibility notes:
-
-- Telegram compatibility helpers remain callable during migration.
-- New multi-channel renderer or preload work should not add fresh Telegram-only methods when
- the generic presenter surface already covers the use case.
-
-## Channel Flow Plan
-
-### 1. Shared Read And Write Flow
-
-- Renderer loads channel descriptors through `listRemoteChannels()`.
-- Channel settings read and write go through `getChannelSettings()` and
- `saveChannelSettings()`.
-- Status cards, sidebar aggregation, and per-tab runtime summaries use `getChannelStatus()`.
-- Binding tables use `getChannelBindings()`, `removeChannelBinding()`, and
- `clearChannelBindings()`.
-
-### 2. Telegram
-
-- Settings remain `botToken`, `remoteEnabled`, and `defaultAgentId`.
-- Status remains Telegram-specific:
- - `pollOffset`
- - `bindingCount`
- - `allowedUserCount`
- - `lastError`
- - `botUser`
-- Pairing flow remains pair-code based with `allowedUserIds` in the pairing snapshot.
-- The original Telegram runtime behavior stays intact:
- - detached sessions
- - `/stop`
- - `/sessions`
- - `/use`
- - `/model`
- - temporary status messages
- - plain-text delivery
-
-### 3. QQBot
-
-- Settings use `appId`, `clientSecret`, `remoteEnabled`, `defaultAgentId`, `defaultWorkdir`,
- and `pairedUserIds`.
-- Status uses `bindingCount`, `pairedUserCount`, `lastError`, and `botUser`.
-- Pairing flow remains pair-code based, but the snapshot must expose both `pairedUserIds` and
- `pairedGroupIds`.
-- Group authorization stays separate from C2C pairing because QQ identity spaces differ.
-
-### 4. Discord
-
-- Settings use `botToken`, `remoteEnabled`, `defaultAgentId`, `defaultWorkdir`, and
- `pairedChannelIds`.
-- Status uses `bindingCount`, `pairedChannelCount`, `lastError`, and `botUser`.
-- Pairing flow remains pair-code based with `pairedChannelIds` in the snapshot.
-
-### 5. Weixin iLink
-
-- Settings use `remoteEnabled`, `defaultAgentId`, `defaultWorkdir`, and connected `accounts`.
-- Status uses `accountCount`, `connectedAccountCount`, `accounts`, `bindingCount`, and
- `lastError`.
-- Weixin iLink does not use pair-code APIs.
-- Login and runtime ownership use:
- - `startWeixinIlinkLogin()`
- - `waitForWeixinIlinkLogin()`
- - `removeWeixinIlinkAccount()`
- - `restartWeixinIlinkAccount()`
-
-### 6. Feishu/Lark Compatibility
-
-- Feishu/Lark stays on the same presenter contract and remains pair-code based.
-- This feature folder does not redefine Feishu-specific behavior, but follow-up work must not
- remove Feishu from the shared remote presenter surface.
-
-## Milestones
-
-### M0 Spec Hygiene
-
-- Align `spec.md`, `plan.md`, and `tasks.md` with the shipped presenter contract.
-- Record resolved clarification items so follow-up work does not re-open Telegram-only
- assumptions.
-
-### M1 Shared Presenter Surface
-
-- Keep the documented remote presenter centered on `listRemoteChannels()`, generic per-channel
- settings, generic per-channel status, generic bindings, and pairable-channel pairing APIs.
-- Preserve Telegram compatibility helpers as migration shims only.
-
-Exit criteria:
-
-- This feature folder no longer documents a Telegram-only IPC surface.
-- The expected method list matches `IRemoteControlPresenter`.
-
-### M2 Pairable Channel Variations
-
-- Document Telegram, QQBot, Discord, and Feishu/Lark pair-code flows with the correct channel
- snapshots and principal-removal semantics.
-- Keep QQBot group authorization explicit in the plan.
-
-Exit criteria:
-
-- Telegram, QQBot, and Discord pairing snapshots are documented with their channel-specific
- fields.
-- Pair-code APIs are clearly limited to `PairableRemoteChannel`.
-
-### M3 Weixin iLink Account Flow
-
-- Document Weixin iLink as an account-login flow instead of a pair-code flow.
-- Keep account lifecycle methods and binding cleanup on the shared presenter surface.
-
-Exit criteria:
-
-- The feature docs describe Weixin iLink login and account restart/removal paths explicitly.
-- The plan does not imply that Weixin iLink participates in pair-code APIs.
-
-### M4 Renderer And Preload Usage
-
-- Keep new Remote settings, status aggregation, and binding views aligned with the generic
- presenter surface.
-- Block new Telegram-only branching in follow-up renderer or preload work unless it is a true
- compatibility shim.
-
-Exit criteria:
-
-- Shared methods are the preferred path for new UI work.
-- Channel-specific differences are documented without changing the IPC shape.
-
-### M5 QA And Compatibility
-
-- Verify the feature docs mirror the current shared types.
-- Verify Telegram runtime expectations remain documented.
-- Keep compatibility guarantees explicit for Telegram compatibility methods and existing
- non-Telegram channels.
-
-Exit criteria:
-
-- Acceptance criteria in [spec.md](./spec.md) line up with the current presenter type and
- channel behavior.
-
-## Rollout Steps
-
-1. Treat the shared presenter type as the documentation baseline for this feature.
-2. Route new renderer or preload work through generic channel methods first.
-3. Use channel-specific sections only for payload or lifecycle differences:
- Telegram allowlist pairing, QQBot group authorization, Discord paired channels, and Weixin
- iLink account login.
-4. Keep Telegram compatibility helpers only while existing callers still need them.
-5. Verify docs and code stay aligned whenever the remote presenter surface changes.
-
-## QA And Compatibility Checks
-
-- Contract checks
- - `spec.md`, `plan.md`, and `tasks.md` name the same channel set:
- `telegram`, `feishu`, `qqbot`, `discord`, `weixin-ilink`
- - The documented methods match the shared presenter type.
-- Channel checks
- - Telegram documents `allowedUserIds` pairing snapshots.
- - QQBot documents `pairedUserIds` and `pairedGroupIds`.
- - Discord documents `pairedChannelIds`.
- - Weixin iLink documents login-session and account-management methods instead of pair-code
- methods.
-- Compatibility checks
- - Telegram runtime behavior from the original feature remains documented.
- - Telegram compatibility methods are still called out as supported shims.
- - Feishu/Lark stays represented on the shared presenter surface.
-- Quality gates
- - `pnpm run format`
- - `pnpm run i18n`
- - `pnpm run lint`
-
-## Resolved Clarifications
-
-- Pair-code methods apply only to `telegram`, `feishu`, `qqbot`, and `discord`.
-- Weixin iLink uses login-session methods and account lifecycle methods instead of pair codes.
-- Binding APIs apply to all channels, including Weixin iLink account-scoped bindings.
-- Telegram compatibility methods remain supported, but they are not the preferred long-term
- contract for new work.
diff --git a/docs/archives/telegram-remote-control/spec.md b/docs/archives/telegram-remote-control/spec.md
deleted file mode 100644
index d8fe00a3e..000000000
--- a/docs/archives/telegram-remote-control/spec.md
+++ /dev/null
@@ -1,176 +0,0 @@
-# Telegram Remote Control
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-Feature: `telegram-remote-control`
-Plan: [plan.md](./plan.md)
-Tasks: [tasks.md](./tasks.md)
-
-## Summary
-
-Telegram remote control is no longer a Telegram-only IPC contract. The shipped implementation
-uses a shared `remoteControlPresenter` surface that serves Telegram, Feishu/Lark, QQBot,
-Discord, and Weixin iLink from the same presenter and preload boundary.
-
-This spec keeps Telegram runtime behavior in scope, but the renderer and preload contract must
-be documented as a multi-channel API so follow-up work does not regress non-Telegram channels
-back to a Telegram-only shape.
-
-## User Stories
-
-- As a DeepChat desktop user, I can configure Telegram, QQBot, Discord, and Weixin iLink from
- one Remote settings flow, while Feishu/Lark compatibility remains on the same presenter
- surface.
-- As a renderer maintainer, I can call one typed presenter API for reading and saving
- per-channel settings, status, bindings, pairing state, and login state.
-- As a Telegram user, I can pair numeric user ids and continue detached DeepChat sessions from
- bot DMs.
-- As a QQBot user, I can keep C2C user pairing separate from authorized groups so group
- control does not disappear from the presenter state.
-- As a Discord user, I can pair DMs or guild channels and inspect runtime state from the same
- Remote settings surface.
-- As a Weixin iLink user, I can connect accounts through a login session instead of a pair
- code and manage account-scoped runtimes.
-
-## Multi-Channel Presenter Surface
-
-The expected shared presenter contract mirrors `IRemoteControlPresenter`:
-
-```ts
-type RemoteChannel = 'telegram' | 'feishu' | 'qqbot' | 'discord' | 'weixin-ilink'
-type PairableRemoteChannel = 'telegram' | 'feishu' | 'qqbot' | 'discord'
-
-interface IRemoteControlPresenter {
- listRemoteChannels(): Promise
-
- getChannelSettings(channel: T): Promise
- saveChannelSettings(
- channel: T,
- input: ChannelSettingsMap[T]
- ): Promise
-
- getChannelStatus(channel: RemoteChannel): Promise
-
- getChannelBindings(channel: RemoteChannel): Promise
- removeChannelBinding(channel: RemoteChannel, endpointKey: string): Promise
- clearChannelBindings(channel: RemoteChannel): Promise
-
- removeChannelPrincipal(channel: PairableRemoteChannel, principalId: string): Promise
- getChannelPairingSnapshot(channel: PairableRemoteChannel): Promise
- createChannelPairCode(
- channel: PairableRemoteChannel
- ): Promise<{ code: string; expiresAt: number }>
- clearChannelPairCode(channel: PairableRemoteChannel): Promise
-
- startWeixinIlinkLogin(input?: { force?: boolean }): Promise
- waitForWeixinIlinkLogin(input: {
- sessionKey: string
- timeoutMs?: number
- }): Promise
- removeWeixinIlinkAccount(accountId: string): Promise
- restartWeixinIlinkAccount(accountId: string): Promise
-}
-```
-
-Notes:
-
-- Telegram compatibility helpers remain callable during migration, but new renderer work should
- prefer the generic channel methods above.
-- Pair-code methods do not apply to Weixin iLink because that channel uses an account-login
- flow instead of a pairing code.
-
-## Channel Variations
-
-- Telegram
- - Settings: `botToken`, `remoteEnabled`, `defaultAgentId`
- - Status: `pollOffset`, `bindingCount`, `allowedUserCount`, `lastError`, `botUser`
- - Pairing snapshot: `allowedUserIds`
-- QQBot
- - Settings: `appId`, `clientSecret`, `remoteEnabled`, `defaultAgentId`, `defaultWorkdir`,
- `pairedUserIds`
- - Status: `bindingCount`, `pairedUserCount`, `lastError`, `botUser`
- - Pairing snapshot: `pairedUserIds` and `pairedGroupIds`
-- Discord
- - Settings: `botToken`, `remoteEnabled`, `defaultAgentId`, `defaultWorkdir`,
- `pairedChannelIds`
- - Status: `bindingCount`, `pairedChannelCount`, `lastError`, `botUser`
- - Pairing snapshot: `pairedChannelIds`
-- Weixin iLink
- - Settings: `remoteEnabled`, `defaultAgentId`, `defaultWorkdir`, `accounts`
- - Status: `accountCount`, `connectedAccountCount`, `accounts`, `bindingCount`, `lastError`
- - Login flow: `startWeixinIlinkLogin`, `waitForWeixinIlinkLogin`,
- `removeWeixinIlinkAccount`, `restartWeixinIlinkAccount`
-- Feishu/Lark compatibility
- - Remains on the same presenter contract with pair codes and paired open ids, even though
- this feature folder focuses on Telegram-originated Remote UX.
-
-## Acceptance Criteria
-
-- This feature folder documents the shipped multi-channel presenter contract instead of a
- Telegram-only IPC subset.
-- The documented channel set includes `telegram`, `feishu`, `qqbot`, `discord`, and
- `weixin-ilink`.
-- Renderer and preload callers use generic `getChannelSettings` and `saveChannelSettings`,
- `getChannelStatus`, `getChannelBindings`, `removeChannelBinding`, and `clearChannelBindings`
- for all shipped channels.
-- Pairable channels (`telegram`, `feishu`, `qqbot`, `discord`) use
- `getChannelPairingSnapshot`, `createChannelPairCode`, `clearChannelPairCode`, and
- `removeChannelPrincipal`.
-- Telegram pairing snapshots expose `allowedUserIds`.
-- QQBot pairing snapshots expose both `pairedUserIds` and `pairedGroupIds`.
-- Discord pairing snapshots expose `pairedChannelIds`.
-- Weixin iLink documents account-login and account-lifecycle methods instead of pair-code
- methods.
-- Remote settings follow per-channel Telegram, QQBot, Discord, and Weixin iLink flows rather
- than reusing Telegram-only assumptions.
-- Telegram runtime behavior from the original feature remains unchanged:
- - detached-session creation from the remote flow
- - `/stop`, `/sessions`, `/use`, and `/model`
- - temporary status message handling
- - plain-text-only remote delivery
-
-## Constraints
-
-- The shared presenter lives in Electron main and crosses renderer through the existing
- presenter IPC path.
-- Per-channel settings stay in their existing config roots:
- - `remoteControl.telegram`
- - `remoteControl.feishu`
- - `remoteControl.qqbot`
- - `remoteControl.discord`
- - `remoteControl.weixinIlink`
-- `RemoteBindingStore`, `RemoteConversationRunner`, and `remoteBlockRenderer` remain the
- source of truth for bindings, session orchestration, and rendered delivery text.
-- Pair-code APIs are limited to pairable channels; Weixin iLink uses login-session APIs.
-- Existing Telegram compatibility methods remain callable during migration, but new renderer
- work should prefer generic channel methods.
-
-## Non-Goals
-
-- Reverting to a Telegram-only presenter or reintroducing Telegram-specific IPC branches for
- shared settings flows.
-- Collapsing Weixin iLink account management into the pair-code flow.
-- Removing Feishu/Lark compatibility from the shared presenter surface.
-- Changing Telegram runtime transport rules that are already implemented outside this
- documentation fix.
-
-## Compatibility
-
-- Existing Telegram runtime behavior and saved settings remain valid.
-- Existing Feishu/Lark, QQBot, Discord, and Weixin iLink settings stay under their current
- config shapes.
-- Existing Telegram compatibility methods continue to work while generic multi-channel methods
- remain the preferred path.
-- Follow-up work may refine channel-specific UI, but it must not narrow the shared presenter
- surface back to Telegram-only.
-
-## Resolved Clarifications
-
-- `PairableRemoteChannel` is `telegram | feishu | qqbot | discord`; Weixin iLink is excluded
- because it uses login sessions, not pair codes.
-- `getChannelBindings`, `removeChannelBinding`, and `clearChannelBindings` apply to all
- channels, including Weixin iLink account-scoped bindings.
-- Telegram compatibility methods are retained as migration shims, not as the preferred
- long-term renderer contract.
-- No open `[NEEDS CLARIFICATION]` markers remain in this feature folder.
diff --git a/docs/archives/telegram-remote-control/tasks.md b/docs/archives/telegram-remote-control/tasks.md
deleted file mode 100644
index ad07f0829..000000000
--- a/docs/archives/telegram-remote-control/tasks.md
+++ /dev/null
@@ -1,112 +0,0 @@
-# Telegram Remote Control Tasks
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-Feature: `telegram-remote-control`
-Spec: [spec.md](./spec.md)
-Plan: [plan.md](./plan.md)
-
-These tasks document the remote presenter contract that follow-up work must preserve. They are
-tracked as completed because the current codebase already ships this surface; future changes
-should use them as a regression checklist.
-
-## Readiness And Clarifications
-
-- [x] `T0.1` Keep companion artifacts in `docs/archives/telegram-remote-control/`:
- `spec.md`, `plan.md`, and `tasks.md`.
- Owner: Remote control maintainer
- Effort: XS
-- [x] `T0.2` Resolve the presenter-scope clarification: the feature now documents the shared
- multi-channel `remoteControlPresenter` contract instead of a Telegram-only IPC surface.
- Owner: Remote control maintainer
- Effort: XS
-- [x] `T0.3` Resolve the pairing clarification: pair-code methods apply only to
- `telegram`, `feishu`, `qqbot`, and `discord`; Weixin iLink uses login-session methods.
- Owner: Remote control maintainer
- Effort: XS
-- [x] `T0.4` Resolve the binding clarification: binding list, remove, and clear flows apply to
- every remote channel, including Weixin iLink account-scoped bindings.
- Owner: Remote control maintainer
- Effort: XS
-
-## Epic E1 Shared Presenter Surface
-
-- [x] `T1.1` Document `listRemoteChannels()` and the shipped channel set:
- `telegram`, `feishu`, `qqbot`, `discord`, `weixin-ilink`.
- Owner: Electron main + preload
- Effort: S
-- [x] `T1.2` Document generic per-channel settings APIs:
- `getChannelSettings()` and `saveChannelSettings()`.
- Owner: Electron main + preload
- Effort: S
-- [x] `T1.3` Document generic runtime and binding APIs:
- `getChannelStatus()`, `getChannelBindings()`, `removeChannelBinding()`,
- `clearChannelBindings()`.
- Owner: Electron main + renderer
- Effort: S
-- [x] `T1.4` Document pairable-channel APIs:
- `removeChannelPrincipal()`, `getChannelPairingSnapshot()`, `createChannelPairCode()`,
- `clearChannelPairCode()`.
- Owner: Electron main + renderer
- Effort: S
-- [x] `T1.5` Document Weixin iLink account APIs:
- `startWeixinIlinkLogin()`, `waitForWeixinIlinkLogin()`, `removeWeixinIlinkAccount()`,
- `restartWeixinIlinkAccount()`.
- Owner: Electron main + renderer
- Effort: S
-- [x] `T1.6` Record Telegram compatibility methods as supported migration shims rather than the
- preferred long-term presenter surface.
- Owner: Electron main + renderer
- Effort: XS
-
-## Epic E2 Channel Variations
-
-- [x] `T2.1` Keep Telegram documentation explicit about settings, runtime status, pair-code
- flow, `allowedUserIds`, and original command/runtime behavior.
- Owner: Telegram runtime maintainer
- Effort: M
-- [x] `T2.2` Keep QQBot documentation explicit about settings, runtime status, pair-code flow,
- `pairedUserIds`, and `pairedGroupIds`.
- Owner: QQBot runtime maintainer
- Effort: M
-- [x] `T2.3` Keep Discord documentation explicit about settings, runtime status, pair-code
- flow, and `pairedChannelIds`.
- Owner: Discord runtime maintainer
- Effort: M
-- [x] `T2.4` Keep Weixin iLink documentation explicit about settings, runtime status,
- account-login flow, and account lifecycle methods.
- Owner: Weixin iLink runtime maintainer
- Effort: M
-- [x] `T2.5` Preserve Feishu/Lark on the shared presenter surface even though this feature
- folder remains Telegram-originated.
- Owner: Remote control maintainer
- Effort: S
-
-## Epic E3 Renderer And Preload Adoption
-
-- [x] `T3.1` Treat generic channel methods as the preferred path for new Remote settings work.
- Owner: Renderer
- Effort: S
-- [x] `T3.2` Keep status cards, binding views, and follow-up IPC work aligned with generic
- channel reads instead of fresh Telegram-only branches.
- Owner: Renderer + preload
- Effort: S
-- [x] `T3.3` Allow Telegram-only helpers to remain only for compatibility with existing callers.
- Owner: Renderer + preload
- Effort: XS
-
-## Epic E4 QA And Acceptance
-
-- [x] `T4.1` Verify the feature docs mirror
- `src/shared/types/presenters/remote-control.presenter.d.ts`.
- Owner: QA + Remote control maintainer
- Effort: XS
-- [x] `T4.2` Verify acceptance criteria cover Telegram, QQBot, Discord, and Weixin iLink
- variations without dropping Feishu/Lark from the shared channel set.
- Owner: QA + Remote control maintainer
- Effort: S
-- [x] `T4.3` Run repository quality gates after documentation updates:
- `pnpm run format`, `pnpm run i18n`, `pnpm run lint`.
- Owner: QA + Remote control maintainer
- Effort: XS
diff --git a/docs/archives/thread-presenter-migration-plan.md b/docs/archives/thread-presenter-migration-plan.md
deleted file mode 100644
index 3dd7e5968..000000000
--- a/docs/archives/thread-presenter-migration-plan.md
+++ /dev/null
@@ -1,2727 +0,0 @@
-# ThreadPresenter 迁移方案:完全迁移到 SessionPresenter
-
-> Archive note: This document is a historical record. File paths and implementation names can reference code that has since moved or been removed.
-
-
-## 概述
-
-本文档详细描述了从 `threadPresenter` 到 `sessionPresenter` 的完整迁移方案。目标是消除 `threadPresenter`,将所有功能按职责分离到合适的模块中,建立清晰的架构边界。
-
-### 核心原则
-
-1. **sessionPresenter** - 负责核心会话管理(生命周期、消息、Agent Loop、ACP、权限协调)
-2. **searchPresenter** - 独立模块,负责搜索引擎和搜索执行
-3. **exporter** - 通用模块,负责会话导出(跨 presenter 使用)
-4. **共享模块** - 消息格式化、内容增强、权限检查等作为共享工具
-
-### 迁移策略
-
-- **完全迁移**:不保留向后兼容,直接替换
-- **功能归属**:所有会话相关功能都去 sessionPresenter;额外功能扩充到 utility 或其他合适的 presenter
-- **保留功能**:子会话/父子会话、消息变体系统完全支持
-
----
-
-## 当前架构分析
-
-### ThreadPresenter 结构(待废弃)
-
-```
-threadPresenter/
-├── handlers/ # 9个处理器,负责特定领域的业务逻辑
-│ ├── baseHandler.ts (20行) - 基础处理器
-│ ├── streamGenerationHandler.ts (645行) - 流式生成处理
-│ ├── llmEventHandler.ts - LLM事件处理
-│ ├── toolCallHandler.ts - 工具调用处理
-│ ├── permissionHandler.ts (862行) - 权限处理
-│ ├── searchHandler.ts (350行) - 搜索处理
-│ ├── utilityHandler.ts (462行) - 工具处理
-│ ├── commandPermissionHandler.ts (275行) - 命令权限
-│ └── contentBufferHandler.ts (196行) - 流式缓冲
-├── managers/ # 3个管理器,负责资源管理和数据持久化
-│ ├── messageManager.ts (389行) - 消息管理
-│ ├── conversationManager.ts (524行) - 会话管理
-│ └── searchManager.ts (1405行) - 搜索管理
-├── exporters/ # 2个导出器,负责数据格式转换
-│ ├── conversationExporter.ts (691行) - 会话导出
-│ └── nowledgeMemExporter.ts (302行) - NowledgeMem导出
-├── utils/ # 工具类,提供辅助功能
-│ └── contentEnricher.ts (384行) - 内容增强
-├── templates/
-│ └── conversationExportTemplates.ts - 导出模板
-├── types.ts # 类型定义
-├── const.ts # 常量定义(搜索提示模板)
-└── index.ts # 主入口(1140行)
-```
-
-### SessionPresenter 结构(目标)
-
-```
-sessionPresenter/
-├── index.ts # 主入口 (576行)
-├── interface.ts # 接口定义 (88行)
-├── types.ts # 类型定义 (81行)
-├── events.ts # 事件定义 (13行)
-│
-├── session/
-│ └── sessionManager.ts # 会话管理器 (247行)
-│
-├── tab/
-│ ├── tabAdapter.ts
-│ ├── tabManager.ts # Tab管理器 (210行)
-│ └── index.ts
-│
-├── loop/
-│ ├── agentLoopHandler.ts # Agent循环处理器 (670行)
-│ ├── loopOrchestrator.ts # 循环编排器 (25行)
-│ ├── loopState.ts # 循环状态 (26行)
-│ ├── errorClassification.ts # 错误分类
-│ └── toolCallProcessor.ts # 工具调用处理器 (445行)
-│
-├── persistence/
-│ ├── conversationPersister.ts # 会话持久化 (46行)
-│ ├── messagePersister.ts # 消息持久化 (97行)
-│ └── index.ts
-│
-├── message/
-│ ├── messageBuilder.ts # 消息构造器 (285行)
-│ ├── messageTruncator.ts
-│ ├── messageFormatter.ts
-│ ├── messageCompressor.ts
-│ └── index.ts
-│
-├── acp/ # ACP模块 (13个文件)
-│ ├── acpProcessManager.ts # ACP进程管理 (1132行)
-│ ├── acpSessionManager.ts # ACP会话管理 (370行)
-│ ├── agentToolManager.ts # Agent工具 (577行)
-│ ├── agentFileSystemHandler.ts# 文件系统 (960行)
-│ └── ... (其他 ACP 相关文件)
-│
-├── tool/
-│ ├── toolCallCenter.ts # 工具调用中心 (26行)
-│ ├── toolRegistry.ts
-│ ├── toolRouter.ts
-│ └── index.ts
-│
-└── utility/
- ├── promptEnhancer.ts # 提示词增强
- └── index.ts
-```
-
----
-
-## 功能归属表
-
-### 现有模块归属
-
-| 模块 | 源文件行数 | 所属 | 说明 | 优先级 |
-|------|-----------|------|------|--------|
-| **会话管理核心** | | | | |
-| ConversationManager | 524行 | sessionPresenter | 会话CRUD、Tab绑定、分支、子会话 | P0 |
-| MessageManager | 389行 | sessionPresenter | 消息CRUD、变体、历史、分页 | P0 |
-| StreamGenerationHandler | 645行 | sessionPresenter | 流式生成协调、搜索集成 | P0 |
-| LLMEventHandler | 389行 | sessionPresenter | LLM事件处理(响应/错误/结束) | P0 |
-| ToolCallHandler | 525行 | sessionPresenter | 工具调用块管理、权限阶段 | P1 |
-| PermissionHandler | 862行 | sessionPresenter | 权限响应协调、ACP/MCP/命令 | P0 |
-| ContentBufferHandler | 196行 | sessionPresenter | 流式缓冲优化、节流发送 | P1 |
-| **搜索功能** | | | | |
-| SearchManager | 1405行 | searchPresenter | 搜索引擎管理、窗口管理、搜索执行 | P0 |
-| SearchHandler | 350行 | searchPresenter | 搜索适配器、LLM查询优化 | P0 |
-| 搜索提示模板 | - | searchPrompts | 搜索提示模板常量 | P2 |
-| **导出功能** | | | | |
-| ConversationExporter | 691行 | exporter | Markdown/HTML/TXT导出 | P1 |
-| NowledgeMemExporter | 302行 | nowledgeMemPresenter | 已存在,保持独立 | 保持 |
-| 导出模板 | - | exporter | HTML模板 | P2 |
-| **辅助功能** | | | | |
-| UtilityHandler::translateText | - | sessionPresenter/utility | 翻译服务 | P1 |
-| UtilityHandler::askAI | - | sessionPresenter/utility | AI问答服务 | P1 |
-| UtilityHandler::summaryTitles | - | sessionPresenter/utility | 标题生成 | P1 |
-| UtilityHandler::exportConversation | - | exporter | 已存在 | 已迁移 |
-| UtilityHandler::getMessageRequestPreview | - | sessionPresenter/utility | 调试预览 | P2 |
-| CommandPermissionHandler | 275行 | permission | 命令权限检查、风险评估 | P0 |
-| ContentEnricher | 384行 | content | URL内容提取、HTML解析 | P2 |
-
----
-
-## 目标架构
-
-```
-src/main/presenter/
-├── sessionPresenter/ # 核心:所有会话管理功能
-│ ├── index.ts # 主入口,实现 ISessionPresenter
-│ ├── interface.ts
-│ ├── types.ts
-│ ├── events.ts
-│ │
-│ ├── session/
-│ │ └── sessionManager.ts
-│ ├── tab/
-│ │ ├── tabAdapter.ts
-│ │ ├── tabManager.ts
-│ │ └── index.ts
-│ ├── loop/
-│ │ ├── agentLoopHandler.ts
-│ │ ├── loopOrchestrator.ts
-│ │ ├── loopState.ts
-│ │ ├── errorClassification.ts
-│ │ ├── toolCallProcessor.ts
-│ │ └── index.ts
-│ ├── persistence/
-│ │ ├── conversationPersister.ts
-│ │ ├── messagePersister.ts
-│ │ └── index.ts
-│ ├── message/
-│ │ ├── messageBuilder.ts
-│ │ ├── messageTruncator.ts
-│ │ ├── messageFormatter.ts
-│ │ ├── messageCompressor.ts
-│ │ └── index.ts
-│ ├── acp/
-│ │ ├── acpProcessManager.ts
-│ │ ├── acpSessionManager.ts
-│ │ ├── agentToolManager.ts
-│ │ ├── agentFileSystemHandler.ts
-│ │ ├── agentBashHandler.ts
-│ │ ├── acpFsHandler.ts
-│ │ ├── acpTerminalManager.ts
-│ │ ├── acpMessageFormatter.ts
-│ │ ├── acpPersistence.ts
-│ │ ├── shellEnvHelper.ts
-│ │ ├── mcpTransportFilter.ts
-│ │ ├── mcpConfigConverter.ts
-│ │ ├── acpContentMapper.ts
-│ │ ├── commandProcessTracker.ts
-│ │ ├── types.ts
-│ │ └── index.ts
-│ ├── tool/
-│ │ ├── toolCallCenter.ts
-│ │ ├── toolRegistry.ts
-│ │ ├── toolRouter.ts
-│ │ └── index.ts
-│ ├── permission/ # 新增:权限协调
-│ │ ├── permissionCoordinator.ts
-│ │ └── index.ts
-│ ├── utility/ # 新增:辅助功能
-│ │ ├── aiService.ts
-│ │ ├── debuggerService.ts
-│ │ ├── titleGenerator.ts
-│ │ ├── promptEnhancer.ts
-│ │ └── index.ts
-│ ├── managers/ # 新增:核心管理器
-│ │ ├── conversationManager.ts
-│ │ ├── messageManager.ts
-│ │ └── index.ts
-│ ├── streaming/
-│ │ ├── streamingGenerator.ts # 新增整合类
-│ │ ├── streamEventHandler.ts
-│ │ ├── contentBufferHandler.ts
-│ │ └── index.ts
-│ └── const.ts
-│
-├── searchPresenter/ # 搜索功能独立模块
-│ ├── index.ts
-│ ├── interface.ts
-│ ├── managers/
-│ │ ├── searchManager.ts
-│ │ └── index.ts
-│ ├── handlers/
-│ │ ├── searchHandler.ts
-│ │ └── index.ts
-│ └── types.ts
-│
-├── exporter/ # 导出服务(跨presenter)
-│ ├── index.ts
-│ ├── interface.ts
-│ ├── formats/
-│ │ ├── conversationExporter.ts
-│ │ ├── markdownExporter.ts
-│ │ ├── htmlExporter.ts
-│ │ ├── textExporter.ts
-│ │ └── index.ts
-│ ├── templates/
-│ │ └── conversationExportTemplates.ts
-│ └── types.ts
-│
-├── knowledgePresenter/ # 保留(不迁移)
-├── nowledgeMemPresenter/ # 保留(独立功能)
-│
-├── permission/ # 共享:权限工具
-│ ├── commandPermissionService.ts
-│ ├── commandPermissionCache.ts
-│ └── index.ts
-│
-├── content/ # 共享:内容处理
-│ ├── contentEnricher.ts
-│ ├── htmlParser.ts
-│ └── index.ts
-│
-├── searchPrompts/ # 共享:搜索提示
-│ ├── searchPrompts.ts
-│ ├── templates/
-│ │ ├── searchPromptTemplate.ts
-│ │ └── searchPromptArtifactsTemplate.ts
-│ └── index.ts
-│
-├── index.ts # 更新:移除 threadPresenter
-└── ... (其他 presenter 保持不变)
-```
-
----
-
-## 迁移阶段计划
-
-### 阶段 1:准备基础设施(1-2天)
-
-#### 1.1 创建新目录结构
-
-```bash
-mkdir -p src/main/presenter/searchPresenter/{managers,handlers}
-mkdir -p src/main/presenter/exporter/{formats,templates}
-mkdir -p src/main/presenter/searchPrompts/templates
-mkdir -p src/main/presenter/permission
-mkdir -p src/main/presenter/content
-mkdir -p src/main/presenter/sessionPresenter/{permission,utility,managers,streaming}
-```
-
-#### 1.2 创建基础接口定义
-
-**`src/main/presenter/searchPresenter/interface.ts`**
-
-```typescript
-export interface ISearchPresenter {
- // 搜索引擎管理
- getEngines(): Promise
- getActiveEngine(): Promise
- setActiveEngine(engineId: string): Promise
- testEngine(query?: string): Promise
-
- // 搜索执行(通过 conversationId 关联到会话)
- executeSearch(conversationId: string, query: string): Promise
- stopSearch(conversationId: string): Promise
-
- // 搜索配置
- updateEngines(engines: SearchEngineConfig[]): Promise
- addCustomEngine(engine: SearchEngineConfig): Promise
- removeCustomEngine(engineId: string): Promise
-
- // 工具方法
- search(query: string): Promise
- testSearch(query?: string): Promise
-}
-```
-
-**`src/main/presenter/exporter/interface.ts`**
-
-```typescript
-export interface IConversationExporter {
- // 导出格式
- exportConversation(
- conversationId: string,
- format: 'markdown' | 'html' | 'txt'
- ): Promise<{ filename: string; content: string }>
-
- // NowledgeMem 集成(委派给 NowledgeMemPresenter)
- exportToNowledgeMem(conversationId: string): Promise<{
- success: boolean
- data?: NowledgeMemThread
- summary?: NowledgeMemExportSummary
- errors?: string[]
- }>
-
- submitToNowledgeMem(conversationId: string): Promise<{
- success: boolean
- threadId?: string
- data?: NowledgeMemThread
- errors?: string[]
- }>
-
- testNowledgeMemConnection(): Promise<{
- success: boolean
- message?: string
- error?: string
- }>
-
- getNowledgeMemConfig(): NowledgeMemConfig
-}
-```
-
----
-
-### 阶段 2:迁移搜索功能(2-3天)
-
-#### 2.1 创建 SearchPresenter
-
-**`src/main/presenter/searchPresenter/index.ts`**
-
-```typescript
-import type { ISearchPresenter } from './interface'
-import { SearchManager } from './managers/searchManager'
-import { SearchHandler } from './handlers/searchHandler'
-import type { IConfigPresenter, IWindowPresenter } from '@shared/presenter'
-import type { ContentEnricher } from '@/main/presenter/content/contentEnricher'
-
-interface SearchPresenterDependencies {
- configPresenter: IConfigPresenter
- windowPresenter: IWindowPresenter
- contentEnricher: ContentEnricher
-}
-
-export class SearchPresenter implements ISearchPresenter {
- private readonly searchManager: SearchManager
- private readonly searchHandler: SearchHandler
-
- constructor(deps: SearchPresenterDependencies) {
- this.searchManager = new SearchManager({
- configPresenter: deps.configPresenter,
- windowPresenter: deps.windowPresenter,
- contentEnricher: deps.contentEnricher
- })
- this.searchHandler = new SearchHandler(this.searchManager)
- }
-
- // 搜索引擎管理
- async getEngines(): Promise {
- return this.searchManager.getEngines()
- }
-
- async getActiveEngine(): Promise {
- return this.searchManager.getActiveEngine()
- }
-
- async setActiveEngine(engineId: string): Promise {
- return this.searchManager.setActiveEngine(engineId)
- }
-
- async testEngine(query?: string): Promise {
- return this.searchManager.testSearch(query)
- }
-
- // 搜索执行
- async executeSearch(conversationId: string, query: string): Promise {
- return this.searchHandler.startStreamSearch(conversationId, query)
- }
-
- async stopSearch(conversationId: string): Promise {
- return this.searchManager.stopSearch(conversationId)
- }
-
- // 搜索配置
- async updateEngines(engines: SearchEngineConfig[]): Promise {
- return this.searchManager.updateEngines(engines)
- }
-
- async addCustomEngine(engine: SearchEngineConfig): Promise {
- return this.searchManager.addCustomEngine(engine)
- }
-
- async removeCustomEngine(engineId: string): Promise {
- return this.searchManager.removeCustomEngine(engineId)
- }
-
- // 工具方法
- async search(query: string): Promise {
- return this.searchManager.search(query)
- }
-
- async testSearch(query?: string): Promise {
- return this.searchManager.testSearch(query)
- }
-
- destroy() {
- this.searchManager.destroy()
- }
-}
-```
-
-#### 2.2 迁移文件清单
-
-- `threadPresenter/managers/searchManager.ts` → `searchPresenter/managers/searchManager.ts`
-- `threadPresenter/handlers/searchHandler.ts` → `searchPresenter/handlers/searchHandler.ts`
-- `threadPresenter/const.ts` (搜索提示模板部分) → `searchPrompts/templates/searchPromptTemplate.ts`
-- `threadPresenter/const.ts` (搜索提示 artifacts 模板) → `searchPrompts/templates/searchPromptArtifactsTemplate.ts`
-
-#### 2.3 更新主 Presenter 初始化
-
-**修改 `src/main/presenter/index.ts`:**
-
-```typescript
-export class Presenter implements IPresenter {
- searchPresenter: ISearchPresenter
-
- private constructor(lifecycleManager: ILifecycleManager) {
- // ... 现有初始化 ...
-
- // 初始化 SearchPresenter
- this.searchPresenter = new SearchPresenter({
- configPresenter: this.configPresenter,
- windowPresenter: this.windowPresenter,
- contentEnricher: new ContentEnricher(this.configPresenter)
- })
- }
-
- // ...
-}
-```
-
----
-
-### 阶段 3:迁移导出功能(1-2天)
-
-#### 3.1 创建通用导出模块
-
-**`src/main/presenter/exporter/index.ts`**
-
-```typescript
-import type { IConversationExporter } from './interface'
-import { ConversationExporter } from './formats/conversationExporter'
-import { type ISQLitePresenter, type IConfigPresenter } from '@shared/presenter'
-
-interface ExporterDependencies {
- sqlitePresenter: ISQLitePresenter
- configPresenter: IConfigPresenter
-}
-
-export class ConversationExporterService implements IConversationExporter {
- private readonly exporter: ConversationExporter
-
- constructor(deps: ExporterDependencies) {
- this.exporter = new ConversationExporter(deps)
- }
-
- async exportConversation(
- conversationId: string,
- format: 'markdown' | 'html' | 'txt'
- ): Promise<{ filename: string; content: string }> {
- return this.exporter.export(conversationId, format)
- }
-
- async exportToNowledgeMem(conversationId: string): Promise<{
- success: boolean
- data?: NowledgeMemThread
- summary?: NowledgeMemExportSummary
- errors?: string[]
- }> {
- return this.exporter.exportToNowledgeMem(conversationId)
- }
-
- async submitToNowledgeMem(conversationId: string): Promise<{
- success: boolean
- threadId?: string
- data?: NowledgeMemThread
- errors?: string[]
- }> {
- return this.exporter.submitToNowledgeMem(conversationId)
- }
-
- getNowledgeMemConfig(): NowledgeMemConfig {
- return this.exporter.getConfig()
- }
-
- async testNowledgeMemConnection(): Promise<{
- success: boolean
- message?: string
- error?: string
- }> {
- return this.exporter.testConnection()
- }
-}
-```
-
-#### 3.2 迁移文件清单
-
-- `threadPresenter/exporters/conversationExporter.ts` → `exporter/formats/conversationExporter.ts`
-- `threadPresenter/exporters/templates/conversationExportTemplates.ts` → `exporter/templates/conversationExportTemplates.ts`
-- 保持 `nowledgeMemPresenter` 独立,不迁移
-
-#### 3.3 更新接口定义
-
-**`src/main/presenter/exporter/formats/conversationExporter.ts`**
-
-```typescript
-export class ConversationExporter {
- async export(
- conversationId: string,
- format: 'markdown' | 'html' | 'txt'
- ): Promise<{ filename: string; content: string }> {
- const conversation = await this.sqlitePresenter.getConversation(conversationId)
- if (!conversation) {
- throw new Error('Conversation not found')
- }
-
- // 获取所有消息(分页处理)
- const messages = await this.fetchAllMessages(conversationId)
-
- switch (format) {
- case 'markdown':
- return this.exportToMarkdown(conversation, messages)
- case 'html':
- return this.exportToHtml(conversation, messages)
- case 'txt':
- return this.exportToText(conversation, messages)
- default:
- throw new Error(`Unsupported format: ${format}`)
- }
- }
-
- private async exportToMarkdown(
- conversation: CONVERSATION,
- messages: Message[]
- ): Promise<{ filename: string; content: string }> {
- const content = buildMarkdownExportContent(conversation, messages)
- const filename = generateExportFilename(conversation, 'md')
- return { filename, content }
- }
-
- private async exportToHtml(
- conversation: CONVERSATION,
- messages: Message[]
- ): Promise<{ filename: string; content: string }> {
- const content = buildHtmlExportContent(conversation, messages)
- const filename = generateExportFilename(conversation, 'html')
- return { filename, content }
- }
-
- private async exportToTxt(
- conversation: CONVERSATION,
- messages: Message[]
- ): Promise<{ filename: string; content: string }> {
- const content = buildTextExportContent(conversation, messages)
- const filename = generateExportFilename(conversation, 'txt')
- return { filename, content }
- }
-
- // NowledgeMem 集成
- async exportToNowledgeMem(conversationId: string): Promise<{
- success: boolean
- data?: NowledgeMemThread
- summary?: NowledgeMemExportSummary
- errors?: string[]
- }> {
- const conversation = await this.sqlitePresenter.getConversation(conversationId)
- if (!conversation) {
- return { success: false, errors: ['Conversation not found'] }
- }
-
- const messages = await this.fetchAllMessages(conversationId)
- return buildNowledgeMemExportData(conversation, messages)
- }
-}
-```
-
----
-
-### 阶段 4:迁移会话核心功能到 sessionPresenter(3-4天)
-
-#### 4.1 迁移 ConversationManager
-
-**`src/main/presenter/sessionPresenter/managers/conversationManager.ts`**
-
-```typescript
-import type {
- CONVERSATION,
- CONVERSATION_SETTINGS,
- ISQLitePresenter,
- IConfigPresenter,
- ParentSelection
-} from '@shared/presenter'
-import type { Message } from '@shared/chat'
-import { presenter } from '@/presenter'
-import { eventBus, SendTarget } from '@/eventbus'
-import { CONVERSATION_EVENTS, TAB_EVENTS } from '@/events'
-import { DEFAULT_SETTINGS } from '../const'
-import type { MessageManager } from './messageManager'
-import type { TabManager } from '../tab/tabManager'
-
-export interface CreateConversationOptions {
- forceNewAndActivate?: boolean
- tabId?: number
-}
-
-export interface CreateChildConversationParams {
- parentConversationId: string
- parentMessageId: string
- parentSelection: ParentSelection | string
- title: string
- settings?: Partial
- tabId?: number
- openInNewTab?: boolean
-}
-
-export class ConversationManager {
- private readonly sqlitePresenter: ISQLitePresenter
- private readonly configPresenter: IConfigPresenter
- private readonly messageManager: MessageManager
- private readonly tabManager: TabManager
- private readonly activeConversationIds: Map
-
- constructor(options: {
- sqlitePresenter: ISQLitePresenter
- configPresenter: IConfigPresenter
- messageManager: MessageManager
- tabManager: TabManager
- activeConversationIds: Map
- }) {
- this.sqlitePresenter = options.sqlitePresenter
- this.configPresenter = options.configPresenter
- this.messageManager = options.messageManager
- this.tabManager = options.tabManager
- this.activeConversationIds = options.activeConversationIds
- }
-
- // === Tab绑定管理 ===
-
- getActiveConversationIdSync(tabId: number): string | null {
- return this.activeConversationIds.get(tabId) || null
- }
-
- getTabsByConversation(conversationId: string): number[] {
- return Array.from(this.activeConversationIds.entries())
- .filter(([, id]) => id === conversationId)
- .map(([tabId]) => tabId)
- }
-
- async setActiveConversation(conversationId: string, tabId: number): Promise {
- // 验证会话存在
- const conversation = await this.sqlitePresenter.getConversation(conversationId)
- if (!conversation) {
- throw new Error(`Conversation not found: ${conversationId}`)
- }
-
- // 检查是否已在其他 tab 中激活
- const existingTabId = await this.findTabForConversation(conversationId)
- if (existingTabId && existingTabId !== tabId) {
- // 可选:询问用户是否切换到已有的 tab
- console.warn(`Conversation ${conversationId} is already active in tab ${existingTabId}`)
- }
-
- // 设置激活状态
- this.activeConversationIds.set(tabId, conversationId)
- eventBus.sendToRenderer(CONVERSATION_EVENTS.ACTIVATED, SendTarget.ALL_WINDOWS, {
- tabId,
- conversationId
- })
- }
-
- async clearActiveConversation(tabId: number, options: { notify?: boolean } = {}): Promise {
- if (!this.activeConversationIds.has(tabId)) {
- return
- }
- this.activeConversationIds.delete(tabId)
- if (options.notify) {
- eventBus.sendToRenderer(CONVERSATION_EVENTS.DEACTIVATED, SendTarget.ALL_WINDOWS, { tabId })
- }
- }
-
- async clearConversationBindings(conversationId: string): Promise {
- for (const [tabId, activeId] of this.activeConversationIds.entries()) {
- if (activeId === conversationId) {
- this.activeConversationIds.delete(tabId)
- eventBus.sendToRenderer(CONVERSATION_EVENTS.DEACTIVATED, SendTarget.ALL_WINDOWS, {
- tabId
- })
- }
- }
- }
-
- async findTabForConversation(conversationId: string): Promise {
- for (const [tabId, activeId] of this.activeConversationIds.entries()) {
- if (activeId === conversationId) {
- try {
- const tabView = await presenter.tabPresenter.getTab(tabId)
- if (tabView && !tabView.webContents.isDestroyed()) {
- return tabId
- }
- } catch (error) {
- console.error('Error finding tab for conversation:', error)
- }
- }
- }
- return null
- }
-
- // === 会话 CRUD ===
-
- async createConversation(
- title: string,
- settings: Partial,
- tabId: number,
- options: CreateConversationOptions = {}
- ): Promise {
- const mergedSettings = { ...DEFAULT_SETTINGS, ...settings }
- const conversationId = await this.sqlitePresenter.createConversation(
- title,
- mergedSettings
- )
-
- // 自动激活
- if (options.forceNewAndActivate || !this.activeConversationIds.has(tabId)) {
- await this.setActiveConversation(conversationId, tabId)
- }
-
- return conversationId
- }
-
- async getConversation(conversationId: string): Promise {
- const conversation = await this.sqlitePresenter.getConversation(conversationId)
- if (!conversation) {
- throw new Error(`Conversation not found: ${conversationId}`)
- }
- return conversation
- }
-
- async getConversationList(
- page: number,
- pageSize: number
- ): Promise<{ total: number; list: CONVERSATION[] }> {
- return this.sqlitePresenter.getConversationList(page, pageSize)
- }
-
- async renameConversation(conversationId: string, title: string): Promise {
- return this.sqlitePresenter.updateConversation(conversationId, { title })
- }
-
- async deleteConversation(conversationId: string): Promise {
- // 清除绑定
- this.clearConversationBindings(conversationId)
-
- // 清除消息
- await this.sqlitePresenter.deleteAllMessages(conversationId)
-
- // 删除会话
- await this.sqlitePresenter.deleteConversation(conversationId)
- }
-
- async toggleConversationPinned(conversationId: string, pinned: boolean): Promise {
- return this.sqlitePresenter.updateConversation(conversationId, { pinned: pinned ? 1 : 0 })
- }
-
- async updateConversationSettings(
- conversationId: string,
- settings: Partial
- ): Promise {
- return this.sqlitePresenter.updateConversation(conversationId, { settings })
- }
-
- async updateConversationTitle(conversationId: string, title: string): Promise {
- return this.sqlitePresenter.updateConversation(conversationId, { title })
- }
-
- async loadMoreThreads(): Promise<{ hasMore: boolean; total: number }> {
- // 实现分页加载逻辑
- // 这里需要根据实际需求实现
- return { hasMore: false, total: 0 }
- }
-
- async broadcastThreadListUpdate(): Promise {
- eventBus.sendToRenderer(CONVERSATION_EVENTS.LIST_UPDATED, SendTarget.ALL_WINDOWS, {})
- }
-
- // === 会话分支 ===
-
- async forkConversation(
- targetConversationId: string,
- targetMessageId: string,
- newTitle: string,
- settings?: Partial,
- selectedVariantsMap?: Record
- ): Promise {
- // 1. 获取源会话和消息
- const sourceConversation = await this.sqlitePresenter.getConversation(targetConversationId)
- if (!sourceConversation) {
- throw new Error('Source conversation not found')
- }
-
- const targetMessage = await this.messageManager.getMessage(targetMessageId)
- if (!targetMessage) {
- throw new Error('Target message not found')
- }
-
- // 2. 创建新会话
- const mergedSettings = {
- ...sourceConversation.settings,
- ...settings,
- selectedVariantsMap,
- parentConversationId: targetConversationId,
- parentMessageId: targetMessageId,
- is_new: 0
- }
- mergedSettings.selectedVariantsMap = {}
-
- const newConversationId = await this.sqlitePresenter.createConversation(newTitle, mergedSettings)
-
- // 3. 复制消息(只到目标消息及其父消息)
- await this.copyMessagesUpTo(
- targetConversationId,
- newConversationId,
- targetMessageId,
- selectedVariantsMap
- )
-
- // 4. 广播更新
- await this.broadcastThreadListUpdate()
-
- return newConversationId
- }
-
- async createChildConversationFromSelection(
- payload: CreateChildConversationParams
- ): Promise {
- const {
- parentConversationId,
- parentMessageId,
- parentSelection,
- title,
- settings,
- tabId,
- openInNewTab
- } = payload
-
- const parentConversation = await this.sqlitePresenter.getConversation(parentConversationId)
- if (!parentConversation) {
- throw new Error('Parent conversation not found')
- }
-
- await this.messageManager.getMessage(parentMessageId)
-
- const mergedSettings = {
- ...parentConversation.settings,
- ...settings
- }
- mergedSettings.selectedVariantsMap = {}
-
- const newConversationId = await this.sqlitePresenter.createConversation(title, mergedSettings)
- const resolvedParentSelection =
- typeof parentSelection === 'string'
- ? (() => {
- try {
- return JSON.parse(parentSelection) as ParentSelection
- } catch {
- throw new Error('Invalid parent selection payload')
- }
- })()
- : parentSelection
- await this.sqlitePresenter.updateConversation(newConversationId, {
- is_new: 0,
- parentConversationId,
- parentMessageId,
- parentSelection: resolvedParentSelection
- })
-
- const shouldOpenInNewTab = openInNewTab ?? true
- if (shouldOpenInNewTab && tabId) {
- const sourceWindowId = presenter.tabPresenter.getWindowIdByWebContentsId(tabId)
- const fallbackWindowId = presenter.windowPresenter.getFocusedWindow()?.id
- const windowId = sourceWindowId ?? fallbackWindowId
-
- if (windowId) {
- const newTabId = await presenter.tabPresenter.createTab(windowId, 'local://chat', {
- active: true
- })
- if (newTabId) {
- await this.setActiveConversation(newConversationId, newTabId)
- await this.broadcastThreadListUpdate()
- return newConversationId
- }
- }
- }
-
- if (tabId) {
- await this.setActiveConversation(newConversationId, tabId)
- }
-
- await this.broadcastThreadListUpdate()
- return newConversationId
- }
-
- // === 子会话查询 ===
-
- async listChildConversationsByParent(parentConversationId: string): Promise {
- return this.sqlitePresenter.listChildConversationsByParent(parentConversationId)
- }
-
- async listChildConversationsByMessageIds(parentMessageIds: string[]): Promise {
- return this.sqlitePresenter.listChildConversationsByMessageIds(parentMessageIds)
- }
-
- // === 私有方法 ===
-
- private async copyMessagesUpTo(
- sourceConversationId: string,
- targetConversationId: string,
- targetMessageId: string,
- selectedVariantsMap?: Record
- ): Promise {
- const allMessages = await this.messageManager.getMessageHistory(targetMessageId, 1000)
-
- for (let i = 0; i < allMessages.length; i++) {
- const msg = allMessages[i]
- if (msg.id === targetMessageId) {
- // 到达目标消息,停止复制
- break
- }
-
- // 应用变体选择
- let content = msg.content
- if (msg.role === 'assistant' && selectedVariantsMap && selectedVariantsMap[msg.id] && msg.variants) {
- const variant = msg.variants.find((v) => v.id === selectedVariantsMap[msg.id])
- if (variant) {
- content = variant.content
- }
- }
-
- // 插入消息到目标会话
- await this.sqlitePresenter.insertMessage(
- targetConversationId,
- JSON.stringify(content),
- msg.role,
- msg.parentId || '',
- JSON.stringify(msg.usage),
- msg.is_variant,
- msg.timestamp || Date.now(),
- msg.status
- )
- }
- }
-}
-```
-
-#### 4.2 迁移 MessageManager
-
-**`src/main/presenter/sessionPresenter/managers/messageManager.ts`**
-
-```typescript
-import {
- IMessageManager,
- MESSAGE_METADATA,
- MESSAGE_ROLE,
- MESSAGE_STATUS,
- ISQLitePresenter,
- SQLITE_MESSAGE
-} from '@shared/presenter'
-import {
- Message,
- AssistantMessageBlock,
- UserMessageContent,
- UserMessageTextBlock,
- UserMessageMentionBlock,
- UserMessageCodeBlock
-} from '@shared/chat'
-import { eventBus, SendTarget } from '@/eventbus'
-import { CONVERSATION_EVENTS } from '@/events'
-
-export class MessageManager implements IMessageManager {
- private sqlitePresenter: ISQLitePresenter
-
- constructor(sqlitePresenter: ISQLitePresenter) {
- this.sqlitePresenter = sqlitePresenter
- }
-
- // === 消息 CRUD ===
-
- async sendMessage(
- conversationId: string,
- content: string,
- role: MESSAGE_ROLE,
- parentId: string,
- isVariant: boolean,
- metadata: MESSAGE_METADATA,
- searchResults?: string
- ): Promise {
- const maxOrderSeq = await this.sqlitePresenter.getMaxOrderSeq(conversationId)
- const msgId = await this.sqlitePresenter.insertMessage(
- conversationId,
- content,
- role,
- parentId,
- JSON.stringify(metadata),
- isVariant,
- maxOrderSeq + 1,
- Date.now(),
- 'pending'
- )
-
- // 如果有搜索结果,保存为附件
- if (searchResults) {
- await this.sqlitePresenter.insertMessageAttachment(msgId, 'search_result', searchResults)
- }
-
- return this.getMessage(msgId)
- }
-
- async getMessage(messageId: string): Promise {
- const sqliteMessage = await this.sqlitePresenter.getMessage(messageId)
- if (!sqliteMessage) {
- throw new Error(`Message not found: ${messageId}`)
- }
- return this.convertToMessage(sqliteMessage)
- }
-
- async editMessage(messageId: string, content: string): Promise {
- await this.sqlitePresenter.updateMessage(messageId, { content })
- return this.getMessage(messageId)
- }
-
- async deleteMessage(messageId: string): Promise {
- await this.sqlitePresenter.deleteMessage(messageId)
- }
-
- async retryMessage(messageId: string, metadata: MESSAGE_METADATA): Promise {
- const originalMessage = await this.getMessage(messageId)
- if (originalMessage.role !== 'assistant') {
- throw new Error('Can only retry assistant messages')
- }
-
- if (!originalMessage.parentId) {
- throw new Error('Assistant message must have a parent')
- }
-
- const newMessage = await this.sendMessage(
- originalMessage.conversationId,
- JSON.stringify([]),
- 'assistant',
- originalMessage.parentId,
- false,
- metadata
- )
-
- return newMessage
- }
-
- async updateMessageStatus(messageId: string, status: MESSAGE_STATUS): Promise {
- await this.sqlitePresenter.updateMessage(messageId, { status })
- }
-
- async updateMessageMetadata(messageId: string, metadata: Partial): Promise {
- const existing = await this.getMessage(messageId)
- const currentMetadata: MESSAGE_METADATA = existing.usage
- const merged = { ...currentMetadata, ...metadata }
-
- await this.sqlitePresenter.updateMessage(messageId, {
- metadata: JSON.stringify(merged)
- })
- }
-
- async markMessageAsContextEdge(messageId: string, isEdge: boolean): Promise {
- await this.updateMessageMetadata(messageId, { contextEdge: isEdge ? 1 : 0 })
- }
-
- // === 消息查询 ===
-
- async getMessageThread(
- conversationId: string,
- page: number,
- pageSize: number
- ): Promise<{ total: number; list: Message[] }> {
- return this.sqlitePresenter.getMessageThread(conversationId, page, pageSize)
- }
-
- async getMessageHistory(messageId: string, limit: number = 100): Promise {
- const message = await this.getMessage(messageId)
- const { list } = await this.getMessageThread(message.conversationId, 1, limit)
-
- // 找到目标消息的位置
- const index = list.findIndex(msg => msg.id === messageId)
- if (index === -1) {
- return list
- }
-
- return list.slice(index + 1)
- }
-
- async getContextMessages(conversationId: string, messageCount: number): Promise {
- const { list } = await this.getMessageThread(conversationId, 1, messageCount)
- return list.slice(-messageCount)
- }
-
- async getMessageVariants(messageId: string): Promise {
- const message = await this.getMessage(messageId)
- return message.variants || []
- }
-
- async getMainMessageByParentId(
- conversationId: string,
- parentId: string
- ): Promise {
- const messages = await this.sqlitePresenter.getMessagesByParentId(conversationId, parentId)
- if (messages.length === 0) {
- return null
- }
-
- // 返回第一条(通常是按 order_seq 排序的)
- return this.convertToMessage(messages[0])
- }
-
-// ... (继续补充剩余方法)
-```
-
-#### 4.3 补充 SessionPresenter 缺失的方法
-
-**需要在 `sessionPresenter/index.ts` 中添加的方法:**
-
-```typescript
-// 从 StreamGenerationHandler 迁移
-async startStreamCompletion(
- sessionId: string,
- queryMsgId?: string,
- selectedVariantsMap?: Record
-): Promise {
- const streamingGenerator = new StreamingGenerator(this.dependencies)
- return streamingGenerator.startStream(sessionId, queryMsgId, selectedVariantsMap)
-}
-
-async continueStreamCompletion(
- sessionId: string,
- queryMsgId: string,
- selectedVariantsMap?: Record
-): Promise {
- const streamingGenerator = new StreamingGenerator(this.dependencies)
- return streamingGenerator.continueStream(sessionId, queryMsgId, selectedVariantsMap)
-}
-
-async regenerateFromUserMessage(
- sessionId: string,
- userMessageId: string,
- selectedVariantsMap?: Record
-): Promise {
- const userMessage = await this.messagePersister.getMessage(userMessageId)
- if (!userMessage || userMessage.role !== 'user') {
- throw new Error('Can only regenerate based on user messages.')
- }
-
- const conversation = await this.conversationPersister.getConversation(sessionId)
- const { providerId, modelId } = conversation.settings
-
- const assistantMessage = await this.messagePersister.insertMessage(
- sessionId,
- JSON.stringify([]),
- 'assistant',
- userMessageId,
- JSON.stringify({
- totalTokens: 0,
- generationTime: 0,
- firstTokenTime: 0,
- tokensPerSecond: 0,
- contextUsage: 0,
- inputTokens: 0,
- outputTokens: 0,
- model: modelId,
- provider: providerId
- }),
- 0,
- Date.now(),
- 'pending',
- 0,
- 0
- )
-
- // 启动流式生成
- await this.startStreamCompletion(sessionId, userMessageId, selectedVariantsMap)
-
- return (await this.getMessage(assistantMessage)) as AssistantMessage
-}
-
-// 从 PermissionHandler 迁移
-async handlePermissionResponse(
- messageId: string,
- toolCallId: string,
- granted: boolean,
- permissionType: 'read' | 'write' | 'all' | 'command',
- remember: boolean = true
-): Promise {
- const permissionCoordinator = new PermissionCoordinator(this.dependencies)
- return permissionCoordinator.handlePermissionResponse(
- messageId,
- toolCallId,
- granted,
- permissionType,
- remember
- )
-}
-
-// 从 MessageManager 迁移
-async editMessage(messageId: string, content: string): Promise {
- const messageManager = new MessageManager(this.sqlitePresenter)
- return messageManager.editMessage(messageId, content)
-}
-
-async retryMessage(messageId: string): Promise {
- const messageManager = new MessageManager(this.sqlitePresenter)
- const message = await messageManager.getMessage(messageId)
-
- const metadata: MESSAGE_METADATA = {
- contextUsage: 0,
- totalTokens: 0,
- generationTime: 0,
- firstTokenTime: 0,
- tokensPerSecond: 0,
- inputTokens: 0,
- outputTokens: 0,
- model: message.model_id,
- provider: message.model_provider
- }
-
- return messageManager.retryMessage(messageId, metadata)
-}
-
-async getMessageThread(
- sessionId: string,
- page: number,
- pageSize: number
-): Promise<{ total: number; messages: Message[] }> {
- const messageManager = new MessageManager(this.sqlitePresenter)
- const result = await messageManager.getMessageThread(sessionId, page, pageSize)
- return { total: result.total, messages: result.list }
-}
-
-async getContextMessages(sessionId: string): Promise {
- const conversation = await this.conversationPersister.getConversation(sessionId)
- let messageCount = Math.ceil(conversation.settings.contextLength / 300)
- if (messageCount < 2) {
- messageCount = 2
- }
- const messageManager = new MessageManager(this.sqlitePresenter)
- return messageManager.getContextMessages(sessionId, messageCount)
-}
-
-async clearContext(sessionId: string): Promise {
- await this.sqlitePresenter.deleteAllMessages(sessionId)
-}
-
-async clearAllMessages(sessionId: string): Promise {
- await this.sqlitePresenter.deleteAllMessages(sessionId)
-
- // 停止所有正在生成的消息
- const tabs = this.getActiveTabsBySession(sessionId)
- for (const tabId of tabs) {
- await this.stopConversationGeneration(sessionId)
- }
-}
-
-async stopConversationGeneration(sessionId: string): Promise {
- const messageIds = Array.from(this.activeLoops.entries())
- .filter(([, state]) => state.conversationId === sessionId)
- .map(([messageId]) => messageId)
-
- await Promise.all(messageIds.map((messageId) => this.cancelLoop(sessionId, messageId)))
-}
-```
-
-#### 4.4 创建流式生成协调器
-
-**`src/main/presenter/sessionPresenter/streaming/streamingGenerator.ts`** (新整合类)
-
-```typescript
-import type { SessionPresenterDependencies } from '../index'
-import type { ISearchPresenter } from '@/main/presenter/searchPresenter'
-import type { Message, AssistantMessage } from '@shared/chat'
-import type { SearchResult, CONVERSATION } from '@shared/presenter'
-import { LoopOrchestrator } from '../loop/loopOrchestrator'
-import { preparePromptContent } from '../message/messageBuilder'
-import { buildUserMessageContext, formatUserMessageContent } from '../message/messageFormatter'
-import { ContentEnricher } from '@/main/presenter/content/contentEnricher'
-
-export class StreamingGenerator {
- constructor(private deps: SessionPresenterDependencies) {}
-
- async startStream(
- sessionId: string,
- userMessageId?: string,
- selectedVariantsMap?: Record