refactor(sdk,store): clean up SDK exports and scope tool interruption

- 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 <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing 2026-02-04 18:12:24 +08:00
parent 703c4686d9
commit cfd46ee602
4 changed files with 16 additions and 24 deletions

View file

@ -39,9 +39,4 @@ export {
type ThinkingContent,
type ToolCall,
type ImageContent,
// Backward-compatible aliases
type TextBlock,
type ThinkingBlock,
type ToolCallBlock,
extractTextFromEvent,
} from "./stream";

View file

@ -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("");
}

View file

@ -139,6 +139,9 @@ function createClient(
)
break
}
case "tool_execution_update":
// Partial results — not rendered yet, ignored for now
break
}
return
}

View file

@ -129,11 +129,14 @@ export const useMessagesStore = create<MessagesStore>()((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