@github/copilot-sdk 0.1.31 → 0.1.33-unstable.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/dist/client.d.ts CHANGED
@@ -1,6 +1,39 @@
1
1
  import { createServerRpc } from "./generated/rpc.js";
2
2
  import { CopilotSession } from "./session.js";
3
3
  import type { ConnectionState, CopilotClientOptions, GetAuthStatusResponse, GetStatusResponse, ModelInfo, ResumeSessionConfig, SessionConfig, SessionLifecycleEventType, SessionLifecycleHandler, SessionListFilter, SessionMetadata, TypedSessionLifecycleHandler } from "./types.js";
4
+ /**
5
+ * Main client for interacting with the Copilot CLI.
6
+ *
7
+ * The CopilotClient manages the connection to the Copilot CLI server and provides
8
+ * methods to create and manage conversation sessions. It can either spawn a CLI
9
+ * server process or connect to an existing server.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { CopilotClient } from "@github/copilot-sdk";
14
+ *
15
+ * // Create a client with default options (spawns CLI server)
16
+ * const client = new CopilotClient();
17
+ *
18
+ * // Or connect to an existing server
19
+ * const client = new CopilotClient({ cliUrl: "localhost:3000" });
20
+ *
21
+ * // Create a session
22
+ * const session = await client.createSession({ onPermissionRequest: approveAll, model: "gpt-4" });
23
+ *
24
+ * // Send messages and handle responses
25
+ * session.on((event) => {
26
+ * if (event.type === "assistant.message") {
27
+ * console.log(event.data.content);
28
+ * }
29
+ * });
30
+ * await session.send({ prompt: "Hello!" });
31
+ *
32
+ * // Clean up
33
+ * await session.disconnect();
34
+ * await client.stop();
35
+ * ```
36
+ */
4
37
  export declare class CopilotClient {
5
38
  private cliProcess;
6
39
  private connection;
@@ -13,12 +46,14 @@ export declare class CopilotClient {
13
46
  private options;
14
47
  private isExternalServer;
15
48
  private forceStopping;
49
+ private onListModels?;
16
50
  private modelsCache;
17
51
  private modelsCacheLock;
18
52
  private sessionLifecycleHandlers;
19
53
  private typedLifecycleHandlers;
20
54
  private _rpc;
21
55
  private processExitPromise;
56
+ private negotiatedProtocolVersion;
22
57
  /**
23
58
  * Typed server-scoped RPC methods.
24
59
  * @throws Error if the client is not connected
@@ -217,14 +252,18 @@ export declare class CopilotClient {
217
252
  /**
218
253
  * List available models with their metadata.
219
254
  *
255
+ * If an `onListModels` handler was provided in the client options,
256
+ * it is called instead of querying the CLI server.
257
+ *
220
258
  * Results are cached after the first successful call to avoid rate limiting.
221
259
  * The cache is cleared when the client disconnects.
222
260
  *
223
- * @throws Error if not authenticated
261
+ * @throws Error if not connected (when no custom handler is set)
224
262
  */
225
263
  listModels(): Promise<ModelInfo[]>;
226
264
  /**
227
- * Verify that the server's protocol version matches the SDK's expected version
265
+ * Verify that the server's protocol version is within the supported range
266
+ * and store the negotiated version.
228
267
  */
229
268
  private verifyProtocolVersion;
230
269
  /**
@@ -383,6 +422,18 @@ export declare class CopilotClient {
383
422
  private handleSessionLifecycleNotification;
384
423
  private handleUserInputRequest;
385
424
  private handleHooksInvoke;
425
+ /**
426
+ * Handles a v2-style tool.call RPC request from the server.
427
+ * Looks up the session and tool handler, executes it, and returns the result
428
+ * in the v2 response format.
429
+ */
430
+ private handleToolCallRequestV2;
431
+ /**
432
+ * Handles a v2-style permission.request RPC request from the server.
433
+ */
434
+ private handlePermissionRequestV2;
435
+ private normalizeToolResultV2;
436
+ private isToolResultObject;
386
437
  /**
387
438
  * Attempt to reconnect to the server
388
439
  */
package/dist/client.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  import { createServerRpc } from "./generated/rpc.js";
12
12
  import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
13
13
  import { CopilotSession } from "./session.js";
14
+ const MIN_PROTOCOL_VERSION = 2;
14
15
  function isZodSchema(value) {
15
16
  return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
16
17
  }
@@ -45,6 +46,7 @@ class CopilotClient {
45
46
  options;
46
47
  isExternalServer = false;
47
48
  forceStopping = false;
49
+ onListModels;
48
50
  modelsCache = null;
49
51
  modelsCacheLock = Promise.resolve();
50
52
  sessionLifecycleHandlers = /* @__PURE__ */ new Set();
@@ -52,6 +54,7 @@ class CopilotClient {
52
54
  _rpc = null;
53
55
  processExitPromise = null;
54
56
  // Rejects when CLI process exits
57
+ negotiatedProtocolVersion = null;
55
58
  /**
56
59
  * Typed server-scoped RPC methods.
57
60
  * @throws Error if the client is not connected
@@ -109,6 +112,7 @@ class CopilotClient {
109
112
  if (options.isChildProcess) {
110
113
  this.isExternalServer = true;
111
114
  }
115
+ this.onListModels = options.onListModels;
112
116
  this.options = {
113
117
  cliPath: options.cliPath || getBundledCliPath(),
114
118
  cliArgs: options.cliArgs ?? [],
@@ -399,6 +403,7 @@ class CopilotClient {
399
403
  mcpServers: config.mcpServers,
400
404
  envValueMode: "direct",
401
405
  customAgents: config.customAgents,
406
+ agent: config.agent,
402
407
  configDir: config.configDir,
403
408
  skillDirectories: config.skillDirectories,
404
409
  disabledSkills: config.disabledSkills,
@@ -478,6 +483,7 @@ class CopilotClient {
478
483
  mcpServers: config.mcpServers,
479
484
  envValueMode: "direct",
480
485
  customAgents: config.customAgents,
486
+ agent: config.agent,
481
487
  skillDirectories: config.skillDirectories,
482
488
  disabledSkills: config.disabledSkills,
483
489
  infiniteSessions: config.infiniteSessions,
@@ -554,15 +560,15 @@ class CopilotClient {
554
560
  /**
555
561
  * List available models with their metadata.
556
562
  *
563
+ * If an `onListModels` handler was provided in the client options,
564
+ * it is called instead of querying the CLI server.
565
+ *
557
566
  * Results are cached after the first successful call to avoid rate limiting.
558
567
  * The cache is cleared when the client disconnects.
559
568
  *
560
- * @throws Error if not authenticated
569
+ * @throws Error if not connected (when no custom handler is set)
561
570
  */
562
571
  async listModels() {
563
- if (!this.connection) {
564
- throw new Error("Client not connected");
565
- }
566
572
  await this.modelsCacheLock;
567
573
  let resolveLock;
568
574
  this.modelsCacheLock = new Promise((resolve) => {
@@ -572,20 +578,29 @@ class CopilotClient {
572
578
  if (this.modelsCache !== null) {
573
579
  return [...this.modelsCache];
574
580
  }
575
- const result = await this.connection.sendRequest("models.list", {});
576
- const response = result;
577
- const models = response.models;
578
- this.modelsCache = models;
581
+ let models;
582
+ if (this.onListModels) {
583
+ models = await this.onListModels();
584
+ } else {
585
+ if (!this.connection) {
586
+ throw new Error("Client not connected");
587
+ }
588
+ const result = await this.connection.sendRequest("models.list", {});
589
+ const response = result;
590
+ models = response.models;
591
+ }
592
+ this.modelsCache = [...models];
579
593
  return [...models];
580
594
  } finally {
581
595
  resolveLock();
582
596
  }
583
597
  }
584
598
  /**
585
- * Verify that the server's protocol version matches the SDK's expected version
599
+ * Verify that the server's protocol version is within the supported range
600
+ * and store the negotiated version.
586
601
  */
587
602
  async verifyProtocolVersion() {
588
- const expectedVersion = getSdkProtocolVersion();
603
+ const maxVersion = getSdkProtocolVersion();
589
604
  let pingResult;
590
605
  if (this.processExitPromise) {
591
606
  pingResult = await Promise.race([this.ping(), this.processExitPromise]);
@@ -595,14 +610,15 @@ class CopilotClient {
595
610
  const serverVersion = pingResult.protocolVersion;
596
611
  if (serverVersion === void 0) {
597
612
  throw new Error(
598
- `SDK protocol version mismatch: SDK expects version ${expectedVersion}, but server does not report a protocol version. Please update your server to ensure compatibility.`
613
+ `SDK protocol version mismatch: SDK supports versions ${MIN_PROTOCOL_VERSION}-${maxVersion}, but server does not report a protocol version. Please update your server to ensure compatibility.`
599
614
  );
600
615
  }
601
- if (serverVersion !== expectedVersion) {
616
+ if (serverVersion < MIN_PROTOCOL_VERSION || serverVersion > maxVersion) {
602
617
  throw new Error(
603
- `SDK protocol version mismatch: SDK expects version ${expectedVersion}, but server reports version ${serverVersion}. Please update your SDK or server to ensure compatibility.`
618
+ `SDK protocol version mismatch: SDK supports versions ${MIN_PROTOCOL_VERSION}-${maxVersion}, but server reports version ${serverVersion}. Please update your SDK or server to ensure compatibility.`
604
619
  );
605
620
  }
621
+ this.negotiatedProtocolVersion = serverVersion;
606
622
  }
607
623
  /**
608
624
  * Gets the ID of the most recently updated session.
@@ -975,6 +991,14 @@ stderr: ${stderrOutput}`
975
991
  this.connection.onNotification("session.lifecycle", (notification) => {
976
992
  this.handleSessionLifecycleNotification(notification);
977
993
  });
994
+ this.connection.onRequest(
995
+ "tool.call",
996
+ async (params) => await this.handleToolCallRequestV2(params)
997
+ );
998
+ this.connection.onRequest(
999
+ "permission.request",
1000
+ async (params) => await this.handlePermissionRequestV2(params)
1001
+ );
978
1002
  this.connection.onRequest(
979
1003
  "userInput.request",
980
1004
  async (params) => await this.handleUserInputRequest(params)
@@ -1047,6 +1071,98 @@ stderr: ${stderrOutput}`
1047
1071
  const output = await session._handleHooksInvoke(params.hookType, params.input);
1048
1072
  return { output };
1049
1073
  }
1074
+ // ========================================================================
1075
+ // Protocol v2 backward-compatibility adapters
1076
+ // ========================================================================
1077
+ /**
1078
+ * Handles a v2-style tool.call RPC request from the server.
1079
+ * Looks up the session and tool handler, executes it, and returns the result
1080
+ * in the v2 response format.
1081
+ */
1082
+ async handleToolCallRequestV2(params) {
1083
+ if (!params || typeof params.sessionId !== "string" || typeof params.toolCallId !== "string" || typeof params.toolName !== "string") {
1084
+ throw new Error("Invalid tool call payload");
1085
+ }
1086
+ const session = this.sessions.get(params.sessionId);
1087
+ if (!session) {
1088
+ throw new Error(`Unknown session ${params.sessionId}`);
1089
+ }
1090
+ const handler = session.getToolHandler(params.toolName);
1091
+ if (!handler) {
1092
+ return {
1093
+ result: {
1094
+ textResultForLlm: `Tool '${params.toolName}' is not supported by this client instance.`,
1095
+ resultType: "failure",
1096
+ error: `tool '${params.toolName}' not supported`,
1097
+ toolTelemetry: {}
1098
+ }
1099
+ };
1100
+ }
1101
+ try {
1102
+ const invocation = {
1103
+ sessionId: params.sessionId,
1104
+ toolCallId: params.toolCallId,
1105
+ toolName: params.toolName,
1106
+ arguments: params.arguments
1107
+ };
1108
+ const result = await handler(params.arguments, invocation);
1109
+ return { result: this.normalizeToolResultV2(result) };
1110
+ } catch (error) {
1111
+ const message = error instanceof Error ? error.message : String(error);
1112
+ return {
1113
+ result: {
1114
+ textResultForLlm: "Invoking this tool produced an error. Detailed information is not available.",
1115
+ resultType: "failure",
1116
+ error: message,
1117
+ toolTelemetry: {}
1118
+ }
1119
+ };
1120
+ }
1121
+ }
1122
+ /**
1123
+ * Handles a v2-style permission.request RPC request from the server.
1124
+ */
1125
+ async handlePermissionRequestV2(params) {
1126
+ if (!params || typeof params.sessionId !== "string" || !params.permissionRequest) {
1127
+ throw new Error("Invalid permission request payload");
1128
+ }
1129
+ const session = this.sessions.get(params.sessionId);
1130
+ if (!session) {
1131
+ throw new Error(`Session not found: ${params.sessionId}`);
1132
+ }
1133
+ try {
1134
+ const result = await session._handlePermissionRequestV2(params.permissionRequest);
1135
+ return { result };
1136
+ } catch (_error) {
1137
+ return {
1138
+ result: {
1139
+ kind: "denied-no-approval-rule-and-could-not-request-from-user"
1140
+ }
1141
+ };
1142
+ }
1143
+ }
1144
+ normalizeToolResultV2(result) {
1145
+ if (result === void 0 || result === null) {
1146
+ return {
1147
+ textResultForLlm: "Tool returned no result",
1148
+ resultType: "failure",
1149
+ error: "tool returned no result",
1150
+ toolTelemetry: {}
1151
+ };
1152
+ }
1153
+ if (this.isToolResultObject(result)) {
1154
+ return result;
1155
+ }
1156
+ const textResult = typeof result === "string" ? result : JSON.stringify(result);
1157
+ return {
1158
+ textResultForLlm: textResult,
1159
+ resultType: "success",
1160
+ toolTelemetry: {}
1161
+ };
1162
+ }
1163
+ isToolResultObject(value) {
1164
+ return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
1165
+ }
1050
1166
  /**
1051
1167
  * Attempt to reconnect to the server
1052
1168
  */
@@ -1,2 +1,20 @@
1
- import { CopilotClient } from "./client.js";
2
- export declare const extension: CopilotClient;
1
+ import type { CopilotSession } from "./session.js";
2
+ import type { ResumeSessionConfig } from "./types.js";
3
+ /**
4
+ * Joins the current foreground session.
5
+ *
6
+ * @param config - Configuration to add to the session
7
+ * @returns A promise that resolves with the joined session
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { approveAll } from "@github/copilot-sdk";
12
+ * import { joinSession } from "@github/copilot-sdk/extension";
13
+ *
14
+ * const session = await joinSession({
15
+ * onPermissionRequest: approveAll,
16
+ * tools: [myTool],
17
+ * });
18
+ * ```
19
+ */
20
+ export declare function joinSession(config: ResumeSessionConfig): Promise<CopilotSession>;
package/dist/extension.js CHANGED
@@ -1,5 +1,17 @@
1
1
  import { CopilotClient } from "./client.js";
2
- const extension = new CopilotClient({ isChildProcess: true });
2
+ async function joinSession(config) {
3
+ const sessionId = process.env.SESSION_ID;
4
+ if (!sessionId) {
5
+ throw new Error(
6
+ "joinSession() is intended for extensions running as child processes of the Copilot CLI."
7
+ );
8
+ }
9
+ const client = new CopilotClient({ isChildProcess: true });
10
+ return client.resumeSession(sessionId, {
11
+ ...config,
12
+ disableResume: config.disableResume ?? true
13
+ });
14
+ }
3
15
  export {
4
- extension
16
+ joinSession
5
17
  };
@@ -162,6 +162,7 @@ export interface SessionModelSwitchToParams {
162
162
  */
163
163
  sessionId: string;
164
164
  modelId: string;
165
+ reasoningEffort?: "low" | "medium" | "high" | "xhigh";
165
166
  }
166
167
  export interface SessionModeGetResult {
167
168
  /**
@@ -442,6 +443,30 @@ export interface SessionPermissionsHandlePendingPermissionRequestParams {
442
443
  message: string;
443
444
  };
444
445
  }
446
+ export interface SessionLogResult {
447
+ /**
448
+ * The unique identifier of the emitted session event
449
+ */
450
+ eventId: string;
451
+ }
452
+ export interface SessionLogParams {
453
+ /**
454
+ * Target session identifier
455
+ */
456
+ sessionId: string;
457
+ /**
458
+ * Human-readable message
459
+ */
460
+ message: string;
461
+ /**
462
+ * Log severity level. Determines how the message is displayed in the timeline. Defaults to "info".
463
+ */
464
+ level?: "info" | "warning" | "error";
465
+ /**
466
+ * When true, the message is transient and not persisted to the session event log on disk
467
+ */
468
+ ephemeral?: boolean;
469
+ }
445
470
  /** Create typed server-scoped RPC methods (no session required). */
446
471
  export declare function createServerRpc(connection: MessageConnection): {
447
472
  ping: (params: PingParams) => Promise<PingResult>;
@@ -493,4 +518,5 @@ export declare function createSessionRpc(connection: MessageConnection, sessionI
493
518
  permissions: {
494
519
  handlePendingPermissionRequest: (params: Omit<SessionPermissionsHandlePendingPermissionRequestParams, "sessionId">) => Promise<SessionPermissionsHandlePendingPermissionRequestResult>;
495
520
  };
521
+ log: (params: Omit<SessionLogParams, "sessionId">) => Promise<SessionLogResult>;
496
522
  };
@@ -49,7 +49,8 @@ function createSessionRpc(connection, sessionId) {
49
49
  },
50
50
  permissions: {
51
51
  handlePendingPermissionRequest: async (params) => connection.sendRequest("session.permissions.handlePendingPermissionRequest", { sessionId, ...params })
52
- }
52
+ },
53
+ log: async (params) => connection.sendRequest("session.log", { sessionId, ...params })
53
54
  };
54
55
  }
55
56
  export {
@@ -66,6 +66,7 @@ export type SessionEvent = {
66
66
  */
67
67
  branch?: string;
68
68
  };
69
+ alreadyInUse?: boolean;
69
70
  };
70
71
  } | {
71
72
  /**
@@ -115,6 +116,7 @@ export type SessionEvent = {
115
116
  */
116
117
  branch?: string;
117
118
  };
119
+ alreadyInUse?: boolean;
118
120
  };
119
121
  } | {
120
122
  /**
@@ -2090,6 +2092,80 @@ export type SessionEvent = {
2090
2092
  };
2091
2093
  };
2092
2094
  };
2095
+ } | {
2096
+ /**
2097
+ * Unique event identifier (UUID v4), generated when the event is emitted
2098
+ */
2099
+ id: string;
2100
+ /**
2101
+ * ISO 8601 timestamp when the event was created
2102
+ */
2103
+ timestamp: string;
2104
+ /**
2105
+ * ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event.
2106
+ */
2107
+ parentId: string | null;
2108
+ /**
2109
+ * When true, the event is transient and not persisted to the session event log on disk
2110
+ */
2111
+ ephemeral?: boolean;
2112
+ type: "system.notification";
2113
+ data: {
2114
+ /**
2115
+ * The notification text, typically wrapped in <system_notification> XML tags
2116
+ */
2117
+ content: string;
2118
+ /**
2119
+ * Structured metadata identifying what triggered this notification
2120
+ */
2121
+ kind: {
2122
+ type: "agent_completed";
2123
+ /**
2124
+ * Unique identifier of the background agent
2125
+ */
2126
+ agentId: string;
2127
+ /**
2128
+ * Type of the agent (e.g., explore, task, general-purpose)
2129
+ */
2130
+ agentType: string;
2131
+ /**
2132
+ * Whether the agent completed successfully or failed
2133
+ */
2134
+ status: "completed" | "failed";
2135
+ /**
2136
+ * Human-readable description of the agent task
2137
+ */
2138
+ description?: string;
2139
+ /**
2140
+ * The full prompt given to the background agent
2141
+ */
2142
+ prompt?: string;
2143
+ } | {
2144
+ type: "shell_completed";
2145
+ /**
2146
+ * Unique identifier of the shell session
2147
+ */
2148
+ shellId: string;
2149
+ /**
2150
+ * Exit code of the shell command, if available
2151
+ */
2152
+ exitCode?: number;
2153
+ /**
2154
+ * Human-readable description of the command
2155
+ */
2156
+ description?: string;
2157
+ } | {
2158
+ type: "shell_detached_completed";
2159
+ /**
2160
+ * Unique identifier of the detached shell session
2161
+ */
2162
+ shellId: string;
2163
+ /**
2164
+ * Human-readable description of the command
2165
+ */
2166
+ description?: string;
2167
+ };
2168
+ };
2093
2169
  } | {
2094
2170
  /**
2095
2171
  * Unique event identifier (UUID v4), generated when the event is emitted
package/dist/session.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { MessageConnection } from "vscode-jsonrpc/node";
6
6
  import { createSessionRpc } from "./generated/rpc.js";
7
- import type { MessageOptions, PermissionHandler, SessionEvent, SessionEventHandler, SessionEventType, SessionHooks, Tool, ToolHandler, TypedSessionEventHandler, UserInputHandler, UserInputResponse } from "./types.js";
7
+ import type { MessageOptions, PermissionHandler, PermissionRequestResult, SessionEvent, SessionEventHandler, SessionEventType, SessionHooks, Tool, ToolHandler, TypedSessionEventHandler, UserInputHandler, UserInputResponse } from "./types.js";
8
8
  /** Assistant message event - the final response from the assistant. */
9
9
  export type AssistantMessageEvent = Extract<SessionEvent, {
10
10
  type: "assistant.message";
@@ -226,6 +226,15 @@ export declare class CopilotSession {
226
226
  * @internal This method is typically called internally when creating a session.
227
227
  */
228
228
  registerHooks(hooks?: SessionHooks): void;
229
+ /**
230
+ * Handles a permission request in the v2 protocol format (synchronous RPC).
231
+ * Used as a back-compat adapter when connected to a v2 server.
232
+ *
233
+ * @param request - The permission request data from the CLI
234
+ * @returns A promise that resolves with the permission decision
235
+ * @internal This method is for internal use by the SDK.
236
+ */
237
+ _handlePermissionRequestV2(request: unknown): Promise<PermissionRequestResult>;
229
238
  /**
230
239
  * Handles a user input request from the Copilot CLI.
231
240
  *
@@ -330,4 +339,24 @@ export declare class CopilotSession {
330
339
  * ```
331
340
  */
332
341
  setModel(model: string): Promise<void>;
342
+ /**
343
+ * Log a message to the session timeline.
344
+ * The message appears in the session event stream and is visible to SDK consumers
345
+ * and (for non-ephemeral messages) persisted to the session event log on disk.
346
+ *
347
+ * @param message - Human-readable message text
348
+ * @param options - Optional log level and ephemeral flag
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * await session.log("Processing started");
353
+ * await session.log("Disk usage high", { level: "warning" });
354
+ * await session.log("Connection failed", { level: "error" });
355
+ * await session.log("Debug info", { ephemeral: true });
356
+ * ```
357
+ */
358
+ log(message: string, options?: {
359
+ level?: "info" | "warning" | "error";
360
+ ephemeral?: boolean;
361
+ }): Promise<void>;
333
362
  }
package/dist/session.js CHANGED
@@ -319,6 +319,27 @@ class CopilotSession {
319
319
  registerHooks(hooks) {
320
320
  this.hooks = hooks;
321
321
  }
322
+ /**
323
+ * Handles a permission request in the v2 protocol format (synchronous RPC).
324
+ * Used as a back-compat adapter when connected to a v2 server.
325
+ *
326
+ * @param request - The permission request data from the CLI
327
+ * @returns A promise that resolves with the permission decision
328
+ * @internal This method is for internal use by the SDK.
329
+ */
330
+ async _handlePermissionRequestV2(request) {
331
+ if (!this.permissionHandler) {
332
+ return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
333
+ }
334
+ try {
335
+ const result = await this.permissionHandler(request, {
336
+ sessionId: this.sessionId
337
+ });
338
+ return result;
339
+ } catch (_error) {
340
+ return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
341
+ }
342
+ }
322
343
  /**
323
344
  * Handles a user input request from the Copilot CLI.
324
345
  *
@@ -480,6 +501,25 @@ class CopilotSession {
480
501
  async setModel(model) {
481
502
  await this.rpc.model.switchTo({ modelId: model });
482
503
  }
504
+ /**
505
+ * Log a message to the session timeline.
506
+ * The message appears in the session event stream and is visible to SDK consumers
507
+ * and (for non-ephemeral messages) persisted to the session event log on disk.
508
+ *
509
+ * @param message - Human-readable message text
510
+ * @param options - Optional log level and ephemeral flag
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * await session.log("Processing started");
515
+ * await session.log("Disk usage high", { level: "warning" });
516
+ * await session.log("Connection failed", { level: "error" });
517
+ * await session.log("Debug info", { ephemeral: true });
518
+ * ```
519
+ */
520
+ async log(message, options) {
521
+ await this.rpc.log({ message, ...options });
522
+ }
483
523
  }
484
524
  export {
485
525
  CopilotSession
package/dist/types.d.ts CHANGED
@@ -77,6 +77,13 @@ export interface CopilotClientOptions {
77
77
  * @default true (but defaults to false when githubToken is provided)
78
78
  */
79
79
  useLoggedInUser?: boolean;
80
+ /**
81
+ * Custom handler for listing available models.
82
+ * When provided, client.listModels() calls this handler instead of
83
+ * querying the CLI server. Useful in BYOK mode to return models
84
+ * available from your custom provider.
85
+ */
86
+ onListModels?: () => Promise<ModelInfo[]> | ModelInfo[];
80
87
  }
81
88
  /**
82
89
  * Configuration for creating a session
@@ -586,6 +593,12 @@ export interface SessionConfig {
586
593
  * Custom agent configurations for the session.
587
594
  */
588
595
  customAgents?: CustomAgentConfig[];
596
+ /**
597
+ * Name of the custom agent to activate when the session starts.
598
+ * Must match the `name` of one of the agents in `customAgents`.
599
+ * Equivalent to calling `session.rpc.agent.select({ name })` after creation.
600
+ */
601
+ agent?: string;
589
602
  /**
590
603
  * Directories to load skills from.
591
604
  */
@@ -604,7 +617,7 @@ export interface SessionConfig {
604
617
  /**
605
618
  * Configuration for resuming a session
606
619
  */
607
- export type ResumeSessionConfig = Pick<SessionConfig, "clientName" | "model" | "tools" | "systemMessage" | "availableTools" | "excludedTools" | "provider" | "streaming" | "reasoningEffort" | "onPermissionRequest" | "onUserInputRequest" | "hooks" | "workingDirectory" | "configDir" | "mcpServers" | "customAgents" | "skillDirectories" | "disabledSkills" | "infiniteSessions"> & {
620
+ export type ResumeSessionConfig = Pick<SessionConfig, "clientName" | "model" | "tools" | "systemMessage" | "availableTools" | "excludedTools" | "provider" | "streaming" | "reasoningEffort" | "onPermissionRequest" | "onUserInputRequest" | "hooks" | "workingDirectory" | "configDir" | "mcpServers" | "customAgents" | "agent" | "skillDirectories" | "disabledSkills" | "infiniteSessions"> & {
608
621
  /**
609
622
  * When true, skips emitting the session.resume event.
610
623
  * Useful for reconnecting to a session without triggering resume-related side effects.