# Die Claude-Agent-SDK-Integration

> Wie der Desktop Claude über einen Node-Agent-Host ausführt: die JSON-Lines-Pipe, die HB-Code-Tool-Brücke, die Projektion von Claude-Nachrichten in die gemeinsamen Timeline-Zeilen, der Zustellnachweis, die Run-Schleife, Subagents und Konten.

Handbuch / Architektur · Gegen den Quellcode geprüft am 2026-10-05 · https://www.hbcode.ai/de/docs/claude-agent-sdk

## Der Agent-Host-Prozess

Claude läuft über den Agent-Host, ein kleines TypeScript-Programm in desktop/agent-host, das @anthropic-ai/claude-agent-sdk verwendet. Der Desktop startet ihn mit Node und übergibt den Pfad der mitgelieferten Claude-Programmdatei als einziges Argument. Ein Entwicklungs-Build führt node aus dem PATH mit agent-host/dist/src/entry.js aus. Eine paketierte App führt ihr mitgeliefertes node mit claude/host/entry.mjs aus ihren Ressourcen aus. Der macOS-Build und die Windows-Builds liefern diese Laufzeit mit.

Beide Seiten tauschen über stdin und stdout ein JSON-Objekt pro Zeile aus. Eine Anfrage trägt eine id mit 1 bis 200 Zeichen, und ihre Antwort trägt dieselbe id mit einem Ergebnis oder einem Fehler. Eine Zeile ohne id ist ein Event. Der Host prüft jede Anfrage gegen ein striktes Schema, daher schlägt ein unbekanntes Feld fehl. Der Desktop gibt jeder Anfrage 35 Sekunden. Eine Anfrage, die bis dahin keine Antwort hat, schlägt mit „Claude host request timed out“ fehl.

| Anfrage | Felder, Grenzen und was der Host tut |
| --- | --- |
| open | Felder: cwd, sessionId (UUID), model, effort (low, medium, high, xhigh oder max, optional), permissionMode (plan, acceptEdits, auto oder bypassPermissions), instructions, appCapabilities. Startet eine SDK-Abfrage für die Session in ihrem Projektordner. instructions ist der Text, den Claude an seinen System-Prompt anhängt, und der Desktop schreibt ihn. Der Ordner und die ausführbare Datei müssen absolute Pfade sein. Sie setzt die Unterhaltung fort, wenn Claude ein gespeichertes Transkript für diese id hat, und startet sonst eine neue mit dieser id. Ein Host besitzt höchstens eine Session, daher scheitert ein zweites open. |
| openWorker | Felder: cwd, model, effort (optional), instructions, prompt, appCapabilities. Startet die private Query eines HB-Code-Workers im Projektordner. Die Query hat nur Read, Glob, Grep und die Tools in appCapabilities, nutzt den Berechtigungsmodus dontAsk, lädt keine Benutzereinstellungen und speichert kein Transkript. Sie läuft einen Turn lang. Ein Host besitzt eine Session oder einen Worker, daher schlägt ein zweites open oder openWorker fehl. |
| send | Felder: messageId (UUID), text. Schreibt die Nachricht mit der UUID, die der Desktop für diese Operation in claude_operations gespeichert hat, in die offene Query. Claude nimmt jederzeit eine Nachricht an. Eine Nachricht, die während eines laufenden Turns ankommt, kommt nach Claudes aktuellem Schritt in diesen Turn. Endet der Turn zuerst, startet die Nachricht den nächsten Turn. |
| cancel | Felder: messageId (UUID). Holt eine Nachricht zurück, die Claude noch nicht begonnen hat, mit dem SDK-Aufruf cancelAsyncMessage. Das Ergebnis ist cancelled: true, oder false, wenn Claude die Nachricht schon hat. |
| permission | Felder: requestId, allow. Beantwortet eine ausstehende Tool-Berechtigung mit allow oder deny. |
| toolResult | Felder: requestId (UUID), result mit success und contentItems. Gibt das Ergebnis eines HB-Code-Tool-Aufrufs an den wartenden SDK-Tool-Handler zurück. Ein Ergebnis für einen Aufruf, der nicht mehr offen ist, wird ignoriert. |
| interrupt | Keine Felder. Lehnt jede offene Berechtigung ab, stoppt jeden offenen HB-Code-Tool-Aufruf und unterbricht den Turn. Er verwirft auch die Nachrichten, die noch auf den Turn warten, sodass keine von ihnen nach dem Stop einen neuen Turn startet. |
| close | Keine Felder. Schließt die Query. Der Host-Prozess endet, wenn der Desktop dessen stdin schließt. |
| history | Auch subagentHistory. Felder: cwd, sessionId, before (UUID, optional), limit 1 bis 100. subagentHistory fügt agentId hinzu, 1 bis 64 Kleinbuchstaben und Ziffern. Gibt eine Seite der gespeicherten Unterhaltung oder eines Subagents zurück. |
| messageSaved | Felder: cwd, sessionId, messageId (UUID). Liefert saved: true, wenn die gespeicherte Unterhaltung die User-Nachricht mit dieser UUID hat. Das ist der Zustellnachweis. Der Host behält die gespeicherten IDs eines Transkripts und liest nur die Bytes, die Claude nach dem letzten Lesen angefügt hat. |
| historyAround | Felder: cwd, sessionId, agentId (optional), place, limit 1 bis 100. Die Stelle ist eine Nachrichten-uuid, eine toolUseId, eine apiMessageId, eine Zeit mit atOrAfter oder atOrBefore oder die Nachrichten nach einer uuid. Liefert mit einem Lesevorgang die Seite der Unterhaltung oder eines Subagents, die die Stelle enthält. Eine Stelle in den neuesten limit Nachrichten ergibt newest: true. Eine Stelle, die das Transkript nicht hat, ergibt found: false. |
| toolOutput | Felder: cwd, sessionId, agentId (optional), toolUseId, 1 bis 128 Buchstaben, Ziffern, _ oder -. Gibt das gespeicherte Ergebnis eines Tool-Aufrufs zurück. |
| list, inspect | Felder: cwd sowie offset und limit 1 bis 100, oder sessionId. Listet die gespeicherten Unterhaltungen des Ordners auf oder gibt eine Unterhaltung mit dem Modell zurück, das zuletzt geantwortet hat. |
| subagents | Felder: cwd, sessionId, known, endedRuns, openRuns, jeweils höchstens 10.000 Einträge. Liefert die Subagents und Workflow-Runs der Unterhaltung. Er liest nur, was der Desktop noch nicht hält oder was sich geändert hat. |
| catalog, usage, title | Felder: cwd für catalog. cwd und prompt, höchstens 40.000 Zeichen, für title. Liest die Modellliste, die Plan-Nutzung oder einen kurzen Session-Titel. |

| Event | Felder und Bedeutung |
| --- | --- |
| message | Felder: message. Eine SDK-Nachricht, wie die Query sie liefert: Stream-Events, Assistant- und User-Nachrichten, Systemnachrichten und das Ergebnis des Turns. |
| permission | Felder: requestId, toolUseId, tool, input, title, reason, agentId. Claude wartet auf eine Antwort, bevor es ein Tool verwendet. agentId ist gesetzt, wenn ein Subagent fragt. |
| permission erledigt | Das Event permissionResolved. Felder: requestId, allow. Die Anfrage ist erledigt, durch eine Antwort oder durch einen Stop. |
| toolCall | Felder: requestId, toolUseId, tool, arguments. Claude hat ein HB-Code-Tool aufgerufen, und der Handler wartet auf toolResult. |
| toolCancel | Felder: requestId. Der Aufruf wurde gestoppt. Der Desktop muss seine Arbeit dafür beenden. |
| failed | Felder: error. Der SDK-Stream hat einen Fehler geworfen. |
| closed | Keine Felder. Der SDK-Stream endete, obwohl niemand die Session geschlossen hat. |

**Ein Host besteht für einen Run, und jeder andere Lesevorgang startet einen eigenen Host**

Vier Bahnen über einen Run, nicht maßstäblich. Der Desktop startet einen Agent-Host und sendet open und send. Der Host hält eine SDK-Abfrage, und das SDK startet die ausführbare Datei claude. Wenn die Session untätig wird, meldet der Host das. Der Desktop sendet close, schließt dann stdin, und der Host beendet sich. Ein Host, der 10 Sekunden später noch läuft, wird mit jedem Prozess beendet, den er gestartet hat. Der Host des Runs beantwortet auch die Lesevorgänge für den Zustellnachweis des Runs. Getrennte Einmal-Hosts beantworten einen Lesevorgang für eine Verlaufsseite, einen für eine Befehlsausgabe und den letzten Lesevorgang, und jeder beendet sich nach einer Anfrage.

- **Run-Host** (ClaudeHost::spawn): Jeder Run startet einen neuen Host mit Modell, Effort und Berechtigungsmodus der Session. Er öffnet eine SDK-Query im Projektordner, setzt die gespeicherte Unterhaltung fort, falls es eine gibt, und überlebt den Run nie.
- **Beenden** (close · stdin · killpg): Im Zustand idle sendet der Desktop close und gibt dafür 3 Sekunden, dann schließt er stdin. Läuft der Host 10 Sekunden später noch, beendet der Desktop ihn mit jedem Prozess, den er gestartet hat, sodass auch der claude-Kindprozess endet.
- **Einmal-Hosts** (ClaudeHost::one_shot): Verlaufsseiten, Befehlsausgaben, Subagent-Transkripte, der letzte Zustellnachweis, der Modellkatalog, die Nutzung und Titel starten je einen Host, der eine Anfrage beantwortet und sich beendet. Während eines Runs beantwortet der Host des Runs die Lesevorgänge für den Zustellnachweis. Transkript-Lesevorgänge nutzen immer den Claude-Hauptordner.
- **Geerbte Markierungen** (CLAUDECODE · CLAUDE_CODE_CHILD_SESSION · CLAUDE_PID): Werden mit sechs weiteren vor jedem Start entfernt. Ein Desktop, der aus Claude Code heraus gestartet wurde, würde seine Sessions sonst als Kind-Sessions ausführen, und Claude speichert deren Transkripte nicht.

Jeder Run bekommt einen neuen Host, und der Host überlebt seinen Run nie. Wenn der Run endet, sendet der Desktop close und wartet höchstens 3 Sekunden auf die Antwort. Dann schließt er stdin und gibt dem Host 10 Sekunden zum Beenden, danach beendet er den Host und jeden Prozess, den der Host gestartet hat: unter macOS die Prozessgruppe, unter Windows den Prozessbaum mit taskkill. Lesevorgänge, die keine offene Unterhaltung brauchen, etwa der Modellkatalog oder ein Titel, laufen in einem eigenen Einmal-Host. Vor dem Start entfernt der Desktop neun Umgebungsmarker, die eine übergeordnete Claude-Code-Session ihren Kindern gibt, darunter CLAUDECODE, CLAUDE_CODE_CHILD_SESSION, CLAUDE_CODE_SESSION_ID und CLAUDE_PID. Ein Desktop, den Sie aus Claude Code heraus starten, würde seine Sessions sonst als Kind-Sessions ausführen, und Claude speichert deren Transkripte nicht.

Eine Anfrage, die am Schema scheitert, bekommt eine Fehlerantwort mit ihrer id. Eine Zeile, die der Host nicht als JSON lesen kann, bekommt eine Antwort mit der id null, und der Desktop ignoriert sie. Der Desktop puffert höchstens 256 Events eines Hosts. Wenn stdout des Hosts schließt oder eine Zeile kein JSON ist, lässt der Desktop jede wartende Anfrage fehlschlagen und erzeugt selbst ein failed-Event. Die Standardfehlerausgabe des Hosts geht in das Debug-Log des Desktops.

- Einstellungen und Prompt: Die Abfrage lädt die Einstellungen user, project und local und nutzt den System-Prompt claude_code. Der Host hängt die instructions von open daran an. Für jeden Run schreibt agents/session_rules.rs diesen Text in dieser Reihenfolge: die Fragenregel, die Regel für Session-Nachrichten, die Regel für Anweisungen und den Text des Antwortstils. Die Fragenregel sagt, dass Claude den Nutzer mit AskUserQuestion fragt und dass der Aufruf nicht wartet: Der Nutzer antwortet in einer späteren Nachricht. Die Regel für Session-Nachrichten sagt, dass Claude eine andere HB-Code-Session nur mit den hbcode-Tools send_session_message und maintain_sessions erreicht, weil SendMessage und ListAgents nur Claude-Code-Sessions außerhalb von HB Code erreichen. Die Regel für Anweisungen sagt Claude, eine ausdrückliche Anweisung ganz auszuführen, eine Frage zu stellen oder mit Belegen zu widersprechen. Simplified Technical English fügt seinen Antworttext zuletzt hinzu, Default fügt keinen Text hinzu. Der App-Tool-Text steht nicht in diesem Text: Er ist die instructions des MCP-Servers hbcode.
- Stream: includePartialMessages liefert die Live-Deltas, und forwardSubagentText sendet jeden Subagent-Turn vollständig. persistSession speichert das Transkript.
- Tools: Die Query bekommt den MCP-Server hbcode und dessen allowedTools. Ein PreToolUse-Hook beantwortet jeden AskUserQuestion-Aufruf sofort. allowDangerouslySkipPermissions ist nur für bypassPermissions gesetzt.
- Run-Ende: Der Host schaltet Claudes Session-Zustands-Events mit einer Umgebungsvariable ein, weil ein Turn, der Subagents im Hintergrund startet, vor ihnen endet. Nur der Zustand idle sagt dem Host, dass der ganze Run vorbei ist.
- Eingabe: Die Query liest ihre Nachrichten aus einem Stream, der für den ganzen Run offen bleibt. Der Host schreibt jedes send sofort hinein, auch während ein Turn läuft.

Die title-Anfrage ist eine eigene Query. Sie verwendet das Modell haiku ohne Tools, mit einem Turn, ohne Einstellungen und ohne gespeicherte Session, und ihr System-Prompt verbietet Claude, auf die Nachricht hin zu handeln. Der Desktop fragt nach send einen Titel an, in einem Einmal-Host mit dem aktiven Konto.

## HB-Code-Tools über den Host

Codex und Claude bekommen dieselben HB-Code-Tools aus einem Katalog in services/agent_tools. Für Claude baut der Desktop die Liste für jeden Run und sendet sie in open.appCapabilities: einen instructions-Text und für jedes Tool Name, Beschreibung, Eingabeschema und Freigabeklasse. Der Host macht aus der Liste einen SDK-MCP-Server im selben Prozess mit dem Namen hbcode, sodass Claude jedes Tool als mcp__hbcode__<name> sieht. Ein doppelter Name oder ein Eingabeschema, das kein Objekt ist, lässt open fehlschlagen.

| Freigabeklasse | Tools und wer einen Aufruf freigibt |
| --- | --- |
| read-only | Tools: maintain_sessions. Niemand gibt frei. Der Host setzt das Tool in allowedTools und markiert es mit readOnlyHint, daher fragt Claude nicht. |
| desktop | Tools: send_session_message. Der Desktop gibt frei, innerhalb des Tools. Der Host setzt das Tool in allowedTools. Eine Nachricht, die Sie braucht, wartet in der Queue des Empfängers. |
| agent | Tools: use_user_browser, manage_sessions, send_mobile_notification, synthesize_speech, create_explainer_video, extract_video_context. Claude fragt über canUseTool, wie sein Berechtigungsmodus es vorgibt. Sie bekommen dieselbe Freigabekarte wie für ein eingebautes Tool. |

Die Liste hängt von den Einstellungen ab. send_mobile_notification ist nur dabei, solange Benachrichtigungen eingeschaltet sind, und synthesize_speech, create_explainer_video und extract_video_context nur mit einem Gemini-Schlüssel.

Wenn Claude ein Tool aufruft, prüft der SDK-Handler die Argumente gegen das vollständige Schema, erzeugt eine neue requestId und sendet toolCall. Dann wartet der Handler. Das Event kann sein Ziel nicht wählen. Der Desktop weist ein toolCall mit einem anderen Feld als requestId, toolUseId, tool und arguments zurück und leitet den Aufruf über die Session und den Run weiter, denen dieser Host gehört. Ein zurückgewiesenes Event oder ein zweiter Aufruf mit derselben requestId lässt den Run fehlschlagen.

**Ein langsames HB-Code-Tool läuft in einem eigenen Task, daher verarbeitet die Run-Schleife Stop weiterhin**

Fünf Bahnen, nicht maßstäblich. Claude ruft in einem Turn zwei HB-Code-Tools auf. Der Agent-Host hält beide Aufrufe und sendet für jeden ein toolCall-Event. Die Run-Schleife startet einen Task pro Aufruf und macht mit Freigaben, Zeilen und ihrem Heartbeat weiter. Task A wird fertig und sendet toolResult an den Host, der Claude das Tool-Ergebnis gibt. Task B läuft noch, als Sie Stop drücken. Die Schleife sendet interrupt, der Host antwortet Claude mit „Run stopped.“ und sendet toolCancel, und der Desktop bricht Task B ab.

- **Tool-Liste** (open.appCapabilities): Der Desktop sendet mit open die Anweisungen und für jedes Tool Name, Beschreibung, Eingabeschema und Freigabeklasse. Der Host baut daraus den SDK-MCP-Server hbcode.
- **Aufruf** (toolCall { requestId, toolUseId, tool, arguments }): Der Host hält den SDK-Tool-Handler unter einer neuen requestId offen. Das Event kann keine Session und keinen Run nennen. Der Desktop leitet den Aufruf über den Run weiter, dem der Host gehört.
- **Tool-Task** (AppToolCalls · toolResult): Jeder Aufruf läuft in einem eigenen Task neben der Schleife, und der Task sendet toolResult selbst. Wenn diese Anfrage fehlschlägt, schlägt der Run fehl.
- **Abbruch** (toolCancel { requestId }): Bei Stop oder einem abgebrochenen Aufruf antwortet der Host Claude mit „Run stopped.“ und sendet toolCancel. Der Desktop bricht den Task ab, und ein Ergebnis, das sich mit dem Abbruch kreuzt, wird ignoriert.

Jeder Aufruf läuft in einem eigenen Task neben der Run-Schleife. Ein langsames Tool kann daher weder Stop noch Freigaben, Timeline-Zeilen oder das Ergebnis eines anderen Tools blockieren. Der Task führt den gemeinsamen Tool-Handler mit Claudes Tool-Use-id als Aufruf-id aus und sendet toolResult dann selbst. Ein Tool, das fehlschlägt, liefert ein Ergebnis mit success false, und Claude sieht einen Tool-Fehler. Wenn die toolResult-Anfrage fehlschlägt, schlägt der Run fehl.

Wenn das SDK einen Aufruf abbricht oder der Run stoppt, antwortet der Host dem Handler mit „Run stopped.“ und sendet toolCancel, und der Desktop bricht diesen Task ab. Ein Ergebnis, das den Abbruch kreuzt, wird ignoriert. Am Ende des Runs stoppt der Desktop jeden verbliebenen Task. In der Timeline erzeugt der Desktop den Titel einer HB-Code-Tool-Zeile aus ihrer Anfrage, zum Beispiel „Create session“ oder „Revise explainer video“.

- [Dokumente, Sprache, Video, Benachrichtigungen, Session-Suche](https://www.hbcode.ai/de/docs/tools/agent-tools.md): Was jedes HB-Code-Tool tut, seine Grenzen und wohin seine Daten gehen.
- [Nachrichten zwischen Sessions](https://www.hbcode.ai/de/docs/message-delivery.md#session-messages): Was send_session_message tut, nachdem der Desktop einen Aufruf angenommen hat.
- [Antwortstil und Anweisungen](https://www.hbcode.ai/de/docs/tools/agent-tools.md#reply-style): Der Text der Regel für Anweisungen und was jeder Antwortstil hinzufügt.

## Verlauf und die gemeinsamen Timeline-Zeilen

Der Desktop liest Claudes Transkriptdateien nie selbst. Der Host liest sie mit getSessionMessages und gibt Seiten mit höchstens 100 vollständigen Nachrichten zurück. Er fügt benachbarte Assistant-Umschläge mit derselben API-Nachrichten-id zusammen und verwirft Nachrichten, die nichts zu zeigen haben. Eine Seitengrenze ist eine Nachrichten-uuid, und wenn diese Nachricht fehlt, bittet die Antwort den Desktop, neu zu laden. Eine Unterhaltung ohne Transkript lädt als leer. Das SDK liefert eine komprimierte Unterhaltung erst ab ihrer letzten Komprimierung, deshalb erscheinen ältere Turns einer solchen Unterhaltung nicht in HB Code. Die vollständige Ausgabe eines Befehls wird bei Bedarf über toolOutput gelesen, auch für einen Subagent, so wie eine Codex-Protokollausgabe.

Der Cursor für ältere Seiten ist ein JSON-Objekt mit session, before, anchor und revision und höchstens 4096 Zeichen lang. Der Desktop prüft ihn gegen die Unterhaltung oder den Subagent, durch den er blättert. Ein Cursor für ein anderes Ziel schlägt mit einem Konflikt fehl, der Sie bittet, die Unterhaltung neu zu laden.

Lesevorgänge sind nicht billig. Jeder Transkript-Lesevorgang startet einen Einmal-Host, und getSessionMessages liest das ganze Transkript, weil der SDK-Aufruf keinen Bereich kennt. Ein Timeline-Ladevorgang lässt zwei Hosts nebeneinander laufen: einen für die Seite und einen für die Subagent-Liste. Ein Zustellnachweis ist ein einziger messageSaved-Lesevorgang. Während eines Runs beantwortet ihn der Host des Runs. Nach dem Run beantwortet ihn ein Einmal-Host.

**Claude speichert eine Zeile pro Inhaltsblock, und HB Code fügt diese Zeilen zu Timeline-Zeilen zusammen**

Links acht Zeilen einer gespeicherten Claude-Unterhaltung, rechts die Timeline-Zeilen, die daraus werden. Drei assistant-Zeilen teilen die API-Nachrichten-id msg_01, daher fügt der Host sie zu einer Nachricht zusammen, bevor der Adapter ihr Thinking, ihren Text und ihren Bash-Aufruf in eine Reasoning-Zeile, eine Antwortzeile und eine Befehlszeile projiziert. Das Bash-Ergebnis kommt in einer späteren user-Zeile und vervollständigt die Befehlszeile über seine tool_use_id. Ein Edit-Aufruf und sein Ergebnis werden zu einer Dateiänderungszeile. Eine synthetische user-Zeile erzeugt keine Zeile.

- **Transkriptseite** (getSessionMessages · 100 Nachrichten): Der Host liest Claudes gespeicherte Unterhaltung und liefert höchstens 100 Nachrichten pro Seite. Eine Seite beginnt bei einer Nachrichten-uuid, und fehlt diese Nachricht, lädt der Desktop neu.
- **Eine API-Nachricht** (message.id): Claude speichert eine Zeile pro Inhaltsblock. Der Host fügt benachbarte Assistant-Zeilen mit derselben API-Nachrichten-id zusammen, sodass eine Nachricht live und gespeichert dieselbe Identität behält.
- **Tool-Ergebnis** (tool_use_id): Kommt in einer späteren user-Zeile, und der Adapter vervollständigt die Aufrufzeile an Ort und Stelle. Ein Bash-Ergebnis wird zu einer Ausgabezeile, die erst beim Öffnen aus dem Transkript gelesen wird.
- **Verborgene Zeilen** (isSynthetic): Synthetische Nutzernachrichten, Meta-Zeilen und leeres Thinking erzeugen keine Zeile. Terminal-Verwaltung wie /exit bleibt ebenfalls verborgen.

Der Claude-Adapter projiziert jede Nachricht in dieselben Timeline-Zeilen, die Codex verwendet. Eine Zeile nimmt ihre Identität nur aus stabilen Daten, daher ersetzt die gespeicherte Zeile die Live-Zeile an derselben Stelle.

| Zeile | Identitätsschlüssel |
| --- | --- |
| Eine Nachricht, die HB Code gesendet hat | user-message::{operationId}, gleich für die Outbox-Zeile, die Live-Zeile und die gespeicherte Zeile |
| Antworttext | {messageId}:text, mit der API-Nachrichten-id |
| Thinking | {messageId}:thinking:{position}, die Position unter den Thinking-Blöcken der Nachricht |
| Tool-Aufruf | tool:{toolUseId}:call |
| Befehlsausgabe oder ein Ergebnis, dessen Aufruf auf einer älteren Seite liegt | tool:{toolUseId}:result |
| Bild in einem Tool-Ergebnis | tool:{toolUseId}:image:{n} |
| Jede andere gespeicherte User-Zeile oder Notiz | Die uuid der gespeicherten Nachricht |

Das SDK sendet für jeden Inhaltsblock einen abschließenden Umschlag. Ein solcher Umschlag ersetzt sein eigenes Textfragment, daher wird ein Delta nie doppelt angefügt. Gespeicherte Zeilen sortieren nach ihrer Position im Transkript, Live-Zeilen nach ihrer Ankunft. Eine Live-Ansicht sendet auch die Zeilen, die sie vorher gezeigt hat und nicht mehr zeigt, sodass jeder Client sie entfernt. Eine Live-Prompt-Zeile gibt es nur für eine Nachricht, die HB Code gesendet hat. Ihre Zeile zeigt den Anzeigetext und die Anhänge aus claude_operations. Der Text, den Claude bekommt, ist länger, weil der Desktop den Skills-Kontext und die Pfade der Anhänge hinzufügt. Jeder andere Prompt erscheint aus dem gespeicherten Transkript.

| Claude-Nachricht oder -Block | Timeline-Zeile |
| --- | --- |
| Prompt | Eine User-Zeile. Eingefügte Bilder werden zu ihren Anhängen, und <system-reminder>-Blöcke werden entfernt. |
| Bash-Tool-Aufruf und sein Ergebnis | Eine Befehlszeile mit ihrer Ausgabe. Die Ausgabezeile nennt das gespeicherte Ergebnis, das erst gelesen wird, wenn Sie es öffnen. Ein fehlgeschlagener Befehl nimmt seinen Exit-Code aus der ersten Ergebniszeile „Exit code N“. |
| Dateiänderungen | Edit, MultiEdit, Write und NotebookEdit. Eine Dateiänderungszeile. Claude liefert den alten und den neuen Text, daher baut der Adapter den Unified Diff. Die Hunks eines Edits haben bloße @@-Header, weil Claude keine Zeilennummern liefert. Ein Write zeigt eine neue Datei mit einem vollständigen Header @@ -0,0 +1,N @@. Wenn sein Ergebnis sagt, dass es eine Datei ersetzt hat, wird die Zeile zu einer Aktualisierung mit einem bloßen @@. Ein fehlgeschlagener Edit zeigt den Grund und keinen Patch. Ein NotebookEdit, das eine Zelle löscht, hat keinen Patch. |
| Lesen | Eine Tool-Zeile mit dem Dateinamen als Titel. Ihr Detail zeigt den Dateitext ohne <system-reminder>-Blöcke und öffnet die Datei. |
| Jedes andere Tool | Eine Zeile, die ihr Ergebnis an Ort und Stelle vervollständigt: Tool call, dann Tool output, Tool error oder Tool declined. Sie zeigt ein lesbares Label, etwa „Search in files“ für Grep, und ein MCP-Tool erscheint als „server: tool“. Ein Ergebnis, dessen Aufruf auf einer älteren Seite liegt, ist eine eigene Zeile, Tool result oder Tool error. Ein Ergebnisblock, der kein Text ist, erscheint als seine Art in eckigen Klammern. |
| Tool-Aufruf ohne gespeichertes Ergebnis | Gilt als beendet, wenn eine spätere Assistant-Nachricht eintrifft. Ein Befehl zeigt dann „Claude did not save the output of this command.“ |
| SendMessage-Aufruf | Eine Nachrichtenzeile „Message to <name>“ ab dem Aufruf. Wenn der Aufruf endet, zeigt die Zeile Sent oder Not sent. Eine Nachricht an einen Subagent dieser Session verlinkt auf den Subagent-Thread zum Zeitpunkt des Aufrufs. Ein Aufruf ohne Empfänger oder ohne Text bleibt eine Tool-Zeile. |
| HB-Code-Tool | Eine Tool-Zeile mit einem Titel, den der Desktop aus der Anfrage erzeugt, zum Beispiel „Create session“. Ein beendeter send_session_message-Aufruf ist eine Nachrichtenzeile. |
| Frage an den Nutzer | AskUserQuestion. Eine Nachrichtenzeile „Question for you“ mit den Fragen und ihren Optionen, keine Tool-Zeile. Eine Unterhaltung aus dem Terminal zeigt auch die gespeicherten Antworten. |
| Plan-Tools | TodoWrite, EnterPlanMode und ExitPlanMode. Ein Plan mit seinen Schritten oder eine Plan-Eventzeile mit dem Plan als Markdown. |
| Bild in einem Tool-Ergebnis | Eine Bildzeile nach der Tool-Zeile. |
| Thinking | Eine Reasoning-Zeile. Leeres Thinking erzeugt keine Zeile. |
| Unterbrechungshinweis | Eine Markierung für einen abgebrochenen Turn. |
| Komprimierungszusammenfassung | Eine Markierung „Conversation compacted“. |
| Lokaler Slash-Befehl | Sein Name und seine Ausgabe. Terminal-Verwaltung wie /exit ist ausgeblendet. |
| Nachricht vom führenden Agenten oder von einer anderen Claude-Session | Eine Nachrichtenzeile: „Message from Claude“ für den führenden Agenten und „Message from <name>“ für eine andere Claude-Session, sonst „Message from Another Claude session“. |
| User-Nachricht in der Ansicht eines Subagents | Eine Nachrichtenzeile: „Task from Claude“ für die erste, danach „Message from Claude“. Jede verlinkt auf die Haupt-Timeline zu ihrem Zeitpunkt. |
| Antwort, die Claude selbst schreibt, etwa ein Nutzungslimit oder ein API-Fehler | Eine Systemzeile. |
| Zeilen ohne Inhalt für Sie | Verborgen: eine synthetische Nutzernachricht (isSynthetic), andere Meta-Zeilen und der Hinweis, dass ein Subagent im Hintergrund fertig ist. |
| Aufruf, der einen Subagent oder einen Workflow startet | Keine Zeile. Der Aufruf und sein Ergebnis verlassen die Ansicht. Die Agentenliste zeigt den Subagent oder den Workflow. |

## Eine gespeicherte Unterhaltung finden und fortsetzen

Um eine bestehende Claude-Code-Unterhaltung fortzusetzen, listet der Desktop die gespeicherten Unterhaltungen genau dieses Workspace-Ordners auf, die neueste zuerst. Der Host ruft listSessions mit includeWorktrees false und includeProgrammatic false auf. Eine Unterhaltung aus einem anderen Checkout oder Worktree kann in diesem Ordner nicht fortgesetzt werden, daher wird sie nicht aufgelistet. Eine Seite enthält standardmäßig 25 Unterhaltungen und höchstens 100, und ihr Cursor ist ein Offset in Claudes eigener sortierter Liste. Jede Zeile trägt linkedAppSessionId, wenn eine HB-Code-Session die Unterhaltung bereits besitzt. Ein Titel erscheint nur, wenn er mehr sagt als der erste Prompt.

Eine Vorschau liest eine Seite des Transkripts. Sie bindet nichts, schreibt nichts und hat keine älteren Seiten.

Wenn Sie fortsetzen, muss inspect das Transkript in genau diesem Ordner finden. Die neue Session übernimmt Claudes eigenen Namen für die Unterhaltung, den benutzerdefinierten Titel oder sonst die Zusammenfassung. Die Tabelle claude_sessions bindet dann die beiden ids. Sie hat eine Zeile pro Session, und native_session_id ist eindeutig. Eine Unterhaltung gehört daher zu höchstens einer HB-Code-Session, und wer sie erneut wählt, öffnet diese Session statt einer zweiten. Wenn diese Session zu einem anderen Ordner gehört, schlägt die Anfrage mit einem Konflikt fehl, der die Session nennt. Eine Kopie einer Session bekommt eine neue Unterhaltungs-id. Transkript-Lesevorgänge brauchen keine Anmeldung und laufen immer mit dem Ordner des Standardkontos.

## Modelle und Effort

Die Modellliste kommt aus einem Einmal-Host für catalog, ein Lesevorgang pro Konto und Workspace-Ordner. Der Desktop behält eine Liste 10 Minuten lang und verwirft alle Listen bei einer Anmeldung, einer Abmeldung, einem Kontowechsel und beim Entfernen eines Kontos. Claude listet Aliasse wie default und opus neben festen Modell-ids auf. Der Desktop behält eine Zeile pro aufgelöster Modell-id, und der Alias default markiert nur das Modell, das das Konto empfiehlt.

Der Standard-Effort eines Modells ist high, wenn das Modell ihn anbietet, und sonst seine erste Stufe. Ein Modell ohne Effort-Stufen bekommt den Effort default, und open trägt dann kein effort-Feld. Eine neue Unterhaltung startet mit dem empfohlenen Modell. Eine fortgesetzte Unterhaltung startet mit dem Modell, das zuletzt geantwortet hat, wenn der Katalog es noch enthält.

## Live-Runs, Zustellnachweis und Freigaben

agents/dispatch sendet eine Nachricht aus der Warteschlange an den Adapter des Agenten der Session. Eine Claude-Session kann den letzten Turn nicht ersetzen und keinen Geschwindigkeitsmodus nutzen. Einen laufenden Turn kann sie steuern. Eine Session hat jeweils einen aktiven Claude-Run. Die Registry RUNS hält einen Run pro App-Session, und ein zweiter Run schlägt mit „The Claude session already has an active run.“ fehl.

Der Run öffnet den Host mit Modell, Effort und Berechtigungsmodus der Session, speichert die Operation und sendet dann den Text. Der Eintrag ist eine Zeile in claude_operations mit einer neuen UUID für die Nachricht. Das Insert behält eine vorhandene Zeile, daher behält ein erneuter Versuch derselben Operation die erste UUID. Ein erneuter Versuch mit anderem Text oder anderen Anhängen schlägt als Konflikt fehl. Ein erneuter Versuch aus einem anderen Run liest zuerst das Transkript. Hat das Transkript die Nachricht, sendet der Run nichts, und die Operation gilt als gesendet. Hat es sie nicht, wechselt die Operation zum neuen Run und geht mit derselben UUID hinaus. Der Host des früheren Runs ist beendet, daher kann Claude die Nachricht nicht mehr von ihm bekommen.

Eine Nachricht, die Sie senden, während der Run aktiv ist, ist eine Steuerung. Die Run-Schleife besitzt jeden Schreibvorgang zum Host, daher geht die Steuerung an die Schleife. Die Schleife speichert die Operation mit einer eigenen UUID und sendet sie. Claude liest die Nachricht nach seinem aktuellen Schritt, zwischen zwei Tool-Runden. Endet der Turn zuerst, startet die Nachricht den nächsten Turn desselben Runs. Der Outbox-Eintrag bleibt sending, mit einer Annahme, die den Run nennt, die Claude-Session-ID als Thread und die Claude-ID der Nachricht, die den Run gestartet hat, als Turn. Eine Steuerung, die keinen aktiven Run findet oder einen anderen Run als den genannten, wird aufgeschoben, und die Nachricht bleibt in der Warteschlange.

Stop and send now holt eine hinzugekommene Nachricht zurück. Die Schleife sendet cancel an den Host. Hat Claude die Nachricht noch nicht begonnen, stoppt der Desktop den Run und sendet dieselbe Operation als neuen Run. Hat Claude die Nachricht schon, wird nichts erneut gesendet.

Claudes Live-Stream wiederholt den Prompt nie, und ein Schreibvorgang auf stdin oder ein Delta ist nie ein Zustellnachweis. Claude speichert die Nachricht, die einen Run startet, bevor es das Modell aufruft. Deshalb startet der erste message-Frame nach send einen Transkript-Lesevorgang. Nach einem Lesevorgang ohne Fund startet jeder Frame, der die Nachricht nennt, einen weiteren. Ein Frame nennt eine Nachricht in user_message_uuid oder user_message_uuids, oder als command_lifecycle-Frame mit dem Zustand started oder completed. Der Thinking-Zähler und der idle-Frame starten keinen. Eine hinzugekommene Nachricht speichert Claude später, mit seiner nächsten Antwort. Deshalb startet der Frame, der sagt, dass Claude die Nachricht begonnen hat, einen Lesevorgang, und jede der nächsten 3 Antworten der Unterhaltung startet einen weiteren. Findet ein Lesevorgang eine Nachricht, erscheint zuerst die gespeicherte Zeile, und danach verschwindet die Outbox-Zeile. Am Ende des Runs ist ein weiterer Lesevorgang für jede Nachricht ohne Nachweis der endgültige Nachweis, auch für einen gestoppten oder fehlgeschlagenen Run. Eine Nachricht, die den Run gestartet hat und die kein Lesevorgang findet, wird nie automatisch erneut gesendet. Eine hinzugekommene Nachricht, die kein Lesevorgang findet, kehrt in die Warteschlange zurück.

**Eine Claude-Nachricht gilt als gesendet, sobald eine Prüfung sie im Transkript findet**

Zeiten aus einem gemessenen Run für die Nachricht, die den Run startet, mit einem Bruch in der Achse. Der Desktop sendet die Nachricht bei 0 ms, und Claude speichert die User-Zeile in seinem Transkript, bevor es das Modell aufruft. Der erste Live-Frame kommt nach etwa 10 ms an und startet einen messageSaved-Lesevorgang im Host des Runs. Dieser Lesevorgang findet die Zeile bei etwa 160 ms, also wird die Outbox-Zeile zur gespeicherten Zeile. Das message_start des Streams, das die uuid der Nachricht nennt, kommt erst bei etwa 1,5 Sekunden, gefolgt von den Deltas der Antwort. Beides zählt nicht als Nachweis. Eine Nachricht, die sich einem laufenden Turn anschließt, wird später gespeichert, mit Claudes nächster Antwort, daher beginnen ihre Lesevorgänge, wenn Claude die Nachricht beginnt.

- **Operations-uuid** (claude_operations): Jede Nachricht bekommt eine neue UUID, die der Host als uuid der SDK-Nutzernachricht sendet. Der Desktop speichert das Paar, und eine gespeicherte Nutzerzeile mit dieser uuid ist der einzige Nachweis.
- **Erste Prüfung** (erster message-Frame): Für die Nachricht, die einen Run startet, beginnt die Prüfung beim ersten Frame nach send. Claude speichert diese Nachricht, bevor es das Modell aufruft, daher findet diese Prüfung sie meist, bevor die Antwort beginnt.
- **Markierter Frame** (user_message_uuid · user_message_uuids): Ein Frame, der die uuid der Operation in user_message_uuid oder user_message_uuids nennt. Für sich beweist er nichts. Nach einer Prüfung ohne Fund startet ein solcher Frame eine weitere Prüfung. Der Thinking-Zähler wiederholt die uuid viele Male pro Sekunde und startet keine.
- **Endprüfung** (reconcile_operation): Läuft, nachdem der Host beendet ist, für abgeschlossene, gestoppte und fehlgeschlagene Runs gleichermaßen. Eine Nachricht, die den Run gestartet hat und die keine Prüfung findet, wird nie automatisch erneut gesendet. Eine hinzugekommene Nachricht, die keine Prüfung findet, kehrt in die Warteschlange zurück.

| Zugriffsstufe | Claude-Berechtigungsmodus |
| --- | --- |
| read-only | plan |
| full-auto | acceptEdits |
| full-access | auto. open schlägt fehl, wenn das Modell den Auto-Modus nicht unterstützt. |
| unrestricted | bypassPermissions |

Wenn Claude ein Tool verwenden will, hält der Host das canUseTool-Promise des SDK und sendet ein permission-Event. Der Desktop zeigt dieselbe Freigabekarte wie für Codex, mit Accept und Decline und keiner anderen Entscheidung. Ein Bash-Aufruf ist eine Befehlsfreigabe, und jedes andere Tool ist eine Berechtigungsfreigabe. Ein Telefon antwortet über run.chatResolveApproval, und der Dispatch lässt den Claude-Adapter zuerst antworten, wenn er diese Anfrage-id hält.

**Claude wartet in canUseTool, bis der Desktop oder ein Telefon antwortet**

Vier Spuren, nicht maßstäblich. Claude will einen Befehl ausführen, und das SDK ruft canUseTool auf. Der Host hält dieses Promise offen und sendet ein permission-Event, daher zeigt der Desktop eine Freigabekarte, und ein Telefon, das die Session beobachtet, zeigt dieselbe Karte. Das Telefon antwortet mit run.chatResolveApproval. Der Desktop sendet dem Host eine permission-Anfrage mit allow, der Host löst das Promise auf, und Claude führt den Befehl aus. Danach zeigen beide Karten die Antwort.

- **canUseTool** (approvals.ts): Das SDK fragt vor jeder Tool-Nutzung, die der Berechtigungsmodus nicht von sich aus erlaubt. Der Host hält das Promise offen, und Claude wartet.
- **Freigabekarte** (permission { requestId, toolUseId, agentId }): Der Desktop zeigt dieselbe Karte wie für Codex, und Telefone bekommen sie als Live-Event. Ein Bash-Aufruf wird zur Befehlsfreigabe, und die Anfrage eines Subagents nennt den Subagent.
- **Antwort** (run.chatResolveApproval): Telefon und Desktop nehmen denselben Dispatch-Weg. Der Claude-Adapter prüft Thread, Turn und Item und sendet dem Host dann eine permission-Anfrage mit allow oder deny.
- **Stop** (interrupt): Der Host lehnt jede offene Anfrage mit „Run stopped.“ ab, und die Karten enden als Abgebrochen, wenn der Run endet. Eine Frage (AskUserQuestion) öffnet nie eine Karte: Der Host beantwortet sie sofort, und der Nutzer antwortet in einer späteren Nachricht.

- Begründungstext: Für ExitPlanMode zeigt die Karte „Claude wants to carry out this plan:“ und den Plan. Für Bash zeigt sie Claudes eigene Beschreibung des Befehls. Für ein anderes Tool zeigt sie die Beschreibung, sonst den Tool-Namen mit seinem Dateipfad oder seiner URL, sonst den Tool-Namen mit seiner Eingabe als JSON.
- Subagents: Die Anfrage eines Subagents beginnt mit „Requested by“ und dem Namen, den die Agentenliste zeigt.
- Antwort: Der Adapter prüft den Thread, den Turn, das Item und die Entscheidung. Wenn kein Run die Anfrage mehr hält, schlägt die Antwort mit „The Claude run has ended.“ fehl.
- Ablehnung: Claude bekommt „The user declined this action.“, und die Tool-Zeile wird zu Tool declined.
- Stop: Der Host lehnt jede offene Anfrage ab und stoppt jeden offenen HB-Code-Tool-Aufruf mit „Run stopped.“ Das Ende des Runs markiert die übrigen Karten als abgebrochen.
- Fragen: Eine Frage hält den Run nie an. Ein PreToolUse-Hook im Host beantwortet AskUserQuestion sofort, in jedem Berechtigungsmodus, und Claude bekommt den Hinweis, dass die Frage angezeigt wird und die Antwort als spätere Nachricht kommt. Die Timeline zeigt den Aufruf als Zeile „Question for you“, ihre Optionen legen eine Antwort in den Composer, und Telefone bekommen eine Benachrichtigung. Es öffnet sich keine Karte.
- Duplikate: Der Host lehnt eine zweite Anfrage mit einer Anfrage-id ab, die noch offen ist.

- [Die Run-Schleife, Fehler und Stop](https://www.hbcode.ai/de/docs/claude-agent-sdk.md#run-loop): Worauf der Run nach send wartet, in welcher Reihenfolge er endet und wo ein Fehler landet.

## Die Run-Schleife, Fehler und Stop

Nach send gehört der Run einer einzigen Schleife. Sie wartet gleichzeitig auf sieben Eingänge und behandelt den, der zuerst bereit ist.

| Eingabe | Was die Schleife tut |
| --- | --- |
| Ein Tool-Task ist fertig | Vergisst den Task. Ein Task-Fehler beendet die Schleife als Fehler. |
| Das Stoppsignal | Schließt den Event-Kanal, sendet interrupt an den Host und verlässt die Schleife als abgebrochen. Der interrupt verwirft die Nachrichten, die noch auf Claude warten, und das Beenden gibt sie in die Warteschlange zurück. |
| Der Heartbeat, alle 2 Sekunden | Ruft touch_chat_run auf, das der Run-Registry meldet, dass der Run lebt. Er verlässt die Schleife außerdem als abgebrochen, wenn der Run als abgebrochen markiert ist. |
| Eine Zustellprüfung ist abgeschlossen | Wenn die Prüfung die Nachricht gefunden hat, fügt sie die gespeicherte User-Zeile hinzu, veröffentlicht die Zeilen und entfernt dann die Outbox-Zeile. |
| Eine Anfrage für den laufenden Turn | Eine Steuerung oder eine Zurücknahme aus einem anderen Task. Die Schleife sendet die Steuerung mit send an den Host oder bittet den Host mit cancel, eine wartende Nachricht zurückzunehmen, und antwortet dem Task. |
| Das Warten nach einer Ruhe, 10 Sekunden | Läuft nur, nachdem Claude geruht hat, während eine hinzugekommene Nachricht noch wartete. Kommt in dieser Zeit kein Frame, endet die Schleife. |
| Ein Host-Event | toolCall startet einen Tool-Task, und toolCancel bricht einen ab. permission und permissionResolved aktualisieren die Freigabekarten. Eine message geht an den Zustellnachweis und die Projektion. failed, closed und das Ende des Event-Kanals beenden die Schleife als Fehler. |

Zwei message-Frames werden besonders behandelt. Der result-Frame hält den Erfolg oder den Fehler des Turns fest, und die Schleife läuft weiter. Der Zustand idle beendet die Schleife, mit einer Ausnahme. Claude kann melden, dass es ruht, kurz bevor es eine Nachricht liest, die in diesem Moment angekommen ist, und startet dann einen Turn für diese Nachricht. Wartet also noch eine hinzugekommene Nachricht auf Claude und hat der Run keinen Fehler, läuft die Schleife weiter und wartet 10 Sekunden auf den nächsten Frame.

**Eine Schleife wartet auf sieben Eingänge, und send trennt die Fehler**

Sieben Bahnen über einen Run, nicht maßstäblich, und ein Streifen mit der Beenden-Reihenfolge. Die Run-Bahn zeigt spawn, open und den Eintrag, dann send, dann die Select-Schleife, dann das Beenden. Jeder Fehler vor send ist ProvenUnadmitted. Ab send entscheidet das gespeicherte Transkript. Sechs Bahnen zeigen Eingänge der Schleife: Host-Events, die mit dem Zustand idle enden, einen Heartbeat alle 2 Sekunden, einen Tool-Task, der fertig wird, eine Zustellprüfung, die die Nachricht findet, eine Nachricht, die in den laufenden Turn kommt, und das Stoppsignal, das in diesem Run nicht benutzt wird. Der siebte Eingang, das Warten von 10 Sekunden nach einer Ruhe, hat keine Bahn. Der Streifen darunter listet die sieben Schritte des Beendens in ihrer festen Reihenfolge.

- **Vor send** (ProvenUnadmitted): Spawn, open, die Prüfung auf den Auto-Modus, ein zweiter Run, ein Konflikt beim Eintrag oder ein Stop vor send. Claude hat die Nachricht nicht bekommen.
- **Ab send** (AdmissionUncertain · reconcile_operation): Claude kann die Nachricht haben. Die Endprüfung entscheidet: Eine gespeicherte Nachricht gilt als gesendet, auch nach einem Fehler. Eine Nachricht, die keine Prüfung findet, wird nie automatisch erneut gesendet.
- **Select-Schleife** (tokio::select! · touch_chat_run): Sieben Eingänge: ein fertiger Tool-Task, das Stoppsignal, der Heartbeat alle 2 Sekunden, eine abgeschlossene Zustellprüfung, eine Anfrage für den laufenden Turn, das Warten von 10 Sekunden nach einer Ruhe und ein Host-Event. Der Zustand idle beendet die Schleife, außer wenn noch eine hinzugekommene Nachricht auf Claude wartet. Der result-Frame tut das nicht.
- **Beenden-Reihenfolge** (turn.rs · execute): Gleich nach idle, Stop und einem Fehler: Tool-Tasks, Host, Freigabekarten, Subagent-Ansichten, Endprüfungen, Endereignis und die Rückgabe der hinzugekommenen Nachrichten, die Claude nicht übernommen hat.

Die Reihenfolge beim Beenden ist fest, egal was die Schleife beendet hat.

1. Der Desktop schließt den Event-Kanal und stoppt jeden Tool-Task, der noch übrig ist.
2. Er fährt den Host herunter: close, 3 Sekunden, stdin geschlossen, 10 Sekunden, Kill.
3. Er markiert jede offene Freigabekarte als abgebrochen.
4. Er schließt jede noch offene Subagent-Ansicht ab: als abgebrochen nach einem Stop oder einem Fehler, sonst als abgeschlossen.
5. Er führt die Endprüfungen aus: zuerst für jede hinzugekommene Nachricht ohne Nachweis, dann für die Nachricht, die den Run gestartet hat.
6. Er sendet das Endereignis des Runs: abgebrochen, fehlgeschlagen mit dem Grund oder abgeschlossen.
7. Er gibt jede hinzugekommene Nachricht, die das Transkript nicht hat, in die Warteschlange zurück.

Dann verwirft der Desktop die Nutzungsmessung des Kontos, beendet den Run in der Run-Registry und entfernt ihn aus RUNS. Die Grenze bei send entscheidet, was ein Fehler bedeutet. ProvenUnadmitted sagt, dass Claude die Nachricht nicht bekommen hat. AdmissionUncertain sagt, dass Claude sie haben kann, daher muss vor einem erneuten Versuch der gespeicherte Verlauf entscheiden.

| Wann | Fehler und Ergebnis |
| --- | --- |
| Eine Steuerung | Kein Run ist aktiv, oder der aktive Run ist nicht der Run, den die Steuerung nennt. Ergebnis: Deferred. Die Nachricht bleibt in der Warteschlange. |
| Eine Steuerung | Die send-Anfrage an den Host schlägt fehl, oder der Task des Runs stoppt, während er Claude die Nachricht gibt. Ergebnis: AdmissionUncertain. |
| Vor send | Kein Kontoordner, eine nicht paketierte Runtime, ein Spawn-Fehler, ein open-Fehler wie ein Modell ohne Auto-Modus, ein zweiter Run für die Session, ein Kind-Thread als Ziel oder ein Konflikt beim Eintrag. Ergebnis: ProvenUnadmitted. |
| Vor send | Stop trifft nach open ein. Ergebnis: ProvenUnadmitted: „The run was stopped before sending.“ |
| Vor send | Die Operation hat einen früheren Versuch aus einem anderen Run, und das Lesen des Transkripts dafür schlägt fehl. Ergebnis: AdmissionUncertain. |
| Bei send | Die send-Anfrage schlägt fehl oder bekommt in 35 Sekunden keine Antwort. Ergebnis: AdmissionUncertain. |
| In der Schleife | Ein failed-Event, „Claude closed before the run completed.“, „The Claude host disconnected.“, ein Fehler eines Tool-Tasks oder ein result mit is_error. Ergebnis: Die Beenden-Reihenfolge läuft, und die Endprüfung entscheidet. Wenn die Nachricht gespeichert ist, gilt sie als gesendet, und der Run endet mit einem failed-Event, das den Grund trägt. |
| Am Ende | Die Endprüfung findet die Nachricht nicht, die den Run gestartet hat. Ergebnis: AdmissionUncertain: „Claude has not confirmed this message in its saved history. It will not be sent again automatically.“ |
| Am Ende | Die Endprüfung findet eine hinzugekommene Nachricht nicht. Ergebnis: Das Register markiert die Operation als canceled, und der Eintrag kehrt mit abgeschaltetem Auto-Drain und dem Text „The active run stopped before this message reached the agent. Review and send it again.“ in die Warteschlange zurück. |
| Am Ende | Das Herunterfahren des Hosts oder die Endprüfung schlägt fehl. Ergebnis: AdmissionUncertain. |

Der Grund eines fehlgeschlagenen Ergebnisses stammt aus Claudes errors-Liste oder, bei API-Fehlern wie einer fehlenden Anmeldung, aus seinem result-Text. Ein fehlgeschlagener Run behält seinen Grund im Fehlerprotokoll des Desktops unter claude_run, mit den ids von Session, Run und Operation. Paketierte Apps verwerfen die Standardfehlerausgabe, daher ginge die Logzeile allein verloren.

Ein Abbruch antwortet nur für die genaue Run-id. Er setzt das Stop-Flag und wartet, bis der Run meldet, dass er fertig ist, was erst nach dem Ende des Hosts geschieht. Wenn der Task des Runs gestorben ist und nichts mehr melden kann, fährt der Abbruch selbst diesen Host herunter, beendet den Run und entfernt ihn aus RUNS.

Die gemeinsame Run-Registry holt einen Run als veraltet zurück, wenn er 45 Sekunden lang keine Aktivität zeigt. Ein Claude-Host kann länger brauchen, um eine Unterhaltung zu öffnen, und der erste Heartbeat kommt erst aus der Schleife. Deshalb fragt die Registry zuerst den Claude-Adapter, und ein Run, dessen genauer Host-Prozess lebt, wird nie zurückgeholt.

- [Die Run-Registry](https://www.hbcode.ai/de/docs/message-delivery.md#runs): Die gemeinsame Registry, an die die Runs beider Agenten melden.
- [Ungewisse Zustellung](https://www.hbcode.ai/de/docs/message-delivery.md#uncertain-delivery): Was die Outbox mit einer Nachricht tut, deren Zustellung nicht nachgewiesen ist.

## Subagents als Kind-Threads

Claude-Subagents verwenden dieselben Listen-, Timeline- und Live-Pfade wie Codex-Kind-Threads. Während eines Runs meldet Claude jeden Subagent über Task-Events und leitet seine Turns mit der id des startenden Tool-Aufrufs weiter. Nur ein task_started-Event mit dem Task-Typ local_agent erzeugt einen Subagent. Der Adapter schickt die weitergeleiteten Turns in die eigene Ansicht des Subagents. Der Aufruf, der den Subagent gestartet hat, und sein Ergebnis verlassen die Ansicht des Elternteils, daher steht keine Zeile für den Subagent. Ein Subagent, den die Unterhaltung gestartet hat, hat die Tiefe 1, und jede Verschachtelung addiert 1. Ein Live-Subagent trägt keine Eltern-id, daher ist die Tiefe eines verschachtelten Subagents nur aus seinem gespeicherten Transkript bekannt.

| Claude-Task-Status | Thread-Status |
| --- | --- |
| pending | PendingInit |
| running, paused | Running |
| completed | Completed |
| failed | Errored |
| killed, stopped | Interrupted |

Ein Workflow ist ein Skript, das die Unterhaltung mit einem Aufruf des Tools Workflow startet, und das Skript startet Subagents in Phasen. Der laufende Turn verfolgt einen Workflow als einen Task: ein task_started-Event mit dem Task-Typ local_workflow, das die ID des startenden Tool-Aufrufs, den Workflow-Namen und eine Zusammenfassung liefert. Die späteren Task-Events dieses Tasks ändern seinen Status, wie die Tabelle oben zeigt. Claude leitet die Turns der Subagents eines Workflows nicht weiter, und kein Task-Event nennt sie. Sie erscheinen in der Liste nur durch ihre gespeicherten Dateien.

**Subagents im Hintergrund halten einen Run nach dem result des Turns am Laufen**

Vier Spuren, nicht maßstäblich. Der Haupt-Turn startet die Subagents A und B im Hintergrund und endet mit einem result. Der Run läuft weiter. Wenn A fertig ist, beantwortet Claude dessen Bericht mit einem kurzen Folge-Turn, und später ebenso für B. Erst dann wechselt die Session in den Zustand idle, was den Run beendet und den Host herunterfährt. Während des Runs gehen die Turns jedes Subagents in dessen eigene Ansicht. Danach sind die gespeicherten Subagent-Transkripte und die Dateien daneben die einzige Quelle.

- **Turn-Ergebnis** (result): Hält Erfolg oder Fehler fest, sonst nichts. Subagents im Hintergrund laufen weiter, und Claude beantwortet jeden fertigen Bericht mit einem Folge-Turn.
- **Idle** (CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS): Vom Host auf der Session-Query gesetzt. Nur der Zustand idle beendet den Run. Dann fährt der Desktop den Host herunter und führt die Endprüfung der Zustellung aus.
- **Subagent-Ansicht** (parent_tool_use_id): Während des Runs gehen die Turns jedes Subagents in seine eigene Ansicht, adressiert wie ein Codex-Kind-Thread. Der Parent erhält eine Spawn-Zeile, die darauf verlinkt. Die Agentenliste nutzt die Zeile, und die Chat-Timeline zeigt sie nicht.
- **Nach dem Run** (getSubagentMessages): Die gespeicherten Transkripte und die Dateien daneben sind die einzige Quelle. Eine Ansicht ohne abgeschlossenes Task-Event wird geschlossen: nach einem Stop oder Fehler als abgebrochen, sonst als abgeschlossen.

Die gespeicherten Dateien sind die Quelle für jeden Subagent, den der laufende Turn nicht meldet, und nach dem Run sind sie die einzige Quelle. Das SDK liefert die Liste der Subagent-IDs und jedes Transkript. Der Host liest außerdem selbst drei Arten von Claude-Dateien, weil das SDK keine davon bereitstellt.

| Datei | Pfad im Session-Ordner und Inhalt |
| --- | --- |
| Begleitdatei | agent-<id>.meta.json neben jedem Transkript, in subagents, oder in subagents/workflows/<runId> für einen Workflow-Subagent. Sie liefert description, agentType und workflowPhase. Der Ordnername liefert die Run-ID. |
| Run-Datensatz | workflows/<runId>.json, das Claude schreibt, wenn ein Workflow-Run endet: workflowName, summary, status, startTime, durationMs, die Phasen und für jeden Subagent, wie er endete, und der Anfang seiner Aufgabe. |
| Transkript | agent-<id>.jsonl selbst: das Feld effort der ersten gespeicherten Assistant-Zeile und die Dateigröße in Bytes. |

Claude benennt einen Projektordner nach einer eigenen Regel aus dem Arbeitsverzeichnis. Deshalb sucht der Host in jedem Ordner unter projects in CLAUDE_CONFIG_DIR, oder unter ~/.claude/projects, nach einem Ordner mit der Session-ID, der einen Ordner subagents mit Einträgen hat. Jeder Leser liefert nichts, wenn eine Datei fehlt oder eine andere Form hat, und die Liste zeigt dann weniger Angaben.

Eine subagents-Anfrage liest all das in einem Einmal-Host. Das Lesen eines Transkripts kostet die ganze Datei, daher nennt der Desktop, was er schon hält, und der Host liest nur den Rest. Der Desktop behält diese Angaben pro Unterhaltung, bis er beendet wird.

| Feld | Inhalt |
| --- | --- |
| Anfrage: known | Jeder beendete Subagent, den der Desktop hält, als agentId und Größe seines Transkripts bei diesem Lesen. Der Host liest ihn nur dann erneut, wenn die Größe abweicht, was bedeutet, dass sein Eltern-Agent ihn fortgesetzt hat. |
| Anfrage: endedRuns | Die IDs der Workflow-Runs mit Datensatz. Sie ändern sich nicht mehr, daher überspringt der Host sie. |
| Anfrage: openRuns | Die IDs der Runs ohne Datensatz, die der Desktop hält. Der Host liest die Hauptunterhaltung, die größte Datei, nur für einen Run, der in keiner der beiden Listen steht, oder für einen Subagent ohne description in der Begleitdatei. Dort findet er den Workflow-Aufruf des Runs und die Eingabe jedes Spawn-Aufrufs. |
| Ergebnis: ids | Die ID jedes Subagents der Unterhaltung. |
| Ergebnis: subagents | Die Angaben zu jedem Subagent, den der Host gelesen hat. |
| Ergebnis: runIds | Die ID jedes Workflow-Runs, aus den Ordnernamen unter subagents/workflows. |
| Ergebnis: workflows | Für jeden Run, der nicht in endedRuns steht: runId, toolUseId, name, summary, phases, outcome, startedAt und endedAt. |

| Subagent-Feld | Quelle |
| --- | --- |
| Identität | agentId und aus der ersten Transkript-Nachricht toolUseId, der startende Tool-Aufruf, und parentAgentId, der Eltern-Subagent. |
| Name und Art | description und subagentType: aus der Begleitdatei, sonst aus der Eingabe des startenden Tool-Aufrufs. |
| task | Die erste Benutzernachricht ohne ihren Workflow-Rahmen, sonst der Anfang der Aufgabe im Datensatz des Runs. |
| model | Das Modell der letzten Assistant-Nachricht, die kein API-Fehler ist. |
| effort | Das Feld effort der ersten gespeicherten Assistant-Zeile. |
| tokens | Die Größe des Kontexts beim letzten Assistant-Turn: input_tokens plus cache_creation_input_tokens plus cache_read_input_tokens. |
| toolCalls | Die Zahl der tool_use-Blöcke in allen Assistant-Nachrichten. |
| runId, phase | Der Workflow-Run aus dem Ordnernamen und workflowPhase aus der Begleitdatei. |
| startedAt, lastActivity | Die Zeitstempel der ersten und der letzten Transkript-Nachricht. |
| size | Die Größe der Transkript-Datei in Bytes. |
| outcome | completed, failed, stopped oder null, solange sich der Subagent noch ändern kann. |

Der Host bestimmt outcome in dieser Reihenfolge. Der Datensatz des Runs ergibt completed für den Zustand done und failed für den Zustand error. Sonst ist eine letzte Nachricht mit dem Modell <synthetic> ein API-Fehler und bedeutet failed. Sonst bedeutet eine letzte Assistant-Nachricht mit stop_reason end_turn, mit Text und ohne Tool-Aufruf completed. Text, der im selben Turn vor einem Tool-Aufruf steht, beendet den Subagent nicht. Sonst gilt ein Subagent eines Runs mit Datensatz als stopped, weil der Run vor dem Subagent endete. Jeder andere Subagent hat outcome null.

Ein Workflow-Run nimmt Name, Zusammenfassung, Phasen, Status, Start und Dauer aus seinem Datensatz. Ein Run ohne Datensatz ist noch offen. Dann verbindet die Zeile „Run ID:“ im Ergebnis seines Workflow-Aufrufs den Run mit diesem Aufruf, und die Zeile „Summary:“ liefert seine Zusammenfassung. Sein Name und die Titel seiner Phasen kommen aus der meta-Anweisung seines Skripts.

Ein Workflow gibt jedem Subagent seine Aufgabe in einem Rahmen. Ein Rahmen ist eine Zeile, die mit „[Workflow harness — user request]“ oder „[Workflow harness — computed task]“ beginnt, und der Text folgt mit zwei Leerzeichen Einzug in jeder Zeile. Der Host entfernt die Rahmenzeile und den Einzug für das Feld task. subagentHistory liefert die berechnete Aufgabe ohne ihren Rahmen und lässt die gerahmte Benutzeranfrage weg, weil die Unterhaltung diese Anfrage schon zeigt.

Der Desktop gibt dann jedem Subagent einen Status. Die erste Regel, die zutrifft, gilt.

1. Der laufende Turn dieser Session meldet den Subagent als Task: der Status dieses Tasks.
2. Die Dateien liefern ein outcome: completed wird Completed, failed wird Errored, und stopped wird Interrupted.
3. Der Subagent gehört zu einem Workflow, den der laufende Turn als laufend meldet: Running.
4. Die letzte Transkript-Nachricht ist weniger als 10 Minuten alt, und sie ist später als das Ende des letzten Runs dieser Session in diesem Desktop-Lauf: Running. Eine andere Claude-App kann dieselbe Unterhaltung ausführen, daher ist neue Aktivität das einzige Zeichen.
5. Sonst: Interrupted.

Die 10 Minuten kommen von Claudes längster normaler Stille, einem Shell-Befehl, den Claude nach höchstens 10 Minuten stoppt. Ein Workflow-Run nimmt das outcome seines Datensatzes, sonst den Status seines Tasks im laufenden Turn, sonst Regel 4 mit der neuesten Aktivität seiner Subagents. Ein Subagent, den der Turn meldet und der noch kein Transkript hat, kommt aus seinem Task-Event in die Liste. Subagents, die am Ende eines Runs noch offen sind, enden mit ihm, daher zählt Aktivität vor diesem Ende nicht.

Das Listenergebnis nutzt den Agent-Thread-Vertrag, den auch Codex-Kind-Threads nutzen. Claude füllt Felder, die Codex leer lässt.

| Feld | Inhalt des AgentThread-Felds für einen Claude-Subagent |
| --- | --- |
| Workflow | workflowId und phase: der Workflow-Run und die Phase des Subagents. Fehlt bei einem Subagent, den die Unterhaltung selbst gestartet hat. |
| usage | tokens, das fehlen kann, und toolCalls. Das Objekt fehlt bei einem Subagent ohne gespeicherten Turn. |
| reasoning | Der Effort. |
| agentRole | Der Subagent-Typ. Claudes Standardarten general-purpose und workflow-subagent werden nicht gesendet. |
| title, task | Der Titel ist die Beschreibung, sonst die erste nicht leere Zeile der Aufgabe, sonst „Subagent“. Die Aufgabe in der Liste wird nach 240 Zeichen abgeschnitten, und die eigene Ansicht des Subagents hat die ganze Aufgabe. |
| Eltern und Tiefe | parentThreadId ist der Eltern-Subagent, sonst die ID der Unterhaltung. Die Tiefe folgt den gespeicherten Eltern. |
| canSteer | Immer false. Nur der Eltern-Agent schreibt an einen Subagent, und ein Senden an einen Kind-Thread schlägt fehl, bevor der Host die Unterhaltung öffnet. |
| Workflow-Liste | workflows im Listenergebnis, ein Eintrag pro Run: workflowId, name, summary, phases, status, startedAt und endedAt. Der Name ist „Workflow“, wenn keine Datei den Run benennt. Ein offener Run nimmt startedAt von seinem ersten Subagent und hat kein endedAt. |

Keine Timeline-Zeile steht für einen Subagent oder einen Workflow. Der Aufruf, der einen startet, und sein Ergebnis verlassen die Ansicht, und die Agentenliste zeigt die Subagents und die Workflow-Runs. Ein Task-Event, das die Liste ändert, und das Ende eines Runs mit offenen Subagents senden das Signal agent-threads. Jeder Client lädt die Liste dann neu. Auf dem Desktop gruppiert die Agentenliste Workflow-Subagents unter ihrem Workflow und ihrer Phase und zeigt Modell, Effort, Tokens, Tool-Aufrufe und Dauer.

## Konten und Nutzung

Das Standardkonto ist die Claude-Anmeldung dieses Computers und läuft mit Claudes eigenem Ordner. Dieser Ordner ist ~/.claude oder der Ordner im eigenen CLAUDE_CONFIG_DIR des Desktops. Jedes hinzugefügte Konto ist ein Ordner claude/accounts/<uuid> in den lokalen Anwendungsdaten von HB Code. Es behält seine eigene Anmeldung und verlinkt jeden anderen Eintrag auf den Hauptordner. Alle Konten lesen und fortsetzen daher dieselben Sessions mit denselben Einstellungen, und ein Wechsel ändert nur, wer für einen Turn bezahlt.

**Jedes Claude-Konto sieht dieselben Sessions, nur die Anmeldung gehört ihm allein**

Zwei Ordner nebeneinander. Links der Ordner der eigenen Anmeldung dieses Computers: ~/.claude oder der Ordner im eigenen CLAUDE_CONFIG_DIR des Desktops. Rechts der Ordner eines Kontos, das in HB Code hinzugefügt wurde. Seine Unterhaltungen, Einstellungen und jeder andere gemeinsame Eintrag sind symbolische Links in diesen Hauptordner, sodass beide Konten dieselben Sessions lesen und fortsetzen. Nur die Kontodateien bleiben getrennt: .claude.json, die Zugangsdaten, backups und statsig. Der nächste Run startet mit dem Ordner des aktiven Kontos.

- **Standardkonto** (~/.claude · CLAUDE_CONFIG_DIR): Die eigene Claude-Anmeldung dieses Computers. Ihr Ordner ist ~/.claude oder der Ordner im eigenen CLAUDE_CONFIG_DIR des Desktops. HB Code setzt dafür keinen Ordner.
- **Weiteres Konto** (claude/accounts/<uuid>): Ein Ordner in den Anwendungsdaten von HB Code. HB Code führt claude auth login --claudeai mit CLAUDE_CONFIG_DIR auf diesen Ordner aus, und Claude behält die Anmeldedaten. Unter macOS gehört der Schlüsselbund-Eintrag zum Ordner.
- **Links** (folders.rs): Jeder Eintrag der obersten Ebene außer .claude.json, seiner Sperrdatei, .credentials.json, backups und statsig verlinkt auf den Hauptordner. Einen Eintrag, den der Kontoordner schon hat, überschreibt HB Code nie.
- **Nutzung** (five_hour · seven_day): Pro Konto über das SDK gelesen: das Fünf-Stunden-Fenster, das Wochenfenster und Wochenfenster pro Modell. Ein Messwert pro Konto und Minute versorgt Seitenleiste, Einstellungen und Telefone.

Die Einstellung agent.chat.claude_account_id nennt das aktive Konto, und kein Wert bedeutet das Standardkonto. Der nächste Turn läuft mit dem aktiven Konto, und ein entferntes Konto lässt das Standardkonto aktiv. Der Desktop legt die Links vor jedem Start eines Claude-Prozesses an. Er erstellt projects zuerst im Hauptordner, damit ein hinzugefügtes Konto nie einen eigenen Transkriptspeicher beginnt. Ein projects-Link, der fehlschlägt, stoppt den Start. Jeder andere Link, der fehlschlägt, wird nur protokolliert. Unter macOS ist jeder Link ein symbolischer Link. Unter Windows bekommt ein Ordner eine Verzeichnisverbindung (Junction), und eine Datei bekommt einen symbolischen Link oder einen Hardlink, wenn Windows den symbolischen Link verweigert. Der Entwicklermodus ist dafür nicht nötig.

HB Code führt Claudes eigene Anmeldebefehle für einen Kontoordner aus. Es liest nie ein Token und führt keinen eigenen Autorisierungsablauf aus.

| Befehl | Frist und Hinweise |
| --- | --- |
| claude auth status --json | Frist: 15 Sekunden. Claude endet mit einem Fehlercode, wenn es abgemeldet ist, und gibt trotzdem dasselbe JSON aus. |
| claude auth login --claudeai | Frist: 600 Sekunden. Es läuft jeweils eine Anmeldung, und Sie können sie abbrechen. Eine zweite Anmeldung bei einem Konto, das schon auf diesem Computer ist, wird wieder abgemeldet und entfernt. |
| claude auth logout | Frist: 15 Sekunden. Das Entfernen meldet das Konto ab und löscht dann seinen Ordner. Es wird abgelehnt, solange ein Run das Konto verwendet. |

Die Nutzung kommt aus dem SDK: das rollierende Fünf-Stunden-Fenster und die Wochenfenster. Das Wochenfenster für Drittanbieter-Apps bleibt außen vor. Ein Konto mit API-Schlüssel hat keine Plan-Fenster. Der Host für usage lädt keine Einstellungen und überspringt den Scan der lokalen Transkripte. Die Nutzung wird nur für ein angemeldetes Konto gelesen, und ein Lesevorgang, der fehlschlägt, erscheint als Fehlertext an diesem Konto, während die Liste trotzdem lädt. Bei den Wochenfenstern pro Modell kommen die eigenen Labels des Servers zuerst, und die festen Felder für Opus und Sonnet ergänzen ein Modell, das er nicht aufgelistet hat. Reset-Zeiten werden auf die nächste Minute gerundet, und jede Messung geht in den Nutzungsverlauf. Die Seitenleiste, die Einstellungsseite und die Telefone teilen sich eine Messung pro Konto und Minute, und ein beendeter Run verwirft die Messung dieses Kontos.
