@mneva/lc-attention
v0.1.0
Published
A locus-coeruleus inspired attention-gain signal for LLM agents. Phasic + tonic noradrenaline-style modulation with provenance.
Maintainers
Readme
@mneva/lc-attention
A locus-coeruleus inspired attention-gain signal for LLM agents. Phasic + tonic noradrenaline-style modulation with provenance, backed by one Postgres table.
Why
Most agent frameworks treat attention as a static prompt or a sampling-temperature knob. The brain doesn't. The locus coeruleus releases noradrenaline in two distinct modes that shape what the cortex foregrounds:
- tonic — slow baseline tracking recent volatility (~30-60 min window)
- phasic — short pulses on big prediction errors, decay over a few minutes
Both modulate cortical gain: what gets foregrounded, how aggressively priors update. This module exposes the same shape as three functions over one Postgres table. The agent reads lcCurrent() at decision points and acts on the signal: high phasic, widen the attention window; tonic creeping up, slow down, the environment is noisy.
The literature this borrows from:
- Aston-Jones & Cohen 2005 — adaptive gain theory
- Yu & Dayan 2005 — NE encodes unexpected uncertainty
- Bouret & Sara 2005 — network reset
Install
npm install @mneva/lc-attention pgThen apply the migration to your Postgres database:
psql "$DATABASE_URL" -f node_modules/@mneva/lc-attention/migrations/001_lc_samples.sqlUse
import { Pool } from 'pg';
import { lcPulse, lcTonicUpdate, lcCurrent } from '@mneva/lc-attention';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// Caller decides when to pulse. Typical trigger: a high-confidence prediction
// that turned out wrong, or a tool result outside the expected distribution.
await lcPulse(pool, {
gain: 1.7,
trigger_source: 'prediction_miss',
reason: 'expected file to exist, got ENOENT',
inputs: { prior_confidence: 0.9, tool: 'Read' },
});
// Caller decides when to recompute baseline. Typical: a daemon every N minutes.
await lcTonicUpdate(pool, {
gain: 1.25,
reason: 'miss rate 0.4 over last 60 min',
inputs: { window_minutes: 60, miss_rate: 0.4 },
});
// At decision points the agent reads the active gain.
const state = await lcCurrent(pool);
// {
// gain: 1.612,
// source: 'phasic',
// interpretation: 'high_arousal: novelty present, widen attention + accelerate learning',
// components: { phasic: {...}, tonic_underlying: {...} }
// }
if (state.gain > 1.3) {
// widen attention: read more files, query more memory, ask more questions
}How it behaves
When you call lcCurrent():
- if a phasic sample is live (age <
ttl_seconds), its gain is returned, exponentially decayed bydecay_half_life - otherwise if a tonic sample is live, the tonic gain is returned
- otherwise 1.0 (neutral)
Phasic overrides tonic when active. Mixing them with a weighted sum produces a number that doesn't mean anything; the brain doesn't do it, and neither does this.
What this is not
- Not a baseline-computer. This module records and serves. The caller decides what signal to pulse on, and what volatility metric to compute the tonic from. Different agents care about different signals.
- Not a learning-rate scheduler. The output is a number. What you do with it is up to you. The interpretation field is a hint, not a policy.
- Not an MCP server. This is a library. If you want it served over MCP, the wider system this came out of does that. Plain function calls are the unit here.
Design notes
Decay half-life matters more than pulse threshold. Too long and pulses flatten into background noise; too short and they expire before the next decision point. 90s (the default) landed for an agent making decisions every 30-60s.
Phasic overrides tonic. Both exist in the brain. The cortex doesn't average them. Pulses ride on top of baseline and dominate while they're active.
Provenance is part of the signal. A pulse from a high-confidence prediction miss should feel different from a pulse from a noisy sensor. lcCurrent() returns the trigger source and reason so callers can choose how much to weight the signal.
Schema
One table, lc_samples. Each row is a gain sample at a moment in time. Phasic and tonic share the table; mode distinguishes. Indexes are tuned for "most recent active sample" lookups.
See migrations/001_lc_samples.sql.
License
MIT
