@actuarial-ts/agents 0.6.0 → 0.7.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 +7 -5
- package/dist/diagnostics.d.ts +32 -27
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +604 -41
- package/dist/diagnostics.js.map +1 -1
- package/dist/divergence.d.ts.map +1 -1
- package/dist/divergence.js +30 -10
- package/dist/divergence.js.map +1 -1
- package/dist/judgment.d.ts +75 -2
- package/dist/judgment.d.ts.map +1 -1
- package/dist/judgment.js +28 -10
- package/dist/judgment.js.map +1 -1
- package/dist/remote.d.ts +5 -5
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +27 -11
- package/dist/tools.js.map +1 -1
- package/package.json +7 -6
- package/src/diagnostics.ts +819 -57
- package/src/divergence.ts +73 -29
- package/src/judgment.ts +56 -20
- package/src/tools.ts +211 -38
package/src/tools.ts
CHANGED
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { createTool, type Tool } from "@mastra/core/tools";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
toStandardSchema,
|
|
23
|
+
type StandardSchemaWithJSON,
|
|
24
|
+
} from "@mastra/core/schema";
|
|
22
25
|
import type { z } from "zod";
|
|
23
26
|
import { AgentsError } from "./errors.js";
|
|
24
27
|
|
|
@@ -33,21 +36,32 @@ export type ToolEnvelopeFailure = {
|
|
|
33
36
|
|
|
34
37
|
const TOOL_INPUT_INVALID: ToolEnvelopeFailure = Object.freeze({
|
|
35
38
|
success: false,
|
|
36
|
-
error: Object.freeze({
|
|
39
|
+
error: Object.freeze({
|
|
40
|
+
code: "TOOL_INPUT_INVALID",
|
|
41
|
+
message: "Tool input failed schema validation",
|
|
42
|
+
}),
|
|
37
43
|
});
|
|
38
44
|
const TOOL_OUTPUT_INVALID: ToolEnvelopeFailure = Object.freeze({
|
|
39
45
|
success: false,
|
|
40
|
-
error: Object.freeze({
|
|
46
|
+
error: Object.freeze({
|
|
47
|
+
code: "TOOL_OUTPUT_INVALID",
|
|
48
|
+
message: "Tool output failed schema validation",
|
|
49
|
+
}),
|
|
41
50
|
});
|
|
42
51
|
|
|
43
52
|
function readonlyFailure(code: string, message: string): ToolEnvelopeFailure {
|
|
44
|
-
return Object.freeze({
|
|
53
|
+
return Object.freeze({
|
|
54
|
+
success: false,
|
|
55
|
+
error: Object.freeze({ code, message }),
|
|
56
|
+
});
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
function deepFreezeResult<T>(value: T, seen = new WeakSet<object>()): T {
|
|
48
|
-
if (value === null || typeof value !== "object" || seen.has(value))
|
|
60
|
+
if (value === null || typeof value !== "object" || seen.has(value))
|
|
61
|
+
return value;
|
|
49
62
|
seen.add(value);
|
|
50
|
-
for (const child of Object.values(value as Record<string, unknown>))
|
|
63
|
+
for (const child of Object.values(value as Record<string, unknown>))
|
|
64
|
+
deepFreezeResult(child, seen);
|
|
51
65
|
return Object.freeze(value);
|
|
52
66
|
}
|
|
53
67
|
|
|
@@ -57,7 +71,10 @@ function deepFreezeResult<T>(value: T, seen = new WeakSet<object>()): T {
|
|
|
57
71
|
* AgentsError, ComplianceError) keep their code; everything else gets
|
|
58
72
|
* fallbackCode.
|
|
59
73
|
*/
|
|
60
|
-
export function envelopeFailure(
|
|
74
|
+
export function envelopeFailure(
|
|
75
|
+
err: unknown,
|
|
76
|
+
fallbackCode = "TOOL_ERROR",
|
|
77
|
+
): ToolEnvelopeFailure {
|
|
61
78
|
let code = fallbackCode;
|
|
62
79
|
let message = "Unknown error";
|
|
63
80
|
try {
|
|
@@ -102,7 +119,10 @@ export interface TenantToolContext {
|
|
|
102
119
|
* a defineActuarialTool execute the wrapper converts that throw into the
|
|
103
120
|
* failure envelope, so the model sees a recoverable error, never a crash.
|
|
104
121
|
*/
|
|
105
|
-
export function tenantOf(
|
|
122
|
+
export function tenantOf(
|
|
123
|
+
context: TenantToolContext | undefined,
|
|
124
|
+
key = "projectId",
|
|
125
|
+
): string {
|
|
106
126
|
return resolveTenant(context, { source: "request-context", key });
|
|
107
127
|
}
|
|
108
128
|
|
|
@@ -121,7 +141,9 @@ export interface McpContextLike {
|
|
|
121
141
|
* the transport-by-transport story). Returns undefined when no auth info is
|
|
122
142
|
* present — the caller decides that is fatal.
|
|
123
143
|
*/
|
|
124
|
-
export function resolveMcpAuthInfo(
|
|
144
|
+
export function resolveMcpAuthInfo(
|
|
145
|
+
context: McpContextLike | undefined,
|
|
146
|
+
): McpAuthInfoLike | undefined {
|
|
125
147
|
if (!context) return undefined;
|
|
126
148
|
|
|
127
149
|
// Primary: the streamable-HTTP call path passes the transport extra at
|
|
@@ -139,7 +161,9 @@ export function resolveMcpAuthInfo(context: McpContextLike | undefined): McpAuth
|
|
|
139
161
|
|
|
140
162
|
// Lock-tested @mastra/mcp 1.17.3: createProxiedRequestContext copies each
|
|
141
163
|
// extra key onto the RequestContext verbatim, so authInfo is top-level.
|
|
142
|
-
const topLevelAuthInfo = requestContext.get("authInfo") as
|
|
164
|
+
const topLevelAuthInfo = requestContext.get("authInfo") as
|
|
165
|
+
| McpAuthInfoLike
|
|
166
|
+
| undefined;
|
|
143
167
|
if (topLevelAuthInfo) return topLevelAuthInfo;
|
|
144
168
|
}
|
|
145
169
|
return undefined;
|
|
@@ -196,7 +220,9 @@ export function resolveTenant(
|
|
|
196
220
|
const TENANT_KEY_PATTERN = /^(project|tenant)[_-]?id$/i;
|
|
197
221
|
|
|
198
222
|
/** Top-level shape keys of a zod object schema, or null when not an object schema. */
|
|
199
|
-
export function zodObjectShape(
|
|
223
|
+
export function zodObjectShape(
|
|
224
|
+
schema: unknown,
|
|
225
|
+
): Record<string, unknown> | null {
|
|
200
226
|
if (typeof schema !== "object" || schema === null) return null;
|
|
201
227
|
const def = (schema as { _def?: { typeName?: unknown } })._def;
|
|
202
228
|
if (def?.typeName !== "ZodObject") return null;
|
|
@@ -302,7 +328,15 @@ function assertNoTenantKeys(
|
|
|
302
328
|
}
|
|
303
329
|
stack.add(schema);
|
|
304
330
|
try {
|
|
305
|
-
lintSchemaNode(
|
|
331
|
+
lintSchemaNode(
|
|
332
|
+
schema,
|
|
333
|
+
toolId,
|
|
334
|
+
path,
|
|
335
|
+
allowUninspected,
|
|
336
|
+
usedAllowances,
|
|
337
|
+
stack,
|
|
338
|
+
depth,
|
|
339
|
+
);
|
|
306
340
|
} finally {
|
|
307
341
|
stack.delete(schema);
|
|
308
342
|
}
|
|
@@ -343,7 +377,15 @@ function lintSchemaNode(
|
|
|
343
377
|
`Tool "${toolId}" declares input key "${path}.${key}": tenant ids travel only via the server-set RequestContext (read them with tenantOf), never through the model-facing input schema`,
|
|
344
378
|
);
|
|
345
379
|
}
|
|
346
|
-
assertNoTenantKeys(
|
|
380
|
+
assertNoTenantKeys(
|
|
381
|
+
value,
|
|
382
|
+
toolId,
|
|
383
|
+
`${path}.${key}`,
|
|
384
|
+
allowUninspected,
|
|
385
|
+
usedAllowances,
|
|
386
|
+
stack,
|
|
387
|
+
depth + 1,
|
|
388
|
+
);
|
|
347
389
|
}
|
|
348
390
|
return;
|
|
349
391
|
}
|
|
@@ -369,13 +411,21 @@ function lintSchemaNode(
|
|
|
369
411
|
throw new AgentsError(
|
|
370
412
|
"BAD_INPUT_SCHEMA",
|
|
371
413
|
`Tool "${toolId}": the ${typeName} at "${path}" admits values the tenant lint cannot ` +
|
|
372
|
-
|
|
373
|
-
|
|
414
|
+
"inspect. Either declare the shape with typed keys, or — if this input is validated " +
|
|
415
|
+
"downstream (parseDocument etc.) — name the exact path in `allowUninspected` so the " +
|
|
374
416
|
"exception is deliberate and greppable",
|
|
375
417
|
);
|
|
376
418
|
}
|
|
377
419
|
if (typeName === "ZodArray") {
|
|
378
|
-
assertNoTenantKeys(
|
|
420
|
+
assertNoTenantKeys(
|
|
421
|
+
def.type,
|
|
422
|
+
toolId,
|
|
423
|
+
`${path}[]`,
|
|
424
|
+
allowUninspected,
|
|
425
|
+
usedAllowances,
|
|
426
|
+
stack,
|
|
427
|
+
depth + 1,
|
|
428
|
+
);
|
|
379
429
|
return;
|
|
380
430
|
}
|
|
381
431
|
if (
|
|
@@ -385,49 +435,146 @@ function lintSchemaNode(
|
|
|
385
435
|
typeName === "ZodCatch" ||
|
|
386
436
|
typeName === "ZodReadonly"
|
|
387
437
|
) {
|
|
388
|
-
assertNoTenantKeys(
|
|
438
|
+
assertNoTenantKeys(
|
|
439
|
+
def.innerType,
|
|
440
|
+
toolId,
|
|
441
|
+
path,
|
|
442
|
+
allowUninspected,
|
|
443
|
+
usedAllowances,
|
|
444
|
+
stack,
|
|
445
|
+
depth + 1,
|
|
446
|
+
);
|
|
389
447
|
return;
|
|
390
448
|
}
|
|
391
449
|
if (typeName === "ZodPromise" || typeName === "ZodBranded") {
|
|
392
|
-
assertNoTenantKeys(
|
|
450
|
+
assertNoTenantKeys(
|
|
451
|
+
def.type,
|
|
452
|
+
toolId,
|
|
453
|
+
path,
|
|
454
|
+
allowUninspected,
|
|
455
|
+
usedAllowances,
|
|
456
|
+
stack,
|
|
457
|
+
depth + 1,
|
|
458
|
+
);
|
|
393
459
|
return;
|
|
394
460
|
}
|
|
395
461
|
if (typeName === "ZodEffects") {
|
|
396
|
-
assertNoTenantKeys(
|
|
462
|
+
assertNoTenantKeys(
|
|
463
|
+
def.schema,
|
|
464
|
+
toolId,
|
|
465
|
+
path,
|
|
466
|
+
allowUninspected,
|
|
467
|
+
usedAllowances,
|
|
468
|
+
stack,
|
|
469
|
+
depth + 1,
|
|
470
|
+
);
|
|
397
471
|
return;
|
|
398
472
|
}
|
|
399
473
|
if (typeName === "ZodUnion" || typeName === "ZodDiscriminatedUnion") {
|
|
400
|
-
for (const opt of def.options ?? [])
|
|
474
|
+
for (const opt of def.options ?? [])
|
|
475
|
+
assertNoTenantKeys(
|
|
476
|
+
opt,
|
|
477
|
+
toolId,
|
|
478
|
+
path,
|
|
479
|
+
allowUninspected,
|
|
480
|
+
usedAllowances,
|
|
481
|
+
stack,
|
|
482
|
+
depth + 1,
|
|
483
|
+
);
|
|
401
484
|
return;
|
|
402
485
|
}
|
|
403
486
|
if (typeName === "ZodTuple") {
|
|
404
487
|
for (const [index, item] of (def.items ?? []).entries()) {
|
|
405
|
-
assertNoTenantKeys(
|
|
488
|
+
assertNoTenantKeys(
|
|
489
|
+
item,
|
|
490
|
+
toolId,
|
|
491
|
+
`${path}[${index}]`,
|
|
492
|
+
allowUninspected,
|
|
493
|
+
usedAllowances,
|
|
494
|
+
stack,
|
|
495
|
+
depth + 1,
|
|
496
|
+
);
|
|
406
497
|
}
|
|
407
498
|
if (def.rest !== undefined && def.rest !== null) {
|
|
408
|
-
assertNoTenantKeys(
|
|
499
|
+
assertNoTenantKeys(
|
|
500
|
+
def.rest,
|
|
501
|
+
toolId,
|
|
502
|
+
`${path}[rest]`,
|
|
503
|
+
allowUninspected,
|
|
504
|
+
usedAllowances,
|
|
505
|
+
stack,
|
|
506
|
+
depth + 1,
|
|
507
|
+
);
|
|
409
508
|
}
|
|
410
509
|
return;
|
|
411
510
|
}
|
|
412
511
|
if (typeName === "ZodIntersection") {
|
|
413
|
-
assertNoTenantKeys(
|
|
414
|
-
|
|
512
|
+
assertNoTenantKeys(
|
|
513
|
+
def.left,
|
|
514
|
+
toolId,
|
|
515
|
+
path,
|
|
516
|
+
allowUninspected,
|
|
517
|
+
usedAllowances,
|
|
518
|
+
stack,
|
|
519
|
+
depth + 1,
|
|
520
|
+
);
|
|
521
|
+
assertNoTenantKeys(
|
|
522
|
+
def.right,
|
|
523
|
+
toolId,
|
|
524
|
+
path,
|
|
525
|
+
allowUninspected,
|
|
526
|
+
usedAllowances,
|
|
527
|
+
stack,
|
|
528
|
+
depth + 1,
|
|
529
|
+
);
|
|
415
530
|
return;
|
|
416
531
|
}
|
|
417
532
|
if (typeName === "ZodPipeline") {
|
|
418
|
-
assertNoTenantKeys(
|
|
419
|
-
|
|
533
|
+
assertNoTenantKeys(
|
|
534
|
+
def.in,
|
|
535
|
+
toolId,
|
|
536
|
+
path,
|
|
537
|
+
allowUninspected,
|
|
538
|
+
usedAllowances,
|
|
539
|
+
stack,
|
|
540
|
+
depth + 1,
|
|
541
|
+
);
|
|
542
|
+
assertNoTenantKeys(
|
|
543
|
+
def.out,
|
|
544
|
+
toolId,
|
|
545
|
+
path,
|
|
546
|
+
allowUninspected,
|
|
547
|
+
usedAllowances,
|
|
548
|
+
stack,
|
|
549
|
+
depth + 1,
|
|
550
|
+
);
|
|
420
551
|
return;
|
|
421
552
|
}
|
|
422
553
|
if (typeName === "ZodSet") {
|
|
423
|
-
assertNoTenantKeys(
|
|
554
|
+
assertNoTenantKeys(
|
|
555
|
+
def.valueType,
|
|
556
|
+
toolId,
|
|
557
|
+
`${path}[]`,
|
|
558
|
+
allowUninspected,
|
|
559
|
+
usedAllowances,
|
|
560
|
+
stack,
|
|
561
|
+
depth + 1,
|
|
562
|
+
);
|
|
424
563
|
return;
|
|
425
564
|
}
|
|
426
565
|
if (typeName === "ZodLazy") {
|
|
427
566
|
// Resolve once. A self-referential lazy resolves to a node already on the
|
|
428
567
|
// recursion stack and is caught by the cycle guard in assertNoTenantKeys;
|
|
429
568
|
// a generative lazy (fresh node per resolution) hits the frame budget.
|
|
430
|
-
assertNoTenantKeys(
|
|
569
|
+
assertNoTenantKeys(
|
|
570
|
+
def.getter?.(),
|
|
571
|
+
toolId,
|
|
572
|
+
path,
|
|
573
|
+
allowUninspected,
|
|
574
|
+
usedAllowances,
|
|
575
|
+
stack,
|
|
576
|
+
depth + 1,
|
|
577
|
+
);
|
|
431
578
|
return;
|
|
432
579
|
}
|
|
433
580
|
|
|
@@ -481,7 +628,11 @@ interface DefineActuarialToolCommon<TShape extends z.ZodRawShape, TResult> {
|
|
|
481
628
|
* Optional observable-result schema. It must admit the complete success /
|
|
482
629
|
* failure union because validation errors are ordinary tool results.
|
|
483
630
|
*/
|
|
484
|
-
outputSchema?: z.ZodType<
|
|
631
|
+
outputSchema?: z.ZodType<
|
|
632
|
+
TResult | ToolEnvelopeFailure,
|
|
633
|
+
z.ZodTypeDef,
|
|
634
|
+
unknown
|
|
635
|
+
>;
|
|
485
636
|
}
|
|
486
637
|
|
|
487
638
|
/**
|
|
@@ -541,7 +692,9 @@ export type DefinedActuarialTool<TInput, TOutput> = Omit<
|
|
|
541
692
|
execute: (input: TInput, context: ActuarialToolContext) => Promise<TOutput>;
|
|
542
693
|
};
|
|
543
694
|
|
|
544
|
-
function metadataBridge(
|
|
695
|
+
function metadataBridge(
|
|
696
|
+
schema: z.ZodTypeAny,
|
|
697
|
+
): StandardSchemaWithJSON<unknown, unknown> {
|
|
545
698
|
const real = toStandardSchema(schema);
|
|
546
699
|
return {
|
|
547
700
|
"~standard": {
|
|
@@ -559,7 +712,12 @@ function metadataBridge(schema: z.ZodTypeAny): StandardSchemaWithJSON<unknown, u
|
|
|
559
712
|
function isFailure(value: unknown): value is ToolEnvelopeFailure {
|
|
560
713
|
if (value === null || typeof value !== "object") return false;
|
|
561
714
|
const candidate = value as { success?: unknown; error?: unknown };
|
|
562
|
-
if (
|
|
715
|
+
if (
|
|
716
|
+
candidate.success !== false ||
|
|
717
|
+
candidate.error === null ||
|
|
718
|
+
typeof candidate.error !== "object"
|
|
719
|
+
)
|
|
720
|
+
return false;
|
|
563
721
|
const error = candidate.error as { code?: unknown; message?: unknown };
|
|
564
722
|
return typeof error.code === "string" && typeof error.message === "string";
|
|
565
723
|
}
|
|
@@ -578,7 +736,10 @@ function sameJson(left: unknown, right: unknown): boolean {
|
|
|
578
736
|
*/
|
|
579
737
|
export function defineActuarialTool<TShape extends z.ZodRawShape, TResult>(
|
|
580
738
|
options: DefineActuarialToolOptions<TShape, TResult>,
|
|
581
|
-
): DefinedActuarialTool<
|
|
739
|
+
): DefinedActuarialTool<
|
|
740
|
+
z.input<z.ZodObject<TShape>>,
|
|
741
|
+
TResult | ToolEnvelopeFailure
|
|
742
|
+
> {
|
|
582
743
|
// FAIL CLOSED: a schema the seam cannot inspect is not definable, and the
|
|
583
744
|
// tenant-key lint recurses through every container the model could reach.
|
|
584
745
|
const shape = zodObjectShape(options.inputSchema);
|
|
@@ -590,7 +751,13 @@ export function defineActuarialTool<TShape extends z.ZodRawShape, TResult>(
|
|
|
590
751
|
}
|
|
591
752
|
const allowUninspected = new Set(options.allowUninspected ?? []);
|
|
592
753
|
const usedAllowances = new Set<string>();
|
|
593
|
-
assertNoTenantKeys(
|
|
754
|
+
assertNoTenantKeys(
|
|
755
|
+
options.inputSchema,
|
|
756
|
+
options.id,
|
|
757
|
+
"input",
|
|
758
|
+
allowUninspected,
|
|
759
|
+
usedAllowances,
|
|
760
|
+
);
|
|
594
761
|
for (const declared of allowUninspected) {
|
|
595
762
|
if (!usedAllowances.has(declared)) {
|
|
596
763
|
throw new AgentsError(
|
|
@@ -632,7 +799,9 @@ export function defineActuarialTool<TShape extends z.ZodRawShape, TResult>(
|
|
|
632
799
|
id: options.id,
|
|
633
800
|
description: options.description,
|
|
634
801
|
inputSchema: metadataBridge(options.inputSchema),
|
|
635
|
-
...(options.outputSchema === undefined
|
|
802
|
+
...(options.outputSchema === undefined
|
|
803
|
+
? {}
|
|
804
|
+
: { outputSchema: metadataBridge(options.outputSchema) }),
|
|
636
805
|
});
|
|
637
806
|
|
|
638
807
|
const execute = async (
|
|
@@ -672,14 +841,18 @@ export function defineActuarialTool<TShape extends z.ZodRawShape, TResult>(
|
|
|
672
841
|
return TOOL_OUTPUT_INVALID;
|
|
673
842
|
}
|
|
674
843
|
if (!parsedOutput.success) return TOOL_OUTPUT_INVALID;
|
|
675
|
-
if (isFailure(rawOutput) && !sameJson(parsedOutput.data, rawOutput))
|
|
844
|
+
if (isFailure(rawOutput) && !sameJson(parsedOutput.data, rawOutput))
|
|
845
|
+
return TOOL_OUTPUT_INVALID;
|
|
676
846
|
return deepFreezeResult(parsedOutput.data);
|
|
677
847
|
};
|
|
678
848
|
|
|
679
849
|
// The metadata bridges deliberately erase domain inference on the inherited
|
|
680
850
|
// Mastra surface. This is the single convergence assertion: the adapter
|
|
681
851
|
// above is the only executor and has the exact public input/output contract.
|
|
682
|
-
const defined = Object.assign(tool, {
|
|
852
|
+
const defined = Object.assign(tool, {
|
|
853
|
+
execute,
|
|
854
|
+
kind: options.kind,
|
|
855
|
+
}) as DefinedActuarialTool<
|
|
683
856
|
z.input<z.ZodObject<TShape>>,
|
|
684
857
|
TResult | ToolEnvelopeFailure
|
|
685
858
|
>;
|
|
@@ -710,10 +883,10 @@ export interface ActuarialToolRegistry<T extends RegistrableActuarialTool> {
|
|
|
710
883
|
export function toolRegistry<T extends RegistrableActuarialTool>(
|
|
711
884
|
tools: readonly T[],
|
|
712
885
|
): ActuarialToolRegistry<T> {
|
|
713
|
-
const record
|
|
886
|
+
const record = Object.create(null) as Record<string, T>;
|
|
714
887
|
const actionToolIds = new Set<string>();
|
|
715
888
|
for (const tool of tools) {
|
|
716
|
-
if (record
|
|
889
|
+
if (Object.prototype.hasOwnProperty.call(record, tool.id)) {
|
|
717
890
|
throw new AgentsError(
|
|
718
891
|
"DUPLICATE_TOOL_ID",
|
|
719
892
|
`Two tools share the id "${tool.id}"; tool ids must be unique within a registry`,
|