lark-remote 0.1.13 → 0.2.1
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 +36 -3
- package/README.en.md +7 -11
- package/README.md +7 -11
- package/dist/bridge/final-usage.d.ts +24 -0
- package/dist/bridge/final-usage.js +55 -0
- package/dist/bridge/index.d.ts +0 -7
- package/dist/bridge/index.js +22 -64
- package/dist/bridge/queue-manager.js +4 -3
- package/dist/card/approval-render.js +39 -94
- package/dist/card/bash-renderer.js +12 -21
- package/dist/card/card-shared.d.ts +13 -1
- package/dist/card/card-shared.js +21 -30
- package/dist/card/run-renderer.d.ts +0 -22
- package/dist/card/run-renderer.js +206 -153
- package/dist/card/run-state.d.ts +29 -1
- package/dist/card/run-state.js +172 -84
- package/dist/card/text-truncate.d.ts +0 -9
- package/dist/card/text-truncate.js +0 -9
- package/dist/card/tool-render.d.ts +4 -1
- package/dist/card/tool-render.js +76 -32
- package/dist/cli.js +2 -2
- package/dist/common/fs.d.ts +14 -0
- package/dist/common/fs.js +57 -3
- package/dist/config/codex-config.js +2 -0
- package/dist/config/index.d.ts +0 -5
- package/dist/config/index.js +0 -11
- package/dist/config/kimi-config.js +1 -0
- package/dist/config/opencode-config.js +13 -2
- package/dist/config/qr.d.ts +36 -0
- package/dist/config/qr.js +124 -0
- package/dist/config/wizard.js +69 -27
- package/dist/connector/index.d.ts +3 -2
- package/dist/connector/index.js +5 -4
- package/dist/index.js +15 -42
- package/dist/instance-lock.js +7 -0
- package/dist/persistence/atomic-write.d.ts +2 -0
- package/dist/persistence/atomic-write.js +20 -5
- package/dist/persistence/load-json-file.d.ts +1 -2
- package/dist/persistence/load-json-file.js +2 -3
- package/dist/platform/agent-stopper.d.ts +22 -0
- package/dist/platform/agent-stopper.js +19 -0
- package/dist/platform/command.d.ts +11 -0
- package/dist/platform/command.js +107 -0
- package/dist/platform/identity.d.ts +33 -0
- package/dist/platform/identity.js +187 -0
- package/dist/platform/path.d.ts +24 -0
- package/dist/platform/path.js +110 -0
- package/dist/platform/select.d.ts +9 -0
- package/dist/platform/select.js +12 -0
- package/dist/platform/shell.d.ts +51 -0
- package/dist/platform/shell.js +75 -0
- package/dist/platform/spawn.d.ts +41 -0
- package/dist/platform/spawn.js +77 -0
- package/dist/platform/terminator-posix.d.ts +38 -0
- package/dist/platform/terminator-posix.js +103 -0
- package/dist/platform/terminator-win32.d.ts +21 -0
- package/dist/platform/terminator-win32.js +110 -0
- package/dist/platform/terminator.d.ts +44 -0
- package/dist/platform/terminator.js +24 -0
- package/dist/platform/types.d.ts +23 -0
- package/dist/platform/types.js +5 -0
- package/dist/restart.js +4 -2
- package/dist/router/card-helpers.d.ts +33 -6
- package/dist/router/card-helpers.js +67 -11
- package/dist/router/config/common/render.d.ts +1 -1
- package/dist/router/config/common/render.js +1 -1
- package/dist/router/config-diff.d.ts +4 -0
- package/dist/router/config-diff.js +70 -0
- package/dist/router/index.d.ts +66 -6
- package/dist/router/index.js +451 -316
- package/dist/router/utils.d.ts +6 -0
- package/dist/router/utils.js +12 -0
- package/dist/runner/bash/runner.d.ts +9 -2
- package/dist/runner/bash/runner.js +45 -20
- package/dist/runner/claude/session.d.ts +4 -1
- package/dist/runner/claude/session.js +18 -4
- package/dist/runner/codex/app-server/protocol-types.d.ts +35 -1
- package/dist/runner/codex/app-server/translator.d.ts +18 -0
- package/dist/runner/codex/app-server/translator.js +146 -0
- package/dist/runner/common/acp/base-acp-runner.d.ts +116 -0
- package/dist/runner/common/acp/base-acp-runner.js +248 -0
- package/dist/runner/common/acp/base-translator.d.ts +141 -0
- package/dist/runner/common/acp/base-translator.js +271 -0
- package/dist/runner/common/acp/protocol-helpers.d.ts +39 -0
- package/dist/runner/common/acp/protocol-helpers.js +34 -0
- package/dist/runner/common/jsonrpc/transport.d.ts +2 -0
- package/dist/runner/common/jsonrpc/transport.js +25 -5
- package/dist/runner/common/process-stopper.d.ts +4 -26
- package/dist/runner/common/process-stopper.js +4 -76
- package/dist/runner/common/runner-utils.d.ts +0 -5
- package/dist/runner/common/runner-utils.js +0 -7
- package/dist/runner/common/spawning-runner.d.ts +42 -132
- package/dist/runner/common/spawning-runner.js +52 -271
- package/dist/runner/common/usage.d.ts +2 -0
- package/dist/runner/common/usage.js +2 -0
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +45 -42
- package/dist/runner/kimi/acp/runner.js +140 -293
- package/dist/runner/kimi/acp/translator.d.ts +18 -100
- package/dist/runner/kimi/acp/translator.js +56 -242
- package/dist/runner/opencode/acp/runner.d.ts +22 -46
- package/dist/runner/opencode/acp/runner.js +73 -256
- package/dist/runner/opencode/acp/translator.d.ts +28 -103
- package/dist/runner/opencode/acp/translator.js +51 -236
- package/dist/runner/pi/rpc/translator.d.ts +3 -0
- package/dist/runner/pi/rpc/translator.js +47 -9
- package/dist/runner/probe.d.ts +8 -11
- package/dist/runner/probe.js +17 -21
- package/dist/runner/registry.d.ts +4 -4
- package/dist/runner/registry.js +4 -4
- package/dist/runner/types.d.ts +38 -1
- package/dist/session/claude/sessions.js +35 -58
- package/dist/session/codex/rollout-reader.d.ts +10 -0
- package/dist/session/codex/rollout-reader.js +123 -41
- package/dist/session/common/constants.d.ts +7 -0
- package/dist/session/common/constants.js +9 -0
- package/dist/session/common/two-pass.d.ts +22 -0
- package/dist/session/common/two-pass.js +70 -0
- package/dist/session/dsh/sessions.js +7 -6
- package/dist/session/kimi/sessions.d.ts +6 -0
- package/dist/session/kimi/sessions.js +50 -73
- package/dist/session/opencode/sessions.js +26 -8
- package/dist/session/pi/sessions.js +54 -73
- package/dist/update/index.d.ts +1 -2
- package/dist/update/index.js +1 -2
- package/dist/update/install.d.ts +2 -1
- package/dist/update/install.js +58 -9
- package/dist/update/version-check.d.ts +5 -0
- package/dist/update/version-check.js +9 -0
- package/package.json +9 -6
- package/dist/update/startup-hint.d.ts +0 -5
- package/dist/update/startup-hint.js +0 -11
|
@@ -95,6 +95,58 @@ function renderTextBlock(content, timestamp, finalized = false) {
|
|
|
95
95
|
}),
|
|
96
96
|
];
|
|
97
97
|
}
|
|
98
|
+
/** 运行期通知作为普通消息渲染:与内容流同级的一行斜体提示。 */
|
|
99
|
+
function renderNoticeRow(notice) {
|
|
100
|
+
const icon = notice.level === 'info' ? 'ℹ️' : '⚠️';
|
|
101
|
+
return markdownDiv(`_${icon} ${notice.text}_`);
|
|
102
|
+
}
|
|
103
|
+
/** 内容组用于时间线插值的锚点时刻(与面板标题展示的时间戳一致)。 */
|
|
104
|
+
function groupTimeMs(group) {
|
|
105
|
+
if (group.kind === 'tool') {
|
|
106
|
+
const ts = group.tool.completedAt ?? group.tool.startedAt;
|
|
107
|
+
return ts ? Date.parse(ts) : undefined;
|
|
108
|
+
}
|
|
109
|
+
return group.timestamp ? Date.parse(group.timestamp) : undefined;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 把 notices 作为普通消息并入内容时间线:按到达时间插到对应内容组之间。
|
|
113
|
+
* 内容组自身的相对顺序不被改动(仍由 run-state 的块数组决定),时间戳只决定
|
|
114
|
+
* notice 的落点;无时间戳的 notice 无法定位,兜底置于内容流末尾。
|
|
115
|
+
*/
|
|
116
|
+
function mergeNoticesIntoContent(groups, notices) {
|
|
117
|
+
if (!notices || notices.length === 0)
|
|
118
|
+
return groups;
|
|
119
|
+
const timed = [];
|
|
120
|
+
const untimed = [];
|
|
121
|
+
for (const notice of notices) {
|
|
122
|
+
if (notice.timestamp)
|
|
123
|
+
timed.push(notice);
|
|
124
|
+
else
|
|
125
|
+
untimed.push(notice);
|
|
126
|
+
}
|
|
127
|
+
const items = [];
|
|
128
|
+
let cursor = 0;
|
|
129
|
+
for (const notice of timed) {
|
|
130
|
+
const t = Date.parse(notice.timestamp);
|
|
131
|
+
if (!Number.isNaN(t)) {
|
|
132
|
+
while (cursor < groups.length &&
|
|
133
|
+
groupTimeMs(groups[cursor]) !== undefined &&
|
|
134
|
+
groupTimeMs(groups[cursor]) <= t) {
|
|
135
|
+
items.push(groups[cursor]);
|
|
136
|
+
cursor++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
items.push({ kind: 'notice', notice });
|
|
140
|
+
}
|
|
141
|
+
while (cursor < groups.length) {
|
|
142
|
+
items.push(groups[cursor]);
|
|
143
|
+
cursor++;
|
|
144
|
+
}
|
|
145
|
+
for (const notice of untimed) {
|
|
146
|
+
items.push({ kind: 'notice', notice });
|
|
147
|
+
}
|
|
148
|
+
return items;
|
|
149
|
+
}
|
|
98
150
|
/**
|
|
99
151
|
* 单次截断:把每个超限块(thinking/plan/text)的最终渲染内容算出来,供
|
|
100
152
|
* estimateCardBytes 与 buildChronologicalContent 共用。此前影子测量与正式渲染
|
|
@@ -180,9 +232,9 @@ function groupBlocks(blocks) {
|
|
|
180
232
|
* - Running tool: expanded (user can watch live)
|
|
181
233
|
* - Completed tool: collapsed (click to inspect)
|
|
182
234
|
*/
|
|
183
|
-
function renderTool(tool, finalized
|
|
235
|
+
function renderTool(tool, finalized) {
|
|
184
236
|
const border = tool.status === 'error' ? 'red' : 'grey';
|
|
185
|
-
const body =
|
|
237
|
+
const body = toolBodyMd(tool);
|
|
186
238
|
return collapsibleMarkdownPanel({
|
|
187
239
|
title: toolTitle(tool),
|
|
188
240
|
expanded: !finalized && tool.status === 'running',
|
|
@@ -208,8 +260,8 @@ const EXTREME_TIER = {
|
|
|
208
260
|
toolKeep: EXTREME_TOOL_KEEP,
|
|
209
261
|
textBytes: DEGRADED_TEXT_BYTES,
|
|
210
262
|
};
|
|
211
|
-
/** Bottom action
|
|
212
|
-
function
|
|
263
|
+
/** Bottom action buttons: stop (if running) + compact (if applicable) + new session (always). */
|
|
264
|
+
function actionButtonList(state, options) {
|
|
213
265
|
const actionButtons = [];
|
|
214
266
|
// finalizing 也显示停止按钮(进程未退出,用户可 /stop)
|
|
215
267
|
const showStop = state.terminal === 'running' || state.terminal === 'finalizing';
|
|
@@ -221,6 +273,11 @@ function actionRow(state, options) {
|
|
|
221
273
|
actionButtons.push(compactButton(state.runId));
|
|
222
274
|
}
|
|
223
275
|
actionButtons.push(newSessionButton());
|
|
276
|
+
return actionButtons;
|
|
277
|
+
}
|
|
278
|
+
/** Bottom action row: spacer + column_set of actionButtonList (估算与渲染共用,W2.2)。 */
|
|
279
|
+
function actionRow(state, options) {
|
|
280
|
+
const actionButtons = actionButtonList(state, options);
|
|
224
281
|
if (actionButtons.length === 0)
|
|
225
282
|
return [];
|
|
226
283
|
return [
|
|
@@ -235,7 +292,9 @@ function actionRow(state, options) {
|
|
|
235
292
|
},
|
|
236
293
|
];
|
|
237
294
|
}
|
|
238
|
-
/** 审批区位于 body 最底部(底部操作行之后):待审批时决策按钮不遮挡内容流。
|
|
295
|
+
/** 审批区位于 body 最底部(底部操作行之后):待审批时决策按钮不遮挡内容流。
|
|
296
|
+
* 例外:skeleton 兜底路径中 approvalArea 在 actionRow 之前(信息保真 C4.3),
|
|
297
|
+
* 保证极端降级时审批入口与 stop/new-session 按钮同时可见。 */
|
|
239
298
|
function approvalArea(state) {
|
|
240
299
|
const elements = [];
|
|
241
300
|
for (const slot of state.approvals ?? []) {
|
|
@@ -247,6 +306,12 @@ function approvalArea(state) {
|
|
|
247
306
|
}
|
|
248
307
|
return elements;
|
|
249
308
|
}
|
|
309
|
+
/** 底部操作行之前的内容区提示:被 MAX_BLOCKS 截掉的早期块计数(信息保真 C4.1)。 */
|
|
310
|
+
function omittedBlocksHint(state) {
|
|
311
|
+
return state.omittedBlocks !== undefined && state.omittedBlocks > 0
|
|
312
|
+
? [markdownDiv(`_💡 已省略 ${state.omittedBlocks} 个早期步骤_`)]
|
|
313
|
+
: [];
|
|
314
|
+
}
|
|
250
315
|
/** Assemble the card shell (schema/config/header) around body elements. */
|
|
251
316
|
function assembleRunCard(state, options, elements) {
|
|
252
317
|
return {
|
|
@@ -267,6 +332,7 @@ function assembleRunCard(state, options, elements) {
|
|
|
267
332
|
function buildFallbackElements(state, options, tier) {
|
|
268
333
|
const elements = [];
|
|
269
334
|
elements.push(statusRow(state));
|
|
335
|
+
elements.push(...omittedBlocksHint(state));
|
|
270
336
|
// 单遍分桶:thinking / tool(保持原相对顺序)
|
|
271
337
|
const { thinking: allThinkingBlocks, tool: allToolBlocks } = bucketThinkingAndTool(state.blocks);
|
|
272
338
|
if (allThinkingBlocks.length > tier.thinkingKeep) {
|
|
@@ -298,11 +364,26 @@ function buildFallbackElements(state, options, tier) {
|
|
|
298
364
|
}
|
|
299
365
|
return true; // keep all non-thinking, non-tool blocks
|
|
300
366
|
});
|
|
301
|
-
//
|
|
302
|
-
|
|
367
|
+
// 信息保真 C4.2:plan/file_change 块在降级路径不渲染内容(正常路径已可见,
|
|
368
|
+
// 降级预算紧张),但必须计数留痕,禁止静默丢弃。
|
|
369
|
+
// 计数在渲染前完成,提示与 thinking/tool 省略提示一并置于卡片顶部——不能等
|
|
370
|
+
// 内容循环结束再追加,否则提示会落在最后一条输出之后,打乱「输出收尾」观感。
|
|
371
|
+
const groupsToRender = groupBlocks(filteredBlocks);
|
|
372
|
+
const omittedPlanOrFileChange = groupsToRender.filter((g) => g.kind === 'plan' || g.kind === 'file_change').length;
|
|
373
|
+
if (omittedPlanOrFileChange > 0) {
|
|
374
|
+
elements.push(markdownDiv(`_💡 另外 ${omittedPlanOrFileChange} 个计划/文件变更已省略_`));
|
|
375
|
+
}
|
|
376
|
+
// notices 作为普通消息按时间插入内容流(与正常路径同语义);降级只裁剪
|
|
377
|
+
// 内容块,不裁剪运行期通知。
|
|
378
|
+
const contentItems = mergeNoticesIntoContent(groupsToRender, state.notices);
|
|
379
|
+
// Render thinking/text/tool/notice in chronological order
|
|
380
|
+
for (const item of contentItems) {
|
|
381
|
+
if (item.kind === 'notice') {
|
|
382
|
+
elements.push(renderNoticeRow(item.notice));
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
const group = item;
|
|
303
386
|
if (group.kind === 'thinking') {
|
|
304
|
-
if (options.showThinking === false)
|
|
305
|
-
continue;
|
|
306
387
|
const ts = formatTimestamp(group.timestamp);
|
|
307
388
|
const title = '💭 **思考完成**';
|
|
308
389
|
const content = truncateUtf8(group.content, tier.thinkingBytes);
|
|
@@ -322,15 +403,13 @@ function buildFallbackElements(state, options, tier) {
|
|
|
322
403
|
}
|
|
323
404
|
else if (group.kind === 'tool') {
|
|
324
405
|
// Show tool individually (each tool is independent now)
|
|
325
|
-
|
|
326
|
-
continue;
|
|
327
|
-
elements.push(renderTool(group.tool, true, options.showToolResult !== false)); // finalized=true
|
|
406
|
+
elements.push(renderTool(group.tool, true)); // finalized=true
|
|
328
407
|
}
|
|
329
408
|
}
|
|
330
409
|
if (elements.length === 0 && tier === EXTREME_TIER) {
|
|
331
410
|
elements.push(markdownDiv('_暂无输出_'));
|
|
332
411
|
}
|
|
333
|
-
elements.push(...buildSummaryContent(state));
|
|
412
|
+
elements.push(...buildSummaryContent(state, { includeNotices: false }));
|
|
334
413
|
elements.push(...actionRow(state, options));
|
|
335
414
|
elements.push(...approvalArea(state));
|
|
336
415
|
return elements;
|
|
@@ -388,6 +467,9 @@ function buildSkeletonElements(state, options) {
|
|
|
388
467
|
elements.push(markdownDiv('_⚠️ 输出过大且含大量转义字符,已省略全部内容以避免超限;完整内容请查日志_'));
|
|
389
468
|
// Summary (token stats etc.) — static, bounded size
|
|
390
469
|
elements.push(...buildSummaryContent(state));
|
|
470
|
+
// 信息保真 C4.3:skeleton 也渲染审批区——极端兜底不丢审批入口
|
|
471
|
+
//(待审批被静默吞掉 = 用户永远看不到审批请求)。
|
|
472
|
+
elements.push(...approvalArea(state));
|
|
391
473
|
// Bottom action row: stop (if running/finalizing) + compact (if applicable) + new session (always).
|
|
392
474
|
// Degraded paths must keep the action buttons reachable (design constraint).
|
|
393
475
|
elements.push(...actionRow(state, options));
|
|
@@ -431,85 +513,67 @@ function bucketThinkingAndTool(blocks) {
|
|
|
431
513
|
* 走正常路径,renderRunCard 仍会 stringify 确认 ≤28KB;若估算低估,仍 fallback
|
|
432
514
|
* 到 degraded(安全网不丢)。
|
|
433
515
|
*/
|
|
516
|
+
/**
|
|
517
|
+
* W2.2 镜像对单源:thinking/plan/file_change 三个 collapsible 面板组的
|
|
518
|
+
* { title, expanded, border, content } 描述,估算(measurePanelBytes)与渲染
|
|
519
|
+
* (collapsibleMarkdownPanel)共用——此前同一套标题/opIcon/内容模板写两遍。
|
|
520
|
+
* text(renderTextBlock)与 tool(renderTool)形态特殊,由调用方各自处理;
|
|
521
|
+
* 返回 null 表示该组不是 collapsible 面板。
|
|
522
|
+
*/
|
|
523
|
+
function describePanelGroup(group, prepared) {
|
|
524
|
+
if (group.kind === 'tool')
|
|
525
|
+
return null;
|
|
526
|
+
const ts = formatTimestamp(group.timestamp);
|
|
527
|
+
const suffix = ts ? ` (${ts})` : '';
|
|
528
|
+
if (group.kind === 'thinking') {
|
|
529
|
+
const title = (group.active ? '💭 **思考中**' : '💭 **思考完成**') + suffix;
|
|
530
|
+
return { title, expanded: group.active, border: 'grey', content: prepared.get(group) ?? '' };
|
|
531
|
+
}
|
|
532
|
+
if (group.kind === 'plan') {
|
|
533
|
+
const title = (group.active ? '📋 **执行计划**' : '📋 **计划完成**') + suffix;
|
|
534
|
+
return { title, expanded: group.active, border: 'blue', content: prepared.get(group) ?? '' };
|
|
535
|
+
}
|
|
536
|
+
if (group.kind === 'file_change') {
|
|
537
|
+
const opIcon = group.operation === 'create'
|
|
538
|
+
? '🆕'
|
|
539
|
+
: group.operation === 'edit'
|
|
540
|
+
? '✏️'
|
|
541
|
+
: group.operation === 'delete'
|
|
542
|
+
? '🗑️'
|
|
543
|
+
: '📖';
|
|
544
|
+
const title = `${opIcon} **文件改动**${suffix}`;
|
|
545
|
+
const content = group.diff
|
|
546
|
+
? `**${group.path}**\n\n\`\`\`\n${group.diff}\n\`\`\``
|
|
547
|
+
: `**${group.path}** (${group.operation})`;
|
|
548
|
+
return { title, expanded: false, border: 'grey', content };
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
434
552
|
/** 导出供测试断言估算精度(估算 ≈ 实际渲染字节,见 tests/anchor/run-card/run-card.test.ts)。 */
|
|
435
553
|
export function estimateCardBytes(state, options = {}, shared) {
|
|
436
554
|
const finalized = state.terminal !== 'running';
|
|
437
|
-
const showResult = options.showToolResult !== false;
|
|
438
|
-
const showThinking = options.showThinking !== false;
|
|
439
|
-
const showToolUse = options.showToolUse !== false;
|
|
440
555
|
// P1-2:renderRunCard 传入共享 groups/prepared 时直接复用(截断只做一次);
|
|
441
556
|
// 独立调用(测试/预算探针)时自建,行为与原先一致。
|
|
442
557
|
const groups = shared?.groups ?? groupBlocks(state.blocks);
|
|
443
558
|
const prepared = shared?.prepared ?? prepareGroupContent(groups);
|
|
444
|
-
//
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
template: headerTemplate2(state),
|
|
450
|
-
title: { content: headerTitle2(state, options), tag: 'plain_text' },
|
|
451
|
-
},
|
|
452
|
-
body: { elements: [] },
|
|
453
|
-
}) +
|
|
559
|
+
// 卡片外壳直接实测 assembleRunCard 空元素产物(schema/config/header/body 结构
|
|
560
|
+
// 与正式渲染完全一致,W2.2);status/summary/操作行同样实测渲染结构。
|
|
561
|
+
// 注:审批区(approvalArea)不参与估算——待审批通常在途且体积小,超预算由
|
|
562
|
+
// renderRunCard 的 stringify 兜底捕获。
|
|
563
|
+
let total = measureJson(assembleRunCard(state, options, [])) +
|
|
454
564
|
measureJson(statusRow(state)) +
|
|
455
|
-
measureJson(buildSummaryContent(state))
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (state.terminal === 'running' || state.terminal === 'finalizing') {
|
|
459
|
-
actionButtons.push(stopButton(state.runId));
|
|
460
|
-
}
|
|
461
|
-
if (shouldShowCompactButton(state, options)) {
|
|
462
|
-
actionButtons.push(compactButton(state.runId));
|
|
463
|
-
}
|
|
464
|
-
actionButtons.push(newSessionButton());
|
|
465
|
-
total += measureJson([
|
|
466
|
-
{ tag: 'div', text: { content: '', tag: 'lark_md' } },
|
|
467
|
-
{
|
|
468
|
-
tag: 'column_set',
|
|
469
|
-
columns: actionButtons.map((btn) => ({
|
|
470
|
-
tag: 'column',
|
|
471
|
-
width: 'auto',
|
|
472
|
-
elements: [btn],
|
|
473
|
-
})),
|
|
474
|
-
},
|
|
475
|
-
]);
|
|
476
|
-
// 块内容:与 buildChronologicalContent 同源(groupBlocks 合并语义一致)
|
|
565
|
+
measureJson(buildSummaryContent(state, { includeNotices: false })) +
|
|
566
|
+
measureJson(actionRow(state, options));
|
|
567
|
+
// 块内容:与 buildChronologicalContent 同源(describePanelGroup 单源,W2.2)
|
|
477
568
|
let renderedAny = false;
|
|
478
569
|
for (const group of groups) {
|
|
479
|
-
if (group.kind === '
|
|
480
|
-
if (!showThinking)
|
|
481
|
-
continue;
|
|
570
|
+
if (group.kind === 'tool') {
|
|
482
571
|
renderedAny = true;
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
total += measurePanelBytes(header, group.active, 'grey', prepared.get(group) ?? '');
|
|
487
|
-
}
|
|
488
|
-
else if (group.kind === 'plan') {
|
|
489
|
-
renderedAny = true;
|
|
490
|
-
const ts = formatTimestamp(group.timestamp);
|
|
491
|
-
const title = group.active ? '📋 **执行计划**' : '📋 **计划完成**';
|
|
492
|
-
const header = ts ? `${title} (${ts})` : title;
|
|
493
|
-
total += measurePanelBytes(header, group.active, 'blue', prepared.get(group) ?? '');
|
|
494
|
-
}
|
|
495
|
-
else if (group.kind === 'file_change') {
|
|
496
|
-
renderedAny = true;
|
|
497
|
-
const ts = formatTimestamp(group.timestamp);
|
|
498
|
-
const opIcon = group.operation === 'create'
|
|
499
|
-
? '🆕'
|
|
500
|
-
: group.operation === 'edit'
|
|
501
|
-
? '✏️'
|
|
502
|
-
: group.operation === 'delete'
|
|
503
|
-
? '🗑️'
|
|
504
|
-
: '📖';
|
|
505
|
-
const title = `${opIcon} **文件改动**`;
|
|
506
|
-
const content = group.diff
|
|
507
|
-
? `**${group.path}**\n\n\`\`\`\n${group.diff}\n\`\`\``
|
|
508
|
-
: `**${group.path}** (${group.operation})`;
|
|
509
|
-
const header = ts ? `${title} (${ts})` : title;
|
|
510
|
-
total += measurePanelBytes(header, false, 'grey', content);
|
|
572
|
+
// tool 直接实测真实渲染元素(复用 renderTool,output ≤1200 字符,物化廉价)
|
|
573
|
+
total += measureJson(renderTool(group.tool, finalized));
|
|
574
|
+
continue;
|
|
511
575
|
}
|
|
512
|
-
|
|
576
|
+
if (group.kind === 'text') {
|
|
513
577
|
const content = prepared.get(group) ?? '';
|
|
514
578
|
if (!content.trim())
|
|
515
579
|
continue;
|
|
@@ -517,14 +581,19 @@ export function estimateCardBytes(state, options = {}, shared) {
|
|
|
517
581
|
const ts = formatTimestamp(group.timestamp);
|
|
518
582
|
const header = `💬 **输出**${ts ? ` (${ts})` : ''}`;
|
|
519
583
|
total += measurePanelBytes(header, true, 'grey', content);
|
|
584
|
+
continue;
|
|
520
585
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
586
|
+
const spec = describePanelGroup(group, prepared);
|
|
587
|
+
if (!spec)
|
|
588
|
+
continue;
|
|
589
|
+
renderedAny = true;
|
|
590
|
+
total += measurePanelBytes(spec.title, spec.expanded, spec.border, spec.content);
|
|
591
|
+
}
|
|
592
|
+
// notices 在正常路径作为普通消息内联渲染(与 buildChronologicalContent 同源,
|
|
593
|
+
// renderNoticeRow 单源);行体积与位置无关,直接按条累加。
|
|
594
|
+
for (const notice of state.notices ?? []) {
|
|
595
|
+
renderedAny = true;
|
|
596
|
+
total += measureJson(renderNoticeRow(notice));
|
|
528
597
|
}
|
|
529
598
|
if (!renderedAny) {
|
|
530
599
|
total += measureJson(markdownDiv('_暂无输出_'));
|
|
@@ -560,8 +629,9 @@ export function renderRunCard(state, options = {}) {
|
|
|
560
629
|
if (estimate < DEGRADED_THRESHOLD) {
|
|
561
630
|
const elements = [
|
|
562
631
|
statusRow(state),
|
|
563
|
-
...
|
|
564
|
-
...
|
|
632
|
+
...omittedBlocksHint(state),
|
|
633
|
+
...buildChronologicalContent(state, prepared, groups),
|
|
634
|
+
...buildSummaryContent(state, { includeNotices: false }),
|
|
565
635
|
...actionRow(state, options),
|
|
566
636
|
...approvalArea(state),
|
|
567
637
|
];
|
|
@@ -599,77 +669,41 @@ export function renderRunCard(state, options = {}) {
|
|
|
599
669
|
return truncatedSkeleton;
|
|
600
670
|
}
|
|
601
671
|
/** Build content in chronological order using groupBlocks to interleave thinking/text/tools. */
|
|
602
|
-
function buildChronologicalContent(state,
|
|
672
|
+
function buildChronologicalContent(state, prepared, groups) {
|
|
603
673
|
const elements = [];
|
|
604
674
|
// finalized = 非 running(含 finalizing:主结果已出,thinking 折叠)
|
|
605
675
|
const finalized = state.terminal !== 'running';
|
|
606
|
-
const showResult = options.showToolResult !== false;
|
|
607
676
|
// P1-2:renderRunCard 传入同一 groups 数组时直接复用(prepared 的键是组对象
|
|
608
677
|
// 引用,重建数组会让 prepared.get 永远 miss,退化为二次截断)。
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const header = ts ? `${title} (${ts})` : title;
|
|
617
|
-
elements.push(collapsibleMarkdownPanel({
|
|
618
|
-
title: header,
|
|
619
|
-
expanded: group.active,
|
|
620
|
-
border: 'grey',
|
|
621
|
-
content,
|
|
622
|
-
textSize: 'notation',
|
|
623
|
-
}));
|
|
624
|
-
}
|
|
625
|
-
else if (group.kind === 'plan') {
|
|
626
|
-
// Plan blocks: render as collapsible panel (default collapsed)
|
|
627
|
-
const ts = formatTimestamp(group.timestamp);
|
|
628
|
-
const title = group.active ? '📋 **执行计划**' : '📋 **计划完成**';
|
|
629
|
-
const content = prepared?.get(group) ?? truncateUtf8(group.content, REASONING_BYTES * 2);
|
|
630
|
-
const header = ts ? `${title} (${ts})` : title;
|
|
631
|
-
elements.push(collapsibleMarkdownPanel({
|
|
632
|
-
title: header,
|
|
633
|
-
expanded: group.active, // expand while active, collapse when done
|
|
634
|
-
border: 'blue',
|
|
635
|
-
content,
|
|
636
|
-
textSize: 'notation',
|
|
637
|
-
}));
|
|
678
|
+
// W2.2:面板组描述经 describePanelGroup 与估算共享同一份模板。
|
|
679
|
+
// notices 作为普通消息按到达时间插入内容流(有内容时不再沉到 summary)。
|
|
680
|
+
const contentItems = mergeNoticesIntoContent(groups, state.notices);
|
|
681
|
+
for (const item of contentItems) {
|
|
682
|
+
if (item.kind === 'notice') {
|
|
683
|
+
elements.push(renderNoticeRow(item.notice));
|
|
684
|
+
continue;
|
|
638
685
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
? '🆕'
|
|
644
|
-
: group.operation === 'edit'
|
|
645
|
-
? '✏️'
|
|
646
|
-
: group.operation === 'delete'
|
|
647
|
-
? '🗑️'
|
|
648
|
-
: '📖';
|
|
649
|
-
const title = `${opIcon} **文件改动**`;
|
|
650
|
-
const content = group.diff
|
|
651
|
-
? `**${group.path}**\n\n\`\`\`\n${group.diff}\n\`\`\``
|
|
652
|
-
: `**${group.path}** (${group.operation})`;
|
|
653
|
-
const header = ts ? `${title} (${ts})` : title;
|
|
654
|
-
elements.push(collapsibleMarkdownPanel({
|
|
655
|
-
title: header,
|
|
656
|
-
expanded: false,
|
|
657
|
-
border: 'grey',
|
|
658
|
-
content,
|
|
659
|
-
textSize: 'notation',
|
|
660
|
-
}));
|
|
686
|
+
const group = item;
|
|
687
|
+
if (group.kind === 'tool') {
|
|
688
|
+
elements.push(renderTool(group.tool, finalized));
|
|
689
|
+
continue;
|
|
661
690
|
}
|
|
662
|
-
|
|
663
|
-
const content = prepared
|
|
691
|
+
if (group.kind === 'text') {
|
|
692
|
+
const content = prepared.get(group) ?? '';
|
|
664
693
|
const ts = formatTimestamp(group.timestamp);
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
668
|
-
else if (group.kind === 'tool') {
|
|
669
|
-
if (options.showToolUse === false)
|
|
670
|
-
continue;
|
|
671
|
-
elements.push(renderTool(group.tool, finalized, showResult));
|
|
694
|
+
elements.push(...renderTextBlock(content, ts, finalized));
|
|
695
|
+
continue;
|
|
672
696
|
}
|
|
697
|
+
const spec = describePanelGroup(group, prepared);
|
|
698
|
+
if (!spec)
|
|
699
|
+
continue;
|
|
700
|
+
elements.push(collapsibleMarkdownPanel({
|
|
701
|
+
title: spec.title,
|
|
702
|
+
expanded: spec.expanded,
|
|
703
|
+
border: spec.border,
|
|
704
|
+
content: spec.content,
|
|
705
|
+
textSize: 'notation',
|
|
706
|
+
}));
|
|
673
707
|
}
|
|
674
708
|
if (elements.length === 0) {
|
|
675
709
|
elements.push(markdownDiv('_暂无输出_'));
|
|
@@ -677,8 +711,16 @@ function buildChronologicalContent(state, options, prepared, groups) {
|
|
|
677
711
|
return elements;
|
|
678
712
|
}
|
|
679
713
|
/** Build summary tab content */
|
|
680
|
-
function buildSummaryContent(state) {
|
|
714
|
+
function buildSummaryContent(state, options = {}) {
|
|
681
715
|
const elements = [];
|
|
716
|
+
// 运行期通知默认仍走 summary(skeleton 兜底:内容区整体缺失时唯一可见位置)。
|
|
717
|
+
// 正常/degraded/extreme 路径传入 includeNotices:false——通知已作为普通消息
|
|
718
|
+
// 按时间线内联渲染(renderNoticeRow),避免同一通知渲染两遍。
|
|
719
|
+
if (options.includeNotices !== false) {
|
|
720
|
+
for (const notice of state.notices ?? []) {
|
|
721
|
+
elements.push(renderNoticeRow(notice));
|
|
722
|
+
}
|
|
723
|
+
}
|
|
682
724
|
// running 时不显示统计(仍在生成);finalizing 显示等待提示
|
|
683
725
|
if (state.terminal === 'running') {
|
|
684
726
|
return elements;
|
|
@@ -710,7 +752,15 @@ function buildSummaryContent(state) {
|
|
|
710
752
|
else {
|
|
711
753
|
const result = state.resultSubtype ?? 'success';
|
|
712
754
|
const hasContent = state.blocks.length > 0;
|
|
713
|
-
|
|
755
|
+
// 压缩(operationKind='compaction')不会返回 agent 正文——引擎只压缩上下文
|
|
756
|
+
// 窗口,空内容属正常,不能沿用普通 run 的「(未返回内容)」异常信号
|
|
757
|
+
// (2026-09 用户反馈)。所有会压缩的 agent(codex/claude/kimi/opencode/pi)
|
|
758
|
+
// 都经 streamCodexCompact 走这条共享渲染路径,一处覆盖全部。
|
|
759
|
+
const empty = hasContent
|
|
760
|
+
? ''
|
|
761
|
+
: state.operationKind === 'compaction'
|
|
762
|
+
? '\n\n🗜 Compact 完成'
|
|
763
|
+
: '\n\n(未返回内容)';
|
|
714
764
|
const usageStatsStr = formatUsageStats({
|
|
715
765
|
contextLength: state.contextLength,
|
|
716
766
|
contextLimit: state.contextLimit,
|
|
@@ -726,6 +776,9 @@ function buildSummaryContent(state) {
|
|
|
726
776
|
cumulativeOutputTokens: state.cumulativeOutputTokens,
|
|
727
777
|
cumulativeCacheReadTokens: state.cumulativeCacheReadTokens,
|
|
728
778
|
cumulativeCacheCreationTokens: state.cumulativeCacheCreationTokens,
|
|
779
|
+
model: state.model,
|
|
780
|
+
costUsd: state.costUsd,
|
|
781
|
+
reasoningTokens: state.reasoningTokens,
|
|
729
782
|
}, { showResult: true, result });
|
|
730
783
|
elements.push(markdownDiv(usageStatsStr + empty));
|
|
731
784
|
}
|
package/dist/card/run-state.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentEvent, ApprovalView } from '../runner/index.js';
|
|
1
|
+
import type { AgentEvent, ApprovalView, ToolHint } from '../runner/index.js';
|
|
2
2
|
export type RunTerminal = 'running' | 'finalizing' | 'done' | 'error' | 'interrupted' | 'idle_timeout';
|
|
3
3
|
export type RunFooter = 'thinking' | 'tool_running' | 'streaming' | null;
|
|
4
4
|
/** 中断来源(interrupted 终态的原因),供卡片如实归因。 */
|
|
@@ -22,6 +22,10 @@ export interface ToolEntry {
|
|
|
22
22
|
status: ToolStatus;
|
|
23
23
|
startedAt?: string;
|
|
24
24
|
completedAt?: string;
|
|
25
|
+
/** 工具渲染 hint(turn_diff 首见时携带;渲染层只消费不推断)。 */
|
|
26
|
+
toolHint?: ToolHint;
|
|
27
|
+
/** 工具原始 title(ACP kind 映射后与 name 不同时存在;渲染做副标题)。 */
|
|
28
|
+
summary?: string;
|
|
25
29
|
}
|
|
26
30
|
export type RunBlock = {
|
|
27
31
|
kind: 'thinking';
|
|
@@ -56,6 +60,14 @@ export type RunBlock = {
|
|
|
56
60
|
timestamp?: string;
|
|
57
61
|
completedAt?: string;
|
|
58
62
|
};
|
|
63
|
+
/** 运行期通知(warning / 重路由 / 压缩提示)。保留到达时间,渲染层可将其
|
|
64
|
+
* 按时间线插入内容流当普通消息;无时间戳时兜底置于内容流末尾。 */
|
|
65
|
+
export interface RunNotice {
|
|
66
|
+
level: 'info' | 'warn' | 'error';
|
|
67
|
+
code?: string;
|
|
68
|
+
text: string;
|
|
69
|
+
timestamp?: string;
|
|
70
|
+
}
|
|
59
71
|
export interface RunState {
|
|
60
72
|
runId: string;
|
|
61
73
|
terminal: RunTerminal;
|
|
@@ -90,6 +102,20 @@ export interface RunState {
|
|
|
90
102
|
/** 会话累计 cache creation token(所有 run 之和)。 */
|
|
91
103
|
cumulativeCacheCreationTokens?: number;
|
|
92
104
|
plan?: string;
|
|
105
|
+
/** 实际生效模型(system.init 携带;失败兜底 synthetic init 为 '',渲染须容忍)。 */
|
|
106
|
+
model?: string;
|
|
107
|
+
/** 本次 run 花费(USD,result 事件 total_cost_usd)。 */
|
|
108
|
+
costUsd?: number;
|
|
109
|
+
/** 运行期通知(warning / 重路由 / 压缩提示),按到达顺序。 */
|
|
110
|
+
notices?: RunNotice[];
|
|
111
|
+
/** 被 MAX_BLOCKS 静默丢弃的早期块计数(>0 时渲染「已省略 N 个早期步骤」)。 */
|
|
112
|
+
omittedBlocks?: number;
|
|
113
|
+
/** 会话标题(ACP session_info_update.title)。 */
|
|
114
|
+
sessionTitle?: string;
|
|
115
|
+
/** 当前模式(ACP current_mode_update.currentModeId)。 */
|
|
116
|
+
mode?: string;
|
|
117
|
+
/** 推理 token(pi usage.reasoning 累加,经 finish meta 写入)。 */
|
|
118
|
+
reasoningTokens?: number;
|
|
93
119
|
/** Codex 操作类型:'turn' 表示普通 run,'compaction' 表示 compact 运行。 */
|
|
94
120
|
operationKind?: 'turn' | 'compaction';
|
|
95
121
|
/** 压缩前上下文水位(codex compact 卡展示「压缩前 X → 压缩后 Y」)。 */
|
|
@@ -135,6 +161,8 @@ export interface FinishMeta {
|
|
|
135
161
|
cumulativeOutputTokens?: number;
|
|
136
162
|
cumulativeCacheReadTokens?: number;
|
|
137
163
|
cumulativeCacheCreationTokens?: number;
|
|
164
|
+
/** 推理 token(pi usage.reasoning 累加,经 usageMeta 透传)。 */
|
|
165
|
+
reasoningTokens?: number;
|
|
138
166
|
}
|
|
139
167
|
export declare function createInitialRunState(runId: string): RunState;
|
|
140
168
|
export declare function reduceRunState(state: RunState, event: AgentEvent): RunState;
|