Summary
The safeguard compaction mode (compaction.mode: "safeguard") excludes toolResult entries from valid cut points in findValidCutPoints(). This causes compaction to fail (producing empty fallback summaries) for sessions dominated by tool calls — exactly the pattern seen in long-running agentic tasks and cron jobs.
Problem to solve
findValidCutPoints() only accepts these entry types as valid cut points:
user
assistant
bashExecution
custom
branchSummary
compactionSummary
toolResult is explicitly excluded.
When a session's messages consist primarily of toolCall → toolResult pairs (with few or no user/assistant text messages), findCutPoint() returns no valid cut point. This triggers the safeguard's "no real conversation messages to summarize" branch, writing an empty fallback summary like:
## Decisions
No prior history.
## Open TODOs
None.
...
The compaction then fails to produce a meaningful summary, and the session cannot be extended via compaction.
Proposed solution
Add toolResult to the list of valid cut points in findValidCutPoints():
// src/core/compaction/compaction.ts (or equivalent location)
const validCutPointTypes = [
'user',
'assistant',
'bashExecution',
'custom',
'branchSummary',
'compactionSummary',
'toolResult', // ← Add this
];
Alternatives considered
If enabling toolResult as a cut point by default is deemed too risky, an alternative would be to make it configurable:
{
"agents": {
"defaults": {
"compaction": {
"toolResultAsCutPoint": true // ← New config option
}
}
}
}
Pros of the configurable approach:
- Allows users to opt in based on their specific use case (agentic tasks vs. simple chat)
- Provides a safe default for existing users while enabling the fix for those who need it
- Easier to roll out and test in production without risking regressions for all users
Recommended default: true for mode: "safeguard" (since safeguard is already the more aggressive mode), or false for backward compatibility.
However, the simpler fix (adding toolResult to valid cut points unconditionally) should work because:
- The compaction splitting logic already ensures tool call pairs stay together (it moves the boundary if it lands inside a tool block)
- Cutting after a
toolResult naturally preserves the preceding toolCall
Impact
This affects:
- Long-running agentic tasks that execute many tool calls (read, exec, api calls) with minimal user text input
- Cron sessions that perform automated workflows with tool-heavy execution
- Any session where tool results dominate the conversation history
Even after the fix for #78300 (which added custom-message, bash, and branch-summary as anchors), sessions without explicit user/assistant text messages still fail to compact properly.
Evidence/examples
Session Analysis
A typical affected session (e.g., from E2E testing):
- 7 safeguard compaction attempts
- All compactions have
firstKeptEntryId pointing to session header
findCutPoint() returns no valid cut point
messagesToSummarize is empty → boundary-only compaction
- No training export produced (no valid sample)
Related Issues
Rationale
-
Tool results carry semantic content: They contain the actual output of tool calls (file contents, command output, API responses) — this is often the most valuable information to preserve in a summary.
-
Tool call pairs are atomic: A toolCall and its matching toolResult should be kept together. Using toolResult as a cut point naturally preserves this pairing (the cut happens after the result, keeping the full call+result unit).
-
Consistent with existing anchors: bashExecution is already a valid cut point, and it's semantically similar to toolResult (both are execution outputs).
-
Fixes the agentic task gap: Long-running agentic tasks are exactly the use case where compaction should work, but they're currently blocked by this limitation.
Additional information
Environment
- OpenClaw version: 2026.5.5 (latest main)
- Config:
compaction.mode: "safeguard"
- Affected session types: Agentic tasks, cron jobs, tool-heavy workflows
Related
Summary
The safeguard compaction mode (
compaction.mode: "safeguard") excludestoolResultentries from valid cut points infindValidCutPoints(). This causes compaction to fail (producing empty fallback summaries) for sessions dominated by tool calls — exactly the pattern seen in long-running agentic tasks and cron jobs.Problem to solve
findValidCutPoints()only accepts these entry types as valid cut points:userassistantbashExecutioncustombranchSummarycompactionSummarytoolResultis explicitly excluded.When a session's messages consist primarily of
toolCall→toolResultpairs (with few or no user/assistant text messages),findCutPoint()returns no valid cut point. This triggers the safeguard's "no real conversation messages to summarize" branch, writing an empty fallback summary like:The compaction then fails to produce a meaningful summary, and the session cannot be extended via compaction.
Proposed solution
Add
toolResultto the list of valid cut points infindValidCutPoints():Alternatives considered
If enabling
toolResultas a cut point by default is deemed too risky, an alternative would be to make it configurable:{ "agents": { "defaults": { "compaction": { "toolResultAsCutPoint": true // ← New config option } } } }Pros of the configurable approach:
Recommended default:
trueformode: "safeguard"(since safeguard is already the more aggressive mode), orfalsefor backward compatibility.However, the simpler fix (adding
toolResultto valid cut points unconditionally) should work because:toolResultnaturally preserves the precedingtoolCallImpact
This affects:
Even after the fix for #78300 (which added
custom-message,bash, andbranch-summaryas anchors), sessions without explicit user/assistant text messages still fail to compact properly.Evidence/examples
Session Analysis
A typical affected session (e.g., from E2E testing):
firstKeptEntryIdpointing to session headerfindCutPoint()returns no valid cut pointmessagesToSummarizeis empty → boundary-only compactionRelated Issues
custom-message,bash,branch-summaryas anchors, but did not includetoolResultRationale
Tool results carry semantic content: They contain the actual output of tool calls (file contents, command output, API responses) — this is often the most valuable information to preserve in a summary.
Tool call pairs are atomic: A
toolCalland its matchingtoolResultshould be kept together. UsingtoolResultas a cut point naturally preserves this pairing (the cut happens after the result, keeping the full call+result unit).Consistent with existing anchors:
bashExecutionis already a valid cut point, and it's semantically similar totoolResult(both are execution outputs).Fixes the agentic task gap: Long-running agentic tasks are exactly the use case where compaction should work, but they're currently blocked by this limitation.
Additional information
Environment
compaction.mode: "safeguard"Related