Skip to content

Commit 2e420fa

Browse files
authored
feat: app settings skills (#1283)
* docs: add spec for app-settings * feat: implement chat-driven settings control with skill gating Add a safe, validated API for modifying DeepChat application settings via natural language. Settings changes are controlled by a dedicated 'deepchat-settings' skill to ensure tools are only available when contextually relevant. Key features: - Skill-gated tool injection: settings tools only appear when deepchat-settings skill is active - Safe settings apply API with Zod validation and strict allowlist - Support for toggles (sound, copy COT, chat mode) and enums (language, theme, font size) - Defense-in-depth: runtime skill verification before applying changes - Deep-linked settings navigation for unsupported/complex settings - Comprehensive test coverage for validation, mapping, and skill gating Changes: - Add ChatSettingsToolHandler with validated apply/open methods - Integrate with AgentToolManager for tool definition gating - Create deepchat-settings built-in skill with clear activation rules - Add shared types for requests/responses (chatSettings.ts) - Implement settings window navigation with SECTION_ALIASES - Add unit tests for handler and integration tests for tool gating - Translate spec documents (plan.md, spec.md, tasks.md) to Chinese - Fix type errors in getCurrentValue and OPEN_SECTION_VALUES * refactor: remove chatMode from settings control and add permission service Remove chatMode setting from the allowlist as it requires conversation-scoped updates that are better handled separately. Add permission checking for settings window opening to provide user control over settings navigation. Key changes: - Remove setChatMode tool and related schemas from ChatSettingsToolHandler - Add SettingsPermissionService for managing tool approvals (one-time and session) - Add permission check for deepchat_settings_open tool - Update PermissionHandler to handle settings permission grants - Add rememberable flag to permission request structure - Update AgentToolManager to consume approvals before opening settings - Add settingsPermissionService to main presenter index - Clear settings approvals when conversation ends - Update spec documents to reflect removed chatMode feature - Remove chatMode-related tests and types This ensures settings window opening requires explicit user approval and provides a cleaner separation of concerns for chat mode management. * docs: translate to en
1 parent c0de19e commit 2e420fa

24 files changed

Lines changed: 1377 additions & 14 deletions

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Plan: Control Settings via Chat
2+
3+
## Key Decision: Skill-Based Context Control
4+
5+
This feature MUST be described and delivered as a DeepChat skill so that additional instructions/context are only injected when the user actually requests to change DeepChat settings.
6+
7+
- Skill Name (suggested): `deepchat-settings`
8+
- Activation: Activated via `skill_control` **ONLY** when the user request involves DeepChat settings/preferences.
9+
- Deactivation: Call `skill_control` after completing the setting change to keep context lean.
10+
11+
## Tool Injection Control (No Skill, No Tools)
12+
13+
Configuration-related tools MUST NOT appear in the LLM tool list (and MUST NOT be mentioned in the system prompt) unless the `deepchat-settings` skill is active.
14+
15+
Implementation intent:
16+
17+
- Define dedicated tools (MCP-format function definitions):
18+
- `deepchat_settings_toggle`
19+
- `deepchat_settings_set_language`
20+
- `deepchat_settings_set_theme`
21+
- `deepchat_settings_set_font_size`
22+
- `deepchat_settings_open`
23+
- **DO NOT** expose them through MCP server/tool list UI (avoid being auto-enabled into `enabledMcpTools`).
24+
- Only inject these tool definitions when:
25+
- `deepchat-settings` is enabled for the current conversation, AND
26+
- The skill's pre-metadata `allowedTools` includes the tool name.
27+
28+
This requires conversation-scoped tool definition construction:
29+
30+
- Extend tool definition construction context to include `conversationId`.
31+
- Retrieve `skillsAllowedTools` for that conversation (via `SkillPresenter.getActiveSkillsAllowedTools`).
32+
- Only conditionally append `deepchat_settings_*` tool definitions when allowed.
33+
34+
## Step 1: Safe Settings Application API (Main Process)
35+
36+
### Entry Point
37+
38+
Implement a narrow, validated application surface in the main process (presenter method or agent tool handler) for:
39+
40+
- Accepting `unknown` input and validating it (Zod-style, similar to `AgentFileSystemHandler`).
41+
- Using an allowlist of setting IDs.
42+
- Applying changes by calling existing `ConfigPresenter` methods so existing event broadcasts remain correct.
43+
- Returning structured results to render confirmation/error messages.
44+
45+
### Allowlisted Settings and Mapping
46+
47+
Toggle settings:
48+
49+
- `soundEnabled` -> `ConfigPresenter.setSoundEnabled(boolean)` (broadcasts: `CONFIG_EVENTS.SOUND_ENABLED_CHANGED`)
50+
- `copyWithCotEnabled` -> `ConfigPresenter.setCopyWithCotEnabled(boolean)` (broadcasts: `CONFIG_EVENTS.COPY_WITH_COT_CHANGED`)
51+
52+
Enum settings:
53+
54+
- `language` -> `ConfigPresenter.setLanguage(locale)` (broadcasts: `CONFIG_EVENTS.LANGUAGE_CHANGED`)
55+
- `theme` -> `ConfigPresenter.setTheme('dark' | 'light' | 'system')` (broadcasts: `CONFIG_EVENTS.THEME_CHANGED`)
56+
- `fontSizeLevel` -> `ConfigPresenter.setSetting('fontSizeLevel', level)` (broadcasts `CONFIG_EVENTS.FONT_SIZE_CHANGED` via special case)
57+
58+
### Validation Rules
59+
60+
- Strict allowlist; reject unknown IDs.
61+
- No implicit type conversion in Step 1.
62+
- Validation per setting:
63+
- Booleans: must be boolean type
64+
- Enum values: must match allowed set
65+
- `fontSizeLevel`: must be integer within supported range (source of truth TBD; may align with `uiSettingsStore` constants)
66+
- `language`: must be one of supported locales (reuse support list from config)
67+
68+
### Defense in Depth: Require Skill Activity
69+
70+
Even with controlled tool injection, maintain runtime checks:
71+
72+
- If `deepchat-settings` is **NOT** enabled for the conversation, reject application and return error telling the model/user to activate it.
73+
- This ensures settings don't accidentally change due to unrelated agent behavior.
74+
75+
## Step 2: Skill Definition (Natural Language Behavior)
76+
77+
### Built-in Skill Artifact
78+
79+
Add `resources/skills/deepchat-settings/SKILL.md`:
80+
81+
- Pre-metadata `description` MUST explicitly state:
82+
- This is ONLY for changing DeepChat application settings.
83+
- Activate ONLY when user requests setting changes (settings/preferences/theme/language/font/sound/copy COT).
84+
- Do NOT activate for OS settings or programming/code settings.
85+
- Body MUST define:
86+
- Supported settings (allowlist) and canonical values.
87+
- How to ask clarifying questions when ambiguous.
88+
- When to refuse and instead open settings.
89+
- Always deactivate after completing setting tasks.
90+
91+
### Disallowed Settings -> Open Settings
92+
93+
For requests involving MCP configuration, prompts, providers, API keys, etc.:
94+
95+
- Do NOT apply via tools.
96+
- Provide precise instructions telling user where to change them.
97+
- Open settings window and navigate to relevant section if possible.
98+
99+
Implementation options for opening/navigating settings:
100+
101+
- Use `presenter.windowPresenter.createSettingsWindow()`.
102+
- Optionally `executeJavaScript` to set localStorage navigation hint that UI can read.
103+
- Or add dedicated IPC channel from main process -> settings renderer to navigate to tab/section.
104+
105+
## Data Model
106+
107+
Introduce shared request/response types (for Step 1 entry point + tools):
108+
109+
- `ChatSettingId` (union of allowlisted IDs)
110+
- `ApplyChatSettingRequest` (discriminated union `{ id, value }`)
111+
- `ApplyChatSettingResult`
112+
- `{ ok: true; id; value; previousValue?; appliedAt }`
113+
- `{ ok: false; errorCode; message; details? }`
114+
115+
## Testing Strategy
116+
117+
- Main process (Vitest):
118+
- Allowlist + validation (reject invalid values, no writes)
119+
- Each supported setting maps to correct `ConfigPresenter` method
120+
- Skill requirement enforcement works (tool rejects when skill inactive)
121+
- Renderer/UI (if any navigation hints added):
122+
- Settings page navigation handler tests (optional)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Control Settings via Chat
2+
3+
## Overview
4+
5+
Allow users to update a small, safe subset of DeepChat settings via natural language within conversations. Changes MUST be validated, persisted, and take effect immediately. For complex/high-risk settings (e.g., MCP configuration, prompts), the assistant MUST NOT apply changes directly; instead, it should explain where to edit them and automatically open settings (ideally deep-linked to the relevant section).
6+
7+
This specification is intentionally split into two increments:
8+
9+
- **Step 1**: Provide a safe, validated settings application API (main process) that can be called from controlled entry points (renderer UI and/or agent tools) to change settings and trigger live updates.
10+
- **Step 2**: Deliver natural language behavior as a **DeepChat skill** so that additional context is injected only when relevant.
11+
12+
## Goals
13+
14+
- Allow in-conversation updates to:
15+
- Toggle settings: Sound, Copy COT details.
16+
- Enum settings: Language, Theme, Font size.
17+
- Apply changes immediately (current window + relevant other windows).
18+
- Persist changes to existing configuration store.
19+
- Keep surface area safe: do not expose arbitrary configuration keys.
20+
- Use skills to control context:
21+
- Settings modification guidance MUST be injected ONLY when user actually requests to change DeepChat settings.
22+
23+
## Non-Goals
24+
25+
- Do NOT allow users to set arbitrary `ConfigPresenter.setSetting(key, value)` keys via chat.
26+
- Do NOT allow setting sensitive values via chat (API keys, tokens, environment variables, file paths, command arguments).
27+
- Do NOT implement editing of MCP servers, prompts, providers, or other complex nested config via natural language.
28+
- Do NOT change how settings are stored on disk (no migrations in this feature).
29+
30+
## User Stories
31+
32+
- As a user, I can say "turn on sound" and it enables sound immediately.
33+
- As a user, I can say "copy COT details when copying" and it enables/disables the toggle.
34+
- As a user, I can say "set language to English" and UI language switches immediately.
35+
- As a user, I can say "use dark theme" or "follow system theme" and theme updates immediately.
36+
- As a user, I can say "make text larger" and font size changes immediately.
37+
- As a user, if I ask "add MCP server" or "edit prompts", the assistant tells me where in settings and opens settings there.
38+
39+
## Acceptance Criteria
40+
41+
### Step 1: Safe Settings Application API (No NLP)
42+
43+
- A main process API exists that accepts restricted, validated requests to change one supported setting.
44+
- Only allowlisted settings from this specification can be changed via this API.
45+
- Setting tools are NOT injected into LLM tool list when `deepchat-settings` skill is **NOT** active.
46+
- On success:
47+
- Setting value is persisted (existing underlying storage).
48+
- Changes take effect immediately in current renderer.
49+
- Cross-window/tab updates happen where existing event flow supports (e.g., theme/language/font size/sound).
50+
- On failure:
51+
- Invalid inputs are rejected with structured, user-presentable errors (no partial writes).
52+
- API is safe to call with untrusted input (strict validation + allowlist).
53+
54+
### Step 2: Natural Language via Skill (Context Control)
55+
56+
- A built-in skill exists (suggested: `deepchat-settings`) describing this functionality.
57+
- This skill is NOT intended to remain active by default:
58+
- It should activate only when user requests to change DeepChat's own settings.
59+
- It should deactivate after setting change is complete.
60+
- When active, assistant:
61+
- Explains user intent, normalizes to canonical values, and calls Step 1 API.
62+
- For disallowed/complex settings (MCP, prompts, etc.), provides guidance and opens settings to best-match section.
63+
64+
## Open Questions [NEEDS CLARIFICATION]
65+
66+
1. Skill Mode Availability
67+
- Skill prompt injection currently seems tied to `chatMode === 'agent'`. Do we want this feature to work in:
68+
- Agent mode only (suggested first increment), OR
69+
- Also in chat/ACP agent modes (requires additional work)?
70+
2. Font Size Representation
71+
- Should chat use semantic labels (`small/medium/large`) mapping to `fontSizeLevel`, or accept explicit numeric levels?
72+
3. Settings Deep Link Targets
73+
- What are the canonical settings tab/section IDs we want to support deep-linking to (e.g., `mcp`, `prompts`, `appearance`, `language`)?
74+
4. UX: Confirm vs Silent Apply
75+
- Should assistant always confirm before applying changes, or apply immediately with "undo" capability?
76+
77+
## Security & Privacy Notes
78+
79+
- Step 1 API MUST:
80+
- Use an allowlist of setting IDs.
81+
- Validate input types and enum ranges.
82+
- Avoid any generic "set arbitrary key" functionality.
83+
- Defense in depth (recommended): Setting tools/entry points should verify the relevant skill is active for the conversation before applying.
84+
- Step 2 MUST NOT allow indirect privilege escalation:
85+
- MUST NOT change file system paths, command arguments, environment variables, or settings that hold secrets via natural language.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Tasks: Control Settings via Chat
2+
3+
## Step 0 - Skill-First Design (Context Control)
4+
5+
1. Draft built-in skill: `resources/skills/deepchat-settings/SKILL.md`.
6+
2. Ensure pre-metadata `description` explicitly restricts activation to only DeepChat setting changes.
7+
3. Ensure skill body lists allowlisted settings + safe handling + self-deactivation guidance.
8+
9+
## Step 1 - Safe Settings Application API (Main Process)
10+
11+
1. Add shared types for settings application request/result.
12+
2. Implement validated application entry point (Zod-style `unknown` parsing).
13+
3. Implement allowlist mapping to existing `ConfigPresenter` methods:
14+
- `soundEnabled`
15+
- `copyWithCotEnabled`
16+
- `language`
17+
- `theme`
18+
- `fontSizeLevel`
19+
4. Implement tool injection control: only include `deepchat_settings_toggle`/`deepchat_settings_set_language`/`deepchat_settings_set_theme`/`deepchat_settings_set_font_size`/`deepchat_settings_open` in tool definitions when `deepchat-settings` is active AND allowed.
20+
5. Add defense-in-depth control: reject application if `deepchat-settings`` skill is not active for conversation.
21+
6. Add "open settings" helper/tool for unsupported settings (MCP/prompts, etc.), including best-eff-mn navigation.
22+
7. Add main process tests:
23+
- Validation and mapping
24+
- Tool definitions only exist when skill active
25+
- Skill control enforces rejection when inactive
26+
27+
## Step 2 - UX Behavior (LLM + Skill)
28+
29+
1. Verify skill metadata prompt list clearly enough lists `deepchat-settings` for model to select it.
30+
2. Ensure skill instructs: activate only when user asks; deactivate after completion.
31+
3. Add examples of Chinese/English user phrasing in SKILL.md.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
name: deepchat-settings
3+
description: DeepChat app settings modification (DeepChat 设置/偏好) skill. Activate ONLY when the user explicitly asks to change DeepChat's own settings/preferences (e.g., theme, language, font size...). Do NOT activate for OS/system settings, editor settings, or other apps.
4+
allowedTools:
5+
- deepchat_settings_toggle
6+
- deepchat_settings_set_language
7+
- deepchat_settings_set_theme
8+
- deepchat_settings_set_font_size
9+
- deepchat_settings_open
10+
---
11+
12+
# DeepChat Settings Modification Skill
13+
14+
Use this skill to safely change DeepChat *application* settings during a conversation.
15+
16+
## Core rules
17+
18+
- Only change settings when the user is asking to change **DeepChat** settings.
19+
- Use the dedicated settings tools; never attempt arbitrary key/value writes.
20+
- These tools are intended to be available only when this skill is active; if they are missing, activate this skill via `skill_control`.
21+
- If the request is ambiguous, ask a clarifying question before applying.
22+
- For unsupported or high-risk settings (MCP, prompts, providers, API keys, paths): do **not** apply changes; instead explain where to change it and open Settings.
23+
- After completing the settings task, deactivate this skill via `skill_control` to keep context small.
24+
25+
## Supported settings (initial allowlist)
26+
27+
Toggles:
28+
29+
- `soundEnabled`: enable/disable sound effects.
30+
- `copyWithCotEnabled`: enable/disable copying COT details.
31+
32+
Enums:
33+
34+
- `language`: DeepChat locale, including `system`, `zh-CN`, `en-US`, `zh-TW`, `zh-HK`, `ko-KR`, `ru-RU`, `ja-JP`, `fr-FR`, `fa-IR`, `pt-BR`, `da-DK`, `he-IL`.
35+
- `theme`: `dark | light | system`.
36+
- `fontSizeLevel`: integer level within supported range.
37+
38+
Settings navigation (open-only):
39+
40+
- Use `deepchat_settings_open` only when the request cannot be fulfilled by the settings tools, and avoid calling it if the change is already applied.
41+
- `section` hints: `common`, `display`, `provider`, `mcp`, `prompt`, `acp`, `skills`, `knowledge-base`, `database`, `shortcut`, `about`.
42+
43+
## Workflow
44+
45+
1. Confirm the user is requesting a DeepChat settings change.
46+
2. Determine the target setting and the intended value.
47+
3. If the setting is supported, call the matching tool:
48+
- toggles: `deepchat_settings_toggle`
49+
- language: `deepchat_settings_set_language`
50+
- theme: `deepchat_settings_set_theme`
51+
- font size: `deepchat_settings_set_font_size`
52+
4. Confirm back to the user what changed (include the final value).
53+
5. If the setting is unsupported, call `deepchat_settings_open` (with `section`) and provide a short pointer to the correct Settings section. Do not call it if the requested change has already been applied.
54+
6. Deactivate this skill via `skill_control`.
55+
56+
## Examples (activate this skill)
57+
58+
- "把主题改成深色"
59+
- "Turn off sound effects"
60+
- "语言改成英文"
61+
- "复制时不要带 COT"
62+
- "Open the MCP settings page"
63+
- "Edit my prompts"
64+
65+
## Examples (do NOT activate this skill)
66+
67+
- "把 Windows 的系统代理改成..."
68+
- "帮我改 VS Code 的字体"
69+
- "把电脑的声音关掉"

src/main/events.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ export const WINDOW_EVENTS = {
106106
WINDOW_RESTORED: 'window:restored'
107107
}
108108

109+
// Settings related events
110+
export const SETTINGS_EVENTS = {
111+
NAVIGATE: 'settings:navigate'
112+
}
113+
109114
// ollama 相关事件
110115
export const OLLAMA_EVENTS = {
111116
PULL_MODEL_PROGRESS: 'ollama:pull-model-progress'

0 commit comments

Comments
 (0)