@github/copilot-sdk 0.2.1-preview.0 → 0.2.1-unstable.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -30
- package/dist/cjs/client.js +88 -11
- package/dist/cjs/generated/rpc.js +69 -3
- package/dist/cjs/session.js +53 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.js +89 -12
- package/dist/generated/rpc.d.ts +419 -2
- package/dist/generated/rpc.js +67 -2
- package/dist/generated/session-events.d.ts +138 -5
- package/dist/index.d.ts +1 -1
- package/dist/session.d.ts +18 -1
- package/dist/session.js +53 -0
- package/dist/types.d.ts +63 -2
- package/package.json +3 -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)
|
|
@@ -117,6 +120,7 @@ Create a new conversation session.
|
|
|
117
120
|
- `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section.
|
|
118
121
|
- `onPermissionRequest: PermissionHandler` - **Required.** Handler called before each tool execution to approve or deny it. Use `approveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section.
|
|
119
122
|
- `onUserInputRequest?: UserInputHandler` - Handler for user input requests from the agent. Enables the `ask_user` tool. See [User Input Requests](#user-input-requests) section.
|
|
123
|
+
- `onElicitationRequest?: ElicitationHandler` - Handler for elicitation requests dispatched by the server. Enables this client to present form-based UI dialogs on behalf of the agent or other session participants. See [Elicitation Requests](#elicitation-requests) section.
|
|
120
124
|
- `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section.
|
|
121
125
|
|
|
122
126
|
##### `resumeSession(sessionId: string, config?: ResumeSessionConfig): Promise<CopilotSession>`
|
|
@@ -184,6 +188,7 @@ const unsubscribe = client.on((event) => {
|
|
|
184
188
|
```
|
|
185
189
|
|
|
186
190
|
**Lifecycle Event Types:**
|
|
191
|
+
|
|
187
192
|
- `session.created` - A new session was created
|
|
188
193
|
- `session.deleted` - A session was deleted
|
|
189
194
|
- `session.updated` - A session was updated (e.g., new messages)
|
|
@@ -289,11 +294,13 @@ if (session.capabilities.ui?.elicitation) {
|
|
|
289
294
|
}
|
|
290
295
|
```
|
|
291
296
|
|
|
297
|
+
Capabilities may update during the session. For example, when another client joins or disconnects with an elicitation handler. The SDK automatically applies `capabilities.changed` events, so this property always reflects the current state.
|
|
298
|
+
|
|
292
299
|
##### `ui: SessionUiApi`
|
|
293
300
|
|
|
294
301
|
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.
|
|
295
302
|
|
|
296
|
-
##### `destroy(): Promise<void>`
|
|
303
|
+
##### `destroy(): Promise<void>` _(deprecated)_
|
|
297
304
|
|
|
298
305
|
Deprecated — use `disconnect()` instead.
|
|
299
306
|
|
|
@@ -454,8 +461,10 @@ defineTool("edit_file", {
|
|
|
454
461
|
description: "Custom file editor with project-specific validation",
|
|
455
462
|
parameters: z.object({ path: z.string(), content: z.string() }),
|
|
456
463
|
overridesBuiltInTool: true,
|
|
457
|
-
handler: async ({ path, content }) => {
|
|
458
|
-
|
|
464
|
+
handler: async ({ path, content }) => {
|
|
465
|
+
/* your logic */
|
|
466
|
+
},
|
|
467
|
+
});
|
|
459
468
|
```
|
|
460
469
|
|
|
461
470
|
#### Skipping Permission Prompts
|
|
@@ -467,8 +476,10 @@ defineTool("safe_lookup", {
|
|
|
467
476
|
description: "A read-only lookup that needs no confirmation",
|
|
468
477
|
parameters: z.object({ id: z.string() }),
|
|
469
478
|
skipPermission: true,
|
|
470
|
-
handler: async ({ id }) => {
|
|
471
|
-
|
|
479
|
+
handler: async ({ id }) => {
|
|
480
|
+
/* your logic */
|
|
481
|
+
},
|
|
482
|
+
});
|
|
472
483
|
```
|
|
473
484
|
|
|
474
485
|
### Commands
|
|
@@ -497,9 +508,9 @@ Commands are sent to the CLI on both `createSession` and `resumeSession`, so you
|
|
|
497
508
|
|
|
498
509
|
### UI Elicitation
|
|
499
510
|
|
|
500
|
-
When the
|
|
511
|
+
When the session has elicitation support — either from the CLI's TUI or from another client that registered an `onElicitationRequest` handler (see [Elicitation Requests](#elicitation-requests)) — the SDK can request interactive form dialogs from the user. The `session.ui` object provides convenience methods built on a single generic `elicitation` RPC.
|
|
501
512
|
|
|
502
|
-
> **Capability check:** Elicitation is only available when
|
|
513
|
+
> **Capability check:** Elicitation is only available when at least one connected participant advertises support. Always check `session.capabilities.ui?.elicitation` before calling UI methods — this property updates automatically as participants join and leave.
|
|
503
514
|
|
|
504
515
|
```ts
|
|
505
516
|
const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
@@ -571,7 +582,10 @@ const session = await client.createSession({
|
|
|
571
582
|
mode: "customize",
|
|
572
583
|
sections: {
|
|
573
584
|
// Replace the tone/style section
|
|
574
|
-
tone: {
|
|
585
|
+
tone: {
|
|
586
|
+
action: "replace",
|
|
587
|
+
content: "Respond in a warm, professional tone. Be thorough in explanations.",
|
|
588
|
+
},
|
|
575
589
|
// Remove coding-specific rules
|
|
576
590
|
code_change_rules: { action: "remove" },
|
|
577
591
|
// Append to existing guidelines
|
|
@@ -586,6 +600,7 @@ const session = await client.createSession({
|
|
|
586
600
|
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.
|
|
587
601
|
|
|
588
602
|
Each section override supports four actions:
|
|
603
|
+
|
|
589
604
|
- **`replace`** — Replace the section content entirely
|
|
590
605
|
- **`remove`** — Remove the section from the prompt
|
|
591
606
|
- **`append`** — Add content after the existing section
|
|
@@ -624,7 +639,7 @@ const session = await client.createSession({
|
|
|
624
639
|
model: "gpt-5",
|
|
625
640
|
infiniteSessions: {
|
|
626
641
|
enabled: true,
|
|
627
|
-
backgroundCompactionThreshold: 0.
|
|
642
|
+
backgroundCompactionThreshold: 0.8, // Start compacting at 80% context usage
|
|
628
643
|
bufferExhaustionThreshold: 0.95, // Block at 95% until compaction completes
|
|
629
644
|
},
|
|
630
645
|
});
|
|
@@ -723,8 +738,8 @@ const session = await client.createSession({
|
|
|
723
738
|
const session = await client.createSession({
|
|
724
739
|
model: "gpt-4",
|
|
725
740
|
provider: {
|
|
726
|
-
type: "azure",
|
|
727
|
-
baseUrl: "https://my-resource.openai.azure.com",
|
|
741
|
+
type: "azure", // Must be "azure" for Azure endpoints, NOT "openai"
|
|
742
|
+
baseUrl: "https://my-resource.openai.azure.com", // Just the host, no path
|
|
728
743
|
apiKey: process.env.AZURE_OPENAI_KEY,
|
|
729
744
|
azure: {
|
|
730
745
|
apiVersion: "2024-10-21",
|
|
@@ -734,6 +749,7 @@ const session = await client.createSession({
|
|
|
734
749
|
```
|
|
735
750
|
|
|
736
751
|
> **Important notes:**
|
|
752
|
+
>
|
|
737
753
|
> - When using a custom provider, the `model` parameter is **required**. The SDK will throw an error if no model is specified.
|
|
738
754
|
> - For Azure OpenAI endpoints (`*.openai.azure.com`), you **must** use `type: "azure"`, not `type: "openai"`.
|
|
739
755
|
> - 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.
|
|
@@ -744,9 +760,9 @@ The SDK supports OpenTelemetry for distributed tracing. Provide a `telemetry` co
|
|
|
744
760
|
|
|
745
761
|
```typescript
|
|
746
762
|
const client = new CopilotClient({
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
763
|
+
telemetry: {
|
|
764
|
+
otlpEndpoint: "http://localhost:4318",
|
|
765
|
+
},
|
|
750
766
|
});
|
|
751
767
|
```
|
|
752
768
|
|
|
@@ -772,12 +788,12 @@ If you're already using `@opentelemetry/api` in your app and want this linkage,
|
|
|
772
788
|
import { propagation, context } from "@opentelemetry/api";
|
|
773
789
|
|
|
774
790
|
const client = new CopilotClient({
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
791
|
+
telemetry: { otlpEndpoint: "http://localhost:4318" },
|
|
792
|
+
onGetTraceContext: () => {
|
|
793
|
+
const carrier: Record<string, string> = {};
|
|
794
|
+
propagation.inject(context.active(), carrier);
|
|
795
|
+
return carrier;
|
|
796
|
+
},
|
|
781
797
|
});
|
|
782
798
|
```
|
|
783
799
|
|
|
@@ -837,14 +853,15 @@ const session = await client.createSession({
|
|
|
837
853
|
|
|
838
854
|
### Permission Result Kinds
|
|
839
855
|
|
|
840
|
-
| Kind
|
|
841
|
-
|
|
842
|
-
| `"approved"`
|
|
843
|
-
| `"denied-interactively-by-user"`
|
|
844
|
-
| `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked
|
|
845
|
-
| `"denied-by-rules"`
|
|
846
|
-
| `"denied-by-content-exclusion-policy"`
|
|
847
|
-
| `"no-result"`
|
|
856
|
+
| Kind | Meaning |
|
|
857
|
+
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
858
|
+
| `"approved"` | Allow the tool to run |
|
|
859
|
+
| `"denied-interactively-by-user"` | User explicitly denied the request |
|
|
860
|
+
| `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked |
|
|
861
|
+
| `"denied-by-rules"` | Denied by a policy rule |
|
|
862
|
+
| `"denied-by-content-exclusion-policy"` | Denied due to a content exclusion policy |
|
|
863
|
+
| `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) |
|
|
864
|
+
|
|
848
865
|
### Resuming Sessions
|
|
849
866
|
|
|
850
867
|
Pass `onPermissionRequest` when resuming a session too — it is required:
|
|
@@ -885,6 +902,42 @@ const session = await client.createSession({
|
|
|
885
902
|
});
|
|
886
903
|
```
|
|
887
904
|
|
|
905
|
+
## Elicitation Requests
|
|
906
|
+
|
|
907
|
+
Register an `onElicitationRequest` handler to let your client act as an elicitation provider — presenting form-based UI dialogs on behalf of the agent. When provided, the server notifies your client whenever a tool or MCP server needs structured user input.
|
|
908
|
+
|
|
909
|
+
```typescript
|
|
910
|
+
const session = await client.createSession({
|
|
911
|
+
model: "gpt-5",
|
|
912
|
+
onPermissionRequest: approveAll,
|
|
913
|
+
onElicitationRequest: async (request, invocation) => {
|
|
914
|
+
// request.message - Description of what information is needed
|
|
915
|
+
// request.requestedSchema - JSON Schema describing the form fields
|
|
916
|
+
// request.mode - "form" (structured input) or "url" (browser redirect)
|
|
917
|
+
// request.elicitationSource - Origin of the request (e.g. MCP server name)
|
|
918
|
+
|
|
919
|
+
console.log(`Elicitation from ${request.elicitationSource}: ${request.message}`);
|
|
920
|
+
|
|
921
|
+
// Present UI to the user and collect their response...
|
|
922
|
+
return {
|
|
923
|
+
action: "accept", // "accept", "decline", or "cancel"
|
|
924
|
+
content: { region: "us-east", dryRun: true },
|
|
925
|
+
};
|
|
926
|
+
},
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
// The session now reports elicitation capability
|
|
930
|
+
console.log(session.capabilities.ui?.elicitation); // true
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
When `onElicitationRequest` is provided, the SDK sends `requestElicitation: true` during session create/resume, which enables `session.capabilities.ui.elicitation` on the session.
|
|
934
|
+
|
|
935
|
+
In multi-client scenarios:
|
|
936
|
+
|
|
937
|
+
- If no connected client was previously providing an elicitation capability, but a new client joins that can, all clients will receive a `capabilities.changed` event to notify them that elicitation is now possible. The SDK automatically updates `session.capabilities` when these events arrive.
|
|
938
|
+
- Similarly, if the last elicitation provider disconnects, all clients receive a `capabilities.changed` event indicating elicitation is no longer available.
|
|
939
|
+
- The server fans out elicitation requests to **all** connected clients that registered a handler — the first response wins.
|
|
940
|
+
|
|
888
941
|
## Session Hooks
|
|
889
942
|
|
|
890
943
|
Hook into session lifecycle events by providing handlers in the `hooks` configuration:
|
package/dist/cjs/client.js
CHANGED
|
@@ -116,6 +116,8 @@ class CopilotClient {
|
|
|
116
116
|
processExitPromise = null;
|
|
117
117
|
// Rejects when CLI process exits
|
|
118
118
|
negotiatedProtocolVersion = null;
|
|
119
|
+
/** Connection-level session filesystem config, set via constructor option. */
|
|
120
|
+
sessionFsConfig = null;
|
|
119
121
|
/**
|
|
120
122
|
* Typed server-scoped RPC methods.
|
|
121
123
|
* @throws Error if the client is not connected
|
|
@@ -175,8 +177,10 @@ class CopilotClient {
|
|
|
175
177
|
}
|
|
176
178
|
this.onListModels = options.onListModels;
|
|
177
179
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
180
|
+
this.sessionFsConfig = options.sessionFs ?? null;
|
|
181
|
+
const effectiveEnv = options.env ?? process.env;
|
|
178
182
|
this.options = {
|
|
179
|
-
cliPath: options.cliUrl ? void 0 : options.cliPath || getBundledCliPath(),
|
|
183
|
+
cliPath: options.cliUrl ? void 0 : options.cliPath || effectiveEnv.COPILOT_CLI_PATH || getBundledCliPath(),
|
|
180
184
|
cliArgs: options.cliArgs ?? [],
|
|
181
185
|
cwd: options.cwd ?? process.cwd(),
|
|
182
186
|
port: options.port || 0,
|
|
@@ -187,7 +191,7 @@ class CopilotClient {
|
|
|
187
191
|
logLevel: options.logLevel || "debug",
|
|
188
192
|
autoStart: options.autoStart ?? true,
|
|
189
193
|
autoRestart: false,
|
|
190
|
-
env:
|
|
194
|
+
env: effectiveEnv,
|
|
191
195
|
githubToken: options.githubToken,
|
|
192
196
|
// Default useLoggedInUser to false when githubToken is provided, otherwise true
|
|
193
197
|
useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true),
|
|
@@ -245,6 +249,13 @@ class CopilotClient {
|
|
|
245
249
|
}
|
|
246
250
|
await this.connectToServer();
|
|
247
251
|
await this.verifyProtocolVersion();
|
|
252
|
+
if (this.sessionFsConfig) {
|
|
253
|
+
await this.connection.sendRequest("sessionFs.setProvider", {
|
|
254
|
+
initialCwd: this.sessionFsConfig.initialCwd,
|
|
255
|
+
sessionStatePath: this.sessionFsConfig.sessionStatePath,
|
|
256
|
+
conventions: this.sessionFsConfig.conventions
|
|
257
|
+
});
|
|
258
|
+
}
|
|
248
259
|
this.state = "connected";
|
|
249
260
|
} catch (error) {
|
|
250
261
|
this.state = "error";
|
|
@@ -456,6 +467,9 @@ class CopilotClient {
|
|
|
456
467
|
if (config.onUserInputRequest) {
|
|
457
468
|
session.registerUserInputHandler(config.onUserInputRequest);
|
|
458
469
|
}
|
|
470
|
+
if (config.onElicitationRequest) {
|
|
471
|
+
session.registerElicitationHandler(config.onElicitationRequest);
|
|
472
|
+
}
|
|
459
473
|
if (config.hooks) {
|
|
460
474
|
session.registerHooks(config.hooks);
|
|
461
475
|
}
|
|
@@ -469,6 +483,15 @@ class CopilotClient {
|
|
|
469
483
|
session.on(config.onEvent);
|
|
470
484
|
}
|
|
471
485
|
this.sessions.set(sessionId, session);
|
|
486
|
+
if (this.sessionFsConfig) {
|
|
487
|
+
if (config.createSessionFsHandler) {
|
|
488
|
+
session.clientSessionApis.sessionFs = config.createSessionFsHandler(session);
|
|
489
|
+
} else {
|
|
490
|
+
throw new Error(
|
|
491
|
+
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
472
495
|
try {
|
|
473
496
|
const response = await this.connection.sendRequest("session.create", {
|
|
474
497
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
@@ -493,6 +516,7 @@ class CopilotClient {
|
|
|
493
516
|
provider: config.provider,
|
|
494
517
|
requestPermission: true,
|
|
495
518
|
requestUserInput: !!config.onUserInputRequest,
|
|
519
|
+
requestElicitation: !!config.onElicitationRequest,
|
|
496
520
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
497
521
|
workingDirectory: config.workingDirectory,
|
|
498
522
|
streaming: config.streaming,
|
|
@@ -563,6 +587,9 @@ class CopilotClient {
|
|
|
563
587
|
if (config.onUserInputRequest) {
|
|
564
588
|
session.registerUserInputHandler(config.onUserInputRequest);
|
|
565
589
|
}
|
|
590
|
+
if (config.onElicitationRequest) {
|
|
591
|
+
session.registerElicitationHandler(config.onElicitationRequest);
|
|
592
|
+
}
|
|
566
593
|
if (config.hooks) {
|
|
567
594
|
session.registerHooks(config.hooks);
|
|
568
595
|
}
|
|
@@ -576,6 +603,15 @@ class CopilotClient {
|
|
|
576
603
|
session.on(config.onEvent);
|
|
577
604
|
}
|
|
578
605
|
this.sessions.set(sessionId, session);
|
|
606
|
+
if (this.sessionFsConfig) {
|
|
607
|
+
if (config.createSessionFsHandler) {
|
|
608
|
+
session.clientSessionApis.sessionFs = config.createSessionFsHandler(session);
|
|
609
|
+
} else {
|
|
610
|
+
throw new Error(
|
|
611
|
+
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
579
615
|
try {
|
|
580
616
|
const response = await this.connection.sendRequest("session.resume", {
|
|
581
617
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
@@ -600,6 +636,7 @@ class CopilotClient {
|
|
|
600
636
|
provider: config.provider,
|
|
601
637
|
requestPermission: true,
|
|
602
638
|
requestUserInput: !!config.onUserInputRequest,
|
|
639
|
+
requestElicitation: !!config.onElicitationRequest,
|
|
603
640
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
604
641
|
workingDirectory: config.workingDirectory,
|
|
605
642
|
configDir: config.configDir,
|
|
@@ -811,16 +848,50 @@ class CopilotClient {
|
|
|
811
848
|
if (!this.connection) {
|
|
812
849
|
throw new Error("Client not connected");
|
|
813
850
|
}
|
|
814
|
-
const response = await this.connection.sendRequest("session.list", {
|
|
851
|
+
const response = await this.connection.sendRequest("session.list", {
|
|
852
|
+
filter
|
|
853
|
+
});
|
|
815
854
|
const { sessions } = response;
|
|
816
|
-
return sessions.map(
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
855
|
+
return sessions.map(CopilotClient.toSessionMetadata);
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Gets metadata for a specific session by ID.
|
|
859
|
+
*
|
|
860
|
+
* This provides an efficient O(1) lookup of a single session's metadata
|
|
861
|
+
* instead of listing all sessions. Returns undefined if the session is not found.
|
|
862
|
+
*
|
|
863
|
+
* @param sessionId - The ID of the session to look up
|
|
864
|
+
* @returns A promise that resolves with the session metadata, or undefined if not found
|
|
865
|
+
* @throws Error if the client is not connected
|
|
866
|
+
*
|
|
867
|
+
* @example
|
|
868
|
+
* ```typescript
|
|
869
|
+
* const metadata = await client.getSessionMetadata("session-123");
|
|
870
|
+
* if (metadata) {
|
|
871
|
+
* console.log(`Session started at: ${metadata.startTime}`);
|
|
872
|
+
* }
|
|
873
|
+
* ```
|
|
874
|
+
*/
|
|
875
|
+
async getSessionMetadata(sessionId) {
|
|
876
|
+
if (!this.connection) {
|
|
877
|
+
throw new Error("Client not connected");
|
|
878
|
+
}
|
|
879
|
+
const response = await this.connection.sendRequest("session.getMetadata", { sessionId });
|
|
880
|
+
const { session } = response;
|
|
881
|
+
if (!session) {
|
|
882
|
+
return void 0;
|
|
883
|
+
}
|
|
884
|
+
return CopilotClient.toSessionMetadata(session);
|
|
885
|
+
}
|
|
886
|
+
static toSessionMetadata(raw) {
|
|
887
|
+
return {
|
|
888
|
+
sessionId: raw.sessionId,
|
|
889
|
+
startTime: new Date(raw.startTime),
|
|
890
|
+
modifiedTime: new Date(raw.modifiedTime),
|
|
891
|
+
summary: raw.summary,
|
|
892
|
+
isRemote: raw.isRemote,
|
|
893
|
+
context: raw.context
|
|
894
|
+
};
|
|
824
895
|
}
|
|
825
896
|
/**
|
|
826
897
|
* Gets the foreground session ID in TUI+server mode.
|
|
@@ -1150,6 +1221,12 @@ stderr: ${stderrOutput}`
|
|
|
1150
1221
|
"systemMessage.transform",
|
|
1151
1222
|
async (params) => await this.handleSystemMessageTransform(params)
|
|
1152
1223
|
);
|
|
1224
|
+
const sessions = this.sessions;
|
|
1225
|
+
(0, import_rpc.registerClientSessionApiHandlers)(this.connection, (sessionId) => {
|
|
1226
|
+
const session = sessions.get(sessionId);
|
|
1227
|
+
if (!session) throw new Error(`No session found for sessionId: ${sessionId}`);
|
|
1228
|
+
return session.clientSessionApis;
|
|
1229
|
+
});
|
|
1153
1230
|
this.connection.onClose(() => {
|
|
1154
1231
|
this.state = "disconnected";
|
|
1155
1232
|
});
|
|
@@ -19,7 +19,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var rpc_exports = {};
|
|
20
20
|
__export(rpc_exports, {
|
|
21
21
|
createServerRpc: () => createServerRpc,
|
|
22
|
-
createSessionRpc: () => createSessionRpc
|
|
22
|
+
createSessionRpc: () => createSessionRpc,
|
|
23
|
+
registerClientSessionApiHandlers: () => registerClientSessionApiHandlers
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(rpc_exports);
|
|
25
26
|
function createServerRpc(connection) {
|
|
@@ -33,6 +34,17 @@ function createServerRpc(connection) {
|
|
|
33
34
|
},
|
|
34
35
|
account: {
|
|
35
36
|
getQuota: async () => connection.sendRequest("account.getQuota", {})
|
|
37
|
+
},
|
|
38
|
+
mcp: {
|
|
39
|
+
config: {
|
|
40
|
+
list: async () => connection.sendRequest("mcp.config.list", {}),
|
|
41
|
+
add: async (params) => connection.sendRequest("mcp.config.add", params),
|
|
42
|
+
update: async (params) => connection.sendRequest("mcp.config.update", params),
|
|
43
|
+
remove: async (params) => connection.sendRequest("mcp.config.remove", params)
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
sessionFs: {
|
|
47
|
+
setProvider: async (params) => connection.sendRequest("sessionFs.setProvider", params)
|
|
36
48
|
}
|
|
37
49
|
};
|
|
38
50
|
}
|
|
@@ -104,7 +116,8 @@ function createSessionRpc(connection, sessionId) {
|
|
|
104
116
|
handlePendingCommand: async (params) => connection.sendRequest("session.commands.handlePendingCommand", { sessionId, ...params })
|
|
105
117
|
},
|
|
106
118
|
ui: {
|
|
107
|
-
elicitation: async (params) => connection.sendRequest("session.ui.elicitation", { sessionId, ...params })
|
|
119
|
+
elicitation: async (params) => connection.sendRequest("session.ui.elicitation", { sessionId, ...params }),
|
|
120
|
+
handlePendingElicitation: async (params) => connection.sendRequest("session.ui.handlePendingElicitation", { sessionId, ...params })
|
|
108
121
|
},
|
|
109
122
|
permissions: {
|
|
110
123
|
handlePendingPermissionRequest: async (params) => connection.sendRequest("session.permissions.handlePendingPermissionRequest", { sessionId, ...params })
|
|
@@ -116,8 +129,61 @@ function createSessionRpc(connection, sessionId) {
|
|
|
116
129
|
}
|
|
117
130
|
};
|
|
118
131
|
}
|
|
132
|
+
function registerClientSessionApiHandlers(connection, getHandlers) {
|
|
133
|
+
connection.onRequest("sessionFs.readFile", async (params) => {
|
|
134
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
135
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
136
|
+
return handler.readFile(params);
|
|
137
|
+
});
|
|
138
|
+
connection.onRequest("sessionFs.writeFile", async (params) => {
|
|
139
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
140
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
141
|
+
return handler.writeFile(params);
|
|
142
|
+
});
|
|
143
|
+
connection.onRequest("sessionFs.appendFile", async (params) => {
|
|
144
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
145
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
146
|
+
return handler.appendFile(params);
|
|
147
|
+
});
|
|
148
|
+
connection.onRequest("sessionFs.exists", async (params) => {
|
|
149
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
150
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
151
|
+
return handler.exists(params);
|
|
152
|
+
});
|
|
153
|
+
connection.onRequest("sessionFs.stat", async (params) => {
|
|
154
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
155
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
156
|
+
return handler.stat(params);
|
|
157
|
+
});
|
|
158
|
+
connection.onRequest("sessionFs.mkdir", async (params) => {
|
|
159
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
160
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
161
|
+
return handler.mkdir(params);
|
|
162
|
+
});
|
|
163
|
+
connection.onRequest("sessionFs.readdir", async (params) => {
|
|
164
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
165
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
166
|
+
return handler.readdir(params);
|
|
167
|
+
});
|
|
168
|
+
connection.onRequest("sessionFs.readdirWithTypes", async (params) => {
|
|
169
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
170
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
171
|
+
return handler.readdirWithTypes(params);
|
|
172
|
+
});
|
|
173
|
+
connection.onRequest("sessionFs.rm", async (params) => {
|
|
174
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
175
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
176
|
+
return handler.rm(params);
|
|
177
|
+
});
|
|
178
|
+
connection.onRequest("sessionFs.rename", async (params) => {
|
|
179
|
+
const handler = getHandlers(params.sessionId).sessionFs;
|
|
180
|
+
if (!handler) throw new Error(`No sessionFs handler registered for session: ${params.sessionId}`);
|
|
181
|
+
return handler.rename(params);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
119
184
|
// Annotate the CommonJS export names for ESM import in node:
|
|
120
185
|
0 && (module.exports = {
|
|
121
186
|
createServerRpc,
|
|
122
|
-
createSessionRpc
|
|
187
|
+
createSessionRpc,
|
|
188
|
+
registerClientSessionApiHandlers
|
|
123
189
|
});
|
package/dist/cjs/session.js
CHANGED
|
@@ -48,11 +48,14 @@ class CopilotSession {
|
|
|
48
48
|
commandHandlers = /* @__PURE__ */ new Map();
|
|
49
49
|
permissionHandler;
|
|
50
50
|
userInputHandler;
|
|
51
|
+
elicitationHandler;
|
|
51
52
|
hooks;
|
|
52
53
|
transformCallbacks;
|
|
53
54
|
_rpc = null;
|
|
54
55
|
traceContextProvider;
|
|
55
56
|
_capabilities = {};
|
|
57
|
+
/** @internal Client session API handlers, populated by CopilotClient during create/resume. */
|
|
58
|
+
clientSessionApis = {};
|
|
56
59
|
/**
|
|
57
60
|
* Typed session-scoped RPC methods.
|
|
58
61
|
*/
|
|
@@ -269,6 +272,22 @@ class CopilotSession {
|
|
|
269
272
|
} else if (event.type === "command.execute") {
|
|
270
273
|
const { requestId, commandName, command, args } = event.data;
|
|
271
274
|
void this._executeCommandAndRespond(requestId, commandName, command, args);
|
|
275
|
+
} else if (event.type === "elicitation.requested") {
|
|
276
|
+
if (this.elicitationHandler) {
|
|
277
|
+
const { message, requestedSchema, mode, elicitationSource, url, requestId } = event.data;
|
|
278
|
+
void this._handleElicitationRequest(
|
|
279
|
+
{
|
|
280
|
+
message,
|
|
281
|
+
requestedSchema,
|
|
282
|
+
mode,
|
|
283
|
+
elicitationSource,
|
|
284
|
+
url
|
|
285
|
+
},
|
|
286
|
+
requestId
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
} else if (event.type === "capabilities.changed") {
|
|
290
|
+
this._capabilities = { ...this._capabilities, ...event.data };
|
|
272
291
|
}
|
|
273
292
|
}
|
|
274
293
|
/**
|
|
@@ -409,6 +428,40 @@ class CopilotSession {
|
|
|
409
428
|
this.commandHandlers.set(cmd.name, cmd.handler);
|
|
410
429
|
}
|
|
411
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Registers the elicitation handler for this session.
|
|
433
|
+
*
|
|
434
|
+
* @param handler - The handler to invoke when the server dispatches an elicitation request
|
|
435
|
+
* @internal This method is typically called internally when creating/resuming a session.
|
|
436
|
+
*/
|
|
437
|
+
registerElicitationHandler(handler) {
|
|
438
|
+
this.elicitationHandler = handler;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Handles an elicitation.requested broadcast event.
|
|
442
|
+
* Invokes the registered handler and responds via handlePendingElicitation RPC.
|
|
443
|
+
* @internal
|
|
444
|
+
*/
|
|
445
|
+
async _handleElicitationRequest(request, requestId) {
|
|
446
|
+
if (!this.elicitationHandler) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
const result = await this.elicitationHandler(request, { sessionId: this.sessionId });
|
|
451
|
+
await this.rpc.ui.handlePendingElicitation({ requestId, result });
|
|
452
|
+
} catch {
|
|
453
|
+
try {
|
|
454
|
+
await this.rpc.ui.handlePendingElicitation({
|
|
455
|
+
requestId,
|
|
456
|
+
result: { action: "cancel" }
|
|
457
|
+
});
|
|
458
|
+
} catch (rpcError) {
|
|
459
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
460
|
+
throw rpcError;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
412
465
|
/**
|
|
413
466
|
* Sets the host capabilities for this session.
|
|
414
467
|
*
|
package/dist/client.d.ts
CHANGED
|
@@ -55,6 +55,8 @@ export declare class CopilotClient {
|
|
|
55
55
|
private _rpc;
|
|
56
56
|
private processExitPromise;
|
|
57
57
|
private negotiatedProtocolVersion;
|
|
58
|
+
/** Connection-level session filesystem config, set via constructor option. */
|
|
59
|
+
private sessionFsConfig;
|
|
58
60
|
/**
|
|
59
61
|
* Typed server-scoped RPC methods.
|
|
60
62
|
* @throws Error if the client is not connected
|
|
@@ -317,6 +319,26 @@ export declare class CopilotClient {
|
|
|
317
319
|
* const sessions = await client.listSessions({ repository: "owner/repo" });
|
|
318
320
|
*/
|
|
319
321
|
listSessions(filter?: SessionListFilter): Promise<SessionMetadata[]>;
|
|
322
|
+
/**
|
|
323
|
+
* Gets metadata for a specific session by ID.
|
|
324
|
+
*
|
|
325
|
+
* This provides an efficient O(1) lookup of a single session's metadata
|
|
326
|
+
* instead of listing all sessions. Returns undefined if the session is not found.
|
|
327
|
+
*
|
|
328
|
+
* @param sessionId - The ID of the session to look up
|
|
329
|
+
* @returns A promise that resolves with the session metadata, or undefined if not found
|
|
330
|
+
* @throws Error if the client is not connected
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* const metadata = await client.getSessionMetadata("session-123");
|
|
335
|
+
* if (metadata) {
|
|
336
|
+
* console.log(`Session started at: ${metadata.startTime}`);
|
|
337
|
+
* }
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
340
|
+
getSessionMetadata(sessionId: string): Promise<SessionMetadata | undefined>;
|
|
341
|
+
private static toSessionMetadata;
|
|
320
342
|
/**
|
|
321
343
|
* Gets the foreground session ID in TUI+server mode.
|
|
322
344
|
*
|