From a6322fda59988b9a6f5e0e3e61ab346fbe2d518b Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 27 Dec 2025 01:52:52 +0100 Subject: [PATCH] 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 --- packages/agent/src/agent.ts | 50 +++++++++++-------- .../coding-agent/src/core/agent-session.ts | 6 +-- packages/coding-agent/src/core/messages.ts | 2 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts index 214bb403..59ead2a3 100644 --- a/packages/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -170,35 +170,45 @@ export class Agent { this.messageQueue = []; } - async prompt(input: string, attachments?: Attachment[]) { + /** Send a prompt to the agent with an AppMessage. */ + async prompt(message: AppMessage): Promise; + /** Send a prompt to the agent with text and optional attachments. */ + async prompt(input: string, attachments?: Attachment[]): Promise; + async prompt(input: string | AppMessage, attachments?: Attachment[]) { const model = this._state.model; if (!model) { throw new Error("No model configured"); } - // Build user message with attachments - const content: Array = [{ type: "text", text: input }]; - if (attachments?.length) { - for (const a of attachments) { - if (a.type === "image") { - content.push({ type: "image", data: a.content, mimeType: a.mimeType }); - } else if (a.type === "document" && a.extractedText) { - content.push({ - type: "text", - text: `\n\n[Document: ${a.fileName}]\n${a.extractedText}`, - isDocument: true, - } as TextContent); + let userMessage: AppMessage; + + if (typeof input === "string") { + // Build user message from text + attachments + const content: Array = [{ type: "text", text: input }]; + if (attachments?.length) { + for (const a of attachments) { + if (a.type === "image") { + content.push({ type: "image", data: a.content, mimeType: a.mimeType }); + } else if (a.type === "document" && a.extractedText) { + content.push({ + type: "text", + text: `\n\n[Document: ${a.fileName}]\n${a.extractedText}`, + isDocument: true, + } as TextContent); + } } } + userMessage = { + role: "user", + content, + attachments: attachments?.length ? attachments : undefined, + timestamp: Date.now(), + }; + } else { + // Use provided AppMessage directly + userMessage = input; } - const userMessage: AppMessage = { - role: "user", - content, - attachments: attachments?.length ? attachments : undefined, - timestamp: Date.now(), - }; - await this._runAgentLoop(userMessage); } diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index 9390f6ea..ffc964f8 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -584,10 +584,8 @@ export class AgentSession { // Queue for processing by agent loop await this.agent.queueMessage(appMessage); } else if (triggerTurn) { - // Append to agent state and session, then trigger a turn - this.agent.appendMessage(appMessage); - // Start a new turn - emit message events for the hook message so TUI can render it - await this.agent.continue(true); + // Send as prompt - agent loop will emit message events + await this.agent.prompt(appMessage); } else { // Just append to agent state and session, no turn this.agent.appendMessage(appMessage); diff --git a/packages/coding-agent/src/core/messages.ts b/packages/coding-agent/src/core/messages.ts index 12367ead..80121d6e 100644 --- a/packages/coding-agent/src/core/messages.ts +++ b/packages/coding-agent/src/core/messages.ts @@ -115,7 +115,7 @@ export function messageTransformer(messages: AppMessage[]): Message[] { }; } if (isHookAppMessage(m)) { - // Convert hook message to user message + // Convert hook message to user message for LLM return { role: "user", content: m.content,