@github/copilot-sdk 1.0.14 → 1.0.15-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
@@ -14,10 +14,14 @@ verified `github/copilot-cli` release assets when the SDK is published, so
14
14
  starting the SDK performs no runtime download. Set `COPILOT_CLI_PATH` to use an
15
15
  existing installation instead.
16
16
 
17
- The checked-in release pin is `copilotCliVersion` in `package.json`. Run
18
- `npm run set:cli-version -- <version>` to update it and regenerate the compiled
19
- metadata in `src/cliVersion.ts`. Packaging verifies release assets against the
20
- release's `SHA256SUMS.txt`.
17
+ The checked-in `copilotCliVersion` in `package.json` and compiled metadata in
18
+ `src/cliVersion.ts` use a development placeholder. The public SDK snapshot
19
+ replaces both with the CLI version published for that snapshot.
20
+
21
+ Do not change these pins for runtime-repository development. If they contain
22
+ `0.0.0-dev`, use the same-checkout runtime; release snapshot export owns replacing
23
+ development placeholders with a published CLI version. See
24
+ [checkout preparation](../CONTRIBUTING.md#testing-an-unreleased-runtime-api).
21
25
 
22
26
  `npm run pack:release` builds the main package and all platform packages. Set
23
27
  `COPILOT_CLI_DOWNLOAD_BASE_URL` to use a release mirror while packaging.
@@ -26,6 +30,12 @@ containing validated runtime npm package roots named for all eight platforms.
26
30
  This keeps `COPILOT_CLI_USE_NPM_PACKAGE` false and embeds those runtime files in
27
31
  the self-contained SDK platform packages.
28
32
 
33
+ In the runtime repository, packaging uses the prepared same-checkout runtime.
34
+ Set `COPILOT_SDK_RUNTIME_PLATFORMS` to the available target (for example,
35
+ `linux-x64`) for both `pack:release` and `verify:release-packages`. SDK CI checks
36
+ that target only; public release workflows leave this unset to package and
37
+ verify all eight platforms.
38
+
29
39
  ## Installation
30
40
 
31
41
  ```bash
@@ -34,7 +44,11 @@ npm install @github/copilot-sdk
34
44
 
35
45
  ## Run the Sample
36
46
 
37
- Try the interactive chat sample (from the repo root):
47
+ Try the interactive chat sample from the SDK root (`src/sdk` when nested).
48
+ In the runtime repository, first run `pnpm run build:cli` from the runtime root
49
+ to prepare the same-checkout executable, then return to `src/sdk`.
50
+ Building the Node SDK alone does not build the runtime. For dependency
51
+ prerequisites, see [development setup](#development).
38
52
 
39
53
  ```bash
40
54
  cd nodejs
@@ -118,6 +132,7 @@ new CopilotClient(options?: CopilotClientOptions)
118
132
  - `mode?: "empty" | "copilot-cli"` - Defaulting strategy. Use `"empty"` for multi-user server mode; defaults to `"copilot-cli"`.
119
133
  - `workingDirectory?: string` - Working directory for the runtime process (default: current process cwd).
120
134
  - `baseDirectory?: string` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime. When not set, the runtime defaults to `~/.copilot`. Ignored when connecting via `RuntimeConnection.forUri`.
135
+ - `extensionLaunchProvider?: ExtensionLaunchProvider` - Experimental connection-level resolver for extension launch profiles. The client installs the reverse-RPC handler and registers the provider during startup before sessions can be created.
121
136
  - `logLevel?: "none" | "error" | "warning" | "info" | "debug" | "all"` - Log level. When omitted, the runtime uses its own default (currently `"info"`).
122
137
  - `env?: Record<string, string | undefined>` - Environment variables for the runtime process. When omitted, inherits `process.env`.
123
138
  - `gitHubToken?: string` - GitHub token for authentication. When provided, takes priority over other auth methods.
@@ -305,6 +320,87 @@ Send a message and wait until the session becomes idle.
305
320
 
306
321
  Returns the final assistant message event, or undefined if none was received.
307
322
 
323
+ ##### Structured output (preview)
324
+
325
+ Requires a runtime build with `responseFormat` and `originatingMessageId` support.
326
+ Pass a raw JSON Schema or a Zod schema as `responseSchema` to `send` or
327
+ `sendAndWait`. As with custom tool parameters, the SDK converts Zod schemas to
328
+ JSON Schema before sending them:
329
+
330
+ ```typescript
331
+ import { z } from "zod";
332
+
333
+ const answerSchema = z.object({ answer: z.number().int() });
334
+ const message = await session.sendAndWait({
335
+ prompt: "What is 19 + 23?",
336
+ responseSchema: answerSchema,
337
+ });
338
+ console.log(message?.data.content); // JSON text
339
+ ```
340
+
341
+ For a typed result, pass the Zod schema as the **second argument** instead:
342
+
343
+ ```typescript
344
+ const answer = await session.sendAndWait("What is 19 + 23?", answerSchema);
345
+ console.log(answer.answer); // number; TResult is inferred from answerSchema
346
+ ```
347
+
348
+ `sendAndWait<TResult>(options, schema, timeout?)` generates the JSON Schema from
349
+ the schema value, parses the final JSON, and validates it with the schema's
350
+ `parse` method. TypeScript cannot derive a runtime schema from an erased type
351
+ parameter alone. Invalid JSON, a schema mismatch, or a completed run without a
352
+ matching assistant message throws. Do not also set `options.responseSchema` when
353
+ using the typed overload.
354
+
355
+ The schema belongs to the submitted run, including its tool-call iterations.
356
+ Internally generated stop-hook corrections retain the schema and originating
357
+ message ID, so the wait returns the corrected answer. Independent subsequent
358
+ sends do not inherit it. Ordinary immediate steering inherits the active schema
359
+ and originating message ID, even when it arrives too late for the current model
360
+ request and is promoted into a follow-up run. Specifying a schema with
361
+ `mode: "immediate"` is rejected, even while idle.
362
+ The generated `session.rpc.send` and `session.rpc.sendMessages` wrappers expose
363
+ the full `responseFormat` contract when you need to set its name, description,
364
+ or strict option rather than using the convenience defaults (`name: "response"`,
365
+ `strict: true`).
366
+ Each batch starts one run: the final returned message ID is its origin, preceding
367
+ messages are context, and an empty batch has no origin. An immediate batch
368
+ steers the active run instead and retains its origin.
369
+ The schema is not a persisted session default: autonomous resume-pending work
370
+ after a restart does not restore it. A terminal tool that clears context ends
371
+ the old run; its fresh seed does not inherit the schema or origin. Such a run
372
+ can finish without a structured result, in which case the typed wait throws.
373
+ After a successful terminal tool, the runtime disables tools while the model
374
+ produces the structured result. Stop-hook corrections remain supported.
375
+ Remote sessions and known HydraFusion routes reject response formats before
376
+ admission. Schemas larger than 32 MiB when JSON-encoded are also rejected before
377
+ admission, using the runtime's existing request-size ceiling. This does not
378
+ guarantee the schema plus conversation and tools fits the provider's budget.
379
+
380
+ Structured waits select the last root-agent message whose `originatingMessageId`
381
+ matches the ID returned by their send, then return at a non-autopilot
382
+ `session.idle`. Other queued work can delay that idle, but cannot replace the
383
+ selected result. The existing unformatted overload retains its session-wide
384
+ behavior. `turnId` identifies an individual model/tool iteration, not the whole
385
+ run; telemetry interaction IDs are not unique run identifiers.
386
+
387
+ For event-driven consumption with `send`, subscribe before sending and collect
388
+ root `assistant.message` events whose `data.originatingMessageId` matches the ID
389
+ returned by `send`; events may arrive before that acknowledgement. Wait for
390
+ `session.idle`, then parse the last matching message without tool requests.
391
+ An earlier response may be superseded by a stop-hook correction. Handle
392
+ `session.error` and aborted idle events rather than returning a partial result.
393
+
394
+ Streaming still delivers ordinary text events, including intermediate messages
395
+ and tool calls. Only the final selected message is parsed by the typed overload;
396
+ not every event is necessarily a complete schema-conforming JSON document.
397
+ Provider errors, refusals, cancellation, truncation, session errors, and timeouts
398
+ can prevent a typed result. A timeout stops waiting, not the runtime's work.
399
+ Use a model and endpoint that support native structured output. An API-compatible
400
+ gateway may ignore format fields even when it accepts the request; for example,
401
+ the Claude Chat-completions compatibility route is not equivalent to Anthropic's
402
+ native `output_config.format` endpoint.
403
+
308
404
  ##### `on(eventType: string, handler: TypedSessionEventHandler): () => void`
309
405
 
310
406
  Subscribe to a specific event type. The handler receives properly typed events.
@@ -1192,19 +1288,28 @@ try {
1192
1288
 
1193
1289
  ## Development
1194
1290
 
1195
- From the repository root:
1291
+ Follow [SDK development setup](../CONTRIBUTING.md#developing-an-sdk) first,
1292
+ including the harness and corrections-script dependencies. From the SDK root
1293
+ (`src/sdk` in the runtime repository, or the standalone repository root):
1196
1294
 
1197
1295
  ```bash
1198
- cd test/harness
1199
- npm ci
1296
+ npm run build:nodejs
1297
+ npm run test:nodejs
1298
+ npm run check:nodejs
1200
1299
  ```
1201
1300
 
1301
+ In the runtime layout, these build/test commands refresh the projection and
1302
+ prepare the checked-out runtime for tests. For focused unit tests after
1303
+ installing Node dependencies:
1304
+
1202
1305
  ```bash
1203
- cd nodejs
1204
- npm ci
1205
- npm test
1306
+ npm --prefix nodejs run test:unit
1206
1307
  ```
1207
1308
 
1309
+ For native Vitest selectors on E2Es, use the
1310
+ [prepared-runtime instructions](../CONTRIBUTING.md#testing-an-unreleased-runtime-api);
1311
+ the SDK facade does not forward selectors.
1312
+
1208
1313
  ## License
1209
1314
 
1210
1315
  MIT
@@ -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.85";
25
+ const COPILOT_CLI_VERSION = "1.0.89-0";
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 = {
@@ -45,13 +45,11 @@ var import_cliVersion = require("./cliVersion.js");
45
45
  var import_sessionFsProvider = require("./sessionFsProvider.js");
46
46
  var import_copilotRequestHandler = require("./copilotRequestHandler.js");
47
47
  var import_telemetry = require("./telemetry.js");
48
+ var import_schema = require("./schema.js");
48
49
  var import_toolSet = require("./toolSet.js");
49
50
  var import_types = require("./types.js");
50
51
  const MIN_PROTOCOL_VERSION = 3;
51
52
  const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
52
- function isZodSchema(value) {
53
- return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
54
- }
55
53
  async function withTimeout(promise, timeoutMs, message) {
56
54
  let timeout;
57
55
  try {
@@ -96,13 +94,6 @@ async function waitForChildExit(child, timeoutMs) {
96
94
  }
97
95
  });
98
96
  }
99
- function toJsonSchema(parameters) {
100
- if (!parameters) return void 0;
101
- if (isZodSchema(parameters)) {
102
- return parameters.toJSONSchema();
103
- }
104
- return parameters;
105
- }
106
97
  const DEFAULT_PROVIDER_NAME = "default";
107
98
  function extractBearerTokenProviders(provider, providers) {
108
99
  const callbacks = /* @__PURE__ */ new Map();
@@ -235,6 +226,7 @@ class CopilotClient {
235
226
  cliProcess = null;
236
227
  ffiHost = null;
237
228
  connection = null;
229
+ requestAdapter = null;
238
230
  messageWriter = null;
239
231
  connectionClosed = false;
240
232
  socket = null;
@@ -272,6 +264,7 @@ class CopilotClient {
272
264
  /** Connection-level session filesystem config, set via constructor option. */
273
265
  sessionFsConfig = null;
274
266
  requestHandler = null;
267
+ extensionLaunchProvider;
275
268
  builtinPluginDirectories = [];
276
269
  onGitHubTelemetry;
277
270
  clientGlobalHandlers = {};
@@ -429,6 +422,7 @@ class CopilotClient {
429
422
  this.onGetTraceContext = options.onGetTraceContext;
430
423
  this.sessionFsConfig = options.sessionFs ?? null;
431
424
  this.requestHandler = options.requestHandler ?? null;
425
+ this.extensionLaunchProvider = options.extensionLaunchProvider;
432
426
  this.onGitHubTelemetry = options.onGitHubTelemetry;
433
427
  this.setupClientGlobalHandlers();
434
428
  const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
@@ -531,14 +525,16 @@ class CopilotClient {
531
525
  }
532
526
  setupClientGlobalHandlers() {
533
527
  const handlers = {};
528
+ handlers.extensionLaunchProvider = this.extensionLaunchProvider;
534
529
  if (this.requestHandler) {
535
- handlers.llmInference = (0, import_copilotRequestHandler.createCopilotRequestAdapter)(this.requestHandler, () => {
530
+ this.requestAdapter = (0, import_copilotRequestHandler.createCopilotRequestAdapter)(this.requestHandler, () => {
536
531
  if (!this.connection) {
537
532
  return void 0;
538
533
  }
539
534
  this._rpc ??= (0, import_rpc.createServerRpc)(this.connection);
540
535
  return this._rpc;
541
536
  });
537
+ handlers.llmInference = this.requestAdapter;
542
538
  }
543
539
  if (this.onGitHubTelemetry) {
544
540
  const onGitHubTelemetry = this.onGitHubTelemetry;
@@ -643,6 +639,9 @@ class CopilotClient {
643
639
  }
644
640
  await this.connectToServer();
645
641
  await this.verifyProtocolVersion();
642
+ if (this.extensionLaunchProvider) {
643
+ await this.rpc.registerExtensionLaunchProvider();
644
+ }
646
645
  if (this.builtinPluginDirectories.length > 0) {
647
646
  try {
648
647
  await this.connection.sendRequest("plugins.builtin.set", {
@@ -731,6 +730,7 @@ class CopilotClient {
731
730
  }
732
731
  this.sessions.clear();
733
732
  this.githubTokenProviders.clear();
733
+ this.requestAdapter?.cancelPending();
734
734
  if (this.connection && !this.connectionClosed && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
735
735
  const runtimeShutdownStart = Date.now();
736
736
  const shutdownPromise = this.rpc.runtime.shutdown();
@@ -886,6 +886,7 @@ class CopilotClient {
886
886
  }
887
887
  this.sessions.clear();
888
888
  this.githubTokenProviders.clear();
889
+ this.requestAdapter?.cancelPending();
889
890
  if (this.messageWriter) {
890
891
  this.messageWriter.suppressWriteErrors = true;
891
892
  }
@@ -1188,7 +1189,7 @@ class CopilotClient {
1188
1189
  tools: config.tools?.map((tool) => ({
1189
1190
  name: tool.name,
1190
1191
  description: tool.description,
1191
- parameters: toJsonSchema(tool.parameters),
1192
+ parameters: (0, import_schema.toJsonSchema)(tool.parameters),
1192
1193
  overridesBuiltInTool: tool.overridesBuiltInTool,
1193
1194
  skipPermission: tool.skipPermission,
1194
1195
  defer: tool.defer,
@@ -1336,11 +1337,14 @@ class CopilotClient {
1336
1337
  async resumeSession(sessionId, config) {
1337
1338
  return this.resumeSessionInternal(sessionId, config);
1338
1339
  }
1339
- /** @internal */
1340
- async resumeSessionForExtension(sessionId, config, factories, extensionOptions) {
1341
- return this.resumeSessionInternal(sessionId, config, factories, extensionOptions);
1340
+ async resumeSessionForExtension(sessionId, config, contributions = {}, extensionOptions) {
1341
+ return this.resumeSessionInternal(sessionId, config, contributions, extensionOptions);
1342
1342
  }
1343
- async resumeSessionInternal(sessionId, config, factories, extensionOptions) {
1343
+ async resumeSessionInternal(sessionId, config, contributions = {}, extensionOptions) {
1344
+ const { factories, workflows } = Array.isArray(contributions) ? { factories: contributions, workflows: void 0 } : contributions;
1345
+ if (factories !== void 0 && workflows !== void 0) {
1346
+ throw new Error("Session configuration cannot include both factories and workflows");
1347
+ }
1344
1348
  if (config.gitHubToken !== void 0 && config.gitHubTokenProvider !== void 0) {
1345
1349
  throw new Error("gitHubToken and gitHubTokenProvider are mutually exclusive");
1346
1350
  }
@@ -1361,6 +1365,7 @@ class CopilotClient {
1361
1365
  session.registerCanvases(config.canvases);
1362
1366
  session.registerCommands(config.commands);
1363
1367
  session.registerFactories(factories);
1368
+ session.registerWorkflows(workflows);
1364
1369
  const {
1365
1370
  wireProvider: bearerWireProvider,
1366
1371
  wireProviders: bearerWireProviders,
@@ -1432,7 +1437,7 @@ class CopilotClient {
1432
1437
  tools: config.tools?.map((tool) => ({
1433
1438
  name: tool.name,
1434
1439
  description: tool.description,
1435
- parameters: toJsonSchema(tool.parameters),
1440
+ parameters: (0, import_schema.toJsonSchema)(tool.parameters),
1436
1441
  overridesBuiltInTool: tool.overridesBuiltInTool,
1437
1442
  skipPermission: tool.skipPermission,
1438
1443
  defer: tool.defer,
@@ -1442,6 +1447,7 @@ class CopilotClient {
1442
1447
  toolSearch: config.toolSearch,
1443
1448
  canvases: config.canvases?.map((canvas) => canvas.declaration),
1444
1449
  factories: factories?.map((factory) => factory.meta),
1450
+ workflows: workflows?.map((workflow) => workflow.meta),
1445
1451
  requestCanvasRenderer: config.requestCanvasRenderer,
1446
1452
  requestExtensions: config.requestExtensions,
1447
1453
  extensionSdkPath: config.extensionSdkPath,
@@ -2294,6 +2300,7 @@ stderr: ${stderrOutput}` : ""}`
2294
2300
  }
2295
2301
  this.sessions.clear();
2296
2302
  this.githubTokenProviders.clear();
2303
+ this.requestAdapter?.cancelPending();
2297
2304
  };
2298
2305
  this.connection.onClose(markDisconnected);
2299
2306
  this.connection.onError(() => {
@@ -27,6 +27,7 @@ __export(copilotRequestHandler_exports, {
27
27
  module.exports = __toCommonJS(copilotRequestHandler_exports);
28
28
  const sharedTextDecoder = new TextDecoder("utf-8", { fatal: false });
29
29
  const sharedTextEncoder = new TextEncoder();
30
+ const HTTP_RESPONSE_READ_AHEAD_BYTES = 32 * 1024;
30
31
  const kBridge = /* @__PURE__ */ Symbol("copilotWebSocketResponseBridge");
31
32
  const kCompletion = /* @__PURE__ */ Symbol("copilotWebSocketCompletion");
32
33
  const kOpen = /* @__PURE__ */ Symbol("copilotWebSocketOpen");
@@ -265,7 +266,9 @@ function createCopilotRequestAdapter(handler, getServerRpc) {
265
266
  const message = err instanceof Error ? err.message : String(err);
266
267
  await finalize(exchange, 502, message);
267
268
  } finally {
268
- pending.delete(exchange.requestId);
269
+ if (pending.get(exchange.requestId) === exchange) {
270
+ pending.delete(exchange.requestId);
271
+ }
269
272
  }
270
273
  }
271
274
  return {
@@ -278,6 +281,13 @@ function createCopilotRequestAdapter(handler, getServerRpc) {
278
281
  async httpRequestChunk(params) {
279
282
  routeChunk(getOrCreate(params.requestId), params);
280
283
  return {};
284
+ },
285
+ cancelPending() {
286
+ const exchanges = [...pending.values()];
287
+ pending.clear();
288
+ for (const exchange of exchanges) {
289
+ exchange.pushCancel("RPC connection closed");
290
+ }
281
291
  }
282
292
  };
283
293
  }
@@ -526,30 +536,156 @@ async function drainAsync(stream) {
526
536
  return out;
527
537
  }
528
538
  async function streamResponse(response, exchange) {
529
- await exchange.startResponse({
530
- status: response.status,
531
- statusText: response.statusText || void 0,
532
- headers: headersToMultiMap(response.headers)
533
- });
534
- const body = response.body;
535
- if (!body) {
536
- await exchange.endResponse();
537
- return;
538
- }
539
- const reader = body.getReader();
539
+ const reader = response.body ? new HttpResponseReader(response.body, exchange.signal) : void 0;
540
540
  try {
541
- for (; ; ) {
542
- const { value, done } = await reader.read();
543
- if (done) {
544
- break;
545
- }
546
- if (value && value.byteLength > 0) {
547
- await exchange.writeResponse(value);
541
+ await exchange.startResponse({
542
+ status: response.status,
543
+ statusText: response.statusText || void 0,
544
+ headers: headersToMultiMap(response.headers)
545
+ });
546
+ if (reader) {
547
+ for (; ; ) {
548
+ const chunk = await reader.nextChunk();
549
+ if (!chunk) {
550
+ break;
551
+ }
552
+ await exchange.writeResponse(chunk);
548
553
  }
549
554
  }
550
555
  await exchange.endResponse();
551
556
  } finally {
552
- reader.releaseLock();
557
+ await reader?.dispose();
558
+ }
559
+ }
560
+ class HttpResponseReader {
561
+ #reader;
562
+ #signal;
563
+ #frames = [];
564
+ #pump;
565
+ #onAbort;
566
+ #queued = 0;
567
+ #done = false;
568
+ #cancelled = false;
569
+ #hasError = false;
570
+ #error;
571
+ #dataWaker;
572
+ #spaceWaker;
573
+ #cancelPromise;
574
+ constructor(body, signal) {
575
+ this.#reader = body.getReader();
576
+ this.#signal = signal;
577
+ this.#onAbort = () => {
578
+ void this.cancel(signal.reason);
579
+ };
580
+ signal.addEventListener("abort", this.#onAbort, { once: true });
581
+ this.#pump = this.#pumpSource();
582
+ if (signal.aborted) {
583
+ this.#onAbort();
584
+ }
585
+ }
586
+ async nextChunk() {
587
+ while (this.#frames.length === 0 && !this.#done && !this.#hasError) {
588
+ await new Promise((resolve) => {
589
+ this.#dataWaker = resolve;
590
+ });
591
+ }
592
+ if (this.#frames.length > 0) {
593
+ const chunk = this.#takeQueued();
594
+ this.#wakeSpace();
595
+ return chunk;
596
+ }
597
+ if (this.#hasError) {
598
+ this.#hasError = false;
599
+ throw this.#error;
600
+ }
601
+ return void 0;
602
+ }
603
+ #takeQueued() {
604
+ const frames = this.#frames;
605
+ const total = this.#queued;
606
+ this.#queued = 0;
607
+ if (frames.length === 1) {
608
+ return frames.pop();
609
+ }
610
+ const chunk = new Uint8Array(total);
611
+ let offset = 0;
612
+ for (const frame of frames) {
613
+ chunk.set(frame, offset);
614
+ offset += frame.byteLength;
615
+ }
616
+ frames.length = 0;
617
+ return chunk;
618
+ }
619
+ async dispose() {
620
+ this.#signal.removeEventListener("abort", this.#onAbort);
621
+ if (!this.#done) {
622
+ await this.cancel();
623
+ } else if (this.#cancelPromise) {
624
+ await this.#cancelPromise;
625
+ }
626
+ await this.#pump;
627
+ }
628
+ cancel(reason) {
629
+ if (this.#done) {
630
+ return Promise.resolve();
631
+ }
632
+ if (!this.#cancelPromise) {
633
+ this.#cancelled = true;
634
+ this.#done = true;
635
+ this.#hasError = true;
636
+ this.#error = reason instanceof Error ? reason : new Error("HTTP response body cancelled.");
637
+ this.#frames.length = 0;
638
+ this.#queued = 0;
639
+ this.#wakeData();
640
+ this.#wakeSpace();
641
+ this.#cancelPromise = this.#reader.cancel(reason).catch(() => void 0);
642
+ }
643
+ return this.#cancelPromise;
644
+ }
645
+ async #pumpSource() {
646
+ try {
647
+ while (!this.#cancelled) {
648
+ while (this.#queued >= HTTP_RESPONSE_READ_AHEAD_BYTES && !this.#cancelled) {
649
+ await new Promise((resolve) => {
650
+ this.#spaceWaker = resolve;
651
+ });
652
+ }
653
+ if (this.#cancelled) {
654
+ return;
655
+ }
656
+ const { value, done } = await this.#reader.read();
657
+ if (done) {
658
+ return;
659
+ }
660
+ if (value.byteLength > 0) {
661
+ this.#frames.push(value);
662
+ this.#queued += value.byteLength;
663
+ this.#wakeData();
664
+ continue;
665
+ }
666
+ await new Promise((resolve) => setImmediate(resolve));
667
+ }
668
+ } catch (error) {
669
+ if (!this.#cancelled) {
670
+ this.#hasError = true;
671
+ this.#error = error;
672
+ }
673
+ } finally {
674
+ this.#done = true;
675
+ this.#wakeData();
676
+ this.#wakeSpace();
677
+ this.#reader.releaseLock();
678
+ }
679
+ }
680
+ #wakeData() {
681
+ const waker = this.#dataWaker;
682
+ this.#dataWaker = void 0;
683
+ waker?.();
684
+ }
685
+ #wakeSpace() {
686
+ const waker = this.#spaceWaker;
687
+ this.#spaceWaker = void 0;
688
+ waker?.();
553
689
  }
554
690
  }
555
691
  function headersToMultiMap(headers) {
@@ -21,9 +21,12 @@ __export(extension_exports, {
21
21
  Canvas: () => import_canvas.Canvas,
22
22
  CanvasError: () => import_canvas.CanvasError,
23
23
  FactoryResumeError: () => import_factory.FactoryResumeError,
24
+ WorkflowResumeError: () => import_workflow.WorkflowResumeError,
24
25
  createCanvas: () => import_canvas.createCanvas,
25
26
  defineFactory: () => import_factory.defineFactory,
27
+ defineWorkflow: () => import_workflow.defineWorkflow,
26
28
  isFactoryRunTerminal: () => import_factory.isFactoryRunTerminal,
29
+ isWorkflowRunTerminal: () => import_workflow.isWorkflowRunTerminal,
27
30
  joinSession: () => joinSession
28
31
  });
29
32
  module.exports = __toCommonJS(extension_exports);
@@ -31,6 +34,7 @@ var import_client = require("./client.js");
31
34
  var import_types = require("./types.js");
32
35
  var import_canvas = require("./canvas.js");
33
36
  var import_factory = require("./factory.js");
37
+ var import_workflow = require("./workflow.js");
34
38
  async function joinSession(config = {}) {
35
39
  const sessionId = process.env.SESSION_ID;
36
40
  if (!sessionId) {
@@ -42,10 +46,14 @@ async function joinSession(config = {}) {
42
46
  const {
43
47
  extensionSdkPath: _stripped,
44
48
  factories,
49
+ workflows,
45
50
  requestedEnvironmentVariables,
46
51
  ...rest
47
52
  } = config;
48
53
  void _stripped;
54
+ if (factories !== void 0 && workflows !== void 0) {
55
+ throw new Error("joinSession cannot register both factories and workflows");
56
+ }
49
57
  return client.resumeSessionForExtension(
50
58
  sessionId,
51
59
  {
@@ -53,7 +61,7 @@ async function joinSession(config = {}) {
53
61
  onPermissionRequest: config.onPermissionRequest ?? import_types.defaultJoinSessionPermissionHandler,
54
62
  suppressResumeEvent: config.suppressResumeEvent ?? true
55
63
  },
56
- factories,
64
+ { factories, workflows },
57
65
  requestedEnvironmentVariables?.length ? { requestedEnvironmentVariables } : void 0
58
66
  );
59
67
  }
@@ -62,8 +70,11 @@ async function joinSession(config = {}) {
62
70
  Canvas,
63
71
  CanvasError,
64
72
  FactoryResumeError,
73
+ WorkflowResumeError,
65
74
  createCanvas,
66
75
  defineFactory,
76
+ defineWorkflow,
67
77
  isFactoryRunTerminal,
78
+ isWorkflowRunTerminal,
68
79
  joinSession
69
80
  });