@github/copilot-sdk 0.2.0 → 0.2.1-preview.1
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 +124 -28
- package/dist/cjs/client.js +17 -4
- package/dist/cjs/session.js +158 -0
- package/dist/client.js +17 -4
- package/dist/generated/session-events.d.ts +102 -0
- package/dist/index.d.ts +1 -1
- package/dist/session.d.ts +48 -1
- package/dist/session.js +158 -0
- package/dist/types.d.ts +188 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -60,7 +60,10 @@ await client.stop();
|
|
|
60
60
|
Sessions also support `Symbol.asyncDispose` for use with [`await using`](https://github.com/tc39/proposal-explicit-resource-management) (TypeScript 5.2+/Node.js 18.0+):
|
|
61
61
|
|
|
62
62
|
```typescript
|
|
63
|
-
await using session = await client.createSession({
|
|
63
|
+
await using session = await client.createSession({
|
|
64
|
+
model: "gpt-5",
|
|
65
|
+
onPermissionRequest: approveAll,
|
|
66
|
+
});
|
|
64
67
|
// session is automatically disconnected when leaving scope
|
|
65
68
|
```
|
|
66
69
|
|
|
@@ -76,7 +79,7 @@ new CopilotClient(options?: CopilotClientOptions)
|
|
|
76
79
|
|
|
77
80
|
**Options:**
|
|
78
81
|
|
|
79
|
-
- `cliPath?: string` - Path to CLI executable (default:
|
|
82
|
+
- `cliPath?: string` - Path to CLI executable (default: uses COPILOT_CLI_PATH env var or bundled instance)
|
|
80
83
|
- `cliArgs?: string[]` - Extra arguments prepended before SDK-managed flags (e.g. `["./dist-cli/index.js"]` when using `node`)
|
|
81
84
|
- `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.
|
|
82
85
|
- `port?: number` - Server port (default: 0 for random)
|
|
@@ -184,6 +187,7 @@ const unsubscribe = client.on((event) => {
|
|
|
184
187
|
```
|
|
185
188
|
|
|
186
189
|
**Lifecycle Event Types:**
|
|
190
|
+
|
|
187
191
|
- `session.created` - A new session was created
|
|
188
192
|
- `session.deleted` - A session was deleted
|
|
189
193
|
- `session.updated` - A session was updated (e.g., new messages)
|
|
@@ -279,7 +283,21 @@ Get all events/messages from this session.
|
|
|
279
283
|
|
|
280
284
|
Disconnect the session and free resources. Session data on disk is preserved for later resumption.
|
|
281
285
|
|
|
282
|
-
##### `
|
|
286
|
+
##### `capabilities: SessionCapabilities`
|
|
287
|
+
|
|
288
|
+
Host capabilities reported when the session was created or resumed. Use this to check feature support before calling capability-gated APIs.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
if (session.capabilities.ui?.elicitation) {
|
|
292
|
+
const ok = await session.ui.confirm("Deploy?");
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
##### `ui: SessionUiApi`
|
|
297
|
+
|
|
298
|
+
Interactive UI methods for showing dialogs to the user. Only available when the CLI host supports elicitation (`session.capabilities.ui?.elicitation === true`). See [UI Elicitation](#ui-elicitation) for full details.
|
|
299
|
+
|
|
300
|
+
##### `destroy(): Promise<void>` _(deprecated)_
|
|
283
301
|
|
|
284
302
|
Deprecated — use `disconnect()` instead.
|
|
285
303
|
|
|
@@ -294,6 +312,8 @@ Sessions emit various events during processing:
|
|
|
294
312
|
- `assistant.message_delta` - Streaming response chunk
|
|
295
313
|
- `tool.execution_start` - Tool execution started
|
|
296
314
|
- `tool.execution_complete` - Tool execution completed
|
|
315
|
+
- `command.execute` - Command dispatch request (handled internally by the SDK)
|
|
316
|
+
- `commands.changed` - Command registration changed
|
|
297
317
|
- And more...
|
|
298
318
|
|
|
299
319
|
See `SessionEvent` type in the source for full details.
|
|
@@ -438,8 +458,10 @@ defineTool("edit_file", {
|
|
|
438
458
|
description: "Custom file editor with project-specific validation",
|
|
439
459
|
parameters: z.object({ path: z.string(), content: z.string() }),
|
|
440
460
|
overridesBuiltInTool: true,
|
|
441
|
-
handler: async ({ path, content }) => {
|
|
442
|
-
|
|
461
|
+
handler: async ({ path, content }) => {
|
|
462
|
+
/* your logic */
|
|
463
|
+
},
|
|
464
|
+
});
|
|
443
465
|
```
|
|
444
466
|
|
|
445
467
|
#### Skipping Permission Prompts
|
|
@@ -451,10 +473,78 @@ defineTool("safe_lookup", {
|
|
|
451
473
|
description: "A read-only lookup that needs no confirmation",
|
|
452
474
|
parameters: z.object({ id: z.string() }),
|
|
453
475
|
skipPermission: true,
|
|
454
|
-
handler: async ({ id }) => {
|
|
455
|
-
|
|
476
|
+
handler: async ({ id }) => {
|
|
477
|
+
/* your logic */
|
|
478
|
+
},
|
|
479
|
+
});
|
|
456
480
|
```
|
|
457
481
|
|
|
482
|
+
### Commands
|
|
483
|
+
|
|
484
|
+
Register slash commands so that users of the CLI's TUI can invoke custom actions via `/commandName`. Each command has a `name`, optional `description`, and a `handler` called when the user executes it.
|
|
485
|
+
|
|
486
|
+
```ts
|
|
487
|
+
const session = await client.createSession({
|
|
488
|
+
onPermissionRequest: approveAll,
|
|
489
|
+
commands: [
|
|
490
|
+
{
|
|
491
|
+
name: "deploy",
|
|
492
|
+
description: "Deploy the app to production",
|
|
493
|
+
handler: async ({ commandName, args }) => {
|
|
494
|
+
console.log(`Deploying with args: ${args}`);
|
|
495
|
+
// Do work here — any thrown error is reported back to the CLI
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
});
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
When the user types `/deploy staging` in the CLI, the SDK receives a `command.execute` event, routes it to your handler, and automatically responds to the CLI. If the handler throws, the error message is forwarded.
|
|
503
|
+
|
|
504
|
+
Commands are sent to the CLI on both `createSession` and `resumeSession`, so you can update the command set when resuming.
|
|
505
|
+
|
|
506
|
+
### UI Elicitation
|
|
507
|
+
|
|
508
|
+
When the CLI is running with a TUI (not in headless mode), the SDK can request interactive form dialogs from the user. The `session.ui` object provides convenience methods built on a single generic `elicitation` RPC.
|
|
509
|
+
|
|
510
|
+
> **Capability check:** Elicitation is only available when the host advertises support. Always check `session.capabilities.ui?.elicitation` before calling UI methods.
|
|
511
|
+
|
|
512
|
+
```ts
|
|
513
|
+
const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
514
|
+
|
|
515
|
+
if (session.capabilities.ui?.elicitation) {
|
|
516
|
+
// Confirm dialog — returns boolean
|
|
517
|
+
const ok = await session.ui.confirm("Deploy to production?");
|
|
518
|
+
|
|
519
|
+
// Selection dialog — returns selected value or null
|
|
520
|
+
const env = await session.ui.select("Pick environment", ["production", "staging", "dev"]);
|
|
521
|
+
|
|
522
|
+
// Text input — returns string or null
|
|
523
|
+
const name = await session.ui.input("Project name:", {
|
|
524
|
+
title: "Name",
|
|
525
|
+
minLength: 1,
|
|
526
|
+
maxLength: 50,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Generic elicitation with full schema control
|
|
530
|
+
const result = await session.ui.elicitation({
|
|
531
|
+
message: "Configure deployment",
|
|
532
|
+
requestedSchema: {
|
|
533
|
+
type: "object",
|
|
534
|
+
properties: {
|
|
535
|
+
region: { type: "string", enum: ["us-east", "eu-west"] },
|
|
536
|
+
dryRun: { type: "boolean", default: true },
|
|
537
|
+
},
|
|
538
|
+
required: ["region"],
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
// result.action: "accept" | "decline" | "cancel"
|
|
542
|
+
// result.content: { region: "us-east", dryRun: true } (when accepted)
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
All UI methods throw if elicitation is not supported by the host.
|
|
547
|
+
|
|
458
548
|
### System Message Customization
|
|
459
549
|
|
|
460
550
|
Control the system prompt using `systemMessage` in session config:
|
|
@@ -489,7 +579,10 @@ const session = await client.createSession({
|
|
|
489
579
|
mode: "customize",
|
|
490
580
|
sections: {
|
|
491
581
|
// Replace the tone/style section
|
|
492
|
-
tone: {
|
|
582
|
+
tone: {
|
|
583
|
+
action: "replace",
|
|
584
|
+
content: "Respond in a warm, professional tone. Be thorough in explanations.",
|
|
585
|
+
},
|
|
493
586
|
// Remove coding-specific rules
|
|
494
587
|
code_change_rules: { action: "remove" },
|
|
495
588
|
// Append to existing guidelines
|
|
@@ -504,6 +597,7 @@ const session = await client.createSession({
|
|
|
504
597
|
Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `last_instructions`. Use the `SYSTEM_PROMPT_SECTIONS` constant for descriptions of each section.
|
|
505
598
|
|
|
506
599
|
Each section override supports four actions:
|
|
600
|
+
|
|
507
601
|
- **`replace`** — Replace the section content entirely
|
|
508
602
|
- **`remove`** — Remove the section from the prompt
|
|
509
603
|
- **`append`** — Add content after the existing section
|
|
@@ -542,7 +636,7 @@ const session = await client.createSession({
|
|
|
542
636
|
model: "gpt-5",
|
|
543
637
|
infiniteSessions: {
|
|
544
638
|
enabled: true,
|
|
545
|
-
backgroundCompactionThreshold: 0.
|
|
639
|
+
backgroundCompactionThreshold: 0.8, // Start compacting at 80% context usage
|
|
546
640
|
bufferExhaustionThreshold: 0.95, // Block at 95% until compaction completes
|
|
547
641
|
},
|
|
548
642
|
});
|
|
@@ -641,8 +735,8 @@ const session = await client.createSession({
|
|
|
641
735
|
const session = await client.createSession({
|
|
642
736
|
model: "gpt-4",
|
|
643
737
|
provider: {
|
|
644
|
-
type: "azure",
|
|
645
|
-
baseUrl: "https://my-resource.openai.azure.com",
|
|
738
|
+
type: "azure", // Must be "azure" for Azure endpoints, NOT "openai"
|
|
739
|
+
baseUrl: "https://my-resource.openai.azure.com", // Just the host, no path
|
|
646
740
|
apiKey: process.env.AZURE_OPENAI_KEY,
|
|
647
741
|
azure: {
|
|
648
742
|
apiVersion: "2024-10-21",
|
|
@@ -652,6 +746,7 @@ const session = await client.createSession({
|
|
|
652
746
|
```
|
|
653
747
|
|
|
654
748
|
> **Important notes:**
|
|
749
|
+
>
|
|
655
750
|
> - When using a custom provider, the `model` parameter is **required**. The SDK will throw an error if no model is specified.
|
|
656
751
|
> - For Azure OpenAI endpoints (`*.openai.azure.com`), you **must** use `type: "azure"`, not `type: "openai"`.
|
|
657
752
|
> - 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.
|
|
@@ -662,9 +757,9 @@ The SDK supports OpenTelemetry for distributed tracing. Provide a `telemetry` co
|
|
|
662
757
|
|
|
663
758
|
```typescript
|
|
664
759
|
const client = new CopilotClient({
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
760
|
+
telemetry: {
|
|
761
|
+
otlpEndpoint: "http://localhost:4318",
|
|
762
|
+
},
|
|
668
763
|
});
|
|
669
764
|
```
|
|
670
765
|
|
|
@@ -690,12 +785,12 @@ If you're already using `@opentelemetry/api` in your app and want this linkage,
|
|
|
690
785
|
import { propagation, context } from "@opentelemetry/api";
|
|
691
786
|
|
|
692
787
|
const client = new CopilotClient({
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
788
|
+
telemetry: { otlpEndpoint: "http://localhost:4318" },
|
|
789
|
+
onGetTraceContext: () => {
|
|
790
|
+
const carrier: Record<string, string> = {};
|
|
791
|
+
propagation.inject(context.active(), carrier);
|
|
792
|
+
return carrier;
|
|
793
|
+
},
|
|
699
794
|
});
|
|
700
795
|
```
|
|
701
796
|
|
|
@@ -755,14 +850,15 @@ const session = await client.createSession({
|
|
|
755
850
|
|
|
756
851
|
### Permission Result Kinds
|
|
757
852
|
|
|
758
|
-
| Kind
|
|
759
|
-
|
|
760
|
-
| `"approved"`
|
|
761
|
-
| `"denied-interactively-by-user"`
|
|
762
|
-
| `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked
|
|
763
|
-
| `"denied-by-rules"`
|
|
764
|
-
| `"denied-by-content-exclusion-policy"`
|
|
765
|
-
| `"no-result"`
|
|
853
|
+
| Kind | Meaning |
|
|
854
|
+
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
855
|
+
| `"approved"` | Allow the tool to run |
|
|
856
|
+
| `"denied-interactively-by-user"` | User explicitly denied the request |
|
|
857
|
+
| `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked |
|
|
858
|
+
| `"denied-by-rules"` | Denied by a policy rule |
|
|
859
|
+
| `"denied-by-content-exclusion-policy"` | Denied due to a content exclusion policy |
|
|
860
|
+
| `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) |
|
|
861
|
+
|
|
766
862
|
### Resuming Sessions
|
|
767
863
|
|
|
768
864
|
Pass `onPermissionRequest` when resuming a session too — it is required:
|
package/dist/cjs/client.js
CHANGED
|
@@ -175,8 +175,9 @@ class CopilotClient {
|
|
|
175
175
|
}
|
|
176
176
|
this.onListModels = options.onListModels;
|
|
177
177
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
178
|
+
const effectiveEnv = options.env ?? process.env;
|
|
178
179
|
this.options = {
|
|
179
|
-
cliPath: options.cliUrl ? void 0 : options.cliPath || getBundledCliPath(),
|
|
180
|
+
cliPath: options.cliUrl ? void 0 : options.cliPath || effectiveEnv.COPILOT_CLI_PATH || getBundledCliPath(),
|
|
180
181
|
cliArgs: options.cliArgs ?? [],
|
|
181
182
|
cwd: options.cwd ?? process.cwd(),
|
|
182
183
|
port: options.port || 0,
|
|
@@ -187,7 +188,7 @@ class CopilotClient {
|
|
|
187
188
|
logLevel: options.logLevel || "debug",
|
|
188
189
|
autoStart: options.autoStart ?? true,
|
|
189
190
|
autoRestart: false,
|
|
190
|
-
env:
|
|
191
|
+
env: effectiveEnv,
|
|
191
192
|
githubToken: options.githubToken,
|
|
192
193
|
// Default useLoggedInUser to false when githubToken is provided, otherwise true
|
|
193
194
|
useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true),
|
|
@@ -451,6 +452,7 @@ class CopilotClient {
|
|
|
451
452
|
this.onGetTraceContext
|
|
452
453
|
);
|
|
453
454
|
session.registerTools(config.tools);
|
|
455
|
+
session.registerCommands(config.commands);
|
|
454
456
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
455
457
|
if (config.onUserInputRequest) {
|
|
456
458
|
session.registerUserInputHandler(config.onUserInputRequest);
|
|
@@ -482,6 +484,10 @@ class CopilotClient {
|
|
|
482
484
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
483
485
|
skipPermission: tool.skipPermission
|
|
484
486
|
})),
|
|
487
|
+
commands: config.commands?.map((cmd) => ({
|
|
488
|
+
name: cmd.name,
|
|
489
|
+
description: cmd.description
|
|
490
|
+
})),
|
|
485
491
|
systemMessage: wireSystemMessage,
|
|
486
492
|
availableTools: config.availableTools,
|
|
487
493
|
excludedTools: config.excludedTools,
|
|
@@ -500,8 +506,9 @@ class CopilotClient {
|
|
|
500
506
|
disabledSkills: config.disabledSkills,
|
|
501
507
|
infiniteSessions: config.infiniteSessions
|
|
502
508
|
});
|
|
503
|
-
const { workspacePath } = response;
|
|
509
|
+
const { workspacePath, capabilities } = response;
|
|
504
510
|
session["_workspacePath"] = workspacePath;
|
|
511
|
+
session.setCapabilities(capabilities);
|
|
505
512
|
} catch (e) {
|
|
506
513
|
this.sessions.delete(sessionId);
|
|
507
514
|
throw e;
|
|
@@ -552,6 +559,7 @@ class CopilotClient {
|
|
|
552
559
|
this.onGetTraceContext
|
|
553
560
|
);
|
|
554
561
|
session.registerTools(config.tools);
|
|
562
|
+
session.registerCommands(config.commands);
|
|
555
563
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
556
564
|
if (config.onUserInputRequest) {
|
|
557
565
|
session.registerUserInputHandler(config.onUserInputRequest);
|
|
@@ -586,6 +594,10 @@ class CopilotClient {
|
|
|
586
594
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
587
595
|
skipPermission: tool.skipPermission
|
|
588
596
|
})),
|
|
597
|
+
commands: config.commands?.map((cmd) => ({
|
|
598
|
+
name: cmd.name,
|
|
599
|
+
description: cmd.description
|
|
600
|
+
})),
|
|
589
601
|
provider: config.provider,
|
|
590
602
|
requestPermission: true,
|
|
591
603
|
requestUserInput: !!config.onUserInputRequest,
|
|
@@ -602,8 +614,9 @@ class CopilotClient {
|
|
|
602
614
|
infiniteSessions: config.infiniteSessions,
|
|
603
615
|
disableResume: config.disableResume
|
|
604
616
|
});
|
|
605
|
-
const { workspacePath } = response;
|
|
617
|
+
const { workspacePath, capabilities } = response;
|
|
606
618
|
session["_workspacePath"] = workspacePath;
|
|
619
|
+
session.setCapabilities(capabilities);
|
|
607
620
|
} catch (e) {
|
|
608
621
|
this.sessions.delete(sessionId);
|
|
609
622
|
throw e;
|
package/dist/cjs/session.js
CHANGED
|
@@ -45,12 +45,14 @@ class CopilotSession {
|
|
|
45
45
|
eventHandlers = /* @__PURE__ */ new Set();
|
|
46
46
|
typedEventHandlers = /* @__PURE__ */ new Map();
|
|
47
47
|
toolHandlers = /* @__PURE__ */ new Map();
|
|
48
|
+
commandHandlers = /* @__PURE__ */ new Map();
|
|
48
49
|
permissionHandler;
|
|
49
50
|
userInputHandler;
|
|
50
51
|
hooks;
|
|
51
52
|
transformCallbacks;
|
|
52
53
|
_rpc = null;
|
|
53
54
|
traceContextProvider;
|
|
55
|
+
_capabilities = {};
|
|
54
56
|
/**
|
|
55
57
|
* Typed session-scoped RPC methods.
|
|
56
58
|
*/
|
|
@@ -68,6 +70,33 @@ class CopilotSession {
|
|
|
68
70
|
get workspacePath() {
|
|
69
71
|
return this._workspacePath;
|
|
70
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Host capabilities reported when the session was created or resumed.
|
|
75
|
+
* Use this to check feature support before calling capability-gated APIs.
|
|
76
|
+
*/
|
|
77
|
+
get capabilities() {
|
|
78
|
+
return this._capabilities;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Interactive UI methods for showing dialogs to the user.
|
|
82
|
+
* Only available when the CLI host supports elicitation
|
|
83
|
+
* (`session.capabilities.ui?.elicitation === true`).
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* if (session.capabilities.ui?.elicitation) {
|
|
88
|
+
* const ok = await session.ui.confirm("Deploy to production?");
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
get ui() {
|
|
93
|
+
return {
|
|
94
|
+
elicitation: (params) => this._elicitation(params),
|
|
95
|
+
confirm: (message) => this._confirm(message),
|
|
96
|
+
select: (message, options) => this._select(message, options),
|
|
97
|
+
input: (message, options) => this._input(message, options)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
71
100
|
/**
|
|
72
101
|
* Sends a message to this session and waits for the response.
|
|
73
102
|
*
|
|
@@ -237,6 +266,9 @@ class CopilotSession {
|
|
|
237
266
|
if (this.permissionHandler) {
|
|
238
267
|
void this._executePermissionAndRespond(requestId, permissionRequest);
|
|
239
268
|
}
|
|
269
|
+
} else if (event.type === "command.execute") {
|
|
270
|
+
const { requestId, commandName, command, args } = event.data;
|
|
271
|
+
void this._executeCommandAndRespond(requestId, commandName, command, args);
|
|
240
272
|
}
|
|
241
273
|
}
|
|
242
274
|
/**
|
|
@@ -301,6 +333,39 @@ class CopilotSession {
|
|
|
301
333
|
}
|
|
302
334
|
}
|
|
303
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Executes a command handler and sends the result back via RPC.
|
|
338
|
+
* @internal
|
|
339
|
+
*/
|
|
340
|
+
async _executeCommandAndRespond(requestId, commandName, command, args) {
|
|
341
|
+
const handler = this.commandHandlers.get(commandName);
|
|
342
|
+
if (!handler) {
|
|
343
|
+
try {
|
|
344
|
+
await this.rpc.commands.handlePendingCommand({
|
|
345
|
+
requestId,
|
|
346
|
+
error: `Unknown command: ${commandName}`
|
|
347
|
+
});
|
|
348
|
+
} catch (rpcError) {
|
|
349
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
350
|
+
throw rpcError;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
await handler({ sessionId: this.sessionId, command, commandName, args });
|
|
357
|
+
await this.rpc.commands.handlePendingCommand({ requestId });
|
|
358
|
+
} catch (error) {
|
|
359
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
360
|
+
try {
|
|
361
|
+
await this.rpc.commands.handlePendingCommand({ requestId, error: message });
|
|
362
|
+
} catch (rpcError) {
|
|
363
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
364
|
+
throw rpcError;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
304
369
|
/**
|
|
305
370
|
* Registers custom tool handlers for this session.
|
|
306
371
|
*
|
|
@@ -329,6 +394,99 @@ class CopilotSession {
|
|
|
329
394
|
getToolHandler(name) {
|
|
330
395
|
return this.toolHandlers.get(name);
|
|
331
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Registers command handlers for this session.
|
|
399
|
+
*
|
|
400
|
+
* @param commands - An array of command definitions with handlers, or undefined to clear
|
|
401
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
402
|
+
*/
|
|
403
|
+
registerCommands(commands) {
|
|
404
|
+
this.commandHandlers.clear();
|
|
405
|
+
if (!commands) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
for (const cmd of commands) {
|
|
409
|
+
this.commandHandlers.set(cmd.name, cmd.handler);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Sets the host capabilities for this session.
|
|
414
|
+
*
|
|
415
|
+
* @param capabilities - The capabilities object from the create/resume response
|
|
416
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
417
|
+
*/
|
|
418
|
+
setCapabilities(capabilities) {
|
|
419
|
+
this._capabilities = capabilities ?? {};
|
|
420
|
+
}
|
|
421
|
+
assertElicitation() {
|
|
422
|
+
if (!this._capabilities.ui?.elicitation) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
"Elicitation is not supported by the host. Check session.capabilities.ui?.elicitation before calling UI methods."
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async _elicitation(params) {
|
|
429
|
+
this.assertElicitation();
|
|
430
|
+
return this.rpc.ui.elicitation({
|
|
431
|
+
message: params.message,
|
|
432
|
+
requestedSchema: params.requestedSchema
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
async _confirm(message) {
|
|
436
|
+
this.assertElicitation();
|
|
437
|
+
const result = await this.rpc.ui.elicitation({
|
|
438
|
+
message,
|
|
439
|
+
requestedSchema: {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {
|
|
442
|
+
confirmed: { type: "boolean", default: true }
|
|
443
|
+
},
|
|
444
|
+
required: ["confirmed"]
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
return result.action === "accept" && result.content?.confirmed === true;
|
|
448
|
+
}
|
|
449
|
+
async _select(message, options) {
|
|
450
|
+
this.assertElicitation();
|
|
451
|
+
const result = await this.rpc.ui.elicitation({
|
|
452
|
+
message,
|
|
453
|
+
requestedSchema: {
|
|
454
|
+
type: "object",
|
|
455
|
+
properties: {
|
|
456
|
+
selection: { type: "string", enum: options }
|
|
457
|
+
},
|
|
458
|
+
required: ["selection"]
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
if (result.action === "accept" && result.content?.selection != null) {
|
|
462
|
+
return result.content.selection;
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
async _input(message, options) {
|
|
467
|
+
this.assertElicitation();
|
|
468
|
+
const field = { type: "string" };
|
|
469
|
+
if (options?.title) field.title = options.title;
|
|
470
|
+
if (options?.description) field.description = options.description;
|
|
471
|
+
if (options?.minLength != null) field.minLength = options.minLength;
|
|
472
|
+
if (options?.maxLength != null) field.maxLength = options.maxLength;
|
|
473
|
+
if (options?.format) field.format = options.format;
|
|
474
|
+
if (options?.default != null) field.default = options.default;
|
|
475
|
+
const result = await this.rpc.ui.elicitation({
|
|
476
|
+
message,
|
|
477
|
+
requestedSchema: {
|
|
478
|
+
type: "object",
|
|
479
|
+
properties: {
|
|
480
|
+
value: field
|
|
481
|
+
},
|
|
482
|
+
required: ["value"]
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
if (result.action === "accept" && result.content?.value != null) {
|
|
486
|
+
return result.content.value;
|
|
487
|
+
}
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
332
490
|
/**
|
|
333
491
|
* Registers a handler for permission requests.
|
|
334
492
|
*
|
package/dist/client.js
CHANGED
|
@@ -155,8 +155,9 @@ class CopilotClient {
|
|
|
155
155
|
}
|
|
156
156
|
this.onListModels = options.onListModels;
|
|
157
157
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
158
|
+
const effectiveEnv = options.env ?? process.env;
|
|
158
159
|
this.options = {
|
|
159
|
-
cliPath: options.cliUrl ? void 0 : options.cliPath || getBundledCliPath(),
|
|
160
|
+
cliPath: options.cliUrl ? void 0 : options.cliPath || effectiveEnv.COPILOT_CLI_PATH || getBundledCliPath(),
|
|
160
161
|
cliArgs: options.cliArgs ?? [],
|
|
161
162
|
cwd: options.cwd ?? process.cwd(),
|
|
162
163
|
port: options.port || 0,
|
|
@@ -167,7 +168,7 @@ class CopilotClient {
|
|
|
167
168
|
logLevel: options.logLevel || "debug",
|
|
168
169
|
autoStart: options.autoStart ?? true,
|
|
169
170
|
autoRestart: false,
|
|
170
|
-
env:
|
|
171
|
+
env: effectiveEnv,
|
|
171
172
|
githubToken: options.githubToken,
|
|
172
173
|
// Default useLoggedInUser to false when githubToken is provided, otherwise true
|
|
173
174
|
useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true),
|
|
@@ -431,6 +432,7 @@ class CopilotClient {
|
|
|
431
432
|
this.onGetTraceContext
|
|
432
433
|
);
|
|
433
434
|
session.registerTools(config.tools);
|
|
435
|
+
session.registerCommands(config.commands);
|
|
434
436
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
435
437
|
if (config.onUserInputRequest) {
|
|
436
438
|
session.registerUserInputHandler(config.onUserInputRequest);
|
|
@@ -462,6 +464,10 @@ class CopilotClient {
|
|
|
462
464
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
463
465
|
skipPermission: tool.skipPermission
|
|
464
466
|
})),
|
|
467
|
+
commands: config.commands?.map((cmd) => ({
|
|
468
|
+
name: cmd.name,
|
|
469
|
+
description: cmd.description
|
|
470
|
+
})),
|
|
465
471
|
systemMessage: wireSystemMessage,
|
|
466
472
|
availableTools: config.availableTools,
|
|
467
473
|
excludedTools: config.excludedTools,
|
|
@@ -480,8 +486,9 @@ class CopilotClient {
|
|
|
480
486
|
disabledSkills: config.disabledSkills,
|
|
481
487
|
infiniteSessions: config.infiniteSessions
|
|
482
488
|
});
|
|
483
|
-
const { workspacePath } = response;
|
|
489
|
+
const { workspacePath, capabilities } = response;
|
|
484
490
|
session["_workspacePath"] = workspacePath;
|
|
491
|
+
session.setCapabilities(capabilities);
|
|
485
492
|
} catch (e) {
|
|
486
493
|
this.sessions.delete(sessionId);
|
|
487
494
|
throw e;
|
|
@@ -532,6 +539,7 @@ class CopilotClient {
|
|
|
532
539
|
this.onGetTraceContext
|
|
533
540
|
);
|
|
534
541
|
session.registerTools(config.tools);
|
|
542
|
+
session.registerCommands(config.commands);
|
|
535
543
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
536
544
|
if (config.onUserInputRequest) {
|
|
537
545
|
session.registerUserInputHandler(config.onUserInputRequest);
|
|
@@ -566,6 +574,10 @@ class CopilotClient {
|
|
|
566
574
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
567
575
|
skipPermission: tool.skipPermission
|
|
568
576
|
})),
|
|
577
|
+
commands: config.commands?.map((cmd) => ({
|
|
578
|
+
name: cmd.name,
|
|
579
|
+
description: cmd.description
|
|
580
|
+
})),
|
|
569
581
|
provider: config.provider,
|
|
570
582
|
requestPermission: true,
|
|
571
583
|
requestUserInput: !!config.onUserInputRequest,
|
|
@@ -582,8 +594,9 @@ class CopilotClient {
|
|
|
582
594
|
infiniteSessions: config.infiniteSessions,
|
|
583
595
|
disableResume: config.disableResume
|
|
584
596
|
});
|
|
585
|
-
const { workspacePath } = response;
|
|
597
|
+
const { workspacePath, capabilities } = response;
|
|
586
598
|
session["_workspacePath"] = workspacePath;
|
|
599
|
+
session.setCapabilities(capabilities);
|
|
587
600
|
} catch (e) {
|
|
588
601
|
this.sessions.delete(sessionId);
|
|
589
602
|
throw e;
|
|
@@ -89,6 +89,10 @@ export type SessionEvent = {
|
|
|
89
89
|
* Whether the session was already in use by another client at start time
|
|
90
90
|
*/
|
|
91
91
|
alreadyInUse?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Whether this session supports remote steering via Mission Control
|
|
94
|
+
*/
|
|
95
|
+
steerable?: boolean;
|
|
92
96
|
};
|
|
93
97
|
} | {
|
|
94
98
|
/**
|
|
@@ -2070,6 +2074,10 @@ export type SessionEvent = {
|
|
|
2070
2074
|
* Version of the plugin this skill originated from, when applicable
|
|
2071
2075
|
*/
|
|
2072
2076
|
pluginVersion?: string;
|
|
2077
|
+
/**
|
|
2078
|
+
* Description of the skill from its SKILL.md frontmatter
|
|
2079
|
+
*/
|
|
2080
|
+
description?: string;
|
|
2073
2081
|
};
|
|
2074
2082
|
} | {
|
|
2075
2083
|
/**
|
|
@@ -2144,6 +2152,22 @@ export type SessionEvent = {
|
|
|
2144
2152
|
* Human-readable display name of the sub-agent
|
|
2145
2153
|
*/
|
|
2146
2154
|
agentDisplayName: string;
|
|
2155
|
+
/**
|
|
2156
|
+
* Model used by the sub-agent
|
|
2157
|
+
*/
|
|
2158
|
+
model?: string;
|
|
2159
|
+
/**
|
|
2160
|
+
* Total number of tool calls made by the sub-agent
|
|
2161
|
+
*/
|
|
2162
|
+
totalToolCalls?: number;
|
|
2163
|
+
/**
|
|
2164
|
+
* Total tokens (input + output) consumed by the sub-agent
|
|
2165
|
+
*/
|
|
2166
|
+
totalTokens?: number;
|
|
2167
|
+
/**
|
|
2168
|
+
* Wall-clock duration of the sub-agent execution in milliseconds
|
|
2169
|
+
*/
|
|
2170
|
+
durationMs?: number;
|
|
2147
2171
|
};
|
|
2148
2172
|
} | {
|
|
2149
2173
|
/**
|
|
@@ -2183,6 +2207,22 @@ export type SessionEvent = {
|
|
|
2183
2207
|
* Error message describing why the sub-agent failed
|
|
2184
2208
|
*/
|
|
2185
2209
|
error: string;
|
|
2210
|
+
/**
|
|
2211
|
+
* Model used by the sub-agent (if any model calls succeeded before failure)
|
|
2212
|
+
*/
|
|
2213
|
+
model?: string;
|
|
2214
|
+
/**
|
|
2215
|
+
* Total number of tool calls made before the sub-agent failed
|
|
2216
|
+
*/
|
|
2217
|
+
totalToolCalls?: number;
|
|
2218
|
+
/**
|
|
2219
|
+
* Total tokens (input + output) consumed before the sub-agent failed
|
|
2220
|
+
*/
|
|
2221
|
+
totalTokens?: number;
|
|
2222
|
+
/**
|
|
2223
|
+
* Wall-clock duration of the sub-agent execution in milliseconds
|
|
2224
|
+
*/
|
|
2225
|
+
durationMs?: number;
|
|
2186
2226
|
};
|
|
2187
2227
|
} | {
|
|
2188
2228
|
/**
|
|
@@ -3300,6 +3340,68 @@ export type SessionEvent = {
|
|
|
3300
3340
|
path?: string;
|
|
3301
3341
|
}[];
|
|
3302
3342
|
};
|
|
3343
|
+
} | {
|
|
3344
|
+
/**
|
|
3345
|
+
* Unique event identifier (UUID v4), generated when the event is emitted
|
|
3346
|
+
*/
|
|
3347
|
+
id: string;
|
|
3348
|
+
/**
|
|
3349
|
+
* ISO 8601 timestamp when the event was created
|
|
3350
|
+
*/
|
|
3351
|
+
timestamp: string;
|
|
3352
|
+
/**
|
|
3353
|
+
* ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event.
|
|
3354
|
+
*/
|
|
3355
|
+
parentId: string | null;
|
|
3356
|
+
ephemeral: true;
|
|
3357
|
+
type: "session.custom_agents_updated";
|
|
3358
|
+
data: {
|
|
3359
|
+
/**
|
|
3360
|
+
* Array of loaded custom agent metadata
|
|
3361
|
+
*/
|
|
3362
|
+
agents: {
|
|
3363
|
+
/**
|
|
3364
|
+
* Unique identifier for the agent
|
|
3365
|
+
*/
|
|
3366
|
+
id: string;
|
|
3367
|
+
/**
|
|
3368
|
+
* Internal name of the agent
|
|
3369
|
+
*/
|
|
3370
|
+
name: string;
|
|
3371
|
+
/**
|
|
3372
|
+
* Human-readable display name
|
|
3373
|
+
*/
|
|
3374
|
+
displayName: string;
|
|
3375
|
+
/**
|
|
3376
|
+
* Description of what the agent does
|
|
3377
|
+
*/
|
|
3378
|
+
description: string;
|
|
3379
|
+
/**
|
|
3380
|
+
* Source location: user, project, inherited, remote, or plugin
|
|
3381
|
+
*/
|
|
3382
|
+
source: string;
|
|
3383
|
+
/**
|
|
3384
|
+
* List of tool names available to this agent
|
|
3385
|
+
*/
|
|
3386
|
+
tools: string[];
|
|
3387
|
+
/**
|
|
3388
|
+
* Whether the agent can be selected by the user
|
|
3389
|
+
*/
|
|
3390
|
+
userInvocable: boolean;
|
|
3391
|
+
/**
|
|
3392
|
+
* Model override for this agent, if set
|
|
3393
|
+
*/
|
|
3394
|
+
model?: string;
|
|
3395
|
+
}[];
|
|
3396
|
+
/**
|
|
3397
|
+
* Non-fatal warnings from agent loading
|
|
3398
|
+
*/
|
|
3399
|
+
warnings: string[];
|
|
3400
|
+
/**
|
|
3401
|
+
* Fatal errors from agent loading
|
|
3402
|
+
*/
|
|
3403
|
+
errors: string[];
|
|
3404
|
+
};
|
|
3303
3405
|
} | {
|
|
3304
3406
|
/**
|
|
3305
3407
|
* Unique event identifier (UUID v4), generated when the event is emitted
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
export { CopilotClient } from "./client.js";
|
|
7
7
|
export { CopilotSession, type AssistantMessageEvent } from "./session.js";
|
|
8
8
|
export { defineTool, approveAll, SYSTEM_PROMPT_SECTIONS } from "./types.js";
|
|
9
|
-
export type { ConnectionState, CopilotClientOptions, CustomAgentConfig, ForegroundSessionInfo, GetAuthStatusResponse, GetStatusResponse, InfiniteSessionConfig, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, MessageOptions, ModelBilling, ModelCapabilities, ModelInfo, ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, ResumeSessionConfig, SectionOverride, SectionOverrideAction, SectionTransformFn, SessionConfig, SessionEvent, SessionEventHandler, SessionEventPayload, SessionEventType, SessionLifecycleEvent, SessionLifecycleEventType, SessionLifecycleHandler, SessionContext, SessionListFilter, SessionMetadata, SystemMessageAppendConfig, SystemMessageConfig, SystemMessageCustomizeConfig, SystemMessageReplaceConfig, SystemPromptSection, TelemetryConfig, TraceContext, TraceContextProvider, Tool, ToolHandler, ToolInvocation, ToolResultObject, TypedSessionEventHandler, TypedSessionLifecycleHandler, ZodSchema, } from "./types.js";
|
|
9
|
+
export type { CommandContext, CommandDefinition, CommandHandler, ConnectionState, CopilotClientOptions, CustomAgentConfig, ElicitationFieldValue, ElicitationParams, ElicitationResult, ElicitationSchema, ElicitationSchemaField, ForegroundSessionInfo, GetAuthStatusResponse, GetStatusResponse, InfiniteSessionConfig, InputOptions, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, MessageOptions, ModelBilling, ModelCapabilities, ModelInfo, ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, ResumeSessionConfig, SectionOverride, SectionOverrideAction, SectionTransformFn, SessionCapabilities, SessionConfig, SessionEvent, SessionEventHandler, SessionEventPayload, SessionEventType, SessionLifecycleEvent, SessionLifecycleEventType, SessionLifecycleHandler, SessionContext, SessionListFilter, SessionMetadata, SessionUiApi, SystemMessageAppendConfig, SystemMessageConfig, SystemMessageCustomizeConfig, SystemMessageReplaceConfig, SystemPromptSection, TelemetryConfig, TraceContext, TraceContextProvider, Tool, ToolHandler, ToolInvocation, ToolResultObject, TypedSessionEventHandler, TypedSessionLifecycleHandler, ZodSchema, } from "./types.js";
|
package/dist/session.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { MessageConnection } from "vscode-jsonrpc/node.js";
|
|
6
6
|
import { createSessionRpc } from "./generated/rpc.js";
|
|
7
|
-
import type { MessageOptions, PermissionHandler, PermissionRequestResult, ReasoningEffort, SectionTransformFn, SessionEvent, SessionEventHandler, SessionEventType, SessionHooks, Tool, ToolHandler, TraceContextProvider, TypedSessionEventHandler, UserInputHandler, UserInputResponse } from "./types.js";
|
|
7
|
+
import type { CommandHandler, MessageOptions, PermissionHandler, PermissionRequestResult, ReasoningEffort, SectionTransformFn, SessionCapabilities, SessionEvent, SessionEventHandler, SessionEventType, SessionHooks, SessionUiApi, Tool, ToolHandler, TraceContextProvider, TypedSessionEventHandler, UserInputHandler, UserInputResponse } from "./types.js";
|
|
8
8
|
export declare const NO_RESULT_PERMISSION_V2_ERROR = "Permission handlers cannot return 'no-result' when connected to a protocol v2 server.";
|
|
9
9
|
/** Assistant message event - the final response from the assistant. */
|
|
10
10
|
export type AssistantMessageEvent = Extract<SessionEvent, {
|
|
@@ -42,12 +42,14 @@ export declare class CopilotSession {
|
|
|
42
42
|
private eventHandlers;
|
|
43
43
|
private typedEventHandlers;
|
|
44
44
|
private toolHandlers;
|
|
45
|
+
private commandHandlers;
|
|
45
46
|
private permissionHandler?;
|
|
46
47
|
private userInputHandler?;
|
|
47
48
|
private hooks?;
|
|
48
49
|
private transformCallbacks?;
|
|
49
50
|
private _rpc;
|
|
50
51
|
private traceContextProvider?;
|
|
52
|
+
private _capabilities;
|
|
51
53
|
/**
|
|
52
54
|
* Creates a new CopilotSession instance.
|
|
53
55
|
*
|
|
@@ -68,6 +70,24 @@ export declare class CopilotSession {
|
|
|
68
70
|
* Undefined if infinite sessions are disabled.
|
|
69
71
|
*/
|
|
70
72
|
get workspacePath(): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Host capabilities reported when the session was created or resumed.
|
|
75
|
+
* Use this to check feature support before calling capability-gated APIs.
|
|
76
|
+
*/
|
|
77
|
+
get capabilities(): SessionCapabilities;
|
|
78
|
+
/**
|
|
79
|
+
* Interactive UI methods for showing dialogs to the user.
|
|
80
|
+
* Only available when the CLI host supports elicitation
|
|
81
|
+
* (`session.capabilities.ui?.elicitation === true`).
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* if (session.capabilities.ui?.elicitation) {
|
|
86
|
+
* const ok = await session.ui.confirm("Deploy to production?");
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
get ui(): SessionUiApi;
|
|
71
91
|
/**
|
|
72
92
|
* Sends a message to this session and waits for the response.
|
|
73
93
|
*
|
|
@@ -182,6 +202,11 @@ export declare class CopilotSession {
|
|
|
182
202
|
* @internal
|
|
183
203
|
*/
|
|
184
204
|
private _executePermissionAndRespond;
|
|
205
|
+
/**
|
|
206
|
+
* Executes a command handler and sends the result back via RPC.
|
|
207
|
+
* @internal
|
|
208
|
+
*/
|
|
209
|
+
private _executeCommandAndRespond;
|
|
185
210
|
/**
|
|
186
211
|
* Registers custom tool handlers for this session.
|
|
187
212
|
*
|
|
@@ -200,6 +225,28 @@ export declare class CopilotSession {
|
|
|
200
225
|
* @internal This method is for internal use by the SDK.
|
|
201
226
|
*/
|
|
202
227
|
getToolHandler(name: string): ToolHandler | undefined;
|
|
228
|
+
/**
|
|
229
|
+
* Registers command handlers for this session.
|
|
230
|
+
*
|
|
231
|
+
* @param commands - An array of command definitions with handlers, or undefined to clear
|
|
232
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
233
|
+
*/
|
|
234
|
+
registerCommands(commands?: {
|
|
235
|
+
name: string;
|
|
236
|
+
handler: CommandHandler;
|
|
237
|
+
}[]): void;
|
|
238
|
+
/**
|
|
239
|
+
* Sets the host capabilities for this session.
|
|
240
|
+
*
|
|
241
|
+
* @param capabilities - The capabilities object from the create/resume response
|
|
242
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
243
|
+
*/
|
|
244
|
+
setCapabilities(capabilities?: SessionCapabilities): void;
|
|
245
|
+
private assertElicitation;
|
|
246
|
+
private _elicitation;
|
|
247
|
+
private _confirm;
|
|
248
|
+
private _select;
|
|
249
|
+
private _input;
|
|
203
250
|
/**
|
|
204
251
|
* Registers a handler for permission requests.
|
|
205
252
|
*
|
package/dist/session.js
CHANGED
|
@@ -21,12 +21,14 @@ class CopilotSession {
|
|
|
21
21
|
eventHandlers = /* @__PURE__ */ new Set();
|
|
22
22
|
typedEventHandlers = /* @__PURE__ */ new Map();
|
|
23
23
|
toolHandlers = /* @__PURE__ */ new Map();
|
|
24
|
+
commandHandlers = /* @__PURE__ */ new Map();
|
|
24
25
|
permissionHandler;
|
|
25
26
|
userInputHandler;
|
|
26
27
|
hooks;
|
|
27
28
|
transformCallbacks;
|
|
28
29
|
_rpc = null;
|
|
29
30
|
traceContextProvider;
|
|
31
|
+
_capabilities = {};
|
|
30
32
|
/**
|
|
31
33
|
* Typed session-scoped RPC methods.
|
|
32
34
|
*/
|
|
@@ -44,6 +46,33 @@ class CopilotSession {
|
|
|
44
46
|
get workspacePath() {
|
|
45
47
|
return this._workspacePath;
|
|
46
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Host capabilities reported when the session was created or resumed.
|
|
51
|
+
* Use this to check feature support before calling capability-gated APIs.
|
|
52
|
+
*/
|
|
53
|
+
get capabilities() {
|
|
54
|
+
return this._capabilities;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Interactive UI methods for showing dialogs to the user.
|
|
58
|
+
* Only available when the CLI host supports elicitation
|
|
59
|
+
* (`session.capabilities.ui?.elicitation === true`).
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* if (session.capabilities.ui?.elicitation) {
|
|
64
|
+
* const ok = await session.ui.confirm("Deploy to production?");
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
get ui() {
|
|
69
|
+
return {
|
|
70
|
+
elicitation: (params) => this._elicitation(params),
|
|
71
|
+
confirm: (message) => this._confirm(message),
|
|
72
|
+
select: (message, options) => this._select(message, options),
|
|
73
|
+
input: (message, options) => this._input(message, options)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
47
76
|
/**
|
|
48
77
|
* Sends a message to this session and waits for the response.
|
|
49
78
|
*
|
|
@@ -213,6 +242,9 @@ class CopilotSession {
|
|
|
213
242
|
if (this.permissionHandler) {
|
|
214
243
|
void this._executePermissionAndRespond(requestId, permissionRequest);
|
|
215
244
|
}
|
|
245
|
+
} else if (event.type === "command.execute") {
|
|
246
|
+
const { requestId, commandName, command, args } = event.data;
|
|
247
|
+
void this._executeCommandAndRespond(requestId, commandName, command, args);
|
|
216
248
|
}
|
|
217
249
|
}
|
|
218
250
|
/**
|
|
@@ -277,6 +309,39 @@ class CopilotSession {
|
|
|
277
309
|
}
|
|
278
310
|
}
|
|
279
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Executes a command handler and sends the result back via RPC.
|
|
314
|
+
* @internal
|
|
315
|
+
*/
|
|
316
|
+
async _executeCommandAndRespond(requestId, commandName, command, args) {
|
|
317
|
+
const handler = this.commandHandlers.get(commandName);
|
|
318
|
+
if (!handler) {
|
|
319
|
+
try {
|
|
320
|
+
await this.rpc.commands.handlePendingCommand({
|
|
321
|
+
requestId,
|
|
322
|
+
error: `Unknown command: ${commandName}`
|
|
323
|
+
});
|
|
324
|
+
} catch (rpcError) {
|
|
325
|
+
if (!(rpcError instanceof ConnectionError || rpcError instanceof ResponseError)) {
|
|
326
|
+
throw rpcError;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
await handler({ sessionId: this.sessionId, command, commandName, args });
|
|
333
|
+
await this.rpc.commands.handlePendingCommand({ requestId });
|
|
334
|
+
} catch (error) {
|
|
335
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
336
|
+
try {
|
|
337
|
+
await this.rpc.commands.handlePendingCommand({ requestId, error: message });
|
|
338
|
+
} catch (rpcError) {
|
|
339
|
+
if (!(rpcError instanceof ConnectionError || rpcError instanceof ResponseError)) {
|
|
340
|
+
throw rpcError;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
280
345
|
/**
|
|
281
346
|
* Registers custom tool handlers for this session.
|
|
282
347
|
*
|
|
@@ -305,6 +370,99 @@ class CopilotSession {
|
|
|
305
370
|
getToolHandler(name) {
|
|
306
371
|
return this.toolHandlers.get(name);
|
|
307
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Registers command handlers for this session.
|
|
375
|
+
*
|
|
376
|
+
* @param commands - An array of command definitions with handlers, or undefined to clear
|
|
377
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
378
|
+
*/
|
|
379
|
+
registerCommands(commands) {
|
|
380
|
+
this.commandHandlers.clear();
|
|
381
|
+
if (!commands) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
for (const cmd of commands) {
|
|
385
|
+
this.commandHandlers.set(cmd.name, cmd.handler);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Sets the host capabilities for this session.
|
|
390
|
+
*
|
|
391
|
+
* @param capabilities - The capabilities object from the create/resume response
|
|
392
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
393
|
+
*/
|
|
394
|
+
setCapabilities(capabilities) {
|
|
395
|
+
this._capabilities = capabilities ?? {};
|
|
396
|
+
}
|
|
397
|
+
assertElicitation() {
|
|
398
|
+
if (!this._capabilities.ui?.elicitation) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
"Elicitation is not supported by the host. Check session.capabilities.ui?.elicitation before calling UI methods."
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
async _elicitation(params) {
|
|
405
|
+
this.assertElicitation();
|
|
406
|
+
return this.rpc.ui.elicitation({
|
|
407
|
+
message: params.message,
|
|
408
|
+
requestedSchema: params.requestedSchema
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
async _confirm(message) {
|
|
412
|
+
this.assertElicitation();
|
|
413
|
+
const result = await this.rpc.ui.elicitation({
|
|
414
|
+
message,
|
|
415
|
+
requestedSchema: {
|
|
416
|
+
type: "object",
|
|
417
|
+
properties: {
|
|
418
|
+
confirmed: { type: "boolean", default: true }
|
|
419
|
+
},
|
|
420
|
+
required: ["confirmed"]
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
return result.action === "accept" && result.content?.confirmed === true;
|
|
424
|
+
}
|
|
425
|
+
async _select(message, options) {
|
|
426
|
+
this.assertElicitation();
|
|
427
|
+
const result = await this.rpc.ui.elicitation({
|
|
428
|
+
message,
|
|
429
|
+
requestedSchema: {
|
|
430
|
+
type: "object",
|
|
431
|
+
properties: {
|
|
432
|
+
selection: { type: "string", enum: options }
|
|
433
|
+
},
|
|
434
|
+
required: ["selection"]
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
if (result.action === "accept" && result.content?.selection != null) {
|
|
438
|
+
return result.content.selection;
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
async _input(message, options) {
|
|
443
|
+
this.assertElicitation();
|
|
444
|
+
const field = { type: "string" };
|
|
445
|
+
if (options?.title) field.title = options.title;
|
|
446
|
+
if (options?.description) field.description = options.description;
|
|
447
|
+
if (options?.minLength != null) field.minLength = options.minLength;
|
|
448
|
+
if (options?.maxLength != null) field.maxLength = options.maxLength;
|
|
449
|
+
if (options?.format) field.format = options.format;
|
|
450
|
+
if (options?.default != null) field.default = options.default;
|
|
451
|
+
const result = await this.rpc.ui.elicitation({
|
|
452
|
+
message,
|
|
453
|
+
requestedSchema: {
|
|
454
|
+
type: "object",
|
|
455
|
+
properties: {
|
|
456
|
+
value: field
|
|
457
|
+
},
|
|
458
|
+
required: ["value"]
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
if (result.action === "accept" && result.content?.value != null) {
|
|
462
|
+
return result.content.value;
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
308
466
|
/**
|
|
309
467
|
* Registers a handler for permission requests.
|
|
310
468
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -218,6 +218,187 @@ export declare function defineTool<T = unknown>(name: string, config: {
|
|
|
218
218
|
overridesBuiltInTool?: boolean;
|
|
219
219
|
skipPermission?: boolean;
|
|
220
220
|
}): Tool<T>;
|
|
221
|
+
/**
|
|
222
|
+
* Context passed to a command handler when a command is executed.
|
|
223
|
+
*/
|
|
224
|
+
export interface CommandContext {
|
|
225
|
+
/** Session ID where the command was invoked */
|
|
226
|
+
sessionId: string;
|
|
227
|
+
/** The full command text (e.g. "/deploy production") */
|
|
228
|
+
command: string;
|
|
229
|
+
/** Command name without leading / */
|
|
230
|
+
commandName: string;
|
|
231
|
+
/** Raw argument string after the command name */
|
|
232
|
+
args: string;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Handler invoked when a registered command is executed by a user.
|
|
236
|
+
*/
|
|
237
|
+
export type CommandHandler = (context: CommandContext) => Promise<void> | void;
|
|
238
|
+
/**
|
|
239
|
+
* Definition of a slash command registered with the session.
|
|
240
|
+
* When the CLI is running with a TUI, registered commands appear as
|
|
241
|
+
* `/commandName` for the user to invoke.
|
|
242
|
+
*/
|
|
243
|
+
export interface CommandDefinition {
|
|
244
|
+
/** Command name (without leading /). */
|
|
245
|
+
name: string;
|
|
246
|
+
/** Human-readable description shown in command completion UI. */
|
|
247
|
+
description?: string;
|
|
248
|
+
/** Handler invoked when the command is executed. */
|
|
249
|
+
handler: CommandHandler;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Capabilities reported by the CLI host for this session.
|
|
253
|
+
*/
|
|
254
|
+
export interface SessionCapabilities {
|
|
255
|
+
ui?: {
|
|
256
|
+
/** Whether the host supports interactive elicitation dialogs. */
|
|
257
|
+
elicitation?: boolean;
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* A single field in an elicitation schema — matches the MCP SDK's
|
|
262
|
+
* `PrimitiveSchemaDefinition` union.
|
|
263
|
+
*/
|
|
264
|
+
export type ElicitationSchemaField = {
|
|
265
|
+
type: "string";
|
|
266
|
+
title?: string;
|
|
267
|
+
description?: string;
|
|
268
|
+
enum: string[];
|
|
269
|
+
enumNames?: string[];
|
|
270
|
+
default?: string;
|
|
271
|
+
} | {
|
|
272
|
+
type: "string";
|
|
273
|
+
title?: string;
|
|
274
|
+
description?: string;
|
|
275
|
+
oneOf: {
|
|
276
|
+
const: string;
|
|
277
|
+
title: string;
|
|
278
|
+
}[];
|
|
279
|
+
default?: string;
|
|
280
|
+
} | {
|
|
281
|
+
type: "array";
|
|
282
|
+
title?: string;
|
|
283
|
+
description?: string;
|
|
284
|
+
minItems?: number;
|
|
285
|
+
maxItems?: number;
|
|
286
|
+
items: {
|
|
287
|
+
type: "string";
|
|
288
|
+
enum: string[];
|
|
289
|
+
};
|
|
290
|
+
default?: string[];
|
|
291
|
+
} | {
|
|
292
|
+
type: "array";
|
|
293
|
+
title?: string;
|
|
294
|
+
description?: string;
|
|
295
|
+
minItems?: number;
|
|
296
|
+
maxItems?: number;
|
|
297
|
+
items: {
|
|
298
|
+
anyOf: {
|
|
299
|
+
const: string;
|
|
300
|
+
title: string;
|
|
301
|
+
}[];
|
|
302
|
+
};
|
|
303
|
+
default?: string[];
|
|
304
|
+
} | {
|
|
305
|
+
type: "boolean";
|
|
306
|
+
title?: string;
|
|
307
|
+
description?: string;
|
|
308
|
+
default?: boolean;
|
|
309
|
+
} | {
|
|
310
|
+
type: "string";
|
|
311
|
+
title?: string;
|
|
312
|
+
description?: string;
|
|
313
|
+
minLength?: number;
|
|
314
|
+
maxLength?: number;
|
|
315
|
+
format?: "email" | "uri" | "date" | "date-time";
|
|
316
|
+
default?: string;
|
|
317
|
+
} | {
|
|
318
|
+
type: "number" | "integer";
|
|
319
|
+
title?: string;
|
|
320
|
+
description?: string;
|
|
321
|
+
minimum?: number;
|
|
322
|
+
maximum?: number;
|
|
323
|
+
default?: number;
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* Schema describing the form fields for an elicitation request.
|
|
327
|
+
*/
|
|
328
|
+
export interface ElicitationSchema {
|
|
329
|
+
type: "object";
|
|
330
|
+
properties: Record<string, ElicitationSchemaField>;
|
|
331
|
+
required?: string[];
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Primitive field value in an elicitation result.
|
|
335
|
+
* Matches MCP SDK's `ElicitResult.content` value type.
|
|
336
|
+
*/
|
|
337
|
+
export type ElicitationFieldValue = string | number | boolean | string[];
|
|
338
|
+
/**
|
|
339
|
+
* Result returned from an elicitation request.
|
|
340
|
+
*/
|
|
341
|
+
export interface ElicitationResult {
|
|
342
|
+
/** User action: "accept" (submitted), "decline" (rejected), or "cancel" (dismissed). */
|
|
343
|
+
action: "accept" | "decline" | "cancel";
|
|
344
|
+
/** Form values submitted by the user (present when action is "accept"). */
|
|
345
|
+
content?: Record<string, ElicitationFieldValue>;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Parameters for a raw elicitation request.
|
|
349
|
+
*/
|
|
350
|
+
export interface ElicitationParams {
|
|
351
|
+
/** Message describing what information is needed from the user. */
|
|
352
|
+
message: string;
|
|
353
|
+
/** JSON Schema describing the form fields to present. */
|
|
354
|
+
requestedSchema: ElicitationSchema;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Options for the `input()` convenience method.
|
|
358
|
+
*/
|
|
359
|
+
export interface InputOptions {
|
|
360
|
+
/** Title label for the input field. */
|
|
361
|
+
title?: string;
|
|
362
|
+
/** Descriptive text shown below the field. */
|
|
363
|
+
description?: string;
|
|
364
|
+
/** Minimum character length. */
|
|
365
|
+
minLength?: number;
|
|
366
|
+
/** Maximum character length. */
|
|
367
|
+
maxLength?: number;
|
|
368
|
+
/** Semantic format hint. */
|
|
369
|
+
format?: "email" | "uri" | "date" | "date-time";
|
|
370
|
+
/** Default value pre-populated in the field. */
|
|
371
|
+
default?: string;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* The `session.ui` API object providing interactive UI methods.
|
|
375
|
+
* Only usable when the CLI host supports elicitation.
|
|
376
|
+
*/
|
|
377
|
+
export interface SessionUiApi {
|
|
378
|
+
/**
|
|
379
|
+
* Shows a generic elicitation dialog with a custom schema.
|
|
380
|
+
* @throws Error if the host does not support elicitation.
|
|
381
|
+
*/
|
|
382
|
+
elicitation(params: ElicitationParams): Promise<ElicitationResult>;
|
|
383
|
+
/**
|
|
384
|
+
* Shows a confirmation dialog and returns the user's boolean answer.
|
|
385
|
+
* Returns `false` if the user declines or cancels.
|
|
386
|
+
* @throws Error if the host does not support elicitation.
|
|
387
|
+
*/
|
|
388
|
+
confirm(message: string): Promise<boolean>;
|
|
389
|
+
/**
|
|
390
|
+
* Shows a selection dialog with the given options.
|
|
391
|
+
* Returns the selected value, or `null` if the user declines/cancels.
|
|
392
|
+
* @throws Error if the host does not support elicitation.
|
|
393
|
+
*/
|
|
394
|
+
select(message: string, options: string[]): Promise<string | null>;
|
|
395
|
+
/**
|
|
396
|
+
* Shows a text input dialog.
|
|
397
|
+
* Returns the entered text, or `null` if the user declines/cancels.
|
|
398
|
+
* @throws Error if the host does not support elicitation.
|
|
399
|
+
*/
|
|
400
|
+
input(message: string, options?: InputOptions): Promise<string | null>;
|
|
401
|
+
}
|
|
221
402
|
export interface ToolCallRequestPayload {
|
|
222
403
|
sessionId: string;
|
|
223
404
|
toolCallId: string;
|
|
@@ -675,6 +856,12 @@ export interface SessionConfig {
|
|
|
675
856
|
* Tools exposed to the CLI server
|
|
676
857
|
*/
|
|
677
858
|
tools?: Tool<any>[];
|
|
859
|
+
/**
|
|
860
|
+
* Slash commands registered for this session.
|
|
861
|
+
* When the CLI has a TUI, each command appears as `/name` for the user to invoke.
|
|
862
|
+
* The handler is called when the user executes the command.
|
|
863
|
+
*/
|
|
864
|
+
commands?: CommandDefinition[];
|
|
678
865
|
/**
|
|
679
866
|
* System message configuration
|
|
680
867
|
* Controls how the system prompt is constructed
|
|
@@ -759,7 +946,7 @@ export interface SessionConfig {
|
|
|
759
946
|
/**
|
|
760
947
|
* Configuration for resuming a session
|
|
761
948
|
*/
|
|
762
|
-
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" | "onEvent"> & {
|
|
949
|
+
export type ResumeSessionConfig = Pick<SessionConfig, "clientName" | "model" | "tools" | "commands" | "systemMessage" | "availableTools" | "excludedTools" | "provider" | "streaming" | "reasoningEffort" | "onPermissionRequest" | "onUserInputRequest" | "hooks" | "workingDirectory" | "configDir" | "mcpServers" | "customAgents" | "agent" | "skillDirectories" | "disabledSkills" | "infiniteSessions" | "onEvent"> & {
|
|
763
950
|
/**
|
|
764
951
|
* When true, skips emitting the session.resume event.
|
|
765
952
|
* Useful for reconnecting to a session without triggering resume-related side effects.
|
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.2.
|
|
7
|
+
"version": "0.2.1-preview.1",
|
|
8
8
|
"description": "TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC",
|
|
9
9
|
"main": "./dist/cjs/index.js",
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"author": "GitHub",
|
|
57
57
|
"license": "MIT",
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@github/copilot": "^1.0.
|
|
59
|
+
"@github/copilot": "^1.0.12-0",
|
|
60
60
|
"vscode-jsonrpc": "^8.2.1",
|
|
61
61
|
"zod": "^4.3.6"
|
|
62
62
|
},
|