@github/copilot-language-server 1.552.0 → 1.553.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.
Files changed (74) hide show
  1. package/README.md +94 -1
  2. package/dist/main.js +1021 -1011
  3. package/dist/node_modules/@github/copilot-sdk/dist/cjs/cliVersion.js +1 -1
  4. package/dist/node_modules/@github/copilot-sdk/dist/cjs/client.js +499 -36
  5. package/dist/node_modules/@github/copilot-sdk/dist/cjs/copilotRequestHandler.js +156 -20
  6. package/dist/node_modules/@github/copilot-sdk/dist/cjs/extension.js +11 -9
  7. package/dist/node_modules/@github/copilot-sdk/dist/cjs/ffiRuntimeHost.js +14 -3
  8. package/dist/node_modules/@github/copilot-sdk/dist/cjs/generated/rpc.js +1305 -304
  9. package/dist/node_modules/@github/copilot-sdk/dist/cjs/host.js +67 -0
  10. package/dist/node_modules/@github/copilot-sdk/dist/cjs/index.js +12 -7
  11. package/dist/node_modules/@github/copilot-sdk/dist/cjs/installationConfirmation.js +98 -0
  12. package/dist/node_modules/@github/copilot-sdk/dist/cjs/runtimeArtifacts.js +1 -0
  13. package/dist/node_modules/@github/copilot-sdk/dist/cjs/schema.js +40 -0
  14. package/dist/node_modules/@github/copilot-sdk/dist/cjs/session.js +474 -151
  15. package/dist/node_modules/@github/copilot-sdk/dist/cjs/sessionFsProvider.js +57 -0
  16. package/dist/node_modules/@github/copilot-sdk/dist/cjs/types.js +5 -2
  17. package/dist/node_modules/@github/copilot-sdk/dist/cjs/{factory.js → workflow.js} +33 -33
  18. package/dist/node_modules/@github/copilot-sdk/package.json +27 -16
  19. package/dist/node_modules/zod/locales/package.json +1 -2
  20. package/dist/node_modules/zod/mini/package.json +1 -2
  21. package/dist/node_modules/zod/package.json +1 -1
  22. package/dist/node_modules/zod/v3/package.json +1 -2
  23. package/dist/node_modules/zod/v4/classic/errors.js +2 -2
  24. package/dist/node_modules/zod/v4/classic/from-json-schema.cjs +16 -31
  25. package/dist/node_modules/zod/v4/classic/from-json-schema.js +16 -31
  26. package/dist/node_modules/zod/v4/classic/package.json +1 -2
  27. package/dist/node_modules/zod/v4/classic/schemas.cjs +119 -358
  28. package/dist/node_modules/zod/v4/classic/schemas.js +118 -356
  29. package/dist/node_modules/zod/v4/core/api.cjs +2 -7
  30. package/dist/node_modules/zod/v4/core/api.js +2 -7
  31. package/dist/node_modules/zod/v4/core/core.cjs +1 -3
  32. package/dist/node_modules/zod/v4/core/core.js +2 -4
  33. package/dist/node_modules/zod/v4/core/errors.cjs +23 -26
  34. package/dist/node_modules/zod/v4/core/errors.js +23 -26
  35. package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +20 -16
  36. package/dist/node_modules/zod/v4/core/json-schema-processors.js +20 -16
  37. package/dist/node_modules/zod/v4/core/package.json +1 -2
  38. package/dist/node_modules/zod/v4/core/parse.cjs +7 -7
  39. package/dist/node_modules/zod/v4/core/parse.js +7 -7
  40. package/dist/node_modules/zod/v4/core/regexes.cjs +3 -9
  41. package/dist/node_modules/zod/v4/core/regexes.js +1 -7
  42. package/dist/node_modules/zod/v4/core/schemas.cjs +65 -211
  43. package/dist/node_modules/zod/v4/core/schemas.js +64 -210
  44. package/dist/node_modules/zod/v4/core/to-json-schema.cjs +1 -12
  45. package/dist/node_modules/zod/v4/core/to-json-schema.js +1 -12
  46. package/dist/node_modules/zod/v4/core/util.cjs +30 -54
  47. package/dist/node_modules/zod/v4/core/util.js +32 -55
  48. package/dist/node_modules/zod/v4/core/versions.cjs +2 -2
  49. package/dist/node_modules/zod/v4/core/versions.js +2 -2
  50. package/dist/node_modules/zod/v4/locales/en.cjs +0 -4
  51. package/dist/node_modules/zod/v4/locales/en.js +0 -4
  52. package/dist/node_modules/zod/v4/locales/fr.cjs +7 -24
  53. package/dist/node_modules/zod/v4/locales/fr.js +7 -24
  54. package/dist/node_modules/zod/v4/locales/index.cjs +1 -8
  55. package/dist/node_modules/zod/v4/locales/index.js +0 -3
  56. package/dist/node_modules/zod/v4/locales/it.cjs +1 -1
  57. package/dist/node_modules/zod/v4/locales/it.js +1 -1
  58. package/dist/node_modules/zod/v4/locales/ka.cjs +8 -8
  59. package/dist/node_modules/zod/v4/locales/ka.js +8 -8
  60. package/dist/node_modules/zod/v4/locales/package.json +1 -2
  61. package/dist/node_modules/zod/v4/locales/uz.cjs +0 -1
  62. package/dist/node_modules/zod/v4/locales/uz.js +0 -1
  63. package/dist/node_modules/zod/v4/mini/package.json +1 -2
  64. package/dist/node_modules/zod/v4/mini/schemas.cjs +4 -41
  65. package/dist/node_modules/zod/v4/mini/schemas.js +4 -40
  66. package/dist/node_modules/zod/v4/package.json +1 -2
  67. package/dist/node_modules/zod/v4-mini/package.json +1 -2
  68. package/package.json +20 -20
  69. package/dist/node_modules/zod/v4/locales/el.cjs +0 -136
  70. package/dist/node_modules/zod/v4/locales/el.js +0 -109
  71. package/dist/node_modules/zod/v4/locales/hr.cjs +0 -149
  72. package/dist/node_modules/zod/v4/locales/hr.js +0 -122
  73. package/dist/node_modules/zod/v4/locales/ro.cjs +0 -146
  74. package/dist/node_modules/zod/v4/locales/ro.js +0 -119
@@ -26,28 +26,41 @@ 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
- var import_factory = require("./factory.js");
31
- function isFactoryResumeErrorCode(value) {
32
- return value === "not_found" || value === "non_resumable" || value === "already_active" || value === "factory_already_running" || value === "factory_limits_invalid" || value === "factory_session_disposed" || value === "factory_storage_unavailable" || value === "factory_storage_corrupt";
31
+ var import_workflow = require("./workflow.js");
32
+ function isWorkflowResumeErrorCode(value) {
33
+ return value === "not_found" || value === "non_resumable" || value === "workflow_run_not_resumable" || value === "already_active" || value === "workflow_already_running" || value === "workflow_limits_invalid" || value === "workflow_session_disposed" || value === "workflow_storage_unavailable" || value === "workflow_storage_corrupt";
33
34
  }
34
- function copyDefinedFactoryAgentOption(source, target, key) {
35
+ function copyDefinedWorkflowAgentOption(source, target, key) {
35
36
  const value = source[key];
36
37
  if (value !== void 0) {
37
38
  target[key] = value;
38
39
  }
39
40
  }
40
- const factoryExecutionStore = new import_node_async_hooks.AsyncLocalStorage();
41
- function throwIfFactoryExecutionIsActive() {
42
- if (factoryExecutionStore.getStore()?.active) {
41
+ function toToolDefinition(tool) {
42
+ return {
43
+ name: tool.name,
44
+ description: tool.description ?? "",
45
+ parameters: (0, import_schema.toJsonSchema)(tool.parameters),
46
+ overridesBuiltInTool: tool.overridesBuiltInTool,
47
+ skipPermission: tool.skipPermission,
48
+ defer: tool.defer,
49
+ metadata: tool.metadata,
50
+ isTerminal: tool.isTerminal
51
+ };
52
+ }
53
+ const workflowExecutionStore = new import_node_async_hooks.AsyncLocalStorage();
54
+ function throwIfWorkflowExecutionIsActive() {
55
+ if (workflowExecutionStore.getStore()?.active) {
43
56
  throw new Error(
44
- "factory.run, factory.resume, and factory.pause are not allowed while a factory body is running on this call path."
57
+ "workflow.run, workflow.resume, and workflow.pause are not allowed while a workflow body is running on this call path."
45
58
  );
46
59
  }
47
60
  }
48
- function runInFactoryHelperScope(helperScope, callback) {
49
- const current = factoryExecutionStore.getStore();
50
- return factoryExecutionStore.run({ active: current?.active ?? false, helperScope }, callback);
61
+ function runInWorkflowHelperScope(helperScope, callback) {
62
+ const current = workflowExecutionStore.getStore();
63
+ return workflowExecutionStore.run({ active: current?.active ?? false, helperScope }, callback);
51
64
  }
52
65
  function deserializeHookInput(raw) {
53
66
  if (!raw || typeof raw !== "object" || typeof raw.timestamp !== "number") {
@@ -69,22 +82,22 @@ function isOpenCanvasInstance(value) {
69
82
  const instance = value;
70
83
  return typeof instance.instanceId === "string" && instance.instanceId.length > 0 && typeof instance.extensionId === "string" && instance.extensionId.length > 0 && typeof instance.canvasId === "string" && instance.canvasId.length > 0;
71
84
  }
72
- const FACTORY_LOG_FLUSH_DELAY_MS = 10;
73
- const MAX_FACTORY_FANOUT_ITEMS = 4096;
74
- function assertFactoryFanoutSize(kind, size) {
75
- if (size > MAX_FACTORY_FANOUT_ITEMS) {
85
+ const WORKFLOW_LOG_FLUSH_DELAY_MS = 10;
86
+ const MAX_WORKFLOW_FANOUT_ITEMS = 4096;
87
+ function assertWorkflowFanoutSize(kind, size) {
88
+ if (size > MAX_WORKFLOW_FANOUT_ITEMS) {
76
89
  throw new Error(
77
- `${kind}() accepts at most ${MAX_FACTORY_FANOUT_ITEMS} items; got ${size}.`
90
+ `${kind}() accepts at most ${MAX_WORKFLOW_FANOUT_ITEMS} items; got ${size}.`
78
91
  );
79
92
  }
80
93
  }
81
- async function runFactoryParallel(thunks) {
94
+ async function runWorkflowParallel(thunks) {
82
95
  if (!Array.isArray(thunks)) {
83
96
  throw new Error(
84
97
  "parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
85
98
  );
86
99
  }
87
- assertFactoryFanoutSize("parallel", thunks.length);
100
+ assertWorkflowFanoutSize("parallel", thunks.length);
88
101
  if (thunks.some((thunk) => typeof thunk !== "function")) {
89
102
  throw new Error(
90
103
  "parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
@@ -92,8 +105,8 @@ async function runFactoryParallel(thunks) {
92
105
  }
93
106
  return Promise.all(
94
107
  thunks.map(
95
- (thunk) => Promise.resolve().then(() => runInFactoryHelperScope("parallel", thunk)).catch((error) => {
96
- if (isFactoryFatalError(error)) {
108
+ (thunk) => Promise.resolve().then(() => runInWorkflowHelperScope("parallel", thunk)).catch((error) => {
109
+ if (isWorkflowFatalError(error)) {
97
110
  throw error;
98
111
  }
99
112
  return null;
@@ -101,22 +114,22 @@ async function runFactoryParallel(thunks) {
101
114
  )
102
115
  );
103
116
  }
104
- async function runFactoryPipeline(items, ...stages) {
117
+ async function runWorkflowPipeline(items, ...stages) {
105
118
  if (!Array.isArray(items)) {
106
119
  throw new Error("pipeline(items, ...stages): items must be an array");
107
120
  }
108
- assertFactoryFanoutSize("pipeline", items.length);
121
+ assertWorkflowFanoutSize("pipeline", items.length);
109
122
  return Promise.all(
110
123
  items.map(async (item, index) => {
111
124
  let previous = item;
112
125
  for (const stage of stages) {
113
126
  try {
114
- previous = await runInFactoryHelperScope(
127
+ previous = await runInWorkflowHelperScope(
115
128
  "pipeline",
116
129
  () => stage(previous, item, index)
117
130
  );
118
131
  } catch (error) {
119
- if (isFactoryFatalError(error)) {
132
+ if (isWorkflowFatalError(error)) {
120
133
  throw error;
121
134
  }
122
135
  return null;
@@ -126,7 +139,7 @@ async function runFactoryPipeline(items, ...stages) {
126
139
  })
127
140
  );
128
141
  }
129
- class FactoryProgressBuffer {
142
+ class WorkflowProgressBuffer {
130
143
  constructor(send) {
131
144
  this.send = send;
132
145
  }
@@ -140,7 +153,7 @@ class FactoryProgressBuffer {
140
153
  closed = false;
141
154
  enqueue(kind, text) {
142
155
  if (this.closed) {
143
- throw new Error("Cannot log after the factory run has settled");
156
+ throw new Error("Cannot log after the workflow run has settled");
144
157
  }
145
158
  this.pending.push({ seq: this.nextSeq++, kind, text });
146
159
  this.scheduleFlush();
@@ -172,7 +185,7 @@ class FactoryProgressBuffer {
172
185
  await this.flushTail;
173
186
  if (this.flushFailed) {
174
187
  console.warn(
175
- "Ignoring a background factory progress flush failure after the factory body settled",
188
+ "Ignoring a background workflow progress flush failure after the workflow body settled",
176
189
  this.flushError
177
190
  );
178
191
  }
@@ -181,7 +194,7 @@ class FactoryProgressBuffer {
181
194
  await this.send(lines);
182
195
  } catch (error) {
183
196
  console.warn(
184
- "Failed to flush final factory progress after the factory body settled",
197
+ "Failed to flush final workflow progress after the workflow body settled",
185
198
  error
186
199
  );
187
200
  }
@@ -195,7 +208,7 @@ class FactoryProgressBuffer {
195
208
  this.flushTimer = void 0;
196
209
  void this.flush().catch(() => {
197
210
  });
198
- }, FACTORY_LOG_FLUSH_DELAY_MS);
211
+ }, WORKFLOW_LOG_FLUSH_DELAY_MS);
199
212
  this.flushTimer.unref?.();
200
213
  }
201
214
  clearFlushTimer() {
@@ -205,30 +218,30 @@ class FactoryProgressBuffer {
205
218
  }
206
219
  }
207
220
  }
208
- async function awaitFactoryOperation(operation, signal) {
221
+ async function awaitWorkflowOperation(operation, signal) {
209
222
  let rejectAbort;
210
223
  const abortPromise = new Promise((_resolve, reject) => {
211
224
  rejectAbort = reject;
212
225
  });
213
- const onAbort = () => rejectAbort?.(signal.reason ?? new DOMException("Factory run was aborted", "AbortError"));
226
+ const onAbort = () => rejectAbort?.(signal.reason ?? new DOMException("Workflow run was aborted", "AbortError"));
214
227
  signal.addEventListener("abort", onAbort, { once: true });
215
228
  try {
216
- throwIfFactoryAborted(signal);
229
+ throwIfWorkflowAborted(signal);
217
230
  return await Promise.race([operation(), abortPromise]);
218
231
  } finally {
219
232
  signal.removeEventListener("abort", onAbort);
220
233
  }
221
234
  }
222
- function throwIfFactoryAborted(signal) {
235
+ function throwIfWorkflowAborted(signal) {
223
236
  if (signal.aborted) {
224
- throw signal.reason ?? new DOMException("Factory run was aborted", "AbortError");
237
+ throw signal.reason ?? new DOMException("Workflow run was aborted", "AbortError");
225
238
  }
226
239
  }
227
- function isFactoryAbortError(error) {
240
+ function isWorkflowAbortError(error) {
228
241
  return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
229
242
  }
230
- function isFactoryFatalError(error) {
231
- return isFactoryAbortError(error) || error instanceof import_node.ResponseError || error instanceof import_node.ConnectionError;
243
+ function isWorkflowFatalError(error) {
244
+ return isWorkflowAbortError(error) || error instanceof import_node.ResponseError || error instanceof import_node.ConnectionError;
232
245
  }
233
246
  const TOOL_SEARCH_TOOL_NAME = "tool_search_tool";
234
247
  class CopilotSession {
@@ -253,21 +266,25 @@ class CopilotSession {
253
266
  sessionId;
254
267
  connection;
255
268
  _workspacePath;
269
+ _transcriptRecovery;
256
270
  eventHandlers = /* @__PURE__ */ new Set();
257
271
  typedEventHandlers = /* @__PURE__ */ new Map();
258
272
  toolHandlers = /* @__PURE__ */ new Map();
273
+ /** Settles once every earlier `setTools` call has finished. */
274
+ setToolsQueue = Promise.resolve();
259
275
  pendingExternalTools = /* @__PURE__ */ new Map();
260
276
  canvases = /* @__PURE__ */ new Map();
261
277
  bearerTokenProviders = /* @__PURE__ */ new Map();
262
278
  commandHandlers = /* @__PURE__ */ new Map();
263
- factories = /* @__PURE__ */ new Map();
264
- factoryAbortControllers = /* @__PURE__ */ new Map();
279
+ workflows = /* @__PURE__ */ new Map();
280
+ workflowAbortControllers = /* @__PURE__ */ new Map();
265
281
  permissionHandler;
266
282
  mcpAuthHandler;
267
283
  userInputHandler;
268
284
  elicitationHandler;
269
285
  exitPlanModeHandler;
270
286
  autoModeSwitchHandler;
287
+ skillProvider;
271
288
  hooks;
272
289
  transformCallbacks;
273
290
  _rpc = null;
@@ -277,28 +294,23 @@ class CopilotSession {
277
294
  _capabilities = {};
278
295
  openCanvasInstances = [];
279
296
  disconnected = false;
297
+ pendingResponseWaits = /* @__PURE__ */ new Set();
298
+ pendingStructuredWaits = /* @__PURE__ */ new Set();
280
299
  disconnecting = false;
281
300
  onDisconnected;
282
301
  /** @internal Client session API handlers, populated by CopilotClient during create/resume. */
283
302
  clientSessionApis = {};
284
303
  /**
285
- * Friendly factory API for running registered factories by name or handle.
304
+ * Friendly workflow API for running registered workflows by name or handle.
286
305
  *
287
- * @experimental Part of the experimental Agent Factories surface and may
306
+ * @experimental Part of the experimental Dynamic Workflows surface and may
288
307
  * change or be removed in future SDK or CLI releases.
289
308
  */
290
- factory = {
309
+ workflow = {
291
310
  run: (async (nameOrHandle, options) => {
292
- throwIfFactoryExecutionIsActive();
293
- const name = typeof nameOrHandle === "string" ? nameOrHandle : (0, import_factory.getFactoryDefinition)(nameOrHandle).meta.name;
294
- if (options?.resumeFromRunId !== void 0) {
295
- return this.factory.resume(options.resumeFromRunId, {
296
- limits: options.limits,
297
- notifyOnComplete: options.notifyOnComplete,
298
- logPhaseNames: options.logPhaseNames
299
- });
300
- }
301
- const envelope = await this.rpc.factory.run({
311
+ throwIfWorkflowExecutionIsActive();
312
+ const name = typeof nameOrHandle === "string" ? nameOrHandle : (0, import_workflow.getWorkflowDefinition)(nameOrHandle).meta.name;
313
+ const envelope = await this.rpc.workflow.run({
302
314
  name,
303
315
  args: options?.args === void 0 ? {} : options.args,
304
316
  options: {
@@ -307,13 +319,13 @@ class CopilotSession {
307
319
  logPhaseNames: options?.logPhaseNames
308
320
  }
309
321
  });
310
- return this.settleFactoryRun(envelope);
322
+ return this.settleWorkflowRun(envelope);
311
323
  }),
312
324
  resume: (async (runId, options) => {
313
- throwIfFactoryExecutionIsActive();
325
+ throwIfWorkflowExecutionIsActive();
314
326
  let response;
315
327
  try {
316
- response = await this.rpc.factory.resume({
328
+ response = await this.rpc.workflow.resume({
317
329
  runId,
318
330
  limits: options?.limits,
319
331
  notifyOnComplete: options?.notifyOnComplete,
@@ -322,43 +334,36 @@ class CopilotSession {
322
334
  } catch (error) {
323
335
  if (error instanceof import_node.ResponseError && typeof error.data === "object" && error.data !== null) {
324
336
  const code = error.data.code;
325
- if (isFactoryResumeErrorCode(code)) {
326
- throw new import_factory.FactoryResumeError(code, error.message);
337
+ if (isWorkflowResumeErrorCode(code)) {
338
+ throw new import_workflow.WorkflowResumeError(code, error.message);
327
339
  }
328
340
  }
329
341
  throw error;
330
342
  }
331
- return this.settleFactoryRun(response.run);
343
+ return this.settleWorkflowRun(response.run);
332
344
  }),
333
- getRun: async (runId) => this.rpc.factory.getRun({ runId }),
334
- waitForRun: (runId, options) => this.waitForFactoryRun(runId, options?.signal),
345
+ getRun: async (runId) => this.rpc.workflow.getRun({ runId }),
346
+ waitForRun: (runId, options) => this.waitForWorkflowRun(runId, options?.signal),
335
347
  listRuns: (async (options) => {
336
- const page = await this.rpc.factory.listRuns(options ?? {});
348
+ const page = await this.rpc.workflow.listRuns(options ?? {});
337
349
  return options === void 0 ? page.runs : page;
338
350
  }),
339
- getRunDetail: (runId) => this.rpc.factory.getRunDetail({ runId }),
340
- getRunProgress: (runId, options = {}) => this.rpc.factory.getRunProgress({ runId, ...options }),
351
+ getRunDetail: (runId) => this.rpc.workflow.getRunDetail({ runId }),
352
+ getRunProgress: (runId, options = {}) => this.rpc.workflow.getRunProgress({ runId, ...options }),
341
353
  pause: async (runId) => {
342
- throwIfFactoryExecutionIsActive();
343
- return this.rpc.factory.pause({ runId });
354
+ throwIfWorkflowExecutionIsActive();
355
+ return this.rpc.workflow.pause({ runId });
344
356
  },
345
- cancel: async (runId) => this.rpc.factory.cancel({ runId })
357
+ cancel: async (runId) => this.rpc.workflow.cancel({ runId })
346
358
  };
347
- /**
348
- * Resolve a start/resume envelope into the terminal envelope callers expect.
349
- *
350
- * The CLI may answer `session.factory.run` and `session.factory.resume`
351
- * before the run settles, so a non-terminal envelope is followed by a wait
352
- * on the run's terminal state.
353
- */
354
- settleFactoryRun(envelope) {
355
- if ((0, import_factory.isFactoryRunTerminal)(envelope.status)) {
359
+ settleWorkflowRun(envelope) {
360
+ if ((0, import_workflow.isWorkflowRunTerminal)(envelope.status)) {
356
361
  return Promise.resolve(envelope);
357
362
  }
358
- return this.waitForFactoryRun(envelope.runId);
363
+ return this.waitForWorkflowRun(envelope.runId);
359
364
  }
360
365
  /**
361
- * Resolve when a factory run reaches a terminal status.
366
+ * Resolve when a workflow run reaches a terminal status.
362
367
  *
363
368
  * The subscription is installed *before* the first read so a transition
364
369
  * landing between the two cannot be missed, and re-reads are serialized so
@@ -367,8 +372,8 @@ class CopilotSession {
367
372
  * collapse into a single in-flight read. A bounded periodic re-read keeps a
368
373
  * dropped invalidation from leaving the wait pending forever.
369
374
  */
370
- waitForFactoryRun(runId, signal) {
371
- const abortError = () => signal?.reason ?? new DOMException("Factory run wait was aborted", "AbortError");
375
+ waitForWorkflowRun(runId, signal) {
376
+ const abortError = () => signal?.reason ?? new DOMException("Workflow run wait was aborted", "AbortError");
372
377
  if (signal?.aborted === true) {
373
378
  return Promise.reject(abortError());
374
379
  }
@@ -405,8 +410,8 @@ class CopilotSession {
405
410
  try {
406
411
  do {
407
412
  rereadRequested = false;
408
- const envelope = await this.rpc.factory.getRun({ runId });
409
- if ((0, import_factory.isFactoryRunTerminal)(envelope.status)) {
413
+ const envelope = await this.rpc.workflow.getRun({ runId });
414
+ if ((0, import_workflow.isWorkflowRunTerminal)(envelope.status)) {
410
415
  finish(() => resolve(envelope));
411
416
  return;
412
417
  }
@@ -421,7 +426,7 @@ class CopilotSession {
421
426
  onAbort = () => finish(() => reject(abortError()));
422
427
  signal.addEventListener("abort", onAbort, { once: true });
423
428
  }
424
- unsubscribe = this.on("factory.run_updated", (event) => {
429
+ unsubscribe = this.on("workflow.run_updated", (event) => {
425
430
  if (event.data.runId === runId) {
426
431
  void read();
427
432
  }
@@ -455,6 +460,14 @@ class CopilotSession {
455
460
  get workspacePath() {
456
461
  return this._workspacePath;
457
462
  }
463
+ /** Recovery observed while loading this session; undefined on a clean resume. */
464
+ get transcriptRecovery() {
465
+ return this._transcriptRecovery;
466
+ }
467
+ /** @internal */
468
+ setTranscriptRecovery(recovery) {
469
+ this._transcriptRecovery = recovery;
470
+ }
458
471
  /**
459
472
  * Host capabilities reported when the session was created or resumed.
460
473
  * Use this to check feature support before calling capability-gated APIs.
@@ -493,19 +506,61 @@ class CopilotSession {
493
506
  attachments: options.attachments,
494
507
  mode: options.mode,
495
508
  agentMode: options.agentMode,
496
- requestHeaders: options.requestHeaders
509
+ requestHeaders: options.requestHeaders,
510
+ ...options.responseSchema ? {
511
+ responseFormat: {
512
+ type: "json_schema",
513
+ jsonSchema: {
514
+ name: "response",
515
+ strict: true,
516
+ schema: (0, import_schema.toJsonSchema)(options.responseSchema)
517
+ }
518
+ }
519
+ } : {}
497
520
  });
498
521
  return response.messageId;
499
522
  }
500
- async sendAndWait(optionsOrPrompt, timeout) {
523
+ async sendAndWait(optionsOrPrompt, schemaOrTimeout, timeout) {
501
524
  const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
502
- const effectiveTimeout = timeout ?? 6e4;
525
+ const typedSchema = (0, import_schema.isResponseSchema)(schemaOrTimeout) ? schemaOrTimeout : void 0;
526
+ if (schemaOrTimeout !== void 0 && typeof schemaOrTimeout !== "number" && !typedSchema) {
527
+ throw new TypeError(
528
+ "The second argument must be a timeout or a schema with toJSONSchema() and parse(). Pass raw JSON Schema in options.responseSchema instead."
529
+ );
530
+ }
531
+ const effectiveTimeout = (typeof schemaOrTimeout === "number" ? schemaOrTimeout : timeout) ?? 6e4;
532
+ if (typedSchema && options.responseSchema) {
533
+ throw new Error(
534
+ "Do not specify responseSchema in options when requesting a typed response."
535
+ );
536
+ }
537
+ if (typedSchema && options.mode === "immediate") {
538
+ throw new Error(
539
+ "Structured output cannot be requested on an immediate steering message."
540
+ );
541
+ }
542
+ if (typedSchema || options.responseSchema) {
543
+ const message = await this.sendAndWaitForStructuredMessage(
544
+ typedSchema ? { ...options, responseSchema: typedSchema } : options,
545
+ effectiveTimeout
546
+ );
547
+ if (typedSchema) {
548
+ if (!message) {
549
+ throw new Error(
550
+ "The requested run completed without a structured assistant response."
551
+ );
552
+ }
553
+ return typedSchema.parse(JSON.parse(message.data.content));
554
+ }
555
+ return message;
556
+ }
503
557
  let resolveOutcome;
504
558
  const outcomePromise = new Promise((resolve) => {
505
559
  resolveOutcome = resolve;
506
560
  });
507
561
  let lastAssistantMessage;
508
562
  const unsubscribe = this.on((event) => {
563
+ if (event.agentId) return;
509
564
  if (event.type === "assistant.message") {
510
565
  lastAssistantMessage = event;
511
566
  } else if (event.type === "session.idle" && event.data.mode !== "autopilot") {
@@ -516,6 +571,8 @@ class CopilotSession {
516
571
  resolveOutcome({ kind: "error", error });
517
572
  }
518
573
  });
574
+ const fail = (error) => resolveOutcome({ kind: "error", error });
575
+ this.pendingResponseWaits.add(fail);
519
576
  let timeoutId;
520
577
  try {
521
578
  await this.send(options);
@@ -539,6 +596,91 @@ class CopilotSession {
539
596
  clearTimeout(timeoutId);
540
597
  }
541
598
  unsubscribe();
599
+ this.pendingResponseWaits.delete(fail);
600
+ }
601
+ }
602
+ async sendAndWaitForStructuredMessage(options, timeout) {
603
+ if (this.disconnected) {
604
+ throw new Error("Session is disconnected");
605
+ }
606
+ let resolveOutcome;
607
+ const outcomePromise = new Promise((resolve) => {
608
+ resolveOutcome = resolve;
609
+ });
610
+ const fail = (error) => resolveOutcome({ kind: "error", error });
611
+ let messageId;
612
+ let consumed = false;
613
+ let lastMessage;
614
+ const buffered = [];
615
+ const observe = (event) => {
616
+ if (event.agentId) return;
617
+ if (event.type === "user.message" && event.data.messageId === messageId) {
618
+ consumed = true;
619
+ } else if (event.type === "assistant.message" && event.data.originatingMessageId === messageId) {
620
+ consumed = true;
621
+ lastMessage = event.data.toolRequests?.length ? void 0 : event;
622
+ } else if (consumed && event.type === "session.idle" && event.data.mode !== "autopilot") {
623
+ if (event.data.aborted) {
624
+ fail(
625
+ new Error(
626
+ "The requested run was aborted before a structured result was completed."
627
+ )
628
+ );
629
+ } else {
630
+ resolveOutcome({ kind: "idle", message: lastMessage });
631
+ }
632
+ } else if (consumed && event.type === "session.error") {
633
+ const error = new Error(event.data.message);
634
+ error.stack = event.data.stack;
635
+ fail(error);
636
+ }
637
+ };
638
+ const unsubscribe = this.on((event) => {
639
+ if (event.type !== "user.message" && event.type !== "assistant.message" && event.type !== "session.idle" && event.type !== "session.error") {
640
+ return;
641
+ }
642
+ if (messageId === void 0) {
643
+ buffered.push(event);
644
+ } else {
645
+ observe(event);
646
+ }
647
+ });
648
+ this.pendingStructuredWaits.add(fail);
649
+ const timer = setTimeout(
650
+ () => fail(new Error(`Timeout after ${timeout}ms waiting for the structured response`)),
651
+ timeout
652
+ );
653
+ try {
654
+ const sendOutcome = this.send(options).then(
655
+ (id) => {
656
+ if (!id) {
657
+ throw new Error(
658
+ "The runtime did not return a message ID for the structured send."
659
+ );
660
+ }
661
+ messageId = id;
662
+ for (const event of buffered) observe(event);
663
+ buffered.length = 0;
664
+ return outcomePromise;
665
+ },
666
+ (error) => ({
667
+ kind: "error",
668
+ error: error instanceof Error ? error : new Error(String(error))
669
+ })
670
+ );
671
+ const outcome = await Promise.race([sendOutcome, outcomePromise]);
672
+ if (outcome.kind === "error") throw outcome.error;
673
+ return outcome.message;
674
+ } finally {
675
+ clearTimeout(timer);
676
+ buffered.length = 0;
677
+ unsubscribe();
678
+ this.pendingStructuredWaits.delete(fail);
679
+ }
680
+ }
681
+ assertConnected() {
682
+ if (this.disconnected) {
683
+ throw new Error("Session is disconnected");
542
684
  }
543
685
  }
544
686
  /** @internal */
@@ -547,40 +689,62 @@ class CopilotSession {
547
689
  return;
548
690
  }
549
691
  this.disconnected = true;
692
+ for (const fail of this.pendingResponseWaits) {
693
+ fail(new Error("Session disconnected while waiting for a response"));
694
+ }
695
+ this.pendingResponseWaits.clear();
696
+ for (const fail of this.pendingStructuredWaits) {
697
+ fail(new Error("Session disconnected while waiting for a structured response"));
698
+ }
699
+ this.pendingStructuredWaits.clear();
550
700
  for (const controller of this.pendingExternalTools.values()) {
551
701
  controller.abort();
552
702
  }
553
703
  this.pendingExternalTools.clear();
554
- this._runOnDisconnected();
555
704
  this.eventHandlers.clear();
556
705
  this.typedEventHandlers.clear();
557
706
  this.toolHandlers.clear();
558
707
  this.permissionHandler = void 0;
708
+ this.mcpAuthHandler = void 0;
559
709
  this.userInputHandler = void 0;
560
710
  this.elicitationHandler = void 0;
561
711
  this.exitPlanModeHandler = void 0;
562
712
  this.autoModeSwitchHandler = void 0;
713
+ this.skillProvider = void 0;
714
+ this.hooks = void 0;
715
+ this.traceContextProvider = void 0;
716
+ this.bearerTokenProviders.clear();
563
717
  this.commandHandlers.clear();
564
718
  this.canvases.clear();
565
- this.factories.clear();
566
- for (const controllersForRun of this.factoryAbortControllers.values()) {
719
+ this.workflows.clear();
720
+ for (const controllersForRun of this.workflowAbortControllers.values()) {
567
721
  for (const controller of controllersForRun.values()) {
568
722
  controller.abort();
569
723
  }
570
724
  }
571
- this.factoryAbortControllers.clear();
572
- this.transformCallbacks?.clear();
725
+ this.workflowAbortControllers.clear();
726
+ this.transformCallbacks = void 0;
727
+ delete this.clientSessionApis.providerToken;
728
+ delete this.clientSessionApis.canvas;
729
+ delete this.clientSessionApis.workflow;
730
+ delete this.clientSessionApis.sessionFs;
731
+ this._runOnDisconnected();
573
732
  }
574
733
  /** @internal */
575
734
  _runOnDisconnected() {
576
- this.onDisconnected?.();
735
+ const callback = this.onDisconnected;
577
736
  this.onDisconnected = void 0;
737
+ callback?.();
578
738
  }
579
739
  /** @internal */
580
740
  _setOnDisconnected(callback) {
741
+ this.assertConnected();
581
742
  this.onDisconnected = callback;
582
743
  }
583
744
  on(eventTypeOrHandler, handler) {
745
+ if (this.disconnected) {
746
+ throw new Error("Session is disconnected");
747
+ }
584
748
  if (typeof eventTypeOrHandler === "string" && handler) {
585
749
  const eventType = eventTypeOrHandler;
586
750
  if (!this.typedEventHandlers.has(eventType)) {
@@ -651,7 +815,8 @@ class CopilotSession {
651
815
  args,
652
816
  handler,
653
817
  traceparent,
654
- tracestate
818
+ tracestate,
819
+ event.agentId || void 0
655
820
  );
656
821
  }
657
822
  } else if (event.type === "external_tool.completed") {
@@ -742,7 +907,7 @@ class CopilotSession {
742
907
  * Executes a tool handler and sends the result back via RPC.
743
908
  * @internal
744
909
  */
745
- async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate) {
910
+ async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler, traceparent, tracestate, agentId) {
746
911
  const controller = new AbortController();
747
912
  if (this.disconnected || this.pendingExternalTools.has(requestId)) {
748
913
  return;
@@ -780,7 +945,9 @@ class CopilotSession {
780
945
  availableTools,
781
946
  traceparent,
782
947
  tracestate,
783
- signal: controller.signal
948
+ signal: controller.signal,
949
+ agentId,
950
+ requestId
784
951
  });
785
952
  let result;
786
953
  if (rawResult == null) {
@@ -941,13 +1108,27 @@ class CopilotSession {
941
1108
  * @internal This method is typically called internally when creating a session with tools.
942
1109
  */
943
1110
  registerTools(tools) {
1111
+ this.assertConnected();
944
1112
  this.toolHandlers.clear();
945
1113
  if (!tools) {
946
1114
  return;
947
1115
  }
948
1116
  for (const tool of tools) {
949
1117
  if (tool.handler) {
950
- this.toolHandlers.set(tool.name, tool.handler);
1118
+ const handler = tool.handler;
1119
+ if (tool.name === "apply_patch" && tool.overridesBuiltInTool && (0, import_schema.toJsonSchema)(tool.parameters)?.type === "string") {
1120
+ this.toolHandlers.set(tool.name, (args, invocation) => {
1121
+ if (typeof args === "string") {
1122
+ return handler(args, invocation);
1123
+ }
1124
+ if (typeof args === "object" && args !== null && "input" in args && typeof args.input === "string") {
1125
+ return handler(args.input, invocation);
1126
+ }
1127
+ throw new TypeError("apply_patch string override requires a string input");
1128
+ });
1129
+ } else {
1130
+ this.toolHandlers.set(tool.name, handler);
1131
+ }
951
1132
  }
952
1133
  }
953
1134
  }
@@ -968,6 +1149,7 @@ class CopilotSession {
968
1149
  * @internal Called by the SDK when creating/resuming a session with `canvases`.
969
1150
  */
970
1151
  registerCanvases(canvases) {
1152
+ this.assertConnected();
971
1153
  this.canvases.clear();
972
1154
  if (!canvases || canvases.length === 0) {
973
1155
  delete this.clientSessionApis.canvas;
@@ -1017,46 +1199,47 @@ class CopilotSession {
1017
1199
  };
1018
1200
  }
1019
1201
  /**
1020
- * Registers factory closures and reverse-RPC handlers for this session.
1202
+ * Registers workflow closures and reverse-RPC handlers for this session.
1021
1203
  *
1022
- * @param factories - Factory handles declared by the joining extension.
1204
+ * @param workflows - Workflow handles declared by the joining extension.
1023
1205
  * @internal Called by the SDK when an extension joins a session.
1024
1206
  */
1025
- registerFactories(factories) {
1026
- this.factories.clear();
1027
- if (!factories || factories.length === 0) {
1028
- delete this.clientSessionApis.factory;
1207
+ registerWorkflows(workflows) {
1208
+ this.assertConnected();
1209
+ this.workflows.clear();
1210
+ if (!workflows || workflows.length === 0) {
1211
+ delete this.clientSessionApis.workflow;
1029
1212
  return;
1030
1213
  }
1031
- for (const handle of factories) {
1032
- const definition = (0, import_factory.getFactoryDefinition)(handle);
1033
- if (this.factories.has(definition.meta.name)) {
1214
+ for (const handle of workflows) {
1215
+ const definition = (0, import_workflow.getWorkflowDefinition)(handle);
1216
+ if (this.workflows.has(definition.meta.name)) {
1034
1217
  throw new Error(
1035
- `Duplicate factory name "${definition.meta.name}". Factory names must be unique within a joinSession call.`
1218
+ `Duplicate workflow name "${definition.meta.name}". Workflow names must be unique within a joinSession call.`
1036
1219
  );
1037
1220
  }
1038
- this.factories.set(definition.meta.name, definition);
1221
+ this.workflows.set(definition.meta.name, definition);
1039
1222
  }
1040
1223
  const self = this;
1041
- this.clientSessionApis.factory = {
1224
+ this.clientSessionApis.workflow = {
1042
1225
  async execute(params) {
1043
- const definition = self.factories.get(params.name);
1226
+ const definition = self.workflows.get(params.name);
1044
1227
  if (!definition) {
1045
- const message = `No factory registered with name "${params.name}"`;
1228
+ const message = `No workflow registered with name "${params.name}"`;
1046
1229
  throw new import_node.ResponseError(import_node.ErrorCodes.InvalidParams, message, {
1047
- code: "factory_not_found",
1230
+ code: "workflow_not_found",
1048
1231
  name: params.name
1049
1232
  });
1050
1233
  }
1051
1234
  const controller = new AbortController();
1052
- let controllersForRun = self.factoryAbortControllers.get(params.runId);
1235
+ let controllersForRun = self.workflowAbortControllers.get(params.runId);
1053
1236
  if (controllersForRun === void 0) {
1054
1237
  controllersForRun = /* @__PURE__ */ new Map();
1055
- self.factoryAbortControllers.set(params.runId, controllersForRun);
1238
+ self.workflowAbortControllers.set(params.runId, controllersForRun);
1056
1239
  }
1057
1240
  controllersForRun.set(params.executionToken, controller);
1058
- const progress = new FactoryProgressBuffer(async (lines) => {
1059
- await self.rpc.factory.log({
1241
+ const progress = new WorkflowProgressBuffer(async (lines) => {
1242
+ await self.rpc.workflow.log({
1060
1243
  runId: params.runId,
1061
1244
  executionToken: params.executionToken,
1062
1245
  lines
@@ -1069,22 +1252,22 @@ class CopilotSession {
1069
1252
  session: self,
1070
1253
  signal: controller.signal,
1071
1254
  phase: (title) => {
1072
- throwIfFactoryAborted(controller.signal);
1255
+ throwIfWorkflowAborted(controller.signal);
1073
1256
  progress.enqueue("phase", title);
1074
1257
  },
1075
1258
  log: (message) => {
1076
- throwIfFactoryAborted(controller.signal);
1259
+ throwIfWorkflowAborted(controller.signal);
1077
1260
  progress.enqueue("log", message);
1078
1261
  },
1079
1262
  agent: async (prompt, options = {}) => {
1080
1263
  await progress.flush();
1081
1264
  const opts = {};
1082
- for (const key of import_factory.FACTORY_AGENT_OPTION_KEYS) {
1083
- copyDefinedFactoryAgentOption(options, opts, key);
1265
+ for (const key of import_workflow.WORKFLOW_AGENT_OPTION_KEYS) {
1266
+ copyDefinedWorkflowAgentOption(options, opts, key);
1084
1267
  }
1085
- const response = await awaitFactoryOperation(
1086
- () => self.rpc.factory.agent({
1087
- factoryRunId: params.runId,
1268
+ const response = await awaitWorkflowOperation(
1269
+ () => self.rpc.workflow.agent({
1270
+ workflowRunId: params.runId,
1088
1271
  executionToken: params.executionToken,
1089
1272
  prompt,
1090
1273
  opts
@@ -1096,11 +1279,11 @@ class CopilotSession {
1096
1279
  step: async (key, producer, options = {}) => {
1097
1280
  await progress.flush();
1098
1281
  if (options.volatile) {
1099
- throwIfFactoryAborted(controller.signal);
1282
+ throwIfWorkflowAborted(controller.signal);
1100
1283
  return producer();
1101
1284
  }
1102
- const cached = await awaitFactoryOperation(
1103
- () => self.rpc.factory.journal.get({
1285
+ const cached = await awaitWorkflowOperation(
1286
+ () => self.rpc.workflow.journal.get({
1104
1287
  runId: params.runId,
1105
1288
  executionToken: params.executionToken,
1106
1289
  key
@@ -1113,13 +1296,13 @@ class CopilotSession {
1113
1296
  `step("${key}") journal returned a hit without a result`
1114
1297
  );
1115
1298
  }
1116
- assertFactoryStepResult(cached.resultJson, key);
1299
+ assertWorkflowStepResult(cached.resultJson, key);
1117
1300
  return cached.resultJson;
1118
1301
  }
1119
1302
  const result2 = await producer();
1120
- assertFactoryStepResult(result2, key);
1121
- await awaitFactoryOperation(
1122
- () => self.rpc.factory.journal.put({
1303
+ assertWorkflowStepResult(result2, key);
1304
+ await awaitWorkflowOperation(
1305
+ () => self.rpc.workflow.journal.put({
1123
1306
  runId: params.runId,
1124
1307
  executionToken: params.executionToken,
1125
1308
  key,
@@ -1131,17 +1314,17 @@ class CopilotSession {
1131
1314
  },
1132
1315
  pause: async (key) => {
1133
1316
  if (typeof key !== "string" || key.length === 0) {
1134
- throw new Error("Factory pause checkpoint key must not be empty");
1317
+ throw new Error("Workflow pause checkpoint key must not be empty");
1135
1318
  }
1136
- const helperScope = factoryExecutionStore.getStore()?.helperScope;
1319
+ const helperScope = workflowExecutionStore.getStore()?.helperScope;
1137
1320
  if (helperScope !== void 0) {
1138
1321
  throw new Error(
1139
- `Factory pause checkpoints are not allowed inside ${helperScope}() branches`
1322
+ `Workflow pause checkpoints are not allowed inside ${helperScope}() branches`
1140
1323
  );
1141
1324
  }
1142
1325
  await progress.flush();
1143
- const response = await awaitFactoryOperation(
1144
- () => self.internalRpc.factory.pauseAtCheckpoint({
1326
+ const response = await awaitWorkflowOperation(
1327
+ () => self.internalRpc.workflow.pauseAtCheckpoint({
1145
1328
  runId: params.runId,
1146
1329
  executionToken: params.executionToken,
1147
1330
  key
@@ -1152,21 +1335,21 @@ class CopilotSession {
1152
1335
  case "continue":
1153
1336
  return;
1154
1337
  case "pause":
1155
- await awaitFactoryOperation(
1338
+ await awaitWorkflowOperation(
1156
1339
  () => new Promise(() => {
1157
1340
  }),
1158
1341
  controller.signal
1159
1342
  );
1160
1343
  }
1161
1344
  },
1162
- parallel: runFactoryParallel,
1163
- pipeline: runFactoryPipeline,
1164
- factory: async () => {
1165
- throw new Error("nested factories are not supported");
1345
+ parallel: runWorkflowParallel,
1346
+ pipeline: runWorkflowPipeline,
1347
+ workflow: async () => {
1348
+ throw new Error("nested workflows are not supported");
1166
1349
  }
1167
1350
  };
1168
1351
  const execution = { active: true };
1169
- const result = await factoryExecutionStore.run(execution, async () => {
1352
+ const result = await workflowExecutionStore.run(execution, async () => {
1170
1353
  try {
1171
1354
  return await definition.run(context);
1172
1355
  } finally {
@@ -1176,27 +1359,27 @@ class CopilotSession {
1176
1359
  if (result === void 0) {
1177
1360
  return {};
1178
1361
  }
1179
- assertFactoryResult(result);
1362
+ assertWorkflowResult(result);
1180
1363
  return { result };
1181
1364
  } finally {
1182
1365
  try {
1183
1366
  await progress.close();
1184
1367
  } finally {
1185
- const controllersForRun2 = self.factoryAbortControllers.get(params.runId);
1368
+ const controllersForRun2 = self.workflowAbortControllers.get(params.runId);
1186
1369
  if (controllersForRun2?.get(params.executionToken) === controller) {
1187
1370
  controllersForRun2.delete(params.executionToken);
1188
1371
  if (controllersForRun2.size === 0) {
1189
- self.factoryAbortControllers.delete(params.runId);
1372
+ self.workflowAbortControllers.delete(params.runId);
1190
1373
  }
1191
1374
  }
1192
1375
  }
1193
1376
  }
1194
1377
  },
1195
1378
  async abort(params) {
1196
- const controllersForRun = self.factoryAbortControllers.get(params.runId);
1379
+ const controllersForRun = self.workflowAbortControllers.get(params.runId);
1197
1380
  const controller = controllersForRun?.get(params.executionToken);
1198
1381
  if (controller !== void 0) {
1199
- controller.abort(new DOMException("Factory run was aborted", "AbortError"));
1382
+ controller.abort(new DOMException("Workflow run was aborted", "AbortError"));
1200
1383
  }
1201
1384
  return {};
1202
1385
  }
@@ -1215,6 +1398,7 @@ class CopilotSession {
1215
1398
  * @internal This method is called internally when creating/resuming a session.
1216
1399
  */
1217
1400
  registerBearerTokenProviders(providers) {
1401
+ this.assertConnected();
1218
1402
  this.bearerTokenProviders.clear();
1219
1403
  if (!providers || providers.size === 0) {
1220
1404
  delete this.clientSessionApis.providerToken;
@@ -1226,6 +1410,7 @@ class CopilotSession {
1226
1410
  const self = this;
1227
1411
  this.clientSessionApis.providerToken = {
1228
1412
  async getToken(params) {
1413
+ self.assertConnected();
1229
1414
  const callback = self.bearerTokenProviders.get(params.providerName);
1230
1415
  if (!callback) {
1231
1416
  throw new Error(
@@ -1247,6 +1432,7 @@ class CopilotSession {
1247
1432
  * @internal This method is typically called internally when creating/resuming a session.
1248
1433
  */
1249
1434
  registerCommands(commands) {
1435
+ this.assertConnected();
1250
1436
  this.commandHandlers.clear();
1251
1437
  if (!commands) {
1252
1438
  return;
@@ -1262,6 +1448,7 @@ class CopilotSession {
1262
1448
  * @internal This method is typically called internally when creating/resuming a session.
1263
1449
  */
1264
1450
  registerElicitationHandler(handler) {
1451
+ this.assertConnected();
1265
1452
  this.elicitationHandler = handler;
1266
1453
  }
1267
1454
  /**
@@ -1271,6 +1458,7 @@ class CopilotSession {
1271
1458
  * @internal This method is typically called internally when creating/resuming a session.
1272
1459
  */
1273
1460
  registerExitPlanModeHandler(handler) {
1461
+ this.assertConnected();
1274
1462
  this.exitPlanModeHandler = handler;
1275
1463
  }
1276
1464
  /**
@@ -1280,6 +1468,7 @@ class CopilotSession {
1280
1468
  * @internal This method is typically called internally when creating/resuming a session.
1281
1469
  */
1282
1470
  registerAutoModeSwitchHandler(handler) {
1471
+ this.assertConnected();
1283
1472
  this.autoModeSwitchHandler = handler;
1284
1473
  }
1285
1474
  /**
@@ -1318,6 +1507,7 @@ class CopilotSession {
1318
1507
  * @internal
1319
1508
  */
1320
1509
  async _handleExitPlanModeRequest(request) {
1510
+ this.assertConnected();
1321
1511
  if (!this.exitPlanModeHandler) {
1322
1512
  return { approved: true };
1323
1513
  }
@@ -1328,6 +1518,7 @@ class CopilotSession {
1328
1518
  * @internal
1329
1519
  */
1330
1520
  async _handleAutoModeSwitchRequest(request) {
1521
+ this.assertConnected();
1331
1522
  if (!this.autoModeSwitchHandler) {
1332
1523
  return "no";
1333
1524
  }
@@ -1439,6 +1630,7 @@ class CopilotSession {
1439
1630
  * @internal This method is typically called internally when creating a session.
1440
1631
  */
1441
1632
  registerPermissionHandler(handler) {
1633
+ this.assertConnected();
1442
1634
  this.permissionHandler = handler;
1443
1635
  }
1444
1636
  /**
@@ -1451,8 +1643,73 @@ class CopilotSession {
1451
1643
  * @internal This method is typically called internally when creating a session.
1452
1644
  */
1453
1645
  registerUserInputHandler(handler) {
1646
+ this.assertConnected();
1454
1647
  this.userInputHandler = handler;
1455
1648
  }
1649
+ /**
1650
+ * Registers the session's skill provider.
1651
+ *
1652
+ * @param provider - The skill provider, or undefined to remove it
1653
+ * @internal This method is typically called internally when creating a session.
1654
+ */
1655
+ registerSkillProvider(provider) {
1656
+ this.assertConnected();
1657
+ this.skillProvider = provider;
1658
+ }
1659
+ /**
1660
+ * Handles a `skillProvider.list` request from the runtime.
1661
+ *
1662
+ * @internal This method is for internal use by the SDK.
1663
+ */
1664
+ async _handleSkillProviderList(token) {
1665
+ const provider = this.requireSkillProvider();
1666
+ return await this.callSkillProvider("listSkills", token, async (options) => ({
1667
+ skills: await provider.listSkills(options) ?? []
1668
+ }));
1669
+ }
1670
+ /**
1671
+ * Handles a `skillProvider.read` request from the runtime.
1672
+ *
1673
+ * @internal This method is for internal use by the SDK.
1674
+ */
1675
+ async _handleSkillProviderRead(name, token) {
1676
+ const provider = this.requireSkillProvider();
1677
+ return await this.callSkillProvider("readSkill", token, async (options) => ({
1678
+ markdown: await provider.readSkill(name, options) ?? null
1679
+ }));
1680
+ }
1681
+ requireSkillProvider() {
1682
+ this.assertConnected();
1683
+ if (!this.skillProvider) {
1684
+ throw new Error(`No skill provider for session: ${this.sessionId}`);
1685
+ }
1686
+ return this.skillProvider;
1687
+ }
1688
+ /**
1689
+ * Runs a provider call with an abort signal tied to the runtime's request
1690
+ * cancellation. Failures are logged locally and reported generically.
1691
+ */
1692
+ async callSkillProvider(operation, token, call) {
1693
+ const controller = new AbortController();
1694
+ const subscription = token?.onCancellationRequested(() => controller.abort());
1695
+ if (token?.isCancellationRequested) {
1696
+ controller.abort();
1697
+ }
1698
+ try {
1699
+ return await call({ signal: controller.signal });
1700
+ } catch (error) {
1701
+ if (controller.signal.aborted) {
1702
+ throw new import_node.ResponseError(-32800, `Skill provider ${operation} cancelled`);
1703
+ }
1704
+ console.error(`Skill provider ${operation} failed`, {
1705
+ sessionId: this.sessionId,
1706
+ error
1707
+ });
1708
+ throw skillProviderFailure(operation);
1709
+ } finally {
1710
+ subscription?.dispose();
1711
+ }
1712
+ }
1456
1713
  /**
1457
1714
  * Registers hook handlers for session lifecycle events.
1458
1715
  *
@@ -1463,6 +1720,7 @@ class CopilotSession {
1463
1720
  * @internal This method is typically called internally when creating a session.
1464
1721
  */
1465
1722
  registerHooks(hooks) {
1723
+ this.assertConnected();
1466
1724
  this.hooks = hooks;
1467
1725
  }
1468
1726
  /**
@@ -1472,6 +1730,7 @@ class CopilotSession {
1472
1730
  * @internal This method is typically called internally when creating a session.
1473
1731
  */
1474
1732
  registerTransformCallbacks(callbacks) {
1733
+ this.assertConnected();
1475
1734
  this.transformCallbacks = callbacks;
1476
1735
  }
1477
1736
  /**
@@ -1483,6 +1742,7 @@ class CopilotSession {
1483
1742
  * @internal This method is for internal use by the SDK.
1484
1743
  */
1485
1744
  async _handleSystemMessageTransform(sections) {
1745
+ this.assertConnected();
1486
1746
  const result = {};
1487
1747
  for (const [sectionId, { content }] of Object.entries(sections)) {
1488
1748
  const callback = this.transformCallbacks?.get(sectionId);
@@ -1507,6 +1767,7 @@ class CopilotSession {
1507
1767
  * @internal This method is for internal use by the SDK.
1508
1768
  */
1509
1769
  async _handleUserInputRequest(request) {
1770
+ this.assertConnected();
1510
1771
  if (!this.userInputHandler) {
1511
1772
  throw new Error("User input requested but no handler registered");
1512
1773
  }
@@ -1528,6 +1789,7 @@ class CopilotSession {
1528
1789
  * @internal This method is for internal use by the SDK.
1529
1790
  */
1530
1791
  async _handleHooksInvoke(hookType, input) {
1792
+ this.assertConnected();
1531
1793
  if (!this.hooks) {
1532
1794
  return void 0;
1533
1795
  }
@@ -1542,7 +1804,9 @@ class CopilotSession {
1542
1804
  sessionStart: this.hooks.onSessionStart,
1543
1805
  sessionEnd: this.hooks.onSessionEnd,
1544
1806
  errorOccurred: this.hooks.onErrorOccurred,
1545
- agentStop: this.hooks.onAgentStop
1807
+ agentStop: this.hooks.onAgentStop,
1808
+ subagentStart: this.hooks.onSubagentStart,
1809
+ subagentStop: this.hooks.onSubagentStop
1546
1810
  };
1547
1811
  const handler = handlerMap[hookType];
1548
1812
  if (!handler) {
@@ -1706,6 +1970,62 @@ class CopilotSession {
1706
1970
  async setAutoTier(autoTier) {
1707
1971
  return await this.rpc.model.switchAutoTier({ autoTier });
1708
1972
  }
1973
+ /**
1974
+ * Replace the tools this client supplies to the session.
1975
+ *
1976
+ * `tools` becomes the complete set of tools this client implements,
1977
+ * replacing the ones it supplied when the session was created or resumed,
1978
+ * or in an earlier call. Built-in, MCP, and plugin tools, and tools other
1979
+ * connected clients supply, are unaffected. Pass an empty array to remove
1980
+ * all of this client's tools.
1981
+ *
1982
+ * Tools are defined the same way as for `createSession`: calls to tools
1983
+ * with a `handler` are dispatched to it, and tools without one are
1984
+ * declaration-only. Once the runtime accepts the replacement, every tool
1985
+ * call this session dispatches uses the new handlers; calls already
1986
+ * running finish on their original handlers. If the runtime rejects the
1987
+ * replacement, this rejects and the previous tools and handlers stay in
1988
+ * place. Concurrent calls on the same session are applied one at a time,
1989
+ * in the order they are made.
1990
+ *
1991
+ * The agent sees the new tools from its next model request, which can fall
1992
+ * within a turn in progress. A model request already in flight was made
1993
+ * with the previous tools, so the agent can still call a tool you removed.
1994
+ * This session doesn't answer that call, and it can stay pending until the
1995
+ * turn is aborted. If a running turn might still call a tool you remove,
1996
+ * replace tools while the session is idle.
1997
+ *
1998
+ * @param tools - The complete set of tools this client supplies
1999
+ * @throws Error if the session disconnects before the replacement is applied
2000
+ *
2001
+ * @experimental Wraps the experimental `session.tools.set` RPC and may change
2002
+ * or be removed in a future release.
2003
+ *
2004
+ * @example
2005
+ * ```typescript
2006
+ * await session.setTools([
2007
+ * defineTool("search_issues", {
2008
+ * description: "Search the issues shown on the current page",
2009
+ * parameters: z.object({ query: z.string() }),
2010
+ * handler: async ({ query }) => searchIssues(query),
2011
+ * }),
2012
+ * ]);
2013
+ * ```
2014
+ */
2015
+ async setTools(tools) {
2016
+ this.assertConnected();
2017
+ const definitions = tools.map(toToolDefinition);
2018
+ const replacement = this.setToolsQueue.then(async () => {
2019
+ this.assertConnected();
2020
+ await this.rpc.tools.set({ tools: definitions });
2021
+ this.registerTools(tools);
2022
+ });
2023
+ this.setToolsQueue = replacement.then(
2024
+ () => void 0,
2025
+ () => void 0
2026
+ );
2027
+ await replacement;
2028
+ }
1709
2029
  /**
1710
2030
  * Log a message to the session timeline.
1711
2031
  * The message appears in the session event stream and is visible to SDK consumers
@@ -1751,6 +2071,9 @@ function toCanvasRpcError(error) {
1751
2071
  const message = error instanceof Error ? error.message : String(error);
1752
2072
  return new import_node.ResponseError(import_node.ErrorCodes.InternalError, message, { code, message });
1753
2073
  }
2074
+ function skillProviderFailure(operation) {
2075
+ return new import_node.ResponseError(import_node.ErrorCodes.InternalError, `Skill provider ${operation} failed`);
2076
+ }
1754
2077
  function strictJsonValidationError(context, category, message, path) {
1755
2078
  return new import_node.ResponseError(import_node.ErrorCodes.InternalError, message, {
1756
2079
  code: context.code,
@@ -1882,17 +2205,17 @@ function assertStrictJson(value, context) {
1882
2205
  };
1883
2206
  visit(value, "$", context.allowTopLevelUndefined);
1884
2207
  }
1885
- function assertFactoryResult(value) {
2208
+ function assertWorkflowResult(value) {
1886
2209
  assertStrictJson(value, {
1887
- code: "factory_result_not_json",
1888
- label: "Factory result",
2210
+ code: "workflow_result_not_json",
2211
+ label: "Workflow result",
1889
2212
  allowTopLevelUndefined: true
1890
2213
  });
1891
2214
  }
1892
- function assertFactoryStepResult(value, key) {
2215
+ function assertWorkflowStepResult(value, key) {
1893
2216
  assertStrictJson(value, {
1894
- code: "factory_step_not_json",
1895
- label: `Factory step "${key}" result`,
2217
+ code: "workflow_step_not_json",
2218
+ label: `Workflow step "${key}" result`,
1896
2219
  allowTopLevelUndefined: false
1897
2220
  });
1898
2221
  }