Behavioral observation daemon

💓 Anima

What it is

Anima is a background daemon that reads a live working session in real time — the model's reasoning, its tool calls, its output, the whole stream — and watches for patterns the moment-to-moment focus tends to miss. It does not modify anything. It does not block anything. It observes, keeps a private running journal, and when a signal crosses a threshold, it surfaces a single, quiet nudge.

The design bet is that the most useful interventions are ambient, not authoritative. A tool that halts you mid-flow to warn you is expensive and easy to resent. A tool that says one true thing softly and then trusts you to decide is something you actually keep running.

Observe, don't interrupt. Whisper, don't shout. Say it once, then trust.

The philosophy

Whisper, don't shout Nudges are ambient and non-blocking — they inform, they never gate.
One nudge, then trust Say a thing once and back off. Repetition is nagging, and nagging gets muted.
Moveset-driven, not timed It reacts to the shape of what's happening, not a clock tick — watches the dance, not the metronome.
Escalation, tiered A three-level scale — from a light note, to a correction, to a genuine intervention — so tone matches stakes.

What it watches

Each signal is a small, cheap detector over the live stream — keyword sentinels, tool-sequence analysis, and rolling-window arithmetic. None of them is smart on its own; the value is in running all of them, quietly, all the time.

🌀 Spiral Retry-and-fail loops — "let me try again," "still failing" — caught by keyword sentinels combined with a rising error rate.
👁️ Blind edits Editing a file without having read it first — pure tool-sequence analysis, no guessing.
Velocity Sudden spikes in tool calls per minute — rolling-window arithmetic over the event stream.
🛒 Drift Scope creep away from the stated task — "while I'm here," "let me also…" — flagged before a small job quietly becomes three.
📉 Affective state Coarse tone signals in the reasoning stream, tracked as two floats (energy, warmth) so the nudge tone can match the moment rather than fight it.

How it decides

A raw keyword match is a false-positive machine. So detection runs in three layers, and a nudge only fires when all three agree:

1 · Structural Cheap arithmetic over the stream — velocity, error rate, file-revisit counts. No language understanding, just shape.
2 · Keyword Behavioral sentinels — known phrases that hint a pattern is forming.
3 · Semantic A small local neural model reads action verbs in context against session history — the layer that tells a real signal from a coincidence.

Requiring all three to align is what keeps false positives near zero — the difference between a tool you trust and one you mute on day two. Internally the daemon splits into three cooperating roles: a journaler that writes observations, a signal window that does the rolling arithmetic, and a history cross-reference that recognises when something echoes an earlier session.

Under the hood

Written in Julia with a small Flux.jl model for the semantic layer, run as a systemd service so it survives crashes and boots with the machine. It reads the transcript like a passenger reading the dashboard — aware, not driving: no hooks into the host, no modifications to output. State is a compact append-only feed, and every detector is a rolling window over it, so memory stays flat no matter how long the session runs. Entirely local — nothing leaves the box. The whole point is a subsystem you forget is running until the one moment it says something worth hearing.

Julia Flux.jl systemd append-only feed rolling-window detectors 100% local

Born from a 3AM sketch over a long weekend — from a scribbled idea to a resident daemon.