@blade-hq/agent-client 2610.0.0-beta.9 → 2610.0.0-beta.90
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 +266 -11
- package/dist/auth.d.ts +0 -1
- package/dist/blade-client.d.ts +24 -3
- package/dist/index.d.ts +37 -7
- package/dist/index.js +2395 -295
- package/dist/index.js.map +1 -1
- package/dist/platform-endpoints.d.ts +14 -0
- package/dist/polling-backoff.d.ts +11 -0
- package/dist/resources/chat-groups.d.ts +31 -0
- package/dist/resources/computers.d.ts +115 -0
- package/dist/resources/models.d.ts +2 -0
- package/dist/resources/plugins.d.ts +43 -0
- package/dist/resources/sessions.d.ts +193 -2
- package/dist/resources/site-visits.d.ts +32 -0
- package/dist/rest.d.ts +8 -0
- package/dist/schemas/context.d.ts +49 -0
- package/dist/schemas/event.d.ts +9 -0
- package/dist/schemas/mcp-app.d.ts +30 -0
- package/dist/schemas/message-utils.d.ts +15 -1
- package/dist/schemas/message.d.ts +6 -0
- package/dist/schemas/projection.d.ts +37 -2
- package/dist/schemas/session.d.ts +10 -1
- package/dist/session/agent-session.d.ts +159 -17
- package/dist/session/definition.d.ts +6 -1
- package/dist/session/errors.d.ts +11 -0
- package/dist/session/events.d.ts +16 -2
- package/dist/session/history-page.d.ts +9 -0
- package/dist/session/hub.d.ts +9 -1
- package/dist/session/queue.d.ts +104 -0
- package/dist/session/state.d.ts +18 -2
- package/dist/shared/auth-busy.d.ts +9 -0
- package/dist/shared/projection/builder.d.ts +12 -0
- package/dist/shared/projection/context.d.ts +3 -0
- package/dist/shared/projection/helpers.d.ts +2 -2
- package/dist/shared/projection/history.d.ts +1 -0
- package/dist/shared/projection/index.d.ts +2 -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 +199 -6
- package/package.json +1 -1
- package/public-api.md +960 -17
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContentBlock, PendingQuestionRef, ToolBridgeContent } from "./projection";
|
|
2
|
+
import type { ContextProjectionData } from "./context";
|
|
2
3
|
export interface TextContentPart {
|
|
3
4
|
type: "text";
|
|
4
5
|
text: string;
|
|
@@ -7,6 +8,9 @@ export interface ImageUrlContentPart {
|
|
|
7
8
|
type: "image_url";
|
|
8
9
|
image_url: {
|
|
9
10
|
url: string;
|
|
11
|
+
source_path?: string;
|
|
12
|
+
width?: number;
|
|
13
|
+
height?: number;
|
|
10
14
|
};
|
|
11
15
|
}
|
|
12
16
|
export interface FileContentPart {
|
|
@@ -82,4 +86,6 @@ export interface ChatMessage {
|
|
|
82
86
|
blocks?: ContentBlock[];
|
|
83
87
|
/** Compaction metadata for compaction turns. */
|
|
84
88
|
compaction?: CompactionInfo;
|
|
89
|
+
/** Sourced Context metadata for rendering or headless consumers. */
|
|
90
|
+
context?: ContextProjectionData;
|
|
85
91
|
}
|
|
@@ -5,6 +5,8 @@ export interface ToolBridgeContent {
|
|
|
5
5
|
export interface ContentBlock {
|
|
6
6
|
type: "text" | "thinking" | "tool_use" | "tool_result" | "tool_ui" | "tool_bridge" | "system_notification" | "mode_change" | "plan_status" | "ask_user_answer";
|
|
7
7
|
content: unknown;
|
|
8
|
+
/** Stable history-entry identity for a tool_bridge delivery. */
|
|
9
|
+
delivery_id?: string | null;
|
|
8
10
|
tool_call_id?: string | null;
|
|
9
11
|
tool_name?: string | null;
|
|
10
12
|
display_name?: string | null;
|
|
@@ -14,6 +16,15 @@ export interface PendingQuestionRef {
|
|
|
14
16
|
child_tool_call_id: string;
|
|
15
17
|
description?: string | null;
|
|
16
18
|
}
|
|
19
|
+
/** 一次运行(用户轮或子智能体)的实际墙钟区间。 */
|
|
20
|
+
export interface RunTiming {
|
|
21
|
+
run_id: string;
|
|
22
|
+
scope: "chat" | "subagent";
|
|
23
|
+
loop_id: string;
|
|
24
|
+
started_at?: string | null;
|
|
25
|
+
duration_ms?: number | null;
|
|
26
|
+
status: "completed" | "failed" | "cancelled" | "paused" | "incomplete";
|
|
27
|
+
}
|
|
17
28
|
export interface ToolCallProjection {
|
|
18
29
|
id: string;
|
|
19
30
|
tool_name: string;
|
|
@@ -22,6 +33,11 @@ export interface ToolCallProjection {
|
|
|
22
33
|
status: "pending" | "done" | "error" | "cancelled" | "awaiting_answer" | string;
|
|
23
34
|
result?: unknown;
|
|
24
35
|
duration_ms?: number | null;
|
|
36
|
+
/** 工具真正进入执行的墙钟起点;排队/待审批的调用没有该值。 */
|
|
37
|
+
started_at?: string | null;
|
|
38
|
+
ran_in_parallel?: boolean;
|
|
39
|
+
/** 该工具派生的子智能体运行区间,仅作外层展示。 */
|
|
40
|
+
subagent_runs?: RunTiming[];
|
|
25
41
|
pending_question_ref?: PendingQuestionRef | null;
|
|
26
42
|
}
|
|
27
43
|
export interface MemoryRef {
|
|
@@ -47,20 +63,39 @@ export interface TurnProjection {
|
|
|
47
63
|
sequence: number;
|
|
48
64
|
turn_id: string;
|
|
49
65
|
loop_id: string;
|
|
50
|
-
kind?: "message" | "compaction";
|
|
66
|
+
kind?: "message" | "context" | "compaction";
|
|
51
67
|
role: "user" | "assistant" | "system";
|
|
52
68
|
status: "streaming" | "completed" | "paused" | "failed" | "interrupted";
|
|
53
69
|
blocks: ContentBlock[];
|
|
54
70
|
tool_calls: ToolCallProjection[];
|
|
55
71
|
model?: string | null;
|
|
56
72
|
usage?: Record<string, unknown> | null;
|
|
57
|
-
|
|
73
|
+
/** 模型请求区间(不含后续工具);未知时为 null,不用 0 冒充。 */
|
|
74
|
+
duration_ms?: number | null;
|
|
75
|
+
first_token_ms?: number | null;
|
|
76
|
+
stream_duration_ms?: number | null;
|
|
77
|
+
first_token_delivery_ms?: number | null;
|
|
58
78
|
started_at?: string | null;
|
|
79
|
+
/** started_at 是否来自生产者上报(而非旧会话的落盘回落)。瀑布只承认前者。 */
|
|
80
|
+
has_reported_timing?: boolean;
|
|
59
81
|
context_window?: number;
|
|
60
82
|
memory_refs?: MemoryRef[] | null;
|
|
61
83
|
parent_fork_tool_call_id?: string | null;
|
|
84
|
+
context_kind?: string | null;
|
|
85
|
+
context_key?: string | null;
|
|
86
|
+
context_name?: string | null;
|
|
87
|
+
context_order?: number | null;
|
|
88
|
+
context_action?: import("./context").ContextAction | null;
|
|
89
|
+
context_revision?: string | null;
|
|
90
|
+
/** 原始 Context 内容仅供开发者模式诊断;常规消息投影不消费。 */
|
|
91
|
+
context_content?: unknown;
|
|
92
|
+
display_title?: string | null;
|
|
93
|
+
display_summary?: string | null;
|
|
94
|
+
sources?: import("./context").ContextSourceInfo[] | null;
|
|
62
95
|
/** Client-only stable identity for reconciling an optimistic user turn. */
|
|
63
96
|
client_render_id?: string | null;
|
|
97
|
+
/** Send identity echoed by the server for exact optimistic reconciliation. */
|
|
98
|
+
client_request_id?: string | null;
|
|
64
99
|
compaction_id?: string | null;
|
|
65
100
|
summary_preview?: string | null;
|
|
66
101
|
summary_full?: string | null;
|
|
@@ -62,6 +62,7 @@ export declare const SessionInfo: import("arktype/internal/variants/object.ts").
|
|
|
62
62
|
shared?: boolean | undefined;
|
|
63
63
|
memory_enabled?: boolean | undefined;
|
|
64
64
|
is_persistent?: boolean | undefined;
|
|
65
|
+
initiator?: "sdk" | "ui" | "unknown" | null | undefined;
|
|
65
66
|
is_headless?: boolean | undefined;
|
|
66
67
|
viewer_role?: "owner" | "viewer" | undefined;
|
|
67
68
|
template_id?: string | null | undefined;
|
|
@@ -82,7 +83,11 @@ export declare const SessionInfo: import("arktype/internal/variants/object.ts").
|
|
|
82
83
|
disable_tools?: string[] | undefined;
|
|
83
84
|
runtime_type?: string | null | undefined;
|
|
84
85
|
daemon_id?: string | null | undefined;
|
|
86
|
+
agent_runtime_id?: string | null | undefined;
|
|
85
87
|
workspace_path?: string | null | undefined;
|
|
88
|
+
ba_version?: string | null | undefined;
|
|
89
|
+
sandbox_version?: string | null | undefined;
|
|
90
|
+
chat_group_id?: string | null | undefined;
|
|
86
91
|
match?: unknown;
|
|
87
92
|
}, {}>;
|
|
88
93
|
export type SessionInfo = Omit<typeof SessionInfo.infer, "template_id" | "solution" | "ports" | "match"> & {
|
|
@@ -106,9 +111,13 @@ export type SessionInfo = Omit<typeof SessionInfo.infer, "template_id" | "soluti
|
|
|
106
111
|
is_example?: boolean;
|
|
107
112
|
ports?: SessionPortMapping[];
|
|
108
113
|
disable_tools?: string[];
|
|
109
|
-
runtime_type?: "sandbox" | "daemon" | string | null;
|
|
114
|
+
runtime_type?: "sandbox" | "daemon" | "native" | string | null;
|
|
110
115
|
daemon_id?: string | null;
|
|
116
|
+
agent_runtime_id?: string | null;
|
|
111
117
|
workspace_path?: string | null;
|
|
118
|
+
ba_version?: string | null;
|
|
119
|
+
sandbox_version?: string | null;
|
|
120
|
+
chat_group_id?: string | null;
|
|
112
121
|
/** 仅在带 q 搜索的列表响应中出现;普通列表里此字段缺省。 */
|
|
113
122
|
match?: SessionSearchMatch | null;
|
|
114
123
|
};
|
|
@@ -4,15 +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
|
-
streamTokens: boolean;
|
|
12
12
|
emitEvent(event: string, data: unknown): void;
|
|
13
13
|
emitWithAck(event: string, data: unknown, timeoutMs: number): Promise<Record<string, unknown> | undefined>;
|
|
14
14
|
ensureConnected(): void;
|
|
15
|
-
|
|
15
|
+
fetchTurnsPage(sessionId: string, query?: SessionTurnsQuery, init?: RequestInit): Promise<SessionTurnsPage>;
|
|
16
|
+
fetchTurnIndex(sessionId: string): Promise<SessionTurnIndex>;
|
|
17
|
+
fetchCommands(sessionId: string): Promise<HistoricalCommand[]>;
|
|
16
18
|
/** 会话详情,目前只用来取回放状态。 */
|
|
17
19
|
fetchSessionInfo(sessionId: string): Promise<{
|
|
18
20
|
replay_state?: ReplayState | null;
|
|
@@ -53,6 +55,13 @@ export interface SendOptions {
|
|
|
53
55
|
deprecate_entry_ids: string[];
|
|
54
56
|
};
|
|
55
57
|
replayDecision?: "keep_replay" | "continue_replay";
|
|
58
|
+
/** 用户显式选择的知识库 id;后端将元信息作为 once Context 提供给模型。 */
|
|
59
|
+
kbIds?: number[];
|
|
60
|
+
/**
|
|
61
|
+
* 声明"会话在跑就把这条消息放进等待队列"。默认不排队、运行中直接报错:
|
|
62
|
+
* 是否真的入队由服务端按运行状态判定(等待回答时发送即作答,不会被误排队)。
|
|
63
|
+
*/
|
|
64
|
+
queueWhenRunning?: boolean;
|
|
56
65
|
}
|
|
57
66
|
export declare function sessionStatusFromChatEnd(status: string | undefined): SessionStatus;
|
|
58
67
|
/**
|
|
@@ -75,10 +84,45 @@ export declare class AgentSession {
|
|
|
75
84
|
private patchFlushCancel;
|
|
76
85
|
private joined;
|
|
77
86
|
private joinPending;
|
|
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;
|
|
78
101
|
private replayQueue;
|
|
102
|
+
private commandRestoreQueue;
|
|
103
|
+
private commandRecoveryGeneration;
|
|
104
|
+
private commandRestoreRetryAttempt;
|
|
105
|
+
private commandRestoreRetryHandle;
|
|
106
|
+
private bufferedCommandDeliveries;
|
|
79
107
|
private pendingReplayMessage;
|
|
80
108
|
private pendingReplayMode;
|
|
109
|
+
private pendingReplayKbIds;
|
|
81
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;
|
|
125
|
+
private subscriptionReplaySource;
|
|
82
126
|
private disposed;
|
|
83
127
|
private getAppContext;
|
|
84
128
|
private sendPending;
|
|
@@ -89,11 +133,55 @@ export declare class AgentSession {
|
|
|
89
133
|
private readonly seenToolCallIds;
|
|
90
134
|
private readonly finishedToolCallIds;
|
|
91
135
|
private readonly emittedMessageTurnIds;
|
|
92
|
-
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;
|
|
93
146
|
/** 从一份静态 turn 快照构造只展示历史的会话(例如公开分享页)。 */
|
|
94
147
|
static fromTurns(sessionId: string, turns: TurnProjection[]): AgentSession;
|
|
95
148
|
/** 返回当前会话快照。引用稳定:状态未变化时返回同一对象。 */
|
|
96
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[]>;
|
|
97
185
|
/** 订阅状态变化。返回取消订阅函数。 */
|
|
98
186
|
subscribe(listener: () => void): () => void;
|
|
99
187
|
private setState;
|
|
@@ -116,10 +204,44 @@ export declare class AgentSession {
|
|
|
116
204
|
attachApp(options: AttachAppOptions): Promise<AppCliAttachment | null>;
|
|
117
205
|
/** 发送一条消息。字符串或多模态内容均可。 */
|
|
118
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;
|
|
119
241
|
private sendUnlocked;
|
|
120
242
|
private rollbackOptimisticSend;
|
|
121
|
-
/**
|
|
122
|
-
|
|
243
|
+
/** 撤回一次乐观提交:移除乐观消息、恢复提问作答、清掉回放草稿。 */
|
|
244
|
+
private discardOptimisticSend;
|
|
123
245
|
/** 停止当前回复。 */
|
|
124
246
|
stop(): Promise<Record<string, unknown>>;
|
|
125
247
|
/** 手动触发上下文压缩。 */
|
|
@@ -146,10 +268,17 @@ export declare class AgentSession {
|
|
|
146
268
|
get isDisposed(): boolean;
|
|
147
269
|
/** @internal 连接状态变化(同 client 下所有会话共享一条连接)。 */
|
|
148
270
|
_setConnection(connection: ConnectionStatus): void;
|
|
271
|
+
/**
|
|
272
|
+
* @internal 服务端广播的权威队列快照。
|
|
273
|
+
* 结构不合法或 revision 不新的一律丢弃,绝不用旧快照覆盖新状态。
|
|
274
|
+
*/
|
|
275
|
+
_handleQueueUpdated(payload: unknown): void;
|
|
149
276
|
/** @internal socket 重连后由 Hub 调用:重新加入房间并补数。 */
|
|
150
277
|
_rejoinAfterReconnect(): Promise<void>;
|
|
151
278
|
/** @internal 首次连接:加载历史 + 加入房间。 */
|
|
152
279
|
_bootstrap(): Promise<void>;
|
|
280
|
+
/** 订阅生命周期串行执行;一次失败只由自己的调用方观察,不截断后续重连。 */
|
|
281
|
+
private enqueueSubscription;
|
|
153
282
|
/** 改回放是 owner 专属(服务端 PATCH 走 _get_owned_session)。 */
|
|
154
283
|
private get canControlReplay();
|
|
155
284
|
/** 拉一次会话详情,填回放状态和只读身份;失败按非回放会话处理,不挡启动。 */
|
|
@@ -167,19 +296,21 @@ export declare class AgentSession {
|
|
|
167
296
|
/** @internal */
|
|
168
297
|
_handleTurnEnd(turn: TurnProjection): void;
|
|
169
298
|
/** @internal 历史回放通道(event_format: raw)。 */
|
|
170
|
-
_handleTurnEvents(events: RawEvent[]): void;
|
|
299
|
+
_handleTurnEvents(events: RawEvent[], replayed?: boolean): void;
|
|
171
300
|
/** @internal */
|
|
172
301
|
_handleChatStart(): void;
|
|
173
302
|
/** @internal */
|
|
174
303
|
_handleChatEnd(status: string): void;
|
|
175
304
|
/** @internal */
|
|
176
|
-
_handleSystemError(message: string): void;
|
|
305
|
+
_handleSystemError(message: string, code?: string): void;
|
|
177
306
|
/** @internal */
|
|
178
307
|
_handleTaskUpdated(tasks: unknown[]): void;
|
|
179
308
|
/** @internal */
|
|
180
309
|
_handleWorkspaceFilesChanged(filePath?: string): void;
|
|
181
310
|
/** @internal */
|
|
182
311
|
_handleSessionUpdated(data: {
|
|
312
|
+
workspace_path?: string;
|
|
313
|
+
chat_group_id?: string | null;
|
|
183
314
|
intent?: string;
|
|
184
315
|
status?: SessionStatus;
|
|
185
316
|
model?: string | null;
|
|
@@ -200,10 +331,13 @@ export declare class AgentSession {
|
|
|
200
331
|
/** @internal */
|
|
201
332
|
_handleArtifact(event: AgentSessionEvents["artifact"]): void;
|
|
202
333
|
/** @internal 加入房间的应答里带 is_running,据此校准流式状态。 */
|
|
203
|
-
_applyRunningState(isRunning: boolean): void;
|
|
334
|
+
_applyRunningState(isRunning: boolean, liveRevisionAtJoin?: number): void;
|
|
204
335
|
private markRunningIfIdle;
|
|
205
336
|
private finishStreaming;
|
|
206
337
|
private completeChat;
|
|
338
|
+
private markTurnFresh;
|
|
339
|
+
private markOptimisticTurnPending;
|
|
340
|
+
private pruneOptimisticLiveRevisions;
|
|
207
341
|
private applyTurn;
|
|
208
342
|
private schedulePatchFlush;
|
|
209
343
|
private cancelPatchFlush;
|
|
@@ -211,18 +345,26 @@ export declare class AgentSession {
|
|
|
211
345
|
/** turn 数据落地后统一派生语义事件(指令 / 工具调用 / 消息 / 模式 / 预览 / OOM)。 */
|
|
212
346
|
private emitTurnDerivedEvents;
|
|
213
347
|
private dispatchCommand;
|
|
348
|
+
private dispatchRawCommands;
|
|
349
|
+
private deliverCommand;
|
|
350
|
+
private deliverCommandNow;
|
|
214
351
|
private notifySandboxOom;
|
|
215
|
-
private syncTurnsFromHistory;
|
|
216
352
|
/**
|
|
217
|
-
*
|
|
353
|
+
* 权威刷新(运行结束 / 重连补数):**按当前窗口第一条发言取窗口**,不再另记"用户想看哪段"。
|
|
218
354
|
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* dispatch 内部按 deliveredCommandKeys 去重,重复调用安全。
|
|
355
|
+
* 窗口本身就是"用户在看哪段":首屏就是最新一页,向上翻页 / 定位之后首条就是用户滚到的位置。
|
|
356
|
+
* 直接拿 `from_entry_id = 当前窗口第一条 turn_id` 去取,运行结束、重连补数、翻页之后的对账
|
|
357
|
+
* 是同一条规则,不需要意图、锚点、已落地锚点这些状态;窗口首条已经不在分支上(404 / 409)时
|
|
358
|
+
* 回落到最新一页。
|
|
224
359
|
*/
|
|
225
|
-
private
|
|
360
|
+
private refreshAuthoritativeHistory;
|
|
361
|
+
private replaceFromHistory;
|
|
362
|
+
private invalidateOlderPaging;
|
|
363
|
+
private beginCommandRecovery;
|
|
364
|
+
private scheduleHistoricalCommandRestore;
|
|
365
|
+
private flushBufferedCommands;
|
|
366
|
+
/** 首次订阅前的 REST 历史建立语义事件基线,随后同一工具的订阅回放不再投递。 */
|
|
367
|
+
private seedToolCallDeliveryBaseline;
|
|
226
368
|
private ensureJoined;
|
|
227
369
|
private ensureIdleBeforeSend;
|
|
228
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
|
/**
|
|
@@ -23,14 +24,22 @@ export interface AgentSessionEvents {
|
|
|
23
24
|
toolResult: {
|
|
24
25
|
toolCall: ToolCallInfo;
|
|
25
26
|
turn: TurnProjection;
|
|
27
|
+
/** 实时到达,或首次连接/断线重连时由服务端回放。 */
|
|
28
|
+
source: "live" | "bootstrap_replay" | "reconnect_replay";
|
|
26
29
|
};
|
|
27
30
|
/** 一轮回复结束。status: completed / paused / interrupted / failed。 */
|
|
28
31
|
chatEnd: {
|
|
29
32
|
status: string;
|
|
30
33
|
};
|
|
31
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* 运行错误(system:error、发送失败等)。
|
|
36
|
+
*
|
|
37
|
+
* `code` 是服务端给的机器可读原因(如 `owner_timeout` / `route_broken`),
|
|
38
|
+
* 供调用方决定要不要重试;`message` 是可以直接展示的业务文案。
|
|
39
|
+
*/
|
|
32
40
|
error: {
|
|
33
41
|
message: string;
|
|
42
|
+
code?: string;
|
|
34
43
|
};
|
|
35
44
|
/** planning / executing 模式切换。 */
|
|
36
45
|
modeChange: {
|
|
@@ -74,10 +83,13 @@ export interface AgentSessionEvents {
|
|
|
74
83
|
};
|
|
75
84
|
/** 工具返回值带 _meta.ui 的可视化预览(resource HTML / URI)。 */
|
|
76
85
|
toolPreview: {
|
|
86
|
+
/** Source file identity when the preview comes from a local HTML file. */
|
|
87
|
+
sourcePath?: string;
|
|
77
88
|
toolCallId: string;
|
|
78
|
-
type: "resource-html" | "resource-uri";
|
|
89
|
+
type: "resource-html" | "resource-uri" | "resource-file";
|
|
79
90
|
content: string;
|
|
80
91
|
title: string;
|
|
92
|
+
mcpApp?: McpAppData;
|
|
81
93
|
};
|
|
82
94
|
/** 任务清单(TaskCreate 等)更新。 */
|
|
83
95
|
taskListUpdated: {
|
|
@@ -106,6 +118,8 @@ export interface AgentSessionEvents {
|
|
|
106
118
|
};
|
|
107
119
|
/** 会话属性(名称、状态、绑定技能等)被服务端更新。 */
|
|
108
120
|
sessionUpdated: {
|
|
121
|
+
workspacePath?: string;
|
|
122
|
+
chatGroupId?: string | null;
|
|
109
123
|
intent?: string;
|
|
110
124
|
status?: SessionStatus;
|
|
111
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
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { BladeClient } from "../blade-client";
|
|
2
2
|
import { AgentSession } from "./agent-session";
|
|
3
3
|
import type { ConnectionStatus } from "./state";
|
|
4
|
+
export interface SessionConnectOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 在会话进入事件路由后、历史加载与房间订阅开始前同步执行。
|
|
7
|
+
* 适合安装不能漏掉 bootstrap 回放事件的监听器。
|
|
8
|
+
*/
|
|
9
|
+
setup?: (session: AgentSession) => void;
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* 每个 BladeClient 一个:管理一条 Socket.IO 连接,按 session_id 把事件
|
|
6
13
|
* 路由给各 AgentSession 实例。多会话共享连接、状态互不干扰。
|
|
@@ -15,6 +22,7 @@ export declare class SessionHub {
|
|
|
15
22
|
private readonly connectionListeners;
|
|
16
23
|
private bound;
|
|
17
24
|
private hasConnected;
|
|
25
|
+
private readonly authBusyReconnect;
|
|
18
26
|
constructor(client: BladeClient);
|
|
19
27
|
/** 当前 Socket.IO 连接状态。 */
|
|
20
28
|
getConnection(): ConnectionStatus;
|
|
@@ -27,7 +35,7 @@ export declare class SessionHub {
|
|
|
27
35
|
* 只有最后一份归还才退订房间——否则 StrictMode 双跑、路由抖动这类"连两次
|
|
28
36
|
* 只释放一次"的场景会把仍在用的会话退订掉,之后所有 turn/chat 事件静默丢失。
|
|
29
37
|
*/
|
|
30
|
-
connect(sessionId: string): Promise<AgentSession>;
|
|
38
|
+
connect(sessionId: string, options?: SessionConnectOptions): Promise<AgentSession>;
|
|
31
39
|
/** 已有实例则返回,不触发网络请求。 */
|
|
32
40
|
peek(sessionId: string): AgentSession | undefined;
|
|
33
41
|
/** 手动重连(例如登录态变化后)。 */
|
|
@@ -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;
|