/**
* Intelligence — Anthropic tool surface for the methodology library.
*
* Every Intelligence agent exposes the same two tools to its LLM:
*
* find_methodologies({type, domain, agent, triggers})
* → search the library for active methodologies that match. Used
* at the start of any analytical operation (Std 5 — declared
* methods only).
*
* get_methodology({methodology_id})
* → fetch the full content of one entry. Used after the LLM
* picks a candidate so it can apply the formula / rule / framework.
*
* The library is loaded lazily on first call and cached for the
* process lifetime. Failures are returned as structured
* MethodologyToolResult objects so the agent's tool-use loop never
* sees a raw exception (Std 12).
*/
import {
findMethodologies,
getMethodology,
type Methodology,
type MethodologyAgent,
type MethodologyType,
} from './library.js';
export interface MethodologyToolDescriptor {
readonly name: string;
readonly description: string;
readonly input_schema: {
readonly type: 'object';
readonly properties: Record<string, { type: string; description: string }>;
readonly required: readonly string[];
};
}
export const METHODOLOGY_TOOLS: readonly MethodologyToolDescriptor[] = [
{
name: 'find_methodologies',
description:
"Search the SME methodology library for active entries that match the current analytical " +
"operation. Filter by `type` (metric_definition | comparison_method | insight_framework | " +
"normalization_rule), `domain` (banking | wealth_management | insurance | all), `agent` " +
"(analytical_table | performance_metrics | comparisons_synthesis | insight_synthesis), and " +
"free-text `triggers`. Returns lightweight {methodology_id, name, type, domain, triggers, " +
"sourceFile} entries; call get_methodology for the full definition.",
input_schema: {
type: 'object',
properties: {
type: {
type: 'string',
description: 'Filter by methodology type.',
},
domain: {
type: 'string',
description: 'Filter by domain (banking, wealth_management, insurance, all, …). Entries with domain="all" always match.',
},
agent: {
type: 'string',
description: 'Filter by intended agent.',
},
triggers: {
type: 'string',
description: 'Optional comma-separated free-text trigger terms (e.g. "growth, trend"). Each term must appear in the entry\'s triggers list (case-insensitive substring).',
},
},
required: [],
},
},
{
name: 'get_methodology',
description:
'Fetch the full content of one methodology by its methodology_id. Returns the formula / ' +
'comparison method / insight framework, the inputs/outputs schema, the comparability rules, ' +
'and the rationale.',
input_schema: {
type: 'object',
properties: {
methodology_id: { type: 'string', description: 'Unique snake_case identifier.' },
},
required: ['methodology_id'],
},
},
];
export interface MethodologyToolResult {
readonly ok: boolean;
readonly result?: unknown;
readonly error?: { readonly category: string; readonly message: string };
}
export async function executeMethodologyTool(
name: string,
rawInput: unknown,
): Promise<MethodologyToolResult> {
const input = (rawInput && typeof rawInput === 'object') ? (rawInput as Record<string, unknown>) : {};
try {
switch (name) {
case 'find_methodologies': {
const triggers = typeof input.triggers === 'string'
? input.triggers.split(',').map(s => s.trim()).filter(s => s.length > 0)
: undefined;
const results = await findMethodologies({
type: typeof input.type === 'string' ? (input.type as MethodologyType) : undefined,
domain: typeof input.domain === 'string' ? input.domain : undefined,
agent: typeof input.agent === 'string' ? (input.agent as MethodologyAgent) : undefined,
triggers,
});
return { ok: true, result: results.map(summarize) };
}
case 'get_methodology': {
const id = typeof input.methodology_id === 'string' ? input.methodology_id : '';
if (!id) {
return { ok: false, error: { category: 'invalid-request', message: 'methodology_id is required.' } };
}
const m = await getMethodology(id);
if (!m) {
return { ok: false, error: { category: 'not-found', message: `no methodology "${id}" in library.` } };
}
return { ok: true, result: m };
}
default:
return { ok: false, error: { category: 'unknown-tool', message: `unknown methodology tool "${name}"` } };
}
} catch (err) {
return {
ok: false,
error: { category: 'internal', message: err instanceof Error ? err.message : String(err) },
};
}
}
function summarize(m: Methodology) {
return {
methodology_id: m.methodology_id,
name: m.name,
type: m.type,
domain: m.domain,
agent: m.applies_to.agent,
triggers: m.applies_to.triggers,
status: m.status,
sourceFile: m.sourceFile,
};
}