mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 11:02:17 +00:00
Breaking changes: - Settings: 'hooks' and 'customTools' arrays replaced with 'extensions' - CLI: '--hook' and '--tool' flags replaced with '--extension' / '-e' - API: HookMessage renamed to CustomMessage, role 'hookMessage' to 'custom' - API: FileSlashCommand renamed to PromptTemplate - API: discoverSlashCommands() renamed to discoverPromptTemplates() - Directories: commands/ renamed to prompts/ for prompt templates Migration: - Session version bumped to 3 (auto-migrates v2 sessions) - Old 'hookMessage' role entries converted to 'custom' Structural changes: - src/core/hooks/ and src/core/custom-tools/ merged into src/core/extensions/ - src/core/slash-commands.ts renamed to src/core/prompt-templates.ts - examples/hooks/ and examples/custom-tools/ merged into examples/extensions/ - docs/hooks.md and docs/custom-tools.md merged into docs/extensions.md New test coverage: - test/extensions-runner.test.ts (10 tests) - test/extensions-discovery.test.ts (26 tests) - test/prompt-templates.test.ts
195 lines
5.1 KiB
TypeScript
195 lines
5.1 KiB
TypeScript
/**
|
|
* Custom message types and transformers for the coding agent.
|
|
*
|
|
* Extends the base AgentMessage type with coding-agent specific message types,
|
|
* and provides a transformer to convert them to LLM-compatible messages.
|
|
*/
|
|
|
|
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
import type { ImageContent, Message, TextContent } from "@mariozechner/pi-ai";
|
|
|
|
export const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:
|
|
|
|
<summary>
|
|
`;
|
|
|
|
export const COMPACTION_SUMMARY_SUFFIX = `
|
|
</summary>`;
|
|
|
|
export const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:
|
|
|
|
<summary>
|
|
`;
|
|
|
|
export const BRANCH_SUMMARY_SUFFIX = `</summary>`;
|
|
|
|
/**
|
|
* Message type for bash executions via the ! command.
|
|
*/
|
|
export interface BashExecutionMessage {
|
|
role: "bashExecution";
|
|
command: string;
|
|
output: string;
|
|
exitCode: number | undefined;
|
|
cancelled: boolean;
|
|
truncated: boolean;
|
|
fullOutputPath?: string;
|
|
timestamp: number;
|
|
/** If true, this message is excluded from LLM context (!! prefix) */
|
|
excludeFromContext?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Message type for extension-injected messages via sendMessage().
|
|
* These are custom messages that extensions can inject into the conversation.
|
|
*/
|
|
export interface CustomMessage<T = unknown> {
|
|
role: "custom";
|
|
customType: string;
|
|
content: string | (TextContent | ImageContent)[];
|
|
display: boolean;
|
|
details?: T;
|
|
timestamp: number;
|
|
}
|
|
|
|
export interface BranchSummaryMessage {
|
|
role: "branchSummary";
|
|
summary: string;
|
|
fromId: string;
|
|
timestamp: number;
|
|
}
|
|
|
|
export interface CompactionSummaryMessage {
|
|
role: "compactionSummary";
|
|
summary: string;
|
|
tokensBefore: number;
|
|
timestamp: number;
|
|
}
|
|
|
|
// Extend CustomAgentMessages via declaration merging
|
|
declare module "@mariozechner/pi-agent-core" {
|
|
interface CustomAgentMessages {
|
|
bashExecution: BashExecutionMessage;
|
|
custom: CustomMessage;
|
|
branchSummary: BranchSummaryMessage;
|
|
compactionSummary: CompactionSummaryMessage;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert a BashExecutionMessage to user message text for LLM context.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
export function createBranchSummaryMessage(summary: string, fromId: string, timestamp: string): BranchSummaryMessage {
|
|
return {
|
|
role: "branchSummary",
|
|
summary,
|
|
fromId,
|
|
timestamp: new Date(timestamp).getTime(),
|
|
};
|
|
}
|
|
|
|
export function createCompactionSummaryMessage(
|
|
summary: string,
|
|
tokensBefore: number,
|
|
timestamp: string,
|
|
): CompactionSummaryMessage {
|
|
return {
|
|
role: "compactionSummary",
|
|
summary: summary,
|
|
tokensBefore,
|
|
timestamp: new Date(timestamp).getTime(),
|
|
};
|
|
}
|
|
|
|
/** Convert CustomMessageEntry to AgentMessage format */
|
|
export function createCustomMessage(
|
|
customType: string,
|
|
content: string | (TextContent | ImageContent)[],
|
|
display: boolean,
|
|
details: unknown | undefined,
|
|
timestamp: string,
|
|
): CustomMessage {
|
|
return {
|
|
role: "custom",
|
|
customType,
|
|
content,
|
|
display,
|
|
details,
|
|
timestamp: new Date(timestamp).getTime(),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Transform AgentMessages (including custom types) to LLM-compatible Messages.
|
|
*
|
|
* This is used by:
|
|
* - Agent's transormToLlm option (for prompt calls and queued messages)
|
|
* - Compaction's generateSummary (for summarization)
|
|
* - Custom extensions and tools
|
|
*/
|
|
export function convertToLlm(messages: AgentMessage[]): Message[] {
|
|
return messages
|
|
.map((m): Message | undefined => {
|
|
switch (m.role) {
|
|
case "bashExecution":
|
|
// Skip messages excluded from context (!! prefix)
|
|
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,
|
|
};
|
|
}
|
|
case "branchSummary":
|
|
return {
|
|
role: "user",
|
|
content: [{ type: "text" as const, text: BRANCH_SUMMARY_PREFIX + m.summary + BRANCH_SUMMARY_SUFFIX }],
|
|
timestamp: m.timestamp,
|
|
};
|
|
case "compactionSummary":
|
|
return {
|
|
role: "user",
|
|
content: [
|
|
{ type: "text" as const, text: COMPACTION_SUMMARY_PREFIX + m.summary + COMPACTION_SUMMARY_SUFFIX },
|
|
],
|
|
timestamp: m.timestamp,
|
|
};
|
|
case "user":
|
|
case "assistant":
|
|
case "toolResult":
|
|
return m;
|
|
default:
|
|
// biome-ignore lint/correctness/noSwitchDeclarations: fine
|
|
const _exhaustiveCheck: never = m;
|
|
return undefined;
|
|
}
|
|
})
|
|
.filter((m) => m !== undefined);
|
|
}
|