Skip to content

Commit 94eab0d

Browse files
committed
fix(chat): persist the source names a selection chip renders from
Cursor Bugbot: fileName/tableName were mapped into the optimistic message and accepted by the API schema, but PersistedMessageContext, buildPersistedUserMessage and toDisplayContexts never carried them. After a reload a file_selection chip fell back to its label for getDocumentIcon — and the label carries a location suffix ('notes.md:12-40'), so extension detection broke. Persists only the two names the display path reads. The rest of the payload (text, rowIds, columnIds, line numbers) stays unpersisted on purpose: it exists to resolve the selection server-side at send time, is never re-read when rendering a past message, and would put a selection-sized blob — up to the 20k char cap — in every stored message. Test asserts both halves of that, and was verified to fail with the mapping removed.
1 parent 1392a74 commit 94eab0d

3 files changed

Lines changed: 59 additions & 0 deletions

File tree

apps/sim/lib/copilot/chat/display-message.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ function toDisplayContexts(
130130
...(c.blockType ? { blockType: c.blockType } : {}),
131131
...(c.skillId ? { skillId: c.skillId } : {}),
132132
...(c.serverId ? { serverId: c.serverId } : {}),
133+
...(c.fileName ? { fileName: c.fileName } : {}),
134+
...(c.tableName ? { tableName: c.tableName } : {}),
133135
}))
134136
}
135137

apps/sim/lib/copilot/chat/persisted-message.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,47 @@ describe('persisted-message', () => {
235235
expect(msg.fileAttachments).toBeUndefined()
236236
expect(msg.contexts).toBeUndefined()
237237
})
238+
239+
it('persists the source names a selection chip renders from, but not its payload', () => {
240+
const msg = buildPersistedUserMessage({
241+
id: 'user-1',
242+
content: 'explain this',
243+
contexts: [
244+
{
245+
kind: 'file_selection',
246+
label: 'notes.md:12-40',
247+
fileId: 'f1',
248+
fileName: 'notes.md',
249+
// Send-time payload: resolved server-side, never re-read for display.
250+
text: 'the exact passage',
251+
startLine: 12,
252+
endLine: 40,
253+
},
254+
{
255+
kind: 'table_selection',
256+
label: 'Sales (2 rows)',
257+
tableId: 't1',
258+
tableName: 'Sales',
259+
rowIds: ['r1', 'r2'],
260+
},
261+
],
262+
})
263+
264+
// fileName must survive: the label carries a `:12-40` suffix, so the chip's
265+
// icon cannot recover an extension from it after a reload.
266+
expect(msg.contexts?.[0]).toEqual({
267+
kind: 'file_selection',
268+
label: 'notes.md:12-40',
269+
fileId: 'f1',
270+
fileName: 'notes.md',
271+
})
272+
expect(msg.contexts?.[1]).toEqual({
273+
kind: 'table_selection',
274+
label: 'Sales (2 rows)',
275+
tableId: 't1',
276+
tableName: 'Sales',
277+
})
278+
})
238279
})
239280

240281
describe('stripToolResultOutput', () => {

apps/sim/lib/copilot/chat/persisted-message.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ interface PersistedMessageContext {
7878
blockType?: string
7979
skillId?: string
8080
serverId?: string
81+
/**
82+
* Source names for `file_selection` / `table_selection` chips. Persisted
83+
* because the rendered chip reads them — the label carries a location suffix
84+
* (`notes.md:12-40`), so the file icon cannot derive an extension from it.
85+
*
86+
* The rest of a selection's payload (`text`, `rowIds`, `columnIds`, line
87+
* numbers) is deliberately NOT persisted: it exists to resolve the selection
88+
* server-side when the message is sent, is never read when re-rendering a past
89+
* message, and would put a selection-sized blob in every stored message.
90+
*/
91+
fileName?: string
92+
tableName?: string
8193
}
8294

8395
export interface PersistedMessage {
@@ -359,6 +371,8 @@ export function buildPersistedUserMessage(params: UserMessageParams): PersistedM
359371
...(c.blockType ? { blockType: c.blockType } : {}),
360372
...(c.skillId ? { skillId: c.skillId } : {}),
361373
...(c.serverId ? { serverId: c.serverId } : {}),
374+
...(c.fileName ? { fileName: c.fileName } : {}),
375+
...(c.tableName ? { tableName: c.tableName } : {}),
362376
}))
363377
}
364378

@@ -680,6 +694,8 @@ export function normalizeMessage(raw: Record<string, unknown>): PersistedMessage
680694
...(c.blockType ? { blockType: c.blockType } : {}),
681695
...(c.skillId ? { skillId: c.skillId } : {}),
682696
...(c.serverId ? { serverId: c.serverId } : {}),
697+
...(c.fileName ? { fileName: c.fileName } : {}),
698+
...(c.tableName ? { tableName: c.tableName } : {}),
683699
}))
684700
}
685701

0 commit comments

Comments
 (0)