Skip to content

feat(notifications): name the session a notification is about - #834

Merged
xintaofei merged 2 commits into
xintaofei:mainfrom
Jonathan-Asher:pr/notification-session
Sep 28, 2026
Merged

xintaofei merged 2 commits into
xintaofei:mainfrom
Jonathan-Asher:pr/notification-session

Conversation

@Jonathan-Asher

Copy link
Copy Markdown
Contributor

Summary

OS notifications raised by agent sessions (turn finished or failed, session error, permission request, question, background task settled) were titled <folder> - Codeg with the window's active folder, not the folder of the session that raised them, and the body named only the agent. With several sessions running there was no way to tell which one needed you, and the folder in the title could be the wrong one.

Each of these notifications now names its session: the session's title as the notification title, and the session's own folder at the start of the body.

Behavior

  • Title: the conversation's title, with reference links folded the same way as in the tab strip. A draft that has no conversation row yet uses its tab label.
  • Body: <folder> · <message>, where the folder is the session's own (its alias when one is set). A chat-mode conversation names its hidden "Chat" folder; a chat draft that has no folder yet names none rather than the active one.
  • "Hide notification contents" on: the notification keeps its previous shape, <folder> - Codeg over the message alone, but names the session's folder. A session title is the user's own words (often the first line of their prompt), which that setting exists to keep out of the notification centre.
  • A connection no tab owns (a canvas card, a delegated sub-agent's own connection) keeps the previous <active folder> - Codeg title.
  • Work-task notifications from the Tasks view are unchanged; they already name the task's own folder.

Implementation:

  • src/lib/notification-session.ts (new): sessionNotificationPayload(contextKey, activeFolderName, { body, redactedBody }). The connection's context key is its tab id; from the tab it finds the persisted conversation (through the runtime session's row id for a draft whose first send has not bound the tab yet) and the folder in allFolders.
  • src/lib/desktop-notification.ts: NotifyPayload gains an optional redactedTitle, used when contents are hidden, the same way as redactedBody.
  • src/contexts/acp-connections-context.tsx: the six notifyDesktop call sites wrap their existing payloads in sessionNotificationPayload. What they notify and when (failure-aware turn completion, acpErrorNotifiesDesktop, quiet) is unchanged.

Verification

  • src/lib/notification-session.test.ts (new, 7 tests): the session title and the session's own folder win over a different active folder; folder alias and reference-link folding; draft tab label; a draft reached through its runtime session; a chat-mode conversation's hidden folder; a folderless chat draft names no folder; an unknown context key keeps the old title.
  • src/lib/desktop-notification.test.ts: the redacted title replaces the title only when contents are hidden.
  • CI passes: the frontend job (pnpm lint ., pnpm test, pnpm build) and every Rust desktop/server cell on Ubuntu, macOS and Windows. There are no Rust changes.

An OS notification was titled with the window's active folder, not the
folder of the session that raised it, and its body named only the agent.
With several sessions running there was no telling which one had
finished, failed, asked a question or was waiting on a permission, and
the folder in the title could be the wrong one.

Every notification raised from an ACP event (turn finished or failed,
session error, permission request, question, background task settled)
now resolves its session from the connection's context key, which is its
tab id. The tab leads to the persisted conversation: its title becomes
the notification title and its own folder leads the body
("<folder> · <message>"). A draft with no conversation row yet uses its
tab label. A context key no tab owns (a canvas card, a delegated
sub-agent's connection) keeps the previous "<folder> - Codeg" title.

With "hide notification contents" on, the notification reads as before,
"<folder> - Codeg" over the message alone, but names the session's own
folder: a session title is the user's own words, which that setting
exists to keep out of the notification centre. NotifyPayload gains an
optional redactedTitle for this, applied like redactedBody.

@dawNotPoi dawNotPoi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the session lookup, notification call sites, and redacted title/body behavior at 09d74bc. Focused notification tests passed 29/29 locally; TypeScript and ESLint on the changed files passed. No blocking findings for this PR scope. Notification-click navigation remains tracked separately in #808.

Closing a tab whose agent is still busy keeps its connection alive (see
`shouldDisconnectOnUnmount`), so the turn that finishes in the background
arrives under a tab id that no longer exists. The tab-only lookup then
fell back to the window's active folder — the wrong-folder title this
change set out to fix, in exactly the case where the notification is the
only way the user learns the turn finished.

When no tab owns the key, resolve the conversation the connection serves
instead: the row id `connect()` was given, or the one a first send linked
(`conversation_linked`), both already remembered per context key past the
surface itself. Not the agent's session id — a Claude `/clear` re-points
the row's `external_id` while the ACP session keeps its own, and Gemini /
Cline rows are renormalized to the parser's id. A tab still wins when
there is one. This also names the session behind a canvas card. A
connection with no known conversation (a delegated sub-agent's own)
keeps the active-folder title.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@xintaofei

Copy link
Copy Markdown
Owner

codeg work task 246 is done — #834 (4 files, +211/-36).

@xintaofei
xintaofei merged commit a8201a6 into xintaofei:main Sep 28, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants