@kya-brasil/agent-tools 0.10.0 → 0.11.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
@@ -100,6 +100,23 @@ const { response, callee } = await me.fetch("https://beta.com.br/orders", { meth
100
100
  if (!callee?.ok) throw new Error("unsigned or invalid callee"); // callee.kya: principal, mandate, status
101
101
  ```
102
102
 
103
+ ### Sub-agents (delegation, D-30): one certificate signature for the whole tree
104
+
105
+ When the mandate carries `delegation: { max_depth, max_ttl_seconds }`, the agent hands a narrower mandate to
106
+ another key with its own signature, no new e-CNPJ or e-CPF signature. The sub-agent signs its requests with its own
107
+ key and sends the chain; your middleware verifies it offline and `check()` / `allowed()` evaluate every mandate in it.
108
+
109
+ ```ts
110
+ import { signDelegation, signRequest } from "@kya-brasil/agent-tools";
111
+ const chain = await signDelegation({ credential, key: agentKey, delegate: { kind: "web-bot-auth", id: subPublicKey },
112
+ mandate: { actions: ["quote"], max_per_operation: { amount: 500, currency: "BRL" } }, ttlSeconds: 600 });
113
+ const headers = await signRequest({ credentialId, key: subKey, delegation: chain, method: "GET", url });
114
+ // server side: c.get("kya").delegation → { depth, delegate, mandates, expires_at, chain }
115
+ ```
116
+
117
+ Short-lived (at most `max_ttl_seconds`, never past the credential), contained in the parent mandate, at most 3
118
+ levels; revoking the credential ends the whole tree. The issuer stores nothing. [SPEC](https://github.com/celfons/kya-brasil/blob/main/docs/SPEC.md) section 17.
119
+
103
120
  ## Person: an agent that acts for you (e-CPF)
104
121
 
105
122
  ```ts
package/dist/auth.d.ts CHANGED
@@ -1,11 +1,14 @@
1
- import { type ActionQuery, type AgentKey, type CredentialStatus, type KyaClient, type Mandate, type Verdict, type Verification, type KyaOptions } from "./index.js";
1
+ import { type Delegation, type DelegationAnchor } from "@kya-brasil/core";
2
+ import { type ActionQuery, type AgentKey, type Credential, type CredentialStatus, type KyaClient, type Mandate, type Verdict, type Verification, type KyaOptions } from "./index.js";
2
3
  export declare const KYA_HEADERS: {
3
4
  readonly credential: "kya-credential";
4
5
  readonly created: "kya-created";
5
6
  readonly expires: "kya-expires";
6
7
  readonly signature: "kya-signature";
7
8
  readonly content: "kya-content-sha256";
9
+ readonly delegation: "kya-delegation";
8
10
  };
11
+ export type { Delegation } from "@kya-brasil/core";
9
12
  export declare function sha256Hex(data: string | ArrayBuffer | Uint8Array): Promise<string>;
10
13
  /** Canonical URL for the signature: origin (lowercase host) + path + query, no fragment. */
11
14
  export declare function canonicalUrl(url: string): string;
@@ -16,6 +19,7 @@ export declare function requestText(p: {
16
19
  created: number;
17
20
  expires: number;
18
21
  contentSha256?: string | null;
22
+ delegationSha256?: string | null;
19
23
  }): string;
20
24
  export type SigningKey = {
21
25
  kind: "evm" | "erc8004";
@@ -24,7 +28,10 @@ export type SigningKey = {
24
28
  kind: "web-bot-auth";
25
29
  privateKey: CryptoKey | string;
26
30
  };
27
- /** Signs a request with the agent key and returns the KYA headers to send with it. */
31
+ /**
32
+ * Signs a request with the agent key and returns the KYA headers to send with it. A sub-agent passes its
33
+ * `delegation` chain (from signDelegation) and signs with its own key.
34
+ */
28
35
  export declare function signRequest(p: {
29
36
  credentialId: string;
30
37
  key: SigningKey;
@@ -33,7 +40,28 @@ export declare function signRequest(p: {
33
40
  body?: string | ArrayBuffer | Uint8Array | null;
34
41
  ttlSeconds?: number;
35
42
  now?: Date;
43
+ delegation?: Delegation[];
36
44
  }): Promise<Record<string, string>>;
45
+ /** The key a signing key verifies as, in the form a credential or a delegation names it. */
46
+ export declare function agentKeyOf(key: SigningKey): Promise<AgentKey>;
47
+ /**
48
+ * Delegation to a sub-agent (D-30): hands `delegate` a mandate contained in the delegator's, for `ttlSeconds`
49
+ * (at most the mandate's `delegation.max_ttl_seconds`). `key` is the credential's agent key, or, to delegate
50
+ * again, the key of the last delegate in `chain`. Returns the new chain; the sub-agent sends it with
51
+ * signRequest({ delegation }). Nothing goes to the issuer.
52
+ */
53
+ export declare function signDelegation(p: {
54
+ credential: DelegationAnchor | Credential;
55
+ key: SigningKey;
56
+ delegate: {
57
+ kind: "evm" | "web-bot-auth";
58
+ id: string;
59
+ };
60
+ mandate: Mandate;
61
+ ttlSeconds: number;
62
+ chain?: Delegation[];
63
+ now?: Date;
64
+ }): Promise<Delegation[]>;
37
65
  export interface KyaAuthOptions extends KyaOptions {
38
66
  /** How long a fetched credential is reused before asking the issuer again (ms). Default 60 000. */
39
67
  cacheTtlMs?: number;
@@ -51,13 +79,28 @@ export interface KyaContext {
51
79
  status: CredentialStatus;
52
80
  mandate: Mandate;
53
81
  credential: Verification["credential"];
54
- /** Which key verified the request signature. */
82
+ /** Which key verified the request signature (the sub-agent's key on a delegated request). */
55
83
  signer: string;
84
+ /**
85
+ * D-30: present when a sub-agent signed the request under a delegation chain. `agent` above stays the
86
+ * credential's agent; `delegate` is the sub-agent; every mandate in `mandates` must allow an action.
87
+ */
88
+ delegation: {
89
+ depth: number;
90
+ delegate: AgentKey;
91
+ expires_at: string;
92
+ chain: Delegation[];
93
+ mandates: Mandate[];
94
+ } | null;
56
95
  /** Whether the body hash was checked ("verified"), absent from the signature ("none") or not checkable ("skipped"). */
57
96
  body: "verified" | "none" | "skipped";
58
- /** Local evaluation of the mandate (no daily ceilings; those need the issuer). */
97
+ /** Local evaluation of the mandate (no daily ceilings; those need the issuer). On a delegated request, of every mandate in the chain. */
59
98
  check(q: ActionQuery): LocalVerdict;
60
- /** Signed verdict from the issuer ($0.01, needs privateKey or internalKey in the options). */
99
+ /**
100
+ * Signed verdict from the issuer ($0.01, needs privateKey or internalKey in the options). The issuer evaluates
101
+ * the credential's mandate; on a delegated request the delegated mandates are evaluated here first, and when
102
+ * one refuses the answer is that local refusal (unsigned, `delegation_level` set, the issuer is not called).
103
+ */
61
104
  allowed(q: ActionQuery): Promise<Verdict>;
62
105
  }
63
106
  export type KyaAuthResult = {
@@ -84,6 +127,10 @@ export interface LocalVerdict {
84
127
  * max_per_day when you pass spent_today (nobody else counts it: the issuer keeps no record of acts).
85
128
  */
86
129
  export declare function checkMandate(mandate: Mandate, q: ActionQuery, now?: Date): LocalVerdict;
130
+ /** checkMandate over every mandate of a delegation chain (credential first): allowed only when all allow it. */
131
+ export declare function checkMandates(mandates: Mandate[], q: ActionQuery, now?: Date): LocalVerdict & {
132
+ levels: LocalVerdict[];
133
+ };
87
134
  export interface KyaAuth {
88
135
  client: KyaClient;
89
136
  /** Authenticate any Web-standard Request. Never throws; read `ok`. */
package/dist/auth.js CHANGED
@@ -16,9 +16,14 @@
16
16
  * const headers = await signRequest({ credentialId, key: { kind: "evm", privateKey }, method: "POST", url, body });
17
17
  * await fetch(url, { method: "POST", headers: { ...headers, "content-type": "application/json" }, body });
18
18
  *
19
- * Request text (what the agent signs; the profile is in docs/SPEC.md section 15):
19
+ * Sub-agent (D-30, the credential's mandate allows delegation):
20
+ * const chain = await signDelegation({ credential, key: parentKey, delegate: { kind: "evm", id: sub }, mandate: { actions: ["quote"] }, ttlSeconds: 600 });
21
+ * const headers = await signRequest({ credentialId, key: subKey, delegation: chain, method: "GET", url });
22
+ *
23
+ * Request text (what the agent signs; the profile is in docs/SPEC.md section 14):
20
24
  * KYA Brasil: request
21
25
  * credential: <credential_id>
26
+ * delegation: <sha256 hex of the KYA-Delegation header> (only on delegated requests)
22
27
  * method: <METHOD>
23
28
  * url: <origin + path + query, lowercase host>
24
29
  * created: <unix seconds>
@@ -26,10 +31,11 @@
26
31
  * content-sha256: <hex of the body | ->
27
32
  */
28
33
  import { verifyMessage } from "viem";
34
+ import { encodeDelegationHeader, decodeDelegationHeader, prepareDelegation, verifyDelegationChain } from "@kya-brasil/core";
29
35
  import { privateKeyToAccount } from "viem/accounts";
30
36
  import { createKyaClient } from "./index.js";
31
37
  import { fromBase64Url, toBase64Url } from "./base64url.js";
32
- export const KYA_HEADERS = { credential: "kya-credential", created: "kya-created", expires: "kya-expires", signature: "kya-signature", content: "kya-content-sha256" };
38
+ export const KYA_HEADERS = { credential: "kya-credential", created: "kya-created", expires: "kya-expires", signature: "kya-signature", content: "kya-content-sha256", delegation: "kya-delegation" };
33
39
  const enc = new TextEncoder();
34
40
  const hex = (buf) => [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
35
41
  const b64u = toBase64Url;
@@ -47,6 +53,7 @@ export function requestText(p) {
47
53
  return [
48
54
  "KYA Brasil: request",
49
55
  `credential: ${p.credentialId}`,
56
+ ...(p.delegationSha256 ? [`delegation: ${p.delegationSha256}`] : []),
50
57
  `method: ${p.method.toUpperCase()}`,
51
58
  `url: ${canonicalUrl(p.url)}`,
52
59
  `created: ${p.created}`,
@@ -54,26 +61,57 @@ export function requestText(p) {
54
61
  `content-sha256: ${p.contentSha256 ?? "-"}`
55
62
  ].join("\n");
56
63
  }
57
- /** Signs a request with the agent key and returns the KYA headers to send with it. */
64
+ // --- Agent side -------------------------------------------------------------------------------
65
+ const importPkcs8 = (pem, extractable = false) => crypto.subtle.importKey("pkcs8", unb64u(pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, "")), { name: "Ed25519" }, extractable, ["sign"]);
66
+ async function signText(key, text) {
67
+ if (key.kind === "web-bot-auth") {
68
+ const k = typeof key.privateKey === "string" ? await importPkcs8(key.privateKey) : key.privateKey;
69
+ return b64u(new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, k, enc.encode(text))));
70
+ }
71
+ return privateKeyToAccount(key.privateKey).signMessage({ message: text });
72
+ }
73
+ /**
74
+ * Signs a request with the agent key and returns the KYA headers to send with it. A sub-agent passes its
75
+ * `delegation` chain (from signDelegation) and signs with its own key.
76
+ */
58
77
  export async function signRequest(p) {
59
78
  const created = Math.floor((p.now ?? new Date()).getTime() / 1000);
60
79
  const expires = created + (p.ttlSeconds ?? 60);
61
80
  const contentSha256 = p.body !== undefined && p.body !== null && (typeof p.body !== "string" || p.body.length > 0) ? await sha256Hex(p.body) : null;
62
- const text = requestText({ credentialId: p.credentialId, method: p.method, url: p.url, created, expires, contentSha256 });
63
- let signature;
64
- if (p.key.kind === "web-bot-auth") {
65
- const key = typeof p.key.privateKey === "string" ? await crypto.subtle.importKey("pkcs8", unb64u(p.key.privateKey.replace(/-----[^-]+-----/g, "").replace(/\s+/g, "")), { name: "Ed25519" }, false, ["sign"]) : p.key.privateKey;
66
- signature = b64u(new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, key, enc.encode(text))));
67
- }
68
- else {
69
- signature = await privateKeyToAccount(p.key.privateKey).signMessage({ message: text });
70
- }
81
+ const delegationHeader = p.delegation && p.delegation.length ? encodeDelegationHeader(p.delegation) : null;
82
+ const delegationSha256 = delegationHeader ? await sha256Hex(delegationHeader) : null;
83
+ const text = requestText({ credentialId: p.credentialId, method: p.method, url: p.url, created, expires, contentSha256, delegationSha256 });
84
+ const signature = await signText(p.key, text);
71
85
  const headers = { [KYA_HEADERS.credential]: p.credentialId, [KYA_HEADERS.created]: String(created), [KYA_HEADERS.expires]: String(expires), [KYA_HEADERS.signature]: signature };
72
86
  if (contentSha256)
73
87
  headers[KYA_HEADERS.content] = contentSha256;
88
+ if (delegationHeader)
89
+ headers[KYA_HEADERS.delegation] = delegationHeader;
74
90
  return headers;
75
91
  }
76
- const RESERVED = ["actions", "max_per_operation", "max_per_day", "currencies", "counterparties", "payment_instruments", "valid_from", "valid_until"];
92
+ /** The key a signing key verifies as, in the form a credential or a delegation names it. */
93
+ export async function agentKeyOf(key) {
94
+ if (key.kind !== "web-bot-auth")
95
+ return { kind: "evm", id: privateKeyToAccount(key.privateKey).address };
96
+ const k = typeof key.privateKey === "string" ? await importPkcs8(key.privateKey, true) : key.privateKey;
97
+ const jwk = (await crypto.subtle.exportKey("jwk", k));
98
+ if (!jwk.x)
99
+ throw new Error("cannot derive the Ed25519 public key from this private key");
100
+ return { kind: "web-bot-auth", id: jwk.x };
101
+ }
102
+ /**
103
+ * Delegation to a sub-agent (D-30): hands `delegate` a mandate contained in the delegator's, for `ttlSeconds`
104
+ * (at most the mandate's `delegation.max_ttl_seconds`). `key` is the credential's agent key, or, to delegate
105
+ * again, the key of the last delegate in `chain`. Returns the new chain; the sub-agent sends it with
106
+ * signRequest({ delegation }). Nothing goes to the issuer.
107
+ */
108
+ export async function signDelegation(p) {
109
+ const chain = p.chain ?? [];
110
+ const { delegation, text } = await prepareDelegation({ credential: p.credential, chain, delegate: p.delegate, mandate: p.mandate, ttlSeconds: p.ttlSeconds, now: p.now });
111
+ const signature = await signText(p.key, text);
112
+ return [...chain, { ...delegation, signature }];
113
+ }
114
+ const RESERVED = ["actions", "max_per_operation", "max_per_day", "currencies", "counterparties", "payment_instruments", "valid_from", "valid_until", "delegation"];
77
115
  function normalizeCounterparty(c) {
78
116
  const t = c.trim();
79
117
  const digits = t.replace(/\D/g, "");
@@ -133,6 +171,17 @@ export function checkMandate(mandate, q, now = new Date()) {
133
171
  not_evaluated.push("max_per_day (pass spent_today: the issuer keeps no record of acts)");
134
172
  return { allowed: checks.every((c) => c.ok), checks, not_evaluated };
135
173
  }
174
+ /** checkMandate over every mandate of a delegation chain (credential first): allowed only when all allow it. */
175
+ export function checkMandates(mandates, q, now = new Date()) {
176
+ const levels = mandates.map((m) => checkMandate(m, q, now));
177
+ const prefix = (i, s) => (i === 0 ? s : `delegation ${i}: ${s}`);
178
+ return {
179
+ allowed: levels.length > 0 && levels.every((l) => l.allowed),
180
+ checks: levels.flatMap((l, i) => l.checks.map((c) => ({ ...c, rule: prefix(i, c.rule) }))),
181
+ not_evaluated: [...new Set(levels.flatMap((l, i) => l.not_evaluated.map((n) => prefix(i, n))))],
182
+ levels
183
+ };
184
+ }
136
185
  const headerOf = (headers, name) => {
137
186
  if (headers instanceof Headers)
138
187
  return headers.get(name);
@@ -177,6 +226,7 @@ export function createKyaAuth(opts = {}) {
177
226
  const expires = Number(headerOf(p.headers, KYA_HEADERS.expires));
178
227
  const signature = headerOf(p.headers, KYA_HEADERS.signature);
179
228
  const contentHeader = headerOf(p.headers, KYA_HEADERS.content);
229
+ const delegationHeader = headerOf(p.headers, KYA_HEADERS.delegation);
180
230
  if (!credentialId || !signature || !Number.isFinite(created) || !Number.isFinite(expires))
181
231
  return fail(401, "KYA_HEADERS_MISSING", `send ${KYA_HEADERS.credential}, ${KYA_HEADERS.created}, ${KYA_HEADERS.expires} and ${KYA_HEADERS.signature}`);
182
232
  const now = Math.floor(Date.now() / 1000);
@@ -208,16 +258,43 @@ export function createKyaAuth(opts = {}) {
208
258
  const status = e.status;
209
259
  return status === 404 ? fail(401, "KYA_CREDENTIAL_UNKNOWN", "unknown credential_id") : fail(502, "KYA_ISSUER_UNAVAILABLE", e.message);
210
260
  }
211
- const text = requestText({ credentialId, method: p.method, url: p.url, created, expires, contentSha256: contentHeader });
212
- return finish(credentialId, v, text, signature, bodyState);
261
+ let delegation = null;
262
+ if (delegationHeader) {
263
+ const d = await delegationOf(credentialId, v, delegationHeader);
264
+ if (!d.ok)
265
+ return d.result;
266
+ delegation = d.delegation;
267
+ }
268
+ const delegationSha256 = delegationHeader ? await sha256Hex(delegationHeader) : null;
269
+ const text = requestText({ credentialId, method: p.method, url: p.url, created, expires, contentSha256: contentHeader, delegationSha256 });
270
+ return finish(credentialId, v, text, signature, bodyState, delegation);
271
+ }
272
+ /** D-30: verifies a KYA-Delegation chain against the credential (offline, core rules). */
273
+ async function delegationOf(credentialId, v, header) {
274
+ const no = (status, error, message) => ({ ok: false, result: fail(status, error, message) });
275
+ if (!v.credential)
276
+ return no(403, "KYA_CREDENTIAL_NOT_ACTIVE", `credential is ${v.status}`);
277
+ let chain;
278
+ try {
279
+ chain = decodeDelegationHeader(header);
280
+ }
281
+ catch (e) {
282
+ return no(401, "KYA_DELEGATION_MALFORMED", e.message);
283
+ }
284
+ const anchor = { credential_id: credentialId, agent: v.agent, mandate: v.credential.mandate, expires_at: v.credential.expires_at };
285
+ const r = await verifyDelegationChain({ credential: anchor, chain });
286
+ if (!r.ok)
287
+ return no(r.error === "KYA_DELEGATION_NOT_ALLOWED" || r.error === "KYA_DELEGATION_BROADER" ? 403 : 401, r.error, r.message);
288
+ return { ok: true, delegation: { depth: r.depth, delegate: r.delegate, expires_at: r.expires_at, chain: chain, mandates: r.mandates } };
213
289
  }
214
- async function finish(credentialId, v, text, signature, bodyState) {
215
- const sig = await verifySignature(v.agent, text, signature);
290
+ async function finish(credentialId, v, text, signature, bodyState, delegation = null) {
291
+ const sig = await verifySignature(delegation ? delegation.delegate : v.agent, text, signature);
216
292
  if (!sig.ok)
217
- return fail(401, "KYA_BAD_SIGNATURE", "signature does not match the credential's agent key over the signed text");
293
+ return fail(401, "KYA_BAD_SIGNATURE", delegation ? "signature does not match the delegate's key over the signed text" : "signature does not match the credential's agent key over the signed text");
218
294
  if (v.status !== "active")
219
295
  return fail(403, "KYA_CREDENTIAL_NOT_ACTIVE", `credential is ${v.status}${v.status_reason ? `: ${v.status_reason}` : ""}`);
220
296
  const mandate = v.credential?.mandate ?? {};
297
+ const mandates = delegation ? delegation.mandates : [mandate];
221
298
  const ctx = {
222
299
  credential_id: credentialId,
223
300
  agent: v.agent,
@@ -227,8 +304,19 @@ export function createKyaAuth(opts = {}) {
227
304
  credential: v.credential,
228
305
  signer: sig.signer,
229
306
  body: bodyState,
230
- check: (q) => checkMandate(mandate, q),
231
- allowed: (q) => client.allowed(credentialId, q)
307
+ delegation,
308
+ check: (q) => (delegation ? checkMandates(mandates, q) : checkMandate(mandate, q)),
309
+ allowed: async (q) => {
310
+ if (delegation) {
311
+ for (let i = 1; i < mandates.length; i++) {
312
+ const local = checkMandate(mandates[i], q);
313
+ if (!local.allowed) {
314
+ return { credential_id: credentialId, status: v.status, allowed: false, reason: `outside the mandate of delegation ${i}`, checks: local.checks.map((c) => ({ field: c.rule, ok: c.ok, detail: c.detail })), not_evaluated: local.not_evaluated, delegation_level: i };
315
+ }
316
+ }
317
+ }
318
+ return client.allowed(credentialId, q);
319
+ }
232
320
  };
233
321
  return { ok: true, kya: ctx };
234
322
  }
@@ -309,13 +397,6 @@ export const KYA_REPLY_HEADER = "kya-in-reply-to";
309
397
  export function responseText(p) {
310
398
  return ["KYA Brasil: response", `credential: ${p.credentialId}`, `in-reply-to: ${p.inReplyTo}`, `created: ${p.created}`, `expires: ${p.expires}`, `content-sha256: ${p.contentSha256 ?? "-"}`].join("\n");
311
399
  }
312
- async function signText(key, text) {
313
- if (key.kind === "web-bot-auth") {
314
- const k = typeof key.privateKey === "string" ? await crypto.subtle.importKey("pkcs8", unb64u(key.privateKey.replace(/-----[^-]+-----/g, "").replace(/\s+/g, "")), { name: "Ed25519" }, false, ["sign"]) : key.privateKey;
315
- return b64u(new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, k, enc.encode(text))));
316
- }
317
- return privateKeyToAccount(key.privateKey).signMessage({ message: text });
318
- }
319
400
  /** Callee side: headers that prove who answered, bound to the request signature it replies to. */
320
401
  export async function signResponse(p) {
321
402
  const created = Math.floor((p.now ?? new Date()).getTime() / 1000);
package/dist/index.d.ts CHANGED
@@ -33,6 +33,11 @@ export interface Mandate {
33
33
  ref_sha256: string;
34
34
  label?: string;
35
35
  }[];
36
+ /** D-30: the agent may hand a narrower mandate to sub-agents (signDelegation), up to max_depth levels below it. */
37
+ delegation?: {
38
+ max_depth: number;
39
+ max_ttl_seconds: number;
40
+ };
36
41
  valid_from?: string;
37
42
  valid_until?: string;
38
43
  [k: string]: unknown;
@@ -408,6 +413,7 @@ export declare function verifyCredentialOffline(credential: Credential, opts?: {
408
413
  }): Promise<OfflineCheck>;
409
414
  export * from "./auth.js";
410
415
  export { isValidCpf, personAgentsText } from "@kya-brasil/core";
416
+ export { checkDelegatedAction, decodeDelegationHeader, encodeDelegationHeader, isMandateSubset, mandateSubsetProblems, verifyDelegationChain } from "@kya-brasil/core";
411
417
  export { buildDetachedCms, cnpjFromCommonName, ecnpjSigner, openPfx, readSigningLink, signWithECnpjPfx, type ECnpjSigner, type RawSigner } from "@kya-brasil/core";
412
418
  export { listTokenCertificates, openToken, type TokenCertificate, type TokenOptions } from "./pkcs11.js";
413
419
  /**
package/dist/index.js CHANGED
@@ -255,6 +255,8 @@ export async function verifyCredentialOffline(credential, opts = {}) {
255
255
  export * from "./auth.js";
256
256
  // e-CNPJ signing (D-26, D-27): the implementation lives in @kya-brasil/core, shared with anyone using the library.
257
257
  export { isValidCpf, personAgentsText } from "@kya-brasil/core";
258
+ // Delegation to sub-agents (D-30): chain rules live in the core, signing and the middleware in ./auth.
259
+ export { checkDelegatedAction, decodeDelegationHeader, encodeDelegationHeader, isMandateSubset, mandateSubsetProblems, verifyDelegationChain } from "@kya-brasil/core";
258
260
  export { buildDetachedCms, cnpjFromCommonName, ecnpjSigner, openPfx, readSigningLink, signWithECnpjPfx } from "@kya-brasil/core";
259
261
  export { listTokenCertificates, openToken } from "./pkcs11.js";
260
262
  import { signingLink as coreSigningLink } from "@kya-brasil/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kya-brasil/agent-tools",
3
- "version": "0.10.0",
3
+ "version": "0.11.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 with the company's e-CNPJ: A1 .pfx or A3 token via PKCS#11, in code or with the kya-sign command). 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",
@@ -56,7 +56,7 @@
56
56
  "@x402/fetch": "^2.28.0",
57
57
  "pkijs": "^3.4.1",
58
58
  "viem": "^2.57.5",
59
- "@kya-brasil/core": "^0.10.0"
59
+ "@kya-brasil/core": "^0.11.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@peculiar/asn1-schema": "^2.3.15",