@github/copilot-sdk 1.0.0-beta.4 → 1.0.0-beta.6
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 +35 -33
- package/dist/cjs/client.js +222 -166
- package/dist/cjs/extension.js +2 -2
- package/dist/cjs/generated/rpc.js +1150 -13
- package/dist/cjs/index.js +9 -6
- package/dist/cjs/session.js +22 -61
- package/dist/cjs/sessionFsProvider.js +34 -0
- package/dist/cjs/types.js +30 -0
- package/dist/client.d.ts +45 -24
- package/dist/client.js +222 -166
- package/dist/extension.js +2 -2
- package/dist/generated/rpc.d.ts +8486 -1065
- package/dist/generated/rpc.js +1150 -13
- package/dist/generated/session-events.d.ts +1132 -110
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/session.d.ts +5 -212
- package/dist/session.js +22 -61
- package/dist/sessionFsProvider.d.ts +28 -1
- package/dist/sessionFsProvider.js +34 -0
- package/dist/types.d.ts +301 -115
- package/dist/types.js +29 -0
- package/docs/examples.md +3 -3
- package/package.json +3 -3
package/dist/cjs/client.js
CHANGED
|
@@ -36,11 +36,6 @@ var import_sessionFsProvider = require("./sessionFsProvider.js");
|
|
|
36
36
|
var import_telemetry = require("./telemetry.js");
|
|
37
37
|
var import_types = require("./types.js");
|
|
38
38
|
const import_meta = {};
|
|
39
|
-
function toWireProviderConfig(provider) {
|
|
40
|
-
const { maxInputTokens, ...rest } = provider;
|
|
41
|
-
if (maxInputTokens === void 0) return rest;
|
|
42
|
-
return { ...rest, maxPromptTokens: maxInputTokens };
|
|
43
|
-
}
|
|
44
39
|
const MIN_PROTOCOL_VERSION = 2;
|
|
45
40
|
function isZodSchema(value) {
|
|
46
41
|
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
@@ -52,6 +47,26 @@ function toJsonSchema(parameters) {
|
|
|
52
47
|
}
|
|
53
48
|
return parameters;
|
|
54
49
|
}
|
|
50
|
+
function toWireMcpServers(mcpServers) {
|
|
51
|
+
if (!mcpServers) return void 0;
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
Object.entries(mcpServers).map(([name, server]) => {
|
|
54
|
+
if ("workingDirectory" in server) {
|
|
55
|
+
const { workingDirectory, ...rest } = server;
|
|
56
|
+
return [name, { ...rest, cwd: workingDirectory }];
|
|
57
|
+
}
|
|
58
|
+
return [name, server];
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
function toWireCustomAgents(agents) {
|
|
63
|
+
if (!agents) return void 0;
|
|
64
|
+
return agents.map((agent) => {
|
|
65
|
+
if (!agent.mcpServers) return agent;
|
|
66
|
+
const { mcpServers, ...rest } = agent;
|
|
67
|
+
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
55
70
|
function extractTransformCallbacks(systemMessage) {
|
|
56
71
|
if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
|
|
57
72
|
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
@@ -105,12 +120,18 @@ class CopilotClient {
|
|
|
105
120
|
cliProcess = null;
|
|
106
121
|
connection = null;
|
|
107
122
|
socket = null;
|
|
108
|
-
|
|
123
|
+
runtimePort = null;
|
|
109
124
|
actualHost = "localhost";
|
|
110
125
|
state = "disconnected";
|
|
111
126
|
sessions = /* @__PURE__ */ new Map();
|
|
112
127
|
stderrBuffer = "";
|
|
113
128
|
// Captures CLI stderr for error messages
|
|
129
|
+
/** Resolved connection mode chosen in the constructor. */
|
|
130
|
+
connectionConfig;
|
|
131
|
+
/** Resolved path to the runtime executable (only used for child-process kinds). */
|
|
132
|
+
resolvedCliPath;
|
|
133
|
+
/** Resolved environment passed to the spawned runtime. */
|
|
134
|
+
resolvedEnv;
|
|
114
135
|
options;
|
|
115
136
|
isExternalServer = false;
|
|
116
137
|
forceStopping = false;
|
|
@@ -159,86 +180,79 @@ class CopilotClient {
|
|
|
159
180
|
* Creates a new CopilotClient instance.
|
|
160
181
|
*
|
|
161
182
|
* @param options - Configuration options for the client
|
|
162
|
-
* @throws Error if mutually exclusive options are provided (e.g., cliUrl with useStdio or cliPath)
|
|
163
183
|
*
|
|
164
184
|
* @example
|
|
165
185
|
* ```typescript
|
|
166
|
-
* // Default
|
|
186
|
+
* // Default: spawns the bundled runtime over stdio
|
|
167
187
|
* const client = new CopilotClient();
|
|
168
188
|
*
|
|
169
|
-
* // Connect to an existing
|
|
170
|
-
* const client = new CopilotClient({
|
|
189
|
+
* // Connect to an existing runtime
|
|
190
|
+
* const client = new CopilotClient({
|
|
191
|
+
* connection: RuntimeConnection.forUri("localhost:3000"),
|
|
192
|
+
* });
|
|
193
|
+
*
|
|
194
|
+
* // Spawn the runtime over TCP on a chosen port
|
|
195
|
+
* const client = new CopilotClient({
|
|
196
|
+
* connection: RuntimeConnection.forTcp({ port: 9001 }),
|
|
197
|
+
* });
|
|
171
198
|
*
|
|
172
|
-
* //
|
|
199
|
+
* // Use a custom runtime binary
|
|
173
200
|
* const client = new CopilotClient({
|
|
174
|
-
*
|
|
175
|
-
* logLevel: "debug"
|
|
201
|
+
* connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }),
|
|
202
|
+
* logLevel: "debug",
|
|
176
203
|
* });
|
|
177
204
|
* ```
|
|
178
205
|
*/
|
|
179
206
|
constructor(options = {}) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
if (options.isChildProcess && (options.cliUrl || options.useStdio === false)) {
|
|
207
|
+
const conn = options._internalConnection ?? options.connection ?? { kind: "stdio" };
|
|
208
|
+
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
184
209
|
throw new Error(
|
|
185
|
-
"
|
|
210
|
+
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
186
211
|
);
|
|
187
212
|
}
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
if (options.tcpConnectionToken !== void 0) {
|
|
194
|
-
if (typeof options.tcpConnectionToken !== "string" || options.tcpConnectionToken.length === 0) {
|
|
195
|
-
throw new Error("tcpConnectionToken must be a non-empty string");
|
|
196
|
-
}
|
|
197
|
-
if (options.useStdio === true) {
|
|
198
|
-
throw new Error("tcpConnectionToken cannot be used with useStdio: true");
|
|
213
|
+
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
214
|
+
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
215
|
+
throw new Error("connectionToken must be a non-empty string");
|
|
199
216
|
}
|
|
200
217
|
}
|
|
201
|
-
|
|
202
|
-
const sdkSpawnsCli = !willUseStdio && !options.cliUrl && !options.isChildProcess;
|
|
203
|
-
this.effectiveConnectionToken = options.tcpConnectionToken ?? (sdkSpawnsCli ? (0, import_node_crypto.randomUUID)() : void 0);
|
|
218
|
+
this.connectionConfig = conn;
|
|
204
219
|
if (options.sessionFs) {
|
|
205
220
|
this.validateSessionFsConfig(options.sessionFs);
|
|
206
221
|
}
|
|
207
|
-
if (
|
|
208
|
-
const { host, port } = this.parseCliUrl(
|
|
222
|
+
if (conn.kind === "uri") {
|
|
223
|
+
const { host, port } = this.parseCliUrl(conn.url);
|
|
209
224
|
this.actualHost = host;
|
|
210
|
-
this.
|
|
225
|
+
this.runtimePort = port;
|
|
211
226
|
this.isExternalServer = true;
|
|
212
|
-
}
|
|
213
|
-
if (options.isChildProcess) {
|
|
227
|
+
} else if (conn.kind === "parent-process") {
|
|
214
228
|
this.isExternalServer = true;
|
|
215
229
|
}
|
|
230
|
+
if (conn.kind === "tcp") {
|
|
231
|
+
this.effectiveConnectionToken = conn.connectionToken ?? (0, import_node_crypto.randomUUID)();
|
|
232
|
+
} else if (conn.kind === "uri") {
|
|
233
|
+
this.effectiveConnectionToken = conn.connectionToken;
|
|
234
|
+
}
|
|
216
235
|
this.onListModels = options.onListModels;
|
|
217
236
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
218
237
|
this.sessionFsConfig = options.sessionFs ?? null;
|
|
219
238
|
const effectiveEnv = options.env ?? process.env;
|
|
239
|
+
this.resolvedEnv = effectiveEnv;
|
|
240
|
+
this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
|
|
241
|
+
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
242
|
+
this.connectionExtraArgs = [...connArgs];
|
|
220
243
|
this.options = {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
cwd: options.cwd ?? process.cwd(),
|
|
224
|
-
port: options.port || 0,
|
|
225
|
-
useStdio: options.cliUrl ? false : options.useStdio ?? true,
|
|
226
|
-
// Default to stdio unless cliUrl is provided
|
|
227
|
-
isChildProcess: options.isChildProcess ?? false,
|
|
228
|
-
cliUrl: options.cliUrl,
|
|
229
|
-
logLevel: options.logLevel || "debug",
|
|
230
|
-
autoStart: options.autoStart ?? true,
|
|
231
|
-
autoRestart: false,
|
|
232
|
-
env: effectiveEnv,
|
|
244
|
+
workingDirectory: options.workingDirectory ?? process.cwd(),
|
|
245
|
+
logLevel: options.logLevel,
|
|
233
246
|
gitHubToken: options.gitHubToken,
|
|
234
|
-
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true
|
|
247
|
+
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true.
|
|
235
248
|
useLoggedInUser: options.useLoggedInUser ?? (options.gitHubToken ? false : true),
|
|
236
249
|
telemetry: options.telemetry,
|
|
237
|
-
|
|
250
|
+
baseDirectory: options.baseDirectory,
|
|
238
251
|
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
239
|
-
|
|
252
|
+
enableRemoteSessions: options.enableRemoteSessions ?? false
|
|
240
253
|
};
|
|
241
254
|
}
|
|
255
|
+
connectionExtraArgs = [];
|
|
242
256
|
/**
|
|
243
257
|
* Parse CLI URL into host and port
|
|
244
258
|
* Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
|
|
@@ -272,20 +286,37 @@ class CopilotClient {
|
|
|
272
286
|
throw new Error("sessionFs.conventions must be either 'windows' or 'posix'");
|
|
273
287
|
}
|
|
274
288
|
}
|
|
289
|
+
setupSessionFs(session, config) {
|
|
290
|
+
if (!this.sessionFsConfig) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!config.createSessionFsProvider) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
"createSessionFsProvider is required in session config when sessionFs is enabled in client options."
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
const provider = config.createSessionFsProvider(session);
|
|
299
|
+
if (this.sessionFsConfig.capabilities?.sqlite && !provider.sqlite) {
|
|
300
|
+
throw new Error(
|
|
301
|
+
"SessionFsConfig declares capabilities.sqlite but the provider does not implement sqlite."
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(provider);
|
|
305
|
+
}
|
|
275
306
|
/**
|
|
276
307
|
* Starts the CLI server and establishes a connection.
|
|
277
308
|
*
|
|
278
309
|
* If connecting to an external server (via cliUrl), only establishes the connection.
|
|
279
310
|
* Otherwise, spawns the CLI server process and then connects.
|
|
280
311
|
*
|
|
281
|
-
* This method is called automatically
|
|
312
|
+
* This method is called automatically the first time you create or resume a session.
|
|
282
313
|
*
|
|
283
314
|
* @returns A promise that resolves when the connection is established
|
|
284
315
|
* @throws Error if the server fails to start or the connection fails
|
|
285
316
|
*
|
|
286
317
|
* @example
|
|
287
318
|
* ```typescript
|
|
288
|
-
* const client = new CopilotClient(
|
|
319
|
+
* const client = new CopilotClient();
|
|
289
320
|
* await client.start();
|
|
290
321
|
* // Now ready to create sessions
|
|
291
322
|
* ```
|
|
@@ -305,7 +336,8 @@ class CopilotClient {
|
|
|
305
336
|
await this.connection.sendRequest("sessionFs.setProvider", {
|
|
306
337
|
initialCwd: this.sessionFsConfig.initialCwd,
|
|
307
338
|
sessionStatePath: this.sessionFsConfig.sessionStatePath,
|
|
308
|
-
conventions: this.sessionFsConfig.conventions
|
|
339
|
+
conventions: this.sessionFsConfig.conventions,
|
|
340
|
+
capabilities: this.sessionFsConfig.capabilities
|
|
309
341
|
});
|
|
310
342
|
}
|
|
311
343
|
this.state = "connected";
|
|
@@ -379,8 +411,15 @@ class CopilotClient {
|
|
|
379
411
|
}
|
|
380
412
|
this.modelsCache = null;
|
|
381
413
|
if (this.socket) {
|
|
414
|
+
const socket = this.socket;
|
|
415
|
+
this.socket = null;
|
|
382
416
|
try {
|
|
383
|
-
|
|
417
|
+
if (!socket.destroyed) {
|
|
418
|
+
await new Promise((resolve) => {
|
|
419
|
+
socket.once("close", () => resolve());
|
|
420
|
+
socket.end();
|
|
421
|
+
});
|
|
422
|
+
}
|
|
384
423
|
} catch (error) {
|
|
385
424
|
errors.push(
|
|
386
425
|
new Error(
|
|
@@ -388,11 +427,18 @@ class CopilotClient {
|
|
|
388
427
|
)
|
|
389
428
|
);
|
|
390
429
|
}
|
|
391
|
-
this.socket = null;
|
|
392
430
|
}
|
|
393
431
|
if (this.cliProcess && !this.isExternalServer) {
|
|
432
|
+
const child = this.cliProcess;
|
|
433
|
+
this.cliProcess = null;
|
|
394
434
|
try {
|
|
395
|
-
|
|
435
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
436
|
+
const exited = new Promise((resolve) => {
|
|
437
|
+
child.once("exit", () => resolve());
|
|
438
|
+
});
|
|
439
|
+
child.kill();
|
|
440
|
+
await exited;
|
|
441
|
+
}
|
|
396
442
|
} catch (error) {
|
|
397
443
|
errors.push(
|
|
398
444
|
new Error(
|
|
@@ -400,18 +446,32 @@ class CopilotClient {
|
|
|
400
446
|
)
|
|
401
447
|
);
|
|
402
448
|
}
|
|
403
|
-
this.cliProcess = null;
|
|
404
449
|
}
|
|
405
450
|
if (this.cliStartTimeout) {
|
|
406
451
|
clearTimeout(this.cliStartTimeout);
|
|
407
452
|
this.cliStartTimeout = null;
|
|
408
453
|
}
|
|
409
454
|
this.state = "disconnected";
|
|
410
|
-
this.
|
|
455
|
+
this.runtimePort = null;
|
|
411
456
|
this.stderrBuffer = "";
|
|
412
457
|
this.processExitPromise = null;
|
|
413
458
|
return errors;
|
|
414
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* Alias for {@link stop} that lets `CopilotClient` participate in `await using`
|
|
462
|
+
* blocks for automatic cleanup.
|
|
463
|
+
*
|
|
464
|
+
* @example
|
|
465
|
+
* ```typescript
|
|
466
|
+
* await using client = new CopilotClient();
|
|
467
|
+
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
468
|
+
* await session.sendAndWait("Hello");
|
|
469
|
+
* // client.stop() is called automatically when the block exits.
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
async [Symbol.asyncDispose]() {
|
|
473
|
+
await this.stop();
|
|
474
|
+
}
|
|
415
475
|
/**
|
|
416
476
|
* Forcefully stops the CLI server without graceful cleanup.
|
|
417
477
|
*
|
|
@@ -468,7 +528,7 @@ class CopilotClient {
|
|
|
468
528
|
this.cliStartTimeout = null;
|
|
469
529
|
}
|
|
470
530
|
this.state = "disconnected";
|
|
471
|
-
this.
|
|
531
|
+
this.runtimePort = null;
|
|
472
532
|
this.stderrBuffer = "";
|
|
473
533
|
this.processExitPromise = null;
|
|
474
534
|
}
|
|
@@ -476,12 +536,11 @@ class CopilotClient {
|
|
|
476
536
|
* Creates a new conversation session with the Copilot CLI.
|
|
477
537
|
*
|
|
478
538
|
* Sessions maintain conversation state, handle events, and manage tool execution.
|
|
479
|
-
* If the client is not connected
|
|
480
|
-
* start the connection.
|
|
539
|
+
* If the client is not connected, this method automatically starts the connection.
|
|
481
540
|
*
|
|
482
541
|
* @param config - Optional configuration for the session
|
|
483
542
|
* @returns A promise that resolves with the created session
|
|
484
|
-
* @throws Error if the client
|
|
543
|
+
* @throws Error if the client fails to start
|
|
485
544
|
*
|
|
486
545
|
* @example
|
|
487
546
|
* ```typescript
|
|
@@ -502,17 +561,8 @@ class CopilotClient {
|
|
|
502
561
|
* ```
|
|
503
562
|
*/
|
|
504
563
|
async createSession(config) {
|
|
505
|
-
if (!config?.onPermissionRequest) {
|
|
506
|
-
throw new Error(
|
|
507
|
-
"An onPermissionRequest handler is required when creating a session. For example, to allow all permissions, use { onPermissionRequest: approveAll }."
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
564
|
if (!this.connection) {
|
|
511
|
-
|
|
512
|
-
await this.start();
|
|
513
|
-
} else {
|
|
514
|
-
throw new Error("Client not connected. Call start() first.");
|
|
515
|
-
}
|
|
565
|
+
await this.start();
|
|
516
566
|
}
|
|
517
567
|
const sessionId = config.sessionId ?? (0, import_node_crypto.randomUUID)();
|
|
518
568
|
const session = new import_session.CopilotSession(
|
|
@@ -530,11 +580,11 @@ class CopilotClient {
|
|
|
530
580
|
if (config.onElicitationRequest) {
|
|
531
581
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
532
582
|
}
|
|
533
|
-
if (config.
|
|
534
|
-
session.registerExitPlanModeHandler(config.
|
|
583
|
+
if (config.onExitPlanModeRequest) {
|
|
584
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
535
585
|
}
|
|
536
|
-
if (config.
|
|
537
|
-
session.registerAutoModeSwitchHandler(config.
|
|
586
|
+
if (config.onAutoModeSwitchRequest) {
|
|
587
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
538
588
|
}
|
|
539
589
|
if (config.hooks) {
|
|
540
590
|
session.registerHooks(config.hooks);
|
|
@@ -549,17 +599,7 @@ class CopilotClient {
|
|
|
549
599
|
session.on(config.onEvent);
|
|
550
600
|
}
|
|
551
601
|
this.sessions.set(sessionId, session);
|
|
552
|
-
|
|
553
|
-
if (config.createSessionFsHandler) {
|
|
554
|
-
session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(
|
|
555
|
-
config.createSessionFsHandler(session)
|
|
556
|
-
);
|
|
557
|
-
} else {
|
|
558
|
-
throw new Error(
|
|
559
|
-
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
602
|
+
this.setupSessionFs(session, config);
|
|
563
603
|
try {
|
|
564
604
|
const response = await this.connection.sendRequest("session.create", {
|
|
565
605
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
@@ -581,21 +621,21 @@ class CopilotClient {
|
|
|
581
621
|
systemMessage: wireSystemMessage,
|
|
582
622
|
availableTools: config.availableTools,
|
|
583
623
|
excludedTools: config.excludedTools,
|
|
584
|
-
provider: config.provider
|
|
624
|
+
provider: config.provider,
|
|
585
625
|
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
586
626
|
modelCapabilities: config.modelCapabilities,
|
|
587
627
|
requestPermission: true,
|
|
588
628
|
requestUserInput: !!config.onUserInputRequest,
|
|
589
629
|
requestElicitation: !!config.onElicitationRequest,
|
|
590
|
-
requestExitPlanMode: !!config.
|
|
591
|
-
requestAutoModeSwitch: !!config.
|
|
630
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
631
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
592
632
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
593
633
|
workingDirectory: config.workingDirectory,
|
|
594
634
|
streaming: config.streaming,
|
|
595
635
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
596
|
-
mcpServers: config.mcpServers,
|
|
636
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
597
637
|
envValueMode: "direct",
|
|
598
|
-
customAgents: config.customAgents,
|
|
638
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
599
639
|
defaultAgent: config.defaultAgent,
|
|
600
640
|
agent: config.agent,
|
|
601
641
|
configDir: config.configDir,
|
|
@@ -604,7 +644,9 @@ class CopilotClient {
|
|
|
604
644
|
instructionDirectories: config.instructionDirectories,
|
|
605
645
|
disabledSkills: config.disabledSkills,
|
|
606
646
|
infiniteSessions: config.infiniteSessions,
|
|
607
|
-
gitHubToken: config.gitHubToken
|
|
647
|
+
gitHubToken: config.gitHubToken,
|
|
648
|
+
remoteSession: config.remoteSession,
|
|
649
|
+
cloud: config.cloud
|
|
608
650
|
});
|
|
609
651
|
const { workspacePath, capabilities } = response;
|
|
610
652
|
session["_workspacePath"] = workspacePath;
|
|
@@ -640,17 +682,8 @@ class CopilotClient {
|
|
|
640
682
|
* ```
|
|
641
683
|
*/
|
|
642
684
|
async resumeSession(sessionId, config) {
|
|
643
|
-
if (!config?.onPermissionRequest) {
|
|
644
|
-
throw new Error(
|
|
645
|
-
"An onPermissionRequest handler is required when resuming a session. For example, to allow all permissions, use { onPermissionRequest: approveAll }."
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
685
|
if (!this.connection) {
|
|
649
|
-
|
|
650
|
-
await this.start();
|
|
651
|
-
} else {
|
|
652
|
-
throw new Error("Client not connected. Call start() first.");
|
|
653
|
-
}
|
|
686
|
+
await this.start();
|
|
654
687
|
}
|
|
655
688
|
const session = new import_session.CopilotSession(
|
|
656
689
|
sessionId,
|
|
@@ -667,11 +700,11 @@ class CopilotClient {
|
|
|
667
700
|
if (config.onElicitationRequest) {
|
|
668
701
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
669
702
|
}
|
|
670
|
-
if (config.
|
|
671
|
-
session.registerExitPlanModeHandler(config.
|
|
703
|
+
if (config.onExitPlanModeRequest) {
|
|
704
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
672
705
|
}
|
|
673
|
-
if (config.
|
|
674
|
-
session.registerAutoModeSwitchHandler(config.
|
|
706
|
+
if (config.onAutoModeSwitchRequest) {
|
|
707
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
675
708
|
}
|
|
676
709
|
if (config.hooks) {
|
|
677
710
|
session.registerHooks(config.hooks);
|
|
@@ -686,17 +719,7 @@ class CopilotClient {
|
|
|
686
719
|
session.on(config.onEvent);
|
|
687
720
|
}
|
|
688
721
|
this.sessions.set(sessionId, session);
|
|
689
|
-
|
|
690
|
-
if (config.createSessionFsHandler) {
|
|
691
|
-
session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(
|
|
692
|
-
config.createSessionFsHandler(session)
|
|
693
|
-
);
|
|
694
|
-
} else {
|
|
695
|
-
throw new Error(
|
|
696
|
-
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
722
|
+
this.setupSessionFs(session, config);
|
|
700
723
|
try {
|
|
701
724
|
const response = await this.connection.sendRequest("session.resume", {
|
|
702
725
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
@@ -719,31 +742,32 @@ class CopilotClient {
|
|
|
719
742
|
name: cmd.name,
|
|
720
743
|
description: cmd.description
|
|
721
744
|
})),
|
|
722
|
-
provider: config.provider
|
|
745
|
+
provider: config.provider,
|
|
723
746
|
modelCapabilities: config.modelCapabilities,
|
|
724
747
|
requestPermission: config.onPermissionRequest !== import_types.defaultJoinSessionPermissionHandler,
|
|
725
748
|
requestUserInput: !!config.onUserInputRequest,
|
|
726
749
|
requestElicitation: !!config.onElicitationRequest,
|
|
727
|
-
requestExitPlanMode: !!config.
|
|
728
|
-
requestAutoModeSwitch: !!config.
|
|
750
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
751
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
729
752
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
730
753
|
workingDirectory: config.workingDirectory,
|
|
731
754
|
configDir: config.configDir,
|
|
732
755
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
733
756
|
streaming: config.streaming,
|
|
734
757
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
735
|
-
mcpServers: config.mcpServers,
|
|
758
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
736
759
|
envValueMode: "direct",
|
|
737
|
-
customAgents: config.customAgents,
|
|
760
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
738
761
|
defaultAgent: config.defaultAgent,
|
|
739
762
|
agent: config.agent,
|
|
740
763
|
skillDirectories: config.skillDirectories,
|
|
741
764
|
instructionDirectories: config.instructionDirectories,
|
|
742
765
|
disabledSkills: config.disabledSkills,
|
|
743
766
|
infiniteSessions: config.infiniteSessions,
|
|
744
|
-
|
|
767
|
+
suppressResumeEvent: config.suppressResumeEvent,
|
|
745
768
|
continuePendingWork: config.continuePendingWork,
|
|
746
|
-
gitHubToken: config.gitHubToken
|
|
769
|
+
gitHubToken: config.gitHubToken,
|
|
770
|
+
remoteSession: config.remoteSession
|
|
747
771
|
});
|
|
748
772
|
const { workspacePath, capabilities } = response;
|
|
749
773
|
session["_workspacePath"] = workspacePath;
|
|
@@ -970,8 +994,13 @@ class CopilotClient {
|
|
|
970
994
|
if (!this.connection) {
|
|
971
995
|
throw new Error("Client not connected");
|
|
972
996
|
}
|
|
997
|
+
let wireFilter;
|
|
998
|
+
if (filter) {
|
|
999
|
+
const { workingDirectory, ...rest } = filter;
|
|
1000
|
+
wireFilter = { ...rest, cwd: workingDirectory };
|
|
1001
|
+
}
|
|
973
1002
|
const response = await this.connection.sendRequest("session.list", {
|
|
974
|
-
filter
|
|
1003
|
+
filter: wireFilter
|
|
975
1004
|
});
|
|
976
1005
|
const { sessions } = response;
|
|
977
1006
|
return sessions.map(CopilotClient.toSessionMetadata);
|
|
@@ -1006,13 +1035,19 @@ class CopilotClient {
|
|
|
1006
1035
|
return CopilotClient.toSessionMetadata(session);
|
|
1007
1036
|
}
|
|
1008
1037
|
static toSessionMetadata(raw) {
|
|
1038
|
+
const { context } = raw;
|
|
1009
1039
|
return {
|
|
1010
1040
|
sessionId: raw.sessionId,
|
|
1011
1041
|
startTime: new Date(raw.startTime),
|
|
1012
1042
|
modifiedTime: new Date(raw.modifiedTime),
|
|
1013
1043
|
summary: raw.summary,
|
|
1014
1044
|
isRemote: raw.isRemote,
|
|
1015
|
-
context:
|
|
1045
|
+
context: context ? {
|
|
1046
|
+
workingDirectory: context.cwd,
|
|
1047
|
+
gitRoot: context.gitRoot,
|
|
1048
|
+
repository: context.repository,
|
|
1049
|
+
branch: context.branch
|
|
1050
|
+
} : void 0
|
|
1016
1051
|
};
|
|
1017
1052
|
}
|
|
1018
1053
|
/**
|
|
@@ -1065,7 +1100,7 @@ class CopilotClient {
|
|
|
1065
1100
|
throw new Error(result.error || "Failed to set foreground session");
|
|
1066
1101
|
}
|
|
1067
1102
|
}
|
|
1068
|
-
|
|
1103
|
+
onLifecycle(eventTypeOrHandler, handler) {
|
|
1069
1104
|
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
1070
1105
|
const eventType = eventTypeOrHandler;
|
|
1071
1106
|
if (!this.typedLifecycleHandlers.has(eventType)) {
|
|
@@ -1092,17 +1127,17 @@ class CopilotClient {
|
|
|
1092
1127
|
async startCLIServer() {
|
|
1093
1128
|
return new Promise((resolve, reject) => {
|
|
1094
1129
|
this.stderrBuffer = "";
|
|
1095
|
-
const args = [
|
|
1096
|
-
|
|
1097
|
-
"--
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
this.options.logLevel
|
|
1101
|
-
];
|
|
1102
|
-
if (this.options.useStdio) {
|
|
1130
|
+
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1131
|
+
if (this.options.logLevel) {
|
|
1132
|
+
args.push("--log-level", this.options.logLevel);
|
|
1133
|
+
}
|
|
1134
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1103
1135
|
args.push("--stdio");
|
|
1104
|
-
} else if (this.
|
|
1105
|
-
|
|
1136
|
+
} else if (this.connectionConfig.kind === "tcp") {
|
|
1137
|
+
const requestedPort = this.connectionConfig.port ?? 0;
|
|
1138
|
+
if (requestedPort > 0) {
|
|
1139
|
+
args.push("--port", requestedPort.toString());
|
|
1140
|
+
}
|
|
1106
1141
|
}
|
|
1107
1142
|
if (this.options.gitHubToken) {
|
|
1108
1143
|
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
@@ -1116,10 +1151,10 @@ class CopilotClient {
|
|
|
1116
1151
|
this.options.sessionIdleTimeoutSeconds.toString()
|
|
1117
1152
|
);
|
|
1118
1153
|
}
|
|
1119
|
-
if (this.options.
|
|
1154
|
+
if (this.options.enableRemoteSessions) {
|
|
1120
1155
|
args.push("--remote");
|
|
1121
1156
|
}
|
|
1122
|
-
const envWithoutNodeDebug = { ...this.
|
|
1157
|
+
const envWithoutNodeDebug = { ...this.resolvedEnv };
|
|
1123
1158
|
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
1124
1159
|
if (this.options.gitHubToken) {
|
|
1125
1160
|
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
@@ -1127,12 +1162,12 @@ class CopilotClient {
|
|
|
1127
1162
|
if (this.effectiveConnectionToken) {
|
|
1128
1163
|
envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1129
1164
|
}
|
|
1130
|
-
if (this.options.
|
|
1131
|
-
envWithoutNodeDebug.COPILOT_HOME = this.options.
|
|
1165
|
+
if (this.options.baseDirectory) {
|
|
1166
|
+
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1132
1167
|
}
|
|
1133
|
-
if (!this.
|
|
1168
|
+
if (!this.resolvedCliPath) {
|
|
1134
1169
|
throw new Error(
|
|
1135
|
-
"Path to Copilot CLI is required. Please
|
|
1170
|
+
"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."
|
|
1136
1171
|
);
|
|
1137
1172
|
}
|
|
1138
1173
|
if (this.options.telemetry) {
|
|
@@ -1151,31 +1186,31 @@ class CopilotClient {
|
|
|
1151
1186
|
t.captureContent
|
|
1152
1187
|
);
|
|
1153
1188
|
}
|
|
1154
|
-
if (!(0, import_node_fs.existsSync)(this.
|
|
1189
|
+
if (!(0, import_node_fs.existsSync)(this.resolvedCliPath)) {
|
|
1155
1190
|
throw new Error(
|
|
1156
|
-
`Copilot CLI not found at ${this.
|
|
1191
|
+
`Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
|
|
1157
1192
|
);
|
|
1158
1193
|
}
|
|
1159
|
-
const stdioConfig = this.
|
|
1160
|
-
const isJsFile = this.
|
|
1194
|
+
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
1195
|
+
const isJsFile = this.resolvedCliPath.endsWith(".js");
|
|
1161
1196
|
if (isJsFile) {
|
|
1162
|
-
this.cliProcess = (0, import_node_child_process.spawn)(getNodeExecPath(), [this.
|
|
1197
|
+
this.cliProcess = (0, import_node_child_process.spawn)(getNodeExecPath(), [this.resolvedCliPath, ...args], {
|
|
1163
1198
|
stdio: stdioConfig,
|
|
1164
|
-
cwd: this.options.
|
|
1199
|
+
cwd: this.options.workingDirectory,
|
|
1165
1200
|
env: envWithoutNodeDebug,
|
|
1166
1201
|
windowsHide: true
|
|
1167
1202
|
});
|
|
1168
1203
|
} else {
|
|
1169
|
-
this.cliProcess = (0, import_node_child_process.spawn)(this.
|
|
1204
|
+
this.cliProcess = (0, import_node_child_process.spawn)(this.resolvedCliPath, args, {
|
|
1170
1205
|
stdio: stdioConfig,
|
|
1171
|
-
cwd: this.options.
|
|
1206
|
+
cwd: this.options.workingDirectory,
|
|
1172
1207
|
env: envWithoutNodeDebug,
|
|
1173
1208
|
windowsHide: true
|
|
1174
1209
|
});
|
|
1175
1210
|
}
|
|
1176
1211
|
let stdout = "";
|
|
1177
1212
|
let resolved = false;
|
|
1178
|
-
if (this.
|
|
1213
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1179
1214
|
resolved = true;
|
|
1180
1215
|
resolve();
|
|
1181
1216
|
} else {
|
|
@@ -1183,7 +1218,7 @@ class CopilotClient {
|
|
|
1183
1218
|
stdout += data.toString();
|
|
1184
1219
|
const match = stdout.match(/listening on port (\d+)/i);
|
|
1185
1220
|
if (match && !resolved) {
|
|
1186
|
-
this.
|
|
1221
|
+
this.runtimePort = parseInt(match[1], 10);
|
|
1187
1222
|
resolved = true;
|
|
1188
1223
|
resolve();
|
|
1189
1224
|
}
|
|
@@ -1257,19 +1292,21 @@ stderr: ${stderrOutput}`
|
|
|
1257
1292
|
resolved = true;
|
|
1258
1293
|
reject(new Error("Timeout waiting for CLI server to start"));
|
|
1259
1294
|
}
|
|
1260
|
-
},
|
|
1295
|
+
}, 3e4);
|
|
1261
1296
|
});
|
|
1262
1297
|
}
|
|
1263
1298
|
/**
|
|
1264
1299
|
* Connect to the CLI server (via socket or stdio)
|
|
1265
1300
|
*/
|
|
1266
1301
|
async connectToServer() {
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1302
|
+
switch (this.connectionConfig.kind) {
|
|
1303
|
+
case "parent-process":
|
|
1304
|
+
return this.connectToParentProcessViaStdio();
|
|
1305
|
+
case "stdio":
|
|
1306
|
+
return this.connectToChildProcessViaStdio();
|
|
1307
|
+
case "tcp":
|
|
1308
|
+
case "uri":
|
|
1309
|
+
return this.connectViaTcp();
|
|
1273
1310
|
}
|
|
1274
1311
|
}
|
|
1275
1312
|
/**
|
|
@@ -1309,12 +1346,17 @@ stderr: ${stderrOutput}`
|
|
|
1309
1346
|
* Connect to the CLI server via TCP socket
|
|
1310
1347
|
*/
|
|
1311
1348
|
async connectViaTcp() {
|
|
1312
|
-
if (!this.
|
|
1349
|
+
if (!this.runtimePort) {
|
|
1313
1350
|
throw new Error("Server port not available");
|
|
1314
1351
|
}
|
|
1315
1352
|
return new Promise((resolve, reject) => {
|
|
1316
1353
|
this.socket = new import_node_net.Socket();
|
|
1317
|
-
|
|
1354
|
+
const connectionTimeout = setTimeout(() => {
|
|
1355
|
+
this.socket?.destroy();
|
|
1356
|
+
reject(new Error("Timeout connecting to CLI server"));
|
|
1357
|
+
}, 1e4);
|
|
1358
|
+
this.socket.connect(this.runtimePort, this.actualHost, () => {
|
|
1359
|
+
clearTimeout(connectionTimeout);
|
|
1318
1360
|
this.connection = (0, import_node.createMessageConnection)(
|
|
1319
1361
|
new import_node.StreamMessageReader(this.socket),
|
|
1320
1362
|
new import_node.StreamMessageWriter(this.socket)
|
|
@@ -1324,6 +1366,7 @@ stderr: ${stderrOutput}`
|
|
|
1324
1366
|
resolve();
|
|
1325
1367
|
});
|
|
1326
1368
|
this.socket.on("error", (error) => {
|
|
1369
|
+
clearTimeout(connectionTimeout);
|
|
1327
1370
|
reject(new Error(`Failed to connect to CLI server: ${error.message}`));
|
|
1328
1371
|
});
|
|
1329
1372
|
});
|
|
@@ -1392,7 +1435,20 @@ stderr: ${stderrOutput}`
|
|
|
1392
1435
|
if (typeof notification !== "object" || !notification || !("type" in notification) || typeof notification.type !== "string" || !("sessionId" in notification) || typeof notification.sessionId !== "string") {
|
|
1393
1436
|
return;
|
|
1394
1437
|
}
|
|
1395
|
-
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
|
+
};
|
|
1396
1452
|
const typedHandlers = this.typedLifecycleHandlers.get(event.type);
|
|
1397
1453
|
if (typedHandlers) {
|
|
1398
1454
|
for (const handler of typedHandlers) {
|