@github/copilot-sdk 0.1.19 → 0.1.21

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/session.js CHANGED
@@ -13,8 +13,11 @@ class CopilotSession {
13
13
  this._workspacePath = _workspacePath;
14
14
  }
15
15
  eventHandlers = /* @__PURE__ */ new Set();
16
+ typedEventHandlers = /* @__PURE__ */ new Map();
16
17
  toolHandlers = /* @__PURE__ */ new Map();
17
18
  permissionHandler;
19
+ userInputHandler;
20
+ hooks;
18
21
  /**
19
22
  * Path to the session workspace directory when infinite sessions are enabled.
20
23
  * Contains checkpoints/, plan.md, and files/ subdirectories.
@@ -111,36 +114,25 @@ class CopilotSession {
111
114
  unsubscribe();
112
115
  }
113
116
  }
114
- /**
115
- * Subscribes to events from this session.
116
- *
117
- * Events include assistant messages, tool executions, errors, and session state changes.
118
- * Multiple handlers can be registered and will all receive events.
119
- *
120
- * @param handler - A callback function that receives session events
121
- * @returns A function that, when called, unsubscribes the handler
122
- *
123
- * @example
124
- * ```typescript
125
- * const unsubscribe = session.on((event) => {
126
- * switch (event.type) {
127
- * case "assistant.message":
128
- * console.log("Assistant:", event.data.content);
129
- * break;
130
- * case "session.error":
131
- * console.error("Error:", event.data.message);
132
- * break;
133
- * }
134
- * });
135
- *
136
- * // Later, to stop receiving events:
137
- * unsubscribe();
138
- * ```
139
- */
140
- on(handler) {
141
- this.eventHandlers.add(handler);
117
+ on(eventTypeOrHandler, handler) {
118
+ if (typeof eventTypeOrHandler === "string" && handler) {
119
+ const eventType = eventTypeOrHandler;
120
+ if (!this.typedEventHandlers.has(eventType)) {
121
+ this.typedEventHandlers.set(eventType, /* @__PURE__ */ new Set());
122
+ }
123
+ const storedHandler = handler;
124
+ this.typedEventHandlers.get(eventType).add(storedHandler);
125
+ return () => {
126
+ const handlers = this.typedEventHandlers.get(eventType);
127
+ if (handlers) {
128
+ handlers.delete(storedHandler);
129
+ }
130
+ };
131
+ }
132
+ const wildcardHandler = eventTypeOrHandler;
133
+ this.eventHandlers.add(wildcardHandler);
142
134
  return () => {
143
- this.eventHandlers.delete(handler);
135
+ this.eventHandlers.delete(wildcardHandler);
144
136
  };
145
137
  }
146
138
  /**
@@ -150,6 +142,15 @@ class CopilotSession {
150
142
  * @internal This method is for internal use by the SDK.
151
143
  */
152
144
  _dispatchEvent(event) {
145
+ const typedHandlers = this.typedEventHandlers.get(event.type);
146
+ if (typedHandlers) {
147
+ for (const handler of typedHandlers) {
148
+ try {
149
+ handler(event);
150
+ } catch (_error) {
151
+ }
152
+ }
153
+ }
153
154
  for (const handler of this.eventHandlers) {
154
155
  try {
155
156
  handler(event);
@@ -197,6 +198,30 @@ class CopilotSession {
197
198
  registerPermissionHandler(handler) {
198
199
  this.permissionHandler = handler;
199
200
  }
201
+ /**
202
+ * Registers a user input handler for ask_user requests.
203
+ *
204
+ * When the agent needs input from the user (via ask_user tool),
205
+ * this handler is called to provide the response.
206
+ *
207
+ * @param handler - The user input handler function, or undefined to remove the handler
208
+ * @internal This method is typically called internally when creating a session.
209
+ */
210
+ registerUserInputHandler(handler) {
211
+ this.userInputHandler = handler;
212
+ }
213
+ /**
214
+ * Registers hook handlers for session lifecycle events.
215
+ *
216
+ * Hooks allow custom logic to be executed at various points during
217
+ * the session lifecycle (before/after tool use, session start/end, etc.).
218
+ *
219
+ * @param hooks - The hook handlers object, or undefined to remove all hooks
220
+ * @internal This method is typically called internally when creating a session.
221
+ */
222
+ registerHooks(hooks) {
223
+ this.hooks = hooks;
224
+ }
200
225
  /**
201
226
  * Handles a permission request from the Copilot CLI.
202
227
  *
@@ -217,6 +242,57 @@ class CopilotSession {
217
242
  return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
218
243
  }
219
244
  }
245
+ /**
246
+ * Handles a user input request from the Copilot CLI.
247
+ *
248
+ * @param request - The user input request data from the CLI
249
+ * @returns A promise that resolves with the user's response
250
+ * @internal This method is for internal use by the SDK.
251
+ */
252
+ async _handleUserInputRequest(request) {
253
+ if (!this.userInputHandler) {
254
+ throw new Error("User input requested but no handler registered");
255
+ }
256
+ try {
257
+ const result = await this.userInputHandler(request, {
258
+ sessionId: this.sessionId
259
+ });
260
+ return result;
261
+ } catch (error) {
262
+ throw error;
263
+ }
264
+ }
265
+ /**
266
+ * Handles a hooks invocation from the Copilot CLI.
267
+ *
268
+ * @param hookType - The type of hook being invoked
269
+ * @param input - The input data for the hook
270
+ * @returns A promise that resolves with the hook output, or undefined
271
+ * @internal This method is for internal use by the SDK.
272
+ */
273
+ async _handleHooksInvoke(hookType, input) {
274
+ if (!this.hooks) {
275
+ return void 0;
276
+ }
277
+ const handlerMap = {
278
+ preToolUse: this.hooks.onPreToolUse,
279
+ postToolUse: this.hooks.onPostToolUse,
280
+ userPromptSubmitted: this.hooks.onUserPromptSubmitted,
281
+ sessionStart: this.hooks.onSessionStart,
282
+ sessionEnd: this.hooks.onSessionEnd,
283
+ errorOccurred: this.hooks.onErrorOccurred
284
+ };
285
+ const handler = handlerMap[hookType];
286
+ if (!handler) {
287
+ return void 0;
288
+ }
289
+ try {
290
+ const result = await handler(input, { sessionId: this.sessionId });
291
+ return result;
292
+ } catch (_error) {
293
+ return void 0;
294
+ }
295
+ }
220
296
  /**
221
297
  * Retrieves all events and messages from this session's history.
222
298
  *
@@ -263,6 +339,7 @@ class CopilotSession {
263
339
  sessionId: this.sessionId
264
340
  });
265
341
  this.eventHandlers.clear();
342
+ this.typedEventHandlers.clear();
266
343
  this.toolHandlers.clear();
267
344
  this.permissionHandler = void 0;
268
345
  }
package/dist/types.d.ts CHANGED
@@ -58,6 +58,19 @@ export interface CopilotClientOptions {
58
58
  * Environment variables to pass to the CLI process. If not set, inherits process.env.
59
59
  */
60
60
  env?: Record<string, string | undefined>;
61
+ /**
62
+ * GitHub token to use for authentication.
63
+ * When provided, the token is passed to the CLI server via environment variable.
64
+ * This takes priority over other authentication methods.
65
+ */
66
+ githubToken?: string;
67
+ /**
68
+ * Whether to use the logged-in user for authentication.
69
+ * When true, the CLI server will attempt to use stored OAuth tokens or gh CLI auth.
70
+ * When false, only explicit tokens (githubToken or environment variables) are used.
71
+ * @default true (but defaults to false when githubToken is provided)
72
+ */
73
+ useLoggedInUser?: boolean;
61
74
  }
62
75
  /**
63
76
  * Configuration for creating a session
@@ -166,6 +179,209 @@ export interface PermissionRequestResult {
166
179
  export type PermissionHandler = (request: PermissionRequest, invocation: {
167
180
  sessionId: string;
168
181
  }) => Promise<PermissionRequestResult> | PermissionRequestResult;
182
+ /**
183
+ * Request for user input from the agent (enables ask_user tool)
184
+ */
185
+ export interface UserInputRequest {
186
+ /**
187
+ * The question to ask the user
188
+ */
189
+ question: string;
190
+ /**
191
+ * Optional choices for multiple choice questions
192
+ */
193
+ choices?: string[];
194
+ /**
195
+ * Whether to allow freeform text input in addition to choices
196
+ * @default true
197
+ */
198
+ allowFreeform?: boolean;
199
+ }
200
+ /**
201
+ * Response to a user input request
202
+ */
203
+ export interface UserInputResponse {
204
+ /**
205
+ * The user's answer
206
+ */
207
+ answer: string;
208
+ /**
209
+ * Whether the answer was freeform (not from choices)
210
+ */
211
+ wasFreeform: boolean;
212
+ }
213
+ /**
214
+ * Handler for user input requests from the agent
215
+ */
216
+ export type UserInputHandler = (request: UserInputRequest, invocation: {
217
+ sessionId: string;
218
+ }) => Promise<UserInputResponse> | UserInputResponse;
219
+ /**
220
+ * Base interface for all hook inputs
221
+ */
222
+ export interface BaseHookInput {
223
+ timestamp: number;
224
+ cwd: string;
225
+ }
226
+ /**
227
+ * Input for pre-tool-use hook
228
+ */
229
+ export interface PreToolUseHookInput extends BaseHookInput {
230
+ toolName: string;
231
+ toolArgs: unknown;
232
+ }
233
+ /**
234
+ * Output for pre-tool-use hook
235
+ */
236
+ export interface PreToolUseHookOutput {
237
+ permissionDecision?: "allow" | "deny" | "ask";
238
+ permissionDecisionReason?: string;
239
+ modifiedArgs?: unknown;
240
+ additionalContext?: string;
241
+ suppressOutput?: boolean;
242
+ }
243
+ /**
244
+ * Handler for pre-tool-use hook
245
+ */
246
+ export type PreToolUseHandler = (input: PreToolUseHookInput, invocation: {
247
+ sessionId: string;
248
+ }) => Promise<PreToolUseHookOutput | void> | PreToolUseHookOutput | void;
249
+ /**
250
+ * Input for post-tool-use hook
251
+ */
252
+ export interface PostToolUseHookInput extends BaseHookInput {
253
+ toolName: string;
254
+ toolArgs: unknown;
255
+ toolResult: ToolResultObject;
256
+ }
257
+ /**
258
+ * Output for post-tool-use hook
259
+ */
260
+ export interface PostToolUseHookOutput {
261
+ modifiedResult?: ToolResultObject;
262
+ additionalContext?: string;
263
+ suppressOutput?: boolean;
264
+ }
265
+ /**
266
+ * Handler for post-tool-use hook
267
+ */
268
+ export type PostToolUseHandler = (input: PostToolUseHookInput, invocation: {
269
+ sessionId: string;
270
+ }) => Promise<PostToolUseHookOutput | void> | PostToolUseHookOutput | void;
271
+ /**
272
+ * Input for user-prompt-submitted hook
273
+ */
274
+ export interface UserPromptSubmittedHookInput extends BaseHookInput {
275
+ prompt: string;
276
+ }
277
+ /**
278
+ * Output for user-prompt-submitted hook
279
+ */
280
+ export interface UserPromptSubmittedHookOutput {
281
+ modifiedPrompt?: string;
282
+ additionalContext?: string;
283
+ suppressOutput?: boolean;
284
+ }
285
+ /**
286
+ * Handler for user-prompt-submitted hook
287
+ */
288
+ export type UserPromptSubmittedHandler = (input: UserPromptSubmittedHookInput, invocation: {
289
+ sessionId: string;
290
+ }) => Promise<UserPromptSubmittedHookOutput | void> | UserPromptSubmittedHookOutput | void;
291
+ /**
292
+ * Input for session-start hook
293
+ */
294
+ export interface SessionStartHookInput extends BaseHookInput {
295
+ source: "startup" | "resume" | "new";
296
+ initialPrompt?: string;
297
+ }
298
+ /**
299
+ * Output for session-start hook
300
+ */
301
+ export interface SessionStartHookOutput {
302
+ additionalContext?: string;
303
+ modifiedConfig?: Record<string, unknown>;
304
+ }
305
+ /**
306
+ * Handler for session-start hook
307
+ */
308
+ export type SessionStartHandler = (input: SessionStartHookInput, invocation: {
309
+ sessionId: string;
310
+ }) => Promise<SessionStartHookOutput | void> | SessionStartHookOutput | void;
311
+ /**
312
+ * Input for session-end hook
313
+ */
314
+ export interface SessionEndHookInput extends BaseHookInput {
315
+ reason: "complete" | "error" | "abort" | "timeout" | "user_exit";
316
+ finalMessage?: string;
317
+ error?: string;
318
+ }
319
+ /**
320
+ * Output for session-end hook
321
+ */
322
+ export interface SessionEndHookOutput {
323
+ suppressOutput?: boolean;
324
+ cleanupActions?: string[];
325
+ sessionSummary?: string;
326
+ }
327
+ /**
328
+ * Handler for session-end hook
329
+ */
330
+ export type SessionEndHandler = (input: SessionEndHookInput, invocation: {
331
+ sessionId: string;
332
+ }) => Promise<SessionEndHookOutput | void> | SessionEndHookOutput | void;
333
+ /**
334
+ * Input for error-occurred hook
335
+ */
336
+ export interface ErrorOccurredHookInput extends BaseHookInput {
337
+ error: string;
338
+ errorContext: "model_call" | "tool_execution" | "system" | "user_input";
339
+ recoverable: boolean;
340
+ }
341
+ /**
342
+ * Output for error-occurred hook
343
+ */
344
+ export interface ErrorOccurredHookOutput {
345
+ suppressOutput?: boolean;
346
+ errorHandling?: "retry" | "skip" | "abort";
347
+ retryCount?: number;
348
+ userNotification?: string;
349
+ }
350
+ /**
351
+ * Handler for error-occurred hook
352
+ */
353
+ export type ErrorOccurredHandler = (input: ErrorOccurredHookInput, invocation: {
354
+ sessionId: string;
355
+ }) => Promise<ErrorOccurredHookOutput | void> | ErrorOccurredHookOutput | void;
356
+ /**
357
+ * Configuration for session hooks
358
+ */
359
+ export interface SessionHooks {
360
+ /**
361
+ * Called before a tool is executed
362
+ */
363
+ onPreToolUse?: PreToolUseHandler;
364
+ /**
365
+ * Called after a tool is executed
366
+ */
367
+ onPostToolUse?: PostToolUseHandler;
368
+ /**
369
+ * Called when the user submits a prompt
370
+ */
371
+ onUserPromptSubmitted?: UserPromptSubmittedHandler;
372
+ /**
373
+ * Called when a session starts
374
+ */
375
+ onSessionStart?: SessionStartHandler;
376
+ /**
377
+ * Called when a session ends
378
+ */
379
+ onSessionEnd?: SessionEndHandler;
380
+ /**
381
+ * Called when an error occurs
382
+ */
383
+ onErrorOccurred?: ErrorOccurredHandler;
384
+ }
169
385
  /**
170
386
  * Base interface for MCP server configuration.
171
387
  */
@@ -274,6 +490,10 @@ export interface InfiniteSessionConfig {
274
490
  */
275
491
  bufferExhaustionThreshold?: number;
276
492
  }
493
+ /**
494
+ * Valid reasoning effort levels for models that support it.
495
+ */
496
+ export type ReasoningEffort = "low" | "medium" | "high" | "xhigh";
277
497
  export interface SessionConfig {
278
498
  /**
279
499
  * Optional custom session ID
@@ -284,6 +504,12 @@ export interface SessionConfig {
284
504
  * Model to use for this session
285
505
  */
286
506
  model?: string;
507
+ /**
508
+ * Reasoning effort level for models that support it.
509
+ * Only valid for models where capabilities.supports.reasoningEffort is true.
510
+ * Use client.listModels() to check supported values for each model.
511
+ */
512
+ reasoningEffort?: ReasoningEffort;
287
513
  /**
288
514
  * Override the default configuration directory location.
289
515
  * When specified, the session will use this directory for storing config and state.
@@ -318,6 +544,21 @@ export interface SessionConfig {
318
544
  * When provided, the server will call this handler to request permission for operations.
319
545
  */
320
546
  onPermissionRequest?: PermissionHandler;
547
+ /**
548
+ * Handler for user input requests from the agent.
549
+ * When provided, enables the ask_user tool allowing the agent to ask questions.
550
+ */
551
+ onUserInputRequest?: UserInputHandler;
552
+ /**
553
+ * Hook handlers for intercepting session lifecycle events.
554
+ * When provided, enables hooks callback allowing custom logic at various points.
555
+ */
556
+ hooks?: SessionHooks;
557
+ /**
558
+ * Working directory for the session.
559
+ * Tool operations will be relative to this directory.
560
+ */
561
+ workingDirectory?: string;
321
562
  streaming?: boolean;
322
563
  /**
323
564
  * MCP server configurations for the session.
@@ -346,7 +587,14 @@ export interface SessionConfig {
346
587
  /**
347
588
  * Configuration for resuming a session
348
589
  */
349
- export type ResumeSessionConfig = Pick<SessionConfig, "tools" | "provider" | "streaming" | "onPermissionRequest" | "mcpServers" | "customAgents" | "skillDirectories" | "disabledSkills">;
590
+ export type ResumeSessionConfig = Pick<SessionConfig, "tools" | "provider" | "streaming" | "reasoningEffort" | "onPermissionRequest" | "onUserInputRequest" | "hooks" | "workingDirectory" | "mcpServers" | "customAgents" | "skillDirectories" | "disabledSkills"> & {
591
+ /**
592
+ * When true, skips emitting the session.resume event.
593
+ * Useful for reconnecting to a session without triggering resume-related side effects.
594
+ * @default false
595
+ */
596
+ disableResume?: boolean;
597
+ };
350
598
  /**
351
599
  * Configuration for a custom API provider.
352
600
  */
@@ -407,7 +655,21 @@ export interface MessageOptions {
407
655
  mode?: "enqueue" | "immediate";
408
656
  }
409
657
  /**
410
- * Event handler callback type
658
+ * All possible event type strings from SessionEvent
659
+ */
660
+ export type SessionEventType = SessionEvent["type"];
661
+ /**
662
+ * Extract the specific event payload for a given event type
663
+ */
664
+ export type SessionEventPayload<T extends SessionEventType> = Extract<SessionEvent, {
665
+ type: T;
666
+ }>;
667
+ /**
668
+ * Event handler for a specific event type
669
+ */
670
+ export type TypedSessionEventHandler<T extends SessionEventType> = (event: SessionEventPayload<T>) => void;
671
+ /**
672
+ * Event handler callback type (for all events)
411
673
  */
412
674
  export type SessionEventHandler = (event: SessionEvent) => void;
413
675
  /**
@@ -454,6 +716,8 @@ export interface GetAuthStatusResponse {
454
716
  export interface ModelCapabilities {
455
717
  supports: {
456
718
  vision: boolean;
719
+ /** Whether this model supports reasoning effort configuration */
720
+ reasoningEffort: boolean;
457
721
  };
458
722
  limits: {
459
723
  max_prompt_tokens?: number;
@@ -492,5 +756,48 @@ export interface ModelInfo {
492
756
  policy?: ModelPolicy;
493
757
  /** Billing information */
494
758
  billing?: ModelBilling;
759
+ /** Supported reasoning effort levels (only present if model supports reasoning effort) */
760
+ supportedReasoningEfforts?: ReasoningEffort[];
761
+ /** Default reasoning effort level (only present if model supports reasoning effort) */
762
+ defaultReasoningEffort?: ReasoningEffort;
763
+ }
764
+ /**
765
+ * Types of session lifecycle events
766
+ */
767
+ export type SessionLifecycleEventType = "session.created" | "session.deleted" | "session.updated" | "session.foreground" | "session.background";
768
+ /**
769
+ * Session lifecycle event notification
770
+ * Sent when sessions are created, deleted, updated, or change foreground/background state
771
+ */
772
+ export interface SessionLifecycleEvent {
773
+ /** Type of lifecycle event */
774
+ type: SessionLifecycleEventType;
775
+ /** ID of the session this event relates to */
776
+ sessionId: string;
777
+ /** Session metadata (not included for deleted sessions) */
778
+ metadata?: {
779
+ startTime: string;
780
+ modifiedTime: string;
781
+ summary?: string;
782
+ };
783
+ }
784
+ /**
785
+ * Handler for session lifecycle events
786
+ */
787
+ export type SessionLifecycleHandler = (event: SessionLifecycleEvent) => void;
788
+ /**
789
+ * Typed handler for specific session lifecycle event types
790
+ */
791
+ export type TypedSessionLifecycleHandler<K extends SessionLifecycleEventType> = (event: SessionLifecycleEvent & {
792
+ type: K;
793
+ }) => void;
794
+ /**
795
+ * Information about the foreground session in TUI+server mode
796
+ */
797
+ export interface ForegroundSessionInfo {
798
+ /** ID of the foreground session, or undefined if none */
799
+ sessionId?: string;
800
+ /** Workspace path of the foreground session */
801
+ workspacePath?: string;
495
802
  }
496
803
  export {};
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.19",
7
+ "version": "0.1.21",
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,15 +40,15 @@
40
40
  "author": "GitHub",
41
41
  "license": "MIT",
42
42
  "dependencies": {
43
- "@github/copilot": "^0.0.394",
43
+ "@github/copilot": "^0.0.402",
44
44
  "vscode-jsonrpc": "^8.2.1",
45
45
  "zod": "^4.3.5"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/node": "^22.19.6",
49
- "@typescript-eslint/eslint-plugin": "^8.0.0",
50
- "@typescript-eslint/parser": "^8.0.0",
51
- "esbuild": "^0.27.0",
48
+ "@types/node": "^25.2.0",
49
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
50
+ "@typescript-eslint/parser": "^8.54.0",
51
+ "esbuild": "^0.27.2",
52
52
  "eslint": "^9.0.0",
53
53
  "glob": "^11.0.0",
54
54
  "json-schema": "^0.4.0",
@@ -59,7 +59,7 @@
59
59
  "semver": "^7.7.3",
60
60
  "tsx": "^4.20.6",
61
61
  "typescript": "^5.0.0",
62
- "vitest": "^4.0.16"
62
+ "vitest": "^4.0.18"
63
63
  },
64
64
  "engines": {
65
65
  "node": ">=18.0.0"