@github/copilot-sdk 0.1.24 → 0.1.25-preview.0

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 CHANGED
@@ -10,6 +10,19 @@ TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC.
10
10
  npm install @github/copilot-sdk
11
11
  ```
12
12
 
13
+ ## Run the Sample
14
+
15
+ Try the interactive chat sample (from the repo root):
16
+
17
+ ```bash
18
+ cd nodejs
19
+ npm ci
20
+ npm run build
21
+ cd samples
22
+ npm install
23
+ npm start
24
+ ```
25
+
13
26
  ## Quick Start
14
27
 
15
28
  ```typescript
package/dist/client.d.ts CHANGED
@@ -9,6 +9,7 @@ export declare class CopilotClient {
9
9
  private actualHost;
10
10
  private state;
11
11
  private sessions;
12
+ private stderrBuffer;
12
13
  private options;
13
14
  private isExternalServer;
14
15
  private forceStopping;
@@ -17,6 +18,7 @@ export declare class CopilotClient {
17
18
  private sessionLifecycleHandlers;
18
19
  private typedLifecycleHandlers;
19
20
  private _rpc;
21
+ private processExitPromise;
20
22
  /**
21
23
  * Typed server-scoped RPC methods.
22
24
  * @throws Error if the client is not connected
package/dist/client.js CHANGED
@@ -34,6 +34,8 @@ class CopilotClient {
34
34
  actualHost = "localhost";
35
35
  state = "disconnected";
36
36
  sessions = /* @__PURE__ */ new Map();
37
+ stderrBuffer = "";
38
+ // Captures CLI stderr for error messages
37
39
  options;
38
40
  isExternalServer = false;
39
41
  forceStopping = false;
@@ -42,6 +44,8 @@ class CopilotClient {
42
44
  sessionLifecycleHandlers = /* @__PURE__ */ new Set();
43
45
  typedLifecycleHandlers = /* @__PURE__ */ new Map();
44
46
  _rpc = null;
47
+ processExitPromise = null;
48
+ // Rejects when CLI process exits
45
49
  /**
46
50
  * Typed server-scoped RPC methods.
47
51
  * @throws Error if the client is not connected
@@ -251,6 +255,8 @@ class CopilotClient {
251
255
  }
252
256
  this.state = "disconnected";
253
257
  this.actualPort = null;
258
+ this.stderrBuffer = "";
259
+ this.processExitPromise = null;
254
260
  return errors;
255
261
  }
256
262
  /**
@@ -306,6 +312,8 @@ class CopilotClient {
306
312
  }
307
313
  this.state = "disconnected";
308
314
  this.actualPort = null;
315
+ this.stderrBuffer = "";
316
+ this.processExitPromise = null;
309
317
  }
310
318
  /**
311
319
  * Creates a new conversation session with the Copilot CLI.
@@ -362,6 +370,7 @@ class CopilotClient {
362
370
  workingDirectory: config.workingDirectory,
363
371
  streaming: config.streaming,
364
372
  mcpServers: config.mcpServers,
373
+ envValueMode: "direct",
365
374
  customAgents: config.customAgents,
366
375
  configDir: config.configDir,
367
376
  skillDirectories: config.skillDirectories,
@@ -434,6 +443,7 @@ class CopilotClient {
434
443
  configDir: config.configDir,
435
444
  streaming: config.streaming,
436
445
  mcpServers: config.mcpServers,
446
+ envValueMode: "direct",
437
447
  customAgents: config.customAgents,
438
448
  skillDirectories: config.skillDirectories,
439
449
  disabledSkills: config.disabledSkills,
@@ -545,7 +555,12 @@ class CopilotClient {
545
555
  */
546
556
  async verifyProtocolVersion() {
547
557
  const expectedVersion = getSdkProtocolVersion();
548
- const pingResult = await this.ping();
558
+ let pingResult;
559
+ if (this.processExitPromise) {
560
+ pingResult = await Promise.race([this.ping(), this.processExitPromise]);
561
+ } else {
562
+ pingResult = await this.ping();
563
+ }
549
564
  const serverVersion = pingResult.protocolVersion;
550
565
  if (serverVersion === void 0) {
551
566
  throw new Error(
@@ -714,6 +729,7 @@ class CopilotClient {
714
729
  */
715
730
  async startCLIServer() {
716
731
  return new Promise((resolve, reject) => {
732
+ this.stderrBuffer = "";
717
733
  const args = [
718
734
  ...this.options.cliArgs,
719
735
  "--headless",
@@ -776,6 +792,7 @@ class CopilotClient {
776
792
  });
777
793
  }
778
794
  this.cliProcess.stderr?.on("data", (data) => {
795
+ this.stderrBuffer += data.toString();
779
796
  const lines = data.toString().split("\n");
780
797
  for (const line of lines) {
781
798
  if (line.trim()) {
@@ -787,13 +804,54 @@ class CopilotClient {
787
804
  this.cliProcess.on("error", (error) => {
788
805
  if (!resolved) {
789
806
  resolved = true;
790
- reject(new Error(`Failed to start CLI server: ${error.message}`));
807
+ const stderrOutput = this.stderrBuffer.trim();
808
+ if (stderrOutput) {
809
+ reject(
810
+ new Error(
811
+ `Failed to start CLI server: ${error.message}
812
+ stderr: ${stderrOutput}`
813
+ )
814
+ );
815
+ } else {
816
+ reject(new Error(`Failed to start CLI server: ${error.message}`));
817
+ }
791
818
  }
792
819
  });
820
+ this.processExitPromise = new Promise((_, rejectProcessExit) => {
821
+ this.cliProcess.on("exit", (code) => {
822
+ setTimeout(() => {
823
+ const stderrOutput = this.stderrBuffer.trim();
824
+ if (stderrOutput) {
825
+ rejectProcessExit(
826
+ new Error(
827
+ `CLI server exited with code ${code}
828
+ stderr: ${stderrOutput}`
829
+ )
830
+ );
831
+ } else {
832
+ rejectProcessExit(
833
+ new Error(`CLI server exited unexpectedly with code ${code}`)
834
+ );
835
+ }
836
+ }, 50);
837
+ });
838
+ });
839
+ this.processExitPromise.catch(() => {
840
+ });
793
841
  this.cliProcess.on("exit", (code) => {
794
842
  if (!resolved) {
795
843
  resolved = true;
796
- reject(new Error(`CLI server exited with code ${code}`));
844
+ const stderrOutput = this.stderrBuffer.trim();
845
+ if (stderrOutput) {
846
+ reject(
847
+ new Error(
848
+ `CLI server exited with code ${code}
849
+ stderr: ${stderrOutput}`
850
+ )
851
+ );
852
+ } else {
853
+ reject(new Error(`CLI server exited with code ${code}`));
854
+ }
797
855
  } else if (this.options.autoRestart && this.state === "connected") {
798
856
  void this.reconnect();
799
857
  }
@@ -163,6 +163,130 @@ export interface SessionModelSwitchToParams {
163
163
  sessionId: string;
164
164
  modelId: string;
165
165
  }
166
+ export interface SessionModeGetResult {
167
+ /**
168
+ * The current agent mode.
169
+ */
170
+ mode: "interactive" | "plan" | "autopilot";
171
+ }
172
+ export interface SessionModeGetParams {
173
+ /**
174
+ * Target session identifier
175
+ */
176
+ sessionId: string;
177
+ }
178
+ export interface SessionModeSetResult {
179
+ /**
180
+ * The agent mode after switching.
181
+ */
182
+ mode: "interactive" | "plan" | "autopilot";
183
+ }
184
+ export interface SessionModeSetParams {
185
+ /**
186
+ * Target session identifier
187
+ */
188
+ sessionId: string;
189
+ /**
190
+ * The mode to switch to. Valid values: "interactive", "plan", "autopilot".
191
+ */
192
+ mode: "interactive" | "plan" | "autopilot";
193
+ }
194
+ export interface SessionPlanReadResult {
195
+ /**
196
+ * Whether plan.md exists in the workspace
197
+ */
198
+ exists: boolean;
199
+ /**
200
+ * The content of plan.md, or null if it does not exist
201
+ */
202
+ content: string | null;
203
+ }
204
+ export interface SessionPlanReadParams {
205
+ /**
206
+ * Target session identifier
207
+ */
208
+ sessionId: string;
209
+ }
210
+ export interface SessionPlanUpdateResult {
211
+ }
212
+ export interface SessionPlanUpdateParams {
213
+ /**
214
+ * Target session identifier
215
+ */
216
+ sessionId: string;
217
+ /**
218
+ * The new content for plan.md
219
+ */
220
+ content: string;
221
+ }
222
+ export interface SessionPlanDeleteResult {
223
+ }
224
+ export interface SessionPlanDeleteParams {
225
+ /**
226
+ * Target session identifier
227
+ */
228
+ sessionId: string;
229
+ }
230
+ export interface SessionWorkspaceListFilesResult {
231
+ /**
232
+ * Relative file paths in the workspace files directory
233
+ */
234
+ files: string[];
235
+ }
236
+ export interface SessionWorkspaceListFilesParams {
237
+ /**
238
+ * Target session identifier
239
+ */
240
+ sessionId: string;
241
+ }
242
+ export interface SessionWorkspaceReadFileResult {
243
+ /**
244
+ * File content as a UTF-8 string
245
+ */
246
+ content: string;
247
+ }
248
+ export interface SessionWorkspaceReadFileParams {
249
+ /**
250
+ * Target session identifier
251
+ */
252
+ sessionId: string;
253
+ /**
254
+ * Relative path within the workspace files directory
255
+ */
256
+ path: string;
257
+ }
258
+ export interface SessionWorkspaceCreateFileResult {
259
+ }
260
+ export interface SessionWorkspaceCreateFileParams {
261
+ /**
262
+ * Target session identifier
263
+ */
264
+ sessionId: string;
265
+ /**
266
+ * Relative path within the workspace files directory
267
+ */
268
+ path: string;
269
+ /**
270
+ * File content to write as a UTF-8 string
271
+ */
272
+ content: string;
273
+ }
274
+ export interface SessionFleetStartResult {
275
+ /**
276
+ * Whether fleet mode was successfully activated
277
+ */
278
+ started: boolean;
279
+ }
280
+ export interface SessionFleetStartParams {
281
+ /**
282
+ * Target session identifier
283
+ */
284
+ sessionId: string;
285
+ /**
286
+ * Optional user prompt to combine with fleet instructions
287
+ */
288
+ prompt?: string;
289
+ }
166
290
  /** Create typed server-scoped RPC methods (no session required). */
167
291
  export declare function createServerRpc(connection: MessageConnection): {
168
292
  ping: (params: PingParams) => Promise<PingResult>;
@@ -182,4 +306,21 @@ export declare function createSessionRpc(connection: MessageConnection, sessionI
182
306
  getCurrent: () => Promise<SessionModelGetCurrentResult>;
183
307
  switchTo: (params: Omit<SessionModelSwitchToParams, "sessionId">) => Promise<SessionModelSwitchToResult>;
184
308
  };
309
+ mode: {
310
+ get: () => Promise<SessionModeGetResult>;
311
+ set: (params: Omit<SessionModeSetParams, "sessionId">) => Promise<SessionModeSetResult>;
312
+ };
313
+ plan: {
314
+ read: () => Promise<SessionPlanReadResult>;
315
+ update: (params: Omit<SessionPlanUpdateParams, "sessionId">) => Promise<SessionPlanUpdateResult>;
316
+ delete: () => Promise<SessionPlanDeleteResult>;
317
+ };
318
+ workspace: {
319
+ listFiles: () => Promise<SessionWorkspaceListFilesResult>;
320
+ readFile: (params: Omit<SessionWorkspaceReadFileParams, "sessionId">) => Promise<SessionWorkspaceReadFileResult>;
321
+ createFile: (params: Omit<SessionWorkspaceCreateFileParams, "sessionId">) => Promise<SessionWorkspaceCreateFileResult>;
322
+ };
323
+ fleet: {
324
+ start: (params: Omit<SessionFleetStartParams, "sessionId">) => Promise<SessionFleetStartResult>;
325
+ };
185
326
  };
@@ -17,6 +17,23 @@ function createSessionRpc(connection, sessionId) {
17
17
  model: {
18
18
  getCurrent: async () => connection.sendRequest("session.model.getCurrent", { sessionId }),
19
19
  switchTo: async (params) => connection.sendRequest("session.model.switchTo", { sessionId, ...params })
20
+ },
21
+ mode: {
22
+ get: async () => connection.sendRequest("session.mode.get", { sessionId }),
23
+ set: async (params) => connection.sendRequest("session.mode.set", { sessionId, ...params })
24
+ },
25
+ plan: {
26
+ read: async () => connection.sendRequest("session.plan.read", { sessionId }),
27
+ update: async (params) => connection.sendRequest("session.plan.update", { sessionId, ...params }),
28
+ delete: async () => connection.sendRequest("session.plan.delete", { sessionId })
29
+ },
30
+ workspace: {
31
+ listFiles: async () => connection.sendRequest("session.workspace.listFiles", { sessionId }),
32
+ readFile: async (params) => connection.sendRequest("session.workspace.readFile", { sessionId, ...params }),
33
+ createFile: async (params) => connection.sendRequest("session.workspace.createFile", { sessionId, ...params })
34
+ },
35
+ fleet: {
36
+ start: async (params) => connection.sendRequest("session.fleet.start", { sessionId, ...params })
20
37
  }
21
38
  };
22
39
  }
@@ -97,6 +97,38 @@ export type SessionEvent = {
97
97
  previousModel?: string;
98
98
  newModel: string;
99
99
  };
100
+ } | {
101
+ id: string;
102
+ timestamp: string;
103
+ parentId: string | null;
104
+ ephemeral?: boolean;
105
+ type: "session.mode_changed";
106
+ data: {
107
+ previousMode: string;
108
+ newMode: string;
109
+ };
110
+ } | {
111
+ id: string;
112
+ timestamp: string;
113
+ parentId: string | null;
114
+ ephemeral?: boolean;
115
+ type: "session.plan_changed";
116
+ data: {
117
+ operation: "create" | "update" | "delete";
118
+ };
119
+ } | {
120
+ id: string;
121
+ timestamp: string;
122
+ parentId: string | null;
123
+ ephemeral?: boolean;
124
+ type: "session.workspace_file_changed";
125
+ data: {
126
+ /**
127
+ * Relative path within the workspace files directory
128
+ */
129
+ path: string;
130
+ operation: "create" | "update";
131
+ };
100
132
  } | {
101
133
  id: string;
102
134
  timestamp: string;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/github/copilot-sdk.git"
6
6
  },
7
- "version": "0.1.24",
7
+ "version": "0.1.25-preview.0",
8
8
  "description": "TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC",
9
9
  "main": "./dist/index.js",
10
10
  "types": "./dist/index.d.ts",
@@ -40,7 +40,7 @@
40
40
  "author": "GitHub",
41
41
  "license": "MIT",
42
42
  "dependencies": {
43
- "@github/copilot": "^0.0.409",
43
+ "@github/copilot": "^0.0.411-1",
44
44
  "vscode-jsonrpc": "^8.2.1",
45
45
  "zod": "^4.3.6"
46
46
  },