You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Cursor agent runtime compacts its own conversation as it approaches
its context threshold (`preCompact` hook, `trigger: "auto"`), so a second
opencode-driven pass is redundant. It is also actively harmful, in two
ways.
The compaction turn asks the model to summarize with zero tools declared.
The Cursor agent runs its own tools regardless, and opencode rejects the
result outright:
Tool call not allowed while generating summary: <tool>
Worse, compaction rewrites the transcript, so the next turn no longer
matches what the Cursor agent saw. `classifyTurn` correctly reports a
divergence and a fresh Cursor agent is created — and every distinct
agentId permanently holds a guarded SQLite store.db/-wal/-shm triple.
`SDKAgent.close()` cannot release those; it only flushes analytics and
releases the executor lease, while the checkpoint store is cached in an
agentId-keyed map evicted solely by dispose()/deleteAgent(). That
descriptor growth fed an uncatchable EXC_GUARD kill of the whole opencode
process (guard cookie 0x08fd4dbfade2dead — Apple's SQLite guard).
Suppress the trigger with a large `limit.input`, which is the value
opencode uses as its compaction threshold:
Is(e) = limit.input ? limit.input - reserved
: limit.context - maxOutput
`limit.context` is left honest, so the TUI context gauge and cost
reporting keep working — the alternative lever, `limit.context: 0`, would
disable the trigger but blank the gauge and regress #89.
`limit.input` is honored by opencode's runtime but is not declared in the
published @opencode-ai/sdk config types, so it is excluded from
`_limitKeyGuard` (which still protects context/output) and gated instead
by a new assertion in the integration test: without it, opencode dropping
support would silently restore auto-compaction and the fd leak.
Verified against the opencode 1.18.11 binary by enumerating the call
sites of Is() rather than textual hits on limit.input, since consumers
reach it transitively. The only other consumer, Pd() (preserve-recent
tokens, also used by manual /compact), clamps to 8000 both before and
after. Confirmed end-to-end under an isolated HOME that the sentinel
survives config validation into Provider.list() with limit.context
intact.
Manual /compact is unaffected. Opt back out with
`provider.cursor.options.autoCompaction: true`.
Tradeoff, documented in the README: this suppresses the proactive
threshold only, and opencode has no reactive context-overflow recovery
wired up for this provider, so its transcript is no longer trimmed
automatically. Ordinary turns send just the new message, but a cold
replay resends everything; if that overflows, the turn fails and
/compact is the manual recovery.
0 commit comments