@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.
- package/README.md +7 -5
- package/dist/bitrefill/index.d.ts +4 -0
- package/dist/bitrefill/index.d.ts.map +1 -0
- package/dist/bitrefill/index.js +3 -0
- package/dist/bitrefill/index.js.map +1 -0
- package/dist/capabilities.d.ts +3 -3
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +4 -4
- package/dist/capabilities.js.map +1 -1
- package/dist/engine/answer.d.ts +37 -0
- package/dist/engine/answer.d.ts.map +1 -0
- package/dist/engine/answer.js +35 -0
- package/dist/engine/answer.js.map +1 -0
- package/dist/engine.d.ts +9 -3
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +159 -175
- package/dist/engine.js.map +1 -1
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.d.ts.map +1 -1
- package/dist/flashnet/index.d.ts +5 -0
- package/dist/flashnet/index.d.ts.map +1 -0
- package/dist/flashnet/index.js +4 -0
- package/dist/flashnet/index.js.map +1 -0
- package/dist/guards.d.ts.map +1 -1
- package/dist/guards.js +20 -0
- package/dist/guards.js.map +1 -1
- package/dist/index.d.ts +7 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -28
- package/dist/index.js.map +1 -1
- package/dist/kaleidoswap/contract.d.ts +7 -0
- package/dist/kaleidoswap/contract.d.ts.map +1 -1
- package/dist/kaleidoswap/contract.js +71 -17
- package/dist/kaleidoswap/contract.js.map +1 -1
- package/dist/kaleidoswap/index.d.ts +8 -0
- package/dist/kaleidoswap/index.d.ts.map +1 -0
- package/dist/kaleidoswap/index.js +7 -0
- package/dist/kaleidoswap/index.js.map +1 -0
- package/dist/knowledge/index.d.ts +9 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/knowledge/index.js +6 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/lsps1/index.d.ts +4 -0
- package/dist/lsps1/index.d.ts.map +1 -0
- package/dist/lsps1/index.js +3 -0
- package/dist/lsps1/index.js.map +1 -0
- package/dist/providers/types.d.ts +3 -3
- package/dist/providers/types.js +3 -3
- package/dist/qvac/index.d.ts +0 -1
- package/dist/qvac/index.d.ts.map +1 -1
- package/dist/qvac/index.js +0 -1
- package/dist/qvac/index.js.map +1 -1
- package/dist/qvac/provider.d.ts +9 -9
- package/dist/qvac/provider.d.ts.map +1 -1
- package/dist/qvac/provider.js +115 -109
- package/dist/qvac/provider.js.map +1 -1
- package/dist/qvac/stream.d.ts +4 -3
- package/dist/qvac/stream.d.ts.map +1 -1
- package/dist/qvac/stream.js.map +1 -1
- package/dist/qvac/voice.d.ts +1 -1
- package/dist/recipe/asset-send.js +1 -1
- package/dist/recipe/asset-send.js.map +1 -1
- package/dist/submarine/index.d.ts +5 -0
- package/dist/submarine/index.d.ts.map +1 -0
- package/dist/submarine/index.js +4 -0
- package/dist/submarine/index.js.map +1 -0
- package/dist/testing/mock-wallet.d.ts.map +1 -1
- package/dist/testing/mock-wallet.js +6 -0
- package/dist/testing/mock-wallet.js.map +1 -1
- package/dist/tools/in-process.d.ts +2 -2
- package/dist/tools/in-process.js +2 -2
- package/dist/wallet/contract.d.ts +5 -0
- package/dist/wallet/contract.d.ts.map +1 -1
- package/dist/wallet/contract.js +39 -6
- package/dist/wallet/contract.js.map +1 -1
- package/package.json +32 -2
- package/scripts/snapshot-mcp-tools.mjs +38 -0
- package/skills/README.md +98 -64
- package/skills/bitrefill/SKILL.md +30 -157
- package/skills/channel-manager/SKILL.md +31 -52
- package/skills/flashnet-swaps/SKILL.md +24 -150
- package/skills/kaleido-node/SKILL.md +25 -55
- package/skills/kaleido-trading/SKILL.md +28 -172
- package/skills/kaleido-trading/references/assets.md +4 -4
- package/skills/kaleido-trading/references/atomic.md +5 -7
- package/skills/merchant-finder/SKILL.md +25 -108
- package/skills/paid-data/SKILL.md +25 -58
- package/skills/portfolio-manager/SKILL.md +26 -60
- package/skills/rgb-lightning-node/SKILL.md +37 -255
- package/skills/rgb-lightning-node/references/channels.md +34 -0
- package/skills/spark-wallet/SKILL.md +26 -228
- package/skills/submarine-swaps/SKILL.md +19 -37
- package/skills/wallet-assistant/SKILL.md +26 -44
- package/src/bitrefill/index.ts +13 -0
- package/src/capabilities.ts +7 -7
- package/src/context/context.test.ts +2 -2
- package/src/engine/answer.ts +66 -0
- package/src/engine.ts +185 -194
- package/src/evidence.ts +1 -1
- package/src/flashnet/index.ts +14 -0
- package/src/funnel.mind.test.ts +6 -5
- package/src/guards.test.ts +12 -0
- package/src/guards.ts +20 -0
- package/src/index.ts +11 -107
- package/src/kaleidoswap/contract.test.ts +32 -3
- package/src/kaleidoswap/contract.ts +65 -17
- package/src/kaleidoswap/index.ts +20 -0
- package/src/knowledge/index.ts +14 -0
- package/src/lsps1/index.ts +13 -0
- package/src/providers/types.ts +3 -3
- package/src/qvac/index.ts +0 -8
- package/src/qvac/provider.test.ts +17 -17
- package/src/qvac/provider.ts +33 -31
- package/src/qvac/stream.ts +4 -3
- package/src/qvac/voice.ts +1 -1
- package/src/recipe/asset-send.ts +1 -1
- package/src/recipe/recipe.test.ts +1 -1
- package/src/skills/catalog.test.ts +215 -0
- package/src/skills/mcp-tools.snapshot.json +1938 -0
- package/src/submarine/index.ts +17 -0
- package/src/testing/mock-wallet.ts +6 -0
- package/src/tools/in-process.ts +2 -2
- package/src/wallet/contract.test.ts +20 -1
- package/src/wallet/contract.ts +36 -6
- package/dist/qvac/delegate.d.ts +0 -50
- package/dist/qvac/delegate.d.ts.map +0 -1
- package/dist/qvac/delegate.js +0 -53
- package/dist/qvac/delegate.js.map +0 -1
- package/skills/dca/SKILL.md +0 -48
- package/skills/kaleido-lsps/SKILL.md +0 -131
- package/skills/liquidity-optimizer/SKILL.md +0 -91
- package/src/qvac/delegate.test.ts +0 -68
- package/src/qvac/delegate.ts +0 -73
package/src/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ export {
|
|
|
49
49
|
walletTools,
|
|
50
50
|
toToolDefs,
|
|
51
51
|
bindWalletTools,
|
|
52
|
+
normalizeWalletArgs,
|
|
52
53
|
} from './wallet/contract.js';
|
|
53
54
|
export type {
|
|
54
55
|
WalletLayer,
|
|
@@ -71,102 +72,9 @@ export {
|
|
|
71
72
|
export { annotateRgbBalances, fixRgbBalanceUnits, formatRgbAmount } from './context/rgb-units.js';
|
|
72
73
|
export type { ArgValidation, UngroundedItem, WalletAction } from './guards.js';
|
|
73
74
|
|
|
74
|
-
// ── KaleidoSwap maker tool contract (single source of truth) ────────────────
|
|
75
|
-
export {
|
|
76
|
-
KALEIDOSWAP_TOOLS,
|
|
77
|
-
KALEIDOSWAP_SPEND_TOOLS,
|
|
78
|
-
isKaleidoswapSpendTool,
|
|
79
|
-
getKaleidoswapTool,
|
|
80
|
-
kaleidoswapTools,
|
|
81
|
-
bindKaleidoswapTools,
|
|
82
|
-
} from './kaleidoswap/contract.js';
|
|
83
|
-
export type {
|
|
84
|
-
KaleidoswapGroup,
|
|
85
|
-
KaleidoswapToolDef,
|
|
86
|
-
KaleidoswapHandler,
|
|
87
|
-
BindKaleidoswapOptions,
|
|
88
|
-
} from './kaleidoswap/contract.js';
|
|
89
|
-
|
|
90
|
-
// ── LSPS1 (Lightning Service Provider channel orders) ───────────────────────
|
|
91
|
-
export {
|
|
92
|
-
LSPS1_TOOLS,
|
|
93
|
-
LSPS1_SPEND_TOOLS,
|
|
94
|
-
isLsps1SpendTool,
|
|
95
|
-
getLsps1Tool,
|
|
96
|
-
bindLsps1Tools,
|
|
97
|
-
} from './lsps1/contract.js';
|
|
98
|
-
export type {
|
|
99
|
-
Lsps1ToolDef,
|
|
100
|
-
Lsps1Handler,
|
|
101
|
-
BindLsps1Options,
|
|
102
|
-
} from './lsps1/contract.js';
|
|
103
|
-
|
|
104
|
-
// ── KaleidoSwap /v2 submarine swaps (pay Lightning from Liquid) ─────────────
|
|
105
|
-
export {
|
|
106
|
-
SUBMARINE_TOOLS,
|
|
107
|
-
SUBMARINE_SPEND_TOOLS,
|
|
108
|
-
SUBMARINE_FROM_ASSETS,
|
|
109
|
-
isSubmarineSpendTool,
|
|
110
|
-
getSubmarineTool,
|
|
111
|
-
formatSubmarineAmount,
|
|
112
|
-
bindSubmarineTools,
|
|
113
|
-
} from './submarine/contract.js';
|
|
114
|
-
export type {
|
|
115
|
-
SubmarineToolDef,
|
|
116
|
-
SubmarineFromAsset,
|
|
117
|
-
SubmarineHandler,
|
|
118
|
-
BindSubmarineOptions,
|
|
119
|
-
} from './submarine/contract.js';
|
|
120
|
-
|
|
121
|
-
// ── Bitrefill (gift cards / mobile top-ups / eSIMs) ─────────────────────────
|
|
122
|
-
export {
|
|
123
|
-
BITREFILL_TOOLS,
|
|
124
|
-
BITREFILL_SPEND_TOOLS,
|
|
125
|
-
isBitrefillSpendTool,
|
|
126
|
-
getBitrefillTool,
|
|
127
|
-
bindBitrefillTools,
|
|
128
|
-
} from './bitrefill/contract.js';
|
|
129
|
-
export type {
|
|
130
|
-
BitrefillToolDef,
|
|
131
|
-
BitrefillHandler,
|
|
132
|
-
BindBitrefillOptions,
|
|
133
|
-
} from './bitrefill/contract.js';
|
|
134
|
-
|
|
135
|
-
// ── Flashnet (Spark-native AMM — swaps over Spark) ──────────────────────────
|
|
136
|
-
export {
|
|
137
|
-
FLASHNET_TOOLS,
|
|
138
|
-
FLASHNET_SPEND_TOOLS,
|
|
139
|
-
isFlashnetSpendTool,
|
|
140
|
-
getFlashnetTool,
|
|
141
|
-
bindFlashnetTools,
|
|
142
|
-
} from './flashnet/contract.js';
|
|
143
|
-
export type {
|
|
144
|
-
FlashnetToolDef,
|
|
145
|
-
FlashnetHandler,
|
|
146
|
-
BindFlashnetOptions,
|
|
147
|
-
} from './flashnet/contract.js';
|
|
148
|
-
|
|
149
|
-
// ── KaleidoSwap recipes (opt-in — register via Funnel.recipes) ──
|
|
150
|
-
// price recipe is read-only (quote-only); atomic recipe runs the full swap.
|
|
151
|
-
// Register the price recipe FIRST so phrasings like "BTC price" are answered
|
|
152
|
-
// without firing any spend.
|
|
153
|
-
export { kaleidoswapPriceRecipe } from './recipe/kaleidoswap-price.js';
|
|
154
|
-
export { kaleidoswapAtomicRecipe } from './recipe/kaleidoswap-atomic.js';
|
|
155
|
-
export { flashnetSwapRecipe } from './recipe/flashnet-swap.js';
|
|
156
|
-
export {
|
|
157
|
-
kaleidoswapChannelOrderRecipe,
|
|
158
|
-
extractChannelOrder,
|
|
159
|
-
} from './recipe/kaleidoswap-channel-order.js';
|
|
160
|
-
|
|
161
|
-
// ── Buy-an-asset-channel recipe (opt-in — register via Funnel.recipes) ─────
|
|
162
|
-
export { buyAssetChannelRecipe, extractBuyAsset } from './recipe/buy-asset-channel.js';
|
|
163
|
-
|
|
164
75
|
// ── Issue-an-RGB-asset recipe (opt-in — register via Funnel.recipes) ───────
|
|
165
76
|
export { issueAssetRecipe, extractIssueAsset } from './recipe/issue-asset.js';
|
|
166
77
|
|
|
167
|
-
// ── Submarine-pay recipe (opt-in — register via Funnel.recipes, before payments) ──
|
|
168
|
-
export { submarinePayRecipe, extractSubmarinePay } from './recipe/submarine-pay.js';
|
|
169
|
-
|
|
170
78
|
// ── Recipes (mobile multi-step: "recipes, not planning") ───────────────────
|
|
171
79
|
export { runRecipe, extractSlots, RecipeRegistry } from './recipe/runner.js';
|
|
172
80
|
export type { RunRecipeOptions } from './recipe/runner.js';
|
|
@@ -224,20 +132,16 @@ export type { ToolCrushOptions, CrushResult } from './context/compress.js';
|
|
|
224
132
|
export { capabilityProfile } from './capabilities.js';
|
|
225
133
|
export type { CapabilityInput, MindCapabilities } from './capabilities.js';
|
|
226
134
|
|
|
227
|
-
// ──
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
export
|
|
232
|
-
export
|
|
233
|
-
export
|
|
234
|
-
export
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
BtcMapFetch,
|
|
238
|
-
LocationProvider,
|
|
239
|
-
LatLng,
|
|
240
|
-
} from './knowledge/btc-map.js';
|
|
135
|
+
// ── Domain packs ─────────────────────────────────────────────────────────────
|
|
136
|
+
// Each has its own subpath (`@kaleidorg/mind/kaleidoswap`, `/lsps1`,
|
|
137
|
+
// `/submarine`, `/bitrefill`, `/flashnet`, `/knowledge`). The root re-exports
|
|
138
|
+
// them for compatibility; 1.0 drops these re-exports.
|
|
139
|
+
export * from './kaleidoswap/index.js';
|
|
140
|
+
export * from './lsps1/index.js';
|
|
141
|
+
export * from './submarine/index.js';
|
|
142
|
+
export * from './bitrefill/index.js';
|
|
143
|
+
export * from './flashnet/index.js';
|
|
144
|
+
export * from './knowledge/index.js';
|
|
241
145
|
|
|
242
146
|
export { Engine } from './engine.js';
|
|
243
147
|
export type { EngineOptions, AgenticOptions, AgenticResult, ComposedSkill } from './engine.js';
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getKaleidoswapTool,
|
|
7
7
|
kaleidoswapTools,
|
|
8
8
|
bindKaleidoswapTools,
|
|
9
|
+
normalizeKaleidoswapArgs,
|
|
9
10
|
type KaleidoswapHandler,
|
|
10
11
|
} from './contract.js';
|
|
11
12
|
|
|
@@ -109,10 +110,14 @@ describe('bindKaleidoswapTools', () => {
|
|
|
109
110
|
expect(pairs?.requiresConfirmation).toBeFalsy();
|
|
110
111
|
});
|
|
111
112
|
|
|
112
|
-
it('dispatches execute()
|
|
113
|
+
it('dispatches execute() with kaleido-mcp args plus the legacy aliases', async () => {
|
|
113
114
|
const src = bindKaleidoswapTools(echoHandlers());
|
|
114
|
-
const r = await src.execute('kaleidoswap_get_quote', {
|
|
115
|
-
expect(r).toMatchObject({
|
|
115
|
+
const r = await src.execute('kaleidoswap_get_quote', { from_asset_id: 'BTC', to_asset_id: 'USDT', from_amount: 0.001 });
|
|
116
|
+
expect(r).toMatchObject({
|
|
117
|
+
ok: true,
|
|
118
|
+
tool: 'get_quote',
|
|
119
|
+
args: { from_asset_id: 'BTC', to_asset_id: 'USDT', from_amount: 0.001, from_asset: 'BTC', to_asset: 'USDT', amount: 100_000 },
|
|
120
|
+
});
|
|
116
121
|
});
|
|
117
122
|
|
|
118
123
|
it('throws when a handler is missing and allowMissing is false', () => {
|
|
@@ -142,3 +147,27 @@ describe('bindKaleidoswapTools', () => {
|
|
|
142
147
|
expect(src.id).toBe('maker-prod');
|
|
143
148
|
});
|
|
144
149
|
});
|
|
150
|
+
|
|
151
|
+
describe('normalizeKaleidoswapArgs', () => {
|
|
152
|
+
it('maps a display-unit quote to the legacy sats amount', () => {
|
|
153
|
+
expect(normalizeKaleidoswapArgs('kaleidoswap_get_quote', { from_asset_id: 'BTC', to_asset_id: 'USDT', from_amount: 0.0005 }))
|
|
154
|
+
.toMatchObject({ from_asset: 'BTC', to_asset: 'USDT', amount: 50_000 });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('keeps RGB amounts in display units and marks a buy-side amount', () => {
|
|
158
|
+
expect(normalizeKaleidoswapArgs('kaleidoswap_get_quote', { from_asset_id: 'BTC', to_asset_id: 'USDT', to_amount: 10 }))
|
|
159
|
+
.toMatchObject({ amount: 10, amount_side: 'to' });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('maps a legacy sats quote to display units', () => {
|
|
163
|
+
expect(normalizeKaleidoswapArgs('kaleidoswap_get_quote', { from_asset: 'BTC', to_asset: 'USDT', amount: 100_000 }))
|
|
164
|
+
.toMatchObject({ from_asset_id: 'BTC', to_asset_id: 'USDT', from_amount: 0.001 });
|
|
165
|
+
expect(normalizeKaleidoswapArgs('kaleidoswap_get_quote', { from_asset: 'USDT', to_asset: 'BTC', amount: 25 }))
|
|
166
|
+
.toMatchObject({ from_amount: 25 });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('aliases rfq_id/quote_id and payment_hash/atomic_id', () => {
|
|
170
|
+
expect(normalizeKaleidoswapArgs('kaleidoswap_atomic_init', { rfq_id: 'r1' })).toMatchObject({ quote_id: 'r1' });
|
|
171
|
+
expect(normalizeKaleidoswapArgs('kaleidoswap_atomic_status', { atomic_id: 'h1' })).toMatchObject({ payment_hash: 'h1' });
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -70,14 +70,16 @@ export const KALEIDOSWAP_TOOLS: KaleidoswapToolDef[] = [
|
|
|
70
70
|
|
|
71
71
|
t('market',
|
|
72
72
|
'kaleidoswap_get_quote',
|
|
73
|
-
'Get an executable quote for
|
|
73
|
+
'Get an executable quote for one pair. Amounts are DISPLAY units (0.0005 = 0.0005 BTC = 50,000 sats). Give exactly one of from_amount (sell a fixed input) or to_amount (buy a fixed output). Returns rfq_id, from_asset/to_asset with amount_display and amount_raw, price and expires_at (~60s).',
|
|
74
74
|
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
from_asset_id: { type: 'string', description: "Asset to sell: ticker ('BTC', 'USDT') or RGB id ('rgb:…')." },
|
|
76
|
+
to_asset_id: { type: 'string', description: "Asset to buy: ticker ('USDT', 'BTC') or RGB id ('rgb:…')." },
|
|
77
|
+
from_layer: { type: 'string', description: "Optional: 'BTC_LN', 'RGB_LN', 'BTC_SPARK'. Derived from the asset when omitted." },
|
|
78
|
+
to_layer: { type: 'string', description: "Optional: 'RGB_LN', 'BTC_LN', 'BTC_SPARK'. Derived from the asset when omitted." },
|
|
79
|
+
from_amount: { type: 'number', description: 'Amount to SELL in display units (e.g. 0.001 BTC, 10 USDT).' },
|
|
80
|
+
to_amount: { type: 'number', description: 'Amount to BUY in display units (e.g. 10 USDT).' },
|
|
79
81
|
},
|
|
80
|
-
['
|
|
82
|
+
['from_asset_id', 'to_asset_id']),
|
|
81
83
|
|
|
82
84
|
t('market',
|
|
83
85
|
'kaleidoswap_get_nodeinfo',
|
|
@@ -86,30 +88,36 @@ export const KALEIDOSWAP_TOOLS: KaleidoswapToolDef[] = [
|
|
|
86
88
|
// ─── atomic (the trust-minimised swap chain — used by the recipe) ───────
|
|
87
89
|
t('atomic',
|
|
88
90
|
'kaleidoswap_atomic_init',
|
|
89
|
-
|
|
91
|
+
'Start an atomic swap from a fresh quote. SPEND: confirmation-gated. Pass the quote rfq_id, both asset ids and the quote legs\' amount_raw values unchanged. Returns swapstring, payment_hash and access_token (keep it for status).',
|
|
90
92
|
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
rfq_id: { type: 'string', description: 'rfq_id from kaleidoswap_get_quote.' },
|
|
94
|
+
from_asset_id: { type: 'string', description: 'from_asset.asset_id from the quote.' },
|
|
95
|
+
from_amount_raw: { type: 'integer', description: 'from_asset.amount_raw from the quote, unchanged.' },
|
|
96
|
+
to_asset_id: { type: 'string', description: 'to_asset.asset_id from the quote.' },
|
|
97
|
+
to_amount_raw: { type: 'integer', description: 'to_asset.amount_raw from the quote, unchanged.' },
|
|
93
98
|
},
|
|
94
|
-
['
|
|
99
|
+
['rfq_id', 'from_asset_id', 'from_amount_raw', 'to_asset_id', 'to_amount_raw'],
|
|
95
100
|
/* spend */ true),
|
|
96
101
|
|
|
97
102
|
t('atomic',
|
|
98
103
|
'kaleidoswap_atomic_execute',
|
|
99
|
-
|
|
104
|
+
'Confirm the swap after rln_atomic_taker whitelisted the swapstring. SPEND: confirmation-gated. taker_pubkey is the pubkey from rln_get_node_info.',
|
|
100
105
|
{
|
|
101
|
-
|
|
106
|
+
swapstring: { type: 'string', description: 'swapstring from kaleidoswap_atomic_init.' },
|
|
107
|
+
taker_pubkey: { type: 'string', description: 'Node pubkey from rln_get_node_info.' },
|
|
108
|
+
payment_hash: { type: 'string', description: 'payment_hash from kaleidoswap_atomic_init.' },
|
|
102
109
|
},
|
|
103
|
-
['
|
|
110
|
+
['swapstring', 'taker_pubkey', 'payment_hash'],
|
|
104
111
|
/* spend */ true),
|
|
105
112
|
|
|
106
113
|
t('atomic',
|
|
107
114
|
'kaleidoswap_atomic_status',
|
|
108
|
-
'Poll
|
|
115
|
+
'Poll an atomic swap by payment_hash: Waiting → Pending → Succeeded | Expired | Failed.',
|
|
109
116
|
{
|
|
110
|
-
|
|
117
|
+
payment_hash: { type: 'string', description: 'payment_hash from kaleidoswap_atomic_init.' },
|
|
118
|
+
access_token: { type: 'string', description: 'access_token from kaleidoswap_atomic_init, when the host needs it.' },
|
|
111
119
|
},
|
|
112
|
-
['
|
|
120
|
+
['payment_hash']),
|
|
113
121
|
|
|
114
122
|
// ─── liquidity (buy a NEW channel pre-loaded with an asset — onboarding) ──
|
|
115
123
|
t('liquidity',
|
|
@@ -157,6 +165,46 @@ export function kaleidoswapTools(
|
|
|
157
165
|
return KALEIDOSWAP_TOOLS.filter((x) => groups.has(x.group));
|
|
158
166
|
}
|
|
159
167
|
|
|
168
|
+
const isBtc = (asset: unknown) => typeof asset === 'string' && /^(btc|sats?)$/i.test(asset.trim());
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Map kaleido-mcp argument names onto the pre-0.9 contract names (and back), so
|
|
172
|
+
* one call shape works on every surface and older host handlers keep working.
|
|
173
|
+
* Canonical names win; legacy names are only filled in when absent. The legacy
|
|
174
|
+
* quote `amount` is sats for BTC and display units otherwise.
|
|
175
|
+
*/
|
|
176
|
+
export function normalizeKaleidoswapArgs(name: string, args: Record<string, unknown>): Record<string, unknown> {
|
|
177
|
+
const a: Record<string, unknown> = { ...args };
|
|
178
|
+
const alias = (canonical: string, legacy: string) => {
|
|
179
|
+
if (a[canonical] == null && a[legacy] != null) a[canonical] = a[legacy];
|
|
180
|
+
if (a[legacy] == null && a[canonical] != null) a[legacy] = a[canonical];
|
|
181
|
+
};
|
|
182
|
+
if (name === 'kaleidoswap_get_quote') {
|
|
183
|
+
if (a.from_asset_id == null && a.from_asset != null) a.from_asset_id = a.from_asset;
|
|
184
|
+
if (a.to_asset_id == null && a.to_asset != null) a.to_asset_id = a.to_asset;
|
|
185
|
+
if (a.from_amount == null && a.to_amount == null && a.amount != null) {
|
|
186
|
+
const side = a.amount_side === 'to' || a.side === 'buy' ? 'to' : 'from';
|
|
187
|
+
const asset = side === 'to' ? a.to_asset_id : a.from_asset_id;
|
|
188
|
+
const display = isBtc(asset) ? Number(a.amount) / 1e8 : Number(a.amount);
|
|
189
|
+
a[side === 'to' ? 'to_amount' : 'from_amount'] = display;
|
|
190
|
+
}
|
|
191
|
+
a.from_asset ??= a.from_asset_id;
|
|
192
|
+
a.to_asset ??= a.to_asset_id;
|
|
193
|
+
if (a.amount == null) {
|
|
194
|
+
const toSide = a.from_amount == null && a.to_amount != null;
|
|
195
|
+
const display = Number(toSide ? a.to_amount : a.from_amount);
|
|
196
|
+
const asset = toSide ? a.to_asset_id : a.from_asset_id;
|
|
197
|
+
if (Number.isFinite(display)) a.amount = isBtc(asset) ? Math.round(display * 1e8) : display;
|
|
198
|
+
if (toSide) a.amount_side = 'to';
|
|
199
|
+
}
|
|
200
|
+
} else if (name === 'kaleidoswap_atomic_init') {
|
|
201
|
+
alias('rfq_id', 'quote_id');
|
|
202
|
+
} else if (name === 'kaleidoswap_atomic_execute' || name === 'kaleidoswap_atomic_status') {
|
|
203
|
+
alias('payment_hash', 'atomic_id');
|
|
204
|
+
}
|
|
205
|
+
return a;
|
|
206
|
+
}
|
|
207
|
+
|
|
160
208
|
/** A handler bound to one contract tool. Args validated by JSON schema upstream. */
|
|
161
209
|
export type KaleidoswapHandler = (args: Record<string, unknown>) => Promise<unknown>;
|
|
162
210
|
|
|
@@ -199,7 +247,7 @@ export function bindKaleidoswapTools(
|
|
|
199
247
|
description: def.description,
|
|
200
248
|
parameters: def.parameters,
|
|
201
249
|
requiresConfirmation: def.requiresConfirmation,
|
|
202
|
-
handler,
|
|
250
|
+
handler: (args) => handler(normalizeKaleidoswapArgs(def.name, args)),
|
|
203
251
|
});
|
|
204
252
|
}
|
|
205
253
|
return new InProcessToolSource(opts.id ?? 'kaleidoswap', bound);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** KaleidoSwap maker: tool contract and recipes (price, atomic swap, channel order, buy asset channel). */
|
|
2
|
+
export {
|
|
3
|
+
KALEIDOSWAP_TOOLS,
|
|
4
|
+
KALEIDOSWAP_SPEND_TOOLS,
|
|
5
|
+
isKaleidoswapSpendTool,
|
|
6
|
+
normalizeKaleidoswapArgs,
|
|
7
|
+
getKaleidoswapTool,
|
|
8
|
+
kaleidoswapTools,
|
|
9
|
+
bindKaleidoswapTools,
|
|
10
|
+
} from './contract.js';
|
|
11
|
+
export type {
|
|
12
|
+
KaleidoswapGroup,
|
|
13
|
+
KaleidoswapToolDef,
|
|
14
|
+
KaleidoswapHandler,
|
|
15
|
+
BindKaleidoswapOptions,
|
|
16
|
+
} from './contract.js';
|
|
17
|
+
export { kaleidoswapPriceRecipe } from '../recipe/kaleidoswap-price.js';
|
|
18
|
+
export { kaleidoswapAtomicRecipe } from '../recipe/kaleidoswap-atomic.js';
|
|
19
|
+
export { kaleidoswapChannelOrderRecipe, extractChannelOrder } from '../recipe/kaleidoswap-channel-order.js';
|
|
20
|
+
export { buyAssetChannelRecipe, extractBuyAsset } from '../recipe/buy-asset-channel.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Knowledge packs and corpus adapters for RAG, and the BTC Map merchant tool. */
|
|
2
|
+
export { BITCOIN_COPILOT_DOCS } from './bitcoin-copilot.js';
|
|
3
|
+
export { walletHistoryToDocuments, contactsToDocuments } from './wallet.js';
|
|
4
|
+
export type { WalletTx, Contact } from './wallet.js';
|
|
5
|
+
export { merchantsToDocuments } from './merchants.js';
|
|
6
|
+
export type { Merchant } from './merchants.js';
|
|
7
|
+
export { createBtcMapToolSource } from './btc-map.js';
|
|
8
|
+
export type {
|
|
9
|
+
BtcMapToolOptions,
|
|
10
|
+
BtcMapMerchant,
|
|
11
|
+
BtcMapFetch,
|
|
12
|
+
LocationProvider,
|
|
13
|
+
LatLng,
|
|
14
|
+
} from './btc-map.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** LSPS1 Lightning Service Provider channel orders: tool contract. */
|
|
2
|
+
export {
|
|
3
|
+
LSPS1_TOOLS,
|
|
4
|
+
LSPS1_SPEND_TOOLS,
|
|
5
|
+
isLsps1SpendTool,
|
|
6
|
+
getLsps1Tool,
|
|
7
|
+
bindLsps1Tools,
|
|
8
|
+
} from './contract.js';
|
|
9
|
+
export type {
|
|
10
|
+
Lsps1ToolDef,
|
|
11
|
+
Lsps1Handler,
|
|
12
|
+
BindLsps1Options,
|
|
13
|
+
} from './contract.js';
|
package/src/providers/types.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* LLMProvider — the only thing the Engine talks to for inference.
|
|
3
3
|
*
|
|
4
4
|
* Each host implements this over its own LLM transport:
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
5
|
+
* - QVAC on-device (rate, desktop sidecar, CLI): `@kaleidorg/mind/qvac`
|
|
6
|
+
* - any OpenAI-compatible server (Ollama, LM Studio, hosted APIs):
|
|
7
|
+
* `@kaleidorg/mind/openai`
|
|
8
8
|
*
|
|
9
9
|
* The core package never imports any LLM SDK — it only depends on this
|
|
10
10
|
* interface, so it stays pure TS and bundles anywhere.
|
package/src/qvac/index.ts
CHANGED
|
@@ -96,23 +96,6 @@ describe('createQvacProvider.runTurn', () => {
|
|
|
96
96
|
expect(calls[0].generationParams).toBeUndefined();
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
it('caps thinking by tokens — cancels the run and returns a fallback', async () => {
|
|
100
|
-
const cancel = vi.fn(async () => {});
|
|
101
|
-
const { fn } = fakeCompletion(
|
|
102
|
-
{ contentText: '', toolCalls: [], raw: { fullText: '' }, stopReason: 'cancelled' },
|
|
103
|
-
[{ type: 'thinkingDelta', text: 'z'.repeat(400) }], // ~100 tokens, budget 4 (+ backstop headroom)
|
|
104
|
-
);
|
|
105
|
-
const p = createQvacProvider({
|
|
106
|
-
completion: fn as any,
|
|
107
|
-
cancel: cancel as any,
|
|
108
|
-
getModelId: () => 'm1',
|
|
109
|
-
maxThinkingTokens: 4,
|
|
110
|
-
});
|
|
111
|
-
const out = await p.runTurn({ messages: [{ role: 'user', content: 'think hard' }], tools: [] });
|
|
112
|
-
expect(cancel).toHaveBeenCalledWith({ requestId: 'req-1' });
|
|
113
|
-
expect(out.text).toMatch(/thinking budget/i);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
99
|
it('sends the thinking cap as the SDK reasoning_budget', async () => {
|
|
117
100
|
const { fn, calls } = fakeCompletion({ contentText: 'ok', toolCalls: [], raw: { fullText: 'ok' } });
|
|
118
101
|
const p = createQvacProvider({ completion: fn as any, cancel: noopCancel, getModelId: () => 'm1', maxThinkingTokens: 128 });
|
|
@@ -140,6 +123,23 @@ describe('createQvacProvider.runTurn', () => {
|
|
|
140
123
|
expect(deleted).toEqual([{ kvCacheKey: 'run-1' }]);
|
|
141
124
|
});
|
|
142
125
|
|
|
126
|
+
it('retries a turn without reasoning when the tool grammar rejects the inserted </think>', async () => {
|
|
127
|
+
const calls: any[] = [];
|
|
128
|
+
const fn = (params: any) => {
|
|
129
|
+
calls.push(params);
|
|
130
|
+
if (calls.length === 1) throw new Error('Unexpected empty grammar stack after accepting piece: </think> (248069)');
|
|
131
|
+
return { requestId: 'req-2', events: (async function* () {})(), final: Promise.resolve({ contentText: 'ok', toolCalls: [], raw: { fullText: 'ok' } }) };
|
|
132
|
+
};
|
|
133
|
+
const p = createQvacProvider({ completion: fn as any, cancel: noopCancel, getModelId: () => 'm1', maxThinkingTokens: 128 });
|
|
134
|
+
const out = await p.runTurn({ messages: [{ role: 'user', content: 'x' }], tools: [] });
|
|
135
|
+
expect(out.text).toBe('ok');
|
|
136
|
+
expect(calls.map((c) => c.generationParams?.reasoning_budget)).toEqual([128, 0]);
|
|
137
|
+
await expect(
|
|
138
|
+
createQvacProvider({ completion: (() => { throw new Error('boom'); }) as any, cancel: noopCancel, getModelId: () => 'm1' })
|
|
139
|
+
.runTurn({ messages: [{ role: 'user', content: 'x' }], tools: [] }),
|
|
140
|
+
).rejects.toThrow('boom');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
143
|
it('keeps the reasoning budget below the output cap', async () => {
|
|
144
144
|
const { fn, calls } = fakeCompletion({ contentText: 'ok', toolCalls: [], raw: { fullText: 'ok' } });
|
|
145
145
|
const p = createQvacProvider({ completion: fn as any, cancel: noopCancel, getModelId: () => 'm1', defaultMaxTokens: 512, maxThinkingTokens: 512 });
|
package/src/qvac/provider.ts
CHANGED
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
* desktop sidecar its lazily-loaded SDK facade — which also makes this provider
|
|
11
11
|
* unit-testable with a fake completion.
|
|
12
12
|
*
|
|
13
|
-
* The host owns model lifecycle (load/unload
|
|
13
|
+
* The host owns model lifecycle (load/unload) and passes
|
|
14
14
|
* `getModelId()` so a turn always runs against the currently-loaded model.
|
|
15
15
|
* Tools are forwarded by schema only; the Engine executes them via its
|
|
16
|
-
* ToolSources, so signing
|
|
17
|
-
* delegated to a desktop peer.
|
|
16
|
+
* ToolSources, so signing and spending stay on the host.
|
|
18
17
|
*/
|
|
19
18
|
import type * as QvacSdk from '@qvac/sdk';
|
|
20
19
|
import type { InferenceMetrics, LLMProvider, TurnInput, TurnOutput } from '../providers/types.js';
|
|
@@ -44,15 +43,16 @@ export interface QvacProviderOptions {
|
|
|
44
43
|
/**
|
|
45
44
|
* Cap `<think>` reasoning at this many TOKENS (not seconds — tok/s varies).
|
|
46
45
|
* Sent as the SDK's `reasoning_budget`, so the model closes its reasoning and
|
|
47
|
-
* answers.
|
|
48
|
-
* short fallback is returned instead of hanging on "Thinking…". Omit for
|
|
49
|
-
* unlimited reasoning.
|
|
46
|
+
* answers. Kept below the output cap. Omit for unlimited reasoning.
|
|
50
47
|
*/
|
|
51
48
|
maxThinkingTokens?: number;
|
|
52
49
|
/**
|
|
53
|
-
* Keep each agentic run in a QVAC KV-cache session
|
|
54
|
-
* so calls after the first send only the new
|
|
55
|
-
* the whole prompt. Needs `deleteCache` to
|
|
50
|
+
* Experimental. Keep each agentic run in a QVAC KV-cache session
|
|
51
|
+
* (`kvCache: sessionKey`), so calls after the first send only the new
|
|
52
|
+
* message instead of re-reading the whole prompt. Needs `deleteCache` to
|
|
53
|
+
* drop the session at the end. In the rgb-agent eval (Qwen3.5 2B) it cut
|
|
54
|
+
* time to first token from ~7 s to ~0.2 s, but the model copied a Lightning
|
|
55
|
+
* invoice correctly in 4/10 runs with it vs 10/10 without. Off by default.
|
|
56
56
|
*/
|
|
57
57
|
sessionCache?: boolean;
|
|
58
58
|
/** The SDK's `deleteCache` (injected); used with `sessionCache`. */
|
|
@@ -76,16 +76,17 @@ export interface QvacTurnInput extends TurnInput {
|
|
|
76
76
|
onStats?: (stats: QvacTurnStats) => void;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
/**
|
|
80
|
+
* llama.cpp's tool-call grammar can reject the `</think>` it inserts when a
|
|
81
|
+
* reasoning budget runs out ("Unexpected empty grammar stack after accepting
|
|
82
|
+
* piece: </think>"), which aborts the turn.
|
|
83
|
+
*/
|
|
84
|
+
function isReasoningGrammarError(err: unknown): boolean {
|
|
85
|
+
return /empty grammar stack/i.test(err instanceof Error ? err.message : String(err));
|
|
86
|
+
}
|
|
83
87
|
|
|
84
88
|
export function createQvacProvider(options: QvacProviderOptions): LLMProvider {
|
|
85
|
-
|
|
86
|
-
name: 'qvac',
|
|
87
|
-
|
|
88
|
-
async runTurn(input: QvacTurnInput): Promise<TurnOutput> {
|
|
89
|
+
const runTurnOnce = async (input: QvacTurnInput): Promise<TurnOutput> => {
|
|
89
90
|
const modelId = options.getModelId();
|
|
90
91
|
if (!modelId) throw new Error('QVAC model not loaded');
|
|
91
92
|
|
|
@@ -153,24 +154,12 @@ export function createQvacProvider(options: QvacProviderOptions): LLMProvider {
|
|
|
153
154
|
const result = await consumeRun(run, {
|
|
154
155
|
onToken: input.onToken,
|
|
155
156
|
onThinking: input.onThinking ?? options.onThinking,
|
|
156
|
-
// Backstop only: the SDK enforces the budget itself, and our count is a
|
|
157
|
-
// char-based estimate, so leave headroom before cancelling.
|
|
158
|
-
maxThinkingTokens:
|
|
159
|
-
maxThinkingTokens === undefined ? undefined : Math.ceil(maxThinkingTokens * 1.25) + 32,
|
|
160
|
-
// Cancel the in-flight run the moment the thinking budget is blown — the
|
|
161
|
-
// SDK keeps generating otherwise. Fire-and-forget; `final` then resolves.
|
|
162
|
-
onThinkingBudgetExceeded: () => {
|
|
163
|
-
void options.cancel({ requestId: run.requestId }).catch(() => {});
|
|
164
|
-
},
|
|
165
157
|
});
|
|
166
158
|
|
|
167
159
|
// Surface the real per-turn inference stats (backend device + throughput).
|
|
168
160
|
if (result.stats) (input.onStats ?? options.onStats)?.(result.stats);
|
|
169
161
|
|
|
170
|
-
|
|
171
|
-
// instead of an empty bubble so the agentic loop ends cleanly.
|
|
172
|
-
const text =
|
|
173
|
-
result.text || (result.thinkingBudgetExceeded ? THINKING_BUDGET_FALLBACK : result.text);
|
|
162
|
+
const text = result.text;
|
|
174
163
|
const promptTokens = result.stats?.promptTokens;
|
|
175
164
|
const generated = result.stats?.generatedTokens;
|
|
176
165
|
const totalTokens =
|
|
@@ -199,7 +188,7 @@ export function createQvacProvider(options: QvacProviderOptions): LLMProvider {
|
|
|
199
188
|
};
|
|
200
189
|
|
|
201
190
|
const incomplete =
|
|
202
|
-
!result.text && result.toolCalls.length === 0 &&
|
|
191
|
+
!result.text && result.toolCalls.length === 0 && !!result.truncated;
|
|
203
192
|
return {
|
|
204
193
|
text,
|
|
205
194
|
rawContent: result.rawContent,
|
|
@@ -209,6 +198,19 @@ export function createQvacProvider(options: QvacProviderOptions): LLMProvider {
|
|
|
209
198
|
inference,
|
|
210
199
|
...(incomplete ? { incomplete: true } : {}),
|
|
211
200
|
};
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
name: 'qvac',
|
|
205
|
+
|
|
206
|
+
async runTurn(input: QvacTurnInput): Promise<TurnOutput> {
|
|
207
|
+
try {
|
|
208
|
+
return await runTurnOnce(input);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
// Retry once without reasoning: no budget, so nothing to insert.
|
|
211
|
+
if (input.thinking === 'off' || !isReasoningGrammarError(err)) throw err;
|
|
212
|
+
return runTurnOnce({ ...input, thinking: 'off' });
|
|
213
|
+
}
|
|
212
214
|
},
|
|
213
215
|
|
|
214
216
|
async endSession(sessionKey: string): Promise<void> {
|
package/src/qvac/stream.ts
CHANGED
|
@@ -28,9 +28,10 @@ export interface StreamHandlers {
|
|
|
28
28
|
/** The model's `<think>` reasoning, streamed separately. */
|
|
29
29
|
onThinking?: (token: string) => void;
|
|
30
30
|
/**
|
|
31
|
-
* Stop
|
|
32
|
-
* from characters)
|
|
33
|
-
*
|
|
31
|
+
* Stop forwarding once `<think>` reasoning exceeds this many tokens
|
|
32
|
+
* (estimated from characters) and call `onThinkingBudgetExceeded`. The QVAC
|
|
33
|
+
* provider uses the SDK's `reasoning_budget` instead; this is for callers
|
|
34
|
+
* driving `completion()` themselves.
|
|
34
35
|
*/
|
|
35
36
|
maxThinkingTokens?: number;
|
|
36
37
|
/**
|
package/src/qvac/voice.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* injected (type-only `@qvac/sdk` import, erased at build) so this carries no
|
|
5
5
|
* runtime SDK dependency and is unit-testable with fakes.
|
|
6
6
|
*
|
|
7
|
-
* The host still owns model lifecycle (download, load
|
|
7
|
+
* The host still owns model lifecycle (download, load) and
|
|
8
8
|
* audio I/O (mic capture, playback). It passes the loaded model-id resolvers;
|
|
9
9
|
* this module does the SDK calls + the text gating that must be identical
|
|
10
10
|
* everywhere (payment-string redaction, U+0060 refusal, file:// stripping).
|
package/src/recipe/asset-send.ts
CHANGED
|
@@ -69,7 +69,7 @@ export const assetSendRecipe: Recipe = {
|
|
|
69
69
|
tool: 'rln_send_asset',
|
|
70
70
|
args: (ctx: RecipeContext) => {
|
|
71
71
|
const contact = ctx.results.contact as { ln_address?: string } | undefined;
|
|
72
|
-
return {
|
|
72
|
+
return { asset_id: ctx.slots.asset, amount: ctx.slots.amount, recipient_id: contact?.ln_address ?? ctx.slots.recipient };
|
|
73
73
|
},
|
|
74
74
|
},
|
|
75
75
|
summary: (ctx) => {
|
|
@@ -257,7 +257,7 @@ describe('runRecipe — asset send', () => {
|
|
|
257
257
|
const res = await runRecipe(assetSendRecipe, 'send 10 usdt to bob', { provider: approve, tools, onConfirm });
|
|
258
258
|
expect(res.status).toBe('done');
|
|
259
259
|
expect(onConfirm).toHaveBeenCalledOnce();
|
|
260
|
-
expect(sent[0]).toEqual({
|
|
260
|
+
expect(sent[0]).toEqual({ asset_id: 'USDT', amount: 10, recipient_id: 'bob@x.com' });
|
|
261
261
|
});
|
|
262
262
|
});
|
|
263
263
|
|