the problem #
metrics.db was multiple GB of body text. every turn stored the entire messages[] array twice - once as a standalone prompt metric, again inside prompt_response (which also stored the full response object). ~400KB/row, kept for a /metrics/:id detail view the dashboard never renders.
the fix #
the list view only ever derives messageCount, lastUserMessage, responsePreview, toolCallCount, model, usage. so:
- drop the redundant standalone
promptmetric (fully subsumed byprompt_response) - compute the summary fields at write time via
summarizePromptResponse()and persist only those - never raw messages/response - remove the dead prompt bucket / list-type / cursor plumbing
each row drops from ~400KB to a few hundred bytes. verified the stored payload contains no messages[] and no full response body; dashboard list fields unchanged; typecheck clean.
note #
separately, the retention prune (pruneOldMetrics, 3-day default) deletes rows but never VACUUMs, so freed pages linger as freelist - that's what let the file sit at 8.2GB even after pruning. worth a periodic VACUUM in the prune path (not in this PR).