@github/copilot-sdk 1.0.14 → 1.0.15-preview.0

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.
@@ -225,7 +225,15 @@ function createServerRpc(connection) {
225
225
  *
226
226
  * @returns Outcome of a catalog.search call: either bounded inert candidates, or one typed refusal. Never a partial success.
227
227
  */
228
- search: async (params) => connection.sendRequest("catalog.search", params)
228
+ search: async (params) => connection.sendRequest("catalog.search", params),
229
+ /**
230
+ * Terminates one retained catalog selection group. A selected outcome returns the native host a fresh single-use candidate handle plus the original searchId for a later explicit mcp.planInstall call; non-selected outcomes release the group without producing a planning input. Candidate state, cards, URLs, credentials and private identifiers remain inside the runtime. The model-facing catalog_select tool projects the result separately and never exposes the candidate handle or searchId.
231
+ *
232
+ * @param params Terminates one retained catalog selection group through an opaque reference previously returned by the model-safe search projection.
233
+ *
234
+ * @returns Typed outcome of catalog.select. Only the selected host result carries a fresh candidate handle; the model-facing projection removes both that handle and searchId.
235
+ */
236
+ select: async (params) => connection.sendRequest("catalog.select", params)
229
237
  },
230
238
  /** @experimental */
231
239
  plugins: {
@@ -456,7 +464,7 @@ function createServerRpc(connection) {
456
464
  /**
457
465
  * Registers an SDK client as the session filesystem provider.
458
466
  *
459
- * @param params Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider.
467
+ * @param params Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. A registered provider is authoritative for path interpretation and filesystem facts used by workspace permission validation. Paths are interpreted lexically; home-relative paths (`~` and `~/...`) and Windows drive-relative paths such as `C:foo` are unsupported. Until provider-side canonicalization is supported, providers must not expose symlinks inside allowed roots that escape those roots.
460
468
  *
461
469
  * @returns Indicates whether the calling client was registered as the session filesystem provider.
462
470
  */
@@ -1020,6 +1028,108 @@ function createSessionRpc(connection, sessionId) {
1020
1028
  }
1021
1029
  },
1022
1030
  /** @experimental */
1031
+ workflow: {
1032
+ /**
1033
+ * Runs a registered dynamic workflow by name at the top level.
1034
+ *
1035
+ * @param params Parameters for invoking a registered workflow.
1036
+ *
1037
+ * @returns Complete current or terminal workflow run envelope.
1038
+ */
1039
+ run: async (params) => connection.sendRequest("session.workflow.run", { sessionId, ...params }),
1040
+ /**
1041
+ * Resumes a dynamic workflow run using its persisted name, arguments, journal, and accounting.
1042
+ *
1043
+ * @param params Parameters for resuming a workflow run from its persisted identity.
1044
+ *
1045
+ * @returns Resolved persisted workflow identity and resumed run envelope.
1046
+ */
1047
+ resume: async (params) => connection.sendRequest("session.workflow.resume", { sessionId, ...params }),
1048
+ /**
1049
+ * Gets the current or settled envelope for a dynamic workflow run.
1050
+ *
1051
+ * @param params Parameters for retrieving a workflow run.
1052
+ *
1053
+ * @returns Complete current or terminal workflow run envelope.
1054
+ */
1055
+ getRun: async (params) => connection.sendRequest("session.workflow.getRun", { sessionId, ...params }),
1056
+ /**
1057
+ * Lists durable dynamic workflow runs for this session in creation order.
1058
+ *
1059
+ * @param params Parameters for paging workflow runs.
1060
+ *
1061
+ * @returns A page of workflow runs in durable creation order.
1062
+ */
1063
+ listRuns: async (params) => connection.sendRequest("session.workflow.listRuns", { sessionId, ...params }),
1064
+ /**
1065
+ * Gets durable and live observability detail for one dynamic workflow run.
1066
+ *
1067
+ * @param params Parameters for retrieving a workflow run.
1068
+ *
1069
+ * @returns Full workflow run observability detail.
1070
+ */
1071
+ getRunDetail: async (params) => connection.sendRequest("session.workflow.getRunDetail", { sessionId, ...params }),
1072
+ /**
1073
+ * Pages durable progress for one dynamic workflow run.
1074
+ *
1075
+ * @param params Parameters for paging workflow progress.
1076
+ *
1077
+ * @returns A bidirectional page of workflow progress.
1078
+ */
1079
+ getRunProgress: async (params) => connection.sendRequest("session.workflow.getRunProgress", { sessionId, ...params }),
1080
+ /**
1081
+ * Requests cancellation of a dynamic workflow run and returns its run envelope.
1082
+ *
1083
+ * @param params Parameters for cancelling a workflow run.
1084
+ *
1085
+ * @returns Complete current or terminal workflow run envelope.
1086
+ */
1087
+ cancel: async (params) => connection.sendRequest("session.workflow.cancel", { sessionId, ...params }),
1088
+ /**
1089
+ * Pauses a running dynamic workflow and returns its settled run envelope.
1090
+ *
1091
+ * @param params Parameters for pausing a running workflow.
1092
+ *
1093
+ * @returns Complete current or terminal workflow run envelope.
1094
+ */
1095
+ pause: async (params) => connection.sendRequest("session.workflow.pause", { sessionId, ...params }),
1096
+ /**
1097
+ * Records a batch of ordered dynamic workflow progress lines.
1098
+ *
1099
+ * @param params Parameters for recording workflow progress.
1100
+ *
1101
+ * @returns Acknowledgement that a workflow request was accepted.
1102
+ */
1103
+ log: async (params) => connection.sendRequest("session.workflow.log", { sessionId, ...params }),
1104
+ /**
1105
+ * Runs one dynamic-workflow-scoped subagent and returns its result.
1106
+ *
1107
+ * @param params Parameters for one workflow-scoped subagent call.
1108
+ *
1109
+ * @returns Result of one workflow-scoped subagent call.
1110
+ */
1111
+ agent: async (params) => connection.sendRequest("session.workflow.agent", { sessionId, ...params }),
1112
+ /** @experimental */
1113
+ journal: {
1114
+ /**
1115
+ * Reads a memoized dynamic workflow journal entry.
1116
+ *
1117
+ * @param params Parameters for reading a workflow journal entry.
1118
+ *
1119
+ * @returns Result of reading a workflow journal entry.
1120
+ */
1121
+ get: async (params) => connection.sendRequest("session.workflow.journal.get", { sessionId, ...params }),
1122
+ /**
1123
+ * Stores a memoized dynamic workflow journal entry.
1124
+ *
1125
+ * @param params Parameters for storing a workflow journal entry.
1126
+ *
1127
+ * @returns Acknowledgement that a workflow request was accepted.
1128
+ */
1129
+ put: async (params) => connection.sendRequest("session.workflow.journal.put", { sessionId, ...params })
1130
+ }
1131
+ },
1132
+ /** @experimental */
1023
1133
  model: {
1024
1134
  /**
1025
1135
  * Gets the session's authoritative model snapshot, including the committed Auto preference and any newer unclaimed Auto preference waiting for a future user turn.
@@ -1725,13 +1835,97 @@ function createSessionRpc(connection, sessionId) {
1725
1835
  }
1726
1836
  },
1727
1837
  /** @experimental */
1838
+ managedSettings: {
1839
+ /**
1840
+ * Waits for the live session's in-flight managed-settings application, then returns the retained effective snapshot used by runtime enforcement and by `session.managed_settings_resolved`. It does not perform another account, device, or server resolution, and rejects when resolution has not produced a snapshot.
1841
+ *
1842
+ * @returns Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values, then the policy helper, per ordinary key, while permissions compose restrictively across device, server, policy-helper, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes.
1843
+ */
1844
+ get: async () => connection.sendRequest("session.managedSettings.get", { sessionId })
1845
+ },
1846
+ /** @experimental */
1728
1847
  plugins: {
1729
1848
  /**
1730
- * Lists plugins installed for the session.
1849
+ * Lists globally installed, live, built-in, and enterprise-managed desired plugins using the live session's authoritative account, working directory, and retained managed policy.
1731
1850
  *
1732
1851
  * @returns Plugins installed for the session, with their enabled state and version metadata.
1733
1852
  */
1734
1853
  list: async () => connection.sendRequest("session.plugins.list", { sessionId }),
1854
+ /**
1855
+ * Installs a plugin using the live session's authoritative account, working directory, and retained managed policy.
1856
+ *
1857
+ * @param params Plugin source resolved relative to the session's authoritative working directory.
1858
+ *
1859
+ * @returns Result of installing a plugin.
1860
+ */
1861
+ install: async (params) => connection.sendRequest("session.plugins.install", { sessionId, ...params }),
1862
+ /**
1863
+ * Uninstalls a plugin when permitted by the live session's retained managed policy.
1864
+ *
1865
+ * @param params Name (or spec) of the plugin to uninstall.
1866
+ */
1867
+ uninstall: async (params) => connection.sendRequest("session.plugins.uninstall", { sessionId, ...params }),
1868
+ /**
1869
+ * Updates an installed plugin using the live session's authoritative account, working directory, and retained managed policy.
1870
+ *
1871
+ * @param params Name (or spec) of the plugin to update.
1872
+ *
1873
+ * @returns Result of updating a single plugin.
1874
+ */
1875
+ update: async (params) => connection.sendRequest("session.plugins.update", { sessionId, ...params }),
1876
+ /**
1877
+ * Enables installed plugins when permitted by the live session's retained managed policy.
1878
+ *
1879
+ * @param params Plugin names (or specs) to enable in the session's authoritative working directory.
1880
+ */
1881
+ enable: async (params) => connection.sendRequest("session.plugins.enable", { sessionId, ...params }),
1882
+ /**
1883
+ * Disables installed plugins when permitted by the live session's retained managed policy.
1884
+ *
1885
+ * @param params Plugin names (or specs) to disable in the session's authoritative working directory.
1886
+ */
1887
+ disable: async (params) => connection.sendRequest("session.plugins.disable", { sessionId, ...params }),
1888
+ /** @experimental */
1889
+ marketplaces: {
1890
+ /**
1891
+ * Lists registered and enterprise-managed desired marketplaces using the live session's retained policy.
1892
+ *
1893
+ * @returns All registered marketplaces, including built-in defaults.
1894
+ */
1895
+ list: async () => connection.sendRequest("session.plugins.marketplaces.list", { sessionId }),
1896
+ /**
1897
+ * Adds a marketplace when permitted by the live session's retained managed policy.
1898
+ *
1899
+ * @param params Marketplace source and optional working directory for relative-path resolution.
1900
+ *
1901
+ * @returns Result of registering a new marketplace.
1902
+ */
1903
+ add: async (params) => connection.sendRequest("session.plugins.marketplaces.add", { sessionId, ...params }),
1904
+ /**
1905
+ * Removes a marketplace when permitted by the live session's retained managed policy.
1906
+ *
1907
+ * @param params Name of the marketplace to remove and an optional force flag.
1908
+ *
1909
+ * @returns Outcome of the remove attempt, including dependent-plugin info when applicable.
1910
+ */
1911
+ remove: async (params) => connection.sendRequest("session.plugins.marketplaces.remove", { sessionId, ...params }),
1912
+ /**
1913
+ * Browses a marketplace resolved through the live session's working directory and retained managed policy.
1914
+ *
1915
+ * @param params Name of the marketplace whose plugin catalog to fetch.
1916
+ *
1917
+ * @returns Plugins advertised by the marketplace.
1918
+ */
1919
+ browse: async (params) => connection.sendRequest("session.plugins.marketplaces.browse", { sessionId, ...params }),
1920
+ /**
1921
+ * Refreshes marketplaces resolved through the live session's working directory and retained managed policy.
1922
+ *
1923
+ * @param params Optional marketplace name; omit to refresh all.
1924
+ *
1925
+ * @returns Result of refreshing one or more marketplace catalogs.
1926
+ */
1927
+ refresh: async (params) => connection.sendRequest("session.plugins.marketplaces.refresh", { sessionId, ...params })
1928
+ },
1735
1929
  /**
1736
1930
  * Reloads the session's plugin set, refreshing MCP servers, custom agents, hooks, and skills cache so SDK-driven changes via `server.plugins.*` take effect immediately.
1737
1931
  *
@@ -2440,6 +2634,22 @@ function createSessionRpc(connection, sessionId) {
2440
2634
  * @returns Result of editing a queued message.
2441
2635
  */
2442
2636
  updateText: async (params) => connection.sendRequest("session.queue.updateText", { sessionId, ...params }),
2637
+ /**
2638
+ * Atomically withdraws an unchanged, unconsumed user message from the local queued or steering lane. A client retaining the original draft may restore it only when removed is true. Does not interrupt the running turn.
2639
+ *
2640
+ * @param params Conditional withdrawal of a single user message, before the runtime claims it for delivery.
2641
+ *
2642
+ * @returns Result of removing a queued item.
2643
+ */
2644
+ withdrawMessage: async (params) => connection.sendRequest("session.queue.withdrawMessage", { sessionId, ...params }),
2645
+ /**
2646
+ * Atomically appends text and attachments to an unchanged, unconsumed local steering message. Returns updated=false if delivery or withdrawal already claimed the message.
2647
+ *
2648
+ * @param params Append to one pending steering message without changing its identity or delivery position.
2649
+ *
2650
+ * @returns Result of editing a queued message.
2651
+ */
2652
+ appendSteering: async (params) => connection.sendRequest("session.queue.appendSteering", { sessionId, ...params }),
2443
2653
  /**
2444
2654
  * Duplicates an addressable queued item immediately after its source.
2445
2655
  *
@@ -2694,6 +2904,31 @@ function createInternalSessionRpc(connection, sessionId) {
2694
2904
  pauseAtCheckpoint: async (params) => connection.sendRequest("session.factory.pauseAtCheckpoint", { sessionId, ...params })
2695
2905
  },
2696
2906
  /** @experimental */
2907
+ workflow: {
2908
+ /**
2909
+ * Internal tool-originated dynamic workflow invocation.
2910
+ *
2911
+ * @param params Internal parameters for invoking a registered workflow from a tool.
2912
+ *
2913
+ * @returns Complete current or terminal workflow run envelope.
2914
+ */
2915
+ runFromTool: async (params) => connection.sendRequest("session.workflow.runFromTool", { sessionId, ...params }),
2916
+ /**
2917
+ * Internal tool-originated dynamic workflow resume.
2918
+ *
2919
+ * @param params Internal parameters for resuming a workflow run from a tool.
2920
+ *
2921
+ * @returns Resolved persisted workflow identity and resumed run envelope.
2922
+ */
2923
+ resumeFromTool: async (params) => connection.sendRequest("session.workflow.resumeFromTool", { sessionId, ...params }),
2924
+ /**
2925
+ * Atomically pauses an owned dynamic workflow attempt at a durable checkpoint.
2926
+ *
2927
+ * @param params Parameters for an owned durable pause checkpoint.
2928
+ */
2929
+ pauseAtCheckpoint: async (params) => connection.sendRequest("session.workflow.pauseAtCheckpoint", { sessionId, ...params })
2930
+ },
2931
+ /** @experimental */
2697
2932
  model: {
2698
2933
  /**
2699
2934
  * Resolves and applies organization-managed and repository model overlays.
@@ -2889,6 +3124,16 @@ function registerClientSessionApiHandlers(connection, getHandlers) {
2889
3124
  if (!handler) throw new Error(`No factory handler registered for session: ${params.sessionId}`);
2890
3125
  return handler.abort(params);
2891
3126
  });
3127
+ connection.onRequest("workflow.execute", async (params) => {
3128
+ const handler = getHandlers(params.sessionId).workflow;
3129
+ if (!handler) throw new Error(`No workflow handler registered for session: ${params.sessionId}`);
3130
+ return handler.execute(params);
3131
+ });
3132
+ connection.onRequest("workflow.abort", async (params) => {
3133
+ const handler = getHandlers(params.sessionId).workflow;
3134
+ if (!handler) throw new Error(`No workflow handler registered for session: ${params.sessionId}`);
3135
+ return handler.abort(params);
3136
+ });
2892
3137
  connection.onRequest("tasks.cancel", async (params) => {
2893
3138
  const handler = getHandlers(params.sessionId).tasks;
2894
3139
  if (!handler) throw new Error(`No tasks handler registered for session: ${params.sessionId}`);
@@ -0,0 +1,40 @@
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 schema_exports = {};
20
+ __export(schema_exports, {
21
+ isResponseSchema: () => isResponseSchema,
22
+ isZodSchema: () => isZodSchema,
23
+ toJsonSchema: () => toJsonSchema
24
+ });
25
+ module.exports = __toCommonJS(schema_exports);
26
+ function isZodSchema(value) {
27
+ return typeof value === "object" && value !== null && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
28
+ }
29
+ function toJsonSchema(schema) {
30
+ return isZodSchema(schema) ? schema.toJSONSchema() : schema;
31
+ }
32
+ function isResponseSchema(value) {
33
+ return isZodSchema(value) && "parse" in value && typeof value.parse === "function";
34
+ }
35
+ // Annotate the CommonJS export names for ESM import in node:
36
+ 0 && (module.exports = {
37
+ isResponseSchema,
38
+ isZodSchema,
39
+ toJsonSchema
40
+ });
@@ -26,6 +26,7 @@ var import_node = require("vscode-jsonrpc/node.js");
26
26
  var import_rpc = require("./generated/rpc.js");
27
27
  var import_canvas = require("./canvas.js");
28
28
  var import_telemetry = require("./telemetry.js");
29
+ var import_schema = require("./schema.js");
29
30
  var import_types = require("./types.js");
30
31
  var import_factory = require("./factory.js");
31
32
  function isFactoryResumeErrorCode(value) {
@@ -277,6 +278,7 @@ class CopilotSession {
277
278
  _capabilities = {};
278
279
  openCanvasInstances = [];
279
280
  disconnected = false;
281
+ pendingStructuredWaits = /* @__PURE__ */ new Set();
280
282
  disconnecting = false;
281
283
  onDisconnected;
282
284
  /** @internal Client session API handlers, populated by CopilotClient during create/resume. */
@@ -493,13 +495,54 @@ class CopilotSession {
493
495
  attachments: options.attachments,
494
496
  mode: options.mode,
495
497
  agentMode: options.agentMode,
496
- requestHeaders: options.requestHeaders
498
+ requestHeaders: options.requestHeaders,
499
+ ...options.responseSchema ? {
500
+ responseFormat: {
501
+ type: "json_schema",
502
+ jsonSchema: {
503
+ name: "response",
504
+ strict: true,
505
+ schema: (0, import_schema.toJsonSchema)(options.responseSchema)
506
+ }
507
+ }
508
+ } : {}
497
509
  });
498
510
  return response.messageId;
499
511
  }
500
- async sendAndWait(optionsOrPrompt, timeout) {
512
+ async sendAndWait(optionsOrPrompt, schemaOrTimeout, timeout) {
501
513
  const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
502
- const effectiveTimeout = timeout ?? 6e4;
514
+ const typedSchema = (0, import_schema.isResponseSchema)(schemaOrTimeout) ? schemaOrTimeout : void 0;
515
+ if (schemaOrTimeout !== void 0 && typeof schemaOrTimeout !== "number" && !typedSchema) {
516
+ throw new TypeError(
517
+ "The second argument must be a timeout or a schema with toJSONSchema() and parse(). Pass raw JSON Schema in options.responseSchema instead."
518
+ );
519
+ }
520
+ const effectiveTimeout = (typeof schemaOrTimeout === "number" ? schemaOrTimeout : timeout) ?? 6e4;
521
+ if (typedSchema && options.responseSchema) {
522
+ throw new Error(
523
+ "Do not specify responseSchema in options when requesting a typed response."
524
+ );
525
+ }
526
+ if (typedSchema && options.mode === "immediate") {
527
+ throw new Error(
528
+ "Structured output cannot be requested on an immediate steering message."
529
+ );
530
+ }
531
+ if (typedSchema || options.responseSchema) {
532
+ const message = await this.sendAndWaitForStructuredMessage(
533
+ typedSchema ? { ...options, responseSchema: typedSchema } : options,
534
+ effectiveTimeout
535
+ );
536
+ if (typedSchema) {
537
+ if (!message) {
538
+ throw new Error(
539
+ "The requested run completed without a structured assistant response."
540
+ );
541
+ }
542
+ return typedSchema.parse(JSON.parse(message.data.content));
543
+ }
544
+ return message;
545
+ }
503
546
  let resolveOutcome;
504
547
  const outcomePromise = new Promise((resolve) => {
505
548
  resolveOutcome = resolve;
@@ -541,12 +584,95 @@ class CopilotSession {
541
584
  unsubscribe();
542
585
  }
543
586
  }
587
+ async sendAndWaitForStructuredMessage(options, timeout) {
588
+ if (this.disconnected) {
589
+ throw new Error("Session is disconnected");
590
+ }
591
+ let resolveOutcome;
592
+ const outcomePromise = new Promise((resolve) => {
593
+ resolveOutcome = resolve;
594
+ });
595
+ const fail = (error) => resolveOutcome({ kind: "error", error });
596
+ let messageId;
597
+ let consumed = false;
598
+ let lastMessage;
599
+ const buffered = [];
600
+ const observe = (event) => {
601
+ if (event.agentId) return;
602
+ if (event.type === "user.message" && event.data.messageId === messageId) {
603
+ consumed = true;
604
+ } else if (event.type === "assistant.message" && event.data.originatingMessageId === messageId) {
605
+ consumed = true;
606
+ lastMessage = event.data.toolRequests?.length ? void 0 : event;
607
+ } else if (consumed && event.type === "session.idle" && event.data.mode !== "autopilot") {
608
+ if (event.data.aborted) {
609
+ fail(
610
+ new Error(
611
+ "The requested run was aborted before a structured result was completed."
612
+ )
613
+ );
614
+ } else {
615
+ resolveOutcome({ kind: "idle", message: lastMessage });
616
+ }
617
+ } else if (consumed && event.type === "session.error") {
618
+ const error = new Error(event.data.message);
619
+ error.stack = event.data.stack;
620
+ fail(error);
621
+ }
622
+ };
623
+ const unsubscribe = this.on((event) => {
624
+ if (event.type !== "user.message" && event.type !== "assistant.message" && event.type !== "session.idle" && event.type !== "session.error") {
625
+ return;
626
+ }
627
+ if (messageId === void 0) {
628
+ buffered.push(event);
629
+ } else {
630
+ observe(event);
631
+ }
632
+ });
633
+ this.pendingStructuredWaits.add(fail);
634
+ const timer = setTimeout(
635
+ () => fail(new Error(`Timeout after ${timeout}ms waiting for the structured response`)),
636
+ timeout
637
+ );
638
+ try {
639
+ const sendOutcome = this.send(options).then(
640
+ (id) => {
641
+ if (!id) {
642
+ throw new Error(
643
+ "The runtime did not return a message ID for the structured send."
644
+ );
645
+ }
646
+ messageId = id;
647
+ for (const event of buffered) observe(event);
648
+ buffered.length = 0;
649
+ return outcomePromise;
650
+ },
651
+ (error) => ({
652
+ kind: "error",
653
+ error: error instanceof Error ? error : new Error(String(error))
654
+ })
655
+ );
656
+ const outcome = await Promise.race([sendOutcome, outcomePromise]);
657
+ if (outcome.kind === "error") throw outcome.error;
658
+ return outcome.message;
659
+ } finally {
660
+ clearTimeout(timer);
661
+ buffered.length = 0;
662
+ unsubscribe();
663
+ this.pendingStructuredWaits.delete(fail);
664
+ }
665
+ }
544
666
  /** @internal */
545
667
  _markDisconnected() {
546
668
  if (this.disconnected) {
547
669
  return;
548
670
  }
549
671
  this.disconnected = true;
672
+ for (const fail of this.pendingStructuredWaits) {
673
+ fail(new Error("Session disconnected while waiting for a structured response"));
674
+ }
675
+ this.pendingStructuredWaits.clear();
550
676
  for (const controller of this.pendingExternalTools.values()) {
551
677
  controller.abort();
552
678
  }
@@ -1,2 +1,2 @@
1
- export declare const COPILOT_CLI_VERSION = "1.0.85";
1
+ export declare const COPILOT_CLI_VERSION = "1.0.87-0";
2
2
  export declare const COPILOT_CLI_USE_NPM_PACKAGE = false;
@@ -1,4 +1,4 @@
1
- const COPILOT_CLI_VERSION = "1.0.85";
1
+ const COPILOT_CLI_VERSION = "1.0.87-0";
2
2
  const COPILOT_CLI_USE_NPM_PACKAGE = false;
3
3
  export {
4
4
  COPILOT_CLI_USE_NPM_PACKAGE,
package/dist/client.d.ts CHANGED
@@ -6,6 +6,7 @@ export declare class CopilotClient {
6
6
  private cliProcess;
7
7
  private ffiHost;
8
8
  private connection;
9
+ private requestAdapter;
9
10
  private messageWriter;
10
11
  private connectionClosed;
11
12
  private socket;
@@ -41,6 +42,7 @@ export declare class CopilotClient {
41
42
  /** Connection-level session filesystem config, set via constructor option. */
42
43
  private sessionFsConfig;
43
44
  private requestHandler;
45
+ private extensionLaunchProvider?;
44
46
  private builtinPluginDirectories;
45
47
  private onGitHubTelemetry?;
46
48
  private clientGlobalHandlers;
package/dist/client.js CHANGED
@@ -23,13 +23,11 @@ import { COPILOT_CLI_VERSION } from "./cliVersion.js";
23
23
  import { createSessionFsAdapter } from "./sessionFsProvider.js";
24
24
  import { createCopilotRequestAdapter } from "./copilotRequestHandler.js";
25
25
  import { getTraceContext } from "./telemetry.js";
26
+ import { toJsonSchema } from "./schema.js";
26
27
  import { ToolSet } from "./toolSet.js";
27
28
  import { defaultJoinSessionPermissionHandler } from "./types.js";
28
29
  const MIN_PROTOCOL_VERSION = 3;
29
30
  const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
30
- function isZodSchema(value) {
31
- return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
32
- }
33
31
  async function withTimeout(promise, timeoutMs, message) {
34
32
  let timeout;
35
33
  try {
@@ -74,13 +72,6 @@ async function waitForChildExit(child, timeoutMs) {
74
72
  }
75
73
  });
76
74
  }
77
- function toJsonSchema(parameters) {
78
- if (!parameters) return void 0;
79
- if (isZodSchema(parameters)) {
80
- return parameters.toJSONSchema();
81
- }
82
- return parameters;
83
- }
84
75
  const DEFAULT_PROVIDER_NAME = "default";
85
76
  function extractBearerTokenProviders(provider, providers) {
86
77
  const callbacks = /* @__PURE__ */ new Map();
@@ -213,6 +204,7 @@ class CopilotClient {
213
204
  cliProcess = null;
214
205
  ffiHost = null;
215
206
  connection = null;
207
+ requestAdapter = null;
216
208
  messageWriter = null;
217
209
  connectionClosed = false;
218
210
  socket = null;
@@ -250,6 +242,7 @@ class CopilotClient {
250
242
  /** Connection-level session filesystem config, set via constructor option. */
251
243
  sessionFsConfig = null;
252
244
  requestHandler = null;
245
+ extensionLaunchProvider;
253
246
  builtinPluginDirectories = [];
254
247
  onGitHubTelemetry;
255
248
  clientGlobalHandlers = {};
@@ -407,6 +400,7 @@ class CopilotClient {
407
400
  this.onGetTraceContext = options.onGetTraceContext;
408
401
  this.sessionFsConfig = options.sessionFs ?? null;
409
402
  this.requestHandler = options.requestHandler ?? null;
403
+ this.extensionLaunchProvider = options.extensionLaunchProvider;
410
404
  this.onGitHubTelemetry = options.onGitHubTelemetry;
411
405
  this.setupClientGlobalHandlers();
412
406
  const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
@@ -509,14 +503,16 @@ class CopilotClient {
509
503
  }
510
504
  setupClientGlobalHandlers() {
511
505
  const handlers = {};
506
+ handlers.extensionLaunchProvider = this.extensionLaunchProvider;
512
507
  if (this.requestHandler) {
513
- handlers.llmInference = createCopilotRequestAdapter(this.requestHandler, () => {
508
+ this.requestAdapter = createCopilotRequestAdapter(this.requestHandler, () => {
514
509
  if (!this.connection) {
515
510
  return void 0;
516
511
  }
517
512
  this._rpc ??= createServerRpc(this.connection);
518
513
  return this._rpc;
519
514
  });
515
+ handlers.llmInference = this.requestAdapter;
520
516
  }
521
517
  if (this.onGitHubTelemetry) {
522
518
  const onGitHubTelemetry = this.onGitHubTelemetry;
@@ -621,6 +617,9 @@ class CopilotClient {
621
617
  }
622
618
  await this.connectToServer();
623
619
  await this.verifyProtocolVersion();
620
+ if (this.extensionLaunchProvider) {
621
+ await this.rpc.registerExtensionLaunchProvider();
622
+ }
624
623
  if (this.builtinPluginDirectories.length > 0) {
625
624
  try {
626
625
  await this.connection.sendRequest("plugins.builtin.set", {
@@ -709,6 +708,7 @@ class CopilotClient {
709
708
  }
710
709
  this.sessions.clear();
711
710
  this.githubTokenProviders.clear();
711
+ this.requestAdapter?.cancelPending();
712
712
  if (this.connection && !this.connectionClosed && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
713
713
  const runtimeShutdownStart = Date.now();
714
714
  const shutdownPromise = this.rpc.runtime.shutdown();
@@ -864,6 +864,7 @@ class CopilotClient {
864
864
  }
865
865
  this.sessions.clear();
866
866
  this.githubTokenProviders.clear();
867
+ this.requestAdapter?.cancelPending();
867
868
  if (this.messageWriter) {
868
869
  this.messageWriter.suppressWriteErrors = true;
869
870
  }
@@ -2272,6 +2273,7 @@ stderr: ${stderrOutput}` : ""}`
2272
2273
  }
2273
2274
  this.sessions.clear();
2274
2275
  this.githubTokenProviders.clear();
2276
+ this.requestAdapter?.cancelPending();
2275
2277
  };
2276
2278
  this.connection.onClose(markDisconnected);
2277
2279
  this.connection.onError(() => {