@github/copilot-sdk 1.0.13-preview.3 → 1.0.13-preview.6
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 +26 -9
- package/dist/cjs/cliVersion.js +31 -0
- package/dist/cjs/client.js +99 -91
- package/dist/cjs/ffiRuntimeHost.js +14 -19
- package/dist/cjs/generated/rpc.js +19 -0
- package/dist/cjs/runtimeArtifacts.js +254 -0
- package/dist/cliVersion.d.ts +2 -0
- package/dist/cliVersion.js +6 -0
- package/dist/client.d.ts +2 -12
- package/dist/client.js +101 -92
- package/dist/ffiRuntimeHost.d.ts +3 -9
- package/dist/ffiRuntimeHost.js +15 -20
- package/dist/generated/rpc.d.ts +267 -7
- package/dist/generated/rpc.js +19 -0
- package/dist/generated/session-events.d.ts +322 -35
- package/dist/index.d.ts +1 -1
- package/dist/runtimeArtifacts.d.ts +18 -0
- package/dist/runtimeArtifacts.js +232 -0
- package/dist/types.d.ts +53 -2
- package/package.json +103 -88
package/README.md
CHANGED
|
@@ -8,6 +8,20 @@ To use the SDK, you'll need:
|
|
|
8
8
|
|
|
9
9
|
- Node.js ^20.19.0 or >=22.12.0
|
|
10
10
|
|
|
11
|
+
The SDK uses an optional `@github/copilot-sdk-<platform>` package containing the
|
|
12
|
+
Copilot CLI runtime for the host platform. These packages are built from
|
|
13
|
+
verified `github/copilot-cli` release assets when the SDK is published, so
|
|
14
|
+
starting the SDK performs no runtime download. Set `COPILOT_CLI_PATH` to use an
|
|
15
|
+
existing installation instead.
|
|
16
|
+
|
|
17
|
+
The checked-in release pin is `copilotCliVersion` in `package.json`. Run
|
|
18
|
+
`npm run set:cli-version -- <version>` to update it and regenerate the compiled
|
|
19
|
+
metadata in `src/cliVersion.ts`. Packaging verifies release assets against the
|
|
20
|
+
release's `SHA256SUMS.txt`.
|
|
21
|
+
|
|
22
|
+
`npm run pack:release` builds the main package and all platform packages. Set
|
|
23
|
+
`COPILOT_CLI_DOWNLOAD_BASE_URL` to use a release mirror while packaging.
|
|
24
|
+
|
|
11
25
|
## Installation
|
|
12
26
|
|
|
13
27
|
```bash
|
|
@@ -22,6 +36,7 @@ Try the interactive chat sample (from the repo root):
|
|
|
22
36
|
cd nodejs
|
|
23
37
|
npm ci
|
|
24
38
|
npm run build
|
|
39
|
+
export COPILOT_CLI_PATH="$(npm run --silent prepare:runtime -- --print-path)"
|
|
25
40
|
cd samples
|
|
26
41
|
npm install
|
|
27
42
|
npm start
|
|
@@ -95,6 +110,7 @@ new CopilotClient(options?: CopilotClientOptions)
|
|
|
95
110
|
- `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.
|
|
96
111
|
- `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.
|
|
97
112
|
- 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).
|
|
113
|
+
- Managed child-process connections materialize the bundled `copilot-runtime` and adjacent `runtime.node`, then launch the wrapper by default. An explicit connection `path` or `COPILOT_CLI_PATH` overrides the bundled runtime.
|
|
98
114
|
- `mode?: "empty" | "copilot-cli"` - Defaulting strategy. Use `"empty"` for multi-user server mode; defaults to `"copilot-cli"`.
|
|
99
115
|
- `workingDirectory?: string` - Working directory for the runtime process (default: current process cwd).
|
|
100
116
|
- `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`.
|
|
@@ -131,6 +147,7 @@ Create a new conversation session.
|
|
|
131
147
|
|
|
132
148
|
- `sessionId?: string` - Custom session ID.
|
|
133
149
|
- `model?: string` - Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.**
|
|
150
|
+
- `capi?: CapiSessionOptions` - Copilot API options. With `model: "auto"`, set `autoTier` to `"efficiency"`, `"balance"`, or `"intelligence"` to choose a routing preference. Requires a runtime with Auto tier support and V2 Auto routing. Omission preserves default behavior. See [Auto tier persistence](../docs/features/session-persistence.md#auto-tier-persistence) for resume semantics.
|
|
134
151
|
- `reasoningEffort?: "low" | "medium" | "high" | "xhigh" | "max"` - Reasoning effort level for models that support it. Use `listModels()` to check which models support this option.
|
|
135
152
|
- `tools?: Tool[]` - Custom tools exposed to the CLI. Tools without `handler` are declaration-only and must be resolved via pending tool-call RPCs.
|
|
136
153
|
- `systemMessage?: SystemMessageConfig` - System message customization (see below)
|
|
@@ -934,15 +951,15 @@ const session = await client.createSession({
|
|
|
934
951
|
|
|
935
952
|
The handler must return one of the `PermissionDecision` shapes (or `{ kind: "no-result" }`). Approval scopes are present-tense — they describe the decision to apply, not the outcome reported back on session events:
|
|
936
953
|
|
|
937
|
-
| Kind | Meaning
|
|
938
|
-
| ------------------------ |
|
|
939
|
-
| `"approve-once"` | Allow this single request
|
|
940
|
-
| `"approve-for-session"` | Allow this request and remember the approval for the rest of the session
|
|
941
|
-
| `"approve-for-location"` | Allow this request and persist the approval for this project location (git root or cwd)
|
|
942
|
-
| `"approve-permanently"` | Allow this request and persist the approval across sessions (currently used for URL domains)
|
|
943
|
-
| `"reject"` | Deny the request
|
|
944
|
-
| `"user-not-available"` | Deny the request because no user is available to confirm it
|
|
945
|
-
| `"no-result"` | Suppress this SDK client's response so another connected client can answer the pending request | —
|
|
954
|
+
| Kind | Meaning | Extra fields |
|
|
955
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
956
|
+
| `"approve-once"` | Allow this single request | — |
|
|
957
|
+
| `"approve-for-session"` | Allow this request and remember the approval for the rest of the session | `approval?` (rule to remember), `domain?` (for URL approvals) |
|
|
958
|
+
| `"approve-for-location"` | Allow this request and persist the approval for this project location (git root or cwd) | `approval` (rule to persist), `locationKey` (location to persist under) |
|
|
959
|
+
| `"approve-permanently"` | Allow this request and persist the approval across sessions (currently used for URL domains) | `domain` (URL domain to approve) |
|
|
960
|
+
| `"reject"` | Deny the request | `feedback?` (optional string surfaced to the agent) |
|
|
961
|
+
| `"user-not-available"` | Deny the request because no user is available to confirm it | — |
|
|
962
|
+
| `"no-result"` | Suppress this SDK client's response so another connected client can answer the pending request | — |
|
|
946
963
|
|
|
947
964
|
### Resuming Sessions
|
|
948
965
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var cliVersion_exports = {};
|
|
20
|
+
__export(cliVersion_exports, {
|
|
21
|
+
COPILOT_CLI_USE_NPM_PACKAGE: () => COPILOT_CLI_USE_NPM_PACKAGE,
|
|
22
|
+
COPILOT_CLI_VERSION: () => COPILOT_CLI_VERSION
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(cliVersion_exports);
|
|
25
|
+
const COPILOT_CLI_VERSION = "1.0.83-3";
|
|
26
|
+
const COPILOT_CLI_USE_NPM_PACKAGE = false;
|
|
27
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
28
|
+
0 && (module.exports = {
|
|
29
|
+
COPILOT_CLI_USE_NPM_PACKAGE,
|
|
30
|
+
COPILOT_CLI_VERSION
|
|
31
|
+
});
|
package/dist/cjs/client.js
CHANGED
|
@@ -34,20 +34,19 @@ module.exports = __toCommonJS(client_exports);
|
|
|
34
34
|
var import_node_child_process = require("node:child_process");
|
|
35
35
|
var import_node_crypto = require("node:crypto");
|
|
36
36
|
var import_node_fs = require("node:fs");
|
|
37
|
-
var import_node_module = require("node:module");
|
|
38
37
|
var import_node_net = require("node:net");
|
|
39
38
|
var import_node_path = require("node:path");
|
|
40
|
-
var import_node_url = require("node:url");
|
|
41
39
|
var import_node = require("vscode-jsonrpc/node.js");
|
|
42
40
|
var import_rpc = require("./generated/rpc.js");
|
|
43
41
|
var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
|
|
44
42
|
var import_session = require("./session.js");
|
|
43
|
+
var import_runtimeArtifacts = require("./runtimeArtifacts.js");
|
|
44
|
+
var import_cliVersion = require("./cliVersion.js");
|
|
45
45
|
var import_sessionFsProvider = require("./sessionFsProvider.js");
|
|
46
46
|
var import_copilotRequestHandler = require("./copilotRequestHandler.js");
|
|
47
47
|
var import_telemetry = require("./telemetry.js");
|
|
48
48
|
var import_toolSet = require("./toolSet.js");
|
|
49
49
|
var import_types = require("./types.js");
|
|
50
|
-
const import_meta = {};
|
|
51
50
|
const MIN_PROTOCOL_VERSION = 3;
|
|
52
51
|
const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
53
52
|
function isZodSchema(value) {
|
|
@@ -72,7 +71,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
72
71
|
if (child.exitCode != null || child.signalCode != null) {
|
|
73
72
|
return true;
|
|
74
73
|
}
|
|
75
|
-
return new Promise((
|
|
74
|
+
return new Promise((resolve2) => {
|
|
76
75
|
let timeout;
|
|
77
76
|
let settled = false;
|
|
78
77
|
const onExit = () => {
|
|
@@ -81,7 +80,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
81
80
|
}
|
|
82
81
|
settled = true;
|
|
83
82
|
clearTimeout(timeout);
|
|
84
|
-
|
|
83
|
+
resolve2(true);
|
|
85
84
|
};
|
|
86
85
|
timeout = setTimeout(() => {
|
|
87
86
|
if (settled) {
|
|
@@ -89,7 +88,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
89
88
|
}
|
|
90
89
|
settled = true;
|
|
91
90
|
child.off("exit", onExit);
|
|
92
|
-
|
|
91
|
+
resolve2(false);
|
|
93
92
|
}, timeoutMs);
|
|
94
93
|
child.once("exit", onExit);
|
|
95
94
|
if (child.exitCode != null || child.signalCode != null) {
|
|
@@ -150,6 +149,15 @@ function toWireCustomAgents(agents) {
|
|
|
150
149
|
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
151
150
|
});
|
|
152
151
|
}
|
|
152
|
+
function clientInfoToWire(info) {
|
|
153
|
+
if (info == null) return void 0;
|
|
154
|
+
const wire = {};
|
|
155
|
+
if (info.applicationName) wire.editorName = info.applicationName;
|
|
156
|
+
if (info.applicationVersion) wire.editorVersion = info.applicationVersion;
|
|
157
|
+
if (info.integrationName) wire.extensionName = info.integrationName;
|
|
158
|
+
if (info.integrationVersion) wire.extensionVersion = info.integrationVersion;
|
|
159
|
+
return Object.keys(wire).length > 0 ? wire : void 0;
|
|
160
|
+
}
|
|
153
161
|
function toWireLargeOutput(config) {
|
|
154
162
|
if (!config) return void 0;
|
|
155
163
|
const { outputDirectory, ...rest } = config;
|
|
@@ -207,39 +215,8 @@ function getNodeExecPath() {
|
|
|
207
215
|
}
|
|
208
216
|
return process.execPath;
|
|
209
217
|
}
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
const variants = process.platform === "linux" ? ["linux", "linuxmusl"] : [process.platform];
|
|
213
|
-
return variants.map((variant) => `@github/copilot-${variant}-${arch}`);
|
|
214
|
-
}
|
|
215
|
-
function getBundledCliPath() {
|
|
216
|
-
const packageNames = getCliPlatformPackageNames();
|
|
217
|
-
if (typeof import_meta.resolve === "function") {
|
|
218
|
-
for (const packageName of packageNames) {
|
|
219
|
-
try {
|
|
220
|
-
const packageEntryUrl = import_meta.resolve(packageName);
|
|
221
|
-
const packageEntryPath = (0, import_node_url.fileURLToPath)(packageEntryUrl);
|
|
222
|
-
return (0, import_node_path.join)((0, import_node_path.dirname)(packageEntryPath), "index.js");
|
|
223
|
-
} catch {
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
throw new Error(
|
|
227
|
-
`Could not resolve a @github/copilot platform package (tried ${packageNames.join(", ")}). Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.`
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
const req = (0, import_node_module.createRequire)(__filename);
|
|
231
|
-
const searchPaths = req.resolve.paths("@github/copilot") ?? [];
|
|
232
|
-
for (const base of searchPaths) {
|
|
233
|
-
for (const packageName of packageNames) {
|
|
234
|
-
const candidate = (0, import_node_path.join)(base, ...packageName.split("/"), "index.js");
|
|
235
|
-
if ((0, import_node_fs.existsSync)(candidate)) {
|
|
236
|
-
return candidate;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
throw new Error(
|
|
241
|
-
`Could not find a @github/copilot platform package (tried ${packageNames.join(", ")}). Searched ${searchPaths.length} paths. Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.`
|
|
242
|
-
);
|
|
218
|
+
function getBundledRuntimePath() {
|
|
219
|
+
return (0, import_runtimeArtifacts.ensureRuntimeBundle)(import_cliVersion.COPILOT_CLI_VERSION);
|
|
243
220
|
}
|
|
244
221
|
class TeardownResilientStreamMessageWriter extends import_node.StreamMessageWriter {
|
|
245
222
|
suppressWriteErrors = false;
|
|
@@ -287,6 +264,7 @@ class CopilotClient {
|
|
|
287
264
|
_internalRpc = null;
|
|
288
265
|
processExitPromise = null;
|
|
289
266
|
// Rejects when CLI process exits
|
|
267
|
+
processTransportError = null;
|
|
290
268
|
negotiatedProtocolVersion = null;
|
|
291
269
|
/** Connection-level session filesystem config, set via constructor option. */
|
|
292
270
|
sessionFsConfig = null;
|
|
@@ -453,7 +431,12 @@ class CopilotClient {
|
|
|
453
431
|
const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
|
|
454
432
|
const effectiveEnv = connEnv ?? options.env ?? process.env;
|
|
455
433
|
this.resolvedEnv = effectiveEnv;
|
|
456
|
-
|
|
434
|
+
if (conn.kind === "stdio" || conn.kind === "tcp") {
|
|
435
|
+
const explicitCliPath = conn.path ?? effectiveEnv.COPILOT_CLI_PATH;
|
|
436
|
+
if (explicitCliPath) {
|
|
437
|
+
this.resolvedCliPath = explicitCliPath;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
457
440
|
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
458
441
|
this.connectionExtraArgs = [...connArgs];
|
|
459
442
|
this.options = {
|
|
@@ -466,7 +449,8 @@ class CopilotClient {
|
|
|
466
449
|
baseDirectory: options.baseDirectory,
|
|
467
450
|
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
468
451
|
enableRemoteSessions: options.enableRemoteSessions ?? false,
|
|
469
|
-
mode: options.mode ?? "copilot-cli"
|
|
452
|
+
mode: options.mode ?? "copilot-cli",
|
|
453
|
+
clientInfo: options.clientInfo
|
|
470
454
|
};
|
|
471
455
|
if (this.options.mode === "empty") {
|
|
472
456
|
const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
|
|
@@ -482,17 +466,29 @@ class CopilotClient {
|
|
|
482
466
|
connectionExtraArgs = [];
|
|
483
467
|
/**
|
|
484
468
|
* Parse CLI URL into host and port
|
|
485
|
-
* Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
|
|
469
|
+
* Supports formats: "host:port", "[ipv6]:port", "http://host:port", "https://host:port", or just "port"
|
|
486
470
|
*/
|
|
487
471
|
parseCliUrl(url) {
|
|
488
|
-
|
|
472
|
+
const cleanUrl = url.replace(/^https?:\/\//, "");
|
|
489
473
|
if (/^\d+$/.test(cleanUrl)) {
|
|
490
474
|
return { host: "localhost", port: parseInt(cleanUrl, 10) };
|
|
491
475
|
}
|
|
476
|
+
const ipv6Match = cleanUrl.match(/^\[([^\]]+)\]:(\d+)$/);
|
|
477
|
+
if (ipv6Match) {
|
|
478
|
+
const host2 = ipv6Match[1];
|
|
479
|
+
if (!(0, import_node_net.isIPv6)(host2)) {
|
|
480
|
+
throw new Error(`Invalid cliUrl format: ${url}`);
|
|
481
|
+
}
|
|
482
|
+
const port2 = parseInt(ipv6Match[2], 10);
|
|
483
|
+
if (isNaN(port2) || port2 <= 0 || port2 > 65535) {
|
|
484
|
+
throw new Error(`Invalid port in cliUrl: ${url}`);
|
|
485
|
+
}
|
|
486
|
+
return { host: host2, port: port2 };
|
|
487
|
+
}
|
|
492
488
|
const parts = cleanUrl.split(":");
|
|
493
489
|
if (parts.length !== 2) {
|
|
494
490
|
throw new Error(
|
|
495
|
-
`Invalid cliUrl format: ${url}. Expected "host:port", "http://host:port", or "port"`
|
|
491
|
+
`Invalid cliUrl format: ${url}. Expected "host:port", "[ipv6]:port", "http://host:port", or "port"`
|
|
496
492
|
);
|
|
497
493
|
}
|
|
498
494
|
const host = parts[0] || "localhost";
|
|
@@ -621,6 +617,8 @@ class CopilotClient {
|
|
|
621
617
|
if (this.state === "connected") {
|
|
622
618
|
return;
|
|
623
619
|
}
|
|
620
|
+
this.forceStopping = false;
|
|
621
|
+
this.processTransportError = null;
|
|
624
622
|
this.state = "connecting";
|
|
625
623
|
try {
|
|
626
624
|
if (this.connectionConfig.kind === "inprocess") {
|
|
@@ -653,8 +651,10 @@ class CopilotClient {
|
|
|
653
651
|
}
|
|
654
652
|
this.state = "connected";
|
|
655
653
|
} catch (error) {
|
|
654
|
+
const startupError = this.processTransportError ?? error;
|
|
655
|
+
await this.forceStop();
|
|
656
656
|
this.state = "error";
|
|
657
|
-
throw
|
|
657
|
+
throw startupError;
|
|
658
658
|
}
|
|
659
659
|
}
|
|
660
660
|
/**
|
|
@@ -699,7 +699,7 @@ class CopilotClient {
|
|
|
699
699
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
700
700
|
if (attempt < 3) {
|
|
701
701
|
const delay = 100 * Math.pow(2, attempt - 1);
|
|
702
|
-
await new Promise((
|
|
702
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
703
703
|
}
|
|
704
704
|
}
|
|
705
705
|
}
|
|
@@ -766,8 +766,8 @@ class CopilotClient {
|
|
|
766
766
|
this.socket = null;
|
|
767
767
|
try {
|
|
768
768
|
if (!socket.destroyed) {
|
|
769
|
-
await new Promise((
|
|
770
|
-
socket.once("close", () =>
|
|
769
|
+
await new Promise((resolve2) => {
|
|
770
|
+
socket.once("close", () => resolve2());
|
|
771
771
|
socket.end();
|
|
772
772
|
});
|
|
773
773
|
}
|
|
@@ -1247,6 +1247,7 @@ class CopilotClient {
|
|
|
1247
1247
|
gitHubTokenProviderRegistrationId,
|
|
1248
1248
|
remoteSession: config.remoteSession,
|
|
1249
1249
|
cloud: config.cloud,
|
|
1250
|
+
featureFlags: config.featureFlags,
|
|
1250
1251
|
expAssignments: config.expAssignments,
|
|
1251
1252
|
enableManagedSettings: config.enableManagedSettings,
|
|
1252
1253
|
managedSettings: config.managedSettings
|
|
@@ -1482,6 +1483,7 @@ class CopilotClient {
|
|
|
1482
1483
|
gitHubTokenProviderRegistrationId,
|
|
1483
1484
|
remoteSession: config.remoteSession,
|
|
1484
1485
|
openCanvases: config.openCanvases,
|
|
1486
|
+
featureFlags: config.featureFlags,
|
|
1485
1487
|
expAssignments: config.expAssignments,
|
|
1486
1488
|
enableManagedSettings: config.enableManagedSettings,
|
|
1487
1489
|
managedSettings: config.managedSettings,
|
|
@@ -1573,8 +1575,8 @@ class CopilotClient {
|
|
|
1573
1575
|
async listModels() {
|
|
1574
1576
|
await this.modelsCacheLock;
|
|
1575
1577
|
let resolveLock;
|
|
1576
|
-
this.modelsCacheLock = new Promise((
|
|
1577
|
-
resolveLock =
|
|
1578
|
+
this.modelsCacheLock = new Promise((resolve2) => {
|
|
1579
|
+
resolveLock = resolve2;
|
|
1578
1580
|
});
|
|
1579
1581
|
try {
|
|
1580
1582
|
if (this.modelsCache !== null) {
|
|
@@ -1630,6 +1632,10 @@ class CopilotClient {
|
|
|
1630
1632
|
if (this.onGitHubTelemetry != null) {
|
|
1631
1633
|
connectParams.enableGitHubTelemetryForwarding = true;
|
|
1632
1634
|
}
|
|
1635
|
+
const clientInfo = clientInfoToWire(this.options.clientInfo);
|
|
1636
|
+
if (clientInfo != null) {
|
|
1637
|
+
connectParams.clientInfo = clientInfo;
|
|
1638
|
+
}
|
|
1633
1639
|
const result = await raceAgainstExit(this.internalRpc.connect(connectParams));
|
|
1634
1640
|
serverVersion = result.protocolVersion;
|
|
1635
1641
|
} catch (err) {
|
|
@@ -1890,7 +1896,8 @@ class CopilotClient {
|
|
|
1890
1896
|
* Start the CLI server process
|
|
1891
1897
|
*/
|
|
1892
1898
|
async startCLIServer() {
|
|
1893
|
-
|
|
1899
|
+
this.resolvedCliPath ??= await getBundledRuntimePath();
|
|
1900
|
+
return new Promise((resolve2, reject) => {
|
|
1894
1901
|
this.stderrBuffer = "";
|
|
1895
1902
|
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1896
1903
|
if (this.options.logLevel) {
|
|
@@ -1927,7 +1934,7 @@ class CopilotClient {
|
|
|
1927
1934
|
}
|
|
1928
1935
|
if (!(0, import_node_fs.existsSync)(this.resolvedCliPath)) {
|
|
1929
1936
|
throw new Error(
|
|
1930
|
-
`Copilot CLI not found at ${this.resolvedCliPath}.
|
|
1937
|
+
`Copilot CLI not found at ${this.resolvedCliPath}. Set COPILOT_CLI_PATH to use a custom installation.`
|
|
1931
1938
|
);
|
|
1932
1939
|
}
|
|
1933
1940
|
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
@@ -1951,7 +1958,7 @@ class CopilotClient {
|
|
|
1951
1958
|
let resolved = false;
|
|
1952
1959
|
if (this.connectionConfig.kind === "stdio") {
|
|
1953
1960
|
resolved = true;
|
|
1954
|
-
|
|
1961
|
+
resolve2();
|
|
1955
1962
|
} else {
|
|
1956
1963
|
this.cliProcess.stdout?.on("data", (data) => {
|
|
1957
1964
|
stdout += data.toString();
|
|
@@ -1959,7 +1966,7 @@ class CopilotClient {
|
|
|
1959
1966
|
if (match && !resolved) {
|
|
1960
1967
|
this.runtimePort = parseInt(match[1], 10);
|
|
1961
1968
|
resolved = true;
|
|
1962
|
-
|
|
1969
|
+
resolve2();
|
|
1963
1970
|
}
|
|
1964
1971
|
});
|
|
1965
1972
|
}
|
|
@@ -1991,26 +1998,27 @@ stderr: ${stderrOutput}`
|
|
|
1991
1998
|
});
|
|
1992
1999
|
this.processExitPromise = new Promise((_, rejectProcessExit) => {
|
|
1993
2000
|
this.cliProcess.on("exit", (code) => {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2001
|
+
if (this.messageWriter) {
|
|
2002
|
+
this.messageWriter.suppressWriteErrors = true;
|
|
2003
|
+
}
|
|
2004
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
2005
|
+
if (stderrOutput) {
|
|
2006
|
+
rejectProcessExit(
|
|
2007
|
+
new Error(
|
|
2008
|
+
`CLI server exited with code ${code}
|
|
2000
2009
|
stderr: ${stderrOutput}`
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
}, 50);
|
|
2010
|
+
)
|
|
2011
|
+
);
|
|
2012
|
+
} else {
|
|
2013
|
+
rejectProcessExit(
|
|
2014
|
+
new Error(`CLI server exited unexpectedly with code ${code}`)
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2009
2017
|
});
|
|
2010
2018
|
});
|
|
2011
2019
|
this.processExitPromise.catch(() => {
|
|
2012
2020
|
});
|
|
2013
|
-
this.cliProcess.on("
|
|
2021
|
+
this.cliProcess.on("close", (code) => {
|
|
2014
2022
|
if (!resolved) {
|
|
2015
2023
|
resolved = true;
|
|
2016
2024
|
const stderrOutput = this.stderrBuffer.trim();
|
|
@@ -2052,7 +2060,20 @@ stderr: ${stderrOutput}`
|
|
|
2052
2060
|
}
|
|
2053
2061
|
/** Starts the in-process FFI runtime with SDK-managed typed options. */
|
|
2054
2062
|
async startInProcessFfi() {
|
|
2055
|
-
const
|
|
2063
|
+
const explicitEntrypoint = this.resolvedEnv.COPILOT_CLI_PATH;
|
|
2064
|
+
let runtimeLibrary;
|
|
2065
|
+
if (explicitEntrypoint) {
|
|
2066
|
+
const entrypointDirectory = (0, import_node_path.dirname)((0, import_node_path.resolve)(explicitEntrypoint));
|
|
2067
|
+
const adjacentRuntime = (0, import_node_path.join)(entrypointDirectory, "runtime.node");
|
|
2068
|
+
runtimeLibrary = (0, import_node_fs.existsSync)(adjacentRuntime) ? adjacentRuntime : (0, import_node_path.join)(
|
|
2069
|
+
entrypointDirectory,
|
|
2070
|
+
"prebuilds",
|
|
2071
|
+
CopilotClient.getNapiPrebuildsFolder(explicitEntrypoint),
|
|
2072
|
+
"runtime.node"
|
|
2073
|
+
);
|
|
2074
|
+
} else {
|
|
2075
|
+
runtimeLibrary = (0, import_node_path.join)((0, import_node_path.dirname)(await getBundledRuntimePath()), "runtime.node");
|
|
2076
|
+
}
|
|
2056
2077
|
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
2057
2078
|
const environment = {};
|
|
2058
2079
|
if (this.options.gitHubToken) {
|
|
@@ -2080,12 +2101,7 @@ stderr: ${stderrOutput}`
|
|
|
2080
2101
|
if (this.options.enableRemoteSessions) {
|
|
2081
2102
|
args.push("--remote");
|
|
2082
2103
|
}
|
|
2083
|
-
const host = FfiRuntimeHost.create(
|
|
2084
|
-
entrypoint,
|
|
2085
|
-
CopilotClient.getNapiPrebuildsFolder(entrypoint),
|
|
2086
|
-
environment,
|
|
2087
|
-
args
|
|
2088
|
-
);
|
|
2104
|
+
const host = FfiRuntimeHost.create(runtimeLibrary, explicitEntrypoint, environment, args);
|
|
2089
2105
|
this.ffiHost = host;
|
|
2090
2106
|
await host.start();
|
|
2091
2107
|
}
|
|
@@ -2105,19 +2121,6 @@ stderr: ${stderrOutput}`
|
|
|
2105
2121
|
this.attachConnectionHandlers();
|
|
2106
2122
|
this.connection.listen();
|
|
2107
2123
|
}
|
|
2108
|
-
/**
|
|
2109
|
-
* Resolves the CLI entrypoint used for in-process FFI hosting: `COPILOT_CLI_PATH`
|
|
2110
|
-
* when set, otherwise the bundled platform-package entrypoint.
|
|
2111
|
-
*/
|
|
2112
|
-
resolveCliPathForFfi() {
|
|
2113
|
-
return this.resolvedEnv.COPILOT_CLI_PATH ?? getBundledCliPath();
|
|
2114
|
-
}
|
|
2115
|
-
/**
|
|
2116
|
-
* Returns the napi prebuilds folder name for the current host — the
|
|
2117
|
-
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
2118
|
-
* `linux-x64`, `linuxmusl-x64`) under which the runtime ships
|
|
2119
|
-
* `prebuilds/<folder>/runtime.node`.
|
|
2120
|
-
*/
|
|
2121
2124
|
static getNapiPrebuildsFolder(entrypoint) {
|
|
2122
2125
|
const arch = process.arch;
|
|
2123
2126
|
if (arch !== "x64" && arch !== "arm64") {
|
|
@@ -2153,6 +2156,11 @@ stderr: ${stderrOutput}`
|
|
|
2153
2156
|
}
|
|
2154
2157
|
this.state = "error";
|
|
2155
2158
|
const reason = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
2159
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
2160
|
+
this.processTransportError = new Error(
|
|
2161
|
+
`CLI server connection failed: ${reason}${stderrOutput ? `
|
|
2162
|
+
stderr: ${stderrOutput}` : ""}`
|
|
2163
|
+
);
|
|
2156
2164
|
this.logDebug(`stdin pipe error: ${reason}`);
|
|
2157
2165
|
try {
|
|
2158
2166
|
this.connection?.dispose();
|
|
@@ -2189,7 +2197,7 @@ stderr: ${stderrOutput}`
|
|
|
2189
2197
|
if (!this.runtimePort) {
|
|
2190
2198
|
throw new Error("Server port not available");
|
|
2191
2199
|
}
|
|
2192
|
-
return new Promise((
|
|
2200
|
+
return new Promise((resolve2, reject) => {
|
|
2193
2201
|
this.socket = new import_node_net.Socket();
|
|
2194
2202
|
const connectionTimeout = setTimeout(() => {
|
|
2195
2203
|
this.socket?.destroy();
|
|
@@ -2204,7 +2212,7 @@ stderr: ${stderrOutput}`
|
|
|
2204
2212
|
);
|
|
2205
2213
|
this.attachConnectionHandlers();
|
|
2206
2214
|
this.connection.listen();
|
|
2207
|
-
|
|
2215
|
+
resolve2();
|
|
2208
2216
|
});
|
|
2209
2217
|
this.socket.on("error", (error) => {
|
|
2210
2218
|
clearTimeout(connectionTimeout);
|
|
@@ -85,7 +85,7 @@ function loadLibrary(libraryPath) {
|
|
|
85
85
|
return loadedLibrary;
|
|
86
86
|
}
|
|
87
87
|
function buildArgvJson(cliEntrypoint, args) {
|
|
88
|
-
const argv = cliEntrypoint.toLowerCase().endsWith(".js") ? ["node", cliEntrypoint, "--embedded-host", "--no-auto-update"] : [cliEntrypoint, "--embedded-host", "--no-auto-update"];
|
|
88
|
+
const argv = cliEntrypoint ? cliEntrypoint.toLowerCase().endsWith(".js") ? ["node", cliEntrypoint, "--embedded-host", "--no-auto-update"] : [cliEntrypoint, "--embedded-host", "--no-auto-update"] : [];
|
|
89
89
|
argv.push(...args);
|
|
90
90
|
return Buffer.from(JSON.stringify(argv), "utf8");
|
|
91
91
|
}
|
|
@@ -140,24 +140,21 @@ class FfiRuntimeHost {
|
|
|
140
140
|
/** The stream JSON-RPC writes client→server frames to. */
|
|
141
141
|
sendStream;
|
|
142
142
|
/**
|
|
143
|
-
*
|
|
144
|
-
* The cdylib is resolved as `prebuilds/<prebuildsFolder>/runtime.node` relative to
|
|
145
|
-
* the entrypoint directory (the napi-rs `<node-platform>-<arch>` layout, e.g.
|
|
146
|
-
* `linux-x64`). Throws if it cannot be found.
|
|
143
|
+
* Loads the runtime cdylib at the given path and prepares the FFI host.
|
|
147
144
|
*/
|
|
148
|
-
static create(
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (!(0, import_node_fs.existsSync)(libraryPath)) {
|
|
153
|
-
throw new Error(`FFI runtime library not found. Looked for '${libraryPath}'.`);
|
|
145
|
+
static create(libraryPath, cliEntrypoint, environment, args) {
|
|
146
|
+
const fullLibraryPath = (0, import_node_path.resolve)(libraryPath);
|
|
147
|
+
if (!(0, import_node_fs.existsSync)(fullLibraryPath)) {
|
|
148
|
+
throw new Error(`FFI runtime library not found at '${fullLibraryPath}'.`);
|
|
154
149
|
}
|
|
155
|
-
return new FfiRuntimeHost(
|
|
150
|
+
return new FfiRuntimeHost(
|
|
151
|
+
fullLibraryPath,
|
|
152
|
+
cliEntrypoint ? (0, import_node_path.resolve)(cliEntrypoint) : void 0,
|
|
153
|
+
environment,
|
|
154
|
+
args
|
|
155
|
+
);
|
|
156
156
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Starts the in-process runtime: spawns the CLI worker via the native host,
|
|
159
|
-
* waits for readiness, and opens the FFI JSON-RPC connection.
|
|
160
|
-
*/
|
|
157
|
+
/** Starts the in-process Rust runtime and opens the FFI JSON-RPC connection. */
|
|
161
158
|
async start() {
|
|
162
159
|
const argvJson = buildArgvJson(this.cliEntrypoint, this.args);
|
|
163
160
|
const envJson = buildEnvJson(this.environment);
|
|
@@ -177,9 +174,7 @@ class FfiRuntimeHost {
|
|
|
177
174
|
);
|
|
178
175
|
});
|
|
179
176
|
if (!this.serverId) {
|
|
180
|
-
throw new Error(
|
|
181
|
-
`copilot_runtime_host_start failed (library '${this.libraryPath}', entrypoint '${this.cliEntrypoint}').`
|
|
182
|
-
);
|
|
177
|
+
throw new Error(`copilot_runtime_host_start failed (library '${this.libraryPath}').`);
|
|
183
178
|
}
|
|
184
179
|
this.outboundCallback = import_koffi.default.register(
|
|
185
180
|
(_userData, bytesPtr, bytesLen) => this.feedInbound(bytesPtr, bytesLen),
|
|
@@ -39,6 +39,17 @@ function createServerRpc(connection) {
|
|
|
39
39
|
*/
|
|
40
40
|
ping: async (params) => connection.sendRequest("ping", params),
|
|
41
41
|
/** @experimental */
|
|
42
|
+
hooks: {
|
|
43
|
+
/**
|
|
44
|
+
* Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources.
|
|
45
|
+
*
|
|
46
|
+
* @param params Optional project paths and host-exclusion behavior for server-scoped hook discovery.
|
|
47
|
+
*
|
|
48
|
+
* @returns Server-discovered hook actions and partial-load diagnostics from user, repository, plugin, and managed-policy sources. Concrete sessions may include additional session-specific hook sources.
|
|
49
|
+
*/
|
|
50
|
+
discover: async (params) => connection.sendRequest("hooks.discover", params)
|
|
51
|
+
},
|
|
52
|
+
/** @experimental */
|
|
42
53
|
models: {
|
|
43
54
|
/**
|
|
44
55
|
* Lists Copilot models available to the authenticated user.
|
|
@@ -506,6 +517,14 @@ function createServerRpc(connection) {
|
|
|
506
517
|
* @returns Sessions matching the filter, ordered most-recently-modified first.
|
|
507
518
|
*/
|
|
508
519
|
list: async (params) => connection.sendRequest("sessions.list", params),
|
|
520
|
+
/**
|
|
521
|
+
* Reads a page of durable events directly from a local session's persisted journal without creating, resuming, or activating the session. The initial backward read uses a bounded tail scan for fast first paint; cursor continuations preserve the session event-log paging semantics. Persisted events may omit payloads that are reconstructed only for an active session.
|
|
522
|
+
*
|
|
523
|
+
* @param params Pagination options for reading an inactive or active local session's persisted event journal.
|
|
524
|
+
*
|
|
525
|
+
* @returns Batch of session events returned by a read, with cursor and continuation metadata.
|
|
526
|
+
*/
|
|
527
|
+
readPersistedEvents: async (params) => connection.sendRequest("sessions.readPersistedEvents", params),
|
|
509
528
|
/**
|
|
510
529
|
* Finds the local session bound to a GitHub task ID, if any.
|
|
511
530
|
*
|