Problem
When a conversation's context is dominated by non-tool content (a large user paste, an assistant dump) and there is no compressible tool result to shrink, the kernel's only hard valve — the emergency tool-result truncate (src/truncate-tools.ts) — is a no-op. It only shrinks old tool-result messages, skips the last preserveRecentMessages (5), skips results < 1000 tok, and never touches user/assistant text. The last user message (the current prompt) is always protected (src/recommend.ts). So a provable overflow exists: a large non-tool message that is neither a tool-result nor compressible, at/over the window, with the advisory nudge ignored (model non-compliance).
This is the last-resort gap the other layers do not cover:
- Preventive (output-headroom — reserves the output budget) does not help when the context itself is already over the window from non-tool content.
- Reactive (overflow self-heal — learns the window + arms the next turn's emergency) still lands on an emergency truncate that is a no-op for non-tool content.
Purpose of the last-resort (design intent)
The truncation is not meant to be a permanent loss. Its purpose is to temporarily truncate / drop some information so that a NEW compression can succeed — i.e. it is a bridge to recovery, not the end state. On that basis a lossy last-resort is worth it.
That intent opens the door to combining it with export: in an emergency, export first (so the truncated information is recoverable), then truncate.
Current state (verified)
| Capability |
Status |
| Kernel emergency truncate |
Only old tool-results; no-op on user/assistant text; skips last 5 + <1000 tok. |
| Last user message |
Always protected (the current prompt). |
| Export |
Exists only in the proxy as an offline CLI command bili export (src/export.ts:147 exportSession(selector,{dir,output,full}) → renderHandoff): default = folded view via kernel prune (summaries in place of compressed ranges); --full = every original message; markdown; optional file output. Operator-invoked, offline, NOT on the emergency path. The extension has no export. |
| "Full compression" |
Does not exist. No tool/mode (kernel, proxy, or extension) that summarizes everything except the last N messages into a single T2 summary. The kernel has a tiered T1→T2→T3 system (src/compression-rules.ts) but no single "full compress" op. |
| Session persistence |
Incremental — compression state + compressed originals only; uncompressed conversation text is not fully persisted (v3 files have a lastMessages snapshot, v2 does not) → an offline export of an older session may lack the full text. |
Candidate approaches
A. Export-then-truncate
On emergency (context ≥ threshold, nothing viable to compress, no tool-result to shrink):
- Export the full current context to a file (reuse the proxy's
exportSession/renderHandoff, but make it live — in the emergency path, using the in-memory context the host already has).
- Truncate the oldest large non-tool message(s) (skip the last user message + the last N), down to a small prefix+suffix (like the tool truncate).
The truncated information is recoverable from the export. This creates room so the model's next compression succeeds (the bridge).
B. Full compression ("global T2")
A new full_compress op: within the remaining compressible space, have the model summarize everything except the last N messages into a single T2 summary (a global tier-2 distill). More elegant — the model summarizes, so the loss is controlled and it produces a real summary block the tier system can later condense — but it needs a model round-trip (not instant) and a new tool/mode.
Open questions (for discussion)
- Which approach — A, B, or an A-then-B escalation ladder (instant truncate as the immediate backstop, full-compression as the follow-up)?
- Placement — truncate/full-compress are kernel capabilities (shared by all hosts); export is a host capability (proxy has it, extension doesn't). Should the kernel emit a "last-resort" signal that the host fulfils (export + optional truncate), or should the kernel do the truncate itself while the host does the export?
- Making export live — the current export is offline CLI. An in-emergency export needs the full current context, which the host has in memory but does not fully persist (incremental persistence). How do we obtain the full text at emergency time?
- What to truncate — which non-tool messages (oldest first? skip the last user message + last N?), and how much to keep (prefix+suffix size)? The user-paste loss is the risk being mitigated by the export.
- Full-compression latency — it needs a model round-trip. Acceptable in an emergency (adds a turn), or must the last-resort be instant (truncate)?
- Extension parity — the extension has no export. Does it need one for the last-resort?
- Trigger condition — exactly when does the last-resort fire (e.g. ≥95% of the (headroom-reserved) window + no viable range + no tool-result to shrink), and how does it interact with the existing overflow self-heal (learned window + armed emergency) and output-headroom?
Scope / non-goals
- This is a design-discussion issue (no implementation in this issue).
- It complements the already-landed preventive (output-headroom) and reactive (overflow self-heal) layers in the two downstream hosts.
Related
billion-context PR #172 (proxy: self-heal + output-headroom)
billion-context-pi PR #177 (extension: self-heal + output-headroom)
- Kernel:
src/truncate-tools.ts (emergency truncate), src/recommend.ts (viability + last-user protection), src/compression-rules.ts (T1→T2→T3 tiers), src/nudge-text.ts (tier distill nudges)
- Proxy:
src/export.ts (offline exportSession/renderHandoff), src/persist.ts (incremental persistence, v2/v3)
Problem
When a conversation's context is dominated by non-tool content (a large user paste, an assistant dump) and there is no compressible tool result to shrink, the kernel's only hard valve — the emergency tool-result truncate (
src/truncate-tools.ts) — is a no-op. It only shrinks oldtool-resultmessages, skips the lastpreserveRecentMessages(5), skips results < 1000 tok, and never touchesuser/assistanttext. The last user message (the current prompt) is always protected (src/recommend.ts). So a provable overflow exists: a large non-tool message that is neither a tool-result nor compressible, at/over the window, with the advisory nudge ignored (model non-compliance).This is the last-resort gap the other layers do not cover:
Purpose of the last-resort (design intent)
The truncation is not meant to be a permanent loss. Its purpose is to temporarily truncate / drop some information so that a NEW compression can succeed — i.e. it is a bridge to recovery, not the end state. On that basis a lossy last-resort is worth it.
That intent opens the door to combining it with export: in an emergency, export first (so the truncated information is recoverable), then truncate.
Current state (verified)
bili export(src/export.ts:147 exportSession(selector,{dir,output,full})→renderHandoff): default = folded view via kernelprune(summaries in place of compressed ranges);--full= every original message; markdown; optional file output. Operator-invoked, offline, NOT on the emergency path. The extension has no export.src/compression-rules.ts) but no single "full compress" op.lastMessagessnapshot, v2 does not) → an offline export of an older session may lack the full text.Candidate approaches
A. Export-then-truncate
On emergency (context ≥ threshold, nothing viable to compress, no tool-result to shrink):
exportSession/renderHandoff, but make it live — in the emergency path, using the in-memory context the host already has).The truncated information is recoverable from the export. This creates room so the model's next compression succeeds (the bridge).
B. Full compression ("global T2")
A new
full_compressop: within the remaining compressible space, have the model summarize everything except the last N messages into a single T2 summary (a global tier-2 distill). More elegant — the model summarizes, so the loss is controlled and it produces a real summary block the tier system can later condense — but it needs a model round-trip (not instant) and a new tool/mode.Open questions (for discussion)
Scope / non-goals
Related
billion-contextPR #172 (proxy: self-heal + output-headroom)billion-context-piPR #177 (extension: self-heal + output-headroom)src/truncate-tools.ts(emergency truncate),src/recommend.ts(viability + last-user protection),src/compression-rules.ts(T1→T2→T3 tiers),src/nudge-text.ts(tier distill nudges)src/export.ts(offlineexportSession/renderHandoff),src/persist.ts(incremental persistence, v2/v3)