feat(FN-3275): unify AgentDetailView header with planning and harden resear

This merge adds idempotent cancel/retry and SSE streaming to the research lifecycle (FN-2999), tightens mobile spacing in the AgentDetailView header cluster (FN-3275), and wires the planning mode into the agent detail header actions (FN-3238). Research orchestrator, store, routes, and the SSE layer

Fusion-Task-Id: FN-3275
This commit is contained in:
Fusion
2026-05-03 09:16:29 -07:00
committed by gsxdsm
parent f2fa44e270
commit aecc2a7f4c
2 changed files with 43 additions and 1 deletions

View File

@@ -1309,7 +1309,12 @@
.planning-view-scroll { .planning-view-scroll {
padding: var(--space-lg); padding: var(--space-lg);
gap: 20px; gap: var(--space-lg);
}
.planning-question-scroll {
padding-top: var(--space-sm);
gap: var(--space-md);
} }
.planning-view-footer, .planning-view-footer,

View File

@@ -187,6 +187,32 @@ class MockEventSource {
} }
} }
function getMediaBlocks(css: string, mediaQuery: string): string[] {
const blocks: string[] = [];
let searchStart = 0;
while (searchStart < css.length) {
const start = css.indexOf(mediaQuery, searchStart);
if (start === -1) break;
const blockStart = css.indexOf("{", start);
if (blockStart === -1) break;
let depth = 1;
let cursor = blockStart + 1;
while (cursor < css.length && depth > 0) {
if (css[cursor] === "{") depth += 1;
else if (css[cursor] === "}") depth -= 1;
cursor += 1;
}
blocks.push(css.slice(start, cursor));
searchStart = cursor;
}
return blocks;
}
describe("PlanningModeModal", () => { describe("PlanningModeModal", () => {
const mockOnClose = vi.fn(); const mockOnClose = vi.fn();
const mockOnTaskCreated = vi.fn(); const mockOnTaskCreated = vi.fn();
@@ -633,6 +659,17 @@ describe("PlanningModeModal", () => {
expect(blockMatch![0]).toContain("padding-inline-start: 0;"); expect(blockMatch![0]).toContain("padding-inline-start: 0;");
expect(blockMatch![0]).toContain("justify-content: center;"); expect(blockMatch![0]).toContain("justify-content: center;");
}); });
it("keeps mobile question view top spacing compact", async () => {
const { loadAllAppCss } = await import("../../test/cssFixture");
const css = loadAllAppCss();
const mobileBlocks = getMediaBlocks(css, "@media (max-width: 768px)");
const mobileCss = mobileBlocks.join("\n");
expect(mobileCss).toContain(".planning-question-scroll");
expect(mobileCss).toContain("padding-top: var(--space-sm);");
expect(mobileCss).toContain("gap: var(--space-md);");
});
}); });
describe("Planning flow", () => { describe("Planning flow", () => {