@github/copilot-sdk 1.0.0-beta.5 → 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 +22 -23
- package/dist/cjs/client.js +193 -134
- package/dist/cjs/extension.js +2 -2
- package/dist/cjs/generated/rpc.js +13 -1
- package/dist/cjs/index.js +9 -6
- package/dist/cjs/session.js +17 -58
- package/dist/cjs/types.js +30 -0
- package/dist/client.d.ts +43 -23
- package/dist/client.js +193 -134
- package/dist/extension.js +2 -2
- package/dist/generated/rpc.d.ts +67 -57
- package/dist/generated/rpc.js +13 -1
- package/dist/generated/session-events.d.ts +25 -9
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/session.d.ts +5 -212
- package/dist/session.js +17 -58
- package/dist/types.d.ts +239 -112
- package/dist/types.js +29 -0
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -22,11 +22,6 @@ import { CopilotSession, NO_RESULT_PERMISSION_V2_ERROR } from "./session.js";
|
|
|
22
22
|
import { createSessionFsAdapter } from "./sessionFsProvider.js";
|
|
23
23
|
import { getTraceContext } from "./telemetry.js";
|
|
24
24
|
import { defaultJoinSessionPermissionHandler } from "./types.js";
|
|
25
|
-
function toWireProviderConfig(provider) {
|
|
26
|
-
const { maxInputTokens, ...rest } = provider;
|
|
27
|
-
if (maxInputTokens === void 0) return rest;
|
|
28
|
-
return { ...rest, maxPromptTokens: maxInputTokens };
|
|
29
|
-
}
|
|
30
25
|
const MIN_PROTOCOL_VERSION = 2;
|
|
31
26
|
function isZodSchema(value) {
|
|
32
27
|
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
@@ -38,6 +33,26 @@ function toJsonSchema(parameters) {
|
|
|
38
33
|
}
|
|
39
34
|
return parameters;
|
|
40
35
|
}
|
|
36
|
+
function toWireMcpServers(mcpServers) {
|
|
37
|
+
if (!mcpServers) return void 0;
|
|
38
|
+
return Object.fromEntries(
|
|
39
|
+
Object.entries(mcpServers).map(([name, server]) => {
|
|
40
|
+
if ("workingDirectory" in server) {
|
|
41
|
+
const { workingDirectory, ...rest } = server;
|
|
42
|
+
return [name, { ...rest, cwd: workingDirectory }];
|
|
43
|
+
}
|
|
44
|
+
return [name, server];
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function toWireCustomAgents(agents) {
|
|
49
|
+
if (!agents) return void 0;
|
|
50
|
+
return agents.map((agent) => {
|
|
51
|
+
if (!agent.mcpServers) return agent;
|
|
52
|
+
const { mcpServers, ...rest } = agent;
|
|
53
|
+
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
41
56
|
function extractTransformCallbacks(systemMessage) {
|
|
42
57
|
if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
|
|
43
58
|
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
@@ -91,12 +106,18 @@ class CopilotClient {
|
|
|
91
106
|
cliProcess = null;
|
|
92
107
|
connection = null;
|
|
93
108
|
socket = null;
|
|
94
|
-
|
|
109
|
+
runtimePort = null;
|
|
95
110
|
actualHost = "localhost";
|
|
96
111
|
state = "disconnected";
|
|
97
112
|
sessions = /* @__PURE__ */ new Map();
|
|
98
113
|
stderrBuffer = "";
|
|
99
114
|
// Captures CLI stderr for error messages
|
|
115
|
+
/** Resolved connection mode chosen in the constructor. */
|
|
116
|
+
connectionConfig;
|
|
117
|
+
/** Resolved path to the runtime executable (only used for child-process kinds). */
|
|
118
|
+
resolvedCliPath;
|
|
119
|
+
/** Resolved environment passed to the spawned runtime. */
|
|
120
|
+
resolvedEnv;
|
|
100
121
|
options;
|
|
101
122
|
isExternalServer = false;
|
|
102
123
|
forceStopping = false;
|
|
@@ -145,86 +166,79 @@ class CopilotClient {
|
|
|
145
166
|
* Creates a new CopilotClient instance.
|
|
146
167
|
*
|
|
147
168
|
* @param options - Configuration options for the client
|
|
148
|
-
* @throws Error if mutually exclusive options are provided (e.g., cliUrl with useStdio or cliPath)
|
|
149
169
|
*
|
|
150
170
|
* @example
|
|
151
171
|
* ```typescript
|
|
152
|
-
* // Default
|
|
172
|
+
* // Default: spawns the bundled runtime over stdio
|
|
153
173
|
* const client = new CopilotClient();
|
|
154
174
|
*
|
|
155
|
-
* // Connect to an existing
|
|
156
|
-
* const client = new CopilotClient({
|
|
175
|
+
* // Connect to an existing runtime
|
|
176
|
+
* const client = new CopilotClient({
|
|
177
|
+
* connection: RuntimeConnection.forUri("localhost:3000"),
|
|
178
|
+
* });
|
|
179
|
+
*
|
|
180
|
+
* // Spawn the runtime over TCP on a chosen port
|
|
181
|
+
* const client = new CopilotClient({
|
|
182
|
+
* connection: RuntimeConnection.forTcp({ port: 9001 }),
|
|
183
|
+
* });
|
|
157
184
|
*
|
|
158
|
-
* //
|
|
185
|
+
* // Use a custom runtime binary
|
|
159
186
|
* const client = new CopilotClient({
|
|
160
|
-
*
|
|
161
|
-
* logLevel: "debug"
|
|
187
|
+
* connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }),
|
|
188
|
+
* logLevel: "debug",
|
|
162
189
|
* });
|
|
163
190
|
* ```
|
|
164
191
|
*/
|
|
165
192
|
constructor(options = {}) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
if (options.isChildProcess && (options.cliUrl || options.useStdio === false)) {
|
|
193
|
+
const conn = options._internalConnection ?? options.connection ?? { kind: "stdio" };
|
|
194
|
+
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
170
195
|
throw new Error(
|
|
171
|
-
"
|
|
196
|
+
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
172
197
|
);
|
|
173
198
|
}
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
if (options.tcpConnectionToken !== void 0) {
|
|
180
|
-
if (typeof options.tcpConnectionToken !== "string" || options.tcpConnectionToken.length === 0) {
|
|
181
|
-
throw new Error("tcpConnectionToken must be a non-empty string");
|
|
182
|
-
}
|
|
183
|
-
if (options.useStdio === true) {
|
|
184
|
-
throw new Error("tcpConnectionToken cannot be used with useStdio: true");
|
|
199
|
+
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
200
|
+
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
201
|
+
throw new Error("connectionToken must be a non-empty string");
|
|
185
202
|
}
|
|
186
203
|
}
|
|
187
|
-
|
|
188
|
-
const sdkSpawnsCli = !willUseStdio && !options.cliUrl && !options.isChildProcess;
|
|
189
|
-
this.effectiveConnectionToken = options.tcpConnectionToken ?? (sdkSpawnsCli ? randomUUID() : void 0);
|
|
204
|
+
this.connectionConfig = conn;
|
|
190
205
|
if (options.sessionFs) {
|
|
191
206
|
this.validateSessionFsConfig(options.sessionFs);
|
|
192
207
|
}
|
|
193
|
-
if (
|
|
194
|
-
const { host, port } = this.parseCliUrl(
|
|
208
|
+
if (conn.kind === "uri") {
|
|
209
|
+
const { host, port } = this.parseCliUrl(conn.url);
|
|
195
210
|
this.actualHost = host;
|
|
196
|
-
this.
|
|
211
|
+
this.runtimePort = port;
|
|
197
212
|
this.isExternalServer = true;
|
|
198
|
-
}
|
|
199
|
-
if (options.isChildProcess) {
|
|
213
|
+
} else if (conn.kind === "parent-process") {
|
|
200
214
|
this.isExternalServer = true;
|
|
201
215
|
}
|
|
216
|
+
if (conn.kind === "tcp") {
|
|
217
|
+
this.effectiveConnectionToken = conn.connectionToken ?? randomUUID();
|
|
218
|
+
} else if (conn.kind === "uri") {
|
|
219
|
+
this.effectiveConnectionToken = conn.connectionToken;
|
|
220
|
+
}
|
|
202
221
|
this.onListModels = options.onListModels;
|
|
203
222
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
204
223
|
this.sessionFsConfig = options.sessionFs ?? null;
|
|
205
224
|
const effectiveEnv = options.env ?? process.env;
|
|
225
|
+
this.resolvedEnv = effectiveEnv;
|
|
226
|
+
this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
|
|
227
|
+
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
228
|
+
this.connectionExtraArgs = [...connArgs];
|
|
206
229
|
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,
|
|
230
|
+
workingDirectory: options.workingDirectory ?? process.cwd(),
|
|
231
|
+
logLevel: options.logLevel,
|
|
219
232
|
gitHubToken: options.gitHubToken,
|
|
220
|
-
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true
|
|
233
|
+
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true.
|
|
221
234
|
useLoggedInUser: options.useLoggedInUser ?? (options.gitHubToken ? false : true),
|
|
222
235
|
telemetry: options.telemetry,
|
|
223
|
-
|
|
236
|
+
baseDirectory: options.baseDirectory,
|
|
224
237
|
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
225
|
-
|
|
238
|
+
enableRemoteSessions: options.enableRemoteSessions ?? false
|
|
226
239
|
};
|
|
227
240
|
}
|
|
241
|
+
connectionExtraArgs = [];
|
|
228
242
|
/**
|
|
229
243
|
* Parse CLI URL into host and port
|
|
230
244
|
* Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
|
|
@@ -262,12 +276,12 @@ class CopilotClient {
|
|
|
262
276
|
if (!this.sessionFsConfig) {
|
|
263
277
|
return;
|
|
264
278
|
}
|
|
265
|
-
if (!config.
|
|
279
|
+
if (!config.createSessionFsProvider) {
|
|
266
280
|
throw new Error(
|
|
267
|
-
"
|
|
281
|
+
"createSessionFsProvider is required in session config when sessionFs is enabled in client options."
|
|
268
282
|
);
|
|
269
283
|
}
|
|
270
|
-
const provider = config.
|
|
284
|
+
const provider = config.createSessionFsProvider(session);
|
|
271
285
|
if (this.sessionFsConfig.capabilities?.sqlite && !provider.sqlite) {
|
|
272
286
|
throw new Error(
|
|
273
287
|
"SessionFsConfig declares capabilities.sqlite but the provider does not implement sqlite."
|
|
@@ -281,14 +295,14 @@ class CopilotClient {
|
|
|
281
295
|
* If connecting to an external server (via cliUrl), only establishes the connection.
|
|
282
296
|
* Otherwise, spawns the CLI server process and then connects.
|
|
283
297
|
*
|
|
284
|
-
* This method is called automatically
|
|
298
|
+
* This method is called automatically the first time you create or resume a session.
|
|
285
299
|
*
|
|
286
300
|
* @returns A promise that resolves when the connection is established
|
|
287
301
|
* @throws Error if the server fails to start or the connection fails
|
|
288
302
|
*
|
|
289
303
|
* @example
|
|
290
304
|
* ```typescript
|
|
291
|
-
* const client = new CopilotClient(
|
|
305
|
+
* const client = new CopilotClient();
|
|
292
306
|
* await client.start();
|
|
293
307
|
* // Now ready to create sessions
|
|
294
308
|
* ```
|
|
@@ -383,8 +397,15 @@ class CopilotClient {
|
|
|
383
397
|
}
|
|
384
398
|
this.modelsCache = null;
|
|
385
399
|
if (this.socket) {
|
|
400
|
+
const socket = this.socket;
|
|
401
|
+
this.socket = null;
|
|
386
402
|
try {
|
|
387
|
-
|
|
403
|
+
if (!socket.destroyed) {
|
|
404
|
+
await new Promise((resolve) => {
|
|
405
|
+
socket.once("close", () => resolve());
|
|
406
|
+
socket.end();
|
|
407
|
+
});
|
|
408
|
+
}
|
|
388
409
|
} catch (error) {
|
|
389
410
|
errors.push(
|
|
390
411
|
new Error(
|
|
@@ -392,11 +413,18 @@ class CopilotClient {
|
|
|
392
413
|
)
|
|
393
414
|
);
|
|
394
415
|
}
|
|
395
|
-
this.socket = null;
|
|
396
416
|
}
|
|
397
417
|
if (this.cliProcess && !this.isExternalServer) {
|
|
418
|
+
const child = this.cliProcess;
|
|
419
|
+
this.cliProcess = null;
|
|
398
420
|
try {
|
|
399
|
-
|
|
421
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
422
|
+
const exited = new Promise((resolve) => {
|
|
423
|
+
child.once("exit", () => resolve());
|
|
424
|
+
});
|
|
425
|
+
child.kill();
|
|
426
|
+
await exited;
|
|
427
|
+
}
|
|
400
428
|
} catch (error) {
|
|
401
429
|
errors.push(
|
|
402
430
|
new Error(
|
|
@@ -404,18 +432,32 @@ class CopilotClient {
|
|
|
404
432
|
)
|
|
405
433
|
);
|
|
406
434
|
}
|
|
407
|
-
this.cliProcess = null;
|
|
408
435
|
}
|
|
409
436
|
if (this.cliStartTimeout) {
|
|
410
437
|
clearTimeout(this.cliStartTimeout);
|
|
411
438
|
this.cliStartTimeout = null;
|
|
412
439
|
}
|
|
413
440
|
this.state = "disconnected";
|
|
414
|
-
this.
|
|
441
|
+
this.runtimePort = null;
|
|
415
442
|
this.stderrBuffer = "";
|
|
416
443
|
this.processExitPromise = null;
|
|
417
444
|
return errors;
|
|
418
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* Alias for {@link stop} that lets `CopilotClient` participate in `await using`
|
|
448
|
+
* blocks for automatic cleanup.
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* await using client = new CopilotClient();
|
|
453
|
+
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
454
|
+
* await session.sendAndWait("Hello");
|
|
455
|
+
* // client.stop() is called automatically when the block exits.
|
|
456
|
+
* ```
|
|
457
|
+
*/
|
|
458
|
+
async [Symbol.asyncDispose]() {
|
|
459
|
+
await this.stop();
|
|
460
|
+
}
|
|
419
461
|
/**
|
|
420
462
|
* Forcefully stops the CLI server without graceful cleanup.
|
|
421
463
|
*
|
|
@@ -472,7 +514,7 @@ class CopilotClient {
|
|
|
472
514
|
this.cliStartTimeout = null;
|
|
473
515
|
}
|
|
474
516
|
this.state = "disconnected";
|
|
475
|
-
this.
|
|
517
|
+
this.runtimePort = null;
|
|
476
518
|
this.stderrBuffer = "";
|
|
477
519
|
this.processExitPromise = null;
|
|
478
520
|
}
|
|
@@ -480,12 +522,11 @@ class CopilotClient {
|
|
|
480
522
|
* Creates a new conversation session with the Copilot CLI.
|
|
481
523
|
*
|
|
482
524
|
* Sessions maintain conversation state, handle events, and manage tool execution.
|
|
483
|
-
* If the client is not connected
|
|
484
|
-
* start the connection.
|
|
525
|
+
* If the client is not connected, this method automatically starts the connection.
|
|
485
526
|
*
|
|
486
527
|
* @param config - Optional configuration for the session
|
|
487
528
|
* @returns A promise that resolves with the created session
|
|
488
|
-
* @throws Error if the client
|
|
529
|
+
* @throws Error if the client fails to start
|
|
489
530
|
*
|
|
490
531
|
* @example
|
|
491
532
|
* ```typescript
|
|
@@ -507,11 +548,7 @@ class CopilotClient {
|
|
|
507
548
|
*/
|
|
508
549
|
async createSession(config) {
|
|
509
550
|
if (!this.connection) {
|
|
510
|
-
|
|
511
|
-
await this.start();
|
|
512
|
-
} else {
|
|
513
|
-
throw new Error("Client not connected. Call start() first.");
|
|
514
|
-
}
|
|
551
|
+
await this.start();
|
|
515
552
|
}
|
|
516
553
|
const sessionId = config.sessionId ?? randomUUID();
|
|
517
554
|
const session = new CopilotSession(
|
|
@@ -529,11 +566,11 @@ class CopilotClient {
|
|
|
529
566
|
if (config.onElicitationRequest) {
|
|
530
567
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
531
568
|
}
|
|
532
|
-
if (config.
|
|
533
|
-
session.registerExitPlanModeHandler(config.
|
|
569
|
+
if (config.onExitPlanModeRequest) {
|
|
570
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
534
571
|
}
|
|
535
|
-
if (config.
|
|
536
|
-
session.registerAutoModeSwitchHandler(config.
|
|
572
|
+
if (config.onAutoModeSwitchRequest) {
|
|
573
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
537
574
|
}
|
|
538
575
|
if (config.hooks) {
|
|
539
576
|
session.registerHooks(config.hooks);
|
|
@@ -570,21 +607,21 @@ class CopilotClient {
|
|
|
570
607
|
systemMessage: wireSystemMessage,
|
|
571
608
|
availableTools: config.availableTools,
|
|
572
609
|
excludedTools: config.excludedTools,
|
|
573
|
-
provider: config.provider
|
|
610
|
+
provider: config.provider,
|
|
574
611
|
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
575
612
|
modelCapabilities: config.modelCapabilities,
|
|
576
613
|
requestPermission: true,
|
|
577
614
|
requestUserInput: !!config.onUserInputRequest,
|
|
578
615
|
requestElicitation: !!config.onElicitationRequest,
|
|
579
|
-
requestExitPlanMode: !!config.
|
|
580
|
-
requestAutoModeSwitch: !!config.
|
|
616
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
617
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
581
618
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
582
619
|
workingDirectory: config.workingDirectory,
|
|
583
620
|
streaming: config.streaming,
|
|
584
621
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
585
|
-
mcpServers: config.mcpServers,
|
|
622
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
586
623
|
envValueMode: "direct",
|
|
587
|
-
customAgents: config.customAgents,
|
|
624
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
588
625
|
defaultAgent: config.defaultAgent,
|
|
589
626
|
agent: config.agent,
|
|
590
627
|
configDir: config.configDir,
|
|
@@ -632,11 +669,7 @@ class CopilotClient {
|
|
|
632
669
|
*/
|
|
633
670
|
async resumeSession(sessionId, config) {
|
|
634
671
|
if (!this.connection) {
|
|
635
|
-
|
|
636
|
-
await this.start();
|
|
637
|
-
} else {
|
|
638
|
-
throw new Error("Client not connected. Call start() first.");
|
|
639
|
-
}
|
|
672
|
+
await this.start();
|
|
640
673
|
}
|
|
641
674
|
const session = new CopilotSession(
|
|
642
675
|
sessionId,
|
|
@@ -653,11 +686,11 @@ class CopilotClient {
|
|
|
653
686
|
if (config.onElicitationRequest) {
|
|
654
687
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
655
688
|
}
|
|
656
|
-
if (config.
|
|
657
|
-
session.registerExitPlanModeHandler(config.
|
|
689
|
+
if (config.onExitPlanModeRequest) {
|
|
690
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
658
691
|
}
|
|
659
|
-
if (config.
|
|
660
|
-
session.registerAutoModeSwitchHandler(config.
|
|
692
|
+
if (config.onAutoModeSwitchRequest) {
|
|
693
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
661
694
|
}
|
|
662
695
|
if (config.hooks) {
|
|
663
696
|
session.registerHooks(config.hooks);
|
|
@@ -695,29 +728,29 @@ class CopilotClient {
|
|
|
695
728
|
name: cmd.name,
|
|
696
729
|
description: cmd.description
|
|
697
730
|
})),
|
|
698
|
-
provider: config.provider
|
|
731
|
+
provider: config.provider,
|
|
699
732
|
modelCapabilities: config.modelCapabilities,
|
|
700
733
|
requestPermission: config.onPermissionRequest !== defaultJoinSessionPermissionHandler,
|
|
701
734
|
requestUserInput: !!config.onUserInputRequest,
|
|
702
735
|
requestElicitation: !!config.onElicitationRequest,
|
|
703
|
-
requestExitPlanMode: !!config.
|
|
704
|
-
requestAutoModeSwitch: !!config.
|
|
736
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
737
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
705
738
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
706
739
|
workingDirectory: config.workingDirectory,
|
|
707
740
|
configDir: config.configDir,
|
|
708
741
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
709
742
|
streaming: config.streaming,
|
|
710
743
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
711
|
-
mcpServers: config.mcpServers,
|
|
744
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
712
745
|
envValueMode: "direct",
|
|
713
|
-
customAgents: config.customAgents,
|
|
746
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
714
747
|
defaultAgent: config.defaultAgent,
|
|
715
748
|
agent: config.agent,
|
|
716
749
|
skillDirectories: config.skillDirectories,
|
|
717
750
|
instructionDirectories: config.instructionDirectories,
|
|
718
751
|
disabledSkills: config.disabledSkills,
|
|
719
752
|
infiniteSessions: config.infiniteSessions,
|
|
720
|
-
|
|
753
|
+
suppressResumeEvent: config.suppressResumeEvent,
|
|
721
754
|
continuePendingWork: config.continuePendingWork,
|
|
722
755
|
gitHubToken: config.gitHubToken,
|
|
723
756
|
remoteSession: config.remoteSession
|
|
@@ -947,8 +980,13 @@ class CopilotClient {
|
|
|
947
980
|
if (!this.connection) {
|
|
948
981
|
throw new Error("Client not connected");
|
|
949
982
|
}
|
|
983
|
+
let wireFilter;
|
|
984
|
+
if (filter) {
|
|
985
|
+
const { workingDirectory, ...rest } = filter;
|
|
986
|
+
wireFilter = { ...rest, cwd: workingDirectory };
|
|
987
|
+
}
|
|
950
988
|
const response = await this.connection.sendRequest("session.list", {
|
|
951
|
-
filter
|
|
989
|
+
filter: wireFilter
|
|
952
990
|
});
|
|
953
991
|
const { sessions } = response;
|
|
954
992
|
return sessions.map(CopilotClient.toSessionMetadata);
|
|
@@ -983,13 +1021,19 @@ class CopilotClient {
|
|
|
983
1021
|
return CopilotClient.toSessionMetadata(session);
|
|
984
1022
|
}
|
|
985
1023
|
static toSessionMetadata(raw) {
|
|
1024
|
+
const { context } = raw;
|
|
986
1025
|
return {
|
|
987
1026
|
sessionId: raw.sessionId,
|
|
988
1027
|
startTime: new Date(raw.startTime),
|
|
989
1028
|
modifiedTime: new Date(raw.modifiedTime),
|
|
990
1029
|
summary: raw.summary,
|
|
991
1030
|
isRemote: raw.isRemote,
|
|
992
|
-
context:
|
|
1031
|
+
context: context ? {
|
|
1032
|
+
workingDirectory: context.cwd,
|
|
1033
|
+
gitRoot: context.gitRoot,
|
|
1034
|
+
repository: context.repository,
|
|
1035
|
+
branch: context.branch
|
|
1036
|
+
} : void 0
|
|
993
1037
|
};
|
|
994
1038
|
}
|
|
995
1039
|
/**
|
|
@@ -1042,7 +1086,7 @@ class CopilotClient {
|
|
|
1042
1086
|
throw new Error(result.error || "Failed to set foreground session");
|
|
1043
1087
|
}
|
|
1044
1088
|
}
|
|
1045
|
-
|
|
1089
|
+
onLifecycle(eventTypeOrHandler, handler) {
|
|
1046
1090
|
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
1047
1091
|
const eventType = eventTypeOrHandler;
|
|
1048
1092
|
if (!this.typedLifecycleHandlers.has(eventType)) {
|
|
@@ -1069,17 +1113,17 @@ class CopilotClient {
|
|
|
1069
1113
|
async startCLIServer() {
|
|
1070
1114
|
return new Promise((resolve, reject) => {
|
|
1071
1115
|
this.stderrBuffer = "";
|
|
1072
|
-
const args = [
|
|
1073
|
-
|
|
1074
|
-
"--
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
this.options.logLevel
|
|
1078
|
-
];
|
|
1079
|
-
if (this.options.useStdio) {
|
|
1116
|
+
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1117
|
+
if (this.options.logLevel) {
|
|
1118
|
+
args.push("--log-level", this.options.logLevel);
|
|
1119
|
+
}
|
|
1120
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1080
1121
|
args.push("--stdio");
|
|
1081
|
-
} else if (this.
|
|
1082
|
-
|
|
1122
|
+
} else if (this.connectionConfig.kind === "tcp") {
|
|
1123
|
+
const requestedPort = this.connectionConfig.port ?? 0;
|
|
1124
|
+
if (requestedPort > 0) {
|
|
1125
|
+
args.push("--port", requestedPort.toString());
|
|
1126
|
+
}
|
|
1083
1127
|
}
|
|
1084
1128
|
if (this.options.gitHubToken) {
|
|
1085
1129
|
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
@@ -1093,10 +1137,10 @@ class CopilotClient {
|
|
|
1093
1137
|
this.options.sessionIdleTimeoutSeconds.toString()
|
|
1094
1138
|
);
|
|
1095
1139
|
}
|
|
1096
|
-
if (this.options.
|
|
1140
|
+
if (this.options.enableRemoteSessions) {
|
|
1097
1141
|
args.push("--remote");
|
|
1098
1142
|
}
|
|
1099
|
-
const envWithoutNodeDebug = { ...this.
|
|
1143
|
+
const envWithoutNodeDebug = { ...this.resolvedEnv };
|
|
1100
1144
|
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
1101
1145
|
if (this.options.gitHubToken) {
|
|
1102
1146
|
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
@@ -1104,12 +1148,12 @@ class CopilotClient {
|
|
|
1104
1148
|
if (this.effectiveConnectionToken) {
|
|
1105
1149
|
envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1106
1150
|
}
|
|
1107
|
-
if (this.options.
|
|
1108
|
-
envWithoutNodeDebug.COPILOT_HOME = this.options.
|
|
1151
|
+
if (this.options.baseDirectory) {
|
|
1152
|
+
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1109
1153
|
}
|
|
1110
|
-
if (!this.
|
|
1154
|
+
if (!this.resolvedCliPath) {
|
|
1111
1155
|
throw new Error(
|
|
1112
|
-
"Path to Copilot CLI is required. Please
|
|
1156
|
+
"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
1157
|
);
|
|
1114
1158
|
}
|
|
1115
1159
|
if (this.options.telemetry) {
|
|
@@ -1128,31 +1172,31 @@ class CopilotClient {
|
|
|
1128
1172
|
t.captureContent
|
|
1129
1173
|
);
|
|
1130
1174
|
}
|
|
1131
|
-
if (!existsSync(this.
|
|
1175
|
+
if (!existsSync(this.resolvedCliPath)) {
|
|
1132
1176
|
throw new Error(
|
|
1133
|
-
`Copilot CLI not found at ${this.
|
|
1177
|
+
`Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
|
|
1134
1178
|
);
|
|
1135
1179
|
}
|
|
1136
|
-
const stdioConfig = this.
|
|
1137
|
-
const isJsFile = this.
|
|
1180
|
+
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
1181
|
+
const isJsFile = this.resolvedCliPath.endsWith(".js");
|
|
1138
1182
|
if (isJsFile) {
|
|
1139
|
-
this.cliProcess = spawn(getNodeExecPath(), [this.
|
|
1183
|
+
this.cliProcess = spawn(getNodeExecPath(), [this.resolvedCliPath, ...args], {
|
|
1140
1184
|
stdio: stdioConfig,
|
|
1141
|
-
cwd: this.options.
|
|
1185
|
+
cwd: this.options.workingDirectory,
|
|
1142
1186
|
env: envWithoutNodeDebug,
|
|
1143
1187
|
windowsHide: true
|
|
1144
1188
|
});
|
|
1145
1189
|
} else {
|
|
1146
|
-
this.cliProcess = spawn(this.
|
|
1190
|
+
this.cliProcess = spawn(this.resolvedCliPath, args, {
|
|
1147
1191
|
stdio: stdioConfig,
|
|
1148
|
-
cwd: this.options.
|
|
1192
|
+
cwd: this.options.workingDirectory,
|
|
1149
1193
|
env: envWithoutNodeDebug,
|
|
1150
1194
|
windowsHide: true
|
|
1151
1195
|
});
|
|
1152
1196
|
}
|
|
1153
1197
|
let stdout = "";
|
|
1154
1198
|
let resolved = false;
|
|
1155
|
-
if (this.
|
|
1199
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1156
1200
|
resolved = true;
|
|
1157
1201
|
resolve();
|
|
1158
1202
|
} else {
|
|
@@ -1160,7 +1204,7 @@ class CopilotClient {
|
|
|
1160
1204
|
stdout += data.toString();
|
|
1161
1205
|
const match = stdout.match(/listening on port (\d+)/i);
|
|
1162
1206
|
if (match && !resolved) {
|
|
1163
|
-
this.
|
|
1207
|
+
this.runtimePort = parseInt(match[1], 10);
|
|
1164
1208
|
resolved = true;
|
|
1165
1209
|
resolve();
|
|
1166
1210
|
}
|
|
@@ -1234,19 +1278,21 @@ stderr: ${stderrOutput}`
|
|
|
1234
1278
|
resolved = true;
|
|
1235
1279
|
reject(new Error("Timeout waiting for CLI server to start"));
|
|
1236
1280
|
}
|
|
1237
|
-
},
|
|
1281
|
+
}, 3e4);
|
|
1238
1282
|
});
|
|
1239
1283
|
}
|
|
1240
1284
|
/**
|
|
1241
1285
|
* Connect to the CLI server (via socket or stdio)
|
|
1242
1286
|
*/
|
|
1243
1287
|
async connectToServer() {
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1288
|
+
switch (this.connectionConfig.kind) {
|
|
1289
|
+
case "parent-process":
|
|
1290
|
+
return this.connectToParentProcessViaStdio();
|
|
1291
|
+
case "stdio":
|
|
1292
|
+
return this.connectToChildProcessViaStdio();
|
|
1293
|
+
case "tcp":
|
|
1294
|
+
case "uri":
|
|
1295
|
+
return this.connectViaTcp();
|
|
1250
1296
|
}
|
|
1251
1297
|
}
|
|
1252
1298
|
/**
|
|
@@ -1286,7 +1332,7 @@ stderr: ${stderrOutput}`
|
|
|
1286
1332
|
* Connect to the CLI server via TCP socket
|
|
1287
1333
|
*/
|
|
1288
1334
|
async connectViaTcp() {
|
|
1289
|
-
if (!this.
|
|
1335
|
+
if (!this.runtimePort) {
|
|
1290
1336
|
throw new Error("Server port not available");
|
|
1291
1337
|
}
|
|
1292
1338
|
return new Promise((resolve, reject) => {
|
|
@@ -1295,7 +1341,7 @@ stderr: ${stderrOutput}`
|
|
|
1295
1341
|
this.socket?.destroy();
|
|
1296
1342
|
reject(new Error("Timeout connecting to CLI server"));
|
|
1297
1343
|
}, 1e4);
|
|
1298
|
-
this.socket.connect(this.
|
|
1344
|
+
this.socket.connect(this.runtimePort, this.actualHost, () => {
|
|
1299
1345
|
clearTimeout(connectionTimeout);
|
|
1300
1346
|
this.connection = createMessageConnection(
|
|
1301
1347
|
new StreamMessageReader(this.socket),
|
|
@@ -1375,7 +1421,20 @@ stderr: ${stderrOutput}`
|
|
|
1375
1421
|
if (typeof notification !== "object" || !notification || !("type" in notification) || typeof notification.type !== "string" || !("sessionId" in notification) || typeof notification.sessionId !== "string") {
|
|
1376
1422
|
return;
|
|
1377
1423
|
}
|
|
1378
|
-
const
|
|
1424
|
+
const raw = notification;
|
|
1425
|
+
let metadata;
|
|
1426
|
+
if (raw.metadata && raw.metadata.startTime && raw.metadata.modifiedTime) {
|
|
1427
|
+
metadata = {
|
|
1428
|
+
startTime: new Date(raw.metadata.startTime),
|
|
1429
|
+
modifiedTime: new Date(raw.metadata.modifiedTime),
|
|
1430
|
+
summary: raw.metadata.summary
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
const event = {
|
|
1434
|
+
type: raw.type,
|
|
1435
|
+
sessionId: raw.sessionId,
|
|
1436
|
+
metadata
|
|
1437
|
+
};
|
|
1379
1438
|
const typedHandlers = this.typedLifecycleHandlers.get(event.type);
|
|
1380
1439
|
if (typedHandlers) {
|
|
1381
1440
|
for (const handler of typedHandlers) {
|
package/dist/extension.js
CHANGED
|
@@ -9,11 +9,11 @@ async function joinSession(config = {}) {
|
|
|
9
9
|
"joinSession() is intended for extensions running as child processes of the Copilot CLI."
|
|
10
10
|
);
|
|
11
11
|
}
|
|
12
|
-
const client = new CopilotClient({
|
|
12
|
+
const client = new CopilotClient({ _internalConnection: { kind: "parent-process" } });
|
|
13
13
|
return client.resumeSession(sessionId, {
|
|
14
14
|
...config,
|
|
15
15
|
onPermissionRequest: config.onPermissionRequest ?? defaultJoinSessionPermissionHandler,
|
|
16
|
-
|
|
16
|
+
suppressResumeEvent: config.suppressResumeEvent ?? true
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
export {
|