Repository navigation
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Editing an earlier dashboard user message currently truncates only the client's message array, then submits the replacement as a normal turn to the original thread. Its checkpoint still contains the original question, the corresponding answer, and the later conversation. The model therefore receives context that has disappeared from the UI.
This PR makes an edit continue in a new thread seeded before the selected user message. It reuses the existing fork infrastructure to copy the retained prefix into both the checkpoint and stored history, resends the replacement into that new thread, and navigates to the resulting conversation. The original thread remains available with its complete transcript.
Fixes #1728
This branch targets
developat2571a04c30a8893d9dacf1bb169946824feeb5d2. The source links below intentionally refer to the earlier revision where the original behavior was inspected.Before and after
Consider a completed conversation:
If U1 is changed to
The launch budget is $25,000., the existing implementation displays only the replacement locally but continues the original checkpoint:After this change, the new conversation's model input begins with:
If U2 is edited instead, the new conversation retains:
The old U2, its answer, and later turns are excluded. This is a conversation-context change, not an attempt to undo tool side effects that have already happened.
Backend boundary and storage
The existing
POST /agents/{agent_id}/threads/{thread_id}/forkendpoint gains an optionaluser_turns_from_endlocator. Existing assistant-fork locators and copy-through boundaries remain unchanged; forks now also inherit conversation mode and approval policy. A request cannot specify both user and assistant turn locators.For an edit,
fork_dashboard_threadfinds the selected user message and copiesmessages[:index]. The selected user itself is excluded, so the edited text becomes a new user turn after the copied prefix. Editing the first user message produces an empty prefix and a fresh conversation.The implementation reuses the existing full-history read and fork write paths:
HistoryArchive, including any legacy segments.No schema migration, new storage format, or separate history-rewrite subsystem is introduced.
Message location
A persisted user message ID is preferred when it exists in checkpoint history. This avoids moving the edit boundary merely because another client appended a turn after the dashboard loaded.
Temporary UI IDs use a suffix count of user turns, not assistant bubbles or tool messages. Counting from the end also supports a dashboard that has loaded only the recent page and then prepended older pages. A multimodal HumanMessage remains one user turn, and intermediate tool traffic does not change the count.
When the supplied original text is non-empty, the fallback checks it and rejects an obvious mismatch instead of silently editing a different turn. It permits the original text followed by server-generated attachment hints. This is deliberately a fallback for UI IDs, not a claim to solve every possible concurrent-history ambiguity; a repeated identical prompt can still be indistinguishable without a persisted ID.
Dashboard continuation and recovery
The dashboard no longer rewrites the source's local message array before preparing the backend context. It requests the fork, reads the destination's server history, and waits if its projection is still loading. Only then does it append and send the replacement into the destination and navigate to it.
The replacement retains the original message's attachments and available composer context: model, connectors, knowledge bases, selected experts, and reasoning settings. The inherited thread settings provide conversation-mode and approval-policy continuity.
A failed fork or destination-history read leaves the source's messages and pagination cursor intact and does not send the replacement to the source. The UI reports an edit failure through an explicit English/Chinese message. A history-read failure after a successful fork can leave the newly created prefix-only conversation available; it does not destroy or rewrite the source.
The edit operation is guarded on both sides. The dashboard avoids editing a streaming conversation, active team speaker, or pending recoverable HITL pause, and prevents overlapping edit requests. The API continues to validate agent/thread ownership and rejects user-edit forks while the source turn or agent is active or a pending approval exists. Users must finish or stop the active work before editing.
The obsolete client-only
truncateAndReplaceUserMessagehelper is removed, and the user-visible behavior is recorded inCHANGELOG.md.Relevant existing code
The affected original behavior can be inspected at the fixed base revision:
Target branch
develop(feature / fix — default)main(release/*orhotfix/*only)Type of change
Test plan
Backend regression coverage exercises the REST endpoint with real database-backed thread/history services, for both legacy and versioned history and for edits to the first and later user turns. It checks the retained checkpoint prefix, destination history, source preservation, composer settings, ownership denial, active-turn/HITL rejection, and ambiguous locator validation. Existing assistant-fork behavior remains covered.
The model-context regression uses a real LangGraph
MessagesStategraph andInMemorySaver. Its deterministic model node records the messages it receives after the replacement is invoked. For a first-turn edit it receives onlyedited; for a second-turn edit it receivesfirst, first answer, edited. Neither case contains the original edited question, its old answer, or subsequent turns. This validates the context supplied to the model node with the actual checkpoint/reducer machinery; it does not require a live provider or depend on the wording of generated output.Frontend regression coverage runs the actual edit hook and chat store with mocked API/transport boundaries. Seven edit tests cover successful destination hydration and resend arguments, attachments and composer selections, source preservation, failed fork, failed history load, empty first-turn prefix, active stream/pending approval, and waiting for projection readiness.
Validation completed locally on Windows with Python 3.13.2:
uv run --no-sync pytest tests/unit/agents/test_thread_fork.py tests/integration/test_chat_ws.py -qdeveloptest_thread_fork.pyrun after the additional multimodal locator regressionuseSessions.test.ts, andTrajectoryInspector.test.tsxuv run --no-sync pytest tests/unit/i18n -quv run --no-sync ruff check src testsuv run --no-sync ruff format --check src testsuv run --no-sync mypy --strict src/octopnpm run buildgit diff --checkmake all PYTEST_JOBS=2The updated session-list and trajectory tests are included in the frontend run against the current
developbase. The model-context check uses a local deterministic graph; the reproduction and regression coverage do not require external provider credentials.The repository pre-commit hook passed (
make precommit: 121 passed, 35 skipped), andnpm run buildcompleted the TypeScript, Vite, and PWA build.make allpasses locallyChecklist
CHANGELOG.md(if user-facing)The API request model and endpoint summary describe the new edit boundary in the generated API documentation. No installation or configuration change is required.