@actuarial-ts/agents 0.1.0 → 0.2.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/dist/divergence.d.ts +267 -0
- package/dist/divergence.d.ts.map +1 -0
- package/dist/divergence.js +409 -0
- package/dist/divergence.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +130 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +138 -0
- package/dist/mcp.js.map +1 -0
- package/dist/promotion.d.ts +269 -0
- package/dist/promotion.d.ts.map +1 -0
- package/dist/promotion.js +776 -0
- package/dist/promotion.js.map +1 -0
- package/dist/remote.d.ts +221 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +285 -0
- package/dist/remote.js.map +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +7 -0
- package/dist/tools.js.map +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAuB,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAKhE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAe,SAAQ,oBAAoB;IAC1D,GAAG,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;CACnC;AA8BD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,EAAE,GAAG,SAAc,GAAG,MAAM,CAU/F;AAKD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CACT,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,gBAAgB,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GACzF,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,iGAAiG;IACjG,MAAM,EAAE,aAAa,CAAC;IACtB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,eAAe,CAAC;CACrC;AA0BD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBtF"}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tenant seam + boot self-test for the governed workspace.
|
|
3
|
+
*
|
|
4
|
+
* When the workspace is exposed over the Model Context Protocol, external AI
|
|
5
|
+
* clients read everything and mutate nothing directly. The one hard guarantee
|
|
6
|
+
* is that EVERY exposed tool resolves its tenant (project id) from the MCP
|
|
7
|
+
* request's auth info, never from the model — the same secureToolWrapper rule
|
|
8
|
+
* the ActNG server proved in production, restated for the MCP transport.
|
|
9
|
+
*
|
|
10
|
+
* @mastra/mcp gives no built-in per-tool authorization outside its EE FGA path,
|
|
11
|
+
* so a missed wire-up FAILS OPEN: a tool that forgets to read authInfo would
|
|
12
|
+
* happily serve an unauthenticated caller. These two helpers close that caveat:
|
|
13
|
+
*
|
|
14
|
+
* 1. requireMcpTenant — the read side. A tool calls it instead of trusting the
|
|
15
|
+
* model; absent auth info it THROWS AgentsError("NO_TENANT_CONTEXT"), which
|
|
16
|
+
* the defineActuarialTool wrapper turns into a { success:false } envelope.
|
|
17
|
+
* A tool built on it fails CLOSED.
|
|
18
|
+
*
|
|
19
|
+
* 2. assertFailClosed — the proof. At server startup, drive a probe read tool
|
|
20
|
+
* through the server WITHOUT auth and assert it fails closed. If the probe
|
|
21
|
+
* SUCCEEDS, the seam is not wired up: this throws MCP_SELF_TEST_FAILED and
|
|
22
|
+
* startup MUST abort.
|
|
23
|
+
*
|
|
24
|
+
* ---------------------------------------------------------------------------
|
|
25
|
+
* VERIFIED against the installed @mastra/mcp 1.14.0 (house rule — types and
|
|
26
|
+
* compiled source, not memory):
|
|
27
|
+
*
|
|
28
|
+
* - MCPServer.executeTool(toolId, args, executionContext?: { messages?,
|
|
29
|
+
* toolCallId?, requestContext? }): Promise<any> returns the tool's execute
|
|
30
|
+
* result VERBATIM (dist/index.js: `const result = await tool.execute(...);
|
|
31
|
+
* return result;`). For a defineActuarialTool with no outputSchema the core
|
|
32
|
+
* tool builder passes the value through untouched, so an executeTool caller
|
|
33
|
+
* sees exactly the { success:false, error:{ code } } envelope the wrapper
|
|
34
|
+
* produced. Argument validation runs FIRST and, on failure, short-circuits
|
|
35
|
+
* to a { error:true, message } object before the tool ever executes — hence
|
|
36
|
+
* assertFailClosed passes minimal VALID probe args.
|
|
37
|
+
*
|
|
38
|
+
* - Auth-context access path. On the real streamable-HTTP call path the SDK
|
|
39
|
+
* copies the transport `extra` (with `authInfo` from `req.auth`) into the
|
|
40
|
+
* tool context TWO ways: directly at `context.mcp.extra`, and — via
|
|
41
|
+
* createProxiedRequestContext — as INDIVIDUAL keys set on a fresh
|
|
42
|
+
* RequestContext (`context.requestContext.get("authInfo")`). NOTE the
|
|
43
|
+
* surprise: the installed 1.14.0 sets each extra key on the RequestContext
|
|
44
|
+
* verbatim, so the tenant lives at `requestContext.get("authInfo")`, NOT
|
|
45
|
+
* under a single `"mcp.extra"` key as the research draft documented. This
|
|
46
|
+
* helper tries all three shapes so it is correct against both the installed
|
|
47
|
+
* package and the documented pattern.
|
|
48
|
+
*/
|
|
49
|
+
import { AgentsError } from "./errors.js";
|
|
50
|
+
/**
|
|
51
|
+
* Resolves the MCP auth-info bag from a tool context, trying every shape the
|
|
52
|
+
* installed and documented transports expose (see the file header). Returns
|
|
53
|
+
* undefined when no auth info is present — the caller decides that is fatal.
|
|
54
|
+
*/
|
|
55
|
+
function resolveMcpAuthInfo(context) {
|
|
56
|
+
if (!context)
|
|
57
|
+
return undefined;
|
|
58
|
+
// Primary: the streamable-HTTP call path passes the transport extra at
|
|
59
|
+
// context.mcp.extra (mcpOptions.mcp.extra in @mastra/mcp).
|
|
60
|
+
const directAuthInfo = context.mcp?.extra?.authInfo;
|
|
61
|
+
if (directAuthInfo)
|
|
62
|
+
return directAuthInfo;
|
|
63
|
+
const requestContext = context.requestContext;
|
|
64
|
+
if (requestContext && typeof requestContext.get === "function") {
|
|
65
|
+
// Documented universal fallback: the whole extra bag under "mcp.extra".
|
|
66
|
+
const proxiedExtra = requestContext.get("mcp.extra");
|
|
67
|
+
if (proxiedExtra?.authInfo)
|
|
68
|
+
return proxiedExtra.authInfo;
|
|
69
|
+
// Installed @mastra/mcp 1.14.0: createProxiedRequestContext copies each
|
|
70
|
+
// extra key onto the RequestContext verbatim, so authInfo is top-level.
|
|
71
|
+
const topLevelAuthInfo = requestContext.get("authInfo");
|
|
72
|
+
if (topLevelAuthInfo)
|
|
73
|
+
return topLevelAuthInfo;
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Reads the tenant id (default key "projectId") from the MCP execution
|
|
79
|
+
* context's auth info — the server-set identity, never the model. THROWS a
|
|
80
|
+
* typed AgentsError("NO_TENANT_CONTEXT") when the auth info or the key is
|
|
81
|
+
* absent, non-string, or empty. Inside a defineActuarialTool execute the
|
|
82
|
+
* wrapper converts that throw into a { success:false } envelope, so a tool
|
|
83
|
+
* built on requireMcpTenant fails CLOSED for any unauthenticated MCP caller.
|
|
84
|
+
*/
|
|
85
|
+
export function requireMcpTenant(context, key = "projectId") {
|
|
86
|
+
const authInfo = resolveMcpAuthInfo(context);
|
|
87
|
+
const value = authInfo?.[key];
|
|
88
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
89
|
+
throw new AgentsError("NO_TENANT_CONTEXT", `MCP tool invoked without a non-empty "${key}" in the request's authInfo; the host's bearer-token middleware must set req.auth = { ${key} } so it reaches context.mcp.extra.authInfo — the tenant never comes from the model`);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
/** Structural check for the { success:false, error:{ code } } tool-failure envelope. */
|
|
94
|
+
function isFailureEnvelope(result) {
|
|
95
|
+
if (typeof result !== "object" || result === null)
|
|
96
|
+
return false;
|
|
97
|
+
if (result.success !== false)
|
|
98
|
+
return false;
|
|
99
|
+
const error = result.error;
|
|
100
|
+
return typeof error?.code === "string";
|
|
101
|
+
}
|
|
102
|
+
/** A short, log-safe description of an unexpected probe result for the abort message. */
|
|
103
|
+
function describeResult(result) {
|
|
104
|
+
if (isFailureEnvelope(result)) {
|
|
105
|
+
return `a { success: false } envelope with code "${result.error.code}"`;
|
|
106
|
+
}
|
|
107
|
+
if (typeof result === "object" && result !== null) {
|
|
108
|
+
if (result.error === true) {
|
|
109
|
+
return "an argument-validation error (the probe never reached the tenant check — pass valid probeArgs)";
|
|
110
|
+
}
|
|
111
|
+
if (result.success === true) {
|
|
112
|
+
return "a { success: true } result (the probe SUCCEEDED without tenant context — the seam is not wired up)";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return "a non-envelope result (the probe did not fail closed on tenant context)";
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The boot self-test. Drives {@link AssertFailClosedOptions.probeToolId}
|
|
119
|
+
* through the server WITHOUT any auth info and asserts it fails closed with the
|
|
120
|
+
* tenant error code. THROWS AgentsError("MCP_SELF_TEST_FAILED") loudly for any
|
|
121
|
+
* other outcome — most importantly if the probe SUCCEEDED, which means the tool
|
|
122
|
+
* did not require tenant context and the MCP tenant seam is a fail-open hole.
|
|
123
|
+
*
|
|
124
|
+
* Call this at server startup whenever MCP is enabled, and ABORT startup if it
|
|
125
|
+
* throws: a governed workspace must never accept unauthenticated MCP clients.
|
|
126
|
+
*/
|
|
127
|
+
export async function assertFailClosed(options) {
|
|
128
|
+
const { server, probeToolId, probeArgs = {}, expectedErrorCode = "NO_TENANT_CONTEXT" } = options;
|
|
129
|
+
// No requestContext, no mcp.extra -> the probe sees no auth info at all.
|
|
130
|
+
const result = await server.executeTool(probeToolId, probeArgs, {});
|
|
131
|
+
if (isFailureEnvelope(result) && result.error.code === expectedErrorCode) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
throw new AgentsError("MCP_SELF_TEST_FAILED", `MCP boot self-test failed: probe tool "${probeToolId}" did not fail closed without auth. ` +
|
|
135
|
+
`Expected a { success: false, error.code: "${expectedErrorCode}" } envelope but got ${describeResult(result)}. ` +
|
|
136
|
+
`An MCP-exposed read tool must resolve its tenant with requireMcpTenant so an unauthenticated call is rejected; this outcome means the tenant seam is not wired up. Abort startup.`);
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAGH,OAAO,EAAE,WAAW,EAAwB,MAAM,aAAa,CAAC;AAkChE;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,OAAmC;IAC7D,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAE/B,uEAAuE;IACvE,2DAA2D;IAC3D,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC;IACpD,IAAI,cAAc;QAAE,OAAO,cAAc,CAAC;IAE1C,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC9C,IAAI,cAAc,IAAI,OAAO,cAAc,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QAC/D,wEAAwE;QACxE,MAAM,YAAY,GAAG,cAAc,CAAC,GAAG,CAAC,WAAW,CAAgC,CAAC;QACpF,IAAI,YAAY,EAAE,QAAQ;YAAE,OAAO,YAAY,CAAC,QAAQ,CAAC;QAEzD,wEAAwE;QACxE,wEAAwE;QACxE,MAAM,gBAAgB,GAAG,cAAc,CAAC,GAAG,CAAC,UAAU,CAA4B,CAAC;QACnF,IAAI,gBAAgB;YAAE,OAAO,gBAAgB,CAAC;IAChD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAmC,EAAE,GAAG,GAAG,WAAW;IACrF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,WAAW,CACnB,mBAAmB,EACnB,yCAAyC,GAAG,yFAAyF,GAAG,qFAAqF,CAC9N,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAsCD,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,MAAe;IACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,IAAK,MAAgC,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,KAAK,GAAI,MAAyC,CAAC,KAAK,CAAC;IAC/D,OAAO,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC;AACzC,CAAC;AAED,yFAAyF;AACzF,SAAS,cAAc,CAAC,MAAe;IACrC,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,4CAA4C,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;IAC1E,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,IAAK,MAA8B,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACnD,OAAO,gGAAgG,CAAC;QAC1G,CAAC;QACD,IAAK,MAAgC,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACvD,OAAO,oGAAoG,CAAC;QAC9G,CAAC;IACH,CAAC;IACD,OAAO,yEAAyE,CAAC;AACnF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAgC;IACrE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,GAAG,EAAE,EAAE,iBAAiB,GAAG,mBAAmB,EAAE,GAAG,OAAO,CAAC;IAEjG,yEAAyE;IACzE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAEpE,IAAI,iBAAiB,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACzE,OAAO;IACT,CAAC;IAED,MAAM,IAAI,WAAW,CACnB,sBAAsB,EACtB,0CAA0C,WAAW,sCAAsC;QACzF,6CAA6C,iBAAiB,wBAAwB,cAAc,CAAC,MAAM,CAAC,IAAI;QAChH,mLAAmL,CACtL,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* promoteStudy: notebook study -> governed judgment (interchange spec rev 2.1,
|
|
3
|
+
* Section 6). A four-gate judgment chain built on createJudgmentChain:
|
|
4
|
+
*
|
|
5
|
+
* Gate 1 "study-intake" - schema/integrity validation via interchange
|
|
6
|
+
* parseDocument, ASOP 23 data review on the study's
|
|
7
|
+
* triangles, selection coherence (spec 3.2 rule),
|
|
8
|
+
* segment resolution (v1: one selection per
|
|
9
|
+
* segment), and the tolerance ceiling.
|
|
10
|
+
* Gate 2 "replay-verify" - table-exact intents replayed through
|
|
11
|
+
* interchange/core; approx/value-only targets
|
|
12
|
+
* labeled verified-by-value; supportingResults
|
|
13
|
+
* refereed via crosscheck at the effective
|
|
14
|
+
* tolerance; a "disagree" verdict HARD-BLOCKS.
|
|
15
|
+
* Gate 3 "rationale" - a DRAFT rationale (template-assembled from the
|
|
16
|
+
* study narrative; options.draftRationale is the
|
|
17
|
+
* injection hook for future agent drafting); resume
|
|
18
|
+
* REQUIRES a non-blank rationale AND attestation.
|
|
19
|
+
* Gate 4 "apply" - deps.applySelections per selection,
|
|
20
|
+
* deps.runAnalysis, ledger entries carrying the
|
|
21
|
+
* Gate 3 rationale + attestation verbatim, and
|
|
22
|
+
* deps.persistNote with the trail + ledger JSON.
|
|
23
|
+
*
|
|
24
|
+
* Architecture decisions (each is load-bearing):
|
|
25
|
+
*
|
|
26
|
+
* - EAGER, DETERMINISTIC INTAKE. All Gate 1/2 computation (parse, review,
|
|
27
|
+
* coherence, segment resolution, replay, crosschecks) runs synchronously at
|
|
28
|
+
* chain-CONSTRUCTION time. Everything is a pure function of the study
|
|
29
|
+
* document plus deps.resolveSegment, so a host that reconstructs the chain
|
|
30
|
+
* after a restart (promoteStudy again with the same study) gets IDENTICAL
|
|
31
|
+
* gates and resume schemas - which is what makes the Gate 2 hard-block
|
|
32
|
+
* structural: when any referee verdict is "disagree", the replay-verify
|
|
33
|
+
* resume schema literally only admits { decision: "abort" }. Mastra's own
|
|
34
|
+
* resume validation rejects an accept; no runtime flag to lose across a
|
|
35
|
+
* snapshot rehydration, no advisory check an agent could talk its way past.
|
|
36
|
+
*
|
|
37
|
+
* - STUDY-CONTENT FAILURES SURFACE AS GATE FAILURES. A malformed study
|
|
38
|
+
* (BAD_INTERCHANGE), a tolerance above the host ceiling
|
|
39
|
+
* (TOLERANCE_CEILING_EXCEEDED), an unresolvable or ambiguous segment
|
|
40
|
+
* (SEGMENT_UNRESOLVED / SEGMENT_AMBIGUOUS), an incoherent selection under
|
|
41
|
+
* refuse strictness (INCOHERENT_SELECTION), or an empty study (EMPTY_STUDY)
|
|
42
|
+
* never throws out of promoteStudy itself: the captured error is thrown
|
|
43
|
+
* from Gate 1's gatherEvidence, so the run fails AT the study-intake gate
|
|
44
|
+
* with the named error, where the host's gate UI can render it.
|
|
45
|
+
* promoteStudy throws synchronously only for programmer errors
|
|
46
|
+
* (BAD_PROMOTION_OPTIONS: missing deps, non-positive ceiling, ...).
|
|
47
|
+
*
|
|
48
|
+
* - DATA REVIEW CHOICE (documented per plan task B2): where a segment group
|
|
49
|
+
* contains exactly one paid and one incurred triangle, the full
|
|
50
|
+
* @actuarial-ts/data reviewTriangles pair review runs (it includes the
|
|
51
|
+
* per-triangle monotonicity and interior-missing checks). Every other core
|
|
52
|
+
* loss triangle gets the per-triangle STRUCTURAL subset of those checks
|
|
53
|
+
* (non-decreasing cumulative rows, interior missing cells), reported in the
|
|
54
|
+
* same DataReviewReport shape. Non-core measures (earnedPremium, custom:*)
|
|
55
|
+
* are exposure/reference data, not loss triangles: their review is reported
|
|
56
|
+
* "not-evaluated" rather than silently passed - unless a selection
|
|
57
|
+
* references one, which fails intake (BAD_INTERCHANGE from the converter).
|
|
58
|
+
* Review findings, including fail-status checks, are EVIDENCE for the human
|
|
59
|
+
* intake decision, not automatic blocks; only the named error classes above
|
|
60
|
+
* block mechanically. The recommendation says so when checks fail.
|
|
61
|
+
*
|
|
62
|
+
* - PURITY. This module never reads a clock; options.now supplies every
|
|
63
|
+
* timestamp (envelope createdAt at construction, ledger timestamps at
|
|
64
|
+
* resume). The tenant seam is untouched: deps close over the host's own
|
|
65
|
+
* authenticated context, and nothing model-facing exists here at all.
|
|
66
|
+
*
|
|
67
|
+
* FOOTGUN (inherited from createJudgmentChain): the returned workflow exposes
|
|
68
|
+
* .then(step), making it an ACCIDENTAL THENABLE. Never await promoteStudy's
|
|
69
|
+
* return value and never return it from an async function - assign it
|
|
70
|
+
* synchronously and register it on a Mastra instance.
|
|
71
|
+
*/
|
|
72
|
+
import { type LdfSelections } from "@actuarial-ts/core";
|
|
73
|
+
import { type CoherenceCheck, type CrosscheckReportDoc, type SelectionDoc, type StudyBody } from "@actuarial-ts/interchange";
|
|
74
|
+
import { type DataReviewReport } from "@actuarial-ts/data";
|
|
75
|
+
import { type JudgmentChainWorkflow } from "./judgment.js";
|
|
76
|
+
/**
|
|
77
|
+
* The host-adapter seam (spec 6 Gate 4; the workbench adapter arrives with
|
|
78
|
+
* plan task B4). resolveSegment is SYNCHRONOUS so promoteStudy can stay
|
|
79
|
+
* synchronous (the accidental-thenable footgun forbids async construction);
|
|
80
|
+
* the mutation hooks may be async - they run inside gate execution.
|
|
81
|
+
*/
|
|
82
|
+
export interface PromoteStudyDeps {
|
|
83
|
+
/**
|
|
84
|
+
* Maps a selection's triangle segment labels to exactly one host workspace
|
|
85
|
+
* target, or null when nothing matches (v1: no fuzzy matching). A
|
|
86
|
+
* single-segment host returns its only target regardless of labels.
|
|
87
|
+
*/
|
|
88
|
+
resolveSegment(labels: Record<string, string>): string | null;
|
|
89
|
+
/**
|
|
90
|
+
* Applies one promoted selection through the host's service layer.
|
|
91
|
+
* `replayedSelections` is the core LdfSelections the replay verified;
|
|
92
|
+
* `segmentTarget` is the resolved workspace target (a two-parameter
|
|
93
|
+
* implementation that closes over its single segment may ignore it).
|
|
94
|
+
*/
|
|
95
|
+
applySelections(selectionDoc: SelectionDoc, replayedSelections: LdfSelections, segmentTarget: string): Promise<void> | void;
|
|
96
|
+
/** Reruns the host analysis after the selections are applied. */
|
|
97
|
+
runAnalysis(label: string): Promise<void> | void;
|
|
98
|
+
/** Persists a completion note (called twice: trail, then ledger JSON). */
|
|
99
|
+
persistNote(text: string, author: string): Promise<void> | void;
|
|
100
|
+
}
|
|
101
|
+
/** Context handed to the rationale-drafting hook. */
|
|
102
|
+
export interface DraftRationaleContext {
|
|
103
|
+
study: StudyBody;
|
|
104
|
+
studyIntegrity: string;
|
|
105
|
+
intake: StudyIntakeEvidence;
|
|
106
|
+
replay: ReplayVerifyEvidence;
|
|
107
|
+
}
|
|
108
|
+
export interface PromoteStudyOptions {
|
|
109
|
+
/**
|
|
110
|
+
* The host's replay-tolerance ceiling. The EFFECTIVE tolerance is
|
|
111
|
+
* min(study replayTolerance, ceiling); a study STATING a tolerance above
|
|
112
|
+
* the ceiling fails intake (tolerance editing is not an escape hatch).
|
|
113
|
+
*/
|
|
114
|
+
toleranceCeiling: number;
|
|
115
|
+
/** Actor recorded when a resume payload names none. Default "actuary". */
|
|
116
|
+
actorDefault?: string;
|
|
117
|
+
/** Host-injected clock (purity: this package never reads Date). */
|
|
118
|
+
now: () => string;
|
|
119
|
+
/**
|
|
120
|
+
* Coherence/integrity strictness for intake. Default "refuse" (promotion
|
|
121
|
+
* is a refuse-mode consumer per spec 3.2); "warn" downgrades divergence
|
|
122
|
+
* to evidence warnings.
|
|
123
|
+
*/
|
|
124
|
+
strictness?: "warn" | "refuse";
|
|
125
|
+
/**
|
|
126
|
+
* Injection hook for agent-assisted rationale drafting (spec 9.2 arrives
|
|
127
|
+
* later); the default assembles a template from the study narrative. The
|
|
128
|
+
* human always owns the final text - this drafts, never decides.
|
|
129
|
+
*/
|
|
130
|
+
draftRationale?: (ctx: DraftRationaleContext) => Promise<string> | string;
|
|
131
|
+
}
|
|
132
|
+
/** Prominent tolerance block (spec 6 Gate 1). */
|
|
133
|
+
export interface ReplayToleranceEvidence {
|
|
134
|
+
/** expectations.replayTolerance as stated, or null when the study omits it. */
|
|
135
|
+
stated: number | null;
|
|
136
|
+
/** The convention-profile default the 10x flag is measured against. */
|
|
137
|
+
profileId: string;
|
|
138
|
+
profileDefault: number;
|
|
139
|
+
/** true when the stated tolerance exceeds 10x the profile default. */
|
|
140
|
+
exceedsTenTimesProfileDefault: boolean;
|
|
141
|
+
/** The host ceiling from options. */
|
|
142
|
+
ceiling: number;
|
|
143
|
+
/** min(stated ?? profileDefault, ceiling): what Gate 2 referees at. */
|
|
144
|
+
effective: number;
|
|
145
|
+
}
|
|
146
|
+
export interface TriangleReviewEvidence {
|
|
147
|
+
/** paired-asop23 = reviewTriangles on a paid/incurred pair; structural =
|
|
148
|
+
* per-triangle checks; not-evaluated = non-core measure. */
|
|
149
|
+
mode: "paired-asop23" | "structural" | "not-evaluated";
|
|
150
|
+
triangles: {
|
|
151
|
+
integrity: string;
|
|
152
|
+
measure: string;
|
|
153
|
+
}[];
|
|
154
|
+
report: DataReviewReport;
|
|
155
|
+
}
|
|
156
|
+
export interface SegmentResolutionEvidence {
|
|
157
|
+
selectionIntegrity: string;
|
|
158
|
+
labels: Record<string, string>;
|
|
159
|
+
target: string;
|
|
160
|
+
}
|
|
161
|
+
export interface SelectionCoherenceEvidence {
|
|
162
|
+
selectionIntegrity: string;
|
|
163
|
+
triangleIntegrity: string;
|
|
164
|
+
coherence: CoherenceCheck;
|
|
165
|
+
}
|
|
166
|
+
export interface StudyIntakeEvidence {
|
|
167
|
+
study: {
|
|
168
|
+
title: string;
|
|
169
|
+
analyst: string | null;
|
|
170
|
+
sourceRef: string | null;
|
|
171
|
+
summary: string;
|
|
172
|
+
integrity: string;
|
|
173
|
+
};
|
|
174
|
+
/** FIRST, on purpose: the spec requires prominent display. */
|
|
175
|
+
replayTolerance: ReplayToleranceEvidence;
|
|
176
|
+
dataReview: TriangleReviewEvidence[];
|
|
177
|
+
coherence: SelectionCoherenceEvidence[];
|
|
178
|
+
segments: SegmentResolutionEvidence[];
|
|
179
|
+
warnings: string[];
|
|
180
|
+
/**
|
|
181
|
+
* ALWAYS populated: the verification-scope disclosure. Coherence, replay,
|
|
182
|
+
* and referee all verify the study against its OWN embedded triangle;
|
|
183
|
+
* whether that triangle IS the host workspace's book of business is not
|
|
184
|
+
* machine-verified anywhere in the chain. Stating this in the evidence
|
|
185
|
+
* keeps the gates honest about what they checked - the data-binding
|
|
186
|
+
* judgment belongs to the reviewing actuary, and the UI must say so
|
|
187
|
+
* rather than let "verified" read wider than it is.
|
|
188
|
+
*/
|
|
189
|
+
workspaceBindingNote: string;
|
|
190
|
+
}
|
|
191
|
+
/** Per replay target: how this shore verified it (spec 3.2 capabilities). */
|
|
192
|
+
export interface ReplayTargetLabel {
|
|
193
|
+
/** "12-24" for a development interval, or "tail". */
|
|
194
|
+
target: string;
|
|
195
|
+
/** exact = independently recomputed; value-only = applied as stated. */
|
|
196
|
+
capability: "exact" | "value-only";
|
|
197
|
+
/** The disclosure-honest label: replayed-exact or verified-by-value. */
|
|
198
|
+
label: "replayed-exact" | "verified-by-value";
|
|
199
|
+
note?: string;
|
|
200
|
+
}
|
|
201
|
+
export interface SelectionReplayEvidence {
|
|
202
|
+
selectionIntegrity: string;
|
|
203
|
+
triangleIntegrity: string;
|
|
204
|
+
segmentTarget: string;
|
|
205
|
+
targets: ReplayTargetLabel[];
|
|
206
|
+
/** Every intent judgmental/external: the whole replay is value transport. */
|
|
207
|
+
verifiedByValueOnly: boolean;
|
|
208
|
+
replayTotals: {
|
|
209
|
+
ultimate: number;
|
|
210
|
+
unpaid: number;
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
export interface SupportingCrosscheckEvidence {
|
|
214
|
+
resultIntegrity: string;
|
|
215
|
+
engine: {
|
|
216
|
+
name: string;
|
|
217
|
+
version: string;
|
|
218
|
+
};
|
|
219
|
+
/** null when the result could not be refereed (see reason). */
|
|
220
|
+
verdict: "agree" | "disagree" | "not-comparable" | "verified-by-value" | null;
|
|
221
|
+
report: CrosscheckReportDoc | null;
|
|
222
|
+
reason?: string;
|
|
223
|
+
}
|
|
224
|
+
export interface ReplayVerifyEvidence {
|
|
225
|
+
effectiveTolerance: number;
|
|
226
|
+
replays: SelectionReplayEvidence[];
|
|
227
|
+
crosschecks: SupportingCrosscheckEvidence[];
|
|
228
|
+
/** Any crosscheck verdict "disagree": the gate cannot accept (spec 6). */
|
|
229
|
+
hardBlocked: boolean;
|
|
230
|
+
/** States exactly what the verification consisted of - including, when the
|
|
231
|
+
* study carried no supportingResults, that it was coherence + replay only. */
|
|
232
|
+
verification: string;
|
|
233
|
+
}
|
|
234
|
+
export interface RationaleEvidence {
|
|
235
|
+
draftRationale: string;
|
|
236
|
+
attestationRequired: true;
|
|
237
|
+
study: {
|
|
238
|
+
title: string;
|
|
239
|
+
sourceRef: string | null;
|
|
240
|
+
analyst: string | null;
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
export interface ApplyEvidence {
|
|
244
|
+
applications: {
|
|
245
|
+
segmentTarget: string;
|
|
246
|
+
selectionIntegrity: string;
|
|
247
|
+
developmentCount: number;
|
|
248
|
+
tailFactor: number;
|
|
249
|
+
}[];
|
|
250
|
+
analysisLabel: string;
|
|
251
|
+
ledgerSource: string;
|
|
252
|
+
}
|
|
253
|
+
/** The workflow id promoteStudy builds its chain with. */
|
|
254
|
+
export declare const PROMOTION_CHAIN_ID = "promote-study";
|
|
255
|
+
/** See StudyIntakeEvidence.workspaceBindingNote; one constant so every
|
|
256
|
+
* intake states the same scope, verbatim. */
|
|
257
|
+
export declare const WORKSPACE_BINDING_NOTE: string;
|
|
258
|
+
/**
|
|
259
|
+
* Builds the promotion judgment chain for one study. SYNCHRONOUS by design
|
|
260
|
+
* (see the accidental-thenable footgun in the module doc): assign the return
|
|
261
|
+
* value and register it on a Mastra instance, exactly like any
|
|
262
|
+
* createJudgmentChain workflow; then createRun/start/resume drive the gates.
|
|
263
|
+
*
|
|
264
|
+
* Reconstruction contract: promoteStudy(deps, studyDoc, options) is
|
|
265
|
+
* deterministic given the study document and deps.resolveSegment, so a host
|
|
266
|
+
* resuming a snapshot after a restart rebuilds the identical chain.
|
|
267
|
+
*/
|
|
268
|
+
export declare function promoteStudy(deps: PromoteStudyDeps, studyDoc: unknown, options: PromoteStudyOptions): JudgmentChainWorkflow;
|
|
269
|
+
//# sourceMappingURL=promotion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion.d.ts","sourceRoot":"","sources":["../src/promotion.ts"],"names":[],"mappings":"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"}
|