Vancetope — Session Duplicate
“Duplicate” creates an independent, continuable copy from an existing Session—including the Chat Memory (Chat Process, complete message history, and associated Memory entries such as Compaction Summaries, Scratchpad, Plan, Insight). The copy resides in the same Project and can be continued independently of the original. The action is located in the Session menu of the Chat list (
⧉), not only in the opened chat.
See also: session-lifecycle session-groups memory-compaction web-ui
1. Purpose
The classic use case is “branch from here”: a conversation has reached a point where the user wants to try two different directions without losing or altering the previous course. A duplicate freezes the current state and provides a second, equivalent Session that carries the same conversational memory.
Duplicate is not an export or an archive—the copy is a fully functional, active Session with its own identity.
2. Scope — what is copied (v1)
| Data Type | Collection | Copied? | Note |
|---|---|---|---|
| Session metadata | sessions |
✅ | new Identity; title “Copy of …”, pinned reset |
| Chat Process | think_processes |
✅ | only the chat process; Config preserved, Runtime reset |
| Chat Messages | chat_messages |
✅ | full history including archived Turns, timestamps preserved |
| Memory entries | memories |
✅ | session-/chat-process-scoped (Compaction, Scratchpad, Plan, Insight) |
| Session Groups | session_groups |
❌ | copy starts without a group |
| Pending Queue | engine_messages |
❌ | transient |
| Telemetry | llm_traces, llm_usage_records, event_log, prak_runs |
❌ | Audit/metrics belong to the original |
| Project Assets | documents, rags |
❌ | project-scoped, are shared (not duplicated) |
Deliberately outside v1:
- Worker Sub-Processes. Only the
chatprocess is copied. Workers are transient task executions, not conversations; their history is not copied. - Cross-Project Duplicate. The copy remains within the same Project/User/Tenant. A target Project picker (with rights/confidentiality checks for Memory and History) is planned as a later extension.
3. Identity & Runtime Reset
The copy is a fresh Session, not a clone of the running state:
- Newly assigned:
sessionId(newSessionId()), Mongo_idof all copies,createdAt/lastActivityAt= now. - Session:
status = INIT,boundConnectionId = null,chatProcessId = null(re-linked after process copy), all Suspend/Archive runtime fields null,pinned = false,titleAutoGenerated = false,userTouchedAtnull. - Chat Process:
status = IDLE,closeReason/boundProfile/activeDelegationWorkerIdnull,haltRequested = false,readState/activatedDeferredTools/workerLinkscleared,parentProcessId = null(the Chat Process duplicate is always top-level), optimistic-lockversionreset. Preserved is the configuration: Engine, Recipe,engineParams, Prompt Overrides,mode,todos,activeSkills, Tool/Skill Overrides.
The copied Chat Engine is not started during duplication. It resumes like any reopened Session as soon as the user opens the duplicate and sends a message (Pending Queue + Lane).
4. Cross-Collection ID-Remapping
The SessionDuplicationService (in vance-brain) holds two maps and orchestrates; each owning service (SessionService, ThinkProcessService, ChatMessageService, MemoryService) performs its own reads/writes (data sovereignty). Order:
- Session Copy (
SessionService.createCopy) — metadata + policy. - Chat Process Copy (
ThinkProcessService.duplicateProcessIntoSession) → old→new process ID. - Relink
chatProcessIdof the copy to the new process. - Message Copy —
sessionId/thinkProcessIdre-assigned,archivedInMemoryIdinitially cleared; results in old→new Message ID map. - Memory Copy —
sessionId/thinkProcessId/projectIdre-assigned,sourceRefsremapped via the Message map; results in old→new Memory ID map. - Rebind
archivedInMemoryIdon the copies via the Memory map (grouped by target Memory, onemarkArchivedper Memory). - Rebind Supersede chain (
supersededByMemoryId) between the Memory copies, so that the same Summary is active as in the original.
Timestamp fidelity: Since @CreatedDate auditing overwrites createdAt on insert, ChatMessageService.insertCopies / MemoryService.insertCopies restore the original createdAt after saving via bulk update—the copied scrollback retains its true chronology.
No Mongo transaction wrapper (like with delete-cascade): the steps are ordered such that an error in the middle leaves an incomplete but internally consistent copy and never touches the original.
5. API
REST: POST /brain/{tenant}/sessions/{sessionId}/duplicate
- Owner-only (same check as archive/delete/patch),
Action.EXECUTE. - Optional body:
SessionDuplicateRequest { title?: string }— empty ⇒ original title adopted unchanged; the Web-UI sends “Copy of …”. - Response:
SessionDuplicateResponse { sessionId, title }— Business ID + resolved title of the copy.
Edge case: If the source never bootstrapped a Chat Process (chatProcessId == null), only the Session metadata copy is created; the duplicate bootstraps a fresh Chat Process when opened.
6. Web-UI
Entry ⧉ Duplicate in the SessionActionsMenu (Chat list, per Session card). Available in any state—even for archived Sessions, as the copy is actively created. Upon success, the list reloads; the duplicate appears (without a group) in the current Project. The shared action logic resides in the Composable useSessionActions (duplicate(title)), together with the other Session actions.