@blade-hq/agent-client 1.2.1-beta.13 → 1.2.1-beta.15

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/dist/index.js CHANGED
@@ -3166,7 +3166,7 @@ var AgentSession = class _AgentSession {
3166
3166
  countFiles: async () => readonlyError(),
3167
3167
  downloadFile: async () => readonlyError(),
3168
3168
  attachAppCli: async () => readonlyError(),
3169
- onDispose: () => void 0
3169
+ onDispose: () => true
3170
3170
  },
3171
3171
  "connected"
3172
3172
  );
@@ -3424,15 +3424,18 @@ var AgentSession = class _AgentSession {
3424
3424
  downloadFile(filePath, downloadName) {
3425
3425
  return this.runtime.downloadFile(this.sessionId, filePath, downloadName);
3426
3426
  }
3427
- /** 释放订阅与监听器。之后该实例不再接收任何事件。 */
3427
+ /**
3428
+ * 归还一份持有。同一个会话被多处 connect 时按引用计数释放:只有最后
3429
+ * 一份归还才真正断开订阅与监听器,之前的归还不影响仍在使用的一方。
3430
+ */
3428
3431
  dispose() {
3429
3432
  if (this.disposed) return;
3433
+ if (!this.runtime.onDispose(this)) return;
3430
3434
  this.disposed = true;
3431
3435
  this.cancelPatchFlush();
3432
3436
  this.listeners.clear();
3433
3437
  this.emitter.clear();
3434
3438
  this.commands.clear();
3435
- this.runtime.onDispose(this);
3436
3439
  }
3437
3440
  get isDisposed() {
3438
3441
  return this.disposed;
@@ -3882,6 +3885,8 @@ var SessionHub = class {
3882
3885
  client;
3883
3886
  sessions = /* @__PURE__ */ new Map();
3884
3887
  bootstrapPending = /* @__PURE__ */ new Map();
3888
+ /** 每个会话当前有多少方持有(connect 一次 +1,dispose 一次 -1)。 */
3889
+ holdCounts = /* @__PURE__ */ new Map();
3885
3890
  connection = "disconnected";
3886
3891
  connectionListeners = /* @__PURE__ */ new Set();
3887
3892
  bound = false;
@@ -3900,22 +3905,30 @@ var SessionHub = class {
3900
3905
  this.connectionListeners.delete(listener);
3901
3906
  };
3902
3907
  }
3903
- /** 取得(或复用)一个会话的 AgentSession,并完成历史加载 + 房间订阅。 */
3908
+ /**
3909
+ * 取得(或复用)一个会话的 AgentSession,并完成历史加载 + 房间订阅。
3910
+ *
3911
+ * 每次调用都算一份持有,调用方用完必须 `dispose()` 归还。复用同一实例时
3912
+ * 只有最后一份归还才退订房间——否则 StrictMode 双跑、路由抖动这类"连两次
3913
+ * 只释放一次"的场景会把仍在用的会话退订掉,之后所有 turn/chat 事件静默丢失。
3914
+ */
3904
3915
  async connect(sessionId) {
3905
3916
  const existing = this.sessions.get(sessionId);
3906
3917
  if (existing && !existing.isDisposed) {
3918
+ this.hold(sessionId);
3907
3919
  return this.bootstrapPending.get(sessionId) ?? existing;
3908
3920
  }
3909
3921
  this.ensureBound();
3910
3922
  const session = new AgentSession(sessionId, this.runtimeFor(), this.connection);
3911
3923
  this.sessions.set(sessionId, session);
3924
+ this.hold(sessionId);
3912
3925
  this.ensureConnected();
3913
3926
  const bootstrap = session._bootstrap().then(() => session);
3914
3927
  this.bootstrapPending.set(sessionId, bootstrap);
3915
3928
  try {
3916
3929
  return await bootstrap;
3917
3930
  } catch (error) {
3918
- session.dispose();
3931
+ this.forceRelease(session);
3919
3932
  throw error;
3920
3933
  } finally {
3921
3934
  if (this.bootstrapPending.get(sessionId) === bootstrap) {
@@ -3946,7 +3959,7 @@ var SessionHub = class {
3946
3959
  disposeAll() {
3947
3960
  this.bootstrapPending.clear();
3948
3961
  for (const session of [...this.sessions.values()]) {
3949
- session.dispose();
3962
+ this.forceRelease(session);
3950
3963
  }
3951
3964
  }
3952
3965
  /**
@@ -3983,15 +3996,36 @@ var SessionHub = class {
3983
3996
  countFiles: (sessionId, dirPath) => this.client.sessions.countFiles(sessionId, dirPath),
3984
3997
  downloadFile: (sessionId, filePath, downloadName) => this.client.sessions.downloadFile(sessionId, filePath, downloadName),
3985
3998
  attachAppCli: (sessionId, definition) => this.client.sessions.attachAppCli(sessionId, definition),
3986
- onDispose: (session) => {
3987
- if (this.sessions.get(session.sessionId) === session) {
3988
- this.sessions.delete(session.sessionId);
3989
- }
3990
- ;
3991
- this.client.socket().emit("session:unsubscribe", { session_id: session.sessionId });
3992
- }
3999
+ onDispose: (session) => this.release(session)
3993
4000
  };
3994
4001
  }
4002
+ /** 记一份持有。 */
4003
+ hold(sessionId) {
4004
+ this.holdCounts.set(sessionId, (this.holdCounts.get(sessionId) ?? 0) + 1);
4005
+ }
4006
+ /**
4007
+ * 归还一份持有;还剩别的持有者时返回 false,让 AgentSession 保持可用。
4008
+ * 归零才退订房间并从路由表移除。
4009
+ */
4010
+ release(session) {
4011
+ const remaining = (this.holdCounts.get(session.sessionId) ?? 0) - 1;
4012
+ if (remaining > 0) {
4013
+ this.holdCounts.set(session.sessionId, remaining);
4014
+ return false;
4015
+ }
4016
+ this.holdCounts.delete(session.sessionId);
4017
+ if (this.sessions.get(session.sessionId) === session) {
4018
+ this.sessions.delete(session.sessionId);
4019
+ }
4020
+ ;
4021
+ this.client.socket().emit("session:unsubscribe", { session_id: session.sessionId });
4022
+ return true;
4023
+ }
4024
+ /** 不管还有几方持有都立即释放(启动失败、切换身份)。 */
4025
+ forceRelease(session) {
4026
+ this.holdCounts.delete(session.sessionId);
4027
+ session.dispose();
4028
+ }
3995
4029
  ensureConnected() {
3996
4030
  const socket = this.client.socket();
3997
4031
  if (socket.connected || socket.active) return;
@@ -4179,7 +4213,7 @@ function resolveAuthToken(options) {
4179
4213
 
4180
4214
  // src/version.ts
4181
4215
  var SDK_NAME = "agent-client";
4182
- var SDK_VERSION = true ? "1.2.1-beta.13" : "1.1.1";
4216
+ var SDK_VERSION = true ? "1.2.1-beta.15" : "1.1.1";
4183
4217
 
4184
4218
  // src/socket.ts
4185
4219
  function withSdkIdentity(auth) {