New reference docs: - agents/ref-btmsg.md: inter-agent messaging schema and CLI - agents/ref-bttask.md: kanban task board operations - providers/ref-providers.md: Claude/Codex/Ollama/Aider comparison - config/ref-settings.md: (already committed) New guides: - contributing/dual-repo-workflow.md: community vs commercial repos - plugins/guide-developing.md: Web Worker sandbox API and publishing New pro docs: - pro/features/knowledge-base.md: persistent memory + symbol graph - pro/features/git-integration.md: context injection + branch policy - pro/marketplace/README.md: 13 plugins catalog Split files: - architecture/data-model.md: from architecture.md (schemas, layout) - production/hardening.md: from production.md (supervisor, sandbox, WAL) - production/features.md: from production.md (FTS5, plugins, secrets, audit)
8 KiB
Data Model & Layout System
This document covers agor's data model (SQLite schemas, project group config), layout system, xterm budget management, and keyboard shortcuts.
SQLite Databases
The backend manages two SQLite databases, both in WAL mode with 5-second busy timeout for concurrent access:
| Database | Location | Purpose |
|---|---|---|
sessions.db |
~/.local/share/agor/ |
Sessions, layout, settings, agent state, metrics, anchors |
btmsg.db |
~/.local/share/agor/ |
Inter-agent messages, tasks, agents registry, audit log |
WAL checkpoints run every 5 minutes via a background tokio task to prevent unbounded WAL growth.
All queries use named column access (row.get("column_name")) — never positional indices. Rust structs use #[serde(rename_all = "camelCase")] so TypeScript interfaces receive camelCase field names on the wire.
Project Group Config (~/.config/agor/groups.json)
Human-editable JSON file defining workspaces. Each group contains up to 5 projects. Loaded at startup by groups.rs, not hot-reloaded.
{
"version": 1,
"groups": [
{
"id": "work-ai",
"name": "AI Projects",
"projects": [
{
"id": "agor",
"name": "Agents Orchestrator",
"identifier": "agor",
"description": "Terminal emulator with Claude integration",
"icon": "\uf120",
"cwd": "/home/user/code/Agents Orchestrator",
"profile": "default",
"enabled": true
}
]
}
],
"activeGroupId": "work-ai"
}
TypeScript Types (src/lib/types/groups.ts)
export interface ProjectConfig {
id: string;
name: string;
identifier: string;
description: string;
icon: string;
cwd: string;
profile: string;
enabled: boolean;
}
export interface GroupConfig {
id: string;
name: string;
projects: ProjectConfig[]; // max 5
}
export interface GroupsFile {
version: number;
groups: GroupConfig[];
activeGroupId: string;
}
SQLite Schema (v3 Additions)
Beyond the core sessions and settings tables, v3 added project-scoped agent persistence:
ALTER TABLE sessions ADD COLUMN project_id TEXT DEFAULT '';
CREATE TABLE IF NOT EXISTS agent_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
project_id TEXT NOT NULL,
sdk_session_id TEXT,
message_type TEXT NOT NULL,
content TEXT NOT NULL,
parent_id TEXT,
created_at INTEGER NOT NULL,
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS project_agent_state (
project_id TEXT PRIMARY KEY,
last_session_id TEXT NOT NULL,
sdk_session_id TEXT,
status TEXT NOT NULL,
cost_usd REAL DEFAULT 0,
input_tokens INTEGER DEFAULT 0,
output_tokens INTEGER DEFAULT 0,
last_prompt TEXT,
updated_at INTEGER NOT NULL
);
Layout System
Project Grid (Flexbox + scroll-snap)
Projects are arranged horizontally in a flex container with CSS scroll-snap for clean project-to-project scrolling:
.project-grid {
display: flex;
gap: 4px;
height: 100%;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.project-box {
flex: 0 0 calc((100% - (N-1) * 4px) / N);
scroll-snap-align: start;
min-width: 480px;
}
N is computed from viewport width: Math.min(projects.length, Math.max(1, Math.floor(containerWidth / 520)))
Project Box Internal Layout
Each project box uses a CSS grid with 4 rows:
+-- ProjectHeader (auto) --------------------+
+---------------------+---------------------+
| AgentSession | TeamAgentsPanel |
| (flex: 1) | (240px/overlay) |
+---------------------+---------------------+
| [Tab1] [Tab2] [+] TabBar auto |
+--------------------------------------------+
| Terminal content (xterm or scrollback) |
+--------------------------------------------+
Team panel: inline at >2560px viewport (240px wide), overlay at <2560px. Collapsed when no subagents running.
Responsive Breakpoints
| Viewport Width | Visible Projects | Team Panel Mode |
|---|---|---|
| 5120px+ | 5 | inline 240px |
| 3840px | 4 | inline 200px |
| 2560px | 3 | overlay |
| 1920px | 3 | overlay |
| <1600px | 1 + project tabs | overlay |
xterm.js Budget: 4 Active Instances
WebKit2GTK OOMs at ~5 simultaneous xterm.js instances. The budget system manages this:
| State | xterm.js Instance? | Memory |
|---|---|---|
| Active-Focused | Yes | ~20MB |
| Active-Background | Yes (if budget allows) | ~20MB |
| Suspended | No (HTML pre scrollback) | ~200KB |
| Uninitialized | No (placeholder) | 0 |
On focus: serialize least-recent xterm scrollback, destroy it, create new for focused tab, reconnect PTY. Suspend/resume cycle < 50ms.
Project Accent Colors
Each project slot gets a distinct Catppuccin accent color for visual distinction:
| Slot | Color | CSS Variable |
|---|---|---|
| 1 | Blue | var(--ctp-blue) |
| 2 | Green | var(--ctp-green) |
| 3 | Mauve | var(--ctp-mauve) |
| 4 | Peach | var(--ctp-peach) |
| 5 | Pink | var(--ctp-pink) |
Applied to border tint and header accent via var(--accent) CSS custom property set per ProjectBox.
Adapters (IPC Bridge Layer)
Adapters wrap Tauri invoke() calls and listen() event subscriptions. They isolate the frontend from IPC details and provide typed TypeScript interfaces.
| Adapter | Backend Module | Purpose |
|---|---|---|
agent-bridge.ts |
sidecar + commands/agent | Agent query/stop/restart |
pty-bridge.ts |
pty + commands/pty | Terminal spawn/write/resize |
claude-messages.ts |
(frontend-only) | Parse Claude SDK NDJSON -> AgentMessage |
codex-messages.ts |
(frontend-only) | Parse Codex ThreadEvents -> AgentMessage |
ollama-messages.ts |
(frontend-only) | Parse Ollama chunks -> AgentMessage |
message-adapters.ts |
(frontend-only) | Provider registry for message parsers |
provider-bridge.ts |
commands/claude | Generic provider bridge (profiles, skills) |
btmsg-bridge.ts |
btmsg | Inter-agent messaging |
bttask-bridge.ts |
bttask | Task board operations |
groups-bridge.ts |
groups | Group config load/save |
session-bridge.ts |
session | Session/layout persistence |
settings-bridge.ts |
session/settings | Key-value settings |
files-bridge.ts |
commands/files | File browser operations |
search-bridge.ts |
search | FTS5 search |
secrets-bridge.ts |
secrets | System keyring |
anchors-bridge.ts |
session/anchors | Session anchor CRUD |
remote-bridge.ts |
remote | Remote machine management |
ssh-bridge.ts |
session/ssh | SSH session CRUD |
ctx-bridge.ts |
ctx | Context database queries |
memora-bridge.ts |
memora | Memora database queries |
fs-watcher-bridge.ts |
fs_watcher | Filesystem change events |
audit-bridge.ts |
btmsg (audit_log) | Audit log queries |
telemetry-bridge.ts |
telemetry | Frontend -> Rust tracing |
notifications-bridge.ts |
notifications | Desktop notification trigger |
plugins-bridge.ts |
plugins | Plugin discovery |
Keyboard Shortcuts
Three-layer shortcut system prevents conflicts between terminal input, workspace navigation, and app-level commands:
| Shortcut | Action | Layer |
|---|---|---|
| Ctrl+K | Command palette | App |
| Ctrl+G | Switch group (palette filtered) | App |
| Ctrl+1..5 | Focus project by index | App |
| Alt+1..4 | Switch sidebar tab + open drawer | App |
| Ctrl+B | Toggle sidebar open/closed | App |
| Ctrl+, | Toggle settings panel | App |
| Escape | Close sidebar drawer | App |
| Ctrl+Shift+F | FTS5 search overlay | App |
| Ctrl+N | New terminal in focused project | Workspace |
| Ctrl+Shift+N | New agent query | Workspace |
| Ctrl+Tab | Next terminal tab | Project |
| Ctrl+W | Close terminal tab | Project |
| Ctrl+Shift+C/V | Copy/paste in terminal | Terminal |
Terminal layer captures raw keys only when focused. App layer has highest priority.