From 7f19311080c7efb5103453903967b53649449854 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 15 May 2026 17:24:27 -0700 Subject: [PATCH 1/5] Improve v2 macOS notification details --- .../V2NotificationController.tsx | 8 ++ .../HostNotificationSubscriber.tsx | 2 + .../lib/lifecycleEvents.ts | 32 ++++- .../lib/notificationContent.test.ts | 116 +++++++++++++++ .../lib/notificationContent.ts | 136 ++++++++++++++++++ 5 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts create mode 100644 apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx index 5b9214f19f6..4365eb48885 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx @@ -15,6 +15,8 @@ interface WorkspaceHostRow { workspaceId: string; organizationId: string; hostId: string; + name: string; + branch: string; } interface HostNotificationSubscriberGroup { @@ -43,6 +45,8 @@ export function V2NotificationController() { workspaceId: v2Workspaces.id, organizationId: v2Workspaces.organizationId, hostId: v2Workspaces.hostId, + name: v2Workspaces.name, + branch: v2Workspaces.branch, })), [collections], ); @@ -118,6 +122,10 @@ function groupWorkspacesByHostUrl({ const group = groups.get(hostUrl) ?? []; group.push({ workspaceId: workspace.workspaceId, + workspaceName: + workspace.name.trim() || + workspace.branch.trim() || + workspace.workspaceId, paneLayout: paneLayoutsByWorkspaceId.get(workspace.workspaceId) ?? null, }); groups.set(hostUrl, group); diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/components/HostNotificationSubscriber/HostNotificationSubscriber.tsx b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/components/HostNotificationSubscriber/HostNotificationSubscriber.tsx index ef274e13b03..ff1390bd1dc 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/components/HostNotificationSubscriber/HostNotificationSubscriber.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/components/HostNotificationSubscriber/HostNotificationSubscriber.tsx @@ -16,6 +16,7 @@ import { export interface HostNotificationWorkspaceState { workspaceId: string; + workspaceName: string; paneLayout: WorkspaceState | null; } @@ -53,6 +54,7 @@ export function HostNotificationSubscriber({ if (!workspace) return; handleV2AgentLifecycleEvent({ workspaceId, + workspaceName: workspace.workspaceName, payload, paneLayout: workspace.paneLayout, volume, diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts index 6892ddc5eb6..4e240856c1d 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts @@ -4,6 +4,7 @@ import type { TerminalLifecyclePayload, } from "@superset/workspace-client"; import { playRingtone } from "renderer/lib/ringtones/play"; +import { terminalRuntimeRegistry } from "renderer/lib/terminal/terminal-runtime-registry"; import { electronTrpcClient } from "renderer/lib/trpc-client"; import type { PaneViewerData } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types"; import { useRingtoneStore } from "renderer/stores/ringtone"; @@ -12,6 +13,7 @@ import { useV2NotificationStore, type V2NotificationSourceInput, } from "renderer/stores/v2-notifications"; +import { getV2NativeNotificationContent } from "./notificationContent"; import { isV2NotificationTargetVisible, resolveV2NotificationTarget, @@ -27,12 +29,14 @@ import { resolveV2AgentStatusTransition } from "./statusTransitions"; */ export function handleV2AgentLifecycleEvent({ workspaceId, + workspaceName, payload, paneLayout, volume, muted, }: { workspaceId: string; + workspaceName: string; payload: AgentLifecyclePayload; paneLayout: WorkspaceState | null | undefined; volume: number; @@ -61,7 +65,13 @@ export function handleV2AgentLifecycleEvent({ const ringtoneId = useRingtoneStore.getState().selectedRingtoneId; void playRingtone({ ringtoneId, volume, muted }); - showNativeNotification({ payload, workspaceId, target }); + showNativeNotification({ + payload, + workspaceId, + workspaceName, + target, + paneLayout, + }); } export function handleV2TerminalLifecycleEvent({ @@ -134,17 +144,27 @@ function shouldSuppress( function showNativeNotification({ payload, workspaceId, + workspaceName, target, + paneLayout, }: { payload: AgentLifecyclePayload; workspaceId: string; + workspaceName: string; target: V2NotificationTarget; + paneLayout: WorkspaceState | null | undefined; }): void { - const isPermission = payload.eventType === "PermissionRequest"; - const title = isPermission ? "Awaiting Response" : "Agent Complete"; - const body = isPermission - ? "Your agent needs input" - : "Your agent has finished"; + const terminalTitle = + terminalRuntimeRegistry + .getTitle(target.terminalId, target.paneId) + ?.trim() || terminalRuntimeRegistry.getTitle(target.terminalId)?.trim(); + const { title, body } = getV2NativeNotificationContent({ + workspaceName, + payload, + target, + paneLayout, + terminalTitle, + }); void electronTrpcClient.notifications.showNative .mutate({ diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts new file mode 100644 index 00000000000..4ecb8b71ff8 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts @@ -0,0 +1,116 @@ +import { describe, expect, it } from "bun:test"; +import type { WorkspaceState } from "@superset/panes"; +import type { AgentLifecyclePayload } from "@superset/workspace-client"; +import type { PaneViewerData } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types"; +import { getV2NativeNotificationContent } from "./notificationContent"; + +const layout: WorkspaceState = { + version: 1, + activeTabId: "tab-1", + tabs: [ + { + id: "tab-1", + titleOverride: "Backend", + createdAt: 1, + activePaneId: "pane-1", + layout: { type: "pane", paneId: "pane-1" }, + panes: { + "pane-1": { + id: "pane-1", + kind: "terminal", + titleOverride: "Test runner", + data: { terminalId: "terminal-1" }, + }, + }, + }, + ], +}; + +function payload( + overrides: Partial, +): AgentLifecyclePayload { + return { + eventType: "Stop", + terminalId: "terminal-1", + occurredAt: 1, + ...overrides, + }; +} + +describe("getV2NativeNotificationContent", () => { + it("includes agent, workspace, pane, and tab labels for completion", () => { + expect( + getV2NativeNotificationContent({ + workspaceName: "Improve notifications", + payload: payload({ + agent: { agentId: "codex", sessionId: "session-1" }, + }), + target: { + workspaceId: "workspace-1", + tabId: "tab-1", + paneId: "pane-1", + terminalId: "terminal-1", + }, + paneLayout: layout, + }), + ).toEqual({ + title: "Codex Complete - Improve notifications", + body: "Pane: Test runner | Tab: Backend", + }); + }); + + it("uses needs-input copy for permission requests", () => { + expect( + getV2NativeNotificationContent({ + workspaceName: "Improve notifications", + payload: payload({ + eventType: "PermissionRequest", + agent: { agentId: "claude" }, + }), + target: { + workspaceId: "workspace-1", + tabId: "tab-1", + paneId: "pane-1", + terminalId: "terminal-1", + }, + paneLayout: layout, + }), + ).toMatchObject({ + title: "Claude Needs Input - Improve notifications", + body: "Pane: Test runner | Tab: Backend", + }); + }); + + it("falls back to runtime terminal title and short terminal id", () => { + expect( + getV2NativeNotificationContent({ + workspaceName: " ", + payload: payload({ agent: { agentId: "droid" } }), + target: { + workspaceId: "workspace-1", + terminalId: "terminal-long-id", + }, + paneLayout: null, + terminalTitle: "deploy script", + }), + ).toEqual({ + title: "Droid Complete - Workspace", + body: "Pane: deploy script", + }); + + expect( + getV2NativeNotificationContent({ + workspaceName: "", + payload: payload({ agent: undefined }), + target: { + workspaceId: "workspace-1", + terminalId: "terminal-long-id", + }, + paneLayout: null, + }), + ).toMatchObject({ + title: "Agent Complete - Workspace", + body: "Pane: Terminal long-id", + }); + }); +}); diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts new file mode 100644 index 00000000000..2ac9039c0b5 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts @@ -0,0 +1,136 @@ +import type { Pane, Tab, WorkspaceState } from "@superset/panes"; +import { + BUILTIN_AGENT_LABELS, + type BuiltinAgentId, +} from "@superset/shared/agent-catalog"; +import type { + AgentIdentity, + AgentLifecyclePayload, +} from "@superset/workspace-client"; +import type { PaneViewerData } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types"; +import type { V2NotificationTarget } from "./resolveV2NotificationTarget"; + +interface V2NativeNotificationContentOptions { + workspaceName: string; + payload: AgentLifecyclePayload; + target: V2NotificationTarget; + paneLayout: WorkspaceState | null | undefined; + terminalTitle?: string | null; +} + +interface ResolvedPaneLocation { + tab?: Tab; + pane?: Pane; +} + +const PANE_KIND_LABELS: Record = { + browser: "Browser", + chat: "Chat", + comment: "Comment", + devtools: "DevTools", + diff: "Changes", + file: "File", + terminal: "Terminal", +}; + +export function getV2NativeNotificationContent({ + workspaceName, + payload, + target, + paneLayout, + terminalTitle, +}: V2NativeNotificationContentOptions): { title: string; body: string } { + const agentLabel = getAgentLabel(payload.agent); + const action = + payload.eventType === "PermissionRequest" ? "Needs Input" : "Complete"; + const workspaceLabel = cleanLabel(workspaceName) ?? "Workspace"; + const location = resolvePaneLocation({ paneLayout, target }); + const paneLabel = getPaneLabel({ + pane: location.pane, + target, + terminalTitle, + }); + const tabLabel = getTabLabel(location.tab, paneLayout); + const bodyParts = [ + `Pane: ${paneLabel}`, + tabLabel ? `Tab: ${tabLabel}` : null, + ].filter((part): part is string => Boolean(part)); + + return { + title: `${agentLabel} ${action} - ${workspaceLabel}`, + body: bodyParts.join(" | "), + }; +} + +function getAgentLabel(agent: AgentIdentity | undefined): string { + const agentId = cleanLabel(agent?.agentId); + if (!agentId) return "Agent"; + if (agentId in BUILTIN_AGENT_LABELS) { + return BUILTIN_AGENT_LABELS[agentId as BuiltinAgentId]; + } + return humanizeIdentifier(agentId); +} + +function resolvePaneLocation({ + paneLayout, + target, +}: { + paneLayout: WorkspaceState | null | undefined; + target: V2NotificationTarget; +}): ResolvedPaneLocation { + const tab = target.tabId + ? paneLayout?.tabs.find((candidate) => candidate.id === target.tabId) + : undefined; + const pane = target.paneId ? tab?.panes[target.paneId] : undefined; + return { tab, pane }; +} + +function getPaneLabel({ + pane, + target, + terminalTitle, +}: { + pane: Pane | undefined; + target: V2NotificationTarget; + terminalTitle?: string | null; +}): string { + return ( + cleanLabel(pane?.titleOverride) ?? + cleanLabel(terminalTitle) ?? + (pane ? PANE_KIND_LABELS[pane.kind] : undefined) ?? + `Terminal ${shortId(target.terminalId)}` + ); +} + +function getTabLabel( + tab: Tab | undefined, + paneLayout: WorkspaceState | null | undefined, +): string | null { + if (!tab) return null; + const explicitTitle = cleanLabel(tab.titleOverride); + if (explicitTitle) return explicitTitle; + const index = paneLayout?.tabs.indexOf(tab); + return typeof index === "number" && index >= 0 ? `Tab ${index + 1}` : null; +} + +function cleanLabel(value: string | null | undefined): string | null { + const trimmed = value?.trim(); + return trimmed ? trimmed : null; +} + +function humanizeIdentifier(value: string): string { + const words = value + .replace(/^custom:/, "") + .split(/[-_:\s]+/) + .filter(Boolean); + if (words.length === 0) return "Agent"; + return words + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); +} + +function shortId(value: string): string { + const withoutTerminalPrefix = value.replace(/^terminal[-_:]?/i, ""); + const candidate = withoutTerminalPrefix || value; + return candidate.length > 8 ? candidate.slice(0, 8) : candidate; +} From 77dd3e737bf3640dc477cbf8dfbeb810375797b8 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 15 May 2026 17:30:50 -0700 Subject: [PATCH 2/5] Adjust v2 notification copy --- .../lib/notificationContent.test.ts | 16 ++++++++-------- .../lib/notificationContent.ts | 6 +++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts index 4ecb8b71ff8..add4037b9be 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts @@ -54,8 +54,8 @@ describe("getV2NativeNotificationContent", () => { paneLayout: layout, }), ).toEqual({ - title: "Codex Complete - Improve notifications", - body: "Pane: Test runner | Tab: Backend", + title: "Agent Complete - Codex", + body: "Workspace: Improve notifications | Pane: Test runner | Tab: Backend", }); }); @@ -76,8 +76,8 @@ describe("getV2NativeNotificationContent", () => { paneLayout: layout, }), ).toMatchObject({ - title: "Claude Needs Input - Improve notifications", - body: "Pane: Test runner | Tab: Backend", + title: "Agent Needs Input - Claude", + body: "Workspace: Improve notifications | Pane: Test runner | Tab: Backend", }); }); @@ -94,8 +94,8 @@ describe("getV2NativeNotificationContent", () => { terminalTitle: "deploy script", }), ).toEqual({ - title: "Droid Complete - Workspace", - body: "Pane: deploy script", + title: "Agent Complete - Droid", + body: "Workspace: Workspace | Pane: deploy script", }); expect( @@ -109,8 +109,8 @@ describe("getV2NativeNotificationContent", () => { paneLayout: null, }), ).toMatchObject({ - title: "Agent Complete - Workspace", - body: "Pane: Terminal long-id", + title: "Agent Complete", + body: "Workspace: Workspace | Pane: Terminal long-id", }); }); }); diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts index 2ac9039c0b5..8e8900bafb0 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts @@ -52,12 +52,16 @@ export function getV2NativeNotificationContent({ }); const tabLabel = getTabLabel(location.tab, paneLayout); const bodyParts = [ + `Workspace: ${workspaceLabel}`, `Pane: ${paneLabel}`, tabLabel ? `Tab: ${tabLabel}` : null, ].filter((part): part is string => Boolean(part)); return { - title: `${agentLabel} ${action} - ${workspaceLabel}`, + title: + agentLabel === "Agent" + ? `Agent ${action}` + : `Agent ${action} - ${agentLabel}`, body: bodyParts.join(" | "), }; } From 0b5d83265dffcd7567d6f5dbe67fa4e7e3604797 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 15 May 2026 17:36:32 -0700 Subject: [PATCH 3/5] Simplify v2 notification body --- .../V2NotificationController.tsx | 4 +- .../lib/lifecycleEvents.ts | 11 --- .../lib/notificationContent.test.ts | 61 ++----------- .../lib/notificationContent.ts | 86 +------------------ 4 files changed, 8 insertions(+), 154 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx index 4365eb48885..ec758ed625e 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/V2NotificationController.tsx @@ -123,9 +123,7 @@ function groupWorkspacesByHostUrl({ group.push({ workspaceId: workspace.workspaceId, workspaceName: - workspace.name.trim() || - workspace.branch.trim() || - workspace.workspaceId, + workspace.name.trim() || workspace.branch.trim() || "Workspace", paneLayout: paneLayoutsByWorkspaceId.get(workspace.workspaceId) ?? null, }); groups.set(hostUrl, group); diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts index 4e240856c1d..841d9c27f81 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/lifecycleEvents.ts @@ -4,7 +4,6 @@ import type { TerminalLifecyclePayload, } from "@superset/workspace-client"; import { playRingtone } from "renderer/lib/ringtones/play"; -import { terminalRuntimeRegistry } from "renderer/lib/terminal/terminal-runtime-registry"; import { electronTrpcClient } from "renderer/lib/trpc-client"; import type { PaneViewerData } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types"; import { useRingtoneStore } from "renderer/stores/ringtone"; @@ -70,7 +69,6 @@ export function handleV2AgentLifecycleEvent({ workspaceId, workspaceName, target, - paneLayout, }); } @@ -146,24 +144,15 @@ function showNativeNotification({ workspaceId, workspaceName, target, - paneLayout, }: { payload: AgentLifecyclePayload; workspaceId: string; workspaceName: string; target: V2NotificationTarget; - paneLayout: WorkspaceState | null | undefined; }): void { - const terminalTitle = - terminalRuntimeRegistry - .getTitle(target.terminalId, target.paneId) - ?.trim() || terminalRuntimeRegistry.getTitle(target.terminalId)?.trim(); const { title, body } = getV2NativeNotificationContent({ workspaceName, payload, - target, - paneLayout, - terminalTitle, }); void electronTrpcClient.notifications.showNative diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts index add4037b9be..70291796269 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts @@ -1,31 +1,7 @@ import { describe, expect, it } from "bun:test"; -import type { WorkspaceState } from "@superset/panes"; import type { AgentLifecyclePayload } from "@superset/workspace-client"; -import type { PaneViewerData } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types"; import { getV2NativeNotificationContent } from "./notificationContent"; -const layout: WorkspaceState = { - version: 1, - activeTabId: "tab-1", - tabs: [ - { - id: "tab-1", - titleOverride: "Backend", - createdAt: 1, - activePaneId: "pane-1", - layout: { type: "pane", paneId: "pane-1" }, - panes: { - "pane-1": { - id: "pane-1", - kind: "terminal", - titleOverride: "Test runner", - data: { terminalId: "terminal-1" }, - }, - }, - }, - ], -}; - function payload( overrides: Partial, ): AgentLifecyclePayload { @@ -38,24 +14,17 @@ function payload( } describe("getV2NativeNotificationContent", () => { - it("includes agent, workspace, pane, and tab labels for completion", () => { + it("uses the agent label in the title and workspace label in the body", () => { expect( getV2NativeNotificationContent({ workspaceName: "Improve notifications", payload: payload({ agent: { agentId: "codex", sessionId: "session-1" }, }), - target: { - workspaceId: "workspace-1", - tabId: "tab-1", - paneId: "pane-1", - terminalId: "terminal-1", - }, - paneLayout: layout, }), ).toEqual({ title: "Agent Complete - Codex", - body: "Workspace: Improve notifications | Pane: Test runner | Tab: Backend", + body: "Workspace: Improve notifications", }); }); @@ -67,50 +36,32 @@ describe("getV2NativeNotificationContent", () => { eventType: "PermissionRequest", agent: { agentId: "claude" }, }), - target: { - workspaceId: "workspace-1", - tabId: "tab-1", - paneId: "pane-1", - terminalId: "terminal-1", - }, - paneLayout: layout, }), ).toMatchObject({ title: "Agent Needs Input - Claude", - body: "Workspace: Improve notifications | Pane: Test runner | Tab: Backend", + body: "Workspace: Improve notifications", }); }); - it("falls back to runtime terminal title and short terminal id", () => { + it("falls back to generic labels", () => { expect( getV2NativeNotificationContent({ workspaceName: " ", payload: payload({ agent: { agentId: "droid" } }), - target: { - workspaceId: "workspace-1", - terminalId: "terminal-long-id", - }, - paneLayout: null, - terminalTitle: "deploy script", }), ).toEqual({ title: "Agent Complete - Droid", - body: "Workspace: Workspace | Pane: deploy script", + body: "Workspace: Workspace", }); expect( getV2NativeNotificationContent({ workspaceName: "", payload: payload({ agent: undefined }), - target: { - workspaceId: "workspace-1", - terminalId: "terminal-long-id", - }, - paneLayout: null, }), ).toMatchObject({ title: "Agent Complete", - body: "Workspace: Workspace | Pane: Terminal long-id", + body: "Workspace: Workspace", }); }); }); diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts index 8e8900bafb0..5f17b84f9c8 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts @@ -1,4 +1,3 @@ -import type { Pane, Tab, WorkspaceState } from "@superset/panes"; import { BUILTIN_AGENT_LABELS, type BuiltinAgentId, @@ -7,62 +6,27 @@ import type { AgentIdentity, AgentLifecyclePayload, } from "@superset/workspace-client"; -import type { PaneViewerData } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types"; -import type { V2NotificationTarget } from "./resolveV2NotificationTarget"; interface V2NativeNotificationContentOptions { workspaceName: string; payload: AgentLifecyclePayload; - target: V2NotificationTarget; - paneLayout: WorkspaceState | null | undefined; - terminalTitle?: string | null; } -interface ResolvedPaneLocation { - tab?: Tab; - pane?: Pane; -} - -const PANE_KIND_LABELS: Record = { - browser: "Browser", - chat: "Chat", - comment: "Comment", - devtools: "DevTools", - diff: "Changes", - file: "File", - terminal: "Terminal", -}; - export function getV2NativeNotificationContent({ workspaceName, payload, - target, - paneLayout, - terminalTitle, }: V2NativeNotificationContentOptions): { title: string; body: string } { const agentLabel = getAgentLabel(payload.agent); const action = payload.eventType === "PermissionRequest" ? "Needs Input" : "Complete"; const workspaceLabel = cleanLabel(workspaceName) ?? "Workspace"; - const location = resolvePaneLocation({ paneLayout, target }); - const paneLabel = getPaneLabel({ - pane: location.pane, - target, - terminalTitle, - }); - const tabLabel = getTabLabel(location.tab, paneLayout); - const bodyParts = [ - `Workspace: ${workspaceLabel}`, - `Pane: ${paneLabel}`, - tabLabel ? `Tab: ${tabLabel}` : null, - ].filter((part): part is string => Boolean(part)); return { title: agentLabel === "Agent" ? `Agent ${action}` : `Agent ${action} - ${agentLabel}`, - body: bodyParts.join(" | "), + body: `Workspace: ${workspaceLabel}`, }; } @@ -75,48 +39,6 @@ function getAgentLabel(agent: AgentIdentity | undefined): string { return humanizeIdentifier(agentId); } -function resolvePaneLocation({ - paneLayout, - target, -}: { - paneLayout: WorkspaceState | null | undefined; - target: V2NotificationTarget; -}): ResolvedPaneLocation { - const tab = target.tabId - ? paneLayout?.tabs.find((candidate) => candidate.id === target.tabId) - : undefined; - const pane = target.paneId ? tab?.panes[target.paneId] : undefined; - return { tab, pane }; -} - -function getPaneLabel({ - pane, - target, - terminalTitle, -}: { - pane: Pane | undefined; - target: V2NotificationTarget; - terminalTitle?: string | null; -}): string { - return ( - cleanLabel(pane?.titleOverride) ?? - cleanLabel(terminalTitle) ?? - (pane ? PANE_KIND_LABELS[pane.kind] : undefined) ?? - `Terminal ${shortId(target.terminalId)}` - ); -} - -function getTabLabel( - tab: Tab | undefined, - paneLayout: WorkspaceState | null | undefined, -): string | null { - if (!tab) return null; - const explicitTitle = cleanLabel(tab.titleOverride); - if (explicitTitle) return explicitTitle; - const index = paneLayout?.tabs.indexOf(tab); - return typeof index === "number" && index >= 0 ? `Tab ${index + 1}` : null; -} - function cleanLabel(value: string | null | undefined): string | null { const trimmed = value?.trim(); return trimmed ? trimmed : null; @@ -132,9 +54,3 @@ function humanizeIdentifier(value: string): string { .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(" "); } - -function shortId(value: string): string { - const withoutTerminalPrefix = value.replace(/^terminal[-_:]?/i, ""); - const candidate = withoutTerminalPrefix || value; - return candidate.length > 8 ? candidate.slice(0, 8) : candidate; -} From 727e2e46c0e303df402325856459ba7de6e2d3af Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 15 May 2026 17:49:46 -0700 Subject: [PATCH 4/5] Remove workspace label prefix --- .../lib/notificationContent.test.ts | 8 ++++---- .../V2NotificationController/lib/notificationContent.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts index 70291796269..404747e91e3 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts @@ -24,7 +24,7 @@ describe("getV2NativeNotificationContent", () => { }), ).toEqual({ title: "Agent Complete - Codex", - body: "Workspace: Improve notifications", + body: "Improve notifications", }); }); @@ -39,7 +39,7 @@ describe("getV2NativeNotificationContent", () => { }), ).toMatchObject({ title: "Agent Needs Input - Claude", - body: "Workspace: Improve notifications", + body: "Improve notifications", }); }); @@ -51,7 +51,7 @@ describe("getV2NativeNotificationContent", () => { }), ).toEqual({ title: "Agent Complete - Droid", - body: "Workspace: Workspace", + body: "Workspace", }); expect( @@ -61,7 +61,7 @@ describe("getV2NativeNotificationContent", () => { }), ).toMatchObject({ title: "Agent Complete", - body: "Workspace: Workspace", + body: "Workspace", }); }); }); diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts index 5f17b84f9c8..8406991c139 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts @@ -26,7 +26,7 @@ export function getV2NativeNotificationContent({ agentLabel === "Agent" ? `Agent ${action}` : `Agent ${action} - ${agentLabel}`, - body: `Workspace: ${workspaceLabel}`, + body: workspaceLabel, }; } From d20323939873af96cf75333fdced90ce92360695 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 15 May 2026 17:53:12 -0700 Subject: [PATCH 5/5] Use agent first notification titles --- .../lib/notificationContent.test.ts | 10 +++++----- .../lib/notificationContent.ts | 7 ++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts index 404747e91e3..bbe72f856fe 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.test.ts @@ -23,12 +23,12 @@ describe("getV2NativeNotificationContent", () => { }), }), ).toEqual({ - title: "Agent Complete - Codex", + title: "Codex - Complete", body: "Improve notifications", }); }); - it("uses needs-input copy for permission requests", () => { + it("uses needs-attention copy for permission requests", () => { expect( getV2NativeNotificationContent({ workspaceName: "Improve notifications", @@ -38,7 +38,7 @@ describe("getV2NativeNotificationContent", () => { }), }), ).toMatchObject({ - title: "Agent Needs Input - Claude", + title: "Claude - Needs Attention", body: "Improve notifications", }); }); @@ -50,7 +50,7 @@ describe("getV2NativeNotificationContent", () => { payload: payload({ agent: { agentId: "droid" } }), }), ).toEqual({ - title: "Agent Complete - Droid", + title: "Droid - Complete", body: "Workspace", }); @@ -60,7 +60,7 @@ describe("getV2NativeNotificationContent", () => { payload: payload({ agent: undefined }), }), ).toMatchObject({ - title: "Agent Complete", + title: "Agent - Complete", body: "Workspace", }); }); diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts index 8406991c139..5f525b53407 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/V2NotificationController/lib/notificationContent.ts @@ -18,14 +18,11 @@ export function getV2NativeNotificationContent({ }: V2NativeNotificationContentOptions): { title: string; body: string } { const agentLabel = getAgentLabel(payload.agent); const action = - payload.eventType === "PermissionRequest" ? "Needs Input" : "Complete"; + payload.eventType === "PermissionRequest" ? "Needs Attention" : "Complete"; const workspaceLabel = cleanLabel(workspaceName) ?? "Workspace"; return { - title: - agentLabel === "Agent" - ? `Agent ${action}` - : `Agent ${action} - ${agentLabel}`, + title: `${agentLabel} - ${action}`, body: workspaceLabel, }; }