@blade-hq/agent-client 2610.0.0-rc.0 → 2610.0.0
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/README.md +202 -12
- package/dist/auth.d.ts +0 -1
- package/dist/blade-client.d.ts +22 -1
- package/dist/index.d.ts +31 -9
- package/dist/index.js +1851 -263
- package/dist/index.js.map +1 -1
- package/dist/polling-backoff.d.ts +11 -0
- package/dist/resources/chat-groups.d.ts +31 -0
- package/dist/resources/computers.d.ts +54 -2
- package/dist/resources/plugins.d.ts +43 -0
- package/dist/resources/sessions.d.ts +190 -1
- package/dist/resources/site-visits.d.ts +32 -0
- package/dist/rest.d.ts +8 -0
- package/dist/schemas/mcp-app.d.ts +30 -0
- package/dist/schemas/message-utils.d.ts +10 -0
- package/dist/schemas/message.d.ts +3 -0
- package/dist/schemas/projection.d.ts +25 -1
- package/dist/schemas/session.d.ts +7 -0
- package/dist/session/agent-session.d.ts +152 -18
- package/dist/session/definition.d.ts +6 -1
- package/dist/session/errors.d.ts +11 -0
- package/dist/session/events.d.ts +14 -2
- package/dist/session/history-page.d.ts +9 -0
- package/dist/session/hub.d.ts +1 -0
- package/dist/session/queue.d.ts +104 -0
- package/dist/session/state.d.ts +15 -1
- package/dist/shared/auth-busy.d.ts +9 -0
- package/dist/shared/projection/builder.d.ts +10 -0
- package/dist/shared/projection/helpers.d.ts +2 -2
- package/dist/shared/projection/index.d.ts +1 -1
- package/dist/shared/projection/state.d.ts +7 -0
- package/dist/shared/tool-ui-card.d.ts +60 -0
- package/dist/shared/type-guards.d.ts +2 -0
- package/dist/socket.d.ts +2 -0
- package/dist/types/socket-events.d.ts +186 -2
- package/package.json +1 -1
- package/public-api.md +747 -11
|
@@ -4,14 +4,17 @@ import type { PatchEnvelope, TurnProjection } from "../schemas/projection";
|
|
|
4
4
|
import type { ReplaySpeed, ReplayState, SessionStatus } from "../schemas/session";
|
|
5
5
|
import { type CommandHandler } from "../commands/registry";
|
|
6
6
|
import { type AgentSessionEvents } from "./events";
|
|
7
|
-
import type { AppCliAttachment, AppCliDefinition, FileEntry } from "../resources/sessions";
|
|
7
|
+
import type { AppCliAttachment, AppCliDefinition, FileEntry, HistoricalCommand, SessionTurnIndex, SessionTurnIndexItem, SessionTurnsPage, SessionTurnsQuery } from "../resources/sessions";
|
|
8
|
+
import { type QueueOperationResult } from "./queue";
|
|
8
9
|
import { type ConnectionStatus, type SessionState } from "./state";
|
|
9
10
|
/** SessionHub 注入给 AgentSession 的运行时依赖(socket 收发 + REST 补数)。 */
|
|
10
11
|
export interface SessionRuntime {
|
|
11
12
|
emitEvent(event: string, data: unknown): void;
|
|
12
13
|
emitWithAck(event: string, data: unknown, timeoutMs: number): Promise<Record<string, unknown> | undefined>;
|
|
13
14
|
ensureConnected(): void;
|
|
14
|
-
|
|
15
|
+
fetchTurnsPage(sessionId: string, query?: SessionTurnsQuery, init?: RequestInit): Promise<SessionTurnsPage>;
|
|
16
|
+
fetchTurnIndex(sessionId: string): Promise<SessionTurnIndex>;
|
|
17
|
+
fetchCommands(sessionId: string): Promise<HistoricalCommand[]>;
|
|
15
18
|
/** 会话详情,目前只用来取回放状态。 */
|
|
16
19
|
fetchSessionInfo(sessionId: string): Promise<{
|
|
17
20
|
replay_state?: ReplayState | null;
|
|
@@ -52,8 +55,13 @@ export interface SendOptions {
|
|
|
52
55
|
deprecate_entry_ids: string[];
|
|
53
56
|
};
|
|
54
57
|
replayDecision?: "keep_replay" | "continue_replay";
|
|
55
|
-
/**
|
|
58
|
+
/** 用户显式选择的知识库 id;后端将元信息作为 once Context 提供给模型。 */
|
|
56
59
|
kbIds?: number[];
|
|
60
|
+
/**
|
|
61
|
+
* 声明"会话在跑就把这条消息放进等待队列"。默认不排队、运行中直接报错:
|
|
62
|
+
* 是否真的入队由服务端按运行状态判定(等待回答时发送即作答,不会被误排队)。
|
|
63
|
+
*/
|
|
64
|
+
queueWhenRunning?: boolean;
|
|
57
65
|
}
|
|
58
66
|
export declare function sessionStatusFromChatEnd(status: string | undefined): SessionStatus;
|
|
59
67
|
/**
|
|
@@ -77,11 +85,43 @@ export declare class AgentSession {
|
|
|
77
85
|
private joined;
|
|
78
86
|
private joinPending;
|
|
79
87
|
private subscriptionQueue;
|
|
88
|
+
private replaceQueue;
|
|
89
|
+
private timelineEpoch;
|
|
90
|
+
/** 定位请求的流水号:被后一次选择取代的那次定位不再提交窗口(见 locateEntry)。 */
|
|
91
|
+
private locateToken;
|
|
92
|
+
/** 在途那次定位的请求句柄。**只**服务定位:权威刷新不共用它(见 cancelPendingLocate)。 */
|
|
93
|
+
private locateAbort;
|
|
94
|
+
private pagingGeneration;
|
|
95
|
+
private liveRevision;
|
|
96
|
+
private modeRevision;
|
|
97
|
+
private readonly liveRevisions;
|
|
98
|
+
private readonly pendingOptimisticTurnIds;
|
|
99
|
+
private olderPending;
|
|
100
|
+
private replaceInFlight;
|
|
80
101
|
private replayQueue;
|
|
102
|
+
private commandRestoreQueue;
|
|
103
|
+
private commandRecoveryGeneration;
|
|
104
|
+
private commandRestoreRetryAttempt;
|
|
105
|
+
private commandRestoreRetryHandle;
|
|
106
|
+
private bufferedCommandDeliveries;
|
|
81
107
|
private pendingReplayMessage;
|
|
82
108
|
private pendingReplayMode;
|
|
83
109
|
private pendingReplayKbIds;
|
|
84
110
|
private recentChatEndAt;
|
|
111
|
+
/**
|
|
112
|
+
* 最近一次已经通过 `error` 事件投递过的服务端失败(`message` + `code`)。
|
|
113
|
+
*
|
|
114
|
+
* `chat:send` 被代理时服务端会先发 `system:error`、再用同一条失败回 ack,两条
|
|
115
|
+
* 通道都会到达这里:前者走 `_handleSystemError`,后者走发送失败的回滚。不去重
|
|
116
|
+
* 的话调用方会收到两次同一个 `error`(重复提示、重试逻辑跑两遍)。
|
|
117
|
+
*
|
|
118
|
+
* 带代际边界,只认「本次 `emitWithAck` 等待期间」投递的那一条:否则队列/停止
|
|
119
|
+
* 失败留下的标记会一直挂着,之后一次无关的订阅失败只是文案码相同就被当成
|
|
120
|
+
* 重复而静音。用单调递增的代际号而不是时间戳,同一毫秒内的两条也不会混淆。
|
|
121
|
+
*/
|
|
122
|
+
private lastDeliveredSystemError;
|
|
123
|
+
/** 每次投递 `system:error` 递增,用于判定某条失败是否属于本次等待。 */
|
|
124
|
+
private systemErrorGeneration;
|
|
85
125
|
private subscriptionReplaySource;
|
|
86
126
|
private disposed;
|
|
87
127
|
private getAppContext;
|
|
@@ -93,11 +133,55 @@ export declare class AgentSession {
|
|
|
93
133
|
private readonly seenToolCallIds;
|
|
94
134
|
private readonly finishedToolCallIds;
|
|
95
135
|
private readonly emittedMessageTurnIds;
|
|
96
|
-
constructor(sessionId: string, runtime: SessionRuntime, connection: ConnectionStatus
|
|
136
|
+
constructor(sessionId: string, runtime: SessionRuntime, connection: ConnectionStatus, options?: {
|
|
137
|
+
staticHistory?: boolean;
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* 静态历史(分享页 / `fromTurns`):没有目录接口,也**没有**"目录还没到位"这个中间态。
|
|
141
|
+
*
|
|
142
|
+
* 宿主据此决定要不要拿已加载窗口当兜底条目——真实会话在 timeline 被重写后,窗口里还是
|
|
143
|
+
* 旧分支的发言,拿它兜底会把已经失效的 entry id 重新摆到轨道上,点了必然找不到。
|
|
144
|
+
*/
|
|
145
|
+
readonly isStatic: boolean;
|
|
97
146
|
/** 从一份静态 turn 快照构造只展示历史的会话(例如公开分享页)。 */
|
|
98
147
|
static fromTurns(sessionId: string, turns: TurnProjection[]): AgentSession;
|
|
99
148
|
/** 返回当前会话快照。引用稳定:状态未变化时返回同一对象。 */
|
|
100
149
|
getState(): SessionState;
|
|
150
|
+
get hasOlderHistory(): boolean;
|
|
151
|
+
/** Load the page immediately preceding the current display window. */
|
|
152
|
+
loadOlderHistory(options?: {
|
|
153
|
+
beforeCommit?: () => void;
|
|
154
|
+
}): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* 定位到一条窗口外的历史发言:请服务端返回"从这条发言到最新"的窗口并整窗替换。
|
|
157
|
+
*
|
|
158
|
+
* 返回 false 表示这次定位没有落地(请求失败,或期间 timeline 被重写、会话已释放),
|
|
159
|
+
* 当前窗口保持不变,调用方可以原样重试。已加载的发言不要走这里——宿主直接滚动,
|
|
160
|
+
* 一次网络请求都不该发。
|
|
161
|
+
*/
|
|
162
|
+
locateEntry(entryId: string): Promise<boolean>;
|
|
163
|
+
/**
|
|
164
|
+
* 取消在途定位:用户改选了另一条发言(哪怕那条已经加载、不需要请求),
|
|
165
|
+
* 先前那次定位既不该再落地,也不该继续占着替换队列——排队中的下一次定位
|
|
166
|
+
* 不然要等它跑完(用户看到的是"点了新目标一直转圈")。
|
|
167
|
+
*
|
|
168
|
+
* **只**中止定位自己的请求:权威刷新(运行结束 / 重连补数)不共用这个 controller,
|
|
169
|
+
* 否则"运行刚结束时点一下侧栏"就会把终态对账一起中止,流式与历史对不上。
|
|
170
|
+
*/
|
|
171
|
+
cancelPendingLocate(): void;
|
|
172
|
+
/**
|
|
173
|
+
* timeline 被整体改写(rewind / checkout / 释放):推进纪元,并把在途定位请求一并中止
|
|
174
|
+
* ——它已经属于旧 timeline,却还占着替换队列,会让改写后的权威窗口排在后面等它跑完。
|
|
175
|
+
* 作废号仍然保留:中止只是让它更早结束。
|
|
176
|
+
*/
|
|
177
|
+
private invalidateTimeline;
|
|
178
|
+
/**
|
|
179
|
+
* 拉取当前分支的用户发言目录(消息导航侧栏用)。
|
|
180
|
+
*
|
|
181
|
+
* 不在这里缓存:什么时候该刷新只有调用方知道(会话切换、历史被替换、新发言),
|
|
182
|
+
* 缓存只会让旧条目在 rewind 之后继续可点。
|
|
183
|
+
*/
|
|
184
|
+
loadTurnIndex(): Promise<SessionTurnIndexItem[]>;
|
|
101
185
|
/** 订阅状态变化。返回取消订阅函数。 */
|
|
102
186
|
subscribe(listener: () => void): () => void;
|
|
103
187
|
private setState;
|
|
@@ -120,10 +204,44 @@ export declare class AgentSession {
|
|
|
120
204
|
attachApp(options: AttachAppOptions): Promise<AppCliAttachment | null>;
|
|
121
205
|
/** 发送一条消息。字符串或多模态内容均可。 */
|
|
122
206
|
send(content: MessageContent, options?: SendOptions): Promise<boolean>;
|
|
207
|
+
/**
|
|
208
|
+
* 入队一条等待消息(运行中排队 / 暂停期间照常入队)。
|
|
209
|
+
*
|
|
210
|
+
* ack 超时或传输失败时结果未知:用**同一个** `client_request_id` 重试一次,
|
|
211
|
+
* 服务端按 `(session_id, client_request_id)` 幂等,不会制造第二条。
|
|
212
|
+
*/
|
|
213
|
+
queueMessage(content: string): Promise<QueueOperationResult>;
|
|
214
|
+
/** 修改一条等待消息的正文;`expectedVersion` 是客户端看到的条目版本。 */
|
|
215
|
+
updateQueuedMessage(itemId: string, expectedVersion: number, content: string): Promise<QueueOperationResult>;
|
|
216
|
+
/**
|
|
217
|
+
* 重排等待消息。`orderedIds` 必须是当前 pending 条目 id 的**完整集合**(顺序任意),
|
|
218
|
+
* 服务端按 `expectedRevision` 校验后整体替换顺序。
|
|
219
|
+
*
|
|
220
|
+
* 拖动排序就该像本地操作一样落地:版本冲突(运行中的任务刚领走一条 / 另一个端也在
|
|
221
|
+
* 动队列)不甩给用户「刷新后重试」,而是拿冲突响应里那份**最新快照**重算一次再提交
|
|
222
|
+
* ——掉队的条目(已经被领取执行的)从顺序里剔除,剩下的保持用户拖出来的相对次序。
|
|
223
|
+
* 最多补两次;仍旧冲突才把结果交回调用方。
|
|
224
|
+
*/
|
|
225
|
+
reorderQueuedMessages(expectedRevision: number, orderedIds: string[]): Promise<QueueOperationResult>;
|
|
226
|
+
/** 删除一条等待消息;不传版本时由服务端自行判定。 */
|
|
227
|
+
cancelQueuedMessage(itemId: string, expectedVersion?: number): Promise<QueueOperationResult>;
|
|
228
|
+
/** 立即补充:把等待消息交给当前运行,在下一个接收点消费,不停止也不另起一轮。 */
|
|
229
|
+
deliverQueuedMessage(itemId: string, expectedVersion: number): Promise<QueueOperationResult>;
|
|
230
|
+
/** 继续执行:清除暂停,并按当前顺序恢复逐条接续。 */
|
|
231
|
+
resumeQueue(): Promise<QueueOperationResult>;
|
|
232
|
+
/** 拉取一份权威队列快照。首次连接与每次重连后由实例自动调用,也可以手动刷新。 */
|
|
233
|
+
refreshQueue(): Promise<QueueOperationResult>;
|
|
234
|
+
/**
|
|
235
|
+
* 发出一条队列命令并安装 ack 里的快照。
|
|
236
|
+
* 传输层失败(超时 / 断线)会 reject,由调用方决定是否重试。
|
|
237
|
+
*/
|
|
238
|
+
private requestQueueAck;
|
|
239
|
+
/** 队列命令的对外入口:传输失败只体现为失败结果,不向调用方抛异常。 */
|
|
240
|
+
private runQueueCommand;
|
|
123
241
|
private sendUnlocked;
|
|
124
242
|
private rollbackOptimisticSend;
|
|
125
|
-
/**
|
|
126
|
-
|
|
243
|
+
/** 撤回一次乐观提交:移除乐观消息、恢复提问作答、清掉回放草稿。 */
|
|
244
|
+
private discardOptimisticSend;
|
|
127
245
|
/** 停止当前回复。 */
|
|
128
246
|
stop(): Promise<Record<string, unknown>>;
|
|
129
247
|
/** 手动触发上下文压缩。 */
|
|
@@ -150,6 +268,11 @@ export declare class AgentSession {
|
|
|
150
268
|
get isDisposed(): boolean;
|
|
151
269
|
/** @internal 连接状态变化(同 client 下所有会话共享一条连接)。 */
|
|
152
270
|
_setConnection(connection: ConnectionStatus): void;
|
|
271
|
+
/**
|
|
272
|
+
* @internal 服务端广播的权威队列快照。
|
|
273
|
+
* 结构不合法或 revision 不新的一律丢弃,绝不用旧快照覆盖新状态。
|
|
274
|
+
*/
|
|
275
|
+
_handleQueueUpdated(payload: unknown): void;
|
|
153
276
|
/** @internal socket 重连后由 Hub 调用:重新加入房间并补数。 */
|
|
154
277
|
_rejoinAfterReconnect(): Promise<void>;
|
|
155
278
|
/** @internal 首次连接:加载历史 + 加入房间。 */
|
|
@@ -179,13 +302,15 @@ export declare class AgentSession {
|
|
|
179
302
|
/** @internal */
|
|
180
303
|
_handleChatEnd(status: string): void;
|
|
181
304
|
/** @internal */
|
|
182
|
-
_handleSystemError(message: string): void;
|
|
305
|
+
_handleSystemError(message: string, code?: string): void;
|
|
183
306
|
/** @internal */
|
|
184
307
|
_handleTaskUpdated(tasks: unknown[]): void;
|
|
185
308
|
/** @internal */
|
|
186
309
|
_handleWorkspaceFilesChanged(filePath?: string): void;
|
|
187
310
|
/** @internal */
|
|
188
311
|
_handleSessionUpdated(data: {
|
|
312
|
+
workspace_path?: string;
|
|
313
|
+
chat_group_id?: string | null;
|
|
189
314
|
intent?: string;
|
|
190
315
|
status?: SessionStatus;
|
|
191
316
|
model?: string | null;
|
|
@@ -206,10 +331,13 @@ export declare class AgentSession {
|
|
|
206
331
|
/** @internal */
|
|
207
332
|
_handleArtifact(event: AgentSessionEvents["artifact"]): void;
|
|
208
333
|
/** @internal 加入房间的应答里带 is_running,据此校准流式状态。 */
|
|
209
|
-
_applyRunningState(isRunning: boolean): void;
|
|
334
|
+
_applyRunningState(isRunning: boolean, liveRevisionAtJoin?: number): void;
|
|
210
335
|
private markRunningIfIdle;
|
|
211
336
|
private finishStreaming;
|
|
212
337
|
private completeChat;
|
|
338
|
+
private markTurnFresh;
|
|
339
|
+
private markOptimisticTurnPending;
|
|
340
|
+
private pruneOptimisticLiveRevisions;
|
|
213
341
|
private applyTurn;
|
|
214
342
|
private schedulePatchFlush;
|
|
215
343
|
private cancelPatchFlush;
|
|
@@ -217,20 +345,26 @@ export declare class AgentSession {
|
|
|
217
345
|
/** turn 数据落地后统一派生语义事件(指令 / 工具调用 / 消息 / 模式 / 预览 / OOM)。 */
|
|
218
346
|
private emitTurnDerivedEvents;
|
|
219
347
|
private dispatchCommand;
|
|
348
|
+
private dispatchRawCommands;
|
|
349
|
+
private deliverCommand;
|
|
350
|
+
private deliverCommandNow;
|
|
220
351
|
private notifySandboxOom;
|
|
221
|
-
private syncTurnsFromHistory;
|
|
222
|
-
/** 首次订阅前的 REST 历史建立语义事件基线,随后同一工具的订阅回放不再投递。 */
|
|
223
|
-
private seedToolCallDeliveryBaseline;
|
|
224
352
|
/**
|
|
225
|
-
*
|
|
353
|
+
* 权威刷新(运行结束 / 重连补数):**按当前窗口第一条发言取窗口**,不再另记"用户想看哪段"。
|
|
226
354
|
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* dispatch 内部按 deliveredCommandKeys 去重,重复调用安全。
|
|
355
|
+
* 窗口本身就是"用户在看哪段":首屏就是最新一页,向上翻页 / 定位之后首条就是用户滚到的位置。
|
|
356
|
+
* 直接拿 `from_entry_id = 当前窗口第一条 turn_id` 去取,运行结束、重连补数、翻页之后的对账
|
|
357
|
+
* 是同一条规则,不需要意图、锚点、已落地锚点这些状态;窗口首条已经不在分支上(404 / 409)时
|
|
358
|
+
* 回落到最新一页。
|
|
232
359
|
*/
|
|
233
|
-
private
|
|
360
|
+
private refreshAuthoritativeHistory;
|
|
361
|
+
private replaceFromHistory;
|
|
362
|
+
private invalidateOlderPaging;
|
|
363
|
+
private beginCommandRecovery;
|
|
364
|
+
private scheduleHistoricalCommandRestore;
|
|
365
|
+
private flushBufferedCommands;
|
|
366
|
+
/** 首次订阅前的 REST 历史建立语义事件基线,随后同一工具的订阅回放不再投递。 */
|
|
367
|
+
private seedToolCallDeliveryBaseline;
|
|
234
368
|
private ensureJoined;
|
|
235
369
|
private ensureIdleBeforeSend;
|
|
236
370
|
}
|
|
@@ -11,11 +11,16 @@ export type SolutionDefinition = {
|
|
|
11
11
|
files: TextFile[];
|
|
12
12
|
role?: string;
|
|
13
13
|
};
|
|
14
|
+
export type SkillFile = TextFile & {
|
|
15
|
+
/** 二进制内容使用 base64;省略时为 UTF-8 文本。 */
|
|
16
|
+
encoding?: "utf-8" | "base64";
|
|
17
|
+
executable?: boolean;
|
|
18
|
+
};
|
|
14
19
|
export interface SkillDefinition {
|
|
15
20
|
/** 会话内安装名,格式为 `组织/名称`。 */
|
|
16
21
|
name: string;
|
|
17
22
|
/** 必须包含根目录的 SKILL.md。 */
|
|
18
|
-
files:
|
|
23
|
+
files: SkillFile[];
|
|
19
24
|
}
|
|
20
25
|
export interface SessionConfig {
|
|
21
26
|
/** Server 模型目录中的 model ID。 */
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 一次会话操作的失败。
|
|
3
|
+
*
|
|
4
|
+
* `code` 是服务端给的机器可读原因(如 `owner_timeout` / `route_broken`),
|
|
5
|
+
* 调用方据此决定要不要重试;`message` 是可以直接展示的业务文案,不要当协议解析。
|
|
6
|
+
*/
|
|
7
|
+
export declare class AgentSessionError extends Error {
|
|
8
|
+
readonly name = "AgentSessionError";
|
|
9
|
+
readonly code?: string;
|
|
10
|
+
constructor(message: string, code?: string);
|
|
11
|
+
}
|
package/dist/session/events.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChatMessage, ToolCallInfo } from "../schemas/message";
|
|
2
|
+
import type { McpAppData } from "../schemas/mcp-app";
|
|
2
3
|
import type { TurnProjection } from "../schemas/projection";
|
|
3
4
|
import type { SessionStatus } from "../schemas/session";
|
|
4
5
|
/**
|
|
@@ -30,9 +31,15 @@ export interface AgentSessionEvents {
|
|
|
30
31
|
chatEnd: {
|
|
31
32
|
status: string;
|
|
32
33
|
};
|
|
33
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* 运行错误(system:error、发送失败等)。
|
|
36
|
+
*
|
|
37
|
+
* `code` 是服务端给的机器可读原因(如 `owner_timeout` / `route_broken`),
|
|
38
|
+
* 供调用方决定要不要重试;`message` 是可以直接展示的业务文案。
|
|
39
|
+
*/
|
|
34
40
|
error: {
|
|
35
41
|
message: string;
|
|
42
|
+
code?: string;
|
|
36
43
|
};
|
|
37
44
|
/** planning / executing 模式切换。 */
|
|
38
45
|
modeChange: {
|
|
@@ -76,10 +83,13 @@ export interface AgentSessionEvents {
|
|
|
76
83
|
};
|
|
77
84
|
/** 工具返回值带 _meta.ui 的可视化预览(resource HTML / URI)。 */
|
|
78
85
|
toolPreview: {
|
|
86
|
+
/** Source file identity when the preview comes from a local HTML file. */
|
|
87
|
+
sourcePath?: string;
|
|
79
88
|
toolCallId: string;
|
|
80
|
-
type: "resource-html" | "resource-uri";
|
|
89
|
+
type: "resource-html" | "resource-uri" | "resource-file";
|
|
81
90
|
content: string;
|
|
82
91
|
title: string;
|
|
92
|
+
mcpApp?: McpAppData;
|
|
83
93
|
};
|
|
84
94
|
/** 任务清单(TaskCreate 等)更新。 */
|
|
85
95
|
taskListUpdated: {
|
|
@@ -108,6 +118,8 @@ export interface AgentSessionEvents {
|
|
|
108
118
|
};
|
|
109
119
|
/** 会话属性(名称、状态、绑定技能等)被服务端更新。 */
|
|
110
120
|
sessionUpdated: {
|
|
121
|
+
workspacePath?: string;
|
|
122
|
+
chatGroupId?: string | null;
|
|
111
123
|
intent?: string;
|
|
112
124
|
status?: SessionStatus;
|
|
113
125
|
model?: string | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TurnProjection } from "../schemas/projection";
|
|
2
|
+
export interface LiveRevisionState {
|
|
3
|
+
current: number;
|
|
4
|
+
byTurnId: ReadonlyMap<string, number>;
|
|
5
|
+
}
|
|
6
|
+
/** Add an older authoritative slice without allowing it to roll back live content. */
|
|
7
|
+
export declare function prependOlder(current: TurnProjection[], page: TurnProjection[]): TurnProjection[];
|
|
8
|
+
/** Install a new authoritative window, retaining only live writes newer than the request. */
|
|
9
|
+
export declare function replaceWindow(current: TurnProjection[], page: TurnProjection[], liveRevisionAtStart: number, liveRevisions: LiveRevisionState): TurnProjection[];
|
package/dist/session/hub.d.ts
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话消息队列的共享领域模型:类型、快照合并规则与中文文案。
|
|
3
|
+
*
|
|
4
|
+
* 这里是**单一定义处**:AgentSession 的状态机、agent-react 与内置 Web 的队列 UI
|
|
5
|
+
* 都只用这一份判定,避免"能不能编辑 / 旧快照要不要覆盖"各写一遍后各自演进。
|
|
6
|
+
* 服务端语义见 issue #2529 与 local://frontend-queue-contract.md。
|
|
7
|
+
*/
|
|
8
|
+
/** 等待消息的服务端生命周期状态。 */
|
|
9
|
+
export type QueuedMessageStatus = "pending" | "delivering" | "claimed" | "consumed" | "cancelled";
|
|
10
|
+
/** 队列里的一条等待消息;权威快照只包含 pending(等待执行)与 delivering(等待接收)。 */
|
|
11
|
+
export interface QueuedMessage {
|
|
12
|
+
id: string;
|
|
13
|
+
content: string;
|
|
14
|
+
position: number;
|
|
15
|
+
version: number;
|
|
16
|
+
status: QueuedMessageStatus;
|
|
17
|
+
created_at: string;
|
|
18
|
+
updated_at: string;
|
|
19
|
+
/** 立即补充指向的当前运行 id;普通等待条目没有。 */
|
|
20
|
+
target_run_id?: string;
|
|
21
|
+
/** 领取或投递失败的原因。 */
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
/** 服务端权威队列快照。`revision` 单调递增,客户端据此丢弃过期快照。 */
|
|
25
|
+
export interface SessionQueueSnapshot {
|
|
26
|
+
session_id: string;
|
|
27
|
+
revision: number;
|
|
28
|
+
paused: boolean;
|
|
29
|
+
pause_reason: string | null;
|
|
30
|
+
items: QueuedMessage[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 还没有拿到服务端快照时的占位。
|
|
34
|
+
*
|
|
35
|
+
* `session_id` 为空表示"身份未知":`shouldApplySnapshot` 会跳过会话身份校验、
|
|
36
|
+
* 只比较 revision,这样第一份快照(哪怕 revision 为 0 的暂停态)也能装进来。
|
|
37
|
+
*/
|
|
38
|
+
export declare const EMPTY_SESSION_QUEUE: SessionQueueSnapshot;
|
|
39
|
+
/**
|
|
40
|
+
* 队列命令的 ack 线格式。
|
|
41
|
+
*
|
|
42
|
+
* `conflict` 表示乐观并发或状态校验失败,此时快照字段是服务端最新值,客户端必须
|
|
43
|
+
* 据此刷新本地并保留用户草稿;`error` 时快照字段可能缺失(如鉴权失败),此时只报错、
|
|
44
|
+
* 不改动本地队列。
|
|
45
|
+
*/
|
|
46
|
+
export interface QueueAck {
|
|
47
|
+
/** `queued` 只出现在 `chat:send` 的运行中改道:消息已进等待队列,不是失败。 */
|
|
48
|
+
status: "ok" | "conflict" | "error" | "queued";
|
|
49
|
+
/**
|
|
50
|
+
* 只有 `queued` 会带:服务端判定这次改道时该会话是否有运行。
|
|
51
|
+
* 客户端据此对齐流式标记,不用自己的猜测。
|
|
52
|
+
*/
|
|
53
|
+
running?: boolean;
|
|
54
|
+
message?: string | null;
|
|
55
|
+
session_id?: string;
|
|
56
|
+
revision?: number;
|
|
57
|
+
paused?: boolean;
|
|
58
|
+
pause_reason?: string | null;
|
|
59
|
+
items?: QueuedMessage[];
|
|
60
|
+
}
|
|
61
|
+
/** 解析后的队列操作结果;UI 只看这一个结构。 */
|
|
62
|
+
export interface QueueOperationResult {
|
|
63
|
+
ok: boolean;
|
|
64
|
+
conflict: boolean;
|
|
65
|
+
/** 服务端快照;缺失时为 null,调用方不得据此清空本地队列。 */
|
|
66
|
+
snapshot: SessionQueueSnapshot | null;
|
|
67
|
+
message: string | null;
|
|
68
|
+
/**
|
|
69
|
+
* 服务端给的机器可读失败原因(如 `owner_timeout` / `route_broken` / `owner_busy`),
|
|
70
|
+
* 供调用方决定要不要重试;`message` 是可以直接展示的业务文案。
|
|
71
|
+
*/
|
|
72
|
+
code?: string;
|
|
73
|
+
}
|
|
74
|
+
/** 判定一份载荷是不是完整的权威队列快照(广播事件用)。 */
|
|
75
|
+
export declare function isSessionQueueSnapshot(value: unknown): value is SessionQueueSnapshot;
|
|
76
|
+
/**
|
|
77
|
+
* 是否用 `next` 覆盖 `current`。
|
|
78
|
+
*
|
|
79
|
+
* 规则只有两条:会话身份必须一致(还没拿到过快照时不校验),且 revision 必须严格
|
|
80
|
+
* 更新——相等或更旧一律不采纳,乱序到达的旧快照不会把新状态顶回去。
|
|
81
|
+
*/
|
|
82
|
+
export declare function shouldApplySnapshot(current: SessionQueueSnapshot | undefined, next: SessionQueueSnapshot): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* 解析 `emitWithAck` 的返回值。
|
|
85
|
+
*
|
|
86
|
+
* - `ok` / `queued` / `conflict` 且能解析成快照 → `snapshot` 有值;
|
|
87
|
+
* - `queued` → `ok=true`:`chat:send` 在运行中改道进了等待队列,调用方要撤掉乐观消息;
|
|
88
|
+
* - `conflict` → `ok=false, conflict=true`(快照可能仍缺失,调用方按 null 处理);
|
|
89
|
+
* - `error` 或结构不合法 → `ok=false, conflict=false`,`message` 为服务端文案或兜底。
|
|
90
|
+
*/
|
|
91
|
+
export declare function parseQueueAck(response: unknown, fallbackSessionId: string): QueueOperationResult;
|
|
92
|
+
/** 等待中的条目才能改正文;已经交给运行或开始执行的条目不再可编辑。 */
|
|
93
|
+
export declare function canEditQueuedMessage(item: QueuedMessage): boolean;
|
|
94
|
+
/** 等待中的条目才能删除。 */
|
|
95
|
+
export declare function canCancelQueuedMessage(item: QueuedMessage): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* 能否立即补充。条目本身必须是等待中的;"会话是否正在运行"由调用方判断——
|
|
98
|
+
* 服务端会在没有可接收补充的任务时返回冲突。
|
|
99
|
+
*/
|
|
100
|
+
export declare function canDeliverQueuedMessage(item: QueuedMessage): boolean;
|
|
101
|
+
/** 面向前端展示的状态文案;已离开队列视图的状态没有文案。 */
|
|
102
|
+
export declare function queuedMessageStatusLabel(status: QueuedMessageStatus): string;
|
|
103
|
+
/** 暂停原因文案;未知原因原样透出,没有原因返回 null。 */
|
|
104
|
+
export declare function queuePauseReasonLabel(reason: string | null | undefined): string | null;
|
package/dist/session/state.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ChatMessage, CompactionInfo, MessageContent } from "../schemas/message";
|
|
2
2
|
import type { ContentBlock, PatchEnvelope, TurnProjection } from "../schemas/projection";
|
|
3
3
|
import type { ReplaySpeed, ReplayState, SessionStatus } from "../schemas/session";
|
|
4
|
+
import { type SessionQueueSnapshot } from "./queue";
|
|
4
5
|
/** 一次 AskUserQuestion 的用户作答(选项选择 + 自定义输入 + 可选补充)。 */
|
|
5
6
|
export interface AskUserAnswerData {
|
|
6
7
|
selections: Record<number, number[]>;
|
|
@@ -43,8 +44,13 @@ export interface SessionState {
|
|
|
43
44
|
activeCompaction: ActiveCompactionState | null;
|
|
44
45
|
/** 回放(演示 / 彩排)状态;不是回放会话时为 null。 */
|
|
45
46
|
replay: ReplaySnapshot | null;
|
|
47
|
+
/** 服务端持久化的等待消息队列快照;只在 revision 更新时替换。 */
|
|
48
|
+
queue: SessionQueueSnapshot;
|
|
46
49
|
/** 当前身份对这个会话的权限;"viewer" 表示只读(分享链接打开的)。 */
|
|
47
50
|
viewerRole: "owner" | "viewer" | null;
|
|
51
|
+
/** Stable anchor for the next older projected page. */
|
|
52
|
+
nextBefore: string | null;
|
|
53
|
+
loadingOlder: boolean;
|
|
48
54
|
}
|
|
49
55
|
/** 回放会话的快照视图,由服务端的 replay_state 归一化而来。 */
|
|
50
56
|
export interface ReplaySnapshot {
|
|
@@ -58,6 +64,9 @@ export declare function toReplaySnapshot(raw: ReplayState | null | undefined): R
|
|
|
58
64
|
export declare function createInitialSessionState(sessionId: string): SessionState;
|
|
59
65
|
export declare function extractModeFromBlocks(blocks: ContentBlock[]): "planning" | "executing" | null;
|
|
60
66
|
export declare function reconcileOptimisticUserTurns(currentTurns: TurnProjection[], incomingTurns: TurnProjection[]): TurnProjection[];
|
|
67
|
+
/** Reconcile a bulk authoritative window only by an echoed request identity.
|
|
68
|
+
* Positional fallback is reserved for a single live turn from an older server. */
|
|
69
|
+
export declare function reconcileHistoricalUserTurns(currentTurns: TurnProjection[], incomingTurns: TurnProjection[]): TurnProjection[];
|
|
61
70
|
/**
|
|
62
71
|
* 用一份新的 turn 列表重建会话状态。
|
|
63
72
|
* addErrorMessage 注入的 role==="error" 消息不在 turns 里,默认会被物化过程抹掉;
|
|
@@ -65,7 +74,12 @@ export declare function reconcileOptimisticUserTurns(currentTurns: TurnProjectio
|
|
|
65
74
|
* turn 推进(新消息 / rewind / 重连)时旧错误自动消失。
|
|
66
75
|
*/
|
|
67
76
|
export declare function withTurns(state: SessionState, turns: TurnProjection[]): SessionState;
|
|
68
|
-
|
|
77
|
+
/**
|
|
78
|
+
* 安装一份服务端权威队列快照。旧 revision、别的会话或结构不合法的快照一律不采纳,
|
|
79
|
+
* 直接返回原 state——引用不变,订阅者不会收到多余通知。
|
|
80
|
+
*/
|
|
81
|
+
export declare function withQueue(state: SessionState, snapshot: SessionQueueSnapshot): SessionState;
|
|
82
|
+
export declare function addUserMessage(state: SessionState, content: MessageContent, clientRequestId?: string): SessionState;
|
|
69
83
|
export declare function upsertTurn(state: SessionState, turn: TurnProjection): SessionState;
|
|
70
84
|
export declare function applyTurnPatch(state: SessionState, patch: PatchEnvelope): SessionState;
|
|
71
85
|
export declare function addErrorMessage(state: SessionState, content: string): SessionState;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Return the server-requested delay for a typed temporary auth rejection. */
|
|
2
|
+
export declare function authBusyRetryDelayMs(error: unknown): number | null;
|
|
3
|
+
/** One pending manual reconnect for Socket.IO CONNECT_ERROR auth overloads. */
|
|
4
|
+
export declare class AuthBusyReconnect {
|
|
5
|
+
private timer;
|
|
6
|
+
get pending(): boolean;
|
|
7
|
+
schedule(error: unknown, reconnect: () => void): boolean;
|
|
8
|
+
cancel(): void;
|
|
9
|
+
}
|
|
@@ -3,6 +3,8 @@ export interface RawEvent {
|
|
|
3
3
|
type: string;
|
|
4
4
|
payload: Record<string, unknown>;
|
|
5
5
|
}
|
|
6
|
+
/** 在任何事件处理前确认 Socket.IO 批次已到达客户端。 */
|
|
7
|
+
export declare function acknowledgeTurnEvents(acknowledge?: () => void): void;
|
|
6
8
|
export type ProjectionUpdate = {
|
|
7
9
|
kind: "upsert";
|
|
8
10
|
turn: TurnProjection;
|
|
@@ -13,6 +15,8 @@ export type ProjectionUpdate = {
|
|
|
13
15
|
toolName?: string;
|
|
14
16
|
workspacePath?: string;
|
|
15
17
|
};
|
|
18
|
+
/** 是否包含会改变聊天运行生命周期的事件。纯观测/整理事件不启动新一轮。 */
|
|
19
|
+
export declare function hasChatRunEvent(events: RawEvent[]): boolean;
|
|
16
20
|
/**
|
|
17
21
|
* 缺失 chat:end 时的终态兜底。只有被投影接受到当前 assistant turn 的 followup
|
|
18
22
|
* 才能结束流式态;陈旧 followup 或其后已有新轮事件时均返回 false。
|
|
@@ -22,7 +26,10 @@ export declare class ClientProjectionBuilder {
|
|
|
22
26
|
private activeTurns;
|
|
23
27
|
private lastTurnIds;
|
|
24
28
|
private latestTurns;
|
|
29
|
+
private turnsById;
|
|
25
30
|
private loopDescriptions;
|
|
31
|
+
/** loop_name → 正在运行的子智能体起点,agent:end 结算并挂到父工具调用上。 */
|
|
32
|
+
private activeSubagentRuns;
|
|
26
33
|
private pendingChildPauses;
|
|
27
34
|
private pendingMemoryRefs;
|
|
28
35
|
private activeCompactions;
|
|
@@ -52,6 +59,8 @@ export declare class ClientProjectionBuilder {
|
|
|
52
59
|
private onToolCallCreated;
|
|
53
60
|
private onToolCallArgumentsDelta;
|
|
54
61
|
private onResponseDone;
|
|
62
|
+
private applyFirstTokenDelivery;
|
|
63
|
+
private applyStreamTiming;
|
|
55
64
|
private onToolResultDone;
|
|
56
65
|
private onToolResultDelta;
|
|
57
66
|
private onToolUi;
|
|
@@ -60,6 +69,7 @@ export declare class ClientProjectionBuilder {
|
|
|
60
69
|
private onChildPause;
|
|
61
70
|
private onChatEnd;
|
|
62
71
|
private onAgentEnd;
|
|
72
|
+
private settleSubagentRun;
|
|
63
73
|
private dropIncompleteToolCalls;
|
|
64
74
|
private onCompaction;
|
|
65
75
|
private syncPatch;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ContentBlock, PendingQuestionRef, ToolCallProjection, TurnProjection } from "../../schemas/projection";
|
|
2
2
|
import type { TurnState } from "./state";
|
|
3
3
|
export declare function upsertToolCall(state: TurnState, toolCallId: string, toolName: string, displayName: string, args: string): void;
|
|
4
|
-
export declare function applyToolResult(state: TurnState, toolCallId: string, result: unknown, status: ToolCallProjection["status"], durationMs: number | null, sourceLoop: {
|
|
4
|
+
export declare function applyToolResult(state: TurnState, toolCallId: string, result: unknown, status: ToolCallProjection["status"], durationMs: number | null, ranInParallel: boolean | null, sourceLoop: {
|
|
5
5
|
loop_name: string;
|
|
6
6
|
description: string;
|
|
7
|
-
} | null): void;
|
|
7
|
+
} | null, startedAt?: string | null): void;
|
|
8
8
|
export declare function appendToolResultDelta(state: TurnState, toolCallId: string, delta: string, reset?: boolean): void;
|
|
9
9
|
export declare function appendTextBlock(state: TurnState, blockType: ContentBlock["type"], content: unknown): void;
|
|
10
10
|
export declare function appendToolCallArguments(state: TurnState, toolCallId: string, delta: string): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { acceptedPostChatFollowupCompletesLatestRun, ClientProjectionBuilder, type RawEvent, type ProjectionUpdate, } from "./builder";
|
|
1
|
+
export { acceptedPostChatFollowupCompletesLatestRun, acknowledgeTurnEvents, ClientProjectionBuilder, hasChatRunEvent, type RawEvent, type ProjectionUpdate, } from "./builder";
|
|
2
2
|
export { type TurnState, createTurnState, snapshot, PAUSE_TOOL_NAMES } from "./state";
|
|
3
3
|
export { projectHistory, type RawHistoryEntry } from "./history";
|
|
4
4
|
export { buildContextTurn } from "./context";
|
|
@@ -10,6 +10,13 @@ export interface TurnState {
|
|
|
10
10
|
toolCalls: ToolCallProjection[];
|
|
11
11
|
usage: Record<string, unknown> | null;
|
|
12
12
|
responseDone: boolean;
|
|
13
|
+
firstTokenMs: number | null;
|
|
14
|
+
streamDurationMs: number | null;
|
|
15
|
+
firstTokenDeliveryMs: number | null;
|
|
16
|
+
/** 生产者上报的模型请求时序;缺失时保持 null,不用浏览器时间补(#2581 A1/I1)。 */
|
|
17
|
+
modelStartedAt: string | null;
|
|
18
|
+
modelDurationMs: number | null;
|
|
19
|
+
modelStartedAtReported: boolean;
|
|
13
20
|
memoryRefs: MemoryRef[] | null;
|
|
14
21
|
parentForkToolCallId: string | null;
|
|
15
22
|
}
|