From ff5148e7cc7dc330fcc61b2619de43feb21022c0 Mon Sep 17 00:00:00 2001 From: Sumeet Agarwal Date: Thu, 12 Feb 2026 11:30:46 -0800 Subject: [PATCH] feat(extensions): forward message and tool execution events to extensions (#1375) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The extension system currently only forwards agent_start, agent_end, turn_start, and turn_end events. This means extensions cannot access streaming text (token-by-token), message lifecycle, or tool execution progress — all of which are available to internal subscribers. This adds forwarding for the remaining 6 agent event types: - message_start, message_update, message_end - tool_execution_start, tool_execution_update, tool_execution_end These follow the exact same pattern as the existing forwarded events: new interfaces in types.ts, exports in index.ts, and else-if blocks in _emitExtensionEvent(). The new types are included in ExtensionEvent and automatically flow through RunnerEmitEvent (they're not in the exclusion list). This enables extensions to build real-time UIs, streaming WebSocket bridges, and other integrations that need fine-grained event access. Co-authored-by: Claude Opus 4.6 Co-authored-by: Mario Zechner --- .../coding-agent/src/core/agent-session.ts | 51 ++++++++++++++++ .../coding-agent/src/core/extensions/index.ts | 8 +++ .../coding-agent/src/core/extensions/types.ts | 58 +++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index 3cb2f3ff..c9bc97b5 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -50,12 +50,18 @@ import { ExtensionRunner, type ExtensionUIContext, type InputSource, + type MessageEndEvent, + type MessageStartEvent, + type MessageUpdateEvent, type SessionBeforeCompactResult, type SessionBeforeForkResult, type SessionBeforeSwitchResult, type SessionBeforeTreeResult, type ShutdownHandler, type ToolDefinition, + type ToolExecutionEndEvent, + type ToolExecutionStartEvent, + type ToolExecutionUpdateEvent, type ToolInfo, type TreePreparation, type TurnEndEvent, @@ -444,6 +450,51 @@ export class AgentSession { }; await this._extensionRunner.emit(extensionEvent); this._turnIndex++; + } else if (event.type === "message_start") { + const extensionEvent: MessageStartEvent = { + type: "message_start", + message: event.message, + }; + await this._extensionRunner.emit(extensionEvent); + } else if (event.type === "message_update") { + const extensionEvent: MessageUpdateEvent = { + type: "message_update", + message: event.message, + assistantMessageEvent: event.assistantMessageEvent, + }; + await this._extensionRunner.emit(extensionEvent); + } else if (event.type === "message_end") { + const extensionEvent: MessageEndEvent = { + type: "message_end", + message: event.message, + }; + await this._extensionRunner.emit(extensionEvent); + } else if (event.type === "tool_execution_start") { + const extensionEvent: ToolExecutionStartEvent = { + type: "tool_execution_start", + toolCallId: event.toolCallId, + toolName: event.toolName, + args: event.args, + }; + await this._extensionRunner.emit(extensionEvent); + } else if (event.type === "tool_execution_update") { + const extensionEvent: ToolExecutionUpdateEvent = { + type: "tool_execution_update", + toolCallId: event.toolCallId, + toolName: event.toolName, + args: event.args, + partialResult: event.partialResult, + }; + await this._extensionRunner.emit(extensionEvent); + } else if (event.type === "tool_execution_end") { + const extensionEvent: ToolExecutionEndEvent = { + type: "tool_execution_end", + toolCallId: event.toolCallId, + toolName: event.toolName, + result: event.result, + isError: event.isError, + }; + await this._extensionRunner.emit(extensionEvent); } } diff --git a/packages/coding-agent/src/core/extensions/index.ts b/packages/coding-agent/src/core/extensions/index.ts index 0934395e..da7a5b40 100644 --- a/packages/coding-agent/src/core/extensions/index.ts +++ b/packages/coding-agent/src/core/extensions/index.ts @@ -81,9 +81,13 @@ export type { LoadExtensionsResult, LsToolCallEvent, LsToolResultEvent, + // Events - Message + MessageEndEvent, // Message Rendering MessageRenderer, MessageRenderOptions, + MessageStartEvent, + MessageUpdateEvent, ModelSelectEvent, ModelSelectSource, // Provider Registration @@ -124,6 +128,10 @@ export type { ToolCallEventResult, // Tools ToolDefinition, + // Events - Tool Execution + ToolExecutionEndEvent, + ToolExecutionStartEvent, + ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, diff --git a/packages/coding-agent/src/core/extensions/types.ts b/packages/coding-agent/src/core/extensions/types.ts index d5afe53a..62a3e567 100644 --- a/packages/coding-agent/src/core/extensions/types.ts +++ b/packages/coding-agent/src/core/extensions/types.ts @@ -16,6 +16,7 @@ import type { } from "@mariozechner/pi-agent-core"; import type { Api, + AssistantMessageEvent, AssistantMessageEventStream, Context, ImageContent, @@ -512,6 +513,51 @@ export interface TurnEndEvent { toolResults: ToolResultMessage[]; } +/** Fired when a message starts (user, assistant, or toolResult) */ +export interface MessageStartEvent { + type: "message_start"; + message: AgentMessage; +} + +/** Fired during assistant message streaming with token-by-token updates */ +export interface MessageUpdateEvent { + type: "message_update"; + message: AgentMessage; + assistantMessageEvent: AssistantMessageEvent; +} + +/** Fired when a message ends */ +export interface MessageEndEvent { + type: "message_end"; + message: AgentMessage; +} + +/** Fired when a tool starts executing */ +export interface ToolExecutionStartEvent { + type: "tool_execution_start"; + toolCallId: string; + toolName: string; + args: any; +} + +/** Fired during tool execution with partial/streaming output */ +export interface ToolExecutionUpdateEvent { + type: "tool_execution_update"; + toolCallId: string; + toolName: string; + args: any; + partialResult: any; +} + +/** Fired when a tool finishes executing */ +export interface ToolExecutionEndEvent { + type: "tool_execution_end"; + toolCallId: string; + toolName: string; + result: any; + isError: boolean; +} + // ============================================================================ // Model Events // ============================================================================ @@ -752,6 +798,12 @@ export type ExtensionEvent = | AgentEndEvent | TurnStartEvent | TurnEndEvent + | MessageStartEvent + | MessageUpdateEvent + | MessageEndEvent + | ToolExecutionStartEvent + | ToolExecutionUpdateEvent + | ToolExecutionEndEvent | ModelSelectEvent | UserBashEvent | InputEvent @@ -883,6 +935,12 @@ export interface ExtensionAPI { on(event: "agent_end", handler: ExtensionHandler): void; on(event: "turn_start", handler: ExtensionHandler): void; on(event: "turn_end", handler: ExtensionHandler): void; + on(event: "message_start", handler: ExtensionHandler): void; + on(event: "message_update", handler: ExtensionHandler): void; + on(event: "message_end", handler: ExtensionHandler): void; + on(event: "tool_execution_start", handler: ExtensionHandler): void; + on(event: "tool_execution_update", handler: ExtensionHandler): void; + on(event: "tool_execution_end", handler: ExtensionHandler): void; on(event: "model_select", handler: ExtensionHandler): void; on(event: "tool_call", handler: ExtensionHandler): void; on(event: "tool_result", handler: ExtensionHandler): void;