lark-remote 0.5.12 → 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -2
- package/README.en.md +1 -14
- package/README.md +1 -14
- package/dist/agent-kinds.d.ts +27 -0
- package/dist/agent-kinds.js +38 -0
- package/dist/bridge/approval-actions.d.ts +95 -0
- package/dist/bridge/approval-actions.js +104 -0
- package/dist/bridge/approval-coordinator.d.ts +1 -0
- package/dist/bridge/approval-coordinator.js +12 -24
- package/dist/bridge/final-usage.d.ts +8 -17
- package/dist/bridge/final-usage.js +1 -18
- package/dist/bridge/index.d.ts +52 -38
- package/dist/bridge/index.js +255 -433
- package/dist/bridge/queue-manager.d.ts +25 -1
- package/dist/bridge/queue-manager.js +179 -160
- package/dist/bridge/run-approval.d.ts +16 -0
- package/dist/bridge/run-approval.js +41 -0
- package/dist/bridge/run-usage.d.ts +32 -0
- package/dist/bridge/run-usage.js +87 -0
- package/dist/bridge/session-writeback.d.ts +19 -0
- package/dist/bridge/session-writeback.js +36 -0
- package/dist/card/approval-render.d.ts +3 -2
- package/dist/card/approval-render.js +3 -2
- package/dist/card/bash-card-session.js +2 -5
- package/dist/{router → card}/card-helpers.d.ts +7 -7
- package/dist/{router → card}/card-helpers.js +8 -8
- package/dist/card/card-session.d.ts +6 -2
- package/dist/card/card-session.js +28 -28
- package/dist/card/card-shared.d.ts +4 -4
- package/dist/card/card-shared.js +4 -10
- package/dist/card/collapsible.js +3 -2
- package/dist/card/final-answer-overflow.d.ts +1 -0
- package/dist/card/final-answer-overflow.js +46 -0
- package/dist/card/run-card-session.d.ts +2 -2
- package/dist/card/run-card-session.js +4 -9
- package/dist/card/run-renderer.d.ts +14 -0
- package/dist/card/run-renderer.js +126 -23
- package/dist/card/run-state.d.ts +40 -51
- package/dist/card/run-state.js +96 -34
- package/dist/card/usage-format.d.ts +30 -0
- package/dist/{router → card}/usage-format.js +3 -10
- package/dist/clone.js +6 -1
- package/dist/config/codex-config.js +4 -4
- package/dist/config/index.d.ts +18 -20
- package/dist/config/index.js +6 -20
- package/dist/config/pi-config.js +6 -4
- package/dist/connector/index.d.ts +2 -4
- package/dist/connector/index.js +7 -11
- package/dist/{runner/dsh → dsh}/auth.js +3 -4
- package/dist/dsh/home.d.ts +2 -0
- package/dist/dsh/home.js +8 -0
- package/dist/dsh/protocol.d.ts +9 -0
- package/dist/dsh/protocol.js +61 -0
- package/dist/inbound/turn-assembler.js +6 -0
- package/dist/index.js +25 -414
- package/dist/message-handlers.d.ts +26 -0
- package/dist/message-handlers.js +275 -0
- package/dist/model-catalog.d.ts +14 -0
- package/dist/model-catalog.js +25 -0
- package/dist/platform/path.d.ts +9 -0
- package/dist/platform/path.js +18 -0
- package/dist/router/active-run-usage.d.ts +4 -0
- package/dist/router/active-run-usage.js +12 -0
- package/dist/router/card-action-payload.d.ts +1 -1
- package/dist/router/card-action-payload.js +1 -1
- package/dist/router/commands/ls.d.ts +9 -0
- package/dist/router/commands/ls.js +243 -0
- package/dist/router/commands/order.d.ts +12 -0
- package/dist/router/commands/order.js +178 -0
- package/dist/router/commands/resume.d.ts +48 -0
- package/dist/router/commands/resume.js +246 -0
- package/dist/router/commands/ws.d.ts +15 -0
- package/dist/router/commands/ws.js +189 -0
- package/dist/router/config/claude.js +5 -4
- package/dist/router/config/codex.js +2 -6
- package/dist/router/config/common/render.d.ts +5 -1
- package/dist/router/config/common/render.js +6 -13
- package/dist/router/config/common/turn-idle.d.ts +2 -0
- package/dist/router/config/common/turn-idle.js +9 -0
- package/dist/router/config/dsh.d.ts +1 -1
- package/dist/router/config/index.d.ts +1 -1
- package/dist/router/config/kimi.js +2 -6
- package/dist/router/config/opencode.js +2 -6
- package/dist/router/config/types.d.ts +11 -3
- package/dist/router/index.d.ts +55 -33
- package/dist/router/index.js +233 -1376
- package/dist/router/paging.d.ts +13 -0
- package/dist/router/paging.js +20 -0
- package/dist/router/path-validation.d.ts +27 -0
- package/dist/router/path-validation.js +45 -0
- package/dist/runner/agent-choices-common.d.ts +1 -1
- package/dist/runner/claude/runner.d.ts +23 -6
- package/dist/runner/claude/runner.js +6 -20
- package/dist/runner/claude/session.js +1 -1
- package/dist/runner/codex/app-server/runner.d.ts +3 -1
- package/dist/runner/codex/app-server/translator.d.ts +13 -0
- package/dist/runner/codex/app-server/translator.js +114 -151
- package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
- package/dist/runner/common/acp/base-translator.d.ts +3 -8
- package/dist/runner/common/acp/base-translator.js +16 -7
- package/dist/runner/common/connection-based-runner.d.ts +3 -1
- package/dist/runner/common/jsonrpc/transport.js +17 -7
- package/dist/runner/common/spawning-runner.js +10 -4
- package/dist/runner/common/turn-started.d.ts +2 -0
- package/dist/runner/common/turn-started.js +10 -0
- package/dist/runner/dsh/client.d.ts +2 -10
- package/dist/runner/dsh/client.js +13 -220
- package/dist/runner/dsh/daemon.d.ts +0 -2
- package/dist/runner/dsh/daemon.js +9 -7
- package/dist/runner/dsh/errors.d.ts +5 -0
- package/dist/runner/dsh/errors.js +11 -0
- package/dist/runner/dsh/exit-cancel.d.ts +0 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +0 -2
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/mux-stream.d.ts +26 -0
- package/dist/runner/dsh/mux-stream.js +176 -0
- package/dist/runner/dsh/runner.d.ts +21 -1
- package/dist/runner/dsh/runner.js +76 -73
- package/dist/runner/dsh/translator.d.ts +1 -11
- package/dist/runner/dsh/translator.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +22 -1
- package/dist/runner/kimi/acp/runner.js +83 -60
- package/dist/runner/kimi/acp/translator.js +1 -12
- package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
- package/dist/runner/opencode/acp/error-monitor.js +31 -2
- package/dist/runner/opencode/acp/runner.d.ts +3 -1
- package/dist/runner/opencode/acp/translator.js +1 -12
- package/dist/runner/pi/rpc/runner.d.ts +2 -1
- package/dist/runner/pi/rpc/translator.d.ts +2 -8
- package/dist/runner/pi/rpc/translator.js +2 -7
- package/dist/runner/probe.d.ts +1 -1
- package/dist/runner/probe.js +10 -9
- package/dist/runner/registry.d.ts +2 -20
- package/dist/runner/registry.js +0 -32
- package/dist/runner/types.d.ts +37 -155
- package/dist/runner/types.js +4 -24
- package/dist/session/claude/session-index.d.ts +11 -4
- package/dist/session/claude/session-index.js +34 -10
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/sessions.d.ts +2 -2
- package/dist/session/claude/sessions.js +41 -26
- package/dist/session/codex/rollout-reader.d.ts +1 -1
- package/dist/session/codex/rollout-reader.js +14 -13
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +4 -2
- package/dist/session/common/jsonl.js +7 -1
- package/dist/session/common/pagination.d.ts +2 -0
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/common/recency.d.ts +6 -0
- package/dist/session/common/recency.js +7 -5
- package/dist/session/common/two-pass.d.ts +1 -1
- package/dist/session/common/usage-accumulator.d.ts +1 -1
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +37 -12
- package/dist/session/kimi/sessions.d.ts +1 -1
- package/dist/session/kimi/sessions.js +37 -10
- package/dist/session/opencode/sessions.d.ts +1 -1
- package/dist/session/opencode/sessions.js +53 -48
- package/dist/session/pi/sessions.d.ts +1 -1
- package/dist/session/pi/sessions.js +23 -9
- package/dist/session/registry.d.ts +2 -1
- package/dist/session/session-store.d.ts +1 -2
- package/dist/session/session-store.js +14 -7
- package/dist/session-contract.d.ts +178 -0
- package/dist/session-contract.js +9 -0
- package/package.json +1 -1
- package/dist/router/usage-format.d.ts +0 -60
- /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.js +0 -0
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Uses CardKit 2.0 (no tag:"action" container — 200861 rule).
|
|
6
6
|
*/
|
|
7
7
|
import type { ApprovalView } from '../runner/types.js';
|
|
8
|
+
import { type RunTerminal } from './run-state.js';
|
|
8
9
|
/**
|
|
9
10
|
* Render the approval area for a run card.
|
|
10
11
|
* Returns an array of CardKit 2.0 elements to append to the card body.
|
|
@@ -12,7 +13,7 @@ import type { ApprovalView } from '../runner/types.js';
|
|
|
12
13
|
export declare function renderApprovalArea(approval: ApprovalView | undefined, opts?: {
|
|
13
14
|
expired?: boolean;
|
|
14
15
|
runId?: string;
|
|
15
|
-
terminal?:
|
|
16
|
+
terminal?: RunTerminal;
|
|
16
17
|
}): object[];
|
|
17
18
|
/**
|
|
18
19
|
* 紧凑审批区(run 卡 skeleton/truncatedSkeleton 兜底档专用):固定小体积的
|
|
@@ -26,5 +27,5 @@ export declare function renderApprovalArea(approval: ApprovalView | undefined, o
|
|
|
26
27
|
export declare function renderApprovalAreaCompact(approval: ApprovalView | undefined, opts?: {
|
|
27
28
|
expired?: boolean;
|
|
28
29
|
runId?: string;
|
|
29
|
-
terminal?:
|
|
30
|
+
terminal?: RunTerminal;
|
|
30
31
|
}): object[];
|
|
@@ -9,6 +9,7 @@ import { truncateMarkdownTables } from './text-truncate.js';
|
|
|
9
9
|
import { truncateUtf8 } from '../common/truncate.js';
|
|
10
10
|
import { cardButton } from './card-shared.js';
|
|
11
11
|
import { countCardElements } from './card-budget.js';
|
|
12
|
+
import { isRunActive } from './run-state.js';
|
|
12
13
|
/** Unique nonce for card button callbacks (SDK dedup + router validation). */
|
|
13
14
|
function mkNonce() {
|
|
14
15
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -156,9 +157,9 @@ export function renderApprovalAreaCompact(approval, opts) {
|
|
|
156
157
|
}
|
|
157
158
|
return elements;
|
|
158
159
|
}
|
|
159
|
-
/** 终态隐藏判定(renderApprovalArea 与 compact
|
|
160
|
+
/** 终态隐藏判定(renderApprovalArea 与 compact 共用):仍在推进的 run 不隐藏按钮。 */
|
|
160
161
|
function isTerminalHidden(opts) {
|
|
161
|
-
return !!opts?.terminal && opts.terminal
|
|
162
|
+
return !!opts?.terminal && !isRunActive(opts.terminal);
|
|
162
163
|
}
|
|
163
164
|
/**
|
|
164
165
|
* Render approval action buttons from the real protocol decision list.
|
|
@@ -76,11 +76,8 @@ export class BashCardSession extends CardSession {
|
|
|
76
76
|
if (this.state.terminal !== 'running') {
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// 保证顺序(pre-terminal 先落地 → terminal 后落地 → terminal 胜出)。
|
|
82
|
-
if (this.flushP)
|
|
83
|
-
await this.flushP;
|
|
79
|
+
// 终态 patch 必须排在已开始的 pre-terminal flush 之后。
|
|
80
|
+
await this.prepareTerminalUpdate();
|
|
84
81
|
this.state = {
|
|
85
82
|
...this.state,
|
|
86
83
|
terminal,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* ls, ws, etc.).
|
|
2
|
+
* 卡片构造的共享工具(会话历史卡、分页条、搜索条、ls/ws 面板等)。
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* 归属 card/:这些是纯建卡函数,此前住在 router/ 导致 card→router 与
|
|
5
|
+
* bridge→router 两条反向依赖。`markdownDiv` 从 `card/collapsible.ts` 复用,
|
|
6
|
+
* 会话内容相关的 builder(如 `sessionEventPanel`)在这里单源,router 与
|
|
7
|
+
* bridge 都只调用不复制。
|
|
8
8
|
*/
|
|
9
|
-
import { markdownDiv } from '
|
|
10
|
-
import type { AgentSessionContentEvent } from '../
|
|
9
|
+
import { markdownDiv } from './collapsible.js';
|
|
10
|
+
import type { AgentSessionContentEvent } from '../session-contract.js';
|
|
11
11
|
import { formatUsageStats } from './usage-format.js';
|
|
12
12
|
type SessionUsageLike = Parameters<typeof formatUsageStats>[0];
|
|
13
13
|
export declare function sessionEventPanel(ev: AgentSessionContentEvent, index: number, totalEvents: number, tailExpandedCount?: number, agentKind?: string): object;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* ls, ws, etc.).
|
|
2
|
+
* 卡片构造的共享工具(会话历史卡、分页条、搜索条、ls/ws 面板等)。
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* 归属 card/:这些是纯建卡函数,此前住在 router/ 导致 card→router 与
|
|
5
|
+
* bridge→router 两条反向依赖。`markdownDiv` 从 `card/collapsible.ts` 复用,
|
|
6
|
+
* 会话内容相关的 builder(如 `sessionEventPanel`)在这里单源,router 与
|
|
7
|
+
* bridge 都只调用不复制。
|
|
8
8
|
*/
|
|
9
|
-
import { agentDisplayName, cardButton } from '
|
|
10
|
-
import { collapsibleMarkdownPanel, markdownDiv } from '
|
|
11
|
-
import { formatMonthDayClock } from '
|
|
9
|
+
import { agentDisplayName, cardButton } from './card-shared.js';
|
|
10
|
+
import { collapsibleMarkdownPanel, markdownDiv } from './collapsible.js';
|
|
11
|
+
import { formatMonthDayClock } from './time.js';
|
|
12
12
|
import { displayWidth, truncateMiddleToDisplayWidth } from '../common/text-width.js';
|
|
13
13
|
import { formatUsageStats } from './usage-format.js';
|
|
14
14
|
/**
|
|
@@ -107,15 +107,19 @@ export declare abstract class CardSession<S, RO = unknown> {
|
|
|
107
107
|
start(): Promise<void>;
|
|
108
108
|
settle(): Promise<StreamSettleResult>;
|
|
109
109
|
/**
|
|
110
|
-
*
|
|
110
|
+
* 渲染当前状态对应的卡片(单一渲染入口)。
|
|
111
|
+
*
|
|
112
|
+
* 所有生产子类的 renderer 都自带 stringify 级预算保护
|
|
111
113
|
* (run/bash 的 degraded/extreme 路径);发送前 Bridge 还会再走一次
|
|
112
114
|
* enforceCardBudget,因此这里不再重复裁剪。
|
|
113
115
|
*/
|
|
114
|
-
protected
|
|
116
|
+
protected renderCurrentCard(): object;
|
|
115
117
|
/**
|
|
116
118
|
* Push an update to the card. Tries controller.update first, falls back to
|
|
117
119
|
* connector.updateCard if the controller is unavailable or fails.
|
|
118
120
|
*/
|
|
119
121
|
protected updateCard(): Promise<void>;
|
|
122
|
+
private waitForControllerReady;
|
|
123
|
+
protected prepareTerminalUpdate(): Promise<void>;
|
|
120
124
|
protected release(): void;
|
|
121
125
|
}
|
|
@@ -138,17 +138,7 @@ export class CardSession {
|
|
|
138
138
|
// 二次调用也套 timeout 保护:若 controllerReady 永不 settle(首次超时失败),
|
|
139
139
|
// 不能永久挂起。复用同样的 startTimeoutMs 超时。streamOutcome 已存在,
|
|
140
140
|
// 不重复创建 stream。
|
|
141
|
-
|
|
142
|
-
const timeout = new Promise((_, reject) => {
|
|
143
|
-
timer = setTimeout(() => reject(new Error(`${this.errorPrefix} stream start timeout`)), this.startTimeoutMs);
|
|
144
|
-
});
|
|
145
|
-
try {
|
|
146
|
-
await Promise.race([this.controllerReady, timeout]);
|
|
147
|
-
}
|
|
148
|
-
finally {
|
|
149
|
-
if (timer)
|
|
150
|
-
clearTimeout(timer);
|
|
151
|
-
}
|
|
141
|
+
await this.waitForControllerReady();
|
|
152
142
|
return;
|
|
153
143
|
}
|
|
154
144
|
// NOTE: the two renderCard calls below are NOT redundant. The streamCard
|
|
@@ -160,11 +150,11 @@ export class CardSession {
|
|
|
160
150
|
// those early events. The"reuse first render" optimization was
|
|
161
151
|
// rejected for this reason (premise "two renders with identical input"
|
|
162
152
|
// does not hold).
|
|
163
|
-
const stream = this.connector.streamCard(this.chatId, this.
|
|
153
|
+
const stream = this.connector.streamCard(this.chatId, this.renderCurrentCard(), async (controller) => {
|
|
164
154
|
this.controller = controller;
|
|
165
155
|
this.messageId = controller.messageId;
|
|
166
156
|
try {
|
|
167
|
-
await controller.update(this.
|
|
157
|
+
await controller.update(this.renderCurrentCard());
|
|
168
158
|
}
|
|
169
159
|
catch (error) {
|
|
170
160
|
getLogger().warn(`${this.logPrefix} initial controller update failed:`, error);
|
|
@@ -177,17 +167,7 @@ export class CardSession {
|
|
|
177
167
|
this.rejectController(error);
|
|
178
168
|
return { ok: false, error };
|
|
179
169
|
});
|
|
180
|
-
|
|
181
|
-
const timeout = new Promise((_, reject) => {
|
|
182
|
-
timer = setTimeout(() => reject(new Error(`${this.errorPrefix} stream start timeout`)), this.startTimeoutMs);
|
|
183
|
-
});
|
|
184
|
-
try {
|
|
185
|
-
await Promise.race([this.controllerReady, timeout]);
|
|
186
|
-
}
|
|
187
|
-
finally {
|
|
188
|
-
if (timer)
|
|
189
|
-
clearTimeout(timer);
|
|
190
|
-
}
|
|
170
|
+
await this.waitForControllerReady();
|
|
191
171
|
}
|
|
192
172
|
async settle() {
|
|
193
173
|
this.release();
|
|
@@ -206,7 +186,7 @@ export class CardSession {
|
|
|
206
186
|
if (!this.messageId)
|
|
207
187
|
return 'unsent';
|
|
208
188
|
try {
|
|
209
|
-
await this.connector.updateCard(this.messageId, this.
|
|
189
|
+
await this.connector.updateCard(this.messageId, this.renderCurrentCard());
|
|
210
190
|
return 'updated';
|
|
211
191
|
}
|
|
212
192
|
catch (error) {
|
|
@@ -215,11 +195,13 @@ export class CardSession {
|
|
|
215
195
|
}
|
|
216
196
|
}
|
|
217
197
|
/**
|
|
218
|
-
*
|
|
198
|
+
* 渲染当前状态对应的卡片(单一渲染入口)。
|
|
199
|
+
*
|
|
200
|
+
* 所有生产子类的 renderer 都自带 stringify 级预算保护
|
|
219
201
|
* (run/bash 的 degraded/extreme 路径);发送前 Bridge 还会再走一次
|
|
220
202
|
* enforceCardBudget,因此这里不再重复裁剪。
|
|
221
203
|
*/
|
|
222
|
-
|
|
204
|
+
renderCurrentCard() {
|
|
223
205
|
return this.renderCard(this.state, this.renderOptions);
|
|
224
206
|
}
|
|
225
207
|
/**
|
|
@@ -227,7 +209,7 @@ export class CardSession {
|
|
|
227
209
|
* connector.updateCard if the controller is unavailable or fails.
|
|
228
210
|
*/
|
|
229
211
|
async updateCard() {
|
|
230
|
-
const safeCard = this.
|
|
212
|
+
const safeCard = this.renderCurrentCard();
|
|
231
213
|
if (this.controller) {
|
|
232
214
|
try {
|
|
233
215
|
await this.controller.update(safeCard);
|
|
@@ -247,6 +229,24 @@ export class CardSession {
|
|
|
247
229
|
}
|
|
248
230
|
}
|
|
249
231
|
}
|
|
232
|
+
async waitForControllerReady() {
|
|
233
|
+
let timer;
|
|
234
|
+
const timeout = new Promise((_, reject) => {
|
|
235
|
+
timer = setTimeout(() => reject(new Error(`${this.errorPrefix} stream start timeout`)), this.startTimeoutMs);
|
|
236
|
+
});
|
|
237
|
+
try {
|
|
238
|
+
await Promise.race([this.controllerReady, timeout]);
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
if (timer)
|
|
242
|
+
clearTimeout(timer);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async prepareTerminalUpdate() {
|
|
246
|
+
this.cancelPendingFlush();
|
|
247
|
+
if (this.flushP)
|
|
248
|
+
await this.flushP;
|
|
249
|
+
}
|
|
250
250
|
release() {
|
|
251
251
|
if (this.released)
|
|
252
252
|
return;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AgentKind } from '../agent-kinds.js';
|
|
2
2
|
/**
|
|
3
3
|
* Display name for an agent kind in the run card header.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Display names are static UI labels, so the constant is the single source.
|
|
6
|
+
* Keeping a second mutable registry here made render helpers depend on startup
|
|
7
|
+
* order and forced tests to reset global state.
|
|
8
8
|
*/
|
|
9
9
|
export declare function agentDisplayName(kind: string): string;
|
|
10
10
|
export declare function terminalToLabel(terminal: string): string;
|
package/dist/card/card-shared.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AGENT_DISPLAY_NAMES } from '../runner/types.js';
|
|
1
|
+
import { AGENT_DISPLAY_NAMES } from '../agent-kinds.js';
|
|
3
2
|
/**
|
|
4
3
|
* Display name for an agent kind in the run card header.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Display names are static UI labels, so the constant is the single source.
|
|
6
|
+
* Keeping a second mutable registry here made render helpers depend on startup
|
|
7
|
+
* order and forced tests to reset global state.
|
|
9
8
|
*/
|
|
10
9
|
export function agentDisplayName(kind) {
|
|
11
|
-
const globalReg = AgentRegistry.getGlobalInstance();
|
|
12
|
-
if (globalReg) {
|
|
13
|
-
return globalReg.getDisplayName(kind);
|
|
14
|
-
}
|
|
15
|
-
// Fallback for render helpers that run without the global registry.
|
|
16
10
|
return AGENT_DISPLAY_NAMES[kind] ?? kind;
|
|
17
11
|
}
|
|
18
12
|
const TERMINAL_LABELS = {
|
package/dist/card/collapsible.js
CHANGED
|
@@ -76,8 +76,9 @@ export function markdownDiv(content, textSize) {
|
|
|
76
76
|
* to be interpreted as a literal. Escape it to preserve the raw backslash.
|
|
77
77
|
*
|
|
78
78
|
* Note: Square brackets [] and pipe | are NOT escaped -- they are valid markdown
|
|
79
|
-
* (links, tables) and escaping them breaks link rendering. Verified
|
|
80
|
-
* tests/escape-bracket-
|
|
79
|
+
* (links, tables) and escaping them breaks link rendering. Verified by the live
|
|
80
|
+
* regression test tests/feishu-live/escape-bracket-regression.test.ts: unescaped
|
|
81
|
+
* []| does not trigger 11311.
|
|
81
82
|
*/
|
|
82
83
|
function escapeMarkdown(content) {
|
|
83
84
|
if (!content)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function splitFinalAnswer(text: string, maxBytes?: number): string[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split an authoritative final answer into plain-text messages without losing
|
|
3
|
+
* any content. Feishu cards have a hard size budget; a long answer must spill
|
|
4
|
+
* rather than being silently truncated inside the progress card.
|
|
5
|
+
*/
|
|
6
|
+
const FINAL_ANSWER_CHUNK_BYTES = 20_000;
|
|
7
|
+
export function splitFinalAnswer(text, maxBytes = FINAL_ANSWER_CHUNK_BYTES) {
|
|
8
|
+
if (!text || maxBytes <= 0)
|
|
9
|
+
return text ? [text] : [];
|
|
10
|
+
const chunks = [];
|
|
11
|
+
let rest = text;
|
|
12
|
+
let remainingBytes = Buffer.byteLength(rest, 'utf8');
|
|
13
|
+
while (remainingBytes > maxBytes) {
|
|
14
|
+
let bytes = 0;
|
|
15
|
+
let end = 0;
|
|
16
|
+
let lastNewline = 0;
|
|
17
|
+
for (const char of rest) {
|
|
18
|
+
const charBytes = Buffer.byteLength(char, 'utf8');
|
|
19
|
+
if (bytes + charBytes > maxBytes)
|
|
20
|
+
break;
|
|
21
|
+
bytes += charBytes;
|
|
22
|
+
end += char.length;
|
|
23
|
+
if (char === '\n')
|
|
24
|
+
lastNewline = end;
|
|
25
|
+
}
|
|
26
|
+
if (end === 0) {
|
|
27
|
+
// A caller can choose a byte limit smaller than one codepoint. Keep one
|
|
28
|
+
// codepoint per chunk rather than looping forever with a zero-length cut.
|
|
29
|
+
const first = Array.from(rest)[0];
|
|
30
|
+
chunks.push(first);
|
|
31
|
+
rest = rest.slice(first.length);
|
|
32
|
+
remainingBytes -= Buffer.byteLength(first, 'utf8');
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
// Prefer a paragraph/line boundary when it does not waste most of a chunk.
|
|
36
|
+
const cut = lastNewline > 0 && lastNewline >= Math.floor(end * 0.6) ? lastNewline : end;
|
|
37
|
+
const chunk = rest.slice(0, cut);
|
|
38
|
+
chunks.push(chunk);
|
|
39
|
+
rest = rest.slice(cut);
|
|
40
|
+
remainingBytes -= Buffer.byteLength(chunk, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
if (rest)
|
|
43
|
+
chunks.push(rest);
|
|
44
|
+
return chunks;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=final-answer-overflow.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CardSession, type CardChannel } from './card-session.js';
|
|
2
2
|
import type { AgentEvent } from '../runner/index.js';
|
|
3
|
-
import { type FinishMeta, type
|
|
3
|
+
import { type FinishMeta, type RunFinalTerminal, type RunState } from './run-state.js';
|
|
4
4
|
import { type RunCardRenderOptions } from './run-renderer.js';
|
|
5
5
|
export type { CardChannel as RunCardChannel };
|
|
6
6
|
export declare class RunCardSession extends CardSession<RunState, RunCardRenderOptions> {
|
|
@@ -20,5 +20,5 @@ export declare class RunCardSession extends CardSession<RunState, RunCardRenderO
|
|
|
20
20
|
protected renderCard(state: RunState, options: RunCardRenderOptions): object;
|
|
21
21
|
protected isFlushableState(): boolean;
|
|
22
22
|
push(event: AgentEvent): Promise<void>;
|
|
23
|
-
finish(terminal:
|
|
23
|
+
finish(terminal: RunFinalTerminal, meta?: FinishMeta): Promise<void>;
|
|
24
24
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CardSession } from './card-session.js';
|
|
2
|
-
import { createInitialRunState, finishRun, reduceRunState, } from './run-state.js';
|
|
2
|
+
import { createInitialRunState, finishRun, isRunActive, reduceRunState, } from './run-state.js';
|
|
3
3
|
import { renderRunCard } from './run-renderer.js';
|
|
4
4
|
export class RunCardSession extends CardSession {
|
|
5
5
|
constructor(opts) {
|
|
@@ -25,7 +25,7 @@ export class RunCardSession extends CardSession {
|
|
|
25
25
|
isFlushableState() {
|
|
26
26
|
// finish() 已转终态(done/error/interrupted/idle_timeout)时不再 patch;
|
|
27
27
|
// 'finalizing' 是非终态,in-flight/延迟 flush 仍可渲染累积事件。
|
|
28
|
-
return this.state.terminal
|
|
28
|
+
return isRunActive(this.state.terminal);
|
|
29
29
|
}
|
|
30
30
|
async push(event) {
|
|
31
31
|
// state 每事件立即 reduce(始终最新),但 updateCard 延迟到合批窗口末尾。
|
|
@@ -46,13 +46,8 @@ export class RunCardSession extends CardSession {
|
|
|
46
46
|
}
|
|
47
47
|
async finish(terminal, meta = {}) {
|
|
48
48
|
// 终态必须即时显示(spec):取消合批窗口,不等延迟 flush 调度。
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// 否则两条并发 controller.update 无 FIFO 保证,in-flight 的 pre-terminal
|
|
52
|
-
// patch 可能在终态 patch 之后 resolve → 用户看到"思考中"终帧。
|
|
53
|
-
// await 保证顺序:pre-terminal 先落地 → terminal 后落地 → terminal 胜出。
|
|
54
|
-
if (this.flushP)
|
|
55
|
-
await this.flushP;
|
|
49
|
+
// 终态 patch 必须排在已开始的 pre-terminal flush 之后。
|
|
50
|
+
await this.prepareTerminalUpdate();
|
|
56
51
|
this.state = finishRun(this.state, terminal, meta);
|
|
57
52
|
await this.updateCard();
|
|
58
53
|
this.release();
|
|
@@ -20,6 +20,12 @@ export interface RunCardRenderOptions {
|
|
|
20
20
|
*/
|
|
21
21
|
sessionFallbackAvailable?: boolean;
|
|
22
22
|
}
|
|
23
|
+
export interface RunCardRenderOutcome {
|
|
24
|
+
card: object;
|
|
25
|
+
finalAnswerStatus: 'none' | 'full' | 'overflow';
|
|
26
|
+
/** Full text to deliver outside the card when finalAnswerStatus is overflow. */
|
|
27
|
+
finalAnswerText?: string;
|
|
28
|
+
}
|
|
23
29
|
type BlockGroup = {
|
|
24
30
|
kind: 'thinking';
|
|
25
31
|
content: string;
|
|
@@ -73,4 +79,12 @@ export declare function estimateCardBytes(state: RunState, options?: RunCardRend
|
|
|
73
79
|
* CardKit 2.0 renderer - simplified for streaming (no tabs)
|
|
74
80
|
*/
|
|
75
81
|
export declare function renderRunCard(state: RunState, options?: RunCardRenderOptions): object;
|
|
82
|
+
/**
|
|
83
|
+
* Render a run card with metadata the bridge needs to deliver an answer that
|
|
84
|
+
* cannot fit in one Feishu card. `renderRunCard` remains the card-only API used
|
|
85
|
+
* by streaming sessions.
|
|
86
|
+
*/
|
|
87
|
+
export declare function renderRunCardDetailed(state: RunState, options?: RunCardRenderOptions): RunCardRenderOutcome;
|
|
88
|
+
/** Exact-match marker used in the card before the answer is sent separately. */
|
|
89
|
+
export declare const FINAL_ANSWER_OVERFLOW_MARKER = "_\u6700\u7EC8\u56DE\u7B54\u8F83\u957F\uFF0C\u5B8C\u6574\u5185\u5BB9\u5DF2\u968F\u540E\u53D1\u9001_";
|
|
76
90
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRunTerminal, } from './run-state.js';
|
|
1
|
+
import { isRunActive, isRunTerminal, } from './run-state.js';
|
|
2
2
|
import { terminalToColor, terminalToLabel, newSessionButton, stopButton, compactButton, cardButton, agentDisplayName, } from './card-shared.js';
|
|
3
3
|
import { collapsibleMarkdownPanel, markdownDiv } from './collapsible.js';
|
|
4
4
|
import { toolBodyMd, toolHeaderText } from './tool-render.js';
|
|
@@ -8,7 +8,7 @@ import { countCardElements, MAX_CARD_ELEMENTS } from './card-budget.js';
|
|
|
8
8
|
import { displayWidth, truncateToDisplayWidth } from '../common/text-width.js';
|
|
9
9
|
import { isContentFilterFailure } from '../common/turn-failure.js';
|
|
10
10
|
import { formatClock, formatTimestamp } from './time.js';
|
|
11
|
-
import { formatUsageStats } from '
|
|
11
|
+
import { formatUsageStats } from './usage-format.js';
|
|
12
12
|
import { renderApprovalArea, renderApprovalAreaCompact } from './approval-render.js';
|
|
13
13
|
const REASONING_BYTES = 4_500;
|
|
14
14
|
const TEXT_BYTES = 10_000;
|
|
@@ -406,7 +406,7 @@ const EXTREME_TIER = {
|
|
|
406
406
|
function actionButtonList(state, options) {
|
|
407
407
|
const actionButtons = [];
|
|
408
408
|
// finalizing 也显示停止按钮(进程未退出,用户可 /stop)
|
|
409
|
-
const showStop = state.terminal
|
|
409
|
+
const showStop = isRunActive(state.terminal);
|
|
410
410
|
if (showStop) {
|
|
411
411
|
actionButtons.push(stopButton(state.runId));
|
|
412
412
|
}
|
|
@@ -867,6 +867,20 @@ function measurePanelBytes(title, expanded, border, content) {
|
|
|
867
867
|
* CardKit 2.0 renderer - simplified for streaming (no tabs)
|
|
868
868
|
*/
|
|
869
869
|
export function renderRunCard(state, options = {}) {
|
|
870
|
+
return renderRunCardDetailed(state, options).card;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Render a run card with metadata the bridge needs to deliver an answer that
|
|
874
|
+
* cannot fit in one Feishu card. `renderRunCard` remains the card-only API used
|
|
875
|
+
* by streaming sessions.
|
|
876
|
+
*/
|
|
877
|
+
export function renderRunCardDetailed(state, options = {}) {
|
|
878
|
+
if (state.finalAnswer?.content.trim()) {
|
|
879
|
+
return buildFinalAnswerPriorityCard(state, options);
|
|
880
|
+
}
|
|
881
|
+
return { card: renderLegacyRunCard(state, options), finalAnswerStatus: 'none' };
|
|
882
|
+
}
|
|
883
|
+
function renderLegacyRunCard(state, options) {
|
|
870
884
|
// 先估后建:用廉价估算预测是否超 28KB 预算,明显超预算直接跳过完整卡构建走
|
|
871
885
|
// degraded。groups/prepared 只算一次,测量与正式渲染共用同一份截断结果,
|
|
872
886
|
// 避免影子测量 + 正式渲染对每个超限块双倍截断。
|
|
@@ -927,6 +941,110 @@ export function renderRunCard(state, options = {}) {
|
|
|
927
941
|
};
|
|
928
942
|
return truncatedSkeleton;
|
|
929
943
|
}
|
|
944
|
+
/** Exact-match marker used in the card before the answer is sent separately. */
|
|
945
|
+
export const FINAL_ANSWER_OVERFLOW_MARKER = '_最终回答较长,完整内容已随后发送_';
|
|
946
|
+
/**
|
|
947
|
+
* Build a card around the authoritative final answer.
|
|
948
|
+
*
|
|
949
|
+
* The answer is measured first with the fixed controls. Progress groups are
|
|
950
|
+
* added newest-first only while the whole card still fits. If the answer
|
|
951
|
+
* cannot fit even without progress/summary, the card carries an explicit
|
|
952
|
+
* marker and the bridge sends the complete answer separately.
|
|
953
|
+
*/
|
|
954
|
+
function buildFinalAnswerPriorityCard(state, options) {
|
|
955
|
+
const finalAnswer = state.finalAnswer;
|
|
956
|
+
const groups = groupBlocks(state.blocks);
|
|
957
|
+
const prepared = prepareGroupContent(groups);
|
|
958
|
+
const safeAnswer = truncateMarkdownTables(finalAnswer.content);
|
|
959
|
+
const requiresOverflow = safeAnswer !== finalAnswer.content;
|
|
960
|
+
const ts = formatTimestamp(finalAnswer.timestamp);
|
|
961
|
+
const answerElements = renderTextBlock(safeAnswer, ts, state.terminal !== 'running');
|
|
962
|
+
const fixedStart = [statusRow(state), ...omittedBlocksHint(state)];
|
|
963
|
+
const summary = buildSummaryContent(state, { includeNotices: false });
|
|
964
|
+
const controls = [...actionRow(state, options), ...approvalArea(state)];
|
|
965
|
+
const buildWithProgress = (start, includeSummary) => {
|
|
966
|
+
const renderedProgress = buildAnswerProgressContent(state, prepared, groups.slice(start));
|
|
967
|
+
const progress = start < groups.length
|
|
968
|
+
? [markdownDiv(`_💡 已省略 ${start} 个早期步骤_`), ...renderedProgress]
|
|
969
|
+
: renderedProgress;
|
|
970
|
+
return assembleRunCard(state, options, [
|
|
971
|
+
...fixedStart,
|
|
972
|
+
...progress,
|
|
973
|
+
...(requiresOverflow ? [markdownDiv(FINAL_ANSWER_OVERFLOW_MARKER)] : []),
|
|
974
|
+
...answerElements,
|
|
975
|
+
...(includeSummary ? summary : []),
|
|
976
|
+
...controls,
|
|
977
|
+
]);
|
|
978
|
+
};
|
|
979
|
+
let best = buildWithProgress(groups.length, true);
|
|
980
|
+
if (withinRunCardBudget(best)) {
|
|
981
|
+
for (let start = groups.length - 1; start >= 0; start--) {
|
|
982
|
+
const candidate = buildWithProgress(start, true);
|
|
983
|
+
if (!withinRunCardBudget(candidate))
|
|
984
|
+
break;
|
|
985
|
+
best = candidate;
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
card: best,
|
|
989
|
+
finalAnswerStatus: requiresOverflow ? 'overflow' : 'full',
|
|
990
|
+
...(requiresOverflow ? { finalAnswerText: finalAnswer.content } : {}),
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
// The answer outranks the usage summary. Keep the answer and controls first,
|
|
994
|
+
// then retry without the summary before declaring a physical overflow.
|
|
995
|
+
const withoutSummary = buildWithProgress(groups.length, false);
|
|
996
|
+
if (withinRunCardBudget(withoutSummary)) {
|
|
997
|
+
return {
|
|
998
|
+
card: withoutSummary,
|
|
999
|
+
finalAnswerStatus: requiresOverflow ? 'overflow' : 'full',
|
|
1000
|
+
...(requiresOverflow ? { finalAnswerText: finalAnswer.content } : {}),
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
const overflowCard = assembleRunCard(state, options, [
|
|
1004
|
+
...fixedStart,
|
|
1005
|
+
markdownDiv(FINAL_ANSWER_OVERFLOW_MARKER),
|
|
1006
|
+
...controls,
|
|
1007
|
+
]);
|
|
1008
|
+
return {
|
|
1009
|
+
card: overflowCard,
|
|
1010
|
+
finalAnswerStatus: 'overflow',
|
|
1011
|
+
finalAnswerText: finalAnswer.content,
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
/** Render a contiguous progress suffix for the answer-priority card. */
|
|
1015
|
+
function buildAnswerProgressContent(state, prepared, visible) {
|
|
1016
|
+
const elements = [];
|
|
1017
|
+
const outputMax = toolOutputBudget(state);
|
|
1018
|
+
const contentItems = mergeNoticesIntoContent(visible, state.notices);
|
|
1019
|
+
for (const item of contentItems) {
|
|
1020
|
+
if (item.kind === 'notice') {
|
|
1021
|
+
elements.push(renderNoticeRow(item.notice));
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
const group = item;
|
|
1025
|
+
if (group.kind === 'tool') {
|
|
1026
|
+
elements.push(renderTool(group.tool, outputMax));
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
if (group.kind === 'text') {
|
|
1030
|
+
const content = prepared.get(group) ?? '';
|
|
1031
|
+
const ts = formatTimestamp(group.timestamp);
|
|
1032
|
+
elements.push(...renderTextBlock(content, ts, state.terminal !== 'running'));
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
const spec = describePanelGroup(group, prepared);
|
|
1036
|
+
if (!spec)
|
|
1037
|
+
continue;
|
|
1038
|
+
elements.push(collapsibleMarkdownPanel({
|
|
1039
|
+
title: spec.title,
|
|
1040
|
+
expanded: spec.expanded,
|
|
1041
|
+
border: spec.border,
|
|
1042
|
+
content: spec.content,
|
|
1043
|
+
textSize: 'notation',
|
|
1044
|
+
}));
|
|
1045
|
+
}
|
|
1046
|
+
return elements;
|
|
1047
|
+
}
|
|
930
1048
|
/**
|
|
931
1049
|
* Run 卡片的双预算检查:UTF-8 字节 ≤ 28KB **且** 元素数 ≤ `MAX_CARD_ELEMENTS`。
|
|
932
1050
|
*
|
|
@@ -1037,7 +1155,7 @@ function buildSummaryContent(state, options = {}) {
|
|
|
1037
1155
|
}
|
|
1038
1156
|
else {
|
|
1039
1157
|
const result = state.resultSubtype ?? 'success';
|
|
1040
|
-
const hasContent = state.blocks.length > 0;
|
|
1158
|
+
const hasContent = state.blocks.length > 0 || !!state.finalAnswer?.content.trim();
|
|
1041
1159
|
// 压缩(operationKind='compaction')不会返回 agent 正文——引擎只压缩上下文
|
|
1042
1160
|
// 窗口,空内容属正常,不能沿用普通 run 的「(未返回内容)」异常信号
|
|
1043
1161
|
// (2026-09 用户反馈)。所有会压缩的 agent(codex/claude/kimi/opencode/pi)
|
|
@@ -1047,25 +1165,10 @@ function buildSummaryContent(state, options = {}) {
|
|
|
1047
1165
|
: state.operationKind === 'compaction'
|
|
1048
1166
|
? '\n\n🗜 Compact 完成'
|
|
1049
1167
|
: '\n\n(未返回内容)';
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
compactPreContextLength: state.compactPreContextLength,
|
|
1055
|
-
cacheReadTokens: state.cacheReadTokens,
|
|
1056
|
-
cacheCreationTokens: state.cacheCreationTokens,
|
|
1057
|
-
totalTokens: state.totalTokens,
|
|
1058
|
-
inputTokens: state.inputTokens,
|
|
1059
|
-
outputTokens: state.outputTokens,
|
|
1060
|
-
cumulativeTotalTokens: state.cumulativeTotalTokens,
|
|
1061
|
-
cumulativeInputTokens: state.cumulativeInputTokens,
|
|
1062
|
-
cumulativeOutputTokens: state.cumulativeOutputTokens,
|
|
1063
|
-
cumulativeCacheReadTokens: state.cumulativeCacheReadTokens,
|
|
1064
|
-
cumulativeCacheCreationTokens: state.cumulativeCacheCreationTokens,
|
|
1065
|
-
model: state.model,
|
|
1066
|
-
costUsd: state.costUsd,
|
|
1067
|
-
reasoningTokens: state.reasoningTokens,
|
|
1068
|
-
}, {
|
|
1168
|
+
// RunState 的用量字段就是 formatUsageStats 的入参形态(同为
|
|
1169
|
+
// `UsageSnapshot`)——原先这里逐字段抄了 17 行,加一个字段要同时改
|
|
1170
|
+
// RunState、FinishMeta 和这张清单(§9.21)。
|
|
1171
|
+
const usageStatsStr = formatUsageStats(state, {
|
|
1069
1172
|
showResult: true,
|
|
1070
1173
|
result,
|
|
1071
1174
|
// 本轮结束但目标继续:不能报"已完成",也不能说"出错"。
|