feat(web,hub): export session conversation (#808)

* test: reproduce issue #793

* fix: add session conversation export (closes #793)

* fix(hub): sort session export by display time for invoked scheduled messages

Export now uses COALESCE(invoked_at, created_at) ordering so JSON/Markdown
exports match the visible chat chronology after scheduled messages are invoked.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): escape newlines in session export YAML front matter

Prevent session metadata containing newlines or quotes from breaking
Markdown export front matter.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
SSU-WEI HUANG
2026-06-05 21:40:59 +08:00
committed by GitHub
co-authored by Cursor
parent dbc9646d06
commit f086949a8a
18 changed files with 901 additions and 3 deletions
+5
View File
@@ -13,6 +13,7 @@ import type { AgentFlavor, CodexCollaborationMode, DecryptedMessage, PermissionM
import { unwrapRoleWrappedRecordEnvelope } from '@hapi/protocol/messages'
import type { Server } from 'socket.io'
import type { Store, CancelQueuedMessageResult } from '../store'
import type { HapiSessionExportResult } from '@hapi/protocol/sessionExport'
import type { RpcRegistry } from '../socket/rpcRegistry'
import type { SSEManager } from '../sse/sseManager'
import { EventPublisher, type SyncEventListener } from './eventPublisher'
@@ -243,6 +244,10 @@ export class SyncEngine {
return this.messageService.getMessagesPage(sessionId, options)
}
getSessionExport(sessionId: string, session: Session): HapiSessionExportResult {
return this.messageService.getSessionExport(sessionId, session)
}
getDeliverableMessagesAfter(sessionId: string, options: { afterSeq: number; limit: number; now: number }): DecryptedMessage[] {
return this.messageService.getDeliverableMessagesAfter(sessionId, options)
}