セッションファイル形式
セッションは JSONL(JSON Lines)ファイルとして保存されます。各行は type フィールドを持つ JSON オブジェクトです。セッションエントリは id/parentId フィールドでツリー構造を形成し、新しいファイルを作成せずにその場でブランチできます。
ファイルの場所
Section titled “ファイルの場所”~/.pi/agent/sessions/--<path>--/<timestamp>_<uuid>.jsonl<path> は作業ディレクトリで、/ が - に置き換えられます。
セッションの削除
Section titled “セッションの削除”~/.pi/agent/sessions/ 配下の .jsonl ファイルを削除することで、セッションを削除できます。
Pi は /resume からの対話的な削除もサポートしています(セッションを選択して Ctrl+D を押し、確認します)。利用可能な場合、pi は trash CLI を使用して完全削除を避けます。
セッションバージョン
Section titled “セッションバージョン”セッションのヘッダーにはバージョンフィールドがあります:
- Version 1:線形エントリシーケンス(レガシー、読み込み時に自動移行)
- Version 2:
id/parentIdリンクによるツリー構造 - Version 3:
hookMessageロールをcustomに改名(拡張の統一)
既存のセッションは読み込み時に現在のバージョン(v3)へ自動移行されます。
ソースファイル
Section titled “ソースファイル”GitHub 上のソース(pi-mono):
packages/coding-agent/src/core/session-manager.ts- セッションエントリ型と SessionManagerpackages/coding-agent/src/core/messages.ts- 拡張メッセージ型(BashExecutionMessage、CustomMessage など)packages/ai/src/types.ts- 基本メッセージ型(UserMessage、AssistantMessage、ToolResultMessage)packages/agent/src/types.ts- AgentMessage ユニオン型
プロジェクトで TypeScript 定義を確認するには、node_modules/@earendil-works/pi-coding-agent/dist/ と node_modules/@earendil-works/pi-ai/dist/ を参照してください。
メッセージ型
Section titled “メッセージ型”セッションエントリには AgentMessage オブジェクトが含まれます。これらの型を理解することは、セッションの解析と拡張の作成に不可欠です。
コンテンツブロック
Section titled “コンテンツブロック”メッセージには型付きコンテンツブロックの配列が含まれます:
interface TextContent { type: "text"; text: string;}
interface ImageContent { type: "image"; data: string; // base64 encoded mimeType: string; // e.g., "image/jpeg", "image/png"}
interface ThinkingContent { type: "thinking"; thinking: string;}
interface ToolCall { type: "toolCall"; id: string; name: string; arguments: Record<string, any>;}基本メッセージ型(pi-ai より)
Section titled “基本メッセージ型(pi-ai より)”interface UserMessage { role: "user"; content: string | (TextContent | ImageContent)[]; timestamp: number; // Unix ms}
interface AssistantMessage { role: "assistant"; content: (TextContent | ThinkingContent | ToolCall)[]; api: string; provider: string; model: string; usage: Usage; stopReason: "stop" | "length" | "toolUse" | "error" | "aborted"; errorMessage?: string; timestamp: number;}
interface ToolResultMessage { role: "toolResult"; toolCallId: string; toolName: string; content: (TextContent | ImageContent)[]; details?: any; // Tool-specific metadata isError: boolean; timestamp: number;}
interface Usage { input: number; output: number; cacheRead: number; cacheWrite: number; totalTokens: number; cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number; };}拡張メッセージ型(pi-coding-agent より)
Section titled “拡張メッセージ型(pi-coding-agent より)”interface BashExecutionMessage { role: "bashExecution"; command: string; output: string; exitCode: number | undefined; cancelled: boolean; truncated: boolean; fullOutputPath?: string; excludeFromContext?: boolean; // true for !! prefix commands timestamp: number;}
interface CustomMessage { role: "custom"; customType: string; // Extension identifier content: string | (TextContent | ImageContent)[]; display: boolean; // Show in TUI details?: any; // Extension-specific metadata timestamp: number;}
interface BranchSummaryMessage { role: "branchSummary"; summary: string; fromId: string; // Entry we branched from timestamp: number;}
interface CompactionSummaryMessage { role: "compactionSummary"; summary: string; tokensBefore: number; timestamp: number;}AgentMessage ユニオン
Section titled “AgentMessage ユニオン”type AgentMessage = | UserMessage | AssistantMessage | ToolResultMessage | BashExecutionMessage | CustomMessage | BranchSummaryMessage | CompactionSummaryMessage;エントリ基底
Section titled “エントリ基底”すべてのエントリ(SessionHeader を除く)は SessionEntryBase を拡張します:
interface SessionEntryBase { type: string; id: string; // 8-char hex ID parentId: string | null; // Parent entry ID (null for first entry) timestamp: string; // ISO timestamp}SessionHeader
Section titled “SessionHeader”ファイルの最初の行。メタデータのみで、ツリーの一部ではありません(id/parentId なし)。
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}親セッションを持つセッション(/fork、/clone、または newSession({ parentSession }) で作成):
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}SessionMessageEntry
Section titled “SessionMessageEntry”会話内のメッセージ。message フィールドには AgentMessage が含まれます。
{"type":"message","id":"a1b2c3d4","parentId":"prev1234","timestamp":"2024-12-03T14:00:01.000Z","message":{"role":"user","content":"Hello"}}{"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}{"type":"message","id":"c3d4e5f6","parentId":"b2c3d4e5","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false}}ModelChangeEntry
Section titled “ModelChangeEntry”ユーザーがセッション中にモデルを切り替えたときに発行されます。
{"type":"model_change","id":"d4e5f6g7","parentId":"c3d4e5f6","timestamp":"2024-12-03T14:05:00.000Z","provider":"openai","modelId":"gpt-4o"}ThinkingLevelChangeEntry
Section titled “ThinkingLevelChangeEntry”ユーザーが思考/推論レベルを変更したときに発行されます。
{"type":"thinking_level_change","id":"e5f6g7h8","parentId":"d4e5f6g7","timestamp":"2024-12-03T14:06:00.000Z","thinkingLevel":"high"}CompactionEntry
Section titled “CompactionEntry”コンテキストがコンパクションされたときに作成されます。以前のメッセージの要約を保存します。
{"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000}オプションフィールド:
details:実装固有のデータ(例:デフォルトの{ readFiles: string[], modifiedFiles: string[] }、または拡張のカスタムデータ)fromHook:拡張で生成された場合はtrue、pi で生成された場合はfalse/undefined(レガシーフィールド名)
BranchSummaryEntry
Section titled “BranchSummaryEntry”/tree でブランチを切り替えるときに作成されます。共通祖先までの左ブランチの LLM 生成要約を含みます。放棄されたパスのコンテキストをキャプチャします。
{"type":"branch_summary","id":"g7h8i9j0","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:15:00.000Z","fromId":"f6g7h8i9","summary":"Branch explored approach A..."}オプションフィールド:
details:ファイル追跡データ(デフォルトの{ readFiles: string[], modifiedFiles: string[] }、または拡張のカスタムデータ)fromHook:拡張で生成された場合はtrue、pi で生成された場合はfalse/undefined(レガシーフィールド名)
CustomEntry
Section titled “CustomEntry”拡張状態の永続化。LLM コンテキストには参加しません。
{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-extension","data":{"count":42}}再読み込み時に拡張のエントリを識別するには customType を使用します。
CustomMessageEntry
Section titled “CustomMessageEntry”LLM コンテキストに参加する拡張注入メッセージ。
{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-extension","content":"Injected context...","display":true}フィールド:
content:文字列または(TextContent | ImageContent)[](UserMessage と同じ)display:true= TUI に独自スタイルで表示、false= 非表示details:オプションの拡張固有メタデータ(LLM には送信されない)
LabelEntry
Section titled “LabelEntry”エントリ上のユーザー定義ブックマーク/マーカー。
{"type":"label","id":"j0k1l2m3","parentId":"i9j0k1l2","timestamp":"2024-12-03T14:30:00.000Z","targetId":"a1b2c3d4","label":"checkpoint-1"}ラベルをクリアするには label を undefined に設定します。
SessionInfoEntry
Section titled “SessionInfoEntry”セッションメタデータ(例:ユーザー定義の表示名)。/name、--name / -n、または拡張内の pi.setSessionName() で設定します。
{"type":"session_info","id":"k1l2m3n4","parentId":"j0k1l2m3","timestamp":"2024-12-03T14:35:00.000Z","name":"Refactor auth module"}設定すると、セッション名は最初のメッセージの代わりにセッションセレクター(/resume)に表示されます。
エントリはツリーを形成します:
- 最初のエントリは
parentId: null - 各後続エントリは
parentIdで親を指す - ブランチは以前のエントリから新しい子を作成する
- 「リーフ」はツリー内の現在位置
[user msg] ─── [assistant] ─── [user msg] ─── [assistant] ─┬─ [user msg] ← current leaf │ └─ [branch_summary] ─── [user msg] ← alternate branchコンテキスト構築
Section titled “コンテキスト構築”buildSessionContext() は現在のリーフからルートまで走査し、LLM 用のメッセージリストを生成します:
- パス上のすべてのエントリを収集
- 現在のモデルと思考レベル設定を抽出
- パス上に
CompactionEntryがある場合:- まず要約を出力
- 次に
firstKeptEntryIdからコンパクションまでのメッセージ - その後コンパクション以降のメッセージ
BranchSummaryEntryとCustomMessageEntryを適切なメッセージ形式に変換
import { readFileSync } from "fs";
const lines = readFileSync("session.jsonl", "utf8").trim().split("\n");
for (const line of lines) { const entry = JSON.parse(line);
switch (entry.type) { case "session": console.log(`Session v${entry.version ?? 1}: ${entry.id}`); break; case "message": console.log(`[${entry.id}] ${entry.message.role}: ${JSON.stringify(entry.message.content)}`); break; case "compaction": console.log(`[${entry.id}] Compaction: ${entry.tokensBefore} tokens summarized`); break; case "branch_summary": console.log(`[${entry.id}] Branch from ${entry.fromId}`); break; case "custom": console.log(`[${entry.id}] Custom (${entry.customType}): ${JSON.stringify(entry.data)}`); break; case "custom_message": console.log(`[${entry.id}] Extension message (${entry.customType}): ${entry.content}`); break; case "label": console.log(`[${entry.id}] Label "${entry.label}" on ${entry.targetId}`); break; case "model_change": console.log(`[${entry.id}] Model: ${entry.provider}/${entry.modelId}`); break; case "thinking_level_change": console.log(`[${entry.id}] Thinking: ${entry.thinkingLevel}`); break; }}SessionManager API
Section titled “SessionManager API”プログラムでセッションを操作するための主要メソッド。
静的作成メソッド
Section titled “静的作成メソッド”SessionManager.create(cwd, sessionDir?)- 新規セッションSessionManager.open(path, sessionDir?)- 既存セッションファイルを開くSessionManager.continueRecent(cwd, sessionDir?)- 最新セッションを続行または新規作成SessionManager.inMemory(cwd?)- ファイル永続化なしSessionManager.forkFrom(sourcePath, targetCwd, sessionDir?)- 別プロジェクトからセッションをフォーク
静的リストメソッド
Section titled “静的リストメソッド”SessionManager.list(cwd, sessionDir?, onProgress?)- ディレクトリのセッションを一覧SessionManager.listAll(onProgress?)- すべてのプロジェクトのセッションを一覧
インスタンスメソッド - セッション管理
Section titled “インスタンスメソッド - セッション管理”newSession(options?)- 新規セッションを開始(オプション:{ parentSession?: string })setSessionFile(path)- 別のセッションファイルに切り替えcreateBranchedSession(leafId)- ブランチを新しいセッションファイルに抽出
インスタンスメソッド - 追加(すべてエントリ ID を返す)
Section titled “インスタンスメソッド - 追加(すべてエントリ ID を返す)”appendMessage(message)- メッセージを追加appendThinkingLevelChange(level)- 思考レベル変更を記録appendModelChange(provider, modelId)- モデル変更を記録appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)- コンパクションを追加appendCustomEntry(customType, data?)- 拡張状態(コンテキスト外)appendSessionInfo(name)- セッション表示名を設定appendCustomMessageEntry(customType, content, display, details?)- 拡張メッセージ(コンテキスト内)appendLabelChange(targetId, label)- ラベルを設定/クリア
インスタンスメソッド - ツリーナビゲーション
Section titled “インスタンスメソッド - ツリーナビゲーション”getLeafId()- 現在位置getLeafEntry()- 現在のリーフエントリを取得getEntry(id)- ID でエントリを取得getBranch(fromId?)- エントリからルートまで走査getTree()- 完全なツリー構造を取得getChildren(parentId)- 直接の子を取得getLabel(id)- エントリのラベルを取得branch(entryId)- リーフを以前のエントリに移動resetLeaf()- リーフを null にリセット(すべてのエントリより前)branchWithSummary(entryId, summary, details?, fromHook?)- コンテキスト要約付きブランチ
インスタンスメソッド - コンテキストと情報
Section titled “インスタンスメソッド - コンテキストと情報”buildSessionContext()- LLM 用のメッセージ、thinkingLevel、model を取得getEntries()- すべてのエントリ(ヘッダーを除く)getHeader()- セッションヘッダーメタデータgetSessionName()- 最新の session_info エントリから表示名を取得getCwd()- 作業ディレクトリgetSessionDir()- セッション保存ディレクトリgetSessionId()- セッション UUIDgetSessionFile()- セッションファイルパス(インメモリの場合は undefined)isPersisted()- セッションがディスクに保存されているか