Architektur im Überblick
Quelldateien
Diese Seite wurde aus den folgenden Quelldateien erstellt:
- package.json
- packages/agent/src/agent.ts
- packages/ai/src/index.ts
- packages/coding-agent/src/core/index.ts
- packages/tui/src/tui.ts
- packages/web-ui/src/index.ts
- packages/coding-agent/src/core/timings.ts
- packages/coding-agent/src/core/model-resolver.ts
- packages/coding-agent/src/core/package-manager.ts
- packages/coding-agent/src/core/session-manager.ts
- packages/mom/src/main.ts
- packages/tui/src/index.ts
- packages/pods/src/index.ts
- packages/agent/src/index.ts
- packages/coding-agent/src/main.ts
- packages/coding-agent/src/index.ts
- packages/mom/src/tools/index.ts
- packages/web-ui/src/tools/index.ts
- packages/web-ui/example/src/main.ts
- packages/coding-agent/src/modes/index.ts
Das Projekt pi-mono ist eine modulare Monorepo-Architektur für KI-gestützte Coding-Assistenten. Es bietet eine flexible Agenten-Abstraktionsschicht, die verschiedene LLM-Provider integriert und über multiple Benutzerschnittstellen (Terminal, Web, Slack) zugänglich gemacht wird. Die Architektur folgt einem Plugin-basierten Ansatz mit strikter Trennung zwischen Kernlogik, Erweiterungen und Präsentationsschichten.
Systemarchitektur
Die Gesamtarchitektur von pi-mono basiert auf einer dreischichtigen Struktur: Core Layer (Agenten-Logik, Session-Management), Extension Layer (Plugins, Skills, Themes) und Presentation Layer (TUI, Web-UI, Slack-Bot). Diese Trennung ermöglicht maximale Wiederverwendbarkeit und unabhängige Entwicklung der einzelnen Komponenten.
正在加载图表渲染器...
Architektur-Highlights:
- Agent Package (packages/agent/src/agent.ts:1-183) bildet die zentrale Abstraktion für alle LLM-Interaktionen mit konfigurierbarem Streaming und Tool-Execution
- Coding Agent Core (packages/coding-agent/src/core/index.ts:1-60) exportiert
AgentSession,EventBusund das Extensions-System als fundamentale Bausteine - Session Manager (packages/coding-agent/src/core/session-manager.ts:503-542) verwaltet Persistierung und Wiederherstellung von Konversationen mit Aktivitäts-Tracking
- Model Resolver (packages/coding-agent/src/core/model-resolver.ts:305-367) implementiert Fuzzy-Matching für Provider- und Modellauswahl über CLI-Argumente
Core Agent Architecture
Das Agent Package stellt die fundamentale Abstraktionsschicht für alle LLM-Interaktionen dar. Die Agent-Klasse kapselt Zustandsverwaltung, Message-Konvertierung und Tool-Execution in einer einheitlichen API.
Zustandsverwaltung und Konfiguration
Der Agent verwaltet einen internen AgentState mit folgenden Kernkomponenten:
typescript1// Aus packages/agent/src/agent.ts:117-127 2private _state: AgentState = { 3 systemPrompt: "", 4 model: getModel("google", "gemini-2.5-flash-lite-preview-06-17"), 5 thinkingLevel: "off", 6 tools: [], 7 messages: [], 8 isStreaming: false, 9 streamMessage: null, 10 pendingToolCalls: new Set<string>(), 11 error: undefined, 12};
Die AgentOptions-Schnittstelle (packages/agent/src/agent.ts:41-114) bietet umfangreiche Konfigurationsmöglichkeiten:
| Option | Typ | Beschreibung |
|---|---|---|
convertToLlm | (messages: AgentMessage[]) => Message[] | Transformiert interne Messages vor LLM-Aufruf |
transformContext | (messages, signal) => Promise<AgentMessage[]> | Context-Pruning oder externe Injektion |
steeringMode | "all" | "one-at-a-time" | Steuerung der Steering-Message-Zustellung |
streamFn | StreamFn | Custom Stream-Funktion für Proxy-Backends |
getApiKey | (provider: string) => Promise<string> | Dynamische API-Key-Auflösung |
toolExecution | "parallel" | "sequential" | Tool-Ausführungsmodus |
Message-Konvertierung und Context-Transformation
Die Standard-Implementierung defaultConvertToLlm (packages/agent/src/agent.ts:37-39) filtert Messages auf LLM-kompatible Rollen:
typescript1function defaultConvertToLlm(messages: AgentMessage[]): Message[] { 2 return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult"); 3}
Die optionale transformContext-Funktion ermöglicht erweiterte Szenarien wie Context-Window-Management oder Injection externer Kontexte vor jedem LLM-Aufruf.
Tool-Execution-Lifecycle
Der Agent unterstützt zwei Tool-Execution-Modi:
- Parallel (Standard): Mehrere Tool-Calls werden gleichzeitig ausgeführt
- Sequential: Tool-Calls werden nacheinander verarbeitet
Zusätzlich bieten beforeToolCall und afterToolCall Hooks (packages/agent/src/agent.ts:109-113) Eingriffspunkte für Validierung, Logging oder Modifikation von Tool-Ergebnissen.
Session Management
Das Session-Management-System verantwortet Persistierung, Wiederherstellung und Metadaten-Verwaltung von Konversationen. Es integriert nahtlos mit dem Agent-Core und unterstützt File-basierte Speicherung.
Session-Info-Erstellung
Die Funktion buildSessionInfo (packages/coding-agent/src/core/session-manager.ts:542) extrahiert Session-Metadaten aus gespeicherten Einträgen:
正在加载图表渲染器...
Die Aktivitätszeit-Berechnung (packages/coding-agent/src/core/session-manager.ts:504-530) priorisiert Message-Timestamps über Header-Timestamps:
typescript1function getLastActivityTime(entries: FileEntry[]): number | undefined { 2 let lastActivityTime: number | undefined; 3 for (const entry of entries) { 4 if (entry.type !== "message") continue; 5 const message = (entry as SessionMessageEntry).message; 6 if (!isMessageWithContent(message)) continue; 7 if (message.role !== "user" && message.role !== "assistant") continue; 8 9 const msgTimestamp = (message as { timestamp?: number }).timestamp; 10 if (typeof msgTimestamp === "number") { 11 lastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp); 12 } 13 } 14 return lastActivityTime; 15}
CLI-Integration
Der Haupteinstiegspunkt (packages/coding-agent/src/main.ts:1-54) integriert Session-Management mit der CLI-Argumentverarbeitung:
typescript1// Aus packages/coding-agent/src/main.ts:25-31 2import { SessionManager } from "./core/session-manager.js"; 3import { SettingsManager } from "./core/settings-manager.js"; 4import { printTimings, time } from "./core/timings.js"; 5import { allTools } from "./core/tools/index.js"; 6import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.js";
Die SessionManager-Klasse wird für Operationen wie Session-Auflistung, -Laden und -Speicherung verwendet, während selectSession (packages/coding-agent/src/main.ts:15) einen interaktiven Session-Picker bereitstellt.
Model Resolution Registry
Das Model-Resolution-System ermöglicht flexible Auswahl von LLM-Providern und Modellen über CLI-Argumente mit Fuzzy-Matching-Unterstützung.
CLI-Modellauflösung
Die Funktion resolveCliModel (packages/coding-agent/src/core/model-resolver.ts:328-367) unterstützt multiple Eingabeformate:
typescript1export function resolveCliModel(options: { 2 cliProvider?: string; 3 cliModel?: string; 4 modelRegistry: ModelRegistry; 5}): ResolveCliModelResult { 6 const { cliProvider, cliModel, modelRegistry } = options; 7 8 if (!cliModel) { 9 return { model: undefined, warning: undefined, error: undefined }; 10 } 11 12 // Wichtig: Verwende ALLE Modelle, nicht nur mit vorkonfiguriertem Auth 13 const availableModels = modelRegistry.getAll(); 14 if (availableModels.length === 0) { 15 return { 16 model: undefined, 17 warning: undefined, 18 error: "No models available. Check installation or add models to models.json.", 19 }; 20 } 21 22 // Case-insensitive Provider-Lookup 23 const providerMap = new Map<string, string>(); 24 for (const m of availableModels) { 25 providerMap.set(m.provider.toLowerCase(), m.provider); 26 } 27 // ... Fuzzy-Matching Logik 28}
Unterstützte CLI-Formate:
| Format | Beispiel | Beschreibung |
|---|---|---|
--provider X --model Y | --provider openai --model gpt-4 | Explizite Provider-Angabe |
--model provider/model | --model openai/gpt-4 | Kombiniertes Format |
--model pattern:thinking | --model claude:high | Mit Thinking-Level |
Dynamische API-Key-Verwaltung
Der Agent unterstützt dynamische API-Key-Auflösung über den getApiKey-Callback (packages/agent/src/agent.ts:79-81):
typescript1/** 2 * Resolves an API key dynamically for each LLM call. 3 * Useful for expiring tokens (e.g., GitHub Copilot OAuth). 4 */ 5getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
Dies ermöglicht die Verwendung von OAuth-Tokens mit begrenzter Gültigkeitsdauer, die vor jedem LLM-Aufruf erneuert werden müssen.
Transport-Konfiguration
Die AgentOptions erlauben die Auswahl des bevorzugten Transports (packages/agent/src/agent.ts:94-96):
typescript1/** 2 * Preferred transport for providers that support multiple transports. 3 */ 4transport?: Transport;
Der Standard-Transport ist "sse" (Server-Sent Events), kann aber für spezifische Provider-Anforderungen überschrieben werden.
Extension Package System
Das Extension-System ermöglicht die Entdeckung und Verwaltung von Plugins, Skills, Prompts und Themes über eine Manifest-basierte Architektur.
Ressourcentypen und Manifest-Struktur
Der Package Manager definiert vier Ressourcentypen (packages/coding-agent/src/core/package-manager.ts:109-118):
typescript1type ResourceType = "extensions" | "skills" | "prompts" | "themes"; 2 3const FILE_PATTERNS: Record<ResourceType, RegExp> = { 4 extensions: /\.(ts|js)$/, 5 skills: /\.md$/, 6 prompts: /\.md$/, 7 themes: /\.json$/, 8};
Das PiManifest-Interface (packages/coding-agent/src/core/package-manager.ts:88-93) definiert die Struktur in package.json:
typescript1interface PiManifest { 2 extensions?: string[]; 3 skills?: string[]; 4 prompts?: string[]; 5 themes?: string[]; 6}
Extension-Discovery
Die Funktion resolveExtensionEntries (packages/coding-agent/src/core/package-manager.ts:420-448) implementiert einen mehrstufigen Discovery-Prozess:
正在加载图表渲染器...
Discovery-Logik:
- Manifest-basiert: Wenn
package.jsoneinpi.extensions-Array enthält, werden diese Pfade aufgelöst - Index-basiert: Fallback auf
index.tsoderindex.jsim Verzeichnis - Validierung: Nur existierende Dateien werden zurückgegeben
Resource Accumulator
Der ResourceAccumulator (packages/coding-agent/src/core/package-manager.ts:95-100) sammelt alle Ressourcen mit Metadaten:
typescript1interface ResourceAccumulator { 2 extensions: Map<string, { metadata: PathMetadata; enabled: boolean }>; 3 skills: Map<string, { metadata: PathMetadata; enabled: boolean }>; 4 prompts: Map<string, { metadata: PathMetadata; enabled: boolean }>; 5 themes: Map<string, { metadata: PathMetadata; enabled: boolean }>; 6}
Die enabled-Flag ermöglicht Deaktivierung ohne physisches Entfernen der Ressource.
User Interface Layers
pi-mono bietet drei unterschiedliche Präsentationsschichten für verschiedene Einsatzszenarien: Terminal (TUI), Browser (Web-UI) und Chat-Plattformen (Slack via Mom).
Terminal User Interface (TUI)
Das TUI-Package (packages/tui/src/tui.ts:57-212) implementiert differenzielles Rendering für Terminal-basierte Interaktion:
typescript1export class TUI extends Container { 2 public terminal: Terminal; 3 private previousLines: string[] = []; 4 private previousWidth = 0; 5 // ... 6}
Kernkonzepte:
- Container-Hierarchie:
Container-Klasse verwaltet Child-Komponenten (packages/tui/src/tui.ts:173-204) - Differenzielles Rendering: Nur veränderte Zeilen werden aktualisiert
- Cursor-Marker: APC-Sequenz für Cursor-Positionierung (packages/tui/src/tui.ts:67)
typescript1export const CURSOR_MARKER = "\x1b_pi:c\x07";
Die OverlayOptions-Schnittstelle (packages/tui/src/tui.ts:114-138) unterstützt flexible Positionierung mit Prozentwerten:
typescript1export interface OverlayOptions { 2 width?: SizeValue; // Number or "50%" 3 minWidth?: number; 4 maxHeight?: SizeValue; 5 anchor?: OverlayAnchor; // "center", "top-left", etc. 6 offsetX?: number; 7 offsetY?: number; 8 // ... 9}
Web User Interface
Das Web-UI-Package (packages/web-ui/src/index.ts:1-119) exportiert eine umfassende React-Komponentenbibliothek:
Hauptkomponenten:
| Komponente | Beschreibung |
|---|---|
ChatPanel | Haupt-Chat-Interface |
MessageList | Nachrichtenliste mit Virtualisierung |
AgentInterface | Container für Agent-Interaktion |
StreamingMessageContainer | Streaming-Nachrichten-Anzeige |
Storage-Backends:
typescript1export { AppStorage, getAppStorage, setAppStorage } from "./storage/app-storage.js"; 2export { IndexedDBStorageBackend } from "./storage/backends/indexeddb-storage-backend.js"; 3export { Store } from "./storage/store.js";
Tool-Renderer:
Das Web-UI bietet ein erweiterbares Tool-Renderer-System (packages/web-ui/src/index.ts:104-112):
typescript1export { getToolRenderer, registerToolRenderer, renderTool, setShowJsonMode } from "./tools/index.js"; 2export { BashRenderer } from "./tools/renderers/BashRenderer.js"; 3export { CalculateRenderer } from "./tools/renderers/CalculateRenderer.js"; 4export { DefaultRenderer } from "./tools/renderers/DefaultRenderer.js";
Slack Integration (Mom Package)
Das Mom-Package (packages/mom/src/main.ts:1-293) integriert den Coding-Agent mit Slack:
typescript1const MOM_SLACK_APP_TOKEN = process.env.MOM_SLACK_APP_TOKEN; 2const MOM_SLACK_BOT_TOKEN = process.env.MOM_SLACK_BOT_TOKEN;
Handler-Implementierung:
Der MomHandler (packages/mom/src/main.ts:281-293) verwaltet Channel-spezifische Agent-Instanzen:
typescript1const handler: MomHandler = { 2 isRunning(channelId: string): boolean { 3 const state = channelStates.get(channelId); 4 return state?.running ?? false; 5 }, 6 7 async handleStop(channelId: string, slack: SlackBot): Promise<void> { 8 const state = channelStates.get(channelId); 9 if (state?.running) { 10 state.stopRequested = true; 11 state.runner.abort(); 12 // ... 13 } 14 } 15};
Message-Management:
Die createMomContext-Funktion (packages/mom/src/main.ts:135-274) implementiert Truncation für Slack-Limits:
typescript1respond: async (text: string, shouldLog = true) => { 2 const MAX_MAIN_LENGTH = 35000; 3 const truncationNote = "\n\n_(message truncated, ask me to elaborate on specific parts)_"; 4 if (accumulatedText.length > MAX_MAIN_LENGTH) { 5 accumulatedText = accumulatedText.substring(0, MAX_MAIN_LENGTH - truncationNote.length) + truncationNote; 6 } 7 // ... 8}
Datenfluss und Aufrufketten
Der folgende Sequenzdiagramm zeigt den vollständigen Datenfluss von einer Benutzereingabe bis zur Tool-Ausführung:
正在加载图表渲染器...
Datenfluss-Erklärung:
- Eingabe-Verarbeitung: Benutzer-Eingabe wird vom UI-Layer an
AgentSessionweitergeleitet - Context-Transformation: Optionale Pruning oder Anreicherung des Contexts (packages/agent/src/agent.ts:52-54)
- Message-Konvertierung: Filterung auf LLM-kompatible Formate
- Streaming: LLM-Provider streamt Tokens zurück
- Tool-Execution: Bei Tool-Calls werden
beforeToolCall, Execution undafterToolCalldurchlaufen - Event-Propagation: Ergebnisse werden als
AgentEventan UI zurückgesendet
Modulabhängigkeiten
Die Modulabhängigkeiten zeigen die hierarchische Struktur des Monorepos:
正在加载图表渲染器...
Abhängigkeitsregeln:
- UI-Pakete (TUI, Web-UI, Mom) hängen vom Core ab, nie umgekehrt
- Agent ist die einzige Abstraktion für LLM-Zugriff
- AI Package kapselt alle Provider-spezifischen Implementierungen
- Pods enthält nur Typ-Definitionen (packages/pods/src/index.ts:1-2)
Technologie-Stack und Design-Entscheidungen
| Technologie | Verwendungszweck | Begründung | Alternativen |
|---|---|---|---|
| TypeScript | Gesamter Codebase | Typsicherheit, bessere IDE-Unterstützung | JavaScript, PureScript |
| Monorepo (pnpm workspaces) | Paketorganisation | Code-Sharing, einheitliche Versionierung | Polyrepo, Lerna |
| React | Web-UI Komponenten | Große Ökosystem, Component-Reuse | Vue, Svelte, Solid |
| IndexedDB | Browser-Speicherung | Persistente Storage, große Datenmengen | localStorage, SQLite WASM |
| Server-Sent Events | LLM-Streaming | Einfache Implementierung, weite Unterstützung | WebSockets, Long-Polling |
| APC-Escape-Sequenzen | Terminal-Cursor | Zero-Width Marker, Terminal-unabhängig | ANSI-Cursor, NCurses |
| JSONL | Session-Dateien | Zeilenweises Parsing, Append-freundlich | JSON, SQLite |
| Fuzzy Matching | Modellauswahl | Benutzerfreundlichkeit, Toleranz bei Tippfehlern | Exact Match, Regex |
Design-Entscheidungen
1. Agent als zentrale Abstraktion
Die Agent-Klasse wurde als einzige Schnittstelle zu LLM-Providern konzipiert. Dies ermöglicht:
- Einheitliche Tool-Execution über alle Provider
- Konsistente Error-Behandlung und Retry-Logik
- Einfache Tests durch Mocking einer einzigen Schnittstelle
2. Session-Persistierung im JSONL-Format
Sessions werden als JSONL (JSON Lines) gespeichert (packages/coding-agent/src/core/session-manager.ts:503-542):
- Vorteil: Zeilenweises Lesen/Schreiben ohne vollständiges Parsen
- Vorteil: Append-Operationen für neue Messages ohne Datei-Rewrite
- Nachteil: Keine direkte Indexierung (gelöst durch separate Metadaten)
3. Plugin-basiertes Extension-System
Extensions werden über Manifest-Einträge oder Index-Dateien entdeckt (packages/coding-agent/src/core/package-manager.ts:409-448):
- Vorteil: Keine Registrierung zur Laufzeit erforderlich
- Vorteil: Statische Analyse der Abhängigkeiten möglich
- Nachteil: Erfordert Neustart für neue Extensions
4. Multi-Transport-Unterstützung
Der Agent unterstützt verschiedene Transport-Protokolle (packages/agent/src/agent.ts:94-96):
- Ermöglicht Provider-spezifische Optimierungen
- Unterstützt Proxy-Szenarien mit angepasstem Transport
5. Differenzielles Terminal-Rendering
Die TUI-Klasse (packages/tui/src/tui.ts:209-212) speichert vorherige Zeilen:
- Minimiert Terminal-Updates
- Reduziert Flackern bei schnellen Updates
- Ermöglicht effiziente Cursor-Positionierung
Startup-Timings und Performance
Das timings-Modul (packages/coding-agent/src/core/timings.ts:9-25) bietet Performance-Monitoring:
typescript1export function time(label: string): void { 2 if (!ENABLED) return; 3 const now = Date.now(); 4 timings.push({ label, ms: now - lastTime }); 5 lastTime = now; 6} 7 8export function printTimings(): void { 9 if (!ENABLED || timings.length === 0) return; 10 console.error("\n--- Startup Timings ---"); 11 for (const t of timings) { 12 console.error(` ${t.label}: ${t.ms}ms`); 13 } 14 console.error(` TOTAL: ${timings.reduce((a, b) => a + b.ms, 0)}ms`); 15}
Verwendung im Main-Entry:
typescript1// Aus packages/coding-agent/src/main.ts:28 2import { printTimings, time } from "./core/timings.js";
Die Timing-Funktion ist standardmäßig deaktiviert (ENABLED-Flag) und kann für Performance-Analyse aktiviert werden.
