Skip to content

Architecture ​

Flightdeck uses a two-tier architecture that separates the orchestration server from the user interface. Agents run in-process via the AcpAdapter and AgentAcpBridge — there is no separate agent server process.

System Overview ​

┌─────────────────────────────────────────────────┐
│          Web Client (Vite + React 19)           │
│  Dashboards, Kanban, Chat, Settings, Timeline   │
└──────────────────┬──────────────────────────────┘
                   │ REST API + WebSocket
                   ▼
┌──────────────────────────────────────────────────┐
│       Orchestration Server (Express 5)           │
│  Projects, Sessions, Knowledge, Governance,      │
│  AgentManager, AcpAdapter, WebSocket hub         │
└──────────────────┬───────────────────────────────┘
                   │ ACP protocol (stdio) via AgentAcpBridge
                   ▼
            ┌──────────────┐
            │  CLI Binaries │
            │  (×N agents)  │
            └──────────────┘

Agent Adapters ​

Agents run in-process within the orchestration server. Each agent is managed by the AgentManager and communicates with its CLI binary via the AcpAdapter and AgentAcpBridge over ACP (stdio).

CLI Adapters ​

All providers use a single adapter backend that implements the AgentAdapter interface:

BackendTransportSession ResumeUsed By
AcpAdapterACP over stdioBest-effort (loadSession(), falls back to newSession())All providers (Copilot, Claude, Gemini, Cursor, Codex, OpenCode)
MockAdapterIn-memoryN/ATesting only

See the adapter-architecture-pattern skill in .github/skills/ for details.

Tier 1: Orchestration Server ​

The orchestration server is the main process — an Express 5 application with WebSocket support that manages projects, sessions, knowledge, governance, and agent coordination.

Entry point: packages/server/src/index.ts

Startup Sequence ​

  1. Load config (YAML + env vars)
  2. Create DI container via createContainer() — 35+ services in 6 dependency tiers
  3. Reconcile stale sessions/agents from previous runs
  4. Mount Express routes and WebSocket server
  5. Attempt session resume (non-blocking)
  6. Install graceful shutdown handlers

Service Container ​

Services are initialized in dependency order (Tier 0 → Tier 5):

TierServicesExamples
0Config & DBDatabase, ConfigStore (hot-reloadable)
1Core registriesProjectRegistry, TaskDAG, KnowledgeStore, FileLockRegistry, RoleRegistry
2Stateless servicesMessageBus, EventPipeline, ModelSelector, TokenBudgetOptimizer
3Composed servicesGovernancePipeline (6 hooks, future feature), SearchEngine, SkillsLoader, EscalationManager
4AgentManagerCentral orchestrator — spawns agents, routes messages, manages delegations
5Manager-dependentAlertEngine, ContextRefresher, SessionResumeManager, IntegrationRouter

Route Modules ​

34 route modules provide ~279 REST endpoints:

agents, analytics, browse, comms, config, conflicts, context, coordination, data, db, decisions, diff, integrations, knowledge, lead, nl, notifications, oversight, projects, replay, roles, search, services, sessions, settings, shared, summary, tasks, teams

All routes receive the full AppContext (service container) for dependency injection.

Key Subsystems ​

SubsystemPurpose
AgentManagerSpawns agents, detects commands in output streams, routes messages, manages delegations
GovernancePipeline6 hooks (file write guard, shell blocklist, commit validation, rate limiting) intercepting agent actions — infrastructure exists, currently disabled
KnowledgeStoreFTS5 full-text search with 4-tier memory categories and token-budgeted injection
SessionResumeManagerPersists agent state, orchestrates resume on startup
TaskDAGDeclarative task scheduling with dependencies, status tracking, soft-delete
CommandDispatcherParses doubled Unicode-bracket commands from agent output, routes to 10 command modules
AlertEngineProactive detection: stuck agents, context pressure, duplicate edits, stale decisions
IntegrationRouterRoutes agent events to external channels (Telegram, Slack) via NotificationBatcher

Tier 2: Web Client ​

A React 19 single-page application built with Vite and Tailwind CSS 4.

Entry point: packages/web/src/main.tsx

State Management ​

7 Zustand stores manage client state:

StoreScope
useAgentStoreAgent roster, status, messages
useProjectStoreProjects, sessions, active selection
useNavigationStoreTab state, history, breadcrumbs
useSettingsStoreUser preferences, Trust Dial level
useWebSocketStoreConnection state, event subscriptions
useSearchStoreGlobal search state
useNotificationStoreToast and notification queue

Real-Time Data Flow ​

The client uses a signal + refetch pattern for real-time updates:

  1. Server emits lightweight WebSocket signal (e.g., attention:updated)
  2. Client receives signal, refetches from REST endpoint
  3. Zustand store updates, React re-renders

This avoids tight coupling between WebSocket event schemas and component expectations. Agent text streaming is the exception — streamed directly over WebSocket for low latency.

Error Handling ​

  • RouteErrorBoundary on all 19 routes — catches render errors, shows retry UI
  • SectionErrorBoundary on feed sections, sidebar, header — isolates failures
  • 30-second fetch timeout on all API calls via AbortController
  • Error boundaries auto-reset on navigation

Communication Protocols ​

REST API (Client ↔ Orchestration Server) ​

Standard REST with JSON payloads. Auth via bearer token (auto-generated on first start). Key endpoint groups:

  • /agents — Agent lifecycle and messaging
  • /projects — Project CRUD, sessions, resume
  • /tasks — DAG task management (Kanban board)
  • /decisions — Decision log with accept/reject
  • /knowledge — Knowledge store CRUD and search
  • /settings — Configuration management

WebSocket (Orchestration Server → Client) ​

40+ event types pushed to connected clients. Key events:

EventPurpose
agent:spawned / agent:terminatedAgent lifecycle
agent:text / agent:thinkingStreaming output
lead:decision / lead:progressLead updates
attention:updatedAttentionBar refresh signal
alert:newSystem alerts
context_compactedContext window compaction

Database ​

SQLite with WAL mode and Drizzle ORM. 30+ tables across domains:

  • Agent state: agent_roster, active_delegations, agent_plans
  • Tasks: dag_tasks (with soft-delete via archivedAt)
  • Knowledge: knowledge_entries, collective_memory
  • Activity: activity_log, decision_log, message_queue
  • Configuration: settings, timers, contacts

Optimized pragmas:

sql
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA busy_timeout = 5000;
PRAGMA cache_size = -256000;  -- 256MB
PRAGMA foreign_keys = ON;

See Database Schema for table definitions.

Configuration ​

Flightdeck loads configuration from multiple sources (later overrides earlier):

  1. Built-in defaults — sensible defaults for all settings
  2. User config — ~/.flightdeck/config.yaml (auto-created on first run)
  3. Repo-level config — flightdeck.config.yaml in the project root
  4. Environment variable override — FLIGHTDECK_CONFIG points to a specific file
  5. Environment variables — startup-only overrides for individual settings

Config is hot-reloadable via chokidar file watcher — changes to the YAML file take effect without restarting the server.

Key environment variables:

VariableDefaultPurpose
FLIGHTDECK_PORT3000Server port
FLIGHTDECK_HOST127.0.0.1Bind address
FLIGHTDECK_CONFIG./flightdeck.config.yamlConfig file path
FLIGHTDECK_STATE_DIR~/.flightdeckState directory

See Configuration Reference for the full config schema and flightdeck.config.example.yaml for an annotated example.

Documentation generated by AI

Documentation generated by AI