15 KiB
Agents
Fusion uses multiple agent roles for triage, execution, review, and merge workflows.
Agent Field Parity Matrix
Every first-class editable agent field has a defined create/edit/import/template behavior. This ensures consistent round-tripping across all surfaces.
Agent Model Fields
| Field | Create | Edit | Import | Notes |
|---|---|---|---|---|
name |
✓ | ✓ | ✓ (from manifest) | Unique identifier |
role |
✓ | ✓ | ✓ (mapped from manifest) | Agent capability |
metadata |
✓ | ✓ | ✓ | Arbitrary key-value data |
title |
✓ | ✓ | ✓ (from manifest) | Job title/description |
icon |
✓ | ✓ | ✓ (from manifest) | Emoji or icon identifier |
reportsTo |
✓ | ✓ | ✓ (from manifest) | Parent agent ID |
runtimeConfig |
✓ | ✓ | ✗ | Heartbeat/budget config |
permissions |
✓ | ✓ | ✗ | Capability flags |
instructionsPath |
✓ | ✓ | ✗ | File-backed instructions path |
instructionsText |
✓ | ✓ | ✓ (from manifest instructionBody) |
Inline instructions |
soul |
✓ | ✓ | ✗ | Personality/identity description |
memory |
✓ | ✓ | ✗ | Per-agent accumulated knowledge |
bundleConfig |
✓ | ✓ | ✗ | Structured instruction bundle |
Agent Companies Manifest Fields
| Manifest Field | First-Class Agent Field | Fallback |
|---|---|---|
name |
name |
— (required) |
title |
title |
— |
icon |
icon |
— |
role |
role (mapped to AgentCapability) |
custom |
reportsTo |
reportsTo |
— |
instructionBody |
instructionsText |
— |
skills |
metadata.skills |
— |
System-Managed Fields (Not User-Editable)
These fields are managed by the engine and cannot be directly edited:
id— Auto-generated unique identifierstate— Agent lifecycle state (managed by engine)taskId— Current working task (managed by scheduler)totalInputTokens/totalOutputTokens— Token usage totals (managed by engine)createdAt/updatedAt/lastHeartbeatAt— Timestamps (managed by system)lastError— Last error message (managed by engine)
Agents View (Dashboard)
The agents surface provides:
- Agent list and status
- Detail/config panels
- Runtime metrics
- Run history
- Task assignment context
Built-In Agent Prompt Templates
Fusion includes built-in templates for role prompts:
default-executordefault-triagedefault-reviewerdefault-mergersenior-engineerstrict-reviewerconcise-triage
These can be assigned per role using agentPrompts.roleAssignments.
Per-Agent Configuration
Agents can be configured with:
- Custom instructions
- Heartbeat interval/timeout limits
- Max concurrent heartbeat runs
Heartbeat values are validated and minimum-clamped.
Agent Instructions (Dashboard)
The Agent Detail view includes a dedicated Instructions tab for editing agent custom instructions. This replaces the previous embedded instructions editor in the Settings tab, providing a more discoverable and user-friendly experience.
Inline vs File-Backed Instructions
There are two ways to provide custom instructions:
-
Inline Instructions: Direct text entry in the dashboard textarea. Good for simple, short instructions.
-
File-Backed Instructions: A path to a
.mdfile in the project that contains the instructions. Good for:- Longer, more complex instructions
- Version control of instruction changes
- Sharing instruction files across teams
Using the Instructions Tab
- Open an agent from the Agents view
- Click the Instructions tab
- Enter inline instructions in the Inline Instructions textarea
- Or set a path in Instructions File Path (e.g.,
.fusion/agents/my-agent.md) - When a path is set, a File Content editor appears for direct file editing
- Save instructions using the Save Instructions button
- Save file content separately using the Save File button
File Editor Behavior
- File content loads automatically when an instructions path is set
- Missing files (ENOENT) are treated as new files with empty content
- Non-ENOENT errors (e.g., permission denied) show an error toast
- The editor has an Unsaved changes indicator when file content is modified
- File saves are independent from instruction metadata saves
New Agent Presets (Dashboard UI)
The New Agent dialog in the dashboard provides quick-start presets for common agent roles. Each preset includes:
- Name and icon - Display identification
- Professional title - Descriptive role title
- Soul - Personality and operating principles defining how the agent thinks and communicates
- Instructions - Actionable behavioral guidelines
Preset Library Location
Preset definitions live in packages/dashboard/app/components/agent-presets/:
agent-presets/
├── index.ts # Exports AGENT_PRESETS and helper functions
├── ceo/soul.md # Chief Executive Officer soul
├── cto/soul.md # Chief Technology Officer soul
├── cmo/soul.md # Chief Marketing Officer soul
├── cfo/soul.md # Chief Financial Officer soul
├── engineer/soul.md # Software Engineer soul
├── backend-engineer/soul.md
├── frontend-engineer/soul.md
├── fullstack-engineer/soul.md
├── qa-engineer/soul.md
├── devops-engineer/soul.md
├── ci-engineer/soul.md
├── security-engineer/soul.md
├── data-engineer/soul.md
├── ml-engineer/soul.md
├── product-manager/soul.md
├── designer/soul.md
├── marketing-manager/soul.md
├── technical-writer/soul.md
├── triage/soul.md
└── reviewer/soul.md
Soul File Format
Each soul.md file is a Markdown document containing:
# Soul: [Role Name]
[First-person identity statement]
## Operating Principles
[Bullet points describing key behaviors]
## Communication Style
[How the agent communicates]
Soul content should be:
- First-person - Written from the agent's perspective ("I am...")
- Role-specific - Defines the unique character of this role
- Actionable - Describes concrete behaviors, not abstract qualities
- Paperclip-inspired - Clear ownership, decision discipline, communication standards
Adding or Modifying Presets
- Create or edit the
soul.mdfile in the appropriate directory - Update
index.tsif adding a new preset (export the imported soul and add toAGENT_PRESETSarray) - Run tests to verify:
pnpm --filter @fusion/dashboard exec vitest run app/components/__tests__/agent-presets.test.ts
Preset vs Engine Templates
Dashboard presets are a UI-only concept that populates the New Agent dialog fields (name, icon, role, soul, instructionsText). They don't map to engine types.
Engine role prompts (in agentPrompts settings) define the actual agent behavior when executing tasks. These are separate from dashboard presets and live in project settings.
This separation means:
- Presets provide starting point personality and instructions for new agents
- Engine templates control actual task execution behavior
- An agent created from a preset can have its engine role prompt customized independently
Configurable Agent Prompts (agentPrompts)
agentPrompts project setting supports:
templates[]: custom prompt templates by roleroleAssignments: map role → template ID
When no assignment is configured, Fusion falls back to built-in defaults.
Fine-Grained Prompt Overrides (promptOverrides)
The Prompts section in the Settings modal provides a user-friendly interface for customizing specific segments of agent prompts. Unlike agentPrompts which replaces entire role templates, promptOverrides allows surgical customization of individual prompt sections.
Supported Override Keys
| Key | Agent Role | Description |
|---|---|---|
executor-welcome |
executor | Introductory section for the executor agent |
executor-guardrails |
executor | Behavioral guardrails and constraints |
executor-spawning |
executor | Instructions for spawning child agents |
executor-completion |
executor | Completion criteria and signaling |
triage-welcome |
triage | Introductory section for the triage/specification agent |
triage-context |
triage | Context-gathering instructions |
reviewer-verdict |
reviewer | Verdict criteria and format |
merger-conflicts |
merger | Merge conflict resolution instructions |
agent-generation-system |
— | System prompt for AI-assisted agent specification generation |
workflow-step-refine |
— | System prompt for refining workflow step descriptions |
How It Works
- Navigate to Settings → Prompts in the dashboard
- Each prompt shows its name, key, description, and current value
- Edit the textarea to create a custom override
- Click Reset to restore the built-in default
Clearing Overrides
To clear a specific override, click the Reset button in the UI. This sends null for that prompt key, deleting the override from settings and reverting to the built-in default.
Relationship with agentPrompts
agentPromptsreplaces entire role templatespromptOverridescustomizes individual segments within any template- Both can be used together —
promptOverridesapplies to the segment even within a custom role template
Inter-Agent Messaging
Messaging is available in dashboard mailbox UI and CLI.
fn message inbox
fn message outbox
fn message send AGENT-001 "Please prioritize FN-420"
fn message read MSG-123
fn message delete MSG-123
fn agent mailbox AGENT-001
Agent Spawning
Executor sessions can spawn child agents through spawn_agent.
Behavior:
- Child agents run in separate worktrees
- Parent/child relationship is tracked
- Limits enforced:
maxSpawnedAgentsPerParent(default 5)maxSpawnedAgentsGlobal(default 20)
- Child sessions terminate when parent task ends
Heartbeat Monitoring and Trigger Scheduling
Fusion's HeartbeatTriggerScheduler supports three trigger types:
timer— periodic wake based on heartbeat intervalassignment— wake when task is assigned to agenton_demand— manual run trigger (POST /api/agents/:id/runs)
All triggers respect per-agent maxConcurrentRuns and produce structured wake context metadata.
Control-Plane Lane (No Task Concurrency Gating)
Heartbeat runs from the Agents panel run on a separate control-plane lane that is independent of task execution concurrency limits. This ensures agent responsiveness is preserved even when task pipelines are saturated.
Key behaviors:
- Heartbeat runs (via
POST /api/agents/:id/runs) execute without gating onmaxConcurrentor in-progress task count - The
HeartbeatTriggerSchedulerandHeartbeatMonitorcomponents do not receive the task-lane semaphore - Trigger scheduling remains responsive regardless of how busy the task pipeline is
- Active-run 409 conflict semantics still apply — a new heartbeat run is rejected if the agent already has an active run
Architectural boundary:
| Component | Path | Concurrency |
|---|---|---|
| TriageProcessor | Task lane | Semaphore-gated |
| TaskExecutor | Task lane | Semaphore-gated |
| Scheduler | Task lane | Semaphore-gated |
| onMerge | Task lane | Semaphore-gated |
| HeartbeatMonitor | Utility/control plane | NOT semaphore-gated |
| HeartbeatTriggerScheduler | Utility/control plane | NOT semaphore-gated |
| CronRunner | Utility/control plane | NOT semaphore-gated |
Dashboard Health Status
The dashboard displays agent health status in AgentsView, AgentListModal, and AgentDetailView using a centralized health evaluation utility (packages/dashboard/app/utils/agentHealth.ts).
Health Labels (Priority Order)
| Label | Condition |
|---|---|
| Terminated | Agent state is "terminated" |
| Error | Agent state is "error" (uses lastError if available) |
| Paused | Agent state is "paused" (uses pauseReason if available) |
| Running | Agent state is "running" |
| Disabled | runtimeConfig.enabled === false |
| Starting... | State is "active" with no lastHeartbeatAt |
| Idle | Non-active state with no lastHeartbeatAt |
| Healthy | Heartbeat is fresh within configured timeout |
| Unresponsive | Heartbeat exceeded configured timeout |
Timeout Configuration
Health status uses a timeout-based evaluation:
- If
runtimeConfig.heartbeatTimeoutMsis set on the agent, use that value - Otherwise, use the default 60-second (60000ms) timeout
Key Behaviors
- Monitoring disabled: Agents with
runtimeConfig.enabled === falsedisplay "Disabled" — they are NOT falsely labeled as "Unresponsive" - Consistent across views: All dashboard surfaces use the same centralized utility, ensuring consistent health labels everywhere
- Auto-refresh: Health status is refreshed every 30 seconds while views are open to keep status current
- State-first evaluation: Terminal states (terminated, error, paused, running) take priority over timeout-based evaluation
Heartbeat Run Lifecycle
Agent runs have a defined lifecycle managed by AgentStore:
Run States
A heartbeat run can be in one of these states:
active— Run is currently executingcompleted— Run finished successfully (viaendHeartbeatRun(runId, "completed"))terminated— Run was stopped (viaendHeartbeatRun(runId, "terminated"))failed— Run encountered an error
Run Lifecycle API
startHeartbeatRun(agentId)— Creates a new run and persists it to structured storageendHeartbeatRun(runId, status)— Ends a run with terminal status, updates persisted stategetActiveHeartbeatRun(agentId)— Returns the current active run (or null)getCompletedHeartbeatRuns(agentId)— Returns all terminal runs (newest first)saveRun(run)— Persists run to structured storagegetRunDetail(agentId, runId)— Gets a specific run by ID
Active-Run Conflict Semantics
When an agent already has an active run, attempts to start a new run return 409 Conflict:
POST /api/agents/:id/runs → 409 { error: "Agent already has an active run", details: { runId } }
After a run is completed (or terminated), a new run can be started successfully:
POST /api/agents/:id/runs → 201 { id: "run-xxx", status: "active", ... }
Storage Architecture
Run records are stored in structured JSON files at .fusion/agents/{agentId}-runs/{runId}.json.
Heartbeat events are also appended to .fusion/agents/{agentId}-heartbeats.jsonl for legacy compatibility. The structured storage is the source of truth; heartbeat events provide a fallback for older run data.
Stopping Runs
Use POST /api/agents/:id/runs/stop to terminate an active run:
POST /api/agents/:id/runs/stop → 200 { ok: true, runId: "run-xxx" }
If there's no active run, returns { ok: true, message: "No active run" }.
