Adapt

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 is Brain.create(config) for fresh-init and Brain.restore(pathOrStore, runtime?) for restore — there is no separate brain.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 configs

2. 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 visible

brain: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 ready

Learning

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:completed

Key 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:updated always follows neuron: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:updated

Signals 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.

PhaseTypical duration
Config generation2–4s
Per neuron init3–10s
Full birth (2–3 neurons)30–60s
Observe (per batch)2–4s
Synthesize (per neuron)2–10s
Internal query3–6s
Single injection8–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

PhaseEventsKey payload fields
Initbrain:init:started, brain:init:config:generating, brain:init:config:generated, brain:init:completed, brain:init:failedconfigs[], neuronIds[], usage
Injectbrain:inject:started, brain:inject:batch:started, brain:inject:batch:completed, brain:inject:completed, brain:inject:failedinjectId, itemCount, batchCount, results[]
Askbrain:ask:started, brain:ask:synthesis:started, brain:ask:completed, brain:ask:failedqueryId, query, insight, sources[], gaps[], usage
Neuron Mgmtbrain:neuron:added, brain:neuron:removed, brain:neuron:status:changedneuronId, name, instructions, previousStatus, newStatus
Signalsbrain:signal:receivedsource, description, timestamp
Evaluatorevaluator:evaluation:started, evaluator:evaluation:completed, evaluator:evaluation:failedsignalCount, decisions[], reasoning
Evolutionevolution:action:started, evolution:action:executed, evolution:action:failedaction, targets[], reasoning, result
Configbrain:config:updatedupdates, changedFields[]

Neuron Events

PhaseEventsKey payload fields
Initneuron:init:started, neuron:init:completed, neuron:init:failedneuronId, systemPrompt, usage
Observeneuron:observe:started, neuron:observe:thinking, neuron:observed, neuron:observe:dismissed, neuron:observe:errorneuronId, output[], importance, bufferCount, gaps[]
Synthesizeneuron:synthesize:started, neuron:synthesize:thinking, neuron:synthesized, neuron:synthesize:dismissed, neuron:synthesize:errorneuronId, newUnderstanding, significance, evolution
Learnneuron:learn:failedneuronId, error
Queryneuron:query:started, neuron:query:completed, neuron:query:failed, neuron:query:thinkingneuronId, insight, relevance, confidence, gaps[]
Healthneuron:health:updatedneuronId, activation, status, previousStatus
Signalsneuron:signalneuronId, description, metrics
Configneuron:config:updated, neuron:prompts:regenerated, neuron:understanding:set, neuron:understanding:adjustedneuronId, 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).

On this page