SwarmWright
documentation · build the swarmQuick Start
SwarmWright runs as a single Docker container. No external infrastructure required — no broker, no separate database server, no build pipeline. Two commands get you running.
docker pull ralphbarendse/swarmwright:latest
docker run -d \
--name swarmwright \
--network host \
--restart unless-stopped \
-v ./data:/data \
ralphbarendse/swarmwright:latest
Open http://localhost:5001. On first boot you will be prompted to create an
admin account — set a username and password, then log in. From there, go to
Settings → LLM Providers and enter your API key.
No environment variables required to get started.
Or with Docker Compose — grab docker/docker-compose.yml from the repo:
docker compose -f docker/docker-compose.yml up -d
Note: An encryption key is generated on first boot and persisted to
data/.encryption_key. Back this file up alongside data/swarm.db.
All LLM credentials are encrypted at rest — they never leave the container.
Managed Hosting
Don't want to run servers? The same product is available as a managed instance at
console.swarmwright.com —
pick a plan, name your instance, and it's live at
your-name.swarmwright.com in about a minute.
- A dedicated, isolated container per customer — nothing shared
- Your own per-instance encryption key; export your data anytime
- Guaranteed CPU, RAM and disk per plan — all plans multi-user
- Bring your own LLM API key, exactly like self-hosting
Note: everything else in these docs applies to hosted instances too — the cloud runs the same container you'd run yourself. See pricing for plans.
Requirements
- Docker Engine 24+ and Docker Compose v2
- An API key for at least one supported LLM provider (Anthropic, OpenAI, or DeepSeek) — entered via the Settings UI after first boot
- A mounted volume for
data/(handled by docker-compose.yml)
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
LLM_PROVIDER | No | — | anthropic, openai, or deepseek. Configurable in Settings UI; env var takes precedence if set. |
LLM_MODEL | No | — | Model identifier string. Configurable in Settings UI; env var takes precedence if set. |
ANTHROPIC_API_KEY | If anthropic | — | Anthropic API key |
OPENAI_API_KEY | If openai | — | OpenAI API key |
DEEPSEEK_API_KEY | If deepseek | — | Deepseek API key |
SWARM_ENCRYPTION_KEY | Optional | auto-generated | 32-byte base64 Fernet key. Auto-generated on first boot if not set. |
DATABASE_URL | No | sqlite:////data/swarm.db | SQLAlchemy database URL |
DATA_DIR | No | /data | Path to the data volume mount |
LOG_LEVEL | No | INFO | DEBUG / INFO / WARNING / ERROR |
SCHEDULER_TIMEZONE | No | Europe/Amsterdam | APScheduler timezone for heartbeat triggers |
Encryption Key
LLM credentials and secrets are encrypted at rest using Fernet symmetric encryption. The master key is resolved in this order on every boot:
SWARM_ENCRYPTION_KEYenvironment variable — wins if set<DATA_DIR>/.encryption_keyfile — auto-managed by the container- If neither exists, a new key is generated and written to the file
For higher-assurance deployments, generate a key out-of-band and pin it:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Core Concepts
SwarmWright organizes work in three nested scopes: Company, Workspace, and Swarm. A workspace is a department-like container (Finance, Operations, HR). A swarm is a coherent set of agents that collaborate to handle one class of work.
Two artifacts define every swarm:
- meta.yaml — display metadata (name, description, icon). Folder names are stable identifiers; display names live here.
- hierarchy.json — the swarm's topology: which agents exist, what edges connect them, what human nodes are wired in, and the purpose of each connection. This is enforced at runtime.
The runtime enforces hierarchy.json strictly. If an agent attempts to call
another agent, tool, or perceptionist that is not declared in the topology, the action is
blocked and logged as a topology violation. There is no way to suppress this
enforcement.
Key idea: The diagram you draw in the canvas is the configuration. There is no separate specification file to keep in sync. If the connection is on the canvas, it is in hierarchy.json, and it is enforceable at runtime.
Agent Layers
Four layers exist. No more, no fewer. Every agent has exactly one layer, declared in its constitution.
| Layer | Color | Role | Typical responsibilities |
|---|---|---|---|
| Policy | Navy #1e3a5f | Strategic decisions, governance, final authority | Sets rules, approves exceptions, handles escalations, the single point of accountability |
| Orchestrator | Teal #2a6b6b | Coordination, routing, workflow management | Receives events, decides which executioner to call, aggregates results, routes to next step |
| Executioner | Slate #3d4f7c | Task execution, tool use, external actions | Calls skills, writes to databases, calls APIs, processes files, produces artifacts |
| Perceptionist | Amber #c97c2a | Read-only grounding — maps reality to internal data | Reads sensors, classifies inputs, extracts structured data, never writes or acts |
The layer hierarchy is enforced by convention, not by the runtime. A Perceptionist is an agent like any other — the distinction is declared in its constitution and visible in the topology. The UI color-codes everything so the structure is legible at a glance.
Topology
The topology is the declared graph in hierarchy.json. It lists every agent, every
edge, every skill, every human node, and every perceptionist the swarm can use. The runtime
enforces this declaration — agents cannot call anything not listed.
Edge kinds
Every connection between two agents uses one of these kinds:
- escalate — upward authority, toward Policy
- delegate — downward authority, from Policy or Orchestrator
- report — returning results upward
Purpose is mandatory. Every edge requires a non-empty purpose string written in plain English. This is enforced at validation time. The friction is intentional — if you can't state why a connection exists, you shouldn't add it.
Topology violations
When a running agent attempts to call another agent, tool, or perceptionist not listed
in its declared connections, the runtime blocks the call and emits a
topology_violation run step. The run continues if possible; the violation is
surfaced in the Control Room step trace and in the live stream bar on the canvas.
Human-in-the-Loop
SwarmWright has first-class support for human participation at any point in a workflow. Two node types represent humans in a swarm topology: Caller and Informer. Both are placed on the canvas just like agents and connected with the same drag-to-connect mechanism.
Caller nodes
A Caller is a blocking human gate. When a workflow reaches a Caller, execution
pauses and a Human Action request is created in the Inbox. The run waits in
awaiting_human status until a human responds with a decision (yes,
no, or an amended instruction). Only then does the workflow continue.
Use Callers for:
- High-stakes approval gates (invoice above threshold, contract signature, system change)
- Ambiguous decisions that require human judgment
- Compliance checkpoints that must be acknowledged
On the canvas, Caller nodes display a raised-hand glyph (✋) and are styled in sage-green
to distinguish them from agent nodes. Connect an agent to a Caller by dragging from the agent's
edge handle to the Caller node — the connection modal asks for a purpose.
Informer nodes
An Informer is a non-blocking notification. When a workflow reaches an Informer, a message is sent to the Inbox and the workflow continues immediately without waiting. Use Informers for progress updates, alerts, and FYI notifications that a human should see but doesn't need to act on.
On the canvas, Informer nodes display a megaphone glyph (📢).
Connecting human nodes
Human nodes (Caller and Informer) are placed on the canvas first, then connected to agents using the same edge-draw mechanism used for agent-to-agent connections:
- Place a Caller or Informer from the palette onto the canvas
- Select an agent node to open the inspector
- Click Connect to… to enter connect mode
- Click the human node you want to connect to
- In the modal that appears, enter the purpose of the connection
- Save — the connection is written to
hierarchy.json
Multiple agents can connect to the same Caller or Informer. The node remains independent on the canvas — it is not "owned" by any single agent.
Inbox interaction
The Inbox tab shows two categories:
- Awaiting — pending Caller requests. Select one to see full context, then approve (
yes), reject (no), or amend (provide an alternative instruction). The workflow resumes from the exact point it paused. - Notifications — unread Informer messages. These are informational and do not block a run. Mark them read individually or all at once.
Past decisions appear under Approved and Rejected tabs with full context for audit purposes.
Resource Scopes
Knowledge documents and skills exist at one of four scopes. References resolve most-local-first: swarm → workspace → company → built-in.
swarm scope → data/workspaces/<wid>/swarms/<sid>/<type>/
workspace scope → data/workspaces/<wid>/<type>/
company scope → data/company/<type>/
built-in scope → app/builtin_skills/ (read-only, platform-provided)
| Reference in constitution | Resolves to |
|---|---|
approval-thresholds | Most-local match: swarm → workspace → company |
workspace/finance-procedures | Workspace scope only |
company/glossary | Company scope only |
write_swarm_file | Resolves to built-in if not overridden at any higher scope |
Company-wide policies attach to all agents; department-level procedures are available to all agents in that workspace; swarm-specific context stays private to one workflow. The built-in scope is the final fallback for skills — it is read-only and cannot be edited, but any built-in skill can be overridden by creating a skill with the same name at any other scope.
Constitutions
A constitution is a .md file with YAML frontmatter that defines what an agent
is — its identity, values, role, and the knowledge it has access to. It does
not declare connections. Connections live in hierarchy.json.
The constitution is passed verbatim as part of the system prompt when the agent runs. Writing a good constitution is the single most impactful thing you can do for agent quality.
---
name: Finance Orchestrator
layer: orchestrator
model: claude-opus-4-7
knowledge:
- approval-thresholds
- workspace/finance-procedures
---
You coordinate invoice intake for the Finance workspace.
Route incoming invoices to the appropriate Executioner agent
based on value, vendor, and approval requirements.
Never approve invoices directly. Escalate ambiguous cases
to the Policy layer with full context.
When routing, always state which agent you are delegating to
and why, so the decision is traceable.
Allowed frontmatter fields:
| Field | Type | Description |
|---|---|---|
name | string | Display name shown in the UI |
layer | string | policy / orchestrator / executioner / perceptionist |
model | string | Model identifier. Overrides the swarm default if set. |
knowledge | list | List of knowledge document references (scope-qualified or bare) |
Connection fields (skills, edges) belong in hierarchy.json,
not in the constitution. Keeping identity separate from topology means you can change what
an agent knows without touching what it connects to, and vice versa.
Skills
Skills are Python scripts invocable by Executioner agents. Each skill lives as two files with the same basename:
skill-name.py— must expose a top-levelrun(input: dict, context: dict) -> dictskill-name.yaml— declaresinput_schema,output_schema,timeout_seconds,allowed_packages
Skills run in a subprocess, never in the Flask process. A misbehaving skill cannot crash
the host. Packages listed in a skill's allowed_packages must first be
registered in the global allowlist via Settings → System → Packages.
Adding a package there installs it into the runtime and persists it across container restarts.
# parse-invoice.yaml
input_schema:
type: object
properties:
text: { type: string }
required: [text]
output_schema:
type: object
properties:
vendor: { type: string }
amount: { type: number }
currency: { type: string }
allowed_packages: [re, json]
timeout_seconds: 10
# parse-invoice.py
import re, json
def run(input: dict, context: dict) -> dict:
text = input["text"]
# ... extraction logic ...
return {"vendor": vendor, "amount": amount, "currency": currency}
Skills can be scoped at company, workspace, or swarm level. A skill at company scope is available to all agents in all swarms. Skills are listed in the Library tab and attached to agents via the topology canvas.
Built-in skills
SwarmWright ships 20 platform-provided skills at the built-in scope. They are always
available to any agent without setup and appear under Library → Skills → Built-in
as read-only cards. No allowed_packages required — they use only the Python standard library.
To override a built-in, create a skill with the same name at swarm, workspace, or company scope. The resolver finds yours first.
File Store
The runtime injects context["files_root"] into every skill call — the path to the current swarm's files/ directory. Path traversal outside this root is rejected.
| Skill | Description | Key inputs | Returns |
|---|---|---|---|
write_swarm_file |
Write a file to files/. Creates parent directories automatically. |
path, content, encoding (utf-8 / base64) |
path, size_bytes, checksum (SHA-256) |
read_swarm_file |
Read a file from files/. |
path, encoding (utf-8 / base64) |
content, encoding, size_bytes |
list_swarm_files |
List files in files/, optionally filtered by path prefix. |
prefix (optional) |
files[] — path, filename, size_bytes, modified_at |
delete_swarm_file |
Delete a file. Succeeds silently if the file does not exist. | path |
deleted (bool), path |
HTTP
| Skill | Description | Key inputs | Returns |
|---|---|---|---|
http_get |
Perform an HTTP GET request. Timeout 30 s. | url, headers (optional), as_json (bool) |
status, headers, body, json (when as_json=true) |
http_post |
Perform an HTTP POST with a JSON body. Sets Content-Type: application/json automatically. |
url, body (object), headers (optional), as_json (bool) |
status, headers, body, json (when as_json=true) |
Key-Value Store
A per-swarm persistent store backed by _kv_store.json in the swarm's files/ directory. Values survive across runs and can hold any JSON-serializable type.
| Skill | Description | Key inputs | Returns |
|---|---|---|---|
kv_get |
Retrieve a value by key. | key, default (optional fallback) |
key, value, found (bool) |
kv_set |
Store a value by key. Atomic write — safe under concurrent runs. | key, value (any JSON type) |
key, value |
Utilities
| Skill | Description | Key inputs | Returns |
|---|---|---|---|
get_datetime |
Return the current UTC date and time. Useful for scheduling logic or timestamping output. | none | utc_iso, unix_timestamp, date, time, weekday, year, month, day, hour, minute |
Notifications
| Skill | Description | Key inputs | Returns |
|---|---|---|---|
send_webhook |
POST a JSON payload to any webhook URL. Works with Slack, Discord, Teams, or custom endpoints. | url, payload (object), headers (optional) |
status, body, ok (true if 2xx) |
Chat — Platform Skills
Used by the built-in Operator and Concierge swarms. Available at company scope so operators can also attach them to custom swarms. Like all built-ins, each can be overridden by a same-named skill at any higher scope.
| Skill | Used by | Description |
|---|---|---|
create_workspace | Operator | Provision a new workspace folder and meta.yaml. |
create_swarm | Operator | Provision a new swarm shell inside a workspace. |
setup_swarm | Operator | Create a swarm and its first agent in a single step. |
add_agent_to_swarm | Operator | Add an agent to an existing swarm, set its entry point, and attach skills. |
draft_constitution | Operator | Generate a starter constitution markdown for a new agent. |
patch_topology | Operator | Apply a named structural edit to a swarm's hierarchy.json. |
patch_swarm | Operator | Enable or disable a swarm, or update its metadata. |
read_skill | Operator | Read an existing skill's source before editing or recreating it. |
create_skill | Operator | Create a custom skill from Python + YAML content. |
trigger_run | Operator | Invoke an existing swarm with a given input payload. |
list_runs | Operator | Fetch recent runs for a swarm with status and summary. |
read_run | Both | Fetch a run's status and full step trail by run ID. |
list_swarms | Operator | List swarms across workspaces with their enabled status. |
list_workspaces | Operator | List all workspaces on the platform. |
list_swarm_artifacts | Operator | List the files a swarm has written to its files/ directory, with size and modified time. |
read_swarm_artifact | Operator | Read the contents of a file from any swarm's files/ directory (200 KB cap). |
search_knowledge | Operator | Full-text search across all knowledge files on the platform. |
web-search | Operator | Search the web via DuckDuckGo for current information. |
list_unmet_needs | Operator | Read the queue of Concierge requests that could not be routed. |
list_workspace_swarms | Concierge | Enumerate sibling swarms in the current workspace. |
flag_unmet_need | Concierge | Record a request the Concierge could not fulfil into unmet_needs. |
Triggers
Triggers are deterministic scripts — not agents. They produce events that enter a swarm. No LLM calls happen inside a trigger. Use regex, JSON Schema, JSONPath, and standard Python libraries. Keep them dumb. Their job is to detect and route, not to think.
Four trigger types:
| Kind | Fires when | Config |
|---|---|---|
| Heartbeat | On a cron schedule via APScheduler | cron expression, payload template |
| Listener | External webhook / incoming message arrives at the listener endpoint | path, optional filter JSONPath expression |
| Invocation | Fired manually via POST /api/v1/triggers/invocations/<id> or the Control Room Fire button |
payload_schema for validation |
| File Watcher | A file matching a glob pattern is created or modified in the swarm's files/ directory |
glob pattern (e.g. reports/*.csv), optional events list (created, modified) |
Triggers are created and managed in the swarm canvas inspector. Each trigger can be individually enabled or disabled without deleting it. The Control Room's Fire button fires a raw event into a swarm without going through a trigger — useful for testing.
Swarm Files
Every swarm has a persistent files/ directory on disk. Agents and humans can
both read and write to it. Files are indexed in the database with full provenance — who
created each file (agent, human, or unknown), which run and which step produced it, and
when it was last modified.
This shared filesystem acts as a durable handoff layer between runs: an agent can write
a CSV to files/reports/ in one run, and a human can download it from the canvas
while another run is processing the next batch.
How agents write files
Agents use the built-in write_swarm_file skill. The runtime injects a
files_root path into every skill's context dict so skills know
where to write without needing database access. Subdirectory nesting is unlimited —
agents can organise files into any folder structure they need.
# write_swarm_file input
{
"path": "reports/2024-06/summary.md",
"content": "# June Summary\n...",
"encoding": "utf-8"
}
After each skill call, the runtime reconciles the file index: new files are inserted with
origin=agent; deleted files are removed from the index.
How humans manage files
The swarm canvas has a Files panel below the inspector. From there you can:
- See all files with their origin (agent-written vs. human-uploaded)
- Upload files by clicking ↑ Upload or dragging and dropping onto the panel
- Create a folder with the + Folder button — prompts for a path like
reports/2024 - Download any file with the ↓ button
- Delete a file with the ✕ button
Human-uploaded files are indexed with origin=human. A color-coded dot on each
row indicates origin: accent color for agent-written, grey for human-uploaded.
The Files browser
Beyond the per-swarm panel, the Files page (#files in the top
navigation) is an org-wide browser over every swarm's file store — one place to find, preview
and manage everything your swarms have produced. A tree on the left groups files by
workspace → swarm → folder; selecting a node scopes the list on the right.
- Search and filter — match by filename or path (resolved server-side), and narrow to a single workspace.
- Table or grid view — toggle between a dense table and a thumbnail grid where images render inline; your choice is remembered.
- At-a-glance metadata — file-type icons, a color-coded origin dot, size, last-modified time, and a link back to the run that produced the file.
- Inline preview — a slide-over panel renders images, PDFs, and text-like files (Markdown, JSON, CSV, logs) without downloading; oversized files fall back to a download.
- Row actions on hover — preview, download, copy path, link into another swarm, and delete.
- Upload from anywhere — upload or drag-and-drop straight into a chosen swarm without opening its canvas first.
Cross-swarm links
A single file can appear in more than one swarm through a link — a logical reference, not a copy. The bytes live exactly once with the original (canonical) file; every other swarm holds a lightweight pointer to it. Use the ↗ row action in the Files browser to link a file into another swarm.
- No duplication — a linked row carries the canonical file's size and type; downloading or previewing a link always serves the canonical bytes.
- Safe deletion — deleting a link removes only that reference. Deleting the canonical file is blocked while any links still point at it, so a link can never dangle silently.
- Provenance — a linked row is badged
↗ linkedand shows where it came from; the canonical file shows how many swarms it appears in. - Passive by design — creating a link does not fire the target swarm's file watcher. Links are for sharing and discovery, not for starting another swarm's pipeline. To kick off a workflow, upload the file into the swarm (or have an agent write it) so a real file lands on disk.
File watcher triggers
A File Watcher trigger watches the swarm's files/ directory using
a glob pattern. When a matching file is created or modified — by an agent, by a human upload,
or by an external process writing to the mounted volume — the trigger fires an invocation event
into the swarm. This enables file-arrival workflows: drop a PDF into files/inbox/
and the swarm starts processing it automatically. Cross-swarm links are passive and do
not fire the watcher — only a real file written to disk does.
Index reconciliation
On each swarm load, SwarmWright reconciles the disk contents of files/ against the
database index. Files present on disk but absent from the index are inserted with
origin=unknown. Index rows pointing to files that no longer exist on disk are
removed. This means the index stays accurate even after manual filesystem operations
(backup restores, external writes, direct volume mounts).
Cross-swarm Delegation
Any agent can invoke another swarm as a synchronous, blocking sub-call — like calling a function in a different department. The calling agent sends an input payload, the target swarm runs to completion, and the result is returned inline. Execution in the parent swarm pauses until the child finishes.
This is different from firing an event into another swarm. A swarm call is:
- Synchronous — the parent waits for the result
- Topology-declared — the connection must be drawn on the canvas; ad-hoc calls are blocked
- Cross-workspace capable — the target swarm can live in a different workspace
- Alias-addressed — the agent uses a short name (alias) to refer to the target, not a raw swarm ID
Setting up a swarm call
- Open the canvas of the calling swarm
- Click External swarm in the left palette — a modal opens listing all swarms across all workspaces
- Select the target swarm and click Add to canvas — a teal ⬡ node appears
- Select an agent node and click Connect to…, then click the ⬡ swarm node
- In the modal: enter an alias (a short slug the agent uses in its action) and a purpose
- Save — the edge is written to
hierarchy.jsonas aswarm_callsentry
Alias naming: Use short, meaningful slugs — hr_lookup, credit_check, compliance_scan. The alias is what the agent writes in its invoke_swarm action. It cannot contain spaces.
How agents invoke a swarm
When an agent has declared swarm calls, the runtime adds them to the system prompt automatically.
The agent invokes a target using the invoke_swarm action:
{
"action": "invoke_swarm",
"alias": "hr_lookup",
"input": {
"employee_id": "EMP-4821",
"query": "Is this employee currently active?"
}
}
The runtime resolves the alias to the target swarm's entry point, creates a child execution context sharing the parent run's ID and step sequence, runs the target swarm, and returns the final output back to the calling agent as the action result. The child swarm's steps appear in the same run trace in the Control Room.
Cross-workspace delegation
Swarm calls can target swarms in any workspace, not just the current one. This enables cross-departmental workflows — a Finance swarm can query an HR swarm, an Operations swarm can trigger a Compliance swarm, and so on. The target swarm does not need to be aware it is being called from another workspace.
On the canvas, cross-workspace swarm nodes are styled in amber (instead of teal) and carry an ↗ cross-workspace badge. This is a deliberate visual warning: cross-workspace calls introduce a dependency between departments. If the target swarm is disabled, renamed, or deleted, the calling swarm will fail at runtime.
Design guidance: Cross-workspace calls are powerful but should be used sparingly. Each one creates an inter-department coupling. If a call is only needed in one direction, prefer making the data available through shared knowledge or a shared skill rather than a live swarm call.
Removing swarm nodes
Click a ⬡ swarm node to open its inspector. The inspector shows the swarm name, workspace, and all agents connected to it. Buttons:
- Open swarm — navigate to the target swarm's canvas in a new tab
- Remove from canvas — removes the swarm node and all its swarm_call edges from the topology
Click a swarm-call edge to inspect it: see the alias, connecting agent, and purpose. Click Remove to delete just that edge without removing the swarm node.
Org Design
The Org tab is your company organogram. It shows all workspaces (departments) and the swarms within them.
Workspaces
Create a workspace for each department or functional area: Finance, Operations, HR, Customer Success. A workspace is a container — it has no runtime behavior of its own. It provides a namespace for swarms and a scope for shared resources (knowledge, skills, perceptionists).
Swarms
Each swarm handles one class of work. Create swarms within workspaces. A swarm has an enabled flag — disabled swarms cannot receive events and will not start runs. Activate or pause swarms from the Org view or the Control Room without deleting them.
Deleting a swarm removes both the database record and the filesystem directory
(data/workspaces/<ws>/swarms/<swarm>/) permanently.
Swarm Canvas
The swarm canvas is where you design a swarm's topology. It is a Cytoscape.js diagram
that writes directly to hierarchy.json on every change — there is no separate
save step.
Adding nodes
Drag items from the left palette onto the canvas:
- Agent layers (Policy, Orchestrator, Executioner, Perceptionist) — adds an agent and opens the constitution editor
- Skill — adds a skill reference node; connect it to an Executioner to declare usage
- Trigger — opens the trigger creation modal
- Caller — places a human Caller node (✋) on the canvas unconnected
- Informer — places a human Informer node (📢) on the canvas unconnected
- External swarm — opens a swarm picker (all workspaces) and places a teal ⬡ swarm node; connect it to an agent to declare a cross-swarm delegation
Drawing connections
- Click a node to open its inspector
- Click Connect to… in the inspector
- A banner appears: "click a target node to connect"
- Click any compatible target (agent, human node)
- Enter the
purposein the modal — this is mandatory - Save — the edge appears and is written to
hierarchy.json
Deleting elements
Click any node or edge to open its inspector, then click Delete. For agents, this removes the agent from the topology and its constitution file. For human nodes, this disconnects all edges and removes the node from the canvas. For edges, only the connection is removed — both endpoints remain.
Inspector
The right inspector panel shows context-sensitive actions for the selected element. For agents: edit constitution, fire test event, view connections. For edges: view purpose, delete. For the canvas background: view swarm info, fire event, delete swarm.
Files panel
Below the inspector is the swarm's Files panel. It shows every file in the
swarm's files/ directory, with a color dot indicating whether each was written
by an agent or uploaded by a human. From the panel header you can upload files, create
folders, download individual files, and delete files. Drag and drop onto the panel is also
supported. See Swarm Files for the full architecture.
Inbox
The Inbox is the human-facing side of the human-in-the-loop system. The notification pip in the top nav shows the count of items requiring attention.
Tabs
| Tab | Contents |
|---|---|
| Awaiting | Pending Caller requests. Each shows the triggering run, the agent that raised the request, and full context. Respond with Approve, Reject, or Amend. |
| Notifications | Unread Informer messages. These are FYI — the workflow already continued. Mark read to dismiss. |
| Approved | Past approvals (decision: yes) with timestamp and reason. |
| Rejected | Past rejections (decision: no) with timestamp and reason. |
Approving a Caller request
Select an awaiting item. The detail panel shows the run context, the requesting agent's question or proposal, and the full event payload. Three actions are available:
- Approve — decision
yes, workflow resumes - Reject — decision
no, workflow continues with rejection noted - Amend — provide an alternative instruction; the agent receives this as amended context
You can optionally add a reason to any decision. This reason is attached to the run step for audit purposes.
Control Room
The Control Room (formerly Runs) is the operational dashboard for your swarm fleet. It combines a live org chart with a filtered run log.
Organogram panel
The left panel shows all workspaces and their swarms as a collapsible tree. For each swarm:
- A green dot (●) indicates the swarm is active and can receive events
- A grey dot (●) indicates the swarm is paused
- The active / paused toggle button activates or pauses the swarm in place
- The ▶ Fire button opens a modal to send a raw event directly into the swarm
Clicking a workspace row filters the run log to that workspace's swarms. Clicking a swarm row filters to that swarm only. Click again to deselect and show all runs.
Run log
The right panel shows runs in reverse-chronological order. Filter by:
- Status: running, completed, failed, pending, awaiting human
- Workspace: filters to all swarms in that workspace
Live updates arrive via SSE — the run log refreshes automatically when a run starts, completes, or fails. No manual refresh needed.
Run detail
Click a run to see its step trace: every agent call, skill invocation, human interaction, and topology violation in sequence. Each step shows the agent name, edge purpose, input/output, duration, and any errors. A Replay button re-fires the original event payload.
Library
The Library tab manages reusable resources: Knowledge (documents) and Skills (Python scripts). Both can exist at company, workspace, or swarm scope. Use the scope sidebar on the left to switch between scopes. Both tabs have a live filter bar to search by name or description.
The Skills scope sidebar also includes a Built-in entry at the top. Built-in skills are platform-provided, read-only, and always available. They are displayed as informational cards — you can click through to read the source but cannot edit or delete them. Override any built-in by creating a skill with the same name at any other scope.
Knowledge documents
Knowledge documents are Markdown files injected into an agent's context at runtime.
Attach them to agents by referencing the document name in the constitution's
knowledge array. Company-scoped documents are available to all agents
without explicit reference.
Clicking + New document or any existing card opens an inline full-pane editor with:
- A Write tab — CodeMirror editor in Markdown mode with syntax highlighting
- A Preview tab — rendered Markdown so you can check formatting before saving
- A Draft with AI button — describe what the document should contain and the LLM generates a first draft
- ⌘S / Ctrl+S to save without reaching for the mouse
Document cards show a content preview snippet and a relative last-modified timestamp.
Skills
Skills are written directly in the browser using a split-pane code editor:
the left pane is skill.py (Python, CodeMirror), the right pane is
config.yaml. Both are saved atomically.
The Draft with AI button generates a working .py + .yaml
pair from a plain-English description. The collapsible Available Python libraries
panel shows what's installed and which packages need an
allowed_packages entry in the YAML.
Once saved, a skill appears in the canvas palette and can be connected to Executioner agents. Skill cards show the description, timeout, package chips, and a last-modified timestamp.
Settings
The Settings tab is the central control panel for your SwarmWright instance. It is organized into six tabs. The Users tab is only visible to administrators.
LLM Providers
Configure credentials for Anthropic, OpenAI, and DeepSeek. Each provider has a Default provider radio button — this determines which provider is used when no model is specified in an agent's constitution. Credentials are encrypted with Fernet before being written to the database.
Use the Test connection button to verify a provider's key is valid before saving.
Models
Manage the list of model identifiers available to agents. The default list includes Claude Opus 4.7, Claude Sonnet 4.6, Claude Haiku 4.5, GPT-4o, GPT-4o mini, DeepSeek Chat, and DeepSeek Reasoner. You can add custom model identifiers (useful for self-hosted models or new releases) and set a default model that applies when no model is declared in an agent's constitution.
Branding
Customize your instance: set a company name (shown in the nav) and upload a logo.
Logos are served from /api/v1/settings/branding/logo. The Reset button
removes the custom logo and reverts to the default SwarmWright mark.
System
Configure runtime defaults:
- Default skill timeout — fallback timeout in seconds for skills that don't declare
timeout_secondsin their YAML - Default heartbeat schedule — cron expression pre-filled when creating a new heartbeat trigger
- Audit log retention — how many days of settings-change audit entries to keep (pruned nightly)
- Event bus workers — thread pool size for the event bus (requires container restart)
Package installer
The Packages panel is a package manager for the skill runtime. Each
package in the global allowlist is available to any skill that declares it in
allowed_packages. Packages not yet installed show an Install
button — clicking it runs pip install inside the container and registers
the package. Installed packages are shown with a green indicator.
Packages are also auto-installed on container start from the allowlist, so new deployments pick up all registered packages without manual steps.
Security
View the settings-change audit log (who changed what, and when) and rotate the master encryption key. Key rotation re-encrypts all stored secrets with a new Fernet key. Session-based login is active once users are configured — see the Users tab.
Users & Permissions
SwarmWright has two account types: Admin and User. Admins have unrestricted access to everything. Regular users get a per-account set of 17 permission flags that gate both the UI (buttons are hidden) and the API (returns 403 when a restricted endpoint is called).
First boot
On first boot, navigating to http://localhost:5001 redirects to
/setup. Fill in a username, optional display name, and password to create
the first admin account. Once created, all subsequent visits redirect to /login
until a valid session exists.
Managing users
Admins can create, edit, and delete accounts from Settings → Users. The Users tab is not visible to non-admin accounts. When creating or editing a user, the Administrator toggle grants full access and hides the permissions checklist. For regular users, each of the 15 flags can be toggled independently.
Default permissions for new users (unless overridden at creation):
- On by default: Start runs, Stop runs, Decide inbox items
- Off by default: everything else
Permission flags
| Flag | What it controls |
|---|---|
can_create_workspace | Create new workspaces |
can_edit_workspace | Rename / update workspace metadata |
can_delete_workspace | Delete a workspace (must be empty) |
can_create_swarm | Create new swarms inside a workspace |
can_edit_swarm | Rename, enable/disable, transfer swarms |
can_delete_swarm | Delete a swarm and its contents |
can_start_run | Fire events, invoke triggers, replay runs |
can_stop_run | Stop a running swarm execution |
can_edit_constitution | Add / remove agents and edit their constitutions; manage callers and informers on the canvas |
can_manage_triggers | Create, edit, and delete triggers |
can_manage_skills | Create, edit, and delete skills in the Library |
can_manage_knowledge | Create, edit, and delete knowledge documents |
can_decide_inbox | Approve or reject human-in-the-loop inbox items |
can_view_settings | Access the Settings tab (read-only for non-admins) |
can_manage_users | Reserved — admin-only user management is enforced server-side regardless of this flag |
can_chat_workspace | Access the Concierge chat inside any workspace the user can already see |
can_chat_operator | Access the Operator chat at the org level. Implies can_chat_workspace. This chat can design topology, create swarms, and trigger runs. |
Session & security
Authentication uses server-side signed sessions (Flask secure cookies). Sessions are
permanent and last 30 days of inactivity. Passwords are hashed with
werkzeug.security (PBKDF2-HMAC-SHA256). There is no token-based or
OAuth flow — this is intentional for the target use case of small, trusted teams on
a private network.
Operator Chat
The Operator is a built-in swarm that ships with the platform at
data/workspaces/platform/swarms/operator-chat/. It gives users with
can_chat_operator a conversational surface for designing and operating
the platform: creating workspaces, drafting swarms, patching topology, triggering runs,
and reviewing the unmet-needs queue.
A round chat icon appears in the top-right of the workspace canvas on the Org screen. Clicking it opens a side panel (40 % screen width, resizable) with the Operator conversation. The panel header reads Operator alongside the currently active default model name and a hint: "This chat can edit your platform."
No special runtime path. The Operator executes as a normal swarm run. Every action it takes — creating a workspace, patching a topology, firing a trigger — goes through the same API surface a human would use through the GUI, producing the same run steps and audit trail.
What the Operator can do
The Operator's topology gives it access to a set of built-in skills with declared purposes (listed in full under Built-in skills → Chat). It cannot perform actions outside these skills — attempts are logged as topology violations.
- Create workspaces, and build full swarms with their first agent in one step
- Add agents to existing swarms and draft starter agent constitutions
- Apply structural edits to a swarm's topology; enable or disable swarms
- Author and edit custom skills (Python + YAML)
- Trigger existing swarms, then read their run trails and status
- Inspect the files a run produced — confirm an artifact exists in a swarm's
files/store and read its contents - Search platform knowledge and the web for current information
- Read the unmet-needs queue raised by Concierge agents
Unmet-needs signals
When a new Operator session opens, if there are unaddressed unmet needs older than 24 hours, the Operator surfaces them as an opening message. A Signals tab next to the conversation transcript lists all open unmet-need rows, grouped by workspace, with a Draft a swarm for this button that pre-fills the Operator's input with the user's original request phrasing.
Model
The Operator constitution omits the model: frontmatter field. It inherits
whatever is selected under Settings → Models → Default model. Changing
the platform default immediately affects the next Operator message — no constitution edit needed.
Concierge Chat
Each workspace has a built-in Concierge swarm provisioned automatically at creation and
on every boot after a Phase 8 upgrade. It lives at
data/workspaces/<wid>/swarms/concierge/.
A round chat icon appears in the workspace topbar next to the existing tabs. Clicking it
opens a side panel whose header reads Concierge — <workspace name>.
Users with can_chat_workspace see the button; users without it do not.
The Concierge routes natural-language requests to the right swarm in the workspace. It never uses the words "swarm" or "agent" in default responses — it speaks in domain terms. Each response includes a collapsed "How did I get this?" link that expands the run-step trail for transparency.
What the Concierge can do
- List sibling swarms in the workspace (
list_workspace_swarms) - Invoke any sibling swarm as a synchronous sub-call and return the result inline
- Read run status and steps (
read_run) - Record an unmet need when no swarm can fulfil a request (
flag_unmet_need)
The Concierge has no topology edges to topology-mutation skills. It cannot
create swarms, patch constitutions, or edit hierarchy files. That boundary is structural, not
a permission check — the skills simply do not exist in its hierarchy.json.
Concierge topology auto-update
The Concierge's swarm_calls block is regenerated to reflect the current set of
sibling swarms in the workspace every time the registry loads. Adding or removing a swarm
from a workspace is immediately reflected in what the Concierge can route to.
Chat sessions
Both chat surfaces persist sessions across browser refreshes. One session exists per
(user, scope) — org for the Operator or a specific workspace ID for the
Concierge. Sessions are created lazily on first message.
A Wipe conversation button (with confirmation) deletes the visible
chat_messages rows. It does not delete the underlying runs or
run steps — the audit trail is unchanged. The chat layer forgets; the ledger remembers.
When the accumulated conversation context would exceed the configured token budget (default 32 000 tokens), the chat layer drops the oldest messages first and prepends a summary line so the agent retains situational context.
API Endpoints
All endpoints are JSON. Base path: /api/v1.
Health
| Method | Path | Description |
|---|---|---|
| GET | /health | Health check — returns {"status":"ok"} |
Workspaces & Swarms
| Method | Path | Description |
|---|---|---|
| GET | /workspaces | List all workspaces |
| POST | /workspaces | Create a workspace. Body: display_name, description |
| GET | /workspaces/<id> | Get workspace with nested swarm list |
| PUT | /workspaces/<id> | Update workspace display name / description |
| DELETE | /workspaces/<id> | Delete workspace (must have no swarms) |
| GET | /workspaces/<id>/swarms | List swarms in a workspace |
| POST | /workspaces/<id>/swarms | Create swarm. Body: display_name, description |
| GET | /swarms/<id> | Get swarm with full metadata |
| PUT | /swarms/<id> | Update swarm. Supports enabled (boolean) to activate/pause |
| DELETE | /swarms/<id> | Delete swarm — removes DB row AND filesystem directory |
Agents
| Method | Path | Description |
|---|---|---|
| GET | /swarms/<id>/agents | List agents in a swarm |
| GET | /agents/<id> | Get one agent with constitution text |
| PUT | /agents/<id>/constitution | Update agent constitution text |
Topology
| Method | Path | Description |
|---|---|---|
| GET | /swarms/<id>/topology | Get the full hierarchy.json content |
| PATCH | /swarms/<id>/topology | Apply a named operation to the topology. See hierarchy.json. |
Topology patch operations:
| op | params | Effect |
|---|---|---|
add_agent | id, layer | Creates agent, adds to topology |
remove_agent | id | Removes agent and all its edges |
add_edge | from, to, kind, purpose | Adds an agent-to-agent edge |
remove_edge | from, to | Removes an edge |
add_call | agent, caller, purpose | Connects an agent to a Caller node |
remove_call | agent, caller | Removes agent→Caller connection |
add_inform | agent, informer, purpose | Connects an agent to an Informer node |
remove_inform | agent, informer | Removes agent→Informer connection |
add_canvas_caller | caller | Places a Caller node on the canvas (unconnected) |
remove_canvas_caller | caller | Removes Caller node and all its connections |
add_canvas_informer | informer | Places an Informer node on the canvas (unconnected) |
remove_canvas_informer | informer | Removes Informer node and all its connections |
add_canvas_swarm | swarm_id | Places an external swarm ⬡ node on the canvas (unconnected) |
remove_canvas_swarm | swarm_id | Removes the swarm node and all its swarm_call edges from the topology |
add_swarm_call | agent, alias, swarm_id, purpose | Connects an agent to an external swarm with a named alias; swarm must already be on the canvas |
remove_swarm_call | agent, alias | Removes a single agent→swarm delegation edge by agent and alias |
Events & Triggers
| Method | Path | Description |
|---|---|---|
| POST | /swarms/<id>/events | Fire an event into a swarm. Body is the event payload (arbitrary JSON). |
| GET | /events | List recent events. Params: swarm_id, limit |
| GET | /swarms/<id>/triggers | List triggers for a swarm |
| POST | /swarms/<id>/triggers | Create trigger. Body: name, kind, config |
| PUT | /triggers/<id> | Update trigger (config, enabled flag) |
| DELETE | /triggers/<id> | Delete trigger |
| POST | /triggers/invocations/<id> | Manually invoke a trigger |
Runs
| Method | Path | Description |
|---|---|---|
| GET | /runs | List runs. Params: status, swarm_id, workspace_id, limit, offset |
| GET | /runs/<id> | Get one run with full step trace |
| POST | /runs/<id>/replay | Re-fire the original event — creates a new run |
Human actions (Caller inbox)
| Method | Path | Description |
|---|---|---|
| GET | /inbox | List Caller requests. Params: status (pending / yes / no), swarm_id |
| GET | /inbox/<id> | Get one action with full context |
| POST | /inbox/<id>/decide | Respond to a Caller request. Body: decision (yes/no), reason, amend |
Human informs (Informer inbox)
| Method | Path | Description |
|---|---|---|
| GET | /informs | List Informer notifications. Params: status (unread / read), swarm_id |
| GET | /informs/<id> | Get one notification with full context |
| POST | /informs/<id>/read | Mark a notification as read |
| POST | /informs/<id>/dismiss | Dismiss a notification |
Knowledge
| Method | Path | Description |
|---|---|---|
| GET | /knowledge | List knowledge documents. Params: scope, workspace_id, swarm_id |
| POST | /knowledge | Create knowledge document. Body: scope, name, title, content |
| GET | /knowledge/<id> | Get one document with full content |
| PUT | /knowledge/<id> | Update document. Body: title, content |
| DELETE | /knowledge/<id> | Delete document and its .md file |
| POST | /knowledge/<id>/draft | AI-generate document content. Body: prompt (optional description) |
Skills
| Method | Path | Description |
|---|---|---|
| GET | /skills | List skills. Params: scope, workspace_id, swarm_id |
| POST | /skills | Create skill. Body: scope, name, py_content, yaml_content |
| GET | /skills/<name> | Get one skill with full .py and .yaml content. Params: scope |
| PUT | /skills/<name> | Update skill files. Body: py_content, yaml_content |
| DELETE | /skills/<name> | Delete skill — removes .py and .yaml files. Params: scope |
| POST | /skills/_meta/draft | AI-generate skill files. Body: name, prompt |
| GET | /skills/_meta/runtime | Returns Python version, stdlib highlights, and installed third-party packages available to skills |
Swarm Files
| Method | Path | Description |
|---|---|---|
| GET | /swarms/<id>/files | List files in the swarm's files/ directory. Param: prefix to filter by path prefix |
| POST | /swarms/<id>/files | Upload a file. Multipart form with file field and optional path field. Add overwrite=true to replace an existing file. Max 50 MB. |
| GET | /swarms/<id>/files/download | Download a file. Param: path (relative to files/). Returns raw bytes with correct MIME type. |
| DELETE | /swarms/<id>/files | Delete a file. Param: path. Also removes the index row and prunes empty parent directories. |
Auth
| Method | Path | Description |
|---|---|---|
| POST | /auth/setup | Create the first admin account (only succeeds when no users exist). Body: username, password, display_name (optional) |
| POST | /auth/login | Log in. Body: username, password. Sets a signed session cookie on success. |
| POST | /auth/logout | Clear the current session. |
| GET | /auth/me | Return the currently logged-in user including all 15 permission flags. Returns 401 if not authenticated. |
Users (admin only)
| Method | Path | Description |
|---|---|---|
| GET | /users | List all users with their permission flags. |
| GET | /users/<id> | Get a single user. |
| POST | /users | Create a user. Body: username, password, display_name, is_admin, permissions (map of flag → bool). |
| PUT | /users/<id> | Update a user. Body: any subset of display_name, password, is_admin, is_active, permissions. Cannot remove own admin status or deactivate own account. |
| DELETE | /users/<id> | Delete a user. Cannot delete own account. |
Chat
All chat endpoints live under /api/chat/ (not /api/v1/). Concierge endpoints require can_chat_workspace; Operator endpoints require can_chat_operator.
| Method | Path | Description |
|---|---|---|
| POST | /api/chat/sessions | Get or create a session. Body: scope ("org" or "workspace"), workspace_id (required when scope = "workspace"). Idempotent — returns the existing session if one already exists for this (user, scope, workspace_id). |
| GET | /api/chat/sessions | List sessions. Params: scope, workspace_id. |
| GET | /api/chat/sessions/<id>/messages | Paginated message history. Params: before_id, limit (default 50). |
| POST | /api/chat/sessions/<id>/messages | Send a message. Body: content. Saves the user message, starts the underlying swarm run, and streams response events over SSE. Returns {message_id, run_id} synchronously. |
| DELETE | /api/chat/sessions/<id>/messages | Wipe message history for this session. Underlying runs are preserved. Returns {deleted: count}. |
| DELETE | /api/chat/sessions/<id> | Delete the session and cascade its messages. Underlying runs are preserved. |
Unmet Needs
| Method | Path | Description |
|---|---|---|
| GET | /api/unmet-needs | List unmet needs. Params: workspace_id, status (default "open"). Requires can_chat_operator. |
| PATCH | /api/unmet-needs/<id> | Update status. Body: status ("dismissed" or "addressed"), addressed_by_run_id (optional). Requires can_chat_operator. |
Settings
| Method | Path | Description |
|---|---|---|
| GET | /settings | List all settings key-value pairs (secrets redacted) |
| GET | /settings/<key> | Get one setting by key |
| PUT | /settings/<key> | Set one setting. Body: value, is_secret, value_type |
| PUT | /settings | Bulk-set multiple settings in one transaction. Body: array of {key, value, is_secret, value_type} |
| POST | /settings/llm/test | Test LLM connectivity. Body: provider, model, api_key (optional — uses stored key if omitted) |
| GET | /settings/audit | Settings change audit log. Params: limit, offset |
| POST | /settings/security/rotate-key | Rotate the master encryption key — re-encrypts all stored secrets |
| GET | /settings/branding/logo | Get the current logo. Returns the image file (PNG or SVG) with correct MIME type |
| POST | /settings/branding/logo | Upload a custom logo. Multipart form with a file field (PNG or SVG, max 2 MB) |
| DELETE | /settings/branding/logo | Remove the custom logo and revert to the default mark |
| GET | /settings/system/packages/check | Check if a package is installed. Param: name (PyPI distribution name, e.g. beautifulsoup4) |
| POST | /settings/system/packages/install | Install a package and add it to the global allowlist. Body: name. Runs pip install inside the container. |
hierarchy.json
The full topology for a swarm. Written by the canvas; read and enforced by the runtime.
| Key | Type | Description |
|---|---|---|
swarm | string | Swarm slug (folder name) |
entry_point | string | Agent ID that receives incoming events |
agents | array | All agent nodes: id, layer |
edges | array | Agent-to-agent connections: from, to, kind, purpose |
skills | array | Skill attachments: agent, skill |
calls | array | Agent→Caller connections: agent, caller, purpose |
informs | array | Agent→Informer connections: agent, informer, purpose |
canvas_callers | array | Names of Caller nodes present on the canvas (may be unconnected) |
canvas_informers | array | Names of Informer nodes present on the canvas (may be unconnected) |
canvas_swarms | array | IDs of external swarm nodes present on the canvas (may be unconnected) |
swarm_calls | array | Declared cross-swarm delegations: agent, alias, swarm_id, purpose |
consultations | array | Reserved for future lateral peer consultation edges |
{
"swarm": "invoice-intake",
"entry_point": "policy-agent",
"agents": [
{ "id": "policy-agent", "layer": "policy" },
{ "id": "finance-orchestrator", "layer": "orchestrator" },
{ "id": "invoice-executor", "layer": "executioner" }
],
"edges": [
{
"from": "policy-agent",
"to": "finance-orchestrator",
"kind": "delegate",
"purpose": "route invoice work to finance orchestrator"
},
{
"from": "finance-orchestrator",
"to": "invoice-executor",
"kind": "delegate",
"purpose": "execute invoice parsing and classification"
}
],
"skills": [
{ "agent": "invoice-executor", "skill": "parse-invoice" }
],
"calls": [
{
"agent": "policy-agent",
"caller": "finance-approval",
"purpose": "request human approval for invoices above 10000"
}
],
"informs": [
{
"agent": "invoice-executor",
"informer": "ops-notifications",
"purpose": "notify ops team when an invoice is successfully processed"
}
],
"canvas_callers": ["finance-approval"],
"canvas_informers": ["ops-notifications"],
"canvas_swarms": ["hr-onboarding"],
"swarm_calls": [
{
"agent": "finance-orchestrator",
"alias": "hr_lookup",
"swarm_id": "hr-onboarding",
"purpose": "look up employee onboarding status for invoice vendor verification"
}
],
"consultations": []
}
Folder Structure
swarmwright/
├── app/
│ ├── models/ — SQLAlchemy ORM (12 tables, incl. chat_sessions, chat_messages, unmet_needs)
│ ├── api/ — Flask blueprints under /api/v1 (chat under /api/chat/)
│ │ ├── workspaces.py
│ │ ├── swarms.py
│ │ ├── agents.py
│ │ ├── topology.py
│ │ ├── events.py
│ │ ├── triggers.py
│ │ ├── runs.py
│ │ ├── callers.py — Caller/Informer nodes + inbox + informs
│ │ ├── knowledge.py
│ │ ├── skills_api.py
│ │ ├── files.py — Swarm file store endpoints
│ │ ├── stream.py — SSE event stream
│ │ ├── chat.py — Chat sessions, messages, unmet-needs endpoints
│ │ └── settings.py
│ ├── core/ — Business logic (no Flask imports)
│ │ ├── registry.py — filesystem scanner, hierarchy cache
│ │ ├── runtime.py — topology-enforced agent execution
│ │ ├── file_store.py — swarm file index management
│ │ ├── executor.py — skill subprocess runner
│ │ └── builtin_swarms.py — reconciliation pass for built-in swarm templates
│ ├── builtin_skills/ — Platform-provided skills (read-only)
│ │ ├── write_swarm_file.py / .yaml
│ │ ├── read_swarm_file.py / .yaml
│ │ ├── list_swarm_files.py / .yaml
│ │ ├── delete_swarm_file.py / .yaml
│ │ ├── http_get.py / .yaml
│ │ ├── http_post.py / .yaml
│ │ ├── kv_get.py / .yaml
│ │ ├── kv_set.py / .yaml
│ │ ├── get_datetime.py / .yaml
│ │ ├── send_webhook.py / .yaml
│ │ ├── create_workspace.py / .yaml — Operator chat
│ │ ├── create_swarm.py / .yaml — Operator chat
│ │ ├── draft_constitution.py / .yaml — Operator chat
│ │ ├── patch_topology.py / .yaml — Operator chat
│ │ ├── trigger_run.py / .yaml — Operator chat
│ │ ├── list_runs.py / .yaml — Operator chat
│ │ ├── read_run.py / .yaml — Operator + Concierge
│ │ ├── list_unmet_needs.py / .yaml — Operator chat
│ │ ├── list_workspace_swarms.py / .yaml — Concierge
│ │ └── flag_unmet_need.py / .yaml — Concierge
│ ├── builtin_swarms/ — Platform-shipped swarm templates
│ │ ├── operator-chat/ — platform-scope (materialised into platform workspace)
│ │ │ ├── meta.yaml
│ │ │ ├── hierarchy.json
│ │ │ └── agents/operator.md
│ │ └── concierge/ — workspace-scope (one per workspace)
│ │ ├── meta.yaml
│ │ ├── hierarchy.json
│ │ └── agents/concierge.md
│ └── static/ — Vanilla JS frontend (no build step)
│ ├── index.html
│ ├── js/
│ │ ├── app.js — router
│ │ ├── api.js — fetch wrapper
│ │ ├── sse.js — SSE client
│ │ ├── views/ — one file per tab
│ │ └── components/
│ │ └── chat-panel.js — shared chat panel for Operator and Concierge
│ └── css/
├── docker/ — Dockerfile, docker-compose.yml, entrypoint.sh
├── migrations/ — Alembic schema migrations
├── tests/ — pytest suite
└── data/ — Mounted volume (never committed to git)
├── swarm.db
├── .encryption_key
├── company/
│ ├── knowledge/
│ └── skills/
└── workspaces/
└── <workspace-slug>/
├── meta.yaml
├── knowledge/
├── skills/
└── swarms/
└── <swarm-slug>/
├── meta.yaml
├── hierarchy.json
├── agents/
│ └── <agent-id>.md
├── skills/
├── knowledge/
├── triggers/
└── files/ — swarm file store (unlimited nesting)
Glossary
| Term | Definition |
|---|---|
| Agent | LLM-powered component with a constitution and a layer |
| Caller | A blocking human-in-the-loop node — pauses the run for a decision |
| Constitution | Markdown file defining an agent's identity, role, and knowledge references |
| Control Room | The operational dashboard — organogram + run log + fire controls |
| Edge | Declared connection in hierarchy.json with kind (escalate/delegate/report) and purpose |
| Entry point | The agent that receives incoming events; the first node in execution |
| Human Action | A pending Caller request waiting for a human decision |
| Human Inform | A non-blocking notification from an Informer node |
| Informer | A non-blocking human-in-the-loop node — sends a notification and continues |
| Layer | One of four agent roles: Policy / Orchestrator / Executioner / Perceptionist |
| Perceptionist | Read-only grounding agent — maps external reality to internal data structures |
| Purpose | A required plain-English string explaining why a connection exists |
| Run | One execution of a swarm in response to a single event |
| Run step | One recorded action within a run (agent call, skill call, human interaction, violation) |
| Scope | company / workspace / swarm — the three levels for reusable resources |
| Skill | Python script callable by an Executioner agent, run as a subprocess with a declared timeout |
| Swarm | A coherent set of agents handling one class of work, with a declared topology |
| Topology | The declared graph in hierarchy.json — enforced at runtime with no exceptions |
| Topology violation | A blocked call to an undeclared target — logged as a run step, surfaced in the UI |
| Built-in skill | A platform-provided skill shipped with SwarmWright, visible in Library → Skills → Built-in; read-only but overridable at any scope |
| File Watcher | A trigger that fires when a file matching a glob pattern appears or changes in the swarm's files/ directory |
| Swarm Files | The per-swarm files/ directory — a shared, persistent filesystem accessible to both agents and humans, indexed in the database |
| Trigger | Deterministic script that produces events (heartbeat, listener, invocation, or file watcher) |
| Workspace | Department-like container for swarms; provides a resource scope |
| Alias (swarm call) | A short slug declared on a swarm-call edge that the calling agent uses in its invoke_swarm action to address the target swarm |
| Swarm call | A topology-declared, synchronous invocation of another swarm's entry point — the caller blocks until the target returns a result |
| Swarm node | A ⬡ canvas element representing an external swarm that can be delegated to; teal for same-workspace targets, amber for cross-workspace |
| Cross-workspace delegation | A swarm call whose target swarm lives in a different workspace; shown in amber with an ↗ badge as a deliberate inter-department coupling warning |
| Operator | A built-in platform-scope swarm that gives authorised users a conversational interface for designing and operating the platform — creating swarms, patching topology, triggering runs |
| Concierge | A built-in workspace-scope swarm, one per workspace, that routes natural-language user requests to the right swarm and records requests it cannot fulfil as unmet needs |
| Chat session | A persisted conversation thread between a user and a chat swarm (Operator or Concierge). One session exists per (user, scope). Wiping a session deletes message rows but not the underlying runs. |
| Unmet need | A request the Concierge could not route to any swarm, recorded in the unmet_needs table and surfaced to the Operator as an organisational feedback signal |
| Built-in swarm | A swarm template shipped with the platform in app/builtin_swarms/, materialised automatically at boot. Operator overrides are preserved; a "Restore to default" action re-materialises from the bundle. |