lark-remote 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/bridge/approval-coordinator.d.ts +30 -1
- package/dist/bridge/approval-coordinator.js +93 -5
- package/dist/bridge/index.d.ts +36 -6
- package/dist/bridge/index.js +56 -10
- package/dist/card/approval-render.js +287 -48
- package/dist/card/card-shared.d.ts +6 -4
- package/dist/card/card-shared.js +6 -4
- package/dist/card/run-renderer.d.ts +7 -0
- package/dist/card/run-renderer.js +22 -11
- package/dist/config/index.d.ts +24 -1
- package/dist/config/index.js +34 -3
- package/dist/index.js +40 -16
- package/dist/order/alias-resolve.d.ts +6 -2
- package/dist/order/index.d.ts +9 -2
- package/dist/order/index.js +33 -0
- package/dist/router/card-action-payload.d.ts +30 -0
- package/dist/router/card-action-payload.js +39 -0
- package/dist/router/config/claude.js +8 -3
- package/dist/router/config/dsh.d.ts +51 -0
- package/dist/router/config/dsh.js +179 -0
- package/dist/router/config/index.d.ts +1 -1
- package/dist/router/config/index.js +4 -1
- package/dist/router/config/types.d.ts +6 -0
- package/dist/router/index.d.ts +39 -4
- package/dist/router/index.js +471 -90
- package/dist/router/utils.js +12 -2
- package/dist/runner/agent-choices-common.js +3 -0
- package/dist/runner/claude/runner.js +22 -0
- package/dist/runner/claude/session.d.ts +26 -0
- package/dist/runner/claude/session.js +105 -9
- package/dist/runner/codex/app-server/protocol-types.d.ts +35 -0
- package/dist/runner/codex/app-server/protocol-types.js +1 -1
- package/dist/runner/codex/app-server/runner.d.ts +10 -0
- package/dist/runner/codex/app-server/runner.js +56 -1
- package/dist/runner/codex/app-server/translator.d.ts +7 -0
- package/dist/runner/codex/app-server/translator.js +45 -0
- package/dist/runner/common/acp/protocol-types.d.ts +33 -0
- package/dist/runner/common/acp/protocol-types.js +2 -0
- package/dist/runner/common/connection-based-runner.js +10 -5
- package/dist/runner/dsh/client.d.ts +95 -0
- package/dist/runner/dsh/client.js +418 -0
- package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
- package/dist/runner/dsh/fake-dsh-server.js +156 -0
- package/dist/runner/dsh/index.d.ts +3 -0
- package/dist/runner/dsh/index.js +4 -0
- package/dist/runner/dsh/runner.d.ts +57 -0
- package/dist/runner/dsh/runner.js +211 -0
- package/dist/runner/dsh/translator.d.ts +28 -0
- package/dist/runner/dsh/translator.js +98 -0
- package/dist/runner/dsh/types.d.ts +155 -0
- package/dist/runner/dsh/types.js +14 -0
- package/dist/runner/index.d.ts +1 -0
- package/dist/runner/index.js +1 -0
- package/dist/runner/kimi/acp/runner.js +84 -1
- package/dist/runner/kimi/acp/translator.d.ts +14 -0
- package/dist/runner/kimi/acp/translator.js +108 -3
- package/dist/runner/pi/rpc/runner.d.ts +1 -7
- package/dist/runner/pi/rpc/runner.js +8 -12
- package/dist/runner/probe.js +6 -1
- package/dist/runner/question-common.d.ts +26 -0
- package/dist/runner/question-common.js +42 -0
- package/dist/runner/types.d.ts +94 -10
- package/dist/session/claude/sessions.js +37 -15
- package/dist/session/dsh/index.d.ts +1 -0
- package/dist/session/dsh/index.js +2 -0
- package/dist/session/dsh/sessions.d.ts +44 -0
- package/dist/session/dsh/sessions.js +252 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/workspace/index.d.ts +12 -0
- package/dist/workspace/index.js +28 -2
- package/package.json +2 -1
- package/dist/order/alias-store.d.ts +0 -38
- package/dist/order/alias-store.js +0 -106
package/dist/config/index.js
CHANGED
|
@@ -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
|
-
|
|
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,39 @@ 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
|
+
// hostProvider 每次调用读最新 config:/config 修改 host 后 runner 重建为
|
|
245
|
+
// 新 host,reader(/resume、用量、完成卡)也跟随新 host(CC-02)。
|
|
246
|
+
hostProvider: () => {
|
|
247
|
+
const container = agentRegistry.getConfigContainer();
|
|
248
|
+
const latestConfig = container?.current ?? config;
|
|
249
|
+
return getAgentConfig(latestConfig, 'dsh')?.host;
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
agentRegistry.register('dsh', (_ws) => {
|
|
253
|
+
// Read latest config from container so /config host changes take effect
|
|
254
|
+
// after bridge.setConfig() + clearRunners().
|
|
255
|
+
const container = agentRegistry.getConfigContainer();
|
|
256
|
+
const latestConfig = container?.current ?? config;
|
|
257
|
+
const dshConf = getAgentConfig(latestConfig, 'dsh');
|
|
258
|
+
return new DshRunner({
|
|
259
|
+
kind: 'dsh',
|
|
260
|
+
sessionReader: dshSessionReader,
|
|
261
|
+
host: dshConf?.host,
|
|
262
|
+
agentPreset: dshConf?.agentPreset,
|
|
263
|
+
model: dshConf?.model,
|
|
264
|
+
reasoningEffort: dshConf?.reasoningEffort,
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
sessionReaderRegistry.register('dsh', dshSessionReader);
|
|
241
268
|
// 2026-07-17: 注册所有 agent 的显示名,实现单点真相
|
|
242
269
|
agentRegistry.registerDisplayName('claude', 'Claude');
|
|
243
270
|
agentRegistry.registerDisplayName('codex', 'Codex');
|
|
244
271
|
agentRegistry.registerDisplayName('opencode', 'Opencode');
|
|
245
272
|
agentRegistry.registerDisplayName('pi', 'Pi');
|
|
246
273
|
agentRegistry.registerDisplayName('kimi', 'Kimi');
|
|
274
|
+
agentRegistry.registerDisplayName('dsh', 'DSH');
|
|
247
275
|
// 设置全局 registry,供 agentDisplayName 等全局函数使用
|
|
248
276
|
AgentRegistry.setGlobalInstance(agentRegistry);
|
|
249
277
|
// Probe agent CLI availability (fire-and-forget, populates cache for /config card).
|
|
@@ -493,18 +521,10 @@ function setupMessageHandlers(connector, router, bridge, sessionStore, workspace
|
|
|
493
521
|
}
|
|
494
522
|
const isImmediate = isImmediateAction(actionValue.cmd);
|
|
495
523
|
const workspace = sessionStore.getCwd(userId) ?? '';
|
|
496
|
-
// Build full value object — spread all fields from actionValue,
|
|
497
|
-
//
|
|
498
|
-
//
|
|
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
|
-
};
|
|
524
|
+
// Build full value object — spread all fields from actionValue, then merge
|
|
525
|
+
// component out-of-band fields (option/formValue/inputValue/options) where
|
|
526
|
+
// present. See card-action-payload.ts for the component-type contract.
|
|
527
|
+
const fullValue = buildCardActionFullValue(actionValue, action);
|
|
508
528
|
// queue.input / config.save / approval.respond / approval.toggle /
|
|
509
529
|
// approval.answer 系列返回 CardActionResponse toast 给点击用户即时反馈。
|
|
510
530
|
// 必须直接返回值 -- enqueueImmediate / enqueueConfigAction 是
|
|
@@ -512,19 +532,24 @@ function setupMessageHandlers(connector, router, bridge, sessionStore, workspace
|
|
|
512
532
|
// 直返列表外,过期/重复 nonce/非法选项等错误 toast 被静默吞掉)。
|
|
513
533
|
// 审批响应尤其不能落串行队列:run 任务占用队列头直到 turn 结束,审批响应
|
|
514
534
|
// 排在后面会形成死锁(run 不结束不执行、run 结束 coordinator 已删响应空转)。
|
|
535
|
+
// order.aliasInput / order.aliasRemove 也返回 toast,需直返避免被吞。
|
|
515
536
|
if (actionValue.cmd === 'queue.input' ||
|
|
537
|
+
actionValue.cmd === 'order.aliasInput' ||
|
|
538
|
+
actionValue.cmd === 'order.aliasRemove' ||
|
|
516
539
|
actionValue.cmd === 'config.save' ||
|
|
517
540
|
actionValue.cmd === 'approval.respond' ||
|
|
518
541
|
actionValue.cmd === 'approval.toggle' ||
|
|
519
542
|
actionValue.cmd === 'approval.answer' ||
|
|
520
543
|
actionValue.cmd === 'approval.answerSubmit' ||
|
|
521
|
-
actionValue.cmd === 'approval.answerCustom'
|
|
544
|
+
actionValue.cmd === 'approval.answerCustom' ||
|
|
545
|
+
actionValue.cmd === 'approval.answerNote') {
|
|
522
546
|
return router.handleCardAction(fullValue, { userId, chatId, messageId });
|
|
523
547
|
}
|
|
524
548
|
// order.exec → equivalent queued message (Plan A): resolve the order text
|
|
525
549
|
// at the enqueue boundary and route it through router.handle, exactly like
|
|
526
550
|
// a hand-typed message. See order-exec-dispatch.ts for the contract and
|
|
527
551
|
// why an internal key replaces the Feishu card messageId.
|
|
552
|
+
// (order.aliasEdit 是控制动作,走 enqueueImmediate 弹卡即可,不需直返。)
|
|
528
553
|
if (actionValue.cmd === 'order.exec') {
|
|
529
554
|
void dispatchOrderExecForQueue({
|
|
530
555
|
router,
|
|
@@ -636,7 +661,6 @@ async function main() {
|
|
|
636
661
|
configPath: path.join(configDir, 'config.yaml'),
|
|
637
662
|
workspacePath: path.join(configDir, 'workspace.json'),
|
|
638
663
|
ordersPath: path.join(configDir, 'orders.json'),
|
|
639
|
-
aliasesPath: path.join(configDir, 'aliases.json'),
|
|
640
664
|
restartSpawner: () => spawnReplacementBridge(path.join(configDir, 'logs')),
|
|
641
665
|
sessionReaderRegistry,
|
|
642
666
|
devMode: cliArgs.dev,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/** 参与别名展开的条目:别名名 + 展开文本(即指令文本)。 */
|
|
2
|
+
export interface AliasResolveEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
2
6
|
/**
|
|
3
7
|
* 一次别名展开。
|
|
4
8
|
*
|
|
@@ -10,4 +14,4 @@ import type { AliasEntry } from './alias-store.js';
|
|
|
10
14
|
*
|
|
11
15
|
* @returns 展开后的完整消息;无匹配时返回 undefined。
|
|
12
16
|
*/
|
|
13
|
-
export declare function resolveAlias(message: string, aliases: readonly
|
|
17
|
+
export declare function resolveAlias(message: string, aliases: readonly AliasResolveEntry[]): string | undefined;
|
package/dist/order/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
interface OrderEntry {
|
|
1
|
+
export interface OrderEntry {
|
|
2
2
|
id: string;
|
|
3
3
|
text: string;
|
|
4
4
|
createdAt: string;
|
|
5
5
|
usedAt?: string;
|
|
6
|
+
/** 可选别名名(该指令的 $name 触发词)。全局唯一,由 OrderStore 校验。 */
|
|
7
|
+
alias?: string;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Global persistent order store.
|
|
@@ -20,8 +22,13 @@ export declare class OrderStore {
|
|
|
20
22
|
private persist;
|
|
21
23
|
save(text: string): OrderEntry;
|
|
22
24
|
updateUsedAt(id: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* 给指令绑定别名(或解绑)。别名全局唯一,撞名抛错;名称必须通过
|
|
27
|
+
* `ALIAS_NAME_PATTERN`(字母/数字/下划线、不能数字开头、≤20 字符)。
|
|
28
|
+
* @returns 绑定后的条目。
|
|
29
|
+
*/
|
|
30
|
+
setAlias(id: string, name: string | undefined): OrderEntry | undefined;
|
|
23
31
|
get(): OrderEntry[];
|
|
24
32
|
has(id: string): boolean;
|
|
25
33
|
remove(id: string): void;
|
|
26
34
|
}
|
|
27
|
-
export {};
|
package/dist/order/index.js
CHANGED
|
@@ -4,6 +4,9 @@ import { atomicWriteJson } from '../persistence/atomic-write.js';
|
|
|
4
4
|
import { loadJsonFile } from '../persistence/load-json-file.js';
|
|
5
5
|
const MAX_TEXT_LENGTH = 200;
|
|
6
6
|
const MAX_ORDERS = 50;
|
|
7
|
+
/** 别名名称规则:`[A-Za-z_][A-Za-z0-9_]*`,≤20 字符,不能数字开头。 */
|
|
8
|
+
const ALIAS_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9
|
+
const MAX_ALIAS_NAME_LENGTH = 20;
|
|
7
10
|
/**
|
|
8
11
|
* Global persistent order store.
|
|
9
12
|
*
|
|
@@ -45,6 +48,11 @@ export class OrderStore {
|
|
|
45
48
|
};
|
|
46
49
|
if (typeof e.usedAt === 'string')
|
|
47
50
|
entry.usedAt = e.usedAt;
|
|
51
|
+
if (typeof e.alias === 'string' &&
|
|
52
|
+
ALIAS_NAME_PATTERN.test(e.alias) &&
|
|
53
|
+
e.alias.length <= MAX_ALIAS_NAME_LENGTH) {
|
|
54
|
+
entry.alias = e.alias;
|
|
55
|
+
}
|
|
48
56
|
valid.push(entry);
|
|
49
57
|
}
|
|
50
58
|
}
|
|
@@ -76,6 +84,31 @@ export class OrderStore {
|
|
|
76
84
|
this.persist();
|
|
77
85
|
}
|
|
78
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* 给指令绑定别名(或解绑)。别名全局唯一,撞名抛错;名称必须通过
|
|
89
|
+
* `ALIAS_NAME_PATTERN`(字母/数字/下划线、不能数字开头、≤20 字符)。
|
|
90
|
+
* @returns 绑定后的条目。
|
|
91
|
+
*/
|
|
92
|
+
setAlias(id, name) {
|
|
93
|
+
const entry = this.data.find((e) => e.id === id);
|
|
94
|
+
if (!entry)
|
|
95
|
+
return undefined;
|
|
96
|
+
if (name === undefined || name === '') {
|
|
97
|
+
delete entry.alias;
|
|
98
|
+
this.persist();
|
|
99
|
+
return entry;
|
|
100
|
+
}
|
|
101
|
+
if (!ALIAS_NAME_PATTERN.test(name) || name.length > MAX_ALIAS_NAME_LENGTH) {
|
|
102
|
+
throw new Error(`别名名称只能包含字母/数字/下划线,不能数字开头,且不超过 ${MAX_ALIAS_NAME_LENGTH} 字符`);
|
|
103
|
+
}
|
|
104
|
+
const existing = this.data.find((e) => e.id !== id && e.alias === name);
|
|
105
|
+
if (existing) {
|
|
106
|
+
throw new Error(`别名 $${name} 已被其他指令占用`);
|
|
107
|
+
}
|
|
108
|
+
entry.alias = name;
|
|
109
|
+
this.persist();
|
|
110
|
+
return entry;
|
|
111
|
+
}
|
|
79
112
|
get() {
|
|
80
113
|
return [...this.data];
|
|
81
114
|
}
|
|
@@ -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
|
-
|
|
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:
|
|
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
|
+
}
|
|
@@ -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
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import type { AgentKind } from '../../runner/types.js';
|
|
8
8
|
import type { AgentConfigCardBuilder } from './types.js';
|
|
9
9
|
/**
|
|
10
|
-
* 首次启动默认展示的 Coding Agent 顺序:Codex → Claude → OpenCode → Pi → Kimi。
|
|
10
|
+
* 首次启动默认展示的 Coding Agent 顺序:Codex → Claude → OpenCode → Pi → Kimi → DSH。
|
|
11
11
|
* 未安装(availability === false)的 agent 在展示时排到后面
|
|
12
12
|
* (见 sortAgentsForDisplay),同组内保持本顺序。
|
|
13
13
|
*/
|