@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.cjs
CHANGED
|
@@ -65,6 +65,7 @@ var init_span_manager = __esm({
|
|
|
65
65
|
async withSpanAsync(name, attributes, fn) {
|
|
66
66
|
const otelApi = this.otelApi;
|
|
67
67
|
return this.tracer.startActiveSpan(name, { attributes }, async (otelSpan) => {
|
|
68
|
+
let statusWasSet = false;
|
|
68
69
|
const handle = {
|
|
69
70
|
setAttribute(key, value) {
|
|
70
71
|
otelSpan.setAttribute(key, value);
|
|
@@ -73,7 +74,8 @@ var init_span_manager = __esm({
|
|
|
73
74
|
otelSpan.addEvent(eventName, attrs);
|
|
74
75
|
},
|
|
75
76
|
setStatus(code, message) {
|
|
76
|
-
|
|
77
|
+
statusWasSet = true;
|
|
78
|
+
const statusCode = code === "ok" ? otelApi.SpanStatusCode.OK : code === "error" ? otelApi.SpanStatusCode.ERROR : otelApi.SpanStatusCode.UNSET;
|
|
77
79
|
otelSpan.setStatus({ code: statusCode, message });
|
|
78
80
|
},
|
|
79
81
|
end() {
|
|
@@ -82,10 +84,10 @@ var init_span_manager = __esm({
|
|
|
82
84
|
};
|
|
83
85
|
try {
|
|
84
86
|
const result = await fn(handle);
|
|
85
|
-
handle.setStatus("ok");
|
|
87
|
+
if (!statusWasSet) handle.setStatus("ok");
|
|
86
88
|
return result;
|
|
87
89
|
} catch (err) {
|
|
88
|
-
handle.setStatus("error"
|
|
90
|
+
handle.setStatus("error");
|
|
89
91
|
throw err;
|
|
90
92
|
} finally {
|
|
91
93
|
handle.end();
|
|
@@ -111,6 +113,8 @@ var init_span_manager = __esm({
|
|
|
111
113
|
var index_exports = {};
|
|
112
114
|
__export(index_exports, {
|
|
113
115
|
AXL_EVENT_TYPES: () => AXL_EVENT_TYPES,
|
|
116
|
+
AXL_EVENT_TYPES_V2: () => AXL_EVENT_TYPES_V2,
|
|
117
|
+
AXL_TOOL_LIFECYCLE_TYPES_V2: () => AXL_TOOL_LIFECYCLE_TYPES_V2,
|
|
114
118
|
AZURE_PROFILE: () => AZURE_PROFILE,
|
|
115
119
|
AnthropicProvider: () => AnthropicProvider,
|
|
116
120
|
AxlError: () => AxlError,
|
|
@@ -158,7 +162,9 @@ __export(index_exports, {
|
|
|
158
162
|
SqliteVectorStore: () => SqliteVectorStore,
|
|
159
163
|
ThinkTagScanner: () => ThinkTagScanner,
|
|
160
164
|
TimeoutError: () => TimeoutError,
|
|
161
|
-
|
|
165
|
+
ToolFailure: () => ToolFailure,
|
|
166
|
+
ToolModelOutputError: () => ToolModelOutputError,
|
|
167
|
+
UnsupportedEventSchemaVersionError: () => UnsupportedEventSchemaVersionError,
|
|
162
168
|
VLLM_PROFILE: () => VLLM_PROFILE,
|
|
163
169
|
ValidationError: () => ValidationError,
|
|
164
170
|
VerifyError: () => VerifyError,
|
|
@@ -173,6 +179,8 @@ __export(index_exports, {
|
|
|
173
179
|
extractJson: () => extractJson,
|
|
174
180
|
extractThinkTags: () => extractThinkTags,
|
|
175
181
|
findConfig: () => findConfig,
|
|
182
|
+
getEventSchemaVersion: () => getEventSchemaVersion,
|
|
183
|
+
getExecutionEventSchemaVersion: () => getExecutionEventSchemaVersion,
|
|
176
184
|
hasPositiveTokens: () => hasPositiveTokens,
|
|
177
185
|
importModule: () => importModule,
|
|
178
186
|
isCostBearingLeaf: () => isCostBearingLeaf,
|
|
@@ -181,6 +189,7 @@ __export(index_exports, {
|
|
|
181
189
|
isUnpricedLeaf: () => isUnpricedLeaf,
|
|
182
190
|
isUsableCost: () => isUsableCost,
|
|
183
191
|
needsTsxLoader: () => needsTsxLoader,
|
|
192
|
+
normalizeStoredExecution: () => normalizeStoredExecution,
|
|
184
193
|
openaiReasoningEmit: () => openaiReasoningEmit,
|
|
185
194
|
parsePartialJson: () => parsePartialJson,
|
|
186
195
|
pickDefault: () => pickDefault,
|
|
@@ -189,6 +198,7 @@ __export(index_exports, {
|
|
|
189
198
|
reasoningEffortEmit: () => reasoningEffortEmit,
|
|
190
199
|
reasoningObjectEmit: () => reasoningObjectEmit,
|
|
191
200
|
redactEvent: () => redactEvent,
|
|
201
|
+
redactHistoricalEvent: () => redactHistoricalEvent,
|
|
192
202
|
registerConditions: () => registerConditions,
|
|
193
203
|
resolveApiKey: () => resolveApiKey,
|
|
194
204
|
resolvePerModel: () => resolvePerModel,
|
|
@@ -201,27 +211,257 @@ __export(index_exports, {
|
|
|
201
211
|
});
|
|
202
212
|
module.exports = __toCommonJS(index_exports);
|
|
203
213
|
|
|
214
|
+
// src/tool.ts
|
|
215
|
+
var import_zod = require("zod");
|
|
216
|
+
|
|
217
|
+
// src/errors.ts
|
|
218
|
+
var AxlError = class extends Error {
|
|
219
|
+
code;
|
|
220
|
+
constructor(code, message) {
|
|
221
|
+
super(message);
|
|
222
|
+
this.name = "AxlError";
|
|
223
|
+
this.code = code;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
var EventStreamOverflowError = class extends Error {
|
|
227
|
+
maxQueued;
|
|
228
|
+
eventType;
|
|
229
|
+
constructor(maxQueued, eventType, cause) {
|
|
230
|
+
super(
|
|
231
|
+
`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.`
|
|
232
|
+
);
|
|
233
|
+
this.name = "EventStreamOverflowError";
|
|
234
|
+
this.maxQueued = maxQueued;
|
|
235
|
+
this.eventType = eventType;
|
|
236
|
+
if (cause !== void 0) {
|
|
237
|
+
Object.defineProperty(this, "cause", {
|
|
238
|
+
configurable: true,
|
|
239
|
+
enumerable: false,
|
|
240
|
+
value: cause
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
function isEventStreamOverflowError(error) {
|
|
246
|
+
return error instanceof EventStreamOverflowError;
|
|
247
|
+
}
|
|
248
|
+
function rethrowEventStreamOverflow(error) {
|
|
249
|
+
if (isEventStreamOverflowError(error)) throw error;
|
|
250
|
+
}
|
|
251
|
+
function preserveErrorCause(error, cause) {
|
|
252
|
+
if (cause === void 0 || cause === error || "cause" in error) return error;
|
|
253
|
+
Object.defineProperty(error, "cause", {
|
|
254
|
+
configurable: true,
|
|
255
|
+
enumerable: false,
|
|
256
|
+
value: cause
|
|
257
|
+
});
|
|
258
|
+
return error;
|
|
259
|
+
}
|
|
260
|
+
var ToolFailure = class extends AxlError {
|
|
261
|
+
modelMessage;
|
|
262
|
+
constructor(options) {
|
|
263
|
+
super(options.code ?? "TOOL_FAILURE", options.message);
|
|
264
|
+
this.name = "ToolFailure";
|
|
265
|
+
this.modelMessage = options.modelMessage;
|
|
266
|
+
if (options.cause !== void 0) {
|
|
267
|
+
Object.defineProperty(this, "cause", {
|
|
268
|
+
configurable: true,
|
|
269
|
+
enumerable: false,
|
|
270
|
+
value: options.cause
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
var ToolModelOutputError = class extends AxlError {
|
|
276
|
+
toolName;
|
|
277
|
+
cause;
|
|
278
|
+
constructor(toolName, cause) {
|
|
279
|
+
super("TOOL_MODEL_OUTPUT_ERROR", `Failed to prepare model output for tool "${toolName}"`);
|
|
280
|
+
this.name = "ToolModelOutputError";
|
|
281
|
+
this.toolName = toolName;
|
|
282
|
+
this.cause = cause;
|
|
283
|
+
Object.defineProperty(this, "cause", { enumerable: false });
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
var VerifyError = class extends AxlError {
|
|
287
|
+
lastOutput;
|
|
288
|
+
zodError;
|
|
289
|
+
retries;
|
|
290
|
+
constructor(lastOutput, zodError, retries) {
|
|
291
|
+
super("VERIFY_ERROR", `Schema validation failed after ${retries} retries: ${zodError.message}`);
|
|
292
|
+
this.name = "VerifyError";
|
|
293
|
+
this.lastOutput = lastOutput;
|
|
294
|
+
this.zodError = zodError;
|
|
295
|
+
this.retries = retries;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
var QuorumNotMet = class extends AxlError {
|
|
299
|
+
results;
|
|
300
|
+
constructor(required, actual, results) {
|
|
301
|
+
super("QUORUM_NOT_MET", `Quorum not met: needed ${required} successes, got ${actual}`);
|
|
302
|
+
this.name = "QuorumNotMet";
|
|
303
|
+
this.results = results;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
var NoConsensus = class extends AxlError {
|
|
307
|
+
constructor(reason) {
|
|
308
|
+
super("NO_CONSENSUS", `No consensus: ${reason}`);
|
|
309
|
+
this.name = "NoConsensus";
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var TimeoutError = class extends AxlError {
|
|
313
|
+
constructor(operation, timeoutMs) {
|
|
314
|
+
super("TIMEOUT", `${operation} exceeded timeout of ${timeoutMs}ms`);
|
|
315
|
+
this.name = "TimeoutError";
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
function formatBudgetCost(cost) {
|
|
319
|
+
if (Number.isNaN(cost)) return "$NaN";
|
|
320
|
+
if (cost === Infinity) return "$Infinity";
|
|
321
|
+
if (cost === -Infinity) return "-$Infinity";
|
|
322
|
+
if (cost === 0) return "$0.00";
|
|
323
|
+
const sign = cost < 0 ? "-" : "";
|
|
324
|
+
const abs = Math.abs(cost);
|
|
325
|
+
if (abs < 1e-6) return `${sign}< $0.000001`;
|
|
326
|
+
if (abs < 1e-4) {
|
|
327
|
+
const [mantissa, exponent] = abs.toExponential(2).split("e");
|
|
328
|
+
return `${sign}$${mantissa}e${parseInt(exponent, 10)}`;
|
|
329
|
+
}
|
|
330
|
+
if (abs < 0.01) return `${sign}$${abs.toFixed(6)}`;
|
|
331
|
+
return `${sign}$${abs.toFixed(2)}`;
|
|
332
|
+
}
|
|
333
|
+
var BudgetExceededError = class extends AxlError {
|
|
334
|
+
limit;
|
|
335
|
+
spent;
|
|
336
|
+
policy;
|
|
337
|
+
constructor(limit, spent, policy) {
|
|
338
|
+
super(
|
|
339
|
+
"BUDGET_EXCEEDED",
|
|
340
|
+
`Budget exceeded: spent ${formatBudgetCost(spent)} of ${formatBudgetCost(
|
|
341
|
+
limit
|
|
342
|
+
)} limit (policy: ${policy})`
|
|
343
|
+
);
|
|
344
|
+
this.name = "BudgetExceededError";
|
|
345
|
+
this.limit = limit;
|
|
346
|
+
this.spent = spent;
|
|
347
|
+
this.policy = policy;
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
var MaxTurnsError = class extends AxlError {
|
|
351
|
+
maxTurns;
|
|
352
|
+
constructor(operation, maxTurns) {
|
|
353
|
+
super("MAX_TURNS", `${operation} exceeded maximum of ${maxTurns} turns`);
|
|
354
|
+
this.name = "MaxTurnsError";
|
|
355
|
+
this.maxTurns = maxTurns;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
var GuardrailError = class extends AxlError {
|
|
359
|
+
guardrailType;
|
|
360
|
+
reason;
|
|
361
|
+
constructor(guardrailType, reason) {
|
|
362
|
+
super("GUARDRAIL_BLOCKED", `${guardrailType} guardrail blocked: ${reason}`);
|
|
363
|
+
this.name = "GuardrailError";
|
|
364
|
+
this.guardrailType = guardrailType;
|
|
365
|
+
this.reason = reason;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
var ValidationError = class extends AxlError {
|
|
369
|
+
lastOutput;
|
|
370
|
+
reason;
|
|
371
|
+
retries;
|
|
372
|
+
constructor(lastOutput, reason, retries) {
|
|
373
|
+
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
374
|
+
this.name = "ValidationError";
|
|
375
|
+
this.lastOutput = lastOutput;
|
|
376
|
+
this.reason = reason;
|
|
377
|
+
this.retries = retries;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
204
381
|
// src/tool.ts
|
|
205
382
|
var DEFAULT_MAX_STRING_LENGTH = 1e4;
|
|
206
|
-
|
|
383
|
+
var ToolStringLengthError = class extends Error {
|
|
384
|
+
constructor(path, actual, maximum) {
|
|
385
|
+
const renderedPath = path.reduce(
|
|
386
|
+
(current, part) => typeof part === "number" ? `${current}[${part}]` : current ? `${current}.${part}` : part,
|
|
387
|
+
""
|
|
388
|
+
);
|
|
389
|
+
super(
|
|
390
|
+
`String argument${renderedPath ? ` at "${renderedPath}"` : ""} exceeds maximum length (${actual} > ${maximum})`
|
|
391
|
+
);
|
|
392
|
+
this.path = path;
|
|
393
|
+
this.actual = actual;
|
|
394
|
+
this.maximum = maximum;
|
|
395
|
+
this.name = "ToolStringLengthError";
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
function validateStringLengths(value, maxLen, path = []) {
|
|
207
399
|
if (typeof value === "string") {
|
|
208
400
|
if (value.length > maxLen) {
|
|
209
|
-
throw new
|
|
210
|
-
`String argument${path ? ` at "${path}"` : ""} exceeds maximum length (${value.length} > ${maxLen})`
|
|
211
|
-
);
|
|
401
|
+
throw new ToolStringLengthError(path, value.length, maxLen);
|
|
212
402
|
}
|
|
213
403
|
} else if (Array.isArray(value)) {
|
|
214
404
|
for (let i = 0; i < value.length; i++) {
|
|
215
|
-
validateStringLengths(value[i], maxLen, path
|
|
405
|
+
validateStringLengths(value[i], maxLen, [...path, i]);
|
|
216
406
|
}
|
|
217
407
|
} else if (value !== null && typeof value === "object") {
|
|
218
408
|
for (const [key, val] of Object.entries(value)) {
|
|
219
|
-
validateStringLengths(val, maxLen, path
|
|
409
|
+
validateStringLengths(val, maxLen, [...path, key]);
|
|
220
410
|
}
|
|
221
411
|
}
|
|
222
412
|
}
|
|
223
|
-
function sleep(ms) {
|
|
224
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
413
|
+
function sleep(ms, signal) {
|
|
414
|
+
if (!signal) return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
415
|
+
signal.throwIfAborted();
|
|
416
|
+
return new Promise((resolve2, reject) => {
|
|
417
|
+
const timeout = setTimeout(() => {
|
|
418
|
+
signal.removeEventListener("abort", onAbort);
|
|
419
|
+
resolve2();
|
|
420
|
+
}, ms);
|
|
421
|
+
const onAbort = () => {
|
|
422
|
+
clearTimeout(timeout);
|
|
423
|
+
reject(signal.reason);
|
|
424
|
+
};
|
|
425
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function isAbortError(error) {
|
|
429
|
+
try {
|
|
430
|
+
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
431
|
+
} catch {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function toolArgumentIssues(error) {
|
|
436
|
+
if (error instanceof import_zod.ZodError) {
|
|
437
|
+
return error.issues.map((issue) => ({
|
|
438
|
+
path: issue.path.map((part) => typeof part === "symbol" ? String(part) : part),
|
|
439
|
+
code: issue.code,
|
|
440
|
+
message: issue.message
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
if (error instanceof ToolStringLengthError) {
|
|
444
|
+
return [
|
|
445
|
+
{
|
|
446
|
+
path: error.path,
|
|
447
|
+
code: "too_big",
|
|
448
|
+
message: error.message
|
|
449
|
+
}
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
return [{ path: [], code: "invalid_arguments", message: "Tool arguments are invalid." }];
|
|
453
|
+
}
|
|
454
|
+
var TOOL_INTERNALS = /* @__PURE__ */ new WeakMap();
|
|
455
|
+
function getToolInternals(tool2) {
|
|
456
|
+
const internals = TOOL_INTERNALS.get(tool2);
|
|
457
|
+
if (!internals) throw new Error(`Tool "${tool2.name}" was not created by tool()`);
|
|
458
|
+
return internals;
|
|
459
|
+
}
|
|
460
|
+
function prepareToolInput(tool2, input) {
|
|
461
|
+
return getToolInternals(tool2).prepareInput(input);
|
|
462
|
+
}
|
|
463
|
+
function executePreparedTool(tool2, input, ctx, options) {
|
|
464
|
+
return getToolInternals(tool2).executePrepared(input, ctx, options);
|
|
225
465
|
}
|
|
226
466
|
function getBackoffMs(attempt, strategy) {
|
|
227
467
|
switch (strategy) {
|
|
@@ -240,17 +480,27 @@ function tool(config) {
|
|
|
240
480
|
on: config.retry?.on
|
|
241
481
|
};
|
|
242
482
|
const maxStringLen = config.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH;
|
|
243
|
-
const
|
|
483
|
+
const prepareInput = (input) => {
|
|
244
484
|
const parsed = config.input.parse(input);
|
|
245
485
|
if (maxStringLen > 0) {
|
|
246
486
|
validateStringLengths(parsed, maxStringLen);
|
|
247
487
|
}
|
|
488
|
+
return parsed;
|
|
489
|
+
};
|
|
490
|
+
const executePrepared = async (parsed, ctx, options) => {
|
|
248
491
|
const maxAttempts = retryPolicy.attempts ?? 1;
|
|
249
492
|
let lastError;
|
|
250
493
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
494
|
+
options?.signal?.throwIfAborted();
|
|
495
|
+
options?.onAttempt?.(attempt);
|
|
251
496
|
try {
|
|
252
|
-
|
|
497
|
+
const result = await config.handler(parsed, ctx);
|
|
498
|
+
if (options?.checkAfterHandlerAbort !== false) options?.signal?.throwIfAborted();
|
|
499
|
+
return result;
|
|
253
500
|
} catch (err) {
|
|
501
|
+
if (options?.signal?.aborted) options.signal.throwIfAborted();
|
|
502
|
+
if (isAbortError(err)) throw err;
|
|
503
|
+
rethrowEventStreamOverflow(err);
|
|
254
504
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
255
505
|
if (attempt === maxAttempts) break;
|
|
256
506
|
if (retryPolicy.on && !retryPolicy.on(lastError)) {
|
|
@@ -258,13 +508,14 @@ function tool(config) {
|
|
|
258
508
|
}
|
|
259
509
|
const backoffMs = getBackoffMs(attempt, retryPolicy.backoff ?? "exponential");
|
|
260
510
|
if (backoffMs > 0) {
|
|
261
|
-
await sleep(backoffMs);
|
|
511
|
+
await sleep(backoffMs, options?.signal);
|
|
262
512
|
}
|
|
263
513
|
}
|
|
264
514
|
}
|
|
265
515
|
throw lastError;
|
|
266
516
|
};
|
|
267
|
-
|
|
517
|
+
const execute = async (input, ctx) => executePrepared(prepareInput(input), ctx);
|
|
518
|
+
const instance = {
|
|
268
519
|
name: config.name,
|
|
269
520
|
description: config.description,
|
|
270
521
|
inputSchema: config.input,
|
|
@@ -272,6 +523,7 @@ function tool(config) {
|
|
|
272
523
|
retry: retryPolicy,
|
|
273
524
|
requireApproval: config.requireApproval ?? false,
|
|
274
525
|
hooks: config.hooks,
|
|
526
|
+
...config.toModelOutput !== void 0 ? { toModelOutput: config.toModelOutput } : {},
|
|
275
527
|
async run(ctx, input) {
|
|
276
528
|
const startTime = Date.now();
|
|
277
529
|
try {
|
|
@@ -299,6 +551,11 @@ function tool(config) {
|
|
|
299
551
|
},
|
|
300
552
|
_execute: execute
|
|
301
553
|
};
|
|
554
|
+
TOOL_INTERNALS.set(instance, {
|
|
555
|
+
prepareInput,
|
|
556
|
+
executePrepared
|
|
557
|
+
});
|
|
558
|
+
return instance;
|
|
302
559
|
}
|
|
303
560
|
|
|
304
561
|
// src/providers/types.ts
|
|
@@ -324,123 +581,6 @@ async function resolveApiKey(source) {
|
|
|
324
581
|
return source ?? "";
|
|
325
582
|
}
|
|
326
583
|
|
|
327
|
-
// src/errors.ts
|
|
328
|
-
var AxlError = class extends Error {
|
|
329
|
-
code;
|
|
330
|
-
constructor(code, message) {
|
|
331
|
-
super(message);
|
|
332
|
-
this.name = "AxlError";
|
|
333
|
-
this.code = code;
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
var VerifyError = class extends AxlError {
|
|
337
|
-
lastOutput;
|
|
338
|
-
zodError;
|
|
339
|
-
retries;
|
|
340
|
-
constructor(lastOutput, zodError, retries) {
|
|
341
|
-
super("VERIFY_ERROR", `Schema validation failed after ${retries} retries: ${zodError.message}`);
|
|
342
|
-
this.name = "VerifyError";
|
|
343
|
-
this.lastOutput = lastOutput;
|
|
344
|
-
this.zodError = zodError;
|
|
345
|
-
this.retries = retries;
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
var QuorumNotMet = class extends AxlError {
|
|
349
|
-
results;
|
|
350
|
-
constructor(required, actual, results) {
|
|
351
|
-
super("QUORUM_NOT_MET", `Quorum not met: needed ${required} successes, got ${actual}`);
|
|
352
|
-
this.name = "QuorumNotMet";
|
|
353
|
-
this.results = results;
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
var NoConsensus = class extends AxlError {
|
|
357
|
-
constructor(reason) {
|
|
358
|
-
super("NO_CONSENSUS", `No consensus: ${reason}`);
|
|
359
|
-
this.name = "NoConsensus";
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
var TimeoutError = class extends AxlError {
|
|
363
|
-
constructor(operation, timeoutMs) {
|
|
364
|
-
super("TIMEOUT", `${operation} exceeded timeout of ${timeoutMs}ms`);
|
|
365
|
-
this.name = "TimeoutError";
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
function formatBudgetCost(cost) {
|
|
369
|
-
if (Number.isNaN(cost)) return "$NaN";
|
|
370
|
-
if (cost === Infinity) return "$Infinity";
|
|
371
|
-
if (cost === -Infinity) return "-$Infinity";
|
|
372
|
-
if (cost === 0) return "$0.00";
|
|
373
|
-
const sign = cost < 0 ? "-" : "";
|
|
374
|
-
const abs = Math.abs(cost);
|
|
375
|
-
if (abs < 1e-6) return `${sign}< $0.000001`;
|
|
376
|
-
if (abs < 1e-4) {
|
|
377
|
-
const [mantissa, exponent] = abs.toExponential(2).split("e");
|
|
378
|
-
return `${sign}$${mantissa}e${parseInt(exponent, 10)}`;
|
|
379
|
-
}
|
|
380
|
-
if (abs < 0.01) return `${sign}$${abs.toFixed(6)}`;
|
|
381
|
-
return `${sign}$${abs.toFixed(2)}`;
|
|
382
|
-
}
|
|
383
|
-
var BudgetExceededError = class extends AxlError {
|
|
384
|
-
limit;
|
|
385
|
-
spent;
|
|
386
|
-
policy;
|
|
387
|
-
constructor(limit, spent, policy) {
|
|
388
|
-
super(
|
|
389
|
-
"BUDGET_EXCEEDED",
|
|
390
|
-
`Budget exceeded: spent ${formatBudgetCost(spent)} of ${formatBudgetCost(
|
|
391
|
-
limit
|
|
392
|
-
)} limit (policy: ${policy})`
|
|
393
|
-
);
|
|
394
|
-
this.name = "BudgetExceededError";
|
|
395
|
-
this.limit = limit;
|
|
396
|
-
this.spent = spent;
|
|
397
|
-
this.policy = policy;
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
var MaxTurnsError = class extends AxlError {
|
|
401
|
-
maxTurns;
|
|
402
|
-
constructor(operation, maxTurns) {
|
|
403
|
-
super("MAX_TURNS", `${operation} exceeded maximum of ${maxTurns} turns`);
|
|
404
|
-
this.name = "MaxTurnsError";
|
|
405
|
-
this.maxTurns = maxTurns;
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
var GuardrailError = class extends AxlError {
|
|
409
|
-
guardrailType;
|
|
410
|
-
reason;
|
|
411
|
-
constructor(guardrailType, reason) {
|
|
412
|
-
super("GUARDRAIL_BLOCKED", `${guardrailType} guardrail blocked: ${reason}`);
|
|
413
|
-
this.name = "GuardrailError";
|
|
414
|
-
this.guardrailType = guardrailType;
|
|
415
|
-
this.reason = reason;
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
var ValidationError = class extends AxlError {
|
|
419
|
-
lastOutput;
|
|
420
|
-
reason;
|
|
421
|
-
retries;
|
|
422
|
-
constructor(lastOutput, reason, retries) {
|
|
423
|
-
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
424
|
-
this.name = "ValidationError";
|
|
425
|
-
this.lastOutput = lastOutput;
|
|
426
|
-
this.reason = reason;
|
|
427
|
-
this.retries = retries;
|
|
428
|
-
}
|
|
429
|
-
};
|
|
430
|
-
var ToolDenied = class extends AxlError {
|
|
431
|
-
toolName;
|
|
432
|
-
agentName;
|
|
433
|
-
constructor(toolName, agentName) {
|
|
434
|
-
super(
|
|
435
|
-
"TOOL_DENIED",
|
|
436
|
-
`Agent "${agentName}" attempted to call tool "${toolName}" which is not in its ACL`
|
|
437
|
-
);
|
|
438
|
-
this.name = "ToolDenied";
|
|
439
|
-
this.toolName = toolName;
|
|
440
|
-
this.agentName = agentName;
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
|
|
444
584
|
// src/providers/errors.ts
|
|
445
585
|
var ProviderError = class extends AxlError {
|
|
446
586
|
/** Adapter/profile name, e.g. `'openai'`, `'anthropic'`, `'google'`. */
|
|
@@ -548,7 +688,7 @@ function sleep2(ms, signal) {
|
|
|
548
688
|
);
|
|
549
689
|
});
|
|
550
690
|
}
|
|
551
|
-
function
|
|
691
|
+
function isAbortError2(err, signal) {
|
|
552
692
|
if (signal?.aborted) return true;
|
|
553
693
|
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
554
694
|
}
|
|
@@ -567,7 +707,7 @@ async function fetchWithRetry(input, init, opts) {
|
|
|
567
707
|
try {
|
|
568
708
|
res = await fetch(input, init);
|
|
569
709
|
} catch (err) {
|
|
570
|
-
if (
|
|
710
|
+
if (isAbortError2(err, init?.signal ?? void 0)) throw err;
|
|
571
711
|
if (attempt >= maxRetries) {
|
|
572
712
|
throw buildProviderError({
|
|
573
713
|
provider,
|
|
@@ -2353,6 +2493,15 @@ function sanitizeSchemaForGemini(schema) {
|
|
|
2353
2493
|
}
|
|
2354
2494
|
return out;
|
|
2355
2495
|
}
|
|
2496
|
+
function parseGeminiFunctionResponse(content) {
|
|
2497
|
+
let parsed;
|
|
2498
|
+
try {
|
|
2499
|
+
parsed = JSON.parse(content);
|
|
2500
|
+
} catch {
|
|
2501
|
+
return { result: content };
|
|
2502
|
+
}
|
|
2503
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { result: parsed };
|
|
2504
|
+
}
|
|
2356
2505
|
var GEMINI_PRICING = {
|
|
2357
2506
|
"gemini-2.5-pro": [125e-8, 1e-5],
|
|
2358
2507
|
"gemini-2.5-flash": [3e-7, 25e-7],
|
|
@@ -2688,15 +2837,9 @@ var GeminiProvider = class {
|
|
|
2688
2837
|
}
|
|
2689
2838
|
} else if (msg.role === "tool") {
|
|
2690
2839
|
const functionName = toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
2691
|
-
let responseData;
|
|
2692
|
-
try {
|
|
2693
|
-
responseData = JSON.parse(msg.content);
|
|
2694
|
-
} catch {
|
|
2695
|
-
responseData = { result: msg.content };
|
|
2696
|
-
}
|
|
2697
2840
|
const functionResponse = {
|
|
2698
2841
|
name: functionName,
|
|
2699
|
-
response:
|
|
2842
|
+
response: parseGeminiFunctionResponse(msg.content)
|
|
2700
2843
|
};
|
|
2701
2844
|
if (msg.tool_call_id && geminiNativeIds.has(msg.tool_call_id)) {
|
|
2702
2845
|
functionResponse.id = msg.tool_call_id;
|
|
@@ -3246,7 +3389,7 @@ var defaultRegistry = new ProviderRegistry();
|
|
|
3246
3389
|
// src/context.ts
|
|
3247
3390
|
var import_node_async_hooks = require("async_hooks");
|
|
3248
3391
|
var import_node_crypto = require("crypto");
|
|
3249
|
-
var
|
|
3392
|
+
var import_zod2 = require("zod");
|
|
3250
3393
|
|
|
3251
3394
|
// src/partial-json.ts
|
|
3252
3395
|
var MAX_DEPTH = 256;
|
|
@@ -3865,9 +4008,9 @@ var AXL_EVENT_TYPES = [
|
|
|
3865
4008
|
// Tool invocation lifecycle
|
|
3866
4009
|
"tool_call_start",
|
|
3867
4010
|
"tool_call_end",
|
|
4011
|
+
"tool_call_rejected",
|
|
3868
4012
|
// Single-point tool events
|
|
3869
4013
|
"tool_approval",
|
|
3870
|
-
"tool_denied",
|
|
3871
4014
|
// Delegation
|
|
3872
4015
|
"delegate",
|
|
3873
4016
|
// Handoff (spans two asks; not AskScoped).
|
|
@@ -3898,7 +4041,7 @@ var AXL_EVENT_TYPES = [
|
|
|
3898
4041
|
"memory_remember",
|
|
3899
4042
|
"memory_recall",
|
|
3900
4043
|
"memory_forget",
|
|
3901
|
-
//
|
|
4044
|
+
// Scoped execution checkpoints (`ctx.checkpoint`)
|
|
3902
4045
|
"checkpoint_save",
|
|
3903
4046
|
"checkpoint_replay",
|
|
3904
4047
|
// Human-in-the-loop (`ctx.awaitHuman`)
|
|
@@ -3913,6 +4056,12 @@ var AXL_EVENT_TYPES = [
|
|
|
3913
4056
|
"done",
|
|
3914
4057
|
"error"
|
|
3915
4058
|
];
|
|
4059
|
+
var AXL_EVENT_TYPES_V2 = AXL_EVENT_TYPES;
|
|
4060
|
+
var AXL_TOOL_LIFECYCLE_TYPES_V2 = [
|
|
4061
|
+
"tool_call_start",
|
|
4062
|
+
"tool_call_end",
|
|
4063
|
+
"tool_call_rejected"
|
|
4064
|
+
];
|
|
3916
4065
|
|
|
3917
4066
|
// src/event-stream.ts
|
|
3918
4067
|
var STREAM_EVENTS = new Set(AXL_EVENT_TYPES);
|
|
@@ -3928,8 +4077,8 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3928
4077
|
"agent_call_end",
|
|
3929
4078
|
"tool_call_start",
|
|
3930
4079
|
"tool_call_end",
|
|
4080
|
+
"tool_call_rejected",
|
|
3931
4081
|
"tool_approval",
|
|
3932
|
-
"tool_denied",
|
|
3933
4082
|
"handoff_start",
|
|
3934
4083
|
"handoff_return",
|
|
3935
4084
|
"delegate",
|
|
@@ -3943,18 +4092,6 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3943
4092
|
"await_human",
|
|
3944
4093
|
"await_human_resolved"
|
|
3945
4094
|
]);
|
|
3946
|
-
var EventStreamOverflowError = class extends Error {
|
|
3947
|
-
maxQueued;
|
|
3948
|
-
eventType;
|
|
3949
|
-
constructor(maxQueued, eventType) {
|
|
3950
|
-
super(
|
|
3951
|
-
`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.`
|
|
3952
|
-
);
|
|
3953
|
-
this.name = "EventStreamOverflowError";
|
|
3954
|
-
this.maxQueued = maxQueued;
|
|
3955
|
-
this.eventType = eventType;
|
|
3956
|
-
}
|
|
3957
|
-
};
|
|
3958
4095
|
var DEFAULT_MAX_QUEUED = 1e4;
|
|
3959
4096
|
var DEFAULT_OVERFLOW = "drop-oldest-non-terminal";
|
|
3960
4097
|
var AxlEventBus = class {
|
|
@@ -3965,6 +4102,7 @@ var AxlEventBus = class {
|
|
|
3965
4102
|
maxQueued;
|
|
3966
4103
|
onOverflow;
|
|
3967
4104
|
overflowWarned = false;
|
|
4105
|
+
droppedEvents = 0;
|
|
3968
4106
|
/** Callbacks fired exactly once when `_finish()` is called. Used by
|
|
3969
4107
|
* curated views (e.g., `partialObjects`) that are listener-based and
|
|
3970
4108
|
* need a termination signal independent of any synthetic `done` event. */
|
|
@@ -4010,6 +4148,10 @@ var AxlEventBus = class {
|
|
|
4010
4148
|
* can be 4 KB+ each, hence the more aggressive cleanup here).
|
|
4011
4149
|
*/
|
|
4012
4150
|
stringStreamByAsk = /* @__PURE__ */ new Map();
|
|
4151
|
+
/** Internal fan-out for `stringStream()` subscribers. Kept separate from
|
|
4152
|
+
* the public EventEmitter so strict curated-view overflow is a runtime
|
|
4153
|
+
* signal, not mistaken for (and swallowed as) a buggy user listener. */
|
|
4154
|
+
stringStreamSubscribers = /* @__PURE__ */ new Set();
|
|
4013
4155
|
/**
|
|
4014
4156
|
* Hook fired when an iterator obtained from `[Symbol.asyncIterator]`
|
|
4015
4157
|
* is `await using`-disposed. Set via the second constructor argument.
|
|
@@ -4043,6 +4185,12 @@ var AxlEventBus = class {
|
|
|
4043
4185
|
this.bus.on("error", () => {
|
|
4044
4186
|
});
|
|
4045
4187
|
}
|
|
4188
|
+
/** Machine-readable completeness across this bus's bounded iterable views.
|
|
4189
|
+
* EventEmitter listener delivery is independent and is not described by
|
|
4190
|
+
* this status. */
|
|
4191
|
+
get observationStatus() {
|
|
4192
|
+
return this.droppedEvents === 0 ? { complete: true } : { complete: false, reason: "queue_overflow", droppedEvents: this.droppedEvents };
|
|
4193
|
+
}
|
|
4046
4194
|
unknownEventWarned = false;
|
|
4047
4195
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4048
4196
|
on(event, handler) {
|
|
@@ -4355,7 +4503,7 @@ var AxlEventBus = class {
|
|
|
4355
4503
|
}
|
|
4356
4504
|
return {
|
|
4357
4505
|
[Symbol.asyncIterator]() {
|
|
4358
|
-
const pending =
|
|
4506
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4359
4507
|
let resolveWaiter = null;
|
|
4360
4508
|
let finished = self.finished;
|
|
4361
4509
|
let unsubscribed = false;
|
|
@@ -4369,10 +4517,39 @@ var AxlEventBus = class {
|
|
|
4369
4517
|
if (filterPath !== void 0 && path !== filterPath) return false;
|
|
4370
4518
|
return true;
|
|
4371
4519
|
};
|
|
4520
|
+
const pendingKey = (askId, path) => JSON.stringify([askId, path]);
|
|
4521
|
+
const enqueue = (event) => {
|
|
4522
|
+
const key = pendingKey(event.askId, event.path);
|
|
4523
|
+
const existing = pending.get(key);
|
|
4524
|
+
if (existing) {
|
|
4525
|
+
pending.set(key, {
|
|
4526
|
+
...event,
|
|
4527
|
+
delta: existing.delta + event.delta
|
|
4528
|
+
});
|
|
4529
|
+
wakeWaiter();
|
|
4530
|
+
return;
|
|
4531
|
+
}
|
|
4532
|
+
if (pending.size >= self.maxQueued) {
|
|
4533
|
+
if (self.onOverflow === "throw") {
|
|
4534
|
+
self.droppedEvents++;
|
|
4535
|
+
throw new EventStreamOverflowError(self.maxQueued, "string_delta");
|
|
4536
|
+
}
|
|
4537
|
+
const oldest = pending.keys().next().value;
|
|
4538
|
+
if (oldest !== void 0) {
|
|
4539
|
+
pending.delete(oldest);
|
|
4540
|
+
self.droppedEvents++;
|
|
4541
|
+
}
|
|
4542
|
+
self.warnOverflow(
|
|
4543
|
+
"dropping oldest pending string field (stringStream subscriber is slower than producer)"
|
|
4544
|
+
);
|
|
4545
|
+
}
|
|
4546
|
+
pending.set(key, event);
|
|
4547
|
+
wakeWaiter();
|
|
4548
|
+
};
|
|
4372
4549
|
for (const [askId, paths] of self.stringStreamByAsk) {
|
|
4373
4550
|
for (const [path, acc] of paths) {
|
|
4374
4551
|
if (!matchesFilter(askId, path)) continue;
|
|
4375
|
-
|
|
4552
|
+
enqueue({
|
|
4376
4553
|
askId,
|
|
4377
4554
|
agent: acc.agent,
|
|
4378
4555
|
path,
|
|
@@ -4382,51 +4559,37 @@ var AxlEventBus = class {
|
|
|
4382
4559
|
});
|
|
4383
4560
|
}
|
|
4384
4561
|
}
|
|
4385
|
-
const
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
askId,
|
|
4394
|
-
agent: event.agent,
|
|
4395
|
-
path: event.data.path,
|
|
4396
|
-
delta: event.data.delta,
|
|
4397
|
-
accumulated,
|
|
4398
|
-
attempt: event.attempt
|
|
4399
|
-
});
|
|
4400
|
-
wakeWaiter();
|
|
4401
|
-
};
|
|
4402
|
-
self.bus.on("string_delta", handler);
|
|
4403
|
-
const pipelineHandler = (event) => {
|
|
4404
|
-
if (event.type !== "pipeline") return;
|
|
4405
|
-
if (event.status !== "failed") return;
|
|
4406
|
-
const askId = event.askId ?? "";
|
|
4407
|
-
for (let i = pending.length - 1; i >= 0; i--) {
|
|
4408
|
-
if (pending[i].askId === askId) pending.splice(i, 1);
|
|
4562
|
+
const subscriber = {
|
|
4563
|
+
push(event) {
|
|
4564
|
+
if (matchesFilter(event.askId, event.path)) enqueue(event);
|
|
4565
|
+
},
|
|
4566
|
+
reset(askId) {
|
|
4567
|
+
for (const [key, event] of pending) {
|
|
4568
|
+
if (event.askId === askId) pending.delete(key);
|
|
4569
|
+
}
|
|
4409
4570
|
}
|
|
4410
4571
|
};
|
|
4411
|
-
self.
|
|
4572
|
+
self.stringStreamSubscribers.add(subscriber);
|
|
4412
4573
|
const unsubscribe = () => {
|
|
4413
4574
|
if (unsubscribed) return;
|
|
4414
4575
|
unsubscribed = true;
|
|
4415
|
-
self.
|
|
4416
|
-
self.bus.off("pipeline", pipelineHandler);
|
|
4576
|
+
self.stringStreamSubscribers.delete(subscriber);
|
|
4417
4577
|
unsubscribeFinish();
|
|
4418
4578
|
};
|
|
4419
4579
|
const unsubscribeFinish = self._onFinish(() => {
|
|
4420
4580
|
finished = true;
|
|
4421
|
-
self.
|
|
4422
|
-
self.bus.off("pipeline", pipelineHandler);
|
|
4581
|
+
self.stringStreamSubscribers.delete(subscriber);
|
|
4423
4582
|
wakeWaiter();
|
|
4424
4583
|
});
|
|
4425
4584
|
return {
|
|
4426
4585
|
async next() {
|
|
4427
4586
|
while (true) {
|
|
4428
|
-
if (pending.
|
|
4429
|
-
|
|
4587
|
+
if (pending.size > 0) {
|
|
4588
|
+
const first = pending.entries().next().value;
|
|
4589
|
+
if (first) {
|
|
4590
|
+
pending.delete(first[0]);
|
|
4591
|
+
return { value: first[1], done: false };
|
|
4592
|
+
}
|
|
4430
4593
|
}
|
|
4431
4594
|
if (finished) {
|
|
4432
4595
|
unsubscribe();
|
|
@@ -4484,6 +4647,7 @@ var AxlEventBus = class {
|
|
|
4484
4647
|
*/
|
|
4485
4648
|
_push(event) {
|
|
4486
4649
|
if (this.finished) return;
|
|
4650
|
+
let overflowError;
|
|
4487
4651
|
if (event.type === "partial_object" && event.data?.object !== void 0) {
|
|
4488
4652
|
const askId = event.askId ?? "";
|
|
4489
4653
|
this.latestPartialByAsk.set(askId, {
|
|
@@ -4505,10 +4669,30 @@ var AxlEventBus = class {
|
|
|
4505
4669
|
text: (existing?.text ?? "") + event.data.delta,
|
|
4506
4670
|
attempt: event.attempt
|
|
4507
4671
|
});
|
|
4672
|
+
const accumulated = paths.get(event.data.path).text;
|
|
4673
|
+
for (const subscriber of this.stringStreamSubscribers) {
|
|
4674
|
+
try {
|
|
4675
|
+
subscriber.push({
|
|
4676
|
+
askId,
|
|
4677
|
+
agent: event.agent,
|
|
4678
|
+
path: event.data.path,
|
|
4679
|
+
delta: event.data.delta,
|
|
4680
|
+
accumulated,
|
|
4681
|
+
attempt: event.attempt
|
|
4682
|
+
});
|
|
4683
|
+
} catch (error) {
|
|
4684
|
+
if (isEventStreamOverflowError(error)) {
|
|
4685
|
+
overflowError ??= error;
|
|
4686
|
+
} else {
|
|
4687
|
+
throw error;
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4508
4691
|
} else if (event.type === "pipeline" && event.status === "failed") {
|
|
4509
4692
|
const askId = event.askId ?? "";
|
|
4510
4693
|
this.latestPartialByAsk.delete(askId);
|
|
4511
4694
|
this.stringStreamByAsk.delete(askId);
|
|
4695
|
+
for (const subscriber of this.stringStreamSubscribers) subscriber.reset(askId);
|
|
4512
4696
|
} else if (event.type === "ask_end") {
|
|
4513
4697
|
this.stringStreamByAsk.delete(event.askId ?? "");
|
|
4514
4698
|
}
|
|
@@ -4523,39 +4707,57 @@ var AxlEventBus = class {
|
|
|
4523
4707
|
const waiter = this.waiters.shift();
|
|
4524
4708
|
if (waiter) {
|
|
4525
4709
|
waiter({ value: event, done: false });
|
|
4710
|
+
if (overflowError) throw overflowError;
|
|
4526
4711
|
return;
|
|
4527
4712
|
}
|
|
4528
4713
|
if (this.eventQueue.length >= this.maxQueued && !TERMINAL_TYPES.has(event.type)) {
|
|
4529
|
-
|
|
4714
|
+
try {
|
|
4715
|
+
this.handleOverflow(event);
|
|
4716
|
+
} catch (error) {
|
|
4717
|
+
if (isEventStreamOverflowError(error)) {
|
|
4718
|
+
overflowError ??= error;
|
|
4719
|
+
} else {
|
|
4720
|
+
throw error;
|
|
4721
|
+
}
|
|
4722
|
+
}
|
|
4723
|
+
if (overflowError) throw overflowError;
|
|
4530
4724
|
return;
|
|
4531
4725
|
}
|
|
4532
4726
|
this.eventQueue.push(event);
|
|
4727
|
+
if (overflowError) throw overflowError;
|
|
4533
4728
|
}
|
|
4534
4729
|
/** Apply the overflow policy when the queue is at `maxQueued` and a
|
|
4535
4730
|
* non-terminal event arrives. Terminal events bypass this path entirely
|
|
4536
4731
|
* (checked by the caller). */
|
|
4537
4732
|
handleOverflow(event) {
|
|
4538
4733
|
if (this.onOverflow === "throw") {
|
|
4734
|
+
this.droppedEvents++;
|
|
4539
4735
|
throw new EventStreamOverflowError(this.maxQueued, event.type);
|
|
4540
4736
|
}
|
|
4541
4737
|
let droppedIdx = -1;
|
|
4542
4738
|
for (let i = 0; i < this.eventQueue.length; i++) {
|
|
4543
|
-
|
|
4739
|
+
const candidate = this.eventQueue[i];
|
|
4740
|
+
if (!TERMINAL_TYPES.has(candidate.type)) {
|
|
4544
4741
|
droppedIdx = i;
|
|
4545
4742
|
break;
|
|
4546
4743
|
}
|
|
4547
4744
|
}
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
`[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.`
|
|
4552
|
-
);
|
|
4553
|
-
}
|
|
4745
|
+
this.warnOverflow(
|
|
4746
|
+
droppedIdx >= 0 ? "dropping oldest non-terminal events (consumer is slower than producer)" : "queue contains only protected terminal events; preserving them past the cap"
|
|
4747
|
+
);
|
|
4554
4748
|
if (droppedIdx >= 0) {
|
|
4555
4749
|
this.eventQueue.splice(droppedIdx, 1);
|
|
4750
|
+
this.droppedEvents++;
|
|
4556
4751
|
}
|
|
4557
4752
|
this.eventQueue.push(event);
|
|
4558
4753
|
}
|
|
4754
|
+
warnOverflow(detail) {
|
|
4755
|
+
if (this.overflowWarned) return;
|
|
4756
|
+
this.overflowWarned = true;
|
|
4757
|
+
console.warn(
|
|
4758
|
+
`[axl] AxlEventBus queue exceeded maxQueued=${this.maxQueued}; ${detail}. This warning fires once per bus instance.`
|
|
4759
|
+
);
|
|
4760
|
+
}
|
|
4559
4761
|
/**
|
|
4560
4762
|
* Mark the bus as finished. Wakes any pending iterators with `done: true`
|
|
4561
4763
|
* and drops further `_push` calls. Idempotent.
|
|
@@ -4622,6 +4824,43 @@ var AxlEventBus = class {
|
|
|
4622
4824
|
}
|
|
4623
4825
|
};
|
|
4624
4826
|
|
|
4827
|
+
// src/event-schema.ts
|
|
4828
|
+
var UnsupportedEventSchemaVersionError = class extends Error {
|
|
4829
|
+
constructor(version) {
|
|
4830
|
+
super(`Unsupported Axl event schema version: ${String(version)}`);
|
|
4831
|
+
this.version = version;
|
|
4832
|
+
this.name = "UnsupportedEventSchemaVersionError";
|
|
4833
|
+
}
|
|
4834
|
+
};
|
|
4835
|
+
function getEventSchemaVersion(event) {
|
|
4836
|
+
const version = event.schemaVersion === void 0 ? 1 : event.schemaVersion;
|
|
4837
|
+
if (version !== 1 && version !== 2) throw new UnsupportedEventSchemaVersionError(version);
|
|
4838
|
+
return version;
|
|
4839
|
+
}
|
|
4840
|
+
function getExecutionEventSchemaVersion(execution) {
|
|
4841
|
+
const version = execution.eventSchemaVersion === void 0 ? 1 : execution.eventSchemaVersion;
|
|
4842
|
+
if (version !== 1 && version !== 2) throw new UnsupportedEventSchemaVersionError(version);
|
|
4843
|
+
return version;
|
|
4844
|
+
}
|
|
4845
|
+
function normalizeStoredExecution(execution) {
|
|
4846
|
+
const version = getExecutionEventSchemaVersion(execution);
|
|
4847
|
+
if (!Array.isArray(execution.events)) {
|
|
4848
|
+
throw new TypeError(`Execution ${execution.executionId} has a non-array events field`);
|
|
4849
|
+
}
|
|
4850
|
+
for (const event of execution.events) {
|
|
4851
|
+
const eventVersion = getEventSchemaVersion(event);
|
|
4852
|
+
if (eventVersion !== version) {
|
|
4853
|
+
throw new Error(
|
|
4854
|
+
`Execution ${execution.executionId} declares event schema v${version} but contains a v${eventVersion} event`
|
|
4855
|
+
);
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
if (version === 2) {
|
|
4859
|
+
return { ...execution, eventSchemaVersion: 2 };
|
|
4860
|
+
}
|
|
4861
|
+
return { ...execution, eventSchemaVersion: 1 };
|
|
4862
|
+
}
|
|
4863
|
+
|
|
4625
4864
|
// src/redaction.ts
|
|
4626
4865
|
var REDACTED = "[redacted]";
|
|
4627
4866
|
function walkObjectOneLevel(obj, preserveKeys) {
|
|
@@ -4672,7 +4911,7 @@ function redactMemory(event) {
|
|
|
4672
4911
|
};
|
|
4673
4912
|
}
|
|
4674
4913
|
var passthrough = (e) => e;
|
|
4675
|
-
var
|
|
4914
|
+
var LEGACY_REDACTION_RULES = {
|
|
4676
4915
|
workflow_start: (e) => e.data.input !== void 0 ? { ...e, data: { ...e.data, input: REDACTED } } : e,
|
|
4677
4916
|
workflow_end: (e) => {
|
|
4678
4917
|
const d = e.data;
|
|
@@ -4772,10 +5011,125 @@ var REDACTION_RULES = {
|
|
|
4772
5011
|
done: (e) => ({ ...e, data: { result: REDACTED } }),
|
|
4773
5012
|
error: (e) => ({ ...e, data: { ...e.data, message: REDACTED } })
|
|
4774
5013
|
};
|
|
5014
|
+
function redactLegacyEvent(event) {
|
|
5015
|
+
const rule = LEGACY_REDACTION_RULES[event.type];
|
|
5016
|
+
return rule(event);
|
|
5017
|
+
}
|
|
5018
|
+
function redactV2Outcome(outcome) {
|
|
5019
|
+
switch (outcome.status) {
|
|
5020
|
+
case "succeeded":
|
|
5021
|
+
return { ...outcome, result: REDACTED };
|
|
5022
|
+
case "denied":
|
|
5023
|
+
return outcome.reason === void 0 ? outcome : { ...outcome, reason: REDACTED };
|
|
5024
|
+
case "cancelled":
|
|
5025
|
+
return {
|
|
5026
|
+
...outcome,
|
|
5027
|
+
cancellation: {
|
|
5028
|
+
...outcome.cancellation,
|
|
5029
|
+
..."result" in outcome.cancellation ? { result: REDACTED } : {},
|
|
5030
|
+
...outcome.cancellation.reason !== void 0 ? { reason: REDACTED } : {}
|
|
5031
|
+
}
|
|
5032
|
+
};
|
|
5033
|
+
case "failed": {
|
|
5034
|
+
const failure = outcome.failure;
|
|
5035
|
+
return {
|
|
5036
|
+
...outcome,
|
|
5037
|
+
failure: {
|
|
5038
|
+
...failure,
|
|
5039
|
+
..."result" in failure ? { result: REDACTED } : {},
|
|
5040
|
+
error: {
|
|
5041
|
+
...failure.error,
|
|
5042
|
+
message: REDACTED,
|
|
5043
|
+
...failure.error.cause !== void 0 ? { cause: REDACTED } : {}
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
5046
|
+
};
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
function redactV2Rejection(data) {
|
|
5051
|
+
switch (data.reason) {
|
|
5052
|
+
case "unavailable":
|
|
5053
|
+
return data;
|
|
5054
|
+
case "invalid_json":
|
|
5055
|
+
return { ...data, message: REDACTED };
|
|
5056
|
+
case "invalid_arguments":
|
|
5057
|
+
return {
|
|
5058
|
+
...data,
|
|
5059
|
+
args: REDACTED,
|
|
5060
|
+
issues: data.issues.map((issue) => ({
|
|
5061
|
+
...issue,
|
|
5062
|
+
...issue.message !== void 0 ? { message: REDACTED } : {}
|
|
5063
|
+
}))
|
|
5064
|
+
};
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
var V2_TOOL_REDACTION_RULES = {
|
|
5068
|
+
tool_call_start: (event) => ({
|
|
5069
|
+
...event,
|
|
5070
|
+
data: { ...event.data, args: REDACTED }
|
|
5071
|
+
}),
|
|
5072
|
+
tool_call_end: (event) => ({
|
|
5073
|
+
...event,
|
|
5074
|
+
data: {
|
|
5075
|
+
...event.data,
|
|
5076
|
+
args: REDACTED,
|
|
5077
|
+
outcome: redactV2Outcome(event.data.outcome)
|
|
5078
|
+
}
|
|
5079
|
+
}),
|
|
5080
|
+
tool_call_rejected: (event) => ({
|
|
5081
|
+
...event,
|
|
5082
|
+
data: redactV2Rejection(event.data)
|
|
5083
|
+
})
|
|
5084
|
+
};
|
|
5085
|
+
function reuseLegacyRule(type) {
|
|
5086
|
+
return LEGACY_REDACTION_RULES[type];
|
|
5087
|
+
}
|
|
5088
|
+
var COMMON_V2_REDACTION_RULES = {
|
|
5089
|
+
workflow_start: reuseLegacyRule("workflow_start"),
|
|
5090
|
+
workflow_end: reuseLegacyRule("workflow_end"),
|
|
5091
|
+
ask_start: reuseLegacyRule("ask_start"),
|
|
5092
|
+
ask_end: reuseLegacyRule("ask_end"),
|
|
5093
|
+
agent_call_start: reuseLegacyRule("agent_call_start"),
|
|
5094
|
+
agent_call_end: reuseLegacyRule("agent_call_end"),
|
|
5095
|
+
token: reuseLegacyRule("token"),
|
|
5096
|
+
tool_approval: reuseLegacyRule("tool_approval"),
|
|
5097
|
+
delegate: reuseLegacyRule("delegate"),
|
|
5098
|
+
handoff_start: reuseLegacyRule("handoff_start"),
|
|
5099
|
+
handoff_return: reuseLegacyRule("handoff_return"),
|
|
5100
|
+
pipeline: reuseLegacyRule("pipeline"),
|
|
5101
|
+
partial_object: reuseLegacyRule("partial_object"),
|
|
5102
|
+
string_delta: reuseLegacyRule("string_delta"),
|
|
5103
|
+
verify: reuseLegacyRule("verify"),
|
|
5104
|
+
schema_diagnostic: reuseLegacyRule("schema_diagnostic"),
|
|
5105
|
+
log: reuseLegacyRule("log"),
|
|
5106
|
+
memory_remember: reuseLegacyRule("memory_remember"),
|
|
5107
|
+
memory_recall: reuseLegacyRule("memory_recall"),
|
|
5108
|
+
memory_forget: reuseLegacyRule("memory_forget"),
|
|
5109
|
+
checkpoint_save: reuseLegacyRule("checkpoint_save"),
|
|
5110
|
+
checkpoint_replay: reuseLegacyRule("checkpoint_replay"),
|
|
5111
|
+
await_human: reuseLegacyRule("await_human"),
|
|
5112
|
+
await_human_resolved: reuseLegacyRule("await_human_resolved"),
|
|
5113
|
+
guardrail: reuseLegacyRule("guardrail"),
|
|
5114
|
+
schema_check: reuseLegacyRule("schema_check"),
|
|
5115
|
+
validate: reuseLegacyRule("validate"),
|
|
5116
|
+
done: reuseLegacyRule("done"),
|
|
5117
|
+
error: reuseLegacyRule("error")
|
|
5118
|
+
};
|
|
5119
|
+
var REDACTION_RULES = {
|
|
5120
|
+
...COMMON_V2_REDACTION_RULES,
|
|
5121
|
+
...V2_TOOL_REDACTION_RULES
|
|
5122
|
+
};
|
|
4775
5123
|
function redactEvent(event) {
|
|
4776
5124
|
const rule = REDACTION_RULES[event.type];
|
|
4777
5125
|
return rule(event);
|
|
4778
5126
|
}
|
|
5127
|
+
function redactHistoricalEvent(event) {
|
|
5128
|
+
if (getEventSchemaVersion(event) === 1) {
|
|
5129
|
+
return redactLegacyEvent(event);
|
|
5130
|
+
}
|
|
5131
|
+
return redactEvent(event);
|
|
5132
|
+
}
|
|
4779
5133
|
|
|
4780
5134
|
// src/schema-diagnostics.ts
|
|
4781
5135
|
var DEFAULT_SCHEMA_OVERSIZED_TOKENS = 4e3;
|
|
@@ -4830,110 +5184,771 @@ function detectDroppedRefinements(schema) {
|
|
|
4830
5184
|
if ("right" in def) walk(def.right, path);
|
|
4831
5185
|
if (typeof def.getter === "function") {
|
|
4832
5186
|
try {
|
|
4833
|
-
walk(def.getter(), path);
|
|
5187
|
+
walk(def.getter(), path);
|
|
5188
|
+
} catch {
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
};
|
|
5192
|
+
walk(schema, "");
|
|
5193
|
+
const result = { count, paths: [...paths] };
|
|
5194
|
+
refinementCache.set(schema, result);
|
|
5195
|
+
return result;
|
|
5196
|
+
}
|
|
5197
|
+
var DOCS_URL = "docs/observability.md#schema-diagnostics";
|
|
5198
|
+
var MAX_WARNED_KEYS = 2048;
|
|
5199
|
+
var warnedDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
5200
|
+
function warnSchemaDiagnosticOnce(key, message, silent) {
|
|
5201
|
+
if (silent || process.env.AXL_DIAGNOSTICS_SILENT === "true") return;
|
|
5202
|
+
if (warnedDiagnosticKeys.has(key)) return;
|
|
5203
|
+
if (warnedDiagnosticKeys.size >= MAX_WARNED_KEYS) warnedDiagnosticKeys.clear();
|
|
5204
|
+
warnedDiagnosticKeys.add(key);
|
|
5205
|
+
console.warn(`[axl] ${message} See ${DOCS_URL}.`);
|
|
5206
|
+
}
|
|
5207
|
+
|
|
5208
|
+
// src/config.ts
|
|
5209
|
+
function defineConfig(config) {
|
|
5210
|
+
return config;
|
|
5211
|
+
}
|
|
5212
|
+
function parseDuration(duration) {
|
|
5213
|
+
const match = duration.match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)$/);
|
|
5214
|
+
if (!match) throw new Error(`Invalid duration format: "${duration}"`);
|
|
5215
|
+
const value = parseFloat(match[1]);
|
|
5216
|
+
const unit = match[2];
|
|
5217
|
+
switch (unit) {
|
|
5218
|
+
case "ms":
|
|
5219
|
+
return value;
|
|
5220
|
+
case "s":
|
|
5221
|
+
return value * 1e3;
|
|
5222
|
+
case "m":
|
|
5223
|
+
return value * 6e4;
|
|
5224
|
+
case "h":
|
|
5225
|
+
return value * 36e5;
|
|
5226
|
+
default:
|
|
5227
|
+
throw new Error(`Unknown duration unit: "${unit}"`);
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
function parseCost(cost) {
|
|
5231
|
+
const match = cost.match(/^\$?([\d.]+)$/);
|
|
5232
|
+
if (!match) throw new Error(`Invalid cost format: "${cost}"`);
|
|
5233
|
+
return parseFloat(match[1]);
|
|
5234
|
+
}
|
|
5235
|
+
function resolveConfig(config) {
|
|
5236
|
+
const resolved = { ...config };
|
|
5237
|
+
if (process.env.AXL_DEFAULT_PROVIDER) {
|
|
5238
|
+
resolved.defaultProvider = process.env.AXL_DEFAULT_PROVIDER;
|
|
5239
|
+
}
|
|
5240
|
+
if (process.env.AXL_STATE_STORE) {
|
|
5241
|
+
const envStore = process.env.AXL_STATE_STORE;
|
|
5242
|
+
if (envStore === "memory" || envStore === "sqlite") {
|
|
5243
|
+
resolved.state = {
|
|
5244
|
+
...resolved.state,
|
|
5245
|
+
store: envStore
|
|
5246
|
+
};
|
|
5247
|
+
}
|
|
5248
|
+
}
|
|
5249
|
+
if (process.env.AXL_TRACE_ENABLED !== void 0) {
|
|
5250
|
+
resolved.trace = { ...resolved.trace, enabled: process.env.AXL_TRACE_ENABLED === "true" };
|
|
5251
|
+
}
|
|
5252
|
+
if (process.env.AXL_TRACE_LEVEL) {
|
|
5253
|
+
resolved.trace = {
|
|
5254
|
+
...resolved.trace,
|
|
5255
|
+
level: process.env.AXL_TRACE_LEVEL
|
|
5256
|
+
};
|
|
5257
|
+
}
|
|
5258
|
+
if (process.env.OPENAI_API_KEY) {
|
|
5259
|
+
if (!resolved.providers) resolved.providers = {};
|
|
5260
|
+
resolved.providers.openai = {
|
|
5261
|
+
...resolved.providers.openai ?? {},
|
|
5262
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
5265
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
5266
|
+
if (!resolved.providers) resolved.providers = {};
|
|
5267
|
+
resolved.providers.anthropic = {
|
|
5268
|
+
...resolved.providers.anthropic ?? {},
|
|
5269
|
+
apiKey: process.env.ANTHROPIC_API_KEY
|
|
5270
|
+
};
|
|
5271
|
+
}
|
|
5272
|
+
const googleKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
|
|
5273
|
+
if (googleKey) {
|
|
5274
|
+
if (!resolved.providers) resolved.providers = {};
|
|
5275
|
+
resolved.providers.google = {
|
|
5276
|
+
...resolved.providers.google ?? {},
|
|
5277
|
+
apiKey: googleKey
|
|
5278
|
+
};
|
|
5279
|
+
}
|
|
5280
|
+
return resolved;
|
|
5281
|
+
}
|
|
5282
|
+
|
|
5283
|
+
// src/tool-model-output.ts
|
|
5284
|
+
var import_node_util = require("util");
|
|
5285
|
+
function invalid(path, reason) {
|
|
5286
|
+
return new Error(`Invalid tool model output at ${path}: ${reason}`);
|
|
5287
|
+
}
|
|
5288
|
+
function propertyPath(parent, key) {
|
|
5289
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) return `${parent}.${key}`;
|
|
5290
|
+
let escaped = "";
|
|
5291
|
+
for (const character of key) {
|
|
5292
|
+
const code = character.charCodeAt(0);
|
|
5293
|
+
if (character === "\\" || character === '"') escaped += `\\${character}`;
|
|
5294
|
+
else if (code <= 31 || code === 8232 || code === 8233) {
|
|
5295
|
+
escaped += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
5296
|
+
} else escaped += character;
|
|
5297
|
+
}
|
|
5298
|
+
return `${parent}["${escaped}"]`;
|
|
5299
|
+
}
|
|
5300
|
+
function normalize(value, path, stack) {
|
|
5301
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
5302
|
+
if (typeof value === "number") {
|
|
5303
|
+
if (!Number.isFinite(value)) throw invalid(path, "number must be finite");
|
|
5304
|
+
return value;
|
|
5305
|
+
}
|
|
5306
|
+
if (value === void 0) throw invalid(path, "undefined is only allowed on object properties");
|
|
5307
|
+
if (typeof value === "bigint" || typeof value === "function" || typeof value === "symbol") {
|
|
5308
|
+
throw invalid(path, `unsupported ${typeof value}`);
|
|
5309
|
+
}
|
|
5310
|
+
if (typeof value !== "object") throw invalid(path, `unsupported ${typeof value}`);
|
|
5311
|
+
let prototype;
|
|
5312
|
+
let descriptors;
|
|
5313
|
+
try {
|
|
5314
|
+
prototype = Object.getPrototypeOf(value);
|
|
5315
|
+
descriptors = Object.getOwnPropertyDescriptors(value);
|
|
5316
|
+
Object.setPrototypeOf(descriptors, null);
|
|
5317
|
+
} catch (cause) {
|
|
5318
|
+
throw invalid(
|
|
5319
|
+
path,
|
|
5320
|
+
`object inspection failed (${cause instanceof Error ? cause.name : "error"})`
|
|
5321
|
+
);
|
|
5322
|
+
}
|
|
5323
|
+
if (stack.has(value)) throw invalid(path, "cyclic reference");
|
|
5324
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
5325
|
+
const descriptor = descriptors[key];
|
|
5326
|
+
if (descriptor && !("value" in descriptor)) {
|
|
5327
|
+
throw invalid(
|
|
5328
|
+
typeof key === "string" ? propertyPath(path, key) : `${path}[symbol]`,
|
|
5329
|
+
"accessor properties are not supported"
|
|
5330
|
+
);
|
|
5331
|
+
}
|
|
5332
|
+
if (typeof key === "symbol") {
|
|
5333
|
+
throw invalid(`${path}[symbol]`, "symbol keys are not supported");
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
if (typeof descriptors.toJSON?.value === "function") {
|
|
5337
|
+
throw invalid(propertyPath(path, "toJSON"), "custom toJSON is not supported");
|
|
5338
|
+
}
|
|
5339
|
+
const array = Array.isArray(value);
|
|
5340
|
+
if (array && prototype !== Array.prototype && prototype !== null) {
|
|
5341
|
+
throw invalid(path, "array subclasses are not supported");
|
|
5342
|
+
}
|
|
5343
|
+
const inheritedToJSON = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "toJSON") : void 0;
|
|
5344
|
+
if (typeof inheritedToJSON?.value === "function") {
|
|
5345
|
+
throw invalid(propertyPath(path, "toJSON"), "inherited custom toJSON is not supported");
|
|
5346
|
+
}
|
|
5347
|
+
const inheritedThen = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "then") : void 0;
|
|
5348
|
+
if (typeof descriptors.then?.value === "function" || inheritedThen !== void 0 && (!("value" in inheritedThen) || typeof inheritedThen.value === "function")) {
|
|
5349
|
+
throw invalid(path, "thenable output is not supported");
|
|
5350
|
+
}
|
|
5351
|
+
stack.add(value);
|
|
5352
|
+
try {
|
|
5353
|
+
if (array) {
|
|
5354
|
+
const normalized2 = [];
|
|
5355
|
+
const length = descriptors.length?.value;
|
|
5356
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
5357
|
+
throw invalid(propertyPath(path, "length"), "array length is invalid");
|
|
5358
|
+
}
|
|
5359
|
+
for (let index = 0; index < length; index++) {
|
|
5360
|
+
const descriptor = descriptors[String(index)];
|
|
5361
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
5362
|
+
throw invalid(`${path}[${index}]`, "arrays must be dense data properties");
|
|
5363
|
+
}
|
|
5364
|
+
if (descriptor.value === void 0) {
|
|
5365
|
+
throw invalid(`${path}[${index}]`, "array values cannot be undefined");
|
|
5366
|
+
}
|
|
5367
|
+
normalized2.push(normalize(descriptor.value, `${path}[${index}]`, stack));
|
|
5368
|
+
}
|
|
5369
|
+
for (const key of Object.keys(descriptors)) {
|
|
5370
|
+
const descriptor = descriptors[key];
|
|
5371
|
+
if (!descriptor?.enumerable) continue;
|
|
5372
|
+
const index = Number(key);
|
|
5373
|
+
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== key) {
|
|
5374
|
+
throw invalid(propertyPath(path, key), "non-index array properties are not supported");
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
Object.setPrototypeOf(normalized2, null);
|
|
5378
|
+
return normalized2;
|
|
5379
|
+
}
|
|
5380
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
5381
|
+
throw invalid(path, "only plain objects and null-prototype records are supported");
|
|
5382
|
+
}
|
|
5383
|
+
const normalized = /* @__PURE__ */ Object.create(null);
|
|
5384
|
+
for (const key of Object.keys(descriptors)) {
|
|
5385
|
+
const descriptor = descriptors[key];
|
|
5386
|
+
if (!descriptor?.enumerable) continue;
|
|
5387
|
+
if (!("value" in descriptor)) {
|
|
5388
|
+
throw invalid(propertyPath(path, key), "accessor properties are not supported");
|
|
5389
|
+
}
|
|
5390
|
+
if (descriptor.value === void 0) continue;
|
|
5391
|
+
normalized[key] = normalize(descriptor.value, propertyPath(path, key), stack);
|
|
5392
|
+
}
|
|
5393
|
+
return normalized;
|
|
5394
|
+
} finally {
|
|
5395
|
+
stack.delete(value);
|
|
5396
|
+
}
|
|
5397
|
+
}
|
|
5398
|
+
function serializeToolModelOutput(toolName, output) {
|
|
5399
|
+
try {
|
|
5400
|
+
if (import_node_util.types.isPromise(output)) {
|
|
5401
|
+
void Promise.prototype.then.call(output, void 0, () => void 0);
|
|
5402
|
+
}
|
|
5403
|
+
if (typeof output === "string") return output;
|
|
5404
|
+
return JSON.stringify(normalize(output, "$", /* @__PURE__ */ new WeakSet()));
|
|
5405
|
+
} catch (cause) {
|
|
5406
|
+
throw new ToolModelOutputError(toolName, cause);
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
|
|
5410
|
+
// src/tool-invocation.ts
|
|
5411
|
+
var INVALID_JSON_MODEL_MESSAGE = "Error: Invalid JSON in tool arguments. Please provide valid JSON.";
|
|
5412
|
+
var INVALID_ARGUMENTS_MODEL_MESSAGE = "Error: Tool arguments are invalid. Correct the arguments and try again.";
|
|
5413
|
+
var SENSITIVE_SUCCESS_MESSAGE = "[REDACTED - sensitive tool output]";
|
|
5414
|
+
var SENSITIVE_FAILURE_MESSAGE = "[REDACTED - sensitive tool failure]";
|
|
5415
|
+
var MAX_MCP_MODEL_ERROR_LENGTH = 2e3;
|
|
5416
|
+
function cloneToolArguments(value) {
|
|
5417
|
+
try {
|
|
5418
|
+
return structuredClone(value);
|
|
5419
|
+
} catch {
|
|
5420
|
+
throw new Error("Prepared tool arguments must be structured-cloneable");
|
|
5421
|
+
}
|
|
5422
|
+
}
|
|
5423
|
+
function parseToolInvocation(options) {
|
|
5424
|
+
const { toolCall, configuredTool, override, mcpCall, mcpTraceName, availableTools } = options;
|
|
5425
|
+
const requestedTool = toolCall.function.name;
|
|
5426
|
+
if (!override && !configuredTool && !mcpCall) {
|
|
5427
|
+
return {
|
|
5428
|
+
kind: "rejected",
|
|
5429
|
+
requestedTool,
|
|
5430
|
+
toolName: requestedTool,
|
|
5431
|
+
callId: toolCall.id,
|
|
5432
|
+
data: { reason: "unavailable", requestedTool, availableTools },
|
|
5433
|
+
modelMessage: `Tool "${requestedTool}" is not available. Available tools: ${availableTools.length > 0 ? availableTools.join(", ") : "none"}`
|
|
5434
|
+
};
|
|
5435
|
+
}
|
|
5436
|
+
let parsedArguments;
|
|
5437
|
+
try {
|
|
5438
|
+
parsedArguments = JSON.parse(toolCall.function.arguments);
|
|
5439
|
+
} catch {
|
|
5440
|
+
return {
|
|
5441
|
+
kind: "rejected",
|
|
5442
|
+
requestedTool,
|
|
5443
|
+
toolName: mcpTraceName ?? requestedTool,
|
|
5444
|
+
callId: toolCall.id,
|
|
5445
|
+
data: {
|
|
5446
|
+
reason: "invalid_json",
|
|
5447
|
+
requestedTool,
|
|
5448
|
+
message: "Tool arguments were not valid JSON."
|
|
5449
|
+
},
|
|
5450
|
+
modelMessage: INVALID_JSON_MODEL_MESSAGE
|
|
5451
|
+
};
|
|
5452
|
+
}
|
|
5453
|
+
if (typeof parsedArguments !== "object" || parsedArguments === null || Array.isArray(parsedArguments)) {
|
|
5454
|
+
return {
|
|
5455
|
+
kind: "rejected",
|
|
5456
|
+
requestedTool,
|
|
5457
|
+
toolName: mcpTraceName ?? requestedTool,
|
|
5458
|
+
callId: toolCall.id,
|
|
5459
|
+
data: {
|
|
5460
|
+
reason: "invalid_json",
|
|
5461
|
+
requestedTool,
|
|
5462
|
+
message: "Tool arguments must decode to a JSON object."
|
|
5463
|
+
},
|
|
5464
|
+
modelMessage: INVALID_JSON_MODEL_MESSAGE
|
|
5465
|
+
};
|
|
5466
|
+
}
|
|
5467
|
+
const source = override ? { kind: "override", execute: override } : configuredTool ? { kind: "local", tool: configuredTool } : { kind: "mcp", call: mcpCall };
|
|
5468
|
+
let preparedArguments = parsedArguments;
|
|
5469
|
+
if (source.kind === "local") {
|
|
5470
|
+
try {
|
|
5471
|
+
preparedArguments = prepareToolInput(source.tool, parsedArguments);
|
|
5472
|
+
} catch (error) {
|
|
5473
|
+
return {
|
|
5474
|
+
kind: "rejected",
|
|
5475
|
+
requestedTool,
|
|
5476
|
+
toolName: requestedTool,
|
|
5477
|
+
callId: toolCall.id,
|
|
5478
|
+
data: {
|
|
5479
|
+
reason: "invalid_arguments",
|
|
5480
|
+
requestedTool,
|
|
5481
|
+
args: parsedArguments,
|
|
5482
|
+
issues: toolArgumentIssues(error)
|
|
5483
|
+
},
|
|
5484
|
+
modelMessage: INVALID_ARGUMENTS_MODEL_MESSAGE
|
|
5485
|
+
};
|
|
5486
|
+
}
|
|
5487
|
+
}
|
|
5488
|
+
try {
|
|
5489
|
+
preparedArguments = cloneToolArguments(preparedArguments);
|
|
5490
|
+
} catch (error) {
|
|
5491
|
+
return {
|
|
5492
|
+
kind: "rejected",
|
|
5493
|
+
requestedTool,
|
|
5494
|
+
toolName: source.kind === "mcp" ? mcpTraceName ?? requestedTool : requestedTool,
|
|
5495
|
+
callId: toolCall.id,
|
|
5496
|
+
data: {
|
|
5497
|
+
reason: "invalid_arguments",
|
|
5498
|
+
requestedTool,
|
|
5499
|
+
args: parsedArguments,
|
|
5500
|
+
issues: toolArgumentIssues(error)
|
|
5501
|
+
},
|
|
5502
|
+
modelMessage: INVALID_ARGUMENTS_MODEL_MESSAGE
|
|
5503
|
+
};
|
|
5504
|
+
}
|
|
5505
|
+
return {
|
|
5506
|
+
requestedTool,
|
|
5507
|
+
toolName: source.kind === "mcp" ? mcpTraceName ?? requestedTool : requestedTool,
|
|
5508
|
+
callId: toolCall.id,
|
|
5509
|
+
args: preparedArguments,
|
|
5510
|
+
configuredTool,
|
|
5511
|
+
source
|
|
5512
|
+
};
|
|
5513
|
+
}
|
|
5514
|
+
function isAbortError3(error) {
|
|
5515
|
+
try {
|
|
5516
|
+
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
5517
|
+
} catch {
|
|
5518
|
+
return false;
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
function normalizeAbortError(reason) {
|
|
5522
|
+
if (isAbortError3(reason)) return reason;
|
|
5523
|
+
const error = new Error("The operation was aborted");
|
|
5524
|
+
error.name = "AbortError";
|
|
5525
|
+
if (reason !== void 0) {
|
|
5526
|
+
Object.defineProperty(error, "cause", { value: reason, enumerable: false });
|
|
5527
|
+
}
|
|
5528
|
+
return error;
|
|
5529
|
+
}
|
|
5530
|
+
function cancellationError(signal, error) {
|
|
5531
|
+
if (signal?.aborted) return normalizeAbortError(signal.reason);
|
|
5532
|
+
return isAbortError3(error) ? normalizeAbortError(error) : void 0;
|
|
5533
|
+
}
|
|
5534
|
+
function eventError(error) {
|
|
5535
|
+
let isError = false;
|
|
5536
|
+
try {
|
|
5537
|
+
isError = error instanceof Error;
|
|
5538
|
+
} catch {
|
|
5539
|
+
}
|
|
5540
|
+
if (isError) {
|
|
5541
|
+
const read = (key) => {
|
|
5542
|
+
try {
|
|
5543
|
+
return Reflect.get(error, key);
|
|
4834
5544
|
} catch {
|
|
5545
|
+
return void 0;
|
|
4835
5546
|
}
|
|
4836
|
-
}
|
|
5547
|
+
};
|
|
5548
|
+
const name = read("name");
|
|
5549
|
+
const message = read("message");
|
|
5550
|
+
const code = read("code");
|
|
5551
|
+
const cause = read("cause");
|
|
5552
|
+
return {
|
|
5553
|
+
name: typeof name === "string" ? name : "Error",
|
|
5554
|
+
message: typeof message === "string" ? message : "Unknown error",
|
|
5555
|
+
...typeof code === "string" ? { code } : {},
|
|
5556
|
+
...cause !== void 0 ? { cause } : {}
|
|
5557
|
+
};
|
|
5558
|
+
}
|
|
5559
|
+
try {
|
|
5560
|
+
return { name: "Error", message: String(error) };
|
|
5561
|
+
} catch {
|
|
5562
|
+
return { name: "Error", message: "Unknown thrown value" };
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
function isToolFailure(error) {
|
|
5566
|
+
try {
|
|
5567
|
+
return error instanceof ToolFailure;
|
|
5568
|
+
} catch {
|
|
5569
|
+
return false;
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
function cancellation(phase, error, result) {
|
|
5573
|
+
let reason;
|
|
5574
|
+
try {
|
|
5575
|
+
reason = error instanceof Error && typeof error.message === "string" ? error.message : void 0;
|
|
5576
|
+
} catch {
|
|
5577
|
+
}
|
|
5578
|
+
const cancellation2 = phase === "after_handler" || phase === "after_hook" || phase === "projection" || phase === "serialization" ? { phase, result, ...reason ? { reason } : {} } : { phase, ...reason ? { reason } : {} };
|
|
5579
|
+
return {
|
|
5580
|
+
kind: "cancelled",
|
|
5581
|
+
cancellation: cancellation2,
|
|
5582
|
+
error: normalizeAbortError(error)
|
|
4837
5583
|
};
|
|
4838
|
-
walk(schema, "");
|
|
4839
|
-
const result = { count, paths: [...paths] };
|
|
4840
|
-
refinementCache.set(schema, result);
|
|
4841
|
-
return result;
|
|
4842
5584
|
}
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
5585
|
+
function failed(phase, error, options = {}) {
|
|
5586
|
+
let modelMessage;
|
|
5587
|
+
if (isToolFailure(error)) {
|
|
5588
|
+
try {
|
|
5589
|
+
modelMessage = typeof error.modelMessage === "string" ? error.modelMessage : void 0;
|
|
5590
|
+
} catch {
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5593
|
+
const toolFailure = modelMessage !== void 0;
|
|
5594
|
+
const common = {
|
|
5595
|
+
phase,
|
|
5596
|
+
kind: toolFailure ? "tool_failure" : "unexpected",
|
|
5597
|
+
disposition: toolFailure ? "continue" : "abort",
|
|
5598
|
+
error: eventError(error)
|
|
5599
|
+
};
|
|
5600
|
+
const failure = phase === "handler" ? { ...common, attempts: options.attempts ?? 1 } : phase === "after_hook" ? { ...common, result: options.result } : common;
|
|
5601
|
+
return {
|
|
5602
|
+
kind: "failed",
|
|
5603
|
+
failure,
|
|
5604
|
+
error,
|
|
5605
|
+
...modelMessage !== void 0 ? { modelMessage } : {}
|
|
5606
|
+
};
|
|
4852
5607
|
}
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
function defineConfig(config) {
|
|
4856
|
-
return config;
|
|
5608
|
+
function mcpContent(result) {
|
|
5609
|
+
return result.content.map((content) => content.type === "text" ? content.text : `[${content.type}]`).join("\n");
|
|
4857
5610
|
}
|
|
4858
|
-
function
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
switch (unit) {
|
|
4864
|
-
case "ms":
|
|
4865
|
-
return value;
|
|
4866
|
-
case "s":
|
|
4867
|
-
return value * 1e3;
|
|
4868
|
-
case "m":
|
|
4869
|
-
return value * 6e4;
|
|
4870
|
-
case "h":
|
|
4871
|
-
return value * 36e5;
|
|
4872
|
-
default:
|
|
4873
|
-
throw new Error(`Unknown duration unit: "${unit}"`);
|
|
5611
|
+
function safeMcpModelError(content) {
|
|
5612
|
+
let normalized = "";
|
|
5613
|
+
for (const character of content) {
|
|
5614
|
+
const code = character.charCodeAt(0);
|
|
5615
|
+
normalized += code <= 31 && code !== 9 && code !== 10 && code !== 13 || code === 127 ? " " : character;
|
|
4874
5616
|
}
|
|
5617
|
+
return `MCP tool error: ${normalized.slice(0, MAX_MCP_MODEL_ERROR_LENGTH)}`;
|
|
4875
5618
|
}
|
|
4876
|
-
function
|
|
4877
|
-
const
|
|
4878
|
-
|
|
4879
|
-
|
|
5619
|
+
async function executeAcceptedTool(options) {
|
|
5620
|
+
const { invocation, context, signal, requestApproval, createChildContext } = options;
|
|
5621
|
+
let effectiveArgs = cloneToolArguments(invocation.args);
|
|
5622
|
+
if (invocation.source.kind === "local" && invocation.source.tool.requireApproval) {
|
|
5623
|
+
try {
|
|
5624
|
+
signal?.throwIfAborted();
|
|
5625
|
+
const approval = await requestApproval();
|
|
5626
|
+
signal?.throwIfAborted();
|
|
5627
|
+
if (!approval.approved) return { kind: "denied", reason: approval.reason };
|
|
5628
|
+
} catch (error) {
|
|
5629
|
+
rethrowEventStreamOverflow(error);
|
|
5630
|
+
const abort = cancellationError(signal, error);
|
|
5631
|
+
if (abort !== void 0) return cancellation("approval", abort);
|
|
5632
|
+
return {
|
|
5633
|
+
kind: "failed",
|
|
5634
|
+
failure: {
|
|
5635
|
+
phase: "approval",
|
|
5636
|
+
kind: "infrastructure",
|
|
5637
|
+
disposition: "abort",
|
|
5638
|
+
error: eventError(error)
|
|
5639
|
+
},
|
|
5640
|
+
error
|
|
5641
|
+
};
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
if (invocation.source.kind === "local" && invocation.source.tool.hooks?.before) {
|
|
5645
|
+
try {
|
|
5646
|
+
signal?.throwIfAborted();
|
|
5647
|
+
effectiveArgs = await invocation.source.tool.hooks.before(effectiveArgs, context);
|
|
5648
|
+
signal?.throwIfAborted();
|
|
5649
|
+
} catch (error) {
|
|
5650
|
+
rethrowEventStreamOverflow(error);
|
|
5651
|
+
const abort = cancellationError(signal, error);
|
|
5652
|
+
if (abort !== void 0) return cancellation("before_hook", abort);
|
|
5653
|
+
return failed("before_hook", error);
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
let result;
|
|
5657
|
+
let attempts = 1;
|
|
5658
|
+
try {
|
|
5659
|
+
signal?.throwIfAborted();
|
|
5660
|
+
switch (invocation.source.kind) {
|
|
5661
|
+
case "override": {
|
|
5662
|
+
const executeOverride = invocation.source.execute;
|
|
5663
|
+
result = await executeOverride(effectiveArgs);
|
|
5664
|
+
break;
|
|
5665
|
+
}
|
|
5666
|
+
case "local":
|
|
5667
|
+
result = await executePreparedTool(
|
|
5668
|
+
invocation.source.tool,
|
|
5669
|
+
effectiveArgs,
|
|
5670
|
+
createChildContext(),
|
|
5671
|
+
{
|
|
5672
|
+
signal,
|
|
5673
|
+
onAttempt: (attempt) => attempts = attempt,
|
|
5674
|
+
checkAfterHandlerAbort: false
|
|
5675
|
+
}
|
|
5676
|
+
);
|
|
5677
|
+
break;
|
|
5678
|
+
case "mcp": {
|
|
5679
|
+
const mcpResult = await invocation.source.call(effectiveArgs);
|
|
5680
|
+
if (signal?.aborted) return cancellation("after_handler", signal.reason, mcpResult);
|
|
5681
|
+
if (mcpResult.isError) {
|
|
5682
|
+
const content = mcpContent(mcpResult);
|
|
5683
|
+
const error = new Error(content || "MCP tool returned an error");
|
|
5684
|
+
return {
|
|
5685
|
+
kind: "failed",
|
|
5686
|
+
failure: {
|
|
5687
|
+
phase: "handler",
|
|
5688
|
+
kind: "mcp_error",
|
|
5689
|
+
disposition: "continue",
|
|
5690
|
+
error: { name: "McpToolError", message: error.message, code: "MCP_TOOL_ERROR" },
|
|
5691
|
+
attempts: 1
|
|
5692
|
+
},
|
|
5693
|
+
error,
|
|
5694
|
+
modelMessage: safeMcpModelError(content)
|
|
5695
|
+
};
|
|
5696
|
+
}
|
|
5697
|
+
result = mcpResult;
|
|
5698
|
+
break;
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
} catch (error) {
|
|
5702
|
+
rethrowEventStreamOverflow(error);
|
|
5703
|
+
const abort = cancellationError(signal, error);
|
|
5704
|
+
if (abort !== void 0) return cancellation("handler", abort);
|
|
5705
|
+
return failed("handler", error, { attempts });
|
|
5706
|
+
}
|
|
5707
|
+
if (signal?.aborted) return cancellation("after_handler", signal.reason, result);
|
|
5708
|
+
if (invocation.source.kind === "local" && invocation.source.tool.hooks?.after) {
|
|
5709
|
+
const rawResult = result;
|
|
5710
|
+
try {
|
|
5711
|
+
result = await invocation.source.tool.hooks.after(result, context);
|
|
5712
|
+
signal?.throwIfAborted();
|
|
5713
|
+
} catch (error) {
|
|
5714
|
+
rethrowEventStreamOverflow(error);
|
|
5715
|
+
const abort = cancellationError(signal, error);
|
|
5716
|
+
if (abort !== void 0) return cancellation("after_hook", abort, rawResult);
|
|
5717
|
+
return failed("after_hook", error, { result: rawResult });
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
return { kind: "succeeded", result };
|
|
4880
5721
|
}
|
|
4881
|
-
function
|
|
4882
|
-
const
|
|
4883
|
-
if (
|
|
4884
|
-
|
|
5722
|
+
function prepareToolMessage(invocation, outcome) {
|
|
5723
|
+
const sensitive = invocation.configuredTool?.sensitive === true;
|
|
5724
|
+
if (outcome.kind === "denied") {
|
|
5725
|
+
return {
|
|
5726
|
+
ok: true,
|
|
5727
|
+
content: JSON.stringify({ error: "Tool request was denied by human approval." })
|
|
5728
|
+
};
|
|
4885
5729
|
}
|
|
4886
|
-
if (
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
5730
|
+
if (outcome.kind === "failed") {
|
|
5731
|
+
if (outcome.failure.disposition !== "continue" || outcome.modelMessage === void 0) {
|
|
5732
|
+
throw new Error("Aborting tool failures cannot produce provider messages");
|
|
5733
|
+
}
|
|
5734
|
+
return { ok: true, content: sensitive ? SENSITIVE_FAILURE_MESSAGE : outcome.modelMessage };
|
|
5735
|
+
}
|
|
5736
|
+
if (outcome.kind !== "succeeded") {
|
|
5737
|
+
throw new Error("Cancelled tool calls cannot produce provider messages");
|
|
5738
|
+
}
|
|
5739
|
+
if (sensitive) return { ok: true, content: SENSITIVE_SUCCESS_MESSAGE };
|
|
5740
|
+
if (invocation.source.kind === "mcp") {
|
|
5741
|
+
try {
|
|
5742
|
+
return { ok: true, content: mcpContent(outcome.result) };
|
|
5743
|
+
} catch (error) {
|
|
5744
|
+
rethrowEventStreamOverflow(error);
|
|
5745
|
+
return { ok: false, phase: "serialization", error };
|
|
5746
|
+
}
|
|
5747
|
+
}
|
|
5748
|
+
if (invocation.configuredTool?.toModelOutput) {
|
|
5749
|
+
try {
|
|
5750
|
+
const project = invocation.configuredTool.toModelOutput;
|
|
5751
|
+
return {
|
|
5752
|
+
ok: true,
|
|
5753
|
+
content: serializeToolModelOutput(
|
|
5754
|
+
invocation.requestedTool,
|
|
5755
|
+
project(outcome.result)
|
|
5756
|
+
)
|
|
5757
|
+
};
|
|
5758
|
+
} catch (error) {
|
|
5759
|
+
rethrowEventStreamOverflow(error);
|
|
5760
|
+
if (error instanceof ToolModelOutputError && isEventStreamOverflowError(error.cause)) {
|
|
5761
|
+
throw error.cause;
|
|
5762
|
+
}
|
|
5763
|
+
return {
|
|
5764
|
+
ok: false,
|
|
5765
|
+
phase: "projection",
|
|
5766
|
+
error: error instanceof ToolModelOutputError && error.toolName === invocation.requestedTool ? error : new ToolModelOutputError(invocation.requestedTool, error)
|
|
4892
5767
|
};
|
|
4893
5768
|
}
|
|
4894
5769
|
}
|
|
4895
|
-
|
|
4896
|
-
|
|
5770
|
+
try {
|
|
5771
|
+
const content = JSON.stringify(outcome.result);
|
|
5772
|
+
if (content === void 0) throw new Error("Tool result is not JSON serializable");
|
|
5773
|
+
return { ok: true, content };
|
|
5774
|
+
} catch (error) {
|
|
5775
|
+
rethrowEventStreamOverflow(error);
|
|
5776
|
+
return { ok: false, phase: "serialization", error };
|
|
4897
5777
|
}
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
5778
|
+
}
|
|
5779
|
+
async function settleAcceptedTool(options) {
|
|
5780
|
+
const internal = await executeAcceptedTool(options);
|
|
5781
|
+
if (internal.kind === "cancelled") {
|
|
5782
|
+
return {
|
|
5783
|
+
outcome: { status: "cancelled", cancellation: internal.cancellation },
|
|
5784
|
+
abortError: internal.error
|
|
4902
5785
|
};
|
|
4903
5786
|
}
|
|
4904
|
-
if (
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
apiKey: process.env.OPENAI_API_KEY
|
|
5787
|
+
if (internal.kind === "failed" && internal.failure.disposition === "abort") {
|
|
5788
|
+
return {
|
|
5789
|
+
outcome: { status: "failed", failure: internal.failure },
|
|
5790
|
+
abortError: internal.error
|
|
4909
5791
|
};
|
|
4910
5792
|
}
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
5793
|
+
const result = internal.kind === "succeeded" ? internal.result : void 0;
|
|
5794
|
+
const outputPhase = internal.kind === "succeeded" && !options.invocation.configuredTool?.sensitive && options.invocation.source.kind !== "mcp" && options.invocation.configuredTool?.toModelOutput ? "projection" : "serialization";
|
|
5795
|
+
if (internal.kind === "succeeded" && options.signal?.aborted) {
|
|
5796
|
+
return {
|
|
5797
|
+
outcome: {
|
|
5798
|
+
status: "cancelled",
|
|
5799
|
+
cancellation: { phase: outputPhase, result }
|
|
5800
|
+
},
|
|
5801
|
+
abortError: normalizeAbortError(options.signal.reason)
|
|
4916
5802
|
};
|
|
4917
5803
|
}
|
|
4918
|
-
const
|
|
4919
|
-
if (
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
5804
|
+
const prepared = prepareToolMessage(options.invocation, internal);
|
|
5805
|
+
if (internal.kind === "succeeded" && options.signal?.aborted) {
|
|
5806
|
+
return {
|
|
5807
|
+
outcome: {
|
|
5808
|
+
status: "cancelled",
|
|
5809
|
+
cancellation: { phase: outputPhase, result }
|
|
5810
|
+
},
|
|
5811
|
+
abortError: normalizeAbortError(options.signal.reason)
|
|
4924
5812
|
};
|
|
4925
5813
|
}
|
|
4926
|
-
|
|
5814
|
+
if (!prepared.ok) {
|
|
5815
|
+
const outputAbort = cancellationError(options.signal, prepared.error) ?? (prepared.error instanceof ToolModelOutputError ? cancellationError(options.signal, prepared.error.cause) : void 0);
|
|
5816
|
+
if (outputAbort !== void 0) {
|
|
5817
|
+
return {
|
|
5818
|
+
outcome: {
|
|
5819
|
+
status: "cancelled",
|
|
5820
|
+
cancellation: { phase: prepared.phase, result }
|
|
5821
|
+
},
|
|
5822
|
+
abortError: outputAbort
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5825
|
+
const failure = {
|
|
5826
|
+
phase: prepared.phase,
|
|
5827
|
+
kind: "output",
|
|
5828
|
+
disposition: "abort",
|
|
5829
|
+
error: eventError(prepared.error),
|
|
5830
|
+
result
|
|
5831
|
+
};
|
|
5832
|
+
return {
|
|
5833
|
+
outcome: { status: "failed", failure },
|
|
5834
|
+
abortError: prepared.error
|
|
5835
|
+
};
|
|
5836
|
+
}
|
|
5837
|
+
if (internal.kind === "succeeded") {
|
|
5838
|
+
return {
|
|
5839
|
+
outcome: { status: "succeeded", result: internal.result },
|
|
5840
|
+
providerContent: prepared.content
|
|
5841
|
+
};
|
|
5842
|
+
}
|
|
5843
|
+
if (internal.kind === "denied") {
|
|
5844
|
+
return {
|
|
5845
|
+
outcome: { status: "denied", ...internal.reason ? { reason: internal.reason } : {} },
|
|
5846
|
+
providerContent: prepared.content
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5849
|
+
return {
|
|
5850
|
+
outcome: { status: "failed", failure: internal.failure },
|
|
5851
|
+
providerContent: prepared.content
|
|
5852
|
+
};
|
|
5853
|
+
}
|
|
5854
|
+
function recordToolSpanOutcome(span, outcome) {
|
|
5855
|
+
span.setAttribute("axl.tool.outcome", outcome.status);
|
|
5856
|
+
span.setAttribute("axl.tool.success", outcome.status === "succeeded");
|
|
5857
|
+
switch (outcome.status) {
|
|
5858
|
+
case "succeeded":
|
|
5859
|
+
span.setStatus("ok");
|
|
5860
|
+
return;
|
|
5861
|
+
case "denied":
|
|
5862
|
+
span.setStatus("unset");
|
|
5863
|
+
return;
|
|
5864
|
+
case "failed":
|
|
5865
|
+
span.setAttribute("axl.tool.phase", outcome.failure.phase);
|
|
5866
|
+
span.setStatus("error");
|
|
5867
|
+
return;
|
|
5868
|
+
case "cancelled":
|
|
5869
|
+
span.setAttribute("axl.tool.phase", outcome.cancellation.phase);
|
|
5870
|
+
span.setStatus("error");
|
|
5871
|
+
}
|
|
4927
5872
|
}
|
|
4928
5873
|
|
|
4929
5874
|
// src/context.ts
|
|
4930
5875
|
var signalStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
5876
|
+
var DEFAULT_BRANCH_DRAIN_TIMEOUT_MS = 5e3;
|
|
5877
|
+
function resolveBranchDrainTimeoutMs(value) {
|
|
5878
|
+
const resolved = value === void 0 ? DEFAULT_BRANCH_DRAIN_TIMEOUT_MS : value;
|
|
5879
|
+
if (typeof resolved !== "number" || Number.isNaN(resolved) || !Number.isFinite(resolved) || resolved < 0) {
|
|
5880
|
+
throw new AxlError(
|
|
5881
|
+
"INVALID_CONFIG",
|
|
5882
|
+
`branchDrainTimeoutMs must be a non-negative finite number (got ${String(resolved)})`
|
|
5883
|
+
);
|
|
5884
|
+
}
|
|
5885
|
+
return resolved;
|
|
5886
|
+
}
|
|
5887
|
+
function parseHumanDecision(value) {
|
|
5888
|
+
const invalid2 = () => {
|
|
5889
|
+
throw new AxlError(
|
|
5890
|
+
"INVALID_HUMAN_DECISION",
|
|
5891
|
+
"Human decision must be a plain object with a boolean approved field and only branch-appropriate string data"
|
|
5892
|
+
);
|
|
5893
|
+
};
|
|
5894
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) invalid2();
|
|
5895
|
+
let prototype;
|
|
5896
|
+
try {
|
|
5897
|
+
prototype = Object.getPrototypeOf(value);
|
|
5898
|
+
} catch {
|
|
5899
|
+
return invalid2();
|
|
5900
|
+
}
|
|
5901
|
+
if (prototype !== Object.prototype && prototype !== null) invalid2();
|
|
5902
|
+
let ownKeys;
|
|
5903
|
+
try {
|
|
5904
|
+
ownKeys = Reflect.ownKeys(value);
|
|
5905
|
+
} catch {
|
|
5906
|
+
return invalid2();
|
|
5907
|
+
}
|
|
5908
|
+
if (ownKeys.some(
|
|
5909
|
+
(key) => typeof key !== "string" || key !== "approved" && key !== "data" && key !== "reason"
|
|
5910
|
+
)) {
|
|
5911
|
+
invalid2();
|
|
5912
|
+
}
|
|
5913
|
+
const readDataProperty = (key) => {
|
|
5914
|
+
let descriptor;
|
|
5915
|
+
try {
|
|
5916
|
+
descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
5917
|
+
} catch {
|
|
5918
|
+
return invalid2();
|
|
5919
|
+
}
|
|
5920
|
+
if (!descriptor) {
|
|
5921
|
+
if (ownKeys.includes(key)) invalid2();
|
|
5922
|
+
return { present: false, value: void 0 };
|
|
5923
|
+
}
|
|
5924
|
+
if (!ownKeys.includes(key)) return invalid2();
|
|
5925
|
+
if (!("value" in descriptor)) return invalid2();
|
|
5926
|
+
return { present: true, value: descriptor.value };
|
|
5927
|
+
};
|
|
5928
|
+
const approved = readDataProperty("approved");
|
|
5929
|
+
const data = readDataProperty("data");
|
|
5930
|
+
const reason = readDataProperty("reason");
|
|
5931
|
+
if (approved.present && approved.value === true && !reason.present) {
|
|
5932
|
+
if (data.value === void 0 || typeof data.value === "string") {
|
|
5933
|
+
return { approved: true, ...data.value === void 0 ? {} : { data: data.value } };
|
|
5934
|
+
}
|
|
5935
|
+
}
|
|
5936
|
+
if (approved.present && approved.value === false && !data.present) {
|
|
5937
|
+
if (reason.value === void 0 || typeof reason.value === "string") {
|
|
5938
|
+
return {
|
|
5939
|
+
approved: false,
|
|
5940
|
+
...reason.value === void 0 ? {} : { reason: reason.value }
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5943
|
+
}
|
|
5944
|
+
return invalid2();
|
|
5945
|
+
}
|
|
4931
5946
|
var askStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
4932
5947
|
var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
4933
5948
|
function zodToJsonSchema(schema) {
|
|
4934
5949
|
const cached = inlineSchemaCache.get(schema);
|
|
4935
5950
|
if (cached !== void 0) return cached;
|
|
4936
|
-
const result =
|
|
5951
|
+
const result = import_zod2.z.toJSONSchema(schema, { unrepresentable: "any" });
|
|
4937
5952
|
delete result.$schema;
|
|
4938
5953
|
Object.freeze(result);
|
|
4939
5954
|
inlineSchemaCache.set(schema, result);
|
|
@@ -4952,7 +5967,7 @@ var nativeSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
|
4952
5967
|
function deriveNativeSchema(schema) {
|
|
4953
5968
|
const cached = nativeSchemaCache.get(schema);
|
|
4954
5969
|
if (cached !== void 0) return cached;
|
|
4955
|
-
const json =
|
|
5970
|
+
const json = import_zod2.z.toJSONSchema(schema, { unrepresentable: "any", io: "input" });
|
|
4956
5971
|
delete json.$schema;
|
|
4957
5972
|
nativeSchemaCache.set(schema, json);
|
|
4958
5973
|
return json;
|
|
@@ -4960,7 +5975,7 @@ function deriveNativeSchema(schema) {
|
|
|
4960
5975
|
function renderSchemaForPrompt(schema) {
|
|
4961
5976
|
const cached = promptSchemaCache.get(schema);
|
|
4962
5977
|
if (cached !== void 0) return cached;
|
|
4963
|
-
const json =
|
|
5978
|
+
const json = import_zod2.z.toJSONSchema(schema, {
|
|
4964
5979
|
unrepresentable: "any",
|
|
4965
5980
|
io: "input",
|
|
4966
5981
|
reused: "ref"
|
|
@@ -5061,9 +6076,8 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5061
6076
|
providerRegistry;
|
|
5062
6077
|
sessionHistory;
|
|
5063
6078
|
onTrace;
|
|
5064
|
-
onToken;
|
|
5065
|
-
onToolCall;
|
|
5066
6079
|
pendingDecisions;
|
|
6080
|
+
awaitHumanClaims;
|
|
5067
6081
|
budgetContext;
|
|
5068
6082
|
stateStore;
|
|
5069
6083
|
/** Root step counter for this execution. Inherited by every ctx.ask()
|
|
@@ -5084,27 +6098,27 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5084
6098
|
* the caller-supplied key.
|
|
5085
6099
|
*/
|
|
5086
6100
|
autoCheckpointCounters;
|
|
5087
|
-
|
|
6101
|
+
branchDrainState;
|
|
6102
|
+
/** Root-shared idempotency guards for workflow lifecycle emission.
|
|
5088
6103
|
* Both `runtime.execute()` and `runtime.stream()` have paths where a
|
|
5089
6104
|
* post-emit side-effect (checkpoint deletion, state-store persistence)
|
|
5090
6105
|
* can throw AFTER `_emitWorkflowEnd({status: 'completed'})` has already
|
|
5091
6106
|
* fired — the outer catch would then fire `_emitWorkflowEnd({status:
|
|
5092
6107
|
* 'failed'})` for a second time. These flags make both emitters
|
|
5093
6108
|
* single-fire so consumers never see paired-then-conflicting terminal
|
|
5094
|
-
* events for one execution.
|
|
5095
|
-
|
|
5096
|
-
|
|
6109
|
+
* events for one execution. Sharing the state with child contexts also
|
|
6110
|
+
* suppresses late branch events after bounded finalization. */
|
|
6111
|
+
workflowLifecycleState;
|
|
5097
6112
|
signal;
|
|
5098
6113
|
summaryCache;
|
|
5099
6114
|
workflowName;
|
|
5100
6115
|
mcpManager;
|
|
5101
6116
|
spanManager;
|
|
5102
6117
|
memoryManager;
|
|
5103
|
-
resumeMode;
|
|
5104
6118
|
toolOverrides;
|
|
5105
6119
|
awaitHumanHandler;
|
|
5106
|
-
onAgentStart;
|
|
5107
6120
|
onAgentCallComplete;
|
|
6121
|
+
_onDecisionCleanupFailed;
|
|
5108
6122
|
/** Shared mutable slot for the lazy `AxlEventBus`. The slot is shared
|
|
5109
6123
|
* by reference across parent/child contexts so a late allocation
|
|
5110
6124
|
* (consumer subscribes after a child context exists) is visible
|
|
@@ -5113,6 +6127,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5113
6127
|
* contexts that never observe). */
|
|
5114
6128
|
_busRef;
|
|
5115
6129
|
_eventStreamOptions;
|
|
6130
|
+
_forceStreaming;
|
|
5116
6131
|
/** Removes the constructor-registered `'abort'` listener from
|
|
5117
6132
|
* `this.signal` so a long-lived signal (e.g., a request-scoped or
|
|
5118
6133
|
* process-wide signal reused across many `runtime.createContext()` /
|
|
@@ -5132,8 +6147,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5132
6147
|
* *between* asks. `ctx.events.partialObjects` is the coalescing view
|
|
5133
6148
|
* designed for streaming-structured-output UIs.
|
|
5134
6149
|
* - **Ad-hoc contexts** from `runtime.createContext()`: the same
|
|
5135
|
-
* iterable
|
|
5136
|
-
* `onAgentStart` callbacks.
|
|
6150
|
+
* iterable provides typed per-context observation.
|
|
5137
6151
|
* - **Cross-execution**: prefer `runtime.on('trace', …)` instead — that
|
|
5138
6152
|
* fans out across all executions, while `ctx.events` is per-context.
|
|
5139
6153
|
*
|
|
@@ -5148,9 +6162,9 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5148
6162
|
*
|
|
5149
6163
|
* **Subscribe before the first `ctx.ask()`.** The streaming code
|
|
5150
6164
|
* path inside `ctx.ask()` activates only when an observer is
|
|
5151
|
-
* present at the time the ask starts (`_streamingEnabled` —
|
|
5152
|
-
*
|
|
5153
|
-
*
|
|
6165
|
+
* present at the time the ask starts (`_streamingEnabled` — an explicit
|
|
6166
|
+
* runtime streaming mode or allocated `ctx.events`). If you allocate
|
|
6167
|
+
* `ctx.events` AFTER a `ctx.ask()` has
|
|
5154
6168
|
* begun, that in-flight ask will not stream `token` /
|
|
5155
6169
|
* `partial_object` events at all (the agent loop went through
|
|
5156
6170
|
* `provider.chat` instead of `provider.stream`). Subsequent asks
|
|
@@ -5189,18 +6203,16 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5189
6203
|
this._busRef.current?._finish();
|
|
5190
6204
|
this.abortListenerCleanup?.();
|
|
5191
6205
|
}
|
|
5192
|
-
/** True
|
|
5193
|
-
*
|
|
5194
|
-
*
|
|
5195
|
-
* been allocated (a workflow-handler consumer subscribed before
|
|
5196
|
-
* the ask). `ctx.ask` reads this to decide whether to enter the
|
|
6206
|
+
/** True when asks should use provider streaming: `runtime.stream()` set an
|
|
6207
|
+
* explicit internal mode or `ctx.events` has been allocated. `ctx.ask`
|
|
6208
|
+
* reads this to decide whether to enter the
|
|
5197
6209
|
* streaming code path; the gate is re-checked per ask, so a
|
|
5198
6210
|
* consumer subscribing AFTER the first ask started still gets
|
|
5199
6211
|
* streaming on the next one. The check `_busRef.current !== undefined`
|
|
5200
6212
|
* is a one-way flag — `_finish` does not unset the reference, so
|
|
5201
6213
|
* once an observer was present, every subsequent ask streams. */
|
|
5202
6214
|
get _streamingEnabled() {
|
|
5203
|
-
return this.
|
|
6215
|
+
return this._forceStreaming || this._busRef.current !== void 0;
|
|
5204
6216
|
}
|
|
5205
6217
|
constructor(init) {
|
|
5206
6218
|
this.input = init.input;
|
|
@@ -5210,9 +6222,8 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5210
6222
|
this.providerRegistry = init.providerRegistry;
|
|
5211
6223
|
this.sessionHistory = init.sessionHistory ?? [];
|
|
5212
6224
|
this.onTrace = init.onTrace;
|
|
5213
|
-
this.onToken = init.onToken;
|
|
5214
|
-
this.onToolCall = init.onToolCall;
|
|
5215
6225
|
this.pendingDecisions = init.pendingDecisions;
|
|
6226
|
+
this.awaitHumanClaims = init.awaitHumanClaims ?? /* @__PURE__ */ new Map();
|
|
5216
6227
|
this.budgetContext = init.budgetContext;
|
|
5217
6228
|
this.stateStore = init.stateStore;
|
|
5218
6229
|
this.signal = init.signal;
|
|
@@ -5220,13 +6231,13 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5220
6231
|
this.mcpManager = init.mcpManager;
|
|
5221
6232
|
this.spanManager = init.spanManager;
|
|
5222
6233
|
this.memoryManager = init.memoryManager;
|
|
5223
|
-
this.resumeMode = init.resumeMode ?? false;
|
|
5224
6234
|
this.toolOverrides = init.toolOverrides;
|
|
5225
6235
|
this.awaitHumanHandler = init.awaitHumanHandler;
|
|
5226
|
-
this.onAgentStart = init.onAgentStart;
|
|
5227
6236
|
this.onAgentCallComplete = init.onAgentCallComplete;
|
|
6237
|
+
this._onDecisionCleanupFailed = init._onDecisionCleanupFailed;
|
|
5228
6238
|
this._busRef = init._busRef ?? { current: void 0 };
|
|
5229
6239
|
this._eventStreamOptions = init.eventStreamOptions;
|
|
6240
|
+
this._forceStreaming = init._forceStreaming ?? false;
|
|
5230
6241
|
if (init._busRef === void 0 && this.signal && !this.signal.aborted) {
|
|
5231
6242
|
const signal = this.signal;
|
|
5232
6243
|
const onAbort = () => {
|
|
@@ -5242,6 +6253,15 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5242
6253
|
byAgent: /* @__PURE__ */ new Map(),
|
|
5243
6254
|
root: 0
|
|
5244
6255
|
};
|
|
6256
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(init.branchDrainTimeoutMs);
|
|
6257
|
+
this.branchDrainState = init.branchDrainState ?? {
|
|
6258
|
+
pending: /* @__PURE__ */ new Set(),
|
|
6259
|
+
timeoutMs: branchDrainTimeoutMs
|
|
6260
|
+
};
|
|
6261
|
+
this.workflowLifecycleState = init.workflowLifecycleState ?? {
|
|
6262
|
+
startEmitted: false,
|
|
6263
|
+
endEmitted: false
|
|
6264
|
+
};
|
|
5245
6265
|
if (init.metadata?.summaryCache) {
|
|
5246
6266
|
this.summaryCache = init.metadata.summaryCache;
|
|
5247
6267
|
}
|
|
@@ -5250,16 +6270,9 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5250
6270
|
* Create a child context for nested agent invocations (e.g., agent-as-tool).
|
|
5251
6271
|
* Shares: budget tracking, abort signals, trace emission, provider registry,
|
|
5252
6272
|
* state store, span manager, memory manager, MCP manager, config,
|
|
5253
|
-
* awaitHuman handler, pending decisions, tool overrides,
|
|
5254
|
-
* streaming callbacks (onToken / onAgentStart / onToolCall).
|
|
6273
|
+
* awaitHuman handler, pending decisions, tool overrides, and event bus.
|
|
5255
6274
|
* Isolates: session history.
|
|
5256
6275
|
*
|
|
5257
|
-
* Streaming callbacks now propagate into nested asks because every callback
|
|
5258
|
-
* invocation carries `meta.askId`/`meta.parentAskId`/`meta.depth` so
|
|
5259
|
-
* consumers that want root-only behavior can filter on `meta.depth === 0`
|
|
5260
|
-
* instead of relying on the runtime to drop nested events. This is the
|
|
5261
|
-
* "nested ask visibility" fix from spec §3.2.
|
|
5262
|
-
*
|
|
5263
6276
|
* The shared step counter lives in `askStorage` (ALS), so there is no
|
|
5264
6277
|
* per-context counter to pass through anymore. Nested events are
|
|
5265
6278
|
* correlated to their parent via `parentAskId` (set on every nested ask
|
|
@@ -5280,12 +6293,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5280
6293
|
spanManager: this.spanManager,
|
|
5281
6294
|
memoryManager: this.memoryManager,
|
|
5282
6295
|
onTrace: this.onTrace,
|
|
5283
|
-
onToken: this.onToken,
|
|
5284
|
-
onToolCall: this.onToolCall,
|
|
5285
|
-
onAgentStart: this.onAgentStart,
|
|
5286
6296
|
onAgentCallComplete: this.onAgentCallComplete,
|
|
6297
|
+
_onDecisionCleanupFailed: this._onDecisionCleanupFailed,
|
|
5287
6298
|
awaitHumanHandler: this.awaitHumanHandler,
|
|
5288
6299
|
pendingDecisions: this.pendingDecisions,
|
|
6300
|
+
awaitHumanClaims: this.awaitHumanClaims,
|
|
5289
6301
|
toolOverrides: this.toolOverrides,
|
|
5290
6302
|
signal: this.signal,
|
|
5291
6303
|
workflowName: this.workflowName,
|
|
@@ -5293,12 +6305,15 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5293
6305
|
// emitted from `ctx.ask` / spawn / race / parallel / map in this
|
|
5294
6306
|
// child context don't collide with the parent's in the state store.
|
|
5295
6307
|
autoCheckpointCounters: this.autoCheckpointCounters,
|
|
6308
|
+
branchDrainState: this.branchDrainState,
|
|
6309
|
+
workflowLifecycleState: this.workflowLifecycleState,
|
|
5296
6310
|
// Share the parent's bus slot (mutable ref). Late allocation in
|
|
5297
6311
|
// either parent or child propagates because both read the same
|
|
5298
6312
|
// slot. Nested-ask events (agent-as-tool pattern) bubble up to
|
|
5299
6313
|
// the parent's iterator with `parentAskId`/`depth` correlation
|
|
5300
6314
|
// intact.
|
|
5301
6315
|
_busRef: this._busRef,
|
|
6316
|
+
_forceStreaming: this._forceStreaming,
|
|
5302
6317
|
eventStreamOptions: this._eventStreamOptions
|
|
5303
6318
|
// Isolated: sessionHistory (empty)
|
|
5304
6319
|
});
|
|
@@ -5311,28 +6326,194 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5311
6326
|
get currentSignal() {
|
|
5312
6327
|
return signalStorage.getStore() ?? this.signal;
|
|
5313
6328
|
}
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
6329
|
+
_registerBranchDrain(promise) {
|
|
6330
|
+
const state = this.branchDrainState;
|
|
6331
|
+
state.pending.add(promise);
|
|
6332
|
+
void promise.then(
|
|
6333
|
+
() => state.pending.delete(promise),
|
|
6334
|
+
(error) => {
|
|
6335
|
+
state.pending.delete(promise);
|
|
6336
|
+
if (!state.overflow && isEventStreamOverflowError(error)) {
|
|
6337
|
+
state.overflow = error;
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
);
|
|
6341
|
+
}
|
|
6342
|
+
/** Wait for race/quorum losers (including nested branch work) to settle.
|
|
6343
|
+
* Primitive calls still resolve as soon as their winner/quorum is known;
|
|
6344
|
+
* this is only the runtime's terminal/persistence barrier. */
|
|
6345
|
+
async _drainBranchWork() {
|
|
6346
|
+
const state = this.branchDrainState;
|
|
6347
|
+
state.deadline ??= Date.now() + state.timeoutMs;
|
|
6348
|
+
while (state.pending.size > 0) {
|
|
6349
|
+
const overflow2 = state.overflow;
|
|
6350
|
+
state.overflow = void 0;
|
|
6351
|
+
if (overflow2) throw overflow2;
|
|
6352
|
+
const remaining = state.deadline - Date.now();
|
|
6353
|
+
if (remaining <= 0) {
|
|
6354
|
+
return {
|
|
6355
|
+
complete: false,
|
|
6356
|
+
reason: "branch_drain_timeout",
|
|
6357
|
+
pendingContinuations: state.pending.size,
|
|
6358
|
+
timeoutMs: state.timeoutMs
|
|
6359
|
+
};
|
|
6360
|
+
}
|
|
6361
|
+
let timer;
|
|
6362
|
+
const settled = await Promise.race([
|
|
6363
|
+
Promise.allSettled([...state.pending]).then(() => true),
|
|
6364
|
+
new Promise((resolve2) => {
|
|
6365
|
+
timer = setTimeout(() => resolve2(false), remaining);
|
|
6366
|
+
})
|
|
6367
|
+
]);
|
|
6368
|
+
if (timer) clearTimeout(timer);
|
|
6369
|
+
if (!settled) {
|
|
6370
|
+
return {
|
|
6371
|
+
complete: false,
|
|
6372
|
+
reason: "branch_drain_timeout",
|
|
6373
|
+
pendingContinuations: state.pending.size,
|
|
6374
|
+
timeoutMs: state.timeoutMs
|
|
6375
|
+
};
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
const overflow = state.overflow;
|
|
6379
|
+
state.overflow = void 0;
|
|
6380
|
+
if (overflow) throw overflow;
|
|
6381
|
+
state.deadline = void 0;
|
|
6382
|
+
return { complete: true };
|
|
6383
|
+
}
|
|
6384
|
+
/** Resolve, execute, and observe one non-handoff tool request. Rejections
|
|
6385
|
+
* never start. Every accepted request emits one terminal event before a
|
|
6386
|
+
* continuation message or abort escapes this coordinator. */
|
|
6387
|
+
async observeToolInvocation(agent2, toolCall, currentMessages, availableTools) {
|
|
6388
|
+
const signal = this.currentSignal;
|
|
6389
|
+
signal?.throwIfAborted();
|
|
6390
|
+
const requestedTool = toolCall.function.name;
|
|
6391
|
+
const configuredTool = agent2._config.tools?.find(
|
|
6392
|
+
(candidate) => candidate.name === requestedTool
|
|
6393
|
+
);
|
|
6394
|
+
const override = this.toolOverrides?.get(requestedTool);
|
|
6395
|
+
const mcpManager = this.mcpManager;
|
|
6396
|
+
const mcpMatch = !configuredTool ? mcpManager?.getToolsForAgent(agent2._config.mcp, agent2._config.mcpTools).find(({ tool: tool2 }) => tool2.name === requestedTool) : void 0;
|
|
6397
|
+
const parsed = parseToolInvocation({
|
|
6398
|
+
toolCall,
|
|
6399
|
+
configuredTool,
|
|
6400
|
+
override,
|
|
6401
|
+
...mcpMatch && mcpManager ? {
|
|
6402
|
+
mcpCall: (args) => mcpManager.callTool(requestedTool, args, mcpMatch.server),
|
|
6403
|
+
mcpTraceName: `${mcpMatch.server}:${requestedTool}`
|
|
6404
|
+
} : {},
|
|
6405
|
+
availableTools
|
|
6406
|
+
});
|
|
6407
|
+
signal?.throwIfAborted();
|
|
6408
|
+
if ("kind" in parsed) {
|
|
6409
|
+
this.emitEvent({
|
|
6410
|
+
type: "tool_call_rejected",
|
|
6411
|
+
agent: agent2._name,
|
|
6412
|
+
tool: parsed.toolName,
|
|
6413
|
+
callId: parsed.callId,
|
|
6414
|
+
data: parsed.data
|
|
6415
|
+
});
|
|
6416
|
+
currentMessages.push({
|
|
6417
|
+
role: "tool",
|
|
6418
|
+
content: parsed.modelMessage,
|
|
6419
|
+
tool_call_id: parsed.callId
|
|
6420
|
+
});
|
|
6421
|
+
return;
|
|
6422
|
+
}
|
|
6423
|
+
const startArgs = cloneToolArguments(parsed.args);
|
|
6424
|
+
this.emitEvent({
|
|
6425
|
+
type: "tool_call_start",
|
|
6426
|
+
agent: agent2._name,
|
|
6427
|
+
tool: parsed.toolName,
|
|
6428
|
+
callId: parsed.callId,
|
|
6429
|
+
data: {
|
|
6430
|
+
args: startArgs,
|
|
6431
|
+
...parsed.requestedTool !== parsed.toolName ? { requestedTool: parsed.requestedTool } : {}
|
|
6432
|
+
}
|
|
6433
|
+
});
|
|
6434
|
+
const startedAt = Date.now();
|
|
6435
|
+
const requestApproval = async () => {
|
|
6436
|
+
const approve = async () => {
|
|
6437
|
+
const approvalArgs = cloneToolArguments(parsed.args);
|
|
6438
|
+
const decision = await this.awaitHuman({
|
|
6439
|
+
channel: "tool_approval",
|
|
6440
|
+
prompt: `Tool "${parsed.toolName}" wants to execute with args: ${JSON.stringify(parsed.args)}`,
|
|
6441
|
+
metadata: { toolName: parsed.toolName, args: approvalArgs, agent: agent2._name }
|
|
6442
|
+
});
|
|
6443
|
+
return decision.approved ? { approved: true } : { approved: false, reason: decision.reason ?? "Denied by human" };
|
|
5333
6444
|
};
|
|
6445
|
+
const approval = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6446
|
+
"axl.tool.approval",
|
|
6447
|
+
{ "axl.tool.name": parsed.toolName, "axl.agent.name": agent2._name },
|
|
6448
|
+
async (span) => {
|
|
6449
|
+
const value = await approve();
|
|
6450
|
+
span.setAttribute("axl.tool.approval.approved", value.approved);
|
|
6451
|
+
return value;
|
|
6452
|
+
}
|
|
6453
|
+
) : await approve();
|
|
6454
|
+
this.emitEvent({
|
|
6455
|
+
type: "tool_approval",
|
|
6456
|
+
agent: agent2._name,
|
|
6457
|
+
tool: parsed.toolName,
|
|
6458
|
+
callId: parsed.callId,
|
|
6459
|
+
data: {
|
|
6460
|
+
approved: approval.approved,
|
|
6461
|
+
args: cloneToolArguments(parsed.args),
|
|
6462
|
+
...approval.reason ? { reason: approval.reason } : {}
|
|
6463
|
+
}
|
|
6464
|
+
});
|
|
6465
|
+
return approval;
|
|
6466
|
+
};
|
|
6467
|
+
let terminalEmitted = false;
|
|
6468
|
+
const executeAndObserve = async (span) => {
|
|
6469
|
+
const settlement2 = await settleAcceptedTool({
|
|
6470
|
+
invocation: parsed,
|
|
6471
|
+
context: this,
|
|
6472
|
+
signal,
|
|
6473
|
+
requestApproval,
|
|
6474
|
+
createChildContext: () => this.createChildContext()
|
|
6475
|
+
});
|
|
6476
|
+
const duration = Date.now() - startedAt;
|
|
6477
|
+
span?.setAttribute("axl.tool.duration", duration);
|
|
6478
|
+
if (span) recordToolSpanOutcome(span, settlement2.outcome);
|
|
6479
|
+
if (terminalEmitted) {
|
|
6480
|
+
throw new Error(`Tool invocation ${parsed.callId} emitted more than one terminal event`);
|
|
6481
|
+
}
|
|
6482
|
+
terminalEmitted = true;
|
|
6483
|
+
try {
|
|
6484
|
+
this.emitEvent({
|
|
6485
|
+
type: "tool_call_end",
|
|
6486
|
+
agent: agent2._name,
|
|
6487
|
+
tool: parsed.toolName,
|
|
6488
|
+
callId: parsed.callId,
|
|
6489
|
+
duration,
|
|
6490
|
+
data: {
|
|
6491
|
+
args: cloneToolArguments(parsed.args),
|
|
6492
|
+
...parsed.requestedTool !== parsed.toolName ? { requestedTool: parsed.requestedTool } : {},
|
|
6493
|
+
outcome: settlement2.outcome
|
|
6494
|
+
}
|
|
6495
|
+
});
|
|
6496
|
+
} catch (error) {
|
|
6497
|
+
if (error instanceof Error && settlement2.abortError !== void 0) {
|
|
6498
|
+
throw preserveErrorCause(error, settlement2.abortError);
|
|
6499
|
+
}
|
|
6500
|
+
throw error;
|
|
6501
|
+
}
|
|
6502
|
+
return settlement2;
|
|
6503
|
+
};
|
|
6504
|
+
const settlement = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6505
|
+
"axl.tool.call",
|
|
6506
|
+
{ "axl.tool.name": parsed.toolName, "axl.agent.name": agent2._name },
|
|
6507
|
+
executeAndObserve
|
|
6508
|
+
) : await executeAndObserve();
|
|
6509
|
+
if (settlement.providerContent !== void 0) {
|
|
6510
|
+
currentMessages.push({
|
|
6511
|
+
role: "tool",
|
|
6512
|
+
content: settlement.providerContent,
|
|
6513
|
+
tool_call_id: parsed.callId
|
|
6514
|
+
});
|
|
5334
6515
|
}
|
|
5335
|
-
|
|
6516
|
+
if (settlement.abortError !== void 0) throw settlement.abortError;
|
|
5336
6517
|
}
|
|
5337
6518
|
// ── ctx.ask() ─────────────────────────────────────────────────────────
|
|
5338
6519
|
async ask(agent2, prompt, options) {
|
|
@@ -5361,18 +6542,21 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5361
6542
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
5362
6543
|
const usageCapture = {};
|
|
5363
6544
|
const doCall = async () => {
|
|
5364
|
-
const
|
|
6545
|
+
const result2 = await this.executeAgentCall(
|
|
5365
6546
|
agent2,
|
|
5366
6547
|
prompt,
|
|
5367
6548
|
options,
|
|
5368
6549
|
void 0,
|
|
5369
6550
|
usageCapture
|
|
5370
6551
|
);
|
|
5371
|
-
return
|
|
6552
|
+
return result2;
|
|
5372
6553
|
};
|
|
5373
6554
|
let outcome;
|
|
6555
|
+
let askFailure;
|
|
6556
|
+
let askFailed = false;
|
|
6557
|
+
let result;
|
|
5374
6558
|
try {
|
|
5375
|
-
|
|
6559
|
+
result = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
5376
6560
|
"axl.agent.ask",
|
|
5377
6561
|
{
|
|
5378
6562
|
"axl.agent.name": agent2._name,
|
|
@@ -5431,36 +6615,34 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
5431
6615
|
);
|
|
5432
6616
|
}
|
|
5433
6617
|
}
|
|
5434
|
-
return result;
|
|
5435
6618
|
} catch (err) {
|
|
6619
|
+
askFailed = true;
|
|
6620
|
+
askFailure = err;
|
|
5436
6621
|
outcome = {
|
|
5437
6622
|
ok: false,
|
|
5438
6623
|
error: err instanceof Error ? err.message : String(err)
|
|
5439
6624
|
};
|
|
5440
|
-
throw err;
|
|
5441
|
-
} finally {
|
|
5442
|
-
try {
|
|
5443
|
-
this.emitEvent({
|
|
5444
|
-
type: "ask_end",
|
|
5445
|
-
outcome: outcome ?? {
|
|
5446
|
-
ok: false,
|
|
5447
|
-
error: "ask_end emitted without outcome \u2014 internal bug"
|
|
5448
|
-
},
|
|
5449
|
-
cost: frame.askCost.value,
|
|
5450
|
-
...frame.askUnpriced ? { unpriced: true } : {},
|
|
5451
|
-
duration: Date.now() - askStart
|
|
5452
|
-
});
|
|
5453
|
-
} catch (emitErr) {
|
|
5454
|
-
if (emitErr instanceof EventStreamOverflowError && outcome && !outcome.ok) {
|
|
5455
|
-
console.error(
|
|
5456
|
-
"[axl] ask_end emit overflowed during error path; preserving original error:",
|
|
5457
|
-
emitErr.message
|
|
5458
|
-
);
|
|
5459
|
-
} else {
|
|
5460
|
-
throw emitErr;
|
|
5461
|
-
}
|
|
5462
|
-
}
|
|
5463
6625
|
}
|
|
6626
|
+
let terminalFailure;
|
|
6627
|
+
let terminalFailed = false;
|
|
6628
|
+
try {
|
|
6629
|
+
this.emitEvent({
|
|
6630
|
+
type: "ask_end",
|
|
6631
|
+
outcome: outcome ?? {
|
|
6632
|
+
ok: false,
|
|
6633
|
+
error: "ask_end emitted without outcome \u2014 internal bug"
|
|
6634
|
+
},
|
|
6635
|
+
cost: frame.askCost.value,
|
|
6636
|
+
...frame.askUnpriced ? { unpriced: true } : {},
|
|
6637
|
+
duration: Date.now() - askStart
|
|
6638
|
+
});
|
|
6639
|
+
} catch (error) {
|
|
6640
|
+
terminalFailed = true;
|
|
6641
|
+
terminalFailure = isEventStreamOverflowError(error) ? preserveErrorCause(error, askFailure) : error;
|
|
6642
|
+
}
|
|
6643
|
+
if (terminalFailed) throw terminalFailure;
|
|
6644
|
+
if (askFailed) throw askFailure;
|
|
6645
|
+
return result;
|
|
5464
6646
|
});
|
|
5465
6647
|
},
|
|
5466
6648
|
{ agent: agentName }
|
|
@@ -5622,6 +6804,7 @@ ${appendedSchemaText}`;
|
|
|
5622
6804
|
let lastStartAttempt = 1;
|
|
5623
6805
|
let lastStartMaxAttempts = 1;
|
|
5624
6806
|
while (turns < maxTurns) {
|
|
6807
|
+
this.currentSignal?.throwIfAborted();
|
|
5625
6808
|
if (Date.now() - startTime > timeoutMs) {
|
|
5626
6809
|
throw new TimeoutError("ctx.ask()", timeoutMs);
|
|
5627
6810
|
}
|
|
@@ -5681,7 +6864,6 @@ ${appendedSchemaText}`;
|
|
|
5681
6864
|
}
|
|
5682
6865
|
} : { type: "json_object" };
|
|
5683
6866
|
}
|
|
5684
|
-
const callbackMeta = this.currentCallbackMeta(agent2._name);
|
|
5685
6867
|
const callParams = {
|
|
5686
6868
|
...chatOptions.temperature !== void 0 ? { temperature: chatOptions.temperature } : {},
|
|
5687
6869
|
maxTokens: chatOptions.maxTokens,
|
|
@@ -5720,16 +6902,16 @@ ${appendedSchemaText}`;
|
|
|
5720
6902
|
...messagesSnapshot ? { messages: messagesSnapshot } : {}
|
|
5721
6903
|
}
|
|
5722
6904
|
});
|
|
5723
|
-
this.onAgentStart?.({ agent: agent2._name, model: modelUri }, callbackMeta);
|
|
5724
6905
|
let response;
|
|
5725
6906
|
try {
|
|
6907
|
+
this.currentSignal?.throwIfAborted();
|
|
5726
6908
|
if (this._streamingEnabled) {
|
|
5727
6909
|
let content2 = "";
|
|
5728
6910
|
const toolCalls = [];
|
|
5729
6911
|
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
5730
6912
|
let streamProviderMetadata;
|
|
5731
6913
|
let thinkingContent = "";
|
|
5732
|
-
const streamingObjectEnabled = !!options?.schema && toolDefs.length === 0 && options.schema instanceof
|
|
6914
|
+
const streamingObjectEnabled = !!options?.schema && toolDefs.length === 0 && options.schema instanceof import_zod2.z.ZodObject;
|
|
5733
6915
|
const currentAttempt = schemaRetries + 1;
|
|
5734
6916
|
const walker = streamingObjectEnabled ? new StreamingWalker({
|
|
5735
6917
|
onStringDelta: (path, delta) => {
|
|
@@ -5745,7 +6927,6 @@ ${appendedSchemaText}`;
|
|
|
5745
6927
|
if (chunk.type === "text_delta") {
|
|
5746
6928
|
content2 += chunk.content;
|
|
5747
6929
|
this.emitEvent({ type: "token", data: chunk.content });
|
|
5748
|
-
this.onToken?.(chunk.content, callbackMeta);
|
|
5749
6930
|
if (walker) {
|
|
5750
6931
|
walker.processChunk(chunk.content);
|
|
5751
6932
|
if (walker.consumeBoundary()) {
|
|
@@ -5855,6 +7036,7 @@ ${appendedSchemaText}`;
|
|
|
5855
7036
|
...retryReason ? { retryReason } : {}
|
|
5856
7037
|
}
|
|
5857
7038
|
});
|
|
7039
|
+
this.currentSignal?.throwIfAborted();
|
|
5858
7040
|
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
5859
7041
|
currentMessages.push({
|
|
5860
7042
|
role: "assistant",
|
|
@@ -6024,232 +7206,12 @@ ${appendedSchemaText}`;
|
|
|
6024
7206
|
return await handoffFn();
|
|
6025
7207
|
}
|
|
6026
7208
|
}
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
currentMessages.push({
|
|
6034
|
-
role: "tool",
|
|
6035
|
-
content: `Error: Invalid JSON in tool arguments. Please provide valid JSON.`,
|
|
6036
|
-
tool_call_id: toolCall.id
|
|
6037
|
-
});
|
|
6038
|
-
continue;
|
|
6039
|
-
}
|
|
6040
|
-
this.emitEvent({
|
|
6041
|
-
type: "tool_call_start",
|
|
6042
|
-
tool: toolName,
|
|
6043
|
-
callId: toolCall.id,
|
|
6044
|
-
data: { args: toolArgs2 }
|
|
6045
|
-
});
|
|
6046
|
-
this.onToolCall?.(
|
|
6047
|
-
{ name: toolName, args: toolArgs2, callId: toolCall.id },
|
|
6048
|
-
callbackMeta
|
|
6049
|
-
);
|
|
6050
|
-
const toolStart2 = Date.now();
|
|
6051
|
-
const executeOverride = async () => {
|
|
6052
|
-
let toolResult3;
|
|
6053
|
-
try {
|
|
6054
|
-
toolResult3 = await toolOverride(toolArgs2);
|
|
6055
|
-
} catch (err) {
|
|
6056
|
-
toolResult3 = { error: err instanceof Error ? err.message : String(err) };
|
|
6057
|
-
}
|
|
6058
|
-
return toolResult3;
|
|
6059
|
-
};
|
|
6060
|
-
const toolResult2 = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6061
|
-
"axl.tool.call",
|
|
6062
|
-
{
|
|
6063
|
-
"axl.tool.name": toolName,
|
|
6064
|
-
"axl.agent.name": agent2._name
|
|
6065
|
-
},
|
|
6066
|
-
async (span) => {
|
|
6067
|
-
const r = await executeOverride();
|
|
6068
|
-
span.setAttribute("axl.tool.duration", Date.now() - toolStart2);
|
|
6069
|
-
const isError = r && typeof r === "object" && "error" in r;
|
|
6070
|
-
span.setAttribute("axl.tool.success", !isError);
|
|
6071
|
-
if (isError)
|
|
6072
|
-
span.setStatus("error", r.error);
|
|
6073
|
-
return r;
|
|
6074
|
-
}
|
|
6075
|
-
) : await executeOverride();
|
|
6076
|
-
const resultContent2 = JSON.stringify(toolResult2);
|
|
6077
|
-
this.emitEvent({
|
|
6078
|
-
type: "tool_call_end",
|
|
6079
|
-
agent: agent2._name,
|
|
6080
|
-
tool: toolName,
|
|
6081
|
-
duration: Date.now() - toolStart2,
|
|
6082
|
-
data: { args: toolArgs2, result: toolResult2, callId: toolCall.id }
|
|
6083
|
-
});
|
|
6084
|
-
currentMessages.push({
|
|
6085
|
-
role: "tool",
|
|
6086
|
-
content: resultContent2,
|
|
6087
|
-
tool_call_id: toolCall.id
|
|
6088
|
-
});
|
|
6089
|
-
continue;
|
|
6090
|
-
}
|
|
6091
|
-
const tool2 = agent2._config.tools?.find((t) => t.name === toolName);
|
|
6092
|
-
const isMcpTool = !tool2 && this.mcpManager?.isMcpTool(toolName);
|
|
6093
|
-
if (!tool2 && !isMcpTool) {
|
|
6094
|
-
this.emitEvent({ type: "tool_denied", agent: agent2._name, tool: toolName });
|
|
6095
|
-
currentMessages.push({
|
|
6096
|
-
role: "tool",
|
|
6097
|
-
content: `Tool "${toolName}" is not available. Available tools: ${agent2._config.tools?.map((t) => t.name).join(", ") ?? "none"}`,
|
|
6098
|
-
tool_call_id: toolCall.id
|
|
6099
|
-
});
|
|
6100
|
-
continue;
|
|
6101
|
-
}
|
|
6102
|
-
let toolArgs;
|
|
6103
|
-
try {
|
|
6104
|
-
toolArgs = JSON.parse(toolCall.function.arguments);
|
|
6105
|
-
} catch {
|
|
6106
|
-
currentMessages.push({
|
|
6107
|
-
role: "tool",
|
|
6108
|
-
content: `Error: Invalid JSON in tool arguments. Please provide valid JSON.`,
|
|
6109
|
-
tool_call_id: toolCall.id
|
|
6110
|
-
});
|
|
6111
|
-
continue;
|
|
6112
|
-
}
|
|
6113
|
-
this.emitEvent({
|
|
6114
|
-
type: "tool_call_start",
|
|
6115
|
-
tool: toolName,
|
|
6116
|
-
callId: toolCall.id,
|
|
6117
|
-
data: { args: toolArgs }
|
|
6118
|
-
});
|
|
6119
|
-
this.onToolCall?.({ name: toolName, args: toolArgs, callId: toolCall.id }, callbackMeta);
|
|
6120
|
-
const toolStart = Date.now();
|
|
6121
|
-
if (tool2 && tool2.requireApproval) {
|
|
6122
|
-
const approvalFn = async () => {
|
|
6123
|
-
const decision = await this.awaitHuman({
|
|
6124
|
-
channel: "tool_approval",
|
|
6125
|
-
prompt: `Tool "${toolName}" wants to execute with args: ${JSON.stringify(toolArgs)}`,
|
|
6126
|
-
metadata: { toolName, args: toolArgs, agent: agent2._name }
|
|
6127
|
-
});
|
|
6128
|
-
if (!decision.approved) {
|
|
6129
|
-
const reason = decision.reason ?? "Denied by human";
|
|
6130
|
-
currentMessages.push({
|
|
6131
|
-
role: "tool",
|
|
6132
|
-
content: JSON.stringify({ error: `Tool denied by human: ${reason}` }),
|
|
6133
|
-
tool_call_id: toolCall.id
|
|
6134
|
-
});
|
|
6135
|
-
return { approved: false, reason };
|
|
6136
|
-
}
|
|
6137
|
-
return { approved: true };
|
|
6138
|
-
};
|
|
6139
|
-
let approvalOutcome;
|
|
6140
|
-
if (this.spanManager) {
|
|
6141
|
-
approvalOutcome = await this.spanManager.withSpanAsync(
|
|
6142
|
-
"axl.tool.approval",
|
|
6143
|
-
{
|
|
6144
|
-
"axl.tool.name": toolName,
|
|
6145
|
-
"axl.agent.name": agent2._name
|
|
6146
|
-
},
|
|
6147
|
-
async (span) => {
|
|
6148
|
-
const result = await approvalFn();
|
|
6149
|
-
span.setAttribute("axl.tool.approval.approved", result.approved);
|
|
6150
|
-
return result;
|
|
6151
|
-
}
|
|
6152
|
-
);
|
|
6153
|
-
} else {
|
|
6154
|
-
approvalOutcome = await approvalFn();
|
|
6155
|
-
}
|
|
6156
|
-
this.emitEvent({
|
|
6157
|
-
type: "tool_approval",
|
|
6158
|
-
agent: agent2._name,
|
|
6159
|
-
tool: toolName,
|
|
6160
|
-
data: {
|
|
6161
|
-
approved: approvalOutcome.approved,
|
|
6162
|
-
args: toolArgs,
|
|
6163
|
-
...approvalOutcome.reason ? { reason: approvalOutcome.reason } : {}
|
|
6164
|
-
}
|
|
6165
|
-
});
|
|
6166
|
-
if (!approvalOutcome.approved) continue;
|
|
6167
|
-
}
|
|
6168
|
-
if (tool2 && tool2.hooks?.before) {
|
|
6169
|
-
try {
|
|
6170
|
-
toolArgs = await tool2.hooks.before(toolArgs, this);
|
|
6171
|
-
} catch (err) {
|
|
6172
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
6173
|
-
currentMessages.push({
|
|
6174
|
-
role: "tool",
|
|
6175
|
-
content: JSON.stringify({ error: `Before hook error: ${errorMsg}` }),
|
|
6176
|
-
tool_call_id: toolCall.id
|
|
6177
|
-
});
|
|
6178
|
-
continue;
|
|
6179
|
-
}
|
|
6180
|
-
}
|
|
6181
|
-
const executeTool = async () => {
|
|
6182
|
-
let toolResult2;
|
|
6183
|
-
let resultContent2;
|
|
6184
|
-
if (isMcpTool && this.mcpManager) {
|
|
6185
|
-
try {
|
|
6186
|
-
const mcpResult = await this.mcpManager.callTool(toolName, toolArgs);
|
|
6187
|
-
toolResult2 = mcpResult;
|
|
6188
|
-
resultContent2 = mcpResult.content.map(
|
|
6189
|
-
(c) => c.type === "text" ? c.text : `[${c.type}]`
|
|
6190
|
-
).join("\n");
|
|
6191
|
-
if (mcpResult.isError) {
|
|
6192
|
-
resultContent2 = `Error: ${resultContent2}`;
|
|
6193
|
-
}
|
|
6194
|
-
} catch (err) {
|
|
6195
|
-
toolResult2 = { error: err instanceof Error ? err.message : String(err) };
|
|
6196
|
-
resultContent2 = JSON.stringify(toolResult2);
|
|
6197
|
-
}
|
|
6198
|
-
} else if (tool2) {
|
|
6199
|
-
const childCtx = this.createChildContext();
|
|
6200
|
-
try {
|
|
6201
|
-
toolResult2 = await tool2._execute(toolArgs, childCtx);
|
|
6202
|
-
} catch (err) {
|
|
6203
|
-
toolResult2 = { error: err instanceof Error ? err.message : String(err) };
|
|
6204
|
-
}
|
|
6205
|
-
if (tool2.hooks?.after && !(toolResult2 && typeof toolResult2 === "object" && "error" in toolResult2)) {
|
|
6206
|
-
try {
|
|
6207
|
-
toolResult2 = await tool2.hooks.after(toolResult2, this);
|
|
6208
|
-
} catch (err) {
|
|
6209
|
-
toolResult2 = {
|
|
6210
|
-
error: `After hook error: ${err instanceof Error ? err.message : String(err)}`
|
|
6211
|
-
};
|
|
6212
|
-
}
|
|
6213
|
-
}
|
|
6214
|
-
resultContent2 = tool2.sensitive ? "[REDACTED - sensitive tool output]" : JSON.stringify(toolResult2);
|
|
6215
|
-
} else {
|
|
6216
|
-
toolResult2 = void 0;
|
|
6217
|
-
resultContent2 = "Tool execution error";
|
|
6218
|
-
}
|
|
6219
|
-
return { toolResult: toolResult2, resultContent: resultContent2 };
|
|
6220
|
-
};
|
|
6221
|
-
const traceName = isMcpTool && this.mcpManager ? this.mcpManager.getQualifiedName(toolName) ?? toolName : toolName;
|
|
6222
|
-
const { toolResult, resultContent } = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
6223
|
-
"axl.tool.call",
|
|
6224
|
-
{
|
|
6225
|
-
"axl.tool.name": traceName,
|
|
6226
|
-
"axl.agent.name": agent2._name
|
|
6227
|
-
},
|
|
6228
|
-
async (span) => {
|
|
6229
|
-
const r = await executeTool();
|
|
6230
|
-
span.setAttribute("axl.tool.duration", Date.now() - toolStart);
|
|
6231
|
-
const isError = r.toolResult && typeof r.toolResult === "object" && "error" in r.toolResult;
|
|
6232
|
-
span.setAttribute("axl.tool.success", !isError);
|
|
6233
|
-
if (isError)
|
|
6234
|
-
span.setStatus(
|
|
6235
|
-
"error",
|
|
6236
|
-
r.toolResult.error
|
|
6237
|
-
);
|
|
6238
|
-
return r;
|
|
6239
|
-
}
|
|
6240
|
-
) : await executeTool();
|
|
6241
|
-
this.emitEvent({
|
|
6242
|
-
type: "tool_call_end",
|
|
6243
|
-
agent: agent2._name,
|
|
6244
|
-
tool: traceName,
|
|
6245
|
-
duration: Date.now() - toolStart,
|
|
6246
|
-
data: { args: toolArgs, result: toolResult, callId: toolCall.id }
|
|
6247
|
-
});
|
|
6248
|
-
currentMessages.push({
|
|
6249
|
-
role: "tool",
|
|
6250
|
-
content: resultContent,
|
|
6251
|
-
tool_call_id: toolCall.id
|
|
6252
|
-
});
|
|
7209
|
+
await this.observeToolInvocation(
|
|
7210
|
+
agent2,
|
|
7211
|
+
toolCall,
|
|
7212
|
+
currentMessages,
|
|
7213
|
+
toolDefs.map((tool2) => tool2.function.name)
|
|
7214
|
+
);
|
|
6253
7215
|
}
|
|
6254
7216
|
continue;
|
|
6255
7217
|
}
|
|
@@ -6369,7 +7331,7 @@ ${appendedSchemaText}`;
|
|
|
6369
7331
|
pendingRetryReason = "schema";
|
|
6370
7332
|
continue;
|
|
6371
7333
|
}
|
|
6372
|
-
const zodErr = schemaErr instanceof
|
|
7334
|
+
const zodErr = schemaErr instanceof import_zod2.ZodError ? schemaErr : new import_zod2.ZodError([
|
|
6373
7335
|
{
|
|
6374
7336
|
code: "custom",
|
|
6375
7337
|
path: [],
|
|
@@ -6386,6 +7348,7 @@ ${appendedSchemaText}`;
|
|
|
6386
7348
|
metadata: this.metadata
|
|
6387
7349
|
});
|
|
6388
7350
|
} catch (err) {
|
|
7351
|
+
rethrowEventStreamOverflow(err);
|
|
6389
7352
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6390
7353
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
6391
7354
|
}
|
|
@@ -6554,7 +7517,7 @@ ${appendedSchemaText}`;
|
|
|
6554
7517
|
);
|
|
6555
7518
|
}
|
|
6556
7519
|
}
|
|
6557
|
-
const rootIsObject = schema instanceof
|
|
7520
|
+
const rootIsObject = schema instanceof import_zod2.z.ZodObject;
|
|
6558
7521
|
const hasTools = toolDefs.length > 0;
|
|
6559
7522
|
if (this._streamingEnabled && (!rootIsObject || hasTools)) {
|
|
6560
7523
|
const cause = !rootIsObject ? "non-object" : "tools";
|
|
@@ -6722,12 +7685,12 @@ ${summaryResponse.content}`
|
|
|
6722
7685
|
* Execute a function with checkpoint-replay semantics.
|
|
6723
7686
|
*
|
|
6724
7687
|
* On first execution, runs `fn()`, saves the result under `name`, and
|
|
6725
|
-
* returns it.
|
|
6726
|
-
* without re-executing.
|
|
6727
|
-
*
|
|
7688
|
+
* returns it. A later evaluation in the same explicit execution scope
|
|
7689
|
+
* returns the saved result without re-executing. Axl does not restart or
|
|
7690
|
+
* resume workflow continuations across processes.
|
|
6728
7691
|
*
|
|
6729
7692
|
* `name` must be a stable, caller-supplied identifier — the same call
|
|
6730
|
-
* site MUST pass the same name across
|
|
7693
|
+
* site MUST pass the same name across evaluations of the same execution for
|
|
6731
7694
|
* replay to work. Names are scoped to a single execution and must be
|
|
6732
7695
|
* unique within it; reusing a name (e.g. inside a loop) gives
|
|
6733
7696
|
* last-write-wins behavior. For loops, compose names from the iterator:
|
|
@@ -6850,6 +7813,7 @@ ${summaryResponse.content}`
|
|
|
6850
7813
|
for (let i = 0; i < n; i++) {
|
|
6851
7814
|
const index = i;
|
|
6852
7815
|
const p = signalStorage.run(composedSignal, () => fn(index));
|
|
7816
|
+
this._registerBranchDrain(p);
|
|
6853
7817
|
p.then((value) => {
|
|
6854
7818
|
if (settled) return;
|
|
6855
7819
|
results[index] = { ok: true, value };
|
|
@@ -6864,6 +7828,14 @@ ${summaryResponse.content}`
|
|
|
6864
7828
|
reject(new QuorumNotMet(quorum, successCount, results));
|
|
6865
7829
|
}
|
|
6866
7830
|
}).catch((err) => {
|
|
7831
|
+
if (isEventStreamOverflowError(err)) {
|
|
7832
|
+
if (!settled) {
|
|
7833
|
+
settled = true;
|
|
7834
|
+
controller.abort();
|
|
7835
|
+
reject(err);
|
|
7836
|
+
}
|
|
7837
|
+
return;
|
|
7838
|
+
}
|
|
6867
7839
|
if (settled) return;
|
|
6868
7840
|
const isAbort = err instanceof DOMException && err.name === "AbortError";
|
|
6869
7841
|
if (isAbort) {
|
|
@@ -6886,14 +7858,16 @@ ${summaryResponse.content}`
|
|
|
6886
7858
|
}
|
|
6887
7859
|
const parentSignal = this.currentSignal;
|
|
6888
7860
|
const promises = Array.from({ length: n }, (_, i) => {
|
|
6889
|
-
const run = () => fn(i).then((value) => ({ ok: true, value })).catch(
|
|
6890
|
-
(err)
|
|
7861
|
+
const run = () => fn(i).then((value) => ({ ok: true, value })).catch((err) => {
|
|
7862
|
+
rethrowEventStreamOverflow(err);
|
|
7863
|
+
return {
|
|
6891
7864
|
ok: false,
|
|
6892
7865
|
error: err instanceof Error ? err.message : String(err)
|
|
6893
|
-
}
|
|
6894
|
-
);
|
|
7866
|
+
};
|
|
7867
|
+
});
|
|
6895
7868
|
return parentSignal ? signalStorage.run(parentSignal, run) : run();
|
|
6896
7869
|
});
|
|
7870
|
+
for (const promise of promises) this._registerBranchDrain(promise);
|
|
6897
7871
|
return Promise.all(promises);
|
|
6898
7872
|
}
|
|
6899
7873
|
// ── ctx.vote() ────────────────────────────────────────────────────────
|
|
@@ -7031,6 +8005,7 @@ ${summaryResponse.content}`
|
|
|
7031
8005
|
try {
|
|
7032
8006
|
validateResult = await options.validate(parsed, { metadata: this.metadata });
|
|
7033
8007
|
} catch (err) {
|
|
8008
|
+
rethrowEventStreamOverflow(err);
|
|
7034
8009
|
const reason = err instanceof Error ? err.message : String(err);
|
|
7035
8010
|
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
7036
8011
|
}
|
|
@@ -7048,6 +8023,7 @@ ${summaryResponse.content}`
|
|
|
7048
8023
|
emitVerifyOutcome(true, attempt + 1);
|
|
7049
8024
|
return parsed;
|
|
7050
8025
|
} catch (err) {
|
|
8026
|
+
rethrowEventStreamOverflow(err);
|
|
7051
8027
|
if (err instanceof ValidationError) {
|
|
7052
8028
|
lastRetry = {
|
|
7053
8029
|
error: err.reason,
|
|
@@ -7070,12 +8046,12 @@ ${summaryResponse.content}`
|
|
|
7070
8046
|
}
|
|
7071
8047
|
continue;
|
|
7072
8048
|
}
|
|
7073
|
-
const errorMsg = err instanceof
|
|
8049
|
+
const errorMsg = err instanceof import_zod2.ZodError ? err.message : err instanceof Error ? err.message : String(err);
|
|
7074
8050
|
lastRetry = { error: errorMsg, output: rawOutput };
|
|
7075
8051
|
if (attempt === maxRetries) {
|
|
7076
8052
|
emitVerifyOutcome(false, attempt + 1, errorMsg);
|
|
7077
8053
|
if (options?.fallback !== void 0) return options.fallback;
|
|
7078
|
-
const zodErr = err instanceof
|
|
8054
|
+
const zodErr = err instanceof import_zod2.ZodError ? err : new import_zod2.ZodError([{ code: "custom", path: [], message: errorMsg }]);
|
|
7079
8055
|
throw new VerifyError(rawOutput, zodErr, maxRetries);
|
|
7080
8056
|
}
|
|
7081
8057
|
}
|
|
@@ -7084,7 +8060,7 @@ ${summaryResponse.content}`
|
|
|
7084
8060
|
if (options?.fallback !== void 0) return options.fallback;
|
|
7085
8061
|
throw new VerifyError(
|
|
7086
8062
|
lastRetry?.output,
|
|
7087
|
-
new
|
|
8063
|
+
new import_zod2.ZodError([{ code: "custom", path: [], message: "Verify failed" }]),
|
|
7088
8064
|
maxRetries
|
|
7089
8065
|
);
|
|
7090
8066
|
}
|
|
@@ -7114,6 +8090,7 @@ ${summaryResponse.content}`
|
|
|
7114
8090
|
const unpriced = this.budgetContext.unpriced;
|
|
7115
8091
|
return { value, budgetExceeded: exceeded, totalCost, unpriced };
|
|
7116
8092
|
} catch (err) {
|
|
8093
|
+
rethrowEventStreamOverflow(err);
|
|
7117
8094
|
if (this.budgetContext.exceeded) {
|
|
7118
8095
|
return {
|
|
7119
8096
|
value: null,
|
|
@@ -7214,7 +8191,8 @@ ${summaryResponse.content}`
|
|
|
7214
8191
|
let remaining = fns.length;
|
|
7215
8192
|
for (const fn of fns) {
|
|
7216
8193
|
const p = signalStorage.run(composedSignal, fn);
|
|
7217
|
-
|
|
8194
|
+
this._registerBranchDrain(p);
|
|
8195
|
+
const continuation = p.then(async (value) => {
|
|
7218
8196
|
if (settled) return;
|
|
7219
8197
|
if (schema) {
|
|
7220
8198
|
const parsed = schema.safeParse(value);
|
|
@@ -7244,6 +8222,7 @@ ${summaryResponse.content}`
|
|
|
7244
8222
|
return;
|
|
7245
8223
|
}
|
|
7246
8224
|
} catch (err) {
|
|
8225
|
+
rethrowEventStreamOverflow(err);
|
|
7247
8226
|
remaining--;
|
|
7248
8227
|
lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
|
|
7249
8228
|
if (remaining === 0 && !settled) {
|
|
@@ -7263,6 +8242,14 @@ ${summaryResponse.content}`
|
|
|
7263
8242
|
controller.abort();
|
|
7264
8243
|
resolve2(value);
|
|
7265
8244
|
}).catch((err) => {
|
|
8245
|
+
if (isEventStreamOverflowError(err)) {
|
|
8246
|
+
if (!settled) {
|
|
8247
|
+
settled = true;
|
|
8248
|
+
controller.abort();
|
|
8249
|
+
reject(err);
|
|
8250
|
+
}
|
|
8251
|
+
throw err;
|
|
8252
|
+
}
|
|
7266
8253
|
if (settled) return;
|
|
7267
8254
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
7268
8255
|
remaining--;
|
|
@@ -7279,6 +8266,7 @@ ${summaryResponse.content}`
|
|
|
7279
8266
|
reject(lastError);
|
|
7280
8267
|
}
|
|
7281
8268
|
});
|
|
8269
|
+
this._registerBranchDrain(continuation);
|
|
7282
8270
|
}
|
|
7283
8271
|
});
|
|
7284
8272
|
}
|
|
@@ -7317,9 +8305,19 @@ ${summaryResponse.content}`
|
|
|
7317
8305
|
const idx = nextIndex++;
|
|
7318
8306
|
try {
|
|
7319
8307
|
const value = mapSignal ? await signalStorage.run(mapSignal, () => fn(items[idx], idx)) : await fn(items[idx], idx);
|
|
8308
|
+
if (settled) return;
|
|
7320
8309
|
results[idx] = { ok: true, value };
|
|
7321
8310
|
successCount++;
|
|
7322
8311
|
} catch (err) {
|
|
8312
|
+
if (isEventStreamOverflowError(err)) {
|
|
8313
|
+
settled = true;
|
|
8314
|
+
controller?.abort();
|
|
8315
|
+
reject(err);
|
|
8316
|
+
throw err;
|
|
8317
|
+
}
|
|
8318
|
+
if (settled) {
|
|
8319
|
+
return;
|
|
8320
|
+
}
|
|
7323
8321
|
if (err instanceof DOMException && err.name === "AbortError" && controller?.signal.aborted) {
|
|
7324
8322
|
completedCount++;
|
|
7325
8323
|
return;
|
|
@@ -7345,7 +8343,9 @@ ${summaryResponse.content}`
|
|
|
7345
8343
|
};
|
|
7346
8344
|
const workers = Math.min(concurrency, items.length);
|
|
7347
8345
|
for (let i = 0; i < workers; i++) {
|
|
7348
|
-
runNext()
|
|
8346
|
+
const worker = runNext();
|
|
8347
|
+
this._registerBranchDrain(worker);
|
|
8348
|
+
worker.catch((err) => {
|
|
7349
8349
|
if (!settled) reject(err);
|
|
7350
8350
|
});
|
|
7351
8351
|
}
|
|
@@ -7371,12 +8371,44 @@ ${summaryResponse.content}`
|
|
|
7371
8371
|
return this._awaitHumanImpl(options);
|
|
7372
8372
|
}
|
|
7373
8373
|
async _awaitHumanImpl(options) {
|
|
8374
|
+
const branchSignal = this.currentSignal;
|
|
8375
|
+
const makeAbortError = (reason) => {
|
|
8376
|
+
if (typeof DOMException !== "undefined") {
|
|
8377
|
+
return new DOMException(
|
|
8378
|
+
typeof reason === "string" ? reason : "awaitHuman aborted",
|
|
8379
|
+
"AbortError"
|
|
8380
|
+
);
|
|
8381
|
+
}
|
|
8382
|
+
const err = new Error(typeof reason === "string" ? reason : "awaitHuman aborted");
|
|
8383
|
+
err.name = "AbortError";
|
|
8384
|
+
return err;
|
|
8385
|
+
};
|
|
8386
|
+
const awaitWithAbort = async (promise, signal2 = branchSignal) => {
|
|
8387
|
+
if (!signal2) return promise;
|
|
8388
|
+
if (signal2.aborted) throw makeAbortError(signal2.reason);
|
|
8389
|
+
let onAbort;
|
|
8390
|
+
try {
|
|
8391
|
+
return await Promise.race([
|
|
8392
|
+
promise,
|
|
8393
|
+
new Promise((_, reject) => {
|
|
8394
|
+
onAbort = () => reject(makeAbortError(signal2.reason));
|
|
8395
|
+
signal2.addEventListener("abort", onAbort, { once: true });
|
|
8396
|
+
})
|
|
8397
|
+
]);
|
|
8398
|
+
} finally {
|
|
8399
|
+
if (onAbort) signal2.removeEventListener("abort", onAbort);
|
|
8400
|
+
}
|
|
8401
|
+
};
|
|
7374
8402
|
if (this.awaitHumanHandler) {
|
|
8403
|
+
if (branchSignal?.aborted) throw makeAbortError(branchSignal.reason);
|
|
7375
8404
|
this.emitEvent({
|
|
7376
8405
|
type: "await_human",
|
|
7377
8406
|
data: { channel: options.channel, prompt: options.prompt }
|
|
7378
8407
|
});
|
|
7379
|
-
|
|
8408
|
+
if (branchSignal?.aborted) throw makeAbortError(branchSignal.reason);
|
|
8409
|
+
const decision2 = parseHumanDecision(
|
|
8410
|
+
await awaitWithAbort(Promise.resolve(this.awaitHumanHandler(options)))
|
|
8411
|
+
);
|
|
7380
8412
|
this.emitEvent({
|
|
7381
8413
|
type: "await_human_resolved",
|
|
7382
8414
|
data: { channel: options.channel, decision: decision2 }
|
|
@@ -7388,68 +8420,126 @@ ${summaryResponse.content}`
|
|
|
7388
8420
|
"Tool requires approval but no approval handler is configured. Provide awaitHumanHandler to createContext() or use runtime.execute() with workflow infrastructure."
|
|
7389
8421
|
);
|
|
7390
8422
|
}
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
awaitHumanPrompt: options.prompt
|
|
7408
|
-
}
|
|
7409
|
-
});
|
|
7410
|
-
}
|
|
7411
|
-
this.emitEvent({
|
|
7412
|
-
type: "await_human",
|
|
7413
|
-
data: { channel: options.channel, prompt: options.prompt }
|
|
8423
|
+
const existingClaim = this.awaitHumanClaims.get(this.executionId);
|
|
8424
|
+
if (existingClaim) {
|
|
8425
|
+
const error = new AxlError(
|
|
8426
|
+
"CONCURRENT_HUMAN_DECISION_UNSUPPORTED",
|
|
8427
|
+
`Execution "${this.executionId}" already has an active human decision. Concurrent awaitHuman calls require request-scoped decision identifiers.`
|
|
8428
|
+
);
|
|
8429
|
+
existingClaim.abort(error);
|
|
8430
|
+
throw error;
|
|
8431
|
+
}
|
|
8432
|
+
const claimController = new AbortController();
|
|
8433
|
+
this.awaitHumanClaims.set(this.executionId, claimController);
|
|
8434
|
+
const signal = branchSignal ? AbortSignal.any([branchSignal, claimController.signal]) : claimController.signal;
|
|
8435
|
+
let saveAttempted = false;
|
|
8436
|
+
let markReady;
|
|
8437
|
+
const ready = new Promise((resolve2) => {
|
|
8438
|
+
markReady = resolve2;
|
|
7414
8439
|
});
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
8440
|
+
let markCleanupFinished;
|
|
8441
|
+
const cleanupFinished = new Promise((resolve2) => {
|
|
8442
|
+
markCleanupFinished = resolve2;
|
|
8443
|
+
});
|
|
8444
|
+
let resolveDecision;
|
|
8445
|
+
let rejectDecision;
|
|
8446
|
+
const decisionPromise = new Promise((resolve2, reject) => {
|
|
8447
|
+
resolveDecision = resolve2;
|
|
8448
|
+
rejectDecision = reject;
|
|
8449
|
+
});
|
|
8450
|
+
void decisionPromise.catch(() => {
|
|
8451
|
+
});
|
|
8452
|
+
const registeredResolver = {
|
|
8453
|
+
resolve: resolveDecision,
|
|
8454
|
+
ready,
|
|
8455
|
+
state: "preparing",
|
|
8456
|
+
cancelled: false,
|
|
8457
|
+
cleanupFinished
|
|
7425
8458
|
};
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
8459
|
+
this.pendingDecisions.set(this.executionId, registeredResolver);
|
|
8460
|
+
const abortListener = () => {
|
|
8461
|
+
registeredResolver.cancelled = true;
|
|
8462
|
+
markReady();
|
|
8463
|
+
rejectDecision(makeAbortError(signal.reason));
|
|
8464
|
+
};
|
|
8465
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
8466
|
+
let decision;
|
|
8467
|
+
try {
|
|
8468
|
+
if (signal.aborted) throw makeAbortError(signal.reason);
|
|
8469
|
+
if (this.stateStore) {
|
|
8470
|
+
saveAttempted = true;
|
|
8471
|
+
await this.stateStore.savePendingDecision(this.executionId, {
|
|
8472
|
+
executionId: this.executionId,
|
|
8473
|
+
channel: options.channel,
|
|
8474
|
+
prompt: options.prompt,
|
|
8475
|
+
metadata: options.metadata,
|
|
8476
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8477
|
+
});
|
|
7430
8478
|
}
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
8479
|
+
if (signal.aborted) throw makeAbortError(signal.reason);
|
|
8480
|
+
this.emitEvent({
|
|
8481
|
+
type: "await_human",
|
|
8482
|
+
data: { channel: options.channel, prompt: options.prompt }
|
|
8483
|
+
});
|
|
8484
|
+
registeredResolver.state = "pending";
|
|
8485
|
+
markReady();
|
|
8486
|
+
decision = await decisionPromise;
|
|
8487
|
+
} catch (error) {
|
|
8488
|
+
if (registeredResolver.state === "preparing") {
|
|
8489
|
+
registeredResolver.state = "failed";
|
|
8490
|
+
markReady();
|
|
8491
|
+
}
|
|
8492
|
+
let needsCompensation = saveAttempted;
|
|
8493
|
+
const inFlightResolution = registeredResolver.resolution;
|
|
8494
|
+
if (registeredResolver.state === "resolving" && inFlightResolution) {
|
|
8495
|
+
needsCompensation = !await inFlightResolution;
|
|
8496
|
+
} else if (registeredResolver.state === "resolved") {
|
|
8497
|
+
needsCompensation = false;
|
|
8498
|
+
}
|
|
8499
|
+
if (this.stateStore && needsCompensation) {
|
|
8500
|
+
try {
|
|
8501
|
+
await this.stateStore.resolveDecision(this.executionId, {
|
|
8502
|
+
approved: false,
|
|
8503
|
+
reason: "Execution aborted while awaiting approval"
|
|
8504
|
+
});
|
|
8505
|
+
} catch (cleanupError) {
|
|
8506
|
+
if (error instanceof Error && Object.isExtensible(error)) {
|
|
8507
|
+
Object.defineProperty(error, "cleanupError", {
|
|
8508
|
+
configurable: true,
|
|
8509
|
+
enumerable: false,
|
|
8510
|
+
value: cleanupError
|
|
8511
|
+
});
|
|
8512
|
+
}
|
|
8513
|
+
try {
|
|
8514
|
+
this._onDecisionCleanupFailed?.({
|
|
8515
|
+
executionId: this.executionId,
|
|
8516
|
+
...this.workflowName ? { workflow: this.workflowName } : {},
|
|
8517
|
+
operation: "resolveDecision_compensation",
|
|
8518
|
+
error: cleanupError
|
|
8519
|
+
});
|
|
8520
|
+
} catch (observerError) {
|
|
8521
|
+
console.error(
|
|
8522
|
+
"[axl] decision_cleanup_failed listener threw; workflow outcome unchanged:",
|
|
8523
|
+
observerError instanceof Error ? observerError.message : String(observerError)
|
|
8524
|
+
);
|
|
8525
|
+
}
|
|
7436
8526
|
}
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
8527
|
+
}
|
|
8528
|
+
throw error;
|
|
8529
|
+
} finally {
|
|
8530
|
+
signal.removeEventListener("abort", abortListener);
|
|
8531
|
+
if (this.pendingDecisions.get(this.executionId) === registeredResolver) {
|
|
8532
|
+
this.pendingDecisions.delete(this.executionId);
|
|
8533
|
+
}
|
|
8534
|
+
if (this.awaitHumanClaims.get(this.executionId) === claimController) {
|
|
8535
|
+
this.awaitHumanClaims.delete(this.executionId);
|
|
8536
|
+
}
|
|
8537
|
+
markCleanupFinished();
|
|
8538
|
+
}
|
|
7441
8539
|
this.emitEvent({
|
|
7442
8540
|
type: "await_human_resolved",
|
|
7443
8541
|
data: { channel: options.channel, decision }
|
|
7444
8542
|
});
|
|
7445
|
-
if (this.stateStore) {
|
|
7446
|
-
await this.stateStore.saveExecutionState(this.executionId, {
|
|
7447
|
-
workflow: this.workflowName ?? "unknown",
|
|
7448
|
-
input: this.input,
|
|
7449
|
-
step: this.stepRefRoot.value,
|
|
7450
|
-
status: "running"
|
|
7451
|
-
});
|
|
7452
|
-
}
|
|
7453
8543
|
return decision;
|
|
7454
8544
|
}
|
|
7455
8545
|
/**
|
|
@@ -7501,8 +8591,8 @@ ${summaryResponse.content}`
|
|
|
7501
8591
|
// Internal: the runtime is the only caller, user workflows never call these.
|
|
7502
8592
|
/** @internal — idempotent; no-ops on second+ call within one ctx. */
|
|
7503
8593
|
_emitWorkflowStart(input) {
|
|
7504
|
-
if (this.
|
|
7505
|
-
this.
|
|
8594
|
+
if (this.workflowLifecycleState.startEmitted) return;
|
|
8595
|
+
this.workflowLifecycleState.startEmitted = true;
|
|
7506
8596
|
this.emitEvent({
|
|
7507
8597
|
type: "workflow_start",
|
|
7508
8598
|
workflow: this.workflowName,
|
|
@@ -7518,8 +8608,8 @@ ${summaryResponse.content}`
|
|
|
7518
8608
|
* status. First-wins semantics: the completed event stands, the
|
|
7519
8609
|
* inner side-effect failure propagates as a normal thrown error. */
|
|
7520
8610
|
_emitWorkflowEnd(info) {
|
|
7521
|
-
if (this.
|
|
7522
|
-
this.
|
|
8611
|
+
if (this.workflowLifecycleState.endEmitted) return;
|
|
8612
|
+
this.workflowLifecycleState.endEmitted = true;
|
|
7523
8613
|
this.emitEvent({
|
|
7524
8614
|
type: "workflow_end",
|
|
7525
8615
|
workflow: this.workflowName,
|
|
@@ -7529,7 +8619,8 @@ ${summaryResponse.content}`
|
|
|
7529
8619
|
duration: info.duration,
|
|
7530
8620
|
...info.result !== void 0 ? { result: info.result } : {},
|
|
7531
8621
|
...info.error !== void 0 ? { error: info.error } : {},
|
|
7532
|
-
...info.aborted ? { aborted: true } : {}
|
|
8622
|
+
...info.aborted ? { aborted: true } : {},
|
|
8623
|
+
...info.observation ? { observation: info.observation } : {}
|
|
7533
8624
|
}
|
|
7534
8625
|
});
|
|
7535
8626
|
}
|
|
@@ -7818,6 +8909,7 @@ ${summaryResponse.content}`
|
|
|
7818
8909
|
* consumer sites via the `type` discriminator.
|
|
7819
8910
|
*/
|
|
7820
8911
|
emitEvent(partial) {
|
|
8912
|
+
if (this.workflowLifecycleState.endEmitted && partial.type !== "workflow_end") return;
|
|
7821
8913
|
const data = partial.data;
|
|
7822
8914
|
const frame = askStorage.getStore();
|
|
7823
8915
|
const step = (frame?.stepRef ?? this.stepRefRoot).value++;
|
|
@@ -7843,6 +8935,7 @@ ${summaryResponse.content}`
|
|
|
7843
8935
|
}
|
|
7844
8936
|
}
|
|
7845
8937
|
const event = {
|
|
8938
|
+
schemaVersion: 2,
|
|
7846
8939
|
executionId: this.executionId,
|
|
7847
8940
|
step,
|
|
7848
8941
|
timestamp: Date.now(),
|
|
@@ -7863,13 +8956,13 @@ ${summaryResponse.content}`
|
|
|
7863
8956
|
...partial,
|
|
7864
8957
|
data
|
|
7865
8958
|
};
|
|
7866
|
-
const finalEvent = this.config.trace?.redact ?
|
|
8959
|
+
const finalEvent = this.config.trace?.redact ? redactHistoricalEvent(event) : event;
|
|
7867
8960
|
let overflowErr;
|
|
7868
8961
|
if (this.onTrace) {
|
|
7869
8962
|
try {
|
|
7870
8963
|
this.onTrace(finalEvent);
|
|
7871
8964
|
} catch (err) {
|
|
7872
|
-
if (err
|
|
8965
|
+
if (isEventStreamOverflowError(err)) {
|
|
7873
8966
|
overflowErr = err;
|
|
7874
8967
|
} else {
|
|
7875
8968
|
console.error(
|
|
@@ -7884,7 +8977,7 @@ ${summaryResponse.content}`
|
|
|
7884
8977
|
try {
|
|
7885
8978
|
bus._push(finalEvent);
|
|
7886
8979
|
} catch (err) {
|
|
7887
|
-
if (err
|
|
8980
|
+
if (isEventStreamOverflowError(err)) {
|
|
7888
8981
|
if (!overflowErr) {
|
|
7889
8982
|
overflowErr = err;
|
|
7890
8983
|
} else {
|
|
@@ -8071,26 +9164,31 @@ var MemoryStore = class {
|
|
|
8071
9164
|
}
|
|
8072
9165
|
// ── Execution History ──────────────────────────────────────────────
|
|
8073
9166
|
async saveExecution(execution) {
|
|
8074
|
-
|
|
9167
|
+
const normalized = normalizeStoredExecution({
|
|
9168
|
+
...execution,
|
|
9169
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
9170
|
+
});
|
|
9171
|
+
this.executionHistory.set(execution.executionId, structuredClone(normalized));
|
|
8075
9172
|
}
|
|
8076
9173
|
async getExecution(executionId) {
|
|
8077
9174
|
const exec = this.executionHistory.get(executionId);
|
|
8078
|
-
return exec ? structuredClone(exec) : null;
|
|
9175
|
+
return exec ? normalizeStoredExecution(structuredClone(exec)) : null;
|
|
8079
9176
|
}
|
|
8080
9177
|
async listExecutions(limit) {
|
|
8081
9178
|
const sorted = [...this.executionHistory.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
8082
9179
|
const result = limit ? sorted.slice(0, limit) : sorted;
|
|
8083
|
-
return result.map((e) => structuredClone(e));
|
|
9180
|
+
return result.map((e) => normalizeStoredExecution(structuredClone(e)));
|
|
8084
9181
|
}
|
|
8085
9182
|
async deleteExecution(executionId) {
|
|
8086
|
-
const
|
|
8087
|
-
this.checkpoints.delete(executionId);
|
|
8088
|
-
this.executionStates.delete(executionId);
|
|
8089
|
-
this.streamingEvents.delete(executionId);
|
|
8090
|
-
|
|
9183
|
+
const removedHistory = this.executionHistory.delete(executionId);
|
|
9184
|
+
const removedCheckpoints = this.checkpoints.delete(executionId);
|
|
9185
|
+
const removedState = this.executionStates.delete(executionId);
|
|
9186
|
+
const removedStreaming = this.streamingEvents.delete(executionId);
|
|
9187
|
+
const removedDecision = this.decisions.delete(executionId);
|
|
9188
|
+
if (removedDecision) {
|
|
8091
9189
|
this.persistAwaitHumanState();
|
|
8092
9190
|
}
|
|
8093
|
-
return
|
|
9191
|
+
return removedHistory || removedCheckpoints || removedState || removedStreaming || removedDecision;
|
|
8094
9192
|
}
|
|
8095
9193
|
// ── Eval History ──────────────────────────────────────────────────
|
|
8096
9194
|
async saveEvalResult(entry) {
|
|
@@ -8196,16 +9294,23 @@ function rowToExecutionInfo(row) {
|
|
|
8196
9294
|
startedAt: row.started_at,
|
|
8197
9295
|
completedAt: row.completed_at ?? void 0,
|
|
8198
9296
|
duration: row.duration,
|
|
8199
|
-
|
|
8200
|
-
|
|
9297
|
+
events: safeJsonParse(row.events) ?? [],
|
|
9298
|
+
...row.event_schema_version != null ? { eventSchemaVersion: row.event_schema_version } : {}
|
|
8201
9299
|
};
|
|
9300
|
+
if (row.error != null) info.error = row.error;
|
|
8202
9301
|
if (row.metadata != null) {
|
|
8203
9302
|
const parsed = safeJsonParse(row.metadata);
|
|
8204
9303
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
8205
9304
|
info.metadata = parsed;
|
|
8206
9305
|
}
|
|
8207
9306
|
}
|
|
8208
|
-
|
|
9307
|
+
if (row.observation != null) {
|
|
9308
|
+
const parsed = safeJsonParse(row.observation);
|
|
9309
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
9310
|
+
info.observation = parsed;
|
|
9311
|
+
}
|
|
9312
|
+
}
|
|
9313
|
+
return normalizeStoredExecution(info);
|
|
8209
9314
|
}
|
|
8210
9315
|
var SQLiteStore = class {
|
|
8211
9316
|
db;
|
|
@@ -8245,6 +9350,10 @@ var SQLiteStore = class {
|
|
|
8245
9350
|
* column (TEXT, nullable, JSON-serialized) so caller-supplied
|
|
8246
9351
|
* `ExecuteOptions.metadata` round-trips through history. Existing
|
|
8247
9352
|
* rows get `NULL`, deserialized as `metadata: undefined`.
|
|
9353
|
+
* - v4 (event schema): add `execution_history.event_schema_version`.
|
|
9354
|
+
* Existing rows remain `NULL`, the documented v1 sentinel.
|
|
9355
|
+
* - v5 (observation completeness): add `execution_history.observation`.
|
|
9356
|
+
* Existing rows remain `NULL`, meaning they predate the contract.
|
|
8248
9357
|
*
|
|
8249
9358
|
* Applied BEFORE `initTables()` so the subsequent `CREATE TABLE
|
|
8250
9359
|
* IF NOT EXISTS` runs against the post-migration column name. Fresh
|
|
@@ -8262,7 +9371,7 @@ var SQLiteStore = class {
|
|
|
8262
9371
|
* transactional re-read costs nothing.
|
|
8263
9372
|
*/
|
|
8264
9373
|
migrate() {
|
|
8265
|
-
const TARGET_VERSION =
|
|
9374
|
+
const TARGET_VERSION = 5;
|
|
8266
9375
|
const current = this.db.pragma("user_version", { simple: true });
|
|
8267
9376
|
if (current >= TARGET_VERSION) return;
|
|
8268
9377
|
this.db.exec("BEGIN IMMEDIATE");
|
|
@@ -8290,6 +9399,18 @@ var SQLiteStore = class {
|
|
|
8290
9399
|
this.db.exec("ALTER TABLE execution_history ADD COLUMN metadata TEXT");
|
|
8291
9400
|
}
|
|
8292
9401
|
}
|
|
9402
|
+
if (committed < 4) {
|
|
9403
|
+
const cols = this.db.pragma("table_info(execution_history)");
|
|
9404
|
+
if (cols.length > 0 && !cols.some((c) => c.name === "event_schema_version")) {
|
|
9405
|
+
this.db.exec("ALTER TABLE execution_history ADD COLUMN event_schema_version INTEGER");
|
|
9406
|
+
}
|
|
9407
|
+
}
|
|
9408
|
+
if (committed < 5) {
|
|
9409
|
+
const cols = this.db.pragma("table_info(execution_history)");
|
|
9410
|
+
if (cols.length > 0 && !cols.some((c) => c.name === "observation")) {
|
|
9411
|
+
this.db.exec("ALTER TABLE execution_history ADD COLUMN observation TEXT");
|
|
9412
|
+
}
|
|
9413
|
+
}
|
|
8293
9414
|
this.db.pragma(`user_version = ${TARGET_VERSION}`);
|
|
8294
9415
|
this.db.exec("COMMIT");
|
|
8295
9416
|
} catch (err) {
|
|
@@ -8353,7 +9474,9 @@ var SQLiteStore = class {
|
|
|
8353
9474
|
duration INTEGER NOT NULL DEFAULT 0,
|
|
8354
9475
|
error TEXT,
|
|
8355
9476
|
events TEXT NOT NULL,
|
|
8356
|
-
metadata TEXT
|
|
9477
|
+
metadata TEXT,
|
|
9478
|
+
event_schema_version INTEGER,
|
|
9479
|
+
observation TEXT
|
|
8357
9480
|
);
|
|
8358
9481
|
|
|
8359
9482
|
CREATE TABLE IF NOT EXISTS eval_history (
|
|
@@ -8438,7 +9561,7 @@ var SQLiteStore = class {
|
|
|
8438
9561
|
const stmt = this.db.prepare("DELETE FROM decisions WHERE execution_id = ?");
|
|
8439
9562
|
stmt.run(executionId);
|
|
8440
9563
|
}
|
|
8441
|
-
// ──
|
|
9564
|
+
// ── Legacy application-managed execution state ────────────────────────
|
|
8442
9565
|
async saveExecutionState(executionId, state) {
|
|
8443
9566
|
const stmt = this.db.prepare(
|
|
8444
9567
|
"INSERT OR REPLACE INTO execution_state (execution_id, workflow, input, step, status, metadata) VALUES (?, ?, ?, ?, ?, ?)"
|
|
@@ -8473,19 +9596,25 @@ var SQLiteStore = class {
|
|
|
8473
9596
|
}
|
|
8474
9597
|
// ── Execution History ────────────────────────────────────────────────────
|
|
8475
9598
|
async saveExecution(execution) {
|
|
9599
|
+
const normalized = normalizeStoredExecution({
|
|
9600
|
+
...execution,
|
|
9601
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
9602
|
+
});
|
|
8476
9603
|
this.db.prepare(
|
|
8477
|
-
"INSERT OR REPLACE INTO execution_history (execution_id, workflow, status, total_cost, started_at, completed_at, duration, error, events, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
9604
|
+
"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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
8478
9605
|
).run(
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
JSON.stringify(
|
|
8488
|
-
|
|
9606
|
+
normalized.executionId,
|
|
9607
|
+
normalized.workflow,
|
|
9608
|
+
normalized.status,
|
|
9609
|
+
normalized.totalCost,
|
|
9610
|
+
normalized.startedAt,
|
|
9611
|
+
normalized.completedAt ?? null,
|
|
9612
|
+
normalized.duration,
|
|
9613
|
+
normalized.error ?? null,
|
|
9614
|
+
JSON.stringify(normalized.events),
|
|
9615
|
+
normalized.metadata !== void 0 ? JSON.stringify(normalized.metadata) : null,
|
|
9616
|
+
normalized.eventSchemaVersion,
|
|
9617
|
+
normalized.observation !== void 0 ? JSON.stringify(normalized.observation) : null
|
|
8489
9618
|
);
|
|
8490
9619
|
}
|
|
8491
9620
|
async getExecution(executionId) {
|
|
@@ -8499,11 +9628,11 @@ var SQLiteStore = class {
|
|
|
8499
9628
|
}
|
|
8500
9629
|
async deleteExecution(executionId) {
|
|
8501
9630
|
const tx = this.db.transaction(() => {
|
|
8502
|
-
const
|
|
8503
|
-
this.db.prepare("DELETE FROM checkpoints WHERE execution_id = ?").run(executionId);
|
|
8504
|
-
this.db.prepare("DELETE FROM execution_state WHERE execution_id = ?").run(executionId);
|
|
8505
|
-
this.db.prepare("DELETE FROM decisions WHERE execution_id = ?").run(executionId);
|
|
8506
|
-
return
|
|
9631
|
+
const historyResult = this.db.prepare("DELETE FROM execution_history WHERE execution_id = ?").run(executionId);
|
|
9632
|
+
const checkpointsResult = this.db.prepare("DELETE FROM checkpoints WHERE execution_id = ?").run(executionId);
|
|
9633
|
+
const stateResult = this.db.prepare("DELETE FROM execution_state WHERE execution_id = ?").run(executionId);
|
|
9634
|
+
const decisionsResult = this.db.prepare("DELETE FROM decisions WHERE execution_id = ?").run(executionId);
|
|
9635
|
+
return historyResult.changes + checkpointsResult.changes + stateResult.changes + decisionsResult.changes > 0;
|
|
8507
9636
|
});
|
|
8508
9637
|
return tx();
|
|
8509
9638
|
}
|
|
@@ -8829,6 +9958,11 @@ var AxlStream = class extends import_node_stream.Readable {
|
|
|
8829
9958
|
get lifecycle() {
|
|
8830
9959
|
return this.events.lifecycle;
|
|
8831
9960
|
}
|
|
9961
|
+
/** Completeness of this stream's iterator queue. Check after consumption
|
|
9962
|
+
* before treating an unmatched lifecycle event as a producer defect. */
|
|
9963
|
+
get observationStatus() {
|
|
9964
|
+
return this.events.observationStatus;
|
|
9965
|
+
}
|
|
8832
9966
|
/** Coalescing iterator over `partial_object` events — yields the latest
|
|
8833
9967
|
* object payload per `askId`. Designed for streaming-structured-output
|
|
8834
9968
|
* UIs: when the producer outpaces the consumer, intermediate snapshots
|
|
@@ -8927,6 +10061,7 @@ var AxlStream = class extends import_node_stream.Readable {
|
|
|
8927
10061
|
if (this.events.isFinished) return;
|
|
8928
10062
|
this.result = result;
|
|
8929
10063
|
const doneEvent = {
|
|
10064
|
+
schemaVersion: 2,
|
|
8930
10065
|
type: "done",
|
|
8931
10066
|
executionId,
|
|
8932
10067
|
step: Number.MAX_SAFE_INTEGER,
|
|
@@ -8945,6 +10080,7 @@ var AxlStream = class extends import_node_stream.Readable {
|
|
|
8945
10080
|
_error(error, executionId) {
|
|
8946
10081
|
if (this.events.isFinished) return;
|
|
8947
10082
|
const errorEvent = {
|
|
10083
|
+
schemaVersion: 2,
|
|
8948
10084
|
type: "error",
|
|
8949
10085
|
executionId,
|
|
8950
10086
|
step: Number.MAX_SAFE_INTEGER,
|
|
@@ -9545,11 +10681,7 @@ var STREAMING_EXCLUDED_TYPES = /* @__PURE__ */ new Set([
|
|
|
9545
10681
|
"partial_object",
|
|
9546
10682
|
"string_delta"
|
|
9547
10683
|
]);
|
|
9548
|
-
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
9549
|
-
"sessionId",
|
|
9550
|
-
"sessionHistory",
|
|
9551
|
-
"resumeMode"
|
|
9552
|
-
]);
|
|
10684
|
+
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["sessionId", "sessionHistory"]);
|
|
9553
10685
|
function liftPersistedMetadata(metadata) {
|
|
9554
10686
|
if (!metadata) return void 0;
|
|
9555
10687
|
const filtered = {};
|
|
@@ -9667,7 +10799,7 @@ var StreamingFlusher = class {
|
|
|
9667
10799
|
await Promise.allSettled(pendings);
|
|
9668
10800
|
}
|
|
9669
10801
|
};
|
|
9670
|
-
function
|
|
10802
|
+
function isAbortError4(err) {
|
|
9671
10803
|
return typeof err === "object" && err !== null && err.name === "AbortError";
|
|
9672
10804
|
}
|
|
9673
10805
|
function pushEventBounded(execInfo, event, cap) {
|
|
@@ -9679,7 +10811,18 @@ function pushEventBounded(execInfo, event, cap) {
|
|
|
9679
10811
|
const last = execInfo.events[execInfo.events.length - 1];
|
|
9680
10812
|
const alreadyTruncated = last?.type === "log" && typeof last.data === "object" && last.data !== null && last.data.event === "events_truncated";
|
|
9681
10813
|
if (!alreadyTruncated) {
|
|
10814
|
+
if (execInfo.observation?.complete !== false) {
|
|
10815
|
+
execInfo.observation = {
|
|
10816
|
+
complete: false,
|
|
10817
|
+
reason: "persistence_truncated",
|
|
10818
|
+
maxEvents: cap
|
|
10819
|
+
};
|
|
10820
|
+
}
|
|
10821
|
+
if (event.type === "workflow_end") {
|
|
10822
|
+
event.data.observation = execInfo.observation;
|
|
10823
|
+
}
|
|
9682
10824
|
execInfo.events[execInfo.events.length - 1] = {
|
|
10825
|
+
schemaVersion: 2,
|
|
9683
10826
|
type: "log",
|
|
9684
10827
|
executionId: event.executionId,
|
|
9685
10828
|
step: event.step,
|
|
@@ -9708,7 +10851,9 @@ function boundRecoveredEvents(events, cap) {
|
|
|
9708
10851
|
const last = truncated[truncated.length - 1];
|
|
9709
10852
|
const alreadyTruncated = last?.type === "log" && typeof last.data === "object" && last.data !== null && last.data.event === "events_truncated";
|
|
9710
10853
|
if (!alreadyTruncated) {
|
|
10854
|
+
const version = getEventSchemaVersion(events[0] ?? {});
|
|
9711
10855
|
truncated[truncated.length - 1] = {
|
|
10856
|
+
...version === 2 ? { schemaVersion: 2 } : {},
|
|
9712
10857
|
type: "log",
|
|
9713
10858
|
executionId: events[0]?.executionId ?? "",
|
|
9714
10859
|
step: last?.step ?? 0,
|
|
@@ -9722,6 +10867,16 @@ function boundRecoveredEvents(events, cap) {
|
|
|
9722
10867
|
}
|
|
9723
10868
|
return truncated;
|
|
9724
10869
|
}
|
|
10870
|
+
var REMOVED_OBSERVATION_CALLBACKS = ["onToken", "onToolCall", "onAgentStart"];
|
|
10871
|
+
function assertNoRemovedObservationCallbacks(options) {
|
|
10872
|
+
if (options === null || typeof options !== "object") return;
|
|
10873
|
+
const removed = REMOVED_OBSERVATION_CALLBACKS.filter((key) => Object.hasOwn(options, key));
|
|
10874
|
+
if (removed.length === 0) return;
|
|
10875
|
+
throw new AxlError(
|
|
10876
|
+
"INVALID_CONFIG",
|
|
10877
|
+
`runtime.createContext() no longer accepts ${removed.join(", ")}. Observe ctx.events or use runtime.stream() instead. See docs/migration/stream-first-observation.md.`
|
|
10878
|
+
);
|
|
10879
|
+
}
|
|
9725
10880
|
var costScopeStorage = new import_node_async_hooks2.AsyncLocalStorage();
|
|
9726
10881
|
function forwardAbortSignal(external, internal) {
|
|
9727
10882
|
if (!external) return () => {
|
|
@@ -9883,9 +11038,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9883
11038
|
* → parse output → emit `workflow_end` → delete checkpoints → persist.
|
|
9884
11039
|
*
|
|
9885
11040
|
* Shared between `execute()` and `stream()`. Centralizes the
|
|
9886
|
-
* "start iff end" pairing invariant
|
|
9887
|
-
* (e.g., `resumeExecution`) can inherit correct behavior by routing
|
|
9888
|
-
* through this helper. Also factors out the AbortError detection.
|
|
11041
|
+
* "start iff end" pairing invariant and AbortError detection.
|
|
9889
11042
|
*
|
|
9890
11043
|
* Side effects on `execInfo`: sets `status` / `completedAt` /
|
|
9891
11044
|
* `duration` / `result` (or `error`) before emitting `workflow_end`.
|
|
@@ -9909,18 +11062,23 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9909
11062
|
*/
|
|
9910
11063
|
async runWorkflowBody(args) {
|
|
9911
11064
|
const { workflow: workflow2, ctx, execInfo, validated, span, cleanupAbortForwarder } = args;
|
|
11065
|
+
let observation = execInfo.observation ?? { complete: true };
|
|
9912
11066
|
try {
|
|
9913
11067
|
ctx._emitWorkflowStart(validated);
|
|
9914
11068
|
const raw = await workflow2.handler(ctx);
|
|
11069
|
+
const drainedObservation = await ctx._drainBranchWork();
|
|
11070
|
+
observation = drainedObservation.complete === false ? drainedObservation : execInfo.observation ?? drainedObservation;
|
|
9915
11071
|
const result = workflow2.outputSchema ? workflow2.outputSchema.parse(raw) : raw;
|
|
9916
11072
|
execInfo.status = "completed";
|
|
9917
11073
|
execInfo.completedAt = Date.now();
|
|
9918
11074
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
9919
11075
|
execInfo.result = result;
|
|
11076
|
+
execInfo.observation = observation;
|
|
9920
11077
|
ctx._emitWorkflowEnd({
|
|
9921
11078
|
status: "completed",
|
|
9922
11079
|
duration: execInfo.duration,
|
|
9923
|
-
result
|
|
11080
|
+
result,
|
|
11081
|
+
observation
|
|
9924
11082
|
});
|
|
9925
11083
|
if (this.stateStore.deleteCheckpoints) {
|
|
9926
11084
|
await this.stateStore.deleteCheckpoints(execInfo.executionId);
|
|
@@ -9930,19 +11088,28 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9930
11088
|
this.persistExecution(execInfo);
|
|
9931
11089
|
return result;
|
|
9932
11090
|
} catch (err) {
|
|
9933
|
-
|
|
11091
|
+
let terminalError = err;
|
|
11092
|
+
try {
|
|
11093
|
+
const drainedObservation = await ctx._drainBranchWork();
|
|
11094
|
+
observation = drainedObservation.complete === false ? drainedObservation : execInfo.observation ?? drainedObservation;
|
|
11095
|
+
} catch (drainErr) {
|
|
11096
|
+
terminalError = drainErr instanceof Error ? preserveErrorCause(drainErr, terminalError) : drainErr;
|
|
11097
|
+
}
|
|
11098
|
+
const aborted = isAbortError4(terminalError);
|
|
9934
11099
|
execInfo.status = "failed";
|
|
9935
11100
|
execInfo.completedAt = Date.now();
|
|
9936
11101
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
9937
|
-
execInfo.error =
|
|
11102
|
+
execInfo.error = terminalError instanceof Error ? terminalError.message : String(terminalError);
|
|
11103
|
+
execInfo.observation = observation;
|
|
9938
11104
|
ctx._emitWorkflowEnd({
|
|
9939
11105
|
status: "failed",
|
|
9940
11106
|
duration: execInfo.duration,
|
|
9941
11107
|
error: execInfo.error,
|
|
9942
|
-
...aborted ? { aborted: true } : {}
|
|
11108
|
+
...aborted ? { aborted: true } : {},
|
|
11109
|
+
observation
|
|
9943
11110
|
});
|
|
9944
11111
|
this.persistExecution(execInfo);
|
|
9945
|
-
throw
|
|
11112
|
+
throw terminalError;
|
|
9946
11113
|
} finally {
|
|
9947
11114
|
this.abortControllers.delete(execInfo.executionId);
|
|
9948
11115
|
cleanupAbortForwarder?.();
|
|
@@ -10130,13 +11297,13 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10130
11297
|
return result;
|
|
10131
11298
|
}
|
|
10132
11299
|
/** Get all execution info (running + completed + historical). */
|
|
10133
|
-
/** Normalize an
|
|
10134
|
-
*
|
|
11300
|
+
/** Normalize an execution loaded from a `StateStore` so the
|
|
11301
|
+
* historical event union holds for downstream consumers
|
|
10135
11302
|
* (Studio aggregators, REST routes, redaction). Custom or legacy stores
|
|
10136
11303
|
* may persist rows without `events` (or with a non-array value) — without
|
|
10137
11304
|
* this guard, a single malformed row crashes every iterator over
|
|
10138
|
-
* `exec.events` and takes down dependent features.
|
|
10139
|
-
*
|
|
11305
|
+
* `exec.events` and takes down dependent features. Returns a normalized
|
|
11306
|
+
* copy; logs at most one warning per `executionId`. */
|
|
10140
11307
|
normalizeStoredExecution(exec) {
|
|
10141
11308
|
if (!Array.isArray(exec.events)) {
|
|
10142
11309
|
if (!this.warnedMalformedExecutions.has(exec.executionId)) {
|
|
@@ -10145,9 +11312,9 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10145
11312
|
`[axl] StateStore returned execution ${exec.executionId} with non-array events (got ${exec.events === null ? "null" : typeof exec.events}); coercing to []`
|
|
10146
11313
|
);
|
|
10147
11314
|
}
|
|
10148
|
-
exec
|
|
11315
|
+
return normalizeStoredExecution({ ...exec, events: [] });
|
|
10149
11316
|
}
|
|
10150
|
-
return exec;
|
|
11317
|
+
return normalizeStoredExecution(exec);
|
|
10151
11318
|
}
|
|
10152
11319
|
async getExecutions() {
|
|
10153
11320
|
if (!this.historicalExecutionsLoadPromise && this.stateStore.listExecutions) {
|
|
@@ -10157,8 +11324,9 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10157
11324
|
this.historicalExecutions.set(exec.executionId, this.normalizeStoredExecution(exec));
|
|
10158
11325
|
}
|
|
10159
11326
|
}
|
|
10160
|
-
}).catch(() => {
|
|
11327
|
+
}).catch((error) => {
|
|
10161
11328
|
this.historicalExecutionsLoadPromise = null;
|
|
11329
|
+
throw error;
|
|
10162
11330
|
});
|
|
10163
11331
|
}
|
|
10164
11332
|
if (this.historicalExecutionsLoadPromise) {
|
|
@@ -10176,6 +11344,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10176
11344
|
* and automatically emits trace events and tracks cost.
|
|
10177
11345
|
*/
|
|
10178
11346
|
createContext(options) {
|
|
11347
|
+
assertNoRemovedObservationCallbacks(options);
|
|
10179
11348
|
const executionId = (0, import_node_crypto3.randomUUID)();
|
|
10180
11349
|
const budgetLimit = options?.budget ? parseCost(options.budget) : Infinity;
|
|
10181
11350
|
this.registerWithCostScope(executionId);
|
|
@@ -10191,10 +11360,8 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10191
11360
|
memoryManager: this.memoryManager,
|
|
10192
11361
|
sessionHistory: options?.sessionHistory,
|
|
10193
11362
|
signal: options?.signal,
|
|
10194
|
-
onToken: options?.onToken,
|
|
10195
|
-
onToolCall: options?.onToolCall,
|
|
10196
|
-
onAgentStart: options?.onAgentStart,
|
|
10197
11363
|
awaitHumanHandler: options?.awaitHumanHandler,
|
|
11364
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
10198
11365
|
eventStreamOptions: options?.events,
|
|
10199
11366
|
onTrace: (event) => {
|
|
10200
11367
|
this.emit("trace", event);
|
|
@@ -10228,6 +11395,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10228
11395
|
);
|
|
10229
11396
|
}
|
|
10230
11397
|
const validated = workflow2.inputSchema.parse(input);
|
|
11398
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(options?.branchDrainTimeoutMs);
|
|
10231
11399
|
const executionId = (0, import_node_crypto3.randomUUID)();
|
|
10232
11400
|
const controller = new AbortController();
|
|
10233
11401
|
this.abortControllers.set(executionId, controller);
|
|
@@ -10240,11 +11408,13 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10240
11408
|
executionId,
|
|
10241
11409
|
workflow: name,
|
|
10242
11410
|
status: "running",
|
|
11411
|
+
eventSchemaVersion: 2,
|
|
10243
11412
|
events: [],
|
|
10244
11413
|
totalCost: 0,
|
|
10245
11414
|
unpriced: false,
|
|
10246
11415
|
startedAt: Date.now(),
|
|
10247
11416
|
duration: 0,
|
|
11417
|
+
observation: { complete: true },
|
|
10248
11418
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
10249
11419
|
};
|
|
10250
11420
|
this.executions.set(executionId, execInfo);
|
|
@@ -10258,6 +11428,8 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10258
11428
|
sessionHistory,
|
|
10259
11429
|
signal: controller.signal,
|
|
10260
11430
|
eventStreamOptions: options?.events,
|
|
11431
|
+
branchDrainTimeoutMs,
|
|
11432
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
10261
11433
|
onTrace: (event) => {
|
|
10262
11434
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
10263
11435
|
execInfo.totalCost += eventCostContribution(event);
|
|
@@ -10302,7 +11474,6 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10302
11474
|
workflowName: name,
|
|
10303
11475
|
mcpManager: this.mcpManager,
|
|
10304
11476
|
memoryManager: this.memoryManager,
|
|
10305
|
-
resumeMode: !!options?.metadata?.resumeMode,
|
|
10306
11477
|
spanManager: this.spanManager,
|
|
10307
11478
|
budgetContext: {
|
|
10308
11479
|
totalCost: 0,
|
|
@@ -10333,6 +11504,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10333
11504
|
}
|
|
10334
11505
|
/** Execute a workflow and return a stream. */
|
|
10335
11506
|
stream(name, input, options) {
|
|
11507
|
+
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(options?.branchDrainTimeoutMs);
|
|
10336
11508
|
const axlStream = new AxlStream(options?.events);
|
|
10337
11509
|
const controller = new AbortController();
|
|
10338
11510
|
axlStream.on("close", () => controller.abort());
|
|
@@ -10353,11 +11525,13 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10353
11525
|
executionId,
|
|
10354
11526
|
workflow: name,
|
|
10355
11527
|
status: "running",
|
|
11528
|
+
eventSchemaVersion: 2,
|
|
10356
11529
|
events: [],
|
|
10357
11530
|
totalCost: 0,
|
|
10358
11531
|
unpriced: false,
|
|
10359
11532
|
startedAt: Date.now(),
|
|
10360
11533
|
duration: 0,
|
|
11534
|
+
observation: { complete: true },
|
|
10361
11535
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
10362
11536
|
};
|
|
10363
11537
|
this.executions.set(executionId, execInfo);
|
|
@@ -10370,12 +11544,11 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10370
11544
|
sessionHistory,
|
|
10371
11545
|
signal: controller.signal,
|
|
10372
11546
|
eventStreamOptions: options?.events,
|
|
10373
|
-
|
|
10374
|
-
|
|
10375
|
-
//
|
|
10376
|
-
//
|
|
10377
|
-
|
|
10378
|
-
},
|
|
11547
|
+
branchDrainTimeoutMs,
|
|
11548
|
+
_onDecisionCleanupFailed: (event) => this.emit("decision_cleanup_failed", event),
|
|
11549
|
+
// Transport mode is explicit internal state. It is inherited by child
|
|
11550
|
+
// contexts and does not allocate an unconsumed ctx.events queue.
|
|
11551
|
+
_forceStreaming: true,
|
|
10379
11552
|
onTrace: (event) => {
|
|
10380
11553
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
10381
11554
|
execInfo.totalCost += eventCostContribution(event);
|
|
@@ -10421,7 +11594,6 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10421
11594
|
workflowName: name,
|
|
10422
11595
|
mcpManager: this.mcpManager,
|
|
10423
11596
|
memoryManager: this.memoryManager,
|
|
10424
|
-
resumeMode: !!options?.metadata?.resumeMode,
|
|
10425
11597
|
spanManager: this.spanManager,
|
|
10426
11598
|
budgetContext: {
|
|
10427
11599
|
totalCost: 0,
|
|
@@ -10585,6 +11757,12 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10585
11757
|
await this.stateStore.finalizeStreamingEvents?.(id);
|
|
10586
11758
|
continue;
|
|
10587
11759
|
}
|
|
11760
|
+
const eventSchemaVersion = getEventSchemaVersion(events[0]);
|
|
11761
|
+
for (const event of events) {
|
|
11762
|
+
if (getEventSchemaVersion(event) !== eventSchemaVersion) {
|
|
11763
|
+
throw new Error(`Streaming buffer ${id} contains mixed event schema versions`);
|
|
11764
|
+
}
|
|
11765
|
+
}
|
|
10588
11766
|
const boundedEvents = boundRecoveredEvents(events, this.maxEventsPerExecution);
|
|
10589
11767
|
const workflowStartEvent = boundedEvents.find((e) => e.type === "workflow_start");
|
|
10590
11768
|
const lastEvent = boundedEvents[boundedEvents.length - 1];
|
|
@@ -10593,7 +11771,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10593
11771
|
const workflowName = workflowStartEvent?.workflow ?? RECOVERED_UNKNOWN_WORKFLOW;
|
|
10594
11772
|
const totalCost = boundedEvents.reduce((sum, e) => sum + eventCostContribution(e), 0);
|
|
10595
11773
|
const unpriced = boundedEvents.some(isUnpricedLeaf);
|
|
10596
|
-
const
|
|
11774
|
+
const executionBase = {
|
|
10597
11775
|
executionId: id,
|
|
10598
11776
|
workflow: workflowName,
|
|
10599
11777
|
status: "failed",
|
|
@@ -10605,6 +11783,16 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10605
11783
|
duration: completedAt - startedAt,
|
|
10606
11784
|
error: "process terminated (recovered from streaming buffer)"
|
|
10607
11785
|
};
|
|
11786
|
+
const synthesized = eventSchemaVersion === 2 ? {
|
|
11787
|
+
...executionBase,
|
|
11788
|
+
eventSchemaVersion: 2,
|
|
11789
|
+
events: boundedEvents,
|
|
11790
|
+
observation: { complete: false, reason: "process_interrupted" }
|
|
11791
|
+
} : {
|
|
11792
|
+
...executionBase,
|
|
11793
|
+
eventSchemaVersion: 1,
|
|
11794
|
+
events: boundedEvents
|
|
11795
|
+
};
|
|
10608
11796
|
if (this.stateStore.saveExecution) {
|
|
10609
11797
|
try {
|
|
10610
11798
|
await this.stateStore.saveExecution(synthesized);
|
|
@@ -10685,7 +11873,8 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10685
11873
|
*/
|
|
10686
11874
|
async deleteExecution(id) {
|
|
10687
11875
|
const wasActive = this.executions.has(id);
|
|
10688
|
-
const
|
|
11876
|
+
const pendingDecisionResolver = this.pendingDecisionResolvers.get(id);
|
|
11877
|
+
const hadPendingDecision = pendingDecisionResolver !== void 0;
|
|
10689
11878
|
if (wasActive) {
|
|
10690
11879
|
this.pendingDeletedExecutions.add(id);
|
|
10691
11880
|
this.streamableExecutionIds.delete(id);
|
|
@@ -10694,8 +11883,12 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10694
11883
|
controller.abort();
|
|
10695
11884
|
}
|
|
10696
11885
|
}
|
|
10697
|
-
if (
|
|
10698
|
-
|
|
11886
|
+
if (pendingDecisionResolver) pendingDecisionResolver.cancelled = true;
|
|
11887
|
+
if (pendingDecisionResolver) {
|
|
11888
|
+
await pendingDecisionResolver.cleanupFinished;
|
|
11889
|
+
if (this.pendingDecisionResolvers.get(id) === pendingDecisionResolver) {
|
|
11890
|
+
this.pendingDecisionResolvers.delete(id);
|
|
11891
|
+
}
|
|
10699
11892
|
}
|
|
10700
11893
|
const existing = await this.getExecution(id);
|
|
10701
11894
|
const workflow2 = existing?.workflow;
|
|
@@ -10773,60 +11966,68 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
10773
11966
|
}
|
|
10774
11967
|
/** Resolve a pending human decision. */
|
|
10775
11968
|
async resolveDecision(executionId, decision) {
|
|
10776
|
-
|
|
11969
|
+
decision = parseHumanDecision(decision);
|
|
11970
|
+
let resolver = this.pendingDecisionResolvers.get(executionId);
|
|
10777
11971
|
if (resolver) {
|
|
10778
|
-
resolver
|
|
10779
|
-
|
|
11972
|
+
if (resolver.cancelled) {
|
|
11973
|
+
throw new AxlError(
|
|
11974
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11975
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
11976
|
+
);
|
|
11977
|
+
}
|
|
11978
|
+
await resolver.ready;
|
|
11979
|
+
if (resolver.cancelled || resolver.state !== "pending" || this.pendingDecisionResolvers.get(executionId) !== resolver) {
|
|
11980
|
+
throw new AxlError(
|
|
11981
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11982
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
11983
|
+
);
|
|
11984
|
+
}
|
|
10780
11985
|
}
|
|
10781
|
-
await this.stateStore.resolveDecision(executionId, decision);
|
|
10782
11986
|
if (!resolver) {
|
|
10783
|
-
const
|
|
10784
|
-
|
|
10785
|
-
|
|
11987
|
+
const pending = await this.stateStore.getPendingDecisions();
|
|
11988
|
+
resolver = this.pendingDecisionResolvers.get(executionId);
|
|
11989
|
+
if (resolver) {
|
|
11990
|
+
if (resolver.cancelled) {
|
|
11991
|
+
throw new AxlError(
|
|
11992
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
11993
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
11994
|
+
);
|
|
11995
|
+
}
|
|
11996
|
+
await resolver.ready;
|
|
11997
|
+
if (resolver.cancelled || resolver.state !== "pending" || this.pendingDecisionResolvers.get(executionId) !== resolver) {
|
|
11998
|
+
throw new AxlError(
|
|
11999
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
12000
|
+
`The pending human decision for execution "${executionId}" is no longer active.`
|
|
12001
|
+
);
|
|
12002
|
+
}
|
|
12003
|
+
} else if (!pending.some((request) => request.executionId === executionId)) {
|
|
12004
|
+
throw new AxlError(
|
|
12005
|
+
"PENDING_DECISION_NOT_FOUND",
|
|
12006
|
+
`No pending human decision exists for execution "${executionId}".`
|
|
12007
|
+
);
|
|
12008
|
+
} else {
|
|
12009
|
+
throw new AxlError(
|
|
12010
|
+
"CROSS_PROCESS_RESUME_UNSUPPORTED",
|
|
12011
|
+
`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.`
|
|
12012
|
+
);
|
|
10786
12013
|
}
|
|
10787
12014
|
}
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
* Re-runs the workflow from scratch; the workflow should use checkpoint-replay
|
|
10792
|
-
* to skip already-completed steps and pick up from the awaitHuman point.
|
|
10793
|
-
*/
|
|
10794
|
-
async resumeExecution(executionId) {
|
|
10795
|
-
const state = await this.stateStore.getExecutionState(executionId);
|
|
10796
|
-
if (!state) {
|
|
10797
|
-
throw new Error(`No execution state found for "${executionId}"`);
|
|
10798
|
-
}
|
|
10799
|
-
const workflow2 = this.workflows.get(state.workflow);
|
|
10800
|
-
if (!workflow2) {
|
|
10801
|
-
throw new Error(
|
|
10802
|
-
`Workflow "${state.workflow}" not registered. Cannot resume execution "${executionId}".`
|
|
10803
|
-
);
|
|
10804
|
-
}
|
|
10805
|
-
return this.execute(state.workflow, state.input, {
|
|
10806
|
-
metadata: { ...state.metadata, resumedFrom: executionId, resumeMode: true }
|
|
12015
|
+
let finishResolution;
|
|
12016
|
+
const resolutionFinished = new Promise((resolve2) => {
|
|
12017
|
+
finishResolution = resolve2;
|
|
10807
12018
|
});
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
await this.resumeExecution(executionId);
|
|
10820
|
-
resumed.push(executionId);
|
|
10821
|
-
} catch (err) {
|
|
10822
|
-
this.emit("error", {
|
|
10823
|
-
type: "resume_failed",
|
|
10824
|
-
executionId,
|
|
10825
|
-
error: err instanceof Error ? err.message : String(err)
|
|
10826
|
-
});
|
|
10827
|
-
}
|
|
12019
|
+
resolver.state = "resolving";
|
|
12020
|
+
resolver.resolution = resolutionFinished;
|
|
12021
|
+
try {
|
|
12022
|
+
await this.stateStore.resolveDecision(executionId, decision);
|
|
12023
|
+
resolver.state = "resolved";
|
|
12024
|
+
finishResolution(true);
|
|
12025
|
+
resolver.resolve(decision);
|
|
12026
|
+
} catch (error) {
|
|
12027
|
+
resolver.state = "pending";
|
|
12028
|
+
finishResolution(false);
|
|
12029
|
+
throw error;
|
|
10828
12030
|
}
|
|
10829
|
-
return resumed;
|
|
10830
12031
|
}
|
|
10831
12032
|
/**
|
|
10832
12033
|
* Summarize a list of chat messages into a concise summary string.
|
|
@@ -11728,28 +12929,32 @@ var RedisStore = class _RedisStore {
|
|
|
11728
12929
|
async saveExecution(execution) {
|
|
11729
12930
|
const ttl = this.ttlFor("executionHistory");
|
|
11730
12931
|
const setOptions = ttl !== void 0 ? { EX: ttl } : void 0;
|
|
11731
|
-
|
|
12932
|
+
const storedExecution = normalizeStoredExecution({
|
|
12933
|
+
...execution,
|
|
12934
|
+
eventSchemaVersion: getExecutionEventSchemaVersion(execution)
|
|
12935
|
+
});
|
|
12936
|
+
await this.client.multi().sAdd(this.execHistorySetKey(), execution.executionId).set(this.execHistoryKey(execution.executionId), JSON.stringify(storedExecution), setOptions).zAdd(this.execHistoryZsetKey(), {
|
|
11732
12937
|
score: execution.startedAt,
|
|
11733
12938
|
value: execution.executionId
|
|
11734
12939
|
}).exec();
|
|
11735
12940
|
}
|
|
11736
12941
|
async getExecution(executionId) {
|
|
11737
12942
|
const raw = await this.client.get(this.execHistoryKey(executionId));
|
|
11738
|
-
return raw ? JSON.parse(raw) : null;
|
|
12943
|
+
return raw ? normalizeStoredExecution(JSON.parse(raw)) : null;
|
|
11739
12944
|
}
|
|
11740
12945
|
async listExecutions(limit) {
|
|
11741
|
-
|
|
12946
|
+
const executions = await this.listHistoryByZset(
|
|
11742
12947
|
this.execHistorySetKey(),
|
|
11743
12948
|
this.execHistoryZsetKey(),
|
|
11744
12949
|
(id) => this.execHistoryKey(id),
|
|
11745
12950
|
(a, b) => b.startedAt - a.startedAt,
|
|
11746
12951
|
limit
|
|
11747
12952
|
);
|
|
12953
|
+
return executions.map(normalizeStoredExecution);
|
|
11748
12954
|
}
|
|
11749
12955
|
async deleteExecution(executionId) {
|
|
11750
|
-
const
|
|
11751
|
-
|
|
11752
|
-
return deleted > 0;
|
|
12956
|
+
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();
|
|
12957
|
+
return results.some((result) => typeof result === "number" && result > 0);
|
|
11753
12958
|
}
|
|
11754
12959
|
// ── Eval History ────────────────────────────────────────────────────
|
|
11755
12960
|
async saveEvalResult(entry) {
|
|
@@ -11841,14 +13046,15 @@ var RedisStore = class _RedisStore {
|
|
|
11841
13046
|
async getStreamingEvents(executionId) {
|
|
11842
13047
|
const raws = await this.client.lRange(this.streamingEventsKey(executionId), 0, -1);
|
|
11843
13048
|
if (raws.length === 0) return [];
|
|
11844
|
-
|
|
11845
|
-
for (const raw of raws) {
|
|
13049
|
+
return raws.map((raw, index) => {
|
|
11846
13050
|
try {
|
|
11847
|
-
|
|
11848
|
-
} catch {
|
|
13051
|
+
return JSON.parse(raw);
|
|
13052
|
+
} catch (error) {
|
|
13053
|
+
throw new Error(`Streaming buffer ${executionId} contains invalid JSON at index ${index}`, {
|
|
13054
|
+
cause: error
|
|
13055
|
+
});
|
|
11849
13056
|
}
|
|
11850
|
-
}
|
|
11851
|
-
return events;
|
|
13057
|
+
});
|
|
11852
13058
|
}
|
|
11853
13059
|
/** Close the Redis connection. */
|
|
11854
13060
|
async close() {
|
|
@@ -12060,6 +13266,8 @@ function cosineSimilarity2(a, b) {
|
|
|
12060
13266
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12061
13267
|
0 && (module.exports = {
|
|
12062
13268
|
AXL_EVENT_TYPES,
|
|
13269
|
+
AXL_EVENT_TYPES_V2,
|
|
13270
|
+
AXL_TOOL_LIFECYCLE_TYPES_V2,
|
|
12063
13271
|
AZURE_PROFILE,
|
|
12064
13272
|
AnthropicProvider,
|
|
12065
13273
|
AxlError,
|
|
@@ -12107,7 +13315,9 @@ function cosineSimilarity2(a, b) {
|
|
|
12107
13315
|
SqliteVectorStore,
|
|
12108
13316
|
ThinkTagScanner,
|
|
12109
13317
|
TimeoutError,
|
|
12110
|
-
|
|
13318
|
+
ToolFailure,
|
|
13319
|
+
ToolModelOutputError,
|
|
13320
|
+
UnsupportedEventSchemaVersionError,
|
|
12111
13321
|
VLLM_PROFILE,
|
|
12112
13322
|
ValidationError,
|
|
12113
13323
|
VerifyError,
|
|
@@ -12122,6 +13332,8 @@ function cosineSimilarity2(a, b) {
|
|
|
12122
13332
|
extractJson,
|
|
12123
13333
|
extractThinkTags,
|
|
12124
13334
|
findConfig,
|
|
13335
|
+
getEventSchemaVersion,
|
|
13336
|
+
getExecutionEventSchemaVersion,
|
|
12125
13337
|
hasPositiveTokens,
|
|
12126
13338
|
importModule,
|
|
12127
13339
|
isCostBearingLeaf,
|
|
@@ -12130,6 +13342,7 @@ function cosineSimilarity2(a, b) {
|
|
|
12130
13342
|
isUnpricedLeaf,
|
|
12131
13343
|
isUsableCost,
|
|
12132
13344
|
needsTsxLoader,
|
|
13345
|
+
normalizeStoredExecution,
|
|
12133
13346
|
openaiReasoningEmit,
|
|
12134
13347
|
parsePartialJson,
|
|
12135
13348
|
pickDefault,
|
|
@@ -12138,6 +13351,7 @@ function cosineSimilarity2(a, b) {
|
|
|
12138
13351
|
reasoningEffortEmit,
|
|
12139
13352
|
reasoningObjectEmit,
|
|
12140
13353
|
redactEvent,
|
|
13354
|
+
redactHistoricalEvent,
|
|
12141
13355
|
registerConditions,
|
|
12142
13356
|
resolveApiKey,
|
|
12143
13357
|
resolvePerModel,
|