State Model

An agent isn't a stateless function. Between a bare axon.request("hello") and a multi-day project thread, there are four distinct layers of state, each with different scope and lifetime. Understanding which layer holds what explains why the agent remembers some things and not others.

State layers, outermost to innermost:

LayerScopeLifetime
Base stateAll sessionsProcess lifetime
Session stateOne working sessionUntil session ends
Thread stateOne conversation lineageWithin session
Request contextOne invocationCleared when loop completes

Base state

Base state is what the agent is when nothing is happening. It comes entirely from the agent folder and is loaded by the runtime.

  • src/boot.vue — identity, working practices, standing instructions
  • src/tools/ — what the agent can call
  • src/prompts/ — available context templates
  • axon.config.ts — engine, policy, environment
  • Installed modules — their tools, prompts, and routes

Base state changes when source changes. In local development, hot reload applies those changes to future work without rewriting the current session or thread history. Nothing that happens during execution changes base state.

This is why the agent's identity and working practices don't drift over a session — they come from source, not from conversation history.

Session state

Session state accumulates over a working session. It includes conversation history across all threads, summaries the runtime builds as threads grow, and any material the agent writes to data/sessions/.

A session typically maps to one working context — one day's work on a project, one deployment cycle, one support shift. Sessions persist to disk, so they survive agent restarts.

Session state is the agent's long-term working memory. It's why an agent can be restarted and still know what it was working on.

Thread state

A thread is a scoped slice of working memory for one conversation lineage. The agent sees the full history of a thread on every call to it, but not the history of other threads.

This is the primary tool for structuring complex work. A main execution thread carries the primary conversation. Named side threads handle isolated sub-tasks without polluting the main context. Parallel threads run independent research simultaneously.

// three calls on the execution thread — agent sees full history across all three
const r1 = await axon.request("what's the current sprint status?")
const r2 = await axon.request("which issues are blocking the release?")
const r3 = await axon.request("draft a status update for the team")

// side thread — isolated, agent has no context from r1-r3
const r4 = await axon.request({
    prompt: "research the error in the sentry trace",
    thread: "sentry-investigation",
})

Thread state persists within a session. Named threads — those with an explicit string name — also persist across script runs within the same session, so work can be resumed.

For the full thread composition patterns, see axon.

Request context

Request context is temporary task state for a single invocation. It's what you pass to axon.request() or axon.stream() — rendered prompts, policy narrowing, inline data.

const context = await axon.prompt("project-context")        // standing task framing
const review  = await axon.prompt("code-review", { issueId })  // task-specific context

const result = await axon.request({
    prompt: [context, review],                      // request context
    policy: { fs: { write: false } },              // narrowed for this invocation
})
// prompt and policy are gone when this completes

Request context is consumed and cleared when the loop completes. Loading a support ticket for one invocation doesn't permanently teach the agent about that ticket — the next invocation starts without it.

This is what makes agents predictable. The same agent handles a hundred different tasks over a session without cross-contamination between them.

Why the layers exist

Each layer exists because it has different ownership and different change cadence.

Base state changes when you edit the agent source. Session state changes as the agent works. Thread state changes per conversation. Request context changes per call.

If everything lived in one pool, an agent that handled a hundred tasks in a day would have a context window full of noise by task fifty. The layering keeps each invocation focused on what's relevant — the agent's permanent identity, the session's accumulated context, the thread's conversation history, and the specific task at hand.