Browse Source

fix(app): finish tool call ID rename (#40539)

Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
opencode-agent[bot] 6 days ago
parent
commit
6d2eb2240a

+ 1 - 1
packages/app/src/context/global-sync/utils.test.ts

@@ -53,7 +53,7 @@ describe("normalizePermissionRequest", () => {
         resources: ["README.md"],
         save: ["*.md"],
         metadata: { path: "README.md" },
-        source: { type: "tool", messageID: "message-1", callID: "call-1" },
+        source: { type: "tool", messageID: "message-1", id: "call-1" },
       }),
     ).toEqual({
       id: "permission-1",

+ 1 - 1
packages/app/src/context/global-sync/utils.ts

@@ -48,7 +48,7 @@ export function normalizePermissionRequest(input: PermissionRequest | LegacyPerm
     always: input.save ?? [],
     metadata: input.metadata ?? {},
     tool:
-      input.source?.type === "tool" ? { messageID: input.source.messageID, callID: input.source.callID } : undefined,
+      input.source?.type === "tool" ? { messageID: input.source.messageID, callID: input.source.id } : undefined,
   }
 }
 

+ 34 - 2
packages/app/src/context/server-sdk.test.ts

@@ -21,12 +21,24 @@ describe("adaptServerEvent", () => {
       id: "evt_1",
       created: 1,
       type: "permission.asked",
-      data: { id: "perm_1", sessionID: "ses_1", action: "read", resources: ["src/**"] },
+      data: {
+        id: "perm_1",
+        sessionID: "ses_1",
+        action: "read",
+        resources: ["src/**"],
+        source: { type: "tool", messageID: "msg_1", id: "call_1" },
+      },
     } as OpenCodeEvent
 
     expect(adaptServerEvent(current)).toMatchObject({
       type: "permission.asked",
-      properties: { id: "perm_1", sessionID: "ses_1", permission: "read", patterns: ["src/**"] },
+      properties: {
+        id: "perm_1",
+        sessionID: "ses_1",
+        permission: "read",
+        patterns: ["src/**"],
+        tool: { messageID: "msg_1", callID: "call_1" },
+      },
       current,
     })
   })
@@ -70,6 +82,26 @@ describe("coalesceServerEvents", () => {
     expect(result[0]?.payload.current).toMatchObject({ id: "evt_2", data: { delta: "hello world" } })
   })
 
+  test("coalesces current tool input deltas by tool ID", () => {
+    const current = (eventID: string, id: string, delta: string) =>
+      adaptServerEvent({
+        id: eventID,
+        created: 1,
+        type: "session.tool.input.delta",
+        location: { directory: "/repo" },
+        data: { sessionID: "ses", assistantMessageID: "msg", id, delta },
+      } as OpenCodeEvent)
+    const result = coalesceServerEvents([
+      { directory: "/repo", payload: current("evt_1", "call_1", "{") },
+      { directory: "/repo", payload: current("evt_2", "call_1", "}") },
+      { directory: "/repo", payload: current("evt_3", "call_2", "[]") },
+    ])
+
+    expect(result).toHaveLength(2)
+    expect(result[0]?.payload.current).toMatchObject({ id: "evt_2", data: { id: "call_1", delta: "{}" } })
+    expect(result[1]?.payload.current).toMatchObject({ id: "evt_3", data: { id: "call_2", delta: "[]" } })
+  })
+
   test("preserves event boundaries and distinct fields", () => {
     const status = {
       directory: "/repo",

+ 2 - 2
packages/app/src/context/server-sdk.tsx

@@ -39,7 +39,7 @@ export function adaptServerEvent(event: OpenCodeEvent): ServerEvent {
         metadata: event.data.metadata ?? {},
         tool:
           event.data.source?.type === "tool"
-            ? { messageID: event.data.source.messageID, callID: event.data.source.callID }
+            ? { messageID: event.data.source.messageID, callID: event.data.source.id }
             : undefined,
       },
       current: event,
@@ -142,7 +142,7 @@ function currentDelta(event: OpenCodeEvent | undefined): CurrentDelta | undefine
 
 function currentDeltaKey(event: CurrentDelta) {
   if (event.type === "session.tool.input.delta")
-    return `${event.type}:${event.data.sessionID}:${event.data.assistantMessageID}:${event.data.callID}`
+    return `${event.type}:${event.data.sessionID}:${event.data.assistantMessageID}:${event.data.id}`
   if (event.type === "session.compaction.delta") return `${event.type}:${event.data.sessionID}`
   return `${event.type}:${event.data.sessionID}:${event.data.assistantMessageID}:${event.data.ordinal}`
 }

+ 4 - 4
packages/app/src/context/server-session-v2-reducer.test.ts

@@ -92,19 +92,19 @@ describe("v2 session reducer", () => {
       ...base,
       id: "evt_tool_start",
       type: "session.tool.input.started",
-      data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", callID: "call_1", name: "bash" },
+      data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", id: "call_1", name: "bash" },
     })
     apply({
       ...base,
       id: "evt_tool_delta",
       type: "session.tool.input.delta",
-      data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", callID: "call_1", delta: "{}" },
+      data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", id: "call_1", delta: "{}" },
     })
     apply({
       ...base,
       id: "evt_tool_called",
       type: "session.tool.called",
-      data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", callID: "call_1", input: {}, executed: true },
+      data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", id: "call_1", input: {}, executed: true },
     })
     apply({
       ...base,
@@ -113,7 +113,7 @@ describe("v2 session reducer", () => {
       data: {
         sessionID: "ses_1",
         assistantMessageID: "msg_assistant",
-        callID: "call_1",
+        id: "call_1",
         metadata: {},
         content: [{ type: "text", text: "done" }],
         executed: true,

+ 8 - 8
packages/app/src/context/server-session-v2-reducer.ts

@@ -241,13 +241,13 @@ export function createV2SessionReducer() {
       case "session.tool.input.started":
         return updateAssistant(source, event.data.assistantMessageID, sessionID, (item) => ({
           ...item,
-          content: item.content.some((content) => content.type === "tool" && content.id === event.data.callID)
+          content: item.content.some((content) => content.type === "tool" && content.id === event.data.id)
             ? item.content
             : [
                 ...item.content,
                 {
                   type: "tool",
-                  id: event.data.callID,
+                  id: event.data.id,
                   name: event.data.name,
                   state: { status: "streaming", input: "" },
                   time: { created: event.created },
@@ -255,17 +255,17 @@ export function createV2SessionReducer() {
               ],
         }))
       case "session.tool.input.delta":
-        return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) =>
+        return updateTool(source, event.data.assistantMessageID, event.data.id, sessionID, (tool) =>
           tool.state.status === "streaming"
             ? { ...tool, state: { ...tool.state, input: tool.state.input + event.data.delta } }
             : tool,
         )
       case "session.tool.input.ended":
-        return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) =>
+        return updateTool(source, event.data.assistantMessageID, event.data.id, sessionID, (tool) =>
           tool.state.status === "streaming" ? { ...tool, state: { ...tool.state, input: event.data.text } } : tool,
         )
       case "session.tool.called":
-        return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) => ({
+        return updateTool(source, event.data.assistantMessageID, event.data.id, sessionID, (tool) => ({
           ...tool,
           executed: event.data.executed,
           providerState: event.data.state,
@@ -274,7 +274,7 @@ export function createV2SessionReducer() {
           time: { ...tool.time, ran: event.created },
         }))
       case "session.tool.progress":
-        return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) =>
+        return updateTool(source, event.data.assistantMessageID, event.data.id, sessionID, (tool) =>
           tool.state.status === "running"
             ? {
                 ...tool,
@@ -284,7 +284,7 @@ export function createV2SessionReducer() {
             : tool,
         )
       case "session.tool.success":
-        return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) => {
+        return updateTool(source, event.data.assistantMessageID, event.data.id, sessionID, (tool) => {
           if (tool.state.status !== "running") return tool
           return {
             ...tool,
@@ -302,7 +302,7 @@ export function createV2SessionReducer() {
           }
         })
       case "session.tool.failed":
-        return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) => {
+        return updateTool(source, event.data.assistantMessageID, event.data.id, sessionID, (tool) => {
           if (tool.state.status !== "streaming" && tool.state.status !== "running") return tool
           return {
             ...tool,