prova-agent-kit 0.1.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/LICENSE ADDED
@@ -0,0 +1,58 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship made available under
36
+ the License, as indicated by a copyright notice that is included in
37
+ or attached with the work.
38
+
39
+ 2. Grant of Copyright License. Subject to the terms and conditions of
40
+ this License, each Contributor hereby grants to You a perpetual,
41
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
42
+ copyright license to reproduce, prepare Derivative Works of,
43
+ publicly display, publicly perform, sublicense, and distribute the
44
+ Work and such Derivative Works in Source or Object form.
45
+
46
+ Copyright 2026 Prova Contributors
47
+
48
+ Licensed under the Apache License, Version 2.0 (the "License");
49
+ you may not use this file except in compliance with the License.
50
+ You may obtain a copy of the License at
51
+
52
+ http://www.apache.org/licenses/LICENSE-2.0
53
+
54
+ Unless required by applicable law or agreed to in writing, software
55
+ distributed under the License is distributed on an "AS IS" BASIS,
56
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
57
+ See the License for the specific language governing permissions and
58
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # prova-agent-kit
2
+
3
+ **Prova adapter for [Solana Agent Kit](https://github.com/sendaifun/solana-agent-kit) (v2).**
4
+ Add **verifiable on-chain receipts** to any SAK agent — in one line. Every action your agent executes gets sealed as an immutable Prova attestation.
5
+
6
+ > Status: **proof of concept** (Devnet). Designed against `solana-agent-kit@^2.0.0`.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install prova-agent-kit prova-agent-sdk solana-agent-kit
12
+ ```
13
+
14
+ ## Quick start
15
+
16
+ ```typescript
17
+ import { SolanaAgentKit } from 'solana-agent-kit';
18
+ import { ProvaClient } from 'prova-agent-sdk';
19
+ import { attachProva, attesterFromProvaClient } from 'prova-agent-kit';
20
+ import { Keypair } from '@solana/web3.js';
21
+
22
+ // 1) Tu agente normal de Solana Agent Kit
23
+ const agent = new SolanaAgentKit(wallet, RPC_URL, {})
24
+ .use(TokenPlugin)
25
+ .use(DefiPlugin);
26
+
27
+ // 2) Cliente Prova + operador (keypair propio de Devnet, NO el wallet de trading)
28
+ const prova = new ProvaClient({ rpcUrl: 'https://api.devnet.solana.com', agentKeypair });
29
+ const operatorKeypair = Keypair.fromSecretKey(/* ... */);
30
+ const attester = attesterFromProvaClient(prova, ProvaClient.hashAction, operatorKeypair);
31
+
32
+ // 3) Una línea: atesta cada acción del agente. Llamar DESPUÉS de todos los .use()
33
+ const prova_handle = attachProva(agent, { attester });
34
+
35
+ // ... el agente opera normal; las acciones se atestan en segundo plano ...
36
+
37
+ await prova_handle.stop(); // flush final al cerrar
38
+ ```
39
+
40
+ ## Cómo funciona
41
+
42
+ Solana Agent Kit v2 no expone hooks/middleware, así que el adapter ofrece dos puntos de captura (úsalos juntos para cobertura total):
43
+
44
+ - **`attachProva(agent, opts)`** — envuelve `agent.actions[].handler`. Captura **nombre + input + output** de cada acción (incluidas las de solo lectura). Mecanismo principal.
45
+ - **`new ProvaWallet(wallet, opts)`** — decorador de `BaseWallet`. Captura la **firma on-chain real** de cada transacción, sin importar el plugin. Cinturón de seguridad.
46
+
47
+ ```typescript
48
+ import { ProvaWallet } from 'prova-agent-kit';
49
+ const agent = new SolanaAgentKit(new ProvaWallet(realWallet, { attester }), RPC_URL, {});
50
+ ```
51
+
52
+ ### No bloquea al agente
53
+ Las atestaciones son **fire-and-forget con batching**: un fallo de Prova nunca rompe ni frena una acción del agente. Los items se acumulan y se envían con `batchAttest` (hasta 100/tx) cada `maxSize` items o `flushIntervalMs` ms.
54
+
55
+ ```typescript
56
+ attachProva(agent, {
57
+ attester,
58
+ rules: (name) => name !== 'fetchPrice', // qué acciones atestar (default: todas)
59
+ batch: { maxSize: 25, flushIntervalMs: 10_000 },
60
+ onError: (err, ctx) => logger.warn(ctx.action, err),
61
+ });
62
+ ```
63
+
64
+ ## Modelo de claves
65
+ Prova atesta con **su propio par operador/agente de Devnet**, independiente del wallet de trading del agente. Ventajas: no necesita la private key de trading, funciona con wallets Privy/Turnkey, y el operador Prova es la identidad que avala al agente. Registra el agente una vez con `prova.registerAgent({ operatorKeypair })`.
66
+
67
+ ## API
68
+ - `attachProva(agent, options) → ProvaHandle` · `{ flush(), stop() }`
69
+ - `ProvaWallet(inner, options)` implements SAK `BaseWallet`
70
+ - `attesterFromProvaClient(client, hashAction, operatorKeypair) → ProvaAttester`
71
+ - `createBatcher(attester, options, onError) → Batcher` (interno, exportado para tests)
72
+ - `buildPayload`, `mapActionType`, `extractSignature` (puros)
73
+
74
+ ## Caveats
75
+ - **Orden:** llama `attachProva` **después** de todos los `.use(plugin)`; las acciones añadidas después no quedan envueltas. El `ProvaWallet` no depende del orden.
76
+ - **Tipos:** el adapter usa tipos estructurales de SAK v2 (no importa `solana-agent-kit`). Verifica compatibilidad al fijar versión.
77
+
78
+ ## License
79
+ Apache-2.0 · Prova Labs
@@ -0,0 +1,29 @@
1
+ import type { ProvaActionType, ProvaAttester } from './types';
2
+ /** Forma estructural del `ProvaClient` que necesitamos (de prova-agent-sdk). */
3
+ export interface ProvaClientLike {
4
+ attest(args: {
5
+ operatorKeypair: unknown;
6
+ actionHash: Uint8Array;
7
+ actionType: ProvaActionType;
8
+ }): Promise<{
9
+ txSignature: string;
10
+ }>;
11
+ batchAttest(args: {
12
+ operatorKeypair: unknown;
13
+ attestations: Array<{
14
+ actionHash: Uint8Array;
15
+ actionType: ProvaActionType;
16
+ }>;
17
+ }): Promise<{
18
+ txSignature: string;
19
+ }>;
20
+ }
21
+ /**
22
+ * Construye un `ProvaAttester` a partir de un `ProvaClient` real, la función
23
+ * estática `ProvaClient.hashAction` y la keypair operadora de Prova.
24
+ *
25
+ * @example
26
+ * const attester = attesterFromProvaClient(prova, ProvaClient.hashAction, operatorKeypair);
27
+ */
28
+ export declare function attesterFromProvaClient(client: ProvaClientLike, hashAction: (payload: string) => Promise<Uint8Array>, operatorKeypair: unknown): ProvaAttester;
29
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAmB,eAAe,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE/E,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,IAAI,EAAE;QACX,eAAe,EAAE,OAAO,CAAC;QACzB,UAAU,EAAE,UAAU,CAAC;QACvB,UAAU,EAAE,eAAe,CAAC;KAC7B,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrC,WAAW,CAAC,IAAI,EAAE;QAChB,eAAe,EAAE,OAAO,CAAC;QACzB,YAAY,EAAE,KAAK,CAAC;YAAE,UAAU,EAAE,UAAU,CAAC;YAAC,UAAU,EAAE,eAAe,CAAA;SAAE,CAAC,CAAC;KAC9E,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,EACpD,eAAe,EAAE,OAAO,GACvB,aAAa,CAYf"}
@@ -0,0 +1,21 @@
1
+ // Puente entre el `ProvaClient` real (prova-agent-sdk) y la interfaz
2
+ // `ProvaAttester` que usa el adapter. Tipado estructural → no importamos el SDK.
3
+ /**
4
+ * Construye un `ProvaAttester` a partir de un `ProvaClient` real, la función
5
+ * estática `ProvaClient.hashAction` y la keypair operadora de Prova.
6
+ *
7
+ * @example
8
+ * const attester = attesterFromProvaClient(prova, ProvaClient.hashAction, operatorKeypair);
9
+ */
10
+ export function attesterFromProvaClient(client, hashAction, operatorKeypair) {
11
+ return {
12
+ hashAction,
13
+ attest: (item) => client.attest({
14
+ operatorKeypair,
15
+ actionHash: item.actionHash,
16
+ actionType: item.actionType,
17
+ }),
18
+ batchAttest: (items) => client.batchAttest({ operatorKeypair, attestations: items }),
19
+ };
20
+ }
21
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,iFAAiF;AAiBjF;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAuB,EACvB,UAAoD,EACpD,eAAwB;IAExB,OAAO;QACL,UAAU;QACV,MAAM,EAAE,CAAC,IAAqB,EAAE,EAAE,CAChC,MAAM,CAAC,MAAM,CAAC;YACZ,eAAe;YACf,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC;QACJ,WAAW,EAAE,CAAC,KAAwB,EAAE,EAAE,CACxC,MAAM,CAAC,WAAW,CAAC,EAAE,eAAe,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;KAC/D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { AttachProvaOptions, HostAgent } from './types';
2
+ export interface ProvaHandle {
3
+ /** Fuerza el envío de las atestaciones pendientes. */
4
+ flush(): Promise<void>;
5
+ /** Detiene el batcher y hace un flush final (llamar al cerrar el agente). */
6
+ stop(): Promise<void>;
7
+ }
8
+ export declare function attachProva(agent: HostAgent, options: AttachProvaOptions): ProvaHandle;
9
+ //# sourceMappingURL=attach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAc,SAAS,EAAiB,MAAM,SAAS,CAAC;AAKxF,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,6EAA6E;IAC7E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAgCtF"}
package/dist/attach.js ADDED
@@ -0,0 +1,37 @@
1
+ // Enfoque B: envuelve los handlers de las acciones del agente para atestar cada
2
+ // ejecución en Prova. Es el mecanismo principal de captura (semántica rica:
3
+ // nombre + input + output). Llamar DESPUÉS de registrar todos los plugins.
4
+ import { buildPayload, mapActionType } from './payload';
5
+ import { createBatcher } from './batcher';
6
+ import { warn } from './runtime';
7
+ export function attachProva(agent, options) {
8
+ const { attester } = options;
9
+ const should = options.rules ?? (() => true);
10
+ const onError = options.onError ??
11
+ ((error, context) => warn(`[Prova] attest failed for "${context.action}":`, error));
12
+ const batcher = createBatcher(attester, options.batch, (error) => onError(error, { action: 'batch' }));
13
+ agent.actions = agent.actions.map((action) => {
14
+ if (!should(action.name))
15
+ return action;
16
+ const original = action.handler;
17
+ return {
18
+ ...action,
19
+ handler: async (a, input) => {
20
+ const result = await original(a, input);
21
+ // Atestación NO bloqueante: un fallo de Prova jamás rompe la acción.
22
+ void captureAction(attester, batcher, action.name, input, result).catch((error) => onError(error, { action: action.name }));
23
+ return result;
24
+ },
25
+ };
26
+ });
27
+ return {
28
+ flush: () => batcher.flush(),
29
+ stop: () => batcher.stop(),
30
+ };
31
+ }
32
+ async function captureAction(attester, batcher, name, input, result) {
33
+ const payload = buildPayload(name, input, result);
34
+ const actionHash = await attester.hashAction(JSON.stringify(payload));
35
+ batcher.add({ actionHash, actionType: mapActionType(name, result) });
36
+ }
37
+ //# sourceMappingURL=attach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.js","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,4EAA4E;AAC5E,2EAA2E;AAG3E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,aAAa,EAAgB,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AASjC,MAAM,UAAU,WAAW,CAAC,KAAgB,EAAE,OAA2B;IACvE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GACX,OAAO,CAAC,OAAO;QACf,CAAC,CAAC,KAAc,EAAE,OAA2B,EAAE,EAAE,CAC/C,IAAI,CAAC,8BAA8B,OAAO,CAAC,MAAM,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAEnE,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAC/D,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CACpC,CAAC;IAEF,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAkB,EAAc,EAAE;QACnE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC;QACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;QAChC,OAAO;YACL,GAAG,MAAM;YACT,OAAO,EAAE,KAAK,EAAE,CAAU,EAAE,KAA8B,EAAE,EAAE;gBAC5D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACxC,qEAAqE;gBACrE,KAAK,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAChF,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CACxC,CAAC;gBACF,OAAO,MAAM,CAAC;YAChB,CAAC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;QAC5B,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE;KAC3B,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,QAAuB,EACvB,OAAgB,EAChB,IAAY,EACZ,KAA8B,EAC9B,MAA+B;IAE/B,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { AttestationItem, BatchOptions, ProvaAttester } from './types';
2
+ export interface Batcher {
3
+ /** Encola una atestación; dispara flush si se alcanza maxSize. */
4
+ add(item: AttestationItem): void;
5
+ /** Envía inmediatamente lo pendiente. */
6
+ flush(): Promise<void>;
7
+ /** Detiene el timer y hace un último flush. */
8
+ stop(): Promise<void>;
9
+ }
10
+ export declare function createBatcher(attester: ProvaAttester, options?: BatchOptions, onError?: (error: unknown) => void): Batcher;
11
+ //# sourceMappingURL=batcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.d.ts","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG5E,MAAM,WAAW,OAAO;IACtB,kEAAkE;IAClE,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,yCAAyC;IACzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+CAA+C;IAC/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAMD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE,YAAiB,EAC1B,OAAO,GAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAqD,GACjF,OAAO,CA2CT"}
@@ -0,0 +1,52 @@
1
+ // Buffer de atestaciones: acumula items y los envía en lote (batchAttest) cada
2
+ // N items o cada T ms, lo que ocurra primero. Aísla la latencia/coste de Solana
3
+ // del hot-path del agente.
4
+ import { clearIntervalSafe, setIntervalSafe, warn } from './runtime';
5
+ const DEFAULT_MAX_SIZE = 25;
6
+ const DEFAULT_INTERVAL_MS = 10_000;
7
+ const HARD_BATCH_LIMIT = 100;
8
+ export function createBatcher(attester, options = {}, onError = (error) => warn('[Prova] batch error:', error)) {
9
+ const maxSize = Math.min(Math.max(options.maxSize ?? DEFAULT_MAX_SIZE, 1), HARD_BATCH_LIMIT);
10
+ const intervalMs = options.flushIntervalMs ?? DEFAULT_INTERVAL_MS;
11
+ let buffer = [];
12
+ let timer;
13
+ async function flush() {
14
+ if (buffer.length === 0)
15
+ return;
16
+ const batch = buffer;
17
+ buffer = [];
18
+ try {
19
+ if (batch.length === 1) {
20
+ await attester.attest(batch[0]);
21
+ }
22
+ else {
23
+ await attester.batchAttest(batch);
24
+ }
25
+ }
26
+ catch (error) {
27
+ onError(error);
28
+ }
29
+ }
30
+ function ensureTimer() {
31
+ if (intervalMs > 0 && timer === undefined) {
32
+ timer = setIntervalSafe(() => void flush(), intervalMs);
33
+ }
34
+ }
35
+ return {
36
+ add(item) {
37
+ buffer.push(item);
38
+ ensureTimer();
39
+ if (buffer.length >= maxSize)
40
+ void flush();
41
+ },
42
+ flush,
43
+ async stop() {
44
+ if (timer !== undefined) {
45
+ clearIntervalSafe(timer);
46
+ timer = undefined;
47
+ }
48
+ await flush();
49
+ },
50
+ };
51
+ }
52
+ //# sourceMappingURL=batcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.js","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,2BAA2B;AAG3B,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,IAAI,EAAgB,MAAM,WAAW,CAAC;AAWnF,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,MAAM,UAAU,aAAa,CAC3B,QAAuB,EACvB,UAAwB,EAAE,EAC1B,UAAoC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,sBAAsB,EAAE,KAAK,CAAC;IAElF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,gBAAgB,EAAE,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC7F,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,IAAI,mBAAmB,CAAC;IAElE,IAAI,MAAM,GAAsB,EAAE,CAAC;IACnC,IAAI,KAA0B,CAAC;IAE/B,KAAK,UAAU,KAAK;QAClB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC;QACrB,MAAM,GAAG,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,MAAM,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,SAAS,WAAW;QAClB,IAAI,UAAU,GAAG,CAAC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1C,KAAK,GAAG,eAAe,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,OAAO;QACL,GAAG,CAAC,IAAqB;YACvB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,WAAW,EAAE,CAAC;YACd,IAAI,MAAM,CAAC,MAAM,IAAI,OAAO;gBAAE,KAAK,KAAK,EAAE,CAAC;QAC7C,CAAC;QACD,KAAK;QACL,KAAK,CAAC,IAAI;YACR,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBACzB,KAAK,GAAG,SAAS,CAAC;YACpB,CAAC;YACD,MAAM,KAAK,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ export { attachProva } from './attach';
2
+ export type { ProvaHandle } from './attach';
3
+ export { ProvaWallet } from './wallet';
4
+ export type { ProvaWalletOptions } from './wallet';
5
+ export { attesterFromProvaClient } from './adapter';
6
+ export type { ProvaClientLike } from './adapter';
7
+ export { createBatcher } from './batcher';
8
+ export type { Batcher } from './batcher';
9
+ export { buildPayload, mapActionType, extractSignature } from './payload';
10
+ export type { HostAgent, HostAction, HostWallet, ProvaAttester, AttestationItem, ProvaActionType, AttachProvaOptions, BatchOptions, } from './types';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE1E,YAAY,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,YAAY,GACb,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // API pública del adapter Prova ↔ Solana Agent Kit.
2
+ export { attachProva } from './attach';
3
+ export { ProvaWallet } from './wallet';
4
+ export { attesterFromProvaClient } from './adapter';
5
+ export { createBatcher } from './batcher';
6
+ export { buildPayload, mapActionType, extractSignature } from './payload';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oDAAoD;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { ActionResult, ProvaActionType } from './types';
2
+ /** Extrae la firma on-chain del result de una acción, si está presente. */
3
+ export declare function extractSignature(result: ActionResult): string | undefined;
4
+ /** Mapea el nombre de una acción de SAK al `ActionType` de Prova. */
5
+ export declare function mapActionType(actionName: string, result: ActionResult): ProvaActionType;
6
+ /** Construye el objeto que se serializa y hashea para producir el action_hash. */
7
+ export declare function buildPayload(actionName: string, input: Record<string, unknown>, result: ActionResult): Record<string, unknown>;
8
+ //# sourceMappingURL=payload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../src/payload.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK7D,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAMzE;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,eAAe,CAIvF;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAC1B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB"}
@@ -0,0 +1,30 @@
1
+ // Funciones puras: derivan el payload y el tipo de atestación a partir de la
2
+ // acción de SAK y su resultado. Sin efectos secundarios → fáciles de testear.
3
+ const SIGNATURE_KEYS = ['signature', 'txSignature', 'txid', 'tx'];
4
+ const DECISION_PATTERN = /decision|choose|select|rebalance|hold|exit|approve|reject/i;
5
+ /** Extrae la firma on-chain del result de una acción, si está presente. */
6
+ export function extractSignature(result) {
7
+ for (const key of SIGNATURE_KEYS) {
8
+ const value = result[key];
9
+ if (typeof value === 'string' && value.length > 0)
10
+ return value;
11
+ }
12
+ return undefined;
13
+ }
14
+ /** Mapea el nombre de una acción de SAK al `ActionType` de Prova. */
15
+ export function mapActionType(actionName, result) {
16
+ if (extractSignature(result))
17
+ return 'Transaction';
18
+ if (DECISION_PATTERN.test(actionName))
19
+ return 'Decision';
20
+ return 'ToolCall';
21
+ }
22
+ /** Construye el objeto que se serializa y hashea para producir el action_hash. */
23
+ export function buildPayload(actionName, input, result) {
24
+ const signature = extractSignature(result);
25
+ if (signature) {
26
+ return { kind: 'transaction', action: actionName, signature, input };
27
+ }
28
+ return { kind: 'toolCall', action: actionName, input, result };
29
+ }
30
+ //# sourceMappingURL=payload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../src/payload.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAI9E,MAAM,cAAc,GAAG,CAAC,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AAC3E,MAAM,gBAAgB,GAAG,4DAA4D,CAAC;AAEtF,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACnD,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,aAAa,CAAC,UAAkB,EAAE,MAAoB;IACpE,IAAI,gBAAgB,CAAC,MAAM,CAAC;QAAE,OAAO,aAAa,CAAC;IACnD,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACzD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAC1B,UAAkB,EAClB,KAA8B,EAC9B,MAAoB;IAEpB,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACvE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACjE,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** Identificador opaco de un timer (number en navegador, objeto en Node). */
2
+ export type TimerId = number | {
3
+ readonly __timer?: unique symbol;
4
+ };
5
+ /** setInterval que no bloquea la salida del proceso (unref en Node si existe). */
6
+ export declare function setIntervalSafe(handler: () => void, ms: number): TimerId;
7
+ export declare function clearIntervalSafe(id: TimerId): void;
8
+ export declare function warn(...args: unknown[]): void;
9
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAIA,6EAA6E;AAC7E,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AAUpE,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAKxE;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAEnD;AAED,wBAAgB,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAE7C"}
@@ -0,0 +1,19 @@
1
+ // Acceso tipado a globales de runtime (timers, console) sin depender de
2
+ // @types/node ni de los tipos del DOM. En ejecución real los provee Node o el
3
+ // navegador; aquí los declaramos mínimamente para mantener el adapter ligero.
4
+ const runtime = globalThis;
5
+ /** setInterval que no bloquea la salida del proceso (unref en Node si existe). */
6
+ export function setIntervalSafe(handler, ms) {
7
+ const id = runtime.setInterval(handler, ms);
8
+ const maybe = id;
9
+ if (typeof maybe.unref === 'function')
10
+ maybe.unref();
11
+ return id;
12
+ }
13
+ export function clearIntervalSafe(id) {
14
+ runtime.clearInterval(id);
15
+ }
16
+ export function warn(...args) {
17
+ runtime.console.warn(...args);
18
+ }
19
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,8EAA8E;AAC9E,8EAA8E;AAW9E,MAAM,OAAO,GAAG,UAAuC,CAAC;AAExD,kFAAkF;AAClF,MAAM,UAAU,eAAe,CAAC,OAAmB,EAAE,EAAU;IAC7D,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,EAAuC,CAAC;IACtD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU;QAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IACrD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAW;IAC3C,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAG,IAAe;IACrC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,66 @@
1
+ /** Resultado típico del handler de una acción de SAK. */
2
+ export type ActionResult = Record<string, unknown>;
3
+ /** Acción de Solana Agent Kit (subconjunto que el adapter necesita). */
4
+ export interface HostAction {
5
+ name: string;
6
+ handler: (agent: unknown, input: Record<string, unknown>) => Promise<ActionResult>;
7
+ [key: string]: unknown;
8
+ }
9
+ /** Agente de Solana Agent Kit (subconjunto que el adapter necesita). */
10
+ export interface HostAgent {
11
+ actions: HostAction[];
12
+ [key: string]: unknown;
13
+ }
14
+ /** BaseWallet de Solana Agent Kit v2, en forma estructural. */
15
+ export interface HostWallet {
16
+ readonly publicKey: {
17
+ toBase58(): string;
18
+ };
19
+ signTransaction<T>(transaction: T): Promise<T>;
20
+ signAllTransactions<T>(transactions: T[]): Promise<T[]>;
21
+ signAndSendTransaction<T>(transaction: T, options?: unknown): Promise<{
22
+ signature: string;
23
+ }>;
24
+ signMessage(message: Uint8Array): Promise<Uint8Array>;
25
+ }
26
+ /** Tipos de acción soportados por Prova (espejo de `ActionType` del SDK). */
27
+ export type ProvaActionType = 'Transaction' | 'ToolCall' | 'ModelInvocation' | 'Decision' | 'ResourceAccess' | 'PolicyCheck' | 'Custom';
28
+ /** Una atestación lista para enviar (action_hash de 32 bytes + tipo). */
29
+ export interface AttestationItem {
30
+ actionHash: Uint8Array;
31
+ actionType: ProvaActionType;
32
+ }
33
+ /**
34
+ * Superficie mínima que el adapter necesita de Prova, inyectada por el
35
+ * integrador. Permite usar el `ProvaClient` real sin acoplarnos a su tipo.
36
+ */
37
+ export interface ProvaAttester {
38
+ hashAction(payload: string): Promise<Uint8Array>;
39
+ attest(item: AttestationItem): Promise<{
40
+ txSignature: string;
41
+ }>;
42
+ batchAttest(items: AttestationItem[]): Promise<{
43
+ txSignature: string;
44
+ }>;
45
+ }
46
+ /** Configuración del batching de atestaciones. */
47
+ export interface BatchOptions {
48
+ /** Nº máximo de atestaciones por transacción (1–100). Default 25. */
49
+ maxSize?: number;
50
+ /** Flush automático cada N ms. Default 10000. 0 = sin flush por tiempo. */
51
+ flushIntervalMs?: number;
52
+ }
53
+ /** Opciones de `attachProva`. */
54
+ export interface AttachProvaOptions {
55
+ /** Puente hacia Prova (ver `attesterFromProvaClient`). */
56
+ attester: ProvaAttester;
57
+ /** Filtra qué acciones atestar por nombre (default: todas). */
58
+ rules?: (actionName: string) => boolean;
59
+ /** Configuración de batching. */
60
+ batch?: BatchOptions;
61
+ /** Manejo de errores (default: warn por consola). Nunca rompe la acción. */
62
+ onError?: (error: unknown, context: {
63
+ action: string;
64
+ }) => void;
65
+ }
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnD,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACnF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wEAAwE;AACxE,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAC;IAC3C,eAAe,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/C,mBAAmB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACxD,sBAAsB,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7F,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACvD;AAED,6EAA6E;AAC7E,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,UAAU,GACV,gBAAgB,GAChB,aAAa,GACb,QAAQ,CAAC;AAEb,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzE;AAED,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,iCAAiC;AACjC,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,QAAQ,EAAE,aAAa,CAAC;IACxB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,iCAAiC;IACjC,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ // Tipos del adapter. Reflejamos estructuralmente la API pública de Solana Agent
2
+ // Kit v2 (BaseWallet, Action, SolanaAgentKit) para NO importar el paquete y
3
+ // mantener el adapter como peerDependency ligera. La compatibilidad se apoya en
4
+ // el tipado estructural de TypeScript.
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,uCAAuC"}
@@ -0,0 +1,27 @@
1
+ import type { BatchOptions, HostWallet, ProvaAttester } from './types';
2
+ export interface ProvaWalletOptions {
3
+ attester: ProvaAttester;
4
+ batch?: BatchOptions;
5
+ onError?: (error: unknown) => void;
6
+ }
7
+ export declare class ProvaWallet implements HostWallet {
8
+ private readonly inner;
9
+ private readonly attester;
10
+ private readonly batcher;
11
+ constructor(inner: HostWallet, options: ProvaWalletOptions);
12
+ get publicKey(): {
13
+ toBase58(): string;
14
+ };
15
+ signTransaction<T>(transaction: T): Promise<T>;
16
+ signAllTransactions<T>(transactions: T[]): Promise<T[]>;
17
+ signMessage(message: Uint8Array): Promise<Uint8Array>;
18
+ signAndSendTransaction<T>(transaction: T, options?: unknown): Promise<{
19
+ signature: string;
20
+ }>;
21
+ /** Fuerza el envío de las atestaciones pendientes. */
22
+ flush(): Promise<void>;
23
+ /** Detiene el batcher y hace un flush final. */
24
+ stop(): Promise<void>;
25
+ private captureSignature;
26
+ }
27
+ //# sourceMappingURL=wallet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGvE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED,qBAAa,WAAY,YAAW,UAAU;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB;IAM1D,IAAI,SAAS,IAAI;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAEtC;IAED,eAAe,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI9C,mBAAmB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAIvD,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI/C,sBAAsB,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAOlG,sDAAsD;IACtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,gDAAgD;IAChD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAIP,gBAAgB;CAK/B"}
package/dist/wallet.js ADDED
@@ -0,0 +1,46 @@
1
+ // Enfoque A: decorador de BaseWallet (SAK v2). Captura la firma on-chain real
2
+ // de cada transacción firmada/enviada, sin importar qué plugin la produjo. Es
3
+ // el "cinturón de seguridad" que complementa a `attachProva`.
4
+ import { createBatcher } from './batcher';
5
+ export class ProvaWallet {
6
+ inner;
7
+ attester;
8
+ batcher;
9
+ constructor(inner, options) {
10
+ this.inner = inner;
11
+ this.attester = options.attester;
12
+ this.batcher = createBatcher(options.attester, options.batch, options.onError);
13
+ }
14
+ get publicKey() {
15
+ return this.inner.publicKey;
16
+ }
17
+ signTransaction(transaction) {
18
+ return this.inner.signTransaction(transaction);
19
+ }
20
+ signAllTransactions(transactions) {
21
+ return this.inner.signAllTransactions(transactions);
22
+ }
23
+ signMessage(message) {
24
+ return this.inner.signMessage(message);
25
+ }
26
+ async signAndSendTransaction(transaction, options) {
27
+ const result = await this.inner.signAndSendTransaction(transaction, options);
28
+ // Atestación NO bloqueante de la firma on-chain real.
29
+ void this.captureSignature(result.signature).catch(() => { });
30
+ return result;
31
+ }
32
+ /** Fuerza el envío de las atestaciones pendientes. */
33
+ flush() {
34
+ return this.batcher.flush();
35
+ }
36
+ /** Detiene el batcher y hace un flush final. */
37
+ stop() {
38
+ return this.batcher.stop();
39
+ }
40
+ async captureSignature(signature) {
41
+ const payload = { kind: 'transaction', signature, source: 'wallet' };
42
+ const actionHash = await this.attester.hashAction(JSON.stringify(payload));
43
+ this.batcher.add({ actionHash, actionType: 'Transaction' });
44
+ }
45
+ }
46
+ //# sourceMappingURL=wallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet.js","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8EAA8E;AAC9E,8DAA8D;AAG9D,OAAO,EAAE,aAAa,EAAgB,MAAM,WAAW,CAAC;AAQxD,MAAM,OAAO,WAAW;IACL,KAAK,CAAa;IAClB,QAAQ,CAAgB;IACxB,OAAO,CAAU;IAElC,YAAY,KAAiB,EAAE,OAA2B;QACxD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACjF,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;IAC9B,CAAC;IAED,eAAe,CAAI,WAAc;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;IACjD,CAAC;IAED,mBAAmB,CAAI,YAAiB;QACtC,OAAO,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;IACtD,CAAC;IAED,WAAW,CAAC,OAAmB;QAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAI,WAAc,EAAE,OAAiB;QAC/D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC7E,sDAAsD;QACtD,KAAK,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC7D,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,sDAAsD;IACtD,KAAK;QACH,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,gDAAgD;IAChD,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,SAAiB;QAC9C,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACrE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAC;IAC9D,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "prova-agent-kit",
3
+ "version": "0.1.0",
4
+ "license": "Apache-2.0",
5
+ "author": "Prova Labs",
6
+ "description": "Prova adapter for Solana Agent Kit — emit verifiable on-chain receipts for every action a SAK agent executes",
7
+ "homepage": "https://prova-solana.vercel.app",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Eras256/Prova.git",
11
+ "directory": "packages/sdk-agent-kit"
12
+ },
13
+ "keywords": [
14
+ "solana",
15
+ "solana-agent-kit",
16
+ "ai-agents",
17
+ "attestation",
18
+ "prova",
19
+ "accountability"
20
+ ],
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "import": "./dist/index.js",
26
+ "require": "./dist/index.js",
27
+ "types": "./dist/index.d.ts"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "README.md"
34
+ ],
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "peerDependencies": {
39
+ "@solana/web3.js": "^1.98.0",
40
+ "prova-agent-sdk": "^0.1.6",
41
+ "solana-agent-kit": "^2.0.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "@solana/web3.js": {
45
+ "optional": true
46
+ }
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.10.7",
50
+ "rimraf": "^6.0.1",
51
+ "typescript": "^5.7.3",
52
+ "vitest": "^2.1.8",
53
+ "@prova/config-typescript": "0.0.1"
54
+ },
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ },
58
+ "scripts": {
59
+ "build": "tsc",
60
+ "typecheck": "tsc --noEmit",
61
+ "test": "vitest run",
62
+ "test:watch": "vitest",
63
+ "clean": "rimraf dist"
64
+ }
65
+ }
package/src/adapter.ts ADDED
@@ -0,0 +1,42 @@
1
+ // Puente entre el `ProvaClient` real (prova-agent-sdk) y la interfaz
2
+ // `ProvaAttester` que usa el adapter. Tipado estructural → no importamos el SDK.
3
+
4
+ import type { AttestationItem, ProvaActionType, ProvaAttester } from './types';
5
+
6
+ /** Forma estructural del `ProvaClient` que necesitamos (de prova-agent-sdk). */
7
+ export interface ProvaClientLike {
8
+ attest(args: {
9
+ operatorKeypair: unknown;
10
+ actionHash: Uint8Array;
11
+ actionType: ProvaActionType;
12
+ }): Promise<{ txSignature: string }>;
13
+ batchAttest(args: {
14
+ operatorKeypair: unknown;
15
+ attestations: Array<{ actionHash: Uint8Array; actionType: ProvaActionType }>;
16
+ }): Promise<{ txSignature: string }>;
17
+ }
18
+
19
+ /**
20
+ * Construye un `ProvaAttester` a partir de un `ProvaClient` real, la función
21
+ * estática `ProvaClient.hashAction` y la keypair operadora de Prova.
22
+ *
23
+ * @example
24
+ * const attester = attesterFromProvaClient(prova, ProvaClient.hashAction, operatorKeypair);
25
+ */
26
+ export function attesterFromProvaClient(
27
+ client: ProvaClientLike,
28
+ hashAction: (payload: string) => Promise<Uint8Array>,
29
+ operatorKeypair: unknown,
30
+ ): ProvaAttester {
31
+ return {
32
+ hashAction,
33
+ attest: (item: AttestationItem) =>
34
+ client.attest({
35
+ operatorKeypair,
36
+ actionHash: item.actionHash,
37
+ actionType: item.actionType,
38
+ }),
39
+ batchAttest: (items: AttestationItem[]) =>
40
+ client.batchAttest({ operatorKeypair, attestations: items }),
41
+ };
42
+ }
@@ -0,0 +1,85 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { attachProva } from './attach';
3
+ import type { AttestationItem, HostAgent, ProvaAttester } from './types';
4
+
5
+ function fakeAttester(): ProvaAttester & { items: AttestationItem[] } {
6
+ const items: AttestationItem[] = [];
7
+ return {
8
+ items,
9
+ hashAction: async () => new Uint8Array(32),
10
+ attest: async (item) => {
11
+ items.push(item);
12
+ return { txSignature: 'tx' };
13
+ },
14
+ batchAttest: async (batch) => {
15
+ items.push(...batch);
16
+ return { txSignature: 'tx' };
17
+ },
18
+ };
19
+ }
20
+
21
+ const tick = () => new Promise((resolve) => setTimeout(resolve, 0));
22
+
23
+ describe('attachProva', () => {
24
+ it('runs the original action and attests its result', async () => {
25
+ const attester = fakeAttester();
26
+ const agent: HostAgent = {
27
+ actions: [{ name: 'trade', handler: async () => ({ signature: 'sig123' }) }],
28
+ };
29
+
30
+ const handle = attachProva(agent, {
31
+ attester,
32
+ batch: { maxSize: 1, flushIntervalMs: 0 },
33
+ });
34
+
35
+ const result = await agent.actions[0]!.handler(agent, { amount: 10 });
36
+ expect(result).toEqual({ signature: 'sig123' });
37
+
38
+ await tick();
39
+ await handle.flush();
40
+
41
+ expect(attester.items).toHaveLength(1);
42
+ expect(attester.items[0]!.actionType).toBe('Transaction');
43
+ });
44
+
45
+ it('never breaks the action when attestation fails', async () => {
46
+ const attester: ProvaAttester = {
47
+ hashAction: async () => {
48
+ throw new Error('boom');
49
+ },
50
+ attest: async () => ({ txSignature: 'tx' }),
51
+ batchAttest: async () => ({ txSignature: 'tx' }),
52
+ };
53
+ const agent: HostAgent = {
54
+ actions: [{ name: 'x', handler: async () => ({ ok: true }) }],
55
+ };
56
+
57
+ attachProva(agent, { attester, onError: () => {} });
58
+
59
+ const result = await agent.actions[0]!.handler(agent, {});
60
+ expect(result).toEqual({ ok: true });
61
+ });
62
+
63
+ it('respects the rules filter', async () => {
64
+ const attester = fakeAttester();
65
+ const agent: HostAgent = {
66
+ actions: [
67
+ { name: 'trade', handler: async () => ({ signature: 's' }) },
68
+ { name: 'fetchPrice', handler: async () => ({ price: 1 }) },
69
+ ],
70
+ };
71
+
72
+ const handle = attachProva(agent, {
73
+ attester,
74
+ rules: (name) => name === 'trade',
75
+ batch: { maxSize: 1, flushIntervalMs: 0 },
76
+ });
77
+
78
+ await agent.actions[0]!.handler(agent, {});
79
+ await agent.actions[1]!.handler(agent, {});
80
+ await tick();
81
+ await handle.flush();
82
+
83
+ expect(attester.items).toHaveLength(1);
84
+ });
85
+ });
package/src/attach.ts ADDED
@@ -0,0 +1,61 @@
1
+ // Enfoque B: envuelve los handlers de las acciones del agente para atestar cada
2
+ // ejecución en Prova. Es el mecanismo principal de captura (semántica rica:
3
+ // nombre + input + output). Llamar DESPUÉS de registrar todos los plugins.
4
+
5
+ import type { AttachProvaOptions, HostAction, HostAgent, ProvaAttester } from './types';
6
+ import { buildPayload, mapActionType } from './payload';
7
+ import { createBatcher, type Batcher } from './batcher';
8
+ import { warn } from './runtime';
9
+
10
+ export interface ProvaHandle {
11
+ /** Fuerza el envío de las atestaciones pendientes. */
12
+ flush(): Promise<void>;
13
+ /** Detiene el batcher y hace un flush final (llamar al cerrar el agente). */
14
+ stop(): Promise<void>;
15
+ }
16
+
17
+ export function attachProva(agent: HostAgent, options: AttachProvaOptions): ProvaHandle {
18
+ const { attester } = options;
19
+ const should = options.rules ?? (() => true);
20
+ const onError =
21
+ options.onError ??
22
+ ((error: unknown, context: { action: string }) =>
23
+ warn(`[Prova] attest failed for "${context.action}":`, error));
24
+
25
+ const batcher = createBatcher(attester, options.batch, (error) =>
26
+ onError(error, { action: 'batch' }),
27
+ );
28
+
29
+ agent.actions = agent.actions.map((action: HostAction): HostAction => {
30
+ if (!should(action.name)) return action;
31
+ const original = action.handler;
32
+ return {
33
+ ...action,
34
+ handler: async (a: unknown, input: Record<string, unknown>) => {
35
+ const result = await original(a, input);
36
+ // Atestación NO bloqueante: un fallo de Prova jamás rompe la acción.
37
+ void captureAction(attester, batcher, action.name, input, result).catch((error) =>
38
+ onError(error, { action: action.name }),
39
+ );
40
+ return result;
41
+ },
42
+ };
43
+ });
44
+
45
+ return {
46
+ flush: () => batcher.flush(),
47
+ stop: () => batcher.stop(),
48
+ };
49
+ }
50
+
51
+ async function captureAction(
52
+ attester: ProvaAttester,
53
+ batcher: Batcher,
54
+ name: string,
55
+ input: Record<string, unknown>,
56
+ result: Record<string, unknown>,
57
+ ): Promise<void> {
58
+ const payload = buildPayload(name, input, result);
59
+ const actionHash = await attester.hashAction(JSON.stringify(payload));
60
+ batcher.add({ actionHash, actionType: mapActionType(name, result) });
61
+ }
package/src/batcher.ts ADDED
@@ -0,0 +1,68 @@
1
+ // Buffer de atestaciones: acumula items y los envía en lote (batchAttest) cada
2
+ // N items o cada T ms, lo que ocurra primero. Aísla la latencia/coste de Solana
3
+ // del hot-path del agente.
4
+
5
+ import type { AttestationItem, BatchOptions, ProvaAttester } from './types';
6
+ import { clearIntervalSafe, setIntervalSafe, warn, type TimerId } from './runtime';
7
+
8
+ export interface Batcher {
9
+ /** Encola una atestación; dispara flush si se alcanza maxSize. */
10
+ add(item: AttestationItem): void;
11
+ /** Envía inmediatamente lo pendiente. */
12
+ flush(): Promise<void>;
13
+ /** Detiene el timer y hace un último flush. */
14
+ stop(): Promise<void>;
15
+ }
16
+
17
+ const DEFAULT_MAX_SIZE = 25;
18
+ const DEFAULT_INTERVAL_MS = 10_000;
19
+ const HARD_BATCH_LIMIT = 100;
20
+
21
+ export function createBatcher(
22
+ attester: ProvaAttester,
23
+ options: BatchOptions = {},
24
+ onError: (error: unknown) => void = (error) => warn('[Prova] batch error:', error),
25
+ ): Batcher {
26
+ const maxSize = Math.min(Math.max(options.maxSize ?? DEFAULT_MAX_SIZE, 1), HARD_BATCH_LIMIT);
27
+ const intervalMs = options.flushIntervalMs ?? DEFAULT_INTERVAL_MS;
28
+
29
+ let buffer: AttestationItem[] = [];
30
+ let timer: TimerId | undefined;
31
+
32
+ async function flush(): Promise<void> {
33
+ if (buffer.length === 0) return;
34
+ const batch = buffer;
35
+ buffer = [];
36
+ try {
37
+ if (batch.length === 1) {
38
+ await attester.attest(batch[0]!);
39
+ } else {
40
+ await attester.batchAttest(batch);
41
+ }
42
+ } catch (error) {
43
+ onError(error);
44
+ }
45
+ }
46
+
47
+ function ensureTimer(): void {
48
+ if (intervalMs > 0 && timer === undefined) {
49
+ timer = setIntervalSafe(() => void flush(), intervalMs);
50
+ }
51
+ }
52
+
53
+ return {
54
+ add(item: AttestationItem): void {
55
+ buffer.push(item);
56
+ ensureTimer();
57
+ if (buffer.length >= maxSize) void flush();
58
+ },
59
+ flush,
60
+ async stop(): Promise<void> {
61
+ if (timer !== undefined) {
62
+ clearIntervalSafe(timer);
63
+ timer = undefined;
64
+ }
65
+ await flush();
66
+ },
67
+ };
68
+ }
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ // API pública del adapter Prova ↔ Solana Agent Kit.
2
+
3
+ export { attachProva } from './attach';
4
+ export type { ProvaHandle } from './attach';
5
+
6
+ export { ProvaWallet } from './wallet';
7
+ export type { ProvaWalletOptions } from './wallet';
8
+
9
+ export { attesterFromProvaClient } from './adapter';
10
+ export type { ProvaClientLike } from './adapter';
11
+
12
+ export { createBatcher } from './batcher';
13
+ export type { Batcher } from './batcher';
14
+
15
+ export { buildPayload, mapActionType, extractSignature } from './payload';
16
+
17
+ export type {
18
+ HostAgent,
19
+ HostAction,
20
+ HostWallet,
21
+ ProvaAttester,
22
+ AttestationItem,
23
+ ProvaActionType,
24
+ AttachProvaOptions,
25
+ BatchOptions,
26
+ } from './types';
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { buildPayload, extractSignature, mapActionType } from './payload';
3
+
4
+ describe('extractSignature', () => {
5
+ it('reads the signature from common SAK result shapes', () => {
6
+ expect(extractSignature({ signature: 'abc' })).toBe('abc');
7
+ expect(extractSignature({ txSignature: 'def' })).toBe('def');
8
+ expect(extractSignature({ txid: 'ghi' })).toBe('ghi');
9
+ });
10
+
11
+ it('returns undefined when there is no signature', () => {
12
+ expect(extractSignature({ foo: 1 })).toBeUndefined();
13
+ expect(extractSignature({ signature: '' })).toBeUndefined();
14
+ });
15
+ });
16
+
17
+ describe('mapActionType', () => {
18
+ it('classifies as Transaction when a signature is present', () => {
19
+ expect(mapActionType('trade', { signature: 'x' })).toBe('Transaction');
20
+ });
21
+
22
+ it('classifies decision-like actions', () => {
23
+ expect(mapActionType('rebalancePortfolio', {})).toBe('Decision');
24
+ expect(mapActionType('exitPosition', {})).toBe('Decision');
25
+ });
26
+
27
+ it('falls back to ToolCall', () => {
28
+ expect(mapActionType('fetchPrice', {})).toBe('ToolCall');
29
+ });
30
+ });
31
+
32
+ describe('buildPayload', () => {
33
+ it('builds a transaction payload when a signature is present', () => {
34
+ const payload = buildPayload('trade', { amount: 1 }, { signature: 'sig' });
35
+ expect(payload['kind']).toBe('transaction');
36
+ expect(payload['signature']).toBe('sig');
37
+ });
38
+
39
+ it('builds a toolCall payload otherwise', () => {
40
+ const payload = buildPayload('fetchPrice', { token: 'SOL' }, { price: 100 });
41
+ expect(payload['kind']).toBe('toolCall');
42
+ });
43
+ });
package/src/payload.ts ADDED
@@ -0,0 +1,36 @@
1
+ // Funciones puras: derivan el payload y el tipo de atestación a partir de la
2
+ // acción de SAK y su resultado. Sin efectos secundarios → fáciles de testear.
3
+
4
+ import type { ActionResult, ProvaActionType } from './types';
5
+
6
+ const SIGNATURE_KEYS = ['signature', 'txSignature', 'txid', 'tx'] as const;
7
+ const DECISION_PATTERN = /decision|choose|select|rebalance|hold|exit|approve|reject/i;
8
+
9
+ /** Extrae la firma on-chain del result de una acción, si está presente. */
10
+ export function extractSignature(result: ActionResult): string | undefined {
11
+ for (const key of SIGNATURE_KEYS) {
12
+ const value = result[key];
13
+ if (typeof value === 'string' && value.length > 0) return value;
14
+ }
15
+ return undefined;
16
+ }
17
+
18
+ /** Mapea el nombre de una acción de SAK al `ActionType` de Prova. */
19
+ export function mapActionType(actionName: string, result: ActionResult): ProvaActionType {
20
+ if (extractSignature(result)) return 'Transaction';
21
+ if (DECISION_PATTERN.test(actionName)) return 'Decision';
22
+ return 'ToolCall';
23
+ }
24
+
25
+ /** Construye el objeto que se serializa y hashea para producir el action_hash. */
26
+ export function buildPayload(
27
+ actionName: string,
28
+ input: Record<string, unknown>,
29
+ result: ActionResult,
30
+ ): Record<string, unknown> {
31
+ const signature = extractSignature(result);
32
+ if (signature) {
33
+ return { kind: 'transaction', action: actionName, signature, input };
34
+ }
35
+ return { kind: 'toolCall', action: actionName, input, result };
36
+ }
package/src/runtime.ts ADDED
@@ -0,0 +1,30 @@
1
+ // Acceso tipado a globales de runtime (timers, console) sin depender de
2
+ // @types/node ni de los tipos del DOM. En ejecución real los provee Node o el
3
+ // navegador; aquí los declaramos mínimamente para mantener el adapter ligero.
4
+
5
+ /** Identificador opaco de un timer (number en navegador, objeto en Node). */
6
+ export type TimerId = number | { readonly __timer?: unique symbol };
7
+
8
+ interface RuntimeGlobals {
9
+ setInterval(handler: () => void, ms: number): TimerId;
10
+ clearInterval(id: TimerId): void;
11
+ console: { warn(...args: unknown[]): void };
12
+ }
13
+
14
+ const runtime = globalThis as unknown as RuntimeGlobals;
15
+
16
+ /** setInterval que no bloquea la salida del proceso (unref en Node si existe). */
17
+ export function setIntervalSafe(handler: () => void, ms: number): TimerId {
18
+ const id = runtime.setInterval(handler, ms);
19
+ const maybe = id as unknown as { unref?: () => void };
20
+ if (typeof maybe.unref === 'function') maybe.unref();
21
+ return id;
22
+ }
23
+
24
+ export function clearIntervalSafe(id: TimerId): void {
25
+ runtime.clearInterval(id);
26
+ }
27
+
28
+ export function warn(...args: unknown[]): void {
29
+ runtime.console.warn(...args);
30
+ }
package/src/types.ts ADDED
@@ -0,0 +1,75 @@
1
+ // Tipos del adapter. Reflejamos estructuralmente la API pública de Solana Agent
2
+ // Kit v2 (BaseWallet, Action, SolanaAgentKit) para NO importar el paquete y
3
+ // mantener el adapter como peerDependency ligera. La compatibilidad se apoya en
4
+ // el tipado estructural de TypeScript.
5
+
6
+ /** Resultado típico del handler de una acción de SAK. */
7
+ export type ActionResult = Record<string, unknown>;
8
+
9
+ /** Acción de Solana Agent Kit (subconjunto que el adapter necesita). */
10
+ export interface HostAction {
11
+ name: string;
12
+ handler: (agent: unknown, input: Record<string, unknown>) => Promise<ActionResult>;
13
+ [key: string]: unknown;
14
+ }
15
+
16
+ /** Agente de Solana Agent Kit (subconjunto que el adapter necesita). */
17
+ export interface HostAgent {
18
+ actions: HostAction[];
19
+ [key: string]: unknown;
20
+ }
21
+
22
+ /** BaseWallet de Solana Agent Kit v2, en forma estructural. */
23
+ export interface HostWallet {
24
+ readonly publicKey: { toBase58(): string };
25
+ signTransaction<T>(transaction: T): Promise<T>;
26
+ signAllTransactions<T>(transactions: T[]): Promise<T[]>;
27
+ signAndSendTransaction<T>(transaction: T, options?: unknown): Promise<{ signature: string }>;
28
+ signMessage(message: Uint8Array): Promise<Uint8Array>;
29
+ }
30
+
31
+ /** Tipos de acción soportados por Prova (espejo de `ActionType` del SDK). */
32
+ export type ProvaActionType =
33
+ | 'Transaction'
34
+ | 'ToolCall'
35
+ | 'ModelInvocation'
36
+ | 'Decision'
37
+ | 'ResourceAccess'
38
+ | 'PolicyCheck'
39
+ | 'Custom';
40
+
41
+ /** Una atestación lista para enviar (action_hash de 32 bytes + tipo). */
42
+ export interface AttestationItem {
43
+ actionHash: Uint8Array;
44
+ actionType: ProvaActionType;
45
+ }
46
+
47
+ /**
48
+ * Superficie mínima que el adapter necesita de Prova, inyectada por el
49
+ * integrador. Permite usar el `ProvaClient` real sin acoplarnos a su tipo.
50
+ */
51
+ export interface ProvaAttester {
52
+ hashAction(payload: string): Promise<Uint8Array>;
53
+ attest(item: AttestationItem): Promise<{ txSignature: string }>;
54
+ batchAttest(items: AttestationItem[]): Promise<{ txSignature: string }>;
55
+ }
56
+
57
+ /** Configuración del batching de atestaciones. */
58
+ export interface BatchOptions {
59
+ /** Nº máximo de atestaciones por transacción (1–100). Default 25. */
60
+ maxSize?: number;
61
+ /** Flush automático cada N ms. Default 10000. 0 = sin flush por tiempo. */
62
+ flushIntervalMs?: number;
63
+ }
64
+
65
+ /** Opciones de `attachProva`. */
66
+ export interface AttachProvaOptions {
67
+ /** Puente hacia Prova (ver `attesterFromProvaClient`). */
68
+ attester: ProvaAttester;
69
+ /** Filtra qué acciones atestar por nombre (default: todas). */
70
+ rules?: (actionName: string) => boolean;
71
+ /** Configuración de batching. */
72
+ batch?: BatchOptions;
73
+ /** Manejo de errores (default: warn por consola). Nunca rompe la acción. */
74
+ onError?: (error: unknown, context: { action: string }) => void;
75
+ }
package/src/wallet.ts ADDED
@@ -0,0 +1,63 @@
1
+ // Enfoque A: decorador de BaseWallet (SAK v2). Captura la firma on-chain real
2
+ // de cada transacción firmada/enviada, sin importar qué plugin la produjo. Es
3
+ // el "cinturón de seguridad" que complementa a `attachProva`.
4
+
5
+ import type { BatchOptions, HostWallet, ProvaAttester } from './types';
6
+ import { createBatcher, type Batcher } from './batcher';
7
+
8
+ export interface ProvaWalletOptions {
9
+ attester: ProvaAttester;
10
+ batch?: BatchOptions;
11
+ onError?: (error: unknown) => void;
12
+ }
13
+
14
+ export class ProvaWallet implements HostWallet {
15
+ private readonly inner: HostWallet;
16
+ private readonly attester: ProvaAttester;
17
+ private readonly batcher: Batcher;
18
+
19
+ constructor(inner: HostWallet, options: ProvaWalletOptions) {
20
+ this.inner = inner;
21
+ this.attester = options.attester;
22
+ this.batcher = createBatcher(options.attester, options.batch, options.onError);
23
+ }
24
+
25
+ get publicKey(): { toBase58(): string } {
26
+ return this.inner.publicKey;
27
+ }
28
+
29
+ signTransaction<T>(transaction: T): Promise<T> {
30
+ return this.inner.signTransaction(transaction);
31
+ }
32
+
33
+ signAllTransactions<T>(transactions: T[]): Promise<T[]> {
34
+ return this.inner.signAllTransactions(transactions);
35
+ }
36
+
37
+ signMessage(message: Uint8Array): Promise<Uint8Array> {
38
+ return this.inner.signMessage(message);
39
+ }
40
+
41
+ async signAndSendTransaction<T>(transaction: T, options?: unknown): Promise<{ signature: string }> {
42
+ const result = await this.inner.signAndSendTransaction(transaction, options);
43
+ // Atestación NO bloqueante de la firma on-chain real.
44
+ void this.captureSignature(result.signature).catch(() => {});
45
+ return result;
46
+ }
47
+
48
+ /** Fuerza el envío de las atestaciones pendientes. */
49
+ flush(): Promise<void> {
50
+ return this.batcher.flush();
51
+ }
52
+
53
+ /** Detiene el batcher y hace un flush final. */
54
+ stop(): Promise<void> {
55
+ return this.batcher.stop();
56
+ }
57
+
58
+ private async captureSignature(signature: string): Promise<void> {
59
+ const payload = { kind: 'transaction', signature, source: 'wallet' };
60
+ const actionHash = await this.attester.hashAction(JSON.stringify(payload));
61
+ this.batcher.add({ actionHash, actionType: 'Transaction' });
62
+ }
63
+ }