@github/copilot-sdk 1.0.22 → 1.0.23-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.
package/README.md CHANGED
@@ -128,11 +128,13 @@ await using host = await client.startAhpHost({
128
128
  ...config,
129
129
  onPermissionRequest: approveAll,
130
130
  systemMessage: { mode: "append", content: "Use the app's greeting tool." },
131
- tools: [defineTool("greeting", {
132
- description: "Get the application's greeting",
133
- parameters: { type: "object", properties: {} },
134
- handler: () => "Hello from the application!",
135
- })],
131
+ tools: [
132
+ defineTool("greeting", {
133
+ description: "Get the application's greeting",
134
+ parameters: { type: "object", properties: {} },
135
+ handler: () => "Hello from the application!",
136
+ }),
137
+ ],
136
138
  });
137
139
  },
138
140
  onSessionReleased: async (originalSession) => {
@@ -272,6 +274,18 @@ new CopilotClient(options?: CopilotClientOptions)
272
274
  - `installationConfirmationHandler?: InstallationConfirmationHandler` - Experimental connection-global human review for `installations.confirm`. Receives the typed request and independent request/connection cancellation signals, and returns an explicit decision. Does not enable installation capabilities.
273
275
  - `logLevel?: "none" | "error" | "warning" | "info" | "debug" | "all"` - Log level. When omitted, the runtime uses its own default (currently `"info"`).
274
276
  - `env?: Record<string, string | undefined>` - Environment variables for the runtime process. When omitted, inherits `process.env`.
277
+
278
+ For the native standalone runtime, set `COPILOT_GITHUB_TELEMETRY_DISABLED=true`
279
+ before startup to disable direct GitHub telemetry uploads for the entire host,
280
+ including session and sessionless events. Authentication, SDK
281
+ `onGitHubTelemetry` forwarding, and user-configured OpenTelemetry export remain
282
+ unchanged. The value must be exactly `"true"` and is read at startup; changing
283
+ it requires a runtime restart. With `RuntimeConnection.forInProcess()`, set it
284
+ in the host process environment; per-client `env` is rejected for that
285
+ transport. This is separate from `enableSessionTelemetry: false`, which
286
+ disables only a session's telemetry pipeline. The Node CLI host does not use
287
+ this environment variable.
288
+
275
289
  - `gitHubToken?: string` - GitHub token for authentication. When provided, takes priority over other auth methods.
276
290
  - `useLoggedInUser?: boolean` - Whether to use logged-in user for authentication (default: true, but false when `gitHubToken` is provided). Cannot be used with `RuntimeConnection.forUri`.
277
291
  - `onListModels?: () => Promise<ModelInfo[]> | ModelInfo[]` - Optional model-list provider, useful when using a custom provider.
@@ -1341,6 +1355,8 @@ Inbound trace context from the CLI is available on the `ToolInvocation` object p
1341
1355
 
1342
1356
  An `onPermissionRequest` handler is optional when you create or resume a session. When provided, it is called before the agent executes each tool (file writes, shell commands, custom tools, etc.) and returns a decision. When omitted, permission requests are emitted as events and left pending for the consumer to resolve with the pending permission RPC.
1343
1357
 
1358
+ Callbacks receive optional derived prompt context as `invocation.promptRequest`. The raw request's `managedApprovalRequired` remains managed-policy provenance and enforces one-shot restrictions. Only derived `commands`, `read`, `write`, `url`, and `workflow` prompts can clear the human requirement after Assisted approval; for missing or other prompt kinds, keep the raw marker authoritative.
1359
+
1344
1360
  ### Approve All (simplest)
1345
1361
 
1346
1362
  Use the built-in `approveAll` helper when managed settings are disabled:
@@ -1620,7 +1636,7 @@ const session = await client.createSession({
1620
1636
  - `onPreToolUse` - Intercept tool calls before execution. Can allow/deny or modify arguments.
1621
1637
  - `onPostToolUse` - Process tool results after **successful** execution. Can modify results or add context.
1622
1638
  - `onPostToolUseFailure` - Observe and append hidden guidance to the model after tool executions whose result was `"failure"`. Register this in addition to `onPostToolUse` to see failed tool calls.
1623
- - `onUserPromptSubmitted` - Intercept user prompts. Can modify the prompt before processing.
1639
+ - `onUserPromptSubmitted` - Intercept user prompts. Can modify the prompt before processing, or return `{ decision: "block", reason }` to refuse it: no model turn runs, no user message is recorded, and the session emits an ephemeral `session.warning` (`warningType: "policy_blocked"`) carrying the reason.
1624
1640
  - `onSessionStart` - Run logic when a session starts or resumes.
1625
1641
  - `onSessionEnd` - Cleanup or logging when session ends.
1626
1642
  - `onErrorOccurred` - Handle errors with retry/skip/abort strategies.
@@ -22,7 +22,7 @@ __export(cliVersion_exports, {
22
22
  COPILOT_CLI_VERSION: () => COPILOT_CLI_VERSION
23
23
  });
24
24
  module.exports = __toCommonJS(cliVersion_exports);
25
- const COPILOT_CLI_VERSION = "1.0.98";
25
+ const COPILOT_CLI_VERSION = "1.0.99-1";
26
26
  const COPILOT_CLI_USE_NPM_PACKAGE = false;
27
27
  // Annotate the CommonJS export names for ESM import in node:
28
28
  0 && (module.exports = {
@@ -1775,7 +1775,8 @@ class CopilotClient {
1775
1775
  await rpc.host.dispose({ hostId });
1776
1776
  },
1777
1777
  (sessionId) => rpc.host.publishSession({ hostId, sessionId }),
1778
- () => rpc.host.listSessions({ hostId })
1778
+ () => rpc.host.listSessions({ hostId }),
1779
+ (sessionId) => rpc.host.unpublishSession({ hostId, sessionId })
1779
1780
  );
1780
1781
  } catch (error) {
1781
1782
  this.hostExitCallbacks.delete(hostId);
@@ -98,7 +98,7 @@ class CopilotWebSocketForwarder extends CopilotWebSocketHandler {
98
98
  constructor(context) {
99
99
  super(context);
100
100
  }
101
- sendRequestMessage(data) {
101
+ sendRequestMessage(data, _ctx) {
102
102
  if (this.#upstream?.readyState !== WebSocket.OPEN) {
103
103
  return;
104
104
  }
@@ -182,6 +182,7 @@ class CopilotRequestHandler {
182
182
  requestId: exchange.requestId,
183
183
  sessionId: exchange.sessionId,
184
184
  agentId: exchange.agentId,
185
+ agentInvocationId: exchange.agentInvocationId,
185
186
  parentAgentId: exchange.parentAgentId,
186
187
  interactionType: exchange.interactionType,
187
188
  transport: exchange.transport,
@@ -209,7 +210,10 @@ class CopilotRequestHandler {
209
210
  let cancelled;
210
211
  const clientSettled = (async () => {
211
212
  for await (const chunk of exchange.requestBody) {
212
- await handler.sendRequestMessage(decodeFrame(chunk));
213
+ await handler.sendRequestMessage(decodeFrame(chunk.chunk ?? new Uint8Array()), {
214
+ ...ctx,
215
+ agentInvocationId: chunk.agentInvocationId
216
+ });
213
217
  }
214
218
  return "client-complete";
215
219
  })().catch((err) => {
@@ -309,7 +313,7 @@ function routeChunk(exchange, params) {
309
313
  return;
310
314
  }
311
315
  if (params.data && params.data.length > 0) {
312
- exchange.pushChunk(decodeChunkData(params.data, !!params.binary));
316
+ exchange.pushChunk(decodeChunkData(params.data, !!params.binary), params.agentInvocationId);
313
317
  }
314
318
  if (params.end) {
315
319
  exchange.pushEnd();
@@ -319,6 +323,7 @@ class CopilotRequestExchange {
319
323
  requestId;
320
324
  sessionId;
321
325
  agentId;
326
+ agentInvocationId;
322
327
  parentAgentId;
323
328
  interactionType;
324
329
  method = "GET";
@@ -341,6 +346,7 @@ class CopilotRequestExchange {
341
346
  setContext(params) {
342
347
  this.sessionId = params.sessionId;
343
348
  this.agentId = params.agentId;
349
+ this.agentInvocationId = params.agentInvocationId;
344
350
  this.parentAgentId = params.parentAgentId;
345
351
  this.interactionType = params.interactionType;
346
352
  this.method = params.method;
@@ -361,8 +367,8 @@ class CopilotRequestExchange {
361
367
  return this.#cancelled;
362
368
  }
363
369
  // --- Request body feed (driven by the adapter as chunk frames arrive) ---
364
- pushChunk(chunk) {
365
- this.#push({ chunk });
370
+ pushChunk(chunk, agentInvocationId) {
371
+ this.#push({ chunk, agentInvocationId });
366
372
  }
367
373
  pushEnd() {
368
374
  this.#push({ end: true });
@@ -405,7 +411,7 @@ class CopilotRequestExchange {
405
411
  this.#drained = true;
406
412
  return { value: void 0, done: true };
407
413
  }
408
- return { value: item.chunk ?? new Uint8Array(), done: false };
414
+ return { value: item, done: false };
409
415
  }
410
416
  })
411
417
  };
@@ -517,9 +523,11 @@ async function buildFetchRequest(exchange) {
517
523
  async function drainAsync(stream) {
518
524
  const parts = [];
519
525
  let total = 0;
520
- for await (const chunk of stream) {
521
- parts.push(chunk);
522
- total += chunk.byteLength;
526
+ for await (const item of stream) {
527
+ if (item.chunk) {
528
+ parts.push(item.chunk);
529
+ total += item.chunk.byteLength;
530
+ }
523
531
  }
524
532
  if (parts.length === 0) {
525
533
  return new Uint8Array(0);
@@ -201,6 +201,14 @@ function createServerRpc(connection) {
201
201
  * @returns The existing runtime identity and its resource on the listener.
202
202
  */
203
203
  publishSession: async (params) => connection.sendRequest("host.publishSession", params),
204
+ /**
205
+ * Withdraws a live or dormant application publication from an owned live host and revokes AHP attachments without deleting local session history, worktrees, or ongoing work. Returns false when already absent; rejects nonapplication publications.
206
+ *
207
+ * @param params Withdraws an application publication without deleting its underlying local session.
208
+ *
209
+ * @returns Publication withdrawal outcome; local history, worktrees and ongoing work are preserved.
210
+ */
211
+ unpublishSession: async (params) => connection.sendRequest("host.unpublishSession", params),
204
212
  /**
205
213
  * Reads the complete advertised live and dormant session catalog of an existing host owned by this connection. Does not start hosting, subscribe to sessions, or guarantee resumability.
206
214
  *
@@ -262,7 +270,15 @@ function createServerRpc(connection) {
262
270
  *
263
271
  * @returns The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata.
264
272
  */
265
- getBuiltInCatalog: async () => connection.sendRequest("models.getBuiltInCatalog", {})
273
+ getBuiltInCatalog: async () => connection.sendRequest("models.getBuiltInCatalog", {}),
274
+ /**
275
+ * Reports the unit of each model's published token prices: AI Credits or nano-AIU. Pair these units with models.list prices by model ID and use identical account-selection options for both calls. When options are omitted, the current account and its model providers are used.
276
+ *
277
+ * @param params Optional opaque account selection or compatibility GitHub token used to list models.
278
+ *
279
+ * @returns Price units for every catalog model that publishes token prices.
280
+ */
281
+ listTokenPriceUnits: async (params) => connection.sendRequest("models.listTokenPriceUnits", params)
266
282
  },
267
283
  /** @experimental */
268
284
  contentExclusion: {
@@ -515,14 +531,6 @@ function createServerRpc(connection) {
515
531
  * @returns Skill installation management result with the honoured contract, or a typed refusal.
516
532
  */
517
533
  planInstall: async (params) => connection.sendRequest("skills.planInstall", params),
518
- /**
519
- * Plans installation of the Skill pinned by an Agent Finder materialisation reference without writing files. The runtime retrieves the descriptor only from its fixed Agent Finder authority and applies the same verification as skills.planInstall; the review shows only verified data. Apply the returned plan with skills.applyInstall.
520
- *
521
- * @param params Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate.
522
- *
523
- * @returns Skill installation management result with the honoured contract, or a typed refusal.
524
- */
525
- planInstallFromReference: async (params) => connection.sendRequest("skills.planInstallFromReference", params),
526
534
  /**
527
535
  * Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled.
528
536
  *
@@ -622,33 +630,24 @@ function createServerRpc(connection) {
622
630
  */
623
631
  getDiscoveryPaths: async (params) => connection.sendRequest("skills.getDiscoveryPaths", params)
624
632
  },
625
- /**
626
- * Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime uses its built-in extension launcher.
627
- *
628
- * @experimental
629
- */
630
- registerExtensionLaunchProvider: async () => connection.sendRequest("registerExtensionLaunchProvider", {}),
631
633
  /** @experimental */
632
- catalog: {
634
+ plugins: {
633
635
  /**
634
- * Requests a bounded catalog search. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with search available returns inert candidate summaries, each with an opaque single-use handle scoped to this runtime instance; a runtime without it returns the typed search-unavailable result. Public authorities may be searched anonymously, while an authority that requires credentials yields the typed authentication-required result. All returned text, URLs, and package metadata are untrusted external data and can never trigger instructions, tools, or installation. Read-only: nothing is installed, configured, or persisted.
636
+ * Plans installation of an Agent Plugin search candidate from its verified Agent Finder materialisation without writing files. Only candidates that carry a handle can be planned. The review shows only verified data, and installing leaves the plugin disabled until it is enabled through plugins.enable.
635
637
  *
636
- * @param params A bounded catalog search. Both the query length and the result count are capped by the schema so a caller cannot request an unbounded scan.
638
+ * @param params Side-effect-free planning of one Agent Plugin search candidate through its Agent Finder materialisation.
637
639
  *
638
- * @returns Outcome of a catalog.search call: either bounded inert candidates, or one typed refusal. Never a partial success.
640
+ * @returns Agent Plugin installation planning result with the honoured contract, or a typed refusal.
639
641
  */
640
- search: async (params) => connection.sendRequest("catalog.search", params),
642
+ planInstall: async (params) => connection.sendRequest("plugins.planInstall", params),
641
643
  /**
642
- * 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.
644
+ * Consumes one verified Agent Plugin installation plan, requests explicit human consent through installations.confirm on the original connection, then reacquires and verifies every file and registers the plugin, disabled, exactly as a direct plugin install. The installed plugin is listed, enabled and uninstalled through the existing plugins methods.
643
645
  *
644
- * @param params Terminates one retained catalog selection group through an opaque reference previously returned by the model-safe search projection.
646
+ * @param params Applies exactly one retained verified Agent Plugin installation plan.
645
647
  *
646
- * @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.
648
+ * @returns Agent Plugin installation result with the honoured contract, or a typed request/negotiation refusal.
647
649
  */
648
- select: async (params) => connection.sendRequest("catalog.select", params)
649
- },
650
- /** @experimental */
651
- plugins: {
650
+ applyInstall: async (params) => connection.sendRequest("plugins.applyInstall", params),
652
651
  /**
653
652
  * Lists plugins installed in user/global state.
654
653
  *
@@ -746,6 +745,39 @@ function createServerRpc(connection) {
746
745
  refresh: async (params) => connection.sendRequest("plugins.marketplaces.refresh", params)
747
746
  }
748
747
  },
748
+ /**
749
+ * Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime uses its built-in extension launcher.
750
+ *
751
+ * @experimental
752
+ */
753
+ registerExtensionLaunchProvider: async () => connection.sendRequest("registerExtensionLaunchProvider", {}),
754
+ /** @experimental */
755
+ catalog: {
756
+ /**
757
+ * Requests a bounded catalog search. This host-implemented server method is available through SDK/TUI hosts; standalone and C-ABI runtimes whose host does not implement server-method dispatch return JSON-RPC MethodNotFound. A runtime with search available returns inert candidate summaries, each with an opaque single-use handle scoped to this runtime instance; a runtime without it returns the typed search-unavailable result. Public authorities may be searched anonymously, while an authority that requires credentials yields the typed authentication-required result. All returned text, URLs, and package metadata are untrusted external data and can never trigger instructions, tools, or installation. Read-only: nothing is installed, configured, or persisted.
758
+ *
759
+ * @param params A bounded catalog search. Both the query length and the result count are capped by the schema so a caller cannot request an unbounded scan.
760
+ *
761
+ * @returns Outcome of a catalog.search call: either bounded inert candidates, or one typed refusal. Never a partial success.
762
+ */
763
+ search: async (params) => connection.sendRequest("catalog.search", params),
764
+ /**
765
+ * 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.
766
+ *
767
+ * @param params Terminates one retained catalog selection group through an opaque reference previously returned by the model-safe search projection.
768
+ *
769
+ * @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.
770
+ */
771
+ select: async (params) => connection.sendRequest("catalog.select", params),
772
+ /**
773
+ * Plans installing the resource an Agent Finder materialisation reference pins, without writing files and whatever its kind. The runtime retrieves the descriptor once, only from its fixed Agent Finder authority, verifies it, and plans by its resolution policy: a Skill (`agent-skill@1.0.0`) when the caller requires skill-confirmed-installation, an Agent Plugin (`plugin@1.0.0`) when it requires agent-plugin-confirmed-installation. Any other kind is refused as unsupported-resource. The outcome names the kind; apply a Skill plan with skills.applyInstall and an Agent Plugin plan with plugins.applyInstall. The review shows only verified data.
774
+ *
775
+ * @param params Side-effect-free planning of whatever resource a materialisation reference pins, rather than a search candidate.
776
+ *
777
+ * @returns Planning result for a materialisation reference with the honoured contract, or a typed refusal.
778
+ */
779
+ planInstallFromReference: async (params) => connection.sendRequest("catalog.planInstallFromReference", params)
780
+ },
749
781
  /** @experimental */
750
782
  agents: {
751
783
  /**
@@ -1671,7 +1703,42 @@ function createInternalServerRpc(connection) {
1671
1703
  *
1672
1704
  * @returns Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request.
1673
1705
  */
1674
- shouldExcludeGitHubTools: async (params) => connection.sendRequest("mcp.shouldExcludeGitHubTools", params)
1706
+ shouldExcludeGitHubTools: async (params) => connection.sendRequest("mcp.shouldExcludeGitHubTools", params),
1707
+ /** @experimental */
1708
+ oauth: {
1709
+ /**
1710
+ * Reads the static OAuth client a remote MCP server configuration pins, so a host can decide whether to register a client dynamically or reuse the configured one. Omits `clientConfig` when no non-empty `oauthClientId` is configured. The client secret is deliberately never carried here: the runtime loads it from the OAuth secret store at flow time.
1711
+ *
1712
+ * @param params A remote MCP server configuration whose pinned OAuth client should be read.
1713
+ *
1714
+ * @returns The OAuth client the configuration pins, when it pins one.
1715
+ */
1716
+ staticClientConfig: async (params) => connection.sendRequest("mcp.oauth.staticClientConfig", params),
1717
+ /**
1718
+ * Reads raw protected-resource metadata after a 401 establishes that OAuth is required. Uses the metadata URL from the server's `WWW-Authenticate` challenge when provided; otherwise, tries conventional metadata endpoints derived from the request URL. A host configuring a remote server interactively needs the unparsed document to classify the challenge before it starts OAuth. The request URL and each metadata URL pass through the named session's egress policy and sandbox proxy. The runtime refuses URLs that the session may not reach.
1719
+ *
1720
+ * @param params An OAuth challenge from a remote MCP server whose protected-resource metadata should be read.
1721
+ *
1722
+ * @returns Raw protected-resource metadata from the explicit metadata URL or conventional discovery.
1723
+ */
1724
+ fetchProtectedResourceMetadata: async (params) => connection.sendRequest("mcp.oauth.fetchProtectedResourceMetadata", params),
1725
+ /**
1726
+ * Asks a remote MCP server's authorization server whether it supports Dynamic Client Registration, after a 401 has already established that OAuth is required. A host configuring a remote server interactively needs this to choose between registering a client automatically and asking the user for client credentials. The request travels through the named session's egress policy and sandbox proxy, so a server that session may not reach is refused rather than contacted.
1727
+ *
1728
+ * @param params A remote MCP server whose authorization server should be asked about Dynamic Client Registration.
1729
+ *
1730
+ * @returns Whether the server's authorization server registers clients dynamically.
1731
+ */
1732
+ checkDcrSupport: async (params) => connection.sendRequest("mcp.oauth.checkDcrSupport", params),
1733
+ /**
1734
+ * Reports whether an authorization server URL belongs to Microsoft Entra ID. Entra does not support Dynamic Client Registration, so a host that finds no DCR support can still authorize against it with a pre-registered client instead of asking the user for client credentials.
1735
+ *
1736
+ * @param params An authorization server URL to classify.
1737
+ *
1738
+ * @returns Whether the URL is a Microsoft Entra ID authorization server.
1739
+ */
1740
+ isMicrosoftAuthorizationServer: async (params) => connection.sendRequest("mcp.oauth.isMicrosoftAuthorizationServer", params)
1741
+ }
1675
1742
  },
1676
1743
  /** @experimental */
1677
1744
  instructions: {
@@ -2089,7 +2156,7 @@ function createSessionRpc(connection, sessionId) {
2089
2156
  /**
2090
2157
  * Begin an interactive login flow for a provider kind (dispatch is kind-only) and return its opaque flow id and first step.
2091
2158
  *
2092
- * @param params Begin an interactive login flow for a provider kind. Dispatch is kind-only.
2159
+ * @param params Begin an interactive login flow for a provider kind, optionally reusing a cached base identity or explicitly requesting provider account selection.
2093
2160
  *
2094
2161
  * @returns A started login flow: its opaque id and first step.
2095
2162
  */
package/dist/cjs/host.js CHANGED
@@ -23,10 +23,11 @@ __export(host_exports, {
23
23
  module.exports = __toCommonJS(host_exports);
24
24
  class AhpHost {
25
25
  /** @internal */
26
- constructor(info, disposeHost, publishHostSession, listHostSessions) {
26
+ constructor(info, disposeHost, publishHostSession, listHostSessions, unpublishHostSession) {
27
27
  this.disposeHost = disposeHost;
28
28
  this.publishHostSession = publishHostSession;
29
29
  this.listHostSessions = listHostSessions;
30
+ this.unpublishHostSession = unpublishHostSession;
30
31
  this.hostId = info.hostId;
31
32
  this.url = info.url;
32
33
  this.environmentId = info.environmentId;
@@ -36,6 +37,7 @@ class AhpHost {
36
37
  disposeHost;
37
38
  publishHostSession;
38
39
  listHostSessions;
40
+ unpublishHostSession;
39
41
  hostId;
40
42
  /** Local WebSocket URL. Absent when only the GitHub environment is enabled. */
41
43
  url;
@@ -53,6 +55,14 @@ class AhpHost {
53
55
  publishSession(sessionId) {
54
56
  return this.publishHostSession(sessionId);
55
57
  }
58
+ /**
59
+ * Withdraw a live or dormant application publication and revoke AHP attachments.
60
+ * Preserves local history, worktrees and ongoing work; returns false when already absent.
61
+ * Rejects nonapplication publications. Does not stop the host.
62
+ */
63
+ unpublishSession(sessionId) {
64
+ return this.unpublishHostSession(sessionId);
65
+ }
56
66
  /** List all live and dormant catalog sessions advertised by this host. */
57
67
  listSessions() {
58
68
  return this.listHostSessions();
@@ -843,12 +843,12 @@ class CopilotSession {
843
843
  }
844
844
  }
845
845
  } else if (event.type === "permission.requested") {
846
- const { requestId, permissionRequest, resolvedByHook } = event.data;
846
+ const { requestId, permissionRequest, promptRequest, resolvedByHook } = event.data;
847
847
  if (resolvedByHook) {
848
848
  return;
849
849
  }
850
850
  if (this.permissionHandler) {
851
- void this._executePermissionAndRespond(requestId, permissionRequest);
851
+ void this._executePermissionAndRespond(requestId, permissionRequest, promptRequest);
852
852
  }
853
853
  } else if (event.type === "mcp.oauth_required") {
854
854
  const data = event.data;
@@ -1007,12 +1007,14 @@ class CopilotSession {
1007
1007
  * Executes a permission handler and sends the result back via RPC.
1008
1008
  * @internal
1009
1009
  */
1010
- async _executePermissionAndRespond(requestId, permissionRequest) {
1010
+ async _executePermissionAndRespond(requestId, permissionRequest, promptRequest) {
1011
1011
  try {
1012
- const handlerResult = await this.permissionHandler(permissionRequest, {
1012
+ const invocation = {
1013
1013
  sessionId: this.sessionId,
1014
- managedSettingsEnabled: this.managedSettingsEnabled
1015
- });
1014
+ managedSettingsEnabled: this.managedSettingsEnabled,
1015
+ ...promptRequest === void 0 ? {} : { promptRequest }
1016
+ };
1017
+ const handlerResult = await this.permissionHandler(permissionRequest, invocation);
1016
1018
  const isAttributed = (0, import_types.isAttributedPermissionResult)(handlerResult);
1017
1019
  const result = isAttributed ? handlerResult.result : handlerResult;
1018
1020
  const decisionContext = isAttributed ? handlerResult.decisionContext : void 0;
@@ -1,2 +1,2 @@
1
- export declare const COPILOT_CLI_VERSION = "1.0.98";
1
+ export declare const COPILOT_CLI_VERSION = "1.0.99-1";
2
2
  export declare const COPILOT_CLI_USE_NPM_PACKAGE = false;
@@ -1,4 +1,4 @@
1
- const COPILOT_CLI_VERSION = "1.0.98";
1
+ const COPILOT_CLI_VERSION = "1.0.99-1";
2
2
  const COPILOT_CLI_USE_NPM_PACKAGE = false;
3
3
  export {
4
4
  COPILOT_CLI_USE_NPM_PACKAGE,
package/dist/client.js CHANGED
@@ -1755,7 +1755,8 @@ class CopilotClient {
1755
1755
  await rpc.host.dispose({ hostId });
1756
1756
  },
1757
1757
  (sessionId) => rpc.host.publishSession({ hostId, sessionId }),
1758
- () => rpc.host.listSessions({ hostId })
1758
+ () => rpc.host.listSessions({ hostId }),
1759
+ (sessionId) => rpc.host.unpublishSession({ hostId, sessionId })
1759
1760
  );
1760
1761
  } catch (error) {
1761
1762
  this.hostExitCallbacks.delete(hostId);
@@ -9,6 +9,8 @@ export interface CopilotRequestContext {
9
9
  readonly requestId: string;
10
10
  readonly sessionId?: string;
11
11
  readonly agentId?: string;
12
+ /** Runtime-provided logical invocation identity, unchanged across physical retries. */
13
+ readonly agentInvocationId?: string;
12
14
  readonly parentAgentId?: string;
13
15
  readonly interactionType?: string;
14
16
  readonly transport: "http" | "websocket";
@@ -47,7 +49,11 @@ export declare abstract class CopilotWebSocketHandler implements AsyncDisposable
47
49
  protected constructor(context: CopilotRequestContext);
48
50
  sendResponseMessage(data: string | Uint8Array): Promise<void>;
49
51
  close(status?: CopilotWebSocketCloseStatus): Promise<void>;
50
- abstract sendRequestMessage(data: string | Uint8Array): Promise<void> | void;
52
+ /**
53
+ * Forward a runtime message. When supplied by the adapter, ctx carries this
54
+ * message's invocation identity, not the identity that opened the connection.
55
+ */
56
+ abstract sendRequestMessage(data: string | Uint8Array, ctx?: CopilotRequestContext): Promise<void> | void;
51
57
  [Symbol.asyncDispose](): Promise<void>;
52
58
  }
53
59
  /**
@@ -65,7 +71,7 @@ export declare abstract class CopilotWebSocketHandler implements AsyncDisposable
65
71
  export declare class CopilotWebSocketForwarder extends CopilotWebSocketHandler {
66
72
  #private;
67
73
  constructor(context: CopilotRequestContext);
68
- sendRequestMessage(data: string | Uint8Array): void;
74
+ sendRequestMessage(data: string | Uint8Array, _ctx?: CopilotRequestContext): void;
69
75
  close(status?: CopilotWebSocketCloseStatus): Promise<void>;
70
76
  [Symbol.asyncDispose](): Promise<void>;
71
77
  }
@@ -71,7 +71,7 @@ class CopilotWebSocketForwarder extends CopilotWebSocketHandler {
71
71
  constructor(context) {
72
72
  super(context);
73
73
  }
74
- sendRequestMessage(data) {
74
+ sendRequestMessage(data, _ctx) {
75
75
  if (this.#upstream?.readyState !== WebSocket.OPEN) {
76
76
  return;
77
77
  }
@@ -155,6 +155,7 @@ class CopilotRequestHandler {
155
155
  requestId: exchange.requestId,
156
156
  sessionId: exchange.sessionId,
157
157
  agentId: exchange.agentId,
158
+ agentInvocationId: exchange.agentInvocationId,
158
159
  parentAgentId: exchange.parentAgentId,
159
160
  interactionType: exchange.interactionType,
160
161
  transport: exchange.transport,
@@ -182,7 +183,10 @@ class CopilotRequestHandler {
182
183
  let cancelled;
183
184
  const clientSettled = (async () => {
184
185
  for await (const chunk of exchange.requestBody) {
185
- await handler.sendRequestMessage(decodeFrame(chunk));
186
+ await handler.sendRequestMessage(decodeFrame(chunk.chunk ?? new Uint8Array()), {
187
+ ...ctx,
188
+ agentInvocationId: chunk.agentInvocationId
189
+ });
186
190
  }
187
191
  return "client-complete";
188
192
  })().catch((err) => {
@@ -282,7 +286,7 @@ function routeChunk(exchange, params) {
282
286
  return;
283
287
  }
284
288
  if (params.data && params.data.length > 0) {
285
- exchange.pushChunk(decodeChunkData(params.data, !!params.binary));
289
+ exchange.pushChunk(decodeChunkData(params.data, !!params.binary), params.agentInvocationId);
286
290
  }
287
291
  if (params.end) {
288
292
  exchange.pushEnd();
@@ -292,6 +296,7 @@ class CopilotRequestExchange {
292
296
  requestId;
293
297
  sessionId;
294
298
  agentId;
299
+ agentInvocationId;
295
300
  parentAgentId;
296
301
  interactionType;
297
302
  method = "GET";
@@ -314,6 +319,7 @@ class CopilotRequestExchange {
314
319
  setContext(params) {
315
320
  this.sessionId = params.sessionId;
316
321
  this.agentId = params.agentId;
322
+ this.agentInvocationId = params.agentInvocationId;
317
323
  this.parentAgentId = params.parentAgentId;
318
324
  this.interactionType = params.interactionType;
319
325
  this.method = params.method;
@@ -334,8 +340,8 @@ class CopilotRequestExchange {
334
340
  return this.#cancelled;
335
341
  }
336
342
  // --- Request body feed (driven by the adapter as chunk frames arrive) ---
337
- pushChunk(chunk) {
338
- this.#push({ chunk });
343
+ pushChunk(chunk, agentInvocationId) {
344
+ this.#push({ chunk, agentInvocationId });
339
345
  }
340
346
  pushEnd() {
341
347
  this.#push({ end: true });
@@ -378,7 +384,7 @@ class CopilotRequestExchange {
378
384
  this.#drained = true;
379
385
  return { value: void 0, done: true };
380
386
  }
381
- return { value: item.chunk ?? new Uint8Array(), done: false };
387
+ return { value: item, done: false };
382
388
  }
383
389
  })
384
390
  };
@@ -490,9 +496,11 @@ async function buildFetchRequest(exchange) {
490
496
  async function drainAsync(stream) {
491
497
  const parts = [];
492
498
  let total = 0;
493
- for await (const chunk of stream) {
494
- parts.push(chunk);
495
- total += chunk.byteLength;
499
+ for await (const item of stream) {
500
+ if (item.chunk) {
501
+ parts.push(item.chunk);
502
+ total += item.chunk.byteLength;
503
+ }
496
504
  }
497
505
  if (parts.length === 0) {
498
506
  return new Uint8Array(0);