@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/dist/cjs/index.js CHANGED
@@ -20,20 +20,23 @@ var index_exports = {};
20
20
  __export(index_exports, {
21
21
  CopilotClient: () => import_client.CopilotClient,
22
22
  CopilotSession: () => import_session.CopilotSession,
23
- SYSTEM_PROMPT_SECTIONS: () => import_types.SYSTEM_PROMPT_SECTIONS,
24
- approveAll: () => import_types.approveAll,
25
- convertMcpCallToolResult: () => import_types.convertMcpCallToolResult,
26
- createSessionFsAdapter: () => import_types.createSessionFsAdapter,
27
- defineTool: () => import_types.defineTool
23
+ RuntimeConnection: () => import_types.RuntimeConnection,
24
+ SYSTEM_PROMPT_SECTIONS: () => import_types2.SYSTEM_PROMPT_SECTIONS,
25
+ approveAll: () => import_types2.approveAll,
26
+ convertMcpCallToolResult: () => import_types2.convertMcpCallToolResult,
27
+ createSessionFsAdapter: () => import_types2.createSessionFsAdapter,
28
+ defineTool: () => import_types2.defineTool
28
29
  });
29
30
  module.exports = __toCommonJS(index_exports);
30
31
  var import_client = require("./client.js");
31
- var import_session = require("./session.js");
32
32
  var import_types = require("./types.js");
33
+ var import_session = require("./session.js");
34
+ var import_types2 = require("./types.js");
33
35
  // Annotate the CommonJS export names for ESM import in node:
34
36
  0 && (module.exports = {
35
37
  CopilotClient,
36
38
  CopilotSession,
39
+ RuntimeConnection,
37
40
  SYSTEM_PROMPT_SECTIONS,
38
41
  approveAll,
39
42
  convertMcpCallToolResult,
@@ -26,6 +26,14 @@ var import_node = require("vscode-jsonrpc/node.js");
26
26
  var import_rpc = require("./generated/rpc.js");
27
27
  var import_telemetry = require("./telemetry.js");
28
28
  const NO_RESULT_PERMISSION_V2_ERROR = "Permission handlers cannot return 'no-result' when connected to a protocol v2 server.";
29
+ function deserializeHookInput(raw) {
30
+ if (!raw || typeof raw !== "object" || typeof raw.timestamp !== "number") {
31
+ return raw;
32
+ }
33
+ const obj = raw;
34
+ const { cwd, ...rest } = obj;
35
+ return { ...rest, timestamp: new Date(obj.timestamp), workingDirectory: cwd };
36
+ }
29
37
  class CopilotSession {
30
38
  /**
31
39
  * Creates a new CopilotSession instance.
@@ -102,25 +110,8 @@ class CopilotSession {
102
110
  input: (message, options) => this._input(message, options)
103
111
  };
104
112
  }
105
- /**
106
- * Sends a message to this session and waits for the response.
107
- *
108
- * The message is processed asynchronously. Subscribe to events via {@link on}
109
- * to receive streaming responses and other session events.
110
- *
111
- * @param options - The message options including the prompt and optional attachments
112
- * @returns A promise that resolves with the message ID of the response
113
- * @throws Error if the session has been disconnected or the connection fails
114
- *
115
- * @example
116
- * ```typescript
117
- * const messageId = await session.send({
118
- * prompt: "Explain this code",
119
- * attachments: [{ type: "file", path: "./src/index.ts" }]
120
- * });
121
- * ```
122
- */
123
- async send(options) {
113
+ async send(optionsOrPrompt) {
114
+ const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
124
115
  const response = await this.connection.sendRequest("session.send", {
125
116
  ...await (0, import_telemetry.getTraceContext)(this.traceContextProvider),
126
117
  sessionId: this.sessionId,
@@ -131,30 +122,8 @@ class CopilotSession {
131
122
  });
132
123
  return response.messageId;
133
124
  }
134
- /**
135
- * Sends a message to this session and waits until the session becomes idle.
136
- *
137
- * This is a convenience method that combines {@link send} with waiting for
138
- * the `session.idle` event. Use this when you want to block until the
139
- * assistant has finished processing the message.
140
- *
141
- * Events are still delivered to handlers registered via {@link on} while waiting.
142
- *
143
- * @param options - The message options including the prompt and optional attachments
144
- * @param timeout - Timeout in milliseconds (default: 60000). Controls how long to wait; does not abort in-flight agent work.
145
- * @returns A promise that resolves with the final assistant message when the session becomes idle,
146
- * or undefined if no assistant message was received
147
- * @throws Error if the timeout is reached before the session becomes idle
148
- * @throws Error if the session has been disconnected or the connection fails
149
- *
150
- * @example
151
- * ```typescript
152
- * // Send and wait for completion with default 60s timeout
153
- * const response = await session.sendAndWait({ prompt: "What is 2+2?" });
154
- * console.log(response?.data.content); // "4"
155
- * ```
156
- */
157
- async sendAndWait(options, timeout) {
125
+ async sendAndWait(optionsOrPrompt, timeout) {
126
+ const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
158
127
  const effectiveTimeout = timeout ?? 6e4;
159
128
  let resolveIdle;
160
129
  let rejectWithError;
@@ -397,8 +366,8 @@ class CopilotSession {
397
366
  /**
398
367
  * Registers custom tool handlers for this session.
399
368
  *
400
- * Tools allow the assistant to execute custom functions. When the assistant
401
- * invokes a tool, the corresponding handler is called with the tool arguments.
369
+ * Tools with handlers allow the assistant to execute custom functions automatically.
370
+ * Declaration-only tools are surfaced as events and left pending for the consumer.
402
371
  *
403
372
  * @param tools - An array of tool definitions with their handlers, or undefined to clear all tools
404
373
  * @internal This method is typically called internally when creating a session with tools.
@@ -409,7 +378,9 @@ class CopilotSession {
409
378
  return;
410
379
  }
411
380
  for (const tool of tools) {
412
- this.toolHandlers.set(tool.name, tool.handler);
381
+ if (tool.handler) {
382
+ this.toolHandlers.set(tool.name, tool.handler);
383
+ }
413
384
  }
414
385
  }
415
386
  /**
@@ -716,8 +687,10 @@ class CopilotSession {
716
687
  if (!this.hooks) {
717
688
  return void 0;
718
689
  }
690
+ const normalized = deserializeHookInput(input);
719
691
  const handlerMap = {
720
692
  preToolUse: this.hooks.onPreToolUse,
693
+ preMcpToolCall: this.hooks.onPreMcpToolCall,
721
694
  postToolUse: this.hooks.onPostToolUse,
722
695
  userPromptSubmitted: this.hooks.onUserPromptSubmitted,
723
696
  sessionStart: this.hooks.onSessionStart,
@@ -729,7 +702,7 @@ class CopilotSession {
729
702
  return void 0;
730
703
  }
731
704
  try {
732
- const result = await handler(input, { sessionId: this.sessionId });
705
+ const result = await handler(normalized, { sessionId: this.sessionId });
733
706
  return result;
734
707
  } catch (_error) {
735
708
  return void 0;
@@ -746,7 +719,7 @@ class CopilotSession {
746
719
  *
747
720
  * @example
748
721
  * ```typescript
749
- * const events = await session.getMessages();
722
+ * const events = await session.getEvents();
750
723
  * for (const event of events) {
751
724
  * if (event.type === "assistant.message") {
752
725
  * console.log("Assistant:", event.data.content);
@@ -754,7 +727,7 @@ class CopilotSession {
754
727
  * }
755
728
  * ```
756
729
  */
757
- async getMessages() {
730
+ async getEvents() {
758
731
  const response = await this.connection.sendRequest("session.getMessages", {
759
732
  sessionId: this.sessionId
760
733
  });
@@ -794,18 +767,6 @@ class CopilotSession {
794
767
  this.exitPlanModeHandler = void 0;
795
768
  this.autoModeSwitchHandler = void 0;
796
769
  }
797
- /**
798
- * @deprecated Use {@link disconnect} instead. This method will be removed in a future release.
799
- *
800
- * Disconnects this session and releases all in-memory resources.
801
- * Session data on disk is preserved for later resumption.
802
- *
803
- * @returns A promise that resolves when the session is disconnected
804
- * @throws Error if the connection fails
805
- */
806
- async destroy() {
807
- return this.disconnect();
808
- }
809
770
  /** Enables `await using session = ...` syntax for automatic cleanup. */
810
771
  async [Symbol.asyncDispose]() {
811
772
  return this.disconnect();
@@ -21,6 +21,18 @@ __export(sessionFsProvider_exports, {
21
21
  createSessionFsAdapter: () => createSessionFsAdapter
22
22
  });
23
23
  module.exports = __toCommonJS(sessionFsProvider_exports);
24
+ function normalizeSqliteParams(params) {
25
+ if (!params) {
26
+ return void 0;
27
+ }
28
+ const normalized = {};
29
+ for (const [key, value] of Object.entries(params)) {
30
+ if (value !== void 0) {
31
+ normalized[key] = value;
32
+ }
33
+ }
34
+ return normalized;
35
+ }
24
36
  function createSessionFsAdapter(provider) {
25
37
  return {
26
38
  readFile: async ({ path }) => {
@@ -107,6 +119,28 @@ function createSessionFsAdapter(provider) {
107
119
  } catch (err) {
108
120
  return toSessionFsError(err);
109
121
  }
122
+ },
123
+ // Unlike the FS methods above, SQLite methods let errors propagate to the JSON-RPC layer
124
+ // rather than catching and mapping via toSessionFsError. The FS error mapping is specifically
125
+ // for translating Node.js errno codes (e.g., ENOENT) into SessionFsError, which isn't
126
+ // meaningful for SQL errors. Letting exceptions propagate preserves the original error
127
+ // message in the JSON-RPC error response.
128
+ sqliteQuery: async ({ queryType, query, params: bindParams }) => {
129
+ if (!provider.sqlite) {
130
+ throw new Error("SQLite is not supported by this provider");
131
+ }
132
+ const result = await provider.sqlite.query(
133
+ queryType,
134
+ query,
135
+ normalizeSqliteParams(bindParams)
136
+ );
137
+ return result ?? { rows: [], columns: [], rowsAffected: 0 };
138
+ },
139
+ sqliteExists: async () => {
140
+ if (!provider.sqlite) {
141
+ throw new Error("SQLite is not supported by this provider");
142
+ }
143
+ return { exists: await provider.sqlite.exists() };
110
144
  }
111
145
  };
112
146
  }
package/dist/cjs/types.js CHANGED
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var types_exports = {};
20
20
  __export(types_exports, {
21
+ RuntimeConnection: () => RuntimeConnection,
21
22
  SYSTEM_PROMPT_SECTIONS: () => SYSTEM_PROMPT_SECTIONS,
22
23
  approveAll: () => approveAll,
23
24
  convertMcpCallToolResult: () => convertMcpCallToolResult,
@@ -27,6 +28,34 @@ __export(types_exports, {
27
28
  });
28
29
  module.exports = __toCommonJS(types_exports);
29
30
  var import_sessionFsProvider = require("./sessionFsProvider.js");
31
+ const RuntimeConnection = {
32
+ /**
33
+ * Spawn a runtime child process and communicate over its stdin/stdout.
34
+ * This is the default if no {@link CopilotClientOptions.connection} is set.
35
+ */
36
+ forStdio(opts = {}) {
37
+ return { kind: "stdio", path: opts.path, args: opts.args };
38
+ },
39
+ /**
40
+ * Spawn a runtime child process that listens on a TCP socket and connect to it.
41
+ */
42
+ forTcp(opts = {}) {
43
+ return {
44
+ kind: "tcp",
45
+ port: opts.port,
46
+ connectionToken: opts.connectionToken,
47
+ path: opts.path,
48
+ args: opts.args
49
+ };
50
+ },
51
+ /**
52
+ * Connect to an already-running runtime at the given URL. The SDK does not
53
+ * spawn a process in this mode.
54
+ */
55
+ forUri(url, opts = {}) {
56
+ return { kind: "uri", url, connectionToken: opts.connectionToken };
57
+ }
58
+ };
30
59
  function convertMcpCallToolResult(callResult) {
31
60
  const textParts = [];
32
61
  const binaryResults = [];
@@ -92,6 +121,7 @@ const defaultJoinSessionPermissionHandler = () => ({
92
121
  });
93
122
  // Annotate the CommonJS export names for ESM import in node:
94
123
  0 && (module.exports = {
124
+ RuntimeConnection,
95
125
  SYSTEM_PROMPT_SECTIONS,
96
126
  approveAll,
97
127
  convertMcpCallToolResult,
package/dist/client.d.ts CHANGED
@@ -16,7 +16,7 @@ import type { ConnectionState, CopilotClientOptions, GetAuthStatusResponse, GetS
16
16
  * const client = new CopilotClient();
17
17
  *
18
18
  * // Or connect to an existing server
19
- * const client = new CopilotClient({ cliUrl: "localhost:3000" });
19
+ * const client = new CopilotClient({ connection: RuntimeConnection.forUri("localhost:3000") });
20
20
  *
21
21
  * // Create a session
22
22
  * const session = await client.createSession({ onPermissionRequest: approveAll, model: "gpt-4" });
@@ -39,11 +39,17 @@ export declare class CopilotClient {
39
39
  private cliProcess;
40
40
  private connection;
41
41
  private socket;
42
- private actualPort;
42
+ private runtimePort;
43
43
  private actualHost;
44
44
  private state;
45
45
  private sessions;
46
46
  private stderrBuffer;
47
+ /** Resolved connection mode chosen in the constructor. */
48
+ private connectionConfig;
49
+ /** Resolved path to the runtime executable (only used for child-process kinds). */
50
+ private resolvedCliPath;
51
+ /** Resolved environment passed to the spawned runtime. */
52
+ private resolvedEnv;
47
53
  private options;
48
54
  private isExternalServer;
49
55
  private forceStopping;
@@ -66,53 +72,56 @@ export declare class CopilotClient {
66
72
  * @throws Error if the client is not connected
67
73
  */
68
74
  get rpc(): ReturnType<typeof createServerRpc>;
69
- /**
70
- * Internal RPC surface (e.g. handshake helpers). Not part of the public API.
71
- * @internal
72
- */
73
- private get internalRpc();
74
75
  /**
75
76
  * Creates a new CopilotClient instance.
76
77
  *
77
78
  * @param options - Configuration options for the client
78
- * @throws Error if mutually exclusive options are provided (e.g., cliUrl with useStdio or cliPath)
79
79
  *
80
80
  * @example
81
81
  * ```typescript
82
- * // Default options - spawns CLI server using stdio
82
+ * // Default: spawns the bundled runtime over stdio
83
83
  * const client = new CopilotClient();
84
84
  *
85
- * // Connect to an existing server
86
- * const client = new CopilotClient({ cliUrl: "localhost:3000" });
85
+ * // Connect to an existing runtime
86
+ * const client = new CopilotClient({
87
+ * connection: RuntimeConnection.forUri("localhost:3000"),
88
+ * });
89
+ *
90
+ * // Spawn the runtime over TCP on a chosen port
91
+ * const client = new CopilotClient({
92
+ * connection: RuntimeConnection.forTcp({ port: 9001 }),
93
+ * });
87
94
  *
88
- * // Custom CLI path with specific log level
95
+ * // Use a custom runtime binary
89
96
  * const client = new CopilotClient({
90
- * cliPath: "/usr/local/bin/copilot",
91
- * logLevel: "debug"
97
+ * connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }),
98
+ * logLevel: "debug",
92
99
  * });
93
100
  * ```
94
101
  */
95
102
  constructor(options?: CopilotClientOptions);
103
+ private connectionExtraArgs;
96
104
  /**
97
105
  * Parse CLI URL into host and port
98
106
  * Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
99
107
  */
100
108
  private parseCliUrl;
101
109
  private validateSessionFsConfig;
110
+ private setupSessionFs;
102
111
  /**
103
112
  * Starts the CLI server and establishes a connection.
104
113
  *
105
114
  * If connecting to an external server (via cliUrl), only establishes the connection.
106
115
  * Otherwise, spawns the CLI server process and then connects.
107
116
  *
108
- * This method is called automatically when creating a session if `autoStart` is true (default).
117
+ * This method is called automatically the first time you create or resume a session.
109
118
  *
110
119
  * @returns A promise that resolves when the connection is established
111
120
  * @throws Error if the server fails to start or the connection fails
112
121
  *
113
122
  * @example
114
123
  * ```typescript
115
- * const client = new CopilotClient({ autoStart: false });
124
+ * const client = new CopilotClient();
116
125
  * await client.start();
117
126
  * // Now ready to create sessions
118
127
  * ```
@@ -142,6 +151,19 @@ export declare class CopilotClient {
142
151
  * ```
143
152
  */
144
153
  stop(): Promise<Error[]>;
154
+ /**
155
+ * Alias for {@link stop} that lets `CopilotClient` participate in `await using`
156
+ * blocks for automatic cleanup.
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * await using client = new CopilotClient();
161
+ * const session = await client.createSession({ onPermissionRequest: approveAll });
162
+ * await session.sendAndWait("Hello");
163
+ * // client.stop() is called automatically when the block exits.
164
+ * ```
165
+ */
166
+ [Symbol.asyncDispose](): Promise<void>;
145
167
  /**
146
168
  * Forcefully stops the CLI server without graceful cleanup.
147
169
  *
@@ -172,12 +194,11 @@ export declare class CopilotClient {
172
194
  * Creates a new conversation session with the Copilot CLI.
173
195
  *
174
196
  * Sessions maintain conversation state, handle events, and manage tool execution.
175
- * If the client is not connected and `autoStart` is enabled, this will automatically
176
- * start the connection.
197
+ * If the client is not connected, this method automatically starts the connection.
177
198
  *
178
199
  * @param config - Optional configuration for the session
179
200
  * @returns A promise that resolves with the created session
180
- * @throws Error if the client is not connected and autoStart is disabled
201
+ * @throws Error if the client fails to start
181
202
  *
182
203
  * @example
183
204
  * ```typescript
@@ -251,7 +272,7 @@ export declare class CopilotClient {
251
272
  */
252
273
  ping(message?: string): Promise<{
253
274
  message: string;
254
- timestamp: number;
275
+ timestamp: string;
255
276
  protocolVersion?: number;
256
277
  }>;
257
278
  /**
@@ -398,7 +419,7 @@ export declare class CopilotClient {
398
419
  * @example
399
420
  * ```typescript
400
421
  * // Listen for when a session becomes foreground in TUI
401
- * const unsubscribe = client.on("session.foreground", (event) => {
422
+ * const unsubscribe = client.onLifecycle("session.foreground", (event) => {
402
423
  * console.log(`Session ${event.sessionId} is now displayed in TUI`);
403
424
  * });
404
425
  *
@@ -406,7 +427,7 @@ export declare class CopilotClient {
406
427
  * unsubscribe();
407
428
  * ```
408
429
  */
409
- on<K extends SessionLifecycleEventType>(eventType: K, handler: TypedSessionLifecycleHandler<K>): () => void;
430
+ onLifecycle<K extends SessionLifecycleEventType>(eventType: K, handler: TypedSessionLifecycleHandler<K>): () => void;
410
431
  /**
411
432
  * Subscribes to all session lifecycle events.
412
433
  *
@@ -415,7 +436,7 @@ export declare class CopilotClient {
415
436
  *
416
437
  * @example
417
438
  * ```typescript
418
- * const unsubscribe = client.on((event) => {
439
+ * const unsubscribe = client.onLifecycle((event) => {
419
440
  * switch (event.type) {
420
441
  * case "session.foreground":
421
442
  * console.log(`Session ${event.sessionId} is now in foreground`);
@@ -430,7 +451,7 @@ export declare class CopilotClient {
430
451
  * unsubscribe();
431
452
  * ```
432
453
  */
433
- on(handler: SessionLifecycleHandler): () => void;
454
+ onLifecycle(handler: SessionLifecycleHandler): () => void;
434
455
  /**
435
456
  * Start the CLI server process
436
457
  */