/**
* Core BID framework types — the JobRequest contract plus the
* lineage / handoff / failure shapes referenced by the 12 universal
* standards in src/standards.ts.
*
* These types are the structural backbone every agent shares; nothing
* in src/agents/** defines its own envelope.
*/
import { z } from 'zod';
/* -----------------------------------------------------------------
* Standard 2: Inputs — JobRequest is the typed instruction the
* orchestrator accepts. Agents fetch and parse unstructured data at
* runtime *against* this request.
* ----------------------------------------------------------------- */
export const jobRequestSchema = z.object({
analysisId: z.string().min(1),
question: z.string().min(1),
entities: z
.array(
z.object({
id: z.string().min(1),
aliases: z.array(z.string()),
}),
)
.min(1),
targetMetrics: z
.array(
z.object({
key: z.string().min(1),
definition: z.string().min(1),
unit: z.string().optional(),
}),
)
.min(1),
/* Std 9: derived (computed) metrics that downstream agents must
* compute from the fetched targetMetrics. `methodology` references
* a methodology_id in the SME library; null means no methodology
* was found, in which case downstream agents escalate. */
derivedMetrics: z
.array(
z.object({
key: z.string().min(1),
definition: z.string().min(1),
unit: z.string().optional(),
methodology: z.string().nullable(),
}),
)
.optional(),
sources: z.array(z.string().min(1)).min(1),
period: z.string().min(1),
/* Pillar 3: which audience tier the eventual decision output is
* addressed to. The framework does not interpret specific roles —
* tiers are free-text labels the organization's SME-encoded rule
* library matches against (e.g. "decision_maker", "internal_partner",
* "analyst"). Defaults to "decision_maker" when not provided so the
* strictest disclosure/cadence rules apply by default. */
audience: z
.object({
tier: z.string().min(1),
severity: z.enum(['low', 'normal', 'material', 'high_impact']).optional(),
})
.optional(),
seedMappings: z
.array(
z.object({
sourceLabel: z.string().min(1),
targetKey: z.string().min(1),
entity: z.string().optional(),
}),
)
.optional(),
});
export type JobRequest = z.infer<typeof jobRequestSchema>;
/* -----------------------------------------------------------------
* Standard 2/11: Lineage — upstream provenance must always persist.
* ----------------------------------------------------------------- */
export const lineageSchema = z.object({
sourceUrl: z.string().nullable(),
capturedAt: z.string(),
effectiveAs: z.string().nullable(),
agentVersion: z.string(),
upstream: z.array(z.string()),
});
export type Lineage = z.infer<typeof lineageSchema>;
/* -----------------------------------------------------------------
* Standard 7: Validation status + confidence score.
* ----------------------------------------------------------------- */
export const validationStatusSchema = z.enum(['passed', 'flagged', 'review']);
export type ValidationStatus = z.infer<typeof validationStatusSchema>;
export const confidenceTierSchema = z.enum(['high', 'medium', 'low']);
export type ConfidenceTier = z.infer<typeof confidenceTierSchema>;
export const confidenceScoreSchema = z.object({
value: z.number().min(0).max(1),
tier: confidenceTierSchema,
rationale: z.string(),
});
export type ConfidenceScore = z.infer<typeof confidenceScoreSchema>;
/* -----------------------------------------------------------------
* Standard 8: Unresolved issue flags carried alongside payloads.
* ----------------------------------------------------------------- */
export const unresolvedIssueSchema = z.object({
category: z.string(),
detail: z.string(),
blocking: z.boolean(),
context: z.record(z.unknown()).optional(),
});
export type UnresolvedIssue = z.infer<typeof unresolvedIssueSchema>;
/* -----------------------------------------------------------------
* Standard 11: Handoff — the only shape agents exchange. Downstream
* agents never reconstruct upstream context; they read this envelope.
* ----------------------------------------------------------------- */
export interface Handoff<T> {
fromAgent: string;
fromAgentVersion: string;
toAgent: string | null;
payload: T;
metadata: Record<string, unknown>;
confidence: ConfidenceScore;
validation: {
status: ValidationStatus;
checks: readonly { name: string; passed: boolean; detail?: string }[];
};
unresolvedIssues: readonly UnresolvedIssue[];
lineage: Lineage;
timestamp: string;
}
/* -----------------------------------------------------------------
* Standard 12: Structured failure — every agent returns one of these
* instead of throwing a raw exception across a boundary.
* ----------------------------------------------------------------- */
export type FailureCategory =
| 'invalid-input'
| 'unrecoverable-extraction'
| 'unresolved-normalization'
| 'unresolved-remediation'
| 'recursion-limit'
| 'retry-limit'
| 'tool-unavailable'
| 'internal-error'
/* Pillar 2 — structural analytical failures (per spec §Std 12). */
| 'analytical-table-incomplete'
| 'methodology-unavailable'
| 'comparability-failure'
| 'unsupported-claim'
/* Pillar 3 — structural decision failures (per spec §Std 12). */
| 'rule-unavailable'
| 'interpretation-blocked'
| 'visualization-rule-gap'
| 'fidelity-violation'
| 'disclosure-blocked'
| 'delivery-rule-gap'
| 'channel-unavailable'
| 'unrecoverable-dispatch';
export interface FailureObject {
agent: string;
agentVersion: string;
category: FailureCategory;
reason: string;
context: Record<string, unknown>;
lineage: Lineage;
attempts: number;
recursionDepth: number;
occurredAt: string;
}
/* -----------------------------------------------------------------
* Execution context — threaded by the orchestrator through every
* agent so retries (Std 12) and recursion (Std 12) are tracked
* globally, not re-counted per agent.
* ----------------------------------------------------------------- */
export interface ExecutionContext {
analysisId: string;
correlationId: string;
retries: number;
recursionDepth: number;
startedAt: string;
/** Append-only structured trace, surfaced in the audit JSON. */
trace: TraceEntry[];
}
export interface TraceEntry {
agent: string;
standard: number;
step: string;
detail: string;
at: string;
}
export function makeContext(analysisId: string, correlationId: string): ExecutionContext {
return {
analysisId,
correlationId,
retries: 0,
recursionDepth: 0,
startedAt: new Date().toISOString(),
trace: [],
};
}
export function nowIso(): string {
return new Date().toISOString();
}