diff --git a/packages/dashboard/app/components/AgentImportModal.tsx b/packages/dashboard/app/components/AgentImportModal.tsx index a4a12ae6a4..42cc1a0f32 100644 --- a/packages/dashboard/app/components/AgentImportModal.tsx +++ b/packages/dashboard/app/components/AgentImportModal.tsx @@ -137,6 +137,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi const [companyName, setCompanyName] = useState("Unknown"); const [agents, setAgents] = useState([]); const [skills, setSkills] = useState([]); + const [previewWarnings, setPreviewWarnings] = useState([]); const [selectedAgentNames, setSelectedAgentNames] = useState([]); const [selectedSkillNames, setSelectedSkillNames] = useState([]); const [isParsing, setIsParsing] = useState(false); @@ -215,6 +216,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi setCompanyName("Unknown"); setAgents([]); setSkills([]); + setPreviewWarnings([]); setSelectedAgentNames([]); setSelectedSkillNames([]); setIsParsing(false); @@ -345,6 +347,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi created: string[]; skipped: string[]; errors: Array<{ name: string; error: string }>; + warnings?: string[]; }; const previewAgents = (data.agents && data.agents.length > 0) @@ -355,6 +358,7 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi setCompanyName(data.companyName ?? "Unknown"); setAgents(previewAgents); setSkills(previewSkills); + setPreviewWarnings(Array.isArray(data.warnings) ? data.warnings : []); setSelectedAgentNames(previewAgents.map((agent) => agent.name)); setSelectedSkillNames(previewSkills.map((skill) => skill.name)); setStep("preview"); @@ -678,6 +682,17 @@ export function AgentImportModal({ isOpen, onClose, onImported, projectId, initi {companyName} + {previewWarnings.length > 0 && ( +
+ {previewWarnings.map((warning, idx) => ( +
+ + {warning} +
+ ))} +
+ )} +
{agents.length} agent{agents.length !== 1 ? "s" : ""} found diff --git a/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx b/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx index 9922f5c06a..5958c61896 100644 --- a/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx +++ b/packages/dashboard/app/components/__tests__/AgentImportModal.test.tsx @@ -110,6 +110,36 @@ describe("AgentImportModal", () => { }); }); + it("surfaces dry-run warnings in the preview step, before import (issue #1261)", async () => { + vi.mocked(globalThis.fetch).mockImplementationOnce(() => mockFetchResponse({ + ok: true, + status: 200, + body: { + dryRun: true, + companyName: "Acme Co", + agents: [{ name: "CEO", role: "custom" }], + created: ["CEO"], + skipped: [], + errors: [], + warnings: [ + "1 imported agent(s) have role \"custom\" and won't be auto-assigned mission or queue work.", + ], + }, + })); + + render(); + + fireEvent.change(screen.getByLabelText("Manifest content"), { + target: { value: "---\nname: CEO\n---\nLead" }, + }); + + fireEvent.click(screen.getByRole("button", { name: "Preview" })); + + await waitFor(() => { + expect(screen.getByText(/won't be auto-assigned mission or queue work/)).toBeTruthy(); + }); + }); + it("imports agents from preview step and shows result summary", async () => { vi.mocked(globalThis.fetch) .mockImplementationOnce(() => mockFetchResponse({ diff --git a/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts b/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts index 8d4317b762..ccec071bf4 100644 --- a/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts +++ b/packages/dashboard/src/routes/register-agent-import-export-generation-routes.ts @@ -4,6 +4,7 @@ import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { Readable } from "node:stream"; import { pipeline as streamPipeline } from "node:stream/promises"; +import { listEligibleExecutorAgents } from "@fusion/engine"; import { ApiError, badRequest, notFound, rateLimited } from "../api-error.js"; import { createSessionDiagnostics } from "../ai-session-diagnostics.js"; import { writeSSEEvent } from "../sse-buffer.js"; @@ -741,7 +742,6 @@ async function persistImportedSkills( const customRoleCount = importItems.filter((item) => item.input.role === "custom").length; const importsAnExecutor = importItems.some((item) => item.input.role === "executor"); if (customRoleCount > 0 && !importsAnExecutor) { - const { listEligibleExecutorAgents } = await import("@fusion/engine"); const existingExecutors = await listEligibleExecutorAgents(agentStore).catch(() => []); if (existingExecutors.length === 0) { importWarnings.push(