From 390cb4b19ccb1adc479c83a96071fd9e36290879 Mon Sep 17 00:00:00 2001 From: Vaibhav Zope Date: Fri, 4 Sep 2026 23:13:26 +0530 Subject: [PATCH] Forget what a Bot did on the computer that was just reset --- CHANGELOG.md | 8 ++ app/src/lib/computers/mutations.ts | 7 +- .../computer-reset-clears-activity.test.ts | 74 +++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 app/tests/computer-reset-clears-activity.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b4e5d4ab..a8f1dbbe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,14 @@ The tools run in the browser as the signed-in person, over the same `POST /api/p person uses, so who may take a slug is answered the same way and the `configuration.changed` audit row is written the same way. +### Resetting a computer clears the activity pane + +Resetting a Bot's computer deletes the browser profile it worked on, but the activity pane beside the +chat went on listing the commands and file operations that ran there. They sat alongside anything the +Bot did afterwards with nothing to tell the two apart, and only reloading the page cleared them. The +pane now forgets a Bot's history when that Bot's computer is reset. Stopping a browser is unchanged: +the profile survives a stop and is meant to resume, so what it did is still true of the machine. + ### A person can set standing instructions that every coworker follows Settings now has a box for standing instructions: one piece of text per person, saved once and diff --git a/app/src/lib/computers/mutations.ts b/app/src/lib/computers/mutations.ts index 8fa014bec..a4c1e8f43 100644 --- a/app/src/lib/computers/mutations.ts +++ b/app/src/lib/computers/mutations.ts @@ -1,5 +1,6 @@ import { mutationOptions, type QueryClient } from "@tanstack/react-query"; import { client } from "@/lib/client"; +import { clearActivity } from "./activity"; import { type ActionPolicy, computerKeys } from "./queries"; /** Stopping frees the container; resetting also deletes the browser profile. */ @@ -23,7 +24,11 @@ export function setComputerStateMutationOptions(queryClient: QueryClient) { }, ); }, - onSuccess: () => invalidateComputers(queryClient), + /** A reset deletes the profile those commands ran on; a stop keeps it, so only reset forgets. */ + onSuccess: (_result, variables) => { + if (variables.action === "reset") clearActivity(variables.botId); + return invalidateComputers(queryClient); + }, }); } diff --git a/app/tests/computer-reset-clears-activity.test.ts b/app/tests/computer-reset-clears-activity.test.ts new file mode 100644 index 000000000..53e2be294 --- /dev/null +++ b/app/tests/computer-reset-clears-activity.test.ts @@ -0,0 +1,74 @@ +import { afterEach, beforeEach, expect, test } from "bun:test"; +import type { QueryClient } from "@tanstack/react-query"; +import { + activityFor, + clearActivity, + recordActivity, +} from "../src/lib/computers/activity"; +import { setComputerStateMutationOptions } from "../src/lib/computers/mutations"; + +const realFetch = globalThis.fetch; + +const queryClient = { + invalidateQueries: async () => undefined, +} as unknown as QueryClient; + +beforeEach(() => { + globalThis.fetch = (async () => + new Response(null, { status: 200 })) as unknown as typeof fetch; + clearActivity("general-assistant"); + recordActivity("general-assistant", { + kind: "command", + subject: "echo repro-marker-bravo", + output: "repro-marker-bravo\n", + exitCode: 0, + }); +}); + +afterEach(() => { + globalThis.fetch = realFetch; + clearActivity("general-assistant"); +}); + +async function run(action: "stop" | "reset") { + const options = setComputerStateMutationOptions(queryClient); + const variables = { action, botId: "general-assistant" } as const; + await options.mutationFn?.(variables); + await options.onSuccess?.( + undefined as never, + variables, + undefined as never, + undefined as never, + ); +} + +test("resetting a computer forgets what the Bot did on the profile it deleted", async () => { + expect(activityFor("general-assistant")).toHaveLength(1); + + await run("reset"); + + expect(activityFor("general-assistant")).toEqual([]); +}); + +test("stopping a browser keeps the history, because the profile survives it", async () => { + await run("stop"); + + expect(activityFor("general-assistant")).toHaveLength(1); + expect(activityFor("general-assistant")[0]?.subject).toBe( + "echo repro-marker-bravo", + ); +}); + +test("one Bot's reset leaves another Bot's history alone", async () => { + recordActivity("knowledge", { + kind: "list_files", + subject: "the workspace", + output: "notes.md\n", + }); + + await run("reset"); + + expect(activityFor("general-assistant")).toEqual([]); + expect(activityFor("knowledge")).toHaveLength(1); + clearActivity("knowledge"); +});