@atlasent/sdk 2.5.0 → 2.12.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.
@@ -45,7 +45,7 @@ declare class StreamParseError extends Error {
45
45
  constructor(rawData: string, cause?: unknown);
46
46
  }
47
47
  /** Discriminator for {@link AtlaSentError.code}. */
48
- type AtlaSentErrorCode = "invalid_api_key" | "forbidden" | "rate_limited" | "timeout" | "network" | "bad_response" | "bad_request" | "server_error" | "feature_disabled";
48
+ type AtlaSentErrorCode = "invalid_api_key" | "forbidden" | "rate_limited" | "timeout" | "network" | "bad_response" | "bad_request" | "server_error" | "feature_disabled" | "claim_evidence_incomplete";
49
49
  /** Initialization options for {@link AtlaSentError}. */
50
50
  interface AtlaSentErrorInit {
51
51
  status?: number;
@@ -97,7 +97,7 @@ type AtlaSentDecision = "deny" | "hold" | "escalate";
97
97
  * `atlasent/docs/REVOCATION_RUNBOOK.md` for the operator-facing
98
98
  * matrix this discriminator drives.
99
99
  */
100
- type PermitOutcome = "permit_consumed" | "permit_expired" | "permit_revoked" | "permit_not_found";
100
+ type PermitOutcome = "permit_consumed" | "permit_expired" | "permit_revoked" | "permit_not_found" | "permit_signing_key_revoked";
101
101
  /**
102
102
  * Map a server-supplied `outcome` string to {@link PermitOutcome}.
103
103
  *
@@ -169,6 +169,11 @@ declare class AtlaSentDeniedError extends AtlaSentError {
169
169
  * (typo, cross-tenant lookup, or pre-issuance race).
170
170
  */
171
171
  get isNotFound(): boolean;
172
+ /**
173
+ * `true` when the permit's signing key KID appears in the
174
+ * trust-root revocation list (ADR-005 D3 R2/R3 key rotation).
175
+ */
176
+ get isSigningKeyRevoked(): boolean;
172
177
  }
173
178
  /** Initialization options for {@link AtlaSentEscalateError}. */
174
179
  interface AtlaSentEscalateErrorInit {
@@ -237,6 +242,50 @@ declare class PermitRevoked extends AtlaSentError {
237
242
  readonly revocationId: string | undefined;
238
243
  constructor(permitId: string, revocationId?: string);
239
244
  }
245
+ /**
246
+ * Initialization options for {@link BundleVerificationError}.
247
+ */
248
+ interface BundleVerificationErrorInit {
249
+ /**
250
+ * Machine-readable reason code:
251
+ * - `trust_snapshot_expired`: the snapshot's `valid_until` has passed
252
+ * and `allowExpiredSnapshot` was not set.
253
+ * - `key_revoked`: the bundle's `signing_key_id` appears in
254
+ * `revoked_keys` of the active trust snapshot.
255
+ * - `key_role_mismatch`: the signing key's `role` is not `"R3_audit"`.
256
+ */
257
+ reason: "trust_snapshot_expired" | "key_revoked" | "key_role_mismatch";
258
+ /** ISO-8601 `valid_until` of the snapshot that caused the failure. */
259
+ snapshotValidUntil?: string;
260
+ /** ISO-8601 `issued_at` of the snapshot (its fetch/pin time). */
261
+ snapshotFetchedAt?: string;
262
+ /** Whether the snapshot came from the bundled vendor files or a live refresh. */
263
+ snapshotSource?: "pinned" | "live";
264
+ /** Which key id was revoked or role-mismatched, when applicable. */
265
+ kid?: string;
266
+ }
267
+ /**
268
+ * Thrown by {@link verifyAuditBundle} / {@link verifyBundle} when the
269
+ * active trust-root snapshot is expired (ADR-005 D3) or the bundle's
270
+ * signing key is revoked / has the wrong role.
271
+ *
272
+ * This error is **always thrown** — it is never returned as a
273
+ * {@link BundleVerificationResult} because ADR-005 D3 requires that
274
+ * an expired snapshot or revoked key constitutes a hard enforcement
275
+ * action, not a soft verification failure.
276
+ *
277
+ * To opt out of fail-closed expiry (air-gap / offline use), pass
278
+ * `allowExpiredSnapshot: true` to `verifyBundle`.
279
+ */
280
+ declare class BundleVerificationError extends AtlaSentError {
281
+ name: string;
282
+ readonly reason: BundleVerificationErrorInit["reason"];
283
+ readonly snapshotValidUntil: string | undefined;
284
+ readonly snapshotFetchedAt: string | undefined;
285
+ readonly snapshotSource: "pinned" | "live" | undefined;
286
+ readonly kid: string | undefined;
287
+ constructor(init: BundleVerificationErrorInit);
288
+ }
240
289
 
241
290
  /**
242
291
  * Retry-policy helpers for the AtlaSent TypeScript SDK.
@@ -268,18 +317,18 @@ declare class PermitRevoked extends AtlaSentError {
268
317
  * It avoids thundering-herd retries from many SDK instances that hit
269
318
  * a 429 in the same window.
270
319
  *
271
- * Default schedule matches the Python SDK:
272
- * attempt 0 (1st retry): base 2 000 ms, jittered in [0, 2 000)
273
- * attempt 1 (2nd retry): base 4 000 ms, jittered in [0, 4 000)
274
- * attempt 2 (3rd retry): base 8 000 ms, jittered in [0, 8 000)
275
- * attempt 3 (4th retry): capped at 16 000 ms, jittered in [0, 16 000)
276
- * Total attempts (including initial): 4
320
+ * Default schedule:
321
+ * attempt 0 (1st retry): base 250 ms, jittered in [0, 250)
322
+ * attempt 1 (2nd retry): base 500 ms, jittered in [0, 500)
323
+ * Total attempts (including initial): 3
277
324
  */
278
325
  /**
279
326
  * Defaults for {@link RetryPolicy}.
280
327
  *
281
- * Matches the Python SDK's backoff schedule:
282
- * 2 s 4 s 8 s 16 s (4 total attempts, cap at 16 s).
328
+ * Exponential backoff with full jitter, base 250 ms, capped at 10 s:
329
+ * attempt 0 (1st retry): base 250 ms, jittered in [0, 250)
330
+ * attempt 1 (2nd retry): base 500 ms, jittered in [0, 500)
331
+ * Total attempts (including initial): 3
283
332
  */
284
333
  declare const DEFAULT_RETRY_POLICY: Required<RetryPolicy>;
285
334
  /**
@@ -686,6 +735,59 @@ interface DeployGateResponse {
686
735
  /** Best-effort audit/evidence metadata available to the SDK. */
687
736
  evidence: DeployGateEvidence;
688
737
  }
738
+ /**
739
+ * Frozen BVS snapshot wire shape (BI4).
740
+ * Carried in {@link EvaluateRequest}.context.bvsSnapshot when
741
+ * the `behavior_conditioning` flag is enabled for the tenant.
742
+ * Produced by behavior-insights GET /api/patterns/snapshot/:userId
743
+ * and attached via `@atlasent/behavior` attachToEvaluate().
744
+ */
745
+ interface BvsSnapshot {
746
+ user_id: string;
747
+ /** Factor model output — keyed by BVS factor slug, value is score 0-1. */
748
+ factors: Record<string, number>;
749
+ /** Aggregate confidence score (0-1). Decays on a 60-day half-life. */
750
+ confidence: number;
751
+ /** True when the aggregate is fresh-and-thin (too few events to trust). */
752
+ confidence_low: boolean;
753
+ /** ISO-8601 timestamp of the compute run that produced this snapshot. */
754
+ computed_at: string;
755
+ }
756
+ /**
757
+ * Consent-class projection (BI5) — the privacy-safe aggregate shape that
758
+ * third-party apps (LedgersMe, hiCoach, echobloom) receive when reading a
759
+ * user's behavioral summary. Counts and timestamps only; no raw free-text.
760
+ * Produced by behavior-insights `/api/patterns/summary/:userId` and fetched
761
+ * via `@atlasent/behavior` getStateSummary(). The SDK enforces
762
+ * {@link https://github.com/AtlaSent-Systems-Inc/atlasent-sdk | assertNoRawText}
763
+ * client-side before returning this shape to callers.
764
+ */
765
+ interface ConsentClassProjection {
766
+ user_id: string;
767
+ window_start: string;
768
+ window_end: string;
769
+ event_count: number;
770
+ category_counts: Partial<Record<string, number>>;
771
+ }
772
+ /**
773
+ * Proof that a specific actor consumed a specific permit for a specific
774
+ * action_type. Pass an array of these as `completion_proofs` on an evaluate
775
+ * request to satisfy multi-actor quorum dependencies.
776
+ *
777
+ * The runtime verifies each proof via two gates (both must pass):
778
+ * 1. A `permit_uses` row exists for `permit_id` (permit was consumed).
779
+ * 2. An `execution_evaluations` row is bound to `actor_id` + `action_type`
780
+ * for the same permit (actor/action binding — Codex P1 #1148 FIX #3).
781
+ * Proofs that fail either gate are silently dropped (fail-closed).
782
+ */
783
+ interface CompletionProof {
784
+ /** The action_type (slug) that was completed by the prior actor. */
785
+ action_type: string;
786
+ /** The actor who completed the action. */
787
+ actor_id: string;
788
+ /** The permit token (or its hash) issued when the action was permitted. */
789
+ permit_id: string;
790
+ }
689
791
  /** Input to {@link AtlaSentClient.evaluate}. */
690
792
  interface EvaluateRequest {
691
793
  /** Identifier of the calling agent (e.g. "clinical-data-agent"). */
@@ -694,6 +796,63 @@ interface EvaluateRequest {
694
796
  action: string;
695
797
  /** Arbitrary policy context (user, environment, resource IDs). */
696
798
  context?: Record<string, unknown>;
799
+ /**
800
+ * When `true`, the server populates `riskEnvelope.factors` with a
801
+ * per-factor breakdown of the weighted risk score. Omit (or `false`)
802
+ * to keep response payloads small.
803
+ */
804
+ explain?: boolean;
805
+ /** Deployment environment where the action executes (e.g. `"production"`). */
806
+ environment?: string;
807
+ /** Structured resource descriptor. Prefer over embedding resource info in `context`. */
808
+ resource?: {
809
+ type: string;
810
+ id?: string;
811
+ attributes?: Record<string, unknown>;
812
+ };
813
+ /** Snapshot of the resource state before the proposed action. Enables state-transition-aware policy evaluation. */
814
+ current_state?: {
815
+ description: string;
816
+ attributes?: Record<string, unknown>;
817
+ };
818
+ /** Desired resource state after the action executes. */
819
+ proposed_state?: {
820
+ description: string;
821
+ attributes?: Record<string, unknown>;
822
+ };
823
+ /** Execution surface binding — identifies the CI/CD adapter, DB driver, or enforcement point. */
824
+ execution_binding?: {
825
+ kind: string;
826
+ adapter_version?: string;
827
+ resource_id?: string;
828
+ enforcement_point?: string;
829
+ };
830
+ /** The desired end-state the actor wants the resource to reach. Enables trajectory-aware authorization. */
831
+ desired_state?: {
832
+ description: string;
833
+ attributes?: Record<string, unknown>;
834
+ fingerprint?: string;
835
+ };
836
+ /** Actor-proposed execution path from current_state to desired_state. The engine returns an authorized_trajectory that may differ. */
837
+ proposed_trajectory?: {
838
+ steps: Array<{
839
+ step: string;
840
+ description?: string;
841
+ required: boolean;
842
+ time_limit_seconds?: number;
843
+ authorized_by?: string;
844
+ constraints?: Record<string, unknown>;
845
+ }>;
846
+ description?: string;
847
+ };
848
+ /**
849
+ * Multi-actor quorum completion proofs. Supply one entry per prior actor
850
+ * whose completed action this evaluation depends on. The runtime verifies
851
+ * each proof (consumed-permit gate + actor/action binding gate) and counts
852
+ * only valid proofs toward quorum. Absent or empty → no quorum proofs
853
+ * submitted (no behavioral change for non-quorum dependencies).
854
+ */
855
+ completion_proofs?: CompletionProof[];
697
856
  }
698
857
  /**
699
858
  * Slim permit object embedded in {@link EvaluateResponse} when the decision
@@ -793,6 +952,97 @@ interface EvaluateResponse {
793
952
  * `X-RateLimit-*` headers. `null` when the server didn't emit them.
794
953
  */
795
954
  rateLimit: RateLimitState | null;
955
+ /**
956
+ * Risk envelope summary from the policy engine. Present on all responses
957
+ * from engine version wire-v1@1.0.0+. Provides the weighted risk score,
958
+ * the pre/post-promotion decisions, and (when evaluate was called with
959
+ * `explain: true`) a per-factor breakdown.
960
+ *
961
+ * The envelope can only raise severity — it structurally cannot soften
962
+ * a deny to allow. When `promoted` is true the live `decision` was
963
+ * upgraded from `engineDecision` to `envelopeDecision`.
964
+ */
965
+ riskEnvelope?: EvaluateRiskEnvelope;
966
+ /**
967
+ * Resolved risk class from the evaluation engine.
968
+ * One of `"critical"`, `"high"`, `"medium"`, `"low"`.
969
+ * Present when the risk envelope assigns a class.
970
+ */
971
+ riskClass?: string;
972
+ /**
973
+ * WHY this permit was issued — the authority kind and a reference to the
974
+ * authorizing entity. Present on `allow` decisions when the control plane
975
+ * attaches explicit authority provenance.
976
+ */
977
+ authorityBasis?: {
978
+ kind: "policy" | "approval" | "emergency" | "maintenance_window" | "delegation" | "quorum";
979
+ reference?: string;
980
+ grantedBy?: string;
981
+ rationale?: string;
982
+ expiresAt?: string;
983
+ };
984
+ /**
985
+ * ID of the HITL escalation auto-created by the control plane.
986
+ * Present iff `decision === "hold"`. Poll `GET /v1/escalations/{id}`
987
+ * for resolution status.
988
+ */
989
+ escalationId?: string;
990
+ /**
991
+ * Authorized execution trajectory returned when the engine approved a
992
+ * `proposed_trajectory`. Present only on `allow` decisions.
993
+ * May differ from what was proposed — the engine may add checkpoints,
994
+ * restrict steps, or tighten time limits. Follow this trajectory exactly;
995
+ * call `POST /v1/trajectory-verify` at each step to confirm on_trajectory.
996
+ */
997
+ authorized_trajectory?: {
998
+ trajectory_id: string;
999
+ steps: Array<{
1000
+ step: string;
1001
+ description?: string;
1002
+ required: boolean;
1003
+ time_limit_seconds?: number;
1004
+ authorized_by?: string;
1005
+ constraints?: Record<string, unknown>;
1006
+ expected_intermediate_state?: {
1007
+ description: string;
1008
+ attributes?: Record<string, unknown>;
1009
+ fingerprint?: string;
1010
+ };
1011
+ }>;
1012
+ description?: string;
1013
+ forbidden_states?: Array<{
1014
+ description: string;
1015
+ attributes?: Record<string, unknown>;
1016
+ fingerprint?: string;
1017
+ }>;
1018
+ expires_at: string;
1019
+ };
1020
+ }
1021
+ /** Per-factor contribution in a {@link EvaluateRiskEnvelope}. */
1022
+ interface EvaluateRiskEnvelopeFactor {
1023
+ /** Factor identifier, e.g. `"ACTION_SENSITIVITY"`. */
1024
+ factor: string;
1025
+ /** Factor score in [0, 1]. Higher = more risk. */
1026
+ value: number;
1027
+ /** Configured weight for this factor. */
1028
+ weight: number;
1029
+ /** Human-readable explanation for the score. */
1030
+ reason: string;
1031
+ }
1032
+ /** Risk envelope summary returned in a top-level {@link EvaluateResponse}. */
1033
+ interface EvaluateRiskEnvelope {
1034
+ /** Weighted risk score in [0, 1]. Score ≥ 0.70 triggers a hold. */
1035
+ weightedScore: number;
1036
+ /** Policy engine decision before envelope promotion. */
1037
+ engineDecision: Decision;
1038
+ /** Decision resolved by the risk envelope. */
1039
+ envelopeDecision: Decision;
1040
+ /** `true` when the envelope raised the decision's severity (most-restrictive-wins). */
1041
+ promoted: boolean;
1042
+ /** Deny codes that unconditionally block regardless of score. */
1043
+ hardBlocks: string[];
1044
+ /** Per-factor breakdown. Present only when `explain: true` was passed. */
1045
+ factors?: EvaluateRiskEnvelopeFactor[];
796
1046
  }
797
1047
  /** Input to {@link AtlaSentClient.verifyPermit}. */
798
1048
  interface VerifyPermitRequest {
@@ -838,6 +1088,11 @@ interface VerifyPermitResponse {
838
1088
  permitHash: string;
839
1089
  /** ISO 8601 timestamp of the verification. */
840
1090
  timestamp: string;
1091
+ /**
1092
+ * ISO-8601 expiration timestamp of the permit. `null` on pre-rollout
1093
+ * server versions that do not yet surface this field.
1094
+ */
1095
+ expiresAt: string | null;
841
1096
  /**
842
1097
  * Per-key rate-limit state for this request's response, parsed from
843
1098
  * `X-RateLimit-*` headers. `null` when the server didn't emit them.
@@ -849,18 +1104,13 @@ interface VerifyPermitResponse {
849
1104
  * key the client was constructed with. Returned by `GET /v1/api-key-self`.
850
1105
  *
851
1106
  * Never includes the raw key or its hash — introspection is intentionally
852
- * read-only and safe to surface in operator dashboards. Useful for:
853
- * - "which key am I?" debugging
854
- * - IP_NOT_ALLOWED failures — `clientIp` is the IP the server observed
855
- * - proactive expiry warnings — `expiresAt` is the server-stored expiry
856
- * (`null` means the key does not auto-expire)
857
- * - verifying scopes before attempting a scope-gated action
1107
+ * read-only and safe to surface in operator dashboards.
858
1108
  */
859
1109
  interface ApiKeySelfResponse {
860
1110
  /** Server-side UUID of the api_keys row for this key. */
861
1111
  keyId: string;
862
1112
  /** Organization the key belongs to. */
863
- organizationId: string;
1113
+ orgId: string;
864
1114
  /** "live" or "test" (or any future environment label the server introduces). */
865
1115
  environment: string;
866
1116
  /** Granted scopes — e.g. ["evaluate", "audit.read"]. */
@@ -885,24 +1135,16 @@ interface ApiKeySelfResponse {
885
1135
  /**
886
1136
  * Result of {@link AtlaSentClient.listAuditEvents}. Extends the raw
887
1137
  * wire page with a camelCase `rateLimit` alongside the snake_case
888
- * wire fields — the wire shape (`events`, `total`, `next_cursor`) is
889
- * untouched so callers that pass it to the offline verifier get
890
- * byte-identical behaviour.
1138
+ * wire fields.
891
1139
  */
892
1140
  interface AuditEventsResult extends AuditEventsPage {
893
- /**
894
- * Per-key rate-limit state for this request's response, parsed from
895
- * `X-RateLimit-*` headers. `null` when the server didn't emit them.
896
- */
897
1141
  rateLimit: RateLimitState | null;
898
1142
  }
899
1143
  /**
900
- * Filter accepted by {@link AtlaSentClient.createAuditExport}. Fields
901
- * are snake_case to match the server's `POST /v1-audit/exports`
902
- * request body; an empty object requests a full-org bundle.
1144
+ * Filter accepted by {@link AtlaSentClient.createAuditExport}.
903
1145
  */
904
1146
  interface AuditExportRequest {
905
- /** Comma-joined list of event types to include (e.g. `"evaluate.allow,policy.updated"`). */
1147
+ /** Comma-joined list of event types to include. */
906
1148
  types?: string;
907
1149
  /** Filter to a single actor. */
908
1150
  actor_id?: string;
@@ -912,18 +1154,9 @@ interface AuditExportRequest {
912
1154
  to?: string;
913
1155
  }
914
1156
  /**
915
- * Result of {@link AtlaSentClient.createAuditExport}. Extends the
916
- * signed bundle shape with a camelCase `rateLimit`. The signed
917
- * envelope fields (`export_id`, `org_id`, `chain_head_hash`,
918
- * `event_count`, `signed_at`, `events`, `signature`) are preserved
919
- * byte-for-byte so the object can be handed straight to
920
- * `verifyAuditBundle(bundle, keys)`.
1157
+ * Result of {@link AtlaSentClient.createAuditExport}.
921
1158
  */
922
1159
  interface AuditExportResult extends AuditExport {
923
- /**
924
- * Per-key rate-limit state for this request's response, parsed from
925
- * `X-RateLimit-*` headers. `null` when the server didn't emit them.
926
- */
927
1160
  rateLimit: RateLimitState | null;
928
1161
  }
929
1162
  /** Constructor options for {@link AtlaSentClient}. */
@@ -948,16 +1181,24 @@ interface AtlaSentClientOptions {
948
1181
  * Pass `{ maxAttempts: 1 }` to disable retries entirely.
949
1182
  */
950
1183
  retryPolicy?: RetryPolicy;
1184
+ /**
1185
+ * Base URL for the trust-root host (default: https://keys.atlasent.io/.well-known).
1186
+ * Override for air-gapped / enterprise mirror deployments.
1187
+ * Per ADR-005 D2.
1188
+ */
1189
+ trustRootUrl?: string;
1190
+ /**
1191
+ * Trust-root snapshot refresh interval in milliseconds.
1192
+ * Default: 4 hours. Floor: 5 minutes (ADR-005 D2).
1193
+ * Set to 0 to inherit the default.
1194
+ */
1195
+ trustSnapshotRefreshMs?: number;
951
1196
  }
952
1197
  /** Permit lifecycle status. */
953
1198
  type PermitStatus = "issued" | "verified" | "consumed" | "expired" | "revoked";
954
1199
  /**
955
1200
  * Wire shape of a Permit row, returned by {@link AtlaSentClient.getPermit}
956
- * and {@link AtlaSentClient.listPermits}. Mirrors the openapi `Permit`
957
- * schema.
958
- *
959
- * Revocation fields (`revoked_at`, `revoked_by`, `revoke_reason`) are
960
- * populated only when `status === 'revoked'`; null otherwise.
1201
+ * and {@link AtlaSentClient.listPermits}.
961
1202
  */
962
1203
  interface PermitRecord {
963
1204
  id: string;
@@ -976,27 +1217,23 @@ interface PermitRecord {
976
1217
  signature?: string;
977
1218
  payload_hash?: string | null;
978
1219
  decision_id?: string | null;
1220
+ /** SHA-256 hex of the CDO that produced this permit. P1 provisional. */
1221
+ cdo_hash?: string | null;
979
1222
  }
980
1223
  /** Optional filters for {@link AtlaSentClient.listPermits}. */
981
1224
  interface ListPermitsRequest {
982
1225
  status?: PermitStatus;
983
1226
  actorId?: string;
984
1227
  actionType?: string;
985
- /** ISO-8601 lower bound on `created_at`. */
986
1228
  from?: string;
987
- /** ISO-8601 upper bound on `created_at`. */
988
1229
  to?: string;
989
- /** Page size. Server max is 500; default 50. */
990
1230
  limit?: number;
991
- /** Pass `nextCursor` from a prior response to page forward. */
992
1231
  cursor?: string;
993
1232
  }
994
1233
  /** Response from {@link AtlaSentClient.listPermits}. */
995
1234
  interface ListPermitsResponse {
996
1235
  permits: PermitRecord[];
997
- /** Total matching rows ignoring `limit`/`cursor`. */
998
1236
  total: number;
999
- /** Pass on next call as `cursor`. Absent when no more rows. */
1000
1237
  nextCursor?: string;
1001
1238
  rateLimit: RateLimitState | null;
1002
1239
  }
@@ -1007,41 +1244,19 @@ interface GetPermitResponse {
1007
1244
  }
1008
1245
  /**
1009
1246
  * Response from {@link AtlaSentClient.checkPermitValid}.
1010
- *
1011
- * Lightweight validity snapshot returned by
1012
- * `GET /v1/permits/{permitId}/valid`. Designed for guard heartbeat
1013
- * polling — returns only the fields needed to determine whether to
1014
- * abort a running permit mid-execution (via {@link PermitRevoked}).
1015
1247
  */
1016
1248
  interface PermitValidResponse {
1017
- /** True iff the permit is currently valid (active). */
1018
1249
  valid: boolean;
1019
- /**
1020
- * Current lifecycle status of the permit.
1021
- * - `"active"` — permit is valid and in-flight.
1022
- * - `"expired"` — TTL elapsed before revocation or consumption.
1023
- * - `"revoked"` — administratively revoked (see `revocation_id`).
1024
- * - `"consumed"` — single-use permit already consumed.
1025
- */
1026
1250
  status: "active" | "expired" | "revoked" | "consumed";
1027
- /** ISO-8601 timestamp when the permit was revoked. Populated only when `status === "revoked"`. */
1028
1251
  revoked_at?: string;
1029
- /** Opaque identifier of the revocation record. Populated only when `status === "revoked"`. */
1030
1252
  revocation_id?: string;
1031
1253
  }
1032
1254
  /** Input for {@link AtlaSentClient.revokePermitById}. */
1033
1255
  interface RevokePermitByIdInput {
1034
- /** Operator-supplied free-text reason. Recorded on the permit row,
1035
- * written to the audit trail, and surfaced (truncated) on later
1036
- * verify responses. Optional but strongly encouraged. */
1037
1256
  reason?: string;
1038
1257
  }
1039
1258
  /**
1040
1259
  * Response from {@link AtlaSentClient.revokePermitById}.
1041
- *
1042
- * Returns the updated {@link PermitRecord} with `status === 'revoked'`
1043
- * and the populated `revoked_at` / `revoked_by` / `revoke_reason`
1044
- * fields.
1045
1260
  */
1046
1261
  interface RevokePermitByIdResponse {
1047
1262
  permit: PermitRecord;
@@ -1049,23 +1264,12 @@ interface RevokePermitByIdResponse {
1049
1264
  }
1050
1265
  /**
1051
1266
  * Response from {@link AtlaSentClient.verifyPermitById}.
1052
- *
1053
- * Returns the canonical verification envelope (`valid`,
1054
- * `verification_type`, `reason`, `verified_at`, `evidence`) plus the
1055
- * legacy {@link PermitRecord} fields preserved at the top level for
1056
- * backward compatibility. The envelope shape matches the unified
1057
- * verify response in atlasent-api PR #352.
1058
1267
  */
1059
1268
  interface VerifyPermitByIdResponse {
1060
- /** `true` iff the permit verified — i.e. unconsumed, unexpired, and signature OK. */
1061
1269
  valid: boolean;
1062
- /** Always `'permit'` on this surface. */
1063
1270
  verification_type: "permit";
1064
- /** Operator-readable explanation when `valid` is `false`; `null` on success. */
1065
1271
  reason: string | null;
1066
- /** Server clock at the moment verification ran. */
1067
1272
  verified_at: string;
1068
- /** Type-specific evidence — same fields as the openapi PermitVerifyEvidence schema. */
1069
1273
  evidence: {
1070
1274
  permit_id: string;
1071
1275
  status: PermitStatus;
@@ -1075,196 +1279,608 @@ interface VerifyPermitByIdResponse {
1075
1279
  payload_hash?: string | null;
1076
1280
  decision_id?: string | null;
1077
1281
  };
1078
- /** Legacy: full permit row preserved at the top level. */
1079
1282
  permit: PermitRecord;
1080
1283
  rateLimit: RateLimitState | null;
1081
1284
  }
1082
1285
  /** Input for {@link AtlaSentClient.revokePermit}. */
1083
1286
  interface RevokePermitRequest {
1084
- /** The permit ID returned by a prior evaluate() call. */
1085
1287
  permitId: string;
1086
- /** Optional human-readable reason stored in the audit log. */
1087
1288
  reason?: string;
1088
1289
  }
1089
1290
  /**
1090
1291
  * Result of {@link AtlaSentClient.revokePermit}.
1091
1292
  *
1092
1293
  * @deprecated Use {@link RevokePermitByIdResponse} via
1093
- * {@link AtlaSentClient.revokePermitById} — the canonical REST surface
1094
- * (`POST /v1/permits/{id}/revoke`) returns the full updated
1095
- * {@link PermitRecord} with `revoked_at`/`revoked_by`/`revoke_reason`
1096
- * populated, instead of the legacy `{revoked, permitId}` envelope.
1294
+ * {@link AtlaSentClient.revokePermitById}.
1097
1295
  * Will be removed in `@atlasent/sdk@3`.
1098
1296
  */
1099
1297
  interface RevokePermitResponse {
1100
- /** `true` when the permit was found and successfully revoked. */
1101
1298
  revoked: boolean;
1102
- /** Echo of the revoked permit's ID. */
1103
1299
  permitId: string;
1104
- /** ISO-8601 timestamp of when the revocation was recorded. `undefined` when not returned by the server. */
1105
1300
  revokedAt?: string | undefined;
1106
- /** Audit hash for the revocation event. `undefined` when not returned by the server. */
1107
1301
  auditHash?: string | undefined;
1108
- /** Per-key rate-limit state. `null` when the server didn't emit headers. */
1109
1302
  rateLimit: RateLimitState | null;
1110
1303
  }
1111
- /**
1112
- * One stage of a single policy's constraint evaluation.
1113
- *
1114
- * Mirrors `ConstraintTraceStage` in
1115
- * `atlasent-api/packages/types/src/index.ts`. Emitted by the rule
1116
- * engine when the request URL carries `?include=constraint_trace`.
1117
- *
1118
- * Forward-compat: extra engine-side keys are tolerated; readers
1119
- * should not assume this is a closed shape.
1120
- */
1121
1304
  interface ConstraintTraceStage {
1122
- /** Engine stage name (e.g. `"role_check"`, `"context"`). */
1123
1305
  readonly stage: string;
1124
- /** Optional rule identifier; absent for wrapper stages. */
1125
1306
  readonly rule?: string;
1126
- /** True iff this stage's predicate fired. */
1127
1307
  readonly matched: boolean;
1128
- /** Optional human-readable note from the engine. */
1129
1308
  readonly detail?: string;
1130
- /** Zero-based position within the policy's `stages` array. */
1131
1309
  readonly order: number;
1132
- /** Forward-compat: tolerate unknown engine-side keys without crashing. */
1133
1310
  readonly [key: string]: unknown;
1134
1311
  }
1135
- /**
1136
- * Per-policy block of a constraint trace.
1137
- *
1138
- * Mirrors `ConstraintTracePolicy` in
1139
- * `atlasent-api/packages/types/src/index.ts`. The handler iterates
1140
- * active policies in order until first non-allow; the policy that
1141
- * produced the outer decision has `decision !== "allow"`.
1142
- */
1143
1312
  interface ConstraintTracePolicy {
1144
- /** Stable identifier of the evaluated policy. */
1145
1313
  readonly policy_id: string;
1146
- /** Policy-level decision (`"allow"|"deny"|"hold"|"escalate"`). */
1147
1314
  readonly decision: string;
1148
- /** Engine-side fingerprint of the bundle row. */
1149
1315
  readonly fingerprint: string;
1150
- /**
1151
- * Optional engine-computed risk score from a `risk` rule clause.
1152
- * Distinct from the heuristic risk score on the outer envelope.
1153
- */
1154
1316
  readonly risk_score?: number;
1155
- /** Ordered stages produced while evaluating this policy. */
1156
1317
  readonly stages: ReadonlyArray<ConstraintTraceStage>;
1157
- /** Forward-compat: tolerate unknown engine-side keys. */
1158
1318
  readonly [key: string]: unknown;
1159
1319
  }
1160
- /**
1161
- * Top-level constraint trace returned by
1162
- * `/v1-evaluate?include=constraint_trace`.
1163
- *
1164
- * Mirrors `ConstraintTraceResponse` in
1165
- * `atlasent-api/packages/types/src/index.ts`. Present iff the
1166
- * caller requested the trace; the SDK's preflight helper always
1167
- * requests it.
1168
- */
1169
1320
  interface ConstraintTrace {
1170
- /** Per-policy blocks in evaluation order. */
1171
1321
  readonly rules_evaluated: ReadonlyArray<ConstraintTracePolicy>;
1172
- /**
1173
- * Policy id whose evaluation produced the outer decision. Equals
1174
- * the outer `matched_policy_id` on non-allow paths; `undefined` on
1175
- * a clean allow (all policies passed).
1176
- */
1177
1322
  readonly matching_policy_id?: string;
1178
- /** Forward-compat: tolerate unknown engine-side keys. */
1179
1323
  readonly [key: string]: unknown;
1180
1324
  }
1181
- /**
1182
- * Result of {@link AtlaSentClient.evaluatePreflight}.
1183
- *
1184
- * Wraps the regular {@link EvaluateResponse} plus the
1185
- * {@link ConstraintTrace} returned when the request URL carries
1186
- * `?include=constraint_trace`. The whole point of preflight is to
1187
- * surface which stages / policies WOULD fire BEFORE pushing the
1188
- * request onto an approval queue, so workflows can reject trivially
1189
- * defective requests at submission time and only forward viable
1190
- * requests to a human reviewer.
1191
- *
1192
- * `constraintTrace` is `null` on responses from older atlasent-api
1193
- * deployments that do not echo the trace — forward-compatible
1194
- * degradation.
1195
- */
1196
1325
  interface EvaluatePreflightResponse {
1197
- /** The regular evaluate response (decision, permitId, ...). */
1198
1326
  readonly evaluation: EvaluateResponse;
1199
- /**
1200
- * The constraint trace, or `null` when the server omitted it
1201
- * (older atlasent-api version).
1202
- */
1203
1327
  readonly constraintTrace: ConstraintTrace | null;
1204
1328
  }
1205
- /**
1206
- * Options for {@link AtlaSentClient.protectStream}.
1207
- *
1208
- * All fields are optional; defaults are used when omitted.
1209
- */
1210
1329
  interface StreamOptions {
1211
- /**
1212
- * Optional abort signal to cancel the stream from the caller side.
1213
- */
1214
1330
  signal?: AbortSignal;
1215
- /**
1216
- * Per-event timeout in milliseconds: if no SSE event arrives within
1217
- * this window the stream throws {@link StreamTimeoutError}.
1218
- * Defaults to 30 000 ms (30 s). Pass `0` to disable.
1219
- */
1220
1331
  timeoutMs?: number;
1221
- /**
1222
- * Maximum reconnection attempts on network drop before the stream
1223
- * gives up and throws. Defaults to 3.
1224
- */
1225
1332
  maxRetries?: number;
1226
1333
  }
1227
- /** A policy decision emitted mid-stream. */
1228
1334
  interface StreamDecisionEvent {
1229
1335
  type: "decision";
1230
- /**
1231
- * Policy decision — canonical 4-value lowercase vocabulary:
1232
- * `"allow"`, `"deny"`, `"hold"`, or `"escalate"`.
1233
- *
1234
- * Previously emitted `"ALLOW"` / `"DENY"` (uppercase, 2-value);
1235
- * now unified with `decision_canonical`.
1236
- *
1237
- * @deprecated Read `decision_canonical` instead for forward-compatible
1238
- * branching. Both fields now carry the same value. Will be
1239
- * removed/changed in `@atlasent/sdk@3`.
1240
- */
1241
1336
  decision: Decision;
1242
- /**
1243
- * Canonical 4-value decision, byte-identical to the wire.
1244
- * One of `"allow"`, `"deny"`, `"hold"`, `"escalate"`.
1245
- */
1246
1337
  decision_canonical: DecisionCanonical;
1247
- /** Opaque permit identifier for a final allow. Pass to verifyPermit. */
1248
1338
  permitId: string;
1249
- /** Human-readable explanation from the policy engine. */
1250
1339
  reason: string;
1251
- /** Audit hash bound to this decision. */
1252
1340
  auditHash: string;
1253
- /** ISO-8601 timestamp of the decision. */
1254
1341
  timestamp: string;
1255
- /** When true the stream will emit done and close after this event. */
1256
1342
  isFinal: boolean;
1257
1343
  }
1258
- /** An intermediate progress hint emitted before the final decision. */
1259
1344
  interface StreamProgressEvent {
1260
1345
  type: "progress";
1261
- /** Human-readable stage name (e.g. "policy_loading", "context_enrichment"). */
1262
1346
  stage: string;
1263
- /** Additional server-defined fields — forward-compat, do not rely on shape. */
1264
1347
  [key: string]: unknown;
1265
1348
  }
1266
- /** Union of all events yielded by {@link AtlaSentClient.protectStream}. */
1267
1349
  type StreamEvent = StreamDecisionEvent | StreamProgressEvent;
1350
+ interface BatchEvalItem {
1351
+ agent: string;
1352
+ action: string;
1353
+ context?: Record<string, unknown>;
1354
+ }
1355
+ interface EvaluateBatchResultItem {
1356
+ index: number;
1357
+ decision?: DecisionCanonical;
1358
+ decisionId?: string;
1359
+ permitToken?: string | null;
1360
+ reason?: string;
1361
+ auditHash?: string;
1362
+ timestamp?: string;
1363
+ error?: string;
1364
+ message?: string;
1365
+ }
1366
+ interface BatchEvalResponse {
1367
+ batchId: string;
1368
+ items: EvaluateBatchResultItem[];
1369
+ partial: boolean;
1370
+ replayed?: boolean;
1371
+ rateLimit: RateLimitState | null;
1372
+ }
1373
+ interface SubscribeDecisionsOptions {
1374
+ types?: string[];
1375
+ actorId?: string;
1376
+ lastEventId?: string;
1377
+ maxSeconds?: number;
1378
+ signal?: AbortSignal;
1379
+ }
1380
+ interface DecisionStreamEvent {
1381
+ id?: string;
1382
+ type: string;
1383
+ decision?: DecisionCanonical;
1384
+ actorId?: string;
1385
+ resourceType?: string;
1386
+ resourceId?: string;
1387
+ payload?: Record<string, unknown>;
1388
+ hash?: string;
1389
+ previousHash?: string;
1390
+ occurredAt?: string;
1391
+ }
1392
+
1393
+ /**
1394
+ * Override types — wire shapes for `/v1/overrides`.
1395
+ *
1396
+ * Overrides allow an authorized actor to bypass a deny decision for a
1397
+ * specific evaluation. They must be approved before they take effect
1398
+ * and can be revoked at any time.
1399
+ *
1400
+ * Mirrors `api/src/schemas/overrides.ts` in atlasent-control-plane.
1401
+ */
1402
+ /**
1403
+ * Lifecycle status of an override request.
1404
+ *
1405
+ * - `pending` — created, waiting for approval
1406
+ * - `approved` — approved and active; the evaluation's deny is lifted
1407
+ * - `revoked` — manually revoked
1408
+ * - `expired` — TTL elapsed before revocation
1409
+ */
1410
+ type OverrideStatus = "pending" | "approved" | "revoked" | "expired";
1411
+ /**
1412
+ * The event types that can appear on an override's event log.
1413
+ */
1414
+ type OverrideEventType = "created" | "approved" | "revoked";
1415
+ /**
1416
+ * Canonical Override domain object returned by the API.
1417
+ *
1418
+ * All timestamps are ISO-8601 UTC strings. Nullable fields are `null`
1419
+ * rather than omitted so wire shapes are stable.
1420
+ */
1421
+ interface OverrideV1 {
1422
+ id: string;
1423
+ orgId: string;
1424
+ /** The evaluation ID this override applies to. */
1425
+ evaluationId: string;
1426
+ /** Human-readable justification provided at creation time. */
1427
+ reason: string;
1428
+ status: OverrideStatus;
1429
+ /** Actor who requested the override. */
1430
+ requestedBy: string;
1431
+ /** Actor who approved the override, or `null` if not yet approved. */
1432
+ approvedBy: string | null;
1433
+ /** Actor who revoked the override, or `null` if not revoked. */
1434
+ revokedBy: string | null;
1435
+ /** ISO-8601 creation timestamp. */
1436
+ createdAt: string;
1437
+ /** ISO-8601 approval timestamp, or `null`. */
1438
+ approvedAt: string | null;
1439
+ /** ISO-8601 revocation timestamp, or `null`. */
1440
+ revokedAt: string | null;
1441
+ /** ISO-8601 expiry timestamp, or `null` if no TTL was set. */
1442
+ expiresAt: string | null;
1443
+ /** Arbitrary key/value metadata attached at creation. `null` when none. */
1444
+ metadata: Record<string, unknown> | null;
1445
+ }
1446
+ /**
1447
+ * Paginated list of overrides.
1448
+ */
1449
+ interface OverrideListResponse {
1450
+ items: OverrideV1[];
1451
+ /** Opaque cursor for the next page. `null` when there are no more results. */
1452
+ nextCursor: string | null;
1453
+ }
1454
+ /**
1455
+ * Input for `POST /v1/overrides` — request a new override.
1456
+ */
1457
+ interface CreateOverrideRequest {
1458
+ /** Human-readable justification. Required; max 2000 characters. */
1459
+ reason: string;
1460
+ /** The evaluation ID to override. */
1461
+ evaluationId: string;
1462
+ /** Lifetime in seconds. Defaults to 3600. Max 604800 (7 days). */
1463
+ ttlSeconds?: number;
1464
+ /** Arbitrary metadata to attach to the override record. */
1465
+ metadata?: Record<string, unknown>;
1466
+ }
1467
+ /**
1468
+ * Audit event appended to an override's event log on every state mutation.
1469
+ */
1470
+ interface OverrideEvent {
1471
+ id: string;
1472
+ overrideId: string;
1473
+ orgId: string;
1474
+ /** Actor who caused this event. */
1475
+ actorId: string;
1476
+ type: OverrideEventType;
1477
+ /** ISO-8601 timestamp. */
1478
+ at: string;
1479
+ /** Event-specific payload. `null` when none. */
1480
+ payload: Record<string, unknown> | null;
1481
+ }
1482
+ /**
1483
+ * Response for `GET /v1/overrides/:id/events`.
1484
+ */
1485
+ interface OverrideEventsResponse {
1486
+ items: OverrideEvent[];
1487
+ }
1488
+
1489
+ /**
1490
+ * Compliance evidence types — wire shapes for `v1-compliance-evidence`.
1491
+ *
1492
+ * Supports on-demand SOC 2 Type II control evidence collection. The
1493
+ * same run shape is used for ISO 27001, GDPR, and HIPAA; control IDs
1494
+ * differ per framework.
1495
+ */
1496
+ type ComplianceFramework = "soc2" | "iso27001" | "gdpr" | "hipaa";
1497
+ type EvidenceControlStatus = "pass" | "gap" | "finding";
1498
+ type ComplianceRunStatus = "pending" | "running" | "completed" | "failed";
1499
+ /**
1500
+ * A single evaluated control within an evidence run.
1501
+ * `evidence` is a free-form object whose keys are framework-specific
1502
+ * metric names (e.g. `mfa_enforced_policies`, `audit_events_last_30d`).
1503
+ */
1504
+ interface EvidenceControl {
1505
+ control_id: string;
1506
+ title: string;
1507
+ status: EvidenceControlStatus;
1508
+ evidence: Record<string, unknown>;
1509
+ }
1510
+ interface ComplianceEvidenceSummary {
1511
+ total: number;
1512
+ pass: number;
1513
+ gap: number;
1514
+ finding: number;
1515
+ }
1516
+ interface ComplianceEvidenceRun {
1517
+ id: string;
1518
+ org_id: string;
1519
+ framework: ComplianceFramework;
1520
+ period_start: string;
1521
+ period_end: string;
1522
+ status: ComplianceRunStatus;
1523
+ controls: EvidenceControl[];
1524
+ summary: ComplianceEvidenceSummary | null;
1525
+ applied_by: string | null;
1526
+ created_at: string;
1527
+ }
1528
+ interface TriggerEvidenceRunRequest {
1529
+ framework: ComplianceFramework;
1530
+ /** ISO 8601 date string; defaults to 30 days ago on the server. */
1531
+ period_start?: string;
1532
+ /** ISO 8601 date string; defaults to now on the server. */
1533
+ period_end?: string;
1534
+ }
1535
+ interface TriggerEvidenceRunResponse {
1536
+ run: ComplianceEvidenceRun;
1537
+ }
1538
+ interface ListEvidenceRunsResponse {
1539
+ runs: ComplianceEvidenceRun[];
1540
+ }
1541
+ /**
1542
+ * SOC 2 control IDs evaluated by `v1-compliance-evidence`.
1543
+ *
1544
+ * | ID | Area |
1545
+ * |--------|------|
1546
+ * | CC6.1 | MFA enforcement |
1547
+ * | CC6.3 | Periodic access reviews |
1548
+ * | CC7.2 | Audit trail completeness |
1549
+ * | CC8.1 | Change management / HITL |
1550
+ * | CC3.2 | Policy violations |
1551
+ */
1552
+ type SOC2ControlId = "CC6.1" | "CC6.3" | "CC7.2" | "CC8.1" | "CC3.2";
1553
+ /**
1554
+ * Returns `true` when every control in the run has `pass` or `gap`
1555
+ * status (no `finding`). A `gap` means a control is partially met;
1556
+ * a `finding` is a blocking deficiency that requires remediation.
1557
+ */
1558
+ declare function evidenceRunPasses(run: ComplianceEvidenceRun): boolean;
1559
+ /**
1560
+ * Returns controls that do not have `pass` status, sorted so
1561
+ * `finding` controls appear before `gap` controls.
1562
+ */
1563
+ declare function nonPassingControls(run: ComplianceEvidenceRun): EvidenceControl[];
1564
+
1565
+ /**
1566
+ * Evidence Engine — per-decision proof artifacts, "why" traces, and
1567
+ * compliance-ready bundles.
1568
+ *
1569
+ * Turn every AtlaSent decision into tamper-evident proof that buyers
1570
+ * can hand to auditors, compliance teams, and regulators.
1571
+ *
1572
+ * Primary entry points:
1573
+ *
1574
+ * 1. `buildWhyTrace(decision, reasons, constraintTrace)` — converts
1575
+ * the ConstraintTrace from `?include=constraint_trace` into a
1576
+ * structured human/machine-readable "why allowed / why denied" trace.
1577
+ *
1578
+ * 2. `buildDecisionReceiptPayload(args)` — assembles the canonical
1579
+ * signable payload for a per-decision receipt.
1580
+ *
1581
+ * 3. `signDecisionReceiptHmac(payload, secret)` — HMAC-SHA256 sign.
1582
+ *
1583
+ * 4. `verifyDecisionReceiptHmac(receipt, secret)` — offline verify.
1584
+ *
1585
+ * 5. `computeBundleHash(bundle)` — SHA-256 of an ActionEvidenceBundle.
1586
+ *
1587
+ * 6. `soc2ControlCoverageForDecision(opts)` — map a decision to SOC 2
1588
+ * control coverage.
1589
+ *
1590
+ * The {@link DecisionReceipt} is the category-defining artifact: a
1591
+ * self-contained, signed, human-readable proof that a specific action
1592
+ * was (or was not) authorized at a specific moment. Every enforcement
1593
+ * adapter produces one; every compliance bundle includes one.
1594
+ */
1595
+
1596
+ /**
1597
+ * One evaluated stage within a policy, in the order the engine ran it.
1598
+ */
1599
+ interface WhyStage {
1600
+ /** Engine stage name (e.g. `"role_check"`, `"context"`). */
1601
+ stage: string;
1602
+ /** Rule identifier, if the stage is rule-bound. */
1603
+ rule?: string;
1604
+ /** Whether this stage's predicate fired / matched. */
1605
+ matched: boolean;
1606
+ /** Non-obvious detail from the engine. */
1607
+ detail?: string;
1608
+ /**
1609
+ * Impact classification:
1610
+ * - `"terminal"` — this stage caused the outer decision.
1611
+ * - `"contributing"` — matched but was not the decisive stage.
1612
+ * - `"passing"` — did not match; execution continued.
1613
+ */
1614
+ impact: "terminal" | "contributing" | "passing";
1615
+ }
1616
+ /** Per-policy evaluation block within a WhyTrace. */
1617
+ interface WhyPolicyEvaluation {
1618
+ policy_id: string;
1619
+ /** Policy-level decision. */
1620
+ decision: string;
1621
+ /** Engine-side fingerprint of the policy bundle row. */
1622
+ fingerprint: string;
1623
+ /** Optional risk score from a `risk` rule clause. */
1624
+ risk_score?: number;
1625
+ /** Stages evaluated for this policy, in order. */
1626
+ stages: WhyStage[];
1627
+ /** `true` iff this policy's decision drove the outer envelope decision. */
1628
+ was_decisive: boolean;
1629
+ }
1630
+ /**
1631
+ * Structured "why allowed / why denied" trace.
1632
+ *
1633
+ * Produced by `buildWhyTrace()` from the `ConstraintTrace` returned
1634
+ * by `/v1-evaluate?include=constraint_trace`. Suitable for:
1635
+ *
1636
+ * - UI display ("Why was this denied?")
1637
+ * - Email / Slack notifications
1638
+ * - Compliance-bundle human-readable section
1639
+ * - Machine-readable policy audit by external verifiers
1640
+ *
1641
+ * `summary` is a one-sentence plain-English explanation.
1642
+ */
1643
+ interface WhyTrace {
1644
+ decision: DecisionCanonical;
1645
+ /** One-sentence human-readable explanation. */
1646
+ summary: string;
1647
+ /** Policy whose decision drove the outer result. Absent on clean allow. */
1648
+ matched_policy_id?: string;
1649
+ /** Per-policy evaluation blocks in evaluation order. */
1650
+ policy_evaluations: WhyPolicyEvaluation[];
1651
+ /**
1652
+ * The single stage that caused the terminal outcome, extracted for
1653
+ * quick access. `undefined` on a clean allow (no blocking stage).
1654
+ */
1655
+ terminal_stage?: WhyStage;
1656
+ /** Total stages evaluated across all policies. */
1657
+ total_stages_evaluated: number;
1658
+ }
1659
+ /** Signing algorithm tag on a {@link DecisionReceipt}. */
1660
+ type DecisionReceiptAlgorithm = "hmac-sha256" | "ed25519" | "none";
1661
+ /**
1662
+ * The canonical signed payload of a {@link DecisionReceipt}.
1663
+ *
1664
+ * Field order is load-bearing: HMAC and chain verifiers stringify
1665
+ * this object and must reproduce byte-identical output. Never reorder
1666
+ * the fields; add new optional fields at the end only.
1667
+ */
1668
+ interface DecisionReceiptPayload {
1669
+ receipt_id: string;
1670
+ evaluation_id: string;
1671
+ org_id: string;
1672
+ decision: DecisionCanonical;
1673
+ action: string;
1674
+ actor: string;
1675
+ resource_type: string | null;
1676
+ resource_id: string | null;
1677
+ reasons: string[];
1678
+ /** One-sentence human-readable summary from the WhyTrace. */
1679
+ why_summary: string;
1680
+ /** Permit ID when the decision was `"allow"`. */
1681
+ permit_id: string | null;
1682
+ /** Permit verification hash when the decision was `"allow"`. */
1683
+ permit_hash: string | null;
1684
+ /** Hash-chained audit-trail entry from the evaluate response. */
1685
+ audit_hash: string;
1686
+ /** SHA-256 hex of canonical JSON of the evaluate context. */
1687
+ context_hash: string;
1688
+ /** ISO-8601 when this receipt was issued. */
1689
+ issued_at: string;
1690
+ /** ISO-8601 TTL, or `null` for non-expiring receipts. */
1691
+ expires_at: string | null;
1692
+ }
1693
+ /**
1694
+ * A signed, tamper-evident record of a single AtlaSent authorization
1695
+ * decision. Self-contained: contains everything an auditor needs to
1696
+ * verify the decision without querying the API.
1697
+ *
1698
+ * **Signature semantics (HMAC-SHA256):**
1699
+ *
1700
+ * `HMAC-SHA256(secret,
1701
+ * receipt_id + "\\n" + issued_at + "\\n" + JSON.stringify(payload))`
1702
+ *
1703
+ * When `algorithm === "ed25519"`, `signature` is hex-encoded Ed25519
1704
+ * over the same input string encoded as UTF-8.
1705
+ *
1706
+ * Offline verification: `verifyDecisionReceiptHmac(receipt, secret)`.
1707
+ *
1708
+ * Callers MUST reject receipts where `algorithm === "none"` in any
1709
+ * context requiring tamper-evidence.
1710
+ */
1711
+ interface DecisionReceipt {
1712
+ receipt_id: string;
1713
+ evaluation_id: string;
1714
+ org_id: string;
1715
+ decision: DecisionCanonical;
1716
+ action: string;
1717
+ actor: string;
1718
+ resource_type: string | null;
1719
+ resource_id: string | null;
1720
+ reasons: string[];
1721
+ /**
1722
+ * Full structured "why" trace. `null` when the evaluation was
1723
+ * performed without `?include=constraint_trace`.
1724
+ */
1725
+ why_trace: WhyTrace | null;
1726
+ permit_id: string | null;
1727
+ permit_hash: string | null;
1728
+ audit_hash: string;
1729
+ /** SHA-256 hex of canonical JSON of the evaluate context. */
1730
+ context_hash: string;
1731
+ issued_at: string;
1732
+ expires_at: string | null;
1733
+ algorithm: DecisionReceiptAlgorithm;
1734
+ /**
1735
+ * Hex (HMAC-SHA256 or Ed25519) signature, or `null` when
1736
+ * `algorithm === "none"`.
1737
+ */
1738
+ signature: string | null;
1739
+ /** Registry key ID that signed, when `algorithm !== "none"`. */
1740
+ signing_key_id: string | null;
1741
+ /**
1742
+ * Full payload that was signed. Pass to `verifyDecisionReceiptHmac`
1743
+ * or reconstruct independently for external verification.
1744
+ */
1745
+ payload: DecisionReceiptPayload;
1746
+ }
1747
+ /** Coverage summary for one compliance control within a bundle. */
1748
+ interface ComplianceControlCoverage {
1749
+ framework: ComplianceFramework;
1750
+ control_id: string;
1751
+ title: string;
1752
+ /** `true` when this bundle provides sufficient evidence for the control. */
1753
+ covered: boolean;
1754
+ /** Evidence kinds present in the bundle that map to this control. */
1755
+ evidence_kinds: string[];
1756
+ }
1757
+ /**
1758
+ * A compliance-ready evidence bundle for a single protected action.
1759
+ *
1760
+ * Contains everything an auditor needs to verify the authorization
1761
+ * decision without querying the API:
1762
+ *
1763
+ * - The signed {@link DecisionReceipt}
1764
+ * - The "why" trace (why allowed / why denied)
1765
+ * - Audit events from the decision window
1766
+ * - Permit chain (when the decision was `"allow"`)
1767
+ * - Active overrides that influenced the decision
1768
+ * - Per-control SOC 2 / compliance coverage map
1769
+ *
1770
+ * `bundle_hash` is SHA-256 of `JSON.stringify(bundle)` with
1771
+ * `bundle_hash` omitted, computed by `computeBundleHash()`. Use it
1772
+ * to verify the bundle was not modified after assembly.
1773
+ */
1774
+ interface ActionEvidenceBundle {
1775
+ /** Wire format version. */
1776
+ v: 1;
1777
+ bundle_id: string;
1778
+ evaluation_id: string;
1779
+ org_id: string;
1780
+ action: string;
1781
+ actor: string;
1782
+ decision: DecisionCanonical;
1783
+ receipt: DecisionReceipt;
1784
+ why_trace: WhyTrace | null;
1785
+ /** Audit events from the evaluation window related to this action. */
1786
+ audit_events: AuditEvent[];
1787
+ /** Permit chain when the decision was `"allow"`. */
1788
+ permit_chain: PermitRecord[];
1789
+ /** Active overrides that influenced the decision. */
1790
+ overrides: OverrideV1[];
1791
+ compliance_controls: ComplianceControlCoverage[];
1792
+ generated_at: string;
1793
+ /** SHA-256 hex of canonical JSON of this bundle (sans this field). */
1794
+ bundle_hash: string;
1795
+ }
1796
+ /**
1797
+ * Convert a raw `ConstraintTrace` (from `?include=constraint_trace`)
1798
+ * into a structured {@link WhyTrace} with a human-readable summary.
1799
+ *
1800
+ * Safe to call with `trace === null` — returns a minimal trace with
1801
+ * the decision and a generic summary derived from `reasons`.
1802
+ *
1803
+ * ```ts
1804
+ * import { buildWhyTrace } from "@atlasent/sdk";
1805
+ *
1806
+ * const preflight = await client.evaluatePreflight({ agent, action, context });
1807
+ * const why = buildWhyTrace(
1808
+ * preflight.evaluation.decision_canonical,
1809
+ * preflight.evaluation.reasons,
1810
+ * preflight.constraintTrace,
1811
+ * );
1812
+ * console.log(why.summary);
1813
+ * // "Denied at stage 'role_check': actor lacks deploy role"
1814
+ * ```
1815
+ */
1816
+ declare function buildWhyTrace(decision: DecisionCanonical, reasons: readonly string[], trace: ConstraintTrace | null): WhyTrace;
1817
+ /**
1818
+ * Compute SHA-256 hex of the recursively key-sorted canonical JSON of
1819
+ * `context`. Used as `context_hash` on a `DecisionReceipt` so the
1820
+ * original evaluate context can be independently verified offline.
1821
+ */
1822
+ declare function computeContextHash(context: Record<string, unknown>): Promise<string>;
1823
+ /**
1824
+ * Assemble a {@link DecisionReceiptPayload} — the canonical object
1825
+ * that is serialised and signed. Field insertion order is fixed;
1826
+ * do NOT reorder the fields below.
1827
+ */
1828
+ declare function buildDecisionReceiptPayload(args: {
1829
+ receipt_id: string;
1830
+ evaluation_id: string;
1831
+ org_id: string;
1832
+ decision: DecisionCanonical;
1833
+ action: string;
1834
+ actor: string;
1835
+ resource_type?: string | null;
1836
+ resource_id?: string | null;
1837
+ reasons: readonly string[];
1838
+ why_summary: string;
1839
+ permit_id?: string | null;
1840
+ permit_hash?: string | null;
1841
+ audit_hash: string;
1842
+ context_hash: string;
1843
+ issued_at: string;
1844
+ expires_at?: string | null;
1845
+ }): DecisionReceiptPayload;
1846
+ /**
1847
+ * HMAC-SHA256 sign a {@link DecisionReceiptPayload}. Returns the
1848
+ * hex-encoded MAC. Store as `receipt.signature` with
1849
+ * `algorithm: "hmac-sha256"`.
1850
+ *
1851
+ * Uses `crypto.subtle` (browser / Node 20+ / Cloudflare) or falls
1852
+ * back to `node:crypto` on older Node runtimes.
1853
+ */
1854
+ declare function signDecisionReceiptHmac(payload: DecisionReceiptPayload, secret: string): Promise<string>;
1855
+ /**
1856
+ * Verify an HMAC-SHA256-signed {@link DecisionReceipt} offline.
1857
+ * Returns `false` (does not throw) on any verification failure.
1858
+ *
1859
+ * Callers MUST reject receipts where `receipt.algorithm !== "hmac-sha256"`.
1860
+ */
1861
+ declare function verifyDecisionReceiptHmac(receipt: DecisionReceipt, secret: string): Promise<boolean>;
1862
+ /**
1863
+ * Compute SHA-256 hex of `JSON.stringify(bundle)` with `bundle_hash`
1864
+ * omitted. Store as `bundle.bundle_hash` after assembly.
1865
+ *
1866
+ * An external verifier can reproduce this value independently to
1867
+ * confirm the bundle was not modified after export.
1868
+ */
1869
+ declare function computeBundleHash(bundle: Omit<ActionEvidenceBundle, "bundle_hash">): Promise<string>;
1870
+ /**
1871
+ * Return the SOC 2 controls covered by a single authorization decision,
1872
+ * given what the bundle contains. Suitable for populating
1873
+ * `ActionEvidenceBundle.compliance_controls`.
1874
+ *
1875
+ * For ISO 27001 / GDPR / HIPAA coverage use the `@atlasent/evidence-bundle`
1876
+ * package's `buildEvidenceBundle()` which handles multi-framework mapping.
1877
+ */
1878
+ declare function soc2ControlCoverageForDecision(opts: {
1879
+ decision: DecisionCanonical;
1880
+ hasPermitChain: boolean;
1881
+ hasAuditEvents: boolean;
1882
+ hasOverrides: boolean;
1883
+ }): ComplianceControlCoverage[];
1268
1884
 
1269
1885
  /** Input to {@link protect}. Same shape as `EvaluateRequest`. */
1270
1886
  interface ProtectRequest {
@@ -1287,6 +1903,8 @@ interface Permit {
1287
1903
  reason: string;
1288
1904
  /** ISO 8601 timestamp of the verification. */
1289
1905
  timestamp: string;
1906
+ /** ISO-8601 expiration timestamp of the permit. null on pre-rollout servers. */
1907
+ permitExpiresAt: string | null;
1290
1908
  }
1291
1909
  /** Configuration for the process-wide singleton used by {@link protect}. */
1292
1910
  interface ConfigureOptions {
@@ -1325,5 +1943,67 @@ declare function deployGate(request?: DeployGateRequest): Promise<DeployGateResp
1325
1943
  * or server error. Same fail-closed contract: do not proceed.
1326
1944
  */
1327
1945
  declare function protect(request: ProtectRequest): Promise<Permit>;
1946
+ /**
1947
+ * A verified {@link Permit} with an embedded signed {@link DecisionReceipt}.
1948
+ *
1949
+ * Returned by {@link protectWithEvidence}. Store `receipt` alongside
1950
+ * your action record (deploy logs, payment records, close workflows)
1951
+ * to give auditors a self-contained proof of authorization.
1952
+ */
1953
+ interface PermitWithEvidence extends Permit {
1954
+ /** Signed per-decision receipt. `algorithm: "none"` when no signing secret was supplied. */
1955
+ receipt: DecisionReceipt;
1956
+ }
1957
+ /** Options for {@link protectWithEvidence}. */
1958
+ interface ProtectWithEvidenceOptions {
1959
+ /**
1960
+ * HMAC-SHA256 signing secret. When provided, the receipt is signed
1961
+ * and can be verified offline with `verifyDecisionReceiptHmac`.
1962
+ * Recommend `process.env.ATLASENT_RECEIPT_SIGNING_SECRET`.
1963
+ */
1964
+ signingSecret?: string;
1965
+ /**
1966
+ * Registry key ID recorded on the receipt, paired with `signingSecret`.
1967
+ * Used for key rotation: store the ID alongside the receipt so
1968
+ * verifiers know which key to use.
1969
+ */
1970
+ signingKeyId?: string;
1971
+ /**
1972
+ * If you have already called `client.evaluatePreflight()` for this
1973
+ * request, pass `constraintTrace` here to populate
1974
+ * `receipt.why_trace` with the full stage-by-stage "why" trace.
1975
+ * When omitted, `why_trace` is `null` on the receipt.
1976
+ */
1977
+ constraintTrace?: ConstraintTrace | null;
1978
+ }
1979
+ /**
1980
+ * Authorize an action end-to-end and mint a signed {@link DecisionReceipt}.
1981
+ *
1982
+ * Same fail-closed contract as {@link protect} — throws
1983
+ * {@link AtlaSentDeniedError} on deny, {@link AtlaSentError} on
1984
+ * transport failure. The action MUST NOT proceed if this throws.
1985
+ *
1986
+ * On allow, returns the verified `Permit` plus a signed `DecisionReceipt`
1987
+ * that captures:
1988
+ * - The evaluation ID and decision
1989
+ * - Human-readable reasons
1990
+ * - Permit ID and hash
1991
+ * - Audit-trail hash (hash-chain link)
1992
+ * - SHA-256 of the evaluate context (tamper-evidence for the inputs)
1993
+ * - Optional "why" trace (pass `constraintTrace` from `evaluatePreflight`)
1994
+ *
1995
+ * ```ts
1996
+ * const { permit, receipt } = await protectWithEvidence(
1997
+ * { agent: "deploy-bot", action: "production.deploy", context },
1998
+ * {
1999
+ * signingSecret: process.env.ATLASENT_RECEIPT_SIGNING_SECRET,
2000
+ * signingKeyId: "key-v1",
2001
+ * },
2002
+ * );
2003
+ * // Store alongside the deployment record.
2004
+ * await db.deployments.create({ commitSha, permit, receipt });
2005
+ * ```
2006
+ */
2007
+ declare function protectWithEvidence(request: ProtectRequest, opts?: ProtectWithEvidenceOptions): Promise<PermitWithEvidence>;
1328
2008
 
1329
- export { type DeployGateDenyCode as $, AtlaSentDeniedError as A, type AtlaSentErrorInit as B, AtlaSentEscalateError as C, type DeployGateRequest as D, type EvaluateRequest as E, type AtlaSentEscalateErrorInit as F, type GetPermitResponse as G, type AuditDecision as H, type AuditEvent as I, type AuditEventsPage as J, type AuditExport as K, type ListPermitsRequest as L, type AuditExportSignatureStatus as M, type ConfigureOptions as N, type ConstraintTrace as O, type Permit as P, type ConstraintTracePolicy as Q, type RateLimitState as R, type StreamOptions as S, type ConstraintTraceStage as T, DEFAULT_RETRY_POLICY as U, type VerifyPermitRequest as V, DEPLOYMENT_PRODUCTION_ACTION as W, DEPLOY_GATE_CODES as X, type Decision as Y, type DecisionCanonical as Z, type DeployGateContext as _, AtlaSentError as a, type DeployGateEvidence as a0, type DeployOverrideClaim as a1, type DeployPermitClaim as a2, type EvaluateResponsePermit as a3, PRODUCTION_DEPLOY_ACTION as a4, type PermitOutcome as a5, type PermitRecord as a6, PermitRevoked as a7, type PermitStatus as a8, type RetryPolicy as a9, type StreamDecisionEvent as aa, StreamParseError as ab, type StreamProgressEvent as ac, StreamTimeoutError as ad, computeBackoffMs as ae, hasAttemptsLeft as af, isRetryable as ag, mergePolicy as ah, normalizePermitOutcome as ai, type ProtectRequest as b, type AtlaSentClientOptions as c, type EvaluateResponse as d, type EvaluatePreflightResponse as e, type VerifyPermitResponse as f, type DeployGateResponse as g, type RevokePermitRequest as h, type RevokePermitResponse as i, type RevokePermitByIdInput as j, type RevokePermitByIdResponse as k, type VerifyPermitByIdResponse as l, type PermitValidResponse as m, type ListPermitsResponse as n, type ApiKeySelfResponse as o, type AuditEventsQuery as p, type AuditEventsResult as q, type AuditExportRequest as r, type AuditExportResult as s, type StreamEvent as t, protect as u, deployGate as v, configure as w, type AtlaSentDecision as x, type AtlaSentDeniedErrorInit as y, type AtlaSentErrorCode as z };
2009
+ export { BundleVerificationError as $, AtlaSentDeniedError as A, type BatchEvalItem as B, type ComplianceEvidenceRun as C, type DecisionCanonical as D, type EvaluateRequest as E, protect as F, type GetPermitResponse as G, deployGate as H, configure as I, type ActionEvidenceBundle as J, type AtlaSentDecision as K, type ListPermitsRequest as L, type AtlaSentDeniedErrorInit as M, type AtlaSentErrorCode as N, type OverrideV1 as O, type Permit as P, type AtlaSentErrorInit as Q, type RateLimitState as R, type SubscribeDecisionsOptions as S, AtlaSentEscalateError as T, type AtlaSentEscalateErrorInit as U, type VerifyPermitRequest as V, type AuditDecision as W, type AuditEvent as X, type AuditEventsPage as Y, type AuditExport as Z, type AuditExportSignatureStatus as _, AtlaSentError as a, protectWithEvidence as a$, type BvsSnapshot as a0, type CompletionProof as a1, type ComplianceControlCoverage as a2, type ComplianceEvidenceSummary as a3, type ComplianceFramework as a4, type ComplianceRunStatus as a5, type ConfigureOptions as a6, type ConsentClassProjection as a7, type ConstraintTrace as a8, type ConstraintTracePolicy as a9, type PermitRecord as aA, PermitRevoked as aB, type PermitStatus as aC, type PermitWithEvidence as aD, type ProtectWithEvidenceOptions as aE, type RetryPolicy as aF, type SOC2ControlId as aG, type StreamDecisionEvent as aH, StreamParseError as aI, type StreamProgressEvent as aJ, StreamTimeoutError as aK, type TriggerEvidenceRunRequest as aL, type TriggerEvidenceRunResponse as aM, type WhyPolicyEvaluation as aN, type WhyStage as aO, type WhyTrace as aP, buildDecisionReceiptPayload as aQ, buildWhyTrace as aR, computeBackoffMs as aS, computeBundleHash as aT, computeContextHash as aU, evidenceRunPasses as aV, hasAttemptsLeft as aW, isRetryable as aX, mergePolicy as aY, nonPassingControls as aZ, normalizePermitOutcome as a_, type ConstraintTraceStage as aa, type CreateOverrideRequest as ab, DEFAULT_RETRY_POLICY as ac, DEPLOYMENT_PRODUCTION_ACTION as ad, DEPLOY_GATE_CODES as ae, type Decision as af, type DecisionReceiptPayload as ag, type DeployGateContext as ah, type DeployGateDenyCode as ai, type DeployGateEvidence as aj, type DeployOverrideClaim as ak, type DeployPermitClaim as al, type EvaluateBatchResultItem as am, type EvaluateResponsePermit as an, type EvaluateRiskEnvelope as ao, type EvaluateRiskEnvelopeFactor as ap, type EvidenceControl as aq, type EvidenceControlStatus as ar, type ListEvidenceRunsResponse as as, type OverrideEvent as at, type OverrideEventType as au, type OverrideEventsResponse as av, type OverrideListResponse as aw, type OverrideStatus as ax, PRODUCTION_DEPLOY_ACTION as ay, type PermitOutcome as az, type ProtectRequest as b, signDecisionReceiptHmac as b0, soc2ControlCoverageForDecision as b1, verifyDecisionReceiptHmac as b2, type AtlaSentClientOptions as c, type EvaluateResponse as d, type BatchEvalResponse as e, type DecisionStreamEvent as f, type EvaluatePreflightResponse as g, type VerifyPermitResponse as h, type DeployGateRequest as i, type DeployGateResponse as j, type RevokePermitRequest as k, type RevokePermitResponse as l, type RevokePermitByIdInput as m, type RevokePermitByIdResponse as n, type VerifyPermitByIdResponse as o, type PermitValidResponse as p, type ListPermitsResponse as q, type ApiKeySelfResponse as r, type AuditEventsQuery as s, type AuditEventsResult as t, type AuditExportRequest as u, type AuditExportResult as v, type StreamOptions as w, type StreamEvent as x, type DecisionReceipt as y, type DecisionReceiptAlgorithm as z };