Skip to content

Latest commit

 

History

History
94 lines (66 loc) · 6.77 KB

File metadata and controls

94 lines (66 loc) · 6.77 KB

Architecture

Robin is a single-process Node.js daemon that owns scheduling, memory, LLM dispatch, and the integration lifecycle. It exposes itself as two MCP servers that Claude Code (or any MCP client) connects to.

Layers

system/                    Framework source (TypeScript, compiled to dist/)
├── kernel/                Daemon runtime, scheduler, health monitor, config, invariants
├── brain/                 Memory (DB, entities, recall, beliefs), cognition (biographer,
│                          dream, capture, embedder), LLM dispatcher
├── integrations/          Built-in integrations + runtime loader
├── skills/                Built-in skills + runtime loader
├── jobs/                  Job runner
├── surfaces/              CLI, HTTP server, MCP servers (core + extension)
├── lib/                   Shared utilities (paths, logging, telemetry, with-timeout, power)
└── tests/                 Integration / architecture boundary tests

user-data/                 Per-user instance data (gitignored)
├── config/                models.yaml, policies.yaml, hardware.yaml, secrets/
├── extensions/            User integrations, jobs, skills, scripts, triggers
├── content/               Knowledge files, artifacts, sources
├── state/                 SQLite DB, runtime PID
└── observability/         Daemon logs, eval data

dist/                      Compiled output (tsc + mirrored manifests + skills)

Database

A single SQLite file (user-data/state/db/robin.sqlite) via better-sqlite3, with sqlite-vec for vector search. WAL mode, applied via a versioned migration runner (system/brain/memory/migrations/).

Table Purpose
events Append-only event firehose — every captured session, integration tick, belief, prediction, etc.
events_content Large bodies + 4096-dim embeddings (Matryoshka, via sqlite-vec). Separated from events for scan efficiency.
events_content_fts FTS5 full-text index over content bodies.
entities Canonical entities (person, place, tool, service, etc.) with profiles.
relations Subject–predicate–object triples linking entities, sourced from biographer extraction.
predictions Claims with confidence, deadline, resolution, Brier score.
corrections What Robin said wrong + the correction — feeds the self-learning loop.
refusals Logged refusals with reason + action policy.
journals Daily narrative summaries generated by the dream loop.
metrics_daily Rolled-up daily metrics (Brier calibration, entity counts, etc.).
jobs Scheduler job queue (cron, manual, delayed). One row per firing; completed rows accumulate.
biographer_progress In-flight chunk cursor for multi-tick biographer extraction. Deleted on session completion.

The relations table is the graph layer — SPO triples indexed on (subject_id, predicate) and (object_id, predicate), traversed via relatedEntities(). A dedicated graph engine (Kuzu) was evaluated and removed 2026-06-11: upstream was archived in Oct 2025, and at current scale (<10k edges) SQLite traversal is nowhere near a bottleneck. If that changes, rebuild a projection from entities + relations against whatever engine has won by then.

LLM dispatch

Role-based routing defined in user-data/config/models.yaml:

roles:
  embed:     { provider: ollama, model: qwen3-embedding:8b }
  reasoning: { provider: ollama, model: qwen3.5:35b-a3b }
  summarize: { provider: ollama, model: qwen3.5:35b-a3b }

The dispatcher wraps every call in withTimeout (default 5 min, overridable per-call) so a hung provider can never wedge the scheduler. Providers are registered at daemon startup; missing secrets produce a warning but don't crash (lenient: true).

Scheduler

The daemon's runLoop calls Scheduler.tickOnce() every second, which claims and runs one pending job. Jobs are leased (5 min TTL); a periodic reaper resets expired leases. Cron jobs self-re-arm via rescheduleCronAfterCompletion so a transient failure can't permanently silence a schedule.

Cognition jobs:

  • biographer.run — entity/relation extraction from captured sessions. Multi-tick: processes a bounded number of chunks per tick, persisting progress in biographer_progress. A session resumes across cron ticks; no session can hold the scheduler past the daemon's 30-min heartbeat recovery gate.
  • embedder.run — embeds events_content rows with null embeddings (deferred from the ingest hot-path, single-flight against Ollama).
  • dream.run — nightly: resolves overdue predictions, rolls up metrics, generates a journal entry.

Health monitor

Invariants run every 60 seconds. The critical one is daemon.heartbeating — if the scheduler tick hasn't run in 5 minutes, the monitor logs CRITICAL. After 30 minutes sustained CRITICAL (not just a slow handler), the daemon calls process.exit(1) for launchd respawn. A boot-window gate (2 min) suppresses recovery during noisy startup.

The biographer's circuit breaker complements this: if every chunk in a tick fails because the LLM is unreachable (connection refused / timeout), the biographer aborts without advancing the cursor or writing a marker — leaving the session to retry when the LLM is back, rather than silently marking it done with empty extraction.

Capture pipeline

A Claude Code hook (~/.claude/settings.json) POSTs session transcripts to the daemon's HTTP server on session end. The daemon projects the JSONL into turns, applies skip rules (short sessions, non-Robin-folder CWD), deduplicates by content hash, and writes a session.captured event. The biographer picks these up on its cron schedule.

Integrations

Each integration is a directory with an integration.yaml manifest and an index.ts exporting { tick, actions?, init?, cleanup? }. The daemon's registerIntegrations loads them from both system/integrations/builtin/ and user-data/extensions/integrations/, registers their cron schedules, and hot-reloads on file changes.

Built-in: gmail, google_calendar, github, linear, chrome, weather, finance_quote, claude_code, notify.

Skills

Skills are markdown methodology files surfaced via the skill MCP tool on robin-core. The loader scans system/skills/builtin/ and user-data/extensions/skills/, parses SKILL.md frontmatter, and merges with user-shadows-system precedence. The catalog of valid skills is embedded in the skill tool's description at MCP server startup for progressive disclosure — Robin sees what's available without a separate list call.

Telemetry

A typed event writer (writeTelemetry) writes structured events to the events table. An optional OTLP HTTP exporter forwards to external collectors. Telemetry is off by default; configure via policies.yaml.