BID · Console
Baseline · Intelligence · Decision
src/types.ts 6,985 bytes · typescript
/**
 * 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();
}