Architecture

How the three layers fit together, what happens when a message arrives, and why the design looks the way it does.

CommonOS is three layers with one rule: dependencies point one way. common-agent ← commonos ← plugins ← commonos-cli. CommonAgent never knows about the database or the web. Plugins never reach into each other or into the core.

Layer Crate Job
CommonAgent common-agent The base agent: turn loop, hook points, tools, model providers, prompt tiers.
CommonOS commonos The host: profiles, the shared database, message routing, the plugin system, the dashboard.
Plugins common-bill, common-memory, common-delegate Add hooks, tools, session context, tables and routes through one contract.
cos commonos-cli The command. Links the plugins in.

The path of a message

  1. Entry pointsDashboard web chat · cos chat
  2. DispatcherGateway hooks · session · frozen context · tags
  3. CommonAgentOne per profile · the turn loop
  4. ProviderAnthropic Messages API · mock
Every turn uses
  • Toolsmemory · session_search · delegate_task
  • HookBusapproval_policy · live_feed · plugin hooks
  • PostgresCore tables · each plugin’s own tables

When you send a message from the dashboard or cos chat, the Dispatcher handles it, in the same order as Hermes’s gateway:

  1. pre_gateway_dispatch runs first. A hook can skip the message or rewrite its text.
  2. Find the profile and session. A new session is created if none was given. An existing session must belong to the profile.
  3. post_gateway_admission runs. A hook can handle the message itself and skip the turn.
  4. Freeze the session context. At a session’s first turn, CommonOS gathers the profile’s skills index and every plugin’s context (such as memory) and stores it on the session. Later turns reuse it, so the cached prompt prefix holds.
  5. Run the turn. Tags are resolved (profile tags plus session tags) and the profile’s CommonAgent runs the turn with a cancellation token. The reply is published as a live event.

From there, the turn loop takes over. To follow one message through every step and hook point, see Message Flow.

Start-up

cos serve and cos chat start the same way:

  1. Connect to Postgres and apply the core schema.
  2. For each enabled plugin, in the order listed in commonos.toml: apply its schema, then call register.
  3. Build one hook bus, in this order: approval_policy first (so no plugin can approve past a deny), then live_feed, then plugin hooks in config order.
  4. Build one CommonAgent per profile folder. Each gets only the tools in its profile’s tools list, or every installed tool when there is no list.

A plugin that is enabled but not installed, a plugin that fails to register, or a profile that lists a tool nobody provides stops start-up with the name in the error. Failures are loud on purpose.

Storage

Everything lives in one Postgres database. Every row carries its profile_id, so one database serves every profile.

  • Core tables: profiles, sessions, turns, messages, tool_calls, approvals.
  • Plugin tables: each plugin owns its own, such as CommonBill’s model_calls and Memory’s memory_entries. The core never reads them.

Schemas use CREATE … IF NOT EXISTS and run on every start under an advisory lock, so two processes starting together don’t race. See Database.

Design choices

Follow Hermes. Hook names, hook behavior, memory caps and child-agent rules match Hermes Agent, so its plugins and habits port over without surprises.

Freeze context per session. Memory and skills are gathered once, at the start of a session. Changes show up next session. This keeps the prompt prefix identical across turns, so the provider’s prompt cache keeps working and long sessions stay cheap.

Tags carry meaning only in CommonBill. The loop copies tags; it never interprets them. Cost is computed in one place.

Fail loudly. A misconfigured plugin or profile stops start-up with a clear message, instead of running with something quietly missing.

Loopback only. The dashboard binds to 127.0.0.1 until it has a login.

Edit this page on GitHub

Esc