An agent can keep working in one conversation while you prepare a question in another. The composer needs the same separation as the execution timeline: text, attachments, and conversation mentions should belong to the conversation that created them.
AIClaw 3.8.3 adds per-conversation composer drafts during the application's running session. It also captures the destination of asynchronous attachment reads and failed-send restoration, so switching conversations while work is pending does not redirect the result into the newly selected chat. This is released behavior; all source links below are pinned to v3.8.3.
Move ownership outside the view
Previously, ChatView.vue held the draft text, attachments, and mention bindings in view-local refs. A single mounted composer could therefore carry its input state across conversation changes, while leaving the view entirely could discard that local state.
The new drafts module is small: a module-level reactive map keyed by session identifier. Each entry contains three fields:
| Field | What belongs to the conversation |
|---|---|
| text | The unsent composer text |
| attachments | The selected attachment objects |
| bindings | The metadata behind conversation mentions |
The draftFor function returns an existing entry or creates an empty one. It does not copy the currently visible composer's values into a newly opened conversation.
ChatView.vue now exposes those fields through computed getters and setters that resolve the current session's draft. Selecting another conversation changes which record the composer displays. Navigating to Settings and back does not require the chat view instance to remain mounted for the draft to survive.
Keeping mention bindings alongside text matters as well. The visible mention and its session reference are two parts of the same draft; isolating only the text would leave the associated metadata attached to the wrong composer.
Capture the destination before awaiting a file read
A reactive reference to the current conversation is convenient for rendering. It is the wrong destination to look up after an asynchronous operation completes.
Consider this sequence:
- In conversation A, choose an attachment.
- While the file is being read, switch to conversation B.
- The file read finishes.
The attachment handler now captures A's draft object before starting its read loop. Attachment limits are evaluated against that captured record, and completed image, audio, or text reads append to its attachments array. The handler does not re-resolve the destination from the conversation selected after the await.
This preserves ownership through navigation. It does not make file reading synchronous or remove the existing attachment validation.
Restore a failed send to its original draft
Submission has another asynchronous boundary. The composer saves its original text, mention bindings, and pending attachments, then attempts to send. If that attempt returns false, the input needs to be restored.
In this release, the submission handler captures the draft object at entry. Recovery writes back to that object. Original text is prepended to text added in the meantime, while pending attachments and mention bindings are combined with the captured record's current arrays.
The important property is the destination: switching to B while A's send is pending should not make A's restored input appear in B. This is recovery of unsent composer state, not automatic resubmission or proof that a provider accepted a message.
Keep delayed transcription with its recording session
Dictation records the session identifier associated with recording. When transcription returns, the code compares that identifier with the currently selected session.
If they still match, it inserts at the current cursor. If they differ, it appends the transcription to the original session's draft, adding a newline when needed. A delayed transcript therefore has an explicit destination rather than inheriting whichever conversation happens to be selected when recognition finishes.
This does not claim recovery after every possible audio-device or application failure. It addresses routing the returned text across a conversation change.
Runtime retention has a clear boundary
The draft map lives in renderer memory. It is not a database-backed autosave service, does not survive an application restart, and does not synchronize between devices. Attachment objects are retained for the running renderer; this change does not introduce a separate durable attachment store.
It is also separate from the background-completion unread marker introduced in 3.8.2. That marker records an outcome awaiting attention. A draft holds input the user has not sent.
What the regression test proves
For this article, I extracted the tag-pinned drafts module and store IPC test file into a temporary directory and ran the test file with Node's test runner and the existing local Vue dependency. All 11 tests passed.
One is the new draft regression: it creates two session records, adds text and a text attachment to the first, verifies that the second starts empty, then updates the captured first record and checks that both sessions retain their own contents. The remaining tests exercise existing reactive-object and IPC cloning behavior.
Despite its asynchronous-recovery wording, the new regression test performs synchronous record mutations. It establishes map isolation and captured-object behavior; it does not simulate a pending attachment read, mount ChatView, drive microphone transcription, or execute an end-to-end failed send. The asynchronous routing described above is supported by the published handler code, not by a claim that this unit test covers those integrations.
The release notes separately report make check in temporary-signing mode and distinguish that run from fixed-certificate acceptance. I did not repeat that broader validation or inspect release-workflow completion for this article.
The improvement is a concrete ownership rule: changing the selected conversation changes the composer you see, while pending input work keeps the conversation it started with.
