@github/copilot-sdk 0.3.0 → 1.0.0-beta.10
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 +52 -42
- package/dist/canvas.d.ts +126 -0
- package/dist/canvas.js +49 -0
- package/dist/cjs/canvas.js +75 -0
- package/dist/cjs/client.js +578 -293
- package/dist/cjs/extension.js +9 -2
- package/dist/cjs/generated/rpc.js +1334 -13
- package/dist/cjs/index.js +22 -7
- package/dist/cjs/session.js +179 -93
- package/dist/cjs/sessionFsProvider.js +34 -0
- package/dist/cjs/toolSet.js +107 -0
- package/dist/cjs/types.js +38 -4
- package/dist/client.d.ts +69 -70
- package/dist/client.js +586 -295
- package/dist/extension.d.ts +3 -1
- package/dist/extension.js +10 -2
- package/dist/generated/rpc.d.ts +10532 -839
- package/dist/generated/rpc.js +1333 -13
- package/dist/generated/session-events.d.ts +2353 -213
- package/dist/index.d.ts +6 -2
- package/dist/index.js +15 -2
- package/dist/session.d.ts +18 -186
- package/dist/session.js +179 -92
- package/dist/sessionFsProvider.d.ts +29 -2
- package/dist/sessionFsProvider.js +34 -0
- package/dist/toolSet.d.ts +75 -0
- package/dist/toolSet.js +82 -0
- package/dist/types.d.ts +750 -112
- package/dist/types.js +36 -3
- package/docs/agent-author.md +31 -7
- package/docs/examples.md +23 -16
- package/package.json +3 -3
package/dist/client.js
CHANGED
|
@@ -7,16 +7,23 @@ import { dirname, join } from "node:path";
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import {
|
|
9
9
|
createMessageConnection,
|
|
10
|
+
ErrorCodes,
|
|
11
|
+
ResponseError,
|
|
10
12
|
StreamMessageReader,
|
|
11
13
|
StreamMessageWriter
|
|
12
14
|
} from "vscode-jsonrpc/node.js";
|
|
13
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
createServerRpc,
|
|
17
|
+
createInternalServerRpc,
|
|
18
|
+
registerClientSessionApiHandlers
|
|
19
|
+
} from "./generated/rpc.js";
|
|
14
20
|
import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
|
|
15
|
-
import { CopilotSession
|
|
21
|
+
import { CopilotSession } from "./session.js";
|
|
16
22
|
import { createSessionFsAdapter } from "./sessionFsProvider.js";
|
|
17
23
|
import { getTraceContext } from "./telemetry.js";
|
|
24
|
+
import { ToolSet } from "./toolSet.js";
|
|
18
25
|
import { defaultJoinSessionPermissionHandler } from "./types.js";
|
|
19
|
-
const MIN_PROTOCOL_VERSION =
|
|
26
|
+
const MIN_PROTOCOL_VERSION = 3;
|
|
20
27
|
function isZodSchema(value) {
|
|
21
28
|
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
22
29
|
}
|
|
@@ -27,6 +34,53 @@ function toJsonSchema(parameters) {
|
|
|
27
34
|
}
|
|
28
35
|
return parameters;
|
|
29
36
|
}
|
|
37
|
+
function toWireMcpServers(mcpServers) {
|
|
38
|
+
if (!mcpServers) return void 0;
|
|
39
|
+
return Object.fromEntries(
|
|
40
|
+
Object.entries(mcpServers).map(([name, server]) => {
|
|
41
|
+
if ("workingDirectory" in server) {
|
|
42
|
+
const { workingDirectory, ...rest } = server;
|
|
43
|
+
return [name, { ...rest, cwd: workingDirectory }];
|
|
44
|
+
}
|
|
45
|
+
return [name, server];
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
function toWireCustomAgents(agents) {
|
|
50
|
+
if (!agents) return void 0;
|
|
51
|
+
return agents.map((agent) => {
|
|
52
|
+
if (!agent.mcpServers) return agent;
|
|
53
|
+
const { mcpServers, ...rest } = agent;
|
|
54
|
+
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function toWireLargeOutput(config) {
|
|
58
|
+
if (!config) return void 0;
|
|
59
|
+
const { outputDirectory, ...rest } = config;
|
|
60
|
+
const wire = { ...rest };
|
|
61
|
+
if (outputDirectory !== void 0) {
|
|
62
|
+
wire.outputDir = outputDirectory;
|
|
63
|
+
}
|
|
64
|
+
return wire;
|
|
65
|
+
}
|
|
66
|
+
function toolFilterListToArray(value) {
|
|
67
|
+
if (value === void 0) {
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
return value instanceof ToolSet ? value.toArray() : value;
|
|
71
|
+
}
|
|
72
|
+
function validateToolFilterList(field, list) {
|
|
73
|
+
if (!list) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const entry of list) {
|
|
77
|
+
if (entry === "*") {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Invalid ${field} entry '*': there is no bare wildcard. Use one or more of \`new ToolSet().addBuiltIn('*')\`, \`.addMcp('*')\`, or \`.addCustom('*')\` to target a specific source.`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
30
84
|
function extractTransformCallbacks(systemMessage) {
|
|
31
85
|
if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
|
|
32
86
|
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
@@ -80,15 +134,23 @@ class CopilotClient {
|
|
|
80
134
|
cliProcess = null;
|
|
81
135
|
connection = null;
|
|
82
136
|
socket = null;
|
|
83
|
-
|
|
137
|
+
runtimePort = null;
|
|
84
138
|
actualHost = "localhost";
|
|
85
139
|
state = "disconnected";
|
|
86
140
|
sessions = /* @__PURE__ */ new Map();
|
|
87
141
|
stderrBuffer = "";
|
|
88
142
|
// Captures CLI stderr for error messages
|
|
143
|
+
/** Resolved connection mode chosen in the constructor. */
|
|
144
|
+
connectionConfig;
|
|
145
|
+
/** Resolved path to the runtime executable (only used for child-process kinds). */
|
|
146
|
+
resolvedCliPath;
|
|
147
|
+
/** Resolved environment passed to the spawned runtime. */
|
|
148
|
+
resolvedEnv;
|
|
89
149
|
options;
|
|
90
150
|
isExternalServer = false;
|
|
91
151
|
forceStopping = false;
|
|
152
|
+
/** Token sent in `connect`; auto-generated when the SDK spawns its own CLI in TCP mode. */
|
|
153
|
+
effectiveConnectionToken;
|
|
92
154
|
onListModels;
|
|
93
155
|
onGetTraceContext;
|
|
94
156
|
modelsCache = null;
|
|
@@ -96,6 +158,7 @@ class CopilotClient {
|
|
|
96
158
|
sessionLifecycleHandlers = /* @__PURE__ */ new Set();
|
|
97
159
|
typedLifecycleHandlers = /* @__PURE__ */ new Map();
|
|
98
160
|
_rpc = null;
|
|
161
|
+
_internalRpc = null;
|
|
99
162
|
processExitPromise = null;
|
|
100
163
|
// Rejects when CLI process exits
|
|
101
164
|
negotiatedProtocolVersion = null;
|
|
@@ -114,77 +177,107 @@ class CopilotClient {
|
|
|
114
177
|
}
|
|
115
178
|
return this._rpc;
|
|
116
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Internal RPC surface (e.g. handshake helpers). Not part of the public API.
|
|
182
|
+
* @internal
|
|
183
|
+
*/
|
|
184
|
+
get internalRpc() {
|
|
185
|
+
if (!this.connection) {
|
|
186
|
+
throw new Error("Client is not connected. Call start() first.");
|
|
187
|
+
}
|
|
188
|
+
if (!this._internalRpc) {
|
|
189
|
+
this._internalRpc = createInternalServerRpc(this.connection);
|
|
190
|
+
}
|
|
191
|
+
return this._internalRpc;
|
|
192
|
+
}
|
|
117
193
|
/**
|
|
118
194
|
* Creates a new CopilotClient instance.
|
|
119
195
|
*
|
|
120
196
|
* @param options - Configuration options for the client
|
|
121
|
-
* @throws Error if mutually exclusive options are provided (e.g., cliUrl with useStdio or cliPath)
|
|
122
197
|
*
|
|
123
198
|
* @example
|
|
124
199
|
* ```typescript
|
|
125
|
-
* // Default
|
|
200
|
+
* // Default: spawns the bundled runtime over stdio
|
|
126
201
|
* const client = new CopilotClient();
|
|
127
202
|
*
|
|
128
|
-
* // Connect to an existing
|
|
129
|
-
* const client = new CopilotClient({
|
|
203
|
+
* // Connect to an existing runtime
|
|
204
|
+
* const client = new CopilotClient({
|
|
205
|
+
* connection: RuntimeConnection.forUri("localhost:3000"),
|
|
206
|
+
* });
|
|
130
207
|
*
|
|
131
|
-
* //
|
|
208
|
+
* // Spawn the runtime over TCP on a chosen port
|
|
132
209
|
* const client = new CopilotClient({
|
|
133
|
-
*
|
|
134
|
-
*
|
|
210
|
+
* connection: RuntimeConnection.forTcp({ port: 9001 }),
|
|
211
|
+
* });
|
|
212
|
+
*
|
|
213
|
+
* // Use a custom runtime binary
|
|
214
|
+
* const client = new CopilotClient({
|
|
215
|
+
* connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }),
|
|
216
|
+
* logLevel: "debug",
|
|
135
217
|
* });
|
|
136
218
|
* ```
|
|
137
219
|
*/
|
|
138
220
|
constructor(options = {}) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
if (options.isChildProcess && (options.cliUrl || options.useStdio === false)) {
|
|
221
|
+
const conn = options._internalConnection ?? options.connection ?? { kind: "stdio" };
|
|
222
|
+
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
143
223
|
throw new Error(
|
|
144
|
-
"
|
|
224
|
+
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
145
225
|
);
|
|
146
226
|
}
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
227
|
+
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
228
|
+
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
229
|
+
throw new Error("connectionToken must be a non-empty string");
|
|
230
|
+
}
|
|
151
231
|
}
|
|
232
|
+
this.connectionConfig = conn;
|
|
152
233
|
if (options.sessionFs) {
|
|
153
234
|
this.validateSessionFsConfig(options.sessionFs);
|
|
154
235
|
}
|
|
155
|
-
if (
|
|
156
|
-
const { host, port } = this.parseCliUrl(
|
|
236
|
+
if (conn.kind === "uri") {
|
|
237
|
+
const { host, port } = this.parseCliUrl(conn.url);
|
|
157
238
|
this.actualHost = host;
|
|
158
|
-
this.
|
|
239
|
+
this.runtimePort = port;
|
|
159
240
|
this.isExternalServer = true;
|
|
160
|
-
}
|
|
161
|
-
if (options.isChildProcess) {
|
|
241
|
+
} else if (conn.kind === "parent-process") {
|
|
162
242
|
this.isExternalServer = true;
|
|
163
243
|
}
|
|
244
|
+
if (conn.kind === "tcp") {
|
|
245
|
+
this.effectiveConnectionToken = conn.connectionToken ?? randomUUID();
|
|
246
|
+
} else if (conn.kind === "uri") {
|
|
247
|
+
this.effectiveConnectionToken = conn.connectionToken;
|
|
248
|
+
}
|
|
164
249
|
this.onListModels = options.onListModels;
|
|
165
250
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
166
251
|
this.sessionFsConfig = options.sessionFs ?? null;
|
|
167
252
|
const effectiveEnv = options.env ?? process.env;
|
|
253
|
+
this.resolvedEnv = effectiveEnv;
|
|
254
|
+
this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
|
|
255
|
+
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
256
|
+
this.connectionExtraArgs = [...connArgs];
|
|
168
257
|
this.options = {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
cwd: options.cwd ?? process.cwd(),
|
|
172
|
-
port: options.port || 0,
|
|
173
|
-
useStdio: options.cliUrl ? false : options.useStdio ?? true,
|
|
174
|
-
// Default to stdio unless cliUrl is provided
|
|
175
|
-
isChildProcess: options.isChildProcess ?? false,
|
|
176
|
-
cliUrl: options.cliUrl,
|
|
177
|
-
logLevel: options.logLevel || "debug",
|
|
178
|
-
autoStart: options.autoStart ?? true,
|
|
179
|
-
autoRestart: false,
|
|
180
|
-
env: effectiveEnv,
|
|
258
|
+
workingDirectory: options.workingDirectory ?? process.cwd(),
|
|
259
|
+
logLevel: options.logLevel,
|
|
181
260
|
gitHubToken: options.gitHubToken,
|
|
182
|
-
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true
|
|
261
|
+
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true.
|
|
183
262
|
useLoggedInUser: options.useLoggedInUser ?? (options.gitHubToken ? false : true),
|
|
184
263
|
telemetry: options.telemetry,
|
|
185
|
-
|
|
264
|
+
baseDirectory: options.baseDirectory,
|
|
265
|
+
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
266
|
+
enableRemoteSessions: options.enableRemoteSessions ?? false,
|
|
267
|
+
mode: options.mode ?? "copilot-cli"
|
|
186
268
|
};
|
|
269
|
+
if (this.options.mode === "empty") {
|
|
270
|
+
const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
|
|
271
|
+
// can't enforce it from here.
|
|
272
|
+
conn.kind === "uri" || conn.kind === "parent-process";
|
|
273
|
+
if (!hasPersistence) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
"CopilotClient was created with mode: 'empty' but neither 'baseDirectory' nor 'sessionFs' was set. Empty mode requires an explicit per-session persistence location; pick one."
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
187
279
|
}
|
|
280
|
+
connectionExtraArgs = [];
|
|
188
281
|
/**
|
|
189
282
|
* Parse CLI URL into host and port
|
|
190
283
|
* Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
|
|
@@ -218,20 +311,37 @@ class CopilotClient {
|
|
|
218
311
|
throw new Error("sessionFs.conventions must be either 'windows' or 'posix'");
|
|
219
312
|
}
|
|
220
313
|
}
|
|
314
|
+
setupSessionFs(session, config) {
|
|
315
|
+
if (!this.sessionFsConfig) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!config.createSessionFsProvider) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
"createSessionFsProvider is required in session config when sessionFs is enabled in client options."
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
const provider = config.createSessionFsProvider(session);
|
|
324
|
+
if (this.sessionFsConfig.capabilities?.sqlite && !provider.sqlite) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
"SessionFsConfig declares capabilities.sqlite but the provider does not implement sqlite."
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
session.clientSessionApis.sessionFs = createSessionFsAdapter(provider);
|
|
330
|
+
}
|
|
221
331
|
/**
|
|
222
332
|
* Starts the CLI server and establishes a connection.
|
|
223
333
|
*
|
|
224
334
|
* If connecting to an external server (via cliUrl), only establishes the connection.
|
|
225
335
|
* Otherwise, spawns the CLI server process and then connects.
|
|
226
336
|
*
|
|
227
|
-
* This method is called automatically
|
|
337
|
+
* This method is called automatically the first time you create or resume a session.
|
|
228
338
|
*
|
|
229
339
|
* @returns A promise that resolves when the connection is established
|
|
230
340
|
* @throws Error if the server fails to start or the connection fails
|
|
231
341
|
*
|
|
232
342
|
* @example
|
|
233
343
|
* ```typescript
|
|
234
|
-
* const client = new CopilotClient(
|
|
344
|
+
* const client = new CopilotClient();
|
|
235
345
|
* await client.start();
|
|
236
346
|
* // Now ready to create sessions
|
|
237
347
|
* ```
|
|
@@ -251,7 +361,8 @@ class CopilotClient {
|
|
|
251
361
|
await this.connection.sendRequest("sessionFs.setProvider", {
|
|
252
362
|
initialCwd: this.sessionFsConfig.initialCwd,
|
|
253
363
|
sessionStatePath: this.sessionFsConfig.sessionStatePath,
|
|
254
|
-
conventions: this.sessionFsConfig.conventions
|
|
364
|
+
conventions: this.sessionFsConfig.conventions,
|
|
365
|
+
capabilities: this.sessionFsConfig.capabilities
|
|
255
366
|
});
|
|
256
367
|
}
|
|
257
368
|
this.state = "connected";
|
|
@@ -325,8 +436,15 @@ class CopilotClient {
|
|
|
325
436
|
}
|
|
326
437
|
this.modelsCache = null;
|
|
327
438
|
if (this.socket) {
|
|
439
|
+
const socket = this.socket;
|
|
440
|
+
this.socket = null;
|
|
328
441
|
try {
|
|
329
|
-
|
|
442
|
+
if (!socket.destroyed) {
|
|
443
|
+
await new Promise((resolve) => {
|
|
444
|
+
socket.once("close", () => resolve());
|
|
445
|
+
socket.end();
|
|
446
|
+
});
|
|
447
|
+
}
|
|
330
448
|
} catch (error) {
|
|
331
449
|
errors.push(
|
|
332
450
|
new Error(
|
|
@@ -334,11 +452,18 @@ class CopilotClient {
|
|
|
334
452
|
)
|
|
335
453
|
);
|
|
336
454
|
}
|
|
337
|
-
this.socket = null;
|
|
338
455
|
}
|
|
339
456
|
if (this.cliProcess && !this.isExternalServer) {
|
|
457
|
+
const child = this.cliProcess;
|
|
458
|
+
this.cliProcess = null;
|
|
340
459
|
try {
|
|
341
|
-
|
|
460
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
461
|
+
const exited = new Promise((resolve) => {
|
|
462
|
+
child.once("exit", () => resolve());
|
|
463
|
+
});
|
|
464
|
+
child.kill();
|
|
465
|
+
await exited;
|
|
466
|
+
}
|
|
342
467
|
} catch (error) {
|
|
343
468
|
errors.push(
|
|
344
469
|
new Error(
|
|
@@ -346,18 +471,32 @@ class CopilotClient {
|
|
|
346
471
|
)
|
|
347
472
|
);
|
|
348
473
|
}
|
|
349
|
-
this.cliProcess = null;
|
|
350
474
|
}
|
|
351
475
|
if (this.cliStartTimeout) {
|
|
352
476
|
clearTimeout(this.cliStartTimeout);
|
|
353
477
|
this.cliStartTimeout = null;
|
|
354
478
|
}
|
|
355
479
|
this.state = "disconnected";
|
|
356
|
-
this.
|
|
480
|
+
this.runtimePort = null;
|
|
357
481
|
this.stderrBuffer = "";
|
|
358
482
|
this.processExitPromise = null;
|
|
359
483
|
return errors;
|
|
360
484
|
}
|
|
485
|
+
/**
|
|
486
|
+
* Alias for {@link stop} that lets `CopilotClient` participate in `await using`
|
|
487
|
+
* blocks for automatic cleanup.
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```typescript
|
|
491
|
+
* await using client = new CopilotClient();
|
|
492
|
+
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
493
|
+
* await session.sendAndWait("Hello");
|
|
494
|
+
* // client.stop() is called automatically when the block exits.
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
async [Symbol.asyncDispose]() {
|
|
498
|
+
await this.stop();
|
|
499
|
+
}
|
|
361
500
|
/**
|
|
362
501
|
* Forcefully stops the CLI server without graceful cleanup.
|
|
363
502
|
*
|
|
@@ -414,7 +553,7 @@ class CopilotClient {
|
|
|
414
553
|
this.cliStartTimeout = null;
|
|
415
554
|
}
|
|
416
555
|
this.state = "disconnected";
|
|
417
|
-
this.
|
|
556
|
+
this.runtimePort = null;
|
|
418
557
|
this.stderrBuffer = "";
|
|
419
558
|
this.processExitPromise = null;
|
|
420
559
|
}
|
|
@@ -422,12 +561,11 @@ class CopilotClient {
|
|
|
422
561
|
* Creates a new conversation session with the Copilot CLI.
|
|
423
562
|
*
|
|
424
563
|
* Sessions maintain conversation state, handle events, and manage tool execution.
|
|
425
|
-
* If the client is not connected
|
|
426
|
-
* start the connection.
|
|
564
|
+
* If the client is not connected, this method automatically starts the connection.
|
|
427
565
|
*
|
|
428
566
|
* @param config - Optional configuration for the session
|
|
429
567
|
* @returns A promise that resolves with the created session
|
|
430
|
-
* @throws Error if the client
|
|
568
|
+
* @throws Error if the client fails to start
|
|
431
569
|
*
|
|
432
570
|
* @example
|
|
433
571
|
* ```typescript
|
|
@@ -447,66 +585,189 @@ class CopilotClient {
|
|
|
447
585
|
* });
|
|
448
586
|
* ```
|
|
449
587
|
*/
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
588
|
+
/**
|
|
589
|
+
* Normalizes session-level tool filter options. Converts {@link ToolSet}
|
|
590
|
+
* instances to plain string arrays, rejects misuse (bare `"*"`) and the
|
|
591
|
+
* missing-availableTools case in `mode = "empty"`.
|
|
592
|
+
*
|
|
593
|
+
* The SDK always sends `toolFilterPrecedence: "excluded"` so callers can
|
|
594
|
+
* compose include + exclude lists naturally (e.g. "everything matching X
|
|
595
|
+
* except Y") regardless of mode. Allowlist-precedence is intentionally not
|
|
596
|
+
* exposed — it's available on the runtime side as a CLI-only concession to
|
|
597
|
+
* legacy behavior, but SDK consumers always get the composable semantics.
|
|
598
|
+
*
|
|
599
|
+
* @internal
|
|
600
|
+
*/
|
|
601
|
+
resolveToolFilterOptions(config) {
|
|
602
|
+
const availableTools = toolFilterListToArray(config.availableTools);
|
|
603
|
+
const excludedTools = toolFilterListToArray(config.excludedTools);
|
|
604
|
+
validateToolFilterList("availableTools", availableTools);
|
|
605
|
+
validateToolFilterList("excludedTools", excludedTools);
|
|
606
|
+
if (this.options.mode === "empty") {
|
|
607
|
+
if (availableTools === void 0) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
"CopilotClient is in mode: 'empty' but the session config did not specify 'availableTools'. Empty mode requires every session to explicitly opt into the tools it wants \u2014 e.g. `new ToolSet().addBuiltIn(BuiltInTools.Isolated)`."
|
|
610
|
+
);
|
|
461
611
|
}
|
|
462
612
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
613
|
+
return { availableTools, excludedTools, toolFilterPrecedence: "excluded" };
|
|
614
|
+
}
|
|
615
|
+
/** Mode-specific defaults spread under the caller's config (app values win). */
|
|
616
|
+
configDefaultsForMode() {
|
|
617
|
+
if (this.options.mode === "empty") {
|
|
618
|
+
return {
|
|
619
|
+
enableSessionTelemetry: false,
|
|
620
|
+
mcpOAuthTokenStorage: "in-memory",
|
|
621
|
+
skipEmbeddingRetrieval: true,
|
|
622
|
+
embeddingCacheStorage: "in-memory",
|
|
623
|
+
enableOnDemandInstructionDiscovery: false,
|
|
624
|
+
enableFileHooks: false,
|
|
625
|
+
enableHostGitOperations: false,
|
|
626
|
+
enableSessionStore: false,
|
|
627
|
+
enableSkills: false
|
|
628
|
+
};
|
|
475
629
|
}
|
|
476
|
-
|
|
477
|
-
|
|
630
|
+
return {};
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Returns the systemMessage config to use, adjusted for the current mode.
|
|
634
|
+
* In empty mode we ensure the environment_context section is removed
|
|
635
|
+
* unless the app has already taken control of it. `append` (and
|
|
636
|
+
* unspecified) mode is promoted to `customize` so we can also strip
|
|
637
|
+
* environment_context; the caller's `content` is preserved verbatim
|
|
638
|
+
* because the runtime appends it as additional instructions in both
|
|
639
|
+
* customize and append modes.
|
|
640
|
+
*/
|
|
641
|
+
getSystemMessageConfigForMode(supplied) {
|
|
642
|
+
if (this.options.mode !== "empty") return supplied;
|
|
643
|
+
if (!supplied) {
|
|
644
|
+
return {
|
|
645
|
+
mode: "customize",
|
|
646
|
+
sections: { environment_context: { action: "remove" } }
|
|
647
|
+
};
|
|
478
648
|
}
|
|
479
|
-
|
|
480
|
-
|
|
649
|
+
switch (supplied.mode) {
|
|
650
|
+
case "replace":
|
|
651
|
+
return supplied;
|
|
652
|
+
case "customize":
|
|
653
|
+
if (supplied.sections?.environment_context) return supplied;
|
|
654
|
+
return {
|
|
655
|
+
...supplied,
|
|
656
|
+
sections: {
|
|
657
|
+
...supplied.sections,
|
|
658
|
+
environment_context: { action: "remove" }
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
case "append":
|
|
662
|
+
case void 0:
|
|
663
|
+
return {
|
|
664
|
+
mode: "customize",
|
|
665
|
+
content: supplied.content,
|
|
666
|
+
sections: { environment_context: { action: "remove" } }
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Mode-specific options applied via session.options.update after create/resume.
|
|
672
|
+
*
|
|
673
|
+
* In empty mode, defaults the four overridable feature flags to safe values
|
|
674
|
+
* (caller values from `config` win). `installedPlugins=[]` is unconditional
|
|
675
|
+
* in empty mode — apps that need custom plugins should switch modes.
|
|
676
|
+
*/
|
|
677
|
+
async updateSessionOptionsForMode(session, config) {
|
|
678
|
+
const patch = {};
|
|
679
|
+
if (this.options.mode === "empty") {
|
|
680
|
+
patch.skipCustomInstructions = config.skipCustomInstructions ?? true;
|
|
681
|
+
patch.customAgentsLocalOnly = config.customAgentsLocalOnly ?? true;
|
|
682
|
+
patch.coauthorEnabled = config.coauthorEnabled ?? false;
|
|
683
|
+
patch.manageScheduleEnabled = config.manageScheduleEnabled ?? false;
|
|
684
|
+
patch.installedPlugins = [];
|
|
685
|
+
} else {
|
|
686
|
+
if (config.skipCustomInstructions !== void 0)
|
|
687
|
+
patch.skipCustomInstructions = config.skipCustomInstructions;
|
|
688
|
+
if (config.customAgentsLocalOnly !== void 0)
|
|
689
|
+
patch.customAgentsLocalOnly = config.customAgentsLocalOnly;
|
|
690
|
+
if (config.coauthorEnabled !== void 0)
|
|
691
|
+
patch.coauthorEnabled = config.coauthorEnabled;
|
|
692
|
+
if (config.manageScheduleEnabled !== void 0)
|
|
693
|
+
patch.manageScheduleEnabled = config.manageScheduleEnabled;
|
|
694
|
+
}
|
|
695
|
+
if (Object.keys(patch).length === 0) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
try {
|
|
699
|
+
await session.rpc.options.update(patch);
|
|
700
|
+
} catch (e) {
|
|
701
|
+
try {
|
|
702
|
+
await session.disconnect();
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
throw e;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async createSession(config) {
|
|
709
|
+
if (!this.connection) {
|
|
710
|
+
await this.start();
|
|
481
711
|
}
|
|
712
|
+
config = { ...this.configDefaultsForMode(), ...config };
|
|
713
|
+
config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
|
|
714
|
+
const callerSessionId = config.sessionId;
|
|
715
|
+
const useServerGeneratedId = config.cloud != null && callerSessionId == null;
|
|
716
|
+
const localSessionId = useServerGeneratedId ? void 0 : callerSessionId ?? randomUUID();
|
|
482
717
|
const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
|
|
483
718
|
config.systemMessage
|
|
484
719
|
);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
);
|
|
720
|
+
const initializeSession = (sessionId) => {
|
|
721
|
+
const s = new CopilotSession(
|
|
722
|
+
sessionId,
|
|
723
|
+
this.connection,
|
|
724
|
+
void 0,
|
|
725
|
+
this.onGetTraceContext
|
|
726
|
+
);
|
|
727
|
+
s.registerTools(config.tools);
|
|
728
|
+
s.registerCanvases(config.canvases);
|
|
729
|
+
s.registerCommands(config.commands);
|
|
730
|
+
s.registerPermissionHandler(config.onPermissionRequest);
|
|
731
|
+
if (config.onUserInputRequest) {
|
|
732
|
+
s.registerUserInputHandler(config.onUserInputRequest);
|
|
733
|
+
}
|
|
734
|
+
if (config.onElicitationRequest) {
|
|
735
|
+
s.registerElicitationHandler(config.onElicitationRequest);
|
|
736
|
+
}
|
|
737
|
+
if (config.onExitPlanModeRequest) {
|
|
738
|
+
s.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
501
739
|
}
|
|
740
|
+
if (config.onAutoModeSwitchRequest) {
|
|
741
|
+
s.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
742
|
+
}
|
|
743
|
+
if (config.hooks) {
|
|
744
|
+
s.registerHooks(config.hooks);
|
|
745
|
+
}
|
|
746
|
+
if (transformCallbacks) {
|
|
747
|
+
s.registerTransformCallbacks(transformCallbacks);
|
|
748
|
+
}
|
|
749
|
+
if (config.onEvent) {
|
|
750
|
+
s.on(config.onEvent);
|
|
751
|
+
}
|
|
752
|
+
this.sessions.set(sessionId, s);
|
|
753
|
+
this.setupSessionFs(s, config);
|
|
754
|
+
return s;
|
|
755
|
+
};
|
|
756
|
+
let session;
|
|
757
|
+
let registeredId;
|
|
758
|
+
if (localSessionId !== void 0) {
|
|
759
|
+
session = initializeSession(localSessionId);
|
|
760
|
+
registeredId = localSessionId;
|
|
502
761
|
}
|
|
762
|
+
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
503
763
|
try {
|
|
504
764
|
const response = await this.connection.sendRequest("session.create", {
|
|
505
765
|
...await getTraceContext(this.onGetTraceContext),
|
|
506
766
|
model: config.model,
|
|
507
|
-
sessionId,
|
|
767
|
+
sessionId: localSessionId,
|
|
508
768
|
clientName: config.clientName,
|
|
509
769
|
reasoningEffort: config.reasoningEffort,
|
|
770
|
+
reasoningSummary: config.reasoningSummary,
|
|
510
771
|
tools: config.tools?.map((tool) => ({
|
|
511
772
|
name: tool.name,
|
|
512
773
|
description: tool.description,
|
|
@@ -514,39 +775,81 @@ class CopilotClient {
|
|
|
514
775
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
515
776
|
skipPermission: tool.skipPermission
|
|
516
777
|
})),
|
|
778
|
+
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
779
|
+
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
780
|
+
requestExtensions: config.requestExtensions,
|
|
781
|
+
extensionInfo: config.extensionInfo,
|
|
517
782
|
commands: config.commands?.map((cmd) => ({
|
|
518
783
|
name: cmd.name,
|
|
519
784
|
description: cmd.description
|
|
520
785
|
})),
|
|
521
786
|
systemMessage: wireSystemMessage,
|
|
522
|
-
availableTools:
|
|
523
|
-
excludedTools:
|
|
787
|
+
availableTools: toolFilterOptions.availableTools,
|
|
788
|
+
excludedTools: toolFilterOptions.excludedTools,
|
|
789
|
+
toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
|
|
524
790
|
provider: config.provider,
|
|
791
|
+
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
525
792
|
modelCapabilities: config.modelCapabilities,
|
|
526
|
-
|
|
793
|
+
largeOutput: toWireLargeOutput(config.largeOutput),
|
|
794
|
+
requestPermission: !!config.onPermissionRequest,
|
|
527
795
|
requestUserInput: !!config.onUserInputRequest,
|
|
528
796
|
requestElicitation: !!config.onElicitationRequest,
|
|
797
|
+
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
798
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
799
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
529
800
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
530
801
|
workingDirectory: config.workingDirectory,
|
|
531
802
|
streaming: config.streaming,
|
|
532
803
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
533
|
-
mcpServers: config.mcpServers,
|
|
804
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
805
|
+
mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
|
|
534
806
|
envValueMode: "direct",
|
|
535
|
-
customAgents: config.customAgents,
|
|
807
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
536
808
|
defaultAgent: config.defaultAgent,
|
|
537
809
|
agent: config.agent,
|
|
538
|
-
configDir: config.
|
|
810
|
+
configDir: config.configDirectory,
|
|
539
811
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
812
|
+
skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
|
|
813
|
+
embeddingCacheStorage: config.embeddingCacheStorage,
|
|
814
|
+
organizationCustomInstructions: config.organizationCustomInstructions,
|
|
815
|
+
enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
|
|
816
|
+
enableFileHooks: config.enableFileHooks,
|
|
817
|
+
enableHostGitOperations: config.enableHostGitOperations,
|
|
818
|
+
enableSessionStore: config.enableSessionStore,
|
|
819
|
+
enableSkills: config.enableSkills,
|
|
540
820
|
skillDirectories: config.skillDirectories,
|
|
821
|
+
pluginDirectories: config.pluginDirectories,
|
|
822
|
+
instructionDirectories: config.instructionDirectories,
|
|
541
823
|
disabledSkills: config.disabledSkills,
|
|
542
824
|
infiniteSessions: config.infiniteSessions,
|
|
543
|
-
gitHubToken: config.gitHubToken
|
|
825
|
+
gitHubToken: config.gitHubToken,
|
|
826
|
+
remoteSession: config.remoteSession,
|
|
827
|
+
cloud: config.cloud
|
|
544
828
|
});
|
|
545
|
-
const {
|
|
829
|
+
const {
|
|
830
|
+
sessionId: returnedSessionId,
|
|
831
|
+
workspacePath,
|
|
832
|
+
capabilities
|
|
833
|
+
} = response;
|
|
834
|
+
if (!returnedSessionId) {
|
|
835
|
+
throw new Error("session.create response did not include a sessionId");
|
|
836
|
+
}
|
|
837
|
+
if (localSessionId !== void 0 && localSessionId !== returnedSessionId) {
|
|
838
|
+
throw new Error(
|
|
839
|
+
`session.create returned sessionId ${returnedSessionId} but the caller requested ${localSessionId}`
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
if (session === void 0) {
|
|
843
|
+
session = initializeSession(returnedSessionId);
|
|
844
|
+
registeredId = returnedSessionId;
|
|
845
|
+
}
|
|
546
846
|
session["_workspacePath"] = workspacePath;
|
|
547
847
|
session.setCapabilities(capabilities);
|
|
848
|
+
await this.updateSessionOptionsForMode(session, config);
|
|
548
849
|
} catch (e) {
|
|
549
|
-
|
|
850
|
+
if (registeredId !== void 0) {
|
|
851
|
+
this.sessions.delete(registeredId);
|
|
852
|
+
}
|
|
550
853
|
throw e;
|
|
551
854
|
}
|
|
552
855
|
return session;
|
|
@@ -576,17 +879,8 @@ class CopilotClient {
|
|
|
576
879
|
* ```
|
|
577
880
|
*/
|
|
578
881
|
async resumeSession(sessionId, config) {
|
|
579
|
-
if (!config?.onPermissionRequest) {
|
|
580
|
-
throw new Error(
|
|
581
|
-
"An onPermissionRequest handler is required when resuming a session. For example, to allow all permissions, use { onPermissionRequest: approveAll }."
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
882
|
if (!this.connection) {
|
|
585
|
-
|
|
586
|
-
await this.start();
|
|
587
|
-
} else {
|
|
588
|
-
throw new Error("Client not connected. Call start() first.");
|
|
589
|
-
}
|
|
883
|
+
await this.start();
|
|
590
884
|
}
|
|
591
885
|
const session = new CopilotSession(
|
|
592
886
|
sessionId,
|
|
@@ -595,6 +889,7 @@ class CopilotClient {
|
|
|
595
889
|
this.onGetTraceContext
|
|
596
890
|
);
|
|
597
891
|
session.registerTools(config.tools);
|
|
892
|
+
session.registerCanvases(config.canvases);
|
|
598
893
|
session.registerCommands(config.commands);
|
|
599
894
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
600
895
|
if (config.onUserInputRequest) {
|
|
@@ -603,9 +898,17 @@ class CopilotClient {
|
|
|
603
898
|
if (config.onElicitationRequest) {
|
|
604
899
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
605
900
|
}
|
|
901
|
+
if (config.onExitPlanModeRequest) {
|
|
902
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
903
|
+
}
|
|
904
|
+
if (config.onAutoModeSwitchRequest) {
|
|
905
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
906
|
+
}
|
|
606
907
|
if (config.hooks) {
|
|
607
908
|
session.registerHooks(config.hooks);
|
|
608
909
|
}
|
|
910
|
+
config = { ...this.configDefaultsForMode(), ...config };
|
|
911
|
+
config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
|
|
609
912
|
const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
|
|
610
913
|
config.systemMessage
|
|
611
914
|
);
|
|
@@ -616,17 +919,8 @@ class CopilotClient {
|
|
|
616
919
|
session.on(config.onEvent);
|
|
617
920
|
}
|
|
618
921
|
this.sessions.set(sessionId, session);
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
session.clientSessionApis.sessionFs = createSessionFsAdapter(
|
|
622
|
-
config.createSessionFsHandler(session)
|
|
623
|
-
);
|
|
624
|
-
} else {
|
|
625
|
-
throw new Error(
|
|
626
|
-
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
922
|
+
this.setupSessionFs(session, config);
|
|
923
|
+
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
630
924
|
try {
|
|
631
925
|
const response = await this.connection.sendRequest("session.resume", {
|
|
632
926
|
...await getTraceContext(this.onGetTraceContext),
|
|
@@ -634,9 +928,12 @@ class CopilotClient {
|
|
|
634
928
|
clientName: config.clientName,
|
|
635
929
|
model: config.model,
|
|
636
930
|
reasoningEffort: config.reasoningEffort,
|
|
931
|
+
reasoningSummary: config.reasoningSummary,
|
|
637
932
|
systemMessage: wireSystemMessage,
|
|
638
|
-
availableTools:
|
|
639
|
-
excludedTools:
|
|
933
|
+
availableTools: toolFilterOptions.availableTools,
|
|
934
|
+
excludedTools: toolFilterOptions.excludedTools,
|
|
935
|
+
toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
|
|
936
|
+
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
640
937
|
tools: config.tools?.map((tool) => ({
|
|
641
938
|
name: tool.name,
|
|
642
939
|
description: tool.description,
|
|
@@ -644,56 +941,65 @@ class CopilotClient {
|
|
|
644
941
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
645
942
|
skipPermission: tool.skipPermission
|
|
646
943
|
})),
|
|
944
|
+
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
945
|
+
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
946
|
+
requestExtensions: config.requestExtensions,
|
|
947
|
+
extensionInfo: config.extensionInfo,
|
|
647
948
|
commands: config.commands?.map((cmd) => ({
|
|
648
949
|
name: cmd.name,
|
|
649
950
|
description: cmd.description
|
|
650
951
|
})),
|
|
651
952
|
provider: config.provider,
|
|
652
953
|
modelCapabilities: config.modelCapabilities,
|
|
954
|
+
largeOutput: toWireLargeOutput(config.largeOutput),
|
|
653
955
|
requestPermission: config.onPermissionRequest !== defaultJoinSessionPermissionHandler,
|
|
654
956
|
requestUserInput: !!config.onUserInputRequest,
|
|
655
957
|
requestElicitation: !!config.onElicitationRequest,
|
|
958
|
+
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
959
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
960
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
656
961
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
657
962
|
workingDirectory: config.workingDirectory,
|
|
658
|
-
configDir: config.
|
|
963
|
+
configDir: config.configDirectory,
|
|
659
964
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
965
|
+
skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
|
|
966
|
+
embeddingCacheStorage: config.embeddingCacheStorage,
|
|
967
|
+
organizationCustomInstructions: config.organizationCustomInstructions,
|
|
968
|
+
enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
|
|
969
|
+
enableFileHooks: config.enableFileHooks,
|
|
970
|
+
enableHostGitOperations: config.enableHostGitOperations,
|
|
971
|
+
enableSessionStore: config.enableSessionStore,
|
|
972
|
+
enableSkills: config.enableSkills,
|
|
660
973
|
streaming: config.streaming,
|
|
661
974
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
662
|
-
mcpServers: config.mcpServers,
|
|
975
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
976
|
+
mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
|
|
663
977
|
envValueMode: "direct",
|
|
664
|
-
customAgents: config.customAgents,
|
|
978
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
665
979
|
defaultAgent: config.defaultAgent,
|
|
666
980
|
agent: config.agent,
|
|
667
981
|
skillDirectories: config.skillDirectories,
|
|
982
|
+
pluginDirectories: config.pluginDirectories,
|
|
983
|
+
instructionDirectories: config.instructionDirectories,
|
|
668
984
|
disabledSkills: config.disabledSkills,
|
|
669
985
|
infiniteSessions: config.infiniteSessions,
|
|
670
|
-
|
|
671
|
-
|
|
986
|
+
suppressResumeEvent: config.suppressResumeEvent,
|
|
987
|
+
continuePendingWork: config.continuePendingWork,
|
|
988
|
+
gitHubToken: config.gitHubToken,
|
|
989
|
+
remoteSession: config.remoteSession,
|
|
990
|
+
openCanvases: config.openCanvases
|
|
672
991
|
});
|
|
673
|
-
const { workspacePath, capabilities } = response;
|
|
992
|
+
const { workspacePath, capabilities, openCanvases } = response;
|
|
674
993
|
session["_workspacePath"] = workspacePath;
|
|
675
994
|
session.setCapabilities(capabilities);
|
|
995
|
+
session.setOpenCanvases(openCanvases ?? []);
|
|
996
|
+
await this.updateSessionOptionsForMode(session, config);
|
|
676
997
|
} catch (e) {
|
|
677
998
|
this.sessions.delete(sessionId);
|
|
678
999
|
throw e;
|
|
679
1000
|
}
|
|
680
1001
|
return session;
|
|
681
1002
|
}
|
|
682
|
-
/**
|
|
683
|
-
* Gets the current connection state of the client.
|
|
684
|
-
*
|
|
685
|
-
* @returns The current connection state: "disconnected", "connecting", "connected", or "error"
|
|
686
|
-
*
|
|
687
|
-
* @example
|
|
688
|
-
* ```typescript
|
|
689
|
-
* if (client.getState() === "connected") {
|
|
690
|
-
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
691
|
-
* }
|
|
692
|
-
* ```
|
|
693
|
-
*/
|
|
694
|
-
getState() {
|
|
695
|
-
return this.state;
|
|
696
|
-
}
|
|
697
1003
|
/**
|
|
698
1004
|
* Sends a ping request to the server to verify connectivity.
|
|
699
1005
|
*
|
|
@@ -789,18 +1095,29 @@ class CopilotClient {
|
|
|
789
1095
|
}
|
|
790
1096
|
}
|
|
791
1097
|
/**
|
|
792
|
-
*
|
|
793
|
-
*
|
|
1098
|
+
* Send the `connect` handshake (carrying the optional token) and verify the
|
|
1099
|
+
* server's protocol version. Falls back to `ping` against legacy servers
|
|
1100
|
+
* that don't implement `connect`.
|
|
794
1101
|
*/
|
|
795
1102
|
async verifyProtocolVersion() {
|
|
1103
|
+
if (!this.connection) {
|
|
1104
|
+
throw new Error("Client not connected");
|
|
1105
|
+
}
|
|
796
1106
|
const maxVersion = getSdkProtocolVersion();
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1107
|
+
const raceAgainstExit = (p) => this.processExitPromise ? Promise.race([p, this.processExitPromise]) : p;
|
|
1108
|
+
let serverVersion;
|
|
1109
|
+
try {
|
|
1110
|
+
const result = await raceAgainstExit(
|
|
1111
|
+
this.internalRpc.connect({ token: this.effectiveConnectionToken })
|
|
1112
|
+
);
|
|
1113
|
+
serverVersion = result.protocolVersion;
|
|
1114
|
+
} catch (err) {
|
|
1115
|
+
if (err instanceof ResponseError && (err.code === ErrorCodes.MethodNotFound || err.message === "Unhandled method connect")) {
|
|
1116
|
+
serverVersion = (await raceAgainstExit(this.ping())).protocolVersion;
|
|
1117
|
+
} else {
|
|
1118
|
+
throw err;
|
|
1119
|
+
}
|
|
802
1120
|
}
|
|
803
|
-
const serverVersion = pingResult.protocolVersion;
|
|
804
1121
|
if (serverVersion === void 0) {
|
|
805
1122
|
throw new Error(
|
|
806
1123
|
`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.`
|
|
@@ -884,8 +1201,13 @@ class CopilotClient {
|
|
|
884
1201
|
if (!this.connection) {
|
|
885
1202
|
throw new Error("Client not connected");
|
|
886
1203
|
}
|
|
1204
|
+
let wireFilter;
|
|
1205
|
+
if (filter) {
|
|
1206
|
+
const { workingDirectory, ...rest } = filter;
|
|
1207
|
+
wireFilter = { ...rest, cwd: workingDirectory };
|
|
1208
|
+
}
|
|
887
1209
|
const response = await this.connection.sendRequest("session.list", {
|
|
888
|
-
filter
|
|
1210
|
+
filter: wireFilter
|
|
889
1211
|
});
|
|
890
1212
|
const { sessions } = response;
|
|
891
1213
|
return sessions.map(CopilotClient.toSessionMetadata);
|
|
@@ -920,13 +1242,19 @@ class CopilotClient {
|
|
|
920
1242
|
return CopilotClient.toSessionMetadata(session);
|
|
921
1243
|
}
|
|
922
1244
|
static toSessionMetadata(raw) {
|
|
1245
|
+
const { context } = raw;
|
|
923
1246
|
return {
|
|
924
1247
|
sessionId: raw.sessionId,
|
|
925
1248
|
startTime: new Date(raw.startTime),
|
|
926
1249
|
modifiedTime: new Date(raw.modifiedTime),
|
|
927
1250
|
summary: raw.summary,
|
|
928
1251
|
isRemote: raw.isRemote,
|
|
929
|
-
context:
|
|
1252
|
+
context: context ? {
|
|
1253
|
+
workingDirectory: context.cwd,
|
|
1254
|
+
gitRoot: context.gitRoot,
|
|
1255
|
+
repository: context.repository,
|
|
1256
|
+
branch: context.branch
|
|
1257
|
+
} : void 0
|
|
930
1258
|
};
|
|
931
1259
|
}
|
|
932
1260
|
/**
|
|
@@ -979,7 +1307,7 @@ class CopilotClient {
|
|
|
979
1307
|
throw new Error(result.error || "Failed to set foreground session");
|
|
980
1308
|
}
|
|
981
1309
|
}
|
|
982
|
-
|
|
1310
|
+
onLifecycle(eventTypeOrHandler, handler) {
|
|
983
1311
|
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
984
1312
|
const eventType = eventTypeOrHandler;
|
|
985
1313
|
if (!this.typedLifecycleHandlers.has(eventType)) {
|
|
@@ -1006,17 +1334,17 @@ class CopilotClient {
|
|
|
1006
1334
|
async startCLIServer() {
|
|
1007
1335
|
return new Promise((resolve, reject) => {
|
|
1008
1336
|
this.stderrBuffer = "";
|
|
1009
|
-
const args = [
|
|
1010
|
-
|
|
1011
|
-
"--
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
this.options.logLevel
|
|
1015
|
-
];
|
|
1016
|
-
if (this.options.useStdio) {
|
|
1337
|
+
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1338
|
+
if (this.options.logLevel) {
|
|
1339
|
+
args.push("--log-level", this.options.logLevel);
|
|
1340
|
+
}
|
|
1341
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1017
1342
|
args.push("--stdio");
|
|
1018
|
-
} else if (this.
|
|
1019
|
-
|
|
1343
|
+
} else if (this.connectionConfig.kind === "tcp") {
|
|
1344
|
+
const requestedPort = this.connectionConfig.port ?? 0;
|
|
1345
|
+
if (requestedPort > 0) {
|
|
1346
|
+
args.push("--port", requestedPort.toString());
|
|
1347
|
+
}
|
|
1020
1348
|
}
|
|
1021
1349
|
if (this.options.gitHubToken) {
|
|
1022
1350
|
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
@@ -1030,14 +1358,26 @@ class CopilotClient {
|
|
|
1030
1358
|
this.options.sessionIdleTimeoutSeconds.toString()
|
|
1031
1359
|
);
|
|
1032
1360
|
}
|
|
1033
|
-
|
|
1361
|
+
if (this.options.enableRemoteSessions) {
|
|
1362
|
+
args.push("--remote");
|
|
1363
|
+
}
|
|
1364
|
+
const envWithoutNodeDebug = { ...this.resolvedEnv };
|
|
1034
1365
|
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
1035
1366
|
if (this.options.gitHubToken) {
|
|
1036
1367
|
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1037
1368
|
}
|
|
1038
|
-
if (
|
|
1369
|
+
if (this.effectiveConnectionToken) {
|
|
1370
|
+
envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1371
|
+
}
|
|
1372
|
+
if (this.options.baseDirectory) {
|
|
1373
|
+
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1374
|
+
}
|
|
1375
|
+
if (this.options.mode === "empty") {
|
|
1376
|
+
envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
|
|
1377
|
+
}
|
|
1378
|
+
if (!this.resolvedCliPath) {
|
|
1039
1379
|
throw new Error(
|
|
1040
|
-
"Path to Copilot CLI is required. Please
|
|
1380
|
+
"Path to Copilot CLI is required. Please supply it via `RuntimeConnection.forStdio({ path })` or `RuntimeConnection.forTcp({ path })`, set the COPILOT_CLI_PATH environment variable, or use `RuntimeConnection.forUri(...)` to connect to an already-running runtime."
|
|
1041
1381
|
);
|
|
1042
1382
|
}
|
|
1043
1383
|
if (this.options.telemetry) {
|
|
@@ -1056,31 +1396,31 @@ class CopilotClient {
|
|
|
1056
1396
|
t.captureContent
|
|
1057
1397
|
);
|
|
1058
1398
|
}
|
|
1059
|
-
if (!existsSync(this.
|
|
1399
|
+
if (!existsSync(this.resolvedCliPath)) {
|
|
1060
1400
|
throw new Error(
|
|
1061
|
-
`Copilot CLI not found at ${this.
|
|
1401
|
+
`Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
|
|
1062
1402
|
);
|
|
1063
1403
|
}
|
|
1064
|
-
const stdioConfig = this.
|
|
1065
|
-
const isJsFile = this.
|
|
1404
|
+
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
1405
|
+
const isJsFile = this.resolvedCliPath.endsWith(".js");
|
|
1066
1406
|
if (isJsFile) {
|
|
1067
|
-
this.cliProcess = spawn(getNodeExecPath(), [this.
|
|
1407
|
+
this.cliProcess = spawn(getNodeExecPath(), [this.resolvedCliPath, ...args], {
|
|
1068
1408
|
stdio: stdioConfig,
|
|
1069
|
-
cwd: this.options.
|
|
1409
|
+
cwd: this.options.workingDirectory,
|
|
1070
1410
|
env: envWithoutNodeDebug,
|
|
1071
1411
|
windowsHide: true
|
|
1072
1412
|
});
|
|
1073
1413
|
} else {
|
|
1074
|
-
this.cliProcess = spawn(this.
|
|
1414
|
+
this.cliProcess = spawn(this.resolvedCliPath, args, {
|
|
1075
1415
|
stdio: stdioConfig,
|
|
1076
|
-
cwd: this.options.
|
|
1416
|
+
cwd: this.options.workingDirectory,
|
|
1077
1417
|
env: envWithoutNodeDebug,
|
|
1078
1418
|
windowsHide: true
|
|
1079
1419
|
});
|
|
1080
1420
|
}
|
|
1081
1421
|
let stdout = "";
|
|
1082
1422
|
let resolved = false;
|
|
1083
|
-
if (this.
|
|
1423
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1084
1424
|
resolved = true;
|
|
1085
1425
|
resolve();
|
|
1086
1426
|
} else {
|
|
@@ -1088,7 +1428,7 @@ class CopilotClient {
|
|
|
1088
1428
|
stdout += data.toString();
|
|
1089
1429
|
const match = stdout.match(/listening on port (\d+)/i);
|
|
1090
1430
|
if (match && !resolved) {
|
|
1091
|
-
this.
|
|
1431
|
+
this.runtimePort = parseInt(match[1], 10);
|
|
1092
1432
|
resolved = true;
|
|
1093
1433
|
resolve();
|
|
1094
1434
|
}
|
|
@@ -1162,19 +1502,21 @@ stderr: ${stderrOutput}`
|
|
|
1162
1502
|
resolved = true;
|
|
1163
1503
|
reject(new Error("Timeout waiting for CLI server to start"));
|
|
1164
1504
|
}
|
|
1165
|
-
},
|
|
1505
|
+
}, 3e4);
|
|
1166
1506
|
});
|
|
1167
1507
|
}
|
|
1168
1508
|
/**
|
|
1169
1509
|
* Connect to the CLI server (via socket or stdio)
|
|
1170
1510
|
*/
|
|
1171
1511
|
async connectToServer() {
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1512
|
+
switch (this.connectionConfig.kind) {
|
|
1513
|
+
case "parent-process":
|
|
1514
|
+
return this.connectToParentProcessViaStdio();
|
|
1515
|
+
case "stdio":
|
|
1516
|
+
return this.connectToChildProcessViaStdio();
|
|
1517
|
+
case "tcp":
|
|
1518
|
+
case "uri":
|
|
1519
|
+
return this.connectViaTcp();
|
|
1178
1520
|
}
|
|
1179
1521
|
}
|
|
1180
1522
|
/**
|
|
@@ -1214,12 +1556,17 @@ stderr: ${stderrOutput}`
|
|
|
1214
1556
|
* Connect to the CLI server via TCP socket
|
|
1215
1557
|
*/
|
|
1216
1558
|
async connectViaTcp() {
|
|
1217
|
-
if (!this.
|
|
1559
|
+
if (!this.runtimePort) {
|
|
1218
1560
|
throw new Error("Server port not available");
|
|
1219
1561
|
}
|
|
1220
1562
|
return new Promise((resolve, reject) => {
|
|
1221
1563
|
this.socket = new Socket();
|
|
1222
|
-
|
|
1564
|
+
const connectionTimeout = setTimeout(() => {
|
|
1565
|
+
this.socket?.destroy();
|
|
1566
|
+
reject(new Error("Timeout connecting to CLI server"));
|
|
1567
|
+
}, 1e4);
|
|
1568
|
+
this.socket.connect(this.runtimePort, this.actualHost, () => {
|
|
1569
|
+
clearTimeout(connectionTimeout);
|
|
1223
1570
|
this.connection = createMessageConnection(
|
|
1224
1571
|
new StreamMessageReader(this.socket),
|
|
1225
1572
|
new StreamMessageWriter(this.socket)
|
|
@@ -1229,6 +1576,7 @@ stderr: ${stderrOutput}`
|
|
|
1229
1576
|
resolve();
|
|
1230
1577
|
});
|
|
1231
1578
|
this.socket.on("error", (error) => {
|
|
1579
|
+
clearTimeout(connectionTimeout);
|
|
1232
1580
|
reject(new Error(`Failed to connect to CLI server: ${error.message}`));
|
|
1233
1581
|
});
|
|
1234
1582
|
});
|
|
@@ -1244,16 +1592,16 @@ stderr: ${stderrOutput}`
|
|
|
1244
1592
|
this.handleSessionLifecycleNotification(notification);
|
|
1245
1593
|
});
|
|
1246
1594
|
this.connection.onRequest(
|
|
1247
|
-
"
|
|
1248
|
-
async (params) => await this.
|
|
1595
|
+
"userInput.request",
|
|
1596
|
+
async (params) => await this.handleUserInputRequest(params)
|
|
1249
1597
|
);
|
|
1250
1598
|
this.connection.onRequest(
|
|
1251
|
-
"
|
|
1252
|
-
async (params) => await this.
|
|
1599
|
+
"exitPlanMode.request",
|
|
1600
|
+
async (params) => await this.handleExitPlanModeRequest(params)
|
|
1253
1601
|
);
|
|
1254
1602
|
this.connection.onRequest(
|
|
1255
|
-
"
|
|
1256
|
-
async (params) => await this.
|
|
1603
|
+
"autoModeSwitch.request",
|
|
1604
|
+
async (params) => await this.handleAutoModeSwitchRequest(params)
|
|
1257
1605
|
);
|
|
1258
1606
|
this.connection.onRequest(
|
|
1259
1607
|
"hooks.invoke",
|
|
@@ -1289,7 +1637,20 @@ stderr: ${stderrOutput}`
|
|
|
1289
1637
|
if (typeof notification !== "object" || !notification || !("type" in notification) || typeof notification.type !== "string" || !("sessionId" in notification) || typeof notification.sessionId !== "string") {
|
|
1290
1638
|
return;
|
|
1291
1639
|
}
|
|
1292
|
-
const
|
|
1640
|
+
const raw = notification;
|
|
1641
|
+
let metadata;
|
|
1642
|
+
if (raw.metadata && raw.metadata.startTime && raw.metadata.modifiedTime) {
|
|
1643
|
+
metadata = {
|
|
1644
|
+
startTime: new Date(raw.metadata.startTime),
|
|
1645
|
+
modifiedTime: new Date(raw.metadata.modifiedTime),
|
|
1646
|
+
summary: raw.metadata.summary
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
const event = {
|
|
1650
|
+
type: raw.type,
|
|
1651
|
+
sessionId: raw.sessionId,
|
|
1652
|
+
metadata
|
|
1653
|
+
};
|
|
1293
1654
|
const typedHandlers = this.typedLifecycleHandlers.get(event.type);
|
|
1294
1655
|
if (typedHandlers) {
|
|
1295
1656
|
for (const handler of typedHandlers) {
|
|
@@ -1321,125 +1682,55 @@ stderr: ${stderrOutput}`
|
|
|
1321
1682
|
});
|
|
1322
1683
|
return result;
|
|
1323
1684
|
}
|
|
1324
|
-
async
|
|
1325
|
-
if (!params || typeof params.sessionId !== "string" || typeof params.
|
|
1326
|
-
throw new Error("Invalid
|
|
1685
|
+
async handleExitPlanModeRequest(params) {
|
|
1686
|
+
if (!params || typeof params.sessionId !== "string" || typeof params.summary !== "string" || !Array.isArray(params.actions) || typeof params.recommendedAction !== "string") {
|
|
1687
|
+
throw new Error("Invalid exit plan mode request payload");
|
|
1327
1688
|
}
|
|
1328
1689
|
const session = this.sessions.get(params.sessionId);
|
|
1329
1690
|
if (!session) {
|
|
1330
1691
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1331
1692
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1693
|
+
return await session._handleExitPlanModeRequest({
|
|
1694
|
+
summary: params.summary,
|
|
1695
|
+
planContent: params.planContent,
|
|
1696
|
+
actions: params.actions,
|
|
1697
|
+
recommendedAction: params.recommendedAction
|
|
1698
|
+
});
|
|
1334
1699
|
}
|
|
1335
|
-
async
|
|
1336
|
-
if (!params || typeof params.sessionId !== "string"
|
|
1337
|
-
throw new Error("Invalid
|
|
1700
|
+
async handleAutoModeSwitchRequest(params) {
|
|
1701
|
+
if (!params || typeof params.sessionId !== "string") {
|
|
1702
|
+
throw new Error("Invalid auto mode switch request payload");
|
|
1338
1703
|
}
|
|
1339
1704
|
const session = this.sessions.get(params.sessionId);
|
|
1340
1705
|
if (!session) {
|
|
1341
1706
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1342
1707
|
}
|
|
1343
|
-
|
|
1708
|
+
const response = await session._handleAutoModeSwitchRequest({
|
|
1709
|
+
errorCode: params.errorCode,
|
|
1710
|
+
retryAfterSeconds: params.retryAfterSeconds
|
|
1711
|
+
});
|
|
1712
|
+
return { response };
|
|
1344
1713
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
/**
|
|
1349
|
-
* Handles a v2-style tool.call RPC request from the server.
|
|
1350
|
-
* Looks up the session and tool handler, executes it, and returns the result
|
|
1351
|
-
* in the v2 response format.
|
|
1352
|
-
*/
|
|
1353
|
-
async handleToolCallRequestV2(params) {
|
|
1354
|
-
if (!params || typeof params.sessionId !== "string" || typeof params.toolCallId !== "string" || typeof params.toolName !== "string") {
|
|
1355
|
-
throw new Error("Invalid tool call payload");
|
|
1714
|
+
async handleHooksInvoke(params) {
|
|
1715
|
+
if (!params || typeof params.sessionId !== "string" || typeof params.hookType !== "string") {
|
|
1716
|
+
throw new Error("Invalid hooks invoke payload");
|
|
1356
1717
|
}
|
|
1357
1718
|
const session = this.sessions.get(params.sessionId);
|
|
1358
1719
|
if (!session) {
|
|
1359
|
-
throw new Error(`
|
|
1360
|
-
}
|
|
1361
|
-
const handler = session.getToolHandler(params.toolName);
|
|
1362
|
-
if (!handler) {
|
|
1363
|
-
return {
|
|
1364
|
-
result: {
|
|
1365
|
-
textResultForLlm: `Tool '${params.toolName}' is not supported by this client instance.`,
|
|
1366
|
-
resultType: "failure",
|
|
1367
|
-
error: `tool '${params.toolName}' not supported`,
|
|
1368
|
-
toolTelemetry: {}
|
|
1369
|
-
}
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
try {
|
|
1373
|
-
const traceparent = params.traceparent;
|
|
1374
|
-
const tracestate = params.tracestate;
|
|
1375
|
-
const invocation = {
|
|
1376
|
-
sessionId: params.sessionId,
|
|
1377
|
-
toolCallId: params.toolCallId,
|
|
1378
|
-
toolName: params.toolName,
|
|
1379
|
-
arguments: params.arguments,
|
|
1380
|
-
traceparent,
|
|
1381
|
-
tracestate
|
|
1382
|
-
};
|
|
1383
|
-
const result = await handler(params.arguments, invocation);
|
|
1384
|
-
return { result: this.normalizeToolResultV2(result) };
|
|
1385
|
-
} catch (error) {
|
|
1386
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1387
|
-
return {
|
|
1388
|
-
result: {
|
|
1389
|
-
textResultForLlm: "Invoking this tool produced an error. Detailed information is not available.",
|
|
1390
|
-
resultType: "failure",
|
|
1391
|
-
error: message,
|
|
1392
|
-
toolTelemetry: {}
|
|
1393
|
-
}
|
|
1394
|
-
};
|
|
1720
|
+
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1395
1721
|
}
|
|
1722
|
+
const output = await session._handleHooksInvoke(params.hookType, params.input);
|
|
1723
|
+
return { output };
|
|
1396
1724
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
async handlePermissionRequestV2(params) {
|
|
1401
|
-
if (!params || typeof params.sessionId !== "string" || !params.permissionRequest) {
|
|
1402
|
-
throw new Error("Invalid permission request payload");
|
|
1725
|
+
async handleSystemMessageTransform(params) {
|
|
1726
|
+
if (!params || typeof params.sessionId !== "string" || !params.sections || typeof params.sections !== "object") {
|
|
1727
|
+
throw new Error("Invalid systemMessage.transform payload");
|
|
1403
1728
|
}
|
|
1404
1729
|
const session = this.sessions.get(params.sessionId);
|
|
1405
1730
|
if (!session) {
|
|
1406
1731
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1407
1732
|
}
|
|
1408
|
-
|
|
1409
|
-
const result = await session._handlePermissionRequestV2(params.permissionRequest);
|
|
1410
|
-
return { result };
|
|
1411
|
-
} catch (error) {
|
|
1412
|
-
if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
|
|
1413
|
-
throw error;
|
|
1414
|
-
}
|
|
1415
|
-
return {
|
|
1416
|
-
result: {
|
|
1417
|
-
kind: "user-not-available"
|
|
1418
|
-
}
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
normalizeToolResultV2(result) {
|
|
1423
|
-
if (result === void 0 || result === null) {
|
|
1424
|
-
return {
|
|
1425
|
-
textResultForLlm: "Tool returned no result",
|
|
1426
|
-
resultType: "failure",
|
|
1427
|
-
error: "tool returned no result",
|
|
1428
|
-
toolTelemetry: {}
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
if (this.isToolResultObject(result)) {
|
|
1432
|
-
return result;
|
|
1433
|
-
}
|
|
1434
|
-
const textResult = typeof result === "string" ? result : JSON.stringify(result);
|
|
1435
|
-
return {
|
|
1436
|
-
textResultForLlm: textResult,
|
|
1437
|
-
resultType: "success",
|
|
1438
|
-
toolTelemetry: {}
|
|
1439
|
-
};
|
|
1440
|
-
}
|
|
1441
|
-
isToolResultObject(value) {
|
|
1442
|
-
return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
|
|
1733
|
+
return await session._handleSystemMessageTransform(params.sections);
|
|
1443
1734
|
}
|
|
1444
1735
|
}
|
|
1445
1736
|
export {
|