Archon Session State Machine: How It Manages AI-Assistant Lifecycle Transitions

Archon's session state machine is a deterministic, type-safe model that categorizes transition triggers into creates, deactivates, or none behaviors to govern exactly when AI-assistant sessions are terminated, regenerated, or preserved during conversation flows.

The session state machine in coleam00/Archon provides the single source of truth for managing AI-assistant conversation lifecycles. Implemented in packages/core/src/state/session-transitions.ts, it uses a strictly typed TransitionTrigger enum and immutable behavior mappings to ensure every state change is auditable via parent-child session chains stored in the database.

Core Architecture: Transition Triggers and Behaviors

The state machine centers on the TransitionTrigger union type, which enumerates every event capable of forcing a session state change:

export type TransitionTrigger =
  | 'first-message'          // a brand‑new conversation
  | 'plan-to-execute'       // the user moved from a *plan* step to execution
  | 'isolation-changed'     // the worktree / cwd was switched
  | 'reset-requested'       // user ran /reset
  | 'worktree-removed'      // a worktree was manually deleted
  | 'conversation-closed';  // platform closed the thread

Source: packages/core/src/state/session-transitions.ts (lines 10-16)

Each trigger maps to exactly one behavior category in the TRIGGER_BEHAVIOR constant:

  • creates – Deactivate the current session and immediately create a fresh one (exclusively for plan-to-execute)
  • deactivates – Only deactivate the session; the next message lazily initializes a new session (covers isolation-changed, reset-requested, worktree-removed, and conversation-closed)
  • none – No session action required (only for first-message)
const TRIGGER_BEHAVIOR = {
  'first-message':      'none',
  'plan-to-execute':   'creates',
  'isolation-changed': 'deactivates',
  'reset-requested':   'deactivates',
  'worktree-removed':  'deactivates',
  'conversation-closed':'deactivates',
} as const;

Source: packages/core/src/state/session-transitions.ts (lines 27-34)

Predicate Helpers for Deterministic Decisions

The system exposes two pure predicate functions to query the state machine without branching logic scattered through the orchestrator:

  • shouldCreateNewSession(trigger) returns true only when TRIGGER_BEHAVIOR[t] === 'creates'
  • shouldDeactivateSession(trigger) returns true for every trigger except first-message
export function shouldCreateNewSession(t: TransitionTrigger) {
  return TRIGGER_BEHAVIOR[t] === 'creates';
}
export function shouldDeactivateSession(t: TransitionTrigger) {
  return TRIGGER_BEHAVIOR[t] !== 'none';
}

Source: packages/core/src/state/session-transitions.ts (lines 39-47)

TypeScript's exhaustiveness checking guarantees that any new TransitionTrigger added to the union must be handled in TRIGGER_BEHAVIOR and these helpers, or compilation fails.

Plan-to-Execute Transition Detection

A critical workflow in Archon involves detecting when a user moves from planning to execution. When the user runs execute immediately after a plan-feature command (or GitHub-specific variants), the system identifies this as the plan-to-execute trigger—the only trigger in the creates category that forces immediate session regeneration.

Source: packages/core/src/state/session-transitions.ts (lines 54-64)

Orchestrator Integration: From Trigger to Database

The orchestrator in packages/core/src/orchestrator/orchestrator-agent.ts consumes the state machine to manage session lifecycle without embedding business logic in database queries.

Handling the First Message

When a conversation contains no active session, the orchestrator uses the first-message trigger to initialize state:

if (!session) {
  session = await sessionDb.transitionSession(conv.id, 'first-message', {
    ai_assistant_type: conv.ai_assistant_type,
  });
}

Source: packages/core/src/orchestrator/orchestrator-agent.ts (lines 45-52)

Processing Slash Commands and State Changes

For subsequent interactions, the orchestrator maps slash commands to triggers via getTriggerForCommand (defined in session-transitions.ts lines 71-89). When a command like /reset is parsed, the orchestrator evaluates the trigger:

const trigger = getTriggerForCommand('reset'); // → 'reset-requested'

If shouldDeactivateSession(trigger) returns true, the orchestrator invokes sessionDb.transitionSession, which performs an atomic database operation:

  1. Deactivates the current session (active = false, sets ended_at and ended_reason)
  2. Creates a new session row linked via parent_session_id
  3. Records the trigger in transition_reason for audit trails
// inside transitionSession()
if (current) {
  await query(
    `UPDATE remote_agent_sessions SET active = false, ended_at = ${dialect.now()}, ended_reason = $2 WHERE id = $1`,
    [current.id, reason]
  );
}
// create new linked session …

Sources: packages/core/src/db/sessions.ts (lines 22-33 and 71-78)

Practical Implementation Examples

Detecting Transitions from Slash Commands

import { getTriggerForCommand, shouldDeactivateSession } from '@archon/core/src/state/session-transitions';

const command = 'reset';
const trigger = getTriggerForCommand(command); // 'reset-requested'

if (trigger && shouldDeactivateSession(trigger)) {
  await sessionDb.transitionSession(conversation.id, trigger, {
    ai_assistant_type: conversation.ai_assistant_type,
  });
}

Forcing New Session Creation After Plan-to-Execute

import { shouldCreateNewSession } from '@archon/core/src/state/session-transitions';

const trigger = 'plan-to-execute';
if (shouldCreateNewSession(trigger)) {
  const newSess = await sessionDb.transitionSession(conv.id, trigger, {
    ai_assistant_type: conv.ai_assistant_type,
  });
  // `newSess.id` is now the fresh session used for the execution phase
}

Walking the Session Audit Chain

import { getSessionChain } from '@archon/core/src/db/sessions';

const chain = await getSessionChain(session.id);
chain.forEach(s => console.log(`Session ${s.id} – reason: ${s.transition_reason}`));

Summary

  • Type-Safe Triggers: The TransitionTrigger enum and TRIGGER_BEHAVIOR mapping provide compile-time guarantees that all state transitions are handled.
  • Three Behavioral Categories: Triggers classify as creates (immediate regeneration), deactivates (lazy next-session creation), or none (no action).
  • Database Audit Trail: Every transition updates the remote_agent_sessions table with active, ended_reason, parent_session_id, and transition_reason fields.
  • Orchestrator Abstraction: The orchestrator delegates state decisions to pure functions in session-transitions.ts while sessions.ts handles atomic DB operations.
  • Plan-to-Execute Special Case: Only the plan-to-execute trigger uses the creates behavior, ensuring execution phases receive fresh session contexts.

Frequently Asked Questions

How does Archon determine when to create a new AI-assistant session versus just deactivating the current one?

Archon uses the shouldCreateNewSession() predicate to check if a trigger maps to the creates behavior category. Currently, only the plan-to-execute trigger returns true, forcing immediate creation of a new session. All other triggers either use deactivates (which waits for the next message to lazily create a session) or none (for the first message in a conversation).

What happens to session data when a user runs the /reset command?

The orchestrator maps /reset to the reset-requested trigger via getTriggerForCommand(). Since this trigger classifies as deactivates, the transitionSession() function in packages/core/src/db/sessions.ts sets active = false and records ended_reason on the current row, then creates a new session with a parent_session_id link to the old one. The historical session remains in the database for audit purposes but is no longer active.

Where is the session state machine logic located in the Archon codebase?

The core state machine definitions reside in packages/core/src/state/session-transitions.ts, which exports the TransitionTrigger type, TRIGGER_BEHAVIOR constants, and predicate helpers. The database implementation lives in packages/core/src/db/sessions.ts, while the orchestrator that consumes these utilities is in packages/core/src/orchestrator/orchestrator-agent.ts.

Why does the plan-to-execute trigger behave differently from other triggers?

The plan-to-execute trigger represents a workflow boundary where the AI assistant transitions from planning mode to execution mode. According to the source code in session-transitions.ts, this is the only trigger classified under creates because execution phases require a completely fresh context without carrying over planning state, whereas other triggers like isolation-changed or reset-requested simply deactivate the session to be lazily recreated on the next user message.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →