Some agents (Pi, and others for injected turns) write a message's `content`
as a string instead of an array of blocks. Parsers did `(content ?? []).filter`,
which throws on a string — and because the daily-cache backfill swallowed parse
errors, one bad session silently wiped the entire trend/history.
- Add normalizeContentBlocks(): string -> one text block, array -> as-is (by
reference; drops null/undefined elements so the same crash can't happen one
level down), else -> []. Applied across pi/codex/droid/cursor-agent and the
Claude path in parser.ts.
- Isolate per-file parse failures in parseProviderSources: skip the offending
file (warn once per provider) instead of re-throwing and aborting the whole
backfill. The stale cache entry is already cleared, so the file is excluded.
- Surface backfill failures in hydrateCache via stderr instead of silently
returning an empty cache.
Likely fixes#425 (previous-day always 0) for the throwing-file cause.
Tests: content-utils unit tests + a Pi string-content regression test.