@decionis/agent-safe-pipeline 0.1.4 → 0.3.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 +170 -10
- package/dist/Index.d.ts +8 -0
- package/dist/Index.d.ts.map +1 -1
- package/dist/Index.js +7 -0
- package/dist/Index.js.map +1 -1
- package/dist/audit/AuditRecorder.d.ts +1 -1
- package/dist/audit/AuditRecorder.d.ts.map +1 -1
- package/dist/audit/AuditRecorder.js.map +1 -1
- package/dist/decision/CreateGate.d.ts +123 -0
- package/dist/decision/CreateGate.d.ts.map +1 -0
- package/dist/decision/CreateGate.js +211 -0
- package/dist/decision/CreateGate.js.map +1 -0
- package/dist/decision/DecionisGate.d.ts +18 -1
- package/dist/decision/DecionisGate.d.ts.map +1 -1
- package/dist/decision/DecionisGate.js +31 -3
- package/dist/decision/DecionisGate.js.map +1 -1
- package/dist/decision/DecisionAuthority.d.ts +26 -0
- package/dist/decision/DecisionAuthority.d.ts.map +1 -1
- package/dist/decision/DecisionAuthority.js.map +1 -1
- package/dist/decision/ImmutableGateDecision.d.ts.map +1 -1
- package/dist/decision/ImmutableGateDecision.js +8 -0
- package/dist/decision/ImmutableGateDecision.js.map +1 -1
- package/dist/decision/Provision.d.ts +33 -0
- package/dist/decision/Provision.d.ts.map +1 -0
- package/dist/decision/Provision.js +116 -0
- package/dist/decision/Provision.js.map +1 -0
- package/dist/decision/ShadowGate.d.ts +31 -0
- package/dist/decision/ShadowGate.d.ts.map +1 -0
- package/dist/decision/ShadowGate.js +79 -0
- package/dist/decision/ShadowGate.js.map +1 -0
- package/dist/execution/ActionRegistry.d.ts +40 -0
- package/dist/execution/ActionRegistry.d.ts.map +1 -1
- package/dist/execution/ActionRegistry.js +47 -2
- package/dist/execution/ActionRegistry.js.map +1 -1
- package/dist/execution/AuthorizationVerifier.d.ts +147 -1
- package/dist/execution/AuthorizationVerifier.d.ts.map +1 -1
- package/dist/execution/AuthorizationVerifier.js +209 -6
- package/dist/execution/AuthorizationVerifier.js.map +1 -1
- package/dist/execution/SafeExecutor.d.ts +34 -3
- package/dist/execution/SafeExecutor.d.ts.map +1 -1
- package/dist/execution/SafeExecutor.js +42 -5
- package/dist/execution/SafeExecutor.js.map +1 -1
- package/dist/http/ClientIdentification.d.ts +22 -0
- package/dist/http/ClientIdentification.d.ts.map +1 -0
- package/dist/http/ClientIdentification.js +44 -0
- package/dist/http/ClientIdentification.js.map +1 -0
- package/dist/http/Credential.d.ts +17 -0
- package/dist/http/Credential.d.ts.map +1 -0
- package/dist/http/Credential.js +5 -0
- package/dist/http/Credential.js.map +1 -0
- package/dist/http/StoredCredentials.d.ts +34 -0
- package/dist/http/StoredCredentials.d.ts.map +1 -0
- package/dist/http/StoredCredentials.js +85 -0
- package/dist/http/StoredCredentials.js.map +1 -0
- package/dist/intent/CanonicalIntentHasher.d.ts.map +1 -1
- package/dist/intent/CanonicalIntentHasher.js +3 -0
- package/dist/intent/CanonicalIntentHasher.js.map +1 -1
- package/dist/intent/ExecutionIntent.d.ts +13 -1
- package/dist/intent/ExecutionIntent.d.ts.map +1 -1
- package/dist/intent/ExecutionIntent.js +4 -0
- package/dist/intent/ExecutionIntent.js.map +1 -1
- package/dist/intent/IntentCapture.d.ts.map +1 -1
- package/dist/intent/IntentCapture.js +3 -0
- package/dist/intent/IntentCapture.js.map +1 -1
- package/dist/report/DecisionReport.d.ts +22 -0
- package/dist/report/DecisionReport.d.ts.map +1 -0
- package/dist/report/DecisionReport.js +37 -0
- package/dist/report/DecisionReport.js.map +1 -0
- package/dist/report/DossierReport.d.ts +43 -0
- package/dist/report/DossierReport.d.ts.map +1 -0
- package/dist/report/DossierReport.js +130 -0
- package/dist/report/DossierReport.js.map +1 -0
- package/dist/report/HostedOutcome.d.ts +18 -0
- package/dist/report/HostedOutcome.d.ts.map +1 -0
- package/dist/report/HostedOutcome.js +24 -0
- package/dist/report/HostedOutcome.js.map +1 -0
- package/dist/testing/Index.d.ts +1 -1
- package/dist/testing/Index.d.ts.map +1 -1
- package/dist/testing/Index.js +1 -1
- package/dist/testing/Index.js.map +1 -1
- package/dist/testing/LocalAuthority.d.ts +156 -0
- package/dist/testing/LocalAuthority.d.ts.map +1 -1
- package/dist/testing/LocalAuthority.js +531 -9
- package/dist/testing/LocalAuthority.js.map +1 -1
- package/package.json +1 -1
|
@@ -21,6 +21,14 @@ export function immutableGateDecision(decision) {
|
|
|
21
21
|
reasonCodes: Object.freeze([...decision.managedEscalation.reasonCodes]),
|
|
22
22
|
}),
|
|
23
23
|
}),
|
|
24
|
+
...(decision.hosted === undefined
|
|
25
|
+
? {}
|
|
26
|
+
: {
|
|
27
|
+
hosted: Object.freeze({
|
|
28
|
+
...decision.hosted,
|
|
29
|
+
reasonCodes: Object.freeze([...decision.hosted.reasonCodes]),
|
|
30
|
+
}),
|
|
31
|
+
}),
|
|
24
32
|
});
|
|
25
33
|
}
|
|
26
34
|
//# sourceMappingURL=ImmutableGateDecision.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImmutableGateDecision.js","sourceRoot":"","sources":["../../src/decision/ImmutableGateDecision.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,QAAsB;IAC1D,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,QAAQ;QACX,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACrD,aAAa,EACX,QAAQ,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,aAAa,EAAE,CAAC;QACvF,GAAG,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS;YACjC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;oBACtB,GAAG,QAAQ,CAAC,QAAQ;oBACpB,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,KAAK,SAAS;wBAC/C,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC;iBAC9E,CAAC;aACH,CAAC;QACN,GAAG,CAAC,QAAQ,CAAC,iBAAiB,KAAK,SAAS;YAC1C,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,iBAAiB,EAAE,MAAM,CAAC,MAAM,CAAC;oBAC/B,GAAG,QAAQ,CAAC,iBAAiB;oBAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;iBACxE,CAAC;aACH,CAAC;KACP,CAAC,CAAC;AACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"ImmutableGateDecision.js","sourceRoot":"","sources":["../../src/decision/ImmutableGateDecision.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,QAAsB;IAC1D,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,QAAQ;QACX,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACrD,aAAa,EACX,QAAQ,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,aAAa,EAAE,CAAC;QACvF,GAAG,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS;YACjC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;oBACtB,GAAG,QAAQ,CAAC,QAAQ;oBACpB,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,KAAK,SAAS;wBAC/C,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC;iBAC9E,CAAC;aACH,CAAC;QACN,GAAG,CAAC,QAAQ,CAAC,iBAAiB,KAAK,SAAS;YAC1C,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,iBAAiB,EAAE,MAAM,CAAC,MAAM,CAAC;oBAC/B,GAAG,QAAQ,CAAC,iBAAiB;oBAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;iBACxE,CAAC;aACH,CAAC;QACN,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS;YAC/B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;oBACpB,GAAG,QAAQ,CAAC,MAAM;oBAClB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;iBAC7D,CAAC;aACH,CAAC;KACP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ClientSource } from "../http/ClientIdentification.js";
|
|
2
|
+
export interface ProvisionOptions {
|
|
3
|
+
/** The authority to provision from; HTTPS, or loopback when allowed. */
|
|
4
|
+
readonly baseUrl: string;
|
|
5
|
+
readonly allowInsecureLoopback?: boolean;
|
|
6
|
+
readonly fetch?: typeof fetch;
|
|
7
|
+
readonly timeoutMs?: number;
|
|
8
|
+
readonly source?: ClientSource;
|
|
9
|
+
/** Shown to the person who later claims the workspace. */
|
|
10
|
+
readonly agentName?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ProvisionedWorkspace {
|
|
13
|
+
readonly orgId: string;
|
|
14
|
+
/** The key, seen here and nowhere else; the caller stores it. */
|
|
15
|
+
readonly rawKey: string;
|
|
16
|
+
readonly provisional: true;
|
|
17
|
+
/** The caps on the anonymous lane, as the authority stated them. */
|
|
18
|
+
readonly limits: Readonly<Record<string, unknown>>;
|
|
19
|
+
/** How a person attaches themselves to the workspace, as the authority stated it. */
|
|
20
|
+
readonly claim: Readonly<Record<string, unknown>>;
|
|
21
|
+
}
|
|
22
|
+
export declare class ProvisionError extends Error {
|
|
23
|
+
readonly code: "PROVISION_LIMIT_REACHED" | "PROVISION_REFUSED" | "PROVISION_UNAVAILABLE" | "PROVISION_RESPONSE_INVALID" | "PROVISION_TIMED_OUT";
|
|
24
|
+
readonly status: number | null;
|
|
25
|
+
/** Seconds to wait before another attempt, when the authority said. */
|
|
26
|
+
readonly retryAfterSeconds: number | null;
|
|
27
|
+
constructor(code: "PROVISION_LIMIT_REACHED" | "PROVISION_REFUSED" | "PROVISION_UNAVAILABLE" | "PROVISION_RESPONSE_INVALID" | "PROVISION_TIMED_OUT", status: number | null,
|
|
28
|
+
/** Seconds to wait before another attempt, when the authority said. */
|
|
29
|
+
retryAfterSeconds?: number | null);
|
|
30
|
+
}
|
|
31
|
+
/** Mints a provisional workspace, or throws a `ProvisionError` that names why. */
|
|
32
|
+
export declare function provisionWorkspace(options: ProvisionOptions): Promise<ProvisionedWorkspace>;
|
|
33
|
+
//# sourceMappingURL=Provision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Provision.d.ts","sourceRoot":"","sources":["../../src/decision/Provision.ts"],"names":[],"mappings":"AAcA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/E,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,qFAAqF;IACrF,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACnD;AAoBD,qBAAa,cAAe,SAAQ,KAAK;aAErB,IAAI,EAChB,yBAAyB,GACzB,mBAAmB,GACnB,uBAAuB,GACvB,4BAA4B,GAC5B,qBAAqB;aACT,MAAM,EAAE,MAAM,GAAG,IAAI;IACrC,uEAAuE;aACvD,iBAAiB,EAAE,MAAM,GAAG,IAAI;gBARhC,IAAI,EAChB,yBAAyB,GACzB,mBAAmB,GACnB,uBAAuB,GACvB,4BAA4B,GAC5B,qBAAqB,EACT,MAAM,EAAE,MAAM,GAAG,IAAI;IACrC,uEAAuE;IACvD,iBAAiB,GAAE,MAAM,GAAG,IAAW;CAK1D;AAQD,kFAAkF;AAClF,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8DjG"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A free Decionis key, issued in the run itself. `POST
|
|
3
|
+
* /v1/public/agents/provision` mints a provisional workspace with no
|
|
4
|
+
* account, no email and no card: an organization id, a key returned exactly
|
|
5
|
+
* once, an allowance of governed decisions, and the way a person later
|
|
6
|
+
* claims it. Every dossier such a workspace mints is signed with the
|
|
7
|
+
* `provisional_anonymous` issuer tier, so a verifier can always tell it
|
|
8
|
+
* from an owned organization's record. The call carries the client
|
|
9
|
+
* identification the hosted gate carries, so the authority can say which
|
|
10
|
+
* repository, example and surface the workspace came from.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { AuthorityBaseUrl } from "../http/AuthorityBaseUrl.js";
|
|
14
|
+
import { BoundedResponseBody } from "../http/BoundedResponseBody.js";
|
|
15
|
+
import { userAgent } from "../http/ClientIdentification.js";
|
|
16
|
+
const MAX_RESPONSE_BYTES = 64 * 1024;
|
|
17
|
+
const DEFAULT_TIMEOUT_MS = 8_000;
|
|
18
|
+
const MAX_AGENT_NAME = 64;
|
|
19
|
+
/**
|
|
20
|
+
* Mirrors `AgentProvisionResponse`: the four fields the caller needs are
|
|
21
|
+
* required; the rest of the contract is open and rides along as it came.
|
|
22
|
+
*/
|
|
23
|
+
const ProvisionResponseSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
org_id: z.string().uuid(),
|
|
26
|
+
raw_key: z.string().min(1).max(4_096),
|
|
27
|
+
provisional: z.literal(true),
|
|
28
|
+
limits: z.record(z.string(), z.unknown()).default({}),
|
|
29
|
+
claim: z.record(z.string(), z.unknown()).default({}),
|
|
30
|
+
})
|
|
31
|
+
.passthrough();
|
|
32
|
+
export class ProvisionError extends Error {
|
|
33
|
+
code;
|
|
34
|
+
status;
|
|
35
|
+
retryAfterSeconds;
|
|
36
|
+
constructor(code, status,
|
|
37
|
+
/** Seconds to wait before another attempt, when the authority said. */
|
|
38
|
+
retryAfterSeconds = null) {
|
|
39
|
+
super(code);
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.status = status;
|
|
42
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
43
|
+
this.name = "ProvisionError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function retryAfter(response) {
|
|
47
|
+
const value = response.headers.get("retry-after");
|
|
48
|
+
if (value === null || !/^\d{1,7}$/.test(value.trim()))
|
|
49
|
+
return null;
|
|
50
|
+
return Number(value.trim());
|
|
51
|
+
}
|
|
52
|
+
/** Mints a provisional workspace, or throws a `ProvisionError` that names why. */
|
|
53
|
+
export async function provisionWorkspace(options) {
|
|
54
|
+
const baseUrl = AuthorityBaseUrl.normalize(options.baseUrl, options.allowInsecureLoopback ?? false);
|
|
55
|
+
const doFetch = options.fetch ?? fetch;
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
58
|
+
const agentName = options.agentName?.slice(0, MAX_AGENT_NAME);
|
|
59
|
+
let response;
|
|
60
|
+
try {
|
|
61
|
+
response = await doFetch(`${baseUrl}/v1/public/agents/provision`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"content-type": "application/json",
|
|
65
|
+
accept: "application/json",
|
|
66
|
+
"user-agent": userAgent(options.source),
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify(agentName === undefined ? {} : { agent_name: agentName }),
|
|
69
|
+
redirect: "error",
|
|
70
|
+
signal: controller.signal,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
throw new ProvisionError(controller.signal.aborted ? "PROVISION_TIMED_OUT" : "PROVISION_UNAVAILABLE", null);
|
|
76
|
+
}
|
|
77
|
+
let text;
|
|
78
|
+
try {
|
|
79
|
+
text = await BoundedResponseBody.read(response, MAX_RESPONSE_BYTES);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
text = null;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
}
|
|
87
|
+
if (response.status === 429) {
|
|
88
|
+
throw new ProvisionError("PROVISION_LIMIT_REACHED", 429, retryAfter(response));
|
|
89
|
+
}
|
|
90
|
+
if (response.status >= 500) {
|
|
91
|
+
throw new ProvisionError("PROVISION_UNAVAILABLE", response.status, retryAfter(response));
|
|
92
|
+
}
|
|
93
|
+
if (response.status !== 201 && response.status !== 200) {
|
|
94
|
+
throw new ProvisionError("PROVISION_REFUSED", response.status);
|
|
95
|
+
}
|
|
96
|
+
if (text === null)
|
|
97
|
+
throw new ProvisionError("PROVISION_RESPONSE_INVALID", response.status);
|
|
98
|
+
let body;
|
|
99
|
+
try {
|
|
100
|
+
body = JSON.parse(text);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
throw new ProvisionError("PROVISION_RESPONSE_INVALID", response.status);
|
|
104
|
+
}
|
|
105
|
+
const parsed = ProvisionResponseSchema.safeParse(body);
|
|
106
|
+
if (!parsed.success)
|
|
107
|
+
throw new ProvisionError("PROVISION_RESPONSE_INVALID", response.status);
|
|
108
|
+
return {
|
|
109
|
+
orgId: parsed.data.org_id,
|
|
110
|
+
rawKey: parsed.data.raw_key,
|
|
111
|
+
provisional: true,
|
|
112
|
+
limits: parsed.data.limits,
|
|
113
|
+
claim: parsed.data.claim,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=Provision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Provision.js","sourceRoot":"","sources":["../../src/decision/Provision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CapturedIntent } from "../intent/ExecutionIntent.js";
|
|
2
|
+
import { type DecisionAuthority, type DecisionEvaluationMode, type DecisionEvaluationOptions, type DecisionEvidence, type GateDecision } from "./DecisionAuthority.js";
|
|
3
|
+
/**
|
|
4
|
+
* Runs a local authority and a hosted Decionis gate against the same intent.
|
|
5
|
+
*
|
|
6
|
+
* `SHADOW`: the local decision governs execution exactly as it would alone,
|
|
7
|
+
* and the hosted evaluation rides along as `hosted` for display and audit. A
|
|
8
|
+
* hosted failure is recorded and changes nothing, so setting a key cannot
|
|
9
|
+
* break an integration that works today.
|
|
10
|
+
*
|
|
11
|
+
* `ENFORCEMENT`: the hosted decision governs, because only its grant can be
|
|
12
|
+
* claimed by the enforcement verifier. The local authority can still tighten
|
|
13
|
+
* it: when the local verdict is strictly more restrictive, that decision is
|
|
14
|
+
* returned instead, and it carries no hosted grant.
|
|
15
|
+
*
|
|
16
|
+
* Neither mode returns a decision less restrictive than the local authority's.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ShadowGate implements DecisionAuthority {
|
|
19
|
+
private readonly local;
|
|
20
|
+
private readonly hosted;
|
|
21
|
+
private readonly mode;
|
|
22
|
+
readonly evaluationMode?: DecisionEvaluationMode;
|
|
23
|
+
constructor(local: DecisionAuthority, hosted: DecisionAuthority, mode: DecisionEvaluationMode);
|
|
24
|
+
evaluate(captured: CapturedIntent, evidence?: DecisionEvidence, options?: DecisionEvaluationOptions): Promise<GateDecision>;
|
|
25
|
+
/**
|
|
26
|
+
* The hosted side never rejects and never answers about another intent:
|
|
27
|
+
* either would let a network fault or a misbound service reach execution.
|
|
28
|
+
*/
|
|
29
|
+
private hostedDecision;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=ShadowGate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShadowGate.d.ts","sourceRoot":"","sources":["../../src/decision/ShadowGate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EAErB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAUhC;;;;;;;;;;;;;;GAcG;AACH,qBAAa,UAAW,YAAW,iBAAiB;IAIhD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IALvB,SAAgB,cAAc,CAAC,EAAE,sBAAsB,CAAC;gBAGrC,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,sBAAsB;IASlC,QAAQ,CACnB,QAAQ,EAAE,cAAc,EACxB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,YAAY,CAAC;IAuBxB;;;OAGG;YACW,cAAc;CAgB7B"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { FailClosedDecision, } from "./DecisionAuthority.js";
|
|
2
|
+
import { immutableGateDecision } from "./ImmutableGateDecision.js";
|
|
3
|
+
/** Higher is more restrictive. The Decionis contract has no fourth verdict. */
|
|
4
|
+
const RESTRICTIVENESS = Object.freeze({
|
|
5
|
+
ALLOW: 0,
|
|
6
|
+
ESCALATE: 1,
|
|
7
|
+
BLOCK: 2,
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* Runs a local authority and a hosted Decionis gate against the same intent.
|
|
11
|
+
*
|
|
12
|
+
* `SHADOW`: the local decision governs execution exactly as it would alone,
|
|
13
|
+
* and the hosted evaluation rides along as `hosted` for display and audit. A
|
|
14
|
+
* hosted failure is recorded and changes nothing, so setting a key cannot
|
|
15
|
+
* break an integration that works today.
|
|
16
|
+
*
|
|
17
|
+
* `ENFORCEMENT`: the hosted decision governs, because only its grant can be
|
|
18
|
+
* claimed by the enforcement verifier. The local authority can still tighten
|
|
19
|
+
* it: when the local verdict is strictly more restrictive, that decision is
|
|
20
|
+
* returned instead, and it carries no hosted grant.
|
|
21
|
+
*
|
|
22
|
+
* Neither mode returns a decision less restrictive than the local authority's.
|
|
23
|
+
*/
|
|
24
|
+
export class ShadowGate {
|
|
25
|
+
local;
|
|
26
|
+
hosted;
|
|
27
|
+
mode;
|
|
28
|
+
evaluationMode;
|
|
29
|
+
constructor(local, hosted, mode) {
|
|
30
|
+
this.local = local;
|
|
31
|
+
this.hosted = hosted;
|
|
32
|
+
this.mode = mode;
|
|
33
|
+
if (mode !== "SHADOW" && mode !== "ENFORCEMENT") {
|
|
34
|
+
throw new Error("DECIONIS_GATE_MODE_INVALID");
|
|
35
|
+
}
|
|
36
|
+
const declared = mode === "ENFORCEMENT" ? mode : local.evaluationMode;
|
|
37
|
+
if (declared !== undefined)
|
|
38
|
+
this.evaluationMode = declared;
|
|
39
|
+
}
|
|
40
|
+
async evaluate(captured, evidence, options = {}) {
|
|
41
|
+
const [local, hosted] = await Promise.all([
|
|
42
|
+
this.local.evaluate(captured, evidence, options),
|
|
43
|
+
this.hostedDecision(captured, evidence, options),
|
|
44
|
+
]);
|
|
45
|
+
const governs = this.mode === "ENFORCEMENT" &&
|
|
46
|
+
RESTRICTIVENESS[hosted.verdict] >= RESTRICTIVENESS[local.verdict];
|
|
47
|
+
return immutableGateDecision({
|
|
48
|
+
...(governs ? hosted : local),
|
|
49
|
+
hosted: {
|
|
50
|
+
mode: this.mode,
|
|
51
|
+
governs,
|
|
52
|
+
verdict: hosted.verdict,
|
|
53
|
+
decisionId: hosted.decisionId,
|
|
54
|
+
dossierId: hosted.dossierId,
|
|
55
|
+
reasonCodes: hosted.reasonCodes,
|
|
56
|
+
failClosed: hosted.failClosed,
|
|
57
|
+
verificationUrl: hosted.verificationUrl ?? null,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The hosted side never rejects and never answers about another intent:
|
|
63
|
+
* either would let a network fault or a misbound service reach execution.
|
|
64
|
+
*/
|
|
65
|
+
async hostedDecision(captured, evidence, options) {
|
|
66
|
+
let decision;
|
|
67
|
+
try {
|
|
68
|
+
decision = await this.hosted.evaluate(captured, evidence, options);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return FailClosedDecision.create(captured.intentHash, "AUTHORITY_UNAVAILABLE");
|
|
72
|
+
}
|
|
73
|
+
if (decision.intentHash !== captured.intentHash) {
|
|
74
|
+
return FailClosedDecision.create(captured.intentHash, "AUTHORITY_BINDING_MISMATCH");
|
|
75
|
+
}
|
|
76
|
+
return decision;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=ShadowGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShadowGate.js","sourceRoot":"","sources":["../../src/decision/ShadowGate.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,GAOnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,+EAA+E;AAC/E,MAAM,eAAe,GAA8C,MAAM,CAAC,MAAM,CAAC;IAC/E,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,CAAC;CACT,CAAC,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,UAAU;IAIF;IACA;IACA;IALH,cAAc,CAA0B;IAExD,YACmB,KAAwB,EACxB,MAAyB,EACzB,IAA4B;QAF5B,UAAK,GAAL,KAAK,CAAmB;QACxB,WAAM,GAAN,MAAM,CAAmB;QACzB,SAAI,GAAJ,IAAI,CAAwB;QAE7C,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC;QACtE,IAAI,QAAQ,KAAK,SAAS;YAAE,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC;IAC7D,CAAC;IAEM,KAAK,CAAC,QAAQ,CACnB,QAAwB,EACxB,QAA2B,EAC3B,UAAqC,EAAE;QAEvC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC;YAChD,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC;SACjD,CAAC,CAAC;QACH,MAAM,OAAO,GACX,IAAI,CAAC,IAAI,KAAK,aAAa;YAC3B,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpE,OAAO,qBAAqB,CAAC;YAC3B,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YAC7B,MAAM,EAAE;gBACN,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO;gBACP,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,IAAI;aAChD;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,cAAc,CAC1B,QAAwB,EACxB,QAAsC,EACtC,OAAkC;QAElC,IAAI,QAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,QAAQ,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC;YAChD,OAAO,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,4BAA4B,CAAC,CAAC;QACtF,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -15,6 +15,14 @@ export interface ActionExecutionContext<TParameters> {
|
|
|
15
15
|
export interface ProviderDispatch {
|
|
16
16
|
readonly idempotencyKey: string;
|
|
17
17
|
run<TResult>(operation: (idempotencyKey: string) => Promise<TResult> | TResult): Promise<TResult>;
|
|
18
|
+
/**
|
|
19
|
+
* The provider's signed effect receipt, when it returned one with its
|
|
20
|
+
* answer, verbatim. It is carried on the attempt to the finalization, where
|
|
21
|
+
* the verifier forwards it to the authority to verify and record; this
|
|
22
|
+
* package never reads it. Only meaningful inside `run`: a receipt before
|
|
23
|
+
* the dispatch describes nothing.
|
|
24
|
+
*/
|
|
25
|
+
receipt(token: string): void;
|
|
18
26
|
}
|
|
19
27
|
export interface ActionReconciliationContext<TParameters> {
|
|
20
28
|
readonly intent: CapturedIntent;
|
|
@@ -38,11 +46,43 @@ export interface ActionHandler<TParameters, TResult> {
|
|
|
38
46
|
export type ActionExecutionAttempt = {
|
|
39
47
|
readonly status: "COMPLETED";
|
|
40
48
|
readonly result: unknown;
|
|
49
|
+
readonly receipt: string | null;
|
|
41
50
|
} | {
|
|
42
51
|
readonly status: "FAILED_BEFORE_DISPATCH";
|
|
52
|
+
} | {
|
|
53
|
+
readonly status: "REFUSED_AFTER_DISPATCH";
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
readonly receipt: string | null;
|
|
43
56
|
} | {
|
|
44
57
|
readonly status: "UNKNOWN_AFTER_DISPATCH";
|
|
58
|
+
readonly receipt: string | null;
|
|
45
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* What a handler throws when the provider refused, definitively, after the
|
|
62
|
+
* dispatch boundary was crossed.
|
|
63
|
+
*
|
|
64
|
+
* Without this, a handler has two ways to report a refusal and neither is
|
|
65
|
+
* true. Returning normally says the side effect happened. Throwing says the
|
|
66
|
+
* outcome is unknown, which invites a reconciliation of something that never
|
|
67
|
+
* needed one. This says the third thing: the request reached the provider,
|
|
68
|
+
* the provider said no, and nothing was effected.
|
|
69
|
+
*
|
|
70
|
+
* Only the provider's own deterministic refusal belongs here — a 4xx with a
|
|
71
|
+
* body that says which rule refused it. A timeout, a 5xx, a dropped
|
|
72
|
+
* connection or an unreadable answer is not a refusal, and throwing this for
|
|
73
|
+
* one of those would turn "nobody knows" into "definitely not", which is the
|
|
74
|
+
* one direction this boundary must never move in.
|
|
75
|
+
*
|
|
76
|
+
* Thrown before the dispatch, it is a handler failure like any other: a
|
|
77
|
+
* refusal cannot be claimed for a request that was never sent.
|
|
78
|
+
*/
|
|
79
|
+
export declare class ProviderRefusal extends Error {
|
|
80
|
+
/** The provider's own reason, as a code; never its response body. */
|
|
81
|
+
readonly reason: string;
|
|
82
|
+
constructor(
|
|
83
|
+
/** The provider's own reason, as a code; never its response body. */
|
|
84
|
+
reason: string);
|
|
85
|
+
}
|
|
46
86
|
export declare class ActionRegistry {
|
|
47
87
|
private readonly actions;
|
|
48
88
|
private readonly pendingReconciliations;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionRegistry.d.ts","sourceRoot":"","sources":["../../src/execution/ActionRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,MAAM,WAAW,sBAAsB,CAAC,WAAW;IACjD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ActionRegistry.d.ts","sourceRoot":"","sources":["../../src/execution/ActionRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a handler throws when the provider refused, definitively, after the
|
|
3
|
+
* dispatch boundary was crossed.
|
|
4
|
+
*
|
|
5
|
+
* Without this, a handler has two ways to report a refusal and neither is
|
|
6
|
+
* true. Returning normally says the side effect happened. Throwing says the
|
|
7
|
+
* outcome is unknown, which invites a reconciliation of something that never
|
|
8
|
+
* needed one. This says the third thing: the request reached the provider,
|
|
9
|
+
* the provider said no, and nothing was effected.
|
|
10
|
+
*
|
|
11
|
+
* Only the provider's own deterministic refusal belongs here — a 4xx with a
|
|
12
|
+
* body that says which rule refused it. A timeout, a 5xx, a dropped
|
|
13
|
+
* connection or an unreadable answer is not a refusal, and throwing this for
|
|
14
|
+
* one of those would turn "nobody knows" into "definitely not", which is the
|
|
15
|
+
* one direction this boundary must never move in.
|
|
16
|
+
*
|
|
17
|
+
* Thrown before the dispatch, it is a handler failure like any other: a
|
|
18
|
+
* refusal cannot be claimed for a request that was never sent.
|
|
19
|
+
*/
|
|
20
|
+
export class ProviderRefusal extends Error {
|
|
21
|
+
reason;
|
|
22
|
+
constructor(
|
|
23
|
+
/** The provider's own reason, as a code; never its response body. */
|
|
24
|
+
reason) {
|
|
25
|
+
super(reason);
|
|
26
|
+
this.reason = reason;
|
|
27
|
+
this.name = "ProviderRefusal";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
1
30
|
export class ActionRegistry {
|
|
2
31
|
actions = new Map();
|
|
3
32
|
pendingReconciliations = new Map();
|
|
@@ -43,6 +72,7 @@ export class ActionRegistry {
|
|
|
43
72
|
if (!result.success)
|
|
44
73
|
throw new Error("ACTION_PARAMETERS_INVALID");
|
|
45
74
|
let dispatched = false;
|
|
75
|
+
let receipt = null;
|
|
46
76
|
const dispatch = Object.freeze({
|
|
47
77
|
idempotencyKey: captured.intent.idempotencyKey,
|
|
48
78
|
run: async (operation) => {
|
|
@@ -51,6 +81,11 @@ export class ActionRegistry {
|
|
|
51
81
|
dispatched = true;
|
|
52
82
|
return await operation(captured.intent.idempotencyKey);
|
|
53
83
|
},
|
|
84
|
+
receipt: (token) => {
|
|
85
|
+
if (!dispatched)
|
|
86
|
+
throw new Error("PROVIDER_RECEIPT_BEFORE_DISPATCH");
|
|
87
|
+
receipt = token;
|
|
88
|
+
},
|
|
54
89
|
});
|
|
55
90
|
try {
|
|
56
91
|
return {
|
|
@@ -61,10 +96,20 @@ export class ActionRegistry {
|
|
|
61
96
|
authorization,
|
|
62
97
|
dispatch,
|
|
63
98
|
}),
|
|
99
|
+
receipt,
|
|
64
100
|
};
|
|
65
101
|
}
|
|
66
|
-
catch {
|
|
67
|
-
|
|
102
|
+
catch (error) {
|
|
103
|
+
// A refusal the provider made is only a refusal if the request reached
|
|
104
|
+
// it. Before the dispatch it is a handler failure, because nothing was
|
|
105
|
+
// sent for anyone to refuse. A receipt can accompany either answer
|
|
106
|
+
// after the dispatch: a provider's signed refusal is evidence too.
|
|
107
|
+
if (dispatched && error instanceof ProviderRefusal) {
|
|
108
|
+
return { status: "REFUSED_AFTER_DISPATCH", reason: error.reason, receipt };
|
|
109
|
+
}
|
|
110
|
+
return dispatched
|
|
111
|
+
? { status: "UNKNOWN_AFTER_DISPATCH", receipt }
|
|
112
|
+
: { status: "FAILED_BEFORE_DISPATCH" };
|
|
68
113
|
}
|
|
69
114
|
}
|
|
70
115
|
async reconcile(captured, idempotencyKey) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionRegistry.js","sourceRoot":"","sources":["../../src/execution/ActionRegistry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ActionRegistry.js","sourceRoot":"","sources":["../../src/execution/ActionRegistry.ts"],"names":[],"mappings":"AAmEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAGtB;IAFlB;IACE,qEAAqE;IACrD,MAAc;QAE9B,KAAK,CAAC,MAAM,CAAC,CAAC;QAFE,WAAM,GAAN,MAAM,CAAQ;QAG9B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,MAAM,OAAO,cAAc;IACR,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC9C,sBAAsB,GAAG,IAAI,GAAG,EAG9C,CAAC;IACI,MAAM,GAAG,KAAK,CAAC;IAEhB,QAAQ,CACb,MAAc,EACd,OAA4C;QAE5C,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC3E,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAA2B,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,IAAI;QACT,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,GAAG,CAAC,MAAc;QACvB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAEM,QAAQ,CAAC,QAAwB;QACtC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACvD,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5E,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,OAAO,CAClB,QAAwB,EACxB,aAAoC;QAEpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACnE,IAAI,OAAO,CAAC,MAAM,KAAK,WAAW;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,QAAwB,EACxB,aAAoC;QAEpC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9E,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAClE,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,OAAO,GAAkB,IAAI,CAAC;QAClC,MAAM,QAAQ,GAAqB,MAAM,CAAC,MAAM,CAAC;YAC/C,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc;YAC9C,GAAG,EAAE,KAAK,EACR,SAAiE,EAC/C,EAAE;gBACpB,IAAI,UAAU;oBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;gBACrE,UAAU,GAAG,IAAI,CAAC;gBAClB,OAAO,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,EAAE,CAAC,KAAa,EAAQ,EAAE;gBAC/B,IAAI,CAAC,UAAU;oBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;gBACrE,OAAO,GAAG,KAAK,CAAC;YAClB,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,OAAO;gBACL,MAAM,EAAE,WAAW;gBACnB,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC;oBAC5B,MAAM,EAAE,QAAQ;oBAChB,UAAU,EAAE,MAAM,CAAC,IAAI;oBACvB,aAAa;oBACb,QAAQ;iBACT,CAAC;gBACF,OAAO;aACR,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uEAAuE;YACvE,uEAAuE;YACvE,mEAAmE;YACnE,mEAAmE;YACnE,IAAI,UAAU,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;gBACnD,OAAO,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC7E,CAAC;YACD,OAAO,UAAU;gBACf,CAAC,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,OAAO,EAAE;gBAC/C,CAAC,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;QAC3C,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,SAAS,CACpB,QAAwB,EACxB,cAAsB;QAEtB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChE,IAAI,cAAc,KAAK,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAClF,IAAI,CAAC,UAAU,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACtE,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAElE,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,UAAU,SAAS,cAAc,EAAE,CAAC;QAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,MAAM,OAAO,CAAC;QAEhD,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,EAAE;aACrC,IAAI,CAAC,KAAK,IAAI,EAAE,CACf,IAAI,CAAC,mBAAmB,CACtB,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;YACxB,MAAM,EAAE,QAAQ;YAChB,UAAU,EAAE,UAAU,CAAC,IAAI;YAC3B,cAAc;SACf,CAAC,CACH,CACF;aACA,KAAK,CAAC,GAAoC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;aACrE,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACrD,OAAO,MAAM,cAAc,CAAC;IAC9B,CAAC;IAEO,mBAAmB,CAAC,KAAc;QACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACxE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC/B,CAAC;QACD,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,WAAW,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,IAAI,MAAM,EAAE,CAAC;YAChF,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,CAAC;QACD,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,cAAc,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC;YACvE,IAAI,CAAC,MAAM,KAAK,CAAC,EACjB,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;CACF"}
|