@github/copilot-sdk 1.0.13-preview.2 → 1.0.13-preview.4
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 +8 -2
- package/dist/cjs/client.js +77 -54
- package/dist/cjs/ffiRuntimeHost.js +14 -19
- package/dist/cjs/generated/rpc.js +10 -1
- package/dist/cjs/runtimeArtifacts.js +159 -0
- package/dist/cjs/session.js +13 -4
- package/dist/client.d.ts +1 -11
- package/dist/client.js +78 -55
- package/dist/extension.d.ts +1 -1
- package/dist/factory.d.ts +35 -1
- package/dist/ffiRuntimeHost.d.ts +3 -9
- package/dist/ffiRuntimeHost.js +15 -20
- package/dist/generated/rpc.d.ts +37 -2
- package/dist/generated/rpc.js +10 -1
- package/dist/generated/session-events.d.ts +28 -1
- package/dist/index.d.ts +2 -2
- package/dist/runtimeArtifacts.d.ts +6 -0
- package/dist/runtimeArtifacts.js +145 -0
- package/dist/session.js +13 -4
- package/dist/types.d.ts +13 -2
- package/docs/factories.md +25 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -95,6 +95,7 @@ new CopilotClient(options?: CopilotClientOptions)
|
|
|
95
95
|
- `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
96
|
- `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
97
|
- 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).
|
|
98
|
+
- 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
99
|
- `mode?: "empty" | "copilot-cli"` - Defaulting strategy. Use `"empty"` for multi-user server mode; defaults to `"copilot-cli"`.
|
|
99
100
|
- `workingDirectory?: string` - Working directory for the runtime process (default: current process cwd).
|
|
100
101
|
- `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`.
|
|
@@ -140,7 +141,8 @@ Create a new conversation session.
|
|
|
140
141
|
- `gitHubTokenProvider?: GitHubTokenProvider` - Acquires rotating, session-scoped GitHub tokens. Token results require a positive `expiresIn` value in seconds remaining when the callback completes; production tokens typically last eight hours. Cannot be combined with `gitHubToken`.
|
|
141
142
|
- `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section.
|
|
142
143
|
- `onPermissionRequest?: PermissionHandler` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `approveAll` approves requests when managed settings are disabled and throws when `enableManagedSettings` is true. Custom handlers can inspect `managedApprovalRequired` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section.
|
|
143
|
-
- `onUserInputRequest?: UserInputHandler` - Handler for
|
|
144
|
+
- `onUserInputRequest?: UserInputHandler` - Handler for legacy question-and-answer requests from the agent. Enables the legacy `ask_user` tool. See [User Input Requests](#user-input-requests) section.
|
|
145
|
+
- `askUserVariant?: "legacy" | "elicitation"` - Selects the model-facing `ask_user` tool shape when creating or cold-resuming a session. Defaults to `"legacy"`; use `"elicitation"` with `onElicitationRequest`.
|
|
144
146
|
- `onElicitationRequest?: ElicitationHandler` - Handler for elicitation requests dispatched by the server. Enables this client to present form-based UI dialogs on behalf of the agent or other session participants. See [Elicitation Requests](#elicitation-requests) section.
|
|
145
147
|
- `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section.
|
|
146
148
|
|
|
@@ -959,7 +961,7 @@ To let a specific custom tool bypass the permission prompt entirely, set `skipPe
|
|
|
959
961
|
|
|
960
962
|
## User Input Requests
|
|
961
963
|
|
|
962
|
-
Enable the
|
|
964
|
+
Enable the legacy question-and-answer `ask_user` tool by providing an `onUserInputRequest` handler:
|
|
963
965
|
|
|
964
966
|
```typescript
|
|
965
967
|
const session = await client.createSession({
|
|
@@ -991,6 +993,7 @@ Register an `onElicitationRequest` handler to let your client act as an elicitat
|
|
|
991
993
|
const session = await client.createSession({
|
|
992
994
|
model: "gpt-5",
|
|
993
995
|
onPermissionRequest: approveAll,
|
|
996
|
+
askUserVariant: "elicitation",
|
|
994
997
|
onElicitationRequest: async (context) => {
|
|
995
998
|
// context.sessionId - Session that triggered the request
|
|
996
999
|
// context.message - Description of what information is needed
|
|
@@ -1012,6 +1015,9 @@ const session = await client.createSession({
|
|
|
1012
1015
|
console.log(session.capabilities.ui?.elicitation); // true
|
|
1013
1016
|
```
|
|
1014
1017
|
|
|
1018
|
+
Set `askUserVariant: "elicitation"` to expose the structured form as the model's
|
|
1019
|
+
`ask_user` tool. Omit it to retain the legacy SDK behavior.
|
|
1020
|
+
|
|
1015
1021
|
When `onElicitationRequest` is provided, the SDK sends `requestElicitation: true` during session create/resume, which enables `session.capabilities.ui.elicitation` on the session.
|
|
1016
1022
|
|
|
1017
1023
|
In multi-client scenarios:
|
package/dist/cjs/client.js
CHANGED
|
@@ -42,6 +42,7 @@ var import_node = require("vscode-jsonrpc/node.js");
|
|
|
42
42
|
var import_rpc = require("./generated/rpc.js");
|
|
43
43
|
var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
|
|
44
44
|
var import_session = require("./session.js");
|
|
45
|
+
var import_runtimeArtifacts = require("./runtimeArtifacts.js");
|
|
45
46
|
var import_sessionFsProvider = require("./sessionFsProvider.js");
|
|
46
47
|
var import_copilotRequestHandler = require("./copilotRequestHandler.js");
|
|
47
48
|
var import_telemetry = require("./telemetry.js");
|
|
@@ -72,7 +73,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
72
73
|
if (child.exitCode != null || child.signalCode != null) {
|
|
73
74
|
return true;
|
|
74
75
|
}
|
|
75
|
-
return new Promise((
|
|
76
|
+
return new Promise((resolve2) => {
|
|
76
77
|
let timeout;
|
|
77
78
|
let settled = false;
|
|
78
79
|
const onExit = () => {
|
|
@@ -81,7 +82,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
81
82
|
}
|
|
82
83
|
settled = true;
|
|
83
84
|
clearTimeout(timeout);
|
|
84
|
-
|
|
85
|
+
resolve2(true);
|
|
85
86
|
};
|
|
86
87
|
timeout = setTimeout(() => {
|
|
87
88
|
if (settled) {
|
|
@@ -89,7 +90,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
89
90
|
}
|
|
90
91
|
settled = true;
|
|
91
92
|
child.off("exit", onExit);
|
|
92
|
-
|
|
93
|
+
resolve2(false);
|
|
93
94
|
}, timeoutMs);
|
|
94
95
|
child.once("exit", onExit);
|
|
95
96
|
if (child.exitCode != null || child.signalCode != null) {
|
|
@@ -212,14 +213,17 @@ function getCliPlatformPackageNames() {
|
|
|
212
213
|
const variants = process.platform === "linux" ? ["linux", "linuxmusl"] : [process.platform];
|
|
213
214
|
return variants.map((variant) => `@github/copilot-${variant}-${arch}`);
|
|
214
215
|
}
|
|
215
|
-
function
|
|
216
|
+
function getBundledCliPackage() {
|
|
216
217
|
const packageNames = getCliPlatformPackageNames();
|
|
217
218
|
if (typeof import_meta.resolve === "function") {
|
|
218
219
|
for (const packageName of packageNames) {
|
|
219
220
|
try {
|
|
220
221
|
const packageEntryUrl = import_meta.resolve(packageName);
|
|
221
222
|
const packageEntryPath = (0, import_node_url.fileURLToPath)(packageEntryUrl);
|
|
222
|
-
return
|
|
223
|
+
return {
|
|
224
|
+
root: (0, import_node_path.dirname)(packageEntryPath),
|
|
225
|
+
platform: packageName.slice("@github/copilot-".length)
|
|
226
|
+
};
|
|
223
227
|
} catch {
|
|
224
228
|
}
|
|
225
229
|
}
|
|
@@ -231,9 +235,13 @@ function getBundledCliPath() {
|
|
|
231
235
|
const searchPaths = req.resolve.paths("@github/copilot") ?? [];
|
|
232
236
|
for (const base of searchPaths) {
|
|
233
237
|
for (const packageName of packageNames) {
|
|
234
|
-
const
|
|
238
|
+
const root = (0, import_node_path.join)(base, ...packageName.split("/"));
|
|
239
|
+
const candidate = (0, import_node_path.join)(root, "index.js");
|
|
235
240
|
if ((0, import_node_fs.existsSync)(candidate)) {
|
|
236
|
-
return
|
|
241
|
+
return {
|
|
242
|
+
root,
|
|
243
|
+
platform: packageName.slice("@github/copilot-".length)
|
|
244
|
+
};
|
|
237
245
|
}
|
|
238
246
|
}
|
|
239
247
|
}
|
|
@@ -241,6 +249,13 @@ function getBundledCliPath() {
|
|
|
241
249
|
`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
250
|
);
|
|
243
251
|
}
|
|
252
|
+
function getBundledRuntimePath() {
|
|
253
|
+
const bundled = getBundledCliPackage();
|
|
254
|
+
return (0, import_runtimeArtifacts.materializeRuntimeBundle)({
|
|
255
|
+
packageRoot: bundled.root,
|
|
256
|
+
platform: bundled.platform
|
|
257
|
+
});
|
|
258
|
+
}
|
|
244
259
|
class TeardownResilientStreamMessageWriter extends import_node.StreamMessageWriter {
|
|
245
260
|
suppressWriteErrors = false;
|
|
246
261
|
async write(msg) {
|
|
@@ -287,6 +302,7 @@ class CopilotClient {
|
|
|
287
302
|
_internalRpc = null;
|
|
288
303
|
processExitPromise = null;
|
|
289
304
|
// Rejects when CLI process exits
|
|
305
|
+
processTransportError = null;
|
|
290
306
|
negotiatedProtocolVersion = null;
|
|
291
307
|
/** Connection-level session filesystem config, set via constructor option. */
|
|
292
308
|
sessionFsConfig = null;
|
|
@@ -453,7 +469,14 @@ class CopilotClient {
|
|
|
453
469
|
const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
|
|
454
470
|
const effectiveEnv = connEnv ?? options.env ?? process.env;
|
|
455
471
|
this.resolvedEnv = effectiveEnv;
|
|
456
|
-
|
|
472
|
+
if (conn.kind === "stdio" || conn.kind === "tcp") {
|
|
473
|
+
const explicitCliPath = conn.path ?? effectiveEnv.COPILOT_CLI_PATH;
|
|
474
|
+
if (explicitCliPath) {
|
|
475
|
+
this.resolvedCliPath = explicitCliPath;
|
|
476
|
+
} else {
|
|
477
|
+
this.resolvedCliPath = getBundledRuntimePath();
|
|
478
|
+
}
|
|
479
|
+
}
|
|
457
480
|
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
458
481
|
this.connectionExtraArgs = [...connArgs];
|
|
459
482
|
this.options = {
|
|
@@ -621,6 +644,8 @@ class CopilotClient {
|
|
|
621
644
|
if (this.state === "connected") {
|
|
622
645
|
return;
|
|
623
646
|
}
|
|
647
|
+
this.forceStopping = false;
|
|
648
|
+
this.processTransportError = null;
|
|
624
649
|
this.state = "connecting";
|
|
625
650
|
try {
|
|
626
651
|
if (this.connectionConfig.kind === "inprocess") {
|
|
@@ -653,8 +678,10 @@ class CopilotClient {
|
|
|
653
678
|
}
|
|
654
679
|
this.state = "connected";
|
|
655
680
|
} catch (error) {
|
|
681
|
+
const startupError = this.processTransportError ?? error;
|
|
682
|
+
await this.forceStop();
|
|
656
683
|
this.state = "error";
|
|
657
|
-
throw
|
|
684
|
+
throw startupError;
|
|
658
685
|
}
|
|
659
686
|
}
|
|
660
687
|
/**
|
|
@@ -699,7 +726,7 @@ class CopilotClient {
|
|
|
699
726
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
700
727
|
if (attempt < 3) {
|
|
701
728
|
const delay = 100 * Math.pow(2, attempt - 1);
|
|
702
|
-
await new Promise((
|
|
729
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
703
730
|
}
|
|
704
731
|
}
|
|
705
732
|
}
|
|
@@ -766,8 +793,8 @@ class CopilotClient {
|
|
|
766
793
|
this.socket = null;
|
|
767
794
|
try {
|
|
768
795
|
if (!socket.destroyed) {
|
|
769
|
-
await new Promise((
|
|
770
|
-
socket.once("close", () =>
|
|
796
|
+
await new Promise((resolve2) => {
|
|
797
|
+
socket.once("close", () => resolve2());
|
|
771
798
|
socket.end();
|
|
772
799
|
});
|
|
773
800
|
}
|
|
@@ -1208,6 +1235,7 @@ class CopilotClient {
|
|
|
1208
1235
|
requestPermission: !!config.onPermissionRequest,
|
|
1209
1236
|
requestUserInput: !!config.onUserInputRequest,
|
|
1210
1237
|
requestElicitation: !!config.onElicitationRequest,
|
|
1238
|
+
askUserVariant: config.askUserVariant,
|
|
1211
1239
|
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
1212
1240
|
...config.githubMcpToolConfig != null ? { githubMcpToolConfig: config.githubMcpToolConfig } : {},
|
|
1213
1241
|
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
@@ -1440,6 +1468,7 @@ class CopilotClient {
|
|
|
1440
1468
|
requestPermission: config.onPermissionRequest !== import_types.defaultJoinSessionPermissionHandler,
|
|
1441
1469
|
requestUserInput: !!config.onUserInputRequest,
|
|
1442
1470
|
requestElicitation: !!config.onElicitationRequest,
|
|
1471
|
+
askUserVariant: config.askUserVariant,
|
|
1443
1472
|
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
1444
1473
|
...config.githubMcpToolConfig != null ? { githubMcpToolConfig: config.githubMcpToolConfig } : {},
|
|
1445
1474
|
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
@@ -1571,8 +1600,8 @@ class CopilotClient {
|
|
|
1571
1600
|
async listModels() {
|
|
1572
1601
|
await this.modelsCacheLock;
|
|
1573
1602
|
let resolveLock;
|
|
1574
|
-
this.modelsCacheLock = new Promise((
|
|
1575
|
-
resolveLock =
|
|
1603
|
+
this.modelsCacheLock = new Promise((resolve2) => {
|
|
1604
|
+
resolveLock = resolve2;
|
|
1576
1605
|
});
|
|
1577
1606
|
try {
|
|
1578
1607
|
if (this.modelsCache !== null) {
|
|
@@ -1888,7 +1917,7 @@ class CopilotClient {
|
|
|
1888
1917
|
* Start the CLI server process
|
|
1889
1918
|
*/
|
|
1890
1919
|
async startCLIServer() {
|
|
1891
|
-
return new Promise((
|
|
1920
|
+
return new Promise((resolve2, reject) => {
|
|
1892
1921
|
this.stderrBuffer = "";
|
|
1893
1922
|
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1894
1923
|
if (this.options.logLevel) {
|
|
@@ -1949,7 +1978,7 @@ class CopilotClient {
|
|
|
1949
1978
|
let resolved = false;
|
|
1950
1979
|
if (this.connectionConfig.kind === "stdio") {
|
|
1951
1980
|
resolved = true;
|
|
1952
|
-
|
|
1981
|
+
resolve2();
|
|
1953
1982
|
} else {
|
|
1954
1983
|
this.cliProcess.stdout?.on("data", (data) => {
|
|
1955
1984
|
stdout += data.toString();
|
|
@@ -1957,7 +1986,7 @@ class CopilotClient {
|
|
|
1957
1986
|
if (match && !resolved) {
|
|
1958
1987
|
this.runtimePort = parseInt(match[1], 10);
|
|
1959
1988
|
resolved = true;
|
|
1960
|
-
|
|
1989
|
+
resolve2();
|
|
1961
1990
|
}
|
|
1962
1991
|
});
|
|
1963
1992
|
}
|
|
@@ -1989,26 +2018,27 @@ stderr: ${stderrOutput}`
|
|
|
1989
2018
|
});
|
|
1990
2019
|
this.processExitPromise = new Promise((_, rejectProcessExit) => {
|
|
1991
2020
|
this.cliProcess.on("exit", (code) => {
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2021
|
+
if (this.messageWriter) {
|
|
2022
|
+
this.messageWriter.suppressWriteErrors = true;
|
|
2023
|
+
}
|
|
2024
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
2025
|
+
if (stderrOutput) {
|
|
2026
|
+
rejectProcessExit(
|
|
2027
|
+
new Error(
|
|
2028
|
+
`CLI server exited with code ${code}
|
|
1998
2029
|
stderr: ${stderrOutput}`
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
}, 50);
|
|
2030
|
+
)
|
|
2031
|
+
);
|
|
2032
|
+
} else {
|
|
2033
|
+
rejectProcessExit(
|
|
2034
|
+
new Error(`CLI server exited unexpectedly with code ${code}`)
|
|
2035
|
+
);
|
|
2036
|
+
}
|
|
2007
2037
|
});
|
|
2008
2038
|
});
|
|
2009
2039
|
this.processExitPromise.catch(() => {
|
|
2010
2040
|
});
|
|
2011
|
-
this.cliProcess.on("
|
|
2041
|
+
this.cliProcess.on("close", (code) => {
|
|
2012
2042
|
if (!resolved) {
|
|
2013
2043
|
resolved = true;
|
|
2014
2044
|
const stderrOutput = this.stderrBuffer.trim();
|
|
@@ -2050,7 +2080,13 @@ stderr: ${stderrOutput}`
|
|
|
2050
2080
|
}
|
|
2051
2081
|
/** Starts the in-process FFI runtime with SDK-managed typed options. */
|
|
2052
2082
|
async startInProcessFfi() {
|
|
2053
|
-
const
|
|
2083
|
+
const explicitEntrypoint = this.resolvedEnv.COPILOT_CLI_PATH;
|
|
2084
|
+
const runtimeLibrary = explicitEntrypoint ? (0, import_node_path.join)(
|
|
2085
|
+
(0, import_node_path.dirname)((0, import_node_path.resolve)(explicitEntrypoint)),
|
|
2086
|
+
"prebuilds",
|
|
2087
|
+
CopilotClient.getNapiPrebuildsFolder(explicitEntrypoint),
|
|
2088
|
+
"runtime.node"
|
|
2089
|
+
) : (0, import_node_path.join)((0, import_node_path.dirname)(getBundledRuntimePath()), "runtime.node");
|
|
2054
2090
|
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
2055
2091
|
const environment = {};
|
|
2056
2092
|
if (this.options.gitHubToken) {
|
|
@@ -2078,12 +2114,7 @@ stderr: ${stderrOutput}`
|
|
|
2078
2114
|
if (this.options.enableRemoteSessions) {
|
|
2079
2115
|
args.push("--remote");
|
|
2080
2116
|
}
|
|
2081
|
-
const host = FfiRuntimeHost.create(
|
|
2082
|
-
entrypoint,
|
|
2083
|
-
CopilotClient.getNapiPrebuildsFolder(entrypoint),
|
|
2084
|
-
environment,
|
|
2085
|
-
args
|
|
2086
|
-
);
|
|
2117
|
+
const host = FfiRuntimeHost.create(runtimeLibrary, explicitEntrypoint, environment, args);
|
|
2087
2118
|
this.ffiHost = host;
|
|
2088
2119
|
await host.start();
|
|
2089
2120
|
}
|
|
@@ -2103,19 +2134,6 @@ stderr: ${stderrOutput}`
|
|
|
2103
2134
|
this.attachConnectionHandlers();
|
|
2104
2135
|
this.connection.listen();
|
|
2105
2136
|
}
|
|
2106
|
-
/**
|
|
2107
|
-
* Resolves the CLI entrypoint used for in-process FFI hosting: `COPILOT_CLI_PATH`
|
|
2108
|
-
* when set, otherwise the bundled platform-package entrypoint.
|
|
2109
|
-
*/
|
|
2110
|
-
resolveCliPathForFfi() {
|
|
2111
|
-
return this.resolvedEnv.COPILOT_CLI_PATH ?? getBundledCliPath();
|
|
2112
|
-
}
|
|
2113
|
-
/**
|
|
2114
|
-
* Returns the napi prebuilds folder name for the current host — the
|
|
2115
|
-
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
2116
|
-
* `linux-x64`, `linuxmusl-x64`) under which the runtime ships
|
|
2117
|
-
* `prebuilds/<folder>/runtime.node`.
|
|
2118
|
-
*/
|
|
2119
2137
|
static getNapiPrebuildsFolder(entrypoint) {
|
|
2120
2138
|
const arch = process.arch;
|
|
2121
2139
|
if (arch !== "x64" && arch !== "arm64") {
|
|
@@ -2151,6 +2169,11 @@ stderr: ${stderrOutput}`
|
|
|
2151
2169
|
}
|
|
2152
2170
|
this.state = "error";
|
|
2153
2171
|
const reason = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
2172
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
2173
|
+
this.processTransportError = new Error(
|
|
2174
|
+
`CLI server connection failed: ${reason}${stderrOutput ? `
|
|
2175
|
+
stderr: ${stderrOutput}` : ""}`
|
|
2176
|
+
);
|
|
2154
2177
|
this.logDebug(`stdin pipe error: ${reason}`);
|
|
2155
2178
|
try {
|
|
2156
2179
|
this.connection?.dispose();
|
|
@@ -2187,7 +2210,7 @@ stderr: ${stderrOutput}`
|
|
|
2187
2210
|
if (!this.runtimePort) {
|
|
2188
2211
|
throw new Error("Server port not available");
|
|
2189
2212
|
}
|
|
2190
|
-
return new Promise((
|
|
2213
|
+
return new Promise((resolve2, reject) => {
|
|
2191
2214
|
this.socket = new import_node_net.Socket();
|
|
2192
2215
|
const connectionTimeout = setTimeout(() => {
|
|
2193
2216
|
this.socket?.destroy();
|
|
@@ -2202,7 +2225,7 @@ stderr: ${stderrOutput}`
|
|
|
2202
2225
|
);
|
|
2203
2226
|
this.attachConnectionHandlers();
|
|
2204
2227
|
this.connection.listen();
|
|
2205
|
-
|
|
2228
|
+
resolve2();
|
|
2206
2229
|
});
|
|
2207
2230
|
this.socket.on("error", (error) => {
|
|
2208
2231
|
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),
|
|
@@ -200,7 +200,7 @@ function createServerRpc(connection) {
|
|
|
200
200
|
disable: async (params) => connection.sendRequest("extensions.disable", params)
|
|
201
201
|
},
|
|
202
202
|
/**
|
|
203
|
-
* Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime
|
|
203
|
+
* Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime uses its built-in extension launcher.
|
|
204
204
|
*
|
|
205
205
|
* @experimental
|
|
206
206
|
*/
|
|
@@ -773,6 +773,15 @@ function createSessionRpc(connection, sessionId) {
|
|
|
773
773
|
* @experimental
|
|
774
774
|
*/
|
|
775
775
|
sendMessages: async (params) => connection.sendRequest("session.sendMessages", { sessionId, ...params }),
|
|
776
|
+
/** @experimental */
|
|
777
|
+
sandbox: {
|
|
778
|
+
/**
|
|
779
|
+
* Returns whether managed policy requires sandbox enforcement and whether an enforcement failure has permanently blocked the session.
|
|
780
|
+
*
|
|
781
|
+
* @returns Managed sandbox enforcement state for a session.
|
|
782
|
+
*/
|
|
783
|
+
getEnforcementStatus: async () => connection.sendRequest("session.sandbox.getEnforcementStatus", { sessionId })
|
|
784
|
+
},
|
|
776
785
|
/**
|
|
777
786
|
* Aborts the current agent turn.
|
|
778
787
|
*
|
|
@@ -0,0 +1,159 @@
|
|
|
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 runtimeArtifacts_exports = {};
|
|
20
|
+
__export(runtimeArtifacts_exports, {
|
|
21
|
+
defaultRuntimeCacheRoot: () => defaultRuntimeCacheRoot,
|
|
22
|
+
materializeRuntimeBundle: () => materializeRuntimeBundle
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(runtimeArtifacts_exports);
|
|
25
|
+
var import_node_crypto = require("node:crypto");
|
|
26
|
+
var import_node_fs = require("node:fs");
|
|
27
|
+
var import_node_os = require("node:os");
|
|
28
|
+
var import_node_path = require("node:path");
|
|
29
|
+
const EXCLUDED_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
30
|
+
"app.js",
|
|
31
|
+
"assets",
|
|
32
|
+
"changelog.json",
|
|
33
|
+
"copilot",
|
|
34
|
+
"copilot.exe",
|
|
35
|
+
"copilot-sdk",
|
|
36
|
+
"foundry-local-sdk",
|
|
37
|
+
"index.js",
|
|
38
|
+
"LICENSE.md",
|
|
39
|
+
"napi-oop-runtime",
|
|
40
|
+
"npm-loader.js",
|
|
41
|
+
"package.json",
|
|
42
|
+
"preloads",
|
|
43
|
+
"pvrecorder",
|
|
44
|
+
"queries",
|
|
45
|
+
"README.md",
|
|
46
|
+
"sdk",
|
|
47
|
+
"sea-loader.js",
|
|
48
|
+
"webview"
|
|
49
|
+
]);
|
|
50
|
+
function validateFile(path, label) {
|
|
51
|
+
if (!(0, import_node_fs.existsSync)(path)) {
|
|
52
|
+
throw new Error(`${label} not found at ${path}.`);
|
|
53
|
+
}
|
|
54
|
+
if ((0, import_node_fs.statSync)(path).size === 0) {
|
|
55
|
+
throw new Error(`${label} at ${path} is empty.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function validateRuntimeBundle(wrapper, runtimeNode) {
|
|
59
|
+
validateFile(wrapper, "Copilot runtime wrapper");
|
|
60
|
+
validateFile(runtimeNode, "Copilot runtime.node");
|
|
61
|
+
}
|
|
62
|
+
function isExcluded(relativePath) {
|
|
63
|
+
const parts = relativePath.split(import_node_path.sep);
|
|
64
|
+
const topLevel = parts[0];
|
|
65
|
+
const fileName = parts.at(-1) ?? "";
|
|
66
|
+
return EXCLUDED_TOP_LEVEL.has(topLevel) || /^tree-sitter.*\.wasm$/.test(topLevel) || /^voice-.*\.js$/.test(topLevel) || fileName === "cli-native.node" || parts.includes("mediaremote-adapter") || fileName.startsWith("copilot-runtime-bin");
|
|
67
|
+
}
|
|
68
|
+
function collectRuntimeAssets(sources) {
|
|
69
|
+
const assets = [];
|
|
70
|
+
const visit = (directory) => {
|
|
71
|
+
for (const entry of (0, import_node_fs.readdirSync)(directory, { withFileTypes: true })) {
|
|
72
|
+
const source = (0, import_node_path.join)(directory, entry.name);
|
|
73
|
+
const sourceRelative = (0, import_node_path.relative)(sources.packageRoot, source);
|
|
74
|
+
if (isExcluded(sourceRelative)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (entry.isDirectory()) {
|
|
78
|
+
visit(source);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const parts = sourceRelative.split(import_node_path.sep);
|
|
85
|
+
let relativePath = sourceRelative;
|
|
86
|
+
if (parts[0] === "prebuilds") {
|
|
87
|
+
if (parts[1] !== sources.platform || parts.length < 3) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
relativePath = parts.slice(2).join(import_node_path.sep);
|
|
91
|
+
}
|
|
92
|
+
assets.push({ source, relativePath });
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
visit(sources.packageRoot);
|
|
96
|
+
return assets.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
97
|
+
}
|
|
98
|
+
function sourceFingerprint(assets) {
|
|
99
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
100
|
+
for (const asset of assets) {
|
|
101
|
+
const stat = (0, import_node_fs.lstatSync)(asset.source);
|
|
102
|
+
hash.update(asset.relativePath).update("\0");
|
|
103
|
+
hash.update(`${stat.size}:${stat.mtimeMs}`).update("\0");
|
|
104
|
+
}
|
|
105
|
+
return hash.digest("hex").slice(0, 20);
|
|
106
|
+
}
|
|
107
|
+
function makeExecutable(path) {
|
|
108
|
+
if (process.platform === "win32") {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const mode = (0, import_node_fs.statSync)(path).mode;
|
|
112
|
+
if ((mode & 73) === 0) {
|
|
113
|
+
(0, import_node_fs.chmodSync)(path, mode | 73);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function defaultRuntimeCacheRoot(platform = process.platform, home = (0, import_node_os.homedir)(), environment = process.env) {
|
|
117
|
+
const cacheDirectory = platform === "win32" ? environment.LOCALAPPDATA ?? (0, import_node_path.join)(home, "AppData", "Local") : platform === "darwin" ? (0, import_node_path.join)(home, "Library", "Caches") : environment.XDG_CACHE_HOME ?? (0, import_node_path.join)(home, ".cache");
|
|
118
|
+
return (0, import_node_path.join)(cacheDirectory, "github-copilot-sdk", "runtime");
|
|
119
|
+
}
|
|
120
|
+
function materializeRuntimeBundle(sources, cacheRoot = defaultRuntimeCacheRoot()) {
|
|
121
|
+
const assets = collectRuntimeAssets(sources);
|
|
122
|
+
const wrapperName = process.platform === "win32" ? "copilot-runtime.exe" : "copilot-runtime";
|
|
123
|
+
const sourceWrapper = assets.find((asset) => asset.relativePath === wrapperName)?.source;
|
|
124
|
+
const sourceRuntimeNode = assets.find((asset) => asset.relativePath === "runtime.node")?.source;
|
|
125
|
+
validateRuntimeBundle(sourceWrapper ?? "", sourceRuntimeNode ?? "");
|
|
126
|
+
const installDir = (0, import_node_path.join)(cacheRoot, `${sources.platform}-${sourceFingerprint(assets)}`);
|
|
127
|
+
const installedWrapper = (0, import_node_path.join)(installDir, wrapperName);
|
|
128
|
+
const installedRuntimeNode = (0, import_node_path.join)(installDir, "runtime.node");
|
|
129
|
+
if ((0, import_node_fs.existsSync)(installDir)) {
|
|
130
|
+
validateRuntimeBundle(installedWrapper, installedRuntimeNode);
|
|
131
|
+
makeExecutable(installedWrapper);
|
|
132
|
+
return installedWrapper;
|
|
133
|
+
}
|
|
134
|
+
(0, import_node_fs.mkdirSync)(cacheRoot, { recursive: true });
|
|
135
|
+
const stagingDir = (0, import_node_fs.mkdtempSync)((0, import_node_path.join)(cacheRoot, ".runtime-"));
|
|
136
|
+
try {
|
|
137
|
+
for (const asset of assets) {
|
|
138
|
+
const destination = (0, import_node_path.join)(stagingDir, asset.relativePath);
|
|
139
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(destination), { recursive: true });
|
|
140
|
+
(0, import_node_fs.copyFileSync)(asset.source, destination);
|
|
141
|
+
}
|
|
142
|
+
const stagedWrapper = (0, import_node_path.join)(stagingDir, wrapperName);
|
|
143
|
+
makeExecutable(stagedWrapper);
|
|
144
|
+
(0, import_node_fs.renameSync)(stagingDir, installDir);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (!(0, import_node_fs.existsSync)(installDir)) {
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
validateRuntimeBundle(installedWrapper, installedRuntimeNode);
|
|
150
|
+
} finally {
|
|
151
|
+
(0, import_node_fs.rmSync)(stagingDir, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
return installedWrapper;
|
|
154
|
+
}
|
|
155
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
156
|
+
0 && (module.exports = {
|
|
157
|
+
defaultRuntimeCacheRoot,
|
|
158
|
+
materializeRuntimeBundle
|
|
159
|
+
});
|
package/dist/cjs/session.js
CHANGED
|
@@ -283,14 +283,18 @@ class CopilotSession {
|
|
|
283
283
|
const name = typeof nameOrHandle === "string" ? nameOrHandle : (0, import_factory.getFactoryDefinition)(nameOrHandle).meta.name;
|
|
284
284
|
if (options?.resumeFromRunId !== void 0) {
|
|
285
285
|
return this.factory.resume(options.resumeFromRunId, {
|
|
286
|
-
limits: options.limits
|
|
286
|
+
limits: options.limits,
|
|
287
|
+
notifyOnComplete: options.notifyOnComplete,
|
|
288
|
+
logPhaseNames: options.logPhaseNames
|
|
287
289
|
});
|
|
288
290
|
}
|
|
289
291
|
const envelope = await this.rpc.factory.run({
|
|
290
292
|
name,
|
|
291
293
|
args: options?.args === void 0 ? {} : options.args,
|
|
292
294
|
options: {
|
|
293
|
-
limits: options?.limits
|
|
295
|
+
limits: options?.limits,
|
|
296
|
+
notifyOnComplete: options?.notifyOnComplete,
|
|
297
|
+
logPhaseNames: options?.logPhaseNames
|
|
294
298
|
}
|
|
295
299
|
});
|
|
296
300
|
return this.settleFactoryRun(envelope);
|
|
@@ -301,7 +305,9 @@ class CopilotSession {
|
|
|
301
305
|
try {
|
|
302
306
|
response = await this.rpc.factory.resume({
|
|
303
307
|
runId,
|
|
304
|
-
limits: options?.limits
|
|
308
|
+
limits: options?.limits,
|
|
309
|
+
notifyOnComplete: options?.notifyOnComplete,
|
|
310
|
+
logPhaseNames: options?.logPhaseNames
|
|
305
311
|
});
|
|
306
312
|
} catch (error) {
|
|
307
313
|
if (error instanceof import_node.ResponseError && typeof error.data === "object" && error.data !== null) {
|
|
@@ -316,7 +322,10 @@ class CopilotSession {
|
|
|
316
322
|
}),
|
|
317
323
|
getRun: async (runId) => this.rpc.factory.getRun({ runId }),
|
|
318
324
|
waitForRun: (runId, options) => this.waitForFactoryRun(runId, options?.signal),
|
|
319
|
-
listRuns: async () =>
|
|
325
|
+
listRuns: (async (options) => {
|
|
326
|
+
const page = await this.rpc.factory.listRuns(options ?? {});
|
|
327
|
+
return options === void 0 ? page.runs : page;
|
|
328
|
+
}),
|
|
320
329
|
getRunDetail: (runId) => this.rpc.factory.getRunDetail({ runId }),
|
|
321
330
|
getRunProgress: (runId, options = {}) => this.rpc.factory.getRunProgress({ runId, ...options }),
|
|
322
331
|
cancel: async (runId) => this.rpc.factory.cancel({ runId })
|