#10456: fix: preserve persona and language continuity in compaction summaries
agents
stale
Cluster:
Compaction Safeguards and Summaries
## Background
I run a Korean-language persona agent on OpenClaw (custom SOUL.md + IDENTITY.md setup). After long conversations, when auto-compaction kicks in, the agent suddenly starts responding in English for a few turns before recovering. The English narration text also leaks into Telegram messages because of `blockStreamingBreak: "text_end"`.
After digging into it, I found the root cause: the SDK's `autoCompact()` in `agent-session.js` hardcodes `customInstructions: undefined` when emitting `session_before_compact`. The summarization prompt and system prompt are both English-only, so the summary always comes out in English. Since the summary gets injected as a `user` message (via `COMPACTION_SUMMARY_PREFIX`), the large block of English text right before the model's next response biases it toward English output.
The system prompt (with SOUL.md etc.) is correctly re-injected every run, so the persona eventually recovers — but for the first few turns after compaction, the agent is broken.
## Approach
The `customInstructions` parameter already exists in the SDK's `generateSummary()` pipeline — it's just never populated during auto-compaction. Since we can't change the SDK directly, this PR works within the safeguard extension layer:
1. **Config field** — adds `compaction.customInstructions` to the agent config schema, so users can provide explicit instructions if needed.
2. **Default instructions** — when no config is set, a `DEFAULT_COMPACTION_INSTRUCTIONS` constant is injected that tells the summarizer to:
- Write the summary body in the conversation's language
- Focus on factual content (what was discussed, decisions made, current state)
- Keep the SDK's required section headers unchanged
- Not translate code, paths, or error messages
3. **Precedence chain** — `event (SDK) → config (runtime) → default constant`, with normalization (trim, empty-string-to-undefined) to prevent blank values from short-circuiting the chain.
4. **All three summarization paths covered** — dropped messages, history, and split-turn prefixes all go through the same resolver. The split-turn path composes the existing `TURN_PREFIX_INSTRUCTIONS` with the resolved instructions.
## Changes
| File | What |
|------|------|
| `compaction-instructions.ts` | New — DEFAULT constant, `resolveCompactionInstructions()`, `composeSplitTurnInstructions()`, Unicode-safe truncation (800 char cap) |
| `compaction-instructions.test.ts` | New — 35 tests covering precedence, normalization edge cases, surrogate pair safety, composition |
| `zod-schema.agent-defaults.ts` | Add `customInstructions` to compaction schema |
| `types.agent-defaults.ts` | Add `customInstructions` to `AgentCompactionConfig` |
| `compaction-safeguard-runtime.ts` | Add `customInstructions` to `CompactionSafeguardRuntimeValue` |
| `extensions.ts` | Pass config value to runtime via `setCompactionSafeguardRuntime()` |
| `compaction-safeguard.ts` | Use `resolveCompactionInstructions()` across all three paths |
## Notes
- Only affects `safeguard` mode — `default` mode is untouched.
- This is an intentional behavior change for safeguard users: summaries will now include language preservation instructions by default. In my testing this significantly improved post-compaction continuity without affecting summary quality.
- The default instructions deliberately avoid persona-specific directives (e.g. "preserve character cues") to prevent the summarizer from injecting persona descriptions into the summary — persona context belongs in the system prompt, not the compaction summary.
- The 800-char cap on custom instructions prevents prompt bloat in the multi-stage summarization pipeline (~200 tokens).
- Truncation uses `Array.from()` to avoid splitting surrogate pairs (emoji, CJK supplementary characters, etc).
## Test plan
- [x] `tsc --noEmit` passes
- [x] All 35 new unit tests pass (precedence, empty strings, whitespace, Unicode truncation, composition)
- [x] Existing compaction-safeguard tests (17) and config tests (2) still pass
- [x] Verified in a live Korean persona session — post-compaction summary now preserves Korean, agent stays in character
Most Similar PRs
#19232: fix(compaction): preserve exact identifiers in summarization
by brandonwise · 2026-02-17
79.8%
#20038: (fix): Compaction: preserve recent context and sync session memory ...
by rodrigouroz · 2026-02-18
79.0%
#11089: feat(compaction): support customInstructions and model override for...
by p697 · 2026-02-07
77.4%
#14330: feat(compaction): structured summary template for safeguard mode
by vpesh · 2026-02-12
75.4%
#10457: fix(compaction): abort compaction when summarization fails instead ...
by lailoo · 2026-02-06
75.1%
#20075: feat(compaction): improve merge summary instructions for better nua...
by jlwestsr · 2026-02-18
75.0%
#10711: fix: cancel compaction instead of truncating history when summariza...
by DukeDeSouth · 2026-02-06
74.2%
#8903: fix: improve compaction summary instructions to preserve active work
by joetomasone · 2026-02-04
72.5%
#14887: feat(compaction): configurable auto-compaction notifications with o...
by seilk · 2026-02-12
72.2%
#10505: feat(compaction): add timeout, model override, and diagnostic logging
by thebtf · 2026-02-06
72.1%