- Home
- Documentation
- Sessioni
- Architettura ad albero delle sessioni (corrente)
Architettura ad albero delle sessioni (corrente)
Riferimento: session.md
Questo documento descrive il funzionamento attuale della navigazione ad albero delle sessioni: modello ad albero in memoria, regole di movimento delle foglie, comportamento di diramazione e integrazione con estensioni/eventi.
Cosa rappresenta questo sottosistema
Sezione intitolata “Cosa rappresenta questo sottosistema”La sessione è memorizzata come un log di voci append-only, ma il comportamento a runtime è basato su albero:
- Ogni voce non di intestazione ha
ideparentId. - La posizione attiva è
leafIdinSessionManager. - L’aggiunta di una voce crea sempre un figlio della foglia corrente.
- La diramazione non riscrive la cronologia; modifica solo il punto a cui la foglia punta prima del successivo inserimento.
File chiave:
src/session/session-manager.ts— modello dati ad albero, attraversamento, movimento delle foglie, estrazione di rami/sessionisrc/session/agent-session.ts— flusso di navigazione/tree, riepilogo, emissione di hook/eventisrc/modes/components/tree-selector.ts— comportamento dell’interfaccia ad albero interattiva e filtraggiosrc/modes/controllers/selector-controller.ts— orchestrazione del selettore per/treee/branchsrc/modes/controllers/input-controller.ts— instradamento dei comandi (/tree,/branch, comportamento del doppio Escape)src/session/messages.ts— conversione delle vocibranch_summary,compactionecustom_messagein messaggi di contesto per il modello LLM
Modello dati ad albero in SessionManager
Sezione intitolata “Modello dati ad albero in SessionManager”Indici a runtime:
#byId: Map<string, SessionEntry>— ricerca rapida per qualsiasi voce#leafId: string | null— posizione corrente nell’albero#labelsById: Map<string, string>— etichette risolte per id della voce di destinazione
API dell’albero:
getBranch(fromId?)percorre i collegamenti al nodo padre fino alla radice e restituisce il percorso radice→nodogetTree()restituisceSessionTreeNode[](entry,children,label)- i collegamenti al nodo padre diventano array di figli
- le voci con nodi padre mancanti sono trattate come radici
- i figli sono ordinati dal più vecchio al più recente per timestamp
getChildren(parentId)restituisce i figli direttigetLabel(id)risolve l’etichetta corrente dalabelsById
getTree() è una proiezione a runtime; la persistenza rimane come voci JSONL append-only.
Semantica del movimento delle foglie
Sezione intitolata “Semantica del movimento delle foglie”Esistono tre primitive di movimento delle foglie:
-
branch(entryId)- Valida l’esistenza della voce
- Imposta
leafId = entryId - Non viene scritta alcuna nuova voce
-
resetLeaf()- Imposta
leafId = null - Il successivo inserimento crea una nuova voce radice (
parentId = null)
- Imposta
-
branchWithSummary(branchFromId, summary, details?, fromExtension?)- Accetta
branchFromId: string | null - Imposta
leafId = branchFromId - Aggiunge una voce
branch_summarycome figlio di quella foglia - Quando
branchFromIdènull,fromIdviene persistito come"root"
- Accetta
Comportamento della navigazione /tree (stesso file di sessione)
Sezione intitolata “Comportamento della navigazione /tree (stesso file di sessione)”AgentSession.navigateTree() è navigazione, non biforcazione di file.
Flusso:
- Validare la destinazione e calcolare il percorso abbandonato (
collectEntriesForBranchSummary) - Emettere
session_before_treeconTreePreparation - Riepilogare facoltativamente le voci abbandonate (riepilogo fornito dall’hook o riepilogatore integrato)
- Calcolare la nuova destinazione della foglia:
- selezionando un messaggio utente: la foglia si sposta al suo nodo padre e il testo del messaggio viene restituito per la precompilazione dell’editor
- selezionando un custom_message: stessa regola del messaggio utente (foglia = nodo padre, il testo precompila l’editor)
- selezionando qualsiasi altra voce: foglia = id della voce selezionata
- Applicare lo spostamento della foglia:
- con riepilogo:
branchWithSummary(newLeafId, ...) - senza riepilogo e
newLeafId === null:resetLeaf() - altrimenti:
branch(newLeafId)
- con riepilogo:
- Ricostruire il contesto dell’agente dalla nuova foglia ed emettere
session_tree
Importante: le voci di riepilogo sono collegate alla nuova posizione di navigazione, non alla coda del ramo abbandonato.
Comportamento di /branch (nuovo file di sessione)
Sezione intitolata “Comportamento di /branch (nuovo file di sessione)”/branch e /tree sono intenzionalmente diversi:
/treenaviga all’interno del file di sessione corrente./branchcrea un nuovo file di ramo di sessione (o una sostituzione in memoria per la modalità non persistente).
Flusso /branch rivolto all’utente (SelectorController.showUserMessageSelector → AgentSession.branch):
- L’origine del ramo deve essere un messaggio utente.
- Il testo utente selezionato viene estratto per la precompilazione dell’editor.
- Se il messaggio utente selezionato è la radice (
parentId === null): avviare una nuova sessione tramitenewSession({ parentSession: previousSessionFile }). - Altrimenti:
createBranchedSession(selectedEntry.parentId)per biforcre la cronologia fino al limite del prompt selezionato.
Specifiche di SessionManager.createBranchedSession(leafId):
- Costruisce il percorso radice→foglia tramite
getBranch(leafId); genera un errore se mancante. - Esclude le voci
labelesistenti dal percorso copiato. - Ricostruisce nuove voci etichetta dagli
labelsByIdrisolti per le voci che rimangono nel percorso. - Modalità persistente: scrive un nuovo file JSONL e trasferisce il manager su di esso; restituisce il nuovo percorso del file.
- Modalità in memoria: sostituisce le voci in memoria; restituisce
undefined.
Ricostruzione del contesto e integrazione di riepilogo/custom
Sezione intitolata “Ricostruzione del contesto e integrazione di riepilogo/custom”buildSessionContext() (in session-manager.ts) risolve il percorso radice→foglia attivo e costruisce lo stato di contesto LLM effettivo:
- Traccia l’ultimo stato di thinking/model/mode/ttsr sul percorso.
- Gestisce l’ultima compattazione sul percorso:
- emette prima il riepilogo della compattazione
- riproduce i messaggi mantenuti da
firstKeptEntryIdal punto di compattazione - poi riproduce i messaggi successivi alla compattazione
- Include le voci
branch_summaryecustom_messagecome oggettiAgentMessage.
session/messages.ts mappa poi questi tipi di messaggi per l’input del modello:
branchSummaryecompactionSummarydiventano messaggi di contesto con template ruolo-utentecustom/hookMessagediventano messaggi di contenuto ruolo-utente
Pertanto, lo spostamento nell’albero modifica il contesto cambiando il percorso della foglia attiva, senza mutare le voci precedenti.
Etichette e comportamento dell’interfaccia ad albero
Sezione intitolata “Etichette e comportamento dell’interfaccia ad albero”Persistenza delle etichette:
appendLabelChange(targetId, label?)scrive vocilabelsulla catena della foglia corrente.labelsByIdviene aggiornato immediatamente (impostazione o eliminazione).getTree()risolve l’etichetta corrente su ciascun nodo restituito.
Comportamento del selettore ad albero (tree-selector.ts):
- Appiattisce l’albero per la navigazione, mantiene l’evidenziazione del percorso attivo e dà priorità alla visualizzazione del ramo attivo.
- Supporta modalità di filtro:
default,no-tools,user-only,labeled-only,all. - Supporta la ricerca di testo libero sul contenuto semantico visualizzato.
Shift+Lapre la modifica inline delle etichette e scrive tramiteappendLabelChange.
Instradamento dei comandi:
/treeapre sempre il selettore ad albero./branchapre il selettore di messaggi utente a meno chedoubleEscapeAction=tree, nel qual caso usa anche l’interfaccia del selettore ad albero.
Punti di integrazione con estensioni e hook per le operazioni ad albero
Sezione intitolata “Punti di integrazione con estensioni e hook per le operazioni ad albero”API delle estensioni per i comandi (ExtensionCommandContext):
branch(entryId)— crea un file di sessione ramificatonavigateTree(targetId, { summarize? })— sposta all’interno dell’albero/file corrente
Eventi relativi alla navigazione ad albero:
session_before_tree- riceve
TreePreparation:targetIdoldLeafIdcommonAncestorIdentriesToSummarizeuserWantsSummary
- può annullare la navigazione
- può fornire il payload di riepilogo usato al posto del riepilogatore integrato
- riceve il segnale di interruzione
signal(percorso di annullamento tramite Escape)
- riceve
session_tree- emette
newLeafId,oldLeafId - include
summaryEntryquando è stato creato un riepilogo fromExtensionindica l’origine del riepilogo
- emette
Hook del ciclo di vita adiacenti ma correlati:
session_before_branch/session_branchper il flusso/branchsession_before_compact,session.compacting,session_compactper le voci di compattazione che influenzano successivamente la ricostruzione del contesto ad albero
Vincoli reali e condizioni limite
Sezione intitolata “Vincoli reali e condizioni limite”branch()non può averenullcome destinazione; usareresetLeaf()per lo stato precedente alla prima voce radice.branchWithSummary()supporta la destinazionenulle registrafromId: "root".- La selezione della foglia corrente nel selettore ad albero è un’operazione nulla.
- Il riepilogo richiede un modello attivo; in sua assenza, la navigazione con riepilogo fallisce immediatamente.
- Se il riepilogo viene interrotto, la navigazione viene annullata e la foglia rimane invariata.
- Le sessioni in memoria non restituiscono mai un percorso del file di ramo da
createBranchedSession.
Compatibilità con le versioni precedenti ancora presente
Sezione intitolata “Compatibilità con le versioni precedenti ancora presente”Le migrazioni di sessione vengono ancora eseguite al caricamento:
- v1→v2 aggiunge
id/parentIde converte l’ancora dell’indice di compattazione in ancora id - v2→v3 migra il ruolo legacy
hookMessageacustom
Il comportamento a runtime corrente è basato sulla semantica ad albero versione 3 dopo la migrazione.