Implement two-stage content codec: truncate oversized agent messages
(48KB head + 12KB tail + marker, idempotent, never user messages) and
compress message content JSON ≥256 bytes via zstd with fallback.
- contentCodec.ts: new codec with truncation and compression routines
- messages.ts: wire codec into addMessage (truncate+compress),
copyMessageToSession (lossless), toStoredMessage (decode both formats)
- index.ts: schema version 15→16 with no-op migration (enforces
schema match on downgrade, no DDL change)
- codexDesktop.ts: pass content through idempotent truncation for
canonical comparison in transcript-import
- cleanup-sessions.ts: decode compressed rows, replace full-scan with
per-session batched scan (LIMIT 50) stopping at first user message
- compact-db.ts: new offline compactor that retroactively applies
truncate+compress+VACUUM to existing DBs; guards against schema
version mismatch, symlink aliasing, and handles re-runs
- Tests: contentCodec round-trip + truncation + idempotence + legacy
decode; messages integration (compressed/truncated round-trip);
migration tests updated to version 16
- README.md: document codec and both maintenance scripts
Measured on 2.26GB production DB: 2159MB→768MB (-64%) in 12s,
748k messages intact, integrity verified.
* fix(hub+cli): defer mergeSessions on cursor ACP reopen until session/load succeeds
Emit session-ready from the CLI after ACP load/newSession completes; hub
resumeSession and cursor dedup wait for that signal before merging rows so a
failed session/load no longer deletes the archived session the operator can retry.
Refs #917. Closes#939.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(hub): gate session-ready wait on cursor ACP protocol only
Legacy stream-json Cursor resumes use cursorLegacyRemoteLauncher, which does
not emit session-ready; limiting the defer-merge and dedup gates to ACP avoids
60s resume_failed timeouts on those sessions.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(hub): block ACP dedup until session-ready, including on session-end
Inactive ACP spawns that never emitted session-ready could still trigger
deduplicateByAgentSessionId on session-end and delete the original row.
Require session-ready for all ACP dedup paths and skip end-of-session dedup
when load never succeeded.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(hub): restore session-end dedup for non-ACP cursor duplicates
Only skip the session-end dedup retry for Cursor ACP rows that never emitted
session-ready. Codex/Claude/legacy Cursor duplicates still merge when the live
row ends.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>