@kaleidorg/mind 0.0.1 → 0.2.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/capabilities.d.ts +34 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +34 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/context/budget.d.ts +29 -0
- package/dist/context/budget.d.ts.map +1 -0
- package/dist/context/budget.js +36 -0
- package/dist/context/budget.js.map +1 -0
- package/dist/context/builder.d.ts +39 -0
- package/dist/context/builder.d.ts.map +1 -0
- package/dist/context/builder.js +77 -0
- package/dist/context/builder.js.map +1 -0
- package/dist/engine.d.ts +9 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +18 -2
- package/dist/engine.js.map +1 -1
- package/dist/fastpath/fastpath.d.ts +38 -0
- package/dist/fastpath/fastpath.d.ts.map +1 -0
- package/dist/fastpath/fastpath.js +52 -0
- package/dist/fastpath/fastpath.js.map +1 -0
- package/dist/funnel.d.ts +111 -0
- package/dist/funnel.d.ts.map +1 -0
- package/dist/funnel.js +175 -0
- package/dist/funnel.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -1
- package/dist/knowledge/bitcoin-copilot.d.ts +11 -0
- package/dist/knowledge/bitcoin-copilot.d.ts.map +1 -0
- package/dist/knowledge/bitcoin-copilot.js +155 -0
- package/dist/knowledge/bitcoin-copilot.js.map +1 -0
- package/dist/knowledge/merchants.d.ts +24 -0
- package/dist/knowledge/merchants.d.ts.map +1 -0
- package/dist/knowledge/merchants.js +34 -0
- package/dist/knowledge/merchants.js.map +1 -0
- package/dist/knowledge/wallet.d.ts +34 -0
- package/dist/knowledge/wallet.d.ts.map +1 -0
- package/dist/knowledge/wallet.js +63 -0
- package/dist/knowledge/wallet.js.map +1 -0
- package/dist/memory/store.d.ts +34 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +103 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/tool.d.ts +9 -0
- package/dist/memory/tool.d.ts.map +1 -0
- package/dist/memory/tool.js +70 -0
- package/dist/memory/tool.js.map +1 -0
- package/dist/memory/types.d.ts +56 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +14 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/rag/retriever.d.ts +30 -0
- package/dist/rag/retriever.d.ts.map +1 -0
- package/dist/rag/retriever.js +72 -0
- package/dist/rag/retriever.js.map +1 -0
- package/dist/rag/tool.d.ts +15 -0
- package/dist/rag/tool.d.ts.map +1 -0
- package/dist/rag/tool.js +42 -0
- package/dist/rag/tool.js.map +1 -0
- package/dist/rag/types.d.ts +44 -0
- package/dist/rag/types.d.ts.map +1 -0
- package/dist/rag/types.js +11 -0
- package/dist/rag/types.js.map +1 -0
- package/dist/rag/vector-store.d.ts +23 -0
- package/dist/rag/vector-store.d.ts.map +1 -0
- package/dist/rag/vector-store.js +72 -0
- package/dist/rag/vector-store.js.map +1 -0
- package/dist/recipe/asset-send.d.ts +15 -0
- package/dist/recipe/asset-send.d.ts.map +1 -0
- package/dist/recipe/asset-send.js +83 -0
- package/dist/recipe/asset-send.js.map +1 -0
- package/dist/recipe/payments.d.ts +15 -0
- package/dist/recipe/payments.d.ts.map +1 -0
- package/dist/recipe/payments.js +119 -0
- package/dist/recipe/payments.js.map +1 -0
- package/dist/recipe/receive.d.ts +14 -0
- package/dist/recipe/receive.d.ts.map +1 -0
- package/dist/recipe/receive.js +109 -0
- package/dist/recipe/receive.js.map +1 -0
- package/dist/recipe/runner.d.ts +42 -0
- package/dist/recipe/runner.d.ts.map +1 -0
- package/dist/recipe/runner.js +94 -0
- package/dist/recipe/runner.js.map +1 -0
- package/dist/recipe/swap.d.ts +16 -0
- package/dist/recipe/swap.d.ts.map +1 -0
- package/dist/recipe/swap.js +73 -0
- package/dist/recipe/swap.js.map +1 -0
- package/dist/recipe/types.d.ts +71 -0
- package/dist/recipe/types.d.ts.map +1 -0
- package/dist/recipe/types.js +13 -0
- package/dist/recipe/types.js.map +1 -0
- package/dist/skills/bundle.d.ts +30 -0
- package/dist/skills/bundle.d.ts.map +1 -0
- package/dist/skills/bundle.js +24 -0
- package/dist/skills/bundle.js.map +1 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +59 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/reference-source.d.ts +18 -0
- package/dist/skills/reference-source.d.ts.map +1 -0
- package/dist/skills/reference-source.js +53 -0
- package/dist/skills/reference-source.js.map +1 -0
- package/dist/skills/registry.d.ts +41 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +167 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/types.d.ts +53 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +18 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tools/cli.d.ts +43 -0
- package/dist/tools/cli.d.ts.map +1 -0
- package/dist/tools/cli.js +61 -0
- package/dist/tools/cli.js.map +1 -0
- package/dist/tools/l402.d.ts +47 -0
- package/dist/tools/l402.d.ts.map +1 -0
- package/dist/tools/l402.js +84 -0
- package/dist/tools/l402.js.map +1 -0
- package/dist/tools/mcp.d.ts +3 -2
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mcp.js +3 -2
- package/dist/tools/mcp.js.map +1 -1
- package/dist/wallet/contract.d.ts +57 -0
- package/dist/wallet/contract.d.ts.map +1 -0
- package/dist/wallet/contract.js +113 -0
- package/dist/wallet/contract.js.map +1 -0
- package/package.json +16 -5
- package/scripts/bundle-skills.mjs +84 -0
- package/skills/README.md +74 -0
- package/skills/bitrefill/SKILL.md +66 -0
- package/skills/bitrefill/references/api.md +99 -0
- package/skills/bitrefill/references/browse.md +71 -0
- package/skills/bitrefill/references/capability-matrix.md +115 -0
- package/skills/bitrefill/references/cli-headless-auth.md +133 -0
- package/skills/bitrefill/references/cli.md +237 -0
- package/skills/bitrefill/references/host-openclaw.md +167 -0
- package/skills/bitrefill/references/mcp.md +150 -0
- package/skills/bitrefill/references/safeguards.md +138 -0
- package/skills/bitrefill/references/troubleshooting.md +182 -0
- package/skills/kaleido-trading/SKILL.md +31 -0
- package/skills/kaleido-wallet/SKILL.md +28 -0
- package/src/capabilities.ts +67 -0
- package/src/context/budget.ts +46 -0
- package/src/context/builder.ts +100 -0
- package/src/context/context.test.ts +83 -0
- package/src/engine.test.ts +204 -0
- package/src/engine.ts +27 -2
- package/src/fastpath/fastpath.test.ts +34 -0
- package/src/fastpath/fastpath.ts +70 -0
- package/src/funnel.test.ts +207 -0
- package/src/funnel.ts +260 -0
- package/src/index.ts +102 -0
- package/src/knowledge/bitcoin-copilot.ts +177 -0
- package/src/knowledge/knowledge.test.ts +63 -0
- package/src/knowledge/merchants.ts +49 -0
- package/src/knowledge/wallet.ts +84 -0
- package/src/memory/memory.test.ts +85 -0
- package/src/memory/store.ts +129 -0
- package/src/memory/tool.ts +76 -0
- package/src/memory/types.ts +63 -0
- package/src/rag/rag.test.ts +85 -0
- package/src/rag/retriever.ts +94 -0
- package/src/rag/tool.ts +55 -0
- package/src/rag/types.ts +49 -0
- package/src/rag/vector-store.ts +78 -0
- package/src/recipe/asset-send.ts +79 -0
- package/src/recipe/payments.ts +116 -0
- package/src/recipe/receive.ts +98 -0
- package/src/recipe/recipe.test.ts +193 -0
- package/src/recipe/runner.ts +122 -0
- package/src/recipe/swap.ts +74 -0
- package/src/recipe/types.ts +76 -0
- package/src/skills/bundle.ts +42 -0
- package/src/skills/loader.ts +63 -0
- package/src/skills/reference-source.ts +60 -0
- package/src/skills/registry.ts +183 -0
- package/src/skills/skills.test.ts +191 -0
- package/src/skills/types.ts +55 -0
- package/src/tools/cli.test.ts +53 -0
- package/src/tools/cli.ts +98 -0
- package/src/tools/l402.test.ts +113 -0
- package/src/tools/l402.ts +122 -0
- package/src/tools/mcp.ts +3 -2
- package/src/wallet/contract.test.ts +89 -0
- package/src/wallet/contract.ts +157 -0
- package/dist/providers/qvac.d.ts +0 -89
- package/dist/providers/qvac.d.ts.map +0 -1
- package/dist/providers/qvac.js +0 -150
- package/dist/providers/qvac.js.map +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L402 tool tests — the "agent pays for a tool in sats" flow, deterministic
|
|
3
|
+
* with a stubbed fetch. No network, no model.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
7
|
+
import { createL402ToolSource, parseL402Challenge, bolt11AmountSats } from './l402.js';
|
|
8
|
+
|
|
9
|
+
describe('L402 helpers', () => {
|
|
10
|
+
it('parses an L402 challenge header', () => {
|
|
11
|
+
const h = 'L402 macaroon="AbCdEf==", invoice="lnbc100n1xyz"';
|
|
12
|
+
expect(parseL402Challenge(h)).toEqual({ macaroon: 'AbCdEf==', invoice: 'lnbc100n1xyz' });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('returns null when the header is not an L402 challenge', () => {
|
|
16
|
+
expect(parseL402Challenge('Bearer xyz')).toBeNull();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('parses bolt11 amounts to sats', () => {
|
|
20
|
+
expect(bolt11AmountSats('lnbc100n1xyz')).toBe(10); // 100 nano-BTC = 10 sats
|
|
21
|
+
expect(bolt11AmountSats('lnbc1u1xyz')).toBe(100); // 1 micro-BTC = 100 sats
|
|
22
|
+
expect(bolt11AmountSats('lnbc2500u1xyz')).toBe(250_000);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('createL402ToolSource — pay-and-fetch flow', () => {
|
|
27
|
+
it('pays the invoice on 402 then re-fetches with the L402 token', async () => {
|
|
28
|
+
const payInvoice = vi.fn(async () => ({ preimage: 'deadbeefpreimage' }));
|
|
29
|
+
|
|
30
|
+
// First call → 402 with a challenge; second (authed) call → 200 with data.
|
|
31
|
+
const fetchImpl = vi
|
|
32
|
+
.fn()
|
|
33
|
+
.mockResolvedValueOnce({
|
|
34
|
+
status: 402,
|
|
35
|
+
ok: false,
|
|
36
|
+
headers: new Map([
|
|
37
|
+
['www-authenticate', 'L402 macaroon="MAC123", invoice="lnbc100n1demo"'],
|
|
38
|
+
]) as any,
|
|
39
|
+
})
|
|
40
|
+
.mockResolvedValueOnce({
|
|
41
|
+
status: 200,
|
|
42
|
+
ok: true,
|
|
43
|
+
headers: new Map() as any,
|
|
44
|
+
async text() {
|
|
45
|
+
return JSON.stringify({ btc_usd: 73000 });
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const src = createL402ToolSource({ payInvoice, fetchImpl: fetchImpl as unknown as typeof fetch });
|
|
50
|
+
|
|
51
|
+
const result = await src.execute('fetch_paid_resource', { url: 'https://api.example/premium/price' });
|
|
52
|
+
|
|
53
|
+
// paid the right invoice + amount (100n = 10 sats)
|
|
54
|
+
expect(payInvoice).toHaveBeenCalledWith('lnbc100n1demo', 10);
|
|
55
|
+
// second fetch carried the L402 Authorization header
|
|
56
|
+
const secondCallArgs = fetchImpl.mock.calls[1];
|
|
57
|
+
expect(secondCallArgs[1].headers.Authorization).toBe('L402 MAC123:deadbeefpreimage');
|
|
58
|
+
// returned the parsed JSON resource
|
|
59
|
+
expect(result).toEqual({ btc_usd: 73000 });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('returns the resource directly when no payment is required (200)', async () => {
|
|
63
|
+
const payInvoice = vi.fn();
|
|
64
|
+
const fetchImpl = vi.fn().mockResolvedValueOnce({
|
|
65
|
+
status: 200,
|
|
66
|
+
ok: true,
|
|
67
|
+
headers: new Map() as any,
|
|
68
|
+
async text() {
|
|
69
|
+
return 'plain text resource';
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const src = createL402ToolSource({ payInvoice, fetchImpl: fetchImpl as unknown as typeof fetch });
|
|
73
|
+
|
|
74
|
+
const result = await src.execute('fetch_paid_resource', { url: 'https://api.example/free' });
|
|
75
|
+
|
|
76
|
+
expect(payInvoice).not.toHaveBeenCalled();
|
|
77
|
+
expect(result).toBe('plain text resource');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('exposes the tool with confirmation required (money tool)', () => {
|
|
81
|
+
const src = createL402ToolSource({ payInvoice: vi.fn() });
|
|
82
|
+
const tools = src.listTools();
|
|
83
|
+
expect(tools).toHaveLength(1);
|
|
84
|
+
expect(tools[0].name).toBe('fetch_paid_resource');
|
|
85
|
+
expect(tools[0].requiresConfirmation).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('honours requiresConfirmation: false (auto-pay hosts)', () => {
|
|
89
|
+
const src = createL402ToolSource({ payInvoice: vi.fn(), requiresConfirmation: false });
|
|
90
|
+
expect(src.listTools()[0].requiresConfirmation).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('declines invoices above maxAutoPaySats', async () => {
|
|
94
|
+
const payInvoice = vi.fn(async () => ({ preimage: 'x' }));
|
|
95
|
+
const fetchImpl = vi.fn().mockResolvedValueOnce({
|
|
96
|
+
status: 402,
|
|
97
|
+
ok: false,
|
|
98
|
+
headers: new Map([
|
|
99
|
+
// 2500u = 250,000 sats — above the 1000 cap
|
|
100
|
+
['www-authenticate', 'L402 macaroon="M", invoice="lnbc2500u1big"'],
|
|
101
|
+
]) as any,
|
|
102
|
+
});
|
|
103
|
+
const src = createL402ToolSource({
|
|
104
|
+
payInvoice,
|
|
105
|
+
maxAutoPaySats: 1000,
|
|
106
|
+
fetchImpl: fetchImpl as unknown as typeof fetch,
|
|
107
|
+
});
|
|
108
|
+
await expect(src.execute('fetch_paid_resource', { url: 'https://x/y' })).rejects.toThrow(
|
|
109
|
+
/above the 1000 sat auto-pay cap/,
|
|
110
|
+
);
|
|
111
|
+
expect(payInvoice).not.toHaveBeenCalled();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L402 tool source — lets the agent pay for paywalled HTTP resources in sats.
|
|
3
|
+
*
|
|
4
|
+
* Exposes one tool, `fetch_paid_resource(url)`, that runs the L402 flow:
|
|
5
|
+
* GET url → 402 with an L402 challenge (macaroon + Lightning invoice)
|
|
6
|
+
* → pay the invoice (via the injected `payInvoice`, e.g. the on-device wallet)
|
|
7
|
+
* → re-GET with `Authorization: L402 <macaroon>:<preimage>` → return the body.
|
|
8
|
+
*
|
|
9
|
+
* This is the "agent pays for a tool in sats" capability: any KaleidoMind agent
|
|
10
|
+
* (mobile or desktop) can buy premium data / inference autonomously. Payment
|
|
11
|
+
* runs through the host's wallet, so it stays on-device; the engine's
|
|
12
|
+
* confirmation gate can wrap the spend if desired.
|
|
13
|
+
*
|
|
14
|
+
* No dependencies — uses global fetch (Node ≥18, React Native). The fetch impl
|
|
15
|
+
* is injectable for testing.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { ToolDef } from '../types.js';
|
|
19
|
+
import type { ToolSource } from './source.js';
|
|
20
|
+
|
|
21
|
+
export interface L402PayResult {
|
|
22
|
+
/** Payment preimage (hex) — proves the invoice was paid. */
|
|
23
|
+
preimage: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface L402Options {
|
|
27
|
+
/** Pay a BOLT11 invoice, resolve with the preimage. Wallet on device; mock in tests. */
|
|
28
|
+
payInvoice: (invoice: string, amountSats: number) => Promise<L402PayResult>;
|
|
29
|
+
/** Override fetch (tests). Defaults to global fetch. */
|
|
30
|
+
fetchImpl?: typeof fetch;
|
|
31
|
+
/** Optional progress logging. */
|
|
32
|
+
log?: (msg: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Gate the tool behind the engine's confirmation (default true). The catch:
|
|
35
|
+
* the invoice amount is only known DURING execution (the 402 challenge), so a
|
|
36
|
+
* pre-execution confirmation can't show it. On hosts without a dedicated L402
|
|
37
|
+
* confirmation UX, set this false and bound spending with `maxAutoPaySats`.
|
|
38
|
+
*/
|
|
39
|
+
requiresConfirmation?: boolean;
|
|
40
|
+
/** Auto-pay only invoices up to this many sats; reject larger ones. */
|
|
41
|
+
maxAutoPaySats?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Parse an L402 (or legacy LSAT) WWW-Authenticate challenge. */
|
|
45
|
+
export function parseL402Challenge(header: string): { macaroon: string; invoice: string } | null {
|
|
46
|
+
const macaroon = header.match(/macaroon="([^"]+)"/i)?.[1];
|
|
47
|
+
const invoice = header.match(/invoice="([^"]+)"/i)?.[1];
|
|
48
|
+
return macaroon && invoice ? { macaroon, invoice } : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Rough BOLT11 amount → sats (for logging / spend caps). 0 if unparseable. */
|
|
52
|
+
export function bolt11AmountSats(invoice: string): number {
|
|
53
|
+
const m = invoice.match(/^ln(?:bc|tb|bcrt)(\d+)([munp]?)/i);
|
|
54
|
+
if (!m) return 0;
|
|
55
|
+
const n = Number(m[1]);
|
|
56
|
+
const mult = (m[2] || '').toLowerCase();
|
|
57
|
+
const btc = mult === 'm' ? n / 1e3 : mult === 'u' ? n / 1e6 : mult === 'n' ? n / 1e9 : mult === 'p' ? n / 1e12 : n;
|
|
58
|
+
return Math.round(btc * 1e8);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createL402ToolSource(opts: L402Options): ToolSource {
|
|
62
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
63
|
+
|
|
64
|
+
const tool: ToolDef = {
|
|
65
|
+
name: 'fetch_paid_resource',
|
|
66
|
+
description:
|
|
67
|
+
'Fetch a paywalled (L402) HTTP resource, automatically paying the required ' +
|
|
68
|
+
'Lightning invoice in sats. Use this for premium or paid APIs (market data, ' +
|
|
69
|
+
'inference, etc.). Pass the resource URL.',
|
|
70
|
+
parameters: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: { url: { type: 'string', description: 'The resource URL to fetch' } },
|
|
73
|
+
required: ['url'],
|
|
74
|
+
},
|
|
75
|
+
requiresConfirmation: opts.requiresConfirmation ?? true,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
async function execute(_name: string, args: Record<string, unknown>): Promise<unknown> {
|
|
79
|
+
const url = String(args.url ?? '');
|
|
80
|
+
if (!url) throw new Error('fetch_paid_resource: url is required');
|
|
81
|
+
|
|
82
|
+
let res = await doFetch(url);
|
|
83
|
+
|
|
84
|
+
if (res.status === 402) {
|
|
85
|
+
const challenge = parseL402Challenge(res.headers.get('www-authenticate') ?? '');
|
|
86
|
+
if (!challenge) throw new Error('402 Payment Required but no L402 challenge present');
|
|
87
|
+
|
|
88
|
+
const amountSats =
|
|
89
|
+
bolt11AmountSats(challenge.invoice) || Number(res.headers.get('x-amount-sats') ?? 0);
|
|
90
|
+
|
|
91
|
+
if (opts.maxAutoPaySats != null && amountSats > opts.maxAutoPaySats) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`L402 invoice is ${amountSats} sats, above the ${opts.maxAutoPaySats} sat auto-pay cap — declined`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
opts.log?.(`L402: ${url} requires ${amountSats} sats — paying…`);
|
|
98
|
+
|
|
99
|
+
const { preimage } = await opts.payInvoice(challenge.invoice, amountSats);
|
|
100
|
+
|
|
101
|
+
res = await doFetch(url, {
|
|
102
|
+
headers: { Authorization: `L402 ${challenge.macaroon}:${preimage}` },
|
|
103
|
+
});
|
|
104
|
+
opts.log?.(`L402: paid ${amountSats} sats → ${res.status}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!res.ok) throw new Error(`fetch_paid_resource: ${res.status} ${res.statusText}`);
|
|
108
|
+
const body = await res.text();
|
|
109
|
+
try {
|
|
110
|
+
return JSON.parse(body);
|
|
111
|
+
} catch {
|
|
112
|
+
return body;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
id: 'l402',
|
|
118
|
+
listTools: () => [tool],
|
|
119
|
+
has: (name) => name === tool.name,
|
|
120
|
+
execute,
|
|
121
|
+
};
|
|
122
|
+
}
|
package/src/tools/mcp.ts
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* file type-checks and ships even where the SDK isn't installed; constructing
|
|
14
14
|
* an McpToolSource without it throws a clear error.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Wired end-to-end: connect() (stdio + HTTP transports), listTools() and
|
|
17
|
+
* execute() are implemented. Used by the desktop sidecar (kaleido-mcp +
|
|
18
|
+
* Bitrefill MCP) and verified against the remote Bitrefill MCP.
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
21
|
import type { ToolDef } from '../types.js';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Wallet contract tests — integrity of the single source of truth. */
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
4
|
+
import {
|
|
5
|
+
WALLET_TOOLS,
|
|
6
|
+
SPEND_TOOLS,
|
|
7
|
+
isSpendTool,
|
|
8
|
+
walletTools,
|
|
9
|
+
toToolDefs,
|
|
10
|
+
bindWalletTools,
|
|
11
|
+
getWalletTool,
|
|
12
|
+
} from './contract.js';
|
|
13
|
+
|
|
14
|
+
describe('WALLET_TOOLS contract', () => {
|
|
15
|
+
it('has unique names and object schemas', () => {
|
|
16
|
+
const names = WALLET_TOOLS.map((t) => t.name);
|
|
17
|
+
expect(new Set(names).size).toBe(names.length);
|
|
18
|
+
for (const t of WALLET_TOOLS) {
|
|
19
|
+
expect(t.name).toMatch(/^[a-z][a-z0-9_]+$/);
|
|
20
|
+
expect((t.parameters as any).type).toBe('object');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('namespaces per-layer tools and keeps core helpers unprefixed', () => {
|
|
25
|
+
for (const t of WALLET_TOOLS) {
|
|
26
|
+
if (t.layer === 'core') continue;
|
|
27
|
+
expect(t.name.startsWith(`${t.layer}_`)).toBe(true);
|
|
28
|
+
}
|
|
29
|
+
expect(getWalletTool('send_payment')!.layer).toBe('core');
|
|
30
|
+
expect(getWalletTool('resolve_contact')!.layer).toBe('core');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('spend tools are confirmation-gated; reads do not move funds', () => {
|
|
34
|
+
for (const t of WALLET_TOOLS) {
|
|
35
|
+
expect(!!t.requiresConfirmation).toBe(!!t.spend);
|
|
36
|
+
}
|
|
37
|
+
// every fund-moving tool is flagged
|
|
38
|
+
expect(isSpendTool('send_payment')).toBe(true);
|
|
39
|
+
expect(isSpendTool('rln_send_asset')).toBe(true);
|
|
40
|
+
expect(isSpendTool('execute_swap')).toBe(true);
|
|
41
|
+
expect(isSpendTool('spark_send')).toBe(true);
|
|
42
|
+
// reads are not
|
|
43
|
+
expect(isSpendTool('get_balances')).toBe(false);
|
|
44
|
+
expect(isSpendTool('get_price')).toBe(false);
|
|
45
|
+
expect([...SPEND_TOOLS].length).toBeGreaterThanOrEqual(5);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('required args declared on the actionable tools', () => {
|
|
49
|
+
expect((getWalletTool('send_payment')!.parameters as any).required).toContain('to');
|
|
50
|
+
expect((getWalletTool('fiat_to_sats')!.parameters as any).required).toEqual(['amount', 'currency']);
|
|
51
|
+
expect((getWalletTool('rln_create_rgb_invoice')!.parameters as any).required).toEqual(['asset', 'amount']);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('selectors', () => {
|
|
56
|
+
it('walletTools filters by layer + always includes core unless disabled', () => {
|
|
57
|
+
const spark = walletTools({ layers: ['spark'] });
|
|
58
|
+
expect(spark.some((t) => t.name === 'spark_send')).toBe(true);
|
|
59
|
+
expect(spark.some((t) => t.layer === 'core')).toBe(true); // core included by default
|
|
60
|
+
expect(spark.some((t) => t.layer === 'rln')).toBe(false);
|
|
61
|
+
|
|
62
|
+
const noCore = walletTools({ layers: ['spark'], includeCore: false });
|
|
63
|
+
expect(noCore.every((t) => t.layer === 'spark')).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('toToolDefs strips metadata but keeps requiresConfirmation', () => {
|
|
67
|
+
const defs = toToolDefs(walletTools({ layers: ['spark'] }));
|
|
68
|
+
const send = defs.find((d) => d.name === 'spark_send')!;
|
|
69
|
+
expect(send.requiresConfirmation).toBe(true);
|
|
70
|
+
expect('layer' in (send as any)).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('bindWalletTools', () => {
|
|
75
|
+
it('binds handlers → an InProcessToolSource with spend flags preserved', async () => {
|
|
76
|
+
const handler = vi.fn(async () => ({ ok: true }));
|
|
77
|
+
const src = bindWalletTools({ spark_get_balance: handler, spark_send: handler }, { layers: ['spark'], includeCore: false, allowMissing: true });
|
|
78
|
+
const tools = src.listTools();
|
|
79
|
+
expect(tools.map((t) => t.name).sort()).toEqual(['spark_get_balance', 'spark_send']);
|
|
80
|
+
expect(tools.find((t) => t.name === 'spark_send')!.requiresConfirmation).toBe(true);
|
|
81
|
+
expect(await src.execute('spark_get_balance', {})).toEqual({ ok: true });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('throws on a missing handler unless allowMissing', () => {
|
|
85
|
+
expect(() => bindWalletTools({}, { layers: ['spark'], includeCore: false })).toThrow(/no handler/);
|
|
86
|
+
const src = bindWalletTools({ spark_get_balance: async () => 1 }, { layers: ['spark'], includeCore: false, allowMissing: true });
|
|
87
|
+
expect(src.listTools().map((t) => t.name)).toEqual(['spark_get_balance']);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical multi-L2 wallet tool contract — the single source of truth for
|
|
3
|
+
* KaleidoMind's wallet tools (names + JSON schemas + spend flags).
|
|
4
|
+
*
|
|
5
|
+
* Every surface implements THESE EXACT tools, only the transport differs:
|
|
6
|
+
* - mobile → in-process handlers over the WDK adapters (`bindWalletTools`)
|
|
7
|
+
* - desktop → kaleido-mcp (tools namespaced per layer) + a `kaleido` CLI
|
|
8
|
+
* - eval → stub handlers
|
|
9
|
+
*
|
|
10
|
+
* Because the schemas are identical everywhere, skills are portable and the
|
|
11
|
+
* model comparison is honest. Tools are namespaced per layer (`spark_*`,
|
|
12
|
+
* `rln_*`, `arkade_*`, `liquid_*`); cross-cutting router/helpers are unprefixed.
|
|
13
|
+
*
|
|
14
|
+
* Spend tools (move funds) carry `spend: true` → `requiresConfirmation: true`,
|
|
15
|
+
* so the Engine always pauses for the host's confirm gate before executing.
|
|
16
|
+
*
|
|
17
|
+
* Pure data — no deps, RN-safe.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { ToolDef } from '../types.js';
|
|
21
|
+
import { InProcessToolSource } from '../tools/in-process.js';
|
|
22
|
+
import type { InProcessTool } from '../tools/in-process.js';
|
|
23
|
+
|
|
24
|
+
export type WalletLayer = 'spark' | 'rln' | 'arkade' | 'liquid' | 'core';
|
|
25
|
+
|
|
26
|
+
export interface WalletToolDef extends ToolDef {
|
|
27
|
+
/** Which L2 (or 'core' for cross-cutting router/helpers). */
|
|
28
|
+
layer: WalletLayer;
|
|
29
|
+
/** Moves funds → confirmation-gated. */
|
|
30
|
+
spend?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type Props = Record<string, { type: string; description?: string; enum?: string[] }>;
|
|
34
|
+
|
|
35
|
+
function t(
|
|
36
|
+
layer: WalletLayer,
|
|
37
|
+
name: string,
|
|
38
|
+
description: string,
|
|
39
|
+
properties: Props = {},
|
|
40
|
+
required: string[] = [],
|
|
41
|
+
spend = false,
|
|
42
|
+
): WalletToolDef {
|
|
43
|
+
return {
|
|
44
|
+
layer,
|
|
45
|
+
name,
|
|
46
|
+
description,
|
|
47
|
+
spend,
|
|
48
|
+
requiresConfirmation: spend,
|
|
49
|
+
parameters: { type: 'object', properties, required },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const sats = { type: 'number', description: 'Amount in satoshis' } as const;
|
|
54
|
+
const asset = { type: 'string', description: "Asset ticker, e.g. 'USDT', 'XAUT', 'BTC'" } as const;
|
|
55
|
+
|
|
56
|
+
/** The full contract. Keep descriptions terse — small models read every word. */
|
|
57
|
+
export const WALLET_TOOLS: WalletToolDef[] = [
|
|
58
|
+
// ── Spark ──────────────────────────────────────────────────────────────
|
|
59
|
+
t('spark', 'spark_get_balance', 'Get the Spark wallet BTC balance.'),
|
|
60
|
+
t('spark', 'spark_get_address', 'Get a Spark deposit address to receive BTC.'),
|
|
61
|
+
t('spark', 'spark_create_invoice', 'Create a Spark Lightning invoice to receive BTC.', { amount_sats: sats }),
|
|
62
|
+
t('spark', 'spark_send', 'Send BTC from Spark to an address or invoice.', { amount_sats: sats, to: { type: 'string', description: 'Address or invoice' } }, ['amount_sats', 'to'], true),
|
|
63
|
+
|
|
64
|
+
// ── RLN / RGB ──────────────────────────────────────────────────────────
|
|
65
|
+
t('rln', 'rln_get_balances', 'Get RLN node balances (BTC + RGB assets).'),
|
|
66
|
+
t('rln', 'rln_get_node_info', 'Get RLN node status and sync state.'),
|
|
67
|
+
t('rln', 'rln_list_channels', 'List the RLN node Lightning channels.'),
|
|
68
|
+
t('rln', 'rln_create_ln_invoice', 'Create a Lightning (BTC) invoice on the RLN node.', { amount_sats: sats }),
|
|
69
|
+
t('rln', 'rln_create_rgb_invoice', 'Create an RGB asset invoice to receive an asset (e.g. USDT).', { asset, amount: { type: 'number', description: 'Asset amount' } }, ['asset', 'amount']),
|
|
70
|
+
t('rln', 'rln_pay_invoice', 'Pay a Lightning invoice from the RLN node.', { invoice: { type: 'string' } }, ['invoice'], true),
|
|
71
|
+
t('rln', 'rln_send_asset', 'Send an RGB asset (e.g. USDT) to a recipient.', { asset, amount: { type: 'number' }, to: { type: 'string' } }, ['asset', 'amount', 'to'], true),
|
|
72
|
+
|
|
73
|
+
// ── Arkade ─────────────────────────────────────────────────────────────
|
|
74
|
+
t('arkade', 'arkade_get_balance', 'Get the Arkade wallet balance.'),
|
|
75
|
+
t('arkade', 'arkade_get_address', 'Get an Arkade address to receive funds.'),
|
|
76
|
+
t('arkade', 'arkade_send', 'Send BTC from Arkade to a recipient.', { amount_sats: sats, to: { type: 'string' } }, ['amount_sats', 'to'], true),
|
|
77
|
+
|
|
78
|
+
// ── Liquid (later) ─────────────────────────────────────────────────────
|
|
79
|
+
t('liquid', 'liquid_get_balance', 'Get the Liquid wallet balance (L-BTC + assets).'),
|
|
80
|
+
t('liquid', 'liquid_create_invoice', 'Create a Liquid invoice/address to receive (L-BTC or L-USDt).', { asset, amount: { type: 'number' } }),
|
|
81
|
+
t('liquid', 'liquid_send', 'Send a Liquid asset (L-BTC or L-USDt) to a recipient.', { asset, amount: { type: 'number' }, to: { type: 'string' } }, ['asset', 'amount', 'to'], true),
|
|
82
|
+
|
|
83
|
+
// ── Core: router + helpers ─────────────────────────────────────────────
|
|
84
|
+
t('core', 'get_balances', 'Get balances across all layers (or one layer).', { layer: { type: 'string', enum: ['spark', 'rln', 'arkade', 'liquid'], description: 'Optional: a single layer' } }),
|
|
85
|
+
t('core', 'resolve_contact', 'Resolve a contact name to a Lightning address / Nostr / preferred rail.', { name: { type: 'string', description: 'Contact name, e.g. "bob"' } }, ['name']),
|
|
86
|
+
t('core', 'get_price', 'Get the current price of an asset, optionally in a fiat currency.', { asset, fiat: { type: 'string', description: "Fiat code, e.g. 'EUR', 'USD'" } }),
|
|
87
|
+
t('core', 'fiat_to_sats', 'Convert a fiat amount to satoshis at the current rate.', { amount: { type: 'number' }, currency: { type: 'string', description: "Fiat code, e.g. 'EUR'" } }, ['amount', 'currency']),
|
|
88
|
+
t('core', 'get_swap_quote', 'Quote a swap between two assets.', { from_asset: asset, to_asset: asset, amount: { type: 'number' } }, ['from_asset', 'to_asset', 'amount']),
|
|
89
|
+
t('core', 'execute_swap', 'Execute a previously quoted swap.', { quote_id: { type: 'string' }, from_asset: asset, to_asset: asset, amount: { type: 'number' } }, [], true),
|
|
90
|
+
// The high-level entry a skill prefers — picks the rail for the asset, or uses `layer`.
|
|
91
|
+
t('core', 'send_payment', 'Send a payment, automatically choosing the best layer for the asset (or use `layer`).', { asset, amount_sats: sats, to: { type: 'string', description: 'Contact, address, or invoice' }, layer: { type: 'string', enum: ['spark', 'rln', 'arkade', 'liquid'] } }, ['to'], true),
|
|
92
|
+
// High-level receive — picks the right invoice/address tool for the asset/layer.
|
|
93
|
+
t('core', 'create_invoice', 'Create an invoice or address to receive funds, choosing the rail for the asset (or use `layer`). Omit amount for an any-amount invoice.', { asset, amount: { type: 'number', description: 'Amount (sats for BTC, asset units otherwise) — optional' }, layer: { type: 'string', enum: ['spark', 'rln', 'arkade', 'liquid'] } }),
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
// ── Selectors ───────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
export const WALLET_LAYERS: WalletLayer[] = ['spark', 'rln', 'arkade', 'liquid', 'core'];
|
|
99
|
+
|
|
100
|
+
/** Names of all spend (fund-moving) tools — these are confirmation-gated. */
|
|
101
|
+
export const SPEND_TOOLS: ReadonlySet<string> = new Set(WALLET_TOOLS.filter((x) => x.spend).map((x) => x.name));
|
|
102
|
+
|
|
103
|
+
export function isSpendTool(name: string): boolean {
|
|
104
|
+
return SPEND_TOOLS.has(name);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function getWalletTool(name: string): WalletToolDef | undefined {
|
|
108
|
+
return WALLET_TOOLS.find((x) => x.name === name);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Pick the contract tools for the given layers (core helpers included by default). */
|
|
112
|
+
export function walletTools(opts: { layers?: WalletLayer[]; includeCore?: boolean } = {}): WalletToolDef[] {
|
|
113
|
+
const layers = new Set(opts.layers ?? (['spark', 'rln', 'arkade', 'liquid'] as WalletLayer[]));
|
|
114
|
+
if (opts.includeCore !== false) layers.add('core');
|
|
115
|
+
return WALLET_TOOLS.filter((x) => layers.has(x.layer));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Strip to plain ToolDefs (drop the layer/spend metadata). */
|
|
119
|
+
export function toToolDefs(tools: WalletToolDef[]): ToolDef[] {
|
|
120
|
+
return tools.map(({ name, description, parameters, requiresConfirmation }) => ({ name, description, parameters, requiresConfirmation }));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** A handler bound to one contract tool. */
|
|
124
|
+
export type WalletHandler = (args: Record<string, unknown>) => Promise<unknown>;
|
|
125
|
+
|
|
126
|
+
export interface BindWalletOptions {
|
|
127
|
+
layers?: WalletLayer[];
|
|
128
|
+
includeCore?: boolean;
|
|
129
|
+
/** Skip tools that have no handler instead of throwing (default false). */
|
|
130
|
+
allowMissing?: boolean;
|
|
131
|
+
id?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Bind contract tools to in-process handlers → an InProcessToolSource. The
|
|
136
|
+
* mobile (and eval) binding: pass a map of `{ toolName: handler }` and you get a
|
|
137
|
+
* ToolSource implementing the canonical schemas with spend flags preserved.
|
|
138
|
+
*/
|
|
139
|
+
export function bindWalletTools(handlers: Record<string, WalletHandler>, opts: BindWalletOptions = {}): InProcessToolSource {
|
|
140
|
+
const tools = walletTools(opts);
|
|
141
|
+
const bound: InProcessTool[] = [];
|
|
142
|
+
for (const def of tools) {
|
|
143
|
+
const handler = handlers[def.name];
|
|
144
|
+
if (!handler) {
|
|
145
|
+
if (opts.allowMissing) continue;
|
|
146
|
+
throw new Error(`bindWalletTools: no handler for "${def.name}"`);
|
|
147
|
+
}
|
|
148
|
+
bound.push({
|
|
149
|
+
name: def.name,
|
|
150
|
+
description: def.description,
|
|
151
|
+
parameters: def.parameters,
|
|
152
|
+
requiresConfirmation: def.requiresConfirmation,
|
|
153
|
+
handler,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return new InProcessToolSource(opts.id ?? 'wallet', bound);
|
|
157
|
+
}
|
package/dist/providers/qvac.d.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QVAC provider — primary, on-device.
|
|
3
|
-
*
|
|
4
|
-
* Wraps the QVAC SDK's llamacpp-completion plugin. The actual binding
|
|
5
|
-
* differs slightly per host (RN vs Node vs browser worker), so the heavy
|
|
6
|
-
* lifting is delegated to a `QvacBinding` injected by the adapter packages.
|
|
7
|
-
*
|
|
8
|
-
* Node (kaleido-agent): binding = await import('@qvac/sdk/node')
|
|
9
|
-
* React Native (rate): binding from @kaleido/mind-rn → wraps QVACService
|
|
10
|
-
* Browser (rate-extension): binding from @kaleido/mind-browser → talks to worker
|
|
11
|
-
*/
|
|
12
|
-
import type { LLMProvider, ProviderConfig, ProviderRunOptions, ProviderRunResult } from './types.js';
|
|
13
|
-
import type { Message, ToolDef, TurnEvent } from '../types.js';
|
|
14
|
-
/**
|
|
15
|
-
* Host-injected binding. Each adapter package supplies its own.
|
|
16
|
-
* We keep this duck-typed (not an abstract class) so adapters
|
|
17
|
-
* don't have to extend anything heavy.
|
|
18
|
-
*/
|
|
19
|
-
export interface QvacBinding {
|
|
20
|
-
load(modelPath: string, opts: {
|
|
21
|
-
contextSize: number;
|
|
22
|
-
backend?: 'metal' | 'cpu' | 'auto';
|
|
23
|
-
}): Promise<void>;
|
|
24
|
-
unload(): Promise<void>;
|
|
25
|
-
isReady(): boolean;
|
|
26
|
-
/** Synchronous completion. The adapter handles the QVAC SDK plumbing. */
|
|
27
|
-
complete(prompt: string, opts: {
|
|
28
|
-
maxTokens?: number;
|
|
29
|
-
temperature?: number;
|
|
30
|
-
topP?: number;
|
|
31
|
-
stop?: string[];
|
|
32
|
-
signal?: AbortSignal;
|
|
33
|
-
}): Promise<{
|
|
34
|
-
text: string;
|
|
35
|
-
usage?: {
|
|
36
|
-
prompt: number;
|
|
37
|
-
completion: number;
|
|
38
|
-
};
|
|
39
|
-
}>;
|
|
40
|
-
/** Streaming completion — yields token strings. */
|
|
41
|
-
stream(prompt: string, opts: {
|
|
42
|
-
maxTokens?: number;
|
|
43
|
-
temperature?: number;
|
|
44
|
-
topP?: number;
|
|
45
|
-
stop?: string[];
|
|
46
|
-
signal?: AbortSignal;
|
|
47
|
-
}): AsyncIterable<string>;
|
|
48
|
-
}
|
|
49
|
-
export interface QvacProviderOpts extends ProviderConfig {
|
|
50
|
-
binding: QvacBinding;
|
|
51
|
-
/**
|
|
52
|
-
* Chat template family. Auto-detect from model name when omitted.
|
|
53
|
-
* 'qwen3' — Qwen 3 family (thinking-mode aware)
|
|
54
|
-
* 'hermes' — Hermes 3/4 family
|
|
55
|
-
* 'llama3' — Llama 3.x Instruct
|
|
56
|
-
* 'chatml' — generic ChatML
|
|
57
|
-
*/
|
|
58
|
-
template?: 'qwen3' | 'hermes' | 'llama3' | 'chatml';
|
|
59
|
-
backend?: 'metal' | 'cpu' | 'auto';
|
|
60
|
-
}
|
|
61
|
-
export declare class QvacProvider implements LLMProvider {
|
|
62
|
-
readonly name = "qvac";
|
|
63
|
-
readonly modelId: string;
|
|
64
|
-
private readonly binding;
|
|
65
|
-
private readonly contextSize;
|
|
66
|
-
private readonly temperature;
|
|
67
|
-
private readonly topP;
|
|
68
|
-
private readonly template;
|
|
69
|
-
private readonly backend;
|
|
70
|
-
constructor(opts: QvacProviderOpts);
|
|
71
|
-
load(): Promise<void>;
|
|
72
|
-
unload(): Promise<void>;
|
|
73
|
-
isReady(): boolean;
|
|
74
|
-
run(messages: Message[], tools: ToolDef[], opts?: ProviderRunOptions): Promise<ProviderRunResult>;
|
|
75
|
-
stream(messages: Message[], tools: ToolDef[], opts?: ProviderRunOptions): AsyncIterable<TurnEvent>;
|
|
76
|
-
private detectTemplate;
|
|
77
|
-
/**
|
|
78
|
-
* Render messages + tools into the model's chat template.
|
|
79
|
-
* TODO: full implementations per family. This stub uses a minimal
|
|
80
|
-
* tool-aware ChatML rendering that works for Qwen3 / Hermes / Llama 3.
|
|
81
|
-
*/
|
|
82
|
-
private formatPrompt;
|
|
83
|
-
/**
|
|
84
|
-
* Parse the model's text output into (text, tool_calls).
|
|
85
|
-
* Recognises <tool_call>{...}</tool_call> blocks.
|
|
86
|
-
*/
|
|
87
|
-
private parseCompletion;
|
|
88
|
-
}
|
|
89
|
-
//# sourceMappingURL=qvac.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"qvac.d.ts","sourceRoot":"","sources":["../../src/providers/qvac.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,OAAO,EAAY,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1G,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,OAAO,CAAC;IACnB,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE;QAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAC9E,mDAAmD;IACnD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpD,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;CACpC;AAED,qBAAa,YAAa,YAAW,WAAW;IAC9C,SAAgB,IAAI,UAAU;IAC9B,SAAgB,OAAO,EAAE,MAAM,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4C;IACrE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;gBAEvD,IAAI,EAAE,gBAAgB;IAU5B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,OAAO,IAAI,OAAO;IAIZ,GAAG,CACP,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,OAAO,EAAE,EAChB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC;IAYtB,MAAM,CACX,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,OAAO,EAAE,EAChB,IAAI,GAAE,kBAAuB,GAC5B,aAAa,CAAC,SAAS,CAAC;IAoB3B,OAAO,CAAC,cAAc;IAQtB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IA+BpB;;;OAGG;IACH,OAAO,CAAC,eAAe;CAwBxB"}
|