@blade-hq/agent-client 2610.0.1-alpha.1 → 2610.0.2
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 +281 -11
- package/dist/auth.d.ts +0 -1
- package/dist/blade-client.d.ts +24 -1
- package/dist/index.d.ts +38 -7
- package/dist/index.js +2520 -283
- 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 +238 -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 +8 -0
- package/dist/schemas/projection-utils.d.ts +3 -0
- package/dist/schemas/projection.d.ts +53 -2
- package/dist/schemas/session.d.ts +10 -1
- package/dist/session/agent-session.d.ts +160 -16
- 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 +19 -2
- package/dist/shared/auth-busy.d.ts +9 -0
- package/dist/shared/projection/builder.d.ts +17 -0
- package/dist/shared/projection/context.d.ts +3 -0
- package/dist/shared/projection/helpers.d.ts +4 -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 +201 -2
- package/package.json +4 -2
- package/public-api.md +1071 -23
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Archived MCP App data. Credentials and executable server configuration are excluded. */
|
|
2
|
+
export interface McpAppData {
|
|
3
|
+
sourceId: string;
|
|
4
|
+
resourceUri: string;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
resourceMeta: Record<string, unknown>;
|
|
7
|
+
toolInput: Record<string, unknown>;
|
|
8
|
+
toolResult: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface McpAppArchive extends McpAppData {
|
|
11
|
+
html: string;
|
|
12
|
+
plugin: string;
|
|
13
|
+
/** Restore the last successful view before delivering a saved failure. */
|
|
14
|
+
previousToolInput?: Record<string, unknown>;
|
|
15
|
+
previousToolResult?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export declare function isMcpAppData(value: unknown): value is McpAppData;
|
|
18
|
+
/** Latest model-context state one MCP App saved for its own card. */
|
|
19
|
+
export interface McpAppContextState {
|
|
20
|
+
revision: string;
|
|
21
|
+
content: string;
|
|
22
|
+
plugin: string | null;
|
|
23
|
+
server_alias: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface McpAppContextUpdate {
|
|
26
|
+
request_id: string;
|
|
27
|
+
/** null on the first save; the server rejects a stale revision. */
|
|
28
|
+
expected_revision: string | null;
|
|
29
|
+
content: string;
|
|
30
|
+
}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import type { ChatMessage, FileContentPart, ImageUrlContentPart, MessageContent } from "./message";
|
|
2
|
+
/** Ordinary chat uses business copy; developer mode may retain diagnostics. */
|
|
3
|
+
export declare function chatErrorForDisplay(message: string, developer?: boolean): string;
|
|
2
4
|
export declare function normalizeMessageContent(content: unknown): MessageContent;
|
|
3
5
|
export declare function getTextContent(content: MessageContent): string;
|
|
4
6
|
export declare function isSystemReminderText(text: string): boolean;
|
|
5
7
|
export declare function isLegacyForkContextText(text: string): boolean;
|
|
6
|
-
|
|
8
|
+
/**
|
|
9
|
+
* 平台自己写进去、不该露给用户的内部提示。
|
|
10
|
+
*
|
|
11
|
+
* 服务端的消息导航目录有一份同名同语义的 Python 实现
|
|
12
|
+
* (`server/.../routes/sessions.py` 的 `_is_hidden_internal_message`):客户端靠它决定
|
|
13
|
+
* 渲染不渲染,服务端靠它决定进不进目录。改这里必须同时改那边,否则目录会列出没有对应
|
|
14
|
+
* 行的死条目,或者把内部提示原文当标题露出去。
|
|
15
|
+
*/
|
|
16
|
+
export declare function isHiddenInternalMessage(message: Pick<ChatMessage, "role" | "content">, options?: {
|
|
17
|
+
includeLegacyForkContext?: boolean;
|
|
18
|
+
}): boolean;
|
|
7
19
|
export declare function buildMessageContent(text: string, attachments: Array<{
|
|
8
20
|
kind: "file";
|
|
9
21
|
name: string;
|
|
@@ -55,4 +67,6 @@ interface MessageGroup {
|
|
|
55
67
|
}[];
|
|
56
68
|
}
|
|
57
69
|
export declare function groupMessagesByLoop(messages: ChatMessage[]): MessageGroup[];
|
|
70
|
+
/** A typed wake input stays internal for navigation, but has a visible notice. */
|
|
71
|
+
export declare function isBackgroundWakeMessage(message: Pick<ChatMessage, "blocks">): boolean;
|
|
58
72
|
export {};
|
|
@@ -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 {
|
|
@@ -70,6 +74,8 @@ export interface ChatMessage {
|
|
|
70
74
|
duration_ms?: number;
|
|
71
75
|
timestamp?: string;
|
|
72
76
|
entry_id?: string;
|
|
77
|
+
/** Stable React key retained when an optimistic user message is confirmed. */
|
|
78
|
+
render_id?: string;
|
|
73
79
|
parent_id?: string | null;
|
|
74
80
|
loop_name?: string;
|
|
75
81
|
/** Entry kind from backend -- present for non-message entries like mode changes. */
|
|
@@ -80,4 +86,6 @@ export interface ChatMessage {
|
|
|
80
86
|
blocks?: ContentBlock[];
|
|
81
87
|
/** Compaction metadata for compaction turns. */
|
|
82
88
|
compaction?: CompactionInfo;
|
|
89
|
+
/** Sourced Context metadata for rendering or headless consumers. */
|
|
90
|
+
context?: ContextProjectionData;
|
|
83
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 {
|
|
@@ -29,23 +45,57 @@ export interface MemoryRef {
|
|
|
29
45
|
content_preview: string;
|
|
30
46
|
skill_name?: string | null;
|
|
31
47
|
}
|
|
48
|
+
export interface FinalArtifact {
|
|
49
|
+
kind: "file" | "link";
|
|
50
|
+
target: string;
|
|
51
|
+
label: string;
|
|
52
|
+
}
|
|
53
|
+
export interface PostChatFollowup {
|
|
54
|
+
assistant_entry_id: string;
|
|
55
|
+
recaption?: string;
|
|
56
|
+
suggestions: string[];
|
|
57
|
+
final_artifacts?: FinalArtifact[];
|
|
58
|
+
feedback_eligible: boolean;
|
|
59
|
+
feedback_scope: "latest_result";
|
|
60
|
+
}
|
|
32
61
|
export interface TurnProjection {
|
|
33
62
|
id: string;
|
|
34
63
|
sequence: number;
|
|
35
64
|
turn_id: string;
|
|
36
65
|
loop_id: string;
|
|
37
|
-
kind?: "message" | "compaction";
|
|
66
|
+
kind?: "message" | "context" | "compaction";
|
|
38
67
|
role: "user" | "assistant" | "system";
|
|
39
68
|
status: "streaming" | "completed" | "paused" | "failed" | "interrupted";
|
|
40
69
|
blocks: ContentBlock[];
|
|
41
70
|
tool_calls: ToolCallProjection[];
|
|
42
71
|
model?: string | null;
|
|
43
72
|
usage?: Record<string, unknown> | null;
|
|
44
|
-
|
|
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;
|
|
45
78
|
started_at?: string | null;
|
|
79
|
+
/** started_at 是否来自生产者上报(而非旧会话的落盘回落)。瀑布只承认前者。 */
|
|
80
|
+
has_reported_timing?: boolean;
|
|
46
81
|
context_window?: number;
|
|
47
82
|
memory_refs?: MemoryRef[] | null;
|
|
48
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;
|
|
95
|
+
/** Client-only stable identity for reconciling an optimistic user turn. */
|
|
96
|
+
client_render_id?: string | null;
|
|
97
|
+
/** Send identity echoed by the server for exact optimistic reconciliation. */
|
|
98
|
+
client_request_id?: string | null;
|
|
49
99
|
compaction_id?: string | null;
|
|
50
100
|
summary_preview?: string | null;
|
|
51
101
|
summary_full?: string | null;
|
|
@@ -58,6 +108,7 @@ export interface TurnProjection {
|
|
|
58
108
|
trigger?: "auto" | "manual" | "forced_retry" | null;
|
|
59
109
|
failure_reason?: string | null;
|
|
60
110
|
fallback_applied?: boolean | null;
|
|
111
|
+
post_chat_followup?: PostChatFollowup | null;
|
|
61
112
|
}
|
|
62
113
|
export interface ArchivedFileInfo {
|
|
63
114
|
tool_name?: 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,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,6 +55,13 @@ export interface SendOptions {
|
|
|
52
55
|
deprecate_entry_ids: string[];
|
|
53
56
|
};
|
|
54
57
|
replayDecision?: "keep_replay" | "continue_replay";
|
|
58
|
+
/** 用户显式选择的知识库 id;后端将元信息作为 once Context 提供给模型。 */
|
|
59
|
+
kbIds?: number[];
|
|
60
|
+
/**
|
|
61
|
+
* 声明"会话在跑就把这条消息放进等待队列"。默认不排队、运行中直接报错:
|
|
62
|
+
* 是否真的入队由服务端按运行状态判定(等待回答时发送即作答,不会被误排队)。
|
|
63
|
+
*/
|
|
64
|
+
queueWhenRunning?: boolean;
|
|
55
65
|
}
|
|
56
66
|
export declare function sessionStatusFromChatEnd(status: string | undefined): SessionStatus;
|
|
57
67
|
/**
|
|
@@ -74,10 +84,45 @@ export declare class AgentSession {
|
|
|
74
84
|
private patchFlushCancel;
|
|
75
85
|
private joined;
|
|
76
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;
|
|
77
101
|
private replayQueue;
|
|
102
|
+
private commandRestoreQueue;
|
|
103
|
+
private commandRecoveryGeneration;
|
|
104
|
+
private commandRestoreRetryAttempt;
|
|
105
|
+
private commandRestoreRetryHandle;
|
|
106
|
+
private bufferedCommandDeliveries;
|
|
78
107
|
private pendingReplayMessage;
|
|
79
108
|
private pendingReplayMode;
|
|
109
|
+
private pendingReplayKbIds;
|
|
80
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;
|
|
81
126
|
private disposed;
|
|
82
127
|
private getAppContext;
|
|
83
128
|
private sendPending;
|
|
@@ -88,11 +133,55 @@ export declare class AgentSession {
|
|
|
88
133
|
private readonly seenToolCallIds;
|
|
89
134
|
private readonly finishedToolCallIds;
|
|
90
135
|
private readonly emittedMessageTurnIds;
|
|
91
|
-
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;
|
|
92
146
|
/** 从一份静态 turn 快照构造只展示历史的会话(例如公开分享页)。 */
|
|
93
147
|
static fromTurns(sessionId: string, turns: TurnProjection[]): AgentSession;
|
|
94
148
|
/** 返回当前会话快照。引用稳定:状态未变化时返回同一对象。 */
|
|
95
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[]>;
|
|
96
185
|
/** 订阅状态变化。返回取消订阅函数。 */
|
|
97
186
|
subscribe(listener: () => void): () => void;
|
|
98
187
|
private setState;
|
|
@@ -115,10 +204,44 @@ export declare class AgentSession {
|
|
|
115
204
|
attachApp(options: AttachAppOptions): Promise<AppCliAttachment | null>;
|
|
116
205
|
/** 发送一条消息。字符串或多模态内容均可。 */
|
|
117
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;
|
|
118
241
|
private sendUnlocked;
|
|
119
242
|
private rollbackOptimisticSend;
|
|
120
|
-
/**
|
|
121
|
-
|
|
243
|
+
/** 撤回一次乐观提交:移除乐观消息、恢复提问作答、清掉回放草稿。 */
|
|
244
|
+
private discardOptimisticSend;
|
|
122
245
|
/** 停止当前回复。 */
|
|
123
246
|
stop(): Promise<Record<string, unknown>>;
|
|
124
247
|
/** 手动触发上下文压缩。 */
|
|
@@ -145,10 +268,17 @@ export declare class AgentSession {
|
|
|
145
268
|
get isDisposed(): boolean;
|
|
146
269
|
/** @internal 连接状态变化(同 client 下所有会话共享一条连接)。 */
|
|
147
270
|
_setConnection(connection: ConnectionStatus): void;
|
|
271
|
+
/**
|
|
272
|
+
* @internal 服务端广播的权威队列快照。
|
|
273
|
+
* 结构不合法或 revision 不新的一律丢弃,绝不用旧快照覆盖新状态。
|
|
274
|
+
*/
|
|
275
|
+
_handleQueueUpdated(payload: unknown): void;
|
|
148
276
|
/** @internal socket 重连后由 Hub 调用:重新加入房间并补数。 */
|
|
149
277
|
_rejoinAfterReconnect(): Promise<void>;
|
|
150
278
|
/** @internal 首次连接:加载历史 + 加入房间。 */
|
|
151
279
|
_bootstrap(): Promise<void>;
|
|
280
|
+
/** 订阅生命周期串行执行;一次失败只由自己的调用方观察,不截断后续重连。 */
|
|
281
|
+
private enqueueSubscription;
|
|
152
282
|
/** 改回放是 owner 专属(服务端 PATCH 走 _get_owned_session)。 */
|
|
153
283
|
private get canControlReplay();
|
|
154
284
|
/** 拉一次会话详情,填回放状态和只读身份;失败按非回放会话处理,不挡启动。 */
|
|
@@ -166,19 +296,21 @@ export declare class AgentSession {
|
|
|
166
296
|
/** @internal */
|
|
167
297
|
_handleTurnEnd(turn: TurnProjection): void;
|
|
168
298
|
/** @internal 历史回放通道(event_format: raw)。 */
|
|
169
|
-
_handleTurnEvents(events: RawEvent[]): void;
|
|
299
|
+
_handleTurnEvents(events: RawEvent[], replayed?: boolean): void;
|
|
170
300
|
/** @internal */
|
|
171
301
|
_handleChatStart(): void;
|
|
172
302
|
/** @internal */
|
|
173
303
|
_handleChatEnd(status: string): void;
|
|
174
304
|
/** @internal */
|
|
175
|
-
_handleSystemError(message: string): void;
|
|
305
|
+
_handleSystemError(message: string, code?: string): void;
|
|
176
306
|
/** @internal */
|
|
177
307
|
_handleTaskUpdated(tasks: unknown[]): void;
|
|
178
308
|
/** @internal */
|
|
179
309
|
_handleWorkspaceFilesChanged(filePath?: string): void;
|
|
180
310
|
/** @internal */
|
|
181
311
|
_handleSessionUpdated(data: {
|
|
312
|
+
workspace_path?: string;
|
|
313
|
+
chat_group_id?: string | null;
|
|
182
314
|
intent?: string;
|
|
183
315
|
status?: SessionStatus;
|
|
184
316
|
model?: string | null;
|
|
@@ -199,9 +331,13 @@ export declare class AgentSession {
|
|
|
199
331
|
/** @internal */
|
|
200
332
|
_handleArtifact(event: AgentSessionEvents["artifact"]): void;
|
|
201
333
|
/** @internal 加入房间的应答里带 is_running,据此校准流式状态。 */
|
|
202
|
-
_applyRunningState(isRunning: boolean): void;
|
|
334
|
+
_applyRunningState(isRunning: boolean, liveRevisionAtJoin?: number): void;
|
|
203
335
|
private markRunningIfIdle;
|
|
204
336
|
private finishStreaming;
|
|
337
|
+
private completeChat;
|
|
338
|
+
private markTurnFresh;
|
|
339
|
+
private markOptimisticTurnPending;
|
|
340
|
+
private pruneOptimisticLiveRevisions;
|
|
205
341
|
private applyTurn;
|
|
206
342
|
private schedulePatchFlush;
|
|
207
343
|
private cancelPatchFlush;
|
|
@@ -209,18 +345,26 @@ export declare class AgentSession {
|
|
|
209
345
|
/** turn 数据落地后统一派生语义事件(指令 / 工具调用 / 消息 / 模式 / 预览 / OOM)。 */
|
|
210
346
|
private emitTurnDerivedEvents;
|
|
211
347
|
private dispatchCommand;
|
|
348
|
+
private dispatchRawCommands;
|
|
349
|
+
private deliverCommand;
|
|
350
|
+
private deliverCommandNow;
|
|
212
351
|
private notifySandboxOom;
|
|
213
|
-
private syncTurnsFromHistory;
|
|
214
352
|
/**
|
|
215
|
-
*
|
|
353
|
+
* 权威刷新(运行结束 / 重连补数):**按当前窗口第一条发言取窗口**,不再另记"用户想看哪段"。
|
|
216
354
|
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* dispatch 内部按 deliveredCommandKeys 去重,重复调用安全。
|
|
355
|
+
* 窗口本身就是"用户在看哪段":首屏就是最新一页,向上翻页 / 定位之后首条就是用户滚到的位置。
|
|
356
|
+
* 直接拿 `from_entry_id = 当前窗口第一条 turn_id` 去取,运行结束、重连补数、翻页之后的对账
|
|
357
|
+
* 是同一条规则,不需要意图、锚点、已落地锚点这些状态;窗口首条已经不在分支上(404 / 409)时
|
|
358
|
+
* 回落到最新一页。
|
|
222
359
|
*/
|
|
223
|
-
private
|
|
360
|
+
private refreshAuthoritativeHistory;
|
|
361
|
+
private replaceFromHistory;
|
|
362
|
+
private invalidateOlderPaging;
|
|
363
|
+
private beginCommandRecovery;
|
|
364
|
+
private scheduleHistoricalCommandRestore;
|
|
365
|
+
private flushBufferedCommands;
|
|
366
|
+
/** 首次订阅前的 REST 历史建立语义事件基线,随后同一工具的订阅回放不再投递。 */
|
|
367
|
+
private seedToolCallDeliveryBaseline;
|
|
224
368
|
private ensureJoined;
|
|
225
369
|
private ensureIdleBeforeSend;
|
|
226
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
|
/** 手动重连(例如登录态变化后)。 */
|