- ホーム
- Documentation
- セッション
- セッションツリーアーキテクチャ(現行)
セッションツリーアーキテクチャ(現行)
リファレンス: session.md
このドキュメントでは、セッションツリーナビゲーションの現在の仕組みについて説明します:インメモリツリーモデル、リーフ移動ルール、ブランチ動作、および拡張機能/イベント統合。
このサブシステムとは
Section titled “このサブシステムとは”セッションは追記専用のエントリログとして保存されますが、ランタイムの動作はツリーベースです:
- ヘッダー以外のすべてのエントリは
idとparentIdを持ちます。 - アクティブな位置は
SessionManagerのleafIdです。 - エントリの追加は常に現在のリーフの子として作成されます。
- ブランチは履歴を書き換えません。次の追加の前にリーフの指す先を変更するだけです。
主要ファイル:
src/session/session-manager.ts— ツリーデータモデル、走査、リーフ移動、ブランチ/セッション抽出src/session/agent-session.ts—/treeナビゲーションフロー、要約、フック/イベント発行src/modes/components/tree-selector.ts— インタラクティブなツリーUIの動作とフィルタリングsrc/modes/controllers/selector-controller.ts—/treeと/branchのセレクターオーケストレーションsrc/modes/controllers/input-controller.ts— コマンドルーティング(/tree、/branch、ダブルエスケープ動作)src/session/messages.ts—branch_summary、compaction、およびcustom_messageエントリのLLMコンテキストメッセージへの変換
SessionManager のツリーデータモデル
Section titled “SessionManager のツリーデータモデル”ランタイムインデックス:
#byId: Map<string, SessionEntry>— 任意のエントリの高速ルックアップ#leafId: string | null— ツリー内の現在位置#labelsById: Map<string, string>— ターゲットエントリIDによる解決済みラベル
ツリーAPI:
getBranch(fromId?)は親リンクをルートまでたどり、ルート→ノードのパスを返しますgetTree()はSessionTreeNode[](entry、children、label)を返します- 親リンクが子配列に変換されます
- 親が見つからないエントリはルートとして扱われます
- 子はタイムスタンプの古い順→新しい順にソートされます
getChildren(parentId)は直接の子を返しますgetLabel(id)はlabelsByIdから現在のラベルを解決します
getTree() はランタイムの射影であり、永続化は追記専用のJSONLエントリのままです。
リーフ移動のセマンティクス
Section titled “リーフ移動のセマンティクス”3つのリーフ移動プリミティブがあります:
-
branch(entryId)- エントリの存在を検証します
leafId = entryIdを設定します- 新しいエントリは書き込まれません
-
resetLeaf()leafId = nullを設定します- 次の追加で新しいルートエントリ(
parentId = null)が作成されます
-
branchWithSummary(branchFromId, summary, details?, fromExtension?)branchFromId: string | nullを受け取りますleafId = branchFromIdを設定します- そのリーフの子として
branch_summaryエントリを追加します branchFromIdがnullの場合、fromIdは"root"として永続化されます
/tree ナビゲーション動作(同一セッションファイル内)
Section titled “/tree ナビゲーション動作(同一セッションファイル内)”AgentSession.navigateTree() はナビゲーションであり、ファイルのフォークではありません。
フロー:
- ターゲットを検証し、放棄されるパスを計算します(
collectEntriesForBranchSummary) TreePreparationとともにsession_before_treeを発行します- オプションで放棄されるエントリを要約します(フック提供の要約または組み込みサマライザー)
- 新しいリーフターゲットを計算します:
- user メッセージを選択した場合:リーフはその親に移動し、メッセージテキストがエディターのプリフィルとして返されます
- custom_message を選択した場合:userメッセージと同じルール(リーフ = 親、テキストがエディターにプリフィル)
- その他のエントリを選択した場合:リーフ = 選択されたエントリのID
- リーフ移動を適用します:
- 要約あり:
branchWithSummary(newLeafId, ...) - 要約なしで
newLeafId === null:resetLeaf() - それ以外:
branch(newLeafId)
- 要約あり:
- 新しいリーフからエージェントコンテキストを再構築し、
session_treeを発行します
重要:要約エントリは、放棄されたブランチの末尾ではなく、新しいナビゲーション位置に添付されます。
/branch 動作(新しいセッションファイル)
Section titled “/branch 動作(新しいセッションファイル)”/branch と /tree は意図的に異なります:
/treeは現在のセッションファイル内をナビゲートします。/branchは新しいセッションブランチファイルを作成します(非永続モードではインメモリ置換)。
ユーザー向け /branch フロー(SelectorController.showUserMessageSelector → AgentSession.branch):
- ブランチソースは user メッセージ である必要があります。
- 選択されたユーザーテキストがエディターのプリフィル用に抽出されます。
- 選択されたユーザーメッセージがルート(
parentId === null)の場合:newSession({ parentSession: previousSessionFile })で新しいセッションを開始します。 - それ以外:
createBranchedSession(selectedEntry.parentId)で選択されたプロンプト境界までの履歴をフォークします。
SessionManager.createBranchedSession(leafId) の詳細:
getBranch(leafId)でルート→リーフのパスを構築します。見つからない場合はスローします。- コピーされるパスから既存の
labelエントリを除外します。 - パスに残るエントリに対して、解決済みの
labelsByIdから新しいラベルエントリを再構築します。 - 永続モード:新しいJSONLファイルを書き込み、マネージャーをそちらに切り替えます。新しいファイルパスを返します。
- インメモリモード:インメモリのエントリを置換します。
undefinedを返します。
コンテキスト再構築と要約/カスタム統合
Section titled “コンテキスト再構築と要約/カスタム統合”buildSessionContext()(session-manager.ts 内)はアクティブなルート→リーフのパスを解決し、有効なLLMコンテキスト状態を構築します:
- パス上の最新のthinking/model/mode/ttsr状態を追跡します。
- パス上の最新のコンパクションを処理します:
- まずコンパクション要約を出力します
firstKeptEntryIdからコンパクションポイントまでの保持メッセージを再生します- その後、コンパクション後のメッセージを再生します
branch_summaryとcustom_messageエントリをAgentMessageオブジェクトとして含めます。
session/messages.ts はこれらのメッセージタイプをモデル入力用にマッピングします:
branchSummaryとcompactionSummaryはuserロールのテンプレート化されたコンテキストメッセージになりますcustom/hookMessageはuserロールのコンテンツメッセージになります
つまり、ツリー移動は古いエントリを変更するのではなく、アクティブなリーフパスを変更することでコンテキストを変更します。
ラベルとツリーUIの動作
Section titled “ラベルとツリーUIの動作”ラベルの永続化:
appendLabelChange(targetId, label?)は現在のリーフチェーン上にlabelエントリを書き込みます。labelsByIdは即座に更新されます(設定または削除)。getTree()は返される各ノードに現在のラベルを解決します。
ツリーセレクターの動作(tree-selector.ts):
- ツリーをフラット化してナビゲーションし、アクティブパスのハイライトを維持し、アクティブブランチの表示を優先します。
- フィルターモードをサポート:
default、no-tools、user-only、labeled-only、all。 - レンダリングされたセマンティックコンテンツに対するフリーテキスト検索をサポートします。
Shift+Lでインラインラベル編集を開き、appendLabelChangeで書き込みます。
コマンドルーティング:
/treeは常にツリーセレクターを開きます。/branchはdoubleEscapeAction=treeでない限りユーザーメッセージセレクターを開きます。その場合はツリーセレクターのUXも使用します。
ツリー操作に対する拡張機能とフックのタッチポイント
Section titled “ツリー操作に対する拡張機能とフックのタッチポイント”コマンド時の拡張機能API(ExtensionCommandContext):
branch(entryId)— ブランチセッションファイルを作成navigateTree(targetId, { summarize? })— 現在のツリー/ファイル内を移動
ツリーナビゲーション周辺のイベント:
session_before_treeTreePreparationを受け取ります:targetIdoldLeafIdcommonAncestorIdentriesToSummarizeuserWantsSummary
- ナビゲーションをキャンセルできます
- 組み込みサマライザーの代わりに使用される要約ペイロードを提供できます
- abort
signalを受け取ります(エスケープキャンセルパス)
session_treenewLeafId、oldLeafIdを発行します- 要約が作成された場合は
summaryEntryを含みます fromExtensionは要約の出所を示します
隣接する関連ライフサイクルフック:
session_before_branch/session_branch—/branchフロー用session_before_compact、session.compacting、session_compact— 後でツリーコンテキスト再構築に影響するコンパクションエントリ用
実際の制約とエッジケース
Section titled “実際の制約とエッジケース”branch()はnullをターゲットにできません。最初のエントリ前のルート状態にはresetLeaf()を使用してください。branchWithSummary()はnullターゲットをサポートし、fromId: "root"を記録します。- ツリーセレクターで現在のリーフを選択するとノーオペレーションになります。
- 要約にはアクティブなモデルが必要です。モデルがない場合、要約ナビゲーションは即座に失敗します。
- 要約が中止された場合、ナビゲーションはキャンセルされ、リーフは変更されません。
- インメモリセッションは
createBranchedSessionからブランチファイルパスを返しません。
残存するレガシー互換性
Section titled “残存するレガシー互換性”セッションマイグレーションはロード時に実行されます:
- v1→v2 は
id/parentIdを追加し、コンパクションインデックスアンカーをIDアンカーに変換します - v2→v3 はレガシーの
hookMessageロールをcustomに移行します
現在のランタイム動作はマイグレーション後のバージョン3ツリーセマンティクスです。