Files
fusion/docs/agents.md
2026-04-12 07:00:49 -07:00

394 lines
15 KiB
Markdown

# Agents
[← Docs index](./README.md)
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 identifier
- `state` — 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
![Agents view](./screenshots/agents-view.png)
## Built-In Agent Prompt Templates
Fusion includes built-in templates for role prompts:
- `default-executor`
- `default-triage`
- `default-reviewer`
- `default-merger`
- `senior-engineer`
- `strict-reviewer`
- `concise-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:
1. **Inline Instructions**: Direct text entry in the dashboard textarea. Good for simple, short instructions.
2. **File-Backed Instructions**: A path to a `.md` file 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
1. Open an agent from the Agents view
2. Click the **Instructions** tab
3. Enter inline instructions in the **Inline Instructions** textarea
4. Or set a path in **Instructions File Path** (e.g., `.fusion/agents/my-agent.md`)
5. When a path is set, a **File Content** editor appears for direct file editing
6. Save instructions using the **Save Instructions** button
7. 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:
```markdown
# 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
1. Create or edit the `soul.md` file in the appropriate directory
2. Update `index.ts` if adding a new preset (export the imported soul and add to `AGENT_PRESETS` array)
3. 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 role
- `roleAssignments`: 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
1. Navigate to **Settings → Prompts** in the dashboard
2. Each prompt shows its name, key, description, and current value
3. Edit the textarea to create a custom override
4. 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`
- `agentPrompts` replaces entire role templates
- `promptOverrides` customizes individual segments within any template
- Both can be used together — `promptOverrides` applies to the segment even within a custom role template
## Inter-Agent Messaging
Messaging is available in dashboard mailbox UI and CLI.
```bash
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 interval
- `assignment` — wake when task is assigned to agent
- `on_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 on `maxConcurrent` or in-progress task count
- The `HeartbeatTriggerScheduler` and `HeartbeatMonitor` components 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:
1. If `runtimeConfig.heartbeatTimeoutMs` is set on the agent, use that value
2. Otherwise, use the default 60-second (60000ms) timeout
### Key Behaviors
- **Monitoring disabled**: Agents with `runtimeConfig.enabled === false` display "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 executing
- `completed` — Run finished successfully (via `endHeartbeatRun(runId, "completed")`)
- `terminated` — Run was stopped (via `endHeartbeatRun(runId, "terminated")`)
- `failed` — Run encountered an error
### Run Lifecycle API
- `startHeartbeatRun(agentId)` — Creates a new run and persists it to structured storage
- `endHeartbeatRun(runId, status)` — Ends a run with terminal status, updates persisted state
- `getActiveHeartbeatRun(agentId)` — Returns the current active run (or null)
- `getCompletedHeartbeatRuns(agentId)` — Returns all terminal runs (newest first)
- `saveRun(run)` — Persists run to structured storage
- `getRunDetail(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" }`.
## Related Docs
- [Workflow Steps](./workflow-steps.md)
- [Settings Reference](./settings-reference.md)
- [Architecture](./architecture.md)