@blade-hq/agent-client 2612.0.0-beta.20 → 2612.0.0-beta.21

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
@@ -162,17 +162,17 @@ const index = await client.sessions.getSessionTurnIndex(id) // SessionTurnIndex
162
162
  const first = index.items[0] // SessionTurnIndexItem
163
163
  ```
164
164
 
165
- 创建会话时可以绑定 Streamable HTTP MCP 来源;BA 会在创建成功前读取并保存工具列表,
166
- 来源此时必须可访问且接受创建请求中的 Bearer 凭据。之后沿用已发现的工具目录,
167
- 刷新来源时更新目录。默认 `exposure: "lazy"` 通过 `lazy_tool_list`、`lazy_tool_inspect`、
168
- `lazy_tool_run` 发现和调用工具;`exposure: "eager"` 会将 `include_tools` 允许的工具和完整
169
- 参数 schema 直接加入执行模式的模型工具列表,无需先 inspect。`eager_tools` 可在 lazy
170
- 来源中选出直接暴露的工具,也可指定 eager 来源中的优先工具。主运行和子智能体使用相同
171
- 策略,计划模式不加载。直接工具的名称为 `mcp_<来源>__<工具>_<hash>`,调用仍经 Go MCP
172
- dispatcher。会话 MCP 的可用工具名称按来源注入 `lazy_tool_catalog`,lazy 工具完整 schema
173
- 由 `lazy_tool_inspect({tool: "session:<source>/<tool>"})` 按需读取。每个运行最多直接加载 32 个工具、64 KiB 完整工具定义;
174
- 超预算的工具仍可通过
175
- `lazy_tool_inspect/lazy_tool_run` 使用。
165
+ 创建会话时可以绑定 Streamable HTTP MCP 来源,并用可选 `description` 提供用途说明。
166
+ 默认 `exposure: "lazy"` 只保存来源配置,创建时不连接 MCP;模型初始只看到来源名称和说明。
167
+ 通过 `lazy_tool_search({query: "所需能力", limit: 8})` 全局搜索并获得命中工具的完整标识、
168
+ 说明和参数 schema,再用 `lazy_tool_run` 调用。
169
+ 首次搜索发现工具,同一执行内复用成功目录;下一次执行重新发现。失败不缓存,也不返回部分成功。
170
+
171
+ `exposure: "eager"` 以及非空 `eager_tools` 保持创建时发现并保存目录:eager 将 `include_tools`
172
+ 允许的工具和完整 schema 直接加入执行模式的模型工具列表;`eager_tools` 可在 lazy 来源中选出
173
+ 直接暴露的工具,也可指定 eager 来源中的优先工具。主运行和子智能体使用相同策略,计划模式不加载。
174
+ 每个运行最多直接加载 32 个工具、64 KiB 完整工具定义;超预算的工具仍可通过搜索后 run 使用。
175
+ 显式会话 refresh 只重新发现 eager 来源目录。调用始终经 Go MCP dispatcher,继续校验当前身份和工具权限。
176
176
  Bearer 凭据只在创建或轮换时提交,查询不会返回它:
177
177
 
178
178
  ```ts
@@ -39,6 +39,7 @@ export declare class BladeClient {
39
39
  private readonly anonymousClients;
40
40
  private readonly toolProviders;
41
41
  private refreshPromise;
42
+ private cookieLogoutPending;
42
43
  private socketInstance;
43
44
  private readonly socketReplacementListeners;
44
45
  /** login() / setToken() 热挂载的令牌,优先级高于构造参数。 */
@@ -213,7 +213,7 @@ var ToolProvider = class {
213
213
 
214
214
  // src/version.ts
215
215
  var SDK_NAME = "agent-client";
216
- var SDK_VERSION = true ? "2612.0.0-beta.20" : "1.1.1";
216
+ var SDK_VERSION = true ? "2612.0.0-beta.21" : "1.1.1";
217
217
 
218
218
  // src/auth-login.ts
219
219
  function isAuthCallbackMessage(value) {
@@ -379,8 +379,8 @@ var AuthResource = class {
379
379
  getProviders() {
380
380
  return this.client.json("GET", "/api/auth/providers");
381
381
  }
382
- getMe() {
383
- return this.client.json("GET", "/api/auth/me");
382
+ getMe(options = {}) {
383
+ return this.client.jsonFromInit("/api/auth/me", { signal: options.signal });
384
384
  }
385
385
  logout() {
386
386
  return this.client.json("POST", "/api/auth/logout");
@@ -1105,8 +1105,8 @@ function snapshot(state, sequence, status) {
1105
1105
  first_token_ms: state.firstTokenMs,
1106
1106
  stream_duration_ms: state.streamDurationMs,
1107
1107
  first_token_delivery_ms: state.firstTokenDeliveryMs,
1108
- started_at: state.modelStartedAt ?? new Date(state.startedAt).toISOString(),
1109
- // 没有 payload 上报时用的是 loop:turn 的浏览器事件时间,标记出来供瀑布区分。
1108
+ started_at: state.modelStartedAt,
1109
+ // 未上报时不伪造服务器起点;浏览器时钟不能与服务器时钟混用。
1110
1110
  has_reported_timing: state.modelStartedAtReported,
1111
1111
  context_window: state.contextWindow,
1112
1112
  memory_refs: state.memoryRefs,
@@ -1859,6 +1859,7 @@ var ClientProjectionBuilder = class {
1859
1859
  return this.syncPatch(state);
1860
1860
  }
1861
1861
  applyFirstTokenDelivery(state, payload) {
1862
+ this.applyStreamTiming(state, payload);
1862
1863
  const firstTokenMs = payload.first_token_ms;
1863
1864
  if (state.firstTokenMs === null && typeof firstTokenMs === "number" && firstTokenMs >= 0) {
1864
1865
  state.firstTokenMs = Math.round(firstTokenMs);
@@ -2600,7 +2601,7 @@ function projectHistory(entries) {
2600
2601
  let content = isRecord(entry.message) ? entry.message.content : void 0;
2601
2602
  if (kind === "tool_result_archive") {
2602
2603
  const archivedCount = Array.isArray(data.archived_files) ? data.archived_files.length : 0;
2603
- content = `<compaction-summary>
2604
+ content = typeof data.summary === "string" && data.summary ? data.summary : `<compaction-summary>
2604
2605
  \u5DF2\u5F52\u6863 ${archivedCount} \u4E2A\u5927\u578B\u5DE5\u5177\u7ED3\u679C\u3002
2605
2606
  </compaction-summary>`;
2606
2607
  }
@@ -3300,8 +3301,8 @@ var ModelsResource = class {
3300
3301
  this.client = client;
3301
3302
  }
3302
3303
  client;
3303
- list() {
3304
- return this.client.json("GET", "/api/config/models");
3304
+ list(options = {}) {
3305
+ return this.client.jsonFromInit("/api/config/models", { signal: options.signal });
3305
3306
  }
3306
3307
  };
3307
3308
 
@@ -4254,6 +4255,20 @@ var SiteVisitsResource = class {
4254
4255
  }
4255
4256
  };
4256
4257
 
4258
+ // src/server-clock.ts
4259
+ var clocks = /* @__PURE__ */ new WeakMap();
4260
+ function syncServerTime(client, timestamp, receivedAt = performance.now()) {
4261
+ if (typeof timestamp !== "string") return;
4262
+ const serverTime = Date.parse(timestamp);
4263
+ if (!Number.isFinite(serverTime)) return;
4264
+ const current = getServerTime(client, receivedAt);
4265
+ clocks.set(client, { serverTime: Math.max(serverTime, current ?? serverTime), receivedAt });
4266
+ }
4267
+ function getServerTime(client, now = performance.now()) {
4268
+ const clock = client ? clocks.get(client) : void 0;
4269
+ return clock ? clock.serverTime + Math.max(0, now - clock.receivedAt) : null;
4270
+ }
4271
+
4257
4272
  // src/session/background.ts
4258
4273
  var BACKGROUND_STATUSES = [
4259
4274
  "starting",
@@ -7857,6 +7872,7 @@ var SessionHub = class {
7857
7872
  socket.on(
7858
7873
  "turn:events",
7859
7874
  (data, acknowledge) => {
7875
+ syncServerTime(this.client, data.server_time);
7860
7876
  acknowledgeTurnEvents(acknowledge);
7861
7877
  if (data.session_id && (this.statusWatchCounts.has(data.session_id) || this.sessions.has(data.session_id)) && data.events?.some((event) => event.type === "log:record")) {
7862
7878
  const delivery = this.statusCursor(data.session_id).consume(data.events ?? []);
@@ -8062,6 +8078,7 @@ var BladeClient = class _BladeClient {
8062
8078
  anonymousClients = /* @__PURE__ */ new Map();
8063
8079
  toolProviders = /* @__PURE__ */ new Set();
8064
8080
  refreshPromise = null;
8081
+ cookieLogoutPending = 0;
8065
8082
  socketInstance = null;
8066
8083
  socketReplacementListeners = /* @__PURE__ */ new Set();
8067
8084
  /** login() / setToken() 热挂载的令牌,优先级高于构造参数。 */
@@ -8287,24 +8304,31 @@ var BladeClient = class _BladeClient {
8287
8304
  }
8288
8305
  async fetch(method, path, init = {}, isRetry = false) {
8289
8306
  const url = this.buildUrl(path);
8290
- const response = await (this.options.fetchImpl ?? fetch)(url.toString(), {
8291
- method,
8292
- body: init.body,
8293
- credentials: init.credentials ?? "include",
8294
- headers: this.buildHeaders(url, init.headers),
8295
- signal: init.signal
8296
- });
8297
- if (response.status === 401 && !isRetry && this.shouldRefreshFor401(url)) {
8298
- const refreshed = await this.tryRefreshUnlessAborted(init.signal);
8299
- if (refreshed) {
8300
- return this.fetch(method, path, init, true);
8307
+ const isLogout = method === "POST" && url.pathname === this.buildUrl("/api/auth/logout").pathname && this.isSameBackendUrl(url);
8308
+ if (isLogout) this.cookieLogoutPending += 1;
8309
+ try {
8310
+ if (isLogout) await this.refreshPromise;
8311
+ const response = await (this.options.fetchImpl ?? fetch)(url.toString(), {
8312
+ method,
8313
+ body: init.body,
8314
+ credentials: init.credentials ?? "include",
8315
+ headers: this.buildHeaders(url, init.headers),
8316
+ signal: init.signal
8317
+ });
8318
+ if (response.status === 401 && !isRetry && this.shouldRefreshFor401(url)) {
8319
+ const refreshed = await this.tryRefreshUnlessAborted(init.signal);
8320
+ if (refreshed) {
8321
+ return this.fetch(method, path, init, true);
8322
+ }
8301
8323
  }
8324
+ if (init.expectOk !== false && !response.ok) {
8325
+ const detail = await extractErrorDetail(response);
8326
+ throw decorateAuthError(new BladeApiError(response, detail));
8327
+ }
8328
+ return response;
8329
+ } finally {
8330
+ if (isLogout) this.cookieLogoutPending -= 1;
8302
8331
  }
8303
- if (init.expectOk !== false && !response.ok) {
8304
- const detail = await extractErrorDetail(response);
8305
- throw decorateAuthError(new BladeApiError(response, detail));
8306
- }
8307
- return response;
8308
8332
  }
8309
8333
  buildAuthedUrl(path) {
8310
8334
  const url = buildBackendUrl(this.options.baseUrl, path);
@@ -8372,7 +8396,7 @@ var BladeClient = class _BladeClient {
8372
8396
  }
8373
8397
  }
8374
8398
  async tryRefresh() {
8375
- if (this.options.anonymousSessionId) return false;
8399
+ if (this.options.anonymousSessionId || this.cookieLogoutPending > 0) return false;
8376
8400
  if (this.refreshPromise) {
8377
8401
  return this.refreshPromise;
8378
8402
  }
@@ -8554,6 +8578,8 @@ export {
8554
8578
  isEntryGoneError,
8555
8579
  PluginsResource,
8556
8580
  ChatGroupsResource,
8581
+ syncServerTime,
8582
+ getServerTime,
8557
8583
  authBusyRetryDelayMs,
8558
8584
  AuthBusyReconnect,
8559
8585
  prependOlder,
@@ -8600,4 +8626,4 @@ export {
8600
8626
  createSocket,
8601
8627
  BladeClient
8602
8628
  };
8603
- //# sourceMappingURL=chunk-2VIJBVAC.js.map
8629
+ //# sourceMappingURL=chunk-J3XXTHHK.js.map