@github/copilot-sdk 1.0.6 → 1.0.7-preview.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/client.js +166 -38
- package/dist/cjs/copilotRequestHandler.js +9 -0
- package/dist/cjs/ffiRuntimeHost.js +282 -0
- package/dist/cjs/generated/rpc.js +63 -15
- package/dist/cjs/types.js +12 -0
- package/dist/client.d.ts +49 -0
- package/dist/client.js +156 -38
- package/dist/copilotRequestHandler.d.ts +3 -0
- package/dist/copilotRequestHandler.js +9 -0
- package/dist/ffiRuntimeHost.d.ts +40 -0
- package/dist/ffiRuntimeHost.js +248 -0
- package/dist/generated/rpc.d.ts +459 -9
- package/dist/generated/rpc.js +63 -15
- package/dist/generated/session-events.d.ts +174 -1
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +60 -1
- package/dist/types.js +12 -0
- package/package.json +3 -2
|
@@ -321,6 +321,15 @@ function createServerRpc(connection) {
|
|
|
321
321
|
getDiscoveryPaths: async (params) => connection.sendRequest("instructions.getDiscoveryPaths", params)
|
|
322
322
|
},
|
|
323
323
|
/** @experimental */
|
|
324
|
+
commands: {
|
|
325
|
+
/**
|
|
326
|
+
* Lists the well-known built-in slash commands that work as the first message in a new session (e.g. /plan, /env), without requiring an active session. Commands that depend on session state, authentication, or a synced session are omitted.
|
|
327
|
+
*
|
|
328
|
+
* @returns Slash commands available in the session, after applying any include/exclude filters.
|
|
329
|
+
*/
|
|
330
|
+
list: async () => connection.sendRequest("commands.list", {})
|
|
331
|
+
},
|
|
332
|
+
/** @experimental */
|
|
324
333
|
user: {
|
|
325
334
|
/** @experimental */
|
|
326
335
|
settings: {
|
|
@@ -656,6 +665,16 @@ function createSessionRpc(connection, sessionId) {
|
|
|
656
665
|
* @experimental
|
|
657
666
|
*/
|
|
658
667
|
send: async (params) => connection.sendRequest("session.send", { sessionId, ...params }),
|
|
668
|
+
/**
|
|
669
|
+
* Sends zero or more user messages to the session in a single turn and returns their message IDs. All provided messages are appended to the conversation in order, then exactly one agent turn runs over the resulting history. When the list is empty, one turn runs over the existing history with no new user message. Remote-backed (Mission Control) sessions do not support this method and will return an error.
|
|
670
|
+
*
|
|
671
|
+
* @param params Parameters for sending zero or more user messages to the session in a single turn. Remote-backed (Mission Control) sessions do not support this method and will return an error.
|
|
672
|
+
*
|
|
673
|
+
* @returns Result of sending zero or more user messages
|
|
674
|
+
*
|
|
675
|
+
* @experimental
|
|
676
|
+
*/
|
|
677
|
+
sendMessages: async (params) => connection.sendRequest("session.sendMessages", { sessionId, ...params }),
|
|
659
678
|
/**
|
|
660
679
|
* Aborts the current agent turn.
|
|
661
680
|
*
|
|
@@ -1153,6 +1172,18 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1153
1172
|
* @returns Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove).
|
|
1154
1173
|
*/
|
|
1155
1174
|
removeGitHub: async () => connection.sendRequest("session.mcp.removeGitHub", { sessionId }),
|
|
1175
|
+
/**
|
|
1176
|
+
* Starts an individual MCP server on the live session from a caller-supplied config. Session-scoped and ephemeral: the server is added to this session's running set only and is reaped when the session ends. Does NOT modify persistent user configuration (`mcp.config.*`), so it does not affect future sessions. The server surfaces through `session.mcp.list` and the `session.mcp_servers_loaded` / `session.mcp_server_status_changed` events like any other server.
|
|
1177
|
+
*
|
|
1178
|
+
* @param params Server name and configuration for an individual MCP server start.
|
|
1179
|
+
*/
|
|
1180
|
+
startServer: async (params) => connection.sendRequest("session.mcp.startServer", { sessionId, ...params }),
|
|
1181
|
+
/**
|
|
1182
|
+
* Restarts an individual MCP server on the live session (stops then starts). Omit `config` for a config-free restart-by-name of an already-configured server; supply `config` to restart with a replacement configuration. Session-scoped and ephemeral: does NOT modify persistent user configuration (`mcp.config.*`).
|
|
1183
|
+
*
|
|
1184
|
+
* @param params Server name and optional replacement configuration for an individual MCP server restart. Omit `config` for a config-free restart-by-name of an already-configured server.
|
|
1185
|
+
*/
|
|
1186
|
+
restartServer: async (params) => connection.sendRequest("session.mcp.restartServer", { sessionId, ...params }),
|
|
1156
1187
|
/**
|
|
1157
1188
|
* Stops an individual MCP server on the session's host.
|
|
1158
1189
|
*
|
|
@@ -1200,11 +1231,13 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1200
1231
|
/** @experimental */
|
|
1201
1232
|
apps: {
|
|
1202
1233
|
/**
|
|
1203
|
-
*
|
|
1234
|
+
* Deprecated/obsolete alias for `session.mcp.resources.read`; retained for backwards compatibility with earlier MCP Apps host integrations.
|
|
1204
1235
|
*
|
|
1205
|
-
* @param params MCP
|
|
1236
|
+
* @param params Deprecated/obsolete MCP Apps alias for `McpResourcesReadRequest`; use `session.mcp.resources.read` instead.
|
|
1206
1237
|
*
|
|
1207
|
-
* @returns
|
|
1238
|
+
* @returns Deprecated/obsolete MCP Apps alias for `McpResourcesReadResult`; use `session.mcp.resources.read` instead.
|
|
1239
|
+
*
|
|
1240
|
+
* @deprecated
|
|
1208
1241
|
*/
|
|
1209
1242
|
readResource: async (params) => connection.sendRequest("session.mcp.apps.readResource", { sessionId, ...params }),
|
|
1210
1243
|
/**
|
|
@@ -1243,6 +1276,33 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1243
1276
|
* @returns Diagnostic snapshot of MCP Apps wiring for the named server.
|
|
1244
1277
|
*/
|
|
1245
1278
|
diagnose: async (params) => connection.sendRequest("session.mcp.apps.diagnose", { sessionId, ...params })
|
|
1279
|
+
},
|
|
1280
|
+
/** @experimental */
|
|
1281
|
+
resources: {
|
|
1282
|
+
/**
|
|
1283
|
+
* Fetch an MCP resource from a connected server by URI (proxies MCP `resources/read`).
|
|
1284
|
+
*
|
|
1285
|
+
* @param params MCP server and resource URI to fetch.
|
|
1286
|
+
*
|
|
1287
|
+
* @returns Resource contents returned by the MCP server.
|
|
1288
|
+
*/
|
|
1289
|
+
read: async (params) => connection.sendRequest("session.mcp.resources.read", { sessionId, ...params }),
|
|
1290
|
+
/**
|
|
1291
|
+
* Enumerate one page of resources a connected MCP server exposes (proxies MCP `resources/list`). Pass `cursor` to continue from a prior result's `nextCursor`.
|
|
1292
|
+
*
|
|
1293
|
+
* @param params MCP server whose resources to enumerate.
|
|
1294
|
+
*
|
|
1295
|
+
* @returns One page of resources advertised by the named MCP server.
|
|
1296
|
+
*/
|
|
1297
|
+
list: async (params) => connection.sendRequest("session.mcp.resources.list", { sessionId, ...params }),
|
|
1298
|
+
/**
|
|
1299
|
+
* Enumerate one page of resource templates a connected MCP server exposes (proxies MCP `resources/templates/list`). Pass `cursor` to continue from a prior result's `nextCursor`.
|
|
1300
|
+
*
|
|
1301
|
+
* @param params MCP server whose resource templates to enumerate.
|
|
1302
|
+
*
|
|
1303
|
+
* @returns One page of resource templates advertised by the named MCP server.
|
|
1304
|
+
*/
|
|
1305
|
+
listTemplates: async (params) => connection.sendRequest("session.mcp.resources.listTemplates", { sessionId, ...params })
|
|
1246
1306
|
}
|
|
1247
1307
|
},
|
|
1248
1308
|
/** @experimental */
|
|
@@ -1972,18 +2032,6 @@ function createInternalSessionRpc(connection, sessionId) {
|
|
|
1972
2032
|
* @returns Result of configuring GitHub MCP.
|
|
1973
2033
|
*/
|
|
1974
2034
|
configureGitHub: async (params) => connection.sendRequest("session.mcp.configureGitHub", { sessionId, ...params }),
|
|
1975
|
-
/**
|
|
1976
|
-
* Starts an individual MCP server on the session's host.
|
|
1977
|
-
*
|
|
1978
|
-
* @param params Server name and opaque configuration for an individual MCP server start.
|
|
1979
|
-
*/
|
|
1980
|
-
startServer: async (params) => connection.sendRequest("session.mcp.startServer", { sessionId, ...params }),
|
|
1981
|
-
/**
|
|
1982
|
-
* Restarts an individual MCP server on the session's host (stops then starts).
|
|
1983
|
-
*
|
|
1984
|
-
* @param params Server name and opaque configuration for an individual MCP server restart.
|
|
1985
|
-
*/
|
|
1986
|
-
restartServer: async (params) => connection.sendRequest("session.mcp.restartServer", { sessionId, ...params }),
|
|
1987
2035
|
/**
|
|
1988
2036
|
* Registers a pre-connected external MCP client (e.g. IDE) on the session's host. The caller retains lifecycle ownership of the client and transport. Marked internal because the `client` and `transport` arguments are in-process MCP SDK instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on top of the SDK, external clients will be expressed as transport configs the runtime can construct itself.
|
|
1989
2037
|
*
|
package/dist/cjs/types.js
CHANGED
|
@@ -59,6 +59,18 @@ const RuntimeConnection = {
|
|
|
59
59
|
*/
|
|
60
60
|
forUri(url, opts = {}) {
|
|
61
61
|
return { kind: "uri", url, connectionToken: opts.connectionToken };
|
|
62
|
+
},
|
|
63
|
+
/**
|
|
64
|
+
* Host the runtime in-process over the native runtime library's C ABI (FFI).
|
|
65
|
+
*
|
|
66
|
+
* @experimental Per-client options lowered to environment variables (`env`,
|
|
67
|
+
* `telemetry`, `gitHubToken`, `baseDirectory`) are **not** honored in-process;
|
|
68
|
+
* the worker inherits the host process's ambient environment. Set the
|
|
69
|
+
* corresponding environment variables on the host process instead. See
|
|
70
|
+
* https://github.com/github/copilot-sdk/issues/1934.
|
|
71
|
+
*/
|
|
72
|
+
forInProcess() {
|
|
73
|
+
return { kind: "inprocess" };
|
|
62
74
|
}
|
|
63
75
|
};
|
|
64
76
|
function convertMcpCallToolResult(callResult) {
|
package/dist/client.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { CopilotClientOptions, GetAuthStatusResponse, GetStatusResponse, Mo
|
|
|
4
4
|
export declare class CopilotClient {
|
|
5
5
|
private cliStartTimeout;
|
|
6
6
|
private cliProcess;
|
|
7
|
+
private ffiHost;
|
|
7
8
|
private connection;
|
|
8
9
|
private messageWriter;
|
|
9
10
|
private socket;
|
|
@@ -45,6 +46,18 @@ export declare class CopilotClient {
|
|
|
45
46
|
get rpc(): ReturnType<typeof createServerRpc>;
|
|
46
47
|
private logDebugTiming;
|
|
47
48
|
private logDebug;
|
|
49
|
+
/**
|
|
50
|
+
* Environment variable that overrides the transport when the caller does not set
|
|
51
|
+
* {@link CopilotClientOptions.connection}. Accepts `"inprocess"` or `"stdio"`
|
|
52
|
+
* (case-insensitive); unset preserves the default stdio transport. Any other value
|
|
53
|
+
* is an error.
|
|
54
|
+
*/
|
|
55
|
+
private static readonly DEFAULT_CONNECTION_ENV_VAR;
|
|
56
|
+
/**
|
|
57
|
+
* Resolves the default {@link RuntimeConnection} for the no-connection case,
|
|
58
|
+
* honoring {@link CopilotClient.DEFAULT_CONNECTION_ENV_VAR}.
|
|
59
|
+
*/
|
|
60
|
+
private static resolveDefaultConnection;
|
|
48
61
|
/**
|
|
49
62
|
* Creates a new CopilotClient instance.
|
|
50
63
|
*
|
|
@@ -406,6 +419,14 @@ export declare class CopilotClient {
|
|
|
406
419
|
* ```
|
|
407
420
|
*/
|
|
408
421
|
onLifecycle(handler: SessionLifecycleHandler): () => void;
|
|
422
|
+
/**
|
|
423
|
+
* Builds the environment for the spawned runtime child process (stdio/TCP): applies
|
|
424
|
+
* the auth token, connection token, `COPILOT_HOME`, keychain setting, and telemetry
|
|
425
|
+
* variables on top of the effective env. Not used by the in-process (FFI) transport,
|
|
426
|
+
* whose worker inherits the host process's ambient environment
|
|
427
|
+
* (see {@link CopilotClient.startInProcessFfi}).
|
|
428
|
+
*/
|
|
429
|
+
private buildRuntimeEnv;
|
|
409
430
|
/**
|
|
410
431
|
* Start the CLI server process
|
|
411
432
|
*/
|
|
@@ -414,6 +435,34 @@ export declare class CopilotClient {
|
|
|
414
435
|
* Connect to the CLI server (via socket or stdio)
|
|
415
436
|
*/
|
|
416
437
|
private connectToServer;
|
|
438
|
+
/**
|
|
439
|
+
* Start the in-process FFI runtime host: resolve the CLI entrypoint and native
|
|
440
|
+
* runtime library, then let the native host spawn the CLI worker.
|
|
441
|
+
*
|
|
442
|
+
* The worker inherits this host process's ambient environment; per-client options
|
|
443
|
+
* that lower to environment variables (`env`, `telemetry`, `gitHubToken`,
|
|
444
|
+
* `baseDirectory`) are intentionally not applied here, because the native runtime
|
|
445
|
+
* loads into the shared host process and a single env block cannot carry per-client
|
|
446
|
+
* values. Configure the in-process runtime via the host process environment instead.
|
|
447
|
+
* See https://github.com/github/copilot-sdk/issues/1934.
|
|
448
|
+
*/
|
|
449
|
+
private startInProcessFfi;
|
|
450
|
+
/**
|
|
451
|
+
* Connect to the in-process FFI runtime host over its receive/send streams,
|
|
452
|
+
* reusing the same `vscode-jsonrpc` framing as the stdio transport.
|
|
453
|
+
*/
|
|
454
|
+
private connectViaFfi;
|
|
455
|
+
/**
|
|
456
|
+
* Resolves the CLI entrypoint used for in-process FFI hosting: `COPILOT_CLI_PATH`
|
|
457
|
+
* when set, otherwise the bundled platform-package entrypoint.
|
|
458
|
+
*/
|
|
459
|
+
private resolveCliPathForFfi;
|
|
460
|
+
/**
|
|
461
|
+
* Returns the napi prebuilds folder name for the current host — the
|
|
462
|
+
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
463
|
+
* `linux-x64`) under which the runtime ships `prebuilds/<folder>/runtime.node`.
|
|
464
|
+
*/
|
|
465
|
+
private static getNapiPrebuildsFolder;
|
|
417
466
|
/**
|
|
418
467
|
* Connect to child via stdio pipes
|
|
419
468
|
*/
|
package/dist/client.js
CHANGED
|
@@ -233,6 +233,7 @@ class TeardownResilientStreamMessageWriter extends StreamMessageWriter {
|
|
|
233
233
|
class CopilotClient {
|
|
234
234
|
cliStartTimeout = null;
|
|
235
235
|
cliProcess = null;
|
|
236
|
+
ffiHost = null;
|
|
236
237
|
connection = null;
|
|
237
238
|
messageWriter = null;
|
|
238
239
|
socket = null;
|
|
@@ -309,6 +310,29 @@ class CopilotClient {
|
|
|
309
310
|
`);
|
|
310
311
|
}
|
|
311
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Environment variable that overrides the transport when the caller does not set
|
|
315
|
+
* {@link CopilotClientOptions.connection}. Accepts `"inprocess"` or `"stdio"`
|
|
316
|
+
* (case-insensitive); unset preserves the default stdio transport. Any other value
|
|
317
|
+
* is an error.
|
|
318
|
+
*/
|
|
319
|
+
static DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION";
|
|
320
|
+
/**
|
|
321
|
+
* Resolves the default {@link RuntimeConnection} for the no-connection case,
|
|
322
|
+
* honoring {@link CopilotClient.DEFAULT_CONNECTION_ENV_VAR}.
|
|
323
|
+
*/
|
|
324
|
+
static resolveDefaultConnection() {
|
|
325
|
+
const value = process.env[CopilotClient.DEFAULT_CONNECTION_ENV_VAR];
|
|
326
|
+
if (!value || value.toLowerCase() === "stdio") {
|
|
327
|
+
return { kind: "stdio" };
|
|
328
|
+
}
|
|
329
|
+
if (value.toLowerCase() === "inprocess") {
|
|
330
|
+
return { kind: "inprocess" };
|
|
331
|
+
}
|
|
332
|
+
throw new Error(
|
|
333
|
+
`Invalid ${CopilotClient.DEFAULT_CONNECTION_ENV_VAR} value '${value}'. Expected 'inprocess', 'stdio', or unset.`
|
|
334
|
+
);
|
|
335
|
+
}
|
|
312
336
|
/**
|
|
313
337
|
* Creates a new CopilotClient instance.
|
|
314
338
|
*
|
|
@@ -337,12 +361,17 @@ class CopilotClient {
|
|
|
337
361
|
* ```
|
|
338
362
|
*/
|
|
339
363
|
constructor(options = {}) {
|
|
340
|
-
const conn = options._internalConnection ?? options.connection ??
|
|
364
|
+
const conn = options._internalConnection ?? options.connection ?? CopilotClient.resolveDefaultConnection();
|
|
341
365
|
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
342
366
|
throw new Error(
|
|
343
367
|
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
344
368
|
);
|
|
345
369
|
}
|
|
370
|
+
if (conn.kind === "inprocess" && options.workingDirectory !== void 0) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
"workingDirectory is not supported with RuntimeConnection.forInProcess(): the in-process transport hosts the runtime in this process, so honoring it would require mutating the shared process-global cwd. Change the host process's working directory before constructing the client instead."
|
|
373
|
+
);
|
|
374
|
+
}
|
|
346
375
|
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
347
376
|
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
348
377
|
throw new Error("connectionToken must be a non-empty string");
|
|
@@ -498,7 +527,9 @@ class CopilotClient {
|
|
|
498
527
|
}
|
|
499
528
|
this.state = "connecting";
|
|
500
529
|
try {
|
|
501
|
-
if (
|
|
530
|
+
if (this.connectionConfig.kind === "inprocess") {
|
|
531
|
+
await this.startInProcessFfi();
|
|
532
|
+
} else if (!this.isExternalServer) {
|
|
502
533
|
await this.startCLIServer();
|
|
503
534
|
}
|
|
504
535
|
await this.connectToServer();
|
|
@@ -547,6 +578,9 @@ class CopilotClient {
|
|
|
547
578
|
async stop() {
|
|
548
579
|
const errors = [];
|
|
549
580
|
const activeSessions = [...this.sessions.values()];
|
|
581
|
+
if (this.connectionConfig.kind === "inprocess") {
|
|
582
|
+
await Promise.allSettled(activeSessions.map((session) => session.abort()));
|
|
583
|
+
}
|
|
550
584
|
for (const session of activeSessions) {
|
|
551
585
|
const sessionId = session.sessionId;
|
|
552
586
|
let lastError = null;
|
|
@@ -575,7 +609,7 @@ class CopilotClient {
|
|
|
575
609
|
session._markDisconnected();
|
|
576
610
|
}
|
|
577
611
|
this.sessions.clear();
|
|
578
|
-
if (this.connection && this.cliProcess && !this.isExternalServer) {
|
|
612
|
+
if (this.connection && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
|
|
579
613
|
const runtimeShutdownStart = Date.now();
|
|
580
614
|
const shutdownPromise = this.rpc.runtime.shutdown();
|
|
581
615
|
void shutdownPromise.catch(() => void 0);
|
|
@@ -660,6 +694,19 @@ class CopilotClient {
|
|
|
660
694
|
);
|
|
661
695
|
}
|
|
662
696
|
}
|
|
697
|
+
if (this.ffiHost) {
|
|
698
|
+
const host = this.ffiHost;
|
|
699
|
+
this.ffiHost = null;
|
|
700
|
+
try {
|
|
701
|
+
host.dispose();
|
|
702
|
+
} catch (error) {
|
|
703
|
+
errors.push(
|
|
704
|
+
new Error(
|
|
705
|
+
`Failed to dispose in-process runtime host: ${error instanceof Error ? error.message : String(error)}`
|
|
706
|
+
)
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
663
710
|
if (this.cliStartTimeout) {
|
|
664
711
|
clearTimeout(this.cliStartTimeout);
|
|
665
712
|
this.cliStartTimeout = null;
|
|
@@ -744,6 +791,13 @@ class CopilotClient {
|
|
|
744
791
|
}
|
|
745
792
|
this.cliProcess = null;
|
|
746
793
|
}
|
|
794
|
+
if (this.ffiHost) {
|
|
795
|
+
try {
|
|
796
|
+
this.ffiHost.dispose();
|
|
797
|
+
} catch {
|
|
798
|
+
}
|
|
799
|
+
this.ffiHost = null;
|
|
800
|
+
}
|
|
747
801
|
if (this.cliStartTimeout) {
|
|
748
802
|
clearTimeout(this.cliStartTimeout);
|
|
749
803
|
this.cliStartTimeout = null;
|
|
@@ -988,6 +1042,7 @@ class CopilotClient {
|
|
|
988
1042
|
requestExtensions: config.requestExtensions,
|
|
989
1043
|
extensionSdkPath: config.extensionSdkPath,
|
|
990
1044
|
extensionInfo: config.extensionInfo,
|
|
1045
|
+
canvasProvider: config.canvasProvider,
|
|
991
1046
|
commands: config.commands?.map((cmd) => ({
|
|
992
1047
|
name: cmd.name,
|
|
993
1048
|
description: cmd.description
|
|
@@ -1042,7 +1097,8 @@ class CopilotClient {
|
|
|
1042
1097
|
gitHubToken: config.gitHubToken,
|
|
1043
1098
|
remoteSession: config.remoteSession,
|
|
1044
1099
|
cloud: config.cloud,
|
|
1045
|
-
expAssignments: config.expAssignments
|
|
1100
|
+
expAssignments: config.expAssignments,
|
|
1101
|
+
enableManagedSettings: config.enableManagedSettings
|
|
1046
1102
|
});
|
|
1047
1103
|
const {
|
|
1048
1104
|
sessionId: returnedSessionId,
|
|
@@ -1184,6 +1240,7 @@ class CopilotClient {
|
|
|
1184
1240
|
requestExtensions: config.requestExtensions,
|
|
1185
1241
|
extensionSdkPath: config.extensionSdkPath,
|
|
1186
1242
|
extensionInfo: config.extensionInfo,
|
|
1243
|
+
canvasProvider: config.canvasProvider,
|
|
1187
1244
|
commands: config.commands?.map((cmd) => ({
|
|
1188
1245
|
name: cmd.name,
|
|
1189
1246
|
description: cmd.description
|
|
@@ -1232,7 +1289,8 @@ class CopilotClient {
|
|
|
1232
1289
|
gitHubToken: config.gitHubToken,
|
|
1233
1290
|
remoteSession: config.remoteSession,
|
|
1234
1291
|
openCanvases: config.openCanvases,
|
|
1235
|
-
expAssignments: config.expAssignments
|
|
1292
|
+
expAssignments: config.expAssignments,
|
|
1293
|
+
enableManagedSettings: config.enableManagedSettings
|
|
1236
1294
|
});
|
|
1237
1295
|
const { workspacePath, capabilities, openCanvases } = response;
|
|
1238
1296
|
session["_workspacePath"] = workspacePath;
|
|
@@ -1581,6 +1639,41 @@ class CopilotClient {
|
|
|
1581
1639
|
this.sessionLifecycleHandlers.delete(wildcardHandler);
|
|
1582
1640
|
};
|
|
1583
1641
|
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Builds the environment for the spawned runtime child process (stdio/TCP): applies
|
|
1644
|
+
* the auth token, connection token, `COPILOT_HOME`, keychain setting, and telemetry
|
|
1645
|
+
* variables on top of the effective env. Not used by the in-process (FFI) transport,
|
|
1646
|
+
* whose worker inherits the host process's ambient environment
|
|
1647
|
+
* (see {@link CopilotClient.startInProcessFfi}).
|
|
1648
|
+
*/
|
|
1649
|
+
buildRuntimeEnv() {
|
|
1650
|
+
const env = { ...this.resolvedEnv };
|
|
1651
|
+
delete env.NODE_DEBUG;
|
|
1652
|
+
if (this.options.gitHubToken) {
|
|
1653
|
+
env.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1654
|
+
}
|
|
1655
|
+
if (this.effectiveConnectionToken) {
|
|
1656
|
+
env.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1657
|
+
}
|
|
1658
|
+
if (this.options.baseDirectory) {
|
|
1659
|
+
env.COPILOT_HOME = this.options.baseDirectory;
|
|
1660
|
+
}
|
|
1661
|
+
if (this.options.mode === "empty") {
|
|
1662
|
+
env.COPILOT_DISABLE_KEYTAR = "1";
|
|
1663
|
+
}
|
|
1664
|
+
if (this.options.telemetry) {
|
|
1665
|
+
const t = this.options.telemetry;
|
|
1666
|
+
env.COPILOT_OTEL_ENABLED = "true";
|
|
1667
|
+
if (t.otlpEndpoint !== void 0) env.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
|
|
1668
|
+
if (t.otlpProtocol !== void 0) env.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
|
|
1669
|
+
if (t.filePath !== void 0) env.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
|
|
1670
|
+
if (t.exporterType !== void 0) env.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
|
|
1671
|
+
if (t.sourceName !== void 0) env.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
|
|
1672
|
+
if (t.captureContent !== void 0)
|
|
1673
|
+
env.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(t.captureContent);
|
|
1674
|
+
}
|
|
1675
|
+
return env;
|
|
1676
|
+
}
|
|
1584
1677
|
/**
|
|
1585
1678
|
* Start the CLI server process
|
|
1586
1679
|
*/
|
|
@@ -1614,43 +1707,12 @@ class CopilotClient {
|
|
|
1614
1707
|
if (this.options.enableRemoteSessions) {
|
|
1615
1708
|
args.push("--remote");
|
|
1616
1709
|
}
|
|
1617
|
-
const envWithoutNodeDebug =
|
|
1618
|
-
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
1619
|
-
if (this.options.gitHubToken) {
|
|
1620
|
-
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1621
|
-
}
|
|
1622
|
-
if (this.effectiveConnectionToken) {
|
|
1623
|
-
envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1624
|
-
}
|
|
1625
|
-
if (this.options.baseDirectory) {
|
|
1626
|
-
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1627
|
-
}
|
|
1628
|
-
if (this.options.mode === "empty") {
|
|
1629
|
-
envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
|
|
1630
|
-
}
|
|
1710
|
+
const envWithoutNodeDebug = this.buildRuntimeEnv();
|
|
1631
1711
|
if (!this.resolvedCliPath) {
|
|
1632
1712
|
throw new Error(
|
|
1633
1713
|
"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."
|
|
1634
1714
|
);
|
|
1635
1715
|
}
|
|
1636
|
-
if (this.options.telemetry) {
|
|
1637
|
-
const t = this.options.telemetry;
|
|
1638
|
-
envWithoutNodeDebug.COPILOT_OTEL_ENABLED = "true";
|
|
1639
|
-
if (t.otlpEndpoint !== void 0)
|
|
1640
|
-
envWithoutNodeDebug.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
|
|
1641
|
-
if (t.otlpProtocol !== void 0)
|
|
1642
|
-
envWithoutNodeDebug.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
|
|
1643
|
-
if (t.filePath !== void 0)
|
|
1644
|
-
envWithoutNodeDebug.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
|
|
1645
|
-
if (t.exporterType !== void 0)
|
|
1646
|
-
envWithoutNodeDebug.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
|
|
1647
|
-
if (t.sourceName !== void 0)
|
|
1648
|
-
envWithoutNodeDebug.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
|
|
1649
|
-
if (t.captureContent !== void 0)
|
|
1650
|
-
envWithoutNodeDebug.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(
|
|
1651
|
-
t.captureContent
|
|
1652
|
-
);
|
|
1653
|
-
}
|
|
1654
1716
|
if (!existsSync(this.resolvedCliPath)) {
|
|
1655
1717
|
throw new Error(
|
|
1656
1718
|
`Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
|
|
@@ -1769,11 +1831,66 @@ stderr: ${stderrOutput}`
|
|
|
1769
1831
|
return this.connectToParentProcessViaStdio();
|
|
1770
1832
|
case "stdio":
|
|
1771
1833
|
return this.connectToChildProcessViaStdio();
|
|
1834
|
+
case "inprocess":
|
|
1835
|
+
return this.connectViaFfi();
|
|
1772
1836
|
case "tcp":
|
|
1773
1837
|
case "uri":
|
|
1774
1838
|
return this.connectViaTcp();
|
|
1775
1839
|
}
|
|
1776
1840
|
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Start the in-process FFI runtime host: resolve the CLI entrypoint and native
|
|
1843
|
+
* runtime library, then let the native host spawn the CLI worker.
|
|
1844
|
+
*
|
|
1845
|
+
* The worker inherits this host process's ambient environment; per-client options
|
|
1846
|
+
* that lower to environment variables (`env`, `telemetry`, `gitHubToken`,
|
|
1847
|
+
* `baseDirectory`) are intentionally not applied here, because the native runtime
|
|
1848
|
+
* loads into the shared host process and a single env block cannot carry per-client
|
|
1849
|
+
* values. Configure the in-process runtime via the host process environment instead.
|
|
1850
|
+
* See https://github.com/github/copilot-sdk/issues/1934.
|
|
1851
|
+
*/
|
|
1852
|
+
async startInProcessFfi() {
|
|
1853
|
+
const entrypoint = this.resolveCliPathForFfi();
|
|
1854
|
+
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
1855
|
+
const host = FfiRuntimeHost.create(entrypoint, CopilotClient.getNapiPrebuildsFolder());
|
|
1856
|
+
this.ffiHost = host;
|
|
1857
|
+
await host.start();
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Connect to the in-process FFI runtime host over its receive/send streams,
|
|
1861
|
+
* reusing the same `vscode-jsonrpc` framing as the stdio transport.
|
|
1862
|
+
*/
|
|
1863
|
+
async connectViaFfi() {
|
|
1864
|
+
if (!this.ffiHost) {
|
|
1865
|
+
throw new Error("In-process FFI runtime host not started");
|
|
1866
|
+
}
|
|
1867
|
+
this.messageWriter = new TeardownResilientStreamMessageWriter(this.ffiHost.sendStream);
|
|
1868
|
+
this.connection = createMessageConnection(
|
|
1869
|
+
new StreamMessageReader(this.ffiHost.receiveStream),
|
|
1870
|
+
this.messageWriter
|
|
1871
|
+
);
|
|
1872
|
+
this.attachConnectionHandlers();
|
|
1873
|
+
this.connection.listen();
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Resolves the CLI entrypoint used for in-process FFI hosting: `COPILOT_CLI_PATH`
|
|
1877
|
+
* when set, otherwise the bundled platform-package entrypoint.
|
|
1878
|
+
*/
|
|
1879
|
+
resolveCliPathForFfi() {
|
|
1880
|
+
return this.resolvedEnv.COPILOT_CLI_PATH ?? getBundledCliPath();
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Returns the napi prebuilds folder name for the current host — the
|
|
1884
|
+
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
1885
|
+
* `linux-x64`) under which the runtime ships `prebuilds/<folder>/runtime.node`.
|
|
1886
|
+
*/
|
|
1887
|
+
static getNapiPrebuildsFolder() {
|
|
1888
|
+
const arch = process.arch;
|
|
1889
|
+
if (arch !== "x64" && arch !== "arm64") {
|
|
1890
|
+
throw new Error(`Unsupported architecture '${arch}' for in-process FFI hosting.`);
|
|
1891
|
+
}
|
|
1892
|
+
return `${process.platform}-${arch}`;
|
|
1893
|
+
}
|
|
1777
1894
|
/**
|
|
1778
1895
|
* Connect to child via stdio pipes
|
|
1779
1896
|
*/
|
|
@@ -1895,8 +2012,9 @@ stderr: ${stderrOutput}`
|
|
|
1895
2012
|
return;
|
|
1896
2013
|
}
|
|
1897
2014
|
const session = this.sessions.get(notification.sessionId);
|
|
2015
|
+
const event = notification.event;
|
|
1898
2016
|
if (session) {
|
|
1899
|
-
session._dispatchEvent(
|
|
2017
|
+
session._dispatchEvent(event);
|
|
1900
2018
|
}
|
|
1901
2019
|
}
|
|
1902
2020
|
handleSessionLifecycleNotification(notification) {
|
|
@@ -8,6 +8,9 @@ declare const kSuppressCloseOnDispose: unique symbol;
|
|
|
8
8
|
export interface CopilotRequestContext {
|
|
9
9
|
readonly requestId: string;
|
|
10
10
|
readonly sessionId?: string;
|
|
11
|
+
readonly agentId?: string;
|
|
12
|
+
readonly parentAgentId?: string;
|
|
13
|
+
readonly interactionType?: string;
|
|
11
14
|
readonly transport: "http" | "websocket";
|
|
12
15
|
url: string;
|
|
13
16
|
headers: LlmInferenceHeaders;
|
|
@@ -153,6 +153,9 @@ class CopilotRequestHandler {
|
|
|
153
153
|
const ctx = {
|
|
154
154
|
requestId: exchange.requestId,
|
|
155
155
|
sessionId: exchange.sessionId,
|
|
156
|
+
agentId: exchange.agentId,
|
|
157
|
+
parentAgentId: exchange.parentAgentId,
|
|
158
|
+
interactionType: exchange.interactionType,
|
|
156
159
|
transport: exchange.transport,
|
|
157
160
|
url: exchange.url,
|
|
158
161
|
headers: exchange.headers,
|
|
@@ -278,6 +281,9 @@ function routeChunk(exchange, params) {
|
|
|
278
281
|
class CopilotRequestExchange {
|
|
279
282
|
requestId;
|
|
280
283
|
sessionId;
|
|
284
|
+
agentId;
|
|
285
|
+
parentAgentId;
|
|
286
|
+
interactionType;
|
|
281
287
|
method = "GET";
|
|
282
288
|
url = "";
|
|
283
289
|
headers = {};
|
|
@@ -297,6 +303,9 @@ class CopilotRequestExchange {
|
|
|
297
303
|
/** Fill in the request context once the matching start frame arrives. */
|
|
298
304
|
setContext(params) {
|
|
299
305
|
this.sessionId = params.sessionId;
|
|
306
|
+
this.agentId = params.agentId;
|
|
307
|
+
this.parentAgentId = params.parentAgentId;
|
|
308
|
+
this.interactionType = params.interactionType;
|
|
300
309
|
this.method = params.method;
|
|
301
310
|
this.url = params.url;
|
|
302
311
|
this.headers = params.headers;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PassThrough, Writable } from "node:stream";
|
|
2
|
+
export declare class FfiRuntimeHost {
|
|
3
|
+
private readonly libraryPath;
|
|
4
|
+
private readonly cliEntrypoint;
|
|
5
|
+
private readonly environment?;
|
|
6
|
+
private readonly lib;
|
|
7
|
+
private serverId;
|
|
8
|
+
private connectionId;
|
|
9
|
+
private disposed;
|
|
10
|
+
private outboundCallback;
|
|
11
|
+
private keepAliveTimer;
|
|
12
|
+
/** The stream JSON-RPC reads server→client frames from. */
|
|
13
|
+
readonly receiveStream: PassThrough;
|
|
14
|
+
/** The stream JSON-RPC writes client→server frames to. */
|
|
15
|
+
readonly sendStream: Writable;
|
|
16
|
+
private constructor();
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the cdylib next to the given CLI entrypoint and prepares the FFI host.
|
|
19
|
+
* The cdylib is resolved as `prebuilds/<prebuildsFolder>/runtime.node` relative to
|
|
20
|
+
* the entrypoint directory (the napi-rs `<node-platform>-<arch>` layout, e.g.
|
|
21
|
+
* `linux-x64`). Throws if it cannot be found.
|
|
22
|
+
*/
|
|
23
|
+
static create(cliEntrypoint: string, prebuildsFolder: string, environment?: Record<string, string | undefined>): FfiRuntimeHost;
|
|
24
|
+
/**
|
|
25
|
+
* Starts the in-process runtime: spawns the CLI worker via the native host,
|
|
26
|
+
* waits for readiness, and opens the FFI JSON-RPC connection.
|
|
27
|
+
*/
|
|
28
|
+
start(): Promise<void>;
|
|
29
|
+
private writeFrame;
|
|
30
|
+
/**
|
|
31
|
+
* Native outbound (server→client) callback. koffi delivers it on the JS event loop
|
|
32
|
+
* via a threadsafe function, so the frame is decoded and written straight to
|
|
33
|
+
* {@link receiveStream}. The native pointer is only valid for this call, so the
|
|
34
|
+
* bytes are copied out before returning.
|
|
35
|
+
*/
|
|
36
|
+
private feedInbound;
|
|
37
|
+
private unregisterCallback;
|
|
38
|
+
/** Closes the FFI connection, shuts down the native host, and releases resources. */
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|