chatccc 0.2.55 → 0.2.57

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/src/simplify.ts CHANGED
@@ -1,120 +1,120 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { resolve as resolvePath } from "node:path";
3
- import { PROJECT_ROOT } from "./config.ts";
4
-
5
- // ---------------------------------------------------------------------------
6
- // 消息简化规则 —— 数据驱动,在 simplify.json 中配置
7
- // ---------------------------------------------------------------------------
8
-
9
- interface ToolRule {
10
- template: string;
11
- maxLength: number;
12
- }
13
-
14
- interface SimplifyConfig {
15
- tool_use?: Record<string, ToolRule>;
16
- tool_result?: Record<string, ToolRule>;
17
- }
18
-
19
- let config: SimplifyConfig | null = null;
20
- let loaded = false;
21
-
22
- function loadConfig(): SimplifyConfig {
23
- const filePath = resolvePath(PROJECT_ROOT, "simplify.json");
24
- if (!existsSync(filePath)) return {};
25
- try {
26
- const raw = readFileSync(filePath, "utf-8");
27
- const parsed = JSON.parse(raw);
28
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
29
- console.error("[SIMPLIFY] simplify.json 格式错误:应为对象");
30
- return {};
31
- }
32
- return parsed as SimplifyConfig;
33
- } catch (err) {
34
- console.error(`[SIMPLIFY] 读取 simplify.json 失败: ${(err as Error).message}`);
35
- return {};
36
- }
37
- }
38
-
39
- function getConfig(): SimplifyConfig {
40
- if (!loaded) {
41
- config = loadConfig();
42
- loaded = true;
43
- }
44
- return config!;
45
- }
46
-
47
- /** 热重载 */
48
- export function reloadSimplifyConfig(): void {
49
- loaded = false;
50
- config = null;
51
- }
52
-
53
- /**
54
- * 对模板字符串中的 {field} 占位符做替换。
55
- * fields 为可用字段映射,extra 是额外的上下文(如 tool_use_id 的 id)。
56
- */
57
- function resolveTemplate(template: string, fields: Record<string, unknown>, extra?: Record<string, string>): string {
58
- let result = template;
59
- if (extra) {
60
- for (const [k, v] of Object.entries(extra)) {
61
- result = result.split(`{${k}}`).join(v);
62
- }
63
- }
64
- for (const [k, v] of Object.entries(fields)) {
65
- const strVal = typeof v === "string" ? v : JSON.stringify(v);
66
- result = result.split(`{${k}}`).join(strVal);
67
- }
68
- return result;
69
- }
70
-
71
- /**
72
- * 简化 tool_use 展示。
73
- * 返回 null 表示无规则,调用方应回退到默认格式化。
74
- */
75
- export function simplifyToolUse(name: string, input: unknown): string | null {
76
- const cfg = getConfig();
77
- const rules = cfg.tool_use;
78
- if (!rules) return null;
79
- const rule = rules[name];
80
- if (!rule) return null;
81
-
82
- const fields = typeof input === "object" && input !== null
83
- ? input as Record<string, unknown>
84
- : {};
85
- let result = resolveTemplate(rule.template, fields);
86
- if (result.length > rule.maxLength) {
87
- result = result.slice(0, rule.maxLength) + "...";
88
- }
89
- return result;
90
- }
91
-
92
- /**
93
- * 简化 tool_result 展示。
94
- * 返回 null 表示无规则,调用方应回退到默认格式化。
95
- * toolCallInput 为对应的 tool_use 输入(可选),用于在 result 模板中引用输入字段。
96
- */
97
- export function simplifyToolResult(
98
- name: string,
99
- toolUseId: string,
100
- isError: boolean,
101
- toolCallInput?: unknown,
102
- ): string | null {
103
- const cfg = getConfig();
104
- const rules = cfg.tool_result;
105
- if (!rules) return null;
106
- const rule = rules[name];
107
- if (!rule) return null;
108
-
109
- const id = toolUseId.slice(-6);
110
- const extra = { id };
111
- const fields = toolCallInput && typeof toolCallInput === "object"
112
- ? toolCallInput as Record<string, unknown>
113
- : {};
114
- let result = resolveTemplate(rule.template, fields, extra);
115
- if (isError) result = "❌ " + result;
116
- if (result.length > rule.maxLength) {
117
- result = result.slice(0, rule.maxLength) + "...";
118
- }
119
- return result;
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { resolve as resolvePath } from "node:path";
3
+ import { PROJECT_ROOT } from "./config.ts";
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // 消息简化规则 —— 数据驱动,在 simplify.json 中配置
7
+ // ---------------------------------------------------------------------------
8
+
9
+ interface ToolRule {
10
+ template: string;
11
+ maxLength: number;
12
+ }
13
+
14
+ interface SimplifyConfig {
15
+ tool_use?: Record<string, ToolRule>;
16
+ tool_result?: Record<string, ToolRule>;
17
+ }
18
+
19
+ let config: SimplifyConfig | null = null;
20
+ let loaded = false;
21
+
22
+ function loadConfig(): SimplifyConfig {
23
+ const filePath = resolvePath(PROJECT_ROOT, "simplify.json");
24
+ if (!existsSync(filePath)) return {};
25
+ try {
26
+ const raw = readFileSync(filePath, "utf-8");
27
+ const parsed = JSON.parse(raw);
28
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
29
+ console.error("[SIMPLIFY] simplify.json 格式错误:应为对象");
30
+ return {};
31
+ }
32
+ return parsed as SimplifyConfig;
33
+ } catch (err) {
34
+ console.error(`[SIMPLIFY] 读取 simplify.json 失败: ${(err as Error).message}`);
35
+ return {};
36
+ }
37
+ }
38
+
39
+ function getConfig(): SimplifyConfig {
40
+ if (!loaded) {
41
+ config = loadConfig();
42
+ loaded = true;
43
+ }
44
+ return config!;
45
+ }
46
+
47
+ /** 热重载 */
48
+ export function reloadSimplifyConfig(): void {
49
+ loaded = false;
50
+ config = null;
51
+ }
52
+
53
+ /**
54
+ * 对模板字符串中的 {field} 占位符做替换。
55
+ * fields 为可用字段映射,extra 是额外的上下文(如 tool_use_id 的 id)。
56
+ */
57
+ function resolveTemplate(template: string, fields: Record<string, unknown>, extra?: Record<string, string>): string {
58
+ let result = template;
59
+ if (extra) {
60
+ for (const [k, v] of Object.entries(extra)) {
61
+ result = result.split(`{${k}}`).join(v);
62
+ }
63
+ }
64
+ for (const [k, v] of Object.entries(fields)) {
65
+ const strVal = typeof v === "string" ? v : JSON.stringify(v);
66
+ result = result.split(`{${k}}`).join(strVal);
67
+ }
68
+ return result;
69
+ }
70
+
71
+ /**
72
+ * 简化 tool_use 展示。
73
+ * 返回 null 表示无规则,调用方应回退到默认格式化。
74
+ */
75
+ export function simplifyToolUse(name: string, input: unknown): string | null {
76
+ const cfg = getConfig();
77
+ const rules = cfg.tool_use;
78
+ if (!rules) return null;
79
+ const rule = rules[name];
80
+ if (!rule) return null;
81
+
82
+ const fields = typeof input === "object" && input !== null
83
+ ? input as Record<string, unknown>
84
+ : {};
85
+ let result = resolveTemplate(rule.template, fields);
86
+ if (result.length > rule.maxLength) {
87
+ result = result.slice(0, rule.maxLength) + "...";
88
+ }
89
+ return result;
90
+ }
91
+
92
+ /**
93
+ * 简化 tool_result 展示。
94
+ * 返回 null 表示无规则,调用方应回退到默认格式化。
95
+ * toolCallInput 为对应的 tool_use 输入(可选),用于在 result 模板中引用输入字段。
96
+ */
97
+ export function simplifyToolResult(
98
+ name: string,
99
+ toolUseId: string,
100
+ isError: boolean,
101
+ toolCallInput?: unknown,
102
+ ): string | null {
103
+ const cfg = getConfig();
104
+ const rules = cfg.tool_result;
105
+ if (!rules) return null;
106
+ const rule = rules[name];
107
+ if (!rule) return null;
108
+
109
+ const id = toolUseId.slice(-6);
110
+ const extra = { id };
111
+ const fields = toolCallInput && typeof toolCallInput === "object"
112
+ ? toolCallInput as Record<string, unknown>
113
+ : {};
114
+ let result = resolveTemplate(rule.template, fields, extra);
115
+ if (isError) result = "❌ " + result;
116
+ if (result.length > rule.maxLength) {
117
+ result = result.slice(0, rule.maxLength) + "...";
118
+ }
119
+ return result;
120
120
  }
package/src/web-ui.ts CHANGED
@@ -31,7 +31,7 @@ interface AppConfig {
31
31
  platforms?: { feishu?: { enabled?: boolean }; ilink?: { enabled?: boolean } };
32
32
  port?: number;
33
33
  gitTimeoutSeconds?: number;
34
- claude?: { enabled?: boolean; defaultAgent?: boolean; model?: string; effort?: string; apiKey?: string; baseUrl?: string };
34
+ claude?: { enabled?: boolean; defaultAgent?: boolean; model?: string; subagentModel?: string; effort?: string; apiKey?: string; baseUrl?: string };
35
35
  // `command` 是已废弃的旧字段名,保留只读以兼容升级前的 config.json
36
36
  cursor?: { enabled?: boolean; defaultAgent?: boolean; path?: string; command?: string; model?: string };
37
37
  codex?: { enabled?: boolean; defaultAgent?: boolean; path?: string; command?: string; model?: string; effort?: string };
@@ -126,7 +126,11 @@ function readToolPath(tool?: { path?: string; command?: string }): string {
126
126
  function loadConfig(): AppConfig {
127
127
  if (!existsSync(CONFIG_FILE)) return {};
128
128
  try {
129
- return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
129
+ let raw = readFileSync(CONFIG_FILE, "utf8");
130
+ // 移除可能意外写入的 UTF-8 BOM,避免 JSON.parse 失败导致返回空对象、
131
+ // UI 显示所有配置为空。
132
+ if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);
133
+ return JSON.parse(raw);
130
134
  } catch {
131
135
  return {};
132
136
  }
@@ -330,7 +334,7 @@ function deepMerge(target: Record<string, unknown>, source: Record<string, unkno
330
334
  }
331
335
 
332
336
  // Convert flat key-value pairs to nested config structure
333
- function unflattenConfig(flat: Record<string, unknown>): Record<string, unknown> {
337
+ export function unflattenConfig(flat: Record<string, unknown>): Record<string, unknown> {
334
338
  const result: Record<string, unknown> = {};
335
339
  for (const [key, val] of Object.entries(flat)) {
336
340
  if (key === "CHATCCC_APP_ID") {
@@ -360,6 +364,9 @@ function unflattenConfig(flat: Record<string, unknown>): Record<string, unknown>
360
364
  } else if (key === "CHATCCC_ANTHROPIC_MODEL") {
361
365
  result.claude = result.claude || {};
362
366
  (result.claude as Record<string, unknown>).model = val;
367
+ } else if (key === "CHATCCC_ANTHROPIC_SUBAGENT_MODEL") {
368
+ result.claude = result.claude || {};
369
+ (result.claude as Record<string, unknown>).subagentModel = val;
363
370
  } else if (key === "CHATCCC_ANTHROPIC_EFFORT") {
364
371
  result.claude = result.claude || {};
365
372
  (result.claude as Record<string, unknown>).effort = val;
@@ -705,6 +712,10 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
705
712
  <label>模型</label>
706
713
  <input type="text" id="field-CHATCCC_ANTHROPIC_MODEL" placeholder="留空表示不向 SDK 传 model">
707
714
  </div>
715
+ <div class="form-group" id="claude-subagent-model-field">
716
+ <label>Subagent 模型</label>
717
+ <input type="text" id="field-CHATCCC_ANTHROPIC_SUBAGENT_MODEL" placeholder="留空表示不向 SDK 传 CLAUDE_CODE_SUBAGENT_MODEL">
718
+ </div>
708
719
  <div class="form-group">
709
720
  <label>思考深度 (Effort)</label>
710
721
  <input type="text" id="field-CHATCCC_ANTHROPIC_EFFORT" placeholder="留空表示不向 SDK 传 effort">
@@ -844,6 +855,7 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
844
855
  <div class="config-row" id="cfg-row-CLAUDE_API_KEY"><span class="key">API Key</span><span class="val" id="cfg-CLAUDE_API_KEY">-</span></div>
845
856
  <div class="config-row" id="cfg-row-CLAUDE_BASE_URL"><span class="key">Base URL</span><span class="val" id="cfg-CLAUDE_BASE_URL">-</span></div>
846
857
  <div class="config-row"><span class="key">模型</span><span class="val" id="cfg-ANTHROPIC_MODEL">-</span></div>
858
+ <div class="config-row" id="cfg-row-ANTHROPIC_SUBAGENT_MODEL"><span class="key">Subagent 模型</span><span class="val" id="cfg-ANTHROPIC_SUBAGENT_MODEL">-</span></div>
847
859
  <div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-ANTHROPIC_EFFORT">-</span></div>
848
860
  <label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-claude" onchange="setDashboardDefaultAgent('claude', this.checked)"> 设为默认 Agent</label>
849
861
  <button class="btn btn-outline" style="margin-top:8px" onclick="editSection('claude')">编辑</button>
@@ -911,7 +923,7 @@ let state = {
911
923
  var step2InputBound = false;
912
924
 
913
925
  const AGENT_FIELDS = {
914
- claude: ['CLAUDE_API_KEY','CLAUDE_BASE_URL','CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_EFFORT'],
926
+ claude: ['CLAUDE_API_KEY','CLAUDE_BASE_URL','CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT'],
915
927
  cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL'],
916
928
  codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_EFFORT']
917
929
  };
@@ -925,7 +937,11 @@ var claudeApiMode = 'official';
925
937
  // 示例只是引导);官方模式不列举任何具体值,避免给出过期/不支持的取值产生误导。
926
938
  var CLAUDE_MODEL_PLACEHOLDER = {
927
939
  official: '留空表示不向 SDK 传 model',
928
- thirdparty: 'deepseek-v4-pro'
940
+ thirdparty: 'claude-sonnet-4-6'
941
+ };
942
+ var CLAUDE_SUBAGENT_MODEL_PLACEHOLDER = {
943
+ official: '留空表示不向 SDK 传 CLAUDE_CODE_SUBAGENT_MODEL',
944
+ thirdparty: 'claude-haiku-4-5-20251001'
929
945
  };
930
946
  var CLAUDE_EFFORT_PLACEHOLDER = {
931
947
  official: '留空表示不向 SDK 传 effort',
@@ -943,13 +959,23 @@ function onClaudeApiModeChange(mode) {
943
959
  if (editThirdPartyEl) {
944
960
  editThirdPartyEl.classList.toggle('hidden', mode !== 'thirdparty');
945
961
  }
962
+ var wizardSubagentField = document.getElementById('claude-subagent-model-field');
963
+ if (wizardSubagentField) wizardSubagentField.classList.toggle('hidden', mode !== 'thirdparty');
964
+ document.querySelectorAll('[data-claude-subagent-field]').forEach(function(el){
965
+ el.classList.toggle('hidden', mode !== 'thirdparty');
966
+ });
946
967
  // 同步 model / effort 输入框的 placeholder(Wizard + Edit Modal 两处都更新)
947
968
  var modelPlaceholder = CLAUDE_MODEL_PLACEHOLDER[mode] || CLAUDE_MODEL_PLACEHOLDER.official;
969
+ var subagentModelPlaceholder = CLAUDE_SUBAGENT_MODEL_PLACEHOLDER[mode] || CLAUDE_SUBAGENT_MODEL_PLACEHOLDER.official;
948
970
  var effortPlaceholder = CLAUDE_EFFORT_PLACEHOLDER[mode] || CLAUDE_EFFORT_PLACEHOLDER.official;
949
971
  var wizardModelEl = document.getElementById('field-CHATCCC_ANTHROPIC_MODEL');
950
972
  if (wizardModelEl) wizardModelEl.placeholder = modelPlaceholder;
951
973
  var editModelEl = document.getElementById('edit-CHATCCC_ANTHROPIC_MODEL');
952
974
  if (editModelEl) editModelEl.placeholder = modelPlaceholder;
975
+ var wizardSubagentModelEl = document.getElementById('field-CHATCCC_ANTHROPIC_SUBAGENT_MODEL');
976
+ if (wizardSubagentModelEl) wizardSubagentModelEl.placeholder = subagentModelPlaceholder;
977
+ var editSubagentModelEl = document.getElementById('edit-CHATCCC_ANTHROPIC_SUBAGENT_MODEL');
978
+ if (editSubagentModelEl) editSubagentModelEl.placeholder = subagentModelPlaceholder;
953
979
  var wizardEffortEl = document.getElementById('field-CHATCCC_ANTHROPIC_EFFORT');
954
980
  if (wizardEffortEl) wizardEffortEl.placeholder = effortPlaceholder;
955
981
  var editEffortEl = document.getElementById('edit-CHATCCC_ANTHROPIC_EFFORT');
@@ -1200,7 +1226,7 @@ function isAgentEnabled(node, keys) {
1200
1226
  return false;
1201
1227
  }
1202
1228
 
1203
- var CLAUDE_FALLBACK_KEYS = ['apiKey','baseUrl','model','effort'];
1229
+ var CLAUDE_FALLBACK_KEYS = ['apiKey','baseUrl','model','subagentModel','effort'];
1204
1230
  var CURSOR_FALLBACK_KEYS = ['path','command','model'];
1205
1231
  var CODEX_FALLBACK_KEYS = ['path','command','model','effort'];
1206
1232
 
@@ -1210,6 +1236,7 @@ function renderStep2() {
1210
1236
  prefillNested('field-CLAUDE_API_KEY', c.claude.apiKey);
1211
1237
  prefillNested('field-CLAUDE_BASE_URL', c.claude.baseUrl);
1212
1238
  prefillNested('field-CHATCCC_ANTHROPIC_MODEL', c.claude.model);
1239
+ prefillNested('field-CHATCCC_ANTHROPIC_SUBAGENT_MODEL', c.claude.subagentModel);
1213
1240
  prefillNested('field-CHATCCC_ANTHROPIC_EFFORT', c.claude.effort);
1214
1241
  }
1215
1242
  // 按已有 apiKey/baseUrl 判定初始 API 模式,并相应显示/隐藏字段
@@ -1285,6 +1312,7 @@ function collectAllFields() {
1285
1312
  vars.CHATCCC_CODEX_DEFAULT_AGENT = state.defaultAgent === 'codex';
1286
1313
  if (state.agentsEnabled.claude) {
1287
1314
  AGENT_FIELDS.claude.forEach(function(key){
1315
+ if (claudeApiMode !== 'thirdparty' && key === 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL') return;
1288
1316
  var el = document.getElementById('field-' + key);
1289
1317
  if (el && el.value.trim()) vars[key] = el.value.trim();
1290
1318
  });
@@ -1345,6 +1373,7 @@ function renderStep3() {
1345
1373
  if (claudeApiMode === 'thirdparty') {
1346
1374
  lines.push('<div class="config-row"><span class="key">API Key</span><span class="val">' + (vars.CLAUDE_API_KEY ? '***已设置***' : '(未设置)') + '</span></div>');
1347
1375
  if (vars.CLAUDE_BASE_URL) lines.push('<div class="config-row"><span class="key">Base URL</span><span class="val">' + vars.CLAUDE_BASE_URL + '</span></div>');
1376
+ lines.push('<div class="config-row"><span class="key">Subagent 模型</span><span class="val">' + (vars.CHATCCC_ANTHROPIC_SUBAGENT_MODEL || '(留空)') + '</span></div>');
1348
1377
  }
1349
1378
  lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_ANTHROPIC_MODEL || '(留空)') + '</span></div>');
1350
1379
  lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_ANTHROPIC_EFFORT || '(留空)') + '</span></div>');
@@ -1542,6 +1571,8 @@ function updateDashboardUI() {
1542
1571
  document.getElementById('cfg-CLAUDE_API_KEY').textContent = claudeApiKey ? '***已设置***' : '-';
1543
1572
  document.getElementById('cfg-CLAUDE_BASE_URL').textContent = claudeBaseUrl || '-';
1544
1573
  document.getElementById('cfg-ANTHROPIC_MODEL').textContent = (c.claude && c.claude.model) || '(留空)';
1574
+ document.getElementById('cfg-row-ANTHROPIC_SUBAGENT_MODEL').style.display = isThirdPartyClaude ? '' : 'none';
1575
+ document.getElementById('cfg-ANTHROPIC_SUBAGENT_MODEL').textContent = (c.claude && c.claude.subagentModel) || '(留空)';
1545
1576
  document.getElementById('cfg-ANTHROPIC_EFFORT').textContent = (c.claude && c.claude.effort) || '(留空)';
1546
1577
  document.getElementById('cfg-CURSOR_PATH').textContent = (c.cursor && (c.cursor.path || c.cursor.command)) || '-';
1547
1578
  document.getElementById('cfg-CURSOR_MODEL').textContent = (c.cursor && c.cursor.model) || '(留空)';
@@ -1603,7 +1634,7 @@ function editSection(section) {
1603
1634
  var labelMap = {
1604
1635
  'CHATCCC_APP_ID': 'App ID', 'CHATCCC_APP_SECRET': 'App Secret',
1605
1636
  'CLAUDE_API_KEY': 'API Key', 'CLAUDE_BASE_URL': 'Base URL',
1606
- 'CHATCCC_ANTHROPIC_MODEL': '模型', 'CHATCCC_ANTHROPIC_EFFORT': 'Effort',
1637
+ 'CHATCCC_ANTHROPIC_MODEL': '模型', 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL': 'Subagent 模型', 'CHATCCC_ANTHROPIC_EFFORT': 'Effort',
1607
1638
  'CHATCCC_CURSOR_PATH': 'CLI 路径', 'CHATCCC_CURSOR_MODEL': '模型',
1608
1639
  'CHATCCC_CODEX_PATH': 'CLI 路径', 'CHATCCC_CODEX_MODEL': '模型', 'CHATCCC_CODEX_EFFORT': 'Effort'
1609
1640
  };
@@ -1630,6 +1661,7 @@ function editSection(section) {
1630
1661
  }
1631
1662
 
1632
1663
  var thirdPartyOpened = false;
1664
+ var thirdPartyClosed = false;
1633
1665
  fields.forEach(function(key){
1634
1666
  var val = state.config[key] || '';
1635
1667
  // Also check nested config
@@ -1641,6 +1673,7 @@ function editSection(section) {
1641
1673
  if (key === 'CLAUDE_API_KEY') val = state.config.claude.apiKey || '';
1642
1674
  else if (key === 'CLAUDE_BASE_URL') val = state.config.claude.baseUrl || '';
1643
1675
  else if (key === 'CHATCCC_ANTHROPIC_MODEL') val = state.config.claude.model || '';
1676
+ else if (key === 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL') val = state.config.claude.subagentModel || '';
1644
1677
  else if (key === 'CHATCCC_ANTHROPIC_EFFORT') val = state.config.claude.effort || '';
1645
1678
  } else if (section === 'cursor' && state.config.cursor) {
1646
1679
  if (key === 'CHATCCC_CURSOR_PATH') val = state.config.cursor.path || state.config.cursor.command || '';
@@ -1653,18 +1686,28 @@ function editSection(section) {
1653
1686
  }
1654
1687
  var isSecret = key.includes('SECRET') || key.includes('API_KEY');
1655
1688
  var isClaudeThirdPartyField = section === 'claude' && (key === 'CLAUDE_API_KEY' || key === 'CLAUDE_BASE_URL');
1689
+ var isClaudeSubagentField = section === 'claude' && key === 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL';
1656
1690
 
1657
1691
  if (isClaudeThirdPartyField && !thirdPartyOpened) {
1658
1692
  html += '<div id="edit-claude-thirdparty-fields"' + (claudeApiMode === 'thirdparty' ? '' : ' class="hidden"') + '>';
1659
1693
  thirdPartyOpened = true;
1660
1694
  }
1661
1695
 
1662
- html += '<div class="form-group"><label>' + (labelMap[key] || key) + '</label>';
1696
+ // 在第三方字段之后、第一个通用字段之前闭合 wrapper,
1697
+ // 避免 model / subagentModel / effort 被包裹在 hidden div 中。
1698
+ if (thirdPartyOpened && !thirdPartyClosed && !isClaudeThirdPartyField) {
1699
+ html += '</div>';
1700
+ thirdPartyClosed = true;
1701
+ }
1702
+
1703
+ var groupClass = 'form-group' + (isClaudeSubagentField && claudeApiMode !== 'thirdparty' ? ' hidden' : '');
1704
+ var subagentAttr = isClaudeSubagentField ? ' data-claude-subagent-field="1"' : '';
1705
+ html += '<div class="' + groupClass + '"' + subagentAttr + '><label>' + (labelMap[key] || key) + '</label>';
1663
1706
  html += '<input type="' + (isSecret ? 'password' : 'text') + '" id="edit-' + key + '" value="' + String(val).replace(/"/g,'&quot;') + '">';
1664
1707
  html += '</div>';
1665
1708
  });
1666
1709
 
1667
- if (thirdPartyOpened) html += '</div>';
1710
+ if (thirdPartyOpened && !thirdPartyClosed) html += '</div>';
1668
1711
 
1669
1712
  document.getElementById('edit-modal-fields').innerHTML = html;
1670
1713
  document.getElementById('edit-modal').classList.remove('hidden');
@@ -1688,6 +1731,7 @@ async function saveEdit() {
1688
1731
 
1689
1732
  var vars = {};
1690
1733
  fields.forEach(function(key){
1734
+ if (editSectionType === 'claude' && claudeApiMode !== 'thirdparty' && key === 'CHATCCC_ANTHROPIC_SUBAGENT_MODEL') return;
1691
1735
  var el = document.getElementById('edit-' + key);
1692
1736
  if (el) vars[key] = el.value.trim();
1693
1737
  });
@@ -157,7 +157,7 @@ export function createWechatAdapter(
157
157
 
158
158
  // 第10条且非最终消息:附加 claw 限制提示
159
159
  if (count === 10 && !isFinal) {
160
- text = text + "\n━━━━━━━━━━━━━━━━━━━━\n后台工作中,由于微信claw机制限制,请唤醒我才能继续发送消息";
160
+ text = text + "\n━━ 后台工作中,由于微信claw机制限制,请唤醒我才能继续发送消息";
161
161
  }
162
162
 
163
163
  // 超过10条后非最终消息不再发送(claw 限制)