refactor(session): remove message content mutation API - #48043
Conversation
Remove session.messageUpdate and Session.updateMessage. Context filtering belongs in the session context hook rather than persisted history edits. Keep historical content-update events available only for durable replay.
|
@rekram1-node Could you reconsider this removal and revert it until a narrower replacement is available? OpenCode owns the database and session consistency, so it is precisely where safe cleanup should happen, not through direct SQL in downstream clients. I think two different concerns are being conflated here: arbitrary history rewriting and selective removal of stored technical payloads. If An append-only history could record that content was pruned while preserving the necessary structure. Is there a concrete invariant that makes this unsafe, even for completed tool/reasoning blocks in idle sessions? Given the immediate impact on CodeNomad 0.20.0, a temporary revert followed by a constrained replacement would let us address your concern without breaking adopted workflow in the meantime. |
…n RPC (#686) ## Summary Restore selective deletion of completed assistant tool/reasoning content after OpenCode removed `session.messageUpdate`, without forking OpenCode or opening a generic RPC proxy. The original draft's unconditional `maintenance_required` stub is replaced by a working **experimental, explicit opt-in** plugin path for the audited `0.0.0-beta-19419` runtime. The default remains read-only. Unknown runtimes/storage and active native execution fail closed. Nothing is installed or enabled on app startup. ## Implementation - Keep individual/message/group/session cleanup entry points. Send selected technical indices and a canonical SHA-256 revision, never arbitrary replacement content, SQL, database filenames or caller-selected locations. - Use an ownership-checked CodeNomad broker with a fixed RPC ID/method/location and worktree-deletion fence. Keep the removed PATCH and generic RPC routes blocked. - Bind the plugin's explicitly configured database connection to the actual daemon DB with a fresh `ctx.storage` challenge. Inside a synchronous SQLite write transaction, recheck directory/project/workspace ownership, native durable execution claim, staged revert/compaction state, event ownership and retained payloads. - Rely on the audited native **write-ahead execution claim before runner history reads**, not an `idle` check, plugin mutex or `BEGIN IMMEDIATE` alone. Never set or release the native claim ourselves. - CAS only the completed assistant's content array. Preserve IDs, ordering, text, snapshots, usage and metadata. Commit a content-free idempotency receipt atomically with the change; identical retries cannot delete a second shifted block. - Emit the custom invalidation after commit. Re-read via the native API, invalidate the SDK transcript and pending rotations, and fence late reads against newer invalidations. Add a public-API TUI cache companion and reconnect handling. - Correct native Windows path encoding; support more than 4,096 selected parts within explicit request/message budgets. - Provide an independently packable plugin with main and `./tui` entrypoints, deployment/safety docs, and a Windows/Linux/macOS native package CI matrix. Plugin installation remains manual and separately approved. ## Validation performed locally | Check | Result | |---|---| | Server/plugin/SQLite/broker/proxy tests | 72 passed | | UI actions, events, SDK projection and stale-read tests | 60 passed | | Plugin/SQLite/TUI-companion tests under Node 22 | 30 passed | | Server and UI TypeScript checks | Passed | | Production UI build | Passed; existing large-chunk warnings | | Official Windows x64 beta-19419 executable | Passed on a private daemon and generated DB | | Independently packed and installed plugin | Same native test passed, outside the checkout | The native integration test uses the real beta-19271 network client against the official beta-19419 runtime, with a local synthetic provider and no credentials. It verifies actual preview/prune RPC, refusal while a primary generation holds the native claim, both prompt/transaction orderings, idempotent retry, two event subscribers, native re-reading, the subsequent primary model payload, fork isolation, pre-compaction history without summary changes, and persistence after server restart. The new three-OS native CI matrix must pass on this head before merge. No CI result is inferred from the local Windows run. ## Boundaries and remaining validation - This is a reviewable opt-in implementation, **not general availability across OpenCode versions/providers/platforms**. Exact runtime gating is intentional; don't widen it without re-auditing/testing. - Interactive official TUI, two native CodeNomad windows/scroll behavior, WSL, provider-specific continuation state and budget/token estimates are not claimed as validated. TUI companion tests use its cache contract, not an interactive terminal. Two HTTP subscribers are not two GUI windows. - Existing bulk cleanup is per-message and reports failure counts; dedicated installation/capability and bulk progress/cancel UI are not added here. - Existing summaries/native checkpoints, fork copies and already assembled/sent requests are not retroactively scrubbed. Historical usage remains historical usage; mock token values are not a token-savings measurement. - This changes persisted V2 content, not only visibility. It frees reusable SQLite pages but does **not** promise a smaller physical file. No VACUUM, V1 cleanup, durable-event rewriting, automatic backup restoration or universal repair is included. - No active user DB was changed, no shared-daemon plugin installed, no shared OpenCode service stopped/upgraded, and no native desktop executable/profile rebuilt or replaced. Tests use isolated synthetic data only. ## Documentation - [Request flow and validation](dev-docs/SESSION_PRUNING_RPC.md) - [Audited safety boundary](dev-docs/SESSION_PRUNING_SAFETY.md) - [Explicit deployment and recovery](dev-docs/SESSION_PRUNING_DEPLOYMENT.md) References: anomalyco/opencode#44984, anomalyco/opencode#48043, anomalyco/opencode#48090 and the maintainer's plugin/RPC direction. No upstream API or distribution fork is introduced.
Summary
Remove the completed-assistant-message content mutation introduced by #45015 for #44984.
PATCH /api/session/:sessionID/message/:messageID(session.messageUpdate), its generated client methods, and CoreSession.updateMessage/session-handle operation and dedicated errors.Context and rationale
The original issue requested V1-style part deletion so clients could prune stored tool/reasoning payloads from long sessions, citing database growth. #45015 implemented a broader operation that replaced all content of a completed assistant message in an idle session.
Following maintainer discussion, arbitrary editing of persisted assistant history should not be part of the session API. Plugins that want to exclude tool/reasoning content from model input should use
ctx.session.hook("context", ...)to transform the outgoing messages instead. The hook changes model context without rewriting stored history.That distinction matters for the original report: context filtering does not reclaim disk space or remove content from stored/UI history. Storage retention is a separate concern; this PR does not claim that the context hook solves database growth.
Compatibility
This intentionally breaks callers of
session.messageUpdate/Session.updateMessageand removes the corresponding public event. Existing session reads, prompts, execution, and other session operations remain available. Historical replacement events remain replayable, including for users who used the removed API before upgrading.Created in a separate worktree from latest
origin/v2atd24f8b0810. Follow-up explanation on #44984 can happen after review.Verification
git diff --checkpass.