@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/evals.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Golden-prompt tool-selection eval harness: does a realistic instruction
|
|
3
|
+
* make the agent call the right tool(s)? Generalizes the ActNG server's
|
|
4
|
+
* eval-advisor script.
|
|
5
|
+
*
|
|
6
|
+
* Asserts tool SELECTION, not prose: each case lists tools that must appear
|
|
7
|
+
* among the turn's calls. Running against a real agent costs live API tokens
|
|
8
|
+
* and is opt-in by design - the harness itself is pure bookkeeping, which is
|
|
9
|
+
* why package tests exercise it with a stubbed agent and canned streams.
|
|
10
|
+
*
|
|
11
|
+
* Chunk shapes (verified against @mastra/core 1.49 fullStream): tool calls
|
|
12
|
+
* arrive as { type: "tool-call", payload: { toolName } }, with a legacy
|
|
13
|
+
* { type: "tool-call", toolName } fallback.
|
|
14
|
+
*/
|
|
15
|
+
export interface ToolSelectionEvalCase {
|
|
16
|
+
id: string;
|
|
17
|
+
prompt: string;
|
|
18
|
+
/** Tool names that must ALL appear among the turn's calls. */
|
|
19
|
+
expectTools: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export interface ToolSelectionCaseResult {
|
|
22
|
+
id: string;
|
|
23
|
+
pass: boolean;
|
|
24
|
+
/** Tool names actually called, in first-call order. */
|
|
25
|
+
called: string[];
|
|
26
|
+
/** Expected tools that never got called. */
|
|
27
|
+
missing: string[];
|
|
28
|
+
/** Stream error or timeout, when the case failed exceptionally. */
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ToolSelectionEvalReport {
|
|
32
|
+
results: ToolSelectionCaseResult[];
|
|
33
|
+
summary: {
|
|
34
|
+
total: number;
|
|
35
|
+
passed: number;
|
|
36
|
+
failed: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The structural slice of an agent the harness needs: stream(messages,
|
|
41
|
+
* options) resolving to something with an async-iterable fullStream.
|
|
42
|
+
*
|
|
43
|
+
* WHY structural instead of the concrete @mastra/core Agent class: the Agent
|
|
44
|
+
* type carries heavy generics and live-model plumbing the harness never
|
|
45
|
+
* touches; it only consumes the fullStream chunk feed. Typing the seam
|
|
46
|
+
* structurally lets package tests substitute a canned-stream stub (no LLM, no
|
|
47
|
+
* network) and lets hosts pass any Agent from any compatible Mastra patch
|
|
48
|
+
* level without generic gymnastics.
|
|
49
|
+
*/
|
|
50
|
+
export interface ToolStreamingAgent {
|
|
51
|
+
stream(messages: Array<{
|
|
52
|
+
role: "user";
|
|
53
|
+
content: string;
|
|
54
|
+
}>, options?: Record<string, unknown>): Promise<{
|
|
55
|
+
fullStream: AsyncIterable<unknown>;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
export interface RunToolSelectionEvalsOptions {
|
|
59
|
+
agent: ToolStreamingAgent;
|
|
60
|
+
cases: readonly ToolSelectionEvalCase[];
|
|
61
|
+
/** Forwarded verbatim to agent.stream options (the tenant seam for tools). */
|
|
62
|
+
requestContext?: unknown;
|
|
63
|
+
/** Max agent steps per case. Default 8. */
|
|
64
|
+
maxSteps?: number;
|
|
65
|
+
/** Per-case wall clock: a stalled stream fails the case, not the suite. Default 180000. */
|
|
66
|
+
timeoutMs?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Optional per-case memory option factory (thread/resource), for agents
|
|
69
|
+
* whose Memory configuration requires one on every stream call.
|
|
70
|
+
*/
|
|
71
|
+
memoryFor?: (evalCase: ToolSelectionEvalCase) => {
|
|
72
|
+
thread: string;
|
|
73
|
+
resource: string;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Runs every case sequentially against the agent, collecting the tool calls
|
|
78
|
+
* from the fullStream and asserting each case's expectTools is a subset of
|
|
79
|
+
* the tools actually called. A per-case Promise.race timeout keeps one
|
|
80
|
+
* stalled stream from hanging the whole suite.
|
|
81
|
+
*/
|
|
82
|
+
export declare function runToolSelectionEvals(options: RunToolSelectionEvalsOptions): Promise<ToolSelectionEvalReport>;
|
|
83
|
+
//# sourceMappingURL=evals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evals.d.ts","sourceRoot":"","sources":["../src/evals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,uDAAuD;IACvD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CACJ,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC;QAAE,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACxC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AAiBD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,uBAAuB,CAAC,CA+DlC"}
|
package/dist/evals.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Golden-prompt tool-selection eval harness: does a realistic instruction
|
|
3
|
+
* make the agent call the right tool(s)? Generalizes the ActNG server's
|
|
4
|
+
* eval-advisor script.
|
|
5
|
+
*
|
|
6
|
+
* Asserts tool SELECTION, not prose: each case lists tools that must appear
|
|
7
|
+
* among the turn's calls. Running against a real agent costs live API tokens
|
|
8
|
+
* and is opt-in by design - the harness itself is pure bookkeeping, which is
|
|
9
|
+
* why package tests exercise it with a stubbed agent and canned streams.
|
|
10
|
+
*
|
|
11
|
+
* Chunk shapes (verified against @mastra/core 1.49 fullStream): tool calls
|
|
12
|
+
* arrive as { type: "tool-call", payload: { toolName } }, with a legacy
|
|
13
|
+
* { type: "tool-call", toolName } fallback.
|
|
14
|
+
*/
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Harness
|
|
17
|
+
/** Extracts the tool name from a fullStream chunk, or null for non-tool-call chunks. */
|
|
18
|
+
function toolCallName(chunk) {
|
|
19
|
+
const c = chunk;
|
|
20
|
+
if (c?.type !== "tool-call")
|
|
21
|
+
return null;
|
|
22
|
+
const name = c.payload?.toolName ?? c.toolName;
|
|
23
|
+
return typeof name === "string" && name.length > 0 ? name : null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Runs every case sequentially against the agent, collecting the tool calls
|
|
27
|
+
* from the fullStream and asserting each case's expectTools is a subset of
|
|
28
|
+
* the tools actually called. A per-case Promise.race timeout keeps one
|
|
29
|
+
* stalled stream from hanging the whole suite.
|
|
30
|
+
*/
|
|
31
|
+
export async function runToolSelectionEvals(options) {
|
|
32
|
+
const { agent, cases, requestContext } = options;
|
|
33
|
+
const maxSteps = options.maxSteps ?? 8;
|
|
34
|
+
const timeoutMs = options.timeoutMs ?? 180_000;
|
|
35
|
+
const results = [];
|
|
36
|
+
for (const evalCase of cases) {
|
|
37
|
+
const called = new Set();
|
|
38
|
+
let error;
|
|
39
|
+
let timer;
|
|
40
|
+
// On timeout the underlying stream is CANCELLED, not abandoned: the
|
|
41
|
+
// iterator's return() is invoked so a well-behaved provider stops
|
|
42
|
+
// generating (and billing), and no chunks bleed into the next case.
|
|
43
|
+
// Hosts whose models accept an abort signal should prefer abortable
|
|
44
|
+
// models; the iterator return() is the portable floor.
|
|
45
|
+
let activeIterator;
|
|
46
|
+
try {
|
|
47
|
+
await Promise.race([
|
|
48
|
+
(async () => {
|
|
49
|
+
const stream = await agent.stream([{ role: "user", content: evalCase.prompt }], {
|
|
50
|
+
...(requestContext !== undefined ? { requestContext } : {}),
|
|
51
|
+
maxSteps,
|
|
52
|
+
...(options.memoryFor ? { memory: options.memoryFor(evalCase) } : {}),
|
|
53
|
+
});
|
|
54
|
+
const iterator = stream.fullStream[Symbol.asyncIterator]();
|
|
55
|
+
activeIterator = iterator;
|
|
56
|
+
for (;;) {
|
|
57
|
+
const next = await iterator.next();
|
|
58
|
+
if (next.done)
|
|
59
|
+
break;
|
|
60
|
+
const name = toolCallName(next.value);
|
|
61
|
+
if (name)
|
|
62
|
+
called.add(name);
|
|
63
|
+
}
|
|
64
|
+
})(),
|
|
65
|
+
new Promise((_, reject) => {
|
|
66
|
+
timer = setTimeout(() => reject(new Error(`case timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
67
|
+
}),
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
error = err instanceof Error ? err.message : String(err);
|
|
72
|
+
// Best-effort cancellation of a stream that outlived its race.
|
|
73
|
+
void activeIterator?.return?.().catch(() => undefined);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
if (timer !== undefined)
|
|
77
|
+
clearTimeout(timer);
|
|
78
|
+
}
|
|
79
|
+
const missing = evalCase.expectTools.filter((tool) => !called.has(tool));
|
|
80
|
+
const result = {
|
|
81
|
+
id: evalCase.id,
|
|
82
|
+
pass: error === undefined && missing.length === 0,
|
|
83
|
+
called: [...called],
|
|
84
|
+
missing,
|
|
85
|
+
...(error !== undefined ? { error } : {}),
|
|
86
|
+
};
|
|
87
|
+
results.push(result);
|
|
88
|
+
}
|
|
89
|
+
const passed = results.filter((r) => r.pass).length;
|
|
90
|
+
return {
|
|
91
|
+
results,
|
|
92
|
+
summary: { total: results.length, passed, failed: results.length - passed },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=evals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evals.js","sourceRoot":"","sources":["../src/evals.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,208 @@
|
|
|
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 { createWorkflow } from "@mastra/core/workflows";
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
import { type AssumptionActor, type AssumptionLedger, type JsonValue } from "@actuarial-ts/compliance";
|
|
40
|
+
/** One decision in the audit trail; skip gates record skipped: true. */
|
|
41
|
+
export interface JudgmentTrailEntry {
|
|
42
|
+
stage: string;
|
|
43
|
+
decision: string;
|
|
44
|
+
rationale: string;
|
|
45
|
+
skipped: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* What earlier gates decided, keyed by gate id: the validated resume payload
|
|
49
|
+
* for decided gates, or { skipped: true, reason } for self-skipped ones.
|
|
50
|
+
* Available to skipWhen/gatherEvidence/applyDecision so a later gate can
|
|
51
|
+
* self-skip on an earlier decision (the server's ilf gate skips when the cap
|
|
52
|
+
* gate chose to stay unlimited).
|
|
53
|
+
*/
|
|
54
|
+
export type JudgmentDecisions = Record<string, unknown>;
|
|
55
|
+
/** Marker recorded into the decisions map when a gate self-skips. */
|
|
56
|
+
export interface JudgmentSkipRecord {
|
|
57
|
+
skipped: true;
|
|
58
|
+
reason: string;
|
|
59
|
+
}
|
|
60
|
+
/** The context every gate hook receives. */
|
|
61
|
+
export interface JudgmentGateContext {
|
|
62
|
+
/** The workflow request context (tenant id lives here, never in inputs). */
|
|
63
|
+
requestContext: {
|
|
64
|
+
get(key: string): unknown;
|
|
65
|
+
};
|
|
66
|
+
/** Host-injected clock; the source of every ledger timestamp. */
|
|
67
|
+
now: () => string;
|
|
68
|
+
/** Decisions from earlier gates, keyed by gate id. */
|
|
69
|
+
decisions: JudgmentDecisions;
|
|
70
|
+
/** The trail accumulated so far. */
|
|
71
|
+
trail: readonly JudgmentTrailEntry[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A ledger entry as a gate's applyDecision returns it: timestamp, actor, and
|
|
75
|
+
* rationale may be omitted - the chain fills timestamp from ctx.now(), actor
|
|
76
|
+
* from the resume payload's optional actor field (default "actuary"), and
|
|
77
|
+
* rationale from the decision's verbatim rationale.
|
|
78
|
+
*/
|
|
79
|
+
export interface JudgmentLedgerEntry {
|
|
80
|
+
/** Dotted assumption path, e.g. "layer.cap". */
|
|
81
|
+
field: string;
|
|
82
|
+
value: JsonValue;
|
|
83
|
+
previousValue?: JsonValue;
|
|
84
|
+
source?: string;
|
|
85
|
+
rationale?: string;
|
|
86
|
+
timestamp?: string;
|
|
87
|
+
actor?: AssumptionActor;
|
|
88
|
+
}
|
|
89
|
+
/** What applyDecision hands back to the chain. */
|
|
90
|
+
export interface JudgmentApplication {
|
|
91
|
+
/** Assumptions this decision fixed; appended to the threaded ledger. */
|
|
92
|
+
ledgerEntries?: JudgmentLedgerEntry[];
|
|
93
|
+
/** Human-readable decision text for the trail; derived from the payload when omitted. */
|
|
94
|
+
summary?: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* One human judgment gate. TDecision is the validated resume payload shape;
|
|
98
|
+
* its schema MUST declare a rationale key (createJudgmentChain rejects the
|
|
99
|
+
* spec otherwise) and SHOULD make it a non-empty string - either way the
|
|
100
|
+
* chain rejects blank rationales at runtime.
|
|
101
|
+
*/
|
|
102
|
+
export interface JudgmentGateSpec<TDecision = unknown> {
|
|
103
|
+
/** Step id; also the resume target (run.resume({ step: id, ... })). */
|
|
104
|
+
id: string;
|
|
105
|
+
/** Stage label surfaced in the suspend payload and the trail. */
|
|
106
|
+
stage: string;
|
|
107
|
+
/** Validates the resume payload. Must contain a rationale key. */
|
|
108
|
+
resumeSchema: z.ZodType<TDecision>;
|
|
109
|
+
/**
|
|
110
|
+
* Returns the skip reason when the gate should pass through without a
|
|
111
|
+
* human decision (e.g. a later gate made moot by an earlier decision),
|
|
112
|
+
* null to proceed. A skipped gate records a trail note and never suspends.
|
|
113
|
+
*/
|
|
114
|
+
skipWhen?: (ctx: JudgmentGateContext) => string | null;
|
|
115
|
+
/** Gathers the evidence and recommendation the gate suspends with. */
|
|
116
|
+
gatherEvidence: (ctx: JudgmentGateContext) => Promise<{
|
|
117
|
+
recommendation: string;
|
|
118
|
+
evidence: unknown;
|
|
119
|
+
}> | {
|
|
120
|
+
recommendation: string;
|
|
121
|
+
evidence: unknown;
|
|
122
|
+
};
|
|
123
|
+
/** Applies the human decision through the host's service layer. */
|
|
124
|
+
applyDecision: (ctx: JudgmentGateContext, decision: TDecision) => Promise<JudgmentApplication>;
|
|
125
|
+
}
|
|
126
|
+
/** The completed chain's output: the audit trail and the fused compliance ledger. */
|
|
127
|
+
export interface JudgmentChainOutcome {
|
|
128
|
+
trail: JudgmentTrailEntry[];
|
|
129
|
+
/** Ready for @actuarial-ts/compliance generateDisclosure. */
|
|
130
|
+
ledger: AssumptionLedger;
|
|
131
|
+
}
|
|
132
|
+
export interface CreateJudgmentChainOptions {
|
|
133
|
+
/** Workflow id. */
|
|
134
|
+
id: string;
|
|
135
|
+
/** The gates, in order. Must be non-empty with unique ids. */
|
|
136
|
+
gates: readonly JudgmentGateSpec<any>[];
|
|
137
|
+
/**
|
|
138
|
+
* Host-injected clock for ledger timestamps (purity: this package never
|
|
139
|
+
* reads Date). Return an ISO-8601 string.
|
|
140
|
+
*/
|
|
141
|
+
now: () => string;
|
|
142
|
+
/** Runs once after the last gate, before the chain returns its outcome. */
|
|
143
|
+
onComplete?: (outcome: JudgmentChainOutcome, ctx: JudgmentGateContext) => Promise<void> | void;
|
|
144
|
+
/** Optional request-context validation (e.g. z.object({ projectId: z.string() })). */
|
|
145
|
+
requestContextSchema?: z.ZodTypeAny;
|
|
146
|
+
}
|
|
147
|
+
declare const chainInputSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
148
|
+
declare const chainResultSchema: z.ZodObject<{
|
|
149
|
+
trail: z.ZodArray<z.ZodObject<{
|
|
150
|
+
stage: z.ZodString;
|
|
151
|
+
decision: z.ZodString;
|
|
152
|
+
rationale: z.ZodString;
|
|
153
|
+
skipped: z.ZodBoolean;
|
|
154
|
+
}, "strip", z.ZodTypeAny, {
|
|
155
|
+
stage: string;
|
|
156
|
+
decision: string;
|
|
157
|
+
rationale: string;
|
|
158
|
+
skipped: boolean;
|
|
159
|
+
}, {
|
|
160
|
+
stage: string;
|
|
161
|
+
decision: string;
|
|
162
|
+
rationale: string;
|
|
163
|
+
skipped: boolean;
|
|
164
|
+
}>, "many">;
|
|
165
|
+
ledger: z.ZodType<AssumptionLedger, z.ZodTypeDef, AssumptionLedger>;
|
|
166
|
+
}, "strip", z.ZodTypeAny, {
|
|
167
|
+
trail: {
|
|
168
|
+
stage: string;
|
|
169
|
+
decision: string;
|
|
170
|
+
rationale: string;
|
|
171
|
+
skipped: boolean;
|
|
172
|
+
}[];
|
|
173
|
+
ledger: AssumptionLedger;
|
|
174
|
+
}, {
|
|
175
|
+
trail: {
|
|
176
|
+
stage: string;
|
|
177
|
+
decision: string;
|
|
178
|
+
rationale: string;
|
|
179
|
+
skipped: boolean;
|
|
180
|
+
}[];
|
|
181
|
+
ledger: AssumptionLedger;
|
|
182
|
+
}>;
|
|
183
|
+
/**
|
|
184
|
+
* The committed workflow type hosts receive: register it on a Mastra instance
|
|
185
|
+
* for snapshot storage, then createRun/start/resume exactly like any Mastra
|
|
186
|
+
* workflow. (Instantiation expression pins the concrete schema generics; the
|
|
187
|
+
* step list is dynamic, so the per-step chain typing is erased - see the cast
|
|
188
|
+
* at the bottom of createJudgmentChain.)
|
|
189
|
+
*/
|
|
190
|
+
export type JudgmentChainWorkflow = ReturnType<typeof createWorkflow<string, typeof chainInputSchema, typeof chainResultSchema>>;
|
|
191
|
+
/**
|
|
192
|
+
* Builds a committed Mastra workflow from an ordered gate list. Each gate:
|
|
193
|
+
* skipWhen? -> pass through with a trail note; else gatherEvidence ->
|
|
194
|
+
* suspend({ stage, recommendation, evidence }); on resume -> validate the
|
|
195
|
+
* decision (schema + non-blank rationale), applyDecision, append the
|
|
196
|
+
* resulting assumptions to the threaded ledger. The terminal step rebuilds
|
|
197
|
+
* the frozen ledger, invokes onComplete, and returns { trail, ledger }.
|
|
198
|
+
*
|
|
199
|
+
* FOOTGUN (learned the hard way): a Mastra Workflow exposes a .then(step)
|
|
200
|
+
* builder method, which makes it an ACCIDENTAL THENABLE. Never await the
|
|
201
|
+
* returned workflow and never return it from an async function - JS will
|
|
202
|
+
* call .then(resolve, reject) with the promise resolver as a "step" and the
|
|
203
|
+
* promise never settles. Assign it synchronously and register it on your
|
|
204
|
+
* Mastra instance.
|
|
205
|
+
*/
|
|
206
|
+
export declare function createJudgmentChain(options: CreateJudgmentChainOptions): JudgmentChainWorkflow;
|
|
207
|
+
export {};
|
|
208
|
+
//# sourceMappingURL=judgment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judgment.d.ts","sourceRoot":"","sources":["../src/judgment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAc,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACf,MAAM,0BAA0B,CAAC;AAOlC,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAExD,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,cAAc,EAAE;QAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC9C,iEAAiE;IACjE,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,sDAAsD;IACtD,SAAS,EAAE,iBAAiB,CAAC;IAC7B,oCAAoC;IACpC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,aAAa,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACtC,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,SAAS,GAAG,OAAO;IACnD,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,MAAM,GAAG,IAAI,CAAC;IACvD,sEAAsE;IACtE,cAAc,EAAE,CACd,GAAG,EAAE,mBAAmB,KACrB,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAC5G,mEAAmE;IACnE,aAAa,EAAE,CAAC,GAAG,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAChG;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,6DAA6D;IAC7D,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,8DAA8D;IAC9D,KAAK,EAAE,SAAS,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;IACxC;;;OAGG;IACH,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CACX,OAAO,EAAE,oBAAoB,EAC7B,GAAG,EAAE,mBAAmB,KACrB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,sFAAsF;IACtF,oBAAoB,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;CACrC;AAqCD,QAAA,MAAM,gBAAgB,gDAAe,CAAC;AACtC,QAAA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQrB,CAAC;AA6CH;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAC5C,OAAO,cAAc,CAAC,MAAM,EAAE,OAAO,gBAAgB,EAAE,OAAO,iBAAiB,CAAC,CACjF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,qBAAqB,CAmJ9F"}
|