diff --git a/packages/builder/scripts/regen-tool-schemas.mjs b/packages/builder/scripts/regen-tool-schemas.mjs new file mode 100644 index 0000000..09582cb --- /dev/null +++ b/packages/builder/scripts/regen-tool-schemas.mjs @@ -0,0 +1,64 @@ +// packages/builder/scripts/regen-tool-schemas.mjs +// Boots the BUILT server, lists tools over SSE, rewrites the smoke snapshot +// sorted by name. Run from the repo root: npm run build && node packages/builder/scripts/regen-tool-schemas.mjs +// +// Deviation from the original brief version: the brief used a flat 2000ms +// sleep before connecting. On this machine that raced the server's actual +// startup (cold module load of lucid-evolution/libsodium takes longer), +// producing ECONNREFUSED. Swapped for the same "wait for the 'listening on +// port' stderr line" readiness signal that test/setup.ts's startServer() +// already uses elsewhere in this repo, with spawn error/exit handling so a +// real startup crash fails loudly instead of hanging. +// +// PR 6 (Task 5) change: each snapshot entry is now { description, inputSchema } +// instead of a bare inputSchema, so the fixture also guards the +// security-teaching copy in tool descriptions (the sign/submit/confirm flow +// callers learn from them), not just the JSON Schema shape. See the docblock +// above the schema-snapshot describe() in +// packages/builder/test/smoke/tool-inventory.test.ts for the full rationale. +import { spawn } from 'node:child_process'; +import { writeFileSync } from 'node:fs'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; + +const PORT = 3939; +const server = spawn('node', ['packages/builder/build/index.js'], { + env: { ...process.env, PORT: String(PORT) }, + stdio: ['ignore', 'ignore', 'pipe'], +}); + +let stderrBuf = ''; +server.stderr.on('data', (chunk) => { stderrBuf += chunk.toString(); }); + +try { + await new Promise((resolvePromise, reject) => { + const timeout = setTimeout(() => reject(new Error(`Server start timeout (20s). stderr so far:\n${stderrBuf}`)), 20_000); + const onData = () => { + if (stderrBuf.includes('listening on port')) { + clearTimeout(timeout); + server.stderr.off('data', onData); + resolvePromise(); + } + }; + server.stderr.on('data', onData); + server.on('error', (err) => { clearTimeout(timeout); reject(err); }); + server.on('exit', (code) => { clearTimeout(timeout); reject(new Error(`Server exited with code ${code}. stderr so far:\n${stderrBuf}`)); }); + }); + + const client = new Client({ name: 'regen', version: '0.0.0' }); + await client.connect(new SSEClientTransport(new URL(`http://localhost:${PORT}/sse`))); + const { tools } = await client.listTools(); + const snapshot = Object.fromEntries( + tools + .map((t) => [t.name, { description: t.description, inputSchema: t.inputSchema }]) + .sort(([a], [b]) => a.localeCompare(b)), + ); + writeFileSync( + 'packages/builder/test/smoke/tool-schemas.snapshot.json', + JSON.stringify(snapshot, null, 2) + '\n', + ); + console.log(`wrote ${tools.length} tool schemas`); + await client.close(); +} finally { + server.kill(); +} diff --git a/packages/builder/src/vector/index.ts b/packages/builder/src/vector/index.ts index 6e4f96f..a7238c0 100644 --- a/packages/builder/src/vector/index.ts +++ b/packages/builder/src/vector/index.ts @@ -1,14 +1,7 @@ export { registerVectorTools } from './vector.js'; export type { - VectorWalletInfo, - VectorToken, - VectorAdaTransactionResult, - VectorTokenTransactionResult, TxOutput, - VectorBuildTransactionResult, VectorDryRunResult, VectorTransactionSummary, VectorTransactionHistoryResult, - VectorDeployContractResult, - VectorInteractContractResult, } from '@apexfusion/vector-mcp-shared/types'; diff --git a/packages/builder/src/vector/vector.ts b/packages/builder/src/vector/vector.ts index 64f54da..86a8adf 100644 --- a/packages/builder/src/vector/vector.ts +++ b/packages/builder/src/vector/vector.ts @@ -1,6 +1,5 @@ import { z } from "zod/v4"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { Lucid } from '@lucid-evolution/lucid'; import * as dotenv from 'dotenv'; import { resolve } from 'path'; @@ -11,14 +10,11 @@ import { lovelaceToAda, formatAssetName } from '@apexfusion/vector-mcp-shared/tx import { VECTOR_OGMIOS_URL, VECTOR_SUBMIT_URL, VECTOR_KOIOS_URL, VECTOR_EXPLORER_URL, explorerTxLink, } from '@apexfusion/vector-mcp-shared/config'; -import { safetyLayer } from './safety.js'; import { limiterFor } from './rate-limiter.js'; import { pollUntilConfirmed, buildConfirmationCheck } from './poll.js'; import { registerAgentNetworkTools } from './agent-network.js'; import { registerSelfImprovementTools } from './self-improvement.js'; -import type { - VectorToken, VectorWalletInfo, VectorDryRunResult, -} from '@apexfusion/vector-mcp-shared/types'; +import type { VectorDryRunResult } from '@apexfusion/vector-mcp-shared/types'; import { lucidForAddress, buildSendApex, buildSendTokens, buildMultiOutput, buildDeployContract, buildInteractContract, @@ -38,96 +34,6 @@ if (existsSync(envPath)) { } } -// Initialize Lucid instance with Ogmios provider -async function initLucid(mnemonic: string, accountIndex: number = 0) { - const provider = new OgmiosProvider({ - ogmiosUrl: VECTOR_OGMIOS_URL, - submitUrl: VECTOR_SUBMIT_URL, - koiosUrl: VECTOR_KOIOS_URL, - }); - - // Vector uses --mainnet flag, so addresses are addr1... format - const lucid = await Lucid(provider, 'Mainnet'); - - if (!mnemonic) { - throw new Error('mnemonic is required'); - } - - const trimmedMnemonic = mnemonic.trim(); - const words = trimmedMnemonic.split(/\s+/); - - const validLengths = [12, 15, 18, 21, 24]; - if (!validLengths.includes(words.length)) { - throw new Error(`Invalid mnemonic: Expected 12, 15, 18, 21 or 24 words, got ${words.length}`); - } - - lucid.selectWallet.fromSeed(trimmedMnemonic, { accountIndex }); - - const address = await lucid.wallet().address(); - if (!address) { - throw new Error('Failed to derive address from mnemonic'); - } - - return lucid; -} - -// Get wallet info -export async function getWalletInfo(mnemonic: string, accountIndex: number = 0): Promise { - console.error('[getWalletInfo] Initializing Lucid...'); - const lucid = await initLucid(mnemonic, accountIndex); - console.error('[getWalletInfo] Getting address...'); - const address = await lucid.wallet().address(); - console.error('[getWalletInfo] Querying UTxOs...'); - const utxos = await lucid.utxosAt(address); - console.error(`[getWalletInfo] Found ${utxos.length} UTxOs`); - - let adaBalance = '0'; - let tokenBalances: VectorToken[] = []; - - if (utxos.length > 0) { - // Aggregate all UTxO assets - const aggregated: Record = {}; - for (const utxo of utxos) { - for (const [unit, qty] of Object.entries(utxo.assets)) { - aggregated[unit] = (aggregated[unit] || 0n) + BigInt(qty); - } - } - - adaBalance = aggregated['lovelace'] ? lovelaceToAda(aggregated['lovelace']) : '0'; - - for (const [unit, quantity] of Object.entries(aggregated)) { - if (unit === 'lovelace') continue; - - try { - const policyId = unit.slice(0, 56); - const assetNameHex = unit.slice(56); - const displayName = assetNameHex - ? formatAssetName(assetNameHex) - : `${policyId.substring(0, 8)}...`; - - tokenBalances.push({ - unit, - name: displayName, - quantity: quantity.toString(), - }); - } catch { - tokenBalances.push({ - unit, - name: unit, - quantity: quantity.toString(), - }); - } - } - } - - return { - address, - utxoCount: utxos.length, - ada: adaBalance, - tokens: tokenBalances, - }; -} - // Shared provider for the keyless build_* tools (Task 5 reuses this). function makeProvider(): OgmiosProvider { return new OgmiosProvider({ @@ -214,91 +120,26 @@ ${tokens.length > 0 ? `Token Holdings (${tokens.length}):\n${tokenList}` : 'No t } ); - // vector_get_address - Get the agent's wallet address and balance - server.tool( - "vector_get_address", - "Get the Vector wallet address, balance, and token holdings derived from a mnemonic", - { - mnemonic: z.string().describe("15 or 24-word BIP39 mnemonic for the wallet"), - }, - async ({ mnemonic }) => { - const rateCheck = rateLimiter.check(); - if (!rateCheck.allowed) { - return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] }; - } - try { - const walletInfo = await getWalletInfo(mnemonic); - - const tokenList = walletInfo.tokens.length > 0 - ? walletInfo.tokens.map((t: VectorToken) => `${t.quantity} ${t.name}`).join('\n') - : 'No tokens found'; - - return { - content: [{ - type: "text", - text: `# Vector Wallet Information - -Address: ${walletInfo.address} -AP3X Balance: ${walletInfo.ada} AP3X -UTXO Count: ${walletInfo.utxoCount} - -${walletInfo.tokens.length > 0 ? `## Token Holdings (${walletInfo.tokens.length}):\n${tokenList}` : 'No token holdings found'}`, - }], - }; - } catch (err) { - const error = err as Error; - return { - content: [{ - type: "text", - text: `Failed to get wallet information: ${error.message} - -**Troubleshooting Tips:** -1. Make sure you have a valid 15 or 24-word BIP39 mnemonic -2. Verify the Ogmios endpoint is reachable: ${VECTOR_OGMIOS_URL} -3. Check the console logs for detailed error information`, - }], - }; - } - } - ); - - // vector_get_utxos - List UTxOs for an address or the wallet + // vector_get_utxos - List UTxOs for an address server.tool( "vector_get_utxos", - "List unspent transaction outputs (UTxOs) for a Vector address or a wallet derived from a mnemonic", + "List unspent transaction outputs (UTxOs) for a Vector address", { - address: z.string().optional().describe("Vector address to query UTxOs for. If omitted, mnemonic is required."), - mnemonic: z.string().optional().describe("15 or 24-word BIP39 mnemonic (required if address is omitted)"), + address: z.string().describe("Vector address to query UTxOs for (addr1...)"), }, - async ({ address, mnemonic }) => { + async ({ address }) => { const rateCheck = rateLimiter.check(); if (!rateCheck.allowed) { return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] }; } try { - let utxos; - let queryAddress: string; - - if (address) { - const provider = new OgmiosProvider({ - ogmiosUrl: VECTOR_OGMIOS_URL, - submitUrl: VECTOR_SUBMIT_URL, - koiosUrl: VECTOR_KOIOS_URL, - }); - utxos = await provider.getUtxos(address); - queryAddress = address; - } else { - if (!mnemonic) throw new Error('Provide either address or mnemonic'); - const lucid = await initLucid(mnemonic); - queryAddress = await lucid.wallet().address(); - utxos = await lucid.utxosAt(queryAddress); - } + const utxos = await makeProvider().getUtxos(address); if (utxos.length === 0) { return { content: [{ type: "text", - text: `No UTxOs found for ${queryAddress}`, + text: `No UTxOs found for ${address}`, }], }; } @@ -312,7 +153,7 @@ ${walletInfo.tokens.length > 0 ? `## Token Holdings (${walletInfo.tokens.length} return { content: [{ type: "text", - text: `# UTxOs for ${queryAddress} + text: `# UTxOs for ${address} Total: ${utxos.length} UTxO(s) @@ -441,50 +282,6 @@ Non-custodial flow: 1) sign locally with vector_signer_sign { txCbor }; 2) broad } ); - // vector_get_spend_limits - Check safety layer status - server.tool( - "vector_get_spend_limits", - "Check current spend limits and remaining daily budget", - {}, - async () => { - const rateCheck = rateLimiter.check(); - if (!rateCheck.allowed) { - return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] }; - } - try { - const status = safetyLayer.getSpendStatus(); - const log = safetyLayer.getAuditLog(); - - const recentTxs = log.slice(-5).reverse().map((entry) => - `- ${entry.timestamp}: ${lovelaceToAda(entry.amountLovelace)} AP3X → ${entry.recipient.substring(0, 20)}... (${entry.txHash.substring(0, 16)}...)` - ).join('\n'); - - return { - content: [{ - type: "text", - text: `# Vector Spend Limits - -Per-Transaction Limit: ${lovelaceToAda(status.perTransactionLimit)} AP3X -Daily Limit: ${lovelaceToAda(status.dailyLimit)} AP3X -Daily Spent: ${lovelaceToAda(status.dailySpent)} AP3X -Daily Remaining: ${lovelaceToAda(status.dailyRemaining)} AP3X -Resets At: ${status.resetTime} - -${log.length > 0 ? `## Recent Transactions (last ${Math.min(5, log.length)}):\n${recentTxs}` : 'No transactions recorded yet.'}`, - }], - }; - } catch (err) { - const error = err as Error; - return { - content: [{ - type: "text", - text: `Failed to get spend limits: ${error.message}`, - }], - }; - } - } - ); - // vector_build_transaction - build an UNSIGNED multi-output transaction (keyless) server.tool( "vector_build_transaction", @@ -522,7 +319,7 @@ ${log.length > 0 ? `## Recent Transactions (last ${Math.min(5, log.length)}):\n$ ${result.txCbor} \`\`\` -Use vector_dry_run with this CBOR to simulate it, sign it locally with vector_signer_sign, then broadcast with vector_submit_transaction.`, +Use vector_dry_run with this CBOR to simulate it, sign it locally with vector_signer_sign, broadcast with vector_submit_transaction, then confirm with vector_await_transaction { txHash }.`, }], }; } catch (err) { @@ -544,18 +341,18 @@ Use vector_dry_run with this CBOR to simulate it, sign it locally with vector_si // vector_dry_run - Simulate a transaction without submitting server.tool( "vector_dry_run", - "Simulate a transaction without submitting - returns fee estimate and validation result", + "Simulate a transaction without submitting - returns fee estimate and validation result. Accepts either built CBOR or outputs to build from (keyless).", { txCbor: z.string().optional().describe("Hex-encoded CBOR of a built transaction to evaluate"), outputs: z.array(z.object({ address: z.string().describe("Recipient Vector address"), lovelace: z.number().describe("Amount in lovelace"), assets: z.record(z.string(), z.string()).optional(), - })).optional().describe("If no txCbor provided, build a TX from these outputs and evaluate it"), - mnemonic: z.string().optional().describe("15 or 24-word BIP39 mnemonic (required when outputs is provided)"), + })).optional().describe("If no txCbor provided, build an unsigned TX from these outputs and evaluate it"), + changeAddress: z.string().optional().describe("Your wallet address (required when building from outputs)"), metadata: z.string().optional().describe("Optional JSON metadata when building from outputs"), }, - async ({ txCbor, outputs, mnemonic, metadata }) => { + async ({ txCbor, outputs, changeAddress, metadata }) => { const rateCheck = rateLimiter.check(); if (!rateCheck.allowed) { return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] }; @@ -565,42 +362,20 @@ Use vector_dry_run with this CBOR to simulate it, sign it locally with vector_si let feeFromBuild: string | null = null; if (!cborToEvaluate && outputs && outputs.length > 0) { - // Build the transaction first - if (!mnemonic) throw new Error('mnemonic is required when building from outputs'); - const lucid = await initLucid(mnemonic); - - let tx = lucid.newTx(); - for (const output of outputs) { - const assets: Record = { lovelace: BigInt(output.lovelace) }; - if (output.assets) { - for (const [unit, qty] of Object.entries(output.assets)) { - assets[unit] = BigInt(qty); - } - } - tx = tx.pay.ToAddress(output.address, assets); - } - - if (metadata) { - const parsedMetadata = typeof metadata === 'string' ? JSON.parse(metadata) : metadata; - tx = tx.attachMetadata(674, parsedMetadata); - } - - const completedTx = await tx.complete(); - const txJson = (completedTx as any).toJSON?.() ?? {}; - feeFromBuild = String(txJson?.body?.fee ?? '0'); - const signedTx = await completedTx.sign.withWallet().complete(); - cborToEvaluate = signedTx.toCBOR(); + if (!changeAddress) throw new Error('changeAddress is required when building from outputs'); + const lucid = await lucidForAddress(makeProvider(), changeAddress); + const built = await buildMultiOutput(lucid, { outputs, metadataJson: metadata }); + feeFromBuild = built.fee; + // Ogmios evaluates scripts against the UNSIGNED transaction - + // signatures are not required for evaluation. + cborToEvaluate = built.txCbor; } if (!cborToEvaluate) { throw new Error('Provide either txCbor or outputs to evaluate'); } - const provider = new OgmiosProvider({ - ogmiosUrl: VECTOR_OGMIOS_URL, - submitUrl: VECTOR_SUBMIT_URL, - koiosUrl: VECTOR_KOIOS_URL, - }); + const provider = makeProvider(); let evalResult: VectorDryRunResult; try { @@ -626,7 +401,6 @@ Use vector_dry_run with this CBOR to simulate it, sign it locally with vector_si executionUnits: (totalMemory > 0 || totalCpu > 0) ? { memory: totalMemory, cpu: totalCpu } : undefined, }; } catch (evalErr) { - // evaluateTransaction failed - still return fee if we built the tx if (feeFromBuild) { evalResult = { valid: true, @@ -768,37 +542,25 @@ Transaction Hash: ${txHash} "vector_get_transaction_history", "Get transaction history for a Vector address via Koios indexed queries", { - address: z.string().optional().describe("Vector address to query. If omitted, mnemonic is required."), - mnemonic: z.string().optional().describe("15 or 24-word BIP39 mnemonic (required if address is omitted)"), + address: z.string().describe("Vector address to query (addr1...)"), limit: z.number().min(1).max(50).optional().describe("Number of transactions to return (default: 20, max: 50)"), offset: z.number().min(0).optional().describe("Offset for pagination (default: 0)"), }, - async ({ address, mnemonic, limit, offset }) => { + async ({ address, limit, offset }) => { const rateCheck = rateLimiter.check(); if (!rateCheck.allowed) { return { content: [{ type: "text", text: `Rate limit exceeded. Retry after ${rateCheck.retryAfterMs}ms.` }] }; } try { - let queryAddress = address; - if (!queryAddress) { - if (!mnemonic) throw new Error('Provide either address or mnemonic'); - const lucid = await initLucid(mnemonic); - queryAddress = await lucid.wallet().address(); - } + const provider = makeProvider(); - const provider = new OgmiosProvider({ - ogmiosUrl: VECTOR_OGMIOS_URL, - submitUrl: VECTOR_SUBMIT_URL, - koiosUrl: VECTOR_KOIOS_URL, - }); - - const txs = await provider.getTransactionHistory(queryAddress, offset || 0, limit || 20); + const txs = await provider.getTransactionHistory(address, offset || 0, limit || 20); if (txs.length === 0) { return { content: [{ type: "text", - text: `No transactions found for ${queryAddress}`, + text: `No transactions found for ${address}`, }], }; } @@ -811,13 +573,13 @@ Transaction Hash: ${txHash} return { content: [{ type: "text", - text: `# Transaction History for ${queryAddress} + text: `# Transaction History for ${address} Showing ${txs.length} transaction(s) (offset: ${offset || 0}): ${txList} -[View on Explorer](${VECTOR_EXPLORER_URL}/address/${queryAddress})`, +[View on Explorer](${VECTOR_EXPLORER_URL}/address/${address})`, }], }; } catch (err) { @@ -872,7 +634,7 @@ ${txList} ${result.txCbor} \`\`\` -Sign locally with vector_signer_sign, broadcast with vector_submit_transaction, then use vector_build_interact_contract to interact with the deployed contract.`, +Sign locally with vector_signer_sign, broadcast with vector_submit_transaction, then confirm with vector_await_transaction { txHash }. Once confirmed, use vector_build_interact_contract to interact with the deployed contract.`, }], }; } catch (err) { @@ -936,7 +698,7 @@ Sign locally with vector_signer_sign, broadcast with vector_submit_transaction, ${result.txCbor} \`\`\` -Sign locally with vector_signer_sign { txCbor }, then broadcast with vector_submit_transaction { signedTxCbor }.`, +Sign locally with vector_signer_sign { txCbor }, then broadcast with vector_submit_transaction { signedTxCbor }, then confirm with vector_await_transaction { txHash }.`, }], }; } catch (err) { diff --git a/packages/builder/test/boundary.test.ts b/packages/builder/test/boundary.test.ts new file mode 100644 index 0000000..4cd1a4a --- /dev/null +++ b/packages/builder/test/boundary.test.ts @@ -0,0 +1,54 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync, readdirSync, statSync } from 'node:fs'; +import { resolve, join, relative } from 'node:path'; + +/** + * The builder-side custody guarantee, made mechanical (design-spec success + * criterion 1: "grep-able: zero mnemonic params in the builder"). + * + * Files still on the custodial path are explicitly allowlisted with the PR + * that removes them. Shrinking this list is the definition of done for the + * non-custodial split; growing it should be impossible to do accidentally. + */ +const SRC_ROOT = resolve(import.meta.dirname!, '../src'); +const CUSTODIAL_UNTIL_LATER_PR = new Set([ + 'vector/agent-network.ts', // family 2 — goes keyless in spec PR 7 + 'vector/self-improvement.ts', // family 3 — goes keyless in spec PR 8 +]); +const FORBIDDEN = /\b(mnemonic|seed|bip39|fromSeed|walletFromSeed|selectWallet\.fromSeed)\b/i; + +function sourceFiles(dir: string): string[] { + const out: string[] = []; + for (const entry of readdirSync(dir)) { + const full = join(dir, entry); + if (statSync(full).isDirectory()) out.push(...sourceFiles(full)); + else if (/\.(ts|js|mjs|cjs|mts|cts)$/.test(entry)) out.push(full); + } + return out; +} + +describe('builder custody boundary', () => { + test('no key-material vocabulary outside the allowlisted custodial files', () => { + const offenders: string[] = []; + for (const file of sourceFiles(SRC_ROOT)) { + const rel = relative(SRC_ROOT, file).replace(/\\/g, '/'); + if (CUSTODIAL_UNTIL_LATER_PR.has(rel)) continue; + const lines = readFileSync(file, 'utf-8').split('\n'); + lines.forEach((line, i) => { + if (FORBIDDEN.test(line)) offenders.push(`${rel}:${i + 1}: ${line.trim()}`); + }); + } + assert.deepEqual(offenders, [], `key-material vocabulary found in keyless builder source:\n${offenders.join('\n')}`); + }); + + test('the allowlist matches reality (files removed from the custodial path must leave the list)', () => { + for (const rel of CUSTODIAL_UNTIL_LATER_PR) { + const content = readFileSync(join(SRC_ROOT, rel), 'utf-8'); + assert.match( + content, FORBIDDEN, + `${rel} is allowlisted as custodial but contains no key-material vocabulary - remove it from CUSTODIAL_UNTIL_LATER_PR`, + ); + } + }); +}); diff --git a/packages/builder/test/integration/rate-limit-gating.test.ts b/packages/builder/test/integration/rate-limit-gating.test.ts index fec0196..c0d71a7 100644 --- a/packages/builder/test/integration/rate-limit-gating.test.ts +++ b/packages/builder/test/integration/rate-limit-gating.test.ts @@ -14,8 +14,14 @@ import type { RawServerHandle } from '../setup.ts'; // .superpowers/sdd/pr5-integration-tests-report.md for the mutation-testing // evidence. // -// Tool choice: vector_get_spend_limits (vector.ts) reads only in-process -// SafetyLayer state - confirmed by reading safety.ts, no network/wallet. +// Tool choice: vector_build_send_apex (vector.ts) with a deliberately invalid +// changeAddress. lucidForAddress (packages/builder/src/vector/build.ts) +// validates the address with getAddressDetails() and throws BEFORE any +// provider call - proven by Task 2's booby-trapped-provider test. Handlers +// return caught errors as normal content, so isError stays false and this +// probe is just as hermetic as the vector_get_spend_limits tool it replaces +// (which vector.ts no longer has - Task 5 deleted it, since SafetyLayer state +// is no longer exposed to keyless callers). // vector_get_agent_profile (agent-network.ts) and // vector_self_improvement_analyze_metrics (self-improvement.ts) DO reach // Ogmios/Koios in their normal bodies, but only after their own @@ -78,11 +84,13 @@ describe('per-identity rate limiting across real registered tools', () => { }); test('alice exhausts her budget on a vector.ts tool; bob is unaffected (isolation via the real tools, not just limiterFor)', async () => { - const first = await call(alice, 'vector_get_spend_limits'); + const PROBE_ARGS = { changeAddress: 'not-an-address', recipientAddress: 'not-an-address', amount: 1 }; + + const first = await call(alice, 'vector_build_send_apex', PROBE_ARGS); assert.equal(first.isError, false, 'first call must not be a tool error'); assert.ok(!RATE_LIMIT_PATTERN.test(first.text), 'first call must not be rate-limited yet'); - const second = await call(alice, 'vector_get_spend_limits'); + const second = await call(alice, 'vector_build_send_apex', PROBE_ARGS); assert.equal(second.isError, false, 'a rate-limit refusal is a normal, well-formed result - not a thrown tool error'); assert.match(second.text, RATE_LIMIT_PATTERN, 'alice must now be rate-limited'); @@ -91,7 +99,7 @@ describe('per-identity rate limiting across real registered tools', () => { // inference from limiterFor() in isolation (rate-limiter.test.ts already // covers that in isolation; this covers the wiring rate-limiter.test.ts // cannot see). - const bobFirst = await call(bob, 'vector_get_spend_limits'); + const bobFirst = await call(bob, 'vector_build_send_apex', PROBE_ARGS); assert.equal(bobFirst.isError, false); assert.ok(!RATE_LIMIT_PATTERN.test(bobFirst.text), "bob must be unaffected by alice's exhausted budget"); }); diff --git a/packages/builder/test/smoke/tool-inventory.test.ts b/packages/builder/test/smoke/tool-inventory.test.ts index 4dfe1e9..ac4ebf7 100644 --- a/packages/builder/test/smoke/tool-inventory.test.ts +++ b/packages/builder/test/smoke/tool-inventory.test.ts @@ -5,13 +5,17 @@ import { resolve } from 'node:path'; import { startServer, stopServer, ServerContext } from '../setup.ts'; /** - * Snapshot of the pre-migration tool surface (23 tools), captured from a real - * `listTools()` call against a built server on the `ci/harness-and-gitignore` - * branch. This is the baseline for the non-custodial split series - * (see docs/architecture/non-custodial-split.md) — PRs 2, 6, 7 and 8 - * deliberately restructure and rename tool families, and each of those PRs is - * expected to update this list deliberately, not accidentally. No wallet, - * no network beyond localhost — this never needs a mnemonic. + * Snapshot of the current tool surface (23 tools) for the non-custodial split + * series (see docs/architecture/non-custodial-split.md). PR 6 (Task 5) is the + * PR that took this list from 25 to 23: it deleted `vector_get_address` and + * `vector_get_spend_limits` outright (the safety-layer/wallet-info tools have + * no keyless equivalent — spend limits are gone because the builder no longer + * holds keys to limit) as part of making the remaining query tools + * (`vector_get_utxos`, `vector_get_transaction_history`, `vector_dry_run`) + * keyless. PRs 2, 6, 7 and 8 each deliberately restructure and rename tool + * families, and each of those PRs is expected to update this list + * deliberately, not accidentally. No wallet, no network beyond localhost — + * this never needs a mnemonic. */ const EXPECTED_TOOLS = [ 'vector_await_transaction', @@ -23,10 +27,8 @@ const EXPECTED_TOOLS = [ 'vector_deregister_agent', 'vector_discover_agents', 'vector_dry_run', - 'vector_get_address', 'vector_get_agent_profile', 'vector_get_balance', - 'vector_get_spend_limits', 'vector_get_transaction_history', 'vector_get_utxos', 'vector_message_agent', @@ -42,33 +44,40 @@ const EXPECTED_TOOLS = [ ]; /** - * Full-schema snapshot: the exact `inputSchema` (JSON Schema) the live server - * advertises for every tool, keyed by tool name. This is a change detector, - * not a freeze — PRs 6, 7 and 8 deliberately restructure tool schemas as part - * of the non-custodial split, and each of those PRs is expected to - * regenerate this fixture to match its intentional changes (boot the built - * server, call listTools(), and dump { [name]: inputSchema } sorted by - * name — do not hand-edit the JSON). + * Full-schema snapshot: the exact `description` and `inputSchema` (JSON + * Schema) the live server advertises for every tool, keyed by tool name. + * This is a change detector, not a freeze — PRs 6, 7 and 8 deliberately + * restructure tool schemas as part of the non-custodial split, and each of + * those PRs is expected to regenerate this fixture to match its intentional + * changes: run `npm run build && node packages/builder/scripts/regen-tool-schemas.mjs` + * (boots the built server, calls listTools(), and dumps + * { [name]: { description, inputSchema } } sorted by name — do not hand-edit + * the JSON). * - * What this guards against is an *unintended* wire-format change slipping - * through unnoticed: the name/count checks above only catch a tool being - * added, removed, or renamed — they say nothing about a tool's argument - * schema quietly changing shape. That happened once already: converting - * vector.ts's Zod import from "zod" to "zod/v4" (for the MCP SDK's Zod 4 - * type inference) also switched which JSON Schema serializer the SDK uses - * for that file's tools — Zod v3 schemas go through the vendored - * zod-to-json-schema, Zod v4 schemas go through Zod's own native + * What this guards against is an *unintended* wire-format or copy change + * slipping through unnoticed: the name/count checks above only catch a tool + * being added, removed, or renamed — they say nothing about a tool's argument + * schema or description quietly changing shape. The inputSchema half happened + * once already: converting vector.ts's Zod import from "zod" to "zod/v4" + * (for the MCP SDK's Zod 4 type inference) also switched which JSON Schema + * serializer the SDK uses for that file's tools — Zod v3 schemas go through + * the vendored zod-to-json-schema, Zod v4 schemas go through Zod's own native * toJSONSchema — and the two disagree on whether object schemas get * `additionalProperties: false`. Nothing in the original name/count smoke - * test could see that; this fixture exists so the next silent schema drift - * fails loudly instead. + * test could see that. `description` was folded into this same fixture in + * PR 6 (Task 5) because several tool descriptions are the only place a + * caller learns the non-custodial build → sign → submit → confirm flow + * (vector_signer_sign / vector_submit_transaction / vector_await_transaction) + * — a silently edited or dropped sentence there is a real regression this + * fixture should catch too, not just a JSON Schema shape change. */ const SNAPSHOT_PATH = resolve(import.meta.dirname!, 'tool-schemas.snapshot.json'); -const schemaSnapshot: Record = JSON.parse(readFileSync(SNAPSHOT_PATH, 'utf-8')); +const schemaSnapshot: Record = + JSON.parse(readFileSync(SNAPSHOT_PATH, 'utf-8')); let ctx: ServerContext; let actualNames: string[]; -let actualToolsByName: Map; +let actualToolsByName: Map; before(async () => { ctx = await startServer(); @@ -82,11 +91,11 @@ after(async () => { }); describe('tool inventory smoke test', () => { - test('exposes exactly 25 tools', () => { + test('exposes exactly 23 tools', () => { assert.equal( actualNames.length, - 25, - `expected 25 tools, got ${actualNames.length}: ${JSON.stringify(actualNames)}` + 23, + `expected 23 tools, got ${actualNames.length}: ${JSON.stringify(actualNames)}` ); }); @@ -117,7 +126,7 @@ describe('tool schema snapshot', () => { 'test/smoke/tool-schemas.snapshot.json keys do not match the live tool set.\n' + ` live tools missing from snapshot: ${missingFromSnapshot.length ? JSON.stringify(missingFromSnapshot) : '(none)'}\n` + ` snapshot entries with no live tool: ${staleInSnapshot.length ? JSON.stringify(staleInSnapshot) : '(none)'}\n` + - 'Regenerate the snapshot from a live built server if this drift is intentional.' + 'Regenerate the snapshot from a live built server (packages/builder/scripts/regen-tool-schemas.mjs) if this drift is intentional.' ); }); @@ -126,18 +135,19 @@ describe('tool schema snapshot', () => { // diff of just that tool's schema, instead of "objects differ" over an // unreadable combined structure. for (const toolName of Object.keys(schemaSnapshot).sort()) { - test(`inputSchema unchanged: ${toolName}`, () => { + test(`schema+description unchanged: ${toolName}`, () => { const live = actualToolsByName.get(toolName); assert.ok( live, `Tool "${toolName}" is in the snapshot but was not returned by the live server's listTools().` ); assert.deepStrictEqual( - live.inputSchema, + { description: live.description, inputSchema: live.inputSchema }, schemaSnapshot[toolName], - `inputSchema for "${toolName}" differs from the checked-in snapshot in test/smoke/tool-schemas.snapshot.json.\n` + - 'If this is a deliberate schema change (e.g. PRs 6-8 restructuring tool families), regenerate the snapshot ' + - 'from a live built server. If not, this is the exact class of silent wire-format regression this test exists to catch.' + `description/inputSchema for "${toolName}" differs from the checked-in snapshot in test/smoke/tool-schemas.snapshot.json.\n` + + 'If this is a deliberate schema or copy change (e.g. PRs 6-8 restructuring tool families), regenerate the snapshot ' + + 'from a live built server (packages/builder/scripts/regen-tool-schemas.mjs). If not, this is the exact class of silent ' + + 'wire-format/copy regression this test exists to catch.' ); }); } diff --git a/packages/builder/test/smoke/tool-schemas.snapshot.json b/packages/builder/test/smoke/tool-schemas.snapshot.json index 1dda661..d73f855 100644 --- a/packages/builder/test/smoke/tool-schemas.snapshot.json +++ b/packages/builder/test/smoke/tool-schemas.snapshot.json @@ -1,799 +1,848 @@ { "vector_await_transaction": { - "type": "object", - "properties": { - "txHash": { - "description": "Transaction hash returned by vector_submit_transaction", - "type": "string" + "description": "Wait for a submitted transaction to be confirmed on Vector. Polls until it appears on-chain or the timeout elapses.", + "inputSchema": { + "type": "object", + "properties": { + "txHash": { + "description": "Transaction hash returned by vector_submit_transaction", + "type": "string" + }, + "timeoutSeconds": { + "description": "How long to wait before giving up (default: 120)", + "type": "number", + "minimum": 1, + "maximum": 300 + } }, - "timeoutSeconds": { - "description": "How long to wait before giving up (default: 120)", - "type": "number", - "minimum": 1, - "maximum": 300 - } - }, - "required": [ - "txHash" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "txHash" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_build_deploy_contract": { - "type": "object", - "properties": { - "changeAddress": { - "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", - "type": "string" + "description": "Build an UNSIGNED transaction deploying a Plutus/Aiken smart contract (locks funds at its script address). Takes no key material - sign with your local signer, then broadcast with vector_submit_transaction.", + "inputSchema": { + "type": "object", + "properties": { + "changeAddress": { + "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", + "type": "string" + }, + "scriptCbor": { + "description": "Compiled Plutus/Aiken script in CBOR hex format", + "type": "string" + }, + "scriptType": { + "description": "Script version", + "type": "string", + "enum": [ + "PlutusV1", + "PlutusV2", + "PlutusV3" + ] + }, + "initialDatum": { + "description": "Initial datum as CBOR hex. Use 'd87980' for void/unit datum. Defaults to void if omitted.", + "type": "string" + }, + "lovelaceAmount": { + "description": "AP3X to lock at the script address in lovelace (default: 2,000,000 = 2 AP3X)", + "type": "number" + } }, - "scriptCbor": { - "description": "Compiled Plutus/Aiken script in CBOR hex format", - "type": "string" - }, - "scriptType": { - "description": "Script version", - "type": "string", - "enum": [ - "PlutusV1", - "PlutusV2", - "PlutusV3" - ] - }, - "initialDatum": { - "description": "Initial datum as CBOR hex. Use 'd87980' for void/unit datum. Defaults to void if omitted.", - "type": "string" - }, - "lovelaceAmount": { - "description": "AP3X to lock at the script address in lovelace (default: 2,000,000 = 2 AP3X)", - "type": "number" - } - }, - "required": [ - "changeAddress", - "scriptCbor", - "scriptType" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "changeAddress", + "scriptCbor", + "scriptType" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_build_interact_contract": { - "type": "object", - "properties": { - "changeAddress": { - "description": "Your wallet address (source of funds, change, and the required signer for spends). Get it from your local signer's vector_signer_get_address.", - "type": "string" - }, - "scriptCbor": { - "description": "Compiled Plutus/Aiken script in CBOR hex", - "type": "string" - }, - "scriptType": { - "description": "Script version", - "type": "string", - "enum": [ - "PlutusV1", - "PlutusV2", - "PlutusV3" - ] - }, - "action": { - "description": "'spend' to collect UTxOs from the script, 'lock' to send funds to it", - "type": "string", - "enum": [ - "spend", - "lock" - ] - }, - "redeemer": { - "description": "Redeemer as CBOR hex (required for spend, use 'd87980' for void)", - "type": "string" - }, - "datum": { - "description": "Datum as CBOR hex (required for lock, use 'd87980' for void)", - "type": "string" - }, - "lovelaceAmount": { - "description": "Lovelace to lock (for lock action, default: 2,000,000 = 2 AP3X)", - "type": "number" - }, - "utxoRef": { - "description": "Specific UTxO to spend from (optional, otherwise spends all UTxOs at script address)", - "type": "object", - "properties": { - "txHash": { + "description": "Build an UNSIGNED transaction interacting with a deployed Plutus/Aiken contract - lock AP3X at it or spend from it. Takes no key material - sign with your local signer, then broadcast with vector_submit_transaction.", + "inputSchema": { + "type": "object", + "properties": { + "changeAddress": { + "description": "Your wallet address (source of funds, change, and the required signer for spends). Get it from your local signer's vector_signer_get_address.", + "type": "string" + }, + "scriptCbor": { + "description": "Compiled Plutus/Aiken script in CBOR hex", + "type": "string" + }, + "scriptType": { + "description": "Script version", + "type": "string", + "enum": [ + "PlutusV1", + "PlutusV2", + "PlutusV3" + ] + }, + "action": { + "description": "'spend' to collect UTxOs from the script, 'lock' to send funds to it", + "type": "string", + "enum": [ + "spend", + "lock" + ] + }, + "redeemer": { + "description": "Redeemer as CBOR hex (required for spend, use 'd87980' for void)", + "type": "string" + }, + "datum": { + "description": "Datum as CBOR hex (required for lock, use 'd87980' for void)", + "type": "string" + }, + "lovelaceAmount": { + "description": "Lovelace to lock (for lock action, default: 2,000,000 = 2 AP3X)", + "type": "number" + }, + "utxoRef": { + "description": "Specific UTxO to spend from (optional, otherwise spends all UTxOs at script address)", + "type": "object", + "properties": { + "txHash": { + "type": "string" + }, + "outputIndex": { + "type": "number" + } + }, + "required": [ + "txHash", + "outputIndex" + ] + }, + "assets": { + "description": "Additional native assets for lock action: { 'policyId+assetNameHex': 'quantity' }", + "type": "object", + "propertyNames": { "type": "string" }, - "outputIndex": { - "type": "number" + "additionalProperties": { + "type": "string" } - }, - "required": [ - "txHash", - "outputIndex" - ] - }, - "assets": { - "description": "Additional native assets for lock action: { 'policyId+assetNameHex': 'quantity' }", - "type": "object", - "propertyNames": { - "type": "string" - }, - "additionalProperties": { - "type": "string" } - } - }, - "required": [ - "changeAddress", - "scriptCbor", - "scriptType", - "action" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + }, + "required": [ + "changeAddress", + "scriptCbor", + "scriptType", + "action" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_build_send_apex": { - "type": "object", - "properties": { - "changeAddress": { - "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", - "type": "string" + "description": "Build an UNSIGNED transaction sending AP3X to a recipient. Takes no key material - sign the returned CBOR with your local signer (vector_signer_sign), then broadcast with vector_submit_transaction.", + "inputSchema": { + "type": "object", + "properties": { + "changeAddress": { + "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", + "type": "string" + }, + "recipientAddress": { + "description": "Recipient Vector address (addr1...)", + "type": "string" + }, + "amount": { + "description": "Amount of AP3X to send", + "type": "number", + "minimum": 1 + }, + "metadata": { + "description": "Optional transaction metadata in JSON format", + "type": "string" + } }, - "recipientAddress": { - "description": "Recipient Vector address (addr1...)", - "type": "string" - }, - "amount": { - "description": "Amount of AP3X to send", - "type": "number", - "minimum": 1 - }, - "metadata": { - "description": "Optional transaction metadata in JSON format", - "type": "string" - } - }, - "required": [ - "changeAddress", - "recipientAddress", - "amount" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "changeAddress", + "recipientAddress", + "amount" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_build_send_tokens": { - "type": "object", - "properties": { - "changeAddress": { - "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", - "type": "string" + "description": "Build an UNSIGNED transaction sending Vector native tokens. Takes no key material - sign the returned CBOR with your local signer (vector_signer_sign), then broadcast with vector_submit_transaction.", + "inputSchema": { + "type": "object", + "properties": { + "changeAddress": { + "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", + "type": "string" + }, + "recipientAddress": { + "description": "Recipient Vector address (addr1...)", + "type": "string" + }, + "policyId": { + "description": "Token policy ID (56 hex characters)", + "type": "string" + }, + "assetName": { + "description": "Asset name (text or hex; can be empty for policy-only tokens)", + "type": "string" + }, + "amount": { + "description": "Amount of tokens to send", + "type": "string" + }, + "adaAmount": { + "description": "Optional AP3X to include with the tokens (defaults to the 2 AP3X minimum)", + "type": "number" + } }, - "recipientAddress": { - "description": "Recipient Vector address (addr1...)", - "type": "string" - }, - "policyId": { - "description": "Token policy ID (56 hex characters)", - "type": "string" - }, - "assetName": { - "description": "Asset name (text or hex; can be empty for policy-only tokens)", - "type": "string" - }, - "amount": { - "description": "Amount of tokens to send", - "type": "string" - }, - "adaAmount": { - "description": "Optional AP3X to include with the tokens (defaults to the 2 AP3X minimum)", - "type": "number" - } - }, - "required": [ - "changeAddress", - "recipientAddress", - "policyId", - "assetName", - "amount" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "changeAddress", + "recipientAddress", + "policyId", + "assetName", + "amount" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_build_transaction": { - "type": "object", - "properties": { - "changeAddress": { - "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", - "type": "string" - }, - "outputs": { - "description": "Transaction outputs", - "minItems": 1, - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "description": "Recipient Vector address", - "type": "string" - }, - "lovelace": { - "description": "Amount in lovelace (1 AP3X = 1,000,000 lovelace)", - "type": "number" - }, - "assets": { - "description": "Optional native assets: { 'policyId+assetNameHex': 'quantity' }", - "type": "object", - "propertyNames": { + "description": "Build an UNSIGNED multi-output transaction with optional metadata. Takes no key material and never submits - sign the returned CBOR with your local signer (vector_signer_sign), then broadcast with vector_submit_transaction.", + "inputSchema": { + "type": "object", + "properties": { + "changeAddress": { + "description": "Your wallet address (source of funds; receives change). Get it from your local signer's vector_signer_get_address.", + "type": "string" + }, + "outputs": { + "description": "Transaction outputs", + "minItems": 1, + "type": "array", + "items": { + "type": "object", + "properties": { + "address": { + "description": "Recipient Vector address", "type": "string" }, - "additionalProperties": { - "type": "string" + "lovelace": { + "description": "Amount in lovelace (1 AP3X = 1,000,000 lovelace)", + "type": "number" + }, + "assets": { + "description": "Optional native assets: { 'policyId+assetNameHex': 'quantity' }", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } } - } - }, - "required": [ - "address", - "lovelace" - ] + }, + "required": [ + "address", + "lovelace" + ] + } + }, + "metadata": { + "description": "Optional JSON metadata (attached under label 674)", + "type": "string" } }, - "metadata": { - "description": "Optional JSON metadata (attached under label 674)", - "type": "string" - } - }, - "required": [ - "changeAddress", - "outputs" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "changeAddress", + "outputs" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_deregister_agent": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the agent's owner wallet", - "type": "string" + "description": "Deregister an agent from the Vector registry. Burns the identity NFT and returns the 10 AP3X deposit to your wallet.", + "inputSchema": { + "type": "object", + "properties": { + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the agent's owner wallet", + "type": "string" + }, + "agent_id": { + "description": "Agent DID to deregister: did:vector:agent:{policyId}:{nftAssetName}", + "type": "string" + } }, - "agent_id": { - "description": "Agent DID to deregister: did:vector:agent:{policyId}:{nftAssetName}", - "type": "string" - } - }, - "required": [ - "mnemonic", - "agent_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "mnemonic", + "agent_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_discover_agents": { - "type": "object", - "properties": { - "capability": { - "description": "Filter by capability tag (e.g. 'investing', 'research')", - "type": "string" + "description": "Discover registered agents in the Vector on-chain registry, optionally filtered by capability or framework", + "inputSchema": { + "type": "object", + "properties": { + "capability": { + "description": "Filter by capability tag (e.g. 'investing', 'research')", + "type": "string" + }, + "framework": { + "description": "Filter by framework (e.g. 'OpenClaw', 'LangChain', 'CrewAI')", + "type": "string" + }, + "limit": { + "description": "Maximum number of agents to return (default: 20)", + "default": 20, + "type": "number" + } }, - "framework": { - "description": "Filter by framework (e.g. 'OpenClaw', 'LangChain', 'CrewAI')", - "type": "string" - }, - "limit": { - "description": "Maximum number of agents to return (default: 20)", - "default": 20, - "type": "number" - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_dry_run": { - "type": "object", - "properties": { - "txCbor": { - "description": "Hex-encoded CBOR of a built transaction to evaluate", - "type": "string" - }, - "outputs": { - "description": "If no txCbor provided, build a TX from these outputs and evaluate it", - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "description": "Recipient Vector address", - "type": "string" - }, - "lovelace": { - "description": "Amount in lovelace", - "type": "number" - }, - "assets": { - "type": "object", - "propertyNames": { + "description": "Simulate a transaction without submitting - returns fee estimate and validation result. Accepts either built CBOR or outputs to build from (keyless).", + "inputSchema": { + "type": "object", + "properties": { + "txCbor": { + "description": "Hex-encoded CBOR of a built transaction to evaluate", + "type": "string" + }, + "outputs": { + "description": "If no txCbor provided, build an unsigned TX from these outputs and evaluate it", + "type": "array", + "items": { + "type": "object", + "properties": { + "address": { + "description": "Recipient Vector address", "type": "string" }, - "additionalProperties": { - "type": "string" + "lovelace": { + "description": "Amount in lovelace", + "type": "number" + }, + "assets": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } } - } - }, - "required": [ - "address", - "lovelace" + }, + "required": [ + "address", + "lovelace" + ] + } + }, + "changeAddress": { + "description": "Your wallet address (required when building from outputs)", + "type": "string" + }, + "metadata": { + "description": "Optional JSON metadata when building from outputs", + "type": "string" + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_get_agent_profile": { + "description": "Get a registered agent's profile from the on-chain registry by DID (did:vector:agent:...)", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "description": "Agent DID: did:vector:agent:{policyId}:{nftAssetName}", + "type": "string" + } + }, + "required": [ + "agent_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_get_balance": { + "description": "Get AP3X and token balances for a Vector address", + "inputSchema": { + "type": "object", + "properties": { + "address": { + "description": "Vector address to check (addr1...)", + "type": "string" + } + }, + "required": [ + "address" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_get_transaction_history": { + "description": "Get transaction history for a Vector address via Koios indexed queries", + "inputSchema": { + "type": "object", + "properties": { + "address": { + "description": "Vector address to query (addr1...)", + "type": "string" + }, + "limit": { + "description": "Number of transactions to return (default: 20, max: 50)", + "type": "number", + "minimum": 1, + "maximum": 50 + }, + "offset": { + "description": "Offset for pagination (default: 0)", + "type": "number", + "minimum": 0 + } + }, + "required": [ + "address" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_get_utxos": { + "description": "List unspent transaction outputs (UTxOs) for a Vector address", + "inputSchema": { + "type": "object", + "properties": { + "address": { + "description": "Vector address to query UTxOs for (addr1...)", + "type": "string" + } + }, + "required": [ + "address" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_message_agent": { + "description": "Send an on-chain message to a registered Vector agent via TX metadata (label 674). Delivers 2 AP3X to the agent's owner address.", + "inputSchema": { + "type": "object", + "properties": { + "agent_id": { + "description": "Recipient agent DID: did:vector:agent:{policyId}:{nftAssetName}", + "type": "string" + }, + "message_type": { + "description": "Type of message", + "type": "string", + "enum": [ + "inquiry", + "proposal", + "result" + ] + }, + "payload": { + "description": "Message payload (string, max 512 chars)", + "type": "string", + "maxLength": 512 + }, + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the sending wallet", + "type": "string" + } + }, + "required": [ + "agent_id", + "message_type", + "payload", + "mnemonic" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_register_agent": { + "description": "Register an agent in the Vector on-chain agent registry. Mints a soulbound identity NFT and locks a 10 AP3X deposit.", + "inputSchema": { + "type": "object", + "properties": { + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the registering wallet", + "type": "string" + }, + "name": { + "description": "Agent name (e.g. 'TradingBot', 'ResearchAgent')", + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "description": { + "description": "Short description of the agent's purpose", + "type": "string", + "maxLength": 256 + }, + "capabilities": { + "description": "List of capability tags (e.g. ['investing', 'research', 'environmental'])", + "type": "array", + "items": { + "type": "string" + } + }, + "framework": { + "description": "Framework used (e.g. 'OpenClaw', 'LangChain', 'CrewAI', 'custom')", + "type": "string" + }, + "endpoint": { + "description": "A2A communication endpoint URL (or empty string if not applicable)", + "type": "string" + } + }, + "required": [ + "mnemonic", + "name", + "description", + "capabilities", + "framework", + "endpoint" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_self_improvement_analyze_metrics": { + "description": "Analyze proposal metrics: proposal activity, adoption rate, treasury health, and engagement statistics. Read-only - no mnemonic needed.", + "inputSchema": { + "type": "object", + "properties": { + "focus": { + "description": "Analysis focus area", + "default": "overview", + "type": "string", + "enum": [ + "overview", + "adoption", + "treasury", + "activity" ] } }, - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic (required when outputs is provided)", - "type": "string" - }, - "metadata": { - "description": "Optional JSON metadata when building from outputs", - "type": "string" - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_get_address": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the wallet", - "type": "string" - } - }, - "required": [ - "mnemonic" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_get_agent_profile": { - "type": "object", - "properties": { - "agent_id": { - "description": "Agent DID: did:vector:agent:{policyId}:{nftAssetName}", - "type": "string" - } - }, - "required": [ - "agent_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_get_balance": { - "type": "object", - "properties": { - "address": { - "description": "Vector address to check (addr1...)", - "type": "string" - } - }, - "required": [ - "address" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_get_spend_limits": { - "type": "object", - "properties": {}, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_get_transaction_history": { - "type": "object", - "properties": { - "address": { - "description": "Vector address to query. If omitted, mnemonic is required.", - "type": "string" - }, - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic (required if address is omitted)", - "type": "string" - }, - "limit": { - "description": "Number of transactions to return (default: 20, max: 50)", - "type": "number", - "minimum": 1, - "maximum": 50 - }, - "offset": { - "description": "Offset for pagination (default: 0)", - "type": "number", - "minimum": 0 - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_get_utxos": { - "type": "object", - "properties": { - "address": { - "description": "Vector address to query UTxOs for. If omitted, mnemonic is required.", - "type": "string" - }, - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic (required if address is omitted)", - "type": "string" - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_message_agent": { - "type": "object", - "properties": { - "agent_id": { - "description": "Recipient agent DID: did:vector:agent:{policyId}:{nftAssetName}", - "type": "string" - }, - "message_type": { - "description": "Type of message", - "type": "string", - "enum": [ - "inquiry", - "proposal", - "result" - ] - }, - "payload": { - "description": "Message payload (string, max 512 chars)", - "type": "string", - "maxLength": 512 - }, - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the sending wallet", - "type": "string" - } - }, - "required": [ - "agent_id", - "message_type", - "payload", - "mnemonic" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_register_agent": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the registering wallet", - "type": "string" - }, - "name": { - "description": "Agent name (e.g. 'TradingBot', 'ResearchAgent')", - "type": "string", - "minLength": 1, - "maxLength": 64 - }, - "description": { - "description": "Short description of the agent's purpose", - "type": "string", - "maxLength": 256 - }, - "capabilities": { - "description": "List of capability tags (e.g. ['investing', 'research', 'environmental'])", - "type": "array", - "items": { - "type": "string" - } - }, - "framework": { - "description": "Framework used (e.g. 'OpenClaw', 'LangChain', 'CrewAI', 'custom')", - "type": "string" - }, - "endpoint": { - "description": "A2A communication endpoint URL (or empty string if not applicable)", - "type": "string" - } - }, - "required": [ - "mnemonic", - "name", - "description", - "capabilities", - "framework", - "endpoint" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_self_improvement_analyze_metrics": { - "type": "object", - "properties": { - "focus": { - "description": "Analysis focus area", - "default": "overview", - "type": "string", - "enum": [ - "overview", - "adoption", - "treasury", - "activity" - ] - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" + "$schema": "http://json-schema.org/draft-07/schema#" + } }, "vector_self_improvement_browse": { - "type": "object", - "properties": { - "entity": { - "description": "What to browse", - "type": "string", - "enum": [ - "proposals", - "critiques", - "endorsements", - "treasury" - ] - }, - "state": { - "description": "Filter proposals by state: Open, Amended, Adopted, Rejected, Expired, Withdrawn", - "type": "string" - }, - "proposalType": { - "description": "Filter proposals by type: ParameterChange, TreasurySpend, ProtocolUpgrade, GameActivation, GeneralSuggestion", - "type": "string" - }, - "proposerDid": { - "description": "Filter by proposer DID (hex)", - "type": "string" - }, - "proposalTxHash": { - "description": "Filter critiques/endorsements by the proposal they reference", - "type": "string" - } - }, - "required": [ - "entity" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_self_improvement_critique": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the wallet", - "type": "string" - }, - "agentDid": { - "description": "Agent DID (hex)", - "type": "string" - }, - "proposalTxHash": { - "description": "TX hash of the proposal UTxO to critique", - "type": "string" - }, - "proposalOutputIndex": { - "description": "Output index of the proposal UTxO", - "default": 0, - "type": "number" - }, - "critiqueDocument": { - "description": "Full critique document as JSON string. Uploaded to IPFS via Filebase; hash and CID computed automatically.", - "type": "string" - }, - "critiqueHash": { - "description": "blake2b_256 hash of critique document (64 hex chars). Required if critiqueDocument is not provided.", - "type": "string" - }, - "critiqueType": { - "description": "Type of critique", - "type": "string", - "enum": [ - "Supportive", - "Opposing", - "Amendment" - ] - }, - "storageUri": { - "description": "Off-chain storage URI for the critique document. Required if critiqueDocument is not provided.", - "type": "string" - }, - "stakeApex": { - "description": "AP3X to stake (minimum 10)", - "type": "number", - "minimum": 10 - } - }, - "required": [ - "mnemonic", - "agentDid", - "proposalTxHash", - "critiqueType", - "stakeApex" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_self_improvement_endorse": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the wallet", - "type": "string" - }, - "agentDid": { - "description": "Agent DID (hex)", - "type": "string" - }, - "proposalTxHash": { - "description": "TX hash of the proposal UTxO to endorse", - "type": "string" - }, - "proposalOutputIndex": { - "description": "Output index of the proposal UTxO", - "default": 0, - "type": "number" - }, - "stakeApex": { - "description": "AP3X to stake as endorsement (minimum 5)", - "type": "number", - "minimum": 5 - } - }, - "required": [ - "mnemonic", - "agentDid", - "proposalTxHash", - "stakeApex" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_self_improvement_submit_proposal": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the wallet", - "type": "string" - }, - "agentDid": { - "description": "Agent DID (hex) - the asset name from Agent Registry NFT", - "type": "string" - }, - "proposalDocument": { - "description": "Full proposal document as JSON string. Uploaded to IPFS via Filebase; hash and CID computed automatically. If provided, proposalHash and storageUri are ignored.", - "type": "string" - }, - "proposalHash": { - "description": "blake2b_256 hash of proposal document (64 hex chars). Required if proposalDocument is not provided.", - "type": "string" - }, - "proposalType": { - "description": "Category of the proposal", - "type": "string", - "enum": [ - "ParameterChange", - "TreasurySpend", - "ProtocolUpgrade", - "GameActivation", - "GeneralSuggestion" - ] - }, - "storageUri": { - "description": "Off-chain storage URI for the full proposal (IPFS CID or OriginTrail UAL). Required if proposalDocument is not provided.", - "type": "string" - }, - "stakeApex": { - "description": "AP3X to stake (minimum 25)", - "type": "number", - "minimum": 25 - }, - "typeParams": { - "description": "Type-specific parameters (required for ParameterChange and TreasurySpend)", - "type": "object", - "properties": { - "paramName": { - "type": "string" - }, - "currentValue": { - "type": "number" - }, - "proposedValue": { - "type": "number" - }, - "amount": { - "type": "number" - }, - "recipientDescription": { - "type": "string" - }, - "upgradeHash": { - "type": "string" - }, - "gameId": { - "type": "number" - } - } - }, - "priority": { - "description": "Priority level (Emergency requires higher stake and reputation)", - "default": "Standard", - "type": "string", - "enum": [ - "Standard", - "Emergency" - ] - } - }, - "required": [ - "mnemonic", - "agentDid", - "proposalType", - "stakeApex" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_submit_transaction": { - "type": "object", - "properties": { - "signedTxCbor": { - "description": "Hex-encoded CBOR of a fully signed transaction", - "type": "string" - } - }, - "required": [ - "signedTxCbor" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_transfer_agent": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the current owner's wallet", - "type": "string" - }, - "agent_id": { - "description": "Agent DID to transfer: did:vector:agent:{policyId}:{nftAssetName}", - "type": "string" - }, - "new_owner_address": { - "description": "Bech32 address of the new owner (must be a verification key address, not a script address)", - "type": "string" - } - }, - "required": [ - "mnemonic", - "agent_id", - "new_owner_address" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "vector_update_agent": { - "type": "object", - "properties": { - "mnemonic": { - "description": "15 or 24-word BIP39 mnemonic for the agent's owner wallet", - "type": "string" - }, - "agent_id": { - "description": "Agent DID to update: did:vector:agent:{policyId}:{nftAssetName}", - "type": "string" - }, - "name": { - "description": "New agent name", - "type": "string", - "minLength": 1, - "maxLength": 64 - }, - "description": { - "description": "New description", - "type": "string", - "maxLength": 256 - }, - "capabilities": { - "description": "New capability tags (replaces existing list)", - "type": "array", - "items": { + "description": "Browse improvement proposals, critiques, and endorsements. Query on-chain UTxOs at the module's script addresses and decode datums into human-readable format.", + "inputSchema": { + "type": "object", + "properties": { + "entity": { + "description": "What to browse", + "type": "string", + "enum": [ + "proposals", + "critiques", + "endorsements", + "treasury" + ] + }, + "state": { + "description": "Filter proposals by state: Open, Amended, Adopted, Rejected, Expired, Withdrawn", + "type": "string" + }, + "proposalType": { + "description": "Filter proposals by type: ParameterChange, TreasurySpend, ProtocolUpgrade, GameActivation, GeneralSuggestion", + "type": "string" + }, + "proposerDid": { + "description": "Filter by proposer DID (hex)", + "type": "string" + }, + "proposalTxHash": { + "description": "Filter critiques/endorsements by the proposal they reference", "type": "string" } }, - "framework": { - "description": "New framework identifier", - "type": "string" + "required": [ + "entity" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_self_improvement_critique": { + "description": "Submit a critique on an improvement proposal. Critiques can support, oppose, or propose amendments. Requires staking AP3X. Provide critiqueDocument (JSON string) for automatic IPFS upload, or critiqueHash + storageUri manually.", + "inputSchema": { + "type": "object", + "properties": { + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the wallet", + "type": "string" + }, + "agentDid": { + "description": "Agent DID (hex)", + "type": "string" + }, + "proposalTxHash": { + "description": "TX hash of the proposal UTxO to critique", + "type": "string" + }, + "proposalOutputIndex": { + "description": "Output index of the proposal UTxO", + "default": 0, + "type": "number" + }, + "critiqueDocument": { + "description": "Full critique document as JSON string. Uploaded to IPFS via Filebase; hash and CID computed automatically.", + "type": "string" + }, + "critiqueHash": { + "description": "blake2b_256 hash of critique document (64 hex chars). Required if critiqueDocument is not provided.", + "type": "string" + }, + "critiqueType": { + "description": "Type of critique", + "type": "string", + "enum": [ + "Supportive", + "Opposing", + "Amendment" + ] + }, + "storageUri": { + "description": "Off-chain storage URI for the critique document. Required if critiqueDocument is not provided.", + "type": "string" + }, + "stakeApex": { + "description": "AP3X to stake (minimum 10)", + "type": "number", + "minimum": 10 + } }, - "endpoint": { - "description": "New A2A endpoint URL (or empty string to clear)", - "type": "string" - } - }, - "required": [ - "mnemonic", - "agent_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" + "required": [ + "mnemonic", + "agentDid", + "proposalTxHash", + "critiqueType", + "stakeApex" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_self_improvement_endorse": { + "description": "Endorse an improvement proposal by staking AP3X. Endorsements signal support to the Foundation Council and are weighted by stake amount.", + "inputSchema": { + "type": "object", + "properties": { + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the wallet", + "type": "string" + }, + "agentDid": { + "description": "Agent DID (hex)", + "type": "string" + }, + "proposalTxHash": { + "description": "TX hash of the proposal UTxO to endorse", + "type": "string" + }, + "proposalOutputIndex": { + "description": "Output index of the proposal UTxO", + "default": 0, + "type": "number" + }, + "stakeApex": { + "description": "AP3X to stake as endorsement (minimum 5)", + "type": "number", + "minimum": 5 + } + }, + "required": [ + "mnemonic", + "agentDid", + "proposalTxHash", + "stakeApex" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_self_improvement_submit_proposal": { + "description": "Submit an improvement proposal to the Vector Self-Improvement Module. Requires staking AP3X. Provide proposalDocument (JSON string) for automatic IPFS upload via Filebase and blake2b_256 hashing, OR provide proposalHash and storageUri manually.", + "inputSchema": { + "type": "object", + "properties": { + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the wallet", + "type": "string" + }, + "agentDid": { + "description": "Agent DID (hex) - the asset name from Agent Registry NFT", + "type": "string" + }, + "proposalDocument": { + "description": "Full proposal document as JSON string. Uploaded to IPFS via Filebase; hash and CID computed automatically. If provided, proposalHash and storageUri are ignored.", + "type": "string" + }, + "proposalHash": { + "description": "blake2b_256 hash of proposal document (64 hex chars). Required if proposalDocument is not provided.", + "type": "string" + }, + "proposalType": { + "description": "Category of the proposal", + "type": "string", + "enum": [ + "ParameterChange", + "TreasurySpend", + "ProtocolUpgrade", + "GameActivation", + "GeneralSuggestion" + ] + }, + "storageUri": { + "description": "Off-chain storage URI for the full proposal (IPFS CID or OriginTrail UAL). Required if proposalDocument is not provided.", + "type": "string" + }, + "stakeApex": { + "description": "AP3X to stake (minimum 25)", + "type": "number", + "minimum": 25 + }, + "typeParams": { + "description": "Type-specific parameters (required for ParameterChange and TreasurySpend)", + "type": "object", + "properties": { + "paramName": { + "type": "string" + }, + "currentValue": { + "type": "number" + }, + "proposedValue": { + "type": "number" + }, + "amount": { + "type": "number" + }, + "recipientDescription": { + "type": "string" + }, + "upgradeHash": { + "type": "string" + }, + "gameId": { + "type": "number" + } + } + }, + "priority": { + "description": "Priority level (Emergency requires higher stake and reputation)", + "default": "Standard", + "type": "string", + "enum": [ + "Standard", + "Emergency" + ] + } + }, + "required": [ + "mnemonic", + "agentDid", + "proposalType", + "stakeApex" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_submit_transaction": { + "description": "Submit an already-signed transaction to Vector. Use this to broadcast CBOR signed elsewhere (for example by a local signer). Does not sign anything.", + "inputSchema": { + "type": "object", + "properties": { + "signedTxCbor": { + "description": "Hex-encoded CBOR of a fully signed transaction", + "type": "string" + } + }, + "required": [ + "signedTxCbor" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_transfer_agent": { + "description": "Transfer agent ownership to a new address. The new owner must have a verification key credential (not a script address).", + "inputSchema": { + "type": "object", + "properties": { + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the current owner's wallet", + "type": "string" + }, + "agent_id": { + "description": "Agent DID to transfer: did:vector:agent:{policyId}:{nftAssetName}", + "type": "string" + }, + "new_owner_address": { + "description": "Bech32 address of the new owner (must be a verification key address, not a script address)", + "type": "string" + } + }, + "required": [ + "mnemonic", + "agent_id", + "new_owner_address" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "vector_update_agent": { + "description": "Update a registered agent's profile fields (name, description, capabilities, framework, endpoint). Only the specified fields are changed; others are preserved.", + "inputSchema": { + "type": "object", + "properties": { + "mnemonic": { + "description": "15 or 24-word BIP39 mnemonic for the agent's owner wallet", + "type": "string" + }, + "agent_id": { + "description": "Agent DID to update: did:vector:agent:{policyId}:{nftAssetName}", + "type": "string" + }, + "name": { + "description": "New agent name", + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "description": { + "description": "New description", + "type": "string", + "maxLength": 256 + }, + "capabilities": { + "description": "New capability tags (replaces existing list)", + "type": "array", + "items": { + "type": "string" + } + }, + "framework": { + "description": "New framework identifier", + "type": "string" + }, + "endpoint": { + "description": "New A2A endpoint URL (or empty string to clear)", + "type": "string" + } + }, + "required": [ + "mnemonic", + "agent_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + } } } diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index 35de9dc..08cd075 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -1,43 +1,5 @@ // Vector-specific type definitions -export interface VectorToken { - unit: string; - name: string; - quantity: string; -} - -export interface VectorWalletInfo { - address: string; - utxoCount: number; - ada: string; - tokens: VectorToken[]; -} - -export interface VectorAdaTransactionResult { - txHash: string; - senderAddress: string; - recipientAddress: string; - amount: number; - links: { - explorer: string; - }; -} - -export interface VectorTokenTransactionResult { - txHash: string; - senderAddress: string; - recipientAddress: string; - token: { - policyId: string; - name: string; - amount: string; - }; - ada: string; - links: { - explorer: string; - }; -} - export interface SpendLimits { perTransaction: number; // lovelace daily: number; // lovelace @@ -67,19 +29,6 @@ export interface TxOutput { assets?: Record; // unit -> quantity } -export interface VectorBuildTransactionResult { - txCbor: string; - txHash: string; - fee: string; - feeAda: string; - outputCount: number; - totalAda: string; - submitted: boolean; - links?: { - explorer: string; - }; -} - // --- Dry Run --- export interface VectorDryRunResult { @@ -108,29 +57,6 @@ export interface VectorTransactionHistoryResult { total: number; } -// --- Deploy Contract --- - -export interface VectorDeployContractResult { - txHash: string; - scriptAddress: string; - scriptHash: string; - scriptType: string; - links: { - explorer: string; - }; -} - -// --- Interact Contract --- - -export interface VectorInteractContractResult { - txHash: string; - scriptAddress: string; - action: 'spend' | 'lock'; - links: { - explorer: string; - }; -} - // --- Keyless build results (non-custodial split, family 1) --- // Every build_* tool returns unsigned CBOR; signing happens on the local // signer, submission via vector_submit_transaction.