@kaleidorg/mind 0.8.1 → 0.10.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.
Files changed (133) hide show
  1. package/README.md +7 -5
  2. package/dist/bitrefill/index.d.ts +4 -0
  3. package/dist/bitrefill/index.d.ts.map +1 -0
  4. package/dist/bitrefill/index.js +3 -0
  5. package/dist/bitrefill/index.js.map +1 -0
  6. package/dist/capabilities.d.ts +3 -3
  7. package/dist/capabilities.d.ts.map +1 -1
  8. package/dist/capabilities.js +4 -4
  9. package/dist/capabilities.js.map +1 -1
  10. package/dist/engine/answer.d.ts +37 -0
  11. package/dist/engine/answer.d.ts.map +1 -0
  12. package/dist/engine/answer.js +35 -0
  13. package/dist/engine/answer.js.map +1 -0
  14. package/dist/engine.d.ts +9 -3
  15. package/dist/engine.d.ts.map +1 -1
  16. package/dist/engine.js +159 -175
  17. package/dist/engine.js.map +1 -1
  18. package/dist/evidence.d.ts +1 -1
  19. package/dist/evidence.d.ts.map +1 -1
  20. package/dist/flashnet/index.d.ts +5 -0
  21. package/dist/flashnet/index.d.ts.map +1 -0
  22. package/dist/flashnet/index.js +4 -0
  23. package/dist/flashnet/index.js.map +1 -0
  24. package/dist/guards.d.ts.map +1 -1
  25. package/dist/guards.js +20 -0
  26. package/dist/guards.js.map +1 -1
  27. package/dist/index.d.ts +7 -24
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +11 -28
  30. package/dist/index.js.map +1 -1
  31. package/dist/kaleidoswap/contract.d.ts +7 -0
  32. package/dist/kaleidoswap/contract.d.ts.map +1 -1
  33. package/dist/kaleidoswap/contract.js +71 -17
  34. package/dist/kaleidoswap/contract.js.map +1 -1
  35. package/dist/kaleidoswap/index.d.ts +8 -0
  36. package/dist/kaleidoswap/index.d.ts.map +1 -0
  37. package/dist/kaleidoswap/index.js +7 -0
  38. package/dist/kaleidoswap/index.js.map +1 -0
  39. package/dist/knowledge/index.d.ts +9 -0
  40. package/dist/knowledge/index.d.ts.map +1 -0
  41. package/dist/knowledge/index.js +6 -0
  42. package/dist/knowledge/index.js.map +1 -0
  43. package/dist/lsps1/index.d.ts +4 -0
  44. package/dist/lsps1/index.d.ts.map +1 -0
  45. package/dist/lsps1/index.js +3 -0
  46. package/dist/lsps1/index.js.map +1 -0
  47. package/dist/providers/types.d.ts +3 -3
  48. package/dist/providers/types.js +3 -3
  49. package/dist/qvac/index.d.ts +0 -1
  50. package/dist/qvac/index.d.ts.map +1 -1
  51. package/dist/qvac/index.js +0 -1
  52. package/dist/qvac/index.js.map +1 -1
  53. package/dist/qvac/provider.d.ts +9 -9
  54. package/dist/qvac/provider.d.ts.map +1 -1
  55. package/dist/qvac/provider.js +115 -109
  56. package/dist/qvac/provider.js.map +1 -1
  57. package/dist/qvac/stream.d.ts +4 -3
  58. package/dist/qvac/stream.d.ts.map +1 -1
  59. package/dist/qvac/stream.js.map +1 -1
  60. package/dist/qvac/voice.d.ts +1 -1
  61. package/dist/recipe/asset-send.js +1 -1
  62. package/dist/recipe/asset-send.js.map +1 -1
  63. package/dist/submarine/index.d.ts +5 -0
  64. package/dist/submarine/index.d.ts.map +1 -0
  65. package/dist/submarine/index.js +4 -0
  66. package/dist/submarine/index.js.map +1 -0
  67. package/dist/testing/mock-wallet.d.ts.map +1 -1
  68. package/dist/testing/mock-wallet.js +6 -0
  69. package/dist/testing/mock-wallet.js.map +1 -1
  70. package/dist/tools/in-process.d.ts +2 -2
  71. package/dist/tools/in-process.js +2 -2
  72. package/dist/wallet/contract.d.ts +5 -0
  73. package/dist/wallet/contract.d.ts.map +1 -1
  74. package/dist/wallet/contract.js +39 -6
  75. package/dist/wallet/contract.js.map +1 -1
  76. package/package.json +32 -2
  77. package/scripts/snapshot-mcp-tools.mjs +38 -0
  78. package/skills/README.md +98 -64
  79. package/skills/bitrefill/SKILL.md +30 -157
  80. package/skills/channel-manager/SKILL.md +31 -52
  81. package/skills/flashnet-swaps/SKILL.md +24 -150
  82. package/skills/kaleido-node/SKILL.md +25 -55
  83. package/skills/kaleido-trading/SKILL.md +28 -172
  84. package/skills/kaleido-trading/references/assets.md +4 -4
  85. package/skills/kaleido-trading/references/atomic.md +5 -7
  86. package/skills/merchant-finder/SKILL.md +25 -108
  87. package/skills/paid-data/SKILL.md +25 -58
  88. package/skills/portfolio-manager/SKILL.md +26 -60
  89. package/skills/rgb-lightning-node/SKILL.md +37 -255
  90. package/skills/rgb-lightning-node/references/channels.md +34 -0
  91. package/skills/spark-wallet/SKILL.md +26 -228
  92. package/skills/submarine-swaps/SKILL.md +19 -37
  93. package/skills/wallet-assistant/SKILL.md +26 -44
  94. package/src/bitrefill/index.ts +13 -0
  95. package/src/capabilities.ts +7 -7
  96. package/src/context/context.test.ts +2 -2
  97. package/src/engine/answer.ts +66 -0
  98. package/src/engine.ts +185 -194
  99. package/src/evidence.ts +1 -1
  100. package/src/flashnet/index.ts +14 -0
  101. package/src/funnel.mind.test.ts +6 -5
  102. package/src/guards.test.ts +12 -0
  103. package/src/guards.ts +20 -0
  104. package/src/index.ts +11 -107
  105. package/src/kaleidoswap/contract.test.ts +32 -3
  106. package/src/kaleidoswap/contract.ts +65 -17
  107. package/src/kaleidoswap/index.ts +20 -0
  108. package/src/knowledge/index.ts +14 -0
  109. package/src/lsps1/index.ts +13 -0
  110. package/src/providers/types.ts +3 -3
  111. package/src/qvac/index.ts +0 -8
  112. package/src/qvac/provider.test.ts +17 -17
  113. package/src/qvac/provider.ts +33 -31
  114. package/src/qvac/stream.ts +4 -3
  115. package/src/qvac/voice.ts +1 -1
  116. package/src/recipe/asset-send.ts +1 -1
  117. package/src/recipe/recipe.test.ts +1 -1
  118. package/src/skills/catalog.test.ts +215 -0
  119. package/src/skills/mcp-tools.snapshot.json +1938 -0
  120. package/src/submarine/index.ts +17 -0
  121. package/src/testing/mock-wallet.ts +6 -0
  122. package/src/tools/in-process.ts +2 -2
  123. package/src/wallet/contract.test.ts +20 -1
  124. package/src/wallet/contract.ts +36 -6
  125. package/dist/qvac/delegate.d.ts +0 -50
  126. package/dist/qvac/delegate.d.ts.map +0 -1
  127. package/dist/qvac/delegate.js +0 -53
  128. package/dist/qvac/delegate.js.map +0 -1
  129. package/skills/dca/SKILL.md +0 -48
  130. package/skills/kaleido-lsps/SKILL.md +0 -131
  131. package/skills/liquidity-optimizer/SKILL.md +0 -91
  132. package/src/qvac/delegate.test.ts +0 -68
  133. package/src/qvac/delegate.ts +0 -73
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Skill catalog checks: every packaged skill must name real tools and every
3
+ * worked example must be a valid call, both against the in-app contracts and
4
+ * against kaleido-mcp (src/skills/mcp-tools.snapshot.json, refreshed with
5
+ * scripts/snapshot-mcp-tools.mjs).
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest';
9
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import type { ToolDef } from '../types.js';
12
+ import { loadSkillsDir, packagedSkillsDir } from './loader.js';
13
+ import { SkillRegistry } from './registry.js';
14
+ import { selectAvailableSkill } from './select.js';
15
+ import { createSkillReferenceToolSource } from './reference-source.js';
16
+ import { WALLET_TOOLS } from '../wallet/contract.js';
17
+ import { KALEIDOSWAP_TOOLS } from '../kaleidoswap/contract.js';
18
+ import { LSPS1_TOOLS } from '../lsps1/contract.js';
19
+ import { SUBMARINE_TOOLS } from '../submarine/contract.js';
20
+ import { FLASHNET_TOOLS } from '../flashnet/contract.js';
21
+ import { BITREFILL_TOOLS } from '../bitrefill/contract.js';
22
+ import { createBtcMapToolSource } from '../knowledge/btc-map.js';
23
+ import { createL402ToolSource } from '../tools/l402.js';
24
+ import { createRagToolSource } from '../rag/tool.js';
25
+ import type { Retriever } from '../rag/retriever.js';
26
+ import { validateToolArgs } from '../guards.js';
27
+ import { estimateTokens } from '../context/budget.js';
28
+
29
+ interface SnapshotTool {
30
+ required: string[];
31
+ properties: Record<string, { type: string; description?: string }>;
32
+ }
33
+
34
+ const SKILLS_DIR = packagedSkillsDir();
35
+ const skills = loadSkillsDir(SKILLS_DIR);
36
+ const registry = new SkillRegistry(skills);
37
+
38
+ const mcp: Record<string, SnapshotTool> = JSON.parse(
39
+ readFileSync(new URL('./mcp-tools.snapshot.json', import.meta.url), 'utf8'),
40
+ ).tools;
41
+
42
+ const core = new Map<string, ToolDef>();
43
+ for (const def of [
44
+ ...WALLET_TOOLS,
45
+ ...KALEIDOSWAP_TOOLS,
46
+ ...LSPS1_TOOLS,
47
+ ...SUBMARINE_TOOLS,
48
+ ...FLASHNET_TOOLS,
49
+ ...BITREFILL_TOOLS,
50
+ ...createBtcMapToolSource({}).listTools(),
51
+ ...createL402ToolSource({ payInvoice: async () => ({ preimage: '' }) }).listTools(),
52
+ ...createRagToolSource({} as Retriever).listTools(),
53
+ ...createSkillReferenceToolSource(registry).listTools(),
54
+ ] as ToolDef[]) core.set(def.name, def);
55
+
56
+ /**
57
+ * Same name, different tool. kaleido-mcp keeps `spark_pay_invoice` as a legacy
58
+ * alias of `spark_pay_spark_invoice` (Spark invoices); its BOLT11 payer is
59
+ * `spark_pay_lightning_invoice`. The in-app `spark_pay_invoice` pays BOLT11, so
60
+ * skills that use it must require an in-app-only tool.
61
+ */
62
+ const NAME_COLLISIONS = new Set(['spark_pay_invoice']);
63
+
64
+ const mcpDef = (name: string): ToolDef => ({
65
+ name,
66
+ description: '',
67
+ parameters: { type: 'object', properties: mcp[name]!.properties, required: mcp[name]!.required },
68
+ });
69
+
70
+ function surfaces(name: string): Array<{ surface: string; def: ToolDef }> {
71
+ const out: Array<{ surface: string; def: ToolDef }> = [];
72
+ if (core.has(name)) out.push({ surface: 'core', def: core.get(name)! });
73
+ if (mcp[name] && !NAME_COLLISIONS.has(name)) out.push({ surface: 'kaleido-mcp', def: mcpDef(name) });
74
+ return out;
75
+ }
76
+
77
+ function props(def: ToolDef): string[] {
78
+ return Object.keys((def.parameters as { properties?: object }).properties ?? {});
79
+ }
80
+
81
+ function required(def: ToolDef): string[] {
82
+ return ((def.parameters as { required?: string[] }).required ?? []);
83
+ }
84
+
85
+ function frontmatterList(name: string, key: string): string[] {
86
+ const skill = skills.find((s) => s.name === name)!;
87
+ if (key === 'tools') return skill.tools ?? [];
88
+ return (skill.metadata?.[key] ?? '').split(',').map((t) => t.trim()).filter(Boolean);
89
+ }
90
+
91
+ /** Every `tool_name {json}` call written in a skill or one of its references. */
92
+ function exampleCalls(dir: string): Array<{ file: string; tool: string; json: string }> {
93
+ const files = [join(dir, 'SKILL.md')];
94
+ const refs = join(dir, 'references');
95
+ if (existsSync(refs)) files.push(...readdirSync(refs).filter((f) => f.endsWith('.md')).map((f) => join(refs, f)));
96
+ const out: Array<{ file: string; tool: string; json: string }> = [];
97
+ for (const file of files) {
98
+ for (const m of readFileSync(file, 'utf8').matchAll(/`([a-z][a-z0-9_]*) (\{[^`]*\})`/g)) {
99
+ if (core.has(m[1]!) || mcp[m[1]!]) out.push({ file, tool: m[1]!, json: m[2]! });
100
+ }
101
+ }
102
+ return out;
103
+ }
104
+
105
+ const isPlaceholder = (v: unknown) => typeof v === 'string' && /^<.*>$|…/.test(v);
106
+
107
+ describe('skill catalog', () => {
108
+ it('ships skills', () => {
109
+ expect(skills.length).toBeGreaterThan(5);
110
+ });
111
+
112
+ it.each(skills.map((s) => [s.name]))('%s: every tool exists on a surface', (name) => {
113
+ const missing = [...frontmatterList(name, 'tools'), ...frontmatterList(name, 'requires-tools')]
114
+ .filter((t) => !core.has(t) && !mcp[t]);
115
+ expect(missing).toEqual([]);
116
+ });
117
+
118
+ it.each(skills.map((s) => [s.name]))('%s: requires-tools are in tools', (name) => {
119
+ const tools = new Set(frontmatterList(name, 'tools'));
120
+ expect(frontmatterList(name, 'requires-tools').filter((t) => !tools.has(t))).toEqual([]);
121
+ });
122
+
123
+ it.each(skills.map((s) => [s.name]))('%s: a colliding tool name is scoped to the app', (name) => {
124
+ const uses = frontmatterList(name, 'tools').some((t) => NAME_COLLISIONS.has(t));
125
+ if (!uses) return;
126
+ const req = frontmatterList(name, 'requires-tools');
127
+ expect(req.length > 0 && req.some((t) => core.has(t) && !mcp[t])).toBe(true);
128
+ });
129
+
130
+ it.each(skills.map((s) => [s.name, s.dir!]))('%s: examples are valid calls', (name, dir) => {
131
+ const scoped = new Set(frontmatterList(name, 'tools'));
132
+ const errors: string[] = [];
133
+ for (const call of exampleCalls(dir)) {
134
+ const where = `${call.file.slice(SKILLS_DIR.length)}: ${call.tool} ${call.json}`;
135
+ let args: Record<string, unknown>;
136
+ try {
137
+ args = JSON.parse(call.json);
138
+ } catch {
139
+ errors.push(`${where} — not JSON`);
140
+ continue;
141
+ }
142
+ if (call.file.endsWith('SKILL.md') && scoped.size && !scoped.has(call.tool)) {
143
+ errors.push(`${where} — tool not in the skill's tools`);
144
+ }
145
+ for (const { surface, def } of surfaces(call.tool)) {
146
+ const known = props(def);
147
+ for (const k of Object.keys(args)) if (!known.includes(k)) errors.push(`${where} — "${k}" unknown on ${surface}`);
148
+ for (const k of required(def)) if (args[k] == null) errors.push(`${where} — "${k}" required on ${surface}`);
149
+ const concrete = Object.fromEntries(Object.entries(args).filter(([, v]) => !isPlaceholder(v)));
150
+ const check = validateToolArgs(def, concrete);
151
+ for (const e of check.errors) if (!/is required/.test(e)) errors.push(`${where} — ${surface}: ${e}`);
152
+ }
153
+ }
154
+ expect(errors).toEqual([]);
155
+ });
156
+
157
+ it.each(skills.map((s) => [s.name]))('%s: tool names in the prose exist', (name) => {
158
+ const body = skills.find((s) => s.name === name)!.instructions;
159
+ const prefixed = /^(rln|wdk|kaleidoswap|kaleido_node|spark|flashnet|bitrefill|mpp|l402|liquid|arkade)_[a-z0-9_]+$/;
160
+ const unknown = [...body.matchAll(/`([a-z][a-z0-9_]*)[` ]/g)]
161
+ .map((m) => m[1]!)
162
+ .filter((w) => prefixed.test(w) && !core.has(w) && !mcp[w]);
163
+ expect([...new Set(unknown)]).toEqual([]);
164
+ });
165
+
166
+ it.each(skills.map((s) => [s.name]))('%s: body stays short', (name) => {
167
+ const skill = skills.find((s) => s.name === name)!;
168
+ expect(estimateTokens(skill.instructions)).toBeLessThanOrEqual(600);
169
+ expect(skill.description.length).toBeLessThanOrEqual(320);
170
+ });
171
+ });
172
+
173
+ describe('core contracts match kaleido-mcp', () => {
174
+ it('shared tool names take the same arguments', () => {
175
+ const errors: string[] = [];
176
+ for (const [name, def] of core) {
177
+ const m = mcp[name];
178
+ if (!m || NAME_COLLISIONS.has(name)) continue;
179
+ const mcpProps = Object.keys(m.properties);
180
+ const coreProps = props(def);
181
+ for (const p of coreProps) if (!mcpProps.includes(p)) errors.push(`${name}: "${p}" is not a kaleido-mcp argument`);
182
+ for (const r of m.required) if (!coreProps.includes(r)) errors.push(`${name}: kaleido-mcp requires "${r}"`);
183
+ }
184
+ expect(errors).toEqual([]);
185
+ });
186
+ });
187
+
188
+ describe('skill selection by surface', () => {
189
+ const mcpTools = Object.keys(mcp);
190
+ const inApp = [...core.keys()];
191
+
192
+ it.each([
193
+ ['Which RGB assets do I hold, and what are the balances?', 'rgb-lightning-node'],
194
+ ['Get a quote to swap 0.0005 BTC into USDT on KaleidoSwap.', 'kaleido-trading'],
195
+ ['Issue a token named Skill Test, ticker SKT, supply 1000, precision 0.', 'rgb-lightning-node'],
196
+ ['Send 1 QTWO to this RGB invoice: rgb:~/~/~/sig/any/1/utxob:test-recipient', 'rgb-lightning-node'],
197
+ ['Create a Lightning invoice for 5000 sats.', 'rgb-lightning-node'],
198
+ ["what's my balance?", 'rgb-lightning-node'],
199
+ ['buy 500k sats of inbound liquidity', 'channel-manager'],
200
+ ['unlock my node', 'kaleido-node'],
201
+ ['pay lntbs10u1pexample with L-USDT', 'submarine-swaps'],
202
+ ])('kaleido-mcp: %s → %s', (query, expected) => {
203
+ expect(selectAvailableSkill(registry, query, mcpTools)?.name).toBe(expected);
204
+ });
205
+
206
+ it.each([
207
+ ["what's my balance?", 'wallet-assistant'],
208
+ ['what is my spark balance', 'spark-wallet'],
209
+ ['swap 100k sats to USDB on flashnet', 'flashnet-swaps'],
210
+ ['buy a $25 amazon gift card', 'bitrefill'],
211
+ ['where can I spend sats in Turin', 'merchant-finder'],
212
+ ])('in-app: %s → %s', (query, expected) => {
213
+ expect(selectAvailableSkill(registry, query, inApp)?.name).toBe(expected);
214
+ });
215
+ });