feat(web,hub): export session conversation#808
Conversation
There was a problem hiding this comment.
Findings
- [Major] Export can reorder invoked scheduled messages —
getAllMessages()reads by insertionseq, but scheduled local messages are inserted when queued and getinvokedAtonly when released/consumed. Once such a message is invoked, this export includes it at its old insertion position, while the visible thread pagination orders byCOALESCE(invoked_at, created_at), seq; JSON/Markdown exports can therefore disagree with the visible conversation chronology. Evidencehub/src/sync/messageService.ts:99, related contexthub/src/store/messages.ts:221.
Suggested fix:const messages = this.store.messages.getAllMessages(sessionId) .filter(isExportVisibleStoredMessage) .sort((a, b) => { const aAt = a.invokedAt ?? a.createdAt const bAt = b.invokedAt ?? b.createdAt return aAt !== bAt ? aAt - bAt : a.seq - b.seq }) .map(toDecryptedMessage)
Summary
- Review mode: initial
- One issue found: export ordering can diverge from the visible chat for scheduled messages after they are invoked.
Testing
- Not run (automation). Add a hub test where a scheduled message is inserted first, a normal message is inserted second, then the scheduled message is marked invoked later; export should order by invoked/display time.
HAPI Bot
| session: Session, | ||
| limit: number = SESSION_EXPORT_MESSAGE_LIMIT | ||
| ): HapiSessionExportResult { | ||
| const messages = this.store.messages.getAllMessages(sessionId) |
There was a problem hiding this comment.
[MAJOR] getAllMessages() returns rows by insertion seq, but scheduled local messages are inserted when queued and receive invokedAt later. After release/consume, this export includes them at their old insertion position, unlike the visible thread which orders by COALESCE(invoked_at, created_at), seq. That makes JSON/Markdown exports disagree with the visible chronology.
Suggested fix:
const messages = this.store.messages.getAllMessages(sessionId)
.filter(isExportVisibleStoredMessage)
.sort((a, b) => {
const aAt = a.invokedAt ?? a.createdAt
const bAt = b.invokedAt ?? b.createdAt
return aAt !== bAt ? aAt - bAt : a.seq - b.seq
})
.map(toDecryptedMessage)…essages 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>
There was a problem hiding this comment.
Findings
- [Minor] Markdown front matter can be broken by session metadata containing newlines —
escapeYamlString()only escapes backslashes and quotes, butmetadata.name,metadata.path, andmetadata.hostare user/session-controlled strings that can contain\n/\r. When those values are interpolated into quoted YAML scalars, a newline can terminate the field and inject extra front matter keys or produce invalid Markdown metadata. Evidenceweb/src/lib/sessionExport/markdown.ts:18.
Suggested fix:function escapeYamlString(value: string): string { return value .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\r/g, '\\r') .replace(/\n/g, '\\n') }
Summary
- Review mode: follow-up after new commits
- One minor issue found in Markdown export front matter escaping. Prior bot finding about scheduled-message export ordering appears addressed by the new sort and regression test.
Testing
- Not run (automation):
bunis not installed in this runner (/bin/bash: bun: command not found). Add a markdown export test with a session title containing a newline and quote.
HAPI Bot
| return payload.session.id.slice(0, 8) | ||
| } | ||
|
|
||
| function escapeYamlString(value: string): string { |
There was a problem hiding this comment.
[MINOR] Markdown front matter can be broken by session metadata containing newlines. escapeYamlString() only escapes backslashes and quotes, but metadata.name, metadata.path, and metadata.host are user/session-controlled strings that can contain \n/\r; interpolating those into quoted YAML scalars can terminate the field and inject extra front matter keys or produce invalid metadata.
Suggested fix:
function escapeYamlString(value: string): string {
return value
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
}Prevent session metadata containing newlines or quotes from breaking Markdown export front matter. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Findings
None.
Questions
None.
Summary
Review mode: follow-up after new commits
No issues found in the latest full diff. The previous front-matter escaping finding is addressed in current head with CR/LF escaping and a regression test. Residual risk: download dialog behavior, AbortController cancellation, and persisted format preference are source-reviewed/unit-reviewed only; no browser-level export flow test found in repo/docs.
Testing
Not run (automation): bun is not installed in this runner (/bin/bash: bun: command not found).
HAPI Bot
Closes #793
Summary
HapiSessionExportv1 shared schema/types and a guardedGET /api/sessions/:id/exporthub endpoint.normalizeDecryptedMessagewith concise tool summaries.Test plan
bun typecheckbun run --cwd hub test src/web/routes/sessions.test.ts src/sync/messageService.test.ts --bail=1bun run --cwd web test src/lib/sessionExport/markdown.test.ts --runbun run test:hubbun run test:webbun run test:sharedbun run --cwd cli test src/claude/claudeRemote.test.ts --run(rerun after unrelated CLI timeout flake)Note: A later full
bun typecheck && bun run testattempt was interrupted/ignored at user request after hitting an unrelated CLI timeout flake insrc/claude/claudeRemote.seam.test.ts; this PR does not touch CLI code.Manual verification
fix/issue-793-session-exporton port 43006.