@github/copilot-sdk 1.0.7-preview.2 → 1.0.7
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 +4 -2
- package/dist/cjs/client.js +78 -22
- package/dist/cjs/ffiRuntimeHost.js +8 -5
- package/dist/cjs/generated/rpc.js +13 -10
- package/dist/cjs/session.js +12 -1
- package/dist/cjs/types.js +3 -2
- package/dist/client.d.ts +4 -12
- package/dist/client.js +78 -22
- package/dist/ffiRuntimeHost.d.ts +3 -2
- package/dist/ffiRuntimeHost.js +8 -5
- package/dist/generated/rpc.d.ts +132 -26
- package/dist/generated/rpc.js +13 -10
- package/dist/generated/session-events.d.ts +183 -7
- package/dist/index.d.ts +1 -1
- package/dist/session.d.ts +6 -1
- package/dist/session.js +12 -1
- package/dist/types.d.ts +78 -10
- package/dist/types.js +3 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -84,9 +84,11 @@ new CopilotClient(options?: CopilotClientOptions)
|
|
|
84
84
|
**Options:**
|
|
85
85
|
|
|
86
86
|
- `connection?: RuntimeConnection` - How to connect to the Copilot runtime. Construct via the factory functions on `RuntimeConnection`:
|
|
87
|
-
- `RuntimeConnection.forStdio({ path?, args? })` (default) — spawn the runtime and communicate over its stdin/stdout.
|
|
88
|
-
- `RuntimeConnection.forTcp({ port?, connectionToken?, path?, args? })` — spawn the runtime as a TCP server.
|
|
87
|
+
- `RuntimeConnection.forStdio({ path?, args?, env? })` (default) — spawn the runtime and communicate over its stdin/stdout.
|
|
88
|
+
- `RuntimeConnection.forTcp({ port?, connectionToken?, path?, args?, env? })` — spawn the runtime as a TCP server.
|
|
89
89
|
- `RuntimeConnection.forUri(url, { connectionToken? })` — connect to an already-running runtime (mutually exclusive with `gitHubToken`/`useLoggedInUser`). There is no top-level `cliUrl` shortcut; use this factory for URL-based connections.
|
|
90
|
+
- `RuntimeConnection.forInProcess()` — host the runtime in-process over its native C ABI (FFI). **Experimental.** Because the runtime shares this process, `env`, `telemetry`, and `workingDirectory` are rejected with this transport; set them on the host process instead.
|
|
91
|
+
- The child-process transports (`forStdio`/`forTcp`) also accept a per-connection `env`. Set it there or via the top-level `env` option — not both (setting both throws).
|
|
90
92
|
- `mode?: "empty" | "copilot-cli"` - Defaulting strategy. Use `"empty"` for multi-user server mode; defaults to `"copilot-cli"`.
|
|
91
93
|
- `workingDirectory?: string` - Working directory for the runtime process (default: current process cwd).
|
|
92
94
|
- `baseDirectory?: string` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime. When not set, the runtime defaults to `~/.copilot`. Ignored when connecting via `RuntimeConnection.forUri`.
|
package/dist/cjs/client.js
CHANGED
|
@@ -395,6 +395,21 @@ class CopilotClient {
|
|
|
395
395
|
"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."
|
|
396
396
|
);
|
|
397
397
|
}
|
|
398
|
+
if (conn.kind === "inprocess" && options.env !== void 0) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
"env is not supported with RuntimeConnection.forInProcess(): the in-process transport loads the native runtime into the shared host process, whose single environment block cannot carry per-client values. Set the variables on the host process environment instead."
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
if (conn.kind === "inprocess" && options.telemetry !== void 0) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
"telemetry is not supported with RuntimeConnection.forInProcess(): telemetry configuration is lowered to environment variables read by native runtime code running in the shared host process, so per-client telemetry cannot be honored in-process. Configure telemetry via the host process environment, or use a child-process transport."
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if ((conn.kind === "stdio" || conn.kind === "tcp") && conn.env !== void 0 && options.env !== void 0) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
"Set environment variables via either the client-level env option or the connection's env (RuntimeConnection.forStdio/forTcp), not both. Prefer the connection-level env for child-process transports."
|
|
411
|
+
);
|
|
412
|
+
}
|
|
398
413
|
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
399
414
|
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
400
415
|
throw new Error("connectionToken must be a non-empty string");
|
|
@@ -423,7 +438,8 @@ class CopilotClient {
|
|
|
423
438
|
this.requestHandler = options.requestHandler ?? null;
|
|
424
439
|
this.onGitHubTelemetry = options.onGitHubTelemetry;
|
|
425
440
|
this.setupClientGlobalHandlers();
|
|
426
|
-
const
|
|
441
|
+
const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
|
|
442
|
+
const effectiveEnv = connEnv ?? options.env ?? process.env;
|
|
427
443
|
this.resolvedEnv = effectiveEnv;
|
|
428
444
|
this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
|
|
429
445
|
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
@@ -504,6 +520,9 @@ class CopilotClient {
|
|
|
504
520
|
}
|
|
505
521
|
setupClientGlobalHandlers() {
|
|
506
522
|
const handlers = {};
|
|
523
|
+
handlers.hooks = {
|
|
524
|
+
invoke: async (params) => await this.handleHooksInvoke(params)
|
|
525
|
+
};
|
|
507
526
|
if (this.requestHandler) {
|
|
508
527
|
handlers.llmInference = (0, import_copilotRequestHandler.createCopilotRequestAdapter)(this.requestHandler, () => {
|
|
509
528
|
if (!this.connection) {
|
|
@@ -1058,8 +1077,10 @@ class CopilotClient {
|
|
|
1058
1077
|
parameters: toJsonSchema(tool.parameters),
|
|
1059
1078
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
1060
1079
|
skipPermission: tool.skipPermission,
|
|
1061
|
-
defer: tool.defer
|
|
1080
|
+
defer: tool.defer,
|
|
1081
|
+
metadata: tool.metadata
|
|
1062
1082
|
})),
|
|
1083
|
+
toolSearch: config.toolSearch,
|
|
1063
1084
|
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
1064
1085
|
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
1065
1086
|
requestExtensions: config.requestExtensions,
|
|
@@ -1256,8 +1277,10 @@ class CopilotClient {
|
|
|
1256
1277
|
parameters: toJsonSchema(tool.parameters),
|
|
1257
1278
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
1258
1279
|
skipPermission: tool.skipPermission,
|
|
1259
|
-
defer: tool.defer
|
|
1280
|
+
defer: tool.defer,
|
|
1281
|
+
metadata: tool.metadata
|
|
1260
1282
|
})),
|
|
1283
|
+
toolSearch: config.toolSearch,
|
|
1261
1284
|
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
1262
1285
|
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
1263
1286
|
requestExtensions: config.requestExtensions,
|
|
@@ -1861,21 +1884,42 @@ stderr: ${stderrOutput}`
|
|
|
1861
1884
|
return this.connectViaTcp();
|
|
1862
1885
|
}
|
|
1863
1886
|
}
|
|
1864
|
-
/**
|
|
1865
|
-
* Start the in-process FFI runtime host: resolve the CLI entrypoint and native
|
|
1866
|
-
* runtime library, then let the native host spawn the CLI worker.
|
|
1867
|
-
*
|
|
1868
|
-
* The worker inherits this host process's ambient environment; per-client options
|
|
1869
|
-
* that lower to environment variables (`env`, `telemetry`, `gitHubToken`,
|
|
1870
|
-
* `baseDirectory`) are intentionally not applied here, because the native runtime
|
|
1871
|
-
* loads into the shared host process and a single env block cannot carry per-client
|
|
1872
|
-
* values. Configure the in-process runtime via the host process environment instead.
|
|
1873
|
-
* See https://github.com/github/copilot-sdk/issues/1934.
|
|
1874
|
-
*/
|
|
1887
|
+
/** Starts the in-process FFI runtime with SDK-managed typed options. */
|
|
1875
1888
|
async startInProcessFfi() {
|
|
1876
1889
|
const entrypoint = this.resolveCliPathForFfi();
|
|
1877
1890
|
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
1878
|
-
const
|
|
1891
|
+
const environment = {};
|
|
1892
|
+
if (this.options.gitHubToken) {
|
|
1893
|
+
environment.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1894
|
+
}
|
|
1895
|
+
if (this.options.baseDirectory) {
|
|
1896
|
+
environment.COPILOT_HOME = this.options.baseDirectory;
|
|
1897
|
+
}
|
|
1898
|
+
if (this.options.mode === "empty") {
|
|
1899
|
+
environment.COPILOT_DISABLE_KEYTAR = "1";
|
|
1900
|
+
}
|
|
1901
|
+
const args = [];
|
|
1902
|
+
if (this.options.logLevel) {
|
|
1903
|
+
args.push("--log-level", this.options.logLevel);
|
|
1904
|
+
}
|
|
1905
|
+
if (this.options.gitHubToken) {
|
|
1906
|
+
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
1907
|
+
}
|
|
1908
|
+
if (!this.options.useLoggedInUser) {
|
|
1909
|
+
args.push("--no-auto-login");
|
|
1910
|
+
}
|
|
1911
|
+
if (this.options.sessionIdleTimeoutSeconds > 0) {
|
|
1912
|
+
args.push("--session-idle-timeout", this.options.sessionIdleTimeoutSeconds.toString());
|
|
1913
|
+
}
|
|
1914
|
+
if (this.options.enableRemoteSessions) {
|
|
1915
|
+
args.push("--remote");
|
|
1916
|
+
}
|
|
1917
|
+
const host = FfiRuntimeHost.create(
|
|
1918
|
+
entrypoint,
|
|
1919
|
+
CopilotClient.getNapiPrebuildsFolder(entrypoint),
|
|
1920
|
+
environment,
|
|
1921
|
+
args
|
|
1922
|
+
);
|
|
1879
1923
|
this.ffiHost = host;
|
|
1880
1924
|
await host.start();
|
|
1881
1925
|
}
|
|
@@ -1905,14 +1949,30 @@ stderr: ${stderrOutput}`
|
|
|
1905
1949
|
/**
|
|
1906
1950
|
* Returns the napi prebuilds folder name for the current host — the
|
|
1907
1951
|
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
1908
|
-
* `linux-x64`) under which the runtime ships
|
|
1952
|
+
* `linux-x64`, `linuxmusl-x64`) under which the runtime ships
|
|
1953
|
+
* `prebuilds/<folder>/runtime.node`.
|
|
1909
1954
|
*/
|
|
1910
|
-
static getNapiPrebuildsFolder() {
|
|
1955
|
+
static getNapiPrebuildsFolder(entrypoint) {
|
|
1911
1956
|
const arch = process.arch;
|
|
1912
1957
|
if (arch !== "x64" && arch !== "arm64") {
|
|
1913
1958
|
throw new Error(`Unsupported architecture '${arch}' for in-process FFI hosting.`);
|
|
1914
1959
|
}
|
|
1915
|
-
|
|
1960
|
+
let platform = process.platform;
|
|
1961
|
+
if (platform === "linux" && CopilotClient.isMusl(entrypoint)) {
|
|
1962
|
+
platform = "linuxmusl";
|
|
1963
|
+
}
|
|
1964
|
+
return `${platform}-${arch}`;
|
|
1965
|
+
}
|
|
1966
|
+
static isMusl(entrypoint) {
|
|
1967
|
+
if (entrypoint.includes(`copilot-linuxmusl-${process.arch}`)) {
|
|
1968
|
+
return true;
|
|
1969
|
+
}
|
|
1970
|
+
if (entrypoint.includes(`copilot-linux-${process.arch}`)) {
|
|
1971
|
+
return false;
|
|
1972
|
+
}
|
|
1973
|
+
const report = process.report?.getReport();
|
|
1974
|
+
const header = report && "header" in report ? report.header : void 0;
|
|
1975
|
+
return header !== void 0 && header.glibcVersionRuntime === void 0;
|
|
1916
1976
|
}
|
|
1917
1977
|
/**
|
|
1918
1978
|
* Connect to child via stdio pipes
|
|
@@ -2008,10 +2068,6 @@ stderr: ${stderrOutput}`
|
|
|
2008
2068
|
"autoModeSwitch.request",
|
|
2009
2069
|
async (params) => await this.handleAutoModeSwitchRequest(params)
|
|
2010
2070
|
);
|
|
2011
|
-
this.connection.onRequest(
|
|
2012
|
-
"hooks.invoke",
|
|
2013
|
-
async (params) => await this.handleHooksInvoke(params)
|
|
2014
|
-
);
|
|
2015
2071
|
this.connection.onRequest(
|
|
2016
2072
|
"systemMessage.transform",
|
|
2017
2073
|
async (params) => await this.handleSystemMessageTransform(params)
|
|
@@ -84,8 +84,9 @@ function loadLibrary(libraryPath) {
|
|
|
84
84
|
loadedLibraryPath = libraryPath;
|
|
85
85
|
return loadedLibrary;
|
|
86
86
|
}
|
|
87
|
-
function buildArgvJson(cliEntrypoint) {
|
|
87
|
+
function buildArgvJson(cliEntrypoint, args) {
|
|
88
88
|
const argv = cliEntrypoint.toLowerCase().endsWith(".js") ? ["node", cliEntrypoint, "--embedded-host", "--no-auto-update"] : [cliEntrypoint, "--embedded-host", "--no-auto-update"];
|
|
89
|
+
argv.push(...args);
|
|
89
90
|
return Buffer.from(JSON.stringify(argv), "utf8");
|
|
90
91
|
}
|
|
91
92
|
function buildEnvJson(environment) {
|
|
@@ -104,10 +105,11 @@ function buildEnvJson(environment) {
|
|
|
104
105
|
return Buffer.from(JSON.stringify(obj), "utf8");
|
|
105
106
|
}
|
|
106
107
|
class FfiRuntimeHost {
|
|
107
|
-
constructor(libraryPath, cliEntrypoint, environment) {
|
|
108
|
+
constructor(libraryPath, cliEntrypoint, environment, args) {
|
|
108
109
|
this.libraryPath = libraryPath;
|
|
109
110
|
this.cliEntrypoint = cliEntrypoint;
|
|
110
111
|
this.environment = environment;
|
|
112
|
+
this.args = args;
|
|
111
113
|
this.lib = loadLibrary(libraryPath);
|
|
112
114
|
this.receiveStream = new import_node_stream.PassThrough();
|
|
113
115
|
this.sendStream = new import_node_stream.Writable({
|
|
@@ -126,6 +128,7 @@ class FfiRuntimeHost {
|
|
|
126
128
|
libraryPath;
|
|
127
129
|
cliEntrypoint;
|
|
128
130
|
environment;
|
|
131
|
+
args;
|
|
129
132
|
lib;
|
|
130
133
|
serverId = 0;
|
|
131
134
|
connectionId = 0;
|
|
@@ -142,21 +145,21 @@ class FfiRuntimeHost {
|
|
|
142
145
|
* the entrypoint directory (the napi-rs `<node-platform>-<arch>` layout, e.g.
|
|
143
146
|
* `linux-x64`). Throws if it cannot be found.
|
|
144
147
|
*/
|
|
145
|
-
static create(cliEntrypoint, prebuildsFolder, environment) {
|
|
148
|
+
static create(cliEntrypoint, prebuildsFolder, environment, args) {
|
|
146
149
|
const fullEntrypoint = (0, import_node_path.resolve)(cliEntrypoint);
|
|
147
150
|
const distDir = (0, import_node_path.dirname)(fullEntrypoint);
|
|
148
151
|
const libraryPath = (0, import_node_path.join)(distDir, "prebuilds", prebuildsFolder, "runtime.node");
|
|
149
152
|
if (!(0, import_node_fs.existsSync)(libraryPath)) {
|
|
150
153
|
throw new Error(`FFI runtime library not found. Looked for '${libraryPath}'.`);
|
|
151
154
|
}
|
|
152
|
-
return new FfiRuntimeHost(libraryPath, fullEntrypoint, environment);
|
|
155
|
+
return new FfiRuntimeHost(libraryPath, fullEntrypoint, environment, args);
|
|
153
156
|
}
|
|
154
157
|
/**
|
|
155
158
|
* Starts the in-process runtime: spawns the CLI worker via the native host,
|
|
156
159
|
* waits for readiness, and opens the FFI JSON-RPC connection.
|
|
157
160
|
*/
|
|
158
161
|
async start() {
|
|
159
|
-
const argvJson = buildArgvJson(this.cliEntrypoint);
|
|
162
|
+
const argvJson = buildArgvJson(this.cliEntrypoint, this.args);
|
|
160
163
|
const envJson = buildEnvJson(this.environment);
|
|
161
164
|
this.serverId = await new Promise((resolvePromise, rejectPromise) => {
|
|
162
165
|
this.lib.hostStart.async(
|
|
@@ -223,7 +223,7 @@ function createServerRpc(connection) {
|
|
|
223
223
|
/**
|
|
224
224
|
* Registers a new marketplace from a source (owner/repo, URL, or local path).
|
|
225
225
|
*
|
|
226
|
-
* @param params Marketplace source
|
|
226
|
+
* @param params Marketplace source and optional working directory for relative-path resolution.
|
|
227
227
|
*
|
|
228
228
|
* @returns Result of registering a new marketplace.
|
|
229
229
|
*/
|
|
@@ -1119,7 +1119,7 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1119
1119
|
*/
|
|
1120
1120
|
list: async () => connection.sendRequest("session.mcp.list", { sessionId }),
|
|
1121
1121
|
/**
|
|
1122
|
-
* Lists the tools exposed by a connected MCP server on this session's host.
|
|
1122
|
+
* Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session.
|
|
1123
1123
|
*
|
|
1124
1124
|
* @param params Server name whose tool list should be returned.
|
|
1125
1125
|
*
|
|
@@ -1231,13 +1231,11 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1231
1231
|
/** @experimental */
|
|
1232
1232
|
apps: {
|
|
1233
1233
|
/**
|
|
1234
|
-
*
|
|
1234
|
+
* Fetch an MCP resource (typically a `ui://` MCP App bundle, per SEP-1865) from a connected server. Requires the `mcp-apps` session capability.
|
|
1235
1235
|
*
|
|
1236
|
-
* @param params
|
|
1237
|
-
*
|
|
1238
|
-
* @returns Deprecated/obsolete MCP Apps alias for `McpResourcesReadResult`; use `session.mcp.resources.read` instead.
|
|
1236
|
+
* @param params MCP server and resource URI to fetch.
|
|
1239
1237
|
*
|
|
1240
|
-
* @
|
|
1238
|
+
* @returns Resource contents returned by the MCP server.
|
|
1241
1239
|
*/
|
|
1242
1240
|
readResource: async (params) => connection.sendRequest("session.mcp.apps.readResource", { sessionId, ...params }),
|
|
1243
1241
|
/**
|
|
@@ -1804,11 +1802,11 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1804
1802
|
*/
|
|
1805
1803
|
recordContextChange: async (params) => connection.sendRequest("session.metadata.recordContextChange", { sessionId, ...params }),
|
|
1806
1804
|
/**
|
|
1807
|
-
* Updates the session's
|
|
1805
|
+
* Updates the session's working directory. For local sessions the target is validated first (an absolute path that exists on disk) and the permission primary directory is re-based; a rejected validation fails the call before any session state changes.
|
|
1808
1806
|
*
|
|
1809
|
-
* @param params Absolute path to set as the session's new working directory.
|
|
1807
|
+
* @param params Absolute path to set as the session's new working directory. For local sessions the path must be absolute and exist on disk: it is validated before any session state changes, and a failing validation rejects the call with nothing mutated, persisted, or emitted. Remote sessions record the path as-is.
|
|
1810
1808
|
*
|
|
1811
|
-
* @returns Update the session's working directory. Used by the host when the user explicitly changes cwd (e.g., the `/cd` slash command). The host is responsible for
|
|
1809
|
+
* @returns Update the session's working directory. Used by the host when the user explicitly changes cwd (e.g., the `/cd` slash command). The host is responsible for any related side-effects (file index, etc.); it does NOT change the process working directory (a session's cwd is per-session, not process-global). For local sessions the runtime validates the target first (an absolute path that exists on disk) and re-bases the permission primary directory; a rejected validation fails the call before anything is mutated, persisted, or emitted. Location-scoped permission rules are then re-keyed to the new directory (best-effort). Remote sessions only record the path.
|
|
1812
1810
|
*/
|
|
1813
1811
|
setWorkingDirectory: async (params) => connection.sendRequest("session.metadata.setWorkingDirectory", { sessionId, ...params }),
|
|
1814
1812
|
/**
|
|
@@ -2147,6 +2145,11 @@ function registerClientSessionApiHandlers(connection, getHandlers) {
|
|
|
2147
2145
|
});
|
|
2148
2146
|
}
|
|
2149
2147
|
function registerClientGlobalApiHandlers(connection, handlers) {
|
|
2148
|
+
connection.onRequest("hooks.invoke", async (params) => {
|
|
2149
|
+
const handler = handlers.hooks;
|
|
2150
|
+
if (!handler) throw new Error("No hooks client-global handler registered");
|
|
2151
|
+
return handler.invoke(params);
|
|
2152
|
+
});
|
|
2150
2153
|
connection.onRequest("llmInference.httpRequestStart", async (params) => {
|
|
2151
2154
|
const handler = handlers.llmInference;
|
|
2152
2155
|
if (!handler) throw new Error("No llmInference client-global handler registered");
|
package/dist/cjs/session.js
CHANGED
|
@@ -40,6 +40,7 @@ function isOpenCanvasInstance(value) {
|
|
|
40
40
|
const instance = value;
|
|
41
41
|
return typeof instance.instanceId === "string" && instance.instanceId.length > 0 && typeof instance.extensionId === "string" && instance.extensionId.length > 0 && typeof instance.canvasId === "string" && instance.canvasId.length > 0;
|
|
42
42
|
}
|
|
43
|
+
const TOOL_SEARCH_TOOL_NAME = "tool_search_tool";
|
|
43
44
|
class CopilotSession {
|
|
44
45
|
/**
|
|
45
46
|
* Creates a new CopilotSession instance.
|
|
@@ -352,11 +353,21 @@ class CopilotSession {
|
|
|
352
353
|
*/
|
|
353
354
|
async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate) {
|
|
354
355
|
try {
|
|
356
|
+
let availableTools;
|
|
357
|
+
if (toolName === TOOL_SEARCH_TOOL_NAME) {
|
|
358
|
+
try {
|
|
359
|
+
const metadata = await this.rpc.tools.getCurrentMetadata();
|
|
360
|
+
availableTools = metadata.tools ?? void 0;
|
|
361
|
+
} catch {
|
|
362
|
+
availableTools = void 0;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
355
365
|
const rawResult = await handler(args, {
|
|
356
366
|
sessionId: this.sessionId,
|
|
357
367
|
toolCallId,
|
|
358
368
|
toolName,
|
|
359
369
|
arguments: args,
|
|
370
|
+
availableTools,
|
|
360
371
|
traceparent,
|
|
361
372
|
tracestate
|
|
362
373
|
});
|
|
@@ -1009,7 +1020,7 @@ class CopilotSession {
|
|
|
1009
1020
|
*
|
|
1010
1021
|
* @example
|
|
1011
1022
|
* ```typescript
|
|
1012
|
-
* await session.setModel("gpt-4
|
|
1023
|
+
* await session.setModel("gpt-5.4");
|
|
1013
1024
|
* await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" });
|
|
1014
1025
|
* ```
|
|
1015
1026
|
*/
|
package/dist/cjs/types.js
CHANGED
|
@@ -39,7 +39,7 @@ const RuntimeConnection = {
|
|
|
39
39
|
* This is the default if no {@link CopilotClientOptions.connection} is set.
|
|
40
40
|
*/
|
|
41
41
|
forStdio(opts = {}) {
|
|
42
|
-
return { kind: "stdio", path: opts.path, args: opts.args };
|
|
42
|
+
return { kind: "stdio", path: opts.path, args: opts.args, env: opts.env };
|
|
43
43
|
},
|
|
44
44
|
/**
|
|
45
45
|
* Spawn a runtime child process that listens on a TCP socket and connect to it.
|
|
@@ -50,7 +50,8 @@ const RuntimeConnection = {
|
|
|
50
50
|
port: opts.port,
|
|
51
51
|
connectionToken: opts.connectionToken,
|
|
52
52
|
path: opts.path,
|
|
53
|
-
args: opts.args
|
|
53
|
+
args: opts.args,
|
|
54
|
+
env: opts.env
|
|
54
55
|
};
|
|
55
56
|
},
|
|
56
57
|
/**
|
package/dist/client.d.ts
CHANGED
|
@@ -435,17 +435,7 @@ export declare class CopilotClient {
|
|
|
435
435
|
* Connect to the CLI server (via socket or stdio)
|
|
436
436
|
*/
|
|
437
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
|
-
*/
|
|
438
|
+
/** Starts the in-process FFI runtime with SDK-managed typed options. */
|
|
449
439
|
private startInProcessFfi;
|
|
450
440
|
/**
|
|
451
441
|
* Connect to the in-process FFI runtime host over its receive/send streams,
|
|
@@ -460,9 +450,11 @@ export declare class CopilotClient {
|
|
|
460
450
|
/**
|
|
461
451
|
* Returns the napi prebuilds folder name for the current host — the
|
|
462
452
|
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
463
|
-
* `linux-x64`) under which the runtime ships
|
|
453
|
+
* `linux-x64`, `linuxmusl-x64`) under which the runtime ships
|
|
454
|
+
* `prebuilds/<folder>/runtime.node`.
|
|
464
455
|
*/
|
|
465
456
|
private static getNapiPrebuildsFolder;
|
|
457
|
+
private static isMusl;
|
|
466
458
|
/**
|
|
467
459
|
* Connect to child via stdio pipes
|
|
468
460
|
*/
|
package/dist/client.js
CHANGED
|
@@ -372,6 +372,21 @@ class CopilotClient {
|
|
|
372
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
373
|
);
|
|
374
374
|
}
|
|
375
|
+
if (conn.kind === "inprocess" && options.env !== void 0) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
"env is not supported with RuntimeConnection.forInProcess(): the in-process transport loads the native runtime into the shared host process, whose single environment block cannot carry per-client values. Set the variables on the host process environment instead."
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
if (conn.kind === "inprocess" && options.telemetry !== void 0) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
"telemetry is not supported with RuntimeConnection.forInProcess(): telemetry configuration is lowered to environment variables read by native runtime code running in the shared host process, so per-client telemetry cannot be honored in-process. Configure telemetry via the host process environment, or use a child-process transport."
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if ((conn.kind === "stdio" || conn.kind === "tcp") && conn.env !== void 0 && options.env !== void 0) {
|
|
386
|
+
throw new Error(
|
|
387
|
+
"Set environment variables via either the client-level env option or the connection's env (RuntimeConnection.forStdio/forTcp), not both. Prefer the connection-level env for child-process transports."
|
|
388
|
+
);
|
|
389
|
+
}
|
|
375
390
|
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
376
391
|
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
377
392
|
throw new Error("connectionToken must be a non-empty string");
|
|
@@ -400,7 +415,8 @@ class CopilotClient {
|
|
|
400
415
|
this.requestHandler = options.requestHandler ?? null;
|
|
401
416
|
this.onGitHubTelemetry = options.onGitHubTelemetry;
|
|
402
417
|
this.setupClientGlobalHandlers();
|
|
403
|
-
const
|
|
418
|
+
const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
|
|
419
|
+
const effectiveEnv = connEnv ?? options.env ?? process.env;
|
|
404
420
|
this.resolvedEnv = effectiveEnv;
|
|
405
421
|
this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
|
|
406
422
|
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
@@ -481,6 +497,9 @@ class CopilotClient {
|
|
|
481
497
|
}
|
|
482
498
|
setupClientGlobalHandlers() {
|
|
483
499
|
const handlers = {};
|
|
500
|
+
handlers.hooks = {
|
|
501
|
+
invoke: async (params) => await this.handleHooksInvoke(params)
|
|
502
|
+
};
|
|
484
503
|
if (this.requestHandler) {
|
|
485
504
|
handlers.llmInference = createCopilotRequestAdapter(this.requestHandler, () => {
|
|
486
505
|
if (!this.connection) {
|
|
@@ -1035,8 +1054,10 @@ class CopilotClient {
|
|
|
1035
1054
|
parameters: toJsonSchema(tool.parameters),
|
|
1036
1055
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
1037
1056
|
skipPermission: tool.skipPermission,
|
|
1038
|
-
defer: tool.defer
|
|
1057
|
+
defer: tool.defer,
|
|
1058
|
+
metadata: tool.metadata
|
|
1039
1059
|
})),
|
|
1060
|
+
toolSearch: config.toolSearch,
|
|
1040
1061
|
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
1041
1062
|
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
1042
1063
|
requestExtensions: config.requestExtensions,
|
|
@@ -1233,8 +1254,10 @@ class CopilotClient {
|
|
|
1233
1254
|
parameters: toJsonSchema(tool.parameters),
|
|
1234
1255
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
1235
1256
|
skipPermission: tool.skipPermission,
|
|
1236
|
-
defer: tool.defer
|
|
1257
|
+
defer: tool.defer,
|
|
1258
|
+
metadata: tool.metadata
|
|
1237
1259
|
})),
|
|
1260
|
+
toolSearch: config.toolSearch,
|
|
1238
1261
|
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
1239
1262
|
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
1240
1263
|
requestExtensions: config.requestExtensions,
|
|
@@ -1838,21 +1861,42 @@ stderr: ${stderrOutput}`
|
|
|
1838
1861
|
return this.connectViaTcp();
|
|
1839
1862
|
}
|
|
1840
1863
|
}
|
|
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
|
-
*/
|
|
1864
|
+
/** Starts the in-process FFI runtime with SDK-managed typed options. */
|
|
1852
1865
|
async startInProcessFfi() {
|
|
1853
1866
|
const entrypoint = this.resolveCliPathForFfi();
|
|
1854
1867
|
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
1855
|
-
const
|
|
1868
|
+
const environment = {};
|
|
1869
|
+
if (this.options.gitHubToken) {
|
|
1870
|
+
environment.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1871
|
+
}
|
|
1872
|
+
if (this.options.baseDirectory) {
|
|
1873
|
+
environment.COPILOT_HOME = this.options.baseDirectory;
|
|
1874
|
+
}
|
|
1875
|
+
if (this.options.mode === "empty") {
|
|
1876
|
+
environment.COPILOT_DISABLE_KEYTAR = "1";
|
|
1877
|
+
}
|
|
1878
|
+
const args = [];
|
|
1879
|
+
if (this.options.logLevel) {
|
|
1880
|
+
args.push("--log-level", this.options.logLevel);
|
|
1881
|
+
}
|
|
1882
|
+
if (this.options.gitHubToken) {
|
|
1883
|
+
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
1884
|
+
}
|
|
1885
|
+
if (!this.options.useLoggedInUser) {
|
|
1886
|
+
args.push("--no-auto-login");
|
|
1887
|
+
}
|
|
1888
|
+
if (this.options.sessionIdleTimeoutSeconds > 0) {
|
|
1889
|
+
args.push("--session-idle-timeout", this.options.sessionIdleTimeoutSeconds.toString());
|
|
1890
|
+
}
|
|
1891
|
+
if (this.options.enableRemoteSessions) {
|
|
1892
|
+
args.push("--remote");
|
|
1893
|
+
}
|
|
1894
|
+
const host = FfiRuntimeHost.create(
|
|
1895
|
+
entrypoint,
|
|
1896
|
+
CopilotClient.getNapiPrebuildsFolder(entrypoint),
|
|
1897
|
+
environment,
|
|
1898
|
+
args
|
|
1899
|
+
);
|
|
1856
1900
|
this.ffiHost = host;
|
|
1857
1901
|
await host.start();
|
|
1858
1902
|
}
|
|
@@ -1882,14 +1926,30 @@ stderr: ${stderrOutput}`
|
|
|
1882
1926
|
/**
|
|
1883
1927
|
* Returns the napi prebuilds folder name for the current host — the
|
|
1884
1928
|
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
1885
|
-
* `linux-x64`) under which the runtime ships
|
|
1929
|
+
* `linux-x64`, `linuxmusl-x64`) under which the runtime ships
|
|
1930
|
+
* `prebuilds/<folder>/runtime.node`.
|
|
1886
1931
|
*/
|
|
1887
|
-
static getNapiPrebuildsFolder() {
|
|
1932
|
+
static getNapiPrebuildsFolder(entrypoint) {
|
|
1888
1933
|
const arch = process.arch;
|
|
1889
1934
|
if (arch !== "x64" && arch !== "arm64") {
|
|
1890
1935
|
throw new Error(`Unsupported architecture '${arch}' for in-process FFI hosting.`);
|
|
1891
1936
|
}
|
|
1892
|
-
|
|
1937
|
+
let platform = process.platform;
|
|
1938
|
+
if (platform === "linux" && CopilotClient.isMusl(entrypoint)) {
|
|
1939
|
+
platform = "linuxmusl";
|
|
1940
|
+
}
|
|
1941
|
+
return `${platform}-${arch}`;
|
|
1942
|
+
}
|
|
1943
|
+
static isMusl(entrypoint) {
|
|
1944
|
+
if (entrypoint.includes(`copilot-linuxmusl-${process.arch}`)) {
|
|
1945
|
+
return true;
|
|
1946
|
+
}
|
|
1947
|
+
if (entrypoint.includes(`copilot-linux-${process.arch}`)) {
|
|
1948
|
+
return false;
|
|
1949
|
+
}
|
|
1950
|
+
const report = process.report?.getReport();
|
|
1951
|
+
const header = report && "header" in report ? report.header : void 0;
|
|
1952
|
+
return header !== void 0 && header.glibcVersionRuntime === void 0;
|
|
1893
1953
|
}
|
|
1894
1954
|
/**
|
|
1895
1955
|
* Connect to child via stdio pipes
|
|
@@ -1985,10 +2045,6 @@ stderr: ${stderrOutput}`
|
|
|
1985
2045
|
"autoModeSwitch.request",
|
|
1986
2046
|
async (params) => await this.handleAutoModeSwitchRequest(params)
|
|
1987
2047
|
);
|
|
1988
|
-
this.connection.onRequest(
|
|
1989
|
-
"hooks.invoke",
|
|
1990
|
-
async (params) => await this.handleHooksInvoke(params)
|
|
1991
|
-
);
|
|
1992
2048
|
this.connection.onRequest(
|
|
1993
2049
|
"systemMessage.transform",
|
|
1994
2050
|
async (params) => await this.handleSystemMessageTransform(params)
|
package/dist/ffiRuntimeHost.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { PassThrough, Writable } from "node:stream";
|
|
|
2
2
|
export declare class FfiRuntimeHost {
|
|
3
3
|
private readonly libraryPath;
|
|
4
4
|
private readonly cliEntrypoint;
|
|
5
|
-
private readonly environment
|
|
5
|
+
private readonly environment;
|
|
6
|
+
private readonly args;
|
|
6
7
|
private readonly lib;
|
|
7
8
|
private serverId;
|
|
8
9
|
private connectionId;
|
|
@@ -20,7 +21,7 @@ export declare class FfiRuntimeHost {
|
|
|
20
21
|
* the entrypoint directory (the napi-rs `<node-platform>-<arch>` layout, e.g.
|
|
21
22
|
* `linux-x64`). Throws if it cannot be found.
|
|
22
23
|
*/
|
|
23
|
-
static create(cliEntrypoint: string, prebuildsFolder: string, environment
|
|
24
|
+
static create(cliEntrypoint: string, prebuildsFolder: string, environment: Record<string, string | undefined> | undefined, args: readonly string[]): FfiRuntimeHost;
|
|
24
25
|
/**
|
|
25
26
|
* Starts the in-process runtime: spawns the CLI worker via the native host,
|
|
26
27
|
* waits for readiness, and opens the FFI JSON-RPC connection.
|
package/dist/ffiRuntimeHost.js
CHANGED
|
@@ -51,8 +51,9 @@ function loadLibrary(libraryPath) {
|
|
|
51
51
|
loadedLibraryPath = libraryPath;
|
|
52
52
|
return loadedLibrary;
|
|
53
53
|
}
|
|
54
|
-
function buildArgvJson(cliEntrypoint) {
|
|
54
|
+
function buildArgvJson(cliEntrypoint, args) {
|
|
55
55
|
const argv = cliEntrypoint.toLowerCase().endsWith(".js") ? ["node", cliEntrypoint, "--embedded-host", "--no-auto-update"] : [cliEntrypoint, "--embedded-host", "--no-auto-update"];
|
|
56
|
+
argv.push(...args);
|
|
56
57
|
return Buffer.from(JSON.stringify(argv), "utf8");
|
|
57
58
|
}
|
|
58
59
|
function buildEnvJson(environment) {
|
|
@@ -71,10 +72,11 @@ function buildEnvJson(environment) {
|
|
|
71
72
|
return Buffer.from(JSON.stringify(obj), "utf8");
|
|
72
73
|
}
|
|
73
74
|
class FfiRuntimeHost {
|
|
74
|
-
constructor(libraryPath, cliEntrypoint, environment) {
|
|
75
|
+
constructor(libraryPath, cliEntrypoint, environment, args) {
|
|
75
76
|
this.libraryPath = libraryPath;
|
|
76
77
|
this.cliEntrypoint = cliEntrypoint;
|
|
77
78
|
this.environment = environment;
|
|
79
|
+
this.args = args;
|
|
78
80
|
this.lib = loadLibrary(libraryPath);
|
|
79
81
|
this.receiveStream = new PassThrough();
|
|
80
82
|
this.sendStream = new Writable({
|
|
@@ -93,6 +95,7 @@ class FfiRuntimeHost {
|
|
|
93
95
|
libraryPath;
|
|
94
96
|
cliEntrypoint;
|
|
95
97
|
environment;
|
|
98
|
+
args;
|
|
96
99
|
lib;
|
|
97
100
|
serverId = 0;
|
|
98
101
|
connectionId = 0;
|
|
@@ -109,21 +112,21 @@ class FfiRuntimeHost {
|
|
|
109
112
|
* the entrypoint directory (the napi-rs `<node-platform>-<arch>` layout, e.g.
|
|
110
113
|
* `linux-x64`). Throws if it cannot be found.
|
|
111
114
|
*/
|
|
112
|
-
static create(cliEntrypoint, prebuildsFolder, environment) {
|
|
115
|
+
static create(cliEntrypoint, prebuildsFolder, environment, args) {
|
|
113
116
|
const fullEntrypoint = resolve(cliEntrypoint);
|
|
114
117
|
const distDir = dirname(fullEntrypoint);
|
|
115
118
|
const libraryPath = join(distDir, "prebuilds", prebuildsFolder, "runtime.node");
|
|
116
119
|
if (!existsSync(libraryPath)) {
|
|
117
120
|
throw new Error(`FFI runtime library not found. Looked for '${libraryPath}'.`);
|
|
118
121
|
}
|
|
119
|
-
return new FfiRuntimeHost(libraryPath, fullEntrypoint, environment);
|
|
122
|
+
return new FfiRuntimeHost(libraryPath, fullEntrypoint, environment, args);
|
|
120
123
|
}
|
|
121
124
|
/**
|
|
122
125
|
* Starts the in-process runtime: spawns the CLI worker via the native host,
|
|
123
126
|
* waits for readiness, and opens the FFI JSON-RPC connection.
|
|
124
127
|
*/
|
|
125
128
|
async start() {
|
|
126
|
-
const argvJson = buildArgvJson(this.cliEntrypoint);
|
|
129
|
+
const argvJson = buildArgvJson(this.cliEntrypoint, this.args);
|
|
127
130
|
const envJson = buildEnvJson(this.environment);
|
|
128
131
|
this.serverId = await new Promise((resolvePromise, rejectPromise) => {
|
|
129
132
|
this.lib.hostStart.async(
|