feat(FN-1059): add manual heartbeat run trigger with execution details in agent UI

- Wire heartbeatMonitor into ServerOptions and pass through to API routes
- Add POST /api/agents/:id/runs route to trigger heartbeat execution with optional source/triggerDetail
- Update API client startAgentRun to accept optional source and triggerDetail parameters
- Add Run Heartbeat button to AgentsView and enhance AgentDetailView RunsTab with execution details and polling
- Add route handler tests for agent runs and UI static analysis tests
- Support run history display with status, duration, trigger type, and result details
This commit is contained in:
gsxdsm
2026-04-07 14:38:12 -07:00
parent d8f4a63e1f
commit f34c86345c
8 changed files with 899 additions and 55 deletions

View File

@@ -1747,8 +1747,8 @@ export function cancelSubtaskBreakdown(sessionId: string, projectId?: string): P
// ── Agent API ────────────────────────────────────────────────────────────
import type { Agent, AgentDetail, AgentCapability, AgentState, AgentHeartbeatEvent, AgentHeartbeatRun, AgentCreateInput, AgentUpdateInput, AgentTaskSession, AgentStats } from "@fusion/core";
export type { Agent, AgentDetail, AgentCapability, AgentState, AgentHeartbeatEvent, AgentHeartbeatRun, AgentCreateInput, AgentUpdateInput, AgentTaskSession, AgentStats };
import type { Agent, AgentDetail, AgentCapability, AgentState, AgentHeartbeatEvent, AgentHeartbeatRun, AgentCreateInput, AgentUpdateInput, AgentTaskSession, AgentStats, HeartbeatInvocationSource } from "@fusion/core";
export type { Agent, AgentDetail, AgentCapability, AgentState, AgentHeartbeatEvent, AgentHeartbeatRun, AgentCreateInput, AgentUpdateInput, AgentTaskSession, AgentStats, HeartbeatInvocationSource };
function withProjectId(path: string, projectId?: string): string {
if (!projectId) return path;
@@ -1846,10 +1846,16 @@ export function fetchAgentRunLogs(agentId: string, runId: string, projectId?: st
}
/** Manually start a heartbeat run for an agent */
export function startAgentRun(agentId: string, projectId?: string): Promise<AgentHeartbeatRun> {
export function startAgentRun(
agentId: string,
projectId?: string,
options?: { source?: HeartbeatInvocationSource; triggerDetail?: string },
): Promise<AgentHeartbeatRun> {
const source = options?.source ?? "manual";
const triggerDetail = options?.triggerDetail ?? "Agent activated via dashboard";
return api<AgentHeartbeatRun>(withProjectId(`/agents/${encodeURIComponent(agentId)}/runs`, projectId), {
method: "POST",
body: JSON.stringify({ source: "manual", triggerDetail: "Agent activated via dashboard" }),
body: JSON.stringify({ source, triggerDetail }),
});
}