fix(app): restore archive and composer CI checks

This commit is contained in:
Mohamed Boudra
2026-04-22 19:16:28 +07:00
parent 89efb007f9
commit d80c524e0e
6 changed files with 149 additions and 25 deletions
+20 -15
View File
@@ -22,6 +22,7 @@ import { buildHostAgentDetailRoute } from "@/utils/host-routes";
import { resolveWorkspaceIdByExecutionDirectory } from "@/utils/workspace-execution"; import { resolveWorkspaceIdByExecutionDirectory } from "@/utils/workspace-execution";
import { prepareWorkspaceTab } from "@/utils/workspace-navigation"; import { prepareWorkspaceTab } from "@/utils/workspace-navigation";
import type { Agent } from "@/stores/session-store"; import type { Agent } from "@/stores/session-store";
import { useArchiveAgent } from "@/hooks/use-archive-agent";
interface AgentListProps { interface AgentListProps {
agents: AggregatedAgent[]; agents: AggregatedAgent[];
@@ -280,6 +281,7 @@ export function AgentList({
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const [actionAgent, setActionAgent] = useState<AggregatedAgent | null>(null); const [actionAgent, setActionAgent] = useState<AggregatedAgent | null>(null);
const isMobile = useIsCompactFormFactor(); const isMobile = useIsCompactFormFactor();
const { archiveAgent } = useArchiveAgent();
const actionClient = useSessionStore((state) => const actionClient = useSessionStore((state) =>
actionAgent?.serverId ? (state.sessions[actionAgent.serverId]?.client ?? null) : null, actionAgent?.serverId ? (state.sessions[actionAgent.serverId]?.client ?? null) : null,
@@ -320,20 +322,23 @@ export function AgentList({
[isActionSheetVisible, onAgentSelect], [isActionSheetVisible, onAgentSelect],
); );
const handleAgentLongPress = useCallback((agent: AggregatedAgent) => { const handleAgentLongPress = useCallback(
const isRunning = agent.status === "running" || agent.status === "initializing"; (agent: AggregatedAgent) => {
if (isRunning) { const isRunning = agent.status === "running" || agent.status === "initializing";
setActionAgent(agent); if (isRunning) {
return; setActionAgent(agent);
} return;
}
const client = useSessionStore.getState().sessions[agent.serverId]?.client ?? null; const client = useSessionStore.getState().sessions[agent.serverId]?.client ?? null;
if (!client) { if (!client) {
setActionAgent(agent); setActionAgent(agent);
return; return;
} }
void client.archiveAgent(agent.id); void archiveAgent({ serverId: agent.serverId, agentId: agent.id }).catch(() => {});
}, []); },
[archiveAgent],
);
const handleCloseActionSheet = useCallback(() => { const handleCloseActionSheet = useCallback(() => {
setActionAgent(null); setActionAgent(null);
@@ -344,9 +349,9 @@ export function AgentList({
return; return;
} }
// Timeout errors are swallowed — the daemon will still process the archive // Timeout errors are swallowed — the daemon will still process the archive
void actionClient.archiveAgent(actionAgent.id).catch(() => {}); void archiveAgent({ serverId: actionAgent.serverId, agentId: actionAgent.id }).catch(() => {});
setActionAgent(null); setActionAgent(null);
}, [actionAgent, actionClient]); }, [actionAgent, actionClient, archiveAgent]);
const flatItems = useMemo((): FlatListItem[] => { const flatItems = useMemo((): FlatListItem[] => {
const order = ["Today", "Yesterday", "This week", "This month", "Older"] as const; const order = ["Today", "Yesterday", "This week", "This month", "Older"] as const;
@@ -344,7 +344,7 @@ vi.mock("@/contexts/toast-context", () => ({
useToast: () => ({ error: vi.fn() }), useToast: () => ({ error: vi.fn() }),
})); }));
vi.mock("@/utils/scroll-jank-investigation", () => ({ vi.mock("@/utils/scroll-jank", () => ({
markScrollInvestigationRender: markScrollInvestigationRenderMock, markScrollInvestigationRender: markScrollInvestigationRenderMock,
markScrollInvestigationEvent: vi.fn(), markScrollInvestigationEvent: vi.fn(),
})); }));
@@ -0,0 +1,3 @@
export function agentHistoryQueryKey(serverId: string | null) {
return ["agentHistory", serverId] as const;
}
+1 -4
View File
@@ -8,6 +8,7 @@ import { useCallback, useMemo } from "react";
import type { AggregatedAgent } from "@/hooks/use-aggregated-agents"; import type { AggregatedAgent } from "@/hooks/use-aggregated-agents";
import { useHostRuntimeClient, useHostRuntimeIsConnected, useHosts } from "@/runtime/host-runtime"; import { useHostRuntimeClient, useHostRuntimeIsConnected, useHosts } from "@/runtime/host-runtime";
import { buildAgentDirectoryState } from "@/utils/agent-directory-sync"; import { buildAgentDirectoryState } from "@/utils/agent-directory-sync";
import { agentHistoryQueryKey } from "./agent-history-query-key";
const AGENT_HISTORY_PAGE_LIMIT = 200; const AGENT_HISTORY_PAGE_LIMIT = 200;
const AGENT_HISTORY_SORT: NonNullable<FetchAgentHistoryOptions["sort"]> = [ const AGENT_HISTORY_SORT: NonNullable<FetchAgentHistoryOptions["sort"]> = [
@@ -30,10 +31,6 @@ type AgentHistoryPage = {
pageInfo: FetchAgentHistoryPageInfo; pageInfo: FetchAgentHistoryPageInfo;
}; };
export function agentHistoryQueryKey(serverId: string | null) {
return ["agentHistory", serverId] as const;
}
async function fetchAgentHistoryPage(input: { async function fetchAgentHistoryPage(input: {
client: DaemonClient; client: DaemonClient;
serverId: string; serverId: string;
@@ -3,6 +3,7 @@ import { QueryClient } from "@tanstack/react-query";
import { beforeEach, describe, expect, it } from "vitest"; import { beforeEach, describe, expect, it } from "vitest";
import type { Agent } from "@/stores/session-store"; import type { Agent } from "@/stores/session-store";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { agentHistoryQueryKey } from "./agent-history-query-key";
import { __private__, applyArchivedAgentCloseResults } from "./use-archive-agent"; import { __private__, applyArchivedAgentCloseResults } from "./use-archive-agent";
function makeAgent(overrides: Partial<Agent> = {}): Agent { function makeAgent(overrides: Partial<Agent> = {}): Agent {
@@ -112,6 +113,17 @@ describe("useArchiveAgent", () => {
queryClient.setQueryData(["allAgents", "server-a"], { queryClient.setQueryData(["allAgents", "server-a"], {
entries: [{ agent: { id: "agent-1" } }, { agent: { id: "agent-2" } }], entries: [{ agent: { id: "agent-1" } }, { agent: { id: "agent-2" } }],
}); });
queryClient.setQueryData(agentHistoryQueryKey("server-a"), {
pages: [
{
agents: [
{ id: "agent-1", archivedAt: null },
{ id: "agent-2", archivedAt: null },
],
},
],
pageParams: [null],
});
applyArchivedAgentCloseResults({ applyArchivedAgentCloseResults({
queryClient, queryClient,
@@ -131,6 +143,17 @@ describe("useArchiveAgent", () => {
expect(queryClient.getQueryData(["allAgents", "server-a"])).toEqual({ expect(queryClient.getQueryData(["allAgents", "server-a"])).toEqual({
entries: [{ agent: { id: "agent-2" } }], entries: [{ agent: { id: "agent-2" } }],
}); });
expect(queryClient.getQueryData(agentHistoryQueryKey("server-a"))).toEqual({
pages: [
{
agents: [
{ id: "agent-1", archivedAt: new Date("2026-04-01T04:00:00.000Z") },
{ id: "agent-2", archivedAt: null },
],
},
],
pageParams: [null],
});
}); });
it("can apply archived agent close results without invalidating cached lists", () => { it("can apply archived agent close results without invalidating cached lists", () => {
@@ -143,6 +166,14 @@ describe("useArchiveAgent", () => {
queryClient.setQueryData(["allAgents", "server-a"], { queryClient.setQueryData(["allAgents", "server-a"], {
entries: [{ agent: { id: "agent-1" } }, { agent: { id: "agent-2" } }], entries: [{ agent: { id: "agent-1" } }, { agent: { id: "agent-2" } }],
}); });
queryClient.setQueryData(agentHistoryQueryKey("server-a"), {
pages: [
{
agents: [{ id: "agent-1", archivedAt: null }],
},
],
pageParams: [null],
});
applyArchivedAgentCloseResults({ applyArchivedAgentCloseResults({
queryClient, queryClient,
@@ -153,5 +184,14 @@ describe("useArchiveAgent", () => {
expect(queryClient.getQueryState(["sidebarAgentsList", "server-a"])?.isInvalidated).toBe(false); expect(queryClient.getQueryState(["sidebarAgentsList", "server-a"])?.isInvalidated).toBe(false);
expect(queryClient.getQueryState(["allAgents", "server-a"])?.isInvalidated).toBe(false); expect(queryClient.getQueryState(["allAgents", "server-a"])?.isInvalidated).toBe(false);
expect(queryClient.getQueryState(agentHistoryQueryKey("server-a"))?.isInvalidated).toBe(false);
expect(queryClient.getQueryData(agentHistoryQueryKey("server-a"))).toEqual({
pages: [
{
agents: [{ id: "agent-1", archivedAt: new Date("2026-04-01T04:00:00.000Z") }],
},
],
pageParams: [null],
});
}); });
}); });
+84 -5
View File
@@ -1,6 +1,7 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { useMutation, useQuery, useQueryClient, type QueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient, type QueryClient } from "@tanstack/react-query";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { agentHistoryQueryKey } from "./agent-history-query-key";
export const ARCHIVE_AGENT_PENDING_QUERY_KEY = ["archive-agent-pending"] as const; export const ARCHIVE_AGENT_PENDING_QUERY_KEY = ["archive-agent-pending"] as const;
@@ -32,6 +33,7 @@ interface AgentsListQueryData {
interface ArchivedAgentListCacheSnapshot { interface ArchivedAgentListCacheSnapshot {
sidebarAgentsList: AgentsListQueryData | undefined; sidebarAgentsList: AgentsListQueryData | undefined;
allAgents: AgentsListQueryData | undefined; allAgents: AgentsListQueryData | undefined;
agentHistory: AgentHistoryQueryData | undefined;
} }
interface ArchiveAgentMutationContext { interface ArchiveAgentMutationContext {
@@ -39,6 +41,19 @@ interface ArchiveAgentMutationContext {
lists: ArchivedAgentListCacheSnapshot; lists: ArchivedAgentListCacheSnapshot;
} }
interface AgentHistoryQueryAgent {
id?: string | null;
archivedAt?: Date | null;
}
interface AgentHistoryQueryPage {
agents?: AgentHistoryQueryAgent[];
}
interface AgentHistoryQueryData {
pages?: AgentHistoryQueryPage[];
}
function toArchiveKey(input: ArchiveAgentInput): string { function toArchiveKey(input: ArchiveAgentInput): string {
const serverId = input.serverId.trim(); const serverId = input.serverId.trim();
const agentId = input.agentId.trim(); const agentId = input.agentId.trim();
@@ -121,6 +136,54 @@ function removeAgentFromCachedLists(queryClient: QueryClient, input: ArchiveAgen
); );
} }
function markAgentArchivedInHistoryPayload<T extends AgentHistoryQueryData | undefined>(
payload: T,
input: ArchiveAgentInput & { archivedAt: string },
): T {
if (!payload || !Array.isArray(payload.pages) || !input.agentId) {
return payload;
}
const archivedAt = new Date(input.archivedAt);
if (Number.isNaN(archivedAt.getTime())) {
return payload;
}
let changed = false;
const pages = payload.pages.map((page) => {
if (!Array.isArray(page.agents)) {
return page;
}
let pageChanged = false;
const agents = page.agents.map((agent) => {
if (agent.id !== input.agentId) {
return agent;
}
pageChanged = true;
changed = true;
return {
...agent,
archivedAt,
};
});
return pageChanged ? { ...page, agents } : page;
});
return changed ? ({ ...payload, pages } as T) : payload;
}
function markAgentArchivedInHistoryCache(
queryClient: QueryClient,
input: ArchiveAgentInput & { archivedAt: string },
): void {
queryClient.setQueryData<AgentHistoryQueryData | undefined>(
agentHistoryQueryKey(input.serverId),
(current) => markAgentArchivedInHistoryPayload(current, input),
);
}
function getStoredAgentSnapshot(input: ArchiveAgentInput) { function getStoredAgentSnapshot(input: ArchiveAgentInput) {
return useSessionStore.getState().sessions[input.serverId]?.agents.get(input.agentId); return useSessionStore.getState().sessions[input.serverId]?.agents.get(input.agentId);
} }
@@ -161,13 +224,16 @@ function getArchivedAgentListCacheSnapshot(
serverId, serverId,
]), ]),
allAgents: queryClient.getQueryData<AgentsListQueryData | undefined>(["allAgents", serverId]), allAgents: queryClient.getQueryData<AgentsListQueryData | undefined>(["allAgents", serverId]),
agentHistory: queryClient.getQueryData<AgentHistoryQueryData | undefined>(
agentHistoryQueryKey(serverId),
),
}; };
} }
function restoreCachedListSnapshot( function restoreCachedQuerySnapshot<T>(
queryClient: QueryClient, queryClient: QueryClient,
queryKey: readonly [string, string], queryKey: readonly unknown[],
snapshot: AgentsListQueryData | undefined, snapshot: T | undefined,
): void { ): void {
if (snapshot === undefined) { if (snapshot === undefined) {
queryClient.removeQueries({ queryKey, exact: true }); queryClient.removeQueries({ queryKey, exact: true });
@@ -181,12 +247,13 @@ function restoreArchivedAgentListCacheSnapshot(
serverId: string, serverId: string,
snapshot: ArchivedAgentListCacheSnapshot, snapshot: ArchivedAgentListCacheSnapshot,
): void { ): void {
restoreCachedListSnapshot( restoreCachedQuerySnapshot(
queryClient, queryClient,
["sidebarAgentsList", serverId], ["sidebarAgentsList", serverId],
snapshot.sidebarAgentsList, snapshot.sidebarAgentsList,
); );
restoreCachedListSnapshot(queryClient, ["allAgents", serverId], snapshot.allAgents); restoreCachedQuerySnapshot(queryClient, ["allAgents", serverId], snapshot.allAgents);
restoreCachedQuerySnapshot(queryClient, agentHistoryQueryKey(serverId), snapshot.agentHistory);
} }
function markAgentArchivedInStore(input: ArchiveAgentInput & { archivedAt: string }): void { function markAgentArchivedInStore(input: ArchiveAgentInput & { archivedAt: string }): void {
@@ -235,6 +302,11 @@ export function applyArchivedAgentCloseResults(input: ApplyArchivedAgentCloseRes
serverId: input.serverId, serverId: input.serverId,
agentId: result.agentId, agentId: result.agentId,
}); });
markAgentArchivedInHistoryCache(input.queryClient, {
serverId: input.serverId,
agentId: result.agentId,
archivedAt: result.archivedAt,
});
} }
if (input.invalidateQueries ?? true) { if (input.invalidateQueries ?? true) {
@@ -244,6 +316,9 @@ export function applyArchivedAgentCloseResults(input: ApplyArchivedAgentCloseRes
void input.queryClient.invalidateQueries({ void input.queryClient.invalidateQueries({
queryKey: ["allAgents", input.serverId], queryKey: ["allAgents", input.serverId],
}); });
void input.queryClient.invalidateQueries({
queryKey: agentHistoryQueryKey(input.serverId),
});
} }
} }
@@ -324,6 +399,9 @@ export function useArchiveAgent() {
void queryClient.invalidateQueries({ void queryClient.invalidateQueries({
queryKey: ["allAgents", input.serverId], queryKey: ["allAgents", input.serverId],
}); });
void queryClient.invalidateQueries({
queryKey: agentHistoryQueryKey(input.serverId),
});
}, },
}); });
@@ -359,4 +437,5 @@ export const __private__ = {
setAgentArchiving, setAgentArchiving,
isAgentArchiving, isAgentArchiving,
removeAgentFromListPayload, removeAgentFromListPayload,
markAgentArchivedInHistoryPayload,
}; };