chatccc 0.2.261 → 0.2.263

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # ChatCCC
2
2
 
3
- **用飞书或微信聊天控制 Claude Code / Cursor / Codex / CCC Agent / DeepSeek Harness。**
3
+ **用飞书或微信聊天控制 Claude Code / Cursor / Codex / CCC Agent / DeepSeek Harness。**
4
4
 
5
- ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机上发消息,让 Claude Code、Cursor Agent、Codex、内置 CCC Agent 或 DeepSeek Harness 继续写代码、查问题、跑命令;不用一直守在电脑前。
5
+ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机上发消息,让 Claude Code、Cursor Agent、Codex、内置 CCC Agent 或 DeepSeek Harness 继续写代码、查问题、跑命令;不用一直守在电脑前。
6
6
 
7
7
  飞书是推荐入口:直接私聊机器人即可持续使用同一个专属会话,需要并行任务时再用 `/new` 创建独立会话群;卡片能流式更新,体验完整。微信 iLink 更适合快速试用或临时使用:扫码即可接入,但只能走私聊文本模式。
8
8
 
@@ -21,7 +21,7 @@ ChatCCC 把本地 AI 编程工具接入即时通讯软件。你可以在手机
21
21
  - **手机上也能用 AI 编程工具**:在飞书或微信发消息,就像在终端给 Agent 下指令。
22
22
  - **飞书体验更完整**:私聊可持续对话,`/new` 创建的一群一会话支持多任务并行,CardKit 卡片可流式更新。
23
23
  - **微信接入更轻**:不用创建飞书应用,启动后扫码即可在微信私聊里使用。
24
- - **多 Agent 切换**:`/new` 使用默认 Agent,也可以用 `/new claude`、`/new cursor`、`/new codex`、`/new ccc`、`/new dsh` 指定工具。
24
+ - **多 Agent 切换**:`/new` 使用默认 Agent,也可以用 `/new claude`、`/new cursor`、`/new codex`、`/new ccc`、`/new dsh` 指定工具。
25
25
  - **群里能跑 git**:`/git status`、`/git pull`、`/git log` 会在当前会话工作目录执行,并把输出发回聊天窗口。
26
26
 
27
27
  ## 飞书和微信的差异
@@ -216,7 +216,7 @@ chatccc
216
216
 
217
217
  ### 3. AI 工具配置
218
218
 
219
- Claude Code、Cursor 和 Codex 需要对应的本地工具;CCC Agent 内置于 ChatCCC,开箱即用,**模型接入不限于 DeepSeek**——它支持 OpenAI-compatible 和 Anthropic Messages 两种协议(详见下文 CCC Agent)。DeepSeek Harness(DSH)采用按需安装,不计入 ChatCCC 主包生产依赖。
219
+ Claude Code、Cursor 和 Codex 需要对应的本地工具;CCC Agent 内置于 ChatCCC,开箱即用,**模型接入不限于 DeepSeek**——它支持 OpenAI-compatible 和 Anthropic Messages 两种协议(详见下文 CCC Agent)。DeepSeek Harness(DSH)采用按需安装,不计入 ChatCCC 主包生产依赖。
220
220
 
221
221
  #### CCC Agent
222
222
 
@@ -242,15 +242,15 @@ ChatCCC 会把 `ccc.DEEPSEEK_API_KEY`、`ccc.DEEPSEEK_BASE_URL`、模型和 effo
242
242
 
243
243
  #### Claude Code
244
244
 
245
- ChatCCC 通过 Anthropic Claude Agent SDK 调用 Claude Code 能力。SDK 引擎(含 Claude Code CLI 原生二进制,约 220MB)**按需下载**:在首次配置向导或 Web 管理页打开 Claude Code 开关,或点击一次安装按钮,页面会连续显示环境检查、下载、校验、Runtime 验证、原子切换和清理进度,不需要逐步确认。**只有启用 Claude Code 的用户才需要下载**,不使用 Claude Code 的安装不包含该引擎,chatccc 主包体积保持精简。
245
+ ChatCCC 通过 Anthropic Claude Agent SDK 调用 Claude Code 能力。SDK 引擎(含 Claude Code CLI 原生二进制,约 220MB)**按需下载**:在首次配置向导或 Web 管理页打开 Claude Code 开关,或点击一次安装按钮,页面会连续显示环境检查、下载、校验、Runtime 验证、原子切换和清理进度,不需要逐步确认。**只有启用 Claude Code 的用户才需要下载**,不使用 Claude Code 的安装不包含该引擎,chatccc 主包体积保持精简。
246
246
 
247
- SDK 始终使用其内置的 Claude Code CLI(档位 b),不依赖用户自行安装的 `claude` 命令。本机完成 Claude Code 登录后即可使用(SDK 复用 `~/.claude` 登录态);同时支持官方和第三方 Anthropic 兼容 API:使用官方服务无需额外配置,使用第三方 API 则填写 `claude.apiKey` 和 `claude.baseUrl`。`claude.model`、`claude.subagentModel`、`claude.effort`、`claude.apiKey`、`claude.baseUrl` 均为选填;`claude.maxTurn` 控制每次对话的最大轮数(默认 0,即无限制)。填写后会把对应配置传给 Claude Agent SDK;留空则以 `~/.claude/settings.json` 为准。
248
-
249
- #### DeepSeek Harness
250
-
251
- DeepSeek Harness 是可选 Agent。在首次配置向导或 Web 管理页打开 DSH 开关,ChatCCC 会一次点击完成环境检查、临时目录准备、精确版本下载、包校验、Runtime 握手、原子切换和旧版本清理,并在页面逐步显示进度。安装失败时不会替换当前可用版本;刷新页面后仍可恢复安装进度。当前固定安装 `0.1.0-rc.6`,要求 Node.js >= 22.19.0。
252
-
253
- DSH 引擎安装在 `~/.chatccc/engines/dsh/`,不加入 ChatCCC 的 `dependencies`。配置 `dsh.apiKey`、`dsh.baseUrl`、`dsh.model`、`dsh.provider` 和 `dsh.maxTokens` 后,可设为默认 Agent 或使用 `/new dsh` 新建会话。安装验证只做本地 JSON-RPC Runtime 握手,不会调用付费模型。
247
+ SDK 始终使用其内置的 Claude Code CLI(档位 b),不依赖用户自行安装的 `claude` 命令。本机完成 Claude Code 登录后即可使用(SDK 复用 `~/.claude` 登录态);同时支持官方和第三方 Anthropic 兼容 API:使用官方服务无需额外配置,使用第三方 API 则填写 `claude.apiKey` 和 `claude.baseUrl`。`claude.model`、`claude.subagentModel`、`claude.effort`、`claude.apiKey`、`claude.baseUrl` 均为选填;`claude.maxTurn` 控制每次对话的最大轮数(默认 0,即无限制)。填写后会把对应配置传给 Claude Agent SDK;留空则以 `~/.claude/settings.json` 为准。
248
+
249
+ #### DeepSeek Harness
250
+
251
+ DeepSeek Harness 是可选 Agent。在首次配置向导或 Web 管理页打开 DSH 开关,ChatCCC 会一次点击完成环境检查、临时目录准备、精确版本下载、包校验、Runtime 握手、原子切换和旧版本清理,并在页面逐步显示进度。安装失败时不会替换当前可用版本;刷新页面后仍可恢复安装进度。当前固定安装 `0.1.0-rc.6`,要求 Node.js >= 22.19.0。
252
+
253
+ DSH 引擎安装在 `~/.chatccc/engines/dsh/`,不加入 ChatCCC 的 `dependencies`。配置 `dsh.apiKey`、`dsh.baseUrl`、`dsh.model`、`dsh.provider` 和 `dsh.maxTokens` 后,可设为默认 Agent 或使用 `/new dsh` 新建会话。安装验证只做本地 JSON-RPC Runtime 握手,不会调用付费模型。
254
254
 
255
255
  #### Cursor Agent CLI
256
256
 
@@ -375,20 +375,21 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
375
375
  | `claude.model` / `claude.subagentModel` / `claude.effort` | 选填;设置后传给 Claude Agent SDK,留空以 `~/.claude/settings.json` 为准 |
376
376
  | `claude.apiKey` / `claude.baseUrl` | 选填;设置后传给 Claude Agent SDK,留空以 `~/.claude/settings.json` 为准 |
377
377
  | `claude.maxTurn` | 选填;Claude 最大对话轮数,默认 0(无限制),可在 Web UI 编辑 |
378
- | `cursor.alternativeModel` / `codex.alternativeModel` / `ccc.alternativeModel` | 单个备选模型;加入 `/model` 人工切换列表,不会自动故障转移 |
378
+ | `cursor.alternativeModel` / `codex.alternativeModel` / `ccc.alternativeModel` / `dsh.alternativeModel` | 单个备选模型;加入 `/model` 人工切换列表,不会自动故障转移 |
379
379
  | `ccc.DEEPSEEK_API_KEY` / `ccc.DEEPSEEK_BASE_URL` | CCC Agent 的 API Key 和服务地址;**不限于 DeepSeek**——可填任意 OpenAI 兼容端点(OpenAI、Kimi、通义、智谱、Ollama 本地等) |
380
380
  | `ccc.model` | CCC Agent 默认模型 |
381
381
  | `ccc.subModel` | CCC Agent 子模型(选填):用于内部轻量环节(压缩摘要生成、task 子代理任务);留空跟随主模型 |
382
382
  | `ccc.compactionTimeoutMs` | CCC Agent 上下文压缩单轮超时(毫秒),默认 300000(5 分钟);压缩超时会让整轮对话失败,建议保持默认或调大 |
383
- | `ccc.contextWindow` | CCC Agent 模型上下文窗口(token),默认 1048576(1M,DeepSeek V4 Pro/Flash 原生规格);压缩阈值自动 = 窗口 × 80%;超过模型/服务端实际上限会被 API 拒绝,可在 Web UI 下拉选择或自定义(单位 k) |
384
- | `dsh.apiKey` / `dsh.baseUrl` | DeepSeek Harness 的 API Key 和官方 DeepSeek 服务地址 |
385
- | `dsh.model` / `dsh.provider` / `dsh.maxTokens` | DSH 默认模型、Runtime provider 和最大 token 数 |
383
+ | `ccc.contextWindow` | CCC Agent 模型上下文窗口(token),默认 1048576(1M,DeepSeek V4 Pro/Flash 原生规格);压缩阈值自动 = 窗口 × 80%;超过模型/服务端实际上限会被 API 拒绝,可在 Web UI 下拉选择或自定义(单位 k) |
384
+ | `dsh.apiKey` / `dsh.baseUrl` | DeepSeek Harness 的 API Key 和官方 DeepSeek 服务地址 |
385
+ | `dsh.model` / `dsh.provider` / `dsh.maxTokens` | DSH 默认模型、Runtime provider 和最大 token 数 |
386
+ | `dsh.subModel` | DSH 子代理模型(选填):用于 `subagent` 工具派生的子代理;留空跟随主模型(与 `ccc.subModel` 语义一致) |
386
387
 
387
388
  > **权限控制**:普通消息以 `bypassPermissions` 模式运行,跳过 Agent 操作确认。使用 `/plan` 或 `/ask` 前缀时,ChatCCC 自动切换为只读模式:Claude SDK 仅放行 Read + stop-stuck-loop 网络请求,Codex 使用 `--sandbox read-only`,Cursor 使用 `--mode plan/ask`。请只在可信环境中使用。
388
389
 
389
390
  ### 5. 开始使用
390
391
 
391
- **飞书:** 找到机器人后直接发送普通消息,即可在当前私聊中创建并持续使用专属 AI 会话;私聊工作目录固定为运行 ChatCCC 的系统账号用户目录。默认 Agent 发生变化后,下一条私聊普通消息会触发切换并创建新的空会话;若旧 Agent 正在生成,该消息会先排队,待当前回复完成后再切换。命令不会触发自动切换。需要独立任务时,发送 `/new`、`/new claude`、`/new cursor`、`/new codex`、`/new ccc` 或 `/new dsh`,机器人会另外创建会话群。
392
+ **飞书:** 找到机器人后直接发送普通消息,即可在当前私聊中创建并持续使用专属 AI 会话;私聊工作目录固定为运行 ChatCCC 的系统账号用户目录。默认 Agent 发生变化后,下一条私聊普通消息会触发切换并创建新的空会话;若旧 Agent 正在生成,该消息会先排队,待当前回复完成后再切换。命令不会触发自动切换。需要独立任务时,发送 `/new`、`/new claude`、`/new cursor`、`/new codex`、`/new ccc` 或 `/new dsh`,机器人会另外创建会话群。
392
393
 
393
394
  **微信:** 扫码登录后,在机器人私聊里发送 `/new` 或指定 Agent 的 `/new ...` 命令即可开始。功能与飞书基本一致,但展示为纯文本。
394
395
 
@@ -404,8 +405,8 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
404
405
  | `/new claude` | 创建 Claude Code 会话;飞书中会创建新群 |
405
406
  | `/new cursor` | 创建 Cursor 会话;飞书中会创建新群 |
406
407
  | `/new codex` | 创建 Codex 会话;飞书中会创建新群 |
407
- | `/new ccc` | 创建内置 CCC Agent 会话;飞书中会创建新群 |
408
- | `/new dsh` | 创建 DeepSeek Harness 会话;飞书中会创建新群 |
408
+ | `/new ccc` | 创建内置 CCC Agent 会话;飞书中会创建新群 |
409
+ | `/new dsh` | 创建 DeepSeek Harness 会话;飞书中会创建新群 |
409
410
  | `/forget` | 在当前聊天原地重置会话(忘掉上下文);群聊保留工作目录,飞书私聊固定使用系统用户目录 |
410
411
  | `/model` | 查看或切换当前会话的模型 |
411
412
  | `/fast` | 查看当前 Codex 会话的 Fast 模式;使用 `/fast on` 或 `/fast off` 切换 |
@@ -415,7 +416,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
415
416
  | `/cd` | 查看或设置后续新建会话的默认工作目录,不改变当前会话;飞书私聊自身始终使用系统用户目录 |
416
417
  | `/sessions` | 查看所有会话状态 |
417
418
  | `/session <数字>` | 将当前群聊切换到 `/sessions` 列表中的指定会话;飞书私聊不支持切换 |
418
- | `/usage` | 查看当前会话对应 Agent 的用量;Codex 显示 5h/7天窗口,Cursor 显示当前周期用量,CCC Agent 和 DSH 仅在官方 DeepSeek 端点时显示账户余额(其他兼容端点自动跳过) |
419
+ | `/usage` | 查看当前会话对应 Agent 的用量;Codex 显示 5h/7天窗口,Cursor 显示当前周期用量,CCC Agent 和 DSH 仅在官方 DeepSeek 端点时显示账户余额(其他兼容端点自动跳过) |
419
420
  | `/git <子命令>` | 在当前会话工作目录执行 `git ...` 并回传输出 |
420
421
  | `/abd<内容>` | 去掉 `/abd` 前缀后把内容发给 Agent,并在消息末尾追加第一性原理需求澄清提示 |
421
422
  | `/plan <内容>` | 只读计划模式:仅允许读文件和 stop-stuck-loop 请求,不执行任何写操作 |
@@ -426,7 +427,7 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
426
427
 
427
428
  `/update` 会在执行 npm 更新前把飞书消息或按钮事件 ID 原子写入 `~/.chatccc/state/update-command-guard.json`。同一 ID 跨重启重投时会静默忽略;用户主动发送的新 `/update` 因事件 ID 不同,仍可立即执行。该保护仅作用于 `/update`,普通消息与 `/restart` 的处理不变。
428
429
 
429
- > **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`;Cursor、Codex 和 CCC Agent 使用各自的 `model` / `alternativeModel`。
430
+ > **模型切换**:`/model` 查看当前会话 Agent 的可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。可选模型来自当前 Agent 的配置:Claude 使用 `claude.model` / `claude.subagentModel`;Cursor、Codex、CCC Agent DSH 使用各自的 `model` / `alternativeModel`。
430
431
 
431
432
  > **Codex Fast 模式**:Web UI 中的“Fast 模式”设置新 Codex 会话的全局默认值,默认关闭。进入 Codex 会话后,`/fast` 查询当前状态,`/fast on` 和 `/fast off` 只覆盖当前会话并从下一条消息生效。ChatCCC 会显式向 Codex CLI 传入 `service_tier="fast"` 或 `service_tier="default"`,因此关闭时不会继承用户 `config.toml` 中可能开启的 Fast。
432
433
 
@@ -434,4 +435,4 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
434
435
 
435
436
  ## 技术栈
436
437
 
437
- TypeScript(发布前编译为 JavaScript,tsx 仅用于开发)/ Node.js >= 20 / AI SDK / Anthropic Claude Agent SDK(按需下载)/ DeepSeek Harness SDK + JSON-RPC Runtime(按需下载,要求 Node.js >= 22.19.0)/ Cursor Agent CLI / Codex CLI / 飞书 WebSocket API / CardKit / 微信 iLink
438
+ TypeScript(发布前编译为 JavaScript,tsx 仅用于开发)/ Node.js >= 20 / AI SDK / Anthropic Claude Agent SDK(按需下载)/ DeepSeek Harness SDK + JSON-RPC Runtime(按需下载,要求 Node.js >= 22.19.0)/ Cursor Agent CLI / Codex CLI / 飞书 WebSocket API / CardKit / 微信 iLink
@@ -37,20 +37,20 @@
37
37
  "retentionDays": 7,
38
38
  "keepCompleted": false
39
39
  },
40
- "ccc": {
41
- "enabled": false,
42
- "maxBytesPerTurn": 52428800,
43
- "retentionDays": 7,
44
- "keepCompleted": false
45
- },
46
- "dsh": {
47
- "enabled": false,
48
- "maxBytesPerTurn": 52428800,
49
- "retentionDays": 7,
50
- "keepCompleted": false
40
+ "ccc": {
41
+ "enabled": false,
42
+ "maxBytesPerTurn": 52428800,
43
+ "retentionDays": 7,
44
+ "keepCompleted": false
45
+ },
46
+ "dsh": {
47
+ "enabled": false,
48
+ "maxBytesPerTurn": 52428800,
49
+ "retentionDays": 7,
50
+ "keepCompleted": false
51
51
  }
52
52
  },
53
- "ccc": {
53
+ "ccc": {
54
54
  "enabled": false,
55
55
  "defaultAgent": false,
56
56
  "DEEPSEEK_API_KEY": "",
@@ -61,17 +61,19 @@
61
61
  "effort": "",
62
62
  "provider": "",
63
63
  "compactionTimeoutMs": 300000,
64
- "contextWindow": 1048576
65
- },
66
- "dsh": {
67
- "enabled": false,
68
- "defaultAgent": false,
69
- "apiKey": "",
70
- "baseUrl": "https://api.deepseek.com/v1",
71
- "model": "deepseek-v4-flash",
72
- "provider": "deepseek-official",
73
- "maxTokens": 49152
74
- },
64
+ "contextWindow": 1048576
65
+ },
66
+ "dsh": {
67
+ "enabled": false,
68
+ "defaultAgent": false,
69
+ "apiKey": "",
70
+ "baseUrl": "https://api.deepseek.com/v1",
71
+ "model": "deepseek-v4-flash",
72
+ "subModel": "",
73
+ "alternativeModel": "",
74
+ "provider": "deepseek-official",
75
+ "maxTokens": 49152
76
+ },
75
77
  "claude": {
76
78
  "enabled": false,
77
79
  "defaultAgent": true,
@@ -50,6 +50,10 @@ export function createDshAdapter(options = {}) {
50
50
  DSH_SESSION_ROOT: sessionRoot,
51
51
  ...(options.apiKey ? { DEEPSEEK_API_KEY: options.apiKey } : {}),
52
52
  ...(options.baseUrl ? { DEEPSEEK_BASE_URL: options.baseUrl } : {}),
53
+ // 子代理模型:subModel 留空时跟随主模型(与 CCC 的 ccc.subModel 语义一致)
54
+ DSH_SUBAGENT_PROVIDER: options.provider ?? "deepseek-official",
55
+ DSH_SUBAGENT_MODEL: options.subModel || options.model || "deepseek-v4-flash",
56
+ DSH_SUBAGENT_MAX_TOKENS: String(options.maxTokens ?? 49152),
53
57
  },
54
58
  },
55
59
  cwd,
@@ -60,6 +60,7 @@ export function getAllModelsForTool(tool, cfg = config) {
60
60
  }
61
61
  else if (tool === "dsh") {
62
62
  collect(cfg.dsh.model);
63
+ collect(cfg.dsh.alternativeModel);
63
64
  }
64
65
  return Array.from(seen).slice(0, 100);
65
66
  }
@@ -326,6 +327,8 @@ function loadConfig() {
326
327
  apiKey: "",
327
328
  baseUrl: DEFAULT_CCC_DEEPSEEK_BASE_URL,
328
329
  model: "deepseek-v4-flash",
330
+ subModel: "",
331
+ alternativeModel: "",
329
332
  provider: "deepseek-official",
330
333
  maxTokens: 49152,
331
334
  },
@@ -533,6 +536,8 @@ function loadConfig() {
533
536
  apiKey: normalizeOptionalConfigField(dshRaw.apiKey, { label: "dsh.apiKey" }),
534
537
  baseUrl: normalizeOptionalConfigField(dshRaw.baseUrl, { label: "dsh.baseUrl", fallback: DEFAULT_CCC_DEEPSEEK_BASE_URL }),
535
538
  model: normalizeOptionalConfigField(dshRaw.model, { label: "dsh.model", fallback: "deepseek-v4-flash" }),
539
+ subModel: normalizeOptionalConfigField(dshRaw.subModel, { label: "dsh.subModel" }),
540
+ alternativeModel: normalizeOptionalConfigField(dshRaw.alternativeModel, { label: "dsh.alternativeModel" }),
536
541
  provider: normalizeOptionalConfigField(dshRaw.provider, { label: "dsh.provider", fallback: "deepseek-official" }),
537
542
  maxTokens: normalizePositiveInteger(dshRaw.maxTokens, 49152),
538
543
  },
@@ -83,6 +83,10 @@ export const DSH_RUNTIME_CONFIG = `# Generated and owned by ChatCCC. Secrets are
83
83
  provider: spawn
84
84
  toolName: subagent
85
85
  enableRunInBackground: false
86
+ agentOptions:
87
+ provider: !!js process.env.DSH_SUBAGENT_PROVIDER ?? 'deepseek-official'
88
+ model: !!js process.env.DSH_SUBAGENT_MODEL ?? 'deepseek-v4-flash'
89
+ maxTokens: !!js Number(process.env.DSH_SUBAGENT_MAX_TOKENS ?? '49152')
86
90
  - id: tool-todo
87
91
  name: '@deepseek-ai/dsh-tool-todo'
88
92
  config:
@@ -240,6 +240,7 @@ const AVATAR_BADGES = {
240
240
  claude: resolvePath(AVATAR_BADGE_DIR, "badge_claude.png"),
241
241
  cursor: resolvePath(AVATAR_BADGE_DIR, "badge_cursor.png"),
242
242
  codex: resolvePath(AVATAR_BADGE_DIR, "badge_codex.png"),
243
+ dsh: resolvePath(AVATAR_BADGE_DIR, "badge_dsh.png"),
243
244
  };
244
245
  const AVATAR_SIZE = 256;
245
246
  const AVATAR_BADGE_SIZE = 92;
@@ -544,6 +544,7 @@ export function getAdapterForTool(tool, sessionId) {
544
544
  apiKey: config.dsh.apiKey,
545
545
  baseUrl: config.dsh.baseUrl,
546
546
  model: effectiveModel || config.dsh.model,
547
+ ...(config.dsh.subModel ? { subModel: config.dsh.subModel } : {}),
547
548
  provider: config.dsh.provider,
548
549
  maxTokens: config.dsh.maxTokens,
549
550
  });
@@ -523,6 +523,14 @@ export function unflattenConfig(flat) {
523
523
  result.dsh = result.dsh || {};
524
524
  result.dsh.model = val;
525
525
  }
526
+ else if (key === "CHATCCC_DSH_SUB_MODEL") {
527
+ result.dsh = result.dsh || {};
528
+ result.dsh.subModel = val;
529
+ }
530
+ else if (key === "CHATCCC_DSH_ALTERNATIVE_MODEL") {
531
+ result.dsh = result.dsh || {};
532
+ result.dsh.alternativeModel = val;
533
+ }
526
534
  else if (key === "CHATCCC_DSH_PROVIDER") {
527
535
  result.dsh = result.dsh || {};
528
536
  result.dsh.provider = val;
@@ -697,12 +705,12 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
697
705
  .btn-danger:hover{background:#dc2626}
698
706
  .btn-success{background:#16a34a;color:#fff}
699
707
  .btn-success:hover{background:#15803d}
700
- .btn:disabled{opacity:.5;cursor:not-allowed}
701
- .engine-status{font-size:13px;color:#64748b;margin-bottom:8px}
702
- .engine-steps{display:grid;gap:5px;margin:8px 0 10px}
703
- .engine-step{display:grid;grid-template-columns:18px 1fr auto;gap:7px;align-items:center;padding:5px 7px;border-radius:7px;background:#f8fafc;font-size:12px;color:#64748b}
704
- .engine-step.running{background:#eff6ff;color:#1d4ed8}.engine-step.completed{color:#15803d}.engine-step.failed{background:#fef2f2;color:#dc2626}
705
- .engine-step-icon{font-weight:700;text-align:center}.engine-step-percent{font-variant-numeric:tabular-nums;color:inherit}
708
+ .btn:disabled{opacity:.5;cursor:not-allowed}
709
+ .engine-status{font-size:13px;color:#64748b;margin-bottom:8px}
710
+ .engine-steps{display:grid;gap:5px;margin:8px 0 10px}
711
+ .engine-step{display:grid;grid-template-columns:18px 1fr auto;gap:7px;align-items:center;padding:5px 7px;border-radius:7px;background:#f8fafc;font-size:12px;color:#64748b}
712
+ .engine-step.running{background:#eff6ff;color:#1d4ed8}.engine-step.completed{color:#15803d}.engine-step.failed{background:#fef2f2;color:#dc2626}
713
+ .engine-step-icon{font-weight:700;text-align:center}.engine-step-percent{font-variant-numeric:tabular-nums;color:inherit}
706
714
  .btn-group{display:flex;gap:8px;flex-wrap:wrap}
707
715
  .agent-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px}
708
716
  .agent-card{min-width:0}
@@ -933,41 +941,43 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
933
941
  <div class="hint">压缩阈值自动 = 窗口 × 80%(超出即把较早消息压缩为摘要)。⚠️ 超过模型/服务端实际上限时请求会被 API 直接拒绝(context length exceeded);实际窗口以模型与所用服务端为准(如 litellm 代理的 max_input_tokens)。</div>
934
942
  </div>
935
943
  </fieldset>
936
- </div>
937
-
938
- <!-- DeepSeek Harness 卡片 -->
939
- <div class="agent-card" id="agent-card-dsh">
940
- <div class="agent-card-header">
941
- <input type="checkbox" class="agent-toggle" id="agent-enable-dsh" onchange="onInstallableAgentToggle('dsh', this)">
942
- <div class="meta">
943
- <div class="name">DeepSeek Harness</div>
944
- <div class="desc">DeepSeek 官方 Agent Harness<br>按需安装,独立于 CCC Agent</div>
945
- </div>
946
- </div>
947
- <label class="agent-default-row">
948
- <input type="checkbox" id="agent-default-dsh" onchange="onDefaultAgentToggle('dsh', this.checked)">
949
- 设为默认 Agent
950
- </label>
951
- <fieldset class="agent-body" id="agent-body-dsh" disabled>
952
- <div class="form-group"><label>API Key</label><input type="password" id="field-CHATCCC_DSH_API_KEY" placeholder="DeepSeek API Key"></div>
953
- <div class="form-group"><label>Base URL</label><input type="text" id="field-CHATCCC_DSH_BASE_URL" placeholder="https://api.deepseek.com/v1"></div>
954
- <div class="form-group"><label>模型</label><input type="text" id="field-CHATCCC_DSH_MODEL" placeholder="deepseek-v4-flash"></div>
955
- <div class="form-group"><label>Provider 路由</label><input type="text" id="field-CHATCCC_DSH_PROVIDER" placeholder="deepseek-official"></div>
956
- <div class="form-group"><label>单次最大输出 Tokens</label><input type="number" id="field-CHATCCC_DSH_MAX_TOKENS" min="1" placeholder="49152"></div>
957
- <div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
958
- <label>DeepSeek Harness 引擎</label>
959
- <div id="dsh-engine-status" class="engine-status">检测中...</div>
960
- <div id="dsh-engine-steps" class="engine-steps"></div>
961
- <button type="button" class="btn btn-outline" id="dsh-engine-install-btn" onclick="installEngine('dsh')">安装并启用</button>
962
- <div class="hint" style="margin-top:6px">一次点击自动完成环境检查、下载、校验、Runtime 握手和原子切换;页面刷新后仍可恢复进度。</div>
963
- </div>
964
- </fieldset>
965
- </div>
966
-
967
- <!-- Claude 卡片 -->
944
+ </div>
945
+
946
+ <!-- DeepSeek Harness 卡片 -->
947
+ <div class="agent-card" id="agent-card-dsh">
948
+ <div class="agent-card-header">
949
+ <input type="checkbox" class="agent-toggle" id="agent-enable-dsh" onchange="onInstallableAgentToggle('dsh', this)">
950
+ <div class="meta">
951
+ <div class="name">DeepSeek Harness</div>
952
+ <div class="desc">DeepSeek 官方 Agent Harness<br>按需安装,独立于 CCC Agent</div>
953
+ </div>
954
+ </div>
955
+ <label class="agent-default-row">
956
+ <input type="checkbox" id="agent-default-dsh" onchange="onDefaultAgentToggle('dsh', this.checked)">
957
+ 设为默认 Agent
958
+ </label>
959
+ <fieldset class="agent-body" id="agent-body-dsh" disabled>
960
+ <div class="form-group"><label>API Key</label><input type="password" id="field-CHATCCC_DSH_API_KEY" placeholder="DeepSeek API Key"></div>
961
+ <div class="form-group"><label>Base URL</label><input type="text" id="field-CHATCCC_DSH_BASE_URL" placeholder="https://api.deepseek.com/v1"></div>
962
+ <div class="form-group"><label>模型</label><input type="text" id="field-CHATCCC_DSH_MODEL" placeholder="deepseek-v4-flash"></div>
963
+ <div class="form-group"><label>子模型(选填)</label><input type="text" id="field-CHATCCC_DSH_SUB_MODEL" placeholder="留空跟随主模型;用于 subagent 子代理任务"></div>
964
+ <div class="form-group"><label>备选模型(选填)</label><input type="text" id="field-CHATCCC_DSH_ALTERNATIVE_MODEL" placeholder="加入 /model 列表,便于会话内切换"></div>
965
+ <div class="form-group"><label>Provider 路由</label><input type="text" id="field-CHATCCC_DSH_PROVIDER" placeholder="deepseek-official"></div>
966
+ <div class="form-group"><label>单次最大输出 Tokens</label><input type="number" id="field-CHATCCC_DSH_MAX_TOKENS" min="1" placeholder="49152"></div>
967
+ <div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
968
+ <label>DeepSeek Harness 引擎</label>
969
+ <div id="dsh-engine-status" class="engine-status">检测中...</div>
970
+ <div id="dsh-engine-steps" class="engine-steps"></div>
971
+ <button type="button" class="btn btn-outline" id="dsh-engine-install-btn" onclick="installEngine('dsh')">安装并启用</button>
972
+ <div class="hint" style="margin-top:6px">一次点击自动完成环境检查、下载、校验、Runtime 握手和原子切换;页面刷新后仍可恢复进度。</div>
973
+ </div>
974
+ </fieldset>
975
+ </div>
976
+
977
+ <!-- Claude 卡片 -->
968
978
  <div class="agent-card" id="agent-card-claude">
969
979
  <div class="agent-card-header">
970
- <input type="checkbox" class="agent-toggle" id="agent-enable-claude" onchange="onInstallableAgentToggle('claude', this)">
980
+ <input type="checkbox" class="agent-toggle" id="agent-enable-claude" onchange="onInstallableAgentToggle('claude', this)">
971
981
  <div class="meta">
972
982
  <div class="name">Claude Code</div>
973
983
  <div class="desc">Anthropic Claude Code CLI<br>模型、effort 均为选填</div>
@@ -1000,9 +1010,9 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
1000
1010
  </div>
1001
1011
  <div class="form-group" style="border-top:1px solid #e2e8f0;padding-top:12px;margin-top:4px">
1002
1012
  <label>Claude Code 引擎(Agent SDK)</label>
1003
- <div id="claude-engine-status" class="engine-status">检测中...</div>
1004
- <div id="claude-engine-steps" class="engine-steps"></div>
1005
- <button type="button" class="btn btn-outline" id="claude-engine-install-btn" onclick="installEngine('claude')">安装并启用</button>
1013
+ <div id="claude-engine-status" class="engine-status">检测中...</div>
1014
+ <div id="claude-engine-steps" class="engine-steps"></div>
1015
+ <button type="button" class="btn btn-outline" id="claude-engine-install-btn" onclick="installEngine('claude')">安装并启用</button>
1006
1016
  <div class="hint" style="margin-top:6px">ChatCCC 通过 Claude Agent SDK 调用 Claude Code;SDK 引擎按需下载到本机(仅启用 Claude Code 时需要),安装期间请保持网络畅通。</div>
1007
1017
  </div>
1008
1018
  </fieldset>
@@ -1180,7 +1190,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
1180
1190
  </div>
1181
1191
  </details>
1182
1192
 
1183
- <details class="card config-section" id="dash-claude">
1193
+ <details class="card config-section" id="dash-claude">
1184
1194
  <summary>Claude Agent</summary>
1185
1195
  <div class="section-detail">
1186
1196
  <div class="config-row"><span class="key">模型</span><span class="val" id="cfg-ANTHROPIC_MODEL">-</span></div>
@@ -1193,24 +1203,26 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
1193
1203
  <div class="hint" style="margin-top:6px;line-height:1.6">生效范围:保存后下一条消息或下个新会话生效,当前生成不中断。</div>
1194
1204
  <button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
1195
1205
  </div>
1196
- </details>
1197
-
1198
- <details class="card config-section" id="dash-dsh">
1199
- <summary>DeepSeek Harness</summary>
1200
- <div class="section-detail">
1201
- <div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-DSH_API_KEY">-</span></div>
1202
- <div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-DSH_BASE_URL">-</span></div>
1203
- <div class="config-row"><span class="key">模型</span><span class="val" id="cfg-DSH_MODEL">-</span></div>
1204
- <div class="config-row"><span class="key">Provider</span><span class="val" id="cfg-DSH_PROVIDER">-</span></div>
1205
- <div class="config-row"><span class="key">最大输出 Tokens</span><span class="val" id="cfg-DSH_MAX_TOKENS">-</span></div>
1206
- <div id="dsh-dashboard-engine-status" class="engine-status">检测中...</div>
1207
- <div id="dsh-dashboard-engine-steps" class="engine-steps"></div>
1208
- <button type="button" class="btn btn-outline" id="dsh-dashboard-engine-install-btn" onclick="installEngine('dsh')">安装或升级引擎</button>
1209
- <label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-dsh" onchange="setDashboardDefaultAgent('dsh', this.checked)"> 设为默认 Agent</label>
1210
- <div class="hint" style="margin-top:6px;line-height:1.6">安装过程不调用付费模型;保存后下一条消息或下个新会话生效。</div>
1211
- <button class="btn btn-outline" style="margin-top:8px" onclick="editSection('dsh')">编辑</button>
1212
- </div>
1213
- </details>
1206
+ </details>
1207
+
1208
+ <details class="card config-section" id="dash-dsh">
1209
+ <summary>DeepSeek Harness</summary>
1210
+ <div class="section-detail">
1211
+ <div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-DSH_API_KEY">-</span></div>
1212
+ <div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-DSH_BASE_URL">-</span></div>
1213
+ <div class="config-row"><span class="key">模型</span><span class="val" id="cfg-DSH_MODEL">-</span></div>
1214
+ <div class="config-row"><span class="key">子模型</span><span class="val" id="cfg-DSH_SUB_MODEL">-</span></div>
1215
+ <div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-DSH_ALTERNATIVE_MODEL">-</span></div>
1216
+ <div class="config-row"><span class="key">Provider</span><span class="val" id="cfg-DSH_PROVIDER">-</span></div>
1217
+ <div class="config-row"><span class="key">最大输出 Tokens</span><span class="val" id="cfg-DSH_MAX_TOKENS">-</span></div>
1218
+ <div id="dsh-dashboard-engine-status" class="engine-status">检测中...</div>
1219
+ <div id="dsh-dashboard-engine-steps" class="engine-steps"></div>
1220
+ <button type="button" class="btn btn-outline" id="dsh-dashboard-engine-install-btn" onclick="installEngine('dsh')">安装或升级引擎</button>
1221
+ <label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-dsh" onchange="setDashboardDefaultAgent('dsh', this.checked)"> 设为默认 Agent</label>
1222
+ <div class="hint" style="margin-top:6px;line-height:1.6">安装过程不调用付费模型;保存后下一条消息或下个新会话生效。</div>
1223
+ <button class="btn btn-outline" style="margin-top:8px" onclick="editSection('dsh')">编辑</button>
1224
+ </div>
1225
+ </details>
1214
1226
 
1215
1227
  <details class="card config-section" id="dash-cursor">
1216
1228
  <summary>Cursor Agent</summary>
@@ -1281,8 +1293,8 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
1281
1293
  <script>
1282
1294
  let state = {
1283
1295
  view: 'loading',
1284
- // 五个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
1285
- agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false, dsh: false },
1296
+ // 五个 Agent 各自的启用开关;初始全 false,renderStep2() 会按已存在 config 自动打开
1297
+ agentsEnabled: { claude: false, cursor: false, codex: false, ccc: false, dsh: false },
1286
1298
  defaultAgent: 'claude',
1287
1299
  wizardStep: 1,
1288
1300
  config: {},
@@ -1299,8 +1311,8 @@ const AGENT_FIELDS = {
1299
1311
  claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL','CHATCCC_ANTHROPIC_MAX_TURN'],
1300
1312
  cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL','CHATCCC_CURSOR_ALTERNATIVE_MODEL','CHATCCC_CURSOR_AVATAR_BATTERY_MODE','CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'],
1301
1313
  codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE'],
1302
- ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_SUB_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL','CHATCCC_CCC_EFFORT','CHATCCC_CCC_PROVIDER','CHATCCC_CCC_CONTEXT_WINDOW'],
1303
- dsh: ['CHATCCC_DSH_API_KEY','CHATCCC_DSH_BASE_URL','CHATCCC_DSH_MODEL','CHATCCC_DSH_PROVIDER','CHATCCC_DSH_MAX_TOKENS']
1314
+ ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_SUB_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL','CHATCCC_CCC_EFFORT','CHATCCC_CCC_PROVIDER','CHATCCC_CCC_CONTEXT_WINDOW'],
1315
+ dsh: ['CHATCCC_DSH_API_KEY','CHATCCC_DSH_BASE_URL','CHATCCC_DSH_MODEL','CHATCCC_DSH_SUB_MODEL','CHATCCC_DSH_ALTERNATIVE_MODEL','CHATCCC_DSH_PROVIDER','CHATCCC_DSH_MAX_TOKENS']
1304
1316
  };
1305
1317
  const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
1306
1318
  const WEB_UI_FIELDS = ['CHATCCC_WEB_UI_OPEN_ON_START'];
@@ -1454,31 +1466,31 @@ function onAgentToggle(agent, enabled) {
1454
1466
  updateStep2NextBtn();
1455
1467
  }
1456
1468
 
1457
- function firstEnabledAgent() {
1469
+ function firstEnabledAgent() {
1458
1470
  if (state.agentsEnabled.claude) return 'claude';
1459
1471
  if (state.agentsEnabled.cursor) return 'cursor';
1460
1472
  if (state.agentsEnabled.codex) return 'codex';
1461
- if (state.agentsEnabled.ccc) return 'ccc';
1462
- if (state.agentsEnabled.dsh) return 'dsh';
1473
+ if (state.agentsEnabled.ccc) return 'ccc';
1474
+ if (state.agentsEnabled.dsh) return 'dsh';
1463
1475
  return null;
1464
1476
  }
1465
1477
 
1466
- function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn) {
1478
+ function resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn) {
1467
1479
  if (claudeOn && c.claude && c.claude.defaultAgent === true) return 'claude';
1468
1480
  if (cursorOn && c.cursor && c.cursor.defaultAgent === true) return 'cursor';
1469
1481
  if (codexOn && c.codex && c.codex.defaultAgent === true) return 'codex';
1470
- if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
1471
- if (dshOn && c.dsh && c.dsh.defaultAgent === true) return 'dsh';
1482
+ if (cccOn && c.ccc && c.ccc.defaultAgent === true) return 'ccc';
1483
+ if (dshOn && c.dsh && c.dsh.defaultAgent === true) return 'dsh';
1472
1484
  if (claudeOn) return 'claude';
1473
1485
  if (cursorOn) return 'cursor';
1474
1486
  if (codexOn) return 'codex';
1475
- if (cccOn) return 'ccc';
1476
- if (dshOn) return 'dsh';
1487
+ if (cccOn) return 'ccc';
1488
+ if (dshOn) return 'dsh';
1477
1489
  return 'claude';
1478
1490
  }
1479
1491
 
1480
1492
  function updateDefaultAgentToggles() {
1481
- ['claude','cursor','codex','ccc','dsh'].forEach(function(agent){
1493
+ ['claude','cursor','codex','ccc','dsh'].forEach(function(agent){
1482
1494
  var el = document.getElementById('agent-default-' + agent);
1483
1495
  if (el) {
1484
1496
  el.checked = state.defaultAgent === agent;
@@ -1510,8 +1522,8 @@ function updateStep2NextBtn() {
1510
1522
  if (state.agentsEnabled.claude) validCount++;
1511
1523
  if (state.agentsEnabled.cursor) validCount++;
1512
1524
  if (state.agentsEnabled.codex) validCount++;
1513
- if (state.agentsEnabled.ccc) validCount++;
1514
- if (state.agentsEnabled.dsh) validCount++;
1525
+ if (state.agentsEnabled.ccc) validCount++;
1526
+ if (state.agentsEnabled.dsh) validCount++;
1515
1527
  if (validCount > 0 && !state.defaultAgent) {
1516
1528
  state.defaultAgent = firstEnabledAgent();
1517
1529
  updateDefaultAgentToggles();
@@ -1653,8 +1665,8 @@ var CLAUDE_FALLBACK_KEYS = ['model','subagentModel','effort','maxTurn'];
1653
1665
  var CURSOR_FALLBACK_KEYS = ['path','command','model','alternativeModel'];
1654
1666
  var CODEX_FALLBACK_KEYS = ['path','command','model','alternativeModel','effort','fastMode'];
1655
1667
  // 旧版 CCC 配置没有 enabled;只有 API Key 才表示用户实际完成了配置。
1656
- var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
1657
- var DSH_FALLBACK_KEYS = ['apiKey'];
1668
+ var CCC_FALLBACK_KEYS = ['DEEPSEEK_API_KEY'];
1669
+ var DSH_FALLBACK_KEYS = ['apiKey'];
1658
1670
 
1659
1671
  function renderStep2() {
1660
1672
  var c = state.config || {};
@@ -1687,7 +1699,7 @@ function renderStep2() {
1687
1699
  }
1688
1700
  var codexFastModeEl = document.getElementById('field-CHATCCC_CODEX_FAST_MODE');
1689
1701
  if (codexFastModeEl) codexFastModeEl.checked = !!(c.codex && c.codex.fastMode === true);
1690
- if (c.ccc) {
1702
+ if (c.ccc) {
1691
1703
  prefillNested('field-CHATCCC_CCC_API_KEY', c.ccc.DEEPSEEK_API_KEY);
1692
1704
  prefillNested('field-CHATCCC_CCC_BASE_URL', c.ccc.DEEPSEEK_BASE_URL);
1693
1705
  prefillNested('field-CHATCCC_CCC_PROVIDER', c.ccc.provider);
@@ -1695,37 +1707,39 @@ function renderStep2() {
1695
1707
  prefillNested('field-CHATCCC_CCC_SUB_MODEL', c.ccc.subModel);
1696
1708
  prefillNested('field-CHATCCC_CCC_ALTERNATIVE_MODEL', c.ccc.alternativeModel);
1697
1709
  prefillNested('field-CHATCCC_CCC_EFFORT', c.ccc.effort);
1698
- prefillContextWindow('field-', c.ccc.contextWindow);
1699
- }
1700
- if (c.dsh) {
1701
- prefillNested('field-CHATCCC_DSH_API_KEY', c.dsh.apiKey);
1702
- prefillNested('field-CHATCCC_DSH_BASE_URL', c.dsh.baseUrl);
1703
- prefillNested('field-CHATCCC_DSH_MODEL', c.dsh.model);
1704
- prefillNested('field-CHATCCC_DSH_PROVIDER', c.dsh.provider);
1705
- prefillNested('field-CHATCCC_DSH_MAX_TOKENS', c.dsh.maxTokens || 49152);
1706
- }
1710
+ prefillContextWindow('field-', c.ccc.contextWindow);
1711
+ }
1712
+ if (c.dsh) {
1713
+ prefillNested('field-CHATCCC_DSH_API_KEY', c.dsh.apiKey);
1714
+ prefillNested('field-CHATCCC_DSH_BASE_URL', c.dsh.baseUrl);
1715
+ prefillNested('field-CHATCCC_DSH_MODEL', c.dsh.model);
1716
+ prefillNested('field-CHATCCC_DSH_SUB_MODEL', c.dsh.subModel);
1717
+ prefillNested('field-CHATCCC_DSH_ALTERNATIVE_MODEL', c.dsh.alternativeModel);
1718
+ prefillNested('field-CHATCCC_DSH_PROVIDER', c.dsh.provider);
1719
+ prefillNested('field-CHATCCC_DSH_MAX_TOKENS', c.dsh.maxTokens || 49152);
1720
+ }
1707
1721
 
1708
1722
  // 按已有 config 决定每个 Agent 默认是否开启:优先 enabled 字段,缺省时按"任一字段非空"
1709
1723
  var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
1710
1724
  var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
1711
1725
  var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
1712
- var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
1713
- var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
1714
- // 全新用户:五个 Agent 均无启用/配置痕迹时,只默认勾选 DeepCCC(ccc),其余不勾
1715
- if (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) {
1726
+ var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
1727
+ var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
1728
+ // 全新用户:五个 Agent 均无启用/配置痕迹时,只默认勾选 DeepCCC(ccc),其余不勾
1729
+ if (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) {
1716
1730
  cccOn = true;
1717
1731
  }
1718
- state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
1732
+ state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
1719
1733
  document.getElementById('agent-enable-claude').checked = claudeOn;
1720
1734
  document.getElementById('agent-enable-cursor').checked = cursorOn;
1721
1735
  document.getElementById('agent-enable-codex').checked = codexOn;
1722
- document.getElementById('agent-enable-ccc').checked = cccOn;
1723
- document.getElementById('agent-enable-dsh').checked = dshOn;
1736
+ document.getElementById('agent-enable-ccc').checked = cccOn;
1737
+ document.getElementById('agent-enable-dsh').checked = dshOn;
1724
1738
  onAgentToggle('claude', claudeOn);
1725
1739
  onAgentToggle('cursor', cursorOn);
1726
1740
  onAgentToggle('codex', codexOn);
1727
- onAgentToggle('ccc', cccOn);
1728
- onAgentToggle('dsh', dshOn);
1741
+ onAgentToggle('ccc', cccOn);
1742
+ onAgentToggle('dsh', dshOn);
1729
1743
  updateDefaultAgentToggles();
1730
1744
 
1731
1745
  // Cursor path placeholder/hint:把已探测到的路径显示为占位
@@ -1749,7 +1763,7 @@ function renderStep2() {
1749
1763
  * 收集"待落地到 config.json 的扁平 vars"。
1750
1764
  *
1751
1765
  * - 飞书字段始终收集
1752
- * - 五个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
1766
+ * - 五个 Agent 的 enabled 状态都显式下发,让 config.json 持久化用户的最新开关偏好
1753
1767
  * - Agent 字段仅在该 Agent 开关启用时收集;未启用的 Agent 不下发其它字段,
1754
1768
  * 服务端 deepMerge 会保留 config.json 中已有值(避免关闭开关时误清空旧配置)
1755
1769
  */
@@ -1775,16 +1789,16 @@ function collectAllFields() {
1775
1789
  vars.CHATCCC_CLAUDE_ENABLED = !!state.agentsEnabled.claude;
1776
1790
  vars.CHATCCC_CURSOR_ENABLED = !!state.agentsEnabled.cursor;
1777
1791
  vars.CHATCCC_CODEX_ENABLED = !!state.agentsEnabled.codex;
1778
- vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
1779
- vars.CHATCCC_DSH_ENABLED = !!state.agentsEnabled.dsh;
1792
+ vars.CHATCCC_CCC_ENABLED = !!state.agentsEnabled.ccc;
1793
+ vars.CHATCCC_DSH_ENABLED = !!state.agentsEnabled.dsh;
1780
1794
  if (!state.defaultAgent || !state.agentsEnabled[state.defaultAgent]) {
1781
1795
  state.defaultAgent = firstEnabledAgent();
1782
1796
  }
1783
1797
  vars.CHATCCC_CLAUDE_DEFAULT_AGENT = state.defaultAgent === 'claude';
1784
1798
  vars.CHATCCC_CURSOR_DEFAULT_AGENT = state.defaultAgent === 'cursor';
1785
1799
  vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
1786
- vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
1787
- vars.CHATCCC_DSH_DEFAULT_AGENT = state.defaultAgent === 'dsh';
1800
+ vars.CHATCCC_CCC_DEFAULT_AGENT = state.defaultAgent === 'ccc';
1801
+ vars.CHATCCC_DSH_DEFAULT_AGENT = state.defaultAgent === 'dsh';
1788
1802
  if (state.agentsEnabled.claude) {
1789
1803
  AGENT_FIELDS.claude.forEach(function(key){
1790
1804
  var el = document.getElementById('field-' + key);
@@ -1805,7 +1819,7 @@ function collectAllFields() {
1805
1819
  else if (el.value.trim()) vars[key] = el.value.trim();
1806
1820
  });
1807
1821
  }
1808
- if (state.agentsEnabled.ccc) {
1822
+ if (state.agentsEnabled.ccc) {
1809
1823
  AGENT_FIELDS.ccc.forEach(function(key){
1810
1824
  if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') {
1811
1825
  var cw = contextWindowToTokensValue('field-');
@@ -1815,13 +1829,13 @@ function collectAllFields() {
1815
1829
  var el = document.getElementById('field-' + key);
1816
1830
  if (el && el.value.trim()) vars[key] = el.value.trim();
1817
1831
  });
1818
- }
1819
- if (state.agentsEnabled.dsh) {
1820
- AGENT_FIELDS.dsh.forEach(function(key){
1821
- var el = document.getElementById('field-' + key);
1822
- if (el && el.value.trim()) vars[key] = el.value.trim();
1823
- });
1824
- }
1832
+ }
1833
+ if (state.agentsEnabled.dsh) {
1834
+ AGENT_FIELDS.dsh.forEach(function(key){
1835
+ var el = document.getElementById('field-' + key);
1836
+ if (el && el.value.trim()) vars[key] = el.value.trim();
1837
+ });
1838
+ }
1825
1839
  return vars;
1826
1840
  }
1827
1841
 
@@ -1861,8 +1875,8 @@ function renderStep3() {
1861
1875
  if (state.agentsEnabled.claude) enabledList.push('claude');
1862
1876
  if (state.agentsEnabled.cursor) enabledList.push('cursor');
1863
1877
  if (state.agentsEnabled.codex) enabledList.push('codex');
1864
- if (state.agentsEnabled.ccc) enabledList.push('ccc');
1865
- if (state.agentsEnabled.dsh) enabledList.push('dsh');
1878
+ if (state.agentsEnabled.ccc) enabledList.push('ccc');
1879
+ if (state.agentsEnabled.dsh) enabledList.push('dsh');
1866
1880
  if (enabledList.length === 0) {
1867
1881
  lines.push('<div style="color:#ef4444">未启用任何 AI Agent</div>');
1868
1882
  } else {
@@ -1893,7 +1907,7 @@ function renderStep3() {
1893
1907
  lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CODEX_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
1894
1908
  lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CODEX_EFFORT || '(留空)') + '</span></div>');
1895
1909
  lines.push('<div class="config-row"><span class="key">Fast 模式</span><span class="val">' + (vars.CHATCCC_CODEX_FAST_MODE ? '已启用' : '已禁用') + '</span></div>');
1896
- } else if (t === 'ccc') {
1910
+ } else if (t === 'ccc') {
1897
1911
  lines.push('<h4 style="margin:10px 0 4px;color:#334155">CCC Agent</h4>');
1898
1912
  lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_CCC_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
1899
1913
  lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_CCC_BASE_URL || '(留空)') + '</span></div>');
@@ -1901,12 +1915,12 @@ function renderStep3() {
1901
1915
  lines.push('<div class="config-row"><span class="key">子模型</span><span class="val">' + (vars.CHATCCC_CCC_SUB_MODEL || '(留空,跟随主模型)') + '</span></div>');
1902
1916
  lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CCC_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
1903
1917
  lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CCC_EFFORT || '(留空)') + '</span></div>');
1904
- lines.push('<div class="config-row"><span class="key">上下文窗口</span><span class="val">' + contextWindowTokensLabel(vars.CHATCCC_CCC_CONTEXT_WINDOW || 1048576) + '</span></div>');
1905
- } else if (t === 'dsh') {
1906
- lines.push('<h4 style="margin:10px 0 4px;color:#334155">DeepSeek Harness</h4>');
1907
- lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_DSH_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
1908
- lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_DSH_BASE_URL || '(默认)') + '</span></div>');
1909
- lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_DSH_MODEL || 'deepseek-v4-flash') + '</span></div>');
1918
+ lines.push('<div class="config-row"><span class="key">上下文窗口</span><span class="val">' + contextWindowTokensLabel(vars.CHATCCC_CCC_CONTEXT_WINDOW || 1048576) + '</span></div>');
1919
+ } else if (t === 'dsh') {
1920
+ lines.push('<h4 style="margin:10px 0 4px;color:#334155">DeepSeek Harness</h4>');
1921
+ lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CHATCCC_DSH_API_KEY ? '***已设置***' : '(留空)') + '</span></div>');
1922
+ lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + (vars.CHATCCC_DSH_BASE_URL || '(默认)') + '</span></div>');
1923
+ lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_DSH_MODEL || 'deepseek-v4-flash') + '</span></div>');
1910
1924
  }
1911
1925
  });
1912
1926
  document.getElementById('review-content').innerHTML = lines.join('');
@@ -1934,21 +1948,21 @@ async function setDashboardDefaultAgent(agent, enabled) {
1934
1948
  CHATCCC_CLAUDE_DEFAULT_AGENT: agent === 'claude',
1935
1949
  CHATCCC_CURSOR_DEFAULT_AGENT: agent === 'cursor',
1936
1950
  CHATCCC_CODEX_DEFAULT_AGENT: agent === 'codex',
1937
- CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc',
1938
- CHATCCC_DSH_DEFAULT_AGENT: agent === 'dsh'
1951
+ CHATCCC_CCC_DEFAULT_AGENT: agent === 'ccc',
1952
+ CHATCCC_DSH_DEFAULT_AGENT: agent === 'dsh'
1939
1953
  };
1940
1954
  var result = await api('/api/config', 'POST', { vars: vars });
1941
1955
  if (result.ok) {
1942
1956
  state.config.claude = state.config.claude || {};
1943
1957
  state.config.cursor = state.config.cursor || {};
1944
1958
  state.config.codex = state.config.codex || {};
1945
- state.config.ccc = state.config.ccc || {};
1946
- state.config.dsh = state.config.dsh || {};
1959
+ state.config.ccc = state.config.ccc || {};
1960
+ state.config.dsh = state.config.dsh || {};
1947
1961
  state.config.claude.defaultAgent = agent === 'claude';
1948
1962
  state.config.cursor.defaultAgent = agent === 'cursor';
1949
1963
  state.config.codex.defaultAgent = agent === 'codex';
1950
- state.config.ccc.defaultAgent = agent === 'ccc';
1951
- state.config.dsh.defaultAgent = agent === 'dsh';
1964
+ state.config.ccc.defaultAgent = agent === 'ccc';
1965
+ state.config.dsh.defaultAgent = agent === 'dsh';
1952
1966
  updateDefaultAgentToggles();
1953
1967
  toastConfigApplyResult(result, '默认 Agent 已更新');
1954
1968
  } else {
@@ -2065,10 +2079,10 @@ function updateDashboardUI() {
2065
2079
  var claudeOn = isAgentEnabled(c.claude, CLAUDE_FALLBACK_KEYS);
2066
2080
  var cursorOn = isAgentEnabled(c.cursor, CURSOR_FALLBACK_KEYS);
2067
2081
  var codexOn = isAgentEnabled(c.codex, CODEX_FALLBACK_KEYS);
2068
- var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
2069
- var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
2070
- state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn, ccc: cccOn, dsh: dshOn };
2071
- state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
2082
+ var cccOn = isAgentEnabled(c.ccc, CCC_FALLBACK_KEYS);
2083
+ var dshOn = isAgentEnabled(c.dsh, DSH_FALLBACK_KEYS);
2084
+ state.agentsEnabled = { claude: claudeOn, cursor: cursorOn, codex: codexOn, ccc: cccOn, dsh: dshOn };
2085
+ state.defaultAgent = resolveDefaultAgentFromConfig(c, claudeOn, cursorOn, codexOn, cccOn, dshOn);
2072
2086
 
2073
2087
  // 微信 iLink 平台开关:同步复选框和标签
2074
2088
  var ilinkEnabled = c.platforms && c.platforms.ilink ? c.platforms.ilink.enabled !== false : true;
@@ -2100,12 +2114,12 @@ function updateDashboardUI() {
2100
2114
  document.getElementById('dash-claude').style.display = claudeOn ? '' : 'none';
2101
2115
  document.getElementById('dash-cursor').style.display = cursorOn ? '' : 'none';
2102
2116
  document.getElementById('dash-codex').style.display = codexOn ? '' : 'none';
2103
- document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
2104
- document.getElementById('dash-dsh').style.display = dshOn ? '' : 'none';
2117
+ document.getElementById('dash-ccc').style.display = cccOn ? '' : 'none';
2118
+ document.getElementById('dash-dsh').style.display = dshOn ? '' : 'none';
2105
2119
  updateDefaultAgentToggles();
2106
- // 五个都未启用时给一个空态提示,引导用户去配置向导启用
2120
+ // 五个都未启用时给一个空态提示,引导用户去配置向导启用
2107
2121
  var emptyHint = document.getElementById('dash-no-agent-hint');
2108
- if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) ? '' : 'none';
2122
+ if (emptyHint) emptyHint.style.display = (!claudeOn && !cursorOn && !codexOn && !cccOn && !dshOn) ? '' : 'none';
2109
2123
 
2110
2124
  document.getElementById('cfg-ANTHROPIC_MODEL').textContent = (c.claude && c.claude.model) || '(留空)';
2111
2125
  document.getElementById('cfg-ANTHROPIC_SUBAGENT_MODEL').textContent = (c.claude && c.claude.subagentModel) || '(留空)';
@@ -2131,13 +2145,15 @@ function updateDashboardUI() {
2131
2145
  document.getElementById('cfg-CCC_PROVIDER').textContent = (c.ccc && c.ccc.provider) ? c.ccc.provider : '(跟随 DeepCCC 内核配置)';
2132
2146
  document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
2133
2147
  document.getElementById('cfg-CCC_SUB_MODEL').textContent = (c.ccc && c.ccc.subModel) || '(留空,跟随主模型)';
2134
- document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
2135
- document.getElementById('cfg-DSH_API_KEY').textContent = (c.dsh && c.dsh.apiKey) ? '***已设置***' : '(留空)';
2136
- document.getElementById('cfg-DSH_BASE_URL').textContent = (c.dsh && c.dsh.baseUrl) || 'https://api.deepseek.com/v1';
2137
- document.getElementById('cfg-DSH_MODEL').textContent = (c.dsh && c.dsh.model) || 'deepseek-v4-flash';
2138
- document.getElementById('cfg-DSH_PROVIDER').textContent = (c.dsh && c.dsh.provider) || 'deepseek-official';
2139
- document.getElementById('cfg-DSH_MAX_TOKENS').textContent = String((c.dsh && c.dsh.maxTokens) || 49152);
2140
- engineRefreshStatus('dsh');
2148
+ document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
2149
+ document.getElementById('cfg-DSH_API_KEY').textContent = (c.dsh && c.dsh.apiKey) ? '***已设置***' : '(留空)';
2150
+ document.getElementById('cfg-DSH_BASE_URL').textContent = (c.dsh && c.dsh.baseUrl) || 'https://api.deepseek.com/v1';
2151
+ document.getElementById('cfg-DSH_MODEL').textContent = (c.dsh && c.dsh.model) || 'deepseek-v4-flash';
2152
+ document.getElementById('cfg-DSH_SUB_MODEL').textContent = (c.dsh && c.dsh.subModel) || '(留空,跟随主模型)';
2153
+ document.getElementById('cfg-DSH_ALTERNATIVE_MODEL').textContent = (c.dsh && c.dsh.alternativeModel) || '(留空)';
2154
+ document.getElementById('cfg-DSH_PROVIDER').textContent = (c.dsh && c.dsh.provider) || 'deepseek-official';
2155
+ document.getElementById('cfg-DSH_MAX_TOKENS').textContent = String((c.dsh && c.dsh.maxTokens) || 49152);
2156
+ engineRefreshStatus('dsh');
2141
2157
  }
2142
2158
 
2143
2159
  function pollStatus() {
@@ -2193,7 +2209,7 @@ function editSection(section) {
2193
2209
  else if (section === 'chromeDevtools') fields = CHROME_DEVTOOLS_FIELDS;
2194
2210
  else fields = AGENT_FIELDS[section] || [];
2195
2211
 
2196
- var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent', dsh: 'DeepSeek Harness' };
2212
+ var titleMap = { feishu: '飞书', webUi: 'Web UI', chromeDevtools: 'Chrome CDP', claude: 'Claude Agent', cursor: 'Cursor Agent', codex: 'Codex Agent', ccc: 'CCC Agent', dsh: 'DeepSeek Harness' };
2197
2213
  document.getElementById('edit-modal-title').textContent = '编辑 ' + (titleMap[section] || section);
2198
2214
 
2199
2215
  document.getElementById('edit-modal-effect').textContent = configEffectHint(section);
@@ -2214,8 +2230,8 @@ function editSection(section) {
2214
2230
  'CHATCCC_CODEX_FAST_MODE': 'Fast 模式',
2215
2231
  'CHATCCC_CCC_API_KEY': 'API Key', 'CHATCCC_CCC_BASE_URL': 'Base URL',
2216
2232
  'CHATCCC_CCC_PROVIDER': 'API 协议(选填)',
2217
- 'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_SUB_MODEL': '子模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort', 'CHATCCC_CCC_CONTEXT_WINDOW': '上下文窗口',
2218
- 'CHATCCC_DSH_API_KEY': 'API Key', 'CHATCCC_DSH_BASE_URL': 'Base URL', 'CHATCCC_DSH_MODEL': '模型', 'CHATCCC_DSH_PROVIDER': 'Provider 路由', 'CHATCCC_DSH_MAX_TOKENS': '单次最大输出 Tokens'
2233
+ 'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_SUB_MODEL': '子模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort', 'CHATCCC_CCC_CONTEXT_WINDOW': '上下文窗口',
2234
+ 'CHATCCC_DSH_API_KEY': 'API Key', 'CHATCCC_DSH_BASE_URL': 'Base URL', 'CHATCCC_DSH_MODEL': '模型', 'CHATCCC_DSH_SUB_MODEL': '子模型', 'CHATCCC_DSH_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_DSH_PROVIDER': 'Provider 路由', 'CHATCCC_DSH_MAX_TOKENS': '单次最大输出 Tokens'
2219
2235
  };
2220
2236
  var hintMap = {
2221
2237
  'CHATCCC_WEB_UI_OPEN_ON_START': '关闭后可继续手动访问 http://localhost:<端口>/;/restart、/update 和 Web UI 重启无论此项为何值都不会自动打开。',
@@ -2263,7 +2279,7 @@ function editSection(section) {
2263
2279
  else if (key === 'CHATCCC_CODEX_ALTERNATIVE_MODEL') val = state.config.codex.alternativeModel || '';
2264
2280
  else if (key === 'CHATCCC_CODEX_EFFORT') val = state.config.codex.effort || '';
2265
2281
  else if (key === 'CHATCCC_CODEX_FAST_MODE') val = state.config.codex.fastMode === true ? 'true' : 'false';
2266
- } else if (section === 'ccc' && state.config.ccc) {
2282
+ } else if (section === 'ccc' && state.config.ccc) {
2267
2283
  if (key === 'CHATCCC_CCC_API_KEY') val = state.config.ccc.DEEPSEEK_API_KEY || '';
2268
2284
  else if (key === 'CHATCCC_CCC_BASE_URL') val = state.config.ccc.DEEPSEEK_BASE_URL || '';
2269
2285
  else if (key === 'CHATCCC_CCC_PROVIDER') val = state.config.ccc.provider || '';
@@ -2271,14 +2287,16 @@ function editSection(section) {
2271
2287
  else if (key === 'CHATCCC_CCC_SUB_MODEL') val = state.config.ccc.subModel || '';
2272
2288
  else if (key === 'CHATCCC_CCC_ALTERNATIVE_MODEL') val = state.config.ccc.alternativeModel || '';
2273
2289
  else if (key === 'CHATCCC_CCC_EFFORT') val = state.config.ccc.effort || '';
2274
- else if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') val = state.config.ccc.contextWindow || '1048576';
2275
- } else if (section === 'dsh' && state.config.dsh) {
2276
- if (key === 'CHATCCC_DSH_API_KEY') val = state.config.dsh.apiKey || '';
2277
- else if (key === 'CHATCCC_DSH_BASE_URL') val = state.config.dsh.baseUrl || '';
2278
- else if (key === 'CHATCCC_DSH_MODEL') val = state.config.dsh.model || '';
2279
- else if (key === 'CHATCCC_DSH_PROVIDER') val = state.config.dsh.provider || '';
2280
- else if (key === 'CHATCCC_DSH_MAX_TOKENS') val = state.config.dsh.maxTokens || '49152';
2281
- }
2290
+ else if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') val = state.config.ccc.contextWindow || '1048576';
2291
+ } else if (section === 'dsh' && state.config.dsh) {
2292
+ if (key === 'CHATCCC_DSH_API_KEY') val = state.config.dsh.apiKey || '';
2293
+ else if (key === 'CHATCCC_DSH_BASE_URL') val = state.config.dsh.baseUrl || '';
2294
+ else if (key === 'CHATCCC_DSH_MODEL') val = state.config.dsh.model || '';
2295
+ else if (key === 'CHATCCC_DSH_SUB_MODEL') val = state.config.dsh.subModel || '';
2296
+ else if (key === 'CHATCCC_DSH_ALTERNATIVE_MODEL') val = state.config.dsh.alternativeModel || '';
2297
+ else if (key === 'CHATCCC_DSH_PROVIDER') val = state.config.dsh.provider || '';
2298
+ else if (key === 'CHATCCC_DSH_MAX_TOKENS') val = state.config.dsh.maxTokens || '49152';
2299
+ }
2282
2300
  }
2283
2301
  var isSecret = key.includes('SECRET') || key.includes('API_KEY');
2284
2302
  if (key === 'CHATCCC_WEB_UI_OPEN_ON_START' || key === 'CHATCCC_CHROME_DEVTOOLS_ENABLED' || key === 'CHATCCC_CODEX_FAST_MODE') {
@@ -2428,95 +2446,95 @@ function validateCli(tool) {
2428
2446
  });
2429
2447
  }
2430
2448
 
2431
- // ---- 通用 Agent 引擎:一次点击,后端自动执行全部原子安装步骤 ----
2432
- var enginePollTimers = {};
2433
- function escapeHtml(value) {
2434
- return String(value == null ? '' : value).replace(/[&<>"']/g, function(char){
2435
- return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[char];
2436
- });
2437
- }
2438
-
2439
- function onInstallableAgentToggle(engineId, el) {
2440
- if (!el.checked) { onAgentToggle(engineId, false); return; }
2441
- onAgentToggle(engineId, true);
2442
- api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
2443
- renderEngineStatus(engineId, status);
2444
- if (!status.installed && !status.running) installEngine(engineId);
2445
- else if (status.running) scheduleEnginePoll(engineId);
2446
- }).catch(function(error){
2447
- el.checked = false;
2448
- onAgentToggle(engineId, false);
2449
- toast('引擎状态查询失败: ' + String(error), 'error');
2450
- });
2451
- }
2452
-
2453
- function renderEngineStatus(engineId, status) {
2454
- var ids = [engineId + '-engine-status', engineId + '-dashboard-engine-status'];
2455
- var job = status.job;
2456
- var state = job && job.state;
2457
- var text = status.installed ? ('已安装 v' + (status.version || '未知版本')) : '未安装';
2458
- var color = status.installed ? '#15803d' : '#64748b';
2459
- if (state === 'running') { text = '安装中 ' + (job.percent || 0) + '%'; color = '#1d4ed8'; }
2460
- if (state === 'failed') { text = '安装失败'; color = '#dc2626'; }
2461
- ids.forEach(function(id){
2462
- var el = document.getElementById(id);
2463
- if (!el) return;
2464
- el.innerHTML = '<span style="color:' + color + '">' + text + '</span>' +
2465
- (job && job.error ? '<br><span style="color:#dc2626;font-size:12px">' + escapeHtml(job.error) + '</span>' : '');
2466
- });
2467
- [engineId + '-engine-steps', engineId + '-dashboard-engine-steps'].forEach(function(id){
2468
- var root = document.getElementById(id);
2469
- if (!root) return;
2470
- root.innerHTML = job && job.steps ? job.steps.map(function(step){
2471
- var icon = step.state === 'completed' ? '✓' : step.state === 'failed' ? '!' : step.state === 'running' ? '●' : '○';
2472
- return '<div class="engine-step ' + step.state + '"><span class="engine-step-icon">' + icon + '</span><span>' +
2473
- escapeHtml(step.label + (step.message && step.message !== '等待中' ? ' · ' + step.message : '')) +
2474
- '</span><span class="engine-step-percent">' + (step.state === 'running' ? step.percent + '%' : '') + '</span></div>';
2475
- }).join('') : '';
2476
- });
2477
- [engineId + '-engine-install-btn', engineId + '-dashboard-engine-install-btn'].forEach(function(id){
2478
- var button = document.getElementById(id);
2479
- if (!button) return;
2480
- button.disabled = state === 'running';
2481
- button.textContent = state === 'running' ? '自动安装中…' : state === 'failed' ? '重试' : status.installed ? '原子升级/重新安装' : '安装并启用';
2482
- });
2483
- }
2484
-
2485
- function scheduleEnginePoll(engineId) {
2486
- if (enginePollTimers[engineId]) clearTimeout(enginePollTimers[engineId]);
2487
- enginePollTimers[engineId] = setTimeout(function(){ engineRefreshStatus(engineId); }, 800);
2488
- }
2489
-
2490
- function engineRefreshStatus(engineId) {
2491
- return api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
2492
- renderEngineStatus(engineId, status);
2493
- if (status.running || (status.job && status.job.state === 'running')) scheduleEnginePoll(engineId);
2494
- return status;
2495
- }).catch(function(){ scheduleEnginePoll(engineId); });
2496
- }
2497
-
2498
- function installEngine(engineId) {
2499
- var toggle = document.getElementById('agent-enable-' + engineId);
2500
- if (toggle && !toggle.checked) {
2501
- toggle.checked = true;
2502
- onAgentToggle(engineId, true);
2503
- }
2504
- return api('/api/engines/' + encodeURIComponent(engineId) + '/install', 'POST').then(function(result){
2505
- if (!result.ok) throw new Error(result.error || '启动安装失败');
2506
- renderEngineStatus(engineId, { installed: false, running: true, job: result.job });
2507
- scheduleEnginePoll(engineId);
2508
- return result;
2509
- }).catch(function(error){
2510
- renderEngineStatus(engineId, { installed: false, running: false, job: { state: 'failed', error: String(error), steps: [] } });
2511
- toast('引擎安装失败: ' + String(error), 'error');
2512
- });
2513
- }
2449
+ // ---- 通用 Agent 引擎:一次点击,后端自动执行全部原子安装步骤 ----
2450
+ var enginePollTimers = {};
2451
+ function escapeHtml(value) {
2452
+ return String(value == null ? '' : value).replace(/[&<>"']/g, function(char){
2453
+ return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[char];
2454
+ });
2455
+ }
2456
+
2457
+ function onInstallableAgentToggle(engineId, el) {
2458
+ if (!el.checked) { onAgentToggle(engineId, false); return; }
2459
+ onAgentToggle(engineId, true);
2460
+ api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
2461
+ renderEngineStatus(engineId, status);
2462
+ if (!status.installed && !status.running) installEngine(engineId);
2463
+ else if (status.running) scheduleEnginePoll(engineId);
2464
+ }).catch(function(error){
2465
+ el.checked = false;
2466
+ onAgentToggle(engineId, false);
2467
+ toast('引擎状态查询失败: ' + String(error), 'error');
2468
+ });
2469
+ }
2470
+
2471
+ function renderEngineStatus(engineId, status) {
2472
+ var ids = [engineId + '-engine-status', engineId + '-dashboard-engine-status'];
2473
+ var job = status.job;
2474
+ var state = job && job.state;
2475
+ var text = status.installed ? ('已安装 v' + (status.version || '未知版本')) : '未安装';
2476
+ var color = status.installed ? '#15803d' : '#64748b';
2477
+ if (state === 'running') { text = '安装中 ' + (job.percent || 0) + '%'; color = '#1d4ed8'; }
2478
+ if (state === 'failed') { text = '安装失败'; color = '#dc2626'; }
2479
+ ids.forEach(function(id){
2480
+ var el = document.getElementById(id);
2481
+ if (!el) return;
2482
+ el.innerHTML = '<span style="color:' + color + '">' + text + '</span>' +
2483
+ (job && job.error ? '<br><span style="color:#dc2626;font-size:12px">' + escapeHtml(job.error) + '</span>' : '');
2484
+ });
2485
+ [engineId + '-engine-steps', engineId + '-dashboard-engine-steps'].forEach(function(id){
2486
+ var root = document.getElementById(id);
2487
+ if (!root) return;
2488
+ root.innerHTML = job && job.steps ? job.steps.map(function(step){
2489
+ var icon = step.state === 'completed' ? '✓' : step.state === 'failed' ? '!' : step.state === 'running' ? '●' : '○';
2490
+ return '<div class="engine-step ' + step.state + '"><span class="engine-step-icon">' + icon + '</span><span>' +
2491
+ escapeHtml(step.label + (step.message && step.message !== '等待中' ? ' · ' + step.message : '')) +
2492
+ '</span><span class="engine-step-percent">' + (step.state === 'running' ? step.percent + '%' : '') + '</span></div>';
2493
+ }).join('') : '';
2494
+ });
2495
+ [engineId + '-engine-install-btn', engineId + '-dashboard-engine-install-btn'].forEach(function(id){
2496
+ var button = document.getElementById(id);
2497
+ if (!button) return;
2498
+ button.disabled = state === 'running';
2499
+ button.textContent = state === 'running' ? '自动安装中…' : state === 'failed' ? '重试' : status.installed ? '原子升级/重新安装' : '安装并启用';
2500
+ });
2501
+ }
2502
+
2503
+ function scheduleEnginePoll(engineId) {
2504
+ if (enginePollTimers[engineId]) clearTimeout(enginePollTimers[engineId]);
2505
+ enginePollTimers[engineId] = setTimeout(function(){ engineRefreshStatus(engineId); }, 800);
2506
+ }
2507
+
2508
+ function engineRefreshStatus(engineId) {
2509
+ return api('/api/engines/' + encodeURIComponent(engineId) + '/status', 'GET').then(function(status){
2510
+ renderEngineStatus(engineId, status);
2511
+ if (status.running || (status.job && status.job.state === 'running')) scheduleEnginePoll(engineId);
2512
+ return status;
2513
+ }).catch(function(){ scheduleEnginePoll(engineId); });
2514
+ }
2515
+
2516
+ function installEngine(engineId) {
2517
+ var toggle = document.getElementById('agent-enable-' + engineId);
2518
+ if (toggle && !toggle.checked) {
2519
+ toggle.checked = true;
2520
+ onAgentToggle(engineId, true);
2521
+ }
2522
+ return api('/api/engines/' + encodeURIComponent(engineId) + '/install', 'POST').then(function(result){
2523
+ if (!result.ok) throw new Error(result.error || '启动安装失败');
2524
+ renderEngineStatus(engineId, { installed: false, running: true, job: result.job });
2525
+ scheduleEnginePoll(engineId);
2526
+ return result;
2527
+ }).catch(function(error){
2528
+ renderEngineStatus(engineId, { installed: false, running: false, job: { state: 'failed', error: String(error), steps: [] } });
2529
+ toast('引擎安装失败: ' + String(error), 'error');
2530
+ });
2531
+ }
2514
2532
 
2515
2533
  // ---- Start ----
2516
2534
  init();
2517
2535
 
2518
- // 页面刷新后从持久化任务文件恢复每一步的安装进度。
2519
- setTimeout(function(){ engineRefreshStatus('claude'); engineRefreshStatus('dsh'); }, 300);
2536
+ // 页面刷新后从持久化任务文件恢复每一步的安装进度。
2537
+ setTimeout(function(){ engineRefreshStatus('claude'); engineRefreshStatus('dsh'); }, 300);
2520
2538
  </script>
2521
2539
  </body>
2522
2540
  </html>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatccc",
3
- "version": "0.2.261",
3
+ "version": "0.2.263",
4
4
  "description": "Feishu bot bridge for Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",