@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/cjs/client.js
CHANGED
|
@@ -34,9 +34,10 @@ var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
|
|
|
34
34
|
var import_session = require("./session.js");
|
|
35
35
|
var import_sessionFsProvider = require("./sessionFsProvider.js");
|
|
36
36
|
var import_telemetry = require("./telemetry.js");
|
|
37
|
+
var import_toolSet = require("./toolSet.js");
|
|
37
38
|
var import_types = require("./types.js");
|
|
38
39
|
const import_meta = {};
|
|
39
|
-
const MIN_PROTOCOL_VERSION =
|
|
40
|
+
const MIN_PROTOCOL_VERSION = 3;
|
|
40
41
|
function isZodSchema(value) {
|
|
41
42
|
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
42
43
|
}
|
|
@@ -47,6 +48,53 @@ function toJsonSchema(parameters) {
|
|
|
47
48
|
}
|
|
48
49
|
return parameters;
|
|
49
50
|
}
|
|
51
|
+
function toWireMcpServers(mcpServers) {
|
|
52
|
+
if (!mcpServers) return void 0;
|
|
53
|
+
return Object.fromEntries(
|
|
54
|
+
Object.entries(mcpServers).map(([name, server]) => {
|
|
55
|
+
if ("workingDirectory" in server) {
|
|
56
|
+
const { workingDirectory, ...rest } = server;
|
|
57
|
+
return [name, { ...rest, cwd: workingDirectory }];
|
|
58
|
+
}
|
|
59
|
+
return [name, server];
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function toWireCustomAgents(agents) {
|
|
64
|
+
if (!agents) return void 0;
|
|
65
|
+
return agents.map((agent) => {
|
|
66
|
+
if (!agent.mcpServers) return agent;
|
|
67
|
+
const { mcpServers, ...rest } = agent;
|
|
68
|
+
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function toWireLargeOutput(config) {
|
|
72
|
+
if (!config) return void 0;
|
|
73
|
+
const { outputDirectory, ...rest } = config;
|
|
74
|
+
const wire = { ...rest };
|
|
75
|
+
if (outputDirectory !== void 0) {
|
|
76
|
+
wire.outputDir = outputDirectory;
|
|
77
|
+
}
|
|
78
|
+
return wire;
|
|
79
|
+
}
|
|
80
|
+
function toolFilterListToArray(value) {
|
|
81
|
+
if (value === void 0) {
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
return value instanceof import_toolSet.ToolSet ? value.toArray() : value;
|
|
85
|
+
}
|
|
86
|
+
function validateToolFilterList(field, list) {
|
|
87
|
+
if (!list) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
for (const entry of list) {
|
|
91
|
+
if (entry === "*") {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Invalid ${field} entry '*': there is no bare wildcard. Use one or more of \`new ToolSet().addBuiltIn('*')\`, \`.addMcp('*')\`, or \`.addCustom('*')\` to target a specific source.`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
50
98
|
function extractTransformCallbacks(systemMessage) {
|
|
51
99
|
if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
|
|
52
100
|
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
@@ -100,15 +148,23 @@ class CopilotClient {
|
|
|
100
148
|
cliProcess = null;
|
|
101
149
|
connection = null;
|
|
102
150
|
socket = null;
|
|
103
|
-
|
|
151
|
+
runtimePort = null;
|
|
104
152
|
actualHost = "localhost";
|
|
105
153
|
state = "disconnected";
|
|
106
154
|
sessions = /* @__PURE__ */ new Map();
|
|
107
155
|
stderrBuffer = "";
|
|
108
156
|
// Captures CLI stderr for error messages
|
|
157
|
+
/** Resolved connection mode chosen in the constructor. */
|
|
158
|
+
connectionConfig;
|
|
159
|
+
/** Resolved path to the runtime executable (only used for child-process kinds). */
|
|
160
|
+
resolvedCliPath;
|
|
161
|
+
/** Resolved environment passed to the spawned runtime. */
|
|
162
|
+
resolvedEnv;
|
|
109
163
|
options;
|
|
110
164
|
isExternalServer = false;
|
|
111
165
|
forceStopping = false;
|
|
166
|
+
/** Token sent in `connect`; auto-generated when the SDK spawns its own CLI in TCP mode. */
|
|
167
|
+
effectiveConnectionToken;
|
|
112
168
|
onListModels;
|
|
113
169
|
onGetTraceContext;
|
|
114
170
|
modelsCache = null;
|
|
@@ -116,6 +172,7 @@ class CopilotClient {
|
|
|
116
172
|
sessionLifecycleHandlers = /* @__PURE__ */ new Set();
|
|
117
173
|
typedLifecycleHandlers = /* @__PURE__ */ new Map();
|
|
118
174
|
_rpc = null;
|
|
175
|
+
_internalRpc = null;
|
|
119
176
|
processExitPromise = null;
|
|
120
177
|
// Rejects when CLI process exits
|
|
121
178
|
negotiatedProtocolVersion = null;
|
|
@@ -134,77 +191,107 @@ class CopilotClient {
|
|
|
134
191
|
}
|
|
135
192
|
return this._rpc;
|
|
136
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Internal RPC surface (e.g. handshake helpers). Not part of the public API.
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
get internalRpc() {
|
|
199
|
+
if (!this.connection) {
|
|
200
|
+
throw new Error("Client is not connected. Call start() first.");
|
|
201
|
+
}
|
|
202
|
+
if (!this._internalRpc) {
|
|
203
|
+
this._internalRpc = (0, import_rpc.createInternalServerRpc)(this.connection);
|
|
204
|
+
}
|
|
205
|
+
return this._internalRpc;
|
|
206
|
+
}
|
|
137
207
|
/**
|
|
138
208
|
* Creates a new CopilotClient instance.
|
|
139
209
|
*
|
|
140
210
|
* @param options - Configuration options for the client
|
|
141
|
-
* @throws Error if mutually exclusive options are provided (e.g., cliUrl with useStdio or cliPath)
|
|
142
211
|
*
|
|
143
212
|
* @example
|
|
144
213
|
* ```typescript
|
|
145
|
-
* // Default
|
|
214
|
+
* // Default: spawns the bundled runtime over stdio
|
|
146
215
|
* const client = new CopilotClient();
|
|
147
216
|
*
|
|
148
|
-
* // Connect to an existing
|
|
149
|
-
* const client = new CopilotClient({
|
|
217
|
+
* // Connect to an existing runtime
|
|
218
|
+
* const client = new CopilotClient({
|
|
219
|
+
* connection: RuntimeConnection.forUri("localhost:3000"),
|
|
220
|
+
* });
|
|
150
221
|
*
|
|
151
|
-
* //
|
|
222
|
+
* // Spawn the runtime over TCP on a chosen port
|
|
152
223
|
* const client = new CopilotClient({
|
|
153
|
-
*
|
|
154
|
-
*
|
|
224
|
+
* connection: RuntimeConnection.forTcp({ port: 9001 }),
|
|
225
|
+
* });
|
|
226
|
+
*
|
|
227
|
+
* // Use a custom runtime binary
|
|
228
|
+
* const client = new CopilotClient({
|
|
229
|
+
* connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }),
|
|
230
|
+
* logLevel: "debug",
|
|
155
231
|
* });
|
|
156
232
|
* ```
|
|
157
233
|
*/
|
|
158
234
|
constructor(options = {}) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
if (options.isChildProcess && (options.cliUrl || options.useStdio === false)) {
|
|
235
|
+
const conn = options._internalConnection ?? options.connection ?? { kind: "stdio" };
|
|
236
|
+
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
163
237
|
throw new Error(
|
|
164
|
-
"
|
|
238
|
+
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
165
239
|
);
|
|
166
240
|
}
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
241
|
+
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
242
|
+
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
243
|
+
throw new Error("connectionToken must be a non-empty string");
|
|
244
|
+
}
|
|
171
245
|
}
|
|
246
|
+
this.connectionConfig = conn;
|
|
172
247
|
if (options.sessionFs) {
|
|
173
248
|
this.validateSessionFsConfig(options.sessionFs);
|
|
174
249
|
}
|
|
175
|
-
if (
|
|
176
|
-
const { host, port } = this.parseCliUrl(
|
|
250
|
+
if (conn.kind === "uri") {
|
|
251
|
+
const { host, port } = this.parseCliUrl(conn.url);
|
|
177
252
|
this.actualHost = host;
|
|
178
|
-
this.
|
|
253
|
+
this.runtimePort = port;
|
|
179
254
|
this.isExternalServer = true;
|
|
180
|
-
}
|
|
181
|
-
if (options.isChildProcess) {
|
|
255
|
+
} else if (conn.kind === "parent-process") {
|
|
182
256
|
this.isExternalServer = true;
|
|
183
257
|
}
|
|
258
|
+
if (conn.kind === "tcp") {
|
|
259
|
+
this.effectiveConnectionToken = conn.connectionToken ?? (0, import_node_crypto.randomUUID)();
|
|
260
|
+
} else if (conn.kind === "uri") {
|
|
261
|
+
this.effectiveConnectionToken = conn.connectionToken;
|
|
262
|
+
}
|
|
184
263
|
this.onListModels = options.onListModels;
|
|
185
264
|
this.onGetTraceContext = options.onGetTraceContext;
|
|
186
265
|
this.sessionFsConfig = options.sessionFs ?? null;
|
|
187
266
|
const effectiveEnv = options.env ?? process.env;
|
|
267
|
+
this.resolvedEnv = effectiveEnv;
|
|
268
|
+
this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
|
|
269
|
+
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
270
|
+
this.connectionExtraArgs = [...connArgs];
|
|
188
271
|
this.options = {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
cwd: options.cwd ?? process.cwd(),
|
|
192
|
-
port: options.port || 0,
|
|
193
|
-
useStdio: options.cliUrl ? false : options.useStdio ?? true,
|
|
194
|
-
// Default to stdio unless cliUrl is provided
|
|
195
|
-
isChildProcess: options.isChildProcess ?? false,
|
|
196
|
-
cliUrl: options.cliUrl,
|
|
197
|
-
logLevel: options.logLevel || "debug",
|
|
198
|
-
autoStart: options.autoStart ?? true,
|
|
199
|
-
autoRestart: false,
|
|
200
|
-
env: effectiveEnv,
|
|
272
|
+
workingDirectory: options.workingDirectory ?? process.cwd(),
|
|
273
|
+
logLevel: options.logLevel,
|
|
201
274
|
gitHubToken: options.gitHubToken,
|
|
202
|
-
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true
|
|
275
|
+
// Default useLoggedInUser to false when gitHubToken is provided, otherwise true.
|
|
203
276
|
useLoggedInUser: options.useLoggedInUser ?? (options.gitHubToken ? false : true),
|
|
204
277
|
telemetry: options.telemetry,
|
|
205
|
-
|
|
278
|
+
baseDirectory: options.baseDirectory,
|
|
279
|
+
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
280
|
+
enableRemoteSessions: options.enableRemoteSessions ?? false,
|
|
281
|
+
mode: options.mode ?? "copilot-cli"
|
|
206
282
|
};
|
|
283
|
+
if (this.options.mode === "empty") {
|
|
284
|
+
const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
|
|
285
|
+
// can't enforce it from here.
|
|
286
|
+
conn.kind === "uri" || conn.kind === "parent-process";
|
|
287
|
+
if (!hasPersistence) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
"CopilotClient was created with mode: 'empty' but neither 'baseDirectory' nor 'sessionFs' was set. Empty mode requires an explicit per-session persistence location; pick one."
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
207
293
|
}
|
|
294
|
+
connectionExtraArgs = [];
|
|
208
295
|
/**
|
|
209
296
|
* Parse CLI URL into host and port
|
|
210
297
|
* Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
|
|
@@ -238,20 +325,37 @@ class CopilotClient {
|
|
|
238
325
|
throw new Error("sessionFs.conventions must be either 'windows' or 'posix'");
|
|
239
326
|
}
|
|
240
327
|
}
|
|
328
|
+
setupSessionFs(session, config) {
|
|
329
|
+
if (!this.sessionFsConfig) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (!config.createSessionFsProvider) {
|
|
333
|
+
throw new Error(
|
|
334
|
+
"createSessionFsProvider is required in session config when sessionFs is enabled in client options."
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
const provider = config.createSessionFsProvider(session);
|
|
338
|
+
if (this.sessionFsConfig.capabilities?.sqlite && !provider.sqlite) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
"SessionFsConfig declares capabilities.sqlite but the provider does not implement sqlite."
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(provider);
|
|
344
|
+
}
|
|
241
345
|
/**
|
|
242
346
|
* Starts the CLI server and establishes a connection.
|
|
243
347
|
*
|
|
244
348
|
* If connecting to an external server (via cliUrl), only establishes the connection.
|
|
245
349
|
* Otherwise, spawns the CLI server process and then connects.
|
|
246
350
|
*
|
|
247
|
-
* This method is called automatically
|
|
351
|
+
* This method is called automatically the first time you create or resume a session.
|
|
248
352
|
*
|
|
249
353
|
* @returns A promise that resolves when the connection is established
|
|
250
354
|
* @throws Error if the server fails to start or the connection fails
|
|
251
355
|
*
|
|
252
356
|
* @example
|
|
253
357
|
* ```typescript
|
|
254
|
-
* const client = new CopilotClient(
|
|
358
|
+
* const client = new CopilotClient();
|
|
255
359
|
* await client.start();
|
|
256
360
|
* // Now ready to create sessions
|
|
257
361
|
* ```
|
|
@@ -271,7 +375,8 @@ class CopilotClient {
|
|
|
271
375
|
await this.connection.sendRequest("sessionFs.setProvider", {
|
|
272
376
|
initialCwd: this.sessionFsConfig.initialCwd,
|
|
273
377
|
sessionStatePath: this.sessionFsConfig.sessionStatePath,
|
|
274
|
-
conventions: this.sessionFsConfig.conventions
|
|
378
|
+
conventions: this.sessionFsConfig.conventions,
|
|
379
|
+
capabilities: this.sessionFsConfig.capabilities
|
|
275
380
|
});
|
|
276
381
|
}
|
|
277
382
|
this.state = "connected";
|
|
@@ -345,8 +450,15 @@ class CopilotClient {
|
|
|
345
450
|
}
|
|
346
451
|
this.modelsCache = null;
|
|
347
452
|
if (this.socket) {
|
|
453
|
+
const socket = this.socket;
|
|
454
|
+
this.socket = null;
|
|
348
455
|
try {
|
|
349
|
-
|
|
456
|
+
if (!socket.destroyed) {
|
|
457
|
+
await new Promise((resolve) => {
|
|
458
|
+
socket.once("close", () => resolve());
|
|
459
|
+
socket.end();
|
|
460
|
+
});
|
|
461
|
+
}
|
|
350
462
|
} catch (error) {
|
|
351
463
|
errors.push(
|
|
352
464
|
new Error(
|
|
@@ -354,11 +466,18 @@ class CopilotClient {
|
|
|
354
466
|
)
|
|
355
467
|
);
|
|
356
468
|
}
|
|
357
|
-
this.socket = null;
|
|
358
469
|
}
|
|
359
470
|
if (this.cliProcess && !this.isExternalServer) {
|
|
471
|
+
const child = this.cliProcess;
|
|
472
|
+
this.cliProcess = null;
|
|
360
473
|
try {
|
|
361
|
-
|
|
474
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
475
|
+
const exited = new Promise((resolve) => {
|
|
476
|
+
child.once("exit", () => resolve());
|
|
477
|
+
});
|
|
478
|
+
child.kill();
|
|
479
|
+
await exited;
|
|
480
|
+
}
|
|
362
481
|
} catch (error) {
|
|
363
482
|
errors.push(
|
|
364
483
|
new Error(
|
|
@@ -366,18 +485,32 @@ class CopilotClient {
|
|
|
366
485
|
)
|
|
367
486
|
);
|
|
368
487
|
}
|
|
369
|
-
this.cliProcess = null;
|
|
370
488
|
}
|
|
371
489
|
if (this.cliStartTimeout) {
|
|
372
490
|
clearTimeout(this.cliStartTimeout);
|
|
373
491
|
this.cliStartTimeout = null;
|
|
374
492
|
}
|
|
375
493
|
this.state = "disconnected";
|
|
376
|
-
this.
|
|
494
|
+
this.runtimePort = null;
|
|
377
495
|
this.stderrBuffer = "";
|
|
378
496
|
this.processExitPromise = null;
|
|
379
497
|
return errors;
|
|
380
498
|
}
|
|
499
|
+
/**
|
|
500
|
+
* Alias for {@link stop} that lets `CopilotClient` participate in `await using`
|
|
501
|
+
* blocks for automatic cleanup.
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* ```typescript
|
|
505
|
+
* await using client = new CopilotClient();
|
|
506
|
+
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
507
|
+
* await session.sendAndWait("Hello");
|
|
508
|
+
* // client.stop() is called automatically when the block exits.
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
511
|
+
async [Symbol.asyncDispose]() {
|
|
512
|
+
await this.stop();
|
|
513
|
+
}
|
|
381
514
|
/**
|
|
382
515
|
* Forcefully stops the CLI server without graceful cleanup.
|
|
383
516
|
*
|
|
@@ -434,7 +567,7 @@ class CopilotClient {
|
|
|
434
567
|
this.cliStartTimeout = null;
|
|
435
568
|
}
|
|
436
569
|
this.state = "disconnected";
|
|
437
|
-
this.
|
|
570
|
+
this.runtimePort = null;
|
|
438
571
|
this.stderrBuffer = "";
|
|
439
572
|
this.processExitPromise = null;
|
|
440
573
|
}
|
|
@@ -442,12 +575,11 @@ class CopilotClient {
|
|
|
442
575
|
* Creates a new conversation session with the Copilot CLI.
|
|
443
576
|
*
|
|
444
577
|
* Sessions maintain conversation state, handle events, and manage tool execution.
|
|
445
|
-
* If the client is not connected
|
|
446
|
-
* start the connection.
|
|
578
|
+
* If the client is not connected, this method automatically starts the connection.
|
|
447
579
|
*
|
|
448
580
|
* @param config - Optional configuration for the session
|
|
449
581
|
* @returns A promise that resolves with the created session
|
|
450
|
-
* @throws Error if the client
|
|
582
|
+
* @throws Error if the client fails to start
|
|
451
583
|
*
|
|
452
584
|
* @example
|
|
453
585
|
* ```typescript
|
|
@@ -467,66 +599,189 @@ class CopilotClient {
|
|
|
467
599
|
* });
|
|
468
600
|
* ```
|
|
469
601
|
*/
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
602
|
+
/**
|
|
603
|
+
* Normalizes session-level tool filter options. Converts {@link ToolSet}
|
|
604
|
+
* instances to plain string arrays, rejects misuse (bare `"*"`) and the
|
|
605
|
+
* missing-availableTools case in `mode = "empty"`.
|
|
606
|
+
*
|
|
607
|
+
* The SDK always sends `toolFilterPrecedence: "excluded"` so callers can
|
|
608
|
+
* compose include + exclude lists naturally (e.g. "everything matching X
|
|
609
|
+
* except Y") regardless of mode. Allowlist-precedence is intentionally not
|
|
610
|
+
* exposed — it's available on the runtime side as a CLI-only concession to
|
|
611
|
+
* legacy behavior, but SDK consumers always get the composable semantics.
|
|
612
|
+
*
|
|
613
|
+
* @internal
|
|
614
|
+
*/
|
|
615
|
+
resolveToolFilterOptions(config) {
|
|
616
|
+
const availableTools = toolFilterListToArray(config.availableTools);
|
|
617
|
+
const excludedTools = toolFilterListToArray(config.excludedTools);
|
|
618
|
+
validateToolFilterList("availableTools", availableTools);
|
|
619
|
+
validateToolFilterList("excludedTools", excludedTools);
|
|
620
|
+
if (this.options.mode === "empty") {
|
|
621
|
+
if (availableTools === void 0) {
|
|
622
|
+
throw new Error(
|
|
623
|
+
"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)`."
|
|
624
|
+
);
|
|
481
625
|
}
|
|
482
626
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
627
|
+
return { availableTools, excludedTools, toolFilterPrecedence: "excluded" };
|
|
628
|
+
}
|
|
629
|
+
/** Mode-specific defaults spread under the caller's config (app values win). */
|
|
630
|
+
configDefaultsForMode() {
|
|
631
|
+
if (this.options.mode === "empty") {
|
|
632
|
+
return {
|
|
633
|
+
enableSessionTelemetry: false,
|
|
634
|
+
mcpOAuthTokenStorage: "in-memory",
|
|
635
|
+
skipEmbeddingRetrieval: true,
|
|
636
|
+
embeddingCacheStorage: "in-memory",
|
|
637
|
+
enableOnDemandInstructionDiscovery: false,
|
|
638
|
+
enableFileHooks: false,
|
|
639
|
+
enableHostGitOperations: false,
|
|
640
|
+
enableSessionStore: false,
|
|
641
|
+
enableSkills: false
|
|
642
|
+
};
|
|
495
643
|
}
|
|
496
|
-
|
|
497
|
-
|
|
644
|
+
return {};
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Returns the systemMessage config to use, adjusted for the current mode.
|
|
648
|
+
* In empty mode we ensure the environment_context section is removed
|
|
649
|
+
* unless the app has already taken control of it. `append` (and
|
|
650
|
+
* unspecified) mode is promoted to `customize` so we can also strip
|
|
651
|
+
* environment_context; the caller's `content` is preserved verbatim
|
|
652
|
+
* because the runtime appends it as additional instructions in both
|
|
653
|
+
* customize and append modes.
|
|
654
|
+
*/
|
|
655
|
+
getSystemMessageConfigForMode(supplied) {
|
|
656
|
+
if (this.options.mode !== "empty") return supplied;
|
|
657
|
+
if (!supplied) {
|
|
658
|
+
return {
|
|
659
|
+
mode: "customize",
|
|
660
|
+
sections: { environment_context: { action: "remove" } }
|
|
661
|
+
};
|
|
498
662
|
}
|
|
499
|
-
|
|
500
|
-
|
|
663
|
+
switch (supplied.mode) {
|
|
664
|
+
case "replace":
|
|
665
|
+
return supplied;
|
|
666
|
+
case "customize":
|
|
667
|
+
if (supplied.sections?.environment_context) return supplied;
|
|
668
|
+
return {
|
|
669
|
+
...supplied,
|
|
670
|
+
sections: {
|
|
671
|
+
...supplied.sections,
|
|
672
|
+
environment_context: { action: "remove" }
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
case "append":
|
|
676
|
+
case void 0:
|
|
677
|
+
return {
|
|
678
|
+
mode: "customize",
|
|
679
|
+
content: supplied.content,
|
|
680
|
+
sections: { environment_context: { action: "remove" } }
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Mode-specific options applied via session.options.update after create/resume.
|
|
686
|
+
*
|
|
687
|
+
* In empty mode, defaults the four overridable feature flags to safe values
|
|
688
|
+
* (caller values from `config` win). `installedPlugins=[]` is unconditional
|
|
689
|
+
* in empty mode — apps that need custom plugins should switch modes.
|
|
690
|
+
*/
|
|
691
|
+
async updateSessionOptionsForMode(session, config) {
|
|
692
|
+
const patch = {};
|
|
693
|
+
if (this.options.mode === "empty") {
|
|
694
|
+
patch.skipCustomInstructions = config.skipCustomInstructions ?? true;
|
|
695
|
+
patch.customAgentsLocalOnly = config.customAgentsLocalOnly ?? true;
|
|
696
|
+
patch.coauthorEnabled = config.coauthorEnabled ?? false;
|
|
697
|
+
patch.manageScheduleEnabled = config.manageScheduleEnabled ?? false;
|
|
698
|
+
patch.installedPlugins = [];
|
|
699
|
+
} else {
|
|
700
|
+
if (config.skipCustomInstructions !== void 0)
|
|
701
|
+
patch.skipCustomInstructions = config.skipCustomInstructions;
|
|
702
|
+
if (config.customAgentsLocalOnly !== void 0)
|
|
703
|
+
patch.customAgentsLocalOnly = config.customAgentsLocalOnly;
|
|
704
|
+
if (config.coauthorEnabled !== void 0)
|
|
705
|
+
patch.coauthorEnabled = config.coauthorEnabled;
|
|
706
|
+
if (config.manageScheduleEnabled !== void 0)
|
|
707
|
+
patch.manageScheduleEnabled = config.manageScheduleEnabled;
|
|
708
|
+
}
|
|
709
|
+
if (Object.keys(patch).length === 0) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
await session.rpc.options.update(patch);
|
|
714
|
+
} catch (e) {
|
|
715
|
+
try {
|
|
716
|
+
await session.disconnect();
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
throw e;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async createSession(config) {
|
|
723
|
+
if (!this.connection) {
|
|
724
|
+
await this.start();
|
|
501
725
|
}
|
|
726
|
+
config = { ...this.configDefaultsForMode(), ...config };
|
|
727
|
+
config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
|
|
728
|
+
const callerSessionId = config.sessionId;
|
|
729
|
+
const useServerGeneratedId = config.cloud != null && callerSessionId == null;
|
|
730
|
+
const localSessionId = useServerGeneratedId ? void 0 : callerSessionId ?? (0, import_node_crypto.randomUUID)();
|
|
502
731
|
const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
|
|
503
732
|
config.systemMessage
|
|
504
733
|
);
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
);
|
|
734
|
+
const initializeSession = (sessionId) => {
|
|
735
|
+
const s = new import_session.CopilotSession(
|
|
736
|
+
sessionId,
|
|
737
|
+
this.connection,
|
|
738
|
+
void 0,
|
|
739
|
+
this.onGetTraceContext
|
|
740
|
+
);
|
|
741
|
+
s.registerTools(config.tools);
|
|
742
|
+
s.registerCanvases(config.canvases);
|
|
743
|
+
s.registerCommands(config.commands);
|
|
744
|
+
s.registerPermissionHandler(config.onPermissionRequest);
|
|
745
|
+
if (config.onUserInputRequest) {
|
|
746
|
+
s.registerUserInputHandler(config.onUserInputRequest);
|
|
747
|
+
}
|
|
748
|
+
if (config.onElicitationRequest) {
|
|
749
|
+
s.registerElicitationHandler(config.onElicitationRequest);
|
|
750
|
+
}
|
|
751
|
+
if (config.onExitPlanModeRequest) {
|
|
752
|
+
s.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
521
753
|
}
|
|
754
|
+
if (config.onAutoModeSwitchRequest) {
|
|
755
|
+
s.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
756
|
+
}
|
|
757
|
+
if (config.hooks) {
|
|
758
|
+
s.registerHooks(config.hooks);
|
|
759
|
+
}
|
|
760
|
+
if (transformCallbacks) {
|
|
761
|
+
s.registerTransformCallbacks(transformCallbacks);
|
|
762
|
+
}
|
|
763
|
+
if (config.onEvent) {
|
|
764
|
+
s.on(config.onEvent);
|
|
765
|
+
}
|
|
766
|
+
this.sessions.set(sessionId, s);
|
|
767
|
+
this.setupSessionFs(s, config);
|
|
768
|
+
return s;
|
|
769
|
+
};
|
|
770
|
+
let session;
|
|
771
|
+
let registeredId;
|
|
772
|
+
if (localSessionId !== void 0) {
|
|
773
|
+
session = initializeSession(localSessionId);
|
|
774
|
+
registeredId = localSessionId;
|
|
522
775
|
}
|
|
776
|
+
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
523
777
|
try {
|
|
524
778
|
const response = await this.connection.sendRequest("session.create", {
|
|
525
779
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
526
780
|
model: config.model,
|
|
527
|
-
sessionId,
|
|
781
|
+
sessionId: localSessionId,
|
|
528
782
|
clientName: config.clientName,
|
|
529
783
|
reasoningEffort: config.reasoningEffort,
|
|
784
|
+
reasoningSummary: config.reasoningSummary,
|
|
530
785
|
tools: config.tools?.map((tool) => ({
|
|
531
786
|
name: tool.name,
|
|
532
787
|
description: tool.description,
|
|
@@ -534,39 +789,81 @@ class CopilotClient {
|
|
|
534
789
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
535
790
|
skipPermission: tool.skipPermission
|
|
536
791
|
})),
|
|
792
|
+
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
793
|
+
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
794
|
+
requestExtensions: config.requestExtensions,
|
|
795
|
+
extensionInfo: config.extensionInfo,
|
|
537
796
|
commands: config.commands?.map((cmd) => ({
|
|
538
797
|
name: cmd.name,
|
|
539
798
|
description: cmd.description
|
|
540
799
|
})),
|
|
541
800
|
systemMessage: wireSystemMessage,
|
|
542
|
-
availableTools:
|
|
543
|
-
excludedTools:
|
|
801
|
+
availableTools: toolFilterOptions.availableTools,
|
|
802
|
+
excludedTools: toolFilterOptions.excludedTools,
|
|
803
|
+
toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
|
|
544
804
|
provider: config.provider,
|
|
805
|
+
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
545
806
|
modelCapabilities: config.modelCapabilities,
|
|
546
|
-
|
|
807
|
+
largeOutput: toWireLargeOutput(config.largeOutput),
|
|
808
|
+
requestPermission: !!config.onPermissionRequest,
|
|
547
809
|
requestUserInput: !!config.onUserInputRequest,
|
|
548
810
|
requestElicitation: !!config.onElicitationRequest,
|
|
811
|
+
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
812
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
813
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
549
814
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
550
815
|
workingDirectory: config.workingDirectory,
|
|
551
816
|
streaming: config.streaming,
|
|
552
817
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
553
|
-
mcpServers: config.mcpServers,
|
|
818
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
819
|
+
mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
|
|
554
820
|
envValueMode: "direct",
|
|
555
|
-
customAgents: config.customAgents,
|
|
821
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
556
822
|
defaultAgent: config.defaultAgent,
|
|
557
823
|
agent: config.agent,
|
|
558
|
-
configDir: config.
|
|
824
|
+
configDir: config.configDirectory,
|
|
559
825
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
826
|
+
skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
|
|
827
|
+
embeddingCacheStorage: config.embeddingCacheStorage,
|
|
828
|
+
organizationCustomInstructions: config.organizationCustomInstructions,
|
|
829
|
+
enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
|
|
830
|
+
enableFileHooks: config.enableFileHooks,
|
|
831
|
+
enableHostGitOperations: config.enableHostGitOperations,
|
|
832
|
+
enableSessionStore: config.enableSessionStore,
|
|
833
|
+
enableSkills: config.enableSkills,
|
|
560
834
|
skillDirectories: config.skillDirectories,
|
|
835
|
+
pluginDirectories: config.pluginDirectories,
|
|
836
|
+
instructionDirectories: config.instructionDirectories,
|
|
561
837
|
disabledSkills: config.disabledSkills,
|
|
562
838
|
infiniteSessions: config.infiniteSessions,
|
|
563
|
-
gitHubToken: config.gitHubToken
|
|
839
|
+
gitHubToken: config.gitHubToken,
|
|
840
|
+
remoteSession: config.remoteSession,
|
|
841
|
+
cloud: config.cloud
|
|
564
842
|
});
|
|
565
|
-
const {
|
|
843
|
+
const {
|
|
844
|
+
sessionId: returnedSessionId,
|
|
845
|
+
workspacePath,
|
|
846
|
+
capabilities
|
|
847
|
+
} = response;
|
|
848
|
+
if (!returnedSessionId) {
|
|
849
|
+
throw new Error("session.create response did not include a sessionId");
|
|
850
|
+
}
|
|
851
|
+
if (localSessionId !== void 0 && localSessionId !== returnedSessionId) {
|
|
852
|
+
throw new Error(
|
|
853
|
+
`session.create returned sessionId ${returnedSessionId} but the caller requested ${localSessionId}`
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
if (session === void 0) {
|
|
857
|
+
session = initializeSession(returnedSessionId);
|
|
858
|
+
registeredId = returnedSessionId;
|
|
859
|
+
}
|
|
566
860
|
session["_workspacePath"] = workspacePath;
|
|
567
861
|
session.setCapabilities(capabilities);
|
|
862
|
+
await this.updateSessionOptionsForMode(session, config);
|
|
568
863
|
} catch (e) {
|
|
569
|
-
|
|
864
|
+
if (registeredId !== void 0) {
|
|
865
|
+
this.sessions.delete(registeredId);
|
|
866
|
+
}
|
|
570
867
|
throw e;
|
|
571
868
|
}
|
|
572
869
|
return session;
|
|
@@ -596,17 +893,8 @@ class CopilotClient {
|
|
|
596
893
|
* ```
|
|
597
894
|
*/
|
|
598
895
|
async resumeSession(sessionId, config) {
|
|
599
|
-
if (!config?.onPermissionRequest) {
|
|
600
|
-
throw new Error(
|
|
601
|
-
"An onPermissionRequest handler is required when resuming a session. For example, to allow all permissions, use { onPermissionRequest: approveAll }."
|
|
602
|
-
);
|
|
603
|
-
}
|
|
604
896
|
if (!this.connection) {
|
|
605
|
-
|
|
606
|
-
await this.start();
|
|
607
|
-
} else {
|
|
608
|
-
throw new Error("Client not connected. Call start() first.");
|
|
609
|
-
}
|
|
897
|
+
await this.start();
|
|
610
898
|
}
|
|
611
899
|
const session = new import_session.CopilotSession(
|
|
612
900
|
sessionId,
|
|
@@ -615,6 +903,7 @@ class CopilotClient {
|
|
|
615
903
|
this.onGetTraceContext
|
|
616
904
|
);
|
|
617
905
|
session.registerTools(config.tools);
|
|
906
|
+
session.registerCanvases(config.canvases);
|
|
618
907
|
session.registerCommands(config.commands);
|
|
619
908
|
session.registerPermissionHandler(config.onPermissionRequest);
|
|
620
909
|
if (config.onUserInputRequest) {
|
|
@@ -623,9 +912,17 @@ class CopilotClient {
|
|
|
623
912
|
if (config.onElicitationRequest) {
|
|
624
913
|
session.registerElicitationHandler(config.onElicitationRequest);
|
|
625
914
|
}
|
|
915
|
+
if (config.onExitPlanModeRequest) {
|
|
916
|
+
session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
|
|
917
|
+
}
|
|
918
|
+
if (config.onAutoModeSwitchRequest) {
|
|
919
|
+
session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
|
|
920
|
+
}
|
|
626
921
|
if (config.hooks) {
|
|
627
922
|
session.registerHooks(config.hooks);
|
|
628
923
|
}
|
|
924
|
+
config = { ...this.configDefaultsForMode(), ...config };
|
|
925
|
+
config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
|
|
629
926
|
const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
|
|
630
927
|
config.systemMessage
|
|
631
928
|
);
|
|
@@ -636,17 +933,8 @@ class CopilotClient {
|
|
|
636
933
|
session.on(config.onEvent);
|
|
637
934
|
}
|
|
638
935
|
this.sessions.set(sessionId, session);
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
session.clientSessionApis.sessionFs = (0, import_sessionFsProvider.createSessionFsAdapter)(
|
|
642
|
-
config.createSessionFsHandler(session)
|
|
643
|
-
);
|
|
644
|
-
} else {
|
|
645
|
-
throw new Error(
|
|
646
|
-
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
936
|
+
this.setupSessionFs(session, config);
|
|
937
|
+
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
650
938
|
try {
|
|
651
939
|
const response = await this.connection.sendRequest("session.resume", {
|
|
652
940
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
@@ -654,9 +942,12 @@ class CopilotClient {
|
|
|
654
942
|
clientName: config.clientName,
|
|
655
943
|
model: config.model,
|
|
656
944
|
reasoningEffort: config.reasoningEffort,
|
|
945
|
+
reasoningSummary: config.reasoningSummary,
|
|
657
946
|
systemMessage: wireSystemMessage,
|
|
658
|
-
availableTools:
|
|
659
|
-
excludedTools:
|
|
947
|
+
availableTools: toolFilterOptions.availableTools,
|
|
948
|
+
excludedTools: toolFilterOptions.excludedTools,
|
|
949
|
+
toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
|
|
950
|
+
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
660
951
|
tools: config.tools?.map((tool) => ({
|
|
661
952
|
name: tool.name,
|
|
662
953
|
description: tool.description,
|
|
@@ -664,56 +955,65 @@ class CopilotClient {
|
|
|
664
955
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
665
956
|
skipPermission: tool.skipPermission
|
|
666
957
|
})),
|
|
958
|
+
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
959
|
+
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
960
|
+
requestExtensions: config.requestExtensions,
|
|
961
|
+
extensionInfo: config.extensionInfo,
|
|
667
962
|
commands: config.commands?.map((cmd) => ({
|
|
668
963
|
name: cmd.name,
|
|
669
964
|
description: cmd.description
|
|
670
965
|
})),
|
|
671
966
|
provider: config.provider,
|
|
672
967
|
modelCapabilities: config.modelCapabilities,
|
|
968
|
+
largeOutput: toWireLargeOutput(config.largeOutput),
|
|
673
969
|
requestPermission: config.onPermissionRequest !== import_types.defaultJoinSessionPermissionHandler,
|
|
674
970
|
requestUserInput: !!config.onUserInputRequest,
|
|
675
971
|
requestElicitation: !!config.onElicitationRequest,
|
|
972
|
+
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
973
|
+
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
974
|
+
requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
|
|
676
975
|
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
677
976
|
workingDirectory: config.workingDirectory,
|
|
678
|
-
configDir: config.
|
|
977
|
+
configDir: config.configDirectory,
|
|
679
978
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
979
|
+
skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
|
|
980
|
+
embeddingCacheStorage: config.embeddingCacheStorage,
|
|
981
|
+
organizationCustomInstructions: config.organizationCustomInstructions,
|
|
982
|
+
enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
|
|
983
|
+
enableFileHooks: config.enableFileHooks,
|
|
984
|
+
enableHostGitOperations: config.enableHostGitOperations,
|
|
985
|
+
enableSessionStore: config.enableSessionStore,
|
|
986
|
+
enableSkills: config.enableSkills,
|
|
680
987
|
streaming: config.streaming,
|
|
681
988
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
682
|
-
mcpServers: config.mcpServers,
|
|
989
|
+
mcpServers: toWireMcpServers(config.mcpServers),
|
|
990
|
+
mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
|
|
683
991
|
envValueMode: "direct",
|
|
684
|
-
customAgents: config.customAgents,
|
|
992
|
+
customAgents: toWireCustomAgents(config.customAgents),
|
|
685
993
|
defaultAgent: config.defaultAgent,
|
|
686
994
|
agent: config.agent,
|
|
687
995
|
skillDirectories: config.skillDirectories,
|
|
996
|
+
pluginDirectories: config.pluginDirectories,
|
|
997
|
+
instructionDirectories: config.instructionDirectories,
|
|
688
998
|
disabledSkills: config.disabledSkills,
|
|
689
999
|
infiniteSessions: config.infiniteSessions,
|
|
690
|
-
|
|
691
|
-
|
|
1000
|
+
suppressResumeEvent: config.suppressResumeEvent,
|
|
1001
|
+
continuePendingWork: config.continuePendingWork,
|
|
1002
|
+
gitHubToken: config.gitHubToken,
|
|
1003
|
+
remoteSession: config.remoteSession,
|
|
1004
|
+
openCanvases: config.openCanvases
|
|
692
1005
|
});
|
|
693
|
-
const { workspacePath, capabilities } = response;
|
|
1006
|
+
const { workspacePath, capabilities, openCanvases } = response;
|
|
694
1007
|
session["_workspacePath"] = workspacePath;
|
|
695
1008
|
session.setCapabilities(capabilities);
|
|
1009
|
+
session.setOpenCanvases(openCanvases ?? []);
|
|
1010
|
+
await this.updateSessionOptionsForMode(session, config);
|
|
696
1011
|
} catch (e) {
|
|
697
1012
|
this.sessions.delete(sessionId);
|
|
698
1013
|
throw e;
|
|
699
1014
|
}
|
|
700
1015
|
return session;
|
|
701
1016
|
}
|
|
702
|
-
/**
|
|
703
|
-
* Gets the current connection state of the client.
|
|
704
|
-
*
|
|
705
|
-
* @returns The current connection state: "disconnected", "connecting", "connected", or "error"
|
|
706
|
-
*
|
|
707
|
-
* @example
|
|
708
|
-
* ```typescript
|
|
709
|
-
* if (client.getState() === "connected") {
|
|
710
|
-
* const session = await client.createSession({ onPermissionRequest: approveAll });
|
|
711
|
-
* }
|
|
712
|
-
* ```
|
|
713
|
-
*/
|
|
714
|
-
getState() {
|
|
715
|
-
return this.state;
|
|
716
|
-
}
|
|
717
1017
|
/**
|
|
718
1018
|
* Sends a ping request to the server to verify connectivity.
|
|
719
1019
|
*
|
|
@@ -809,18 +1109,29 @@ class CopilotClient {
|
|
|
809
1109
|
}
|
|
810
1110
|
}
|
|
811
1111
|
/**
|
|
812
|
-
*
|
|
813
|
-
*
|
|
1112
|
+
* Send the `connect` handshake (carrying the optional token) and verify the
|
|
1113
|
+
* server's protocol version. Falls back to `ping` against legacy servers
|
|
1114
|
+
* that don't implement `connect`.
|
|
814
1115
|
*/
|
|
815
1116
|
async verifyProtocolVersion() {
|
|
1117
|
+
if (!this.connection) {
|
|
1118
|
+
throw new Error("Client not connected");
|
|
1119
|
+
}
|
|
816
1120
|
const maxVersion = (0, import_sdkProtocolVersion.getSdkProtocolVersion)();
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1121
|
+
const raceAgainstExit = (p) => this.processExitPromise ? Promise.race([p, this.processExitPromise]) : p;
|
|
1122
|
+
let serverVersion;
|
|
1123
|
+
try {
|
|
1124
|
+
const result = await raceAgainstExit(
|
|
1125
|
+
this.internalRpc.connect({ token: this.effectiveConnectionToken })
|
|
1126
|
+
);
|
|
1127
|
+
serverVersion = result.protocolVersion;
|
|
1128
|
+
} catch (err) {
|
|
1129
|
+
if (err instanceof import_node.ResponseError && (err.code === import_node.ErrorCodes.MethodNotFound || err.message === "Unhandled method connect")) {
|
|
1130
|
+
serverVersion = (await raceAgainstExit(this.ping())).protocolVersion;
|
|
1131
|
+
} else {
|
|
1132
|
+
throw err;
|
|
1133
|
+
}
|
|
822
1134
|
}
|
|
823
|
-
const serverVersion = pingResult.protocolVersion;
|
|
824
1135
|
if (serverVersion === void 0) {
|
|
825
1136
|
throw new Error(
|
|
826
1137
|
`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.`
|
|
@@ -904,8 +1215,13 @@ class CopilotClient {
|
|
|
904
1215
|
if (!this.connection) {
|
|
905
1216
|
throw new Error("Client not connected");
|
|
906
1217
|
}
|
|
1218
|
+
let wireFilter;
|
|
1219
|
+
if (filter) {
|
|
1220
|
+
const { workingDirectory, ...rest } = filter;
|
|
1221
|
+
wireFilter = { ...rest, cwd: workingDirectory };
|
|
1222
|
+
}
|
|
907
1223
|
const response = await this.connection.sendRequest("session.list", {
|
|
908
|
-
filter
|
|
1224
|
+
filter: wireFilter
|
|
909
1225
|
});
|
|
910
1226
|
const { sessions } = response;
|
|
911
1227
|
return sessions.map(CopilotClient.toSessionMetadata);
|
|
@@ -940,13 +1256,19 @@ class CopilotClient {
|
|
|
940
1256
|
return CopilotClient.toSessionMetadata(session);
|
|
941
1257
|
}
|
|
942
1258
|
static toSessionMetadata(raw) {
|
|
1259
|
+
const { context } = raw;
|
|
943
1260
|
return {
|
|
944
1261
|
sessionId: raw.sessionId,
|
|
945
1262
|
startTime: new Date(raw.startTime),
|
|
946
1263
|
modifiedTime: new Date(raw.modifiedTime),
|
|
947
1264
|
summary: raw.summary,
|
|
948
1265
|
isRemote: raw.isRemote,
|
|
949
|
-
context:
|
|
1266
|
+
context: context ? {
|
|
1267
|
+
workingDirectory: context.cwd,
|
|
1268
|
+
gitRoot: context.gitRoot,
|
|
1269
|
+
repository: context.repository,
|
|
1270
|
+
branch: context.branch
|
|
1271
|
+
} : void 0
|
|
950
1272
|
};
|
|
951
1273
|
}
|
|
952
1274
|
/**
|
|
@@ -999,7 +1321,7 @@ class CopilotClient {
|
|
|
999
1321
|
throw new Error(result.error || "Failed to set foreground session");
|
|
1000
1322
|
}
|
|
1001
1323
|
}
|
|
1002
|
-
|
|
1324
|
+
onLifecycle(eventTypeOrHandler, handler) {
|
|
1003
1325
|
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
1004
1326
|
const eventType = eventTypeOrHandler;
|
|
1005
1327
|
if (!this.typedLifecycleHandlers.has(eventType)) {
|
|
@@ -1026,17 +1348,17 @@ class CopilotClient {
|
|
|
1026
1348
|
async startCLIServer() {
|
|
1027
1349
|
return new Promise((resolve, reject) => {
|
|
1028
1350
|
this.stderrBuffer = "";
|
|
1029
|
-
const args = [
|
|
1030
|
-
|
|
1031
|
-
"--
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
this.options.logLevel
|
|
1035
|
-
];
|
|
1036
|
-
if (this.options.useStdio) {
|
|
1351
|
+
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1352
|
+
if (this.options.logLevel) {
|
|
1353
|
+
args.push("--log-level", this.options.logLevel);
|
|
1354
|
+
}
|
|
1355
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1037
1356
|
args.push("--stdio");
|
|
1038
|
-
} else if (this.
|
|
1039
|
-
|
|
1357
|
+
} else if (this.connectionConfig.kind === "tcp") {
|
|
1358
|
+
const requestedPort = this.connectionConfig.port ?? 0;
|
|
1359
|
+
if (requestedPort > 0) {
|
|
1360
|
+
args.push("--port", requestedPort.toString());
|
|
1361
|
+
}
|
|
1040
1362
|
}
|
|
1041
1363
|
if (this.options.gitHubToken) {
|
|
1042
1364
|
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
@@ -1050,14 +1372,26 @@ class CopilotClient {
|
|
|
1050
1372
|
this.options.sessionIdleTimeoutSeconds.toString()
|
|
1051
1373
|
);
|
|
1052
1374
|
}
|
|
1053
|
-
|
|
1375
|
+
if (this.options.enableRemoteSessions) {
|
|
1376
|
+
args.push("--remote");
|
|
1377
|
+
}
|
|
1378
|
+
const envWithoutNodeDebug = { ...this.resolvedEnv };
|
|
1054
1379
|
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
1055
1380
|
if (this.options.gitHubToken) {
|
|
1056
1381
|
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1057
1382
|
}
|
|
1058
|
-
if (
|
|
1383
|
+
if (this.effectiveConnectionToken) {
|
|
1384
|
+
envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1385
|
+
}
|
|
1386
|
+
if (this.options.baseDirectory) {
|
|
1387
|
+
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1388
|
+
}
|
|
1389
|
+
if (this.options.mode === "empty") {
|
|
1390
|
+
envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
|
|
1391
|
+
}
|
|
1392
|
+
if (!this.resolvedCliPath) {
|
|
1059
1393
|
throw new Error(
|
|
1060
|
-
"Path to Copilot CLI is required. Please
|
|
1394
|
+
"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."
|
|
1061
1395
|
);
|
|
1062
1396
|
}
|
|
1063
1397
|
if (this.options.telemetry) {
|
|
@@ -1076,31 +1410,31 @@ class CopilotClient {
|
|
|
1076
1410
|
t.captureContent
|
|
1077
1411
|
);
|
|
1078
1412
|
}
|
|
1079
|
-
if (!(0, import_node_fs.existsSync)(this.
|
|
1413
|
+
if (!(0, import_node_fs.existsSync)(this.resolvedCliPath)) {
|
|
1080
1414
|
throw new Error(
|
|
1081
|
-
`Copilot CLI not found at ${this.
|
|
1415
|
+
`Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
|
|
1082
1416
|
);
|
|
1083
1417
|
}
|
|
1084
|
-
const stdioConfig = this.
|
|
1085
|
-
const isJsFile = this.
|
|
1418
|
+
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
1419
|
+
const isJsFile = this.resolvedCliPath.endsWith(".js");
|
|
1086
1420
|
if (isJsFile) {
|
|
1087
|
-
this.cliProcess = (0, import_node_child_process.spawn)(getNodeExecPath(), [this.
|
|
1421
|
+
this.cliProcess = (0, import_node_child_process.spawn)(getNodeExecPath(), [this.resolvedCliPath, ...args], {
|
|
1088
1422
|
stdio: stdioConfig,
|
|
1089
|
-
cwd: this.options.
|
|
1423
|
+
cwd: this.options.workingDirectory,
|
|
1090
1424
|
env: envWithoutNodeDebug,
|
|
1091
1425
|
windowsHide: true
|
|
1092
1426
|
});
|
|
1093
1427
|
} else {
|
|
1094
|
-
this.cliProcess = (0, import_node_child_process.spawn)(this.
|
|
1428
|
+
this.cliProcess = (0, import_node_child_process.spawn)(this.resolvedCliPath, args, {
|
|
1095
1429
|
stdio: stdioConfig,
|
|
1096
|
-
cwd: this.options.
|
|
1430
|
+
cwd: this.options.workingDirectory,
|
|
1097
1431
|
env: envWithoutNodeDebug,
|
|
1098
1432
|
windowsHide: true
|
|
1099
1433
|
});
|
|
1100
1434
|
}
|
|
1101
1435
|
let stdout = "";
|
|
1102
1436
|
let resolved = false;
|
|
1103
|
-
if (this.
|
|
1437
|
+
if (this.connectionConfig.kind === "stdio") {
|
|
1104
1438
|
resolved = true;
|
|
1105
1439
|
resolve();
|
|
1106
1440
|
} else {
|
|
@@ -1108,7 +1442,7 @@ class CopilotClient {
|
|
|
1108
1442
|
stdout += data.toString();
|
|
1109
1443
|
const match = stdout.match(/listening on port (\d+)/i);
|
|
1110
1444
|
if (match && !resolved) {
|
|
1111
|
-
this.
|
|
1445
|
+
this.runtimePort = parseInt(match[1], 10);
|
|
1112
1446
|
resolved = true;
|
|
1113
1447
|
resolve();
|
|
1114
1448
|
}
|
|
@@ -1182,19 +1516,21 @@ stderr: ${stderrOutput}`
|
|
|
1182
1516
|
resolved = true;
|
|
1183
1517
|
reject(new Error("Timeout waiting for CLI server to start"));
|
|
1184
1518
|
}
|
|
1185
|
-
},
|
|
1519
|
+
}, 3e4);
|
|
1186
1520
|
});
|
|
1187
1521
|
}
|
|
1188
1522
|
/**
|
|
1189
1523
|
* Connect to the CLI server (via socket or stdio)
|
|
1190
1524
|
*/
|
|
1191
1525
|
async connectToServer() {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1526
|
+
switch (this.connectionConfig.kind) {
|
|
1527
|
+
case "parent-process":
|
|
1528
|
+
return this.connectToParentProcessViaStdio();
|
|
1529
|
+
case "stdio":
|
|
1530
|
+
return this.connectToChildProcessViaStdio();
|
|
1531
|
+
case "tcp":
|
|
1532
|
+
case "uri":
|
|
1533
|
+
return this.connectViaTcp();
|
|
1198
1534
|
}
|
|
1199
1535
|
}
|
|
1200
1536
|
/**
|
|
@@ -1234,12 +1570,17 @@ stderr: ${stderrOutput}`
|
|
|
1234
1570
|
* Connect to the CLI server via TCP socket
|
|
1235
1571
|
*/
|
|
1236
1572
|
async connectViaTcp() {
|
|
1237
|
-
if (!this.
|
|
1573
|
+
if (!this.runtimePort) {
|
|
1238
1574
|
throw new Error("Server port not available");
|
|
1239
1575
|
}
|
|
1240
1576
|
return new Promise((resolve, reject) => {
|
|
1241
1577
|
this.socket = new import_node_net.Socket();
|
|
1242
|
-
|
|
1578
|
+
const connectionTimeout = setTimeout(() => {
|
|
1579
|
+
this.socket?.destroy();
|
|
1580
|
+
reject(new Error("Timeout connecting to CLI server"));
|
|
1581
|
+
}, 1e4);
|
|
1582
|
+
this.socket.connect(this.runtimePort, this.actualHost, () => {
|
|
1583
|
+
clearTimeout(connectionTimeout);
|
|
1243
1584
|
this.connection = (0, import_node.createMessageConnection)(
|
|
1244
1585
|
new import_node.StreamMessageReader(this.socket),
|
|
1245
1586
|
new import_node.StreamMessageWriter(this.socket)
|
|
@@ -1249,6 +1590,7 @@ stderr: ${stderrOutput}`
|
|
|
1249
1590
|
resolve();
|
|
1250
1591
|
});
|
|
1251
1592
|
this.socket.on("error", (error) => {
|
|
1593
|
+
clearTimeout(connectionTimeout);
|
|
1252
1594
|
reject(new Error(`Failed to connect to CLI server: ${error.message}`));
|
|
1253
1595
|
});
|
|
1254
1596
|
});
|
|
@@ -1264,16 +1606,16 @@ stderr: ${stderrOutput}`
|
|
|
1264
1606
|
this.handleSessionLifecycleNotification(notification);
|
|
1265
1607
|
});
|
|
1266
1608
|
this.connection.onRequest(
|
|
1267
|
-
"
|
|
1268
|
-
async (params) => await this.
|
|
1609
|
+
"userInput.request",
|
|
1610
|
+
async (params) => await this.handleUserInputRequest(params)
|
|
1269
1611
|
);
|
|
1270
1612
|
this.connection.onRequest(
|
|
1271
|
-
"
|
|
1272
|
-
async (params) => await this.
|
|
1613
|
+
"exitPlanMode.request",
|
|
1614
|
+
async (params) => await this.handleExitPlanModeRequest(params)
|
|
1273
1615
|
);
|
|
1274
1616
|
this.connection.onRequest(
|
|
1275
|
-
"
|
|
1276
|
-
async (params) => await this.
|
|
1617
|
+
"autoModeSwitch.request",
|
|
1618
|
+
async (params) => await this.handleAutoModeSwitchRequest(params)
|
|
1277
1619
|
);
|
|
1278
1620
|
this.connection.onRequest(
|
|
1279
1621
|
"hooks.invoke",
|
|
@@ -1309,7 +1651,20 @@ stderr: ${stderrOutput}`
|
|
|
1309
1651
|
if (typeof notification !== "object" || !notification || !("type" in notification) || typeof notification.type !== "string" || !("sessionId" in notification) || typeof notification.sessionId !== "string") {
|
|
1310
1652
|
return;
|
|
1311
1653
|
}
|
|
1312
|
-
const
|
|
1654
|
+
const raw = notification;
|
|
1655
|
+
let metadata;
|
|
1656
|
+
if (raw.metadata && raw.metadata.startTime && raw.metadata.modifiedTime) {
|
|
1657
|
+
metadata = {
|
|
1658
|
+
startTime: new Date(raw.metadata.startTime),
|
|
1659
|
+
modifiedTime: new Date(raw.metadata.modifiedTime),
|
|
1660
|
+
summary: raw.metadata.summary
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
const event = {
|
|
1664
|
+
type: raw.type,
|
|
1665
|
+
sessionId: raw.sessionId,
|
|
1666
|
+
metadata
|
|
1667
|
+
};
|
|
1313
1668
|
const typedHandlers = this.typedLifecycleHandlers.get(event.type);
|
|
1314
1669
|
if (typedHandlers) {
|
|
1315
1670
|
for (const handler of typedHandlers) {
|
|
@@ -1341,125 +1696,55 @@ stderr: ${stderrOutput}`
|
|
|
1341
1696
|
});
|
|
1342
1697
|
return result;
|
|
1343
1698
|
}
|
|
1344
|
-
async
|
|
1345
|
-
if (!params || typeof params.sessionId !== "string" || typeof params.
|
|
1346
|
-
throw new Error("Invalid
|
|
1699
|
+
async handleExitPlanModeRequest(params) {
|
|
1700
|
+
if (!params || typeof params.sessionId !== "string" || typeof params.summary !== "string" || !Array.isArray(params.actions) || typeof params.recommendedAction !== "string") {
|
|
1701
|
+
throw new Error("Invalid exit plan mode request payload");
|
|
1347
1702
|
}
|
|
1348
1703
|
const session = this.sessions.get(params.sessionId);
|
|
1349
1704
|
if (!session) {
|
|
1350
1705
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1351
1706
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1707
|
+
return await session._handleExitPlanModeRequest({
|
|
1708
|
+
summary: params.summary,
|
|
1709
|
+
planContent: params.planContent,
|
|
1710
|
+
actions: params.actions,
|
|
1711
|
+
recommendedAction: params.recommendedAction
|
|
1712
|
+
});
|
|
1354
1713
|
}
|
|
1355
|
-
async
|
|
1356
|
-
if (!params || typeof params.sessionId !== "string"
|
|
1357
|
-
throw new Error("Invalid
|
|
1714
|
+
async handleAutoModeSwitchRequest(params) {
|
|
1715
|
+
if (!params || typeof params.sessionId !== "string") {
|
|
1716
|
+
throw new Error("Invalid auto mode switch request payload");
|
|
1358
1717
|
}
|
|
1359
1718
|
const session = this.sessions.get(params.sessionId);
|
|
1360
1719
|
if (!session) {
|
|
1361
1720
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1362
1721
|
}
|
|
1363
|
-
|
|
1722
|
+
const response = await session._handleAutoModeSwitchRequest({
|
|
1723
|
+
errorCode: params.errorCode,
|
|
1724
|
+
retryAfterSeconds: params.retryAfterSeconds
|
|
1725
|
+
});
|
|
1726
|
+
return { response };
|
|
1364
1727
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
/**
|
|
1369
|
-
* Handles a v2-style tool.call RPC request from the server.
|
|
1370
|
-
* Looks up the session and tool handler, executes it, and returns the result
|
|
1371
|
-
* in the v2 response format.
|
|
1372
|
-
*/
|
|
1373
|
-
async handleToolCallRequestV2(params) {
|
|
1374
|
-
if (!params || typeof params.sessionId !== "string" || typeof params.toolCallId !== "string" || typeof params.toolName !== "string") {
|
|
1375
|
-
throw new Error("Invalid tool call payload");
|
|
1728
|
+
async handleHooksInvoke(params) {
|
|
1729
|
+
if (!params || typeof params.sessionId !== "string" || typeof params.hookType !== "string") {
|
|
1730
|
+
throw new Error("Invalid hooks invoke payload");
|
|
1376
1731
|
}
|
|
1377
1732
|
const session = this.sessions.get(params.sessionId);
|
|
1378
1733
|
if (!session) {
|
|
1379
|
-
throw new Error(`
|
|
1380
|
-
}
|
|
1381
|
-
const handler = session.getToolHandler(params.toolName);
|
|
1382
|
-
if (!handler) {
|
|
1383
|
-
return {
|
|
1384
|
-
result: {
|
|
1385
|
-
textResultForLlm: `Tool '${params.toolName}' is not supported by this client instance.`,
|
|
1386
|
-
resultType: "failure",
|
|
1387
|
-
error: `tool '${params.toolName}' not supported`,
|
|
1388
|
-
toolTelemetry: {}
|
|
1389
|
-
}
|
|
1390
|
-
};
|
|
1391
|
-
}
|
|
1392
|
-
try {
|
|
1393
|
-
const traceparent = params.traceparent;
|
|
1394
|
-
const tracestate = params.tracestate;
|
|
1395
|
-
const invocation = {
|
|
1396
|
-
sessionId: params.sessionId,
|
|
1397
|
-
toolCallId: params.toolCallId,
|
|
1398
|
-
toolName: params.toolName,
|
|
1399
|
-
arguments: params.arguments,
|
|
1400
|
-
traceparent,
|
|
1401
|
-
tracestate
|
|
1402
|
-
};
|
|
1403
|
-
const result = await handler(params.arguments, invocation);
|
|
1404
|
-
return { result: this.normalizeToolResultV2(result) };
|
|
1405
|
-
} catch (error) {
|
|
1406
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1407
|
-
return {
|
|
1408
|
-
result: {
|
|
1409
|
-
textResultForLlm: "Invoking this tool produced an error. Detailed information is not available.",
|
|
1410
|
-
resultType: "failure",
|
|
1411
|
-
error: message,
|
|
1412
|
-
toolTelemetry: {}
|
|
1413
|
-
}
|
|
1414
|
-
};
|
|
1734
|
+
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1415
1735
|
}
|
|
1736
|
+
const output = await session._handleHooksInvoke(params.hookType, params.input);
|
|
1737
|
+
return { output };
|
|
1416
1738
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
async handlePermissionRequestV2(params) {
|
|
1421
|
-
if (!params || typeof params.sessionId !== "string" || !params.permissionRequest) {
|
|
1422
|
-
throw new Error("Invalid permission request payload");
|
|
1739
|
+
async handleSystemMessageTransform(params) {
|
|
1740
|
+
if (!params || typeof params.sessionId !== "string" || !params.sections || typeof params.sections !== "object") {
|
|
1741
|
+
throw new Error("Invalid systemMessage.transform payload");
|
|
1423
1742
|
}
|
|
1424
1743
|
const session = this.sessions.get(params.sessionId);
|
|
1425
1744
|
if (!session) {
|
|
1426
1745
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
1427
1746
|
}
|
|
1428
|
-
|
|
1429
|
-
const result = await session._handlePermissionRequestV2(params.permissionRequest);
|
|
1430
|
-
return { result };
|
|
1431
|
-
} catch (error) {
|
|
1432
|
-
if (error instanceof Error && error.message === import_session.NO_RESULT_PERMISSION_V2_ERROR) {
|
|
1433
|
-
throw error;
|
|
1434
|
-
}
|
|
1435
|
-
return {
|
|
1436
|
-
result: {
|
|
1437
|
-
kind: "user-not-available"
|
|
1438
|
-
}
|
|
1439
|
-
};
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
normalizeToolResultV2(result) {
|
|
1443
|
-
if (result === void 0 || result === null) {
|
|
1444
|
-
return {
|
|
1445
|
-
textResultForLlm: "Tool returned no result",
|
|
1446
|
-
resultType: "failure",
|
|
1447
|
-
error: "tool returned no result",
|
|
1448
|
-
toolTelemetry: {}
|
|
1449
|
-
};
|
|
1450
|
-
}
|
|
1451
|
-
if (this.isToolResultObject(result)) {
|
|
1452
|
-
return result;
|
|
1453
|
-
}
|
|
1454
|
-
const textResult = typeof result === "string" ? result : JSON.stringify(result);
|
|
1455
|
-
return {
|
|
1456
|
-
textResultForLlm: textResult,
|
|
1457
|
-
resultType: "success",
|
|
1458
|
-
toolTelemetry: {}
|
|
1459
|
-
};
|
|
1460
|
-
}
|
|
1461
|
-
isToolResultObject(value) {
|
|
1462
|
-
return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
|
|
1747
|
+
return await session._handleSystemMessageTransform(params.sections);
|
|
1463
1748
|
}
|
|
1464
1749
|
}
|
|
1465
1750
|
// Annotate the CommonJS export names for ESM import in node:
|