@blade-hq/agent-client 2610.0.0-beta.47 → 2610.0.0-beta.49

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
@@ -172,7 +172,7 @@ await client.sessions.getSessionTurnsPage(id, { before: page.nextBefore! })
172
172
  ```
173
173
 
174
174
  会话插件可通过 `client.sessions.listSessionPlugins(id)` 查询,并用
175
- `client.sessions.setSessionPluginActivation(id, name, active)` 切换当前会话;返回项类型为公开的 `SessionPlugin`。
175
+ `client.sessions.setSessionPluginActivation(id, name, active)` 切换当前会话。列表项为 `SessionPlugin`;修改结果为 `SessionPluginActivation`,只返回选择和准备状态,安装事实需从列表获取。
176
176
 
177
177
  ### 会话回放(演示 / 彩排)
178
178
 
@@ -502,6 +502,15 @@ PATCH / query / FormData / AbortSignal 等完整 HTTP 语义(否则不够用
502
502
  > **安全约束**:`client.token` 只应发往 `baseUrl` 同源的接口。不要把它附加到
503
503
  > 第三方域名的请求上——那等于把用户的访问凭据交给别人。
504
504
 
505
+ ### 只读轮询退避
506
+
507
+ `new PollingBackoff(3000)` 为轮询维护独立退避状态,参数为正常间隔(大于 0、不超过 60 秒)。
508
+ 请求成功后调用 `reset()`,失败后调用 `failed(error)`;`waitMs()` 返回剩余等待毫秒数,
509
+ `0` 表示可以请求,`false` 表示停止。HTTP 错误应传入保留响应头的 `BladeApiError`。
510
+ 401/403 等确定性错误停止,网络/5xx 指数退避带抖动且最高 60 秒;`Retry-After`
511
+ 可以要求更长等待,超出浏览器定时器范围时停止。策略自身不发送请求,调度器须传递
512
+ AbortSignal、在卸载时停止,并避免将它用于自动重放 POST 等有副作用的操作。
513
+
505
514
  ## headless:一次性问答
506
515
 
507
516
  ```ts
@@ -607,7 +616,7 @@ transformSlashCommand(skillId, prompt, { local: false, installed: false })
607
616
  - **SDK 身份**:`SDK_NAME`、`SDK_VERSION`
608
617
  - **声明式会话**:`SessionDefinition`、`SolutionDefinition`、`SkillDefinition`、`SessionConfig`、`TextFile`、`SessionSetupError`、`SessionSetupStage`
609
618
  - **模型目录**:`ModelsResource`、`ModelCatalog`、`ModelOption`
610
- - **聊天项目资源(REST)**:`ChatProjectsResource`、`ChatProject`
619
+ - **聊天项目资源(REST)**:`ChatProjectsResource`、`ChatProject`(含 `delete` 永久删除)
611
620
  - **会话资源(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`
612
621
  - **会话回放**:`ReplayState`、`ReplaySpeed`、`ReplayPreview`、`ReplaySnapshot`、`toReplaySnapshot`、`DEFAULT_REPLAY_SPEED`
613
622
  - **会话状态机**:`SessionHub`、`SessionConnectOptions`、`SessionState`、`SendOptions`、`ConnectionStatus`、`AskUserAnswerData`、`AgentLoopInfo`、`ActiveCompactionState`、`createInitialSessionState`、`AgentSessionEventName`
@@ -615,4 +624,4 @@ transformSlashCommand(skillId, prompt, { local: false, installed: false })
615
624
  - **消息与投影协议**:`MessageContent`、`MessageContentPart`、`TextContentPart`、`ImageUrlContentPart`、`FileContentPart`、`ToolCallInfo`、`ToolBridgeContent`、`CompactionInfo`、`ContextProjectionData`、`ContextProjectionFields`、`ContextDisplayState`、`ContextGroupDisplayState`、`ContextAction`、`ContextSourceInfo`、`MemoryRefInfo`、`ArchivedFileInfo`、`ArchivedToolCallInfo`、`TurnProjection`、`ContentBlock`、`PatchEnvelope`、`MemoryRef`、`PostChatFollowup`、`FinalArtifact`、`contextProjectionData`、`getContextDisplayState`、`getContextGroupDisplayState`、`groupAdjacentContextRuns`、`latestPostChatFollowup`、`buildMessageContent`、`normalizeMessageContent`、`isHiddenInternalMessage`、`transformSlashCommand`、`SkillMentionAvailability`、`extractTextAttachments`、`ParsedTextAttachment`、`ParsedTextContext`
616
625
  - **Solution / 任务协议**:`Solution`、`SolutionAppField`、`SolutionAppState`、`SolutionAppUiConfig`、`SolutionRef`、`PublishedSolutionRef`、`ExistingSolutionRef`、`PreparedSolution`、`PreparedSolutionAsset`、`LayoutType`、`BizRole`、`TaskStatus`、`BackgroundTask`、`BackgroundTaskStopResult`
617
626
  - **Headless**:`HeadlessResource`、`RunOptions`、`RunResult`、`RunTrace`
618
- - **低层通道(apps/web 等高级集成)**:`createSocket`、`CreateSocketOptions`、`TypedSocket`、`AsrAudioPayload`、`ClientProjectionBuilder`、`RawEvent`、`acknowledgeTurnEvents`、`hasChatRunEvent`、`acceptedPostChatFollowupCompletesLatestRun`、`reconcileOptimisticUserTurns`、`reconcileHistoricalUserTurns`
627
+ - **低层通道(apps/web 等高级集成)**:`createSocket`、`CreateSocketOptions`、`TypedSocket`、`AsrAudioPayload`、`AuthBusyReconnect`、`authBusyRetryDelayMs`、`ClientProjectionBuilder`、`RawEvent`、`acknowledgeTurnEvents`、`hasChatRunEvent`、`acceptedPostChatFollowupCompletesLatestRun`、`reconcileOptimisticUserTurns`、`reconcileHistoricalUserTurns`
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { BladeClient } from "./blade-client";
2
2
  export type { BladeClientOptions, UploadProgress } from "./blade-client";
3
3
  export { BladeApiError } from "./rest";
4
+ export { PollingBackoff } from "./polling-backoff";
4
5
  export type { LoginOptions, LoginResult, TokenStorageMode } from "./auth-login";
5
6
  export { EMPTY_PLATFORM_ENDPOINTS, loadPlatformEndpoints, resolveServiceUrl, } from "./platform-endpoints";
6
7
  export type { LoadPlatformEndpointsOptions, PlatformEndpoints, PlatformServiceName, } from "./platform-endpoints";
@@ -27,7 +28,7 @@ export type { ComputerOS, ComputerState, SessionComputer, SessionComputerList, }
27
28
  export { ModelsResource } from "./resources/models";
28
29
  export type { ModelCatalog, ModelOption } from "./resources/models";
29
30
  export type { SessionsResource } from "./resources/sessions";
30
- export type { SessionPlugin } from "./resources/sessions";
31
+ export type { SessionPlugin, SessionPluginActivation } from "./resources/sessions";
31
32
  export { ChatProjectsResource } from "./resources/chat-projects";
32
33
  export type { ChatProject } from "./resources/chat-projects";
33
34
  export { hasChatRunEvent } from "./shared/projection";
@@ -50,6 +51,7 @@ export { Task, TaskStatus } from "./schemas/task";
50
51
  export type { BackgroundTask, BackgroundTaskStopResult } from "./schemas/background";
51
52
  export { acceptedPostChatFollowupCompletesLatestRun, acknowledgeTurnEvents, ClientProjectionBuilder, } from "./shared/projection";
52
53
  export type { RawEvent } from "./shared/projection";
54
+ export { AuthBusyReconnect, authBusyRetryDelayMs } from "./shared/auth-busy";
53
55
  export { createSocket } from "./socket";
54
56
  export type { CreateSocketOptions, TypedSocket } from "./socket";
55
57
  export type { AsrAudioPayload } from "./types/socket-events";
package/dist/index.js CHANGED
@@ -2271,7 +2271,8 @@ function projectHistory(entries) {
2271
2271
  const data = entry.data ?? {};
2272
2272
  const toolCallId = String(data.tool_call_id ?? "");
2273
2273
  const owner = kind === "tool_bridge" ? toolCallId ? toolOwners.get(toolCallId) : void 0 : toolOwners.get(toolCallId) ?? findLatestAssistant(turns, loopId);
2274
- const content = kind === "tool_ui" ? data.ui : data.bridge;
2274
+ const rawContent = kind === "tool_ui" ? data.ui : data.bridge;
2275
+ const content = kind === "tool_ui" && isRecord(rawContent) ? { ...rawContent, archived: true } : rawContent;
2275
2276
  if (owner && content !== void 0) {
2276
2277
  owner.blocks.push({
2277
2278
  type: kind,
@@ -3053,6 +3054,50 @@ var ChatProjectsResource = class {
3053
3054
  update(projectId, payload) {
3054
3055
  return this.client.json("PATCH", `/api/chat-projects/${encodeURIComponent(projectId)}`, payload);
3055
3056
  }
3057
+ delete(projectId) {
3058
+ return this.client.json("DELETE", `/api/chat-projects/${encodeURIComponent(projectId)}`);
3059
+ }
3060
+ };
3061
+
3062
+ // src/shared/auth-busy.ts
3063
+ var DEFAULT_RETRY_AFTER_SECONDS = 1;
3064
+ var MAX_TIMER_DELAY_MS = 2147483647;
3065
+ function errorData(error) {
3066
+ if (typeof error !== "object" || error === null || !("data" in error)) return null;
3067
+ const data = error.data;
3068
+ return typeof data === "object" && data !== null ? data : null;
3069
+ }
3070
+ function authBusyRetryDelayMs(error) {
3071
+ const data = errorData(error);
3072
+ if (data?.code !== "auth_busy" || Number(data.status) !== 429) return null;
3073
+ const rawRetryAfter = data.retry_after;
3074
+ const seconds = rawRetryAfter == null || String(rawRetryAfter).trim() === "" ? Number.NaN : Number(rawRetryAfter);
3075
+ const delaySeconds = Number.isFinite(seconds) && seconds >= 0 ? seconds : DEFAULT_RETRY_AFTER_SECONDS;
3076
+ return Math.min(delaySeconds * 1e3, MAX_TIMER_DELAY_MS);
3077
+ }
3078
+ var AuthBusyReconnect = class {
3079
+ timer = null;
3080
+ get pending() {
3081
+ return this.timer !== null;
3082
+ }
3083
+ schedule(error, reconnect) {
3084
+ const delay = authBusyRetryDelayMs(error);
3085
+ if (delay === null) {
3086
+ this.cancel();
3087
+ return false;
3088
+ }
3089
+ this.cancel();
3090
+ this.timer = globalThis.setTimeout(() => {
3091
+ this.timer = null;
3092
+ reconnect();
3093
+ }, delay);
3094
+ return true;
3095
+ }
3096
+ cancel() {
3097
+ if (this.timer === null) return;
3098
+ globalThis.clearTimeout(this.timer);
3099
+ this.timer = null;
3100
+ }
3056
3101
  };
3057
3102
 
3058
3103
  // src/commands/registry.ts
@@ -4813,7 +4858,8 @@ ${text}` } : block
4813
4858
  const ui = block.content;
4814
4859
  if (ui.target !== "preview") continue;
4815
4860
  const previewType = ui.resourceHTML ? "resource-html" : "resource-uri";
4816
- const previewContent = ui.resourceHTML ?? ui.resourceUri ?? ui.resourceURI;
4861
+ const previewContent = ui.resourceHTML ?? (ui.archived ? void 0 : ui.resourceUri ?? ui.resourceURI);
4862
+ if (!previewContent) continue;
4817
4863
  if (!previewContent) continue;
4818
4864
  const key = `${block.tool_call_id}:${previewType}`;
4819
4865
  if (this.deliveredToolPreviews.get(key) === previewContent) continue;
@@ -5099,6 +5145,7 @@ var SessionHub = class {
5099
5145
  connectionListeners = /* @__PURE__ */ new Set();
5100
5146
  bound = false;
5101
5147
  hasConnected = false;
5148
+ authBusyReconnect = new AuthBusyReconnect();
5102
5149
  constructor(client) {
5103
5150
  this.client = client;
5104
5151
  }
@@ -5165,6 +5212,7 @@ var SessionHub = class {
5165
5212
  /** 手动重连(例如登录态变化后)。 */
5166
5213
  reconnect() {
5167
5214
  this.ensureBound();
5215
+ this.authBusyReconnect.cancel();
5168
5216
  const socket = this.client.socket();
5169
5217
  socket.disconnect();
5170
5218
  this.setConnection("connecting");
@@ -5172,11 +5220,13 @@ var SessionHub = class {
5172
5220
  }
5173
5221
  /** 断开连接(不清理 session 实例)。 */
5174
5222
  disconnect() {
5223
+ this.authBusyReconnect.cancel();
5175
5224
  this.client.socket().disconnect();
5176
5225
  this.setConnection("disconnected");
5177
5226
  }
5178
5227
  /** 清理当前身份下缓存的全部会话,避免切换登录身份后复用旧快照。 */
5179
5228
  disposeAll() {
5229
+ this.authBusyReconnect.cancel();
5180
5230
  this.bootstrapPending.clear();
5181
5231
  for (const session of [...this.sessions.values()]) {
5182
5232
  this.forceRelease(session);
@@ -5188,6 +5238,7 @@ var SessionHub = class {
5188
5238
  * turn:* / chat:* 事件都会静默丢弃。
5189
5239
  */
5190
5240
  _handleSocketReplaced() {
5241
+ this.authBusyReconnect.cancel();
5191
5242
  this.bound = false;
5192
5243
  this.setConnection("disconnected");
5193
5244
  if (this.sessions.size === 0) return;
@@ -5240,6 +5291,9 @@ var SessionHub = class {
5240
5291
  if (this.sessions.get(session.sessionId) === session) {
5241
5292
  this.sessions.delete(session.sessionId);
5242
5293
  }
5294
+ if (this.sessions.size === 0) {
5295
+ this.authBusyReconnect.cancel();
5296
+ }
5243
5297
  ;
5244
5298
  this.client.socket().emit("session:unsubscribe", { session_id: session.sessionId });
5245
5299
  return true;
@@ -5248,10 +5302,13 @@ var SessionHub = class {
5248
5302
  forceRelease(session) {
5249
5303
  this.holdCounts.delete(session.sessionId);
5250
5304
  session.dispose();
5305
+ if (this.sessions.size === 0) {
5306
+ this.authBusyReconnect.cancel();
5307
+ }
5251
5308
  }
5252
5309
  ensureConnected() {
5253
5310
  const socket = this.client.socket();
5254
- if (socket.connected || socket.active) return;
5311
+ if (socket.connected || socket.active || this.authBusyReconnect.pending) return;
5255
5312
  this.setConnection("connecting");
5256
5313
  socket.connect();
5257
5314
  }
@@ -5283,6 +5340,7 @@ var SessionHub = class {
5283
5340
  this.bound = true;
5284
5341
  const socket = this.client.socket();
5285
5342
  socket.on("connect", () => {
5343
+ this.authBusyReconnect.cancel();
5286
5344
  const isReconnect = this.hasConnected;
5287
5345
  this.hasConnected = true;
5288
5346
  this.setConnection("connected");
@@ -5292,8 +5350,24 @@ var SessionHub = class {
5292
5350
  }
5293
5351
  }
5294
5352
  });
5295
- socket.on("connect_error", () => this.setConnection("disconnected"));
5296
- socket.on("disconnect", () => this.setConnection("disconnected"));
5353
+ socket.on("connect_error", (error) => {
5354
+ this.setConnection("disconnected");
5355
+ if (this.sessions.size === 0) {
5356
+ this.authBusyReconnect.cancel();
5357
+ return;
5358
+ }
5359
+ if (this.authBusyReconnect.schedule(error, () => {
5360
+ const current = this.client.socket();
5361
+ if (current !== socket || current.connected || current.active) return;
5362
+ this.setConnection("connecting");
5363
+ current.connect();
5364
+ })) return;
5365
+ this.authBusyReconnect.cancel();
5366
+ });
5367
+ socket.on("disconnect", () => {
5368
+ this.authBusyReconnect.cancel();
5369
+ this.setConnection("disconnected");
5370
+ });
5297
5371
  socket.io.on("reconnect_attempt", () => this.setConnection("reconnecting"));
5298
5372
  socket.on("turn:start", (data) => {
5299
5373
  for (const session of this.route(data.session_id)) session._handleTurnStart(data);
@@ -5842,6 +5916,42 @@ function parseXhrHeaders(rawHeaders) {
5842
5916
  return headers;
5843
5917
  }
5844
5918
 
5919
+ // src/polling-backoff.ts
5920
+ var MAX_BACKOFF_MS = 6e4;
5921
+ var MAX_TIMER_MS = 2147483647;
5922
+ var PollingBackoff = class {
5923
+ constructor(intervalMs) {
5924
+ this.intervalMs = intervalMs;
5925
+ if (!Number.isFinite(intervalMs) || intervalMs <= 0 || intervalMs > MAX_BACKOFF_MS) {
5926
+ throw new RangeError("Polling interval must be between 0 and 60000 ms");
5927
+ }
5928
+ }
5929
+ intervalMs;
5930
+ failures = 0;
5931
+ nextAt = 0;
5932
+ reset() {
5933
+ this.failures = 0;
5934
+ this.nextAt = 0;
5935
+ }
5936
+ failed(error) {
5937
+ if (error instanceof Error && error.name === "AbortError" || error instanceof BladeApiError && error.status < 500 && error.status !== 408 && error.status !== 429) {
5938
+ this.nextAt = false;
5939
+ return;
5940
+ }
5941
+ this.failures = Math.min(this.failures + 1, 10);
5942
+ const ceiling = Math.min(MAX_BACKOFF_MS, this.intervalMs * 2 ** this.failures);
5943
+ const backoff = Math.max(this.intervalMs, ceiling * (0.5 + Math.random() * 0.5));
5944
+ const header = error instanceof BladeApiError ? error.response.headers.get("Retry-After")?.trim() : void 0;
5945
+ const retryAfter = header ? /^\d+$/.test(header) ? Number(header) * 1e3 : Date.parse(header) - Date.now() : 0;
5946
+ const delay = Math.max(backoff, Number.isNaN(retryAfter) ? 0 : retryAfter);
5947
+ this.nextAt = delay > MAX_TIMER_MS ? false : Date.now() + delay;
5948
+ }
5949
+ /** Zero means ready, false means stop, otherwise wait at least this many ms. */
5950
+ waitMs() {
5951
+ return this.nextAt === false ? false : Math.max(0, this.nextAt - Date.now());
5952
+ }
5953
+ };
5954
+
5845
5955
  // src/platform-endpoints.ts
5846
5956
  var PLATFORM_SERVICE_NAMES = [
5847
5957
  "os",
@@ -5924,7 +6034,7 @@ function resolveServiceUrl(endpoints, name, path = "") {
5924
6034
 
5925
6035
  // src/version.ts
5926
6036
  var SDK_NAME = "agent-client";
5927
- var SDK_VERSION = true ? "2610.0.0-beta.47" : "1.1.1";
6037
+ var SDK_VERSION = true ? "2610.0.0-beta.49" : "1.1.1";
5928
6038
 
5929
6039
  // src/commands/protocol.ts
5930
6040
  function isCommandEnvelope(value) {
@@ -6026,6 +6136,7 @@ var Task = type2({
6026
6136
  });
6027
6137
  export {
6028
6138
  AgentSession,
6139
+ AuthBusyReconnect,
6029
6140
  BladeApiError,
6030
6141
  BladeClient,
6031
6142
  ChatProjectsResource,
@@ -6035,6 +6146,7 @@ export {
6035
6146
  EMPTY_PLATFORM_ENDPOINTS,
6036
6147
  LayoutType,
6037
6148
  ModelsResource,
6149
+ PollingBackoff,
6038
6150
  SDK_NAME,
6039
6151
  SDK_VERSION,
6040
6152
  SessionHub,
@@ -6045,6 +6157,7 @@ export {
6045
6157
  TaskStatus,
6046
6158
  acceptedPostChatFollowupCompletesLatestRun,
6047
6159
  acknowledgeTurnEvents,
6160
+ authBusyRetryDelayMs,
6048
6161
  buildMessageContent,
6049
6162
  canToggleComputer,
6050
6163
  chatErrorForDisplay,