Konfiguration, Anpassung, Plattform-Einrichtung und API-Referenzen für Pi.

Sitzungsdateiformat

Sitzungen werden als JSONL (JSON Zeilen) Dateien gespeichert. Jede Zeile ist ein JSON-Objekt mit einem type-Feld. Sitzungseinträge bilden über id/parentId-Felder eine Baumstruktur und ermöglichen eine direkte Verzweigung, ohne dass neue Dateien erstellt werden müssen.

Dateispeicherort

~/.pi/agent/sessions/--<path>--/<timestamp>_<uuid>.jsonl

Dabei ist <path> das Arbeitsverzeichnis, wobei / durch - ersetzt wird.

Sitzungen löschen

Sitzungen können entfernt werden, indem ihre .jsonl-Dateien unter ~/.pi/agent/sessions/ gelöscht werden.

Pi unterstützt auch das interaktive Löschen von Sitzungen aus /resume (wählen Sie eine Sitzung aus und drücken Sie Ctrl+D, dann bestätigen). Wenn verfügbar, verwendet Pi trash CLI, um ein dauerhaftes Löschen zu vermeiden.

Sitzungsversion

Sitzungen haben ein Versionsfeld in der Kopfzeile:

  • Version 1: Lineare Eingabesequenz (alt, beim Laden automatisch migriert)
  • Version 2: Baumstruktur mit id/parentId-Verknüpfung
  • Version 3: Rolle hookMessage in custom umbenannt (Vereinheitlichung der Erweiterungen)

Bestehende Sitzungen werden beim Laden automatisch auf die aktuelle Version (v3) migriert.

Quelldateien

Quelle am GitHub (pi-mono):

Überprüfen Sie für TypeScript-Definitionen in Ihrem Projekt node_modules/@earendil-works/pi-coding-agent/dist/ und node_modules/@earendil-works/pi-ai/dist/.

Nachrichtentypen

Sitzungseinträge enthalten AgentMessage Objekte. Das Verständnis dieser Typen ist für das Parsen von Sitzungen und das Schreiben von Erweiterungen unerlässlich.

Inhaltsblöcke

Nachrichten enthalten Arrays typisierter Inhaltsblöcke:

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>;
}

Basisnachrichtentypen (von 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
  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;
  };
}

Der exportierte pi-ai-Typ StopReason enthält auch "pending", dieser Wert ist jedoch für Teilnachrichten in Streaming-Ereignissen reserviert. Terminal-Nachrichten done/error ersetzen es durch einen Abschlussgrund, bevor pi die Assistentennachricht beibehält, sodass "pending" niemals in Sitzung JSONL erscheinen sollte.

Erweiterte Nachrichtentypen (von 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 Union

type AgentMessage =
  | UserMessage
  | AssistantMessage
  | ToolResultMessage
  | BashExecutionMessage
  | CustomMessage
  | BranchSummaryMessage
  | CompactionSummaryMessage;

Einstiegsbasis

Alle Einträge (außer SessionHeader) erweitern 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
}

Eintragstypen

SessionHeader

Erste Zeile der Datei. Nur Metadaten, kein Teil des Baums (kein id/parentId).

{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}

Für Sitzungen mit einem Elternteil (erstellt über /fork, /clone oder 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

Eine Nachricht im Gespräch. Das Feld message enthält eine 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

Wird ausgegeben, wenn der Benutzer mitten in der Sitzung das Modell wechselt.

{"type":"model_change","id":"d4e5f6g7","parentId":"c3d4e5f6","timestamp":"2024-12-03T14:05:00.000Z","provider":"openai","modelId":"gpt-4o"}

ThinkingLevelChangeEntry

Wird ausgegeben, wenn der Benutzer die Denk-/Argumentationsebene ändert.

{"type":"thinking_level_change","id":"e5f6g7h8","parentId":"d4e5f6g7","timestamp":"2024-12-03T14:06:00.000Z","thinkingLevel":"high"}

Verdichtungseintrag

Wird erstellt, wenn der Kontext komprimiert wird. Speichert eine Zusammenfassung früherer Nachrichten.

{"type":"compaction","id":"f6g7h8i9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:10:00.000Z","summary":"User discussed X, Y, Z...","firstKeptEntryId":"c3d4e5f6","tokensBefore":50000}

Neuere, durch Kabelbäume generierte Verdichtungen betten den beibehaltenen Post-Verdichtungskontext direkt in den Eintrag ein, statt firstKeptEntryId:

{"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"}]}

Optionale Felder:

  • usage: LLM-Nutzung durch Generierung der Zusammenfassung; im Sitzungs-Token und in den Gesamtkosten enthalten
  • retainedTail: Materialisiert AgentMessage[] bleibt nach der Verdichtung erhalten. Dies ist nur aus Gründen der Abwärtskompatibilität mit älteren Sitzungen optional. Neuere, durch Kabelbäume generierte Komprimierungen enthalten es, sodass wir den Kontext von diesem Prüfpunkt aus neu erstellen können, ohne ältere Einträge vor dem Komprimierungseintrag zu durchlaufen.
  • details: Implementierungsspezifische Daten (z. B. { readFiles: string[], modifiedFiles: string[] } für Standard oder benutzerdefinierte Daten für Erweiterungen)
  • fromHook: true, wenn durch eine Erweiterung generiert, false/undefined, wenn Pi-generiert (Legacy-Feldname)
  • firstKeptEntryId: für Kompatibilität mit dem alten Eingabeformat.

BranchSummaryEntry

Wird beim Zweigwechsel über /tree mit einer LLM-generierten Zusammenfassung des linken Zweigs bis zum gemeinsamen Vorfahren erstellt. Erfasst den Kontext des verlassenen Pfads.

{"type":"branch_summary","id":"g7h8i9j0","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:15:00.000Z","fromId":"f6g7h8i9","summary":"Branch explored approach A..."}

Optionale Felder:

  • usage: LLM-Nutzung durch Generierung der Zusammenfassung; im Sitzungs-Token und in den Gesamtkosten enthalten
  • details: Dateiverfolgungsdaten ({ readFiles: string[], modifiedFiles: string[] }) für Standard oder benutzerdefinierte Daten für Erweiterungen
  • fromHook: true, wenn durch eine Erweiterung generiert, false/undefined, wenn Pi-generiert (Legacy-Feldname)

Benutzerdefinierter Eintrag

Persistenz des Erweiterungsstatus. Nimmt NICHT am LLM-Kontext teil.

{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-extension","data":{"count":42}}

Verwenden Sie customType, um die Einträge Ihrer Erweiterung beim Neuladen zu identifizieren. Der interaktive Modus kann benutzerdefinierte Einträge über pi.registerEntryRenderer(customType, renderer) rendern, sie nehmen jedoch immer noch nicht am LLM-Kontext teil.

CustomMessageEntry

Durch Erweiterungen eingefügte Nachrichten, die am LLM-Kontext beteiligt sind.

{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-extension","content":"Injected context...","display":true}

Felder:

  • content: String oder (TextContent | ImageContent)[] (wie UserMessage)
  • display: true = in TUI mit eindeutigem Stil anzeigen, false = ausgeblendet
  • details: Optionale erweiterungsspezifische Metadaten (nicht an LLM gesendet)

Etiketteneintrag

Benutzerdefiniertes Lesezeichen/Markierung für einen Eintrag.

{"type":"label","id":"j0k1l2m3","parentId":"i9j0k1l2","timestamp":"2024-12-03T14:30:00.000Z","targetId":"a1b2c3d4","label":"checkpoint-1"}

Setzen Sie label auf undefined, um ein Etikett zu löschen.

SessionInfoEntry

Sitzungsmetadaten (z. B. benutzerdefinierter Anzeigename). Wird über /name, --name / -n oder pi.setSessionName() in Erweiterungen eingestellt.

{"type":"session_info","id":"k1l2m3n4","parentId":"j0k1l2m3","timestamp":"2024-12-03T14:35:00.000Z","name":"Refactor auth module"}

Der Sitzungsname wird in der Sitzungsauswahl (/resume) anstelle der ersten Nachricht angezeigt, wenn diese festgelegt ist.

Baumstruktur

Einträge bilden einen Baum:

  • Erster Eintrag hat parentId: null
  • Jeder nachfolgende Eintrag verweist über parentId auf seinen übergeordneten Eintrag.
  • Durch die Verzweigung werden neue untergeordnete Elemente aus einem früheren Eintrag erstellt
  • Das „Blatt“ ist die aktuelle Position im Baum
[user msg] ─── [assistant] ─── [user msg] ─── [assistant] ─┬─ [user msg] ← current leaf
                                                            │
                                                            └─ [branch_summary] ─── [user msg] ← alternate branch

Kontextbildung

buildContextEntries() geht vom aktuellen Blatt zur Wurzel und erstellt die aktive Eintragsliste unter Berücksichtigung der Komprimierung:

  1. Sammelt alle Einträge auf dem Pfad
  2. Wenn sich eine CompactionEntry auf dem Pfad befindet:
    • Beinhaltet zuerst den Komprimierungseintrag
    • Wenn retainedTail vorhanden ist, fungiert es als eigenständiger Prüfpunkt und Einträge nach der Komprimierung werden einbezogen
    • Ansonsten sind Einträge von firstKeptEntryId bis zur Verdichtung enthalten
    • Dann werden Einträge nach der Komprimierung einbezogen
  3. Behält Nicht-Nachrichteneinträge im ausgewählten Bereich bei, sodass sie im interaktiven Modus gerendert werden können

buildSessionContext() baut auf dieser Eintragsliste auf, um die Nachrichtenliste für das LLM zu erstellen:

  1. Extrahiert aktuelle Modell- und Denkebeneneinstellungen aus dem vollständigen Pfad
  2. Konvertiert ausgewählte Einträge in Nachrichten:
    • message -> gespeichert AgentMessage
    • compaction -> compactionSummary plus retainedTail, falls vorhanden
    • branch_summary -> branchSummary
    • custom_message -> CustomMessage
    • custom -> keine Kontextmeldung

Dadurch wirken neuere Verdichtungen wie eigenständige Kontrollpunkte. retainedTail ist nur optional, damit ältere Sitzungen, die nur firstKeptEntryId speichern, weiterhin korrekt geladen werden.

Parsing-Beispiel

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

Schlüsselmethoden für die programmgesteuerte Arbeit mit Sitzungen.

Statische Erstellungsmethoden

  • SessionManager.create(cwd, sessionDir?) – Neue Sitzung
  • SessionManager.open(path, sessionDir?) – Vorhandene Sitzungsdatei öffnen
  • SessionManager.continueRecent(cwd, sessionDir?) – Mit der neuesten Version fortfahren oder eine neue erstellen
  • SessionManager.inMemory(cwd?) – Keine Dateipersistenz
  • SessionManager.forkFrom(sourcePath, targetCwd, sessionDir?) – Fork-Sitzung von einem anderen Projekt

Statische Auflistungsmethoden

  • SessionManager.list(cwd, sessionDir?, onProgress?) – Sitzungen für ein Verzeichnis auflisten
  • SessionManager.listAll(onProgress?) – Alle Sitzungen in allen Projekten auflisten

Instanzmethoden – Sitzungsverwaltung

  • newSession(options?) – Eine neue Sitzung starten (Optionen: { parentSession?: string })
  • setSessionFile(path) – Wechseln Sie zu einer anderen Sitzungsdatei
  • createBranchedSession(leafId) – Zweig in neue Sitzungsdatei extrahieren

Instanzmethoden – Anhängen (alle Rückgabeeintrags-ID)

  • appendMessage(message) – Nachricht hinzufügen
  • appendThinkingLevelChange(level) – Denkänderungen aufzeichnen
  • appendModelChange(provider, modelId) – Modellwechsel aufzeichnen
  • appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?) – Komprimierung hinzufügen
  • appendCustomEntry(customType, data?) – Erweiterungsstatus (nicht im Kontext)
  • appendSessionInfo(name) – Sitzungsanzeigenamen festlegen
  • appendCustomMessageEntry(customType, content, display, details?) – Erweiterungsnachricht (im Kontext)
  • appendLabelChange(targetId, label) – Beschriftung festlegen/löschen

Instanzmethoden – Baumnavigation

  • getLeafId() – Aktuelle Position
  • getLeafEntry() – Aktuellen Blatteintrag abrufen
  • getEntry(id) – Erhalten Sie Zutritt per ID
  • getBranch(fromId?) – Gehen Sie vom Eingang zur Wurzel
  • getTree() – Vollständige Baumstruktur erhalten
  • getChildren(parentId) – Holen Sie sich direkte Kinder
  • getLabel(id) – Label für den Eintrag abrufen
  • branch(entryId) – Blatt zum früheren Eintrag verschieben
  • resetLeaf() – Blatt auf Null zurücksetzen (vor irgendwelchen Einträgen)
  • branchWithSummary(entryId, summary, details?, fromHook?) – Zweig mit Kontextzusammenfassung

Instanzmethoden – Kontext und Informationen

  • buildContextEntries() – Aktive Zweigeinträge mit angewendeter Komprimierung abrufen
  • buildSessionContext() – Erhalten Sie Nachrichten, Denkebene und Modell für LLM
  • getEntries() – Alle Einträge (außer Header)
  • getHeader() – Sitzungsheader-Metadaten
  • getSessionName() – Anzeigenamen aus dem letzten session_info-Eintrag abrufen
  • getCwd() – Arbeitsverzeichnis
  • getSessionDir() – Sitzungsspeicherverzeichnis
  • getSessionId() – Sitzungs-UUID
  • getSessionFile() – Sitzungsdateipfad (undefiniert für In-Memory)
  • isPersisted() – Ob die Sitzung auf der Festplatte gespeichert wird