zot/internal
patriceckhart 7794a253b9 feat(session): /session fork + /session tree
Branch semantics for conversations: rewind to a past user message
and continue from there in a new session, with a visual tree
picker to switch between branches later.

/session fork
  Opens the /jump turn picker in fork mode. Pick any past user
  message; zot copies every message from the session start up to
  and including that turn into a new session file, records the
  parent id + fork point in the new meta, and swaps the running
  agent onto the new branch. The parent session file stays on
  disk unchanged; you can return to it later via /session tree.

/session tree
  Shows every session in the current cwd arranged by parent/child
  relationships. Depth-first flatten with two-space indent per
  level; the current session is tagged "[current]". Pick any
  other entry to switch into it (same semantics as /sessions).

Why both commands:
  /sessions remains the "flat list of everything in this
  directory" resume picker. /session tree is the fork-aware
  variant. /session fork is the equivalent of git branch; /session
  tree is the equivalent of checkout.

core additions:

  SessionMeta gains two fields:
    - Parent    string  (parent session ID, empty for roots)
    - ForkPoint int     (0-indexed message position of the cut)

  core.BranchSession(parentPath, root, cwd, version, upToIdx)
    Reads the parent session, writes a new session file in
    SessionsDir(root, cwd) containing the first upToIdx message
    rows + any usage rows that came before the cut. The new meta
    records Parent=<parent id>, ForkPoint=<upToIdx>, fresh id,
    cwd, Started, Version.

  core.BuildSessionTree(root, cwd) []*TreeNode
    Walks every session file in the cwd dir, reads each one's
    meta, links children to parents by ID. Returns the forest
    rooted at parentless sessions. Missing-parent sessions (if
    the parent file was manually deleted) surface as roots so
    they stay discoverable.

  core.FindSessionByID(root, cwd, id) string
    O(n) lookup used when resolving a tree pick back to a file
    path. Files in the dir are small in practice.

  readSessionMeta helper (unexported) reads just the first line
    of a session file and decodes the meta; avoids loading the
    whole transcript when BuildSessionTree only needs the
    parent/id pair.

tui additions:

  session_tree_dialog.go
    Flat list with indent-based nesting to match the other
    picker dialogs' shape. Up/down moves; enter switches; esc
    cancels. Rows show "<relative-when> <prompt-preview> N msgs"
    with a muted "[current]" tag on the current session.

  interactive.go
    - sessionTreeDialog field + constructor.
    - /session fork / /session tree cases in doSessionOp.
    - doSessionFork flips pendingFork=true and opens the
      jumpDialog over the agent's current messages.
    - The jump-dialog key handler checks pendingFork; if set,
      routes the selection to applyForkSelection instead of the
      normal applyJumpSelection. pendingFork clears on select
      OR on dismiss so a later plain /jump isn't hijacked.
    - applyForkSelection calls FlushSession (so the branch gets
      everything in memory, not just what was lazy-flushed),
      then core.BranchSession, then LoadSession to swap.
    - doSessionTree calls FlushSession first so the tree shows
      the true current message count, then
      core.BuildSessionTree, then hands the forest to the tree
      dialog.
    - applySessionTreeSelection hands the picked path to
      LoadSession.

tests:

  TestBranchSessionCopiesPrefix
    Parent with three messages; branch at upToIdx=2; verify the
    child has exactly 2 messages, parent ID matches, fork point
    = 2, ID rotated.

  TestBuildSessionTree
    Parent + 2 branches off it; verify roots=[parent],
    roots[0].Children has both branches.

README: /session row expanded to cover all four ops.
2026-04-20 11:10:56 +02:00
..
agent feat(session): /session fork + /session tree 2026-04-20 11:10:56 +02:00
assets add logo to callback page 2026-04-18 10:15:53 +02:00
auth feat(auth,tui): dark login pages + /logout picker 2026-04-19 20:14:22 +02:00
core feat(session): /session fork + /session tree 2026-04-20 11:10:56 +02:00
extproto feat(ext): phase 4 - full-event interception, arg rewrites, /reload-ext 2026-04-19 17:02:04 +02:00
provider perf(anthropic): fix cost double-count, tighten caching, correct catalog 2026-04-19 18:57:18 +02:00
skills perf(prompt): cut system prompt to the bone (410 -> 54 tokens) 2026-04-19 17:39:38 +02:00
tui feat(tui): /telegram connect | disconnect | status 2026-04-20 09:18:04 +02:00