@nomosprotocol/sdk 1.3.4 → 1.4.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 +25 -0
- package/dist/index.cjs +90 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -9
- package/dist/index.d.ts +149 -9
- package/dist/index.js +90 -19
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
package/dist/index.d.cts
CHANGED
|
@@ -128,7 +128,10 @@ interface CanResult {
|
|
|
128
128
|
}>;
|
|
129
129
|
/** RECOMMENDED maximum seconds to hold this verdict before re-querying or re-checking the
|
|
130
130
|
* issuer's revocation list (NOMOS-SPEC-006 §6.2). Advisory; absent when the artifact isn't
|
|
131
|
-
* structurally revocable on this deployment — treat absence as "unknown," not "safe forever."
|
|
131
|
+
* structurally revocable on this deployment — treat absence as "unknown," not "safe forever."
|
|
132
|
+
* Disclosed gap: only ever set on the public authority rail today — client.can() and the
|
|
133
|
+
* presented-artifact rail don't emit it yet, so apply the same "don't hold a stale verdict
|
|
134
|
+
* across a meaningful delay" discipline manually on those two until that's closed. */
|
|
132
135
|
max_age?: number;
|
|
133
136
|
/** Present only when your own authenticated artifact answered — that rail's own provenance
|
|
134
137
|
* (a hash you can store for compliance), not a permanent public transcript like `query_id`. */
|
|
@@ -151,6 +154,65 @@ interface CanResult {
|
|
|
151
154
|
* (not a guess) on the Exchange/private-artifact rails, where this concept doesn't apply. */
|
|
152
155
|
revocation_checked?: 'live' | 'staple' | 'unchecked';
|
|
153
156
|
}
|
|
157
|
+
interface ConservedStateInfo {
|
|
158
|
+
artifact_id: string;
|
|
159
|
+
scope: string;
|
|
160
|
+
limit_amount: number;
|
|
161
|
+
consumed: number;
|
|
162
|
+
reserved: number;
|
|
163
|
+
available: number;
|
|
164
|
+
/** The raw ledger fact to pass into `can()` as `consumed_before` — already consumed+reserved
|
|
165
|
+
* (outstanding reservations count as spoken-for), not consumed alone. */
|
|
166
|
+
consumed_before: number;
|
|
167
|
+
version: number;
|
|
168
|
+
state_digest: string;
|
|
169
|
+
}
|
|
170
|
+
/** Reserve against a ledger using a real `can()` decision. */
|
|
171
|
+
interface ReserveByQueryParams {
|
|
172
|
+
artifact_id: string;
|
|
173
|
+
scope: string;
|
|
174
|
+
amount: number;
|
|
175
|
+
/** The `query_id` from a real `can()`/`nomos.can()` call that returned AUTHORIZED.
|
|
176
|
+
* `reserve()` looks that decision up itself and derives its own binding from it — this is a
|
|
177
|
+
* lookup key, not a claim you're asserting, and a made-up or already-used one is rejected. */
|
|
178
|
+
query_id: string;
|
|
179
|
+
action: string;
|
|
180
|
+
/** Optimistic-concurrency check: reject unless the ledger's state still matches what you last
|
|
181
|
+
* read via `conservedState.get()`. Omit only when you have no prior read to compare against. */
|
|
182
|
+
expected_state_digest?: string;
|
|
183
|
+
ttl_seconds?: number;
|
|
184
|
+
}
|
|
185
|
+
/** Reserve against a ledger using a NOMOS-SPEC-008 Act: the ledger verifies the Act itself (its
|
|
186
|
+
* own pinned root and audience id, its own `consumed_before`, single-use nonce) and holds exactly
|
|
187
|
+
* `act.action.params.amount` for `act.action.type === 'conserved_state.reserve'`, or nothing. */
|
|
188
|
+
interface ReserveByActParams {
|
|
189
|
+
artifact_id: string;
|
|
190
|
+
scope: string;
|
|
191
|
+
act: Record<string, unknown>;
|
|
192
|
+
ttl_seconds?: number;
|
|
193
|
+
}
|
|
194
|
+
type ReserveParams = ReserveByQueryParams | ReserveByActParams;
|
|
195
|
+
/** The cryptographically-bound reservation object — R-12 §5. */
|
|
196
|
+
interface Reservation {
|
|
197
|
+
reservation_id: string;
|
|
198
|
+
authority_seal: string;
|
|
199
|
+
decision_digest: string;
|
|
200
|
+
agent: string;
|
|
201
|
+
action: string;
|
|
202
|
+
scope: string;
|
|
203
|
+
amount: number;
|
|
204
|
+
state_digest: string;
|
|
205
|
+
expires_at: string;
|
|
206
|
+
status: 'held' | 'settled' | 'released' | 'expired';
|
|
207
|
+
}
|
|
208
|
+
interface ConservedStateReceipt {
|
|
209
|
+
reservation_id: string;
|
|
210
|
+
outcome: 'settled' | 'released';
|
|
211
|
+
amount: number;
|
|
212
|
+
ledger_key: string;
|
|
213
|
+
resolved_at: string;
|
|
214
|
+
reference?: string;
|
|
215
|
+
}
|
|
154
216
|
type ChainVerdictDecision = 'ALLOWED' | 'ISSUER_NOT_RECOGNIZED' | 'KEY_REVOKED' | 'CERTIFICATE_REVOKED' | 'OUT_OF_SCOPE' | 'SEAL_INVALID' | 'MALFORMED';
|
|
155
217
|
interface KeyCertificate {
|
|
156
218
|
parent_kid: string;
|
|
@@ -525,32 +587,107 @@ declare class Webhooks {
|
|
|
525
587
|
verifySignature(payload: string | Uint8Array, signature: string, secret: string, toleranceSeconds?: number): Promise<boolean>;
|
|
526
588
|
}
|
|
527
589
|
|
|
590
|
+
/**
|
|
591
|
+
* The state layer for a conserved (depleting) budget — R-12, a design in progress
|
|
592
|
+
* (docs/design-notes/conserved-state-authority.md in the platform repo; public mirror:
|
|
593
|
+
* computableauthority.com/research/conserved-state-authority). PROTOTYPE, not a numbered spec.
|
|
594
|
+
*
|
|
595
|
+
* `can()` stays exactly what it always was: pure, stateless, given facts and returning a
|
|
596
|
+
* verdict. Nothing here changes that. This resource is what a caller consults BEFORE calling
|
|
597
|
+
* `can()` (to build the `consumed_before` fact) and commits to AFTER `can()` returns AUTHORIZED
|
|
598
|
+
* and the caller has actually decided to act — never at `can()` time itself (repeatedly calling
|
|
599
|
+
* `can()` while deciding must never itself burn the budget).
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* const state = await client.conservedState.get('supplier-budget-2026', 'supplier:acme');
|
|
603
|
+
* const verdict = await client.can({ artifact_id: 'supplier-budget-2026', consumed_before: state.consumed_before });
|
|
604
|
+
* if (verdict.allowed) {
|
|
605
|
+
* const reservation = await client.conservedState.reserve({
|
|
606
|
+
* artifact_id: 'supplier-budget-2026', scope: 'supplier:acme',
|
|
607
|
+
* amount: 5000, query_id: verdict.query_id!, action: 'payments.transfer',
|
|
608
|
+
* });
|
|
609
|
+
* try {
|
|
610
|
+
* // ... perform the real action ...
|
|
611
|
+
* await client.conservedState.settle(reservation.reservation_id);
|
|
612
|
+
* } catch {
|
|
613
|
+
* await client.conservedState.release(reservation.reservation_id);
|
|
614
|
+
* }
|
|
615
|
+
* }
|
|
616
|
+
*/
|
|
617
|
+
declare class ConservedState {
|
|
618
|
+
private readonly http;
|
|
619
|
+
constructor(http: HttpClient);
|
|
620
|
+
/**
|
|
621
|
+
* Read a ledger's current balance — no auth required, same posture as `can()`'s own keyless
|
|
622
|
+
* rail (reading how much budget remains is no more sensitive than reading a verdict).
|
|
623
|
+
*/
|
|
624
|
+
get(artifactId: string, scope: string, opts?: RequestOptions): Promise<WithLastResponse<ConservedStateInfo>>;
|
|
625
|
+
/**
|
|
626
|
+
* Commit capacity against a REAL, AUTHORIZED `can()` decision. Authenticated — unlike `can()`,
|
|
627
|
+
* spending against a shared budget requires an API key or session; `agent` on the returned
|
|
628
|
+
* reservation traces to that authenticated identity, never a value you pass in.
|
|
629
|
+
*
|
|
630
|
+
* Throws (via the SDK's standard NomosError hierarchy; `err.details?.code` carries the
|
|
631
|
+
* server's own code): `DECISION_NOT_FOUND` (the query_id names no real query), `DECISION_NOT
|
|
632
|
+
* _AUTHORIZED` (that decision didn't verdict AUTHORIZED for this artifact), `DECISION_ALREADY
|
|
633
|
+
* _USED` (already bound to a reservation — one decision authorizes one, ever), `STATE_CONFLICT`
|
|
634
|
+
* (a stale `expected_state_digest`), `INSUFFICIENT_CAPACITY` (`err.details.requested`/
|
|
635
|
+
* `.available` carry the numbers), or `NO_LEDGER` (nobody has declared this budget yet).
|
|
636
|
+
*
|
|
637
|
+
* Passing `{ artifact_id, scope, act }` instead authorizes the hold with a NOMOS-SPEC-008 Act.
|
|
638
|
+
* Extra codes: `ACT_NOT_ACCEPTED` (the ledger has no Act binding) and `ACT_REJECTED`, with the
|
|
639
|
+
* verifier's full answer in `err.details.result` — for `INCOMPLETE` it names the open rules and
|
|
640
|
+
* the facts whose testimony would decide them.
|
|
641
|
+
*/
|
|
642
|
+
reserve(params: ReserveParams, opts?: RequestOptions): Promise<WithLastResponse<Reservation>>;
|
|
643
|
+
/**
|
|
644
|
+
* The reserved action succeeded — moves the hold into real, settled spend. Idempotent when
|
|
645
|
+
* you pass the same `reference` again for a retry (e.g. after a network timeout): you get the
|
|
646
|
+
* ORIGINAL receipt back, never a double-charge.
|
|
647
|
+
*/
|
|
648
|
+
settle(reservationId: string, reference?: string, opts?: RequestOptions): Promise<WithLastResponse<ConservedStateReceipt>>;
|
|
649
|
+
/**
|
|
650
|
+
* The reserved action failed, or you decided not to act — gives the hold's capacity back
|
|
651
|
+
* without ever touching what's actually been spent.
|
|
652
|
+
*/
|
|
653
|
+
release(reservationId: string, reference?: string, opts?: RequestOptions): Promise<WithLastResponse<ConservedStateReceipt>>;
|
|
654
|
+
}
|
|
655
|
+
|
|
528
656
|
type NomosErrorCode = 'authentication_error' | 'authorization_error' | 'rate_limit_error' | 'validation_error' | 'not_found' | 'api_error' | 'network_error' | 'webhook_signature_error' | 'payment_required' | 'issuer_not_trusted';
|
|
529
657
|
declare class NomosError extends Error {
|
|
530
658
|
readonly code: NomosErrorCode;
|
|
531
659
|
readonly status?: number;
|
|
532
660
|
readonly requestId?: string;
|
|
533
|
-
|
|
661
|
+
/** The full parsed JSON response body, when the server sent one — every field it put there
|
|
662
|
+
* (its own `code`, and anything endpoint-specific like `requested`/`available` on a
|
|
663
|
+
* conserved-state capacity rejection, or `price_usd`/`purchase_url` before those got a
|
|
664
|
+
* dedicated error class). `NomosError`'s own `code` above is the SDK's fixed HTTP-status
|
|
665
|
+
* taxonomy; `details?.code` is the SERVER's own, finer-grained application code — the two
|
|
666
|
+
* are deliberately not conflated into one field. */
|
|
667
|
+
readonly details?: unknown;
|
|
668
|
+
constructor(message: string, code: NomosErrorCode, status?: number, requestId?: string, details?: unknown);
|
|
534
669
|
}
|
|
535
670
|
declare class NomosAuthenticationError extends NomosError {
|
|
536
|
-
constructor(message?: string, requestId?: string);
|
|
671
|
+
constructor(message?: string, requestId?: string, details?: unknown);
|
|
537
672
|
}
|
|
538
673
|
declare class NomosAuthorizationError extends NomosError {
|
|
539
|
-
constructor(message?: string, requestId?: string);
|
|
674
|
+
constructor(message?: string, requestId?: string, details?: unknown);
|
|
540
675
|
}
|
|
541
676
|
declare class NomosRateLimitError extends NomosError {
|
|
542
677
|
readonly retryAfter?: number;
|
|
543
|
-
constructor(message?: string, retryAfter?: number, requestId?: string);
|
|
678
|
+
constructor(message?: string, retryAfter?: number, requestId?: string, details?: unknown);
|
|
544
679
|
}
|
|
680
|
+
/** `details` is the parsed response body — kept as its own constructor param (rather than just
|
|
681
|
+
* relying on the base class field) since it long predates NomosError.details and every caller
|
|
682
|
+
* already passes it positionally in this slot; unchanged for backward compatibility. */
|
|
545
683
|
declare class NomosValidationError extends NomosError {
|
|
546
|
-
readonly details?: unknown;
|
|
547
684
|
constructor(message: string, details?: unknown, requestId?: string);
|
|
548
685
|
}
|
|
549
686
|
declare class NomosNotFoundError extends NomosError {
|
|
550
|
-
constructor(message: string, requestId?: string);
|
|
687
|
+
constructor(message: string, requestId?: string, details?: unknown);
|
|
551
688
|
}
|
|
552
689
|
declare class NomosAPIError extends NomosError {
|
|
553
|
-
constructor(message: string, status: number, requestId?: string);
|
|
690
|
+
constructor(message: string, status: number, requestId?: string, details?: unknown);
|
|
554
691
|
}
|
|
555
692
|
declare class NomosNetworkError extends NomosError {
|
|
556
693
|
constructor(message: string);
|
|
@@ -620,6 +757,9 @@ declare class Nomos {
|
|
|
620
757
|
readonly artifacts: Artifacts;
|
|
621
758
|
readonly governance: Governance;
|
|
622
759
|
readonly webhooks: Webhooks;
|
|
760
|
+
/** The R-12 conserved-state (depleting-budget) state layer — PROTOTYPE, not a numbered spec.
|
|
761
|
+
* See resources/conserved-state.ts for the full reserve/settle/release lifecycle. */
|
|
762
|
+
readonly conservedState: ConservedState;
|
|
623
763
|
/** Whether this instance is using a live or test API key. */
|
|
624
764
|
readonly mode: 'live' | 'test';
|
|
625
765
|
static readonly errors: typeof errors;
|
|
@@ -669,4 +809,4 @@ declare const nomos: {
|
|
|
669
809
|
*/
|
|
670
810
|
declare function verifyChain(params: VerifyChainParams, opts?: VerifyChainOptions): Promise<VerifyChainResult>;
|
|
671
811
|
|
|
672
|
-
export { type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type CanOptions, type CanParams, type CanResult, type CanVerdict, type ChainVerdictDecision, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type GenerateGovernanceParams, type GenerateGovernanceResult, type KeyCertificate, type ListArtifactsParams, Nomos, NomosAPIError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type RequestOptions, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, type WithLastResponse, can, nomos, verifyChain };
|
|
812
|
+
export { type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type CanOptions, type CanParams, type CanResult, type CanVerdict, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type GenerateGovernanceParams, type GenerateGovernanceResult, type KeyCertificate, type ListArtifactsParams, Nomos, NomosAPIError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, type WithLastResponse, can, nomos, verifyChain };
|
package/dist/index.d.ts
CHANGED
|
@@ -128,7 +128,10 @@ interface CanResult {
|
|
|
128
128
|
}>;
|
|
129
129
|
/** RECOMMENDED maximum seconds to hold this verdict before re-querying or re-checking the
|
|
130
130
|
* issuer's revocation list (NOMOS-SPEC-006 §6.2). Advisory; absent when the artifact isn't
|
|
131
|
-
* structurally revocable on this deployment — treat absence as "unknown," not "safe forever."
|
|
131
|
+
* structurally revocable on this deployment — treat absence as "unknown," not "safe forever."
|
|
132
|
+
* Disclosed gap: only ever set on the public authority rail today — client.can() and the
|
|
133
|
+
* presented-artifact rail don't emit it yet, so apply the same "don't hold a stale verdict
|
|
134
|
+
* across a meaningful delay" discipline manually on those two until that's closed. */
|
|
132
135
|
max_age?: number;
|
|
133
136
|
/** Present only when your own authenticated artifact answered — that rail's own provenance
|
|
134
137
|
* (a hash you can store for compliance), not a permanent public transcript like `query_id`. */
|
|
@@ -151,6 +154,65 @@ interface CanResult {
|
|
|
151
154
|
* (not a guess) on the Exchange/private-artifact rails, where this concept doesn't apply. */
|
|
152
155
|
revocation_checked?: 'live' | 'staple' | 'unchecked';
|
|
153
156
|
}
|
|
157
|
+
interface ConservedStateInfo {
|
|
158
|
+
artifact_id: string;
|
|
159
|
+
scope: string;
|
|
160
|
+
limit_amount: number;
|
|
161
|
+
consumed: number;
|
|
162
|
+
reserved: number;
|
|
163
|
+
available: number;
|
|
164
|
+
/** The raw ledger fact to pass into `can()` as `consumed_before` — already consumed+reserved
|
|
165
|
+
* (outstanding reservations count as spoken-for), not consumed alone. */
|
|
166
|
+
consumed_before: number;
|
|
167
|
+
version: number;
|
|
168
|
+
state_digest: string;
|
|
169
|
+
}
|
|
170
|
+
/** Reserve against a ledger using a real `can()` decision. */
|
|
171
|
+
interface ReserveByQueryParams {
|
|
172
|
+
artifact_id: string;
|
|
173
|
+
scope: string;
|
|
174
|
+
amount: number;
|
|
175
|
+
/** The `query_id` from a real `can()`/`nomos.can()` call that returned AUTHORIZED.
|
|
176
|
+
* `reserve()` looks that decision up itself and derives its own binding from it — this is a
|
|
177
|
+
* lookup key, not a claim you're asserting, and a made-up or already-used one is rejected. */
|
|
178
|
+
query_id: string;
|
|
179
|
+
action: string;
|
|
180
|
+
/** Optimistic-concurrency check: reject unless the ledger's state still matches what you last
|
|
181
|
+
* read via `conservedState.get()`. Omit only when you have no prior read to compare against. */
|
|
182
|
+
expected_state_digest?: string;
|
|
183
|
+
ttl_seconds?: number;
|
|
184
|
+
}
|
|
185
|
+
/** Reserve against a ledger using a NOMOS-SPEC-008 Act: the ledger verifies the Act itself (its
|
|
186
|
+
* own pinned root and audience id, its own `consumed_before`, single-use nonce) and holds exactly
|
|
187
|
+
* `act.action.params.amount` for `act.action.type === 'conserved_state.reserve'`, or nothing. */
|
|
188
|
+
interface ReserveByActParams {
|
|
189
|
+
artifact_id: string;
|
|
190
|
+
scope: string;
|
|
191
|
+
act: Record<string, unknown>;
|
|
192
|
+
ttl_seconds?: number;
|
|
193
|
+
}
|
|
194
|
+
type ReserveParams = ReserveByQueryParams | ReserveByActParams;
|
|
195
|
+
/** The cryptographically-bound reservation object — R-12 §5. */
|
|
196
|
+
interface Reservation {
|
|
197
|
+
reservation_id: string;
|
|
198
|
+
authority_seal: string;
|
|
199
|
+
decision_digest: string;
|
|
200
|
+
agent: string;
|
|
201
|
+
action: string;
|
|
202
|
+
scope: string;
|
|
203
|
+
amount: number;
|
|
204
|
+
state_digest: string;
|
|
205
|
+
expires_at: string;
|
|
206
|
+
status: 'held' | 'settled' | 'released' | 'expired';
|
|
207
|
+
}
|
|
208
|
+
interface ConservedStateReceipt {
|
|
209
|
+
reservation_id: string;
|
|
210
|
+
outcome: 'settled' | 'released';
|
|
211
|
+
amount: number;
|
|
212
|
+
ledger_key: string;
|
|
213
|
+
resolved_at: string;
|
|
214
|
+
reference?: string;
|
|
215
|
+
}
|
|
154
216
|
type ChainVerdictDecision = 'ALLOWED' | 'ISSUER_NOT_RECOGNIZED' | 'KEY_REVOKED' | 'CERTIFICATE_REVOKED' | 'OUT_OF_SCOPE' | 'SEAL_INVALID' | 'MALFORMED';
|
|
155
217
|
interface KeyCertificate {
|
|
156
218
|
parent_kid: string;
|
|
@@ -525,32 +587,107 @@ declare class Webhooks {
|
|
|
525
587
|
verifySignature(payload: string | Uint8Array, signature: string, secret: string, toleranceSeconds?: number): Promise<boolean>;
|
|
526
588
|
}
|
|
527
589
|
|
|
590
|
+
/**
|
|
591
|
+
* The state layer for a conserved (depleting) budget — R-12, a design in progress
|
|
592
|
+
* (docs/design-notes/conserved-state-authority.md in the platform repo; public mirror:
|
|
593
|
+
* computableauthority.com/research/conserved-state-authority). PROTOTYPE, not a numbered spec.
|
|
594
|
+
*
|
|
595
|
+
* `can()` stays exactly what it always was: pure, stateless, given facts and returning a
|
|
596
|
+
* verdict. Nothing here changes that. This resource is what a caller consults BEFORE calling
|
|
597
|
+
* `can()` (to build the `consumed_before` fact) and commits to AFTER `can()` returns AUTHORIZED
|
|
598
|
+
* and the caller has actually decided to act — never at `can()` time itself (repeatedly calling
|
|
599
|
+
* `can()` while deciding must never itself burn the budget).
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* const state = await client.conservedState.get('supplier-budget-2026', 'supplier:acme');
|
|
603
|
+
* const verdict = await client.can({ artifact_id: 'supplier-budget-2026', consumed_before: state.consumed_before });
|
|
604
|
+
* if (verdict.allowed) {
|
|
605
|
+
* const reservation = await client.conservedState.reserve({
|
|
606
|
+
* artifact_id: 'supplier-budget-2026', scope: 'supplier:acme',
|
|
607
|
+
* amount: 5000, query_id: verdict.query_id!, action: 'payments.transfer',
|
|
608
|
+
* });
|
|
609
|
+
* try {
|
|
610
|
+
* // ... perform the real action ...
|
|
611
|
+
* await client.conservedState.settle(reservation.reservation_id);
|
|
612
|
+
* } catch {
|
|
613
|
+
* await client.conservedState.release(reservation.reservation_id);
|
|
614
|
+
* }
|
|
615
|
+
* }
|
|
616
|
+
*/
|
|
617
|
+
declare class ConservedState {
|
|
618
|
+
private readonly http;
|
|
619
|
+
constructor(http: HttpClient);
|
|
620
|
+
/**
|
|
621
|
+
* Read a ledger's current balance — no auth required, same posture as `can()`'s own keyless
|
|
622
|
+
* rail (reading how much budget remains is no more sensitive than reading a verdict).
|
|
623
|
+
*/
|
|
624
|
+
get(artifactId: string, scope: string, opts?: RequestOptions): Promise<WithLastResponse<ConservedStateInfo>>;
|
|
625
|
+
/**
|
|
626
|
+
* Commit capacity against a REAL, AUTHORIZED `can()` decision. Authenticated — unlike `can()`,
|
|
627
|
+
* spending against a shared budget requires an API key or session; `agent` on the returned
|
|
628
|
+
* reservation traces to that authenticated identity, never a value you pass in.
|
|
629
|
+
*
|
|
630
|
+
* Throws (via the SDK's standard NomosError hierarchy; `err.details?.code` carries the
|
|
631
|
+
* server's own code): `DECISION_NOT_FOUND` (the query_id names no real query), `DECISION_NOT
|
|
632
|
+
* _AUTHORIZED` (that decision didn't verdict AUTHORIZED for this artifact), `DECISION_ALREADY
|
|
633
|
+
* _USED` (already bound to a reservation — one decision authorizes one, ever), `STATE_CONFLICT`
|
|
634
|
+
* (a stale `expected_state_digest`), `INSUFFICIENT_CAPACITY` (`err.details.requested`/
|
|
635
|
+
* `.available` carry the numbers), or `NO_LEDGER` (nobody has declared this budget yet).
|
|
636
|
+
*
|
|
637
|
+
* Passing `{ artifact_id, scope, act }` instead authorizes the hold with a NOMOS-SPEC-008 Act.
|
|
638
|
+
* Extra codes: `ACT_NOT_ACCEPTED` (the ledger has no Act binding) and `ACT_REJECTED`, with the
|
|
639
|
+
* verifier's full answer in `err.details.result` — for `INCOMPLETE` it names the open rules and
|
|
640
|
+
* the facts whose testimony would decide them.
|
|
641
|
+
*/
|
|
642
|
+
reserve(params: ReserveParams, opts?: RequestOptions): Promise<WithLastResponse<Reservation>>;
|
|
643
|
+
/**
|
|
644
|
+
* The reserved action succeeded — moves the hold into real, settled spend. Idempotent when
|
|
645
|
+
* you pass the same `reference` again for a retry (e.g. after a network timeout): you get the
|
|
646
|
+
* ORIGINAL receipt back, never a double-charge.
|
|
647
|
+
*/
|
|
648
|
+
settle(reservationId: string, reference?: string, opts?: RequestOptions): Promise<WithLastResponse<ConservedStateReceipt>>;
|
|
649
|
+
/**
|
|
650
|
+
* The reserved action failed, or you decided not to act — gives the hold's capacity back
|
|
651
|
+
* without ever touching what's actually been spent.
|
|
652
|
+
*/
|
|
653
|
+
release(reservationId: string, reference?: string, opts?: RequestOptions): Promise<WithLastResponse<ConservedStateReceipt>>;
|
|
654
|
+
}
|
|
655
|
+
|
|
528
656
|
type NomosErrorCode = 'authentication_error' | 'authorization_error' | 'rate_limit_error' | 'validation_error' | 'not_found' | 'api_error' | 'network_error' | 'webhook_signature_error' | 'payment_required' | 'issuer_not_trusted';
|
|
529
657
|
declare class NomosError extends Error {
|
|
530
658
|
readonly code: NomosErrorCode;
|
|
531
659
|
readonly status?: number;
|
|
532
660
|
readonly requestId?: string;
|
|
533
|
-
|
|
661
|
+
/** The full parsed JSON response body, when the server sent one — every field it put there
|
|
662
|
+
* (its own `code`, and anything endpoint-specific like `requested`/`available` on a
|
|
663
|
+
* conserved-state capacity rejection, or `price_usd`/`purchase_url` before those got a
|
|
664
|
+
* dedicated error class). `NomosError`'s own `code` above is the SDK's fixed HTTP-status
|
|
665
|
+
* taxonomy; `details?.code` is the SERVER's own, finer-grained application code — the two
|
|
666
|
+
* are deliberately not conflated into one field. */
|
|
667
|
+
readonly details?: unknown;
|
|
668
|
+
constructor(message: string, code: NomosErrorCode, status?: number, requestId?: string, details?: unknown);
|
|
534
669
|
}
|
|
535
670
|
declare class NomosAuthenticationError extends NomosError {
|
|
536
|
-
constructor(message?: string, requestId?: string);
|
|
671
|
+
constructor(message?: string, requestId?: string, details?: unknown);
|
|
537
672
|
}
|
|
538
673
|
declare class NomosAuthorizationError extends NomosError {
|
|
539
|
-
constructor(message?: string, requestId?: string);
|
|
674
|
+
constructor(message?: string, requestId?: string, details?: unknown);
|
|
540
675
|
}
|
|
541
676
|
declare class NomosRateLimitError extends NomosError {
|
|
542
677
|
readonly retryAfter?: number;
|
|
543
|
-
constructor(message?: string, retryAfter?: number, requestId?: string);
|
|
678
|
+
constructor(message?: string, retryAfter?: number, requestId?: string, details?: unknown);
|
|
544
679
|
}
|
|
680
|
+
/** `details` is the parsed response body — kept as its own constructor param (rather than just
|
|
681
|
+
* relying on the base class field) since it long predates NomosError.details and every caller
|
|
682
|
+
* already passes it positionally in this slot; unchanged for backward compatibility. */
|
|
545
683
|
declare class NomosValidationError extends NomosError {
|
|
546
|
-
readonly details?: unknown;
|
|
547
684
|
constructor(message: string, details?: unknown, requestId?: string);
|
|
548
685
|
}
|
|
549
686
|
declare class NomosNotFoundError extends NomosError {
|
|
550
|
-
constructor(message: string, requestId?: string);
|
|
687
|
+
constructor(message: string, requestId?: string, details?: unknown);
|
|
551
688
|
}
|
|
552
689
|
declare class NomosAPIError extends NomosError {
|
|
553
|
-
constructor(message: string, status: number, requestId?: string);
|
|
690
|
+
constructor(message: string, status: number, requestId?: string, details?: unknown);
|
|
554
691
|
}
|
|
555
692
|
declare class NomosNetworkError extends NomosError {
|
|
556
693
|
constructor(message: string);
|
|
@@ -620,6 +757,9 @@ declare class Nomos {
|
|
|
620
757
|
readonly artifacts: Artifacts;
|
|
621
758
|
readonly governance: Governance;
|
|
622
759
|
readonly webhooks: Webhooks;
|
|
760
|
+
/** The R-12 conserved-state (depleting-budget) state layer — PROTOTYPE, not a numbered spec.
|
|
761
|
+
* See resources/conserved-state.ts for the full reserve/settle/release lifecycle. */
|
|
762
|
+
readonly conservedState: ConservedState;
|
|
623
763
|
/** Whether this instance is using a live or test API key. */
|
|
624
764
|
readonly mode: 'live' | 'test';
|
|
625
765
|
static readonly errors: typeof errors;
|
|
@@ -669,4 +809,4 @@ declare const nomos: {
|
|
|
669
809
|
*/
|
|
670
810
|
declare function verifyChain(params: VerifyChainParams, opts?: VerifyChainOptions): Promise<VerifyChainResult>;
|
|
671
811
|
|
|
672
|
-
export { type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type CanOptions, type CanParams, type CanResult, type CanVerdict, type ChainVerdictDecision, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type GenerateGovernanceParams, type GenerateGovernanceResult, type KeyCertificate, type ListArtifactsParams, Nomos, NomosAPIError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type RequestOptions, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, type WithLastResponse, can, nomos, verifyChain };
|
|
812
|
+
export { type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type CanOptions, type CanParams, type CanResult, type CanVerdict, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type GenerateGovernanceParams, type GenerateGovernanceResult, type KeyCertificate, type ListArtifactsParams, Nomos, NomosAPIError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, type WithLastResponse, can, nomos, verifyChain };
|
package/dist/index.js
CHANGED
|
@@ -20,49 +20,49 @@ __export(errors_exports, {
|
|
|
20
20
|
NomosWebhookSignatureError: () => NomosWebhookSignatureError
|
|
21
21
|
});
|
|
22
22
|
var NomosError = class extends Error {
|
|
23
|
-
constructor(message, code, status, requestId) {
|
|
23
|
+
constructor(message, code, status, requestId, details) {
|
|
24
24
|
super(message);
|
|
25
25
|
this.name = "NomosError";
|
|
26
26
|
this.code = code;
|
|
27
27
|
this.status = status;
|
|
28
28
|
this.requestId = requestId;
|
|
29
|
+
this.details = details;
|
|
29
30
|
}
|
|
30
31
|
};
|
|
31
32
|
var NomosAuthenticationError = class extends NomosError {
|
|
32
|
-
constructor(message = "Invalid API key", requestId) {
|
|
33
|
-
super(message, "authentication_error", 401, requestId);
|
|
33
|
+
constructor(message = "Invalid API key", requestId, details) {
|
|
34
|
+
super(message, "authentication_error", 401, requestId, details);
|
|
34
35
|
this.name = "NomosAuthenticationError";
|
|
35
36
|
}
|
|
36
37
|
};
|
|
37
38
|
var NomosAuthorizationError = class extends NomosError {
|
|
38
|
-
constructor(message = "Access denied", requestId) {
|
|
39
|
-
super(message, "authorization_error", 403, requestId);
|
|
39
|
+
constructor(message = "Access denied", requestId, details) {
|
|
40
|
+
super(message, "authorization_error", 403, requestId, details);
|
|
40
41
|
this.name = "NomosAuthorizationError";
|
|
41
42
|
}
|
|
42
43
|
};
|
|
43
44
|
var NomosRateLimitError = class extends NomosError {
|
|
44
|
-
constructor(message = "Rate limit exceeded", retryAfter, requestId) {
|
|
45
|
-
super(message, "rate_limit_error", 429, requestId);
|
|
45
|
+
constructor(message = "Rate limit exceeded", retryAfter, requestId, details) {
|
|
46
|
+
super(message, "rate_limit_error", 429, requestId, details);
|
|
46
47
|
this.name = "NomosRateLimitError";
|
|
47
48
|
this.retryAfter = retryAfter;
|
|
48
49
|
}
|
|
49
50
|
};
|
|
50
51
|
var NomosValidationError = class extends NomosError {
|
|
51
52
|
constructor(message, details, requestId) {
|
|
52
|
-
super(message, "validation_error", 400, requestId);
|
|
53
|
+
super(message, "validation_error", 400, requestId, details);
|
|
53
54
|
this.name = "NomosValidationError";
|
|
54
|
-
this.details = details;
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
var NomosNotFoundError = class extends NomosError {
|
|
58
|
-
constructor(message, requestId) {
|
|
59
|
-
super(message, "not_found", 404, requestId);
|
|
58
|
+
constructor(message, requestId, details) {
|
|
59
|
+
super(message, "not_found", 404, requestId, details);
|
|
60
60
|
this.name = "NomosNotFoundError";
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
var NomosAPIError = class extends NomosError {
|
|
64
|
-
constructor(message, status, requestId) {
|
|
65
|
-
super(message, "api_error", status, requestId);
|
|
64
|
+
constructor(message, status, requestId, details) {
|
|
65
|
+
super(message, "api_error", status, requestId, details);
|
|
66
66
|
this.name = "NomosAPIError";
|
|
67
67
|
}
|
|
68
68
|
};
|
|
@@ -179,22 +179,27 @@ var HttpClient = class {
|
|
|
179
179
|
const message = errBody?.error ?? errBody?.message ?? res.statusText;
|
|
180
180
|
switch (res.status) {
|
|
181
181
|
case 401:
|
|
182
|
-
throw new NomosAuthenticationError(message, requestId);
|
|
182
|
+
throw new NomosAuthenticationError(message, requestId, errBody);
|
|
183
183
|
case 403:
|
|
184
|
-
throw new NomosAuthorizationError(message, requestId);
|
|
184
|
+
throw new NomosAuthorizationError(message, requestId, errBody);
|
|
185
185
|
case 404:
|
|
186
|
-
throw new NomosNotFoundError(message, requestId);
|
|
186
|
+
throw new NomosNotFoundError(message, requestId, errBody);
|
|
187
187
|
case 422:
|
|
188
188
|
case 400:
|
|
189
|
-
throw new NomosValidationError(message, errBody?.details, requestId);
|
|
189
|
+
throw new NomosValidationError(message, errBody?.details ?? errBody, requestId);
|
|
190
190
|
case 429:
|
|
191
191
|
throw new NomosRateLimitError(
|
|
192
192
|
message,
|
|
193
193
|
res.headers.get("retry-after") ? parseInt(res.headers.get("retry-after"), 10) : void 0,
|
|
194
|
-
requestId
|
|
194
|
+
requestId,
|
|
195
|
+
errBody
|
|
195
196
|
);
|
|
197
|
+
// No case for 409 (conflict) specifically — several endpoints use it for distinct
|
|
198
|
+
// application-level rejections (e.g. conserved-state capacity/conflict/already-used);
|
|
199
|
+
// `errBody` (this default branch's `details`) is what lets a caller tell those apart via
|
|
200
|
+
// `err.details?.code`, not a status-code switch that would have to keep growing per feature.
|
|
196
201
|
default:
|
|
197
|
-
throw new NomosAPIError(message, res.status, requestId);
|
|
202
|
+
throw new NomosAPIError(message, res.status, requestId, errBody);
|
|
198
203
|
}
|
|
199
204
|
}
|
|
200
205
|
};
|
|
@@ -512,6 +517,71 @@ var Webhooks = class {
|
|
|
512
517
|
}
|
|
513
518
|
};
|
|
514
519
|
|
|
520
|
+
// src/resources/conserved-state.ts
|
|
521
|
+
var ConservedState = class {
|
|
522
|
+
constructor(http) {
|
|
523
|
+
this.http = http;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Read a ledger's current balance — no auth required, same posture as `can()`'s own keyless
|
|
527
|
+
* rail (reading how much budget remains is no more sensitive than reading a verdict).
|
|
528
|
+
*/
|
|
529
|
+
async get(artifactId, scope, opts) {
|
|
530
|
+
const qs = new URLSearchParams({ artifact_id: artifactId, scope });
|
|
531
|
+
const { data, lastResponse } = await this.http.get(
|
|
532
|
+
`/api/v1/conserved-state?${qs.toString()}`,
|
|
533
|
+
opts
|
|
534
|
+
);
|
|
535
|
+
return { ...data, lastResponse };
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Commit capacity against a REAL, AUTHORIZED `can()` decision. Authenticated — unlike `can()`,
|
|
539
|
+
* spending against a shared budget requires an API key or session; `agent` on the returned
|
|
540
|
+
* reservation traces to that authenticated identity, never a value you pass in.
|
|
541
|
+
*
|
|
542
|
+
* Throws (via the SDK's standard NomosError hierarchy; `err.details?.code` carries the
|
|
543
|
+
* server's own code): `DECISION_NOT_FOUND` (the query_id names no real query), `DECISION_NOT
|
|
544
|
+
* _AUTHORIZED` (that decision didn't verdict AUTHORIZED for this artifact), `DECISION_ALREADY
|
|
545
|
+
* _USED` (already bound to a reservation — one decision authorizes one, ever), `STATE_CONFLICT`
|
|
546
|
+
* (a stale `expected_state_digest`), `INSUFFICIENT_CAPACITY` (`err.details.requested`/
|
|
547
|
+
* `.available` carry the numbers), or `NO_LEDGER` (nobody has declared this budget yet).
|
|
548
|
+
*
|
|
549
|
+
* Passing `{ artifact_id, scope, act }` instead authorizes the hold with a NOMOS-SPEC-008 Act.
|
|
550
|
+
* Extra codes: `ACT_NOT_ACCEPTED` (the ledger has no Act binding) and `ACT_REJECTED`, with the
|
|
551
|
+
* verifier's full answer in `err.details.result` — for `INCOMPLETE` it names the open rules and
|
|
552
|
+
* the facts whose testimony would decide them.
|
|
553
|
+
*/
|
|
554
|
+
async reserve(params, opts) {
|
|
555
|
+
const { data, lastResponse } = await this.http.post("/api/v1/reserve", params, opts);
|
|
556
|
+
return { ...data, lastResponse };
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* The reserved action succeeded — moves the hold into real, settled spend. Idempotent when
|
|
560
|
+
* you pass the same `reference` again for a retry (e.g. after a network timeout): you get the
|
|
561
|
+
* ORIGINAL receipt back, never a double-charge.
|
|
562
|
+
*/
|
|
563
|
+
async settle(reservationId, reference, opts) {
|
|
564
|
+
const { data, lastResponse } = await this.http.post(
|
|
565
|
+
"/api/v1/settle",
|
|
566
|
+
{ reservation_id: reservationId, reference },
|
|
567
|
+
opts
|
|
568
|
+
);
|
|
569
|
+
return { ...data, lastResponse };
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* The reserved action failed, or you decided not to act — gives the hold's capacity back
|
|
573
|
+
* without ever touching what's actually been spent.
|
|
574
|
+
*/
|
|
575
|
+
async release(reservationId, reference, opts) {
|
|
576
|
+
const { data, lastResponse } = await this.http.post(
|
|
577
|
+
"/api/v1/release",
|
|
578
|
+
{ reservation_id: reservationId, reference },
|
|
579
|
+
opts
|
|
580
|
+
);
|
|
581
|
+
return { ...data, lastResponse };
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
|
|
515
585
|
// src/can.ts
|
|
516
586
|
var CHAIN_PROTOCOL_VERSION = "1";
|
|
517
587
|
var DEFAULT_EXCHANGE_BASE = "https://www.nomosprotocol.com";
|
|
@@ -726,6 +796,7 @@ var Nomos = class {
|
|
|
726
796
|
this.artifacts = new Artifacts(this.http);
|
|
727
797
|
this.governance = new Governance(this.http);
|
|
728
798
|
this.webhooks = new Webhooks();
|
|
799
|
+
this.conservedState = new ConservedState(this.http);
|
|
729
800
|
}
|
|
730
801
|
/**
|
|
731
802
|
* "Can I do this?" — the one verb, now covering your own artifacts too. Pass `authority` for a
|