lark-remote 0.5.13 → 0.5.15

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 (130) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/agent-kinds.d.ts +27 -0
  3. package/dist/agent-kinds.js +38 -0
  4. package/dist/bridge/approval-actions.d.ts +95 -0
  5. package/dist/bridge/approval-actions.js +104 -0
  6. package/dist/bridge/approval-coordinator.d.ts +13 -0
  7. package/dist/bridge/approval-coordinator.js +18 -0
  8. package/dist/bridge/final-usage.d.ts +8 -17
  9. package/dist/bridge/final-usage.js +1 -18
  10. package/dist/bridge/index.d.ts +46 -39
  11. package/dist/bridge/index.js +269 -240
  12. package/dist/bridge/queue-manager.d.ts +25 -1
  13. package/dist/bridge/queue-manager.js +179 -160
  14. package/dist/bridge/run-approval.d.ts +2 -1
  15. package/dist/bridge/run-usage.d.ts +21 -30
  16. package/dist/bridge/run-usage.js +47 -51
  17. package/dist/bridge/session-writeback.d.ts +3 -2
  18. package/dist/card/approval-render.d.ts +3 -2
  19. package/dist/card/approval-render.js +3 -2
  20. package/dist/{router → card}/card-helpers.d.ts +7 -7
  21. package/dist/{router → card}/card-helpers.js +8 -8
  22. package/dist/card/card-session.d.ts +4 -2
  23. package/dist/card/card-session.js +8 -6
  24. package/dist/card/card-shared.d.ts +1 -1
  25. package/dist/card/card-shared.js +1 -1
  26. package/dist/card/final-answer-overflow.d.ts +0 -6
  27. package/dist/card/final-answer-overflow.js +1 -1
  28. package/dist/card/run-card-session.d.ts +2 -2
  29. package/dist/card/run-card-session.js +2 -2
  30. package/dist/card/run-renderer.js +29 -30
  31. package/dist/card/run-state.d.ts +28 -50
  32. package/dist/card/run-state.js +23 -0
  33. package/dist/card/usage-format.d.ts +30 -0
  34. package/dist/{router → card}/usage-format.js +3 -10
  35. package/dist/clone.js +6 -1
  36. package/dist/config/index.d.ts +16 -20
  37. package/dist/config/index.js +0 -16
  38. package/dist/config/pi-config.js +6 -4
  39. package/dist/connector/index.d.ts +2 -4
  40. package/dist/connector/index.js +7 -11
  41. package/dist/{runner/dsh → dsh}/auth.js +1 -1
  42. package/dist/dsh/protocol.d.ts +9 -0
  43. package/dist/dsh/protocol.js +61 -0
  44. package/dist/index.js +10 -5
  45. package/dist/model-catalog.d.ts +14 -0
  46. package/dist/model-catalog.js +25 -0
  47. package/dist/platform/path.d.ts +9 -0
  48. package/dist/platform/path.js +18 -0
  49. package/dist/router/active-run-usage.d.ts +4 -0
  50. package/dist/router/active-run-usage.js +12 -0
  51. package/dist/router/commands/ls.js +1 -1
  52. package/dist/router/commands/order.js +1 -1
  53. package/dist/router/commands/resume.d.ts +5 -4
  54. package/dist/router/commands/resume.js +4 -4
  55. package/dist/router/commands/ws.js +1 -1
  56. package/dist/router/config/claude.js +2 -1
  57. package/dist/router/config/common/render.d.ts +5 -1
  58. package/dist/router/config/common/render.js +6 -13
  59. package/dist/router/config/dsh.d.ts +1 -1
  60. package/dist/router/config/index.d.ts +1 -1
  61. package/dist/router/config/types.d.ts +11 -3
  62. package/dist/router/index.d.ts +41 -11
  63. package/dist/router/index.js +169 -233
  64. package/dist/runner/agent-choices-common.d.ts +1 -1
  65. package/dist/runner/claude/runner.d.ts +20 -2
  66. package/dist/runner/claude/runner.js +2 -15
  67. package/dist/runner/codex/app-server/runner.d.ts +3 -1
  68. package/dist/runner/codex/app-server/translator.d.ts +11 -0
  69. package/dist/runner/codex/app-server/translator.js +56 -149
  70. package/dist/runner/common/acp/base-acp-runner.d.ts +19 -1
  71. package/dist/runner/common/acp/base-acp-runner.js +37 -3
  72. package/dist/runner/common/connection-based-runner.d.ts +3 -1
  73. package/dist/runner/common/jsonrpc/transport.js +17 -7
  74. package/dist/runner/dsh/client.d.ts +1 -4
  75. package/dist/runner/dsh/client.js +7 -55
  76. package/dist/runner/dsh/daemon.js +9 -2
  77. package/dist/runner/dsh/exit-cancel.js +1 -1
  78. package/dist/runner/dsh/mux-stream.js +21 -2
  79. package/dist/runner/dsh/runner.d.ts +21 -1
  80. package/dist/runner/dsh/runner.js +97 -78
  81. package/dist/runner/dsh/translator.d.ts +1 -1
  82. package/dist/runner/index.d.ts +1 -1
  83. package/dist/runner/kimi/acp/runner.d.ts +22 -1
  84. package/dist/runner/kimi/acp/runner.js +83 -60
  85. package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
  86. package/dist/runner/opencode/acp/error-monitor.js +31 -2
  87. package/dist/runner/opencode/acp/runner.d.ts +14 -1
  88. package/dist/runner/opencode/acp/runner.js +13 -2
  89. package/dist/runner/pi/rpc/runner.d.ts +2 -1
  90. package/dist/runner/probe.d.ts +1 -1
  91. package/dist/runner/probe.js +10 -9
  92. package/dist/runner/registry.d.ts +2 -1
  93. package/dist/runner/types.d.ts +20 -154
  94. package/dist/runner/types.js +4 -24
  95. package/dist/session/claude/session-index.d.ts +11 -4
  96. package/dist/session/claude/session-index.js +30 -8
  97. package/dist/session/claude/session-reader.d.ts +1 -1
  98. package/dist/session/claude/sessions.d.ts +2 -2
  99. package/dist/session/claude/sessions.js +41 -26
  100. package/dist/session/codex/rollout-reader.d.ts +1 -1
  101. package/dist/session/codex/rollout-reader.js +14 -13
  102. package/dist/session/codex/sessions.d.ts +1 -1
  103. package/dist/session/codex/sessions.js +4 -2
  104. package/dist/session/common/jsonl.js +7 -1
  105. package/dist/session/common/pagination.d.ts +2 -0
  106. package/dist/session/common/pagination.js +1 -1
  107. package/dist/session/common/recency.d.ts +6 -0
  108. package/dist/session/common/recency.js +7 -5
  109. package/dist/session/common/two-pass.d.ts +1 -1
  110. package/dist/session/common/usage-accumulator.d.ts +1 -1
  111. package/dist/session/dsh/sessions.d.ts +1 -1
  112. package/dist/session/dsh/sessions.js +37 -12
  113. package/dist/session/kimi/sessions.d.ts +1 -1
  114. package/dist/session/kimi/sessions.js +72 -27
  115. package/dist/session/opencode/sessions.d.ts +1 -1
  116. package/dist/session/opencode/sessions.js +164 -81
  117. package/dist/session/pi/sessions.d.ts +1 -1
  118. package/dist/session/pi/sessions.js +20 -7
  119. package/dist/session/registry.d.ts +2 -1
  120. package/dist/session/session-store.d.ts +1 -2
  121. package/dist/session/session-store.js +14 -7
  122. package/dist/session-contract.d.ts +178 -0
  123. package/dist/session-contract.js +9 -0
  124. package/package.json +1 -1
  125. package/dist/router/usage-format.d.ts +0 -60
  126. /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
  127. /package/dist/{runner/dsh → dsh}/home.d.ts +0 -0
  128. /package/dist/{runner/dsh → dsh}/home.js +0 -0
  129. /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
  130. /package/dist/{runner/dsh → dsh}/types.js +0 -0
@@ -62,20 +62,6 @@ export const DEFAULT_APPROVAL_TIMEOUT_MS = DEFAULTS.APPROVAL_TIMEOUT_MS;
62
62
  */
63
63
  export const DEFAULT_INBOUND_MEDIA_DIR_NAME = '.lark-remote-temp';
64
64
  export const DEFAULT_INBOUND_MEDIA_MAX_SIZE_MB = 100;
65
- /** 模型 ID → alias 映射。 */
66
- export const MODEL_ID_TO_ALIAS = {
67
- 'claude-opus-4-8': 'opus',
68
- 'claude-sonnet-4-6': 'sonnet',
69
- 'claude-haiku-4-5-20250501': 'haiku',
70
- 'claude-fable-5': 'fable',
71
- };
72
- /** alias → model ID 反向映射。 */
73
- export const MODEL_ALIAS_TO_ID = {
74
- opus: 'claude-opus-4-8',
75
- sonnet: 'claude-sonnet-4-6',
76
- haiku: 'claude-haiku-4-5-20250501',
77
- fable: 'claude-fable-5',
78
- };
79
65
  const FeishuConfigSchema = z.object({
80
66
  appId: z.string().min(1),
81
67
  appSecret: z.string().min(1),
@@ -568,8 +554,6 @@ export function getAgentConfig(config, kind) {
568
554
  export function getModelOptionsFromSettings(settingsPath) {
569
555
  if (!settingsPath)
570
556
  return [];
571
- if (!fs.existsSync(settingsPath))
572
- return [];
573
557
  try {
574
558
  const content = fs.readFileSync(settingsPath, 'utf-8');
575
559
  const settings = JSON.parse(content);
@@ -6,6 +6,7 @@
6
6
  import fs from 'node:fs';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
+ import { getLogger } from '../logger/index.js';
9
10
  /**
10
11
  * 解析 pi 配置目录。优先使用 PI_CONFIG_DIR 环境变量(测试隔离用),
11
12
  * 默认 ~/.pi/agent。每次调用时动态读取环境变量,避免模块级缓存导致
@@ -71,8 +72,9 @@ export function loadPiProviderConfig() {
71
72
  try {
72
73
  modelsData = JSON.parse(fs.readFileSync(modelsFile, 'utf-8'));
73
74
  }
74
- catch {
75
- // 文件格式错误,跳过
75
+ catch (err) {
76
+ // 解析失败 = pi 模型选项会静默消失,必须留痕(此前全文件零日志)。
77
+ getLogger().warn(`[pi-config] cannot parse ${modelsFile}: ${err.message}`);
76
78
  }
77
79
  }
78
80
  // 2. 读取 auth.json
@@ -81,8 +83,8 @@ export function loadPiProviderConfig() {
81
83
  try {
82
84
  authData = JSON.parse(fs.readFileSync(authFile, 'utf-8')) ?? {};
83
85
  }
84
- catch {
85
- // 文件格式错误,跳过
86
+ catch (err) {
87
+ getLogger().warn(`[pi-config] cannot parse ${authFile}: ${err.message}`);
86
88
  }
87
89
  }
88
90
  // 3. 从 models.json 提取 providers
@@ -7,9 +7,9 @@ import type { AppConfig } from '../config/index.js';
7
7
  * - SDK 的 cardAction dedup eventId = `card:{messageId}:{operator.openId}:{actionId}`
8
8
  * - actionId = `tag|name|option|JSON.stringify(value)`,不含时间戳/事件序号
9
9
  * - config 卡片原地更新(updateCardInPlace),用户在同一张卡上重复触发
10
- * 固定 value 的按钮(如 config.toggle):
10
+ * 固定 value 的按钮(如 config.save):
11
11
  * messageId / operator / value 三段都相同 → eventId 完全相同 → 第二次点击被
12
- * seenCache drop,toggle 不可逆(第二次点击静默失效)。
12
+ * seenCache drop,按钮不可逆(第二次点击静默失效)。
13
13
  *
14
14
  * SDK 默认 12h 太长;60s 仍会误伤连击。300ms 挡飞书瞬时重投递(<100ms 级),
15
15
  * 放过用户连击(慢点两次通常 >500ms)。代价:削弱 message 秒级重投递防护——
@@ -166,8 +166,6 @@ export declare class FeishuConnector {
166
166
  * `rawClient` 缺失(测试 mock)时回退 SDK 入口,行为退化为「看不见业务码」。
167
167
  */
168
168
  updateCard(messageId: string, card: object): Promise<void>;
169
- /** Format error for logging without causing circular serialization */
170
- private formatError;
171
169
  addReaction(messageId: string, emoji: string): Promise<void>;
172
170
  /**
173
171
  * Remove a reaction by emoji (e.g. retract the Typing indicator).
@@ -33,9 +33,9 @@ const noKeepAliveAgent = new https.Agent({ keepAlive: false });
33
33
  * - SDK 的 cardAction dedup eventId = `card:{messageId}:{operator.openId}:{actionId}`
34
34
  * - actionId = `tag|name|option|JSON.stringify(value)`,不含时间戳/事件序号
35
35
  * - config 卡片原地更新(updateCardInPlace),用户在同一张卡上重复触发
36
- * 固定 value 的按钮(如 config.toggle):
36
+ * 固定 value 的按钮(如 config.save):
37
37
  * messageId / operator / value 三段都相同 → eventId 完全相同 → 第二次点击被
38
- * seenCache drop,toggle 不可逆(第二次点击静默失效)。
38
+ * seenCache drop,按钮不可逆(第二次点击静默失效)。
39
39
  *
40
40
  * SDK 默认 12h 太长;60s 仍会误伤连击。300ms 挡飞书瞬时重投递(<100ms 级),
41
41
  * 放过用户连击(慢点两次通常 >500ms)。代价:削弱 message 秒级重投递防护——
@@ -520,7 +520,7 @@ export class FeishuConnector {
520
520
  catch (err) {
521
521
  // 抛出去前先落一条格式化日志(调用方只看到包装后的 message)。
522
522
  // 抛普通 Error 而不是原 err:axios 错误对象跨层序列化会丢信息。
523
- const errorInfo = this.formatError(err);
523
+ const errorInfo = formatErrorForLog(err);
524
524
  getLogger().error('[feishu] streamCard failed:', errorInfo);
525
525
  throw new Error(`streamCard failed: ${errorInfo}`, { cause: err });
526
526
  }
@@ -554,7 +554,7 @@ export class FeishuConnector {
554
554
  catch (err) {
555
555
  // Log the error but don't swallow it - a plain error avoids axios
556
556
  // serialization issues in the logger.
557
- const errorInfo = this.formatError(err);
557
+ const errorInfo = formatErrorForLog(err);
558
558
  getLogger().error('[feishu] updateCard failed:', errorInfo);
559
559
  throw new Error(`updateCard failed: ${errorInfo}`, { cause: err });
560
560
  }
@@ -562,16 +562,12 @@ export class FeishuConnector {
562
562
  throw new Error(`updateCard failed: Feishu rejected the card (code=${rejection.code} msg=${rejection.msg ?? ''})`, { cause: rejection });
563
563
  }
564
564
  }
565
- /** Format error for logging without causing circular serialization */
566
- formatError(err) {
567
- return formatErrorForLog(err);
568
- }
569
565
  async addReaction(messageId, emoji) {
570
566
  try {
571
567
  await this.channel.addReaction(messageId, emoji);
572
568
  }
573
569
  catch (err) {
574
- getLogger().error('[feishu] addReaction failed:', this.formatError(err));
570
+ getLogger().error('[feishu] addReaction failed:', formatErrorForLog(err));
575
571
  }
576
572
  }
577
573
  /**
@@ -583,7 +579,7 @@ export class FeishuConnector {
583
579
  await this.channel.removeReactionByEmoji(messageId, emoji);
584
580
  }
585
581
  catch (err) {
586
- getLogger().error('[feishu] removeReactionByEmoji failed:', this.formatError(err));
582
+ getLogger().error('[feishu] removeReactionByEmoji failed:', formatErrorForLog(err));
587
583
  }
588
584
  }
589
585
  /**
@@ -635,7 +631,7 @@ export class FeishuConnector {
635
631
  }
636
632
  }
637
633
  catch (err) {
638
- const errorInfo = this.formatError(err);
634
+ const errorInfo = formatErrorForLog(err);
639
635
  getLogger().error(`[feishu] ${label} failed:`, errorInfo);
640
636
  throw new Error(`${label} failed: ${errorInfo}`, { cause: err });
641
637
  }
@@ -12,7 +12,7 @@ import { createHash, createHmac } from 'node:crypto';
12
12
  import fs from 'node:fs';
13
13
  import path from 'node:path';
14
14
  import YAML from 'yaml';
15
- import { isWin32 } from '../../platform/select.js';
15
+ import { isWin32 } from '../platform/select.js';
16
16
  import { resolveDshHome } from './home.js';
17
17
  /** Credential record that owns the browser-cookie signing secret. */
18
18
  export const DSH_BROWSER_CREDENTIAL_KEY = 'client-connection/browser-session';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * DSH 远程调用的线格式助手(中立层,runner 与 session 共用)。
3
+ *
4
+ * 这两个函数原先住在 `runner/dsh/client.ts`,于是 session/dsh 为了拼一个
5
+ * `args` 信封要 import 上层 runner —— 与 AgentSessionReader 同一类反向依赖。
6
+ */
7
+ /** Extract the `token` query parameter from a pasted `dsh web` URL. */
8
+ export declare function extractDshToken(raw: string | undefined): string;
9
+ export declare function dshArgs(method: string, payload: unknown): Record<string, unknown>;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * DSH 远程调用的线格式助手(中立层,runner 与 session 共用)。
3
+ *
4
+ * 这两个函数原先住在 `runner/dsh/client.ts`,于是 session/dsh 为了拼一个
5
+ * `args` 信封要 import 上层 runner —— 与 AgentSessionReader 同一类反向依赖。
6
+ */
7
+ /** Extract the `token` query parameter from a pasted `dsh web` URL. */
8
+ export function extractDshToken(raw) {
9
+ const value = (raw ?? '').trim();
10
+ if (!value)
11
+ return '';
12
+ if (!value.includes('token=') && !value.includes('://'))
13
+ return value;
14
+ try {
15
+ const url = new URL(value.startsWith('?') ? `http://dsh.invalid/${value}` : value);
16
+ const token = url.searchParams.get('token');
17
+ if (token)
18
+ return token;
19
+ // 带 scheme 却没有 token 参数的输入是误贴的 host URL(如
20
+ // `http://127.0.0.1:3080`):整串不是合法 token,参与握手只会得到
21
+ // 指向性缺失的 401。返回空串让上游走「token 缺失」的诊断路径。
22
+ if (value.includes('://'))
23
+ return '';
24
+ return value;
25
+ }
26
+ catch {
27
+ return value;
28
+ }
29
+ }
30
+ /**
31
+ * Remote methods take named parameters. Most session methods call their single
32
+ * argument `request`; `session/list` reserves `_request`; catalog methods take
33
+ * no arguments. `commands/*` address the receiving agent by its wire field
34
+ * `agentId`, so their payload is already in wire shape (pass-through).
35
+ * The gateway rejects any other `args` shape.
36
+ */
37
+ const DSH_ARGS_KEY = {
38
+ 'session/list': '_request',
39
+ 'session/create': 'request',
40
+ 'session/prompt': 'request',
41
+ 'session/cancel': 'request',
42
+ 'session/selectModel': 'request',
43
+ 'session/projections': 'request',
44
+ 'session/page': 'request',
45
+ 'session/follow': 'request',
46
+ 'session/updateQueue': 'request',
47
+ 'job/list': 'request',
48
+ 'job/kill': 'request',
49
+ 'session/modelCatalog': null,
50
+ 'agentPresets/list': null,
51
+ 'permissionPresets/catalog': null,
52
+ };
53
+ export function dshArgs(method, payload) {
54
+ const key = DSH_ARGS_KEY[method];
55
+ if (key === null)
56
+ return {};
57
+ if (key === undefined)
58
+ return payload;
59
+ return { [key]: payload };
60
+ }
61
+ //# sourceMappingURL=protocol.js.map
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { loadConfig, getAgentConfig } from './config/index.js';
2
- import { resolveAgentChoices, } from './runner/index.js';
2
+ 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';
@@ -25,7 +25,7 @@ import { startSleepBlocker } from './platform/sleep-blocker.js';
25
25
  import { checkLatestVersion, compareVersions, runInstallLatest, formatUpdateHint, } from './update/index.js';
26
26
  import { classifyRejection } from './error-classification.js';
27
27
  import { WorkspaceStore } from './workspace/index.js';
28
- import { buildSessionHistoryCard, pickResumeCardEvents } from './router/card-helpers.js';
28
+ import { buildSessionHistoryCard, pickResumeCardEvents } from './card/card-helpers.js';
29
29
  import { newSessionButton, resumeCompactButton, agentDisplayName } from './card/card-shared.js';
30
30
  import path from 'node:path';
31
31
  import { setupMessageHandlers } from './message-handlers.js';
@@ -137,6 +137,10 @@ function initializeRunner(config, configDir, cliArgs) {
137
137
  manageProcess: () => getAgentConfig(latest(), 'dsh')?.manageProcess ?? true,
138
138
  startTimeoutMs: () => getAgentConfig(latest(), 'dsh')?.startTimeoutMs,
139
139
  });
140
+ // claude 的会话读取器与其余 agent 一样由组合根注入:runner 侧不 import session
141
+ // 层(否则 runner ↔ session 互相依赖),且 bridge 读 usage / router 列 session
142
+ // 用的是同一个实例(缓存共用)。
143
+ const claudeSessionReader = new ClaudeSessionReader();
140
144
  // Claude factory reads from configContainer (not closure) so runtime
141
145
  // config changes (model, effort, stopGraceMs) take effect after
142
146
  // bridge.setConfig() + clearRunners(). Same pattern as codex/pi/kimi.
@@ -149,6 +153,7 @@ function initializeRunner(config, configDir, cliArgs) {
149
153
  settings: cliArgs.settings,
150
154
  pidDir: configDir,
151
155
  cwd: ws,
156
+ sessionReader: claudeSessionReader,
152
157
  permissionMode: claudeConfig.permissionMode,
153
158
  idleTtlMs: claudeConfig.idleTtlMinutes != null ? claudeConfig.idleTtlMinutes * 60_000 : undefined,
154
159
  });
@@ -179,7 +184,7 @@ function initializeRunner(config, configDir, cliArgs) {
179
184
  // Register OpencodeAcpRunner (pure ACP mode, `opencode acp` JSON-RPC over stdio)
180
185
  // Register session reader (same instance as used by runner)
181
186
  const sessionReaderRegistry = new SessionReaderRegistry();
182
- sessionReaderRegistry.register('claude', new ClaudeSessionReader());
187
+ sessionReaderRegistry.register('claude', claudeSessionReader);
183
188
  sessionReaderRegistry.register('codex', codexSessionReader);
184
189
  // Register OpencodeSessionReader (CLI version)
185
190
  // Using `opencode session list` and `session export` instead of HTTP API.
@@ -436,8 +441,8 @@ async function main() {
436
441
  try {
437
442
  await connector.connect();
438
443
  }
439
- catch {
440
- logger.error('failed to connect to Feishu, check appId/appSecret');
444
+ catch (err) {
445
+ logger.error('failed to connect to Feishu, check appId/appSecret:', err);
441
446
  process.exit(1);
442
447
  }
443
448
  if (binder.isBound()) {
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Claude 模型 ID ↔ CLI alias 的双向表。
3
+ *
4
+ * 中立层(谁都不许反向依赖它):config 要写默认值、runner/claude 要把 alias 传给
5
+ * CLI、router/config 要把 alias 显示给用户。此前这张表住在 config/,于是
6
+ * 「地基层被 runner 反向 import」;挪到这里后三方共同引用同一份。
7
+ *
8
+ * 表内容来自 Claude CLI 的模型别名(alias 是 CLI 侧的稳定名字,model ID 会随
9
+ * 版本变),新增一行只改这里。
10
+ */
11
+ /** 模型 ID → alias 映射。 */
12
+ export declare const MODEL_ID_TO_ALIAS: Record<string, string>;
13
+ /** alias → model ID 反向映射。 */
14
+ export declare const MODEL_ALIAS_TO_ID: Record<string, string>;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Claude 模型 ID ↔ CLI alias 的双向表。
3
+ *
4
+ * 中立层(谁都不许反向依赖它):config 要写默认值、runner/claude 要把 alias 传给
5
+ * CLI、router/config 要把 alias 显示给用户。此前这张表住在 config/,于是
6
+ * 「地基层被 runner 反向 import」;挪到这里后三方共同引用同一份。
7
+ *
8
+ * 表内容来自 Claude CLI 的模型别名(alias 是 CLI 侧的稳定名字,model ID 会随
9
+ * 版本变),新增一行只改这里。
10
+ */
11
+ /** 模型 ID → alias 映射。 */
12
+ export const MODEL_ID_TO_ALIAS = {
13
+ 'claude-opus-4-8': 'opus',
14
+ 'claude-sonnet-4-6': 'sonnet',
15
+ 'claude-haiku-4-5-20250501': 'haiku',
16
+ 'claude-fable-5': 'fable',
17
+ };
18
+ /** alias → model ID 反向映射。 */
19
+ export const MODEL_ALIAS_TO_ID = {
20
+ opus: 'claude-opus-4-8',
21
+ sonnet: 'claude-sonnet-4-6',
22
+ haiku: 'claude-haiku-4-5-20250501',
23
+ fable: 'claude-fable-5',
24
+ };
25
+ //# sourceMappingURL=model-catalog.js.map
@@ -1,6 +1,15 @@
1
1
  export interface PathOptions {
2
2
  platform?: NodeJS.Platform;
3
3
  }
4
+ /**
5
+ * realpath 归一(macOS `/tmp` → `/private/tmp`),失败回退原样输入。
6
+ *
7
+ * 各 session reader 此前有三种口径:静默回退(opencode)、回退 + warn(kimi
8
+ * 单会话读)、裸调让一次 ENOENT 把整张会话列表清掉(kimi list)。统一成
9
+ * 「回退优先于丢结果」,失败原因交给调用方记日志(`onFailure`),因为
10
+ * ENOENT(陈旧 cwd)是正常空态、EACCES/EBUSY 才需要留痕。
11
+ */
12
+ export declare function safeRealpath(cwd: string, onFailure?: (err: unknown) => void): string;
4
13
  /**
5
14
  * 路径的「判等键」形态:把 samePath 的平台归一规则单源化,供 Map/Set 键控
6
15
  * 场景使用(按 cwd 建倒排索引时不能每次查询做 O(n) 两两比较)。不变量:
@@ -9,8 +9,26 @@
9
9
  * 全部函数接受显式 `platform` 参数(默认 currentPlatform),使 win32 语义
10
10
  * 可在任意宿主的单元测试中验证。
11
11
  */
12
+ import fs from 'node:fs';
12
13
  import path from 'node:path';
13
14
  import { currentPlatform, isWin32 } from './select.js';
15
+ /**
16
+ * realpath 归一(macOS `/tmp` → `/private/tmp`),失败回退原样输入。
17
+ *
18
+ * 各 session reader 此前有三种口径:静默回退(opencode)、回退 + warn(kimi
19
+ * 单会话读)、裸调让一次 ENOENT 把整张会话列表清掉(kimi list)。统一成
20
+ * 「回退优先于丢结果」,失败原因交给调用方记日志(`onFailure`),因为
21
+ * ENOENT(陈旧 cwd)是正常空态、EACCES/EBUSY 才需要留痕。
22
+ */
23
+ export function safeRealpath(cwd, onFailure) {
24
+ try {
25
+ return fs.realpathSync(cwd);
26
+ }
27
+ catch (err) {
28
+ onFailure?.(err);
29
+ return cwd;
30
+ }
31
+ }
14
32
  function resolveOptions(opts) {
15
33
  return { platform: opts?.platform ?? currentPlatform };
16
34
  }
@@ -0,0 +1,4 @@
1
+ import type { ActiveRunSnapshot } from '../bridge/index.js';
2
+ import type { SessionDisplayUsage } from '../card/usage-format.js';
3
+ /** 从 bridge 的活跃 run 快照提取会话卡需要的用量字段。 */
4
+ export declare function activeRunUsage(activeRun: ActiveRunSnapshot | undefined): SessionDisplayUsage | undefined;
@@ -0,0 +1,12 @@
1
+ /** 从 bridge 的活跃 run 快照提取会话卡需要的用量字段。 */
2
+ export function activeRunUsage(activeRun) {
3
+ if (!activeRun || activeRun.contextLength === undefined)
4
+ return undefined;
5
+ return {
6
+ contextLength: activeRun.contextLength,
7
+ compactCount: activeRun.compactCount,
8
+ cacheReadTokens: activeRun.cacheReadTokens,
9
+ cacheCreationTokens: activeRun.cacheCreationTokens,
10
+ };
11
+ }
12
+ //# sourceMappingURL=active-run-usage.js.map
@@ -2,7 +2,7 @@ import fs from 'node:fs';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { cardButton } from '../../card/card-shared.js';
5
- import { paginationBar, searchBar } from '../card-helpers.js';
5
+ import { paginationBar, searchBar } from '../../card/card-helpers.js';
6
6
  import { inspectPath, statPath } from '../path-validation.js';
7
7
  import { pageSlice } from '../paging.js';
8
8
  export const LS_PAGE_SIZE = 30;
@@ -1,5 +1,5 @@
1
1
  import { cardButton } from '../../card/card-shared.js';
2
- import { paginationBar } from '../card-helpers.js';
2
+ import { paginationBar } from '../../card/card-helpers.js';
3
3
  import { pageSlice } from '../paging.js';
4
4
  export const ORDER_PAGE_SIZE = 8;
5
5
  export function buildOrderCommand(args, ctx, offset, deps) {
@@ -1,11 +1,13 @@
1
1
  import type { CommandContext, CommandResult } from '../../bridge/index.js';
2
- import type { AgentKind, AgentSessionContentEvent, AgentSessionReader } from '../../runner/index.js';
3
- import type { SessionDisplayUsage } from '../usage-format.js';
2
+ import type { AgentKind } from '../../agent-kinds.js';
3
+ import type { AgentSessionContentEvent, AgentSessionReader } from '../../session-contract.js';
4
+ import type { SessionDisplayUsage } from '../../card/usage-format.js';
4
5
  export declare const AUTO_RESUME_MAX_EVENTS = 5;
5
6
  export declare const RESUME_PAGE_SIZE = 5;
6
7
  interface ResumeSessionEntry {
7
8
  cwd?: string;
8
- sessions?: Map<string, string>;
9
+ /** SessionStore 的 entry 恒有 sessions(`get()` 只会返回整条或缺席)。 */
10
+ sessions: Map<string, string>;
9
11
  }
10
12
  interface SessionDisplay {
11
13
  events: AgentSessionContentEvent[];
@@ -18,7 +20,6 @@ interface SessionDisplay {
18
20
  }
19
21
  export interface ResumeCommandDeps {
20
22
  defaultAgent: AgentKind;
21
- validAgents: readonly AgentKind[];
22
23
  getSessionEntry(userId: string): ResumeSessionEntry | undefined;
23
24
  setSessionId(userId: string, agent: AgentKind, sessionId: string, cwd: string): void;
24
25
  defaultReader(): AgentSessionReader;
@@ -1,5 +1,6 @@
1
+ import { isAgentKind } from '../../agent-kinds.js';
1
2
  import { agentDisplayName } from '../../card/card-shared.js';
2
- import { buildSessionHistoryCard, paginationBar } from '../card-helpers.js';
3
+ import { buildSessionHistoryCard, paginationBar } from '../../card/card-helpers.js';
3
4
  import { pageInfo } from '../paging.js';
4
5
  import { clampInt } from '../../common/clamp.js';
5
6
  export const AUTO_RESUME_MAX_EVENTS = 5;
@@ -14,12 +15,11 @@ function sessionPreviewLine(text, max) {
14
15
  export function buildResumeCommand(args, ctx, offset, deps) {
15
16
  const entry = deps.getSessionEntry(ctx.userId);
16
17
  const cwd = entry?.cwd;
17
- const validAgents = deps.validAgents;
18
18
  let agentKind = deps.defaultAgent;
19
19
  let limit;
20
20
  let sessionIdArg;
21
21
  if (args[0]) {
22
- if (validAgents.includes(args[0])) {
22
+ if (isAgentKind(args[0])) {
23
23
  agentKind = args[0];
24
24
  if (args[1]) {
25
25
  if (/^\d+$/.test(args[1]))
@@ -149,7 +149,7 @@ export function buildResumeCommand(args, ctx, offset, deps) {
149
149
  const elements = [
150
150
  { tag: 'div', text: { tag: 'lark_md', content: `📂 当前工作目录:\`${cwd}\`` } },
151
151
  ];
152
- const currentId = entry?.sessions?.get(agentKind) ?? '';
152
+ const currentId = entry.sessions.get(agentKind) ?? '';
153
153
  for (const session of allSessions) {
154
154
  const isCurrent = agentKind === deps.defaultAgent && session.sessionId === currentId;
155
155
  const meta = sessionMeta.get(session.sessionId);
@@ -1,5 +1,5 @@
1
1
  import { cardButton } from '../../card/card-shared.js';
2
- import { paginationBar, searchBar } from '../card-helpers.js';
2
+ import { paginationBar, searchBar } from '../../card/card-helpers.js';
3
3
  import { pageSlice } from '../paging.js';
4
4
  import { resolveDirectory } from '../path-validation.js';
5
5
  export const WS_PAGE_SIZE = 5;
@@ -4,7 +4,8 @@
4
4
  import fs from 'node:fs';
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
7
- import { MODEL_ID_TO_ALIAS, MODEL_ALIAS_TO_ID, getModelOptionsFromSettings, CLAUDE_EFFORTS, CLAUDE_PERMISSION_MODES, } from '../../config/index.js';
7
+ import { CLAUDE_EFFORTS, CLAUDE_PERMISSION_MODES, getModelOptionsFromSettings, } from '../../config/index.js';
8
+ import { MODEL_ALIAS_TO_ID, MODEL_ID_TO_ALIAS } from '../../model-catalog.js';
8
9
  /** Claude 官方 --permission-mode 中文注释(value 走 CLAUDE_PERMISSION_MODES SSOT)。 */
9
10
  const PERMISSION_MODE_LABELS = {
10
11
  default: '默认(敏感操作逐项询问)',
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Shared config card rendering logic.
3
3
  *
4
- * Builds CardKit 2.0 elements for config fields (boolean toggle, select dropdown, input).
4
+ * Builds CardKit 2.0 elements for config fields (select dropdown, input, note).
5
5
  * Used by all agent config builders and the shared tabs (idle, logging).
6
+ *
7
+ * 控件形态只有 `ConfigField.type` 里那三种,且每种在本文件都有对应 builder:
8
+ * 渲染端与处理端(router 的 `config.set` / `config.input`)必须一一对应,
9
+ * 「渲染得出按钮、点了没人处理」是禁止状态(守卫见 config-card-controls.test.ts)。
6
10
  */
7
11
  import { type AppConfig } from '../../../config/index.js';
8
12
  import type { ConfigField } from '../types.js';
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Shared config card rendering logic.
3
3
  *
4
- * Builds CardKit 2.0 elements for config fields (boolean toggle, select dropdown, input).
4
+ * Builds CardKit 2.0 elements for config fields (select dropdown, input, note).
5
5
  * Used by all agent config builders and the shared tabs (idle, logging).
6
+ *
7
+ * 控件形态只有 `ConfigField.type` 里那三种,且每种在本文件都有对应 builder:
8
+ * 渲染端与处理端(router 的 `config.set` / `config.input`)必须一一对应,
9
+ * 「渲染得出按钮、点了没人处理」是禁止状态(守卫见 config-card-controls.test.ts)。
6
10
  */
7
11
  import { getConfigValue } from '../../../config/index.js';
8
12
  import { cardButton } from '../../../card/card-shared.js';
@@ -12,12 +16,7 @@ function buildFieldElements(fields, displayConfig) {
12
16
  for (const field of fields) {
13
17
  // Get current value from displayConfig for dynamic fields
14
18
  const currentValue = getConfigValue(displayConfig, field.key);
15
- if (field.type === 'boolean') {
16
- // For boolean, use field.currentValue if set, otherwise use config value
17
- const isOn = field.currentValue !== undefined ? field.currentValue === true : currentValue === true;
18
- elements.push(buildBooleanElement(field, isOn));
19
- }
20
- else if (field.type === 'select' && field.options) {
19
+ if (field.type === 'select' && field.options) {
21
20
  elements.push(buildSelectElement(field, currentValue));
22
21
  }
23
22
  else if (field.type === 'note') {
@@ -53,12 +52,6 @@ function fieldRow(label, control) {
53
52
  ],
54
53
  };
55
54
  }
56
- function buildBooleanElement(field, isOn) {
57
- const displayVal = isOn ? '✅ 已开启' : '⚪ 已关闭';
58
- return fieldRow(field.label, cardButton(displayVal, { cmd: 'config.toggle', key: field.key }, {
59
- type: isOn ? 'primary' : 'default',
60
- }));
61
- }
62
55
  function buildSelectElement(field, currentValue) {
63
56
  // 优先使用 field.currentValue(卡片显示用覆盖值),否则使用 config 中的值
64
57
  const selectedValue = field.currentValue != null
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import type { AgentConfigCardBuilder, ConfigField } from './types.js';
19
19
  import { type AppConfig } from '../../config/index.js';
20
- import type { DshModelCatalogValue, DshPermissionCatalogValue, DshPresetListValue } from '../../runner/dsh/types.js';
20
+ import type { DshModelCatalogValue, DshPermissionCatalogValue, DshPresetListValue } from '../../dsh/types.js';
21
21
  /** DshConfigBuilder 预取只依赖的 client 方法子集。 */
22
22
  export interface DshCatalogClient {
23
23
  listModels(): Promise<DshModelCatalogValue>;
@@ -4,7 +4,7 @@
4
4
  * Each agent implements AgentConfigCardBuilder in its own file.
5
5
  * Adding a new agent = add a file + register here (no upstream changes needed).
6
6
  */
7
- import type { AgentKind } from '../../runner/types.js';
7
+ import type { AgentKind } from '../../agent-kinds.js';
8
8
  import type { AgentConfigCardBuilder } from './types.js';
9
9
  /**
10
10
  * 首次启动默认展示的 Coding Agent 顺序:Codex → Claude → OpenCode → Pi → Kimi → DSH。
@@ -16,13 +16,21 @@ export interface SelectOption {
16
16
  export interface ConfigField {
17
17
  key: string;
18
18
  label: string;
19
- type: 'boolean' | 'select' | 'input' | 'note';
19
+ /**
20
+ * 卡片上真实存在的控件形态,每种都在 `common/render.ts` 有对应 builder:
21
+ * select=下拉、input=文本框、note=纯说明行(无回调)。
22
+ *
23
+ * 没有 boolean:卡片曾留着一套「开关」控件(`config.toggle`),但全仓没有
24
+ * 任何字段声明它,2026-10-27 成套删除。原则是**卡片只放配得了的控件**,
25
+ * 实现细节与未上卡的键走 YAML(见 `buildConfigCard` 的分 tab 注释)。
26
+ */
27
+ type: 'select' | 'input' | 'note';
20
28
  /** For select type: the list of options shown in the dropdown.
21
29
  * Accepts plain strings (text=value) or {text, value} objects for
22
30
  * display-value separation (e.g. 'manual(逐项审批)' → value 'manual'). */
23
31
  options?: readonly (string | SelectOption)[];
24
32
  /** Override the currentValue shown in the card (takes priority over getConfigValue). */
25
- currentValue?: string | boolean;
33
+ currentValue?: string;
26
34
  }
27
35
  /**
28
36
  * Builder that provides config fields and handles field changes for a specific agent.
@@ -40,7 +48,7 @@ export interface AgentConfigCardBuilder {
40
48
  * Handle a field change for this agent.
41
49
  *
42
50
  * @param key - The field key (e.g. 'agents.codex.modelProvider')
43
- * @param value - The new value (string for select/input, already toggled for boolean)
51
+ * @param value - The new value (string for select/input)
44
52
  * @param config - The current pendingConfig (read-only reference for deriving dependent values)
45
53
  * @returns An array of {key, value} patches to apply to pendingConfig.
46
54
  * The caller (router) applies these via setNestedValue.