lark-remote 0.1.7 → 0.1.8

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/bridge/approval-coordinator.d.ts +15 -1
  3. package/dist/bridge/approval-coordinator.js +35 -2
  4. package/dist/bridge/index.d.ts +19 -6
  5. package/dist/bridge/index.js +25 -8
  6. package/dist/card/approval-render.js +187 -45
  7. package/dist/card/card-shared.d.ts +6 -4
  8. package/dist/card/card-shared.js +6 -4
  9. package/dist/card/run-renderer.d.ts +7 -0
  10. package/dist/card/run-renderer.js +22 -11
  11. package/dist/config/index.d.ts +24 -1
  12. package/dist/config/index.js +34 -3
  13. package/dist/index.js +28 -14
  14. package/dist/router/card-action-payload.d.ts +30 -0
  15. package/dist/router/card-action-payload.js +39 -0
  16. package/dist/router/config/claude.js +8 -3
  17. package/dist/router/config/dsh.d.ts +51 -0
  18. package/dist/router/config/dsh.js +179 -0
  19. package/dist/router/config/index.d.ts +1 -1
  20. package/dist/router/config/index.js +4 -1
  21. package/dist/router/config/types.d.ts +6 -0
  22. package/dist/router/index.d.ts +20 -1
  23. package/dist/router/index.js +113 -7
  24. package/dist/router/utils.js +12 -2
  25. package/dist/runner/agent-choices-common.js +3 -0
  26. package/dist/runner/claude/session.js +24 -5
  27. package/dist/runner/codex/app-server/protocol-types.d.ts +35 -0
  28. package/dist/runner/codex/app-server/protocol-types.js +1 -1
  29. package/dist/runner/codex/app-server/runner.d.ts +10 -0
  30. package/dist/runner/codex/app-server/runner.js +56 -1
  31. package/dist/runner/codex/app-server/translator.d.ts +7 -0
  32. package/dist/runner/codex/app-server/translator.js +45 -0
  33. package/dist/runner/common/acp/protocol-types.d.ts +33 -0
  34. package/dist/runner/common/acp/protocol-types.js +2 -0
  35. package/dist/runner/dsh/client.d.ts +95 -0
  36. package/dist/runner/dsh/client.js +418 -0
  37. package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
  38. package/dist/runner/dsh/fake-dsh-server.js +156 -0
  39. package/dist/runner/dsh/index.d.ts +3 -0
  40. package/dist/runner/dsh/index.js +4 -0
  41. package/dist/runner/dsh/runner.d.ts +57 -0
  42. package/dist/runner/dsh/runner.js +191 -0
  43. package/dist/runner/dsh/translator.d.ts +28 -0
  44. package/dist/runner/dsh/translator.js +98 -0
  45. package/dist/runner/dsh/types.d.ts +155 -0
  46. package/dist/runner/dsh/types.js +14 -0
  47. package/dist/runner/index.d.ts +1 -0
  48. package/dist/runner/index.js +1 -0
  49. package/dist/runner/kimi/acp/runner.js +69 -1
  50. package/dist/runner/kimi/acp/translator.d.ts +14 -0
  51. package/dist/runner/kimi/acp/translator.js +108 -3
  52. package/dist/runner/pi/rpc/runner.d.ts +1 -7
  53. package/dist/runner/pi/rpc/runner.js +2 -11
  54. package/dist/runner/probe.js +6 -1
  55. package/dist/runner/question-common.d.ts +26 -0
  56. package/dist/runner/question-common.js +42 -0
  57. package/dist/runner/types.d.ts +63 -10
  58. package/dist/session/claude/sessions.js +37 -15
  59. package/dist/session/dsh/index.d.ts +1 -0
  60. package/dist/session/dsh/index.js +2 -0
  61. package/dist/session/dsh/sessions.d.ts +39 -0
  62. package/dist/session/dsh/sessions.js +237 -0
  63. package/dist/session/index.d.ts +1 -0
  64. package/dist/session/index.js +1 -0
  65. package/dist/workspace/index.d.ts +4 -0
  66. package/dist/workspace/index.js +8 -1
  67. package/package.json +2 -1
@@ -25,13 +25,21 @@ function measureJson(obj) {
25
25
  /** markdownDiv('', 'notation') 的序列化字节(空内容 div 的常量开销,估算时复用)。 */
26
26
  const EMPTY_MD_DIV_BYTES = measureJson(markdownDiv('', 'notation'));
27
27
  /**
28
- * Compact 按钮可见性:普通 run(operationKind='turn')到达任意终态即可压缩。
28
+ * Compact 按钮可见性:到达任意终态即可压缩。
29
29
  * 异常退出(error/interrupted/idle_timeout)时上下文往往更大,更需要压缩后
30
30
  * 再继续,因此不再要求 resultSubtype==='success'。compaction 卡
31
31
  * (operationKind='compaction')永不显示,防止递归 Compact。
32
+ *
33
+ * 能力门控(compactSupported !== false)与执行侧 `'runCompact' in runner`
34
+ * 鸭子探测对齐:claude 走 stream-json,不发射 turn_started,operationKind
35
+ * 恒 undefined——若以 operationKind==='turn' 作门控,claude 终态卡永远拿
36
+ * 不到按钮(2026-08-19 缺陷)。compaction 卡的「防递归」仍由 operationKind
37
+ * 保证,两者是不同质的判断。
32
38
  */
33
- function shouldShowCompactButton(state) {
34
- if (state.operationKind !== 'turn')
39
+ function shouldShowCompactButton(state, options) {
40
+ if (state.operationKind === 'compaction')
41
+ return false;
42
+ if (options.compactSupported === false)
35
43
  return false;
36
44
  return (state.terminal === 'done' ||
37
45
  state.terminal === 'error' ||
@@ -201,15 +209,15 @@ const EXTREME_TIER = {
201
209
  textBytes: DEGRADED_TEXT_BYTES,
202
210
  };
203
211
  /** Bottom action row: stop (if running) + compact (if applicable) + new session (always). */
204
- function actionRow(state) {
212
+ function actionRow(state, options) {
205
213
  const actionButtons = [];
206
214
  // finalizing 也显示停止按钮(进程未退出,用户可 /stop)
207
215
  const showStop = state.terminal === 'running' || state.terminal === 'finalizing';
208
216
  if (showStop) {
209
217
  actionButtons.push(stopButton(state.runId));
210
218
  }
211
- // Compact 按钮:普通 turn 到达任意终态即可压缩(含异常退出),compaction 卡除外
212
- if (shouldShowCompactButton(state)) {
219
+ // Compact 按钮:到达任意终态即可压缩(含异常退出),compaction 卡除外
220
+ if (shouldShowCompactButton(state, options)) {
213
221
  actionButtons.push(compactButton(state.runId));
214
222
  }
215
223
  actionButtons.push(newSessionButton());
@@ -323,7 +331,7 @@ function buildFallbackElements(state, options, tier) {
323
331
  elements.push(markdownDiv('_暂无输出_'));
324
332
  }
325
333
  elements.push(...buildSummaryContent(state));
326
- elements.push(...actionRow(state));
334
+ elements.push(...actionRow(state, options));
327
335
  elements.push(...approvalArea(state));
328
336
  return elements;
329
337
  }
@@ -374,7 +382,7 @@ function statusRow(state) {
374
382
  * stop while running/finalizing). No block content is rendered, so the size is
375
383
  * bounded by static structure regardless of input — closing the safety-net gap.
376
384
  */
377
- function buildSkeletonElements(state) {
385
+ function buildSkeletonElements(state, options) {
378
386
  const elements = [];
379
387
  elements.push(statusRow(state));
380
388
  elements.push(markdownDiv('_⚠️ 输出过大且含大量转义字符,已省略全部内容以避免超限;完整内容请查日志_'));
@@ -382,7 +390,7 @@ function buildSkeletonElements(state) {
382
390
  elements.push(...buildSummaryContent(state));
383
391
  // Bottom action row: stop (if running/finalizing) + compact (if applicable) + new session (always).
384
392
  // Degraded paths must keep the action buttons reachable (design constraint).
385
- elements.push(...actionRow(state));
393
+ elements.push(...actionRow(state, options));
386
394
  return elements;
387
395
  }
388
396
  /**
@@ -450,6 +458,9 @@ export function estimateCardBytes(state, options = {}, shared) {
450
458
  if (state.terminal === 'running' || state.terminal === 'finalizing') {
451
459
  actionButtons.push(stopButton(state.runId));
452
460
  }
461
+ if (shouldShowCompactButton(state, options)) {
462
+ actionButtons.push(compactButton(state.runId));
463
+ }
453
464
  actionButtons.push(newSessionButton());
454
465
  total += measureJson([
455
466
  { tag: 'div', text: { content: '‎', tag: 'lark_md' } },
@@ -551,7 +562,7 @@ export function renderRunCard(state, options = {}) {
551
562
  statusRow(state),
552
563
  ...buildChronologicalContent(state, options, prepared, groups),
553
564
  ...buildSummaryContent(state),
554
- ...actionRow(state),
565
+ ...actionRow(state, options),
555
566
  ...approvalArea(state),
556
567
  ];
557
568
  const card = assembleRunCard(state, options, elements);
@@ -573,7 +584,7 @@ export function renderRunCard(state, options = {}) {
573
584
  if (Buffer.byteLength(JSON.stringify(extremeCard), 'utf8') <= CARD_BUDGET_BYTES) {
574
585
  return extremeCard;
575
586
  }
576
- const skeletonCard = assembleRunCard(state, options, buildSkeletonElements(state));
587
+ const skeletonCard = assembleRunCard(state, options, buildSkeletonElements(state, options));
577
588
  // skeleton 卡是静态结构,理论上必 ≤28KB;保留兜底断言以防未来结构膨胀
578
589
  if (Buffer.byteLength(JSON.stringify(skeletonCard), 'utf8') <= CARD_BUDGET_BYTES) {
579
590
  return skeletonCard;
@@ -6,6 +6,8 @@ export declare const CLAUDE_EFFORTS: readonly ["low", "medium", "high", "xhigh",
6
6
  * Claude 官方 --permission-mode 枚举(claude --help 输出,2026-08 实测)。
7
7
  * 额外支持 'default':Claude settings.json 的官方未设置值,等价于不传
8
8
  * --permission-mode(交互式默认:高风险工具逐个询问)。
9
+ * 'manual' 是 'default' 的别名(claude CLI 等价),schema 保留以向后兼容旧配置,
10
+ * 但 UI 下拉不展示(见 router/config/claude.ts)。
9
11
  */
10
12
  export declare const CLAUDE_PERMISSION_MODES: readonly ["default", "acceptEdits", "auto", "bypassPermissions", "manual", "dontAsk", "plan"];
11
13
  /** Valid thinking levels for Pi */
@@ -17,7 +19,7 @@ export declare const PI_THINKING_LEVELS: readonly ["off", "minimal", "low", "med
17
19
  */
18
20
  export declare const DEFAULT_STOP_GRACE_MS: 5000;
19
21
  /** Codex app-server turn idle timeout default (minutes). */
20
- export declare const DEFAULT_TURN_IDLE_TIMEOUT_MINUTES: 10;
22
+ export declare const DEFAULT_TURN_IDLE_TIMEOUT_MINUTES: 30;
21
23
  /** 入站媒体默认配置(schema 默认与 connector 兜底共用,避免双源漂移)。 */
22
24
  export declare const DEFAULT_INBOUND_MEDIA_DIR_NAME = ".lark-remote-temp";
23
25
  export declare const DEFAULT_INBOUND_MEDIA_MAX_SIZE_MB = 50;
@@ -112,6 +114,15 @@ declare const KimiConfigSchema: z.ZodObject<{
112
114
  turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
113
115
  }, z.core.$strip>>;
114
116
  }, z.core.$strip>;
117
+ /** DSH (DeepSeek Harness) Web Host default base URL. */
118
+ export declare const DEFAULT_DSH_HOST = "http://127.0.0.1:3080";
119
+ /** DSH (DeepSeek Harness) Web Host connection config. */
120
+ declare const DshConfigSchema: z.ZodObject<{
121
+ host: z.ZodDefault<z.ZodString>;
122
+ agentPreset: z.ZodOptional<z.ZodString>;
123
+ model: z.ZodOptional<z.ZodString>;
124
+ reasoningEffort: z.ZodOptional<z.ZodString>;
125
+ }, z.core.$strip>;
115
126
  export declare const AppConfigSchema: z.ZodObject<{
116
127
  feishu: z.ZodObject<{
117
128
  appId: z.ZodString;
@@ -201,6 +212,12 @@ export declare const AppConfigSchema: z.ZodObject<{
201
212
  turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
202
213
  }, z.core.$strip>>;
203
214
  }, z.core.$strip>>;
215
+ dsh: z.ZodOptional<z.ZodObject<{
216
+ host: z.ZodDefault<z.ZodString>;
217
+ agentPreset: z.ZodOptional<z.ZodString>;
218
+ model: z.ZodOptional<z.ZodString>;
219
+ reasoningEffort: z.ZodOptional<z.ZodString>;
220
+ }, z.core.$strip>>;
204
221
  }, z.core.$strip>>;
205
222
  agentChoices: z.ZodOptional<z.ZodObject<{
206
223
  codex: z.ZodOptional<z.ZodObject<{
@@ -220,6 +237,9 @@ export declare const AppConfigSchema: z.ZodObject<{
220
237
  model: z.ZodOptional<z.ZodString>;
221
238
  thinkingEffort: z.ZodOptional<z.ZodString>;
222
239
  }, z.core.$strip>>;
240
+ dsh: z.ZodOptional<z.ZodObject<{
241
+ host: z.ZodOptional<z.ZodString>;
242
+ }, z.core.$strip>>;
223
243
  }, z.core.$strip>>;
224
244
  idle: z.ZodDefault<z.ZodObject<{
225
245
  watchdogMinutes: z.ZodDefault<z.ZodNumber>;
@@ -248,6 +268,7 @@ export declare const AppConfigSchema: z.ZodObject<{
248
268
  opencode: "opencode";
249
269
  pi: "pi";
250
270
  kimi: "kimi";
271
+ dsh: "dsh";
251
272
  }>>;
252
273
  checkUpdateOnStartup: z.ZodDefault<z.ZodBoolean>;
253
274
  }, z.core.$strip>;
@@ -299,12 +320,14 @@ type CodexConfig = z.infer<typeof CodexConfigSchema>;
299
320
  type OpencodeConfig = z.infer<typeof OpencodeConfigSchema>;
300
321
  type PiConfig = z.infer<typeof PiConfigSchema>;
301
322
  type KimiConfig = z.infer<typeof KimiConfigSchema>;
323
+ export type DshConfig = z.infer<typeof DshConfigSchema>;
302
324
  /** Get configuration for a specific agent. Falls back to defaults if not configured. */
303
325
  export declare function getAgentConfig(config: AppConfig, kind: 'claude'): ClaudeConfig;
304
326
  export declare function getAgentConfig(config: AppConfig, kind: 'codex'): CodexConfig | undefined;
305
327
  export declare function getAgentConfig(config: AppConfig, kind: 'opencode'): OpencodeConfig | undefined;
306
328
  export declare function getAgentConfig(config: AppConfig, kind: 'pi'): PiConfig | undefined;
307
329
  export declare function getAgentConfig(config: AppConfig, kind: 'kimi'): KimiConfig | undefined;
330
+ export declare function getAgentConfig(config: AppConfig, kind: 'dsh'): DshConfig | undefined;
308
331
  /**
309
332
  * 从 Claude settings.json 的 env 中读取所有以 MODEL 结尾的环境变量值,
310
333
  * 返回去重后的模型 ID 列表。
@@ -13,6 +13,8 @@ export const CLAUDE_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
13
13
  * Claude 官方 --permission-mode 枚举(claude --help 输出,2026-08 实测)。
14
14
  * 额外支持 'default':Claude settings.json 的官方未设置值,等价于不传
15
15
  * --permission-mode(交互式默认:高风险工具逐个询问)。
16
+ * 'manual' 是 'default' 的别名(claude CLI 等价),schema 保留以向后兼容旧配置,
17
+ * 但 UI 下拉不展示(见 router/config/claude.ts)。
16
18
  */
17
19
  export const CLAUDE_PERMISSION_MODES = [
18
20
  'default',
@@ -30,7 +32,9 @@ const DEFAULTS = {
30
32
  CLAUDE_MODEL: 'claude-opus-4-8',
31
33
  STOP_GRACE_MS: 5000,
32
34
  IDLE_WATCHDOG_MINUTES: 15,
33
- TURN_IDLE_TIMEOUT_MINUTES: 10,
35
+ /** ACP/app-server turn idle 超时默认 30 分钟:Kimi/opencode/codex 常因
36
+ * 等待 SubAgent 或长思考长时间静默,10 分钟会误杀正常进行中的 run。 */
37
+ TURN_IDLE_TIMEOUT_MINUTES: 30,
34
38
  /** 审批超时默认 5 分钟(对齐 codex 红线:勿随意改短)。 */
35
39
  APPROVAL_TIMEOUT_MS: 5 * 60 * 1000,
36
40
  /** Claude 会话级空闲回收默认 30 分钟(对齐 codex appServer.idleTtlMs)。 */
@@ -157,6 +161,27 @@ const KimiConfigSchema = z.object({
157
161
  })
158
162
  .optional(),
159
163
  });
164
+ /** DSH (DeepSeek Harness) Web Host default base URL. */
165
+ export const DEFAULT_DSH_HOST = 'http://127.0.0.1:3080';
166
+ /** DSH (DeepSeek Harness) Web Host connection config. */
167
+ const DshConfigSchema = z.object({
168
+ /** DSH Web Host base URL (no auth, local). */
169
+ host: z.string().url().default(DEFAULT_DSH_HOST),
170
+ /**
171
+ * Session preset(agentPreset)。preset 在 session 创建时固定,中途切换会被
172
+ * 服务端拒绝(agent-preset-conflict)。留空 = 跟随服务端默认(不传 agentPreset)。
173
+ * 枚举不在此硬编码:preset 清单随 DSH profile bundle 变化,合法性由配置卡下拉
174
+ * 控制 + 运行时 DSH API 报错兜底。
175
+ */
176
+ agentPreset: z.string().optional(),
177
+ /** 模型 ID。留空 = 跟随服务端默认。 */
178
+ model: z.string().optional(),
179
+ /**
180
+ * 推理强度(off / low / high / max,跟随 llm.models 实际档位)。留空 = 跟随
181
+ * 服务端默认。枚举不硬编码(理由同上 agentPreset)。
182
+ */
183
+ reasoningEffort: z.string().optional(),
184
+ });
160
185
  /** 首次启动的默认 agents 配置:codex 默认 app-server / on-request / workspace-write。 */
161
186
  const DEFAULT_AGENTS_CONFIG = {
162
187
  codex: CodexConfigSchema.parse({}),
@@ -167,6 +192,7 @@ const AgentsConfigSchema = z.object({
167
192
  opencode: OpencodeConfigSchema.optional(),
168
193
  pi: PiConfigSchema.optional(),
169
194
  kimi: KimiConfigSchema.optional(),
195
+ dsh: DshConfigSchema.optional(),
170
196
  });
171
197
  const IdleConfigSchema = z.object({
172
198
  /**
@@ -220,6 +246,11 @@ const AgentChoicesSchema = z.object({
220
246
  thinkingEffort: z.string().optional(),
221
247
  })
222
248
  .optional(),
249
+ dsh: z
250
+ .object({
251
+ host: z.string().optional(),
252
+ })
253
+ .optional(),
223
254
  });
224
255
  export const AppConfigSchema = z.object({
225
256
  feishu: FeishuConfigSchema,
@@ -235,7 +266,7 @@ export const AppConfigSchema = z.object({
235
266
  /** 入站媒体(图片/文件)落盘配置。 */
236
267
  inboundMedia: InboundMediaConfigSchema.default(InboundMediaConfigSchema.parse({})),
237
268
  /** Default agent for new runs. */
238
- defaultAgent: z.enum(['claude', 'codex', 'opencode', 'pi', 'kimi']).default('claude'),
269
+ defaultAgent: z.enum(['claude', 'codex', 'opencode', 'pi', 'kimi', 'dsh']).default('claude'),
239
270
  /** Check for updates on bridge startup (default: false). */
240
271
  checkUpdateOnStartup: z.boolean().default(false),
241
272
  });
@@ -288,7 +319,7 @@ export function loadConfig(configPath) {
288
319
  * Agent key 前缀,这些前缀的 key 会映射到 `agents.<key>` 路径下。
289
320
  * `claude.` 保持顶层(不映射到 agents.claude)。
290
321
  */
291
- const AGENT_PREFIXES = ['pi', 'codex', 'opencode', 'kimi'];
322
+ const AGENT_PREFIXES = ['pi', 'codex', 'opencode', 'kimi', 'dsh'];
292
323
  /**
293
324
  * 将卡片短 key 映射到 schema 期望的路径:
294
325
  * pi.xxx → agents.pi.xxx
package/dist/index.js CHANGED
@@ -3,13 +3,14 @@ import { resolveAgentChoices, } from './runner/index.js';
3
3
  import { ensureConfig } from './config/wizard.js';
4
4
  import { parseCliArgs, resolveConfigDir, setConfigDir, printHelp, printVersion, } from './config/dir.js';
5
5
  import { FeishuConnector } from './connector/index.js';
6
- import { ClaudeRunner, CodexAppServerRunner, OpencodeAcpRunner, PiRpcRunner, KimiAcpRunner, } from './runner/index.js';
6
+ import { ClaudeRunner, CodexAppServerRunner, OpencodeAcpRunner, PiRpcRunner, KimiAcpRunner, DshRunner, } from './runner/index.js';
7
7
  import { AgentRegistry } from './runner/registry.js';
8
8
  import { probeAllAgents } from './runner/probe.js';
9
9
  import { SessionReaderRegistry, SessionStore } from './session/index.js';
10
- import { ClaudeSessionReader, CodexSessionReader, OpencodeSessionReader, PiSessionReader, KimiSessionReader, } from './session/index.js';
10
+ import { ClaudeSessionReader, CodexSessionReader, OpencodeSessionReader, PiSessionReader, KimiSessionReader, DshSessionReader, } from './session/index.js';
11
11
  import { CommandRouter, isImmediateAction } from './router/index.js';
12
12
  import { dispatchOrderExecForQueue } from './router/order-exec-dispatch.js';
13
+ import { buildCardActionFullValue } from './router/card-action-payload.js';
13
14
  import { Bridge } from './bridge/index.js';
14
15
  import { initLogger, getLogger } from './logger/index.js';
15
16
  import { StartupContactStore, sendStartupHello } from './startup-contact.js';
@@ -238,12 +239,33 @@ function initializeRunner(config, configDir, cliArgs) {
238
239
  });
239
240
  });
240
241
  sessionReaderRegistry.register('kimi', kimiSessionReader);
242
+ // Register DshRunner (HTTP-only DSH Web Host agent) and DshSessionReader.
243
+ const dshSessionReader = new DshSessionReader({
244
+ host: getAgentConfig(config, 'dsh')?.host,
245
+ });
246
+ agentRegistry.register('dsh', (_ws) => {
247
+ // Read latest config from container so /config host changes take effect
248
+ // after bridge.setConfig() + clearRunners().
249
+ const container = agentRegistry.getConfigContainer();
250
+ const latestConfig = container?.current ?? config;
251
+ const dshConf = getAgentConfig(latestConfig, 'dsh');
252
+ return new DshRunner({
253
+ kind: 'dsh',
254
+ sessionReader: dshSessionReader,
255
+ host: dshConf?.host,
256
+ agentPreset: dshConf?.agentPreset,
257
+ model: dshConf?.model,
258
+ reasoningEffort: dshConf?.reasoningEffort,
259
+ });
260
+ });
261
+ sessionReaderRegistry.register('dsh', dshSessionReader);
241
262
  // 2026-07-17: 注册所有 agent 的显示名,实现单点真相
242
263
  agentRegistry.registerDisplayName('claude', 'Claude');
243
264
  agentRegistry.registerDisplayName('codex', 'Codex');
244
265
  agentRegistry.registerDisplayName('opencode', 'Opencode');
245
266
  agentRegistry.registerDisplayName('pi', 'Pi');
246
267
  agentRegistry.registerDisplayName('kimi', 'Kimi');
268
+ agentRegistry.registerDisplayName('dsh', 'DSH');
247
269
  // 设置全局 registry,供 agentDisplayName 等全局函数使用
248
270
  AgentRegistry.setGlobalInstance(agentRegistry);
249
271
  // Probe agent CLI availability (fire-and-forget, populates cache for /config card).
@@ -493,18 +515,10 @@ function setupMessageHandlers(connector, router, bridge, sessionStore, workspace
493
515
  }
494
516
  const isImmediate = isImmediateAction(actionValue.cmd);
495
517
  const workspace = sessionStore.getCwd(userId) ?? '';
496
- // Build full value object — spread all fields from actionValue,
497
- // then override option/formValue/inputValue from action for CardKit 2.0 compatibility.
498
- // 2026-07-04: inputValue 来自 CardKit 2.0 input 组件自带提交图标触发 callback
499
- const fullValue = {
500
- ...actionValue,
501
- option: action.action.option,
502
- formValue: action.action.formValue,
503
- // CardKit 2.0 input ✓ 提交图标:输入值走 raw.action.input_value(SDK normalizer
504
- // 丢弃 action.action.input_value,需 connector includeRawEvent: true)。
505
- inputValue: action.raw?.action?.input_value ??
506
- action.action.input_value,
507
- };
518
+ // Build full value object — spread all fields from actionValue, then merge
519
+ // component out-of-band fields (option/formValue/inputValue/options) where
520
+ // present. See card-action-payload.ts for the component-type contract.
521
+ const fullValue = buildCardActionFullValue(actionValue, action);
508
522
  // queue.input / config.save / approval.respond / approval.toggle /
509
523
  // approval.answer 系列返回 CardActionResponse toast 给点击用户即时反馈。
510
524
  // 必须直接返回值 -- enqueueImmediate / enqueueConfigAction 是
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Card action payload assembly (entry layer).
3
+ *
4
+ * Feishu card callbacks arrive as a normalized `CardActionEvent` whose
5
+ * `action` only carries `value/tag/name/option/formValue` — the
6
+ * `@larksuite/channel` normalizer drops everything else (e.g. `input_value`,
7
+ * multi-select `options`). This module rebuilds the full payload that the
8
+ * router consumes, merging the button's behavior `value` with component
9
+ * out-of-band fields.
10
+ *
11
+ * Extracted from src/index.ts so the entry transform is behavior-testable
12
+ * (the main() wiring itself cannot be injected, see index-wiring.test.ts).
13
+ */
14
+ import type { CardActionEvent } from '@larksuite/channel';
15
+ import type { CardActionPayload } from './index.js';
16
+ /**
17
+ * Build the full card action payload for router.handleCardAction.
18
+ *
19
+ * - `option`/`formValue`: select/form components deliver the user's choice
20
+ * out-of-band in `action.option`/`action.formValue`; button callbacks carry
21
+ * it in the behavior `value` instead (and `action.option` is absent). The
22
+ * behavior-value field must survive for buttons, so the component field
23
+ * only wins when it is actually present.
24
+ * - `inputValue`: CardKit 2.0 input submit-icon callbacks deliver the text in
25
+ * the raw event (`action.raw.action.input_value`); the SDK normalizer drops
26
+ * `action.action.input_value`.
27
+ * - `options`: reserved for multi-select components (future agent support);
28
+ * the SDK normalizer drops `action.options`, so it is read from raw.
29
+ */
30
+ export declare function buildCardActionFullValue(actionValue: CardActionPayload, action: CardActionEvent): CardActionPayload;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Card action payload assembly (entry layer).
3
+ *
4
+ * Feishu card callbacks arrive as a normalized `CardActionEvent` whose
5
+ * `action` only carries `value/tag/name/option/formValue` — the
6
+ * `@larksuite/channel` normalizer drops everything else (e.g. `input_value`,
7
+ * multi-select `options`). This module rebuilds the full payload that the
8
+ * router consumes, merging the button's behavior `value` with component
9
+ * out-of-band fields.
10
+ *
11
+ * Extracted from src/index.ts so the entry transform is behavior-testable
12
+ * (the main() wiring itself cannot be injected, see index-wiring.test.ts).
13
+ */
14
+ /**
15
+ * Build the full card action payload for router.handleCardAction.
16
+ *
17
+ * - `option`/`formValue`: select/form components deliver the user's choice
18
+ * out-of-band in `action.option`/`action.formValue`; button callbacks carry
19
+ * it in the behavior `value` instead (and `action.option` is absent). The
20
+ * behavior-value field must survive for buttons, so the component field
21
+ * only wins when it is actually present.
22
+ * - `inputValue`: CardKit 2.0 input submit-icon callbacks deliver the text in
23
+ * the raw event (`action.raw.action.input_value`); the SDK normalizer drops
24
+ * `action.action.input_value`.
25
+ * - `options`: reserved for multi-select components (future agent support);
26
+ * the SDK normalizer drops `action.options`, so it is read from raw.
27
+ */
28
+ export function buildCardActionFullValue(actionValue, action) {
29
+ return {
30
+ ...actionValue,
31
+ option: action.action.option ?? actionValue.option,
32
+ formValue: action.action.formValue ?? actionValue.formValue,
33
+ options: action.raw?.action?.options ??
34
+ actionValue.options,
35
+ inputValue: action.raw?.action?.input_value ??
36
+ action.action.input_value,
37
+ };
38
+ }
39
+ //# sourceMappingURL=card-action-payload.js.map
@@ -11,11 +11,14 @@ const PERMISSION_MODE_LABELS = {
11
11
  acceptEdits: '自动接受文件编辑',
12
12
  auto: '自动放行低风险操作',
13
13
  bypassPermissions: '跳过所有权限检查',
14
- manual: '手动审批(default 的别名)',
15
14
  dontAsk: '仅预先批准的工具',
16
15
  plan: '计划模式(只读探索)',
17
16
  };
18
- const PERMISSION_MODE_OPTIONS = CLAUDE_PERMISSION_MODES.map((m) => ({
17
+ /**
18
+ * 下拉选项排除 manual:manual 是 default 的别名(claude CLI 等价),
19
+ * 只保留 default 一个,避免下拉出现两个等价项。schema 仍接受 manual(旧配置向后兼容)。
20
+ */
21
+ const PERMISSION_MODE_OPTIONS = CLAUDE_PERMISSION_MODES.filter((m) => m !== 'manual').map((m) => ({
19
22
  text: `${m}(${PERMISSION_MODE_LABELS[m] ?? ''})`,
20
23
  value: m,
21
24
  }));
@@ -78,12 +81,14 @@ export class ClaudeConfigBuilder {
78
81
  // 权限模式(Claude 官方 --permission-mode 枚举;bypassPermissions 为 schema 默认,
79
82
  // 保持旧行为无审批卡,配置为其他值后激活交互式审批)
80
83
  const currentPermissionMode = displayConfig.claude?.permissionMode ?? 'bypassPermissions';
84
+ // manual 是 default 的别名:旧配置按 default 显示(下拉无 manual 项,避免空选)
85
+ const displayedPermissionMode = currentPermissionMode === 'manual' ? 'default' : currentPermissionMode;
81
86
  fields.push({
82
87
  key: 'claude.permissionMode',
83
88
  label: '权限模式',
84
89
  type: 'select',
85
90
  options: PERMISSION_MODE_OPTIONS,
86
- currentValue: currentPermissionMode,
91
+ currentValue: displayedPermissionMode,
87
92
  });
88
93
  return fields;
89
94
  }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * DshConfigBuilder — /config card fields for the DSH agent.
3
+ *
4
+ * DSH (DeepSeek Harness) is an HTTP-only agent. Beyond the Web Host base URL it
5
+ * exposes three runtime-configurable knobs — preset, model, reasoning effort.
6
+ *
7
+ * 清单来源分两期:
8
+ * - 动态清单(当前实现):打开 /config 前通过 `llm.models` / `agentPreset.list`
9
+ * 预取(Router 调 `prefetch()`),失败/离线回退下方固定兜底清单,卡不依赖
10
+ * DSH 服务在线。
11
+ * - 三个可选字段用空字符串哨兵表示「跟随服务端默认」:select 的 value `''`
12
+ * 在 handleFieldChange 中被转成 undefined patch(router 删除键),config.yaml
13
+ * 不写该键 = 服务端默认。
14
+ */
15
+ import type { AgentConfigCardBuilder, ConfigField } from './types.js';
16
+ import { type AppConfig } from '../../config/index.js';
17
+ import type { DshModelCatalogValue, DshPresetListValue } from '../../runner/dsh/types.js';
18
+ /** DshConfigBuilder 预取只依赖的 client 方法子集。 */
19
+ export interface DshCatalogClient {
20
+ listModels(): Promise<DshModelCatalogValue>;
21
+ listPresets(): Promise<DshPresetListValue>;
22
+ }
23
+ export declare class DshConfigBuilder implements AgentConfigCardBuilder {
24
+ /** 最近一次预取用的 host;host 变更时缓存失效(下一张卡重新拉取)。 */
25
+ private prefetchHost;
26
+ private modelCatalog;
27
+ private presets;
28
+ /** 可注入 client 工厂(测试用);默认生产实现。 */
29
+ private readonly clientFactory;
30
+ constructor(clientFactory?: (baseUrl: string) => DshCatalogClient);
31
+ /** 异步预取模型/预设目录(§4.2 二期动态清单)。失败/离线静默回退固定兜底清单,
32
+ * 卡不依赖 DSH 服务在线;同一 host 只拉一次,避免每次刷卡重复请求。 */
33
+ prefetch(host?: string): Promise<void>;
34
+ /** 同步注入 catalog/presets(测试用,替代异步 prefetch;标记 prefetchHost 使
35
+ * 后续 prefetch 短路,避免测试误触发网络请求)。 */
36
+ setCatalog(catalog: DshModelCatalogValue, presets?: DshPresetListValue): void;
37
+ /** 展平 llm.models 各 provider group 的模型 id;无 catalog 时回退固定清单。 */
38
+ private modelOptions;
39
+ /** preset 清单;无 presets 时回退固定清单。显示名优先用 preset.yml 的 name。 */
40
+ private presetOptions;
41
+ /** 模型在 catalog 中声明的推理档位;无 catalog/模型不在目录时回退固定档位。 */
42
+ private effortOptionsForModel;
43
+ buildFields(displayConfig: AppConfig): ConfigField[];
44
+ handleFieldChange(key: string, value: unknown, config: AppConfig): Array<{
45
+ key: string;
46
+ value: unknown;
47
+ }>;
48
+ /** 切到 newModel 后的档位补丁:当前档位仍被新模型支持 → 不产出;否则用新模型
49
+ * 目录中位;无目录/空列表 → undefined(清空 = 跟随服务端默认)。 */
50
+ private effortPatchForModel;
51
+ }