Summary
Add a way to fork a session from the current state, with the last model response included, as an alternative to the existing fork behavior, which always rewinds to before a selected user message (dropping that message and everything after it).
Today the /fork selector only offers user messages as fork points, and forking rewinds to the parent of the selected message. The selected user message is returned only as pre-fill text for the editor. As a consequence, the final assistant/model response can never be captured in a fork — the tail of any forked branch is always the assistant response that precedes some user message.
Current Behavior
For a conversation U1, A1, U2, A2, U3, A3:
- The fork selector offers only
U1, U2, U3 (user messages) — see getUserMessagesForForking() in agent-session.ts.
- Forking at the latest question
U3 branches from its parent A2 (createBranchedSession(selectedEntry.parentId) in agent-session.ts fork()), so the new branch tail is A2. Both U3 and the final response A3 are dropped.
U3's text is returned as selectedText to pre-fill the editor, letting the user re-run or edit that question — but there is no way to fork in a state that keeps A3.
There is no fork point after the last assistant response, because fork points are user-message-only and forking always excludes the selected message forward.
Proposed Behavior
Offer a fork option that branches from the current leaf / last entry (including the final model response A3), producing a new branch whose tail is the latest assistant answer, with an empty editor ready for the next turn. This is complementary to the existing "fork at a user message (re-ask that question)" flow.
Both behaviors should remain available:
- Fork from current state — include everything up to and including the last model response; start a fresh turn on top of it.
- Fork at a previous question — existing behavior; rewind to before a chosen user message and pre-fill it as an editable/re-runnable prompt.
Acceptance Criteria
Technical Notes
Relevant code:
packages/coding-agent/src/core/agent-session.ts
fork(entryId) (~L3630): rewinds via createBranchedSession(selectedEntry.parentId) and only accepts role === "user" entries.
getUserMessagesForForking() (~L3919): filters to user messages, so assistant responses are never offered as fork points.
packages/coding-agent/src/core/session-manager.ts
createBranchedSession(leafId) (~L1216): builds the branch up to and including leafId. Forking from the current state likely means calling this with the current leaf id (see getLeafId()), rather than a user message's parent.
packages/coding-agent/src/modes/interactive/interactive-mode.ts
showUserMessageSelector() (~L4298): the /fork selector UI (UserMessageSelectorComponent).
- Dashboard / RPC:
getForkMessages() and fork(entryId) in modes/rpc/rpc-client.ts, handlers in modes/rpc/rpc-mode.ts, dashboard endpoints in packages/dashboard/src/server/server.ts (/fork-messages, /fork).
Design considerations:
fork() currently asserts the entry is a user message and returns selectedText for editor pre-fill. A "fork from current state" path needs a different contract (no pre-fill, branch includes the tail). Consider either a dedicated method or an option flag on fork().
- Note the existing
navigateTree() can land on other nodes in-place but creates no new session file; this feature is specifically about creating a new forked branch that includes the latest answer.
- Ensure the UI clearly distinguishes "re-ask this question" (rewind) from "continue from here" (keep last answer).
Summary
Add a way to fork a session from the current state, with the last model response included, as an alternative to the existing fork behavior, which always rewinds to before a selected user message (dropping that message and everything after it).
Today the
/forkselector only offers user messages as fork points, and forking rewinds to the parent of the selected message. The selected user message is returned only as pre-fill text for the editor. As a consequence, the final assistant/model response can never be captured in a fork — the tail of any forked branch is always the assistant response that precedes some user message.Current Behavior
For a conversation
U1, A1, U2, A2, U3, A3:U1, U2, U3(user messages) — seegetUserMessagesForForking()inagent-session.ts.U3branches from its parentA2(createBranchedSession(selectedEntry.parentId)inagent-session.tsfork()), so the new branch tail isA2. BothU3and the final responseA3are dropped.U3's text is returned asselectedTextto pre-fill the editor, letting the user re-run or edit that question — but there is no way to fork in a state that keepsA3.There is no fork point after the last assistant response, because fork points are user-message-only and forking always excludes the selected message forward.
Proposed Behavior
Offer a fork option that branches from the current leaf / last entry (including the final model response
A3), producing a new branch whose tail is the latest assistant answer, with an empty editor ready for the next turn. This is complementary to the existing "fork at a user message (re-ask that question)" flow.Both behaviors should remain available:
Acceptance Criteria
A3), not just up throughA2./forkUI (e.g. an explicit "fork from current state / here" entry alongside the user-message list).getForkMessages/forkRPC), and print/RPC modes where applicable.session_before_fork/session_forkevents fire as they do today.Technical Notes
Relevant code:
packages/coding-agent/src/core/agent-session.tsfork(entryId)(~L3630): rewinds viacreateBranchedSession(selectedEntry.parentId)and only acceptsrole === "user"entries.getUserMessagesForForking()(~L3919): filters to user messages, so assistant responses are never offered as fork points.packages/coding-agent/src/core/session-manager.tscreateBranchedSession(leafId)(~L1216): builds the branch up to and includingleafId. Forking from the current state likely means calling this with the current leaf id (seegetLeafId()), rather than a user message's parent.packages/coding-agent/src/modes/interactive/interactive-mode.tsshowUserMessageSelector()(~L4298): the/forkselector UI (UserMessageSelectorComponent).getForkMessages()andfork(entryId)inmodes/rpc/rpc-client.ts, handlers inmodes/rpc/rpc-mode.ts, dashboard endpoints inpackages/dashboard/src/server/server.ts(/fork-messages,/fork).Design considerations:
fork()currently asserts the entry is a user message and returnsselectedTextfor editor pre-fill. A "fork from current state" path needs a different contract (no pre-fill, branch includes the tail). Consider either a dedicated method or an option flag onfork().navigateTree()can land on other nodes in-place but creates no new session file; this feature is specifically about creating a new forked branch that includes the latest answer.