From cfd46ee602a5605f16ece9bd46d9a8c6d9ff847d Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Wed, 4 Feb 2026 18:12:24 +0800 Subject: [PATCH] refactor(sdk,store): clean up SDK exports and scope tool interruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove backward-compatible aliases (TextBlock, ThinkingBlock, ToolCallBlock) and extractTextFromEvent from SDK — unused after prior refactors - Add explicit ContentBlock doc comment explaining the wider union tradeoff - Scope endStream tool interruption to the same agentId (prevents cross-agent interference in multi-agent scenarios) - Handle tool_execution_update event (no-op for now, avoids unhandled case) Co-Authored-By: Claude Opus 4.5 --- packages/sdk/src/actions/index.ts | 5 ----- packages/sdk/src/actions/stream.ts | 25 ++++++++----------------- packages/store/src/connection-store.ts | 3 +++ packages/store/src/messages.ts | 7 +++++-- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/packages/sdk/src/actions/index.ts b/packages/sdk/src/actions/index.ts index 4a28bc4f..cdbbe9ce 100644 --- a/packages/sdk/src/actions/index.ts +++ b/packages/sdk/src/actions/index.ts @@ -39,9 +39,4 @@ export { type ThinkingContent, type ToolCall, type ImageContent, - // Backward-compatible aliases - type TextBlock, - type ThinkingBlock, - type ToolCallBlock, - extractTextFromEvent, } from "./stream"; diff --git a/packages/sdk/src/actions/stream.ts b/packages/sdk/src/actions/stream.ts index 8babb7c0..8437029e 100644 --- a/packages/sdk/src/actions/stream.ts +++ b/packages/sdk/src/actions/stream.ts @@ -15,10 +15,14 @@ import type { AgentEvent } from "@mariozechner/pi-agent-core"; export type { TextContent, ThinkingContent, ToolCall, ImageContent }; export type { AgentEvent }; -/** Backward-compatible aliases */ -export type TextBlock = TextContent; -export type ThinkingBlock = ThinkingContent; -export type ToolCallBlock = ToolCall; +/** + * Convenience union of all content block types across message roles. + * + * NOTE: This is a deliberate simplification. The backend uses narrower unions + * per role (e.g. AssistantMessage.content excludes ImageContent, UserMessage + * excludes ThinkingContent/ToolCall). We accept the wider union on the frontend + * for simpler handling — the backend already guarantees correctness. + */ export type ContentBlock = TextContent | ThinkingContent | ToolCall | ImageContent; // --- Stream event types --- @@ -32,16 +36,3 @@ export interface StreamPayload { agentId: string; event: AgentEvent; } - -/** Extract plain text from an AgentEvent that carries a message */ -export function extractTextFromEvent(event: AgentEvent): string { - if (!("message" in event)) return ""; - const msg = event.message; - if (!msg || !("content" in msg)) return ""; - const content = msg.content; - if (!Array.isArray(content)) return ""; - return content - .filter((c): c is TextContent => c.type === "text") - .map((c) => c.text ?? "") - .join(""); -} diff --git a/packages/store/src/connection-store.ts b/packages/store/src/connection-store.ts index ea2407c2..b2951b38 100644 --- a/packages/store/src/connection-store.ts +++ b/packages/store/src/connection-store.ts @@ -139,6 +139,9 @@ function createClient( ) break } + case "tool_execution_update": + // Partial results — not rendered yet, ignored for now + break } return } diff --git a/packages/store/src/messages.ts b/packages/store/src/messages.ts index 61827471..45555dd6 100644 --- a/packages/store/src/messages.ts +++ b/packages/store/src/messages.ts @@ -129,11 +129,14 @@ export const useMessagesStore = create()((set, get) => ({ set((s) => { const ids = new Set(s.streamingIds) ids.delete(streamId) + // Find the agentId of the stream being ended to scope tool interruption + const streamMsg = s.messages.find((m) => m.id === streamId) + const streamAgentId = streamMsg?.agentId return { messages: s.messages.map((m) => { if (m.id === streamId) return { ...m, content, stopReason } - // Interrupt any still-running tool executions - if (m.role === "toolResult" && m.toolStatus === "running") { + // Interrupt running tool executions belonging to the same agent + if (m.role === "toolResult" && m.toolStatus === "running" && m.agentId === streamAgentId) { return { ...m, toolStatus: "interrupted" as ToolStatus } } return m