lark-remote 0.1.6 → 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 (216) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/README.en.md +9 -1
  3. package/README.md +14 -2
  4. package/dist/bridge/approval-coordinator.d.ts +71 -34
  5. package/dist/bridge/approval-coordinator.js +225 -41
  6. package/dist/bridge/inbound-media.d.ts +62 -0
  7. package/dist/bridge/inbound-media.js +282 -0
  8. package/dist/bridge/index.d.ts +97 -13
  9. package/dist/bridge/index.js +227 -87
  10. package/dist/bridge/queue-manager.d.ts +2 -1
  11. package/dist/bridge/queue-manager.js +1 -0
  12. package/dist/card/approval-render.js +294 -36
  13. package/dist/card/bash-card-session.d.ts +2 -24
  14. package/dist/card/bash-card-session.js +6 -73
  15. package/dist/card/bash-renderer.js +2 -3
  16. package/dist/card/card-budget.js +29 -75
  17. package/dist/card/card-session.d.ts +48 -0
  18. package/dist/card/card-session.js +86 -0
  19. package/dist/card/card-shared.d.ts +7 -16
  20. package/dist/card/card-shared.js +7 -30
  21. package/dist/card/run-card-session.d.ts +2 -55
  22. package/dist/card/run-card-session.js +6 -104
  23. package/dist/card/run-renderer.d.ts +7 -0
  24. package/dist/card/run-renderer.js +116 -238
  25. package/dist/card/run-state.d.ts +13 -0
  26. package/dist/card/run-state.js +25 -16
  27. package/dist/card/text-truncate.d.ts +13 -12
  28. package/dist/card/text-truncate.js +13 -63
  29. package/dist/common/fs.d.ts +8 -0
  30. package/dist/common/fs.js +17 -0
  31. package/dist/common/guards.d.ts +0 -20
  32. package/dist/common/guards.js +0 -43
  33. package/dist/common/sleep.d.ts +2 -0
  34. package/dist/common/sleep.js +5 -0
  35. package/dist/common/truncate.d.ts +39 -0
  36. package/dist/common/truncate.js +92 -0
  37. package/dist/config/codex-config.d.ts +1 -1
  38. package/dist/config/codex-config.js +1 -1
  39. package/dist/config/index.d.ts +112 -24
  40. package/dist/config/index.js +139 -37
  41. package/dist/connector/index.d.ts +69 -2
  42. package/dist/connector/index.js +107 -1
  43. package/dist/index.js +150 -118
  44. package/dist/instance-lock.js +4 -12
  45. package/dist/order/alias-resolve.d.ts +13 -0
  46. package/dist/order/alias-resolve.js +22 -0
  47. package/dist/order/alias-store.d.ts +38 -0
  48. package/dist/order/alias-store.js +106 -0
  49. package/dist/persistence/atomic-write.d.ts +8 -2
  50. package/dist/persistence/atomic-write.js +23 -7
  51. package/dist/restart.js +2 -1
  52. package/dist/router/card-action-payload.d.ts +30 -0
  53. package/dist/router/card-action-payload.js +39 -0
  54. package/dist/router/card-helpers.d.ts +63 -0
  55. package/dist/router/card-helpers.js +146 -0
  56. package/dist/router/config/claude.js +30 -1
  57. package/dist/router/config/codex.js +29 -50
  58. package/dist/router/config/common/model-patch.d.ts +22 -0
  59. package/dist/router/config/common/model-patch.js +26 -0
  60. package/dist/router/config/common/render.js +5 -4
  61. package/dist/router/config/dsh.d.ts +51 -0
  62. package/dist/router/config/dsh.js +179 -0
  63. package/dist/router/config/index.d.ts +12 -0
  64. package/dist/router/config/index.js +28 -1
  65. package/dist/router/config/kimi.d.ts +4 -1
  66. package/dist/router/config/kimi.js +27 -0
  67. package/dist/router/config/opencode.js +18 -5
  68. package/dist/router/config/pi.js +4 -5
  69. package/dist/router/config/types.d.ts +17 -2
  70. package/dist/router/index.d.ts +76 -22
  71. package/dist/router/index.js +479 -548
  72. package/dist/router/utils.d.ts +2 -0
  73. package/dist/router/utils.js +16 -2
  74. package/dist/runner/agent-choices-common.d.ts +16 -0
  75. package/dist/runner/agent-choices-common.js +34 -0
  76. package/dist/runner/claude/runner.d.ts +47 -10
  77. package/dist/runner/claude/runner.js +117 -46
  78. package/dist/runner/claude/session.d.ts +166 -0
  79. package/dist/runner/claude/session.js +768 -0
  80. package/dist/runner/codex/app-server/protocol-types.d.ts +66 -39
  81. package/dist/runner/codex/app-server/protocol-types.js +2 -13
  82. package/dist/runner/codex/app-server/runner.d.ts +52 -59
  83. package/dist/runner/codex/app-server/runner.js +216 -338
  84. package/dist/runner/codex/app-server/translator.d.ts +8 -21
  85. package/dist/runner/codex/app-server/translator.js +45 -18
  86. package/dist/runner/codex/index.d.ts +0 -2
  87. package/dist/runner/codex/index.js +0 -2
  88. package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
  89. package/dist/runner/common/acp/protocol-helpers.js +21 -0
  90. package/dist/runner/common/acp/protocol-types.d.ts +271 -0
  91. package/dist/runner/common/acp/protocol-types.js +77 -0
  92. package/dist/runner/common/connection-based-runner.d.ts +111 -0
  93. package/dist/runner/common/connection-based-runner.js +261 -0
  94. package/dist/runner/{codex/app-server → common/jsonrpc}/client.d.ts +14 -7
  95. package/dist/runner/{codex/app-server → common/jsonrpc}/client.js +27 -18
  96. package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
  97. package/dist/runner/{codex/app-server → common/jsonrpc}/connection-manager.js +63 -28
  98. package/dist/runner/{codex/app-server → common/jsonrpc}/transport.d.ts +5 -4
  99. package/dist/runner/{codex/app-server → common/jsonrpc}/transport.js +18 -19
  100. package/dist/runner/common/jsonrpc/types.d.ts +32 -0
  101. package/dist/runner/common/jsonrpc/types.js +9 -0
  102. package/dist/runner/common/process-stopper.d.ts +2 -1
  103. package/dist/runner/common/process-stopper.js +1 -0
  104. package/dist/runner/common/runner-utils.d.ts +6 -13
  105. package/dist/runner/common/runner-utils.js +6 -47
  106. package/dist/runner/common/spawning-runner.d.ts +24 -70
  107. package/dist/runner/common/spawning-runner.js +98 -152
  108. package/dist/runner/dsh/client.d.ts +95 -0
  109. package/dist/runner/dsh/client.js +418 -0
  110. package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
  111. package/dist/runner/dsh/fake-dsh-server.js +156 -0
  112. package/dist/runner/dsh/index.d.ts +3 -0
  113. package/dist/runner/dsh/index.js +4 -0
  114. package/dist/runner/dsh/runner.d.ts +57 -0
  115. package/dist/runner/dsh/runner.js +191 -0
  116. package/dist/runner/dsh/translator.d.ts +28 -0
  117. package/dist/runner/dsh/translator.js +98 -0
  118. package/dist/runner/dsh/types.d.ts +155 -0
  119. package/dist/runner/dsh/types.js +14 -0
  120. package/dist/runner/index.d.ts +5 -4
  121. package/dist/runner/index.js +5 -4
  122. package/dist/runner/kimi/acp/runner.d.ts +106 -0
  123. package/dist/runner/kimi/acp/runner.js +516 -0
  124. package/dist/runner/kimi/acp/translator.d.ts +138 -0
  125. package/dist/runner/kimi/acp/translator.js +441 -0
  126. package/dist/runner/kimi/index.d.ts +1 -1
  127. package/dist/runner/kimi/index.js +1 -1
  128. package/dist/runner/opencode/acp/index.d.ts +1 -0
  129. package/dist/runner/opencode/acp/index.js +2 -0
  130. package/dist/runner/opencode/acp/runner.d.ts +121 -0
  131. package/dist/runner/opencode/acp/runner.js +429 -0
  132. package/dist/runner/opencode/acp/translator.d.ts +119 -0
  133. package/dist/runner/opencode/acp/translator.js +345 -0
  134. package/dist/runner/opencode/index.d.ts +1 -1
  135. package/dist/runner/opencode/index.js +1 -1
  136. package/dist/runner/pi/index.d.ts +1 -1
  137. package/dist/runner/pi/index.js +1 -1
  138. package/dist/runner/pi/rpc/client.d.ts +52 -0
  139. package/dist/runner/pi/rpc/client.js +114 -0
  140. package/dist/runner/pi/rpc/index.d.ts +4 -0
  141. package/dist/runner/pi/rpc/index.js +4 -0
  142. package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
  143. package/dist/runner/pi/rpc/protocol-types.js +21 -0
  144. package/dist/runner/pi/rpc/runner.d.ts +74 -0
  145. package/dist/runner/pi/rpc/runner.js +190 -0
  146. package/dist/runner/pi/rpc/translator.d.ts +55 -0
  147. package/dist/runner/pi/rpc/translator.js +258 -0
  148. package/dist/runner/probe.js +6 -1
  149. package/dist/runner/question-common.d.ts +26 -0
  150. package/dist/runner/question-common.js +42 -0
  151. package/dist/runner/registry.d.ts +7 -8
  152. package/dist/runner/registry.js +3 -4
  153. package/dist/runner/resolve-agent-choices.js +18 -59
  154. package/dist/runner/sync-agent-choices.js +16 -67
  155. package/dist/runner/types.d.ts +97 -14
  156. package/dist/session/claude/session-index.js +2 -3
  157. package/dist/session/claude/session-reader.js +3 -6
  158. package/dist/session/claude/sessions.js +43 -28
  159. package/dist/session/codex/rollout-reader.d.ts +2 -2
  160. package/dist/session/codex/rollout-reader.js +14 -17
  161. package/dist/session/codex/sessions.d.ts +1 -1
  162. package/dist/session/codex/sessions.js +2 -2
  163. package/dist/session/common/content-blocks.d.ts +7 -0
  164. package/dist/session/common/content-blocks.js +13 -9
  165. package/dist/session/common/jsonl.d.ts +8 -25
  166. package/dist/session/common/jsonl.js +126 -261
  167. package/dist/session/common/pagination.d.ts +28 -0
  168. package/dist/session/common/pagination.js +33 -0
  169. package/dist/session/dsh/index.d.ts +1 -0
  170. package/dist/session/dsh/index.js +2 -0
  171. package/dist/session/dsh/sessions.d.ts +39 -0
  172. package/dist/session/dsh/sessions.js +237 -0
  173. package/dist/session/index.d.ts +1 -0
  174. package/dist/session/index.js +1 -0
  175. package/dist/session/kimi/sessions.d.ts +34 -1
  176. package/dist/session/kimi/sessions.js +208 -98
  177. package/dist/session/opencode/sessions.d.ts +17 -3
  178. package/dist/session/opencode/sessions.js +29 -29
  179. package/dist/session/pi/sessions.d.ts +5 -1
  180. package/dist/session/pi/sessions.js +12 -16
  181. package/dist/session/session-store.d.ts +11 -1
  182. package/dist/session/session-store.js +75 -178
  183. package/dist/update/index.d.ts +2 -2
  184. package/dist/update/index.js +2 -2
  185. package/dist/update/install.d.ts +20 -2
  186. package/dist/update/install.js +51 -7
  187. package/dist/workspace/index.d.ts +4 -0
  188. package/dist/workspace/index.js +8 -1
  189. package/package.json +2 -1
  190. package/dist/runner/codex/app-server/connection-manager.d.ts +0 -56
  191. package/dist/runner/codex/argv.d.ts +0 -33
  192. package/dist/runner/codex/argv.js +0 -40
  193. package/dist/runner/codex/factory.d.ts +0 -29
  194. package/dist/runner/codex/factory.js +0 -39
  195. package/dist/runner/codex/jsonl.d.ts +0 -37
  196. package/dist/runner/codex/jsonl.js +0 -221
  197. package/dist/runner/codex/runner.d.ts +0 -66
  198. package/dist/runner/codex/runner.js +0 -129
  199. package/dist/runner/common/exec-translator.d.ts +0 -73
  200. package/dist/runner/common/exec-translator.js +0 -105
  201. package/dist/runner/kimi/jsonl.d.ts +0 -28
  202. package/dist/runner/kimi/jsonl.js +0 -143
  203. package/dist/runner/kimi/runner.d.ts +0 -49
  204. package/dist/runner/kimi/runner.js +0 -105
  205. package/dist/runner/opencode/argv.d.ts +0 -32
  206. package/dist/runner/opencode/argv.js +0 -35
  207. package/dist/runner/opencode/jsonl.d.ts +0 -68
  208. package/dist/runner/opencode/jsonl.js +0 -295
  209. package/dist/runner/opencode/runner.d.ts +0 -61
  210. package/dist/runner/opencode/runner.js +0 -93
  211. package/dist/runner/pi/jsonl.d.ts +0 -128
  212. package/dist/runner/pi/jsonl.js +0 -249
  213. package/dist/runner/pi/runner.d.ts +0 -36
  214. package/dist/runner/pi/runner.js +0 -76
  215. package/dist/session/common/catch-up.d.ts +0 -12
  216. package/dist/session/common/catch-up.js +0 -31
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { loadCodexConfig, getReasoningEffortOptions, getDefaultReasoningEffort, } from '../../config/codex-config.js';
5
5
  import { DEFAULT_TURN_IDLE_TIMEOUT_MINUTES } from '../../config/index.js';
6
+ import { resetModelPatch } from './common/model-patch.js';
6
7
  export class CodexConfigBuilder {
7
8
  buildFields(displayConfig) {
8
9
  const fields = [];
@@ -44,54 +45,33 @@ export class CodexConfigBuilder {
44
45
  options: getReasoningEffortOptions(currentCodexModel),
45
46
  currentValue: currentReasoningEffort,
46
47
  });
47
- // Service mode (exec vs app-server)
48
- const currentServiceMode = displayConfig.agents?.codex?.serviceMode ?? 'exec';
48
+ // 审批策略与沙箱模式由 CodexAppServerRunner 读取(Codex 官方枚举标准值)。
49
+ // Approval policy (Codex 官方 AskForApproval 标准值;on-request 为 codex 默认)
50
+ const currentApprovalPolicy = displayConfig.agents?.codex?.approvalPolicy ?? 'on-request';
49
51
  fields.push({
50
- key: 'agents.codex.serviceMode',
51
- label: '运行模式',
52
+ key: 'agents.codex.approvalPolicy',
53
+ label: '审批策略',
52
54
  type: 'select',
53
- options: ['exec', 'app-server'],
54
- currentValue: currentServiceMode,
55
+ options: ['untrusted', 'on-request', 'never'],
56
+ currentValue: currentApprovalPolicy,
57
+ });
58
+ // Sandbox mode (Codex 官方 SandboxMode 标准值;默认 workspace-write)
59
+ const currentSandbox = displayConfig.agents?.codex?.sandbox ?? 'workspace-write';
60
+ fields.push({
61
+ key: 'agents.codex.sandbox',
62
+ label: '沙箱模式',
63
+ type: 'select',
64
+ options: ['read-only', 'workspace-write', 'danger-full-access'],
65
+ currentValue: currentSandbox,
66
+ });
67
+ const currentTurnIdleTimeoutMinutes = displayConfig.agents?.codex?.appServer?.turnIdleTimeoutMinutes ??
68
+ DEFAULT_TURN_IDLE_TIMEOUT_MINUTES;
69
+ fields.push({
70
+ key: 'agents.codex.appServer.turnIdleTimeoutMinutes',
71
+ label: 'Turn 空闲超时(分钟, 0关闭)',
72
+ type: 'input',
73
+ currentValue: String(currentTurnIdleTimeoutMinutes),
55
74
  });
56
- // 审批策略与沙箱模式仅 app-server 模式可配置(CodexAppServerRunner 读取);
57
- // exec 模式在 runner 层固定 `--sandbox danger-full-access` +
58
- // `approval_policy="never"`(src/runner/codex/argv.ts),卡片不暴露选项,
59
- // 只用说明行把默认语义呈现给用户。
60
- if (currentServiceMode === 'app-server') {
61
- // Approval policy (Codex 官方 AskForApproval 标准值;on-request 为 codex 默认)
62
- const currentApprovalPolicy = displayConfig.agents?.codex?.approvalPolicy ?? 'on-request';
63
- fields.push({
64
- key: 'agents.codex.approvalPolicy',
65
- label: '审批策略',
66
- type: 'select',
67
- options: ['untrusted', 'on-request', 'never'],
68
- currentValue: currentApprovalPolicy,
69
- });
70
- // Sandbox mode (Codex 官方 SandboxMode 标准值)
71
- const currentSandbox = displayConfig.agents?.codex?.sandbox ?? 'danger-full-access';
72
- fields.push({
73
- key: 'agents.codex.sandbox',
74
- label: '沙箱模式',
75
- type: 'select',
76
- options: ['read-only', 'workspace-write', 'danger-full-access'],
77
- currentValue: currentSandbox,
78
- });
79
- const currentTurnIdleTimeoutMinutes = displayConfig.agents?.codex?.appServer?.turnIdleTimeoutMinutes ??
80
- DEFAULT_TURN_IDLE_TIMEOUT_MINUTES;
81
- fields.push({
82
- key: 'agents.codex.appServer.turnIdleTimeoutMinutes',
83
- label: 'Turn 空闲超时(分钟, 0关闭)',
84
- type: 'input',
85
- currentValue: String(currentTurnIdleTimeoutMinutes),
86
- });
87
- }
88
- else {
89
- fields.push({
90
- key: 'agents.codex.modeNote',
91
- label: '命令行模式固定为完全访问(danger-full-access)且无需审批(approval never),不提供审批策略/沙箱配置',
92
- type: 'note',
93
- });
94
- }
95
75
  return fields;
96
76
  }
97
77
  handleFieldChange(key, value, config) {
@@ -100,12 +80,11 @@ export class CodexConfigBuilder {
100
80
  // provider 变更时,重置 model 为新 provider 的首个模型
101
81
  if (key === 'agents.codex.modelProvider' && typeof value === 'string') {
102
82
  const codexCfg = loadCodexConfig();
103
- const newModelOptions = codexCfg.modelOptions(value);
104
83
  const currentModel = config.agents?.codex?.model;
105
- const currentModelIsValid = newModelOptions.some((m) => m === currentModel);
106
- if (!currentModelIsValid && newModelOptions.length > 0) {
107
- const replacedModel = newModelOptions[0];
108
- patches.push({ key: 'agents.codex.model', value: replacedModel });
84
+ const modelPatch = resetModelPatch(key, 'agents.codex.model', currentModel, codexCfg.modelOptions(value));
85
+ if (modelPatch) {
86
+ patches.push(modelPatch);
87
+ const replacedModel = modelPatch.value;
109
88
  // provider 切换自动替换模型后,必须对替换后的模型执行
110
89
  // 同样的档位校验——否则旧模型支持的档位会原样留在卡片/透传给不支持它的模型
111
90
  // (codex 只在会话中 with_model 时钳制,session 创建路径不钳制)。
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared field-change patch helpers for agent config cards.
3
+ *
4
+ * `resetModelPatch` captures the "provider changed → reset model to the new
5
+ * provider's first model" behavior that opencode/pi/codex config builders were
6
+ * duplicating.
7
+ */
8
+ /**
9
+ * Build a model-reset patch when the provider field changes: if the current
10
+ * model is not offered by the new provider (and the new provider has models),
11
+ * patch the model to the provider's first option.
12
+ *
13
+ * @param providerKey The field key that changed, e.g. 'agents.codex.modelProvider'.
14
+ * @param modelKey The model field to reset, e.g. 'agents.codex.model'.
15
+ * @param currentModel The currently-configured model value (may be undefined).
16
+ * @param newModelOptions Models offered by the newly-selected provider.
17
+ * @returns A `{ key, value }` patch, or null when no reset is needed.
18
+ */
19
+ export declare function resetModelPatch(providerKey: string, modelKey: string, currentModel: string | undefined, newModelOptions: string[]): {
20
+ key: string;
21
+ value: unknown;
22
+ } | null;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared field-change patch helpers for agent config cards.
3
+ *
4
+ * `resetModelPatch` captures the "provider changed → reset model to the new
5
+ * provider's first model" behavior that opencode/pi/codex config builders were
6
+ * duplicating.
7
+ */
8
+ /**
9
+ * Build a model-reset patch when the provider field changes: if the current
10
+ * model is not offered by the new provider (and the new provider has models),
11
+ * patch the model to the provider's first option.
12
+ *
13
+ * @param providerKey The field key that changed, e.g. 'agents.codex.modelProvider'.
14
+ * @param modelKey The model field to reset, e.g. 'agents.codex.model'.
15
+ * @param currentModel The currently-configured model value (may be undefined).
16
+ * @param newModelOptions Models offered by the newly-selected provider.
17
+ * @returns A `{ key, value }` patch, or null when no reset is needed.
18
+ */
19
+ export function resetModelPatch(providerKey, modelKey, currentModel, newModelOptions) {
20
+ const currentModelIsValid = newModelOptions.some((m) => m === currentModel);
21
+ if (!currentModelIsValid && newModelOptions.length > 0) {
22
+ return { key: modelKey, value: newModelOptions[0] };
23
+ }
24
+ return null;
25
+ }
26
+ //# sourceMappingURL=model-patch.js.map
@@ -86,10 +86,11 @@ function buildSelectElement(field, currentValue) {
86
86
  {
87
87
  tag: 'select_static',
88
88
  placeholder: { tag: 'plain_text', content: '请选择' },
89
- options: field.options.map((opt) => ({
90
- text: { tag: 'plain_text', content: opt },
91
- value: opt,
92
- })),
89
+ options: field.options.map((opt) => {
90
+ const text = typeof opt === 'string' ? opt : opt.text;
91
+ const value = typeof opt === 'string' ? opt : opt.value;
92
+ return { text: { tag: 'plain_text', content: text }, value };
93
+ }),
93
94
  initial_option: selectedValue,
94
95
  behaviors: [{ type: 'callback', value: { cmd: 'config.set', key: field.key } }],
95
96
  },
@@ -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
+ }
@@ -0,0 +1,179 @@
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 { DEFAULT_DSH_HOST } from '../../config/index.js';
16
+ import { DshClient } from '../../runner/dsh/client.js';
17
+ import { getLogger } from '../../logger/index.js';
18
+ /** 固定兜底 preset 清单(DSH rc.7 实测;动态拉取失败时使用)。 */
19
+ const FALLBACK_PRESET_OPTIONS = [
20
+ { text: 'standard(标准模式)', value: 'standard' },
21
+ { text: 'minimal(极简模式)', value: 'minimal' },
22
+ { text: 'code(PTC 模式)', value: 'code' },
23
+ { text: 'cordis(创造模式)', value: 'cordis' },
24
+ ];
25
+ /** 固定兜底模型清单(DSH rc.7 实测;动态拉取失败时使用)。 */
26
+ const FALLBACK_MODEL_OPTIONS = ['deepseek-v4-flash', 'deepseek-v4-pro'];
27
+ /** 固定兜底推理档位(llm.models 实际档位;动态拉取失败时使用)。 */
28
+ const FALLBACK_EFFORT_OPTIONS = ['off', 'low', 'high', 'max'];
29
+ /** 「跟随服务端默认」哨兵值:select value 用空串,保存时转 undefined 删除键。 */
30
+ const DEFAULT_SENTINEL = '';
31
+ export class DshConfigBuilder {
32
+ /** 最近一次预取用的 host;host 变更时缓存失效(下一张卡重新拉取)。 */
33
+ prefetchHost;
34
+ modelCatalog;
35
+ presets;
36
+ /** 可注入 client 工厂(测试用);默认生产实现。 */
37
+ clientFactory;
38
+ constructor(clientFactory) {
39
+ this.clientFactory = clientFactory ?? ((base) => new DshClient(base));
40
+ }
41
+ /** 异步预取模型/预设目录(§4.2 二期动态清单)。失败/离线静默回退固定兜底清单,
42
+ * 卡不依赖 DSH 服务在线;同一 host 只拉一次,避免每次刷卡重复请求。 */
43
+ async prefetch(host) {
44
+ const base = (host ?? DEFAULT_DSH_HOST).replace(/\/$/, '');
45
+ if (!base || base === this.prefetchHost)
46
+ return;
47
+ const client = this.clientFactory(base);
48
+ try {
49
+ this.modelCatalog = await client.listModels();
50
+ }
51
+ catch (err) {
52
+ getLogger().warn(`[dsh-config] llm.models prefetch failed (falling back to fixed catalog): ${err.message}`);
53
+ this.modelCatalog = undefined;
54
+ }
55
+ try {
56
+ this.presets = await client.listPresets();
57
+ }
58
+ catch (err) {
59
+ getLogger().warn(`[dsh-config] agentPreset.list prefetch failed (falling back to fixed presets): ${err.message}`);
60
+ this.presets = undefined;
61
+ }
62
+ this.prefetchHost = base;
63
+ }
64
+ /** 同步注入 catalog/presets(测试用,替代异步 prefetch;标记 prefetchHost 使
65
+ * 后续 prefetch 短路,避免测试误触发网络请求)。 */
66
+ setCatalog(catalog, presets) {
67
+ this.modelCatalog = catalog;
68
+ if (presets)
69
+ this.presets = presets;
70
+ this.prefetchHost = 'injected';
71
+ }
72
+ /** 展平 llm.models 各 provider group 的模型 id;无 catalog 时回退固定清单。 */
73
+ modelOptions() {
74
+ if (this.modelCatalog) {
75
+ const ids = this.modelCatalog.groups.flatMap((g) => g.models.map((m) => m.id));
76
+ if (ids.length > 0)
77
+ return ids;
78
+ }
79
+ return FALLBACK_MODEL_OPTIONS;
80
+ }
81
+ /** preset 清单;无 presets 时回退固定清单。显示名优先用 preset.yml 的 name。 */
82
+ presetOptions() {
83
+ if (this.presets && this.presets.presets.length > 0) {
84
+ return this.presets.presets
85
+ .filter((p) => !p.broken)
86
+ .map((p) => ({ text: p.name ? `${p.id}(${p.name})` : p.id, value: p.id }));
87
+ }
88
+ return FALLBACK_PRESET_OPTIONS;
89
+ }
90
+ /** 模型在 catalog 中声明的推理档位;无 catalog/模型不在目录时回退固定档位。 */
91
+ effortOptionsForModel(model) {
92
+ if (model && this.modelCatalog) {
93
+ for (const group of this.modelCatalog.groups) {
94
+ const entry = group.models.find((m) => m.id === model);
95
+ if (entry?.reasoning && entry.reasoning.efforts.length > 0) {
96
+ return entry.reasoning.efforts.map((e) => e.id);
97
+ }
98
+ }
99
+ }
100
+ return FALLBACK_EFFORT_OPTIONS;
101
+ }
102
+ buildFields(displayConfig) {
103
+ const dsh = displayConfig.agents?.dsh;
104
+ const currentHost = dsh?.host ?? DEFAULT_DSH_HOST;
105
+ const currentPreset = dsh?.agentPreset ?? DEFAULT_SENTINEL;
106
+ const currentModel = dsh?.model ?? DEFAULT_SENTINEL;
107
+ const currentEffort = dsh?.reasoningEffort ?? DEFAULT_SENTINEL;
108
+ const modelOptions = this.modelOptions();
109
+ const isCustomModel = currentModel !== DEFAULT_SENTINEL && !modelOptions.includes(currentModel);
110
+ const effortOptions = this.effortOptionsForModel(currentModel === DEFAULT_SENTINEL ? undefined : currentModel);
111
+ return [
112
+ {
113
+ key: 'agents.dsh.host',
114
+ label: 'DSH Web Host',
115
+ type: 'input',
116
+ currentValue: currentHost,
117
+ },
118
+ {
119
+ key: 'agents.dsh.agentPreset',
120
+ label: '预设模式',
121
+ type: 'select',
122
+ options: [{ text: '跟随服务端默认', value: DEFAULT_SENTINEL }, ...this.presetOptions()],
123
+ currentValue: currentPreset,
124
+ },
125
+ {
126
+ key: 'agents.dsh.model',
127
+ label: '使用模型',
128
+ type: 'select',
129
+ options: [{ text: '跟随服务端默认', value: DEFAULT_SENTINEL }, ...modelOptions],
130
+ currentValue: isCustomModel ? undefined : currentModel,
131
+ },
132
+ // 自定义模型名输入框(与 codex/claude 一致的行为)
133
+ {
134
+ key: 'agents.dsh.model',
135
+ label: '自定义模型名',
136
+ type: 'input',
137
+ currentValue: isCustomModel ? currentModel : '',
138
+ },
139
+ {
140
+ key: 'agents.dsh.reasoningEffort',
141
+ label: '推理强度',
142
+ type: 'select',
143
+ options: [{ text: '跟随服务端默认', value: DEFAULT_SENTINEL }, ...effortOptions],
144
+ currentValue: currentEffort,
145
+ },
146
+ ];
147
+ }
148
+ handleFieldChange(key, value, config) {
149
+ const patches = [];
150
+ // 空串哨兵「跟随服务端默认」→ undefined patch(router 删除键)
151
+ const effective = value === DEFAULT_SENTINEL ? undefined : value;
152
+ patches.push({ key, value: effective });
153
+ // 模型变更时,若当前档位不被新模型支持,重置(参照 kimi/codex 语义)
154
+ if (key === 'agents.dsh.model' && typeof value === 'string') {
155
+ const effortPatch = this.effortPatchForModel(config, effective);
156
+ if (effortPatch)
157
+ patches.push(effortPatch);
158
+ }
159
+ return patches;
160
+ }
161
+ /** 切到 newModel 后的档位补丁:当前档位仍被新模型支持 → 不产出;否则用新模型
162
+ * 目录中位;无目录/空列表 → undefined(清空 = 跟随服务端默认)。 */
163
+ effortPatchForModel(config, newModel) {
164
+ const currentEffort = config.agents?.dsh?.reasoningEffort;
165
+ if (!newModel) {
166
+ // 模型被清空(跟随默认)→ 档位一并清空跟随默认
167
+ return currentEffort ? { key: 'agents.dsh.reasoningEffort', value: undefined } : null;
168
+ }
169
+ const supported = this.effortOptionsForModel(newModel);
170
+ if (currentEffort && supported.includes(currentEffort))
171
+ return null;
172
+ // 新模型目录中位档位;偶数长度取偏高位(off/low → low,符合 DSH 默认 high 语义)
173
+ const middle = supported[Math.floor(supported.length / 2)];
174
+ return middle
175
+ ? { key: 'agents.dsh.reasoningEffort', value: middle }
176
+ : { key: 'agents.dsh.reasoningEffort', value: undefined };
177
+ }
178
+ }
179
+ //# sourceMappingURL=dsh.js.map
@@ -6,6 +6,12 @@
6
6
  */
7
7
  import type { AgentKind } from '../../runner/types.js';
8
8
  import type { AgentConfigCardBuilder } from './types.js';
9
+ /**
10
+ * 首次启动默认展示的 Coding Agent 顺序:Codex → Claude → OpenCode → Pi → Kimi → DSH。
11
+ * 未安装(availability === false)的 agent 在展示时排到后面
12
+ * (见 sortAgentsForDisplay),同组内保持本顺序。
13
+ */
14
+ export declare const DEFAULT_AGENT_ORDER: readonly AgentKind[];
9
15
  /**
10
16
  * Get the config builder for a specific agent.
11
17
  *
@@ -16,3 +22,9 @@ import type { AgentConfigCardBuilder } from './types.js';
16
22
  export declare function getConfigBuilder(agent: AgentKind): AgentConfigCardBuilder;
17
23
  /** List all registered agent kinds. */
18
24
  export declare function listRegisteredAgents(): AgentKind[];
25
+ /**
26
+ * 按展示规则排序 agent 列表:明确未安装(availability === false)的排后,
27
+ * 已安装/未知的在前;同组内保持传入顺序(canonical order)。
28
+ * Array.prototype.sort 是稳定排序,因此按可用性分组不会打乱组内顺序。
29
+ */
30
+ export declare function sortAgentsForDisplay(agents: readonly AgentKind[], availability: (kind: AgentKind) => boolean | undefined): AgentKind[];
@@ -9,6 +9,20 @@ import { CodexConfigBuilder } from './codex.js';
9
9
  import { PiConfigBuilder } from './pi.js';
10
10
  import { OpencodeConfigBuilder } from './opencode.js';
11
11
  import { KimiConfigBuilder } from './kimi.js';
12
+ import { DshConfigBuilder } from './dsh.js';
13
+ /**
14
+ * 首次启动默认展示的 Coding Agent 顺序:Codex → Claude → OpenCode → Pi → Kimi → DSH。
15
+ * 未安装(availability === false)的 agent 在展示时排到后面
16
+ * (见 sortAgentsForDisplay),同组内保持本顺序。
17
+ */
18
+ export const DEFAULT_AGENT_ORDER = [
19
+ 'codex',
20
+ 'claude',
21
+ 'opencode',
22
+ 'pi',
23
+ 'kimi',
24
+ 'dsh',
25
+ ];
12
26
  /** Registry of config builders, keyed by agent kind. */
13
27
  const builders = {
14
28
  claude: new ClaudeConfigBuilder(),
@@ -16,6 +30,7 @@ const builders = {
16
30
  pi: new PiConfigBuilder(),
17
31
  opencode: new OpencodeConfigBuilder(),
18
32
  kimi: new KimiConfigBuilder(),
33
+ dsh: new DshConfigBuilder(),
19
34
  };
20
35
  /**
21
36
  * Get the config builder for a specific agent.
@@ -33,6 +48,18 @@ export function getConfigBuilder(agent) {
33
48
  }
34
49
  /** List all registered agent kinds. */
35
50
  export function listRegisteredAgents() {
36
- return Object.keys(builders);
51
+ return DEFAULT_AGENT_ORDER.filter((kind) => kind in builders);
52
+ }
53
+ /**
54
+ * 按展示规则排序 agent 列表:明确未安装(availability === false)的排后,
55
+ * 已安装/未知的在前;同组内保持传入顺序(canonical order)。
56
+ * Array.prototype.sort 是稳定排序,因此按可用性分组不会打乱组内顺序。
57
+ */
58
+ export function sortAgentsForDisplay(agents, availability) {
59
+ return [...agents].sort((a, b) => {
60
+ const aMissing = availability(a) === false ? 1 : 0;
61
+ const bMissing = availability(b) === false ? 1 : 0;
62
+ return aMissing - bMissing;
63
+ });
37
64
  }
38
65
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Kimi config builder — builds fields and handles field changes for Kimi agent.
3
+ *
4
+ * Kimi is pure ACP mode: permissionMode (manual/auto/yolo) and the ACP
5
+ * sub-config fields are always shown.
3
6
  */
4
7
  import type { AgentConfigCardBuilder, ConfigField } from './types.js';
5
- import type { AppConfig } from '../../config/index.js';
8
+ import { type AppConfig } from '../../config/index.js';
6
9
  export declare class KimiConfigBuilder implements AgentConfigCardBuilder {
7
10
  buildFields(displayConfig: AppConfig): ConfigField[];
8
11
  handleFieldChange(key: string, value: unknown, config: AppConfig): Array<{
@@ -1,7 +1,16 @@
1
1
  /**
2
2
  * Kimi config builder — builds fields and handles field changes for Kimi agent.
3
+ *
4
+ * Kimi is pure ACP mode: permissionMode (manual/auto/yolo) and the ACP
5
+ * sub-config fields are always shown.
3
6
  */
4
7
  import { loadKimiConfig, KIMI_THINKING_EFFORTS, FALLBACK_EFFORTS, } from '../../config/kimi-config.js';
8
+ import { DEFAULT_TURN_IDLE_TIMEOUT_MINUTES } from '../../config/index.js';
9
+ const PERMISSION_MODE_OPTIONS = [
10
+ { text: 'manual(逐项审批)', value: 'manual' },
11
+ { text: 'auto(引擎裁决)', value: 'auto' },
12
+ { text: 'yolo(全部放行)', value: 'yolo' },
13
+ ];
5
14
  export class KimiConfigBuilder {
6
15
  buildFields(displayConfig) {
7
16
  const fields = [];
@@ -9,6 +18,8 @@ export class KimiConfigBuilder {
9
18
  const kimiCfg = loadKimiConfig();
10
19
  const currentModel = displayConfig.agents?.kimi?.model ?? kimiCfg.currentModel;
11
20
  const currentThinking = displayConfig.agents?.kimi?.thinkingEffort ?? 'max';
21
+ const currentPermissionMode = displayConfig.agents?.kimi?.permissionMode ?? 'manual';
22
+ // §7: 现有 model + thinkingEffort select(顺序不变)
12
23
  fields.push({
13
24
  key: 'agents.kimi.model',
14
25
  label: '使用模型',
@@ -25,6 +36,22 @@ export class KimiConfigBuilder {
25
36
  options: (kimiCfg.modelEfforts[currentModel] ?? [...FALLBACK_EFFORTS]).filter((e) => KIMI_THINKING_EFFORTS.includes(e)),
26
37
  currentValue: currentThinking,
27
38
  });
39
+ // Permission mode + ACP sub-config (pure ACP: always shown)
40
+ // §7: 文案用官方名 + 中文注释
41
+ fields.push({
42
+ key: 'agents.kimi.permissionMode',
43
+ label: '权限模式',
44
+ type: 'select',
45
+ options: PERMISSION_MODE_OPTIONS,
46
+ currentValue: currentPermissionMode,
47
+ });
48
+ const currentTurnIdleTimeoutMinutes = displayConfig.agents?.kimi?.acp?.turnIdleTimeoutMinutes ?? DEFAULT_TURN_IDLE_TIMEOUT_MINUTES;
49
+ fields.push({
50
+ key: 'agents.kimi.acp.turnIdleTimeoutMinutes',
51
+ label: 'Turn 空闲超时(分钟, 0关闭)',
52
+ type: 'input',
53
+ currentValue: String(currentTurnIdleTimeoutMinutes),
54
+ });
28
55
  return fields;
29
56
  }
30
57
  handleFieldChange(key, value, config) {
@@ -2,6 +2,11 @@
2
2
  * OpenCode config builder — builds fields and handles field changes for OpenCode agent.
3
3
  */
4
4
  import { loadOpencodeConfig } from '../../config/opencode-config.js';
5
+ import { resetModelPatch } from './common/model-patch.js';
6
+ const MODE_OPTIONS = [
7
+ { text: 'build(默认,逐项审批)', value: 'build' },
8
+ { text: 'plan(规划模式)', value: 'plan' },
9
+ ];
5
10
  export class OpencodeConfigBuilder {
6
11
  buildFields(displayConfig) {
7
12
  const fields = [];
@@ -23,6 +28,16 @@ export class OpencodeConfigBuilder {
23
28
  type: 'select',
24
29
  options: modelOptions,
25
30
  });
31
+ // §P5: mode select(build/plan,默认 build)——opencode 无运行期审批档位,
32
+ // mode 即审批粒度档位(build 逐项审批 / plan 规划模式)。
33
+ const currentMode = displayConfig.agents?.opencode?.mode ?? 'build';
34
+ fields.push({
35
+ key: 'agents.opencode.mode',
36
+ label: '会话模式',
37
+ type: 'select',
38
+ options: MODE_OPTIONS,
39
+ currentValue: currentMode,
40
+ });
26
41
  return fields;
27
42
  }
28
43
  handleFieldChange(key, value, config) {
@@ -31,12 +46,10 @@ export class OpencodeConfigBuilder {
31
46
  // provider 变更时,重置 model 为新 provider 的首个模型
32
47
  if (key === 'agents.opencode.providerID' && typeof value === 'string') {
33
48
  const opencodeCfg = loadOpencodeConfig();
34
- const newModelOptions = opencodeCfg.modelOptions(value);
35
49
  const currentModel = config.agents?.opencode?.modelID;
36
- const currentModelIsValid = newModelOptions.some((m) => m === currentModel);
37
- if (!currentModelIsValid && newModelOptions.length > 0) {
38
- patches.push({ key: 'agents.opencode.modelID', value: newModelOptions[0] });
39
- }
50
+ const modelPatch = resetModelPatch(key, 'agents.opencode.modelID', currentModel, opencodeCfg.modelOptions(value));
51
+ if (modelPatch)
52
+ patches.push(modelPatch);
40
53
  }
41
54
  return patches;
42
55
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { loadPiConfig, getPiModelOptions } from '../../config/pi-config.js';
5
5
  import { PI_THINKING_LEVELS } from '../../config/index.js';
6
+ import { resetModelPatch } from './common/model-patch.js';
6
7
  export class PiConfigBuilder {
7
8
  buildFields(displayConfig) {
8
9
  const fields = [];
@@ -37,12 +38,10 @@ export class PiConfigBuilder {
37
38
  patches.push({ key, value });
38
39
  // provider 变更时,重置 model 为新 provider 的首个模型
39
40
  if (key === 'agents.pi.provider' && typeof value === 'string') {
40
- const newModelOptions = getPiModelOptions(value);
41
41
  const currentModel = config.agents?.pi?.model;
42
- const currentModelIsValid = newModelOptions.some((m) => m === currentModel);
43
- if (!currentModelIsValid && newModelOptions.length > 0) {
44
- patches.push({ key: 'agents.pi.model', value: newModelOptions[0] });
45
- }
42
+ const modelPatch = resetModelPatch(key, 'agents.pi.model', currentModel, getPiModelOptions(value));
43
+ if (modelPatch)
44
+ patches.push(modelPatch);
46
45
  }
47
46
  return patches;
48
47
  }
@@ -5,13 +5,22 @@
5
5
  * The router delegates to `getConfigBuilder(agent)` instead of a giant switch-case.
6
6
  */
7
7
  import type { AppConfig } from '../../config/index.js';
8
+ /** A select option with separate display text and config value. */
9
+ export interface SelectOption {
10
+ /** Display text shown in the dropdown (may include annotations). */
11
+ text: string;
12
+ /** Config value written to config.yaml (must match schema enum values). */
13
+ value: string;
14
+ }
8
15
  /** A single field descriptor in the /config card. */
9
16
  export interface ConfigField {
10
17
  key: string;
11
18
  label: string;
12
19
  type: 'boolean' | 'select' | 'input' | 'note';
13
- /** For select type: the list of options shown in the dropdown. */
14
- options?: readonly string[];
20
+ /** For select type: the list of options shown in the dropdown.
21
+ * Accepts plain strings (text=value) or {text, value} objects for
22
+ * display-value separation (e.g. 'manual(逐项审批)' → value 'manual'). */
23
+ options?: readonly (string | SelectOption)[];
15
24
  /** Override the currentValue shown in the card (takes priority over getConfigValue). */
16
25
  currentValue?: string | boolean;
17
26
  }
@@ -40,4 +49,10 @@ export interface AgentConfigCardBuilder {
40
49
  key: string;
41
50
  value: unknown;
42
51
  }>;
52
+ /**
53
+ * 可选的异步预取钩子:卡片渲染前拉取动态选项(如 DSH 的模型/预设目录)。
54
+ * host 为该 agent 的当前连接地址;实现内部应缓存(同一 host 只拉一次),
55
+ * 失败静默回退固定兜底,不阻断卡片渲染。非异步预取的 agent 可不实现。
56
+ */
57
+ prefetch?(host?: string): Promise<void>;
43
58
  }