@fourier-labs/harbour-contracts 0.3.1 → 0.5.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 +1 -1
- package/dist/gen-ts/ai-callsite-inventory.d.ts +49 -0
- package/dist/gen-ts/ai-gateway-key-lookup.d.ts +20 -0
- package/dist/gen-ts/ai-gateway-key-token.d.ts +4 -0
- package/dist/gen-ts/ai-gateway-key-token.js +6 -0
- package/dist/gen-ts/ai-governance-decision.d.ts +74 -0
- package/dist/gen-ts/ai-policy-snapshot.d.ts +429 -0
- package/dist/gen-ts/ai-runtime-contract.d.ts +27 -0
- package/dist/gen-ts/ai-trace-event.d.ts +68 -0
- package/dist/gen-ts/ai-transform-event.d.ts +66 -0
- package/dist/gen-ts/ai-transform-result.d.ts +32 -0
- package/dist/gen-ts/ai-verification-evidence.d.ts +49 -0
- package/dist/gen-ts/data-dependency.d.ts +42 -0
- package/dist/gen-ts/data-grant.d.ts +58 -0
- package/dist/gen-ts/data-grant.js +12 -0
- package/dist/gen-ts/data-source.d.ts +74 -0
- package/dist/{deploy-request.d.ts → gen-ts/deploy-request.d.ts} +30 -0
- package/dist/gen-ts/deploy-response.js +3 -0
- package/dist/gen-ts/deployment-environment.js +3 -0
- package/dist/gen-ts/deployment-source.js +3 -0
- package/dist/gen-ts/deployment-status-failure.js +3 -0
- package/dist/gen-ts/deployment-status-request.js +3 -0
- package/dist/{deployment-status-response.d.ts → gen-ts/deployment-status-response.d.ts} +2 -2
- package/dist/gen-ts/deployment-status-response.js +3 -0
- package/dist/gen-ts/env-bindings.js +3 -0
- package/dist/{error-category-registry.d.ts → gen-ts/error-category-registry.d.ts} +7 -0
- package/dist/{error-category-registry.js → gen-ts/error-category-registry.js} +14 -0
- package/dist/gen-ts/error-category.js +3 -0
- package/dist/gen-ts/error-envelope.js +3 -0
- package/dist/{index.d.ts → gen-ts/index.d.ts} +16 -0
- package/dist/{index.js → gen-ts/index.js} +16 -0
- package/dist/gen-ts/kvs-routing-entry.js +3 -0
- package/dist/gen-ts/promotion-request.d.ts +17 -0
- package/dist/gen-ts/promotion-request.js +3 -0
- package/dist/gen-ts/promotion-response.d.ts +17 -0
- package/dist/gen-ts/promotion-response.js +3 -0
- package/dist/gen-ts/secret-reference.d.ts +36 -0
- package/dist/gen-ts/secret-reference.js +3 -0
- package/dist/{version-state-machine.d.ts → gen-ts/version-state-machine.d.ts} +7 -2
- package/dist/gen-ts/version-state-machine.js +45 -0
- package/dist/gen-ts/version-state.js +3 -0
- package/dist/resolver/index.d.ts +256 -0
- package/dist/resolver/index.js +679 -0
- package/package.json +14 -4
- package/schema/ai-callsite-inventory.schema.json +138 -0
- package/schema/ai-gateway-key-lookup.schema.json +53 -0
- package/schema/ai-governance-decision.schema.json +105 -0
- package/schema/ai-policy-snapshot.schema.json +340 -0
- package/schema/ai-runtime-contract.schema.json +85 -0
- package/schema/ai-trace-event.schema.json +178 -0
- package/schema/ai-transform-event.schema.json +72 -0
- package/schema/ai-transform-result.schema.json +46 -0
- package/schema/ai-verification-evidence.schema.json +57 -0
- package/schema/data-dependency.schema.json +89 -0
- package/schema/data-grant.schema.json +107 -0
- package/schema/data-source.schema.json +151 -0
- package/schema/deploy-request.schema.json +60 -0
- package/schema/deploy-response.schema.json +1 -1
- package/schema/deployment-status-request.schema.json +1 -1
- package/schema/deployment-status-response.schema.json +2 -2
- package/schema/error-category.enum.json +7 -0
- package/schema/promotion-request.schema.json +26 -0
- package/schema/promotion-response.schema.json +50 -0
- package/schema/secret-reference.schema.json +70 -0
- package/schema/version-state.schema.json +25 -24
- package/dist/version-state-machine.js +0 -35
- /package/dist/{api-error-response.js → gen-ts/ai-callsite-inventory.js} +0 -0
- /package/dist/{artifact-contract.js → gen-ts/ai-gateway-key-lookup.js} +0 -0
- /package/dist/{deploy-request.js → gen-ts/ai-governance-decision.js} +0 -0
- /package/dist/{deploy-response.js → gen-ts/ai-policy-snapshot.js} +0 -0
- /package/dist/{deployment-environment.js → gen-ts/ai-runtime-contract.js} +0 -0
- /package/dist/{deployment-source.js → gen-ts/ai-trace-event.js} +0 -0
- /package/dist/{deployment-status-failure.js → gen-ts/ai-transform-event.js} +0 -0
- /package/dist/{deployment-status-request.js → gen-ts/ai-transform-result.js} +0 -0
- /package/dist/{deployment-status-response.js → gen-ts/ai-verification-evidence.js} +0 -0
- /package/dist/{api-error-response.d.ts → gen-ts/api-error-response.d.ts} +0 -0
- /package/dist/{env-bindings.js → gen-ts/api-error-response.js} +0 -0
- /package/dist/{artifact-contract.d.ts → gen-ts/artifact-contract.d.ts} +0 -0
- /package/dist/{error-category.js → gen-ts/artifact-contract.js} +0 -0
- /package/dist/{error-envelope.js → gen-ts/data-dependency.js} +0 -0
- /package/dist/{kvs-routing-entry.js → gen-ts/data-source.js} +0 -0
- /package/dist/{version-state.js → gen-ts/deploy-request.js} +0 -0
- /package/dist/{deploy-response.d.ts → gen-ts/deploy-response.d.ts} +0 -0
- /package/dist/{deployment-environment.d.ts → gen-ts/deployment-environment.d.ts} +0 -0
- /package/dist/{deployment-source.d.ts → gen-ts/deployment-source.d.ts} +0 -0
- /package/dist/{deployment-status-failure.d.ts → gen-ts/deployment-status-failure.d.ts} +0 -0
- /package/dist/{deployment-status-request.d.ts → gen-ts/deployment-status-request.d.ts} +0 -0
- /package/dist/{env-bindings.d.ts → gen-ts/env-bindings.d.ts} +0 -0
- /package/dist/{error-category.d.ts → gen-ts/error-category.d.ts} +0 -0
- /package/dist/{error-envelope.d.ts → gen-ts/error-envelope.d.ts} +0 -0
- /package/dist/{kvs-routing-entry.d.ts → gen-ts/kvs-routing-entry.d.ts} +0 -0
- /package/dist/{version-state.d.ts → gen-ts/version-state.d.ts} +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
export const States = {
|
|
4
|
+
QUEUED: "QUEUED",
|
|
5
|
+
ANALYZING: "ANALYZING",
|
|
6
|
+
TRANSFORMING: "TRANSFORMING",
|
|
7
|
+
VALIDATING: "VALIDATING",
|
|
8
|
+
BUILDING: "BUILDING",
|
|
9
|
+
VERIFYING: "VERIFYING",
|
|
10
|
+
GATING: "GATING",
|
|
11
|
+
LIVE: "LIVE",
|
|
12
|
+
AWAITINGPROMOTION: "AWAITING-PROMOTION",
|
|
13
|
+
PROMOTING: "PROMOTING",
|
|
14
|
+
FAILED: "FAILED",
|
|
15
|
+
STALE: "STALE",
|
|
16
|
+
};
|
|
17
|
+
export const Environments = {
|
|
18
|
+
Preview: "preview",
|
|
19
|
+
Prod: "prod",
|
|
20
|
+
};
|
|
21
|
+
const legalTransitions = {
|
|
22
|
+
"preview": {
|
|
23
|
+
"QUEUED": { "ANALYZING": true, "FAILED": true, "STALE": true },
|
|
24
|
+
"ANALYZING": { "BUILDING": true, "TRANSFORMING": true, "FAILED": true, "STALE": true },
|
|
25
|
+
"TRANSFORMING": { "VALIDATING": true, "FAILED": true, "STALE": true },
|
|
26
|
+
"VALIDATING": { "BUILDING": true, "TRANSFORMING": true, "FAILED": true, "STALE": true },
|
|
27
|
+
"BUILDING": { "VERIFYING": true, "FAILED": true, "STALE": true },
|
|
28
|
+
"VERIFYING": { "GATING": true, "FAILED": true, "STALE": true },
|
|
29
|
+
"GATING": { "LIVE": true, "FAILED": true, "STALE": true },
|
|
30
|
+
"LIVE": { "AWAITING-PROMOTION": true },
|
|
31
|
+
"AWAITING-PROMOTION": { "PROMOTING": true },
|
|
32
|
+
"PROMOTING": { "LIVE": true, "FAILED": true },
|
|
33
|
+
},
|
|
34
|
+
"prod": {
|
|
35
|
+
"QUEUED": { "PROMOTING": true, "FAILED": true },
|
|
36
|
+
"PROMOTING": { "LIVE": true, "FAILED": true },
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
const terminalStates = new Set(["FAILED", "STALE"]);
|
|
40
|
+
/** True iff from→to is a legal edge for environment in the T1a state machine. */
|
|
41
|
+
export function canTransition(environment, from, to) {
|
|
42
|
+
return Boolean(legalTransitions[environment]?.[from]?.[to]);
|
|
43
|
+
}
|
|
44
|
+
/** True iff the state is terminal (FAILED, STALE). */
|
|
45
|
+
export function isTerminal(state) { return terminalStates.has(state); }
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harbour AI policy resolver — the ONE hardened five-layer merge, shipped with
|
|
3
|
+
* the contracts package so governance and the gateway resolve identically
|
|
4
|
+
* (completion-spec D-6). Extracted from
|
|
5
|
+
* `harbour-governance-control-plane/src/ai-governance.ts` (merge machinery)
|
|
6
|
+
* and `src/ai-platform-controls.ts` (platform baseline + classification
|
|
7
|
+
* floor) as PURE functions over the contracted snapshot types: no store, no
|
|
8
|
+
* service, no I/O, no crypto.
|
|
9
|
+
*
|
|
10
|
+
* Five-layer resolution per LLD §6.3 (lines 525-542): platform baseline →
|
|
11
|
+
* data-classification minimum → tenant published defaults → application
|
|
12
|
+
* published assignment → version runtime contract. Later layers narrow
|
|
13
|
+
* earlier ones; an application snapshot never replaces the tenant snapshot.
|
|
14
|
+
*
|
|
15
|
+
* The merge is TENANT-WINS by default. Three structural defenses carried over
|
|
16
|
+
* from the governance hardening rounds:
|
|
17
|
+
*
|
|
18
|
+
* - `narrowField` — ONE CONTROL, ONE GUARD. The clamp is the single statement
|
|
19
|
+
* of each control and the rejection is DERIVED from it (a value the clamp
|
|
20
|
+
* had to change is an attempted widening, which §6.3 line 540 requires to be
|
|
21
|
+
* rejected rather than silently narrowed), so deleting the clamp always
|
|
22
|
+
* changes an observable outcome and every control is mutation-testable.
|
|
23
|
+
* - `narrowEntry` closes the FIELD-level default: only fields on an explicit
|
|
24
|
+
* per-family opt-in list may differ from the tenant entry; every other field
|
|
25
|
+
* — including any field a future contract revision adds — is tenant-locked.
|
|
26
|
+
* - `mergeKeyedFamily` closes the ENTRY-level default: both directions of the
|
|
27
|
+
* key set (a tenant key the application omits, and an application key the
|
|
28
|
+
* tenant never published) must be stated per family; there is no permissive
|
|
29
|
+
* fallthrough for an unenumerated key.
|
|
30
|
+
*
|
|
31
|
+
* The `Narrowed` brand is the registry's type-level counterpart: a family
|
|
32
|
+
* merge must RETURN a `Narrowed` value, and the brand is granted only by the
|
|
33
|
+
* narrowing combinators, so a passthrough `(tenant, application) =>
|
|
34
|
+
* application` fails to compile and a deliberate escape requires a visible
|
|
35
|
+
* cast.
|
|
36
|
+
*/
|
|
37
|
+
import type { AiPolicySnapshot, AiPolicyGuardrailRule } from "../gen-ts/ai-policy-snapshot.js";
|
|
38
|
+
export type { AiPolicySnapshot, AiPolicyGuardrailRule, AiPolicyGuardrailParameters, AiPolicyRetention, AiPolicyRoute, AiPolicyRuleSet, } from "../gen-ts/ai-policy-snapshot.js";
|
|
39
|
+
/** Version of the resolver ALGORITHM (merge rules + floor artifacts), not of
|
|
40
|
+
* the package. Consumers record it beside the composite policy digest so a
|
|
41
|
+
* behavioral divergence between two deployments is attributable to a resolver
|
|
42
|
+
* revision rather than guessed at. Bump on ANY change to the merge rules, the
|
|
43
|
+
* platform baseline, or the classification minimums. */
|
|
44
|
+
export declare const RESOLVER_VERSION = "1.0.0";
|
|
45
|
+
export type AiProviderType = "openai" | "anthropic" | "gemini" | "openrouter";
|
|
46
|
+
export type AiCaptureMode = "OFF" | "BEST_EFFORT" | "REQUIRED";
|
|
47
|
+
export type AiDataClassification = "Green" | "Yellow" | "Orange" | "Red";
|
|
48
|
+
export type AiGuardrailAction = "ALLOW" | "OBSERVE" | "REDACT" | "BLOCK";
|
|
49
|
+
export type AiGuardrailMinimum = {
|
|
50
|
+
ruleId: string;
|
|
51
|
+
detectorId: string;
|
|
52
|
+
detectorVersion: string;
|
|
53
|
+
phase: "input" | "output";
|
|
54
|
+
action: AiGuardrailAction;
|
|
55
|
+
};
|
|
56
|
+
/** One control set. Every allowlist is an upper bound on what later layers may
|
|
57
|
+
* permit, every numeric field is a ceiling, and `requiredGuardrails` is a
|
|
58
|
+
* floor that later layers may only strengthen. */
|
|
59
|
+
export type AiControlSet = {
|
|
60
|
+
permittedProviderTypes: AiProviderType[];
|
|
61
|
+
requireApprovedModels: boolean;
|
|
62
|
+
maxCaptureMode: AiCaptureMode;
|
|
63
|
+
maxRetentionDays: number;
|
|
64
|
+
maxRequestBytes: number;
|
|
65
|
+
maxInputTokens: number;
|
|
66
|
+
maxOutputTokens: number;
|
|
67
|
+
maxRequestsPerMinute: number;
|
|
68
|
+
maxTokensPerMinute: number;
|
|
69
|
+
maxMonthlyBudgetMicroUsd: number;
|
|
70
|
+
permittedRequestFields: {
|
|
71
|
+
temperature: boolean;
|
|
72
|
+
maxOutputTokens: boolean;
|
|
73
|
+
responseFormat: boolean;
|
|
74
|
+
tools: boolean;
|
|
75
|
+
};
|
|
76
|
+
requiredGuardrails: AiGuardrailMinimum[];
|
|
77
|
+
};
|
|
78
|
+
export type AiPlatformBaselineArtifact = {
|
|
79
|
+
schema: "harbour.ai-platform-baseline/1.0";
|
|
80
|
+
artifactVersion: string;
|
|
81
|
+
controls: AiControlSet;
|
|
82
|
+
};
|
|
83
|
+
export type AiClassificationMappingArtifact = {
|
|
84
|
+
schema: "harbour.ai-classification-minimums/1.0";
|
|
85
|
+
artifactVersion: string;
|
|
86
|
+
/** LLD §6.3 line 538: applies until the security-assurance workflow stamps a
|
|
87
|
+
* classification and the tenant policy names its own default floor. */
|
|
88
|
+
defaultClassification: AiDataClassification;
|
|
89
|
+
classes: Record<AiDataClassification, AiControlSet>;
|
|
90
|
+
};
|
|
91
|
+
export declare const AI_PLATFORM_BASELINE: AiPlatformBaselineArtifact;
|
|
92
|
+
/** LLD §17 item 11 (line 1225) leaves the per-class values to be agreed with
|
|
93
|
+
* the security-assurance workstream; these are the initial code-owned values
|
|
94
|
+
* and are versioned by `artifactVersion` plus `RESOLVER_VERSION`. */
|
|
95
|
+
export declare const AI_CLASSIFICATION_MINIMUMS: AiClassificationMappingArtifact;
|
|
96
|
+
export declare const CAPTURE_MODE_RANK: Record<AiCaptureMode, number>;
|
|
97
|
+
/** Ascending strictness. Used to fail closed on the classification layer: an
|
|
98
|
+
* application-published snapshot may raise the class but never lower it. */
|
|
99
|
+
export declare const CLASSIFICATION_RANK: Record<AiDataClassification, number>;
|
|
100
|
+
export declare function strictestClassification(left: AiDataClassification, right: AiDataClassification): AiDataClassification;
|
|
101
|
+
export declare const GUARDRAIL_ACTION_RANK: Record<AiGuardrailAction, number>;
|
|
102
|
+
/** Element-wise most restrictive of two control sets. Used to fold the
|
|
103
|
+
* classification minimum into the platform baseline before any tenant or
|
|
104
|
+
* application layer is applied. */
|
|
105
|
+
export declare function narrowControls(left: AiControlSet, right: AiControlSet): AiControlSet;
|
|
106
|
+
/** Platform baseline folded with one classification's minimum control set:
|
|
107
|
+
* layers 1 and 2 of §6.3 as a single floor that later layers may only narrow. */
|
|
108
|
+
export declare function classificationFloor(classification: AiDataClassification): AiControlSet;
|
|
109
|
+
/** Every rejection carries a stable code and HTTP-shaped status so both
|
|
110
|
+
* resolver consumers surface identical errors for identical escapes. */
|
|
111
|
+
export declare class AiPolicyResolutionError extends Error {
|
|
112
|
+
readonly status: number;
|
|
113
|
+
readonly code: string;
|
|
114
|
+
constructor(status: number, code: string, message: string);
|
|
115
|
+
}
|
|
116
|
+
/** Stable codes for a later layer that tried to widen, not narrow, an earlier
|
|
117
|
+
* one (LLD §6.3 line 540). The audit acceptance criterion is rejection, never
|
|
118
|
+
* silent narrowing. */
|
|
119
|
+
export declare const WIDENS_TENANT_ALLOWLIST = "configuration_widens_tenant_allowlist";
|
|
120
|
+
export declare const RAISES_TENANT_CEILING = "configuration_raises_tenant_ceiling";
|
|
121
|
+
export declare const WEAKENS_TENANT_GUARDRAIL = "configuration_weakens_tenant_guardrail";
|
|
122
|
+
/** An application entry tried to change a field only the tenant may set. */
|
|
123
|
+
export declare const OVERRIDES_TENANT_LOCKED = "configuration_overrides_tenant_locked_field";
|
|
124
|
+
/** The merge output outgrew a `maxItems` bound of the snapshot contract. */
|
|
125
|
+
export declare const EXCEEDS_CONTRACT_BOUNDS = "configuration_exceeds_contract_bounds";
|
|
126
|
+
/** Canonical JSON with sorted object keys — the equality the tenant-locked
|
|
127
|
+
* comparisons are stated in, so key order can never disguise a difference. */
|
|
128
|
+
export declare function canonicalJson(value: unknown): string;
|
|
129
|
+
/** The snapshot fields that identify and seal a snapshot rather than state a
|
|
130
|
+
* control. Everything a snapshot carries that is NOT one of these is a policy
|
|
131
|
+
* family that resolution must merge.
|
|
132
|
+
*
|
|
133
|
+
* Stated as an EXCLUSION on purpose: with `Omit<>`, a new snapshot field is a
|
|
134
|
+
* member of `AiScopePolicySet` the moment it is declared, and
|
|
135
|
+
* `SCOPE_FAMILY_MERGES` below (a `Record` over `keyof AiScopePolicySet`) fails
|
|
136
|
+
* to compile until someone states its merge rules. A `Pick<>` here previously
|
|
137
|
+
* let a seventh family parse, store, and enter the snapshot digest while being
|
|
138
|
+
* silently dropped from the effective policy. */
|
|
139
|
+
type AiPolicySnapshotIdentity = "schema" | "scope" | "revision" | "publishedAt" | "contentDigest" | "baselineDigest" | "classificationMinimum";
|
|
140
|
+
/** The resource families a scope snapshot contributes to resolution. */
|
|
141
|
+
export type AiScopePolicySet = Omit<AiPolicySnapshot, AiPolicySnapshotIdentity>;
|
|
142
|
+
/** The merged control set every later layer narrowed but could not weaken. */
|
|
143
|
+
export type AiEffectivePolicy = AiScopePolicySet & {
|
|
144
|
+
classification: AiDataClassification;
|
|
145
|
+
};
|
|
146
|
+
/** The assigned model aliases: which capabilities a deployment may use and
|
|
147
|
+
* which route serves each. Mirrors `AiRuntimeContract.modelAliases`. */
|
|
148
|
+
export type AiModelAliasAssignment = {
|
|
149
|
+
generation?: string;
|
|
150
|
+
embeddings?: string;
|
|
151
|
+
};
|
|
152
|
+
declare const narrowedBrand: unique symbol;
|
|
153
|
+
/** Granted only by the narrowing combinators (`mergeKeyedFamily`,
|
|
154
|
+
* `narrowEntry`); a family merge must return it, so a compliant-looking
|
|
155
|
+
* passthrough fails to compile. */
|
|
156
|
+
export type Narrowed<T> = T & {
|
|
157
|
+
readonly [narrowedBrand]: true;
|
|
158
|
+
};
|
|
159
|
+
/** ONE CONTROL, ONE GUARD: the clamp is the single statement of the control
|
|
160
|
+
* and the rejection is derived from it, so deleting the clamp always changes
|
|
161
|
+
* an observable outcome and the control is mutation-testable. */
|
|
162
|
+
export declare function narrowField<T>(tenant: T, application: T, clamp: (tenantValue: T, applicationValue: T) => T, code: string, describe: () => string): T;
|
|
163
|
+
/** Merges one application entry over its tenant counterpart so that
|
|
164
|
+
* TENANT-WINS is the DEFAULT: only fields named in `narrowable` are replaced
|
|
165
|
+
* by the caller-computed `narrowed` values; every other field — including any
|
|
166
|
+
* field a future contract revision adds — must equal the tenant value or the
|
|
167
|
+
* entry is rejected. Spreading the application entry is never correct here. */
|
|
168
|
+
export declare function narrowEntry<T extends object>(tenant: T, application: T, narrowable: readonly string[], narrowed: Partial<T>, describe: (field: string) => string): Narrowed<T>;
|
|
169
|
+
/** What an application entry whose key the tenant never published means. The
|
|
170
|
+
* discriminant is required, so a family added by a later contract revision
|
|
171
|
+
* cannot inherit a permissive answer by omission. */
|
|
172
|
+
export type AddedEntryRule<T> = {
|
|
173
|
+
decision: "reject";
|
|
174
|
+
code: string;
|
|
175
|
+
describe: (entry: T) => string;
|
|
176
|
+
}
|
|
177
|
+
/** `admit` returns the entry to merge in, or throws — an added entry may
|
|
178
|
+
* need more than one guard (an added guardrail must both restrict AND carry
|
|
179
|
+
* no parameters the tenant never bounded). */
|
|
180
|
+
| {
|
|
181
|
+
decision: "permit_when_restrictive";
|
|
182
|
+
admit: (entry: T) => T;
|
|
183
|
+
};
|
|
184
|
+
/** The complete, explicit merge contract of one key-addressed family. Both
|
|
185
|
+
* directions of the key set must be stated; leaving either implicit is exactly
|
|
186
|
+
* how the escape rounds arose. */
|
|
187
|
+
export type KeyedMergeRules<T> = {
|
|
188
|
+
key: (entry: T) => string;
|
|
189
|
+
/** A tenant key the application does not repeat. `drop` for allowlists,
|
|
190
|
+
* where omission is the narrowing; `keep` for restriction sets and ceiling
|
|
191
|
+
* sets, where omission must not delete the tenant's control. */
|
|
192
|
+
omittedByApplication: "drop" | "keep";
|
|
193
|
+
added: AddedEntryRule<T>;
|
|
194
|
+
/** Applied when the keys match; every family delegates to `narrowEntry`. */
|
|
195
|
+
narrow: (tenant: T, application: T) => T;
|
|
196
|
+
};
|
|
197
|
+
/** The single path by which any key-addressed family merges: no per-family
|
|
198
|
+
* loop can quietly grow its own `if (!base)` branch. */
|
|
199
|
+
export declare function mergeKeyedFamily<T>(tenant: readonly T[], application: readonly T[], rules: KeyedMergeRules<T>): Narrowed<T[]>;
|
|
200
|
+
/** A grant can only flow down: an application may drop a capability, but may
|
|
201
|
+
* neither assign one the tenant did not assign nor repoint an assignment at
|
|
202
|
+
* another route. `{ ...tenant, ...application }` made the application the
|
|
203
|
+
* authority for both. */
|
|
204
|
+
export declare function mergeModelAliases(tenant: AiModelAliasAssignment, application: AiModelAliasAssignment): AiModelAliasAssignment;
|
|
205
|
+
/** `Record`-over-`keyof AiScopePolicySet` is the exhaustiveness mechanism:
|
|
206
|
+
* because `AiScopePolicySet` is an `Omit<>` of the snapshot rather than a
|
|
207
|
+
* `Pick<>`, a new snapshot family is a new key of this mapped type, and this
|
|
208
|
+
* declaration fails to compile until its merge rules are stated. */
|
|
209
|
+
export type ScopeFamilyMerges = {
|
|
210
|
+
[K in keyof AiScopePolicySet]-?: (tenant: AiScopePolicySet[K], application: AiScopePolicySet[K]) => Narrowed<AiScopePolicySet[K]>;
|
|
211
|
+
};
|
|
212
|
+
export declare function scopeFamilies(): Array<keyof AiScopePolicySet>;
|
|
213
|
+
/** The single-scope projection: the snapshot's policy families with the
|
|
214
|
+
* identity fields subtracted, re-checked against the contract bounds. */
|
|
215
|
+
export declare function scopePolicySet(snapshot: AiPolicySnapshot): AiScopePolicySet;
|
|
216
|
+
/** Layers 3 and 4 of §6.3: the application/environment snapshot narrows the
|
|
217
|
+
* tenant snapshot per family — intersection for allowlists, minimum for
|
|
218
|
+
* numeric ceilings, union for guardrail restrictions — and any attempt to
|
|
219
|
+
* widen is rejected. Driven off `SCOPE_FAMILY_MERGES` rather than written out,
|
|
220
|
+
* so there is no place left for a family to be answered by omission. */
|
|
221
|
+
export declare function mergeScopeSnapshots(tenant: AiPolicySnapshot, application: AiPolicySnapshot): AiScopePolicySet;
|
|
222
|
+
/** An application entry the merge admits — today only a restricting guardrail
|
|
223
|
+
* the tenant never published — is the one way the merge OUTPUT can be larger
|
|
224
|
+
* than either input, so the merged set and the effective set are both
|
|
225
|
+
* re-validated against every `maxItems` in
|
|
226
|
+
* `schema/ai-policy-snapshot.schema.json`. */
|
|
227
|
+
export declare function assertContractBounds<T extends AiScopePolicySet>(set: T): T;
|
|
228
|
+
/** The floor is not negotiable, so it clamps the merged tenant/application
|
|
229
|
+
* set instead of rejecting it: a stamped classification arrives after
|
|
230
|
+
* publication and must still bind. */
|
|
231
|
+
export declare function applyControlFloor(merged: AiScopePolicySet, floor: AiControlSet, classification: AiDataClassification): AiEffectivePolicy;
|
|
232
|
+
/** A platform-required rule's IDENTITY is non-negotiable and replaces whatever
|
|
233
|
+
* held the id (a snapshot could otherwise pre-occupy a required rule id with a
|
|
234
|
+
* NOOP detector of its own choosing); only `action` is negotiable, and only
|
|
235
|
+
* upward. `parameters` go with the displaced entry, because they tune that
|
|
236
|
+
* entry's detector rather than the platform's. */
|
|
237
|
+
export declare function applyRequiredGuardrails(guardrails: AiPolicyGuardrailRule[], required: AiGuardrailMinimum[]): AiPolicyGuardrailRule[];
|
|
238
|
+
/** The snapshot half of five-layer resolution, composed: classification from
|
|
239
|
+
* the published minimums (strictest wins; the platform default applies when no
|
|
240
|
+
* layer states one) optionally raised by a stamped classification, the
|
|
241
|
+
* tenant/application merge (a single present scope resolves alone), then the
|
|
242
|
+
* classification floor. Version controls (mode, gateway endpoint, alias
|
|
243
|
+
* grant) live on the published-configuration wrapper, which is not part of
|
|
244
|
+
* the snapshot contract — both consumers resolve them beside this call, using
|
|
245
|
+
* `mergeModelAliases` for the grant. */
|
|
246
|
+
export declare function resolveEffectiveScopePolicy(input: {
|
|
247
|
+
tenant?: AiPolicySnapshot;
|
|
248
|
+
application?: AiPolicySnapshot;
|
|
249
|
+
/** The security-assurance stamp for the deployment version, when one
|
|
250
|
+
* exists. It can raise the class above what the snapshots publish, never
|
|
251
|
+
* lower it. */
|
|
252
|
+
stampedClassification?: AiDataClassification;
|
|
253
|
+
}): {
|
|
254
|
+
classification: AiDataClassification;
|
|
255
|
+
effective: AiEffectivePolicy;
|
|
256
|
+
};
|