@github/copilot-sdk 1.0.25 → 1.0.26-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.
@@ -311,6 +311,7 @@ class CopilotSession {
311
311
  disconnected = false;
312
312
  pendingResponseWaits = /* @__PURE__ */ new Set();
313
313
  pendingStructuredWaits = /* @__PURE__ */ new Set();
314
+ completionRpcUnsupported = false;
314
315
  disconnecting = false;
315
316
  onDisconnected;
316
317
  /** @internal Client session API handlers, populated by CopilotClient during create/resume. */
@@ -512,9 +513,22 @@ class CopilotSession {
512
513
  }
513
514
  async send(optionsOrPrompt) {
514
515
  const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
516
+ return this.sendMessage(options);
517
+ }
518
+ async sendMessage(options, cancelled) {
519
+ const message = await this.createSendParams(options);
520
+ if (cancelled?.()) {
521
+ throw new Error("Completion wait cancelled");
522
+ }
515
523
  const response = await this.connection.sendRequest("session.send", {
516
- ...await (0, import_telemetry.getTraceContext)(this.traceContextProvider),
517
524
  sessionId: this.sessionId,
525
+ ...message
526
+ });
527
+ return response.messageId;
528
+ }
529
+ async createSendParams(options) {
530
+ return {
531
+ ...await (0, import_telemetry.getTraceContext)(this.traceContextProvider),
518
532
  prompt: options.prompt,
519
533
  source: options.source,
520
534
  displayPrompt: options.displayPrompt,
@@ -532,8 +546,7 @@ class CopilotSession {
532
546
  }
533
547
  }
534
548
  } : {}
535
- });
536
- return response.messageId;
549
+ };
537
550
  }
538
551
  async sendAndWait(optionsOrPrompt, schemaOrTimeout, timeout) {
539
552
  const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
@@ -554,10 +567,39 @@ class CopilotSession {
554
567
  "Structured output cannot be requested on an immediate steering message."
555
568
  );
556
569
  }
570
+ const startedAt = Date.now();
571
+ const completion = await this.sendWithCompletion(
572
+ typedSchema ? { ...options, responseSchema: typedSchema } : options,
573
+ effectiveTimeout
574
+ );
575
+ if (completion !== void 0) {
576
+ const message = completion.response;
577
+ if (message !== void 0 && (message.type !== "assistant.message" || message.agentId)) {
578
+ throw new Error("The runtime returned a non-assistant response.");
579
+ }
580
+ if (typedSchema || options.responseSchema) {
581
+ if (completion.aborted) {
582
+ throw new Error(
583
+ "The requested run was aborted before a structured result was completed."
584
+ );
585
+ }
586
+ if (!message || message.data.toolRequests?.length || !message.data.content.trim()) {
587
+ throw new Error(
588
+ "The requested run completed without a structured assistant response."
589
+ );
590
+ }
591
+ return typedSchema ? typedSchema.parse(JSON.parse(message.data.content)) : message;
592
+ }
593
+ if (completion.aborted) {
594
+ throw new Error("The requested run was aborted before completion.");
595
+ }
596
+ return message;
597
+ }
598
+ const remainingTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
557
599
  if (typedSchema || options.responseSchema) {
558
600
  const message = await this.sendAndWaitForStructuredMessage(
559
601
  typedSchema ? { ...options, responseSchema: typedSchema } : options,
560
- effectiveTimeout
602
+ remainingTimeout
561
603
  );
562
604
  if (typedSchema) {
563
605
  if (!message) {
@@ -627,14 +669,8 @@ class CopilotSession {
627
669
  };
628
670
  this.pendingResponseWaits.add(fail);
629
671
  let timeoutId;
672
+ let waiting = true;
630
673
  try {
631
- messageId = await this.send(options);
632
- admitted = true;
633
- for (const event of buffered) {
634
- if (event instanceof Error) fail(event);
635
- else observe(event);
636
- }
637
- buffered.length = 0;
638
674
  const timeoutPromise = new Promise((_, reject) => {
639
675
  timeoutId = setTimeout(
640
676
  () => reject(
@@ -642,15 +678,28 @@ class CopilotSession {
642
678
  `Timeout after ${effectiveTimeout}ms waiting for response completion`
643
679
  )
644
680
  ),
645
- effectiveTimeout
681
+ remainingTimeout
646
682
  );
647
683
  });
648
- const outcome = await Promise.race([outcomePromise, timeoutPromise]);
684
+ const outcome = await Promise.race([
685
+ this.sendMessage(options, () => !waiting).then((id) => {
686
+ messageId = id;
687
+ admitted = true;
688
+ for (const event of buffered) {
689
+ if (event instanceof Error) fail(event);
690
+ else observe(event);
691
+ }
692
+ buffered.length = 0;
693
+ return outcomePromise;
694
+ }),
695
+ timeoutPromise
696
+ ]);
649
697
  if (outcome.kind === "error") {
650
698
  throw outcome.error;
651
699
  }
652
700
  return outcome.message;
653
701
  } finally {
702
+ waiting = false;
654
703
  if (timeoutId !== void 0) {
655
704
  clearTimeout(timeoutId);
656
705
  }
@@ -659,6 +708,81 @@ class CopilotSession {
659
708
  this.pendingResponseWaits.delete(fail);
660
709
  }
661
710
  }
711
+ async sendWithCompletion(options, timeout) {
712
+ if (this.completionRpcUnsupported) return void 0;
713
+ if (this.disconnected) throw new Error("Session is disconnected");
714
+ const cancellation = new import_node.CancellationTokenSource();
715
+ const dispatched = /* @__PURE__ */ new Set();
716
+ let completionEventId;
717
+ let resolveDispatched;
718
+ const dispatchCompleted = new Promise((resolve) => {
719
+ resolveDispatched = resolve;
720
+ });
721
+ const unsubscribe = this.on((event) => {
722
+ if (event.agentId || event.type !== "session.idle" && event.type !== "session.autopilot_stopped" && event.type !== "session.error") {
723
+ return;
724
+ }
725
+ dispatched.add(event.id);
726
+ if (event.id === completionEventId) resolveDispatched();
727
+ });
728
+ const drain = async (id) => {
729
+ if (id && !dispatched.has(id)) {
730
+ completionEventId = id;
731
+ await dispatchCompleted;
732
+ }
733
+ };
734
+ let fail;
735
+ const stopped = new Promise((_, reject) => {
736
+ fail = reject;
737
+ });
738
+ this.pendingResponseWaits.add(fail);
739
+ const timer = setTimeout(
740
+ () => fail(new Error(`Timeout after ${timeout}ms waiting for session.idle`)),
741
+ timeout
742
+ );
743
+ const operation = async () => {
744
+ try {
745
+ const message = await this.createSendParams(options);
746
+ if (cancellation.token.isCancellationRequested) {
747
+ throw new Error("Completion wait cancelled");
748
+ }
749
+ const result = await this.connection.sendRequest(
750
+ "session.sendAndWait",
751
+ {
752
+ sessionId: this.sessionId,
753
+ ...message
754
+ },
755
+ cancellation.token
756
+ );
757
+ await drain(result.completionEventId);
758
+ return result;
759
+ } catch (error) {
760
+ if (error instanceof import_node.ResponseError && error.code === import_node.ErrorCodes.MethodNotFound) {
761
+ this.completionRpcUnsupported = true;
762
+ return void 0;
763
+ }
764
+ if (error instanceof import_node.ResponseError && typeof error.data === "object" && error.data !== null) {
765
+ if ("stack" in error.data && typeof error.data.stack === "string") {
766
+ error.stack = error.data.stack;
767
+ }
768
+ if ("completionEventId" in error.data && typeof error.data.completionEventId === "string") {
769
+ await drain(error.data.completionEventId);
770
+ }
771
+ }
772
+ throw error;
773
+ }
774
+ };
775
+ try {
776
+ return await Promise.race([operation(), stopped]);
777
+ } finally {
778
+ clearTimeout(timer);
779
+ unsubscribe();
780
+ this.pendingResponseWaits.delete(fail);
781
+ resolveDispatched();
782
+ cancellation.cancel();
783
+ cancellation.dispose();
784
+ }
785
+ }
662
786
  async sendAndWaitForStructuredMessage(options, timeout) {
663
787
  if (this.disconnected) {
664
788
  throw new Error("Session is disconnected");
@@ -708,12 +832,13 @@ class CopilotSession {
708
832
  }
709
833
  });
710
834
  this.pendingStructuredWaits.add(fail);
835
+ let waiting = true;
711
836
  const timer = setTimeout(
712
837
  () => fail(new Error(`Timeout after ${timeout}ms waiting for the structured response`)),
713
838
  timeout
714
839
  );
715
840
  try {
716
- const sendOutcome = this.send(options).then(
841
+ const sendOutcome = this.sendMessage(options, () => !waiting).then(
717
842
  (id) => {
718
843
  if (!id) {
719
844
  throw new Error(
@@ -734,6 +859,7 @@ class CopilotSession {
734
859
  if (outcome.kind === "error") throw outcome.error;
735
860
  return outcome.message;
736
861
  } finally {
862
+ waiting = false;
737
863
  clearTimeout(timer);
738
864
  buffered.length = 0;
739
865
  unsubscribe();
@@ -1,2 +1,2 @@
1
- export declare const COPILOT_CLI_VERSION = "1.0.102";
1
+ export declare const COPILOT_CLI_VERSION = "1.0.103-0";
2
2
  export declare const COPILOT_CLI_USE_NPM_PACKAGE = false;
@@ -1,4 +1,4 @@
1
- const COPILOT_CLI_VERSION = "1.0.102";
1
+ const COPILOT_CLI_VERSION = "1.0.103-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
@@ -20,6 +20,8 @@ export declare class CopilotClient {
20
20
  private sessions;
21
21
  private hostExitCallbacks;
22
22
  private hostSessionFactories;
23
+ private hostAuthenticators;
24
+ private hostAuthentications;
23
25
  private hostHandoffs;
24
26
  private stderrBuffer;
25
27
  /** Resolved connection mode chosen in the constructor. */
package/dist/client.js CHANGED
@@ -18,7 +18,9 @@ import {
18
18
  } from "./generated/rpc.js";
19
19
  import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
20
20
  import { CopilotSession } from "./session.js";
21
- import { AhpHost } from "./host.js";
21
+ import {
22
+ AhpHost
23
+ } from "./host.js";
22
24
  import { ensureRuntimeBundle } from "./runtimeArtifacts.js";
23
25
  import { COPILOT_CLI_VERSION } from "./cliVersion.js";
24
26
  import { createSessionFsAdapter } from "./sessionFsProvider.js";
@@ -273,6 +275,8 @@ class CopilotClient {
273
275
  sessions = /* @__PURE__ */ new Map();
274
276
  hostExitCallbacks = /* @__PURE__ */ new Map();
275
277
  hostSessionFactories = /* @__PURE__ */ new Map();
278
+ hostAuthenticators = /* @__PURE__ */ new Map();
279
+ hostAuthentications = /* @__PURE__ */ new Map();
276
280
  hostHandoffs = /* @__PURE__ */ new Map();
277
281
  stderrBuffer = "";
278
282
  // Captures CLI stderr for error messages
@@ -477,6 +481,7 @@ class CopilotClient {
477
481
  const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
478
482
  this.connectionExtraArgs = [...connArgs];
479
483
  this.options = {
484
+ entra: options.entra ? { ...options.entra } : void 0,
480
485
  workingDirectory: options.workingDirectory ?? process.cwd(),
481
486
  logLevel: options.logLevel,
482
487
  gitHubToken: options.gitHubToken,
@@ -485,7 +490,6 @@ class CopilotClient {
485
490
  telemetry: options.telemetry,
486
491
  baseDirectory: options.baseDirectory,
487
492
  sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
488
- enableRemoteSessions: options.enableRemoteSessions ?? false,
489
493
  mode: options.mode ?? "copilot-cli",
490
494
  clientInfo: options.clientInfo
491
495
  };
@@ -1399,7 +1403,6 @@ class CopilotClient {
1399
1403
  memory: config.memory,
1400
1404
  gitHubToken: config.gitHubToken,
1401
1405
  gitHubTokenProviderRegistrationId,
1402
- remoteSession: config.remoteSession,
1403
1406
  cloud: config.cloud,
1404
1407
  featureFlags: config.featureFlags,
1405
1408
  expAssignments: config.expAssignments,
@@ -1675,7 +1678,6 @@ class CopilotClient {
1675
1678
  continuePendingWork: config.continuePendingWork,
1676
1679
  gitHubToken: config.gitHubToken,
1677
1680
  gitHubTokenProviderRegistrationId,
1678
- remoteSession: config.remoteSession,
1679
1681
  openCanvases: config.openCanvases,
1680
1682
  featureFlags: config.featureFlags,
1681
1683
  expAssignments: config.expAssignments,
@@ -1745,6 +1747,18 @@ class CopilotClient {
1745
1747
  * @experimental
1746
1748
  */
1747
1749
  async startAhpHost(options) {
1750
+ const authentication = options?.authentication;
1751
+ if (!authentication || !["github", "none", "custom"].includes(authentication.mode)) {
1752
+ throw new Error("An explicit authentication mode is required");
1753
+ }
1754
+ if (authentication.mode === "custom" && (typeof authentication.requireIdentity !== "string" || !authentication.requireIdentity.trim() || typeof authentication.authenticate !== "function")) {
1755
+ throw new Error(
1756
+ "Custom authentication requires a nonempty requireIdentity and authenticate callback"
1757
+ );
1758
+ }
1759
+ if (authentication.mode !== "github" && options.githubEnvironment) {
1760
+ throw new Error("githubEnvironment requires github authentication");
1761
+ }
1748
1762
  if (!options?.localServer && !options?.githubEnvironment) {
1749
1763
  throw new Error("At least one of localServer or githubEnvironment is required");
1750
1764
  }
@@ -1753,8 +1767,12 @@ class CopilotClient {
1753
1767
  }
1754
1768
  const rpc = this.rpc;
1755
1769
  const hostId = randomUUID();
1770
+ if (authentication.mode === "custom") {
1771
+ this.hostAuthenticators.set(hostId, { ...authentication });
1772
+ }
1756
1773
  const {
1757
1774
  computeId,
1775
+ allowCreatingNewSessions,
1758
1776
  localServer,
1759
1777
  githubEnvironment,
1760
1778
  onExit,
@@ -1766,7 +1784,11 @@ class CopilotClient {
1766
1784
  this.hostSessionFactories.set(hostId, {
1767
1785
  createSession,
1768
1786
  resumeSession,
1769
- onSessionReleased
1787
+ onSessionReleased,
1788
+ policy: {
1789
+ mode: authentication.mode,
1790
+ requireIdentity: authentication.mode === "custom" ? authentication.requireIdentity : void 0
1791
+ }
1770
1792
  });
1771
1793
  }
1772
1794
  if (onExit) {
@@ -1775,11 +1797,13 @@ class CopilotClient {
1775
1797
  try {
1776
1798
  const info = await rpc.host.start({
1777
1799
  hostId,
1800
+ authentication: authentication.mode === "custom" ? { mode: "custom", requireIdentity: authentication.requireIdentity } : { mode: authentication.mode },
1778
1801
  computeId,
1779
1802
  localServer,
1780
1803
  githubEnvironment,
1781
1804
  sessionFactory: createSession ? true : void 0,
1782
- resumeFactory: resumeSession ? true : void 0
1805
+ resumeFactory: resumeSession ? true : void 0,
1806
+ allowCreatingNewSessions
1783
1807
  });
1784
1808
  return new AhpHost(
1785
1809
  info,
@@ -1788,7 +1812,13 @@ class CopilotClient {
1788
1812
  },
1789
1813
  (sessionId) => rpc.host.publishSession({ hostId, sessionId }),
1790
1814
  () => rpc.host.listSessions({ hostId }),
1791
- (sessionId) => rpc.host.unpublishSession({ hostId, sessionId })
1815
+ (sessionId) => rpc.host.unpublishSession({ hostId, sessionId }),
1816
+ async (allowed) => {
1817
+ await rpc.host.setSessionCreationPolicy({
1818
+ hostId,
1819
+ allowCreatingNewSessions: allowed
1820
+ });
1821
+ }
1792
1822
  );
1793
1823
  } catch (error) {
1794
1824
  this.hostExitCallbacks.delete(hostId);
@@ -1814,7 +1844,8 @@ class CopilotClient {
1814
1844
  disconnectHosts() {
1815
1845
  for (const hostId of /* @__PURE__ */ new Set([
1816
1846
  ...this.hostExitCallbacks.keys(),
1817
- ...this.hostSessionFactories.keys()
1847
+ ...this.hostSessionFactories.keys(),
1848
+ ...this.hostAuthenticators.keys()
1818
1849
  ])) {
1819
1850
  this.handleHostExit({
1820
1851
  hostId,
@@ -1824,6 +1855,13 @@ class CopilotClient {
1824
1855
  }
1825
1856
  }
1826
1857
  releaseHostSessions(hostId) {
1858
+ this.hostAuthenticators.delete(hostId);
1859
+ for (const [requestId, entry] of this.hostAuthentications) {
1860
+ if (entry.hostId === hostId) {
1861
+ this.hostAuthentications.delete(requestId);
1862
+ entry.controller.abort();
1863
+ }
1864
+ }
1827
1865
  this.hostSessionFactories.delete(hostId);
1828
1866
  for (const [handoffId, entry] of this.hostHandoffs) {
1829
1867
  if (entry.hostId === hostId) this.releaseHostSession(handoffId);
@@ -1852,10 +1890,19 @@ class CopilotClient {
1852
1890
  }
1853
1891
  }
1854
1892
  async createHostSession(params) {
1893
+ const authentication = params.authentication;
1894
+ if (!authentication) {
1895
+ throw new Error("AHP session handoff authentication is required");
1896
+ }
1855
1897
  const options = this.hostSessionFactories.get(params.hostId);
1856
1898
  if (!(params.resume ? options?.resumeSession : options?.createSession) || this.hostHandoffs.has(params.handoffId)) {
1857
1899
  throw new Error("AHP session factory is unavailable or handoff already exists");
1858
1900
  }
1901
+ const policy = options.policy;
1902
+ const identity = authentication.identity;
1903
+ if (authentication.mode !== policy.mode || (authentication.mode === "none" ? identity !== void 0 : typeof identity !== "string" || !identity.trim()) || policy.mode === "custom" && identity !== policy.requireIdentity) {
1904
+ throw new Error("AHP trusted authentication does not match host policy");
1905
+ }
1859
1906
  const entry = {
1860
1907
  hostId: params.hostId,
1861
1908
  requestedSessionId: params.config.sessionId,
@@ -1882,12 +1929,14 @@ class CopilotClient {
1882
1929
  const { sessionId, ...resumeConfig } = config;
1883
1930
  if (!sessionId) throw new Error("AHP resume requires sessionId");
1884
1931
  return options.resumeSession({
1932
+ authentication,
1885
1933
  sessionId,
1886
1934
  config: resumeConfig,
1887
1935
  signal: entry.controller.signal
1888
1936
  });
1889
1937
  }
1890
1938
  return options.createSession({
1939
+ authentication,
1891
1940
  config,
1892
1941
  signal: entry.controller.signal
1893
1942
  });
@@ -2034,6 +2083,7 @@ class CopilotClient {
2034
2083
  let serverVersion;
2035
2084
  try {
2036
2085
  const connectParams = {
2086
+ entra: this.options.entra,
2037
2087
  token: this.effectiveConnectionToken,
2038
2088
  supportedTaskKinds: ["agent", "client", "shell"]
2039
2089
  };
@@ -2048,6 +2098,9 @@ class CopilotClient {
2048
2098
  serverVersion = result.protocolVersion;
2049
2099
  } catch (err) {
2050
2100
  if (err instanceof ResponseError && (err.code === ErrorCodes.MethodNotFound || err.message === "Unhandled method connect")) {
2101
+ if (this.options.entra) {
2102
+ throw new Error("This runtime does not support Entra client configuration.");
2103
+ }
2051
2104
  serverVersion = (await raceAgainstExit(this.ping())).protocolVersion;
2052
2105
  } else {
2053
2106
  throw err;
@@ -2334,9 +2387,6 @@ class CopilotClient {
2334
2387
  this.options.sessionIdleTimeoutSeconds.toString()
2335
2388
  );
2336
2389
  }
2337
- if (this.options.enableRemoteSessions) {
2338
- args.push("--remote");
2339
- }
2340
2390
  const envWithoutNodeDebug = this.buildRuntimeEnv();
2341
2391
  if (!this.resolvedCliPath) {
2342
2392
  throw new Error(
@@ -2522,9 +2572,6 @@ stderr: ${this.stderrBuffer.trim().slice(-8192) || "(empty)"}`
2522
2572
  if (this.options.sessionIdleTimeoutSeconds > 0) {
2523
2573
  args.push("--session-idle-timeout", this.options.sessionIdleTimeoutSeconds.toString());
2524
2574
  }
2525
- if (this.options.enableRemoteSessions) {
2526
- args.push("--remote");
2527
- }
2528
2575
  if (cliLoginSyncIntegrationId) {
2529
2576
  args.push(`--cli-login-sync-integration-id=${cliLoginSyncIntegrationId}`);
2530
2577
  }
@@ -2700,6 +2747,49 @@ stderr: ${stderrOutput}` : ""}`
2700
2747
  "host.materializeSession",
2701
2748
  (params) => this.createHostSession(params)
2702
2749
  );
2750
+ this.connection.onRequest(
2751
+ "host.validateAuthentication",
2752
+ async (params) => {
2753
+ const authenticator = this.hostAuthenticators.get(params.hostId);
2754
+ if (!authenticator || this.hostAuthentications.has(params.requestId)) {
2755
+ throw new Error("AHP authenticator is unavailable or request already exists");
2756
+ }
2757
+ const entry = { hostId: params.hostId, controller: new AbortController() };
2758
+ this.hostAuthentications.set(params.requestId, entry);
2759
+ const signal = entry.controller.signal;
2760
+ const cancelled = new Promise((_resolve, reject) => {
2761
+ signal.addEventListener(
2762
+ "abort",
2763
+ () => reject(new Error("AHP authentication ended")),
2764
+ { once: true }
2765
+ );
2766
+ });
2767
+ try {
2768
+ const result = await Promise.race([
2769
+ Promise.resolve().then(
2770
+ () => authenticator.authenticate({ token: params.token, signal })
2771
+ ),
2772
+ cancelled
2773
+ ]);
2774
+ if (signal.aborted || this.hostAuthenticators.get(params.hostId) !== authenticator) {
2775
+ throw new Error("AHP authentication ended");
2776
+ }
2777
+ return result;
2778
+ } finally {
2779
+ if (this.hostAuthentications.get(params.requestId) === entry) {
2780
+ this.hostAuthentications.delete(params.requestId);
2781
+ }
2782
+ entry.controller.abort();
2783
+ }
2784
+ }
2785
+ );
2786
+ this.connection.onNotification(
2787
+ "host.authenticationCancelled",
2788
+ (params) => {
2789
+ const entry = this.hostAuthentications.get(params.requestId);
2790
+ if (entry?.hostId === params.hostId) entry.controller.abort();
2791
+ }
2792
+ );
2703
2793
  this.connection.onNotification(
2704
2794
  "host.sessionReleased",
2705
2795
  (params) => {