copilot-sdk-supercharged 1.0.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 ADDED
@@ -0,0 +1,652 @@
1
+ # Copilot SDK for Node.js/TypeScript
2
+
3
+ TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC.
4
+
5
+ > **Note:** This SDK is in technical preview and may change in breaking ways.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @github/copilot-sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { CopilotClient } from "@github/copilot-sdk";
17
+
18
+ // Create and start client
19
+ const client = new CopilotClient();
20
+ await client.start();
21
+
22
+ // Create a session
23
+ const session = await client.createSession({
24
+ model: "gpt-5",
25
+ });
26
+
27
+ // Wait for response using typed event handlers
28
+ const done = new Promise<void>((resolve) => {
29
+ session.on("assistant.message", (event) => {
30
+ console.log(event.data.content);
31
+ });
32
+ session.on("session.idle", () => {
33
+ resolve();
34
+ });
35
+ });
36
+
37
+ // Send a message and wait for completion
38
+ await session.send({ prompt: "What is 2+2?" });
39
+ await done;
40
+
41
+ // Clean up
42
+ await session.destroy();
43
+ await client.stop();
44
+ ```
45
+
46
+ ## API Reference
47
+
48
+ ### CopilotClient
49
+
50
+ #### Constructor
51
+
52
+ ```typescript
53
+ new CopilotClient(options?: CopilotClientOptions)
54
+ ```
55
+
56
+ **Options:**
57
+
58
+ - `cliPath?: string` - Path to CLI executable (default: "copilot" from PATH)
59
+ - `cliArgs?: string[]` - Extra arguments prepended before SDK-managed flags (e.g. `["./dist-cli/index.js"]` when using `node`)
60
+ - `cliUrl?: string` - URL of existing CLI server to connect to (e.g., `"localhost:8080"`, `"http://127.0.0.1:9000"`, or just `"8080"`). When provided, the client will not spawn a CLI process.
61
+ - `port?: number` - Server port (default: 0 for random)
62
+ - `useStdio?: boolean` - Use stdio transport instead of TCP (default: true)
63
+ - `logLevel?: string` - Log level (default: "info")
64
+ - `autoStart?: boolean` - Auto-start server (default: true)
65
+ - `autoRestart?: boolean` - Auto-restart on crash (default: true)
66
+ - `githubToken?: string` - GitHub token for authentication. When provided, takes priority over other auth methods.
67
+ - `useLoggedInUser?: boolean` - Whether to use logged-in user for authentication (default: true, but false when `githubToken` is provided). Cannot be used with `cliUrl`.
68
+
69
+ #### Methods
70
+
71
+ ##### `start(): Promise<void>`
72
+
73
+ Start the CLI server and establish connection.
74
+
75
+ ##### `stop(): Promise<Error[]>`
76
+
77
+ Stop the server and close all sessions. Returns a list of any errors encountered during cleanup.
78
+
79
+ ##### `forceStop(): Promise<void>`
80
+
81
+ Force stop the CLI server without graceful cleanup. Use when `stop()` takes too long.
82
+
83
+ ##### `createSession(config?: SessionConfig): Promise<CopilotSession>`
84
+
85
+ Create a new conversation session.
86
+
87
+ **Config:**
88
+
89
+ - `sessionId?: string` - Custom session ID.
90
+ - `model?: string` - Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.**
91
+ - `reasoningEffort?: "low" | "medium" | "high" | "xhigh"` - Reasoning effort level for models that support it. Use `listModels()` to check which models support this option.
92
+ - `tools?: Tool[]` - Custom tools exposed to the CLI
93
+ - `systemMessage?: SystemMessageConfig` - System message customization (see below)
94
+ - `infiniteSessions?: InfiniteSessionConfig` - Configure automatic context compaction (see below)
95
+ - `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section.
96
+ - `onUserInputRequest?: UserInputHandler` - Handler for user input requests from the agent. Enables the `ask_user` tool. See [User Input Requests](#user-input-requests) section.
97
+ - `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section.
98
+
99
+ ##### `resumeSession(sessionId: string, config?: ResumeSessionConfig): Promise<CopilotSession>`
100
+
101
+ Resume an existing session. Returns the session with `workspacePath` populated if infinite sessions were enabled.
102
+
103
+ ##### `ping(message?: string): Promise<{ message: string; timestamp: number }>`
104
+
105
+ Ping the server to check connectivity.
106
+
107
+ ##### `getState(): ConnectionState`
108
+
109
+ Get current connection state.
110
+
111
+ ##### `listSessions(): Promise<SessionMetadata[]>`
112
+
113
+ List all available sessions.
114
+
115
+ ##### `deleteSession(sessionId: string): Promise<void>`
116
+
117
+ Delete a session and its data from disk.
118
+
119
+ ##### `getForegroundSessionId(): Promise<string | undefined>`
120
+
121
+ Get the ID of the session currently displayed in the TUI. Only available when connecting to a server running in TUI+server mode (`--ui-server`).
122
+
123
+ ##### `setForegroundSessionId(sessionId: string): Promise<void>`
124
+
125
+ Request the TUI to switch to displaying the specified session. Only available in TUI+server mode.
126
+
127
+ ##### `on(eventType: SessionLifecycleEventType, handler): () => void`
128
+
129
+ Subscribe to a specific session lifecycle event type. Returns an unsubscribe function.
130
+
131
+ ```typescript
132
+ const unsubscribe = client.on("session.foreground", (event) => {
133
+ console.log(`Session ${event.sessionId} is now in foreground`);
134
+ });
135
+ ```
136
+
137
+ ##### `on(handler: SessionLifecycleHandler): () => void`
138
+
139
+ Subscribe to all session lifecycle events. Returns an unsubscribe function.
140
+
141
+ ```typescript
142
+ const unsubscribe = client.on((event) => {
143
+ console.log(`${event.type}: ${event.sessionId}`);
144
+ });
145
+ ```
146
+
147
+ **Lifecycle Event Types:**
148
+ - `session.created` - A new session was created
149
+ - `session.deleted` - A session was deleted
150
+ - `session.updated` - A session was updated (e.g., new messages)
151
+ - `session.foreground` - A session became the foreground session in TUI
152
+ - `session.background` - A session is no longer the foreground session
153
+
154
+ ---
155
+
156
+ ### CopilotSession
157
+
158
+ Represents a single conversation session.
159
+
160
+ #### Properties
161
+
162
+ ##### `sessionId: string`
163
+
164
+ The unique identifier for this session.
165
+
166
+ ##### `workspacePath?: string`
167
+
168
+ Path to the session workspace directory when infinite sessions are enabled. Contains `checkpoints/`, `plan.md`, and `files/` subdirectories. Undefined if infinite sessions are disabled.
169
+
170
+ #### Methods
171
+
172
+ ##### `send(options: MessageOptions): Promise<string>`
173
+
174
+ Send a message to the session. Returns immediately after the message is queued; use event handlers or `sendAndWait()` to wait for completion.
175
+
176
+ **Options:**
177
+
178
+ - `prompt: string` - The message/prompt to send
179
+ - `attachments?: Array<{type, path, displayName}>` - File attachments
180
+ - `mode?: "enqueue" | "immediate"` - Delivery mode
181
+
182
+ Returns the message ID.
183
+
184
+ ##### `sendAndWait(options: MessageOptions, timeout?: number): Promise<AssistantMessageEvent | undefined>`
185
+
186
+ Send a message and wait until the session becomes idle.
187
+
188
+ **Options:**
189
+
190
+ - `prompt: string` - The message/prompt to send
191
+ - `attachments?: Array<{type, path, displayName}>` - File attachments
192
+ - `mode?: "enqueue" | "immediate"` - Delivery mode
193
+ - `timeout?: number` - Optional timeout in milliseconds
194
+
195
+ Returns the final assistant message event, or undefined if none was received.
196
+
197
+ ##### `on(eventType: string, handler: TypedSessionEventHandler): () => void`
198
+
199
+ Subscribe to a specific event type. The handler receives properly typed events.
200
+
201
+ ```typescript
202
+ // Listen for specific event types with full type inference
203
+ session.on("assistant.message", (event) => {
204
+ console.log(event.data.content); // TypeScript knows about event.data.content
205
+ });
206
+
207
+ session.on("session.idle", () => {
208
+ console.log("Session is idle");
209
+ });
210
+
211
+ // Listen to streaming events
212
+ session.on("assistant.message_delta", (event) => {
213
+ process.stdout.write(event.data.deltaContent);
214
+ });
215
+ ```
216
+
217
+ ##### `on(handler: SessionEventHandler): () => void`
218
+
219
+ Subscribe to all session events. Returns an unsubscribe function.
220
+
221
+ ```typescript
222
+ const unsubscribe = session.on((event) => {
223
+ // Handle any event type
224
+ console.log(event.type, event);
225
+ });
226
+
227
+ // Later...
228
+ unsubscribe();
229
+ ```
230
+
231
+ ##### `abort(): Promise<void>`
232
+
233
+ Abort the currently processing message in this session.
234
+
235
+ ##### `getMessages(): Promise<SessionEvent[]>`
236
+
237
+ Get all events/messages from this session.
238
+
239
+ ##### `destroy(): Promise<void>`
240
+
241
+ Destroy the session and free resources.
242
+
243
+ ---
244
+
245
+ ## Event Types
246
+
247
+ Sessions emit various events during processing:
248
+
249
+ - `user.message` - User message added
250
+ - `assistant.message` - Assistant response
251
+ - `assistant.message_delta` - Streaming response chunk
252
+ - `tool.execution_start` - Tool execution started
253
+ - `tool.execution_complete` - Tool execution completed
254
+ - And more...
255
+
256
+ See `SessionEvent` type in the source for full details.
257
+
258
+ ## Image Support
259
+
260
+ The SDK supports image attachments via the `attachments` parameter. You can attach images by providing their file path:
261
+
262
+ ```typescript
263
+ await session.send({
264
+ prompt: "What's in this image?",
265
+ attachments: [
266
+ {
267
+ type: "file",
268
+ path: "/path/to/image.jpg",
269
+ },
270
+ ],
271
+ });
272
+ ```
273
+
274
+ Supported image formats include JPG, PNG, GIF, and other common image types. The agent's `view` tool can also read images directly from the filesystem, so you can also ask questions like:
275
+
276
+ ```typescript
277
+ await session.send({ prompt: "What does the most recent jpg in this directory portray?" });
278
+ ```
279
+
280
+ ## Streaming
281
+
282
+ Enable streaming to receive assistant response chunks as they're generated:
283
+
284
+ ```typescript
285
+ const session = await client.createSession({
286
+ model: "gpt-5",
287
+ streaming: true,
288
+ });
289
+
290
+ // Wait for completion using typed event handlers
291
+ const done = new Promise<void>((resolve) => {
292
+ session.on("assistant.message_delta", (event) => {
293
+ // Streaming message chunk - print incrementally
294
+ process.stdout.write(event.data.deltaContent);
295
+ });
296
+
297
+ session.on("assistant.reasoning_delta", (event) => {
298
+ // Streaming reasoning chunk (if model supports reasoning)
299
+ process.stdout.write(event.data.deltaContent);
300
+ });
301
+
302
+ session.on("assistant.message", (event) => {
303
+ // Final message - complete content
304
+ console.log("\n--- Final message ---");
305
+ console.log(event.data.content);
306
+ });
307
+
308
+ session.on("assistant.reasoning", (event) => {
309
+ // Final reasoning content (if model supports reasoning)
310
+ console.log("--- Reasoning ---");
311
+ console.log(event.data.content);
312
+ });
313
+
314
+ session.on("session.idle", () => {
315
+ // Session finished processing
316
+ resolve();
317
+ });
318
+ });
319
+
320
+ await session.send({ prompt: "Tell me a short story" });
321
+ await done; // Wait for streaming to complete
322
+ ```
323
+
324
+ When `streaming: true`:
325
+
326
+ - `assistant.message_delta` events are sent with `deltaContent` containing incremental text
327
+ - `assistant.reasoning_delta` events are sent with `deltaContent` for reasoning/chain-of-thought (model-dependent)
328
+ - Accumulate `deltaContent` values to build the full response progressively
329
+ - The final `assistant.message` and `assistant.reasoning` events contain the complete content
330
+
331
+ Note: `assistant.message` and `assistant.reasoning` (final events) are always sent regardless of streaming setting.
332
+
333
+ ## Advanced Usage
334
+
335
+ ### Manual Server Control
336
+
337
+ ```typescript
338
+ const client = new CopilotClient({ autoStart: false });
339
+
340
+ // Start manually
341
+ await client.start();
342
+
343
+ // Use client...
344
+
345
+ // Stop manually
346
+ await client.stop();
347
+ ```
348
+
349
+ ### Tools
350
+
351
+ You can let the CLI call back into your process when the model needs capabilities you own. Use `defineTool` with Zod schemas for type-safe tool definitions:
352
+
353
+ ```ts
354
+ import { z } from "zod";
355
+ import { CopilotClient, defineTool } from "@github/copilot-sdk";
356
+
357
+ const session = await client.createSession({
358
+ model: "gpt-5",
359
+ tools: [
360
+ defineTool("lookup_issue", {
361
+ description: "Fetch issue details from our tracker",
362
+ parameters: z.object({
363
+ id: z.string().describe("Issue identifier"),
364
+ }),
365
+ handler: async ({ id }) => {
366
+ const issue = await fetchIssue(id);
367
+ return issue;
368
+ },
369
+ }),
370
+ ],
371
+ });
372
+ ```
373
+
374
+ When Copilot invokes `lookup_issue`, the client automatically runs your handler and responds to the CLI. Handlers can return any JSON-serializable value (automatically wrapped), a simple string, or a `ToolResultObject` for full control over result metadata. Raw JSON schemas are also supported if Zod isn't desired.
375
+
376
+ ### System Message Customization
377
+
378
+ Control the system prompt using `systemMessage` in session config:
379
+
380
+ ```typescript
381
+ const session = await client.createSession({
382
+ model: "gpt-5",
383
+ systemMessage: {
384
+ content: `
385
+ <workflow_rules>
386
+ - Always check for security vulnerabilities
387
+ - Suggest performance improvements when applicable
388
+ </workflow_rules>
389
+ `,
390
+ },
391
+ });
392
+ ```
393
+
394
+ The SDK auto-injects environment context, tool instructions, and security guardrails. The default CLI persona is preserved, and your `content` is appended after SDK-managed sections. To change the persona or fully redefine the prompt, use `mode: "replace"`.
395
+
396
+ For full control (removes all guardrails), use `mode: "replace"`:
397
+
398
+ ```typescript
399
+ const session = await client.createSession({
400
+ model: "gpt-5",
401
+ systemMessage: {
402
+ mode: "replace",
403
+ content: "You are a helpful assistant.",
404
+ },
405
+ });
406
+ ```
407
+
408
+ ### Infinite Sessions
409
+
410
+ By default, sessions use **infinite sessions** which automatically manage context window limits through background compaction and persist state to a workspace directory.
411
+
412
+ ```typescript
413
+ // Default: infinite sessions enabled with default thresholds
414
+ const session = await client.createSession({ model: "gpt-5" });
415
+
416
+ // Access the workspace path for checkpoints and files
417
+ console.log(session.workspacePath);
418
+ // => ~/.copilot/session-state/{sessionId}/
419
+
420
+ // Custom thresholds
421
+ const session = await client.createSession({
422
+ model: "gpt-5",
423
+ infiniteSessions: {
424
+ enabled: true,
425
+ backgroundCompactionThreshold: 0.80, // Start compacting at 80% context usage
426
+ bufferExhaustionThreshold: 0.95, // Block at 95% until compaction completes
427
+ },
428
+ });
429
+
430
+ // Disable infinite sessions
431
+ const session = await client.createSession({
432
+ model: "gpt-5",
433
+ infiniteSessions: { enabled: false },
434
+ });
435
+ ```
436
+
437
+ When enabled, sessions emit compaction events:
438
+
439
+ - `session.compaction_start` - Background compaction started
440
+ - `session.compaction_complete` - Compaction finished (includes token counts)
441
+
442
+ ### Multiple Sessions
443
+
444
+ ```typescript
445
+ const session1 = await client.createSession({ model: "gpt-5" });
446
+ const session2 = await client.createSession({ model: "claude-sonnet-4.5" });
447
+
448
+ // Both sessions are independent
449
+ await session1.sendAndWait({ prompt: "Hello from session 1" });
450
+ await session2.sendAndWait({ prompt: "Hello from session 2" });
451
+ ```
452
+
453
+ ### Custom Session IDs
454
+
455
+ ```typescript
456
+ const session = await client.createSession({
457
+ sessionId: "my-custom-session-id",
458
+ model: "gpt-5",
459
+ });
460
+ ```
461
+
462
+ ### File Attachments
463
+
464
+ ```typescript
465
+ await session.send({
466
+ prompt: "Analyze this file",
467
+ attachments: [
468
+ {
469
+ type: "file",
470
+ path: "/path/to/file.js",
471
+ displayName: "My File",
472
+ },
473
+ ],
474
+ });
475
+ ```
476
+
477
+ ### Custom Providers
478
+
479
+ The SDK supports custom OpenAI-compatible API providers (BYOK - Bring Your Own Key), including local providers like Ollama. When using a custom provider, you must specify the `model` explicitly.
480
+
481
+ **ProviderConfig:**
482
+
483
+ - `type?: "openai" | "azure" | "anthropic"` - Provider type (default: "openai")
484
+ - `baseUrl: string` - API endpoint URL (required)
485
+ - `apiKey?: string` - API key (optional for local providers like Ollama)
486
+ - `bearerToken?: string` - Bearer token for authentication (takes precedence over apiKey)
487
+ - `wireApi?: "completions" | "responses"` - API format for OpenAI/Azure (default: "completions")
488
+ - `azure?.apiVersion?: string` - Azure API version (default: "2024-10-21")
489
+
490
+ **Example with Ollama:**
491
+
492
+ ```typescript
493
+ const session = await client.createSession({
494
+ model: "deepseek-coder-v2:16b", // Required when using custom provider
495
+ provider: {
496
+ type: "openai",
497
+ baseUrl: "http://localhost:11434/v1", // Ollama endpoint
498
+ // apiKey not required for Ollama
499
+ },
500
+ });
501
+
502
+ await session.sendAndWait({ prompt: "Hello!" });
503
+ ```
504
+
505
+ **Example with custom OpenAI-compatible API:**
506
+
507
+ ```typescript
508
+ const session = await client.createSession({
509
+ model: "gpt-4",
510
+ provider: {
511
+ type: "openai",
512
+ baseUrl: "https://my-api.example.com/v1",
513
+ apiKey: process.env.MY_API_KEY,
514
+ },
515
+ });
516
+ ```
517
+
518
+ **Example with Azure OpenAI:**
519
+
520
+ ```typescript
521
+ const session = await client.createSession({
522
+ model: "gpt-4",
523
+ provider: {
524
+ type: "azure", // Must be "azure" for Azure endpoints, NOT "openai"
525
+ baseUrl: "https://my-resource.openai.azure.com", // Just the host, no path
526
+ apiKey: process.env.AZURE_OPENAI_KEY,
527
+ azure: {
528
+ apiVersion: "2024-10-21",
529
+ },
530
+ },
531
+ });
532
+ ```
533
+
534
+ > **Important notes:**
535
+ > - When using a custom provider, the `model` parameter is **required**. The SDK will throw an error if no model is specified.
536
+ > - For Azure OpenAI endpoints (`*.openai.azure.com`), you **must** use `type: "azure"`, not `type: "openai"`.
537
+ > - The `baseUrl` should be just the host (e.g., `https://my-resource.openai.azure.com`). Do **not** include `/openai/v1` in the URL - the SDK handles path construction automatically.
538
+
539
+ ## User Input Requests
540
+
541
+ Enable the agent to ask questions to the user using the `ask_user` tool by providing an `onUserInputRequest` handler:
542
+
543
+ ```typescript
544
+ const session = await client.createSession({
545
+ model: "gpt-5",
546
+ onUserInputRequest: async (request, invocation) => {
547
+ // request.question - The question to ask
548
+ // request.choices - Optional array of choices for multiple choice
549
+ // request.allowFreeform - Whether freeform input is allowed (default: true)
550
+
551
+ console.log(`Agent asks: ${request.question}`);
552
+ if (request.choices) {
553
+ console.log(`Choices: ${request.choices.join(", ")}`);
554
+ }
555
+
556
+ // Return the user's response
557
+ return {
558
+ answer: "User's answer here",
559
+ wasFreeform: true, // Whether the answer was freeform (not from choices)
560
+ };
561
+ },
562
+ });
563
+ ```
564
+
565
+ ## Session Hooks
566
+
567
+ Hook into session lifecycle events by providing handlers in the `hooks` configuration:
568
+
569
+ ```typescript
570
+ const session = await client.createSession({
571
+ model: "gpt-5",
572
+ hooks: {
573
+ // Called before each tool execution
574
+ onPreToolUse: async (input, invocation) => {
575
+ console.log(`About to run tool: ${input.toolName}`);
576
+ // Return permission decision and optionally modify args
577
+ return {
578
+ permissionDecision: "allow", // "allow", "deny", or "ask"
579
+ modifiedArgs: input.toolArgs, // Optionally modify tool arguments
580
+ additionalContext: "Extra context for the model",
581
+ };
582
+ },
583
+
584
+ // Called after each tool execution
585
+ onPostToolUse: async (input, invocation) => {
586
+ console.log(`Tool ${input.toolName} completed`);
587
+ // Optionally modify the result or add context
588
+ return {
589
+ additionalContext: "Post-execution notes",
590
+ };
591
+ },
592
+
593
+ // Called when user submits a prompt
594
+ onUserPromptSubmitted: async (input, invocation) => {
595
+ console.log(`User prompt: ${input.prompt}`);
596
+ return {
597
+ modifiedPrompt: input.prompt, // Optionally modify the prompt
598
+ };
599
+ },
600
+
601
+ // Called when session starts
602
+ onSessionStart: async (input, invocation) => {
603
+ console.log(`Session started from: ${input.source}`); // "startup", "resume", "new"
604
+ return {
605
+ additionalContext: "Session initialization context",
606
+ };
607
+ },
608
+
609
+ // Called when session ends
610
+ onSessionEnd: async (input, invocation) => {
611
+ console.log(`Session ended: ${input.reason}`);
612
+ },
613
+
614
+ // Called when an error occurs
615
+ onErrorOccurred: async (input, invocation) => {
616
+ console.error(`Error in ${input.errorContext}: ${input.error}`);
617
+ return {
618
+ errorHandling: "retry", // "retry", "skip", or "abort"
619
+ };
620
+ },
621
+ },
622
+ });
623
+ ```
624
+
625
+ **Available hooks:**
626
+
627
+ - `onPreToolUse` - Intercept tool calls before execution. Can allow/deny or modify arguments.
628
+ - `onPostToolUse` - Process tool results after execution. Can modify results or add context.
629
+ - `onUserPromptSubmitted` - Intercept user prompts. Can modify the prompt before processing.
630
+ - `onSessionStart` - Run logic when a session starts or resumes.
631
+ - `onSessionEnd` - Cleanup or logging when session ends.
632
+ - `onErrorOccurred` - Handle errors with retry/skip/abort strategies.
633
+
634
+ ## Error Handling
635
+
636
+ ```typescript
637
+ try {
638
+ const session = await client.createSession();
639
+ await session.send({ prompt: "Hello" });
640
+ } catch (error) {
641
+ console.error("Error:", error.message);
642
+ }
643
+ ```
644
+
645
+ ## Requirements
646
+
647
+ - Node.js >= 18.0.0
648
+ - GitHub Copilot CLI installed and in PATH (or provide custom `cliPath`)
649
+
650
+ ## License
651
+
652
+ MIT