Skip to content

Commit 50098a3

Browse files
authored
fix(ipc): allow attachment date metadata (#1547)
1 parent a5d1efa commit 50098a3

5 files changed

Lines changed: 112 additions & 4 deletions

File tree

src/renderer/src/stores/ui/session.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ export const useSessionStore = defineStore('session', () => {
591591
pageRouter.goToChat(session.id)
592592
} catch (createError) {
593593
error.value = `Failed to create session: ${createError}`
594+
throw createError
594595
}
595596
}
596597

@@ -649,6 +650,7 @@ export const useSessionStore = defineStore('session', () => {
649650
await chatClient.sendMessage(sessionId, content)
650651
} catch (sendError) {
651652
error.value = `Failed to send message: ${sendError}`
653+
throw sendError
652654
}
653655
}
654656

src/shared/contracts/common.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export const JsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>
3131
])
3232
)
3333

34+
export const FileMetadataValueSchema = z.union([JsonValueSchema, z.date()])
35+
3436
export const AppErrorSchema = z.object({
3537
code: z.string(),
3638
message: z.string(),
@@ -76,7 +78,7 @@ export const MessageFileSchema = z.object({
7678
mimeType: z.string().optional(),
7779
token: z.number().optional(),
7880
thumbnail: z.string().optional(),
79-
metadata: z.record(JsonValueSchema).optional()
81+
metadata: z.record(FileMetadataValueSchema).optional()
8082
})
8183

8284
export const SendMessageInputSchema = z.object({

src/shared/contracts/domainSchemas.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from 'zod'
22
import { BrowserPageStatus } from '../types/browser'
33
import { ApiEndpointType, ModelType, NEW_API_ENDPOINT_TYPES } from '../model'
4-
import { JsonValueSchema, ProviderModelSummarySchema } from './common'
4+
import { FileMetadataValueSchema, JsonValueSchema, ProviderModelSummarySchema } from './common'
55
import {
66
ReasoningEffortSchema,
77
ReasoningModeSchema,
@@ -377,8 +377,6 @@ export const ConfigValueSchema = z.union([
377377
JsonValueSchema
378378
])
379379

380-
const FileMetadataValueSchema = z.union([JsonValueSchema, z.date()])
381-
382380
export const PreparedMessageFileSchema = z.object({
383381
name: z.string(),
384382
path: z.string(),

test/main/routes/contracts.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,53 @@ describe('main kernel contracts', () => {
201201
})
202202
})
203203

204+
it('accepts prepared attachment metadata dates in message route contracts', () => {
205+
const fileCreated = new Date('2024-01-01T00:00:00.000Z')
206+
const fileModified = new Date('2024-01-02T00:00:00.000Z')
207+
const pdfAttachment = {
208+
name: 'sample.pdf',
209+
path: '/tmp/sample.pdf',
210+
mimeType: 'application/pdf',
211+
content: '# PDF file description',
212+
token: 128,
213+
metadata: {
214+
fileName: 'sample.pdf',
215+
fileSize: 1024,
216+
fileDescription: 'PDF Document',
217+
fileCreated,
218+
fileModified
219+
}
220+
}
221+
222+
expect(
223+
sessionsCreateRoute.input.parse({
224+
agentId: 'deepchat',
225+
message: 'summarize this',
226+
files: [pdfAttachment]
227+
})
228+
).toEqual({
229+
agentId: 'deepchat',
230+
message: 'summarize this',
231+
files: [pdfAttachment]
232+
})
233+
234+
expect(
235+
chatSendMessageRoute.input.parse({
236+
sessionId: 'session-1',
237+
content: {
238+
text: 'summarize this',
239+
files: [pdfAttachment]
240+
}
241+
})
242+
).toEqual({
243+
sessionId: 'session-1',
244+
content: {
245+
text: 'summarize this',
246+
files: [pdfAttachment]
247+
}
248+
})
249+
})
250+
204251
it('validates typed provider and tool interaction routes through the shared contract catalog', () => {
205252
expect(
206253
providersListModelsRoute.output.parse({

test/renderer/components/NewThreadPage.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,29 @@ describe('NewThreadPage ACP draft session bootstrap', () => {
300300
expect(sessionStore.createSession).not.toHaveBeenCalled()
301301
})
302302

303+
it('keeps draft input when ACP draft send fails', async () => {
304+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
305+
try {
306+
const { wrapper, sessionStore } = await setup()
307+
const file = { name: 'a.pdf', path: '/tmp/a.pdf', mimeType: 'application/pdf' }
308+
;(wrapper.vm as any).message = 'hello from draft'
309+
;(wrapper.vm as any).attachedFiles = [file]
310+
sessionStore.sendMessage.mockRejectedValueOnce(new Error('send failed'))
311+
312+
await (wrapper.vm as any).onSubmit()
313+
await flushPromises()
314+
315+
expect(sessionStore.sendMessage).toHaveBeenCalledWith('draft-1', {
316+
text: 'hello from draft',
317+
files: [file]
318+
})
319+
expect((wrapper.vm as any).message).toBe('hello from draft')
320+
expect((wrapper.vm as any).attachedFiles).toEqual([file])
321+
} finally {
322+
consoleErrorSpy.mockRestore()
323+
}
324+
})
325+
303326
it('passes draft generation settings when creating a deepchat session', async () => {
304327
const { wrapper, sessionStore, agentStore, modelStore, draftStore } = await setup()
305328

@@ -343,6 +366,42 @@ describe('NewThreadPage ACP draft session bootstrap', () => {
343366
)
344367
})
345368

369+
it('keeps draft input when deepchat session creation fails', async () => {
370+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
371+
try {
372+
const { wrapper, sessionStore, agentStore, modelStore, draftStore } = await setup()
373+
const file = { name: 'a.pdf', path: '/tmp/a.pdf', mimeType: 'application/pdf' }
374+
375+
agentStore.selectedAgentId = 'deepchat'
376+
await flushPromises()
377+
modelStore.enabledModels = [
378+
{
379+
providerId: 'openai',
380+
models: [{ id: 'gpt-4', name: 'GPT-4' }]
381+
}
382+
]
383+
draftStore.providerId = 'openai'
384+
draftStore.modelId = 'gpt-4'
385+
;(wrapper.vm as any).message = 'hello deepchat'
386+
;(wrapper.vm as any).attachedFiles = [file]
387+
sessionStore.createSession.mockRejectedValueOnce(new Error('create failed'))
388+
389+
await (wrapper.vm as any).onSubmit()
390+
await flushPromises()
391+
392+
expect(sessionStore.createSession).toHaveBeenCalledWith(
393+
expect.objectContaining({
394+
message: 'hello deepchat',
395+
files: [file]
396+
})
397+
)
398+
expect((wrapper.vm as any).message).toBe('hello deepchat')
399+
expect((wrapper.vm as any).attachedFiles).toEqual([file])
400+
} finally {
401+
consoleErrorSpy.mockRestore()
402+
}
403+
})
404+
346405
it('awaits full model initialization before creating a deepchat session', async () => {
347406
const { wrapper, sessionStore, agentStore, modelStore, draftStore } = await setup({
348407
modelStoreInitialized: false

0 commit comments

Comments
 (0)