diff --git a/.changeset/preserve-background-agent-dismissals.md b/.changeset/preserve-background-agent-dismissals.md new file mode 100644 index 00000000000..01f1a7816e8 --- /dev/null +++ b/.changeset/preserve-background-agent-dismissals.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Keep dismissed background agents hidden when switching sessions or returning from History and empty chats. diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/BackgroundAgents.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/BackgroundAgents.tsx index d5612e066d2..e724587af6e 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/BackgroundAgents.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/BackgroundAgents.tsx @@ -36,14 +36,12 @@ export const BackgroundAgents: Component<{ readonly?: boolean }> = (props) => { const [open, setOpen] = createSignal(false) const [jobs, setJobs] = createSignal([]) const [loaded, setLoaded] = createSignal(false) - const [hidden, setHidden] = createSignal>(new Set()) const [mounted, setMounted] = createSignal(false) let pending: string | undefined let revision = 0 createEffect( on(session.currentSessionID, () => { - setHidden(new Set()) setLoaded(false) setJobs([]) pending = undefined @@ -95,7 +93,12 @@ export const BackgroundAgents: Component<{ readonly?: boolean }> = (props) => { return fallback() }) - const visible = createMemo(() => agents().filter((agent) => showBackgroundAgent(agent, hidden()))) + const visible = createMemo(() => { + const id = session.currentSessionID() + if (!id) return [] + const hidden = session.dismissedBackgroundJobs(id) + return agents().filter((agent) => showBackgroundAgent(agent, hidden)) + }) const summary = createMemo(() => { const running = visible().filter((agent) => agent.status === "running").length const total = visible().length @@ -174,13 +177,16 @@ export const BackgroundAgents: Component<{ readonly?: boolean }> = (props) => { vscode.postMessage({ type: "cancelBackgroundJob", jobID: agent.jobID, sessionID: id, requestID: pending }) } + const hide = (ids: string[]) => { + const id = session.currentSessionID() + if (id) session.dismissBackgroundJobs(id, ids) + } + const hideFinished = () => - setHidden( - new Set( - agents() - .filter((agent) => agent.status !== "running") - .map((agent) => agent.jobID), - ), + hide( + agents() + .filter((agent) => agent.status !== "running") + .map((agent) => agent.jobID), ) return ( @@ -334,7 +340,7 @@ export const BackgroundAgents: Component<{ readonly?: boolean }> = (props) => { aria-label={`${language.t("task.backgroundAgents.dismiss")}: ${label(agent)}`} onClick={(event: MouseEvent) => { event.stopPropagation() - setHidden((current) => new Set(current).add(agent.jobID)) + hide([agent.jobID]) }} > diff --git a/packages/kilo-vscode/webview-ui/src/context/session-types.ts b/packages/kilo-vscode/webview-ui/src/context/session-types.ts index 9bc29fb394b..3e4995480fb 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session-types.ts +++ b/packages/kilo-vscode/webview-ui/src/context/session-types.ts @@ -75,6 +75,8 @@ export interface SessionContextValue { // Tool parts for a specific session, maintained incrementally for streaming views getSessionToolParts: (sessionID: string) => ToolPart[] getSessionToolCount: (sessionID: string) => number + dismissedBackgroundJobs: (sessionID: string) => ReadonlySet + dismissBackgroundJobs: (sessionID: string, ids: string[]) => void // Hidden after model changes so switching models can clear stale provider errors // without removing messages and their checkpoint restore actions. diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx index ffb64fe6e5e..ff5cff12291 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx @@ -283,6 +283,12 @@ export const SessionProvider: ParentComponent = (props) => { // Cloud session preview state const [cloudPreviewId, setCloudPreviewId] = createSignal(null) const [hiddenErrors, setHiddenErrors] = createSignal>(new Set()) + const [dismissals, setDismissals] = createStore>>({}) + const dismissedBackgroundJobs = (id: string): ReadonlySet => dismissals[id] ?? new Set() + const dismissBackgroundJobs = (id: string, ids: string[]) => { + if (!ids.length) return + setDismissals(id, (current) => new Set([...(current ?? []), ...ids])) + } // Live worktree diff stats from extension polling const [worktreeStats, setWorktreeStats] = createSignal< @@ -978,6 +984,11 @@ export const SessionProvider: ParentComponent = (props) => { case "sessionDeleted": handleSessionDeleted(message.sessionID) + setDismissals( + produce((map) => { + delete map[message.sessionID] + }), + ) break case "messageRemoved": @@ -2879,6 +2890,8 @@ export const SessionProvider: ParentComponent = (props) => { getParts, getSessionToolParts, getSessionToolCount, + dismissedBackgroundJobs, + dismissBackgroundJobs, isErrorHidden: (messageID: string) => hiddenErrors().has(messageID), hydrateParts, todos, diff --git a/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx b/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx index 4723242ee45..aec116a739c 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx @@ -217,6 +217,8 @@ export function mockSessionValue(overrides?: { getParts: () => [], getSessionToolParts: () => [], getSessionToolCount: () => 0, + dismissedBackgroundJobs: () => new Set(), + dismissBackgroundJobs: noop, isErrorHidden: () => false, hydrateParts: noop, todos: () => [],