mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
Bump export schema to v2 with scratchlist text and attachment metadata so operators keep notes when they export-then-delete. Markdown gets a Scratchlist section; attachment bytes stay out of the JSON. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -190,6 +190,71 @@ describe('MessageService goal status filtering', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('includes scratchlist text and attachment metadata in chronological order (tiann/hapi#1235)', () => {
|
||||
const store = makeStore()
|
||||
const session = makeSession(store, 'session-export-scratchlist')
|
||||
|
||||
store.messages.addMessage(session.id, { role: 'user', content: 'Hello' })
|
||||
const older = store.scratchlist.create(session.id, 'Park this idea', {
|
||||
entryId: 'entry-older',
|
||||
createdAt: 1_000,
|
||||
attachments: [{
|
||||
id: 'att-1',
|
||||
filename: 'note.png',
|
||||
mimeType: 'image/png',
|
||||
size: 42,
|
||||
path: 'hapi-hub:scratchlist/att-1'
|
||||
}]
|
||||
})
|
||||
const newer = store.scratchlist.create(session.id, 'Follow up tomorrow', {
|
||||
entryId: 'entry-newer',
|
||||
createdAt: 2_000
|
||||
})
|
||||
expect(older.outcome).toBe('created')
|
||||
expect(newer.outcome).toBe('created')
|
||||
|
||||
const service = new MessageService(store, makeIo(() => {}), makePublisher() as any)
|
||||
const result = service.getSessionExport(session.id, toProtocolSession(session))
|
||||
|
||||
expect(result.type).toBe('success')
|
||||
if (result.type !== 'success') throw new Error('Expected success export')
|
||||
expect(result.payload.schemaVersion).toBe(2)
|
||||
expect(result.payload.scratchlist).toEqual([
|
||||
{
|
||||
entryId: 'entry-older',
|
||||
text: 'Park this idea',
|
||||
createdAt: 1_000,
|
||||
updatedAt: expect.any(Number),
|
||||
attachments: [{
|
||||
id: 'att-1',
|
||||
filename: 'note.png',
|
||||
mimeType: 'image/png',
|
||||
size: 42,
|
||||
path: 'hapi-hub:scratchlist/att-1'
|
||||
}]
|
||||
},
|
||||
{
|
||||
entryId: 'entry-newer',
|
||||
text: 'Follow up tomorrow',
|
||||
createdAt: 2_000,
|
||||
updatedAt: expect.any(Number),
|
||||
attachments: []
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('emits an empty scratchlist array when the session has no notes', () => {
|
||||
const store = makeStore()
|
||||
const session = makeSession(store, 'session-export-no-scratchlist')
|
||||
|
||||
const service = new MessageService(store, makeIo(() => {}), makePublisher() as any)
|
||||
const result = service.getSessionExport(session.id, toProtocolSession(session))
|
||||
|
||||
expect(result.type).toBe('success')
|
||||
if (result.type !== 'success') throw new Error('Expected success export')
|
||||
expect(result.payload.scratchlist).toEqual([])
|
||||
})
|
||||
|
||||
it('pages past hidden-only goal status rows', () => {
|
||||
const store = makeStore()
|
||||
const session = makeSession(store, 'goal-status-pagination')
|
||||
|
||||
@@ -138,13 +138,29 @@ export class MessageService {
|
||||
}
|
||||
}
|
||||
|
||||
// Chronological ASC for archive readability (store list is DESC).
|
||||
const scratchlist = this.store.scratchlist.list(sessionId)
|
||||
.slice()
|
||||
.sort((a, b) => {
|
||||
if (a.createdAt !== b.createdAt) return a.createdAt - b.createdAt
|
||||
return a.entryId < b.entryId ? -1 : a.entryId > b.entryId ? 1 : 0
|
||||
})
|
||||
.map((row) => ({
|
||||
entryId: row.entryId,
|
||||
text: row.text,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
attachments: row.attachments
|
||||
}))
|
||||
|
||||
return {
|
||||
type: 'success',
|
||||
payload: {
|
||||
schemaVersion: HAPI_SESSION_EXPORT_SCHEMA_VERSION,
|
||||
exportedAt: Date.now(),
|
||||
session,
|
||||
messages
|
||||
messages,
|
||||
scratchlist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +137,11 @@ function createApp(session: Session, opts?: {
|
||||
getSessionExport: opts?.getSessionExport ?? (() => ({
|
||||
type: 'success',
|
||||
payload: {
|
||||
schemaVersion: 1,
|
||||
schemaVersion: 2,
|
||||
exportedAt: 1_762_000_000_000,
|
||||
session,
|
||||
messages: []
|
||||
messages: [],
|
||||
scratchlist: []
|
||||
}
|
||||
})),
|
||||
listSlashCommands: opts?.listSlashCommands ?? (async () => ({
|
||||
@@ -191,10 +192,11 @@ describe('sessions routes', () => {
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(await response.json()).toEqual({
|
||||
schemaVersion: 1,
|
||||
schemaVersion: 2,
|
||||
exportedAt: 1_762_000_000_000,
|
||||
session,
|
||||
messages: []
|
||||
messages: [],
|
||||
scratchlist: []
|
||||
})
|
||||
})
|
||||
|
||||
@@ -224,10 +226,11 @@ describe('sessions routes', () => {
|
||||
getSessionExport: () => ({
|
||||
type: 'success',
|
||||
payload: {
|
||||
schemaVersion: 1,
|
||||
schemaVersion: 2,
|
||||
exportedAt: 1_762_000_000_000,
|
||||
session,
|
||||
messages
|
||||
messages,
|
||||
scratchlist: []
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user