@github/copilot-sdk 0.1.31 → 0.1.33-preview.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/dist/client.d.ts +53 -2
- package/dist/client.js +221 -77
- package/dist/extension.d.ts +19 -2
- package/dist/extension.js +18 -2
- package/dist/generated/rpc.d.ts +119 -0
- package/dist/generated/rpc.js +5 -0
- package/dist/generated/session-events.d.ts +419 -20
- package/dist/session.d.ts +33 -3
- package/dist/session.js +53 -2
- package/dist/types.d.ts +27 -2
- package/docs/agent-author.md +263 -0
- package/docs/examples.md +668 -0
- package/docs/extensions.md +59 -0
- package/package.json +3 -2
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
import { createServerRpc } from "./generated/rpc.js";
|
|
2
2
|
import { CopilotSession } from "./session.js";
|
|
3
3
|
import type { ConnectionState, CopilotClientOptions, GetAuthStatusResponse, GetStatusResponse, ModelInfo, ResumeSessionConfig, SessionConfig, SessionLifecycleEventType, SessionLifecycleHandler, SessionListFilter, SessionMetadata, TypedSessionLifecycleHandler } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Main client for interacting with the Copilot CLI.
|
|
6
|
+
*
|
|
7
|
+
* The CopilotClient manages the connection to the Copilot CLI server and provides
|
|
8
|
+
* methods to create and manage conversation sessions. It can either spawn a CLI
|
|
9
|
+
* server process or connect to an existing server.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { CopilotClient } from "@github/copilot-sdk";
|
|
14
|
+
*
|
|
15
|
+
* // Create a client with default options (spawns CLI server)
|
|
16
|
+
* const client = new CopilotClient();
|
|
17
|
+
*
|
|
18
|
+
* // Or connect to an existing server
|
|
19
|
+
* const client = new CopilotClient({ cliUrl: "localhost:3000" });
|
|
20
|
+
*
|
|
21
|
+
* // Create a session
|
|
22
|
+
* const session = await client.createSession({ onPermissionRequest: approveAll, model: "gpt-4" });
|
|
23
|
+
*
|
|
24
|
+
* // Send messages and handle responses
|
|
25
|
+
* session.on((event) => {
|
|
26
|
+
* if (event.type === "assistant.message") {
|
|
27
|
+
* console.log(event.data.content);
|
|
28
|
+
* }
|
|
29
|
+
* });
|
|
30
|
+
* await session.send({ prompt: "Hello!" });
|
|
31
|
+
*
|
|
32
|
+
* // Clean up
|
|
33
|
+
* await session.disconnect();
|
|
34
|
+
* await client.stop();
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
4
37
|
export declare class CopilotClient {
|
|
5
38
|
private cliProcess;
|
|
6
39
|
private connection;
|
|
@@ -13,12 +46,14 @@ export declare class CopilotClient {
|
|
|
13
46
|
private options;
|
|
14
47
|
private isExternalServer;
|
|
15
48
|
private forceStopping;
|
|
49
|
+
private onListModels?;
|
|
16
50
|
private modelsCache;
|
|
17
51
|
private modelsCacheLock;
|
|
18
52
|
private sessionLifecycleHandlers;
|
|
19
53
|
private typedLifecycleHandlers;
|
|
20
54
|
private _rpc;
|
|
21
55
|
private processExitPromise;
|
|
56
|
+
private negotiatedProtocolVersion;
|
|
22
57
|
/**
|
|
23
58
|
* Typed server-scoped RPC methods.
|
|
24
59
|
* @throws Error if the client is not connected
|
|
@@ -217,14 +252,18 @@ export declare class CopilotClient {
|
|
|
217
252
|
/**
|
|
218
253
|
* List available models with their metadata.
|
|
219
254
|
*
|
|
255
|
+
* If an `onListModels` handler was provided in the client options,
|
|
256
|
+
* it is called instead of querying the CLI server.
|
|
257
|
+
*
|
|
220
258
|
* Results are cached after the first successful call to avoid rate limiting.
|
|
221
259
|
* The cache is cleared when the client disconnects.
|
|
222
260
|
*
|
|
223
|
-
* @throws Error if not
|
|
261
|
+
* @throws Error if not connected (when no custom handler is set)
|
|
224
262
|
*/
|
|
225
263
|
listModels(): Promise<ModelInfo[]>;
|
|
226
264
|
/**
|
|
227
|
-
* Verify that the server's protocol version
|
|
265
|
+
* Verify that the server's protocol version is within the supported range
|
|
266
|
+
* and store the negotiated version.
|
|
228
267
|
*/
|
|
229
268
|
private verifyProtocolVersion;
|
|
230
269
|
/**
|
|
@@ -383,6 +422,18 @@ export declare class CopilotClient {
|
|
|
383
422
|
private handleSessionLifecycleNotification;
|
|
384
423
|
private handleUserInputRequest;
|
|
385
424
|
private handleHooksInvoke;
|
|
425
|
+
/**
|
|
426
|
+
* Handles a v2-style tool.call RPC request from the server.
|
|
427
|
+
* Looks up the session and tool handler, executes it, and returns the result
|
|
428
|
+
* in the v2 response format.
|
|
429
|
+
*/
|
|
430
|
+
private handleToolCallRequestV2;
|
|
431
|
+
/**
|
|
432
|
+
* Handles a v2-style permission.request RPC request from the server.
|
|
433
|
+
*/
|
|
434
|
+
private handlePermissionRequestV2;
|
|
435
|
+
private normalizeToolResultV2;
|
|
436
|
+
private isToolResultObject;
|
|
386
437
|
/**
|
|
387
438
|
* Attempt to reconnect to the server
|
|
388
439
|
*/
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { Socket } from "node:net";
|
|
4
5
|
import { dirname, join } from "node:path";
|
|
@@ -10,7 +11,8 @@ import {
|
|
|
10
11
|
} from "vscode-jsonrpc/node.js";
|
|
11
12
|
import { createServerRpc } from "./generated/rpc.js";
|
|
12
13
|
import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
|
|
13
|
-
import { CopilotSession } from "./session.js";
|
|
14
|
+
import { CopilotSession, NO_RESULT_PERMISSION_V2_ERROR } from "./session.js";
|
|
15
|
+
const MIN_PROTOCOL_VERSION = 2;
|
|
14
16
|
function isZodSchema(value) {
|
|
15
17
|
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
16
18
|
}
|
|
@@ -45,6 +47,7 @@ class CopilotClient {
|
|
|
45
47
|
options;
|
|
46
48
|
isExternalServer = false;
|
|
47
49
|
forceStopping = false;
|
|
50
|
+
onListModels;
|
|
48
51
|
modelsCache = null;
|
|
49
52
|
modelsCacheLock = Promise.resolve();
|
|
50
53
|
sessionLifecycleHandlers = /* @__PURE__ */ new Set();
|
|
@@ -52,6 +55,7 @@ class CopilotClient {
|
|
|
52
55
|
_rpc = null;
|
|
53
56
|
processExitPromise = null;
|
|
54
57
|
// Rejects when CLI process exits
|
|
58
|
+
negotiatedProtocolVersion = null;
|
|
55
59
|
/**
|
|
56
60
|
* Typed server-scoped RPC methods.
|
|
57
61
|
* @throws Error if the client is not connected
|
|
@@ -109,8 +113,9 @@ class CopilotClient {
|
|
|
109
113
|
if (options.isChildProcess) {
|
|
110
114
|
this.isExternalServer = true;
|
|
111
115
|
}
|
|
116
|
+
this.onListModels = options.onListModels;
|
|
112
117
|
this.options = {
|
|
113
|
-
cliPath: options.cliPath || getBundledCliPath(),
|
|
118
|
+
cliPath: options.cliUrl ? void 0 : options.cliPath || getBundledCliPath(),
|
|
114
119
|
cliArgs: options.cliArgs ?? [],
|
|
115
120
|
cwd: options.cwd ?? process.cwd(),
|
|
116
121
|
port: options.port || 0,
|
|
@@ -376,36 +381,8 @@ class CopilotClient {
|
|
|
376
381
|
throw new Error("Client not connected. Call start() first.");
|
|
377
382
|
}
|
|
378
383
|
}
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
sessionId: config.sessionId,
|
|
382
|
-
clientName: config.clientName,
|
|
383
|
-
reasoningEffort: config.reasoningEffort,
|
|
384
|
-
tools: config.tools?.map((tool) => ({
|
|
385
|
-
name: tool.name,
|
|
386
|
-
description: tool.description,
|
|
387
|
-
parameters: toJsonSchema(tool.parameters),
|
|
388
|
-
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
389
|
-
})),
|
|
390
|
-
systemMessage: config.systemMessage,
|
|
391
|
-
availableTools: config.availableTools,
|
|
392
|
-
excludedTools: config.excludedTools,
|
|
393
|
-
provider: config.provider,
|
|
394
|
-
requestPermission: true,
|
|
395
|
-
requestUserInput: !!config.onUserInputRequest,
|
|
396
|
-
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
397
|
-
workingDirectory: config.workingDirectory,
|
|
398
|
-
streaming: config.streaming,
|
|
399
|
-
mcpServers: config.mcpServers,
|
|
400
|
-
envValueMode: "direct",
|
|
401
|
-
customAgents: config.customAgents,
|
|
402
|
-
configDir: config.configDir,
|
|
403
|
-
skillDirectories: config.skillDirectories,
|
|
404
|
-
disabledSkills: config.disabledSkills,
|
|
405
|
-
infiniteSessions: config.infiniteSessions
|
|
406
|
-
});
|
|
407
|
-
const { sessionId, workspacePath } = response;
|
|
408
|
-
const session = new CopilotSession(sessionId, this.connection, workspacePath);
|
|
384
|
+
const sessionId = config.sessionId ?? randomUUID();
|
|
385
|
+
const session = new CopilotSession(sessionId, this.connection);
|
|
409
386
|
session.registerTools(config.tools);
|
|
410
387
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
411
388
|
if (config.onUserInputRequest) {
|
|
@@ -414,7 +391,46 @@ class CopilotClient {
|
|
|
414
391
|
if (config.hooks) {
|
|
415
392
|
session.registerHooks(config.hooks);
|
|
416
393
|
}
|
|
394
|
+
if (config.onEvent) {
|
|
395
|
+
session.on(config.onEvent);
|
|
396
|
+
}
|
|
417
397
|
this.sessions.set(sessionId, session);
|
|
398
|
+
try {
|
|
399
|
+
const response = await this.connection.sendRequest("session.create", {
|
|
400
|
+
model: config.model,
|
|
401
|
+
sessionId,
|
|
402
|
+
clientName: config.clientName,
|
|
403
|
+
reasoningEffort: config.reasoningEffort,
|
|
404
|
+
tools: config.tools?.map((tool) => ({
|
|
405
|
+
name: tool.name,
|
|
406
|
+
description: tool.description,
|
|
407
|
+
parameters: toJsonSchema(tool.parameters),
|
|
408
|
+
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
409
|
+
})),
|
|
410
|
+
systemMessage: config.systemMessage,
|
|
411
|
+
availableTools: config.availableTools,
|
|
412
|
+
excludedTools: config.excludedTools,
|
|
413
|
+
provider: config.provider,
|
|
414
|
+
requestPermission: true,
|
|
415
|
+
requestUserInput: !!config.onUserInputRequest,
|
|
416
|
+
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
417
|
+
workingDirectory: config.workingDirectory,
|
|
418
|
+
streaming: config.streaming,
|
|
419
|
+
mcpServers: config.mcpServers,
|
|
420
|
+
envValueMode: "direct",
|
|
421
|
+
customAgents: config.customAgents,
|
|
422
|
+
agent: config.agent,
|
|
423
|
+
configDir: config.configDir,
|
|
424
|
+
skillDirectories: config.skillDirectories,
|
|
425
|
+
disabledSkills: config.disabledSkills,
|
|
426
|
+
infiniteSessions: config.infiniteSessions
|
|
427
|
+
});
|
|
428
|
+
const { workspacePath } = response;
|
|
429
|
+
session["_workspacePath"] = workspacePath;
|
|
430
|
+
} catch (e) {
|
|
431
|
+
this.sessions.delete(sessionId);
|
|
432
|
+
throw e;
|
|
433
|
+
}
|
|
418
434
|
return session;
|
|
419
435
|
}
|
|
420
436
|
/**
|
|
@@ -454,37 +470,7 @@ class CopilotClient {
|
|
|
454
470
|
throw new Error("Client not connected. Call start() first.");
|
|
455
471
|
}
|
|
456
472
|
}
|
|
457
|
-
const
|
|
458
|
-
sessionId,
|
|
459
|
-
clientName: config.clientName,
|
|
460
|
-
model: config.model,
|
|
461
|
-
reasoningEffort: config.reasoningEffort,
|
|
462
|
-
systemMessage: config.systemMessage,
|
|
463
|
-
availableTools: config.availableTools,
|
|
464
|
-
excludedTools: config.excludedTools,
|
|
465
|
-
tools: config.tools?.map((tool) => ({
|
|
466
|
-
name: tool.name,
|
|
467
|
-
description: tool.description,
|
|
468
|
-
parameters: toJsonSchema(tool.parameters),
|
|
469
|
-
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
470
|
-
})),
|
|
471
|
-
provider: config.provider,
|
|
472
|
-
requestPermission: true,
|
|
473
|
-
requestUserInput: !!config.onUserInputRequest,
|
|
474
|
-
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
475
|
-
workingDirectory: config.workingDirectory,
|
|
476
|
-
configDir: config.configDir,
|
|
477
|
-
streaming: config.streaming,
|
|
478
|
-
mcpServers: config.mcpServers,
|
|
479
|
-
envValueMode: "direct",
|
|
480
|
-
customAgents: config.customAgents,
|
|
481
|
-
skillDirectories: config.skillDirectories,
|
|
482
|
-
disabledSkills: config.disabledSkills,
|
|
483
|
-
infiniteSessions: config.infiniteSessions,
|
|
484
|
-
disableResume: config.disableResume
|
|
485
|
-
});
|
|
486
|
-
const { sessionId: resumedSessionId, workspacePath } = response;
|
|
487
|
-
const session = new CopilotSession(resumedSessionId, this.connection, workspacePath);
|
|
473
|
+
const session = new CopilotSession(sessionId, this.connection);
|
|
488
474
|
session.registerTools(config.tools);
|
|
489
475
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
490
476
|
if (config.onUserInputRequest) {
|
|
@@ -493,7 +479,47 @@ class CopilotClient {
|
|
|
493
479
|
if (config.hooks) {
|
|
494
480
|
session.registerHooks(config.hooks);
|
|
495
481
|
}
|
|
496
|
-
|
|
482
|
+
if (config.onEvent) {
|
|
483
|
+
session.on(config.onEvent);
|
|
484
|
+
}
|
|
485
|
+
this.sessions.set(sessionId, session);
|
|
486
|
+
try {
|
|
487
|
+
const response = await this.connection.sendRequest("session.resume", {
|
|
488
|
+
sessionId,
|
|
489
|
+
clientName: config.clientName,
|
|
490
|
+
model: config.model,
|
|
491
|
+
reasoningEffort: config.reasoningEffort,
|
|
492
|
+
systemMessage: config.systemMessage,
|
|
493
|
+
availableTools: config.availableTools,
|
|
494
|
+
excludedTools: config.excludedTools,
|
|
495
|
+
tools: config.tools?.map((tool) => ({
|
|
496
|
+
name: tool.name,
|
|
497
|
+
description: tool.description,
|
|
498
|
+
parameters: toJsonSchema(tool.parameters),
|
|
499
|
+
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
500
|
+
})),
|
|
501
|
+
provider: config.provider,
|
|
502
|
+
requestPermission: true,
|
|
503
|
+
requestUserInput: !!config.onUserInputRequest,
|
|
504
|
+
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
505
|
+
workingDirectory: config.workingDirectory,
|
|
506
|
+
configDir: config.configDir,
|
|
507
|
+
streaming: config.streaming,
|
|
508
|
+
mcpServers: config.mcpServers,
|
|
509
|
+
envValueMode: "direct",
|
|
510
|
+
customAgents: config.customAgents,
|
|
511
|
+
agent: config.agent,
|
|
512
|
+
skillDirectories: config.skillDirectories,
|
|
513
|
+
disabledSkills: config.disabledSkills,
|
|
514
|
+
infiniteSessions: config.infiniteSessions,
|
|
515
|
+
disableResume: config.disableResume
|
|
516
|
+
});
|
|
517
|
+
const { workspacePath } = response;
|
|
518
|
+
session["_workspacePath"] = workspacePath;
|
|
519
|
+
} catch (e) {
|
|
520
|
+
this.sessions.delete(sessionId);
|
|
521
|
+
throw e;
|
|
522
|
+
}
|
|
497
523
|
return session;
|
|
498
524
|
}
|
|
499
525
|
/**
|
|
@@ -554,15 +580,15 @@ class CopilotClient {
|
|
|
554
580
|
/**
|
|
555
581
|
* List available models with their metadata.
|
|
556
582
|
*
|
|
583
|
+
* If an `onListModels` handler was provided in the client options,
|
|
584
|
+
* it is called instead of querying the CLI server.
|
|
585
|
+
*
|
|
557
586
|
* Results are cached after the first successful call to avoid rate limiting.
|
|
558
587
|
* The cache is cleared when the client disconnects.
|
|
559
588
|
*
|
|
560
|
-
* @throws Error if not
|
|
589
|
+
* @throws Error if not connected (when no custom handler is set)
|
|
561
590
|
*/
|
|
562
591
|
async listModels() {
|
|
563
|
-
if (!this.connection) {
|
|
564
|
-
throw new Error("Client not connected");
|
|
565
|
-
}
|
|
566
592
|
await this.modelsCacheLock;
|
|
567
593
|
let resolveLock;
|
|
568
594
|
this.modelsCacheLock = new Promise((resolve) => {
|
|
@@ -572,20 +598,29 @@ class CopilotClient {
|
|
|
572
598
|
if (this.modelsCache !== null) {
|
|
573
599
|
return [...this.modelsCache];
|
|
574
600
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
601
|
+
let models;
|
|
602
|
+
if (this.onListModels) {
|
|
603
|
+
models = await this.onListModels();
|
|
604
|
+
} else {
|
|
605
|
+
if (!this.connection) {
|
|
606
|
+
throw new Error("Client not connected");
|
|
607
|
+
}
|
|
608
|
+
const result = await this.connection.sendRequest("models.list", {});
|
|
609
|
+
const response = result;
|
|
610
|
+
models = response.models;
|
|
611
|
+
}
|
|
612
|
+
this.modelsCache = [...models];
|
|
579
613
|
return [...models];
|
|
580
614
|
} finally {
|
|
581
615
|
resolveLock();
|
|
582
616
|
}
|
|
583
617
|
}
|
|
584
618
|
/**
|
|
585
|
-
* Verify that the server's protocol version
|
|
619
|
+
* Verify that the server's protocol version is within the supported range
|
|
620
|
+
* and store the negotiated version.
|
|
586
621
|
*/
|
|
587
622
|
async verifyProtocolVersion() {
|
|
588
|
-
const
|
|
623
|
+
const maxVersion = getSdkProtocolVersion();
|
|
589
624
|
let pingResult;
|
|
590
625
|
if (this.processExitPromise) {
|
|
591
626
|
pingResult = await Promise.race([this.ping(), this.processExitPromise]);
|
|
@@ -595,14 +630,15 @@ class CopilotClient {
|
|
|
595
630
|
const serverVersion = pingResult.protocolVersion;
|
|
596
631
|
if (serverVersion === void 0) {
|
|
597
632
|
throw new Error(
|
|
598
|
-
`SDK protocol version mismatch: SDK
|
|
633
|
+
`SDK protocol version mismatch: SDK supports versions ${MIN_PROTOCOL_VERSION}-${maxVersion}, but server does not report a protocol version. Please update your server to ensure compatibility.`
|
|
599
634
|
);
|
|
600
635
|
}
|
|
601
|
-
if (serverVersion
|
|
636
|
+
if (serverVersion < MIN_PROTOCOL_VERSION || serverVersion > maxVersion) {
|
|
602
637
|
throw new Error(
|
|
603
|
-
`SDK protocol version mismatch: SDK
|
|
638
|
+
`SDK protocol version mismatch: SDK supports versions ${MIN_PROTOCOL_VERSION}-${maxVersion}, but server reports version ${serverVersion}. Please update your SDK or server to ensure compatibility.`
|
|
604
639
|
);
|
|
605
640
|
}
|
|
641
|
+
this.negotiatedProtocolVersion = serverVersion;
|
|
606
642
|
}
|
|
607
643
|
/**
|
|
608
644
|
* Gets the ID of the most recently updated session.
|
|
@@ -786,6 +822,11 @@ class CopilotClient {
|
|
|
786
822
|
if (this.options.githubToken) {
|
|
787
823
|
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.githubToken;
|
|
788
824
|
}
|
|
825
|
+
if (!this.options.cliPath) {
|
|
826
|
+
throw new Error(
|
|
827
|
+
"Path to Copilot CLI is required. Please provide it via the cliPath option, or use cliUrl to rely on a remote CLI."
|
|
828
|
+
);
|
|
829
|
+
}
|
|
789
830
|
if (!existsSync(this.options.cliPath)) {
|
|
790
831
|
throw new Error(
|
|
791
832
|
`Copilot CLI not found at ${this.options.cliPath}. Ensure @github/copilot is installed.`
|
|
@@ -975,6 +1016,14 @@ stderr: ${stderrOutput}`
|
|
|
975
1016
|
this.connection.onNotification("session.lifecycle", (notification) => {
|
|
976
1017
|
this.handleSessionLifecycleNotification(notification);
|
|
977
1018
|
});
|
|
1019
|
+
this.connection.onRequest(
|
|
1020
|
+
"tool.call",
|
|
1021
|
+
async (params) => await this.handleToolCallRequestV2(params)
|
|
1022
|
+
);
|
|
1023
|
+
this.connection.onRequest(
|
|
1024
|
+
"permission.request",
|
|
1025
|
+
async (params) => await this.handlePermissionRequestV2(params)
|
|
1026
|
+
);
|
|
978
1027
|
this.connection.onRequest(
|
|
979
1028
|
"userInput.request",
|
|
980
1029
|
async (params) => await this.handleUserInputRequest(params)
|
|
@@ -1047,6 +1096,101 @@ stderr: ${stderrOutput}`
|
|
|
1047
1096
|
const output = await session._handleHooksInvoke(params.hookType, params.input);
|
|
1048
1097
|
return { output };
|
|
1049
1098
|
}
|
|
1099
|
+
// ========================================================================
|
|
1100
|
+
// Protocol v2 backward-compatibility adapters
|
|
1101
|
+
// ========================================================================
|
|
1102
|
+
/**
|
|
1103
|
+
* Handles a v2-style tool.call RPC request from the server.
|
|
1104
|
+
* Looks up the session and tool handler, executes it, and returns the result
|
|
1105
|
+
* in the v2 response format.
|
|
1106
|
+
*/
|
|
1107
|
+
async handleToolCallRequestV2(params) {
|
|
1108
|
+
if (!params || typeof params.sessionId !== "string" || typeof params.toolCallId !== "string" || typeof params.toolName !== "string") {
|
|
1109
|
+
throw new Error("Invalid tool call payload");
|
|
1110
|
+
}
|
|
1111
|
+
const session = this.sessions.get(params.sessionId);
|
|
1112
|
+
if (!session) {
|
|
1113
|
+
throw new Error(`Unknown session ${params.sessionId}`);
|
|
1114
|
+
}
|
|
1115
|
+
const handler = session.getToolHandler(params.toolName);
|
|
1116
|
+
if (!handler) {
|
|
1117
|
+
return {
|
|
1118
|
+
result: {
|
|
1119
|
+
textResultForLlm: `Tool '${params.toolName}' is not supported by this client instance.`,
|
|
1120
|
+
resultType: "failure",
|
|
1121
|
+
error: `tool '${params.toolName}' not supported`,
|
|
1122
|
+
toolTelemetry: {}
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
try {
|
|
1127
|
+
const invocation = {
|
|
1128
|
+
sessionId: params.sessionId,
|
|
1129
|
+
toolCallId: params.toolCallId,
|
|
1130
|
+
toolName: params.toolName,
|
|
1131
|
+
arguments: params.arguments
|
|
1132
|
+
};
|
|
1133
|
+
const result = await handler(params.arguments, invocation);
|
|
1134
|
+
return { result: this.normalizeToolResultV2(result) };
|
|
1135
|
+
} catch (error) {
|
|
1136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1137
|
+
return {
|
|
1138
|
+
result: {
|
|
1139
|
+
textResultForLlm: "Invoking this tool produced an error. Detailed information is not available.",
|
|
1140
|
+
resultType: "failure",
|
|
1141
|
+
error: message,
|
|
1142
|
+
toolTelemetry: {}
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Handles a v2-style permission.request RPC request from the server.
|
|
1149
|
+
*/
|
|
1150
|
+
async handlePermissionRequestV2(params) {
|
|
1151
|
+
if (!params || typeof params.sessionId !== "string" || !params.permissionRequest) {
|
|
1152
|
+
throw new Error("Invalid permission request payload");
|
|
1153
|
+
}
|
|
1154
|
+
const session = this.sessions.get(params.sessionId);
|
|
1155
|
+
if (!session) {
|
|
1156
|
+
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1157
|
+
}
|
|
1158
|
+
try {
|
|
1159
|
+
const result = await session._handlePermissionRequestV2(params.permissionRequest);
|
|
1160
|
+
return { result };
|
|
1161
|
+
} catch (error) {
|
|
1162
|
+
if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
|
|
1163
|
+
throw error;
|
|
1164
|
+
}
|
|
1165
|
+
return {
|
|
1166
|
+
result: {
|
|
1167
|
+
kind: "denied-no-approval-rule-and-could-not-request-from-user"
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
normalizeToolResultV2(result) {
|
|
1173
|
+
if (result === void 0 || result === null) {
|
|
1174
|
+
return {
|
|
1175
|
+
textResultForLlm: "Tool returned no result",
|
|
1176
|
+
resultType: "failure",
|
|
1177
|
+
error: "tool returned no result",
|
|
1178
|
+
toolTelemetry: {}
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
if (this.isToolResultObject(result)) {
|
|
1182
|
+
return result;
|
|
1183
|
+
}
|
|
1184
|
+
const textResult = typeof result === "string" ? result : JSON.stringify(result);
|
|
1185
|
+
return {
|
|
1186
|
+
textResultForLlm: textResult,
|
|
1187
|
+
resultType: "success",
|
|
1188
|
+
toolTelemetry: {}
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
isToolResultObject(value) {
|
|
1192
|
+
return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
|
|
1193
|
+
}
|
|
1050
1194
|
/**
|
|
1051
1195
|
* Attempt to reconnect to the server
|
|
1052
1196
|
*/
|
package/dist/extension.d.ts
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type { CopilotSession } from "./session.js";
|
|
2
|
+
import type { PermissionHandler, ResumeSessionConfig } from "./types.js";
|
|
3
|
+
export type JoinSessionConfig = Omit<ResumeSessionConfig, "onPermissionRequest"> & {
|
|
4
|
+
onPermissionRequest?: PermissionHandler;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Joins the current foreground session.
|
|
8
|
+
*
|
|
9
|
+
* @param config - Configuration to add to the session
|
|
10
|
+
* @returns A promise that resolves with the joined session
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { joinSession } from "@github/copilot-sdk/extension";
|
|
15
|
+
*
|
|
16
|
+
* const session = await joinSession({ tools: [myTool] });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function joinSession(config?: JoinSessionConfig): Promise<CopilotSession>;
|
package/dist/extension.js
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { CopilotClient } from "./client.js";
|
|
2
|
-
const
|
|
2
|
+
const defaultJoinSessionPermissionHandler = () => ({
|
|
3
|
+
kind: "no-result"
|
|
4
|
+
});
|
|
5
|
+
async function joinSession(config = {}) {
|
|
6
|
+
const sessionId = process.env.SESSION_ID;
|
|
7
|
+
if (!sessionId) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"joinSession() is intended for extensions running as child processes of the Copilot CLI."
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
const client = new CopilotClient({ isChildProcess: true });
|
|
13
|
+
return client.resumeSession(sessionId, {
|
|
14
|
+
...config,
|
|
15
|
+
onPermissionRequest: config.onPermissionRequest ?? defaultJoinSessionPermissionHandler,
|
|
16
|
+
disableResume: config.disableResume ?? true
|
|
17
|
+
});
|
|
18
|
+
}
|
|
3
19
|
export {
|
|
4
|
-
|
|
20
|
+
joinSession
|
|
5
21
|
};
|