Vancetope — Session Modify / Crop
“Crop” removes messages from a Session’s memory without deleting them. The user can remove individual turns in the middle or “crop from here” (this message and all subsequent ones) — and restore everything at any time. Removed messages remain in the database (auditable) but are invisible to the AI: out of every LLM replay, compaction, recompaction, and history search path, and out of the chat history. The action is located in the Session menu of the chat list (
✂) and opens a modal — the target Session does not need to be open.
1. Marking — No New Attribute
“Removed” is expressed via the existing meta.kind type on ChatMessageDocument
(the same mechanism as kind=interim), with the new value
meta.kind = "removed":
ChatMessageDocument.KIND_REMOVED = "removed"+ helperisRemoved()— parallel toKIND_INTERIM/isInterim().- No new DB field. Since
ChatMessageDto.metaalready goes to the client, the Crop UI sees the state (meta.kind === 'removed') without DTO changes. - Only the marker changes —
content,role,tags, timestamps remain untouched. Reversible: removing the marker = message back in memory.
Distinction from other exclusion mechanisms: archivedInMemoryId
(Compaction) folds messages into a Summary; STRENGTH:* tags are a
soft filter. “Removed” is the explicit user decision “this does not
belong in memory”.
2. Where “removed” is respected
| Path | Method | Behavior |
|---|---|---|
| LLM Replay (all Engines, Compaction, Prak) | ChatMessageService.activeHistory |
removed out (in addition to interim) |
| Chat Scrollback (REST) | activeHistoryWithInterim |
removed out, interim remains (dimmed) |
| Crop Editor | historyForCrop |
removed in (for restoration), interim out |
| Recompaction Range | findActiveInRange |
removed out (Query Criteria) |
| history_search Tool | search |
removed out (Query Criteria) |
| WS Resume Push | SessionCreateHandler.pushAppendedMessages |
removed not re-emitted |
| Audit / Full History | history |
removed in (nothing is lost) |
3. Mutation
ChatMessageService:
markRemoved(tenantId, sessionId, ids)—$set meta.kind=removed, tenant+session-scoped, idempotent.unmarkRemoved(tenantId, sessionId, ids)—$unset meta.kind, only on rows that are currentlyremoved(protects a potentialinterimmarker).
4. API
GET /brain/{tenant}/sessions/{id}/messages?includeRemoved=true— Crop list (logical conversation including removed, without interim). Owner-only (the normal scrollback remains readable for shared Sessions; the Crop view does not).PATCH /brain/{tenant}/sessions/{id}/messages/crop— BodySessionCropRequest { remove: string[], restore: string[] }(both optional/idempotent). Owner-only,Action.WRITE. Response: the fresh Crop list (ChatMessageDto[]), so the modal re-renders in one roundtrip. Crop-from-point is expressed by the client as aremovelist of all messages from the selected point.
5. Web UI
✂ Crop in the SessionActionsMenu (owner-gated, in any Session state
— history survives archiving). If the Session is currently connected
(session.bound — someone might be working on it), the UI warns in advance
via a confirmation dialog before opening the modal (consistent with the Compact
action, see session-compact §4). Opens SessionCropModal (Host:
PickerView): list of all messages with a checkbox per turn, ✂ button “crop
from here”, live preview (removed = struck through/red, restored = green),
batch apply with change counter. Persisted only on “Apply” (Diff → remove/restore),
before that purely local and discardable.
6. Limitations (v1)
- No WS live notification to a currently open chat view of the same Session: anyone with the chat open in parallel will only see the removal after reload/reconnect. (The REST and Resume paths are consistent; only the ongoing live stream is not retroactively cleaned up.)
- Cropping affects the
chatprocess of the Session (the user conversation). Worker sub-processes do not have their own Crop interface. - No cascade to already generated Compaction Summaries: a Summary that summarized a message later removed will not be recomputed. New Compactions will consider the removed status.