fix(desktop): stop message age falling back to the 1970 epoch ("20663d ago")

This commit is contained in:
Brooklyn Nicholson 2026-07-28 20:33:25 -05:00
parent 76173ba8cd
commit 64bdbd7f3c
2 changed files with 36 additions and 3 deletions

View file

@ -6,6 +6,7 @@ import {
attachmentDisplayText,
attachmentId,
coerceThinkingText,
messageCreatedAt,
optimisticAttachmentRef,
parseCommandDispatch,
parseSlashCommand
@ -178,3 +179,24 @@ describe('attachmentId', () => {
expect(attachmentId('url', 'https://example.com/a')).not.toBe(attachmentId('url', 'https://example.com/b'))
})
})
describe('messageCreatedAt', () => {
const NOW = Date.UTC(2026, 6, 28, 18, 0, 0)
it('reads the authoritative Unix-seconds timestamp (not ms)', () => {
// 1785282262s → July 2026, not the 1970 epoch a *1000-less read would give.
expect(messageCreatedAt({ timestamp: 1785282262 }, NOW).getFullYear()).toBe(2026)
})
it('falls back to now — never digs digits out of the id → "20663d ago" (1970)', () => {
// The old fallback did `new Date(Number(id.match(/\d+/)))`, so a session-style
// id like 20260728_184420_05e697 parsed to 20260728 *ms* = Jan 1970, showing
// as an absurd 20663-day age. A timestamp-less message is freshly created.
expect(messageCreatedAt({ timestamp: undefined }, NOW).getTime()).toBe(NOW)
})
it('treats a zero / non-finite timestamp as absent', () => {
expect(messageCreatedAt({ timestamp: 0 }, NOW).getTime()).toBe(NOW)
expect(messageCreatedAt({ timestamp: Number.NaN }, NOW).getTime()).toBe(NOW)
})
})

View file

@ -368,13 +368,24 @@ export function quickModelOptions(
return options.slice(0, 8)
}
// A message's display time. `timestamp` (Unix seconds) is authoritative when
// present. Without it we fall back to *now* rather than digging digits out of
// the id: message ids come in incompatible shapes — `assistant-<ms>`,
// `<seconds>-<i>-<role>`, session-style `20260728_184420_…` — and feeding any
// of them to `new Date()` (which reads ms) lands on the 1970 epoch, rendering
// as an absurd "20663d ago". A timestamp-less message is a freshly created
// optimistic/streaming one, so *now* is the right age anyway.
export function messageCreatedAt(message: Pick<ChatMessage, 'timestamp'>, nowMs = Date.now()): Date {
return typeof message.timestamp === 'number' && Number.isFinite(message.timestamp) && message.timestamp > 0
? new Date(message.timestamp * 1000)
: new Date(nowMs)
}
export function toRuntimeMessage(message: ChatMessage): ThreadMessage {
const role =
message.role === 'user' || message.role === 'assistant' || message.role === 'system' ? message.role : 'assistant'
const createdAt = message.timestamp
? new Date(message.timestamp * 1000)
: new Date(Number(message.id.match(/\d+/)?.[0]) || Date.now())
const createdAt = messageCreatedAt(message)
if (role === 'user') {
return {