@band-ai/sdk 0.4.2 → 0.4.4

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.cjs CHANGED
@@ -1070,7 +1070,9 @@ __export(src_exports, {
1070
1070
  ClaudeSDKAdapter: () => ClaudeSDKAdapter,
1071
1071
  CodexAdapter: () => CodexAdapter,
1072
1072
  CopilotACPAdapter: () => CopilotACPAdapter,
1073
+ CursorACPAdapter: () => CursorACPAdapter,
1073
1074
  DEFAULT_COPILOT_ACP_COMMAND: () => DEFAULT_COPILOT_ACP_COMMAND,
1075
+ DEFAULT_CURSOR_ACP_COMMAND: () => DEFAULT_CURSOR_ACP_COMMAND,
1074
1076
  DEFAULT_OMP_ACP_COMMAND: () => DEFAULT_OMP_ACP_COMMAND,
1075
1077
  DefaultPreprocessor: () => DefaultPreprocessor,
1076
1078
  DeliveryFailedError: () => DeliveryFailedError,
@@ -8342,15 +8344,20 @@ function systemUpdateParts(participantsMessage, contactsMessage) {
8342
8344
  }
8343
8345
  function buildConversationPrompt(options) {
8344
8346
  const parts = [];
8345
- if (options.isSessionBootstrap && options.history.length > 0) {
8346
- const historyText = options.history.raw.slice(-(options.maxHistoryMessages ?? 50)).map(formatHistoryLine).join("\n");
8347
- parts.push(`${options.historyHeader}
8347
+ if (options.isSessionBootstrap) {
8348
+ const historyText = options.history.raw.slice(-(options.maxHistoryMessages ?? 50)).filter(isTextHistoryEntry).map(formatHistoryLine).join("\n");
8349
+ if (historyText) {
8350
+ parts.push(`${options.historyHeader}
8348
8351
  ${historyText}`);
8352
+ }
8349
8353
  }
8350
8354
  parts.push(...systemUpdateParts(options.participantsMessage, options.contactsMessage));
8351
8355
  parts.push(options.currentMessage);
8352
8356
  return parts.join("\n\n");
8353
8357
  }
8358
+ function isTextHistoryEntry(entry) {
8359
+ return entry.message_type === void 0 || entry.message_type === "text";
8360
+ }
8354
8361
  function formatHistoryLine(entry) {
8355
8362
  const sender = String(entry.sender_name ?? entry.sender_type ?? "Unknown");
8356
8363
  const content = String(entry.content ?? "");
@@ -17255,23 +17262,17 @@ function asAcpJsonRpcError(error) {
17255
17262
  init_chatEvents();
17256
17263
  init_schemas();
17257
17264
 
17258
- // src/adapters/acp/types.ts
17259
- function choosePermissionOption(options) {
17260
- if (options.length === 0) {
17261
- return null;
17262
- }
17263
- return options.find((option) => option.kind === "allow_once") ?? options.find((option) => option.kind === "allow_always") ?? options[0];
17264
- }
17265
-
17266
17265
  // src/adapters/acp/client.ts
17267
17266
  var BandACPClient = class {
17268
17267
  sessionChunks = /* @__PURE__ */ new Map();
17269
17268
  permissionHandler;
17269
+ extensionHandler;
17270
17270
  // The handler is connection-scoped and required at construction, so it is
17271
17271
  // already in place before the agent process is spawned: there is no window
17272
17272
  // in which a `session/request_permission` has nowhere to go.
17273
- constructor(permissionHandler) {
17273
+ constructor(permissionHandler, extensionHandler) {
17274
17274
  this.permissionHandler = permissionHandler;
17275
+ this.extensionHandler = extensionHandler;
17275
17276
  }
17276
17277
  beginSession(sessionId) {
17277
17278
  this.sessionChunks.set(sessionId, []);
@@ -17316,62 +17317,26 @@ var BandACPClient = class {
17316
17317
  return chunks;
17317
17318
  }
17318
17319
  async extMethod(method, params) {
17319
- if (method === "cursor/ask_question") {
17320
- const options = Array.isArray(params.options) ? params.options : [];
17321
- const selected = choosePermissionOption(
17322
- options.filter((option) => !!option && typeof option === "object")
17323
- );
17324
- if (!selected) {
17325
- return {
17326
- outcome: {
17327
- type: "cancelled"
17328
- }
17329
- };
17330
- }
17331
- return {
17332
- outcome: {
17333
- type: "selected",
17334
- optionId: selected.optionId
17335
- }
17336
- };
17337
- }
17338
- if (method === "cursor/create_plan") {
17339
- return {
17340
- outcome: {
17341
- type: "approved"
17342
- }
17343
- };
17344
- }
17345
- return {};
17320
+ const result = await this.extensionHandler?.extMethod?.(
17321
+ method,
17322
+ params,
17323
+ { sessionId: sessionIdFrom(params) }
17324
+ );
17325
+ return result ?? {};
17346
17326
  }
17347
17327
  async extNotification(method, params) {
17348
- const sessionId = toOptionalString(params.sessionId) ?? toOptionalString(params.session_id);
17349
- if (!sessionId) {
17350
- return;
17351
- }
17352
- if (method === "cursor/update_todos") {
17353
- const todos = Array.isArray(params.todos) ? params.todos : [];
17354
- const lines = todos.filter((todo) => !!todo && typeof todo === "object").map((todo) => `- [${todo.completed === true ? "x" : " "}] ${String(todo.content ?? "")}`).filter((line) => line.trim().length > 0);
17355
- if (lines.length > 0) {
17356
- this.appendChunk(sessionId, {
17357
- chunkType: "plan",
17358
- content: lines.join("\n"),
17359
- metadata: {},
17360
- streamed: false
17361
- });
17362
- }
17328
+ const sessionId = sessionIdFrom(params);
17329
+ const chunks = await this.extensionHandler?.extNotification?.(
17330
+ method,
17331
+ params,
17332
+ { sessionId }
17333
+ );
17334
+ const targetSessionId = sessionId ?? this.extensionHandler?.extensionSessionId?.() ?? null;
17335
+ if (!targetSessionId || !chunks) {
17363
17336
  return;
17364
17337
  }
17365
- if (method === "cursor/task") {
17366
- const result = toOptionalString(params.result);
17367
- if (result) {
17368
- this.appendChunk(sessionId, {
17369
- chunkType: "text",
17370
- content: `[Task completed] ${result}`,
17371
- metadata: {},
17372
- streamed: false
17373
- });
17374
- }
17338
+ for (const chunk of chunks) {
17339
+ this.appendChunk(targetSessionId, chunk);
17375
17340
  }
17376
17341
  }
17377
17342
  appendChunk(sessionId, chunk) {
@@ -17496,6 +17461,17 @@ function extractTextFromContent(content) {
17496
17461
  function toOptionalString(value) {
17497
17462
  return typeof value === "string" && value.length > 0 ? value : null;
17498
17463
  }
17464
+ function sessionIdFrom(params) {
17465
+ return toOptionalString(params.sessionId) ?? toOptionalString(params.session_id);
17466
+ }
17467
+
17468
+ // src/adapters/acp/types.ts
17469
+ function choosePermissionOption(options) {
17470
+ if (options.length === 0) {
17471
+ return null;
17472
+ }
17473
+ return options.find((option) => option.kind === "allow_once") ?? options.find((option) => option.kind === "allow_always") ?? options[0];
17474
+ }
17499
17475
 
17500
17476
  // src/adapters/acp/loader.ts
17501
17477
  init_errors();
@@ -17542,6 +17518,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
17542
17518
  additionalMcpTools;
17543
17519
  clientCapabilities;
17544
17520
  connectionFactory;
17521
+ extensionHandler;
17545
17522
  tcpEndpoint;
17546
17523
  // The value's `generation` is the connection generation the session was
17547
17524
  // last established/restored against. `client` is the exact BandACPClient
@@ -17606,6 +17583,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
17606
17583
  this.additionalMcpTools = [...options.additionalMcpTools ?? []];
17607
17584
  this.clientCapabilities = options.clientCapabilities;
17608
17585
  this.connectionFactory = options.connectionFactory;
17586
+ this.extensionHandler = options.extensionHandler;
17609
17587
  this.tcpEndpoint = tcpEndpoint;
17610
17588
  this.resolvePermission = options.resolvePermission;
17611
17589
  this.resolveSessionMode = options.resolveSessionMode;
@@ -17661,6 +17639,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
17661
17639
  let client = null;
17662
17640
  let sessionId;
17663
17641
  let generation = 0;
17642
+ await this.onAcpTurnStarted(message, tools, context);
17664
17643
  try {
17665
17644
  const ensured = await this.ensureConnection();
17666
17645
  connection = ensured.connection;
@@ -17671,6 +17650,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
17671
17650
  }
17672
17651
  sessionId = await this.getOrCreateSession(context.roomId, connection, generation, client);
17673
17652
  const sessionKey = this.sessionKey(generation, sessionId);
17653
+ await this.onAcpSessionReady(message, tools, context, sessionId);
17674
17654
  client.beginSession(sessionId);
17675
17655
  const content = replaceUuidMentions(message.content, mentionSubjectsFromMetadata(message.metadata));
17676
17656
  const messageWithContext = [...systemUpdateParts(participantsMessage, contactsMessage), content].join("\n\n");
@@ -17741,8 +17721,16 @@ ${messageWithContext}`;
17741
17721
  ...configError ? { optionId: configError.optionId, selectedValue: configError.selectedValue } : {}
17742
17722
  }
17743
17723
  );
17724
+ } finally {
17725
+ await this.onAcpTurnFinished(message, tools, context);
17744
17726
  }
17745
17727
  }
17728
+ async onAcpTurnStarted(_message, _tools, _context) {
17729
+ }
17730
+ async onAcpTurnFinished(_message, _tools, _context) {
17731
+ }
17732
+ async onAcpSessionReady(_message, _tools, _context, _sessionId) {
17733
+ }
17746
17734
  // Best-effort: tells the agent to stop working on a turn Band has already
17747
17735
  // given up waiting for (the ACP client has no way to force it), evicts the
17748
17736
  // session so the room's next turn re-establishes rather than reuses it
@@ -17912,6 +17900,9 @@ ${messageWithContext}`;
17912
17900
  sessionKey(generation, sessionId) {
17913
17901
  return `${generation}:${sessionId}`;
17914
17902
  }
17903
+ roomIdForSession(sessionId) {
17904
+ return [...this.roomToSession.entries()].find(([, owner]) => owner.sessionId === sessionId)?.[0];
17905
+ }
17915
17906
  async ensureConnection() {
17916
17907
  if (this.connection && !this.connection.signal.aborted) {
17917
17908
  return { connection: this.connection, generation: this.connectionGeneration };
@@ -17948,7 +17939,10 @@ ${messageWithContext}`;
17948
17939
  throw new Error(CONNECTION_ATTEMPT_SUPERSEDED_ERROR);
17949
17940
  }
17950
17941
  const owner = { generation: -1 };
17951
- const client = new BandACPClient((params) => this.routePermissionRequest(params, owner.generation));
17942
+ const client = new BandACPClient(
17943
+ (params) => this.routePermissionRequest(params, owner.generation),
17944
+ this.extensionHandler
17945
+ );
17952
17946
  handle = await (this.connectionFactory ? this.connectionFactory(client, {
17953
17947
  command: this.command,
17954
17948
  cwd: this.cwd,
@@ -18804,6 +18798,358 @@ var CopilotACPAdapter = class extends ACPClientAdapter {
18804
18798
  }
18805
18799
  };
18806
18800
 
18801
+ // src/adapters/cursor-acp/CursorACPAdapter.ts
18802
+ var DEFAULT_CURSOR_ACP_COMMAND = ["agent", "acp"];
18803
+ var DEFAULT_CURSOR_DECISION_TIMEOUT_MS = 3e5;
18804
+ var DEFAULT_CURSOR_MAX_PENDING_DECISIONS = 10;
18805
+ var CursorExtensions = class {
18806
+ adapter = null;
18807
+ todosBySession = /* @__PURE__ */ new Map();
18808
+ bind(adapter) {
18809
+ this.adapter = adapter;
18810
+ }
18811
+ async resolvePermission(request, signal) {
18812
+ return this.adapter?.resolveCursorPermission(request, signal);
18813
+ }
18814
+ extensionSessionId() {
18815
+ return this.adapter?.extensionSessionId() ?? null;
18816
+ }
18817
+ async extMethod(method, params, context) {
18818
+ return this.adapter?.resolveExtension(method, params, context.sessionId) ?? null;
18819
+ }
18820
+ async extNotification(method, params, context) {
18821
+ const sessionId = context.sessionId ?? this.extensionSessionId();
18822
+ if (!sessionId) {
18823
+ return;
18824
+ }
18825
+ if (method === "cursor/update_todos") {
18826
+ const content = this.updateTodos(sessionId, params);
18827
+ if (!content) {
18828
+ return;
18829
+ }
18830
+ return [{ chunkType: "plan", content, metadata: { cursor_todos: true }, streamed: false }];
18831
+ }
18832
+ if (method === "cursor/task") {
18833
+ const description = stringValue(params.description);
18834
+ if (!description) {
18835
+ return [];
18836
+ }
18837
+ const subagentType = stringValue(params.subagentType) ?? "unspecified";
18838
+ const model = stringValue(params.model);
18839
+ const suffix = model ? ` (${model})` : "";
18840
+ return [{ chunkType: "plan", content: `[Cursor ${subagentType} task] ${description}${suffix}`, metadata: {}, streamed: false }];
18841
+ }
18842
+ if (method === "cursor/generate_image") {
18843
+ const description = stringValue(params.description);
18844
+ if (!description) {
18845
+ return [];
18846
+ }
18847
+ const filePath = stringValue(params.filePath);
18848
+ return [{ chunkType: "plan", content: `[Cursor generated image] ${description}${filePath ? ` \u2192 ${filePath}` : ""}`, metadata: {}, streamed: false }];
18849
+ }
18850
+ }
18851
+ forgetSession(sessionId) {
18852
+ this.todosBySession.delete(sessionId);
18853
+ }
18854
+ clearSessions() {
18855
+ this.todosBySession.clear();
18856
+ }
18857
+ updateTodos(sessionId, params) {
18858
+ const todos = parseTodos(params.todos);
18859
+ if (params.merge === true) {
18860
+ const current2 = this.todosBySession.get(sessionId) ?? /* @__PURE__ */ new Map();
18861
+ for (const todo of todos) {
18862
+ current2.set(todo.id, todo);
18863
+ }
18864
+ this.todosBySession.set(sessionId, current2);
18865
+ } else {
18866
+ this.todosBySession.set(sessionId, new Map(todos.map((todo) => [todo.id, todo])));
18867
+ }
18868
+ const current = this.todosBySession.get(sessionId);
18869
+ return current && current.size > 0 ? [...current.values()].map((todo) => `- [${todoMark(todo.status)}] ${todo.content}`).join("\n") : void 0;
18870
+ }
18871
+ };
18872
+ var CursorACPAdapter = class extends ACPClientAdapter {
18873
+ provider = "cursor-acp";
18874
+ approvalMode;
18875
+ questionMode;
18876
+ planMode;
18877
+ decisionTimeoutMs;
18878
+ maxPendingDecisions;
18879
+ authorizedSenders;
18880
+ decisionLogger;
18881
+ extensions;
18882
+ turns = /* @__PURE__ */ new Map();
18883
+ pending = /* @__PURE__ */ new Map();
18884
+ activeTurn = null;
18885
+ turnTail = Promise.resolve();
18886
+ constructor(options = {}) {
18887
+ const extensions = new CursorExtensions();
18888
+ validateOptions(options);
18889
+ const env = cursorEnv(options);
18890
+ super({
18891
+ ...options,
18892
+ env,
18893
+ command: options.command ?? [...DEFAULT_CURSOR_ACP_COMMAND],
18894
+ authMethod: "cursor_login",
18895
+ extensionHandler: extensions,
18896
+ resolvePermission: (request, signal) => extensions.resolvePermission(request, signal)
18897
+ });
18898
+ extensions.bind(this);
18899
+ this.extensions = extensions;
18900
+ this.approvalMode = options.approvalMode ?? "manual";
18901
+ this.questionMode = options.questionMode ?? "manual";
18902
+ this.planMode = options.planMode ?? "manual";
18903
+ this.decisionTimeoutMs = options.decisionTimeoutMs ?? DEFAULT_CURSOR_DECISION_TIMEOUT_MS;
18904
+ this.maxPendingDecisions = options.maxPendingDecisions ?? DEFAULT_CURSOR_MAX_PENDING_DECISIONS;
18905
+ this.authorizedSenders = options.decisionAuthorizedSenders ? new Set(options.decisionAuthorizedSenders) : null;
18906
+ this.decisionLogger = resolveLogger(options.logger);
18907
+ }
18908
+ async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
18909
+ if (await this.handleControl(message, tools, context.roomId)) {
18910
+ return;
18911
+ }
18912
+ await this.withCursorTurnLock(() => super.onMessage(message, tools, history, participantsMessage, contactsMessage, context));
18913
+ }
18914
+ async onAcpTurnStarted(message, tools, context) {
18915
+ const turn = { messageId: message.id, roomId: context.roomId, tools, requesterId: message.senderId };
18916
+ this.turns.set(context.roomId, turn);
18917
+ this.activeTurn = turn;
18918
+ }
18919
+ async onAcpSessionReady(message, _tools, context, sessionId) {
18920
+ const turn = this.turns.get(context.roomId);
18921
+ if (turn?.messageId === message.id) {
18922
+ turn.sessionId = sessionId;
18923
+ }
18924
+ }
18925
+ async onAcpTurnFinished(message, _tools, context) {
18926
+ const turn = this.turns.get(context.roomId);
18927
+ if (turn?.messageId === message.id) {
18928
+ this.turns.delete(context.roomId);
18929
+ this.cancelRoom(context.roomId);
18930
+ }
18931
+ if (this.activeTurn?.messageId === message.id) {
18932
+ this.activeTurn = null;
18933
+ }
18934
+ }
18935
+ async onCleanup(roomId) {
18936
+ const sessionId = this.turns.get(roomId)?.sessionId;
18937
+ this.cancelRoom(roomId);
18938
+ this.turns.delete(roomId);
18939
+ if (this.activeTurn?.roomId === roomId) {
18940
+ this.activeTurn = null;
18941
+ }
18942
+ await super.onCleanup(roomId);
18943
+ if (sessionId) {
18944
+ this.extensions.forgetSession(sessionId);
18945
+ }
18946
+ }
18947
+ async stop() {
18948
+ for (const decision of this.pending.values()) {
18949
+ decision.resolve(void 0);
18950
+ }
18951
+ this.pending.clear();
18952
+ this.turns.clear();
18953
+ this.activeTurn = null;
18954
+ this.extensions.clearSessions();
18955
+ await super.stop();
18956
+ }
18957
+ async resolveExtension(method, params, sessionId) {
18958
+ const roomId = sessionId ? this.roomIdForSession(sessionId) : this.activeTurn?.roomId;
18959
+ const turn = roomId ? this.turns.get(roomId) : void 0;
18960
+ if (!turn || sessionId && turn.sessionId !== sessionId) {
18961
+ return { outcome: { outcome: "cancelled" } };
18962
+ }
18963
+ if (method === "cursor/ask_question") {
18964
+ return this.resolveQuestion(turn.roomId, turn, params);
18965
+ }
18966
+ if (method === "cursor/create_plan") {
18967
+ return this.resolvePlan(turn.roomId, turn, params);
18968
+ }
18969
+ return {};
18970
+ }
18971
+ async resolveCursorPermission(request, signal) {
18972
+ if (this.approvalMode === "autoAccept") {
18973
+ return allowOption(request.options)?.optionId;
18974
+ }
18975
+ if (this.approvalMode === "autoDecline") {
18976
+ return void 0;
18977
+ }
18978
+ const turn = this.turns.get(request.roomId);
18979
+ if (!turn) {
18980
+ return void 0;
18981
+ }
18982
+ const options = request.options.map((option) => option.optionId);
18983
+ const token = await this.waitForDecision("permission", request.roomId, turn, /* @__PURE__ */ new Map([["permission", options]]), /* @__PURE__ */ new Set(), `Cursor needs permission. Reply \`/cursor select {token} option-id\` or \`/cursor deny {token}\`.`, signal);
18984
+ return typeof token === "string" && options.includes(token) ? token : void 0;
18985
+ }
18986
+ extensionSessionId() {
18987
+ return this.activeTurn?.sessionId ?? null;
18988
+ }
18989
+ async resolveQuestion(roomId, turn, params) {
18990
+ const questions = questionChoices(params.questions);
18991
+ if (questions.choices.size === 0) {
18992
+ return { outcome: { outcome: "cancelled" } };
18993
+ }
18994
+ if (this.questionMode === "autoCancel") {
18995
+ return { outcome: { outcome: "cancelled" } };
18996
+ }
18997
+ if (this.questionMode === "autoFirst") {
18998
+ return answered(Object.fromEntries([...questions.choices].map(([id, options]) => [id, [options[0]]])));
18999
+ }
19000
+ const result = await this.waitForDecision("question", roomId, turn, questions.choices, questions.multiSelect, "Cursor needs input. Reply `/cursor answer {token} question-id=option-id[,option-id] ...`.");
19001
+ return isRecord3(result) ? result : { outcome: { outcome: "cancelled" } };
19002
+ }
19003
+ async resolvePlan(roomId, turn, params) {
19004
+ if (this.planMode === "autoAccept") {
19005
+ return { outcome: { outcome: "accepted" } };
19006
+ }
19007
+ if (this.planMode === "autoDecline") {
19008
+ return { outcome: { outcome: "rejected" } };
19009
+ }
19010
+ const title = stringValue(params.title) ?? "Cursor plan";
19011
+ const result = await this.waitForDecision("plan", roomId, turn, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Set(), `${title} needs approval. Reply \`/cursor accept {token}\` or \`/cursor reject {token}\`.`);
19012
+ return isRecord3(result) ? result : { outcome: { outcome: "cancelled" } };
19013
+ }
19014
+ async waitForDecision(kind, roomId, turn, choices, multiSelect, prompt, signal) {
19015
+ if (this.pending.size >= this.maxPendingDecisions) {
19016
+ this.pending.values().next().value?.resolve(void 0);
19017
+ }
19018
+ const token = crypto.randomUUID().slice(0, 8);
19019
+ return new Promise((resolve) => {
19020
+ const timer = setTimeout(() => settle(void 0), this.decisionTimeoutMs);
19021
+ const abort = () => settle(void 0);
19022
+ const settle = (value) => {
19023
+ clearTimeout(timer);
19024
+ signal?.removeEventListener("abort", abort);
19025
+ this.pending.delete(token);
19026
+ resolve(value);
19027
+ };
19028
+ this.pending.set(token, { kind, roomId, choices, multiSelect, resolve: settle });
19029
+ signal?.addEventListener("abort", abort, { once: true });
19030
+ void turn.tools.sendMessage(prompt.replaceAll("{token}", token), [turn.requesterId]).catch((error) => {
19031
+ this.decisionLogger.warn("cursor_acp.decision_prompt_delivery_failed", { roomId, kind, error: String(error) });
19032
+ settle(void 0);
19033
+ });
19034
+ });
19035
+ }
19036
+ async handleControl(message, tools, roomId) {
19037
+ const words = message.content.trim().split(/\s+/);
19038
+ if (words[0]?.toLowerCase() !== "/cursor") {
19039
+ return false;
19040
+ }
19041
+ if (words.length === 1 || words[1]?.toLowerCase() === "decisions") {
19042
+ const entries = [...this.pending.entries()].filter(([, decision2]) => decision2.roomId === roomId).map(([token2, decision2]) => `\`${token2}\` (${decision2.kind})`);
19043
+ await tools.sendMessage(`Pending Cursor decisions: ${entries.join(", ") || "none"}`);
19044
+ return true;
19045
+ }
19046
+ const [_, action, token, ...args] = words;
19047
+ const decision = token ? this.pending.get(token) : void 0;
19048
+ if (!decision || decision.roomId !== roomId) {
19049
+ await tools.sendMessage(`Cursor decision \`${token ?? ""}\` is not pending.`);
19050
+ return true;
19051
+ }
19052
+ if (this.authorizedSenders && !this.authorizedSenders.has(message.senderId)) {
19053
+ await tools.sendMessage("You are not authorized to resolve Cursor decisions.");
19054
+ return true;
19055
+ }
19056
+ const result = commandResult(action ?? "", args, decision);
19057
+ if (result === null) {
19058
+ await tools.sendMessage(`That command is not valid for Cursor ${decision.kind} decision \`${token}\`.`);
19059
+ return true;
19060
+ }
19061
+ decision.resolve(result);
19062
+ await tools.sendMessage(`Cursor ${decision.kind} decision \`${token}\` resolved.`);
19063
+ return true;
19064
+ }
19065
+ cancelRoom(roomId) {
19066
+ for (const [token, decision] of this.pending) {
19067
+ if (decision.roomId === roomId) {
19068
+ decision.resolve(void 0);
19069
+ this.pending.delete(token);
19070
+ }
19071
+ }
19072
+ }
19073
+ async withCursorTurnLock(run) {
19074
+ const queued = this.turnTail.then(run, run);
19075
+ this.turnTail = queued.then(() => void 0, () => void 0);
19076
+ return queued;
19077
+ }
19078
+ };
19079
+ function cursorEnv(options) {
19080
+ const env = { ...options.env };
19081
+ if (options.apiKey) env.CURSOR_API_KEY ??= options.apiKey;
19082
+ if (options.authToken) env.CURSOR_AUTH_TOKEN ??= options.authToken;
19083
+ return Object.keys(env).length > 0 ? env : void 0;
19084
+ }
19085
+ function validateOptions(options) {
19086
+ if (options.apiKey && options.authToken) throw new Error("set either apiKey or authToken, not both");
19087
+ if (Array.isArray(options.command) && options.command.length === 0) throw new Error("Cursor ACP command must not be empty");
19088
+ if (options.decisionTimeoutMs !== void 0 && (!Number.isFinite(options.decisionTimeoutMs) || options.decisionTimeoutMs <= 0)) throw new Error("decisionTimeoutMs must be a positive finite number");
19089
+ if (options.maxPendingDecisions !== void 0 && (!Number.isInteger(options.maxPendingDecisions) || options.maxPendingDecisions <= 0)) throw new Error("maxPendingDecisions must be a positive integer");
19090
+ }
19091
+ function allowOption(options) {
19092
+ return options.find((option) => option.kind === "allow_once") ?? options.find((option) => option.kind === "allow_always");
19093
+ }
19094
+ function questionChoices(value) {
19095
+ const choices = /* @__PURE__ */ new Map();
19096
+ const multiSelect = /* @__PURE__ */ new Set();
19097
+ if (!Array.isArray(value)) return { choices, multiSelect };
19098
+ for (const question of value) {
19099
+ if (!isRecord3(question) || typeof question.id !== "string" || !Array.isArray(question.options)) continue;
19100
+ const options = question.options.filter(isRecord3).map((option) => stringValue(option.id)).filter((id) => !!id);
19101
+ if (options.length === 0) continue;
19102
+ choices.set(question.id, options);
19103
+ if (question.allowMultiple === true) multiSelect.add(question.id);
19104
+ }
19105
+ return { choices, multiSelect };
19106
+ }
19107
+ function commandResult(action, args, decision) {
19108
+ if (decision.kind === "permission") return action === "deny" ? void 0 : action === "select" && args.length === 1 && decision.choices.get("permission")?.includes(args[0] ?? "") ? args[0] : null;
19109
+ if (decision.kind === "plan") return action === "accept" ? { outcome: { outcome: "accepted" } } : action === "reject" ? { outcome: { outcome: "rejected" } } : null;
19110
+ if (action !== "answer") return null;
19111
+ const selected = {};
19112
+ for (const argument of args) {
19113
+ const [id, raw] = argument.split("=", 2);
19114
+ const values = raw?.split(",") ?? [];
19115
+ const offered = id ? decision.choices.get(id) : void 0;
19116
+ if (!id || !offered || selected[id] || values.length === 0 || values.length > 1 && !decision.multiSelect.has(id) || values.some((value) => !offered.includes(value))) return null;
19117
+ selected[id] = values;
19118
+ }
19119
+ return Object.keys(selected).length === decision.choices.size ? answered(selected) : null;
19120
+ }
19121
+ function answered(selected) {
19122
+ return { outcome: { outcome: "answered", answers: Object.entries(selected).map(([questionId, selectedOptionIds]) => ({ questionId, selectedOptionIds })) } };
19123
+ }
19124
+ function parseTodos(value) {
19125
+ if (!Array.isArray(value)) return [];
19126
+ return value.flatMap((todo) => {
19127
+ if (!isRecord3(todo)) return [];
19128
+ const id = stringValue(todo.id);
19129
+ const content = stringValue(todo.content);
19130
+ const status = stringValue(todo.status);
19131
+ return id && content && status ? [{ id, content, status }] : [];
19132
+ });
19133
+ }
19134
+ function todoMark(status) {
19135
+ switch (status) {
19136
+ case "completed":
19137
+ return "x";
19138
+ case "in_progress":
19139
+ return "~";
19140
+ case "cancelled":
19141
+ return "-";
19142
+ default:
19143
+ return " ";
19144
+ }
19145
+ }
19146
+ function isRecord3(value) {
19147
+ return !!value && typeof value === "object" && !Array.isArray(value);
19148
+ }
19149
+ function stringValue(value) {
19150
+ return typeof value === "string" && value.length > 0 ? value : void 0;
19151
+ }
19152
+
18807
19153
  // src/core/index.ts
18808
19154
  var import_band_sdk_core13 = require("@band-ai/band-sdk-core");
18809
19155
  init_protocols();
@@ -18828,7 +19174,9 @@ init_schemas();
18828
19174
  ClaudeSDKAdapter,
18829
19175
  CodexAdapter,
18830
19176
  CopilotACPAdapter,
19177
+ CursorACPAdapter,
18831
19178
  DEFAULT_COPILOT_ACP_COMMAND,
19179
+ DEFAULT_CURSOR_ACP_COMMAND,
18832
19180
  DEFAULT_OMP_ACP_COMMAND,
18833
19181
  DefaultPreprocessor,
18834
19182
  DeliveryFailedError,
package/dist/index.d.cts CHANGED
@@ -9,7 +9,7 @@ import { P as PlatformMessage } from './types-DtcOLALn.cjs';
9
9
  export { A as AgentConfig, a as AgentInput, C as ContactEvent, b as ContactEventCallback, c as ContactEventConfig, d as ContactEventStrategy, e as ConversationContext, H as HistoryProvider, M as MessageHandler, f as PlatformEvent, R as ReconnectedEvent, S as SessionConfig } from './types-DtcOLALn.cjs';
10
10
  export { W as WebSocketConflictPolicy, a as WebSocketDisconnectError, b as WebSocketDisconnectReason } from './disconnectReason-Cctmg1SN.cjs';
11
11
  export { C as CustomToolDef } from './customTools-Bfecd0mJ.cjs';
12
- export { A as A2AAdapter, a as A2AAdapterOptions, b as A2AGatewayAdapter, c as ACPClientAdapter, d as ACPClientAdapterBaseOptions, e as ACPClientAdapterOptions, f as ACPClientStdioOptions, g as ACPClientTcpOptions, h as ACPConfigRequest, i as ACPConfigSelections, j as AcpSessionConfigError, k as AnthropicAdapter, l as AnthropicAdapterOptions, C as CODEX_REASONING_EFFORTS, m as CODEX_REASONING_SUMMARIES, n as CODEX_WEB_SEARCH_MODES, o as ClaudePermissionMode, p as ClaudeSDKAdapter, q as ClaudeSDKAdapterOptions, r as CodexAdapter, s as CodexAdapterConfig, t as CodexApprovalPolicy, u as CodexReasoningEffort, v as CodexReasoningSummary, w as CodexSandboxMode, x as CodexWebSearchMode, y as CopilotACPAdapter, z as CopilotACPAdapterOptions, B as CopilotACPStdioOptions, D as CopilotACPTcpOptions, E as DEFAULT_COPILOT_ACP_COMMAND, F as DEFAULT_OMP_ACP_COMMAND, G as FAILURE_CODE_SESSION_CONFIG, H as GeminiAdapter, I as GeminiAdapterOptions, J as GenericAdapter, K as GenericAdapterHandler, L as GoogleADKAdapter, M as GoogleADKAdapterOptions, N as LangGraphAdapter, O as LangGraphAdapterOptions, P as LangGraphGraph, Q as LettaAdapter, R as LettaAdapterOptions, S as MISSING_CONFIG_OPTIONS_REASON, T as OmpACPAdapter, U as OmpACPAdapterOptions, V as OpenAIAdapter, W as OpenAIAdapterOptions, X as OpencodeAdapter, Y as OpencodeAdapterConfig, Z as OpencodeApprovalMode, _ as OpencodeApprovalReply, $ as OpencodeQuestionMode, a0 as ParlantAdapter, a1 as ParlantAdapterOptions, a2 as ToolCallingModel, a3 as VercelAISDKAdapter, a4 as VercelAISDKAdapterOptions, a5 as applySessionConfigSelections } from './CopilotACPAdapter-HRvat7CP.cjs';
12
+ export { A as A2AAdapter, a as A2AAdapterOptions, b as A2AGatewayAdapter, c as ACPClientAdapter, d as ACPClientAdapterBaseOptions, e as ACPClientAdapterOptions, f as ACPClientStdioOptions, g as ACPClientTcpOptions, h as ACPConfigRequest, i as ACPConfigSelections, j as AcpSessionConfigError, k as AnthropicAdapter, l as AnthropicAdapterOptions, C as CODEX_REASONING_EFFORTS, m as CODEX_REASONING_SUMMARIES, n as CODEX_WEB_SEARCH_MODES, o as ClaudePermissionMode, p as ClaudeSDKAdapter, q as ClaudeSDKAdapterOptions, r as CodexAdapter, s as CodexAdapterConfig, t as CodexApprovalPolicy, u as CodexReasoningEffort, v as CodexReasoningSummary, w as CodexSandboxMode, x as CodexWebSearchMode, y as CopilotACPAdapter, z as CopilotACPAdapterOptions, B as CopilotACPStdioOptions, D as CopilotACPTcpOptions, E as CursorACPAdapter, F as CursorACPAdapterOptions, G as CursorApprovalMode, H as CursorPlanMode, I as CursorQuestionMode, J as DEFAULT_COPILOT_ACP_COMMAND, K as DEFAULT_CURSOR_ACP_COMMAND, L as DEFAULT_OMP_ACP_COMMAND, M as FAILURE_CODE_SESSION_CONFIG, N as GeminiAdapter, O as GeminiAdapterOptions, P as GenericAdapter, Q as GenericAdapterHandler, R as GoogleADKAdapter, S as GoogleADKAdapterOptions, T as LangGraphAdapter, U as LangGraphAdapterOptions, V as LangGraphGraph, W as LettaAdapter, X as LettaAdapterOptions, Y as MISSING_CONFIG_OPTIONS_REASON, Z as OmpACPAdapter, _ as OmpACPAdapterOptions, $ as OpenAIAdapter, a0 as OpenAIAdapterOptions, a1 as OpencodeAdapter, a2 as OpencodeAdapterConfig, a3 as OpencodeApprovalMode, a4 as OpencodeApprovalReply, a5 as OpencodeQuestionMode, a6 as ParlantAdapter, a7 as ParlantAdapterOptions, a8 as ToolCallingModel, a9 as VercelAISDKAdapter, aa as VercelAISDKAdapterOptions, ab as applySessionConfigSelections } from './CursorACPAdapter-_YhrXRWq.cjs';
13
13
  export { S as SimpleAdapter } from './simpleAdapter-D75rcz9n.cjs';
14
14
  export { A as A2AAuth, a as A2AGatewayAdapterOptions } from './acp-client-D-I_5lK-.cjs';
15
15
  export { AgentFailure } from '@band-ai/band-sdk-core';
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ import { P as PlatformMessage } from './types-CKU1N0SK.js';
9
9
  export { A as AgentConfig, a as AgentInput, C as ContactEvent, b as ContactEventCallback, c as ContactEventConfig, d as ContactEventStrategy, e as ConversationContext, H as HistoryProvider, M as MessageHandler, f as PlatformEvent, R as ReconnectedEvent, S as SessionConfig } from './types-CKU1N0SK.js';
10
10
  export { W as WebSocketConflictPolicy, a as WebSocketDisconnectError, b as WebSocketDisconnectReason } from './disconnectReason-Cctmg1SN.js';
11
11
  export { C as CustomToolDef } from './customTools-Bfecd0mJ.js';
12
- export { A as A2AAdapter, a as A2AAdapterOptions, b as A2AGatewayAdapter, c as ACPClientAdapter, d as ACPClientAdapterBaseOptions, e as ACPClientAdapterOptions, f as ACPClientStdioOptions, g as ACPClientTcpOptions, h as ACPConfigRequest, i as ACPConfigSelections, j as AcpSessionConfigError, k as AnthropicAdapter, l as AnthropicAdapterOptions, C as CODEX_REASONING_EFFORTS, m as CODEX_REASONING_SUMMARIES, n as CODEX_WEB_SEARCH_MODES, o as ClaudePermissionMode, p as ClaudeSDKAdapter, q as ClaudeSDKAdapterOptions, r as CodexAdapter, s as CodexAdapterConfig, t as CodexApprovalPolicy, u as CodexReasoningEffort, v as CodexReasoningSummary, w as CodexSandboxMode, x as CodexWebSearchMode, y as CopilotACPAdapter, z as CopilotACPAdapterOptions, B as CopilotACPStdioOptions, D as CopilotACPTcpOptions, E as DEFAULT_COPILOT_ACP_COMMAND, F as DEFAULT_OMP_ACP_COMMAND, G as FAILURE_CODE_SESSION_CONFIG, H as GeminiAdapter, I as GeminiAdapterOptions, J as GenericAdapter, K as GenericAdapterHandler, L as GoogleADKAdapter, M as GoogleADKAdapterOptions, N as LangGraphAdapter, O as LangGraphAdapterOptions, P as LangGraphGraph, Q as LettaAdapter, R as LettaAdapterOptions, S as MISSING_CONFIG_OPTIONS_REASON, T as OmpACPAdapter, U as OmpACPAdapterOptions, V as OpenAIAdapter, W as OpenAIAdapterOptions, X as OpencodeAdapter, Y as OpencodeAdapterConfig, Z as OpencodeApprovalMode, _ as OpencodeApprovalReply, $ as OpencodeQuestionMode, a0 as ParlantAdapter, a1 as ParlantAdapterOptions, a2 as ToolCallingModel, a3 as VercelAISDKAdapter, a4 as VercelAISDKAdapterOptions, a5 as applySessionConfigSelections } from './CopilotACPAdapter-Bj6NRdqd.js';
12
+ export { A as A2AAdapter, a as A2AAdapterOptions, b as A2AGatewayAdapter, c as ACPClientAdapter, d as ACPClientAdapterBaseOptions, e as ACPClientAdapterOptions, f as ACPClientStdioOptions, g as ACPClientTcpOptions, h as ACPConfigRequest, i as ACPConfigSelections, j as AcpSessionConfigError, k as AnthropicAdapter, l as AnthropicAdapterOptions, C as CODEX_REASONING_EFFORTS, m as CODEX_REASONING_SUMMARIES, n as CODEX_WEB_SEARCH_MODES, o as ClaudePermissionMode, p as ClaudeSDKAdapter, q as ClaudeSDKAdapterOptions, r as CodexAdapter, s as CodexAdapterConfig, t as CodexApprovalPolicy, u as CodexReasoningEffort, v as CodexReasoningSummary, w as CodexSandboxMode, x as CodexWebSearchMode, y as CopilotACPAdapter, z as CopilotACPAdapterOptions, B as CopilotACPStdioOptions, D as CopilotACPTcpOptions, E as CursorACPAdapter, F as CursorACPAdapterOptions, G as CursorApprovalMode, H as CursorPlanMode, I as CursorQuestionMode, J as DEFAULT_COPILOT_ACP_COMMAND, K as DEFAULT_CURSOR_ACP_COMMAND, L as DEFAULT_OMP_ACP_COMMAND, M as FAILURE_CODE_SESSION_CONFIG, N as GeminiAdapter, O as GeminiAdapterOptions, P as GenericAdapter, Q as GenericAdapterHandler, R as GoogleADKAdapter, S as GoogleADKAdapterOptions, T as LangGraphAdapter, U as LangGraphAdapterOptions, V as LangGraphGraph, W as LettaAdapter, X as LettaAdapterOptions, Y as MISSING_CONFIG_OPTIONS_REASON, Z as OmpACPAdapter, _ as OmpACPAdapterOptions, $ as OpenAIAdapter, a0 as OpenAIAdapterOptions, a1 as OpencodeAdapter, a2 as OpencodeAdapterConfig, a3 as OpencodeApprovalMode, a4 as OpencodeApprovalReply, a5 as OpencodeQuestionMode, a6 as ParlantAdapter, a7 as ParlantAdapterOptions, a8 as ToolCallingModel, a9 as VercelAISDKAdapter, aa as VercelAISDKAdapterOptions, ab as applySessionConfigSelections } from './CursorACPAdapter-7-Jtl1LV.js';
13
13
  export { S as SimpleAdapter } from './simpleAdapter-eLvsAQNo.js';
14
14
  export { A as A2AAuth, a as A2AGatewayAdapterOptions } from './acp-client-DUXyczlF.js';
15
15
  export { AgentFailure } from '@band-ai/band-sdk-core';
package/dist/index.js CHANGED
@@ -8,7 +8,9 @@ import {
8
8
  ClaudeSDKAdapter,
9
9
  CodexAdapter,
10
10
  CopilotACPAdapter,
11
+ CursorACPAdapter,
11
12
  DEFAULT_COPILOT_ACP_COMMAND,
13
+ DEFAULT_CURSOR_ACP_COMMAND,
12
14
  DEFAULT_OMP_ACP_COMMAND,
13
15
  FAILURE_CODE_SESSION_CONFIG,
14
16
  GeminiAdapter,
@@ -22,7 +24,7 @@ import {
22
24
  OpencodeAdapter,
23
25
  VercelAISDKAdapter,
24
26
  applySessionConfigSelections
25
- } from "./chunk-KXLV645N.js";
27
+ } from "./chunk-STUSNTHF.js";
26
28
  import "./chunk-UL3Y5C4J.js";
27
29
  import "./chunk-JDW5WSGF.js";
28
30
  import {
@@ -283,7 +285,9 @@ export {
283
285
  ClaudeSDKAdapter,
284
286
  CodexAdapter,
285
287
  CopilotACPAdapter,
288
+ CursorACPAdapter,
286
289
  DEFAULT_COPILOT_ACP_COMMAND,
290
+ DEFAULT_CURSOR_ACP_COMMAND,
287
291
  DEFAULT_OMP_ACP_COMMAND,
288
292
  DefaultPreprocessor,
289
293
  DeliveryFailedError,