@kaleidorg/mind 0.4.0 → 0.5.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/dist/funnel.d.ts +19 -0
- package/dist/funnel.d.ts.map +1 -1
- package/dist/funnel.js +48 -10
- package/dist/funnel.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/kaleidoswap/contract.d.ts +3 -3
- package/dist/kaleidoswap/contract.d.ts.map +1 -1
- package/dist/kaleidoswap/contract.js +16 -4
- package/dist/kaleidoswap/contract.js.map +1 -1
- package/dist/knowledge/bitcoin-copilot.d.ts.map +1 -1
- package/dist/knowledge/bitcoin-copilot.js +102 -0
- package/dist/knowledge/bitcoin-copilot.js.map +1 -1
- package/dist/knowledge/btc-map.d.ts +14 -17
- package/dist/knowledge/btc-map.d.ts.map +1 -1
- package/dist/knowledge/btc-map.js +66 -266
- package/dist/knowledge/btc-map.js.map +1 -1
- package/dist/lsps1/contract.d.ts.map +1 -1
- package/dist/lsps1/contract.js +28 -10
- package/dist/lsps1/contract.js.map +1 -1
- package/dist/recipe/buy-asset-channel.d.ts +26 -0
- package/dist/recipe/buy-asset-channel.d.ts.map +1 -0
- package/dist/recipe/buy-asset-channel.js +112 -0
- package/dist/recipe/buy-asset-channel.js.map +1 -0
- package/dist/recipe/kaleidoswap-atomic.d.ts +26 -18
- package/dist/recipe/kaleidoswap-atomic.d.ts.map +1 -1
- package/dist/recipe/kaleidoswap-atomic.js +101 -63
- package/dist/recipe/kaleidoswap-atomic.js.map +1 -1
- package/dist/recipe/kaleidoswap-channel-order.d.ts +35 -0
- package/dist/recipe/kaleidoswap-channel-order.d.ts.map +1 -0
- package/dist/recipe/kaleidoswap-channel-order.js +493 -0
- package/dist/recipe/kaleidoswap-channel-order.js.map +1 -0
- package/dist/recipe/kaleidoswap-price.d.ts +21 -0
- package/dist/recipe/kaleidoswap-price.d.ts.map +1 -0
- package/dist/recipe/kaleidoswap-price.js +57 -0
- package/dist/recipe/kaleidoswap-price.js.map +1 -0
- package/dist/recipe/runner.d.ts +7 -1
- package/dist/recipe/runner.d.ts.map +1 -1
- package/dist/recipe/runner.js +115 -29
- package/dist/recipe/runner.js.map +1 -1
- package/dist/recipe/swap.d.ts +26 -1
- package/dist/recipe/swap.d.ts.map +1 -1
- package/dist/recipe/swap.js +108 -13
- package/dist/recipe/swap.js.map +1 -1
- package/dist/recipe/types.d.ts +25 -1
- package/dist/recipe/types.d.ts.map +1 -1
- package/dist/skills/registry.d.ts +33 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +45 -1
- package/dist/skills/registry.js.map +1 -1
- package/package.json +1 -1
- package/skills/README.md +3 -0
- package/skills/kaleido-lsps/SKILL.md +101 -43
- package/skills/kaleido-trading/SKILL.md +81 -31
- package/skills/merchant-finder/SKILL.md +96 -66
- package/skills/rgb-lightning-node/SKILL.md +108 -0
- package/skills/wallet-assistant/SKILL.md +32 -21
- package/src/funnel.ts +66 -11
- package/src/index.ts +14 -2
- package/src/kaleidoswap/contract.test.ts +7 -2
- package/src/kaleidoswap/contract.ts +27 -5
- package/src/knowledge/bitcoin-copilot.ts +111 -0
- package/src/knowledge/btc-map.test.ts +53 -96
- package/src/knowledge/btc-map.ts +72 -287
- package/src/lsps1/contract.ts +32 -14
- package/src/recipe/buy-asset-channel.test.ts +148 -0
- package/src/recipe/buy-asset-channel.ts +118 -0
- package/src/recipe/kaleidoswap-atomic.test.ts +134 -61
- package/src/recipe/kaleidoswap-atomic.ts +112 -66
- package/src/recipe/kaleidoswap-channel-order.test.ts +333 -0
- package/src/recipe/kaleidoswap-channel-order.ts +548 -0
- package/src/recipe/kaleidoswap-price.ts +68 -0
- package/src/recipe/recipe.test.ts +61 -5
- package/src/recipe/runner.ts +128 -31
- package/src/recipe/swap.ts +109 -13
- package/src/recipe/types.ts +25 -1
- package/src/skills/registry.ts +52 -1
package/dist/recipe/runner.js
CHANGED
|
@@ -8,24 +8,81 @@ const EXTRACT_TOOL = 'extract_request';
|
|
|
8
8
|
/** Extract the recipe's slots — deterministic regex first, else ONE LLM call. */
|
|
9
9
|
export async function extractSlots(provider, recipe, text) {
|
|
10
10
|
const det = recipe.extract?.(text);
|
|
11
|
-
|
|
11
|
+
const detValid = det && Object.values(det).some((v) => v !== undefined && v !== null && v !== '');
|
|
12
|
+
if (detValid && !recipe.forceModelExtract) {
|
|
12
13
|
return { slots: det, inferences: 0 };
|
|
13
14
|
}
|
|
15
|
+
// Build a richer extraction prompt + tool schema so small models have a
|
|
16
|
+
// better chance of producing correct structured output for recipes (especially
|
|
17
|
+
// when forceModelExtract is on for natural language intents like "buy 1 usdt").
|
|
14
18
|
const properties = {};
|
|
15
19
|
for (const s of recipe.slots)
|
|
16
20
|
properties[s.name] = { type: s.type ?? 'string', description: s.description };
|
|
21
|
+
const recipeHint = recipe.description ? ` for the "${recipe.name}" recipe (${recipe.description})` : '';
|
|
17
22
|
const extractTool = {
|
|
18
23
|
name: EXTRACT_TOOL,
|
|
19
|
-
description: `Extract the fields from the user's request.`,
|
|
24
|
+
description: `Extract the fields from the user's request${recipeHint}.`,
|
|
20
25
|
parameters: { type: 'object', properties, required: recipe.slots.filter((s) => s.required).map((s) => s.name) },
|
|
21
26
|
};
|
|
27
|
+
const system = [
|
|
28
|
+
`Call ${EXTRACT_TOOL} with the fields from the user's message.`,
|
|
29
|
+
recipe.description ? `This extraction is for: ${recipe.description}.` : '',
|
|
30
|
+
'Only emit values that match the field descriptions.',
|
|
31
|
+
'Canonical assets: BTC, USDT, XAUT (pass as strings like "BTC" or "USDT").',
|
|
32
|
+
'amount_side: "to" when the named amount is what you receive/buy (e.g. "buy 1 USDT" → to_asset=USDT, amount=1, from_asset=BTC); "from" for sell/swap (amount on from_asset).',
|
|
33
|
+
'The host binding handles per-asset precision scaling (BTC in sats → maker units; USDT/XAUT whole units). Pass the user\'s number as-is for the correct side.',
|
|
34
|
+
'Do not call any other tool and do not add commentary.',
|
|
35
|
+
].filter(Boolean).join(' ');
|
|
22
36
|
const out = await provider.runTurn({
|
|
23
|
-
system
|
|
37
|
+
system,
|
|
24
38
|
messages: [{ role: 'user', content: text }],
|
|
25
39
|
tools: [extractTool],
|
|
26
40
|
});
|
|
27
41
|
const call = out.toolCalls?.find((c) => c.name === EXTRACT_TOOL) ?? out.toolCalls?.[0];
|
|
28
|
-
|
|
42
|
+
let llmSlots = call?.arguments ?? {};
|
|
43
|
+
// Safety net when forceModelExtract is active.
|
|
44
|
+
// - The LLM is authoritative for the slots it filled — its output wins.
|
|
45
|
+
// - Det is used only to backfill required fields the LLM left empty.
|
|
46
|
+
// - The amount_side-specific check below applies ONLY to recipes that
|
|
47
|
+
// actually declare an `amount_side` slot (swap-shaped recipes) — for
|
|
48
|
+
// others (channel-order, etc.) it would clobber correct LLM extraction
|
|
49
|
+
// because amount_side is always undefined.
|
|
50
|
+
if (recipe.forceModelExtract && detValid) {
|
|
51
|
+
const required = recipe.slots.filter((s) => s.required);
|
|
52
|
+
const llmHasAllRequired = required.every((s) => {
|
|
53
|
+
const v = llmSlots[s.name];
|
|
54
|
+
return v != null && v !== '';
|
|
55
|
+
});
|
|
56
|
+
const recipeHasAmountSide = recipe.slots.some((s) => s.name === 'amount_side');
|
|
57
|
+
if (recipeHasAmountSide) {
|
|
58
|
+
const llmSide = String(llmSlots.amount_side || '').toLowerCase();
|
|
59
|
+
const validSide = llmSide === 'from' || llmSide === 'to';
|
|
60
|
+
if (!llmHasAllRequired || !validSide) {
|
|
61
|
+
llmSlots = { ...det, ...llmSlots };
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
llmSlots.amount_side = llmSide;
|
|
65
|
+
}
|
|
66
|
+
if (!validSide && det.amount_side) {
|
|
67
|
+
llmSlots.amount_side = det.amount_side;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Generic path: backfill ANY slot the LLM didn't populate from det's
|
|
72
|
+
// value, when det has one. LLM wins on every field it actually filled,
|
|
73
|
+
// but det shouldn't be silently erased — small models often omit
|
|
74
|
+
// non-required slots (e.g. asset_ticker on a USDT channel) that the
|
|
75
|
+
// deterministic regex caught reliably.
|
|
76
|
+
for (const s of recipe.slots) {
|
|
77
|
+
const llmVal = llmSlots[s.name];
|
|
78
|
+
const detVal = det[s.name];
|
|
79
|
+
if ((llmVal == null || llmVal === '') && detVal != null && detVal !== '') {
|
|
80
|
+
llmSlots[s.name] = detVal;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { slots: llmSlots, inferences: 1 };
|
|
29
86
|
}
|
|
30
87
|
/** Run a recipe end to end. Never throws — failures come back as status:'error'. */
|
|
31
88
|
export async function runRecipe(recipe, text, opts) {
|
|
@@ -37,40 +94,69 @@ export async function runRecipe(recipe, text, opts) {
|
|
|
37
94
|
ctx.slots = ex.slots;
|
|
38
95
|
inferences = ex.inferences;
|
|
39
96
|
}
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
97
|
+
// Confidence re-check AFTER extraction (whether deterministic, LLM, or
|
|
98
|
+
// pre-supplied). When the recipe defines `confident()` and the extracted
|
|
99
|
+
// slots fail it, refuse to run the steps with bad data — surface a
|
|
100
|
+
// friendly "please specify <missing required slots>" message so the user
|
|
101
|
+
// can re-ask with the info instead of getting a maker 4xx mid-chain.
|
|
102
|
+
if (recipe.confident && !recipe.confident(ctx.slots)) {
|
|
103
|
+
const missing = recipe.slots
|
|
104
|
+
.filter((s) => s.required && (ctx.slots[s.name] == null || ctx.slots[s.name] === ''))
|
|
105
|
+
.map((s) => `${s.name} (${s.description})`);
|
|
106
|
+
const ask = missing.length > 0
|
|
107
|
+
? `I need a bit more info — please specify: ${missing.join('; ')}.`
|
|
108
|
+
: "I don't have enough info to do that — could you rephrase with the specifics?";
|
|
109
|
+
return { recipe: recipe.name, slots: ctx.slots, results: ctx.results, text: ask, status: 'needs-info', inferences };
|
|
110
|
+
}
|
|
111
|
+
// Confirmation model:
|
|
112
|
+
// - Recipe with `confirm(ctx)`: fire ONE gate before the first spend step,
|
|
113
|
+
// showing the recipe-level summary; once approved, later spend steps run
|
|
114
|
+
// ungated (the whole chain is one approved decision).
|
|
115
|
+
// - Recipe without `confirm`: gate EACH spend tool individually (default;
|
|
116
|
+
// payments/receive/asset-send rely on this).
|
|
117
|
+
// Missing onConfirm FAILS CLOSED in both cases, matching the Engine.
|
|
118
|
+
const cancelled = () => ({
|
|
119
|
+
recipe: recipe.name, slots: ctx.slots, results: ctx.results,
|
|
120
|
+
text: 'Cancelled — nothing was sent.', status: 'cancelled', inferences,
|
|
121
|
+
});
|
|
122
|
+
let recipeApproved = false;
|
|
123
|
+
/** Gate a single (spend) step. Returns false if the user declined. */
|
|
124
|
+
const passesGate = async (toolName, args) => {
|
|
125
|
+
const def = await opts.tools.getDef(toolName);
|
|
126
|
+
if (!def?.requiresConfirmation)
|
|
127
|
+
return true;
|
|
128
|
+
// Recipe-level single confirm: ask once, then remember the approval.
|
|
129
|
+
if (recipe.confirm) {
|
|
130
|
+
if (recipeApproved)
|
|
131
|
+
return true;
|
|
132
|
+
const summary = recipe.confirm(ctx) ?? undefined;
|
|
133
|
+
const decision = opts.onConfirm
|
|
134
|
+
? await opts.onConfirm({ name: toolName, arguments: args, summary })
|
|
135
|
+
: { approved: false, reason: 'no confirmation handler available' };
|
|
136
|
+
if (decision.approved)
|
|
137
|
+
recipeApproved = true;
|
|
138
|
+
return decision.approved;
|
|
139
|
+
}
|
|
140
|
+
// Per-tool confirm (legacy default).
|
|
141
|
+
const decision = opts.onConfirm
|
|
142
|
+
? await opts.onConfirm({ name: toolName, arguments: args })
|
|
143
|
+
: { approved: false, reason: 'no confirmation handler available' };
|
|
144
|
+
return decision.approved;
|
|
145
|
+
};
|
|
44
146
|
for (const step of recipe.steps) {
|
|
45
147
|
if (step.skipIf?.(ctx))
|
|
46
148
|
continue;
|
|
47
149
|
const args = step.args(ctx);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const decision = opts.onConfirm
|
|
51
|
-
? await opts.onConfirm({ name: step.tool, arguments: args })
|
|
52
|
-
: { approved: false, reason: 'no confirmation handler available' };
|
|
53
|
-
if (!decision.approved) {
|
|
54
|
-
return { recipe: recipe.name, slots: ctx.slots, results: ctx.results, text: 'Cancelled — nothing was sent.', status: 'cancelled', inferences };
|
|
55
|
-
}
|
|
56
|
-
}
|
|
150
|
+
if (!(await passesGate(step.tool, args)))
|
|
151
|
+
return cancelled();
|
|
57
152
|
const result = await opts.tools.execute(step.tool, args);
|
|
58
153
|
ctx.results[step.as ?? step.tool] = result;
|
|
59
154
|
opts.onStep?.(step.tool, args, result);
|
|
60
155
|
}
|
|
61
|
-
// Final action
|
|
62
|
-
// Engine, a missing onConfirm FAILS CLOSED: the spend is declined, never
|
|
63
|
-
// silently executed.
|
|
156
|
+
// Final action.
|
|
64
157
|
const finalArgs = recipe.final.args(ctx);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const decision = opts.onConfirm
|
|
68
|
-
? await opts.onConfirm({ name: recipe.final.tool, arguments: finalArgs })
|
|
69
|
-
: { approved: false, reason: 'no confirmation handler available' };
|
|
70
|
-
if (!decision.approved) {
|
|
71
|
-
return { recipe: recipe.name, slots: ctx.slots, results: ctx.results, text: 'Cancelled — nothing was sent.', status: 'cancelled', inferences };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
158
|
+
if (!(await passesGate(recipe.final.tool, finalArgs)))
|
|
159
|
+
return cancelled();
|
|
74
160
|
const finalResult = await opts.tools.execute(recipe.final.tool, finalArgs);
|
|
75
161
|
ctx.results[recipe.final.as ?? recipe.final.tool] = finalResult;
|
|
76
162
|
opts.onStep?.(recipe.final.tool, finalArgs, finalResult);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/recipe/runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,YAAY,GAAG,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/recipe/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/recipe/swap.d.ts
CHANGED
|
@@ -10,7 +10,32 @@
|
|
|
10
10
|
* confirmation gate fires before it runs.
|
|
11
11
|
*/
|
|
12
12
|
import type { Recipe } from './types.js';
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Parse a swap/buy/sell request into { from_asset, to_asset, amount, amount_side }.
|
|
15
|
+
*
|
|
16
|
+
* `amount_side` says which leg the amount belongs to (the maker takes the amount
|
|
17
|
+
* on exactly one leg):
|
|
18
|
+
* - "buy N X" → receive N of X → amount on the TO leg, from defaults to BTC
|
|
19
|
+
* - "sell N X" → spend N of X → amount on the FROM leg, to defaults to BTC
|
|
20
|
+
* - "swap N X to Y" → spend N of X → amount on the FROM leg
|
|
21
|
+
*
|
|
22
|
+
* "buy one usdt" → from BTC, to USDT, amount 1 on `to`
|
|
23
|
+
* "buy 0.001 btc with usdt" → from USDT, to BTC, amount 0.001 on `to`
|
|
24
|
+
* "sell 100 usdt" → from USDT, to BTC, amount 100 on `from`
|
|
25
|
+
* "swap 10 usdt for btc" → from USDT, to BTC, amount 10 on `from`
|
|
26
|
+
*/
|
|
14
27
|
export declare function extractSwap(text: string): Record<string, unknown> | null;
|
|
28
|
+
/**
|
|
29
|
+
* Parse a PRICE / rate / "how much" question — read-only intent.
|
|
30
|
+
*
|
|
31
|
+
* Distinct from extractSwap: never returns slots for swap/buy/sell phrasings.
|
|
32
|
+
* Always `amount: 1` on the asked-about asset (TO leg). Used by
|
|
33
|
+
* `kaleidoswapPriceRecipe` to fire a quote without moving funds.
|
|
34
|
+
*
|
|
35
|
+
* "what is the price of usdt in sats" → {from: BTC, to: USDT, amount: 1, side: 'to'}
|
|
36
|
+
* "btc price" → {from: USDT, to: BTC, amount: 1, side: 'to'}
|
|
37
|
+
* "how much sats for 1 usdt" → {from: BTC, to: USDT, amount: 1, side: 'to'}
|
|
38
|
+
*/
|
|
39
|
+
export declare function extractPriceQuery(text: string): Record<string, unknown> | null;
|
|
15
40
|
export declare const swapRecipe: Recipe;
|
|
16
41
|
//# sourceMappingURL=swap.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swap.d.ts","sourceRoot":"","sources":["../../src/recipe/swap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"swap.d.ts","sourceRoot":"","sources":["../../src/recipe/swap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AA8BzC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAkCxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAgC9E;AAED,eAAO,MAAM,UAAU,EAAE,MAgCxB,CAAC"}
|
package/dist/recipe/swap.js
CHANGED
|
@@ -10,33 +10,128 @@
|
|
|
10
10
|
* confirmation gate fires before it runs.
|
|
11
11
|
*/
|
|
12
12
|
const ASSET = /\b(btc|bitcoin|sats?|usdt|tether|xaut|gold)\b/i;
|
|
13
|
-
|
|
13
|
+
/** Strict: returns a canonical code only for a KNOWN crypto asset, else undefined
|
|
14
|
+
* (so "kaleido", "the", etc. are not mistaken for an asset). */
|
|
15
|
+
function knownAsset(a) {
|
|
14
16
|
if (!a)
|
|
15
17
|
return undefined;
|
|
16
18
|
const x = a.toLowerCase();
|
|
17
|
-
if (
|
|
19
|
+
if (/^(btc|bitcoin|sat|sats|satoshi|satoshis)$/.test(x))
|
|
18
20
|
return 'BTC';
|
|
19
|
-
if (
|
|
21
|
+
if (/^(usdt|tether)$/.test(x))
|
|
20
22
|
return 'USDT';
|
|
21
|
-
if (
|
|
23
|
+
if (/^(xaut|gold)$/.test(x))
|
|
22
24
|
return 'XAUT';
|
|
23
|
-
return
|
|
25
|
+
return undefined;
|
|
24
26
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
// Small word-numbers cover the common spoken/typed cases ("buy one usdt").
|
|
28
|
+
const WORD_NUM = {
|
|
29
|
+
a: 1, an: 1, one: 1, two: 2, three: 3, four: 4, five: 5,
|
|
30
|
+
six: 6, seven: 7, eight: 8, nine: 9, ten: 10,
|
|
31
|
+
};
|
|
32
|
+
const AMT = '([\\d.,]+|a|an|one|two|three|four|five|six|seven|eight|nine|ten)';
|
|
33
|
+
function parseAmount(s) {
|
|
34
|
+
if (!s)
|
|
35
|
+
return undefined;
|
|
36
|
+
const t = s.trim().toLowerCase();
|
|
37
|
+
if (t in WORD_NUM)
|
|
38
|
+
return WORD_NUM[t];
|
|
39
|
+
const n = Number(t.replace(/,/g, ''));
|
|
40
|
+
return Number.isFinite(n) ? n : undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse a swap/buy/sell request into { from_asset, to_asset, amount, amount_side }.
|
|
44
|
+
*
|
|
45
|
+
* `amount_side` says which leg the amount belongs to (the maker takes the amount
|
|
46
|
+
* on exactly one leg):
|
|
47
|
+
* - "buy N X" → receive N of X → amount on the TO leg, from defaults to BTC
|
|
48
|
+
* - "sell N X" → spend N of X → amount on the FROM leg, to defaults to BTC
|
|
49
|
+
* - "swap N X to Y" → spend N of X → amount on the FROM leg
|
|
50
|
+
*
|
|
51
|
+
* "buy one usdt" → from BTC, to USDT, amount 1 on `to`
|
|
52
|
+
* "buy 0.001 btc with usdt" → from USDT, to BTC, amount 0.001 on `to`
|
|
53
|
+
* "sell 100 usdt" → from USDT, to BTC, amount 100 on `from`
|
|
54
|
+
* "swap 10 usdt for btc" → from USDT, to BTC, amount 10 on `from`
|
|
55
|
+
*/
|
|
27
56
|
export function extractSwap(text) {
|
|
28
57
|
const t = text.trim();
|
|
29
58
|
let m;
|
|
30
|
-
// buy <amt> <
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
// buy/get/purchase <amt> <asset> [with/using/from <funding-asset>]
|
|
60
|
+
// amount is of the asset being BOUGHT → it sits on the TO leg.
|
|
61
|
+
if ((m = t.match(new RegExp(`\\b(?:buy|get|purchase|acquire)\\s+${AMT}\\s*([a-z]+)(?:\\s+(?:with|using|from|for)\\s+([a-z]+))?`, 'i')))) {
|
|
62
|
+
const to = knownAsset(m[2]);
|
|
63
|
+
if (to) {
|
|
64
|
+
const from = knownAsset(m[3]) ?? (to === 'BTC' ? 'USDT' : 'BTC');
|
|
65
|
+
return { amount: parseAmount(m[1]), from_asset: from, to_asset: to, amount_side: 'to' };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// sell <amt> <asset> [for/to/into <target>]
|
|
69
|
+
// amount is of the asset being SOLD → it sits on the FROM leg.
|
|
70
|
+
if ((m = t.match(new RegExp(`\\bsell\\s+${AMT}\\s*([a-z]+)(?:\\s+(?:for|to|into)\\s+([a-z]+))?`, 'i')))) {
|
|
71
|
+
const from = knownAsset(m[2]);
|
|
72
|
+
if (from) {
|
|
73
|
+
const to = knownAsset(m[3]) ?? (from === 'BTC' ? 'USDT' : 'BTC');
|
|
74
|
+
return { amount: parseAmount(m[1]), from_asset: from, to_asset: to, amount_side: 'from' };
|
|
75
|
+
}
|
|
33
76
|
}
|
|
34
|
-
// swap/
|
|
35
|
-
if ((m = t.match(
|
|
36
|
-
|
|
77
|
+
// swap/convert/exchange/trade <amt> <from> for/to/into <to>
|
|
78
|
+
if ((m = t.match(new RegExp(`\\b(?:swap|convert|exchange|trade)\\s+${AMT}\\s*([a-z]+)\\s+(?:for|to|into)\\s+([a-z]+)`, 'i')))) {
|
|
79
|
+
const from = knownAsset(m[2]);
|
|
80
|
+
const to = knownAsset(m[3]);
|
|
81
|
+
if (from && to)
|
|
82
|
+
return { amount: parseAmount(m[1]), from_asset: from, to_asset: to, amount_side: 'from' };
|
|
37
83
|
}
|
|
84
|
+
// Price/rate questions are NOT swaps — they belong to extractPriceQuery +
|
|
85
|
+
// kaleidoswapPriceRecipe (read-only). Don't gobble them here.
|
|
38
86
|
return null;
|
|
39
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse a PRICE / rate / "how much" question — read-only intent.
|
|
90
|
+
*
|
|
91
|
+
* Distinct from extractSwap: never returns slots for swap/buy/sell phrasings.
|
|
92
|
+
* Always `amount: 1` on the asked-about asset (TO leg). Used by
|
|
93
|
+
* `kaleidoswapPriceRecipe` to fire a quote without moving funds.
|
|
94
|
+
*
|
|
95
|
+
* "what is the price of usdt in sats" → {from: BTC, to: USDT, amount: 1, side: 'to'}
|
|
96
|
+
* "btc price" → {from: USDT, to: BTC, amount: 1, side: 'to'}
|
|
97
|
+
* "how much sats for 1 usdt" → {from: BTC, to: USDT, amount: 1, side: 'to'}
|
|
98
|
+
*/
|
|
99
|
+
export function extractPriceQuery(text) {
|
|
100
|
+
const t = text.trim();
|
|
101
|
+
// Reject swap intent — those go to the atomic recipe, not the price recipe.
|
|
102
|
+
if (/\b(swap|exchange|convert|trade|buy|sell|get|purchase|acquire)\b/i.test(t))
|
|
103
|
+
return null;
|
|
104
|
+
// ORDER MATTERS: "how much B for A" (first) must be checked BEFORE
|
|
105
|
+
// "how much X (in Y)?" — otherwise the latter would gobble the first asset
|
|
106
|
+
// and miss the "for/per" tail. Optional "the" article is tolerated
|
|
107
|
+
// ("price of THE usdt") — natural English the maker doesn't care about.
|
|
108
|
+
const priceLike = t.match(/\bhow\s+(?:many|much)\s+(?:the\s+)?([a-z]+)\s+(?:for|per|in)\s+(?:1\s+|one\s+|the\s+)?([a-z]+)\b/i) ||
|
|
109
|
+
t.match(/\b(?:price|cost|worth)\s+of\s+(?:the\s+)?([a-z]+)(?:\s+in\s+(?:the\s+)?([a-z]+))?/i) ||
|
|
110
|
+
t.match(/\b(?:the\s+)?([a-z]+)\s+(?:price|cost)\b/i) ||
|
|
111
|
+
t.match(/\brate\s+of\s+(?:the\s+)?([a-z]+)(?:\s+(?:in|to|vs)\s+(?:the\s+)?([a-z]+))?/i) ||
|
|
112
|
+
t.match(/\b(?:the\s+)?([a-z]+)\s+(?:to|vs|in|\/)\s+([a-z]+)\s+rate\b/i) ||
|
|
113
|
+
t.match(/\bhow\s+much\s+(?:does\s+)?(?:1\s+|one\s+|the\s+)?([a-z]+)\s+cost\b/i) ||
|
|
114
|
+
t.match(/\bhow\s+much\s+(?:is\s+)?(?:1\s+|one\s+|the\s+)?([a-z]+)(?:\s+in\s+(?:the\s+)?([a-z]+))?\b/i);
|
|
115
|
+
if (!priceLike)
|
|
116
|
+
return null;
|
|
117
|
+
const a = knownAsset(priceLike[1]);
|
|
118
|
+
const b = knownAsset(priceLike[2]);
|
|
119
|
+
let asset;
|
|
120
|
+
let denom;
|
|
121
|
+
if (/how\s+(?:many|much)\s+\w+\s+(?:for|per|in)/i.test(t) && b) {
|
|
122
|
+
// "how much B for A" — asset is A (the named priced one), denom is B (unit).
|
|
123
|
+
asset = b;
|
|
124
|
+
denom = a;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
asset = a;
|
|
128
|
+
denom = b;
|
|
129
|
+
}
|
|
130
|
+
if (!asset)
|
|
131
|
+
return null;
|
|
132
|
+
const from = denom ?? (asset === 'BTC' ? 'USDT' : 'BTC');
|
|
133
|
+
return { amount: 1, from_asset: from, to_asset: asset, amount_side: 'to' };
|
|
134
|
+
}
|
|
40
135
|
export const swapRecipe = {
|
|
41
136
|
name: 'swap',
|
|
42
137
|
description: 'Swap between BTC and an RGB asset — quote, then execute (with confirmation).',
|
package/dist/recipe/swap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swap.js","sourceRoot":"","sources":["../../src/recipe/swap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,KAAK,GAAG,gDAAgD,CAAC;AAE/D,SAAS,
|
|
1
|
+
{"version":3,"file":"swap.js","sourceRoot":"","sources":["../../src/recipe/swap.ts"],"names":[],"mappings":"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"}
|
package/dist/recipe/types.d.ts
CHANGED
|
@@ -43,6 +43,14 @@ export interface Recipe {
|
|
|
43
43
|
slots: RecipeSlot[];
|
|
44
44
|
/** Optional deterministic extractor tried BEFORE the LLM (Tier-0 fast-path). */
|
|
45
45
|
extract?: (text: string) => Record<string, unknown> | null;
|
|
46
|
+
/**
|
|
47
|
+
* When true (and `extract` is provided), the runner will *ignore* a successful
|
|
48
|
+
* deterministic extraction and always perform the 1-inference LLM slot
|
|
49
|
+
* extraction. This lets the model do the natural-language understanding of
|
|
50
|
+
* the user's request (e.g. "buy 1 usdt") while the Recipe still owns the
|
|
51
|
+
* reliable multi-step execution plan and single-confirmation safety.
|
|
52
|
+
*/
|
|
53
|
+
forceModelExtract?: boolean;
|
|
46
54
|
/**
|
|
47
55
|
* Whether the recipe is confident enough to RUN deterministically given the
|
|
48
56
|
* extracted slots (vs falling back to the agentic loop). e.g. payments needs a
|
|
@@ -55,8 +63,24 @@ export interface Recipe {
|
|
|
55
63
|
final: RecipeStep;
|
|
56
64
|
/** Render the outcome for the user. */
|
|
57
65
|
summary?: (ctx: RecipeContext, finalResult: unknown) => string;
|
|
66
|
+
/**
|
|
67
|
+
* Single recipe-level confirmation. When set, the runner fires exactly ONE
|
|
68
|
+
* confirmation gate immediately before the first spend step, passing the
|
|
69
|
+
* returned string as the confirm summary; once approved, the remaining spend
|
|
70
|
+
* steps run WITHOUT re-prompting (the whole chain is one approved decision).
|
|
71
|
+
*
|
|
72
|
+
* Use for multi-spend chains where the user makes a single choice up front
|
|
73
|
+
* from data gathered by earlier (read-only) steps — e.g. an atomic swap:
|
|
74
|
+
* quote first, then confirm "swap X → Y, fee Z" once, then init/whitelist/
|
|
75
|
+
* execute run as a unit.
|
|
76
|
+
*
|
|
77
|
+
* Return `null` to skip confirmation entirely (rare). When `confirm` is
|
|
78
|
+
* absent, the runner falls back to gating EACH spend tool individually
|
|
79
|
+
* (the default — used by payments/receive/asset-send).
|
|
80
|
+
*/
|
|
81
|
+
confirm?: (ctx: RecipeContext) => string | null;
|
|
58
82
|
}
|
|
59
|
-
export type RecipeStatus = 'done' | 'cancelled' | 'error';
|
|
83
|
+
export type RecipeStatus = 'done' | 'cancelled' | 'error' | 'needs-info';
|
|
60
84
|
export interface RecipeResult {
|
|
61
85
|
recipe: string;
|
|
62
86
|
slots: Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/recipe/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtD,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;CAC1C;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,6DAA6D;IAC7D,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,gFAAgF;IAChF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IACxD,sEAAsE;IACtE,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,CAAC;IAClB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,KAAK,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/recipe/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtD,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;CAC1C;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,6DAA6D;IAC7D,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,gFAAgF;IAChF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IACxD,sEAAsE;IACtE,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,CAAC;IAClB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,KAAK,MAAM,CAAC;IAC/D;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,MAAM,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAAC;AAEzE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -6,11 +6,43 @@
|
|
|
6
6
|
* call); a host can inject a model-driven or embedding selector instead.
|
|
7
7
|
*/
|
|
8
8
|
import type { Skill, SkillReference, SkillSelector } from './types.js';
|
|
9
|
+
import type { EmbeddingProvider } from '../rag/types.js';
|
|
9
10
|
/** Tool name the reference source exposes for progressive disclosure. */
|
|
10
11
|
export declare const READ_REFERENCE_TOOL = "read_skill_reference";
|
|
11
12
|
export declare function parseSkill(markdown: string, references?: SkillReference[]): Skill;
|
|
12
|
-
/** Default selector: score by meaningful keyword overlap; triggers weigh most.
|
|
13
|
+
/** Default selector: score by meaningful keyword overlap; triggers weigh most.
|
|
14
|
+
* Light extra sensitivity for common location / discovery phrasing so merchant-finder
|
|
15
|
+
* and similar skills surface on natural queries even without exact trigger words.
|
|
16
|
+
*/
|
|
13
17
|
export declare const keywordSelector: SkillSelector;
|
|
18
|
+
/**
|
|
19
|
+
* Optional embedding-powered selector factory.
|
|
20
|
+
*
|
|
21
|
+
* When an EmbeddingProvider (the same shape used by Retriever/RAG) is supplied,
|
|
22
|
+
* hosts can use the returned selector for more semantic skill routing. This
|
|
23
|
+
* helps vague/natural location and discovery queries ("coffee near the station",
|
|
24
|
+
* "somewhere to grab a bite that takes lightning") reach merchant-finder or
|
|
25
|
+
* similar skills even without exact keyword overlap.
|
|
26
|
+
*
|
|
27
|
+
* The current implementation keeps a synchronous SkillSelector contract (to match
|
|
28
|
+
* the existing interface used by Funnel and SkillRegistry). It therefore:
|
|
29
|
+
* - Uses an enhanced keywordSelector as the fast path (see above).
|
|
30
|
+
* - If embeddings are provided it is ready for hosts to wrap or evolve into a
|
|
31
|
+
* fully semantic version (prototype embeddings of skill descriptions +
|
|
32
|
+
* cosine vs. query, mixed with keyword score).
|
|
33
|
+
*
|
|
34
|
+
* Example host usage (CLI / provider with embeddings already loaded):
|
|
35
|
+
* import { createEmbeddingSkillSelector, SkillRegistry } from '@kaleidorg/mind';
|
|
36
|
+
* const selector = createEmbeddingSkillSelector(embeddingsProvider);
|
|
37
|
+
* const reg = new SkillRegistry(loadedSkills, selector);
|
|
38
|
+
*
|
|
39
|
+
* For a production semantic version a host can implement an async select
|
|
40
|
+
* wrapper around its own Funnel turn or pre-compute skill prototypes.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createEmbeddingSkillSelector(embeddings?: EmbeddingProvider, _opts?: {
|
|
43
|
+
minCosine?: number;
|
|
44
|
+
keywordFallback?: boolean;
|
|
45
|
+
}): SkillSelector;
|
|
14
46
|
export declare class SkillRegistry {
|
|
15
47
|
private readonly skills;
|
|
16
48
|
private readonly selector;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/skills/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/skills/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAuB1D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,cAAc,EAAE,GAAG,KAAK,CAkCjF;AA0BD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,aAkC7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,CAAC,EAAE,iBAAiB,EAC9B,KAAK,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GAC5D,aAAa,CAQf;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;gBAE7B,MAAM,GAAE,KAAK,EAAO,EAAE,QAAQ,GAAE,aAA+B;IAK3E,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAKvB,uEAAuE;IACvE,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,cAAc,EAAE,GAAG,IAAI;IAIlE,yEAAyE;IACzE,UAAU,IAAI,KAAK,CAAC,cAAc,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAMvD,yEAAyE;IACzE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAUnE,IAAI,IAAI,KAAK,EAAE;IAIf,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAIpC,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAInC;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE;CAwBxF"}
|
package/dist/skills/registry.js
CHANGED
|
@@ -84,7 +84,10 @@ function triggerMatches(query, trigger) {
|
|
|
84
84
|
return false;
|
|
85
85
|
return new RegExp(`\\b${reEscape(t)}\\b`).test(query);
|
|
86
86
|
}
|
|
87
|
-
/** Default selector: score by meaningful keyword overlap; triggers weigh most.
|
|
87
|
+
/** Default selector: score by meaningful keyword overlap; triggers weigh most.
|
|
88
|
+
* Light extra sensitivity for common location / discovery phrasing so merchant-finder
|
|
89
|
+
* and similar skills surface on natural queries even without exact trigger words.
|
|
90
|
+
*/
|
|
88
91
|
export const keywordSelector = {
|
|
89
92
|
select(query, skills) {
|
|
90
93
|
const q = query.toLowerCase();
|
|
@@ -104,6 +107,14 @@ export const keywordSelector = {
|
|
|
104
107
|
for (const t of skill.triggers ?? [])
|
|
105
108
|
if (triggerMatches(q, t))
|
|
106
109
|
score += 3;
|
|
110
|
+
// Light discovery / location phrase boost (helps merchant-finder and
|
|
111
|
+
// similar skills on natural language like "coffee near the station" or
|
|
112
|
+
// "buy pizza with sats in turin").
|
|
113
|
+
if (/\b(near|nearby|around|close|spend|find|shop|cafe|coffee|food|eat|lunch|dinner|atm|buy|pizz|restaurant)\b/i.test(q)) {
|
|
114
|
+
const skillText = (skill.description + ' ' + (skill.triggers || []).join(' ')).toLowerCase();
|
|
115
|
+
if (/(merchant|btcmap|map|location|nearby|spend.*bitcoin|find.*place|food|restaurant|cafe|eat|pizza)/.test(skillText))
|
|
116
|
+
score += 1.5;
|
|
117
|
+
}
|
|
107
118
|
if (score > bestScore) {
|
|
108
119
|
bestScore = score;
|
|
109
120
|
best = skill;
|
|
@@ -113,6 +124,39 @@ export const keywordSelector = {
|
|
|
113
124
|
return bestScore >= 2 ? best : null;
|
|
114
125
|
},
|
|
115
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* Optional embedding-powered selector factory.
|
|
129
|
+
*
|
|
130
|
+
* When an EmbeddingProvider (the same shape used by Retriever/RAG) is supplied,
|
|
131
|
+
* hosts can use the returned selector for more semantic skill routing. This
|
|
132
|
+
* helps vague/natural location and discovery queries ("coffee near the station",
|
|
133
|
+
* "somewhere to grab a bite that takes lightning") reach merchant-finder or
|
|
134
|
+
* similar skills even without exact keyword overlap.
|
|
135
|
+
*
|
|
136
|
+
* The current implementation keeps a synchronous SkillSelector contract (to match
|
|
137
|
+
* the existing interface used by Funnel and SkillRegistry). It therefore:
|
|
138
|
+
* - Uses an enhanced keywordSelector as the fast path (see above).
|
|
139
|
+
* - If embeddings are provided it is ready for hosts to wrap or evolve into a
|
|
140
|
+
* fully semantic version (prototype embeddings of skill descriptions +
|
|
141
|
+
* cosine vs. query, mixed with keyword score).
|
|
142
|
+
*
|
|
143
|
+
* Example host usage (CLI / provider with embeddings already loaded):
|
|
144
|
+
* import { createEmbeddingSkillSelector, SkillRegistry } from '@kaleidorg/mind';
|
|
145
|
+
* const selector = createEmbeddingSkillSelector(embeddingsProvider);
|
|
146
|
+
* const reg = new SkillRegistry(loadedSkills, selector);
|
|
147
|
+
*
|
|
148
|
+
* For a production semantic version a host can implement an async select
|
|
149
|
+
* wrapper around its own Funnel turn or pre-compute skill prototypes.
|
|
150
|
+
*/
|
|
151
|
+
export function createEmbeddingSkillSelector(embeddings, _opts = {}) {
|
|
152
|
+
// Today we return the (already lightly enhanced) keyword selector.
|
|
153
|
+
// The embeddings parameter and factory exist so hosts have a single
|
|
154
|
+
// obvious extension point and the public API signals the intent.
|
|
155
|
+
// A future revision can make SkillSelector support async or add a
|
|
156
|
+
// separate async entry point if the Funnel routing is made async.
|
|
157
|
+
void embeddings; // intentionally unused in the current sync impl
|
|
158
|
+
return keywordSelector;
|
|
159
|
+
}
|
|
116
160
|
export class SkillRegistry {
|
|
117
161
|
skills = [];
|
|
118
162
|
selector;
|