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
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
* Renders command/file/permissions approval areas with button mappings per §8.3.
|
|
5
5
|
* Uses CardKit 2.0 (no tag:"action" container — 200861 rule).
|
|
6
6
|
*/
|
|
7
|
+
import { collapsibleMarkdownPanel } from './collapsible.js';
|
|
8
|
+
import { truncateUtf8, truncateMarkdownTables } from './text-truncate.js';
|
|
7
9
|
/** Unique nonce for card button callbacks (SDK dedup + router validation). */
|
|
8
10
|
function mkNonce() {
|
|
9
11
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
10
12
|
}
|
|
13
|
+
/** 计划面板正文字节预算(折叠展示 + 截断保护 28KB 卡片预算)。 */
|
|
14
|
+
const PLAN_PANEL_BYTES = 6000;
|
|
11
15
|
/** reason div(3 处共用);反引号中和防 lark_md 解析错误(review P2-2)。 */
|
|
12
16
|
function reasonDiv(approval) {
|
|
13
17
|
if (!approval.reason)
|
|
@@ -42,6 +46,9 @@ export function renderApprovalArea(approval, opts) {
|
|
|
42
46
|
if (approval.kind === 'command') {
|
|
43
47
|
elements.push(...renderCommandApproval(approval, opts?.expired, opts?.runId));
|
|
44
48
|
}
|
|
49
|
+
else if (approval.kind === 'tool') {
|
|
50
|
+
elements.push(...renderToolApproval(approval, opts?.expired, opts?.runId));
|
|
51
|
+
}
|
|
45
52
|
else if (approval.kind === 'file') {
|
|
46
53
|
elements.push(...renderFileApproval(approval, opts?.expired, opts?.runId));
|
|
47
54
|
}
|
|
@@ -68,20 +75,26 @@ function renderDecisionButtons(approval, expired, runId) {
|
|
|
68
75
|
},
|
|
69
76
|
];
|
|
70
77
|
}
|
|
78
|
+
// ExitPlanMode 计划审批用 TUI 语义文案;其他审批保持原文案。
|
|
79
|
+
const isPlanExit = approval.kind === 'tool' && approval.toolName === 'ExitPlanMode';
|
|
71
80
|
const BUTTONS = {
|
|
72
|
-
accept: { label: '✅ 允许', type: 'primary' },
|
|
81
|
+
accept: { label: isPlanExit ? '✅ 批准并执行' : '✅ 允许', type: 'primary' },
|
|
73
82
|
acceptForSession: { label: '✅ 允许本次会话', type: 'primary' },
|
|
74
83
|
acceptWithExecpolicyAmendment: { label: '🔒 允许并记住命令', type: 'primary' },
|
|
75
|
-
acceptAll: { label: '✅ 允许所有', type: 'primary' },
|
|
84
|
+
acceptAll: { label: isPlanExit ? '✅ 批准并自动放行' : '✅ 允许所有', type: 'primary' },
|
|
85
|
+
acceptWithFeedback: { label: '✍️ 批准并采纳修改', type: 'primary' },
|
|
86
|
+
declineWithFeedback: { label: '✏️ 拒绝并附意见', type: 'default' },
|
|
76
87
|
decline: { label: '❌ 拒绝', type: 'danger' },
|
|
77
88
|
};
|
|
78
89
|
// Fixed presentation order; only offer decisions the protocol listed
|
|
79
|
-
// (accept/decline are always available;
|
|
90
|
+
// (accept/decline are always available; 其余按 availableDecisions 出现)。
|
|
80
91
|
const offered = [
|
|
81
92
|
'accept',
|
|
82
93
|
'acceptForSession',
|
|
83
94
|
'acceptWithExecpolicyAmendment',
|
|
84
95
|
'acceptAll',
|
|
96
|
+
'acceptWithFeedback',
|
|
97
|
+
'declineWithFeedback',
|
|
85
98
|
'decline',
|
|
86
99
|
].filter((d) => d === 'accept' || d === 'decline' || approval.availableDecisions.includes(d));
|
|
87
100
|
// 决策按钮逐个作为 body 直接元素返回(CardKit body 纵向堆叠 = 上下排列)。
|
|
@@ -106,6 +119,129 @@ function renderDecisionButtons(approval, expired, runId) {
|
|
|
106
119
|
}));
|
|
107
120
|
}
|
|
108
121
|
// =============================================================================
|
|
122
|
+
// Tool Approval (non-command tools, e.g. ExitPlanMode)
|
|
123
|
+
// =============================================================================
|
|
124
|
+
/**
|
|
125
|
+
* 通用工具权限请求(kind === 'tool'):展示工具名 + 用途说明(reason)+ 原始
|
|
126
|
+
* input(若有)。ExitPlanMode 走独立计划审批分支(计划全文 + 修改意见输入)。
|
|
127
|
+
*/
|
|
128
|
+
function renderToolApproval(approval, expired, runId) {
|
|
129
|
+
// ExitPlanMode:对齐 TUI「先看完整计划再交互」——计划全文折叠面板 +
|
|
130
|
+
// 修改意见输入 + 五决策按钮(批准并执行 / 批准并自动放行 / 拒绝并附意见 /
|
|
131
|
+
// 批准并采纳修改 / 拒绝)。
|
|
132
|
+
if (approval.toolName === 'ExitPlanMode') {
|
|
133
|
+
return renderPlanApproval(approval, expired, runId);
|
|
134
|
+
}
|
|
135
|
+
const elements = [];
|
|
136
|
+
elements.push({
|
|
137
|
+
tag: 'div',
|
|
138
|
+
text: {
|
|
139
|
+
tag: 'lark_md',
|
|
140
|
+
content: '**🔧 工具请求**',
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
const toolLabel = approval.toolName ?? '(unknown tool)';
|
|
144
|
+
elements.push({
|
|
145
|
+
tag: 'div',
|
|
146
|
+
text: { tag: 'lark_md', content: `🔧 \`${toolLabel}\`` },
|
|
147
|
+
});
|
|
148
|
+
pushIf(elements, reasonDiv(approval));
|
|
149
|
+
// 原始 input(非空时展示,截断保护卡片预算;ExitPlanMode 的 `{}` 不展示)。
|
|
150
|
+
if (approval.toolInput && approval.toolInput !== '{}') {
|
|
151
|
+
const neutralized = approval.toolInput.replace(/`/g, '·');
|
|
152
|
+
elements.push({
|
|
153
|
+
tag: 'div',
|
|
154
|
+
text: { tag: 'lark_md', content: `\`${neutralized.slice(0, 500)}\`` },
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
elements.push(...renderDecisionButtons(approval, expired, runId));
|
|
158
|
+
return elements;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* ExitPlanMode 计划审批区:
|
|
162
|
+
* - 计划全文(input.plan 或计划文件读取)折叠展示,反引号中和 + 表格限数 +
|
|
163
|
+
* 字节预算截断;
|
|
164
|
+
* - 计划文件路径(有则展示,含模型写错目录的退化场景);
|
|
165
|
+
* - 修改意见输入框(approval.planFeedback → coordinator 回流回显),供
|
|
166
|
+
* 「拒绝并附意见」/「批准并采纳修改」复用;
|
|
167
|
+
* - 决策按钮由 renderDecisionButtons 按 availableDecisions 渲染。
|
|
168
|
+
*/
|
|
169
|
+
function renderPlanApproval(approval, expired, runId) {
|
|
170
|
+
const elements = [];
|
|
171
|
+
elements.push({
|
|
172
|
+
tag: 'div',
|
|
173
|
+
text: { tag: 'lark_md', content: '**📋 计划审批**' },
|
|
174
|
+
});
|
|
175
|
+
pushIf(elements, reasonDiv(approval));
|
|
176
|
+
if (approval.planFilePath) {
|
|
177
|
+
const neutralizedPath = approval.planFilePath.replace(/`/g, '·');
|
|
178
|
+
elements.push({
|
|
179
|
+
tag: 'div',
|
|
180
|
+
text: { tag: 'lark_md', content: `📄 ${neutralizedPath}` },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (approval.plan) {
|
|
184
|
+
// 反引号中和防 11311(同 reasonDiv/command 策略);表格限数防 11310;
|
|
185
|
+
// 字节预算截断防 28KB 超限。
|
|
186
|
+
const neutralized = approval.plan.replace(/`/g, '·');
|
|
187
|
+
const tableSafe = truncateMarkdownTables(neutralized);
|
|
188
|
+
const content = truncateUtf8(tableSafe, PLAN_PANEL_BYTES);
|
|
189
|
+
elements.push(collapsibleMarkdownPanel({
|
|
190
|
+
title: '📋 计划',
|
|
191
|
+
expanded: false,
|
|
192
|
+
border: 'blue',
|
|
193
|
+
content,
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
elements.push({
|
|
198
|
+
tag: 'div',
|
|
199
|
+
text: {
|
|
200
|
+
tag: 'lark_md',
|
|
201
|
+
content: '📋 未获取到计划文件内容(可参考上方正文摘要)',
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (!expired) {
|
|
206
|
+
elements.push(renderPlanFeedbackInput(approval.requestId, runId));
|
|
207
|
+
if (approval.planFeedback) {
|
|
208
|
+
const neutralized = approval.planFeedback.replace(/`/g, '·');
|
|
209
|
+
elements.push({
|
|
210
|
+
tag: 'div',
|
|
211
|
+
text: { tag: 'lark_md', content: `📝 ${neutralized}` },
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
elements.push(...renderDecisionButtons(approval, expired, runId));
|
|
216
|
+
return elements;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 计划修改意见输入行:输入后点击右侧 ✓ 提交图标,input_value 经 connector
|
|
220
|
+
* raw 事件回传(CardKit input 的 input_value 会被 SDK normalizer 丢弃,
|
|
221
|
+
* 必须走 includeRawEvent 路径,同 answerNote)。
|
|
222
|
+
*/
|
|
223
|
+
function renderPlanFeedbackInput(requestId, runId) {
|
|
224
|
+
return {
|
|
225
|
+
tag: 'input',
|
|
226
|
+
name: `plan-feedback-${requestId}`,
|
|
227
|
+
placeholder: {
|
|
228
|
+
tag: 'plain_text',
|
|
229
|
+
content: '填写修改意见(可配合「拒绝并附意见」/「批准并采纳修改」)…',
|
|
230
|
+
},
|
|
231
|
+
behaviors: [
|
|
232
|
+
{
|
|
233
|
+
type: 'callback',
|
|
234
|
+
value: {
|
|
235
|
+
cmd: 'approval.planFeedback',
|
|
236
|
+
requestId,
|
|
237
|
+
runId,
|
|
238
|
+
nonce: mkNonce(),
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// =============================================================================
|
|
109
245
|
// Command Approval
|
|
110
246
|
// =============================================================================
|
|
111
247
|
function renderCommandApproval(approval, expired, runId) {
|
|
@@ -259,6 +395,14 @@ function renderQuestionApproval(approval, expired, runId) {
|
|
|
259
395
|
tag: 'div',
|
|
260
396
|
text: { tag: 'lark_md', content: '**❓ 需要你回答**' },
|
|
261
397
|
});
|
|
398
|
+
// Kimi elicitation form:完整题干只在 form message 里合并出现,概要行展示
|
|
399
|
+
// 于提问卡顶部(数据驱动,Kimi runner 翻译时填充 approval.intro)。
|
|
400
|
+
if (approval.intro) {
|
|
401
|
+
elements.push({
|
|
402
|
+
tag: 'div',
|
|
403
|
+
text: { tag: 'lark_md', content: `📝 ${approval.intro}` },
|
|
404
|
+
});
|
|
405
|
+
}
|
|
262
406
|
if (expired) {
|
|
263
407
|
elements.push({
|
|
264
408
|
tag: 'div',
|
|
@@ -271,6 +415,7 @@ function renderQuestionApproval(approval, expired, runId) {
|
|
|
271
415
|
const q = questions[i];
|
|
272
416
|
const selected = q.selected ?? [];
|
|
273
417
|
const header = q.header || `问题 ${i + 1}`;
|
|
418
|
+
const freeText = (q.options?.length ?? 0) === 0;
|
|
274
419
|
elements.push({
|
|
275
420
|
tag: 'div',
|
|
276
421
|
text: {
|
|
@@ -278,34 +423,26 @@ function renderQuestionApproval(approval, expired, runId) {
|
|
|
278
423
|
content: `**${header}**${q.multiSelect ? '(可多选)' : ''}\n${q.question}`,
|
|
279
424
|
},
|
|
280
425
|
});
|
|
281
|
-
|
|
426
|
+
if (freeText) {
|
|
427
|
+
// 自由文本题(Codex options:null / Pi extension input):无选项行,
|
|
428
|
+
// 直接渲染题面 + 输入框,提交走 answerCustom(coordinator 不要求有选项)。
|
|
429
|
+
elements.push(renderAnswerInput(approval.requestId, i, q.placeholder ?? '输入回答…', runId));
|
|
430
|
+
// 已答回显:input_value 不跨卡片更新保留,多题场景下用户提交后若还有
|
|
431
|
+
// 其他问题未答,卡片必须展示已答文本(与自定义答案选中态同模式)。
|
|
432
|
+
const answered = q.selected?.find((s) => s.length > 0);
|
|
433
|
+
if (answered) {
|
|
434
|
+
elements.push({
|
|
435
|
+
tag: 'div',
|
|
436
|
+
text: { tag: 'lark_md', content: `✍️ 已答:${answered}` },
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
for (const option of q.options ?? []) {
|
|
282
442
|
const isSelected = selected.includes(option.label);
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
tag: 'lark_md',
|
|
287
|
-
content: `${isSelected ? '✅' : '⬜'} ${option.label}${option.description ? `\n_${option.description}_` : ''}`,
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
elements.push({
|
|
291
|
-
tag: 'button',
|
|
292
|
-
text: { tag: 'plain_text', content: isSelected ? '取消选择' : '选择' },
|
|
293
|
-
type: isSelected ? 'default' : 'primary',
|
|
294
|
-
size: 'small',
|
|
295
|
-
behaviors: [
|
|
296
|
-
{
|
|
297
|
-
type: 'callback',
|
|
298
|
-
value: {
|
|
299
|
-
cmd: 'approval.answer',
|
|
300
|
-
requestId: approval.requestId,
|
|
301
|
-
questionIndex: i,
|
|
302
|
-
option: option.label,
|
|
303
|
-
runId,
|
|
304
|
-
nonce: mkNonce(),
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
],
|
|
308
|
-
});
|
|
443
|
+
// 单选/多选图标区分:单选 ⚪/🔵(radio 隐喻),多选 ⬜/☑️(checkbox 隐喻)。
|
|
444
|
+
const icon = q.multiSelect ? (isSelected ? '☑️' : '⬜') : isSelected ? '🔵' : '⚪';
|
|
445
|
+
elements.push(renderOptionRow(approval.requestId, i, option, isSelected, icon, runId));
|
|
309
446
|
}
|
|
310
447
|
// 自定义答案(Other)的选中态展示:选项按钮无法表示自由文本,单独显示
|
|
311
448
|
// 已选文本,避免「点了没反应」的困惑(review P3)。
|
|
@@ -318,24 +455,21 @@ function renderQuestionApproval(approval, expired, runId) {
|
|
|
318
455
|
}
|
|
319
456
|
// review P3-4:单选问题提供自定义答案(Other)输入——输入文本后点击
|
|
320
457
|
// 输入框右侧 ✓ 提交图标,input_value 经 connector raw 事件回传。
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
},
|
|
337
|
-
],
|
|
338
|
-
});
|
|
458
|
+
// 显隐按 isOther !== false:Kimi form 会丢弃非声明选项值,翻译时置
|
|
459
|
+
// isOther=false 隐藏输入;Claude 未设置该字段默认显示(行为不变)。
|
|
460
|
+
if (!q.multiSelect && q.isOther !== false) {
|
|
461
|
+
elements.push(renderAnswerInput(approval.requestId, i, '自定义答案(Other)…', runId, 'approval.answerCustom'));
|
|
462
|
+
}
|
|
463
|
+
// Codex user_note:选项之外的补充说明(allowNote 数据驱动门控)。
|
|
464
|
+
// 提交走 answerNote(coordinator 记录 note,随答案一起提交),已填回显。
|
|
465
|
+
if (q.allowNote === true) {
|
|
466
|
+
elements.push(renderAnswerInput(approval.requestId, i, '补充说明(可选)…', runId, 'approval.answerNote'));
|
|
467
|
+
if (q.note) {
|
|
468
|
+
elements.push({
|
|
469
|
+
tag: 'div',
|
|
470
|
+
text: { tag: 'lark_md', content: `📝 ${q.note}` },
|
|
471
|
+
});
|
|
472
|
+
}
|
|
339
473
|
}
|
|
340
474
|
// 多选问题:有勾选才给提交按钮(单选在协调器内即时提交)。
|
|
341
475
|
if (q.multiSelect && selected.length > 0) {
|
|
@@ -358,6 +492,111 @@ function renderQuestionApproval(approval, expired, runId) {
|
|
|
358
492
|
});
|
|
359
493
|
}
|
|
360
494
|
}
|
|
495
|
+
// 提问卡底部统一「跳过回答」:走现有 approval.respond + decision=decline
|
|
496
|
+
// (coordinator 对 question 允许 decline 作为安全兜底),runner 按协议映射
|
|
497
|
+
// 跳过语义(Claude deny / Codex 空 answers / Kimi action decline / Pi cancelled)。
|
|
498
|
+
if (!expired) {
|
|
499
|
+
elements.push({
|
|
500
|
+
tag: 'button',
|
|
501
|
+
text: { tag: 'plain_text', content: '⏭️ 跳过回答' },
|
|
502
|
+
type: 'default',
|
|
503
|
+
behaviors: [
|
|
504
|
+
{
|
|
505
|
+
type: 'callback',
|
|
506
|
+
value: {
|
|
507
|
+
cmd: 'approval.respond',
|
|
508
|
+
decision: 'decline',
|
|
509
|
+
requestId: approval.requestId,
|
|
510
|
+
runId,
|
|
511
|
+
nonce: mkNonce(),
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
});
|
|
516
|
+
}
|
|
361
517
|
return elements;
|
|
362
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* 选项行:左按钮右描述(固定 100px 按钮列保证对齐),单选/多选图标区分。
|
|
521
|
+
* 与 renderAnswerInput 分开以便自由文本题不渲染选项行。
|
|
522
|
+
*/
|
|
523
|
+
function renderOptionRow(requestId, questionIndex, option, isSelected, icon, runId) {
|
|
524
|
+
return {
|
|
525
|
+
tag: 'column_set',
|
|
526
|
+
flex_mode: 'none',
|
|
527
|
+
columns: [
|
|
528
|
+
{
|
|
529
|
+
tag: 'column',
|
|
530
|
+
width: 'auto',
|
|
531
|
+
vertical_align: 'center',
|
|
532
|
+
elements: [
|
|
533
|
+
{
|
|
534
|
+
tag: 'button',
|
|
535
|
+
text: { tag: 'plain_text', content: isSelected ? '取消选择' : '选择' },
|
|
536
|
+
type: isSelected ? 'default' : 'primary',
|
|
537
|
+
size: 'small',
|
|
538
|
+
// 固定宽度让所有选项行的按钮左对齐(描述长短不影响按钮列)。
|
|
539
|
+
width: '100px',
|
|
540
|
+
behaviors: [
|
|
541
|
+
{
|
|
542
|
+
type: 'callback',
|
|
543
|
+
value: {
|
|
544
|
+
cmd: 'approval.answer',
|
|
545
|
+
requestId,
|
|
546
|
+
questionIndex,
|
|
547
|
+
option: option.label,
|
|
548
|
+
runId,
|
|
549
|
+
nonce: mkNonce(),
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
},
|
|
554
|
+
],
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
tag: 'column',
|
|
558
|
+
width: 'weighted',
|
|
559
|
+
weight: 1,
|
|
560
|
+
vertical_align: 'center',
|
|
561
|
+
elements: [
|
|
562
|
+
{
|
|
563
|
+
tag: 'div',
|
|
564
|
+
text: {
|
|
565
|
+
tag: 'lark_md',
|
|
566
|
+
content: `${icon} ${option.label}${option.description ? `\n_${option.description}_` : ''}`,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
},
|
|
571
|
+
],
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* 文本输入行(Other 输入与自由文本题共用):输入后点击右侧 ✓ 提交图标,
|
|
576
|
+
* input_value 经 connector raw 事件回传(CardKit input 的 input_value 会被
|
|
577
|
+
* SDK normalizer 丢弃,必须走 includeRawEvent 路径)。
|
|
578
|
+
*/
|
|
579
|
+
function renderAnswerInput(requestId, questionIndex, placeholder, runId, cmd = 'approval.answerCustom') {
|
|
580
|
+
// name 必须含 cmd,保证同一题同时渲染多个输入框(如单选选项题的
|
|
581
|
+
// Other 输入 + Note 输入)时元素 name 唯一——飞书对同卡内重复 name
|
|
582
|
+
// 报 ErrCode 11310 拒绝整卡(2026-08-19 线上:name(answer-custom-0-0) duplicate)。
|
|
583
|
+
const kind = cmd === 'approval.answerNote' ? 'note' : 'custom';
|
|
584
|
+
return {
|
|
585
|
+
tag: 'input',
|
|
586
|
+
name: `answer-${kind}-${requestId}-${questionIndex}`,
|
|
587
|
+
placeholder: { tag: 'plain_text', content: placeholder },
|
|
588
|
+
behaviors: [
|
|
589
|
+
{
|
|
590
|
+
type: 'callback',
|
|
591
|
+
value: {
|
|
592
|
+
cmd,
|
|
593
|
+
requestId,
|
|
594
|
+
questionIndex,
|
|
595
|
+
runId,
|
|
596
|
+
nonce: mkNonce(),
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
};
|
|
601
|
+
}
|
|
363
602
|
//# sourceMappingURL=approval-render.js.map
|
|
@@ -23,15 +23,17 @@ export declare function stopButton(runId: string): object;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function newSessionButton(): object;
|
|
25
25
|
/**
|
|
26
|
-
* Create a CardKit 2.0 Compact button for
|
|
27
|
-
*
|
|
26
|
+
* Create a CardKit 2.0 Compact button for any runCompact-capable runner
|
|
27
|
+
* (codex/kimi/opencode/pi/claude,鸭子类型 `'runCompact' in runner` 探测)。
|
|
28
|
+
* Triggers the compact flow via the codex.compact callback(handler 按
|
|
29
|
+
* runId 解析 agentKind,claude 走 stream-json 内建 /compact)。
|
|
28
30
|
*/
|
|
29
31
|
export declare function compactButton(runId: string): object;
|
|
30
32
|
/**
|
|
31
33
|
* Create a CardKit 2.0 Compact button for resume cards (auto-resume / `/resume <id>`).
|
|
32
34
|
* Unlike compactButton (which targets a finished run's runId), this targets a
|
|
33
|
-
* sessionId directly: the bridge resolves the session and calls the
|
|
34
|
-
*
|
|
35
|
+
* sessionId directly: the bridge resolves the session and calls the runner's
|
|
36
|
+
* runCompact() without a runId. Carries agent so the handler routes
|
|
35
37
|
* to the correct session reader + runner when the card was rendered for a
|
|
36
38
|
* non-default agent.
|
|
37
39
|
*/
|
package/dist/card/card-shared.js
CHANGED
|
@@ -78,8 +78,10 @@ export function newSessionButton() {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
|
-
* Create a CardKit 2.0 Compact button for
|
|
82
|
-
*
|
|
81
|
+
* Create a CardKit 2.0 Compact button for any runCompact-capable runner
|
|
82
|
+
* (codex/kimi/opencode/pi/claude,鸭子类型 `'runCompact' in runner` 探测)。
|
|
83
|
+
* Triggers the compact flow via the codex.compact callback(handler 按
|
|
84
|
+
* runId 解析 agentKind,claude 走 stream-json 内建 /compact)。
|
|
83
85
|
*/
|
|
84
86
|
export function compactButton(runId) {
|
|
85
87
|
return {
|
|
@@ -92,8 +94,8 @@ export function compactButton(runId) {
|
|
|
92
94
|
/**
|
|
93
95
|
* Create a CardKit 2.0 Compact button for resume cards (auto-resume / `/resume <id>`).
|
|
94
96
|
* Unlike compactButton (which targets a finished run's runId), this targets a
|
|
95
|
-
* sessionId directly: the bridge resolves the session and calls the
|
|
96
|
-
*
|
|
97
|
+
* sessionId directly: the bridge resolves the session and calls the runner's
|
|
98
|
+
* runCompact() without a runId. Carries agent so the handler routes
|
|
97
99
|
* to the correct session reader + runner when the card was rendered for a
|
|
98
100
|
* non-default agent.
|
|
99
101
|
*/
|
|
@@ -8,6 +8,13 @@ export interface RunCardRenderOptions {
|
|
|
8
8
|
* Defaults to 'claude' when not provided; the bridge passes `config.defaultAgent`.
|
|
9
9
|
*/
|
|
10
10
|
agentKind?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the terminal-state Compact button may be rendered. Defaults to true.
|
|
13
|
+
* The bridge passes `runner 有 runCompact`(与 resume 卡 / auto-resume 卡的
|
|
14
|
+
* hasRunCompact 探测一致),保证「有能力才渲染」:claude(stream-json,
|
|
15
|
+
* 无 turn_started → operationKind undefined)同样能拿到按钮。
|
|
16
|
+
*/
|
|
17
|
+
compactSupported?: boolean;
|
|
11
18
|
}
|
|
12
19
|
type BlockGroup = {
|
|
13
20
|
kind: 'thinking';
|
|
@@ -25,13 +25,21 @@ function measureJson(obj) {
|
|
|
25
25
|
/** markdownDiv('', 'notation') 的序列化字节(空内容 div 的常量开销,估算时复用)。 */
|
|
26
26
|
const EMPTY_MD_DIV_BYTES = measureJson(markdownDiv('', 'notation'));
|
|
27
27
|
/**
|
|
28
|
-
* Compact
|
|
28
|
+
* Compact 按钮可见性:到达任意终态即可压缩。
|
|
29
29
|
* 异常退出(error/interrupted/idle_timeout)时上下文往往更大,更需要压缩后
|
|
30
30
|
* 再继续,因此不再要求 resultSubtype==='success'。compaction 卡
|
|
31
31
|
* (operationKind='compaction')永不显示,防止递归 Compact。
|
|
32
|
+
*
|
|
33
|
+
* 能力门控(compactSupported !== false)与执行侧 `'runCompact' in runner`
|
|
34
|
+
* 鸭子探测对齐:claude 走 stream-json,不发射 turn_started,operationKind
|
|
35
|
+
* 恒 undefined——若以 operationKind==='turn' 作门控,claude 终态卡永远拿
|
|
36
|
+
* 不到按钮(2026-08-19 缺陷)。compaction 卡的「防递归」仍由 operationKind
|
|
37
|
+
* 保证,两者是不同质的判断。
|
|
32
38
|
*/
|
|
33
|
-
function shouldShowCompactButton(state) {
|
|
34
|
-
if (state.operationKind
|
|
39
|
+
function shouldShowCompactButton(state, options) {
|
|
40
|
+
if (state.operationKind === 'compaction')
|
|
41
|
+
return false;
|
|
42
|
+
if (options.compactSupported === false)
|
|
35
43
|
return false;
|
|
36
44
|
return (state.terminal === 'done' ||
|
|
37
45
|
state.terminal === 'error' ||
|
|
@@ -201,15 +209,15 @@ const EXTREME_TIER = {
|
|
|
201
209
|
textBytes: DEGRADED_TEXT_BYTES,
|
|
202
210
|
};
|
|
203
211
|
/** Bottom action row: stop (if running) + compact (if applicable) + new session (always). */
|
|
204
|
-
function actionRow(state) {
|
|
212
|
+
function actionRow(state, options) {
|
|
205
213
|
const actionButtons = [];
|
|
206
214
|
// finalizing 也显示停止按钮(进程未退出,用户可 /stop)
|
|
207
215
|
const showStop = state.terminal === 'running' || state.terminal === 'finalizing';
|
|
208
216
|
if (showStop) {
|
|
209
217
|
actionButtons.push(stopButton(state.runId));
|
|
210
218
|
}
|
|
211
|
-
// Compact
|
|
212
|
-
if (shouldShowCompactButton(state)) {
|
|
219
|
+
// Compact 按钮:到达任意终态即可压缩(含异常退出),compaction 卡除外
|
|
220
|
+
if (shouldShowCompactButton(state, options)) {
|
|
213
221
|
actionButtons.push(compactButton(state.runId));
|
|
214
222
|
}
|
|
215
223
|
actionButtons.push(newSessionButton());
|
|
@@ -323,7 +331,7 @@ function buildFallbackElements(state, options, tier) {
|
|
|
323
331
|
elements.push(markdownDiv('_暂无输出_'));
|
|
324
332
|
}
|
|
325
333
|
elements.push(...buildSummaryContent(state));
|
|
326
|
-
elements.push(...actionRow(state));
|
|
334
|
+
elements.push(...actionRow(state, options));
|
|
327
335
|
elements.push(...approvalArea(state));
|
|
328
336
|
return elements;
|
|
329
337
|
}
|
|
@@ -374,7 +382,7 @@ function statusRow(state) {
|
|
|
374
382
|
* stop while running/finalizing). No block content is rendered, so the size is
|
|
375
383
|
* bounded by static structure regardless of input — closing the safety-net gap.
|
|
376
384
|
*/
|
|
377
|
-
function buildSkeletonElements(state) {
|
|
385
|
+
function buildSkeletonElements(state, options) {
|
|
378
386
|
const elements = [];
|
|
379
387
|
elements.push(statusRow(state));
|
|
380
388
|
elements.push(markdownDiv('_⚠️ 输出过大且含大量转义字符,已省略全部内容以避免超限;完整内容请查日志_'));
|
|
@@ -382,7 +390,7 @@ function buildSkeletonElements(state) {
|
|
|
382
390
|
elements.push(...buildSummaryContent(state));
|
|
383
391
|
// Bottom action row: stop (if running/finalizing) + compact (if applicable) + new session (always).
|
|
384
392
|
// Degraded paths must keep the action buttons reachable (design constraint).
|
|
385
|
-
elements.push(...actionRow(state));
|
|
393
|
+
elements.push(...actionRow(state, options));
|
|
386
394
|
return elements;
|
|
387
395
|
}
|
|
388
396
|
/**
|
|
@@ -450,6 +458,9 @@ export function estimateCardBytes(state, options = {}, shared) {
|
|
|
450
458
|
if (state.terminal === 'running' || state.terminal === 'finalizing') {
|
|
451
459
|
actionButtons.push(stopButton(state.runId));
|
|
452
460
|
}
|
|
461
|
+
if (shouldShowCompactButton(state, options)) {
|
|
462
|
+
actionButtons.push(compactButton(state.runId));
|
|
463
|
+
}
|
|
453
464
|
actionButtons.push(newSessionButton());
|
|
454
465
|
total += measureJson([
|
|
455
466
|
{ tag: 'div', text: { content: '', tag: 'lark_md' } },
|
|
@@ -551,7 +562,7 @@ export function renderRunCard(state, options = {}) {
|
|
|
551
562
|
statusRow(state),
|
|
552
563
|
...buildChronologicalContent(state, options, prepared, groups),
|
|
553
564
|
...buildSummaryContent(state),
|
|
554
|
-
...actionRow(state),
|
|
565
|
+
...actionRow(state, options),
|
|
555
566
|
...approvalArea(state),
|
|
556
567
|
];
|
|
557
568
|
const card = assembleRunCard(state, options, elements);
|
|
@@ -573,7 +584,7 @@ export function renderRunCard(state, options = {}) {
|
|
|
573
584
|
if (Buffer.byteLength(JSON.stringify(extremeCard), 'utf8') <= CARD_BUDGET_BYTES) {
|
|
574
585
|
return extremeCard;
|
|
575
586
|
}
|
|
576
|
-
const skeletonCard = assembleRunCard(state, options, buildSkeletonElements(state));
|
|
587
|
+
const skeletonCard = assembleRunCard(state, options, buildSkeletonElements(state, options));
|
|
577
588
|
// skeleton 卡是静态结构,理论上必 ≤28KB;保留兜底断言以防未来结构膨胀
|
|
578
589
|
if (Buffer.byteLength(JSON.stringify(skeletonCard), 'utf8') <= CARD_BUDGET_BYTES) {
|
|
579
590
|
return skeletonCard;
|
package/dist/config/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export declare const CLAUDE_EFFORTS: readonly ["low", "medium", "high", "xhigh",
|
|
|
6
6
|
* Claude 官方 --permission-mode 枚举(claude --help 输出,2026-08 实测)。
|
|
7
7
|
* 额外支持 'default':Claude settings.json 的官方未设置值,等价于不传
|
|
8
8
|
* --permission-mode(交互式默认:高风险工具逐个询问)。
|
|
9
|
+
* 'manual' 是 'default' 的别名(claude CLI 等价),schema 保留以向后兼容旧配置,
|
|
10
|
+
* 但 UI 下拉不展示(见 router/config/claude.ts)。
|
|
9
11
|
*/
|
|
10
12
|
export declare const CLAUDE_PERMISSION_MODES: readonly ["default", "acceptEdits", "auto", "bypassPermissions", "manual", "dontAsk", "plan"];
|
|
11
13
|
/** Valid thinking levels for Pi */
|
|
@@ -17,7 +19,7 @@ export declare const PI_THINKING_LEVELS: readonly ["off", "minimal", "low", "med
|
|
|
17
19
|
*/
|
|
18
20
|
export declare const DEFAULT_STOP_GRACE_MS: 5000;
|
|
19
21
|
/** Codex app-server turn idle timeout default (minutes). */
|
|
20
|
-
export declare const DEFAULT_TURN_IDLE_TIMEOUT_MINUTES:
|
|
22
|
+
export declare const DEFAULT_TURN_IDLE_TIMEOUT_MINUTES: 30;
|
|
21
23
|
/** 入站媒体默认配置(schema 默认与 connector 兜底共用,避免双源漂移)。 */
|
|
22
24
|
export declare const DEFAULT_INBOUND_MEDIA_DIR_NAME = ".lark-remote-temp";
|
|
23
25
|
export declare const DEFAULT_INBOUND_MEDIA_MAX_SIZE_MB = 50;
|
|
@@ -112,6 +114,15 @@ declare const KimiConfigSchema: z.ZodObject<{
|
|
|
112
114
|
turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
|
|
113
115
|
}, z.core.$strip>>;
|
|
114
116
|
}, z.core.$strip>;
|
|
117
|
+
/** DSH (DeepSeek Harness) Web Host default base URL. */
|
|
118
|
+
export declare const DEFAULT_DSH_HOST = "http://127.0.0.1:3080";
|
|
119
|
+
/** DSH (DeepSeek Harness) Web Host connection config. */
|
|
120
|
+
declare const DshConfigSchema: z.ZodObject<{
|
|
121
|
+
host: z.ZodDefault<z.ZodString>;
|
|
122
|
+
agentPreset: z.ZodOptional<z.ZodString>;
|
|
123
|
+
model: z.ZodOptional<z.ZodString>;
|
|
124
|
+
reasoningEffort: z.ZodOptional<z.ZodString>;
|
|
125
|
+
}, z.core.$strip>;
|
|
115
126
|
export declare const AppConfigSchema: z.ZodObject<{
|
|
116
127
|
feishu: z.ZodObject<{
|
|
117
128
|
appId: z.ZodString;
|
|
@@ -201,6 +212,12 @@ export declare const AppConfigSchema: z.ZodObject<{
|
|
|
201
212
|
turnIdleTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
|
|
202
213
|
}, z.core.$strip>>;
|
|
203
214
|
}, z.core.$strip>>;
|
|
215
|
+
dsh: z.ZodOptional<z.ZodObject<{
|
|
216
|
+
host: z.ZodDefault<z.ZodString>;
|
|
217
|
+
agentPreset: z.ZodOptional<z.ZodString>;
|
|
218
|
+
model: z.ZodOptional<z.ZodString>;
|
|
219
|
+
reasoningEffort: z.ZodOptional<z.ZodString>;
|
|
220
|
+
}, z.core.$strip>>;
|
|
204
221
|
}, z.core.$strip>>;
|
|
205
222
|
agentChoices: z.ZodOptional<z.ZodObject<{
|
|
206
223
|
codex: z.ZodOptional<z.ZodObject<{
|
|
@@ -220,6 +237,9 @@ export declare const AppConfigSchema: z.ZodObject<{
|
|
|
220
237
|
model: z.ZodOptional<z.ZodString>;
|
|
221
238
|
thinkingEffort: z.ZodOptional<z.ZodString>;
|
|
222
239
|
}, z.core.$strip>>;
|
|
240
|
+
dsh: z.ZodOptional<z.ZodObject<{
|
|
241
|
+
host: z.ZodOptional<z.ZodString>;
|
|
242
|
+
}, z.core.$strip>>;
|
|
223
243
|
}, z.core.$strip>>;
|
|
224
244
|
idle: z.ZodDefault<z.ZodObject<{
|
|
225
245
|
watchdogMinutes: z.ZodDefault<z.ZodNumber>;
|
|
@@ -248,6 +268,7 @@ export declare const AppConfigSchema: z.ZodObject<{
|
|
|
248
268
|
opencode: "opencode";
|
|
249
269
|
pi: "pi";
|
|
250
270
|
kimi: "kimi";
|
|
271
|
+
dsh: "dsh";
|
|
251
272
|
}>>;
|
|
252
273
|
checkUpdateOnStartup: z.ZodDefault<z.ZodBoolean>;
|
|
253
274
|
}, z.core.$strip>;
|
|
@@ -299,12 +320,14 @@ type CodexConfig = z.infer<typeof CodexConfigSchema>;
|
|
|
299
320
|
type OpencodeConfig = z.infer<typeof OpencodeConfigSchema>;
|
|
300
321
|
type PiConfig = z.infer<typeof PiConfigSchema>;
|
|
301
322
|
type KimiConfig = z.infer<typeof KimiConfigSchema>;
|
|
323
|
+
export type DshConfig = z.infer<typeof DshConfigSchema>;
|
|
302
324
|
/** Get configuration for a specific agent. Falls back to defaults if not configured. */
|
|
303
325
|
export declare function getAgentConfig(config: AppConfig, kind: 'claude'): ClaudeConfig;
|
|
304
326
|
export declare function getAgentConfig(config: AppConfig, kind: 'codex'): CodexConfig | undefined;
|
|
305
327
|
export declare function getAgentConfig(config: AppConfig, kind: 'opencode'): OpencodeConfig | undefined;
|
|
306
328
|
export declare function getAgentConfig(config: AppConfig, kind: 'pi'): PiConfig | undefined;
|
|
307
329
|
export declare function getAgentConfig(config: AppConfig, kind: 'kimi'): KimiConfig | undefined;
|
|
330
|
+
export declare function getAgentConfig(config: AppConfig, kind: 'dsh'): DshConfig | undefined;
|
|
308
331
|
/**
|
|
309
332
|
* 从 Claude settings.json 的 env 中读取所有以 MODEL 结尾的环境变量值,
|
|
310
333
|
* 返回去重后的模型 ID 列表。
|