MusePi

Session tree architecture (current)

English | 中文 Reference: session.md

This document describes how session tree navigation works today: in-memory tree model, leaf movement rules, branching behavior, and extension/event integration.

What this subsystem is

The session is stored as an append-only entry log, but runtime behavior is tree-based:

Key files:

Tree data model in SessionManager

Runtime indices live in a SessionEntryIndex helper, held as #index on SessionManager and kept in lockstep with the journal array #entries:

Tree APIs:

getTree() is a runtime projection; persistence remains append-only JSONL entries.

Leaf movement semantics

There are three leaf movement primitives:

  1. branch(entryId)
    • Validates entry exists
    • Sets leafId = entryId
    • No new entry is written
  2. resetLeaf()
    • Sets leafId = null
    • Next append creates a new root entry (parentId = null)
  3. branchWithSummary(branchFromId, summary, details?, fromExtension?)
    • Accepts branchFromId: string | null
    • Sets leafId = branchFromId
    • Appends a branch_summary entry as child of that leaf
    • When branchFromId is null, fromId is persisted as "root"

/tree navigation behavior (same session file)

AgentSession.navigateTree() is navigation, not file forking.

Flow:

  1. Validate target and compute abandoned path (collectEntriesForBranchSummary)
  2. Emit session_before_tree with TreePreparation
  3. Optionally summarize abandoned entries (hook-provided summary or built-in summarizer)
  4. Compute new leaf target:
    • selecting a user message: leaf moves to its parent, and message text is returned for editor prefill
    • selecting a custom_message: same rule as user message (leaf = parent, text prefills editor)
    • selecting any other entry: leaf = selected entry id
  5. Apply leaf move:
    • with summary: branchWithSummary(newLeafId, ...)
    • without summary and newLeafId === null: resetLeaf()
    • otherwise: branch(newLeafId)
  6. Rebuild agent context from new leaf and emit session_tree

Important: summary entries are attached at the new navigation position, not on the abandoned branch tail.

/branch behavior (new session file)

/branch and /tree are intentionally different:

User-facing /branch flow (SelectorController.showUserMessageSelectorAgentSession.branch):

SessionManager.createBranchedSession(leafId) specifics:

Context reconstruction and summary/custom integration

buildSessionContext() (in session-context.ts, exposed via SessionManager.buildSessionContext()) resolves the active root→leaf path and builds effective LLM context state:

session/messages.ts then maps these message types for model input:

So tree movement changes context by changing the active leaf path, not by mutating old entries.

Labels and tree UI behavior

Label persistence:

Tree selector behavior (tree-selector.ts):

Command routing:

Extension and hook touchpoints for tree operations

Command-time extension API (ExtensionCommandContext):

Events around tree navigation:

Adjacent but related lifecycle hooks:

Real constraints and edge conditions

Plan approval session naming

When a user approves a plan from plan mode (InteractiveMode.#approvePlan), the approval handler seeds the session name from the plan’s title so the resulting (fresh or compacted) session does not stay unnamed.

Trigger:

Naming source:

Examples (from humanizePlanTitle):

Legacy compatibility still present

Session migrations still run on load:

Current runtime behavior is version-3 tree semantics after migration.