@fevex/core 0.1.0-alpha.3 → 0.1.0-alpha.5
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 +183 -2
- package/dist/agents/index.d.mts +1 -1
- package/dist/artifacts/index.d.mts +2 -0
- package/dist/artifacts/index.mjs +2 -0
- package/dist/artifacts-gUzemET7.mjs +440 -0
- package/dist/channels/index.d.mts +2 -2
- package/dist/channels/index.mjs +70 -28
- package/dist/evals/index.d.mts +4 -4
- package/dist/{fevex-Ddyi6z6L.d.mts → fevex-B5NxFKcV.d.mts} +41 -11
- package/dist/http/index.d.mts +65 -5
- package/dist/http/index.mjs +236 -16
- package/dist/{index-Bgcxgy66.d.mts → index--_LdSEuF.d.mts} +31 -10
- package/dist/{index-CrOId2px.d.mts → index-BFnWUu7j.d.mts} +1 -1
- package/dist/{index-CTIwrVf9.d.mts → index-BPwkgdo3.d.mts} +1 -1
- package/dist/{index-DviHtsLo.d.mts → index-Br-zPxZH.d.mts} +2 -2
- package/dist/{index-Dx_LotWZ.d.mts → index-CGmHXV31.d.mts} +38 -3
- package/dist/{index-CqOjvtQ9.d.mts → index-CKQCNvJz.d.mts} +4 -2
- package/dist/{index-dkFyTNh4.d.mts → index-Cb3AB98n.d.mts} +2 -2
- package/dist/{index-B4PIkqOU.d.mts → index-D6lnI92z.d.mts} +3 -3
- package/dist/{index-Bg9rr8Qe.d.mts → index-Dsulk-n6.d.mts} +3 -3
- package/dist/{index-DeXUtyd9.d.mts → index-aayvtf0J.d.mts} +2 -2
- package/dist/index.d.mts +13 -13
- package/dist/index.mjs +464 -103
- package/dist/knowledge/index.d.mts +1 -1
- package/dist/model-stream-JQdHDDcj.mjs +131 -0
- package/dist/models/index.d.mts +1 -1
- package/dist/observability/index.d.mts +1 -1
- package/dist/policies/index.d.mts +1 -1
- package/dist/{model-stream-u2xMsEaf.mjs → run-support-CmZ6Xo6H.mjs} +41 -130
- package/dist/runtime/index.d.mts +2 -2
- package/dist/runtime/index.mjs +43 -1
- package/dist/sandbox/index.d.mts +1 -1
- package/dist/sandbox/index.mjs +42 -17
- package/dist/teams/index.d.mts +1 -1
- package/dist/testing/index.d.mts +13 -7
- package/dist/testing/index.mjs +98 -2
- package/dist/tools/index.d.mts +2 -2
- package/dist/types-BDVUoAU6.d.mts +559 -0
- package/dist/workflows/index.d.mts +1 -1
- package/package.json +6 -2
- package/dist/types-CuoRx12Q.d.mts +0 -333
package/dist/http/index.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { o as ArtifactError } from "../artifacts-gUzemET7.mjs";
|
|
1
2
|
import { t as FevexRunError } from "../run-error-Cr17wPGH.mjs";
|
|
3
|
+
import { t as MAX_DEADLINE_MS } from "../run-support-CmZ6Xo6H.mjs";
|
|
2
4
|
//#region src/http/protocol.ts
|
|
3
5
|
const FEVEX_HTTP_PROTOCOL_VERSION = "3";
|
|
4
6
|
const FEVEX_HTTP_PROTOCOL_VERSION_HEADER = "Fevex-Protocol-Version";
|
|
@@ -21,6 +23,7 @@ const FEVEX_RUN_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
21
23
|
"CHECKPOINT_UNSUPPORTED",
|
|
22
24
|
"RUN_DEFINITION_CHANGED",
|
|
23
25
|
"POLICY_DENIED",
|
|
26
|
+
"ADMISSION_DENIED",
|
|
24
27
|
"CREDENTIAL_NOT_FOUND",
|
|
25
28
|
"SANDBOX_REQUIRED",
|
|
26
29
|
"ELICITATION_INVALID",
|
|
@@ -32,69 +35,135 @@ const FEVEX_RUN_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
32
35
|
function createFevexHttpHandler(options) {
|
|
33
36
|
const pollIntervalMs = options.pollIntervalMs ?? 100;
|
|
34
37
|
const eventPageSize = options.eventPageSize ?? 100;
|
|
38
|
+
const maxConnections = options.maxConnections;
|
|
35
39
|
if (!Number.isSafeInteger(eventPageSize) || eventPageSize < 1 || eventPageSize > 1e3) throw new TypeError("eventPageSize must be an integer between 1 and 1000");
|
|
36
40
|
if (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 1) throw new TypeError("pollIntervalMs must be a positive number");
|
|
41
|
+
if (maxConnections !== void 0 && (!Number.isSafeInteger(maxConnections) || maxConnections < 1)) throw new TypeError("maxConnections must be a positive safe integer");
|
|
42
|
+
let activeConnections = 0;
|
|
43
|
+
const authorize = async (access) => {
|
|
44
|
+
if (options.authorize && !await options.authorize(access)) throw new HttpProtocolError(403, "FORBIDDEN", "Forbidden");
|
|
45
|
+
};
|
|
46
|
+
const authorizeArtifact = async (access) => {
|
|
47
|
+
if (options.authorizeArtifact && !await options.authorizeArtifact(access)) throw new HttpProtocolError(403, "FORBIDDEN", "Forbidden");
|
|
48
|
+
};
|
|
37
49
|
return async (request, handlerContext = {}) => {
|
|
38
50
|
const url = new URL(request.url);
|
|
39
51
|
const path = url.pathname;
|
|
52
|
+
const commitStart = async (run) => {
|
|
53
|
+
try {
|
|
54
|
+
await options.onRunStarted?.(run, handlerContext.context);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
await options.fevex.cancelRun(run.id).catch(() => {});
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
40
60
|
try {
|
|
41
61
|
const start = path.match(/^\/v1\/agents\/([^/]+)\/runs$/);
|
|
42
62
|
if (start && request.method === "POST") {
|
|
43
63
|
const body = await readJson(request);
|
|
44
64
|
if (!hasOwn(body, "input")) throw badRequest("input is required");
|
|
65
|
+
const name = decodeURIComponent(start[1]);
|
|
45
66
|
const sessionId = optionalId(body.sessionId, "sessionId");
|
|
46
67
|
const model = optionalId(body.model, "model");
|
|
47
68
|
const reasoning = optionalReasoning(body.reasoning);
|
|
48
|
-
|
|
69
|
+
const deadlineMs = optionalDeadline(body.deadlineMs, "deadlineMs");
|
|
70
|
+
await authorize({
|
|
71
|
+
action: "start",
|
|
72
|
+
kind: "agent",
|
|
73
|
+
name,
|
|
74
|
+
sessionId,
|
|
75
|
+
context: handlerContext.context
|
|
76
|
+
});
|
|
77
|
+
const run = await options.fevex.startAgent(name, {
|
|
49
78
|
input: body.input,
|
|
50
79
|
...model ? { model } : {},
|
|
51
80
|
...reasoning === void 0 ? {} : { reasoning },
|
|
81
|
+
...deadlineMs === void 0 ? {} : { deadlineMs },
|
|
52
82
|
...sessionId ? { sessionId } : {},
|
|
53
83
|
...handlerContext.context ? { context: handlerContext.context } : {}
|
|
54
|
-
})
|
|
84
|
+
});
|
|
85
|
+
await commitStart(run);
|
|
86
|
+
return json(run, 202);
|
|
55
87
|
}
|
|
56
88
|
const startWorkflow = path.match(/^\/v1\/workflows\/([^/]+)\/runs$/);
|
|
57
89
|
if (startWorkflow && request.method === "POST") {
|
|
58
90
|
const body = await readJson(request);
|
|
59
91
|
if (!hasOwn(body, "input")) throw badRequest("input is required");
|
|
92
|
+
const name = decodeURIComponent(startWorkflow[1]);
|
|
60
93
|
const sessionId = optionalId(body.sessionId, "sessionId");
|
|
61
94
|
const model = optionalId(body.model, "model");
|
|
62
95
|
const reasoning = optionalReasoning(body.reasoning);
|
|
63
|
-
|
|
96
|
+
const deadlineMs = optionalDeadline(body.deadlineMs, "deadlineMs");
|
|
97
|
+
await authorize({
|
|
98
|
+
action: "start",
|
|
99
|
+
kind: "workflow",
|
|
100
|
+
name,
|
|
101
|
+
sessionId,
|
|
102
|
+
context: handlerContext.context
|
|
103
|
+
});
|
|
104
|
+
const run = await options.fevex.startWorkflow(name, {
|
|
64
105
|
input: body.input,
|
|
65
106
|
...model ? { model } : {},
|
|
66
107
|
...reasoning === void 0 ? {} : { reasoning },
|
|
108
|
+
...deadlineMs === void 0 ? {} : { deadlineMs },
|
|
67
109
|
...sessionId ? { sessionId } : {},
|
|
68
110
|
...handlerContext.context ? { context: handlerContext.context } : {}
|
|
69
|
-
})
|
|
111
|
+
});
|
|
112
|
+
await commitStart(run);
|
|
113
|
+
return json(run, 202);
|
|
70
114
|
}
|
|
71
115
|
const startTeam = path.match(/^\/v1\/teams\/([^/]+)\/runs$/);
|
|
72
116
|
if (startTeam && request.method === "POST") {
|
|
73
117
|
const body = await readJson(request);
|
|
74
118
|
if (!hasOwn(body, "input")) throw badRequest("Run input is required");
|
|
119
|
+
const name = decodeURIComponent(startTeam[1]);
|
|
75
120
|
const sessionId = optionalId(body.sessionId, "sessionId");
|
|
76
121
|
const model = optionalId(body.model, "model");
|
|
77
122
|
const reasoning = optionalReasoning(body.reasoning);
|
|
78
|
-
|
|
123
|
+
const deadlineMs = optionalDeadline(body.deadlineMs, "deadlineMs");
|
|
124
|
+
await authorize({
|
|
125
|
+
action: "start",
|
|
126
|
+
kind: "team",
|
|
127
|
+
name,
|
|
128
|
+
sessionId,
|
|
129
|
+
context: handlerContext.context
|
|
130
|
+
});
|
|
131
|
+
const run = await options.fevex.startTeam(name, {
|
|
79
132
|
input: body.input,
|
|
80
133
|
...model ? { model } : {},
|
|
81
134
|
...reasoning === void 0 ? {} : { reasoning },
|
|
135
|
+
...deadlineMs === void 0 ? {} : { deadlineMs },
|
|
82
136
|
...sessionId ? { sessionId } : {},
|
|
83
137
|
...handlerContext.context ? { context: handlerContext.context } : {}
|
|
84
|
-
})
|
|
138
|
+
});
|
|
139
|
+
await commitStart(run);
|
|
140
|
+
return json(run, 202);
|
|
85
141
|
}
|
|
86
142
|
const events = path.match(/^\/v1\/runs\/([^/]+)\/events$/);
|
|
87
143
|
if (events && request.method === "GET") {
|
|
88
144
|
const runId = decodeURIComponent(events[1]);
|
|
145
|
+
await authorize({
|
|
146
|
+
action: "observe",
|
|
147
|
+
runId,
|
|
148
|
+
context: handlerContext.context
|
|
149
|
+
});
|
|
89
150
|
if (!await options.fevex.getRun(runId)) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
90
151
|
const after = request.headers.get("last-event-id")?.trim() || void 0;
|
|
91
152
|
const initial = await options.fevex.listEvents(runId, {
|
|
92
153
|
after,
|
|
93
154
|
limit: eventPageSize
|
|
94
155
|
});
|
|
156
|
+
if (maxConnections !== void 0 && activeConnections >= maxConnections) throw new HttpProtocolError(429, "CONNECTION_LIMIT", "Too many event streams");
|
|
157
|
+
activeConnections += 1;
|
|
158
|
+
let released = false;
|
|
159
|
+
const release = () => {
|
|
160
|
+
if (released) return;
|
|
161
|
+
released = true;
|
|
162
|
+
activeConnections -= 1;
|
|
163
|
+
};
|
|
95
164
|
const disconnected = new AbortController();
|
|
96
165
|
const iterator = observeEvents(options.fevex, runId, initial, after, pollIntervalMs, AbortSignal.any([request.signal, disconnected.signal]), eventPageSize);
|
|
97
|
-
return versioned(new Response(iteratorStream(iterator, () => disconnected.abort()), { headers: {
|
|
166
|
+
return versioned(new Response(iteratorStream(iterator, () => disconnected.abort(), release), { headers: {
|
|
98
167
|
"content-type": `${SSE_TYPE}; charset=utf-8`,
|
|
99
168
|
"cache-control": "no-cache, no-transform"
|
|
100
169
|
} }));
|
|
@@ -102,28 +171,99 @@ function createFevexHttpHandler(options) {
|
|
|
102
171
|
const resume = path.match(/^\/v1\/runs\/([^/]+)\/resume$/);
|
|
103
172
|
if (resume && request.method === "POST") {
|
|
104
173
|
const runId = decodeURIComponent(resume[1]);
|
|
105
|
-
if (!await options.fevex.getRun(runId)) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
106
174
|
const actor = handlerContext.context?.actor;
|
|
107
175
|
if (!actor?.id?.trim()) throw new HttpProtocolError(401, "ACTOR_REQUIRED", "Authenticated actor is required");
|
|
176
|
+
await authorize({
|
|
177
|
+
action: "resume",
|
|
178
|
+
runId,
|
|
179
|
+
context: handlerContext.context
|
|
180
|
+
});
|
|
181
|
+
if (!await options.fevex.getRun(runId)) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
108
182
|
const resolution = readResolution(await readJson(request), actor);
|
|
109
183
|
return json(await options.fevex.resumeRun(runId, resolution), 202);
|
|
110
184
|
}
|
|
111
185
|
const recover = path.match(/^\/v1\/runs\/([^/]+)\/recover$/);
|
|
112
186
|
if (recover && request.method === "POST") {
|
|
113
187
|
const runId = decodeURIComponent(recover[1]);
|
|
114
|
-
if (!await options.fevex.getRun(runId)) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
115
188
|
const actor = handlerContext.context?.actor;
|
|
116
189
|
if (!actor?.id?.trim()) throw new HttpProtocolError(401, "ACTOR_REQUIRED", "Authenticated actor is required");
|
|
190
|
+
await authorize({
|
|
191
|
+
action: "recover",
|
|
192
|
+
runId,
|
|
193
|
+
context: handlerContext.context
|
|
194
|
+
});
|
|
195
|
+
if (!await options.fevex.getRun(runId)) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
117
196
|
return json(await options.fevex.recoverRun(runId, { actor }), 202);
|
|
118
197
|
}
|
|
198
|
+
const runArtifacts = path.match(/^\/v1\/runs\/([^/]+)\/artifacts$/);
|
|
199
|
+
if (runArtifacts && request.method === "GET") {
|
|
200
|
+
const runId = decodeURIComponent(runArtifacts[1]);
|
|
201
|
+
await authorizeArtifact({
|
|
202
|
+
action: "read",
|
|
203
|
+
kind: "artifacts",
|
|
204
|
+
runId,
|
|
205
|
+
context: handlerContext.context
|
|
206
|
+
});
|
|
207
|
+
if (!options.artifactCatalog) throw new HttpProtocolError(501, "ARTIFACT_CATALOG_NOT_CONFIGURED", "Artifact catalog is not configured");
|
|
208
|
+
if (!await options.fevex.getRun(runId)) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
209
|
+
const limit = optionalLimit(url.searchParams.get("limit"));
|
|
210
|
+
return json((await options.artifactCatalog.list({
|
|
211
|
+
runId,
|
|
212
|
+
...limit === void 0 ? {} : { limit }
|
|
213
|
+
}, artifactContext(handlerContext, request))).map(({ descriptor }) => descriptor));
|
|
214
|
+
}
|
|
215
|
+
const artifactRoute = path.match(/^\/v1\/artifacts\/([^/]+)$/);
|
|
216
|
+
if (artifactRoute && (request.method === "GET" || request.method === "HEAD")) {
|
|
217
|
+
const artifactId = decodeURIComponent(artifactRoute[1]);
|
|
218
|
+
await authorizeArtifact({
|
|
219
|
+
action: "read",
|
|
220
|
+
kind: "artifact",
|
|
221
|
+
artifactId,
|
|
222
|
+
context: handlerContext.context
|
|
223
|
+
});
|
|
224
|
+
if (!options.artifacts) throw new HttpProtocolError(501, "ARTIFACTS_NOT_CONFIGURED", "Artifact store is not configured");
|
|
225
|
+
const context = artifactContext(handlerContext, request);
|
|
226
|
+
if (request.method === "HEAD" && options.artifacts.head) {
|
|
227
|
+
const descriptor = await options.artifacts.head(artifactId, context);
|
|
228
|
+
if (!descriptor) throw notFound("ARTIFACT_NOT_FOUND", "Artifact not found");
|
|
229
|
+
return artifactResponse(descriptor, null);
|
|
230
|
+
}
|
|
231
|
+
const content = await options.artifacts.get(artifactId, context);
|
|
232
|
+
if (!content) throw notFound("ARTIFACT_NOT_FOUND", "Artifact not found");
|
|
233
|
+
return artifactResponse(content.descriptor, request.method === "HEAD" ? null : content.bytes);
|
|
234
|
+
}
|
|
235
|
+
if (artifactRoute && request.method === "DELETE") {
|
|
236
|
+
const artifactId = decodeURIComponent(artifactRoute[1]);
|
|
237
|
+
await authorizeArtifact({
|
|
238
|
+
action: "delete",
|
|
239
|
+
kind: "artifact",
|
|
240
|
+
artifactId,
|
|
241
|
+
context: handlerContext.context
|
|
242
|
+
});
|
|
243
|
+
if (!options.artifacts) throw new HttpProtocolError(501, "ARTIFACTS_NOT_CONFIGURED", "Artifact store is not configured");
|
|
244
|
+
if (!options.artifacts.delete) throw new HttpProtocolError(501, "ARTIFACT_DELETE_UNSUPPORTED", "Artifact store does not support deletion");
|
|
245
|
+
if (!await options.artifacts.delete(artifactId, artifactContext(handlerContext, request))) throw notFound("ARTIFACT_NOT_FOUND", "Artifact not found");
|
|
246
|
+
return versioned(new Response(null, { status: 204 }));
|
|
247
|
+
}
|
|
119
248
|
const run = path.match(/^\/v1\/runs\/([^/]+)$/);
|
|
120
249
|
if (run && request.method === "GET") {
|
|
121
|
-
const
|
|
250
|
+
const runId = decodeURIComponent(run[1]);
|
|
251
|
+
await authorize({
|
|
252
|
+
action: "read",
|
|
253
|
+
runId,
|
|
254
|
+
context: handlerContext.context
|
|
255
|
+
});
|
|
256
|
+
const value = await options.fevex.getRun(runId);
|
|
122
257
|
if (!value) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
123
258
|
return json(value);
|
|
124
259
|
}
|
|
125
260
|
if (run && request.method === "DELETE") {
|
|
126
261
|
const runId = decodeURIComponent(run[1]);
|
|
262
|
+
await authorize({
|
|
263
|
+
action: "cancel",
|
|
264
|
+
runId,
|
|
265
|
+
context: handlerContext.context
|
|
266
|
+
});
|
|
127
267
|
const value = await options.fevex.getRun(runId);
|
|
128
268
|
if (!value) throw notFound("RUN_NOT_FOUND", "Run not found");
|
|
129
269
|
if (value.status === "running" || value.status === "paused") {
|
|
@@ -131,7 +271,7 @@ function createFevexHttpHandler(options) {
|
|
|
131
271
|
}
|
|
132
272
|
return versioned(new Response(null, { status: 204 }));
|
|
133
273
|
}
|
|
134
|
-
if (start || startWorkflow || startTeam || events || resume || recover || run) throw new HttpProtocolError(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
274
|
+
if (start || startWorkflow || startTeam || events || resume || recover || runArtifacts || artifactRoute || run) throw new HttpProtocolError(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
135
275
|
throw notFound("ROUTE_NOT_FOUND", "Route not found");
|
|
136
276
|
} catch (error) {
|
|
137
277
|
return problem(error, url.pathname, options.onError);
|
|
@@ -192,6 +332,17 @@ function createFevexHttpClient(options) {
|
|
|
192
332
|
},
|
|
193
333
|
async cancelRun(runId) {
|
|
194
334
|
await assertResponse(await fetcher(`${baseUrl}/v1/runs/${encodeURIComponent(runId)}`, { method: "DELETE" }));
|
|
335
|
+
},
|
|
336
|
+
async getArtifact(artifactId) {
|
|
337
|
+
const response = await fetcher(`${baseUrl}/v1/artifacts/${encodeURIComponent(artifactId)}`);
|
|
338
|
+
await assertResponse(response);
|
|
339
|
+
return response;
|
|
340
|
+
},
|
|
341
|
+
listArtifacts(runId) {
|
|
342
|
+
return request(`/v1/runs/${encodeURIComponent(runId)}/artifacts`);
|
|
343
|
+
},
|
|
344
|
+
async deleteArtifact(artifactId) {
|
|
345
|
+
await assertResponse(await fetcher(`${baseUrl}/v1/artifacts/${encodeURIComponent(artifactId)}`, { method: "DELETE" }));
|
|
195
346
|
}
|
|
196
347
|
};
|
|
197
348
|
}
|
|
@@ -220,18 +371,28 @@ async function* observeEvents(fevex, runId, initial, after, pollIntervalMs, sign
|
|
|
220
371
|
});
|
|
221
372
|
}
|
|
222
373
|
}
|
|
223
|
-
function iteratorStream(iterator, cancel) {
|
|
374
|
+
function iteratorStream(iterator, cancel, done) {
|
|
375
|
+
let finished = false;
|
|
376
|
+
const finish = () => {
|
|
377
|
+
if (finished) return;
|
|
378
|
+
finished = true;
|
|
379
|
+
done?.();
|
|
380
|
+
};
|
|
224
381
|
return new ReadableStream({
|
|
225
382
|
async pull(controller) {
|
|
226
383
|
try {
|
|
227
384
|
const next = await iterator.next();
|
|
228
|
-
if (next.done)
|
|
229
|
-
|
|
385
|
+
if (next.done) {
|
|
386
|
+
finish();
|
|
387
|
+
controller.close();
|
|
388
|
+
} else controller.enqueue(next.value);
|
|
230
389
|
} catch (error) {
|
|
390
|
+
finish();
|
|
231
391
|
controller.error(error);
|
|
232
392
|
}
|
|
233
393
|
},
|
|
234
394
|
async cancel() {
|
|
395
|
+
finish();
|
|
235
396
|
cancel();
|
|
236
397
|
await iterator.return(void 0);
|
|
237
398
|
}
|
|
@@ -280,6 +441,37 @@ function findBoundary(value) {
|
|
|
280
441
|
function formatEvent(event) {
|
|
281
442
|
return `id: ${event.id}\nevent: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`;
|
|
282
443
|
}
|
|
444
|
+
function artifactContext(handlerContext, request) {
|
|
445
|
+
const tenantId = handlerContext.context?.attributes?.tenantId;
|
|
446
|
+
if (tenantId !== void 0 && typeof tenantId !== "string") throw badRequest("tenantId attribute must be a string");
|
|
447
|
+
const actor = handlerContext.context?.actor;
|
|
448
|
+
return {
|
|
449
|
+
...typeof tenantId === "string" ? { tenantId } : {},
|
|
450
|
+
...actor === void 0 ? {} : { actor },
|
|
451
|
+
signal: request.signal
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function optionalLimit(value) {
|
|
455
|
+
if (value === null) return void 0;
|
|
456
|
+
const parsed = Number(value);
|
|
457
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) throw badRequest("limit must be a positive integer");
|
|
458
|
+
return parsed;
|
|
459
|
+
}
|
|
460
|
+
function artifactResponse(descriptor, bytes) {
|
|
461
|
+
return versioned(new Response(bytes === null ? null : bytes.slice().buffer, { headers: {
|
|
462
|
+
"content-type": descriptor.mimeType,
|
|
463
|
+
"content-length": String(descriptor.size),
|
|
464
|
+
"content-disposition": contentDisposition(descriptor.filename),
|
|
465
|
+
"cache-control": "private, max-age=0, must-revalidate",
|
|
466
|
+
etag: `"${descriptor.sha256}"`,
|
|
467
|
+
"x-content-type-options": "nosniff",
|
|
468
|
+
"fevex-artifact-id": descriptor.artifactId,
|
|
469
|
+
"fevex-artifact-sha256": descriptor.sha256
|
|
470
|
+
} }));
|
|
471
|
+
}
|
|
472
|
+
function contentDisposition(filename) {
|
|
473
|
+
return `attachment; filename="${filename.replace(/[^\x20-\x7e]/gu, "_").replace(/["\\]/gu, "_")}"; filename*=UTF-8''${encodeURIComponent(filename).replace(/'/gu, "%27")}`;
|
|
474
|
+
}
|
|
283
475
|
function readResolution(body, actor) {
|
|
284
476
|
if (body.type === "elicitation" && typeof body.requestId === "string" && body.requestId.trim() && hasOwn(body, "value")) return {
|
|
285
477
|
type: "elicitation",
|
|
@@ -334,6 +526,16 @@ function optionalId(value, name) {
|
|
|
334
526
|
if (typeof value !== "string" || !value.trim()) throw badRequest(`${name} must be a string`);
|
|
335
527
|
return value.trim();
|
|
336
528
|
}
|
|
529
|
+
function optionalPositiveInteger(value, name) {
|
|
530
|
+
if (value === void 0) return void 0;
|
|
531
|
+
if (!Number.isSafeInteger(value) || value < 1) throw badRequest(`${name} must be a positive integer`);
|
|
532
|
+
return value;
|
|
533
|
+
}
|
|
534
|
+
function optionalDeadline(value, name) {
|
|
535
|
+
const deadlineMs = optionalPositiveInteger(value, name);
|
|
536
|
+
if (deadlineMs !== void 0 && deadlineMs > 2147483647) throw badRequest(`${name} must be at most ${MAX_DEADLINE_MS}`);
|
|
537
|
+
return deadlineMs;
|
|
538
|
+
}
|
|
337
539
|
function optionalReasoning(value) {
|
|
338
540
|
if (value === void 0) return void 0;
|
|
339
541
|
if (typeof value !== "string" || ![
|
|
@@ -408,6 +610,11 @@ function problem(error, instance, onError) {
|
|
|
408
610
|
({status, code} = error);
|
|
409
611
|
title = error.message;
|
|
410
612
|
detail = error.message;
|
|
613
|
+
} else if (error instanceof ArtifactError) {
|
|
614
|
+
code = error.code;
|
|
615
|
+
status = statusForArtifactErrorCode(error.code);
|
|
616
|
+
title = titleForStatus(status);
|
|
617
|
+
detail = error.message;
|
|
411
618
|
} else {
|
|
412
619
|
const runError = readFevexRunError(error);
|
|
413
620
|
if (runError) {
|
|
@@ -446,13 +653,26 @@ function statusForRunErrorCode(code) {
|
|
|
446
653
|
if (code === "AGENT_NOT_FOUND" || code === "WORKFLOW_NOT_FOUND" || code === "TEAM_NOT_FOUND" || code === "SESSION_NOT_FOUND") return 404;
|
|
447
654
|
if (code === "APPROVAL_INVALID" || code === "INVALID_CURSOR") return 400;
|
|
448
655
|
if (code === "POLICY_DENIED") return 403;
|
|
656
|
+
if (code === "ADMISSION_DENIED") return 429;
|
|
449
657
|
return 409;
|
|
450
658
|
}
|
|
659
|
+
function statusForArtifactErrorCode(code) {
|
|
660
|
+
if (code === "ARTIFACT_INVALID_ID" || code === "ARTIFACT_INVALID_INPUT" || code === "ARTIFACT_INVALID_CONTEXT") return 400;
|
|
661
|
+
if (code === "ARTIFACT_TOO_LARGE") return 413;
|
|
662
|
+
if (code === "ARTIFACT_MIME_UNSUPPORTED") return 415;
|
|
663
|
+
if (code === "ARTIFACT_CONFLICT") return 409;
|
|
664
|
+
return 500;
|
|
665
|
+
}
|
|
451
666
|
function titleForStatus(status) {
|
|
452
|
-
if (status === 404) return "Not Found";
|
|
453
667
|
if (status === 400) return "Bad Request";
|
|
454
668
|
if (status === 403) return "Forbidden";
|
|
455
|
-
return "
|
|
669
|
+
if (status === 404) return "Not Found";
|
|
670
|
+
if (status === 409) return "Conflict";
|
|
671
|
+
if (status === 413) return "Payload Too Large";
|
|
672
|
+
if (status === 415) return "Unsupported Media Type";
|
|
673
|
+
if (status === 429) return "Too Many Requests";
|
|
674
|
+
if (status === 501) return "Not Implemented";
|
|
675
|
+
return "Internal Server Error";
|
|
456
676
|
}
|
|
457
677
|
function wait(ms, signal) {
|
|
458
678
|
return new Promise((resolve) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { o as ExecutionContext, s as JsonObject, t as AgentEvent } from "./types-
|
|
2
|
-
import {
|
|
3
|
-
import { t as Fevex } from "./fevex-
|
|
1
|
+
import { f as RunId, o as ExecutionContext, s as JsonObject, t as AgentEvent } from "./types-BDVUoAU6.mjs";
|
|
2
|
+
import { I as SessionId, a as AgentRun, h as ElicitationRequest } from "./index-CGmHXV31.mjs";
|
|
3
|
+
import { t as Fevex } from "./fevex-B5NxFKcV.mjs";
|
|
4
4
|
//#region src/channels/channel.d.ts
|
|
5
5
|
interface ChannelMessage {
|
|
6
6
|
id: string;
|
|
@@ -21,11 +21,19 @@ interface ChannelOutput {
|
|
|
21
21
|
interface ChannelContext {
|
|
22
22
|
signal?: AbortSignal;
|
|
23
23
|
}
|
|
24
|
+
interface ChannelElicitationContext extends ChannelContext {
|
|
25
|
+
/** Stable key for transport-level idempotency, preserved across delivery retries. */
|
|
26
|
+
deliveryId: string;
|
|
27
|
+
runId: RunId;
|
|
28
|
+
sessionId: SessionId;
|
|
29
|
+
conversationId: string;
|
|
30
|
+
threadId?: string;
|
|
31
|
+
}
|
|
24
32
|
interface ChannelAdapter<TInput = unknown, TOutput = unknown> {
|
|
25
33
|
name: string;
|
|
26
34
|
parse(input: TInput, context: ChannelContext): Promise<ChannelMessage | null>;
|
|
27
35
|
deliver(output: ChannelOutput, context: ChannelContext): Promise<TOutput>;
|
|
28
|
-
deliverElicitation?(request: ElicitationRequest, context:
|
|
36
|
+
deliverElicitation?(request: ElicitationRequest, context: ChannelElicitationContext): Promise<TOutput>;
|
|
29
37
|
}
|
|
30
38
|
type ChannelEventType = 'channel.received' | 'channel.delivered' | 'channel.failed';
|
|
31
39
|
type ChannelFailurePhase = 'parse' | 'run' | 'deliver';
|
|
@@ -37,6 +45,8 @@ interface ChannelEventBase {
|
|
|
37
45
|
deliveryId?: string;
|
|
38
46
|
conversationId?: string;
|
|
39
47
|
threadId?: string;
|
|
48
|
+
runId?: RunId;
|
|
49
|
+
requestId?: string;
|
|
40
50
|
}
|
|
41
51
|
type ChannelEvent = (ChannelEventBase & {
|
|
42
52
|
type: 'channel.received';
|
|
@@ -53,16 +63,21 @@ declare class ChannelError extends Error {
|
|
|
53
63
|
readonly code: string;
|
|
54
64
|
readonly safeMessage: string;
|
|
55
65
|
readonly name = "ChannelError";
|
|
56
|
-
|
|
66
|
+
readonly runId?: RunId;
|
|
67
|
+
constructor(code: string, safeMessage: string, options?: ErrorOptions & {
|
|
68
|
+
runId?: RunId;
|
|
69
|
+
});
|
|
57
70
|
}
|
|
58
|
-
interface
|
|
71
|
+
interface ChannelDeliveryOptions<TInput = unknown, TDelivery = unknown> {
|
|
59
72
|
fevex: Fevex;
|
|
60
73
|
adapter: ChannelAdapter<TInput, TDelivery>;
|
|
61
|
-
agentName: string;
|
|
62
|
-
resolveSessionId?: (message: ChannelMessage) => SessionId | undefined | Promise<SessionId | undefined>;
|
|
63
74
|
onEvent?: (event: ChannelEvent) => void;
|
|
64
75
|
signal?: AbortSignal;
|
|
65
76
|
}
|
|
77
|
+
interface ChannelRunOptions<TInput = unknown, TDelivery = unknown> extends ChannelDeliveryOptions<TInput, TDelivery> {
|
|
78
|
+
agentName: string;
|
|
79
|
+
resolveSessionId?: (message: ChannelMessage) => SessionId | undefined | Promise<SessionId | undefined>;
|
|
80
|
+
}
|
|
66
81
|
type ChannelRunResult<TDelivery = unknown> = {
|
|
67
82
|
ignored: true;
|
|
68
83
|
events: ChannelRunEvent[];
|
|
@@ -72,11 +87,17 @@ type ChannelRunResult<TDelivery = unknown> = {
|
|
|
72
87
|
run: AgentRun;
|
|
73
88
|
output: ChannelOutput;
|
|
74
89
|
delivery: TDelivery;
|
|
90
|
+
/** Present when the agent is waiting for external information. */
|
|
91
|
+
elicitation?: ElicitationRequest;
|
|
75
92
|
events: ChannelRunEvent[];
|
|
76
93
|
};
|
|
77
94
|
//#endregion
|
|
78
95
|
//#region src/channels/handle-channel-input.d.ts
|
|
79
|
-
/** Parses one channel input, runs the configured agent, and delivers its
|
|
96
|
+
/** Parses one channel input, runs the configured agent, and delivers its result or elicitation. */
|
|
80
97
|
declare function handleChannelInput<TInput, TDelivery>(input: TInput, options: ChannelRunOptions<TInput, TDelivery>): Promise<ChannelRunResult<TDelivery>>;
|
|
98
|
+
/** Delivers a stored agent result or elicitation without executing the agent again. */
|
|
99
|
+
declare function deliverChannelRun<TInput, TDelivery>(runId: RunId, message: ChannelMessage, options: ChannelDeliveryOptions<TInput, TDelivery>): Promise<Exclude<ChannelRunResult<TDelivery>, {
|
|
100
|
+
ignored: true;
|
|
101
|
+
}>>;
|
|
81
102
|
//#endregion
|
|
82
|
-
export {
|
|
103
|
+
export { ChannelDeliveryOptions as a, ChannelEvent as c, ChannelMessage as d, ChannelOutput as f, ChannelRunResult as h, ChannelContext as i, ChannelEventType as l, ChannelRunOptions as m, handleChannelInput as n, ChannelElicitationContext as o, ChannelRunEvent as p, ChannelAdapter as r, ChannelError as s, deliverChannelRun as t, ChannelFailurePhase as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as ToolSpec, i as AgentMessage, l as JsonValue, m as ToolCall, s as JsonObject } from "./types-
|
|
1
|
+
import { g as ToolSpec, i as AgentMessage, l as JsonValue, m as ToolCall, s as JsonObject } from "./types-BDVUoAU6.mjs";
|
|
2
2
|
//#region src/models/model.d.ts
|
|
3
3
|
/** Provider-neutral reasoning levels accepted by model gateways. */
|
|
4
4
|
type ReasoningEffort = 'provider-default' | 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as ExactDefinition } from "./exact-definition-NHBBFlFR.mjs";
|
|
2
|
-
import { f as RunId, l as JsonValue, o as ExecutionContext, s as JsonObject } from "./types-
|
|
3
|
-
import {
|
|
2
|
+
import { f as RunId, l as JsonValue, o as ExecutionContext, s as JsonObject } from "./types-BDVUoAU6.mjs";
|
|
3
|
+
import { I as SessionId, M as RunResult, j as RunRequest, n as AgentLimits } from "./index-CGmHXV31.mjs";
|
|
4
4
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
5
|
//#region src/workflows/workflow.d.ts
|
|
6
6
|
/** Context shared with workflow steps and compensation handlers. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as ExactDefinition } from "./exact-definition-NHBBFlFR.mjs";
|
|
2
|
-
import { f as RunId, i as AgentMessage, l as JsonValue, o as ExecutionContext, s as JsonObject, t as AgentEvent } from "./types-
|
|
3
|
-
import { c as ReasoningEffort, i as ModelRef, l as ToolChoice, s as ModelUsage } from "./index-
|
|
2
|
+
import { f as RunId, i as AgentMessage, l as JsonValue, o as ExecutionContext, s as JsonObject, t as AgentEvent } from "./types-BDVUoAU6.mjs";
|
|
3
|
+
import { c as ReasoningEffort, i as ModelRef, l as ToolChoice, r as ModelMetadata, s as ModelUsage } from "./index-BPwkgdo3.mjs";
|
|
4
4
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
5
|
//#region src/runtime/run-store.d.ts
|
|
6
6
|
/** Stable identifier for conversation history shared across runs. */
|
|
@@ -115,11 +115,16 @@ interface AgentRun<TOutput = JsonValue> {
|
|
|
115
115
|
id: RunId;
|
|
116
116
|
sessionId: SessionId;
|
|
117
117
|
agentName: string;
|
|
118
|
+
/** Effective model identity retained after checkpoint cleanup. Absent on older runs. */
|
|
119
|
+
modelName?: string;
|
|
120
|
+
modelMetadata?: ModelMetadata;
|
|
118
121
|
status: RunStatus;
|
|
119
122
|
revision: number;
|
|
120
123
|
pause?: RunPause;
|
|
121
124
|
createdAt: string;
|
|
122
125
|
updatedAt: string;
|
|
126
|
+
/** Absolute deadline for the run, derived from `RunRequest.deadlineMs`. */
|
|
127
|
+
deadlineAt?: string;
|
|
123
128
|
output?: TOutput;
|
|
124
129
|
error?: string;
|
|
125
130
|
usage?: ModelUsage;
|
|
@@ -134,6 +139,8 @@ interface WorkflowRun<TOutput = JsonValue> {
|
|
|
134
139
|
pause?: RunPause;
|
|
135
140
|
createdAt: string;
|
|
136
141
|
updatedAt: string;
|
|
142
|
+
/** Absolute deadline for the run, derived from `RunRequest.deadlineMs`. */
|
|
143
|
+
deadlineAt?: string;
|
|
137
144
|
output?: TOutput;
|
|
138
145
|
error?: string;
|
|
139
146
|
usage?: ModelUsage;
|
|
@@ -148,6 +155,8 @@ interface TeamRun<TOutput = JsonValue> {
|
|
|
148
155
|
pause?: RunPause;
|
|
149
156
|
createdAt: string;
|
|
150
157
|
updatedAt: string;
|
|
158
|
+
/** Absolute deadline for the run, derived from `RunRequest.deadlineMs`. */
|
|
159
|
+
deadlineAt?: string;
|
|
151
160
|
output?: TOutput;
|
|
152
161
|
error?: string;
|
|
153
162
|
usage?: ModelUsage;
|
|
@@ -170,6 +179,29 @@ interface ListEventsOptions {
|
|
|
170
179
|
order?: 'asc' | 'desc';
|
|
171
180
|
}
|
|
172
181
|
declare function validateListEventsOptions(options: ListEventsOptions): void;
|
|
182
|
+
type RetainableRunStatus = 'completed' | 'failed' | 'cancelled';
|
|
183
|
+
interface PurgeRunsOptions {
|
|
184
|
+
/** Delete runs created strictly before this ISO timestamp. */
|
|
185
|
+
before?: string;
|
|
186
|
+
/** Terminal statuses to delete. Defaults to completed, failed and cancelled. */
|
|
187
|
+
statuses?: RetainableRunStatus[];
|
|
188
|
+
/** Maximum runs to delete in one call. */
|
|
189
|
+
limit?: number;
|
|
190
|
+
}
|
|
191
|
+
interface PurgeRunsResult {
|
|
192
|
+
runs: number;
|
|
193
|
+
sessions: number;
|
|
194
|
+
}
|
|
195
|
+
/** Optional capability for stores that can delete terminal runs and empty sessions. */
|
|
196
|
+
interface RetainableRunStore extends RunStore {
|
|
197
|
+
purgeRuns(options?: PurgeRunsOptions): Promise<PurgeRunsResult>;
|
|
198
|
+
}
|
|
199
|
+
declare function isRetainableRunStore(store: RunStore): store is RetainableRunStore;
|
|
200
|
+
declare function validatePurgeRunsOptions(options?: PurgeRunsOptions): {
|
|
201
|
+
statuses: RetainableRunStatus[];
|
|
202
|
+
before?: string;
|
|
203
|
+
limit?: number;
|
|
204
|
+
};
|
|
173
205
|
interface RunStore {
|
|
174
206
|
getRun<TRun extends RunRecord<unknown> = AgentRun>(runId: RunId): Promise<TRun | undefined>;
|
|
175
207
|
saveRun(run: RunRecord): Promise<void>;
|
|
@@ -358,6 +390,8 @@ interface RunRequest<TInput = unknown, TOutput = unknown> {
|
|
|
358
390
|
toolChoice?: ToolChoice;
|
|
359
391
|
sessionId?: SessionId;
|
|
360
392
|
signal?: AbortSignal;
|
|
393
|
+
/** Maximum wall-clock time for this run, including time spent paused. */
|
|
394
|
+
deadlineMs?: number;
|
|
361
395
|
}
|
|
362
396
|
interface RunResult<TOutput = unknown> {
|
|
363
397
|
runId: RunId;
|
|
@@ -386,6 +420,7 @@ declare class InMemoryRunStore implements DurableRunStore {
|
|
|
386
420
|
getToolExecution(runId: RunId, toolCallId: string): Promise<ToolExecutionRecord | undefined>;
|
|
387
421
|
createExecution(create: ExecutionCreate): Promise<boolean>;
|
|
388
422
|
commitExecution(commit: ExecutionCommit): Promise<boolean>;
|
|
423
|
+
purgeRuns(options?: PurgeRunsOptions): Promise<PurgeRunsResult>;
|
|
389
424
|
acquireLease(lease: RunLease): Promise<boolean>;
|
|
390
425
|
renewLease(lease: RunLease): Promise<boolean>;
|
|
391
426
|
releaseLease(runId: RunId, ownerId: string, generation: number): Promise<void>;
|
|
@@ -422,4 +457,4 @@ interface AgentDefinition<TInput = unknown, TOutput = unknown> {
|
|
|
422
457
|
}
|
|
423
458
|
declare function defineAgent<T extends AgentDefinition>(agent: ExactDefinition<T, AgentDefinition>): T;
|
|
424
459
|
//#endregion
|
|
425
|
-
export {
|
|
460
|
+
export { validatePurgeRunsOptions as $, RunRecord as A, TimerResolution as B, PurgeRunsResult as C, RunCheckpoint as D, RetainableRunStore as E, Session as F, WorkflowCheckpoint as G, ToolExecutionResolution as H, SessionId as I, WorkflowStepRecord as J, WorkflowCompensationRecord as K, StoredRunCheckpoint as L, RunResult as M, RunStatus as N, RunLease as O, RunStore as P, validateListEventsOptions as Q, TeamCheckpoint as R, PurgeRunsOptions as S, RetainableRunStatus as T, ToolExecutionStatus as U, ToolExecutionRecord as V, WorkflowBudgetUsage as W, isDurableRunStore as X, WorkflowWaitRecord as Y, isRetainableRunStore as Z, EventResolution as _, AgentRun as a, ListEventsOptions as b, ApprovalRequest as c, CoordinatorRun as d, CredentialStore as f, ElicitationResolution as g, ElicitationRequest as h, InMemoryRunStore as i, RunRequest as j, RunPause as k, ApprovalResolution as l, ElicitationMode as m, AgentLimits as n, AgentRunPause as o, DurableRunStore as p, WorkflowRun as q, defineAgent as r, ApprovalMode as s, AgentDefinition as t, CoordinatorCheckpoint as u, ExecutionCommit as v, ResumeRunResolution as w, PendingToolExecution as x, ExecutionCreate as y, TeamRun as z };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as ExactDefinition } from "./exact-definition-NHBBFlFR.mjs";
|
|
2
|
-
import { f as RunId, g as ToolSpec, h as ToolEventSource, l as JsonValue, o as ExecutionContext, s as JsonObject } from "./types-
|
|
3
|
-
import { i as SandboxCapabilities, r as Sandbox } from "./index-
|
|
2
|
+
import { F as ArtifactStore, f as RunId, g as ToolSpec, h as ToolEventSource, l as JsonValue, o as ExecutionContext, s as JsonObject } from "./types-BDVUoAU6.mjs";
|
|
3
|
+
import { i as SandboxCapabilities, r as Sandbox } from "./index-BFnWUu7j.mjs";
|
|
4
4
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
5
|
//#region src/tools/tool.d.ts
|
|
6
6
|
interface ToolExecutionContext {
|
|
@@ -10,6 +10,8 @@ interface ToolExecutionContext {
|
|
|
10
10
|
idempotencyKey: string;
|
|
11
11
|
getCredential(name: string): Promise<string>;
|
|
12
12
|
sandbox?: Sandbox;
|
|
13
|
+
/** Optional artifact store; writes emit `artifact.created`. */
|
|
14
|
+
artifacts?: ArtifactStore;
|
|
13
15
|
context?: ExecutionContext;
|
|
14
16
|
signal?: AbortSignal;
|
|
15
17
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { f as RunId, l as JsonValue, o as ExecutionContext } from "./types-
|
|
2
|
-
import { x as ToolRisk } from "./index-
|
|
1
|
+
import { f as RunId, l as JsonValue, o as ExecutionContext } from "./types-BDVUoAU6.mjs";
|
|
2
|
+
import { x as ToolRisk } from "./index-CKQCNvJz.mjs";
|
|
3
3
|
//#region src/policies/policy.d.ts
|
|
4
4
|
/** Authorization outcome returned by a policy. */
|
|
5
5
|
type PolicyDecision = 'allow' | 'deny' | 'require_approval';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { f as RunId, l as JsonValue, s as JsonObject } from "./types-
|
|
2
|
-
import { r as ModelMetadata, s as ModelUsage } from "./index-
|
|
3
|
-
import {
|
|
1
|
+
import { f as RunId, l as JsonValue, s as JsonObject } from "./types-BDVUoAU6.mjs";
|
|
2
|
+
import { r as ModelMetadata, s as ModelUsage } from "./index-BPwkgdo3.mjs";
|
|
3
|
+
import { I as SessionId } from "./index-CGmHXV31.mjs";
|
|
4
4
|
//#region src/observability/trace.d.ts
|
|
5
5
|
/** Final state represented by an exported run trace. */
|
|
6
6
|
type TraceStatus = 'completed' | 'failed' | 'cancelled';
|