Jelajahi Sumber

fix(acp): replay loaded session transcript (#30645)

Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
opencode-agent[bot] 3 bulan lalu
induk
melakukan
69cfc44dba

+ 25 - 0
packages/opencode/src/acp/event.ts

@@ -12,6 +12,7 @@ import type {
 import { Effect } from "effect"
 import { ACPSession } from "./session"
 import { ACPPermission } from "./permission"
+import { partsToContentChunks, type ReplayPart } from "./content"
 import {
   duplicateRunningToolUpdate,
   errorToolUpdate,
@@ -87,7 +88,31 @@ export class Subscription {
       await this.recordFetchedPart(message.info.sessionID, message, part)
       if (part.type === "tool") {
         await this.handleToolPart(message.info.sessionID, part)
+        continue
       }
+      await this.replayContentPart(message, part)
+    }
+  }
+
+  private async replayContentPart(message: SessionMessageResponse, part: Part) {
+    if (part.type !== "text" && part.type !== "file" && part.type !== "reasoning") return
+
+    const sessionUpdate =
+      part.type === "reasoning"
+        ? "agent_thought_chunk"
+        : message.info.role === "user"
+          ? "user_message_chunk"
+          : "agent_message_chunk"
+
+    for (const chunk of partsToContentChunks([part as ReplayPart])) {
+      await this.input.connection.sessionUpdate({
+        sessionId: message.info.sessionID,
+        update: {
+          sessionUpdate,
+          messageId: message.info.id,
+          ...chunk,
+        },
+      })
     }
   }
 

+ 40 - 0
packages/opencode/test/acp/service-session.test.ts

@@ -316,6 +316,46 @@ describe("ACP service sessions", () => {
     expect(result.configOptions?.find((option) => option.id === "mode")?.currentValue).toBe("plan")
   })
 
+  it("replays loaded session transcript chunks", async () => {
+    const { service, updates } = makeService([
+      {
+        info: { id: "msg_user", sessionID: "ses_loaded", role: "user" },
+        parts: [{ id: "part_user", sessionID: "ses_loaded", messageID: "msg_user", type: "text", text: "hello" }],
+      },
+      {
+        info: { id: "msg_assistant", sessionID: "ses_loaded", role: "assistant" },
+        parts: [
+          {
+            id: "part_assistant",
+            sessionID: "ses_loaded",
+            messageID: "msg_assistant",
+            type: "text",
+            text: "hi there",
+          },
+        ],
+      },
+    ])
+
+    await Effect.runPromise(service.loadSession({ cwd: "/workspace", sessionId: "ses_loaded", mcpServers: [] }))
+
+    expect(
+      updates
+        .map((item) => item.update)
+        .filter((item) => item.sessionUpdate === "user_message_chunk" || item.sessionUpdate === "agent_message_chunk"),
+    ).toEqual([
+      {
+        sessionUpdate: "user_message_chunk",
+        messageId: "msg_user",
+        content: { type: "text", text: "hello" },
+      },
+      {
+        sessionUpdate: "agent_message_chunk",
+        messageId: "msg_assistant",
+        content: { type: "text", text: "hi there" },
+      },
+    ])
+  })
+
   it("lists sessions sorted by updated time with cursor support", async () => {
     const { service } = makeService()
     const first = await Effect.runPromise(service.listSessions({ cwd: "/workspace" }))