Oturum Dosyası Formatı
Oturumlar JSONL (JSON Satır) dosyaları olarak saklanır. Her satır, type alanına sahip bir JSON nesnesidir. Oturum girişleri id/parentId alanları aracılığıyla bir ağaç yapısı oluşturarak yeni dosyalar oluşturmadan yerinde dallanmaya olanak tanır.
Dosya Konumu
~/.pi/agent/sessions/--<path>--/<timestamp>_<uuid>.jsonlBurada <path>, /'nin - ile değiştirildiği çalışma dizini.
Oturumları Silme
Oturumlar, ~/.pi/agent/sessions/ altındaki .jsonl dosyaları silinerek kaldırılabilir.
Pi ayrıca oturumların /resume'den etkileşimli olarak silinmesini de destekler (bir oturum seçin ve Ctrl+D tuşuna basın, ardından onaylayın). Mevcut olduğunda pi, kalıcı olarak silinmeyi önlemek için trash CLI tuşlarını kullanır.
Oturum Sürümü
Oturumların başlığında bir sürüm alanı bulunur:
- Sürüm 1: Doğrusal giriş sırası (eski, yükte otomatik olarak taşınan)
- Sürüm 2:
id/parentIdbağlantılı ağaç yapısı - Sürüm 3:
hookMessagerolücustomolarak yeniden adlandırıldı (uzantı birleştirme)
Mevcut oturumlar yüklendiğinde otomatik olarak geçerli sürüme (v3) taşınır.
Kaynak Dosyaları
GitHub (pi-mono)'deki kaynak:
packages/coding-agent/src/core/session-manager.ts- Oturum giriş türleri ve SessionManagerpackages/coding-agent/src/core/messages.ts- Genişletilmiş mesaj türleri (BashExecutionMessage, CustomMessage, vb.)packages/ai/src/types.ts- Temel mesaj türleri (UserMessage, AssistantMessage, ToolResultMessage)packages/agent/src/types.ts- AgentMessage birleşim türü
Projenizdeki TypeScript tanımları için node_modules/@earendil-works/pi-coding-agent/dist/ ve node_modules/@earendil-works/pi-ai/dist/'yi inceleyin.
Mesaj Türleri
Oturum girişleri AgentMessage nesne içerir. Bu türleri anlamak, oturumları ayrıştırmak ve uzantıları yazmak için çok önemlidir.
İçerik Blokları
Mesajlar, yazılan içerik bloklarından oluşan diziler içerir:
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>;
}Temel Mesaj Türleri (pi-ai'den)
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
usage?: Usage; // Nested LLM work performed by the tool
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;
};
}Dışa aktarılan pi-ai StopReason türü aynı zamanda "pending"'yi de içerir, ancak bu değer akış etkinliklerindeki kısmi mesajlar için ayrılmıştır. Terminal done/error mesajları, pi'nin asistan mesajını sürdürmesinden önce bunu bir tamamlanma nedeni ile değiştirir, bu nedenle "pending" JSONL oturumunda asla görünmemelidir.
Genişletilmiş Mesaj Türleri (pi-coding-agent'tan)
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 Birliği
type AgentMessage =
| UserMessage
| AssistantMessage
| ToolResultMessage
| BashExecutionMessage
| CustomMessage
| BranchSummaryMessage
| CompactionSummaryMessage;Giriş Tabanı
Tüm girişler (SessionHeader hariç) SessionEntryBase'yi genişletir:
interface SessionEntryBase {
type: string;
id: string; // 8-char hex ID
parentId: string | null; // Parent entry ID (null for first entry)
timestamp: string; // ISO timestamp
}Giriş Türleri
Oturum Başlığı
Dosyanın ilk satırı. Yalnızca meta veriler, ağacın parçası değil (id/parentId yok).
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}Ebeveynle yapılan oturumlar için (/fork, /clone veya newSession({ parentSession }) aracılığıyla oluşturulan):
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}OturumMesajGirişi
Görüşmede bir mesaj. message alanı bir AgentMessage içerir.
{"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}}Model Değişikliği Girişi
Kullanıcı oturumun ortasında modelleri değiştirdiğinde ortaya çıkar.
{"type":"model_change","id":"d4e5f6g7","parentId":"c3d4e5f6","timestamp":"2024-12-03T14:05:00.000Z","provider":"openai","modelId":"gpt-4o"}Düşünme Seviyesi Değişimi Girişi
Kullanıcı düşünme/akıl yürütme düzeyini değiştirdiğinde yayılır.
{"type":"thinking_level_change","id":"e5f6g7h8","parentId":"d4e5f6g7","timestamp":"2024-12-03T14:06:00.000Z","thinkingLevel":"high"}Sıkıştırma Girişi
Bağlam sıkıştırıldığında oluşturulur. Önceki mesajların özetini saklar.
{"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000}Daha yeni donanımla oluşturulan sıkıştırmalar, tutulan sıkıştırma sonrası bağlamı firstKeptEntryId yerine doğrudan girişe yerleştirir:
{"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","tokensBefore":50000,"retainedTail":[{"role":"user","content":"latest request"},{"role":"assistant","content":[{"type":"text","text":"latest reply"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}]}İsteğe bağlı alanlar:
usage: Özetin oluşturulmasından LLM kullanımı; oturum belirtecine ve maliyet toplamlarına dahil edilirretainedTail: Sıkıştırıldıktan sonra saklanan materyalizeAgentMessage[]. Bu yalnızca eski oturumlarla geriye dönük uyumluluk için isteğe bağlıdır. Daha yeni donanımla oluşturulan sıkıştırmalar bunu içerir, böylece sıkıştırma girişinden önce eski girişleri yürümeden bu kontrol noktasından bağlamı yeniden oluşturabiliriz.details: Uygulamaya özel veriler (ör. varsayılan için{ readFiles: string[], modifiedFiles: string[] }veya uzantılar için özel veriler)fromHook:truebir uzantı tarafından oluşturulmuşsa,false/undefinedpi tarafından oluşturulmuşsa (eski alan adı)firstKeptEntryId: eski giriş formatıyla uyumluluk için.
ŞubeÖzetGiriş
/tree yoluyla dallar değiştirilirken, sol dalın ortak ataya kadar LLM tarafından oluşturulan bir özetiyle oluşturulur. Terk edilmiş yoldan bağlamı yakalar.
{"type":"branch_summary","id":"g7h8i9j0","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:15:00.000Z","fromId":"f6g7h8i9","summary":"Branch explored approach A..."}İsteğe bağlı alanlar:
usage: Özetin oluşturulmasından LLM kullanımı; oturum belirtecine ve maliyet toplamlarına dahil edilirdetails: Varsayılan için dosya izleme verileri ({ readFiles: string[], modifiedFiles: string[] }) veya uzantılar için özel verilerfromHook:truebir uzantı tarafından oluşturulmuşsa,false/undefinedpi tarafından oluşturulmuşsa (eski alan adı)
Özel Giriş
Uzantı durumunun kalıcılığı. LLM bağlamına katılmaz.
{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-extension","data":{"count":42}}Yeniden yükleme sırasında uzantınızın girişlerini tanımlamak için customType tuşunu kullanın. İnteraktif mod, özel girişleri pi.registerEntryRenderer(customType, renderer) aracılığıyla işleyebilir ancak yine de Yüksek Lisans bağlamına katılmazlar.
ÖzelMesajGirişi
LLM bağlamına katılan, uzantı enjekte edilen mesajlar.
{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-extension","content":"Injected context...","display":true}Alanlar:
content: Dize veya(TextContent | ImageContent)[](KullanıcıMesajı ile aynı)display:true= TUI'de farklı stilde göster,false= gizlidetails: İsteğe bağlı uzantıya özgü meta veriler (LLM'ye gönderilmez)
Etiket Girişi
Bir girişteki kullanıcı tanımlı yer imi/işaretçi.
{"type":"label","id":"j0k1l2m3","parentId":"i9j0k1l2","timestamp":"2024-12-03T14:30:00.000Z","targetId":"a1b2c3d4","label":"checkpoint-1"}Bir etiketi temizlemek için label'ı undefined olarak ayarlayın.
Oturum Bilgisi Girişi
Oturum meta verileri (ör. kullanıcı tanımlı görünen ad). Uzantılarda /name, --name / -n veya pi.setSessionName() aracılığıyla ayarlayın.
{"type":"session_info","id":"k1l2m3n4","parentId":"j0k1l2m3","timestamp":"2024-12-03T14:35:00.000Z","name":"Refactor auth module"}Oturum adı, ayarlandığında ilk mesaj yerine oturum seçicide (/resume) görüntülenir.
Ağaç Yapısı
Girişler bir ağaç oluşturur:
- İlk girişte
parentId: nullvar - Sonraki her giriş
parentIdaracılığıyla ebeveynine işaret eder - Dallanma daha önceki bir girdiden yeni alt öğeler yaratır
- "Yaprak" ağaçtaki mevcut konumdur
[user msg] ─── [assistant] ─── [user msg] ─── [assistant] ─┬─ [user msg] ← current leaf
│
└─ [branch_summary] ─── [user msg] ← alternate branchBağlam Oluşturma
buildContextEntries() mevcut yapraktan köke doğru yürür ve sıkıştırmayı dikkate alarak aktif giriş listesini üretir:
- Yoldaki tüm girişleri toplar
- Yol üzerinde bir
CompactionEntryvarsa:- Önce sıkıştırma girişini içerir
retainedTailmevcutsa, bağımsız bir kontrol noktası görevi görür ve sıkıştırma sonrasındaki girişler dahil edilir- Aksi takdirde
firstKeptEntryId'den sıkıştırmaya kadar olan girişler dahil edilir - Daha sonra sıkıştırmadan sonraki girişler dahil edilir
- Etkileşimli modun bunları oluşturabilmesi için seçilen aralıktaki mesaj dışı girişleri korur
buildSessionContext() Yüksek Lisans için mesaj listesini oluşturmak üzere bu giriş listesini temel alır:
- Geçerli modeli ve düşünme düzeyi ayarlarını tam yoldan çıkarır
- Seçilen girişleri mesajlara dönüştürür:
message-> saklananAgentMessagecompaction->compactionSummaryartıretainedTailmevcut olduğundabranch_summary->branchSummarycustom_message->CustomMessagecustom-> içerik mesajı yok
Bu, daha yeni sıkıştırmaların müstakil kontrol noktaları gibi davranmasını sağlar. retainedTail isteğe bağlıdır, bu nedenle yalnızca firstKeptEntryId depolayan eski oturumlar doğru şekilde yüklenmeye devam eder.
Ayrıştırma Örneği
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;
}
}Oturum Yöneticisi API
Oturumlarla programlı olarak çalışmanın temel yöntemleri.
Statik Oluşturma Yöntemleri
SessionManager.create(cwd, sessionDir?)- Yeni oturumSessionManager.open(path, sessionDir?)- Mevcut oturum dosyasını açSessionManager.continueRecent(cwd, sessionDir?)- En yeniye devam et veya yeni oluşturSessionManager.inMemory(cwd?)- Dosya kalıcılığı yokSessionManager.forkFrom(sourcePath, targetCwd, sessionDir?)- Başka bir projeden oturumu çatallayın
Statik Listeleme Yöntemleri
SessionManager.list(cwd, sessionDir?, onProgress?)- Bir dizindeki oturumları listeleyinSessionManager.listAll(onProgress?)- Tüm projelerdeki tüm oturumları listeleyin
Örnek Yöntemleri - Oturum Yönetimi
newSession(options?)- Yeni bir oturum başlatın (seçenekler:{ parentSession?: string })setSessionFile(path)- Farklı bir oturum dosyasına geçincreateBranchedSession(leafId)- Şubeyi yeni oturum dosyasına çıkart
Örnek Yöntemleri - Ekleme (tüm dönüş giriş kimlikleri)
appendMessage(message)- Mesaj ekleappendThinkingLevelChange(level)- Düşünce değişikliğini kaydedinappendModelChange(provider, modelId)- Model değişikliğini kaydedinappendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)- Sıkıştırma ekleyinappendCustomEntry(customType, data?)- Uzantı durumu (bağlamda değil)appendSessionInfo(name)- Oturumun görünen adını ayarlayınappendCustomMessageEntry(customType, content, display, details?)- Uzantı mesajı (bağlamda)appendLabelChange(targetId, label)- Etiketi ayarla/temizle
Örnek Yöntemleri - Ağaçta Gezinme
getLeafId()- Mevcut konumgetLeafEntry()- Mevcut yaprak girişini alıngetEntry(id)- Kimliğe göre giriş alıngetBranch(fromId?)- Girişten köke doğru yürüyüngetTree()- Tam ağaç yapısını elde edingetChildren(parentId)- Doğrudan çocukları alıngetLabel(id)- Giriş için etiketi alınbranch(entryId)- Yaprağı önceki girişe taşıresetLeaf()- Yaprağı null değerine sıfırla (herhangi bir girişten önce)branchWithSummary(entryId, summary, details?, fromHook?)- Bağlam özetini içeren dal
Örnek Yöntemleri - Bağlam ve Bilgi
buildContextEntries()- Sıkıştırma uygulanmış aktif şube girişlerini alınbuildSessionContext()- Yüksek Lisans için mesajları, düşünce seviyesini ve modeli alıngetEntries()- Tüm girişler (başlık hariç)getHeader()- Oturum başlığı meta verilerigetSessionName()- En son session_info girişinden görünen adı alıngetCwd()- Çalışma dizinigetSessionDir()- Oturum depolama dizinigetSessionId()- Oturum UUID'sigetSessionFile()- Oturum dosyası yolu (bellek içi için tanımsız)isPersisted()- Oturumun diske kaydedilip kaydedilmeyeceği