@github/copilot-sdk-win32-x64 1.0.15 → 1.0.16

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.
@@ -4041,6 +4041,60 @@ import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2 } f
4041
4041
  // src/sdk/nodejs/dist/generated/rpc.js
4042
4042
  function createServerRpc(connection) {
4043
4043
  return {
4044
+ /** @experimental */
4045
+ environments: {
4046
+ /**
4047
+ * Lists GitHub Mission Control environments visible to the authenticated identity. Does not require a running host and excludes host relay credentials.
4048
+ *
4049
+ * @param params Optional discovery filters supported by GitHub Mission Control.
4050
+ *
4051
+ * @returns Environments visible to the authenticated caller and matching the supplied filters.
4052
+ */
4053
+ list: async (params) => connection.sendRequest("environments.list", params),
4054
+ /**
4055
+ * Gets safe discovery information for a GitHub Mission Control environment without requiring a running host.
4056
+ *
4057
+ * @param params Identify a Mission Control environment to retrieve.
4058
+ *
4059
+ * @returns Safe discovery information for the requested environment.
4060
+ */
4061
+ get: async (params) => connection.sendRequest("environments.get", params),
4062
+ /**
4063
+ * Deletes a user-managed GitHub Mission Control environment. GitHub-managed environments cannot be deleted. Does not stop a running host, which may register again.
4064
+ *
4065
+ * @param params Identify a user-managed Mission Control environment to delete.
4066
+ *
4067
+ * @returns Acknowledgement that the requested environment was deleted.
4068
+ */
4069
+ delete: async (params) => connection.sendRequest("environments.delete", params)
4070
+ },
4071
+ /** @experimental */
4072
+ host: {
4073
+ /**
4074
+ * Publishes an attached resident session for this listener's lifetime without copying it.
4075
+ *
4076
+ * @param params Publishes a resident session attached to the listener's owning connection.
4077
+ *
4078
+ * @returns The existing runtime identity and its resource on the listener.
4079
+ */
4080
+ publishSession: async (params) => connection.sendRequest("host.publishSession", params),
4081
+ /**
4082
+ * Starts a connection-owned AHP host with explicit localServer and/or githubEnvironment transports as a supervised SDK participant.
4083
+ *
4084
+ * @param params Starts a supervised AHP host with at least one explicitly selected transport.
4085
+ *
4086
+ * @returns Listener readiness, returned only after binding and the supervised participant's SDK handshake.
4087
+ */
4088
+ start: async (params) => connection.sendRequest("host.start", params),
4089
+ /**
4090
+ * Stops a listener owned by this SDK connection and joins its cleanup without deleting sessions.
4091
+ *
4092
+ * @param params Stops a connection-owned listener and joins its teardown.
4093
+ *
4094
+ * @returns Empty acknowledgement for a completed host lifecycle operation.
4095
+ */
4096
+ dispose: async (params) => connection.sendRequest("host.dispose", params)
4097
+ },
4044
4098
  /**
4045
4099
  * Checks server responsiveness and returns protocol information.
4046
4100
  *
@@ -4873,6 +4927,45 @@ function createServerRpc(connection) {
4873
4927
  }
4874
4928
  function createInternalServerRpc(connection) {
4875
4929
  return {
4930
+ /** @experimental */
4931
+ host: {
4932
+ /**
4933
+ * Requests app-owned materialization over the owning SDK participant.
4934
+ *
4935
+ * @param params One application-owned session handoff, requested by the supervised hosting participant.
4936
+ *
4937
+ * @returns The resident session the application has materialized on its own connection.
4938
+ */
4939
+ createSession: async (params) => connection.sendRequest("host.createSession", params),
4940
+ /**
4941
+ * Releases app ownership retention after AHP detaches.
4942
+ *
4943
+ * @param params Ends one participation, not the application's session lifetime.
4944
+ *
4945
+ * @returns Empty acknowledgement for a completed host lifecycle operation.
4946
+ */
4947
+ releaseSession: async (params) => connection.sendRequest("host.releaseSession", params),
4948
+ /**
4949
+ * Resolves current authenticated credentials and remote-control policy only for the runtime-owned Mission Control hosting participant.
4950
+ *
4951
+ * @returns Private credentials delivered only to a runtime-owned Mission Control hosting participant.
4952
+ */
4953
+ getEnvironmentCredentials: async () => connection.sendRequest("host.getEnvironmentCredentials", {}),
4954
+ /**
4955
+ * Returns listener settings only to the supervised hosting participant over its SDK connection.
4956
+ *
4957
+ * @returns Normalized listener settings delivered only to the supervised hosting participant.
4958
+ */
4959
+ getConfiguration: async () => connection.sendRequest("host.getConfiguration", {}),
4960
+ /**
4961
+ * Reports a supervised hosting participant's bound AHP endpoint after its SDK handshake.
4962
+ *
4963
+ * @param params Readiness reported by the supervised hosting participant on its own SDK connection.
4964
+ *
4965
+ * @returns Empty acknowledgement for a completed host lifecycle operation.
4966
+ */
4967
+ ready: async (params) => connection.sendRequest("host.ready", params)
4968
+ },
4876
4969
  /**
4877
4970
  * Performs the SDK server connection handshake and validates the optional connection token. Marked internal because this is JSON-RPC transport plumbing invoked automatically by an SDK client's own `connect()` wrapper, not a user-facing method. Stays internal as long as the SDK client owns the handshake; would only become public if the SDK ever exposed the raw schema surface to consumers without a connection wrapper.
4878
4971
  *
@@ -4994,7 +5087,15 @@ function createSessionRpc(connection, sessionId) {
4994
5087
  *
4995
5088
  * @returns Result of attempting to disable sandboxing for the current session.
4996
5089
  */
4997
- disableForSession: async (params) => connection.sendRequest("session.sandbox.disableForSession", { sessionId, ...params })
5090
+ disableForSession: async (params) => connection.sendRequest("session.sandbox.disableForSession", { sessionId, ...params }),
5091
+ /**
5092
+ * Adds the path offered by a pending sandbox escalation permission request's sandboxPathGrant to the session's sandbox policy and approves the request, so the blocked operation re-runs inside the sandbox rather than outside it. The request is rejected unless the exact request is still pending, carries a sandboxPathGrant, and the grant still takes effect under the current managed policy. Does not persist the path; hosts that store sandbox settings save it themselves.
5093
+ *
5094
+ * @param params Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt.
5095
+ *
5096
+ * @returns Result of accepting a sandbox path grant.
5097
+ */
5098
+ grantPathForRequest: async (params) => connection.sendRequest("session.sandbox.grantPathForRequest", { sessionId, ...params })
4998
5099
  },
4999
5100
  /**
5000
5101
  * Aborts the current agent turn.
@@ -6202,7 +6303,15 @@ function createSessionRpc(connection, sessionId) {
6202
6303
  *
6203
6304
  * @returns The selectable model entries and selection ids synthesized for the synchronized BYOK models.
6204
6305
  */
6205
- sync: async (params) => connection.sendRequest("session.provider.sync", { sessionId, ...params })
6306
+ sync: async (params) => connection.sendRequest("session.provider.sync", { sessionId, ...params }),
6307
+ /**
6308
+ * Withdraws named host-managed models from the session's BYOK registry, leaving every other entry untouched. The scoped counterpart to `provider.sync`: a snapshot can only describe entries the caller knows about, so using it to remove one model silently withdraws rows registered by another source, such as a plugin calling `provider.add` at runtime. Naming what to remove leaves unrelated entries alone. Selection ids that are not registered are ignored, so withdrawal is idempotent. A provider is removed only when one of the withdrawn models was the last entry referencing it; a provider that simply has no models, which is the normal state while its rows are supplied by catalog discovery, is left in place.
6309
+ *
6310
+ * @param params Host-managed model selection ids to withdraw from the session's BYOK registry.
6311
+ *
6312
+ * @returns What the withdrawal actually removed from the registry.
6313
+ */
6314
+ withdraw: async (params) => connection.sendRequest("session.provider.withdraw", { sessionId, ...params })
6206
6315
  },
6207
6316
  /** @experimental */
6208
6317
  options: {
@@ -7455,6 +7564,11 @@ function registerClientSessionApiHandlers(connection, getHandlers) {
7455
7564
  });
7456
7565
  }
7457
7566
  function registerClientGlobalApiHandlers(connection, handlers) {
7567
+ connection.onNotification("host.exited", async (params) => {
7568
+ const handler = handlers.host;
7569
+ if (!handler) return;
7570
+ await handler.exited(params);
7571
+ });
7458
7572
  connection.onRequest("extensionLaunchProvider.resolve", async (params) => {
7459
7573
  const handler = handlers.extensionLaunchProvider;
7460
7574
  if (!handler) throw new Error("No extensionLaunchProvider client-global handler registered");
@@ -10696,6 +10810,42 @@ function assertWorkflowStepResult(value, key) {
10696
10810
  });
10697
10811
  }
10698
10812
 
10813
+ // src/sdk/nodejs/dist/host.js
10814
+ var AhpHost = class {
10815
+ /** @internal */
10816
+ constructor(info, disposeHost, publishHostSession) {
10817
+ this.disposeHost = disposeHost;
10818
+ this.publishHostSession = publishHostSession;
10819
+ this.hostId = info.hostId;
10820
+ this.url = info.url;
10821
+ this.environmentId = info.environmentId;
10822
+ this.token = info.token;
10823
+ this.pid = info.pid;
10824
+ }
10825
+ disposeHost;
10826
+ publishHostSession;
10827
+ hostId;
10828
+ /** Local WebSocket URL. Absent when only the GitHub environment is enabled. */
10829
+ url;
10830
+ /** Mission Control environment ID, when GitHub hosting is enabled. */
10831
+ environmentId;
10832
+ /** Connection token, when required by the listener. Treat this value as a secret. */
10833
+ token;
10834
+ /** Legacy separate host process ID. Absent for in-process listeners; use dispose() to stop. */
10835
+ pid;
10836
+ /** Ask the runtime to stop the listener and await its cleanup, on every call. */
10837
+ dispose() {
10838
+ return this.disposeHost();
10839
+ }
10840
+ /** Publish an existing session attached to this client, until this listener stops. */
10841
+ publishSession(sessionId) {
10842
+ return this.publishHostSession(sessionId);
10843
+ }
10844
+ async [Symbol.asyncDispose]() {
10845
+ await this.dispose();
10846
+ }
10847
+ };
10848
+
10699
10849
  // src/sdk/nodejs/dist/runtimeArtifacts.js
10700
10850
  import {
10701
10851
  chmodSync,
@@ -11067,6 +11217,57 @@ var BuiltInTools = {
11067
11217
  // src/sdk/nodejs/dist/client.js
11068
11218
  var MIN_PROTOCOL_VERSION = 3;
11069
11219
  var RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
11220
+ function createMessageConnection(reader, writer) {
11221
+ let dispatch;
11222
+ let finishDrain;
11223
+ let drained;
11224
+ let disposed = false;
11225
+ const barrier = { jsonrpc: "2.0", method: "$/sdkDrain" };
11226
+ const connection = (0, import_node3.createMessageConnection)(
11227
+ {
11228
+ onError: reader.onError,
11229
+ onClose: reader.onClose,
11230
+ onPartialMessage: reader.onPartialMessage,
11231
+ listen: (callback) => {
11232
+ dispatch = callback;
11233
+ return reader.listen(callback);
11234
+ },
11235
+ dispose: () => reader.dispose()
11236
+ },
11237
+ writer,
11238
+ void 0,
11239
+ {
11240
+ messageStrategy: {
11241
+ handleMessage: (message, next) => {
11242
+ if (message === barrier) {
11243
+ finishDrain?.();
11244
+ } else {
11245
+ next(message);
11246
+ }
11247
+ }
11248
+ }
11249
+ }
11250
+ );
11251
+ connection.onDispose(() => {
11252
+ disposed = true;
11253
+ finishDrain?.();
11254
+ });
11255
+ return Object.assign(connection, {
11256
+ drain: () => {
11257
+ if (!drained) {
11258
+ drained = new Promise((resolve22) => {
11259
+ finishDrain = resolve22;
11260
+ if (disposed || !dispatch) {
11261
+ resolve22();
11262
+ } else {
11263
+ dispatch(barrier);
11264
+ }
11265
+ });
11266
+ }
11267
+ return drained;
11268
+ }
11269
+ });
11270
+ }
11070
11271
  async function withTimeout(promise, timeoutMs, message) {
11071
11272
  let timeout;
11072
11273
  try {
@@ -11253,6 +11454,9 @@ var CopilotClient = class _CopilotClient {
11253
11454
  /** Shared in-flight start; concurrent callers await it instead of spawning another CLI. */
11254
11455
  startPromise = null;
11255
11456
  sessions = /* @__PURE__ */ new Map();
11457
+ hostExitCallbacks = /* @__PURE__ */ new Map();
11458
+ hostSessionFactories = /* @__PURE__ */ new Map();
11459
+ hostHandoffs = /* @__PURE__ */ new Map();
11256
11460
  stderrBuffer = "";
11257
11461
  // Captures CLI stderr for error messages
11258
11462
  /** Resolved connection mode chosen in the constructor. */
@@ -11544,6 +11748,11 @@ var CopilotClient = class _CopilotClient {
11544
11748
  }
11545
11749
  setupClientGlobalHandlers() {
11546
11750
  const handlers = {};
11751
+ handlers.host = {
11752
+ exited: async (exit) => {
11753
+ this.handleHostExit(exit);
11754
+ }
11755
+ };
11547
11756
  handlers.extensionLaunchProvider = this.extensionLaunchProvider;
11548
11757
  if (this.requestHandler) {
11549
11758
  this.requestAdapter = createCopilotRequestAdapter(this.requestHandler, () => {
@@ -11697,7 +11906,8 @@ var CopilotClient = class _CopilotClient {
11697
11906
  * 1. Closes all active sessions (releases in-memory resources)
11698
11907
  * 2. Requests runtime shutdown for SDK-owned CLI processes
11699
11908
  * 3. Closes the JSON-RPC connection
11700
- * 4. Terminates the CLI server process (if spawned by this client)
11909
+ * 4. Signals EOF to an owned stdio process and waits for host cleanup, then
11910
+ * terminates the process if it does not exit within the shutdown timeout
11701
11911
  *
11702
11912
  * Note: session data on disk is preserved, so sessions can be resumed later.
11703
11913
  * To permanently remove session data before stopping, call
@@ -11815,7 +12025,23 @@ var CopilotClient = class _CopilotClient {
11815
12025
  }
11816
12026
  if (this.cliProcess && !this.isExternalServer) {
11817
12027
  const child = this.cliProcess;
11818
- this.cliProcess = null;
12028
+ if (this.connectionConfig.kind === "stdio" && child.stdin && child.exitCode == null && child.signalCode == null) {
12029
+ const gracefulExitStart = Date.now();
12030
+ try {
12031
+ child.stdin.end();
12032
+ const exited = await waitForChildExit(child, RUNTIME_SHUTDOWN_TIMEOUT_MS);
12033
+ this.logDebugTiming(
12034
+ exited ? "CopilotClient.stop graceful stdio exit complete" : "CopilotClient.stop graceful stdio exit timed out; terminating child",
12035
+ gracefulExitStart
12036
+ );
12037
+ } catch (error) {
12038
+ errors.push(
12039
+ new Error(
12040
+ `Failed to close CLI stdin: ${error instanceof Error ? error.message : String(error)}`
12041
+ )
12042
+ );
12043
+ }
12044
+ }
11819
12045
  try {
11820
12046
  if (child.exitCode == null && child.signalCode == null) {
11821
12047
  child.kill();
@@ -11833,6 +12059,10 @@ var CopilotClient = class _CopilotClient {
11833
12059
  `Failed to kill CLI process: ${error instanceof Error ? error.message : String(error)}`
11834
12060
  )
11835
12061
  );
12062
+ } finally {
12063
+ if (this.cliProcess === child) {
12064
+ this.cliProcess = null;
12065
+ }
11836
12066
  }
11837
12067
  }
11838
12068
  if (this.ffiHost) {
@@ -11856,6 +12086,7 @@ var CopilotClient = class _CopilotClient {
11856
12086
  this.runtimePort = null;
11857
12087
  this.stderrBuffer = "";
11858
12088
  this.processExitPromise = null;
12089
+ this.disconnectHosts();
11859
12090
  return errors;
11860
12091
  }
11861
12092
  /**
@@ -11900,6 +12131,7 @@ var CopilotClient = class _CopilotClient {
11900
12131
  */
11901
12132
  async forceStop() {
11902
12133
  this.forceStopping = true;
12134
+ this.disconnectHosts();
11903
12135
  for (const session of this.sessions.values()) {
11904
12136
  session._markDisconnected();
11905
12137
  }
@@ -12199,6 +12431,7 @@ var CopilotClient = class _CopilotClient {
12199
12431
  const response = await this.connection.sendRequest("session.create", {
12200
12432
  ...await getTraceContext(this.onGetTraceContext),
12201
12433
  model: config.model,
12434
+ allowedModels: config.allowedModels,
12202
12435
  sessionId: localSessionId,
12203
12436
  clientName: config.clientName,
12204
12437
  reasoningEffort: config.reasoningEffort,
@@ -12329,6 +12562,11 @@ var CopilotClient = class _CopilotClient {
12329
12562
  }
12330
12563
  throw e;
12331
12564
  }
12565
+ for (const entry of this.hostHandoffs.values()) {
12566
+ if (entry.requestedSessionId === session.sessionId) {
12567
+ entry.configs.set(session, config);
12568
+ }
12569
+ }
12332
12570
  return session;
12333
12571
  }
12334
12572
  /**
@@ -12435,6 +12673,7 @@ var CopilotClient = class _CopilotClient {
12435
12673
  }
12436
12674
  try {
12437
12675
  const response = await this.connection.sendRequest("session.resume", {
12676
+ allowedModels: config.allowedModels,
12438
12677
  ...await getTraceContext(this.onGetTraceContext),
12439
12678
  sessionId,
12440
12679
  clientName: config.clientName,
@@ -12563,8 +12802,205 @@ var CopilotClient = class _CopilotClient {
12563
12802
  }
12564
12803
  throw e;
12565
12804
  }
12805
+ for (const entry of this.hostHandoffs.values()) {
12806
+ if (entry.requestedSessionId === session.sessionId) {
12807
+ entry.configs.set(session, { ...config, sessionId });
12808
+ }
12809
+ }
12566
12810
  return session;
12567
12811
  }
12812
+ /**
12813
+ * Start a complete AHP listener hosted in-process by this runtime.
12814
+ *
12815
+ * This connection owns the host. Disposing it or disconnecting the client
12816
+ * stops the listener without deleting sessions. The host uses a separate
12817
+ * SDK connection to the same runtime, not another runtime process.
12818
+ * Only one AHP host may own the catalog in an effective Copilot home
12819
+ * at a time. Other SDK clients and sessions remain usable in that home.
12820
+ * Select localServer, githubEnvironment, or both. An empty localServer
12821
+ * selects 127.0.0.1 on an available port. `onExit` runs at most once; owner disconnection cannot
12822
+ * acknowledge listener cleanup over the disconnected transport.
12823
+ *
12824
+ * @experimental
12825
+ */
12826
+ async startAhpHost(options) {
12827
+ if (!options?.localServer && !options?.githubEnvironment) {
12828
+ throw new Error("At least one of localServer or githubEnvironment is required");
12829
+ }
12830
+ if (this.state !== "connected") {
12831
+ await this.start();
12832
+ }
12833
+ const rpc = this.rpc;
12834
+ const hostId = randomUUID();
12835
+ const {
12836
+ localServer,
12837
+ githubEnvironment,
12838
+ onExit,
12839
+ createSession,
12840
+ resumeSession,
12841
+ onSessionReleased
12842
+ } = options;
12843
+ if (createSession || resumeSession) {
12844
+ this.hostSessionFactories.set(hostId, {
12845
+ createSession,
12846
+ resumeSession,
12847
+ onSessionReleased
12848
+ });
12849
+ }
12850
+ if (onExit) {
12851
+ this.hostExitCallbacks.set(hostId, onExit);
12852
+ }
12853
+ try {
12854
+ const info = await rpc.host.start({
12855
+ hostId,
12856
+ localServer,
12857
+ githubEnvironment,
12858
+ sessionFactory: createSession ? true : void 0,
12859
+ resumeFactory: resumeSession ? true : void 0
12860
+ });
12861
+ return new AhpHost(
12862
+ info,
12863
+ async () => {
12864
+ await rpc.host.dispose({ hostId });
12865
+ },
12866
+ (sessionId) => rpc.host.publishSession({ hostId, sessionId })
12867
+ );
12868
+ } catch (error) {
12869
+ this.hostExitCallbacks.delete(hostId);
12870
+ this.releaseHostSessions(hostId);
12871
+ throw error;
12872
+ }
12873
+ }
12874
+ handleHostExit(exit) {
12875
+ this.releaseHostSessions(exit.hostId);
12876
+ const onExit = this.hostExitCallbacks.get(exit.hostId);
12877
+ if (onExit) {
12878
+ this.hostExitCallbacks.delete(exit.hostId);
12879
+ const reportError = (error) => {
12880
+ console.error("AHP host exit callback failed", { hostId: exit.hostId, error });
12881
+ };
12882
+ try {
12883
+ void Promise.resolve(onExit(exit)).catch(reportError);
12884
+ } catch (error) {
12885
+ reportError(error);
12886
+ }
12887
+ }
12888
+ }
12889
+ disconnectHosts() {
12890
+ for (const hostId of /* @__PURE__ */ new Set([
12891
+ ...this.hostExitCallbacks.keys(),
12892
+ ...this.hostSessionFactories.keys()
12893
+ ])) {
12894
+ this.handleHostExit({
12895
+ hostId,
12896
+ reason: "ownerDisconnected",
12897
+ error: "Owner connection closed; runtime cleanup cannot be acknowledged on this connection."
12898
+ });
12899
+ }
12900
+ }
12901
+ releaseHostSessions(hostId) {
12902
+ this.hostSessionFactories.delete(hostId);
12903
+ for (const [handoffId, entry] of this.hostHandoffs) {
12904
+ if (entry.hostId === hostId) this.releaseHostSession(handoffId);
12905
+ }
12906
+ }
12907
+ releaseHostSession(handoffId) {
12908
+ const entry = this.hostHandoffs.get(handoffId);
12909
+ if (!entry) return;
12910
+ this.hostHandoffs.delete(handoffId);
12911
+ entry.released = true;
12912
+ entry.configs.clear();
12913
+ entry.controller.abort();
12914
+ if (entry.session) this.notifyHostSessionReleased(entry);
12915
+ }
12916
+ notifyHostSessionReleased(entry) {
12917
+ const { session, onReleased } = entry;
12918
+ entry.session = void 0;
12919
+ if (session && onReleased) {
12920
+ try {
12921
+ void Promise.resolve(onReleased(session)).catch(() => {
12922
+ console.error("AHP session release callback failed");
12923
+ });
12924
+ } catch {
12925
+ console.error("AHP session release callback failed");
12926
+ }
12927
+ }
12928
+ }
12929
+ async createHostSession(params) {
12930
+ const options = this.hostSessionFactories.get(params.hostId);
12931
+ if (!(params.resume ? options?.resumeSession : options?.createSession) || this.hostHandoffs.has(params.handoffId)) {
12932
+ throw new Error("AHP session factory is unavailable or handoff already exists");
12933
+ }
12934
+ const entry = {
12935
+ hostId: params.hostId,
12936
+ requestedSessionId: params.config.sessionId,
12937
+ controller: new AbortController(),
12938
+ configs: /* @__PURE__ */ new Map(),
12939
+ released: false,
12940
+ onReleased: options.onSessionReleased
12941
+ };
12942
+ this.hostHandoffs.set(params.handoffId, entry);
12943
+ const { configDir, ...config } = params.config;
12944
+ if (configDir !== void 0) config.configDirectory = configDir;
12945
+ const expected = structuredClone(config);
12946
+ const cancelled = new Promise((_resolve, reject) => {
12947
+ entry.controller.signal.addEventListener(
12948
+ "abort",
12949
+ () => {
12950
+ reject(new Error("AHP session handoff ended"));
12951
+ },
12952
+ { once: true }
12953
+ );
12954
+ });
12955
+ const materialized = Promise.resolve().then(() => {
12956
+ if (params.resume) {
12957
+ const { sessionId, ...resumeConfig } = config;
12958
+ if (!sessionId) throw new Error("AHP resume requires sessionId");
12959
+ return options.resumeSession({
12960
+ sessionId,
12961
+ config: resumeConfig,
12962
+ signal: entry.controller.signal
12963
+ });
12964
+ }
12965
+ return options.createSession({
12966
+ config,
12967
+ signal: entry.controller.signal
12968
+ });
12969
+ }).then((session) => {
12970
+ entry.session = session;
12971
+ const actual = entry.configs.get(session);
12972
+ entry.configs.clear();
12973
+ if (entry.released) {
12974
+ this.notifyHostSessionReleased(entry);
12975
+ throw new Error("AHP session handoff ended");
12976
+ }
12977
+ if (this.sessions.get(session.sessionId) !== session || session.sessionId !== expected.sessionId) {
12978
+ throw new Error(
12979
+ "AHP callback must return the requested session from this client"
12980
+ );
12981
+ }
12982
+ const contains = (value, required) => {
12983
+ if (required && typeof required === "object" && !Array.isArray(required)) {
12984
+ return !!value && typeof value === "object" && Object.entries(required).every(
12985
+ ([key, child]) => contains(value[key], child)
12986
+ );
12987
+ }
12988
+ return JSON.stringify(value) === JSON.stringify(required);
12989
+ };
12990
+ if ((!params.resume || actual !== void 0) && !contains(actual, expected)) {
12991
+ throw new Error(
12992
+ "AHP callback must preserve the supplied session configuration"
12993
+ );
12994
+ }
12995
+ return { sessionId: session.sessionId };
12996
+ });
12997
+ try {
12998
+ return await Promise.race([materialized, cancelled]);
12999
+ } catch (error) {
13000
+ this.releaseHostSession(params.handoffId);
13001
+ throw error;
13002
+ }
13003
+ }
12568
13004
  /**
12569
13005
  * Sends a ping request to the server to verify connectivity.
12570
13006
  *
@@ -13104,8 +13540,13 @@ stderr: ${stderrOutput}`
13104
13540
  case "inprocess":
13105
13541
  return this.connectViaFfi();
13106
13542
  case "tcp":
13107
- case "uri":
13108
13543
  return this.connectViaTcp();
13544
+ case "uri": {
13545
+ const { host, port } = this.parseCliUrl(this.connectionConfig.url);
13546
+ this.actualHost = host;
13547
+ this.runtimePort = port;
13548
+ return this.connectViaTcp();
13549
+ }
13109
13550
  }
13110
13551
  }
13111
13552
  /** Starts the in-process FFI runtime with SDK-managed typed options. */
@@ -13164,7 +13605,7 @@ stderr: ${stderrOutput}`
13164
13605
  throw new Error("In-process FFI runtime host not started");
13165
13606
  }
13166
13607
  this.messageWriter = new TeardownResilientStreamMessageWriter(this.ffiHost.sendStream);
13167
- this.connection = (0, import_node3.createMessageConnection)(
13608
+ this.connection = createMessageConnection(
13168
13609
  new import_node3.StreamMessageReader(this.ffiHost.receiveStream),
13169
13610
  this.messageWriter
13170
13611
  );
@@ -13218,7 +13659,7 @@ stderr: ${stderrOutput}` : ""}`
13218
13659
  }
13219
13660
  });
13220
13661
  this.messageWriter = new TeardownResilientStreamMessageWriter(this.cliProcess.stdin);
13221
- this.connection = (0, import_node3.createMessageConnection)(
13662
+ this.connection = createMessageConnection(
13222
13663
  new import_node3.StreamMessageReader(this.cliProcess.stdout),
13223
13664
  this.messageWriter
13224
13665
  );
@@ -13233,7 +13674,7 @@ stderr: ${stderrOutput}` : ""}`
13233
13674
  throw new Error("CLI child process was unexpectedly started in parent process mode");
13234
13675
  }
13235
13676
  this.messageWriter = new TeardownResilientStreamMessageWriter(process.stdout);
13236
- this.connection = (0, import_node3.createMessageConnection)(
13677
+ this.connection = createMessageConnection(
13237
13678
  new import_node3.StreamMessageReader(process.stdin),
13238
13679
  this.messageWriter
13239
13680
  );
@@ -13256,7 +13697,7 @@ stderr: ${stderrOutput}` : ""}`
13256
13697
  this.socket.connect(this.runtimePort, this.actualHost, () => {
13257
13698
  clearTimeout(connectionTimeout);
13258
13699
  this.messageWriter = new TeardownResilientStreamMessageWriter(this.socket);
13259
- this.connection = (0, import_node3.createMessageConnection)(
13700
+ this.connection = createMessageConnection(
13260
13701
  new import_node3.StreamMessageReader(this.socket),
13261
13702
  this.messageWriter
13262
13703
  );
@@ -13313,6 +13754,18 @@ stderr: ${stderrOutput}` : ""}`
13313
13754
  (params, token) => confirmation.confirm(params, token)
13314
13755
  );
13315
13756
  }
13757
+ this.connection.onRequest(
13758
+ "host.materializeSession",
13759
+ (params) => this.createHostSession(params)
13760
+ );
13761
+ this.connection.onNotification(
13762
+ "host.sessionReleased",
13763
+ (params) => {
13764
+ if (this.hostHandoffs.get(params.handoffId)?.hostId === params.hostId) {
13765
+ this.releaseHostSession(params.handoffId);
13766
+ }
13767
+ }
13768
+ );
13316
13769
  this.connection.onRequest(
13317
13770
  "hooks.invoke",
13318
13771
  async (params) => {
@@ -13320,20 +13773,27 @@ stderr: ${stderrOutput}` : ""}`
13320
13773
  }
13321
13774
  );
13322
13775
  const connection = this.connection;
13776
+ const messageWriter = this.messageWriter;
13323
13777
  const markDisconnected = () => {
13324
13778
  if (this.connection !== connection) {
13779
+ connection.dispose();
13325
13780
  return;
13326
13781
  }
13327
13782
  this.connectionClosed = true;
13328
13783
  this.state = "disconnected";
13784
+ this.disconnectHosts();
13329
13785
  for (const session of this.sessions.values()) {
13330
13786
  session._markDisconnected();
13331
13787
  }
13332
13788
  this.sessions.clear();
13333
13789
  this.githubTokenProviders.clear();
13334
13790
  this.requestAdapter?.cancelPending();
13791
+ connection.dispose();
13335
13792
  };
13336
- this.connection.onClose(markDisconnected);
13793
+ this.connection.onClose(() => {
13794
+ if (messageWriter) messageWriter.suppressWriteErrors = true;
13795
+ void connection.drain().then(markDisconnected);
13796
+ });
13337
13797
  this.connection.onError(() => {
13338
13798
  if (this.connection === connection) {
13339
13799
  this.state = "disconnected";
@@ -13451,6 +13911,7 @@ stderr: ${stderrOutput}` : ""}`
13451
13911
  }
13452
13912
  };
13453
13913
  export {
13914
+ AhpHost,
13454
13915
  BuiltInTools,
13455
13916
  Canvas,
13456
13917
  CanvasError,
@@ -13,6 +13,7 @@ import type { ExtensionLaunchProviderHandler as GeneratedExtensionLaunchProvider
13
13
  import type { ToolSet } from "./toolSet.js";
14
14
  export type { RemoteSessionMode } from "./generated/rpc.js";
15
15
  export type { CurrentToolMetadata } from "./generated/rpc.js";
16
+ export type { SandboxConfigSource } from "./generated/rpc.js";
16
17
  export type { ConnectorAccountRequest, ConnectorAvailability, ConnectorCapabilities, ConnectorCatalogEntry, ConnectorCatalogResult, ConnectorCatalogStatus, ConnectorConnectRequest, ConnectorConnectResult, ConnectorContinueRequest, ConnectorDisconnectResult, ConnectorMcpStatus, ConnectorReconcileRequest, ConnectorRuntimeStatus, ConnectorStatus, ExtensionLaunchProfile, ExtensionLaunchProviderResolveRequest, ExtensionLaunchProviderResolveResult, GitHubTokenAcquireReason, GitHubTokenAcquireResult, GitHubTelemetryNotification, GitHubTelemetryEvent, GitHubTelemetryClientInfo, InstallationConfirmationRequest, InstallationConfirmationResponse, InstallationDecision, InstallationReview, McpInstallationReview, } from "./generated/rpc.js";
17
18
  /**
18
19
  * Arguments passed to a session's {@link GitHubTokenProvider}.
@@ -1839,6 +1840,8 @@ export type AskUserVariant = "legacy" | "elicitation";
1839
1840
  * an existing one).
1840
1841
  */
1841
1842
  export interface SessionConfigBase {
1843
+ /** Exact model IDs allowed by the host. Omission preserves runtime policy. */
1844
+ allowedModels?: string[];
1842
1845
  /**
1843
1846
  * Client name to identify the application using the SDK.
1844
1847
  * Included in the User-Agent header for API requests.