fix(terminal): truncate console values by size and cycles, not nesting depth#4924
Conversation
…g depth normalizeConsoleValue replaced any value at depth >= 6 with [Truncated object], discarding tiny payloads solely for their position in the tree (agent tool-call rows sit at exactly depth 6). The depth cap was also the only guard against infinite recursion on circular structures. Add a path-tracked WeakSet so true ancestor cycles resolve to [Circular] while values shared across sibling positions still render fully, and raise MAX_DEPTH to 12 as a pathological-nesting backstop. Actual size stays bounded by the existing 50KB string cap and 256KB byte cap.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview
New unit tests cover tool-call-shaped nesting, cycles, shared references, depth backstop, and existing byte/string caps. Reviewed by Cursor Bugbot for commit 18b10a6. Configure here. |
Greptile SummaryThis PR fixes premature
Confidence Score: 5/5Safe to merge — the change is display-only, the algorithm is the standard ancestor-chain pattern, and the unit tests cover all the targeted edge cases. The path-tracking WeakSet (add → recurse → delete in finally) is the correct standard algorithm for distinguishing true ancestor cycles from shared sibling references. The depth check short-circuits cleanly before the cycle check, so the two guards remain orthogonal. MAX_DEPTH=12 leaves a large enough safety margin while eliminating the spurious truncation at depth 6. safeConsoleStringify (unchanged) still uses a global non-path-tracking set, but since normalizeConsoleValue always materialises fresh output objects, that function is never handed shared-reference data in any of the three call sites. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[normalizeConsoleValue called] --> B{Primitive or null or Error?}
B -- yes --> C[Return early]
B -- no --> D{depth >= MAX_DEPTH?}
D -- yes --> E[Return Truncated marker]
D -- no --> F{seen.has objectValue?}
F -- yes --> G[Return Circular marker]
F -- no --> H[seen.add objectValue]
H --> I{Array?}
I -- yes --> J[Recurse items with depth+1 and seen]
I -- no --> K[Recurse values with depth+1 and seen]
J --> L[finally: seen.delete objectValue]
K --> L
L --> M[Return normalised value]
Reviews (1): Last reviewed commit: "fix(terminal): truncate console values b..." | Re-trigger Greptile |
Summary
[Truncated object]for tiny payloads (e.g. a ~400-byte CRM deal row) becausenormalizeConsoleValuereplaced any value at nesting depth ≥ 6 with a marker — agent tool-call rows sit at exactly depth 6 (output → toolCalls → list[] → call → result → rows[] → row), so they died purely from position, not size.normalizeConsoleValuehad no cycle protection of its own).WeakSetso true ancestor cycles resolve to[Circular]while values shared across sibling positions still render fully, and raisedMAX_DEPTH6 → 12 as a pathological-nesting backstop. Actual size stays bounded by the existing 50KB per-string cap and 256KB whole-value byte cap.Type of Change
Testing
[Circular], shared sibling ref renders fully, backstop still truncates pastMAX_DEPTH, byte cap still independent.bunx vitest run stores/terminal/console/utils.test.ts stores/terminal/console/store.test.ts— 15 passed.Checklist