diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index b4128781f8..7e295ddc08 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -686,6 +686,7 @@ Navigation: Features: - Switch between **List**, **Board**, and **Org chart** layouts - Filter by role/state, include/exclude system agents, and inspect health/status +- Agent list cards show the configured **Model** or plugin **Runtime** for each agent, falling back to **Auto** when no override is set - First-run setup asks whether to create an optional project agent after project registration. The default template is **CEO**; users can choose another preset, use the AI interview when `experimentalFeatures.agentOnboarding` is enabled, or skip it. Fusion can still build tasks without an agent by starting temporary agents to plan, code, review, and merge task work. - Start, pause, stop, and trigger agent runs from the view and from detail panels - In **Agent detail**, use the kebab **Bulk agent actions** button in the header utility cluster (next to **Refresh** and **Close**) to run project-wide lifecycle transitions for non-ephemeral agents in the current project — **Pause All Agents** targets agents in the `active` or `running` state, while **Resume All Agents** targets agents in the `paused` state only diff --git a/packages/dashboard/app/components/AgentsView.css b/packages/dashboard/app/components/AgentsView.css index eb879bfa11..597969d478 100644 --- a/packages/dashboard/app/components/AgentsView.css +++ b/packages/dashboard/app/components/AgentsView.css @@ -787,11 +787,24 @@ FN-6774 removes the saturated left-edge status stripe from split-sidebar agent c } .agent-task, -.agent-heartbeat { +.agent-heartbeat, +.agent-model-runtime { display: flex; gap: var(--space-sm); } +.agent-model-runtime { + align-items: center; + min-width: 0; +} + +.agent-model-runtime__value { + min-width: 0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; +} + .agent-heartbeat-control { display: flex; align-items: center; @@ -1394,6 +1407,10 @@ AgentsView uses the measured SVG overlay as the single connector system so paren font-size: calc(var(--space-sm) + var(--space-xs) * 0.625); } + .agent-model-runtime { + flex-wrap: wrap; + } + .agent-card-error { width: 100%; padding: var(--space-xs) var(--space-sm); diff --git a/packages/dashboard/app/components/AgentsView.tsx b/packages/dashboard/app/components/AgentsView.tsx index 8dc4590123..b19d6dcb86 100644 --- a/packages/dashboard/app/components/AgentsView.tsx +++ b/packages/dashboard/app/components/AgentsView.tsx @@ -121,6 +121,37 @@ function getStateCardClass( } } +interface AgentModelLabel { + label: string | null; + isRuntime: boolean; +} + +/* +FNXC:AgentsView 2026-06-23-04:00: +Agent list cards must expose the configured model or plugin runtime without requiring a detail-view open. +Use the same runtimeHint/modelProvider+modelId/legacy model fallback order as the detail view and leave no-override agents as Auto at render time. +*/ +function getAgentModelLabel(agent: Agent): AgentModelLabel { + const runtimeConfig = agent.runtimeConfig ?? {}; + const runtimeHint = typeof runtimeConfig.runtimeHint === "string" ? runtimeConfig.runtimeHint : ""; + if (runtimeHint) { + return { label: runtimeHint, isRuntime: true }; + } + + const modelProvider = typeof runtimeConfig.modelProvider === "string" ? runtimeConfig.modelProvider : ""; + const modelId = typeof runtimeConfig.modelId === "string" ? runtimeConfig.modelId : ""; + if (modelProvider && modelId) { + return { label: `${modelProvider}/${modelId}`, isRuntime: false }; + } + + const legacyModel = typeof runtimeConfig.model === "string" ? runtimeConfig.model : ""; + if (legacyModel.includes("/")) { + const slashIdx = legacyModel.indexOf("/"); + return { label: legacyModel.slice(slashIdx + 1), isRuntime: false }; + } + + return { label: null, isRuntime: false }; +} function getOrgChartLeafCount(node: OrgTreeNode): number { if (node.children.length === 0) { @@ -1720,6 +1751,7 @@ export function AgentsView({ addToast, projectId, onOpenTaskLogs, agentOnboardin const configuredIntervalMs = resolveHeartbeatIntervalMs(agent.runtimeConfig?.heartbeatIntervalMs); const heartbeatOptions = getHeartbeatIntervalOptions(configuredIntervalMs); const isUpdatingHeartbeat = updatingHeartbeatAgentId === agent.id; + const modelLabel = getAgentModelLabel(agent); return (