@kaleidorg/mind 0.7.0 → 0.8.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.
Files changed (136) hide show
  1. package/README.md +96 -8
  2. package/dist/context/compress.d.ts.map +1 -1
  3. package/dist/context/compress.js +1 -0
  4. package/dist/context/compress.js.map +1 -1
  5. package/dist/context/rgb-units.d.ts +18 -0
  6. package/dist/context/rgb-units.d.ts.map +1 -0
  7. package/dist/context/rgb-units.js +86 -0
  8. package/dist/context/rgb-units.js.map +1 -0
  9. package/dist/engine.d.ts +59 -1
  10. package/dist/engine.d.ts.map +1 -1
  11. package/dist/engine.js +196 -17
  12. package/dist/engine.js.map +1 -1
  13. package/dist/fastpath/fastpath.d.ts +6 -1
  14. package/dist/fastpath/fastpath.d.ts.map +1 -1
  15. package/dist/fastpath/fastpath.js +16 -2
  16. package/dist/fastpath/fastpath.js.map +1 -1
  17. package/dist/fastpath/render.d.ts +2 -0
  18. package/dist/fastpath/render.d.ts.map +1 -0
  19. package/dist/fastpath/render.js +58 -0
  20. package/dist/fastpath/render.js.map +1 -0
  21. package/dist/funnel.d.ts.map +1 -1
  22. package/dist/funnel.js +34 -18
  23. package/dist/funnel.js.map +1 -1
  24. package/dist/guards.d.ts +63 -0
  25. package/dist/guards.d.ts.map +1 -0
  26. package/dist/guards.js +284 -0
  27. package/dist/guards.js.map +1 -0
  28. package/dist/index.d.ts +9 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +7 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/kaleidoswap/contract.d.ts +3 -4
  33. package/dist/kaleidoswap/contract.d.ts.map +1 -1
  34. package/dist/kaleidoswap/contract.js +3 -17
  35. package/dist/kaleidoswap/contract.js.map +1 -1
  36. package/dist/providers/openai.d.ts +64 -0
  37. package/dist/providers/openai.d.ts.map +1 -0
  38. package/dist/providers/openai.js +233 -0
  39. package/dist/providers/openai.js.map +1 -0
  40. package/dist/providers/types.d.ts +33 -0
  41. package/dist/providers/types.d.ts.map +1 -1
  42. package/dist/qvac/config.d.ts +1 -1
  43. package/dist/qvac/config.js +1 -1
  44. package/dist/qvac/index.d.ts +1 -0
  45. package/dist/qvac/index.d.ts.map +1 -1
  46. package/dist/qvac/index.js +1 -0
  47. package/dist/qvac/index.js.map +1 -1
  48. package/dist/qvac/models.d.ts +31 -0
  49. package/dist/qvac/models.d.ts.map +1 -0
  50. package/dist/qvac/models.js +80 -0
  51. package/dist/qvac/models.js.map +1 -0
  52. package/dist/qvac/parse.d.ts +11 -0
  53. package/dist/qvac/parse.d.ts.map +1 -1
  54. package/dist/qvac/parse.js +29 -7
  55. package/dist/qvac/parse.js.map +1 -1
  56. package/dist/qvac/provider.d.ts +14 -4
  57. package/dist/qvac/provider.d.ts.map +1 -1
  58. package/dist/qvac/provider.js +33 -8
  59. package/dist/qvac/provider.js.map +1 -1
  60. package/dist/qvac/stream.d.ts +3 -5
  61. package/dist/qvac/stream.d.ts.map +1 -1
  62. package/dist/qvac/stream.js.map +1 -1
  63. package/dist/qvac/tools.d.ts +5 -0
  64. package/dist/qvac/tools.d.ts.map +1 -1
  65. package/dist/qvac/tools.js +10 -0
  66. package/dist/qvac/tools.js.map +1 -1
  67. package/dist/recipe/issue-asset.d.ts.map +1 -1
  68. package/dist/recipe/issue-asset.js +42 -16
  69. package/dist/recipe/issue-asset.js.map +1 -1
  70. package/dist/recipe/runner.d.ts.map +1 -1
  71. package/dist/recipe/runner.js +1 -0
  72. package/dist/recipe/runner.js.map +1 -1
  73. package/dist/recipe/submarine-pay.d.ts +17 -0
  74. package/dist/recipe/submarine-pay.d.ts.map +1 -0
  75. package/dist/recipe/submarine-pay.js +79 -0
  76. package/dist/recipe/submarine-pay.js.map +1 -0
  77. package/dist/skills/registry.js +1 -1
  78. package/dist/skills/select.d.ts +20 -0
  79. package/dist/skills/select.d.ts.map +1 -0
  80. package/dist/skills/select.js +32 -0
  81. package/dist/skills/select.js.map +1 -0
  82. package/dist/submarine/contract.d.ts +42 -0
  83. package/dist/submarine/contract.d.ts.map +1 -0
  84. package/dist/submarine/contract.js +73 -0
  85. package/dist/submarine/contract.js.map +1 -0
  86. package/dist/testing/mock-wallet.d.ts +24 -0
  87. package/dist/testing/mock-wallet.d.ts.map +1 -1
  88. package/dist/testing/mock-wallet.js +75 -6
  89. package/dist/testing/mock-wallet.js.map +1 -1
  90. package/dist/wallet/confirm.d.ts.map +1 -1
  91. package/dist/wallet/confirm.js +27 -9
  92. package/dist/wallet/confirm.js.map +1 -1
  93. package/package.json +6 -1
  94. package/skills/README.md +1 -0
  95. package/skills/kaleido-trading/SKILL.md +22 -28
  96. package/skills/kaleido-trading/references/api.md +3 -3
  97. package/skills/rgb-lightning-node/SKILL.md +32 -12
  98. package/skills/spark-wallet/SKILL.md +1 -0
  99. package/skills/submarine-swaps/SKILL.md +48 -0
  100. package/src/context/compress.ts +1 -0
  101. package/src/context/rgb-units.test.ts +42 -0
  102. package/src/context/rgb-units.ts +89 -0
  103. package/src/engine.test.ts +133 -3
  104. package/src/engine.ts +254 -19
  105. package/src/fastpath/fastpath.ts +22 -2
  106. package/src/fastpath/render.test.ts +39 -0
  107. package/src/fastpath/render.ts +56 -0
  108. package/src/funnel.mind.test.ts +15 -17
  109. package/src/funnel.ts +34 -19
  110. package/src/guards.test.ts +399 -0
  111. package/src/guards.ts +299 -0
  112. package/src/index.ts +37 -1
  113. package/src/kaleidoswap/contract.test.ts +8 -16
  114. package/src/kaleidoswap/contract.ts +4 -32
  115. package/src/providers/openai.test.ts +127 -0
  116. package/src/providers/openai.ts +282 -0
  117. package/src/providers/types.ts +35 -0
  118. package/src/qvac/config.ts +1 -1
  119. package/src/qvac/index.ts +10 -0
  120. package/src/qvac/models.ts +98 -0
  121. package/src/qvac/parse.test.ts +7 -0
  122. package/src/qvac/parse.ts +33 -1
  123. package/src/qvac/provider.test.ts +54 -1
  124. package/src/qvac/provider.ts +49 -13
  125. package/src/qvac/stream.ts +3 -5
  126. package/src/qvac/tools.ts +10 -0
  127. package/src/recipe/issue-asset.test.ts +39 -0
  128. package/src/recipe/issue-asset.ts +41 -13
  129. package/src/recipe/runner.ts +1 -0
  130. package/src/recipe/submarine-pay.test.ts +87 -0
  131. package/src/recipe/submarine-pay.ts +78 -0
  132. package/src/skills/registry.ts +1 -1
  133. package/src/skills/select.ts +41 -0
  134. package/src/submarine/contract.ts +112 -0
  135. package/src/testing/mock-wallet.ts +73 -6
  136. package/src/wallet/confirm.ts +27 -9
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { bindWalletTools, type WalletHandler } from '../wallet/contract.js';
13
13
  import { ToolRegistry } from '../tools/registry.js';
14
+ import { bindSubmarineTools, type SubmarineHandler } from '../submarine/contract.js';
14
15
 
15
16
  export interface SendRecord {
16
17
  tool: string;
@@ -28,6 +29,8 @@ export interface MockWalletOptions {
28
29
  assets?: Record<string, number>;
29
30
  /** Free colored UTXOs on the RLN node (issuance / RGB receive consume one). */
30
31
  utxos?: number;
32
+ /** Liquid balances in smallest units (L-USDT: 8 decimals, L-BTC: sats) for submarine swaps. */
33
+ liquid?: { 'L-USDT': number; 'L-BTC': number };
31
34
  }
32
35
  export interface MockRgbAsset {
33
36
  asset_id: string;
@@ -52,23 +55,31 @@ export class MockWallet {
52
55
  assets: Record<string, number>;
53
56
  contacts: MockContact[];
54
57
  utxos: number;
58
+ liquid: { 'L-USDT': number; 'L-BTC': number };
59
+ /** Submarine swaps opened against the mock /v2 maker, by id. */
60
+ submarineSwaps: Record<string, { from: 'L-USDT' | 'L-BTC'; invoice: string; expected: number; funded: boolean }> = {};
55
61
  /** RGB assets the node knows about (seeded USDT/XAUT + anything issued). */
56
62
  rgbAssets: MockRgbAsset[];
57
63
  transfers: MockTransfer[] = [];
58
64
  sends: SendRecord[] = [];
59
65
 
66
+ private readonly opts: MockWalletOptions;
67
+
60
68
  constructor(o: MockWalletOptions = {}) {
69
+ this.opts = o;
61
70
  this.priceUsd = o.priceUsd ?? 65_000;
62
71
  this.failRoute = o.failRoute ?? false;
63
- this.balances = o.balances ?? { spark: 500_000, rln: 300_000, arkade: 200_000 };
64
- this.assets = o.assets ?? { USDT: 25_000_000, XAUT: 0 };
65
- this.contacts = o.contacts ?? [
72
+ // Copies: the wallet mutates its state, and reset() rebuilds from `o`.
73
+ this.balances = { ...(o.balances ?? { spark: 500_000, rln: 300_000, arkade: 200_000 }) };
74
+ this.assets = { ...(o.assets ?? { USDT: 25_000_000, XAUT: 0 }) };
75
+ this.contacts = o.contacts ? [...o.contacts] : [
66
76
  { name: 'bob', ln_address: 'bob@kaleidoswap.com' },
67
77
  { name: 'alice', ln_address: 'alice@kaleidoswap.com' },
68
78
  { name: 'john', ln_address: 'john.smith@kaleidoswap.com' },
69
79
  { name: 'john', ln_address: 'john.doe@kaleidoswap.com' }, // ambiguous on purpose
70
80
  ];
71
81
  this.utxos = o.utxos ?? 5;
82
+ this.liquid = { ...(o.liquid ?? { 'L-USDT': 100 * 1e8, 'L-BTC': 200_000 }) };
72
83
  this.rgbAssets = Object.keys(this.assets).map((ticker) => ({
73
84
  asset_id: mockAssetId(ticker),
74
85
  ticker,
@@ -112,9 +123,9 @@ export class MockWallet {
112
123
  totalSats(): number {
113
124
  return this.balances.spark + this.balances.rln + this.balances.arkade;
114
125
  }
126
+ /** Back to the constructor state: balances, contacts, UTXOs, issued assets, history. */
115
127
  reset(): void {
116
- this.sends = [];
117
- this.transfers = [];
128
+ Object.assign(this, new MockWallet(this.opts));
118
129
  }
119
130
 
120
131
  private send(tool: string, rec: { to: string; amount_sats?: number; asset?: string; amount?: number }) {
@@ -196,10 +207,56 @@ export class MockWallet {
196
207
  };
197
208
  }
198
209
 
210
+ /**
211
+ * The KaleidoSwap /v2 submarine contract against a toy maker: 0.5% fee, the
212
+ * invoice amount read from its BOLT11 human-readable part, L-USDT at `priceUsd`.
213
+ */
214
+ submarineHandlers(): Record<string, SubmarineHandler> {
215
+ return {
216
+ kaleidoswap_submarine_pairs: async () => ({
217
+ pairs: [
218
+ { from: 'L-USDT', to: 'BTC', fees: { percentage: 0.5 }, limits: { minimal: 10 * 1e8, maximal: 1_000 * 1e8 } },
219
+ { from: 'L-BTC', to: 'BTC', fees: { percentage: 0.5 }, limits: { minimal: 10_000, maximal: 1_000_000 } },
220
+ ],
221
+ }),
222
+ kaleidoswap_submarine_create: async ({ invoice, from_asset }) => {
223
+ const from = (from_asset ?? 'L-USDT') as 'L-USDT' | 'L-BTC';
224
+ if (from !== 'L-USDT' && from !== 'L-BTC') throw new Error(`Unsupported from_asset "${String(from_asset)}".`);
225
+ const sats = bolt11Sats(String(invoice));
226
+ if (sats == null) throw new Error('invalid_invoice: the invoice must be BOLT11 with an amount.');
227
+ const withFee = sats * 1.005;
228
+ const expected = Math.ceil(from === 'L-USDT' ? (withFee / 1e8) * this.priceUsd * 1e8 : withFee);
229
+ const id = `sub${Object.keys(this.submarineSwaps).length + 1}`;
230
+ this.submarineSwaps[id] = { from, invoice: String(invoice), expected, funded: false };
231
+ return { swap_id: id, from_asset: from, expected_amount: String(expected), lockup_address: `tlq1qmock${id}` };
232
+ },
233
+ kaleidoswap_submarine_fund: async ({ swap_id }) => {
234
+ const swap = this.submarineSwaps[String(swap_id)];
235
+ if (!swap) throw new Error(`No submarine swap "${String(swap_id)}" was created by this server.`);
236
+ if (swap.funded) throw new Error(`Swap ${String(swap_id)} is already funded.`);
237
+ if (this.liquid[swap.from] < swap.expected) throw new Error(`Insufficient ${swap.from} balance.`);
238
+ this.liquid[swap.from] -= swap.expected;
239
+ swap.funded = true;
240
+ this.sends.push({ tool: 'kaleidoswap_submarine_fund', to: swap.invoice, asset: swap.from, amount: swap.expected });
241
+ return { funded: true, swap_id, asset: swap.from, amount: String(swap.expected), txid: `mocktx${String(swap_id)}` };
242
+ },
243
+ kaleidoswap_submarine_status: async ({ swap_id }) => {
244
+ const swap = this.submarineSwaps[String(swap_id)];
245
+ if (!swap) throw new Error(`invalid_swap_id: "${String(swap_id)}" is not a swap id`);
246
+ const status = swap.funded ? 'transaction.claimed' : 'swap.created';
247
+ return { swap_id, status, done: swap.funded, failed: false, funded: swap.funded };
248
+ },
249
+ };
250
+ }
251
+
199
252
  /** Bind the contract tools to this wallet (optionally overriding some — e.g. to inject). */
200
253
  registry(overrides?: Partial<Record<string, WalletHandler>>): ToolRegistry {
201
254
  const h = { ...this.handlers(), ...(overrides ?? {}) } as Record<string, WalletHandler>;
202
- return new ToolRegistry([bindWalletTools(h, { layers: ['spark', 'rln', 'arkade', 'core'], allowMissing: true })]);
255
+ const sub = { ...this.submarineHandlers(), ...(overrides ?? {}) } as Record<string, SubmarineHandler>;
256
+ return new ToolRegistry([
257
+ bindWalletTools(h, { layers: ['spark', 'rln', 'arkade', 'core'], allowMissing: true }),
258
+ bindSubmarineTools(sub, { allowMissing: true }),
259
+ ]);
203
260
  }
204
261
  }
205
262
 
@@ -209,3 +266,13 @@ function mockAssetId(ticker: string): string {
209
266
  for (const c of ticker) h = (h * 31 + c.charCodeAt(0)) >>> 0;
210
267
  return `rgb:mock-${ticker.toLowerCase()}-${h.toString(36)}`;
211
268
  }
269
+
270
+ /** Amount in sats from a BOLT11 human-readable part ("lntbs10u…" → 1000), or null. */
271
+ function bolt11Sats(invoice: string): number | null {
272
+ const m = invoice.toLowerCase().match(/^ln(?:bcrt|tbs|bc|tb)(\d+)([munp])?1/);
273
+ if (!m) return null;
274
+ const n = Number(m[1]);
275
+ const mult = { m: 1e5, u: 100, n: 0.1, p: 0.0001 }[m[2] ?? ''] ?? 1e8;
276
+ const sats = n * mult;
277
+ return sats >= 1 ? Math.round(sats) : null;
278
+ }
@@ -31,7 +31,7 @@ function isRef(s: string): boolean {
31
31
  /** Shorten an address/invoice for speech; leave contact names intact. */
32
32
  function shortRef(s: string): string {
33
33
  const v = s.trim();
34
- return isRef(v) ? `${v.slice(0, 6)}…${v.slice(-4)}` : v;
34
+ return isRef(v) && v.length > 12 ? `${v.slice(0, 6)}…${v.slice(-4)}` : v;
35
35
  }
36
36
 
37
37
  /** " over Spark" suffix for the call's layer (explicit arg wins, else the tool's). */
@@ -41,8 +41,26 @@ function over(name: string, args: Record<string, unknown>): string {
41
41
  return label ? ` over ${label}` : '';
42
42
  }
43
43
 
44
- const sats = (v: unknown) => `${fmtNum(Number(v))} sats`;
45
- const asset = (amount: unknown, ticker: unknown) => `${fmtNum(Number(amount))} ${String(ticker)}`;
44
+ /** A finite number from a tool arg, else undefined (never NaN in a readback). */
45
+ function num(v: unknown): number | undefined {
46
+ if (typeof v === 'number') return Number.isFinite(v) ? v : undefined;
47
+ if (typeof v === 'string' && v.trim() !== '' && Number.isFinite(Number(v))) return Number(v);
48
+ return undefined;
49
+ }
50
+
51
+ /** A display string from a tool arg, stripped of stray quotes/commas a model may leave in. */
52
+ function label(v: unknown): string {
53
+ return String(v ?? '').trim().replace(/^[\s'"`,]+|[\s'"`,]+$/g, '') || '?';
54
+ }
55
+
56
+ const sats = (v: unknown) => {
57
+ const n = num(v);
58
+ return n === undefined ? 'an unspecified amount of sats' : `${fmtNum(n)} sats`;
59
+ };
60
+ const asset = (amount: unknown, ticker: unknown) => {
61
+ const n = num(amount);
62
+ return n === undefined ? `an unspecified amount of ${label(ticker)}` : `${fmtNum(n)} ${label(ticker)}`;
63
+ };
46
64
 
47
65
  /** A spoken confirmation ending in "Confirm?", or null for non-spend tools. */
48
66
  export function confirmReadback(call: { name: string; arguments: Record<string, unknown> }): string | null {
@@ -68,18 +86,18 @@ export function confirmReadback(call: { name: string; arguments: Record<string,
68
86
  case 'spark_pay_invoice':
69
87
  return ask(`Pay Lightning invoice ${shortRef(String(a.invoice ?? ''))}${over(name, a)}`);
70
88
  case 'rln_issue_asset': {
71
- const label = a.name != null && a.ticker != null && String(a.name) !== String(a.ticker) ? ` (${String(a.name)})` : '';
89
+ const named = a.name != null && a.ticker != null && label(a.name) !== label(a.ticker) ? ` (${label(a.name)})` : '';
72
90
  return a.schema === 'UDA'
73
- ? ask(`Issue unique asset ${String(a.ticker ?? a.name)}${label} on RGB`)
74
- : ask(`Issue ${asset(a.amount, a.ticker ?? a.name)}${label}, a new RGB asset`);
91
+ ? ask(`Issue unique asset ${label(a.ticker ?? a.name)}${named} on RGB`)
92
+ : ask(`Issue ${asset(a.amount, a.ticker ?? a.name)}${named}, a new RGB asset`);
75
93
  }
76
94
  case 'rln_create_utxos':
77
- return ask(`Create ${fmtNum(Number(a.num ?? 5))} RGB UTXOs on-chain${over(name, a)}`);
95
+ return ask(`Create ${fmtNum(num(a.num) ?? 5)} RGB UTXOs on-chain${over(name, a)}`);
78
96
  case 'rln_send_btc':
79
97
  return ask(`Send ${sats(a.amount_sat)} on-chain to ${to('address')}${over(name, a)}`);
80
98
  case 'rln_open_channel': {
81
99
  const peer = shortRef(String(a.peer_pubkey_and_addr ?? '').split('@')[0] ?? '');
82
- const withAsset = a.asset_id != null && a.asset_amount != null ? ` with ${fmtNum(Number(a.asset_amount))} of ${shortRef(String(a.asset_id))}` : '';
100
+ const withAsset = a.asset_id != null && num(a.asset_amount) !== undefined ? ` with ${fmtNum(num(a.asset_amount)!)} of ${shortRef(String(a.asset_id))}` : '';
83
101
  return ask(`Open a ${sats(a.capacity_sat)} channel to ${peer}${withAsset}`);
84
102
  }
85
103
  case 'rln_close_channel':
@@ -87,7 +105,7 @@ export function confirmReadback(call: { name: string; arguments: Record<string,
87
105
  case 'rln_atomic_taker':
88
106
  return ask(`Accept atomic swap ${shortRef(String(a.swapstring ?? ''))} on your node`);
89
107
  case 'execute_swap':
90
- return ask(`Swap ${fmtNum(Number(a.amount))} ${String(a.from_asset)} for ${String(a.to_asset)}`);
108
+ return ask(`Swap ${asset(a.amount, a.from_asset)} for ${label(a.to_asset)}`);
91
109
  default:
92
110
  // Unknown but spend-flagged tool → a generic, still-honest readback.
93
111
  return getWalletTool(name)?.spend ? ask(`Confirm ${name.replace(/_/g, ' ')}`) : null;