@nomosprotocol/sdk 2.5.0 → 2.6.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 CHANGED
@@ -122,9 +122,55 @@ It abstracts three different authority situations behind one call and one verdic
122
122
  | A public authority published on the NOMOS Exchange | `nomos.can({ authority, ...facts })` | No |
123
123
  | Your organization's sealed artifact | `client.can({ artifact_id, ...facts })` | Yes |
124
124
  | A sealed artifact presented directly by an agent or system | `nomos.can({ artifact, key_certs, root_public_key_pem, ...facts })` | No |
125
+ | An act, and you don't know which laws govern it | `nomos.can({ context, ...facts })` | No |
125
126
 
126
127
  The caller does not need to know which evaluation rail is being used. `can()` routes internally based on what you provide.
127
128
 
129
+ ### 0. Ask by context: let NOMOS find the laws (2.6.0)
130
+
131
+ Naming a law lets the caller choose which law is checked, and leave out the strict one. Describe
132
+ the act instead, and NOMOS works out which laws govern it, asks every one, and combines the answers:
133
+ any deny denies, and a yes needs every law to say yes.
134
+
135
+ ```ts
136
+ import { can } from '@nomosprotocol/sdk';
137
+
138
+ const v = await can({
139
+ context: {
140
+ jurisdiction: 'AE', // where the act takes effect
141
+ established_in: 'DIFC', // where your organisation is incorporated or established
142
+ subjects_in: 'AE', // where the people it concerns are
143
+ output_used_in: 'AE', // where any AI output is used
144
+ },
145
+ action: 'share_customer_data_with_analytics_vendor',
146
+ processing_fair_and_transparent: false,
147
+ });
148
+
149
+ v.mode; // 'resolution'
150
+ v.verdict; // 'DENIED': a DIFC firm acting in mainland Dubai answers to DIFC law
151
+ v.answered_under; // ['difc-data-protection-law-difc-law-no-5-of-2020']
152
+ v.coverage; // what the answer covers, in words
153
+ v.members; // each law's own answer, each a signed record (query_id)
154
+ ```
155
+
156
+ What can come back besides a verdict:
157
+
158
+ - `INSUFFICIENT_DATA` with `open_facts`: a fact that decides which law applies was not stated
159
+ (for example `established_in`). It is asked for, never assumed.
160
+ - `INSUFFICIENT_DATA` for a place the registrar does not cover (Saudi Arabia, the US, an ADGM or
161
+ QFC firm today). "No law found" is never read as "no law applies".
162
+ - `CONFLICT`: two registrars each claim exclusive precedence. A person decides.
163
+
164
+ Which laws apply where comes from the NOMOS registrar: a signed, append-only list in the public
165
+ log at `/.well-known/nomos-exchange-log`, verifiable offline. Each claim states the provision it
166
+ rests on. It currently covers the EU AI Act, Qatar Law No. 13 of 2016, UAE Federal Decree-Law
167
+ No. 45 of 2021 and DIFC Law No. 5 of 2020, and lists only its own laws, so other law may also
168
+ apply. Only want the list of laws? `POST /api/v1/resolve { context }` returns it without asking
169
+ them, and records nothing.
170
+
171
+ `accept()` still verifies the one authority an Act carries. Resolving inside `accept()`, from
172
+ registrars the receiving system pins, is the next step of NOMOS-SPEC-014.
173
+
128
174
  ### 1. Public authority
129
175
 
130
176
  Ask a published authority directly:
@@ -187,7 +233,11 @@ import { Nomos } from "@nomosprotocol/sdk";
187
233
 
188
234
  const client = new Nomos(process.env.NOMOS_API_KEY!);
189
235
  const r = await client.can(
190
- { authority: "data-protection-law-difc-law-no-5-of-2020", days_since_access_request: 45 },
236
+ { authority: "difc-data-protection-law-difc-law-no-5-of-2020",
237
+ ...allTwelveNo, // the law's intake questions, listed on its page
238
+ q_individual_request: true, // "yes": a request from a person
239
+ access_request_received: true, days_since_access_request: 45,
240
+ identity_doubt_unresolved: false, response_period_extended_with_notice: false },
191
241
  { awaitApproval: { timeoutMs: 10 * 60_000, onApproval: (a) => console.log("waiting on", a.url) } },
192
242
  );
193
243
 
@@ -196,6 +246,14 @@ else console.log(r.verdict, r.approval?.status); // DENIED, or rejected / timed_
196
246
  ```
197
247
 
198
248
  The approval is bound to the authority, the action and the facts: different facts need a new one.
249
+
250
+ A large law carries a few yes/no **intake questions** (listed on its page). Each "no" rules out the
251
+ articles it covers, so an ordinary action gets a firm answer in one call: on the DIFC Data Protection
252
+ Law, all twelve "no" is `AUTHORIZED`.
253
+
254
+ When a listing is replaced by a newer version and retired, calls to the old address throw a
255
+ `NomosAPIError` with status 410: the message names the new version's address. Move to it; the old
256
+ address is never answered from the old rules.
199
257
  When the law gets a new version, you get an email with what changed. Turn either email off on the
200
258
  same page.
201
259
 
package/dist/index.cjs CHANGED
@@ -191,7 +191,7 @@ function isRetryable(status) {
191
191
  }
192
192
 
193
193
  // src/http-client.ts
194
- var SDK_VERSION = "2.5.0";
194
+ var SDK_VERSION = "2.6.0";
195
195
  var HttpClient = class {
196
196
  constructor(opts) {
197
197
  this.opts = opts;
@@ -858,6 +858,50 @@ async function canViaChain(artifact, keyCerts, rootPublicKeyPem, facts, action,
858
858
  if (data?.decision === "MALFORMED") throw new NomosValidationError(message, data, requestId);
859
859
  mapHttpError(res.status, data, requestId);
860
860
  }
861
+ function normalizeResolutionResult(data) {
862
+ return {
863
+ mode: "resolution",
864
+ allowed: data.verdict === "AUTHORIZED",
865
+ verdict: data.verdict,
866
+ reason: data.reason ?? "",
867
+ resolution: data.resolution ?? { status: "INCOMPLETE", proof: null },
868
+ members: Array.isArray(data.members) ? data.members : [],
869
+ deciding_authorities: Array.isArray(data.deciding_authorities) ? data.deciding_authorities : [],
870
+ escalations: Array.isArray(data.escalations) ? data.escalations : [],
871
+ open_facts: Array.isArray(data.open_facts) ? data.open_facts : [],
872
+ ...Array.isArray(data.answered_under) ? { answered_under: data.answered_under } : {},
873
+ ...typeof data.coverage === "string" ? { coverage: data.coverage } : {}
874
+ };
875
+ }
876
+ function resolutionBody(params) {
877
+ const { authority, artifact_id, artifact, key_certs, root_public_key_pem, action, inputs, context, ...flat } = params;
878
+ void authority;
879
+ void artifact_id;
880
+ void artifact;
881
+ void key_certs;
882
+ void root_public_key_pem;
883
+ const facts = inputs && typeof inputs === "object" ? inputs : flat;
884
+ return { context, ...action ? { action } : {}, ...facts };
885
+ }
886
+ async function canViaResolution(params, opts) {
887
+ const base = (opts?.baseUrl ?? DEFAULT_EXCHANGE_BASE).replace(/\/$/, "");
888
+ let res;
889
+ try {
890
+ res = await fetch(`${base}/api/v1/can`, {
891
+ method: "POST",
892
+ headers: { "Content-Type": "application/json", "User-Agent": `nomos-node/${SDK_VERSION}`, ...callerHeaders(opts?.caller) },
893
+ body: JSON.stringify(resolutionBody(params)),
894
+ signal: opts?.signal
895
+ });
896
+ } catch (err) {
897
+ if (err?.name === "AbortError") throw new NomosNetworkError("Request aborted");
898
+ throw new NomosNetworkError(err?.message ?? "Network error");
899
+ }
900
+ const requestId = res.headers.get("x-request-id") ?? "";
901
+ const data = await res.json().catch(() => null);
902
+ if (!res.ok) mapHttpError(res.status, data, requestId);
903
+ return normalizeResolutionResult(data);
904
+ }
861
905
  async function can(params, opts) {
862
906
  if (params.artifact !== void 0) {
863
907
  if (!params.key_certs || !params.root_public_key_pem) {
@@ -869,8 +913,11 @@ async function can(params, opts) {
869
913
  if (params.artifact_id) {
870
914
  throw new Error("can(): `artifact_id` requires an API key \u2014 use `client.can(...)` on an authenticated Nomos instance, not the free `can()` export.");
871
915
  }
916
+ if (!params.authority && params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
917
+ return canViaResolution(params, opts);
918
+ }
872
919
  if (!params.authority || typeof params.authority !== "string") {
873
- throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()), or `artifact` (chain-of-trust) is required.");
920
+ throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
874
921
  }
875
922
  const { facts, action } = splitFacts(params);
876
923
  return canViaExchange(params.authority, facts, action, opts);
@@ -885,7 +932,11 @@ async function canAuthenticated(http, params, opts) {
885
932
  }
886
933
  if (params.artifact_id) return canViaArtifact(http, params.artifact_id, facts, action);
887
934
  if (params.authority) return canViaExchangeAuthenticated(http, params.authority, facts, action, opts);
888
- throw new Error("can(): `authority` (a public Exchange artifact), `artifact_id` (your own artifact), or `artifact` (chain-of-trust) is required.");
935
+ if (params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
936
+ const data = (await http.post("/api/v1/can", resolutionBody(params), { headers: callerHeaders(opts?.caller) })).data;
937
+ return normalizeResolutionResult(data);
938
+ }
939
+ throw new Error("can(): `authority` (a public Exchange artifact), `artifact_id` (your own artifact), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
889
940
  }
890
941
  var nomos = { can };
891
942
 
package/dist/index.d.cts CHANGED
@@ -80,9 +80,77 @@ interface CanParams {
80
80
  /** The facts to evaluate. Either pass this explicitly, or spread the facts flat alongside
81
81
  * `authority`/`action` — both shapes are accepted (explicit `inputs` wins if both given). */
82
82
  inputs?: Record<string, unknown>;
83
+ /** Resolution rail (NOMOS-SPEC-014 Draft): the properties that attach law to an act —
84
+ * e.g. { jurisdiction: 'EU', sector: 'ai' }. With `context` and NO `authority`/`artifact_id`/
85
+ * `artifact`, the server first resolves WHICH authorities govern this context (from its own
86
+ * signed registrar chain — never from anything you present), asks every one of them, and
87
+ * combines strictly. Returns a CanResolutionResult instead of a CanResult. Keyless. */
88
+ context?: Record<string, unknown>;
83
89
  /** Flat-facts convenience: any extra keys are treated as inputs when `inputs` is omitted. */
84
90
  [key: string]: unknown;
85
91
  }
92
+ /** One governing authority the resolver found for a context (NOMOS-SPEC-014). */
93
+ interface ResolvedAuthorityRef {
94
+ authority: string;
95
+ version: string | null;
96
+ registrar: string;
97
+ scope: string;
98
+ }
99
+ /** One resolved authority's answer inside a resolution-rail can() call. `verdict` is null with
100
+ * `unavailable` set when the member could not be consulted — which holds the WHOLE combined
101
+ * answer open (INSUFFICIENT_DATA), never acts as a permit. */
102
+ interface CanResolutionMember {
103
+ authority: string;
104
+ verdict: string | null;
105
+ unavailable: string | null;
106
+ query_id?: string | null;
107
+ rule?: string | null;
108
+ reason?: string | null;
109
+ escalation_id?: string | null;
110
+ revoked?: boolean;
111
+ missing_facts?: string[];
112
+ }
113
+ /** The resolution rail's answer (NOMOS-SPEC-014): which authorities govern, each one's own
114
+ * sealed answer, and the strict combination. `verdict` adds two values CanResult never has:
115
+ * INSUFFICIENT_DATA (resolution or a member held open — named in `open_facts`/`resolution`)
116
+ * and CONFLICT (two registrars assert exclusive precedence; a person settles it). */
117
+ interface CanResolutionResult {
118
+ mode: 'resolution';
119
+ allowed: boolean;
120
+ verdict: 'AUTHORIZED' | 'DENIED' | 'ESCALATED' | 'INSUFFICIENT_DATA' | 'CONFLICT';
121
+ reason: string;
122
+ resolution: {
123
+ status: 'RESOLVED' | 'CONFLICT' | 'INCOMPLETE';
124
+ applicable?: ResolvedAuthorityRef[];
125
+ missing?: {
126
+ context_facts: string[];
127
+ unreachable_registrars: string[];
128
+ stale_closures: string[];
129
+ /** SPEC-014 0.2.0: a current closure names law the registrar's log no longer claims, or the reverse */
130
+ closure_disagreements?: Array<{
131
+ registrar: string;
132
+ authority: string;
133
+ reason: 'closed_but_not_claimed' | 'claimed_but_not_closed';
134
+ }>;
135
+ /** SPEC-014 0.2.0: registrars whose log copy is older than the relying party's bound */
136
+ stale_heads?: string[];
137
+ };
138
+ conflicts?: unknown[];
139
+ proof: unknown;
140
+ };
141
+ members: CanResolutionMember[];
142
+ deciding_authorities: string[];
143
+ escalations: Array<{
144
+ authority: string;
145
+ escalation_id: string | null;
146
+ }>;
147
+ open_facts: string[];
148
+ /** The laws this answer was computed under (the resolved set). */
149
+ answered_under?: string[];
150
+ /** What the answer covers, in words: the registrar lists only its own authorities, so other law
151
+ * may also apply. An empty set is never AUTHORIZED. */
152
+ coverage?: string;
153
+ }
86
154
  interface CanOptions {
87
155
  /** Override the API host (defaults to the public NOMOS Exchange). */
88
156
  baseUrl?: string;
@@ -836,6 +904,12 @@ declare class Nomos {
836
904
  can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
837
905
  }
838
906
 
907
+ declare function can(params: CanParams & {
908
+ context: Record<string, unknown>;
909
+ authority?: undefined;
910
+ artifact_id?: undefined;
911
+ artifact?: undefined;
912
+ }, opts?: CanOptions): Promise<CanResolutionResult>;
839
913
  declare function can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
840
914
  /**
841
915
  * Ready-to-use, keyless entry point matching the canonical `nomos.can(...)` form.
@@ -1676,4 +1750,4 @@ declare function approver(o: ApproverOptions): {
1676
1750
  pending: Map<string, Pending>;
1677
1751
  };
1678
1752
 
1679
- export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
1753
+ export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResolutionMember, type CanResolutionResult, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type ResolvedAuthorityRef, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
package/dist/index.d.ts CHANGED
@@ -80,9 +80,77 @@ interface CanParams {
80
80
  /** The facts to evaluate. Either pass this explicitly, or spread the facts flat alongside
81
81
  * `authority`/`action` — both shapes are accepted (explicit `inputs` wins if both given). */
82
82
  inputs?: Record<string, unknown>;
83
+ /** Resolution rail (NOMOS-SPEC-014 Draft): the properties that attach law to an act —
84
+ * e.g. { jurisdiction: 'EU', sector: 'ai' }. With `context` and NO `authority`/`artifact_id`/
85
+ * `artifact`, the server first resolves WHICH authorities govern this context (from its own
86
+ * signed registrar chain — never from anything you present), asks every one of them, and
87
+ * combines strictly. Returns a CanResolutionResult instead of a CanResult. Keyless. */
88
+ context?: Record<string, unknown>;
83
89
  /** Flat-facts convenience: any extra keys are treated as inputs when `inputs` is omitted. */
84
90
  [key: string]: unknown;
85
91
  }
92
+ /** One governing authority the resolver found for a context (NOMOS-SPEC-014). */
93
+ interface ResolvedAuthorityRef {
94
+ authority: string;
95
+ version: string | null;
96
+ registrar: string;
97
+ scope: string;
98
+ }
99
+ /** One resolved authority's answer inside a resolution-rail can() call. `verdict` is null with
100
+ * `unavailable` set when the member could not be consulted — which holds the WHOLE combined
101
+ * answer open (INSUFFICIENT_DATA), never acts as a permit. */
102
+ interface CanResolutionMember {
103
+ authority: string;
104
+ verdict: string | null;
105
+ unavailable: string | null;
106
+ query_id?: string | null;
107
+ rule?: string | null;
108
+ reason?: string | null;
109
+ escalation_id?: string | null;
110
+ revoked?: boolean;
111
+ missing_facts?: string[];
112
+ }
113
+ /** The resolution rail's answer (NOMOS-SPEC-014): which authorities govern, each one's own
114
+ * sealed answer, and the strict combination. `verdict` adds two values CanResult never has:
115
+ * INSUFFICIENT_DATA (resolution or a member held open — named in `open_facts`/`resolution`)
116
+ * and CONFLICT (two registrars assert exclusive precedence; a person settles it). */
117
+ interface CanResolutionResult {
118
+ mode: 'resolution';
119
+ allowed: boolean;
120
+ verdict: 'AUTHORIZED' | 'DENIED' | 'ESCALATED' | 'INSUFFICIENT_DATA' | 'CONFLICT';
121
+ reason: string;
122
+ resolution: {
123
+ status: 'RESOLVED' | 'CONFLICT' | 'INCOMPLETE';
124
+ applicable?: ResolvedAuthorityRef[];
125
+ missing?: {
126
+ context_facts: string[];
127
+ unreachable_registrars: string[];
128
+ stale_closures: string[];
129
+ /** SPEC-014 0.2.0: a current closure names law the registrar's log no longer claims, or the reverse */
130
+ closure_disagreements?: Array<{
131
+ registrar: string;
132
+ authority: string;
133
+ reason: 'closed_but_not_claimed' | 'claimed_but_not_closed';
134
+ }>;
135
+ /** SPEC-014 0.2.0: registrars whose log copy is older than the relying party's bound */
136
+ stale_heads?: string[];
137
+ };
138
+ conflicts?: unknown[];
139
+ proof: unknown;
140
+ };
141
+ members: CanResolutionMember[];
142
+ deciding_authorities: string[];
143
+ escalations: Array<{
144
+ authority: string;
145
+ escalation_id: string | null;
146
+ }>;
147
+ open_facts: string[];
148
+ /** The laws this answer was computed under (the resolved set). */
149
+ answered_under?: string[];
150
+ /** What the answer covers, in words: the registrar lists only its own authorities, so other law
151
+ * may also apply. An empty set is never AUTHORIZED. */
152
+ coverage?: string;
153
+ }
86
154
  interface CanOptions {
87
155
  /** Override the API host (defaults to the public NOMOS Exchange). */
88
156
  baseUrl?: string;
@@ -836,6 +904,12 @@ declare class Nomos {
836
904
  can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
837
905
  }
838
906
 
907
+ declare function can(params: CanParams & {
908
+ context: Record<string, unknown>;
909
+ authority?: undefined;
910
+ artifact_id?: undefined;
911
+ artifact?: undefined;
912
+ }, opts?: CanOptions): Promise<CanResolutionResult>;
839
913
  declare function can(params: CanParams, opts?: CanOptions): Promise<CanResult>;
840
914
  /**
841
915
  * Ready-to-use, keyless entry point matching the canonical `nomos.can(...)` form.
@@ -1676,4 +1750,4 @@ declare function approver(o: ApproverOptions): {
1676
1750
  pending: Map<string, Pending>;
1677
1751
  };
1678
1752
 
1679
- export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
1753
+ export { ACT_MEDIA_TYPE, type AcceptOptions, type AcceptStore, type AcceptTransaction, type AcceptedAuthority, type KeyCertificate as ActKeyCertificate, type ActOptions, type ActResult, type ActStatus, type Action, type Answer, type ApproverOptions, type Artifact, type ArtifactList, type ArtifactSchema, type ArtifactSchemaField, type AttestationInfo, type AwaitApprovalOptions, type BindingProposal, type BindingSuggestion, type BuildActOptions, type CanApproval, type CanCaller, type CanOptions, type CanParams, type CanResolutionMember, type CanResolutionResult, type CanResult, type CanVerdict, type CertificateRegistry, type ChainVerdictDecision, type ConservedStateInfo, type ConservedStateReceipt, type Contradiction, type DetectContradictionsParams, type DetectContradictionsResult, type EnrolmentClaims, type FactBinding, type GenerateGovernanceParams, type GenerateGovernanceResult, type IssueOptions, type IssuerDocument, type IssuerOptions, type KeyCertificate$1 as KeyCertificate, type ListArtifactsParams, MAX_PRESENTED, Nomos, NomosAPIError, NomosActError, NomosAuthenticationError, NomosAuthorizationError, type NomosConfig, NomosDiscoveryError, NomosError, type NomosErrorCode, NomosIssuerNotTrustedError, NomosNetworkError, NomosNotFoundError, NomosPaymentRequiredError, NomosRateLimitError, type NomosRawResponse, NomosValidationError, type NomosWebhookEvent, type NomosWebhookEventType, NomosWebhookSignatureError, type PasskeyApproverOptions, type PasskeyCredential, type PasskeyStore, type PendingApproval, type PgClient, type PgPool, type PolicyField, type PostgresStoreOptions, type Receipt, RegistryError, type RegistryStore, type RelyingPartyDocument, type RequestOptions, type Reservation, type ReserveByActParams, type ReserveByQueryParams, type ReserveParams, type ResolvedAuthorityRef, type RevocationSourceOptions, RevocationUnavailable, type SignatureSuite, type Signer, type ToolBindings, type ToolDescription, type UnresolvedFact, type Verdict, type VerifyChainOptions, type VerifyChainParams, type VerifyChainResult, type VerifyDecisionParams, type VerifyDecisionResult, WELL_KNOWN, type WithLastResponse, type WitnessDocument, type WitnessOptions, accept, act, applyToolBindings, approver, buildAct, can, certificateRegistry, certify, describeSuggestion, fetchDocument, generateSigner, issuer, kidOf, localSigner, memoryPasskeyStore, memoryRegistryStore, memoryStore, nomos, originOf, parseToolBindings, passkeyApprover, postgresPasskeyStore, postgresRegistryStore, postgresStore, presentedCertificates, readAttestation, reliedKids, revocationListCertificates, revocationSource, seal, seededSigner, signConsent, signEnrolmentToken, signInForce, signRevocation, signRevocationList, signTestimony, statusFor, suggestToolBindings, verifyChain, verifyEnrolmentToken, witness };
package/dist/index.js CHANGED
@@ -107,7 +107,7 @@ function isRetryable(status) {
107
107
  }
108
108
 
109
109
  // src/http-client.ts
110
- var SDK_VERSION = "2.5.0";
110
+ var SDK_VERSION = "2.6.0";
111
111
  var HttpClient = class {
112
112
  constructor(opts) {
113
113
  this.opts = opts;
@@ -774,6 +774,50 @@ async function canViaChain(artifact, keyCerts, rootPublicKeyPem, facts, action,
774
774
  if (data?.decision === "MALFORMED") throw new NomosValidationError(message, data, requestId);
775
775
  mapHttpError(res.status, data, requestId);
776
776
  }
777
+ function normalizeResolutionResult(data) {
778
+ return {
779
+ mode: "resolution",
780
+ allowed: data.verdict === "AUTHORIZED",
781
+ verdict: data.verdict,
782
+ reason: data.reason ?? "",
783
+ resolution: data.resolution ?? { status: "INCOMPLETE", proof: null },
784
+ members: Array.isArray(data.members) ? data.members : [],
785
+ deciding_authorities: Array.isArray(data.deciding_authorities) ? data.deciding_authorities : [],
786
+ escalations: Array.isArray(data.escalations) ? data.escalations : [],
787
+ open_facts: Array.isArray(data.open_facts) ? data.open_facts : [],
788
+ ...Array.isArray(data.answered_under) ? { answered_under: data.answered_under } : {},
789
+ ...typeof data.coverage === "string" ? { coverage: data.coverage } : {}
790
+ };
791
+ }
792
+ function resolutionBody(params) {
793
+ const { authority, artifact_id, artifact, key_certs, root_public_key_pem, action, inputs, context, ...flat } = params;
794
+ void authority;
795
+ void artifact_id;
796
+ void artifact;
797
+ void key_certs;
798
+ void root_public_key_pem;
799
+ const facts = inputs && typeof inputs === "object" ? inputs : flat;
800
+ return { context, ...action ? { action } : {}, ...facts };
801
+ }
802
+ async function canViaResolution(params, opts) {
803
+ const base = (opts?.baseUrl ?? DEFAULT_EXCHANGE_BASE).replace(/\/$/, "");
804
+ let res;
805
+ try {
806
+ res = await fetch(`${base}/api/v1/can`, {
807
+ method: "POST",
808
+ headers: { "Content-Type": "application/json", "User-Agent": `nomos-node/${SDK_VERSION}`, ...callerHeaders(opts?.caller) },
809
+ body: JSON.stringify(resolutionBody(params)),
810
+ signal: opts?.signal
811
+ });
812
+ } catch (err) {
813
+ if (err?.name === "AbortError") throw new NomosNetworkError("Request aborted");
814
+ throw new NomosNetworkError(err?.message ?? "Network error");
815
+ }
816
+ const requestId = res.headers.get("x-request-id") ?? "";
817
+ const data = await res.json().catch(() => null);
818
+ if (!res.ok) mapHttpError(res.status, data, requestId);
819
+ return normalizeResolutionResult(data);
820
+ }
777
821
  async function can(params, opts) {
778
822
  if (params.artifact !== void 0) {
779
823
  if (!params.key_certs || !params.root_public_key_pem) {
@@ -785,8 +829,11 @@ async function can(params, opts) {
785
829
  if (params.artifact_id) {
786
830
  throw new Error("can(): `artifact_id` requires an API key \u2014 use `client.can(...)` on an authenticated Nomos instance, not the free `can()` export.");
787
831
  }
832
+ if (!params.authority && params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
833
+ return canViaResolution(params, opts);
834
+ }
788
835
  if (!params.authority || typeof params.authority !== "string") {
789
- throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()), or `artifact` (chain-of-trust) is required.");
836
+ throw new Error("can(): `authority` (an Exchange artifact id or slug), `artifact_id` (via client.can()), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
790
837
  }
791
838
  const { facts, action } = splitFacts(params);
792
839
  return canViaExchange(params.authority, facts, action, opts);
@@ -801,7 +848,11 @@ async function canAuthenticated(http, params, opts) {
801
848
  }
802
849
  if (params.artifact_id) return canViaArtifact(http, params.artifact_id, facts, action);
803
850
  if (params.authority) return canViaExchangeAuthenticated(http, params.authority, facts, action, opts);
804
- throw new Error("can(): `authority` (a public Exchange artifact), `artifact_id` (your own artifact), or `artifact` (chain-of-trust) is required.");
851
+ if (params.context && typeof params.context === "object" && !Array.isArray(params.context)) {
852
+ const data = (await http.post("/api/v1/can", resolutionBody(params), { headers: callerHeaders(opts?.caller) })).data;
853
+ return normalizeResolutionResult(data);
854
+ }
855
+ throw new Error("can(): `authority` (a public Exchange artifact), `artifact_id` (your own artifact), `artifact` (chain-of-trust), or `context` (resolution, NOMOS-SPEC-014) is required.");
805
856
  }
806
857
  var nomos = { can };
807
858
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomosprotocol/sdk",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "NOMOS for TypeScript: accept() for the system that makes a change, act() for the agent that asks, the issuer, witness and approver roles, and the Exchange client. Verification is local (@nomosprotocol/verify).",
5
5
  "publishConfig": {
6
6
  "access": "public"