@blade-hq/agent-client 2610.0.0-beta.0 → 2610.0.0-beta.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 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
 
@@ -133,6 +134,7 @@ Solution 和 Skill 都声明为 `{ path, content }[]` 文本文件;SDK 负责
133
134
 
134
135
  ```ts
135
136
  await client.sessions.listSessions() // 会话列表
137
+ await client.sessions.searchOwnedContent({ q: "项目报告" }) // 搜索自己的对话和文件名
136
138
  await client.sessions.getSession(id) // 会话详情
137
139
  await client.sessions.updateSession(id, { intent: "新标题" })
138
140
  await client.sessions.deleteSession(id)
@@ -224,6 +226,18 @@ await chat.countFiles() // 递归统计,不含目录
224
226
  await chat.downloadFile("输出/简历.md", "简历.md") // 浏览器下载
225
227
  ```
226
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
+
227
241
  ### 模型目录
228
242
 
229
243
  ```ts
@@ -502,11 +516,11 @@ transformSlashCommand(skillId, prompt, { local: false, installed: false })
502
516
  - **SDK 身份**:`SDK_NAME`、`SDK_VERSION`
503
517
  - **声明式会话**:`SessionDefinition`、`SolutionDefinition`、`SkillDefinition`、`SessionConfig`、`TextFile`、`SessionSetupError`、`SessionSetupStage`
504
518
  - **模型目录**:`ModelsResource`、`ModelCatalog`、`ModelOption`
505
- - **会话资源(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`
506
520
  - **会话回放**:`ReplayState`、`ReplaySpeed`、`ReplayPreview`、`ReplaySnapshot`、`toReplaySnapshot`、`DEFAULT_REPLAY_SPEED`
507
521
  - **会话状态机**:`SessionHub`、`SessionState`、`SendOptions`、`ConnectionStatus`、`AskUserAnswerData`、`AgentLoopInfo`、`ActiveCompactionState`、`createInitialSessionState`、`AgentSessionEventName`
508
522
  - **页面协作**:`EmbeddedChat`、`EmbeddedChatOptions`、`CommandHandler`、`CommandEnvelope`、`InboundAction`、`InboundEnvelope`、`isCommandEnvelope`、`isInboundEnvelope`
509
- - **消息与投影协议**:`MessageContent`、`MessageContentPart`、`TextContentPart`、`ImageUrlContentPart`、`FileContentPart`、`ToolCallInfo`、`ToolBridgeContent`、`CompactionInfo`、`MemoryRefInfo`、`ArchivedFileInfo`、`ArchivedToolCallInfo`、`TurnProjection`、`ContentBlock`、`PatchEnvelope`、`MemoryRef`、`buildMessageContent`、`normalizeMessageContent`、`isHiddenInternalMessage`、`transformSlashCommand`、`SkillMentionAvailability`、`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`
510
524
  - **Solution / 任务协议**:`Solution`、`SolutionAppField`、`SolutionAppState`、`SolutionAppUiConfig`、`SolutionRef`、`PublishedSolutionRef`、`ExistingSolutionRef`、`PreparedSolution`、`PreparedSolutionAsset`、`LayoutType`、`BizRole`、`TaskStatus`、`BackgroundTask`、`BackgroundTaskStopResult`
511
525
  - **Headless**:`HeadlessResource`、`RunOptions`、`RunResult`、`RunTrace`
512
- - **低层通道(apps/web 等高级集成)**:`createSocket`、`CreateSocketOptions`、`TypedSocket`、`AsrAudioPayload`、`ClientProjectionBuilder`、`RawEvent`
526
+ - **低层通道(apps/web 等高级集成)**:`createSocket`、`CreateSocketOptions`、`TypedSocket`、`AsrAudioPayload`、`ClientProjectionBuilder`、`RawEvent`、`acceptedPostChatFollowupCompletesLatestRun`
@@ -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
@@ -21,11 +21,12 @@ 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
27
  export type { ParsedTextAttachment, ParsedTextContext, SkillMentionAvailability, } from "./schemas/message-utils";
28
- export type { ContentBlock, MemoryRef, PatchEnvelope, TurnProjection } from "./schemas/projection";
28
+ export type { ContentBlock, FinalArtifact, MemoryRef, PatchEnvelope, PostChatFollowup, TurnProjection, } from "./schemas/projection";
29
+ export { latestPostChatFollowup } from "./schemas/projection-utils";
29
30
  export { DEFAULT_REPLAY_SPEED } from "./schemas/session";
30
31
  export { SessionInfo, SessionStatus } from "./schemas/session";
31
32
  export type { ModeId, PrimarySkillParallelMode, PrimarySkillSnapshot, ReplayPreview, ReplaySpeed, ReplayState, SessionDetail, SessionPortMapping, PublishedSolutionRef, TemplateId, } from "./schemas/session";
@@ -33,7 +34,7 @@ export { LayoutType } from "./schemas/solution";
33
34
  export type { BizRole, Solution, SolutionAppField, SolutionAppState, SolutionAppUiConfig, } from "./schemas/solution";
34
35
  export { Task, TaskStatus } from "./schemas/task";
35
36
  export type { BackgroundTask, BackgroundTaskStopResult } from "./schemas/background";
36
- export { ClientProjectionBuilder } from "./shared/projection";
37
+ export { acceptedPostChatFollowupCompletesLatestRun, ClientProjectionBuilder } from "./shared/projection";
37
38
  export type { RawEvent } from "./shared/projection";
38
39
  export { createSocket } from "./socket";
39
40
  export type { CreateSocketOptions, TypedSocket } from "./socket";
package/dist/index.js CHANGED
@@ -350,7 +350,11 @@ var HeadlessResource = class {
350
350
  socket.on("chat:start", onChatStart);
351
351
  socket.on("chat:end", onChatEnd);
352
352
  socket.on("system:error", onSystemError);
353
- socket.emit("session:subscribe", { session_id, event_format: "raw" });
353
+ socket.emit("session:subscribe", {
354
+ session_id,
355
+ event_format: "raw",
356
+ stream_tokens: this.client.options.streamTokens !== false
357
+ });
354
358
  socket.emit(
355
359
  "chat:send",
356
360
  {
@@ -893,7 +897,7 @@ function sourceLoopFor(loopId, loopDescriptions) {
893
897
  if (!description) return null;
894
898
  return { loop_name: loopId, description };
895
899
  }
896
- var MAX_FINAL_ARTIFACTS = 3;
900
+ var MAX_FINAL_ARTIFACTS = 20;
897
901
  function normalizeFinalArtifacts(raw) {
898
902
  if (!Array.isArray(raw)) return [];
899
903
  const artifacts = [];
@@ -927,12 +931,16 @@ function normalizePostChatFollowup(data, assistantEntryId) {
927
931
  }
928
932
  const recaption = String(data.recaption ?? "");
929
933
  const finalArtifacts = normalizeFinalArtifacts(data.final_artifacts);
930
- if (suggestions.length === 0 && !recaption && finalArtifacts.length === 0) return null;
934
+ const feedbackEligible = data.feedback_eligible === true;
935
+ if (suggestions.length === 0 && !recaption && finalArtifacts.length === 0 && !feedbackEligible)
936
+ return null;
931
937
  return {
932
938
  assistant_entry_id: assistantEntryId,
933
939
  recaption,
934
940
  suggestions: suggestions.slice(0, 3),
935
- final_artifacts: finalArtifacts
941
+ final_artifacts: finalArtifacts,
942
+ feedback_eligible: feedbackEligible,
943
+ feedback_scope: "latest_result"
936
944
  };
937
945
  }
938
946
  function normalizeChildPause(pausePayload) {
@@ -1016,11 +1024,60 @@ function toolStatusFromResult(result) {
1016
1024
  }
1017
1025
 
1018
1026
  // src/shared/projection/builder.ts
1027
+ function hasChatRunEvent(events) {
1028
+ return events.some(
1029
+ (event) => event.type !== "session:post_chat_followup" && !event.type.startsWith("compaction:")
1030
+ );
1031
+ }
1032
+ function acceptedPostChatFollowupCompletesLatestRun(events, updates) {
1033
+ const acceptedAssistantEntryIds = new Set(
1034
+ updates.flatMap((update) => {
1035
+ if (update.kind !== "upsert") return [];
1036
+ const followup = update.turn.post_chat_followup;
1037
+ return followup?.assistant_entry_id ? [followup.assistant_entry_id] : [];
1038
+ })
1039
+ );
1040
+ for (let index = events.length - 1; index >= 0; index -= 1) {
1041
+ const event = events[index];
1042
+ if (event?.type !== "session:post_chat_followup") continue;
1043
+ const assistantEntryId = String(event.payload.assistant_entry_id ?? "");
1044
+ return acceptedAssistantEntryIds.has(assistantEntryId) && !hasChatRunEvent(events.slice(index + 1));
1045
+ }
1046
+ return false;
1047
+ }
1019
1048
  function isConversationTurn(turn) {
1020
1049
  return (turn.role === "user" || turn.role === "assistant") && (turn.kind ?? "message") === "message" && !turn.blocks.some(
1021
1050
  (block) => block.type === "mode_change" || block.type === "plan_status" || block.type === "ask_user_answer"
1022
1051
  );
1023
1052
  }
1053
+ function completedResponseText(content) {
1054
+ if (typeof content === "string") return content;
1055
+ if (!Array.isArray(content)) return content == null ? "" : String(content);
1056
+ return content.filter(
1057
+ (part) => typeof part === "object" && part !== null && !Array.isArray(part)
1058
+ ).filter((part) => part.type === "text").map((part) => String(part.text ?? "")).join("");
1059
+ }
1060
+ function replaceTextContent(state, content) {
1061
+ if (state.blocks.some((block) => block.type === "text")) return;
1062
+ const replacement = { type: "text", content };
1063
+ const blocks = [];
1064
+ let inserted = false;
1065
+ for (const block of state.blocks) {
1066
+ if (block.type === "text") {
1067
+ if (content && !inserted) {
1068
+ blocks.push(replacement);
1069
+ inserted = true;
1070
+ }
1071
+ continue;
1072
+ }
1073
+ blocks.push(block);
1074
+ }
1075
+ if (content && !inserted) {
1076
+ const toolIndex = blocks.findIndex((block) => block.type === "tool_use");
1077
+ blocks.splice(toolIndex < 0 ? blocks.length : toolIndex, 0, replacement);
1078
+ }
1079
+ state.blocks = blocks;
1080
+ }
1024
1081
  var ClientProjectionBuilder = class {
1025
1082
  activeTurns = /* @__PURE__ */ new Map();
1026
1083
  lastTurnIds = /* @__PURE__ */ new Map();
@@ -1103,6 +1160,9 @@ var ClientProjectionBuilder = class {
1103
1160
  return this.onDelta(state, "thinking", "", String(payload.content ?? ""));
1104
1161
  case "llm:content:delta":
1105
1162
  return this.onDelta(state, "text", "", String(payload.content ?? ""));
1163
+ case "llm:content:complete":
1164
+ replaceTextContent(state, String(payload.content ?? ""));
1165
+ return this.syncPatch(state);
1106
1166
  case "llm:tool_call:created":
1107
1167
  return this.onToolCallCreated(state, payload);
1108
1168
  case "llm:response:done":
@@ -1392,6 +1452,9 @@ var ClientProjectionBuilder = class {
1392
1452
  if (typeof payload.usage === "object" && payload.usage !== null) {
1393
1453
  state.usage = { ...payload.usage };
1394
1454
  }
1455
+ if ("content" in payload) {
1456
+ replaceTextContent(state, completedResponseText(payload.content));
1457
+ }
1395
1458
  const toolCalls = payload.tool_calls;
1396
1459
  if (Array.isArray(toolCalls)) {
1397
1460
  for (const raw of toolCalls) {
@@ -2195,6 +2258,11 @@ var SessionsResource = class {
2195
2258
  listSessionsWithSkillData() {
2196
2259
  return this.client.json("GET", "/api/sessions/with-skill-data");
2197
2260
  }
2261
+ searchOwnedContent(params) {
2262
+ const searchParams = new URLSearchParams({ q: params.q.trim() });
2263
+ if (params.limit !== void 0) searchParams.set("limit", String(params.limit));
2264
+ return this.client.json("GET", `/api/search?${searchParams.toString()}`);
2265
+ }
2198
2266
  createSession(intent, template_id, primary_skill_id) {
2199
2267
  return this.createSessionWithRequest({ intent, template_id, primary_skill_id });
2200
2268
  }
@@ -2261,6 +2329,16 @@ var SessionsResource = class {
2261
2329
  );
2262
2330
  return projectHistory(entries);
2263
2331
  }
2332
+ listResultFeedback(sessionId) {
2333
+ return this.client.json("GET", `/api/sessions/${encodeURIComponent(sessionId)}/feedback`);
2334
+ }
2335
+ putResultFeedback(sessionId, assistantEntryId, feedback) {
2336
+ return this.client.json(
2337
+ "PUT",
2338
+ `/api/sessions/${encodeURIComponent(sessionId)}/turns/${encodeURIComponent(assistantEntryId)}/feedback`,
2339
+ feedback
2340
+ );
2341
+ }
2264
2342
  getSessionContextStats(sessionId, init) {
2265
2343
  return this.client.jsonFromInit(`/api/sessions/${sessionId}/context-stats`, init);
2266
2344
  }
@@ -3229,7 +3307,7 @@ function extractTerminalChatEndStatus(events) {
3229
3307
  }
3230
3308
  return null;
3231
3309
  }
3232
- function hasChatRunEvent(events) {
3310
+ function hasChatRunEvent2(events) {
3233
3311
  return events.some(
3234
3312
  (event) => event.type !== "session:post_chat_followup" && !event.type.startsWith("compaction:")
3235
3313
  );
@@ -3281,6 +3359,7 @@ var AgentSession = class _AgentSession {
3281
3359
  const session = new _AgentSession(
3282
3360
  sessionId,
3283
3361
  {
3362
+ streamTokens: false,
3284
3363
  emitEvent: readonlyError,
3285
3364
  emitWithAck: async () => readonlyError(),
3286
3365
  ensureConnected: readonlyError,
@@ -3654,7 +3733,7 @@ var AgentSession = class _AgentSession {
3654
3733
  _handleTurnEvents(events) {
3655
3734
  if (!events.length) return;
3656
3735
  const terminalChatEndStatus = extractTerminalChatEndStatus(events);
3657
- if (!terminalChatEndStatus && hasChatRunEvent(events)) {
3736
+ if (!terminalChatEndStatus && hasChatRunEvent2(events)) {
3658
3737
  this.markRunningIfIdle();
3659
3738
  }
3660
3739
  if (this.state.turns.length) {
@@ -3668,8 +3747,8 @@ var AgentSession = class _AgentSession {
3668
3747
  this.emitter.emit("workspaceChanged", {});
3669
3748
  }
3670
3749
  }
3671
- if (terminalChatEndStatus) {
3672
- this.finishStreaming(terminalChatEndStatus);
3750
+ if (terminalChatEndStatus || acceptedPostChatFollowupCompletesLatestRun(events, updates)) {
3751
+ this.completeChat(terminalChatEndStatus ?? "completed");
3673
3752
  }
3674
3753
  }
3675
3754
  /** @internal */
@@ -3679,10 +3758,7 @@ var AgentSession = class _AgentSession {
3679
3758
  /** @internal */
3680
3759
  _handleChatEnd(status) {
3681
3760
  this.flushPatches();
3682
- this.finishStreaming(status);
3683
- this.recentChatEndAt = Date.now();
3684
- void this.syncTurnsFromHistory();
3685
- this.emitter.emit("chatEnd", { status });
3761
+ this.completeChat(status);
3686
3762
  }
3687
3763
  /** @internal */
3688
3764
  _handleSystemError(message) {
@@ -3826,6 +3902,12 @@ var AgentSession = class _AgentSession {
3826
3902
  return next;
3827
3903
  });
3828
3904
  }
3905
+ completeChat(status) {
3906
+ this.finishStreaming(status);
3907
+ this.recentChatEndAt = Date.now();
3908
+ void this.syncTurnsFromHistory();
3909
+ this.emitter.emit("chatEnd", { status });
3910
+ }
3829
3911
  applyTurn(turn) {
3830
3912
  this.flushPatches();
3831
3913
  const previous = this.state;
@@ -4008,7 +4090,12 @@ var AgentSession = class _AgentSession {
4008
4090
  if (this.joinPending) return this.joinPending;
4009
4091
  const join = this.runtime.emitWithAck(
4010
4092
  "session:subscribe",
4011
- { session_id: this.sessionId, event_format: "raw", resume: true },
4093
+ {
4094
+ session_id: this.sessionId,
4095
+ event_format: "raw",
4096
+ resume: true,
4097
+ stream_tokens: this.runtime.streamTokens
4098
+ },
4012
4099
  5e3
4013
4100
  ).then((response) => {
4014
4101
  if (response && response.ok === false) {
@@ -4144,6 +4231,7 @@ var SessionHub = class {
4144
4231
  }
4145
4232
  runtimeFor() {
4146
4233
  return {
4234
+ streamTokens: this.client.options?.streamTokens !== false,
4147
4235
  emitEvent: (event, data) => {
4148
4236
  ;
4149
4237
  this.client.socket().emit(event, data);
@@ -4383,7 +4471,7 @@ function resolveAuthToken(options) {
4383
4471
 
4384
4472
  // src/version.ts
4385
4473
  var SDK_NAME = "agent-client";
4386
- var SDK_VERSION = true ? "2610.0.0-beta.0" : "1.1.1";
4474
+ var SDK_VERSION = true ? "2610.0.0-beta.2" : "1.1.1";
4387
4475
 
4388
4476
  // src/socket.ts
4389
4477
  function withSdkIdentity(auth) {
@@ -4835,6 +4923,19 @@ function connectEmbedded(options) {
4835
4923
  };
4836
4924
  }
4837
4925
 
4926
+ // src/schemas/projection-utils.ts
4927
+ function latestPostChatFollowup(turns) {
4928
+ for (let index = turns.length - 1; index >= 0; index -= 1) {
4929
+ const turn = turns[index];
4930
+ if (turn.loop_id === "root" && (turn.role === "user" || turn.role === "assistant") && (turn.kind ?? "message") === "message" && !turn.blocks.some(
4931
+ (block) => ["mode_change", "plan_status", "ask_user_answer"].includes(block.type)
4932
+ )) {
4933
+ return turn.role === "assistant" ? turn.post_chat_followup ?? null : null;
4934
+ }
4935
+ }
4936
+ return null;
4937
+ }
4938
+
4838
4939
  // src/schemas/solution.ts
4839
4940
  var LayoutType = /* @__PURE__ */ ((LayoutType2) => {
4840
4941
  LayoutType2["Default"] = "default";
@@ -4877,6 +4978,7 @@ export {
4877
4978
  SessionStatus,
4878
4979
  Task,
4879
4980
  TaskStatus,
4981
+ acceptedPostChatFollowupCompletesLatestRun,
4880
4982
  buildMessageContent,
4881
4983
  connectEmbedded,
4882
4984
  contentPreview,
@@ -4890,6 +4992,7 @@ export {
4890
4992
  isCommandEnvelope,
4891
4993
  isHiddenInternalMessage,
4892
4994
  isInboundEnvelope,
4995
+ latestPostChatFollowup,
4893
4996
  normalizeMessageContent,
4894
4997
  toReplaySnapshot,
4895
4998
  transformSlashCommand