FABRIC/src/types.ts
jedarden e4d7378096
Some checks are pending
CI / test (18.x) (push) Waiting to run
CI / test (20.x) (push) Waiting to run
CI / test (22.x) (push) Waiting to run
feat(types): add granular NEEDLE worker states + directory tailer startup re-read
- Add BUILDING, DISPATCHING, EXECUTING, HANDLING, LOGGING, EXHAUSTED_IDLE states
- These represent the inner loop of bead execution, all map to WORKING display
- DirectoryTailer now re-reads files modified within 4 hours from start on startup
  This reconstructs worker state after FABRIC restart without replaying ancient history
- Update VALID_TRANSITIONS to include new state transitions
- Update color/icon mappings for new states
2026-05-26 22:18:56 -04:00

2936 lines
65 KiB
TypeScript

/**
* FABRIC Type Definitions
*
* Core types for NEEDLE log parsing and worker state management.
*/
/**
* Schema version for the NEEDLE event wire format.
* Bumped when the canonical NeedleEvent shape changes.
* Both NEEDLE and FABRIC must agree on this version.
*/
export const NEEDLE_EVENT_SCHEMA_VERSION = 1;
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
export type WorkerStatus = 'active' | 'idle' | 'error';
/**
* NEEDLE worker state machine — first-class states emitted by
* worker.state_transition events. These are the canonical states
* that replace the coarse WorkerStatus in the UI.
*
* Abstract: BOOTING → SELECTING → CLAIMING → WORKING → CLOSING → STOPPED
* Granular: SELECTING → BUILDING → DISPATCHING → EXECUTING → HANDLING → LOGGING → SELECTING
*
* BUILDING/DISPATCHING/EXECUTING/HANDLING/LOGGING are the inner loop of a
* single bead execution. They all map to the WORKING display state.
*/
export type NeedleState =
| 'BOOTING'
| 'SELECTING'
| 'CLAIMING'
| 'WORKING'
| 'BUILDING'
| 'DISPATCHING'
| 'EXECUTING'
| 'HANDLING'
| 'LOGGING'
| 'CLOSING'
| 'EXHAUSTED_IDLE'
| 'STOPPED';
/**
* All valid state transitions in the NEEDLE worker state machine.
*/
export const VALID_TRANSITIONS: Partial<Record<NeedleState, NeedleState[]>> = {
BOOTING: ['SELECTING'],
SELECTING: ['BUILDING', 'CLAIMING', 'EXHAUSTED_IDLE', 'STOPPED'],
CLAIMING: ['WORKING', 'SELECTING'],
BUILDING: ['DISPATCHING', 'SELECTING'],
DISPATCHING: ['EXECUTING', 'SELECTING'],
EXECUTING: ['HANDLING', 'SELECTING'],
HANDLING: ['LOGGING', 'SELECTING'],
LOGGING: ['SELECTING'],
WORKING: ['CLOSING', 'SELECTING'],
CLOSING: ['SELECTING', 'STOPPED'],
EXHAUSTED_IDLE: ['SELECTING', 'STOPPED'],
STOPPED: ['BOOTING'],
};
/**
* Map a NeedleState to the coarser WorkerStatus for backward compatibility.
*/
export function needleStateToStatus(state: NeedleState): WorkerStatus {
switch (state) {
case 'BOOTING':
case 'SELECTING':
case 'CLAIMING':
case 'BUILDING':
case 'DISPATCHING':
case 'EXECUTING':
case 'HANDLING':
case 'LOGGING':
case 'WORKING':
return 'active';
case 'CLOSING':
return 'active';
case 'EXHAUSTED_IDLE':
case 'STOPPED':
return 'idle';
}
}
/**
* All event types emitted by NEEDLE's telemetry pipeline.
* Format: category.action — matches NEEDLE's EventKind::event_type() mapping.
*
* Note: This is a non-exhaustive list of known event types for documentation.
* The parser accepts any string for event_type to forward-compat with new NEEDLE versions.
*/
export type NeedleEventType =
// Worker lifecycle
| 'worker.booting'
| 'worker.started'
| 'worker.stopped'
| 'worker.errored'
| 'worker.exhausted'
| 'worker.idle'
| 'worker.idle_sleep_completed'
| 'worker.idle_sleep_entered'
| 'worker.state_transition'
| 'worker.queue_empty'
| 'worker.boot.timeout'
| 'worker.handling.timeout'
| 'worker.upgrade.detected'
| 'worker.upgrade.completed'
// Initialization
| 'init.step.started'
| 'init.step.completed'
// Strand lifecycle
| 'strand.evaluated'
| 'strand.skipped'
// Bead claim lifecycle
| 'bead.claim.attempted'
| 'bead.claim.succeeded'
| 'bead.claim.race_lost'
| 'bead.claim.race_lost_skipped'
| 'bead.claim.failed'
// Bead lifecycle
| 'bead.released'
| 'bead.release.failed'
| 'bead.completed'
| 'bead.orphaned'
// Bead mitosis
| 'bead.mitosis.evaluated'
| 'bead.mitosis.split'
| 'bead.mitosis.skipped'
// Bead unravel (alternatives)
| 'bead.unravel.analyzed'
| 'bead.unravel.skipped'
// Agent lifecycle
| 'agent.dispatched'
| 'agent.completed'
| 'agent.transform.spawned'
| 'agent.transform.exited'
| 'agent.transform.skipped'
// Build lifecycle
| 'build.timeout'
| 'build.heartbeat'
// Outcome handling
| 'outcome.classified'
| 'outcome.handled'
// Heartbeat & peer detection
| 'heartbeat.emitted'
| 'peer.stale'
| 'peer.crashed'
// Health checks
| 'health.check'
// Mend (maintenance cycle)
| 'mend.orphaned_lock_removed'
| 'mend.dependency_cleaned'
| 'mend.db_repaired'
| 'mend.db_rebuilt'
| 'mend.cycle_summary'
| 'mend.trace_cleanup'
| 'mend.learning_cleanup'
| 'mend.idle_worker_flagged'
| 'mend.worker_deregistered'
| 'mend.orphaned_heartbeat_removed'
| 'mend.dependency_removed'
| 'mend.bead_release_failed'
| 'mend.dependency_cleanup_failed'
| 'mend.lock_remove_failed'
| 'mend.rate_limit_cleaned'
| 'mend.rate_limit_provider_removed'
| 'mend.rate_limit_provider_reset'
| 'mend.zero_activity_log_cleaned'
// Effort & budget
| 'effort.recorded'
| 'budget.warning'
| 'budget.stop'
// Rate limiting
| 'rate_limit.wait'
| 'rate_limit.allowed'
// Verification
| 'verification.failed'
| 'verification.passed'
// Reflect (drift detection & decision mining)
| 'reflect.started'
| 'reflect.consolidated'
| 'reflect.skipped'
| 'reflect.transcripts_read'
| 'reflect.drift_detected'
| 'reflect.drift_promoted'
| 'reflect.decision_extracted'
| 'reflect.adr_created'
| 'reflect.learning_promoted'
| 'reflect.learning_deduplicated'
| 'reflect.claudemd_written'
// Drift detection
| 'drift.started'
| 'drift.completed'
| 'drift.skipped'
| 'drift.report_written'
// Decision detection
| 'decision.started'
| 'decision.completed'
| 'decision.skipped'
// Pulse (health monitoring)
| 'pulse.scanner_started'
| 'pulse.scanner_completed'
| 'pulse.scanner_failed'
| 'pulse.bead_created'
| 'pulse.skipped'
// Rollback
| 'rollback.completed'
// Canary deployment
| 'canary.started'
| 'canary.suite_completed'
| 'canary.promoted'
| 'canary.rejected'
// Transform lifecycle
| 'transform.started'
| 'transform.completed'
| 'transform.failed'
| 'transform.skipped'
// Telemetry internal
| 'telemetry.sink_error'
| 'telemetry.otlp.dropped'
| 'telemetry.otlp.shutdown_timeout';
// ============================================
// Canonical NeedleEvent (wire schema)
// ============================================
/**
* Canonical event shape emitted by NEEDLE over JSONL and OTLP.
* This is the contract between NEEDLE and FABRIC — versioned via NEEDLE_EVENT_SCHEMA_VERSION.
*
* Ordering: sort by (worker_id, sequence), NOT by timestamp.
* Wall clocks skew across hosts; sequence is the worker's monotonic counter.
*/
export interface NeedleEvent {
/** RFC3339 timestamp — display only, NOT authoritative for ordering */
timestamp: string;
/** Event taxonomy string, e.g. "worker.started", "bead.claimed" */
event_type: NeedleEventType | string;
/** Worker identifier (e.g. "tcb-alpha") */
worker_id: string;
/** Session identifier grouping a worker's lifetime events */
session_id: string;
/** Per-worker monotonic counter — authoritative for ordering */
sequence: number;
/** Present when event pertains to a specific bead */
bead_id?: string;
/** Event-specific payload */
data: Record<string, unknown>;
}
// ============================================
// Conversation Event Types
// ============================================
/**
* Role in a conversation
*/
export type ConversationRole = 'system' | 'user' | 'assistant' | 'tool';
/**
* Type of conversation event
*/
export type ConversationEventType =
| 'prompt' // User input/prompt
| 'response' // Assistant response text
| 'thinking' // Internal reasoning/thinking block
| 'tool_call' // Tool being called with arguments
| 'tool_result'; // Result from a tool call
/**
* Base interface for all conversation events
*/
export interface ConversationEventBase {
/** Unique event identifier */
id: string;
/** Type of conversation event */
type: ConversationEventType;
/** Role in conversation */
role: ConversationRole;
/** Unix timestamp in milliseconds */
ts: number;
/** Worker identifier */
worker: string;
/** Associated bead/task ID (if any) */
bead?: string;
/** Sequence number within the conversation */
sequence: number;
/** Token count for this event (if available) */
tokens?: number;
}
/**
* User prompt event
*/
export interface PromptEvent extends ConversationEventBase {
type: 'prompt';
role: 'user';
/** The user's prompt text */
content: string;
/** Whether this is a continuation of a previous prompt */
isContinuation?: boolean;
}
/**
* Assistant response event
*/
export interface ResponseEvent extends ConversationEventBase {
type: 'response';
role: 'assistant';
/** The response text */
content: string;
/** Whether the response is truncated */
isTruncated?: boolean;
/** Model used for this response */
model?: string;
/** Stop reason (if available) */
stopReason?: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use';
}
/**
* Thinking/reasoning block event
*/
export interface ThinkingEvent extends ConversationEventBase {
type: 'thinking';
role: 'assistant';
/** The thinking content */
content: string;
/** Whether thinking is truncated */
isTruncated?: boolean;
/** Duration of thinking in ms (if available) */
durationMs?: number;
}
/**
* Tool argument types
*/
export type ToolArgValue = string | number | boolean | null | ToolArgValue[] | { [key: string]: ToolArgValue };
/**
* Tool call event
*/
export interface ToolCallEvent extends ConversationEventBase {
type: 'tool_call';
role: 'assistant';
/** Tool name */
tool: string;
/** Tool arguments */
args: Record<string, ToolArgValue>;
/** Tool call ID (for correlating with results) */
toolCallId?: string;
/** Human-readable summary of the call */
summary?: string;
}
/**
* Tool result event
*/
export interface ToolResultEvent extends ConversationEventBase {
type: 'tool_result';
role: 'tool';
/** Tool name */
tool: string;
/** Tool call ID this is a response to */
toolCallId?: string;
/** Result content (may be truncated) */
content: string;
/** Whether the tool call succeeded */
success: boolean;
/** Error message if failed */
error?: string;
/** Duration of tool call in ms */
durationMs?: number;
/** Whether the result is truncated */
isTruncated?: boolean;
/** Size of full result in bytes (for context) */
resultSize?: number;
}
/**
* Union type for all conversation events
*/
export type ConversationEvent =
| PromptEvent
| ResponseEvent
| ThinkingEvent
| ToolCallEvent
| ToolResultEvent;
/**
* A complete conversation session
*/
export interface ConversationSession {
/** Session identifier */
id: string;
/** Worker ID */
workerId: string;
/** Associated bead ID (if any) */
beadId?: string;
/** Start timestamp */
startTime: number;
/** End timestamp (if complete) */
endTime?: number;
/** All events in chronological order */
events: ConversationEvent[];
/** Total token count */
totalTokens: number;
/** Number of turns */
turnCount: number;
/** Tools used in this session */
toolsUsed: string[];
/** Whether the session is still active */
isActive: boolean;
}
/**
* Options for parsing conversation events
*/
export interface ConversationParseOptions {
/** Maximum content length before truncation */
maxContentLength?: number;
/** Include thinking blocks */
includeThinking?: boolean;
/** Include tool results */
includeToolResults?: boolean;
/** Truncate tool results longer than this */
maxToolResultLength?: number;
}
// ============================================
// Core Log Event Types
// ============================================
export interface LogEvent {
/** Unix timestamp in milliseconds — display only, NOT authoritative for ordering */
ts: number;
/** Worker identifier (e.g., 'w-abc123') */
worker: string;
/** Per-worker monotonic counter — authoritative for ordering (from NeedleEvent.sequence) */
sequence?: number;
/** Log level */
level: LogLevel;
/** Log message */
msg: string;
/** Optional: Tool that was called */
tool?: string;
/** Optional: File path being operated on */
path?: string;
/** Optional: Bead/task identifier */
bead?: string;
/** Optional: Duration in milliseconds */
duration_ms?: number;
/** Optional: Error details */
error?: string;
/** NEEDLE session identifier (e.g. 'needle-claude-anthropic-sonnet-alpha') */
session?: string;
/** AI provider extracted from NEEDLE worker string (e.g. 'anthropic', 'openai') */
provider?: string;
/** AI model identifier extracted from NEEDLE worker string (e.g. 'sonnet', 'gpt-4o') */
model?: string;
/** Any additional fields */
[key: string]: unknown;
}
/**
* Compare two LogEvents by (worker, sequence), falling back to ts.
*
* Sequence is the authoritative ordering key within a worker (monotonic counter
* from NEEDLE). Timestamp is used only as a display fallback for legacy events
* that lack a sequence number.
*/
export function compareEventsBySequence(a: LogEvent, b: LogEvent): number {
const seqA = a.sequence != null && a.sequence >= 0 ? a.sequence : null;
const seqB = b.sequence != null && b.sequence >= 0 ? b.sequence : null;
if (seqA !== null && seqB !== null) {
if (a.worker !== b.worker) return a.worker.localeCompare(b.worker);
return seqA - seqB;
}
return a.ts - b.ts;
}
export interface WorkerInfo {
/** Worker identifier */
id: string;
/** Current status (coarse — derived from needleState) */
status: WorkerStatus;
/** Current NEEDLE state machine state (fine-grained) */
needleState?: NeedleState;
/** Timestamp of the last state transition (ms since epoch) */
lastStateTransition?: number;
/** Last event received */
lastEvent?: LogEvent;
/** Total beads completed */
beadsCompleted: number;
/** First seen timestamp */
firstSeen: number;
/** Last activity timestamp */
lastActivity: number;
/** Files currently being modified by this worker */
activeFiles: string[];
/** Whether this worker is involved in any collisions */
hasCollision: boolean;
/** Current bead/task being worked on */
activeBead?: string;
/** Current bead from bead.claim.succeeded, cleared on bead.released */
currentBead: string | null;
/** Directories this worker is active in */
activeDirectories: string[];
/** All collision types this worker is involved in */
collisionTypes: ('file' | 'bead' | 'task')[];
/** Total number of events received for this worker */
eventCount: number;
/** Whether the worker appears stuck (gap-based detection) */
stuck?: boolean;
/** Human-readable reason the worker is stuck */
stuckReason?: string;
}
export interface EventFilter {
/** Filter by worker ID */
worker?: string;
/** Filter by log level */
level?: LogLevel;
/** Filter by bead ID */
bead?: string;
/** Filter by event type (glob pattern, e.g. "bead.*") */
eventType?: string;
/** Filter by file path */
path?: string;
/** Time range start (Unix timestamp) */
since?: number;
/** Time range end (Unix timestamp) */
until?: number;
}
/**
* File collision event - when multiple workers modify the same file concurrently
*/
export interface FileCollision {
/** File path being contested */
path: string;
/** Workers involved in the collision */
workers: string[];
/** Timestamp when collision was detected */
detectedAt: number;
/** Events that triggered the collision */
events: LogEvent[];
/** Whether the collision is still active */
isActive: boolean;
}
/**
* Bead collision - when multiple workers work on the same bead/task
*/
export interface BeadCollision {
/** Bead ID being contested */
beadId: string;
/** Workers working on this bead */
workers: string[];
/** Timestamp when collision was detected */
detectedAt: number;
/** Events that triggered the collision */
events: LogEvent[];
/** Whether the collision is still active */
isActive: boolean;
/** Collision severity based on operation types */
severity: 'warning' | 'critical';
}
/**
* Task collision - when workers work on tasks that may conflict
*/
export interface TaskCollision {
/** Type of collision */
type: 'directory' | 'related_files' | 'dependency';
/** Human-readable description */
description: string;
/** Workers involved */
workers: string[];
/** Affected paths/beads */
affectedResources: string[];
/** Timestamp when collision was detected */
detectedAt: number;
/** Whether the collision is still active */
isActive: boolean;
/** Risk level */
riskLevel: 'low' | 'medium' | 'high';
}
/**
* Collision alert for user notification
*/
export interface CollisionAlert {
/** Unique alert ID */
id: string;
/** Alert type */
type: 'file' | 'bead' | 'task';
/** Severity level */
severity: 'info' | 'warning' | 'error' | 'critical';
/** Human-readable title */
title: string;
/** Detailed description */
description: string;
/** Workers involved */
workers: string[];
/** Timestamp when alert was generated */
timestamp: number;
/** Whether the alert has been acknowledged */
acknowledged: boolean;
/** Related collision data */
collision: FileCollision | BeadCollision | TaskCollision;
/** Suggested resolution */
suggestion?: string;
}
export interface EventStore {
/** Add an event to the store */
add(event: LogEvent): void;
/** Query events with optional filter */
query(filter?: EventFilter): LogEvent[];
/** Query events sorted by (worker, sequence), falling back to ts */
queryOrdered(filter?: EventFilter): LogEvent[];
/** Get worker info */
getWorker(workerId: string): WorkerInfo | undefined;
/** Get all workers */
getWorkers(): WorkerInfo[];
/** Clear all events */
clear(): void;
/** Get all active collisions */
getCollisions(): FileCollision[];
/** Get collisions for a specific worker */
getWorkerCollisions(workerId: string): FileCollision[];
}
// ============================================
// Error Grouping Types
// ============================================
/**
* Error fingerprint - used to identify similar errors
*/
export interface ErrorFingerprint {
/** Normalized pattern signature (e.g., "ECONNREFUSED_*:*" for connection errors) */
signature: string;
/** Category of error (network, permission, validation, etc.) */
category: ErrorCategory;
/** Original error message (first occurrence) */
sampleMessage: string;
/** Hash of the signature for quick comparison */
hash: string;
}
/**
* Error categories for grouping
*/
export type ErrorCategory =
| 'network' // Connection errors, timeouts, DNS issues
| 'permission' // Auth failures, access denied
| 'validation' // Invalid input, schema errors
| 'resource' // Out of memory, disk full, quota exceeded
| 'not_found' // File not found, 404 errors
| 'timeout' // Operation timed out
| 'syntax' // Parse errors, malformed data
| 'tool' // Tool-specific failures
| 'unknown'; // Uncategorized errors
/**
* Grouped error - clusters similar errors together
*/
export interface ErrorGroup {
/** Unique group ID */
id: string;
/** Fingerprint identifying this group */
fingerprint: ErrorFingerprint;
/** All error events in this group */
events: LogEvent[];
/** First occurrence timestamp */
firstSeen: number;
/** Most recent occurrence timestamp */
lastSeen: number;
/** Number of occurrences */
count: number;
/** Workers that have encountered this error */
affectedWorkers: string[];
/** Whether this error group is currently active (seen recently) */
isActive: boolean;
/** Severity based on frequency and recency */
severity: 'low' | 'medium' | 'high' | 'critical';
}
/**
* Options for error grouping
*/
export interface ErrorGroupingOptions {
/** Time window to consider errors as active (ms), default 5 minutes */
activeWindowMs?: number;
/** Minimum occurrences for high severity */
highSeverityThreshold?: number;
/** Minimum occurrences for critical severity */
criticalSeverityThreshold?: number;
/** Maximum groups to track */
maxGroups?: number;
}
// ============================================
// Session Replay Types
// ============================================
export type ReplaySpeed = 0.5 | 1 | 2 | 5 | 10;
export type ReplayState = 'idle' | 'playing' | 'paused' | 'ended';
export interface ReplaySession {
/** Unique session identifier */
id: string;
/** Source log file path */
sourcePath: string;
/** Total events in the session */
totalEvents: number;
/** Current playback position (event index) */
currentIndex: number;
/** Playback state */
state: ReplayState;
/** Playback speed multiplier */
speed: ReplaySpeed;
/** Start timestamp of session (first event) */
startTime: number;
/** End timestamp of session (last event) */
endTime: number;
/** Filter applied during replay */
filter?: EventFilter;
}
export interface ReplayControls {
/** Start or resume playback */
play(): void;
/** Pause playback */
pause(): void;
/** Toggle play/pause */
toggle(): void;
/** Step to next event */
stepForward(): void;
/** Step to previous event */
stepBackward(): void;
/** Jump to specific event index */
seekTo(index: number): void;
/** Set playback speed */
setSpeed(speed: ReplaySpeed): void;
/** Stop and reset replay */
reset(): void;
}
// ============================================
// File Heatmap Types
// ============================================
/**
* Heat level for a file based on modification frequency
*/
export type HeatLevel = 'cold' | 'warm' | 'hot' | 'critical';
/**
* Worker contribution to a file's modification history
*/
export interface WorkerFileContribution {
/** Worker ID */
workerId: string;
/** Number of modifications by this worker */
modifications: number;
/** Last modification timestamp */
lastModified: number;
/** Percentage of total modifications (0-100) */
percentage: number;
}
/**
* Single file entry in the heatmap
*/
export interface FileHeatmapEntry {
/** File path */
path: string;
/** Total modification count */
modifications: number;
/** Heat level based on frequency */
heatLevel: HeatLevel;
/** Workers who have modified this file */
workers: WorkerFileContribution[];
/** First modification timestamp */
firstModified: number;
/** Most recent modification timestamp */
lastModified: number;
/** Whether this file is currently being modified by multiple workers */
hasCollision: boolean;
/** Number of workers currently active on this file */
activeWorkers: number;
/** Average time between modifications (ms) */
avgModificationInterval: number;
}
/**
* Options for heatmap generation
*/
export interface HeatmapOptions {
/** Minimum modifications to be included in heatmap */
minModifications?: number;
/** Maximum entries to return */
maxEntries?: number;
/** Sort by: 'modifications' | 'recent' | 'workers' | 'collisions' */
sortBy?: 'modifications' | 'recent' | 'workers' | 'collisions';
/** Filter by directory prefix */
directoryFilter?: string;
/** Only show files with collisions */
collisionsOnly?: boolean;
}
/**
* Statistics for the entire file heatmap
*/
export interface FileHeatmapStats {
/** Total files being tracked */
totalFiles: number;
/** Total modifications across all files */
totalModifications: number;
/** Files with collisions */
collisionFiles: number;
/** Files currently being modified */
activeFiles: number;
/** Heat level distribution */
heatDistribution: Record<HeatLevel, number>;
/** Most active directory */
mostActiveDirectory: string;
/** Average modifications per file */
avgModificationsPerFile: number;
}
/**
* Heatmap snapshot at a specific point in time
*/
export interface HeatmapSnapshot {
/** Timestamp of this snapshot */
timestamp: number;
/** Heatmap entries at this point in time */
entries: FileHeatmapEntry[];
/** Statistics at this point in time */
stats: FileHeatmapStats;
}
/**
* Time-series heatmap data for animation
*/
export interface HeatmapTimelapse {
/** Start timestamp of the timelapse */
startTimestamp: number;
/** End timestamp of the timelapse */
endTimestamp: number;
/** Time interval between snapshots (ms) */
interval: number;
/** Total number of snapshots */
totalSnapshots: number;
/** Array of heatmap snapshots */
snapshots: HeatmapSnapshot[];
}
/**
* Options for generating timelapse data
*/
export interface TimelapseOptions {
/** Start timestamp (defaults to oldest modification) */
startTimestamp?: number;
/** End timestamp (defaults to now) */
endTimestamp?: number;
/** Number of snapshots to generate (default 30) */
snapshotCount?: number;
/** Minimum modifications to be included */
minModifications?: number;
/** Maximum entries per snapshot */
maxEntries?: number;
/** Sort mode for snapshots */
sortBy?: 'modifications' | 'recent' | 'workers' | 'collisions';
/** Filter by directory prefix */
directoryFilter?: string;
/** Only show files with collisions */
collisionsOnly?: boolean;
}
// ============================================
// File Anomaly Detection Types
// ============================================
/**
* Types of file activity anomalies
*/
export type AnomalyType =
| 'config_modification' // Config file modified outside expected context
| 'high_frequency' // Unusually high modification rate
| 'burst_activity' // Sudden burst of modifications
| 'unusual_pattern' // Activity pattern outside normal bounds
| 'sensitive_file'; // Sensitive file (secrets, credentials) touched
/**
* Severity level for anomalies
*/
export type AnomalySeverity = 'info' | 'warning' | 'critical';
/**
* Detected file anomaly
*/
export interface FileAnomaly {
/** File path with anomaly */
path: string;
/** Type of anomaly detected */
type: AnomalyType;
/** Severity level */
severity: AnomalySeverity;
/** Human-readable description */
message: string;
/** When the anomaly was detected */
detectedAt: number;
/** Supporting data for the anomaly */
details: {
/** Modification count involved */
modifications?: number;
/** Workers involved */
workers?: string[];
/** Time span of anomalous activity (ms) */
timeSpan?: number;
/** Expected normal value */
expectedValue?: number;
/** Actual observed value */
actualValue?: number;
/** Additional context */
context?: Record<string, unknown>;
};
}
/**
* Options for anomaly detection
*/
export interface AnomalyDetectionOptions {
/** Minimum modifications to consider for anomaly detection */
minModifications?: number;
/** Threshold multiplier for high-frequency detection (e.g., 3 = 3x average) */
frequencyThreshold?: number;
/** Time window for burst detection (ms) */
burstWindow?: number;
/** Minimum burst count to trigger anomaly */
burstThreshold?: number;
/** Custom patterns for sensitive files */
sensitivePatterns?: string[];
}
/**
* Statistics for anomaly detection
*/
export interface AnomalyStats {
/** Total anomalies detected */
totalAnomalies: number;
/** Count by type */
byType: Record<AnomalyType, number>;
/** Count by severity */
bySeverity: Record<AnomalySeverity, number>;
/** Files with most anomalies */
topAnomalyFiles: Array<{
path: string;
count: number;
types: AnomalyType[];
}>;
}
// ============================================
// Dependency DAG Types
// ============================================
/**
* Bead status type
*/
export type BeadStatus = 'open' | 'in_progress' | 'blocked' | 'completed' | 'closed' | 'deferred';
/**
* Single node in the dependency graph
*/
export interface BeadNode {
/** Bead ID (e.g., 'bd-abc123') */
id: string;
/** Bead title */
title: string;
/** Current status */
status: BeadStatus;
/** Priority level (0-4) */
priority: number;
/** Depth in the dependency tree (0 = root) */
depth: number;
/** Number of dependents (beads that depend on this) */
dependentCount: number;
/** Number of dependencies (beads this depends on) */
dependencyCount: number;
/** Whether this is on the critical path */
isCriticalPath: boolean;
/** Estimated effort (if available) */
estimatedEffort?: number;
}
/**
* Edge in the dependency graph
*/
export interface DependencyEdge {
/** Source bead ID (the one that depends) */
from: string;
/** Target bead ID (the dependency) */
to: string;
/** Whether this edge is part of the critical path */
isCritical: boolean;
}
/**
* Connected component in the dependency graph
*/
export interface DagComponent {
/** All nodes in this component */
nodes: BeadNode[];
/** All edges in this component */
edges: DependencyEdge[];
/** Root nodes (no incoming edges) */
roots: string[];
/** Whether this component contains cycles */
hasCycle: boolean;
/** Critical path through this component (bead IDs) */
criticalPath: string[];
/** Total depth of the component */
maxDepth: number;
}
/**
* Full dependency graph
*/
export interface DependencyGraph {
/** All connected components */
components: DagComponent[];
/** Total nodes across all components */
totalNodes: number;
/** Total edges across all components */
totalEdges: number;
/** Total components */
totalComponents: number;
/** Overall critical path (longest path across all components) */
globalCriticalPath: string[];
/** Timestamp when graph was generated */
generatedAt: number;
}
/**
* Options for DAG visualization
*/
export interface DagOptions {
/** Filter by status */
status?: BeadStatus | 'all';
/** Filter by priority range */
minPriority?: number;
maxPriority?: number;
/** Show only critical path */
criticalOnly?: boolean;
/** Maximum depth to display */
maxDepth?: number;
/** Sort order: 'priority' | 'depth' | 'dependents' */
sortBy?: 'priority' | 'depth' | 'dependents';
/** Include closed/completed beads */
includeClosed?: boolean;
}
/**
* Statistics about the dependency graph
*/
export interface DagStats {
/** Total beads tracked */
totalBeads: number;
/** Blocked beads count */
blockedCount: number;
/** Ready beads (unblocked, open) */
readyCount: number;
/** Average dependencies per bead */
avgDependencies: number;
/** Average dependents per bead */
avgDependents: number;
/** Maximum depth found */
maxDepth: number;
/** Number of cycles detected */
cycleCount: number;
/** Critical path length */
criticalPathLength: number;
/** Beads on critical path */
criticalPathBeads: number;
}
// ============================================
// Span-based DAG Types (OTLP span hierarchy)
// ============================================
/**
* A node in the span DAG, built from paired .started/.finished NeedleEvents
* derived from OTLP spans. Parent-child relationships come from
* parent_span_id rather than bead dependencies.
*/
export interface SpanNode {
/** OTLP span ID */
span_id: string;
/** OTLP trace ID */
trace_id: string;
/** Parent span ID (null for root spans) */
parent_span_id: string | null;
/** Span name (e.g. "bead.lifecycle", "tool.call", "llm.request") */
name: string;
/** Worker that emitted this span */
worker_id: string;
/** Associated bead ID (null if not associated) */
bead_id: string | null;
/** Start timestamp (ms from .started event, null if not available) */
start_ts: number | null;
/** End timestamp (ms from .finished event, null if not available) */
end_ts: number | null;
/** Duration in ms (null if not available) */
duration_ms: number | null;
/** Span completion status */
status: 'ok' | 'error' | 'unknown';
/** Child spans (populated during tree construction) */
children: SpanNode[];
/** Remaining span attributes */
attributes: Record<string, unknown>;
}
/**
* A span hierarchy DAG built from OTLP span events.
* Unlike the bead DependencyGraph (which uses br graph --json),
* the SpanDag is built from the live event stream using parent_span_id
* for parent-child linkage.
*/
export interface SpanDag {
/** Root spans (no parent_span_id or orphaned) */
roots: SpanNode[];
/** All spans indexed by span_id */
allSpans: Map<string, SpanNode>;
/** Spans grouped by trace_id */
traces: Map<string, SpanNode[]>;
}
/**
* JSON-serializable span DAG response for API endpoints.
* Unlike SpanDag (which uses Maps for internal use), this format
* is designed for HTTP responses and JSON serialization.
*/
export interface SpanDagResponse {
/** Root spans (no parent_span_id or orphaned) */
roots: SpanNode[];
/** Total number of spans in the DAG */
totalSpans: number;
/** Trace summary with span counts */
traces: Array<{ trace_id: string; span_count: number }>;
}
// ============================================
// Git Event Types
// ============================================
/**
* Type of git event
*/
export type GitEventType =
| 'status' // Git status output (staged, unstaged, untracked)
| 'commit' // Git commit
| 'branch' // Branch information
| 'diff'; // Git diff output
/**
* File status in git
*/
export type GitFileStatus =
| 'added'
| 'modified'
| 'deleted'
| 'renamed'
| 'copied'
| 'untracked'
| 'unmerged';
/**
* Single file change in git
*/
export interface GitFileChange {
/** File path */
path: string;
/** Status of the file */
status: GitFileStatus;
/** Original path (for renames) */
originalPath?: string;
/** Staging area status */
staged: boolean;
}
/**
* Base interface for all git events
*/
export interface GitEventBase {
/** Unique event identifier */
id: string;
/** Type of git event */
type: GitEventType;
/** Unix timestamp in milliseconds */
ts: number;
/** Worker identifier */
worker: string;
/** Associated bead/task ID (if any) */
bead?: string;
}
/**
* Git status event
*/
export interface GitStatusEvent extends GitEventBase {
type: 'status';
/** Current branch name */
branch: string;
/** Commit hash (HEAD) */
commit?: string;
/** Staged file changes */
staged: GitFileChange[];
/** Unstaged file changes */
unstaged: GitFileChange[];
/** Untracked files */
untracked: string[];
/** Commits ahead of remote */
ahead?: number;
/** Commits behind remote */
behind?: number;
/** Remote tracking branch */
tracking?: string;
}
/**
* Git commit event
*/
export interface GitCommitEvent extends GitEventBase {
type: 'commit';
/** Commit hash */
hash: string;
/** Commit message */
message: string;
/** Branch name */
branch?: string;
/** Author name */
author?: string;
/** Author email */
email?: string;
/** Parent commit hash(es) */
parents?: string[];
/** Files changed in this commit */
files?: GitFileChange[];
}
/**
* Git branch event
*/
export interface GitBranchEvent extends GitEventBase {
type: 'branch';
/** Current branch name */
current: string;
/** All local branches */
branches?: string[];
/** Remote tracking branch */
tracking?: string;
/** Commits ahead of tracking */
ahead?: number;
/** Commits behind tracking */
behind?: number;
}
/**
* Git diff event
*/
export interface GitDiffEvent extends GitEventBase {
type: 'diff';
/** Diff target (e.g., 'HEAD', 'origin/main') */
target: string;
/** Files with changes */
files: GitFileChange[];
/** Total lines added */
linesAdded: number;
/** Total lines deleted */
linesDeleted: number;
/** Diff content (may be truncated) */
content?: string;
/** Whether diff content is truncated */
isTruncated?: boolean;
}
/**
* Union type for all git events
*/
export type GitEvent =
| GitStatusEvent
| GitCommitEvent
| GitBranchEvent
| GitDiffEvent;
/**
* Options for parsing git events
*/
export interface GitParseOptions {
/** Maximum diff content length before truncation */
maxDiffLength?: number;
/** Include file change details */
includeFileChanges?: boolean;
/** Maximum files to track in a single event */
maxFiles?: number;
}
// ============================================
// PR Preview Types
// ============================================
/**
* File change with diff statistics
*/
export interface PRFileChange extends GitFileChange {
/** Lines added in this file */
linesAdded: number;
/** Lines deleted in this file */
linesDeleted: number;
/** Worker who made the change */
worker?: string;
}
/**
* Upstream commit that might conflict
*/
export interface UpstreamCommit {
/** Commit hash */
hash: string;
/** Commit message */
message: string;
/** Author name */
author?: string;
/** Files changed in this commit */
files: string[];
/** Timestamp */
ts: number;
}
/**
* Potential conflict information
*/
export interface PotentialConflict {
/** Whether there are upstream commits */
hasUpstreamCommits: boolean;
/** Number of upstream commits */
upstreamCommitCount: number;
/** Upstream commits that might conflict */
upstreamCommits: UpstreamCommit[];
/** Files that might have conflicts */
conflictingFiles: string[];
/** Whether rebase is recommended */
rebaseRecommended: boolean;
/** Reason for rebase recommendation */
rebaseReason?: string;
}
/**
* PR Preview data
*/
export interface PRPreview {
/** Generated PR title */
title: string;
/** Generated PR description */
description: string;
/** Commit message preview */
commitMessage: string;
/** All files changed */
files: PRFileChange[];
/** Total lines added */
totalLinesAdded: number;
/** Total lines deleted */
totalLinesDeleted: number;
/** Number of files changed */
filesChanged: number;
/** Potential conflicts with upstream */
conflicts: PotentialConflict;
/** Source branch */
sourceBranch: string;
/** Target branch (usually main) */
targetBranch: string;
/** Number of commits ahead */
ahead: number;
/** Number of commits behind */
behind: number;
/** Whether there are uncommitted changes */
hasUncommittedChanges: boolean;
/** Timestamp when preview was generated */
generatedAt: number;
}
// ============================================
// Cross-Reference Types
// ============================================
/**
* Type of entity that can be cross-referenced
*/
export type CrossReferenceEntityType = 'event' | 'bead' | 'file' | 'worker' | 'session';
/**
* A single cross-reference link
*/
export interface CrossReferenceLink {
/** Unique link ID */
id: string;
/** Source entity type */
sourceType: CrossReferenceEntityType;
/** Source entity ID */
sourceId: string;
/** Target entity type */
targetType: CrossReferenceEntityType;
/** Target entity ID */
targetId: string;
/** Relationship type */
relationship: CrossReferenceRelationship;
/** Strength of the relationship (0-1) */
strength: number;
/** When this link was detected */
detectedAt: number;
/** Optional context about why this link exists */
context?: string;
}
/**
* Types of relationships between entities
*/
export type CrossReferenceRelationship =
| 'same_bead' // Events working on the same bead/task
| 'same_file' // Events modifying the same file
| 'same_worker' // Events from the same worker
| 'temporal_proximity' // Events happening close together in time
| 'same_session' // Events in the same worker session
| 'dependency' // One bead depends on another
| 'collision' // Workers colliding on the same file
| 'parent_child' // Hierarchical relationship
| 'error_related' // Events related to the same error
| 'tool_sequence'; // Tool calls that form a logical sequence
/**
* A cross-reference entity with its links
*/
export interface CrossReferenceEntity {
/** Entity type */
type: CrossReferenceEntityType;
/** Entity ID */
id: string;
/** Human-readable label */
label: string;
/** All links from this entity */
outgoingLinks: CrossReferenceLink[];
/** All links to this entity */
incomingLinks: CrossReferenceLink[];
/** Related entities grouped by type */
relatedEntities: Map<CrossReferenceEntityType, CrossReferenceLink[]>;
/** Total link count */
linkCount: number;
/** Most recent link timestamp */
lastLinkedAt: number;
/** First seen timestamp */
firstSeen: number;
/** Number of occurrences */
occurrenceCount: number;
}
/**
* Options for cross-reference queries
*/
export interface CrossReferenceQueryOptions {
/** Filter by source entity type */
sourceType?: CrossReferenceEntityType;
/** Filter by target entity type */
targetType?: CrossReferenceEntityType;
/** Filter by relationship type */
relationship?: CrossReferenceRelationship;
/** Minimum relationship strength */
minStrength?: number;
/** Time range start */
since?: number;
/** Time range end */
until?: number;
/** Maximum results */
limit?: number;
}
/** Alias for backward compatibility */
export type CrossReferenceFilter = CrossReferenceQueryOptions;
/**
* Statistics about cross-references
*/
export interface CrossReferenceStats {
/** Total links tracked */
totalLinks: number;
/** Total entities tracked */
totalEntities: number;
/** Links by relationship type */
byRelationship: Record<CrossReferenceRelationship, number>;
/** Entities by type */
byEntityType: Record<CrossReferenceEntityType, number>;
/** Most linked entities */
mostLinked: CrossReferenceEntity[];
/** Recent links */
recentLinks: CrossReferenceLink[];
}
/**
* A navigation path through cross-references
*/
export interface CrossReferencePath {
/** Starting entity */
start: CrossReferenceEntity;
/** Ending entity */
end: CrossReferenceEntity;
/** Path steps */
steps: CrossReferenceLink[];
/** Total path length */
length: number;
/** Path description */
description: string;
}
// ============================================
// Recovery Playbook Types
// ============================================
/**
* Priority level for recovery actions
*/
export type RecoveryPriority = 'immediate' | 'high' | 'normal' | 'low';
/**
* Type of recovery action
*/
export type RecoveryActionType =
| 'retry' // Simple retry the operation
| 'backoff' // Retry with exponential backoff
| 'alternative' // Use alternative approach
| 'escalate' // Escalate to human
| 'skip' // Skip and continue
| 'fix_config' // Fix configuration
| 'install_dep' // Install missing dependency
| 'fix_permissions' // Fix file permissions
| 'cleanup' // Clean up resources
| 'restart' // Restart service/process
| 'investigate'; // Requires further investigation
/**
* A single recovery action step
*/
export interface RecoveryAction {
/** Unique action ID */
id: string;
/** Action type */
type: RecoveryActionType;
/** Human-readable title */
title: string;
/** Detailed description of the action */
description: string;
/** Priority for ordering actions */
priority: RecoveryPriority;
/** Whether this action can be automated */
automated: boolean;
/** Command or code snippet to execute (if applicable) */
command?: string;
/** Expected outcome */
expectedOutcome?: string;
/** Prerequisites before this action */
prerequisites?: string[];
/** Risk level of this action */
riskLevel?: 'safe' | 'moderate' | 'risky';
/** Estimated time to complete (seconds) */
estimatedTime?: number;
}
/**
* A recovery playbook entry mapping error patterns to actions
*/
export interface RecoveryPlaybookEntry {
/** Unique playbook ID */
id: string;
/** Error category this applies to */
category: ErrorCategory;
/** Title for this recovery playbook */
title: string;
/** Description of the error pattern */
description: string;
/** Pattern to match error messages */
patterns: RegExp[];
/** Recovery actions in order */
actions: RecoveryAction[];
/** When this playbook was created */
createdAt: number;
/** When this playbook was last updated */
updatedAt: number;
/** Tags for categorization */
tags: string[];
}
/**
* A specific recovery suggestion for an error
*/
export interface RecoverySuggestion {
/** Unique suggestion ID */
id: string;
/** The error group this suggestion is for */
errorGroupId: string;
/** The playbook entry this came from (if any) */
playbookId?: string;
/** Error category */
category: ErrorCategory;
/** Title for the suggestion */
title: string;
/** Summary of the error */
errorSummary: string;
/** Recommended actions in order */
actions: RecoveryAction[];
/** When this suggestion was generated */
generatedAt: number;
/** Confidence level (0-1) */
confidence: number;
/** Related workers affected */
affectedWorkers: string[];
/** Similar past errors (if any) */
relatedErrors?: string[];
/** Whether this is still relevant */
isActive: boolean;
}
/**
* Options for recovery suggestion generation
*/
export interface RecoveryOptions {
/** Maximum actions to include per suggestion */
maxActions?: number;
/** Only include automated actions */
automatedOnly?: boolean;
/** Minimum confidence threshold */
minConfidence?: number;
/** Filter by category */
category?: ErrorCategory;
/** Filter by worker */
workerId?: string;
}
/**
* Statistics about recovery suggestions
*/
export interface RecoveryStats {
/** Total suggestions generated */
totalSuggestions: number;
/** Active suggestions */
activeSuggestions: number;
/** Suggestions by category */
byCategory: Record<ErrorCategory, number>;
/** Automated vs manual actions */
automatedActions: number;
manualActions: number;
/** Average confidence */
avgConfidence: number;
/** Most common recovery action types */
topActionTypes: Array<{ type: RecoveryActionType; count: number }>;
}
// ============================================
// Session Digest Types
// ============================================
/**
* Bead completion summary
*/
export interface BeadCompletion {
/** Bead ID */
beadId: string;
/** Worker that completed the bead */
workerId: string;
/** Completion timestamp */
completedAt: number;
/** Duration in milliseconds */
durationMs?: number;
}
/**
* File modification summary
*/
export interface FileModificationSummary {
/** File path */
path: string;
/** Number of modifications */
modifications: number;
/** Workers who modified this file */
workers: string[];
/** Tools used */
tools: string[];
}
/**
* Error occurrence in session
*/
export interface ErrorOccurrence {
/** Error message */
message: string;
/** Error category */
category: ErrorCategory;
/** Worker that encountered the error */
workerId: string;
/** Timestamp */
timestamp: number;
/** Error fingerprint */
fingerprint?: string;
}
/**
* Worker session summary
*/
export interface WorkerSessionSummary {
/** Worker ID */
workerId: string;
/** Beads completed */
beadsCompleted: number;
/** Files modified */
filesModified: number;
/** Errors encountered */
errorsEncountered: number;
/** Total events */
totalEvents: number;
/** Active time in milliseconds */
activeTimeMs: number;
/** First activity timestamp */
firstActivity: number;
/** Last activity timestamp */
lastActivity: number;
}
/**
* Complete session digest
*/
export interface SessionDigest {
/** Session ID or identifier */
sessionId: string;
/** Session start timestamp */
startTime: number;
/** Session end timestamp */
endTime: number;
/** Total duration in milliseconds */
durationMs: number;
/** Beads completed */
beadsCompleted: BeadCompletion[];
/** Files modified */
filesModified: FileModificationSummary[];
/** Errors encountered */
errors: ErrorOccurrence[];
/** Worker summaries */
workers: WorkerSessionSummary[];
/** Token usage and cost */
cost: {
totalTokens: number;
inputTokens: number;
outputTokens: number;
estimatedCostUsd: number;
};
/** Overall statistics */
stats: {
totalEvents: number;
totalWorkers: number;
totalBeads: number;
totalFiles: number;
totalErrors: number;
avgEventsPerWorker: number;
avgBeadsPerWorker: number;
};
}
/**
* Options for session digest generation
*/
export interface SessionDigestOptions {
/** Start time filter */
startTime?: number;
/** End time filter */
endTime?: number;
/** Include only specific workers */
workers?: string[];
/** Include error details */
includeErrors?: boolean;
/** Include cost breakdown */
includeCost?: boolean;
/** Maximum files to list */
maxFiles?: number;
/** Maximum errors to list */
maxErrors?: number;
}
// ============================================
// Worker Analytics Types
// ============================================
/**
* Time window for aggregation
*/
export type TimeWindow = 'hour' | 'day' | 'week' | 'all';
/**
* Worker analytics metrics for a specific time period
*/
export interface WorkerMetrics {
/** Worker ID */
workerId: string;
/** Time period start (Unix timestamp) */
periodStart: number;
/** Time period end (Unix timestamp) */
periodEnd: number;
/** Total beads completed in this period */
beadsCompleted: number;
/** Beads per hour (rate) */
beadsPerHour: number;
/** Average completion time per bead (milliseconds) */
avgCompletionTimeMs: number;
/** Total errors encountered */
errorCount: number;
/** Error rate (errors per bead) */
errorRate: number;
/** Total cost incurred (USD) */
totalCostUsd: number;
/** Cost per bead (USD) */
costPerBead: number;
/** Total active time (milliseconds) */
activeTimeMs: number;
/** Total idle time (milliseconds) */
idleTimeMs: number;
/** Idle percentage (0-100) */
idlePercentage: number;
/** Total events processed */
totalEvents: number;
/** Total tokens used */
totalTokens: number;
/** Tokens per bead */
tokensPerBead: number;
/** Efficiency score (0-1, calculated from active time vs idle time) */
efficiencyScore: number;
/** Performance trend data */
trend?: {
direction: 'improving' | 'declining' | 'stable';
confidence: number;
factors: string[];
};
}
/**
* Time-series data point for worker metrics
*/
export interface MetricsDataPoint {
/** Timestamp of this data point */
timestamp: number;
/** Worker ID */
workerId: string;
/** Metrics snapshot at this time */
metrics: Partial<WorkerMetrics>;
}
/**
* Worker performance trend
*/
export interface PerformanceTrend {
/** Worker ID */
workerId: string;
/** Metric being tracked */
metric: keyof WorkerMetrics;
/** Time-series data points */
dataPoints: MetricsDataPoint[];
/** Trend direction: 'improving' | 'declining' | 'stable' */
trend: 'improving' | 'declining' | 'stable';
/** Percentage change from first to last data point */
changePercent: number;
/** Average value across all data points */
average: number;
/** Minimum value */
min: number;
/** Maximum value */
max: number;
}
/**
* Aggregated analytics across all workers
*/
export interface AggregatedAnalytics {
/** Time period covered */
periodStart: number;
periodEnd: number;
/** Total workers tracked */
totalWorkers: number;
/** Total beads completed */
totalBeadsCompleted: number;
/** Average beads per hour across all workers */
avgBeadsPerHour: number;
/** Average completion time across all workers */
avgCompletionTimeMs: number;
/** Total errors across all workers */
totalErrors: number;
/** Overall error rate */
overallErrorRate: number;
/** Total cost across all workers */
totalCostUsd: number;
/** Average cost per bead */
avgCostPerBead: number;
/** Top performers (sorted by beads completed) */
topPerformers: WorkerMetrics[];
/** Workers with highest error rates */
highErrorRateWorkers: WorkerMetrics[];
/** Most cost-efficient workers (lowest cost per bead) */
costEfficientWorkers: WorkerMetrics[];
/** Number of currently active workers */
activeWorkerCount: number;
/** Total tokens used across all workers */
totalTokens: number;
/** Average efficiency across all workers */
avgEfficiency: number;
/** Underperforming workers (high error rate or low efficiency) */
underperformers: WorkerMetrics[];
}
/**
* Options for worker analytics
*/
export interface WorkerAnalyticsOptions {
/** Time window for aggregation */
timeWindow?: TimeWindow;
/** Custom start time (overrides timeWindow) */
startTime?: number;
/** Custom end time (overrides timeWindow) */
endTime?: number;
/** Filter by specific worker IDs */
workerIds?: string[];
/** Minimum beads completed to be included */
minBeadsCompleted?: number;
/** Maximum workers to return in rankings */
maxWorkers?: number;
/** Include time-series data */
includeTimeSeries?: boolean;
/** Time-series data point interval (milliseconds) */
timeSeriesInterval?: number;
}
/**
* Worker analytics store interface
*/
export interface WorkerAnalyticsStore {
/** Process an event and update analytics */
processEvent(event: LogEvent): void;
/** Get metrics for a specific worker */
getWorkerMetrics(workerId: string, options?: WorkerAnalyticsOptions): WorkerMetrics | undefined;
/** Get metrics for all workers */
getAllWorkerMetrics(options?: WorkerAnalyticsOptions): WorkerMetrics[];
/** Get aggregated analytics */
getAggregatedAnalytics(options?: WorkerAnalyticsOptions): AggregatedAnalytics;
/** Get performance trends */
getPerformanceTrends(workerId: string, metric: keyof WorkerMetrics, options?: WorkerAnalyticsOptions): PerformanceTrend;
/** Get time-series data */
getTimeSeriesData(workerId: string, options?: WorkerAnalyticsOptions): MetricsDataPoint[];
/** Clear all analytics data */
clear(): void;
/** Get analytics summary as formatted string */
getSummary(options?: WorkerAnalyticsOptions): string;
}
/**
* Worker comparison result - side-by-side comparison of two workers
*/
export interface WorkerComparison {
/** First worker metrics */
worker1: WorkerMetrics;
/** Second worker metrics */
worker2: WorkerMetrics;
/** Metric differences (worker1 - worker2, positive means worker1 is higher) */
differences: {
beadsCompleted: number;
beadsPerHour: number;
avgCompletionTimeMs: number;
errorRate: number;
costPerBead: number;
efficiencyScore: number;
};
/** Percentage differences (worker1 relative to worker2) */
percentDifferences: {
beadsCompleted: number;
beadsPerHour: number;
avgCompletionTimeMs: number;
errorRate: number;
costPerBead: number;
efficiencyScore: number;
};
/** Which worker is better for each metric */
betterWorker: {
beadsCompleted: 'worker1' | 'worker2' | 'tie';
beadsPerHour: 'worker1' | 'worker2' | 'tie';
avgCompletionTimeMs: 'worker1' | 'worker2' | 'tie';
errorRate: 'worker1' | 'worker2' | 'tie';
costPerBead: 'worker1' | 'worker2' | 'tie';
efficiencyScore: 'worker1' | 'worker2' | 'tie';
};
/** Overall winner based on majority of metrics */
overallWinner: 'worker1' | 'worker2' | 'tie';
/** Score tally for determining overall winner */
score: { worker1: number; worker2: number };
}
// ============================================
// Semantic Narrative Types
// ============================================
/**
* Narrative style for summarization
*/
export type NarrativeStyle = 'brief' | 'detailed' | 'timeline' | 'technical';
/**
* Event pattern types that drive narrative generation
*/
export type EventPattern =
| 'bead_started' // Worker started working on a bead
| 'bead_completed' // Worker completed a bead
| 'file_editing' // Editing files
| 'file_created' // Creating new files
| 'testing' // Running tests
| 'debugging' // Debugging errors
| 'git_operations' // Git commits, pushes, etc.
| 'dependency_install' // Installing dependencies
| 'collision_detected' // Workers colliding
| 'error_recovery' // Recovering from errors
| 'iteration' // Iterative refinement
| 'investigation' // Investigating/researching
| 'tool_usage' // Tool usage patterns
| 'error_handling' // Error handling patterns
| 'task_completion' // Task completion patterns
| 'exploration' // Exploration patterns
| 'planning' // Planning patterns
| 'research'; // Research patterns
/**
* A single narrative segment describing a sequence of events
*/
export interface NarrativeSegment {
/** Unique segment ID */
id: string;
/** Event pattern this segment describes */
pattern: EventPattern;
/** Natural language summary */
summary: string;
/** Detailed narrative (if available) */
details?: string;
/** Start timestamp */
startTime: number;
/** End timestamp */
endTime: number;
/** Duration in milliseconds */
durationMs: number;
/** Worker ID */
workerId: string;
/** Associated bead (if any) */
beadId?: string;
/** Events that comprise this segment */
events: LogEvent[];
/** Key entities mentioned (files, tools, etc.) */
entities: {
files?: string[];
tools?: string[];
beads?: string[];
errors?: string[];
};
/** Confidence in pattern detection (0-1) */
confidence: number;
/** Whether this segment is still active/ongoing */
isActive: boolean;
}
/**
* A complete narrative for a worker session or time period
*/
export interface SemanticNarrative {
/** Narrative ID */
id: string;
/** Worker ID (or 'all' for multi-worker narratives) */
workerId: string;
/** Narrative title */
title: string;
/** High-level summary (1-2 sentences) */
summary: string;
/** All narrative segments in chronological order */
segments: NarrativeSegment[];
/** Full narrative text */
fullNarrative: string;
/** Timeline of key events */
timeline: string[];
/** Start timestamp */
startTime: number;
/** End timestamp */
endTime: number;
/** Total duration */
durationMs: number;
/** Key accomplishments */
accomplishments: string[];
/** Challenges encountered */
challenges: string[];
/** Overall sentiment: 'productive' | 'struggling' | 'mixed' | 'idle' */
sentiment: 'productive' | 'struggling' | 'mixed' | 'idle';
/** Statistics */
stats: {
totalEvents: number;
segmentCount: number;
beadsWorked: number;
filesModified: number;
errorsEncountered: number;
toolsUsed: number;
};
/** When this narrative was generated */
generatedAt: number;
/** Whether this narrative is still being updated */
isLive: boolean;
}
/**
* Options for narrative generation
*/
export interface NarrativeOptions {
/** Narrative style */
style?: NarrativeStyle;
/** Filter by worker ID */
workerId?: string;
/** Filter by bead ID */
beadId?: string;
/** Time range start */
startTime?: number;
/** Time range end */
endTime?: number;
/** Minimum confidence for pattern detection */
minConfidence?: number;
/** Maximum segments to generate */
maxSegments?: number;
/** Include technical details */
includeTechnicalDetails?: boolean;
/** Include timeline */
includeTimeline?: boolean;
/** Group events by time window (ms) */
segmentWindowMs?: number;
/** Minimum events per segment */
minEventsPerSegment?: number;
}
/**
* Narrative update event - emitted when narrative changes
*/
export interface NarrativeUpdate {
/** Narrative ID */
narrativeId: string;
/** Update type */
type: 'segment_added' | 'segment_updated' | 'segment_completed' | 'narrative_completed';
/** Updated segment (if applicable) */
segment?: NarrativeSegment;
/** Timestamp of update */
timestamp: number;
/** New summary text */
summary?: string;
}
/**
* Semantic narrative manager interface
*/
export interface SemanticNarrativeManager {
/** Process an event and update narratives */
processEvent(event: LogEvent): void;
/** Generate narrative for a worker */
generateNarrative(workerId: string, options?: NarrativeOptions): SemanticNarrative;
/** Generate narrative for all workers */
generateAggregatedNarrative(options?: NarrativeOptions): SemanticNarrative;
/** Get current active narratives */
getActiveNarratives(): SemanticNarrative[];
/** Get narrative by ID */
getNarrative(narrativeId: string): SemanticNarrative | undefined;
/** Subscribe to narrative updates */
onUpdate(callback: (update: NarrativeUpdate) => void): () => void;
/** Clear all narratives */
clear(): void;
/** Get narrative as formatted string */
formatNarrative(narrative: SemanticNarrative, style?: NarrativeStyle): string;
}
// ============================================
// Worker Fleet Analytics Types
// ============================================
/**
* Duration bucket for bead completion histogram
*/
export type DurationBucket = '<5s' | '5-30s' | '30-120s' | '2-10m' | '10m+';
/**
* Model performance metrics
*/
export interface ModelPerformanceMetrics {
/** Model identifier (e.g., 'sonnet', 'glm-4.7', 'gpt-4o') */
model: string;
/** Provider (e.g., 'anthropic', 'openai', 'glm') */
provider: string;
/** Total beads completed */
beadsCompleted: number;
/** Average bead duration in ms */
avgDurationMs: number;
/** Median bead duration in ms */
medianDurationMs: number;
/** Duration distribution histogram */
durationDistribution: Record<DurationBucket, number>;
/** Success rate (0-1) */
successRate: number;
/** Total errors */
errorCount: number;
/** Total cost in USD */
totalCostUsd: number;
/** Average cost per bead */
avgCostPerBead: number;
}
/**
* Strand utilization metrics
*/
export interface StrandMetrics {
/** Strand name (pluck, mend, explore, weave, pulse, unravel, knot) */
strand: string;
/** Number of invocations */
invocations: number;
/** Number of successful invocations (found work) */
successCount: number;
/** Success rate (0-1) */
successRate: number;
/** Total time spent in ms */
totalTimeMs: number;
/** Average time per invocation in ms */
avgTimeMs: number;
/** Beads created by this strand (for weave, explore, etc.) */
beadsCreated?: number;
}
/**
* Completion quality indicator
*/
export interface CompletionQualityIndicator {
/** Bead ID */
beadId: string;
/** Worker that completed */
workerId: string;
/** Model used */
model: string;
/** Duration in ms */
durationMs: number;
/** Quality flags */
flags: {
/** Completed in <10s - suspicious shallow */
isShallow: boolean;
/** Had git commits */
hadCommits: boolean;
/** Was reopened after closure */
wasReopened: boolean;
/** Was claimed by multiple workers (race) */
hadClaimRace: boolean;
};
/** Completion timestamp */
completedAt: number;
}
/**
* Fleet time series data point
*/
export interface FleetTimeSeriesPoint {
/** Timestamp */
ts: number;
/** Active worker count */
activeWorkers: number;
/** Beads completed in this bucket */
beadsCompleted: number;
/** Errors in this bucket */
errors: number;
/** Cost in this bucket */
costUsd: number;
}
/**
* Workspace coverage info
*/
export interface WorkspaceCoverage {
/** Workspace path */
path: string;
/** Active workers in this workspace */
activeWorkers: number;
/** Total beads worked on */
beadsWorked: number;
/** Last activity timestamp */
lastActivity: number;
/** Worker IDs */
workers: string[];
}
/**
* Complete fleet analytics data
*/
export interface FleetAnalytics {
/** Time range covered */
timeRange: {
start: number;
end: number;
windowLabel: string;
};
/** Model performance breakdown */
modelPerformance: ModelPerformanceMetrics[];
/** Strand utilization */
strandMetrics: StrandMetrics[];
/** Quality indicators */
qualityIndicators: {
/** Total shallow completions (<10s) */
shallowCompletions: number;
/** Completions with git commits */
completionsWithCommits: number;
/** Reopened beads */
reopenedBeads: number;
/** Claim races */
claimRaces: number;
/** Recent suspicious completions */
suspiciousCompletions: CompletionQualityIndicator[];
};
/** Fleet overview */
fleetOverview: {
/** Total unique workers seen */
totalWorkers: number;
/** Currently active workers */
activeWorkers: number;
/** Total beads completed */
totalBeadsCompleted: number;
/** Total errors */
totalErrors: number;
/** Bead throughput per hour */
beadsPerHour: number;
/** Watchdog relaunch count */
watchdogRelaunchCount: number;
/** Workspace coverage */
workspaces: WorkspaceCoverage[];
};
/** Time series data */
timeSeries: FleetTimeSeriesPoint[];
}
/**
* Options for fleet analytics queries
*/
export interface FleetAnalyticsOptions {
/** Time window */
timeWindow?: TimeWindow;
/** Custom start time */
startTime?: number;
/** Custom end time */
endTime?: number;
/** Include time series data */
includeTimeSeries?: boolean;
/** Time series bucket size in minutes */
timeSeriesBucketMinutes?: number;
/** Filter by model */
modelFilter?: string;
/** Maximum suspicious completions to return */
maxSuspiciousCompletions?: number;
}