@axlsdk/axl 0.19.0 → 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 +31 -10
- package/dist/{chunk-JBLQKU6X.js → chunk-NVPA4RY6.js} +6 -4
- package/dist/chunk-NVPA4RY6.js.map +1 -0
- package/dist/index.cjs +2005 -791
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +518 -251
- package/dist/index.d.ts +518 -251
- package/dist/index.js +1988 -784
- 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,28 +1,258 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__require
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NVPA4RY6.js";
|
|
4
|
+
|
|
5
|
+
// src/tool.ts
|
|
6
|
+
import { ZodError } from "zod";
|
|
7
|
+
|
|
8
|
+
// src/errors.ts
|
|
9
|
+
var AxlError = class extends Error {
|
|
10
|
+
code;
|
|
11
|
+
constructor(code, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "AxlError";
|
|
14
|
+
this.code = code;
|
|
15
|
+
}
|
|
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
|
+
};
|
|
66
|
+
var ToolModelOutputError = class extends AxlError {
|
|
67
|
+
toolName;
|
|
68
|
+
cause;
|
|
69
|
+
constructor(toolName, cause) {
|
|
70
|
+
super("TOOL_MODEL_OUTPUT_ERROR", `Failed to prepare model output for tool "${toolName}"`);
|
|
71
|
+
this.name = "ToolModelOutputError";
|
|
72
|
+
this.toolName = toolName;
|
|
73
|
+
this.cause = cause;
|
|
74
|
+
Object.defineProperty(this, "cause", { enumerable: false });
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var VerifyError = class extends AxlError {
|
|
78
|
+
lastOutput;
|
|
79
|
+
zodError;
|
|
80
|
+
retries;
|
|
81
|
+
constructor(lastOutput, zodError, retries) {
|
|
82
|
+
super("VERIFY_ERROR", `Schema validation failed after ${retries} retries: ${zodError.message}`);
|
|
83
|
+
this.name = "VerifyError";
|
|
84
|
+
this.lastOutput = lastOutput;
|
|
85
|
+
this.zodError = zodError;
|
|
86
|
+
this.retries = retries;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var QuorumNotMet = class extends AxlError {
|
|
90
|
+
results;
|
|
91
|
+
constructor(required, actual, results) {
|
|
92
|
+
super("QUORUM_NOT_MET", `Quorum not met: needed ${required} successes, got ${actual}`);
|
|
93
|
+
this.name = "QuorumNotMet";
|
|
94
|
+
this.results = results;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var NoConsensus = class extends AxlError {
|
|
98
|
+
constructor(reason) {
|
|
99
|
+
super("NO_CONSENSUS", `No consensus: ${reason}`);
|
|
100
|
+
this.name = "NoConsensus";
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var TimeoutError = class extends AxlError {
|
|
104
|
+
constructor(operation, timeoutMs) {
|
|
105
|
+
super("TIMEOUT", `${operation} exceeded timeout of ${timeoutMs}ms`);
|
|
106
|
+
this.name = "TimeoutError";
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
function formatBudgetCost(cost) {
|
|
110
|
+
if (Number.isNaN(cost)) return "$NaN";
|
|
111
|
+
if (cost === Infinity) return "$Infinity";
|
|
112
|
+
if (cost === -Infinity) return "-$Infinity";
|
|
113
|
+
if (cost === 0) return "$0.00";
|
|
114
|
+
const sign = cost < 0 ? "-" : "";
|
|
115
|
+
const abs = Math.abs(cost);
|
|
116
|
+
if (abs < 1e-6) return `${sign}< $0.000001`;
|
|
117
|
+
if (abs < 1e-4) {
|
|
118
|
+
const [mantissa, exponent] = abs.toExponential(2).split("e");
|
|
119
|
+
return `${sign}$${mantissa}e${parseInt(exponent, 10)}`;
|
|
120
|
+
}
|
|
121
|
+
if (abs < 0.01) return `${sign}$${abs.toFixed(6)}`;
|
|
122
|
+
return `${sign}$${abs.toFixed(2)}`;
|
|
123
|
+
}
|
|
124
|
+
var BudgetExceededError = class extends AxlError {
|
|
125
|
+
limit;
|
|
126
|
+
spent;
|
|
127
|
+
policy;
|
|
128
|
+
constructor(limit, spent, policy) {
|
|
129
|
+
super(
|
|
130
|
+
"BUDGET_EXCEEDED",
|
|
131
|
+
`Budget exceeded: spent ${formatBudgetCost(spent)} of ${formatBudgetCost(
|
|
132
|
+
limit
|
|
133
|
+
)} limit (policy: ${policy})`
|
|
134
|
+
);
|
|
135
|
+
this.name = "BudgetExceededError";
|
|
136
|
+
this.limit = limit;
|
|
137
|
+
this.spent = spent;
|
|
138
|
+
this.policy = policy;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
var MaxTurnsError = class extends AxlError {
|
|
142
|
+
maxTurns;
|
|
143
|
+
constructor(operation, maxTurns) {
|
|
144
|
+
super("MAX_TURNS", `${operation} exceeded maximum of ${maxTurns} turns`);
|
|
145
|
+
this.name = "MaxTurnsError";
|
|
146
|
+
this.maxTurns = maxTurns;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var GuardrailError = class extends AxlError {
|
|
150
|
+
guardrailType;
|
|
151
|
+
reason;
|
|
152
|
+
constructor(guardrailType, reason) {
|
|
153
|
+
super("GUARDRAIL_BLOCKED", `${guardrailType} guardrail blocked: ${reason}`);
|
|
154
|
+
this.name = "GuardrailError";
|
|
155
|
+
this.guardrailType = guardrailType;
|
|
156
|
+
this.reason = reason;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var ValidationError = class extends AxlError {
|
|
160
|
+
lastOutput;
|
|
161
|
+
reason;
|
|
162
|
+
retries;
|
|
163
|
+
constructor(lastOutput, reason, retries) {
|
|
164
|
+
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
165
|
+
this.name = "ValidationError";
|
|
166
|
+
this.lastOutput = lastOutput;
|
|
167
|
+
this.reason = reason;
|
|
168
|
+
this.retries = retries;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
4
171
|
|
|
5
172
|
// src/tool.ts
|
|
6
173
|
var DEFAULT_MAX_STRING_LENGTH = 1e4;
|
|
7
|
-
|
|
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
|
+
);
|
|
180
|
+
super(
|
|
181
|
+
`String argument${renderedPath ? ` at "${renderedPath}"` : ""} exceeds maximum length (${actual} > ${maximum})`
|
|
182
|
+
);
|
|
183
|
+
this.path = path;
|
|
184
|
+
this.actual = actual;
|
|
185
|
+
this.maximum = maximum;
|
|
186
|
+
this.name = "ToolStringLengthError";
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
function validateStringLengths(value, maxLen, path = []) {
|
|
8
190
|
if (typeof value === "string") {
|
|
9
191
|
if (value.length > maxLen) {
|
|
10
|
-
throw new
|
|
11
|
-
`String argument${path ? ` at "${path}"` : ""} exceeds maximum length (${value.length} > ${maxLen})`
|
|
12
|
-
);
|
|
192
|
+
throw new ToolStringLengthError(path, value.length, maxLen);
|
|
13
193
|
}
|
|
14
194
|
} else if (Array.isArray(value)) {
|
|
15
195
|
for (let i = 0; i < value.length; i++) {
|
|
16
|
-
validateStringLengths(value[i], maxLen, path
|
|
196
|
+
validateStringLengths(value[i], maxLen, [...path, i]);
|
|
17
197
|
}
|
|
18
198
|
} else if (value !== null && typeof value === "object") {
|
|
19
199
|
for (const [key, val] of Object.entries(value)) {
|
|
20
|
-
validateStringLengths(val, maxLen, path
|
|
200
|
+
validateStringLengths(val, maxLen, [...path, key]);
|
|
21
201
|
}
|
|
22
202
|
}
|
|
23
203
|
}
|
|
24
|
-
function sleep(ms) {
|
|
25
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
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);
|
|
26
256
|
}
|
|
27
257
|
function getBackoffMs(attempt, strategy) {
|
|
28
258
|
switch (strategy) {
|
|
@@ -41,17 +271,27 @@ function tool(config) {
|
|
|
41
271
|
on: config.retry?.on
|
|
42
272
|
};
|
|
43
273
|
const maxStringLen = config.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH;
|
|
44
|
-
const
|
|
274
|
+
const prepareInput = (input) => {
|
|
45
275
|
const parsed = config.input.parse(input);
|
|
46
276
|
if (maxStringLen > 0) {
|
|
47
277
|
validateStringLengths(parsed, maxStringLen);
|
|
48
278
|
}
|
|
279
|
+
return parsed;
|
|
280
|
+
};
|
|
281
|
+
const executePrepared = async (parsed, ctx, options) => {
|
|
49
282
|
const maxAttempts = retryPolicy.attempts ?? 1;
|
|
50
283
|
let lastError;
|
|
51
284
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
285
|
+
options?.signal?.throwIfAborted();
|
|
286
|
+
options?.onAttempt?.(attempt);
|
|
52
287
|
try {
|
|
53
|
-
|
|
288
|
+
const result = await config.handler(parsed, ctx);
|
|
289
|
+
if (options?.checkAfterHandlerAbort !== false) options?.signal?.throwIfAborted();
|
|
290
|
+
return result;
|
|
54
291
|
} catch (err) {
|
|
292
|
+
if (options?.signal?.aborted) options.signal.throwIfAborted();
|
|
293
|
+
if (isAbortError(err)) throw err;
|
|
294
|
+
rethrowEventStreamOverflow(err);
|
|
55
295
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
56
296
|
if (attempt === maxAttempts) break;
|
|
57
297
|
if (retryPolicy.on && !retryPolicy.on(lastError)) {
|
|
@@ -59,13 +299,14 @@ function tool(config) {
|
|
|
59
299
|
}
|
|
60
300
|
const backoffMs = getBackoffMs(attempt, retryPolicy.backoff ?? "exponential");
|
|
61
301
|
if (backoffMs > 0) {
|
|
62
|
-
await sleep(backoffMs);
|
|
302
|
+
await sleep(backoffMs, options?.signal);
|
|
63
303
|
}
|
|
64
304
|
}
|
|
65
305
|
}
|
|
66
306
|
throw lastError;
|
|
67
307
|
};
|
|
68
|
-
|
|
308
|
+
const execute = async (input, ctx) => executePrepared(prepareInput(input), ctx);
|
|
309
|
+
const instance = {
|
|
69
310
|
name: config.name,
|
|
70
311
|
description: config.description,
|
|
71
312
|
inputSchema: config.input,
|
|
@@ -73,6 +314,7 @@ function tool(config) {
|
|
|
73
314
|
retry: retryPolicy,
|
|
74
315
|
requireApproval: config.requireApproval ?? false,
|
|
75
316
|
hooks: config.hooks,
|
|
317
|
+
...config.toModelOutput !== void 0 ? { toModelOutput: config.toModelOutput } : {},
|
|
76
318
|
async run(ctx, input) {
|
|
77
319
|
const startTime = Date.now();
|
|
78
320
|
try {
|
|
@@ -100,6 +342,11 @@ function tool(config) {
|
|
|
100
342
|
},
|
|
101
343
|
_execute: execute
|
|
102
344
|
};
|
|
345
|
+
TOOL_INTERNALS.set(instance, {
|
|
346
|
+
prepareInput,
|
|
347
|
+
executePrepared
|
|
348
|
+
});
|
|
349
|
+
return instance;
|
|
103
350
|
}
|
|
104
351
|
|
|
105
352
|
// src/providers/types.ts
|
|
@@ -125,123 +372,6 @@ async function resolveApiKey(source) {
|
|
|
125
372
|
return source ?? "";
|
|
126
373
|
}
|
|
127
374
|
|
|
128
|
-
// src/errors.ts
|
|
129
|
-
var AxlError = class extends Error {
|
|
130
|
-
code;
|
|
131
|
-
constructor(code, message) {
|
|
132
|
-
super(message);
|
|
133
|
-
this.name = "AxlError";
|
|
134
|
-
this.code = code;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
var VerifyError = class extends AxlError {
|
|
138
|
-
lastOutput;
|
|
139
|
-
zodError;
|
|
140
|
-
retries;
|
|
141
|
-
constructor(lastOutput, zodError, retries) {
|
|
142
|
-
super("VERIFY_ERROR", `Schema validation failed after ${retries} retries: ${zodError.message}`);
|
|
143
|
-
this.name = "VerifyError";
|
|
144
|
-
this.lastOutput = lastOutput;
|
|
145
|
-
this.zodError = zodError;
|
|
146
|
-
this.retries = retries;
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
var QuorumNotMet = class extends AxlError {
|
|
150
|
-
results;
|
|
151
|
-
constructor(required, actual, results) {
|
|
152
|
-
super("QUORUM_NOT_MET", `Quorum not met: needed ${required} successes, got ${actual}`);
|
|
153
|
-
this.name = "QuorumNotMet";
|
|
154
|
-
this.results = results;
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
var NoConsensus = class extends AxlError {
|
|
158
|
-
constructor(reason) {
|
|
159
|
-
super("NO_CONSENSUS", `No consensus: ${reason}`);
|
|
160
|
-
this.name = "NoConsensus";
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
var TimeoutError = class extends AxlError {
|
|
164
|
-
constructor(operation, timeoutMs) {
|
|
165
|
-
super("TIMEOUT", `${operation} exceeded timeout of ${timeoutMs}ms`);
|
|
166
|
-
this.name = "TimeoutError";
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
function formatBudgetCost(cost) {
|
|
170
|
-
if (Number.isNaN(cost)) return "$NaN";
|
|
171
|
-
if (cost === Infinity) return "$Infinity";
|
|
172
|
-
if (cost === -Infinity) return "-$Infinity";
|
|
173
|
-
if (cost === 0) return "$0.00";
|
|
174
|
-
const sign = cost < 0 ? "-" : "";
|
|
175
|
-
const abs = Math.abs(cost);
|
|
176
|
-
if (abs < 1e-6) return `${sign}< $0.000001`;
|
|
177
|
-
if (abs < 1e-4) {
|
|
178
|
-
const [mantissa, exponent] = abs.toExponential(2).split("e");
|
|
179
|
-
return `${sign}$${mantissa}e${parseInt(exponent, 10)}`;
|
|
180
|
-
}
|
|
181
|
-
if (abs < 0.01) return `${sign}$${abs.toFixed(6)}`;
|
|
182
|
-
return `${sign}$${abs.toFixed(2)}`;
|
|
183
|
-
}
|
|
184
|
-
var BudgetExceededError = class extends AxlError {
|
|
185
|
-
limit;
|
|
186
|
-
spent;
|
|
187
|
-
policy;
|
|
188
|
-
constructor(limit, spent, policy) {
|
|
189
|
-
super(
|
|
190
|
-
"BUDGET_EXCEEDED",
|
|
191
|
-
`Budget exceeded: spent ${formatBudgetCost(spent)} of ${formatBudgetCost(
|
|
192
|
-
limit
|
|
193
|
-
)} limit (policy: ${policy})`
|
|
194
|
-
);
|
|
195
|
-
this.name = "BudgetExceededError";
|
|
196
|
-
this.limit = limit;
|
|
197
|
-
this.spent = spent;
|
|
198
|
-
this.policy = policy;
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
var MaxTurnsError = class extends AxlError {
|
|
202
|
-
maxTurns;
|
|
203
|
-
constructor(operation, maxTurns) {
|
|
204
|
-
super("MAX_TURNS", `${operation} exceeded maximum of ${maxTurns} turns`);
|
|
205
|
-
this.name = "MaxTurnsError";
|
|
206
|
-
this.maxTurns = maxTurns;
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
var GuardrailError = class extends AxlError {
|
|
210
|
-
guardrailType;
|
|
211
|
-
reason;
|
|
212
|
-
constructor(guardrailType, reason) {
|
|
213
|
-
super("GUARDRAIL_BLOCKED", `${guardrailType} guardrail blocked: ${reason}`);
|
|
214
|
-
this.name = "GuardrailError";
|
|
215
|
-
this.guardrailType = guardrailType;
|
|
216
|
-
this.reason = reason;
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
var ValidationError = class extends AxlError {
|
|
220
|
-
lastOutput;
|
|
221
|
-
reason;
|
|
222
|
-
retries;
|
|
223
|
-
constructor(lastOutput, reason, retries) {
|
|
224
|
-
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
225
|
-
this.name = "ValidationError";
|
|
226
|
-
this.lastOutput = lastOutput;
|
|
227
|
-
this.reason = reason;
|
|
228
|
-
this.retries = retries;
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
var ToolDenied = class extends AxlError {
|
|
232
|
-
toolName;
|
|
233
|
-
agentName;
|
|
234
|
-
constructor(toolName, agentName) {
|
|
235
|
-
super(
|
|
236
|
-
"TOOL_DENIED",
|
|
237
|
-
`Agent "${agentName}" attempted to call tool "${toolName}" which is not in its ACL`
|
|
238
|
-
);
|
|
239
|
-
this.name = "ToolDenied";
|
|
240
|
-
this.toolName = toolName;
|
|
241
|
-
this.agentName = agentName;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
|
|
245
375
|
// src/providers/errors.ts
|
|
246
376
|
var ProviderError = class extends AxlError {
|
|
247
377
|
/** Adapter/profile name, e.g. `'openai'`, `'anthropic'`, `'google'`. */
|
|
@@ -349,7 +479,7 @@ function sleep2(ms, signal) {
|
|
|
349
479
|
);
|
|
350
480
|
});
|
|
351
481
|
}
|
|
352
|
-
function
|
|
482
|
+
function isAbortError2(err, signal) {
|
|
353
483
|
if (signal?.aborted) return true;
|
|
354
484
|
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
355
485
|
}
|
|
@@ -368,7 +498,7 @@ async function fetchWithRetry(input, init, opts) {
|
|
|
368
498
|
try {
|
|
369
499
|
res = await fetch(input, init);
|
|
370
500
|
} catch (err) {
|
|
371
|
-
if (
|
|
501
|
+
if (isAbortError2(err, init?.signal ?? void 0)) throw err;
|
|
372
502
|
if (attempt >= maxRetries) {
|
|
373
503
|
throw buildProviderError({
|
|
374
504
|
provider,
|
|
@@ -2154,6 +2284,15 @@ function sanitizeSchemaForGemini(schema) {
|
|
|
2154
2284
|
}
|
|
2155
2285
|
return out;
|
|
2156
2286
|
}
|
|
2287
|
+
function parseGeminiFunctionResponse(content) {
|
|
2288
|
+
let parsed;
|
|
2289
|
+
try {
|
|
2290
|
+
parsed = JSON.parse(content);
|
|
2291
|
+
} catch {
|
|
2292
|
+
return { result: content };
|
|
2293
|
+
}
|
|
2294
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { result: parsed };
|
|
2295
|
+
}
|
|
2157
2296
|
var GEMINI_PRICING = {
|
|
2158
2297
|
"gemini-2.5-pro": [125e-8, 1e-5],
|
|
2159
2298
|
"gemini-2.5-flash": [3e-7, 25e-7],
|
|
@@ -2489,15 +2628,9 @@ var GeminiProvider = class {
|
|
|
2489
2628
|
}
|
|
2490
2629
|
} else if (msg.role === "tool") {
|
|
2491
2630
|
const functionName = toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
2492
|
-
let responseData;
|
|
2493
|
-
try {
|
|
2494
|
-
responseData = JSON.parse(msg.content);
|
|
2495
|
-
} catch {
|
|
2496
|
-
responseData = { result: msg.content };
|
|
2497
|
-
}
|
|
2498
2631
|
const functionResponse = {
|
|
2499
2632
|
name: functionName,
|
|
2500
|
-
response:
|
|
2633
|
+
response: parseGeminiFunctionResponse(msg.content)
|
|
2501
2634
|
};
|
|
2502
2635
|
if (msg.tool_call_id && geminiNativeIds.has(msg.tool_call_id)) {
|
|
2503
2636
|
functionResponse.id = msg.tool_call_id;
|
|
@@ -3047,7 +3180,7 @@ var defaultRegistry = new ProviderRegistry();
|
|
|
3047
3180
|
// src/context.ts
|
|
3048
3181
|
import { AsyncLocalStorage } from "async_hooks";
|
|
3049
3182
|
import { randomUUID } from "crypto";
|
|
3050
|
-
import { z, ZodError } from "zod";
|
|
3183
|
+
import { z, ZodError as ZodError2 } from "zod";
|
|
3051
3184
|
|
|
3052
3185
|
// src/partial-json.ts
|
|
3053
3186
|
var MAX_DEPTH = 256;
|
|
@@ -3666,9 +3799,9 @@ var AXL_EVENT_TYPES = [
|
|
|
3666
3799
|
// Tool invocation lifecycle
|
|
3667
3800
|
"tool_call_start",
|
|
3668
3801
|
"tool_call_end",
|
|
3802
|
+
"tool_call_rejected",
|
|
3669
3803
|
// Single-point tool events
|
|
3670
3804
|
"tool_approval",
|
|
3671
|
-
"tool_denied",
|
|
3672
3805
|
// Delegation
|
|
3673
3806
|
"delegate",
|
|
3674
3807
|
// Handoff (spans two asks; not AskScoped).
|
|
@@ -3699,7 +3832,7 @@ var AXL_EVENT_TYPES = [
|
|
|
3699
3832
|
"memory_remember",
|
|
3700
3833
|
"memory_recall",
|
|
3701
3834
|
"memory_forget",
|
|
3702
|
-
//
|
|
3835
|
+
// Scoped execution checkpoints (`ctx.checkpoint`)
|
|
3703
3836
|
"checkpoint_save",
|
|
3704
3837
|
"checkpoint_replay",
|
|
3705
3838
|
// Human-in-the-loop (`ctx.awaitHuman`)
|
|
@@ -3714,6 +3847,12 @@ var AXL_EVENT_TYPES = [
|
|
|
3714
3847
|
"done",
|
|
3715
3848
|
"error"
|
|
3716
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
|
+
];
|
|
3717
3856
|
|
|
3718
3857
|
// src/event-stream.ts
|
|
3719
3858
|
var STREAM_EVENTS = new Set(AXL_EVENT_TYPES);
|
|
@@ -3729,8 +3868,8 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3729
3868
|
"agent_call_end",
|
|
3730
3869
|
"tool_call_start",
|
|
3731
3870
|
"tool_call_end",
|
|
3871
|
+
"tool_call_rejected",
|
|
3732
3872
|
"tool_approval",
|
|
3733
|
-
"tool_denied",
|
|
3734
3873
|
"handoff_start",
|
|
3735
3874
|
"handoff_return",
|
|
3736
3875
|
"delegate",
|
|
@@ -3744,18 +3883,6 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3744
3883
|
"await_human",
|
|
3745
3884
|
"await_human_resolved"
|
|
3746
3885
|
]);
|
|
3747
|
-
var EventStreamOverflowError = class extends Error {
|
|
3748
|
-
maxQueued;
|
|
3749
|
-
eventType;
|
|
3750
|
-
constructor(maxQueued, eventType) {
|
|
3751
|
-
super(
|
|
3752
|
-
`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.`
|
|
3753
|
-
);
|
|
3754
|
-
this.name = "EventStreamOverflowError";
|
|
3755
|
-
this.maxQueued = maxQueued;
|
|
3756
|
-
this.eventType = eventType;
|
|
3757
|
-
}
|
|
3758
|
-
};
|
|
3759
3886
|
var DEFAULT_MAX_QUEUED = 1e4;
|
|
3760
3887
|
var DEFAULT_OVERFLOW = "drop-oldest-non-terminal";
|
|
3761
3888
|
var AxlEventBus = class {
|
|
@@ -3766,6 +3893,7 @@ var AxlEventBus = class {
|
|
|
3766
3893
|
maxQueued;
|
|
3767
3894
|
onOverflow;
|
|
3768
3895
|
overflowWarned = false;
|
|
3896
|
+
droppedEvents = 0;
|
|
3769
3897
|
/** Callbacks fired exactly once when `_finish()` is called. Used by
|
|
3770
3898
|
* curated views (e.g., `partialObjects`) that are listener-based and
|
|
3771
3899
|
* need a termination signal independent of any synthetic `done` event. */
|
|
@@ -3811,6 +3939,10 @@ var AxlEventBus = class {
|
|
|
3811
3939
|
* can be 4 KB+ each, hence the more aggressive cleanup here).
|
|
3812
3940
|
*/
|
|
3813
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();
|
|
3814
3946
|
/**
|
|
3815
3947
|
* Hook fired when an iterator obtained from `[Symbol.asyncIterator]`
|
|
3816
3948
|
* is `await using`-disposed. Set via the second constructor argument.
|
|
@@ -3844,6 +3976,12 @@ var AxlEventBus = class {
|
|
|
3844
3976
|
this.bus.on("error", () => {
|
|
3845
3977
|
});
|
|
3846
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
|
+
}
|
|
3847
3985
|
unknownEventWarned = false;
|
|
3848
3986
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3849
3987
|
on(event, handler) {
|
|
@@ -4156,7 +4294,7 @@ var AxlEventBus = class {
|
|
|
4156
4294
|
}
|
|
4157
4295
|
return {
|
|
4158
4296
|
[Symbol.asyncIterator]() {
|
|
4159
|
-
const pending =
|
|
4297
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4160
4298
|
let resolveWaiter = null;
|
|
4161
4299
|
let finished = self.finished;
|
|
4162
4300
|
let unsubscribed = false;
|
|
@@ -4170,10 +4308,39 @@ var AxlEventBus = class {
|
|
|
4170
4308
|
if (filterPath !== void 0 && path !== filterPath) return false;
|
|
4171
4309
|
return true;
|
|
4172
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
|
+
};
|
|
4173
4340
|
for (const [askId, paths] of self.stringStreamByAsk) {
|
|
4174
4341
|
for (const [path, acc] of paths) {
|
|
4175
4342
|
if (!matchesFilter(askId, path)) continue;
|
|
4176
|
-
|
|
4343
|
+
enqueue({
|
|
4177
4344
|
askId,
|
|
4178
4345
|
agent: acc.agent,
|
|
4179
4346
|
path,
|
|
@@ -4183,51 +4350,37 @@ var AxlEventBus = class {
|
|
|
4183
4350
|
});
|
|
4184
4351
|
}
|
|
4185
4352
|
}
|
|
4186
|
-
const
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
askId,
|
|
4195
|
-
agent: event.agent,
|
|
4196
|
-
path: event.data.path,
|
|
4197
|
-
delta: event.data.delta,
|
|
4198
|
-
accumulated,
|
|
4199
|
-
attempt: event.attempt
|
|
4200
|
-
});
|
|
4201
|
-
wakeWaiter();
|
|
4202
|
-
};
|
|
4203
|
-
self.bus.on("string_delta", handler);
|
|
4204
|
-
const pipelineHandler = (event) => {
|
|
4205
|
-
if (event.type !== "pipeline") return;
|
|
4206
|
-
if (event.status !== "failed") return;
|
|
4207
|
-
const askId = event.askId ?? "";
|
|
4208
|
-
for (let i = pending.length - 1; i >= 0; i--) {
|
|
4209
|
-
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
|
+
}
|
|
4210
4361
|
}
|
|
4211
4362
|
};
|
|
4212
|
-
self.
|
|
4363
|
+
self.stringStreamSubscribers.add(subscriber);
|
|
4213
4364
|
const unsubscribe = () => {
|
|
4214
4365
|
if (unsubscribed) return;
|
|
4215
4366
|
unsubscribed = true;
|
|
4216
|
-
self.
|
|
4217
|
-
self.bus.off("pipeline", pipelineHandler);
|
|
4367
|
+
self.stringStreamSubscribers.delete(subscriber);
|
|
4218
4368
|
unsubscribeFinish();
|
|
4219
4369
|
};
|
|
4220
4370
|
const unsubscribeFinish = self._onFinish(() => {
|
|
4221
4371
|
finished = true;
|
|
4222
|
-
self.
|
|
4223
|
-
self.bus.off("pipeline", pipelineHandler);
|
|
4372
|
+
self.stringStreamSubscribers.delete(subscriber);
|
|
4224
4373
|
wakeWaiter();
|
|
4225
4374
|
});
|
|
4226
4375
|
return {
|
|
4227
4376
|
async next() {
|
|
4228
4377
|
while (true) {
|
|
4229
|
-
if (pending.
|
|
4230
|
-
|
|
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
|
+
}
|
|
4231
4384
|
}
|
|
4232
4385
|
if (finished) {
|
|
4233
4386
|
unsubscribe();
|
|
@@ -4285,6 +4438,7 @@ var AxlEventBus = class {
|
|
|
4285
4438
|
*/
|
|
4286
4439
|
_push(event) {
|
|
4287
4440
|
if (this.finished) return;
|
|
4441
|
+
let overflowError;
|
|
4288
4442
|
if (event.type === "partial_object" && event.data?.object !== void 0) {
|
|
4289
4443
|
const askId = event.askId ?? "";
|
|
4290
4444
|
this.latestPartialByAsk.set(askId, {
|
|
@@ -4306,10 +4460,30 @@ var AxlEventBus = class {
|
|
|
4306
4460
|
text: (existing?.text ?? "") + event.data.delta,
|
|
4307
4461
|
attempt: event.attempt
|
|
4308
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
|
+
}
|
|
4309
4482
|
} else if (event.type === "pipeline" && event.status === "failed") {
|
|
4310
4483
|
const askId = event.askId ?? "";
|
|
4311
4484
|
this.latestPartialByAsk.delete(askId);
|
|
4312
4485
|
this.stringStreamByAsk.delete(askId);
|
|
4486
|
+
for (const subscriber of this.stringStreamSubscribers) subscriber.reset(askId);
|
|
4313
4487
|
} else if (event.type === "ask_end") {
|
|
4314
4488
|
this.stringStreamByAsk.delete(event.askId ?? "");
|
|
4315
4489
|
}
|
|
@@ -4324,39 +4498,57 @@ var AxlEventBus = class {
|
|
|
4324
4498
|
const waiter = this.waiters.shift();
|
|
4325
4499
|
if (waiter) {
|
|
4326
4500
|
waiter({ value: event, done: false });
|
|
4501
|
+
if (overflowError) throw overflowError;
|
|
4327
4502
|
return;
|
|
4328
4503
|
}
|
|
4329
4504
|
if (this.eventQueue.length >= this.maxQueued && !TERMINAL_TYPES.has(event.type)) {
|
|
4330
|
-
|
|
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;
|
|
4331
4515
|
return;
|
|
4332
4516
|
}
|
|
4333
4517
|
this.eventQueue.push(event);
|
|
4518
|
+
if (overflowError) throw overflowError;
|
|
4334
4519
|
}
|
|
4335
4520
|
/** Apply the overflow policy when the queue is at `maxQueued` and a
|
|
4336
4521
|
* non-terminal event arrives. Terminal events bypass this path entirely
|
|
4337
4522
|
* (checked by the caller). */
|
|
4338
4523
|
handleOverflow(event) {
|
|
4339
4524
|
if (this.onOverflow === "throw") {
|
|
4525
|
+
this.droppedEvents++;
|
|
4340
4526
|
throw new EventStreamOverflowError(this.maxQueued, event.type);
|
|
4341
4527
|
}
|
|
4342
4528
|
let droppedIdx = -1;
|
|
4343
4529
|
for (let i = 0; i < this.eventQueue.length; i++) {
|
|
4344
|
-
|
|
4530
|
+
const candidate = this.eventQueue[i];
|
|
4531
|
+
if (!TERMINAL_TYPES.has(candidate.type)) {
|
|
4345
4532
|
droppedIdx = i;
|
|
4346
4533
|
break;
|
|
4347
4534
|
}
|
|
4348
4535
|
}
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
`[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.`
|
|
4353
|
-
);
|
|
4354
|
-
}
|
|
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
|
+
);
|
|
4355
4539
|
if (droppedIdx >= 0) {
|
|
4356
4540
|
this.eventQueue.splice(droppedIdx, 1);
|
|
4541
|
+
this.droppedEvents++;
|
|
4357
4542
|
}
|
|
4358
4543
|
this.eventQueue.push(event);
|
|
4359
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
|
+
}
|
|
4360
4552
|
/**
|
|
4361
4553
|
* Mark the bus as finished. Wakes any pending iterators with `done: true`
|
|
4362
4554
|
* and drops further `_push` calls. Idempotent.
|
|
@@ -4423,6 +4615,43 @@ var AxlEventBus = class {
|
|
|
4423
4615
|
}
|
|
4424
4616
|
};
|
|
4425
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
|
+
|
|
4426
4655
|
// src/redaction.ts
|
|
4427
4656
|
var REDACTED = "[redacted]";
|
|
4428
4657
|
function walkObjectOneLevel(obj, preserveKeys) {
|
|
@@ -4473,7 +4702,7 @@ function redactMemory(event) {
|
|
|
4473
4702
|
};
|
|
4474
4703
|
}
|
|
4475
4704
|
var passthrough = (e) => e;
|
|
4476
|
-
var
|
|
4705
|
+
var LEGACY_REDACTION_RULES = {
|
|
4477
4706
|
workflow_start: (e) => e.data.input !== void 0 ? { ...e, data: { ...e.data, input: REDACTED } } : e,
|
|
4478
4707
|
workflow_end: (e) => {
|
|
4479
4708
|
const d = e.data;
|
|
@@ -4573,10 +4802,125 @@ var REDACTION_RULES = {
|
|
|
4573
4802
|
done: (e) => ({ ...e, data: { result: REDACTED } }),
|
|
4574
4803
|
error: (e) => ({ ...e, data: { ...e.data, message: REDACTED } })
|
|
4575
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
|
+
};
|
|
4576
4914
|
function redactEvent(event) {
|
|
4577
4915
|
const rule = REDACTION_RULES[event.type];
|
|
4578
4916
|
return rule(event);
|
|
4579
4917
|
}
|
|
4918
|
+
function redactHistoricalEvent(event) {
|
|
4919
|
+
if (getEventSchemaVersion(event) === 1) {
|
|
4920
|
+
return redactLegacyEvent(event);
|
|
4921
|
+
}
|
|
4922
|
+
return redactEvent(event);
|
|
4923
|
+
}
|
|
4580
4924
|
|
|
4581
4925
|
// src/schema-diagnostics.ts
|
|
4582
4926
|
var DEFAULT_SCHEMA_OVERSIZED_TOKENS = 4e3;
|
|
@@ -4631,104 +4975,765 @@ function detectDroppedRefinements(schema) {
|
|
|
4631
4975
|
if ("right" in def) walk(def.right, path);
|
|
4632
4976
|
if (typeof def.getter === "function") {
|
|
4633
4977
|
try {
|
|
4634
|
-
walk(def.getter(), path);
|
|
4978
|
+
walk(def.getter(), path);
|
|
4979
|
+
} catch {
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
};
|
|
4983
|
+
walk(schema, "");
|
|
4984
|
+
const result = { count, paths: [...paths] };
|
|
4985
|
+
refinementCache.set(schema, result);
|
|
4986
|
+
return result;
|
|
4987
|
+
}
|
|
4988
|
+
var DOCS_URL = "docs/observability.md#schema-diagnostics";
|
|
4989
|
+
var MAX_WARNED_KEYS = 2048;
|
|
4990
|
+
var warnedDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
4991
|
+
function warnSchemaDiagnosticOnce(key, message, silent) {
|
|
4992
|
+
if (silent || process.env.AXL_DIAGNOSTICS_SILENT === "true") return;
|
|
4993
|
+
if (warnedDiagnosticKeys.has(key)) return;
|
|
4994
|
+
if (warnedDiagnosticKeys.size >= MAX_WARNED_KEYS) warnedDiagnosticKeys.clear();
|
|
4995
|
+
warnedDiagnosticKeys.add(key);
|
|
4996
|
+
console.warn(`[axl] ${message} See ${DOCS_URL}.`);
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
// src/config.ts
|
|
5000
|
+
function defineConfig(config) {
|
|
5001
|
+
return config;
|
|
5002
|
+
}
|
|
5003
|
+
function parseDuration(duration) {
|
|
5004
|
+
const match = duration.match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)$/);
|
|
5005
|
+
if (!match) throw new Error(`Invalid duration format: "${duration}"`);
|
|
5006
|
+
const value = parseFloat(match[1]);
|
|
5007
|
+
const unit = match[2];
|
|
5008
|
+
switch (unit) {
|
|
5009
|
+
case "ms":
|
|
5010
|
+
return value;
|
|
5011
|
+
case "s":
|
|
5012
|
+
return value * 1e3;
|
|
5013
|
+
case "m":
|
|
5014
|
+
return value * 6e4;
|
|
5015
|
+
case "h":
|
|
5016
|
+
return value * 36e5;
|
|
5017
|
+
default:
|
|
5018
|
+
throw new Error(`Unknown duration unit: "${unit}"`);
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
function parseCost(cost) {
|
|
5022
|
+
const match = cost.match(/^\$?([\d.]+)$/);
|
|
5023
|
+
if (!match) throw new Error(`Invalid cost format: "${cost}"`);
|
|
5024
|
+
return parseFloat(match[1]);
|
|
5025
|
+
}
|
|
5026
|
+
function resolveConfig(config) {
|
|
5027
|
+
const resolved = { ...config };
|
|
5028
|
+
if (process.env.AXL_DEFAULT_PROVIDER) {
|
|
5029
|
+
resolved.defaultProvider = process.env.AXL_DEFAULT_PROVIDER;
|
|
5030
|
+
}
|
|
5031
|
+
if (process.env.AXL_STATE_STORE) {
|
|
5032
|
+
const envStore = process.env.AXL_STATE_STORE;
|
|
5033
|
+
if (envStore === "memory" || envStore === "sqlite") {
|
|
5034
|
+
resolved.state = {
|
|
5035
|
+
...resolved.state,
|
|
5036
|
+
store: envStore
|
|
5037
|
+
};
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
if (process.env.AXL_TRACE_ENABLED !== void 0) {
|
|
5041
|
+
resolved.trace = { ...resolved.trace, enabled: process.env.AXL_TRACE_ENABLED === "true" };
|
|
5042
|
+
}
|
|
5043
|
+
if (process.env.AXL_TRACE_LEVEL) {
|
|
5044
|
+
resolved.trace = {
|
|
5045
|
+
...resolved.trace,
|
|
5046
|
+
level: process.env.AXL_TRACE_LEVEL
|
|
5047
|
+
};
|
|
5048
|
+
}
|
|
5049
|
+
if (process.env.OPENAI_API_KEY) {
|
|
5050
|
+
if (!resolved.providers) resolved.providers = {};
|
|
5051
|
+
resolved.providers.openai = {
|
|
5052
|
+
...resolved.providers.openai ?? {},
|
|
5053
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
5054
|
+
};
|
|
5055
|
+
}
|
|
5056
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
5057
|
+
if (!resolved.providers) resolved.providers = {};
|
|
5058
|
+
resolved.providers.anthropic = {
|
|
5059
|
+
...resolved.providers.anthropic ?? {},
|
|
5060
|
+
apiKey: process.env.ANTHROPIC_API_KEY
|
|
5061
|
+
};
|
|
5062
|
+
}
|
|
5063
|
+
const googleKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
|
|
5064
|
+
if (googleKey) {
|
|
5065
|
+
if (!resolved.providers) resolved.providers = {};
|
|
5066
|
+
resolved.providers.google = {
|
|
5067
|
+
...resolved.providers.google ?? {},
|
|
5068
|
+
apiKey: googleKey
|
|
5069
|
+
};
|
|
5070
|
+
}
|
|
5071
|
+
return resolved;
|
|
5072
|
+
}
|
|
5073
|
+
|
|
5074
|
+
// src/tool-model-output.ts
|
|
5075
|
+
import { types as nodeTypes } from "util";
|
|
5076
|
+
function invalid(path, reason) {
|
|
5077
|
+
return new Error(`Invalid tool model output at ${path}: ${reason}`);
|
|
5078
|
+
}
|
|
5079
|
+
function propertyPath(parent, key) {
|
|
5080
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) return `${parent}.${key}`;
|
|
5081
|
+
let escaped = "";
|
|
5082
|
+
for (const character of key) {
|
|
5083
|
+
const code = character.charCodeAt(0);
|
|
5084
|
+
if (character === "\\" || character === '"') escaped += `\\${character}`;
|
|
5085
|
+
else if (code <= 31 || code === 8232 || code === 8233) {
|
|
5086
|
+
escaped += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
5087
|
+
} else escaped += character;
|
|
5088
|
+
}
|
|
5089
|
+
return `${parent}["${escaped}"]`;
|
|
5090
|
+
}
|
|
5091
|
+
function normalize(value, path, stack) {
|
|
5092
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
5093
|
+
if (typeof value === "number") {
|
|
5094
|
+
if (!Number.isFinite(value)) throw invalid(path, "number must be finite");
|
|
5095
|
+
return value;
|
|
5096
|
+
}
|
|
5097
|
+
if (value === void 0) throw invalid(path, "undefined is only allowed on object properties");
|
|
5098
|
+
if (typeof value === "bigint" || typeof value === "function" || typeof value === "symbol") {
|
|
5099
|
+
throw invalid(path, `unsupported ${typeof value}`);
|
|
5100
|
+
}
|
|
5101
|
+
if (typeof value !== "object") throw invalid(path, `unsupported ${typeof value}`);
|
|
5102
|
+
let prototype;
|
|
5103
|
+
let descriptors;
|
|
5104
|
+
try {
|
|
5105
|
+
prototype = Object.getPrototypeOf(value);
|
|
5106
|
+
descriptors = Object.getOwnPropertyDescriptors(value);
|
|
5107
|
+
Object.setPrototypeOf(descriptors, null);
|
|
5108
|
+
} catch (cause) {
|
|
5109
|
+
throw invalid(
|
|
5110
|
+
path,
|
|
5111
|
+
`object inspection failed (${cause instanceof Error ? cause.name : "error"})`
|
|
5112
|
+
);
|
|
5113
|
+
}
|
|
5114
|
+
if (stack.has(value)) throw invalid(path, "cyclic reference");
|
|
5115
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
5116
|
+
const descriptor = descriptors[key];
|
|
5117
|
+
if (descriptor && !("value" in descriptor)) {
|
|
5118
|
+
throw invalid(
|
|
5119
|
+
typeof key === "string" ? propertyPath(path, key) : `${path}[symbol]`,
|
|
5120
|
+
"accessor properties are not supported"
|
|
5121
|
+
);
|
|
5122
|
+
}
|
|
5123
|
+
if (typeof key === "symbol") {
|
|
5124
|
+
throw invalid(`${path}[symbol]`, "symbol keys are not supported");
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
if (typeof descriptors.toJSON?.value === "function") {
|
|
5128
|
+
throw invalid(propertyPath(path, "toJSON"), "custom toJSON is not supported");
|
|
5129
|
+
}
|
|
5130
|
+
const array = Array.isArray(value);
|
|
5131
|
+
if (array && prototype !== Array.prototype && prototype !== null) {
|
|
5132
|
+
throw invalid(path, "array subclasses are not supported");
|
|
5133
|
+
}
|
|
5134
|
+
const inheritedToJSON = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "toJSON") : void 0;
|
|
5135
|
+
if (typeof inheritedToJSON?.value === "function") {
|
|
5136
|
+
throw invalid(propertyPath(path, "toJSON"), "inherited custom toJSON is not supported");
|
|
5137
|
+
}
|
|
5138
|
+
const inheritedThen = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "then") : void 0;
|
|
5139
|
+
if (typeof descriptors.then?.value === "function" || inheritedThen !== void 0 && (!("value" in inheritedThen) || typeof inheritedThen.value === "function")) {
|
|
5140
|
+
throw invalid(path, "thenable output is not supported");
|
|
5141
|
+
}
|
|
5142
|
+
stack.add(value);
|
|
5143
|
+
try {
|
|
5144
|
+
if (array) {
|
|
5145
|
+
const normalized2 = [];
|
|
5146
|
+
const length = descriptors.length?.value;
|
|
5147
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
5148
|
+
throw invalid(propertyPath(path, "length"), "array length is invalid");
|
|
5149
|
+
}
|
|
5150
|
+
for (let index = 0; index < length; index++) {
|
|
5151
|
+
const descriptor = descriptors[String(index)];
|
|
5152
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
5153
|
+
throw invalid(`${path}[${index}]`, "arrays must be dense data properties");
|
|
5154
|
+
}
|
|
5155
|
+
if (descriptor.value === void 0) {
|
|
5156
|
+
throw invalid(`${path}[${index}]`, "array values cannot be undefined");
|
|
5157
|
+
}
|
|
5158
|
+
normalized2.push(normalize(descriptor.value, `${path}[${index}]`, stack));
|
|
5159
|
+
}
|
|
5160
|
+
for (const key of Object.keys(descriptors)) {
|
|
5161
|
+
const descriptor = descriptors[key];
|
|
5162
|
+
if (!descriptor?.enumerable) continue;
|
|
5163
|
+
const index = Number(key);
|
|
5164
|
+
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== key) {
|
|
5165
|
+
throw invalid(propertyPath(path, key), "non-index array properties are not supported");
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
Object.setPrototypeOf(normalized2, null);
|
|
5169
|
+
return normalized2;
|
|
5170
|
+
}
|
|
5171
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
5172
|
+
throw invalid(path, "only plain objects and null-prototype records are supported");
|
|
5173
|
+
}
|
|
5174
|
+
const normalized = /* @__PURE__ */ Object.create(null);
|
|
5175
|
+
for (const key of Object.keys(descriptors)) {
|
|
5176
|
+
const descriptor = descriptors[key];
|
|
5177
|
+
if (!descriptor?.enumerable) continue;
|
|
5178
|
+
if (!("value" in descriptor)) {
|
|
5179
|
+
throw invalid(propertyPath(path, key), "accessor properties are not supported");
|
|
5180
|
+
}
|
|
5181
|
+
if (descriptor.value === void 0) continue;
|
|
5182
|
+
normalized[key] = normalize(descriptor.value, propertyPath(path, key), stack);
|
|
5183
|
+
}
|
|
5184
|
+
return normalized;
|
|
5185
|
+
} finally {
|
|
5186
|
+
stack.delete(value);
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
function serializeToolModelOutput(toolName, output) {
|
|
5190
|
+
try {
|
|
5191
|
+
if (nodeTypes.isPromise(output)) {
|
|
5192
|
+
void Promise.prototype.then.call(output, void 0, () => void 0);
|
|
5193
|
+
}
|
|
5194
|
+
if (typeof output === "string") return output;
|
|
5195
|
+
return JSON.stringify(normalize(output, "$", /* @__PURE__ */ new WeakSet()));
|
|
5196
|
+
} catch (cause) {
|
|
5197
|
+
throw new ToolModelOutputError(toolName, cause);
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
|
|
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
|
+
};
|
|
5304
|
+
}
|
|
5305
|
+
function isAbortError3(error) {
|
|
5306
|
+
try {
|
|
5307
|
+
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
5308
|
+
} catch {
|
|
5309
|
+
return false;
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
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) {
|
|
5326
|
+
let isError = false;
|
|
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);
|
|
4635
5335
|
} catch {
|
|
5336
|
+
return void 0;
|
|
4636
5337
|
}
|
|
4637
|
-
}
|
|
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)
|
|
4638
5374
|
};
|
|
4639
|
-
walk(schema, "");
|
|
4640
|
-
const result = { count, paths: [...paths] };
|
|
4641
|
-
refinementCache.set(schema, result);
|
|
4642
|
-
return result;
|
|
4643
5375
|
}
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
5376
|
+
function failed(phase, error, options = {}) {
|
|
5377
|
+
let modelMessage;
|
|
5378
|
+
if (isToolFailure(error)) {
|
|
5379
|
+
try {
|
|
5380
|
+
modelMessage = typeof error.modelMessage === "string" ? error.modelMessage : void 0;
|
|
5381
|
+
} catch {
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
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
|
+
};
|
|
4653
5398
|
}
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
function defineConfig(config) {
|
|
4657
|
-
return config;
|
|
5399
|
+
function mcpContent(result) {
|
|
5400
|
+
return result.content.map((content) => content.type === "text" ? content.text : `[${content.type}]`).join("\n");
|
|
4658
5401
|
}
|
|
4659
|
-
function
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
switch (unit) {
|
|
4665
|
-
case "ms":
|
|
4666
|
-
return value;
|
|
4667
|
-
case "s":
|
|
4668
|
-
return value * 1e3;
|
|
4669
|
-
case "m":
|
|
4670
|
-
return value * 6e4;
|
|
4671
|
-
case "h":
|
|
4672
|
-
return value * 36e5;
|
|
4673
|
-
default:
|
|
4674
|
-
throw new Error(`Unknown duration unit: "${unit}"`);
|
|
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;
|
|
4675
5407
|
}
|
|
5408
|
+
return `MCP tool error: ${normalized.slice(0, MAX_MCP_MODEL_ERROR_LENGTH)}`;
|
|
4676
5409
|
}
|
|
4677
|
-
function
|
|
4678
|
-
const
|
|
4679
|
-
|
|
4680
|
-
|
|
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;
|
|
5449
|
+
try {
|
|
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 };
|
|
4681
5512
|
}
|
|
4682
|
-
function
|
|
4683
|
-
const
|
|
4684
|
-
if (
|
|
4685
|
-
|
|
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
|
+
};
|
|
4686
5520
|
}
|
|
4687
|
-
if (
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
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)
|
|
4693
5558
|
};
|
|
4694
5559
|
}
|
|
4695
5560
|
}
|
|
4696
|
-
|
|
4697
|
-
|
|
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 };
|
|
4698
5568
|
}
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
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
|
|
4703
5576
|
};
|
|
4704
5577
|
}
|
|
4705
|
-
if (
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
apiKey: process.env.OPENAI_API_KEY
|
|
5578
|
+
if (internal.kind === "failed" && internal.failure.disposition === "abort") {
|
|
5579
|
+
return {
|
|
5580
|
+
outcome: { status: "failed", failure: internal.failure },
|
|
5581
|
+
abortError: internal.error
|
|
4710
5582
|
};
|
|
4711
5583
|
}
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
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)
|
|
4717
5593
|
};
|
|
4718
5594
|
}
|
|
4719
|
-
const
|
|
4720
|
-
if (
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
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)
|
|
4725
5603
|
};
|
|
4726
5604
|
}
|
|
4727
|
-
|
|
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
|
+
}
|
|
4728
5663
|
}
|
|
4729
5664
|
|
|
4730
5665
|
// src/context.ts
|
|
4731
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
|
+
);
|
|
5675
|
+
}
|
|
5676
|
+
return resolved;
|
|
5677
|
+
}
|
|
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
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
return invalid2();
|
|
5736
|
+
}
|
|
4732
5737
|
var askStorage = new AsyncLocalStorage();
|
|
4733
5738
|
var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
4734
5739
|
function zodToJsonSchema(schema) {
|
|
@@ -4862,9 +5867,8 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4862
5867
|
providerRegistry;
|
|
4863
5868
|
sessionHistory;
|
|
4864
5869
|
onTrace;
|
|
4865
|
-
onToken;
|
|
4866
|
-
onToolCall;
|
|
4867
5870
|
pendingDecisions;
|
|
5871
|
+
awaitHumanClaims;
|
|
4868
5872
|
budgetContext;
|
|
4869
5873
|
stateStore;
|
|
4870
5874
|
/** Root step counter for this execution. Inherited by every ctx.ask()
|
|
@@ -4885,27 +5889,27 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4885
5889
|
* the caller-supplied key.
|
|
4886
5890
|
*/
|
|
4887
5891
|
autoCheckpointCounters;
|
|
4888
|
-
|
|
5892
|
+
branchDrainState;
|
|
5893
|
+
/** Root-shared idempotency guards for workflow lifecycle emission.
|
|
4889
5894
|
* Both `runtime.execute()` and `runtime.stream()` have paths where a
|
|
4890
5895
|
* post-emit side-effect (checkpoint deletion, state-store persistence)
|
|
4891
5896
|
* can throw AFTER `_emitWorkflowEnd({status: 'completed'})` has already
|
|
4892
5897
|
* fired — the outer catch would then fire `_emitWorkflowEnd({status:
|
|
4893
5898
|
* 'failed'})` for a second time. These flags make both emitters
|
|
4894
5899
|
* single-fire so consumers never see paired-then-conflicting terminal
|
|
4895
|
-
* events for one execution.
|
|
4896
|
-
|
|
4897
|
-
|
|
5900
|
+
* events for one execution. Sharing the state with child contexts also
|
|
5901
|
+
* suppresses late branch events after bounded finalization. */
|
|
5902
|
+
workflowLifecycleState;
|
|
4898
5903
|
signal;
|
|
4899
5904
|
summaryCache;
|
|
4900
5905
|
workflowName;
|
|
4901
5906
|
mcpManager;
|
|
4902
5907
|
spanManager;
|
|
4903
5908
|
memoryManager;
|
|
4904
|
-
resumeMode;
|
|
4905
5909
|
toolOverrides;
|
|
4906
5910
|
awaitHumanHandler;
|
|
4907
|
-
onAgentStart;
|
|
4908
5911
|
onAgentCallComplete;
|
|
5912
|
+
_onDecisionCleanupFailed;
|
|
4909
5913
|
/** Shared mutable slot for the lazy `AxlEventBus`. The slot is shared
|
|
4910
5914
|
* by reference across parent/child contexts so a late allocation
|
|
4911
5915
|
* (consumer subscribes after a child context exists) is visible
|
|
@@ -4914,6 +5918,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4914
5918
|
* contexts that never observe). */
|
|
4915
5919
|
_busRef;
|
|
4916
5920
|
_eventStreamOptions;
|
|
5921
|
+
_forceStreaming;
|
|
4917
5922
|
/** Removes the constructor-registered `'abort'` listener from
|
|
4918
5923
|
* `this.signal` so a long-lived signal (e.g., a request-scoped or
|
|
4919
5924
|
* process-wide signal reused across many `runtime.createContext()` /
|
|
@@ -4933,8 +5938,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4933
5938
|
* *between* asks. `ctx.events.partialObjects` is the coalescing view
|
|
4934
5939
|
* designed for streaming-structured-output UIs.
|
|
4935
5940
|
* - **Ad-hoc contexts** from `runtime.createContext()`: the same
|
|
4936
|
-
* iterable
|
|
4937
|
-
* `onAgentStart` callbacks.
|
|
5941
|
+
* iterable provides typed per-context observation.
|
|
4938
5942
|
* - **Cross-execution**: prefer `runtime.on('trace', …)` instead — that
|
|
4939
5943
|
* fans out across all executions, while `ctx.events` is per-context.
|
|
4940
5944
|
*
|
|
@@ -4949,9 +5953,9 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4949
5953
|
*
|
|
4950
5954
|
* **Subscribe before the first `ctx.ask()`.** The streaming code
|
|
4951
5955
|
* path inside `ctx.ask()` activates only when an observer is
|
|
4952
|
-
* present at the time the ask starts (`_streamingEnabled` —
|
|
4953
|
-
*
|
|
4954
|
-
*
|
|
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
|
|
4955
5959
|
* begun, that in-flight ask will not stream `token` /
|
|
4956
5960
|
* `partial_object` events at all (the agent loop went through
|
|
4957
5961
|
* `provider.chat` instead of `provider.stream`). Subsequent asks
|
|
@@ -4990,18 +5994,16 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4990
5994
|
this._busRef.current?._finish();
|
|
4991
5995
|
this.abortListenerCleanup?.();
|
|
4992
5996
|
}
|
|
4993
|
-
/** True
|
|
4994
|
-
*
|
|
4995
|
-
*
|
|
4996
|
-
* been allocated (a workflow-handler consumer subscribed before
|
|
4997
|
-
* 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
|
|
4998
6000
|
* streaming code path; the gate is re-checked per ask, so a
|
|
4999
6001
|
* consumer subscribing AFTER the first ask started still gets
|
|
5000
6002
|
* streaming on the next one. The check `_busRef.current !== undefined`
|
|
5001
6003
|
* is a one-way flag — `_finish` does not unset the reference, so
|
|
5002
6004
|
* once an observer was present, every subsequent ask streams. */
|
|
5003
6005
|
get _streamingEnabled() {
|
|
5004
|
-
return this.
|
|
6006
|
+
return this._forceStreaming || this._busRef.current !== void 0;
|
|
5005
6007
|
}
|
|
5006
6008
|
constructor(init) {
|
|
5007
6009
|
this.input = init.input;
|
|
@@ -5011,9 +6013,8 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5011
6013
|
this.providerRegistry = init.providerRegistry;
|
|
5012
6014
|
this.sessionHistory = init.sessionHistory ?? [];
|
|
5013
6015
|
this.onTrace = init.onTrace;
|
|
5014
|
-
this.onToken = init.onToken;
|
|
5015
|
-
this.onToolCall = init.onToolCall;
|
|
5016
6016
|
this.pendingDecisions = init.pendingDecisions;
|
|
6017
|
+
this.awaitHumanClaims = init.awaitHumanClaims ?? /* @__PURE__ */ new Map();
|
|
5017
6018
|
this.budgetContext = init.budgetContext;
|
|
5018
6019
|
this.stateStore = init.stateStore;
|
|
5019
6020
|
this.signal = init.signal;
|
|
@@ -5021,13 +6022,13 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5021
6022
|
this.mcpManager = init.mcpManager;
|
|
5022
6023
|
this.spanManager = init.spanManager;
|
|
5023
6024
|
this.memoryManager = init.memoryManager;
|
|
5024
|
-
this.resumeMode = init.resumeMode ?? false;
|
|
5025
6025
|
this.toolOverrides = init.toolOverrides;
|
|
5026
6026
|
this.awaitHumanHandler = init.awaitHumanHandler;
|
|
5027
|
-
this.onAgentStart = init.onAgentStart;
|
|
5028
6027
|
this.onAgentCallComplete = init.onAgentCallComplete;
|
|
6028
|
+
this._onDecisionCleanupFailed = init._onDecisionCleanupFailed;
|
|
5029
6029
|
this._busRef = init._busRef ?? { current: void 0 };
|
|
5030
6030
|
this._eventStreamOptions = init.eventStreamOptions;
|
|
6031
|
+
this._forceStreaming = init._forceStreaming ?? false;
|
|
5031
6032
|
if (init._busRef === void 0 && this.signal && !this.signal.aborted) {
|
|
5032
6033
|
const signal = this.signal;
|
|
5033
6034
|
const onAbort = () => {
|
|
@@ -5043,6 +6044,15 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5043
6044
|
byAgent: /* @__PURE__ */ new Map(),
|
|
5044
6045
|
root: 0
|
|
5045
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
|
+
};
|
|
5046
6056
|
if (init.metadata?.summaryCache) {
|
|
5047
6057
|
this.summaryCache = init.metadata.summaryCache;
|
|
5048
6058
|
}
|
|
@@ -5051,16 +6061,9 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5051
6061
|
* Create a child context for nested agent invocations (e.g., agent-as-tool).
|
|
5052
6062
|
* Shares: budget tracking, abort signals, trace emission, provider registry,
|
|
5053
6063
|
* state store, span manager, memory manager, MCP manager, config,
|
|
5054
|
-
* awaitHuman handler, pending decisions, tool overrides,
|
|
5055
|
-
* streaming callbacks (onToken / onAgentStart / onToolCall).
|
|
6064
|
+
* awaitHuman handler, pending decisions, tool overrides, and event bus.
|
|
5056
6065
|
* Isolates: session history.
|
|
5057
6066
|
*
|
|
5058
|
-
* Streaming callbacks now propagate into nested asks because every callback
|
|
5059
|
-
* invocation carries `meta.askId`/`meta.parentAskId`/`meta.depth` so
|
|
5060
|
-
* consumers that want root-only behavior can filter on `meta.depth === 0`
|
|
5061
|
-
* instead of relying on the runtime to drop nested events. This is the
|
|
5062
|
-
* "nested ask visibility" fix from spec §3.2.
|
|
5063
|
-
*
|
|
5064
6067
|
* The shared step counter lives in `askStorage` (ALS), so there is no
|
|
5065
6068
|
* per-context counter to pass through anymore. Nested events are
|
|
5066
6069
|
* correlated to their parent via `parentAskId` (set on every nested ask
|
|
@@ -5081,12 +6084,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5081
6084
|
spanManager: this.spanManager,
|
|
5082
6085
|
memoryManager: this.memoryManager,
|
|
5083
6086
|
onTrace: this.onTrace,
|
|
5084
|
-
onToken: this.onToken,
|
|
5085
|
-
onToolCall: this.onToolCall,
|
|
5086
|
-
onAgentStart: this.onAgentStart,
|
|
5087
6087
|
onAgentCallComplete: this.onAgentCallComplete,
|
|
6088
|
+
_onDecisionCleanupFailed: this._onDecisionCleanupFailed,
|
|
5088
6089
|
awaitHumanHandler: this.awaitHumanHandler,
|
|
5089
6090
|
pendingDecisions: this.pendingDecisions,
|
|
6091
|
+
awaitHumanClaims: this.awaitHumanClaims,
|
|
5090
6092
|
toolOverrides: this.toolOverrides,
|
|
5091
6093
|
signal: this.signal,
|
|
5092
6094
|
workflowName: this.workflowName,
|
|
@@ -5094,12 +6096,15 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5094
6096
|
// emitted from `ctx.ask` / spawn / race / parallel / map in this
|
|
5095
6097
|
// child context don't collide with the parent's in the state store.
|
|
5096
6098
|
autoCheckpointCounters: this.autoCheckpointCounters,
|
|
6099
|
+
branchDrainState: this.branchDrainState,
|
|
6100
|
+
workflowLifecycleState: this.workflowLifecycleState,
|
|
5097
6101
|
// Share the parent's bus slot (mutable ref). Late allocation in
|
|
5098
6102
|
// either parent or child propagates because both read the same
|
|
5099
6103
|
// slot. Nested-ask events (agent-as-tool pattern) bubble up to
|
|
5100
6104
|
// the parent's iterator with `parentAskId`/`depth` correlation
|
|
5101
6105
|
// intact.
|
|
5102
6106
|
_busRef: this._busRef,
|
|
6107
|
+
_forceStreaming: this._forceStreaming,
|
|
5103
6108
|
eventStreamOptions: this._eventStreamOptions
|
|
5104
6109
|
// Isolated: sessionHistory (empty)
|
|
5105
6110
|
});
|
|
@@ -5112,28 +6117,194 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5112
6117
|
get currentSignal() {
|
|
5113
6118
|
return signalStorage.getStore() ?? this.signal;
|
|
5114
6119
|
}
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
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" };
|
|
5134
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
|
+
});
|
|
5135
6306
|
}
|
|
5136
|
-
|
|
6307
|
+
if (settlement.abortError !== void 0) throw settlement.abortError;
|
|
5137
6308
|
}
|
|
5138
6309
|
// ── ctx.ask() ─────────────────────────────────────────────────────────
|
|
5139
6310
|
async ask(agent2, prompt, options) {
|
|
@@ -5162,18 +6333,21 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5162
6333
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
5163
6334
|
const usageCapture = {};
|
|
5164
6335
|
const doCall = async () => {
|
|
5165
|
-
const
|
|
6336
|
+
const result2 = await this.executeAgentCall(
|
|
5166
6337
|
agent2,
|
|
5167
6338
|
prompt,
|
|
5168
6339
|
options,
|
|
5169
6340
|
void 0,
|
|
5170
6341
|
usageCapture
|
|
5171
6342
|
);
|
|
5172
|
-
return
|
|
6343
|
+
return result2;
|
|
5173
6344
|
};
|
|
5174
6345
|
let outcome;
|
|
6346
|
+
let askFailure;
|
|
6347
|
+
let askFailed = false;
|
|
6348
|
+
let result;
|
|
5175
6349
|
try {
|
|
5176
|
-
|
|
6350
|
+
result = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
5177
6351
|
"axl.agent.ask",
|
|
5178
6352
|
{
|
|
5179
6353
|
"axl.agent.name": agent2._name,
|
|
@@ -5232,36 +6406,34 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5232
6406
|
);
|
|
5233
6407
|
}
|
|
5234
6408
|
}
|
|
5235
|
-
return result;
|
|
5236
6409
|
} catch (err) {
|
|
6410
|
+
askFailed = true;
|
|
6411
|
+
askFailure = err;
|
|
5237
6412
|
outcome = {
|
|
5238
6413
|
ok: false,
|
|
5239
6414
|
error: err instanceof Error ? err.message : String(err)
|
|
5240
6415
|
};
|
|
5241
|
-
throw err;
|
|
5242
|
-
} finally {
|
|
5243
|
-
try {
|
|
5244
|
-
this.emitEvent({
|
|
5245
|
-
type: "ask_end",
|
|
5246
|
-
outcome: outcome ?? {
|
|
5247
|
-
ok: false,
|
|
5248
|
-
error: "ask_end emitted without outcome \u2014 internal bug"
|
|
5249
|
-
},
|
|
5250
|
-
cost: frame.askCost.value,
|
|
5251
|
-
...frame.askUnpriced ? { unpriced: true } : {},
|
|
5252
|
-
duration: Date.now() - askStart
|
|
5253
|
-
});
|
|
5254
|
-
} catch (emitErr) {
|
|
5255
|
-
if (emitErr instanceof EventStreamOverflowError && outcome && !outcome.ok) {
|
|
5256
|
-
console.error(
|
|
5257
|
-
"[axl] ask_end emit overflowed during error path; preserving original error:",
|
|
5258
|
-
emitErr.message
|
|
5259
|
-
);
|
|
5260
|
-
} else {
|
|
5261
|
-
throw emitErr;
|
|
5262
|
-
}
|
|
5263
|
-
}
|
|
5264
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;
|
|
5265
6437
|
});
|
|
5266
6438
|
},
|
|
5267
6439
|
{ agent: agentName }
|
|
@@ -5423,6 +6595,7 @@ ${appendedSchemaText}`;
|
|
|
5423
6595
|
let lastStartAttempt = 1;
|
|
5424
6596
|
let lastStartMaxAttempts = 1;
|
|
5425
6597
|
while (turns < maxTurns) {
|
|
6598
|
+
this.currentSignal?.throwIfAborted();
|
|
5426
6599
|
if (Date.now() - startTime > timeoutMs) {
|
|
5427
6600
|
throw new TimeoutError("ctx.ask()", timeoutMs);
|
|
5428
6601
|
}
|
|
@@ -5482,7 +6655,6 @@ ${appendedSchemaText}`;
|
|
|
5482
6655
|
}
|
|
5483
6656
|
} : { type: "json_object" };
|
|
5484
6657
|
}
|
|
5485
|
-
const callbackMeta = this.currentCallbackMeta(agent2._name);
|
|
5486
6658
|
const callParams = {
|
|
5487
6659
|
...chatOptions.temperature !== void 0 ? { temperature: chatOptions.temperature } : {},
|
|
5488
6660
|
maxTokens: chatOptions.maxTokens,
|
|
@@ -5521,9 +6693,9 @@ ${appendedSchemaText}`;
|
|
|
5521
6693
|
...messagesSnapshot ? { messages: messagesSnapshot } : {}
|
|
5522
6694
|
}
|
|
5523
6695
|
});
|
|
5524
|
-
this.onAgentStart?.({ agent: agent2._name, model: modelUri }, callbackMeta);
|
|
5525
6696
|
let response;
|
|
5526
6697
|
try {
|
|
6698
|
+
this.currentSignal?.throwIfAborted();
|
|
5527
6699
|
if (this._streamingEnabled) {
|
|
5528
6700
|
let content2 = "";
|
|
5529
6701
|
const toolCalls = [];
|
|
@@ -5546,7 +6718,6 @@ ${appendedSchemaText}`;
|
|
|
5546
6718
|
if (chunk.type === "text_delta") {
|
|
5547
6719
|
content2 += chunk.content;
|
|
5548
6720
|
this.emitEvent({ type: "token", data: chunk.content });
|
|
5549
|
-
this.onToken?.(chunk.content, callbackMeta);
|
|
5550
6721
|
if (walker) {
|
|
5551
6722
|
walker.processChunk(chunk.content);
|
|
5552
6723
|
if (walker.consumeBoundary()) {
|
|
@@ -5656,6 +6827,7 @@ ${appendedSchemaText}`;
|
|
|
5656
6827
|
...retryReason ? { retryReason } : {}
|
|
5657
6828
|
}
|
|
5658
6829
|
});
|
|
6830
|
+
this.currentSignal?.throwIfAborted();
|
|
5659
6831
|
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
5660
6832
|
currentMessages.push({
|
|
5661
6833
|
role: "assistant",
|
|
@@ -5825,232 +6997,12 @@ ${appendedSchemaText}`;
|
|
|
5825
6997
|
return await handoffFn();
|
|
5826
6998
|
}
|
|
5827
6999
|
}
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
currentMessages.push({
|
|
5835
|
-
role: "tool",
|
|
5836
|
-
content: `Error: Invalid JSON in tool arguments. Please provide valid JSON.`,
|
|
5837
|
-
tool_call_id: toolCall.id
|
|
5838
|
-
});
|
|
5839
|
-
continue;
|
|
5840
|
-
}
|
|
5841
|
-
this.emitEvent({
|
|
5842
|
-
type: "tool_call_start",
|
|
5843
|
-
tool: toolName,
|
|
5844
|
-
callId: toolCall.id,
|
|
5845
|
-
data: { args: toolArgs2 }
|
|
5846
|
-
});
|
|
5847
|
-
this.onToolCall?.(
|
|
5848
|
-
{ name: toolName, args: toolArgs2, callId: toolCall.id },
|
|
5849
|
-
callbackMeta
|
|
5850
|
-
);
|
|
5851
|
-
const toolStart2 = Date.now();
|
|
5852
|
-
const executeOverride = async () => {
|
|
5853
|
-
let toolResult3;
|
|
5854
|
-
try {
|
|
5855
|
-
toolResult3 = await toolOverride(toolArgs2);
|
|
5856
|
-
} catch (err) {
|
|
5857
|
-
toolResult3 = { error: err instanceof Error ? err.message : String(err) };
|
|
5858
|
-
}
|
|
5859
|
-
return toolResult3;
|
|
5860
|
-
};
|
|
5861
|
-
const toolResult2 = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
5862
|
-
"axl.tool.call",
|
|
5863
|
-
{
|
|
5864
|
-
"axl.tool.name": toolName,
|
|
5865
|
-
"axl.agent.name": agent2._name
|
|
5866
|
-
},
|
|
5867
|
-
async (span) => {
|
|
5868
|
-
const r = await executeOverride();
|
|
5869
|
-
span.setAttribute("axl.tool.duration", Date.now() - toolStart2);
|
|
5870
|
-
const isError = r && typeof r === "object" && "error" in r;
|
|
5871
|
-
span.setAttribute("axl.tool.success", !isError);
|
|
5872
|
-
if (isError)
|
|
5873
|
-
span.setStatus("error", r.error);
|
|
5874
|
-
return r;
|
|
5875
|
-
}
|
|
5876
|
-
) : await executeOverride();
|
|
5877
|
-
const resultContent2 = JSON.stringify(toolResult2);
|
|
5878
|
-
this.emitEvent({
|
|
5879
|
-
type: "tool_call_end",
|
|
5880
|
-
agent: agent2._name,
|
|
5881
|
-
tool: toolName,
|
|
5882
|
-
duration: Date.now() - toolStart2,
|
|
5883
|
-
data: { args: toolArgs2, result: toolResult2, callId: toolCall.id }
|
|
5884
|
-
});
|
|
5885
|
-
currentMessages.push({
|
|
5886
|
-
role: "tool",
|
|
5887
|
-
content: resultContent2,
|
|
5888
|
-
tool_call_id: toolCall.id
|
|
5889
|
-
});
|
|
5890
|
-
continue;
|
|
5891
|
-
}
|
|
5892
|
-
const tool2 = agent2._config.tools?.find((t) => t.name === toolName);
|
|
5893
|
-
const isMcpTool = !tool2 && this.mcpManager?.isMcpTool(toolName);
|
|
5894
|
-
if (!tool2 && !isMcpTool) {
|
|
5895
|
-
this.emitEvent({ type: "tool_denied", agent: agent2._name, tool: toolName });
|
|
5896
|
-
currentMessages.push({
|
|
5897
|
-
role: "tool",
|
|
5898
|
-
content: `Tool "${toolName}" is not available. Available tools: ${agent2._config.tools?.map((t) => t.name).join(", ") ?? "none"}`,
|
|
5899
|
-
tool_call_id: toolCall.id
|
|
5900
|
-
});
|
|
5901
|
-
continue;
|
|
5902
|
-
}
|
|
5903
|
-
let toolArgs;
|
|
5904
|
-
try {
|
|
5905
|
-
toolArgs = JSON.parse(toolCall.function.arguments);
|
|
5906
|
-
} catch {
|
|
5907
|
-
currentMessages.push({
|
|
5908
|
-
role: "tool",
|
|
5909
|
-
content: `Error: Invalid JSON in tool arguments. Please provide valid JSON.`,
|
|
5910
|
-
tool_call_id: toolCall.id
|
|
5911
|
-
});
|
|
5912
|
-
continue;
|
|
5913
|
-
}
|
|
5914
|
-
this.emitEvent({
|
|
5915
|
-
type: "tool_call_start",
|
|
5916
|
-
tool: toolName,
|
|
5917
|
-
callId: toolCall.id,
|
|
5918
|
-
data: { args: toolArgs }
|
|
5919
|
-
});
|
|
5920
|
-
this.onToolCall?.({ name: toolName, args: toolArgs, callId: toolCall.id }, callbackMeta);
|
|
5921
|
-
const toolStart = Date.now();
|
|
5922
|
-
if (tool2 && tool2.requireApproval) {
|
|
5923
|
-
const approvalFn = async () => {
|
|
5924
|
-
const decision = await this.awaitHuman({
|
|
5925
|
-
channel: "tool_approval",
|
|
5926
|
-
prompt: `Tool "${toolName}" wants to execute with args: ${JSON.stringify(toolArgs)}`,
|
|
5927
|
-
metadata: { toolName, args: toolArgs, agent: agent2._name }
|
|
5928
|
-
});
|
|
5929
|
-
if (!decision.approved) {
|
|
5930
|
-
const reason = decision.reason ?? "Denied by human";
|
|
5931
|
-
currentMessages.push({
|
|
5932
|
-
role: "tool",
|
|
5933
|
-
content: JSON.stringify({ error: `Tool denied by human: ${reason}` }),
|
|
5934
|
-
tool_call_id: toolCall.id
|
|
5935
|
-
});
|
|
5936
|
-
return { approved: false, reason };
|
|
5937
|
-
}
|
|
5938
|
-
return { approved: true };
|
|
5939
|
-
};
|
|
5940
|
-
let approvalOutcome;
|
|
5941
|
-
if (this.spanManager) {
|
|
5942
|
-
approvalOutcome = await this.spanManager.withSpanAsync(
|
|
5943
|
-
"axl.tool.approval",
|
|
5944
|
-
{
|
|
5945
|
-
"axl.tool.name": toolName,
|
|
5946
|
-
"axl.agent.name": agent2._name
|
|
5947
|
-
},
|
|
5948
|
-
async (span) => {
|
|
5949
|
-
const result = await approvalFn();
|
|
5950
|
-
span.setAttribute("axl.tool.approval.approved", result.approved);
|
|
5951
|
-
return result;
|
|
5952
|
-
}
|
|
5953
|
-
);
|
|
5954
|
-
} else {
|
|
5955
|
-
approvalOutcome = await approvalFn();
|
|
5956
|
-
}
|
|
5957
|
-
this.emitEvent({
|
|
5958
|
-
type: "tool_approval",
|
|
5959
|
-
agent: agent2._name,
|
|
5960
|
-
tool: toolName,
|
|
5961
|
-
data: {
|
|
5962
|
-
approved: approvalOutcome.approved,
|
|
5963
|
-
args: toolArgs,
|
|
5964
|
-
...approvalOutcome.reason ? { reason: approvalOutcome.reason } : {}
|
|
5965
|
-
}
|
|
5966
|
-
});
|
|
5967
|
-
if (!approvalOutcome.approved) continue;
|
|
5968
|
-
}
|
|
5969
|
-
if (tool2 && tool2.hooks?.before) {
|
|
5970
|
-
try {
|
|
5971
|
-
toolArgs = await tool2.hooks.before(toolArgs, this);
|
|
5972
|
-
} catch (err) {
|
|
5973
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
5974
|
-
currentMessages.push({
|
|
5975
|
-
role: "tool",
|
|
5976
|
-
content: JSON.stringify({ error: `Before hook error: ${errorMsg}` }),
|
|
5977
|
-
tool_call_id: toolCall.id
|
|
5978
|
-
});
|
|
5979
|
-
continue;
|
|
5980
|
-
}
|
|
5981
|
-
}
|
|
5982
|
-
const executeTool = async () => {
|
|
5983
|
-
let toolResult2;
|
|
5984
|
-
let resultContent2;
|
|
5985
|
-
if (isMcpTool && this.mcpManager) {
|
|
5986
|
-
try {
|
|
5987
|
-
const mcpResult = await this.mcpManager.callTool(toolName, toolArgs);
|
|
5988
|
-
toolResult2 = mcpResult;
|
|
5989
|
-
resultContent2 = mcpResult.content.map(
|
|
5990
|
-
(c) => c.type === "text" ? c.text : `[${c.type}]`
|
|
5991
|
-
).join("\n");
|
|
5992
|
-
if (mcpResult.isError) {
|
|
5993
|
-
resultContent2 = `Error: ${resultContent2}`;
|
|
5994
|
-
}
|
|
5995
|
-
} catch (err) {
|
|
5996
|
-
toolResult2 = { error: err instanceof Error ? err.message : String(err) };
|
|
5997
|
-
resultContent2 = JSON.stringify(toolResult2);
|
|
5998
|
-
}
|
|
5999
|
-
} else if (tool2) {
|
|
6000
|
-
const childCtx = this.createChildContext();
|
|
6001
|
-
try {
|
|
6002
|
-
toolResult2 = await tool2._execute(toolArgs, childCtx);
|
|
6003
|
-
} catch (err) {
|
|
6004
|
-
toolResult2 = { error: err instanceof Error ? err.message : String(err) };
|
|
6005
|
-
}
|
|
6006
|
-
if (tool2.hooks?.after && !(toolResult2 && typeof toolResult2 === "object" && "error" in toolResult2)) {
|
|
6007
|
-
try {
|
|
6008
|
-
toolResult2 = await tool2.hooks.after(toolResult2, this);
|
|
6009
|
-
} catch (err) {
|
|
6010
|
-
toolResult2 = {
|
|
6011
|
-
error: `After hook error: ${err instanceof Error ? err.message : String(err)}`
|
|
6012
|
-
};
|
|
6013
|
-
}
|
|
6014
|
-
}
|
|
6015
|
-
resultContent2 = tool2.sensitive ? "[REDACTED - sensitive tool output]" : JSON.stringify(toolResult2);
|
|
6016
|
-
} else {
|
|
6017
|
-
toolResult2 = void 0;
|
|
6018
|
-
resultContent2 = "Tool execution error";
|
|
6019
|
-
}
|
|
6020
|
-
return { toolResult: toolResult2, resultContent: resultContent2 };
|
|
6021
|
-
};
|
|
6022
|
-
const traceName = isMcpTool && this.mcpManager ? this.mcpManager.getQualifiedName(toolName) ?? toolName : toolName;
|
|
6023
|
-
const { toolResult, resultContent } = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6024
|
-
"axl.tool.call",
|
|
6025
|
-
{
|
|
6026
|
-
"axl.tool.name": traceName,
|
|
6027
|
-
"axl.agent.name": agent2._name
|
|
6028
|
-
},
|
|
6029
|
-
async (span) => {
|
|
6030
|
-
const r = await executeTool();
|
|
6031
|
-
span.setAttribute("axl.tool.duration", Date.now() - toolStart);
|
|
6032
|
-
const isError = r.toolResult && typeof r.toolResult === "object" && "error" in r.toolResult;
|
|
6033
|
-
span.setAttribute("axl.tool.success", !isError);
|
|
6034
|
-
if (isError)
|
|
6035
|
-
span.setStatus(
|
|
6036
|
-
"error",
|
|
6037
|
-
r.toolResult.error
|
|
6038
|
-
);
|
|
6039
|
-
return r;
|
|
6040
|
-
}
|
|
6041
|
-
) : await executeTool();
|
|
6042
|
-
this.emitEvent({
|
|
6043
|
-
type: "tool_call_end",
|
|
6044
|
-
agent: agent2._name,
|
|
6045
|
-
tool: traceName,
|
|
6046
|
-
duration: Date.now() - toolStart,
|
|
6047
|
-
data: { args: toolArgs, result: toolResult, callId: toolCall.id }
|
|
6048
|
-
});
|
|
6049
|
-
currentMessages.push({
|
|
6050
|
-
role: "tool",
|
|
6051
|
-
content: resultContent,
|
|
6052
|
-
tool_call_id: toolCall.id
|
|
6053
|
-
});
|
|
7000
|
+
await this.observeToolInvocation(
|
|
7001
|
+
agent2,
|
|
7002
|
+
toolCall,
|
|
7003
|
+
currentMessages,
|
|
7004
|
+
toolDefs.map((tool2) => tool2.function.name)
|
|
7005
|
+
);
|
|
6054
7006
|
}
|
|
6055
7007
|
continue;
|
|
6056
7008
|
}
|
|
@@ -6170,7 +7122,7 @@ ${appendedSchemaText}`;
|
|
|
6170
7122
|
pendingRetryReason = "schema";
|
|
6171
7123
|
continue;
|
|
6172
7124
|
}
|
|
6173
|
-
const zodErr = schemaErr instanceof
|
|
7125
|
+
const zodErr = schemaErr instanceof ZodError2 ? schemaErr : new ZodError2([
|
|
6174
7126
|
{
|
|
6175
7127
|
code: "custom",
|
|
6176
7128
|
path: [],
|
|
@@ -6187,6 +7139,7 @@ ${appendedSchemaText}`;
|
|
|
6187
7139
|
metadata: this.metadata
|
|
6188
7140
|
});
|
|
6189
7141
|
} catch (err) {
|
|
7142
|
+
rethrowEventStreamOverflow(err);
|
|
6190
7143
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6191
7144
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
6192
7145
|
}
|
|
@@ -6523,12 +7476,12 @@ ${summaryResponse.content}`
|
|
|
6523
7476
|
* Execute a function with checkpoint-replay semantics.
|
|
6524
7477
|
*
|
|
6525
7478
|
* On first execution, runs `fn()`, saves the result under `name`, and
|
|
6526
|
-
* returns it.
|
|
6527
|
-
* without re-executing.
|
|
6528
|
-
*
|
|
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.
|
|
6529
7482
|
*
|
|
6530
7483
|
* `name` must be a stable, caller-supplied identifier — the same call
|
|
6531
|
-
* site MUST pass the same name across
|
|
7484
|
+
* site MUST pass the same name across evaluations of the same execution for
|
|
6532
7485
|
* replay to work. Names are scoped to a single execution and must be
|
|
6533
7486
|
* unique within it; reusing a name (e.g. inside a loop) gives
|
|
6534
7487
|
* last-write-wins behavior. For loops, compose names from the iterator:
|
|
@@ -6651,6 +7604,7 @@ ${summaryResponse.content}`
|
|
|
6651
7604
|
for (let i = 0; i < n; i++) {
|
|
6652
7605
|
const index = i;
|
|
6653
7606
|
const p = signalStorage.run(composedSignal, () => fn(index));
|
|
7607
|
+
this._registerBranchDrain(p);
|
|
6654
7608
|
p.then((value) => {
|
|
6655
7609
|
if (settled) return;
|
|
6656
7610
|
results[index] = { ok: true, value };
|
|
@@ -6665,6 +7619,14 @@ ${summaryResponse.content}`
|
|
|
6665
7619
|
reject(new QuorumNotMet(quorum, successCount, results));
|
|
6666
7620
|
}
|
|
6667
7621
|
}).catch((err) => {
|
|
7622
|
+
if (isEventStreamOverflowError(err)) {
|
|
7623
|
+
if (!settled) {
|
|
7624
|
+
settled = true;
|
|
7625
|
+
controller.abort();
|
|
7626
|
+
reject(err);
|
|
7627
|
+
}
|
|
7628
|
+
return;
|
|
7629
|
+
}
|
|
6668
7630
|
if (settled) return;
|
|
6669
7631
|
const isAbort = err instanceof DOMException && err.name === "AbortError";
|
|
6670
7632
|
if (isAbort) {
|
|
@@ -6687,14 +7649,16 @@ ${summaryResponse.content}`
|
|
|
6687
7649
|
}
|
|
6688
7650
|
const parentSignal = this.currentSignal;
|
|
6689
7651
|
const promises = Array.from({ length: n }, (_, i) => {
|
|
6690
|
-
const run = () => fn(i).then((value) => ({ ok: true, value })).catch(
|
|
6691
|
-
(err)
|
|
7652
|
+
const run = () => fn(i).then((value) => ({ ok: true, value })).catch((err) => {
|
|
7653
|
+
rethrowEventStreamOverflow(err);
|
|
7654
|
+
return {
|
|
6692
7655
|
ok: false,
|
|
6693
7656
|
error: err instanceof Error ? err.message : String(err)
|
|
6694
|
-
}
|
|
6695
|
-
);
|
|
7657
|
+
};
|
|
7658
|
+
});
|
|
6696
7659
|
return parentSignal ? signalStorage.run(parentSignal, run) : run();
|
|
6697
7660
|
});
|
|
7661
|
+
for (const promise of promises) this._registerBranchDrain(promise);
|
|
6698
7662
|
return Promise.all(promises);
|
|
6699
7663
|
}
|
|
6700
7664
|
// ── ctx.vote() ────────────────────────────────────────────────────────
|
|
@@ -6832,6 +7796,7 @@ ${summaryResponse.content}`
|
|
|
6832
7796
|
try {
|
|
6833
7797
|
validateResult = await options.validate(parsed, { metadata: this.metadata });
|
|
6834
7798
|
} catch (err) {
|
|
7799
|
+
rethrowEventStreamOverflow(err);
|
|
6835
7800
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6836
7801
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
6837
7802
|
}
|
|
@@ -6849,6 +7814,7 @@ ${summaryResponse.content}`
|
|
|
6849
7814
|
emitVerifyOutcome(true, attempt + 1);
|
|
6850
7815
|
return parsed;
|
|
6851
7816
|
} catch (err) {
|
|
7817
|
+
rethrowEventStreamOverflow(err);
|
|
6852
7818
|
if (err instanceof ValidationError) {
|
|
6853
7819
|
lastRetry = {
|
|
6854
7820
|
error: err.reason,
|
|
@@ -6871,12 +7837,12 @@ ${summaryResponse.content}`
|
|
|
6871
7837
|
}
|
|
6872
7838
|
continue;
|
|
6873
7839
|
}
|
|
6874
|
-
const errorMsg = err instanceof
|
|
7840
|
+
const errorMsg = err instanceof ZodError2 ? err.message : err instanceof Error ? err.message : String(err);
|
|
6875
7841
|
lastRetry = { error: errorMsg, output: rawOutput };
|
|
6876
7842
|
if (attempt === maxRetries) {
|
|
6877
7843
|
emitVerifyOutcome(false, attempt + 1, errorMsg);
|
|
6878
7844
|
if (options?.fallback !== void 0) return options.fallback;
|
|
6879
|
-
const zodErr = err instanceof
|
|
7845
|
+
const zodErr = err instanceof ZodError2 ? err : new ZodError2([{ code: "custom", path: [], message: errorMsg }]);
|
|
6880
7846
|
throw new VerifyError(rawOutput, zodErr, maxRetries);
|
|
6881
7847
|
}
|
|
6882
7848
|
}
|
|
@@ -6885,7 +7851,7 @@ ${summaryResponse.content}`
|
|
|
6885
7851
|
if (options?.fallback !== void 0) return options.fallback;
|
|
6886
7852
|
throw new VerifyError(
|
|
6887
7853
|
lastRetry?.output,
|
|
6888
|
-
new
|
|
7854
|
+
new ZodError2([{ code: "custom", path: [], message: "Verify failed" }]),
|
|
6889
7855
|
maxRetries
|
|
6890
7856
|
);
|
|
6891
7857
|
}
|
|
@@ -6915,6 +7881,7 @@ ${summaryResponse.content}`
|
|
|
6915
7881
|
const unpriced = this.budgetContext.unpriced;
|
|
6916
7882
|
return { value, budgetExceeded: exceeded, totalCost, unpriced };
|
|
6917
7883
|
} catch (err) {
|
|
7884
|
+
rethrowEventStreamOverflow(err);
|
|
6918
7885
|
if (this.budgetContext.exceeded) {
|
|
6919
7886
|
return {
|
|
6920
7887
|
value: null,
|
|
@@ -7015,7 +7982,8 @@ ${summaryResponse.content}`
|
|
|
7015
7982
|
let remaining = fns.length;
|
|
7016
7983
|
for (const fn of fns) {
|
|
7017
7984
|
const p = signalStorage.run(composedSignal, fn);
|
|
7018
|
-
|
|
7985
|
+
this._registerBranchDrain(p);
|
|
7986
|
+
const continuation = p.then(async (value) => {
|
|
7019
7987
|
if (settled) return;
|
|
7020
7988
|
if (schema) {
|
|
7021
7989
|
const parsed = schema.safeParse(value);
|
|
@@ -7045,6 +8013,7 @@ ${summaryResponse.content}`
|
|
|
7045
8013
|
return;
|
|
7046
8014
|
}
|
|
7047
8015
|
} catch (err) {
|
|
8016
|
+
rethrowEventStreamOverflow(err);
|
|
7048
8017
|
remaining--;
|
|
7049
8018
|
lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
|
|
7050
8019
|
if (remaining === 0 && !settled) {
|
|
@@ -7064,6 +8033,14 @@ ${summaryResponse.content}`
|
|
|
7064
8033
|
controller.abort();
|
|
7065
8034
|
resolve2(value);
|
|
7066
8035
|
}).catch((err) => {
|
|
8036
|
+
if (isEventStreamOverflowError(err)) {
|
|
8037
|
+
if (!settled) {
|
|
8038
|
+
settled = true;
|
|
8039
|
+
controller.abort();
|
|
8040
|
+
reject(err);
|
|
8041
|
+
}
|
|
8042
|
+
throw err;
|
|
8043
|
+
}
|
|
7067
8044
|
if (settled) return;
|
|
7068
8045
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
7069
8046
|
remaining--;
|
|
@@ -7080,6 +8057,7 @@ ${summaryResponse.content}`
|
|
|
7080
8057
|
reject(lastError);
|
|
7081
8058
|
}
|
|
7082
8059
|
});
|
|
8060
|
+
this._registerBranchDrain(continuation);
|
|
7083
8061
|
}
|
|
7084
8062
|
});
|
|
7085
8063
|
}
|
|
@@ -7118,9 +8096,19 @@ ${summaryResponse.content}`
|
|
|
7118
8096
|
const idx = nextIndex++;
|
|
7119
8097
|
try {
|
|
7120
8098
|
const value = mapSignal ? await signalStorage.run(mapSignal, () => fn(items[idx], idx)) : await fn(items[idx], idx);
|
|
8099
|
+
if (settled) return;
|
|
7121
8100
|
results[idx] = { ok: true, value };
|
|
7122
8101
|
successCount++;
|
|
7123
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
|
+
}
|
|
7124
8112
|
if (err instanceof DOMException && err.name === "AbortError" && controller?.signal.aborted) {
|
|
7125
8113
|
completedCount++;
|
|
7126
8114
|
return;
|
|
@@ -7146,7 +8134,9 @@ ${summaryResponse.content}`
|
|
|
7146
8134
|
};
|
|
7147
8135
|
const workers = Math.min(concurrency, items.length);
|
|
7148
8136
|
for (let i = 0; i < workers; i++) {
|
|
7149
|
-
runNext()
|
|
8137
|
+
const worker = runNext();
|
|
8138
|
+
this._registerBranchDrain(worker);
|
|
8139
|
+
worker.catch((err) => {
|
|
7150
8140
|
if (!settled) reject(err);
|
|
7151
8141
|
});
|
|
7152
8142
|
}
|
|
@@ -7172,12 +8162,44 @@ ${summaryResponse.content}`
|
|
|
7172
8162
|
return this._awaitHumanImpl(options);
|
|
7173
8163
|
}
|
|
7174
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
|
+
};
|
|
7175
8193
|
if (this.awaitHumanHandler) {
|
|
8194
|
+
if (branchSignal?.aborted) throw makeAbortError(branchSignal.reason);
|
|
7176
8195
|
this.emitEvent({
|
|
7177
8196
|
type: "await_human",
|
|
7178
8197
|
data: { channel: options.channel, prompt: options.prompt }
|
|
7179
8198
|
});
|
|
7180
|
-
|
|
8199
|
+
if (branchSignal?.aborted) throw makeAbortError(branchSignal.reason);
|
|
8200
|
+
const decision2 = parseHumanDecision(
|
|
8201
|
+
await awaitWithAbort(Promise.resolve(this.awaitHumanHandler(options)))
|
|
8202
|
+
);
|
|
7181
8203
|
this.emitEvent({
|
|
7182
8204
|
type: "await_human_resolved",
|
|
7183
8205
|
data: { channel: options.channel, decision: decision2 }
|
|
@@ -7189,68 +8211,126 @@ ${summaryResponse.content}`
|
|
|
7189
8211
|
"Tool requires approval but no approval handler is configured. Provide awaitHumanHandler to createContext() or use runtime.execute() with workflow infrastructure."
|
|
7190
8212
|
);
|
|
7191
8213
|
}
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
awaitHumanPrompt: options.prompt
|
|
7209
|
-
}
|
|
7210
|
-
});
|
|
7211
|
-
}
|
|
7212
|
-
this.emitEvent({
|
|
7213
|
-
type: "await_human",
|
|
7214
|
-
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;
|
|
7215
8230
|
});
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
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
|
|
7226
8249
|
};
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
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
|
+
});
|
|
7231
8269
|
}
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
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
|
+
}
|
|
7237
8317
|
}
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
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
|
+
}
|
|
7242
8330
|
this.emitEvent({
|
|
7243
8331
|
type: "await_human_resolved",
|
|
7244
8332
|
data: { channel: options.channel, decision }
|
|
7245
8333
|
});
|
|
7246
|
-
if (this.stateStore) {
|
|
7247
|
-
await this.stateStore.saveExecutionState(this.executionId, {
|
|
7248
|
-
workflow: this.workflowName ?? "unknown",
|
|
7249
|
-
input: this.input,
|
|
7250
|
-
step: this.stepRefRoot.value,
|
|
7251
|
-
status: "running"
|
|
7252
|
-
});
|
|
7253
|
-
}
|
|
7254
8334
|
return decision;
|
|
7255
8335
|
}
|
|
7256
8336
|
/**
|
|
@@ -7302,8 +8382,8 @@ ${summaryResponse.content}`
|
|
|
7302
8382
|
// Internal: the runtime is the only caller, user workflows never call these.
|
|
7303
8383
|
/** @internal — idempotent; no-ops on second+ call within one ctx. */
|
|
7304
8384
|
_emitWorkflowStart(input) {
|
|
7305
|
-
if (this.
|
|
7306
|
-
this.
|
|
8385
|
+
if (this.workflowLifecycleState.startEmitted) return;
|
|
8386
|
+
this.workflowLifecycleState.startEmitted = true;
|
|
7307
8387
|
this.emitEvent({
|
|
7308
8388
|
type: "workflow_start",
|
|
7309
8389
|
workflow: this.workflowName,
|
|
@@ -7319,8 +8399,8 @@ ${summaryResponse.content}`
|
|
|
7319
8399
|
* status. First-wins semantics: the completed event stands, the
|
|
7320
8400
|
* inner side-effect failure propagates as a normal thrown error. */
|
|
7321
8401
|
_emitWorkflowEnd(info) {
|
|
7322
|
-
if (this.
|
|
7323
|
-
this.
|
|
8402
|
+
if (this.workflowLifecycleState.endEmitted) return;
|
|
8403
|
+
this.workflowLifecycleState.endEmitted = true;
|
|
7324
8404
|
this.emitEvent({
|
|
7325
8405
|
type: "workflow_end",
|
|
7326
8406
|
workflow: this.workflowName,
|
|
@@ -7330,7 +8410,8 @@ ${summaryResponse.content}`
|
|
|
7330
8410
|
duration: info.duration,
|
|
7331
8411
|
...info.result !== void 0 ? { result: info.result } : {},
|
|
7332
8412
|
...info.error !== void 0 ? { error: info.error } : {},
|
|
7333
|
-
...info.aborted ? { aborted: true } : {}
|
|
8413
|
+
...info.aborted ? { aborted: true } : {},
|
|
8414
|
+
...info.observation ? { observation: info.observation } : {}
|
|
7334
8415
|
}
|
|
7335
8416
|
});
|
|
7336
8417
|
}
|
|
@@ -7619,6 +8700,7 @@ ${summaryResponse.content}`
|
|
|
7619
8700
|
* consumer sites via the `type` discriminator.
|
|
7620
8701
|
*/
|
|
7621
8702
|
emitEvent(partial) {
|
|
8703
|
+
if (this.workflowLifecycleState.endEmitted && partial.type !== "workflow_end") return;
|
|
7622
8704
|
const data = partial.data;
|
|
7623
8705
|
const frame = askStorage.getStore();
|
|
7624
8706
|
const step = (frame?.stepRef ?? this.stepRefRoot).value++;
|
|
@@ -7644,6 +8726,7 @@ ${summaryResponse.content}`
|
|
|
7644
8726
|
}
|
|
7645
8727
|
}
|
|
7646
8728
|
const event = {
|
|
8729
|
+
schemaVersion: 2,
|
|
7647
8730
|
executionId: this.executionId,
|
|
7648
8731
|
step,
|
|
7649
8732
|
timestamp: Date.now(),
|
|
@@ -7664,13 +8747,13 @@ ${summaryResponse.content}`
|
|
|
7664
8747
|
...partial,
|
|
7665
8748
|
data
|
|
7666
8749
|
};
|
|
7667
|
-
const finalEvent = this.config.trace?.redact ?
|
|
8750
|
+
const finalEvent = this.config.trace?.redact ? redactHistoricalEvent(event) : event;
|
|
7668
8751
|
let overflowErr;
|
|
7669
8752
|
if (this.onTrace) {
|
|
7670
8753
|
try {
|
|
7671
8754
|
this.onTrace(finalEvent);
|
|
7672
8755
|
} catch (err) {
|
|
7673
|
-
if (err
|
|
8756
|
+
if (isEventStreamOverflowError(err)) {
|
|
7674
8757
|
overflowErr = err;
|
|
7675
8758
|
} else {
|
|
7676
8759
|
console.error(
|
|
@@ -7685,7 +8768,7 @@ ${summaryResponse.content}`
|
|
|
7685
8768
|
try {
|
|
7686
8769
|
bus._push(finalEvent);
|
|
7687
8770
|
} catch (err) {
|
|
7688
|
-
if (err
|
|
8771
|
+
if (isEventStreamOverflowError(err)) {
|
|
7689
8772
|
if (!overflowErr) {
|
|
7690
8773
|
overflowErr = err;
|
|
7691
8774
|
} else {
|
|
@@ -7872,26 +8955,31 @@ var MemoryStore = class {
|
|
|
7872
8955
|
}
|
|
7873
8956
|
// ── Execution History ──────────────────────────────────────────────
|
|
7874
8957
|
async saveExecution(execution) {
|
|
7875
|
-
|
|
8958
|
+
const normalized = normalizeStoredExecution({
|
|
8959
|
+
...execution,
|
|
8960
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
8961
|
+
});
|
|
8962
|
+
this.executionHistory.set(execution.executionId, structuredClone(normalized));
|
|
7876
8963
|
}
|
|
7877
8964
|
async getExecution(executionId) {
|
|
7878
8965
|
const exec = this.executionHistory.get(executionId);
|
|
7879
|
-
return exec ? structuredClone(exec) : null;
|
|
8966
|
+
return exec ? normalizeStoredExecution(structuredClone(exec)) : null;
|
|
7880
8967
|
}
|
|
7881
8968
|
async listExecutions(limit) {
|
|
7882
8969
|
const sorted = [...this.executionHistory.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
7883
8970
|
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
7884
|
-
return result.map((e) => structuredClone(e));
|
|
8971
|
+
return result.map((e) => normalizeStoredExecution(structuredClone(e)));
|
|
7885
8972
|
}
|
|
7886
8973
|
async deleteExecution(executionId) {
|
|
7887
|
-
const
|
|
7888
|
-
this.checkpoints.delete(executionId);
|
|
7889
|
-
this.executionStates.delete(executionId);
|
|
7890
|
-
this.streamingEvents.delete(executionId);
|
|
7891
|
-
|
|
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) {
|
|
7892
8980
|
this.persistAwaitHumanState();
|
|
7893
8981
|
}
|
|
7894
|
-
return
|
|
8982
|
+
return removedHistory || removedCheckpoints || removedState || removedStreaming || removedDecision;
|
|
7895
8983
|
}
|
|
7896
8984
|
// ── Eval History ──────────────────────────────────────────────────
|
|
7897
8985
|
async saveEvalResult(entry) {
|
|
@@ -7997,16 +9085,23 @@ function rowToExecutionInfo(row) {
|
|
|
7997
9085
|
startedAt: row.started_at,
|
|
7998
9086
|
completedAt: row.completed_at ?? void 0,
|
|
7999
9087
|
duration: row.duration,
|
|
8000
|
-
|
|
8001
|
-
|
|
9088
|
+
events: safeJsonParse(row.events) ?? [],
|
|
9089
|
+
...row.event_schema_version != null ? { eventSchemaVersion: row.event_schema_version } : {}
|
|
8002
9090
|
};
|
|
9091
|
+
if (row.error != null) info.error = row.error;
|
|
8003
9092
|
if (row.metadata != null) {
|
|
8004
9093
|
const parsed = safeJsonParse(row.metadata);
|
|
8005
9094
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
8006
9095
|
info.metadata = parsed;
|
|
8007
9096
|
}
|
|
8008
9097
|
}
|
|
8009
|
-
|
|
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);
|
|
8010
9105
|
}
|
|
8011
9106
|
var SQLiteStore = class {
|
|
8012
9107
|
db;
|
|
@@ -8046,6 +9141,10 @@ var SQLiteStore = class {
|
|
|
8046
9141
|
* column (TEXT, nullable, JSON-serialized) so caller-supplied
|
|
8047
9142
|
* `ExecuteOptions.metadata` round-trips through history. Existing
|
|
8048
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.
|
|
8049
9148
|
*
|
|
8050
9149
|
* Applied BEFORE `initTables()` so the subsequent `CREATE TABLE
|
|
8051
9150
|
* IF NOT EXISTS` runs against the post-migration column name. Fresh
|
|
@@ -8063,7 +9162,7 @@ var SQLiteStore = class {
|
|
|
8063
9162
|
* transactional re-read costs nothing.
|
|
8064
9163
|
*/
|
|
8065
9164
|
migrate() {
|
|
8066
|
-
const TARGET_VERSION =
|
|
9165
|
+
const TARGET_VERSION = 5;
|
|
8067
9166
|
const current = this.db.pragma("user_version", { simple: true });
|
|
8068
9167
|
if (current >= TARGET_VERSION) return;
|
|
8069
9168
|
this.db.exec("BEGIN IMMEDIATE");
|
|
@@ -8091,6 +9190,18 @@ var SQLiteStore = class {
|
|
|
8091
9190
|
this.db.exec("ALTER TABLE execution_history ADD COLUMN metadata TEXT");
|
|
8092
9191
|
}
|
|
8093
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
|
+
}
|
|
8094
9205
|
this.db.pragma(`user_version = ${TARGET_VERSION}`);
|
|
8095
9206
|
this.db.exec("COMMIT");
|
|
8096
9207
|
} catch (err) {
|
|
@@ -8154,7 +9265,9 @@ var SQLiteStore = class {
|
|
|
8154
9265
|
duration INTEGER NOT NULL DEFAULT 0,
|
|
8155
9266
|
error TEXT,
|
|
8156
9267
|
events TEXT NOT NULL,
|
|
8157
|
-
metadata TEXT
|
|
9268
|
+
metadata TEXT,
|
|
9269
|
+
event_schema_version INTEGER,
|
|
9270
|
+
observation TEXT
|
|
8158
9271
|
);
|
|
8159
9272
|
|
|
8160
9273
|
CREATE TABLE IF NOT EXISTS eval_history (
|
|
@@ -8239,7 +9352,7 @@ var SQLiteStore = class {
|
|
|
8239
9352
|
const stmt = this.db.prepare("DELETE FROM decisions WHERE execution_id = ?");
|
|
8240
9353
|
stmt.run(executionId);
|
|
8241
9354
|
}
|
|
8242
|
-
// ──
|
|
9355
|
+
// ── Legacy application-managed execution state ────────────────────────
|
|
8243
9356
|
async saveExecutionState(executionId, state) {
|
|
8244
9357
|
const stmt = this.db.prepare(
|
|
8245
9358
|
"INSERT OR REPLACE INTO execution_state (execution_id, workflow, input, step, status, metadata) VALUES (?, ?, ?, ?, ?, ?)"
|
|
@@ -8274,19 +9387,25 @@ var SQLiteStore = class {
|
|
|
8274
9387
|
}
|
|
8275
9388
|
// ── Execution History ────────────────────────────────────────────────────
|
|
8276
9389
|
async saveExecution(execution) {
|
|
9390
|
+
const normalized = normalizeStoredExecution({
|
|
9391
|
+
...execution,
|
|
9392
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
9393
|
+
});
|
|
8277
9394
|
this.db.prepare(
|
|
8278
|
-
"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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
8279
9396
|
).run(
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
JSON.stringify(
|
|
8289
|
-
|
|
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
|
|
8290
9409
|
);
|
|
8291
9410
|
}
|
|
8292
9411
|
async getExecution(executionId) {
|
|
@@ -8300,11 +9419,11 @@ var SQLiteStore = class {
|
|
|
8300
9419
|
}
|
|
8301
9420
|
async deleteExecution(executionId) {
|
|
8302
9421
|
const tx = this.db.transaction(() => {
|
|
8303
|
-
const
|
|
8304
|
-
this.db.prepare("DELETE FROM checkpoints WHERE execution_id = ?").run(executionId);
|
|
8305
|
-
this.db.prepare("DELETE FROM execution_state WHERE execution_id = ?").run(executionId);
|
|
8306
|
-
this.db.prepare("DELETE FROM decisions WHERE execution_id = ?").run(executionId);
|
|
8307
|
-
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;
|
|
8308
9427
|
});
|
|
8309
9428
|
return tx();
|
|
8310
9429
|
}
|
|
@@ -8630,6 +9749,11 @@ var AxlStream = class extends Readable {
|
|
|
8630
9749
|
get lifecycle() {
|
|
8631
9750
|
return this.events.lifecycle;
|
|
8632
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
|
+
}
|
|
8633
9757
|
/** Coalescing iterator over `partial_object` events — yields the latest
|
|
8634
9758
|
* object payload per `askId`. Designed for streaming-structured-output
|
|
8635
9759
|
* UIs: when the producer outpaces the consumer, intermediate snapshots
|
|
@@ -8728,6 +9852,7 @@ var AxlStream = class extends Readable {
|
|
|
8728
9852
|
if (this.events.isFinished) return;
|
|
8729
9853
|
this.result = result;
|
|
8730
9854
|
const doneEvent = {
|
|
9855
|
+
schemaVersion: 2,
|
|
8731
9856
|
type: "done",
|
|
8732
9857
|
executionId,
|
|
8733
9858
|
step: Number.MAX_SAFE_INTEGER,
|
|
@@ -8746,6 +9871,7 @@ var AxlStream = class extends Readable {
|
|
|
8746
9871
|
_error(error, executionId) {
|
|
8747
9872
|
if (this.events.isFinished) return;
|
|
8748
9873
|
const errorEvent = {
|
|
9874
|
+
schemaVersion: 2,
|
|
8749
9875
|
type: "error",
|
|
8750
9876
|
executionId,
|
|
8751
9877
|
step: Number.MAX_SAFE_INTEGER,
|
|
@@ -9324,7 +10450,7 @@ async function createSpanManager(config) {
|
|
|
9324
10450
|
if (!config?.enabled) {
|
|
9325
10451
|
return new NoopSpanManager();
|
|
9326
10452
|
}
|
|
9327
|
-
const { OTelSpanManager } = await import("./span-manager-
|
|
10453
|
+
const { OTelSpanManager } = await import("./span-manager-74ZCGELO.js");
|
|
9328
10454
|
return OTelSpanManager.create(config);
|
|
9329
10455
|
}
|
|
9330
10456
|
|
|
@@ -9346,11 +10472,7 @@ var STREAMING_EXCLUDED_TYPES = /* @__PURE__ */ new Set([
|
|
|
9346
10472
|
"partial_object",
|
|
9347
10473
|
"string_delta"
|
|
9348
10474
|
]);
|
|
9349
|
-
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
9350
|
-
"sessionId",
|
|
9351
|
-
"sessionHistory",
|
|
9352
|
-
"resumeMode"
|
|
9353
|
-
]);
|
|
10475
|
+
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["sessionId", "sessionHistory"]);
|
|
9354
10476
|
function liftPersistedMetadata(metadata) {
|
|
9355
10477
|
if (!metadata) return void 0;
|
|
9356
10478
|
const filtered = {};
|
|
@@ -9468,7 +10590,7 @@ var StreamingFlusher = class {
|
|
|
9468
10590
|
await Promise.allSettled(pendings);
|
|
9469
10591
|
}
|
|
9470
10592
|
};
|
|
9471
|
-
function
|
|
10593
|
+
function isAbortError4(err) {
|
|
9472
10594
|
return typeof err === "object" && err !== null && err.name === "AbortError";
|
|
9473
10595
|
}
|
|
9474
10596
|
function pushEventBounded(execInfo, event, cap) {
|
|
@@ -9480,7 +10602,18 @@ function pushEventBounded(execInfo, event, cap) {
|
|
|
9480
10602
|
const last = execInfo.events[execInfo.events.length - 1];
|
|
9481
10603
|
const alreadyTruncated = last?.type === "log" && typeof last.data === "object" && last.data !== null && last.data.event === "events_truncated";
|
|
9482
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
|
+
}
|
|
9483
10615
|
execInfo.events[execInfo.events.length - 1] = {
|
|
10616
|
+
schemaVersion: 2,
|
|
9484
10617
|
type: "log",
|
|
9485
10618
|
executionId: event.executionId,
|
|
9486
10619
|
step: event.step,
|
|
@@ -9509,7 +10642,9 @@ function boundRecoveredEvents(events, cap) {
|
|
|
9509
10642
|
const last = truncated[truncated.length - 1];
|
|
9510
10643
|
const alreadyTruncated = last?.type === "log" && typeof last.data === "object" && last.data !== null && last.data.event === "events_truncated";
|
|
9511
10644
|
if (!alreadyTruncated) {
|
|
10645
|
+
const version = getEventSchemaVersion(events[0] ?? {});
|
|
9512
10646
|
truncated[truncated.length - 1] = {
|
|
10647
|
+
...version === 2 ? { schemaVersion: 2 } : {},
|
|
9513
10648
|
type: "log",
|
|
9514
10649
|
executionId: events[0]?.executionId ?? "",
|
|
9515
10650
|
step: last?.step ?? 0,
|
|
@@ -9523,6 +10658,16 @@ function boundRecoveredEvents(events, cap) {
|
|
|
9523
10658
|
}
|
|
9524
10659
|
return truncated;
|
|
9525
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
|
+
}
|
|
9526
10671
|
var costScopeStorage = new AsyncLocalStorage2();
|
|
9527
10672
|
function forwardAbortSignal(external, internal) {
|
|
9528
10673
|
if (!external) return () => {
|
|
@@ -9684,9 +10829,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9684
10829
|
* → parse output → emit `workflow_end` → delete checkpoints → persist.
|
|
9685
10830
|
*
|
|
9686
10831
|
* Shared between `execute()` and `stream()`. Centralizes the
|
|
9687
|
-
* "start iff end" pairing invariant
|
|
9688
|
-
* (e.g., `resumeExecution`) can inherit correct behavior by routing
|
|
9689
|
-
* through this helper. Also factors out the AbortError detection.
|
|
10832
|
+
* "start iff end" pairing invariant and AbortError detection.
|
|
9690
10833
|
*
|
|
9691
10834
|
* Side effects on `execInfo`: sets `status` / `completedAt` /
|
|
9692
10835
|
* `duration` / `result` (or `error`) before emitting `workflow_end`.
|
|
@@ -9710,18 +10853,23 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9710
10853
|
*/
|
|
9711
10854
|
async runWorkflowBody(args) {
|
|
9712
10855
|
const { workflow: workflow2, ctx, execInfo, validated, span, cleanupAbortForwarder } = args;
|
|
10856
|
+
let observation = execInfo.observation ?? { complete: true };
|
|
9713
10857
|
try {
|
|
9714
10858
|
ctx._emitWorkflowStart(validated);
|
|
9715
10859
|
const raw = await workflow2.handler(ctx);
|
|
10860
|
+
const drainedObservation = await ctx._drainBranchWork();
|
|
10861
|
+
observation = drainedObservation.complete === false ? drainedObservation : execInfo.observation ?? drainedObservation;
|
|
9716
10862
|
const result = workflow2.outputSchema ? workflow2.outputSchema.parse(raw) : raw;
|
|
9717
10863
|
execInfo.status = "completed";
|
|
9718
10864
|
execInfo.completedAt = Date.now();
|
|
9719
10865
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
9720
10866
|
execInfo.result = result;
|
|
10867
|
+
execInfo.observation = observation;
|
|
9721
10868
|
ctx._emitWorkflowEnd({
|
|
9722
10869
|
status: "completed",
|
|
9723
10870
|
duration: execInfo.duration,
|
|
9724
|
-
result
|
|
10871
|
+
result,
|
|
10872
|
+
observation
|
|
9725
10873
|
});
|
|
9726
10874
|
if (this.stateStore.deleteCheckpoints) {
|
|
9727
10875
|
await this.stateStore.deleteCheckpoints(execInfo.executionId);
|
|
@@ -9731,19 +10879,28 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9731
10879
|
this.persistExecution(execInfo);
|
|
9732
10880
|
return result;
|
|
9733
10881
|
} catch (err) {
|
|
9734
|
-
|
|
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);
|
|
9735
10890
|
execInfo.status = "failed";
|
|
9736
10891
|
execInfo.completedAt = Date.now();
|
|
9737
10892
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
9738
|
-
execInfo.error =
|
|
10893
|
+
execInfo.error = terminalError instanceof Error ? terminalError.message : String(terminalError);
|
|
10894
|
+
execInfo.observation = observation;
|
|
9739
10895
|
ctx._emitWorkflowEnd({
|
|
9740
10896
|
status: "failed",
|
|
9741
10897
|
duration: execInfo.duration,
|
|
9742
10898
|
error: execInfo.error,
|
|
9743
|
-
...aborted ? { aborted: true } : {}
|
|
10899
|
+
...aborted ? { aborted: true } : {},
|
|
10900
|
+
observation
|
|
9744
10901
|
});
|
|
9745
10902
|
this.persistExecution(execInfo);
|
|
9746
|
-
throw
|
|
10903
|
+
throw terminalError;
|
|
9747
10904
|
} finally {
|
|
9748
10905
|
this.abortControllers.delete(execInfo.executionId);
|
|
9749
10906
|
cleanupAbortForwarder?.();
|
|
@@ -9931,13 +11088,13 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9931
11088
|
return result;
|
|
9932
11089
|
}
|
|
9933
11090
|
/** Get all execution info (running + completed + historical). */
|
|
9934
|
-
/** Normalize an
|
|
9935
|
-
*
|
|
11091
|
+
/** Normalize an execution loaded from a `StateStore` so the
|
|
11092
|
+
* historical event union holds for downstream consumers
|
|
9936
11093
|
* (Studio aggregators, REST routes, redaction). Custom or legacy stores
|
|
9937
11094
|
* may persist rows without `events` (or with a non-array value) — without
|
|
9938
11095
|
* this guard, a single malformed row crashes every iterator over
|
|
9939
|
-
* `exec.events` and takes down dependent features.
|
|
9940
|
-
*
|
|
11096
|
+
* `exec.events` and takes down dependent features. Returns a normalized
|
|
11097
|
+
* copy; logs at most one warning per `executionId`. */
|
|
9941
11098
|
normalizeStoredExecution(exec) {
|
|
9942
11099
|
if (!Array.isArray(exec.events)) {
|
|
9943
11100
|
if (!this.warnedMalformedExecutions.has(exec.executionId)) {
|
|
@@ -9946,9 +11103,9 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9946
11103
|
`[axl] StateStore returned execution ${exec.executionId} with non-array events (got ${exec.events === null ? "null" : typeof exec.events}); coercing to []`
|
|
9947
11104
|
);
|
|
9948
11105
|
}
|
|
9949
|
-
exec
|
|
11106
|
+
return normalizeStoredExecution({ ...exec, events: [] });
|
|
9950
11107
|
}
|
|
9951
|
-
return exec;
|
|
11108
|
+
return normalizeStoredExecution(exec);
|
|
9952
11109
|
}
|
|
9953
11110
|
async getExecutions() {
|
|
9954
11111
|
if (!this.historicalExecutionsLoadPromise && this.stateStore.listExecutions) {
|
|
@@ -9958,8 +11115,9 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9958
11115
|
this.historicalExecutions.set(exec.executionId, this.normalizeStoredExecution(exec));
|
|
9959
11116
|
}
|
|
9960
11117
|
}
|
|
9961
|
-
}).catch(() => {
|
|
11118
|
+
}).catch((error) => {
|
|
9962
11119
|
this.historicalExecutionsLoadPromise = null;
|
|
11120
|
+
throw error;
|
|
9963
11121
|
});
|
|
9964
11122
|
}
|
|
9965
11123
|
if (this.historicalExecutionsLoadPromise) {
|
|
@@ -9977,6 +11135,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9977
11135
|
* and automatically emits trace events and tracks cost.
|
|
9978
11136
|
*/
|
|
9979
11137
|
createContext(options) {
|
|
11138
|
+
assertNoRemovedObservationCallbacks(options);
|
|
9980
11139
|
const executionId = randomUUID3();
|
|
9981
11140
|
const budgetLimit = options?.budget ? parseCost(options.budget) : Infinity;
|
|
9982
11141
|
this.registerWithCostScope(executionId);
|
|
@@ -9992,10 +11151,8 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
9992
11151
|
memoryManager: this.memoryManager,
|
|
9993
11152
|
sessionHistory: options?.sessionHistory,
|
|
9994
11153
|
signal: options?.signal,
|
|
9995
|
-
onToken: options?.onToken,
|
|
9996
|
-
onToolCall: options?.onToolCall,
|
|
9997
|
-
onAgentStart: options?.onAgentStart,
|
|
9998
11154
|
awaitHumanHandler: options?.awaitHumanHandler,
|
|
11155
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
9999
11156
|
eventStreamOptions: options?.events,
|
|
10000
11157
|
onTrace: (event) => {
|
|
10001
11158
|
this.emit("trace", event);
|
|
@@ -10029,6 +11186,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10029
11186
|
);
|
|
10030
11187
|
}
|
|
10031
11188
|
const validated = workflow2.inputSchema.parse(input);
|
|
11189
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(options?.branchDrainTimeoutMs);
|
|
10032
11190
|
const executionId = randomUUID3();
|
|
10033
11191
|
const controller = new AbortController();
|
|
10034
11192
|
this.abortControllers.set(executionId, controller);
|
|
@@ -10041,11 +11199,13 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10041
11199
|
executionId,
|
|
10042
11200
|
workflow: name,
|
|
10043
11201
|
status: "running",
|
|
11202
|
+
eventSchemaVersion: 2,
|
|
10044
11203
|
events: [],
|
|
10045
11204
|
totalCost: 0,
|
|
10046
11205
|
unpriced: false,
|
|
10047
11206
|
startedAt: Date.now(),
|
|
10048
11207
|
duration: 0,
|
|
11208
|
+
observation: { complete: true },
|
|
10049
11209
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
10050
11210
|
};
|
|
10051
11211
|
this.executions.set(executionId, execInfo);
|
|
@@ -10059,6 +11219,8 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10059
11219
|
sessionHistory,
|
|
10060
11220
|
signal: controller.signal,
|
|
10061
11221
|
eventStreamOptions: options?.events,
|
|
11222
|
+
branchDrainTimeoutMs,
|
|
11223
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
10062
11224
|
onTrace: (event) => {
|
|
10063
11225
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
10064
11226
|
execInfo.totalCost += eventCostContribution(event);
|
|
@@ -10103,7 +11265,6 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10103
11265
|
workflowName: name,
|
|
10104
11266
|
mcpManager: this.mcpManager,
|
|
10105
11267
|
memoryManager: this.memoryManager,
|
|
10106
|
-
resumeMode: !!options?.metadata?.resumeMode,
|
|
10107
11268
|
spanManager: this.spanManager,
|
|
10108
11269
|
budgetContext: {
|
|
10109
11270
|
totalCost: 0,
|
|
@@ -10134,6 +11295,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10134
11295
|
}
|
|
10135
11296
|
/** Execute a workflow and return a stream. */
|
|
10136
11297
|
stream(name, input, options) {
|
|
11298
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(options?.branchDrainTimeoutMs);
|
|
10137
11299
|
const axlStream = new AxlStream(options?.events);
|
|
10138
11300
|
const controller = new AbortController();
|
|
10139
11301
|
axlStream.on("close", () => controller.abort());
|
|
@@ -10154,11 +11316,13 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10154
11316
|
executionId,
|
|
10155
11317
|
workflow: name,
|
|
10156
11318
|
status: "running",
|
|
11319
|
+
eventSchemaVersion: 2,
|
|
10157
11320
|
events: [],
|
|
10158
11321
|
totalCost: 0,
|
|
10159
11322
|
unpriced: false,
|
|
10160
11323
|
startedAt: Date.now(),
|
|
10161
11324
|
duration: 0,
|
|
11325
|
+
observation: { complete: true },
|
|
10162
11326
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
10163
11327
|
};
|
|
10164
11328
|
this.executions.set(executionId, execInfo);
|
|
@@ -10171,12 +11335,11 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10171
11335
|
sessionHistory,
|
|
10172
11336
|
signal: controller.signal,
|
|
10173
11337
|
eventStreamOptions: options?.events,
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
//
|
|
10177
|
-
//
|
|
10178
|
-
|
|
10179
|
-
},
|
|
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,
|
|
10180
11343
|
onTrace: (event) => {
|
|
10181
11344
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
10182
11345
|
execInfo.totalCost += eventCostContribution(event);
|
|
@@ -10222,7 +11385,6 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10222
11385
|
workflowName: name,
|
|
10223
11386
|
mcpManager: this.mcpManager,
|
|
10224
11387
|
memoryManager: this.memoryManager,
|
|
10225
|
-
resumeMode: !!options?.metadata?.resumeMode,
|
|
10226
11388
|
spanManager: this.spanManager,
|
|
10227
11389
|
budgetContext: {
|
|
10228
11390
|
totalCost: 0,
|
|
@@ -10386,6 +11548,12 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10386
11548
|
await this.stateStore.finalizeStreamingEvents?.(id);
|
|
10387
11549
|
continue;
|
|
10388
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
|
+
}
|
|
10389
11557
|
const boundedEvents = boundRecoveredEvents(events, this.maxEventsPerExecution);
|
|
10390
11558
|
const workflowStartEvent = boundedEvents.find((e) => e.type === "workflow_start");
|
|
10391
11559
|
const lastEvent = boundedEvents[boundedEvents.length - 1];
|
|
@@ -10394,7 +11562,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10394
11562
|
const workflowName = workflowStartEvent?.workflow ?? RECOVERED_UNKNOWN_WORKFLOW;
|
|
10395
11563
|
const totalCost = boundedEvents.reduce((sum, e) => sum + eventCostContribution(e), 0);
|
|
10396
11564
|
const unpriced = boundedEvents.some(isUnpricedLeaf);
|
|
10397
|
-
const
|
|
11565
|
+
const executionBase = {
|
|
10398
11566
|
executionId: id,
|
|
10399
11567
|
workflow: workflowName,
|
|
10400
11568
|
status: "failed",
|
|
@@ -10406,6 +11574,16 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10406
11574
|
duration: completedAt - startedAt,
|
|
10407
11575
|
error: "process terminated (recovered from streaming buffer)"
|
|
10408
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
|
+
};
|
|
10409
11587
|
if (this.stateStore.saveExecution) {
|
|
10410
11588
|
try {
|
|
10411
11589
|
await this.stateStore.saveExecution(synthesized);
|
|
@@ -10486,7 +11664,8 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10486
11664
|
*/
|
|
10487
11665
|
async deleteExecution(id) {
|
|
10488
11666
|
const wasActive = this.executions.has(id);
|
|
10489
|
-
const
|
|
11667
|
+
const pendingDecisionResolver = this.pendingDecisionResolvers.get(id);
|
|
11668
|
+
const hadPendingDecision = pendingDecisionResolver !== void 0;
|
|
10490
11669
|
if (wasActive) {
|
|
10491
11670
|
this.pendingDeletedExecutions.add(id);
|
|
10492
11671
|
this.streamableExecutionIds.delete(id);
|
|
@@ -10495,8 +11674,12 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10495
11674
|
controller.abort();
|
|
10496
11675
|
}
|
|
10497
11676
|
}
|
|
10498
|
-
if (
|
|
10499
|
-
|
|
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
|
+
}
|
|
10500
11683
|
}
|
|
10501
11684
|
const existing = await this.getExecution(id);
|
|
10502
11685
|
const workflow2 = existing?.workflow;
|
|
@@ -10574,60 +11757,68 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
10574
11757
|
}
|
|
10575
11758
|
/** Resolve a pending human decision. */
|
|
10576
11759
|
async resolveDecision(executionId, decision) {
|
|
10577
|
-
|
|
11760
|
+
decision = parseHumanDecision(decision);
|
|
11761
|
+
let resolver = this.pendingDecisionResolvers.get(executionId);
|
|
10578
11762
|
if (resolver) {
|
|
10579
|
-
resolver
|
|
10580
|
-
|
|
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
|
+
}
|
|
10581
11776
|
}
|
|
10582
|
-
await this.stateStore.resolveDecision(executionId, decision);
|
|
10583
11777
|
if (!resolver) {
|
|
10584
|
-
const
|
|
10585
|
-
|
|
10586
|
-
|
|
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
|
+
);
|
|
10587
11804
|
}
|
|
10588
11805
|
}
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
* Re-runs the workflow from scratch; the workflow should use checkpoint-replay
|
|
10593
|
-
* to skip already-completed steps and pick up from the awaitHuman point.
|
|
10594
|
-
*/
|
|
10595
|
-
async resumeExecution(executionId) {
|
|
10596
|
-
const state = await this.stateStore.getExecutionState(executionId);
|
|
10597
|
-
if (!state) {
|
|
10598
|
-
throw new Error(`No execution state found for "${executionId}"`);
|
|
10599
|
-
}
|
|
10600
|
-
const workflow2 = this.workflows.get(state.workflow);
|
|
10601
|
-
if (!workflow2) {
|
|
10602
|
-
throw new Error(
|
|
10603
|
-
`Workflow "${state.workflow}" not registered. Cannot resume execution "${executionId}".`
|
|
10604
|
-
);
|
|
10605
|
-
}
|
|
10606
|
-
return this.execute(state.workflow, state.input, {
|
|
10607
|
-
metadata: { ...state.metadata, resumedFrom: executionId, resumeMode: true }
|
|
11806
|
+
let finishResolution;
|
|
11807
|
+
const resolutionFinished = new Promise((resolve2) => {
|
|
11808
|
+
finishResolution = resolve2;
|
|
10608
11809
|
});
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
await this.resumeExecution(executionId);
|
|
10621
|
-
resumed.push(executionId);
|
|
10622
|
-
} catch (err) {
|
|
10623
|
-
this.emit("error", {
|
|
10624
|
-
type: "resume_failed",
|
|
10625
|
-
executionId,
|
|
10626
|
-
error: err instanceof Error ? err.message : String(err)
|
|
10627
|
-
});
|
|
10628
|
-
}
|
|
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;
|
|
10629
11821
|
}
|
|
10630
|
-
return resumed;
|
|
10631
11822
|
}
|
|
10632
11823
|
/**
|
|
10633
11824
|
* Summarize a list of chat messages into a concise summary string.
|
|
@@ -11529,28 +12720,32 @@ var RedisStore = class _RedisStore {
|
|
|
11529
12720
|
async saveExecution(execution) {
|
|
11530
12721
|
const ttl = this.ttlFor("executionHistory");
|
|
11531
12722
|
const setOptions = ttl !== void 0 ? { EX: ttl } : void 0;
|
|
11532
|
-
|
|
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(), {
|
|
11533
12728
|
score: execution.startedAt,
|
|
11534
12729
|
value: execution.executionId
|
|
11535
12730
|
}).exec();
|
|
11536
12731
|
}
|
|
11537
12732
|
async getExecution(executionId) {
|
|
11538
12733
|
const raw = await this.client.get(this.execHistoryKey(executionId));
|
|
11539
|
-
return raw ? JSON.parse(raw) : null;
|
|
12734
|
+
return raw ? normalizeStoredExecution(JSON.parse(raw)) : null;
|
|
11540
12735
|
}
|
|
11541
12736
|
async listExecutions(limit) {
|
|
11542
|
-
|
|
12737
|
+
const executions = await this.listHistoryByZset(
|
|
11543
12738
|
this.execHistorySetKey(),
|
|
11544
12739
|
this.execHistoryZsetKey(),
|
|
11545
12740
|
(id) => this.execHistoryKey(id),
|
|
11546
12741
|
(a, b) => b.startedAt - a.startedAt,
|
|
11547
12742
|
limit
|
|
11548
12743
|
);
|
|
12744
|
+
return executions.map(normalizeStoredExecution);
|
|
11549
12745
|
}
|
|
11550
12746
|
async deleteExecution(executionId) {
|
|
11551
|
-
const
|
|
11552
|
-
|
|
11553
|
-
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);
|
|
11554
12749
|
}
|
|
11555
12750
|
// ── Eval History ────────────────────────────────────────────────────
|
|
11556
12751
|
async saveEvalResult(entry) {
|
|
@@ -11642,14 +12837,15 @@ var RedisStore = class _RedisStore {
|
|
|
11642
12837
|
async getStreamingEvents(executionId) {
|
|
11643
12838
|
const raws = await this.client.lRange(this.streamingEventsKey(executionId), 0, -1);
|
|
11644
12839
|
if (raws.length === 0) return [];
|
|
11645
|
-
|
|
11646
|
-
for (const raw of raws) {
|
|
12840
|
+
return raws.map((raw, index) => {
|
|
11647
12841
|
try {
|
|
11648
|
-
|
|
11649
|
-
} 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
|
+
});
|
|
11650
12847
|
}
|
|
11651
|
-
}
|
|
11652
|
-
return events;
|
|
12848
|
+
});
|
|
11653
12849
|
}
|
|
11654
12850
|
/** Close the Redis connection. */
|
|
11655
12851
|
async close() {
|
|
@@ -11860,6 +13056,8 @@ function cosineSimilarity2(a, b) {
|
|
|
11860
13056
|
}
|
|
11861
13057
|
export {
|
|
11862
13058
|
AXL_EVENT_TYPES,
|
|
13059
|
+
AXL_EVENT_TYPES_V2,
|
|
13060
|
+
AXL_TOOL_LIFECYCLE_TYPES_V2,
|
|
11863
13061
|
AZURE_PROFILE,
|
|
11864
13062
|
AnthropicProvider,
|
|
11865
13063
|
AxlError,
|
|
@@ -11907,7 +13105,9 @@ export {
|
|
|
11907
13105
|
SqliteVectorStore,
|
|
11908
13106
|
ThinkTagScanner,
|
|
11909
13107
|
TimeoutError,
|
|
11910
|
-
|
|
13108
|
+
ToolFailure,
|
|
13109
|
+
ToolModelOutputError,
|
|
13110
|
+
UnsupportedEventSchemaVersionError,
|
|
11911
13111
|
VLLM_PROFILE,
|
|
11912
13112
|
ValidationError,
|
|
11913
13113
|
VerifyError,
|
|
@@ -11922,6 +13122,8 @@ export {
|
|
|
11922
13122
|
extractJson,
|
|
11923
13123
|
extractThinkTags,
|
|
11924
13124
|
findConfig,
|
|
13125
|
+
getEventSchemaVersion,
|
|
13126
|
+
getExecutionEventSchemaVersion,
|
|
11925
13127
|
hasPositiveTokens,
|
|
11926
13128
|
importModule,
|
|
11927
13129
|
isCostBearingLeaf,
|
|
@@ -11930,6 +13132,7 @@ export {
|
|
|
11930
13132
|
isUnpricedLeaf,
|
|
11931
13133
|
isUsableCost,
|
|
11932
13134
|
needsTsxLoader,
|
|
13135
|
+
normalizeStoredExecution,
|
|
11933
13136
|
openaiReasoningEmit,
|
|
11934
13137
|
parsePartialJson,
|
|
11935
13138
|
pickDefault,
|
|
@@ -11938,6 +13141,7 @@ export {
|
|
|
11938
13141
|
reasoningEffortEmit,
|
|
11939
13142
|
reasoningObjectEmit,
|
|
11940
13143
|
redactEvent,
|
|
13144
|
+
redactHistoricalEvent,
|
|
11941
13145
|
registerConditions,
|
|
11942
13146
|
resolveApiKey,
|
|
11943
13147
|
resolvePerModel,
|