@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 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 user input requests from the agent. Enables the `ask_user` tool. See [User Input Requests](#user-input-requests) section.
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 agent to ask questions to the user using the `ask_user` tool by providing an `onUserInputRequest` handler:
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:
@@ -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((resolve) => {
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
- resolve(true);
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
- resolve(false);
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 getBundledCliPath() {
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 (0, import_node_path.join)((0, import_node_path.dirname)(packageEntryPath), "index.js");
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 candidate = (0, import_node_path.join)(base, ...packageName.split("/"), "index.js");
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 candidate;
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
- this.resolvedCliPath = conn.kind === "stdio" || conn.kind === "tcp" ? conn.path ?? effectiveEnv.COPILOT_CLI_PATH ?? getBundledCliPath() : void 0;
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 error;
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((resolve) => setTimeout(resolve, delay));
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((resolve) => {
770
- socket.once("close", () => resolve());
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((resolve) => {
1575
- resolveLock = resolve;
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((resolve, reject) => {
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
- resolve();
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
- resolve();
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
- setTimeout(() => {
1993
- const stderrOutput = this.stderrBuffer.trim();
1994
- if (stderrOutput) {
1995
- rejectProcessExit(
1996
- new Error(
1997
- `CLI server exited with code ${code}
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
- } else {
2002
- rejectProcessExit(
2003
- new Error(`CLI server exited unexpectedly with code ${code}`)
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("exit", (code) => {
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 entrypoint = this.resolveCliPathForFfi();
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((resolve, reject) => {
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
- resolve();
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
- * Resolves the cdylib next to the given CLI entrypoint and prepares the FFI host.
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(cliEntrypoint, prebuildsFolder, environment, args) {
149
- const fullEntrypoint = (0, import_node_path.resolve)(cliEntrypoint);
150
- const distDir = (0, import_node_path.dirname)(fullEntrypoint);
151
- const libraryPath = (0, import_node_path.join)(distDir, "prebuilds", prebuildsFolder, "runtime.node");
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(libraryPath, fullEntrypoint, environment, args);
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 temporarily falls back to its built-in Node launcher for backward compatibility.
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
+ });
@@ -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 () => (await this.rpc.factory.listRuns({})).runs,
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 })