diff --git a/packages/core/agents/stores/view-store.test.ts b/packages/core/agents/stores/view-store.test.ts index 3f9ce176b..7cb5397da 100644 --- a/packages/core/agents/stores/view-store.test.ts +++ b/packages/core/agents/stores/view-store.test.ts @@ -100,4 +100,25 @@ describe("useAgentsViewStore", () => { expect(useAgentsViewStore.getState().scope).toBe("mine"); expect(localStorage.getItem("multica_agents_view:acme")).not.toBeNull(); }); + + it("backfills new filter dimensions when rehydrating a pre-owners payload", async () => { + // A payload persisted before the `owners` filter existed must not drop + // the key to undefined (the agents list filter predicate reads + // `filters.owners.length` and would crash). + localStorage.setItem( + "multica_agents_view:acme", + JSON.stringify({ + state: { filters: { availability: ["online"], runtimes: [] } }, + version: 0, + }), + ); + + setCurrentWorkspace("acme", "ws_a"); + await flush(); + await flush(); + + const filters = useAgentsViewStore.getState().filters; + expect(filters.owners).toEqual([]); + expect(filters.availability).toEqual(["online"]); + }); }); diff --git a/packages/core/agents/stores/view-store.ts b/packages/core/agents/stores/view-store.ts index e61f8d02a..1cf0b0e5a 100644 --- a/packages/core/agents/stores/view-store.ts +++ b/packages/core/agents/stores/view-store.ts @@ -171,7 +171,15 @@ export const useAgentsViewStore = create()( // persisted is undefined, which would leak state across workspaces. merge: (persisted, current) => { if (!persisted) return { ...current, ...DEFAULTS }; - return { ...current, ...(persisted as Partial) }; + const p = persisted as Partial; + // Deep-merge filters so a payload persisted before a new filter + // dimension existed (e.g. `owners`) still gets that key's default + // instead of dropping it to `undefined` and crashing `.length`. + return { + ...current, + ...p, + filters: { ...EMPTY_AGENT_FILTERS, ...(p.filters ?? {}) }, + }; }, }, ),