@github/copilot-sdk 1.0.0-beta.5 → 1.0.0-beta.7
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/README.md +25 -30
- package/dist/canvas.d.ts +163 -0
- package/dist/canvas.js +92 -0
- package/dist/cjs/canvas.js +119 -0
- package/dist/cjs/client.js +243 -251
- package/dist/cjs/extension.js +9 -2
- package/dist/cjs/generated/rpc.js +97 -1
- package/dist/cjs/index.js +17 -7
- package/dist/cjs/session.js +64 -90
- package/dist/cjs/types.js +36 -3
- package/dist/client.d.ts +46 -49
- package/dist/client.js +246 -252
- package/dist/extension.d.ts +3 -1
- package/dist/extension.js +10 -2
- package/dist/generated/rpc.d.ts +796 -63
- package/dist/generated/rpc.js +97 -1
- package/dist/generated/session-events.d.ts +491 -78
- package/dist/index.d.ts +4 -2
- package/dist/index.js +12 -2
- package/dist/session.d.ts +13 -210
- package/dist/session.js +63 -88
- package/dist/types.d.ts +291 -121
- package/dist/types.js +34 -2
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -17,17 +17,15 @@ import {
|
|
|
17
17
|
createInternalServerRpc,
|
|
18
18
|
registerClientSessionApiHandlers
|
|
19
19
|
} from "./generated/rpc.js";
|
|
20
|
+
import {
|
|
21
|
+
dispatchCanvasProviderRequest
|
|
22
|
+
} from "./canvas.js";
|
|
20
23
|
import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
|
|
21
|
-
import { CopilotSession
|
|
24
|
+
import { CopilotSession } from "./session.js";
|
|
22
25
|
import { createSessionFsAdapter } from "./sessionFsProvider.js";
|
|
23
26
|
import { getTraceContext } from "./telemetry.js";
|
|
24
27
|
import { defaultJoinSessionPermissionHandler } from "./types.js";
|
|
25
|
-
|
|
26
|
-
const { maxInputTokens, ...rest } = provider;
|
|
27
|
-
if (maxInputTokens === void 0) return rest;
|
|
28
|
-
return { ...rest, maxPromptTokens: maxInputTokens };
|
|
29
|
-
}
|
|
30
|
-
const MIN_PROTOCOL_VERSION = 2;
|
|
28
|
+
const MIN_PROTOCOL_VERSION = 3;
|
|
31
29
|
function isZodSchema(value) {
|
|
32
30
|
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
33
31
|
}
|
|
@@ -38,6 +36,36 @@ function toJsonSchema(parameters) {
|
|
|
38
36
|
}
|
|
39
37
|
return parameters;
|
|
40
38
|
}
|
|
39
|
+
function toWireMcpServers(mcpServers) {
|
|
40
|
+
if (!mcpServers) return void 0;
|
|
41
|
+
return Object.fromEntries(
|
|
42
|
+
Object.entries(mcpServers).map(([name, server]) => {
|
|
43
|
+
if ("workingDirectory" in server) {
|
|
44
|
+
const { workingDirectory, ...rest } = server;
|
|
45
|
+
return [name, { ...rest, cwd: workingDirectory }];
|
|
46
|
+
}
|
|
47
|
+
return [name, server];
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
function toWireCustomAgents(agents) {
|
|
52
|
+
if (!agents) return void 0;
|
|
53
|
+
return agents.map((agent) => {
|
|
54
|
+
if (!agent.mcpServers) return agent;
|
|
55
|
+
const { mcpServers, ...rest } = agent;
|
|
56
|
+
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function isCanvasProviderRequestParams(params) {
|
|
60
|
+
if (!params || typeof params !== "object") {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const request = params;
|
|
64
|
+
return typeof request.sessionId === "string" && typeof request.extensionId === "string" && typeof request.canvasId === "string" && typeof request.instanceId === "string";
|
|
65
|
+
}
|
|
66
|
+
function isCanvasActionInvokeParams(params) {
|
|
67
|
+
return isCanvasProviderRequestParams(params) && typeof params.actionName === "string";
|
|
68
|
+
}
|
|
41
69
|
function extractTransformCallbacks(systemMessage) {
|
|
42
70
|
if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
|
|
43
71
|
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
@@ -91,12 +119,18 @@ class CopilotClient {
|
|
|
91
119
|
cliProcess = null;
|
|
92
120
|
connection = null;
|
|
93
121
|
socket = null;
|
|
94
|
-
|
|
122
|
+
runtimePort = null;
|
|
95
123
|
actualHost = "localhost";
|
|
96
124
|
state = "disconnected";
|
|
97
125
|
sessions = /* @__PURE__ */ new Map();
|
|
98
126
|
stderrBuffer = "";
|
|
99
127
|
// Captures CLI stderr for error messages
|
|
128
|
+
/** Resolved connection mode chosen in the constructor. */
|
|
129
|
+
connectionConfig;
|
|
130
|
+
/** Resolved path to the runtime executable (only used for child-process kinds). */
|
|
131
|
+
resolvedCliPath;
|
|
132
|
+
/** Resolved environment passed to the spawned runtime. */
|
|
133
|
+
resolvedEnv;
|
|
100
134
|
options;
|
|
101
135
|
isExternalServer = false;
|
|
102
136
|
forceStopping = false;
|
|
@@ -145,86 +179,79 @@ class CopilotClient {
|
|
|
145
179
|
* Creates a new CopilotClient instance.
|
|
146
180
|
*
|
|
147
181
|
* @param options - Configuration options for the client
|
|
148
|
-
* @throws Error if mutually exclusive options are provided (e.g., cliUrl with useStdio or cliPath)
|
|
149
182
|
*
|
|
150
183
|
* @example
|
|
151
184
|
* ```typescript
|
|
152
|
-
* // Default
|
|
185
|
+
* // Default: spawns the bundled runtime over stdio
|
|
153
186
|
* const client = new CopilotClient();
|
|
154
187
|
*
|
|
155
|
-
* // Connect to an existing
|
|
156
|
-
* const client = new CopilotClient({
|
|
188
|
+
* // Connect to an existing runtime
|
|
189
|
+
* const client = new CopilotClient({
|
|
190
|
+
* connection: RuntimeConnection.forUri("localhost:3000"),
|
|
191
|
+
* });
|
|
157
192
|
*
|
|
158
|
-
* //
|
|
193
|
+
* // Spawn the runtime over TCP on a chosen port
|
|
159
194
|
* const client = new CopilotClient({
|
|
160
|
-
*
|
|
161
|
-
*
|
|
195
|
+
* connection: RuntimeConnection.forTcp({ port: 9001 }),
|
|
196
|
+
* });
|
|
197
|
+
*
|
|
198
|
+
* // Use a custom runtime binary
|
|
199
|
+
* const client = new CopilotClient({
|
|
200
|
+
* connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }),
|
|
201
|
+
* logLevel: "debug",
|
|
162
202
|
* });
|
|
163
203
|
* ```
|
|
164
204
|
*/
|
|
165
205
|
constructor(options = {}) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
if (options.isChildProcess && (options.cliUrl || options.useStdio === false)) {
|
|
170
|
-
throw new Error(
|
|
171
|
-
"isChildProcess must be used in conjunction with useStdio and not with cliUrl"
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
if (options.cliUrl && (options.gitHubToken || options.useLoggedInUser !== void 0)) {
|
|
206
|
+
const conn = options._internalConnection ?? options.connection ?? { kind: "stdio" };
|
|
207
|
+
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
175
208
|
throw new Error(
|
|
176
|
-
"gitHubToken and useLoggedInUser cannot be used with
|
|
209
|
+
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
177
210
|
);
|
|
178
211
|
}
|
|
179
|
-
if (
|
|
180
|
-
if (typeof
|
|
181
|
-
throw new Error("
|
|
182
|
-
}
|
|
183
|
-
if (options.useStdio === true) {
|
|
184
|
-
throw new Error("tcpConnectionToken cannot be used with useStdio: true");
|
|
212
|
+
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
213
|
+
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
214
|
+
throw new Error("connectionToken must be a non-empty string");
|
|
185
215
|
}
|
|
186
216
|
}
|
|
187
|
-
|
|
188
|
-
const sdkSpawnsCli = !willUseStdio && !options.cliUrl && !options.isChildProcess;
|
|
189
|
-
this.effectiveConnectionToken = options.tcpConnectionToken ?? (sdkSpawnsCli ? randomUUID() : void 0);
|
|
217
|
+
this.connectionConfig = conn;
|
|
190
218
|
if (options.sessionFs) {
|
|
191
219
|
this.validateSessionFsConfig(options.sessionFs);
|
|
192
220
|
}
|
|
193
|
-
if (
|
|
194
|
-
const { host, port } = this.parseCliUrl(
|
|
221
|
+
if (conn.kind === "uri") {
|
|
222
|
+
const { host, port } = this.parseCliUrl(conn.url);
|
|
195
223
|
this.actualHost = host;
|
|
196
|
-
this.
|
|
224
|
+
this.runtimePort = port;
|
|
197
225
|
this.isExternalServer = true;
|
|
198
|
-
}
|
|
199
|
-
if (options.isChildProcess) {
|
|
226
|
+
} else if (conn.kind === "parent-process") {
|
|
200
227
|
this.isExternalServer = true;
|
|
201
228
|
}
|
|
229
|
+
if (conn.kind === "tcp") {
|
|
230
|
+
this.effectiveConnectionToken = conn.connectionToken ?? randomUUID();
|
|
231
|
+
} else if (conn.kind === "uri") {
|
|
232
|
+
this.effectiveConnectionToken = conn.connectionToken;
|
|
233
|
+
}
|
|
202
234
|
this.onListModels = options.onListModels;
|
|
203
235
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
204
236
|
this.sessionFsConfig = options.sessionFs ?? null;
|
|
205
237
|
const effectiveEnv = options.env ?? process.env;
|
|
238
|
+
this.resolvedEnv = effectiveEnv;
|
|
239
|
+
this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
|
|
240
|
+
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
241
|
+
this.connectionExtraArgs = [...connArgs];
|
|
206
242
|
this.options = {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
cwd: options.cwd ?? process.cwd(),
|
|
210
|
-
port: options.port || 0,
|
|
211
|
-
useStdio: options.cliUrl ? false : options.useStdio ?? true,
|
|
212
|
-
// Default to stdio unless cliUrl is provided
|
|
213
|
-
isChildProcess: options.isChildProcess ?? false,
|
|
214
|
-
cliUrl: options.cliUrl,
|
|
215
|
-
logLevel: options.logLevel || "debug",
|
|
216
|
-
autoStart: options.autoStart ?? true,
|
|
217
|
-
autoRestart: false,
|
|
218
|
-
env: effectiveEnv,
|
|
243
|
+
workingDirectory: options.workingDirectory ?? process.cwd(),
|
|
244
|
+
logLevel: options.logLevel,
|
|
219
245
|
gitHubToken: options.gitHubToken,
|
|
220
|
-
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true
|
|
246
|
+
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true.
|
|
221
247
|
useLoggedInUser: options.useLoggedInUser ?? (options.gitHubToken ? false : true),
|
|
222
248
|
telemetry: options.telemetry,
|
|
223
|
-
|
|
249
|
+
baseDirectory: options.baseDirectory,
|
|
224
250
|
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
225
|
-
|
|
251
|
+
enableRemoteSessions: options.enableRemoteSessions ?? false
|
|
226
252
|
};
|
|
227
253
|
}
|
|
254
|
+
connectionExtraArgs = [];
|
|
228
255
|
/**
|
|
229
256
|
* Parse CLI URL into host and port
|
|
230
257
|
* Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
|
|
@@ -262,12 +289,12 @@ class CopilotClient {
|
|
|
262
289
|
if (!this.sessionFsConfig) {
|
|
263
290
|
return;
|
|
264
291
|
}
|
|
265
|
-
if (!config.
|
|
292
|
+
if (!config.createSessionFsProvider) {
|
|
266
293
|
throw new Error(
|
|
267
|
-
"
|
|
294
|
+
"createSessionFsProvider is required in session config when sessionFs is enabled in client options."
|
|
268
295
|
);
|
|
269
296
|
}
|
|
270
|
-
const provider = config.
|
|
297
|
+
const provider = config.createSessionFsProvider(session);
|
|
271
298
|
if (this.sessionFsConfig.capabilities?.sqlite && !provider.sqlite) {
|
|
272
299
|
throw new Error(
|
|
273
300
|
"SessionFsConfig declares capabilities.sqlite but the provider does not implement sqlite."
|
|
@@ -281,14 +308,14 @@ class CopilotClient {
|
|
|
281
308
|
* If connecting to an external server (via cliUrl), only establishes the connection.
|
|
282
309
|
* Otherwise, spawns the CLI server process and then connects.
|
|
283
310
|
*
|
|
284
|
-
* This method is called automatically
|
|
311
|
+
* This method is called automatically the first time you create or resume a session.
|
|
285
312
|
*
|
|
286
313
|
* @returns A promise that resolves when the connection is established
|
|
287
314
|
* @throws Error if the server fails to start or the connection fails
|
|
288
315
|
*
|
|
289
316
|
* @example
|
|
290
317
|
* ```typescript
|
|
291
|
-
* const client = new CopilotClient(
|
|
318
|
+
* const client = new CopilotClient();
|
|
292
319
|
* await client.start();
|
|
293
320
|
* // Now ready to create sessions
|
|
294
321
|
* ```
|
|
@@ -383,8 +410,15 @@ class CopilotClient {
|
|
|
383
410
|
}
|
|
384
411
|
this.modelsCache = null;
|
|
385
412
|
if (this.socket) {
|
|
413
|
+
const socket = this.socket;
|
|
414
|
+
this.socket = null;
|
|
386
415
|
try {
|
|
387
|
-
|
|
416
|
+
if (!socket.destroyed) {
|
|
417
|
+
await new Promise((resolve) => {
|
|
418
|
+
socket.once("close", () => resolve());
|
|
419
|
+
socket.end();
|
|
420
|
+
});
|
|
421
|
+
}
|
|
388
422
|
} catch (error) {
|
|
389
423
|
errors.push(
|
|
390
424
|
new Error(
|
|
@@ -392,11 +426,18 @@ class CopilotClient {
|
|
|
392
426
|
)
|
|
393
427
|
);
|
|
394
428
|
}
|
|
395
|
-
this.socket = null;
|
|
396
429
|
}
|
|
397
430
|
if (this.cliProcess && !this.isExternalServer) {
|
|
431
|
+
const child = this.cliProcess;
|
|
432
|
+
this.cliProcess = null;
|
|
398
433
|
try {
|
|
399
|
-
|
|
434
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
435
|
+
const exited = new Promise((resolve) => {
|
|
436
|
+
child.once("exit", () => resolve());
|
|
437
|
+
});
|
|
438
|
+
child.kill();
|
|
439
|
+
await exited;
|
|
440
|
+
}
|
|
400
441
|
} catch (error) {
|
|
401
442
|
errors.push(
|
|
402
443
|
new Error(
|
|
@@ -404,18 +445,32 @@ class CopilotClient {
|
|
|
404
445
|
)
|
|
405
446
|
);
|
|
406
447
|
}
|
|
407
|
-
this.cliProcess = null;
|
|
408
448
|
}
|
|
409
449
|
if (this.cliStartTimeout) {
|
|
410
450
|
clearTimeout(this.cliStartTimeout);
|
|
411
451
|
this.cliStartTimeout = null;
|
|
412
452
|
}
|
|
413
453
|
this.state = "disconnected";
|
|
414
|
-
this.
|
|
454
|
+
this.runtimePort = null;
|
|
415
455
|
this.stderrBuffer = "";
|
|
416
456
|
this.processExitPromise = null;
|
|
417
457
|
return errors;
|
|
418
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Alias for {@link stop} that lets `CopilotClient` participate in `await using`
|
|
461
|
+
* blocks for automatic cleanup.
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* ```typescript
|
|
465
|
+
* await using client = new CopilotClient();
|
|
466
|
+
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
467
|
+
* await session.sendAndWait("Hello");
|
|
468
|
+
* // client.stop() is called automatically when the block exits.
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
async [Symbol.asyncDispose]() {
|
|
472
|
+
await this.stop();
|
|
473
|
+
}
|
|
419
474
|
/**
|
|
420
475
|
* Forcefully stops the CLI server without graceful cleanup.
|
|
421
476
|
*
|
|
@@ -472,7 +527,7 @@ class CopilotClient {
|
|
|
472
527
|
this.cliStartTimeout = null;
|
|
473
528
|
}
|
|
474
529
|
this.state = "disconnected";
|
|
475
|
-
this.
|
|
530
|
+
this.runtimePort = null;
|
|
476
531
|
this.stderrBuffer = "";
|
|
477
532
|
this.processExitPromise = null;
|
|
478
533
|
}
|
|
@@ -480,12 +535,11 @@ class CopilotClient {
|
|
|
480
535
|
* Creates a new conversation session with the Copilot CLI.
|
|
481
536
|
*
|
|
482
537
|
* Sessions maintain conversation state, handle events, and manage tool execution.
|
|
483
|
-
* If the client is not connected
|
|
484
|
-
* start the connection.
|
|
538
|
+
* If the client is not connected, this method automatically starts the connection.
|
|
485
539
|
*
|
|
486
540
|
* @param config - Optional configuration for the session
|
|
487
541
|
* @returns A promise that resolves with the created session
|
|
488
|
-
* @throws Error if the client
|
|
542
|
+
* @throws Error if the client fails to start
|
|
489
543
|
*
|
|
490
544
|
* @example
|
|
491
545
|
* ```typescript
|
|
@@ -507,11 +561,7 @@ class CopilotClient {
|
|
|
507
561
|
*/
|
|
508
562
|
async createSession(config) {
|
|
509
563
|
if (!this.connection) {
|
|
510
|
-
|
|
511
|
-
await this.start();
|
|
512
|
-
} else {
|
|
513
|
-
throw new Error("Client not connected. Call start() first.");
|
|
514
|
-
}
|
|
564
|
+
await this.start();
|
|
515
565
|
}
|
|
516
566
|
const sessionId = config.sessionId ?? randomUUID();
|
|
517
567
|
const session = new CopilotSession(
|
|
@@ -521,6 +571,7 @@ class CopilotClient {
|
|
|
521
571
|
this.onGetTraceContext
|
|
522
572
|
);
|
|
523
573
|
session.registerTools(config.tools);
|
|
574
|
+
session.registerCanvases(config.canvases);
|
|
524
575
|
session.registerCommands(config.commands);
|
|
525
576
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
526
577
|
if (config.onUserInputRequest) {
|
|
@@ -529,11 +580,11 @@ class CopilotClient {
|
|
|
529
580
|
if (config.onElicitationRequest) {
|
|
530
581
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
531
582
|
}
|
|
532
|
-
if (config.
|
|
533
|
-
session.registerExitPlanModeHandler(config.
|
|
583
|
+
if (config.onExitPlanModeRequest) {
|
|
584
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
534
585
|
}
|
|
535
|
-
if (config.
|
|
536
|
-
session.registerAutoModeSwitchHandler(config.
|
|
586
|
+
if (config.onAutoModeSwitchRequest) {
|
|
587
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
537
588
|
}
|
|
538
589
|
if (config.hooks) {
|
|
539
590
|
session.registerHooks(config.hooks);
|
|
@@ -563,6 +614,10 @@ class CopilotClient {
|
|
|
563
614
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
564
615
|
skipPermission: tool.skipPermission
|
|
565
616
|
})),
|
|
617
|
+
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
618
|
+
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
619
|
+
requestExtensions: config.requestExtensions,
|
|
620
|
+
extensionInfo: config.extensionInfo,
|
|
566
621
|
commands: config.commands?.map((cmd) => ({
|
|
567
622
|
name: cmd.name,
|
|
568
623
|
description: cmd.description
|
|
@@ -570,21 +625,21 @@ class CopilotClient {
|
|
|
570
625
|
systemMessage: wireSystemMessage,
|
|
571
626
|
availableTools: config.availableTools,
|
|
572
627
|
excludedTools: config.excludedTools,
|
|
573
|
-
provider: config.provider
|
|
628
|
+
provider: config.provider,
|
|
574
629
|
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
575
630
|
modelCapabilities: config.modelCapabilities,
|
|
576
|
-
requestPermission:
|
|
631
|
+
requestPermission: !!config.onPermissionRequest,
|
|
577
632
|
requestUserInput: !!config.onUserInputRequest,
|
|
578
633
|
requestElicitation: !!config.onElicitationRequest,
|
|
579
|
-
requestExitPlanMode: !!config.
|
|
580
|
-
requestAutoModeSwitch: !!config.
|
|
634
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
635
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
581
636
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
582
637
|
workingDirectory: config.workingDirectory,
|
|
583
638
|
streaming: config.streaming,
|
|
584
639
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
585
|
-
mcpServers: config.mcpServers,
|
|
640
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
586
641
|
envValueMode: "direct",
|
|
587
|
-
customAgents: config.customAgents,
|
|
642
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
588
643
|
defaultAgent: config.defaultAgent,
|
|
589
644
|
agent: config.agent,
|
|
590
645
|
configDir: config.configDir,
|
|
@@ -632,11 +687,7 @@ class CopilotClient {
|
|
|
632
687
|
*/
|
|
633
688
|
async resumeSession(sessionId, config) {
|
|
634
689
|
if (!this.connection) {
|
|
635
|
-
|
|
636
|
-
await this.start();
|
|
637
|
-
} else {
|
|
638
|
-
throw new Error("Client not connected. Call start() first.");
|
|
639
|
-
}
|
|
690
|
+
await this.start();
|
|
640
691
|
}
|
|
641
692
|
const session = new CopilotSession(
|
|
642
693
|
sessionId,
|
|
@@ -645,6 +696,7 @@ class CopilotClient {
|
|
|
645
696
|
this.onGetTraceContext
|
|
646
697
|
);
|
|
647
698
|
session.registerTools(config.tools);
|
|
699
|
+
session.registerCanvases(config.canvases);
|
|
648
700
|
session.registerCommands(config.commands);
|
|
649
701
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
650
702
|
if (config.onUserInputRequest) {
|
|
@@ -653,11 +705,11 @@ class CopilotClient {
|
|
|
653
705
|
if (config.onElicitationRequest) {
|
|
654
706
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
655
707
|
}
|
|
656
|
-
if (config.
|
|
657
|
-
session.registerExitPlanModeHandler(config.
|
|
708
|
+
if (config.onExitPlanModeRequest) {
|
|
709
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
658
710
|
}
|
|
659
|
-
if (config.
|
|
660
|
-
session.registerAutoModeSwitchHandler(config.
|
|
711
|
+
if (config.onAutoModeSwitchRequest) {
|
|
712
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
661
713
|
}
|
|
662
714
|
if (config.hooks) {
|
|
663
715
|
session.registerHooks(config.hooks);
|
|
@@ -691,61 +743,52 @@ class CopilotClient {
|
|
|
691
743
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
692
744
|
skipPermission: tool.skipPermission
|
|
693
745
|
})),
|
|
746
|
+
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
747
|
+
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
748
|
+
requestExtensions: config.requestExtensions,
|
|
749
|
+
extensionInfo: config.extensionInfo,
|
|
694
750
|
commands: config.commands?.map((cmd) => ({
|
|
695
751
|
name: cmd.name,
|
|
696
752
|
description: cmd.description
|
|
697
753
|
})),
|
|
698
|
-
provider: config.provider
|
|
754
|
+
provider: config.provider,
|
|
699
755
|
modelCapabilities: config.modelCapabilities,
|
|
700
756
|
requestPermission: config.onPermissionRequest !== defaultJoinSessionPermissionHandler,
|
|
701
757
|
requestUserInput: !!config.onUserInputRequest,
|
|
702
758
|
requestElicitation: !!config.onElicitationRequest,
|
|
703
|
-
requestExitPlanMode: !!config.
|
|
704
|
-
requestAutoModeSwitch: !!config.
|
|
759
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
760
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
705
761
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
706
762
|
workingDirectory: config.workingDirectory,
|
|
707
763
|
configDir: config.configDir,
|
|
708
764
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
709
765
|
streaming: config.streaming,
|
|
710
766
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
711
|
-
mcpServers: config.mcpServers,
|
|
767
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
712
768
|
envValueMode: "direct",
|
|
713
|
-
customAgents: config.customAgents,
|
|
769
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
714
770
|
defaultAgent: config.defaultAgent,
|
|
715
771
|
agent: config.agent,
|
|
716
772
|
skillDirectories: config.skillDirectories,
|
|
717
773
|
instructionDirectories: config.instructionDirectories,
|
|
718
774
|
disabledSkills: config.disabledSkills,
|
|
719
775
|
infiniteSessions: config.infiniteSessions,
|
|
720
|
-
|
|
776
|
+
suppressResumeEvent: config.suppressResumeEvent,
|
|
721
777
|
continuePendingWork: config.continuePendingWork,
|
|
722
778
|
gitHubToken: config.gitHubToken,
|
|
723
|
-
remoteSession: config.remoteSession
|
|
779
|
+
remoteSession: config.remoteSession,
|
|
780
|
+
openCanvases: config.openCanvases
|
|
724
781
|
});
|
|
725
|
-
const { workspacePath, capabilities } = response;
|
|
782
|
+
const { workspacePath, capabilities, openCanvases } = response;
|
|
726
783
|
session["_workspacePath"] = workspacePath;
|
|
727
784
|
session.setCapabilities(capabilities);
|
|
785
|
+
session.setOpenCanvases(openCanvases ?? []);
|
|
728
786
|
} catch (e) {
|
|
729
787
|
this.sessions.delete(sessionId);
|
|
730
788
|
throw e;
|
|
731
789
|
}
|
|
732
790
|
return session;
|
|
733
791
|
}
|
|
734
|
-
/**
|
|
735
|
-
* Gets the current connection state of the client.
|
|
736
|
-
*
|
|
737
|
-
* @returns The current connection state: "disconnected", "connecting", "connected", or "error"
|
|
738
|
-
*
|
|
739
|
-
* @example
|
|
740
|
-
* ```typescript
|
|
741
|
-
* if (client.getState() === "connected") {
|
|
742
|
-
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
743
|
-
* }
|
|
744
|
-
* ```
|
|
745
|
-
*/
|
|
746
|
-
getState() {
|
|
747
|
-
return this.state;
|
|
748
|
-
}
|
|
749
792
|
/**
|
|
750
793
|
* Sends a ping request to the server to verify connectivity.
|
|
751
794
|
*
|
|
@@ -947,8 +990,13 @@ class CopilotClient {
|
|
|
947
990
|
if (!this.connection) {
|
|
948
991
|
throw new Error("Client not connected");
|
|
949
992
|
}
|
|
993
|
+
let wireFilter;
|
|
994
|
+
if (filter) {
|
|
995
|
+
const { workingDirectory, ...rest } = filter;
|
|
996
|
+
wireFilter = { ...rest, cwd: workingDirectory };
|
|
997
|
+
}
|
|
950
998
|
const response = await this.connection.sendRequest("session.list", {
|
|
951
|
-
filter
|
|
999
|
+
filter: wireFilter
|
|
952
1000
|
});
|
|
953
1001
|
const { sessions } = response;
|
|
954
1002
|
return sessions.map(CopilotClient.toSessionMetadata);
|
|
@@ -983,13 +1031,19 @@ class CopilotClient {
|
|
|
983
1031
|
return CopilotClient.toSessionMetadata(session);
|
|
984
1032
|
}
|
|
985
1033
|
static toSessionMetadata(raw) {
|
|
1034
|
+
const { context } = raw;
|
|
986
1035
|
return {
|
|
987
1036
|
sessionId: raw.sessionId,
|
|
988
1037
|
startTime: new Date(raw.startTime),
|
|
989
1038
|
modifiedTime: new Date(raw.modifiedTime),
|
|
990
1039
|
summary: raw.summary,
|
|
991
1040
|
isRemote: raw.isRemote,
|
|
992
|
-
context:
|
|
1041
|
+
context: context ? {
|
|
1042
|
+
workingDirectory: context.cwd,
|
|
1043
|
+
gitRoot: context.gitRoot,
|
|
1044
|
+
repository: context.repository,
|
|
1045
|
+
branch: context.branch
|
|
1046
|
+
} : void 0
|
|
993
1047
|
};
|
|
994
1048
|
}
|
|
995
1049
|
/**
|
|
@@ -1042,7 +1096,7 @@ class CopilotClient {
|
|
|
1042
1096
|
throw new Error(result.error || "Failed to set foreground session");
|
|
1043
1097
|
}
|
|
1044
1098
|
}
|
|
1045
|
-
|
|
1099
|
+
onLifecycle(eventTypeOrHandler, handler) {
|
|
1046
1100
|
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
1047
1101
|
const eventType = eventTypeOrHandler;
|
|
1048
1102
|
if (!this.typedLifecycleHandlers.has(eventType)) {
|
|
@@ -1069,17 +1123,17 @@ class CopilotClient {
|
|
|
1069
1123
|
async startCLIServer() {
|
|
1070
1124
|
return new Promise((resolve, reject) => {
|
|
1071
1125
|
this.stderrBuffer = "";
|
|
1072
|
-
const args = [
|
|
1073
|
-
|
|
1074
|
-
"--
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
this.options.logLevel
|
|
1078
|
-
];
|
|
1079
|
-
if (this.options.useStdio) {
|
|
1126
|
+
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1127
|
+
if (this.options.logLevel) {
|
|
1128
|
+
args.push("--log-level", this.options.logLevel);
|
|
1129
|
+
}
|
|
1130
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1080
1131
|
args.push("--stdio");
|
|
1081
|
-
} else if (this.
|
|
1082
|
-
|
|
1132
|
+
} else if (this.connectionConfig.kind === "tcp") {
|
|
1133
|
+
const requestedPort = this.connectionConfig.port ?? 0;
|
|
1134
|
+
if (requestedPort > 0) {
|
|
1135
|
+
args.push("--port", requestedPort.toString());
|
|
1136
|
+
}
|
|
1083
1137
|
}
|
|
1084
1138
|
if (this.options.gitHubToken) {
|
|
1085
1139
|
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
@@ -1093,10 +1147,10 @@ class CopilotClient {
|
|
|
1093
1147
|
this.options.sessionIdleTimeoutSeconds.toString()
|
|
1094
1148
|
);
|
|
1095
1149
|
}
|
|
1096
|
-
if (this.options.
|
|
1150
|
+
if (this.options.enableRemoteSessions) {
|
|
1097
1151
|
args.push("--remote");
|
|
1098
1152
|
}
|
|
1099
|
-
const envWithoutNodeDebug = { ...this.
|
|
1153
|
+
const envWithoutNodeDebug = { ...this.resolvedEnv };
|
|
1100
1154
|
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
1101
1155
|
if (this.options.gitHubToken) {
|
|
1102
1156
|
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
@@ -1104,12 +1158,12 @@ class CopilotClient {
|
|
|
1104
1158
|
if (this.effectiveConnectionToken) {
|
|
1105
1159
|
envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1106
1160
|
}
|
|
1107
|
-
if (this.options.
|
|
1108
|
-
envWithoutNodeDebug.COPILOT_HOME = this.options.
|
|
1161
|
+
if (this.options.baseDirectory) {
|
|
1162
|
+
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1109
1163
|
}
|
|
1110
|
-
if (!this.
|
|
1164
|
+
if (!this.resolvedCliPath) {
|
|
1111
1165
|
throw new Error(
|
|
1112
|
-
"Path to Copilot CLI is required. Please
|
|
1166
|
+
"Path to Copilot CLI is required. Please supply it via `RuntimeConnection.forStdio({ path })` or `RuntimeConnection.forTcp({ path })`, set the COPILOT_CLI_PATH environment variable, or use `RuntimeConnection.forUri(...)` to connect to an already-running runtime."
|
|
1113
1167
|
);
|
|
1114
1168
|
}
|
|
1115
1169
|
if (this.options.telemetry) {
|
|
@@ -1128,31 +1182,31 @@ class CopilotClient {
|
|
|
1128
1182
|
t.captureContent
|
|
1129
1183
|
);
|
|
1130
1184
|
}
|
|
1131
|
-
if (!existsSync(this.
|
|
1185
|
+
if (!existsSync(this.resolvedCliPath)) {
|
|
1132
1186
|
throw new Error(
|
|
1133
|
-
`Copilot CLI not found at ${this.
|
|
1187
|
+
`Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
|
|
1134
1188
|
);
|
|
1135
1189
|
}
|
|
1136
|
-
const stdioConfig = this.
|
|
1137
|
-
const isJsFile = this.
|
|
1190
|
+
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
1191
|
+
const isJsFile = this.resolvedCliPath.endsWith(".js");
|
|
1138
1192
|
if (isJsFile) {
|
|
1139
|
-
this.cliProcess = spawn(getNodeExecPath(), [this.
|
|
1193
|
+
this.cliProcess = spawn(getNodeExecPath(), [this.resolvedCliPath, ...args], {
|
|
1140
1194
|
stdio: stdioConfig,
|
|
1141
|
-
cwd: this.options.
|
|
1195
|
+
cwd: this.options.workingDirectory,
|
|
1142
1196
|
env: envWithoutNodeDebug,
|
|
1143
1197
|
windowsHide: true
|
|
1144
1198
|
});
|
|
1145
1199
|
} else {
|
|
1146
|
-
this.cliProcess = spawn(this.
|
|
1200
|
+
this.cliProcess = spawn(this.resolvedCliPath, args, {
|
|
1147
1201
|
stdio: stdioConfig,
|
|
1148
|
-
cwd: this.options.
|
|
1202
|
+
cwd: this.options.workingDirectory,
|
|
1149
1203
|
env: envWithoutNodeDebug,
|
|
1150
1204
|
windowsHide: true
|
|
1151
1205
|
});
|
|
1152
1206
|
}
|
|
1153
1207
|
let stdout = "";
|
|
1154
1208
|
let resolved = false;
|
|
1155
|
-
if (this.
|
|
1209
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1156
1210
|
resolved = true;
|
|
1157
1211
|
resolve();
|
|
1158
1212
|
} else {
|
|
@@ -1160,7 +1214,7 @@ class CopilotClient {
|
|
|
1160
1214
|
stdout += data.toString();
|
|
1161
1215
|
const match = stdout.match(/listening on port (\d+)/i);
|
|
1162
1216
|
if (match && !resolved) {
|
|
1163
|
-
this.
|
|
1217
|
+
this.runtimePort = parseInt(match[1], 10);
|
|
1164
1218
|
resolved = true;
|
|
1165
1219
|
resolve();
|
|
1166
1220
|
}
|
|
@@ -1234,19 +1288,21 @@ stderr: ${stderrOutput}`
|
|
|
1234
1288
|
resolved = true;
|
|
1235
1289
|
reject(new Error("Timeout waiting for CLI server to start"));
|
|
1236
1290
|
}
|
|
1237
|
-
},
|
|
1291
|
+
}, 3e4);
|
|
1238
1292
|
});
|
|
1239
1293
|
}
|
|
1240
1294
|
/**
|
|
1241
1295
|
* Connect to the CLI server (via socket or stdio)
|
|
1242
1296
|
*/
|
|
1243
1297
|
async connectToServer() {
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1298
|
+
switch (this.connectionConfig.kind) {
|
|
1299
|
+
case "parent-process":
|
|
1300
|
+
return this.connectToParentProcessViaStdio();
|
|
1301
|
+
case "stdio":
|
|
1302
|
+
return this.connectToChildProcessViaStdio();
|
|
1303
|
+
case "tcp":
|
|
1304
|
+
case "uri":
|
|
1305
|
+
return this.connectViaTcp();
|
|
1250
1306
|
}
|
|
1251
1307
|
}
|
|
1252
1308
|
/**
|
|
@@ -1286,7 +1342,7 @@ stderr: ${stderrOutput}`
|
|
|
1286
1342
|
* Connect to the CLI server via TCP socket
|
|
1287
1343
|
*/
|
|
1288
1344
|
async connectViaTcp() {
|
|
1289
|
-
if (!this.
|
|
1345
|
+
if (!this.runtimePort) {
|
|
1290
1346
|
throw new Error("Server port not available");
|
|
1291
1347
|
}
|
|
1292
1348
|
return new Promise((resolve, reject) => {
|
|
@@ -1295,7 +1351,7 @@ stderr: ${stderrOutput}`
|
|
|
1295
1351
|
this.socket?.destroy();
|
|
1296
1352
|
reject(new Error("Timeout connecting to CLI server"));
|
|
1297
1353
|
}, 1e4);
|
|
1298
|
-
this.socket.connect(this.
|
|
1354
|
+
this.socket.connect(this.runtimePort, this.actualHost, () => {
|
|
1299
1355
|
clearTimeout(connectionTimeout);
|
|
1300
1356
|
this.connection = createMessageConnection(
|
|
1301
1357
|
new StreamMessageReader(this.socket),
|
|
@@ -1321,14 +1377,6 @@ stderr: ${stderrOutput}`
|
|
|
1321
1377
|
this.connection.onNotification("session.lifecycle", (notification) => {
|
|
1322
1378
|
this.handleSessionLifecycleNotification(notification);
|
|
1323
1379
|
});
|
|
1324
|
-
this.connection.onRequest(
|
|
1325
|
-
"tool.call",
|
|
1326
|
-
async (params) => await this.handleToolCallRequestV2(params)
|
|
1327
|
-
);
|
|
1328
|
-
this.connection.onRequest(
|
|
1329
|
-
"permission.request",
|
|
1330
|
-
async (params) => await this.handlePermissionRequestV2(params)
|
|
1331
|
-
);
|
|
1332
1380
|
this.connection.onRequest(
|
|
1333
1381
|
"userInput.request",
|
|
1334
1382
|
async (params) => await this.handleUserInputRequest(params)
|
|
@@ -1349,6 +1397,18 @@ stderr: ${stderrOutput}`
|
|
|
1349
1397
|
"systemMessage.transform",
|
|
1350
1398
|
async (params) => await this.handleSystemMessageTransform(params)
|
|
1351
1399
|
);
|
|
1400
|
+
this.connection.onRequest(
|
|
1401
|
+
"canvas.open",
|
|
1402
|
+
async (params) => this.handleCanvasProviderRequest("canvas.open", params)
|
|
1403
|
+
);
|
|
1404
|
+
this.connection.onRequest(
|
|
1405
|
+
"canvas.close",
|
|
1406
|
+
async (params) => this.handleCanvasProviderRequest("canvas.close", params)
|
|
1407
|
+
);
|
|
1408
|
+
this.connection.onRequest(
|
|
1409
|
+
"canvas.action.invoke",
|
|
1410
|
+
async (params) => this.handleCanvasActionInvokeRequest(params)
|
|
1411
|
+
);
|
|
1352
1412
|
const sessions = this.sessions;
|
|
1353
1413
|
registerClientSessionApiHandlers(this.connection, (sessionId) => {
|
|
1354
1414
|
const session = sessions.get(sessionId);
|
|
@@ -1375,7 +1435,20 @@ stderr: ${stderrOutput}`
|
|
|
1375
1435
|
if (typeof notification !== "object" || !notification || !("type" in notification) || typeof notification.type !== "string" || !("sessionId" in notification) || typeof notification.sessionId !== "string") {
|
|
1376
1436
|
return;
|
|
1377
1437
|
}
|
|
1378
|
-
const
|
|
1438
|
+
const raw = notification;
|
|
1439
|
+
let metadata;
|
|
1440
|
+
if (raw.metadata && raw.metadata.startTime && raw.metadata.modifiedTime) {
|
|
1441
|
+
metadata = {
|
|
1442
|
+
startTime: new Date(raw.metadata.startTime),
|
|
1443
|
+
modifiedTime: new Date(raw.metadata.modifiedTime),
|
|
1444
|
+
summary: raw.metadata.summary
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
const event = {
|
|
1448
|
+
type: raw.type,
|
|
1449
|
+
sessionId: raw.sessionId,
|
|
1450
|
+
metadata
|
|
1451
|
+
};
|
|
1379
1452
|
const typedHandlers = this.typedLifecycleHandlers.get(event.type);
|
|
1380
1453
|
if (typedHandlers) {
|
|
1381
1454
|
for (const handler of typedHandlers) {
|
|
@@ -1457,104 +1530,25 @@ stderr: ${stderrOutput}`
|
|
|
1457
1530
|
}
|
|
1458
1531
|
return await session._handleSystemMessageTransform(params.sections);
|
|
1459
1532
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
/**
|
|
1464
|
-
* Handles a v2-style tool.call RPC request from the server.
|
|
1465
|
-
* Looks up the session and tool handler, executes it, and returns the result
|
|
1466
|
-
* in the v2 response format.
|
|
1467
|
-
*/
|
|
1468
|
-
async handleToolCallRequestV2(params) {
|
|
1469
|
-
if (!params || typeof params.sessionId !== "string" || typeof params.toolCallId !== "string" || typeof params.toolName !== "string") {
|
|
1470
|
-
throw new Error("Invalid tool call payload");
|
|
1471
|
-
}
|
|
1472
|
-
const session = this.sessions.get(params.sessionId);
|
|
1473
|
-
if (!session) {
|
|
1474
|
-
throw new Error(`Unknown session ${params.sessionId}`);
|
|
1475
|
-
}
|
|
1476
|
-
const handler = session.getToolHandler(params.toolName);
|
|
1477
|
-
if (!handler) {
|
|
1478
|
-
return {
|
|
1479
|
-
result: {
|
|
1480
|
-
textResultForLlm: `Tool '${params.toolName}' is not supported by this client instance.`,
|
|
1481
|
-
resultType: "failure",
|
|
1482
|
-
error: `tool '${params.toolName}' not supported`,
|
|
1483
|
-
toolTelemetry: {}
|
|
1484
|
-
}
|
|
1485
|
-
};
|
|
1486
|
-
}
|
|
1487
|
-
try {
|
|
1488
|
-
const traceparent = params.traceparent;
|
|
1489
|
-
const tracestate = params.tracestate;
|
|
1490
|
-
const invocation = {
|
|
1491
|
-
sessionId: params.sessionId,
|
|
1492
|
-
toolCallId: params.toolCallId,
|
|
1493
|
-
toolName: params.toolName,
|
|
1494
|
-
arguments: params.arguments,
|
|
1495
|
-
traceparent,
|
|
1496
|
-
tracestate
|
|
1497
|
-
};
|
|
1498
|
-
const result = await handler(params.arguments, invocation);
|
|
1499
|
-
return { result: this.normalizeToolResultV2(result) };
|
|
1500
|
-
} catch (error) {
|
|
1501
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1502
|
-
return {
|
|
1503
|
-
result: {
|
|
1504
|
-
textResultForLlm: "Invoking this tool produced an error. Detailed information is not available.",
|
|
1505
|
-
resultType: "failure",
|
|
1506
|
-
error: message,
|
|
1507
|
-
toolTelemetry: {}
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
/**
|
|
1513
|
-
* Handles a v2-style permission.request RPC request from the server.
|
|
1514
|
-
*/
|
|
1515
|
-
async handlePermissionRequestV2(params) {
|
|
1516
|
-
if (!params || typeof params.sessionId !== "string" || !params.permissionRequest) {
|
|
1517
|
-
throw new Error("Invalid permission request payload");
|
|
1533
|
+
async handleCanvasProviderRequest(actionName, params) {
|
|
1534
|
+
if (!isCanvasProviderRequestParams(params)) {
|
|
1535
|
+
throw new Error("Invalid canvas provider request payload");
|
|
1518
1536
|
}
|
|
1519
1537
|
const session = this.sessions.get(params.sessionId);
|
|
1520
1538
|
if (!session) {
|
|
1521
1539
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1522
1540
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
} catch (error) {
|
|
1527
|
-
if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
|
|
1528
|
-
throw error;
|
|
1529
|
-
}
|
|
1530
|
-
return {
|
|
1531
|
-
result: {
|
|
1532
|
-
kind: "user-not-available"
|
|
1533
|
-
}
|
|
1534
|
-
};
|
|
1541
|
+
const canvas = session.getCanvas(params.canvasId);
|
|
1542
|
+
if (!canvas) {
|
|
1543
|
+
throw new Error(`No canvas registered with id "${params.canvasId}"`);
|
|
1535
1544
|
}
|
|
1545
|
+
return dispatchCanvasProviderRequest(canvas, actionName, params);
|
|
1536
1546
|
}
|
|
1537
|
-
|
|
1538
|
-
if (
|
|
1539
|
-
|
|
1540
|
-
textResultForLlm: "Tool returned no result",
|
|
1541
|
-
resultType: "failure",
|
|
1542
|
-
error: "tool returned no result",
|
|
1543
|
-
toolTelemetry: {}
|
|
1544
|
-
};
|
|
1545
|
-
}
|
|
1546
|
-
if (this.isToolResultObject(result)) {
|
|
1547
|
-
return result;
|
|
1547
|
+
async handleCanvasActionInvokeRequest(params) {
|
|
1548
|
+
if (!isCanvasActionInvokeParams(params)) {
|
|
1549
|
+
throw new Error("Invalid canvas provider request payload");
|
|
1548
1550
|
}
|
|
1549
|
-
|
|
1550
|
-
return {
|
|
1551
|
-
textResultForLlm: textResult,
|
|
1552
|
-
resultType: "success",
|
|
1553
|
-
toolTelemetry: {}
|
|
1554
|
-
};
|
|
1555
|
-
}
|
|
1556
|
-
isToolResultObject(value) {
|
|
1557
|
-
return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
|
|
1551
|
+
return this.handleCanvasProviderRequest(params.actionName, params);
|
|
1558
1552
|
}
|
|
1559
1553
|
}
|
|
1560
1554
|
export {
|