fix: guard against undefined tool output in caching and pruning#2
Closed
Sathvik-1007 wants to merge 1 commit intosdwolf4103:mainfrom
Closed
fix: guard against undefined tool output in caching and pruning#2Sathvik-1007 wants to merge 1 commit intosdwolf4103:mainfrom
Sathvik-1007 wants to merge 1 commit intosdwolf4103:mainfrom
Conversation
hookOutput.output can be undefined for some tool calls. This undefined value gets cached without a fullOutput field, and later crashes applySmartPruning/keepLast when they call .split() or .length on it. Two fixes: - Nullish coalesce toolOutput to empty string before caching and extraction - Check cached.fullOutput is truthy before passing to applySmartPruning Fixes sdwolf4103#1
Owner
|
Thanks @Sathvik-1007 for the report and fix. This PR targets the old index.ts implementation, and the current codebase has since been refactored/released so this patch no longer applies cleanly to current main. I am closing this stale PR for now. If the same crash still appears on the current version, please open a fresh PR or issue against the latest code. |
sdwolf4103
added a commit
that referenced
this pull request
May 2, 2026
…nce, and validation baseline Wave 1 — Compaction prompt improvement: - Add three wording-reuse bullets to buildCompactionPrompt() under CRITICAL MEMORY RULES: do not create rephrased duplicates, reuse existing wording exactly when re-emitting, only emit new memories when the fact is new, materially corrected, or more specific. - This attacks the root cause of zero reinforcement: compaction generating variant text for the same durable fact. Wave 2 — Bug fixes: - Bug #2: Add placeholder comment to superseded_existing branch in decision dedupe (unreachable until v1.5.4 numbered refs). Preserve as const type assertions. - Bug #3: Add memory_migration_superseded evidence event type. Both P0 and quality cleanup migrations now produce evidence events for superseded entries. loadWorkspaceMemory appends migration evidence on first-load migrations only (idempotent via migration IDs). No historical backfill. - Bug #4: Add documentation comment explaining that feedback identity key returns exact key (absorbed_identity currently impossible for feedback). Add test verifying this behavior. Wave 3 — Validation baseline script: - Add scripts/dev/validate-identity-keys.ts: read-only script that scans workspace memory stores, computes exact/identity key collisions, and reports reinforcement statistics. Baseline matches audit: 0 exact collisions, 0 identity collisions, 0 reinforcement events across 123 active memories. Identity extension is gated on measurement: if the prompt change produces measurable reinforcement (reinforcementCount > 0), identity extension may be unnecessary. Decision dedupe stays exact-only (Wave 4 deferred).
sdwolf4103
added a commit
that referenced
this pull request
May 8, 2026
…nce, and validation baseline Wave 1 — Compaction prompt improvement: - Add three wording-reuse bullets to buildCompactionPrompt() under CRITICAL MEMORY RULES: do not create rephrased duplicates, reuse existing wording exactly when re-emitting, only emit new memories when the fact is new, materially corrected, or more specific. - This attacks the root cause of zero reinforcement: compaction generating variant text for the same durable fact. Wave 2 — Bug fixes: - Bug #2: Add placeholder comment to superseded_existing branch in decision dedupe (unreachable until v1.5.4 numbered refs). Preserve as const type assertions. - Bug #3: Add memory_migration_superseded evidence event type. Both P0 and quality cleanup migrations now produce evidence events for superseded entries. loadWorkspaceMemory appends migration evidence on first-load migrations only (idempotent via migration IDs). No historical backfill. - Bug #4: Add documentation comment explaining that feedback identity key returns exact key (absorbed_identity currently impossible for feedback). Add test verifying this behavior. Wave 3 — Validation baseline script: - Add scripts/dev/validate-identity-keys.ts: read-only script that scans workspace memory stores, computes exact/identity key collisions, and reports reinforcement statistics. Baseline matches audit: 0 exact collisions, 0 identity collisions, 0 reinforcement events across 123 active memories. Identity extension is gated on measurement: if the prompt change produces measurable reinforcement (reinforcementCount > 0), identity extension may be unnecessary. Decision dedupe stays exact-only (Wave 4 deferred).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1 —
TypeError: undefined is not an objectcrash inapplySmartPruning/keepLastwhen cached tool output has nofullOutputfield.Root Cause
hookOutput.outputcan beundefinedfor certain tool calls (e.g. MCP tools that return no output). When cached viacacheToolOutput, thefullOutputfield is omitted from the JSON. On later replay,getCachedToolOutputreturns an object wherefullOutputisundefined, which crashes when passed toapplySmartPruning→.split('\n')orkeepLast→.length.Changes
Two defensive guards in
index.ts:toolOutput ?? ""before storing, sofullOutputis always a string in the cache file. Same guard applied toextractFromToolOutputcall.cached.fullOutputis truthy before passing toapplySmartPruning, skipping records with missing output instead of crashing.Testing
Reproduced the crash by triggering tool calls that return undefined output. After the fix, pruning skips gracefully and no crash occurs.