@actuarial-ts/agents 0.1.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/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +155 -0
- package/dist/advisor.d.ts +69 -0
- package/dist/advisor.d.ts.map +1 -0
- package/dist/advisor.js +83 -0
- package/dist/advisor.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/evals.d.ts +83 -0
- package/dist/evals.d.ts.map +1 -0
- package/dist/evals.js +95 -0
- package/dist/evals.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/judgment.d.ts +208 -0
- package/dist/judgment.d.ts.map +1 -0
- package/dist/judgment.js +247 -0
- package/dist/judgment.js.map +1 -0
- package/dist/tools.d.ts +103 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +185 -0
- package/dist/tools.js.map +1 -0
- package/package.json +63 -0
package/dist/judgment.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Judgment gates that write the compliance ledger: the generalization of the
|
|
3
|
+
* ActNG server's derive-expected-losses workflow (cap -> restoration ->
|
|
4
|
+
* trends -> ELR) into a factory any host can point at its own gate list.
|
|
5
|
+
*
|
|
6
|
+
* The loop each gate enforces is propose -> justify -> approve -> record:
|
|
7
|
+
* the gate gathers evidence and SUSPENDS with a recommendation; a human
|
|
8
|
+
* decides; the resume payload carries the decision plus a VERBATIM rationale;
|
|
9
|
+
* the gate applies the decision and appends the resulting assumptions to an
|
|
10
|
+
* @actuarial-ts/compliance ledger threaded through the workflow state. A
|
|
11
|
+
* completed chain returns { trail, ledger } - a ledger ready to hand straight
|
|
12
|
+
* to generateDisclosure, so ASOP 41 documentation falls out of running the
|
|
13
|
+
* analysis instead of being reconstructed afterwards.
|
|
14
|
+
*
|
|
15
|
+
* Ground truth (verified against @mastra/core 1.49 dist types and a live
|
|
16
|
+
* suspend/resume probe):
|
|
17
|
+
* - Steps execute as ({ inputData, resumeData, suspend, requestContext });
|
|
18
|
+
* suspend(payload) pauses the run with the payload surfaced under
|
|
19
|
+
* result.steps[stepId].suspendPayload; run.resume({ step, resumeData,
|
|
20
|
+
* requestContext }) re-executes the step with resumeData set.
|
|
21
|
+
* - Resume data is validated against the gate's resumeSchema by Mastra
|
|
22
|
+
* itself; run.resume REJECTS on schema violations. The chain additionally
|
|
23
|
+
* rejects blank rationales at runtime (AgentsError MISSING_RATIONALE), so a
|
|
24
|
+
* host schema of plain z.string() cannot smuggle undocumented judgment.
|
|
25
|
+
* - Suspend/resume requires snapshot storage: hosts register the returned
|
|
26
|
+
* workflow on their Mastra instance (new Mastra({ workflows: { chain } })),
|
|
27
|
+
* which provides in-memory storage by default and durable storage when
|
|
28
|
+
* configured - exactly how the server keeps paused derivations resumable
|
|
29
|
+
* across restarts.
|
|
30
|
+
*
|
|
31
|
+
* Purity: the package never reads a clock. The host supplies now() and every
|
|
32
|
+
* ledger timestamp comes from it.
|
|
33
|
+
*
|
|
34
|
+
* Tenant seam: the tenant id travels in the workflow requestContext, never in
|
|
35
|
+
* step inputs - the same boundary as every actuarial tool.
|
|
36
|
+
*/
|
|
37
|
+
import { createStep, createWorkflow } from "@mastra/core/workflows";
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
import { createLedger, recordAssumption, } from "@actuarial-ts/compliance";
|
|
40
|
+
import { AgentsError } from "./errors.js";
|
|
41
|
+
import { zodObjectShape } from "./tools.js";
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Chain state threaded through step outputs
|
|
44
|
+
const trailEntrySchema = z.object({
|
|
45
|
+
stage: z.string(),
|
|
46
|
+
decision: z.string(),
|
|
47
|
+
rationale: z.string(),
|
|
48
|
+
skipped: z.boolean(),
|
|
49
|
+
});
|
|
50
|
+
// Declares EVERY AssumptionEntry field: zod strips undeclared keys on parse,
|
|
51
|
+
// and losing previousValue/source between steps would corrupt the ledger.
|
|
52
|
+
const ledgerEntrySchema = z.object({
|
|
53
|
+
seq: z.number().int().positive(),
|
|
54
|
+
timestamp: z.string(),
|
|
55
|
+
actor: z.enum(["default", "actuary", "agent"]),
|
|
56
|
+
field: z.string(),
|
|
57
|
+
value: z.unknown(),
|
|
58
|
+
previousValue: z.unknown().optional(),
|
|
59
|
+
source: z.string().optional(),
|
|
60
|
+
rationale: z.string().optional(),
|
|
61
|
+
});
|
|
62
|
+
const chainStateSchema = z.object({
|
|
63
|
+
trail: z.array(trailEntrySchema).default([]),
|
|
64
|
+
ledgerEntries: z.array(ledgerEntrySchema).default([]),
|
|
65
|
+
decisions: z.record(z.unknown()).default({}),
|
|
66
|
+
});
|
|
67
|
+
const suspendSchema = z.object({
|
|
68
|
+
stage: z.string(),
|
|
69
|
+
recommendation: z.string(),
|
|
70
|
+
evidence: z.unknown(),
|
|
71
|
+
});
|
|
72
|
+
const chainInputSchema = z.object({});
|
|
73
|
+
const chainResultSchema = z.object({
|
|
74
|
+
trail: z.array(trailEntrySchema),
|
|
75
|
+
ledger: z.custom((value) => typeof value === "object" &&
|
|
76
|
+
value !== null &&
|
|
77
|
+
Array.isArray(value.entries)),
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* The first gate receives the workflow input ({}), and snapshot storage may
|
|
81
|
+
* round-trip state through JSON, so normalize defensively instead of trusting
|
|
82
|
+
* zod defaults to have been applied.
|
|
83
|
+
*/
|
|
84
|
+
function normalizeState(input) {
|
|
85
|
+
const raw = (input ?? {});
|
|
86
|
+
return {
|
|
87
|
+
trail: Array.isArray(raw.trail) ? raw.trail : [],
|
|
88
|
+
ledgerEntries: Array.isArray(raw.ledgerEntries) ? raw.ledgerEntries : [],
|
|
89
|
+
decisions: typeof raw.decisions === "object" && raw.decisions !== null ? raw.decisions : {},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** Trail text for a decision when applyDecision returns no summary. */
|
|
93
|
+
function describeDecision(decision) {
|
|
94
|
+
if (typeof decision === "object" && decision !== null) {
|
|
95
|
+
const named = decision.decision;
|
|
96
|
+
if (typeof named === "string" && named.length > 0)
|
|
97
|
+
return named;
|
|
98
|
+
const { rationale: _rationale, actor: _actor, ...rest } = decision;
|
|
99
|
+
if (Object.keys(rest).length > 0)
|
|
100
|
+
return JSON.stringify(rest);
|
|
101
|
+
}
|
|
102
|
+
return "decided";
|
|
103
|
+
}
|
|
104
|
+
function actorOf(decision) {
|
|
105
|
+
const actor = decision?.actor;
|
|
106
|
+
return actor === "agent" || actor === "default" || actor === "actuary" ? actor : "actuary";
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Factory
|
|
110
|
+
/** Reserved id of the terminal step that assembles { trail, ledger }. */
|
|
111
|
+
const COMPLETE_STEP_ID = "complete";
|
|
112
|
+
/**
|
|
113
|
+
* Builds a committed Mastra workflow from an ordered gate list. Each gate:
|
|
114
|
+
* skipWhen? -> pass through with a trail note; else gatherEvidence ->
|
|
115
|
+
* suspend({ stage, recommendation, evidence }); on resume -> validate the
|
|
116
|
+
* decision (schema + non-blank rationale), applyDecision, append the
|
|
117
|
+
* resulting assumptions to the threaded ledger. The terminal step rebuilds
|
|
118
|
+
* the frozen ledger, invokes onComplete, and returns { trail, ledger }.
|
|
119
|
+
*
|
|
120
|
+
* FOOTGUN (learned the hard way): a Mastra Workflow exposes a .then(step)
|
|
121
|
+
* builder method, which makes it an ACCIDENTAL THENABLE. Never await the
|
|
122
|
+
* returned workflow and never return it from an async function - JS will
|
|
123
|
+
* call .then(resolve, reject) with the promise resolver as a "step" and the
|
|
124
|
+
* promise never settles. Assign it synchronously and register it on your
|
|
125
|
+
* Mastra instance.
|
|
126
|
+
*/
|
|
127
|
+
export function createJudgmentChain(options) {
|
|
128
|
+
const { id, gates, now, onComplete, requestContextSchema } = options;
|
|
129
|
+
if (gates.length === 0) {
|
|
130
|
+
throw new AgentsError("BAD_GATE", `Judgment chain "${id}" needs at least one gate`);
|
|
131
|
+
}
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
for (const gate of gates) {
|
|
134
|
+
if (gate.id === COMPLETE_STEP_ID) {
|
|
135
|
+
throw new AgentsError("BAD_GATE", `Gate id "${COMPLETE_STEP_ID}" is reserved for the chain's terminal step`);
|
|
136
|
+
}
|
|
137
|
+
if (seen.has(gate.id)) {
|
|
138
|
+
throw new AgentsError("BAD_GATE", `Duplicate gate id "${gate.id}" in chain "${id}"`);
|
|
139
|
+
}
|
|
140
|
+
seen.add(gate.id);
|
|
141
|
+
const shape = zodObjectShape(gate.resumeSchema);
|
|
142
|
+
if (!shape || !("rationale" in shape)) {
|
|
143
|
+
throw new AgentsError("BAD_GATE", `Gate "${gate.id}" resumeSchema must be a zod object containing a "rationale" key: every human decision enters the audit trail with its verbatim rationale`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const gateSteps = gates.map((gate) => createStep({
|
|
147
|
+
id: gate.id,
|
|
148
|
+
inputSchema: chainStateSchema,
|
|
149
|
+
outputSchema: chainStateSchema,
|
|
150
|
+
suspendSchema,
|
|
151
|
+
resumeSchema: gate.resumeSchema,
|
|
152
|
+
execute: async ({ inputData, resumeData, suspend, requestContext }) => {
|
|
153
|
+
const state = normalizeState(inputData);
|
|
154
|
+
const ctx = {
|
|
155
|
+
requestContext,
|
|
156
|
+
now,
|
|
157
|
+
decisions: state.decisions,
|
|
158
|
+
trail: state.trail,
|
|
159
|
+
};
|
|
160
|
+
const skipReason = gate.skipWhen?.(ctx) ?? null;
|
|
161
|
+
if (skipReason !== null) {
|
|
162
|
+
const skip = { skipped: true, reason: skipReason };
|
|
163
|
+
return {
|
|
164
|
+
trail: [
|
|
165
|
+
...state.trail,
|
|
166
|
+
{ stage: gate.stage, decision: "skipped", rationale: skipReason, skipped: true },
|
|
167
|
+
],
|
|
168
|
+
ledgerEntries: state.ledgerEntries,
|
|
169
|
+
decisions: { ...state.decisions, [gate.id]: skip },
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (resumeData === undefined) {
|
|
173
|
+
const { recommendation, evidence } = await gate.gatherEvidence(ctx);
|
|
174
|
+
await suspend({ stage: gate.stage, recommendation, evidence });
|
|
175
|
+
return state; // unreachable after suspend; satisfies the output type
|
|
176
|
+
}
|
|
177
|
+
const decision = resumeData;
|
|
178
|
+
const rationale = decision?.rationale;
|
|
179
|
+
if (typeof rationale !== "string" || rationale.trim() === "") {
|
|
180
|
+
throw new AgentsError("MISSING_RATIONALE", `Gate "${gate.id}" (${gate.stage}) resumed without a rationale; undocumented judgment is exactly what the ledger exists to prevent`);
|
|
181
|
+
}
|
|
182
|
+
const actor = actorOf(decision);
|
|
183
|
+
const applied = await gate.applyDecision(ctx, decision);
|
|
184
|
+
// recordAssumption is the single validator/freezer: replay the
|
|
185
|
+
// threaded entries into a ledger, append, and thread the plain
|
|
186
|
+
// entries array (snapshot storage may JSON round-trip state).
|
|
187
|
+
let ledger = { entries: state.ledgerEntries };
|
|
188
|
+
for (const entry of applied.ledgerEntries ?? []) {
|
|
189
|
+
ledger = recordAssumption(ledger, {
|
|
190
|
+
field: entry.field,
|
|
191
|
+
value: entry.value,
|
|
192
|
+
...(entry.previousValue !== undefined ? { previousValue: entry.previousValue } : {}),
|
|
193
|
+
...(entry.source !== undefined ? { source: entry.source } : {}),
|
|
194
|
+
timestamp: entry.timestamp ?? now(),
|
|
195
|
+
actor: entry.actor ?? actor,
|
|
196
|
+
rationale: entry.rationale ?? rationale,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
trail: [
|
|
201
|
+
...state.trail,
|
|
202
|
+
{
|
|
203
|
+
stage: gate.stage,
|
|
204
|
+
decision: applied.summary ?? describeDecision(decision),
|
|
205
|
+
rationale,
|
|
206
|
+
skipped: false,
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
ledgerEntries: [...ledger.entries],
|
|
210
|
+
decisions: { ...state.decisions, [gate.id]: decision },
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
}));
|
|
214
|
+
const completeStep = createStep({
|
|
215
|
+
id: COMPLETE_STEP_ID,
|
|
216
|
+
inputSchema: chainStateSchema,
|
|
217
|
+
outputSchema: chainResultSchema,
|
|
218
|
+
execute: async ({ inputData, requestContext }) => {
|
|
219
|
+
const state = normalizeState(inputData);
|
|
220
|
+
// Rebuild through recordAssumption so the returned ledger is frozen and
|
|
221
|
+
// seq-consistent even after a JSON round-trip through snapshot storage.
|
|
222
|
+
let ledger = createLedger();
|
|
223
|
+
for (const entry of state.ledgerEntries) {
|
|
224
|
+
const { seq: _seq, ...fresh } = entry;
|
|
225
|
+
ledger = recordAssumption(ledger, fresh);
|
|
226
|
+
}
|
|
227
|
+
const outcome = { trail: state.trail, ledger };
|
|
228
|
+
await onComplete?.(outcome, {
|
|
229
|
+
requestContext,
|
|
230
|
+
now,
|
|
231
|
+
decisions: state.decisions,
|
|
232
|
+
trail: state.trail,
|
|
233
|
+
});
|
|
234
|
+
return outcome;
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
let builder = createWorkflow({
|
|
238
|
+
id,
|
|
239
|
+
inputSchema: chainInputSchema,
|
|
240
|
+
outputSchema: chainResultSchema,
|
|
241
|
+
...(requestContextSchema ? { requestContextSchema } : {}),
|
|
242
|
+
});
|
|
243
|
+
for (const step of gateSteps)
|
|
244
|
+
builder = builder.then(step);
|
|
245
|
+
return builder.then(completeStep).commit();
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=judgment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judgment.js","sourceRoot":"","sources":["../src/judgment.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actuarial tool factory: Mastra createTool with the two hard guarantees the
|
|
3
|
+
* ActNG server proved in production, generalized for any host.
|
|
4
|
+
*
|
|
5
|
+
* 1. SECURITY SEAM. The tenant id (project id) ALWAYS comes from the
|
|
6
|
+
* server-side request context, never from the model. tenantOf reads it;
|
|
7
|
+
* defineActuarialTool REJECTS, at definition time, any input schema that
|
|
8
|
+
* declares a tenant-id key - the model must not even be able to express
|
|
9
|
+
* one. (Verified against @mastra/core 1.49: tools execute as
|
|
10
|
+
* (inputData, context) with context.requestContext.get(key).)
|
|
11
|
+
*
|
|
12
|
+
* 2. ERROR CONTRACT. Tools never throw into the model. Anything the host's
|
|
13
|
+
* execute throws is converted to { success: false, error: { code, message } }
|
|
14
|
+
* so the agent can recover: retry with adjusted parameters, suggest an
|
|
15
|
+
* alternative, or ask. Errors carrying a string code (the server's
|
|
16
|
+
* HttpError, this package's AgentsError, compliance's ComplianceError)
|
|
17
|
+
* keep their code; everything else gets the fallback.
|
|
18
|
+
*/
|
|
19
|
+
import type { z } from "zod";
|
|
20
|
+
/** The uniform tool-failure shape: agents branch on success, hosts log code. */
|
|
21
|
+
export type ToolEnvelopeFailure = {
|
|
22
|
+
success: false;
|
|
23
|
+
error: {
|
|
24
|
+
code: string;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Converts anything thrown by a tool into the failure envelope. Never throws.
|
|
30
|
+
* Error-like values with a non-empty string "code" property (HttpError,
|
|
31
|
+
* AgentsError, ComplianceError) keep their code; everything else gets
|
|
32
|
+
* fallbackCode.
|
|
33
|
+
*/
|
|
34
|
+
export declare function envelopeFailure(err: unknown, fallbackCode?: string): ToolEnvelopeFailure;
|
|
35
|
+
/**
|
|
36
|
+
* The structural slice of Mastra's ToolExecutionContext that the tenant seam
|
|
37
|
+
* needs. Typed structurally (not as the concrete Mastra type) so tests can
|
|
38
|
+
* pass a minimal object and hosts on any 1.49+ patch level are assignable.
|
|
39
|
+
*/
|
|
40
|
+
export interface TenantToolContext {
|
|
41
|
+
requestContext?: {
|
|
42
|
+
get(key: string): unknown;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Reads the tenant id from the server-set request context. Throws a typed
|
|
47
|
+
* AgentsError("NO_TENANT_CONTEXT") when absent, non-string, or empty; inside
|
|
48
|
+
* a defineActuarialTool execute the wrapper converts that throw into the
|
|
49
|
+
* failure envelope, so the model sees a recoverable error, never a crash.
|
|
50
|
+
*/
|
|
51
|
+
export declare function tenantOf(context: TenantToolContext | undefined, key?: string): string;
|
|
52
|
+
/** Top-level shape keys of a zod object schema, or null when not an object schema. */
|
|
53
|
+
export declare function zodObjectShape(schema: unknown): Record<string, unknown> | null;
|
|
54
|
+
/** read = inspect/analyze only; action = mutates host state (drives client refresh). */
|
|
55
|
+
export type ActuarialToolKind = "read" | "action";
|
|
56
|
+
/**
|
|
57
|
+
* The execution context handed to an actuarial tool's execute. Structural on
|
|
58
|
+
* purpose: it is the slice of Mastra's ToolExecutionContext this package
|
|
59
|
+
* relies on, and tests exercise tools without booting an agent.
|
|
60
|
+
*/
|
|
61
|
+
export type ActuarialToolContext = TenantToolContext;
|
|
62
|
+
export interface DefineActuarialToolOptions<TShape extends z.ZodRawShape, TResult> {
|
|
63
|
+
id: string;
|
|
64
|
+
description: string;
|
|
65
|
+
kind: ActuarialToolKind;
|
|
66
|
+
/**
|
|
67
|
+
* The model-facing input schema. MUST NOT contain a tenant-id key
|
|
68
|
+
* (projectId/tenantId in any casing) - defineActuarialTool throws
|
|
69
|
+
* AgentsError("TENANT_IN_SCHEMA") at definition time if it does.
|
|
70
|
+
*/
|
|
71
|
+
inputSchema: z.ZodObject<TShape>;
|
|
72
|
+
/**
|
|
73
|
+
* The tool body. May throw freely (HttpError-style coded errors keep their
|
|
74
|
+
* code); the wrapper guarantees the model receives either the return value
|
|
75
|
+
* or a { success: false, error } envelope, never an exception.
|
|
76
|
+
*/
|
|
77
|
+
execute: (input: z.infer<z.ZodObject<TShape>>, context: ActuarialToolContext) => Promise<TResult>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Wraps Mastra's createTool with the envelope + tenant-seam guarantees and
|
|
81
|
+
* tags the result with its kind for toolRegistry classification.
|
|
82
|
+
*/
|
|
83
|
+
export declare function defineActuarialTool<TShape extends z.ZodRawShape, TResult>(options: DefineActuarialToolOptions<TShape, TResult>): import("@mastra/core/tools").Tool<z.objectUtil.addQuestionMarks<z.baseObjectOutputType<TShape>, any> extends infer T ? { [k in keyof T]: T[k]; } : never, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, string, unknown> & {
|
|
84
|
+
kind: ActuarialToolKind;
|
|
85
|
+
};
|
|
86
|
+
/** The minimal slice toolRegistry needs; every defineActuarialTool result satisfies it. */
|
|
87
|
+
export interface RegistrableActuarialTool {
|
|
88
|
+
id: string;
|
|
89
|
+
kind: ActuarialToolKind;
|
|
90
|
+
}
|
|
91
|
+
export interface ActuarialToolRegistry<T extends RegistrableActuarialTool> {
|
|
92
|
+
/** Tools keyed by id, ready for new Agent({ tools }). */
|
|
93
|
+
tools: Record<string, T>;
|
|
94
|
+
/** Ids of state-mutating tools (the host client refreshes after these). */
|
|
95
|
+
actionToolIds: Set<string>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Classifies a tool list into the shape hosts wire into their agent: a
|
|
99
|
+
* tools record keyed by id plus the action-tool id set that drives client
|
|
100
|
+
* refresh semantics (the generalization of the server's ACTION_TOOL_IDS).
|
|
101
|
+
*/
|
|
102
|
+
export declare function toolRegistry<T extends RegistrableActuarialTool>(tools: readonly T[]): ActuarialToolRegistry<T>;
|
|
103
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAM7B,gFAAgF;AAChF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,SAAe,GAAG,mBAAmB,CAkB9F;AAKD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE;QAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CAChD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,EAAE,GAAG,SAAc,GAAG,MAAM,CAS1F;AASD,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAO9E;AAoFD,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAElD;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAErD,MAAM,WAAW,0BAA0B,CAAC,MAAM,SAAS,CAAC,CAAC,WAAW,EAAE,OAAO;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,iBAAiB,CAAC;IACxB;;;;OAIG;IACH,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,OAAO,EAAE,CACP,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EACnC,OAAO,EAAE,oBAAoB,KAC1B,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,SAAS,CAAC,CAAC,WAAW,EAAE,OAAO,EACvE,OAAO,EAAE,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC;;EAyBrD;AAKD,2FAA2F;AAC3F,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,wBAAwB;IACvE,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACzB,2EAA2E;IAC3E,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,wBAAwB,EAC7D,KAAK,EAAE,SAAS,CAAC,EAAE,GAClB,qBAAqB,CAAC,CAAC,CAAC,CAc1B"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actuarial tool factory: Mastra createTool with the two hard guarantees the
|
|
3
|
+
* ActNG server proved in production, generalized for any host.
|
|
4
|
+
*
|
|
5
|
+
* 1. SECURITY SEAM. The tenant id (project id) ALWAYS comes from the
|
|
6
|
+
* server-side request context, never from the model. tenantOf reads it;
|
|
7
|
+
* defineActuarialTool REJECTS, at definition time, any input schema that
|
|
8
|
+
* declares a tenant-id key - the model must not even be able to express
|
|
9
|
+
* one. (Verified against @mastra/core 1.49: tools execute as
|
|
10
|
+
* (inputData, context) with context.requestContext.get(key).)
|
|
11
|
+
*
|
|
12
|
+
* 2. ERROR CONTRACT. Tools never throw into the model. Anything the host's
|
|
13
|
+
* execute throws is converted to { success: false, error: { code, message } }
|
|
14
|
+
* so the agent can recover: retry with adjusted parameters, suggest an
|
|
15
|
+
* alternative, or ask. Errors carrying a string code (the server's
|
|
16
|
+
* HttpError, this package's AgentsError, compliance's ComplianceError)
|
|
17
|
+
* keep their code; everything else gets the fallback.
|
|
18
|
+
*/
|
|
19
|
+
import { createTool } from "@mastra/core/tools";
|
|
20
|
+
import { AgentsError } from "./errors.js";
|
|
21
|
+
/**
|
|
22
|
+
* Converts anything thrown by a tool into the failure envelope. Never throws.
|
|
23
|
+
* Error-like values with a non-empty string "code" property (HttpError,
|
|
24
|
+
* AgentsError, ComplianceError) keep their code; everything else gets
|
|
25
|
+
* fallbackCode.
|
|
26
|
+
*/
|
|
27
|
+
export function envelopeFailure(err, fallbackCode = "TOOL_ERROR") {
|
|
28
|
+
let code = fallbackCode;
|
|
29
|
+
let message = "Unknown error";
|
|
30
|
+
try {
|
|
31
|
+
const candidate = err?.code;
|
|
32
|
+
if (typeof candidate === "string" && candidate.length > 0)
|
|
33
|
+
code = candidate;
|
|
34
|
+
if (err instanceof Error) {
|
|
35
|
+
message = err.message;
|
|
36
|
+
}
|
|
37
|
+
else if (typeof err === "string" && err.length > 0) {
|
|
38
|
+
message = err;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const msg = err?.message;
|
|
42
|
+
if (typeof msg === "string" && msg.length > 0)
|
|
43
|
+
message = msg;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// A hostile getter must not break the envelope; keep the fallbacks.
|
|
48
|
+
}
|
|
49
|
+
return { success: false, error: { code, message } };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Reads the tenant id from the server-set request context. Throws a typed
|
|
53
|
+
* AgentsError("NO_TENANT_CONTEXT") when absent, non-string, or empty; inside
|
|
54
|
+
* a defineActuarialTool execute the wrapper converts that throw into the
|
|
55
|
+
* failure envelope, so the model sees a recoverable error, never a crash.
|
|
56
|
+
*/
|
|
57
|
+
export function tenantOf(context, key = "projectId") {
|
|
58
|
+
const value = context?.requestContext?.get(key);
|
|
59
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
60
|
+
throw new AgentsError("NO_TENANT_CONTEXT", `Tool invoked without a "${key}" in the request context; the host must set it server-side from the authenticated request, never from the model`);
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Tenant-id keys a tool input schema may never declare. Case-insensitive and
|
|
66
|
+
* separator-tolerant (projectId, project_id, tenantId, TenantID, ...): the
|
|
67
|
+
* lint exists to make the security seam unexpressable, so it errs wide.
|
|
68
|
+
*/
|
|
69
|
+
const TENANT_KEY_PATTERN = /^(project|tenant)[_-]?id$/i;
|
|
70
|
+
/** Top-level shape keys of a zod object schema, or null when not an object schema. */
|
|
71
|
+
export function zodObjectShape(schema) {
|
|
72
|
+
if (typeof schema !== "object" || schema === null)
|
|
73
|
+
return null;
|
|
74
|
+
const def = schema._def;
|
|
75
|
+
if (def?.typeName !== "ZodObject")
|
|
76
|
+
return null;
|
|
77
|
+
const shape = schema.shape;
|
|
78
|
+
if (typeof shape !== "object" || shape === null)
|
|
79
|
+
return null;
|
|
80
|
+
return shape;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The recursive tenant-key lint behind defineActuarialTool. Walks every
|
|
84
|
+
* container the model could reach — nested objects, arrays, optional /
|
|
85
|
+
* nullable / default / effects wrappers, unions — and rejects:
|
|
86
|
+
* - any object key matching TENANT_KEY_PATTERN at any depth;
|
|
87
|
+
* - .passthrough() objects and non-never .catchall(...) (they let the model
|
|
88
|
+
* smuggle arbitrary keys past the lint);
|
|
89
|
+
* - z.record(...) (dynamic string keys — same smuggling surface).
|
|
90
|
+
* Leaves (strings, numbers, enums, literals) end recursion. Anything with
|
|
91
|
+
* an unrecognized CONTAINER shape simply is not traversed further; the
|
|
92
|
+
* fail-closed top-level check in defineActuarialTool guarantees the root is
|
|
93
|
+
* an inspectable plain object.
|
|
94
|
+
*/
|
|
95
|
+
function assertNoTenantKeys(schema, toolId, path) {
|
|
96
|
+
if (typeof schema !== "object" || schema === null)
|
|
97
|
+
return;
|
|
98
|
+
const def = schema._def;
|
|
99
|
+
if (!def)
|
|
100
|
+
return;
|
|
101
|
+
const typeName = def.typeName;
|
|
102
|
+
if (typeName === "ZodObject") {
|
|
103
|
+
if (def.unknownKeys === "passthrough") {
|
|
104
|
+
throw new AgentsError("TENANT_IN_SCHEMA", `Tool "${toolId}": the object at "${path}" uses .passthrough(), which lets the model smuggle arbitrary keys (including tenant ids) past the seam; declare every key explicitly`);
|
|
105
|
+
}
|
|
106
|
+
const catchallType = def.catchall?._def?.typeName;
|
|
107
|
+
if (catchallType !== undefined && catchallType !== "ZodNever") {
|
|
108
|
+
throw new AgentsError("TENANT_IN_SCHEMA", `Tool "${toolId}": the object at "${path}" uses .catchall(...), which admits undeclared keys; declare every key explicitly`);
|
|
109
|
+
}
|
|
110
|
+
const shape = zodObjectShape(schema) ?? {};
|
|
111
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
112
|
+
if (TENANT_KEY_PATTERN.test(key)) {
|
|
113
|
+
throw new AgentsError("TENANT_IN_SCHEMA", `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`);
|
|
114
|
+
}
|
|
115
|
+
assertNoTenantKeys(value, toolId, `${path}.${key}`);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (typeName === "ZodRecord") {
|
|
120
|
+
throw new AgentsError("TENANT_IN_SCHEMA", `Tool "${toolId}": the record at "${path}" admits arbitrary string keys (including tenant ids); use an explicit z.object shape`);
|
|
121
|
+
}
|
|
122
|
+
if (typeName === "ZodArray") {
|
|
123
|
+
assertNoTenantKeys(def.type, toolId, `${path}[]`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (typeName === "ZodOptional" || typeName === "ZodNullable" || typeName === "ZodDefault") {
|
|
127
|
+
assertNoTenantKeys(def.innerType, toolId, path);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (typeName === "ZodEffects") {
|
|
131
|
+
assertNoTenantKeys(def.schema, toolId, path);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (typeName === "ZodUnion" || typeName === "ZodDiscriminatedUnion") {
|
|
135
|
+
for (const opt of def.options ?? [])
|
|
136
|
+
assertNoTenantKeys(opt, toolId, path);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Wraps Mastra's createTool with the envelope + tenant-seam guarantees and
|
|
142
|
+
* tags the result with its kind for toolRegistry classification.
|
|
143
|
+
*/
|
|
144
|
+
export function defineActuarialTool(options) {
|
|
145
|
+
// FAIL CLOSED: a schema the seam cannot inspect is not definable, and the
|
|
146
|
+
// tenant-key lint recurses through every container the model could reach.
|
|
147
|
+
const shape = zodObjectShape(options.inputSchema);
|
|
148
|
+
if (!shape) {
|
|
149
|
+
throw new AgentsError("BAD_INPUT_SCHEMA", `Tool "${options.id}": inputSchema must be a plain z.object(...) the tenant seam can inspect; got a schema whose shape is unreadable`);
|
|
150
|
+
}
|
|
151
|
+
assertNoTenantKeys(options.inputSchema, options.id, "input");
|
|
152
|
+
const tool = createTool({
|
|
153
|
+
id: options.id,
|
|
154
|
+
description: options.description,
|
|
155
|
+
inputSchema: options.inputSchema,
|
|
156
|
+
execute: async (input, context) => {
|
|
157
|
+
try {
|
|
158
|
+
return await options.execute(input, context);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
return envelopeFailure(err);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
return Object.assign(tool, { kind: options.kind });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Classifies a tool list into the shape hosts wire into their agent: a
|
|
169
|
+
* tools record keyed by id plus the action-tool id set that drives client
|
|
170
|
+
* refresh semantics (the generalization of the server's ACTION_TOOL_IDS).
|
|
171
|
+
*/
|
|
172
|
+
export function toolRegistry(tools) {
|
|
173
|
+
const record = {};
|
|
174
|
+
const actionToolIds = new Set();
|
|
175
|
+
for (const tool of tools) {
|
|
176
|
+
if (record[tool.id]) {
|
|
177
|
+
throw new AgentsError("DUPLICATE_TOOL_ID", `Two tools share the id "${tool.id}"; tool ids must be unique within a registry`);
|
|
178
|
+
}
|
|
179
|
+
record[tool.id] = tool;
|
|
180
|
+
if (tool.kind === "action")
|
|
181
|
+
actionToolIds.add(tool.id);
|
|
182
|
+
}
|
|
183
|
+
return { tools: record, actionToolIds };
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@actuarial-ts/agents",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Mastra agent toolkit for the actuarial-ts SDK: typed actuarial tools with a hard tenant seam, human-gated judgment workflows that write the compliance assumption ledger, a reserving advisor factory, and a golden-prompt eval harness.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"NOTICE"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/yerromnitsuj/actng.git",
|
|
29
|
+
"directory": "packages/agents"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"actuarial",
|
|
33
|
+
"agents",
|
|
34
|
+
"mastra",
|
|
35
|
+
"ai",
|
|
36
|
+
"reserving",
|
|
37
|
+
"human-in-the-loop",
|
|
38
|
+
"tools"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.build.json",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"prepack": "tsc -p tsconfig.build.json"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@actuarial-ts/compliance": "^0.1.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"typescript": "^5.7.3",
|
|
52
|
+
"vitest": "^3.0.5",
|
|
53
|
+
"@mastra/core": "^1.49.0",
|
|
54
|
+
"zod": "^3.25.76"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@mastra/core": ">=1.49.0 <2",
|
|
58
|
+
"zod": "^3.25.0"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|