Skip to content

Architecture

shipking-ai edited this page Sep 3, 2026 · 1 revision

Architecture

Two programs, one version line, one boundary between them and everything OmniRoute does.

The OmniRoute boundary

OmniHarness treats OmniRoute as an opaque provider execution layer. It never reads or stores provider credentials, never talks to an upstream provider directly, and never implements quota or provider-translation logic itself. What it does do: analyze a task, choose a strategy, orchestrate agents, express model-selection intent as provider/model plus a capability, send OpenAI-compatible chat requests to the configured endpoint, and record what actually happened — cost, tokens, outcome.

internal/gateway is the only package that speaks to OmniRoute. Swapping the transport — a different gateway, a direct provider, an in-process stub for tests — is a one-file change, which is what lets the whole Go suite run without a live OmniRoute server.

System topology

USER
 │
 ▼
CLI ──────────┐        ┌── TUI (thin consumer of the event bus)
              ▼        ▼
        ┌─────────────────────────────┐
        │      core.Runtime           │  wiring, lifecycle, shutdown
        └─────────────┬───────────────┘
                      ▼  typed events (internal spine)
┌──────────┬──────────┬──────────┬──────────────┬─────────────┐
│ Task     │Strategy  │Orchestr. │ Agent        │ Context     │
│ Analyzer │ Engine   │ (graph)  │ Runtime      │ Composer    │
└────┬─────┴────┬─────┴────┬─────┴──────┬───────┴──────┬──────┘
     │          │         │            │              │
     ▼          ▼         ▼            ▼              ▼
 ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
 │ Budget   │ │ Policy   │ │ Tools +  │ │ Evaluate │ │ Repair   │
 │ Engine   │ │ Engine   │ │ MCP      │ │ Engine   │ │ Engine   │
 └──────────┘ └──────────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
                                │            │            │
                                ▼            ▼            ▼
                        ┌─────────────────────────────────────┐
                        │        Model Selection Engine       │
                        └──────────────────┬──────────────────┘
                                           ▼  intent: provider/model + capabilities
                        ┌─────────────────────────────────────┐
                        │   gateway.Client → OmniRoute HTTP   │  ← the ONLY boundary
                        └─────────────────────────────────────┘
                                           ▼
                                  ┌──────────────────┐
                                  │    OmniRoute     │  providers, accounts, quota
                                  └──────────────────┘

Cross-cutting: event (the spine every state change flows through), session (durable SQLite state), telemetry (recorded from real events, never fabricated), memory (performance and project memory).

Package layout (Go)

cmd/omniharness        entry point
internal/event         typed event system (spine)
internal/config        TOML config + validation
internal/session       session/task/agent persistence (SQLite, modernc)
internal/task          Task model + Analyzer → TaskProfile
internal/strategy      strategy selection from profile
internal/orchestrator  task-graph scheduler, worker pool, result synthesis
internal/agent         agent runtime, roles, lifecycle state machine
internal/model         capability-based model selection
internal/gateway       OmniRoute OpenAI-compatible client — the only boundary
internal/context       context engine + composer
internal/memory        project + performance memory (SQLite)
internal/tools         tool registry + native tools (fs, shell, git, search, proc)
internal/mcp           MCP stdio client
internal/policy        risk classes, permission evaluation, approvals
internal/evaluate      evaluator framework (build/test/lint/constraint/evidence)
internal/repair        failure classification + repair strategies
internal/budget        token/cost/time/agent/tool-call budgets
internal/telemetry     metric recording + aggregation
internal/cli           cobra commands (headless-first)
internal/tui           the original Bubble Tea cockpit (still builds; users run the npm one)
internal/combo         model-combo registry (auto/* routing combos + catalog)
internal/version       build info

Dependency direction is downward only. event and config are leaves. No cycles.

The terminal UI most people run is the TypeScript one under npm/, not internal/tui — see its own layout in CONTRIBUTING.

Decisions that shaped the rest

  • The event system is the spine. A typed envelope (Event{Type, Data}), a fan-out bus with bounded per-subscriber buffers that drop the oldest event rather than ever block the runtime, full persistence per session for replay and debugging.
  • SQLite via modernc.org/sqlite — pure Go, no CGO, so the build needs no C toolchain on any platform. This is also what let one runner cross-compile all six release binaries.
  • Strategy selection is a pure function of the task profile, budgets, and historical performance — testable with no runtime attached.
  • Single-agent is a first-class strategy, not a degenerate case of multi-agent. The engine can say "one agent, direct" and mean it.
  • Model selection speaks intent, not identity. A capability (reasoning, fast, cheap, long-context, coding, vision, tools, research, review) resolves to a concrete provider/model via config plus a live catalog probe, only at request time.
  • Repair changes variables, never blind-retries the identical failed execution — model, role, context, instructions, or the execution strategy itself.
  • No fabricated telemetry. Every metric shown anywhere comes from a recorded event or a session row. See Security Model for what this means for the numbers a session panel is and isn't allowed to show.
  • MCP is first-class, not bolted on — a native stdio JSON-RPC client, with MCP tools registered into the same registry as native tools so policy applies identically to both.
  • The TUI is a consumer, not a source of truth. All state flows through the runtime event bus; it renders and sends control commands.
  • The API key is never written by Save. Config save scrubs the key before encoding, so an env-provided key can never leak into the config file on disk.

Why internal/envguard exists

Every spawned process — shell, git, an evaluator, an MCP server — inherits the parent environment minus OMNIROUTE_API_KEY, OMNIHARNESS_API_KEY, and ROUTER_API_KEY. That list can't be turned off from config. Third-party credentials (GITHUB_TOKEN, AWS_SECRET_ACCESS_KEY, NPM_TOKEN) are inherited by default — an agent asked to open a PR or publish a package needs them — and a deployment that doesn't want that names them in policy.secret_env. See Security Model for the full list of what's in and out of scope.

Clone this wiki locally