Edit-and-resend revisions reject or drop structured message context across Desktop and TUI
- Dominant language
- TypeScript
- Stars
- 5.4k
- Forks
- 502
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 715
Description
## What happened
Edit-and-resend does not preserve one canonical user-message contract across Desktop and TUI when the selected message or its retained history contains structured context such as quotes or attachments.
Three related behaviors are observable on current `main`:
1. **Desktop rejects a selected message containing a quote or attachment.** A large paste is automatically stored as a `QuoteRef`, so a normal large-paste message immediately becomes ineligible for Edit & resend. The tooltip says that historical messages containing quotes are not supported.
2. **Desktop also rejects editing a later plain-text message when an earlier retained turn contains an attachment.** This renderer guard is stale: Runtime Host conversation-copy now copies referenced user-upload artifacts, rewrites their target Session refs, and has integration coverage for revision copies.
3. **TUI `/rewind` permits the selected turn but restores only `userFacingText(promptMessage)`.** Its subsequent submit carries only `text`/`displayText`, so a selected message's `quotes` or `attachments` are not restored into the replacement turn. This is a fail-open counterpart to Desktop's fail-closed behavior.
Edit-and-resend is implemented as a non-destructive Session revision: copy the exact history before turn T, then submit a replacement T'. Its expected invariant is therefore:
```text
revision(T) = exactCopy(turns before T) + explicitlyEditedCanonicalContent(T)
```
`CanonicalContent(T)` must include the structured context the user keeps, changes, or explicitly removes; it must not degrade implicitly to a display string.
Expected behavior:
- Earlier retained quotes and attachments are copied exactly or revision preparation fails explicitly.
- The selected turn's text and structured context are restored into an editable revision draft.
- Unchanged structured context is preserved by default.
- Removing or changing a quote/attachment is an explicit edit.
- Desktop and TUI expose the same revision semantics even if their controls differ.
## How to reproduce
### A. Desktop large-paste quote
1. Open a Desktop conversation.
2. Paste text containing at least 1,000 characters or more than 10 lines.
3. Send the message and wait for the turn to settle.
4. Hover or activate **Edit & resend** on that user message.
Actual: the action is disabled with “Edit & resend does not yet support messages with quotes.”
Expected: create a revision draft containing the original text and quote, allowing the user to keep, edit, replace, or remove the quoted content before sending.
### B. Desktop retained attachment
1. Send turn T1 with an uploaded image.
2. Send a plain-text turn T2 and wait for it to settle.
3. Choose **Edit & resend** on T2.
Actual: the renderer rejects the revision because an earlier retained turn contains an attachment.
Expected: T1 and its rewritten, target-owned artifact are retained in the revision; T2 is restored for editing.
### C. TUI selected turn with structured context
1. Open in TUI a Session containing a user turn with `quotes` or `attachments` (for example, a turn created in Desktop).
2. Run `/rewind` and select that turn.
3. Edit the refilled prompt and send it.
Actual: `/rewind` creates the revision, but only the selected message's human-facing text is refilled and the replacement submit has no quotes or attachments.
Expected: the replacement draft retains the selected turn's structured context unless the user explicitly removes it, or TUI fails closed with a precise unsupported-content notice until it can do so.
## Environment
- Maka commit: `fcff80f58910f4de118fd7edbf7f02068239c50a`
- OS and version: Linux; exact distribution/version not captured for the reported Desktop observation
- Surface: Desktop and TUI; shared Runtime Host revision path
- Node.js version used for source verification: `v26.3.0`
## Logs, screenshots, or additional context
### Verified implementation boundaries
- Desktop disables selected user messages with attachments, directory references, or quotes before the revision action runs:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/packages/ui/src/chat-turn.tsx#L580-L598
- Desktop revision drafts currently retain only string fields (`originalText` and `previousComposerText`), not canonical structured message content:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/apps/desktop/src/renderer/app-shell-revision-actions.ts#L54-L66
- Desktop independently scans earlier turns and rejects any retained attachment history:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/apps/desktop/src/renderer/app-shell-revision-actions.ts#L156-L178
- Runtime Host revision copies use the exclusive `before` boundary for the selected turn:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/packages/runtime-host/src/server/session-revision-coordinator.ts#L358-L373
- Runtime Host already collects referenced Session files, copies artifacts, and rewrites target-owned references for the retained slice:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/packages/runtime-host/src/server/session-revision-coordinator.ts#L423-L428
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/packages/runtime-host/src/server/session-revision-coordinator.ts#L554-L602
- Cross-client integration coverage verifies that revision copies retain readable user-uploaded bytes under a rewritten target Session ref:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts#L1630-L1647
- TUI creates the same `session.revision.create` copy but returns only `userFacingText(promptMessage)` to its editor:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/packages/cli/src/runtime-host-session-driver.ts#L855-L880
- TUI's ordinary submit path sends only `text`/`displayText`:
https://github.com/apache/maka/blob/fcff80f58910f4de118fd7edbf7f02068239c50a/packages/cli/src/runtime-host-session-driver.ts#L519-L550
### Verification performed
The relevant current-source packages were rebuilt, then these focused tests passed:
```text
✔ two Clients share exact retryable Session branch and revision authority
✔ opens a rewind picker from /rewind and branches on select
```
The first test covers exact retained-history attachment copying in revision copies. The existing TUI rewind test covers plain-text refill only; it does not cover selected-turn quotes or attachments, which is the missing regression case.
### Suggested ownership boundary
The Host should remain authoritative for copying Session-owned artifact bytes and rewriting refs. Surface-specific clients should own the editable draft presentation, but consume/submit one shared canonical revision-draft content shape rather than reconstructing a turn from display text.
Unchanged detection must compare canonical structured content, not only `text.trim()`. In the current Desktop path, `hasPendingContext` counts attachments/directories but not quotes, so merely wiring the existing quote restore helper would still misclassify quote-only edits as unchanged.
Contributor guide
Assessment
This issue has not been assessed yet.