@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/dist/session.js CHANGED
@@ -3,6 +3,7 @@ import { ConnectionError, ErrorCodes, ResponseError } from "vscode-jsonrpc/node.
3
3
  import { createInternalSessionRpc, createSessionRpc } from "./generated/rpc.js";
4
4
  import { CanvasError } from "./canvas.js";
5
5
  import { getTraceContext } from "./telemetry.js";
6
+ import { isResponseSchema, toJsonSchema } from "./schema.js";
6
7
  import { isAttributedPermissionResult } from "./types.js";
7
8
  import {
8
9
  FACTORY_AGENT_OPTION_KEYS,
@@ -10,6 +11,12 @@ import {
10
11
  FactoryResumeError,
11
12
  isFactoryRunTerminal
12
13
  } from "./factory.js";
14
+ import {
15
+ WORKFLOW_AGENT_OPTION_KEYS,
16
+ getWorkflowDefinition,
17
+ WorkflowResumeError,
18
+ isWorkflowRunTerminal
19
+ } from "./workflow.js";
13
20
  function isFactoryResumeErrorCode(value) {
14
21
  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";
15
22
  }
@@ -19,6 +26,15 @@ function copyDefinedFactoryAgentOption(source, target, key) {
19
26
  target[key] = value;
20
27
  }
21
28
  }
29
+ function isWorkflowResumeErrorCode(value) {
30
+ 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";
31
+ }
32
+ function copyDefinedWorkflowAgentOption(source, target, key) {
33
+ const value = source[key];
34
+ if (value !== void 0) {
35
+ target[key] = value;
36
+ }
37
+ }
22
38
  const factoryExecutionStore = new AsyncLocalStorage();
23
39
  function throwIfFactoryExecutionIsActive() {
24
40
  if (factoryExecutionStore.getStore()?.active) {
@@ -27,6 +43,18 @@ function throwIfFactoryExecutionIsActive() {
27
43
  );
28
44
  }
29
45
  }
46
+ const workflowExecutionStore = new AsyncLocalStorage();
47
+ function throwIfWorkflowExecutionIsActive() {
48
+ if (workflowExecutionStore.getStore()?.active) {
49
+ throw new Error(
50
+ "workflow.run, workflow.resume, and workflow.pause are not allowed while a workflow body is running on this call path."
51
+ );
52
+ }
53
+ }
54
+ function runInWorkflowHelperScope(helperScope, callback) {
55
+ const current = workflowExecutionStore.getStore();
56
+ return workflowExecutionStore.run({ active: current?.active ?? false, helperScope }, callback);
57
+ }
30
58
  function runInFactoryHelperScope(helperScope, callback) {
31
59
  const current = factoryExecutionStore.getStore();
32
60
  return factoryExecutionStore.run({ active: current?.active ?? false, helperScope }, callback);
@@ -108,6 +136,54 @@ async function runFactoryPipeline(items, ...stages) {
108
136
  })
109
137
  );
110
138
  }
139
+ async function runWorkflowParallel(thunks) {
140
+ if (!Array.isArray(thunks)) {
141
+ throw new Error(
142
+ "parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
143
+ );
144
+ }
145
+ assertFactoryFanoutSize("parallel", thunks.length);
146
+ if (thunks.some((thunk) => typeof thunk !== "function")) {
147
+ throw new Error(
148
+ "parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
149
+ );
150
+ }
151
+ return Promise.all(
152
+ thunks.map(
153
+ (thunk) => Promise.resolve().then(() => runInWorkflowHelperScope("parallel", thunk)).catch((error) => {
154
+ if (isFactoryFatalError(error)) {
155
+ throw error;
156
+ }
157
+ return null;
158
+ })
159
+ )
160
+ );
161
+ }
162
+ async function runWorkflowPipeline(items, ...stages) {
163
+ if (!Array.isArray(items)) {
164
+ throw new Error("pipeline(items, ...stages): items must be an array");
165
+ }
166
+ assertFactoryFanoutSize("pipeline", items.length);
167
+ return Promise.all(
168
+ items.map(async (item, index) => {
169
+ let previous = item;
170
+ for (const stage of stages) {
171
+ try {
172
+ previous = await runInWorkflowHelperScope(
173
+ "pipeline",
174
+ () => stage(previous, item, index)
175
+ );
176
+ } catch (error) {
177
+ if (isFactoryFatalError(error)) {
178
+ throw error;
179
+ }
180
+ return null;
181
+ }
182
+ }
183
+ return previous;
184
+ })
185
+ );
186
+ }
111
187
  class FactoryProgressBuffer {
112
188
  constructor(send) {
113
189
  this.send = send;
@@ -187,6 +263,85 @@ class FactoryProgressBuffer {
187
263
  }
188
264
  }
189
265
  }
266
+ class WorkflowProgressBuffer {
267
+ constructor(send) {
268
+ this.send = send;
269
+ }
270
+ send;
271
+ nextSeq = 0;
272
+ pending = [];
273
+ flushTimer;
274
+ flushTail = Promise.resolve();
275
+ flushError;
276
+ flushFailed = false;
277
+ closed = false;
278
+ enqueue(kind, text) {
279
+ if (this.closed) {
280
+ throw new Error("Cannot log after the workflow run has settled");
281
+ }
282
+ this.pending.push({ seq: this.nextSeq++, kind, text });
283
+ this.scheduleFlush();
284
+ }
285
+ async flush() {
286
+ this.clearFlushTimer();
287
+ const lines = this.pending.splice(0);
288
+ if (lines.length > 0) {
289
+ this.flushTail = this.flushTail.then(async () => {
290
+ try {
291
+ await this.send(lines);
292
+ } catch (error) {
293
+ if (!this.flushFailed) {
294
+ this.flushFailed = true;
295
+ this.flushError = error;
296
+ }
297
+ }
298
+ });
299
+ }
300
+ await this.flushTail;
301
+ if (this.flushFailed) {
302
+ throw this.flushError;
303
+ }
304
+ }
305
+ async close() {
306
+ this.closed = true;
307
+ this.clearFlushTimer();
308
+ const lines = this.pending.splice(0);
309
+ await this.flushTail;
310
+ if (this.flushFailed) {
311
+ console.warn(
312
+ "Ignoring a background workflow progress flush failure after the workflow body settled",
313
+ this.flushError
314
+ );
315
+ }
316
+ if (lines.length > 0) {
317
+ try {
318
+ await this.send(lines);
319
+ } catch (error) {
320
+ console.warn(
321
+ "Failed to flush final workflow progress after the workflow body settled",
322
+ error
323
+ );
324
+ }
325
+ }
326
+ }
327
+ scheduleFlush() {
328
+ if (this.flushTimer !== void 0) {
329
+ return;
330
+ }
331
+ this.flushTimer = setTimeout(() => {
332
+ this.flushTimer = void 0;
333
+ void this.flush().catch(() => {
334
+ });
335
+ }, FACTORY_LOG_FLUSH_DELAY_MS);
336
+ this.flushTimer.unref?.();
337
+ }
338
+ clearFlushTimer() {
339
+ if (this.flushTimer !== void 0) {
340
+ clearTimeout(this.flushTimer);
341
+ this.flushTimer = void 0;
342
+ }
343
+ }
344
+ }
190
345
  async function awaitFactoryOperation(operation, signal) {
191
346
  let rejectAbort;
192
347
  const abortPromise = new Promise((_resolve, reject) => {
@@ -201,11 +356,19 @@ async function awaitFactoryOperation(operation, signal) {
201
356
  signal.removeEventListener("abort", onAbort);
202
357
  }
203
358
  }
359
+ async function awaitWorkflowOperation(operation, signal) {
360
+ return awaitFactoryOperation(operation, signal);
361
+ }
204
362
  function throwIfFactoryAborted(signal) {
205
363
  if (signal.aborted) {
206
364
  throw signal.reason ?? new DOMException("Factory run was aborted", "AbortError");
207
365
  }
208
366
  }
367
+ function throwIfWorkflowAborted(signal) {
368
+ if (signal.aborted) {
369
+ throw signal.reason ?? new DOMException("Workflow run was aborted", "AbortError");
370
+ }
371
+ }
209
372
  function isFactoryAbortError(error) {
210
373
  return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
211
374
  }
@@ -244,6 +407,8 @@ class CopilotSession {
244
407
  commandHandlers = /* @__PURE__ */ new Map();
245
408
  factories = /* @__PURE__ */ new Map();
246
409
  factoryAbortControllers = /* @__PURE__ */ new Map();
410
+ workflows = /* @__PURE__ */ new Map();
411
+ workflowAbortControllers = /* @__PURE__ */ new Map();
247
412
  permissionHandler;
248
413
  mcpAuthHandler;
249
414
  userInputHandler;
@@ -259,6 +424,7 @@ class CopilotSession {
259
424
  _capabilities = {};
260
425
  openCanvasInstances = [];
261
426
  disconnected = false;
427
+ pendingStructuredWaits = /* @__PURE__ */ new Set();
262
428
  disconnecting = false;
263
429
  onDisconnected;
264
430
  /** @internal Client session API handlers, populated by CopilotClient during create/resume. */
@@ -326,6 +492,56 @@ class CopilotSession {
326
492
  },
327
493
  cancel: async (runId) => this.rpc.factory.cancel({ runId })
328
494
  };
495
+ workflow = {
496
+ run: (async (nameOrHandle, options) => {
497
+ throwIfWorkflowExecutionIsActive();
498
+ const name = typeof nameOrHandle === "string" ? nameOrHandle : getWorkflowDefinition(nameOrHandle).meta.name;
499
+ const envelope = await this.rpc.workflow.run({
500
+ name,
501
+ args: options?.args === void 0 ? {} : options.args,
502
+ options: {
503
+ limits: options?.limits,
504
+ notifyOnComplete: options?.notifyOnComplete,
505
+ logPhaseNames: options?.logPhaseNames
506
+ }
507
+ });
508
+ return this.settleWorkflowRun(envelope);
509
+ }),
510
+ resume: (async (runId, options) => {
511
+ throwIfWorkflowExecutionIsActive();
512
+ let response;
513
+ try {
514
+ response = await this.rpc.workflow.resume({
515
+ runId,
516
+ limits: options?.limits,
517
+ notifyOnComplete: options?.notifyOnComplete,
518
+ logPhaseNames: options?.logPhaseNames
519
+ });
520
+ } catch (error) {
521
+ if (error instanceof ResponseError && typeof error.data === "object" && error.data !== null) {
522
+ const code = error.data.code;
523
+ if (isWorkflowResumeErrorCode(code)) {
524
+ throw new WorkflowResumeError(code, error.message);
525
+ }
526
+ }
527
+ throw error;
528
+ }
529
+ return this.settleWorkflowRun(response.run);
530
+ }),
531
+ getRun: async (runId) => this.rpc.workflow.getRun({ runId }),
532
+ waitForRun: (runId, options) => this.waitForWorkflowRun(runId, options?.signal),
533
+ listRuns: (async (options) => {
534
+ const page = await this.rpc.workflow.listRuns(options ?? {});
535
+ return options === void 0 ? page.runs : page;
536
+ }),
537
+ getRunDetail: (runId) => this.rpc.workflow.getRunDetail({ runId }),
538
+ getRunProgress: (runId, options = {}) => this.rpc.workflow.getRunProgress({ runId, ...options }),
539
+ pause: async (runId) => {
540
+ throwIfWorkflowExecutionIsActive();
541
+ return this.rpc.workflow.pause({ runId });
542
+ },
543
+ cancel: async (runId) => this.rpc.workflow.cancel({ runId })
544
+ };
329
545
  /**
330
546
  * Resolve a start/resume envelope into the terminal envelope callers expect.
331
547
  *
@@ -413,6 +629,86 @@ class CopilotSession {
413
629
  void read();
414
630
  });
415
631
  }
632
+ settleWorkflowRun(envelope) {
633
+ if (isWorkflowRunTerminal(envelope.status)) {
634
+ return Promise.resolve(envelope);
635
+ }
636
+ return this.waitForWorkflowRun(envelope.runId);
637
+ }
638
+ /**
639
+ * Resolve when a workflow run reaches a terminal status.
640
+ *
641
+ * The subscription is installed *before* the first read so a transition
642
+ * landing between the two cannot be missed, and re-reads are serialized so
643
+ * overlapping invalidation events cannot interleave — the run's revision
644
+ * advances once per operation, so a burst of events is common and must
645
+ * collapse into a single in-flight read. A bounded periodic re-read keeps a
646
+ * dropped invalidation from leaving the wait pending forever.
647
+ */
648
+ waitForWorkflowRun(runId, signal) {
649
+ const abortError = () => signal?.reason ?? new DOMException("Workflow run wait was aborted", "AbortError");
650
+ if (signal?.aborted === true) {
651
+ return Promise.reject(abortError());
652
+ }
653
+ return new Promise((resolve, reject) => {
654
+ let settled = false;
655
+ let reading = false;
656
+ let rereadRequested = false;
657
+ let pollHandle;
658
+ let unsubscribe;
659
+ let onAbort;
660
+ const finish = (complete) => {
661
+ if (settled) {
662
+ return;
663
+ }
664
+ settled = true;
665
+ if (pollHandle !== void 0) {
666
+ clearInterval(pollHandle);
667
+ }
668
+ unsubscribe?.();
669
+ if (onAbort !== void 0) {
670
+ signal?.removeEventListener("abort", onAbort);
671
+ }
672
+ complete();
673
+ };
674
+ const read = async () => {
675
+ if (settled) {
676
+ return;
677
+ }
678
+ if (reading) {
679
+ rereadRequested = true;
680
+ return;
681
+ }
682
+ reading = true;
683
+ try {
684
+ do {
685
+ rereadRequested = false;
686
+ const envelope = await this.rpc.workflow.getRun({ runId });
687
+ if (isWorkflowRunTerminal(envelope.status)) {
688
+ finish(() => resolve(envelope));
689
+ return;
690
+ }
691
+ } while (rereadRequested && !settled);
692
+ } catch (error) {
693
+ finish(() => reject(error));
694
+ } finally {
695
+ reading = false;
696
+ }
697
+ };
698
+ if (signal !== void 0) {
699
+ onAbort = () => finish(() => reject(abortError()));
700
+ signal.addEventListener("abort", onAbort, { once: true });
701
+ }
702
+ unsubscribe = this.on("factory.run_updated", (event) => {
703
+ if (event.data.runId === runId) {
704
+ void read();
705
+ }
706
+ });
707
+ pollHandle = setInterval(() => void read(), 5e3);
708
+ pollHandle.unref?.();
709
+ void read();
710
+ });
711
+ }
416
712
  /**
417
713
  * Typed session-scoped RPC methods.
418
714
  */
@@ -475,13 +771,54 @@ class CopilotSession {
475
771
  attachments: options.attachments,
476
772
  mode: options.mode,
477
773
  agentMode: options.agentMode,
478
- requestHeaders: options.requestHeaders
774
+ requestHeaders: options.requestHeaders,
775
+ ...options.responseSchema ? {
776
+ responseFormat: {
777
+ type: "json_schema",
778
+ jsonSchema: {
779
+ name: "response",
780
+ strict: true,
781
+ schema: toJsonSchema(options.responseSchema)
782
+ }
783
+ }
784
+ } : {}
479
785
  });
480
786
  return response.messageId;
481
787
  }
482
- async sendAndWait(optionsOrPrompt, timeout) {
788
+ async sendAndWait(optionsOrPrompt, schemaOrTimeout, timeout) {
483
789
  const options = typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt;
484
- const effectiveTimeout = timeout ?? 6e4;
790
+ const typedSchema = isResponseSchema(schemaOrTimeout) ? schemaOrTimeout : void 0;
791
+ if (schemaOrTimeout !== void 0 && typeof schemaOrTimeout !== "number" && !typedSchema) {
792
+ throw new TypeError(
793
+ "The second argument must be a timeout or a schema with toJSONSchema() and parse(). Pass raw JSON Schema in options.responseSchema instead."
794
+ );
795
+ }
796
+ const effectiveTimeout = (typeof schemaOrTimeout === "number" ? schemaOrTimeout : timeout) ?? 6e4;
797
+ if (typedSchema && options.responseSchema) {
798
+ throw new Error(
799
+ "Do not specify responseSchema in options when requesting a typed response."
800
+ );
801
+ }
802
+ if (typedSchema && options.mode === "immediate") {
803
+ throw new Error(
804
+ "Structured output cannot be requested on an immediate steering message."
805
+ );
806
+ }
807
+ if (typedSchema || options.responseSchema) {
808
+ const message = await this.sendAndWaitForStructuredMessage(
809
+ typedSchema ? { ...options, responseSchema: typedSchema } : options,
810
+ effectiveTimeout
811
+ );
812
+ if (typedSchema) {
813
+ if (!message) {
814
+ throw new Error(
815
+ "The requested run completed without a structured assistant response."
816
+ );
817
+ }
818
+ return typedSchema.parse(JSON.parse(message.data.content));
819
+ }
820
+ return message;
821
+ }
485
822
  let resolveOutcome;
486
823
  const outcomePromise = new Promise((resolve) => {
487
824
  resolveOutcome = resolve;
@@ -523,12 +860,95 @@ class CopilotSession {
523
860
  unsubscribe();
524
861
  }
525
862
  }
863
+ async sendAndWaitForStructuredMessage(options, timeout) {
864
+ if (this.disconnected) {
865
+ throw new Error("Session is disconnected");
866
+ }
867
+ let resolveOutcome;
868
+ const outcomePromise = new Promise((resolve) => {
869
+ resolveOutcome = resolve;
870
+ });
871
+ const fail = (error) => resolveOutcome({ kind: "error", error });
872
+ let messageId;
873
+ let consumed = false;
874
+ let lastMessage;
875
+ const buffered = [];
876
+ const observe = (event) => {
877
+ if (event.agentId) return;
878
+ if (event.type === "user.message" && event.data.messageId === messageId) {
879
+ consumed = true;
880
+ } else if (event.type === "assistant.message" && event.data.originatingMessageId === messageId) {
881
+ consumed = true;
882
+ lastMessage = event.data.toolRequests?.length ? void 0 : event;
883
+ } else if (consumed && event.type === "session.idle" && event.data.mode !== "autopilot") {
884
+ if (event.data.aborted) {
885
+ fail(
886
+ new Error(
887
+ "The requested run was aborted before a structured result was completed."
888
+ )
889
+ );
890
+ } else {
891
+ resolveOutcome({ kind: "idle", message: lastMessage });
892
+ }
893
+ } else if (consumed && event.type === "session.error") {
894
+ const error = new Error(event.data.message);
895
+ error.stack = event.data.stack;
896
+ fail(error);
897
+ }
898
+ };
899
+ const unsubscribe = this.on((event) => {
900
+ if (event.type !== "user.message" && event.type !== "assistant.message" && event.type !== "session.idle" && event.type !== "session.error") {
901
+ return;
902
+ }
903
+ if (messageId === void 0) {
904
+ buffered.push(event);
905
+ } else {
906
+ observe(event);
907
+ }
908
+ });
909
+ this.pendingStructuredWaits.add(fail);
910
+ const timer = setTimeout(
911
+ () => fail(new Error(`Timeout after ${timeout}ms waiting for the structured response`)),
912
+ timeout
913
+ );
914
+ try {
915
+ const sendOutcome = this.send(options).then(
916
+ (id) => {
917
+ if (!id) {
918
+ throw new Error(
919
+ "The runtime did not return a message ID for the structured send."
920
+ );
921
+ }
922
+ messageId = id;
923
+ for (const event of buffered) observe(event);
924
+ buffered.length = 0;
925
+ return outcomePromise;
926
+ },
927
+ (error) => ({
928
+ kind: "error",
929
+ error: error instanceof Error ? error : new Error(String(error))
930
+ })
931
+ );
932
+ const outcome = await Promise.race([sendOutcome, outcomePromise]);
933
+ if (outcome.kind === "error") throw outcome.error;
934
+ return outcome.message;
935
+ } finally {
936
+ clearTimeout(timer);
937
+ buffered.length = 0;
938
+ unsubscribe();
939
+ this.pendingStructuredWaits.delete(fail);
940
+ }
941
+ }
526
942
  /** @internal */
527
943
  _markDisconnected() {
528
944
  if (this.disconnected) {
529
945
  return;
530
946
  }
531
947
  this.disconnected = true;
948
+ for (const fail of this.pendingStructuredWaits) {
949
+ fail(new Error("Session disconnected while waiting for a structured response"));
950
+ }
951
+ this.pendingStructuredWaits.clear();
532
952
  for (const controller of this.pendingExternalTools.values()) {
533
953
  controller.abort();
534
954
  }
@@ -551,6 +971,13 @@ class CopilotSession {
551
971
  }
552
972
  }
553
973
  this.factoryAbortControllers.clear();
974
+ this.workflows.clear();
975
+ for (const controllersForRun of this.workflowAbortControllers.values()) {
976
+ for (const controller of controllersForRun.values()) {
977
+ controller.abort();
978
+ }
979
+ }
980
+ this.workflowAbortControllers.clear();
554
981
  this.transformCallbacks?.clear();
555
982
  }
556
983
  /** @internal */
@@ -1184,6 +1611,192 @@ class CopilotSession {
1184
1611
  }
1185
1612
  };
1186
1613
  }
1614
+ /**
1615
+ * Registers workflow closures and reverse-RPC handlers for this session.
1616
+ *
1617
+ * @param workflows - Workflow handles declared by the joining extension.
1618
+ * @internal Called by the SDK when an extension joins a session.
1619
+ */
1620
+ registerWorkflows(workflows) {
1621
+ this.workflows.clear();
1622
+ if (!workflows || workflows.length === 0) {
1623
+ delete this.clientSessionApis.workflow;
1624
+ return;
1625
+ }
1626
+ for (const handle of workflows) {
1627
+ const definition = getWorkflowDefinition(handle);
1628
+ if (this.workflows.has(definition.meta.name)) {
1629
+ throw new Error(
1630
+ `Duplicate workflow name "${definition.meta.name}". Workflow names must be unique within a joinSession call.`
1631
+ );
1632
+ }
1633
+ this.workflows.set(definition.meta.name, definition);
1634
+ }
1635
+ const self = this;
1636
+ this.clientSessionApis.workflow = {
1637
+ async execute(params) {
1638
+ const definition = self.workflows.get(params.name);
1639
+ if (!definition) {
1640
+ const message = `No workflow registered with name "${params.name}"`;
1641
+ throw new ResponseError(ErrorCodes.InvalidParams, message, {
1642
+ code: "workflow_not_found",
1643
+ name: params.name
1644
+ });
1645
+ }
1646
+ const controller = new AbortController();
1647
+ let controllersForRun = self.workflowAbortControllers.get(params.runId);
1648
+ if (controllersForRun === void 0) {
1649
+ controllersForRun = /* @__PURE__ */ new Map();
1650
+ self.workflowAbortControllers.set(params.runId, controllersForRun);
1651
+ }
1652
+ controllersForRun.set(params.executionToken, controller);
1653
+ const progress = new WorkflowProgressBuffer(async (lines) => {
1654
+ await self.rpc.workflow.log({
1655
+ runId: params.runId,
1656
+ executionToken: params.executionToken,
1657
+ lines
1658
+ });
1659
+ });
1660
+ try {
1661
+ const context = {
1662
+ runId: params.runId,
1663
+ args: params.args,
1664
+ session: self,
1665
+ signal: controller.signal,
1666
+ phase: (title) => {
1667
+ throwIfWorkflowAborted(controller.signal);
1668
+ progress.enqueue("phase", title);
1669
+ },
1670
+ log: (message) => {
1671
+ throwIfWorkflowAborted(controller.signal);
1672
+ progress.enqueue("log", message);
1673
+ },
1674
+ agent: async (prompt, options = {}) => {
1675
+ await progress.flush();
1676
+ const opts = {};
1677
+ for (const key of WORKFLOW_AGENT_OPTION_KEYS) {
1678
+ copyDefinedWorkflowAgentOption(options, opts, key);
1679
+ }
1680
+ const response = await awaitWorkflowOperation(
1681
+ () => self.rpc.workflow.agent({
1682
+ workflowRunId: params.runId,
1683
+ executionToken: params.executionToken,
1684
+ prompt,
1685
+ opts
1686
+ }),
1687
+ controller.signal
1688
+ );
1689
+ return response.result ?? null;
1690
+ },
1691
+ step: async (key, producer, options = {}) => {
1692
+ await progress.flush();
1693
+ if (options.volatile) {
1694
+ throwIfWorkflowAborted(controller.signal);
1695
+ return producer();
1696
+ }
1697
+ const cached = await awaitWorkflowOperation(
1698
+ () => self.rpc.workflow.journal.get({
1699
+ runId: params.runId,
1700
+ executionToken: params.executionToken,
1701
+ key
1702
+ }),
1703
+ controller.signal
1704
+ );
1705
+ if (cached.hit) {
1706
+ if (cached.resultJson === void 0) {
1707
+ throw new Error(
1708
+ `step("${key}") journal returned a hit without a result`
1709
+ );
1710
+ }
1711
+ assertWorkflowStepResult(cached.resultJson, key);
1712
+ return cached.resultJson;
1713
+ }
1714
+ const result2 = await producer();
1715
+ assertWorkflowStepResult(result2, key);
1716
+ await awaitWorkflowOperation(
1717
+ () => self.rpc.workflow.journal.put({
1718
+ runId: params.runId,
1719
+ executionToken: params.executionToken,
1720
+ key,
1721
+ resultJson: result2
1722
+ }),
1723
+ controller.signal
1724
+ );
1725
+ return result2;
1726
+ },
1727
+ pause: async (key) => {
1728
+ if (typeof key !== "string" || key.length === 0) {
1729
+ throw new Error("Workflow pause checkpoint key must not be empty");
1730
+ }
1731
+ const helperScope = workflowExecutionStore.getStore()?.helperScope;
1732
+ if (helperScope !== void 0) {
1733
+ throw new Error(
1734
+ `Workflow pause checkpoints are not allowed inside ${helperScope}() branches`
1735
+ );
1736
+ }
1737
+ await progress.flush();
1738
+ const response = await awaitWorkflowOperation(
1739
+ () => self.internalRpc.workflow.pauseAtCheckpoint({
1740
+ runId: params.runId,
1741
+ executionToken: params.executionToken,
1742
+ key
1743
+ }),
1744
+ controller.signal
1745
+ );
1746
+ switch (response.action) {
1747
+ case "continue":
1748
+ return;
1749
+ case "pause":
1750
+ await awaitWorkflowOperation(
1751
+ () => new Promise(() => {
1752
+ }),
1753
+ controller.signal
1754
+ );
1755
+ }
1756
+ },
1757
+ parallel: runWorkflowParallel,
1758
+ pipeline: runWorkflowPipeline,
1759
+ workflow: async () => {
1760
+ throw new Error("nested workflows are not supported");
1761
+ }
1762
+ };
1763
+ const execution = { active: true };
1764
+ const result = await workflowExecutionStore.run(execution, async () => {
1765
+ try {
1766
+ return await definition.run(context);
1767
+ } finally {
1768
+ execution.active = false;
1769
+ }
1770
+ });
1771
+ if (result === void 0) {
1772
+ return {};
1773
+ }
1774
+ assertWorkflowResult(result);
1775
+ return { result };
1776
+ } finally {
1777
+ try {
1778
+ await progress.close();
1779
+ } finally {
1780
+ const controllersForRun2 = self.workflowAbortControllers.get(params.runId);
1781
+ if (controllersForRun2?.get(params.executionToken) === controller) {
1782
+ controllersForRun2.delete(params.executionToken);
1783
+ if (controllersForRun2.size === 0) {
1784
+ self.workflowAbortControllers.delete(params.runId);
1785
+ }
1786
+ }
1787
+ }
1788
+ }
1789
+ },
1790
+ async abort(params) {
1791
+ const controllersForRun = self.workflowAbortControllers.get(params.runId);
1792
+ const controller = controllersForRun?.get(params.executionToken);
1793
+ if (controller !== void 0) {
1794
+ controller.abort(new DOMException("Workflow run was aborted", "AbortError"));
1795
+ }
1796
+ return {};
1797
+ }
1798
+ };
1799
+ }
1187
1800
  /**
1188
1801
  * Registers per-provider {@link BearerTokenProvider} callbacks for BYOK providers
1189
1802
  * configured with managed-identity / on-demand bearer-token auth.
@@ -1878,6 +2491,20 @@ function assertFactoryStepResult(value, key) {
1878
2491
  allowTopLevelUndefined: false
1879
2492
  });
1880
2493
  }
2494
+ function assertWorkflowResult(value) {
2495
+ assertStrictJson(value, {
2496
+ code: "workflow_result_not_json",
2497
+ label: "Workflow result",
2498
+ allowTopLevelUndefined: true
2499
+ });
2500
+ }
2501
+ function assertWorkflowStepResult(value, key) {
2502
+ assertStrictJson(value, {
2503
+ code: "workflow_step_not_json",
2504
+ label: `Workflow step "${key}" result`,
2505
+ allowTopLevelUndefined: false
2506
+ });
2507
+ }
1881
2508
  export {
1882
2509
  CopilotSession
1883
2510
  };