@band-ai/sdk 0.3.0 → 0.3.1

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/adapters.cjs CHANGED
@@ -2167,8 +2167,8 @@ var ACPClientAdapter = class extends SimpleAdapter {
2167
2167
  bootstrappedSessions = /* @__PURE__ */ new Set();
2168
2168
  pendingPermissions = /* @__PURE__ */ new Map();
2169
2169
  resolvePermission;
2170
+ resolveSessionMode;
2170
2171
  permissionTimeoutMs;
2171
- requestedPermissionMode;
2172
2172
  logger;
2173
2173
  backend = null;
2174
2174
  backendPromise = null;
@@ -2197,13 +2197,10 @@ var ACPClientAdapter = class extends SimpleAdapter {
2197
2197
  this.clientCapabilities = options.clientCapabilities;
2198
2198
  this.connectionFactory = options.connectionFactory ?? createSubprocessConnection;
2199
2199
  this.resolvePermission = options.resolvePermission;
2200
+ this.resolveSessionMode = options.resolveSessionMode;
2200
2201
  this.logger = options.logger ?? new NoopLogger();
2201
2202
  this.permissionTimeoutMs = options.permissionTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
2202
- this.requestedPermissionMode = options.requestedPermissionMode;
2203
- if (this.requestedPermissionMode !== void 0 && this.requestedPermissionMode.length === 0) {
2204
- throw new ValidationError("requestedPermissionMode must be a non-empty mode id, got an empty string");
2205
- }
2206
- if (this.resolvePermission && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
2203
+ if ((this.resolvePermission || this.resolveSessionMode) && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
2207
2204
  throw new ValidationError(`permissionTimeoutMs must be a positive finite number, got ${options.permissionTimeoutMs}`);
2208
2205
  }
2209
2206
  }
@@ -2367,7 +2364,7 @@ ${messageWithContext}`;
2367
2364
  if (restored.ok) {
2368
2365
  this.activeSessions.add(existingSessionId);
2369
2366
  this.bootstrappedSessions.add(existingSessionId);
2370
- await this.applyRequestedPermissionMode(connection, existingSessionId, restored.modes);
2367
+ await this.configureSessionMode(roomId, existingSessionId, restored.modes, connection);
2371
2368
  return existingSessionId;
2372
2369
  }
2373
2370
  }
@@ -2377,53 +2374,93 @@ ${messageWithContext}`;
2377
2374
  });
2378
2375
  this.roomToSession.set(roomId, created.sessionId);
2379
2376
  this.activeSessions.add(created.sessionId);
2380
- await this.applyRequestedPermissionMode(connection, created.sessionId, created.modes);
2377
+ await this.configureSessionMode(roomId, created.sessionId, created.modes, connection);
2381
2378
  return created.sessionId;
2382
2379
  }
2383
- async tryRestoreSession(connection, sessionId, mcpServers) {
2384
- const capabilities = this.connectionState?.agentCapabilities;
2385
- const params = { cwd: this.cwd, mcpServers, sessionId };
2386
- const restore = capabilities?.loadSession ? () => connection.loadSession(params) : capabilities?.sessionCapabilities?.resume ? () => connection.unstable_resumeSession(params) : null;
2387
- if (!restore) {
2388
- return { ok: false };
2389
- }
2390
- try {
2391
- const restored = await restore();
2392
- return { ok: true, modes: restored?.modes };
2393
- } catch {
2394
- return { ok: false };
2395
- }
2396
- }
2397
2380
  // Best-effort: never throws, so a mode switch going wrong can't take a
2398
2381
  // session establishment down with it.
2399
- async applyRequestedPermissionMode(connection, sessionId, modes) {
2400
- const requestedModeId = this.requestedPermissionMode;
2401
- if (!requestedModeId || !modes || modes.currentModeId === requestedModeId) {
2382
+ async configureSessionMode(roomId, sessionId, modes, connection) {
2383
+ if (!this.resolveSessionMode || !modes) {
2402
2384
  return;
2403
2385
  }
2404
2386
  const availableModes = Array.isArray(modes.availableModes) ? modes.availableModes : [];
2405
- if (!availableModes.some((mode) => mode?.id === requestedModeId)) {
2406
- this.safeWarn("requested permission mode is not advertised by this session", {
2387
+ if (availableModes.length === 0) {
2388
+ return;
2389
+ }
2390
+ const selectedModeId = await this.resolveSessionModeManually(
2391
+ (signal) => this.resolveSessionMode({
2392
+ roomId,
2407
2393
  sessionId,
2408
- requestedModeId,
2394
+ currentModeId: modes.currentModeId,
2395
+ modes: availableModes
2396
+ }, signal),
2397
+ connection.signal
2398
+ );
2399
+ if (!selectedModeId || selectedModeId === modes.currentModeId) {
2400
+ return;
2401
+ }
2402
+ if (!availableModes.some((mode) => mode?.id === selectedModeId)) {
2403
+ this.safeWarn("resolveSessionMode selected a mode id this session does not advertise", {
2404
+ sessionId,
2405
+ selectedModeId,
2409
2406
  availableModeIds: availableModes.map((mode) => mode?.id)
2410
2407
  });
2411
2408
  return;
2412
2409
  }
2413
2410
  try {
2414
2411
  await withTimeout(
2415
- connection.setSessionMode({ sessionId, modeId: requestedModeId }),
2412
+ connection.setSessionMode({ sessionId, modeId: selectedModeId }),
2416
2413
  SET_SESSION_MODE_TIMEOUT_MS,
2417
2414
  `setSessionMode did not respond within ${SET_SESSION_MODE_TIMEOUT_MS}ms`
2418
2415
  );
2419
2416
  } catch (error) {
2420
- this.safeWarn("failed to switch session into the requested permission mode", {
2417
+ this.safeWarn("failed to switch session into the selected mode", {
2421
2418
  sessionId,
2422
- requestedModeId,
2419
+ selectedModeId,
2423
2420
  error: String(error)
2424
2421
  });
2425
2422
  }
2426
2423
  }
2424
+ async resolveSessionModeManually(resolver, signal) {
2425
+ const controller = new AbortController();
2426
+ const abort = () => controller.abort();
2427
+ signal.addEventListener("abort", abort, { once: true });
2428
+ let timer;
2429
+ try {
2430
+ const cancelled = new Promise((resolve) => {
2431
+ controller.signal.addEventListener("abort", () => resolve(void 0), { once: true });
2432
+ });
2433
+ const timeout = new Promise((resolve) => {
2434
+ timer = setTimeout(() => resolve(void 0), this.permissionTimeoutMs);
2435
+ });
2436
+ return await Promise.race([
2437
+ Promise.resolve().then(() => resolver(controller.signal)).catch((error) => {
2438
+ this.safeWarn("resolveSessionMode threw; preserving the harness default", { error: String(error) });
2439
+ return void 0;
2440
+ }),
2441
+ timeout,
2442
+ cancelled
2443
+ ]);
2444
+ } finally {
2445
+ clearTimeout(timer);
2446
+ signal.removeEventListener("abort", abort);
2447
+ controller.abort();
2448
+ }
2449
+ }
2450
+ async tryRestoreSession(connection, sessionId, mcpServers) {
2451
+ const capabilities = this.connectionState?.agentCapabilities;
2452
+ const params = { cwd: this.cwd, mcpServers, sessionId };
2453
+ const restore = capabilities?.loadSession ? () => connection.loadSession(params) : capabilities?.sessionCapabilities?.resume ? () => connection.unstable_resumeSession(params) : null;
2454
+ if (!restore) {
2455
+ return { ok: false };
2456
+ }
2457
+ try {
2458
+ const restored = await restore();
2459
+ return { ok: true, modes: restored?.modes };
2460
+ } catch {
2461
+ return { ok: false };
2462
+ }
2463
+ }
2427
2464
  async buildSessionMcpServers() {
2428
2465
  const mcpServers = [...this.mcpServers];
2429
2466
  if (!this.enableMcpTools) {
@@ -1,6 +1,6 @@
1
1
  export { A as A2AAdapter, a as A2AAdapterOptions, K as A2AClientFactory, M as A2AClientLike, b as A2AGatewayAdapter, c as AnthropicAdapter, d as AnthropicAdapterOptions, N as AnthropicClientFactory, Q as AnthropicToolCallingModel, R as AnthropicToolCallingModelOptions, C as CODEX_REASONING_EFFORTS, e as CODEX_REASONING_SUMMARIES, f as CODEX_WEB_SEARCH_MODES, g as ClaudePermissionMode, h as ClaudeSDKAdapter, i as ClaudeSDKAdapterOptions, S as ClaudeSDKQuery, U as ClaudeSDKQueryParams, j as CodexAdapter, k as CodexAdapterConfig, W as CodexAppServerStdioClient, l as CodexApprovalPolicy, X as CodexClientLike, Y as CodexJsonRpcError, m as CodexReasoningEffort, n as CodexReasoningSummary, o as CodexSandboxMode, p as CodexWebSearchMode, Z as DynamicToolCallParams, _ as DynamicToolCallResponse, $ as DynamicToolSpec, G as GeminiAdapter, q as GeminiAdapterOptions, a0 as GeminiClientFactory, a1 as GeminiToolCallingModel, a2 as GeminiToolCallingModelOptions, r as GenericAdapter, s as GenericAdapterHandler, t as GoogleADKAdapter, u as GoogleADKAdapterOptions, a3 as HttpOpencodeClient, a4 as HttpOpencodeClientOptions, a5 as HttpStatusError, L as LangGraphAdapter, v as LangGraphAdapterOptions, w as LangGraphGraph, x as LettaAdapter, y as LettaAdapterOptions, a6 as LettaAgentCreateParams, a7 as LettaClientFactory, a8 as LettaClientLike, a9 as LettaHistoryConverter, aa as LettaMessage, ab as LettaMessageCreateParams, ac as LettaMessages, ad as LettaRequestOptions, ae as LettaResponse, af as LettaResponseMessage, O as OpenAIAdapter, z as OpenAIAdapterOptions, ag as OpenAIClientFactory, ah as OpenAIToolCallingModel, ai as OpenAIToolCallingModelOptions, B as OpencodeAdapter, D as OpencodeAdapterConfig, E as OpencodeApprovalMode, F as OpencodeApprovalReply, aj as OpencodeClientLike, H as OpencodeQuestionMode, P as ParlantAdapter, I as ParlantAdapterOptions, ak as ParlantClientFactory, al as ParlantClientLike, am as ToolCall, an as ToolCallingAdapter, ao as ToolCallingAdapterOptions, T as ToolCallingModel, ap as ToolCallingModelRequest, aq as ToolCallingResponse, ar as ToolResult, as as TurnStartParams, V as VercelAISDKAdapter, J as VercelAISDKAdapterOptions, at as VercelAISDKToolCallingModel, au as VercelAISDKToolCallingModelOptions, av as runSingleToolRound } from './ClaudeSDKAdapter-Cx6zhSaG.cjs';
2
2
  import * as _agentclientprotocol_sdk from '@agentclientprotocol/sdk';
3
- import { Client, ClientSideConnection, McpServer, ClientCapabilities, RequestPermissionRequest, SessionModeId, SessionMode, AgentSideConnection, SessionModeState, Agent, InitializeResponse, Implementation, Stream, InitializeRequest, LoadSessionRequest, LoadSessionResponse, ListSessionsRequest, ListSessionsResponse, ForkSessionRequest, ForkSessionResponse, ResumeSessionRequest, ResumeSessionResponse, SetSessionModeRequest, SetSessionModeResponse, SetSessionModelRequest, SetSessionModelResponse, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, AuthenticateRequest, AuthenticateResponse, PromptRequest, PromptResponse } from '@agentclientprotocol/sdk';
3
+ import { Client, ClientSideConnection, McpServer, ClientCapabilities, RequestPermissionRequest, SessionMode, AgentSideConnection, SessionModeState, Agent, InitializeResponse, Implementation, Stream, InitializeRequest, LoadSessionRequest, LoadSessionResponse, ListSessionsRequest, ListSessionsResponse, ForkSessionRequest, ForkSessionResponse, ResumeSessionRequest, ResumeSessionResponse, SetSessionModeRequest, SetSessionModeResponse, SetSessionModelRequest, SetSessionModelResponse, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, AuthenticateRequest, AuthenticateResponse, PromptRequest, PromptResponse } from '@agentclientprotocol/sdk';
4
4
  import { A as ACPClientSessionState, a as ACPServerSessionState } from './acp-server-Dlj7D563.cjs';
5
5
  export { G as GatewayHistoryConverter } from './acp-server-Dlj7D563.cjs';
6
6
  import { S as SimpleAdapter } from './simpleAdapter-BpT4XbZC.cjs';
@@ -41,6 +41,12 @@ type ACPClientConnectionFactory = (client: Client, options: {
41
41
  env?: Record<string, string>;
42
42
  }) => Promise<ACPClientConnectionHandle>;
43
43
 
44
+ interface ACPModeRequest {
45
+ roomId: string;
46
+ sessionId: string;
47
+ currentModeId: string;
48
+ modes: readonly SessionMode[];
49
+ }
44
50
  interface ACPClientAdapterOptions {
45
51
  command: string | string[];
46
52
  cwd?: string;
@@ -54,7 +60,7 @@ interface ACPClientAdapterOptions {
54
60
  connectionFactory?: ACPClientConnectionFactory;
55
61
  resolvePermission?: (request: RequestPermissionRequest, signal: AbortSignal) => Promise<string | undefined>;
56
62
  permissionTimeoutMs?: number;
57
- requestedPermissionMode?: SessionModeId;
63
+ resolveSessionMode?: (request: ACPModeRequest, signal: AbortSignal) => Promise<string | undefined>;
58
64
  logger?: Logger;
59
65
  }
60
66
  declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, AdapterToolsProtocol> {
@@ -74,8 +80,8 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
74
80
  private readonly bootstrappedSessions;
75
81
  private readonly pendingPermissions;
76
82
  private readonly resolvePermission?;
83
+ private readonly resolveSessionMode?;
77
84
  private readonly permissionTimeoutMs;
78
- private readonly requestedPermissionMode?;
79
85
  private readonly logger;
80
86
  private backend;
81
87
  private backendPromise;
@@ -99,8 +105,9 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
99
105
  private ensureConnection;
100
106
  private spawnConnection;
101
107
  private getOrCreateSession;
108
+ private configureSessionMode;
109
+ private resolveSessionModeManually;
102
110
  private tryRestoreSession;
103
- private applyRequestedPermissionMode;
104
111
  private buildSessionMcpServers;
105
112
  private getOrCreateBackend;
106
113
  private createBackend;
@@ -1,6 +1,6 @@
1
1
  export { A as A2AAdapter, a as A2AAdapterOptions, K as A2AClientFactory, M as A2AClientLike, b as A2AGatewayAdapter, c as AnthropicAdapter, d as AnthropicAdapterOptions, N as AnthropicClientFactory, Q as AnthropicToolCallingModel, R as AnthropicToolCallingModelOptions, C as CODEX_REASONING_EFFORTS, e as CODEX_REASONING_SUMMARIES, f as CODEX_WEB_SEARCH_MODES, g as ClaudePermissionMode, h as ClaudeSDKAdapter, i as ClaudeSDKAdapterOptions, S as ClaudeSDKQuery, U as ClaudeSDKQueryParams, j as CodexAdapter, k as CodexAdapterConfig, W as CodexAppServerStdioClient, l as CodexApprovalPolicy, X as CodexClientLike, Y as CodexJsonRpcError, m as CodexReasoningEffort, n as CodexReasoningSummary, o as CodexSandboxMode, p as CodexWebSearchMode, Z as DynamicToolCallParams, _ as DynamicToolCallResponse, $ as DynamicToolSpec, G as GeminiAdapter, q as GeminiAdapterOptions, a0 as GeminiClientFactory, a1 as GeminiToolCallingModel, a2 as GeminiToolCallingModelOptions, r as GenericAdapter, s as GenericAdapterHandler, t as GoogleADKAdapter, u as GoogleADKAdapterOptions, a3 as HttpOpencodeClient, a4 as HttpOpencodeClientOptions, a5 as HttpStatusError, L as LangGraphAdapter, v as LangGraphAdapterOptions, w as LangGraphGraph, x as LettaAdapter, y as LettaAdapterOptions, a6 as LettaAgentCreateParams, a7 as LettaClientFactory, a8 as LettaClientLike, a9 as LettaHistoryConverter, aa as LettaMessage, ab as LettaMessageCreateParams, ac as LettaMessages, ad as LettaRequestOptions, ae as LettaResponse, af as LettaResponseMessage, O as OpenAIAdapter, z as OpenAIAdapterOptions, ag as OpenAIClientFactory, ah as OpenAIToolCallingModel, ai as OpenAIToolCallingModelOptions, B as OpencodeAdapter, D as OpencodeAdapterConfig, E as OpencodeApprovalMode, F as OpencodeApprovalReply, aj as OpencodeClientLike, H as OpencodeQuestionMode, P as ParlantAdapter, I as ParlantAdapterOptions, ak as ParlantClientFactory, al as ParlantClientLike, am as ToolCall, an as ToolCallingAdapter, ao as ToolCallingAdapterOptions, T as ToolCallingModel, ap as ToolCallingModelRequest, aq as ToolCallingResponse, ar as ToolResult, as as TurnStartParams, V as VercelAISDKAdapter, J as VercelAISDKAdapterOptions, at as VercelAISDKToolCallingModel, au as VercelAISDKToolCallingModelOptions, av as runSingleToolRound } from './ClaudeSDKAdapter-CXud2DBE.js';
2
2
  import * as _agentclientprotocol_sdk from '@agentclientprotocol/sdk';
3
- import { Client, ClientSideConnection, McpServer, ClientCapabilities, RequestPermissionRequest, SessionModeId, SessionMode, AgentSideConnection, SessionModeState, Agent, InitializeResponse, Implementation, Stream, InitializeRequest, LoadSessionRequest, LoadSessionResponse, ListSessionsRequest, ListSessionsResponse, ForkSessionRequest, ForkSessionResponse, ResumeSessionRequest, ResumeSessionResponse, SetSessionModeRequest, SetSessionModeResponse, SetSessionModelRequest, SetSessionModelResponse, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, AuthenticateRequest, AuthenticateResponse, PromptRequest, PromptResponse } from '@agentclientprotocol/sdk';
3
+ import { Client, ClientSideConnection, McpServer, ClientCapabilities, RequestPermissionRequest, SessionMode, AgentSideConnection, SessionModeState, Agent, InitializeResponse, Implementation, Stream, InitializeRequest, LoadSessionRequest, LoadSessionResponse, ListSessionsRequest, ListSessionsResponse, ForkSessionRequest, ForkSessionResponse, ResumeSessionRequest, ResumeSessionResponse, SetSessionModeRequest, SetSessionModeResponse, SetSessionModelRequest, SetSessionModelResponse, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, AuthenticateRequest, AuthenticateResponse, PromptRequest, PromptResponse } from '@agentclientprotocol/sdk';
4
4
  import { A as ACPClientSessionState, a as ACPServerSessionState } from './acp-server-CiUqN3G3.js';
5
5
  export { G as GatewayHistoryConverter } from './acp-server-CiUqN3G3.js';
6
6
  import { S as SimpleAdapter } from './simpleAdapter--wznuoOw.js';
@@ -41,6 +41,12 @@ type ACPClientConnectionFactory = (client: Client, options: {
41
41
  env?: Record<string, string>;
42
42
  }) => Promise<ACPClientConnectionHandle>;
43
43
 
44
+ interface ACPModeRequest {
45
+ roomId: string;
46
+ sessionId: string;
47
+ currentModeId: string;
48
+ modes: readonly SessionMode[];
49
+ }
44
50
  interface ACPClientAdapterOptions {
45
51
  command: string | string[];
46
52
  cwd?: string;
@@ -54,7 +60,7 @@ interface ACPClientAdapterOptions {
54
60
  connectionFactory?: ACPClientConnectionFactory;
55
61
  resolvePermission?: (request: RequestPermissionRequest, signal: AbortSignal) => Promise<string | undefined>;
56
62
  permissionTimeoutMs?: number;
57
- requestedPermissionMode?: SessionModeId;
63
+ resolveSessionMode?: (request: ACPModeRequest, signal: AbortSignal) => Promise<string | undefined>;
58
64
  logger?: Logger;
59
65
  }
60
66
  declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, AdapterToolsProtocol> {
@@ -74,8 +80,8 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
74
80
  private readonly bootstrappedSessions;
75
81
  private readonly pendingPermissions;
76
82
  private readonly resolvePermission?;
83
+ private readonly resolveSessionMode?;
77
84
  private readonly permissionTimeoutMs;
78
- private readonly requestedPermissionMode?;
79
85
  private readonly logger;
80
86
  private backend;
81
87
  private backendPromise;
@@ -99,8 +105,9 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
99
105
  private ensureConnection;
100
106
  private spawnConnection;
101
107
  private getOrCreateSession;
108
+ private configureSessionMode;
109
+ private resolveSessionModeManually;
102
110
  private tryRestoreSession;
103
- private applyRequestedPermissionMode;
104
111
  private buildSessionMcpServers;
105
112
  private getOrCreateBackend;
106
113
  private createBackend;
package/dist/adapters.js CHANGED
@@ -387,8 +387,8 @@ var ACPClientAdapter = class extends SimpleAdapter {
387
387
  bootstrappedSessions = /* @__PURE__ */ new Set();
388
388
  pendingPermissions = /* @__PURE__ */ new Map();
389
389
  resolvePermission;
390
+ resolveSessionMode;
390
391
  permissionTimeoutMs;
391
- requestedPermissionMode;
392
392
  logger;
393
393
  backend = null;
394
394
  backendPromise = null;
@@ -417,13 +417,10 @@ var ACPClientAdapter = class extends SimpleAdapter {
417
417
  this.clientCapabilities = options.clientCapabilities;
418
418
  this.connectionFactory = options.connectionFactory ?? createSubprocessConnection;
419
419
  this.resolvePermission = options.resolvePermission;
420
+ this.resolveSessionMode = options.resolveSessionMode;
420
421
  this.logger = options.logger ?? new NoopLogger();
421
422
  this.permissionTimeoutMs = options.permissionTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
422
- this.requestedPermissionMode = options.requestedPermissionMode;
423
- if (this.requestedPermissionMode !== void 0 && this.requestedPermissionMode.length === 0) {
424
- throw new ValidationError("requestedPermissionMode must be a non-empty mode id, got an empty string");
425
- }
426
- if (this.resolvePermission && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
423
+ if ((this.resolvePermission || this.resolveSessionMode) && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
427
424
  throw new ValidationError(`permissionTimeoutMs must be a positive finite number, got ${options.permissionTimeoutMs}`);
428
425
  }
429
426
  }
@@ -587,7 +584,7 @@ ${messageWithContext}`;
587
584
  if (restored.ok) {
588
585
  this.activeSessions.add(existingSessionId);
589
586
  this.bootstrappedSessions.add(existingSessionId);
590
- await this.applyRequestedPermissionMode(connection, existingSessionId, restored.modes);
587
+ await this.configureSessionMode(roomId, existingSessionId, restored.modes, connection);
591
588
  return existingSessionId;
592
589
  }
593
590
  }
@@ -597,53 +594,93 @@ ${messageWithContext}`;
597
594
  });
598
595
  this.roomToSession.set(roomId, created.sessionId);
599
596
  this.activeSessions.add(created.sessionId);
600
- await this.applyRequestedPermissionMode(connection, created.sessionId, created.modes);
597
+ await this.configureSessionMode(roomId, created.sessionId, created.modes, connection);
601
598
  return created.sessionId;
602
599
  }
603
- async tryRestoreSession(connection, sessionId, mcpServers) {
604
- const capabilities = this.connectionState?.agentCapabilities;
605
- const params = { cwd: this.cwd, mcpServers, sessionId };
606
- const restore = capabilities?.loadSession ? () => connection.loadSession(params) : capabilities?.sessionCapabilities?.resume ? () => connection.unstable_resumeSession(params) : null;
607
- if (!restore) {
608
- return { ok: false };
609
- }
610
- try {
611
- const restored = await restore();
612
- return { ok: true, modes: restored?.modes };
613
- } catch {
614
- return { ok: false };
615
- }
616
- }
617
600
  // Best-effort: never throws, so a mode switch going wrong can't take a
618
601
  // session establishment down with it.
619
- async applyRequestedPermissionMode(connection, sessionId, modes) {
620
- const requestedModeId = this.requestedPermissionMode;
621
- if (!requestedModeId || !modes || modes.currentModeId === requestedModeId) {
602
+ async configureSessionMode(roomId, sessionId, modes, connection) {
603
+ if (!this.resolveSessionMode || !modes) {
622
604
  return;
623
605
  }
624
606
  const availableModes = Array.isArray(modes.availableModes) ? modes.availableModes : [];
625
- if (!availableModes.some((mode) => mode?.id === requestedModeId)) {
626
- this.safeWarn("requested permission mode is not advertised by this session", {
607
+ if (availableModes.length === 0) {
608
+ return;
609
+ }
610
+ const selectedModeId = await this.resolveSessionModeManually(
611
+ (signal) => this.resolveSessionMode({
612
+ roomId,
627
613
  sessionId,
628
- requestedModeId,
614
+ currentModeId: modes.currentModeId,
615
+ modes: availableModes
616
+ }, signal),
617
+ connection.signal
618
+ );
619
+ if (!selectedModeId || selectedModeId === modes.currentModeId) {
620
+ return;
621
+ }
622
+ if (!availableModes.some((mode) => mode?.id === selectedModeId)) {
623
+ this.safeWarn("resolveSessionMode selected a mode id this session does not advertise", {
624
+ sessionId,
625
+ selectedModeId,
629
626
  availableModeIds: availableModes.map((mode) => mode?.id)
630
627
  });
631
628
  return;
632
629
  }
633
630
  try {
634
631
  await withTimeout(
635
- connection.setSessionMode({ sessionId, modeId: requestedModeId }),
632
+ connection.setSessionMode({ sessionId, modeId: selectedModeId }),
636
633
  SET_SESSION_MODE_TIMEOUT_MS,
637
634
  `setSessionMode did not respond within ${SET_SESSION_MODE_TIMEOUT_MS}ms`
638
635
  );
639
636
  } catch (error) {
640
- this.safeWarn("failed to switch session into the requested permission mode", {
637
+ this.safeWarn("failed to switch session into the selected mode", {
641
638
  sessionId,
642
- requestedModeId,
639
+ selectedModeId,
643
640
  error: String(error)
644
641
  });
645
642
  }
646
643
  }
644
+ async resolveSessionModeManually(resolver, signal) {
645
+ const controller = new AbortController();
646
+ const abort = () => controller.abort();
647
+ signal.addEventListener("abort", abort, { once: true });
648
+ let timer;
649
+ try {
650
+ const cancelled = new Promise((resolve) => {
651
+ controller.signal.addEventListener("abort", () => resolve(void 0), { once: true });
652
+ });
653
+ const timeout = new Promise((resolve) => {
654
+ timer = setTimeout(() => resolve(void 0), this.permissionTimeoutMs);
655
+ });
656
+ return await Promise.race([
657
+ Promise.resolve().then(() => resolver(controller.signal)).catch((error) => {
658
+ this.safeWarn("resolveSessionMode threw; preserving the harness default", { error: String(error) });
659
+ return void 0;
660
+ }),
661
+ timeout,
662
+ cancelled
663
+ ]);
664
+ } finally {
665
+ clearTimeout(timer);
666
+ signal.removeEventListener("abort", abort);
667
+ controller.abort();
668
+ }
669
+ }
670
+ async tryRestoreSession(connection, sessionId, mcpServers) {
671
+ const capabilities = this.connectionState?.agentCapabilities;
672
+ const params = { cwd: this.cwd, mcpServers, sessionId };
673
+ const restore = capabilities?.loadSession ? () => connection.loadSession(params) : capabilities?.sessionCapabilities?.resume ? () => connection.unstable_resumeSession(params) : null;
674
+ if (!restore) {
675
+ return { ok: false };
676
+ }
677
+ try {
678
+ const restored = await restore();
679
+ return { ok: true, modes: restored?.modes };
680
+ } catch {
681
+ return { ok: false };
682
+ }
683
+ }
647
684
  async buildSessionMcpServers() {
648
685
  const mcpServers = [...this.mcpServers];
649
686
  if (!this.enableMcpTools) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@band-ai/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Band TypeScript SDK core runtime",
5
5
  "license": "MIT",
6
6
  "repository": {