Files
AtreidesAssetsFree/server/mcp/detail-tool.mjs
T

179 lines
7.4 KiB
JavaScript
Raw Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:c8d6a3abd48730edcf0689606e46d90e911be6fb20a947fc1829093ad98e9800
import { listAccounts } from '../data/accounts.js';
import { listAssets } from '../data/assets.js';
import { listLabels } from '../data/labels.js';
import { accountStructure, assetStructure, labelStructure, eventRefs } from '../data/structure.js';
import { rowOrigin } from '../data/ai-proposals.js';
import { assertScope } from '../ai/gate.js';
import { limitedList } from '../ai/limits.js';
import { vertalerVoor } from './session.mjs';
import { HANDLE_VORM, idVoorHandle } from './handles.mjs';
function assetVoorSymbool(gevraagd) {
const gezocht = String(gevraagd ?? '').trim().toUpperCase();
if (!gezocht || gezocht.includes(':')) return null;
return listAssets().find(a => String(a.symbol).toUpperCase() === gezocht) || null;
}
const SOORTEN = Object.freeze({
account: Object.freeze({ ids: () => listAccounts().map(r => r.id), structuur: accountStructure }),
asset: Object.freeze({ ids: () => listAssets().map(r => r.id), structuur: assetStructure }),
label: Object.freeze({ ids: () => listLabels().map(r => r.id), structuur: labelStructure }),
});
const SOORT_NAMEN = Object.keys(SOORTEN);
const telRegel = (aantal, enkelvoud, meervoud) =>
`${aantal} ${aantal === 1 ? enkelvoud : meervoud}`;
const TELLING_TEKST = Object.freeze({
events: n => telRegel(n, 'gebeurtenis', 'gebeurtenissen'),
assets: n => telRegel(n, 'asset waarin geboekt is', 'assets waarin geboekt is'),
accounts: n => telRegel(n, 'account waarop hij voorkomt', 'accounts waarop hij voorkomt'),
walletSources: n => telRegel(n, 'wallet-bron', 'wallet-bronnen'),
addresses: n => telRegel(n, 'adres', 'adressen'),
walletTransactions: n => telRegel(n, 'on-chain beweging', 'on-chain bewegingen'),
valuations: n => telRegel(n, 'balans', 'balansen'),
pricePoints: n => telRegel(n, 'koerspunt', 'koerspunten'),
products: n => telRegel(n, 'metaalproduct', 'metaalproducten'),
coverage: n => telRegel(n, 'koersbron die dit symbool zegt te dekken', 'koersbronnen die dit symbool zeggen te dekken'),
});
function rijRegels(veilig) {
return Object.entries(veilig)
.filter(([naam]) => naam !== 'id')
.map(([naam, waarde]) => ` ${naam} = ${waarde === null ? '(leeg)' : waarde}`);
}
const recordDetail = Object.freeze({
name: 'record_detail',
title: 'Eén rij uitdiepen',
description:
'Geeft van één rij uit de administratie van AssetApp terug waar hij aan vasthangt: zijn eigen '
+ 'kenmerken en hoeveel gebeurtenissen, adressen, balansen en koerspunten eraan hangen. Bedoeld als '
+ 'vervolgvraag op een verwijzing die admin_overview of data_health heeft gegeven. Werkt op een '
+ 'account (account:...) of een label (label:...), ook als die verwijzing uit een eerder gesprek '
+ 'komt, en op een asset bij zijn SYMBOOL (BTC). Bij die twee ook de gebeurtenissen met hun '
+ 'verwijzing, soort en maand. Alleen kenmerken, aantallen en verwijzingen; geen bedragen, geen '
+ 'exacte datums en geen adressen, en wijzigt niets.',
inputSchema: Object.freeze({
type: 'object',
properties: {
handle: {
type: 'string',
maxLength: 64,
description:
'De verwijzing zoals een andere tool hem gaf (account:7f3a9c, label:9e8f7a), of het symbool '
+ 'van een asset (BTC).',
},
},
required: ['handle'],
additionalProperties: false,
}),
scope: 'read',
async run(args, context) {
assertScope(recordDetail.scope);
const t = await vertalerVoor(context);
t.beginCall();
const gevraagd = typeof args?.handle === 'string' ? args.handle.trim() : '';
const symbool = assetVoorSymbool(gevraagd);
if (symbool) return detailVan('asset', symbool.id, gevraagd, t);
const vorm = HANDLE_VORM.exec(gevraagd);
if (!vorm) {
return {
text: `'${gevraagd}' is geen verwijzing zoals deze app ze uitdeelt en geen asset-symbool. `
+ `Verwijzingen zien eruit als account:7f3a9c: een soort, een dubbele punt en zes tekens. `
+ `Staat onbewerkt delen aan, dan is het de soort met het rij-id erachter, zoals account:3. `
+ `Een asset wijs je aan met zijn symbool, zoals BTC.`,
fieldsShared: [],
};
}
const soort = vorm[1];
if (!Object.hasOwn(SOORTEN, soort)) {
return {
text: `Een verwijzing van de soort '${soort}' kan deze tool niet uitdiepen. Dat kan voor: `
+ `${SOORT_NAMEN.join(', ')}.`,
fieldsShared: [],
};
}
const kandidaten = SOORTEN[soort].ids();
const id = idVoorHandle(t, soort, gevraagd, kandidaten) ?? idVoorHandle(t, soort, vorm[2], kandidaten);
if (id === null) {
return {
text: `De verwijzing ${gevraagd} hoort bij geen enkele ${soort} in deze administratie. `
+ `Mogelijk is de rij verwijderd.`,
fieldsShared: [],
};
}
return detailVan(soort, id, gevraagd, t);
},
});
function detailVan(soort, id, gevraagd, t) {
{
const structuur = SOORTEN[soort].structuur(id);
if (!structuur) {
return { text: `De ${soort} achter ${gevraagd} bestaat niet meer.`, fieldsShared: [] };
}
const veilig = t.toAi(structuur.row, { entity: soort });
const tellingen = limitedList(
Object.entries(structuur.counts),
([naam, n]) => ` ${TELLING_TEKST[naam] ? TELLING_TEKST[naam](n) : `${naam} = ${n}`}`,
).lines;
const verwijzingen = Object.entries(structuur.refs)
.filter(([, ids]) => Array.isArray(ids) && ids.length)
.map(([naam, ids]) => {
const soortVanRef = naam === 'assetIds' ? 'asset' : 'account';
const { lines } = limitedList(ids, refId => ` ${t.toAi({ id: refId }, { entity: soortVanRef }).id}`);
return [`${naam === 'assetIds' ? 'Assets' : 'Accounts'}:`, ...lines].join('\n');
});
const gebeurtenissen = (soort === 'account' || soort === 'asset')
? limitedList(
eventRefs(soort === 'account' ? { accountId: id } : { assetId: id }),
(e) => {
const uit = rowOrigin('event', e.id);
const bron = uit ? ` · uit ${t.toAi({ id: uit.proposalId }, { entity: 'proposal' }).id}` : '';
return ` ${t.toAi({ id: e.id }, { entity: 'event' }).id} · ${e.type} · ${e.month}${bron}`;
},
).lines
: [];
const herkomst = soort === 'account' ? rowOrigin('account', id) : null;
const herkomstRegels = herkomst
? ['', `Ontstaan uit ${t.toAi({ id: herkomst.proposalId }, { entity: 'proposal' }).id}, `
+ `goedgekeurd op ${String(herkomst.resolvedAt || '').slice(0, 10)}.`]
: [];
return {
text: [
`${soort} ${gevraagd}:`,
...rijRegels(veilig),
'',
'Hangt eraan:',
...tellingen,
...herkomstRegels,
...(gebeurtenissen.length ? ['', 'Gebeurtenissen (nieuwste eerst, maand):', ...gebeurtenissen] : []),
...(verwijzingen.length ? ['', ...verwijzingen] : []),
].join('\n'),
fieldsShared: [...new Set([
...t.fieldsShared(),
'assetId', 'fromAccountId', 'toAccountId', 'accountId', 'labelId', 'providerId',
...(gebeurtenissen.length ? ['type', 'occurredAt'] : []),
...(gebeurtenissen.some(r => r.includes(' · uit ')) ? ['createdEventId'] : []),
...(herkomst ? ['createdAccountId'] : []),
])].sort(),
};
}
}
export const DETAIL_TOOLS = Object.freeze([recordDetail]);