瀏覽代碼

feat(studio): hydrate chat from shared interaction session

Ma 7 月之前
父節點
當前提交
f29aa4c656
共有 2 個文件被更改,包括 45 次插入 和 9 次删除
  1. 33 8
      packages/studio/src/components/ChatBar.tsx
  2. 12 1
      packages/studio/src/components/chatbar-state.test.ts

+ 33 - 8
packages/studio/src/components/ChatBar.tsx

@@ -50,6 +50,18 @@ interface BookRef {
   readonly id: string;
   readonly id: string;
 }
 }
 
 
+export function coerceSharedSessionMessages(
+  messages: ReadonlyArray<{ role: "user" | "assistant" | "system"; content: string; timestamp: number }>,
+): ReadonlyArray<ChatMessage> {
+  return messages
+    .filter((message) => message.role === "user" || message.role === "assistant")
+    .map((message) => ({
+      role: message.role as "user" | "assistant",
+      content: message.content,
+      timestamp: message.timestamp,
+    }));
+}
+
 export function resolveDirectWriteTarget(
 export function resolveDirectWriteTarget(
   activeBookId: string | undefined,
   activeBookId: string | undefined,
   books: ReadonlyArray<BookRef>,
   books: ReadonlyArray<BookRef>,
@@ -242,13 +254,7 @@ export function ChatPanel({ open, onClose, t, sse, activeBookId }: {
         activeBookId: data.activeBookId ?? data.session?.activeBookId,
         activeBookId: data.activeBookId ?? data.session?.activeBookId,
         automationMode: data.session?.automationMode,
         automationMode: data.session?.automationMode,
       });
       });
-      const restored = (data.session?.messages ?? [])
-        .filter((message) => message.role === "user" || message.role === "assistant")
-        .map((message) => ({
-          role: message.role as "user" | "assistant",
-          content: message.content,
-          timestamp: message.timestamp,
-        }));
+      const restored = coerceSharedSessionMessages(data.session?.messages ?? []);
       if (restored.length > 0) {
       if (restored.length > 0) {
         setMessages(restored);
         setMessages(restored);
       }
       }
@@ -342,12 +348,31 @@ export function ChatPanel({ open, onClose, t, sse, activeBookId }: {
         return;
         return;
       }
       }
 
 
-      const data = await fetchJson<{ response?: string; error?: string }>("/agent", {
+      const data = await fetchJson<{
+        response?: string;
+        error?: string;
+        session?: {
+          activeBookId?: string;
+          automationMode?: string;
+          messages?: ReadonlyArray<{ role: "user" | "assistant" | "system"; content: string; timestamp: number }>;
+        };
+      }>("/agent", {
         method: "POST",
         method: "POST",
         headers: { "Content-Type": "application/json" },
         headers: { "Content-Type": "application/json" },
         body: JSON.stringify({ instruction: text, activeBookId }),
         body: JSON.stringify({ instruction: text, activeBookId }),
       });
       });
       setLoading(false);
       setLoading(false);
+      if (data.session) {
+        setSessionMeta({
+          activeBookId: data.session.activeBookId ?? activeBookId,
+          automationMode: data.session.automationMode,
+        });
+        const restored = coerceSharedSessionMessages(data.session.messages ?? []);
+        if (restored.length > 0) {
+          setMessages(restored);
+          return;
+        }
+      }
       setMessages((prev) => [...prev, {
       setMessages((prev) => [...prev, {
         role: "assistant",
         role: "assistant",
         content: data.response ?? data.error ?? "Acknowledged.",
         content: data.response ?? data.error ?? "Acknowledged.",

+ 12 - 1
packages/studio/src/components/chatbar-state.test.ts

@@ -1,5 +1,5 @@
 import { describe, expect, it } from "vitest";
 import { describe, expect, it } from "vitest";
-import { resolveDirectWriteTarget } from "./ChatBar";
+import { coerceSharedSessionMessages, resolveDirectWriteTarget } from "./ChatBar";
 
 
 describe("resolveDirectWriteTarget", () => {
 describe("resolveDirectWriteTarget", () => {
   it("prefers the active book when the user is already inside a book flow", () => {
   it("prefers the active book when the user is already inside a book flow", () => {
@@ -35,4 +35,15 @@ describe("resolveDirectWriteTarget", () => {
       reason: "ambiguous",
       reason: "ambiguous",
     });
     });
   });
   });
+
+  it("coerces shared session messages into chat bubbles", () => {
+    expect(coerceSharedSessionMessages([
+      { role: "user", content: "continue", timestamp: 1 },
+      { role: "assistant", content: "Completed write_next for harbor.", timestamp: 2 },
+      { role: "system", content: "internal", timestamp: 3 },
+    ])).toEqual([
+      { role: "user", content: "continue", timestamp: 1 },
+      { role: "assistant", content: "Completed write_next for harbor.", timestamp: 2 },
+    ]);
+  });
 });
 });