@axlsdk/axl 0.7.6 → 0.9.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 +32 -7
- package/dist/index.cjs +167 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -11
- package/dist/index.d.ts +56 -11
- package/dist/index.js +167 -95
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -7,9 +7,11 @@ Core SDK for orchestrating agentic systems in TypeScript. Part of the [Axl](http
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @axlsdk/axl zod
|
|
10
|
+
npm install @axlsdk/axl zod@^4
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
> **Note:** `zod` is a peer dependency — your application and Axl share a single Zod instance. Zod v4 (`^4.0.0`) is required.
|
|
14
|
+
|
|
13
15
|
## Project Structure
|
|
14
16
|
|
|
15
17
|
The recommended pattern separates config, tools, agents, workflows, and runtime into their own modules. Dependencies flow one direction: tools → agents → workflows → runtime.
|
|
@@ -320,7 +322,7 @@ for await (const event of sessionStream) {
|
|
|
320
322
|
All available on `ctx` inside workflow handlers. See the [API Reference](../../docs/api-reference.md) for complete option types, valid values, and defaults.
|
|
321
323
|
|
|
322
324
|
```typescript
|
|
323
|
-
// Invoke an agent (schema retries
|
|
325
|
+
// Invoke an agent (schema/validate retries accumulate — LLM sees all previous failed attempts)
|
|
324
326
|
const answer = await ctx.ask(agent, 'prompt', { schema, retries });
|
|
325
327
|
|
|
326
328
|
// Run 3 agents in parallel — each gets the same question independently
|
|
@@ -329,11 +331,11 @@ const results = await ctx.spawn(3, async (i) => ctx.ask(agent, prompts[i]));
|
|
|
329
331
|
// Pick the answer that appeared most often — also supports LLM-as-judge via scorer
|
|
330
332
|
const winner = await ctx.vote(results, { strategy: 'majority', key: 'answer' });
|
|
331
333
|
|
|
332
|
-
//
|
|
334
|
+
// Retry-until-valid loop — for APIs, pipelines, or as a repair fallback for ctx.ask()
|
|
333
335
|
const valid = await ctx.verify(
|
|
334
|
-
async (
|
|
335
|
-
|
|
336
|
-
{ retries: 3, fallback:
|
|
336
|
+
async () => fetchRouteFromAPI(origin, destination),
|
|
337
|
+
RouteSchema,
|
|
338
|
+
{ retries: 3, fallback: defaultRoute },
|
|
337
339
|
);
|
|
338
340
|
|
|
339
341
|
// Cost control — returns { value, budgetExceeded, totalCost }
|
|
@@ -477,7 +479,29 @@ const safe = agent({
|
|
|
477
479
|
});
|
|
478
480
|
```
|
|
479
481
|
|
|
480
|
-
When `onBlock` is `'retry'`, the LLM's blocked output is appended to the conversation (as an assistant message) along with a system message containing the block reason, then the LLM is re-called so it can self-correct. These messages **accumulate** across retries — if the guardrail blocks multiple times, the LLM sees all prior failed attempts and corrections before its next try. All retry messages are ephemeral — they are **not** persisted to session history, so subsequent session turns never see the blocked attempts.
|
|
482
|
+
When `onBlock` is `'retry'`, the LLM's blocked output is appended to the conversation (as an assistant message) along with a system message containing the block reason, then the LLM is re-called so it can self-correct. These messages **accumulate** across retries — if the guardrail blocks multiple times, the LLM sees all prior failed attempts and corrections before its next try. All retry messages are ephemeral — they are **not** persisted to session history, so subsequent session turns never see the blocked attempts. Schema retries and validate retries use the same accumulating pattern. Input guardrails always throw since the prompt is user-supplied. Throws `GuardrailError` if retries are exhausted or `onBlock` is `'throw'`.
|
|
483
|
+
|
|
484
|
+
For **business rule validation** on the parsed typed object (not raw text), use `validate` on `ctx.ask()`:
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
const UserSchema = z.object({
|
|
488
|
+
name: z.string(),
|
|
489
|
+
email: z.string(),
|
|
490
|
+
role: z.enum(['admin', 'editor', 'viewer']),
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const result = await ctx.ask(extractAgent, 'Extract user from this text', {
|
|
494
|
+
schema: UserSchema,
|
|
495
|
+
validate: (user) => {
|
|
496
|
+
if (user.role === 'admin' && !user.email.endsWith('@company.com')) {
|
|
497
|
+
return { valid: false, reason: 'Admin users must have a company email' };
|
|
498
|
+
}
|
|
499
|
+
return { valid: true };
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
`validate` is per-call, co-located with the `schema` it validates. It runs **after** schema parsing succeeds, receiving the fully typed object. On failure, the LLM sees all previous attempts (accumulating context) and the validation reason. Requires `schema` — without it, validate is skipped (use guardrails for raw text). Throws `ValidationError` after retries are exhausted. Also supported on `ctx.delegate()`, `ctx.race()`, and `ctx.verify()`.
|
|
481
505
|
|
|
482
506
|
### State Stores
|
|
483
507
|
|
|
@@ -556,6 +580,7 @@ import {
|
|
|
556
580
|
MaxTurnsError, // Agent exceeded max tool-calling turns
|
|
557
581
|
BudgetExceededError, // Budget limit exceeded
|
|
558
582
|
GuardrailError, // Guardrail blocked input or output
|
|
583
|
+
ValidationError, // Post-schema business rule validation failed after retries
|
|
559
584
|
ToolDenied, // Agent tried to call unauthorized tool
|
|
560
585
|
} from '@axlsdk/axl';
|
|
561
586
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -135,6 +135,7 @@ __export(index_exports, {
|
|
|
135
135
|
SqliteVectorStore: () => SqliteVectorStore,
|
|
136
136
|
TimeoutError: () => TimeoutError,
|
|
137
137
|
ToolDenied: () => ToolDenied,
|
|
138
|
+
ValidationError: () => ValidationError,
|
|
138
139
|
VerifyError: () => VerifyError,
|
|
139
140
|
WorkflowContext: () => WorkflowContext,
|
|
140
141
|
agent: () => agent,
|
|
@@ -2143,6 +2144,18 @@ var GuardrailError = class extends AxlError {
|
|
|
2143
2144
|
this.reason = reason;
|
|
2144
2145
|
}
|
|
2145
2146
|
};
|
|
2147
|
+
var ValidationError = class extends AxlError {
|
|
2148
|
+
lastOutput;
|
|
2149
|
+
reason;
|
|
2150
|
+
retries;
|
|
2151
|
+
constructor(lastOutput, reason, retries) {
|
|
2152
|
+
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
2153
|
+
this.name = "ValidationError";
|
|
2154
|
+
this.lastOutput = lastOutput;
|
|
2155
|
+
this.reason = reason;
|
|
2156
|
+
this.retries = retries;
|
|
2157
|
+
}
|
|
2158
|
+
};
|
|
2146
2159
|
var ToolDenied = class extends AxlError {
|
|
2147
2160
|
toolName;
|
|
2148
2161
|
agentName;
|
|
@@ -2235,48 +2248,9 @@ function resolveConfig(config) {
|
|
|
2235
2248
|
// src/context.ts
|
|
2236
2249
|
var signalStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
2237
2250
|
function zodToJsonSchema(schema) {
|
|
2238
|
-
const
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
case "ZodString":
|
|
2242
|
-
return { type: "string" };
|
|
2243
|
-
case "ZodNumber":
|
|
2244
|
-
return { type: "number" };
|
|
2245
|
-
case "ZodBoolean":
|
|
2246
|
-
return { type: "boolean" };
|
|
2247
|
-
case "ZodArray":
|
|
2248
|
-
return { type: "array", items: zodToJsonSchema(def.type) };
|
|
2249
|
-
case "ZodObject": {
|
|
2250
|
-
const shape = def.shape?.() ?? {};
|
|
2251
|
-
const properties = {};
|
|
2252
|
-
const required = [];
|
|
2253
|
-
for (const [key, value] of Object.entries(shape)) {
|
|
2254
|
-
properties[key] = zodToJsonSchema(value);
|
|
2255
|
-
const innerDef = value._def;
|
|
2256
|
-
if (innerDef?.typeName !== "ZodOptional" && innerDef?.typeName !== "ZodDefault") {
|
|
2257
|
-
required.push(key);
|
|
2258
|
-
}
|
|
2259
|
-
}
|
|
2260
|
-
return { type: "object", properties, required: required.length > 0 ? required : void 0 };
|
|
2261
|
-
}
|
|
2262
|
-
case "ZodOptional":
|
|
2263
|
-
return zodToJsonSchema(def.innerType);
|
|
2264
|
-
case "ZodDefault":
|
|
2265
|
-
return zodToJsonSchema(def.innerType);
|
|
2266
|
-
case "ZodEnum":
|
|
2267
|
-
return { type: "string", enum: def.values };
|
|
2268
|
-
case "ZodLiteral": {
|
|
2269
|
-
const v = def.value;
|
|
2270
|
-
const t = v === null ? "null" : typeof v;
|
|
2271
|
-
return { type: t, const: v };
|
|
2272
|
-
}
|
|
2273
|
-
case "ZodUnion":
|
|
2274
|
-
return { oneOf: def.options.map((o) => zodToJsonSchema(o)) };
|
|
2275
|
-
case "ZodNullable":
|
|
2276
|
-
return { ...zodToJsonSchema(def.innerType), nullable: true };
|
|
2277
|
-
default:
|
|
2278
|
-
return {};
|
|
2279
|
-
}
|
|
2251
|
+
const result = import_zod.z.toJSONSchema(schema, { unrepresentable: "any" });
|
|
2252
|
+
delete result.$schema;
|
|
2253
|
+
return result;
|
|
2280
2254
|
}
|
|
2281
2255
|
function estimateTokens(text) {
|
|
2282
2256
|
return Math.ceil(text.length / 4);
|
|
@@ -2407,9 +2381,6 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
2407
2381
|
agent2,
|
|
2408
2382
|
prompt,
|
|
2409
2383
|
options,
|
|
2410
|
-
0,
|
|
2411
|
-
void 0,
|
|
2412
|
-
void 0,
|
|
2413
2384
|
void 0,
|
|
2414
2385
|
usageCapture
|
|
2415
2386
|
);
|
|
@@ -2459,7 +2430,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
2459
2430
|
return result;
|
|
2460
2431
|
});
|
|
2461
2432
|
}
|
|
2462
|
-
async executeAgentCall(agent2, prompt, options,
|
|
2433
|
+
async executeAgentCall(agent2, prompt, options, handoffMessages, usageCapture) {
|
|
2463
2434
|
if (this.budgetContext?.exceeded) {
|
|
2464
2435
|
const { limit, totalCost: spent, policy } = this.budgetContext;
|
|
2465
2436
|
if (policy === "warn") {
|
|
@@ -2531,16 +2502,6 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
2531
2502
|
|
|
2532
2503
|
Respond with valid JSON matching this schema:
|
|
2533
2504
|
${JSON.stringify(jsonSchema, null, 2)}`;
|
|
2534
|
-
}
|
|
2535
|
-
if (previousOutput && previousError) {
|
|
2536
|
-
userContent += `
|
|
2537
|
-
|
|
2538
|
-
Your previous response was invalid:
|
|
2539
|
-
${previousOutput}
|
|
2540
|
-
|
|
2541
|
-
Error: ${previousError}
|
|
2542
|
-
|
|
2543
|
-
Please fix and try again.`;
|
|
2544
2505
|
}
|
|
2545
2506
|
messages.push({ role: "user", content: userContent });
|
|
2546
2507
|
if (handoffMessages && handoffMessages.length > 0) {
|
|
@@ -2585,9 +2546,17 @@ Please fix and try again.`;
|
|
|
2585
2546
|
const maxTurns = agent2._config.maxTurns ?? 25;
|
|
2586
2547
|
const timeoutMs = parseDuration(agent2._config.timeout ?? "60s");
|
|
2587
2548
|
const startTime = Date.now();
|
|
2549
|
+
if (this.onToken && options?.validate) {
|
|
2550
|
+
throw new AxlError(
|
|
2551
|
+
"INVALID_CONFIG",
|
|
2552
|
+
"Cannot use validate with streaming. Validate requires schema (JSON output) which does not benefit from token streaming. Use a non-streaming call instead."
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2588
2555
|
const currentMessages = [...messages];
|
|
2589
2556
|
let turns = 0;
|
|
2590
2557
|
let guardrailOutputRetries = 0;
|
|
2558
|
+
let schemaRetries = 0;
|
|
2559
|
+
let validateRetries = 0;
|
|
2591
2560
|
while (turns < maxTurns) {
|
|
2592
2561
|
if (Date.now() - startTime > timeoutMs) {
|
|
2593
2562
|
throw new TimeoutError("ctx.ask()", timeoutMs);
|
|
@@ -2715,14 +2684,17 @@ Please fix and try again.`;
|
|
|
2715
2684
|
}
|
|
2716
2685
|
}
|
|
2717
2686
|
const handoffStart = Date.now();
|
|
2718
|
-
const handoffOptions = options ? {
|
|
2687
|
+
const handoffOptions = options ? {
|
|
2688
|
+
schema: options.schema,
|
|
2689
|
+
retries: options.retries,
|
|
2690
|
+
metadata: options.metadata,
|
|
2691
|
+
validate: options.validate,
|
|
2692
|
+
validateRetries: options.validateRetries
|
|
2693
|
+
} : void 0;
|
|
2719
2694
|
const handoffFn = () => this.executeAgentCall(
|
|
2720
2695
|
descriptor.agent,
|
|
2721
2696
|
handoffPrompt,
|
|
2722
2697
|
handoffOptions,
|
|
2723
|
-
0,
|
|
2724
|
-
void 0,
|
|
2725
|
-
void 0,
|
|
2726
2698
|
currentMessages,
|
|
2727
2699
|
usageCapture
|
|
2728
2700
|
);
|
|
@@ -3056,26 +3028,26 @@ Please fix and try again.`;
|
|
|
3056
3028
|
throw new GuardrailError("output", outputResult.reason ?? "Output blocked by guardrail");
|
|
3057
3029
|
}
|
|
3058
3030
|
}
|
|
3031
|
+
let validated = void 0;
|
|
3059
3032
|
if (options?.schema) {
|
|
3060
3033
|
try {
|
|
3061
3034
|
const parsed = JSON.parse(stripMarkdownFences(content));
|
|
3062
|
-
|
|
3063
|
-
this.pushAssistantToSessionHistory(content, response.providerMetadata);
|
|
3064
|
-
return validated;
|
|
3035
|
+
validated = options.schema.parse(parsed);
|
|
3065
3036
|
} catch (err) {
|
|
3066
|
-
const
|
|
3067
|
-
if (
|
|
3037
|
+
const maxSchemaRetries = options.retries ?? 3;
|
|
3038
|
+
if (schemaRetries < maxSchemaRetries) {
|
|
3039
|
+
schemaRetries++;
|
|
3068
3040
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
prompt,
|
|
3072
|
-
options,
|
|
3073
|
-
retryCount + 1,
|
|
3041
|
+
currentMessages.push({
|
|
3042
|
+
role: "assistant",
|
|
3074
3043
|
content,
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3044
|
+
...response.providerMetadata ? { providerMetadata: response.providerMetadata } : {}
|
|
3045
|
+
});
|
|
3046
|
+
currentMessages.push({
|
|
3047
|
+
role: "system",
|
|
3048
|
+
content: `Your response was not valid JSON or did not match the required schema: ${errorMsg}. Please fix and try again.`
|
|
3049
|
+
});
|
|
3050
|
+
continue;
|
|
3079
3051
|
}
|
|
3080
3052
|
const zodErr = err instanceof import_zod.ZodError ? err : new import_zod.ZodError([
|
|
3081
3053
|
{
|
|
@@ -3084,11 +3056,55 @@ Please fix and try again.`;
|
|
|
3084
3056
|
message: err instanceof Error ? err.message : String(err)
|
|
3085
3057
|
}
|
|
3086
3058
|
]);
|
|
3087
|
-
throw new VerifyError(content, zodErr,
|
|
3059
|
+
throw new VerifyError(content, zodErr, maxSchemaRetries);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
if (options?.schema && options.validate) {
|
|
3063
|
+
let validateResult;
|
|
3064
|
+
try {
|
|
3065
|
+
validateResult = await options.validate(validated, {
|
|
3066
|
+
metadata: this.metadata
|
|
3067
|
+
});
|
|
3068
|
+
} catch (err) {
|
|
3069
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3070
|
+
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
3071
|
+
}
|
|
3072
|
+
this.emitTrace({
|
|
3073
|
+
type: "validate",
|
|
3074
|
+
agent: agent2._name,
|
|
3075
|
+
data: {
|
|
3076
|
+
valid: validateResult.valid,
|
|
3077
|
+
...validateResult.reason ? { reason: validateResult.reason } : {}
|
|
3078
|
+
}
|
|
3079
|
+
});
|
|
3080
|
+
this.spanManager?.addEventToActiveSpan("axl.validate.check", {
|
|
3081
|
+
"axl.validate.valid": validateResult.valid,
|
|
3082
|
+
...validateResult.reason ? { "axl.validate.reason": validateResult.reason } : {}
|
|
3083
|
+
});
|
|
3084
|
+
if (!validateResult.valid) {
|
|
3085
|
+
const maxValidateRetries = options.validateRetries ?? 2;
|
|
3086
|
+
if (validateRetries < maxValidateRetries) {
|
|
3087
|
+
validateRetries++;
|
|
3088
|
+
currentMessages.push({
|
|
3089
|
+
role: "assistant",
|
|
3090
|
+
content,
|
|
3091
|
+
...response.providerMetadata ? { providerMetadata: response.providerMetadata } : {}
|
|
3092
|
+
});
|
|
3093
|
+
currentMessages.push({
|
|
3094
|
+
role: "system",
|
|
3095
|
+
content: `Your response parsed correctly but failed validation: ${validateResult.reason ?? "Validation failed"}. Previous attempts are visible above. Please fix and try again.`
|
|
3096
|
+
});
|
|
3097
|
+
continue;
|
|
3098
|
+
}
|
|
3099
|
+
throw new ValidationError(
|
|
3100
|
+
validated,
|
|
3101
|
+
validateResult.reason ?? "Validation failed",
|
|
3102
|
+
maxValidateRetries
|
|
3103
|
+
);
|
|
3088
3104
|
}
|
|
3089
3105
|
}
|
|
3090
3106
|
this.pushAssistantToSessionHistory(content, response.providerMetadata);
|
|
3091
|
-
return content;
|
|
3107
|
+
return validated ?? content;
|
|
3092
3108
|
}
|
|
3093
3109
|
throw new MaxTurnsError("ctx.ask()", maxTurns);
|
|
3094
3110
|
}
|
|
@@ -3445,32 +3461,57 @@ ${summaryResponse.content}`
|
|
|
3445
3461
|
// ── ctx.verify() ──────────────────────────────────────────────────────
|
|
3446
3462
|
async verify(fn, schema, options) {
|
|
3447
3463
|
const maxRetries = options?.retries ?? 3;
|
|
3448
|
-
let
|
|
3449
|
-
let lastErrorMessage = void 0;
|
|
3464
|
+
let lastRetry = void 0;
|
|
3450
3465
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3451
|
-
let
|
|
3466
|
+
let rawOutput;
|
|
3452
3467
|
try {
|
|
3453
|
-
result = await fn(
|
|
3454
|
-
|
|
3455
|
-
|
|
3468
|
+
const result = await fn(lastRetry);
|
|
3469
|
+
rawOutput = result;
|
|
3470
|
+
const parsed = schema.parse(result);
|
|
3471
|
+
if (options?.validate) {
|
|
3472
|
+
let validateResult;
|
|
3473
|
+
try {
|
|
3474
|
+
validateResult = await options.validate(parsed, { metadata: this.metadata });
|
|
3475
|
+
} catch (err) {
|
|
3476
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3477
|
+
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
3478
|
+
}
|
|
3479
|
+
if (!validateResult.valid) {
|
|
3480
|
+
const errorMsg = validateResult.reason ?? "Validation failed";
|
|
3481
|
+
lastRetry = { error: errorMsg, output: rawOutput, parsed };
|
|
3482
|
+
if (attempt === maxRetries) {
|
|
3483
|
+
if (options?.fallback !== void 0) return options.fallback;
|
|
3484
|
+
throw new ValidationError(parsed, errorMsg, maxRetries);
|
|
3485
|
+
}
|
|
3486
|
+
continue;
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
return parsed;
|
|
3456
3490
|
} catch (err) {
|
|
3457
|
-
if (err instanceof
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3491
|
+
if (err instanceof ValidationError) {
|
|
3492
|
+
lastRetry = {
|
|
3493
|
+
error: err.reason,
|
|
3494
|
+
output: rawOutput,
|
|
3495
|
+
parsed: err.lastOutput
|
|
3496
|
+
};
|
|
3497
|
+
if (attempt === maxRetries) {
|
|
3498
|
+
if (options?.fallback !== void 0) return options.fallback;
|
|
3499
|
+
throw err;
|
|
3500
|
+
}
|
|
3501
|
+
continue;
|
|
3463
3502
|
}
|
|
3503
|
+
const errorMsg = err instanceof import_zod.ZodError ? err.message : err instanceof Error ? err.message : String(err);
|
|
3504
|
+
lastRetry = { error: errorMsg, output: rawOutput };
|
|
3464
3505
|
if (attempt === maxRetries) {
|
|
3465
3506
|
if (options?.fallback !== void 0) return options.fallback;
|
|
3466
|
-
const zodErr = err instanceof import_zod.ZodError ? err : new import_zod.ZodError([{ code: "custom", path: [], message:
|
|
3467
|
-
throw new VerifyError(
|
|
3507
|
+
const zodErr = err instanceof import_zod.ZodError ? err : new import_zod.ZodError([{ code: "custom", path: [], message: errorMsg }]);
|
|
3508
|
+
throw new VerifyError(rawOutput, zodErr, maxRetries);
|
|
3468
3509
|
}
|
|
3469
3510
|
}
|
|
3470
3511
|
}
|
|
3471
3512
|
if (options?.fallback !== void 0) return options.fallback;
|
|
3472
3513
|
throw new VerifyError(
|
|
3473
|
-
|
|
3514
|
+
lastRetry?.output,
|
|
3474
3515
|
new import_zod.ZodError([{ code: "custom", path: [], message: "Verify failed" }]),
|
|
3475
3516
|
maxRetries
|
|
3476
3517
|
);
|
|
@@ -3572,7 +3613,7 @@ ${summaryResponse.content}`
|
|
|
3572
3613
|
let remaining = fns.length;
|
|
3573
3614
|
for (const fn of fns) {
|
|
3574
3615
|
const p = signalStorage.run(composedSignal, fn);
|
|
3575
|
-
p.then((value) => {
|
|
3616
|
+
p.then(async (value) => {
|
|
3576
3617
|
if (settled) return;
|
|
3577
3618
|
if (schema) {
|
|
3578
3619
|
const parsed = schema.safeParse(value);
|
|
@@ -3585,6 +3626,33 @@ ${summaryResponse.content}`
|
|
|
3585
3626
|
}
|
|
3586
3627
|
return;
|
|
3587
3628
|
}
|
|
3629
|
+
if (options?.validate) {
|
|
3630
|
+
try {
|
|
3631
|
+
const validateResult = await options.validate(parsed.data, {
|
|
3632
|
+
metadata: this.metadata
|
|
3633
|
+
});
|
|
3634
|
+
if (!validateResult.valid) {
|
|
3635
|
+
remaining--;
|
|
3636
|
+
lastError = new Error(
|
|
3637
|
+
`Validation failed: ${validateResult.reason ?? "Validation failed"}`
|
|
3638
|
+
);
|
|
3639
|
+
if (remaining === 0 && !settled) {
|
|
3640
|
+
settled = true;
|
|
3641
|
+
reject(lastError);
|
|
3642
|
+
}
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
} catch (err) {
|
|
3646
|
+
remaining--;
|
|
3647
|
+
lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
|
|
3648
|
+
if (remaining === 0 && !settled) {
|
|
3649
|
+
settled = true;
|
|
3650
|
+
reject(lastError);
|
|
3651
|
+
}
|
|
3652
|
+
return;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
if (settled) return;
|
|
3588
3656
|
settled = true;
|
|
3589
3657
|
controller.abort();
|
|
3590
3658
|
resolve(parsed.data);
|
|
@@ -3836,7 +3904,9 @@ ${summaryResponse.content}`
|
|
|
3836
3904
|
return this.ask(agents[0], prompt, {
|
|
3837
3905
|
schema: options?.schema,
|
|
3838
3906
|
retries: options?.retries,
|
|
3839
|
-
metadata: options?.metadata
|
|
3907
|
+
metadata: options?.metadata,
|
|
3908
|
+
validate: options?.validate,
|
|
3909
|
+
validateRetries: options?.validateRetries
|
|
3840
3910
|
});
|
|
3841
3911
|
}
|
|
3842
3912
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
@@ -3877,7 +3947,9 @@ ${summaryResponse.content}`
|
|
|
3877
3947
|
return this.ask(routerAgent, prompt, {
|
|
3878
3948
|
schema: options?.schema,
|
|
3879
3949
|
retries: options?.retries,
|
|
3880
|
-
metadata: options?.metadata
|
|
3950
|
+
metadata: options?.metadata,
|
|
3951
|
+
validate: options?.validate,
|
|
3952
|
+
validateRetries: options?.validateRetries
|
|
3881
3953
|
});
|
|
3882
3954
|
}
|
|
3883
3955
|
// ── Private ───────────────────────────────────────────────────────────
|
|
@@ -6159,6 +6231,7 @@ function cosineSimilarity2(a, b) {
|
|
|
6159
6231
|
SqliteVectorStore,
|
|
6160
6232
|
TimeoutError,
|
|
6161
6233
|
ToolDenied,
|
|
6234
|
+
ValidationError,
|
|
6162
6235
|
VerifyError,
|
|
6163
6236
|
WorkflowContext,
|
|
6164
6237
|
agent,
|