fix(copilot): use providerMetadata for isLongRunning flag

The AI SDK strips unknown fields from tool-input-available events.
Use the standard providerMetadata field instead, which the SDK
preserves, to pass the isLongRunning flag to the frontend.

Backend changes:
- Change isLongRunning field to providerMetadata object
- Set providerMetadata: {isLongRunning: true} for long-running tools
- Add debug logging to verify flag is set

Frontend changes:
- Check part.providerMetadata.isLongRunning instead of part.isLongRunning
- Add console debug logging to verify detection

Tested programmatically - the complete flow works correctly.
This commit is contained in:
Zamil Majdy
2026-02-21 19:42:29 +07:00
parent 2447c30eff
commit b4c3bbe4c4
3 changed files with 35 additions and 6 deletions

View File

@@ -148,9 +148,9 @@ class StreamToolInputAvailable(StreamBaseResponse):
input: dict[str, Any] = Field(
default_factory=dict, description="Tool input arguments"
)
isLongRunning: bool = Field(
default=False,
description="Whether this tool is long-running (triggers UI feedback)",
providerMetadata: dict[str, Any] | None = Field(
default=None,
description="Provider metadata - used to pass isLongRunning flag to frontend",
)

View File

@@ -116,6 +116,11 @@ class SDKResponseAdapter:
tool = get_tool(tool_name)
is_long_running = tool.is_long_running if tool else False
logger.info(
f"[ADAPTER] Tool: {tool_name}, has_tool={tool is not None}, "
f"is_long_running={is_long_running}"
)
responses.append(
StreamToolInputStart(toolCallId=block.id, toolName=tool_name)
)
@@ -124,9 +129,15 @@ class SDKResponseAdapter:
toolCallId=block.id,
toolName=tool_name,
input=block.input,
isLongRunning=is_long_running,
providerMetadata=(
{"isLongRunning": True} if is_long_running else None
),
)
)
logger.info(
f"[ADAPTER] Created StreamToolInputAvailable with "
f"providerMetadata={{'isLongRunning': {is_long_running}}}"
)
self.current_tool_calls[block.id] = {"name": tool_name}
elif isinstance(sdk_message, UserMessage):

View File

@@ -14,8 +14,26 @@ export function ToolWrapper({ part, children }: Props) {
const isStreaming =
part.state === "input-streaming" || part.state === "input-available";
// Check if this tool is marked as long-running in the part itself
const isLongRunning = "isLongRunning" in part && part.isLongRunning === true;
// Check if this tool is marked as long-running via providerMetadata
const isLongRunning =
"providerMetadata" in part &&
part.providerMetadata &&
typeof part.providerMetadata === "object" &&
"isLongRunning" in part.providerMetadata &&
part.providerMetadata.isLongRunning === true;
// Debug logging
if (part.type.startsWith("tool-")) {
console.log("[ToolWrapper]", {
toolName: "toolName" in part ? part.toolName : "unknown",
hasProviderMetadata: "providerMetadata" in part,
providerMetadata:
"providerMetadata" in part ? part.providerMetadata : undefined,
computed: isLongRunning,
state: part.state,
part,
});
}
return (
<>