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 forplan-to-execute)deactivates– Only deactivate the session; the next message lazily initializes a new session (coversisolation-changed,reset-requested,worktree-removed, andconversation-closed)none– No session action required (only forfirst-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)returnstrueonly whenTRIGGER_BEHAVIOR[t] === 'creates'shouldDeactivateSession(trigger)returnstruefor every trigger exceptfirst-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:
- Deactivates the current session (
active = false, setsended_atandended_reason) - Creates a new session row linked via
parent_session_id - Records the trigger in
transition_reasonfor 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
TransitionTriggerenum andTRIGGER_BEHAVIORmapping provide compile-time guarantees that all state transitions are handled. - Three Behavioral Categories: Triggers classify as
creates(immediate regeneration),deactivates(lazy next-session creation), ornone(no action). - Database Audit Trail: Every transition updates the
remote_agent_sessionstable withactive,ended_reason,parent_session_id, andtransition_reasonfields. - Orchestrator Abstraction: The orchestrator delegates state decisions to pure functions in
session-transitions.tswhilesessions.tshandles atomic DB operations. - Plan-to-Execute Special Case: Only the
plan-to-executetrigger uses thecreatesbehavior, 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →