prova-agent-kit 0.1.1 → 0.1.2
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 +4 -2
- package/dist/batcher.d.ts +2 -2
- package/dist/batcher.d.ts.map +1 -1
- package/dist/batcher.js +29 -15
- package/dist/batcher.js.map +1 -1
- package/dist/runtime.d.ts +3 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +7 -7
- package/dist/runtime.js.map +1 -1
- package/dist/types.d.ts +7 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/attach.test.ts +2 -2
- package/src/batcher.test.ts +70 -0
- package/src/batcher.ts +32 -17
- package/src/runtime.ts +7 -7
- package/src/types.ts +7 -3
package/README.md
CHANGED
|
@@ -50,17 +50,19 @@ const agent = new SolanaAgentKit(new ProvaWallet(realWallet, { attester }), RPC_
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
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.
|
|
53
|
+
Las atestaciones son **fire-and-forget con batching por debounce**: un fallo de Prova nunca rompe ni frena una acción del agente. Una ráfaga de tool calls (multi-tool-calling) se agrupa y se envía en **UNA sola tx** (`batchAttest`, hasta 100) apenas la ráfaga se detiene (`flushDelayMs`), o al llegar a `maxSize`.
|
|
54
54
|
|
|
55
55
|
```typescript
|
|
56
56
|
attachProva(agent, {
|
|
57
57
|
attester,
|
|
58
58
|
rules: (name) => name !== 'fetchPrice', // qué acciones atestar (default: todas)
|
|
59
|
-
batch: { maxSize: 25,
|
|
59
|
+
batch: { maxSize: 25, flushDelayMs: 1000 }, // agrupa la ráfaga y envía ~1s tras la última acción
|
|
60
60
|
onError: (err, ctx) => logger.warn(ctx.action, err),
|
|
61
61
|
});
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
> **Importante:** llama a `handle.stop()` cuando el agente termine su turno para garantizar el flush final de lo pendiente.
|
|
65
|
+
|
|
64
66
|
## Modelo de claves
|
|
65
67
|
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
68
|
|
package/dist/batcher.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { AttestationItem, BatchOptions, ProvaAttester } from './types';
|
|
2
2
|
export interface Batcher {
|
|
3
|
-
/** Encola una atestación;
|
|
3
|
+
/** Encola una atestación; programa el flush (o lo dispara si se alcanza maxSize). */
|
|
4
4
|
add(item: AttestationItem): void;
|
|
5
5
|
/** Envía inmediatamente lo pendiente. */
|
|
6
6
|
flush(): Promise<void>;
|
|
7
|
-
/**
|
|
7
|
+
/** Cancela el timer y hace un último flush (llamar al cerrar el agente). */
|
|
8
8
|
stop(): Promise<void>;
|
|
9
9
|
}
|
|
10
10
|
export declare function createBatcher(attester: ProvaAttester, options?: BatchOptions, onError?: (error: unknown) => void): Batcher;
|
package/dist/batcher.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batcher.d.ts","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"batcher.d.ts","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG5E,MAAM,WAAW,OAAO;IACtB,qFAAqF;IACrF,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,yCAAyC;IACzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,4EAA4E;IAC5E,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,CAwDT"}
|
package/dist/batcher.js
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Buffer de atestaciones
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// Buffer de atestaciones con flush por DEBOUNCE: agrupa una ráfaga de acciones
|
|
3
|
+
// (multi-tool-calling) y las envía en UNA sola tx (batchAttest, hasta 100) apenas
|
|
4
|
+
// la ráfaga se detiene. Esto arregla el bug de "N tool calls seguidos → 0 guardadas":
|
|
5
|
+
// antes el flush solo ocurría a los 25 items o cada 10s, así que una ráfaga corta
|
|
6
|
+
// que terminaba antes quedaba sin enviar.
|
|
5
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
8
|
exports.createBatcher = createBatcher;
|
|
7
9
|
const runtime_1 = require("./runtime");
|
|
8
10
|
const DEFAULT_MAX_SIZE = 25;
|
|
9
|
-
const
|
|
11
|
+
const DEFAULT_FLUSH_DELAY_MS = 1000;
|
|
10
12
|
const HARD_BATCH_LIMIT = 100;
|
|
11
13
|
function createBatcher(attester, options = {}, onError = (error) => (0, runtime_1.warn)('[Prova] batch error:', error)) {
|
|
12
14
|
const maxSize = Math.min(Math.max(options.maxSize ?? DEFAULT_MAX_SIZE, 1), HARD_BATCH_LIMIT);
|
|
13
|
-
const
|
|
15
|
+
const flushDelayMs = options.flushDelayMs ?? DEFAULT_FLUSH_DELAY_MS;
|
|
14
16
|
let buffer = [];
|
|
15
17
|
let timer;
|
|
18
|
+
function clearTimer() {
|
|
19
|
+
if (timer !== undefined) {
|
|
20
|
+
(0, runtime_1.clearTimeoutSafe)(timer);
|
|
21
|
+
timer = undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
16
24
|
async function flush() {
|
|
25
|
+
clearTimer();
|
|
17
26
|
if (buffer.length === 0)
|
|
18
27
|
return;
|
|
19
28
|
const batch = buffer;
|
|
@@ -30,24 +39,29 @@ function createBatcher(attester, options = {}, onError = (error) => (0, runtime_
|
|
|
30
39
|
onError(error);
|
|
31
40
|
}
|
|
32
41
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
// Debounce: cada acción reinicia el timer, de modo que el flush ocurre
|
|
43
|
+
// `flushDelayMs` ms DESPUÉS de la última acción de la ráfaga → todo en 1 tx.
|
|
44
|
+
function scheduleFlush() {
|
|
45
|
+
clearTimer();
|
|
46
|
+
if (flushDelayMs <= 0) {
|
|
47
|
+
void flush();
|
|
48
|
+
return;
|
|
36
49
|
}
|
|
50
|
+
timer = (0, runtime_1.setTimeoutSafe)(() => void flush(), flushDelayMs);
|
|
37
51
|
}
|
|
38
52
|
return {
|
|
39
53
|
add(item) {
|
|
40
54
|
buffer.push(item);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
if (buffer.length >= maxSize) {
|
|
56
|
+
void flush(); // llegó al tope → envía ya
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
scheduleFlush(); // si no, agrupa la ráfaga y envía al detenerse
|
|
60
|
+
}
|
|
44
61
|
},
|
|
45
62
|
flush,
|
|
46
63
|
async stop() {
|
|
47
|
-
|
|
48
|
-
(0, runtime_1.clearIntervalSafe)(timer);
|
|
49
|
-
timer = undefined;
|
|
50
|
-
}
|
|
64
|
+
clearTimer();
|
|
51
65
|
await flush();
|
|
52
66
|
},
|
|
53
67
|
};
|
package/dist/batcher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batcher.js","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,
|
|
1
|
+
{"version":3,"file":"batcher.js","sourceRoot":"","sources":["../src/batcher.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,kFAAkF;AAClF,sFAAsF;AACtF,kFAAkF;AAClF,0CAA0C;;AAkB1C,sCA4DC;AA3ED,uCAAiF;AAWjF,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,SAAgB,aAAa,CAC3B,QAAuB,EACvB,UAAwB,EAAE,EAC1B,UAAoC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,cAAI,EAAC,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,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,sBAAsB,CAAC;IAEpE,IAAI,MAAM,GAAsB,EAAE,CAAC;IACnC,IAAI,KAA0B,CAAC;IAE/B,SAAS,UAAU;QACjB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAA,0BAAgB,EAAC,KAAK,CAAC,CAAC;YACxB,KAAK,GAAG,SAAS,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,UAAU,KAAK;QAClB,UAAU,EAAE,CAAC;QACb,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,uEAAuE;IACvE,6EAA6E;IAC7E,SAAS,aAAa;QACpB,UAAU,EAAE,CAAC;QACb,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;YACtB,KAAK,KAAK,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QACD,KAAK,GAAG,IAAA,wBAAc,EAAC,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO;QACL,GAAG,CAAC,IAAqB;YACvB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,MAAM,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,KAAK,KAAK,EAAE,CAAC,CAAC,2BAA2B;YAC3C,CAAC;iBAAM,CAAC;gBACN,aAAa,EAAE,CAAC,CAAC,+CAA+C;YAClE,CAAC;QACH,CAAC;QACD,KAAK;QACL,KAAK,CAAC,IAAI;YACR,UAAU,EAAE,CAAC;YACb,MAAM,KAAK,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
export type TimerId = number | {
|
|
3
3
|
readonly __timer?: unique symbol;
|
|
4
4
|
};
|
|
5
|
-
/**
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
5
|
+
/** setTimeout que no bloquea la salida del proceso (unref en Node si existe). */
|
|
6
|
+
export declare function setTimeoutSafe(handler: () => void, ms: number): TimerId;
|
|
7
|
+
export declare function clearTimeoutSafe(id: TimerId): void;
|
|
8
8
|
export declare function warn(...args: unknown[]): void;
|
|
9
9
|
//# sourceMappingURL=runtime.d.ts.map
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAKvE;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAElD;AAED,wBAAgB,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAE7C"}
|
package/dist/runtime.js
CHANGED
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
// @types/node ni de los tipos del DOM. En ejecución real los provee Node o el
|
|
4
4
|
// navegador; aquí los declaramos mínimamente para mantener el adapter ligero.
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
6
|
+
exports.setTimeoutSafe = setTimeoutSafe;
|
|
7
|
+
exports.clearTimeoutSafe = clearTimeoutSafe;
|
|
8
8
|
exports.warn = warn;
|
|
9
9
|
const runtime = globalThis;
|
|
10
|
-
/**
|
|
11
|
-
function
|
|
12
|
-
const id = runtime.
|
|
10
|
+
/** setTimeout que no bloquea la salida del proceso (unref en Node si existe). */
|
|
11
|
+
function setTimeoutSafe(handler, ms) {
|
|
12
|
+
const id = runtime.setTimeout(handler, ms);
|
|
13
13
|
const maybe = id;
|
|
14
14
|
if (typeof maybe.unref === 'function')
|
|
15
15
|
maybe.unref();
|
|
16
16
|
return id;
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
runtime.
|
|
18
|
+
function clearTimeoutSafe(id) {
|
|
19
|
+
runtime.clearTimeout(id);
|
|
20
20
|
}
|
|
21
21
|
function warn(...args) {
|
|
22
22
|
runtime.console.warn(...args);
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":";AAAA,wEAAwE;AACxE,8EAA8E;AAC9E,8EAA8E;;AAc9E,
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":";AAAA,wEAAwE;AACxE,8EAA8E;AAC9E,8EAA8E;;AAc9E,wCAKC;AAED,4CAEC;AAED,oBAEC;AAhBD,MAAM,OAAO,GAAG,UAAuC,CAAC;AAExD,iFAAiF;AACjF,SAAgB,cAAc,CAAC,OAAmB,EAAE,EAAU;IAC5D,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC3C,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,SAAgB,gBAAgB,CAAC,EAAW;IAC1C,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,SAAgB,IAAI,CAAC,GAAG,IAAe;IACrC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AAChC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -45,10 +45,14 @@ export interface ProvaAttester {
|
|
|
45
45
|
}
|
|
46
46
|
/** Configuración del batching de atestaciones. */
|
|
47
47
|
export interface BatchOptions {
|
|
48
|
-
/** Nº máximo de atestaciones por transacción (1–100). Default 25. */
|
|
48
|
+
/** Nº máximo de atestaciones por transacción (1–100). Al alcanzarlo, flush inmediato. Default 25. */
|
|
49
49
|
maxSize?: number;
|
|
50
|
-
/**
|
|
51
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Debounce: hace flush este nº de ms tras la ÚLTIMA acción. Así una ráfaga de
|
|
52
|
+
* tool calls (multi-tool-calling) se agrupa en UNA sola tx on-chain apenas
|
|
53
|
+
* termina la ráfaga. Default 1000. 0 = flush inmediato en cada acción (sin batch).
|
|
54
|
+
*/
|
|
55
|
+
flushDelayMs?: number;
|
|
52
56
|
}
|
|
53
57
|
/** Opciones de `attachProva`. */
|
|
54
58
|
export interface AttachProvaOptions {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,qGAAqG;IACrG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;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/package.json
CHANGED
package/src/attach.test.ts
CHANGED
|
@@ -29,7 +29,7 @@ describe('attachProva', () => {
|
|
|
29
29
|
|
|
30
30
|
const handle = attachProva(agent, {
|
|
31
31
|
attester,
|
|
32
|
-
batch: { maxSize: 1,
|
|
32
|
+
batch: { maxSize: 1, flushDelayMs: 0 },
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
const result = await agent.actions[0]!.handler(agent, { amount: 10 });
|
|
@@ -72,7 +72,7 @@ describe('attachProva', () => {
|
|
|
72
72
|
const handle = attachProva(agent, {
|
|
73
73
|
attester,
|
|
74
74
|
rules: (name) => name === 'trade',
|
|
75
|
-
batch: { maxSize: 1,
|
|
75
|
+
batch: { maxSize: 1, flushDelayMs: 0 },
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
await agent.actions[0]!.handler(agent, {});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createBatcher } from './batcher';
|
|
3
|
+
import type { AttestationItem, ProvaAttester } from './types';
|
|
4
|
+
|
|
5
|
+
function recordingAttester() {
|
|
6
|
+
const attests: AttestationItem[] = [];
|
|
7
|
+
const batches: AttestationItem[][] = [];
|
|
8
|
+
const attester: ProvaAttester = {
|
|
9
|
+
hashAction: async () => new Uint8Array(32),
|
|
10
|
+
attest: async (item) => {
|
|
11
|
+
attests.push(item);
|
|
12
|
+
return { txSignature: 'tx' };
|
|
13
|
+
},
|
|
14
|
+
batchAttest: async (items) => {
|
|
15
|
+
batches.push(items);
|
|
16
|
+
return { txSignature: 'tx' };
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
return { attester, attests, batches };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const item = (): AttestationItem => ({ actionHash: new Uint8Array(32), actionType: 'ToolCall' });
|
|
23
|
+
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
24
|
+
|
|
25
|
+
describe('createBatcher — multi-tool-calling (bug del mentor)', () => {
|
|
26
|
+
it('agrupa una RÁFAGA de 11 acciones en UNA sola batchAttest', async () => {
|
|
27
|
+
const { attester, attests, batches } = recordingAttester();
|
|
28
|
+
const batcher = createBatcher(attester, { maxSize: 25, flushDelayMs: 20 });
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < 11; i++) batcher.add(item());
|
|
31
|
+
|
|
32
|
+
await wait(50); // pasa el debounce
|
|
33
|
+
expect(batches).toHaveLength(1); // UNA sola tx
|
|
34
|
+
expect(batches[0]).toHaveLength(11); // con las 11 dentro
|
|
35
|
+
expect(attests).toHaveLength(0); // ninguna suelta
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('no pierde nada si el agente cierra antes del debounce (stop hace flush)', async () => {
|
|
39
|
+
const { attester, batches } = recordingAttester();
|
|
40
|
+
const batcher = createBatcher(attester, { maxSize: 25, flushDelayMs: 5000 });
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < 11; i++) batcher.add(item());
|
|
43
|
+
await batcher.stop(); // cierre inmediato
|
|
44
|
+
|
|
45
|
+
expect(batches).toHaveLength(1);
|
|
46
|
+
expect(batches[0]).toHaveLength(11);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('hace flush inmediato al alcanzar maxSize', async () => {
|
|
50
|
+
const { attester, batches } = recordingAttester();
|
|
51
|
+
const batcher = createBatcher(attester, { maxSize: 5, flushDelayMs: 5000 });
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < 5; i++) batcher.add(item());
|
|
54
|
+
await wait(0);
|
|
55
|
+
|
|
56
|
+
expect(batches).toHaveLength(1);
|
|
57
|
+
expect(batches[0]).toHaveLength(5);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('una sola acción usa attest (no batchAttest)', async () => {
|
|
61
|
+
const { attester, attests, batches } = recordingAttester();
|
|
62
|
+
const batcher = createBatcher(attester, { flushDelayMs: 10 });
|
|
63
|
+
|
|
64
|
+
batcher.add(item());
|
|
65
|
+
await wait(30);
|
|
66
|
+
|
|
67
|
+
expect(attests).toHaveLength(1);
|
|
68
|
+
expect(batches).toHaveLength(0);
|
|
69
|
+
});
|
|
70
|
+
});
|
package/src/batcher.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
// Buffer de atestaciones
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Buffer de atestaciones con flush por DEBOUNCE: agrupa una ráfaga de acciones
|
|
2
|
+
// (multi-tool-calling) y las envía en UNA sola tx (batchAttest, hasta 100) apenas
|
|
3
|
+
// la ráfaga se detiene. Esto arregla el bug de "N tool calls seguidos → 0 guardadas":
|
|
4
|
+
// antes el flush solo ocurría a los 25 items o cada 10s, así que una ráfaga corta
|
|
5
|
+
// que terminaba antes quedaba sin enviar.
|
|
4
6
|
|
|
5
7
|
import type { AttestationItem, BatchOptions, ProvaAttester } from './types';
|
|
6
|
-
import {
|
|
8
|
+
import { clearTimeoutSafe, setTimeoutSafe, warn, type TimerId } from './runtime';
|
|
7
9
|
|
|
8
10
|
export interface Batcher {
|
|
9
|
-
/** Encola una atestación;
|
|
11
|
+
/** Encola una atestación; programa el flush (o lo dispara si se alcanza maxSize). */
|
|
10
12
|
add(item: AttestationItem): void;
|
|
11
13
|
/** Envía inmediatamente lo pendiente. */
|
|
12
14
|
flush(): Promise<void>;
|
|
13
|
-
/**
|
|
15
|
+
/** Cancela el timer y hace un último flush (llamar al cerrar el agente). */
|
|
14
16
|
stop(): Promise<void>;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
const DEFAULT_MAX_SIZE = 25;
|
|
18
|
-
const
|
|
20
|
+
const DEFAULT_FLUSH_DELAY_MS = 1000;
|
|
19
21
|
const HARD_BATCH_LIMIT = 100;
|
|
20
22
|
|
|
21
23
|
export function createBatcher(
|
|
@@ -24,12 +26,20 @@ export function createBatcher(
|
|
|
24
26
|
onError: (error: unknown) => void = (error) => warn('[Prova] batch error:', error),
|
|
25
27
|
): Batcher {
|
|
26
28
|
const maxSize = Math.min(Math.max(options.maxSize ?? DEFAULT_MAX_SIZE, 1), HARD_BATCH_LIMIT);
|
|
27
|
-
const
|
|
29
|
+
const flushDelayMs = options.flushDelayMs ?? DEFAULT_FLUSH_DELAY_MS;
|
|
28
30
|
|
|
29
31
|
let buffer: AttestationItem[] = [];
|
|
30
32
|
let timer: TimerId | undefined;
|
|
31
33
|
|
|
34
|
+
function clearTimer(): void {
|
|
35
|
+
if (timer !== undefined) {
|
|
36
|
+
clearTimeoutSafe(timer);
|
|
37
|
+
timer = undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
async function flush(): Promise<void> {
|
|
42
|
+
clearTimer();
|
|
33
43
|
if (buffer.length === 0) return;
|
|
34
44
|
const batch = buffer;
|
|
35
45
|
buffer = [];
|
|
@@ -44,24 +54,29 @@ export function createBatcher(
|
|
|
44
54
|
}
|
|
45
55
|
}
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
// Debounce: cada acción reinicia el timer, de modo que el flush ocurre
|
|
58
|
+
// `flushDelayMs` ms DESPUÉS de la última acción de la ráfaga → todo en 1 tx.
|
|
59
|
+
function scheduleFlush(): void {
|
|
60
|
+
clearTimer();
|
|
61
|
+
if (flushDelayMs <= 0) {
|
|
62
|
+
void flush();
|
|
63
|
+
return;
|
|
50
64
|
}
|
|
65
|
+
timer = setTimeoutSafe(() => void flush(), flushDelayMs);
|
|
51
66
|
}
|
|
52
67
|
|
|
53
68
|
return {
|
|
54
69
|
add(item: AttestationItem): void {
|
|
55
70
|
buffer.push(item);
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
if (buffer.length >= maxSize) {
|
|
72
|
+
void flush(); // llegó al tope → envía ya
|
|
73
|
+
} else {
|
|
74
|
+
scheduleFlush(); // si no, agrupa la ráfaga y envía al detenerse
|
|
75
|
+
}
|
|
58
76
|
},
|
|
59
77
|
flush,
|
|
60
78
|
async stop(): Promise<void> {
|
|
61
|
-
|
|
62
|
-
clearIntervalSafe(timer);
|
|
63
|
-
timer = undefined;
|
|
64
|
-
}
|
|
79
|
+
clearTimer();
|
|
65
80
|
await flush();
|
|
66
81
|
},
|
|
67
82
|
};
|
package/src/runtime.ts
CHANGED
|
@@ -6,23 +6,23 @@
|
|
|
6
6
|
export type TimerId = number | { readonly __timer?: unique symbol };
|
|
7
7
|
|
|
8
8
|
interface RuntimeGlobals {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
setTimeout(handler: () => void, ms: number): TimerId;
|
|
10
|
+
clearTimeout(id: TimerId): void;
|
|
11
11
|
console: { warn(...args: unknown[]): void };
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const runtime = globalThis as unknown as RuntimeGlobals;
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
export function
|
|
18
|
-
const id = runtime.
|
|
16
|
+
/** setTimeout que no bloquea la salida del proceso (unref en Node si existe). */
|
|
17
|
+
export function setTimeoutSafe(handler: () => void, ms: number): TimerId {
|
|
18
|
+
const id = runtime.setTimeout(handler, ms);
|
|
19
19
|
const maybe = id as unknown as { unref?: () => void };
|
|
20
20
|
if (typeof maybe.unref === 'function') maybe.unref();
|
|
21
21
|
return id;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function
|
|
25
|
-
runtime.
|
|
24
|
+
export function clearTimeoutSafe(id: TimerId): void {
|
|
25
|
+
runtime.clearTimeout(id);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export function warn(...args: unknown[]): void {
|
package/src/types.ts
CHANGED
|
@@ -56,10 +56,14 @@ export interface ProvaAttester {
|
|
|
56
56
|
|
|
57
57
|
/** Configuración del batching de atestaciones. */
|
|
58
58
|
export interface BatchOptions {
|
|
59
|
-
/** Nº máximo de atestaciones por transacción (1–100). Default 25. */
|
|
59
|
+
/** Nº máximo de atestaciones por transacción (1–100). Al alcanzarlo, flush inmediato. Default 25. */
|
|
60
60
|
maxSize?: number;
|
|
61
|
-
/**
|
|
62
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Debounce: hace flush este nº de ms tras la ÚLTIMA acción. Así una ráfaga de
|
|
63
|
+
* tool calls (multi-tool-calling) se agrupa en UNA sola tx on-chain apenas
|
|
64
|
+
* termina la ráfaga. Default 1000. 0 = flush inmediato en cada acción (sin batch).
|
|
65
|
+
*/
|
|
66
|
+
flushDelayMs?: number;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
/** Opciones de `attachProva`. */
|