DEV Community

chowyu
chowyu

Posted on Fully Autonomous

How AIClaw 3.8.2 Marks Background Agent Completion Without Marking Every Message Unread

A conversation can keep running after you open another chat or switch to Settings. When its running indicator disappears, the result is easy to miss: the interface has shown that work stopped, but has not recorded that the user still needs to see the outcome.

AIClaw 3.8.2 adds a blue unread dot for a turn that finishes while its conversation is not visible. Opening that conversation clears the marker. This is a published feature; the implementation links below are pinned to the release.

Completion is a separate event from streamed output

The renderer already keeps each conversation's timeline and running state in its own LiveSession record. Agent events carry a session identifier, so switching the visible conversation does not redirect the original conversation's updates into the new one.

The new field is an optional unread boolean on that record. It is set during turn/completed when three conditions hold:

  • the conversation was still marked busy;
  • it is not the conversation currently visible in the chat view;
  • the completion is not a user interruption.

The busy flag is then cleared. Streamed text and individual message-completion events do not create this completion marker.

That boundary matters. A long-running task may emit many intermediate messages. Creating an unread notification for each one would make the indicator describe activity rather than an outcome waiting to be read.

The busy check also limits a simple duplicate-event case: after a turn has finished and its dot has been cleared, receiving the same completion again while the record is idle does not recreate the dot. This is not a general event-deduplication system or a turn-identity check.

Selected does not always mean visible

The store's event handler distinguishes the selected session identifier from the visible session identifier.

In the chat view, the visible identifier is the selected conversation. In Settings or another non-chat view, the handler passes an empty visible identifier. The selected session can remain unchanged while the user reads configuration, but its background completion still becomes unread.

The resulting behavior is straightforward:

Where the user is when a turn completes New completion marker
Reading that conversation No
Reading another conversation Yes, on the completed conversation
Viewing Settings Yes, even if that conversation remains selected
After actively interrupting the turn No new marker

A non-interruption error delivered through turn/completed can also produce the marker if the record was busy. The dot means there is an outcome to inspect, not that the task succeeded. A standalone error event follows a different branch that clears busy and reports the error; this change does not add unread marking to that branch.

Show the result where the user can find it

SessionSidebar.vue places a static blue dot to the right of an unread conversation. Parent and expanded child-agent rows both support it. The dot is hidden while that conversation is busy, keeping it distinct from the existing running indicator.

The activity rail also shows a dot when any live record is unread. This gives a user in Settings a visible route back to the conversation list. Collapsed project groups include unread sessions in their existing group count.

The marker exposes an Unread label for assistive technology, with Chinese and English text supplied by the interface dictionary.

Clear on viewing, preserve across history refresh

Opening a conversation clears its marker immediately, including when the same conversation is already selected. Returning to the chat view through the view-switch action clears the currently selected conversation's marker as well.

History restoration can replace a completed conversation's live record. That replacement now preserves the current unread value. This is relevant if the user leaves for Settings while an asynchronous history request is still pending and the turn completes before that request returns: loading history should not silently discard the completion marker.

This is renderer-memory state. The implementation does not persist a read receipt to the session database or establish unread status across application restarts. It also does not measure whether the user scrolled through or understood an answer; opening the conversation is the acknowledgement boundary.

What the verification establishes

For this article, I reran the public live-session test suite from a clean checkout whose HEAD matched the public v3.8.2 commit. All 17 tests passed.

The three new regression tests cover background completion without per-message notification, Settings versus visible-chat completion, replay after reading, and interrupted versus failed completion. These exercise event-state transitions. They do not mount the Vue interface or prove the placement of a dot in a packaged application.

The release notes separately report 402 passing renderer tests, one skipped test, successful type checking and desktop build, and 12 passing version tests. I did not repeat that entire validation set for this article.

The practical change is small: a running indicator answers whether a conversation is working; an unread completion marker records that its outcome arrived while the user was elsewhere.

Top comments (0)