Events & Lifecycle
Subscribing to brain events, the order they fire in, timing expectations, and the full event reference.
Brain emits events at every pipeline stage. Use them for real-time UIs, debugging, side effects, or forwarding to external systems. Neuron events are forwarded through Brain — subscribe on Brain.
Subscribing
// Specific event (typed)
brain.on('neuron:synthesized', (payload) => {
console.log(`${payload.neuronId}: ${payload.significance} — ${payload.evolution}`)
})
// All events
brain.on((event) => {
console.log(`[${event.type}] ${event.id} at ${event.timestamp}`)
})Forwarding to a UI
// Forward all brain events to an Electron renderer
brain.on((event) => {
window.webContents.send('brain:event', {
type: event.type,
...event.payload,
})
})See Recipes — SSE Event Broadcasting for a server-sent events example.
Catching init events
brain.on(...) can only see events fired after it runs. But brain:init:* and neuron:init:* events fire during Brain.create() / Brain.restore() — before you hold a brain reference to subscribe on. To observe the init sequence, pass an onEvent handler in the config (or restore runtime options). It is attached via .on(...) before initialization starts:
const brain = await Brain.create({
prompt: '...',
model: openai('gpt-4o'),
onEvent: (event) => console.log(`[${event.type}]`), // sees brain:init:started onward
})onEvent is also accepted by Brain.restore() runtime options, and by TextNeuron/ListNeuron create configs and restore options for standalone-neuron neuron:init:* events.
Lifecycle
A Brain's lifecycle has two phases:
BIRTH LEARN (repeats per inject)
───── ─────
Config generation Observe (parallel)
Neuron setup (sequential) Synthesize
Internal neuron setup Internal cascade
Ready Evolve (optional)Birth happens once. Learning repeats every time you call inject().
Internal event names use the
brain:init:*/neuron:init:*prefixes. The public callsite isBrain.create(config)for fresh-init andBrain.restore(pathOrStore, runtime?)for restore — there is no separatebrain.init()step.
Birth
Brain.create() runs three stages (skipped on Brain.restore(), which loads from the store with no LLM calls):
1. Config generation — the Brain decomposes your prompt into neuron configurations. Skipped if you provide explicit neurons.
brain:init:started
brain:init:config:generating
brain:init:config:generated → N neuron configs2. Neuron setup — each neuron is initialized sequentially. The observe and understand system prompts are assembled deterministically from the verbatim instructions (no LLM call). Each neuron makes one init LLM call — a TextNeuron for cognitive-skill customization, a ListNeuron for schema generation (skipped when a custom schema is supplied).
neuron:init:started → neuron 1
neuron:init:completed
brain:neuron:added → neuron 1 visible
neuron:init:started → neuron 2
neuron:init:completed
brain:neuron:added → neuron 2 visiblebrain:neuron:added fires when a neuron becomes visible to the outside world.
3. Internal neuron setup — Brain initializes its four internal neurons. Sequentially, but no brain:neuron:added (they're internal).
neuron:init:started / completed ×4
brain:init:completed → Brain is readyLearning
Each brain.inject() call processes data through observe → synthesize. Injections are split into batches.
brain:inject:started
└─ Batch 1 ─────────────────────────────────────
neuron:observe:started (×N) ← parallel
neuron:observe:thinking ← per neuron
neuron:observed ← observation recorded
neuron:synthesize:started ← begins when observe completes
neuron:synthesized ← understanding updated
neuron:health:updated ← always follows synthesize
brain:inject:batch:completed
└─ Batch 2 (internal cascade) ──────────────────
neuron:observe:started (×M) ← internal neurons
neuron:synthesize:started
neuron:synthesized
neuron:health:updated
neuron:query:started ← internal global query
brain:inject:batch:completed
brain:inject:completedKey invariants:
- Observe is parallel. All neurons in a batch begin observing at the same time.
- Synthesize follows observe. A neuron synthesizes as soon as its buffer crosses a threshold — it doesn't wait for siblings.
neuron:health:updatedalways followsneuron:synthesized. Reliable pair.- Internal neurons run in a second batch. They observe the user-facing neurons' updated state, then synthesize their own meta-knowledge. This is why you see two batches per injection.
Async cascade
Internal neurons issue queries that complete asynchronously — often during the next injection rather than the current one:
Injection 1:
neuron:synthesized
neuron:query:started ← internal query begins
brain:inject:completed ← injection finishes, query still running
Injection 2:
brain:inject:started
neuron:observe:started (×N)
neuron:query:completed ← previous query finishes mid-injection
...This overlap is normal. Internal queries don't block injections.
Evolution signals
When neurons detect structural problems (high dismissal, coverage gaps, low relevance), signals fire:
neuron:synthesized
brain:signal:received ← evolution signal
neuron:health:updatedSignals accumulate. When enough build up (default threshold: 5), the evaluator triggers automatically and may restructure the brain. See Evolution.
Timing expectations
Rough ranges based on typical LLM latency. Actual times vary by model, provider, and data volume.
| Phase | Typical duration |
|---|---|
| Config generation | 2–4s |
| Per neuron init | 3–10s |
| Full birth (2–3 neurons) | 30–60s |
| Observe (per batch) | 2–4s |
| Synthesize (per neuron) | 2–10s |
| Internal query | 3–6s |
| Single injection | 8–30s |
The largest variance comes from synthesis — a neuron integrating many observations into a complex understanding takes longer than one processing routine data. Injections that trigger evolution take longer still.
Event Reference
Events are grouped by phase. The most-used ones are neuron:synthesized (a neuron updated its knowledge), brain:ask:completed (a query finished), and evaluator:evaluation:completed (the evolution system made decisions).
Brain Events
| Phase | Events | Key payload fields |
|---|---|---|
| Init | brain:init:started, brain:init:config:generating, brain:init:config:generated, brain:init:completed, brain:init:failed | configs[], neuronIds[], usage |
| Inject | brain:inject:started, brain:inject:batch:started, brain:inject:batch:completed, brain:inject:completed, brain:inject:failed | injectId, itemCount, batchCount, results[] |
| Ask | brain:ask:started, brain:ask:synthesis:started, brain:ask:completed, brain:ask:failed | queryId, query, insight, sources[], gaps[], usage |
| Neuron Mgmt | brain:neuron:added, brain:neuron:removed, brain:neuron:status:changed | neuronId, name, instructions, previousStatus, newStatus |
| Signals | brain:signal:received | source, description, timestamp |
| Evaluator | evaluator:evaluation:started, evaluator:evaluation:completed, evaluator:evaluation:failed | signalCount, decisions[], reasoning |
| Evolution | evolution:action:started, evolution:action:executed, evolution:action:failed | action, targets[], reasoning, result |
| Config | brain:config:updated | updates, changedFields[] |
Neuron Events
| Phase | Events | Key payload fields |
|---|---|---|
| Init | neuron:init:started, neuron:init:completed, neuron:init:failed | neuronId, systemPrompt, usage |
| Observe | neuron:observe:started, neuron:observe:thinking, neuron:observed, neuron:observe:dismissed, neuron:observe:error | neuronId, output[], importance, bufferCount, gaps[] |
| Synthesize | neuron:synthesize:started, neuron:synthesize:thinking, neuron:synthesized, neuron:synthesize:dismissed, neuron:synthesize:error | neuronId, newUnderstanding, significance, evolution |
| Learn | neuron:learn:failed | neuronId, error |
| Query | neuron:query:started, neuron:query:completed, neuron:query:failed, neuron:query:thinking | neuronId, insight, relevance, confidence, gaps[] |
| Health | neuron:health:updated | neuronId, activation, status, previousStatus |
| Signals | neuron:signal | neuronId, description, metrics |
| Config | neuron:config:updated, neuron:prompts:regenerated, neuron:understanding:set, neuron:understanding:adjusted | neuronId, changedFields[], directive, significance |
Thinking events
neuron:observe:thinking, neuron:synthesize:thinking, and neuron:query:thinking fire when the LLM produces intermediate reasoning. Payload: thoughts: string[] and usage: TokenUsage. Useful for debugging (seeing why a neuron made a decision) and real-time UI feedback (streaming the thought process to users).