dero-mcp-server 0.1.2 → 0.4.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/POSITIONING.md +94 -0
- package/README.md +132 -31
- package/SKILL.md +264 -0
- package/data/docs-index.json +276 -264
- package/dist/bn254.d.ts +74 -0
- package/dist/bn254.d.ts.map +1 -0
- package/dist/bn254.js +205 -0
- package/dist/bn254.js.map +1 -0
- package/dist/citations.d.ts +140 -0
- package/dist/citations.d.ts.map +1 -0
- package/dist/citations.js +322 -0
- package/dist/citations.js.map +1 -0
- package/dist/composites/_shared.d.ts +119 -0
- package/dist/composites/_shared.d.ts.map +1 -0
- package/dist/composites/_shared.js +152 -0
- package/dist/composites/_shared.js.map +1 -0
- package/dist/composites/audit-chain-artifact-claim.d.ts +128 -0
- package/dist/composites/audit-chain-artifact-claim.d.ts.map +1 -0
- package/dist/composites/audit-chain-artifact-claim.js +305 -0
- package/dist/composites/audit-chain-artifact-claim.js.map +1 -0
- package/dist/composites/diagnose-chain-health.d.ts +64 -0
- package/dist/composites/diagnose-chain-health.d.ts.map +1 -0
- package/dist/composites/diagnose-chain-health.js +144 -0
- package/dist/composites/diagnose-chain-health.js.map +1 -0
- package/dist/composites/estimate-deploy-cost.d.ts +83 -0
- package/dist/composites/estimate-deploy-cost.d.ts.map +1 -0
- package/dist/composites/estimate-deploy-cost.js +116 -0
- package/dist/composites/estimate-deploy-cost.js.map +1 -0
- package/dist/composites/explain-smart-contract.d.ts +64 -0
- package/dist/composites/explain-smart-contract.d.ts.map +1 -0
- package/dist/composites/explain-smart-contract.js +149 -0
- package/dist/composites/explain-smart-contract.js.map +1 -0
- package/dist/composites/forge-demo-proof.d.ts +81 -0
- package/dist/composites/forge-demo-proof.d.ts.map +1 -0
- package/dist/composites/forge-demo-proof.js +204 -0
- package/dist/composites/forge-demo-proof.js.map +1 -0
- package/dist/composites/recommend-docs-path.d.ts +97 -0
- package/dist/composites/recommend-docs-path.d.ts.map +1 -0
- package/dist/composites/recommend-docs-path.js +149 -0
- package/dist/composites/recommend-docs-path.js.map +1 -0
- package/dist/composites/trace-transaction-with-context.d.ts +107 -0
- package/dist/composites/trace-transaction-with-context.d.ts.map +1 -0
- package/dist/composites/trace-transaction-with-context.js +217 -0
- package/dist/composites/trace-transaction-with-context.js.map +1 -0
- package/dist/daemon-base.d.ts +28 -0
- package/dist/daemon-base.d.ts.map +1 -0
- package/dist/daemon-base.js +62 -0
- package/dist/daemon-base.js.map +1 -0
- package/dist/dero-curve.d.ts +79 -0
- package/dist/dero-curve.d.ts.map +1 -0
- package/dist/dero-curve.js +79 -0
- package/dist/dero-curve.js.map +1 -0
- package/dist/docs-parse.d.ts.map +1 -1
- package/dist/docs-parse.js +18 -2
- package/dist/docs-parse.js.map +1 -1
- package/dist/http-server.d.ts +37 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +132 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.js +18 -11
- package/dist/index.js.map +1 -1
- package/dist/proof-decode.d.ts +125 -0
- package/dist/proof-decode.d.ts.map +1 -0
- package/dist/proof-decode.js +619 -0
- package/dist/proof-decode.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +414 -114
- package/dist/server.js.map +1 -1
- package/dist/tool-descriptions.d.ts +53 -0
- package/dist/tool-descriptions.d.ts.map +1 -0
- package/dist/tool-descriptions.js +285 -0
- package/dist/tool-descriptions.js.map +1 -0
- package/dist/tx-parse.d.ts +63 -0
- package/dist/tx-parse.d.ts.map +1 -0
- package/dist/tx-parse.js +183 -0
- package/dist/tx-parse.js.map +1 -0
- package/package.json +27 -2
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dero_forge_demo_proof` — composite #7.
|
|
3
|
+
*
|
|
4
|
+
* Builds a `deroproof…` display object for a chosen transaction, ring slot,
|
|
5
|
+
* and amount. The string is constructed locally from public chain data — no
|
|
6
|
+
* wallet, no keys, no broadcast. On an unpatched explorer it shows
|
|
7
|
+
* **Verified ✓** for the chosen amount; on the chain, nothing changes.
|
|
8
|
+
*
|
|
9
|
+
* Purpose: defensive demonstration of the integrity-page Part 3 forge math
|
|
10
|
+
* (`docs/integrity/inflation-claim`, Part 3). When an AI agent is asked
|
|
11
|
+
* "does this deroproof string prove the chain minted coins?", the agent can
|
|
12
|
+
* forge an equivalent string here and show, on real chain inputs, that
|
|
13
|
+
* **Verified** is a display-layer fact about the pasted object — not a
|
|
14
|
+
* consensus statement about coins moving.
|
|
15
|
+
*
|
|
16
|
+
* Math (same equation `proof.Prove()` checks at `proof/proof.go:88-95`):
|
|
17
|
+
*
|
|
18
|
+
* blinder = C[slot] − amount × G
|
|
19
|
+
* forged_string = bech32("deroproof", version || blinder_compressed || CBOR({HH: 0…0, VU: amount}))
|
|
20
|
+
*
|
|
21
|
+
* Self-check: amount × G + blinder == C[slot]. If this fails, the encoded
|
|
22
|
+
* string would not verify and we never return it.
|
|
23
|
+
*
|
|
24
|
+
* Read-only invariant: this tool computes a string and returns it. It does
|
|
25
|
+
* NOT touch a wallet, broadcast a transaction, or mutate any chain state.
|
|
26
|
+
* Annotation `readOnlyHint: true` is preserved.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
import { G, add, deroCompressHex, negate, pointsEqual, scalarMult, uint64ToSignedScalar, } from '../bn254.js';
|
|
30
|
+
import { enrichWithFlaggedArtifacts, relatedDocsFor, } from '../citations.js';
|
|
31
|
+
import { encodeForgeProofString } from '../proof-decode.js';
|
|
32
|
+
import { getRingCommitment, parseTransaction } from '../tx-parse.js';
|
|
33
|
+
import { runChain, stepLatencies, stepValue, } from './_shared.js';
|
|
34
|
+
const ATOMIC = 100000n;
|
|
35
|
+
const UINT64_MOD = 1n << 64n;
|
|
36
|
+
export const forgeDemoProofInputSchema = {
|
|
37
|
+
tx_hash: z
|
|
38
|
+
.string()
|
|
39
|
+
.regex(/^[0-9a-fA-F]{64}$/, 'tx_hash must be 64 hex characters')
|
|
40
|
+
.optional()
|
|
41
|
+
.describe('TX hash to forge against. Daemon fetches the TX hex + ring members. Mutually exclusive with tx_hex.'),
|
|
42
|
+
tx_hex: z
|
|
43
|
+
.string()
|
|
44
|
+
.min(2)
|
|
45
|
+
.optional()
|
|
46
|
+
.describe('Raw TX bytes as hex (skip the daemon round-trip). Mutually exclusive with tx_hash. When provided, ring_receiver_address is omitted from the response (the hex carries publickey pointers, not full addresses).'),
|
|
47
|
+
ring_slot: z
|
|
48
|
+
.number()
|
|
49
|
+
.int()
|
|
50
|
+
.nonnegative()
|
|
51
|
+
.default(0)
|
|
52
|
+
.describe('Which ring slot 0..ring_size-1 the forged proof should resolve to. Defaults to 0.'),
|
|
53
|
+
amount_dero: z
|
|
54
|
+
.string()
|
|
55
|
+
.regex(/^-?\d+(\.\d{1,5})?$/, 'amount_dero must be a signed decimal with at most 5 fractional digits (e.g. "-1", "1000000", "-2200000.00181")')
|
|
56
|
+
.default('-1')
|
|
57
|
+
.describe('Target display amount in signed DERO (5 fractional digits = atomic precision). Negative values demonstrate the uint64 wraparound. Default "-1".'),
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Parse a signed-decimal DERO string (e.g. "-2200000.00181") into:
|
|
61
|
+
* - signed atoms (bigint, may be negative)
|
|
62
|
+
* - uint64 (atoms mod 2^64)
|
|
63
|
+
*/
|
|
64
|
+
function parseAmount(amountDero) {
|
|
65
|
+
const negative = amountDero.startsWith('-');
|
|
66
|
+
const unsigned = negative ? amountDero.slice(1) : amountDero;
|
|
67
|
+
const dot = unsigned.indexOf('.');
|
|
68
|
+
const wholeStr = dot === -1 ? unsigned : unsigned.slice(0, dot);
|
|
69
|
+
const fracStr = dot === -1 ? '' : unsigned.slice(dot + 1);
|
|
70
|
+
const fracPadded = (fracStr + '00000').slice(0, 5);
|
|
71
|
+
const whole = BigInt(wholeStr.length === 0 ? '0' : wholeStr);
|
|
72
|
+
const frac = BigInt(fracPadded);
|
|
73
|
+
const absAtoms = whole * ATOMIC + frac;
|
|
74
|
+
const signedAtoms = negative ? -absAtoms : absAtoms;
|
|
75
|
+
const uint64 = ((signedAtoms % UINT64_MOD) + UINT64_MOD) % UINT64_MOD;
|
|
76
|
+
// Normalize the input echo so callers see the canonical form (5 fractional digits).
|
|
77
|
+
const normalized = `${negative && absAtoms > 0n ? '-' : ''}${whole.toString()}.${fracPadded}`;
|
|
78
|
+
return { signedAtoms, uint64, signedDeroNormalized: normalized };
|
|
79
|
+
}
|
|
80
|
+
/** Format a uint64 as the unpatched-explorer display amount (whole.fractional with 5 digits). */
|
|
81
|
+
function formatExplorerDisplay(uint64) {
|
|
82
|
+
const whole = uint64 / ATOMIC;
|
|
83
|
+
const frac = uint64 % ATOMIC;
|
|
84
|
+
// Group thousands in the whole-part for readability.
|
|
85
|
+
const wholeStr = whole.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
86
|
+
return `${wholeStr}.${frac.toString().padStart(5, '0')} DERO`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Execute the forge. Composes:
|
|
90
|
+
* 1. (optional) daemon fetch of TX hex + ring members
|
|
91
|
+
* 2. parse TX → extract C[ring_slot]
|
|
92
|
+
* 3. compute blinder = C[slot] − amount × G
|
|
93
|
+
* 4. self-check: amount × G + blinder == C[slot]
|
|
94
|
+
* 5. encode forged proof string + assemble narrative + citations
|
|
95
|
+
*/
|
|
96
|
+
export async function forgeDemoProof(rpc, input) {
|
|
97
|
+
if (input.tx_hash && input.tx_hex) {
|
|
98
|
+
throw new Error('INVALID_INPUT: provide exactly one of tx_hash or tx_hex (not both)');
|
|
99
|
+
}
|
|
100
|
+
if (!input.tx_hash && !input.tx_hex) {
|
|
101
|
+
throw new Error('INVALID_INPUT: provide either tx_hash or tx_hex');
|
|
102
|
+
}
|
|
103
|
+
const ringSlot = input.ring_slot ?? 0;
|
|
104
|
+
const amountDero = input.amount_dero ?? '-1';
|
|
105
|
+
// ─── 1. Fetch TX (optional daemon round-trip) ─────────────────────────────
|
|
106
|
+
let txHex;
|
|
107
|
+
let receiverAddress = null;
|
|
108
|
+
const txSource = input.tx_hash ? 'tx_hash' : 'tx_hex';
|
|
109
|
+
const steps = [];
|
|
110
|
+
if (input.tx_hash) {
|
|
111
|
+
const hash = input.tx_hash;
|
|
112
|
+
steps.push({
|
|
113
|
+
name: 'get_transaction',
|
|
114
|
+
required: true,
|
|
115
|
+
fn: () => rpc('DERO.GetTransaction', {
|
|
116
|
+
txs_hashes: [hash],
|
|
117
|
+
decode_as_json: 1,
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const chain = await runChain(steps);
|
|
122
|
+
if (input.tx_hash) {
|
|
123
|
+
const txResult = stepValue(chain, 'get_transaction');
|
|
124
|
+
if (!txResult || !txResult.txs_as_hex || txResult.txs_as_hex.length === 0) {
|
|
125
|
+
throw new Error(`daemon did not return TX hex for ${input.tx_hash} (chain halted at ${chain.haltedAt ?? '?'})`);
|
|
126
|
+
}
|
|
127
|
+
txHex = txResult.txs_as_hex[0];
|
|
128
|
+
const ring = txResult.txs?.[0]?.ring?.[0];
|
|
129
|
+
if (ring && ringSlot >= 0 && ringSlot < ring.length) {
|
|
130
|
+
receiverAddress = ring[ringSlot];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
txHex = input.tx_hex;
|
|
135
|
+
}
|
|
136
|
+
// ─── 2. Parse TX → extract C[ring_slot] ──────────────────────────────────
|
|
137
|
+
let tx;
|
|
138
|
+
try {
|
|
139
|
+
tx = parseTransaction(txHex);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
throw new Error(`tx-parse failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
143
|
+
}
|
|
144
|
+
const stmt = tx.payloads[0].statement;
|
|
145
|
+
if (ringSlot >= stmt.ring_size) {
|
|
146
|
+
throw new Error(`INVALID_INPUT: ring_slot ${ringSlot} >= ring_size ${stmt.ring_size}`);
|
|
147
|
+
}
|
|
148
|
+
const Cslot = getRingCommitment(tx, ringSlot);
|
|
149
|
+
// ─── 3. Forge math ────────────────────────────────────────────────────────
|
|
150
|
+
const { signedAtoms, uint64, signedDeroNormalized } = parseAmount(amountDero);
|
|
151
|
+
const amountG = scalarMult(G, uint64ToSignedScalar(uint64));
|
|
152
|
+
const blinder = add(Cslot, negate(amountG));
|
|
153
|
+
// ─── 4. Self-check (mirror of proof.Prove() inner loop) ──────────────────
|
|
154
|
+
const reconstructed = add(amountG, blinder);
|
|
155
|
+
const verified = pointsEqual(reconstructed, Cslot);
|
|
156
|
+
if (!verified) {
|
|
157
|
+
// Should be impossible by construction — bail loud rather than emit a string
|
|
158
|
+
// that would fail an explorer check.
|
|
159
|
+
throw new Error('self-check FAILED: amount × G + blinder != C[slot] — bn254 math regression');
|
|
160
|
+
}
|
|
161
|
+
// ─── 5. Encode forged proof string ────────────────────────────────────────
|
|
162
|
+
const blinderHex = deroCompressHex(blinder);
|
|
163
|
+
const blinderBytes = new Uint8Array(33);
|
|
164
|
+
for (let i = 0; i < 33; i++)
|
|
165
|
+
blinderBytes[i] = parseInt(blinderHex.slice(i * 2, i * 2 + 2), 16);
|
|
166
|
+
const forgedString = encodeForgeProofString(blinderBytes, uint64);
|
|
167
|
+
// ─── 6. Citations + context_note enrichment ───────────────────────────────
|
|
168
|
+
const enrichment = enrichWithFlaggedArtifacts({
|
|
169
|
+
...(input.tx_hash ? { tx_hash: input.tx_hash } : {}),
|
|
170
|
+
}, relatedDocsFor('dero_forge_demo_proof'));
|
|
171
|
+
const related_docs = enrichment?.related_docs ?? relatedDocsFor('dero_forge_demo_proof') ?? [];
|
|
172
|
+
const context_note = enrichment?.context_note ??
|
|
173
|
+
'This is a forged display object built locally from public chain data — no wallet, no keys, no broadcast. On unpatched explorers it shows Verified ✓ for the chosen amount; on the chain, nothing changed. See related_docs for the technical rebuttal.';
|
|
174
|
+
return {
|
|
175
|
+
forged_proof_string: forgedString,
|
|
176
|
+
target_amount: {
|
|
177
|
+
dero: signedDeroNormalized,
|
|
178
|
+
atoms_signed: signedAtoms.toString(),
|
|
179
|
+
atoms_uint64: uint64.toString(),
|
|
180
|
+
},
|
|
181
|
+
ring_slot: ringSlot,
|
|
182
|
+
ring_size: stmt.ring_size,
|
|
183
|
+
ring_receiver_address: receiverAddress,
|
|
184
|
+
math: {
|
|
185
|
+
C_slot_hex: stmt.C_hex[ringSlot],
|
|
186
|
+
amount_x_G_hex: deroCompressHex(amountG),
|
|
187
|
+
blinder_hex: blinderHex,
|
|
188
|
+
},
|
|
189
|
+
self_check: {
|
|
190
|
+
verified: true,
|
|
191
|
+
method: 'Reconstructed amount × G + blinder and confirmed equal to C[slot] — same equation proof.Prove() uses (proof/proof.go:88-95).',
|
|
192
|
+
},
|
|
193
|
+
explorer_display_amount: formatExplorerDisplay(uint64),
|
|
194
|
+
context_note,
|
|
195
|
+
related_docs,
|
|
196
|
+
_diagnostics: {
|
|
197
|
+
step_latencies: stepLatencies(chain),
|
|
198
|
+
halted_at: chain.haltedAt,
|
|
199
|
+
total_ms: chain.totalMs,
|
|
200
|
+
tx_source: txSource,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=forge-demo-proof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forge-demo-proof.js","sourceRoot":"","sources":["../../src/composites/forge-demo-proof.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `recommend_docs_path` — Phase C composite #3.
|
|
3
|
+
*
|
|
4
|
+
* Docs-only composite. Takes a natural-language intent, runs scoped
|
|
5
|
+
* searches across ALL four products in parallel, boosts
|
|
6
|
+
* hint-matching scores by 1.5× (when `product_hint` is provided),
|
|
7
|
+
* groups results by product, and returns a ranked path with
|
|
8
|
+
* per-result rationale and ready-to-cite citations.
|
|
9
|
+
*
|
|
10
|
+
* Wedge: agents currently have to guess which docs product to search
|
|
11
|
+
* (derod for daemon RPC, tela for on-chain apps, hologram for the
|
|
12
|
+
* simulator, deropay for the merchant flow). This composite removes
|
|
13
|
+
* that guess: one call, one ranked answer, with citations the agent
|
|
14
|
+
* can drop straight into a response.
|
|
15
|
+
*
|
|
16
|
+
* Design contract § 3. Sequencing rule: SHIP
|
|
17
|
+
* THIRD. Pure docs composition — no chain reads. Proves the
|
|
18
|
+
* "docs-only composite" pattern that future docs-heavy tools can
|
|
19
|
+
* reuse.
|
|
20
|
+
*
|
|
21
|
+
* Design clarification: the spec text "for each product (or just
|
|
22
|
+
* `product_hint` if provided)" reads like a filter, but the spec's
|
|
23
|
+
* scoring rule ("score by `score * productHintBoost` for hint
|
|
24
|
+
* matches") only makes sense if all four products are always
|
|
25
|
+
* searched and the hint is treated as a BIAS, not a FILTER. Treating
|
|
26
|
+
* the hint as a filter would make the boost a uniform no-op. This
|
|
27
|
+
* implementation searches all four products on every call so the
|
|
28
|
+
* hint scoring carries real weight and the agent still sees relevant
|
|
29
|
+
* cross-product docs (e.g. derod RPC pages that touch TELA).
|
|
30
|
+
*
|
|
31
|
+
* Failure model:
|
|
32
|
+
* - Zero matches across every product → throw
|
|
33
|
+
* `'No DERO docs matched intent: "..."'`. The `withStructuredErrors`
|
|
34
|
+
* wrapper classifies this as `NO_DOCS_MATCH` and emits a hint
|
|
35
|
+
* telling the agent to rephrase (or change the `product_hint`).
|
|
36
|
+
* - The bundled docs index missing → propagates the existing
|
|
37
|
+
* `DOCS_UNAVAILABLE` classification from `searchDeroDocs`.
|
|
38
|
+
*/
|
|
39
|
+
import { z } from 'zod';
|
|
40
|
+
import { type DeroDocProduct } from '../docs.js';
|
|
41
|
+
import { type DeroCitation } from '../citations.js';
|
|
42
|
+
export declare const recommendDocsPathInputSchema: {
|
|
43
|
+
readonly intent: z.ZodString;
|
|
44
|
+
readonly product_hint: z.ZodOptional<z.ZodEnum<["derod", "tela", "hologram", "deropay"]>>;
|
|
45
|
+
readonly limit_per_product: z.ZodOptional<z.ZodNumber>;
|
|
46
|
+
};
|
|
47
|
+
type RecommendInput = {
|
|
48
|
+
intent: string;
|
|
49
|
+
product_hint?: DeroDocProduct;
|
|
50
|
+
limit_per_product?: number;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Per-result shape returned by `searchDeroDocs`. Re-declared here as the
|
|
54
|
+
* minimum surface the composite touches so this module stays decoupled
|
|
55
|
+
* from internal changes to the docs search response.
|
|
56
|
+
*/
|
|
57
|
+
type SearchHit = {
|
|
58
|
+
product: DeroDocProduct;
|
|
59
|
+
slug: string;
|
|
60
|
+
title: string;
|
|
61
|
+
description?: string;
|
|
62
|
+
canonical_url: string;
|
|
63
|
+
headings: string[];
|
|
64
|
+
excerpt: string;
|
|
65
|
+
score: number;
|
|
66
|
+
};
|
|
67
|
+
type Recommendation = {
|
|
68
|
+
product: DeroDocProduct;
|
|
69
|
+
slug: string;
|
|
70
|
+
title: string;
|
|
71
|
+
canonical_url: string;
|
|
72
|
+
score: number;
|
|
73
|
+
boosted_score: number;
|
|
74
|
+
rationale: string;
|
|
75
|
+
};
|
|
76
|
+
type ByProductSummary = Record<DeroDocProduct, {
|
|
77
|
+
count: number;
|
|
78
|
+
top_slug: string | null;
|
|
79
|
+
top_score: number | null;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Pure helper: turn raw per-product hits into a ranked, deduplicated
|
|
83
|
+
* list with boosted scores and rationale strings. Exported so flow
|
|
84
|
+
* tests / future unit tests can call it without spinning up an MCP
|
|
85
|
+
* client.
|
|
86
|
+
*/
|
|
87
|
+
export declare function rankRecommendations(intent: string, productHint: DeroDocProduct | undefined, hitsByProduct: ReadonlyMap<DeroDocProduct, readonly SearchHit[]>): Recommendation[];
|
|
88
|
+
export declare function recommendDocsPath(args: RecommendInput): Promise<{
|
|
89
|
+
intent: string;
|
|
90
|
+
product_hint: "derod" | "tela" | "hologram" | "deropay" | null;
|
|
91
|
+
limit_per_product: number;
|
|
92
|
+
recommended: Recommendation[];
|
|
93
|
+
by_product: ByProductSummary;
|
|
94
|
+
related_docs: DeroCitation[];
|
|
95
|
+
}>;
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=recommend-docs-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommend-docs-path.d.ts","sourceRoot":"","sources":["../../src/composites/recommend-docs-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAItE,eAAO,MAAM,4BAA4B;;;;CAkB/B,CAAA;AAEV,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,cAAc,CAAA;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;;GAIG;AACH,KAAK,SAAS,GAAG;IACf,OAAO,EAAE,cAAc,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,cAAc,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,KAAK,gBAAgB,GAAG,MAAM,CAC5B,cAAc,EACd;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CACrE,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,cAAc,GAAG,SAAS,EACvC,aAAa,EAAE,WAAW,CAAC,cAAc,EAAE,SAAS,SAAS,EAAE,CAAC,GAC/D,cAAc,EAAE,CA2BlB;AA4CD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,cAAc;;;;;;;GAsC3D"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `recommend_docs_path` — Phase C composite #3.
|
|
3
|
+
*
|
|
4
|
+
* Docs-only composite. Takes a natural-language intent, runs scoped
|
|
5
|
+
* searches across ALL four products in parallel, boosts
|
|
6
|
+
* hint-matching scores by 1.5× (when `product_hint` is provided),
|
|
7
|
+
* groups results by product, and returns a ranked path with
|
|
8
|
+
* per-result rationale and ready-to-cite citations.
|
|
9
|
+
*
|
|
10
|
+
* Wedge: agents currently have to guess which docs product to search
|
|
11
|
+
* (derod for daemon RPC, tela for on-chain apps, hologram for the
|
|
12
|
+
* simulator, deropay for the merchant flow). This composite removes
|
|
13
|
+
* that guess: one call, one ranked answer, with citations the agent
|
|
14
|
+
* can drop straight into a response.
|
|
15
|
+
*
|
|
16
|
+
* Design contract § 3. Sequencing rule: SHIP
|
|
17
|
+
* THIRD. Pure docs composition — no chain reads. Proves the
|
|
18
|
+
* "docs-only composite" pattern that future docs-heavy tools can
|
|
19
|
+
* reuse.
|
|
20
|
+
*
|
|
21
|
+
* Design clarification: the spec text "for each product (or just
|
|
22
|
+
* `product_hint` if provided)" reads like a filter, but the spec's
|
|
23
|
+
* scoring rule ("score by `score * productHintBoost` for hint
|
|
24
|
+
* matches") only makes sense if all four products are always
|
|
25
|
+
* searched and the hint is treated as a BIAS, not a FILTER. Treating
|
|
26
|
+
* the hint as a filter would make the boost a uniform no-op. This
|
|
27
|
+
* implementation searches all four products on every call so the
|
|
28
|
+
* hint scoring carries real weight and the agent still sees relevant
|
|
29
|
+
* cross-product docs (e.g. derod RPC pages that touch TELA).
|
|
30
|
+
*
|
|
31
|
+
* Failure model:
|
|
32
|
+
* - Zero matches across every product → throw
|
|
33
|
+
* `'No DERO docs matched intent: "..."'`. The `withStructuredErrors`
|
|
34
|
+
* wrapper classifies this as `NO_DOCS_MATCH` and emits a hint
|
|
35
|
+
* telling the agent to rephrase (or change the `product_hint`).
|
|
36
|
+
* - The bundled docs index missing → propagates the existing
|
|
37
|
+
* `DOCS_UNAVAILABLE` classification from `searchDeroDocs`.
|
|
38
|
+
*/
|
|
39
|
+
import { z } from 'zod';
|
|
40
|
+
import { DERO_DOC_PRODUCTS, searchDeroDocs, } from '../docs.js';
|
|
41
|
+
import { buildDeroCitation } from '../citations.js';
|
|
42
|
+
const PRODUCT_HINT_BOOST = 1.5;
|
|
43
|
+
export const recommendDocsPathInputSchema = {
|
|
44
|
+
intent: z
|
|
45
|
+
.string()
|
|
46
|
+
.min(8)
|
|
47
|
+
.describe('Natural-language description of what the user wants to do (e.g. "deploy a TELA app", "trace a transaction by hash", "verify a webhook signature").'),
|
|
48
|
+
product_hint: z
|
|
49
|
+
.enum(DERO_DOC_PRODUCTS)
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Optional bias toward one product (derod | tela | hologram | deropay) when known.'),
|
|
52
|
+
limit_per_product: z
|
|
53
|
+
.number()
|
|
54
|
+
.int()
|
|
55
|
+
.min(1)
|
|
56
|
+
.max(5)
|
|
57
|
+
.optional()
|
|
58
|
+
.describe('Cap per-product search results before merging. Default 2.'),
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Pure helper: turn raw per-product hits into a ranked, deduplicated
|
|
62
|
+
* list with boosted scores and rationale strings. Exported so flow
|
|
63
|
+
* tests / future unit tests can call it without spinning up an MCP
|
|
64
|
+
* client.
|
|
65
|
+
*/
|
|
66
|
+
export function rankRecommendations(intent, productHint, hitsByProduct) {
|
|
67
|
+
const out = [];
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
for (const [product, hits] of hitsByProduct) {
|
|
70
|
+
for (const hit of hits) {
|
|
71
|
+
const key = `${product}::${hit.slug}`;
|
|
72
|
+
if (seen.has(key))
|
|
73
|
+
continue;
|
|
74
|
+
seen.add(key);
|
|
75
|
+
const boost = product === productHint ? PRODUCT_HINT_BOOST : 1;
|
|
76
|
+
const boostedScore = Math.round(hit.score * boost * 100) / 100;
|
|
77
|
+
const topHeading = hit.headings[0];
|
|
78
|
+
const rationale = buildRationale(intent, product, hit.score, boost, topHeading);
|
|
79
|
+
out.push({
|
|
80
|
+
product,
|
|
81
|
+
slug: hit.slug,
|
|
82
|
+
title: hit.title,
|
|
83
|
+
canonical_url: hit.canonical_url,
|
|
84
|
+
score: hit.score,
|
|
85
|
+
boosted_score: boostedScore,
|
|
86
|
+
rationale,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
out.sort((a, b) => b.boosted_score - a.boosted_score);
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function buildRationale(intent, product, rawScore, boost, topHeading) {
|
|
94
|
+
const boostNote = boost > 1 ? ` (×${boost} product_hint boost applied)` : '';
|
|
95
|
+
const headingPart = topHeading ? ` Top heading: "${topHeading.slice(0, 80)}".` : '';
|
|
96
|
+
return `Match for "${intent.slice(0, 60)}" under product=${product} (raw score ${rawScore}${boostNote}).${headingPart}`;
|
|
97
|
+
}
|
|
98
|
+
function summarizeByProduct(recs) {
|
|
99
|
+
const out = Object.create(null);
|
|
100
|
+
for (const product of DERO_DOC_PRODUCTS) {
|
|
101
|
+
out[product] = { count: 0, top_slug: null, top_score: null };
|
|
102
|
+
}
|
|
103
|
+
for (const rec of recs) {
|
|
104
|
+
const bucket = out[rec.product];
|
|
105
|
+
bucket.count += 1;
|
|
106
|
+
if (bucket.top_score === null || rec.boosted_score > bucket.top_score) {
|
|
107
|
+
bucket.top_score = rec.boosted_score;
|
|
108
|
+
bucket.top_slug = rec.slug;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build top-N citations from the ranked recommendations. Always
|
|
115
|
+
* deduplicated by `product::slug` (which `rankRecommendations` also
|
|
116
|
+
* enforces) so the citation list never lists the same page twice.
|
|
117
|
+
*/
|
|
118
|
+
function citationsFromRecommendations(recs, topN) {
|
|
119
|
+
return recs
|
|
120
|
+
.slice(0, topN)
|
|
121
|
+
.map((rec) => buildDeroCitation(rec.product, rec.slug, rec.title));
|
|
122
|
+
}
|
|
123
|
+
export async function recommendDocsPath(args) {
|
|
124
|
+
const intent = args.intent.trim();
|
|
125
|
+
const limitPerProduct = args.limit_per_product ?? 2;
|
|
126
|
+
const productHint = args.product_hint;
|
|
127
|
+
// Always fan out to all four products. `searchDeroDocs` shares an
|
|
128
|
+
// in-process page cache (15s TTL) so the second+ calls in the batch
|
|
129
|
+
// resolve from memory; the network/disk cost is paid once per cache
|
|
130
|
+
// window. See the module header for why we don't filter to
|
|
131
|
+
// product_hint.
|
|
132
|
+
const searches = await Promise.all(DERO_DOC_PRODUCTS.map((product) => searchDeroDocs({ query: intent, product, limit: limitPerProduct }).then((res) => [product, res.results])));
|
|
133
|
+
const hitsByProduct = new Map(searches);
|
|
134
|
+
const recommended = rankRecommendations(intent, productHint, hitsByProduct);
|
|
135
|
+
if (recommended.length === 0) {
|
|
136
|
+
throw new Error(`No DERO docs matched intent: "${intent}" across products: ${DERO_DOC_PRODUCTS.join(', ')}. Try rephrasing with product-specific nouns (e.g. "TELA INDEX-1 contract", "DeroPay webhook").`);
|
|
137
|
+
}
|
|
138
|
+
const by_product = summarizeByProduct(recommended);
|
|
139
|
+
const related_docs = citationsFromRecommendations(recommended, 2);
|
|
140
|
+
return {
|
|
141
|
+
intent,
|
|
142
|
+
product_hint: productHint ?? null,
|
|
143
|
+
limit_per_product: limitPerProduct,
|
|
144
|
+
recommended,
|
|
145
|
+
by_product,
|
|
146
|
+
related_docs,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=recommend-docs-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommend-docs-path.js","sourceRoot":"","sources":["../../src/composites/recommend-docs-path.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `trace_transaction_with_context` — Phase C composite #5 (final).
|
|
3
|
+
*
|
|
4
|
+
* Wedge: `dero_get_transaction` returns the raw tx record (block,
|
|
5
|
+
* ring, signer, code, balances) but leaves the agent to figure out
|
|
6
|
+
* confirmation state, classify the tx kind, and separately fetch
|
|
7
|
+
* any SC context. This composite combines all of that in one call
|
|
8
|
+
* and stitches the right docs page as a citation.
|
|
9
|
+
*
|
|
10
|
+
* Design contract § 5. Sequencing rule: SHIP
|
|
11
|
+
* LAST — highest fan-out + failure-mode count of all Phase C
|
|
12
|
+
* composites.
|
|
13
|
+
*
|
|
14
|
+
* Implementation notes (recorded honestly so the design doc and
|
|
15
|
+
* shipped surface stay in sync):
|
|
16
|
+
*
|
|
17
|
+
* - SC INSTALL detection works directly off the tx record: when
|
|
18
|
+
* `tx.code` is non-empty the tx_hash itself IS the resulting
|
|
19
|
+
* SCID and the embedded code is the deployed source. We run
|
|
20
|
+
* `extractScSurface` on `tx.code` directly — no second
|
|
21
|
+
* `DERO.GetSC` call is needed for installs.
|
|
22
|
+
* - SC INVOCATION arg/entrypoint decoding requires walking the
|
|
23
|
+
* binary tx blob (`txs_as_hex`) with DERO's tx codec. That
|
|
24
|
+
* codec is not yet bundled in this MCP, so the composite does
|
|
25
|
+
* NOT fabricate decoded args. It surfaces `raw_tx_hex_length`
|
|
26
|
+
* so the agent knows the binary is available via
|
|
27
|
+
* `dero_get_transaction` if a downstream wallet wants to
|
|
28
|
+
* decode it.
|
|
29
|
+
* - TX_NOT_FOUND is signalled by the daemon as an EMPTY record
|
|
30
|
+
* (`block_height: 0, in_pool: false, code: ''`) — it does
|
|
31
|
+
* not throw. The composite detects this and throws a
|
|
32
|
+
* classifier-friendly message so `withStructuredErrors`
|
|
33
|
+
* surfaces `_meta.error.code = TX_NOT_FOUND`.
|
|
34
|
+
*
|
|
35
|
+
* Failure model:
|
|
36
|
+
* - `DERO.GetTransaction` throws → propagates via
|
|
37
|
+
* `withStructuredErrors` (RPC_UNREACHABLE / RPC_INVALID_PARAMS
|
|
38
|
+
* classifier branches handle the daemon-side cases).
|
|
39
|
+
* - Daemon returns empty record for the hash → composite throws
|
|
40
|
+
* `'DERO transaction not found: ...'` → classifier returns
|
|
41
|
+
* `TX_NOT_FOUND` with a retry hint.
|
|
42
|
+
* - Tx found but SC surface extraction degraded for the install
|
|
43
|
+
* case → record the surface as-is (`has_code: false`,
|
|
44
|
+
* `functions: []`) and let the narrative explain. Never abort.
|
|
45
|
+
*/
|
|
46
|
+
import { z } from 'zod';
|
|
47
|
+
import { type DeroDaemonRpc } from './_shared.js';
|
|
48
|
+
export declare const traceTransactionWithContextInputSchema: {
|
|
49
|
+
readonly tx_hash: z.ZodString;
|
|
50
|
+
readonly decode: z.ZodOptional<z.ZodBoolean>;
|
|
51
|
+
readonly include_sc_context: z.ZodOptional<z.ZodBoolean>;
|
|
52
|
+
};
|
|
53
|
+
type TraceInput = {
|
|
54
|
+
tx_hash: string;
|
|
55
|
+
decode?: boolean;
|
|
56
|
+
include_sc_context?: boolean;
|
|
57
|
+
};
|
|
58
|
+
type ConfirmationStatus = 'confirmed' | 'mempool' | 'unknown';
|
|
59
|
+
type TxKind = 'sc_install' | 'transfer_or_invocation' | 'coinbase' | 'unknown';
|
|
60
|
+
export declare function traceTransactionWithContext(rpc: DeroDaemonRpc, args: TraceInput): Promise<{
|
|
61
|
+
tx_hash: string;
|
|
62
|
+
confirmation: {
|
|
63
|
+
status: ConfirmationStatus;
|
|
64
|
+
block_height: number | null;
|
|
65
|
+
valid_block: string | null;
|
|
66
|
+
invalid_blocks: string[];
|
|
67
|
+
in_pool: boolean;
|
|
68
|
+
};
|
|
69
|
+
kind: TxKind;
|
|
70
|
+
ring: {
|
|
71
|
+
groups: number | null;
|
|
72
|
+
first_group_size: number | null;
|
|
73
|
+
};
|
|
74
|
+
reward: number | null;
|
|
75
|
+
signer_visible: boolean;
|
|
76
|
+
native_balance: {
|
|
77
|
+
scid: string;
|
|
78
|
+
at_tx: number | null;
|
|
79
|
+
current: number | null;
|
|
80
|
+
};
|
|
81
|
+
sc_install: {
|
|
82
|
+
scid: string;
|
|
83
|
+
surface: {
|
|
84
|
+
functions: import("./_shared.js").DvmFunctionSignature[];
|
|
85
|
+
stringkeys: string[];
|
|
86
|
+
uint64keys: string[];
|
|
87
|
+
balances: Record<string, string | number>;
|
|
88
|
+
};
|
|
89
|
+
raw_code_length: number;
|
|
90
|
+
has_code: boolean;
|
|
91
|
+
} | null;
|
|
92
|
+
raw_tx_hex_length: number;
|
|
93
|
+
narrative: string;
|
|
94
|
+
_diagnostics: {
|
|
95
|
+
step_latency_ms: Record<string, number>;
|
|
96
|
+
total_ms: number;
|
|
97
|
+
halted_at: string | null;
|
|
98
|
+
decode_as_json: boolean;
|
|
99
|
+
include_sc_context: boolean;
|
|
100
|
+
sc_install_surface_attempted: boolean;
|
|
101
|
+
sc_install_surface_failed: boolean;
|
|
102
|
+
};
|
|
103
|
+
} & {
|
|
104
|
+
related_docs?: import("../citations.js").DeroCitation[];
|
|
105
|
+
}>;
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=trace-transaction-with-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-transaction-with-context.d.ts","sourceRoot":"","sources":["../../src/composites/trace-transaction-with-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAOL,KAAK,aAAa,EAEnB,MAAM,cAAc,CAAA;AAKrB,eAAO,MAAM,sCAAsC;;;;CAezC,CAAA;AAEV,KAAK,UAAU,GAAG;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,CAAA;AA6BD,KAAK,kBAAkB,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAA;AAC7D,KAAK,MAAM,GAAG,YAAY,GAAG,wBAAwB,GAAG,UAAU,GAAG,SAAS,CAAA;AAgF9E,wBAAsB,2BAA2B,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GrF"}
|