feat(FN-3586): fix agent heartbeat auto-claim, inbox read behavior, and age
The merge integrates five commits across the engine and dashboard: it fixes a quick chat icon-tag regression, ensures manual heartbeat runs respond immediately once a run record exists, splits the agents overview label into separate active vs running counts, prevents inbox messages from being auto-m Fusion-Task-Id: FN-3586
This commit is contained in:
@@ -247,7 +247,10 @@
|
|||||||
/* Icon-only fallback when the model name is too long for the header. */
|
/* Icon-only fallback when the model name is too long for the header. */
|
||||||
.quick-chat-model-tag--icon {
|
.quick-chat-model-tag--icon {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
padding: var(--space-xs);
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { Agent } from "../../api";
|
|||||||
import type { ChatSession } from "@fusion/core";
|
import type { ChatSession } from "@fusion/core";
|
||||||
import * as apiModule from "../../api";
|
import * as apiModule from "../../api";
|
||||||
import { useAgents } from "../../hooks/useAgents";
|
import { useAgents } from "../../hooks/useAgents";
|
||||||
|
import { useViewportMode } from "../../hooks/useViewportMode";
|
||||||
import { QuickChatFAB } from "../QuickChatFAB";
|
import { QuickChatFAB } from "../QuickChatFAB";
|
||||||
|
|
||||||
vi.mock("../../api", () => ({
|
vi.mock("../../api", () => ({
|
||||||
@@ -19,6 +20,7 @@ vi.mock("../../api", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../../hooks/useAgents", () => ({ useAgents: vi.fn() }));
|
vi.mock("../../hooks/useAgents", () => ({ useAgents: vi.fn() }));
|
||||||
|
vi.mock("../../hooks/useViewportMode", () => ({ useViewportMode: vi.fn() }));
|
||||||
|
|
||||||
const mockFetchResumeChatSession = vi.mocked(apiModule.fetchResumeChatSession);
|
const mockFetchResumeChatSession = vi.mocked(apiModule.fetchResumeChatSession);
|
||||||
const mockFetchChatSessions = vi.mocked(apiModule.fetchChatSessions);
|
const mockFetchChatSessions = vi.mocked(apiModule.fetchChatSessions);
|
||||||
@@ -29,6 +31,7 @@ const mockFetchDiscoveredSkills = vi.mocked(apiModule.fetchDiscoveredSkills);
|
|||||||
const mockStreamChatResponse = vi.mocked(apiModule.streamChatResponse);
|
const mockStreamChatResponse = vi.mocked(apiModule.streamChatResponse);
|
||||||
const mockCancelChatResponse = vi.mocked(apiModule.cancelChatResponse);
|
const mockCancelChatResponse = vi.mocked(apiModule.cancelChatResponse);
|
||||||
const mockUseAgents = vi.mocked(useAgents);
|
const mockUseAgents = vi.mocked(useAgents);
|
||||||
|
const mockUseViewportMode = vi.mocked(useViewportMode);
|
||||||
|
|
||||||
const agents: Agent[] = [
|
const agents: Agent[] = [
|
||||||
{ id: "agent-001", name: "Agent One", role: "executor", state: "active", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), metadata: {} },
|
{ id: "agent-001", name: "Agent One", role: "executor", state: "active", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), metadata: {} },
|
||||||
@@ -76,6 +79,7 @@ describe("QuickChatFAB session-first UX", () => {
|
|||||||
window.dispatchEvent(new Event("resize"));
|
window.dispatchEvent(new Event("resize"));
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
mockUseAgents.mockReturnValue({ agents, activeAgents: agents, stats: null, isLoading: false, loadAgents: vi.fn(), loadStats: vi.fn() });
|
mockUseAgents.mockReturnValue({ agents, activeAgents: agents, stats: null, isLoading: false, loadAgents: vi.fn(), loadStats: vi.fn() });
|
||||||
|
mockUseViewportMode.mockReturnValue("desktop");
|
||||||
mockFetchResumeChatSession.mockResolvedValue({ session: modelSession });
|
mockFetchResumeChatSession.mockResolvedValue({ session: modelSession });
|
||||||
mockFetchChatMessages.mockResolvedValue({ messages: [] });
|
mockFetchChatMessages.mockResolvedValue({ messages: [] });
|
||||||
mockFetchChatSessions.mockResolvedValue({ sessions: [modelSession, agentSession] });
|
mockFetchChatSessions.mockResolvedValue({ sessions: [modelSession, agentSession] });
|
||||||
@@ -183,6 +187,32 @@ describe("QuickChatFAB session-first UX", () => {
|
|||||||
expect(await screen.findByRole("option", { name: "Model thread — GPT-4o [openai/gpt-4o]" })).toBeInTheDocument();
|
expect(await screen.findByRole("option", { name: "Model thread — GPT-4o [openai/gpt-4o]" })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses icon-only model tag without pill styling when mobile header fallback is active", async () => {
|
||||||
|
Object.defineProperty(window, "innerWidth", { configurable: true, value: 390 });
|
||||||
|
window.dispatchEvent(new Event("resize"));
|
||||||
|
mockUseViewportMode.mockReturnValue("mobile");
|
||||||
|
|
||||||
|
mockFetchModels.mockResolvedValueOnce({
|
||||||
|
models: [{ provider: "openai", id: "gpt-4o", name: "Extremely Long Model Name", reasoning: true, contextWindow: 128000 }],
|
||||||
|
favoriteProviders: [],
|
||||||
|
favoriteModels: [],
|
||||||
|
defaultProvider: "openai",
|
||||||
|
defaultModelId: "gpt-4o",
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||||
|
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||||
|
|
||||||
|
const modelTag = await screen.findByTestId("quick-chat-model-tag");
|
||||||
|
expect(modelTag).toHaveClass("quick-chat-model-tag--icon");
|
||||||
|
|
||||||
|
const styles = window.getComputedStyle(modelTag);
|
||||||
|
expect(styles.backgroundColor).toBe("rgba(0, 0, 0, 0)");
|
||||||
|
expect(styles.borderTopStyle).toBe("none");
|
||||||
|
expect(styles.paddingLeft).toBe("0px");
|
||||||
|
expect(styles.paddingRight).toBe("0px");
|
||||||
|
});
|
||||||
|
|
||||||
it("intercepts exact /clear and starts a fresh session for the active target", async () => {
|
it("intercepts exact /clear and starts a fresh session for the active target", async () => {
|
||||||
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||||
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||||
|
|||||||
Reference in New Issue
Block a user