@blade-hq/agent-client 2608.0.6 → 2608.0.7-beta.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 CHANGED
@@ -56,6 +56,7 @@ new BladeClient({
56
56
  baseUrl: "https://blade.example.com", // 后端地址;同域部署可传 ""
57
57
  token: "sk-blade-xxx", // 可选:PAT。不传则用 cookie 或 login()
58
58
  tokenStorage: "local", // 可选:login() 的令牌存哪("local" 默认 / "memory")
59
+ streamTokens: false, // 可选:只接收完成态内容,不订阅逐 token 增量
59
60
  })
60
61
  ```
61
62
 
@@ -76,6 +77,15 @@ await client.auth.getProviders() // 服务端支持的登录方式
76
77
 
77
78
  `login()` 失败时的错误都带中文原因:弹窗被拦截、窗口被关闭、超时。
78
79
 
80
+ 令牌要留在自己的服务端时,浏览器只把授权回调拿到的一次性 code 交给后端,由后端换:
81
+
82
+ ```ts
83
+ // ExchangeCodeParams -> ExchangeCodeResult
84
+ const { access_token } = await client.auth.exchangeCode({ code, state, clientOrigin })
85
+ ```
86
+
87
+ `clientOrigin` 要和发起授权时用的 origin 一致;服务端调用会自动补 `Origin` 头。
88
+
79
89
  ## 会话:client.sessions
80
90
 
81
91
  ### 实时对话(推荐入口)
@@ -124,12 +134,80 @@ Solution 和 Skill 都声明为 `{ path, content }[]` 文本文件;SDK 负责
124
134
 
125
135
  ```ts
126
136
  await client.sessions.listSessions() // 会话列表
137
+ await client.sessions.searchOwnedContent({ q: "项目报告" }) // 搜索自己的对话和文件名
127
138
  await client.sessions.getSession(id) // 会话详情
128
139
  await client.sessions.updateSession(id, { intent: "新标题" })
129
140
  await client.sessions.deleteSession(id)
130
141
  await client.sessions.getSessionTurns(id) // 历史消息(投影格式,与实时流同构)
131
142
  ```
132
143
 
144
+ ### 会话回放(演示 / 彩排)
145
+
146
+ 拿一个已经聊完的会话当素材,重现当时的回复和工具调用,**完全不调用模型**。
147
+ 用来做演示和彩排:离线可跑、不花钱、不会临场翻车。
148
+
149
+ **它不是把现有会话切成回放模式,而是派生一个新会话**,源会话原样不动:
150
+
151
+ ```ts
152
+ // 1. 能不能拿它当素材(有并行子智能体、运行快照不完整的不行)
153
+ const { supported, reason } = await client.sessions.getReplayPreview(sourceId)
154
+ if (!supported) return show(reason) // reason 可直接展示给用户
155
+
156
+ // 2. 派生回放会话——这一步才是"进入回放",返回的是一个全新的 session_id
157
+ const { session_id: replayId } = await client.sessions.startReplaySession(sourceId)
158
+ const fast = await client.sessions.startReplaySession(sourceId, 2) // 或显式 1 | 2 | 5
159
+
160
+ // 3. 连上它,一出生就带着 replay 状态
161
+ const session = await client.sessions.connect(replayId)
162
+ session.getState().replay
163
+ // → { isReplay: true, speed: 5, sourceSessionId: sourceId }
164
+ ```
165
+
166
+ 不传倍速时用 `DEFAULT_REPLAY_SPEED`;所有创建入口都该用它,免得同一个源对话
167
+ 从不同入口开出来速度不一样。
168
+
169
+ **连上之后不会自动播——它跟用户对台词**(下面的 `session` 都是第 3 步连上的**回放会话**):
170
+
171
+ ```ts
172
+ // 源会话当初第一句问的是"帮我查下当前目录"
173
+ await session.send("帮我查下当前目录") // 对得上:按倍速重现当时的回复和工具调用
174
+ await session.send("今天天气怎么样") // 对不上:抛 replayMismatch 等你决定(见下)
175
+ ```
176
+
177
+ **回放中的状态和动作**都挂在会话上,和 `send()` 同级:
178
+
179
+ ```ts
180
+ session.getState().replay // { isReplay, speed, sourceSessionId } | null(不是回放会话)
181
+ session.getState().viewerRole // "viewer" 表示只读,改不动回放
182
+ await session.setReplaySpeed(2)
183
+ await session.exitReplay() // 退出回放,之后的对话真的运行
184
+ ```
185
+
186
+ 改回放是 owner 专属(服务端 `PATCH` 走 owner 校验),只读身份下这两个动作是 no-op。
187
+
188
+ `replay` 是 `SessionState` 的一部分,跟着快照订阅走——Vue 直接接 `shallowRef` 即可,
189
+ 不用自己拉状态:
190
+
191
+ ```ts
192
+ const state = shallowRef(session.getState()) // session = connect(replayId) 的返回值
193
+ session.subscribe(() => (state.value = session.getState()))
194
+ // 模板里 state.value.replay?.isReplay / state.value.replay?.speed
195
+ ```
196
+
197
+ 连接会话时会一并拉好初始回放状态,所以第一帧就知道自己在不在回放。
198
+
199
+ **输入冲突**:用户说的话和录制内容对不上时,服务端会推 `replayMismatch`,必须在事件里给出决定:
200
+
201
+ ```ts
202
+ session.on("replayMismatch", ({ expectedMessage, actualMessage, respond }) => {
203
+ respond("keep_replay") // 按录制内容继续
204
+ // respond("continue_replay") // 从这里开始真的运行
205
+ })
206
+ ```
207
+
208
+ `replayMismatch` **只交给最先注册的处理器**;一个都没注册时默认 `continue_replay`
209
+ (静默转为真实运行)。React 应用可直接用 `@blade-hq/agent-react` 的 `useReplay()`。
210
+
133
211
  ### 工作区文件
134
212
 
135
213
  ```ts
@@ -148,13 +226,41 @@ await chat.countFiles() // 递归统计,不含目录
148
226
  await chat.downloadFile("输出/简历.md", "简历.md") // 浏览器下载
149
227
  ```
150
228
 
229
+ 对符合评价条件的最新整体结果,可读取或更新当前用户的反馈:
230
+
231
+ ```ts
232
+ const feedback = await client.sessions.listResultFeedback(id)
233
+ await client.sessions.putResultFeedback(id, assistantEntryId, {
234
+ helpful: false,
235
+ reason: "incomplete",
236
+ })
237
+ ```
238
+
239
+ `ResultFeedbackReason` 是固定枚举;Server 会校验结果资格、写权限,并自行推导 Chat / 软件工厂场景和项目 ID。
240
+
151
241
  ### 模型目录
152
242
 
153
243
  ```ts
154
- const { default: defaultModel, models } = await client.models.list()
155
- // models: Array<{ id, label }>
244
+ const { default: defaultModel, models, baseUrl, defaultServiceModel } = await client.models.list()
245
+ // models: Array<{ id, label, serviceModelId }>
246
+ // baseUrl: 平台默认模型服务的 OpenAI 兼容地址,自建应用带用户令牌可直接调;
247
+ // 各部署端口不同,别写死,老版本 Server 返回空串。
248
+ ```
249
+
250
+ **建会话选模型用 `default` / `id`,直接调 `baseUrl` 用 `defaultServiceModel` / `serviceModelId`。**
251
+ 两套名字不通用:`id` 是平台内部标识,平台接了多个模型服务时形如
252
+ `provider-xxx::deepseek-v4-flash`,拿它去调 `baseUrl` 只会得到「模型不存在」。
253
+ `serviceModelId` 为空串表示这个模型不在 `baseUrl` 那个服务上,做模型选择器时按它过滤。
254
+
255
+ ```ts
256
+ // 自己调模型服务
257
+ const body = { model: defaultServiceModel, messages, stream: true }
156
258
  ```
157
259
 
260
+ `baseUrl` 是**服务端视角**的地址。平台常配成 `http://127.0.0.1:30000/v1`——从自己后端转发
261
+ 没问题,直接交给浏览器就指向用户自己的机器了。默认形态是后端透传(密钥本来也不该进浏览器),
262
+ 浏览器直连只适合这个地址对浏览器同样可达的场景。
263
+
158
264
  ## AgentSession
159
265
 
160
266
  一个会话的实时状态机。**状态归属实例**:同一页面建多个会话互不干扰。
@@ -171,6 +277,8 @@ chat.getState()
171
277
  // mode, "planning" | "executing" | null
172
278
  // connection, 连接状态:"connected" | "connecting" | "reconnecting" | "disconnected"
173
279
  // errorMessage, 最近一次运行错误
280
+ // replay, 回放状态:{ isReplay, speed, sourceSessionId } | null
281
+ // viewerRole, "owner" | "viewer" | null(只读身份改不动会话)
174
282
  // turns, askAnswers, agentLoops, activeCompaction 进阶字段
175
283
  // }
176
284
 
@@ -365,6 +473,29 @@ function Message({ message }: { message: ChatMessage }) {
365
473
  }
366
474
  ```
367
475
 
476
+ ### 让用户挑一个技能来做事
477
+
478
+ 自己实现技能选择器时,用 `transformSlashCommand` 把选中的技能翻译成智能体能执行的一段话:
479
+
480
+ ```ts
481
+ import { transformSlashCommand, type SkillMentionAvailability } from "@blade-hq/agent-client"
482
+
483
+ transformSlashCommand("org/data-analysis", "分析这份季度报表")
484
+ // "请使用 org/data-analysis skill 完成任务\n分析这份季度报表"
485
+ ```
486
+
487
+ 第三个参数说明这个技能眼下能不能直接用。技能装没装、加载没加载,用户不需要知道,但智能体需要——传进来之后它会被翻译成对应的 CLI 步骤:
488
+
489
+ ```ts
490
+ // Blade Hub 上已安装、但当前会话还没加载:要求先 use
491
+ transformSlashCommand(skillId, prompt, { local: false, installed: true })
492
+
493
+ // Blade Hub 上还没安装:要求先 install 再 use
494
+ transformSlashCommand(skillId, prompt, { local: false, installed: false })
495
+ ```
496
+
497
+ 不传第三个参数时按本地已有处理,和不带这个参数的老用法结果一致。
498
+
368
499
  ## 常见问题
369
500
 
370
501
  | 现象 | 原因与解法 |
@@ -385,10 +516,11 @@ function Message({ message }: { message: ChatMessage }) {
385
516
  - **SDK 身份**:`SDK_NAME`、`SDK_VERSION`
386
517
  - **声明式会话**:`SessionDefinition`、`SolutionDefinition`、`SkillDefinition`、`SessionConfig`、`TextFile`、`SessionSetupError`、`SessionSetupStage`
387
518
  - **模型目录**:`ModelsResource`、`ModelCatalog`、`ModelOption`
388
- - **会话资源(REST)**:`SessionsResource`、`CreateSessionRequest`、`ImportSessionOptions`、`AppCliDefinition`、`AppCliAttachment`、`AttachAppOptions`、`PaginatedSessionsResult`、`SessionHistory`、`SessionContextStats`、`ShareLinkResult`、`FileEntry`、`UploadFileEntry`、`UploadFilesOptions`、`SessionProfile`、`SessionDetail`、`SessionInfo`、`SessionStatus`、`SessionPortMapping`、`ModeId`、`TemplateId`、`PrimarySkillSnapshot`、`PrimarySkillParallelMode`
519
+ - **会话资源(REST)**:`SessionsResource`、`CreateSessionRequest`、`ImportSessionOptions`、`AppCliDefinition`、`AppCliAttachment`、`AttachAppOptions`、`PaginatedSessionsResult`、`GlobalSearchResult`、`GlobalSearchResultItem`、`GlobalSearchConversationResult`、`GlobalSearchFileResult`、`SessionHistory`、`SessionContextStats`、`ResultFeedback`、`ResultFeedbackReason`、`ShareLinkResult`、`FileEntry`、`UploadFileEntry`、`UploadFilesOptions`、`SessionProfile`、`SessionDetail`、`SessionInfo`、`SessionStatus`、`SessionPortMapping`、`ModeId`、`TemplateId`、`PrimarySkillSnapshot`、`PrimarySkillParallelMode`
520
+ - **会话回放**:`ReplayState`、`ReplaySpeed`、`ReplayPreview`、`ReplaySnapshot`、`toReplaySnapshot`、`DEFAULT_REPLAY_SPEED`
389
521
  - **会话状态机**:`SessionHub`、`SessionState`、`SendOptions`、`ConnectionStatus`、`AskUserAnswerData`、`AgentLoopInfo`、`ActiveCompactionState`、`createInitialSessionState`、`AgentSessionEventName`
390
522
  - **页面协作**:`EmbeddedChat`、`EmbeddedChatOptions`、`CommandHandler`、`CommandEnvelope`、`InboundAction`、`InboundEnvelope`、`isCommandEnvelope`、`isInboundEnvelope`
391
- - **消息与投影协议**:`MessageContent`、`MessageContentPart`、`TextContentPart`、`ImageUrlContentPart`、`FileContentPart`、`ToolCallInfo`、`ToolBridgeContent`、`CompactionInfo`、`MemoryRefInfo`、`ArchivedFileInfo`、`ArchivedToolCallInfo`、`TurnProjection`、`ContentBlock`、`PatchEnvelope`、`MemoryRef`、`buildMessageContent`、`normalizeMessageContent`、`isHiddenInternalMessage`、`transformSlashCommand`、`extractTextAttachments`、`ParsedTextAttachment`、`ParsedTextContext`
523
+ - **消息与投影协议**:`MessageContent`、`MessageContentPart`、`TextContentPart`、`ImageUrlContentPart`、`FileContentPart`、`ToolCallInfo`、`ToolBridgeContent`、`CompactionInfo`、`MemoryRefInfo`、`ArchivedFileInfo`、`ArchivedToolCallInfo`、`TurnProjection`、`ContentBlock`、`PatchEnvelope`、`MemoryRef`、`PostChatFollowup`、`FinalArtifact`、`latestPostChatFollowup`、`buildMessageContent`、`normalizeMessageContent`、`isHiddenInternalMessage`、`transformSlashCommand`、`SkillMentionAvailability`、`extractTextAttachments`、`ParsedTextAttachment`、`ParsedTextContext`
392
524
  - **Solution / 任务协议**:`Solution`、`SolutionAppField`、`SolutionAppState`、`SolutionAppUiConfig`、`SolutionRef`、`PublishedSolutionRef`、`ExistingSolutionRef`、`PreparedSolution`、`PreparedSolutionAsset`、`LayoutType`、`BizRole`、`TaskStatus`、`BackgroundTask`、`BackgroundTaskStopResult`
393
525
  - **Headless**:`HeadlessResource`、`RunOptions`、`RunResult`、`RunTrace`
394
- - **低层通道(apps/web 等高级集成)**:`createSocket`、`CreateSocketOptions`、`TypedSocket`、`AsrAudioPayload`、`ClientProjectionBuilder`、`RawEvent`
526
+ - **低层通道(apps/web 等高级集成)**:`createSocket`、`CreateSocketOptions`、`TypedSocket`、`AsrAudioPayload`、`ClientProjectionBuilder`、`RawEvent`、`acceptedPostChatFollowupCompletesLatestRun`、`reconcileOptimisticUserTurns`
@@ -23,6 +23,8 @@ export interface BladeClientOptions {
23
23
  tokenStorage?: TokenStorageMode;
24
24
  fetchImpl?: typeof fetch;
25
25
  onRefreshSuccess?: () => void | Promise<void>;
26
+ /** 是否订阅逐 token 增量,默认 true。关闭后仍接收完整消息和语义事件。 */
27
+ streamTokens?: boolean;
26
28
  }
27
29
  export declare class BladeClient {
28
30
  private refreshPromise;
package/dist/index.d.ts CHANGED
@@ -9,30 +9,32 @@ export { SessionHub } from "./session/hub";
9
9
  export type { AgentSessionEvents, AgentSessionEventName } from "./session/events";
10
10
  export { SessionSetupError } from "./session/definition";
11
11
  export type { SessionConfig, SessionDefinition, SessionSetupStage, SkillDefinition, TextFile, SolutionDefinition, } from "./session/definition";
12
- export { createInitialSessionState } from "./session/state";
13
- export type { AskUserAnswerData, AgentLoopInfo, ActiveCompactionState, ConnectionStatus, SessionState, } from "./session/state";
12
+ export { createInitialSessionState, reconcileOptimisticUserTurns, toReplaySnapshot, } from "./session/state";
13
+ export type { AskUserAnswerData, AgentLoopInfo, ActiveCompactionState, ConnectionStatus, ReplaySnapshot, SessionState, } from "./session/state";
14
14
  export { connectEmbedded } from "./commands/embedded";
15
15
  export type { EmbeddedChat, EmbeddedChatOptions } from "./commands/embedded";
16
16
  export type { CommandHandler } from "./commands/registry";
17
17
  export type { CommandEnvelope, InboundAction, InboundEnvelope } from "./commands/protocol";
18
18
  export { isCommandEnvelope, isInboundEnvelope } from "./commands/protocol";
19
- export type { AuthResource, ProvidersResponse, UserInfo } from "./resources/auth";
19
+ export type { AuthResource, ExchangeCodeParams, ExchangeCodeResult, ProvidersResponse, UserInfo, } from "./resources/auth";
20
20
  export type { HeadlessResource } from "./resources/headless";
21
21
  export { ModelsResource } from "./resources/models";
22
22
  export type { ModelCatalog, ModelOption } from "./resources/models";
23
23
  export type { SessionsResource } from "./resources/sessions";
24
- export type { CreateSessionRequest, AppCliAttachment, AppCliDefinition, FileEntry, ImportSessionOptions, PaginatedSessionsResult, SessionContextStats, SessionHistory, ShareLinkResult, UploadFileEntry, UploadFilesOptions, } from "./resources/sessions";
24
+ export type { CreateSessionRequest, AppCliAttachment, AppCliDefinition, FileEntry, ImportSessionOptions, PaginatedSessionsResult, GlobalSearchConversationResult, GlobalSearchFileResult, GlobalSearchResult, GlobalSearchResultItem, ResultFeedback, ResultFeedbackReason, SessionContextStats, SessionHistory, ShareLinkResult, UploadFileEntry, UploadFilesOptions, } from "./resources/sessions";
25
25
  export type { ArchivedFileInfo, ArchivedToolCallInfo, ChatMessage, CompactionInfo, FileContentPart, ImageUrlContentPart, MemoryRefInfo, MessageContent, MessageContentPart, TextContentPart, ToolBridgeContent, ToolCallInfo, } from "./schemas/message";
26
26
  export { buildMessageContent, contentPreview, extractTextAttachments, getFileParts, getImageParts, getTextContent, groupMessagesByLoop, isHiddenInternalMessage, normalizeMessageContent, transformSlashCommand, } from "./schemas/message-utils";
27
- export type { ParsedTextAttachment, ParsedTextContext } from "./schemas/message-utils";
28
- export type { ContentBlock, MemoryRef, PatchEnvelope, TurnProjection } from "./schemas/projection";
27
+ export type { ParsedTextAttachment, ParsedTextContext, SkillMentionAvailability, } from "./schemas/message-utils";
28
+ export type { ContentBlock, FinalArtifact, MemoryRef, PatchEnvelope, PostChatFollowup, TurnProjection, } from "./schemas/projection";
29
+ export { latestPostChatFollowup } from "./schemas/projection-utils";
30
+ export { DEFAULT_REPLAY_SPEED } from "./schemas/session";
29
31
  export { SessionInfo, SessionStatus } from "./schemas/session";
30
- export type { ModeId, PrimarySkillParallelMode, PrimarySkillSnapshot, SessionDetail, SessionPortMapping, PublishedSolutionRef, TemplateId, } from "./schemas/session";
32
+ export type { ModeId, PrimarySkillParallelMode, PrimarySkillSnapshot, ReplayPreview, ReplaySpeed, ReplayState, SessionDetail, SessionPortMapping, PublishedSolutionRef, TemplateId, } from "./schemas/session";
31
33
  export { LayoutType } from "./schemas/solution";
32
34
  export type { BizRole, Solution, SolutionAppField, SolutionAppState, SolutionAppUiConfig, } from "./schemas/solution";
33
35
  export { Task, TaskStatus } from "./schemas/task";
34
36
  export type { BackgroundTask, BackgroundTaskStopResult } from "./schemas/background";
35
- export { ClientProjectionBuilder } from "./shared/projection";
37
+ export { acceptedPostChatFollowupCompletesLatestRun, ClientProjectionBuilder } from "./shared/projection";
36
38
  export type { RawEvent } from "./shared/projection";
37
39
  export { createSocket } from "./socket";
38
40
  export type { CreateSocketOptions, TypedSocket } from "./socket";