fix(desktop): preserve activity window navigation context

Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-23 23:25:46 -07:00
parent 9d3c54a78f
commit dde8a9ea74
4 changed files with 99 additions and 3 deletions

View File

@@ -51,6 +51,7 @@ export default defineConfig({
"**/observer-feed-screenshots.spec.ts",
"**/core-memory-screenshots.spec.ts",
"**/activity-scope-label-screenshots.spec.ts",
"**/agent-activity-window.spec.ts",
"**/welcome-agent-modal-screenshots.spec.ts",
"**/local-archive-screenshots.spec.ts",
"**/voice-settings.spec.ts",

View File

@@ -7,6 +7,7 @@ import {
} from "@tanstack/react-router";
import { openSearchHitWithNavigation } from "@/app/navigation/searchHitNavigation";
import { currentCompanionWindowKind } from "@/app/companionWindow";
import type { SearchHit } from "@/shared/api/types";
type NavigationBehavior = {
@@ -20,6 +21,16 @@ export function useAppNavigation() {
const navigate = useNavigate();
const location = useLocation();
const canGoBack = useCanGoBack();
const companionSearch = location.search as Record<string, unknown>;
const companionCoordinates =
currentCompanionWindowKind() === "agent-activity" &&
typeof companionSearch.community === "string" &&
typeof companionSearch.agentSession === "string"
? {
community: companionSearch.community,
agentSession: companionSearch.agentSession,
}
: undefined;
const commitNavigation = React.useCallback(
async (
@@ -31,20 +42,26 @@ export function useAppNavigation() {
},
behavior: NavigationBehavior = {},
) => {
const nextLocation = router.buildLocation(next as never);
const destination = companionCoordinates
? {
...next,
search: { ...next.search, ...companionCoordinates },
}
: next;
const nextLocation = router.buildLocation(destination as never);
if (location.href === nextLocation.href && !behavior.force) {
return false;
}
await navigate({
...next,
...destination,
replace: behavior.replace,
resetScroll: behavior.resetScroll,
} as never);
return true;
},
[location.href, navigate, router],
[companionCoordinates, location.href, navigate, router],
);
const goHome = React.useCallback(

View File

@@ -13,6 +13,8 @@ import { ViewLoadingFallback } from "@/shared/ui/ViewLoadingFallback";
type ChannelRouteSearch = {
agentSession?: string;
/** Immutable origin community carried by dedicated activity windows. */
community?: string;
/**
* When set, the composer on mount will auto-submit its loaded draft once,
* then clear this param. Value is the draft key that was loaded so the
@@ -36,6 +38,7 @@ function validateChannelSearch(
): ChannelRouteSearch {
return {
agentSession: nonEmptyString(search.agentSession),
community: nonEmptyString(search.community),
autoSend: nonEmptyString(search.autoSend),
messageId: nonEmptyString(search.messageId),
profile: nonEmptyString(search.profile),

View File

@@ -0,0 +1,75 @@
import { expect, test } from "@playwright/test";
import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge";
const AGENT_PUBKEY = TEST_IDENTITIES.tyler.pubkey;
const AGENTS_CHANNEL_ID = "94a444a4-c0a3-5966-ab05-530c6ddc2301";
const GENERAL_CHANNEL_ID = "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50";
const COMMUNITY_ID = "e2e-default-community";
test("keeps dedicated activity windows connected when opening a channel reference", async ({
page,
}) => {
await installMockBridge(page, {
windowLabel: `agent-activity-${AGENT_PUBKEY}-${AGENTS_CHANNEL_ID}`,
managedAgents: [
{
pubkey: AGENT_PUBKEY,
name: "Observer Agent",
status: "running",
channelNames: ["agents"],
},
],
});
await page.goto(
`/#/channels/${AGENTS_CHANNEL_ID}?community=${COMMUNITY_ID}&agentSession=${AGENT_PUBKEY}`,
);
const panel = page.getByTestId("agent-session-thread-panel");
await expect(panel).toBeVisible({ timeout: 10_000 });
await page.waitForFunction(
() => typeof window.__BUZZ_E2E_SEED_OBSERVER_EVENTS__ === "function",
);
await page.evaluate(
({ agentPubkey, channelId }) => {
window.__BUZZ_E2E_SEED_OBSERVER_EVENTS__?.({
agentPubkey,
events: [
{
seq: 1,
timestamp: new Date().toISOString(),
kind: "acp_read",
agentIndex: 0,
channelId,
sessionId: "session-activity-window",
turnId: "turn-activity-window",
payload: {
method: "session/update",
params: {
update: {
sessionUpdate: "agent_message_chunk",
content: { type: "text", text: "Continue in #general" },
},
},
},
},
],
});
},
{ agentPubkey: AGENT_PUBKEY, channelId: AGENTS_CHANNEL_ID },
);
const channelReference = panel.getByRole("button", {
name: "Open channel general",
});
await expect(channelReference).toBeVisible();
await channelReference.click();
await expect(page).toHaveURL(
new RegExp(
`#/channels/${GENERAL_CHANNEL_ID}\\?.*community=${COMMUNITY_ID}`,
),
);
await expect(panel).toBeVisible();
await expect(page.locator("body")).not.toBeEmpty();
});