@github/copilot-sdk 1.0.29-preview.0 → 1.0.29-preview.10

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/dist/session.js CHANGED
@@ -157,6 +157,11 @@ class WorkflowProgressBuffer {
157
157
  if (this.closed) {
158
158
  throw new Error("Cannot log after the workflow run has settled");
159
159
  }
160
+ if (typeof text !== "string") {
161
+ throw new TypeError(
162
+ `${kind}() expects a string; got ${text === null ? "null" : typeof text}.`
163
+ );
164
+ }
160
165
  this.pending.push({ seq: this.nextSeq++, kind, text });
161
166
  this.scheduleFlush();
162
167
  }
@@ -284,6 +289,7 @@ class CopilotSession {
284
289
  /** Settles once every earlier `setTools` call has finished. */
285
290
  setToolsQueue = Promise.resolve();
286
291
  pendingExternalTools = /* @__PURE__ */ new Map();
292
+ pendingSessionCreations = /* @__PURE__ */ new Set();
287
293
  canvases = /* @__PURE__ */ new Map();
288
294
  bearerTokenProviders = /* @__PURE__ */ new Map();
289
295
  commandHandlers = /* @__PURE__ */ new Map();
@@ -296,6 +302,7 @@ class CopilotSession {
296
302
  exitPlanModeHandler;
297
303
  autoModeSwitchHandler;
298
304
  skillProvider;
305
+ sessionCreationHandler;
299
306
  hooks;
300
307
  transformCallbacks;
301
308
  _rpc = null;
@@ -883,6 +890,10 @@ class CopilotSession {
883
890
  controller.abort();
884
891
  }
885
892
  this.pendingExternalTools.clear();
893
+ for (const controller of this.pendingSessionCreations) {
894
+ controller.abort();
895
+ }
896
+ this.pendingSessionCreations.clear();
886
897
  this.eventHandlers.clear();
887
898
  this.typedEventHandlers.clear();
888
899
  this.toolHandlers.clear();
@@ -893,6 +904,7 @@ class CopilotSession {
893
904
  this.exitPlanModeHandler = void 0;
894
905
  this.autoModeSwitchHandler = void 0;
895
906
  this.skillProvider = void 0;
907
+ this.sessionCreationHandler = void 0;
896
908
  this.hooks = void 0;
897
909
  this.traceContextProvider = void 0;
898
910
  this.bearerTokenProviders.clear();
@@ -1152,6 +1164,13 @@ class CopilotSession {
1152
1164
  } else if (typeof rawResult === "string") {
1153
1165
  result = rawResult;
1154
1166
  } else if (isToolResultObject(rawResult)) {
1167
+ try {
1168
+ JSON.stringify(rawResult.toolTelemetry);
1169
+ } catch (error) {
1170
+ const message = error instanceof Error ? error.message : String(error);
1171
+ throw new Error(`failed to serialize tool telemetry: ${message}`);
1172
+ }
1173
+ JSON.stringify(rawResult);
1155
1174
  result = rawResult;
1156
1175
  } else {
1157
1176
  result = JSON.stringify(rawResult);
@@ -1164,7 +1183,10 @@ class CopilotSession {
1164
1183
  if (!this._claimExternalTool(requestId, controller)) {
1165
1184
  return;
1166
1185
  }
1167
- const message = error instanceof Error ? error.message : String(error);
1186
+ let message = error instanceof Error ? error.message : String(error);
1187
+ if (!message) {
1188
+ message = error instanceof Error && error.name || "Error";
1189
+ }
1168
1190
  try {
1169
1191
  await this.rpc.tools.handlePendingToolCall({ requestId, error: message });
1170
1192
  } catch (rpcError) {
@@ -1562,6 +1584,7 @@ class CopilotSession {
1562
1584
  assertWorkflowResult(result);
1563
1585
  return { result };
1564
1586
  } finally {
1587
+ controller.abort(new DOMException("Workflow run has settled", "AbortError"));
1565
1588
  try {
1566
1589
  await progress.close();
1567
1590
  } finally {
@@ -1856,6 +1879,41 @@ class CopilotSession {
1856
1879
  this.assertConnected();
1857
1880
  this.skillProvider = provider;
1858
1881
  }
1882
+ /**
1883
+ * Registers the host's creation callback without enabling the tool group.
1884
+ * Enable it separately with `session.rpc.management.configure({ enabled: true })`.
1885
+ */
1886
+ async setSessionCreationHandler(handler) {
1887
+ this.assertConnected();
1888
+ const previous = this.sessionCreationHandler;
1889
+ this.sessionCreationHandler = handler;
1890
+ try {
1891
+ return await this.rpc.management.configure({ hasCreateHandler: handler !== void 0 });
1892
+ } catch (error) {
1893
+ if (this.sessionCreationHandler === handler) this.sessionCreationHandler = previous;
1894
+ throw error;
1895
+ }
1896
+ }
1897
+ /** @internal */
1898
+ async _handleSessionManagementCreate(request, token) {
1899
+ this.assertConnected();
1900
+ const handler = this.sessionCreationHandler;
1901
+ if (!handler || request.parentSessionId !== this.sessionId || request.sessionId !== this.sessionId) {
1902
+ throw new Error("Session creation handler is unavailable");
1903
+ }
1904
+ const controller = new AbortController();
1905
+ this.pendingSessionCreations.add(controller);
1906
+ const subscription = token.onCancellationRequested(() => controller.abort());
1907
+ if (token.isCancellationRequested) controller.abort();
1908
+ try {
1909
+ return await handler(request, { signal: controller.signal });
1910
+ } catch {
1911
+ throw new Error("Session creation callback failed");
1912
+ } finally {
1913
+ subscription.dispose();
1914
+ this.pendingSessionCreations.delete(controller);
1915
+ }
1916
+ }
1859
1917
  /**
1860
1918
  * Handles a `skillProvider.list` request from the runtime.
1861
1919
  *
package/dist/types.d.ts CHANGED
@@ -9,13 +9,23 @@ export type { InstallationConfirmationHandler } from "./installationConfirmation
9
9
  import type { AgentModelPolicy, AttachmentExtensionContext as GeneratedExtensionContextAttachment, AutoTier, ModelProviderRef, PermissionRequest as GeneratedPermissionRequest, PermissionRequestedData as GeneratedPermissionRequestedData, PermissionRequestedEvent as GeneratedPermissionRequestedEvent, ReasoningSummary, SessionLimitsConfig, SessionEvent as GeneratedSessionEvent } from "./generated/session-events.js";
10
10
  import type { CopilotSession } from "./session.js";
11
11
  import type { JsonValue } from "./workflow.js";
12
- import type { ExtensionLaunchProviderHandler as GeneratedExtensionLaunchProvider, AutopilotOptions, GitHubTokenAcquireRequest, GitHubTokenAcquireResult, GitHubTelemetryNotification, HostDiagnostics, ModelBillingTokenPrices, DiagnosticsConfiguration, OpenCanvasInstance, ProtocolExternalToolNamespace, CurrentToolMetadata, KerberosAuthAcquireRequest, KerberosAuthMode, KerberosAuthScope, SandboxConfig } from "./generated/rpc.js";
12
+ import type { ExtensionLaunchProviderHandler as GeneratedExtensionLaunchProvider, AutopilotOptions, GitHubTokenAcquireRequest, GitHubTokenAcquireResult, GitHubTelemetryNotification, HostDiagnostics, ModelBillingTokenPrices, DiagnosticsConfiguration, OpenCanvasInstance, ProtocolExternalToolNamespace, CurrentToolMetadata, KerberosAuthAcquireRequest, KerberosAuthMode, KerberosAuthScope, SandboxConfig, SessionManagementCreateHostRequest, SessionManagementCreateResult } from "./generated/rpc.js";
13
13
  import type { ToolSet } from "./toolSet.js";
14
14
  export type { KerberosAuthAcquireRequest, KerberosAuthMode, KerberosAuthScope, } from "./generated/rpc.js";
15
15
  export type { RemoteSessionMode } from "./generated/rpc.js";
16
16
  export type { AutopilotOptions } from "./generated/rpc.js";
17
17
  export type { CurrentToolMetadata } from "./generated/rpc.js";
18
18
  export type { SandboxConfig, SandboxConfigSource } from "./generated/rpc.js";
19
+ export type { ManagedSessionInfo, SessionManagementCloseRequest, SessionManagementCloseResult, SessionManagementConfigureRequest, SessionManagementCreateHostRequest, SessionManagementCreateRequest, SessionManagementCreateResult, SessionManagementListRequest, SessionManagementListResult, SessionManagementMessageRequest, SessionManagementMessageResult, SessionManagementPolicy, SessionManagementScope, SessionManagementToolOverrides, } from "./generated/rpc.js";
20
+ /**
21
+ * Creates and supervises a separate session for this session's management tools.
22
+ * Use newSessionId as the child's exact identity and apply the requested launch
23
+ * options and permission ceiling. Route approvals independently, retain the new
24
+ * session, and dispose it with the host. Do not copy the parent's conversation.
25
+ */
26
+ export type SessionCreationHandler = (request: SessionManagementCreateHostRequest, options: {
27
+ signal: AbortSignal;
28
+ }) => SessionManagementCreateResult | Promise<SessionManagementCreateResult>;
19
29
  export type { AuthIdentityMetadata, AuthInfoType, ConnectorAccountRequest, ConnectorAvailability, ConnectorCapabilities, ConnectorCatalogEntry, ConnectorCatalogResult, ConnectorCatalogStatus, ConnectorConnectRequest, ConnectorConnectResult, ConnectorContinueRequest, ConnectorDisconnectResult, ConnectorMcpStatus, ConnectorReconcileRequest, ConnectorRuntimeStatus, ConnectorSessionAccount, ConnectorSessionAccountResult, ConnectorStatus, ExtensionLaunchProfile, ExtensionLaunchProviderResolveRequest, ExtensionLaunchProviderResolveResult, GitHubTokenAcquireReason, GitHubTokenAcquireResult, GitHubTelemetryNotification, GitHubTelemetryEvent, GitHubTelemetryClientInfo, HostDiagnostics, HostErrorReportingStatus, InstallationConfirmationRequest, InstallationConfirmationResponse, InstallationDecision, InstallationReview, McpInstallationReview, } from "./generated/rpc.js";
20
30
  /**
21
31
  * Arguments passed to a session's {@link GitHubTokenProvider}.
@@ -132,14 +142,10 @@ export interface StdioRuntimeConnection extends ChildProcessRuntimeConnection {
132
142
  * {@link RuntimeConnection.forInProcess}.
133
143
  *
134
144
  * @experimental The in-process (FFI) transport is experimental and its behavior may
135
- * change. Per-client options that are lowered to environment variables — including
136
- * {@link CopilotClientOptions.env}, {@link CopilotClientOptions.telemetry},
137
- * {@link CopilotClientOptions.gitHubToken}, and
138
- * {@link CopilotClientOptions.baseDirectory} — are **not** honored with this
139
- * transport, because the native runtime loads into the shared host process and its
140
- * worker inherits that process's ambient environment. To configure the in-process
141
- * runtime, set the corresponding environment variables on the host process before
142
- * constructing the client. See https://github.com/github/copilot-sdk/issues/1934.
145
+ * change. Typed authentication and {@link CopilotClientOptions.baseDirectory}
146
+ * are scoped to this host. Client-level `env`, `workingDirectory` and
147
+ * `telemetry` are unsupported; set working directories
148
+ * on individual sessions.
143
149
  */
144
150
  export interface InProcessRuntimeConnection {
145
151
  readonly kind: "inprocess";
@@ -218,11 +224,9 @@ export declare const RuntimeConnection: {
218
224
  /**
219
225
  * Host the runtime in-process over the native runtime library's C ABI (FFI).
220
226
  *
221
- * @experimental Per-client options lowered to environment variables (`env`,
222
- * `telemetry`, `gitHubToken`, `baseDirectory`) are **not** honored in-process;
223
- * the worker inherits the host process's ambient environment. Set the
224
- * corresponding environment variables on the host process instead. See
225
- * https://github.com/github/copilot-sdk/issues/1934.
227
+ * @experimental Runtime configuration from `gitHubToken` and
228
+ * `baseDirectory` is scoped to this host, not the process environment.
229
+ * Client-level `env`, `telemetry` and `workingDirectory` are unsupported.
226
230
  */
227
231
  readonly forInProcess: (options?: Pick<InProcessRuntimeConnection, "runOnUiThread">) => InProcessRuntimeConnection;
228
232
  };
@@ -317,6 +321,7 @@ export interface CopilotClientOptions {
317
321
  /**
318
322
  * Base directory for Copilot data (session state, config, etc.).
319
323
  * Sets the COPILOT_HOME environment variable on the spawned runtime.
324
+ * In-process hosts also keep their runtime cache in this directory's `cache` subdirectory.
320
325
  * When not set, the runtime defaults to ~/.copilot.
321
326
  * Ignored when connecting to an existing runtime via {@link RuntimeConnection.forUri}.
322
327
  */
@@ -352,6 +357,7 @@ export interface CopilotClientOptions {
352
357
  logLevel?: "none" | "error" | "warning" | "info" | "debug" | "all";
353
358
  /**
354
359
  * Environment variables to pass to the runtime process. If not set, inherits process.env.
360
+ * Unsupported with {@link RuntimeConnection.forInProcess}; use typed client/session options.
355
361
  */
356
362
  env?: Record<string, string | undefined>;
357
363
  /**
@@ -363,7 +369,8 @@ export interface CopilotClientOptions {
363
369
  /**
364
370
  * Whether to use the logged-in user for authentication.
365
371
  * When true, the runtime will attempt to use stored OAuth tokens or gh CLI auth.
366
- * When false, only explicit tokens (gitHubToken or environment variables) are used.
372
+ * When false, only explicit tokens such as gitHubToken are used; environment variable
373
+ * tokens (COPILOT_GITHUB_TOKEN, GH_TOKEN, GITHUB_TOKEN) are ignored.
367
374
  * @default true (but defaults to false when gitHubToken is provided)
368
375
  */
369
376
  useLoggedInUser?: boolean;
@@ -1181,6 +1188,12 @@ export interface UserInputRequest {
1181
1188
  * @default true
1182
1189
  */
1183
1190
  allowFreeform?: boolean;
1191
+ /**
1192
+ * Runtime ID of the pending `user_input.requested` request. Pass it as `requestId`
1193
+ * to answer this exact request through the session's UI RPCs. Absent when the
1194
+ * runtime predates this field.
1195
+ */
1196
+ requestId?: string;
1184
1197
  }
1185
1198
  /**
1186
1199
  * Response to a user input request
@@ -1213,6 +1226,12 @@ export interface ExitPlanModeRequest {
1213
1226
  actions: string[];
1214
1227
  /** The action recommended by the runtime. */
1215
1228
  recommendedAction: string;
1229
+ /**
1230
+ * Runtime ID of the pending `exit_plan_mode.requested` request. Pass it as
1231
+ * `requestId` to answer this exact request through the session's UI RPCs. Absent
1232
+ * when the runtime predates this field.
1233
+ */
1234
+ requestId?: string;
1216
1235
  }
1217
1236
  /**
1218
1237
  * Response to an exit-plan-mode request.
@@ -1854,9 +1873,10 @@ export interface LargeToolOutputConfig {
1854
1873
  outputDirectory?: string;
1855
1874
  }
1856
1875
  /**
1857
- * Valid reasoning effort levels for models that support it.
1876
+ * Model-defined reasoning effort for models that support it.
1877
+ * Use client.listModels() to discover supported values; BYOK providers can define additional levels.
1858
1878
  */
1859
- export type ReasoningEffort = "low" | "medium" | "high" | "xhigh" | "max";
1879
+ export type ReasoningEffort = string;
1860
1880
  /**
1861
1881
  * Context window tier for the session. "long_context" pins the session to the
1862
1882
  * long-context tier when the selected model supports it.
@@ -3261,6 +3281,9 @@ export interface MessageOptions {
3261
3281
  * sendAndWait still returns an assistant message event. For a typed result, pass a
3262
3282
  * Zod-compatible schema as sendAndWait's second argument instead.
3263
3283
  * Streaming events remain text and may include intermediate messages.
3284
+ * Sent with strict: true and not rewritten, so provider schema restrictions apply: strict
3285
+ * OpenAI output needs an object root with every property required (use Zod's .nullable()
3286
+ * rather than .optional()).
3264
3287
  * Use rpc.send's responseFormat for provider-specific name, description and strict options.
3265
3288
  */
3266
3289
  responseSchema?: ZodSchema | Record<string, unknown>;
@@ -3359,6 +3382,18 @@ export interface SessionMetadata {
3359
3382
  isRemote: boolean;
3360
3383
  /** Working directory context (working directory, git info) from session creation */
3361
3384
  context?: SessionContext;
3385
+ /**
3386
+ * Name of the client that created the session (for example `github/cli`). Omitted when
3387
+ * unknown.
3388
+ */
3389
+ clientName?: string;
3390
+ /**
3391
+ * Name of the client that most recently ran the session: the client that created it, or a
3392
+ * client that resumed it and then ran it. Resuming a session only to observe it does not
3393
+ * change this. Omitted when unknown, such as for sessions persisted before this field
3394
+ * existed.
3395
+ */
3396
+ lastClientName?: string;
3362
3397
  }
3363
3398
  /**
3364
3399
  * Response from status.get
package/dist/types.js CHANGED
@@ -38,11 +38,9 @@ const RuntimeConnection = {
38
38
  /**
39
39
  * Host the runtime in-process over the native runtime library's C ABI (FFI).
40
40
  *
41
- * @experimental Per-client options lowered to environment variables (`env`,
42
- * `telemetry`, `gitHubToken`, `baseDirectory`) are **not** honored in-process;
43
- * the worker inherits the host process's ambient environment. Set the
44
- * corresponding environment variables on the host process instead. See
45
- * https://github.com/github/copilot-sdk/issues/1934.
41
+ * @experimental Runtime configuration from `gitHubToken` and
42
+ * `baseDirectory` is scoped to this host, not the process environment.
43
+ * Client-level `env`, `telemetry` and `workingDirectory` are unsupported.
46
44
  */
47
45
  forInProcess(options = {}) {
48
46
  return { kind: "inprocess", ...options };
@@ -95,7 +95,7 @@ tools: [
95
95
  // Return value: string or ToolResultObject
96
96
  // string → treated as success
97
97
  // { textResultForLlm, resultType } → structured result
98
- // resultType: "success" | "failure" | "rejected" | "denied"
98
+ // resultType: "success" | "failure" | "rejected" | "denied" | "timeout"
99
99
  return `Result: ${args.arg1}`;
100
100
  },
101
101
  },
@@ -105,7 +105,7 @@ tools: [
105
105
  **Constraints:**
106
106
 
107
107
  - Tool names must be unique across ALL loaded extensions. Collisions cause the second extension to fail to load.
108
- - Handler must return a string or `{ textResultForLlm: string, resultType?: string }`.
108
+ - Handler must return a string or `{ textResultForLlm: string, resultType: "success" | "failure" | "rejected" | "denied" | "timeout" }`. `resultType` is required: an object without a recognized `resultType` is not a structured result, and the model receives it serialized as JSON text.
109
109
  - Handler receives `(args, invocation)` — the second argument has `sessionId`, `toolCallId`, `toolName`.
110
110
  - Use `session.log()` to surface messages to the user. Don't use `console.log()` (stdout is reserved for JSON-RPC).
111
111
 
@@ -116,18 +116,22 @@ tools: [
116
116
  ```js
117
117
  hooks: {
118
118
  onUserPromptSubmitted: async (input, invocation) => { ... },
119
+ onUserPromptTransformed: async (input, invocation) => { ... },
119
120
  onPreToolUse: async (input, invocation) => { ... },
121
+ onPreMcpToolCall: async (input, invocation) => { ... },
120
122
  onPostToolUse: async (input, invocation) => { ... },
121
123
  onPostToolUseFailure: async (input, invocation) => { ... },
122
124
  onSessionStart: async (input, invocation) => { ... },
123
125
  onSessionEnd: async (input, invocation) => { ... },
124
126
  onErrorOccurred: async (input, invocation) => { ... },
127
+ onAgentStop: async (input, invocation) => { ... },
125
128
  onSubagentStart: async (input, invocation) => { ... },
126
129
  onSubagentStop: async (input, invocation) => { ... },
127
130
  }
128
131
  ```
129
132
 
130
133
  All hook inputs include `sessionId`, `timestamp` (`Date`) and `workingDirectory`.
134
+ Tool hooks also receive `toolCallId` when the runtime supplies one; correlate a call's hooks with `(input.sessionId, input.toolCallId)`.
131
135
  All handlers receive `invocation: { sessionId: string }` as the second argument.
132
136
  All handlers may return `void`/`undefined` (no-op) or an output object.
133
137
 
@@ -143,9 +147,23 @@ All handlers may return `void`/`undefined` (no-op) or an output object.
143
147
  | `decision` | `"block"` | Refuses the prompt: no model turn runs and no user message is recorded |
144
148
  | `reason` | `string` | Why the prompt was blocked, carried by the ephemeral `session.warning` (`warningType: "policy_blocked"`) |
145
149
 
150
+ ### onUserPromptTransformed
151
+
152
+ Fires after the runtime adds generated context to the submitted prompt, before
153
+ the result is stored in session history or sent to the model. `prompt` is the
154
+ prompt after `onUserPromptSubmitted`; `transformedPrompt` also carries the
155
+ runtime-generated context.
156
+
157
+ **Input:** `{ prompt: string, transformedPrompt: string, timestamp, workingDirectory }`
158
+
159
+ **Output (all fields optional):**
160
+ | Field | Type | Effect |
161
+ |-------|------|--------|
162
+ | `modifiedTransformedPrompt` | `string` | Replaces the content stored in session history and sent to the model |
163
+
146
164
  ### onPreToolUse
147
165
 
148
- **Input:** `{ toolName: string, toolArgs: unknown, timestamp, workingDirectory }`
166
+ **Input:** `{ toolCallId?: string, toolName: string, toolArgs: unknown, timestamp, workingDirectory }`
149
167
 
150
168
  **Output (all fields optional):**
151
169
  | Field | Type | Effect |
@@ -155,9 +173,22 @@ All handlers may return `void`/`undefined` (no-op) or an output object.
155
173
  | `modifiedArgs` | `unknown` | Replaces the tool arguments |
156
174
  | `additionalContext` | `string` | Injected into the conversation |
157
175
 
176
+ ### onPreMcpToolCall
177
+
178
+ Fires before a tool call is sent to an MCP server. It controls only the `_meta`
179
+ object sent with the request; to allow, deny or change the call itself, use
180
+ `onPreToolUse`.
181
+
182
+ **Input:** `{ toolCallId?: string, serverName: string, toolName: string, arguments: unknown, _meta?: Record<string, unknown>, timestamp, workingDirectory }`
183
+
184
+ **Output (all fields optional):**
185
+ | Field | Type | Effect |
186
+ |-------|------|--------|
187
+ | `metaToUse` | `Record<string, unknown> \| null` | An object becomes the request's `_meta`; `null` omits it; leaving it out keeps the current `_meta` |
188
+
158
189
  ### onPostToolUse
159
190
 
160
- **Input:** `{ toolName: string, toolArgs: unknown, toolResult: ToolResultObject, timestamp, workingDirectory }`
191
+ **Input:** `{ toolCallId?: string, toolName: string, toolArgs: unknown, toolResult: ToolResultObject, timestamp, workingDirectory }`
161
192
 
162
193
  Fires only when the tool returned a successful result. To observe non-success
163
194
  outcomes, register `onPostToolUseFailure` as well.
@@ -170,7 +201,7 @@ outcomes, register `onPostToolUseFailure` as well.
170
201
 
171
202
  ### onPostToolUseFailure
172
203
 
173
- **Input:** `{ toolName: string, toolArgs: unknown, error: string, timestamp, workingDirectory }`
204
+ **Input:** `{ toolCallId?: string, toolName: string, toolArgs: unknown, error: string, timestamp, workingDirectory }`
174
205
 
175
206
  Fires after a tool execution whose result was `"failure"`. `onPostToolUse`
176
207
  does **not** fire for these outcomes, so register this handler to observe or
@@ -218,6 +249,23 @@ fire it.
218
249
  | `retryCount` | `number` | Max retries (when errorHandling is "retry") |
219
250
  | `userNotification` | `string` | Message shown to the user |
220
251
 
252
+ ### onAgentStop
253
+
254
+ Fires when the top-level agent reaches a natural terminal stop: it went idle
255
+ without pending work and was not aborted. Sub-agents fire `onSubagentStop`
256
+ instead.
257
+
258
+ **Input:** `{ stopReason?: string, transcriptPath?: string, stopHookActive?: boolean, timestamp, workingDirectory }`
259
+
260
+ `stopHookActive` is `true` when this stop follows an earlier `"block"` from this
261
+ hook; check it to avoid blocking indefinitely. The runtime also caps
262
+ consecutive blocks.
263
+
264
+ **Output (optional):**
265
+ | Field | Type | Effect |
266
+ |-------|------|--------|
267
+ | `decision` and `reason` | `"block"` and `string` | Keep the agent running; `reason` is enqueued as its next user message |
268
+
221
269
  ### onSubagentStart
222
270
 
223
271
  Fires before a sub-agent's first turn. The input's `sessionId` and the
package/docs/examples.md CHANGED
@@ -152,15 +152,20 @@ Hooks intercept and modify behavior at key lifecycle points. Register them in th
152
152
 
153
153
  ### Available Hooks
154
154
 
155
- | Hook | Fires When | Can Modify |
156
- | ----------------------- | ---------------------------------------- | ------------------------------------------- |
157
- | `onUserPromptSubmitted` | User sends a message | The prompt text, add context |
158
- | `onPreToolUse` | Before a tool executes | Tool args, permission decision, add context |
159
- | `onPostToolUse` | After a tool executes successfully | Tool result, add context |
160
- | `onPostToolUseFailure` | After a tool execution returns a failure | Add hidden guidance to the model |
161
- | `onSessionStart` | Session starts or resumes | Add context |
162
- | `onSessionEnd` | Session ends | Cleanup actions, summary |
163
- | `onErrorOccurred` | An error occurs | Error handling strategy (retry/skip/abort) |
155
+ | Hook | Fires When | Can Modify |
156
+ | ------------------------- | -------------------------------------------- | ------------------------------------------- |
157
+ | `onUserPromptSubmitted` | User sends a message | The prompt text, add context |
158
+ | `onUserPromptTransformed` | Runtime has transformed the submitted prompt | The transformed prompt |
159
+ | `onPreToolUse` | Before a tool executes | Tool args, permission decision, add context |
160
+ | `onPreMcpToolCall` | Before an MCP tool call is sent | The request's `_meta` |
161
+ | `onPostToolUse` | After a tool executes successfully | Tool result, add context |
162
+ | `onPostToolUseFailure` | After a tool execution returns a failure | Add hidden guidance to the model |
163
+ | `onSessionStart` | Session starts or resumes | Add context |
164
+ | `onSessionEnd` | Session ends | Cleanup actions, summary |
165
+ | `onErrorOccurred` | An error occurs | Error handling strategy (retry/skip/abort) |
166
+ | `onAgentStop` | Top-level agent naturally stops | Block the stop and request another turn |
167
+ | `onSubagentStart` | Before a sub-agent's first turn | Add context to its prompt |
168
+ | `onSubagentStop` | After a sub-agent completes a turn | Request another turn, replace its response |
164
169
 
165
170
  All hook inputs include `timestamp` (`Date`) and `workingDirectory`.
166
171
 
@@ -198,7 +203,7 @@ Use `session.send()` to programmatically inject a new user message.
198
203
  ```js
199
204
  hooks: {
200
205
  onUserPromptSubmitted: async (input) => {
201
- if (/\\burgent\\b/i.test(input.prompt)) {
206
+ if (/\burgent\b/i.test(input.prompt)) {
202
207
  // Fire-and-forget a follow-up message
203
208
  setTimeout(() => session.send({ prompt: "Please prioritize this." }), 0);
204
209
  }
@@ -217,7 +222,7 @@ hooks: {
217
222
  onPreToolUse: async (input) => {
218
223
  if (input.toolName === "bash") {
219
224
  const cmd = String(input.toolArgs?.command || "");
220
- if (/rm\\s+-rf/i.test(cmd) || /Remove-Item\\s+.*-Recurse/i.test(cmd)) {
225
+ if (/rm\s+-rf/i.test(cmd) || /Remove-Item\s+.*-Recurse/i.test(cmd)) {
221
226
  return {
222
227
  permissionDecision: "deny",
223
228
  permissionDecisionReason: "Destructive commands are not allowed.",
@@ -394,7 +399,7 @@ function copyToClipboard(text) {
394
399
  const session = await joinSession({
395
400
  hooks: {
396
401
  onUserPromptSubmitted: async (input) => {
397
- if (/\\bcopy\\b/i.test(input.prompt)) {
402
+ if (/\bcopy\b/i.test(input.prompt)) {
398
403
  copyNextResponse = true;
399
404
  }
400
405
  },
@@ -513,7 +518,7 @@ const debounce = new Map();
513
518
 
514
519
  watch(cwd, { recursive: true }, (eventType, filename) => {
515
520
  if (!filename || eventType !== "change") return;
516
- if (filename.split(/[\\\\\\/]/).some((p) => IGNORE.has(p))) return;
521
+ if (filename.split(/[\\/]/).some((p) => IGNORE.has(p))) return;
517
522
 
518
523
  if (debounce.has(filename)) clearTimeout(debounce.get(filename));
519
524
  debounce.set(filename, setTimeout(() => {
@@ -524,7 +529,7 @@ watch(cwd, { recursive: true }, (eventType, filename) => {
524
529
  try { if (!statSync(fullPath).isFile()) return; } catch { return; }
525
530
  const relPath = relative(cwd, fullPath);
526
531
  session.send({
527
- prompt: `The user edited \\`${relPath}\\`.`,
532
+ prompt: `The user edited \`${relPath}\`.`,
528
533
  attachments: [{ type: "file", path: fullPath }],
529
534
  });
530
535
  }, 500));
@@ -619,7 +624,7 @@ function openInEditor(filePath) {
619
624
  const session = await joinSession({
620
625
  hooks: {
621
626
  onUserPromptSubmitted: async (input) => {
622
- if (/\\bcopy this\\b/i.test(input.prompt)) {
627
+ if (/\bcopy this\b/i.test(input.prompt)) {
623
628
  copyNextResponse = true;
624
629
  }
625
630
  return {
@@ -629,7 +634,7 @@ const session = await joinSession({
629
634
  onPreToolUse: async (input) => {
630
635
  if (input.toolName === "bash") {
631
636
  const cmd = String(input.toolArgs?.command || "");
632
- if (/rm\\s+-rf\\s+\//i.test(cmd) || /Remove-Item\\s+.*-Recurse/i.test(cmd)) {
637
+ if (/rm\s+-rf\s+\//i.test(cmd) || /Remove-Item\s+.*-Recurse/i.test(cmd)) {
633
638
  return {
634
639
  permissionDecision: "deny",
635
640
  permissionDecisionReason: "Destructive commands are not allowed.",
package/docs/workflows.md CHANGED
@@ -225,7 +225,6 @@ Observation does not restore execution eligibility. Starting, resuming, pausing,
225
225
  ```ts
226
226
  const runs = await session.workflow.listRuns();
227
227
  const runsPage = await session.workflow.listRuns({
228
- afterSeq,
229
228
  beforeSeq,
230
229
  limit,
231
230
  });
@@ -233,15 +232,15 @@ const detail = await session.workflow.getRunDetail(runId);
233
232
  const progressPage = await session.workflow.getRunProgress(runId, {
234
233
  phaseId,
235
234
  afterSeq,
236
- beforeSeq,
237
235
  limit,
238
236
  });
239
237
  ```
240
238
 
241
239
  - `listRuns()` returns only the runs array from the newest default page of this session's durable workflow runs. This overload preserves the original convenience API.
242
- - `listRuns({ afterSeq, beforeSeq, limit })` returns the full page. Its `oldestSeq`, `newestSeq`, `hasMoreNewer`, and `omittedOlder` fields let callers continue paging without raw RPC calls.
240
+ - `listRuns({ afterSeq, limit })` or `listRuns({ beforeSeq, limit })` returns the full page. Its `oldestSeq`, `newestSeq`, `hasMoreNewer`, and `omittedOlder` fields let callers continue paging without raw RPC calls.
243
241
  - `getRunDetail(runId)` returns phases, prompt-safe agent summaries, and the latest progress page. Each current-runtime agent summary includes `consumedNanoAiu`, the known spend from that direct agent and its descendants. Older runtimes omit the field, and historical agents without attributable charge rows also omit it when the run contains unattributed spend.
244
242
  - `getRunProgress(runId, options?)` pages progress forward, backward, by phase, or from the latest tail.
243
+ - Both paging calls take at most one cursor: `afterSeq` pages forward and `beforeSeq` pages backward, each exclusive of the given sequence number. A request with both fails with JSON-RPC `-32602` and `data.code: "workflow_runs_invalid_cursor"` from `listRuns`, or `"workflow_progress_invalid_cursor"` from `getRunProgress`.
245
244
 
246
245
  `getRun(runId)` reads the latest run envelope. `pause(runId)` pauses a running attempt and returns its `paused` envelope. `cancel(runId)` cancels a run and returns its terminal envelope.
247
246
 
package/package.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "url": "https://github.com/github/copilot-agent-runtime.git",
6
6
  "directory": "src/sdk/nodejs"
7
7
  },
8
- "version": "1.0.29-preview.0",
9
- "copilotCliVersion": "1.0.106-1",
8
+ "version": "1.0.29-preview.10",
9
+ "copilotCliVersion": "1.0.106-11",
10
10
  "description": "TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC",
11
11
  "main": "./dist/cjs/index.js",
12
12
  "types": "./dist/index.d.ts",
@@ -68,7 +68,7 @@
68
68
  "author": "GitHub",
69
69
  "license": "MIT",
70
70
  "dependencies": {
71
- "koffi": "3.3.3",
71
+ "koffi": "3.3.4",
72
72
  "vscode-jsonrpc": "10.0.0",
73
73
  "zod": "4.6.7"
74
74
  },
@@ -79,12 +79,13 @@
79
79
  "@types/node": "^26.6.5",
80
80
  "@types/semver": "7.8.0",
81
81
  "@types/ws": "^8.18.1",
82
- "@typescript-eslint/eslint-plugin": "^8.72.1",
83
- "@typescript-eslint/parser": "^8.72.1",
82
+ "@typescript-eslint/eslint-plugin": "^8.72.0",
83
+ "@typescript-eslint/parser": "^8.72.0",
84
+ "@vitest/coverage-v8": "^5.0.5",
84
85
  "esbuild": "^0.28.1",
85
86
  "eslint": "^10.13.0",
86
87
  "file-entry-cache": "file:./scripts/eslint-file-cache",
87
- "glob": "^13.0.1",
88
+ "glob": "^14.0.3",
88
89
  "json-schema": "^0.4.0",
89
90
  "json-schema-to-typescript": "^16.0.0",
90
91
  "jsonc-parser": "^3.3.1",
@@ -101,6 +102,8 @@
101
102
  },
102
103
  "overrides": {
103
104
  "file-entry-cache": "$file-entry-cache",
105
+ "glob": "$glob",
106
+ "minimatch": "^11.0.4",
104
107
  "prettier": "$prettier",
105
108
  "vite": "$vite"
106
109
  },
@@ -114,18 +117,18 @@
114
117
  "README.md"
115
118
  ],
116
119
  "copilotRuntime": {
117
- "sourceSha": "1fb35cfaf220f19b8983d5cbd2f7aa2128e58b3c",
118
- "version": "1.0.106-1",
120
+ "sourceSha": "2bfb456b9e0878c4cb3dd05cb9877ac9c97cbd68",
121
+ "version": "1.0.106-11",
119
122
  "visibility": "public"
120
123
  },
121
124
  "optionalDependencies": {
122
- "@github/copilot-sdk-darwin-arm64": "1.0.29-preview.0",
123
- "@github/copilot-sdk-darwin-x64": "1.0.29-preview.0",
124
- "@github/copilot-sdk-linux-arm64": "1.0.29-preview.0",
125
- "@github/copilot-sdk-linux-x64": "1.0.29-preview.0",
126
- "@github/copilot-sdk-linuxmusl-arm64": "1.0.29-preview.0",
127
- "@github/copilot-sdk-linuxmusl-x64": "1.0.29-preview.0",
128
- "@github/copilot-sdk-win32-arm64": "1.0.29-preview.0",
129
- "@github/copilot-sdk-win32-x64": "1.0.29-preview.0"
125
+ "@github/copilot-sdk-darwin-arm64": "1.0.29-preview.10",
126
+ "@github/copilot-sdk-darwin-x64": "1.0.29-preview.10",
127
+ "@github/copilot-sdk-linux-arm64": "1.0.29-preview.10",
128
+ "@github/copilot-sdk-linux-x64": "1.0.29-preview.10",
129
+ "@github/copilot-sdk-linuxmusl-arm64": "1.0.29-preview.10",
130
+ "@github/copilot-sdk-linuxmusl-x64": "1.0.29-preview.10",
131
+ "@github/copilot-sdk-win32-arm64": "1.0.29-preview.10",
132
+ "@github/copilot-sdk-win32-x64": "1.0.29-preview.10"
130
133
  }
131
134
  }