Add Agent.prompt(AppMessage) overload for custom message types

Instead of using continue() which validates roles, prompt() now accepts
an AppMessage directly. This allows hook messages with role: 'hookMessage'
to trigger proper agent loop with message events.

- Add overloads: prompt(AppMessage) and prompt(string, attachments?)
- sendHookMessage uses prompt(appMessage) instead of appendMessage+continue
This commit is contained in:
Mario Zechner 2025-12-27 01:52:52 +01:00
parent 02f2c50155
commit a6322fda59
3 changed files with 33 additions and 25 deletions

View file

@ -170,13 +170,20 @@ export class Agent {
this.messageQueue = []; this.messageQueue = [];
} }
async prompt(input: string, attachments?: Attachment[]) { /** Send a prompt to the agent with an AppMessage. */
async prompt(message: AppMessage): Promise<void>;
/** Send a prompt to the agent with text and optional attachments. */
async prompt(input: string, attachments?: Attachment[]): Promise<void>;
async prompt(input: string | AppMessage, attachments?: Attachment[]) {
const model = this._state.model; const model = this._state.model;
if (!model) { if (!model) {
throw new Error("No model configured"); throw new Error("No model configured");
} }
// Build user message with attachments let userMessage: AppMessage;
if (typeof input === "string") {
// Build user message from text + attachments
const content: Array<TextContent | ImageContent> = [{ type: "text", text: input }]; const content: Array<TextContent | ImageContent> = [{ type: "text", text: input }];
if (attachments?.length) { if (attachments?.length) {
for (const a of attachments) { for (const a of attachments) {
@ -191,13 +198,16 @@ export class Agent {
} }
} }
} }
userMessage = {
const userMessage: AppMessage = {
role: "user", role: "user",
content, content,
attachments: attachments?.length ? attachments : undefined, attachments: attachments?.length ? attachments : undefined,
timestamp: Date.now(), timestamp: Date.now(),
}; };
} else {
// Use provided AppMessage directly
userMessage = input;
}
await this._runAgentLoop(userMessage); await this._runAgentLoop(userMessage);
} }

View file

@ -584,10 +584,8 @@ export class AgentSession {
// Queue for processing by agent loop // Queue for processing by agent loop
await this.agent.queueMessage(appMessage); await this.agent.queueMessage(appMessage);
} else if (triggerTurn) { } else if (triggerTurn) {
// Append to agent state and session, then trigger a turn // Send as prompt - agent loop will emit message events
this.agent.appendMessage(appMessage); await this.agent.prompt(appMessage);
// Start a new turn - emit message events for the hook message so TUI can render it
await this.agent.continue(true);
} else { } else {
// Just append to agent state and session, no turn // Just append to agent state and session, no turn
this.agent.appendMessage(appMessage); this.agent.appendMessage(appMessage);

View file

@ -115,7 +115,7 @@ export function messageTransformer(messages: AppMessage[]): Message[] {
}; };
} }
if (isHookAppMessage(m)) { if (isHookAppMessage(m)) {
// Convert hook message to user message // Convert hook message to user message for LLM
return { return {
role: "user", role: "user",
content: m.content, content: m.content,