@github/copilot-sdk 1.0.15-preview.0 → 1.0.15-preview.2
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 +34 -11
- package/dist/cjs/cliVersion.js +1 -1
- package/dist/cjs/client.js +9 -4
- package/dist/cjs/extension.js +12 -1
- package/dist/cjs/generated/rpc.js +101 -5
- package/dist/cjs/index.js +9 -2
- package/dist/cjs/session.js +496 -0
- package/dist/cjs/workflow.js +134 -0
- package/dist/cliVersion.d.ts +1 -1
- package/dist/cliVersion.js +1 -1
- package/dist/client.js +9 -4
- package/dist/extension.d.ts +22 -8
- package/dist/extension.js +13 -1
- package/dist/generated/rpc.d.ts +547 -36
- package/dist/generated/rpc.js +101 -5
- package/dist/generated/session-events.d.ts +79 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -1
- package/dist/session.d.ts +16 -0
- package/dist/session.js +501 -0
- package/dist/workflow.d.ts +364 -0
- package/dist/workflow.js +106 -0
- package/docs/extensions.md +1 -0
- package/docs/workflows.md +257 -0
- package/package.json +11 -10
package/dist/session.js
CHANGED
|
@@ -11,6 +11,12 @@ import {
|
|
|
11
11
|
FactoryResumeError,
|
|
12
12
|
isFactoryRunTerminal
|
|
13
13
|
} from "./factory.js";
|
|
14
|
+
import {
|
|
15
|
+
WORKFLOW_AGENT_OPTION_KEYS,
|
|
16
|
+
getWorkflowDefinition,
|
|
17
|
+
WorkflowResumeError,
|
|
18
|
+
isWorkflowRunTerminal
|
|
19
|
+
} from "./workflow.js";
|
|
14
20
|
function isFactoryResumeErrorCode(value) {
|
|
15
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";
|
|
16
22
|
}
|
|
@@ -20,6 +26,15 @@ function copyDefinedFactoryAgentOption(source, target, key) {
|
|
|
20
26
|
target[key] = value;
|
|
21
27
|
}
|
|
22
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
|
+
}
|
|
23
38
|
const factoryExecutionStore = new AsyncLocalStorage();
|
|
24
39
|
function throwIfFactoryExecutionIsActive() {
|
|
25
40
|
if (factoryExecutionStore.getStore()?.active) {
|
|
@@ -28,6 +43,18 @@ function throwIfFactoryExecutionIsActive() {
|
|
|
28
43
|
);
|
|
29
44
|
}
|
|
30
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
|
+
}
|
|
31
58
|
function runInFactoryHelperScope(helperScope, callback) {
|
|
32
59
|
const current = factoryExecutionStore.getStore();
|
|
33
60
|
return factoryExecutionStore.run({ active: current?.active ?? false, helperScope }, callback);
|
|
@@ -109,6 +136,54 @@ async function runFactoryPipeline(items, ...stages) {
|
|
|
109
136
|
})
|
|
110
137
|
);
|
|
111
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
|
+
}
|
|
112
187
|
class FactoryProgressBuffer {
|
|
113
188
|
constructor(send) {
|
|
114
189
|
this.send = send;
|
|
@@ -188,6 +263,85 @@ class FactoryProgressBuffer {
|
|
|
188
263
|
}
|
|
189
264
|
}
|
|
190
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
|
+
}
|
|
191
345
|
async function awaitFactoryOperation(operation, signal) {
|
|
192
346
|
let rejectAbort;
|
|
193
347
|
const abortPromise = new Promise((_resolve, reject) => {
|
|
@@ -202,11 +356,19 @@ async function awaitFactoryOperation(operation, signal) {
|
|
|
202
356
|
signal.removeEventListener("abort", onAbort);
|
|
203
357
|
}
|
|
204
358
|
}
|
|
359
|
+
async function awaitWorkflowOperation(operation, signal) {
|
|
360
|
+
return awaitFactoryOperation(operation, signal);
|
|
361
|
+
}
|
|
205
362
|
function throwIfFactoryAborted(signal) {
|
|
206
363
|
if (signal.aborted) {
|
|
207
364
|
throw signal.reason ?? new DOMException("Factory run was aborted", "AbortError");
|
|
208
365
|
}
|
|
209
366
|
}
|
|
367
|
+
function throwIfWorkflowAborted(signal) {
|
|
368
|
+
if (signal.aborted) {
|
|
369
|
+
throw signal.reason ?? new DOMException("Workflow run was aborted", "AbortError");
|
|
370
|
+
}
|
|
371
|
+
}
|
|
210
372
|
function isFactoryAbortError(error) {
|
|
211
373
|
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
212
374
|
}
|
|
@@ -245,6 +407,8 @@ class CopilotSession {
|
|
|
245
407
|
commandHandlers = /* @__PURE__ */ new Map();
|
|
246
408
|
factories = /* @__PURE__ */ new Map();
|
|
247
409
|
factoryAbortControllers = /* @__PURE__ */ new Map();
|
|
410
|
+
workflows = /* @__PURE__ */ new Map();
|
|
411
|
+
workflowAbortControllers = /* @__PURE__ */ new Map();
|
|
248
412
|
permissionHandler;
|
|
249
413
|
mcpAuthHandler;
|
|
250
414
|
userInputHandler;
|
|
@@ -328,6 +492,56 @@ class CopilotSession {
|
|
|
328
492
|
},
|
|
329
493
|
cancel: async (runId) => this.rpc.factory.cancel({ runId })
|
|
330
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
|
+
};
|
|
331
545
|
/**
|
|
332
546
|
* Resolve a start/resume envelope into the terminal envelope callers expect.
|
|
333
547
|
*
|
|
@@ -415,6 +629,86 @@ class CopilotSession {
|
|
|
415
629
|
void read();
|
|
416
630
|
});
|
|
417
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
|
+
}
|
|
418
712
|
/**
|
|
419
713
|
* Typed session-scoped RPC methods.
|
|
420
714
|
*/
|
|
@@ -677,6 +971,13 @@ class CopilotSession {
|
|
|
677
971
|
}
|
|
678
972
|
}
|
|
679
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();
|
|
680
981
|
this.transformCallbacks?.clear();
|
|
681
982
|
}
|
|
682
983
|
/** @internal */
|
|
@@ -1310,6 +1611,192 @@ class CopilotSession {
|
|
|
1310
1611
|
}
|
|
1311
1612
|
};
|
|
1312
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
|
+
}
|
|
1313
1800
|
/**
|
|
1314
1801
|
* Registers per-provider {@link BearerTokenProvider} callbacks for BYOK providers
|
|
1315
1802
|
* configured with managed-identity / on-demand bearer-token auth.
|
|
@@ -2004,6 +2491,20 @@ function assertFactoryStepResult(value, key) {
|
|
|
2004
2491
|
allowTopLevelUndefined: false
|
|
2005
2492
|
});
|
|
2006
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
|
+
}
|
|
2007
2508
|
export {
|
|
2008
2509
|
CopilotSession
|
|
2009
2510
|
};
|