From ca3f559b8f8898262ac14d175e1279a7b0c08430 Mon Sep 17 00:00:00 2001 From: kumarabhirup Date: Thu, 5 Mar 2026 19:09:39 -0800 Subject: [PATCH] fix(web): honor initial session ID and forward subagent session keys Reconnect to the correct chat session when initialSessionId is provided instead of always picking the latest, and pass through the subagent sessionKey for clickable subagent cards. --- apps/web/app/components/chat-message.tsx | 14 +++++++++----- apps/web/app/components/chat-panel.tsx | 18 ++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/web/app/components/chat-message.tsx b/apps/web/app/components/chat-message.tsx index 12fafabd139..c732b49561e 100644 --- a/apps/web/app/components/chat-message.tsx +++ b/apps/web/app/components/chat-message.tsx @@ -52,7 +52,7 @@ type MessageSegment = | { type: "chain"; parts: ChainPart[] } | { type: "report-artifact"; config: ReportConfig } | { type: "diff-artifact"; diff: string } - | { type: "subagent-card"; task: string; label?: string; status: "running" | "done" | "error" }; + | { type: "subagent-card"; task: string; label?: string; sessionKey?: string; status: "running" | "done" | "error" }; /** Map AI SDK tool state string to a simplified status */ function toolStatus(state: string): "running" | "done" | "error" { @@ -145,9 +145,11 @@ function groupParts(parts: UIMessage["parts"]): MessageSegment[] { if (tp.toolName === "sessions_spawn") { flush(true); const args = asRecord(tp.input); + const out = asRecord(tp.output); const task = typeof args?.task === "string" ? args.task : "Subagent task"; const label = typeof args?.label === "string" ? args.label : undefined; - segments.push({ type: "subagent-card", task, label, status: toolStatus(tp.state) }); + const sessionKey = typeof out?.sessionKey === "string" ? out.sessionKey : undefined; + segments.push({ type: "subagent-card", task, label, sessionKey, status: toolStatus(tp.state) }); } else { chain.push({ kind: "tool", @@ -178,12 +180,14 @@ function groupParts(parts: UIMessage["parts"]): MessageSegment[] { if (resolvedToolName === "sessions_spawn") { flush(true); const args = asRecord(tp.input) ?? asRecord(tp.args); + const out = asRecord(tp.output) ?? asRecord(tp.result); const task = typeof args?.task === "string" ? args.task : "Subagent task"; const label = typeof args?.label === "string" ? args.label : undefined; + const sessionKey = typeof out?.sessionKey === "string" ? out.sessionKey : undefined; const resolvedState = tp.state ?? (tp.errorText ? "error" : ("result" in tp || "output" in tp) ? "output-available" : "input-available"); - segments.push({ type: "subagent-card", task, label, status: toolStatus(resolvedState) }); + segments.push({ type: "subagent-card", task, label, sessionKey, status: toolStatus(resolvedState) }); } else { // Persisted tool-invocation parts have no state field but // include result/output/errorText to indicate completion. @@ -565,7 +569,7 @@ function createMarkdownComponents( const normalizedHref = normalizePathReference(rawHref); const isExternal = rawHref && (rawHref.startsWith("http://") || rawHref.startsWith("https://") || rawHref.startsWith("//")); - const isWorkspaceAppLink = rawHref.startsWith("/workspace"); + const isWorkspaceAppLink = rawHref.startsWith("/workspace") || rawHref.startsWith("/?"); const isLocalPathLink = !isWorkspaceAppLink && (Boolean(rawHref.startsWith("file://")) || @@ -929,7 +933,7 @@ export const ChatMessage = memo(function ChatMessage({ message, isStreaming, onS >