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 +652 -0
- package/dist/client.d.ts +378 -0
- package/dist/client.js +1034 -0
- package/dist/generated/session-events.d.ts +536 -0
- package/dist/generated/session-events.js +0 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/dist/sdkProtocolVersion.d.ts +10 -0
- package/dist/sdkProtocolVersion.js +8 -0
- package/dist/session.d.ts +288 -0
- package/dist/session.js +378 -0
- package/dist/types.d.ts +822 -0
- package/dist/types.js +6 -0
- package/package.json +75 -0
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
|