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 chat process 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 _id of 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, userTouchedAt null.
  • Chat Process: status = IDLE, closeReason/boundProfile/activeDelegationWorkerId null, haltRequested = false, readState/activatedDeferredTools/workerLinks cleared, parentProcessId = null (the Chat Process duplicate is always top-level), optimistic-lock version reset. 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:

  1. Session Copy (SessionService.createCopy) — metadata + policy.
  2. Chat Process Copy (ThinkProcessService.duplicateProcessIntoSession) → old→new process ID.
  3. Relink chatProcessId of the copy to the new process.
  4. Message CopysessionId/thinkProcessId re-assigned, archivedInMemoryId initially cleared; results in old→new Message ID map.
  5. Memory CopysessionId/thinkProcessId/projectId re-assigned, sourceRefs remapped via the Message map; results in old→new Memory ID map.
  6. Rebind archivedInMemoryId on the copies via the Memory map (grouped by target Memory, one markArchived per Memory).
  7. 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.