@kaleidorg/mind 0.6.0 → 0.6.1
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/dist/bitrefill/contract.d.ts +60 -0
- package/dist/bitrefill/contract.d.ts.map +1 -0
- package/dist/bitrefill/contract.js +119 -0
- package/dist/bitrefill/contract.js.map +1 -0
- package/dist/context/compress.d.ts +65 -0
- package/dist/context/compress.d.ts.map +1 -0
- package/dist/context/compress.js +181 -0
- package/dist/context/compress.js.map +1 -0
- package/dist/engine.d.ts +20 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +23 -4
- package/dist/engine.js.map +1 -1
- package/dist/evidence.d.ts +62 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +47 -0
- package/dist/evidence.js.map +1 -0
- package/dist/flashnet/contract.d.ts +56 -0
- package/dist/flashnet/contract.d.ts.map +1 -0
- package/dist/flashnet/contract.js +100 -0
- package/dist/flashnet/contract.js.map +1 -0
- package/dist/funnel.d.ts +11 -0
- package/dist/funnel.d.ts.map +1 -1
- package/dist/funnel.js +50 -7
- package/dist/funnel.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/kaleidoswap/contract.js +1 -1
- package/dist/kaleidoswap/contract.js.map +1 -1
- package/dist/knowledge/bitcoin-copilot.d.ts.map +1 -1
- package/dist/knowledge/bitcoin-copilot.js +83 -0
- package/dist/knowledge/bitcoin-copilot.js.map +1 -1
- package/dist/providers/types.d.ts +17 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/qvac/provider.d.ts.map +1 -1
- package/dist/qvac/provider.js +23 -0
- package/dist/qvac/provider.js.map +1 -1
- package/dist/qvac/stream.d.ts +6 -0
- package/dist/qvac/stream.d.ts.map +1 -1
- package/dist/qvac/stream.js +12 -0
- package/dist/qvac/stream.js.map +1 -1
- package/dist/recipe/flashnet-swap.d.ts +35 -0
- package/dist/recipe/flashnet-swap.d.ts.map +1 -0
- package/dist/recipe/flashnet-swap.js +239 -0
- package/dist/recipe/flashnet-swap.js.map +1 -0
- package/dist/recipe/kaleidoswap-atomic.d.ts.map +1 -1
- package/dist/recipe/kaleidoswap-atomic.js +37 -16
- package/dist/recipe/kaleidoswap-atomic.js.map +1 -1
- package/dist/recipe/kaleidoswap-channel-order.d.ts.map +1 -1
- package/dist/recipe/kaleidoswap-channel-order.js +31 -10
- package/dist/recipe/kaleidoswap-channel-order.js.map +1 -1
- package/dist/recipe/kaleidoswap-price.d.ts.map +1 -1
- package/dist/recipe/kaleidoswap-price.js +7 -1
- package/dist/recipe/kaleidoswap-price.js.map +1 -1
- package/dist/recipe/runner.d.ts.map +1 -1
- package/dist/recipe/runner.js +5 -3
- package/dist/recipe/runner.js.map +1 -1
- package/dist/recipe/swap.d.ts.map +1 -1
- package/dist/recipe/swap.js +14 -1
- package/dist/recipe/swap.js.map +1 -1
- package/dist/wallet/confirm.d.ts.map +1 -1
- package/dist/wallet/confirm.js +1 -0
- package/dist/wallet/confirm.js.map +1 -1
- package/dist/wallet/contract.d.ts.map +1 -1
- package/dist/wallet/contract.js +20 -4
- package/dist/wallet/contract.js.map +1 -1
- package/package.json +4 -4
- package/skills/bitrefill/SKILL.md +152 -52
- package/skills/flashnet-swaps/SKILL.md +158 -0
- package/skills/kaleido-lsps/SKILL.md +25 -8
- package/skills/kaleido-trading/SKILL.md +36 -12
- package/skills/merchant-finder/SKILL.md +1 -1
- package/skills/rgb-lightning-node/SKILL.md +35 -8
- package/skills/spark-wallet/SKILL.md +235 -0
- package/skills/wallet-assistant/SKILL.md +2 -2
- package/src/bitrefill/contract.test.ts +89 -0
- package/src/bitrefill/contract.ts +190 -0
- package/src/context/compress.test.ts +120 -0
- package/src/context/compress.ts +230 -0
- package/src/engine.test.ts +34 -0
- package/src/engine.ts +35 -4
- package/src/evidence.test.ts +80 -0
- package/src/evidence.ts +114 -0
- package/src/flashnet/contract.test.ts +101 -0
- package/src/flashnet/contract.ts +164 -0
- package/src/funnel.ts +59 -8
- package/src/index.ts +51 -1
- package/src/kaleidoswap/contract.ts +1 -1
- package/src/knowledge/bitcoin-copilot.ts +94 -0
- package/src/providers/types.ts +18 -0
- package/src/qvac/provider.ts +25 -1
- package/src/qvac/stream.test.ts +11 -0
- package/src/qvac/stream.ts +16 -0
- package/src/recipe/flashnet-swap.test.ts +114 -0
- package/src/recipe/flashnet-swap.ts +266 -0
- package/src/recipe/kaleidoswap-atomic.test.ts +21 -0
- package/src/recipe/kaleidoswap-atomic.ts +34 -16
- package/src/recipe/kaleidoswap-channel-order.test.ts +38 -0
- package/src/recipe/kaleidoswap-channel-order.ts +27 -9
- package/src/recipe/kaleidoswap-price.ts +7 -1
- package/src/recipe/recipe.test.ts +5 -0
- package/src/recipe/runner.ts +5 -3
- package/src/recipe/swap.ts +16 -1
- package/src/wallet/confirm.test.ts +8 -0
- package/src/wallet/confirm.ts +1 -0
- package/src/wallet/contract.test.ts +10 -0
- package/src/wallet/contract.ts +26 -4
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Bitrefill tool contract — gift cards, mobile top-ups, eSIMs.
|
|
3
|
+
*
|
|
4
|
+
* Same pattern as the LSPS1 contract: the tool *names + schemas* live here so
|
|
5
|
+
* every host (CLI REST adapter, desktop MCP, mobile WDK adapter) exposes the
|
|
6
|
+
* exact same surface to the agent. Only the transport differs.
|
|
7
|
+
*
|
|
8
|
+
* - CLI / desktop server → REST against `https://api.bitrefill.com/v2`
|
|
9
|
+
* (see `apps/cli/src/bitrefillTools.ts`).
|
|
10
|
+
* - Desktop sidecar → the remote MCP at `api.bitrefill.com/mcp` already
|
|
11
|
+
* exposes equivalent tools under different names; a binder there can
|
|
12
|
+
* rename them to this contract for parity.
|
|
13
|
+
*
|
|
14
|
+
* `bitrefill_create_invoice` is the spend — confirmation-gated by the contract.
|
|
15
|
+
* Everything else is read-only (search, product details, balance, invoice/order
|
|
16
|
+
* status). Invoice creation supports `payment_method:"balance"` (instant, pulls
|
|
17
|
+
* from pre-funded account) or `lightning|bitcoin|usdc_base|...` (the response
|
|
18
|
+
* carries a payment URI/invoice; the user pays out-of-band, then the order is
|
|
19
|
+
* fulfilled).
|
|
20
|
+
*
|
|
21
|
+
* Pure data — no deps, no fetch, RN-safe.
|
|
22
|
+
*/
|
|
23
|
+
import type { ToolDef } from '../types.js';
|
|
24
|
+
import { InProcessToolSource } from '../tools/in-process.js';
|
|
25
|
+
export interface BitrefillToolDef extends ToolDef {
|
|
26
|
+
/** Moves real money → confirmation-gated. */
|
|
27
|
+
spend?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The canonical Bitrefill tool list. Each host's binder translates these
|
|
31
|
+
* args into the Bitrefill REST body (CLI) or MCP/CLI call (other hosts).
|
|
32
|
+
*/
|
|
33
|
+
export declare const BITREFILL_TOOLS: BitrefillToolDef[];
|
|
34
|
+
/** All Bitrefill tool names that move money (confirmation-gated). */
|
|
35
|
+
export declare const BITREFILL_SPEND_TOOLS: Set<string>;
|
|
36
|
+
export declare function isBitrefillSpendTool(name: string): boolean;
|
|
37
|
+
export declare function getBitrefillTool(name: string): BitrefillToolDef | undefined;
|
|
38
|
+
/** A handler bound to one Bitrefill tool. */
|
|
39
|
+
export type BitrefillHandler = (args: Record<string, unknown>) => Promise<unknown>;
|
|
40
|
+
export interface BindBitrefillOptions {
|
|
41
|
+
/** Skip tools without a handler instead of throwing (default false). */
|
|
42
|
+
allowMissing?: boolean;
|
|
43
|
+
/** ToolSource id for the registry (default 'bitrefill'). */
|
|
44
|
+
id?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Bind Bitrefill contract tools to in-process handlers → an InProcessToolSource.
|
|
48
|
+
*
|
|
49
|
+
* const source = bindBitrefillTools({
|
|
50
|
+
* bitrefill_search: async (args) => api.search(args),
|
|
51
|
+
* bitrefill_get_product: async ({ product_id }) => api.product(product_id),
|
|
52
|
+
* bitrefill_get_balance: async () => api.balance(),
|
|
53
|
+
* bitrefill_create_invoice: async (args) => api.createInvoice(args),
|
|
54
|
+
* bitrefill_get_invoice: async ({ invoice_id }) => api.invoice(invoice_id),
|
|
55
|
+
* bitrefill_get_order: async ({ order_id }) => api.order(order_id),
|
|
56
|
+
* });
|
|
57
|
+
* tools.register(source);
|
|
58
|
+
*/
|
|
59
|
+
export declare function bindBitrefillTools(handlers: Record<string, BitrefillHandler>, opts?: BindBitrefillOptions): InProcessToolSource;
|
|
60
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/bitrefill/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG7D,MAAM,WAAW,gBAAiB,SAAQ,OAAO;IAC/C,6CAA6C;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAuBD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,gBAAgB,EA0E7C,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAE7C,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE3E;AAED,6CAA6C;AAC7C,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEnF,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4DAA4D;IAC5D,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC1C,IAAI,GAAE,oBAAyB,GAC9B,mBAAmB,CAiBrB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Bitrefill tool contract — gift cards, mobile top-ups, eSIMs.
|
|
3
|
+
*
|
|
4
|
+
* Same pattern as the LSPS1 contract: the tool *names + schemas* live here so
|
|
5
|
+
* every host (CLI REST adapter, desktop MCP, mobile WDK adapter) exposes the
|
|
6
|
+
* exact same surface to the agent. Only the transport differs.
|
|
7
|
+
*
|
|
8
|
+
* - CLI / desktop server → REST against `https://api.bitrefill.com/v2`
|
|
9
|
+
* (see `apps/cli/src/bitrefillTools.ts`).
|
|
10
|
+
* - Desktop sidecar → the remote MCP at `api.bitrefill.com/mcp` already
|
|
11
|
+
* exposes equivalent tools under different names; a binder there can
|
|
12
|
+
* rename them to this contract for parity.
|
|
13
|
+
*
|
|
14
|
+
* `bitrefill_create_invoice` is the spend — confirmation-gated by the contract.
|
|
15
|
+
* Everything else is read-only (search, product details, balance, invoice/order
|
|
16
|
+
* status). Invoice creation supports `payment_method:"balance"` (instant, pulls
|
|
17
|
+
* from pre-funded account) or `lightning|bitcoin|usdc_base|...` (the response
|
|
18
|
+
* carries a payment URI/invoice; the user pays out-of-band, then the order is
|
|
19
|
+
* fulfilled).
|
|
20
|
+
*
|
|
21
|
+
* Pure data — no deps, no fetch, RN-safe.
|
|
22
|
+
*/
|
|
23
|
+
import { InProcessToolSource } from '../tools/in-process.js';
|
|
24
|
+
function t(name, description, properties = {}, required = [], spend = false) {
|
|
25
|
+
return {
|
|
26
|
+
name,
|
|
27
|
+
description,
|
|
28
|
+
spend,
|
|
29
|
+
requiresConfirmation: spend,
|
|
30
|
+
parameters: { type: 'object', properties, required },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The canonical Bitrefill tool list. Each host's binder translates these
|
|
35
|
+
* args into the Bitrefill REST body (CLI) or MCP/CLI call (other hosts).
|
|
36
|
+
*/
|
|
37
|
+
export const BITREFILL_TOOLS = [
|
|
38
|
+
t('bitrefill_search', "Search Bitrefill's product catalog by keyword (brand, country, type). Returns up to ~20 matches with `id`, `name`, `country`, `category` and `denominations`. The model picks the right product id and then calls `bitrefill_get_product` for the package list.", {
|
|
39
|
+
query: { type: 'string', description: 'Search keyword. e.g. "amazon", "steam", "vodafone uk", "esim europe".' },
|
|
40
|
+
country: { type: 'string', description: 'OPTIONAL — ISO country code to scope results (e.g. "US", "GB", "DE"). Many brands are country-specific.' },
|
|
41
|
+
limit: { type: 'number', description: 'OPTIONAL — max results (1–25, default 10).' },
|
|
42
|
+
}, ['query']),
|
|
43
|
+
t('bitrefill_get_product', "Get full details for one product, including its `packages` array (each package = a denomination with `id`, `value`, `price`, `currency`). Use the package `id` (NOT the bare value) when creating an invoice.", {
|
|
44
|
+
product_id: { type: 'string', description: 'Product slug from bitrefill_search, e.g. "amazon-us", "steam-us".' },
|
|
45
|
+
}, ['product_id']),
|
|
46
|
+
t('bitrefill_get_balance', "Get the user's Bitrefill account balance (the pre-funded pool used by `payment_method:\"balance\"`). Returns `{ balance, currency }`. No args."),
|
|
47
|
+
t('bitrefill_create_invoice', "SPEND: confirmation-gated. Create an invoice for one or more products. Pass `payment_method:\"balance\"` + `auto_pay:true` for instant fulfillment from the account balance (lowest blast radius). For Lightning/on-chain, omit `auto_pay`, set `payment_method:\"lightning\"` (etc.) and `refund_address` — the response carries the payment URI; poll `bitrefill_get_invoice` until status=\"complete\" and then read the order. Up to 20 line items per invoice.", {
|
|
48
|
+
products: {
|
|
49
|
+
type: 'array',
|
|
50
|
+
description: 'Line items. Each: { product_id, package_id, quantity }. Get `package_id` from bitrefill_get_product (NOT the bare denomination value).',
|
|
51
|
+
items: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
product_id: { type: 'string' },
|
|
55
|
+
package_id: { type: 'string' },
|
|
56
|
+
quantity: { type: 'number' },
|
|
57
|
+
},
|
|
58
|
+
required: ['product_id', 'package_id', 'quantity'],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
payment_method: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
description: 'How to pay: "balance" (account balance, instant), "lightning", "bitcoin", "usdc_base" (x402), "usdc_polygon", "usdt_tron", etc.',
|
|
64
|
+
enum: ['balance', 'lightning', 'bitcoin', 'usdc_base', 'usdc_polygon', 'usdc_ethereum', 'usdt_tron', 'usdt_ethereum'],
|
|
65
|
+
},
|
|
66
|
+
auto_pay: { type: 'boolean', description: 'Required true with `payment_method:"balance"` for instant settlement. Omit for crypto methods.' },
|
|
67
|
+
refund_address: { type: 'string', description: 'REQUIRED for non-balance crypto methods — refund destination if the invoice expires or partially pays.' },
|
|
68
|
+
email: { type: 'string', description: 'OPTIONAL — delivery / receipt email. Defaults to the account email when authenticated.' },
|
|
69
|
+
webhook_url: { type: 'string', description: 'OPTIONAL — URL Bitrefill calls when the order is delivered.' },
|
|
70
|
+
}, ['products', 'payment_method'],
|
|
71
|
+
/* spend */ true),
|
|
72
|
+
t('bitrefill_get_invoice', "Get the invoice's current status: `unpaid`, `pending`, `paid`, `complete`, `expired`, `failed`. For crypto payment methods, poll this until `complete`; then call `bitrefill_get_order` for redemption details.", {
|
|
73
|
+
invoice_id: { type: 'string', description: 'Invoice id returned by bitrefill_create_invoice.' },
|
|
74
|
+
}, ['invoice_id']),
|
|
75
|
+
t('bitrefill_get_order', "Get an order's redemption details once delivered. Returns `redemption_info` containing the code, PIN (for prepaid cards), redemption link, instructions. ONLY call after the corresponding invoice status is `complete`. Treat the returned code as cash — never paste it in shared chats.", {
|
|
76
|
+
order_id: { type: 'string', description: 'Order id from a completed invoice (`order_id` on the invoice or in its `orders[]`).' },
|
|
77
|
+
}, ['order_id']),
|
|
78
|
+
];
|
|
79
|
+
/** All Bitrefill tool names that move money (confirmation-gated). */
|
|
80
|
+
export const BITREFILL_SPEND_TOOLS = new Set(BITREFILL_TOOLS.filter((t) => t.spend).map((t) => t.name));
|
|
81
|
+
export function isBitrefillSpendTool(name) {
|
|
82
|
+
return BITREFILL_SPEND_TOOLS.has(name);
|
|
83
|
+
}
|
|
84
|
+
export function getBitrefillTool(name) {
|
|
85
|
+
return BITREFILL_TOOLS.find((t) => t.name === name);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Bind Bitrefill contract tools to in-process handlers → an InProcessToolSource.
|
|
89
|
+
*
|
|
90
|
+
* const source = bindBitrefillTools({
|
|
91
|
+
* bitrefill_search: async (args) => api.search(args),
|
|
92
|
+
* bitrefill_get_product: async ({ product_id }) => api.product(product_id),
|
|
93
|
+
* bitrefill_get_balance: async () => api.balance(),
|
|
94
|
+
* bitrefill_create_invoice: async (args) => api.createInvoice(args),
|
|
95
|
+
* bitrefill_get_invoice: async ({ invoice_id }) => api.invoice(invoice_id),
|
|
96
|
+
* bitrefill_get_order: async ({ order_id }) => api.order(order_id),
|
|
97
|
+
* });
|
|
98
|
+
* tools.register(source);
|
|
99
|
+
*/
|
|
100
|
+
export function bindBitrefillTools(handlers, opts = {}) {
|
|
101
|
+
const bound = [];
|
|
102
|
+
for (const def of BITREFILL_TOOLS) {
|
|
103
|
+
const handler = handlers[def.name];
|
|
104
|
+
if (!handler) {
|
|
105
|
+
if (opts.allowMissing)
|
|
106
|
+
continue;
|
|
107
|
+
throw new Error(`bindBitrefillTools: no handler for "${def.name}"`);
|
|
108
|
+
}
|
|
109
|
+
bound.push({
|
|
110
|
+
name: def.name,
|
|
111
|
+
description: def.description,
|
|
112
|
+
parameters: def.parameters,
|
|
113
|
+
requiresConfirmation: def.requiresConfirmation,
|
|
114
|
+
handler,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return new InProcessToolSource(opts.id ?? 'bitrefill', bound);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/bitrefill/contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-output compression — the "fit more into a tiny window" part.
|
|
3
|
+
*
|
|
4
|
+
* Tool results are the single biggest, most repetitive thing the engine pushes
|
|
5
|
+
* into a small on-device model's context. A merchant search returns 40 near
|
|
6
|
+
* identical rows; a tx history returns hundreds; a swap quote nests config the
|
|
7
|
+
* model never reads. Every round, the *raw* `JSON.stringify(result)` is fed back
|
|
8
|
+
* into history — so on a 2k-window 0.6B model the conversation drowns in JSON
|
|
9
|
+
* the model didn't need, crowding out the system prompt, the skill, and the
|
|
10
|
+
* actual question.
|
|
11
|
+
*
|
|
12
|
+
* `compressToolResult` is a structural crusher (the idea behind Headroom's
|
|
13
|
+
* SmartCrusher/ToolCrusher, reimplemented natively — no dependency, no network,
|
|
14
|
+
* no proxy, so it stays on-device and private). It walks the JSON and:
|
|
15
|
+
*
|
|
16
|
+
* • dedupes identical array items, then keeps the first/last few and replaces
|
|
17
|
+
* the middle with an honest `{ "__elided__": N }` marker,
|
|
18
|
+
* • caps nesting depth (deep config → a one-line summary),
|
|
19
|
+
* • truncates long *prose* strings (logs, descriptions),
|
|
20
|
+
*
|
|
21
|
+
* and never regresses: if crushing doesn't actually save tokens, the original
|
|
22
|
+
* is returned untouched.
|
|
23
|
+
*
|
|
24
|
+
* SAFETY: it never touches numbers, never elides whole objects, never truncates
|
|
25
|
+
* whitespace-free strings (addresses, BOLT11 invoices, txids, pubkeys), and
|
|
26
|
+
* never touches a value under a money/identity key (see DEFAULT_PRESERVE_KEYS).
|
|
27
|
+
* Amounts and recipients reach the model intact — the confirm readback is built
|
|
28
|
+
* deterministically from the resolved call anyway, but this keeps the model's
|
|
29
|
+
* own view honest too. Small results (below `minTokens`) are passed through
|
|
30
|
+
* verbatim so nothing changes for the common case.
|
|
31
|
+
*/
|
|
32
|
+
/** Keys whose values are never elided or truncated — amounts, ids, recipients. */
|
|
33
|
+
export declare const DEFAULT_PRESERVE_KEYS: readonly string[];
|
|
34
|
+
export interface ToolCrushOptions {
|
|
35
|
+
/** Don't compress results estimated below this many tokens. Default 200. */
|
|
36
|
+
minTokens?: number;
|
|
37
|
+
/** Max items kept in any array before the middle is elided. Default 8. */
|
|
38
|
+
maxArrayItems?: number;
|
|
39
|
+
/** Max nesting depth kept verbatim; deeper is summarized. Default 6. */
|
|
40
|
+
maxDepth?: number;
|
|
41
|
+
/** Max chars for a single prose string before truncation (0 disables). Default 600. */
|
|
42
|
+
maxStringLength?: number;
|
|
43
|
+
/** Dedupe identical array items before eliding. Default true. */
|
|
44
|
+
dedupe?: boolean;
|
|
45
|
+
/** Keys whose values are never elided/truncated (defaults to DEFAULT_PRESERVE_KEYS). */
|
|
46
|
+
preserveKeys?: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
export interface CrushResult {
|
|
49
|
+
/** Serialized, compressed content — ready to push into history. */
|
|
50
|
+
content: string;
|
|
51
|
+
/** Estimated tokens of the original serialization. */
|
|
52
|
+
originalTokens: number;
|
|
53
|
+
/** Estimated tokens after crushing. */
|
|
54
|
+
compressedTokens: number;
|
|
55
|
+
/** Total array items dropped across the whole structure. */
|
|
56
|
+
elided: number;
|
|
57
|
+
/** False when the original was returned untouched (too small, or no win). */
|
|
58
|
+
changed: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Crush a tool result for inclusion in model context. Returns the original,
|
|
62
|
+
* verbatim, when it's small or when crushing wouldn't save tokens.
|
|
63
|
+
*/
|
|
64
|
+
export declare function compressToolResult(value: unknown, opts?: ToolCrushOptions): CrushResult;
|
|
65
|
+
//# sourceMappingURL=compress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../../src/context/compress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAiClD,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAC;CAClB;AA4BD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAE,gBAAqB,GAAG,WAAW,CA+B3F"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-output compression — the "fit more into a tiny window" part.
|
|
3
|
+
*
|
|
4
|
+
* Tool results are the single biggest, most repetitive thing the engine pushes
|
|
5
|
+
* into a small on-device model's context. A merchant search returns 40 near
|
|
6
|
+
* identical rows; a tx history returns hundreds; a swap quote nests config the
|
|
7
|
+
* model never reads. Every round, the *raw* `JSON.stringify(result)` is fed back
|
|
8
|
+
* into history — so on a 2k-window 0.6B model the conversation drowns in JSON
|
|
9
|
+
* the model didn't need, crowding out the system prompt, the skill, and the
|
|
10
|
+
* actual question.
|
|
11
|
+
*
|
|
12
|
+
* `compressToolResult` is a structural crusher (the idea behind Headroom's
|
|
13
|
+
* SmartCrusher/ToolCrusher, reimplemented natively — no dependency, no network,
|
|
14
|
+
* no proxy, so it stays on-device and private). It walks the JSON and:
|
|
15
|
+
*
|
|
16
|
+
* • dedupes identical array items, then keeps the first/last few and replaces
|
|
17
|
+
* the middle with an honest `{ "__elided__": N }` marker,
|
|
18
|
+
* • caps nesting depth (deep config → a one-line summary),
|
|
19
|
+
* • truncates long *prose* strings (logs, descriptions),
|
|
20
|
+
*
|
|
21
|
+
* and never regresses: if crushing doesn't actually save tokens, the original
|
|
22
|
+
* is returned untouched.
|
|
23
|
+
*
|
|
24
|
+
* SAFETY: it never touches numbers, never elides whole objects, never truncates
|
|
25
|
+
* whitespace-free strings (addresses, BOLT11 invoices, txids, pubkeys), and
|
|
26
|
+
* never touches a value under a money/identity key (see DEFAULT_PRESERVE_KEYS).
|
|
27
|
+
* Amounts and recipients reach the model intact — the confirm readback is built
|
|
28
|
+
* deterministically from the resolved call anyway, but this keeps the model's
|
|
29
|
+
* own view honest too. Small results (below `minTokens`) are passed through
|
|
30
|
+
* verbatim so nothing changes for the common case.
|
|
31
|
+
*/
|
|
32
|
+
import { estimateTokens } from './budget.js';
|
|
33
|
+
/** Keys whose values are never elided or truncated — amounts, ids, recipients. */
|
|
34
|
+
export const DEFAULT_PRESERVE_KEYS = [
|
|
35
|
+
'amount',
|
|
36
|
+
'amount_sat',
|
|
37
|
+
'amount_sats',
|
|
38
|
+
'amount_msat',
|
|
39
|
+
'sat',
|
|
40
|
+
'sats',
|
|
41
|
+
'msat',
|
|
42
|
+
'value',
|
|
43
|
+
'fee',
|
|
44
|
+
'fee_sat',
|
|
45
|
+
'fee_sats',
|
|
46
|
+
'total',
|
|
47
|
+
'total_sats',
|
|
48
|
+
'balance',
|
|
49
|
+
'balance_sat',
|
|
50
|
+
'address',
|
|
51
|
+
'invoice',
|
|
52
|
+
'bolt11',
|
|
53
|
+
'payment_request',
|
|
54
|
+
'payment_hash',
|
|
55
|
+
'preimage',
|
|
56
|
+
'txid',
|
|
57
|
+
'tx_id',
|
|
58
|
+
'pubkey',
|
|
59
|
+
'node_id',
|
|
60
|
+
'recipient',
|
|
61
|
+
'destination',
|
|
62
|
+
'asset_id',
|
|
63
|
+
'contract_id',
|
|
64
|
+
'rate',
|
|
65
|
+
'price',
|
|
66
|
+
'price_usd',
|
|
67
|
+
];
|
|
68
|
+
/** A string with no whitespace is treated as an identifier (address/invoice/hash) and never truncated. */
|
|
69
|
+
function isIdentifierLike(s) {
|
|
70
|
+
return !/\s/.test(s);
|
|
71
|
+
}
|
|
72
|
+
function serialize(value) {
|
|
73
|
+
return typeof value === 'string' ? value : safeStringify(value);
|
|
74
|
+
}
|
|
75
|
+
function safeStringify(value) {
|
|
76
|
+
try {
|
|
77
|
+
return JSON.stringify(value) ?? String(value);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return String(value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Crush a tool result for inclusion in model context. Returns the original,
|
|
85
|
+
* verbatim, when it's small or when crushing wouldn't save tokens.
|
|
86
|
+
*/
|
|
87
|
+
export function compressToolResult(value, opts = {}) {
|
|
88
|
+
const cfg = {
|
|
89
|
+
minTokens: opts.minTokens ?? 200,
|
|
90
|
+
maxArrayItems: Math.max(2, opts.maxArrayItems ?? 8),
|
|
91
|
+
maxDepth: Math.max(1, opts.maxDepth ?? 6),
|
|
92
|
+
maxStringLength: opts.maxStringLength ?? 600,
|
|
93
|
+
dedupe: opts.dedupe ?? true,
|
|
94
|
+
preserve: new Set((opts.preserveKeys ?? DEFAULT_PRESERVE_KEYS).map((k) => k.toLowerCase())),
|
|
95
|
+
};
|
|
96
|
+
const original = serialize(value);
|
|
97
|
+
const originalTokens = estimateTokens(original);
|
|
98
|
+
// Below the floor, never touch it — correctness over savings for small results.
|
|
99
|
+
if (originalTokens < cfg.minTokens) {
|
|
100
|
+
return { content: original, originalTokens, compressedTokens: originalTokens, elided: 0, changed: false };
|
|
101
|
+
}
|
|
102
|
+
let elided = 0;
|
|
103
|
+
const crushed = crush(value, cfg, 0, false, () => {
|
|
104
|
+
elided += 1;
|
|
105
|
+
});
|
|
106
|
+
const content = serialize(crushed);
|
|
107
|
+
const compressedTokens = estimateTokens(content);
|
|
108
|
+
// Never regress: if crushing didn't actually shrink it, keep the original.
|
|
109
|
+
if (compressedTokens >= originalTokens) {
|
|
110
|
+
return { content: original, originalTokens, compressedTokens: originalTokens, elided: 0, changed: false };
|
|
111
|
+
}
|
|
112
|
+
return { content, originalTokens, compressedTokens, elided, changed: true };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Recursively crush a JSON-ish value.
|
|
116
|
+
*
|
|
117
|
+
* @param preserved when true, the value sits under a preserve key — kept verbatim.
|
|
118
|
+
* @param onElide called once per array item dropped (for stats).
|
|
119
|
+
*/
|
|
120
|
+
function crush(value, cfg, depth, preserved, onElide) {
|
|
121
|
+
if (value === null || value === undefined)
|
|
122
|
+
return value;
|
|
123
|
+
if (typeof value === 'string') {
|
|
124
|
+
if (preserved || isIdentifierLike(value))
|
|
125
|
+
return value;
|
|
126
|
+
if (cfg.maxStringLength > 0 && value.length > cfg.maxStringLength) {
|
|
127
|
+
const omitted = value.length - cfg.maxStringLength;
|
|
128
|
+
return `${value.slice(0, cfg.maxStringLength)}… (+${omitted} chars)`;
|
|
129
|
+
}
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value !== 'object')
|
|
133
|
+
return value; // number, boolean — never touched
|
|
134
|
+
// Beyond max depth, collapse to a one-line shape summary instead of the subtree.
|
|
135
|
+
if (depth >= cfg.maxDepth) {
|
|
136
|
+
if (Array.isArray(value))
|
|
137
|
+
return `[array: ${value.length} items]`;
|
|
138
|
+
return `[object: ${Object.keys(value).length} keys]`;
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(value)) {
|
|
141
|
+
let items = value;
|
|
142
|
+
if (cfg.dedupe && items.length > cfg.maxArrayItems) {
|
|
143
|
+
const seen = new Set();
|
|
144
|
+
const unique = [];
|
|
145
|
+
for (const item of items) {
|
|
146
|
+
const key = safeStringify(item);
|
|
147
|
+
if (seen.has(key))
|
|
148
|
+
continue;
|
|
149
|
+
seen.add(key);
|
|
150
|
+
unique.push(item);
|
|
151
|
+
}
|
|
152
|
+
items = unique;
|
|
153
|
+
}
|
|
154
|
+
if (items.length <= cfg.maxArrayItems) {
|
|
155
|
+
return items.map((v) => crush(v, cfg, depth + 1, preserved, onElide));
|
|
156
|
+
}
|
|
157
|
+
// Keep the front and a little of the tail (Headroom's "anchors"); elide the
|
|
158
|
+
// middle with an honest marker so the model knows data was omitted and can
|
|
159
|
+
// ask a more specific question / call a narrower tool.
|
|
160
|
+
const keepFirst = Math.max(1, Math.ceil(cfg.maxArrayItems * 0.6));
|
|
161
|
+
const keepLast = Math.max(0, cfg.maxArrayItems - keepFirst);
|
|
162
|
+
const head = items.slice(0, keepFirst);
|
|
163
|
+
const tail = keepLast > 0 ? items.slice(items.length - keepLast) : [];
|
|
164
|
+
const droppedCount = items.length - head.length - tail.length;
|
|
165
|
+
for (let i = 0; i < droppedCount; i++)
|
|
166
|
+
onElide();
|
|
167
|
+
const out = head.map((v) => crush(v, cfg, depth + 1, preserved, onElide));
|
|
168
|
+
out.push({ __elided__: droppedCount, note: 'items omitted to fit context' });
|
|
169
|
+
for (const v of tail)
|
|
170
|
+
out.push(crush(v, cfg, depth + 1, preserved, onElide));
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
const obj = value;
|
|
174
|
+
const result = {};
|
|
175
|
+
for (const [key, v] of Object.entries(obj)) {
|
|
176
|
+
const keep = preserved || cfg.preserve.has(key.toLowerCase());
|
|
177
|
+
result[key] = crush(v, cfg, depth + 1, keep, onElide);
|
|
178
|
+
}
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=compress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compress.js","sourceRoot":"","sources":["../../src/context/compress.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { ConfirmDecision, Message, ToolResult } from './types.js';
|
|
17
17
|
import type { LLMProvider } from './providers/types.js';
|
|
18
|
+
import type { InferenceMetrics } from './providers/types.js';
|
|
18
19
|
import type { ToolRegistry } from './tools/registry.js';
|
|
20
|
+
import { type ToolCrushOptions } from './context/compress.js';
|
|
19
21
|
export interface EngineOptions {
|
|
20
22
|
provider: LLMProvider;
|
|
21
23
|
tools: ToolRegistry;
|
|
@@ -23,6 +25,15 @@ export interface EngineOptions {
|
|
|
23
25
|
defaultSystem?: string;
|
|
24
26
|
/** Max reasoning↔tool rounds before forcing a stop. Default 5. */
|
|
25
27
|
defaultMaxTurns?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Crush verbose tool results before they're fed back into history, so a
|
|
30
|
+
* tiny on-device model's context window isn't drowned in repetitive JSON
|
|
31
|
+
* (merchant lists, tx history, nested quotes). `true` uses safe defaults;
|
|
32
|
+
* pass options to tune. Off by default — small results are never touched and
|
|
33
|
+
* amounts/addresses/invoices are always preserved (see compressToolResult).
|
|
34
|
+
* The `onToolResult` callback and `toolCalls` still carry the raw result.
|
|
35
|
+
*/
|
|
36
|
+
compressToolOutput?: boolean | ToolCrushOptions;
|
|
26
37
|
}
|
|
27
38
|
export interface AgenticOptions {
|
|
28
39
|
maxTurns?: number;
|
|
@@ -65,15 +76,24 @@ export interface AgenticResult {
|
|
|
65
76
|
messages: Message[];
|
|
66
77
|
/** Wall-clock duration of the whole agentic run, ms. */
|
|
67
78
|
latencyMs: number;
|
|
79
|
+
/** One receipt per model call in this agentic run. */
|
|
80
|
+
inference: InferenceMetrics[];
|
|
68
81
|
}
|
|
69
82
|
export declare class Engine {
|
|
70
83
|
private readonly provider;
|
|
71
84
|
private readonly registry;
|
|
72
85
|
private readonly defaultSystem?;
|
|
73
86
|
private readonly defaultMaxTurns;
|
|
87
|
+
private readonly compressOpts?;
|
|
74
88
|
constructor(opts: EngineOptions);
|
|
75
89
|
runAgentic(messages: Message[], opts?: AgenticOptions): Promise<AgenticResult>;
|
|
76
90
|
cancel(requestId: string): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Serialize a tool result for history, optionally crushing verbose JSON so
|
|
93
|
+
* it doesn't swamp a small context window. The raw result is unchanged for
|
|
94
|
+
* callbacks/logs — only the model-facing history copy is compressed.
|
|
95
|
+
*/
|
|
96
|
+
private toHistoryContent;
|
|
77
97
|
private safeExecute;
|
|
78
98
|
}
|
|
79
99
|
//# sourceMappingURL=engine.d.ts.map
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -13,16 +13,23 @@
|
|
|
13
13
|
* wherever the ToolSource lives (on the phone for the wallet), even when
|
|
14
14
|
* inference is delegated to a remote provider.
|
|
15
15
|
*/
|
|
16
|
+
import { compressToolResult } from './context/compress.js';
|
|
16
17
|
export class Engine {
|
|
17
18
|
provider;
|
|
18
19
|
registry;
|
|
19
20
|
defaultSystem;
|
|
20
21
|
defaultMaxTurns;
|
|
22
|
+
compressOpts;
|
|
21
23
|
constructor(opts) {
|
|
22
24
|
this.provider = opts.provider;
|
|
23
25
|
this.registry = opts.tools;
|
|
24
26
|
this.defaultSystem = opts.defaultSystem;
|
|
25
27
|
this.defaultMaxTurns = opts.defaultMaxTurns ?? 5;
|
|
28
|
+
this.compressOpts = opts.compressToolOutput
|
|
29
|
+
? opts.compressToolOutput === true
|
|
30
|
+
? {}
|
|
31
|
+
: opts.compressToolOutput
|
|
32
|
+
: undefined;
|
|
26
33
|
}
|
|
27
34
|
async runAgentic(messages, opts = {}) {
|
|
28
35
|
const maxTurns = opts.maxTurns ?? this.defaultMaxTurns;
|
|
@@ -39,6 +46,7 @@ export class Engine {
|
|
|
39
46
|
let lastRequestId;
|
|
40
47
|
let finalText = '';
|
|
41
48
|
let turns = 0;
|
|
49
|
+
const inference = [];
|
|
42
50
|
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
43
51
|
turns = turn;
|
|
44
52
|
if (opts.signal?.aborted)
|
|
@@ -51,6 +59,8 @@ export class Engine {
|
|
|
51
59
|
signal: opts.signal,
|
|
52
60
|
});
|
|
53
61
|
lastRequestId = out.requestId;
|
|
62
|
+
if (out.inference)
|
|
63
|
+
inference.push(out.inference);
|
|
54
64
|
if (out.requestId)
|
|
55
65
|
opts.onStart?.(out.requestId, turn);
|
|
56
66
|
finalText = (out.text || '').trim();
|
|
@@ -79,10 +89,7 @@ export class Engine {
|
|
|
79
89
|
}
|
|
80
90
|
executed.push({ name: call.name, arguments: call.arguments, result });
|
|
81
91
|
opts.onToolResult?.({ name: call.name, arguments: call.arguments, result }, turn);
|
|
82
|
-
history.push({
|
|
83
|
-
role: 'tool',
|
|
84
|
-
content: typeof result === 'string' ? result : JSON.stringify(result),
|
|
85
|
-
});
|
|
92
|
+
history.push({ role: 'tool', content: this.toHistoryContent(result) });
|
|
86
93
|
}
|
|
87
94
|
if (turn === maxTurns && !finalText) {
|
|
88
95
|
finalText = 'I had to stop after several steps — please try a more specific request.';
|
|
@@ -99,11 +106,23 @@ export class Engine {
|
|
|
99
106
|
requestId: lastRequestId,
|
|
100
107
|
messages: history,
|
|
101
108
|
latencyMs: Date.now() - startedAt,
|
|
109
|
+
inference,
|
|
102
110
|
};
|
|
103
111
|
}
|
|
104
112
|
async cancel(requestId) {
|
|
105
113
|
await this.provider.cancel?.(requestId);
|
|
106
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Serialize a tool result for history, optionally crushing verbose JSON so
|
|
117
|
+
* it doesn't swamp a small context window. The raw result is unchanged for
|
|
118
|
+
* callbacks/logs — only the model-facing history copy is compressed.
|
|
119
|
+
*/
|
|
120
|
+
toHistoryContent(result) {
|
|
121
|
+
if (!this.compressOpts) {
|
|
122
|
+
return typeof result === 'string' ? result : JSON.stringify(result);
|
|
123
|
+
}
|
|
124
|
+
return compressToolResult(result, this.compressOpts).content;
|
|
125
|
+
}
|
|
107
126
|
async safeExecute(name, args) {
|
|
108
127
|
try {
|
|
109
128
|
return await this.registry.execute(name, args);
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|