Version: morph-compact@0.2.8 (Claude Code plugin, installed via claude plugin install morph-compact@morph)
Summary
hooks/lib/transcript.js reads block.input when extracting tool_result content blocks. Anthropic tool_result blocks carry their payload in block.content, not block.input — input is a field on tool_use. The condition is therefore always false and every tool result in the transcript is silently dropped before the history is sent to the Compact API.
Net effect: Morph only ever receives assistant/user prose plus [tool: name] breadcrumbs. No file contents, no command output, no error messages, no search results. For a coding agent that is the majority of the useful context, and it's exactly the material that most benefits from verbatim compaction rather than lossy re-summarization.
Fails silently — no error, no warning. The only visible symptom is an implausibly small inputChars in the state file written by pre-compact.js.
Root cause
hooks/lib/transcript.js, in extractText():
case "tool_result":
if (typeof block.input === "string" && block.input) { // <-- always undefined
parts.push(`[result]: ${block.input}`);
}
break;
tool_result blocks in a Claude Code transcript have exactly these keys:
tool_result keys: ['content', 'tool_use_id', 'type']
has 'input': False
block.content is either a string or an array of content blocks ({type: "text", text: "..."}), depending on the tool.
Evidence
Measured by running the shipped parseTranscript() against a real ~/.claude/projects/<project>/<session>.jsonl, before vs. after fixing the field name:
BEFORE: messages=93 chars=35129
AFTER: messages=137 chars=72302
44 messages consisted entirely of tool results and were dropped in full; total content roughly doubled. That transcript contained 40 tool_use blocks and 40 matching tool_result blocks — none of the results survived extraction.
A separate session recorded stats: {messageCount: 54, inputChars: 7952, ...} — about 147 chars per message, which is what tipped me off.
Repro
- Run a Claude Code session that makes several tool calls producing substantial output.
- Trigger
/compact.
- Inspect the state file written by
pre-compact.js ($TMPDIR/compact-hook/<session_id>) before session-start.js consumes it.
stats.inputChars is far smaller than the real conversation; the resulting summary contains [tool: X] placeholders with no corresponding results.
Suggested patch
case "tool_result": {
// tool_result carries `content` (string | block[]), not `input`
const c = block.content;
if (typeof c === "string") {
if (c) parts.push(`[result]: ${c}`);
} else if (Array.isArray(c)) {
for (const inner of c) {
if (inner?.type === "text" && inner.text) {
parts.push(`[result]: ${inner.text}`);
}
}
}
break;
}
Verified locally: node --check passes and the measurement above is the result of this exact change.
Secondary note (not fixed here)
The tool_use case keeps only [tool: ${block.name}] and discards block.input, so the arguments are lost too — which file was read, which command ran, which pattern was searched. That may well be deliberate compression, but combined with the dropped results it means a compacted session retains no record of what the agent actually did, only that it did something. Worth a second look while the above is being fixed.
Version:
morph-compact@0.2.8(Claude Code plugin, installed viaclaude plugin install morph-compact@morph)Summary
hooks/lib/transcript.jsreadsblock.inputwhen extractingtool_resultcontent blocks. Anthropictool_resultblocks carry their payload inblock.content, notblock.input—inputis a field ontool_use. The condition is therefore always false and every tool result in the transcript is silently dropped before the history is sent to the Compact API.Net effect: Morph only ever receives assistant/user prose plus
[tool: name]breadcrumbs. No file contents, no command output, no error messages, no search results. For a coding agent that is the majority of the useful context, and it's exactly the material that most benefits from verbatim compaction rather than lossy re-summarization.Fails silently — no error, no warning. The only visible symptom is an implausibly small
inputCharsin the state file written bypre-compact.js.Root cause
hooks/lib/transcript.js, inextractText():tool_resultblocks in a Claude Code transcript have exactly these keys:block.contentis either a string or an array of content blocks ({type: "text", text: "..."}), depending on the tool.Evidence
Measured by running the shipped
parseTranscript()against a real~/.claude/projects/<project>/<session>.jsonl, before vs. after fixing the field name:44 messages consisted entirely of tool results and were dropped in full; total content roughly doubled. That transcript contained 40
tool_useblocks and 40 matchingtool_resultblocks — none of the results survived extraction.A separate session recorded
stats: {messageCount: 54, inputChars: 7952, ...}— about 147 chars per message, which is what tipped me off.Repro
/compact.pre-compact.js($TMPDIR/compact-hook/<session_id>) beforesession-start.jsconsumes it.stats.inputCharsis far smaller than the real conversation; the resulting summary contains[tool: X]placeholders with no corresponding results.Suggested patch
Verified locally:
node --checkpasses and the measurement above is the result of this exact change.Secondary note (not fixed here)
The
tool_usecase keeps only[tool: ${block.name}]and discardsblock.input, so the arguments are lost too — which file was read, which command ran, which pattern was searched. That may well be deliberate compression, but combined with the dropped results it means a compacted session retains no record of what the agent actually did, only that it did something. Worth a second look while the above is being fixed.