@axlsdk/axl 0.19.1 → 0.20.0
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 +27 -11
- package/dist/{chunk-JBLQKU6X.js → chunk-NVPA4RY6.js} +6 -4
- package/dist/chunk-NVPA4RY6.js.map +1 -0
- package/dist/index.cjs +1838 -857
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +510 -257
- package/dist/index.d.ts +510 -257
- package/dist/index.js +1752 -780
- package/dist/index.js.map +1 -1
- package/dist/span-manager-74ZCGELO.js +7 -0
- package/package.json +2 -2
- package/dist/chunk-JBLQKU6X.js.map +0 -1
- package/dist/span-manager-3IKXXUTZ.js +0 -7
- /package/dist/{span-manager-3IKXXUTZ.js.map → span-manager-74ZCGELO.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,130 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__require
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NVPA4RY6.js";
|
|
4
4
|
|
|
5
5
|
// src/tool.ts
|
|
6
|
-
|
|
7
|
-
function validateStringLengths(value, maxLen, path = "") {
|
|
8
|
-
if (typeof value === "string") {
|
|
9
|
-
if (value.length > maxLen) {
|
|
10
|
-
throw new Error(
|
|
11
|
-
`String argument${path ? ` at "${path}"` : ""} exceeds maximum length (${value.length} > ${maxLen})`
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
} else if (Array.isArray(value)) {
|
|
15
|
-
for (let i = 0; i < value.length; i++) {
|
|
16
|
-
validateStringLengths(value[i], maxLen, path ? `${path}[${i}]` : `[${i}]`);
|
|
17
|
-
}
|
|
18
|
-
} else if (value !== null && typeof value === "object") {
|
|
19
|
-
for (const [key, val] of Object.entries(value)) {
|
|
20
|
-
validateStringLengths(val, maxLen, path ? `${path}.${key}` : key);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function sleep(ms) {
|
|
25
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
26
|
-
}
|
|
27
|
-
function getBackoffMs(attempt, strategy) {
|
|
28
|
-
switch (strategy) {
|
|
29
|
-
case "none":
|
|
30
|
-
return 0;
|
|
31
|
-
case "linear":
|
|
32
|
-
return attempt * 1e3;
|
|
33
|
-
case "exponential":
|
|
34
|
-
return Math.pow(2, attempt - 1) * 1e3;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function tool(config) {
|
|
38
|
-
const retryPolicy = {
|
|
39
|
-
attempts: config.retry?.attempts ?? 1,
|
|
40
|
-
backoff: config.retry?.backoff ?? "exponential",
|
|
41
|
-
on: config.retry?.on
|
|
42
|
-
};
|
|
43
|
-
const maxStringLen = config.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH;
|
|
44
|
-
const execute = async (input, ctx) => {
|
|
45
|
-
const parsed = config.input.parse(input);
|
|
46
|
-
if (maxStringLen > 0) {
|
|
47
|
-
validateStringLengths(parsed, maxStringLen);
|
|
48
|
-
}
|
|
49
|
-
const maxAttempts = retryPolicy.attempts ?? 1;
|
|
50
|
-
let lastError;
|
|
51
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
52
|
-
try {
|
|
53
|
-
return await config.handler(parsed, ctx);
|
|
54
|
-
} catch (err) {
|
|
55
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
56
|
-
if (attempt === maxAttempts) break;
|
|
57
|
-
if (retryPolicy.on && !retryPolicy.on(lastError)) {
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
const backoffMs = getBackoffMs(attempt, retryPolicy.backoff ?? "exponential");
|
|
61
|
-
if (backoffMs > 0) {
|
|
62
|
-
await sleep(backoffMs);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
throw lastError;
|
|
67
|
-
};
|
|
68
|
-
return {
|
|
69
|
-
name: config.name,
|
|
70
|
-
description: config.description,
|
|
71
|
-
inputSchema: config.input,
|
|
72
|
-
sensitive: config.sensitive ?? false,
|
|
73
|
-
retry: retryPolicy,
|
|
74
|
-
requireApproval: config.requireApproval ?? false,
|
|
75
|
-
hooks: config.hooks,
|
|
76
|
-
...config.toModelOutput !== void 0 ? { toModelOutput: config.toModelOutput } : {},
|
|
77
|
-
async run(ctx, input) {
|
|
78
|
-
const startTime = Date.now();
|
|
79
|
-
try {
|
|
80
|
-
let processedInput = input;
|
|
81
|
-
if (config.hooks?.before) {
|
|
82
|
-
processedInput = await config.hooks.before(processedInput, ctx);
|
|
83
|
-
}
|
|
84
|
-
let result = await execute(processedInput, ctx);
|
|
85
|
-
if (config.hooks?.after) {
|
|
86
|
-
result = await config.hooks.after(result, ctx);
|
|
87
|
-
}
|
|
88
|
-
ctx.log("tool_call_complete", {
|
|
89
|
-
tool: config.name,
|
|
90
|
-
duration: Date.now() - startTime
|
|
91
|
-
});
|
|
92
|
-
return result;
|
|
93
|
-
} catch (err) {
|
|
94
|
-
ctx.log("tool_call_error", {
|
|
95
|
-
tool: config.name,
|
|
96
|
-
error: err instanceof Error ? err.message : String(err),
|
|
97
|
-
duration: Date.now() - startTime
|
|
98
|
-
});
|
|
99
|
-
throw err;
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
_execute: execute
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/providers/types.ts
|
|
107
|
-
function resolveThinkingOptions(options) {
|
|
108
|
-
if (options.thinkingBudget !== void 0 && options.thinkingBudget < 0) {
|
|
109
|
-
throw new Error(`thinkingBudget must be non-negative, got ${options.thinkingBudget}`);
|
|
110
|
-
}
|
|
111
|
-
const effort = options.effort;
|
|
112
|
-
const thinkingBudget = options.thinkingBudget;
|
|
113
|
-
const hasBudgetOverride = thinkingBudget !== void 0 && thinkingBudget > 0;
|
|
114
|
-
return {
|
|
115
|
-
effort,
|
|
116
|
-
thinkingBudget,
|
|
117
|
-
includeThoughts: options.includeThoughts ?? false,
|
|
118
|
-
// Budget override wins: effort: 'none' + thinkingBudget: 5000 → thinking enabled
|
|
119
|
-
thinkingDisabled: (effort === "none" || thinkingBudget === 0) && !hasBudgetOverride,
|
|
120
|
-
activeEffort: effort && effort !== "none" ? effort : void 0,
|
|
121
|
-
hasBudgetOverride
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
async function resolveApiKey(source) {
|
|
125
|
-
if (typeof source === "function") return await source() ?? "";
|
|
126
|
-
return source ?? "";
|
|
127
|
-
}
|
|
6
|
+
import { ZodError } from "zod";
|
|
128
7
|
|
|
129
8
|
// src/errors.ts
|
|
130
9
|
var AxlError = class extends Error {
|
|
@@ -135,6 +14,55 @@ var AxlError = class extends Error {
|
|
|
135
14
|
this.code = code;
|
|
136
15
|
}
|
|
137
16
|
};
|
|
17
|
+
var EventStreamOverflowError = class extends Error {
|
|
18
|
+
maxQueued;
|
|
19
|
+
eventType;
|
|
20
|
+
constructor(maxQueued, eventType, cause) {
|
|
21
|
+
super(
|
|
22
|
+
`AxlEventBus queue exceeded maxQueued=${maxQueued} (event type: ${eventType}). Consumer is too slow or the producer is unbounded. Configure \`maxQueued\`/\`onOverflow\` on the runtime, or set maxQueued: Infinity to disable.`
|
|
23
|
+
);
|
|
24
|
+
this.name = "EventStreamOverflowError";
|
|
25
|
+
this.maxQueued = maxQueued;
|
|
26
|
+
this.eventType = eventType;
|
|
27
|
+
if (cause !== void 0) {
|
|
28
|
+
Object.defineProperty(this, "cause", {
|
|
29
|
+
configurable: true,
|
|
30
|
+
enumerable: false,
|
|
31
|
+
value: cause
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
function isEventStreamOverflowError(error) {
|
|
37
|
+
return error instanceof EventStreamOverflowError;
|
|
38
|
+
}
|
|
39
|
+
function rethrowEventStreamOverflow(error) {
|
|
40
|
+
if (isEventStreamOverflowError(error)) throw error;
|
|
41
|
+
}
|
|
42
|
+
function preserveErrorCause(error, cause) {
|
|
43
|
+
if (cause === void 0 || cause === error || "cause" in error) return error;
|
|
44
|
+
Object.defineProperty(error, "cause", {
|
|
45
|
+
configurable: true,
|
|
46
|
+
enumerable: false,
|
|
47
|
+
value: cause
|
|
48
|
+
});
|
|
49
|
+
return error;
|
|
50
|
+
}
|
|
51
|
+
var ToolFailure = class extends AxlError {
|
|
52
|
+
modelMessage;
|
|
53
|
+
constructor(options) {
|
|
54
|
+
super(options.code ?? "TOOL_FAILURE", options.message);
|
|
55
|
+
this.name = "ToolFailure";
|
|
56
|
+
this.modelMessage = options.modelMessage;
|
|
57
|
+
if (options.cause !== void 0) {
|
|
58
|
+
Object.defineProperty(this, "cause", {
|
|
59
|
+
configurable: true,
|
|
60
|
+
enumerable: false,
|
|
61
|
+
value: options.cause
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
138
66
|
var ToolModelOutputError = class extends AxlError {
|
|
139
67
|
toolName;
|
|
140
68
|
cause;
|
|
@@ -240,19 +168,209 @@ var ValidationError = class extends AxlError {
|
|
|
240
168
|
this.retries = retries;
|
|
241
169
|
}
|
|
242
170
|
};
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
171
|
+
|
|
172
|
+
// src/tool.ts
|
|
173
|
+
var DEFAULT_MAX_STRING_LENGTH = 1e4;
|
|
174
|
+
var ToolStringLengthError = class extends Error {
|
|
175
|
+
constructor(path, actual, maximum) {
|
|
176
|
+
const renderedPath = path.reduce(
|
|
177
|
+
(current, part) => typeof part === "number" ? `${current}[${part}]` : current ? `${current}.${part}` : part,
|
|
178
|
+
""
|
|
179
|
+
);
|
|
247
180
|
super(
|
|
248
|
-
"
|
|
249
|
-
`Agent "${agentName}" attempted to call tool "${toolName}" which is not in its ACL`
|
|
181
|
+
`String argument${renderedPath ? ` at "${renderedPath}"` : ""} exceeds maximum length (${actual} > ${maximum})`
|
|
250
182
|
);
|
|
251
|
-
this.
|
|
252
|
-
this.
|
|
253
|
-
this.
|
|
183
|
+
this.path = path;
|
|
184
|
+
this.actual = actual;
|
|
185
|
+
this.maximum = maximum;
|
|
186
|
+
this.name = "ToolStringLengthError";
|
|
254
187
|
}
|
|
255
188
|
};
|
|
189
|
+
function validateStringLengths(value, maxLen, path = []) {
|
|
190
|
+
if (typeof value === "string") {
|
|
191
|
+
if (value.length > maxLen) {
|
|
192
|
+
throw new ToolStringLengthError(path, value.length, maxLen);
|
|
193
|
+
}
|
|
194
|
+
} else if (Array.isArray(value)) {
|
|
195
|
+
for (let i = 0; i < value.length; i++) {
|
|
196
|
+
validateStringLengths(value[i], maxLen, [...path, i]);
|
|
197
|
+
}
|
|
198
|
+
} else if (value !== null && typeof value === "object") {
|
|
199
|
+
for (const [key, val] of Object.entries(value)) {
|
|
200
|
+
validateStringLengths(val, maxLen, [...path, key]);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function sleep(ms, signal) {
|
|
205
|
+
if (!signal) return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
206
|
+
signal.throwIfAborted();
|
|
207
|
+
return new Promise((resolve2, reject) => {
|
|
208
|
+
const timeout = setTimeout(() => {
|
|
209
|
+
signal.removeEventListener("abort", onAbort);
|
|
210
|
+
resolve2();
|
|
211
|
+
}, ms);
|
|
212
|
+
const onAbort = () => {
|
|
213
|
+
clearTimeout(timeout);
|
|
214
|
+
reject(signal.reason);
|
|
215
|
+
};
|
|
216
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
function isAbortError(error) {
|
|
220
|
+
try {
|
|
221
|
+
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
222
|
+
} catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function toolArgumentIssues(error) {
|
|
227
|
+
if (error instanceof ZodError) {
|
|
228
|
+
return error.issues.map((issue) => ({
|
|
229
|
+
path: issue.path.map((part) => typeof part === "symbol" ? String(part) : part),
|
|
230
|
+
code: issue.code,
|
|
231
|
+
message: issue.message
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
if (error instanceof ToolStringLengthError) {
|
|
235
|
+
return [
|
|
236
|
+
{
|
|
237
|
+
path: error.path,
|
|
238
|
+
code: "too_big",
|
|
239
|
+
message: error.message
|
|
240
|
+
}
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
return [{ path: [], code: "invalid_arguments", message: "Tool arguments are invalid." }];
|
|
244
|
+
}
|
|
245
|
+
var TOOL_INTERNALS = /* @__PURE__ */ new WeakMap();
|
|
246
|
+
function getToolInternals(tool2) {
|
|
247
|
+
const internals = TOOL_INTERNALS.get(tool2);
|
|
248
|
+
if (!internals) throw new Error(`Tool "${tool2.name}" was not created by tool()`);
|
|
249
|
+
return internals;
|
|
250
|
+
}
|
|
251
|
+
function prepareToolInput(tool2, input) {
|
|
252
|
+
return getToolInternals(tool2).prepareInput(input);
|
|
253
|
+
}
|
|
254
|
+
function executePreparedTool(tool2, input, ctx, options) {
|
|
255
|
+
return getToolInternals(tool2).executePrepared(input, ctx, options);
|
|
256
|
+
}
|
|
257
|
+
function getBackoffMs(attempt, strategy) {
|
|
258
|
+
switch (strategy) {
|
|
259
|
+
case "none":
|
|
260
|
+
return 0;
|
|
261
|
+
case "linear":
|
|
262
|
+
return attempt * 1e3;
|
|
263
|
+
case "exponential":
|
|
264
|
+
return Math.pow(2, attempt - 1) * 1e3;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function tool(config) {
|
|
268
|
+
const retryPolicy = {
|
|
269
|
+
attempts: config.retry?.attempts ?? 1,
|
|
270
|
+
backoff: config.retry?.backoff ?? "exponential",
|
|
271
|
+
on: config.retry?.on
|
|
272
|
+
};
|
|
273
|
+
const maxStringLen = config.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH;
|
|
274
|
+
const prepareInput = (input) => {
|
|
275
|
+
const parsed = config.input.parse(input);
|
|
276
|
+
if (maxStringLen > 0) {
|
|
277
|
+
validateStringLengths(parsed, maxStringLen);
|
|
278
|
+
}
|
|
279
|
+
return parsed;
|
|
280
|
+
};
|
|
281
|
+
const executePrepared = async (parsed, ctx, options) => {
|
|
282
|
+
const maxAttempts = retryPolicy.attempts ?? 1;
|
|
283
|
+
let lastError;
|
|
284
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
285
|
+
options?.signal?.throwIfAborted();
|
|
286
|
+
options?.onAttempt?.(attempt);
|
|
287
|
+
try {
|
|
288
|
+
const result = await config.handler(parsed, ctx);
|
|
289
|
+
if (options?.checkAfterHandlerAbort !== false) options?.signal?.throwIfAborted();
|
|
290
|
+
return result;
|
|
291
|
+
} catch (err) {
|
|
292
|
+
if (options?.signal?.aborted) options.signal.throwIfAborted();
|
|
293
|
+
if (isAbortError(err)) throw err;
|
|
294
|
+
rethrowEventStreamOverflow(err);
|
|
295
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
296
|
+
if (attempt === maxAttempts) break;
|
|
297
|
+
if (retryPolicy.on && !retryPolicy.on(lastError)) {
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
const backoffMs = getBackoffMs(attempt, retryPolicy.backoff ?? "exponential");
|
|
301
|
+
if (backoffMs > 0) {
|
|
302
|
+
await sleep(backoffMs, options?.signal);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
throw lastError;
|
|
307
|
+
};
|
|
308
|
+
const execute = async (input, ctx) => executePrepared(prepareInput(input), ctx);
|
|
309
|
+
const instance = {
|
|
310
|
+
name: config.name,
|
|
311
|
+
description: config.description,
|
|
312
|
+
inputSchema: config.input,
|
|
313
|
+
sensitive: config.sensitive ?? false,
|
|
314
|
+
retry: retryPolicy,
|
|
315
|
+
requireApproval: config.requireApproval ?? false,
|
|
316
|
+
hooks: config.hooks,
|
|
317
|
+
...config.toModelOutput !== void 0 ? { toModelOutput: config.toModelOutput } : {},
|
|
318
|
+
async run(ctx, input) {
|
|
319
|
+
const startTime = Date.now();
|
|
320
|
+
try {
|
|
321
|
+
let processedInput = input;
|
|
322
|
+
if (config.hooks?.before) {
|
|
323
|
+
processedInput = await config.hooks.before(processedInput, ctx);
|
|
324
|
+
}
|
|
325
|
+
let result = await execute(processedInput, ctx);
|
|
326
|
+
if (config.hooks?.after) {
|
|
327
|
+
result = await config.hooks.after(result, ctx);
|
|
328
|
+
}
|
|
329
|
+
ctx.log("tool_call_complete", {
|
|
330
|
+
tool: config.name,
|
|
331
|
+
duration: Date.now() - startTime
|
|
332
|
+
});
|
|
333
|
+
return result;
|
|
334
|
+
} catch (err) {
|
|
335
|
+
ctx.log("tool_call_error", {
|
|
336
|
+
tool: config.name,
|
|
337
|
+
error: err instanceof Error ? err.message : String(err),
|
|
338
|
+
duration: Date.now() - startTime
|
|
339
|
+
});
|
|
340
|
+
throw err;
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
_execute: execute
|
|
344
|
+
};
|
|
345
|
+
TOOL_INTERNALS.set(instance, {
|
|
346
|
+
prepareInput,
|
|
347
|
+
executePrepared
|
|
348
|
+
});
|
|
349
|
+
return instance;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/providers/types.ts
|
|
353
|
+
function resolveThinkingOptions(options) {
|
|
354
|
+
if (options.thinkingBudget !== void 0 && options.thinkingBudget < 0) {
|
|
355
|
+
throw new Error(`thinkingBudget must be non-negative, got ${options.thinkingBudget}`);
|
|
356
|
+
}
|
|
357
|
+
const effort = options.effort;
|
|
358
|
+
const thinkingBudget = options.thinkingBudget;
|
|
359
|
+
const hasBudgetOverride = thinkingBudget !== void 0 && thinkingBudget > 0;
|
|
360
|
+
return {
|
|
361
|
+
effort,
|
|
362
|
+
thinkingBudget,
|
|
363
|
+
includeThoughts: options.includeThoughts ?? false,
|
|
364
|
+
// Budget override wins: effort: 'none' + thinkingBudget: 5000 → thinking enabled
|
|
365
|
+
thinkingDisabled: (effort === "none" || thinkingBudget === 0) && !hasBudgetOverride,
|
|
366
|
+
activeEffort: effort && effort !== "none" ? effort : void 0,
|
|
367
|
+
hasBudgetOverride
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
async function resolveApiKey(source) {
|
|
371
|
+
if (typeof source === "function") return await source() ?? "";
|
|
372
|
+
return source ?? "";
|
|
373
|
+
}
|
|
256
374
|
|
|
257
375
|
// src/providers/errors.ts
|
|
258
376
|
var ProviderError = class extends AxlError {
|
|
@@ -361,7 +479,7 @@ function sleep2(ms, signal) {
|
|
|
361
479
|
);
|
|
362
480
|
});
|
|
363
481
|
}
|
|
364
|
-
function
|
|
482
|
+
function isAbortError2(err, signal) {
|
|
365
483
|
if (signal?.aborted) return true;
|
|
366
484
|
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
367
485
|
}
|
|
@@ -380,7 +498,7 @@ async function fetchWithRetry(input, init, opts) {
|
|
|
380
498
|
try {
|
|
381
499
|
res = await fetch(input, init);
|
|
382
500
|
} catch (err) {
|
|
383
|
-
if (
|
|
501
|
+
if (isAbortError2(err, init?.signal ?? void 0)) throw err;
|
|
384
502
|
if (attempt >= maxRetries) {
|
|
385
503
|
throw buildProviderError({
|
|
386
504
|
provider,
|
|
@@ -3062,7 +3180,7 @@ var defaultRegistry = new ProviderRegistry();
|
|
|
3062
3180
|
// src/context.ts
|
|
3063
3181
|
import { AsyncLocalStorage } from "async_hooks";
|
|
3064
3182
|
import { randomUUID } from "crypto";
|
|
3065
|
-
import { z, ZodError } from "zod";
|
|
3183
|
+
import { z, ZodError as ZodError2 } from "zod";
|
|
3066
3184
|
|
|
3067
3185
|
// src/partial-json.ts
|
|
3068
3186
|
var MAX_DEPTH = 256;
|
|
@@ -3681,9 +3799,9 @@ var AXL_EVENT_TYPES = [
|
|
|
3681
3799
|
// Tool invocation lifecycle
|
|
3682
3800
|
"tool_call_start",
|
|
3683
3801
|
"tool_call_end",
|
|
3802
|
+
"tool_call_rejected",
|
|
3684
3803
|
// Single-point tool events
|
|
3685
3804
|
"tool_approval",
|
|
3686
|
-
"tool_denied",
|
|
3687
3805
|
// Delegation
|
|
3688
3806
|
"delegate",
|
|
3689
3807
|
// Handoff (spans two asks; not AskScoped).
|
|
@@ -3714,7 +3832,7 @@ var AXL_EVENT_TYPES = [
|
|
|
3714
3832
|
"memory_remember",
|
|
3715
3833
|
"memory_recall",
|
|
3716
3834
|
"memory_forget",
|
|
3717
|
-
//
|
|
3835
|
+
// Scoped execution checkpoints (`ctx.checkpoint`)
|
|
3718
3836
|
"checkpoint_save",
|
|
3719
3837
|
"checkpoint_replay",
|
|
3720
3838
|
// Human-in-the-loop (`ctx.awaitHuman`)
|
|
@@ -3729,6 +3847,12 @@ var AXL_EVENT_TYPES = [
|
|
|
3729
3847
|
"done",
|
|
3730
3848
|
"error"
|
|
3731
3849
|
];
|
|
3850
|
+
var AXL_EVENT_TYPES_V2 = AXL_EVENT_TYPES;
|
|
3851
|
+
var AXL_TOOL_LIFECYCLE_TYPES_V2 = [
|
|
3852
|
+
"tool_call_start",
|
|
3853
|
+
"tool_call_end",
|
|
3854
|
+
"tool_call_rejected"
|
|
3855
|
+
];
|
|
3732
3856
|
|
|
3733
3857
|
// src/event-stream.ts
|
|
3734
3858
|
var STREAM_EVENTS = new Set(AXL_EVENT_TYPES);
|
|
@@ -3744,8 +3868,8 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3744
3868
|
"agent_call_end",
|
|
3745
3869
|
"tool_call_start",
|
|
3746
3870
|
"tool_call_end",
|
|
3871
|
+
"tool_call_rejected",
|
|
3747
3872
|
"tool_approval",
|
|
3748
|
-
"tool_denied",
|
|
3749
3873
|
"handoff_start",
|
|
3750
3874
|
"handoff_return",
|
|
3751
3875
|
"delegate",
|
|
@@ -3759,18 +3883,6 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3759
3883
|
"await_human",
|
|
3760
3884
|
"await_human_resolved"
|
|
3761
3885
|
]);
|
|
3762
|
-
var EventStreamOverflowError = class extends Error {
|
|
3763
|
-
maxQueued;
|
|
3764
|
-
eventType;
|
|
3765
|
-
constructor(maxQueued, eventType) {
|
|
3766
|
-
super(
|
|
3767
|
-
`AxlEventBus queue exceeded maxQueued=${maxQueued} (event type: ${eventType}). Consumer is too slow or the producer is unbounded. Configure \`maxQueued\`/\`onOverflow\` on the runtime, or set maxQueued: Infinity to disable.`
|
|
3768
|
-
);
|
|
3769
|
-
this.name = "EventStreamOverflowError";
|
|
3770
|
-
this.maxQueued = maxQueued;
|
|
3771
|
-
this.eventType = eventType;
|
|
3772
|
-
}
|
|
3773
|
-
};
|
|
3774
3886
|
var DEFAULT_MAX_QUEUED = 1e4;
|
|
3775
3887
|
var DEFAULT_OVERFLOW = "drop-oldest-non-terminal";
|
|
3776
3888
|
var AxlEventBus = class {
|
|
@@ -3781,6 +3893,7 @@ var AxlEventBus = class {
|
|
|
3781
3893
|
maxQueued;
|
|
3782
3894
|
onOverflow;
|
|
3783
3895
|
overflowWarned = false;
|
|
3896
|
+
droppedEvents = 0;
|
|
3784
3897
|
/** Callbacks fired exactly once when `_finish()` is called. Used by
|
|
3785
3898
|
* curated views (e.g., `partialObjects`) that are listener-based and
|
|
3786
3899
|
* need a termination signal independent of any synthetic `done` event. */
|
|
@@ -3826,6 +3939,10 @@ var AxlEventBus = class {
|
|
|
3826
3939
|
* can be 4 KB+ each, hence the more aggressive cleanup here).
|
|
3827
3940
|
*/
|
|
3828
3941
|
stringStreamByAsk = /* @__PURE__ */ new Map();
|
|
3942
|
+
/** Internal fan-out for `stringStream()` subscribers. Kept separate from
|
|
3943
|
+
* the public EventEmitter so strict curated-view overflow is a runtime
|
|
3944
|
+
* signal, not mistaken for (and swallowed as) a buggy user listener. */
|
|
3945
|
+
stringStreamSubscribers = /* @__PURE__ */ new Set();
|
|
3829
3946
|
/**
|
|
3830
3947
|
* Hook fired when an iterator obtained from `[Symbol.asyncIterator]`
|
|
3831
3948
|
* is `await using`-disposed. Set via the second constructor argument.
|
|
@@ -3859,6 +3976,12 @@ var AxlEventBus = class {
|
|
|
3859
3976
|
this.bus.on("error", () => {
|
|
3860
3977
|
});
|
|
3861
3978
|
}
|
|
3979
|
+
/** Machine-readable completeness across this bus's bounded iterable views.
|
|
3980
|
+
* EventEmitter listener delivery is independent and is not described by
|
|
3981
|
+
* this status. */
|
|
3982
|
+
get observationStatus() {
|
|
3983
|
+
return this.droppedEvents === 0 ? { complete: true } : { complete: false, reason: "queue_overflow", droppedEvents: this.droppedEvents };
|
|
3984
|
+
}
|
|
3862
3985
|
unknownEventWarned = false;
|
|
3863
3986
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3864
3987
|
on(event, handler) {
|
|
@@ -4171,7 +4294,7 @@ var AxlEventBus = class {
|
|
|
4171
4294
|
}
|
|
4172
4295
|
return {
|
|
4173
4296
|
[Symbol.asyncIterator]() {
|
|
4174
|
-
const pending =
|
|
4297
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4175
4298
|
let resolveWaiter = null;
|
|
4176
4299
|
let finished = self.finished;
|
|
4177
4300
|
let unsubscribed = false;
|
|
@@ -4185,10 +4308,39 @@ var AxlEventBus = class {
|
|
|
4185
4308
|
if (filterPath !== void 0 && path !== filterPath) return false;
|
|
4186
4309
|
return true;
|
|
4187
4310
|
};
|
|
4311
|
+
const pendingKey = (askId, path) => JSON.stringify([askId, path]);
|
|
4312
|
+
const enqueue = (event) => {
|
|
4313
|
+
const key = pendingKey(event.askId, event.path);
|
|
4314
|
+
const existing = pending.get(key);
|
|
4315
|
+
if (existing) {
|
|
4316
|
+
pending.set(key, {
|
|
4317
|
+
...event,
|
|
4318
|
+
delta: existing.delta + event.delta
|
|
4319
|
+
});
|
|
4320
|
+
wakeWaiter();
|
|
4321
|
+
return;
|
|
4322
|
+
}
|
|
4323
|
+
if (pending.size >= self.maxQueued) {
|
|
4324
|
+
if (self.onOverflow === "throw") {
|
|
4325
|
+
self.droppedEvents++;
|
|
4326
|
+
throw new EventStreamOverflowError(self.maxQueued, "string_delta");
|
|
4327
|
+
}
|
|
4328
|
+
const oldest = pending.keys().next().value;
|
|
4329
|
+
if (oldest !== void 0) {
|
|
4330
|
+
pending.delete(oldest);
|
|
4331
|
+
self.droppedEvents++;
|
|
4332
|
+
}
|
|
4333
|
+
self.warnOverflow(
|
|
4334
|
+
"dropping oldest pending string field (stringStream subscriber is slower than producer)"
|
|
4335
|
+
);
|
|
4336
|
+
}
|
|
4337
|
+
pending.set(key, event);
|
|
4338
|
+
wakeWaiter();
|
|
4339
|
+
};
|
|
4188
4340
|
for (const [askId, paths] of self.stringStreamByAsk) {
|
|
4189
4341
|
for (const [path, acc] of paths) {
|
|
4190
4342
|
if (!matchesFilter(askId, path)) continue;
|
|
4191
|
-
|
|
4343
|
+
enqueue({
|
|
4192
4344
|
askId,
|
|
4193
4345
|
agent: acc.agent,
|
|
4194
4346
|
path,
|
|
@@ -4198,51 +4350,37 @@ var AxlEventBus = class {
|
|
|
4198
4350
|
});
|
|
4199
4351
|
}
|
|
4200
4352
|
}
|
|
4201
|
-
const
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
askId,
|
|
4210
|
-
agent: event.agent,
|
|
4211
|
-
path: event.data.path,
|
|
4212
|
-
delta: event.data.delta,
|
|
4213
|
-
accumulated,
|
|
4214
|
-
attempt: event.attempt
|
|
4215
|
-
});
|
|
4216
|
-
wakeWaiter();
|
|
4217
|
-
};
|
|
4218
|
-
self.bus.on("string_delta", handler);
|
|
4219
|
-
const pipelineHandler = (event) => {
|
|
4220
|
-
if (event.type !== "pipeline") return;
|
|
4221
|
-
if (event.status !== "failed") return;
|
|
4222
|
-
const askId = event.askId ?? "";
|
|
4223
|
-
for (let i = pending.length - 1; i >= 0; i--) {
|
|
4224
|
-
if (pending[i].askId === askId) pending.splice(i, 1);
|
|
4353
|
+
const subscriber = {
|
|
4354
|
+
push(event) {
|
|
4355
|
+
if (matchesFilter(event.askId, event.path)) enqueue(event);
|
|
4356
|
+
},
|
|
4357
|
+
reset(askId) {
|
|
4358
|
+
for (const [key, event] of pending) {
|
|
4359
|
+
if (event.askId === askId) pending.delete(key);
|
|
4360
|
+
}
|
|
4225
4361
|
}
|
|
4226
4362
|
};
|
|
4227
|
-
self.
|
|
4363
|
+
self.stringStreamSubscribers.add(subscriber);
|
|
4228
4364
|
const unsubscribe = () => {
|
|
4229
4365
|
if (unsubscribed) return;
|
|
4230
4366
|
unsubscribed = true;
|
|
4231
|
-
self.
|
|
4232
|
-
self.bus.off("pipeline", pipelineHandler);
|
|
4367
|
+
self.stringStreamSubscribers.delete(subscriber);
|
|
4233
4368
|
unsubscribeFinish();
|
|
4234
4369
|
};
|
|
4235
4370
|
const unsubscribeFinish = self._onFinish(() => {
|
|
4236
4371
|
finished = true;
|
|
4237
|
-
self.
|
|
4238
|
-
self.bus.off("pipeline", pipelineHandler);
|
|
4372
|
+
self.stringStreamSubscribers.delete(subscriber);
|
|
4239
4373
|
wakeWaiter();
|
|
4240
4374
|
});
|
|
4241
4375
|
return {
|
|
4242
4376
|
async next() {
|
|
4243
4377
|
while (true) {
|
|
4244
|
-
if (pending.
|
|
4245
|
-
|
|
4378
|
+
if (pending.size > 0) {
|
|
4379
|
+
const first = pending.entries().next().value;
|
|
4380
|
+
if (first) {
|
|
4381
|
+
pending.delete(first[0]);
|
|
4382
|
+
return { value: first[1], done: false };
|
|
4383
|
+
}
|
|
4246
4384
|
}
|
|
4247
4385
|
if (finished) {
|
|
4248
4386
|
unsubscribe();
|
|
@@ -4300,6 +4438,7 @@ var AxlEventBus = class {
|
|
|
4300
4438
|
*/
|
|
4301
4439
|
_push(event) {
|
|
4302
4440
|
if (this.finished) return;
|
|
4441
|
+
let overflowError;
|
|
4303
4442
|
if (event.type === "partial_object" && event.data?.object !== void 0) {
|
|
4304
4443
|
const askId = event.askId ?? "";
|
|
4305
4444
|
this.latestPartialByAsk.set(askId, {
|
|
@@ -4321,10 +4460,30 @@ var AxlEventBus = class {
|
|
|
4321
4460
|
text: (existing?.text ?? "") + event.data.delta,
|
|
4322
4461
|
attempt: event.attempt
|
|
4323
4462
|
});
|
|
4463
|
+
const accumulated = paths.get(event.data.path).text;
|
|
4464
|
+
for (const subscriber of this.stringStreamSubscribers) {
|
|
4465
|
+
try {
|
|
4466
|
+
subscriber.push({
|
|
4467
|
+
askId,
|
|
4468
|
+
agent: event.agent,
|
|
4469
|
+
path: event.data.path,
|
|
4470
|
+
delta: event.data.delta,
|
|
4471
|
+
accumulated,
|
|
4472
|
+
attempt: event.attempt
|
|
4473
|
+
});
|
|
4474
|
+
} catch (error) {
|
|
4475
|
+
if (isEventStreamOverflowError(error)) {
|
|
4476
|
+
overflowError ??= error;
|
|
4477
|
+
} else {
|
|
4478
|
+
throw error;
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4324
4482
|
} else if (event.type === "pipeline" && event.status === "failed") {
|
|
4325
4483
|
const askId = event.askId ?? "";
|
|
4326
4484
|
this.latestPartialByAsk.delete(askId);
|
|
4327
4485
|
this.stringStreamByAsk.delete(askId);
|
|
4486
|
+
for (const subscriber of this.stringStreamSubscribers) subscriber.reset(askId);
|
|
4328
4487
|
} else if (event.type === "ask_end") {
|
|
4329
4488
|
this.stringStreamByAsk.delete(event.askId ?? "");
|
|
4330
4489
|
}
|
|
@@ -4339,39 +4498,57 @@ var AxlEventBus = class {
|
|
|
4339
4498
|
const waiter = this.waiters.shift();
|
|
4340
4499
|
if (waiter) {
|
|
4341
4500
|
waiter({ value: event, done: false });
|
|
4501
|
+
if (overflowError) throw overflowError;
|
|
4342
4502
|
return;
|
|
4343
4503
|
}
|
|
4344
4504
|
if (this.eventQueue.length >= this.maxQueued && !TERMINAL_TYPES.has(event.type)) {
|
|
4345
|
-
|
|
4505
|
+
try {
|
|
4506
|
+
this.handleOverflow(event);
|
|
4507
|
+
} catch (error) {
|
|
4508
|
+
if (isEventStreamOverflowError(error)) {
|
|
4509
|
+
overflowError ??= error;
|
|
4510
|
+
} else {
|
|
4511
|
+
throw error;
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
if (overflowError) throw overflowError;
|
|
4346
4515
|
return;
|
|
4347
4516
|
}
|
|
4348
4517
|
this.eventQueue.push(event);
|
|
4518
|
+
if (overflowError) throw overflowError;
|
|
4349
4519
|
}
|
|
4350
4520
|
/** Apply the overflow policy when the queue is at `maxQueued` and a
|
|
4351
4521
|
* non-terminal event arrives. Terminal events bypass this path entirely
|
|
4352
4522
|
* (checked by the caller). */
|
|
4353
4523
|
handleOverflow(event) {
|
|
4354
4524
|
if (this.onOverflow === "throw") {
|
|
4525
|
+
this.droppedEvents++;
|
|
4355
4526
|
throw new EventStreamOverflowError(this.maxQueued, event.type);
|
|
4356
4527
|
}
|
|
4357
4528
|
let droppedIdx = -1;
|
|
4358
4529
|
for (let i = 0; i < this.eventQueue.length; i++) {
|
|
4359
|
-
|
|
4530
|
+
const candidate = this.eventQueue[i];
|
|
4531
|
+
if (!TERMINAL_TYPES.has(candidate.type)) {
|
|
4360
4532
|
droppedIdx = i;
|
|
4361
4533
|
break;
|
|
4362
4534
|
}
|
|
4363
4535
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
`[axl] AxlEventBus queue exceeded maxQueued=${this.maxQueued}` + (droppedIdx >= 0 ? `; dropping oldest non-terminal events (consumer is slower than producer).` : `; queue is all terminals (preserved past cap so no terminal is lost).`) + ` This warning fires once per bus instance.`
|
|
4368
|
-
);
|
|
4369
|
-
}
|
|
4536
|
+
this.warnOverflow(
|
|
4537
|
+
droppedIdx >= 0 ? "dropping oldest non-terminal events (consumer is slower than producer)" : "queue contains only protected terminal events; preserving them past the cap"
|
|
4538
|
+
);
|
|
4370
4539
|
if (droppedIdx >= 0) {
|
|
4371
4540
|
this.eventQueue.splice(droppedIdx, 1);
|
|
4541
|
+
this.droppedEvents++;
|
|
4372
4542
|
}
|
|
4373
4543
|
this.eventQueue.push(event);
|
|
4374
4544
|
}
|
|
4545
|
+
warnOverflow(detail) {
|
|
4546
|
+
if (this.overflowWarned) return;
|
|
4547
|
+
this.overflowWarned = true;
|
|
4548
|
+
console.warn(
|
|
4549
|
+
`[axl] AxlEventBus queue exceeded maxQueued=${this.maxQueued}; ${detail}. This warning fires once per bus instance.`
|
|
4550
|
+
);
|
|
4551
|
+
}
|
|
4375
4552
|
/**
|
|
4376
4553
|
* Mark the bus as finished. Wakes any pending iterators with `done: true`
|
|
4377
4554
|
* and drops further `_push` calls. Idempotent.
|
|
@@ -4438,6 +4615,43 @@ var AxlEventBus = class {
|
|
|
4438
4615
|
}
|
|
4439
4616
|
};
|
|
4440
4617
|
|
|
4618
|
+
// src/event-schema.ts
|
|
4619
|
+
var UnsupportedEventSchemaVersionError = class extends Error {
|
|
4620
|
+
constructor(version) {
|
|
4621
|
+
super(`Unsupported Axl event schema version: ${String(version)}`);
|
|
4622
|
+
this.version = version;
|
|
4623
|
+
this.name = "UnsupportedEventSchemaVersionError";
|
|
4624
|
+
}
|
|
4625
|
+
};
|
|
4626
|
+
function getEventSchemaVersion(event) {
|
|
4627
|
+
const version = event.schemaVersion === void 0 ? 1 : event.schemaVersion;
|
|
4628
|
+
if (version !== 1 && version !== 2) throw new UnsupportedEventSchemaVersionError(version);
|
|
4629
|
+
return version;
|
|
4630
|
+
}
|
|
4631
|
+
function getExecutionEventSchemaVersion(execution) {
|
|
4632
|
+
const version = execution.eventSchemaVersion === void 0 ? 1 : execution.eventSchemaVersion;
|
|
4633
|
+
if (version !== 1 && version !== 2) throw new UnsupportedEventSchemaVersionError(version);
|
|
4634
|
+
return version;
|
|
4635
|
+
}
|
|
4636
|
+
function normalizeStoredExecution(execution) {
|
|
4637
|
+
const version = getExecutionEventSchemaVersion(execution);
|
|
4638
|
+
if (!Array.isArray(execution.events)) {
|
|
4639
|
+
throw new TypeError(`Execution ${execution.executionId} has a non-array events field`);
|
|
4640
|
+
}
|
|
4641
|
+
for (const event of execution.events) {
|
|
4642
|
+
const eventVersion = getEventSchemaVersion(event);
|
|
4643
|
+
if (eventVersion !== version) {
|
|
4644
|
+
throw new Error(
|
|
4645
|
+
`Execution ${execution.executionId} declares event schema v${version} but contains a v${eventVersion} event`
|
|
4646
|
+
);
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
if (version === 2) {
|
|
4650
|
+
return { ...execution, eventSchemaVersion: 2 };
|
|
4651
|
+
}
|
|
4652
|
+
return { ...execution, eventSchemaVersion: 1 };
|
|
4653
|
+
}
|
|
4654
|
+
|
|
4441
4655
|
// src/redaction.ts
|
|
4442
4656
|
var REDACTED = "[redacted]";
|
|
4443
4657
|
function walkObjectOneLevel(obj, preserveKeys) {
|
|
@@ -4488,7 +4702,7 @@ function redactMemory(event) {
|
|
|
4488
4702
|
};
|
|
4489
4703
|
}
|
|
4490
4704
|
var passthrough = (e) => e;
|
|
4491
|
-
var
|
|
4705
|
+
var LEGACY_REDACTION_RULES = {
|
|
4492
4706
|
workflow_start: (e) => e.data.input !== void 0 ? { ...e, data: { ...e.data, input: REDACTED } } : e,
|
|
4493
4707
|
workflow_end: (e) => {
|
|
4494
4708
|
const d = e.data;
|
|
@@ -4588,10 +4802,125 @@ var REDACTION_RULES = {
|
|
|
4588
4802
|
done: (e) => ({ ...e, data: { result: REDACTED } }),
|
|
4589
4803
|
error: (e) => ({ ...e, data: { ...e.data, message: REDACTED } })
|
|
4590
4804
|
};
|
|
4805
|
+
function redactLegacyEvent(event) {
|
|
4806
|
+
const rule = LEGACY_REDACTION_RULES[event.type];
|
|
4807
|
+
return rule(event);
|
|
4808
|
+
}
|
|
4809
|
+
function redactV2Outcome(outcome) {
|
|
4810
|
+
switch (outcome.status) {
|
|
4811
|
+
case "succeeded":
|
|
4812
|
+
return { ...outcome, result: REDACTED };
|
|
4813
|
+
case "denied":
|
|
4814
|
+
return outcome.reason === void 0 ? outcome : { ...outcome, reason: REDACTED };
|
|
4815
|
+
case "cancelled":
|
|
4816
|
+
return {
|
|
4817
|
+
...outcome,
|
|
4818
|
+
cancellation: {
|
|
4819
|
+
...outcome.cancellation,
|
|
4820
|
+
..."result" in outcome.cancellation ? { result: REDACTED } : {},
|
|
4821
|
+
...outcome.cancellation.reason !== void 0 ? { reason: REDACTED } : {}
|
|
4822
|
+
}
|
|
4823
|
+
};
|
|
4824
|
+
case "failed": {
|
|
4825
|
+
const failure = outcome.failure;
|
|
4826
|
+
return {
|
|
4827
|
+
...outcome,
|
|
4828
|
+
failure: {
|
|
4829
|
+
...failure,
|
|
4830
|
+
..."result" in failure ? { result: REDACTED } : {},
|
|
4831
|
+
error: {
|
|
4832
|
+
...failure.error,
|
|
4833
|
+
message: REDACTED,
|
|
4834
|
+
...failure.error.cause !== void 0 ? { cause: REDACTED } : {}
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
};
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
function redactV2Rejection(data) {
|
|
4842
|
+
switch (data.reason) {
|
|
4843
|
+
case "unavailable":
|
|
4844
|
+
return data;
|
|
4845
|
+
case "invalid_json":
|
|
4846
|
+
return { ...data, message: REDACTED };
|
|
4847
|
+
case "invalid_arguments":
|
|
4848
|
+
return {
|
|
4849
|
+
...data,
|
|
4850
|
+
args: REDACTED,
|
|
4851
|
+
issues: data.issues.map((issue) => ({
|
|
4852
|
+
...issue,
|
|
4853
|
+
...issue.message !== void 0 ? { message: REDACTED } : {}
|
|
4854
|
+
}))
|
|
4855
|
+
};
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
var V2_TOOL_REDACTION_RULES = {
|
|
4859
|
+
tool_call_start: (event) => ({
|
|
4860
|
+
...event,
|
|
4861
|
+
data: { ...event.data, args: REDACTED }
|
|
4862
|
+
}),
|
|
4863
|
+
tool_call_end: (event) => ({
|
|
4864
|
+
...event,
|
|
4865
|
+
data: {
|
|
4866
|
+
...event.data,
|
|
4867
|
+
args: REDACTED,
|
|
4868
|
+
outcome: redactV2Outcome(event.data.outcome)
|
|
4869
|
+
}
|
|
4870
|
+
}),
|
|
4871
|
+
tool_call_rejected: (event) => ({
|
|
4872
|
+
...event,
|
|
4873
|
+
data: redactV2Rejection(event.data)
|
|
4874
|
+
})
|
|
4875
|
+
};
|
|
4876
|
+
function reuseLegacyRule(type) {
|
|
4877
|
+
return LEGACY_REDACTION_RULES[type];
|
|
4878
|
+
}
|
|
4879
|
+
var COMMON_V2_REDACTION_RULES = {
|
|
4880
|
+
workflow_start: reuseLegacyRule("workflow_start"),
|
|
4881
|
+
workflow_end: reuseLegacyRule("workflow_end"),
|
|
4882
|
+
ask_start: reuseLegacyRule("ask_start"),
|
|
4883
|
+
ask_end: reuseLegacyRule("ask_end"),
|
|
4884
|
+
agent_call_start: reuseLegacyRule("agent_call_start"),
|
|
4885
|
+
agent_call_end: reuseLegacyRule("agent_call_end"),
|
|
4886
|
+
token: reuseLegacyRule("token"),
|
|
4887
|
+
tool_approval: reuseLegacyRule("tool_approval"),
|
|
4888
|
+
delegate: reuseLegacyRule("delegate"),
|
|
4889
|
+
handoff_start: reuseLegacyRule("handoff_start"),
|
|
4890
|
+
handoff_return: reuseLegacyRule("handoff_return"),
|
|
4891
|
+
pipeline: reuseLegacyRule("pipeline"),
|
|
4892
|
+
partial_object: reuseLegacyRule("partial_object"),
|
|
4893
|
+
string_delta: reuseLegacyRule("string_delta"),
|
|
4894
|
+
verify: reuseLegacyRule("verify"),
|
|
4895
|
+
schema_diagnostic: reuseLegacyRule("schema_diagnostic"),
|
|
4896
|
+
log: reuseLegacyRule("log"),
|
|
4897
|
+
memory_remember: reuseLegacyRule("memory_remember"),
|
|
4898
|
+
memory_recall: reuseLegacyRule("memory_recall"),
|
|
4899
|
+
memory_forget: reuseLegacyRule("memory_forget"),
|
|
4900
|
+
checkpoint_save: reuseLegacyRule("checkpoint_save"),
|
|
4901
|
+
checkpoint_replay: reuseLegacyRule("checkpoint_replay"),
|
|
4902
|
+
await_human: reuseLegacyRule("await_human"),
|
|
4903
|
+
await_human_resolved: reuseLegacyRule("await_human_resolved"),
|
|
4904
|
+
guardrail: reuseLegacyRule("guardrail"),
|
|
4905
|
+
schema_check: reuseLegacyRule("schema_check"),
|
|
4906
|
+
validate: reuseLegacyRule("validate"),
|
|
4907
|
+
done: reuseLegacyRule("done"),
|
|
4908
|
+
error: reuseLegacyRule("error")
|
|
4909
|
+
};
|
|
4910
|
+
var REDACTION_RULES = {
|
|
4911
|
+
...COMMON_V2_REDACTION_RULES,
|
|
4912
|
+
...V2_TOOL_REDACTION_RULES
|
|
4913
|
+
};
|
|
4591
4914
|
function redactEvent(event) {
|
|
4592
4915
|
const rule = REDACTION_RULES[event.type];
|
|
4593
4916
|
return rule(event);
|
|
4594
4917
|
}
|
|
4918
|
+
function redactHistoricalEvent(event) {
|
|
4919
|
+
if (getEventSchemaVersion(event) === 1) {
|
|
4920
|
+
return redactLegacyEvent(event);
|
|
4921
|
+
}
|
|
4922
|
+
return redactEvent(event);
|
|
4923
|
+
}
|
|
4595
4924
|
|
|
4596
4925
|
// src/schema-diagnostics.ts
|
|
4597
4926
|
var DEFAULT_SCHEMA_OVERSIZED_TOKENS = 4e3;
|
|
@@ -4791,8 +5120,8 @@ function normalize(value, path, stack) {
|
|
|
4791
5120
|
"accessor properties are not supported"
|
|
4792
5121
|
);
|
|
4793
5122
|
}
|
|
4794
|
-
if (typeof key === "symbol"
|
|
4795
|
-
throw invalid(`${path}[symbol]`, "
|
|
5123
|
+
if (typeof key === "symbol") {
|
|
5124
|
+
throw invalid(`${path}[symbol]`, "symbol keys are not supported");
|
|
4796
5125
|
}
|
|
4797
5126
|
}
|
|
4798
5127
|
if (typeof descriptors.toJSON?.value === "function") {
|
|
@@ -4869,63 +5198,541 @@ function serializeToolModelOutput(toolName, output) {
|
|
|
4869
5198
|
}
|
|
4870
5199
|
}
|
|
4871
5200
|
|
|
4872
|
-
// src/
|
|
4873
|
-
var
|
|
4874
|
-
|
|
4875
|
-
|
|
5201
|
+
// src/tool-invocation.ts
|
|
5202
|
+
var INVALID_JSON_MODEL_MESSAGE = "Error: Invalid JSON in tool arguments. Please provide valid JSON.";
|
|
5203
|
+
var INVALID_ARGUMENTS_MODEL_MESSAGE = "Error: Tool arguments are invalid. Correct the arguments and try again.";
|
|
5204
|
+
var SENSITIVE_SUCCESS_MESSAGE = "[REDACTED - sensitive tool output]";
|
|
5205
|
+
var SENSITIVE_FAILURE_MESSAGE = "[REDACTED - sensitive tool failure]";
|
|
5206
|
+
var MAX_MCP_MODEL_ERROR_LENGTH = 2e3;
|
|
5207
|
+
function cloneToolArguments(value) {
|
|
5208
|
+
try {
|
|
5209
|
+
return structuredClone(value);
|
|
5210
|
+
} catch {
|
|
5211
|
+
throw new Error("Prepared tool arguments must be structured-cloneable");
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
function parseToolInvocation(options) {
|
|
5215
|
+
const { toolCall, configuredTool, override, mcpCall, mcpTraceName, availableTools } = options;
|
|
5216
|
+
const requestedTool = toolCall.function.name;
|
|
5217
|
+
if (!override && !configuredTool && !mcpCall) {
|
|
5218
|
+
return {
|
|
5219
|
+
kind: "rejected",
|
|
5220
|
+
requestedTool,
|
|
5221
|
+
toolName: requestedTool,
|
|
5222
|
+
callId: toolCall.id,
|
|
5223
|
+
data: { reason: "unavailable", requestedTool, availableTools },
|
|
5224
|
+
modelMessage: `Tool "${requestedTool}" is not available. Available tools: ${availableTools.length > 0 ? availableTools.join(", ") : "none"}`
|
|
5225
|
+
};
|
|
5226
|
+
}
|
|
5227
|
+
let parsedArguments;
|
|
5228
|
+
try {
|
|
5229
|
+
parsedArguments = JSON.parse(toolCall.function.arguments);
|
|
5230
|
+
} catch {
|
|
5231
|
+
return {
|
|
5232
|
+
kind: "rejected",
|
|
5233
|
+
requestedTool,
|
|
5234
|
+
toolName: mcpTraceName ?? requestedTool,
|
|
5235
|
+
callId: toolCall.id,
|
|
5236
|
+
data: {
|
|
5237
|
+
reason: "invalid_json",
|
|
5238
|
+
requestedTool,
|
|
5239
|
+
message: "Tool arguments were not valid JSON."
|
|
5240
|
+
},
|
|
5241
|
+
modelMessage: INVALID_JSON_MODEL_MESSAGE
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5244
|
+
if (typeof parsedArguments !== "object" || parsedArguments === null || Array.isArray(parsedArguments)) {
|
|
5245
|
+
return {
|
|
5246
|
+
kind: "rejected",
|
|
5247
|
+
requestedTool,
|
|
5248
|
+
toolName: mcpTraceName ?? requestedTool,
|
|
5249
|
+
callId: toolCall.id,
|
|
5250
|
+
data: {
|
|
5251
|
+
reason: "invalid_json",
|
|
5252
|
+
requestedTool,
|
|
5253
|
+
message: "Tool arguments must decode to a JSON object."
|
|
5254
|
+
},
|
|
5255
|
+
modelMessage: INVALID_JSON_MODEL_MESSAGE
|
|
5256
|
+
};
|
|
5257
|
+
}
|
|
5258
|
+
const source = override ? { kind: "override", execute: override } : configuredTool ? { kind: "local", tool: configuredTool } : { kind: "mcp", call: mcpCall };
|
|
5259
|
+
let preparedArguments = parsedArguments;
|
|
5260
|
+
if (source.kind === "local") {
|
|
5261
|
+
try {
|
|
5262
|
+
preparedArguments = prepareToolInput(source.tool, parsedArguments);
|
|
5263
|
+
} catch (error) {
|
|
5264
|
+
return {
|
|
5265
|
+
kind: "rejected",
|
|
5266
|
+
requestedTool,
|
|
5267
|
+
toolName: requestedTool,
|
|
5268
|
+
callId: toolCall.id,
|
|
5269
|
+
data: {
|
|
5270
|
+
reason: "invalid_arguments",
|
|
5271
|
+
requestedTool,
|
|
5272
|
+
args: parsedArguments,
|
|
5273
|
+
issues: toolArgumentIssues(error)
|
|
5274
|
+
},
|
|
5275
|
+
modelMessage: INVALID_ARGUMENTS_MODEL_MESSAGE
|
|
5276
|
+
};
|
|
5277
|
+
}
|
|
5278
|
+
}
|
|
5279
|
+
try {
|
|
5280
|
+
preparedArguments = cloneToolArguments(preparedArguments);
|
|
5281
|
+
} catch (error) {
|
|
5282
|
+
return {
|
|
5283
|
+
kind: "rejected",
|
|
5284
|
+
requestedTool,
|
|
5285
|
+
toolName: source.kind === "mcp" ? mcpTraceName ?? requestedTool : requestedTool,
|
|
5286
|
+
callId: toolCall.id,
|
|
5287
|
+
data: {
|
|
5288
|
+
reason: "invalid_arguments",
|
|
5289
|
+
requestedTool,
|
|
5290
|
+
args: parsedArguments,
|
|
5291
|
+
issues: toolArgumentIssues(error)
|
|
5292
|
+
},
|
|
5293
|
+
modelMessage: INVALID_ARGUMENTS_MODEL_MESSAGE
|
|
5294
|
+
};
|
|
5295
|
+
}
|
|
5296
|
+
return {
|
|
5297
|
+
requestedTool,
|
|
5298
|
+
toolName: source.kind === "mcp" ? mcpTraceName ?? requestedTool : requestedTool,
|
|
5299
|
+
callId: toolCall.id,
|
|
5300
|
+
args: preparedArguments,
|
|
5301
|
+
configuredTool,
|
|
5302
|
+
source
|
|
5303
|
+
};
|
|
4876
5304
|
}
|
|
4877
|
-
function
|
|
5305
|
+
function isAbortError3(error) {
|
|
4878
5306
|
try {
|
|
4879
|
-
return
|
|
5307
|
+
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
4880
5308
|
} catch {
|
|
4881
5309
|
return false;
|
|
4882
5310
|
}
|
|
4883
5311
|
}
|
|
4884
|
-
function
|
|
5312
|
+
function normalizeAbortError(reason) {
|
|
5313
|
+
if (isAbortError3(reason)) return reason;
|
|
5314
|
+
const error = new Error("The operation was aborted");
|
|
5315
|
+
error.name = "AbortError";
|
|
5316
|
+
if (reason !== void 0) {
|
|
5317
|
+
Object.defineProperty(error, "cause", { value: reason, enumerable: false });
|
|
5318
|
+
}
|
|
5319
|
+
return error;
|
|
5320
|
+
}
|
|
5321
|
+
function cancellationError(signal, error) {
|
|
5322
|
+
if (signal?.aborted) return normalizeAbortError(signal.reason);
|
|
5323
|
+
return isAbortError3(error) ? normalizeAbortError(error) : void 0;
|
|
5324
|
+
}
|
|
5325
|
+
function eventError(error) {
|
|
4885
5326
|
let isError = false;
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
5327
|
+
try {
|
|
5328
|
+
isError = error instanceof Error;
|
|
5329
|
+
} catch {
|
|
5330
|
+
}
|
|
5331
|
+
if (isError) {
|
|
5332
|
+
const read = (key) => {
|
|
5333
|
+
try {
|
|
5334
|
+
return Reflect.get(error, key);
|
|
5335
|
+
} catch {
|
|
5336
|
+
return void 0;
|
|
5337
|
+
}
|
|
5338
|
+
};
|
|
5339
|
+
const name = read("name");
|
|
5340
|
+
const message = read("message");
|
|
5341
|
+
const code = read("code");
|
|
5342
|
+
const cause = read("cause");
|
|
5343
|
+
return {
|
|
5344
|
+
name: typeof name === "string" ? name : "Error",
|
|
5345
|
+
message: typeof message === "string" ? message : "Unknown error",
|
|
5346
|
+
...typeof code === "string" ? { code } : {},
|
|
5347
|
+
...cause !== void 0 ? { cause } : {}
|
|
5348
|
+
};
|
|
5349
|
+
}
|
|
5350
|
+
try {
|
|
5351
|
+
return { name: "Error", message: String(error) };
|
|
5352
|
+
} catch {
|
|
5353
|
+
return { name: "Error", message: "Unknown thrown value" };
|
|
5354
|
+
}
|
|
5355
|
+
}
|
|
5356
|
+
function isToolFailure(error) {
|
|
5357
|
+
try {
|
|
5358
|
+
return error instanceof ToolFailure;
|
|
5359
|
+
} catch {
|
|
5360
|
+
return false;
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
function cancellation(phase, error, result) {
|
|
5364
|
+
let reason;
|
|
5365
|
+
try {
|
|
5366
|
+
reason = error instanceof Error && typeof error.message === "string" ? error.message : void 0;
|
|
5367
|
+
} catch {
|
|
5368
|
+
}
|
|
5369
|
+
const cancellation2 = phase === "after_handler" || phase === "after_hook" || phase === "projection" || phase === "serialization" ? { phase, result, ...reason ? { reason } : {} } : { phase, ...reason ? { reason } : {} };
|
|
5370
|
+
return {
|
|
5371
|
+
kind: "cancelled",
|
|
5372
|
+
cancellation: cancellation2,
|
|
5373
|
+
error: normalizeAbortError(error)
|
|
4894
5374
|
};
|
|
4895
|
-
|
|
5375
|
+
}
|
|
5376
|
+
function failed(phase, error, options = {}) {
|
|
5377
|
+
let modelMessage;
|
|
5378
|
+
if (isToolFailure(error)) {
|
|
4896
5379
|
try {
|
|
4897
|
-
|
|
5380
|
+
modelMessage = typeof error.modelMessage === "string" ? error.modelMessage : void 0;
|
|
4898
5381
|
} catch {
|
|
4899
|
-
isError = false;
|
|
4900
|
-
errorMessage = void 0;
|
|
4901
5382
|
}
|
|
4902
|
-
} else {
|
|
4903
|
-
inspect();
|
|
4904
5383
|
}
|
|
4905
|
-
|
|
4906
|
-
|
|
5384
|
+
const toolFailure = modelMessage !== void 0;
|
|
5385
|
+
const common = {
|
|
5386
|
+
phase,
|
|
5387
|
+
kind: toolFailure ? "tool_failure" : "unexpected",
|
|
5388
|
+
disposition: toolFailure ? "continue" : "abort",
|
|
5389
|
+
error: eventError(error)
|
|
5390
|
+
};
|
|
5391
|
+
const failure = phase === "handler" ? { ...common, attempts: options.attempts ?? 1 } : phase === "after_hook" ? { ...common, result: options.result } : common;
|
|
5392
|
+
return {
|
|
5393
|
+
kind: "failed",
|
|
5394
|
+
failure,
|
|
5395
|
+
error,
|
|
5396
|
+
...modelMessage !== void 0 ? { modelMessage } : {}
|
|
5397
|
+
};
|
|
5398
|
+
}
|
|
5399
|
+
function mcpContent(result) {
|
|
5400
|
+
return result.content.map((content) => content.type === "text" ? content.text : `[${content.type}]`).join("\n");
|
|
5401
|
+
}
|
|
5402
|
+
function safeMcpModelError(content) {
|
|
5403
|
+
let normalized = "";
|
|
5404
|
+
for (const character of content) {
|
|
5405
|
+
const code = character.charCodeAt(0);
|
|
5406
|
+
normalized += code <= 31 && code !== 9 && code !== 10 && code !== 13 || code === 127 ? " " : character;
|
|
5407
|
+
}
|
|
5408
|
+
return `MCP tool error: ${normalized.slice(0, MAX_MCP_MODEL_ERROR_LENGTH)}`;
|
|
4907
5409
|
}
|
|
4908
|
-
function
|
|
4909
|
-
|
|
5410
|
+
async function executeAcceptedTool(options) {
|
|
5411
|
+
const { invocation, context, signal, requestApproval, createChildContext } = options;
|
|
5412
|
+
let effectiveArgs = cloneToolArguments(invocation.args);
|
|
5413
|
+
if (invocation.source.kind === "local" && invocation.source.tool.requireApproval) {
|
|
5414
|
+
try {
|
|
5415
|
+
signal?.throwIfAborted();
|
|
5416
|
+
const approval = await requestApproval();
|
|
5417
|
+
signal?.throwIfAborted();
|
|
5418
|
+
if (!approval.approved) return { kind: "denied", reason: approval.reason };
|
|
5419
|
+
} catch (error) {
|
|
5420
|
+
rethrowEventStreamOverflow(error);
|
|
5421
|
+
const abort = cancellationError(signal, error);
|
|
5422
|
+
if (abort !== void 0) return cancellation("approval", abort);
|
|
5423
|
+
return {
|
|
5424
|
+
kind: "failed",
|
|
5425
|
+
failure: {
|
|
5426
|
+
phase: "approval",
|
|
5427
|
+
kind: "infrastructure",
|
|
5428
|
+
disposition: "abort",
|
|
5429
|
+
error: eventError(error)
|
|
5430
|
+
},
|
|
5431
|
+
error
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
if (invocation.source.kind === "local" && invocation.source.tool.hooks?.before) {
|
|
5436
|
+
try {
|
|
5437
|
+
signal?.throwIfAborted();
|
|
5438
|
+
effectiveArgs = await invocation.source.tool.hooks.before(effectiveArgs, context);
|
|
5439
|
+
signal?.throwIfAborted();
|
|
5440
|
+
} catch (error) {
|
|
5441
|
+
rethrowEventStreamOverflow(error);
|
|
5442
|
+
const abort = cancellationError(signal, error);
|
|
5443
|
+
if (abort !== void 0) return cancellation("before_hook", abort);
|
|
5444
|
+
return failed("before_hook", error);
|
|
5445
|
+
}
|
|
5446
|
+
}
|
|
5447
|
+
let result;
|
|
5448
|
+
let attempts = 1;
|
|
4910
5449
|
try {
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
5450
|
+
signal?.throwIfAborted();
|
|
5451
|
+
switch (invocation.source.kind) {
|
|
5452
|
+
case "override": {
|
|
5453
|
+
const executeOverride = invocation.source.execute;
|
|
5454
|
+
result = await executeOverride(effectiveArgs);
|
|
5455
|
+
break;
|
|
5456
|
+
}
|
|
5457
|
+
case "local":
|
|
5458
|
+
result = await executePreparedTool(
|
|
5459
|
+
invocation.source.tool,
|
|
5460
|
+
effectiveArgs,
|
|
5461
|
+
createChildContext(),
|
|
5462
|
+
{
|
|
5463
|
+
signal,
|
|
5464
|
+
onAttempt: (attempt) => attempts = attempt,
|
|
5465
|
+
checkAfterHandlerAbort: false
|
|
5466
|
+
}
|
|
5467
|
+
);
|
|
5468
|
+
break;
|
|
5469
|
+
case "mcp": {
|
|
5470
|
+
const mcpResult = await invocation.source.call(effectiveArgs);
|
|
5471
|
+
if (signal?.aborted) return cancellation("after_handler", signal.reason, mcpResult);
|
|
5472
|
+
if (mcpResult.isError) {
|
|
5473
|
+
const content = mcpContent(mcpResult);
|
|
5474
|
+
const error = new Error(content || "MCP tool returned an error");
|
|
5475
|
+
return {
|
|
5476
|
+
kind: "failed",
|
|
5477
|
+
failure: {
|
|
5478
|
+
phase: "handler",
|
|
5479
|
+
kind: "mcp_error",
|
|
5480
|
+
disposition: "continue",
|
|
5481
|
+
error: { name: "McpToolError", message: error.message, code: "MCP_TOOL_ERROR" },
|
|
5482
|
+
attempts: 1
|
|
5483
|
+
},
|
|
5484
|
+
error,
|
|
5485
|
+
modelMessage: safeMcpModelError(content)
|
|
5486
|
+
};
|
|
5487
|
+
}
|
|
5488
|
+
result = mcpResult;
|
|
5489
|
+
break;
|
|
5490
|
+
}
|
|
5491
|
+
}
|
|
5492
|
+
} catch (error) {
|
|
5493
|
+
rethrowEventStreamOverflow(error);
|
|
5494
|
+
const abort = cancellationError(signal, error);
|
|
5495
|
+
if (abort !== void 0) return cancellation("handler", abort);
|
|
5496
|
+
return failed("handler", error, { attempts });
|
|
5497
|
+
}
|
|
5498
|
+
if (signal?.aborted) return cancellation("after_handler", signal.reason, result);
|
|
5499
|
+
if (invocation.source.kind === "local" && invocation.source.tool.hooks?.after) {
|
|
5500
|
+
const rawResult = result;
|
|
5501
|
+
try {
|
|
5502
|
+
result = await invocation.source.tool.hooks.after(result, context);
|
|
5503
|
+
signal?.throwIfAborted();
|
|
5504
|
+
} catch (error) {
|
|
5505
|
+
rethrowEventStreamOverflow(error);
|
|
5506
|
+
const abort = cancellationError(signal, error);
|
|
5507
|
+
if (abort !== void 0) return cancellation("after_hook", abort, rawResult);
|
|
5508
|
+
return failed("after_hook", error, { result: rawResult });
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5511
|
+
return { kind: "succeeded", result };
|
|
5512
|
+
}
|
|
5513
|
+
function prepareToolMessage(invocation, outcome) {
|
|
5514
|
+
const sensitive = invocation.configuredTool?.sensitive === true;
|
|
5515
|
+
if (outcome.kind === "denied") {
|
|
5516
|
+
return {
|
|
5517
|
+
ok: true,
|
|
5518
|
+
content: JSON.stringify({ error: "Tool request was denied by human approval." })
|
|
5519
|
+
};
|
|
5520
|
+
}
|
|
5521
|
+
if (outcome.kind === "failed") {
|
|
5522
|
+
if (outcome.failure.disposition !== "continue" || outcome.modelMessage === void 0) {
|
|
5523
|
+
throw new Error("Aborting tool failures cannot produce provider messages");
|
|
5524
|
+
}
|
|
5525
|
+
return { ok: true, content: sensitive ? SENSITIVE_FAILURE_MESSAGE : outcome.modelMessage };
|
|
5526
|
+
}
|
|
5527
|
+
if (outcome.kind !== "succeeded") {
|
|
5528
|
+
throw new Error("Cancelled tool calls cannot produce provider messages");
|
|
5529
|
+
}
|
|
5530
|
+
if (sensitive) return { ok: true, content: SENSITIVE_SUCCESS_MESSAGE };
|
|
5531
|
+
if (invocation.source.kind === "mcp") {
|
|
5532
|
+
try {
|
|
5533
|
+
return { ok: true, content: mcpContent(outcome.result) };
|
|
5534
|
+
} catch (error) {
|
|
5535
|
+
rethrowEventStreamOverflow(error);
|
|
5536
|
+
return { ok: false, phase: "serialization", error };
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
if (invocation.configuredTool?.toModelOutput) {
|
|
5540
|
+
try {
|
|
5541
|
+
const project = invocation.configuredTool.toModelOutput;
|
|
5542
|
+
return {
|
|
5543
|
+
ok: true,
|
|
5544
|
+
content: serializeToolModelOutput(
|
|
5545
|
+
invocation.requestedTool,
|
|
5546
|
+
project(outcome.result)
|
|
5547
|
+
)
|
|
5548
|
+
};
|
|
5549
|
+
} catch (error) {
|
|
5550
|
+
rethrowEventStreamOverflow(error);
|
|
5551
|
+
if (error instanceof ToolModelOutputError && isEventStreamOverflowError(error.cause)) {
|
|
5552
|
+
throw error.cause;
|
|
5553
|
+
}
|
|
5554
|
+
return {
|
|
5555
|
+
ok: false,
|
|
5556
|
+
phase: "projection",
|
|
5557
|
+
error: error instanceof ToolModelOutputError && error.toolName === invocation.requestedTool ? error : new ToolModelOutputError(invocation.requestedTool, error)
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
try {
|
|
5562
|
+
const content = JSON.stringify(outcome.result);
|
|
5563
|
+
if (content === void 0) throw new Error("Tool result is not JSON serializable");
|
|
5564
|
+
return { ok: true, content };
|
|
5565
|
+
} catch (error) {
|
|
5566
|
+
rethrowEventStreamOverflow(error);
|
|
5567
|
+
return { ok: false, phase: "serialization", error };
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
async function settleAcceptedTool(options) {
|
|
5571
|
+
const internal = await executeAcceptedTool(options);
|
|
5572
|
+
if (internal.kind === "cancelled") {
|
|
5573
|
+
return {
|
|
5574
|
+
outcome: { status: "cancelled", cancellation: internal.cancellation },
|
|
5575
|
+
abortError: internal.error
|
|
5576
|
+
};
|
|
5577
|
+
}
|
|
5578
|
+
if (internal.kind === "failed" && internal.failure.disposition === "abort") {
|
|
5579
|
+
return {
|
|
5580
|
+
outcome: { status: "failed", failure: internal.failure },
|
|
5581
|
+
abortError: internal.error
|
|
5582
|
+
};
|
|
5583
|
+
}
|
|
5584
|
+
const result = internal.kind === "succeeded" ? internal.result : void 0;
|
|
5585
|
+
const outputPhase = internal.kind === "succeeded" && !options.invocation.configuredTool?.sensitive && options.invocation.source.kind !== "mcp" && options.invocation.configuredTool?.toModelOutput ? "projection" : "serialization";
|
|
5586
|
+
if (internal.kind === "succeeded" && options.signal?.aborted) {
|
|
5587
|
+
return {
|
|
5588
|
+
outcome: {
|
|
5589
|
+
status: "cancelled",
|
|
5590
|
+
cancellation: { phase: outputPhase, result }
|
|
5591
|
+
},
|
|
5592
|
+
abortError: normalizeAbortError(options.signal.reason)
|
|
5593
|
+
};
|
|
5594
|
+
}
|
|
5595
|
+
const prepared = prepareToolMessage(options.invocation, internal);
|
|
5596
|
+
if (internal.kind === "succeeded" && options.signal?.aborted) {
|
|
5597
|
+
return {
|
|
5598
|
+
outcome: {
|
|
5599
|
+
status: "cancelled",
|
|
5600
|
+
cancellation: { phase: outputPhase, result }
|
|
5601
|
+
},
|
|
5602
|
+
abortError: normalizeAbortError(options.signal.reason)
|
|
5603
|
+
};
|
|
5604
|
+
}
|
|
5605
|
+
if (!prepared.ok) {
|
|
5606
|
+
const outputAbort = cancellationError(options.signal, prepared.error) ?? (prepared.error instanceof ToolModelOutputError ? cancellationError(options.signal, prepared.error.cause) : void 0);
|
|
5607
|
+
if (outputAbort !== void 0) {
|
|
5608
|
+
return {
|
|
5609
|
+
outcome: {
|
|
5610
|
+
status: "cancelled",
|
|
5611
|
+
cancellation: { phase: prepared.phase, result }
|
|
5612
|
+
},
|
|
5613
|
+
abortError: outputAbort
|
|
5614
|
+
};
|
|
5615
|
+
}
|
|
5616
|
+
const failure = {
|
|
5617
|
+
phase: prepared.phase,
|
|
5618
|
+
kind: "output",
|
|
5619
|
+
disposition: "abort",
|
|
5620
|
+
error: eventError(prepared.error),
|
|
5621
|
+
result
|
|
5622
|
+
};
|
|
5623
|
+
return {
|
|
5624
|
+
outcome: { status: "failed", failure },
|
|
5625
|
+
abortError: prepared.error
|
|
5626
|
+
};
|
|
5627
|
+
}
|
|
5628
|
+
if (internal.kind === "succeeded") {
|
|
5629
|
+
return {
|
|
5630
|
+
outcome: { status: "succeeded", result: internal.result },
|
|
5631
|
+
providerContent: prepared.content
|
|
5632
|
+
};
|
|
5633
|
+
}
|
|
5634
|
+
if (internal.kind === "denied") {
|
|
5635
|
+
return {
|
|
5636
|
+
outcome: { status: "denied", ...internal.reason ? { reason: internal.reason } : {} },
|
|
5637
|
+
providerContent: prepared.content
|
|
5638
|
+
};
|
|
5639
|
+
}
|
|
5640
|
+
return {
|
|
5641
|
+
outcome: { status: "failed", failure: internal.failure },
|
|
5642
|
+
providerContent: prepared.content
|
|
5643
|
+
};
|
|
5644
|
+
}
|
|
5645
|
+
function recordToolSpanOutcome(span, outcome) {
|
|
5646
|
+
span.setAttribute("axl.tool.outcome", outcome.status);
|
|
5647
|
+
span.setAttribute("axl.tool.success", outcome.status === "succeeded");
|
|
5648
|
+
switch (outcome.status) {
|
|
5649
|
+
case "succeeded":
|
|
5650
|
+
span.setStatus("ok");
|
|
5651
|
+
return;
|
|
5652
|
+
case "denied":
|
|
5653
|
+
span.setStatus("unset");
|
|
5654
|
+
return;
|
|
5655
|
+
case "failed":
|
|
5656
|
+
span.setAttribute("axl.tool.phase", outcome.failure.phase);
|
|
5657
|
+
span.setStatus("error");
|
|
5658
|
+
return;
|
|
5659
|
+
case "cancelled":
|
|
5660
|
+
span.setAttribute("axl.tool.phase", outcome.cancellation.phase);
|
|
5661
|
+
span.setStatus("error");
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5664
|
+
|
|
5665
|
+
// src/context.ts
|
|
5666
|
+
var signalStorage = new AsyncLocalStorage();
|
|
5667
|
+
var DEFAULT_BRANCH_DRAIN_TIMEOUT_MS = 5e3;
|
|
5668
|
+
function resolveBranchDrainTimeoutMs(value) {
|
|
5669
|
+
const resolved = value === void 0 ? DEFAULT_BRANCH_DRAIN_TIMEOUT_MS : value;
|
|
5670
|
+
if (typeof resolved !== "number" || Number.isNaN(resolved) || !Number.isFinite(resolved) || resolved < 0) {
|
|
5671
|
+
throw new AxlError(
|
|
5672
|
+
"INVALID_CONFIG",
|
|
5673
|
+
`branchDrainTimeoutMs must be a non-negative finite number (got ${String(resolved)})`
|
|
5674
|
+
);
|
|
4914
5675
|
}
|
|
4915
|
-
return
|
|
5676
|
+
return resolved;
|
|
4916
5677
|
}
|
|
4917
|
-
function
|
|
4918
|
-
const
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
5678
|
+
function parseHumanDecision(value) {
|
|
5679
|
+
const invalid2 = () => {
|
|
5680
|
+
throw new AxlError(
|
|
5681
|
+
"INVALID_HUMAN_DECISION",
|
|
5682
|
+
"Human decision must be a plain object with a boolean approved field and only branch-appropriate string data"
|
|
5683
|
+
);
|
|
5684
|
+
};
|
|
5685
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) invalid2();
|
|
5686
|
+
let prototype;
|
|
5687
|
+
try {
|
|
5688
|
+
prototype = Object.getPrototypeOf(value);
|
|
5689
|
+
} catch {
|
|
5690
|
+
return invalid2();
|
|
5691
|
+
}
|
|
5692
|
+
if (prototype !== Object.prototype && prototype !== null) invalid2();
|
|
5693
|
+
let ownKeys;
|
|
5694
|
+
try {
|
|
5695
|
+
ownKeys = Reflect.ownKeys(value);
|
|
5696
|
+
} catch {
|
|
5697
|
+
return invalid2();
|
|
5698
|
+
}
|
|
5699
|
+
if (ownKeys.some(
|
|
5700
|
+
(key) => typeof key !== "string" || key !== "approved" && key !== "data" && key !== "reason"
|
|
5701
|
+
)) {
|
|
5702
|
+
invalid2();
|
|
5703
|
+
}
|
|
5704
|
+
const readDataProperty = (key) => {
|
|
5705
|
+
let descriptor;
|
|
5706
|
+
try {
|
|
5707
|
+
descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
5708
|
+
} catch {
|
|
5709
|
+
return invalid2();
|
|
5710
|
+
}
|
|
5711
|
+
if (!descriptor) {
|
|
5712
|
+
if (ownKeys.includes(key)) invalid2();
|
|
5713
|
+
return { present: false, value: void 0 };
|
|
5714
|
+
}
|
|
5715
|
+
if (!ownKeys.includes(key)) return invalid2();
|
|
5716
|
+
if (!("value" in descriptor)) return invalid2();
|
|
5717
|
+
return { present: true, value: descriptor.value };
|
|
5718
|
+
};
|
|
5719
|
+
const approved = readDataProperty("approved");
|
|
5720
|
+
const data = readDataProperty("data");
|
|
5721
|
+
const reason = readDataProperty("reason");
|
|
5722
|
+
if (approved.present && approved.value === true && !reason.present) {
|
|
5723
|
+
if (data.value === void 0 || typeof data.value === "string") {
|
|
5724
|
+
return { approved: true, ...data.value === void 0 ? {} : { data: data.value } };
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
if (approved.present && approved.value === false && !data.present) {
|
|
5728
|
+
if (reason.value === void 0 || typeof reason.value === "string") {
|
|
5729
|
+
return {
|
|
5730
|
+
approved: false,
|
|
5731
|
+
...reason.value === void 0 ? {} : { reason: reason.value }
|
|
5732
|
+
};
|
|
5733
|
+
}
|
|
4925
5734
|
}
|
|
4926
|
-
|
|
4927
|
-
emitEnd();
|
|
4928
|
-
return content;
|
|
5735
|
+
return invalid2();
|
|
4929
5736
|
}
|
|
4930
5737
|
var askStorage = new AsyncLocalStorage();
|
|
4931
5738
|
var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -5060,9 +5867,8 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5060
5867
|
providerRegistry;
|
|
5061
5868
|
sessionHistory;
|
|
5062
5869
|
onTrace;
|
|
5063
|
-
onToken;
|
|
5064
|
-
onToolCall;
|
|
5065
5870
|
pendingDecisions;
|
|
5871
|
+
awaitHumanClaims;
|
|
5066
5872
|
budgetContext;
|
|
5067
5873
|
stateStore;
|
|
5068
5874
|
/** Root step counter for this execution. Inherited by every ctx.ask()
|
|
@@ -5083,27 +5889,27 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5083
5889
|
* the caller-supplied key.
|
|
5084
5890
|
*/
|
|
5085
5891
|
autoCheckpointCounters;
|
|
5086
|
-
|
|
5892
|
+
branchDrainState;
|
|
5893
|
+
/** Root-shared idempotency guards for workflow lifecycle emission.
|
|
5087
5894
|
* Both `runtime.execute()` and `runtime.stream()` have paths where a
|
|
5088
5895
|
* post-emit side-effect (checkpoint deletion, state-store persistence)
|
|
5089
5896
|
* can throw AFTER `_emitWorkflowEnd({status: 'completed'})` has already
|
|
5090
5897
|
* fired — the outer catch would then fire `_emitWorkflowEnd({status:
|
|
5091
5898
|
* 'failed'})` for a second time. These flags make both emitters
|
|
5092
5899
|
* single-fire so consumers never see paired-then-conflicting terminal
|
|
5093
|
-
* events for one execution.
|
|
5094
|
-
|
|
5095
|
-
|
|
5900
|
+
* events for one execution. Sharing the state with child contexts also
|
|
5901
|
+
* suppresses late branch events after bounded finalization. */
|
|
5902
|
+
workflowLifecycleState;
|
|
5096
5903
|
signal;
|
|
5097
5904
|
summaryCache;
|
|
5098
5905
|
workflowName;
|
|
5099
5906
|
mcpManager;
|
|
5100
5907
|
spanManager;
|
|
5101
5908
|
memoryManager;
|
|
5102
|
-
resumeMode;
|
|
5103
5909
|
toolOverrides;
|
|
5104
5910
|
awaitHumanHandler;
|
|
5105
|
-
onAgentStart;
|
|
5106
5911
|
onAgentCallComplete;
|
|
5912
|
+
_onDecisionCleanupFailed;
|
|
5107
5913
|
/** Shared mutable slot for the lazy `AxlEventBus`. The slot is shared
|
|
5108
5914
|
* by reference across parent/child contexts so a late allocation
|
|
5109
5915
|
* (consumer subscribes after a child context exists) is visible
|
|
@@ -5112,6 +5918,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5112
5918
|
* contexts that never observe). */
|
|
5113
5919
|
_busRef;
|
|
5114
5920
|
_eventStreamOptions;
|
|
5921
|
+
_forceStreaming;
|
|
5115
5922
|
/** Removes the constructor-registered `'abort'` listener from
|
|
5116
5923
|
* `this.signal` so a long-lived signal (e.g., a request-scoped or
|
|
5117
5924
|
* process-wide signal reused across many `runtime.createContext()` /
|
|
@@ -5131,8 +5938,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5131
5938
|
* *between* asks. `ctx.events.partialObjects` is the coalescing view
|
|
5132
5939
|
* designed for streaming-structured-output UIs.
|
|
5133
5940
|
* - **Ad-hoc contexts** from `runtime.createContext()`: the same
|
|
5134
|
-
* iterable
|
|
5135
|
-
* `onAgentStart` callbacks.
|
|
5941
|
+
* iterable provides typed per-context observation.
|
|
5136
5942
|
* - **Cross-execution**: prefer `runtime.on('trace', …)` instead — that
|
|
5137
5943
|
* fans out across all executions, while `ctx.events` is per-context.
|
|
5138
5944
|
*
|
|
@@ -5147,9 +5953,9 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5147
5953
|
*
|
|
5148
5954
|
* **Subscribe before the first `ctx.ask()`.** The streaming code
|
|
5149
5955
|
* path inside `ctx.ask()` activates only when an observer is
|
|
5150
|
-
* present at the time the ask starts (`_streamingEnabled` —
|
|
5151
|
-
*
|
|
5152
|
-
*
|
|
5956
|
+
* present at the time the ask starts (`_streamingEnabled` — an explicit
|
|
5957
|
+
* runtime streaming mode or allocated `ctx.events`). If you allocate
|
|
5958
|
+
* `ctx.events` AFTER a `ctx.ask()` has
|
|
5153
5959
|
* begun, that in-flight ask will not stream `token` /
|
|
5154
5960
|
* `partial_object` events at all (the agent loop went through
|
|
5155
5961
|
* `provider.chat` instead of `provider.stream`). Subsequent asks
|
|
@@ -5188,18 +5994,16 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5188
5994
|
this._busRef.current?._finish();
|
|
5189
5995
|
this.abortListenerCleanup?.();
|
|
5190
5996
|
}
|
|
5191
|
-
/** True
|
|
5192
|
-
*
|
|
5193
|
-
*
|
|
5194
|
-
* been allocated (a workflow-handler consumer subscribed before
|
|
5195
|
-
* the ask). `ctx.ask` reads this to decide whether to enter the
|
|
5997
|
+
/** True when asks should use provider streaming: `runtime.stream()` set an
|
|
5998
|
+
* explicit internal mode or `ctx.events` has been allocated. `ctx.ask`
|
|
5999
|
+
* reads this to decide whether to enter the
|
|
5196
6000
|
* streaming code path; the gate is re-checked per ask, so a
|
|
5197
6001
|
* consumer subscribing AFTER the first ask started still gets
|
|
5198
6002
|
* streaming on the next one. The check `_busRef.current !== undefined`
|
|
5199
6003
|
* is a one-way flag — `_finish` does not unset the reference, so
|
|
5200
6004
|
* once an observer was present, every subsequent ask streams. */
|
|
5201
6005
|
get _streamingEnabled() {
|
|
5202
|
-
return this.
|
|
6006
|
+
return this._forceStreaming || this._busRef.current !== void 0;
|
|
5203
6007
|
}
|
|
5204
6008
|
constructor(init) {
|
|
5205
6009
|
this.input = init.input;
|
|
@@ -5209,9 +6013,8 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5209
6013
|
this.providerRegistry = init.providerRegistry;
|
|
5210
6014
|
this.sessionHistory = init.sessionHistory ?? [];
|
|
5211
6015
|
this.onTrace = init.onTrace;
|
|
5212
|
-
this.onToken = init.onToken;
|
|
5213
|
-
this.onToolCall = init.onToolCall;
|
|
5214
6016
|
this.pendingDecisions = init.pendingDecisions;
|
|
6017
|
+
this.awaitHumanClaims = init.awaitHumanClaims ?? /* @__PURE__ */ new Map();
|
|
5215
6018
|
this.budgetContext = init.budgetContext;
|
|
5216
6019
|
this.stateStore = init.stateStore;
|
|
5217
6020
|
this.signal = init.signal;
|
|
@@ -5219,13 +6022,13 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5219
6022
|
this.mcpManager = init.mcpManager;
|
|
5220
6023
|
this.spanManager = init.spanManager;
|
|
5221
6024
|
this.memoryManager = init.memoryManager;
|
|
5222
|
-
this.resumeMode = init.resumeMode ?? false;
|
|
5223
6025
|
this.toolOverrides = init.toolOverrides;
|
|
5224
6026
|
this.awaitHumanHandler = init.awaitHumanHandler;
|
|
5225
|
-
this.onAgentStart = init.onAgentStart;
|
|
5226
6027
|
this.onAgentCallComplete = init.onAgentCallComplete;
|
|
6028
|
+
this._onDecisionCleanupFailed = init._onDecisionCleanupFailed;
|
|
5227
6029
|
this._busRef = init._busRef ?? { current: void 0 };
|
|
5228
6030
|
this._eventStreamOptions = init.eventStreamOptions;
|
|
6031
|
+
this._forceStreaming = init._forceStreaming ?? false;
|
|
5229
6032
|
if (init._busRef === void 0 && this.signal && !this.signal.aborted) {
|
|
5230
6033
|
const signal = this.signal;
|
|
5231
6034
|
const onAbort = () => {
|
|
@@ -5241,6 +6044,15 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5241
6044
|
byAgent: /* @__PURE__ */ new Map(),
|
|
5242
6045
|
root: 0
|
|
5243
6046
|
};
|
|
6047
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(init.branchDrainTimeoutMs);
|
|
6048
|
+
this.branchDrainState = init.branchDrainState ?? {
|
|
6049
|
+
pending: /* @__PURE__ */ new Set(),
|
|
6050
|
+
timeoutMs: branchDrainTimeoutMs
|
|
6051
|
+
};
|
|
6052
|
+
this.workflowLifecycleState = init.workflowLifecycleState ?? {
|
|
6053
|
+
startEmitted: false,
|
|
6054
|
+
endEmitted: false
|
|
6055
|
+
};
|
|
5244
6056
|
if (init.metadata?.summaryCache) {
|
|
5245
6057
|
this.summaryCache = init.metadata.summaryCache;
|
|
5246
6058
|
}
|
|
@@ -5249,16 +6061,9 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5249
6061
|
* Create a child context for nested agent invocations (e.g., agent-as-tool).
|
|
5250
6062
|
* Shares: budget tracking, abort signals, trace emission, provider registry,
|
|
5251
6063
|
* state store, span manager, memory manager, MCP manager, config,
|
|
5252
|
-
* awaitHuman handler, pending decisions, tool overrides,
|
|
5253
|
-
* streaming callbacks (onToken / onAgentStart / onToolCall).
|
|
6064
|
+
* awaitHuman handler, pending decisions, tool overrides, and event bus.
|
|
5254
6065
|
* Isolates: session history.
|
|
5255
6066
|
*
|
|
5256
|
-
* Streaming callbacks now propagate into nested asks because every callback
|
|
5257
|
-
* invocation carries `meta.askId`/`meta.parentAskId`/`meta.depth` so
|
|
5258
|
-
* consumers that want root-only behavior can filter on `meta.depth === 0`
|
|
5259
|
-
* instead of relying on the runtime to drop nested events. This is the
|
|
5260
|
-
* "nested ask visibility" fix from spec §3.2.
|
|
5261
|
-
*
|
|
5262
6067
|
* The shared step counter lives in `askStorage` (ALS), so there is no
|
|
5263
6068
|
* per-context counter to pass through anymore. Nested events are
|
|
5264
6069
|
* correlated to their parent via `parentAskId` (set on every nested ask
|
|
@@ -5279,12 +6084,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5279
6084
|
spanManager: this.spanManager,
|
|
5280
6085
|
memoryManager: this.memoryManager,
|
|
5281
6086
|
onTrace: this.onTrace,
|
|
5282
|
-
onToken: this.onToken,
|
|
5283
|
-
onToolCall: this.onToolCall,
|
|
5284
|
-
onAgentStart: this.onAgentStart,
|
|
5285
6087
|
onAgentCallComplete: this.onAgentCallComplete,
|
|
6088
|
+
_onDecisionCleanupFailed: this._onDecisionCleanupFailed,
|
|
5286
6089
|
awaitHumanHandler: this.awaitHumanHandler,
|
|
5287
6090
|
pendingDecisions: this.pendingDecisions,
|
|
6091
|
+
awaitHumanClaims: this.awaitHumanClaims,
|
|
5288
6092
|
toolOverrides: this.toolOverrides,
|
|
5289
6093
|
signal: this.signal,
|
|
5290
6094
|
workflowName: this.workflowName,
|
|
@@ -5292,12 +6096,15 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5292
6096
|
// emitted from `ctx.ask` / spawn / race / parallel / map in this
|
|
5293
6097
|
// child context don't collide with the parent's in the state store.
|
|
5294
6098
|
autoCheckpointCounters: this.autoCheckpointCounters,
|
|
6099
|
+
branchDrainState: this.branchDrainState,
|
|
6100
|
+
workflowLifecycleState: this.workflowLifecycleState,
|
|
5295
6101
|
// Share the parent's bus slot (mutable ref). Late allocation in
|
|
5296
6102
|
// either parent or child propagates because both read the same
|
|
5297
6103
|
// slot. Nested-ask events (agent-as-tool pattern) bubble up to
|
|
5298
6104
|
// the parent's iterator with `parentAskId`/`depth` correlation
|
|
5299
6105
|
// intact.
|
|
5300
6106
|
_busRef: this._busRef,
|
|
6107
|
+
_forceStreaming: this._forceStreaming,
|
|
5301
6108
|
eventStreamOptions: this._eventStreamOptions
|
|
5302
6109
|
// Isolated: sessionHistory (empty)
|
|
5303
6110
|
});
|
|
@@ -5310,28 +6117,194 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5310
6117
|
get currentSignal() {
|
|
5311
6118
|
return signalStorage.getStore() ?? this.signal;
|
|
5312
6119
|
}
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
6120
|
+
_registerBranchDrain(promise) {
|
|
6121
|
+
const state = this.branchDrainState;
|
|
6122
|
+
state.pending.add(promise);
|
|
6123
|
+
void promise.then(
|
|
6124
|
+
() => state.pending.delete(promise),
|
|
6125
|
+
(error) => {
|
|
6126
|
+
state.pending.delete(promise);
|
|
6127
|
+
if (!state.overflow && isEventStreamOverflowError(error)) {
|
|
6128
|
+
state.overflow = error;
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6131
|
+
);
|
|
6132
|
+
}
|
|
6133
|
+
/** Wait for race/quorum losers (including nested branch work) to settle.
|
|
6134
|
+
* Primitive calls still resolve as soon as their winner/quorum is known;
|
|
6135
|
+
* this is only the runtime's terminal/persistence barrier. */
|
|
6136
|
+
async _drainBranchWork() {
|
|
6137
|
+
const state = this.branchDrainState;
|
|
6138
|
+
state.deadline ??= Date.now() + state.timeoutMs;
|
|
6139
|
+
while (state.pending.size > 0) {
|
|
6140
|
+
const overflow2 = state.overflow;
|
|
6141
|
+
state.overflow = void 0;
|
|
6142
|
+
if (overflow2) throw overflow2;
|
|
6143
|
+
const remaining = state.deadline - Date.now();
|
|
6144
|
+
if (remaining <= 0) {
|
|
6145
|
+
return {
|
|
6146
|
+
complete: false,
|
|
6147
|
+
reason: "branch_drain_timeout",
|
|
6148
|
+
pendingContinuations: state.pending.size,
|
|
6149
|
+
timeoutMs: state.timeoutMs
|
|
6150
|
+
};
|
|
6151
|
+
}
|
|
6152
|
+
let timer;
|
|
6153
|
+
const settled = await Promise.race([
|
|
6154
|
+
Promise.allSettled([...state.pending]).then(() => true),
|
|
6155
|
+
new Promise((resolve2) => {
|
|
6156
|
+
timer = setTimeout(() => resolve2(false), remaining);
|
|
6157
|
+
})
|
|
6158
|
+
]);
|
|
6159
|
+
if (timer) clearTimeout(timer);
|
|
6160
|
+
if (!settled) {
|
|
6161
|
+
return {
|
|
6162
|
+
complete: false,
|
|
6163
|
+
reason: "branch_drain_timeout",
|
|
6164
|
+
pendingContinuations: state.pending.size,
|
|
6165
|
+
timeoutMs: state.timeoutMs
|
|
6166
|
+
};
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
const overflow = state.overflow;
|
|
6170
|
+
state.overflow = void 0;
|
|
6171
|
+
if (overflow) throw overflow;
|
|
6172
|
+
state.deadline = void 0;
|
|
6173
|
+
return { complete: true };
|
|
6174
|
+
}
|
|
6175
|
+
/** Resolve, execute, and observe one non-handoff tool request. Rejections
|
|
6176
|
+
* never start. Every accepted request emits one terminal event before a
|
|
6177
|
+
* continuation message or abort escapes this coordinator. */
|
|
6178
|
+
async observeToolInvocation(agent2, toolCall, currentMessages, availableTools) {
|
|
6179
|
+
const signal = this.currentSignal;
|
|
6180
|
+
signal?.throwIfAborted();
|
|
6181
|
+
const requestedTool = toolCall.function.name;
|
|
6182
|
+
const configuredTool = agent2._config.tools?.find(
|
|
6183
|
+
(candidate) => candidate.name === requestedTool
|
|
6184
|
+
);
|
|
6185
|
+
const override = this.toolOverrides?.get(requestedTool);
|
|
6186
|
+
const mcpManager = this.mcpManager;
|
|
6187
|
+
const mcpMatch = !configuredTool ? mcpManager?.getToolsForAgent(agent2._config.mcp, agent2._config.mcpTools).find(({ tool: tool2 }) => tool2.name === requestedTool) : void 0;
|
|
6188
|
+
const parsed = parseToolInvocation({
|
|
6189
|
+
toolCall,
|
|
6190
|
+
configuredTool,
|
|
6191
|
+
override,
|
|
6192
|
+
...mcpMatch && mcpManager ? {
|
|
6193
|
+
mcpCall: (args) => mcpManager.callTool(requestedTool, args, mcpMatch.server),
|
|
6194
|
+
mcpTraceName: `${mcpMatch.server}:${requestedTool}`
|
|
6195
|
+
} : {},
|
|
6196
|
+
availableTools
|
|
6197
|
+
});
|
|
6198
|
+
signal?.throwIfAborted();
|
|
6199
|
+
if ("kind" in parsed) {
|
|
6200
|
+
this.emitEvent({
|
|
6201
|
+
type: "tool_call_rejected",
|
|
6202
|
+
agent: agent2._name,
|
|
6203
|
+
tool: parsed.toolName,
|
|
6204
|
+
callId: parsed.callId,
|
|
6205
|
+
data: parsed.data
|
|
6206
|
+
});
|
|
6207
|
+
currentMessages.push({
|
|
6208
|
+
role: "tool",
|
|
6209
|
+
content: parsed.modelMessage,
|
|
6210
|
+
tool_call_id: parsed.callId
|
|
6211
|
+
});
|
|
6212
|
+
return;
|
|
6213
|
+
}
|
|
6214
|
+
const startArgs = cloneToolArguments(parsed.args);
|
|
6215
|
+
this.emitEvent({
|
|
6216
|
+
type: "tool_call_start",
|
|
6217
|
+
agent: agent2._name,
|
|
6218
|
+
tool: parsed.toolName,
|
|
6219
|
+
callId: parsed.callId,
|
|
6220
|
+
data: {
|
|
6221
|
+
args: startArgs,
|
|
6222
|
+
...parsed.requestedTool !== parsed.toolName ? { requestedTool: parsed.requestedTool } : {}
|
|
6223
|
+
}
|
|
6224
|
+
});
|
|
6225
|
+
const startedAt = Date.now();
|
|
6226
|
+
const requestApproval = async () => {
|
|
6227
|
+
const approve = async () => {
|
|
6228
|
+
const approvalArgs = cloneToolArguments(parsed.args);
|
|
6229
|
+
const decision = await this.awaitHuman({
|
|
6230
|
+
channel: "tool_approval",
|
|
6231
|
+
prompt: `Tool "${parsed.toolName}" wants to execute with args: ${JSON.stringify(parsed.args)}`,
|
|
6232
|
+
metadata: { toolName: parsed.toolName, args: approvalArgs, agent: agent2._name }
|
|
6233
|
+
});
|
|
6234
|
+
return decision.approved ? { approved: true } : { approved: false, reason: decision.reason ?? "Denied by human" };
|
|
5332
6235
|
};
|
|
6236
|
+
const approval = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6237
|
+
"axl.tool.approval",
|
|
6238
|
+
{ "axl.tool.name": parsed.toolName, "axl.agent.name": agent2._name },
|
|
6239
|
+
async (span) => {
|
|
6240
|
+
const value = await approve();
|
|
6241
|
+
span.setAttribute("axl.tool.approval.approved", value.approved);
|
|
6242
|
+
return value;
|
|
6243
|
+
}
|
|
6244
|
+
) : await approve();
|
|
6245
|
+
this.emitEvent({
|
|
6246
|
+
type: "tool_approval",
|
|
6247
|
+
agent: agent2._name,
|
|
6248
|
+
tool: parsed.toolName,
|
|
6249
|
+
callId: parsed.callId,
|
|
6250
|
+
data: {
|
|
6251
|
+
approved: approval.approved,
|
|
6252
|
+
args: cloneToolArguments(parsed.args),
|
|
6253
|
+
...approval.reason ? { reason: approval.reason } : {}
|
|
6254
|
+
}
|
|
6255
|
+
});
|
|
6256
|
+
return approval;
|
|
6257
|
+
};
|
|
6258
|
+
let terminalEmitted = false;
|
|
6259
|
+
const executeAndObserve = async (span) => {
|
|
6260
|
+
const settlement2 = await settleAcceptedTool({
|
|
6261
|
+
invocation: parsed,
|
|
6262
|
+
context: this,
|
|
6263
|
+
signal,
|
|
6264
|
+
requestApproval,
|
|
6265
|
+
createChildContext: () => this.createChildContext()
|
|
6266
|
+
});
|
|
6267
|
+
const duration = Date.now() - startedAt;
|
|
6268
|
+
span?.setAttribute("axl.tool.duration", duration);
|
|
6269
|
+
if (span) recordToolSpanOutcome(span, settlement2.outcome);
|
|
6270
|
+
if (terminalEmitted) {
|
|
6271
|
+
throw new Error(`Tool invocation ${parsed.callId} emitted more than one terminal event`);
|
|
6272
|
+
}
|
|
6273
|
+
terminalEmitted = true;
|
|
6274
|
+
try {
|
|
6275
|
+
this.emitEvent({
|
|
6276
|
+
type: "tool_call_end",
|
|
6277
|
+
agent: agent2._name,
|
|
6278
|
+
tool: parsed.toolName,
|
|
6279
|
+
callId: parsed.callId,
|
|
6280
|
+
duration,
|
|
6281
|
+
data: {
|
|
6282
|
+
args: cloneToolArguments(parsed.args),
|
|
6283
|
+
...parsed.requestedTool !== parsed.toolName ? { requestedTool: parsed.requestedTool } : {},
|
|
6284
|
+
outcome: settlement2.outcome
|
|
6285
|
+
}
|
|
6286
|
+
});
|
|
6287
|
+
} catch (error) {
|
|
6288
|
+
if (error instanceof Error && settlement2.abortError !== void 0) {
|
|
6289
|
+
throw preserveErrorCause(error, settlement2.abortError);
|
|
6290
|
+
}
|
|
6291
|
+
throw error;
|
|
6292
|
+
}
|
|
6293
|
+
return settlement2;
|
|
6294
|
+
};
|
|
6295
|
+
const settlement = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6296
|
+
"axl.tool.call",
|
|
6297
|
+
{ "axl.tool.name": parsed.toolName, "axl.agent.name": agent2._name },
|
|
6298
|
+
executeAndObserve
|
|
6299
|
+
) : await executeAndObserve();
|
|
6300
|
+
if (settlement.providerContent !== void 0) {
|
|
6301
|
+
currentMessages.push({
|
|
6302
|
+
role: "tool",
|
|
6303
|
+
content: settlement.providerContent,
|
|
6304
|
+
tool_call_id: parsed.callId
|
|
6305
|
+
});
|
|
5333
6306
|
}
|
|
5334
|
-
|
|
6307
|
+
if (settlement.abortError !== void 0) throw settlement.abortError;
|
|
5335
6308
|
}
|
|
5336
6309
|
// ── ctx.ask() ─────────────────────────────────────────────────────────
|
|
5337
6310
|
async ask(agent2, prompt, options) {
|
|
@@ -5360,18 +6333,21 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5360
6333
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
5361
6334
|
const usageCapture = {};
|
|
5362
6335
|
const doCall = async () => {
|
|
5363
|
-
const
|
|
6336
|
+
const result2 = await this.executeAgentCall(
|
|
5364
6337
|
agent2,
|
|
5365
6338
|
prompt,
|
|
5366
6339
|
options,
|
|
5367
6340
|
void 0,
|
|
5368
6341
|
usageCapture
|
|
5369
6342
|
);
|
|
5370
|
-
return
|
|
6343
|
+
return result2;
|
|
5371
6344
|
};
|
|
5372
6345
|
let outcome;
|
|
6346
|
+
let askFailure;
|
|
6347
|
+
let askFailed = false;
|
|
6348
|
+
let result;
|
|
5373
6349
|
try {
|
|
5374
|
-
|
|
6350
|
+
result = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
5375
6351
|
"axl.agent.ask",
|
|
5376
6352
|
{
|
|
5377
6353
|
"axl.agent.name": agent2._name,
|
|
@@ -5430,36 +6406,34 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5430
6406
|
);
|
|
5431
6407
|
}
|
|
5432
6408
|
}
|
|
5433
|
-
return result;
|
|
5434
6409
|
} catch (err) {
|
|
6410
|
+
askFailed = true;
|
|
6411
|
+
askFailure = err;
|
|
5435
6412
|
outcome = {
|
|
5436
6413
|
ok: false,
|
|
5437
6414
|
error: err instanceof Error ? err.message : String(err)
|
|
5438
6415
|
};
|
|
5439
|
-
throw err;
|
|
5440
|
-
} finally {
|
|
5441
|
-
try {
|
|
5442
|
-
this.emitEvent({
|
|
5443
|
-
type: "ask_end",
|
|
5444
|
-
outcome: outcome ?? {
|
|
5445
|
-
ok: false,
|
|
5446
|
-
error: "ask_end emitted without outcome \u2014 internal bug"
|
|
5447
|
-
},
|
|
5448
|
-
cost: frame.askCost.value,
|
|
5449
|
-
...frame.askUnpriced ? { unpriced: true } : {},
|
|
5450
|
-
duration: Date.now() - askStart
|
|
5451
|
-
});
|
|
5452
|
-
} catch (emitErr) {
|
|
5453
|
-
if (emitErr instanceof EventStreamOverflowError && outcome && !outcome.ok) {
|
|
5454
|
-
console.error(
|
|
5455
|
-
"[axl] ask_end emit overflowed during error path; preserving original error:",
|
|
5456
|
-
emitErr.message
|
|
5457
|
-
);
|
|
5458
|
-
} else {
|
|
5459
|
-
throw emitErr;
|
|
5460
|
-
}
|
|
5461
|
-
}
|
|
5462
6416
|
}
|
|
6417
|
+
let terminalFailure;
|
|
6418
|
+
let terminalFailed = false;
|
|
6419
|
+
try {
|
|
6420
|
+
this.emitEvent({
|
|
6421
|
+
type: "ask_end",
|
|
6422
|
+
outcome: outcome ?? {
|
|
6423
|
+
ok: false,
|
|
6424
|
+
error: "ask_end emitted without outcome \u2014 internal bug"
|
|
6425
|
+
},
|
|
6426
|
+
cost: frame.askCost.value,
|
|
6427
|
+
...frame.askUnpriced ? { unpriced: true } : {},
|
|
6428
|
+
duration: Date.now() - askStart
|
|
6429
|
+
});
|
|
6430
|
+
} catch (error) {
|
|
6431
|
+
terminalFailed = true;
|
|
6432
|
+
terminalFailure = isEventStreamOverflowError(error) ? preserveErrorCause(error, askFailure) : error;
|
|
6433
|
+
}
|
|
6434
|
+
if (terminalFailed) throw terminalFailure;
|
|
6435
|
+
if (askFailed) throw askFailure;
|
|
6436
|
+
return result;
|
|
5463
6437
|
});
|
|
5464
6438
|
},
|
|
5465
6439
|
{ agent: agentName }
|
|
@@ -5681,7 +6655,6 @@ ${appendedSchemaText}`;
|
|
|
5681
6655
|
}
|
|
5682
6656
|
} : { type: "json_object" };
|
|
5683
6657
|
}
|
|
5684
|
-
const callbackMeta = this.currentCallbackMeta(agent2._name);
|
|
5685
6658
|
const callParams = {
|
|
5686
6659
|
...chatOptions.temperature !== void 0 ? { temperature: chatOptions.temperature } : {},
|
|
5687
6660
|
maxTokens: chatOptions.maxTokens,
|
|
@@ -5720,7 +6693,6 @@ ${appendedSchemaText}`;
|
|
|
5720
6693
|
...messagesSnapshot ? { messages: messagesSnapshot } : {}
|
|
5721
6694
|
}
|
|
5722
6695
|
});
|
|
5723
|
-
this.onAgentStart?.({ agent: agent2._name, model: modelUri }, callbackMeta);
|
|
5724
6696
|
let response;
|
|
5725
6697
|
try {
|
|
5726
6698
|
this.currentSignal?.throwIfAborted();
|
|
@@ -5746,7 +6718,6 @@ ${appendedSchemaText}`;
|
|
|
5746
6718
|
if (chunk.type === "text_delta") {
|
|
5747
6719
|
content2 += chunk.content;
|
|
5748
6720
|
this.emitEvent({ type: "token", data: chunk.content });
|
|
5749
|
-
this.onToken?.(chunk.content, callbackMeta);
|
|
5750
6721
|
if (walker) {
|
|
5751
6722
|
walker.processChunk(chunk.content);
|
|
5752
6723
|
if (walker.consumeBoundary()) {
|
|
@@ -5856,6 +6827,7 @@ ${appendedSchemaText}`;
|
|
|
5856
6827
|
...retryReason ? { retryReason } : {}
|
|
5857
6828
|
}
|
|
5858
6829
|
});
|
|
6830
|
+
this.currentSignal?.throwIfAborted();
|
|
5859
6831
|
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
5860
6832
|
currentMessages.push({
|
|
5861
6833
|
role: "assistant",
|
|
@@ -6025,263 +6997,12 @@ ${appendedSchemaText}`;
|
|
|
6025
6997
|
return await handoffFn();
|
|
6026
6998
|
}
|
|
6027
6999
|
}
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
toolArgs2 = JSON.parse(toolCall.function.arguments);
|
|
6035
|
-
} catch {
|
|
6036
|
-
currentMessages.push({
|
|
6037
|
-
role: "tool",
|
|
6038
|
-
content: `Error: Invalid JSON in tool arguments. Please provide valid JSON.`,
|
|
6039
|
-
tool_call_id: toolCall.id
|
|
6040
|
-
});
|
|
6041
|
-
continue;
|
|
6042
|
-
}
|
|
6043
|
-
this.emitEvent({
|
|
6044
|
-
type: "tool_call_start",
|
|
6045
|
-
tool: toolName,
|
|
6046
|
-
callId: toolCall.id,
|
|
6047
|
-
data: { args: toolArgs2 }
|
|
6048
|
-
});
|
|
6049
|
-
this.onToolCall?.(
|
|
6050
|
-
{ name: toolName, args: toolArgs2, callId: toolCall.id },
|
|
6051
|
-
callbackMeta
|
|
6052
|
-
);
|
|
6053
|
-
const toolStart2 = Date.now();
|
|
6054
|
-
const suppressErrorShapeInspection2 = tool2?.sensitive === true || tool2?.toModelOutput !== void 0;
|
|
6055
|
-
const executeOverride = async () => {
|
|
6056
|
-
try {
|
|
6057
|
-
return { kind: "returned", value: await toolOverride(toolArgs2) };
|
|
6058
|
-
} catch (err) {
|
|
6059
|
-
return {
|
|
6060
|
-
kind: "thrown",
|
|
6061
|
-
value: { error: err instanceof Error ? err.message : String(err) }
|
|
6062
|
-
};
|
|
6063
|
-
}
|
|
6064
|
-
};
|
|
6065
|
-
const outcome = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6066
|
-
"axl.tool.call",
|
|
6067
|
-
{
|
|
6068
|
-
"axl.tool.name": toolName,
|
|
6069
|
-
"axl.agent.name": agent2._name
|
|
6070
|
-
},
|
|
6071
|
-
async (span) => {
|
|
6072
|
-
const result = await executeOverride();
|
|
6073
|
-
span.setAttribute("axl.tool.duration", Date.now() - toolStart2);
|
|
6074
|
-
recordToolSpanResult(span, result.value, suppressErrorShapeInspection2);
|
|
6075
|
-
return result;
|
|
6076
|
-
}
|
|
6077
|
-
) : await executeOverride();
|
|
6078
|
-
const toolResult2 = outcome.value;
|
|
6079
|
-
const resultContent2 = finalizeToolResult({
|
|
6080
|
-
toolName,
|
|
6081
|
-
configuredTool: tool2,
|
|
6082
|
-
outcome,
|
|
6083
|
-
legacyContent: () => tool2?.sensitive ? "[REDACTED - sensitive tool output]" : JSON.stringify(toolResult2),
|
|
6084
|
-
emitEnd: () => {
|
|
6085
|
-
const toolDuration2 = Date.now() - toolStart2;
|
|
6086
|
-
this.emitEvent({
|
|
6087
|
-
type: "tool_call_end",
|
|
6088
|
-
agent: agent2._name,
|
|
6089
|
-
tool: toolName,
|
|
6090
|
-
duration: toolDuration2,
|
|
6091
|
-
data: { args: toolArgs2, result: toolResult2, callId: toolCall.id }
|
|
6092
|
-
});
|
|
6093
|
-
},
|
|
6094
|
-
beforeProjection: () => this.currentSignal?.throwIfAborted()
|
|
6095
|
-
});
|
|
6096
|
-
currentMessages.push({
|
|
6097
|
-
role: "tool",
|
|
6098
|
-
content: resultContent2,
|
|
6099
|
-
tool_call_id: toolCall.id
|
|
6100
|
-
});
|
|
6101
|
-
continue;
|
|
6102
|
-
}
|
|
6103
|
-
if (!tool2 && !isMcpTool) {
|
|
6104
|
-
this.emitEvent({ type: "tool_denied", agent: agent2._name, tool: toolName });
|
|
6105
|
-
currentMessages.push({
|
|
6106
|
-
role: "tool",
|
|
6107
|
-
content: `Tool "${toolName}" is not available. Available tools: ${agent2._config.tools?.map((t) => t.name).join(", ") ?? "none"}`,
|
|
6108
|
-
tool_call_id: toolCall.id
|
|
6109
|
-
});
|
|
6110
|
-
continue;
|
|
6111
|
-
}
|
|
6112
|
-
let toolArgs;
|
|
6113
|
-
try {
|
|
6114
|
-
toolArgs = JSON.parse(toolCall.function.arguments);
|
|
6115
|
-
} catch {
|
|
6116
|
-
currentMessages.push({
|
|
6117
|
-
role: "tool",
|
|
6118
|
-
content: `Error: Invalid JSON in tool arguments. Please provide valid JSON.`,
|
|
6119
|
-
tool_call_id: toolCall.id
|
|
6120
|
-
});
|
|
6121
|
-
continue;
|
|
6122
|
-
}
|
|
6123
|
-
this.emitEvent({
|
|
6124
|
-
type: "tool_call_start",
|
|
6125
|
-
tool: toolName,
|
|
6126
|
-
callId: toolCall.id,
|
|
6127
|
-
data: { args: toolArgs }
|
|
6128
|
-
});
|
|
6129
|
-
this.onToolCall?.({ name: toolName, args: toolArgs, callId: toolCall.id }, callbackMeta);
|
|
6130
|
-
const toolStart = Date.now();
|
|
6131
|
-
const suppressErrorShapeInspection = tool2?.sensitive === true || tool2?.toModelOutput !== void 0;
|
|
6132
|
-
if (tool2 && tool2.requireApproval) {
|
|
6133
|
-
const approvalFn = async () => {
|
|
6134
|
-
const decision = await this.awaitHuman({
|
|
6135
|
-
channel: "tool_approval",
|
|
6136
|
-
prompt: `Tool "${toolName}" wants to execute with args: ${JSON.stringify(toolArgs)}`,
|
|
6137
|
-
metadata: { toolName, args: toolArgs, agent: agent2._name }
|
|
6138
|
-
});
|
|
6139
|
-
if (!decision.approved) {
|
|
6140
|
-
const reason = decision.reason ?? "Denied by human";
|
|
6141
|
-
currentMessages.push({
|
|
6142
|
-
role: "tool",
|
|
6143
|
-
content: JSON.stringify({ error: `Tool denied by human: ${reason}` }),
|
|
6144
|
-
tool_call_id: toolCall.id
|
|
6145
|
-
});
|
|
6146
|
-
return { approved: false, reason };
|
|
6147
|
-
}
|
|
6148
|
-
return { approved: true };
|
|
6149
|
-
};
|
|
6150
|
-
let approvalOutcome;
|
|
6151
|
-
if (this.spanManager) {
|
|
6152
|
-
approvalOutcome = await this.spanManager.withSpanAsync(
|
|
6153
|
-
"axl.tool.approval",
|
|
6154
|
-
{
|
|
6155
|
-
"axl.tool.name": toolName,
|
|
6156
|
-
"axl.agent.name": agent2._name
|
|
6157
|
-
},
|
|
6158
|
-
async (span) => {
|
|
6159
|
-
const result = await approvalFn();
|
|
6160
|
-
span.setAttribute("axl.tool.approval.approved", result.approved);
|
|
6161
|
-
return result;
|
|
6162
|
-
}
|
|
6163
|
-
);
|
|
6164
|
-
} else {
|
|
6165
|
-
approvalOutcome = await approvalFn();
|
|
6166
|
-
}
|
|
6167
|
-
this.emitEvent({
|
|
6168
|
-
type: "tool_approval",
|
|
6169
|
-
agent: agent2._name,
|
|
6170
|
-
tool: toolName,
|
|
6171
|
-
data: {
|
|
6172
|
-
approved: approvalOutcome.approved,
|
|
6173
|
-
args: toolArgs,
|
|
6174
|
-
...approvalOutcome.reason ? { reason: approvalOutcome.reason } : {}
|
|
6175
|
-
}
|
|
6176
|
-
});
|
|
6177
|
-
if (!approvalOutcome.approved) continue;
|
|
6178
|
-
}
|
|
6179
|
-
if (tool2 && tool2.hooks?.before) {
|
|
6180
|
-
try {
|
|
6181
|
-
toolArgs = await tool2.hooks.before(toolArgs, this);
|
|
6182
|
-
} catch (err) {
|
|
6183
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
6184
|
-
currentMessages.push({
|
|
6185
|
-
role: "tool",
|
|
6186
|
-
content: JSON.stringify({ error: `Before hook error: ${errorMsg}` }),
|
|
6187
|
-
tool_call_id: toolCall.id
|
|
6188
|
-
});
|
|
6189
|
-
continue;
|
|
6190
|
-
}
|
|
6191
|
-
}
|
|
6192
|
-
const executeTool = async () => {
|
|
6193
|
-
let outcome;
|
|
6194
|
-
let resultContent2;
|
|
6195
|
-
if (isMcpTool && this.mcpManager) {
|
|
6196
|
-
try {
|
|
6197
|
-
const mcpResult = await this.mcpManager.callTool(toolName, toolArgs);
|
|
6198
|
-
outcome = { kind: "returned", value: mcpResult };
|
|
6199
|
-
resultContent2 = mcpResult.content.map(
|
|
6200
|
-
(c) => c.type === "text" ? c.text : `[${c.type}]`
|
|
6201
|
-
).join("\n");
|
|
6202
|
-
if (mcpResult.isError) {
|
|
6203
|
-
resultContent2 = `Error: ${resultContent2}`;
|
|
6204
|
-
}
|
|
6205
|
-
} catch (err) {
|
|
6206
|
-
outcome = {
|
|
6207
|
-
kind: "thrown",
|
|
6208
|
-
value: { error: err instanceof Error ? err.message : String(err) }
|
|
6209
|
-
};
|
|
6210
|
-
resultContent2 = JSON.stringify(outcome.value);
|
|
6211
|
-
}
|
|
6212
|
-
} else if (tool2) {
|
|
6213
|
-
const childCtx = this.createChildContext();
|
|
6214
|
-
try {
|
|
6215
|
-
outcome = { kind: "returned", value: await tool2._execute(toolArgs, childCtx) };
|
|
6216
|
-
} catch (err) {
|
|
6217
|
-
outcome = {
|
|
6218
|
-
kind: "thrown",
|
|
6219
|
-
value: { error: err instanceof Error ? err.message : String(err) }
|
|
6220
|
-
};
|
|
6221
|
-
}
|
|
6222
|
-
if (outcome.kind === "returned" && tool2.hooks?.after && !(suppressErrorShapeInspection ? isErrorShapedSafely(outcome.value) : isErrorShaped(outcome.value))) {
|
|
6223
|
-
try {
|
|
6224
|
-
outcome = {
|
|
6225
|
-
kind: "returned",
|
|
6226
|
-
value: await tool2.hooks.after(outcome.value, this)
|
|
6227
|
-
};
|
|
6228
|
-
} catch (err) {
|
|
6229
|
-
outcome = {
|
|
6230
|
-
kind: "thrown",
|
|
6231
|
-
value: {
|
|
6232
|
-
error: `After hook error: ${err instanceof Error ? err.message : String(err)}`
|
|
6233
|
-
}
|
|
6234
|
-
};
|
|
6235
|
-
}
|
|
6236
|
-
}
|
|
6237
|
-
if (tool2.sensitive) {
|
|
6238
|
-
resultContent2 = "[REDACTED - sensitive tool output]";
|
|
6239
|
-
} else if (outcome.kind === "thrown" || tool2.toModelOutput === void 0) {
|
|
6240
|
-
resultContent2 = JSON.stringify(outcome.value);
|
|
6241
|
-
}
|
|
6242
|
-
} else {
|
|
6243
|
-
outcome = { kind: "returned", value: void 0 };
|
|
6244
|
-
resultContent2 = "Tool execution error";
|
|
6245
|
-
}
|
|
6246
|
-
return { outcome, resultContent: resultContent2 };
|
|
6247
|
-
};
|
|
6248
|
-
const traceName = isMcpTool && this.mcpManager ? this.mcpManager.getQualifiedName(toolName) ?? toolName : toolName;
|
|
6249
|
-
const execution = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6250
|
-
"axl.tool.call",
|
|
6251
|
-
{
|
|
6252
|
-
"axl.tool.name": traceName,
|
|
6253
|
-
"axl.agent.name": agent2._name
|
|
6254
|
-
},
|
|
6255
|
-
async (span) => {
|
|
6256
|
-
const r = await executeTool();
|
|
6257
|
-
span.setAttribute("axl.tool.duration", Date.now() - toolStart);
|
|
6258
|
-
recordToolSpanResult(span, r.outcome.value, suppressErrorShapeInspection);
|
|
6259
|
-
return r;
|
|
6260
|
-
}
|
|
6261
|
-
) : await executeTool();
|
|
6262
|
-
const toolResult = execution.outcome.value;
|
|
6263
|
-
const toolDuration = Date.now() - toolStart;
|
|
6264
|
-
const resultContent = finalizeToolResult({
|
|
6265
|
-
toolName,
|
|
6266
|
-
configuredTool: tool2,
|
|
6267
|
-
outcome: execution.outcome,
|
|
6268
|
-
legacyContent: () => execution.resultContent,
|
|
6269
|
-
emitEnd: () => {
|
|
6270
|
-
this.emitEvent({
|
|
6271
|
-
type: "tool_call_end",
|
|
6272
|
-
agent: agent2._name,
|
|
6273
|
-
tool: traceName,
|
|
6274
|
-
duration: toolDuration,
|
|
6275
|
-
data: { args: toolArgs, result: toolResult, callId: toolCall.id }
|
|
6276
|
-
});
|
|
6277
|
-
},
|
|
6278
|
-
beforeProjection: () => this.currentSignal?.throwIfAborted()
|
|
6279
|
-
});
|
|
6280
|
-
currentMessages.push({
|
|
6281
|
-
role: "tool",
|
|
6282
|
-
content: resultContent,
|
|
6283
|
-
tool_call_id: toolCall.id
|
|
6284
|
-
});
|
|
7000
|
+
await this.observeToolInvocation(
|
|
7001
|
+
agent2,
|
|
7002
|
+
toolCall,
|
|
7003
|
+
currentMessages,
|
|
7004
|
+
toolDefs.map((tool2) => tool2.function.name)
|
|
7005
|
+
);
|
|
6285
7006
|
}
|
|
6286
7007
|
continue;
|
|
6287
7008
|
}
|
|
@@ -6401,7 +7122,7 @@ ${appendedSchemaText}`;
|
|
|
6401
7122
|
pendingRetryReason = "schema";
|
|
6402
7123
|
continue;
|
|
6403
7124
|
}
|
|
6404
|
-
const zodErr = schemaErr instanceof
|
|
7125
|
+
const zodErr = schemaErr instanceof ZodError2 ? schemaErr : new ZodError2([
|
|
6405
7126
|
{
|
|
6406
7127
|
code: "custom",
|
|
6407
7128
|
path: [],
|
|
@@ -6418,6 +7139,7 @@ ${appendedSchemaText}`;
|
|
|
6418
7139
|
metadata: this.metadata
|
|
6419
7140
|
});
|
|
6420
7141
|
} catch (err) {
|
|
7142
|
+
rethrowEventStreamOverflow(err);
|
|
6421
7143
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6422
7144
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
6423
7145
|
}
|
|
@@ -6754,12 +7476,12 @@ ${summaryResponse.content}`
|
|
|
6754
7476
|
* Execute a function with checkpoint-replay semantics.
|
|
6755
7477
|
*
|
|
6756
7478
|
* On first execution, runs `fn()`, saves the result under `name`, and
|
|
6757
|
-
* returns it.
|
|
6758
|
-
* without re-executing.
|
|
6759
|
-
*
|
|
7479
|
+
* returns it. A later evaluation in the same explicit execution scope
|
|
7480
|
+
* returns the saved result without re-executing. Axl does not restart or
|
|
7481
|
+
* resume workflow continuations across processes.
|
|
6760
7482
|
*
|
|
6761
7483
|
* `name` must be a stable, caller-supplied identifier — the same call
|
|
6762
|
-
* site MUST pass the same name across
|
|
7484
|
+
* site MUST pass the same name across evaluations of the same execution for
|
|
6763
7485
|
* replay to work. Names are scoped to a single execution and must be
|
|
6764
7486
|
* unique within it; reusing a name (e.g. inside a loop) gives
|
|
6765
7487
|
* last-write-wins behavior. For loops, compose names from the iterator:
|
|
@@ -6882,6 +7604,7 @@ ${summaryResponse.content}`
|
|
|
6882
7604
|
for (let i = 0; i < n; i++) {
|
|
6883
7605
|
const index = i;
|
|
6884
7606
|
const p = signalStorage.run(composedSignal, () => fn(index));
|
|
7607
|
+
this._registerBranchDrain(p);
|
|
6885
7608
|
p.then((value) => {
|
|
6886
7609
|
if (settled) return;
|
|
6887
7610
|
results[index] = { ok: true, value };
|
|
@@ -6896,6 +7619,14 @@ ${summaryResponse.content}`
|
|
|
6896
7619
|
reject(new QuorumNotMet(quorum, successCount, results));
|
|
6897
7620
|
}
|
|
6898
7621
|
}).catch((err) => {
|
|
7622
|
+
if (isEventStreamOverflowError(err)) {
|
|
7623
|
+
if (!settled) {
|
|
7624
|
+
settled = true;
|
|
7625
|
+
controller.abort();
|
|
7626
|
+
reject(err);
|
|
7627
|
+
}
|
|
7628
|
+
return;
|
|
7629
|
+
}
|
|
6899
7630
|
if (settled) return;
|
|
6900
7631
|
const isAbort = err instanceof DOMException && err.name === "AbortError";
|
|
6901
7632
|
if (isAbort) {
|
|
@@ -6918,14 +7649,16 @@ ${summaryResponse.content}`
|
|
|
6918
7649
|
}
|
|
6919
7650
|
const parentSignal = this.currentSignal;
|
|
6920
7651
|
const promises = Array.from({ length: n }, (_, i) => {
|
|
6921
|
-
const run = () => fn(i).then((value) => ({ ok: true, value })).catch(
|
|
6922
|
-
(err)
|
|
7652
|
+
const run = () => fn(i).then((value) => ({ ok: true, value })).catch((err) => {
|
|
7653
|
+
rethrowEventStreamOverflow(err);
|
|
7654
|
+
return {
|
|
6923
7655
|
ok: false,
|
|
6924
7656
|
error: err instanceof Error ? err.message : String(err)
|
|
6925
|
-
}
|
|
6926
|
-
);
|
|
7657
|
+
};
|
|
7658
|
+
});
|
|
6927
7659
|
return parentSignal ? signalStorage.run(parentSignal, run) : run();
|
|
6928
7660
|
});
|
|
7661
|
+
for (const promise of promises) this._registerBranchDrain(promise);
|
|
6929
7662
|
return Promise.all(promises);
|
|
6930
7663
|
}
|
|
6931
7664
|
// ── ctx.vote() ────────────────────────────────────────────────────────
|
|
@@ -7063,6 +7796,7 @@ ${summaryResponse.content}`
|
|
|
7063
7796
|
try {
|
|
7064
7797
|
validateResult = await options.validate(parsed, { metadata: this.metadata });
|
|
7065
7798
|
} catch (err) {
|
|
7799
|
+
rethrowEventStreamOverflow(err);
|
|
7066
7800
|
const reason = err instanceof Error ? err.message : String(err);
|
|
7067
7801
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
7068
7802
|
}
|
|
@@ -7080,6 +7814,7 @@ ${summaryResponse.content}`
|
|
|
7080
7814
|
emitVerifyOutcome(true, attempt + 1);
|
|
7081
7815
|
return parsed;
|
|
7082
7816
|
} catch (err) {
|
|
7817
|
+
rethrowEventStreamOverflow(err);
|
|
7083
7818
|
if (err instanceof ValidationError) {
|
|
7084
7819
|
lastRetry = {
|
|
7085
7820
|
error: err.reason,
|
|
@@ -7102,12 +7837,12 @@ ${summaryResponse.content}`
|
|
|
7102
7837
|
}
|
|
7103
7838
|
continue;
|
|
7104
7839
|
}
|
|
7105
|
-
const errorMsg = err instanceof
|
|
7840
|
+
const errorMsg = err instanceof ZodError2 ? err.message : err instanceof Error ? err.message : String(err);
|
|
7106
7841
|
lastRetry = { error: errorMsg, output: rawOutput };
|
|
7107
7842
|
if (attempt === maxRetries) {
|
|
7108
7843
|
emitVerifyOutcome(false, attempt + 1, errorMsg);
|
|
7109
7844
|
if (options?.fallback !== void 0) return options.fallback;
|
|
7110
|
-
const zodErr = err instanceof
|
|
7845
|
+
const zodErr = err instanceof ZodError2 ? err : new ZodError2([{ code: "custom", path: [], message: errorMsg }]);
|
|
7111
7846
|
throw new VerifyError(rawOutput, zodErr, maxRetries);
|
|
7112
7847
|
}
|
|
7113
7848
|
}
|
|
@@ -7116,7 +7851,7 @@ ${summaryResponse.content}`
|
|
|
7116
7851
|
if (options?.fallback !== void 0) return options.fallback;
|
|
7117
7852
|
throw new VerifyError(
|
|
7118
7853
|
lastRetry?.output,
|
|
7119
|
-
new
|
|
7854
|
+
new ZodError2([{ code: "custom", path: [], message: "Verify failed" }]),
|
|
7120
7855
|
maxRetries
|
|
7121
7856
|
);
|
|
7122
7857
|
}
|
|
@@ -7146,6 +7881,7 @@ ${summaryResponse.content}`
|
|
|
7146
7881
|
const unpriced = this.budgetContext.unpriced;
|
|
7147
7882
|
return { value, budgetExceeded: exceeded, totalCost, unpriced };
|
|
7148
7883
|
} catch (err) {
|
|
7884
|
+
rethrowEventStreamOverflow(err);
|
|
7149
7885
|
if (this.budgetContext.exceeded) {
|
|
7150
7886
|
return {
|
|
7151
7887
|
value: null,
|
|
@@ -7246,7 +7982,8 @@ ${summaryResponse.content}`
|
|
|
7246
7982
|
let remaining = fns.length;
|
|
7247
7983
|
for (const fn of fns) {
|
|
7248
7984
|
const p = signalStorage.run(composedSignal, fn);
|
|
7249
|
-
|
|
7985
|
+
this._registerBranchDrain(p);
|
|
7986
|
+
const continuation = p.then(async (value) => {
|
|
7250
7987
|
if (settled) return;
|
|
7251
7988
|
if (schema) {
|
|
7252
7989
|
const parsed = schema.safeParse(value);
|
|
@@ -7276,6 +8013,7 @@ ${summaryResponse.content}`
|
|
|
7276
8013
|
return;
|
|
7277
8014
|
}
|
|
7278
8015
|
} catch (err) {
|
|
8016
|
+
rethrowEventStreamOverflow(err);
|
|
7279
8017
|
remaining--;
|
|
7280
8018
|
lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
|
|
7281
8019
|
if (remaining === 0 && !settled) {
|
|
@@ -7295,6 +8033,14 @@ ${summaryResponse.content}`
|
|
|
7295
8033
|
controller.abort();
|
|
7296
8034
|
resolve2(value);
|
|
7297
8035
|
}).catch((err) => {
|
|
8036
|
+
if (isEventStreamOverflowError(err)) {
|
|
8037
|
+
if (!settled) {
|
|
8038
|
+
settled = true;
|
|
8039
|
+
controller.abort();
|
|
8040
|
+
reject(err);
|
|
8041
|
+
}
|
|
8042
|
+
throw err;
|
|
8043
|
+
}
|
|
7298
8044
|
if (settled) return;
|
|
7299
8045
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
7300
8046
|
remaining--;
|
|
@@ -7311,6 +8057,7 @@ ${summaryResponse.content}`
|
|
|
7311
8057
|
reject(lastError);
|
|
7312
8058
|
}
|
|
7313
8059
|
});
|
|
8060
|
+
this._registerBranchDrain(continuation);
|
|
7314
8061
|
}
|
|
7315
8062
|
});
|
|
7316
8063
|
}
|
|
@@ -7349,9 +8096,19 @@ ${summaryResponse.content}`
|
|
|
7349
8096
|
const idx = nextIndex++;
|
|
7350
8097
|
try {
|
|
7351
8098
|
const value = mapSignal ? await signalStorage.run(mapSignal, () => fn(items[idx], idx)) : await fn(items[idx], idx);
|
|
8099
|
+
if (settled) return;
|
|
7352
8100
|
results[idx] = { ok: true, value };
|
|
7353
8101
|
successCount++;
|
|
7354
8102
|
} catch (err) {
|
|
8103
|
+
if (isEventStreamOverflowError(err)) {
|
|
8104
|
+
settled = true;
|
|
8105
|
+
controller?.abort();
|
|
8106
|
+
reject(err);
|
|
8107
|
+
throw err;
|
|
8108
|
+
}
|
|
8109
|
+
if (settled) {
|
|
8110
|
+
return;
|
|
8111
|
+
}
|
|
7355
8112
|
if (err instanceof DOMException && err.name === "AbortError" && controller?.signal.aborted) {
|
|
7356
8113
|
completedCount++;
|
|
7357
8114
|
return;
|
|
@@ -7377,7 +8134,9 @@ ${summaryResponse.content}`
|
|
|
7377
8134
|
};
|
|
7378
8135
|
const workers = Math.min(concurrency, items.length);
|
|
7379
8136
|
for (let i = 0; i < workers; i++) {
|
|
7380
|
-
runNext()
|
|
8137
|
+
const worker = runNext();
|
|
8138
|
+
this._registerBranchDrain(worker);
|
|
8139
|
+
worker.catch((err) => {
|
|
7381
8140
|
if (!settled) reject(err);
|
|
7382
8141
|
});
|
|
7383
8142
|
}
|
|
@@ -7403,12 +8162,44 @@ ${summaryResponse.content}`
|
|
|
7403
8162
|
return this._awaitHumanImpl(options);
|
|
7404
8163
|
}
|
|
7405
8164
|
async _awaitHumanImpl(options) {
|
|
8165
|
+
const branchSignal = this.currentSignal;
|
|
8166
|
+
const makeAbortError = (reason) => {
|
|
8167
|
+
if (typeof DOMException !== "undefined") {
|
|
8168
|
+
return new DOMException(
|
|
8169
|
+
typeof reason === "string" ? reason : "awaitHuman aborted",
|
|
8170
|
+
"AbortError"
|
|
8171
|
+
);
|
|
8172
|
+
}
|
|
8173
|
+
const err = new Error(typeof reason === "string" ? reason : "awaitHuman aborted");
|
|
8174
|
+
err.name = "AbortError";
|
|
8175
|
+
return err;
|
|
8176
|
+
};
|
|
8177
|
+
const awaitWithAbort = async (promise, signal2 = branchSignal) => {
|
|
8178
|
+
if (!signal2) return promise;
|
|
8179
|
+
if (signal2.aborted) throw makeAbortError(signal2.reason);
|
|
8180
|
+
let onAbort;
|
|
8181
|
+
try {
|
|
8182
|
+
return await Promise.race([
|
|
8183
|
+
promise,
|
|
8184
|
+
new Promise((_, reject) => {
|
|
8185
|
+
onAbort = () => reject(makeAbortError(signal2.reason));
|
|
8186
|
+
signal2.addEventListener("abort", onAbort, { once: true });
|
|
8187
|
+
})
|
|
8188
|
+
]);
|
|
8189
|
+
} finally {
|
|
8190
|
+
if (onAbort) signal2.removeEventListener("abort", onAbort);
|
|
8191
|
+
}
|
|
8192
|
+
};
|
|
7406
8193
|
if (this.awaitHumanHandler) {
|
|
8194
|
+
if (branchSignal?.aborted) throw makeAbortError(branchSignal.reason);
|
|
7407
8195
|
this.emitEvent({
|
|
7408
8196
|
type: "await_human",
|
|
7409
8197
|
data: { channel: options.channel, prompt: options.prompt }
|
|
7410
8198
|
});
|
|
7411
|
-
|
|
8199
|
+
if (branchSignal?.aborted) throw makeAbortError(branchSignal.reason);
|
|
8200
|
+
const decision2 = parseHumanDecision(
|
|
8201
|
+
await awaitWithAbort(Promise.resolve(this.awaitHumanHandler(options)))
|
|
8202
|
+
);
|
|
7412
8203
|
this.emitEvent({
|
|
7413
8204
|
type: "await_human_resolved",
|
|
7414
8205
|
data: { channel: options.channel, decision: decision2 }
|
|
@@ -7420,68 +8211,126 @@ ${summaryResponse.content}`
|
|
|
7420
8211
|
"Tool requires approval but no approval handler is configured. Provide awaitHumanHandler to createContext() or use runtime.execute() with workflow infrastructure."
|
|
7421
8212
|
);
|
|
7422
8213
|
}
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
awaitHumanPrompt: options.prompt
|
|
7440
|
-
}
|
|
7441
|
-
});
|
|
7442
|
-
}
|
|
7443
|
-
this.emitEvent({
|
|
7444
|
-
type: "await_human",
|
|
7445
|
-
data: { channel: options.channel, prompt: options.prompt }
|
|
8214
|
+
const existingClaim = this.awaitHumanClaims.get(this.executionId);
|
|
8215
|
+
if (existingClaim) {
|
|
8216
|
+
const error = new AxlError(
|
|
8217
|
+
"CONCURRENT_HUMAN_DECISION_UNSUPPORTED",
|
|
8218
|
+
`Execution "${this.executionId}" already has an active human decision. Concurrent awaitHuman calls require request-scoped decision identifiers.`
|
|
8219
|
+
);
|
|
8220
|
+
existingClaim.abort(error);
|
|
8221
|
+
throw error;
|
|
8222
|
+
}
|
|
8223
|
+
const claimController = new AbortController();
|
|
8224
|
+
this.awaitHumanClaims.set(this.executionId, claimController);
|
|
8225
|
+
const signal = branchSignal ? AbortSignal.any([branchSignal, claimController.signal]) : claimController.signal;
|
|
8226
|
+
let saveAttempted = false;
|
|
8227
|
+
let markReady;
|
|
8228
|
+
const ready = new Promise((resolve2) => {
|
|
8229
|
+
markReady = resolve2;
|
|
7446
8230
|
});
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
8231
|
+
let markCleanupFinished;
|
|
8232
|
+
const cleanupFinished = new Promise((resolve2) => {
|
|
8233
|
+
markCleanupFinished = resolve2;
|
|
8234
|
+
});
|
|
8235
|
+
let resolveDecision;
|
|
8236
|
+
let rejectDecision;
|
|
8237
|
+
const decisionPromise = new Promise((resolve2, reject) => {
|
|
8238
|
+
resolveDecision = resolve2;
|
|
8239
|
+
rejectDecision = reject;
|
|
8240
|
+
});
|
|
8241
|
+
void decisionPromise.catch(() => {
|
|
8242
|
+
});
|
|
8243
|
+
const registeredResolver = {
|
|
8244
|
+
resolve: resolveDecision,
|
|
8245
|
+
ready,
|
|
8246
|
+
state: "preparing",
|
|
8247
|
+
cancelled: false,
|
|
8248
|
+
cleanupFinished
|
|
7457
8249
|
};
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
8250
|
+
this.pendingDecisions.set(this.executionId, registeredResolver);
|
|
8251
|
+
const abortListener = () => {
|
|
8252
|
+
registeredResolver.cancelled = true;
|
|
8253
|
+
markReady();
|
|
8254
|
+
rejectDecision(makeAbortError(signal.reason));
|
|
8255
|
+
};
|
|
8256
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
8257
|
+
let decision;
|
|
8258
|
+
try {
|
|
8259
|
+
if (signal.aborted) throw makeAbortError(signal.reason);
|
|
8260
|
+
if (this.stateStore) {
|
|
8261
|
+
saveAttempted = true;
|
|
8262
|
+
await this.stateStore.savePendingDecision(this.executionId, {
|
|
8263
|
+
executionId: this.executionId,
|
|
8264
|
+
channel: options.channel,
|
|
8265
|
+
prompt: options.prompt,
|
|
8266
|
+
metadata: options.metadata,
|
|
8267
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8268
|
+
});
|
|
7462
8269
|
}
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
8270
|
+
if (signal.aborted) throw makeAbortError(signal.reason);
|
|
8271
|
+
this.emitEvent({
|
|
8272
|
+
type: "await_human",
|
|
8273
|
+
data: { channel: options.channel, prompt: options.prompt }
|
|
8274
|
+
});
|
|
8275
|
+
registeredResolver.state = "pending";
|
|
8276
|
+
markReady();
|
|
8277
|
+
decision = await decisionPromise;
|
|
8278
|
+
} catch (error) {
|
|
8279
|
+
if (registeredResolver.state === "preparing") {
|
|
8280
|
+
registeredResolver.state = "failed";
|
|
8281
|
+
markReady();
|
|
8282
|
+
}
|
|
8283
|
+
let needsCompensation = saveAttempted;
|
|
8284
|
+
const inFlightResolution = registeredResolver.resolution;
|
|
8285
|
+
if (registeredResolver.state === "resolving" && inFlightResolution) {
|
|
8286
|
+
needsCompensation = !await inFlightResolution;
|
|
8287
|
+
} else if (registeredResolver.state === "resolved") {
|
|
8288
|
+
needsCompensation = false;
|
|
8289
|
+
}
|
|
8290
|
+
if (this.stateStore && needsCompensation) {
|
|
8291
|
+
try {
|
|
8292
|
+
await this.stateStore.resolveDecision(this.executionId, {
|
|
8293
|
+
approved: false,
|
|
8294
|
+
reason: "Execution aborted while awaiting approval"
|
|
8295
|
+
});
|
|
8296
|
+
} catch (cleanupError) {
|
|
8297
|
+
if (error instanceof Error && Object.isExtensible(error)) {
|
|
8298
|
+
Object.defineProperty(error, "cleanupError", {
|
|
8299
|
+
configurable: true,
|
|
8300
|
+
enumerable: false,
|
|
8301
|
+
value: cleanupError
|
|
8302
|
+
});
|
|
8303
|
+
}
|
|
8304
|
+
try {
|
|
8305
|
+
this._onDecisionCleanupFailed?.({
|
|
8306
|
+
executionId: this.executionId,
|
|
8307
|
+
...this.workflowName ? { workflow: this.workflowName } : {},
|
|
8308
|
+
operation: "resolveDecision_compensation",
|
|
8309
|
+
error: cleanupError
|
|
8310
|
+
});
|
|
8311
|
+
} catch (observerError) {
|
|
8312
|
+
console.error(
|
|
8313
|
+
"[axl] decision_cleanup_failed listener threw; workflow outcome unchanged:",
|
|
8314
|
+
observerError instanceof Error ? observerError.message : String(observerError)
|
|
8315
|
+
);
|
|
8316
|
+
}
|
|
7468
8317
|
}
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
8318
|
+
}
|
|
8319
|
+
throw error;
|
|
8320
|
+
} finally {
|
|
8321
|
+
signal.removeEventListener("abort", abortListener);
|
|
8322
|
+
if (this.pendingDecisions.get(this.executionId) === registeredResolver) {
|
|
8323
|
+
this.pendingDecisions.delete(this.executionId);
|
|
8324
|
+
}
|
|
8325
|
+
if (this.awaitHumanClaims.get(this.executionId) === claimController) {
|
|
8326
|
+
this.awaitHumanClaims.delete(this.executionId);
|
|
8327
|
+
}
|
|
8328
|
+
markCleanupFinished();
|
|
8329
|
+
}
|
|
7473
8330
|
this.emitEvent({
|
|
7474
8331
|
type: "await_human_resolved",
|
|
7475
8332
|
data: { channel: options.channel, decision }
|
|
7476
8333
|
});
|
|
7477
|
-
if (this.stateStore) {
|
|
7478
|
-
await this.stateStore.saveExecutionState(this.executionId, {
|
|
7479
|
-
workflow: this.workflowName ?? "unknown",
|
|
7480
|
-
input: this.input,
|
|
7481
|
-
step: this.stepRefRoot.value,
|
|
7482
|
-
status: "running"
|
|
7483
|
-
});
|
|
7484
|
-
}
|
|
7485
8334
|
return decision;
|
|
7486
8335
|
}
|
|
7487
8336
|
/**
|
|
@@ -7533,8 +8382,8 @@ ${summaryResponse.content}`
|
|
|
7533
8382
|
// Internal: the runtime is the only caller, user workflows never call these.
|
|
7534
8383
|
/** @internal — idempotent; no-ops on second+ call within one ctx. */
|
|
7535
8384
|
_emitWorkflowStart(input) {
|
|
7536
|
-
if (this.
|
|
7537
|
-
this.
|
|
8385
|
+
if (this.workflowLifecycleState.startEmitted) return;
|
|
8386
|
+
this.workflowLifecycleState.startEmitted = true;
|
|
7538
8387
|
this.emitEvent({
|
|
7539
8388
|
type: "workflow_start",
|
|
7540
8389
|
workflow: this.workflowName,
|
|
@@ -7550,8 +8399,8 @@ ${summaryResponse.content}`
|
|
|
7550
8399
|
* status. First-wins semantics: the completed event stands, the
|
|
7551
8400
|
* inner side-effect failure propagates as a normal thrown error. */
|
|
7552
8401
|
_emitWorkflowEnd(info) {
|
|
7553
|
-
if (this.
|
|
7554
|
-
this.
|
|
8402
|
+
if (this.workflowLifecycleState.endEmitted) return;
|
|
8403
|
+
this.workflowLifecycleState.endEmitted = true;
|
|
7555
8404
|
this.emitEvent({
|
|
7556
8405
|
type: "workflow_end",
|
|
7557
8406
|
workflow: this.workflowName,
|
|
@@ -7561,7 +8410,8 @@ ${summaryResponse.content}`
|
|
|
7561
8410
|
duration: info.duration,
|
|
7562
8411
|
...info.result !== void 0 ? { result: info.result } : {},
|
|
7563
8412
|
...info.error !== void 0 ? { error: info.error } : {},
|
|
7564
|
-
...info.aborted ? { aborted: true } : {}
|
|
8413
|
+
...info.aborted ? { aborted: true } : {},
|
|
8414
|
+
...info.observation ? { observation: info.observation } : {}
|
|
7565
8415
|
}
|
|
7566
8416
|
});
|
|
7567
8417
|
}
|
|
@@ -7850,6 +8700,7 @@ ${summaryResponse.content}`
|
|
|
7850
8700
|
* consumer sites via the `type` discriminator.
|
|
7851
8701
|
*/
|
|
7852
8702
|
emitEvent(partial) {
|
|
8703
|
+
if (this.workflowLifecycleState.endEmitted && partial.type !== "workflow_end") return;
|
|
7853
8704
|
const data = partial.data;
|
|
7854
8705
|
const frame = askStorage.getStore();
|
|
7855
8706
|
const step = (frame?.stepRef ?? this.stepRefRoot).value++;
|
|
@@ -7875,6 +8726,7 @@ ${summaryResponse.content}`
|
|
|
7875
8726
|
}
|
|
7876
8727
|
}
|
|
7877
8728
|
const event = {
|
|
8729
|
+
schemaVersion: 2,
|
|
7878
8730
|
executionId: this.executionId,
|
|
7879
8731
|
step,
|
|
7880
8732
|
timestamp: Date.now(),
|
|
@@ -7895,13 +8747,13 @@ ${summaryResponse.content}`
|
|
|
7895
8747
|
...partial,
|
|
7896
8748
|
data
|
|
7897
8749
|
};
|
|
7898
|
-
const finalEvent = this.config.trace?.redact ?
|
|
8750
|
+
const finalEvent = this.config.trace?.redact ? redactHistoricalEvent(event) : event;
|
|
7899
8751
|
let overflowErr;
|
|
7900
8752
|
if (this.onTrace) {
|
|
7901
8753
|
try {
|
|
7902
8754
|
this.onTrace(finalEvent);
|
|
7903
8755
|
} catch (err) {
|
|
7904
|
-
if (err
|
|
8756
|
+
if (isEventStreamOverflowError(err)) {
|
|
7905
8757
|
overflowErr = err;
|
|
7906
8758
|
} else {
|
|
7907
8759
|
console.error(
|
|
@@ -7916,7 +8768,7 @@ ${summaryResponse.content}`
|
|
|
7916
8768
|
try {
|
|
7917
8769
|
bus._push(finalEvent);
|
|
7918
8770
|
} catch (err) {
|
|
7919
|
-
if (err
|
|
8771
|
+
if (isEventStreamOverflowError(err)) {
|
|
7920
8772
|
if (!overflowErr) {
|
|
7921
8773
|
overflowErr = err;
|
|
7922
8774
|
} else {
|
|
@@ -8103,26 +8955,31 @@ var MemoryStore = class {
|
|
|
8103
8955
|
}
|
|
8104
8956
|
// ── Execution History ──────────────────────────────────────────────
|
|
8105
8957
|
async saveExecution(execution) {
|
|
8106
|
-
|
|
8958
|
+
const normalized = normalizeStoredExecution({
|
|
8959
|
+
...execution,
|
|
8960
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
8961
|
+
});
|
|
8962
|
+
this.executionHistory.set(execution.executionId, structuredClone(normalized));
|
|
8107
8963
|
}
|
|
8108
8964
|
async getExecution(executionId) {
|
|
8109
8965
|
const exec = this.executionHistory.get(executionId);
|
|
8110
|
-
return exec ? structuredClone(exec) : null;
|
|
8966
|
+
return exec ? normalizeStoredExecution(structuredClone(exec)) : null;
|
|
8111
8967
|
}
|
|
8112
8968
|
async listExecutions(limit) {
|
|
8113
8969
|
const sorted = [...this.executionHistory.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
8114
8970
|
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
8115
|
-
return result.map((e) => structuredClone(e));
|
|
8971
|
+
return result.map((e) => normalizeStoredExecution(structuredClone(e)));
|
|
8116
8972
|
}
|
|
8117
8973
|
async deleteExecution(executionId) {
|
|
8118
|
-
const
|
|
8119
|
-
this.checkpoints.delete(executionId);
|
|
8120
|
-
this.executionStates.delete(executionId);
|
|
8121
|
-
this.streamingEvents.delete(executionId);
|
|
8122
|
-
|
|
8974
|
+
const removedHistory = this.executionHistory.delete(executionId);
|
|
8975
|
+
const removedCheckpoints = this.checkpoints.delete(executionId);
|
|
8976
|
+
const removedState = this.executionStates.delete(executionId);
|
|
8977
|
+
const removedStreaming = this.streamingEvents.delete(executionId);
|
|
8978
|
+
const removedDecision = this.decisions.delete(executionId);
|
|
8979
|
+
if (removedDecision) {
|
|
8123
8980
|
this.persistAwaitHumanState();
|
|
8124
8981
|
}
|
|
8125
|
-
return
|
|
8982
|
+
return removedHistory || removedCheckpoints || removedState || removedStreaming || removedDecision;
|
|
8126
8983
|
}
|
|
8127
8984
|
// ── Eval History ──────────────────────────────────────────────────
|
|
8128
8985
|
async saveEvalResult(entry) {
|
|
@@ -8228,16 +9085,23 @@ function rowToExecutionInfo(row) {
|
|
|
8228
9085
|
startedAt: row.started_at,
|
|
8229
9086
|
completedAt: row.completed_at ?? void 0,
|
|
8230
9087
|
duration: row.duration,
|
|
8231
|
-
|
|
8232
|
-
|
|
9088
|
+
events: safeJsonParse(row.events) ?? [],
|
|
9089
|
+
...row.event_schema_version != null ? { eventSchemaVersion: row.event_schema_version } : {}
|
|
8233
9090
|
};
|
|
9091
|
+
if (row.error != null) info.error = row.error;
|
|
8234
9092
|
if (row.metadata != null) {
|
|
8235
9093
|
const parsed = safeJsonParse(row.metadata);
|
|
8236
9094
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
8237
9095
|
info.metadata = parsed;
|
|
8238
9096
|
}
|
|
8239
9097
|
}
|
|
8240
|
-
|
|
9098
|
+
if (row.observation != null) {
|
|
9099
|
+
const parsed = safeJsonParse(row.observation);
|
|
9100
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
9101
|
+
info.observation = parsed;
|
|
9102
|
+
}
|
|
9103
|
+
}
|
|
9104
|
+
return normalizeStoredExecution(info);
|
|
8241
9105
|
}
|
|
8242
9106
|
var SQLiteStore = class {
|
|
8243
9107
|
db;
|
|
@@ -8277,6 +9141,10 @@ var SQLiteStore = class {
|
|
|
8277
9141
|
* column (TEXT, nullable, JSON-serialized) so caller-supplied
|
|
8278
9142
|
* `ExecuteOptions.metadata` round-trips through history. Existing
|
|
8279
9143
|
* rows get `NULL`, deserialized as `metadata: undefined`.
|
|
9144
|
+
* - v4 (event schema): add `execution_history.event_schema_version`.
|
|
9145
|
+
* Existing rows remain `NULL`, the documented v1 sentinel.
|
|
9146
|
+
* - v5 (observation completeness): add `execution_history.observation`.
|
|
9147
|
+
* Existing rows remain `NULL`, meaning they predate the contract.
|
|
8280
9148
|
*
|
|
8281
9149
|
* Applied BEFORE `initTables()` so the subsequent `CREATE TABLE
|
|
8282
9150
|
* IF NOT EXISTS` runs against the post-migration column name. Fresh
|
|
@@ -8294,7 +9162,7 @@ var SQLiteStore = class {
|
|
|
8294
9162
|
* transactional re-read costs nothing.
|
|
8295
9163
|
*/
|
|
8296
9164
|
migrate() {
|
|
8297
|
-
const TARGET_VERSION =
|
|
9165
|
+
const TARGET_VERSION = 5;
|
|
8298
9166
|
const current = this.db.pragma("user_version", { simple: true });
|
|
8299
9167
|
if (current >= TARGET_VERSION) return;
|
|
8300
9168
|
this.db.exec("BEGIN IMMEDIATE");
|
|
@@ -8322,6 +9190,18 @@ var SQLiteStore = class {
|
|
|
8322
9190
|
this.db.exec("ALTER TABLE execution_history ADD COLUMN metadata TEXT");
|
|
8323
9191
|
}
|
|
8324
9192
|
}
|
|
9193
|
+
if (committed < 4) {
|
|
9194
|
+
const cols = this.db.pragma("table_info(execution_history)");
|
|
9195
|
+
if (cols.length > 0 && !cols.some((c) => c.name === "event_schema_version")) {
|
|
9196
|
+
this.db.exec("ALTER TABLE execution_history ADD COLUMN event_schema_version INTEGER");
|
|
9197
|
+
}
|
|
9198
|
+
}
|
|
9199
|
+
if (committed < 5) {
|
|
9200
|
+
const cols = this.db.pragma("table_info(execution_history)");
|
|
9201
|
+
if (cols.length > 0 && !cols.some((c) => c.name === "observation")) {
|
|
9202
|
+
this.db.exec("ALTER TABLE execution_history ADD COLUMN observation TEXT");
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
8325
9205
|
this.db.pragma(`user_version = ${TARGET_VERSION}`);
|
|
8326
9206
|
this.db.exec("COMMIT");
|
|
8327
9207
|
} catch (err) {
|
|
@@ -8385,7 +9265,9 @@ var SQLiteStore = class {
|
|
|
8385
9265
|
duration INTEGER NOT NULL DEFAULT 0,
|
|
8386
9266
|
error TEXT,
|
|
8387
9267
|
events TEXT NOT NULL,
|
|
8388
|
-
metadata TEXT
|
|
9268
|
+
metadata TEXT,
|
|
9269
|
+
event_schema_version INTEGER,
|
|
9270
|
+
observation TEXT
|
|
8389
9271
|
);
|
|
8390
9272
|
|
|
8391
9273
|
CREATE TABLE IF NOT EXISTS eval_history (
|
|
@@ -8470,7 +9352,7 @@ var SQLiteStore = class {
|
|
|
8470
9352
|
const stmt = this.db.prepare("DELETE FROM decisions WHERE execution_id = ?");
|
|
8471
9353
|
stmt.run(executionId);
|
|
8472
9354
|
}
|
|
8473
|
-
// ──
|
|
9355
|
+
// ── Legacy application-managed execution state ────────────────────────
|
|
8474
9356
|
async saveExecutionState(executionId, state) {
|
|
8475
9357
|
const stmt = this.db.prepare(
|
|
8476
9358
|
"INSERT OR REPLACE INTO execution_state (execution_id, workflow, input, step, status, metadata) VALUES (?, ?, ?, ?, ?, ?)"
|
|
@@ -8505,19 +9387,25 @@ var SQLiteStore = class {
|
|
|
8505
9387
|
}
|
|
8506
9388
|
// ── Execution History ────────────────────────────────────────────────────
|
|
8507
9389
|
async saveExecution(execution) {
|
|
9390
|
+
const normalized = normalizeStoredExecution({
|
|
9391
|
+
...execution,
|
|
9392
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
9393
|
+
});
|
|
8508
9394
|
this.db.prepare(
|
|
8509
|
-
"INSERT OR REPLACE INTO execution_history (execution_id, workflow, status, total_cost, started_at, completed_at, duration, error, events, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
9395
|
+
"INSERT OR REPLACE INTO execution_history (execution_id, workflow, status, total_cost, started_at, completed_at, duration, error, events, metadata, event_schema_version, observation) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
8510
9396
|
).run(
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
JSON.stringify(
|
|
8520
|
-
|
|
9397
|
+
normalized.executionId,
|
|
9398
|
+
normalized.workflow,
|
|
9399
|
+
normalized.status,
|
|
9400
|
+
normalized.totalCost,
|
|
9401
|
+
normalized.startedAt,
|
|
9402
|
+
normalized.completedAt ?? null,
|
|
9403
|
+
normalized.duration,
|
|
9404
|
+
normalized.error ?? null,
|
|
9405
|
+
JSON.stringify(normalized.events),
|
|
9406
|
+
normalized.metadata !== void 0 ? JSON.stringify(normalized.metadata) : null,
|
|
9407
|
+
normalized.eventSchemaVersion,
|
|
9408
|
+
normalized.observation !== void 0 ? JSON.stringify(normalized.observation) : null
|
|
8521
9409
|
);
|
|
8522
9410
|
}
|
|
8523
9411
|
async getExecution(executionId) {
|
|
@@ -8531,11 +9419,11 @@ var SQLiteStore = class {
|
|
|
8531
9419
|
}
|
|
8532
9420
|
async deleteExecution(executionId) {
|
|
8533
9421
|
const tx = this.db.transaction(() => {
|
|
8534
|
-
const
|
|
8535
|
-
this.db.prepare("DELETE FROM checkpoints WHERE execution_id = ?").run(executionId);
|
|
8536
|
-
this.db.prepare("DELETE FROM execution_state WHERE execution_id = ?").run(executionId);
|
|
8537
|
-
this.db.prepare("DELETE FROM decisions WHERE execution_id = ?").run(executionId);
|
|
8538
|
-
return
|
|
9422
|
+
const historyResult = this.db.prepare("DELETE FROM execution_history WHERE execution_id = ?").run(executionId);
|
|
9423
|
+
const checkpointsResult = this.db.prepare("DELETE FROM checkpoints WHERE execution_id = ?").run(executionId);
|
|
9424
|
+
const stateResult = this.db.prepare("DELETE FROM execution_state WHERE execution_id = ?").run(executionId);
|
|
9425
|
+
const decisionsResult = this.db.prepare("DELETE FROM decisions WHERE execution_id = ?").run(executionId);
|
|
9426
|
+
return historyResult.changes + checkpointsResult.changes + stateResult.changes + decisionsResult.changes > 0;
|
|
8539
9427
|
});
|
|
8540
9428
|
return tx();
|
|
8541
9429
|
}
|
|
@@ -8861,6 +9749,11 @@ var AxlStream = class extends Readable {
|
|
|
8861
9749
|
get lifecycle() {
|
|
8862
9750
|
return this.events.lifecycle;
|
|
8863
9751
|
}
|
|
9752
|
+
/** Completeness of this stream's iterator queue. Check after consumption
|
|
9753
|
+
* before treating an unmatched lifecycle event as a producer defect. */
|
|
9754
|
+
get observationStatus() {
|
|
9755
|
+
return this.events.observationStatus;
|
|
9756
|
+
}
|
|
8864
9757
|
/** Coalescing iterator over `partial_object` events — yields the latest
|
|
8865
9758
|
* object payload per `askId`. Designed for streaming-structured-output
|
|
8866
9759
|
* UIs: when the producer outpaces the consumer, intermediate snapshots
|
|
@@ -8959,6 +9852,7 @@ var AxlStream = class extends Readable {
|
|
|
8959
9852
|
if (this.events.isFinished) return;
|
|
8960
9853
|
this.result = result;
|
|
8961
9854
|
const doneEvent = {
|
|
9855
|
+
schemaVersion: 2,
|
|
8962
9856
|
type: "done",
|
|
8963
9857
|
executionId,
|
|
8964
9858
|
step: Number.MAX_SAFE_INTEGER,
|
|
@@ -8977,6 +9871,7 @@ var AxlStream = class extends Readable {
|
|
|
8977
9871
|
_error(error, executionId) {
|
|
8978
9872
|
if (this.events.isFinished) return;
|
|
8979
9873
|
const errorEvent = {
|
|
9874
|
+
schemaVersion: 2,
|
|
8980
9875
|
type: "error",
|
|
8981
9876
|
executionId,
|
|
8982
9877
|
step: Number.MAX_SAFE_INTEGER,
|
|
@@ -9555,7 +10450,7 @@ async function createSpanManager(config) {
|
|
|
9555
10450
|
if (!config?.enabled) {
|
|
9556
10451
|
return new NoopSpanManager();
|
|
9557
10452
|
}
|
|
9558
|
-
const { OTelSpanManager } = await import("./span-manager-
|
|
10453
|
+
const { OTelSpanManager } = await import("./span-manager-74ZCGELO.js");
|
|
9559
10454
|
return OTelSpanManager.create(config);
|
|
9560
10455
|
}
|
|
9561
10456
|
|
|
@@ -9577,11 +10472,7 @@ var STREAMING_EXCLUDED_TYPES = /* @__PURE__ */ new Set([
|
|
|
9577
10472
|
"partial_object",
|
|
9578
10473
|
"string_delta"
|
|
9579
10474
|
]);
|
|
9580
|
-
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
9581
|
-
"sessionId",
|
|
9582
|
-
"sessionHistory",
|
|
9583
|
-
"resumeMode"
|
|
9584
|
-
]);
|
|
10475
|
+
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["sessionId", "sessionHistory"]);
|
|
9585
10476
|
function liftPersistedMetadata(metadata) {
|
|
9586
10477
|
if (!metadata) return void 0;
|
|
9587
10478
|
const filtered = {};
|
|
@@ -9699,7 +10590,7 @@ var StreamingFlusher = class {
|
|
|
9699
10590
|
await Promise.allSettled(pendings);
|
|
9700
10591
|
}
|
|
9701
10592
|
};
|
|
9702
|
-
function
|
|
10593
|
+
function isAbortError4(err) {
|
|
9703
10594
|
return typeof err === "object" && err !== null && err.name === "AbortError";
|
|
9704
10595
|
}
|
|
9705
10596
|
function pushEventBounded(execInfo, event, cap) {
|
|
@@ -9711,7 +10602,18 @@ function pushEventBounded(execInfo, event, cap) {
|
|
|
9711
10602
|
const last = execInfo.events[execInfo.events.length - 1];
|
|
9712
10603
|
const alreadyTruncated = last?.type === "log" && typeof last.data === "object" && last.data !== null && last.data.event === "events_truncated";
|
|
9713
10604
|
if (!alreadyTruncated) {
|
|
10605
|
+
if (execInfo.observation?.complete !== false) {
|
|
10606
|
+
execInfo.observation = {
|
|
10607
|
+
complete: false,
|
|
10608
|
+
reason: "persistence_truncated",
|
|
10609
|
+
maxEvents: cap
|
|
10610
|
+
};
|
|
10611
|
+
}
|
|
10612
|
+
if (event.type === "workflow_end") {
|
|
10613
|
+
event.data.observation = execInfo.observation;
|
|
10614
|
+
}
|
|
9714
10615
|
execInfo.events[execInfo.events.length - 1] = {
|
|
10616
|
+
schemaVersion: 2,
|
|
9715
10617
|
type: "log",
|
|
9716
10618
|
executionId: event.executionId,
|
|
9717
10619
|
step: event.step,
|
|
@@ -9740,7 +10642,9 @@ function boundRecoveredEvents(events, cap) {
|
|
|
9740
10642
|
const last = truncated[truncated.length - 1];
|
|
9741
10643
|
const alreadyTruncated = last?.type === "log" && typeof last.data === "object" && last.data !== null && last.data.event === "events_truncated";
|
|
9742
10644
|
if (!alreadyTruncated) {
|
|
10645
|
+
const version = getEventSchemaVersion(events[0] ?? {});
|
|
9743
10646
|
truncated[truncated.length - 1] = {
|
|
10647
|
+
...version === 2 ? { schemaVersion: 2 } : {},
|
|
9744
10648
|
type: "log",
|
|
9745
10649
|
executionId: events[0]?.executionId ?? "",
|
|
9746
10650
|
step: last?.step ?? 0,
|
|
@@ -9754,6 +10658,16 @@ function boundRecoveredEvents(events, cap) {
|
|
|
9754
10658
|
}
|
|
9755
10659
|
return truncated;
|
|
9756
10660
|
}
|
|
10661
|
+
var REMOVED_OBSERVATION_CALLBACKS = ["onToken", "onToolCall", "onAgentStart"];
|
|
10662
|
+
function assertNoRemovedObservationCallbacks(options) {
|
|
10663
|
+
if (options === null || typeof options !== "object") return;
|
|
10664
|
+
const removed = REMOVED_OBSERVATION_CALLBACKS.filter((key) => Object.hasOwn(options, key));
|
|
10665
|
+
if (removed.length === 0) return;
|
|
10666
|
+
throw new AxlError(
|
|
10667
|
+
"INVALID_CONFIG",
|
|
10668
|
+
`runtime.createContext() no longer accepts ${removed.join(", ")}. Observe ctx.events or use runtime.stream() instead. See docs/migration/stream-first-observation.md.`
|
|
10669
|
+
);
|
|
10670
|
+
}
|
|
9757
10671
|
var costScopeStorage = new AsyncLocalStorage2();
|
|
9758
10672
|
function forwardAbortSignal(external, internal) {
|
|
9759
10673
|
if (!external) return () => {
|
|
@@ -9915,9 +10829,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9915
10829
|
* → parse output → emit `workflow_end` → delete checkpoints → persist.
|
|
9916
10830
|
*
|
|
9917
10831
|
* Shared between `execute()` and `stream()`. Centralizes the
|
|
9918
|
-
* "start iff end" pairing invariant
|
|
9919
|
-
* (e.g., `resumeExecution`) can inherit correct behavior by routing
|
|
9920
|
-
* through this helper. Also factors out the AbortError detection.
|
|
10832
|
+
* "start iff end" pairing invariant and AbortError detection.
|
|
9921
10833
|
*
|
|
9922
10834
|
* Side effects on `execInfo`: sets `status` / `completedAt` /
|
|
9923
10835
|
* `duration` / `result` (or `error`) before emitting `workflow_end`.
|
|
@@ -9941,18 +10853,23 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9941
10853
|
*/
|
|
9942
10854
|
async runWorkflowBody(args) {
|
|
9943
10855
|
const { workflow: workflow2, ctx, execInfo, validated, span, cleanupAbortForwarder } = args;
|
|
10856
|
+
let observation = execInfo.observation ?? { complete: true };
|
|
9944
10857
|
try {
|
|
9945
10858
|
ctx._emitWorkflowStart(validated);
|
|
9946
10859
|
const raw = await workflow2.handler(ctx);
|
|
10860
|
+
const drainedObservation = await ctx._drainBranchWork();
|
|
10861
|
+
observation = drainedObservation.complete === false ? drainedObservation : execInfo.observation ?? drainedObservation;
|
|
9947
10862
|
const result = workflow2.outputSchema ? workflow2.outputSchema.parse(raw) : raw;
|
|
9948
10863
|
execInfo.status = "completed";
|
|
9949
10864
|
execInfo.completedAt = Date.now();
|
|
9950
10865
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
9951
10866
|
execInfo.result = result;
|
|
10867
|
+
execInfo.observation = observation;
|
|
9952
10868
|
ctx._emitWorkflowEnd({
|
|
9953
10869
|
status: "completed",
|
|
9954
10870
|
duration: execInfo.duration,
|
|
9955
|
-
result
|
|
10871
|
+
result,
|
|
10872
|
+
observation
|
|
9956
10873
|
});
|
|
9957
10874
|
if (this.stateStore.deleteCheckpoints) {
|
|
9958
10875
|
await this.stateStore.deleteCheckpoints(execInfo.executionId);
|
|
@@ -9962,19 +10879,28 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9962
10879
|
this.persistExecution(execInfo);
|
|
9963
10880
|
return result;
|
|
9964
10881
|
} catch (err) {
|
|
9965
|
-
|
|
10882
|
+
let terminalError = err;
|
|
10883
|
+
try {
|
|
10884
|
+
const drainedObservation = await ctx._drainBranchWork();
|
|
10885
|
+
observation = drainedObservation.complete === false ? drainedObservation : execInfo.observation ?? drainedObservation;
|
|
10886
|
+
} catch (drainErr) {
|
|
10887
|
+
terminalError = drainErr instanceof Error ? preserveErrorCause(drainErr, terminalError) : drainErr;
|
|
10888
|
+
}
|
|
10889
|
+
const aborted = isAbortError4(terminalError);
|
|
9966
10890
|
execInfo.status = "failed";
|
|
9967
10891
|
execInfo.completedAt = Date.now();
|
|
9968
10892
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
9969
|
-
execInfo.error =
|
|
10893
|
+
execInfo.error = terminalError instanceof Error ? terminalError.message : String(terminalError);
|
|
10894
|
+
execInfo.observation = observation;
|
|
9970
10895
|
ctx._emitWorkflowEnd({
|
|
9971
10896
|
status: "failed",
|
|
9972
10897
|
duration: execInfo.duration,
|
|
9973
10898
|
error: execInfo.error,
|
|
9974
|
-
...aborted ? { aborted: true } : {}
|
|
10899
|
+
...aborted ? { aborted: true } : {},
|
|
10900
|
+
observation
|
|
9975
10901
|
});
|
|
9976
10902
|
this.persistExecution(execInfo);
|
|
9977
|
-
throw
|
|
10903
|
+
throw terminalError;
|
|
9978
10904
|
} finally {
|
|
9979
10905
|
this.abortControllers.delete(execInfo.executionId);
|
|
9980
10906
|
cleanupAbortForwarder?.();
|
|
@@ -10162,13 +11088,13 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10162
11088
|
return result;
|
|
10163
11089
|
}
|
|
10164
11090
|
/** Get all execution info (running + completed + historical). */
|
|
10165
|
-
/** Normalize an
|
|
10166
|
-
*
|
|
11091
|
+
/** Normalize an execution loaded from a `StateStore` so the
|
|
11092
|
+
* historical event union holds for downstream consumers
|
|
10167
11093
|
* (Studio aggregators, REST routes, redaction). Custom or legacy stores
|
|
10168
11094
|
* may persist rows without `events` (or with a non-array value) — without
|
|
10169
11095
|
* this guard, a single malformed row crashes every iterator over
|
|
10170
|
-
* `exec.events` and takes down dependent features.
|
|
10171
|
-
*
|
|
11096
|
+
* `exec.events` and takes down dependent features. Returns a normalized
|
|
11097
|
+
* copy; logs at most one warning per `executionId`. */
|
|
10172
11098
|
normalizeStoredExecution(exec) {
|
|
10173
11099
|
if (!Array.isArray(exec.events)) {
|
|
10174
11100
|
if (!this.warnedMalformedExecutions.has(exec.executionId)) {
|
|
@@ -10177,9 +11103,9 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10177
11103
|
`[axl] StateStore returned execution ${exec.executionId} with non-array events (got ${exec.events === null ? "null" : typeof exec.events}); coercing to []`
|
|
10178
11104
|
);
|
|
10179
11105
|
}
|
|
10180
|
-
exec
|
|
11106
|
+
return normalizeStoredExecution({ ...exec, events: [] });
|
|
10181
11107
|
}
|
|
10182
|
-
return exec;
|
|
11108
|
+
return normalizeStoredExecution(exec);
|
|
10183
11109
|
}
|
|
10184
11110
|
async getExecutions() {
|
|
10185
11111
|
if (!this.historicalExecutionsLoadPromise && this.stateStore.listExecutions) {
|
|
@@ -10189,8 +11115,9 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10189
11115
|
this.historicalExecutions.set(exec.executionId, this.normalizeStoredExecution(exec));
|
|
10190
11116
|
}
|
|
10191
11117
|
}
|
|
10192
|
-
}).catch(() => {
|
|
11118
|
+
}).catch((error) => {
|
|
10193
11119
|
this.historicalExecutionsLoadPromise = null;
|
|
11120
|
+
throw error;
|
|
10194
11121
|
});
|
|
10195
11122
|
}
|
|
10196
11123
|
if (this.historicalExecutionsLoadPromise) {
|
|
@@ -10208,6 +11135,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10208
11135
|
* and automatically emits trace events and tracks cost.
|
|
10209
11136
|
*/
|
|
10210
11137
|
createContext(options) {
|
|
11138
|
+
assertNoRemovedObservationCallbacks(options);
|
|
10211
11139
|
const executionId = randomUUID3();
|
|
10212
11140
|
const budgetLimit = options?.budget ? parseCost(options.budget) : Infinity;
|
|
10213
11141
|
this.registerWithCostScope(executionId);
|
|
@@ -10223,10 +11151,8 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10223
11151
|
memoryManager: this.memoryManager,
|
|
10224
11152
|
sessionHistory: options?.sessionHistory,
|
|
10225
11153
|
signal: options?.signal,
|
|
10226
|
-
onToken: options?.onToken,
|
|
10227
|
-
onToolCall: options?.onToolCall,
|
|
10228
|
-
onAgentStart: options?.onAgentStart,
|
|
10229
11154
|
awaitHumanHandler: options?.awaitHumanHandler,
|
|
11155
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
10230
11156
|
eventStreamOptions: options?.events,
|
|
10231
11157
|
onTrace: (event) => {
|
|
10232
11158
|
this.emit("trace", event);
|
|
@@ -10260,6 +11186,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10260
11186
|
);
|
|
10261
11187
|
}
|
|
10262
11188
|
const validated = workflow2.inputSchema.parse(input);
|
|
11189
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(options?.branchDrainTimeoutMs);
|
|
10263
11190
|
const executionId = randomUUID3();
|
|
10264
11191
|
const controller = new AbortController();
|
|
10265
11192
|
this.abortControllers.set(executionId, controller);
|
|
@@ -10272,11 +11199,13 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10272
11199
|
executionId,
|
|
10273
11200
|
workflow: name,
|
|
10274
11201
|
status: "running",
|
|
11202
|
+
eventSchemaVersion: 2,
|
|
10275
11203
|
events: [],
|
|
10276
11204
|
totalCost: 0,
|
|
10277
11205
|
unpriced: false,
|
|
10278
11206
|
startedAt: Date.now(),
|
|
10279
11207
|
duration: 0,
|
|
11208
|
+
observation: { complete: true },
|
|
10280
11209
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
10281
11210
|
};
|
|
10282
11211
|
this.executions.set(executionId, execInfo);
|
|
@@ -10290,6 +11219,8 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10290
11219
|
sessionHistory,
|
|
10291
11220
|
signal: controller.signal,
|
|
10292
11221
|
eventStreamOptions: options?.events,
|
|
11222
|
+
branchDrainTimeoutMs,
|
|
11223
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
10293
11224
|
onTrace: (event) => {
|
|
10294
11225
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
10295
11226
|
execInfo.totalCost += eventCostContribution(event);
|
|
@@ -10334,7 +11265,6 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10334
11265
|
workflowName: name,
|
|
10335
11266
|
mcpManager: this.mcpManager,
|
|
10336
11267
|
memoryManager: this.memoryManager,
|
|
10337
|
-
resumeMode: !!options?.metadata?.resumeMode,
|
|
10338
11268
|
spanManager: this.spanManager,
|
|
10339
11269
|
budgetContext: {
|
|
10340
11270
|
totalCost: 0,
|
|
@@ -10365,6 +11295,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10365
11295
|
}
|
|
10366
11296
|
/** Execute a workflow and return a stream. */
|
|
10367
11297
|
stream(name, input, options) {
|
|
11298
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(options?.branchDrainTimeoutMs);
|
|
10368
11299
|
const axlStream = new AxlStream(options?.events);
|
|
10369
11300
|
const controller = new AbortController();
|
|
10370
11301
|
axlStream.on("close", () => controller.abort());
|
|
@@ -10385,11 +11316,13 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10385
11316
|
executionId,
|
|
10386
11317
|
workflow: name,
|
|
10387
11318
|
status: "running",
|
|
11319
|
+
eventSchemaVersion: 2,
|
|
10388
11320
|
events: [],
|
|
10389
11321
|
totalCost: 0,
|
|
10390
11322
|
unpriced: false,
|
|
10391
11323
|
startedAt: Date.now(),
|
|
10392
11324
|
duration: 0,
|
|
11325
|
+
observation: { complete: true },
|
|
10393
11326
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
10394
11327
|
};
|
|
10395
11328
|
this.executions.set(executionId, execInfo);
|
|
@@ -10402,12 +11335,11 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10402
11335
|
sessionHistory,
|
|
10403
11336
|
signal: controller.signal,
|
|
10404
11337
|
eventStreamOptions: options?.events,
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
//
|
|
10408
|
-
//
|
|
10409
|
-
|
|
10410
|
-
},
|
|
11338
|
+
branchDrainTimeoutMs,
|
|
11339
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
11340
|
+
// Transport mode is explicit internal state. It is inherited by child
|
|
11341
|
+
// contexts and does not allocate an unconsumed ctx.events queue.
|
|
11342
|
+
_forceStreaming: true,
|
|
10411
11343
|
onTrace: (event) => {
|
|
10412
11344
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
10413
11345
|
execInfo.totalCost += eventCostContribution(event);
|
|
@@ -10453,7 +11385,6 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10453
11385
|
workflowName: name,
|
|
10454
11386
|
mcpManager: this.mcpManager,
|
|
10455
11387
|
memoryManager: this.memoryManager,
|
|
10456
|
-
resumeMode: !!options?.metadata?.resumeMode,
|
|
10457
11388
|
spanManager: this.spanManager,
|
|
10458
11389
|
budgetContext: {
|
|
10459
11390
|
totalCost: 0,
|
|
@@ -10617,6 +11548,12 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10617
11548
|
await this.stateStore.finalizeStreamingEvents?.(id);
|
|
10618
11549
|
continue;
|
|
10619
11550
|
}
|
|
11551
|
+
const eventSchemaVersion = getEventSchemaVersion(events[0]);
|
|
11552
|
+
for (const event of events) {
|
|
11553
|
+
if (getEventSchemaVersion(event) !== eventSchemaVersion) {
|
|
11554
|
+
throw new Error(`Streaming buffer ${id} contains mixed event schema versions`);
|
|
11555
|
+
}
|
|
11556
|
+
}
|
|
10620
11557
|
const boundedEvents = boundRecoveredEvents(events, this.maxEventsPerExecution);
|
|
10621
11558
|
const workflowStartEvent = boundedEvents.find((e) => e.type === "workflow_start");
|
|
10622
11559
|
const lastEvent = boundedEvents[boundedEvents.length - 1];
|
|
@@ -10625,7 +11562,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10625
11562
|
const workflowName = workflowStartEvent?.workflow ?? RECOVERED_UNKNOWN_WORKFLOW;
|
|
10626
11563
|
const totalCost = boundedEvents.reduce((sum, e) => sum + eventCostContribution(e), 0);
|
|
10627
11564
|
const unpriced = boundedEvents.some(isUnpricedLeaf);
|
|
10628
|
-
const
|
|
11565
|
+
const executionBase = {
|
|
10629
11566
|
executionId: id,
|
|
10630
11567
|
workflow: workflowName,
|
|
10631
11568
|
status: "failed",
|
|
@@ -10637,6 +11574,16 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10637
11574
|
duration: completedAt - startedAt,
|
|
10638
11575
|
error: "process terminated (recovered from streaming buffer)"
|
|
10639
11576
|
};
|
|
11577
|
+
const synthesized = eventSchemaVersion === 2 ? {
|
|
11578
|
+
...executionBase,
|
|
11579
|
+
eventSchemaVersion: 2,
|
|
11580
|
+
events: boundedEvents,
|
|
11581
|
+
observation: { complete: false, reason: "process_interrupted" }
|
|
11582
|
+
} : {
|
|
11583
|
+
...executionBase,
|
|
11584
|
+
eventSchemaVersion: 1,
|
|
11585
|
+
events: boundedEvents
|
|
11586
|
+
};
|
|
10640
11587
|
if (this.stateStore.saveExecution) {
|
|
10641
11588
|
try {
|
|
10642
11589
|
await this.stateStore.saveExecution(synthesized);
|
|
@@ -10717,7 +11664,8 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10717
11664
|
*/
|
|
10718
11665
|
async deleteExecution(id) {
|
|
10719
11666
|
const wasActive = this.executions.has(id);
|
|
10720
|
-
const
|
|
11667
|
+
const pendingDecisionResolver = this.pendingDecisionResolvers.get(id);
|
|
11668
|
+
const hadPendingDecision = pendingDecisionResolver !== void 0;
|
|
10721
11669
|
if (wasActive) {
|
|
10722
11670
|
this.pendingDeletedExecutions.add(id);
|
|
10723
11671
|
this.streamableExecutionIds.delete(id);
|
|
@@ -10726,8 +11674,12 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10726
11674
|
controller.abort();
|
|
10727
11675
|
}
|
|
10728
11676
|
}
|
|
10729
|
-
if (
|
|
10730
|
-
|
|
11677
|
+
if (pendingDecisionResolver) pendingDecisionResolver.cancelled = true;
|
|
11678
|
+
if (pendingDecisionResolver) {
|
|
11679
|
+
await pendingDecisionResolver.cleanupFinished;
|
|
11680
|
+
if (this.pendingDecisionResolvers.get(id) === pendingDecisionResolver) {
|
|
11681
|
+
this.pendingDecisionResolvers.delete(id);
|
|
11682
|
+
}
|
|
10731
11683
|
}
|
|
10732
11684
|
const existing = await this.getExecution(id);
|
|
10733
11685
|
const workflow2 = existing?.workflow;
|
|
@@ -10805,60 +11757,68 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10805
11757
|
}
|
|
10806
11758
|
/** Resolve a pending human decision. */
|
|
10807
11759
|
async resolveDecision(executionId, decision) {
|
|
10808
|
-
|
|
11760
|
+
decision = parseHumanDecision(decision);
|
|
11761
|
+
let resolver = this.pendingDecisionResolvers.get(executionId);
|
|
10809
11762
|
if (resolver) {
|
|
10810
|
-
resolver
|
|
10811
|
-
|
|
11763
|
+
if (resolver.cancelled) {
|
|
11764
|
+
throw new AxlError(
|
|
11765
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11766
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
11767
|
+
);
|
|
11768
|
+
}
|
|
11769
|
+
await resolver.ready;
|
|
11770
|
+
if (resolver.cancelled || resolver.state !== "pending" || this.pendingDecisionResolvers.get(executionId) !== resolver) {
|
|
11771
|
+
throw new AxlError(
|
|
11772
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11773
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
11774
|
+
);
|
|
11775
|
+
}
|
|
10812
11776
|
}
|
|
10813
|
-
await this.stateStore.resolveDecision(executionId, decision);
|
|
10814
11777
|
if (!resolver) {
|
|
10815
|
-
const
|
|
10816
|
-
|
|
10817
|
-
|
|
11778
|
+
const pending = await this.stateStore.getPendingDecisions();
|
|
11779
|
+
resolver = this.pendingDecisionResolvers.get(executionId);
|
|
11780
|
+
if (resolver) {
|
|
11781
|
+
if (resolver.cancelled) {
|
|
11782
|
+
throw new AxlError(
|
|
11783
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11784
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
11785
|
+
);
|
|
11786
|
+
}
|
|
11787
|
+
await resolver.ready;
|
|
11788
|
+
if (resolver.cancelled || resolver.state !== "pending" || this.pendingDecisionResolvers.get(executionId) !== resolver) {
|
|
11789
|
+
throw new AxlError(
|
|
11790
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11791
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
11792
|
+
);
|
|
11793
|
+
}
|
|
11794
|
+
} else if (!pending.some((request) => request.executionId === executionId)) {
|
|
11795
|
+
throw new AxlError(
|
|
11796
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11797
|
+
`No pending human decision exists for execution "${executionId}".`
|
|
11798
|
+
);
|
|
11799
|
+
} else {
|
|
11800
|
+
throw new AxlError(
|
|
11801
|
+
"CROSS_PROCESS_RESUME_UNSUPPORTED",
|
|
11802
|
+
`Execution "${executionId}" is not awaiting a decision in this runtime. Cross-process approval replay requires a durable decision/lease lineage and is not yet supported; any persisted pending request was preserved.`
|
|
11803
|
+
);
|
|
10818
11804
|
}
|
|
10819
11805
|
}
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
* Re-runs the workflow from scratch; the workflow should use checkpoint-replay
|
|
10824
|
-
* to skip already-completed steps and pick up from the awaitHuman point.
|
|
10825
|
-
*/
|
|
10826
|
-
async resumeExecution(executionId) {
|
|
10827
|
-
const state = await this.stateStore.getExecutionState(executionId);
|
|
10828
|
-
if (!state) {
|
|
10829
|
-
throw new Error(`No execution state found for "${executionId}"`);
|
|
10830
|
-
}
|
|
10831
|
-
const workflow2 = this.workflows.get(state.workflow);
|
|
10832
|
-
if (!workflow2) {
|
|
10833
|
-
throw new Error(
|
|
10834
|
-
`Workflow "${state.workflow}" not registered. Cannot resume execution "${executionId}".`
|
|
10835
|
-
);
|
|
10836
|
-
}
|
|
10837
|
-
return this.execute(state.workflow, state.input, {
|
|
10838
|
-
metadata: { ...state.metadata, resumedFrom: executionId, resumeMode: true }
|
|
11806
|
+
let finishResolution;
|
|
11807
|
+
const resolutionFinished = new Promise((resolve2) => {
|
|
11808
|
+
finishResolution = resolve2;
|
|
10839
11809
|
});
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
await this.resumeExecution(executionId);
|
|
10852
|
-
resumed.push(executionId);
|
|
10853
|
-
} catch (err) {
|
|
10854
|
-
this.emit("error", {
|
|
10855
|
-
type: "resume_failed",
|
|
10856
|
-
executionId,
|
|
10857
|
-
error: err instanceof Error ? err.message : String(err)
|
|
10858
|
-
});
|
|
10859
|
-
}
|
|
11810
|
+
resolver.state = "resolving";
|
|
11811
|
+
resolver.resolution = resolutionFinished;
|
|
11812
|
+
try {
|
|
11813
|
+
await this.stateStore.resolveDecision(executionId, decision);
|
|
11814
|
+
resolver.state = "resolved";
|
|
11815
|
+
finishResolution(true);
|
|
11816
|
+
resolver.resolve(decision);
|
|
11817
|
+
} catch (error) {
|
|
11818
|
+
resolver.state = "pending";
|
|
11819
|
+
finishResolution(false);
|
|
11820
|
+
throw error;
|
|
10860
11821
|
}
|
|
10861
|
-
return resumed;
|
|
10862
11822
|
}
|
|
10863
11823
|
/**
|
|
10864
11824
|
* Summarize a list of chat messages into a concise summary string.
|
|
@@ -11760,28 +12720,32 @@ var RedisStore = class _RedisStore {
|
|
|
11760
12720
|
async saveExecution(execution) {
|
|
11761
12721
|
const ttl = this.ttlFor("executionHistory");
|
|
11762
12722
|
const setOptions = ttl !== void 0 ? { EX: ttl } : void 0;
|
|
11763
|
-
|
|
12723
|
+
const storedExecution = normalizeStoredExecution({
|
|
12724
|
+
...execution,
|
|
12725
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
12726
|
+
});
|
|
12727
|
+
await this.client.multi().sAdd(this.execHistorySetKey(), execution.executionId).set(this.execHistoryKey(execution.executionId), JSON.stringify(storedExecution), setOptions).zAdd(this.execHistoryZsetKey(), {
|
|
11764
12728
|
score: execution.startedAt,
|
|
11765
12729
|
value: execution.executionId
|
|
11766
12730
|
}).exec();
|
|
11767
12731
|
}
|
|
11768
12732
|
async getExecution(executionId) {
|
|
11769
12733
|
const raw = await this.client.get(this.execHistoryKey(executionId));
|
|
11770
|
-
return raw ? JSON.parse(raw) : null;
|
|
12734
|
+
return raw ? normalizeStoredExecution(JSON.parse(raw)) : null;
|
|
11771
12735
|
}
|
|
11772
12736
|
async listExecutions(limit) {
|
|
11773
|
-
|
|
12737
|
+
const executions = await this.listHistoryByZset(
|
|
11774
12738
|
this.execHistorySetKey(),
|
|
11775
12739
|
this.execHistoryZsetKey(),
|
|
11776
12740
|
(id) => this.execHistoryKey(id),
|
|
11777
12741
|
(a, b) => b.startedAt - a.startedAt,
|
|
11778
12742
|
limit
|
|
11779
12743
|
);
|
|
12744
|
+
return executions.map(normalizeStoredExecution);
|
|
11780
12745
|
}
|
|
11781
12746
|
async deleteExecution(executionId) {
|
|
11782
|
-
const
|
|
11783
|
-
|
|
11784
|
-
return deleted > 0;
|
|
12747
|
+
const results = await this.client.multi().sRem(this.execHistorySetKey(), executionId).del(this.execHistoryKey(executionId)).zRem(this.execHistoryZsetKey(), executionId).del(this.checkpointKey(executionId)).del(this.executionStateKey(executionId)).sRem(this.pendingExecSetKey(), executionId).del(this.streamingEventsKey(executionId)).sRem(this.streamingIdsKey(), executionId).hDel(this.decisionsKey(), executionId).exec();
|
|
12748
|
+
return results.some((result) => typeof result === "number" && result > 0);
|
|
11785
12749
|
}
|
|
11786
12750
|
// ── Eval History ────────────────────────────────────────────────────
|
|
11787
12751
|
async saveEvalResult(entry) {
|
|
@@ -11873,14 +12837,15 @@ var RedisStore = class _RedisStore {
|
|
|
11873
12837
|
async getStreamingEvents(executionId) {
|
|
11874
12838
|
const raws = await this.client.lRange(this.streamingEventsKey(executionId), 0, -1);
|
|
11875
12839
|
if (raws.length === 0) return [];
|
|
11876
|
-
|
|
11877
|
-
for (const raw of raws) {
|
|
12840
|
+
return raws.map((raw, index) => {
|
|
11878
12841
|
try {
|
|
11879
|
-
|
|
11880
|
-
} catch {
|
|
12842
|
+
return JSON.parse(raw);
|
|
12843
|
+
} catch (error) {
|
|
12844
|
+
throw new Error(`Streaming buffer ${executionId} contains invalid JSON at index ${index}`, {
|
|
12845
|
+
cause: error
|
|
12846
|
+
});
|
|
11881
12847
|
}
|
|
11882
|
-
}
|
|
11883
|
-
return events;
|
|
12848
|
+
});
|
|
11884
12849
|
}
|
|
11885
12850
|
/** Close the Redis connection. */
|
|
11886
12851
|
async close() {
|
|
@@ -12091,6 +13056,8 @@ function cosineSimilarity2(a, b) {
|
|
|
12091
13056
|
}
|
|
12092
13057
|
export {
|
|
12093
13058
|
AXL_EVENT_TYPES,
|
|
13059
|
+
AXL_EVENT_TYPES_V2,
|
|
13060
|
+
AXL_TOOL_LIFECYCLE_TYPES_V2,
|
|
12094
13061
|
AZURE_PROFILE,
|
|
12095
13062
|
AnthropicProvider,
|
|
12096
13063
|
AxlError,
|
|
@@ -12138,8 +13105,9 @@ export {
|
|
|
12138
13105
|
SqliteVectorStore,
|
|
12139
13106
|
ThinkTagScanner,
|
|
12140
13107
|
TimeoutError,
|
|
12141
|
-
|
|
13108
|
+
ToolFailure,
|
|
12142
13109
|
ToolModelOutputError,
|
|
13110
|
+
UnsupportedEventSchemaVersionError,
|
|
12143
13111
|
VLLM_PROFILE,
|
|
12144
13112
|
ValidationError,
|
|
12145
13113
|
VerifyError,
|
|
@@ -12154,6 +13122,8 @@ export {
|
|
|
12154
13122
|
extractJson,
|
|
12155
13123
|
extractThinkTags,
|
|
12156
13124
|
findConfig,
|
|
13125
|
+
getEventSchemaVersion,
|
|
13126
|
+
getExecutionEventSchemaVersion,
|
|
12157
13127
|
hasPositiveTokens,
|
|
12158
13128
|
importModule,
|
|
12159
13129
|
isCostBearingLeaf,
|
|
@@ -12162,6 +13132,7 @@ export {
|
|
|
12162
13132
|
isUnpricedLeaf,
|
|
12163
13133
|
isUsableCost,
|
|
12164
13134
|
needsTsxLoader,
|
|
13135
|
+
normalizeStoredExecution,
|
|
12165
13136
|
openaiReasoningEmit,
|
|
12166
13137
|
parsePartialJson,
|
|
12167
13138
|
pickDefault,
|
|
@@ -12170,6 +13141,7 @@ export {
|
|
|
12170
13141
|
reasoningEffortEmit,
|
|
12171
13142
|
reasoningObjectEmit,
|
|
12172
13143
|
redactEvent,
|
|
13144
|
+
redactHistoricalEvent,
|
|
12173
13145
|
registerConditions,
|
|
12174
13146
|
resolveApiKey,
|
|
12175
13147
|
resolvePerModel,
|