Sessions
A session is the fundamental unit of work in Magia. It represents a single, isolated conversation with an AI agent that operates inside a specific project directory.
What is a session?
Section titled “What is a session?”When you create a session you choose a working directory. The agent process (e.g. the Claude Code CLI) is spawned with that directory as its root. Every file read, edit, or shell command the agent executes is scoped to that directory tree. Two sessions pointing at the same directory are fully independent — they share no in-memory state and no conversation history.
Each session carries:
| Field | Description |
|---|---|
id | A UUID that is the single source of truth for the session (see Two ID spaces below) |
projectPath | The absolute filesystem path the agent operates in |
provider | Which LLM CLI backs this session (claude, gemini, codex) |
status | Current lifecycle state (see Session lifecycle) |
title + titleSource | Display name and how it was set (see Session naming) |
Two ID spaces
Section titled “Two ID spaces”Magia maintains two separate identifier namespaces for every session:
- Workspace UUID (
session.id) — A UUID generated by Magia. This is the stable, permanent identifier used throughout the UI, the sidebar, and all Tauri commands. It does not change across resumes or forks. - CLI session ID (
session.claudeSessionId, deprecated field) — The session ID assigned by the underlying CLI tool. For Claude Code this is the value passed via--session-idand stored in the.claude/projects/JSONL files. For Gemini and Codex, the CLI generates its own internal IDs.
The two spaces are bridged: when an agent process emits events, those events carry the CLI-level session ID; Magia maps them back to the workspace UUID via an in-memory index so the UI always shows events against the correct session record.
Practical implication: When resuming a session Magia passes the workspace UUID as the --session-id to Claude Code, keeping the two IDs in sync for Claude sessions. For turn-based providers (Gemini, Codex) Magia uses its own UUID for routing only.
Session lifecycle
Section titled “Session lifecycle”create → running → idle → closed ↘ error → dead| Status | Meaning |
|---|---|
running | The agent process is actively executing a task |
idle | The agent has finished its last turn and is waiting for the next message |
error | The agent process exited with a non-zero status or the startup watchdog fired |
closed | The session was explicitly stopped |
dead | The process disappeared unexpectedly (e.g. OOM kill) |
Sessions are created by calling create_agent_session on the Rust backend. For persistent providers (Claude Code), one long-running process is spawned and messages are written to its stdin. For turn-based providers (Gemini CLI, Codex CLI), the process creation is deferred until the first message is sent; each subsequent message spawns a new process with --resume flags.
On app shutdown, all managed agent processes are killed to prevent orphaned processes from consuming API credits.
Session naming
Section titled “Session naming”The display name of a session is derived from two fields:
| Field | Type | Description |
|---|---|---|
title | string | null | The current title value |
titleSource | "user" | "auto" | "prompt" | null | Which mechanism set the title |
A strict priority hierarchy governs which source can overwrite an existing title:
null < prompt < auto < usernull— No title has been set; the UI falls back to"New Session".prompt— Magia extracted the first user prompt from the session transcript during discovery. This is the lowest automatic source.auto— An AI-generated title suggestion was accepted.user— The user explicitly renamed the session. This is never overwritten automatically.
The helper resolveSessionTitle(session) returns session.title ?? "New Session" and is the canonical way to get a displayable name.
Forking and resuming
Section titled “Forking and resuming”Sessions track their lineage through two optional fields:
resumedFrom— The ID of the session this one was resumed from. A resumed session picks up the conversation history of the original and continues from where it left off.forkedFrom— The ID of the session this one was forked from. A forked session starts a new, independent conversation from a specific branch point in the parent session’s history, optionally restoring the git working tree state at that moment.
Branch points are lightweight records that store the conversation event index and an optional git snapshot ref (refs/magia/snapshots/{uuid}). Forking does not create a git worktree immediately — that happens on demand when the user chooses to fork from a branch point.
Pinning
Section titled “Pinning”Sessions can be pinned to the top of the sidebar for quick access. Pinned sessions are ordered manually (drag-to-reorder). A configurable maximum (maxPinnedSessions, 0 = unlimited) prevents the pinned section from growing indefinitely — when the limit is reached, the oldest pinned session is automatically unpinned.
Pinning is stored per-session via pinnedAt (Unix timestamp, null = not pinned) and pinnedOrder (sort position within the pinned section).
Hiding and filtering
Section titled “Hiding and filtering”Sessions can be marked hidden: true to remove them from the default sidebar view without deleting them. Hidden sessions are excluded from discovery and list views unless the user explicitly enables “show hidden sessions”.
The sidebar supports filtering sessions by:
- Free-text search across title, project path, and first prompt
- Provider (
claude,gemini,codex) - Status (
running,idle,error,closed)
Additional working directories
Section titled “Additional working directories”Beyond the primary projectPath, a session can have additionalDirs — extra directories added during a session (e.g. via the /add-dir command in chat). These are passed to the CLI as additional context paths and are persisted with the session record.
Persistence
Section titled “Persistence”Session metadata is persisted to sessions.json in Magia’s app data directory (~/Library/Application Support/sh.magia.com/ on macOS, sh.magia.dev/ in dev mode). The Zustand store is an in-memory cache; the Rust backend is the source of truth for session files.