/** * Core TypeScript interfaces for Twining MCP Server. * Matches TWINING-DESIGN-SPEC.md section 3 exactly. */ import type { Provenance } from "./provenance.js"; export declare const ENTRY_TYPES: readonly ["need", "offer", "finding", "decision", "constraint", "question", "answer", "status", "artifact", "warning"]; export type EntryType = (typeof ENTRY_TYPES)[number]; /** * How an entry came to exist relative to the session that wrote it. * "narration" — the session describing its own activity (twining_record's * auto-emitted status post). "discovery" — something the session found * (twining_record's findings fan-out). An ABSENT marker means unknown * (records written before this field existed, or posted directly via * twining_post); never read absence as either value. */ export type EntryOrigin = "narration" | "discovery"; /** Blackboard Entry — spec section 3.1 */ export interface BlackboardEntry { id: string; timestamp: string; agent_id: string; entry_type: EntryType; tags: string[]; relates_to?: string[]; scope: string; summary: string; detail: string; embedding_id?: string; /** Provenance of the entry relative to its writing session. */ origin?: EntryOrigin; /** * Persisted lifecycle (D2). ABSENT means open (every record written * before this field existed is open, not unknown — openness was the only * state). "resolved" closes the entry out of the open triage lane while * preserving the record; contrast twining_dismiss, which removes it. * The relates_to back-reference mechanism (resolution.ts) remains valid — * computeResolvedIds unions both — but explicit status survives its * resolver being archived or dismissed, which the back-reference does not. */ status?: "open" | "resolved"; /** ISO timestamp of the first resolve; never overwritten by re-resolves. */ resolved_at?: string; resolved_by?: string; resolution_note?: string; /** Branch + commit at time of recording; used for staleness detection. */ provenance?: Provenance; } /** Decision alternative — nested in Decision */ export interface DecisionAlternative { option: string; pros: string[]; cons: string[]; /** * Why this option was rejected. Optional because the natural-language path * often identifies WHICH option was rejected without stating WHY, and a * fabricated reason is worse than an absent one — the placeholder it used to * write ("Not chosen") filled the why-not field with a tautology on 217 of * 217 NL-derived alternatives. Absent means "not stated", never "no reason". */ reason_rejected?: string; } /** * Where a decision's rationale came from. "authored" — the caller stated it. * "derived" — it is an echo of the summary because none was supplied, so it * records the WHAT, not the WHY. An ABSENT marker means unknown (records * written before this field existed); never read absence as "authored". */ export type RationaleSource = "authored" | "derived"; export type DecisionConfidence = "high" | "medium" | "low"; export type DecisionStatus = "active" | "provisional" | "superseded" | "overridden" | "archived"; /** Decision — spec section 3.2 */ export interface Decision { id: string; timestamp: string; agent_id: string; domain: string; scope: string; summary: string; context: string; rationale: string; /** Provenance of `rationale`. Absent on records written before this field. */ rationale_source?: RationaleSource; constraints: string[]; alternatives: DecisionAlternative[]; depends_on: string[]; supersedes?: string; /** Back-link written when another decision supersedes this one (#31). */ superseded_by?: string; /** * Status the decision held before twining_archive_stale archived it — * twining_unarchive restores to it, so a provisional never comes back * ratified and a superseded decision never resurrects as authoritative. * Cleared on restore; absent on records archived before this field. */ archived_from?: DecisionStatus; confidence: DecisionConfidence; status: DecisionStatus; reversible: boolean; affected_files: string[]; affected_symbols: string[]; assumptions?: string[]; commit_hashes: string[]; overridden_by?: string; override_reason?: string; /** * Who ratified this decision from provisional to active, and when — * absent on decisions active since creation or promoted before * attribution existed. Lets a later promote's already_active answer * "who ratified it?" instead of reading as a silent no-op (field D15). */ promoted_by?: string; promoted_at?: string; assembled_before?: boolean; /** Branch + commit at time of recording; used for staleness detection. */ provenance?: Provenance; /** * Append-only metadata repair trail (field D11). Each entry records what * twining_amend added and why; the record's semantic content (summary, * rationale, context, alternatives) is never amendable. */ amendments?: DecisionAmendment[]; } /** One append-only metadata amendment on a decision (field D11). */ export interface DecisionAmendment { amended_at: string; amended_by: string; added_files: string[]; added_symbols: string[]; reason?: string; } /** Knowledge Graph Entity — spec section 3.3 */ export interface Entity { id: string; name: string; type: "module" | "function" | "class" | "file" | "concept" | "pattern" | "dependency" | "api_endpoint" | "agent" | "commit"; properties: Record; created_at: string; updated_at: string; } /** Knowledge Graph Relation — spec section 3.4 */ export interface Relation { id: string; source: string; target: string; type: "depends_on" | "implements" | "decided_by" | "affects" | "tested_by" | "calls" | "imports" | "related_to" | "supersedes" | "produces" | "challenged"; properties: Record; created_at: string; } /** Assembled Context — spec section 3.5 (ephemeral output) */ export interface AssembledContext { assembled_at: string; task: string; scope: string; token_estimate: number; /** * Warnings that did not fit the token budget even as summary-only, and are * therefore absent from active_warnings. Non-zero means the briefing is * incomplete and must not claim there are no constraints. */ warnings_omitted?: number; /** * Archived decisions in scope hidden from this briefing (D3). Non-zero * distinguishes "decisions were archived away" from "no decisions exist" — * without it, a bad archive sweep blinds the gate invisibly. Restore with * twining_unarchive. */ archived_excluded_count?: number; /** Superseded/overridden decisions hidden from this briefing (field D10) — same blindness class as archived_excluded_count. */ superseded_excluded_count?: number; active_decisions: { id: string; summary: string; rationale: string; confidence: string; affected_files: string[]; constraints?: string[]; rejected_alternatives?: string[]; assumptions?: string[]; assumptions_status?: "hold" | "challenged"; challenged_assumptions?: string[]; relevance_path?: string; }[]; open_needs: Pick[]; recent_findings: Pick[]; active_warnings: (Pick & { /** * Posted by THIS server process, i.e. the calling session (field D12). * agent_id cannot carry this — it is a role label ("main" on most * entries), not a session identity. Marked, never hidden or re-scored: * a session that lost context genuinely wants its own trail. */ self_authored?: boolean; })[]; recent_questions: Pick[]; related_entities: { name: string; type: string; relations: string[]; }[]; planning_state?: PlanningState; recent_handoffs?: { id: string; source_agent: string; target_agent: string; scope: string; summary: string; result_status: string; acknowledged: boolean; created_at: string; results?: HandoffResult[]; }[]; suggested_agents?: { agent_id: string; capabilities: string[]; liveness: string; }[]; } /** Config — matches spec section 2.3 config.yml structure */ export interface TwiningConfig { version: number; project_name: string; embedding_model: string; storage?: { /** * Persistence backend. "auto" (v2 default) resolves by legacy * detection: sqlite state → sqlite, legacy content → files (with a * migrate nudge), fresh → sqlite. Explicit "files"/"sqlite" pins the * choice. sqlite requires node:sqlite (Node >= 22.13) and falls back * to "files" with a warning when unavailable. */ backend?: "files" | "sqlite" | "auto"; /** * Maintain the committable per-record export tree (.twining/records/) * alongside the sqlite database, and converge the database to it on * startup. Only applies to the sqlite backend (twining.db is a * gitignored local cache; the export tree is how state rides git * between users, branches, and worktrees). Default: true. */ export_records?: boolean; /** * Opt-in (v2): when the auto-resolved backend is "files" because of * legacy content, run `twining-mcp migrate` automatically at startup * instead of only nudging. Equivalent to TWINING_AUTO_MIGRATE=1. * Default: false — migration is a deliberate act. */ auto_migrate?: boolean; }; archive: { auto_archive_on_commit: boolean; auto_archive_on_context_switch: boolean; max_blackboard_entries_before_archive: number; /** * Count-based retention (D4): sweeps keep the newest N non-exempt * entries on the board. Count-based, not age-based — the #35 outage * proved an age cutoff cannot bound a same-hour burst. 0 disables. */ retain_recent: number; }; context_assembly: { default_max_tokens: number; priority_weights: { recency: number; relevance: number; decision_confidence: number; warning_boost: number; graph_connectivity?: number; graph_reachability?: number; }; }; conflict_resolution: string; agents?: { liveness: { idle_after_ms: number; gone_after_ms: number; }; }; delegations?: { timeouts: { high_ms: number; normal_ms: number; low_ms: number; }; }; analytics?: AnalyticsConfig; instructions?: { /** Whether to include workflow instructions in the MCP initialize response (default: true) */ auto_inject: boolean; }; tools?: { mode: "full" | "lite"; /** When true, register all 32 tools including rarely-used ones (default: false) */ full_surface: boolean; }; graph?: { /** When true, auto-populate knowledge graph from tool calls (default: false) */ auto_populate: boolean; }; housekeeping?: { /** Score threshold (0-1) above which entries are flagged stale during staleness_review. Default 0.95. */ staleness_threshold: number; }; } /** Summarize result — spec section 4.3 twining_summarize return */ export interface SummarizeResult { scope: string; active_decisions: number; provisional_decisions: number; open_needs: number; active_warnings: number; unanswered_questions: number; recent_activity_summary: string; planning_state?: PlanningState; } /** What changed result — spec section 4.3 twining_what_changed return */ export interface WhatChangedResult { new_decisions: { id: string; summary: string; }[]; new_entries: { id: string; entry_type: string; summary: string; }[]; overridden_decisions: { id: string; summary: string; reason: string; }[]; reconsidered_decisions: { id: string; summary: string; }[]; } /** Planning state from .planning/ directory */ export interface PlanningState { current_phase: string; progress: string; blockers: string[]; pending_todos: string[]; open_requirements: string[]; } /** Decision index entry — subset for fast lookup */ export interface DecisionIndexEntry { id: string; timestamp: string; domain: string; scope: string; summary: string; confidence: DecisionConfidence; status: DecisionStatus; affected_files: string[]; affected_symbols: string[]; commit_hashes: string[]; } /** Agent liveness state derived from last_active timestamp */ export type AgentLiveness = "active" | "idle" | "gone"; /** Configurable thresholds for liveness computation */ export interface LivenessThresholds { idle_after_ms: number; gone_after_ms: number; } /** Agent registry record — spec section for agent coordination */ export interface AgentRecord { agent_id: string; capabilities: string[]; role?: string; description?: string; registered_at: string; last_active: string; } /** Structured result within a handoff */ export interface HandoffResult { description: string; status: "completed" | "partial" | "blocked" | "failed"; artifacts?: string[]; notes?: string; } /** Full handoff record between agents */ export interface HandoffRecord { id: string; created_at: string; source_agent: string; target_agent?: string; scope?: string; summary: string; results: HandoffResult[]; context_snapshot: { decision_ids: string[]; warning_ids: string[]; finding_ids: string[]; summaries: string[]; }; acknowledged_by?: string; acknowledged_at?: string; } /** Lightweight handoff index entry for JSONL index */ export interface HandoffIndexEntry { id: string; created_at: string; source_agent: string; target_agent?: string; scope?: string; summary: string; result_status: "completed" | "partial" | "blocked" | "failed" | "mixed"; acknowledged: boolean; } /** Input for agent discovery/ranking */ export interface DiscoverInput { required_capabilities: string[]; include_gone?: boolean; min_score?: number; } /** Scored agent result from discovery */ export interface AgentScore { agent_id: string; capabilities: string[]; role?: string; description?: string; liveness: AgentLiveness; capability_overlap: number; liveness_score: number; total_score: number; matched_capabilities: string[]; } /** Result of agent discovery */ export interface DiscoverResult { agents: AgentScore[]; total_registered: number; /** Zero-capability-overlap agents excluded by the no-min_score default * (S4-8) — nonzero means "absent from results", not "unregistered". */ excluded_zero_overlap: number; } /** Urgency levels for delegated tasks */ export type DelegationUrgency = "high" | "normal" | "low"; /** Metadata attached to delegation blackboard entries */ export interface DelegationMetadata { type: "delegation"; required_capabilities: string[]; urgency: DelegationUrgency; expires_at: string; timeout_ms?: number; } /** Input for posting a delegation to the blackboard */ export interface DelegationInput { summary: string; required_capabilities: string[]; urgency?: DelegationUrgency; timeout_ms?: number; scope?: string; tags?: string[]; agent_id?: string; } /** Result of posting a delegation */ export interface DelegationResult { entry_id: string; timestamp: string; expires_at: string; suggested_agents: AgentScore[]; /** Zero-overlap agents excluded from suggestions by discover's default * (2.16.0 review CS-6) — nonzero means agents exist but none advertise * the requested capabilities. */ excluded_zero_overlap?: number; } /** Input for creating a handoff between agents */ export interface CreateHandoffInput { source_agent: string; target_agent?: string; scope?: string; summary: string; results: HandoffResult[]; auto_snapshot?: boolean; context_snapshot?: HandoffRecord["context_snapshot"]; } /** Test coverage check result */ export interface TestCoverageCheck { status: "pass" | "warn" | "fail"; decisions_in_scope: number; decisions_with_tested_by: number; uncovered: Array<{ decision_id: string; summary: string; affected_files: string[]; }>; } /** Warnings check result */ export interface WarningsCheck { status: "pass" | "warn" | "fail"; warnings_in_scope: number; acknowledged: number; resolved: number; silently_ignored: number; ignored_details: Array<{ id: string; summary: string; }>; } /** Assembly tracking check result */ export interface AssemblyCheck { status: "pass" | "warn" | "fail"; decisions_by_agent: number; assembled_before: number; blind_decisions: Array<{ decision_id: string; summary: string; agent_id: string; }>; } /** Drift detection check (stub for P2) */ export interface DriftCheck { status: "pass" | "warn" | "skip"; decisions_checked: number; stale: Array<{ decision_id: string; summary: string; affected_file: string; decision_timestamp: string; last_file_modification: string; modifying_commit: string; }>; } /** Constraints check (stub for P2) */ export interface ConstraintsCheck { status: "pass" | "warn" | "fail" | "skip"; checkable: number; passed: number; failed: Array<{ constraint_id: string; summary: string; check_command: string; actual: string; expected: string; }>; } /** Full verification result from twining_verify */ export interface VerifyResult { scope: string; verified_at: string; checks: { test_coverage?: TestCoverageCheck; warnings?: WarningsCheck; assembly?: AssemblyCheck; drift?: DriftCheck; constraints?: ConstraintsCheck; }; summary: string; } /** Test coverage result from GraphEngine */ export interface TestCoverageResult { decisions_in_scope: number; decisions_with_tested_by: number; uncovered: Array<{ decision_id: string; summary: string; affected_files: string[]; }>; } /** Single tool call metric entry (appended to metrics.jsonl) */ export interface MetricEntry { tool_name: string; timestamp: string; duration_ms: number; success: boolean; error_code?: string; agent_id: string; /** Serialized response size in bytes (S4-4 — context cost measurement). */ response_bytes?: number; /** Length of the first top-level array in the response — best-effort. */ result_count?: number; /** The call's scope argument, when it was a string. */ scope?: string; } /** Aggregated tool usage summary */ export interface ToolUsageSummary { tool_name: string; call_count: number; error_count: number; avg_duration_ms: number; p95_duration_ms: number; last_called: string; } /** Time-bucketed usage data */ export interface UsageBucket { bucket_start: string; bucket_end: string; call_count: number; error_count: number; avg_duration_ms: number; } /** Value stats computed from existing .twining/ data */ export interface ValueStats { blind_decisions_prevented: { total_decisions: number; assembled_before: number; prevention_rate: number; }; warnings_surfaced: { total: number; acknowledged: number; resolved: number; ignored: number; }; test_coverage: { total_decisions: number; with_tested_by: number; coverage_rate: number; }; decision_lifecycle: { active: number; provisional: number; superseded: number; overridden: number; archived: number; }; commit_traceability: { total_decisions: number; with_commits: number; traceability_rate: number; }; knowledge_graph: { entities: number; relations: number; entities_by_type: Record; relations_by_type: Record; }; agent_coordination: { total_handoffs: number; by_result_status: Record; acknowledgment_rate: number; }; } /** Analytics configuration section for TwiningConfig */ export interface AnalyticsConfig { metrics: { enabled: boolean; }; telemetry: { enabled: boolean; posthog_api_key: string; posthog_host: string; }; } /** Single triage item — a decision or blackboard entry awaiting/reporting. */ export interface TriageItem { kind: "decision" | "need" | "question" | "warning" | "artifact"; id: string; scope: string; summary: string; agent_id: string; timestamp: string; age_ms: number; tags?: string[]; origin?: EntryOrigin; detail_preview?: string; detail_truncated?: true; reversible?: boolean; confidence?: DecisionConfidence; status?: "provisional" | "active"; urgency?: DelegationUrgency; expires_at?: string; } /** Result of buildTriage — docs/TRIAGE-SPEC.md §4 */ export interface TriageResult { generated_at: string; window_ms: number; section: "all" | "open" | "recent"; scope?: string; for_agent?: string; since?: string; open?: TriageItem[]; open_cursor?: string; recent?: TriageItem[]; counts: { open: { total: number; irreversible: number; by_kind: { decision: number; need: number; question: number; warning: number; }; }; recent: { total: number; irreversible: number; by_kind: { decision: number; artifact: number; }; }; }; } //# sourceMappingURL=types.d.ts.map