@github/copilot-sdk 1.0.15-unstable.35663726336.gcb2a8cc → 1.0.15
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 +55 -4
- package/dist/cjs/cliVersion.js +1 -1
- package/dist/cjs/client.js +25 -5
- package/dist/cjs/extension.js +9 -9
- package/dist/cjs/generated/rpc.js +408 -182
- package/dist/cjs/index.js +7 -7
- package/dist/cjs/installationConfirmation.js +98 -0
- package/dist/cjs/session.js +122 -135
- package/dist/cjs/{factory.js → workflow.js} +33 -33
- package/dist/cliVersion.d.ts +1 -1
- package/dist/cliVersion.js +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.js +27 -5
- package/dist/extension.d.ts +6 -6
- package/dist/extension.js +9 -9
- package/dist/generated/rpc.d.ts +4189 -2011
- package/dist/generated/rpc.js +408 -182
- package/dist/generated/session-events.d.ts +289 -121
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -4
- package/dist/installationConfirmation.d.ts +19 -0
- package/dist/installationConfirmation.js +77 -0
- package/dist/session.d.ts +10 -16
- package/dist/session.js +126 -139
- package/dist/types.d.ts +31 -66
- package/dist/workflow.d.ts +367 -0
- package/dist/{factory.js → workflow.js} +25 -25
- package/docs/extensions.md +1 -1
- package/docs/workflows.md +255 -0
- package/package.json +11 -10
- package/dist/factory.d.ts +0 -327
- package/docs/factories.md +0 -296
- package/docs/factory-patterns.md +0 -194
package/dist/session.js
CHANGED
|
@@ -6,31 +6,31 @@ import { getTraceContext } from "./telemetry.js";
|
|
|
6
6
|
import { isResponseSchema, toJsonSchema } from "./schema.js";
|
|
7
7
|
import { isAttributedPermissionResult } from "./types.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "./
|
|
14
|
-
function
|
|
15
|
-
return value === "not_found" || value === "non_resumable" || value === "already_active" || value === "
|
|
9
|
+
WORKFLOW_AGENT_OPTION_KEYS,
|
|
10
|
+
getWorkflowDefinition,
|
|
11
|
+
WorkflowResumeError,
|
|
12
|
+
isWorkflowRunTerminal
|
|
13
|
+
} from "./workflow.js";
|
|
14
|
+
function isWorkflowResumeErrorCode(value) {
|
|
15
|
+
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";
|
|
16
16
|
}
|
|
17
|
-
function
|
|
17
|
+
function copyDefinedWorkflowAgentOption(source, target, key) {
|
|
18
18
|
const value = source[key];
|
|
19
19
|
if (value !== void 0) {
|
|
20
20
|
target[key] = value;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
const
|
|
24
|
-
function
|
|
25
|
-
if (
|
|
23
|
+
const workflowExecutionStore = new AsyncLocalStorage();
|
|
24
|
+
function throwIfWorkflowExecutionIsActive() {
|
|
25
|
+
if (workflowExecutionStore.getStore()?.active) {
|
|
26
26
|
throw new Error(
|
|
27
|
-
"
|
|
27
|
+
"workflow.run, workflow.resume, and workflow.pause are not allowed while a workflow body is running on this call path."
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
const current =
|
|
33
|
-
return
|
|
31
|
+
function runInWorkflowHelperScope(helperScope, callback) {
|
|
32
|
+
const current = workflowExecutionStore.getStore();
|
|
33
|
+
return workflowExecutionStore.run({ active: current?.active ?? false, helperScope }, callback);
|
|
34
34
|
}
|
|
35
35
|
function deserializeHookInput(raw) {
|
|
36
36
|
if (!raw || typeof raw !== "object" || typeof raw.timestamp !== "number") {
|
|
@@ -54,20 +54,20 @@ function isOpenCanvasInstance(value) {
|
|
|
54
54
|
}
|
|
55
55
|
const FACTORY_LOG_FLUSH_DELAY_MS = 10;
|
|
56
56
|
const MAX_FACTORY_FANOUT_ITEMS = 4096;
|
|
57
|
-
function
|
|
57
|
+
function assertWorkflowFanoutSize(kind, size) {
|
|
58
58
|
if (size > MAX_FACTORY_FANOUT_ITEMS) {
|
|
59
59
|
throw new Error(
|
|
60
60
|
`${kind}() accepts at most ${MAX_FACTORY_FANOUT_ITEMS} items; got ${size}.`
|
|
61
61
|
);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
async function
|
|
64
|
+
async function runWorkflowParallel(thunks) {
|
|
65
65
|
if (!Array.isArray(thunks)) {
|
|
66
66
|
throw new Error(
|
|
67
67
|
"parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
assertWorkflowFanoutSize("parallel", thunks.length);
|
|
71
71
|
if (thunks.some((thunk) => typeof thunk !== "function")) {
|
|
72
72
|
throw new Error(
|
|
73
73
|
"parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)"
|
|
@@ -75,8 +75,8 @@ async function runFactoryParallel(thunks) {
|
|
|
75
75
|
}
|
|
76
76
|
return Promise.all(
|
|
77
77
|
thunks.map(
|
|
78
|
-
(thunk) => Promise.resolve().then(() =>
|
|
79
|
-
if (
|
|
78
|
+
(thunk) => Promise.resolve().then(() => runInWorkflowHelperScope("parallel", thunk)).catch((error) => {
|
|
79
|
+
if (isWorkflowFatalError(error)) {
|
|
80
80
|
throw error;
|
|
81
81
|
}
|
|
82
82
|
return null;
|
|
@@ -84,22 +84,22 @@ async function runFactoryParallel(thunks) {
|
|
|
84
84
|
)
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
|
-
async function
|
|
87
|
+
async function runWorkflowPipeline(items, ...stages) {
|
|
88
88
|
if (!Array.isArray(items)) {
|
|
89
89
|
throw new Error("pipeline(items, ...stages): items must be an array");
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
assertWorkflowFanoutSize("pipeline", items.length);
|
|
92
92
|
return Promise.all(
|
|
93
93
|
items.map(async (item, index) => {
|
|
94
94
|
let previous = item;
|
|
95
95
|
for (const stage of stages) {
|
|
96
96
|
try {
|
|
97
|
-
previous = await
|
|
97
|
+
previous = await runInWorkflowHelperScope(
|
|
98
98
|
"pipeline",
|
|
99
99
|
() => stage(previous, item, index)
|
|
100
100
|
);
|
|
101
101
|
} catch (error) {
|
|
102
|
-
if (
|
|
102
|
+
if (isWorkflowFatalError(error)) {
|
|
103
103
|
throw error;
|
|
104
104
|
}
|
|
105
105
|
return null;
|
|
@@ -109,7 +109,7 @@ async function runFactoryPipeline(items, ...stages) {
|
|
|
109
109
|
})
|
|
110
110
|
);
|
|
111
111
|
}
|
|
112
|
-
class
|
|
112
|
+
class WorkflowProgressBuffer {
|
|
113
113
|
constructor(send) {
|
|
114
114
|
this.send = send;
|
|
115
115
|
}
|
|
@@ -123,7 +123,7 @@ class FactoryProgressBuffer {
|
|
|
123
123
|
closed = false;
|
|
124
124
|
enqueue(kind, text) {
|
|
125
125
|
if (this.closed) {
|
|
126
|
-
throw new Error("Cannot log after the
|
|
126
|
+
throw new Error("Cannot log after the workflow run has settled");
|
|
127
127
|
}
|
|
128
128
|
this.pending.push({ seq: this.nextSeq++, kind, text });
|
|
129
129
|
this.scheduleFlush();
|
|
@@ -155,7 +155,7 @@ class FactoryProgressBuffer {
|
|
|
155
155
|
await this.flushTail;
|
|
156
156
|
if (this.flushFailed) {
|
|
157
157
|
console.warn(
|
|
158
|
-
"Ignoring a background
|
|
158
|
+
"Ignoring a background workflow progress flush failure after the workflow body settled",
|
|
159
159
|
this.flushError
|
|
160
160
|
);
|
|
161
161
|
}
|
|
@@ -164,7 +164,7 @@ class FactoryProgressBuffer {
|
|
|
164
164
|
await this.send(lines);
|
|
165
165
|
} catch (error) {
|
|
166
166
|
console.warn(
|
|
167
|
-
"Failed to flush final
|
|
167
|
+
"Failed to flush final workflow progress after the workflow body settled",
|
|
168
168
|
error
|
|
169
169
|
);
|
|
170
170
|
}
|
|
@@ -188,30 +188,30 @@ class FactoryProgressBuffer {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
|
-
async function
|
|
191
|
+
async function awaitWorkflowOperation(operation, signal) {
|
|
192
192
|
let rejectAbort;
|
|
193
193
|
const abortPromise = new Promise((_resolve, reject) => {
|
|
194
194
|
rejectAbort = reject;
|
|
195
195
|
});
|
|
196
|
-
const onAbort = () => rejectAbort?.(signal.reason ?? new DOMException("
|
|
196
|
+
const onAbort = () => rejectAbort?.(signal.reason ?? new DOMException("Workflow run was aborted", "AbortError"));
|
|
197
197
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
198
198
|
try {
|
|
199
|
-
|
|
199
|
+
throwIfWorkflowAborted(signal);
|
|
200
200
|
return await Promise.race([operation(), abortPromise]);
|
|
201
201
|
} finally {
|
|
202
202
|
signal.removeEventListener("abort", onAbort);
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
function
|
|
205
|
+
function throwIfWorkflowAborted(signal) {
|
|
206
206
|
if (signal.aborted) {
|
|
207
|
-
throw signal.reason ?? new DOMException("
|
|
207
|
+
throw signal.reason ?? new DOMException("Workflow run was aborted", "AbortError");
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
-
function
|
|
210
|
+
function isWorkflowAbortError(error) {
|
|
211
211
|
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
212
212
|
}
|
|
213
|
-
function
|
|
214
|
-
return
|
|
213
|
+
function isWorkflowFatalError(error) {
|
|
214
|
+
return isWorkflowAbortError(error) || error instanceof ResponseError || error instanceof ConnectionError;
|
|
215
215
|
}
|
|
216
216
|
const TOOL_SEARCH_TOOL_NAME = "tool_search_tool";
|
|
217
217
|
class CopilotSession {
|
|
@@ -243,8 +243,8 @@ class CopilotSession {
|
|
|
243
243
|
canvases = /* @__PURE__ */ new Map();
|
|
244
244
|
bearerTokenProviders = /* @__PURE__ */ new Map();
|
|
245
245
|
commandHandlers = /* @__PURE__ */ new Map();
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
workflows = /* @__PURE__ */ new Map();
|
|
247
|
+
workflowAbortControllers = /* @__PURE__ */ new Map();
|
|
248
248
|
permissionHandler;
|
|
249
249
|
mcpAuthHandler;
|
|
250
250
|
userInputHandler;
|
|
@@ -266,23 +266,16 @@ class CopilotSession {
|
|
|
266
266
|
/** @internal Client session API handlers, populated by CopilotClient during create/resume. */
|
|
267
267
|
clientSessionApis = {};
|
|
268
268
|
/**
|
|
269
|
-
* Friendly
|
|
269
|
+
* Friendly workflow API for running registered workflows by name or handle.
|
|
270
270
|
*
|
|
271
|
-
* @experimental Part of the experimental
|
|
271
|
+
* @experimental Part of the experimental Dynamic Workflows surface and may
|
|
272
272
|
* change or be removed in future SDK or CLI releases.
|
|
273
273
|
*/
|
|
274
|
-
|
|
274
|
+
workflow = {
|
|
275
275
|
run: (async (nameOrHandle, options) => {
|
|
276
|
-
|
|
277
|
-
const name = typeof nameOrHandle === "string" ? nameOrHandle :
|
|
278
|
-
|
|
279
|
-
return this.factory.resume(options.resumeFromRunId, {
|
|
280
|
-
limits: options.limits,
|
|
281
|
-
notifyOnComplete: options.notifyOnComplete,
|
|
282
|
-
logPhaseNames: options.logPhaseNames
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
const envelope = await this.rpc.factory.run({
|
|
276
|
+
throwIfWorkflowExecutionIsActive();
|
|
277
|
+
const name = typeof nameOrHandle === "string" ? nameOrHandle : getWorkflowDefinition(nameOrHandle).meta.name;
|
|
278
|
+
const envelope = await this.rpc.workflow.run({
|
|
286
279
|
name,
|
|
287
280
|
args: options?.args === void 0 ? {} : options.args,
|
|
288
281
|
options: {
|
|
@@ -291,13 +284,13 @@ class CopilotSession {
|
|
|
291
284
|
logPhaseNames: options?.logPhaseNames
|
|
292
285
|
}
|
|
293
286
|
});
|
|
294
|
-
return this.
|
|
287
|
+
return this.settleWorkflowRun(envelope);
|
|
295
288
|
}),
|
|
296
289
|
resume: (async (runId, options) => {
|
|
297
|
-
|
|
290
|
+
throwIfWorkflowExecutionIsActive();
|
|
298
291
|
let response;
|
|
299
292
|
try {
|
|
300
|
-
response = await this.rpc.
|
|
293
|
+
response = await this.rpc.workflow.resume({
|
|
301
294
|
runId,
|
|
302
295
|
limits: options?.limits,
|
|
303
296
|
notifyOnComplete: options?.notifyOnComplete,
|
|
@@ -306,43 +299,36 @@ class CopilotSession {
|
|
|
306
299
|
} catch (error) {
|
|
307
300
|
if (error instanceof ResponseError && typeof error.data === "object" && error.data !== null) {
|
|
308
301
|
const code = error.data.code;
|
|
309
|
-
if (
|
|
310
|
-
throw new
|
|
302
|
+
if (isWorkflowResumeErrorCode(code)) {
|
|
303
|
+
throw new WorkflowResumeError(code, error.message);
|
|
311
304
|
}
|
|
312
305
|
}
|
|
313
306
|
throw error;
|
|
314
307
|
}
|
|
315
|
-
return this.
|
|
308
|
+
return this.settleWorkflowRun(response.run);
|
|
316
309
|
}),
|
|
317
|
-
getRun: async (runId) => this.rpc.
|
|
318
|
-
waitForRun: (runId, options) => this.
|
|
310
|
+
getRun: async (runId) => this.rpc.workflow.getRun({ runId }),
|
|
311
|
+
waitForRun: (runId, options) => this.waitForWorkflowRun(runId, options?.signal),
|
|
319
312
|
listRuns: (async (options) => {
|
|
320
|
-
const page = await this.rpc.
|
|
313
|
+
const page = await this.rpc.workflow.listRuns(options ?? {});
|
|
321
314
|
return options === void 0 ? page.runs : page;
|
|
322
315
|
}),
|
|
323
|
-
getRunDetail: (runId) => this.rpc.
|
|
324
|
-
getRunProgress: (runId, options = {}) => this.rpc.
|
|
316
|
+
getRunDetail: (runId) => this.rpc.workflow.getRunDetail({ runId }),
|
|
317
|
+
getRunProgress: (runId, options = {}) => this.rpc.workflow.getRunProgress({ runId, ...options }),
|
|
325
318
|
pause: async (runId) => {
|
|
326
|
-
|
|
327
|
-
return this.rpc.
|
|
319
|
+
throwIfWorkflowExecutionIsActive();
|
|
320
|
+
return this.rpc.workflow.pause({ runId });
|
|
328
321
|
},
|
|
329
|
-
cancel: async (runId) => this.rpc.
|
|
322
|
+
cancel: async (runId) => this.rpc.workflow.cancel({ runId })
|
|
330
323
|
};
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
*
|
|
334
|
-
* The CLI may answer `session.factory.run` and `session.factory.resume`
|
|
335
|
-
* before the run settles, so a non-terminal envelope is followed by a wait
|
|
336
|
-
* on the run's terminal state.
|
|
337
|
-
*/
|
|
338
|
-
settleFactoryRun(envelope) {
|
|
339
|
-
if (isFactoryRunTerminal(envelope.status)) {
|
|
324
|
+
settleWorkflowRun(envelope) {
|
|
325
|
+
if (isWorkflowRunTerminal(envelope.status)) {
|
|
340
326
|
return Promise.resolve(envelope);
|
|
341
327
|
}
|
|
342
|
-
return this.
|
|
328
|
+
return this.waitForWorkflowRun(envelope.runId);
|
|
343
329
|
}
|
|
344
330
|
/**
|
|
345
|
-
* Resolve when a
|
|
331
|
+
* Resolve when a workflow run reaches a terminal status.
|
|
346
332
|
*
|
|
347
333
|
* The subscription is installed *before* the first read so a transition
|
|
348
334
|
* landing between the two cannot be missed, and re-reads are serialized so
|
|
@@ -351,8 +337,8 @@ class CopilotSession {
|
|
|
351
337
|
* collapse into a single in-flight read. A bounded periodic re-read keeps a
|
|
352
338
|
* dropped invalidation from leaving the wait pending forever.
|
|
353
339
|
*/
|
|
354
|
-
|
|
355
|
-
const abortError = () => signal?.reason ?? new DOMException("
|
|
340
|
+
waitForWorkflowRun(runId, signal) {
|
|
341
|
+
const abortError = () => signal?.reason ?? new DOMException("Workflow run wait was aborted", "AbortError");
|
|
356
342
|
if (signal?.aborted === true) {
|
|
357
343
|
return Promise.reject(abortError());
|
|
358
344
|
}
|
|
@@ -389,8 +375,8 @@ class CopilotSession {
|
|
|
389
375
|
try {
|
|
390
376
|
do {
|
|
391
377
|
rereadRequested = false;
|
|
392
|
-
const envelope = await this.rpc.
|
|
393
|
-
if (
|
|
378
|
+
const envelope = await this.rpc.workflow.getRun({ runId });
|
|
379
|
+
if (isWorkflowRunTerminal(envelope.status)) {
|
|
394
380
|
finish(() => resolve(envelope));
|
|
395
381
|
return;
|
|
396
382
|
}
|
|
@@ -405,7 +391,7 @@ class CopilotSession {
|
|
|
405
391
|
onAbort = () => finish(() => reject(abortError()));
|
|
406
392
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
407
393
|
}
|
|
408
|
-
unsubscribe = this.on("
|
|
394
|
+
unsubscribe = this.on("workflow.run_updated", (event) => {
|
|
409
395
|
if (event.data.runId === runId) {
|
|
410
396
|
void read();
|
|
411
397
|
}
|
|
@@ -531,6 +517,7 @@ class CopilotSession {
|
|
|
531
517
|
});
|
|
532
518
|
let lastAssistantMessage;
|
|
533
519
|
const unsubscribe = this.on((event) => {
|
|
520
|
+
if (event.agentId) return;
|
|
534
521
|
if (event.type === "assistant.message") {
|
|
535
522
|
lastAssistantMessage = event;
|
|
536
523
|
} else if (event.type === "session.idle" && event.data.mode !== "autopilot") {
|
|
@@ -670,13 +657,13 @@ class CopilotSession {
|
|
|
670
657
|
this.autoModeSwitchHandler = void 0;
|
|
671
658
|
this.commandHandlers.clear();
|
|
672
659
|
this.canvases.clear();
|
|
673
|
-
this.
|
|
674
|
-
for (const controllersForRun of this.
|
|
660
|
+
this.workflows.clear();
|
|
661
|
+
for (const controllersForRun of this.workflowAbortControllers.values()) {
|
|
675
662
|
for (const controller of controllersForRun.values()) {
|
|
676
663
|
controller.abort();
|
|
677
664
|
}
|
|
678
665
|
}
|
|
679
|
-
this.
|
|
666
|
+
this.workflowAbortControllers.clear();
|
|
680
667
|
this.transformCallbacks?.clear();
|
|
681
668
|
}
|
|
682
669
|
/** @internal */
|
|
@@ -1125,46 +1112,46 @@ class CopilotSession {
|
|
|
1125
1112
|
};
|
|
1126
1113
|
}
|
|
1127
1114
|
/**
|
|
1128
|
-
* Registers
|
|
1115
|
+
* Registers workflow closures and reverse-RPC handlers for this session.
|
|
1129
1116
|
*
|
|
1130
|
-
* @param
|
|
1117
|
+
* @param workflows - Workflow handles declared by the joining extension.
|
|
1131
1118
|
* @internal Called by the SDK when an extension joins a session.
|
|
1132
1119
|
*/
|
|
1133
|
-
|
|
1134
|
-
this.
|
|
1135
|
-
if (!
|
|
1136
|
-
delete this.clientSessionApis.
|
|
1120
|
+
registerWorkflows(workflows) {
|
|
1121
|
+
this.workflows.clear();
|
|
1122
|
+
if (!workflows || workflows.length === 0) {
|
|
1123
|
+
delete this.clientSessionApis.workflow;
|
|
1137
1124
|
return;
|
|
1138
1125
|
}
|
|
1139
|
-
for (const handle of
|
|
1140
|
-
const definition =
|
|
1141
|
-
if (this.
|
|
1126
|
+
for (const handle of workflows) {
|
|
1127
|
+
const definition = getWorkflowDefinition(handle);
|
|
1128
|
+
if (this.workflows.has(definition.meta.name)) {
|
|
1142
1129
|
throw new Error(
|
|
1143
|
-
`Duplicate
|
|
1130
|
+
`Duplicate workflow name "${definition.meta.name}". Workflow names must be unique within a joinSession call.`
|
|
1144
1131
|
);
|
|
1145
1132
|
}
|
|
1146
|
-
this.
|
|
1133
|
+
this.workflows.set(definition.meta.name, definition);
|
|
1147
1134
|
}
|
|
1148
1135
|
const self = this;
|
|
1149
|
-
this.clientSessionApis.
|
|
1136
|
+
this.clientSessionApis.workflow = {
|
|
1150
1137
|
async execute(params) {
|
|
1151
|
-
const definition = self.
|
|
1138
|
+
const definition = self.workflows.get(params.name);
|
|
1152
1139
|
if (!definition) {
|
|
1153
|
-
const message = `No
|
|
1140
|
+
const message = `No workflow registered with name "${params.name}"`;
|
|
1154
1141
|
throw new ResponseError(ErrorCodes.InvalidParams, message, {
|
|
1155
|
-
code: "
|
|
1142
|
+
code: "workflow_not_found",
|
|
1156
1143
|
name: params.name
|
|
1157
1144
|
});
|
|
1158
1145
|
}
|
|
1159
1146
|
const controller = new AbortController();
|
|
1160
|
-
let controllersForRun = self.
|
|
1147
|
+
let controllersForRun = self.workflowAbortControllers.get(params.runId);
|
|
1161
1148
|
if (controllersForRun === void 0) {
|
|
1162
1149
|
controllersForRun = /* @__PURE__ */ new Map();
|
|
1163
|
-
self.
|
|
1150
|
+
self.workflowAbortControllers.set(params.runId, controllersForRun);
|
|
1164
1151
|
}
|
|
1165
1152
|
controllersForRun.set(params.executionToken, controller);
|
|
1166
|
-
const progress = new
|
|
1167
|
-
await self.rpc.
|
|
1153
|
+
const progress = new WorkflowProgressBuffer(async (lines) => {
|
|
1154
|
+
await self.rpc.workflow.log({
|
|
1168
1155
|
runId: params.runId,
|
|
1169
1156
|
executionToken: params.executionToken,
|
|
1170
1157
|
lines
|
|
@@ -1177,22 +1164,22 @@ class CopilotSession {
|
|
|
1177
1164
|
session: self,
|
|
1178
1165
|
signal: controller.signal,
|
|
1179
1166
|
phase: (title) => {
|
|
1180
|
-
|
|
1167
|
+
throwIfWorkflowAborted(controller.signal);
|
|
1181
1168
|
progress.enqueue("phase", title);
|
|
1182
1169
|
},
|
|
1183
1170
|
log: (message) => {
|
|
1184
|
-
|
|
1171
|
+
throwIfWorkflowAborted(controller.signal);
|
|
1185
1172
|
progress.enqueue("log", message);
|
|
1186
1173
|
},
|
|
1187
1174
|
agent: async (prompt, options = {}) => {
|
|
1188
1175
|
await progress.flush();
|
|
1189
1176
|
const opts = {};
|
|
1190
|
-
for (const key of
|
|
1191
|
-
|
|
1177
|
+
for (const key of WORKFLOW_AGENT_OPTION_KEYS) {
|
|
1178
|
+
copyDefinedWorkflowAgentOption(options, opts, key);
|
|
1192
1179
|
}
|
|
1193
|
-
const response = await
|
|
1194
|
-
() => self.rpc.
|
|
1195
|
-
|
|
1180
|
+
const response = await awaitWorkflowOperation(
|
|
1181
|
+
() => self.rpc.workflow.agent({
|
|
1182
|
+
workflowRunId: params.runId,
|
|
1196
1183
|
executionToken: params.executionToken,
|
|
1197
1184
|
prompt,
|
|
1198
1185
|
opts
|
|
@@ -1204,11 +1191,11 @@ class CopilotSession {
|
|
|
1204
1191
|
step: async (key, producer, options = {}) => {
|
|
1205
1192
|
await progress.flush();
|
|
1206
1193
|
if (options.volatile) {
|
|
1207
|
-
|
|
1194
|
+
throwIfWorkflowAborted(controller.signal);
|
|
1208
1195
|
return producer();
|
|
1209
1196
|
}
|
|
1210
|
-
const cached = await
|
|
1211
|
-
() => self.rpc.
|
|
1197
|
+
const cached = await awaitWorkflowOperation(
|
|
1198
|
+
() => self.rpc.workflow.journal.get({
|
|
1212
1199
|
runId: params.runId,
|
|
1213
1200
|
executionToken: params.executionToken,
|
|
1214
1201
|
key
|
|
@@ -1221,13 +1208,13 @@ class CopilotSession {
|
|
|
1221
1208
|
`step("${key}") journal returned a hit without a result`
|
|
1222
1209
|
);
|
|
1223
1210
|
}
|
|
1224
|
-
|
|
1211
|
+
assertWorkflowStepResult(cached.resultJson, key);
|
|
1225
1212
|
return cached.resultJson;
|
|
1226
1213
|
}
|
|
1227
1214
|
const result2 = await producer();
|
|
1228
|
-
|
|
1229
|
-
await
|
|
1230
|
-
() => self.rpc.
|
|
1215
|
+
assertWorkflowStepResult(result2, key);
|
|
1216
|
+
await awaitWorkflowOperation(
|
|
1217
|
+
() => self.rpc.workflow.journal.put({
|
|
1231
1218
|
runId: params.runId,
|
|
1232
1219
|
executionToken: params.executionToken,
|
|
1233
1220
|
key,
|
|
@@ -1239,17 +1226,17 @@ class CopilotSession {
|
|
|
1239
1226
|
},
|
|
1240
1227
|
pause: async (key) => {
|
|
1241
1228
|
if (typeof key !== "string" || key.length === 0) {
|
|
1242
|
-
throw new Error("
|
|
1229
|
+
throw new Error("Workflow pause checkpoint key must not be empty");
|
|
1243
1230
|
}
|
|
1244
|
-
const helperScope =
|
|
1231
|
+
const helperScope = workflowExecutionStore.getStore()?.helperScope;
|
|
1245
1232
|
if (helperScope !== void 0) {
|
|
1246
1233
|
throw new Error(
|
|
1247
|
-
`
|
|
1234
|
+
`Workflow pause checkpoints are not allowed inside ${helperScope}() branches`
|
|
1248
1235
|
);
|
|
1249
1236
|
}
|
|
1250
1237
|
await progress.flush();
|
|
1251
|
-
const response = await
|
|
1252
|
-
() => self.internalRpc.
|
|
1238
|
+
const response = await awaitWorkflowOperation(
|
|
1239
|
+
() => self.internalRpc.workflow.pauseAtCheckpoint({
|
|
1253
1240
|
runId: params.runId,
|
|
1254
1241
|
executionToken: params.executionToken,
|
|
1255
1242
|
key
|
|
@@ -1260,21 +1247,21 @@ class CopilotSession {
|
|
|
1260
1247
|
case "continue":
|
|
1261
1248
|
return;
|
|
1262
1249
|
case "pause":
|
|
1263
|
-
await
|
|
1250
|
+
await awaitWorkflowOperation(
|
|
1264
1251
|
() => new Promise(() => {
|
|
1265
1252
|
}),
|
|
1266
1253
|
controller.signal
|
|
1267
1254
|
);
|
|
1268
1255
|
}
|
|
1269
1256
|
},
|
|
1270
|
-
parallel:
|
|
1271
|
-
pipeline:
|
|
1272
|
-
|
|
1273
|
-
throw new Error("nested
|
|
1257
|
+
parallel: runWorkflowParallel,
|
|
1258
|
+
pipeline: runWorkflowPipeline,
|
|
1259
|
+
workflow: async () => {
|
|
1260
|
+
throw new Error("nested workflows are not supported");
|
|
1274
1261
|
}
|
|
1275
1262
|
};
|
|
1276
1263
|
const execution = { active: true };
|
|
1277
|
-
const result = await
|
|
1264
|
+
const result = await workflowExecutionStore.run(execution, async () => {
|
|
1278
1265
|
try {
|
|
1279
1266
|
return await definition.run(context);
|
|
1280
1267
|
} finally {
|
|
@@ -1284,27 +1271,27 @@ class CopilotSession {
|
|
|
1284
1271
|
if (result === void 0) {
|
|
1285
1272
|
return {};
|
|
1286
1273
|
}
|
|
1287
|
-
|
|
1274
|
+
assertWorkflowResult(result);
|
|
1288
1275
|
return { result };
|
|
1289
1276
|
} finally {
|
|
1290
1277
|
try {
|
|
1291
1278
|
await progress.close();
|
|
1292
1279
|
} finally {
|
|
1293
|
-
const controllersForRun2 = self.
|
|
1280
|
+
const controllersForRun2 = self.workflowAbortControllers.get(params.runId);
|
|
1294
1281
|
if (controllersForRun2?.get(params.executionToken) === controller) {
|
|
1295
1282
|
controllersForRun2.delete(params.executionToken);
|
|
1296
1283
|
if (controllersForRun2.size === 0) {
|
|
1297
|
-
self.
|
|
1284
|
+
self.workflowAbortControllers.delete(params.runId);
|
|
1298
1285
|
}
|
|
1299
1286
|
}
|
|
1300
1287
|
}
|
|
1301
1288
|
}
|
|
1302
1289
|
},
|
|
1303
1290
|
async abort(params) {
|
|
1304
|
-
const controllersForRun = self.
|
|
1291
|
+
const controllersForRun = self.workflowAbortControllers.get(params.runId);
|
|
1305
1292
|
const controller = controllersForRun?.get(params.executionToken);
|
|
1306
1293
|
if (controller !== void 0) {
|
|
1307
|
-
controller.abort(new DOMException("
|
|
1294
|
+
controller.abort(new DOMException("Workflow run was aborted", "AbortError"));
|
|
1308
1295
|
}
|
|
1309
1296
|
return {};
|
|
1310
1297
|
}
|
|
@@ -1990,17 +1977,17 @@ function assertStrictJson(value, context) {
|
|
|
1990
1977
|
};
|
|
1991
1978
|
visit(value, "$", context.allowTopLevelUndefined);
|
|
1992
1979
|
}
|
|
1993
|
-
function
|
|
1980
|
+
function assertWorkflowResult(value) {
|
|
1994
1981
|
assertStrictJson(value, {
|
|
1995
|
-
code: "
|
|
1996
|
-
label: "
|
|
1982
|
+
code: "workflow_result_not_json",
|
|
1983
|
+
label: "Workflow result",
|
|
1997
1984
|
allowTopLevelUndefined: true
|
|
1998
1985
|
});
|
|
1999
1986
|
}
|
|
2000
|
-
function
|
|
1987
|
+
function assertWorkflowStepResult(value, key) {
|
|
2001
1988
|
assertStrictJson(value, {
|
|
2002
|
-
code: "
|
|
2003
|
-
label: `
|
|
1989
|
+
code: "workflow_step_not_json",
|
|
1990
|
+
label: `Workflow step "${key}" result`,
|
|
2004
1991
|
allowTopLevelUndefined: false
|
|
2005
1992
|
});
|
|
2006
1993
|
}
|