lark-remote 0.1.5 → 0.1.7
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 +39 -0
- package/README.en.md +5 -1
- package/README.md +10 -2
- package/dist/bridge/approval-coordinator.d.ts +131 -0
- package/dist/bridge/approval-coordinator.js +409 -0
- package/dist/bridge/inbound-media.d.ts +62 -0
- package/dist/bridge/inbound-media.js +282 -0
- package/dist/bridge/index.d.ts +138 -5
- package/dist/bridge/index.js +569 -65
- package/dist/bridge/queue-manager.d.ts +11 -1
- package/dist/bridge/queue-manager.js +20 -11
- package/dist/card/approval-render.d.ts +16 -0
- package/dist/card/approval-render.js +363 -0
- package/dist/card/bash-card-session.d.ts +2 -24
- package/dist/card/bash-card-session.js +6 -73
- package/dist/card/bash-renderer.js +2 -3
- package/dist/card/card-budget.js +29 -75
- package/dist/card/card-session.d.ts +48 -0
- package/dist/card/card-session.js +86 -0
- package/dist/card/card-shared.d.ts +13 -10
- package/dist/card/card-shared.js +27 -24
- package/dist/card/run-card-session.d.ts +2 -55
- package/dist/card/run-card-session.js +6 -104
- package/dist/card/run-renderer.js +140 -202
- package/dist/card/run-state.d.ts +38 -3
- package/dist/card/run-state.js +221 -11
- package/dist/card/text-truncate.d.ts +13 -12
- package/dist/card/text-truncate.js +13 -63
- package/dist/common/fs.d.ts +8 -0
- package/dist/common/fs.js +17 -0
- package/dist/common/guards.d.ts +0 -20
- package/dist/common/guards.js +0 -43
- package/dist/common/sleep.d.ts +2 -0
- package/dist/common/sleep.js +5 -0
- package/dist/common/truncate.d.ts +39 -0
- package/dist/common/truncate.js +92 -0
- package/dist/config/codex-config.d.ts +1 -1
- package/dist/config/codex-config.js +1 -1
- package/dist/config/dir.js +1 -1
- package/dist/config/index.d.ts +116 -9
- package/dist/config/index.js +122 -29
- package/dist/connector/index.d.ts +74 -2
- package/dist/connector/index.js +119 -1
- package/dist/index.js +139 -99
- package/dist/instance-lock.js +4 -12
- package/dist/order/alias-resolve.d.ts +13 -0
- package/dist/order/alias-resolve.js +22 -0
- package/dist/order/alias-store.d.ts +38 -0
- package/dist/order/alias-store.js +106 -0
- package/dist/persistence/atomic-write.d.ts +8 -2
- package/dist/persistence/atomic-write.js +23 -7
- package/dist/restart.js +2 -1
- package/dist/router/card-helpers.d.ts +63 -0
- package/dist/router/card-helpers.js +146 -0
- package/dist/router/config/claude.js +25 -1
- package/dist/router/config/codex.d.ts +1 -1
- package/dist/router/config/codex.js +33 -5
- package/dist/router/config/common/model-patch.d.ts +22 -0
- package/dist/router/config/common/model-patch.js +26 -0
- package/dist/router/config/common/render.js +9 -4
- package/dist/router/config/index.d.ts +12 -0
- package/dist/router/config/index.js +25 -1
- package/dist/router/config/kimi.d.ts +4 -1
- package/dist/router/config/kimi.js +27 -0
- package/dist/router/config/opencode.js +18 -5
- package/dist/router/config/pi.js +4 -5
- package/dist/router/config/types.d.ts +12 -3
- package/dist/router/index.d.ts +77 -17
- package/dist/router/index.js +455 -549
- package/dist/router/utils.d.ts +4 -0
- package/dist/router/utils.js +9 -2
- package/dist/runner/agent-choices-common.d.ts +16 -0
- package/dist/runner/agent-choices-common.js +31 -0
- package/dist/runner/claude/runner.d.ts +47 -10
- package/dist/runner/claude/runner.js +117 -46
- package/dist/runner/claude/session.d.ts +166 -0
- package/dist/runner/claude/session.js +749 -0
- package/dist/runner/codex/app-server/protocol-types.d.ts +519 -0
- package/dist/runner/codex/app-server/protocol-types.js +56 -0
- package/dist/runner/codex/app-server/runner.d.ts +104 -0
- package/dist/runner/codex/app-server/runner.js +377 -0
- package/dist/runner/codex/app-server/translator.d.ts +89 -0
- package/dist/runner/codex/app-server/translator.js +716 -0
- package/dist/runner/codex/index.d.ts +1 -1
- package/dist/runner/codex/index.js +1 -1
- package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
- package/dist/runner/common/acp/protocol-helpers.js +21 -0
- package/dist/runner/common/acp/protocol-types.d.ts +238 -0
- package/dist/runner/common/acp/protocol-types.js +75 -0
- package/dist/runner/common/connection-based-runner.d.ts +111 -0
- package/dist/runner/common/connection-based-runner.js +261 -0
- package/dist/runner/common/jsonrpc/client.d.ts +89 -0
- package/dist/runner/common/jsonrpc/client.js +243 -0
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
- package/dist/runner/common/jsonrpc/connection-manager.js +205 -0
- package/dist/runner/common/jsonrpc/transport.d.ts +50 -0
- package/dist/runner/common/jsonrpc/transport.js +230 -0
- package/dist/runner/common/jsonrpc/types.d.ts +32 -0
- package/dist/runner/common/jsonrpc/types.js +9 -0
- package/dist/runner/common/process-stopper.d.ts +2 -1
- package/dist/runner/common/process-stopper.js +1 -0
- package/dist/runner/common/runner-utils.d.ts +6 -13
- package/dist/runner/common/runner-utils.js +6 -47
- package/dist/runner/common/spawning-runner.d.ts +24 -70
- package/dist/runner/common/spawning-runner.js +98 -152
- package/dist/runner/common/usage.d.ts +2 -0
- package/dist/runner/common/usage.js +2 -0
- package/dist/runner/index.d.ts +5 -5
- package/dist/runner/index.js +4 -4
- package/dist/runner/kimi/acp/runner.d.ts +106 -0
- package/dist/runner/kimi/acp/runner.js +448 -0
- package/dist/runner/kimi/acp/translator.d.ts +124 -0
- package/dist/runner/kimi/acp/translator.js +336 -0
- package/dist/runner/kimi/index.d.ts +1 -1
- package/dist/runner/kimi/index.js +1 -1
- package/dist/runner/opencode/acp/index.d.ts +1 -0
- package/dist/runner/opencode/acp/index.js +2 -0
- package/dist/runner/opencode/acp/runner.d.ts +121 -0
- package/dist/runner/opencode/acp/runner.js +429 -0
- package/dist/runner/opencode/acp/translator.d.ts +119 -0
- package/dist/runner/opencode/acp/translator.js +345 -0
- package/dist/runner/opencode/index.d.ts +1 -1
- package/dist/runner/opencode/index.js +1 -1
- package/dist/runner/pi/index.d.ts +1 -1
- package/dist/runner/pi/index.js +1 -1
- package/dist/runner/pi/rpc/client.d.ts +52 -0
- package/dist/runner/pi/rpc/client.js +114 -0
- package/dist/runner/pi/rpc/index.d.ts +4 -0
- package/dist/runner/pi/rpc/index.js +4 -0
- package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
- package/dist/runner/pi/rpc/protocol-types.js +21 -0
- package/dist/runner/pi/rpc/runner.d.ts +80 -0
- package/dist/runner/pi/rpc/runner.js +199 -0
- package/dist/runner/pi/rpc/translator.d.ts +55 -0
- package/dist/runner/pi/rpc/translator.js +258 -0
- package/dist/runner/registry.d.ts +7 -8
- package/dist/runner/registry.js +3 -4
- package/dist/runner/resolve-agent-choices.js +18 -59
- package/dist/runner/sync-agent-choices.js +16 -67
- package/dist/runner/types.d.ts +153 -2
- package/dist/session/claude/session-index.js +2 -3
- package/dist/session/claude/session-reader.js +3 -6
- package/dist/session/claude/sessions.js +6 -13
- package/dist/session/codex/rollout-reader.d.ts +2 -2
- package/dist/session/codex/rollout-reader.js +57 -17
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +2 -2
- package/dist/session/common/content-blocks.d.ts +7 -0
- package/dist/session/common/content-blocks.js +13 -9
- package/dist/session/common/jsonl.d.ts +8 -25
- package/dist/session/common/jsonl.js +126 -261
- package/dist/session/common/pagination.d.ts +28 -0
- package/dist/session/common/pagination.js +33 -0
- package/dist/session/kimi/sessions.d.ts +34 -1
- package/dist/session/kimi/sessions.js +208 -98
- package/dist/session/opencode/sessions.d.ts +17 -3
- package/dist/session/opencode/sessions.js +29 -29
- package/dist/session/pi/sessions.d.ts +5 -1
- package/dist/session/pi/sessions.js +12 -16
- package/dist/session/session-store.d.ts +11 -1
- package/dist/session/session-store.js +75 -178
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/install.d.ts +20 -2
- package/dist/update/install.js +51 -7
- package/package.json +1 -1
- package/dist/runner/codex/argv.d.ts +0 -33
- package/dist/runner/codex/argv.js +0 -40
- package/dist/runner/codex/jsonl.d.ts +0 -37
- package/dist/runner/codex/jsonl.js +0 -221
- package/dist/runner/codex/runner.d.ts +0 -63
- package/dist/runner/codex/runner.js +0 -124
- package/dist/runner/common/exec-translator.d.ts +0 -73
- package/dist/runner/common/exec-translator.js +0 -105
- package/dist/runner/kimi/jsonl.d.ts +0 -28
- package/dist/runner/kimi/jsonl.js +0 -143
- package/dist/runner/kimi/runner.d.ts +0 -49
- package/dist/runner/kimi/runner.js +0 -105
- package/dist/runner/opencode/argv.d.ts +0 -32
- package/dist/runner/opencode/argv.js +0 -35
- package/dist/runner/opencode/jsonl.d.ts +0 -68
- package/dist/runner/opencode/jsonl.js +0 -295
- package/dist/runner/opencode/runner.d.ts +0 -61
- package/dist/runner/opencode/runner.js +0 -93
- package/dist/runner/pi/jsonl.d.ts +0 -128
- package/dist/runner/pi/jsonl.js +0 -249
- package/dist/runner/pi/runner.d.ts +0 -36
- package/dist/runner/pi/runner.js +0 -76
- package/dist/session/common/catch-up.d.ts +0 -12
- package/dist/session/common/catch-up.js +0 -31
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { terminalToColor, terminalToLabel, newSessionButton, stopButton, agentDisplayName, } from './card-shared.js';
|
|
1
|
+
import { terminalToColor, terminalToLabel, newSessionButton, stopButton, compactButton, agentDisplayName, } from './card-shared.js';
|
|
2
2
|
import { collapsibleMarkdownPanel, markdownDiv } from './collapsible.js';
|
|
3
3
|
import { toolBodyMd, toolHeaderText } from './tool-render.js';
|
|
4
|
-
import { truncateUtf8, truncateMarkdownTables } from './text-truncate.js';
|
|
4
|
+
import { truncateUtf8, truncateMarkdownTables, CARD_BUDGET_BYTES } from './text-truncate.js';
|
|
5
5
|
import { formatTimestamp } from './time.js';
|
|
6
|
-
import { formatUsageStats } from '../router/
|
|
7
|
-
|
|
6
|
+
import { formatUsageStats } from '../router/utils.js';
|
|
7
|
+
import { renderApprovalArea } from './approval-render.js';
|
|
8
8
|
const REASONING_BYTES = 4_500;
|
|
9
9
|
const TEXT_BYTES = 10_000;
|
|
10
10
|
const DEGRADED_THINKING_KEEP = 2;
|
|
@@ -24,6 +24,20 @@ function measureJson(obj) {
|
|
|
24
24
|
}
|
|
25
25
|
/** markdownDiv('', 'notation') 的序列化字节(空内容 div 的常量开销,估算时复用)。 */
|
|
26
26
|
const EMPTY_MD_DIV_BYTES = measureJson(markdownDiv('', 'notation'));
|
|
27
|
+
/**
|
|
28
|
+
* Compact 按钮可见性:普通 run(operationKind='turn')到达任意终态即可压缩。
|
|
29
|
+
* 异常退出(error/interrupted/idle_timeout)时上下文往往更大,更需要压缩后
|
|
30
|
+
* 再继续,因此不再要求 resultSubtype==='success'。compaction 卡
|
|
31
|
+
* (operationKind='compaction')永不显示,防止递归 Compact。
|
|
32
|
+
*/
|
|
33
|
+
function shouldShowCompactButton(state) {
|
|
34
|
+
if (state.operationKind !== 'turn')
|
|
35
|
+
return false;
|
|
36
|
+
return (state.terminal === 'done' ||
|
|
37
|
+
state.terminal === 'error' ||
|
|
38
|
+
state.terminal === 'interrupted' ||
|
|
39
|
+
state.terminal === 'idle_timeout');
|
|
40
|
+
}
|
|
27
41
|
/**
|
|
28
42
|
* Wrap text block in a collapsible panel with timestamp in the header.
|
|
29
43
|
* This unifies timestamp position across all block types (thinking/plan/file_change/tool/text).
|
|
@@ -174,92 +188,35 @@ function toolTitle(tool) {
|
|
|
174
188
|
const title = toolHeaderText(tool);
|
|
175
189
|
return ts ? `${title} (${ts})` : title;
|
|
176
190
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const omitted = allThinkingBlocks.length - DEGRADED_THINKING_KEEP;
|
|
192
|
-
elements.push(markdownDiv(`_💡 ${omitted} 个早期思考已省略_`));
|
|
193
|
-
}
|
|
194
|
-
// Keep only the last N thinking blocks for display
|
|
195
|
-
const thinkingBlocksToShow = allThinkingBlocks.slice(-DEGRADED_THINKING_KEEP);
|
|
196
|
-
const toolsToShow = allToolBlocks.slice(-DEGRADED_TOOL_KEEP);
|
|
197
|
-
const toolsOmitted = allToolBlocks.length - DEGRADED_TOOL_KEEP;
|
|
198
|
-
// Add omit hint for tools if any were omitted
|
|
199
|
-
if (toolsOmitted > 0) {
|
|
200
|
-
elements.push(markdownDiv(`_💡 另外 ${toolsOmitted} 个工具调用已省略_`));
|
|
201
|
-
}
|
|
202
|
-
// Build a filtered blocks list that only includes the thinking blocks we want to show
|
|
203
|
-
// and the tool blocks we want to show.
|
|
204
|
-
// P2-6: use object references (like the extreme path) instead of timestamps.
|
|
205
|
-
// RunBlock.timestamp has millisecond precision; ≥2 thinking blocks sharing a
|
|
206
|
-
// timestamp collapse to one Set entry, so `has(block.timestamp)` lets ALL
|
|
207
|
-
// same-timestamp blocks (including ones meant to be omitted) pass through —
|
|
208
|
-
// inflating the degraded card past 28KB and contradicting the "N omitted"
|
|
209
|
-
// hint. `thinkingBlocksToShow` is a slice of `allThinkingBlocks`, so the
|
|
210
|
-
// blocks retain stable references and the Set membership check is exact.
|
|
211
|
-
const thinkingSetToShow = new Set(thinkingBlocksToShow);
|
|
212
|
-
const toolIdsToShow = new Set(toolsToShow.map((b) => b.tool.id));
|
|
213
|
-
const filteredBlocks = state.blocks.filter((block) => {
|
|
214
|
-
if (block.kind === 'thinking') {
|
|
215
|
-
return thinkingSetToShow.has(block);
|
|
216
|
-
}
|
|
217
|
-
if (block.kind === 'tool') {
|
|
218
|
-
return toolIdsToShow.has(block.tool.id);
|
|
219
|
-
}
|
|
220
|
-
return true; // keep all non-thinking, non-tool blocks
|
|
221
|
-
});
|
|
222
|
-
// Render thinking/text/tool in chronological order using groupBlocks
|
|
223
|
-
for (const group of groupBlocks(filteredBlocks)) {
|
|
224
|
-
if (group.kind === 'thinking') {
|
|
225
|
-
if (options.showThinking === false)
|
|
226
|
-
continue;
|
|
227
|
-
const ts = formatTimestamp(group.timestamp);
|
|
228
|
-
const title = '💭 **思考完成**';
|
|
229
|
-
const content = truncateUtf8(group.content, REASONING_BYTES);
|
|
230
|
-
const header = ts ? `${title} (${ts})` : title;
|
|
231
|
-
elements.push(collapsibleMarkdownPanel({
|
|
232
|
-
title: header,
|
|
233
|
-
expanded: false,
|
|
234
|
-
border: 'grey',
|
|
235
|
-
content,
|
|
236
|
-
textSize: 'notation',
|
|
237
|
-
}));
|
|
238
|
-
}
|
|
239
|
-
else if (group.kind === 'text') {
|
|
240
|
-
const content = truncateUtf8(group.content, TEXT_BYTES, true, '…(已截断)\n');
|
|
241
|
-
const ts = formatTimestamp(group.timestamp);
|
|
242
|
-
const els = renderTextBlock(content, ts, true);
|
|
243
|
-
elements.push(...els);
|
|
244
|
-
}
|
|
245
|
-
else if (group.kind === 'tool') {
|
|
246
|
-
// Degraded: show tool individually (each tool is independent now)
|
|
247
|
-
if (options.showToolUse === false)
|
|
248
|
-
continue;
|
|
249
|
-
elements.push(renderTool(group.tool, true, options.showToolResult !== false)); // finalized=true
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
elements.push(...buildSummaryContent(state));
|
|
253
|
-
// Bottom action row: stop (if running) + new session (always)
|
|
191
|
+
const DEGRADED_TIER = {
|
|
192
|
+
thinkingKeep: DEGRADED_THINKING_KEEP,
|
|
193
|
+
thinkingBytes: REASONING_BYTES,
|
|
194
|
+
toolKeep: DEGRADED_TOOL_KEEP,
|
|
195
|
+
textBytes: TEXT_BYTES,
|
|
196
|
+
};
|
|
197
|
+
const EXTREME_TIER = {
|
|
198
|
+
thinkingKeep: 1,
|
|
199
|
+
thinkingBytes: DEGRADED_THINKING_BYTES,
|
|
200
|
+
toolKeep: EXTREME_TOOL_KEEP,
|
|
201
|
+
textBytes: DEGRADED_TEXT_BYTES,
|
|
202
|
+
};
|
|
203
|
+
/** Bottom action row: stop (if running) + compact (if applicable) + new session (always). */
|
|
204
|
+
function actionRow(state) {
|
|
254
205
|
const actionButtons = [];
|
|
255
206
|
// finalizing 也显示停止按钮(进程未退出,用户可 /stop)
|
|
256
207
|
const showStop = state.terminal === 'running' || state.terminal === 'finalizing';
|
|
257
208
|
if (showStop) {
|
|
258
209
|
actionButtons.push(stopButton(state.runId));
|
|
259
210
|
}
|
|
211
|
+
// Compact 按钮:普通 turn 到达任意终态即可压缩(含异常退出),compaction 卡除外
|
|
212
|
+
if (shouldShowCompactButton(state)) {
|
|
213
|
+
actionButtons.push(compactButton(state.runId));
|
|
214
|
+
}
|
|
260
215
|
actionButtons.push(newSessionButton());
|
|
261
|
-
if (actionButtons.length
|
|
262
|
-
|
|
216
|
+
if (actionButtons.length === 0)
|
|
217
|
+
return [];
|
|
218
|
+
return [
|
|
219
|
+
{ tag: 'div', text: { content: '', tag: 'lark_md' } }, // spacer
|
|
263
220
|
{
|
|
264
221
|
tag: 'column_set',
|
|
265
222
|
columns: actionButtons.map((btn) => ({
|
|
@@ -267,53 +224,80 @@ function buildDegradedElements(state, options) {
|
|
|
267
224
|
width: 'auto',
|
|
268
225
|
elements: [btn],
|
|
269
226
|
})),
|
|
270
|
-
}
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
/** 审批区位于 body 最底部(底部操作行之后):待审批时决策按钮不遮挡内容流。 */
|
|
231
|
+
function approvalArea(state) {
|
|
232
|
+
const elements = [];
|
|
233
|
+
for (const slot of state.approvals ?? []) {
|
|
234
|
+
elements.push(...renderApprovalArea(slot.view, {
|
|
235
|
+
expired: slot.expired,
|
|
236
|
+
runId: state.runId,
|
|
237
|
+
terminal: state.terminal,
|
|
238
|
+
}));
|
|
271
239
|
}
|
|
272
240
|
return elements;
|
|
273
241
|
}
|
|
242
|
+
/** Assemble the card shell (schema/config/header) around body elements. */
|
|
243
|
+
function assembleRunCard(state, options, elements) {
|
|
244
|
+
return {
|
|
245
|
+
schema: '2.0',
|
|
246
|
+
config: { wide_screen_mode: true, update_multi: true },
|
|
247
|
+
header: {
|
|
248
|
+
template: headerTemplate2(state),
|
|
249
|
+
title: { content: headerTitle2(state, options), tag: 'plain_text' },
|
|
250
|
+
},
|
|
251
|
+
body: { elements },
|
|
252
|
+
};
|
|
253
|
+
}
|
|
274
254
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* 性能优化:单遍分桶替代多次 state.blocks.filter()。
|
|
255
|
+
* Build a budget-fallback tier (degraded or extreme): keep the last N thinking
|
|
256
|
+
* blocks and last N tools, truncate text/thinking per tier, then render in
|
|
257
|
+
* chronological order. Shared by buildDegradedElements / buildExtremeFallback.
|
|
280
258
|
*/
|
|
281
|
-
function
|
|
259
|
+
function buildFallbackElements(state, options, tier) {
|
|
282
260
|
const elements = [];
|
|
283
261
|
elements.push(statusRow(state));
|
|
284
|
-
// 单遍分桶:thinking / tool
|
|
262
|
+
// 单遍分桶:thinking / tool(保持原相对顺序)
|
|
285
263
|
const { thinking: allThinkingBlocks, tool: allToolBlocks } = bucketThinkingAndTool(state.blocks);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const omitted = allThinkingBlocks.length - 1;
|
|
264
|
+
if (allThinkingBlocks.length > tier.thinkingKeep) {
|
|
265
|
+
const omitted = allThinkingBlocks.length - tier.thinkingKeep;
|
|
289
266
|
elements.push(markdownDiv(`_💡 ${omitted} 个早期思考已省略_`));
|
|
290
267
|
}
|
|
291
|
-
const
|
|
292
|
-
const toolsToShow = allToolBlocks.slice(-
|
|
293
|
-
const toolsOmitted = allToolBlocks.length -
|
|
268
|
+
const thinkingBlocksToShow = allThinkingBlocks.slice(-tier.thinkingKeep);
|
|
269
|
+
const toolsToShow = allToolBlocks.slice(-tier.toolKeep);
|
|
270
|
+
const toolsOmitted = allToolBlocks.length - tier.toolKeep;
|
|
294
271
|
// Add omit hint for tools if any were omitted
|
|
295
272
|
if (toolsOmitted > 0) {
|
|
296
273
|
elements.push(markdownDiv(`_💡 另外 ${toolsOmitted} 个工具调用已省略_`));
|
|
297
274
|
}
|
|
298
|
-
//
|
|
275
|
+
// P2-6: use object references (like the extreme path) instead of timestamps.
|
|
276
|
+
// RunBlock.timestamp has millisecond precision; ≥2 thinking blocks sharing a
|
|
277
|
+
// timestamp collapse to one Set entry, so `has(block.timestamp)` lets ALL
|
|
278
|
+
// same-timestamp blocks (including ones meant to be omitted) pass through —
|
|
279
|
+
// inflating the card past 28KB and contradicting the "N omitted" hint.
|
|
280
|
+
// `thinkingBlocksToShow` is a slice of `allThinkingBlocks`, so the blocks
|
|
281
|
+
// retain stable references and the Set membership check is exact.
|
|
282
|
+
const thinkingSetToShow = new Set(thinkingBlocksToShow);
|
|
299
283
|
const toolIdsToShow = new Set(toolsToShow.map((b) => b.tool.id));
|
|
300
284
|
const filteredBlocks = state.blocks.filter((block) => {
|
|
301
285
|
if (block.kind === 'thinking') {
|
|
302
|
-
return block
|
|
286
|
+
return thinkingSetToShow.has(block);
|
|
303
287
|
}
|
|
304
288
|
if (block.kind === 'tool') {
|
|
305
289
|
return toolIdsToShow.has(block.tool.id);
|
|
306
290
|
}
|
|
307
|
-
return true; // keep all
|
|
291
|
+
return true; // keep all non-thinking, non-tool blocks
|
|
308
292
|
});
|
|
309
|
-
// Render in chronological order
|
|
293
|
+
// Render thinking/text/tool in chronological order using groupBlocks
|
|
310
294
|
for (const group of groupBlocks(filteredBlocks)) {
|
|
311
295
|
if (group.kind === 'thinking') {
|
|
312
296
|
if (options.showThinking === false)
|
|
313
297
|
continue;
|
|
314
298
|
const ts = formatTimestamp(group.timestamp);
|
|
315
299
|
const title = '💭 **思考完成**';
|
|
316
|
-
const content = truncateUtf8(group.content,
|
|
300
|
+
const content = truncateUtf8(group.content, tier.thinkingBytes);
|
|
317
301
|
const header = ts ? `${title} (${ts})` : title;
|
|
318
302
|
elements.push(collapsibleMarkdownPanel({
|
|
319
303
|
title: header,
|
|
@@ -324,43 +308,40 @@ function buildExtremeFallbackElements(state, options) {
|
|
|
324
308
|
}));
|
|
325
309
|
}
|
|
326
310
|
else if (group.kind === 'text') {
|
|
327
|
-
const content = truncateUtf8(group.content,
|
|
311
|
+
const content = truncateUtf8(group.content, tier.textBytes, true, '…(已截断)\n');
|
|
328
312
|
const ts = formatTimestamp(group.timestamp);
|
|
329
|
-
|
|
330
|
-
elements.push(...els);
|
|
313
|
+
elements.push(...renderTextBlock(content, ts, true));
|
|
331
314
|
}
|
|
332
315
|
else if (group.kind === 'tool') {
|
|
333
|
-
//
|
|
316
|
+
// Show tool individually (each tool is independent now)
|
|
334
317
|
if (options.showToolUse === false)
|
|
335
318
|
continue;
|
|
336
|
-
elements.push(renderTool(group.tool, true, options.showToolResult !== false));
|
|
319
|
+
elements.push(renderTool(group.tool, true, options.showToolResult !== false)); // finalized=true
|
|
337
320
|
}
|
|
338
321
|
}
|
|
339
|
-
if (elements.length === 0) {
|
|
322
|
+
if (elements.length === 0 && tier === EXTREME_TIER) {
|
|
340
323
|
elements.push(markdownDiv('_暂无输出_'));
|
|
341
324
|
}
|
|
342
325
|
elements.push(...buildSummaryContent(state));
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
// finalizing 也显示停止按钮(进程未退出,用户可 /stop)
|
|
346
|
-
const showStop = state.terminal === 'running' || state.terminal === 'finalizing';
|
|
347
|
-
if (showStop) {
|
|
348
|
-
actionButtons.push(stopButton(state.runId));
|
|
349
|
-
}
|
|
350
|
-
actionButtons.push(newSessionButton());
|
|
351
|
-
if (actionButtons.length > 0) {
|
|
352
|
-
elements.push({ tag: 'div', text: { content: '', tag: 'lark_md' } }, // spacer
|
|
353
|
-
{
|
|
354
|
-
tag: 'column_set',
|
|
355
|
-
columns: actionButtons.map((btn) => ({
|
|
356
|
-
tag: 'column',
|
|
357
|
-
width: 'auto',
|
|
358
|
-
elements: [btn],
|
|
359
|
-
})),
|
|
360
|
-
});
|
|
361
|
-
}
|
|
326
|
+
elements.push(...actionRow(state));
|
|
327
|
+
elements.push(...approvalArea(state));
|
|
362
328
|
return elements;
|
|
363
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Build degraded elements when full card exceeds 28KB budget.
|
|
332
|
+
* Strategy: text fully preserved, thinking keeps last 2, tools keep last N (compressed).
|
|
333
|
+
*/
|
|
334
|
+
function buildDegradedElements(state, options) {
|
|
335
|
+
return buildFallbackElements(state, options, DEGRADED_TIER);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Extreme fallback when even degraded card exceeds 28KB.
|
|
339
|
+
* Strategy: text truncated to 5KB tail, thinking only last 1 truncated to 1KB,
|
|
340
|
+
* tools keep last N (more aggressive than degraded).
|
|
341
|
+
*/
|
|
342
|
+
function buildExtremeFallbackElements(state, options) {
|
|
343
|
+
return buildFallbackElements(state, options, EXTREME_TIER);
|
|
344
|
+
}
|
|
364
345
|
/** Build the status row element (shared across full/degraded/extreme). */
|
|
365
346
|
function statusRow(state) {
|
|
366
347
|
const statusLabel = statusTagLabel(state.terminal);
|
|
@@ -399,23 +380,9 @@ function buildSkeletonElements(state) {
|
|
|
399
380
|
elements.push(markdownDiv('_⚠️ 输出过大且含大量转义字符,已省略全部内容以避免超限;完整内容请查日志_'));
|
|
400
381
|
// Summary (token stats etc.) — static, bounded size
|
|
401
382
|
elements.push(...buildSummaryContent(state));
|
|
402
|
-
// Bottom action row: stop (if running/finalizing) + new session (always).
|
|
383
|
+
// Bottom action row: stop (if running/finalizing) + compact (if applicable) + new session (always).
|
|
403
384
|
// Degraded paths must keep the action buttons reachable (design constraint).
|
|
404
|
-
|
|
405
|
-
const showStop = state.terminal === 'running' || state.terminal === 'finalizing';
|
|
406
|
-
if (showStop) {
|
|
407
|
-
actionButtons.push(stopButton(state.runId));
|
|
408
|
-
}
|
|
409
|
-
actionButtons.push(newSessionButton());
|
|
410
|
-
elements.push({ tag: 'div', text: { content: '', tag: 'lark_md' } }, // spacer
|
|
411
|
-
{
|
|
412
|
-
tag: 'column_set',
|
|
413
|
-
columns: actionButtons.map((btn) => ({
|
|
414
|
-
tag: 'column',
|
|
415
|
-
width: 'auto',
|
|
416
|
-
elements: [btn],
|
|
417
|
-
})),
|
|
418
|
-
});
|
|
385
|
+
elements.push(...actionRow(state));
|
|
419
386
|
return elements;
|
|
420
387
|
}
|
|
421
388
|
/**
|
|
@@ -572,93 +539,41 @@ function measurePanelBytes(title, expanded, border, content) {
|
|
|
572
539
|
* CardKit 2.0 renderer - simplified for streaming (no tabs)
|
|
573
540
|
*/
|
|
574
541
|
export function renderRunCard(state, options = {}) {
|
|
575
|
-
const { terminal, runId } = state;
|
|
576
542
|
// 先估后建:用廉价估算预测是否超 28KB 预算,明显超预算直接跳过完整卡构建走
|
|
577
543
|
// degraded。P1-2:groups/prepared 只算一次,测量与正式渲染共用同一份截断结果,
|
|
578
544
|
// 避免影子测量 + 正式渲染对每个超限块双倍截断。
|
|
579
545
|
const groups = groupBlocks(state.blocks);
|
|
580
546
|
const prepared = prepareGroupContent(groups);
|
|
581
547
|
const estimate = estimateCardBytes(state, options, { groups, prepared });
|
|
582
|
-
// Stop button: show when running or finalizing (process not yet exited)
|
|
583
|
-
const showStop = terminal === 'running' || terminal === 'finalizing';
|
|
584
548
|
// 构建完整卡(仅在估算低于阈值时,避免大 state 浪费 render + stringify)
|
|
585
549
|
if (estimate < DEGRADED_THRESHOLD) {
|
|
586
550
|
const elements = [
|
|
587
551
|
statusRow(state),
|
|
588
552
|
...buildChronologicalContent(state, options, prepared, groups),
|
|
589
553
|
...buildSummaryContent(state),
|
|
554
|
+
...actionRow(state),
|
|
555
|
+
...approvalArea(state),
|
|
590
556
|
];
|
|
591
|
-
|
|
592
|
-
const actionButtons = [];
|
|
593
|
-
if (showStop) {
|
|
594
|
-
actionButtons.push(stopButton(runId));
|
|
595
|
-
}
|
|
596
|
-
actionButtons.push(newSessionButton());
|
|
597
|
-
if (actionButtons.length > 0) {
|
|
598
|
-
elements.push({ tag: 'div', text: { content: '', tag: 'lark_md' } }, // spacer
|
|
599
|
-
{
|
|
600
|
-
tag: 'column_set',
|
|
601
|
-
columns: actionButtons.map((btn) => ({
|
|
602
|
-
tag: 'column',
|
|
603
|
-
width: 'auto',
|
|
604
|
-
elements: [btn],
|
|
605
|
-
})),
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
const card = {
|
|
609
|
-
schema: '2.0',
|
|
610
|
-
config: { wide_screen_mode: true, update_multi: true },
|
|
611
|
-
header: {
|
|
612
|
-
template: headerTemplate2(state),
|
|
613
|
-
title: { content: headerTitle2(state, options), tag: 'plain_text' },
|
|
614
|
-
},
|
|
615
|
-
body: { elements },
|
|
616
|
-
};
|
|
557
|
+
const card = assembleRunCard(state, options, elements);
|
|
617
558
|
// Check budget — stringify 兜底(估算可能低估,仍保留安全网)
|
|
618
559
|
if (Buffer.byteLength(JSON.stringify(card), 'utf8') <= CARD_BUDGET_BYTES)
|
|
619
560
|
return card;
|
|
620
561
|
// 超预算 → 继续走 degraded(不 return,fallthrough)
|
|
621
562
|
}
|
|
622
563
|
// 估算 ≥ 阈值 或 完整卡 stringify 超预算 — 走 degraded 渲染
|
|
623
|
-
const
|
|
624
|
-
const degradedCard = {
|
|
625
|
-
schema: '2.0',
|
|
626
|
-
config: { wide_screen_mode: true, update_multi: true },
|
|
627
|
-
header: {
|
|
628
|
-
template: headerTemplate2(state),
|
|
629
|
-
title: { content: headerTitle2(state, options), tag: 'plain_text' },
|
|
630
|
-
},
|
|
631
|
-
body: { elements: degradedElements },
|
|
632
|
-
};
|
|
564
|
+
const degradedCard = assembleRunCard(state, options, buildDegradedElements(state, options));
|
|
633
565
|
if (Buffer.byteLength(JSON.stringify(degradedCard), 'utf8') <= CARD_BUDGET_BYTES) {
|
|
634
566
|
return degradedCard;
|
|
635
567
|
}
|
|
636
568
|
// Extreme fallback: even degraded exceeds budget
|
|
637
|
-
const
|
|
638
|
-
const extremeCard = {
|
|
639
|
-
schema: '2.0',
|
|
640
|
-
config: { wide_screen_mode: true, update_multi: true },
|
|
641
|
-
header: {
|
|
642
|
-
template: headerTemplate2(state),
|
|
643
|
-
title: { content: headerTitle2(state, options), tag: 'plain_text' },
|
|
644
|
-
},
|
|
645
|
-
body: { elements: extremeElements },
|
|
646
|
-
};
|
|
569
|
+
const extremeCard = assembleRunCard(state, options, buildExtremeFallbackElements(state, options));
|
|
647
570
|
// Final safety net: extreme fallback 是最后一层,但高转义内容(反斜杠 4× 膨胀)
|
|
648
571
|
// 可能让 extreme 产物本身 >28KB。补上 stringify 兜底(与 normal/degraded 对称):
|
|
649
572
|
// 超预算则降级到结构性保证 ≤28KB 的 skeleton 卡,避免飞书 11310 整卡不可用。
|
|
650
573
|
if (Buffer.byteLength(JSON.stringify(extremeCard), 'utf8') <= CARD_BUDGET_BYTES) {
|
|
651
574
|
return extremeCard;
|
|
652
575
|
}
|
|
653
|
-
const skeletonCard =
|
|
654
|
-
schema: '2.0',
|
|
655
|
-
config: { wide_screen_mode: true, update_multi: true },
|
|
656
|
-
header: {
|
|
657
|
-
template: headerTemplate2(state),
|
|
658
|
-
title: { content: headerTitle2(state, options), tag: 'plain_text' },
|
|
659
|
-
},
|
|
660
|
-
body: { elements: buildSkeletonElements(state) },
|
|
661
|
-
};
|
|
576
|
+
const skeletonCard = assembleRunCard(state, options, buildSkeletonElements(state));
|
|
662
577
|
// skeleton 卡是静态结构,理论上必 ≤28KB;保留兜底断言以防未来结构膨胀
|
|
663
578
|
if (Buffer.byteLength(JSON.stringify(skeletonCard), 'utf8') <= CARD_BUDGET_BYTES) {
|
|
664
579
|
return skeletonCard;
|
|
@@ -765,7 +680,18 @@ function buildSummaryContent(state) {
|
|
|
765
680
|
elements.push(markdownDiv(`⚠️ **运行出错**\n\n${state.errorMsg ?? '未知错误'}`));
|
|
766
681
|
}
|
|
767
682
|
else if (state.terminal === 'interrupted') {
|
|
768
|
-
|
|
683
|
+
// 审批超时(approval_expired 已标记)是「无人响应被自动取消」,不是用户
|
|
684
|
+
// 主动终止;如实展示原因,避免用户误判为 Agent 出错或自己操作过。
|
|
685
|
+
// 顶层 approvalExpired 优先:approval_resolved 会移除审批条目,但标记
|
|
686
|
+
// 必须活到终态渲染(2026-08-15 事故回归)。
|
|
687
|
+
const expiredApproval = state.interruptedReason === 'approval_timeout' ||
|
|
688
|
+
state.approvalExpired === true ||
|
|
689
|
+
(state.approvals ?? []).some((a) => a.expired);
|
|
690
|
+
elements.push(markdownDiv(expiredApproval
|
|
691
|
+
? '⏰ **审批超时未响应,已自动取消**'
|
|
692
|
+
: state.interruptedReason === 'approval_cancelled'
|
|
693
|
+
? '⏹ **已取消审批,任务终止**'
|
|
694
|
+
: '⏹ **已被用户终止**'));
|
|
769
695
|
}
|
|
770
696
|
else if (state.terminal === 'idle_timeout') {
|
|
771
697
|
elements.push(markdownDiv(`⏱ **已超时**\n\n${state.idleTimeoutMinutes ?? 0} 分钟无响应,已自动终止`));
|
|
@@ -778,6 +704,7 @@ function buildSummaryContent(state) {
|
|
|
778
704
|
contextLength: state.contextLength,
|
|
779
705
|
contextLimit: state.contextLimit,
|
|
780
706
|
compactCount: state.compactCount,
|
|
707
|
+
compactPreContextLength: state.compactPreContextLength,
|
|
781
708
|
cacheReadTokens: state.cacheReadTokens,
|
|
782
709
|
cacheCreationTokens: state.cacheCreationTokens,
|
|
783
710
|
totalTokens: state.totalTokens,
|
|
@@ -801,6 +728,9 @@ function buildDurationInfo(state) {
|
|
|
801
728
|
state.terminal === 'idle_timeout') {
|
|
802
729
|
return `⏱ ${state.footer || '已完成'}`;
|
|
803
730
|
}
|
|
731
|
+
// 与标题一致:审批等待期间状态行显示「等待审批中」,而不是「工具调用中」。
|
|
732
|
+
if (hasPendingApproval(state))
|
|
733
|
+
return '✋ 等待审批中';
|
|
804
734
|
return footerText2(state.footer);
|
|
805
735
|
}
|
|
806
736
|
function footerText2(footer) {
|
|
@@ -827,6 +757,10 @@ function headerTitle2(state, options = {}) {
|
|
|
827
757
|
return `⏱ ${agent} · 已超时`;
|
|
828
758
|
if (state.terminal === 'finalizing')
|
|
829
759
|
return `⏳ ${agent} · 完成中`;
|
|
760
|
+
// 审批等待期间 server 暂停 turn,命令工具停在 tool_running——标题必须提示
|
|
761
|
+
// 正在等人工决策,而不是「调用工具」(2026-08-14 UX)。
|
|
762
|
+
if (hasPendingApproval(state))
|
|
763
|
+
return `✋ ${agent} · 等待审批`;
|
|
830
764
|
if (state.footer === 'thinking')
|
|
831
765
|
return `💭 ${agent} · 思考中`;
|
|
832
766
|
if (state.footer === 'streaming')
|
|
@@ -835,6 +769,10 @@ function headerTitle2(state, options = {}) {
|
|
|
835
769
|
return `🔧 ${agent} · 调用工具`;
|
|
836
770
|
return `🔴 ${agent} · 运行中`;
|
|
837
771
|
}
|
|
772
|
+
/** 是否有未过期(仍可操作)的待审批请求。 */
|
|
773
|
+
function hasPendingApproval(state) {
|
|
774
|
+
return (state.approvals ?? []).some((a) => !a.expired);
|
|
775
|
+
}
|
|
838
776
|
/** CardKit 2.0 header template color */
|
|
839
777
|
function headerTemplate2(state) {
|
|
840
778
|
return terminalToColor(state.terminal);
|
package/dist/card/run-state.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { AgentEvent } from '../runner/index.js';
|
|
1
|
+
import type { AgentEvent, ApprovalView } 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
|
+
/** 中断来源(interrupted 终态的原因),供卡片如实归因。 */
|
|
5
|
+
export type InterruptedReason = 'approval_timeout' | 'user_stop' | 'approval_cancelled';
|
|
4
6
|
type ToolStatus = 'running' | 'ok' | 'error';
|
|
5
7
|
export interface ToolEntry {
|
|
6
8
|
id: string;
|
|
@@ -23,27 +25,36 @@ export interface ToolEntry {
|
|
|
23
25
|
}
|
|
24
26
|
export type RunBlock = {
|
|
25
27
|
kind: 'thinking';
|
|
28
|
+
/** app-server item id; exec-mode blocks leave it undefined. */
|
|
29
|
+
itemId?: string;
|
|
26
30
|
content: string;
|
|
27
31
|
active: boolean;
|
|
28
32
|
timestamp?: string;
|
|
33
|
+
completedAt?: string;
|
|
29
34
|
} | {
|
|
30
35
|
kind: 'text';
|
|
36
|
+
itemId?: string;
|
|
31
37
|
content: string;
|
|
32
38
|
timestamp?: string;
|
|
39
|
+
completedAt?: string;
|
|
33
40
|
} | {
|
|
34
41
|
kind: 'tool';
|
|
35
42
|
tool: ToolEntry;
|
|
36
43
|
} | {
|
|
37
44
|
kind: 'plan';
|
|
45
|
+
itemId?: string;
|
|
38
46
|
content: string;
|
|
39
47
|
active: boolean;
|
|
40
48
|
timestamp?: string;
|
|
49
|
+
completedAt?: string;
|
|
41
50
|
} | {
|
|
42
51
|
kind: 'file_change';
|
|
52
|
+
itemId?: string;
|
|
43
53
|
path: string;
|
|
44
54
|
operation: 'create' | 'edit' | 'delete' | 'read';
|
|
45
55
|
diff?: string;
|
|
46
56
|
timestamp?: string;
|
|
57
|
+
completedAt?: string;
|
|
47
58
|
};
|
|
48
59
|
export interface RunState {
|
|
49
60
|
runId: string;
|
|
@@ -51,7 +62,9 @@ export interface RunState {
|
|
|
51
62
|
footer: RunFooter;
|
|
52
63
|
blocks: RunBlock[];
|
|
53
64
|
sessionId?: string;
|
|
54
|
-
resultSubtype?: 'success' | 'error';
|
|
65
|
+
resultSubtype?: 'success' | 'error' | 'interrupted';
|
|
66
|
+
/** interrupted 终态的中断来源;undefined = 未归类(渲染默认文案)。 */
|
|
67
|
+
interruptedReason?: InterruptedReason;
|
|
55
68
|
contextLength?: number;
|
|
56
69
|
/** 当前模型 context window 上限(codex jsonl 提供);卡片据此渲染百分比。 */
|
|
57
70
|
contextLimit?: number;
|
|
@@ -77,15 +90,37 @@ export interface RunState {
|
|
|
77
90
|
/** 会话累计 cache creation token(所有 run 之和)。 */
|
|
78
91
|
cumulativeCacheCreationTokens?: number;
|
|
79
92
|
plan?: string;
|
|
93
|
+
/** Codex 操作类型:'turn' 表示普通 run,'compaction' 表示 compact 运行。 */
|
|
94
|
+
operationKind?: 'turn' | 'compaction';
|
|
95
|
+
/** 压缩前上下文水位(codex compact 卡展示「压缩前 X → 压缩后 Y」)。 */
|
|
96
|
+
compactPreContextLength?: number;
|
|
97
|
+
/**
|
|
98
|
+
* 待审批请求列表(app-server 模式,review P2-3)。同一 turn 内并发多个审批
|
|
99
|
+
* 时全部渲染,避免后到者顶掉先到者的按钮(单槽会丢 UI,只能等超时 cancel)。
|
|
100
|
+
*/
|
|
101
|
+
approvals?: Array<{
|
|
102
|
+
view: ApprovalView;
|
|
103
|
+
expired?: boolean;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* 审批是否曾过期(桥侧 timer 触发,approval_expired 事件置位)。
|
|
107
|
+
* 独立于 approvals 数组持久保存:cancel 送达后 server 会回
|
|
108
|
+
* approval_resolved,把对应审批条目从数组移除,但过期原因必须活到
|
|
109
|
+
* 终态渲染(2026-08-15 事故:标记被消费后卡片误报「已被用户终止」)。
|
|
110
|
+
*/
|
|
111
|
+
approvalExpired?: boolean;
|
|
80
112
|
}
|
|
81
113
|
export interface FinishMeta {
|
|
82
|
-
resultSubtype?: 'success' | 'error';
|
|
114
|
+
resultSubtype?: 'success' | 'error' | 'interrupted';
|
|
115
|
+
/** 中断来源:由桥侧在 finish 时显式传入(/stop → user_stop、审批卡取消 → approval_cancelled)。 */
|
|
116
|
+
interruptedReason?: InterruptedReason;
|
|
83
117
|
contextLength?: number;
|
|
84
118
|
/** 当前模型 context window 上限;透传到 RunState 供 done 卡片显示百分比。 */
|
|
85
119
|
contextLimit?: number;
|
|
86
120
|
errorMsg?: string;
|
|
87
121
|
idleTimeoutMinutes?: number;
|
|
88
122
|
compactCount?: number;
|
|
123
|
+
compactPreContextLength?: number;
|
|
89
124
|
cacheReadTokens?: number;
|
|
90
125
|
cacheCreationTokens?: number;
|
|
91
126
|
/** Agent-declared total; threaded to RunState for the done card. */
|