Explorar o código

Merge pull request #4275 from Kilo-Org/use-presigned-urls-to-upload-session-data

Igor Šćekić hai 2 meses
pai
achega
769fe712b0

+ 5 - 0
.changeset/famous-places-attend.md

@@ -0,0 +1,5 @@
+---
+"kilo-code": patch
+---
+
+use new endpoint for uploading session blobs via presigned r2 urls

+ 36 - 8
src/shared/kilocode/cli-sessions/core/SessionClient.ts

@@ -180,30 +180,58 @@ export class SessionClient {
 	}
 
 	/**
-	 * Upload a blob for a session
+	 * Upload a blob for a session using signed URL
 	 */
 	async uploadBlob(
 		sessionId: string,
 		blobType: "api_conversation_history" | "task_metadata" | "ui_messages" | "git_state",
 		blobData: unknown,
-	): Promise<{ session_id: string; updated_at: string }> {
+	): Promise<void> {
+		const blobBody = JSON.stringify(blobData)
+		const contentLength = new TextEncoder().encode(blobBody).length
+
+		const signedUrlResponse = await this.getSignedUploadUrl(sessionId, blobType, contentLength)
+
+		const uploadResponse = await fetch(signedUrlResponse.signed_url, {
+			method: "PUT",
+			headers: {
+				"Content-Type": "application/json",
+			},
+			body: blobBody,
+		})
+
+		if (!uploadResponse.ok) {
+			throw new Error(`uploadBlob failed: upload to signed URL returned ${uploadResponse.status}`)
+		}
+	}
+
+	/**
+	 * Get a signed URL for uploading a blob
+	 */
+	private async getSignedUploadUrl(
+		sessionId: string,
+		blobType: "api_conversation_history" | "task_metadata" | "ui_messages" | "git_state",
+		contentLength: number,
+	): Promise<{ signed_url: string }> {
 		const { endpoint, getToken } = this.trpcClient
 
-		const url = new URL("/api/upload-cli-session-blob", endpoint)
-		url.searchParams.set("session_id", sessionId)
-		url.searchParams.set("blob_type", blobType)
+		const url = new URL("/api/upload-cli-session-blob-v2", endpoint)
 
 		const response = await fetch(url.toString(), {
 			method: "POST",
 			headers: {
-				"Content-Type": "application/json",
 				Authorization: `Bearer ${await getToken()}`,
+				"Content-Type": "application/json",
 			},
-			body: JSON.stringify(blobData),
+			body: JSON.stringify({
+				session_id: sessionId,
+				blob_type: blobType,
+				content_length: contentLength,
+			}),
 		})
 
 		if (!response.ok) {
-			throw new Error(`uploadBlob failed: ${url.toString()} ${response.status}`)
+			throw new Error(`getSignedUploadUrl failed: ${url.toString()} ${response.status}`)
 		}
 
 		return response.json()

+ 193 - 0
src/shared/kilocode/cli-sessions/core/__tests__/SessionClient.spec.ts

@@ -0,0 +1,193 @@
+import { SessionClient } from "../SessionClient"
+import type { TrpcClient } from "../TrpcClient"
+
+const mockFetch = vi.fn()
+global.fetch = mockFetch
+
+describe("SessionClient", () => {
+	let sessionClient: SessionClient
+	let mockTrpcClient: TrpcClient
+
+	beforeEach(() => {
+		vi.clearAllMocks()
+
+		mockTrpcClient = {
+			endpoint: "https://api.example.com",
+			getToken: vi.fn().mockResolvedValue("test-token"),
+			request: vi.fn(),
+		} as unknown as TrpcClient
+
+		sessionClient = new SessionClient(mockTrpcClient)
+	})
+
+	describe("uploadBlob", () => {
+		const sessionId = "test-session-123"
+		const blobType = "ui_messages" as const
+		const blobData = { messages: ["hello", "world"] }
+
+		it("should get signed URL and upload blob successfully", async () => {
+			const signedUrl = "https://storage.example.com/signed-upload-url"
+			const blobBody = JSON.stringify(blobData)
+			const contentLength = new TextEncoder().encode(blobBody).length
+
+			mockFetch
+				.mockResolvedValueOnce({
+					ok: true,
+					json: () => Promise.resolve({ signed_url: signedUrl }),
+				})
+				.mockResolvedValueOnce({
+					ok: true,
+				})
+
+			await sessionClient.uploadBlob(sessionId, blobType, blobData)
+
+			expect(mockFetch).toHaveBeenCalledTimes(2)
+
+			expect(mockFetch).toHaveBeenNthCalledWith(1, "https://api.example.com/api/upload-cli-session-blob-v2", {
+				method: "POST",
+				headers: {
+					Authorization: "Bearer test-token",
+					"Content-Type": "application/json",
+				},
+				body: JSON.stringify({
+					session_id: sessionId,
+					blob_type: blobType,
+					content_length: contentLength,
+				}),
+			})
+
+			expect(mockFetch).toHaveBeenNthCalledWith(2, signedUrl, {
+				method: "PUT",
+				headers: {
+					"Content-Type": "application/json",
+				},
+				body: blobBody,
+			})
+		})
+
+		it("should throw error when getting signed URL fails", async () => {
+			mockFetch.mockResolvedValueOnce({
+				ok: false,
+				status: 404,
+			})
+
+			await expect(sessionClient.uploadBlob(sessionId, blobType, blobData)).rejects.toThrow(
+				"getSignedUploadUrl failed",
+			)
+
+			expect(mockFetch).toHaveBeenCalledTimes(1)
+		})
+
+		it("should throw error when upload to signed URL fails", async () => {
+			const signedUrl = "https://storage.example.com/signed-upload-url"
+
+			mockFetch
+				.mockResolvedValueOnce({
+					ok: true,
+					json: () => Promise.resolve({ signed_url: signedUrl }),
+				})
+				.mockResolvedValueOnce({
+					ok: false,
+					status: 500,
+				})
+
+			await expect(sessionClient.uploadBlob(sessionId, blobType, blobData)).rejects.toThrow(
+				"uploadBlob failed: upload to signed URL returned 500",
+			)
+
+			expect(mockFetch).toHaveBeenCalledTimes(2)
+		})
+
+		it("should work with different blob types", async () => {
+			const signedUrl = "https://storage.example.com/signed-upload-url"
+			const blobBody = JSON.stringify(blobData)
+			const contentLength = new TextEncoder().encode(blobBody).length
+
+			mockFetch
+				.mockResolvedValueOnce({
+					ok: true,
+					json: () => Promise.resolve({ signed_url: signedUrl }),
+				})
+				.mockResolvedValueOnce({
+					ok: true,
+				})
+
+			await sessionClient.uploadBlob(sessionId, "api_conversation_history", blobData)
+
+			expect(mockFetch).toHaveBeenNthCalledWith(1, "https://api.example.com/api/upload-cli-session-blob-v2", {
+				method: "POST",
+				headers: {
+					Authorization: "Bearer test-token",
+					"Content-Type": "application/json",
+				},
+				body: JSON.stringify({
+					session_id: sessionId,
+					blob_type: "api_conversation_history",
+					content_length: contentLength,
+				}),
+			})
+		})
+
+		it("should work with task_metadata blob type", async () => {
+			const signedUrl = "https://storage.example.com/signed-upload-url"
+			const taskData = { task: "test" }
+			const blobBody = JSON.stringify(taskData)
+			const contentLength = new TextEncoder().encode(blobBody).length
+
+			mockFetch
+				.mockResolvedValueOnce({
+					ok: true,
+					json: () => Promise.resolve({ signed_url: signedUrl }),
+				})
+				.mockResolvedValueOnce({
+					ok: true,
+				})
+
+			await sessionClient.uploadBlob(sessionId, "task_metadata", taskData)
+
+			expect(mockFetch).toHaveBeenNthCalledWith(1, "https://api.example.com/api/upload-cli-session-blob-v2", {
+				method: "POST",
+				headers: {
+					Authorization: "Bearer test-token",
+					"Content-Type": "application/json",
+				},
+				body: JSON.stringify({
+					session_id: sessionId,
+					blob_type: "task_metadata",
+					content_length: contentLength,
+				}),
+			})
+		})
+
+		it("should work with git_state blob type", async () => {
+			const signedUrl = "https://storage.example.com/signed-upload-url"
+			const gitData = { head: "abc123" }
+			const blobBody = JSON.stringify(gitData)
+			const contentLength = new TextEncoder().encode(blobBody).length
+
+			mockFetch
+				.mockResolvedValueOnce({
+					ok: true,
+					json: () => Promise.resolve({ signed_url: signedUrl }),
+				})
+				.mockResolvedValueOnce({
+					ok: true,
+				})
+
+			await sessionClient.uploadBlob(sessionId, "git_state", gitData)
+
+			expect(mockFetch).toHaveBeenNthCalledWith(1, "https://api.example.com/api/upload-cli-session-blob-v2", {
+				method: "POST",
+				headers: {
+					Authorization: "Bearer test-token",
+					"Content-Type": "application/json",
+				},
+				body: JSON.stringify({
+					session_id: sessionId,
+					blob_type: "git_state",
+					content_length: contentLength,
+				}),
+			})
+		})
+	})
+})

+ 2 - 8
src/shared/kilocode/cli-sessions/core/__tests__/SessionManager.spec.ts

@@ -588,10 +588,7 @@ describe("SessionManager", () => {
 				created_at: new Date().toISOString(),
 				updated_at: new Date().toISOString(),
 			})
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "new-session-456",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue()
 
 			const result = await manager.getSessionFromTask("task-123", mockTaskDataProvider)
 
@@ -615,10 +612,7 @@ describe("SessionManager", () => {
 				created_at: new Date().toISOString(),
 				updated_at: new Date().toISOString(),
 			})
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "new-session-456",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue()
 
 			await manager.getSessionFromTask("task-123", mockTaskDataProvider)
 

+ 20 - 100
src/shared/kilocode/cli-sessions/core/__tests__/SessionManager.syncSession.spec.ts

@@ -212,10 +212,7 @@ describe("SessionManager.syncSession", () => {
 				updated_at: new Date().toISOString(),
 			})
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "new-session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
 
@@ -240,10 +237,7 @@ describe("SessionManager.syncSession", () => {
 				updated_at: new Date().toISOString(),
 			})
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "new-session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
 
@@ -259,10 +253,7 @@ describe("SessionManager.syncSession", () => {
 		it("should use existing session when task already has one", async () => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("existing-session-456")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "existing-session-456",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
 
@@ -280,10 +271,7 @@ describe("SessionManager.syncSession", () => {
 	describe("blob uploads", () => {
 		beforeEach(() => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 		})
 
 		it("should upload ui_messages blob", async () => {
@@ -392,10 +380,7 @@ describe("SessionManager.syncSession", () => {
 		beforeEach(() => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 		})
 
 		it("should upload git state when it changes", async () => {
@@ -444,10 +429,7 @@ describe("SessionManager.syncSession", () => {
 		it("should update session when git URL changes", async () => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 			vi.mocked(manager.sessionClient!.update).mockResolvedValue({
 				session_id: "session-123",
 				title: "",
@@ -471,10 +453,7 @@ describe("SessionManager.syncSession", () => {
 	describe("title generation", () => {
 		beforeEach(() => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 		})
 
 		it("should check for title generation when uploading ui_messages blob", async () => {
@@ -529,10 +508,7 @@ describe("SessionManager.syncSession", () => {
 				.mockReturnValueOnce("session-1")
 				.mockReturnValueOnce("session-2")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-1",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-1", "uiMessagesPath", "/path/to/file1.json")
 			manager.handleFileUpdate("task-2", "uiMessagesPath", "/path/to/file2.json")
@@ -557,10 +533,7 @@ describe("SessionManager.syncSession", () => {
 				.mockReturnValueOnce("session-2")
 				.mockReturnValueOnce("session-2")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-2",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-1", "uiMessagesPath", "/path/to/file1.json")
 			manager.handleFileUpdate("task-2", "uiMessagesPath", "/path/to/file2.json")
@@ -575,10 +548,7 @@ describe("SessionManager.syncSession", () => {
 		it("should reset isSyncing to false after sync completes", async () => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
 
@@ -613,10 +583,7 @@ describe("SessionManager.syncSession", () => {
 				})
 				.mockReturnValueOnce(JSON.stringify([]))
 
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-2",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-1", "uiMessagesPath", "/path/to/file1.json")
 			manager.handleFileUpdate("task-2", "uiMessagesPath", "/path/to/file2.json")
@@ -660,10 +627,7 @@ describe("SessionManager.syncSession", () => {
 		describe("title generation race conditions", () => {
 			beforeEach(() => {
 				vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
-				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-					session_id: "session-123",
-					updated_at: new Date().toISOString(),
-				})
+				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 			})
 
 			it("should not trigger multiple title generations for the same session", async () => {
@@ -790,10 +754,6 @@ describe("SessionManager.syncSession", () => {
 					uploadStarted = true
 					await new Promise((resolve) => setTimeout(resolve, 100))
 					uploadCompleted = true
-					return {
-						session_id: "session-123",
-						updated_at: new Date().toISOString(),
-					}
 				})
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
@@ -821,10 +781,6 @@ describe("SessionManager.syncSession", () => {
 				vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
 				vi.mocked(manager.sessionClient!.uploadBlob).mockImplementation(async () => {
 					await new Promise((resolve) => setTimeout(resolve, 50))
-					return {
-						session_id: "session-123",
-						updated_at: new Date().toISOString(),
-					}
 				})
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file1.json")
@@ -857,10 +813,6 @@ describe("SessionManager.syncSession", () => {
 					syncInProgress = true
 					await new Promise((resolve) => setTimeout(resolve, 50))
 					syncInProgress = false
-					return {
-						session_id: "session-123",
-						updated_at: new Date().toISOString(),
-					}
 				})
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file1.json")
@@ -886,10 +838,6 @@ describe("SessionManager.syncSession", () => {
 				vi.mocked(manager.sessionClient!.uploadBlob).mockImplementation(async () => {
 					uploadTimestamps.push(Date.now())
 					await new Promise((resolve) => setTimeout(resolve, 30))
-					return {
-						session_id: "session-123",
-						updated_at: new Date().toISOString(),
-					}
 				})
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file1.json")
@@ -925,10 +873,7 @@ describe("SessionManager.syncSession", () => {
 						updated_at: new Date().toISOString(),
 					}
 				})
-				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-					session_id: "session-1",
-					updated_at: new Date().toISOString(),
-				})
+				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file1.json")
 
@@ -959,10 +904,7 @@ describe("SessionManager.syncSession", () => {
 						updated_at: new Date().toISOString(),
 					}
 				})
-				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-					session_id: "session-1",
-					updated_at: new Date().toISOString(),
-				})
+				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 				manager.handleFileUpdate("task-1", "uiMessagesPath", "/path/to/file1.json")
 				manager.handleFileUpdate("task-2", "uiMessagesPath", "/path/to/file2.json")
@@ -990,10 +932,7 @@ describe("SessionManager.syncSession", () => {
 					return `diff-${gitCallCount}`
 				})
 
-				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-					session_id: "session-123",
-					updated_at: new Date().toISOString(),
-				})
+				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
 
@@ -1010,10 +949,7 @@ describe("SessionManager.syncSession", () => {
 			it("should use consistent git state hash for deduplication", async () => {
 				vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 				vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-					session_id: "session-123",
-					updated_at: new Date().toISOString(),
-				})
+				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 				mockGit.revparse.mockResolvedValue("same-commit")
 				mockGit.diff.mockResolvedValue("same-diff")
@@ -1042,10 +978,7 @@ describe("SessionManager.syncSession", () => {
 			it("should skip interval sync when pendingSync exists", async () => {
 				vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 				vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-					session_id: "session-123",
-					updated_at: new Date().toISOString(),
-				})
+				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 				const existingPromise = new Promise<void>((resolve) => setTimeout(resolve, 200))
 				setPendingSync(existingPromise)
@@ -1060,10 +993,7 @@ describe("SessionManager.syncSession", () => {
 			it("should clear pendingSync after sync completes via direct call", async () => {
 				vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 				vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-					session_id: "session-123",
-					updated_at: new Date().toISOString(),
-				})
+				vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
 
@@ -1082,10 +1012,6 @@ describe("SessionManager.syncSession", () => {
 				let pendingSyncDuringUpload: Promise<void> | null = null
 				vi.mocked(manager.sessionClient!.uploadBlob).mockImplementation(async () => {
 					pendingSyncDuringUpload = getIsSyncing() ? Promise.resolve() : null
-					return {
-						session_id: "session-123",
-						updated_at: new Date().toISOString(),
-					}
 				})
 
 				manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
@@ -1101,10 +1027,7 @@ describe("SessionManager.syncSession", () => {
 		it("should trigger final sync on destroy", async () => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file.json")
 
@@ -1131,10 +1054,7 @@ describe("SessionManager.syncSession", () => {
 		it("should flush queue items during destroy", async () => {
 			vi.mocked(manager.sessionPersistenceManager!.getSessionForTask).mockReturnValue("session-123")
 			vi.mocked(readFileSync).mockReturnValue(JSON.stringify([]))
-			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue({
-				session_id: "session-123",
-				updated_at: new Date().toISOString(),
-			})
+			vi.mocked(manager.sessionClient!.uploadBlob).mockResolvedValue(undefined)
 
 			manager.handleFileUpdate("task-123", "uiMessagesPath", "/path/to/file1.json")
 			manager.handleFileUpdate("task-123", "apiConversationHistoryPath", "/path/to/file2.json")