feat(trace): opt-in lossless recording tier sharing the event pipeline - #2878
Conversation
Adds a second, opt-in recording tier for the existing trace pipeline. The default tier is byte-identical to today (content-free provenance, 20 KB tool-result cap); the lossless tier persists full LLM request messages/tool schemas (clamped at 500 KB) and raises the tool-result cap to 200 KB, enabling deep debugging and request reconstruction without weakening the default privacy contract. Tier wiring: startRun reads the opt-in 'losslessTrace' storage key once per run (explicit meta.lossless override wins), stamps the run record, and restores the decision after SW eviction from the durable record via peekRunFlags. recordLLMRequest branches on the tier; recordToolCall picks the cap from the run state. Settings UI gains a 'Record full request details (debug tier)' toggle gated on tracing being enabled, with disclosure copy in all 23 locales (en fallback never needed since keys are present everywhere). The Markdown exporter renders bounded, credential-masked message previews for lossless runs only; default-tier exports are unchanged. One pinning test updated for the forced-flag recovery implementation change (peekRunFlags replaces the in-memory lookahead; behavior identical). Mirrored to Firefox. Closes webbrain-one#2876
|
@alectimison-maker is attempting to deploy a commit to the esokullu's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
The lossless tier fails to remain lossless after service-worker recovery and its Markdown masking can expose common credential formats. Request bounds, truncated export handling, tier visibility, and the claimed default-format compatibility also remain incomplete. Full review comments:
|
|
Thanks for the follow-up. I re-reviewed the current head ( What is fixedThe follow-up does address most of the original findings:
What still blocks merge
ValidationThe current head is syntactically clean and the existing suites are green:
Those suites currently do not exercise the raw JSON export, the broader credential-key set, or service-worker recovery followed by immediate finalization. Recommendation: hold merge until the two export/privacy P1s and the finalization race are fixed. The storage-budget scope and documentation should also be resolved explicitly before this PR closes #2876. |
|
Follow-up pushed in 9bc5c04.
Validated: node syntax checks and git diff --check. Full node test/run.js was started, but this environment did not complete it within the available command window; GitHub smoke is queued. Vercel remains an external authorization-required status. |
|
Follow-up pushed in 049daa8: lossless storage now has a 50 MB aggregate budget in addition to the 5 MB per-run cap. After each lossless request/tool write, completed lossless runs are sorted by startedAt and deleted oldest-first via deleteRun until the aggregate is within budget. The active/running run is never selected for eviction. Also updated the recorder event-model assertion for the queue-internal deferred payload path. Syntax checks and git diff --check pass; GitHub checks will be refreshed for this head. |
… tier pins - add recovery to the sensitive-key code group in both catalogs (chrome cloud-runs.js, firefox trace-export.js) so credentials stored as structured args no longer survive sanitizeTraceExport - extend the JSON export test with a structured-args case and a non-sensitive passthrough assertion - update stale lossless-tier pins to the post-recovery write queue shape (losslessBytes stamp, queue-callback tier branch, tool cap)
|
Reviewed Fixed
Suite is green at Non-blocking, fine as follow-ups
|
- evictOldestLosslessRuns keeps a running aggregate so under-budget
writes skip the store-wide scan entirely (one scan per worker
lifetime to seed it, resynced on every eviction pass and clearAllRuns)
- the eviction scan walks every run instead of listRuns({ limit: 500 }),
so older lossless runs count toward the 50 MB budget and stay
evictable once past that window
- budget-reached truncation markers now carry the dropped payload's
true length instead of length: 0
|
Follow-up in
Screenshot blobs remain unmetered — they live in a separate Suite green at |
Keep both sides of the recorder conflict: the lossless tier state loader/request clamp and the SW-eviction repair transaction added on main touch disjoint functions.
Summary
recordLLMRequestpersists the full message/tool shape (clamped at 500 KB) instead of content-free provenance, and the tool-result cap rises from 20 KB to 200 KB. The default tier is byte-identical to today.startRunreads thelosslessTracestorage key once per run (explicitmeta.losslessoverride wins), stampslosslesson the run record, and restores the decision after SW eviction from the durable record (peekRunFlags).Motivation
Closes #2876.
WebBrain's privacy contract records traces without prompts/messages/tool schemas — the right default, but it leaves the single hardest debugging question unanswerable: "what exactly did the model see and say in this failing run?" The recorder had one tier; this adds a second, opt-in tier through the same pipeline, so deep debugging and request reconstruction are possible without weakening the default contract.
Design
startRun(never per event), stored on the run record, and branched on insiderecordLLMRequest/recordToolCall. SW-eviction recovery restores the tier from the durable record.{_truncated, length, head}marker, matching the existing tool-result convention); tool results keep up to 200 KB verbatim. Prevents an opt-in debugging tier from exhausting IndexedDB.llm_requestevents render up to 12 messages with a per-message preview cap and regex masking of credential shapes (sk-...,Bearer ...,api_key=/token=/password=); default runs render no messages. The masking helper is pure and browser-neutral, mirroring the strict-redaction spirit used elsewhere.losslessTrace(default false) with the same storage pattern astracingEnabled; the toggle disables (and clears) when tracing is off. Disclosure copy added to all 23 locales — the i18n en-fallback exists, but key parity is enforced by the locale tests, so keys are present everywhere.Testing
node test/run.js— passed, 1973 tests (5 new: allowlist bounds, recorder tier wiring, settings UI wiring, masked export previews + default-tier no-preview, privacy guard)npm run test:toolbar-guard— passed (33)npm run test:security— passed (60/60)node --checkon every touched file — passedOne upstream pinning test was updated for the forced-flag recovery implementation change:
peekRunFlagsnow restoresforced+losslessfrom the durable run record instead of an extraisForcedTraceRunlookup; behavior is identical (the forced flag still comes from the run record).Skipped: unpacked-browser manual verification (no browser session in this environment); the lossless tier's storage round-trip is covered by source-level and pure-module tests, and runtime IDB behavior in Chrome/Firefox is not manually exercised.
Compatibility and risks
losslessis additive; old runs simply read as default tier.Scope
Deferred (follow-ups): per-run storage budget/eviction, cloud-forced lossless runs, per-message opt-out, encryption at rest.