@band-ai/sdk 0.2.0 → 0.2.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
@@ -30,6 +30,38 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
+ // src/core/errors.ts
34
+ var BandSdkError, UnsupportedFeatureError, ValidationError, RuntimeStateError;
35
+ var init_errors = __esm({
36
+ "src/core/errors.ts"() {
37
+ "use strict";
38
+ BandSdkError = class extends Error {
39
+ constructor(message, cause) {
40
+ super(message, cause !== void 0 ? { cause } : void 0);
41
+ this.name = "BandSdkError";
42
+ }
43
+ };
44
+ UnsupportedFeatureError = class extends BandSdkError {
45
+ constructor(message) {
46
+ super(message);
47
+ this.name = "UnsupportedFeatureError";
48
+ }
49
+ };
50
+ ValidationError = class extends BandSdkError {
51
+ constructor(message, cause) {
52
+ super(message, cause);
53
+ this.name = "ValidationError";
54
+ }
55
+ };
56
+ RuntimeStateError = class extends BandSdkError {
57
+ constructor(message) {
58
+ super(message);
59
+ this.name = "RuntimeStateError";
60
+ }
61
+ };
62
+ }
63
+ });
64
+
33
65
  // src/contracts/memory.ts
34
66
  function namedValues(values) {
35
67
  return Object.fromEntries(values.map((value) => [value, value]));
@@ -108,38 +140,6 @@ var init_memory = __esm({
108
140
  }
109
141
  });
110
142
 
111
- // src/core/errors.ts
112
- var BandSdkError, UnsupportedFeatureError, ValidationError, RuntimeStateError;
113
- var init_errors = __esm({
114
- "src/core/errors.ts"() {
115
- "use strict";
116
- BandSdkError = class extends Error {
117
- constructor(message, cause) {
118
- super(message, cause !== void 0 ? { cause } : void 0);
119
- this.name = "BandSdkError";
120
- }
121
- };
122
- UnsupportedFeatureError = class extends BandSdkError {
123
- constructor(message) {
124
- super(message);
125
- this.name = "UnsupportedFeatureError";
126
- }
127
- };
128
- ValidationError = class extends BandSdkError {
129
- constructor(message, cause) {
130
- super(message, cause);
131
- this.name = "ValidationError";
132
- }
133
- };
134
- RuntimeStateError = class extends BandSdkError {
135
- constructor(message) {
136
- super(message);
137
- this.name = "RuntimeStateError";
138
- }
139
- };
140
- }
141
- });
142
-
143
143
  // src/contracts/chatEvents.ts
144
144
  var CHAT_EVENT_TYPES, CHAT_MESSAGE_TYPES;
145
145
  var init_chatEvents = __esm({
@@ -1066,6 +1066,18 @@ var ACPClientHistoryConverter = class {
1066
1066
  }
1067
1067
  };
1068
1068
 
1069
+ // src/core/logger.ts
1070
+ var noop = () => void 0;
1071
+ var NoopLogger = class {
1072
+ debug = noop;
1073
+ info = noop;
1074
+ warn = noop;
1075
+ error = noop;
1076
+ };
1077
+
1078
+ // src/adapters/acp/ACPClientAdapter.ts
1079
+ init_errors();
1080
+
1069
1081
  // src/runtime/prompts/base.ts
1070
1082
  var ENVIRONMENT_SECTION = `
1071
1083
  ## Environment
@@ -2016,6 +2028,7 @@ var acpModule = new LazyAsyncValue({
2016
2028
  });
2017
2029
 
2018
2030
  // src/adapters/acp/ACPClientAdapter.ts
2031
+ var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 6e4;
2019
2032
  var ACPClientAdapter = class extends SimpleAdapter {
2020
2033
  command;
2021
2034
  cwd;
@@ -2031,6 +2044,10 @@ var ACPClientAdapter = class extends SimpleAdapter {
2031
2044
  roomTools = /* @__PURE__ */ new Map();
2032
2045
  activeSessions = /* @__PURE__ */ new Set();
2033
2046
  bootstrappedSessions = /* @__PURE__ */ new Set();
2047
+ pendingPermissions = /* @__PURE__ */ new Map();
2048
+ resolvePermission;
2049
+ permissionTimeoutMs;
2050
+ logger;
2034
2051
  backend = null;
2035
2052
  backendPromise = null;
2036
2053
  client = null;
@@ -2057,6 +2074,12 @@ var ACPClientAdapter = class extends SimpleAdapter {
2057
2074
  this.additionalMcpTools = [...options.additionalMcpTools ?? []];
2058
2075
  this.clientCapabilities = options.clientCapabilities;
2059
2076
  this.connectionFactory = options.connectionFactory ?? createSubprocessConnection;
2077
+ this.resolvePermission = options.resolvePermission;
2078
+ this.logger = options.logger ?? new NoopLogger();
2079
+ this.permissionTimeoutMs = options.permissionTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
2080
+ if (this.resolvePermission && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
2081
+ throw new ValidationError(`permissionTimeoutMs must be a positive finite number, got ${options.permissionTimeoutMs}`);
2082
+ }
2060
2083
  }
2061
2084
  async onStarted(agentName, agentDescription) {
2062
2085
  await super.onStarted(agentName, agentDescription);
@@ -2122,6 +2145,7 @@ ${message.content}`;
2122
2145
  this.activeSessions.delete(sessionId);
2123
2146
  this.bootstrappedSessions.delete(sessionId);
2124
2147
  this.client?.setPermissionHandler(sessionId, void 0);
2148
+ this.cancelPendingPermissions(sessionId);
2125
2149
  }
2126
2150
  }
2127
2151
  async onRuntimeStop() {
@@ -2134,6 +2158,7 @@ ${message.content}`;
2134
2158
  this.bootstrappedSessions.clear();
2135
2159
  this.roomToSession.clear();
2136
2160
  this.roomTools.clear();
2161
+ this.cancelAllPendingPermissions();
2137
2162
  this.client = null;
2138
2163
  this.connection = null;
2139
2164
  if (this.backend) {
@@ -2356,28 +2381,94 @@ ${message.content}`;
2356
2381
  ].join("\n");
2357
2382
  }
2358
2383
  async handlePermissionRequest(tools, roomId, params) {
2359
- const selected = choosePermissionOption(params.options);
2360
2384
  const toolName = params.toolCall.title ?? "unknown";
2361
- await tools.sendEvent(`Permission requested: ${toolName}`, "tool_call", {
2362
- permission_request: true,
2363
- tool_name: toolName,
2364
- tool_call_id: params.toolCall.toolCallId,
2365
- acp_session_id: params.sessionId,
2366
- auto_allowed: selected !== null
2367
- });
2368
- if (!selected) {
2369
- return {
2370
- outcome: {
2371
- outcome: "cancelled"
2372
- }
2373
- };
2385
+ const autoSelection = this.resolvePermission ? void 0 : choosePermissionOption(params.options);
2386
+ const controller = this.resolvePermission ? new AbortController() : void 0;
2387
+ if (controller) {
2388
+ this.trackPending(params.sessionId, controller);
2389
+ }
2390
+ const [, chosenId] = await Promise.all([
2391
+ // This is the room's only "a permission request is pending" signal,
2392
+ // and the only one other room participants ever see — started
2393
+ // immediately rather than serialized in front of a manual wait that
2394
+ // can take up to `permissionTimeoutMs`.
2395
+ tools.sendEvent(`Permission requested: ${toolName}`, "tool_call", {
2396
+ permission_request: true,
2397
+ tool_name: toolName,
2398
+ tool_call_id: params.toolCall.toolCallId,
2399
+ acp_session_id: params.sessionId,
2400
+ auto_allowed: autoSelection !== void 0 && autoSelection !== null
2401
+ }),
2402
+ controller ? this.resolveManually(params.sessionId, params, controller) : Promise.resolve(autoSelection?.optionId)
2403
+ ]);
2404
+ return this.toResponse(chosenId, params.options);
2405
+ }
2406
+ // `undefined`, or an id absent from this request's own `options` (a buggy
2407
+ // or stale caller), both map to `cancelled` — never silently treated as a
2408
+ // deny. A real match, reject-kind options included, maps to `selected`.
2409
+ toResponse(chosenId, options) {
2410
+ const matched = chosenId !== void 0 && options.some((option) => option.optionId === chosenId);
2411
+ return matched ? { outcome: { outcome: "selected", optionId: chosenId } } : { outcome: { outcome: "cancelled" } };
2412
+ }
2413
+ // Races the caller-supplied resolver against a timeout and against
2414
+ // `controller`'s own abort signal — aborted externally by
2415
+ // `cancelPendingPermissions`/`cancelAllPendingPermissions` (fired from
2416
+ // `onCleanup`/`stop()` below) when a room or the whole adapter tears down
2417
+ // while this is still pending. `controller` is the same object tracked in
2418
+ // `pendingPermissions` by the caller, so there is exactly one cancellation
2419
+ // channel here, not a second hand-rolled one alongside it.
2420
+ async resolveManually(sessionId, params, controller) {
2421
+ let timer;
2422
+ const cancelled = new Promise((resolve) => {
2423
+ controller.signal.addEventListener("abort", () => resolve(void 0));
2424
+ });
2425
+ try {
2426
+ const timeout = new Promise((resolve) => {
2427
+ timer = setTimeout(() => resolve(void 0), this.permissionTimeoutMs);
2428
+ });
2429
+ return await Promise.race([
2430
+ // `resolvePermission` is caller-supplied; nothing guarantees it's
2431
+ // `async` or otherwise well-behaved. `Promise.resolve().then(...)`
2432
+ // normalizes a synchronous throw the same way it normalizes a
2433
+ // rejected promise, so both land in the `.catch` below rather than
2434
+ // escaping this race uncaught.
2435
+ Promise.resolve().then(() => this.resolvePermission(params, controller.signal)).catch((error) => {
2436
+ try {
2437
+ this.logger.warn("resolvePermission threw; treating as no answer", { error: String(error) });
2438
+ } catch {
2439
+ }
2440
+ return void 0;
2441
+ }),
2442
+ timeout,
2443
+ cancelled
2444
+ ]);
2445
+ } finally {
2446
+ clearTimeout(timer);
2447
+ this.untrackPending(sessionId, controller);
2448
+ controller.abort();
2449
+ }
2450
+ }
2451
+ trackPending(sessionId, controller) {
2452
+ const pending = this.pendingPermissions.get(sessionId) ?? /* @__PURE__ */ new Set();
2453
+ pending.add(controller);
2454
+ this.pendingPermissions.set(sessionId, pending);
2455
+ }
2456
+ untrackPending(sessionId, controller) {
2457
+ const pending = this.pendingPermissions.get(sessionId);
2458
+ pending?.delete(controller);
2459
+ if (pending?.size === 0) {
2460
+ this.pendingPermissions.delete(sessionId);
2461
+ }
2462
+ }
2463
+ cancelPendingPermissions(sessionId) {
2464
+ for (const controller of this.pendingPermissions.get(sessionId) ?? []) {
2465
+ controller.abort();
2466
+ }
2467
+ }
2468
+ cancelAllPendingPermissions() {
2469
+ for (const sessionId of this.pendingPermissions.keys()) {
2470
+ this.cancelPendingPermissions(sessionId);
2374
2471
  }
2375
- return {
2376
- outcome: {
2377
- outcome: "selected",
2378
- optionId: selected.optionId
2379
- }
2380
- };
2381
2472
  }
2382
2473
  async flushChunks(input) {
2383
2474
  const client = this.client;
@@ -3345,15 +3436,6 @@ function extractPromptText(prompt) {
3345
3436
  // src/adapters/tool-calling/ToolCallingAdapter.ts
3346
3437
  init_protocols();
3347
3438
 
3348
- // src/core/logger.ts
3349
- var noop = () => void 0;
3350
- var NoopLogger = class {
3351
- debug = noop;
3352
- info = noop;
3353
- warn = noop;
3354
- error = noop;
3355
- };
3356
-
3357
3439
  // src/runtime/formatters.ts
3358
3440
  function isMetadataMap(value) {
3359
3441
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -1,16 +1,16 @@
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-BY4zEbCl.cjs';
2
2
  import * as _agentclientprotocol_sdk from '@agentclientprotocol/sdk';
3
- import { Client, ClientSideConnection, McpServer, ClientCapabilities, 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';
7
+ import { L as Logger } from './logger-CABQOnlR.cjs';
7
8
  import { A as AdapterToolsProtocol, c as MessagingTools } from './protocols-Bz1rxcUg.cjs';
8
9
  import { P as PlatformMessage } from './types-D958TC71.cjs';
9
10
  import { M as McpToolRegistration } from './sdk-BTciSLRQ.cjs';
10
11
  import { R as RestApi } from './types-BgYFd_Yw.cjs';
11
12
  import { b as GatewayServerLike, c as GatewayServerOptions } from './opencode-2YdscIc1.cjs';
12
13
  export { A as A2AAuth, a as A2AGatewayAdapterOptions, d as A2AHistoryConverter, e as A2ASessionState, f as GatewayA2AMessage, g as GatewayA2AStatusUpdateEvent, h as GatewayCancelRequest, i as GatewayPeer, j as GatewayRequest, k as GatewayServerFactory, G as GatewaySessionState, l as GatewayTaskState, P as ParlantHistoryConverter, m as ParlantMessage, n as ParlantMessages, o as PendingA2ATask, p as buildA2AAuthHeaders } from './opencode-2YdscIc1.cjs';
13
- import './logger-CABQOnlR.cjs';
14
14
  import './customTools-BzF0IISO.cjs';
15
15
  import 'zod';
16
16
  import './dtos-B8KU_q4d.cjs';
@@ -52,6 +52,9 @@ interface ACPClientAdapterOptions {
52
52
  additionalMcpTools?: McpToolRegistration[];
53
53
  clientCapabilities?: ClientCapabilities;
54
54
  connectionFactory?: ACPClientConnectionFactory;
55
+ resolvePermission?: (request: RequestPermissionRequest, signal: AbortSignal) => Promise<string | undefined>;
56
+ permissionTimeoutMs?: number;
57
+ logger?: Logger;
55
58
  }
56
59
  declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, AdapterToolsProtocol> {
57
60
  private readonly command;
@@ -68,6 +71,10 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
68
71
  private readonly roomTools;
69
72
  private readonly activeSessions;
70
73
  private readonly bootstrappedSessions;
74
+ private readonly pendingPermissions;
75
+ private readonly resolvePermission?;
76
+ private readonly permissionTimeoutMs;
77
+ private readonly logger;
71
78
  private backend;
72
79
  private backendPromise;
73
80
  private client;
@@ -96,6 +103,12 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
96
103
  private createBackend;
97
104
  private buildSystemContext;
98
105
  private handlePermissionRequest;
106
+ private toResponse;
107
+ private resolveManually;
108
+ private trackPending;
109
+ private untrackPending;
110
+ private cancelPendingPermissions;
111
+ private cancelAllPendingPermissions;
99
112
  private flushChunks;
100
113
  }
101
114
 
@@ -1,16 +1,16 @@
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-Cna5P1dR.js';
2
2
  import * as _agentclientprotocol_sdk from '@agentclientprotocol/sdk';
3
- import { Client, ClientSideConnection, McpServer, ClientCapabilities, 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';
7
+ import { L as Logger } from './logger-CABQOnlR.js';
7
8
  import { A as AdapterToolsProtocol, c as MessagingTools } from './protocols-Bwwcql0h.js';
8
9
  import { P as PlatformMessage } from './types-BTN_ZETM.js';
9
10
  import { M as McpToolRegistration } from './sdk-qKJtMma_.js';
10
11
  import { R as RestApi } from './types-BivGO7I9.js';
11
12
  import { b as GatewayServerLike, c as GatewayServerOptions } from './opencode-Bld62x5t.js';
12
13
  export { A as A2AAuth, a as A2AGatewayAdapterOptions, d as A2AHistoryConverter, e as A2ASessionState, f as GatewayA2AMessage, g as GatewayA2AStatusUpdateEvent, h as GatewayCancelRequest, i as GatewayPeer, j as GatewayRequest, k as GatewayServerFactory, G as GatewaySessionState, l as GatewayTaskState, P as ParlantHistoryConverter, m as ParlantMessage, n as ParlantMessages, o as PendingA2ATask, p as buildA2AAuthHeaders } from './opencode-Bld62x5t.js';
13
- import './logger-CABQOnlR.js';
14
14
  import './customTools-BzF0IISO.js';
15
15
  import 'zod';
16
16
  import './dtos-B8KU_q4d.js';
@@ -52,6 +52,9 @@ interface ACPClientAdapterOptions {
52
52
  additionalMcpTools?: McpToolRegistration[];
53
53
  clientCapabilities?: ClientCapabilities;
54
54
  connectionFactory?: ACPClientConnectionFactory;
55
+ resolvePermission?: (request: RequestPermissionRequest, signal: AbortSignal) => Promise<string | undefined>;
56
+ permissionTimeoutMs?: number;
57
+ logger?: Logger;
55
58
  }
56
59
  declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, AdapterToolsProtocol> {
57
60
  private readonly command;
@@ -68,6 +71,10 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
68
71
  private readonly roomTools;
69
72
  private readonly activeSessions;
70
73
  private readonly bootstrappedSessions;
74
+ private readonly pendingPermissions;
75
+ private readonly resolvePermission?;
76
+ private readonly permissionTimeoutMs;
77
+ private readonly logger;
71
78
  private backend;
72
79
  private backendPromise;
73
80
  private client;
@@ -96,6 +103,12 @@ declare class ACPClientAdapter extends SimpleAdapter<ACPClientSessionState, Adap
96
103
  private createBackend;
97
104
  private buildSystemContext;
98
105
  private handlePermissionRequest;
106
+ private toResponse;
107
+ private resolveManually;
108
+ private trackPending;
109
+ private untrackPending;
110
+ private cancelPendingPermissions;
111
+ private cancelAllPendingPermissions;
99
112
  private flushChunks;
100
113
  }
101
114
 
package/dist/adapters.js CHANGED
@@ -63,9 +63,12 @@ import {
63
63
  } from "./chunk-RV76SKFT.js";
64
64
  import "./chunk-ZZJRRBPQ.js";
65
65
  import "./chunk-3BKAC4OZ.js";
66
- import "./chunk-OD2G5LFQ.js";
67
66
  import {
68
- UnsupportedFeatureError
67
+ NoopLogger
68
+ } from "./chunk-OD2G5LFQ.js";
69
+ import {
70
+ UnsupportedFeatureError,
71
+ ValidationError
69
72
  } from "./chunk-JY75VSQK.js";
70
73
 
71
74
  // src/adapters/acp/ACPClientAdapter.ts
@@ -344,6 +347,7 @@ var acpModule = new LazyAsyncValue({
344
347
  });
345
348
 
346
349
  // src/adapters/acp/ACPClientAdapter.ts
350
+ var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 6e4;
347
351
  var ACPClientAdapter = class extends SimpleAdapter {
348
352
  command;
349
353
  cwd;
@@ -359,6 +363,10 @@ var ACPClientAdapter = class extends SimpleAdapter {
359
363
  roomTools = /* @__PURE__ */ new Map();
360
364
  activeSessions = /* @__PURE__ */ new Set();
361
365
  bootstrappedSessions = /* @__PURE__ */ new Set();
366
+ pendingPermissions = /* @__PURE__ */ new Map();
367
+ resolvePermission;
368
+ permissionTimeoutMs;
369
+ logger;
362
370
  backend = null;
363
371
  backendPromise = null;
364
372
  client = null;
@@ -385,6 +393,12 @@ var ACPClientAdapter = class extends SimpleAdapter {
385
393
  this.additionalMcpTools = [...options.additionalMcpTools ?? []];
386
394
  this.clientCapabilities = options.clientCapabilities;
387
395
  this.connectionFactory = options.connectionFactory ?? createSubprocessConnection;
396
+ this.resolvePermission = options.resolvePermission;
397
+ this.logger = options.logger ?? new NoopLogger();
398
+ this.permissionTimeoutMs = options.permissionTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
399
+ if (this.resolvePermission && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
400
+ throw new ValidationError(`permissionTimeoutMs must be a positive finite number, got ${options.permissionTimeoutMs}`);
401
+ }
388
402
  }
389
403
  async onStarted(agentName, agentDescription) {
390
404
  await super.onStarted(agentName, agentDescription);
@@ -450,6 +464,7 @@ ${message.content}`;
450
464
  this.activeSessions.delete(sessionId);
451
465
  this.bootstrappedSessions.delete(sessionId);
452
466
  this.client?.setPermissionHandler(sessionId, void 0);
467
+ this.cancelPendingPermissions(sessionId);
453
468
  }
454
469
  }
455
470
  async onRuntimeStop() {
@@ -462,6 +477,7 @@ ${message.content}`;
462
477
  this.bootstrappedSessions.clear();
463
478
  this.roomToSession.clear();
464
479
  this.roomTools.clear();
480
+ this.cancelAllPendingPermissions();
465
481
  this.client = null;
466
482
  this.connection = null;
467
483
  if (this.backend) {
@@ -684,28 +700,94 @@ ${message.content}`;
684
700
  ].join("\n");
685
701
  }
686
702
  async handlePermissionRequest(tools, roomId, params) {
687
- const selected = choosePermissionOption(params.options);
688
703
  const toolName = params.toolCall.title ?? "unknown";
689
- await tools.sendEvent(`Permission requested: ${toolName}`, "tool_call", {
690
- permission_request: true,
691
- tool_name: toolName,
692
- tool_call_id: params.toolCall.toolCallId,
693
- acp_session_id: params.sessionId,
694
- auto_allowed: selected !== null
704
+ const autoSelection = this.resolvePermission ? void 0 : choosePermissionOption(params.options);
705
+ const controller = this.resolvePermission ? new AbortController() : void 0;
706
+ if (controller) {
707
+ this.trackPending(params.sessionId, controller);
708
+ }
709
+ const [, chosenId] = await Promise.all([
710
+ // This is the room's only "a permission request is pending" signal,
711
+ // and the only one other room participants ever see — started
712
+ // immediately rather than serialized in front of a manual wait that
713
+ // can take up to `permissionTimeoutMs`.
714
+ tools.sendEvent(`Permission requested: ${toolName}`, "tool_call", {
715
+ permission_request: true,
716
+ tool_name: toolName,
717
+ tool_call_id: params.toolCall.toolCallId,
718
+ acp_session_id: params.sessionId,
719
+ auto_allowed: autoSelection !== void 0 && autoSelection !== null
720
+ }),
721
+ controller ? this.resolveManually(params.sessionId, params, controller) : Promise.resolve(autoSelection?.optionId)
722
+ ]);
723
+ return this.toResponse(chosenId, params.options);
724
+ }
725
+ // `undefined`, or an id absent from this request's own `options` (a buggy
726
+ // or stale caller), both map to `cancelled` — never silently treated as a
727
+ // deny. A real match, reject-kind options included, maps to `selected`.
728
+ toResponse(chosenId, options) {
729
+ const matched = chosenId !== void 0 && options.some((option) => option.optionId === chosenId);
730
+ return matched ? { outcome: { outcome: "selected", optionId: chosenId } } : { outcome: { outcome: "cancelled" } };
731
+ }
732
+ // Races the caller-supplied resolver against a timeout and against
733
+ // `controller`'s own abort signal — aborted externally by
734
+ // `cancelPendingPermissions`/`cancelAllPendingPermissions` (fired from
735
+ // `onCleanup`/`stop()` below) when a room or the whole adapter tears down
736
+ // while this is still pending. `controller` is the same object tracked in
737
+ // `pendingPermissions` by the caller, so there is exactly one cancellation
738
+ // channel here, not a second hand-rolled one alongside it.
739
+ async resolveManually(sessionId, params, controller) {
740
+ let timer;
741
+ const cancelled = new Promise((resolve) => {
742
+ controller.signal.addEventListener("abort", () => resolve(void 0));
695
743
  });
696
- if (!selected) {
697
- return {
698
- outcome: {
699
- outcome: "cancelled"
700
- }
701
- };
744
+ try {
745
+ const timeout = new Promise((resolve) => {
746
+ timer = setTimeout(() => resolve(void 0), this.permissionTimeoutMs);
747
+ });
748
+ return await Promise.race([
749
+ // `resolvePermission` is caller-supplied; nothing guarantees it's
750
+ // `async` or otherwise well-behaved. `Promise.resolve().then(...)`
751
+ // normalizes a synchronous throw the same way it normalizes a
752
+ // rejected promise, so both land in the `.catch` below rather than
753
+ // escaping this race uncaught.
754
+ Promise.resolve().then(() => this.resolvePermission(params, controller.signal)).catch((error) => {
755
+ try {
756
+ this.logger.warn("resolvePermission threw; treating as no answer", { error: String(error) });
757
+ } catch {
758
+ }
759
+ return void 0;
760
+ }),
761
+ timeout,
762
+ cancelled
763
+ ]);
764
+ } finally {
765
+ clearTimeout(timer);
766
+ this.untrackPending(sessionId, controller);
767
+ controller.abort();
768
+ }
769
+ }
770
+ trackPending(sessionId, controller) {
771
+ const pending = this.pendingPermissions.get(sessionId) ?? /* @__PURE__ */ new Set();
772
+ pending.add(controller);
773
+ this.pendingPermissions.set(sessionId, pending);
774
+ }
775
+ untrackPending(sessionId, controller) {
776
+ const pending = this.pendingPermissions.get(sessionId);
777
+ pending?.delete(controller);
778
+ if (pending?.size === 0) {
779
+ this.pendingPermissions.delete(sessionId);
780
+ }
781
+ }
782
+ cancelPendingPermissions(sessionId) {
783
+ for (const controller of this.pendingPermissions.get(sessionId) ?? []) {
784
+ controller.abort();
785
+ }
786
+ }
787
+ cancelAllPendingPermissions() {
788
+ for (const sessionId of this.pendingPermissions.keys()) {
789
+ this.cancelPendingPermissions(sessionId);
702
790
  }
703
- return {
704
- outcome: {
705
- outcome: "selected",
706
- optionId: selected.optionId
707
- }
708
- };
709
791
  }
710
792
  async flushChunks(input) {
711
793
  const client = this.client;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@band-ai/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Band TypeScript SDK core runtime",
5
5
  "license": "MIT",
6
6
  "repository": {