@blade-hq/agent-client 2610.0.0-beta.80 → 2610.0.0-beta.81

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
@@ -478,7 +478,7 @@ await chat.refreshQueue() // 手动拉一份权威快照(首次连接与每
478
478
 
479
479
  - **冲突处理**:`conflict` 表示乐观并发或状态校验失败,服务端快照已自动刷新到本地,此时保留用户草稿并说明原因即可;`ok=false` 且 `snapshot` 为 null(例如鉴权失败)时不要改动本地队列。`queueMessage` 结果未知(ack 超时)时会用同一个 `client_request_id` 重试一次,不会制造第二条。
480
480
  - **可操作性判定**:`canEditQueuedMessage` / `canCancelQueuedMessage` / `canDeliverQueuedMessage` 只对 `pending` 返回 true;文案用 `queuedMessageStatusLabel` 与 `queuePauseReasonLabel`。
481
- - **自己处理广播**:需要绕过状态机时用 `isSessionQueueSnapshot` + `shouldApplySnapshot` + `EMPTY_SESSION_QUEUE`;解析自定义 ack 用 `parseQueueAck`,返回统一的 `QueueOperationResult`(`QueueAck` 是 ack 的线格式)。
481
+ - **自己处理广播**:需要绕过状态机时用 `isSessionQueueSnapshot` + `shouldApplySnapshot` + `EMPTY_SESSION_QUEUE`;解析自定义 ack 用 `parseQueueAck`,返回统一的 `QueueOperationResult`(`QueueAck` 是 ack 的线格式;`code` 是路由失败原因,供调用方决定是否重试)。
482
482
 
483
483
  ### 页面协作:让智能体和你的页面互动
484
484
 
@@ -544,9 +544,11 @@ chat.on("toolCall", (e) => console.log("调用工具", e.toolCall.name))
544
544
  chat.on("toolResult", (e) => console.log("工具结果", e.toolCall.result))
545
545
  chat.on("message", (e) => console.log("新消息", e.message))
546
546
  chat.on("chatEnd", (e) => console.log("回复结束", e.status))
547
- chat.on("error", (e) => console.error(e.message))
547
+ chat.on("error", (e) => console.error(e.message, e.code))
548
548
  ```
549
549
 
550
+ `error` 的 `message` 是可以直接展示的业务文案;`code` 是服务端给的机器可读原因(如 `owner_timeout` / `route_broken` / `owner_busy`),调用方据此决定要不要提示重试,不要解析 `message`。同一组原因也以 `AgentSessionError.code` 的形式抛出(`send()` 这类只回布尔值的入口从 `error` 事件拿),队列 API 则通过 `QueueOperationResult.code` 给出。
551
+
550
552
  完整事件表见 `AgentSessionEvents` 类型定义(含 `modeChange` / `workspaceChanged` / `artifact` / `notification` / `backgroundTask` / `taskListUpdated` / `rewind` / `replayMismatch` 等)。`toolResult.source` 区分实时结果、首次连接回放和断线重连回放;`on()` 返回取消函数,handler 抛异常只告警、不影响会话。
551
553
 
552
554
  分页响应是 `SessionTurnsPage`,其中 `nextBefore: string | null` 是唯一的“还有更早历史”真值。历史页面指令通过独立的 `HistoricalCommand` 日志恢复,不混进展示页。自定义状态容器可复用 `prependOlder`、`replaceWindow` 与 `LiveRevisionState`,但一般直接使用 `AgentSession` 即可。
@@ -739,6 +741,7 @@ HTML 一律判无效);`isInternalStatusToolUiCard` 识别内部「阶段进
739
741
  - **客户端与登录**:`BladeClientOptions`、`LoginOptions`、`LoginResult`、`TokenStorageMode`、`UploadProgress`、`BladeApiError`、`AuthResource`、`ProvidersResponse`、`UserInfo`
740
742
  - **SDK 身份**:`SDK_NAME`、`SDK_VERSION`
741
743
  - **声明式会话**:`SessionDefinition`、`SolutionDefinition`、`SkillDefinition`、`SessionConfig`、`TextFile`、`SessionSetupError`、`SessionSetupStage`
744
+ - **会话错误**:`AgentSessionError`(`code` 是服务端路由失败原因,供调用方决定是否重试)
742
745
  - **模型目录**:`ModelsResource`、`ModelCatalog`、`ModelOption`
743
746
  - **聊天分组资源(REST)**:`ChatGroupsResource`、`ChatGroup`(含 `delete` 永久删除);`client.chatGroups`。旧名 `ChatProjectsResource`、`ChatProject`、`client.chatProjects` 与 `chat_project_id` 字段已随分组改名一并移除,接入方请改用 `chatGroups` 与 `chat_group_id`
744
747
  - **插件目录(REST,无会话)**:`PluginsResource`、`PluginCatalogEntry`(`client.plugins`)。首页这类还没有会话的界面用它读名称、业务名与图标 token
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export type { LoadPlatformEndpointsOptions, PlatformEndpoints, PlatformServiceNa
9
9
  export { SDK_NAME, SDK_VERSION } from "./version";
10
10
  export { AgentSession } from "./session/agent-session";
11
11
  export type { AttachAppOptions, SendOptions } from "./session/agent-session";
12
+ export { AgentSessionError } from "./session/errors";
12
13
  export { SessionHub } from "./session/hub";
13
14
  export type { SessionConnectOptions } from "./session/hub";
14
15
  export type { AgentSessionEvents, AgentSessionEventName } from "./session/events";
package/dist/index.js CHANGED
@@ -3591,18 +3591,37 @@ function parseQueueAck(response, fallbackSessionId) {
3591
3591
  return { ok: false, conflict: false, snapshot: null, message: QUEUE_FAILURE_MESSAGE };
3592
3592
  }
3593
3593
  const message = typeof response.message === "string" && response.message ? response.message : null;
3594
+ const code = typeof response.code === "string" && response.code ? response.code : void 0;
3594
3595
  const status = response.status;
3595
3596
  if (status !== "ok" && status !== "conflict" && status !== "queued") {
3596
- return { ok: false, conflict: false, snapshot: null, message: message ?? QUEUE_FAILURE_MESSAGE };
3597
+ return {
3598
+ ok: false,
3599
+ conflict: false,
3600
+ snapshot: null,
3601
+ message: message ?? QUEUE_FAILURE_MESSAGE,
3602
+ ...code ? { code } : {}
3603
+ };
3597
3604
  }
3598
3605
  const snapshot2 = toQueueSnapshot(response, fallbackSessionId);
3599
3606
  if (status === "conflict") {
3600
- return { ok: false, conflict: true, snapshot: snapshot2, message: message ?? QUEUE_FAILURE_MESSAGE };
3607
+ return {
3608
+ ok: false,
3609
+ conflict: true,
3610
+ snapshot: snapshot2,
3611
+ message: message ?? QUEUE_FAILURE_MESSAGE,
3612
+ ...code ? { code } : {}
3613
+ };
3601
3614
  }
3602
3615
  if (!snapshot2) {
3603
- return { ok: false, conflict: false, snapshot: null, message: message ?? QUEUE_FAILURE_MESSAGE };
3616
+ return {
3617
+ ok: false,
3618
+ conflict: false,
3619
+ snapshot: null,
3620
+ message: message ?? QUEUE_FAILURE_MESSAGE,
3621
+ ...code ? { code } : {}
3622
+ };
3604
3623
  }
3605
- return { ok: true, conflict: false, snapshot: snapshot2, message };
3624
+ return { ok: true, conflict: false, snapshot: snapshot2, message, ...code ? { code } : {} };
3606
3625
  }
3607
3626
  function canEditQueuedMessage(item) {
3608
3627
  return item.status === "pending";
@@ -4427,6 +4446,16 @@ function markStreamingTurns(state, turnStatus, toolCallStatus) {
4427
4446
  return withTurns(state, turns);
4428
4447
  }
4429
4448
 
4449
+ // src/session/errors.ts
4450
+ var AgentSessionError = class extends Error {
4451
+ name = "AgentSessionError";
4452
+ code;
4453
+ constructor(message, code) {
4454
+ super(message);
4455
+ this.code = code;
4456
+ }
4457
+ };
4458
+
4430
4459
  // src/session/agent-session.ts
4431
4460
  var OOM_MESSAGE = "\u6C99\u76D2\u5185\u5B58\u4F7F\u7528\u8D85\u51FA\u9650\u5236\uFF0C\u5DF2\u81EA\u52A8\u91CD\u542F\u3002\u5982\u679C\u7ECF\u5E38\u89E6\u53D1\uFF0C\u53EF\u4EE5\u8054\u7CFB\u7BA1\u7406\u5458\u8C03\u6574\u989D\u5EA6\u3002";
4432
4461
  var COMMAND_RESTORE_RETRY_BASE_MS = 1e3;
@@ -4444,6 +4473,16 @@ var OOM_KEYWORDS_RE = /(?:\b(?:exit_code|ExitedWith|exited with)|退出码)\D*(?
4444
4473
  function isOomText(value) {
4445
4474
  return typeof value === "string" && OOM_KEYWORDS_RE.test(value);
4446
4475
  }
4476
+ function describeSessionError(error, fallback) {
4477
+ if (error instanceof AgentSessionError) {
4478
+ return {
4479
+ message: error.message || fallback,
4480
+ ...error.code ? { code: error.code } : {}
4481
+ };
4482
+ }
4483
+ if (error instanceof Error && error.message) return { message: error.message };
4484
+ return { message: fallback };
4485
+ }
4447
4486
  function parseJsonRecord(value) {
4448
4487
  if (isRecord(value)) return value;
4449
4488
  if (typeof value !== "string") return null;
@@ -4479,11 +4518,12 @@ function isCursorInvalidError(error) {
4479
4518
  return typeof error === "object" && error !== null && "status" in error && error.status === 409;
4480
4519
  }
4481
4520
  function queueRequestFailure(error) {
4521
+ const failure = error instanceof AgentSessionError ? { message: error.message, ...error.code ? { code: error.code } : {} } : { message: error instanceof Error && error.message ? error.message : "\u961F\u5217\u64CD\u4F5C\u5931\u8D25" };
4482
4522
  return {
4483
4523
  ok: false,
4484
4524
  conflict: false,
4485
4525
  snapshot: null,
4486
- message: error instanceof Error && error.message ? error.message : "\u961F\u5217\u64CD\u4F5C\u5931\u8D25"
4526
+ ...failure
4487
4527
  };
4488
4528
  }
4489
4529
  function commandDeliveryKey(deliveryId) {
@@ -4551,6 +4591,20 @@ var AgentSession = class _AgentSession {
4551
4591
  pendingReplayMode;
4552
4592
  pendingReplayKbIds;
4553
4593
  recentChatEndAt = 0;
4594
+ /**
4595
+ * 最近一次已经通过 `error` 事件投递过的服务端失败(`message` + `code`)。
4596
+ *
4597
+ * `chat:send` 被代理时服务端会先发 `system:error`、再用同一条失败回 ack,两条
4598
+ * 通道都会到达这里:前者走 `_handleSystemError`,后者走发送失败的回滚。不去重
4599
+ * 的话调用方会收到两次同一个 `error`(重复提示、重试逻辑跑两遍)。
4600
+ *
4601
+ * 带代际边界,只认「本次 `emitWithAck` 等待期间」投递的那一条:否则队列/停止
4602
+ * 失败留下的标记会一直挂着,之后一次无关的订阅失败只是文案码相同就被当成
4603
+ * 重复而静音。用单调递增的代际号而不是时间戳,同一毫秒内的两条也不会混淆。
4604
+ */
4605
+ lastDeliveredSystemError = null;
4606
+ /** 每次投递 `system:error` 递增,用于判定某条失败是否属于本次等待。 */
4607
+ systemErrorGeneration = 0;
4554
4608
  subscriptionReplaySource = null;
4555
4609
  disposed = false;
4556
4610
  getAppContext = null;
@@ -4965,16 +5019,19 @@ var AgentSession = class _AgentSession {
4965
5019
  try {
4966
5020
  await this.ensureJoined();
4967
5021
  } catch (error) {
5022
+ const failure = describeSessionError(error, "\u4F1A\u8BDD\u8BA2\u9605\u5931\u8D25\uFF0C\u6D88\u606F\u672A\u53D1\u9001");
4968
5023
  this.rollbackOptimisticSend(
4969
5024
  optimisticTurnId,
4970
5025
  askUserAnswer,
4971
5026
  optimisticAskAnswer,
4972
5027
  previousAskAnswer,
4973
5028
  wasStreaming,
4974
- error instanceof Error && error.message ? error.message : "\u4F1A\u8BDD\u8BA2\u9605\u5931\u8D25\uFF0C\u6D88\u606F\u672A\u53D1\u9001"
5029
+ failure.message,
5030
+ failure.code
4975
5031
  );
4976
5032
  return false;
4977
5033
  }
5034
+ const waitStartedAt = this.systemErrorGeneration;
4978
5035
  try {
4979
5036
  const response = await this.runtime.emitWithAck("chat:send", payload, 3e4);
4980
5037
  if (response?.status === "queued") {
@@ -4993,24 +5050,28 @@ var AgentSession = class _AgentSession {
4993
5050
  return true;
4994
5051
  }
4995
5052
  if (response?.status !== "accepted") {
4996
- throw new Error(
4997
- typeof response?.message === "string" ? response.message : "\u6D88\u606F\u672A\u88AB\u670D\u52A1\u7AEF\u63A5\u53D7"
5053
+ throw new AgentSessionError(
5054
+ typeof response?.message === "string" ? response.message : "\u6D88\u606F\u672A\u88AB\u670D\u52A1\u7AEF\u63A5\u53D7",
5055
+ typeof response?.code === "string" ? response.code : void 0
4998
5056
  );
4999
5057
  }
5000
5058
  return true;
5001
5059
  } catch (error) {
5060
+ const failure = describeSessionError(error, "\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
5002
5061
  this.rollbackOptimisticSend(
5003
5062
  optimisticTurnId,
5004
5063
  askUserAnswer,
5005
5064
  optimisticAskAnswer,
5006
5065
  previousAskAnswer,
5007
5066
  wasStreaming,
5008
- error instanceof Error && error.message ? error.message : "\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"
5067
+ failure.message,
5068
+ failure.code,
5069
+ waitStartedAt
5009
5070
  );
5010
5071
  return false;
5011
5072
  }
5012
5073
  }
5013
- rollbackOptimisticSend(optimisticTurnId, askUserAnswer, optimisticAskAnswer, previousAskAnswer, wasStreaming, message) {
5074
+ rollbackOptimisticSend(optimisticTurnId, askUserAnswer, optimisticAskAnswer, previousAskAnswer, wasStreaming, message, code, waitStartedAt) {
5014
5075
  this.discardOptimisticSend(
5015
5076
  optimisticTurnId,
5016
5077
  askUserAnswer,
@@ -5020,6 +5081,12 @@ var AgentSession = class _AgentSession {
5020
5081
  this.update(
5021
5082
  (current) => addErrorMessage(wasStreaming ? current : { ...current, isStreaming: false }, message)
5022
5083
  );
5084
+ const delivered = this.lastDeliveredSystemError;
5085
+ if (waitStartedAt !== void 0 && delivered && delivered.generation > waitStartedAt && delivered.message === message && delivered.code === code) {
5086
+ this.lastDeliveredSystemError = null;
5087
+ return;
5088
+ }
5089
+ this.emitter.emit("error", { message, ...code ? { code } : {} });
5023
5090
  }
5024
5091
  /** 撤回一次乐观提交:移除乐观消息、恢复提问作答、清掉回放草稿。 */
5025
5092
  discardOptimisticSend(optimisticTurnId, askUserAnswer, optimisticAskAnswer, previousAskAnswer) {
@@ -5284,7 +5351,12 @@ var AgentSession = class _AgentSession {
5284
5351
  }
5285
5352
  /** @internal */
5286
5353
  _handleChatStart() {
5287
- this.update((s) => ({ ...s, status: "running", errorMessage: null, isStreaming: true }));
5354
+ this.update((s) => ({
5355
+ ...s,
5356
+ status: "running",
5357
+ errorMessage: null,
5358
+ isStreaming: true
5359
+ }));
5288
5360
  }
5289
5361
  /** @internal */
5290
5362
  _handleChatEnd(status) {
@@ -5292,14 +5364,31 @@ var AgentSession = class _AgentSession {
5292
5364
  this.completeChat(status);
5293
5365
  }
5294
5366
  /** @internal */
5295
- _handleSystemError(message) {
5367
+ _handleSystemError(message, code) {
5296
5368
  this.flushPatches();
5297
5369
  const finalMessage = isOomText(message) ? OOM_MESSAGE : message;
5298
5370
  this.update(
5299
- (s) => addErrorMessage(markFailed({ ...s, status: "failed", errorMessage: finalMessage, isStreaming: false }), finalMessage)
5371
+ (s) => addErrorMessage(
5372
+ markFailed({
5373
+ ...s,
5374
+ status: "failed",
5375
+ errorMessage: finalMessage,
5376
+ isStreaming: false
5377
+ }),
5378
+ finalMessage
5379
+ )
5300
5380
  );
5301
5381
  this.recentChatEndAt = Date.now();
5302
- this.emitter.emit("error", { message: finalMessage });
5382
+ this.systemErrorGeneration += 1;
5383
+ this.lastDeliveredSystemError = {
5384
+ message: finalMessage,
5385
+ ...code ? { code } : {},
5386
+ generation: this.systemErrorGeneration
5387
+ };
5388
+ this.emitter.emit("error", {
5389
+ message: finalMessage,
5390
+ ...code ? { code } : {}
5391
+ });
5303
5392
  }
5304
5393
  /** @internal */
5305
5394
  _handleTaskUpdated(tasks) {
@@ -5834,8 +5923,9 @@ var AgentSession = class _AgentSession {
5834
5923
  5e3
5835
5924
  ).then((response) => {
5836
5925
  if (response && response.ok === false) {
5837
- throw new Error(
5838
- typeof response.message === "string" && response.message ? response.message : "Session subscription failed"
5926
+ throw new AgentSessionError(
5927
+ typeof response.message === "string" && response.message ? response.message : "Session subscription failed",
5928
+ typeof response.code === "string" ? response.code : void 0
5839
5929
  );
5840
5930
  }
5841
5931
  this.joined = true;
@@ -6144,9 +6234,15 @@ var SessionHub = class {
6144
6234
  session._handleChatEnd(data?.status ?? "completed");
6145
6235
  }
6146
6236
  });
6147
- socket.on("system:error", (data) => {
6148
- for (const session of this.route(data.session_id)) session._handleSystemError(data.message);
6149
- });
6237
+ socket.on(
6238
+ "system:error",
6239
+ (data) => {
6240
+ const code = data.code == null ? void 0 : String(data.code);
6241
+ for (const session of this.route(data.session_id)) {
6242
+ session._handleSystemError(data.message, code);
6243
+ }
6244
+ }
6245
+ );
6150
6246
  socket.on("chat:queue:updated", (data) => {
6151
6247
  for (const session of this.route(data?.session_id)) session._handleQueueUpdated(data);
6152
6248
  });
@@ -6803,7 +6899,7 @@ function resolveServiceUrl(endpoints, name, path = "") {
6803
6899
 
6804
6900
  // src/version.ts
6805
6901
  var SDK_NAME = "agent-client";
6806
- var SDK_VERSION = true ? "2610.0.0-beta.80" : "1.1.1";
6902
+ var SDK_VERSION = true ? "2610.0.0-beta.81" : "1.1.1";
6807
6903
 
6808
6904
  // src/commands/protocol.ts
6809
6905
  function isCommandEnvelope(value) {
@@ -6905,6 +7001,7 @@ var Task = type2({
6905
7001
  });
6906
7002
  export {
6907
7003
  AgentSession,
7004
+ AgentSessionError,
6908
7005
  AuthBusyReconnect,
6909
7006
  BladeApiError,
6910
7007
  BladeClient,