/**
* Source/Extraction — system prompt builder.
*
* Constructs the agent's operational spec at runtime by combining the
* canonical 12 universal standards (src/standards.ts) with the agent's
* matrix row, runbook, and operating principles.
*
* Std 5 — cost-appropriate prompts: callers pass an optional
* PromptScope listing the standards their step directly engages. When
* provided, the prompt is trimmed to those standards and the matching
* operating principles; the matrix dump and runbook are omitted.
* Without a scope, the full prompt is returned (today's behaviour) —
* used by interpretive multi-turn tool-use calls.
*/
import { STANDARDS_SUMMARY, type PromptScope } from '../../../standards.js';
import {
AGENT_NAME,
AGENT_VERSION,
sourceExtractionContract,
sourceExtractionMatrix,
} from './matrix.js';
interface OpPrinciple { readonly n: number; readonly text: string; }
/** n: which standard this principle primarily engages (0 = pillar
* guardrail, always rendered). */
const OPERATING_PRINCIPLES: readonly OpPrinciple[] = [
{ n: 3, text: 'explicit, deterministic reasoning where possible; every decision must be explainable.' },
{ n: 4, text: 'preserve raw source integrity, preserve lineage, never fabricate values.' },
{ n: 5, text: 'invoke only capabilities declared in your matrix row (retrieval, API, web, parser, OCR, repository tools).' },
{ n: 5, text: 'narrow-first retrieval: when the JobRequest specifies concrete data elements (specific concepts, fields, identifiers — e.g. named targetMetrics or methodology input fields), prefer retrieval tools that target those elements directly. Reserve broader discovery tools for cases where inputs are abstract or exploratory.' },
{ n: 5, text: 'narrow-first scope parameters: when target entity, period, unit, or concept are already known from the JobRequest, pass them as scope arguments to retrieval tools that accept them (e.g. `period: "FY-2024"`, `unit: "USD"`). Do not retrieve a full time series and then filter — the same principle as preferring narrow tools applies to arguments on those tools.' },
{ n: 7, text: 'each output you produce must carry a confidence score with an explainable rationale.' },
{ n: 11, text: 'your final output is a Structured Payload that the Normalization agent will consume; never assume downstream context.' },
{ n: 12, text: 'fail safely. Return null for values you cannot locate; do not invent.' },
{ n: 0, text: 'Baseline-specific: NO strategic insight, NO benchmarking, NO maturity scoring, NO recommendations.' },
];
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(sourceExtractionMatrix)
.map(([key, value]) => {
const n = key.split('_')[0];
return `${n}. ${value}`;
})
.join('\n');
}
function renderRunbook(): string {
return sourceExtractionContract.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}) in the BID Baseline 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');
}