@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/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
- actualPort = null;
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 options - spawns CLI server using stdio
172
+ * // Default: spawns the bundled runtime over stdio
153
173
  * const client = new CopilotClient();
154
174
  *
155
- * // Connect to an existing server
156
- * const client = new CopilotClient({ cliUrl: "localhost:3000" });
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
- * // Custom CLI path with specific log level
185
+ * // Use a custom runtime binary
159
186
  * const client = new CopilotClient({
160
- * cliPath: "/usr/local/bin/copilot",
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
- if (options.cliUrl && (options.useStdio === true || options.cliPath)) {
167
- throw new Error("cliUrl is mutually exclusive with useStdio and cliPath");
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
- "isChildProcess must be used in conjunction with useStdio and not with cliUrl"
196
+ "gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
172
197
  );
173
198
  }
174
- if (options.cliUrl && (options.gitHubToken || options.useLoggedInUser !== void 0)) {
175
- throw new Error(
176
- "gitHubToken and useLoggedInUser cannot be used with cliUrl (external server manages its own auth)"
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
- const willUseStdio = options.cliUrl ? false : options.useStdio ?? true;
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 (options.cliUrl) {
194
- const { host, port } = this.parseCliUrl(options.cliUrl);
208
+ if (conn.kind === "uri") {
209
+ const { host, port } = this.parseCliUrl(conn.url);
195
210
  this.actualHost = host;
196
- this.actualPort = port;
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
- cliPath: options.cliUrl ? void 0 : options.cliPath || effectiveEnv.COPILOT_CLI_PATH || getBundledCliPath(),
208
- cliArgs: options.cliArgs ?? [],
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
- copilotHome: options.copilotHome,
236
+ baseDirectory: options.baseDirectory,
224
237
  sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
225
- remote: options.remote ?? false
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.createSessionFsHandler) {
279
+ if (!config.createSessionFsProvider) {
266
280
  throw new Error(
267
- "createSessionFsHandler is required in session config when sessionFs is enabled in client options."
281
+ "createSessionFsProvider is required in session config when sessionFs is enabled in client options."
268
282
  );
269
283
  }
270
- const provider = config.createSessionFsHandler(session);
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 when creating a session if `autoStart` is true (default).
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({ autoStart: false });
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
- this.socket.end();
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
- this.cliProcess.kill();
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.actualPort = null;
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.actualPort = null;
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 and `autoStart` is enabled, this will automatically
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 is not connected and autoStart is disabled
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
- if (this.options.autoStart) {
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.onExitPlanMode) {
533
- session.registerExitPlanModeHandler(config.onExitPlanMode);
569
+ if (config.onExitPlanModeRequest) {
570
+ session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
534
571
  }
535
- if (config.onAutoModeSwitch) {
536
- session.registerAutoModeSwitchHandler(config.onAutoModeSwitch);
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 ? toWireProviderConfig(config.provider) : void 0,
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.onExitPlanMode,
580
- requestAutoModeSwitch: !!config.onAutoModeSwitch,
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
- if (this.options.autoStart) {
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.onExitPlanMode) {
657
- session.registerExitPlanModeHandler(config.onExitPlanMode);
689
+ if (config.onExitPlanModeRequest) {
690
+ session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
658
691
  }
659
- if (config.onAutoModeSwitch) {
660
- session.registerAutoModeSwitchHandler(config.onAutoModeSwitch);
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 ? toWireProviderConfig(config.provider) : void 0,
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.onExitPlanMode,
704
- requestAutoModeSwitch: !!config.onAutoModeSwitch,
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
- disableResume: config.disableResume,
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: raw.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
- on(eventTypeOrHandler, handler) {
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
- ...this.options.cliArgs,
1074
- "--headless",
1075
- "--no-auto-update",
1076
- "--log-level",
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.options.port > 0) {
1082
- args.push("--port", this.options.port.toString());
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.remote) {
1140
+ if (this.options.enableRemoteSessions) {
1097
1141
  args.push("--remote");
1098
1142
  }
1099
- const envWithoutNodeDebug = { ...this.options.env };
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.copilotHome) {
1108
- envWithoutNodeDebug.COPILOT_HOME = this.options.copilotHome;
1151
+ if (this.options.baseDirectory) {
1152
+ envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
1109
1153
  }
1110
- if (!this.options.cliPath) {
1154
+ if (!this.resolvedCliPath) {
1111
1155
  throw new Error(
1112
- "Path to Copilot CLI is required. Please provide it via the cliPath option, or use cliUrl to rely on a remote CLI."
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.options.cliPath)) {
1175
+ if (!existsSync(this.resolvedCliPath)) {
1132
1176
  throw new Error(
1133
- `Copilot CLI not found at ${this.options.cliPath}. Ensure @github/copilot is installed.`
1177
+ `Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
1134
1178
  );
1135
1179
  }
1136
- const stdioConfig = this.options.useStdio ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
1137
- const isJsFile = this.options.cliPath.endsWith(".js");
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.options.cliPath, ...args], {
1183
+ this.cliProcess = spawn(getNodeExecPath(), [this.resolvedCliPath, ...args], {
1140
1184
  stdio: stdioConfig,
1141
- cwd: this.options.cwd,
1185
+ cwd: this.options.workingDirectory,
1142
1186
  env: envWithoutNodeDebug,
1143
1187
  windowsHide: true
1144
1188
  });
1145
1189
  } else {
1146
- this.cliProcess = spawn(this.options.cliPath, args, {
1190
+ this.cliProcess = spawn(this.resolvedCliPath, args, {
1147
1191
  stdio: stdioConfig,
1148
- cwd: this.options.cwd,
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.options.useStdio) {
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.actualPort = parseInt(match[1], 10);
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
- }, 1e4);
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
- if (this.options.isChildProcess) {
1245
- return this.connectToParentProcessViaStdio();
1246
- } else if (this.options.useStdio) {
1247
- return this.connectToChildProcessViaStdio();
1248
- } else {
1249
- return this.connectViaTcp();
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.actualPort) {
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.actualPort, this.actualHost, () => {
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 event = notification;
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({ isChildProcess: true });
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
- disableResume: config.disableResume ?? true
16
+ suppressResumeEvent: config.suppressResumeEvent ?? true
17
17
  });
18
18
  }
19
19
  export {