Tipos de mensaje y convertToLlm
messages.ts añade cuatro tipos de mensaje personalizado al AgentMessage de pi-agent-core: bashExecution, custom, branchSummary, compactionSummary. El Agent subyacente sólo reconoce user/assistant/toolResult; convertToLlm traduce estos cuatro tipos personalizados a Message[] y filtra los resultados de bash con excludeFromContext. La extensión se hace por declaración merging sobre la interfaz CustomAgentMessages, alineando tipo y runtime.
Responsabilidades
- Extensión de tipos: con
declare moduleañade cuatro roles aCustomAgentMessages. Verpackages/coding-agent/src/core/messages.ts:69-77. - Mensaje bash execution: el resultado de un comando
!, concommand,output,exitCode,cancelled,truncated,fullOutputPath, yexcludeFromContextopcional (para el prefijo!!). Verpackages/coding-agent/src/core/messages.ts:29-40. - Mensaje custom: inyectado por extensiones vía
sendMessage;customTypedistingue el tipo,displaycontrola si se muestra en la UI,detailses data estructurada opcional. Verpackages/coding-agent/src/core/messages.ts:46-53. - Resumen de branch / compaction: al volver de un fork se inserta un resumen de branch, al compactar se inserta un resumen de compactación; ambos envueltos en etiquetas
<summary>. Verpackages/coding-agent/src/core/messages.ts:55-67. - Fábricas:
createBranchSummaryMessage/createCompactionSummaryMessage/createCustomMessageconvierten un timestamp string a milisegundos numéricos. Verpackages/coding-agent/src/core/messages.ts:100-138. - convertToLlm: traduce los mensajes personalizados a
Message[]; bash usabashExecutionToTexty se convierte en user message; branch/compaction se envuelven en<summary>; los bash conexcludeFromContextse filtran devolviendo undefined. Verpackages/coding-agent/src/core/messages.ts:148-195.
Motivación de diseño
¿Por qué no reutilizar directamente el mensaje user? Porque la UI necesita distinguir "esto es salida de bash" de "esto es entrada del usuario": estilo de renderizado, plegado, reenvío. Pero el LLM sólo necesita ver texto: convertToLlm aplana todos los tipos personalizados a user message antes de llamar al provider, y el modelo no sabe que existen los tipos custom. Así se preserva la riqueza semántica en la UI sin contaminar el contexto del modelo.
bashExecutionToText formatea la salida de bash como bloque de código Markdown, con prefijo Ran \command`y código de salida como sufijo, de modo que el LLM ve un formato parecido al que ve el usuario. Los bash con prefijo!!se eliminan completamente del contexto del LLM víaexcludeFromContext: true`: es la elección del usuario de "que el modelo no vea este paso", típico en operaciones sensibles o salidas ruidosas.
branchSummary y compactionSummary se envuelven en etiquetas XML <summary>; es el formato que Anthropic recomienda, y el modelo tiende a tratar el contenido de la etiqueta como una unidad completa en vez de dispersarlo en párrafos.
Archivos clave
packages/coding-agent/src/core/messages.ts:11-24— ConstantesCOMPACTION_SUMMARY_PREFIX/SUFFIXyBRANCH_SUMMARY_PREFIX/SUFFIX.packages/coding-agent/src/core/messages.ts:29-40— InterfazBashExecutionMessage.packages/coding-agent/src/core/messages.ts:46-53— InterfazCustomMessage<T>.packages/coding-agent/src/core/messages.ts:55-67—BranchSummaryMessageyCompactionSummaryMessage; este último guardatokensBeforepara diagnóstico.packages/coding-agent/src/core/messages.ts:69-77—declare modulepara declaration merging y extenderCustomAgentMessages.packages/coding-agent/src/core/messages.ts:82-98—bashExecutionToText, formatea la salida de bash a texto LLM-friendly.packages/coding-agent/src/core/messages.ts:100-138— Las tres fábricas create.packages/coding-agent/src/core/messages.ts:148-195—convertToLlm, patrón switch + filter.
convertToLlm usa switch + cheque exhaustivo never; si se añade un tipo nuevo y se olvida, falla en compilación:
// 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 envuelve la salida en un bloque Markdown, con código de salida y aviso de truncado:
// 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;
}Flujo de datos
Camino de los mensajes hasta el LLM:
Límites y fallos
- excludeFromContext: los resultados bash con prefijo
!!se filtran enconvertToLlm; el modelo no los ve, pero la UI los sigue mostrando. Verpackages/coding-agent/src/core/messages.ts:152-156. - content dual en custom:
CustomMessage.contentpuede ser string oArray<TextContent | ImageContent>; si es string se convierte en array de un TextContent. Verpackages/coding-agent/src/core/messages.ts:162-168. - timestamp string a número: las tres funciones create usan
new Date(timestamp).getTime(); si ya viene como timestamp numérico, devuelven NaN; el llamador debe pasar ISO string. Verpackages/coding-agent/src/core/messages.ts:105-106. - Envoltura blockImages:
createAgentSessionenvuelveconvertToLlmconconvertToLlmWithBlockImages, que lee settings dinámicamente y reemplaza imágenes por placeholder. Ver la sección de límites de ensamblaje createAgentSession. - Compactación usa la misma función: tanto
transformToLlmdelAgentcomogenerateSummaryde compactación usanconvertToLlm, para que el contexto que ve la compactación coincida con el que se envía al provider.
Resumen
messages.ts usa declaration merging de TypeScript para añadir cuatro tipos personalizados a AgentMessage; convertToLlm los aplana a user message antes de llamar al provider, y la UI conserva el tipo original para renderizado diferenciado. Al ensamblar, esta función se envuelve con la capa blockImages en ensamblaje createAgentSession; los detalles de CompactionSummaryMessage generado por la compactación están en el directorio compaction.