/**
* Analytical Table — system prompt builder.
*
* Combines the 12 universal standards with the agent's matrix row and
* runbook into a self-describing system prompt (Std 3). Std 5 —
* cost-appropriate prompts: callers pass an optional PromptScope
* listing the standards their step engages; when given, the prompt is
* trimmed. Multi-turn tool-use calls (the default here) omit scope
* and receive the full prompt.
*/
import { STANDARDS_SUMMARY, type PromptScope } from '../../../standards.js';
import {
AGENT_NAME,
AGENT_VERSION,
analyticalTableContract,
analyticalTableMatrix,
} from './matrix.js';
interface OpPrinciple { readonly n: number; readonly text: string; }
const OPERATING_PRINCIPLES: readonly OpPrinciple[] = [
{ n: 1, text: 'structure data only. Do NOT compute metrics, run comparisons, or generate insights — those are downstream agents\' jobs.' },
{ n: 4, text: 'never invent values to fill gaps. A missing cell is a missing cell; record it in missingCells with a reason.' },
{ n: 4, text: 'every cell carries lineage back to the Pillar 1 source URL it came from.' },
{ n: 5, text: 'when you need a normalization rule, query the methodology library via find_methodologies(type="normalization_rule"). Apply only declared methodologies.' },
{ n: 7, text: 'assign honest confidence per cell — inherit upstream confidence when this step introduces no new uncertainty; lower it if you had to make a normalization judgement.' },
{ n: 8, text: 'flag missing data, ontology conflicts, mixed units, and methodology gaps explicitly.' },
{ n: 0, text: 'Intelligence-forbidden: do not fetch fresh data (connectors are Pillar 1\'s job), do not produce recommendations, do not make unsupported claims.' },
];
function renderUniversal(engaged?: readonly number[]): string {
const set = engaged && engaged.length > 0 ? new Set(engaged) : null;
return STANDARDS_SUMMARY
.filter(s => !set || set.has(s.n))
.map(s => `${s.n}. ${s.name} — ${s.gist}`)
.join('\n');
}
function renderMatrix(): string {
return Object.entries(analyticalTableMatrix)
.map(([key, value]) => `${key.split('_')[0]}. ${value}`)
.join('\n');
}
function renderRunbook(): string {
return analyticalTableContract.runbook
.map(s => ` ${s.n}. ${s.name} — ${s.description}`)
.join('\n');
}
function renderPrinciples(engaged?: readonly number[]): string {
const set = engaged && engaged.length > 0 ? new Set(engaged) : null;
return OPERATING_PRINCIPLES
.filter(p => p.n === 0 || !set || set.has(p.n))
.map(p => (p.n === 0 ? `- ${p.text}` : `- Std ${p.n}: ${p.text}`))
.join('\n');
}
export function buildSystemPrompt(scope?: PromptScope): string {
const engaged = scope?.engagedStandards;
const isScoped = !!(engaged && engaged.length > 0);
const omitMatrix = scope?.omitMatrix ?? isScoped;
const omitRunbook = scope?.omitRunbook ?? isScoped;
const lines: string[] = [
`You are the ${AGENT_NAME} agent (v${AGENT_VERSION}) — the first agent of the BID Intelligence pillar.`,
];
if (isScoped) {
lines.push(
`All 12 universal operational standards govern your behavior. ` +
(scope?.stepLabel ? `Current step: "${scope.stepLabel}". ` : '') +
`Standards directly engaged by this step:`,
renderUniversal(engaged),
);
} else {
lines.push(
`Your operation is bounded by the 12 universal operational standards every BID agent must satisfy, plus your per-agent matrix row.`,
``,
`## Universal standards (canonical, apply to every agent)`,
renderUniversal(),
);
}
if (!omitMatrix) {
lines.push(``, `## Your matrix row (your concrete fill-ins for the 12 standards)`, renderMatrix());
}
if (!omitRunbook) {
lines.push(``, `## Your runbook (Std 6 — modular, repeatable, replayable)`, renderRunbook());
}
lines.push(``, `## Operating principles`, renderPrinciples(engaged));
return lines.join('\n');
}