@github/copilot-sdk 1.0.7-preview.0 → 1.0.7-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var client_exports = {};
20
30
  __export(client_exports, {
@@ -246,6 +256,7 @@ class TeardownResilientStreamMessageWriter extends import_node.StreamMessageWrit
246
256
  class CopilotClient {
247
257
  cliStartTimeout = null;
248
258
  cliProcess = null;
259
+ ffiHost = null;
249
260
  connection = null;
250
261
  messageWriter = null;
251
262
  socket = null;
@@ -322,6 +333,29 @@ class CopilotClient {
322
333
  `);
323
334
  }
324
335
  }
336
+ /**
337
+ * Environment variable that overrides the transport when the caller does not set
338
+ * {@link CopilotClientOptions.connection}. Accepts `"inprocess"` or `"stdio"`
339
+ * (case-insensitive); unset preserves the default stdio transport. Any other value
340
+ * is an error.
341
+ */
342
+ static DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION";
343
+ /**
344
+ * Resolves the default {@link RuntimeConnection} for the no-connection case,
345
+ * honoring {@link CopilotClient.DEFAULT_CONNECTION_ENV_VAR}.
346
+ */
347
+ static resolveDefaultConnection() {
348
+ const value = process.env[CopilotClient.DEFAULT_CONNECTION_ENV_VAR];
349
+ if (!value || value.toLowerCase() === "stdio") {
350
+ return { kind: "stdio" };
351
+ }
352
+ if (value.toLowerCase() === "inprocess") {
353
+ return { kind: "inprocess" };
354
+ }
355
+ throw new Error(
356
+ `Invalid ${CopilotClient.DEFAULT_CONNECTION_ENV_VAR} value '${value}'. Expected 'inprocess', 'stdio', or unset.`
357
+ );
358
+ }
325
359
  /**
326
360
  * Creates a new CopilotClient instance.
327
361
  *
@@ -350,12 +384,17 @@ class CopilotClient {
350
384
  * ```
351
385
  */
352
386
  constructor(options = {}) {
353
- const conn = options._internalConnection ?? options.connection ?? { kind: "stdio" };
387
+ const conn = options._internalConnection ?? options.connection ?? CopilotClient.resolveDefaultConnection();
354
388
  if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
355
389
  throw new Error(
356
390
  "gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
357
391
  );
358
392
  }
393
+ if (conn.kind === "inprocess" && options.workingDirectory !== void 0) {
394
+ throw new Error(
395
+ "workingDirectory is not supported with RuntimeConnection.forInProcess(): the in-process transport hosts the runtime in this process, so honoring it would require mutating the shared process-global cwd. Change the host process's working directory before constructing the client instead."
396
+ );
397
+ }
359
398
  if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
360
399
  if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
361
400
  throw new Error("connectionToken must be a non-empty string");
@@ -511,7 +550,9 @@ class CopilotClient {
511
550
  }
512
551
  this.state = "connecting";
513
552
  try {
514
- if (!this.isExternalServer) {
553
+ if (this.connectionConfig.kind === "inprocess") {
554
+ await this.startInProcessFfi();
555
+ } else if (!this.isExternalServer) {
515
556
  await this.startCLIServer();
516
557
  }
517
558
  await this.connectToServer();
@@ -560,6 +601,9 @@ class CopilotClient {
560
601
  async stop() {
561
602
  const errors = [];
562
603
  const activeSessions = [...this.sessions.values()];
604
+ if (this.connectionConfig.kind === "inprocess") {
605
+ await Promise.allSettled(activeSessions.map((session) => session.abort()));
606
+ }
563
607
  for (const session of activeSessions) {
564
608
  const sessionId = session.sessionId;
565
609
  let lastError = null;
@@ -588,7 +632,7 @@ class CopilotClient {
588
632
  session._markDisconnected();
589
633
  }
590
634
  this.sessions.clear();
591
- if (this.connection && this.cliProcess && !this.isExternalServer) {
635
+ if (this.connection && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
592
636
  const runtimeShutdownStart = Date.now();
593
637
  const shutdownPromise = this.rpc.runtime.shutdown();
594
638
  void shutdownPromise.catch(() => void 0);
@@ -673,6 +717,19 @@ class CopilotClient {
673
717
  );
674
718
  }
675
719
  }
720
+ if (this.ffiHost) {
721
+ const host = this.ffiHost;
722
+ this.ffiHost = null;
723
+ try {
724
+ host.dispose();
725
+ } catch (error) {
726
+ errors.push(
727
+ new Error(
728
+ `Failed to dispose in-process runtime host: ${error instanceof Error ? error.message : String(error)}`
729
+ )
730
+ );
731
+ }
732
+ }
676
733
  if (this.cliStartTimeout) {
677
734
  clearTimeout(this.cliStartTimeout);
678
735
  this.cliStartTimeout = null;
@@ -757,6 +814,13 @@ class CopilotClient {
757
814
  }
758
815
  this.cliProcess = null;
759
816
  }
817
+ if (this.ffiHost) {
818
+ try {
819
+ this.ffiHost.dispose();
820
+ } catch {
821
+ }
822
+ this.ffiHost = null;
823
+ }
760
824
  if (this.cliStartTimeout) {
761
825
  clearTimeout(this.cliStartTimeout);
762
826
  this.cliStartTimeout = null;
@@ -1001,6 +1065,7 @@ class CopilotClient {
1001
1065
  requestExtensions: config.requestExtensions,
1002
1066
  extensionSdkPath: config.extensionSdkPath,
1003
1067
  extensionInfo: config.extensionInfo,
1068
+ canvasProvider: config.canvasProvider,
1004
1069
  commands: config.commands?.map((cmd) => ({
1005
1070
  name: cmd.name,
1006
1071
  description: cmd.description
@@ -1198,6 +1263,7 @@ class CopilotClient {
1198
1263
  requestExtensions: config.requestExtensions,
1199
1264
  extensionSdkPath: config.extensionSdkPath,
1200
1265
  extensionInfo: config.extensionInfo,
1266
+ canvasProvider: config.canvasProvider,
1201
1267
  commands: config.commands?.map((cmd) => ({
1202
1268
  name: cmd.name,
1203
1269
  description: cmd.description
@@ -1596,6 +1662,41 @@ class CopilotClient {
1596
1662
  this.sessionLifecycleHandlers.delete(wildcardHandler);
1597
1663
  };
1598
1664
  }
1665
+ /**
1666
+ * Builds the environment for the spawned runtime child process (stdio/TCP): applies
1667
+ * the auth token, connection token, `COPILOT_HOME`, keychain setting, and telemetry
1668
+ * variables on top of the effective env. Not used by the in-process (FFI) transport,
1669
+ * whose worker inherits the host process's ambient environment
1670
+ * (see {@link CopilotClient.startInProcessFfi}).
1671
+ */
1672
+ buildRuntimeEnv() {
1673
+ const env = { ...this.resolvedEnv };
1674
+ delete env.NODE_DEBUG;
1675
+ if (this.options.gitHubToken) {
1676
+ env.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
1677
+ }
1678
+ if (this.effectiveConnectionToken) {
1679
+ env.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
1680
+ }
1681
+ if (this.options.baseDirectory) {
1682
+ env.COPILOT_HOME = this.options.baseDirectory;
1683
+ }
1684
+ if (this.options.mode === "empty") {
1685
+ env.COPILOT_DISABLE_KEYTAR = "1";
1686
+ }
1687
+ if (this.options.telemetry) {
1688
+ const t = this.options.telemetry;
1689
+ env.COPILOT_OTEL_ENABLED = "true";
1690
+ if (t.otlpEndpoint !== void 0) env.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
1691
+ if (t.otlpProtocol !== void 0) env.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
1692
+ if (t.filePath !== void 0) env.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
1693
+ if (t.exporterType !== void 0) env.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
1694
+ if (t.sourceName !== void 0) env.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
1695
+ if (t.captureContent !== void 0)
1696
+ env.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(t.captureContent);
1697
+ }
1698
+ return env;
1699
+ }
1599
1700
  /**
1600
1701
  * Start the CLI server process
1601
1702
  */
@@ -1629,43 +1730,12 @@ class CopilotClient {
1629
1730
  if (this.options.enableRemoteSessions) {
1630
1731
  args.push("--remote");
1631
1732
  }
1632
- const envWithoutNodeDebug = { ...this.resolvedEnv };
1633
- delete envWithoutNodeDebug.NODE_DEBUG;
1634
- if (this.options.gitHubToken) {
1635
- envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
1636
- }
1637
- if (this.effectiveConnectionToken) {
1638
- envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
1639
- }
1640
- if (this.options.baseDirectory) {
1641
- envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
1642
- }
1643
- if (this.options.mode === "empty") {
1644
- envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
1645
- }
1733
+ const envWithoutNodeDebug = this.buildRuntimeEnv();
1646
1734
  if (!this.resolvedCliPath) {
1647
1735
  throw new Error(
1648
1736
  "Path to Copilot CLI is required. Please supply it via `RuntimeConnection.forStdio({ path })` or `RuntimeConnection.forTcp({ path })`, set the COPILOT_CLI_PATH environment variable, or use `RuntimeConnection.forUri(...)` to connect to an already-running runtime."
1649
1737
  );
1650
1738
  }
1651
- if (this.options.telemetry) {
1652
- const t = this.options.telemetry;
1653
- envWithoutNodeDebug.COPILOT_OTEL_ENABLED = "true";
1654
- if (t.otlpEndpoint !== void 0)
1655
- envWithoutNodeDebug.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
1656
- if (t.otlpProtocol !== void 0)
1657
- envWithoutNodeDebug.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
1658
- if (t.filePath !== void 0)
1659
- envWithoutNodeDebug.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
1660
- if (t.exporterType !== void 0)
1661
- envWithoutNodeDebug.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
1662
- if (t.sourceName !== void 0)
1663
- envWithoutNodeDebug.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
1664
- if (t.captureContent !== void 0)
1665
- envWithoutNodeDebug.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(
1666
- t.captureContent
1667
- );
1668
- }
1669
1739
  if (!(0, import_node_fs.existsSync)(this.resolvedCliPath)) {
1670
1740
  throw new Error(
1671
1741
  `Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
@@ -1784,11 +1854,66 @@ stderr: ${stderrOutput}`
1784
1854
  return this.connectToParentProcessViaStdio();
1785
1855
  case "stdio":
1786
1856
  return this.connectToChildProcessViaStdio();
1857
+ case "inprocess":
1858
+ return this.connectViaFfi();
1787
1859
  case "tcp":
1788
1860
  case "uri":
1789
1861
  return this.connectViaTcp();
1790
1862
  }
1791
1863
  }
1864
+ /**
1865
+ * Start the in-process FFI runtime host: resolve the CLI entrypoint and native
1866
+ * runtime library, then let the native host spawn the CLI worker.
1867
+ *
1868
+ * The worker inherits this host process's ambient environment; per-client options
1869
+ * that lower to environment variables (`env`, `telemetry`, `gitHubToken`,
1870
+ * `baseDirectory`) are intentionally not applied here, because the native runtime
1871
+ * loads into the shared host process and a single env block cannot carry per-client
1872
+ * values. Configure the in-process runtime via the host process environment instead.
1873
+ * See https://github.com/github/copilot-sdk/issues/1934.
1874
+ */
1875
+ async startInProcessFfi() {
1876
+ const entrypoint = this.resolveCliPathForFfi();
1877
+ const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
1878
+ const host = FfiRuntimeHost.create(entrypoint, CopilotClient.getNapiPrebuildsFolder());
1879
+ this.ffiHost = host;
1880
+ await host.start();
1881
+ }
1882
+ /**
1883
+ * Connect to the in-process FFI runtime host over its receive/send streams,
1884
+ * reusing the same `vscode-jsonrpc` framing as the stdio transport.
1885
+ */
1886
+ async connectViaFfi() {
1887
+ if (!this.ffiHost) {
1888
+ throw new Error("In-process FFI runtime host not started");
1889
+ }
1890
+ this.messageWriter = new TeardownResilientStreamMessageWriter(this.ffiHost.sendStream);
1891
+ this.connection = (0, import_node.createMessageConnection)(
1892
+ new import_node.StreamMessageReader(this.ffiHost.receiveStream),
1893
+ this.messageWriter
1894
+ );
1895
+ this.attachConnectionHandlers();
1896
+ this.connection.listen();
1897
+ }
1898
+ /**
1899
+ * Resolves the CLI entrypoint used for in-process FFI hosting: `COPILOT_CLI_PATH`
1900
+ * when set, otherwise the bundled platform-package entrypoint.
1901
+ */
1902
+ resolveCliPathForFfi() {
1903
+ return this.resolvedEnv.COPILOT_CLI_PATH ?? getBundledCliPath();
1904
+ }
1905
+ /**
1906
+ * Returns the napi prebuilds folder name for the current host — the
1907
+ * `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
1908
+ * `linux-x64`) under which the runtime ships `prebuilds/<folder>/runtime.node`.
1909
+ */
1910
+ static getNapiPrebuildsFolder() {
1911
+ const arch = process.arch;
1912
+ if (arch !== "x64" && arch !== "arm64") {
1913
+ throw new Error(`Unsupported architecture '${arch}' for in-process FFI hosting.`);
1914
+ }
1915
+ return `${process.platform}-${arch}`;
1916
+ }
1792
1917
  /**
1793
1918
  * Connect to child via stdio pipes
1794
1919
  */
@@ -1910,8 +2035,9 @@ stderr: ${stderrOutput}`
1910
2035
  return;
1911
2036
  }
1912
2037
  const session = this.sessions.get(notification.sessionId);
2038
+ const event = notification.event;
1913
2039
  if (session) {
1914
- session._dispatchEvent(notification.event);
2040
+ session._dispatchEvent(event);
1915
2041
  }
1916
2042
  }
1917
2043
  handleSessionLifecycleNotification(notification) {
@@ -180,6 +180,9 @@ class CopilotRequestHandler {
180
180
  const ctx = {
181
181
  requestId: exchange.requestId,
182
182
  sessionId: exchange.sessionId,
183
+ agentId: exchange.agentId,
184
+ parentAgentId: exchange.parentAgentId,
185
+ interactionType: exchange.interactionType,
183
186
  transport: exchange.transport,
184
187
  url: exchange.url,
185
188
  headers: exchange.headers,
@@ -305,6 +308,9 @@ function routeChunk(exchange, params) {
305
308
  class CopilotRequestExchange {
306
309
  requestId;
307
310
  sessionId;
311
+ agentId;
312
+ parentAgentId;
313
+ interactionType;
308
314
  method = "GET";
309
315
  url = "";
310
316
  headers = {};
@@ -324,6 +330,9 @@ class CopilotRequestExchange {
324
330
  /** Fill in the request context once the matching start frame arrives. */
325
331
  setContext(params) {
326
332
  this.sessionId = params.sessionId;
333
+ this.agentId = params.agentId;
334
+ this.parentAgentId = params.parentAgentId;
335
+ this.interactionType = params.interactionType;
327
336
  this.method = params.method;
328
337
  this.url = params.url;
329
338
  this.headers = params.headers;
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var ffiRuntimeHost_exports = {};
30
+ __export(ffiRuntimeHost_exports, {
31
+ FfiRuntimeHost: () => FfiRuntimeHost
32
+ });
33
+ module.exports = __toCommonJS(ffiRuntimeHost_exports);
34
+ var import_node_fs = require("node:fs");
35
+ var import_koffi = __toESM(require("koffi"), 1);
36
+ var import_node_path = require("node:path");
37
+ var import_node_stream = require("node:stream");
38
+ const SYMBOL_PREFIX = "copilot_runtime_";
39
+ const KEEP_ALIVE_INTERVAL_MS = 1 << 30;
40
+ let loadedLibraryPath;
41
+ let loadedLibrary;
42
+ function loadLibrary(libraryPath) {
43
+ if (loadedLibrary) {
44
+ if (loadedLibraryPath !== libraryPath) {
45
+ throw new Error(
46
+ `An in-process FFI runtime library is already loaded from '${loadedLibraryPath}'; loading a different library from '${libraryPath}' in the same process is not supported.`
47
+ );
48
+ }
49
+ return loadedLibrary;
50
+ }
51
+ const lib = import_koffi.default.load(libraryPath);
52
+ const outboundCallbackType = import_koffi.default.pointer(
53
+ import_koffi.default.proto(
54
+ `void ${SYMBOL_PREFIX}outbound(void *userData, uint8 *bytesPtr, size_t bytesLen)`
55
+ )
56
+ );
57
+ loadedLibrary = {
58
+ hostStart: lib.func(`${SYMBOL_PREFIX}host_start`, "uint32", [
59
+ "uint8*",
60
+ "size_t",
61
+ "uint8*",
62
+ "size_t"
63
+ ]),
64
+ hostShutdown: lib.func(`${SYMBOL_PREFIX}host_shutdown`, "bool", ["uint32"]),
65
+ connectionOpen: lib.func(`${SYMBOL_PREFIX}connection_open`, "uint32", [
66
+ "uint32",
67
+ outboundCallbackType,
68
+ "void*",
69
+ "uint8*",
70
+ "size_t",
71
+ "uint8*",
72
+ "size_t",
73
+ "uint8*",
74
+ "size_t"
75
+ ]),
76
+ connectionWrite: lib.func(`${SYMBOL_PREFIX}connection_write`, "bool", [
77
+ "uint32",
78
+ "uint8*",
79
+ "size_t"
80
+ ]),
81
+ connectionClose: lib.func(`${SYMBOL_PREFIX}connection_close`, "bool", ["uint32"]),
82
+ outboundCallbackType
83
+ };
84
+ loadedLibraryPath = libraryPath;
85
+ return loadedLibrary;
86
+ }
87
+ function buildArgvJson(cliEntrypoint) {
88
+ const argv = cliEntrypoint.toLowerCase().endsWith(".js") ? ["node", cliEntrypoint, "--embedded-host", "--no-auto-update"] : [cliEntrypoint, "--embedded-host", "--no-auto-update"];
89
+ return Buffer.from(JSON.stringify(argv), "utf8");
90
+ }
91
+ function buildEnvJson(environment) {
92
+ if (!environment) {
93
+ return null;
94
+ }
95
+ const obj = {};
96
+ for (const [key, value] of Object.entries(environment)) {
97
+ if (value !== void 0) {
98
+ obj[key] = value;
99
+ }
100
+ }
101
+ if (Object.keys(obj).length === 0) {
102
+ return null;
103
+ }
104
+ return Buffer.from(JSON.stringify(obj), "utf8");
105
+ }
106
+ class FfiRuntimeHost {
107
+ constructor(libraryPath, cliEntrypoint, environment) {
108
+ this.libraryPath = libraryPath;
109
+ this.cliEntrypoint = cliEntrypoint;
110
+ this.environment = environment;
111
+ this.lib = loadLibrary(libraryPath);
112
+ this.receiveStream = new import_node_stream.PassThrough();
113
+ this.sendStream = new import_node_stream.Writable({
114
+ // connection_write enqueues the frame into the runtime's inbound channel and
115
+ // returns immediately, so a synchronous FFI call is sufficient here.
116
+ write: (chunk, _encoding, callback) => {
117
+ try {
118
+ this.writeFrame(chunk);
119
+ callback();
120
+ } catch (error) {
121
+ callback(error);
122
+ }
123
+ }
124
+ });
125
+ }
126
+ libraryPath;
127
+ cliEntrypoint;
128
+ environment;
129
+ lib;
130
+ serverId = 0;
131
+ connectionId = 0;
132
+ disposed = false;
133
+ outboundCallback;
134
+ keepAliveTimer;
135
+ /** The stream JSON-RPC reads server→client frames from. */
136
+ receiveStream;
137
+ /** The stream JSON-RPC writes client→server frames to. */
138
+ sendStream;
139
+ /**
140
+ * Resolves the cdylib next to the given CLI entrypoint and prepares the FFI host.
141
+ * The cdylib is resolved as `prebuilds/<prebuildsFolder>/runtime.node` relative to
142
+ * the entrypoint directory (the napi-rs `<node-platform>-<arch>` layout, e.g.
143
+ * `linux-x64`). Throws if it cannot be found.
144
+ */
145
+ static create(cliEntrypoint, prebuildsFolder, environment) {
146
+ const fullEntrypoint = (0, import_node_path.resolve)(cliEntrypoint);
147
+ const distDir = (0, import_node_path.dirname)(fullEntrypoint);
148
+ const libraryPath = (0, import_node_path.join)(distDir, "prebuilds", prebuildsFolder, "runtime.node");
149
+ if (!(0, import_node_fs.existsSync)(libraryPath)) {
150
+ throw new Error(`FFI runtime library not found. Looked for '${libraryPath}'.`);
151
+ }
152
+ return new FfiRuntimeHost(libraryPath, fullEntrypoint, environment);
153
+ }
154
+ /**
155
+ * Starts the in-process runtime: spawns the CLI worker via the native host,
156
+ * waits for readiness, and opens the FFI JSON-RPC connection.
157
+ */
158
+ async start() {
159
+ const argvJson = buildArgvJson(this.cliEntrypoint);
160
+ const envJson = buildEnvJson(this.environment);
161
+ this.serverId = await new Promise((resolvePromise, rejectPromise) => {
162
+ this.lib.hostStart.async(
163
+ argvJson,
164
+ argvJson.length,
165
+ envJson,
166
+ envJson ? envJson.length : 0,
167
+ (error, result) => {
168
+ if (error) {
169
+ rejectPromise(error);
170
+ } else {
171
+ resolvePromise(result);
172
+ }
173
+ }
174
+ );
175
+ });
176
+ if (!this.serverId) {
177
+ throw new Error(
178
+ `copilot_runtime_host_start failed (library '${this.libraryPath}', entrypoint '${this.cliEntrypoint}').`
179
+ );
180
+ }
181
+ this.outboundCallback = import_koffi.default.register(
182
+ (_userData, bytesPtr, bytesLen) => this.feedInbound(bytesPtr, bytesLen),
183
+ this.lib.outboundCallbackType
184
+ );
185
+ this.connectionId = this.lib.connectionOpen(
186
+ this.serverId,
187
+ this.outboundCallback,
188
+ null,
189
+ null,
190
+ 0,
191
+ null,
192
+ 0,
193
+ null,
194
+ 0
195
+ );
196
+ if (!this.connectionId) {
197
+ this.unregisterCallback();
198
+ this.lib.hostShutdown(this.serverId);
199
+ this.serverId = 0;
200
+ throw new Error("copilot_runtime_connection_open failed.");
201
+ }
202
+ this.keepAliveTimer = setInterval(() => {
203
+ }, KEEP_ALIVE_INTERVAL_MS);
204
+ }
205
+ writeFrame(frame) {
206
+ if (this.disposed || !this.connectionId) {
207
+ throw new Error("The in-process runtime connection is closed.");
208
+ }
209
+ const ok = this.lib.connectionWrite(this.connectionId, frame, frame.length);
210
+ if (!ok) {
211
+ throw new Error("Failed to write a frame to the in-process runtime connection.");
212
+ }
213
+ }
214
+ /**
215
+ * Native outbound (server→client) callback. koffi delivers it on the JS event loop
216
+ * via a threadsafe function, so the frame is decoded and written straight to
217
+ * {@link receiveStream}. The native pointer is only valid for this call, so the
218
+ * bytes are copied out before returning.
219
+ */
220
+ feedInbound(bytesPtr, bytesLen) {
221
+ try {
222
+ if (this.disposed || this.receiveStream.writableEnded) {
223
+ return;
224
+ }
225
+ const length = Number(bytesLen);
226
+ if (!bytesPtr || length <= 0) {
227
+ return;
228
+ }
229
+ const bytes = import_koffi.default.decode(
230
+ bytesPtr,
231
+ import_koffi.default.array("uint8", length, "Typed")
232
+ );
233
+ this.receiveStream.write(Buffer.from(bytes));
234
+ } catch (error) {
235
+ console.error(
236
+ `In-process FFI inbound callback failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}`
237
+ );
238
+ }
239
+ }
240
+ unregisterCallback() {
241
+ if (this.outboundCallback === void 0) {
242
+ return;
243
+ }
244
+ const callback = this.outboundCallback;
245
+ this.outboundCallback = void 0;
246
+ try {
247
+ import_koffi.default.unregister(callback);
248
+ } catch {
249
+ }
250
+ }
251
+ /** Closes the FFI connection, shuts down the native host, and releases resources. */
252
+ dispose() {
253
+ if (this.disposed) {
254
+ return;
255
+ }
256
+ this.disposed = true;
257
+ if (this.keepAliveTimer !== void 0) {
258
+ clearInterval(this.keepAliveTimer);
259
+ this.keepAliveTimer = void 0;
260
+ }
261
+ try {
262
+ if (this.connectionId) {
263
+ this.lib.connectionClose(this.connectionId);
264
+ this.connectionId = 0;
265
+ }
266
+ } catch {
267
+ }
268
+ try {
269
+ if (this.serverId) {
270
+ this.lib.hostShutdown(this.serverId);
271
+ this.serverId = 0;
272
+ }
273
+ } catch {
274
+ }
275
+ this.receiveStream.end();
276
+ this.unregisterCallback();
277
+ }
278
+ }
279
+ // Annotate the CommonJS export names for ESM import in node:
280
+ 0 && (module.exports = {
281
+ FfiRuntimeHost
282
+ });
@@ -1231,11 +1231,13 @@ function createSessionRpc(connection, sessionId) {
1231
1231
  /** @experimental */
1232
1232
  apps: {
1233
1233
  /**
1234
- * Fetch an MCP resource (typically a `ui://` MCP App bundle, per SEP-1865) from a connected server. Requires the `mcp-apps` session capability.
1234
+ * Deprecated/obsolete alias for `session.mcp.resources.read`; retained for backwards compatibility with earlier MCP Apps host integrations.
1235
1235
  *
1236
- * @param params MCP server and resource URI to fetch.
1236
+ * @param params Deprecated/obsolete MCP Apps alias for `McpResourcesReadRequest`; use `session.mcp.resources.read` instead.
1237
1237
  *
1238
- * @returns Resource contents returned by the MCP server.
1238
+ * @returns Deprecated/obsolete MCP Apps alias for `McpResourcesReadResult`; use `session.mcp.resources.read` instead.
1239
+ *
1240
+ * @deprecated
1239
1241
  */
1240
1242
  readResource: async (params) => connection.sendRequest("session.mcp.apps.readResource", { sessionId, ...params }),
1241
1243
  /**
@@ -1274,6 +1276,33 @@ function createSessionRpc(connection, sessionId) {
1274
1276
  * @returns Diagnostic snapshot of MCP Apps wiring for the named server.
1275
1277
  */
1276
1278
  diagnose: async (params) => connection.sendRequest("session.mcp.apps.diagnose", { sessionId, ...params })
1279
+ },
1280
+ /** @experimental */
1281
+ resources: {
1282
+ /**
1283
+ * Fetch an MCP resource from a connected server by URI (proxies MCP `resources/read`).
1284
+ *
1285
+ * @param params MCP server and resource URI to fetch.
1286
+ *
1287
+ * @returns Resource contents returned by the MCP server.
1288
+ */
1289
+ read: async (params) => connection.sendRequest("session.mcp.resources.read", { sessionId, ...params }),
1290
+ /**
1291
+ * Enumerate one page of resources a connected MCP server exposes (proxies MCP `resources/list`). Pass `cursor` to continue from a prior result's `nextCursor`.
1292
+ *
1293
+ * @param params MCP server whose resources to enumerate.
1294
+ *
1295
+ * @returns One page of resources advertised by the named MCP server.
1296
+ */
1297
+ list: async (params) => connection.sendRequest("session.mcp.resources.list", { sessionId, ...params }),
1298
+ /**
1299
+ * Enumerate one page of resource templates a connected MCP server exposes (proxies MCP `resources/templates/list`). Pass `cursor` to continue from a prior result's `nextCursor`.
1300
+ *
1301
+ * @param params MCP server whose resource templates to enumerate.
1302
+ *
1303
+ * @returns One page of resource templates advertised by the named MCP server.
1304
+ */
1305
+ listTemplates: async (params) => connection.sendRequest("session.mcp.resources.listTemplates", { sessionId, ...params })
1277
1306
  }
1278
1307
  },
1279
1308
  /** @experimental */