@band-ai/sdk 0.4.2 → 0.4.3
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/{CopilotACPAdapter-Bj6NRdqd.d.ts → CursorACPAdapter-7-Jtl1LV.d.ts} +90 -1
- package/dist/{CopilotACPAdapter-HRvat7CP.d.cts → CursorACPAdapter-_YhrXRWq.d.cts} +90 -1
- package/dist/adapters.cjs +468 -125
- package/dist/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/adapters.js +5 -1
- package/dist/{chunk-KXLV645N.js → chunk-VHYPQAQ5.js} +455 -114
- package/dist/index.cjs +405 -62
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/package.json +1 -1
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,
|
|
@@ -17255,23 +17257,17 @@ function asAcpJsonRpcError(error) {
|
|
|
17255
17257
|
init_chatEvents();
|
|
17256
17258
|
init_schemas();
|
|
17257
17259
|
|
|
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
17260
|
// src/adapters/acp/client.ts
|
|
17267
17261
|
var BandACPClient = class {
|
|
17268
17262
|
sessionChunks = /* @__PURE__ */ new Map();
|
|
17269
17263
|
permissionHandler;
|
|
17264
|
+
extensionHandler;
|
|
17270
17265
|
// The handler is connection-scoped and required at construction, so it is
|
|
17271
17266
|
// already in place before the agent process is spawned: there is no window
|
|
17272
17267
|
// in which a `session/request_permission` has nowhere to go.
|
|
17273
|
-
constructor(permissionHandler) {
|
|
17268
|
+
constructor(permissionHandler, extensionHandler) {
|
|
17274
17269
|
this.permissionHandler = permissionHandler;
|
|
17270
|
+
this.extensionHandler = extensionHandler;
|
|
17275
17271
|
}
|
|
17276
17272
|
beginSession(sessionId) {
|
|
17277
17273
|
this.sessionChunks.set(sessionId, []);
|
|
@@ -17316,62 +17312,26 @@ var BandACPClient = class {
|
|
|
17316
17312
|
return chunks;
|
|
17317
17313
|
}
|
|
17318
17314
|
async extMethod(method, params) {
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17322
|
-
|
|
17323
|
-
|
|
17324
|
-
|
|
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 {};
|
|
17315
|
+
const result = await this.extensionHandler?.extMethod?.(
|
|
17316
|
+
method,
|
|
17317
|
+
params,
|
|
17318
|
+
{ sessionId: sessionIdFrom(params) }
|
|
17319
|
+
);
|
|
17320
|
+
return result ?? {};
|
|
17346
17321
|
}
|
|
17347
17322
|
async extNotification(method, params) {
|
|
17348
|
-
const sessionId =
|
|
17349
|
-
|
|
17350
|
-
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17356
|
-
this.appendChunk(sessionId, {
|
|
17357
|
-
chunkType: "plan",
|
|
17358
|
-
content: lines.join("\n"),
|
|
17359
|
-
metadata: {},
|
|
17360
|
-
streamed: false
|
|
17361
|
-
});
|
|
17362
|
-
}
|
|
17323
|
+
const sessionId = sessionIdFrom(params);
|
|
17324
|
+
const chunks = await this.extensionHandler?.extNotification?.(
|
|
17325
|
+
method,
|
|
17326
|
+
params,
|
|
17327
|
+
{ sessionId }
|
|
17328
|
+
);
|
|
17329
|
+
const targetSessionId = sessionId ?? this.extensionHandler?.extensionSessionId?.() ?? null;
|
|
17330
|
+
if (!targetSessionId || !chunks) {
|
|
17363
17331
|
return;
|
|
17364
17332
|
}
|
|
17365
|
-
|
|
17366
|
-
|
|
17367
|
-
if (result) {
|
|
17368
|
-
this.appendChunk(sessionId, {
|
|
17369
|
-
chunkType: "text",
|
|
17370
|
-
content: `[Task completed] ${result}`,
|
|
17371
|
-
metadata: {},
|
|
17372
|
-
streamed: false
|
|
17373
|
-
});
|
|
17374
|
-
}
|
|
17333
|
+
for (const chunk of chunks) {
|
|
17334
|
+
this.appendChunk(targetSessionId, chunk);
|
|
17375
17335
|
}
|
|
17376
17336
|
}
|
|
17377
17337
|
appendChunk(sessionId, chunk) {
|
|
@@ -17496,6 +17456,17 @@ function extractTextFromContent(content) {
|
|
|
17496
17456
|
function toOptionalString(value) {
|
|
17497
17457
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
17498
17458
|
}
|
|
17459
|
+
function sessionIdFrom(params) {
|
|
17460
|
+
return toOptionalString(params.sessionId) ?? toOptionalString(params.session_id);
|
|
17461
|
+
}
|
|
17462
|
+
|
|
17463
|
+
// src/adapters/acp/types.ts
|
|
17464
|
+
function choosePermissionOption(options) {
|
|
17465
|
+
if (options.length === 0) {
|
|
17466
|
+
return null;
|
|
17467
|
+
}
|
|
17468
|
+
return options.find((option) => option.kind === "allow_once") ?? options.find((option) => option.kind === "allow_always") ?? options[0];
|
|
17469
|
+
}
|
|
17499
17470
|
|
|
17500
17471
|
// src/adapters/acp/loader.ts
|
|
17501
17472
|
init_errors();
|
|
@@ -17542,6 +17513,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
17542
17513
|
additionalMcpTools;
|
|
17543
17514
|
clientCapabilities;
|
|
17544
17515
|
connectionFactory;
|
|
17516
|
+
extensionHandler;
|
|
17545
17517
|
tcpEndpoint;
|
|
17546
17518
|
// The value's `generation` is the connection generation the session was
|
|
17547
17519
|
// last established/restored against. `client` is the exact BandACPClient
|
|
@@ -17606,6 +17578,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
17606
17578
|
this.additionalMcpTools = [...options.additionalMcpTools ?? []];
|
|
17607
17579
|
this.clientCapabilities = options.clientCapabilities;
|
|
17608
17580
|
this.connectionFactory = options.connectionFactory;
|
|
17581
|
+
this.extensionHandler = options.extensionHandler;
|
|
17609
17582
|
this.tcpEndpoint = tcpEndpoint;
|
|
17610
17583
|
this.resolvePermission = options.resolvePermission;
|
|
17611
17584
|
this.resolveSessionMode = options.resolveSessionMode;
|
|
@@ -17661,6 +17634,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
17661
17634
|
let client = null;
|
|
17662
17635
|
let sessionId;
|
|
17663
17636
|
let generation = 0;
|
|
17637
|
+
await this.onAcpTurnStarted(message, tools, context);
|
|
17664
17638
|
try {
|
|
17665
17639
|
const ensured = await this.ensureConnection();
|
|
17666
17640
|
connection = ensured.connection;
|
|
@@ -17671,6 +17645,7 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
17671
17645
|
}
|
|
17672
17646
|
sessionId = await this.getOrCreateSession(context.roomId, connection, generation, client);
|
|
17673
17647
|
const sessionKey = this.sessionKey(generation, sessionId);
|
|
17648
|
+
await this.onAcpSessionReady(message, tools, context, sessionId);
|
|
17674
17649
|
client.beginSession(sessionId);
|
|
17675
17650
|
const content = replaceUuidMentions(message.content, mentionSubjectsFromMetadata(message.metadata));
|
|
17676
17651
|
const messageWithContext = [...systemUpdateParts(participantsMessage, contactsMessage), content].join("\n\n");
|
|
@@ -17741,8 +17716,16 @@ ${messageWithContext}`;
|
|
|
17741
17716
|
...configError ? { optionId: configError.optionId, selectedValue: configError.selectedValue } : {}
|
|
17742
17717
|
}
|
|
17743
17718
|
);
|
|
17719
|
+
} finally {
|
|
17720
|
+
await this.onAcpTurnFinished(message, tools, context);
|
|
17744
17721
|
}
|
|
17745
17722
|
}
|
|
17723
|
+
async onAcpTurnStarted(_message, _tools, _context) {
|
|
17724
|
+
}
|
|
17725
|
+
async onAcpTurnFinished(_message, _tools, _context) {
|
|
17726
|
+
}
|
|
17727
|
+
async onAcpSessionReady(_message, _tools, _context, _sessionId) {
|
|
17728
|
+
}
|
|
17746
17729
|
// Best-effort: tells the agent to stop working on a turn Band has already
|
|
17747
17730
|
// given up waiting for (the ACP client has no way to force it), evicts the
|
|
17748
17731
|
// session so the room's next turn re-establishes rather than reuses it
|
|
@@ -17912,6 +17895,9 @@ ${messageWithContext}`;
|
|
|
17912
17895
|
sessionKey(generation, sessionId) {
|
|
17913
17896
|
return `${generation}:${sessionId}`;
|
|
17914
17897
|
}
|
|
17898
|
+
roomIdForSession(sessionId) {
|
|
17899
|
+
return [...this.roomToSession.entries()].find(([, owner]) => owner.sessionId === sessionId)?.[0];
|
|
17900
|
+
}
|
|
17915
17901
|
async ensureConnection() {
|
|
17916
17902
|
if (this.connection && !this.connection.signal.aborted) {
|
|
17917
17903
|
return { connection: this.connection, generation: this.connectionGeneration };
|
|
@@ -17948,7 +17934,10 @@ ${messageWithContext}`;
|
|
|
17948
17934
|
throw new Error(CONNECTION_ATTEMPT_SUPERSEDED_ERROR);
|
|
17949
17935
|
}
|
|
17950
17936
|
const owner = { generation: -1 };
|
|
17951
|
-
const client = new BandACPClient(
|
|
17937
|
+
const client = new BandACPClient(
|
|
17938
|
+
(params) => this.routePermissionRequest(params, owner.generation),
|
|
17939
|
+
this.extensionHandler
|
|
17940
|
+
);
|
|
17952
17941
|
handle = await (this.connectionFactory ? this.connectionFactory(client, {
|
|
17953
17942
|
command: this.command,
|
|
17954
17943
|
cwd: this.cwd,
|
|
@@ -18804,6 +18793,358 @@ var CopilotACPAdapter = class extends ACPClientAdapter {
|
|
|
18804
18793
|
}
|
|
18805
18794
|
};
|
|
18806
18795
|
|
|
18796
|
+
// src/adapters/cursor-acp/CursorACPAdapter.ts
|
|
18797
|
+
var DEFAULT_CURSOR_ACP_COMMAND = ["agent", "acp"];
|
|
18798
|
+
var DEFAULT_CURSOR_DECISION_TIMEOUT_MS = 3e5;
|
|
18799
|
+
var DEFAULT_CURSOR_MAX_PENDING_DECISIONS = 10;
|
|
18800
|
+
var CursorExtensions = class {
|
|
18801
|
+
adapter = null;
|
|
18802
|
+
todosBySession = /* @__PURE__ */ new Map();
|
|
18803
|
+
bind(adapter) {
|
|
18804
|
+
this.adapter = adapter;
|
|
18805
|
+
}
|
|
18806
|
+
async resolvePermission(request, signal) {
|
|
18807
|
+
return this.adapter?.resolveCursorPermission(request, signal);
|
|
18808
|
+
}
|
|
18809
|
+
extensionSessionId() {
|
|
18810
|
+
return this.adapter?.extensionSessionId() ?? null;
|
|
18811
|
+
}
|
|
18812
|
+
async extMethod(method, params, context) {
|
|
18813
|
+
return this.adapter?.resolveExtension(method, params, context.sessionId) ?? null;
|
|
18814
|
+
}
|
|
18815
|
+
async extNotification(method, params, context) {
|
|
18816
|
+
const sessionId = context.sessionId ?? this.extensionSessionId();
|
|
18817
|
+
if (!sessionId) {
|
|
18818
|
+
return;
|
|
18819
|
+
}
|
|
18820
|
+
if (method === "cursor/update_todos") {
|
|
18821
|
+
const content = this.updateTodos(sessionId, params);
|
|
18822
|
+
if (!content) {
|
|
18823
|
+
return;
|
|
18824
|
+
}
|
|
18825
|
+
return [{ chunkType: "plan", content, metadata: { cursor_todos: true }, streamed: false }];
|
|
18826
|
+
}
|
|
18827
|
+
if (method === "cursor/task") {
|
|
18828
|
+
const description = stringValue(params.description);
|
|
18829
|
+
if (!description) {
|
|
18830
|
+
return [];
|
|
18831
|
+
}
|
|
18832
|
+
const subagentType = stringValue(params.subagentType) ?? "unspecified";
|
|
18833
|
+
const model = stringValue(params.model);
|
|
18834
|
+
const suffix = model ? ` (${model})` : "";
|
|
18835
|
+
return [{ chunkType: "plan", content: `[Cursor ${subagentType} task] ${description}${suffix}`, metadata: {}, streamed: false }];
|
|
18836
|
+
}
|
|
18837
|
+
if (method === "cursor/generate_image") {
|
|
18838
|
+
const description = stringValue(params.description);
|
|
18839
|
+
if (!description) {
|
|
18840
|
+
return [];
|
|
18841
|
+
}
|
|
18842
|
+
const filePath = stringValue(params.filePath);
|
|
18843
|
+
return [{ chunkType: "plan", content: `[Cursor generated image] ${description}${filePath ? ` \u2192 ${filePath}` : ""}`, metadata: {}, streamed: false }];
|
|
18844
|
+
}
|
|
18845
|
+
}
|
|
18846
|
+
forgetSession(sessionId) {
|
|
18847
|
+
this.todosBySession.delete(sessionId);
|
|
18848
|
+
}
|
|
18849
|
+
clearSessions() {
|
|
18850
|
+
this.todosBySession.clear();
|
|
18851
|
+
}
|
|
18852
|
+
updateTodos(sessionId, params) {
|
|
18853
|
+
const todos = parseTodos(params.todos);
|
|
18854
|
+
if (params.merge === true) {
|
|
18855
|
+
const current2 = this.todosBySession.get(sessionId) ?? /* @__PURE__ */ new Map();
|
|
18856
|
+
for (const todo of todos) {
|
|
18857
|
+
current2.set(todo.id, todo);
|
|
18858
|
+
}
|
|
18859
|
+
this.todosBySession.set(sessionId, current2);
|
|
18860
|
+
} else {
|
|
18861
|
+
this.todosBySession.set(sessionId, new Map(todos.map((todo) => [todo.id, todo])));
|
|
18862
|
+
}
|
|
18863
|
+
const current = this.todosBySession.get(sessionId);
|
|
18864
|
+
return current && current.size > 0 ? [...current.values()].map((todo) => `- [${todoMark(todo.status)}] ${todo.content}`).join("\n") : void 0;
|
|
18865
|
+
}
|
|
18866
|
+
};
|
|
18867
|
+
var CursorACPAdapter = class extends ACPClientAdapter {
|
|
18868
|
+
provider = "cursor-acp";
|
|
18869
|
+
approvalMode;
|
|
18870
|
+
questionMode;
|
|
18871
|
+
planMode;
|
|
18872
|
+
decisionTimeoutMs;
|
|
18873
|
+
maxPendingDecisions;
|
|
18874
|
+
authorizedSenders;
|
|
18875
|
+
decisionLogger;
|
|
18876
|
+
extensions;
|
|
18877
|
+
turns = /* @__PURE__ */ new Map();
|
|
18878
|
+
pending = /* @__PURE__ */ new Map();
|
|
18879
|
+
activeTurn = null;
|
|
18880
|
+
turnTail = Promise.resolve();
|
|
18881
|
+
constructor(options = {}) {
|
|
18882
|
+
const extensions = new CursorExtensions();
|
|
18883
|
+
validateOptions(options);
|
|
18884
|
+
const env = cursorEnv(options);
|
|
18885
|
+
super({
|
|
18886
|
+
...options,
|
|
18887
|
+
env,
|
|
18888
|
+
command: options.command ?? [...DEFAULT_CURSOR_ACP_COMMAND],
|
|
18889
|
+
authMethod: "cursor_login",
|
|
18890
|
+
extensionHandler: extensions,
|
|
18891
|
+
resolvePermission: (request, signal) => extensions.resolvePermission(request, signal)
|
|
18892
|
+
});
|
|
18893
|
+
extensions.bind(this);
|
|
18894
|
+
this.extensions = extensions;
|
|
18895
|
+
this.approvalMode = options.approvalMode ?? "manual";
|
|
18896
|
+
this.questionMode = options.questionMode ?? "manual";
|
|
18897
|
+
this.planMode = options.planMode ?? "manual";
|
|
18898
|
+
this.decisionTimeoutMs = options.decisionTimeoutMs ?? DEFAULT_CURSOR_DECISION_TIMEOUT_MS;
|
|
18899
|
+
this.maxPendingDecisions = options.maxPendingDecisions ?? DEFAULT_CURSOR_MAX_PENDING_DECISIONS;
|
|
18900
|
+
this.authorizedSenders = options.decisionAuthorizedSenders ? new Set(options.decisionAuthorizedSenders) : null;
|
|
18901
|
+
this.decisionLogger = resolveLogger(options.logger);
|
|
18902
|
+
}
|
|
18903
|
+
async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
|
|
18904
|
+
if (await this.handleControl(message, tools, context.roomId)) {
|
|
18905
|
+
return;
|
|
18906
|
+
}
|
|
18907
|
+
await this.withCursorTurnLock(() => super.onMessage(message, tools, history, participantsMessage, contactsMessage, context));
|
|
18908
|
+
}
|
|
18909
|
+
async onAcpTurnStarted(message, tools, context) {
|
|
18910
|
+
const turn = { messageId: message.id, roomId: context.roomId, tools, requesterId: message.senderId };
|
|
18911
|
+
this.turns.set(context.roomId, turn);
|
|
18912
|
+
this.activeTurn = turn;
|
|
18913
|
+
}
|
|
18914
|
+
async onAcpSessionReady(message, _tools, context, sessionId) {
|
|
18915
|
+
const turn = this.turns.get(context.roomId);
|
|
18916
|
+
if (turn?.messageId === message.id) {
|
|
18917
|
+
turn.sessionId = sessionId;
|
|
18918
|
+
}
|
|
18919
|
+
}
|
|
18920
|
+
async onAcpTurnFinished(message, _tools, context) {
|
|
18921
|
+
const turn = this.turns.get(context.roomId);
|
|
18922
|
+
if (turn?.messageId === message.id) {
|
|
18923
|
+
this.turns.delete(context.roomId);
|
|
18924
|
+
this.cancelRoom(context.roomId);
|
|
18925
|
+
}
|
|
18926
|
+
if (this.activeTurn?.messageId === message.id) {
|
|
18927
|
+
this.activeTurn = null;
|
|
18928
|
+
}
|
|
18929
|
+
}
|
|
18930
|
+
async onCleanup(roomId) {
|
|
18931
|
+
const sessionId = this.turns.get(roomId)?.sessionId;
|
|
18932
|
+
this.cancelRoom(roomId);
|
|
18933
|
+
this.turns.delete(roomId);
|
|
18934
|
+
if (this.activeTurn?.roomId === roomId) {
|
|
18935
|
+
this.activeTurn = null;
|
|
18936
|
+
}
|
|
18937
|
+
await super.onCleanup(roomId);
|
|
18938
|
+
if (sessionId) {
|
|
18939
|
+
this.extensions.forgetSession(sessionId);
|
|
18940
|
+
}
|
|
18941
|
+
}
|
|
18942
|
+
async stop() {
|
|
18943
|
+
for (const decision of this.pending.values()) {
|
|
18944
|
+
decision.resolve(void 0);
|
|
18945
|
+
}
|
|
18946
|
+
this.pending.clear();
|
|
18947
|
+
this.turns.clear();
|
|
18948
|
+
this.activeTurn = null;
|
|
18949
|
+
this.extensions.clearSessions();
|
|
18950
|
+
await super.stop();
|
|
18951
|
+
}
|
|
18952
|
+
async resolveExtension(method, params, sessionId) {
|
|
18953
|
+
const roomId = sessionId ? this.roomIdForSession(sessionId) : this.activeTurn?.roomId;
|
|
18954
|
+
const turn = roomId ? this.turns.get(roomId) : void 0;
|
|
18955
|
+
if (!turn || sessionId && turn.sessionId !== sessionId) {
|
|
18956
|
+
return { outcome: { outcome: "cancelled" } };
|
|
18957
|
+
}
|
|
18958
|
+
if (method === "cursor/ask_question") {
|
|
18959
|
+
return this.resolveQuestion(turn.roomId, turn, params);
|
|
18960
|
+
}
|
|
18961
|
+
if (method === "cursor/create_plan") {
|
|
18962
|
+
return this.resolvePlan(turn.roomId, turn, params);
|
|
18963
|
+
}
|
|
18964
|
+
return {};
|
|
18965
|
+
}
|
|
18966
|
+
async resolveCursorPermission(request, signal) {
|
|
18967
|
+
if (this.approvalMode === "autoAccept") {
|
|
18968
|
+
return allowOption(request.options)?.optionId;
|
|
18969
|
+
}
|
|
18970
|
+
if (this.approvalMode === "autoDecline") {
|
|
18971
|
+
return void 0;
|
|
18972
|
+
}
|
|
18973
|
+
const turn = this.turns.get(request.roomId);
|
|
18974
|
+
if (!turn) {
|
|
18975
|
+
return void 0;
|
|
18976
|
+
}
|
|
18977
|
+
const options = request.options.map((option) => option.optionId);
|
|
18978
|
+
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);
|
|
18979
|
+
return typeof token === "string" && options.includes(token) ? token : void 0;
|
|
18980
|
+
}
|
|
18981
|
+
extensionSessionId() {
|
|
18982
|
+
return this.activeTurn?.sessionId ?? null;
|
|
18983
|
+
}
|
|
18984
|
+
async resolveQuestion(roomId, turn, params) {
|
|
18985
|
+
const questions = questionChoices(params.questions);
|
|
18986
|
+
if (questions.choices.size === 0) {
|
|
18987
|
+
return { outcome: { outcome: "cancelled" } };
|
|
18988
|
+
}
|
|
18989
|
+
if (this.questionMode === "autoCancel") {
|
|
18990
|
+
return { outcome: { outcome: "cancelled" } };
|
|
18991
|
+
}
|
|
18992
|
+
if (this.questionMode === "autoFirst") {
|
|
18993
|
+
return answered(Object.fromEntries([...questions.choices].map(([id, options]) => [id, [options[0]]])));
|
|
18994
|
+
}
|
|
18995
|
+
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] ...`.");
|
|
18996
|
+
return isRecord3(result) ? result : { outcome: { outcome: "cancelled" } };
|
|
18997
|
+
}
|
|
18998
|
+
async resolvePlan(roomId, turn, params) {
|
|
18999
|
+
if (this.planMode === "autoAccept") {
|
|
19000
|
+
return { outcome: { outcome: "accepted" } };
|
|
19001
|
+
}
|
|
19002
|
+
if (this.planMode === "autoDecline") {
|
|
19003
|
+
return { outcome: { outcome: "rejected" } };
|
|
19004
|
+
}
|
|
19005
|
+
const title = stringValue(params.title) ?? "Cursor plan";
|
|
19006
|
+
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}\`.`);
|
|
19007
|
+
return isRecord3(result) ? result : { outcome: { outcome: "cancelled" } };
|
|
19008
|
+
}
|
|
19009
|
+
async waitForDecision(kind, roomId, turn, choices, multiSelect, prompt, signal) {
|
|
19010
|
+
if (this.pending.size >= this.maxPendingDecisions) {
|
|
19011
|
+
this.pending.values().next().value?.resolve(void 0);
|
|
19012
|
+
}
|
|
19013
|
+
const token = crypto.randomUUID().slice(0, 8);
|
|
19014
|
+
return new Promise((resolve) => {
|
|
19015
|
+
const timer = setTimeout(() => settle(void 0), this.decisionTimeoutMs);
|
|
19016
|
+
const abort = () => settle(void 0);
|
|
19017
|
+
const settle = (value) => {
|
|
19018
|
+
clearTimeout(timer);
|
|
19019
|
+
signal?.removeEventListener("abort", abort);
|
|
19020
|
+
this.pending.delete(token);
|
|
19021
|
+
resolve(value);
|
|
19022
|
+
};
|
|
19023
|
+
this.pending.set(token, { kind, roomId, choices, multiSelect, resolve: settle });
|
|
19024
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
19025
|
+
void turn.tools.sendMessage(prompt.replaceAll("{token}", token), [turn.requesterId]).catch((error) => {
|
|
19026
|
+
this.decisionLogger.warn("cursor_acp.decision_prompt_delivery_failed", { roomId, kind, error: String(error) });
|
|
19027
|
+
settle(void 0);
|
|
19028
|
+
});
|
|
19029
|
+
});
|
|
19030
|
+
}
|
|
19031
|
+
async handleControl(message, tools, roomId) {
|
|
19032
|
+
const words = message.content.trim().split(/\s+/);
|
|
19033
|
+
if (words[0]?.toLowerCase() !== "/cursor") {
|
|
19034
|
+
return false;
|
|
19035
|
+
}
|
|
19036
|
+
if (words.length === 1 || words[1]?.toLowerCase() === "decisions") {
|
|
19037
|
+
const entries = [...this.pending.entries()].filter(([, decision2]) => decision2.roomId === roomId).map(([token2, decision2]) => `\`${token2}\` (${decision2.kind})`);
|
|
19038
|
+
await tools.sendMessage(`Pending Cursor decisions: ${entries.join(", ") || "none"}`);
|
|
19039
|
+
return true;
|
|
19040
|
+
}
|
|
19041
|
+
const [_, action, token, ...args] = words;
|
|
19042
|
+
const decision = token ? this.pending.get(token) : void 0;
|
|
19043
|
+
if (!decision || decision.roomId !== roomId) {
|
|
19044
|
+
await tools.sendMessage(`Cursor decision \`${token ?? ""}\` is not pending.`);
|
|
19045
|
+
return true;
|
|
19046
|
+
}
|
|
19047
|
+
if (this.authorizedSenders && !this.authorizedSenders.has(message.senderId)) {
|
|
19048
|
+
await tools.sendMessage("You are not authorized to resolve Cursor decisions.");
|
|
19049
|
+
return true;
|
|
19050
|
+
}
|
|
19051
|
+
const result = commandResult(action ?? "", args, decision);
|
|
19052
|
+
if (result === null) {
|
|
19053
|
+
await tools.sendMessage(`That command is not valid for Cursor ${decision.kind} decision \`${token}\`.`);
|
|
19054
|
+
return true;
|
|
19055
|
+
}
|
|
19056
|
+
decision.resolve(result);
|
|
19057
|
+
await tools.sendMessage(`Cursor ${decision.kind} decision \`${token}\` resolved.`);
|
|
19058
|
+
return true;
|
|
19059
|
+
}
|
|
19060
|
+
cancelRoom(roomId) {
|
|
19061
|
+
for (const [token, decision] of this.pending) {
|
|
19062
|
+
if (decision.roomId === roomId) {
|
|
19063
|
+
decision.resolve(void 0);
|
|
19064
|
+
this.pending.delete(token);
|
|
19065
|
+
}
|
|
19066
|
+
}
|
|
19067
|
+
}
|
|
19068
|
+
async withCursorTurnLock(run) {
|
|
19069
|
+
const queued = this.turnTail.then(run, run);
|
|
19070
|
+
this.turnTail = queued.then(() => void 0, () => void 0);
|
|
19071
|
+
return queued;
|
|
19072
|
+
}
|
|
19073
|
+
};
|
|
19074
|
+
function cursorEnv(options) {
|
|
19075
|
+
const env = { ...options.env };
|
|
19076
|
+
if (options.apiKey) env.CURSOR_API_KEY ??= options.apiKey;
|
|
19077
|
+
if (options.authToken) env.CURSOR_AUTH_TOKEN ??= options.authToken;
|
|
19078
|
+
return Object.keys(env).length > 0 ? env : void 0;
|
|
19079
|
+
}
|
|
19080
|
+
function validateOptions(options) {
|
|
19081
|
+
if (options.apiKey && options.authToken) throw new Error("set either apiKey or authToken, not both");
|
|
19082
|
+
if (Array.isArray(options.command) && options.command.length === 0) throw new Error("Cursor ACP command must not be empty");
|
|
19083
|
+
if (options.decisionTimeoutMs !== void 0 && (!Number.isFinite(options.decisionTimeoutMs) || options.decisionTimeoutMs <= 0)) throw new Error("decisionTimeoutMs must be a positive finite number");
|
|
19084
|
+
if (options.maxPendingDecisions !== void 0 && (!Number.isInteger(options.maxPendingDecisions) || options.maxPendingDecisions <= 0)) throw new Error("maxPendingDecisions must be a positive integer");
|
|
19085
|
+
}
|
|
19086
|
+
function allowOption(options) {
|
|
19087
|
+
return options.find((option) => option.kind === "allow_once") ?? options.find((option) => option.kind === "allow_always");
|
|
19088
|
+
}
|
|
19089
|
+
function questionChoices(value) {
|
|
19090
|
+
const choices = /* @__PURE__ */ new Map();
|
|
19091
|
+
const multiSelect = /* @__PURE__ */ new Set();
|
|
19092
|
+
if (!Array.isArray(value)) return { choices, multiSelect };
|
|
19093
|
+
for (const question of value) {
|
|
19094
|
+
if (!isRecord3(question) || typeof question.id !== "string" || !Array.isArray(question.options)) continue;
|
|
19095
|
+
const options = question.options.filter(isRecord3).map((option) => stringValue(option.id)).filter((id) => !!id);
|
|
19096
|
+
if (options.length === 0) continue;
|
|
19097
|
+
choices.set(question.id, options);
|
|
19098
|
+
if (question.allowMultiple === true) multiSelect.add(question.id);
|
|
19099
|
+
}
|
|
19100
|
+
return { choices, multiSelect };
|
|
19101
|
+
}
|
|
19102
|
+
function commandResult(action, args, decision) {
|
|
19103
|
+
if (decision.kind === "permission") return action === "deny" ? void 0 : action === "select" && args.length === 1 && decision.choices.get("permission")?.includes(args[0] ?? "") ? args[0] : null;
|
|
19104
|
+
if (decision.kind === "plan") return action === "accept" ? { outcome: { outcome: "accepted" } } : action === "reject" ? { outcome: { outcome: "rejected" } } : null;
|
|
19105
|
+
if (action !== "answer") return null;
|
|
19106
|
+
const selected = {};
|
|
19107
|
+
for (const argument of args) {
|
|
19108
|
+
const [id, raw] = argument.split("=", 2);
|
|
19109
|
+
const values = raw?.split(",") ?? [];
|
|
19110
|
+
const offered = id ? decision.choices.get(id) : void 0;
|
|
19111
|
+
if (!id || !offered || selected[id] || values.length === 0 || values.length > 1 && !decision.multiSelect.has(id) || values.some((value) => !offered.includes(value))) return null;
|
|
19112
|
+
selected[id] = values;
|
|
19113
|
+
}
|
|
19114
|
+
return Object.keys(selected).length === decision.choices.size ? answered(selected) : null;
|
|
19115
|
+
}
|
|
19116
|
+
function answered(selected) {
|
|
19117
|
+
return { outcome: { outcome: "answered", answers: Object.entries(selected).map(([questionId, selectedOptionIds]) => ({ questionId, selectedOptionIds })) } };
|
|
19118
|
+
}
|
|
19119
|
+
function parseTodos(value) {
|
|
19120
|
+
if (!Array.isArray(value)) return [];
|
|
19121
|
+
return value.flatMap((todo) => {
|
|
19122
|
+
if (!isRecord3(todo)) return [];
|
|
19123
|
+
const id = stringValue(todo.id);
|
|
19124
|
+
const content = stringValue(todo.content);
|
|
19125
|
+
const status = stringValue(todo.status);
|
|
19126
|
+
return id && content && status ? [{ id, content, status }] : [];
|
|
19127
|
+
});
|
|
19128
|
+
}
|
|
19129
|
+
function todoMark(status) {
|
|
19130
|
+
switch (status) {
|
|
19131
|
+
case "completed":
|
|
19132
|
+
return "x";
|
|
19133
|
+
case "in_progress":
|
|
19134
|
+
return "~";
|
|
19135
|
+
case "cancelled":
|
|
19136
|
+
return "-";
|
|
19137
|
+
default:
|
|
19138
|
+
return " ";
|
|
19139
|
+
}
|
|
19140
|
+
}
|
|
19141
|
+
function isRecord3(value) {
|
|
19142
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
19143
|
+
}
|
|
19144
|
+
function stringValue(value) {
|
|
19145
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
19146
|
+
}
|
|
19147
|
+
|
|
18807
19148
|
// src/core/index.ts
|
|
18808
19149
|
var import_band_sdk_core13 = require("@band-ai/band-sdk-core");
|
|
18809
19150
|
init_protocols();
|
|
@@ -18828,7 +19169,9 @@ init_schemas();
|
|
|
18828
19169
|
ClaudeSDKAdapter,
|
|
18829
19170
|
CodexAdapter,
|
|
18830
19171
|
CopilotACPAdapter,
|
|
19172
|
+
CursorACPAdapter,
|
|
18831
19173
|
DEFAULT_COPILOT_ACP_COMMAND,
|
|
19174
|
+
DEFAULT_CURSOR_ACP_COMMAND,
|
|
18832
19175
|
DEFAULT_OMP_ACP_COMMAND,
|
|
18833
19176
|
DefaultPreprocessor,
|
|
18834
19177
|
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
|
|
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
|
|
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-
|
|
27
|
+
} from "./chunk-VHYPQAQ5.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,
|