@decionis/agent-safe-pipeline 0.1.3 → 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/README.md +477 -16
- package/dist/Index.d.ts +9 -0
- package/dist/Index.d.ts.map +1 -1
- package/dist/Index.js +8 -0
- package/dist/Index.js.map +1 -1
- package/dist/approval/PresenceApprovalCoordinator.d.ts +25 -0
- package/dist/approval/PresenceApprovalCoordinator.d.ts.map +1 -1
- package/dist/approval/PresenceApprovalCoordinator.js +54 -6
- package/dist/approval/PresenceApprovalCoordinator.js.map +1 -1
- package/dist/audit/AuditRecorder.d.ts +122 -0
- package/dist/audit/AuditRecorder.d.ts.map +1 -0
- package/dist/audit/AuditRecorder.js +312 -0
- package/dist/audit/AuditRecorder.js.map +1 -0
- 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 +109 -3
- package/dist/decision/DecionisGate.d.ts.map +1 -1
- package/dist/decision/DecionisGate.js +823 -46
- package/dist/decision/DecionisGate.js.map +1 -1
- package/dist/decision/DecisionAuthority.d.ts +76 -1
- package/dist/decision/DecisionAuthority.d.ts.map +1 -1
- package/dist/decision/DecisionAuthority.js.map +1 -1
- package/dist/decision/FixtureDecisionAuthority.d.ts +27 -1
- package/dist/decision/FixtureDecisionAuthority.d.ts.map +1 -1
- package/dist/decision/FixtureDecisionAuthority.js +47 -0
- package/dist/decision/FixtureDecisionAuthority.js.map +1 -1
- package/dist/decision/ImmutableGateDecision.d.ts.map +1 -1
- package/dist/decision/ImmutableGateDecision.js +26 -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 +66 -0
- package/dist/execution/ActionRegistry.d.ts.map +1 -1
- package/dist/execution/ActionRegistry.js +110 -1
- package/dist/execution/ActionRegistry.js.map +1 -1
- package/dist/execution/AuthorizationVerifier.d.ts +149 -1
- package/dist/execution/AuthorizationVerifier.d.ts.map +1 -1
- package/dist/execution/AuthorizationVerifier.js +302 -27
- package/dist/execution/AuthorizationVerifier.js.map +1 -1
- package/dist/execution/SafeExecutor.d.ts +102 -7
- package/dist/execution/SafeExecutor.d.ts.map +1 -1
- package/dist/execution/SafeExecutor.js +288 -17
- 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 +1 -1
- package/dist/intent/CanonicalIntentHasher.d.ts.map +1 -1
- package/dist/intent/CanonicalIntentHasher.js +9 -2
- package/dist/intent/CanonicalIntentHasher.js.map +1 -1
- package/dist/intent/ExecutionIntent.d.ts +26 -2
- package/dist/intent/ExecutionIntent.d.ts.map +1 -1
- package/dist/intent/ExecutionIntent.js +11 -0
- package/dist/intent/ExecutionIntent.js.map +1 -1
- package/dist/intent/IntentCapture.d.ts +5 -0
- package/dist/intent/IntentCapture.d.ts.map +1 -1
- package/dist/intent/IntentCapture.js +15 -1
- 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/shadow/ShadowPipeline.d.ts +75 -5
- package/dist/shadow/ShadowPipeline.d.ts.map +1 -1
- package/dist/shadow/ShadowPipeline.js +157 -7
- package/dist/shadow/ShadowPipeline.js.map +1 -1
- package/dist/testing/Index.d.ts +12 -0
- package/dist/testing/Index.d.ts.map +1 -0
- package/dist/testing/Index.js +12 -0
- package/dist/testing/Index.js.map +1 -0
- package/dist/testing/LocalAuthority.d.ts +292 -0
- package/dist/testing/LocalAuthority.d.ts.map +1 -0
- package/dist/testing/LocalAuthority.js +1124 -0
- package/dist/testing/LocalAuthority.js.map +1 -0
- package/dist/testing/LocalPresence.d.ts +210 -0
- package/dist/testing/LocalPresence.d.ts.map +1 -0
- package/dist/testing/LocalPresence.js +473 -0
- package/dist/testing/LocalPresence.js.map +1 -0
- package/package.json +40 -4
|
@@ -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"}
|
|
@@ -5,18 +5,84 @@ export interface ActionExecutionContext<TParameters> {
|
|
|
5
5
|
readonly intent: CapturedIntent;
|
|
6
6
|
readonly parameters: TParameters;
|
|
7
7
|
readonly authorization: VerifiedAuthorization;
|
|
8
|
+
/**
|
|
9
|
+
* Trusted handlers must place the provider side effect inside `dispatch.run`.
|
|
10
|
+
* This records the point after which a transport error has an unknown outcome
|
|
11
|
+
* and exposes only the intent-bound idempotency key, never an execution token.
|
|
12
|
+
*/
|
|
13
|
+
readonly dispatch: ProviderDispatch;
|
|
8
14
|
}
|
|
15
|
+
export interface ProviderDispatch {
|
|
16
|
+
readonly idempotencyKey: string;
|
|
17
|
+
run<TResult>(operation: (idempotencyKey: string) => Promise<TResult> | TResult): Promise<TResult>;
|
|
18
|
+
}
|
|
19
|
+
export interface ActionReconciliationContext<TParameters> {
|
|
20
|
+
readonly intent: CapturedIntent;
|
|
21
|
+
readonly parameters: TParameters;
|
|
22
|
+
readonly idempotencyKey: string;
|
|
23
|
+
}
|
|
24
|
+
export type ProviderReconciliation<TResult> = {
|
|
25
|
+
readonly status: "COMPLETED";
|
|
26
|
+
readonly result: TResult;
|
|
27
|
+
} | {
|
|
28
|
+
readonly status: "NOT_EXECUTED";
|
|
29
|
+
} | {
|
|
30
|
+
readonly status: "UNKNOWN";
|
|
31
|
+
};
|
|
9
32
|
export interface ActionHandler<TParameters, TResult> {
|
|
10
33
|
readonly parametersSchema: z.ZodType<TParameters>;
|
|
11
34
|
execute(context: ActionExecutionContext<TParameters>): Promise<TResult> | TResult;
|
|
35
|
+
/** Read-only provider lookup. It must never initiate or retry a side effect. */
|
|
36
|
+
reconcile?(context: ActionReconciliationContext<TParameters>): Promise<ProviderReconciliation<TResult>> | ProviderReconciliation<TResult>;
|
|
37
|
+
}
|
|
38
|
+
export type ActionExecutionAttempt = {
|
|
39
|
+
readonly status: "COMPLETED";
|
|
40
|
+
readonly result: unknown;
|
|
41
|
+
} | {
|
|
42
|
+
readonly status: "FAILED_BEFORE_DISPATCH";
|
|
43
|
+
} | {
|
|
44
|
+
readonly status: "REFUSED_AFTER_DISPATCH";
|
|
45
|
+
readonly reason: string;
|
|
46
|
+
} | {
|
|
47
|
+
readonly status: "UNKNOWN_AFTER_DISPATCH";
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* What a handler throws when the provider refused, definitively, after the
|
|
51
|
+
* dispatch boundary was crossed.
|
|
52
|
+
*
|
|
53
|
+
* Without this, a handler has two ways to report a refusal and neither is
|
|
54
|
+
* true. Returning normally says the side effect happened. Throwing says the
|
|
55
|
+
* outcome is unknown, which invites a reconciliation of something that never
|
|
56
|
+
* needed one. This says the third thing: the request reached the provider,
|
|
57
|
+
* the provider said no, and nothing was effected.
|
|
58
|
+
*
|
|
59
|
+
* Only the provider's own deterministic refusal belongs here — a 4xx with a
|
|
60
|
+
* body that says which rule refused it. A timeout, a 5xx, a dropped
|
|
61
|
+
* connection or an unreadable answer is not a refusal, and throwing this for
|
|
62
|
+
* one of those would turn "nobody knows" into "definitely not", which is the
|
|
63
|
+
* one direction this boundary must never move in.
|
|
64
|
+
*
|
|
65
|
+
* Thrown before the dispatch, it is a handler failure like any other: a
|
|
66
|
+
* refusal cannot be claimed for a request that was never sent.
|
|
67
|
+
*/
|
|
68
|
+
export declare class ProviderRefusal extends Error {
|
|
69
|
+
/** The provider's own reason, as a code; never its response body. */
|
|
70
|
+
readonly reason: string;
|
|
71
|
+
constructor(
|
|
72
|
+
/** The provider's own reason, as a code; never its response body. */
|
|
73
|
+
reason: string);
|
|
12
74
|
}
|
|
13
75
|
export declare class ActionRegistry {
|
|
14
76
|
private readonly actions;
|
|
77
|
+
private readonly pendingReconciliations;
|
|
15
78
|
private sealed;
|
|
16
79
|
register<TParameters, TResult>(action: string, handler: ActionHandler<TParameters, TResult>): this;
|
|
17
80
|
seal(): this;
|
|
18
81
|
has(action: string): boolean;
|
|
19
82
|
validate(captured: CapturedIntent): void;
|
|
20
83
|
execute(captured: CapturedIntent, authorization: VerifiedAuthorization): Promise<unknown>;
|
|
84
|
+
executeTracked(captured: CapturedIntent, authorization: VerifiedAuthorization): Promise<ActionExecutionAttempt>;
|
|
85
|
+
reconcile(captured: CapturedIntent, idempotencyKey: string): Promise<ProviderReconciliation<unknown>>;
|
|
86
|
+
private validReconciliation;
|
|
21
87
|
}
|
|
22
88
|
//# sourceMappingURL=ActionRegistry.d.ts.map
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"ActionRegistry.d.ts","sourceRoot":"","sources":["../../src/execution/ActionRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,35 @@
|
|
|
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();
|
|
32
|
+
pendingReconciliations = new Map();
|
|
3
33
|
sealed = false;
|
|
4
34
|
register(action, handler) {
|
|
5
35
|
if (this.sealed)
|
|
@@ -27,6 +57,12 @@ export class ActionRegistry {
|
|
|
27
57
|
}
|
|
28
58
|
}
|
|
29
59
|
async execute(captured, authorization) {
|
|
60
|
+
const attempt = await this.executeTracked(captured, authorization);
|
|
61
|
+
if (attempt.status === "COMPLETED")
|
|
62
|
+
return attempt.result;
|
|
63
|
+
throw new Error(attempt.status);
|
|
64
|
+
}
|
|
65
|
+
async executeTracked(captured, authorization) {
|
|
30
66
|
if (!this.sealed)
|
|
31
67
|
throw new Error("ACTION_REGISTRY_NOT_SEALED");
|
|
32
68
|
const handler = this.actions.get(captured.intent.action);
|
|
@@ -35,7 +71,80 @@ export class ActionRegistry {
|
|
|
35
71
|
const result = handler.parametersSchema.safeParse(captured.intent.parameters);
|
|
36
72
|
if (!result.success)
|
|
37
73
|
throw new Error("ACTION_PARAMETERS_INVALID");
|
|
38
|
-
|
|
74
|
+
let dispatched = false;
|
|
75
|
+
const dispatch = Object.freeze({
|
|
76
|
+
idempotencyKey: captured.intent.idempotencyKey,
|
|
77
|
+
run: async (operation) => {
|
|
78
|
+
if (dispatched)
|
|
79
|
+
throw new Error("PROVIDER_DISPATCH_ALREADY_STARTED");
|
|
80
|
+
dispatched = true;
|
|
81
|
+
return await operation(captured.intent.idempotencyKey);
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
try {
|
|
85
|
+
return {
|
|
86
|
+
status: "COMPLETED",
|
|
87
|
+
result: await handler.execute({
|
|
88
|
+
intent: captured,
|
|
89
|
+
parameters: result.data,
|
|
90
|
+
authorization,
|
|
91
|
+
dispatch,
|
|
92
|
+
}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// A refusal the provider made is only a refusal if the request reached
|
|
97
|
+
// it. Before the dispatch it is a handler failure, because nothing was
|
|
98
|
+
// sent for anyone to refuse.
|
|
99
|
+
if (dispatched && error instanceof ProviderRefusal) {
|
|
100
|
+
return { status: "REFUSED_AFTER_DISPATCH", reason: error.reason };
|
|
101
|
+
}
|
|
102
|
+
return { status: dispatched ? "UNKNOWN_AFTER_DISPATCH" : "FAILED_BEFORE_DISPATCH" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async reconcile(captured, idempotencyKey) {
|
|
106
|
+
if (!this.sealed)
|
|
107
|
+
throw new Error("ACTION_REGISTRY_NOT_SEALED");
|
|
108
|
+
if (idempotencyKey !== captured.intent.idempotencyKey) {
|
|
109
|
+
throw new Error("RECONCILIATION_BINDING_MISMATCH");
|
|
110
|
+
}
|
|
111
|
+
const handler = this.actions.get(captured.intent.action);
|
|
112
|
+
if (!handler)
|
|
113
|
+
throw new Error("ACTION_NOT_REGISTERED");
|
|
114
|
+
const parameters = handler.parametersSchema.safeParse(captured.intent.parameters);
|
|
115
|
+
if (!parameters.success)
|
|
116
|
+
throw new Error("ACTION_PARAMETERS_INVALID");
|
|
117
|
+
if (handler.reconcile === undefined)
|
|
118
|
+
return { status: "UNKNOWN" };
|
|
119
|
+
const key = `${captured.intentHash}\u0000${idempotencyKey}`;
|
|
120
|
+
const pending = this.pendingReconciliations.get(key);
|
|
121
|
+
if (pending !== undefined)
|
|
122
|
+
return await pending;
|
|
123
|
+
const reconciliation = Promise.resolve()
|
|
124
|
+
.then(async () => this.validReconciliation(await handler.reconcile?.({
|
|
125
|
+
intent: captured,
|
|
126
|
+
parameters: parameters.data,
|
|
127
|
+
idempotencyKey,
|
|
128
|
+
})))
|
|
129
|
+
.catch(() => ({ status: "UNKNOWN" }))
|
|
130
|
+
.finally(() => this.pendingReconciliations.delete(key));
|
|
131
|
+
this.pendingReconciliations.set(key, reconciliation);
|
|
132
|
+
return await reconciliation;
|
|
133
|
+
}
|
|
134
|
+
validReconciliation(value) {
|
|
135
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
136
|
+
return { status: "UNKNOWN" };
|
|
137
|
+
}
|
|
138
|
+
const record = value;
|
|
139
|
+
const keys = Object.keys(record);
|
|
140
|
+
if (record["status"] === "COMPLETED" && keys.length === 2 && "result" in record) {
|
|
141
|
+
return { status: "COMPLETED", result: record["result"] };
|
|
142
|
+
}
|
|
143
|
+
if ((record["status"] === "NOT_EXECUTED" || record["status"] === "UNKNOWN") &&
|
|
144
|
+
keys.length === 1) {
|
|
145
|
+
return { status: record["status"] };
|
|
146
|
+
}
|
|
147
|
+
return { status: "UNKNOWN" };
|
|
39
148
|
}
|
|
40
149
|
}
|
|
41
150
|
//# sourceMappingURL=ActionRegistry.js.map
|
|
@@ -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":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type Credential } from "../http/Credential.js";
|
|
1
2
|
import type { CapturedIntent } from "../intent/ExecutionIntent.js";
|
|
3
|
+
import type { JsonObject } from "../intent/JsonValue.js";
|
|
2
4
|
import type { GateDecision } from "../decision/DecisionAuthority.js";
|
|
3
5
|
export interface VerifiedAuthorization {
|
|
4
6
|
readonly decisionId: string;
|
|
@@ -6,23 +8,169 @@ export interface VerifiedAuthorization {
|
|
|
6
8
|
readonly grantId: string;
|
|
7
9
|
readonly intentHash: string;
|
|
8
10
|
readonly expiresAt: string;
|
|
11
|
+
/**
|
|
12
|
+
* When the authority stops accepting a commit for this claim, if it said.
|
|
13
|
+
* A lease is shorter than the grant's own expiry and is the real window a
|
|
14
|
+
* side effect has to finish inside: a commit the authority will no longer
|
|
15
|
+
* record leaves an outcome nobody can reconcile from the record, however
|
|
16
|
+
* successful the dispatch was.
|
|
17
|
+
*
|
|
18
|
+
* Absent when the authority did not return one, which is how a deployment
|
|
19
|
+
* whose issuer predates the field behaves. A consumer that ignores this
|
|
20
|
+
* field is exactly as correct as it was before the field existed.
|
|
21
|
+
*/
|
|
22
|
+
readonly leaseExpiresAt?: string;
|
|
23
|
+
/**
|
|
24
|
+
* The digest the authority bound over the canonical action parameters, when
|
|
25
|
+
* it exposed one. It has already been checked against this package's own
|
|
26
|
+
* digest of the same parameters before this object exists: a claim whose
|
|
27
|
+
* digest disagreed was refused, not returned. It is carried so a handler
|
|
28
|
+
* can show a downstream what the authority bound, not so anyone re-checks it.
|
|
29
|
+
*/
|
|
30
|
+
readonly payloadDigest?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Proof of this claim a system of record can verify without trusting this
|
|
33
|
+
* process: a compact JWS the authority signed over the grant, the decision,
|
|
34
|
+
* the binding and the lease. A handler forwards it downstream, and a
|
|
35
|
+
* downstream that requires it can refuse an instruction the authority never
|
|
36
|
+
* claimed. Absent when the authority predates it.
|
|
37
|
+
*/
|
|
38
|
+
readonly claimAttestation?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Outcome of the downstream attempt reported to the authority after a claimed grant. */
|
|
41
|
+
export type ExecutionCommitOutcome = "COMMITTED" | "FAILED" | "INDETERMINATE";
|
|
42
|
+
/**
|
|
43
|
+
* `RECORDED`: the authority accepted the commit evidence. `PENDING`: it could
|
|
44
|
+
* not be delivered and the authority's lease recovery owns it. `UNSUPPORTED`:
|
|
45
|
+
* the verifier has no finalization contract.
|
|
46
|
+
*/
|
|
47
|
+
export type ExecutionFinalization = "RECORDED" | "PENDING" | "UNSUPPORTED";
|
|
48
|
+
/**
|
|
49
|
+
* Exact wire shape of the Decionis Protocol 1.1 `EffectEvidence` contract: an
|
|
50
|
+
* observation of what happened downstream, supplied by the trusted runtime that
|
|
51
|
+
* observed it. This package never constructs, interprets, upgrades, or
|
|
52
|
+
* downgrades an observation; it forwards a bounded, validated copy of the one
|
|
53
|
+
* it was handed, and drops anything outside the contract's own shape rather
|
|
54
|
+
* than serialising a caller-controlled object onto an authenticated request.
|
|
55
|
+
*/
|
|
56
|
+
export interface AuthorityEffectEvidence {
|
|
57
|
+
readonly version: "1.0";
|
|
58
|
+
readonly status: "CONFIRMED" | "UNCONFIRMED";
|
|
59
|
+
readonly observation_method: "DOWNSTREAM_ACK" | "READ_AFTER_WRITE" | "EVENT_CONFIRMATION" | "STATE_RECONCILIATION" | "SIGNED_RECEIPT" | "EXTERNAL_ATTESTATION" | "HUMAN_VALIDATION";
|
|
60
|
+
readonly observer: {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly version: string | null;
|
|
63
|
+
} | null;
|
|
64
|
+
readonly expected_effect_digest: string;
|
|
65
|
+
readonly observed_effect_digest: string | null;
|
|
66
|
+
readonly observed_at: string | null;
|
|
67
|
+
readonly evidence_digest: string | null;
|
|
68
|
+
readonly evidence_reference: string | null;
|
|
69
|
+
readonly execution_correlation_id: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* What the authority itself said about an observation it was sent, narrowed by
|
|
73
|
+
* what this executor knows it sent. `effectEvidenceRecorded` and
|
|
74
|
+
* `effectConfirmation` are the authority's statement, but they are only ever
|
|
75
|
+
* reported for a finalization that actually carried the observation: an
|
|
76
|
+
* authority claiming to have recorded evidence it was not sent is not evidence.
|
|
77
|
+
*/
|
|
78
|
+
export interface AuthorityEffectReport {
|
|
79
|
+
/** The finalization that was recorded carried the observation. */
|
|
80
|
+
readonly effectEvidenceSent: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* An earlier finalization attempt carried the observation and the authority
|
|
83
|
+
* refused it, so this commit record was recovered without it. Distinguishes a
|
|
84
|
+
* refused observation from one that was dropped or never supplied.
|
|
85
|
+
*/
|
|
86
|
+
readonly effectEvidenceRefused: boolean;
|
|
87
|
+
readonly effectEvidenceRecorded: boolean;
|
|
88
|
+
readonly effectConfirmation: "CONFIRMED" | "UNCONFIRMED";
|
|
89
|
+
}
|
|
90
|
+
export interface AuthorizationFinalizationInput {
|
|
91
|
+
readonly captured: CapturedIntent;
|
|
92
|
+
readonly decision: GateDecision;
|
|
93
|
+
readonly authorization: VerifiedAuthorization;
|
|
94
|
+
readonly outcome: ExecutionCommitOutcome;
|
|
95
|
+
/**
|
|
96
|
+
* Optional observation of the downstream effect, forwarded verbatim. It is
|
|
97
|
+
* sent only when the signed grant committed to an expected-effect digest, the
|
|
98
|
+
* evidence names that same digest, and its `execution_correlation_id` equals
|
|
99
|
+
* the commit correlation id this grant was claimed with. Otherwise it is
|
|
100
|
+
* dropped, because the authority refuses the entire finalization for evidence
|
|
101
|
+
* it cannot bind and the commit outcome would be lost with it. It is also
|
|
102
|
+
* dropped when it does not conform to the contract's own bounded shape.
|
|
103
|
+
*/
|
|
104
|
+
readonly effectEvidence?: AuthorityEffectEvidence;
|
|
9
105
|
}
|
|
10
106
|
export interface AuthorizationVerifier {
|
|
11
107
|
verifyAndConsume(captured: CapturedIntent, decision: GateDecision): Promise<VerifiedAuthorization | null>;
|
|
108
|
+
/**
|
|
109
|
+
* Reports the downstream attempt outcome for an authorization returned by
|
|
110
|
+
* this verifier's `verifyAndConsume`. Finalization is evidence, never
|
|
111
|
+
* authority: it must not retry a side effect, and it must not throw.
|
|
112
|
+
*/
|
|
113
|
+
finalize?(input: AuthorizationFinalizationInput): Promise<"RECORDED" | "PENDING">;
|
|
12
114
|
}
|
|
13
115
|
export interface DecionisGrantVerifierOptions {
|
|
14
116
|
readonly baseUrl: string;
|
|
15
|
-
|
|
117
|
+
/** The Decionis credential, as a value or as a function read at each request. */
|
|
118
|
+
readonly apiKey: Credential;
|
|
16
119
|
readonly timeoutMs?: number;
|
|
17
120
|
readonly fetch?: typeof fetch;
|
|
18
121
|
readonly allowInsecureLoopback?: boolean;
|
|
19
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Claims a Decionis execution grant through `/v1/execution/claim-token`
|
|
125
|
+
* immediately before dispatch and finalizes the attempt through
|
|
126
|
+
* `/v1/execution/finalize-token` afterwards, so commit evidence joins the
|
|
127
|
+
* Decision Dossier chain.
|
|
128
|
+
*/
|
|
20
129
|
export declare class DecionisGrantVerifier implements AuthorizationVerifier {
|
|
21
130
|
private readonly baseUrl;
|
|
22
131
|
private readonly apiKey;
|
|
23
132
|
private readonly timeoutMs;
|
|
24
133
|
private readonly fetchImpl;
|
|
134
|
+
/** Claim material is keyed by the frozen authorization it belongs to and never exposed. */
|
|
135
|
+
private readonly claims;
|
|
136
|
+
/** The authority's own answer about an observation, keyed by the frozen authorization. */
|
|
137
|
+
private readonly reports;
|
|
138
|
+
/**
|
|
139
|
+
* The digest the authority binds over the action parameters: SHA-256 of
|
|
140
|
+
* their RFC 8785 canonical form, the same canonicalization the intent hash
|
|
141
|
+
* uses. Exposed so a test, or an authority implementation, can produce the
|
|
142
|
+
* value this verifier will accept.
|
|
143
|
+
*/
|
|
144
|
+
static parametersDigest(parameters: JsonObject): `sha256:${string}`;
|
|
25
145
|
constructor(options: DecionisGrantVerifierOptions);
|
|
26
146
|
verifyAndConsume(captured: CapturedIntent, decision: GateDecision): Promise<VerifiedAuthorization | null>;
|
|
147
|
+
finalize(input: AuthorizationFinalizationInput): Promise<"RECORDED" | "PENDING">;
|
|
148
|
+
/**
|
|
149
|
+
* What the authority reported about effect evidence for an authorization this
|
|
150
|
+
* verifier finalized. `null` until a finalization was recorded. This is the
|
|
151
|
+
* authority's statement, never this package's inference.
|
|
152
|
+
*/
|
|
153
|
+
effectReport(authorization: VerifiedAuthorization): AuthorityEffectReport | null;
|
|
154
|
+
/**
|
|
155
|
+
* `REFUSED` only for the one status a finalization refusal uses (409); every
|
|
156
|
+
* other non-ok status, a throw, a timeout, or an oversize body is `PENDING`,
|
|
157
|
+
* exactly as it was before effect evidence existed. A rate-limited or
|
|
158
|
+
* unauthenticated authority must never be re-posted to.
|
|
159
|
+
*/
|
|
160
|
+
private post;
|
|
161
|
+
/**
|
|
162
|
+
* Effect evidence reaches the authority only when this executor can prove the
|
|
163
|
+
* authority will bind it: it must conform to the contract's bounded shape,
|
|
164
|
+
* the grant the authority returned must have committed to an expected-effect
|
|
165
|
+
* digest, the evidence must name that exact digest, and it must carry the
|
|
166
|
+
* correlation id this claim was made with. Unbindable evidence is dropped
|
|
167
|
+
* rather than sent, because the authority answers it with a 409 that aborts
|
|
168
|
+
* the whole finalization rather than discarding the observation.
|
|
169
|
+
*
|
|
170
|
+
* The checks run against the parsed copy, which is also the copy that is
|
|
171
|
+
* serialised, so a caller object with accessors cannot present one value to
|
|
172
|
+
* the drop rule and another to the wire.
|
|
173
|
+
*/
|
|
174
|
+
private static bindableEffectEvidence;
|
|
27
175
|
}
|
|
28
176
|
//# sourceMappingURL=AuthorizationVerifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthorizationVerifier.d.ts","sourceRoot":"","sources":["../../src/execution/AuthorizationVerifier.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AuthorizationVerifier.d.ts","sourceRoot":"","sources":["../../src/execution/AuthorizationVerifier.ts"],"names":[],"mappings":"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"}
|