mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 08:02:17 +00:00
Extensions: add pi.sendUserMessage() for sending user messages
Adds sendUserMessage() to the extension API, allowing extensions to send actual user messages (role: user) rather than custom messages. Unlike sendMessage(), this always triggers a turn and behaves as if the user typed the message. - Add SendUserMessageHandler type and sendUserMessage() to ExtensionAPI - Wire handler through loader, runner, and all modes - Implement via prompt() with expandPromptTemplates: false - Add send-user-message.ts example with /ask, /steer, /followup commands - Document in extensions.md fixes #483
This commit is contained in:
parent
f023af0dab
commit
7210086677
13 changed files with 222 additions and 1 deletions
|
|
@ -788,6 +788,45 @@ export class AgentSession {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a user message to the agent. Always triggers a turn.
|
||||
* When the agent is streaming, use deliverAs to specify how to queue the message.
|
||||
*
|
||||
* @param content User message content (string or content array)
|
||||
* @param options.deliverAs Delivery mode when streaming: "steer" or "followUp"
|
||||
*/
|
||||
async sendUserMessage(
|
||||
content: string | (TextContent | ImageContent)[],
|
||||
options?: { deliverAs?: "steer" | "followUp" },
|
||||
): Promise<void> {
|
||||
// Normalize content to text string + optional images
|
||||
let text: string;
|
||||
let images: ImageContent[] | undefined;
|
||||
|
||||
if (typeof content === "string") {
|
||||
text = content;
|
||||
} else {
|
||||
const textParts: string[] = [];
|
||||
images = [];
|
||||
for (const part of content) {
|
||||
if (part.type === "text") {
|
||||
textParts.push(part.text);
|
||||
} else {
|
||||
images.push(part);
|
||||
}
|
||||
}
|
||||
text = textParts.join("\n");
|
||||
if (images.length === 0) images = undefined;
|
||||
}
|
||||
|
||||
// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion
|
||||
await this.prompt(text, {
|
||||
expandPromptTemplates: false,
|
||||
streamingBehavior: options?.deliverAs,
|
||||
images,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all queued messages and return them.
|
||||
* Useful for restoring to editor when user aborts.
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ export type {
|
|||
RegisteredCommand,
|
||||
RegisteredTool,
|
||||
SendMessageHandler,
|
||||
SendUserMessageHandler,
|
||||
SessionBeforeBranchEvent,
|
||||
SessionBeforeBranchResult,
|
||||
SessionBeforeCompactEvent,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import type {
|
|||
RegisteredCommand,
|
||||
RegisteredTool,
|
||||
SendMessageHandler,
|
||||
SendUserMessageHandler,
|
||||
SetActiveToolsHandler,
|
||||
ToolDefinition,
|
||||
} from "./types.js";
|
||||
|
|
@ -117,6 +118,7 @@ function createExtensionAPI(
|
|||
flagValues: Map<string, boolean | string>;
|
||||
shortcuts: Map<KeyId, ExtensionShortcut>;
|
||||
setSendMessageHandler: (handler: SendMessageHandler) => void;
|
||||
setSendUserMessageHandler: (handler: SendUserMessageHandler) => void;
|
||||
setAppendEntryHandler: (handler: AppendEntryHandler) => void;
|
||||
setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => void;
|
||||
setGetAllToolsHandler: (handler: GetAllToolsHandler) => void;
|
||||
|
|
@ -124,6 +126,7 @@ function createExtensionAPI(
|
|||
setFlagValue: (name: string, value: boolean | string) => void;
|
||||
} {
|
||||
let sendMessageHandler: SendMessageHandler = () => {};
|
||||
let sendUserMessageHandler: SendUserMessageHandler = () => {};
|
||||
let appendEntryHandler: AppendEntryHandler = () => {};
|
||||
let getActiveToolsHandler: GetActiveToolsHandler = () => [];
|
||||
let getAllToolsHandler: GetAllToolsHandler = () => [];
|
||||
|
|
@ -185,6 +188,10 @@ function createExtensionAPI(
|
|||
sendMessageHandler(message, options);
|
||||
},
|
||||
|
||||
sendUserMessage(content, options): void {
|
||||
sendUserMessageHandler(content, options);
|
||||
},
|
||||
|
||||
appendEntry(customType: string, data?: unknown): void {
|
||||
appendEntryHandler(customType, data);
|
||||
},
|
||||
|
|
@ -218,6 +225,9 @@ function createExtensionAPI(
|
|||
setSendMessageHandler: (handler: SendMessageHandler) => {
|
||||
sendMessageHandler = handler;
|
||||
},
|
||||
setSendUserMessageHandler: (handler: SendUserMessageHandler) => {
|
||||
sendUserMessageHandler = handler;
|
||||
},
|
||||
setAppendEntryHandler: (handler: AppendEntryHandler) => {
|
||||
appendEntryHandler = handler;
|
||||
},
|
||||
|
|
@ -261,6 +271,7 @@ async function loadExtensionWithBun(
|
|||
flagValues,
|
||||
shortcuts,
|
||||
setSendMessageHandler,
|
||||
setSendUserMessageHandler,
|
||||
setAppendEntryHandler,
|
||||
setGetActiveToolsHandler,
|
||||
setGetAllToolsHandler,
|
||||
|
|
@ -282,6 +293,7 @@ async function loadExtensionWithBun(
|
|||
flagValues,
|
||||
shortcuts,
|
||||
setSendMessageHandler,
|
||||
setSendUserMessageHandler,
|
||||
setAppendEntryHandler,
|
||||
setGetActiveToolsHandler,
|
||||
setGetAllToolsHandler,
|
||||
|
|
@ -341,6 +353,7 @@ async function loadExtension(
|
|||
flagValues,
|
||||
shortcuts,
|
||||
setSendMessageHandler,
|
||||
setSendUserMessageHandler,
|
||||
setAppendEntryHandler,
|
||||
setGetActiveToolsHandler,
|
||||
setGetAllToolsHandler,
|
||||
|
|
@ -362,6 +375,7 @@ async function loadExtension(
|
|||
flagValues,
|
||||
shortcuts,
|
||||
setSendMessageHandler,
|
||||
setSendUserMessageHandler,
|
||||
setAppendEntryHandler,
|
||||
setGetActiveToolsHandler,
|
||||
setGetAllToolsHandler,
|
||||
|
|
@ -396,6 +410,7 @@ export function loadExtensionFromFactory(
|
|||
flagValues,
|
||||
shortcuts,
|
||||
setSendMessageHandler,
|
||||
setSendUserMessageHandler,
|
||||
setAppendEntryHandler,
|
||||
setGetActiveToolsHandler,
|
||||
setGetAllToolsHandler,
|
||||
|
|
@ -416,6 +431,7 @@ export function loadExtensionFromFactory(
|
|||
flagValues,
|
||||
shortcuts,
|
||||
setSendMessageHandler,
|
||||
setSendUserMessageHandler,
|
||||
setAppendEntryHandler,
|
||||
setGetActiveToolsHandler,
|
||||
setGetAllToolsHandler,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import type {
|
|||
RegisteredCommand,
|
||||
RegisteredTool,
|
||||
SendMessageHandler,
|
||||
SendUserMessageHandler,
|
||||
SessionBeforeCompactResult,
|
||||
SessionBeforeTreeResult,
|
||||
SetActiveToolsHandler,
|
||||
|
|
@ -108,6 +109,7 @@ export class ExtensionRunner {
|
|||
initialize(options: {
|
||||
getModel: () => Model<any> | undefined;
|
||||
sendMessageHandler: SendMessageHandler;
|
||||
sendUserMessageHandler: SendUserMessageHandler;
|
||||
appendEntryHandler: AppendEntryHandler;
|
||||
getActiveToolsHandler: GetActiveToolsHandler;
|
||||
getAllToolsHandler: GetAllToolsHandler;
|
||||
|
|
@ -140,6 +142,7 @@ export class ExtensionRunner {
|
|||
|
||||
for (const ext of this.extensions) {
|
||||
ext.setSendMessageHandler(options.sendMessageHandler);
|
||||
ext.setSendUserMessageHandler(options.sendUserMessageHandler);
|
||||
ext.setAppendEntryHandler(options.appendEntryHandler);
|
||||
ext.setGetActiveToolsHandler(options.getActiveToolsHandler);
|
||||
ext.setGetAllToolsHandler(options.getAllToolsHandler);
|
||||
|
|
|
|||
|
|
@ -592,6 +592,15 @@ export interface ExtensionAPI {
|
|||
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Send a user message to the agent. Always triggers a turn.
|
||||
* When the agent is streaming, use deliverAs to specify how to queue the message.
|
||||
*/
|
||||
sendUserMessage(
|
||||
content: string | (TextContent | ImageContent)[],
|
||||
options?: { deliverAs?: "steer" | "followUp" },
|
||||
): void;
|
||||
|
||||
/** Append a custom entry to the session for state persistence (not sent to LLM). */
|
||||
appendEntry<T = unknown>(customType: string, data?: T): void;
|
||||
|
||||
|
|
@ -645,6 +654,11 @@ export type SendMessageHandler = <T = unknown>(
|
|||
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
||||
) => void;
|
||||
|
||||
export type SendUserMessageHandler = (
|
||||
content: string | (TextContent | ImageContent)[],
|
||||
options?: { deliverAs?: "steer" | "followUp" },
|
||||
) => void;
|
||||
|
||||
export type AppendEntryHandler = <T = unknown>(customType: string, data?: T) => void;
|
||||
|
||||
export type GetActiveToolsHandler = () => string[];
|
||||
|
|
@ -665,6 +679,7 @@ export interface LoadedExtension {
|
|||
flagValues: Map<string, boolean | string>;
|
||||
shortcuts: Map<KeyId, ExtensionShortcut>;
|
||||
setSendMessageHandler: (handler: SendMessageHandler) => void;
|
||||
setSendUserMessageHandler: (handler: SendUserMessageHandler) => void;
|
||||
setAppendEntryHandler: (handler: AppendEntryHandler) => void;
|
||||
setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => void;
|
||||
setGetAllToolsHandler: (handler: GetAllToolsHandler) => void;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue