Files
waggle-os/docs/backend-map/00-MENTAL-MODEL.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

219 lines
13 KiB
Markdown

# 00 · Mental Model — Waggle OS for the Frontend Rebuild
**Audience.** You are about to rebuild the Waggle OS frontend in Lovable. Before you touch a
single screen, read this. It is the *concept map* — what the system is, how its layers fit
together, and which two or three ideas everything else hangs off. The detailed wire contracts
(exact types, routes, capability flags) live in the sibling sections under
`docs/backend-map/sections/`; this document is the picture you keep in your head while you read
those. Every claim here is grounded in `CLAUDE.md`, `docs/ARCHITECTURE.md`,
`docs/WAGGLE-SYSTEM-MAP.md`, and the written subsystem sections.
---
## 1. What Waggle OS is (one paragraph)
Waggle OS is a **workspace-native AI agent operating system with persistent memory**. It looks and
behaves like a small desktop OS — a window manager, a dock, and ~18 first-party "apps" (Chat,
Memory, Files, Marketplace, Mission Control, and more) — but every app is a surface onto a stack of
AI agents that *remember*. It ships three ways from one codebase: a **Tauri 2.0 desktop binary**
(Rust shell, ~120 MB) for Windows and macOS, the same React app as a **web bundle**, and a
**Node.js Fastify sidecar** that does all the real work (agent loop, tool execution, memory,
LLM routing). The product thesis is that memory is the moat: the longer you use Waggle, the more it
knows you, and that accumulated memory is what makes the agents progressively more useful — and
expensive to leave behind. Strategically, Waggle is the demand-creation funnel for **KVARK**,
Egzakta Group's sovereign enterprise AI platform.
---
## 2. The layered mental model
Everything a user does flows through the same six layers, top to bottom. There are no hidden
services and no external dependency for core functionality — the whole stack runs locally.
```mermaid
flowchart TD
U[User] --> SHELL[Tauri Shell · Rust\nspawns sidecar · tray · global hotkey · watchdog]
SHELL -->|hosts webview| UI[React Desktop OS · apps/web\nwindow manager · dock · 18+ apps · SSE]
UI -->|HTTP + SSE + WebSocket| SIDE[Sidecar · Node.js + Fastify\n150+ routes · agent loop · tool execution]
SIDE -->|direct function calls| PKG[Packages · TypeScript libraries\nagent · core · hive-mind-core · shared · marketplace ...]
PKG --> MIND[(Per-workspace SQLite\n.mind files · memory + KG + vectors)]
PKG --> PG[(Team / Cloud Postgres\nDrizzle · users · teams · tasks · governance)]
SIDE -->|OpenAI-compatible| LLM[LLM Router · LiteLLM\nfalls back to built-in Anthropic proxy / echo]
classDef store fill:#1d2330,stroke:#a78bfa,color:#fff;
class MIND,PG store;
```
Read it as a request lifecycle:
1. **Shell (Tauri / Rust).** Owns the OS-level concerns only: spawns and supervises the sidecar
(watchdog restarts it on crash, max 5/10 min), the tray icon, the global toggle hotkey, and the
webview that loads the web app. The shell holds no business logic — for a Lovable rebuild it is
effectively invisible; you target the same web app it hosts.
2. **UI (React desktop OS — `apps/web/`).** The window manager, dock, and apps. **This is what you
are rebuilding.** It is a thin client: it renders state, streams responses, and POSTs user
intent. It never talks to a database or an LLM directly — it talks only to the sidecar over
HTTP, Server-Sent Events (streaming chat/events/notifications), and WebSocket (team presence).
3. **Sidecar (Node.js + Fastify, port 3333).** The brain stem. 150+ routes. It resolves the
workspace session, builds the system prompt, runs the agent loop, executes tools, and streams
results back as SSE. Every UI action terminates here.
4. **Packages (TypeScript libraries).** The sidecar is thin glue over a monorepo of ~27 workspace
packages. The ones you will feel through the API: `@waggle/agent` (the loop, personas, tools,
gates), `@waggle/hive-mind-core` (the memory substrate — mind + harvest), `@waggle/core`
(config, vault, telemetry, compliance), `@waggle/shared` (the wire types + tier model),
`@waggle/marketplace`, `@waggle/waggle-dance`.
5. **Dual data stores.** Two databases that never share a foreign key — see §3.
6. **LLM router (LiteLLM).** The agent loop POSTs to an OpenAI-compatible `/chat/completions`
endpoint. In practice that is LiteLLM (`litellm-config.yaml`), which routes/normalizes model
names; it falls back to a built-in Anthropic proxy, and to a deterministic echo provider when no
keys are present. The frontend never sees this — it only ever sees SSE tokens.
The single rule to internalize: **the frontend is a presentation + streaming client. All
intelligence, persistence, and secret handling lives in the sidecar and below.** If you find
yourself wanting to put logic in the UI, it almost certainly already exists behind a route.
---
## 3. Dual persistence — and why both exist
Waggle has **two** stores, deliberately. Confusing them is the most common architectural mistake.
| | Per-workspace **SQLite "mind"** | Team / Cloud **Postgres** |
|---|---|---|
| Engine | SQLite (`better-sqlite3` + `sqlite-vec`) | PostgreSQL via Drizzle ORM |
| Scope | **One workspace's private memory** | Teams, users, agents, tasks, jobs, governance |
| Holds | Memory frames, knowledge graph, embeddings, identity, awareness | Multi-user, multi-tenant relational data |
| Location | A local file per workspace (`mind_path`) | Cloud / server (`DATABASE_URL`) |
| Defined in | `packages/hive-mind-core/src/mind/` | `packages/server/src/db/schema.ts` (20 tables) |
**Why two?** They answer two different questions.
- The **mind** answers *"what does this agent know and remember here?"* It is the moat. It is
local-first, private, per-workspace, and runs synchronously with zero network. Memory is written
as **frames** (I-frame = foundational identity, P-frame = incremental update, B-frame = bridge),
searched by a **hybrid** pipeline (FTS5 keyword + sqlite-vec vector, fused by reciprocal-rank),
and grown into a knowledge graph by the Cognify pipeline. Each workspace is its own `.mind` file,
so workspace isolation is a filesystem fact, not a query filter.
- **Postgres** answers *"who are the people, teams, and tasks, and what are they allowed to do?"*
It is the shared, governed, collaborative substrate — only meaningful in TEAMS/cloud mode. It
carries no memory frames.
The **only bridge** between them is one nullable column: `users.mind_path`, a text pointer from a
cloud user row to where that user's local SQLite mind lives. There are **no cross-database foreign
keys** and no cascades — the two layers are joined only in application code. For the frontend, this
means: memory-related screens read from mind-backed routes; team/admin/governance screens read from
Postgres-backed routes; never assume one knows about the other.
---
## 4. The moat (and therefore the upgrade trigger)
This is the business model encoded into the architecture — and it dictates what the UI should and
should not nag the user about.
- **Memory + Harvest are free forever.** Harvest ingests the user's existing AI history (ChatGPT,
Claude, Claude Code, Gemini, Perplexity, and more) and memory accumulates from every
conversation. This is the lock-in moat: the longer they stay, the more Waggle knows them, the
costlier it is to leave. It is never paywalled.
- **Agents are free.** Spawning agents is enabled on *every* tier (`spawnAgents: true` everywhere).
Agents are free precisely *because they generate memory* — they feed the moat. Gating them would
be self-defeating.
- **Skills + Connectors + Team features are the upgrade trigger.** FREE blocks custom skills, caps
connectors at 5 and workspaces at 5, and limits export formats. PRO ($19/mo) unlocks unlimited
connectors/workspaces, custom skills, and full export — but stays solo. TEAMS unlocks shared
workspaces, the team skill library, governance, and cloud sync.
**Frontend consequence:** put friction on *skills, connectors, and team collaboration*, never on
*memory or agents*. Upgrade CTAs belong on locked skill/connector/team surfaces. (And there is a
distinct, more aggressive CTA reserved for the KVARK enterprise path — see §6.)
---
## 5. The agent runtime (what `POST /api/chat` actually does)
When the user sends a message, the sidecar turns it into one agent turn. Conceptually it is a
**persona-driven, memory-aware, tool-calling loop with completion gates.**
```mermaid
flowchart LR
M[User message] --> ORCH[Orchestrator\nbuild system prompt]
ORCH --> RECALL[Recall memory\nhybrid search → inject top frames]
RECALL --> PROMPT[Layered prompt\nidentity + memory + behavioral spec + persona + scaffold]
PROMPT --> LOOP{Agent loop\nup to 200 turns}
LOOP -->|tool calls| TOOLS[Execute tools\ngovernance → hooks → injection-scan → loop-guard]
TOOLS --> LOOP
LOOP -->|no tool calls| GATES{Completion gates\nverify · real writes · skill distill}
GATES -->|gate fires| LOOP
GATES -->|clean| OUT[Final answer\nstream SSE → autosave memory]
```
The pieces a frontend should understand:
- **Persona-driven.** Every turn runs under one of the built-in personas (22 in data;
general-purpose / planner / verifier / coordinator among them). The persona supplies the system
prompt slant, model preference, and — critically — its **tool allowlist/denylist**. Read-only
personas (e.g. verifier, planner) cannot be handed write tools. This is why the UI lets the user
switch personas: it changes the agent's whole posture and capability set.
- **Gated tools.** Tools are never executed blindly. Each call passes an ordered middleware chain:
governance (`blockedTools`), pre/post hooks, **injection scanning** of external/connector input,
and loop-guard (duplicate/oscillation detection). Sensitive tools hit an **approval gate**
which surfaces in the UI as an approval card the user must accept. (YOLO/auto-approve is opt-in,
off by default.)
- **Memory recall + autosave.** Before the loop, the orchestrator recalls relevant frames and
injects them. After it, important facts/decisions/preferences are auto-saved back to the mind.
The conversation also persists to a `.jsonl` session file. This recall→answer→save cycle is the
loop that keeps the moat filling.
- **Self-evolution.** Turns are traced; recurring capability gaps, corrections, and workflow
patterns are tallied as improvement signals and surfaced to the user as suggestions (max a few at
a time). The system improves its own prompts/personas over time — a closed loop, not tier-gated.
- **Streaming contract.** The UI never blocks on a full response. It consumes SSE events —
`token`, `step`, `tool_start`/`tool_end`, `done` — and renders them incrementally. Tool cards,
approval gates, and the typing stream are all driven off this one stream.
---
## 6. The 5-tier model and the KVARK funnel
Tiers are defined canonically in `packages/shared/src/tiers.ts` and consumed by both sidecar and
UI. There are exactly five.
| Tier | Price | What it is |
|---|---|---|
| **TRIAL** | $0 / 15 days | All features unlocked; **falls back to FREE after 15 days** |
| **FREE** | $0 forever | 5 workspaces, agents, built-in skills only |
| **PRO** | $19/mo | Solo power tier: unlimited workspaces/connectors, custom skills, full export |
| **TEAMS** | $49/mo per seat | Shared workspaces, WaggleDance, governance, cloud sync, KVARK CTA active |
| **ENTERPRISE** | Consultative | **KVARK** sovereign on-prem (www.kvark.ai) — contract-billed |
Two rules the frontend must obey:
1. **Always resolve the effective tier first.** A stored `TRIAL` whose 15 days have elapsed must
be treated as `FREE`. Run the user's tier through `getEffectiveTier(tier, trialStartedAt)`
*before* reading any capability flag, or you will render features an expired trial cannot use.
In `TierCapabilities`, **`-1` means "unlimited"**, not zero.
2. **KVARK is the top of the funnel, not a sixth tier.** ENTERPRISE *is* KVARK: "everything Waggle
does, on your own infrastructure, inside your perimeter." Waggle's whole job strategically is to
create qualified demand for KVARK. The tier model exposes this as `kvarkCta` (`none`/`subtle`/
`active`) — the CTA escalates as the user climbs toward TEAMS. KVARK URLs are hardcoded in
exactly two places (`kvark-tools.ts` and the `KvarkNudge` component); the `kvark_search` /
`kvark_ask_document` tools are gated to TEAMS/ENTERPRISE. The frontend surfaces the CTA per the
tier's `kvarkCta` value — it does not invent its own enterprise pitch.
---
## TL;DR for the rebuild
- The frontend is a **streaming presentation client** over a local Fastify sidecar — no DB, no LLM,
no secrets in the UI.
- Two stores: **per-workspace SQLite mind** (memory, private, local-first) and **cloud Postgres**
(teams/governance). Bridged only by `users.mind_path`.
- **Memory + agents are free** (they fill the moat); **skills, connectors, and team features** are
what you gate and up-sell.
- Chat is a **persona-driven, gated, memory-aware loop** whose output you render as an **SSE
stream** (tokens, tool cards, approval gates).
- Five tiers; **always `getEffectiveTier` first**; **KVARK is the enterprise funnel**, surfaced via
the tier's `kvarkCta` flag.
For exact shapes and routes, continue to `docs/backend-map/sections/` (02* = data model & tiers,
03* = API surface, 05* = subsystems).