Nachrichtentypen und convertToLlm
messages.ts fügt dem AgentMessage von pi-agent-core vier eigene Nachrichtentypen hinzu: bashExecution, custom, branchSummary, compactionSummary. Der darunterliegende Agent kennt nur user/assistant/toolResult, convertToLlm übersetzt diese vier eigenen Typen in Message[] und filtert bash-Ausführungsergebnisse mit excludeFromContext heraus. Die Erweiterung erfolgt über das CustomAgentMessages-Interface per declaration merging, auf Typ- und auf Runtime-Ebene ausgerichtet.
Verantwortung
- Typerweiterung: Mit
declare modulewerden demCustomAgentMessages-Interface vier Rollen hinzugefügt, siehepackages/coding-agent/src/core/messages.ts:69-77. - bash-Ausführungsnachricht: Das Ergebnis eines
!-Befehls mitcommand,output,exitCode,cancelled,truncated,fullOutputPath, optionalesexcludeFromContext(für!!-Präfix). Siehepackages/coding-agent/src/core/messages.ts:29-40. - custom-Nachricht: Von einer Extension über
sendMessageinjizierte Nachricht,customTypeunterscheidet den Typ,displaysteuert, ob sie in der UI angezeigt wird,detailsist eine optionale strukturierte Datenform. Siehepackages/coding-agent/src/core/messages.ts:46-53. - branch / compaction summary: Branch-Zusammenfassung beim Zurückkehren auf den Hauptstrang, Kompressions-Zusammenfassung nach der Kompression, beide mit
<summary>-Tag gewrappt, siehepackages/coding-agent/src/core/messages.ts:55-67. - Fabrikfunktionen:
createBranchSummaryMessage/createCompactionSummaryMessage/createCustomMessagewandeln String-timestamp in numerische Millisekunden um. Siehepackages/coding-agent/src/core/messages.ts:100-138. - convertToLlm: Wandelt eigene Nachrichten in
Message[]um, bash wird überbashExecutionToTextzu einer user-Nachricht, branch/compaction summary werden in<summary>-Tags gewrappt, bash mitexcludeFromContextwird direkt mit undefined herausgefiltert. Siehepackages/coding-agent/src/core/messages.ts:148-195.
Entwurfsmotivation
Warum nicht einfach user-Nachrichten wiederverwenden? Weil die UI-Schicht zwischen "das ist bash-Ausgabe" und "das ist Nutzereingabe" unterscheiden muss - Render-Stil, Einklappbarkeit, erneutes Senden sind unterschiedlich. Der LLM braucht aber nur den Text zu sehen - convertToLlm klappt vor dem Senden an den Provider alle eigenen Typen zu user-Nachrichten flach, das Modell weiß gar nicht, dass es custom-Typen gibt. So bleibt die semantische Reichhaltigkeit für die UI erhalten, ohne den Modellkontext zu verschmutzen.
bashExecutionToText formatiert bash-Ausgabe als Markdown-Codeblock, mit Ran \command`-Präfix und Exit-Code-Suffix, damit der LLM das gleiche Format sieht wie der Nutzer in der UI. bash mit !!-Präfix wird über excludeFromContext: true` komplett aus dem LLM-Kontext entfernt - das ist die aktive Nutzerwahl "diesen Schritt soll das Modell nicht sehen", üblich bei sensiblen Operationen oder lautem Output.
branchSummary und compactionSummary werden mit <summary>-XML-Tags gewrappt, das ist das von Anthropic empfohlene Format, das Modell behandelt den Inhalt eher als geschlossene Einheit statt als verteilte Absätze.
Wichtige Dateien
packages/coding-agent/src/core/messages.ts:11-24— KonstantenCOMPACTION_SUMMARY_PREFIX/SUFFIXundBRANCH_SUMMARY_PREFIX/SUFFIX.packages/coding-agent/src/core/messages.ts:29-40—BashExecutionMessage-Interface.packages/coding-agent/src/core/messages.ts:46-53—CustomMessage<T>-Interface.packages/coding-agent/src/core/messages.ts:55-67—BranchSummaryMessageundCompactionSummaryMessage, letzterer speichert auchtokensBeforefür Diagnose.packages/coding-agent/src/core/messages.ts:69-77—declare moduledeclaration merging, erweitertCustomAgentMessages.packages/coding-agent/src/core/messages.ts:82-98—bashExecutionToText, formatiert bash-Ausgabe zu LLM-freundlichem Text.packages/coding-agent/src/core/messages.ts:100-138— Drei create-Fabrikfunktionen.packages/coding-agent/src/core/messages.ts:148-195—convertToLlm, switch + filter-Muster.
convertToLlm nutzt switch + never für erschöpfende Prüfung, ein vergessener neuer Typ führt zu einem Compile-Fehler:
// packages/coding-agent/src/core/messages.ts:148-195
export function convertToLlm(messages: AgentMessage[]): Message[] {
return messages
.map((m): Message | undefined => {
switch (m.role) {
case "bashExecution":
if (m.excludeFromContext) {
return undefined;
}
return {
role: "user",
content: [{ type: "text", text: bashExecutionToText(m) }],
timestamp: m.timestamp,
};
case "custom": {
const content = typeof m.content === "string" ? [{ type: "text" as const, text: m.content }] : m.content;
return { role: "user", content, timestamp: m.timestamp };
}
// ... branchSummary / compactionSummary ...
case "user":
case "assistant":
case "toolResult":
return m;
default:
const _exhaustiveCheck: never = m;
return undefined;
}
})
.filter((m) => m !== undefined);
}bashExecutionToText wickelt das Ausführungsergebnis in einen Markdown-Codeblock mit Exit-Code und Truncate-Hinweis:
// packages/coding-agent/src/core/messages.ts:82-98
export function bashExecutionToText(msg: BashExecutionMessage): string {
let text = `Ran \`${msg.command}\`\n`;
if (msg.output) {
text += `\`\`\`\n${msg.output}\n\`\`\``;
} else {
text += "(no output)";
}
if (msg.cancelled) {
text += "\n\n(command cancelled)";
} else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {
text += `\n\nCommand exited with code ${msg.exitCode}`;
}
if (msg.truncated && msg.fullOutputPath) {
text += `\n\n[Output truncated. Full output: ${msg.fullOutputPath}]`;
}
return text;
}Datenfluss
Pfad einer Nachricht von der Entstehung zum LLM:
Grenzen und Fehler
- excludeFromContext: bash-Ergebnis mit
!!-Präfix wird inconvertToLlmkomplett gefiltert, das Modell sieht es nicht, aber die UI zeigt es noch, siehepackages/coding-agent/src/core/messages.ts:152-156. - custom content zwei Formen:
CustomMessage.contentkann string oderArray<TextContent | ImageContent>sein, bei string wird es in ein einzelnes TextContent-Array umgewandelt, siehepackages/coding-agent/src/core/messages.ts:162-168. - timestamp String zu Zahl: Die drei create-Funktionen nutzen
new Date(timestamp).getTime(), wenn schon ein numerischer timestamp reinkommt, ergibt das NaN, der Aufrufer muss ISO-Strings übergeben, siehepackages/coding-agent/src/core/messages.ts:105-106. - blockImages-Wrapper-Schicht:
createAgentSessionwickeltconvertToLlmnoch in eineconvertToLlmWithBlockImages-Schicht ein, die settings dynamisch liest und Bilder durch Platzhaltertext ersetzt, siehe den Grenz-Abschnitt in createAgentSession Zusammenbau. - Kompression nutzt dieselbe Funktion: Sowohl
transformToLlmvonAgentals auchgenerateSummaryder Kompression nutzenconvertToLlm, damit der bei der Kompression sichtbare Kontext mit dem beim Senden an den Provider übereinstimmt.
Zusammenfassung
messages.ts fügt über TypeScript declaration merging AgentMessage vier eigene Typen hinzu, convertToLlm klappt sie vor dem Senden an den Provider zu user-Nachrichten flach, die UI-Schicht behält die Originaltypen für differenziertes Rendern. Beim Zusammenbau wird diese Funktion von createAgentSession noch in eine blockImages-Filter-Schicht gewrappt, siehe createAgentSession Zusammenbau; die Details, wie der Kompressionsprozess eine CompactionSummaryMessage erzeugt, im compaction-Verzeichnis.