lark-remote 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/bridge/approval-coordinator.d.ts +15 -1
- package/dist/bridge/approval-coordinator.js +35 -2
- package/dist/bridge/index.d.ts +19 -6
- package/dist/bridge/index.js +25 -8
- package/dist/card/approval-render.js +187 -45
- 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 +28 -14
- 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 +20 -1
- package/dist/router/index.js +113 -7
- package/dist/router/utils.js +12 -2
- package/dist/runner/agent-choices-common.js +3 -0
- package/dist/runner/claude/session.js +24 -5
- 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/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 +191 -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 +69 -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 +2 -11
- 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 +63 -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 +39 -0
- package/dist/session/dsh/sessions.js +237 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/workspace/index.d.ts +4 -0
- package/dist/workspace/index.js +8 -1
- package/package.json +2 -1
|
@@ -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
|
*/
|
|
@@ -9,8 +9,9 @@ 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';
|
|
12
13
|
/**
|
|
13
|
-
* 首次启动默认展示的 Coding Agent 顺序:Codex → Claude → OpenCode → Pi → Kimi。
|
|
14
|
+
* 首次启动默认展示的 Coding Agent 顺序:Codex → Claude → OpenCode → Pi → Kimi → DSH。
|
|
14
15
|
* 未安装(availability === false)的 agent 在展示时排到后面
|
|
15
16
|
* (见 sortAgentsForDisplay),同组内保持本顺序。
|
|
16
17
|
*/
|
|
@@ -20,6 +21,7 @@ export const DEFAULT_AGENT_ORDER = [
|
|
|
20
21
|
'opencode',
|
|
21
22
|
'pi',
|
|
22
23
|
'kimi',
|
|
24
|
+
'dsh',
|
|
23
25
|
];
|
|
24
26
|
/** Registry of config builders, keyed by agent kind. */
|
|
25
27
|
const builders = {
|
|
@@ -28,6 +30,7 @@ const builders = {
|
|
|
28
30
|
pi: new PiConfigBuilder(),
|
|
29
31
|
opencode: new OpencodeConfigBuilder(),
|
|
30
32
|
kimi: new KimiConfigBuilder(),
|
|
33
|
+
dsh: new DshConfigBuilder(),
|
|
31
34
|
};
|
|
32
35
|
/**
|
|
33
36
|
* Get the config builder for a specific agent.
|
|
@@ -49,4 +49,10 @@ export interface AgentConfigCardBuilder {
|
|
|
49
49
|
key: string;
|
|
50
50
|
value: unknown;
|
|
51
51
|
}>;
|
|
52
|
+
/**
|
|
53
|
+
* 可选的异步预取钩子:卡片渲染前拉取动态选项(如 DSH 的模型/预设目录)。
|
|
54
|
+
* host 为该 agent 的当前连接地址;实现内部应缓存(同一 host 只拉一次),
|
|
55
|
+
* 失败静默回退固定兜底,不阻断卡片渲染。非异步预取的 agent 可不实现。
|
|
56
|
+
*/
|
|
57
|
+
prefetch?(host?: string): Promise<void>;
|
|
52
58
|
}
|
package/dist/router/index.d.ts
CHANGED
|
@@ -72,6 +72,8 @@ export interface CardActionPayload {
|
|
|
72
72
|
selected?: boolean;
|
|
73
73
|
/** Claude AskUserQuestion 问题索引(approval.answer / approval.answerSubmit)。 */
|
|
74
74
|
questionIndex?: number;
|
|
75
|
+
/** 多选组件(multi_select_static 等)回传的选中值数组(预留公共层能力)。 */
|
|
76
|
+
options?: string[];
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
77
79
|
* Response a cardAction handler may return to give the clicking user native
|
|
@@ -89,7 +91,7 @@ interface CardActionResponse {
|
|
|
89
91
|
}
|
|
90
92
|
export declare class CommandRouter {
|
|
91
93
|
/** Valid agent kinds — single source of truth for resume.use / resume.page / cmdResume. */
|
|
92
|
-
static readonly VALID_AGENTS: readonly ["claude", "codex", "opencode", "pi", "kimi"];
|
|
94
|
+
static readonly VALID_AGENTS: readonly ["claude", "codex", "opencode", "pi", "kimi", "dsh"];
|
|
93
95
|
private sessionStore;
|
|
94
96
|
/** 飞书桥接实例(public:测试直接访问断言,替代 as unknown as)。 */
|
|
95
97
|
bridge: Bridge;
|
|
@@ -203,6 +205,9 @@ export declare class CommandRouter {
|
|
|
203
205
|
* Routes to the appropriate bridge method based on value.cmd.
|
|
204
206
|
* Returns a toast response for immediate user feedback.
|
|
205
207
|
*/
|
|
208
|
+
/** 答案类操作的统一失败反馈:重复投递(同一 nonce 第二次点击)是已生效的
|
|
209
|
+
* 中性事件,报 error 会误导(首次点击实际已成功)。 */
|
|
210
|
+
private answerFailureToast;
|
|
206
211
|
private handleApprovalAction;
|
|
207
212
|
/**
|
|
208
213
|
* Handle queue.cancel: remove this message from the queue.
|
|
@@ -434,6 +439,14 @@ export declare class CommandRouter {
|
|
|
434
439
|
* /resume 核心实现(public:handleResumePage / handle / resume.use 共用,
|
|
435
440
|
* 也是测试的直接 seam,避免测试用 @ts-expect-error 摸私有方法)。
|
|
436
441
|
*/
|
|
442
|
+
/**
|
|
443
|
+
* DSH /resume preset 一致性校验:返回 mismatch 详情,或 undefined(一致/不可判定)。
|
|
444
|
+
*
|
|
445
|
+
* session 的 preset 在创建时固定(§2.1)。通过 listSessions 拿到目标 session 的
|
|
446
|
+
* agentPreset 与当前配置比对:不一致时不静默复用(返回提示),一致或查询失败
|
|
447
|
+
* (无法判定)时不阻断,避免 DSH 离线导致 /resume 完全不可用。
|
|
448
|
+
*/
|
|
449
|
+
private dshPresetMismatch;
|
|
437
450
|
cmdResume(args: string[], ctx: CommandContext, offset?: number): CommandResult;
|
|
438
451
|
private cmdActive;
|
|
439
452
|
/**
|
|
@@ -444,6 +457,12 @@ export declare class CommandRouter {
|
|
|
444
457
|
private buildActiveCardFromMemory;
|
|
445
458
|
private cmdReconnect;
|
|
446
459
|
private cmdConfig;
|
|
460
|
+
/**
|
|
461
|
+
* DSH /config 卡片构建前异步预取模型/预设目录(§4.2 动态清单)。
|
|
462
|
+
* 只在 defaultAgent 为 dsh 时生效;其他 agent 无 prefetch 钩子直接跳过。
|
|
463
|
+
* 预取失败不阻断——DshConfigBuilder 内部已回退固定兜底清单。
|
|
464
|
+
*/
|
|
465
|
+
private prefetchConfigCardCatalogs;
|
|
447
466
|
/** Build an interactive config card with CardKit 2.0 tabs + batch save */
|
|
448
467
|
buildConfigCard(): CommandResult;
|
|
449
468
|
/** /order 实现(public:测试直接调用,替代 as unknown as)。 */
|
package/dist/router/index.js
CHANGED
|
@@ -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 YAML from 'yaml';
|
|
5
|
-
import { getConfigDir, getConfigValue, setConfigValue, setConfigValues, mapAgentKey, assertSafeKeyPart, walkNestedContainer, } from '../config/index.js';
|
|
5
|
+
import { getConfigDir, getConfigValue, setConfigValue, setConfigValues, mapAgentKey, assertSafeKeyPart, walkNestedContainer, getAgentConfig, } from '../config/index.js';
|
|
6
6
|
import { syncAgentChoices } from '../runner/index.js';
|
|
7
7
|
import { WorkspaceStore } from '../workspace/index.js';
|
|
8
8
|
import { OrderStore } from '../order/index.js';
|
|
@@ -121,11 +121,12 @@ export function isImmediateAction(cmd) {
|
|
|
121
121
|
cmd === 'approval.toggle' ||
|
|
122
122
|
cmd === 'approval.answer' ||
|
|
123
123
|
cmd === 'approval.answerSubmit' ||
|
|
124
|
-
cmd === 'approval.answerCustom'
|
|
124
|
+
cmd === 'approval.answerCustom' ||
|
|
125
|
+
cmd === 'approval.answerNote');
|
|
125
126
|
}
|
|
126
127
|
export class CommandRouter {
|
|
127
128
|
/** Valid agent kinds — single source of truth for resume.use / resume.page / cmdResume. */
|
|
128
|
-
static VALID_AGENTS = ['claude', 'codex', 'opencode', 'pi', 'kimi'];
|
|
129
|
+
static VALID_AGENTS = ['claude', 'codex', 'opencode', 'pi', 'kimi', 'dsh'];
|
|
129
130
|
sessionStore;
|
|
130
131
|
/** 飞书桥接实例(public:测试直接访问断言,替代 as unknown as)。 */
|
|
131
132
|
bridge;
|
|
@@ -365,6 +366,7 @@ export class CommandRouter {
|
|
|
365
366
|
case 'approval.answer':
|
|
366
367
|
case 'approval.answerSubmit':
|
|
367
368
|
case 'approval.answerCustom':
|
|
369
|
+
case 'approval.answerNote':
|
|
368
370
|
return this.handleApprovalAction(value, ctx);
|
|
369
371
|
case 'codex.compact':
|
|
370
372
|
await this.bridge.handleCodexCompact(value, ctx);
|
|
@@ -403,6 +405,14 @@ export class CommandRouter {
|
|
|
403
405
|
* Routes to the appropriate bridge method based on value.cmd.
|
|
404
406
|
* Returns a toast response for immediate user feedback.
|
|
405
407
|
*/
|
|
408
|
+
/** 答案类操作的统一失败反馈:重复投递(同一 nonce 第二次点击)是已生效的
|
|
409
|
+
* 中性事件,报 error 会误导(首次点击实际已成功)。 */
|
|
410
|
+
answerFailureToast(msg) {
|
|
411
|
+
if (/already submitted \(duplicate nonce\)/.test(msg)) {
|
|
412
|
+
return { toast: { type: 'info', content: '该选项已处理,请勿重复点击' } };
|
|
413
|
+
}
|
|
414
|
+
return { toast: { type: 'error', content: `答案提交失败:${msg}` } };
|
|
415
|
+
}
|
|
406
416
|
async handleApprovalAction(value, _ctx) {
|
|
407
417
|
const { runId, requestId, decision, nonce, permId } = value;
|
|
408
418
|
if (value.cmd === 'approval.respond') {
|
|
@@ -465,7 +475,7 @@ export class CommandRouter {
|
|
|
465
475
|
}
|
|
466
476
|
catch (err) {
|
|
467
477
|
const msg = err instanceof Error ? err.message : String(err);
|
|
468
|
-
return
|
|
478
|
+
return this.answerFailureToast(msg);
|
|
469
479
|
}
|
|
470
480
|
}
|
|
471
481
|
if (value.cmd === 'approval.answerSubmit') {
|
|
@@ -483,7 +493,7 @@ export class CommandRouter {
|
|
|
483
493
|
}
|
|
484
494
|
catch (err) {
|
|
485
495
|
const msg = err instanceof Error ? err.message : String(err);
|
|
486
|
-
return
|
|
496
|
+
return this.answerFailureToast(msg);
|
|
487
497
|
}
|
|
488
498
|
}
|
|
489
499
|
if (value.cmd === 'approval.answerCustom') {
|
|
@@ -506,7 +516,30 @@ export class CommandRouter {
|
|
|
506
516
|
}
|
|
507
517
|
catch (err) {
|
|
508
518
|
const msg = err instanceof Error ? err.message : String(err);
|
|
509
|
-
return
|
|
519
|
+
return this.answerFailureToast(msg);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (value.cmd === 'approval.answerNote') {
|
|
523
|
+
if (!runId ||
|
|
524
|
+
requestId === undefined ||
|
|
525
|
+
value.questionIndex === undefined ||
|
|
526
|
+
!nonce ||
|
|
527
|
+
!value.inputValue) {
|
|
528
|
+
return { toast: { type: 'error', content: '缺少补充说明参数' } };
|
|
529
|
+
}
|
|
530
|
+
try {
|
|
531
|
+
await this.bridge.handleApprovalAnswerNote({
|
|
532
|
+
runId,
|
|
533
|
+
requestId,
|
|
534
|
+
questionIndex: value.questionIndex,
|
|
535
|
+
text: value.inputValue,
|
|
536
|
+
nonce,
|
|
537
|
+
});
|
|
538
|
+
return { toast: { type: 'success', content: '补充说明已保存' } };
|
|
539
|
+
}
|
|
540
|
+
catch (err) {
|
|
541
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
542
|
+
return this.answerFailureToast(msg);
|
|
510
543
|
}
|
|
511
544
|
}
|
|
512
545
|
return { toast: { type: 'error', content: '未知的审批操作' } };
|
|
@@ -1144,7 +1177,7 @@ export class CommandRouter {
|
|
|
1144
1177
|
// immediate action the callback response is swallowed by enqueueImmediate
|
|
1145
1178
|
// (fire-and-forget), and even if delivered it is transient — the user gets
|
|
1146
1179
|
// no perceivable feedback on the switch. Same rationale as config.save
|
|
1147
|
-
// agent-switch notice
|
|
1180
|
+
// agent-switch notice: persistent message first, toast only as a
|
|
1148
1181
|
// fallback if sendResult fails.
|
|
1149
1182
|
let fallbackToast;
|
|
1150
1183
|
if (useResult.card || useResult.text) {
|
|
@@ -1377,6 +1410,8 @@ export class CommandRouter {
|
|
|
1377
1410
|
// 2026-08-03: 卡片刷新失败不吞切换通知——保存已成功,仅记录日志,
|
|
1378
1411
|
// 后续切换消息照常发送;真正写盘/传播阶段的失败才走外层 catch 报「保存失败」。
|
|
1379
1412
|
try {
|
|
1413
|
+
// host 可能已变更 → 重新预取 DSH 目录(内部按 host 缓存,未变不重复拉)
|
|
1414
|
+
await this.prefetchConfigCardCatalogs();
|
|
1380
1415
|
await this.bridge.updateCardInPlace(this.buildConfigCard().card, ctx);
|
|
1381
1416
|
}
|
|
1382
1417
|
catch (refreshErr) {
|
|
@@ -1553,6 +1588,24 @@ export class CommandRouter {
|
|
|
1553
1588
|
switchSessionId = '';
|
|
1554
1589
|
}
|
|
1555
1590
|
}
|
|
1591
|
+
// DSH preset 变更 = 下次 run 新建 session(§4.3 D1):
|
|
1592
|
+
// preset 在 session 创建时固定,中途切换会被服务端拒绝(agent-preset-conflict)。
|
|
1593
|
+
// 旧 sessionId 存入 previousSessions 停车位(可手动 /resume,但 /resume 会做
|
|
1594
|
+
// preset 一致性校验),当前 sessionId 清空——下次 run 走 session.create 新建。
|
|
1595
|
+
const hasDshPresetChange = Object.keys(updates).some((k) => k === 'agents.dsh.agentPreset');
|
|
1596
|
+
// 已发生 agent 切换时(switchNotice 已设置)preset 分支跳过,避免覆盖切换 notice;
|
|
1597
|
+
// 切换分支对 dsh 的 session 已做恢复/清空处理。
|
|
1598
|
+
if (hasDshPresetChange && this.config.defaultAgent === 'dsh' && !switchNotice) {
|
|
1599
|
+
const oldSessionId = this.sessionStore.getSessionId(ctx.userId, 'dsh');
|
|
1600
|
+
if (oldSessionId) {
|
|
1601
|
+
this.sessionStore.setPreviousSessionId(ctx.userId, 'dsh', oldSessionId);
|
|
1602
|
+
this.sessionStore.clearSessionId(ctx.userId, 'dsh');
|
|
1603
|
+
switchNotice = `DSH preset 已变更,旧会话已停放到恢复槽(可 /resume 校验后恢复),下次消息将新建会话`;
|
|
1604
|
+
}
|
|
1605
|
+
else {
|
|
1606
|
+
switchNotice = `DSH preset 已变更,下次消息将新建会话`;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1556
1609
|
// 2026-07-13: 同步 agent 配置到 agentChoices(用于切换 agent 时恢复配置)
|
|
1557
1610
|
const currentAgent = this.config.defaultAgent;
|
|
1558
1611
|
const currentAgentConfigKey = `agents.${currentAgent}`;
|
|
@@ -2579,6 +2632,32 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2579
2632
|
* /resume 核心实现(public:handleResumePage / handle / resume.use 共用,
|
|
2580
2633
|
* 也是测试的直接 seam,避免测试用 @ts-expect-error 摸私有方法)。
|
|
2581
2634
|
*/
|
|
2635
|
+
/**
|
|
2636
|
+
* DSH /resume preset 一致性校验:返回 mismatch 详情,或 undefined(一致/不可判定)。
|
|
2637
|
+
*
|
|
2638
|
+
* session 的 preset 在创建时固定(§2.1)。通过 listSessions 拿到目标 session 的
|
|
2639
|
+
* agentPreset 与当前配置比对:不一致时不静默复用(返回提示),一致或查询失败
|
|
2640
|
+
* (无法判定)时不阻断,避免 DSH 离线导致 /resume 完全不可用。
|
|
2641
|
+
*/
|
|
2642
|
+
dshPresetMismatch(reader, sessionId, cwd) {
|
|
2643
|
+
const currentPreset = this.config.agents?.dsh?.agentPreset;
|
|
2644
|
+
if (!currentPreset)
|
|
2645
|
+
return undefined; // 配置跟随服务端默认,无法判定 → 放行
|
|
2646
|
+
let sessionPreset;
|
|
2647
|
+
try {
|
|
2648
|
+
const { sessions } = reader.listSessions(cwd, { limit: 100 });
|
|
2649
|
+
sessionPreset = sessions.find((s) => s.sessionId === sessionId)?.agentPreset;
|
|
2650
|
+
}
|
|
2651
|
+
catch (err) {
|
|
2652
|
+
getLogger().warn(`[router] dshPresetMismatch listSessions failed for ${sessionId}: ${err.message}`);
|
|
2653
|
+
return undefined;
|
|
2654
|
+
}
|
|
2655
|
+
if (!sessionPreset)
|
|
2656
|
+
return undefined; // 服务端未返回 preset → 无法判定,放行
|
|
2657
|
+
if (sessionPreset === currentPreset)
|
|
2658
|
+
return undefined;
|
|
2659
|
+
return { sessionPreset, currentPreset };
|
|
2660
|
+
}
|
|
2582
2661
|
cmdResume(args, ctx, offset = 0) {
|
|
2583
2662
|
const entry = this.sessionStore.get(ctx.userId);
|
|
2584
2663
|
const cwd = entry?.cwd;
|
|
@@ -2625,6 +2704,19 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2625
2704
|
text: `未找到 session ${sessionIdArg}(当前目录: ${cwd})。请确认该 session 属于此目录,或先 /cd 到正确目录。`,
|
|
2626
2705
|
};
|
|
2627
2706
|
}
|
|
2707
|
+
// DSH preset 一致性校验(§6 风险 1):session 的 preset 在创建时固定,
|
|
2708
|
+
// 若与当前配置不符,直接复用会让后续 run 报 agent-preset-conflict。
|
|
2709
|
+
// 不静默复用——明确提示用户,由用户决定是否继续(配置里手动改/清 preset)。
|
|
2710
|
+
if (agentKind === 'dsh' && this.config.defaultAgent === 'dsh') {
|
|
2711
|
+
const presetMismatch = this.dshPresetMismatch(reader, sessionIdArg, cwd);
|
|
2712
|
+
if (presetMismatch) {
|
|
2713
|
+
return {
|
|
2714
|
+
text: `⚠️ 该 DSH session 的 preset 是「${presetMismatch.sessionPreset}」,与当前配置「${presetMismatch.currentPreset}」不一致。\n` +
|
|
2715
|
+
`preset 在 session 创建时固定,无法中途切换(DSH 返回 agent-preset-conflict)。\n` +
|
|
2716
|
+
`继续恢复将在下次 run 失败。请在 /config 中把预设模式改回「${presetMismatch.sessionPreset}」后重试,或保持当前配置并发新消息新建会话。`,
|
|
2717
|
+
};
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2628
2720
|
// L3: 校验通过后才写入
|
|
2629
2721
|
this.sessionStore.setSessionId(ctx.userId, agentKind, sessionIdArg, cwd);
|
|
2630
2722
|
const { events: content, usage, isActive, aiTitle, recap, displayTitle, activeRunRunId, } = this.readSessionDisplayState(sessionIdArg, cwd, {
|
|
@@ -2984,6 +3076,8 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2984
3076
|
async cmdConfig(args, ctx) {
|
|
2985
3077
|
// Ensure probe cache is populated before building the card.
|
|
2986
3078
|
await probeAllAgents();
|
|
3079
|
+
// 动态清单预取(DSH 模型/预设目录):卡渲染前拉取,失败回退兜底清单
|
|
3080
|
+
await this.prefetchConfigCardCatalogs();
|
|
2987
3081
|
// Check if this is a direct set command: /config <key> <value>
|
|
2988
3082
|
// Command-style set: immediate write to disk, clear pendingConfig
|
|
2989
3083
|
if (args.length >= 2) {
|
|
@@ -3030,6 +3124,18 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3030
3124
|
this.ensurePendingConfig();
|
|
3031
3125
|
return this.buildConfigCard();
|
|
3032
3126
|
}
|
|
3127
|
+
/**
|
|
3128
|
+
* DSH /config 卡片构建前异步预取模型/预设目录(§4.2 动态清单)。
|
|
3129
|
+
* 只在 defaultAgent 为 dsh 时生效;其他 agent 无 prefetch 钩子直接跳过。
|
|
3130
|
+
* 预取失败不阻断——DshConfigBuilder 内部已回退固定兜底清单。
|
|
3131
|
+
*/
|
|
3132
|
+
async prefetchConfigCardCatalogs() {
|
|
3133
|
+
if (this.config.defaultAgent !== 'dsh')
|
|
3134
|
+
return;
|
|
3135
|
+
const builder = getConfigBuilder('dsh');
|
|
3136
|
+
const dshConf = getAgentConfig(this.config, 'dsh');
|
|
3137
|
+
await builder.prefetch?.(dshConf?.host);
|
|
3138
|
+
}
|
|
3033
3139
|
/** Build an interactive config card with CardKit 2.0 tabs + batch save */
|
|
3034
3140
|
buildConfigCard() {
|
|
3035
3141
|
// 使用 pendingConfig(若存在)或当前 config
|
package/dist/router/utils.js
CHANGED
|
@@ -80,6 +80,16 @@ export function formatUsageStats(usage, options) {
|
|
|
80
80
|
const cumTotalSuffix = usage?.cumulativeTotalTokens !== undefined
|
|
81
81
|
? ` · 累计 ${formatTokenK(usage.cumulativeTotalTokens)}`
|
|
82
82
|
: '';
|
|
83
|
+
// 一致性护栏:本 run ≤ 累计 是硬不变量(累计 = 所有 run 之和,必 ≥ 本 run)。
|
|
84
|
+
// 违反说明来源 scope 混用(如 DeepSeek live result usage 是累积/超大 scope,
|
|
85
|
+
// 大于 jsonl 去重聚合的 session 累计)。只标记不修正——数字保持原样便于排查,
|
|
86
|
+
// 由数据源修复。两侧都有值时校验(避免旧数据/缺失字段误报)。
|
|
87
|
+
// 两个分支(real input/output / 10% 估计)共用,保证"本 run > 累计"在任意
|
|
88
|
+
// 路径都显式暴露。
|
|
89
|
+
const withCumGuard = (totalTokens) => cumTotalSuffix +
|
|
90
|
+
(usage?.cumulativeTotalTokens !== undefined && totalTokens > usage.cumulativeTotalTokens
|
|
91
|
+
? ' · ⚠️ 累计异常'
|
|
92
|
+
: '');
|
|
83
93
|
// 计算累计 cache 命中率
|
|
84
94
|
const cumInput = usage?.cumulativeInputTokens;
|
|
85
95
|
const cumCacheRead = usage?.cumulativeCacheReadTokens;
|
|
@@ -110,7 +120,7 @@ export function formatUsageStats(usage, options) {
|
|
|
110
120
|
lines.push(`Cache create - ${formatTokenK(cacheCreation)}${cumCacheCreateSuffix}`);
|
|
111
121
|
}
|
|
112
122
|
lines.push(`Cached token - ${formatTokenK(cacheRead)} (${cachePercent}%)${cumCacheReadSuffix}`);
|
|
113
|
-
lines.push(`Total token - ${formatTokenK(totalTokens)}${
|
|
123
|
+
lines.push(`Total token - ${formatTokenK(totalTokens)}${withCumGuard(totalTokens)}`);
|
|
114
124
|
}
|
|
115
125
|
else {
|
|
116
126
|
// Estimate path: when real input/output are absent,
|
|
@@ -127,7 +137,7 @@ export function formatUsageStats(usage, options) {
|
|
|
127
137
|
lines.push(`Cache create - ${formatTokenK(cacheCreation)}${cumCacheCreateSuffix}`);
|
|
128
138
|
}
|
|
129
139
|
lines.push(`Cached token - ${formatTokenK(cacheRead)} (${cachePercent}%)${cumCacheReadSuffix}`);
|
|
130
|
-
lines.push(`Total token - ${formatTokenK(totalTokens)}${
|
|
140
|
+
lines.push(`Total token - ${formatTokenK(totalTokens)}${withCumGuard(totalTokens)}`);
|
|
131
141
|
}
|
|
132
142
|
}
|
|
133
143
|
return lines.join('\n');
|
|
@@ -23,6 +23,9 @@ export const AGENT_CHOICE_FIELDS = {
|
|
|
23
23
|
{ configKey: 'model', choicesKey: 'model' },
|
|
24
24
|
{ configKey: 'thinkingEffort', choicesKey: 'thinkingEffort' },
|
|
25
25
|
],
|
|
26
|
+
// DSH has no model/provider choice fields (host endpoint is connection
|
|
27
|
+
// config, not a per-run choice) — empty to satisfy the Record type.
|
|
28
|
+
dsh: [],
|
|
26
29
|
};
|
|
27
30
|
/** 返回指定 agent 的字段映射;claude 或未知 agent 返回 undefined。 */
|
|
28
31
|
export function choiceFieldsFor(agent) {
|
|
@@ -32,6 +32,7 @@ import { getLogger } from '../../logger/index.js';
|
|
|
32
32
|
import { SpawningRunner } from '../common/spawning-runner.js';
|
|
33
33
|
import { pipeAllStdio } from '../common/runner-utils.js';
|
|
34
34
|
import { authErrorEvent, syntheticInitEvent } from '../common/runner-utils.js';
|
|
35
|
+
import { makeQuestionApprovalEvent } from '../question-common.js';
|
|
35
36
|
/** 拒绝权限时的默认提示(进入 claude 上下文,中文用户可读)。 */
|
|
36
37
|
const DENY_MESSAGE = '用户拒绝了此工具调用,请停止并等待用户指令。';
|
|
37
38
|
/** 会话级空闲回收默认 TTL(对齐 codex appServer.idleTtlMs 默认 30 分钟)。 */
|
|
@@ -639,17 +640,30 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
639
640
|
this.pendingToolInputs.delete(requestId);
|
|
640
641
|
return [];
|
|
641
642
|
}
|
|
643
|
+
return [makeQuestionApprovalEvent(requestId, questions, this.sessionId)];
|
|
644
|
+
}
|
|
645
|
+
// ExitPlanMode:语义是「退出计划模式」的通知型工具,input 天然为空(plan
|
|
646
|
+
// 内容在 plan 文件 + 前置 thinking/text 里,不走 tool input)。它没有
|
|
647
|
+
// command 语义,塞进 command 槽位会显示无意义 `{}` → 用独立 tool kind,
|
|
648
|
+
// 标题/工具名 + reason 表达审批内容。
|
|
649
|
+
if (toolName === 'ExitPlanMode') {
|
|
650
|
+
const rawInput = JSON.stringify(input);
|
|
642
651
|
const view = {
|
|
643
652
|
requestId,
|
|
644
|
-
kind: '
|
|
645
|
-
|
|
646
|
-
|
|
653
|
+
kind: 'tool',
|
|
654
|
+
toolName,
|
|
655
|
+
reason: typeof request.description === 'string' ? request.description : undefined,
|
|
656
|
+
// 非空 input 携带给渲染层(当前 ExitPlanMode 为 `{}`,渲染层不展示)。
|
|
657
|
+
...(rawInput && rawInput !== '{}' ? { toolInput: rawInput.slice(0, 500) } : {}),
|
|
658
|
+
// 计划审批没有「后续自动放行」语义,acceptAll 无意义;只留 accept/decline。
|
|
659
|
+
availableDecisions: ['accept', 'decline'],
|
|
647
660
|
};
|
|
661
|
+
getLogger().info(`[${this.logTag}] permission request requestId=${requestId} tool=${toolName}`);
|
|
648
662
|
return [
|
|
649
663
|
{
|
|
650
664
|
type: 'approval_requested',
|
|
651
665
|
requestId,
|
|
652
|
-
kind: '
|
|
666
|
+
kind: 'tool',
|
|
653
667
|
threadId: this.sessionId,
|
|
654
668
|
turnId: '',
|
|
655
669
|
itemId: '',
|
|
@@ -658,7 +672,12 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
658
672
|
},
|
|
659
673
|
];
|
|
660
674
|
}
|
|
661
|
-
|
|
675
|
+
// 非命令工具的兜底防御:input 无 command 字段时 JSON 序列化,但空对象
|
|
676
|
+
// 序列化成 `{}` 对用户无意义——内容为空时回退展示工具名(review P3-5)。
|
|
677
|
+
const rawCommand = typeof input.command === 'string' ? input.command : JSON.stringify(input);
|
|
678
|
+
const command = rawCommand && rawCommand !== '{}' && rawCommand !== 'null'
|
|
679
|
+
? rawCommand
|
|
680
|
+
: toolName || '(unknown tool)';
|
|
662
681
|
const view = {
|
|
663
682
|
requestId,
|
|
664
683
|
kind: 'command',
|
|
@@ -83,6 +83,8 @@ export interface ThreadResumeParams {
|
|
|
83
83
|
modelProvider?: string | null;
|
|
84
84
|
approvalPolicy?: AskForApproval;
|
|
85
85
|
sandbox?: SandboxMode;
|
|
86
|
+
/** Configuration overrides for the resumed thread (codex v2 wire field). */
|
|
87
|
+
config?: Record<string, unknown> | null;
|
|
86
88
|
}
|
|
87
89
|
export interface ThreadResumeResponse {
|
|
88
90
|
thread: Thread;
|
|
@@ -432,6 +434,7 @@ export declare const ServerRequestMethod: {
|
|
|
432
434
|
readonly COMMAND_EXECUTION_APPROVAL: "item/commandExecution/requestApproval";
|
|
433
435
|
readonly FILE_CHANGE_APPROVAL: "item/fileChange/requestApproval";
|
|
434
436
|
readonly PERMISSIONS_APPROVAL: "item/permissions/requestApproval";
|
|
437
|
+
readonly REQUEST_USER_INPUT: "item/tool/requestUserInput";
|
|
435
438
|
};
|
|
436
439
|
export interface NetworkApprovalContext {
|
|
437
440
|
host: string;
|
|
@@ -515,5 +518,37 @@ export interface PermissionsRequestApprovalResponse {
|
|
|
515
518
|
permissions: GrantedPermissionProfile;
|
|
516
519
|
scope?: 'turn' | 'session';
|
|
517
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Codex AskUserQuestion(request_user_input)协议形状。
|
|
523
|
+
* 来源:codex-rs/codex-rs/protocol/src/request_user_input.rs +
|
|
524
|
+
* app-server-protocol v2/item.rs ToolRequestUserInput*(EXPERIMENTAL)。
|
|
525
|
+
* isOther=true → 渲染层提供自定义答案输入;isSecret 仅影响展示(按普通输入
|
|
526
|
+
* 处理);options 为 null/空 → 自由文本题。
|
|
527
|
+
*/
|
|
528
|
+
export interface ToolRequestUserInputOption {
|
|
529
|
+
label: string;
|
|
530
|
+
description: string;
|
|
531
|
+
}
|
|
532
|
+
export interface ToolRequestUserInputQuestion {
|
|
533
|
+
id: string;
|
|
534
|
+
header: string;
|
|
535
|
+
question: string;
|
|
536
|
+
isOther: boolean;
|
|
537
|
+
isSecret: boolean;
|
|
538
|
+
options: ToolRequestUserInputOption[] | null;
|
|
539
|
+
}
|
|
540
|
+
export interface ToolRequestUserInputParams {
|
|
541
|
+
threadId: string;
|
|
542
|
+
turnId: string;
|
|
543
|
+
itemId: string;
|
|
544
|
+
questions: ToolRequestUserInputQuestion[];
|
|
545
|
+
autoResolutionMs?: number | null;
|
|
546
|
+
}
|
|
547
|
+
export interface ToolRequestUserInputAnswer {
|
|
548
|
+
answers: string[];
|
|
549
|
+
}
|
|
550
|
+
export interface ToolRequestUserInputResponse {
|
|
551
|
+
answers: Record<string, ToolRequestUserInputAnswer>;
|
|
552
|
+
}
|
|
518
553
|
/** Unsupported / ignored server request methods. */
|
|
519
554
|
export declare const UNSUPPORTED_SERVER_REQUEST_METHODS: Set<string>;
|