A human-like, proactive AI agent whose architecture mirrors the human body — senses, neural impulses, brain regions, a heartbeat, and physiology.
Most "AI agents" are request/response loops: you send a message, the model replies, nothing happens in between. lifemodel is built the other way around. It runs continuously, feels internal and external pressure building up over time, and decides on its own when to think, act, or reach out to you — the same way a person does.
The guiding metaphor is the human body, taken seriously as an engineering constraint:
| Body | lifemodel |
|---|---|
| 👂 Senses | Inbound stream (gateway adapters are separate) |
| ⚡ Neural impulses | Signals (one unified data model for everything) |
| 🧠 Brain regions | Layers (autonomic → aggregation → cognition) |
| ❤️ Heartbeat | CoreLoop (a fixed 1-second tick) |
| 🔋 Physiology | Energy & state (thinking is expensive; resting is free) |
| 💪 Motor function | Motor Cortex (sandboxed code execution & tools) |
This isn't just naming. The metaphor drives real architectural decisions — energy conservation, emergence over polling, and layered processing where cheap reflexes run constantly and expensive reasoning only wakes up when it's truly needed.
- 🔋 Energy-conserving cognition. Every tick runs free, zero-LLM "autonomic" and "aggregation" layers. The expensive LLM-powered cognition layer only wakes up when accumulated pressure crosses a threshold (or you message it directly). No wasteful polling, no burning tokens to do nothing.
- 🌱 Emergence over polling. The agent doesn't run on timers asking "should I do something now?". State accumulates, pressure builds, and actions emerge when thresholds are crossed — proactive messages, thoughts, reminders.
- ⚡ Signals, not events. Everything that flows through the system — an inbound message, an internal urge, an overdue commitment — is a single unified
Signaltype. One model for all data flow. - 🧠 Two-speed thinking (System 1 + System 2). A cheap fast model handles classification and quick reactions; an expensive smart model is escalated to only on low confidence (and only when it's safe to retry). Inspired by Kahneman's dual-process theory.
- 💪 Sandboxed Motor Cortex. When the agent needs to act (run code, fetch data, use a skill), it dispatches to a separate agentic runtime that executes inside per-run Docker containers —
--read-only,--network none,--cap-drop ALL, strict resource limits. Results flow back as signals. - 🗂️ Dual-layer long-term memory. A vector store (semantic recall, salience decay) in Postgres with pgvector, combined with a graph store (entities, relations, spreading activation) for associative context.
- 🧩 Strictly-isolated plugin system. Core never imports plugin types. Plugins extend the agent (neurons, channels, tools, providers, filters) only through a small
PluginPrimitivesAPI. - 📜 Agent Skills standard. Skills declare their own dependencies (
npm/pip/apt) and are loaded into the sandbox with content-addressed dependency caching.
Signals enter through channels and flow up through progressively more expensive layers. Most ticks never reach the top.
flowchart TD
GW["📥 Inbound stream<br/>— gateway input"] -->|Signals| AUT
subgraph BRAIN [" "]
direction TB
AUT["🧬 AUTONOMIC LAYER<br/>Neurons monitor state · Weber–Fechner change detection<br/><b>Zero LLM cost · like the brain stem</b>"]
AGG["🔀 AGGREGATION LAYER<br/>Buckets signals · detects patterns · habituation/deferral<br/>Decides: wake COGNITION?<br/><b>Zero LLM cost · like the thalamus</b>"]
COG["💡 COGNITION LAYER<br/>Fast model first (System 1) · smart retry if uncertain<br/>Deep reasoning only when needed<br/><b>LLM cost · like System 1 + 2</b>"]
AUT --> AGG
AGG -->|only if threshold crossed| COG
end
COG -.->|core.act| MC["💪 Motor Cortex<br/>Sandboxed agentic runtime<br/>(Docker-isolated code & tools)"]
MC -.->|motor_result signal| AUT
Most ticks: only autonomic and aggregation run. Cognition wakes for user messages or threshold crossings. The smart (expensive) model is used only on retry — when confidence is low and it's safe to retry.
A fixed 1-second tick drives everything:
- Collect signals from channels (sensory input)
- Update thought pressure & desire pressure from memory
- Check overdue commitments and predictions → emit signals
- Autonomic layer: neurons emit internal signals
- Aggregation layer: collect, aggregate, decide wake threshold
- Cognition layer (only if woken): process with the LLM
- Apply the intents returned by all layers
Cognition uses native OpenAI tool-calling with Codex-style natural termination — the model stops calling tools when it's done; there's no mandatory "final" tool. It supports smart escalation (core.escalate), smart retry on low confidence, and proactive deferral (core.defer) so the agent can choose not to interrupt you right now.
📖 Full details: docs/architecture.md
- Node.js ≥ 24
- Docker — required for the Motor Cortex's agentic (code-executing) runs and for the isolated test boundary (see Test isolation)
- An OpenAI-compatible endpoint with a model per role — LM Studio, Ollama, vLLM, OpenRouter, …
# 1. Clone
git clone https://github.com/shady2k/lifemodel.git
cd lifemodel
# 2. Install dependencies
npm install
# 3. Configure environment (see below)
cp .env.example .env # then edit .env
# 4. Run in development (hot-reload via tsx)
npm run dev
# — or build & run for production —
npm run build
npm startThe supported deployment is the k3s platform. See the platform installation guide.
Process options can be set in a .env file. Durable lifemodel settings, including the model endpoint, are stored in Postgres. Process option essentials:
| Variable | Description |
|---|---|
TZ |
Your timezone (e.g. Europe/Moscow) |
LOG_LEVEL |
info, debug, … |
The model endpoint — the only model configuration
Any OpenAI-compatible server works (LM Studio, Ollama, LocalAI, vLLM), and the
endpoint carries no key of its own — a server that needs one is reached through
Agent Vault, which injects it. This endpoint is the only model configuration
there is: there is no separate keyed provider surface to fall back to. The
endpoint and its models are lifemodel's own
settings: they are set in lifemodel's settings interface (docs/features/instance/settings.md) or in a checkout with these environment variables, which name the same fields:
| Variable | Description |
|---|---|
LLM_ENDPOINT_BASE_URL |
Base URL of the endpoint (e.g. http://localhost:1234/v1) |
LLM_ENDPOINT_FAST_MODEL |
The model for the fast role (classification) |
LLM_ENDPOINT_SMART_MODEL |
The model for the smart role (reasoning, composition) |
LLM_ENDPOINT_MOTOR_MODEL |
The model for the motor role (Motor Cortex) |
LIFEMODEL_MODEL_BASE_URL |
The platform's own pin on the endpoint (the cluster's model gateway). It overrides the saved value and cannot be edited in the settings interface |
There is no second endpoint and no core fallback. Without the platform pin,
a role the endpoint does not name is refused with an error naming that role,
never answered with another role's model. Only when LIFEMODEL_MODEL_BASE_URL
is set do blank role models become default; the gateway maps that name to
its configured slot model. No key is ever set here: lifemodel sends no
Authorization header at all — in the cluster the platform's model gateway
injects the owner's key from OpenBao on the upstream leg, and no key reaches
lifemodel's process, settings or logs (ADR-013). A completion is one HTTP
attempt; the gateway holds the primary/reserve failover.
Optional: web search providers
| Variable | Description |
|---|---|
SERPER_API_KEY |
Serper API key |
TAVILY_API_KEY |
Tavily API key |
SEARCH_PROVIDER_PRIORITY |
Provider fallback order |
The core image requires an x86-64-v2 CPU or newer for memory search. On a VM, set the vCPU type to host or to an x86-64-v2 model; the hypervisor default Common KVM processor / qemu64 may expose only x86-64-v1. lifemodel doctor warns when /proc/cpuinfo lacks the sse4_2 or popcnt flags, because memory search may fail to load.
The supported deployment target is the k3s platform installed through the
NixOS module or the generic Linux installer. See the
platform installation guide and
ADR-010. The standalone core image is
built by scripts/build-core-image.sh for platform use; the former Docker-run
instance image is not supported.
Capabilities are added through strictly-isolated plugins — neurons (monitor state), channels (senses), tools (cognition capabilities), providers (external services), and filters (signal transformation).
| Plugin | Type | What it does |
|---|---|---|
reminder |
tool | Natural-language reminders with recurrence |
thoughts |
neuron | Builds thought pressure from accumulated unprocessed thoughts |
social-debt |
neuron | Builds social pressure from lack of interaction |
calories |
tool + neuron | Tracks food, calories & weight with proactive deficit monitoring |
news |
tool + filter | Fetches & filters news articles by your interests |
web-search / web-fetch |
tool | Search the web and fetch/clean page content |
📖 Plugin model & API: docs/plugins/overview.md
The codebase is heavily documented. Start here:
- Architecture — the 3-layer brain, CoreLoop, the agentic loop, project structure
- Concepts — Signals · Intents · Energy · Memory · Soul · Conversation history
- Features — Thinking · News · Reminders · Commitments · Desires · Social debt · Motor Cortex
- Plugins — Overview · Neurons · Channels
- Architecture Decision Records — the why behind key choices
Language & runtime: TypeScript (strict, ESM) on Node.js ≥ 24 LLM: Vercel AI SDK · OpenAI-compatible providers Memory: Postgres with pgvector (vector store) + a custom JSON graph store · Transformers.js embeddings Sandbox: Docker-isolated runtime + IPC Infra: Fastify · Pino logging · Zod validation · Luxon time
npm run dev # run with hot reload (tsx)
npm run build # compile TypeScript → dist/
npm start # run the compiled build
npm test # isolated unit and ordinary integration tests
npm run test:docker # retained backend; heavy image acceptance is CI-only
npm run test:watch # unsupported — see Test isolation below
npm run check # typecheck + lint + format + suite, via the isolated launcher
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run format # prettier --writeTests live under tests/ (unit, integration, helpers) — never inside src/. Pre-commit hooks (Husky + lint-staged) enforce lint & formatting on staged files, then run the product checks through the isolated launcher.
Local and ordinary CI suites never start on the host. npm test and npm run check start
scripts/test-isolated.mjs, which runs the checks and the suite one-shot and
bounded inside a disposable Node 24 container built from a selective snapshot
of the tree. Real Kubernetes server-side apply ownership transitions are accepted
against managedFields on the owner's VM; the local installer double does not
emulate an API server. The run never sees your .env, your data/ state, your git
credentials or your Docker daemon — nothing of your checkout is bind-mounted
and no Docker socket is mounted — and the launcher stops and removes the
containers it started when the run ends, fails, times out or is interrupted
(a hard kill leaves a container that the next run prunes and reports).
Prerequisites for tests and checks: Node.js ≥ 24 and Docker. Watch mode is
explicitly unsupported (npm run test:watch explains why and exits): run a
bounded one-shot suite instead, e.g.
node scripts/test-isolated.mjs test -- tests/unit/energy-management.test.ts.
Ordinary checks use an offline, non-root Node 24.21.0 container (2 CPUs,
3 GiB memory, no extra swap, 256 processes). Its root is read-only; temporary
writes use capped memory-backed filesystems. Dependencies are installed inside
a container and cached. Per-run containers and source images are removed after
success, failure, timeout or interruption. The default deadline is 25 minutes.
docker mode uses a private rootless daemon in a disposable OrbStack machine
on macOS: no Mac mounts, no SSH forwarding, and network isolation from the host
and other machines. Its limits are 2 CPUs, 4 GiB memory and 32 GiB disk. OrbStack
shares a Linux kernel; it is not a hardware VM. Missing or unsupported backends
fail closed, never to your socket or a privileged fallback. CI runs ordinary
checks through the same launcher with GitHub's disposable Docker daemon.
CI builds the standalone core image separately; it does not run tests directly on the host.