Environment
opencode-supermemory (current main)
- OpenCode
1.18.8 on a non-Claude provider (Z.ai GLM-5.2 1M-ctx main agent / GLM-4.7 200K-ctx compaction agent)
supermemory.jsonc with compactionThreshold set
Summary
Three related bugs in createCompactionHook (dist/index.js) plus a feature gap. On a non-Claude provider whose compaction model has a smaller context window than the main model, the plugin's custom compaction path (a) overflows the compaction model, (b) leaves the session "stuck" on the compaction model after summarizing, and (c) races with OpenCode's native compaction.auto.
Bug 1 — Post-summarize "Continue" runs on the compaction model, not the session model
After session.summarize, the hook fires a synthetic continuation:
// dist/index.js ~line 14945
await ctx.client.session.promptAsync({
path: { id: sessionID },
body: {
agent: storedMessage2?.agent, // ← agent resolved from the NEAREST message
parts: [{ type: "text", text: "Continue" }]
},
query: { directory: ctx.directory }
})
storedMessage2 comes from findNearestMessageWithFields(messageDir), which picks up the summary message just produced by session.summarize — whose agent is the compaction agent. The session therefore continues on the compaction model (e.g. GLM-4.7) instead of the session's main model (GLM-5.2), and stays there until the user manually switches. On a 1M-ctx main / 200K-ctx compaction split this is both a quality regression and a context-window trap.
Suggested fix: capture the pre-compaction agent/model before calling session.summarize and restore them for the "Continue".
Bug 2 — Compaction context-window mismatch
The trigger fires at threshold × getModelLimit(mainModel) (e.g. 0.8 × 1M ≈ 800K tokens):
// dist/index.js ~line 15171-15174
const compactionHook = isConfigured() && ctx.client ? createCompactionHook(ctx, tags, {
threshold: CONFIG.compactionThreshold,
getModelLimit
}) : null;
…but session.summarize runs on the compaction agent (200K ctx):
// dist/index.js ~line 14927
await ctx.client.session.summarize({
path: { id: sessionID },
body: { providerID, modelID }, // ← compaction agent's model
query: { directory: ctx.directory }
});
So the summarize request can be 4× the compaction model's context window → it fails or yields a truncated/garbage summary. In a 13-day window I see 47 sessions >1M tokens and 104 >200K in ~/.local/share/opencode/session.db. Compaction needs a model whose context window ≥ the main model's, or chunked summarization.
Bug 3 — validateCompactionThreshold makes 0 un-disablable
There is no documented way to disable the plugin's custom compaction. Setting compactionThreshold: 0 does not disable it — it silently falls back to the default:
// dist/index.js ~line 13771
function validateCompactionThreshold(value) {
if (value === undefined || typeof value !== "number" || isNaN(value)) {
return DEFAULTS.compactionThreshold; // 0.8
}
if (value <= 0 || value > 1)
return DEFAULTS.compactionThreshold; // ← 0 lands here → silently re-enabled at 0.8
return value;
}
The only effective disable is 1 (triggers at 100% of the model limit, which native compaction always beats). A 0 (or false) value should disable, not fall back.
Bug 4 — Races with OpenCode native compaction
The plugin's token-ratio → summarize → Continue path duplicates OpenCode's native compaction.auto; the two can double-trigger on the same session. OpenCode now exposes an experimental.session.compacting hook (fires before the continuation summary; lets plugins inject into output.context or replace output.prompt). The plugin should defer to that hook instead of reimplementing the trigger + continuation.
Feature gap — memory capture is post-hoc only
The only memory captured is the lossy post-compaction summary (saveSummaryAsMemory, ~line 14988). There is no pre-compaction extraction step. A review-and-extract pass (read the about-to-be-discarded context → curate durable facts → store, then lean summarize) would align the plugin with retrieval-augmented compaction (Anthropic Effective Context Engineering; LangChain write/isolate/compress) and substantially reduce context loss across the compaction boundary.
Workaround I'm using
agent.compaction.model set to a model with a context window matching the main agent.
compactionThreshold: 1 (the only value that effectively disables the plugin's compaction).
- Native
compaction.auto enabled.
- A local
experimental.session.compacting plugin that does pre-compaction extraction + dedup + store.
Happy to PR any of the fixes if there's interest. Tagging this against the version above; happy to provide the exact dist/index.js commit hash on request.
Environment
opencode-supermemory(currentmain)1.18.8on a non-Claude provider (Z.ai GLM-5.2 1M-ctx main agent / GLM-4.7 200K-ctx compaction agent)supermemory.jsoncwithcompactionThresholdsetSummary
Three related bugs in
createCompactionHook(dist/index.js) plus a feature gap. On a non-Claude provider whose compaction model has a smaller context window than the main model, the plugin's custom compaction path (a) overflows the compaction model, (b) leaves the session "stuck" on the compaction model after summarizing, and (c) races with OpenCode's nativecompaction.auto.Bug 1 — Post-summarize "Continue" runs on the compaction model, not the session model
After
session.summarize, the hook fires a synthetic continuation:storedMessage2comes fromfindNearestMessageWithFields(messageDir), which picks up the summary message just produced bysession.summarize— whose agent is the compaction agent. The session therefore continues on the compaction model (e.g. GLM-4.7) instead of the session's main model (GLM-5.2), and stays there until the user manually switches. On a 1M-ctx main / 200K-ctx compaction split this is both a quality regression and a context-window trap.Suggested fix: capture the pre-compaction agent/model before calling
session.summarizeand restore them for the "Continue".Bug 2 — Compaction context-window mismatch
The trigger fires at
threshold × getModelLimit(mainModel)(e.g.0.8 × 1M ≈ 800Ktokens):…but
session.summarizeruns on the compaction agent (200K ctx):So the summarize request can be 4× the compaction model's context window → it fails or yields a truncated/garbage summary. In a 13-day window I see 47 sessions >1M tokens and 104 >200K in
~/.local/share/opencode/session.db. Compaction needs a model whose context window ≥ the main model's, or chunked summarization.Bug 3 —
validateCompactionThresholdmakes0un-disablableThere is no documented way to disable the plugin's custom compaction. Setting
compactionThreshold: 0does not disable it — it silently falls back to the default:The only effective disable is
1(triggers at 100% of the model limit, which native compaction always beats). A0(orfalse) value should disable, not fall back.Bug 4 — Races with OpenCode native compaction
The plugin's
token-ratio → summarize → Continuepath duplicates OpenCode's nativecompaction.auto; the two can double-trigger on the same session. OpenCode now exposes anexperimental.session.compactinghook (fires before the continuation summary; lets plugins inject intooutput.contextor replaceoutput.prompt). The plugin should defer to that hook instead of reimplementing the trigger + continuation.Feature gap — memory capture is post-hoc only
The only memory captured is the lossy post-compaction summary (
saveSummaryAsMemory, ~line 14988). There is no pre-compaction extraction step. A review-and-extract pass (read the about-to-be-discarded context → curate durable facts → store, then lean summarize) would align the plugin with retrieval-augmented compaction (Anthropic Effective Context Engineering; LangChain write/isolate/compress) and substantially reduce context loss across the compaction boundary.Workaround I'm using
agent.compaction.modelset to a model with a context window matching the main agent.compactionThreshold: 1(the only value that effectively disables the plugin's compaction).compaction.autoenabled.experimental.session.compactingplugin that does pre-compaction extraction + dedup + store.Happy to PR any of the fixes if there's interest. Tagging this against the version above; happy to provide the exact
dist/index.jscommit hash on request.