@kya-brasil/agent-tools 0.5.3 → 0.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
@@ -19,7 +19,7 @@ Node 18+ (global `fetch`, WebCrypto). ESM.
19
19
  ## Verifier: you receive an action from someone else's agent
20
20
 
21
21
  ```ts
22
- import { createKyaClient, verifyAttestation, verifyCredentialOffline, companyText, signWithECnpj } from "@kya-brasil/agent-tools";
22
+ import { createKyaClient, verifyAttestation, verifyCredentialOffline, companyText, signWithECnpj, hashPaymentRef } from "@kya-brasil/agent-tools";
23
23
 
24
24
  const kya = createKyaClient({ privateKey: process.env.KYA_PAYER_PK as `0x${string}` }); // wallet optional
25
25
 
@@ -34,6 +34,8 @@ if (!verdict.allowed) reject(verdict.reason);
34
34
  await verifyAttestation(verdict); // local Ed25519 check of the issuer's signature
35
35
 
36
36
  // max_per_day: pass your own spent_today; the issuer does not record acts.
37
+ // Payment instrument (card networks, acquirers, D-25): pass the hash of the token reference or PAR you received,
38
+ // never the card number: { payment_network: "visa", payment_ref_sha256: await hashPaymentRef(tokenRef) }
37
39
 
38
40
  const offline = await verifyCredentialOffline(credential); // issuer down: signature + expires_at only,
39
41
  // live_status "unknown" (your risk until expires_at)
package/dist/auth.d.ts CHANGED
@@ -80,8 +80,8 @@ export interface LocalVerdict {
80
80
  }
81
81
  /**
82
82
  * Local evaluation of the reserved mandate fields: actions, max_per_operation, currencies, counterparties
83
- * (with `*.` wildcards), valid_from / valid_until. `max_per_day` is listed as not evaluated: only the issuer
84
- * knows nothing of it either: pass your own spent_today to ctx.allowed.
83
+ * (with `*.` wildcards), payment_instruments (when you pass payment_ref_sha256), valid_from / valid_until, and
84
+ * max_per_day when you pass spent_today (nobody else counts it: the issuer keeps no record of acts).
85
85
  */
86
86
  export declare function checkMandate(mandate: Mandate, q: ActionQuery, now?: Date): LocalVerdict;
87
87
  export interface KyaAuth {
package/dist/auth.js CHANGED
@@ -73,7 +73,7 @@ export async function signRequest(p) {
73
73
  headers[KYA_HEADERS.content] = contentSha256;
74
74
  return headers;
75
75
  }
76
- const RESERVED = ["actions", "max_per_operation", "max_per_day", "currencies", "counterparties", "valid_from", "valid_until"];
76
+ const RESERVED = ["actions", "max_per_operation", "max_per_day", "currencies", "counterparties", "payment_instruments", "valid_from", "valid_until"];
77
77
  function normalizeCounterparty(c) {
78
78
  const t = c.trim();
79
79
  const digits = t.replace(/\D/g, "");
@@ -88,8 +88,8 @@ function normalizeCounterparty(c) {
88
88
  }
89
89
  /**
90
90
  * Local evaluation of the reserved mandate fields: actions, max_per_operation, currencies, counterparties
91
- * (with `*.` wildcards), valid_from / valid_until. `max_per_day` is listed as not evaluated: only the issuer
92
- * knows nothing of it either: pass your own spent_today to ctx.allowed.
91
+ * (with `*.` wildcards), payment_instruments (when you pass payment_ref_sha256), valid_from / valid_until, and
92
+ * max_per_day when you pass spent_today (nobody else counts it: the issuer keeps no record of acts).
93
93
  */
94
94
  export function checkMandate(mandate, q, now = new Date()) {
95
95
  const checks = [];
@@ -116,9 +116,21 @@ export function checkMandate(mandate, q, now = new Date()) {
116
116
  });
117
117
  push("counterparties", ok, `${c} ${ok ? "is" : "is not"} an allowed counterparty`);
118
118
  }
119
+ if (mandate.max_per_day && q.amount !== undefined && q.spent_today !== undefined) {
120
+ const total = q.amount + q.spent_today;
121
+ const same = !q.currency || q.currency.toUpperCase() === mandate.max_per_day.currency;
122
+ push("max_per_day", same && total <= mandate.max_per_day.amount, same ? `${total} today (incl. this one) vs ceiling ${mandate.max_per_day.amount} ${mandate.max_per_day.currency}` : `ceiling is in ${mandate.max_per_day.currency}, operation in ${q.currency}`);
123
+ }
124
+ const instruments = Array.isArray(mandate.payment_instruments) ? mandate.payment_instruments : [];
125
+ if (instruments.length && q.payment_ref_sha256) {
126
+ const h = q.payment_ref_sha256.trim().toLowerCase();
127
+ const net = q.payment_network?.trim().toLowerCase();
128
+ const hit = instruments.find((p) => p.ref_sha256 === h && (!net || p.network === net));
129
+ push("payment_instruments", !!hit, hit ? `${hit.network} instrument ${h.slice(0, 12)}… is authorized` : `instrument ${net ? `${net} ` : ""}${h.slice(0, 12)}… is not in the mandate`);
130
+ }
119
131
  const not_evaluated = Object.keys(mandate).filter((k) => !RESERVED.includes(k));
120
- if (mandate.max_per_day)
121
- not_evaluated.push("max_per_day (only the issuer knows today's registered total)");
132
+ if (mandate.max_per_day && q.spent_today === undefined)
133
+ not_evaluated.push("max_per_day (pass spent_today: the issuer keeps no record of acts)");
122
134
  return { allowed: checks.every((c) => c.ok), checks, not_evaluated };
123
135
  }
124
136
  const headerOf = (headers, name) => {
package/dist/index.d.ts CHANGED
@@ -27,6 +27,12 @@ export interface Mandate {
27
27
  };
28
28
  currencies?: string[];
29
29
  counterparties?: string[];
30
+ /** D-25: instruments the agent may use, as SHA-256 of the network's token reference or PAR (see hashPaymentRef). */
31
+ payment_instruments?: {
32
+ network: string;
33
+ ref_sha256: string;
34
+ label?: string;
35
+ }[];
30
36
  valid_from?: string;
31
37
  valid_until?: string;
32
38
  [k: string]: unknown;
@@ -93,7 +99,16 @@ export interface ActionQuery {
93
99
  currency?: string;
94
100
  counterparty?: string;
95
101
  spent_today?: number;
102
+ /** D-25: network of the payment instrument used (visa, mastercard, elo, pix, ...). */
103
+ payment_network?: string;
104
+ /** D-25: SHA-256 hex of its token reference or PAR (hashPaymentRef); never a card number. */
105
+ payment_ref_sha256?: string;
96
106
  }
107
+ /**
108
+ * SHA-256 (lowercase hex) of a payment reference as mandates store it (D-25): UTF-8, whitespace removed, case
109
+ * kept. Use it on a network token reference or PAR, never on a card number.
110
+ */
111
+ export declare function hashPaymentRef(ref: string): Promise<string>;
97
112
  export interface Verdict {
98
113
  credential_id: string;
99
114
  status: CredentialStatus;
@@ -185,7 +200,8 @@ export interface KyaClient {
185
200
  }): Promise<Registration>;
186
201
  /** Free: send both signatures; the credential is issued if the e-CNPJ is valid and not revoked, the CNPJ matches and the company is ATIVA. */
187
202
  prove(credentialId: string, proof: Proof): Promise<{
188
- credential: Credential; /** /v1 compatibility, always empty. */
203
+ credential: Credential; /** D-24: e.g. registration status not confirmed (issued anyway). */
204
+ warnings?: string[]; /** /v1 compatibility, same as warnings. */
189
205
  kyb_warnings: string[];
190
206
  }>;
191
207
  /** $0.10: renewal challenge (then renewProve). */
@@ -198,7 +214,8 @@ export interface KyaClient {
198
214
  };
199
215
  }>;
200
216
  renewProve(credentialId: string, proof: Proof): Promise<{
201
- credential: Credential; /** /v1 compatibility, always empty. */
217
+ credential: Credential; /** D-24: e.g. registration status not confirmed (issued anyway). */
218
+ warnings?: string[]; /** /v1 compatibility, same as warnings. */
202
219
  kyb_warnings: string[];
203
220
  }>;
204
221
  /** Free: the company revokes at once. signed_text must be "KYA Brasil: revoke <id>\n<nonce>", signed with the e-CNPJ. */
package/dist/index.js CHANGED
@@ -23,6 +23,17 @@ import { privateKeyToAccount } from "viem/accounts";
23
23
  import * as pkijs from "pkijs";
24
24
  import { toBase64Url } from "./base64url.js";
25
25
  export const DEFAULT_BASE_URL = "https://kya-br-agents.marcelfons.workers.dev";
26
+ /**
27
+ * SHA-256 (lowercase hex) of a payment reference as mandates store it (D-25): UTF-8, whitespace removed, case
28
+ * kept. Use it on a network token reference or PAR, never on a card number.
29
+ */
30
+ export async function hashPaymentRef(ref) {
31
+ const clean = ref.replace(/\s+/g, "");
32
+ if (!clean)
33
+ throw new Error("empty payment reference");
34
+ const d = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(clean)));
35
+ return Array.from(d, (b) => b.toString(16).padStart(2, "0")).join("");
36
+ }
26
37
  export class KyaError extends Error {
27
38
  code;
28
39
  status;
@@ -102,8 +113,8 @@ export function createKyaClient(opts = {}) {
102
113
  {
103
114
  name: "kya_check_action", priceUsd: 0.01,
104
115
  description: "Signed verdict ($0.01): may this agent perform this action (action, amount, currency, counterparty) under its company mandate, and is the credential active right now? Use before accepting an order, a payment or a signature from an agent.",
105
- parameters: { type: "object", properties: { credential_id: ID, action: { type: "string" }, amount: { type: "number" }, currency: { type: "string" }, counterparty: { type: "string", description: "Your CNPJ, domain or wallet address" }, spent_today: { type: "number" } }, required: ["credential_id"], additionalProperties: false },
106
- execute: async (a) => client.allowed(String(a.credential_id), { action: a.action, amount: a.amount, currency: a.currency, counterparty: a.counterparty, spent_today: a.spent_today })
116
+ parameters: { type: "object", properties: { credential_id: ID, action: { type: "string" }, amount: { type: "number" }, currency: { type: "string" }, counterparty: { type: "string", description: "Your CNPJ, domain or wallet address" }, spent_today: { type: "number" }, payment_network: { type: "string", description: "visa, mastercard, elo, pix, ..." }, payment_ref_sha256: { type: "string", description: "SHA-256 hex of the payment token reference or PAR used (never a card number)" } }, required: ["credential_id"], additionalProperties: false },
117
+ execute: async (a) => client.allowed(String(a.credential_id), { action: a.action, amount: a.amount, currency: a.currency, counterparty: a.counterparty, spent_today: a.spent_today, payment_network: a.payment_network, payment_ref_sha256: a.payment_ref_sha256 })
107
118
  },
108
119
  {
109
120
  name: "kya_verify_paid", priceUsd: 0.01,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kya-brasil/agent-tools",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "KYA Brasil for AI agents: verify that an agent acts for a Brazilian company (e-CNPJ-signed credential, revocation and company registration checked daily against the Receita's open data, no third-party service), check an action against its mandate, or give your own agent a credential. Sign in with KYA middleware (Hono, Express, Workers) and request signing. x402 payments (USDC on Base) handled for you. Adapters for Vercel AI SDK, LangChain, OpenAI and Anthropic.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -58,7 +58,7 @@
58
58
  "viem": "^2.57.5"
59
59
  },
60
60
  "devDependencies": {
61
- "@kya-br/core": "workspace:*",
61
+ "@kya-brasil/core": "workspace:*",
62
62
  "@peculiar/asn1-schema": "^2.3.15",
63
63
  "@peculiar/asn1-x509": "^2.3.15",
64
64
  "@peculiar/x509": "^2.1.0",