Repository navigation
Opencode v2 compatibility improvements - #627
sasa-tomic wants to merge 9 commits into
Conversation
A v2 host restart lost persisted compression blocks and prune marks, so pruned content reappeared and drop-first bookkeeping reset. Write block liveness and prune-marking changes through to disk on the v2 session path so state survives host restarts.
…e 2.0.10
OpenCode 2.0.10 reshaped the resolved theme: contextual.overlay became
surface("dialog") and token paths were renamed. Replace the rigid typed
adapter with a defensive runtime bridge that walks both shapes by path
and degrades to unset instead of throwing, so future host theme changes
can never crash the DCP panel.
Some gateways shadow a plugin tool named 'compress' with their own built-in, leaving DCP's tool unreachable. Make the registered name configurable and default it to 'dcp_compress', keeping prompt text and self-heal lookups consistent with the configured name.
Upstream v2 setups could silently never compress: when the host drops or never lists the plugin tool, injected nudges reference a phantom tool and the model cannot compress. Self-heal the tool into event.tools from DCP's own definition, align host permission enforcement with the configured verdict, and write block-liveness and prune-mark changes through to disk so restarts cannot lose them. Also make atomic-write temp paths unique per call (concurrent saves for the same session no longer clobber each other's temp file) and refuse to overwrite a corrupt state file from manual-mode saves. Tests ported to the compact id format (@1@ refs) the v2 path now injects.
The model had no cost model for what it carried: block summaries rode along forever with invisible token cost, and old uncompressed tool outputs were never named as drop targets. Extend the compress guidance with: - per-block summary token cost and total, so merges are informed - an explicit decision order: DROP stale content first, MERGE only what must survive the session, leave uncertain content alone - a merge caveat: merging fuses blocks and forfeits individual drop/decompress later - per-message occupancy list of the largest uncompressed messages with refs, token estimates, tool-output counts, and protected flags, reasoning and projected media included in the estimate - trigger prompt assigns message refs so named targets stay valid, and phantom block ids are dropped from the active list Block refs and placeholder syntax follow the session's id format ((bN) xml / @b1@ compact).
…encode/plugin @opencode/plugin >=2.0.4 declares peerOptional @opentui/core >=0.5.10, which conflicted with the root pin of ^0.4.5 and broke npm install. 0.5.12 satisfies both plugin ranges. Also migrate the v2 media-part projection and test fixture to the new @opencode/ai schema, where media parts carry a Media.Asset instead of flat mediaType/data fields.
|
Does this include the other one? #628 |
…on V2 - default pruneNotificationType to toast: transient, never stored, never sent to the model (V2 hosts do not strip 'ignored' parts) - V2 reports are ephemeral rpc events rendered as TUI toasts by the DCP TUI plugin, replacing the silent logger.debug path - stamp every DCP chat notification with the DCP marker and drop marker (and legacy manual-trigger) user messages from the V2 request view, so historical notifications stop re-entering model context each request - compact the compress nudge guidance: cap block cost list at 8, shrink occupancy list to 8, condense decision-order prose
The <compress triggered manually> marker is the live V2 manual-compression trigger (system prompt tells the model to wait for it), so only the DCP chat-notification marker is dropped from the request view.
Hosts persist request-time mutations, which surfaced DCP nudges and message-id tags inside the user's own chat bubble. Inject nudges and user-message tags as standalone synthetic text parts (model-visible, TUI-hidden, safe to persist) and stamp ignored chat notifications with synthetic so TUIs hide them.
|
I'd hold off on merging. Four things are actually broken, and two of them reintroduce the class of bug this PR sets out to eliminate. The worst is Serializing writes per state file covers the DCP-against-DCP case. You also want a bounded retry on EACCES/EPERM/EBUSY, because a mutex cannot beat an A/V scanner holding the directory. npm ships that shape in Three smaller ones, a few lines each. Two things I checked and one I got wrong. The theme bridge in An earlier draft of this comment flagged that same hook for overriding an explicit deny, and raised a question about whether Everything below is in code this PR introduced or modified, so I would fix it here rather than in follow-ups. I also read the diff for added complexity and found nothing worth acting on. There are two cosmetic shrinks in Full audit, 9 findings with line referencesVerification: The four blockers
part.type === "tool" && part.tool === "compress" && part.state?.status === "completed",A duplicate of
text.replaceAll("compress tool", `${toolName} tool`)Only matches that exact phrase, but
part.tool === LEGACY_COMPRESS_TOOL_NAME || part.tool === DEFAULT_COMPRESS_TOOL_NAME
The five medium ones
if (foundTokens) {
return 0
}
Nit
Checked, no findings
|
|
@amnesiaof will you make a separate PR then? |
… in manual trigger only Port of upstream PR Tarquinen#627 commit 6aeccfc with one deliberate change: the per-message occupancy list is rendered only in the one-shot /dcp-compress trigger prompt. Upstream also injected it into every anchored nudge, which rebuilds the text on every request and changes old anchors' content as messages grow, invalidating the prompt-cache prefix. Block costs and the drop/merge decision order stay in anchored nudges since they only change when a compression runs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nen#627 ade9e15) Cherry-picked from upstream PR Tarquinen#627 with changes: - keep Tarquinen#607's guard: a block whose origin and anchor are both missing is deactivated (otherwise prune hides its range while the summary has nowhere to render, silently losing content) - fix two bugs in that guard: a user-decompressed block with a missing origin was reactivated, and a merged block with a missing origin skipped retiring the blocks it consumed (double-injected summaries) - drop running deduplicate/purgeErrors on every V2 request: it rewrites earlier tool outputs mid-session and invalidates the prompt cache; DCP runs them at compress time by design - syncCompressionBlocks returns whether liveness changed; V2 writes through Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arquinen#627 61adf37) Cherry-picked from upstream PR Tarquinen#627, adapted to keep the tool named "compress" (the dcp_compress rename is not taken): - self-heal the compress tool into event.tools when the host drops it - refuse to overwrite a state file that exists but failed to load; unique temp paths for atomic writes - content-based token usage fallback when no assistant reports tokens, merged with Tarquinen#582's max(reported, estimate) and memoized per window, since Tarquinen#660 made counting async and it runs on every request Bun-only tests (V2 setup seam, TUI render) move to tests/bun and run via scripts/test-bun.mjs with bun or the OpenCode binary (BUN_BE_BUN=1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on V2 (from Tarquinen#627 1a726a7) Cherry-picked from upstream PR Tarquinen#627. Fixes Tarquinen#664 (V2 report() was a no-op). Adapted: keeps the pre-bump media projection (the @opencode/ai Media.Asset API comes with a dependency bump not taken here), keeps the corrected merge caveat wording and the placeholder rule in the compacted block guidance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hosts persist request-time mutations, which surfaced DCP nudges and message-id tags inside the user's own chat bubble. Inject nudges and user-message tags as standalone synthetic text parts (model-visible, TUI-hidden, safe to persist) and stamp ignored chat notifications with synthetic so TUIs hide them. Cherry-picked from upstream PR Tarquinen#627. Addresses Tarquinen#608/Tarquinen#642/Tarquinen#662 (DCP decorations showing in the user's chat bubble on V2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eholders Seen overnight on a local Qwen session: the block guidance ported from Tarquinen#627 (per-block token costs + 'MERGE only what must persist') led the model to compress @b1@..@301@ at 46% context, expecting to shrink old summaries. That cannot happen: blocks inside a range are carried over verbatim (placeholders expanded, missing ones re-appended). The range rewrote everything after the system prompt (49k re-prefill instead of ~100k cache reuse) and cost ~11k output tokens for no summary saving. The guidance now states that spanning a block never shrinks it and that ranges should cover raw messages after the latest block. The same summary listed @b3@..@b7@ twice; only the first copy is expanded, so later copies stayed in the stored summary as stray block refs. Drop them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hi! Thanks for the GREAT plugin. Absolutely brilliant, and pretty much the only reason I use opencode and not something else!
Now, the upgrade of opencode to v2 caused some problems. In particular, when opencode backend service restarts, the compression seems to break.
I asked the AI to fix that, plus a few other papercuts.
Please take a look and pick whatever makes sense to you.