Synced from Hive. This page is pulled from kubestellar/hive@v4 during the docs build. Edit the canonical source in the Hive repository.
Agent Configuration
A hive agent is a long-running AI worker — a CLI session the hive keeps alive in tmux, kicks on a cadence with a work prompt, and watches for stalls, rate limits, and login expiry. An agent’s configuration is YAML entry under agents: in hive.yaml: it names the agent, picks the engine that powers it (a subscription CLI or a self-hosted inference endpoint), and declares how it behaves. Everything else — cadences, models, pins, ACMM level — layers on top.
Start with a name, a method, and a model. Add the rest when the agent needs it.
The smallest agent that works
agents:
scanner:
backend: copilot
model: claude-sonnet-4-6
For the portable agent definition YAML format, see ../AGENT-DEFINITION.md. For a complete portable AgentDefinition that exercises advanced display, channel, tool, and connection fields, see ../examples/agents/customized-agent.yaml.
That is a complete, valid agent. Defaults fill in the rest at load time:
enabled: true(unless you explicitly writeenabled: false)clear_on_kick: true— the session context is cleared before each kickidandroledefault to the agent’s name (scanner)bead_role: worker,beads_dir: /data/beads/scanner- Well-known names (
scanner,ci-maintainer,architect,supervisor,sec-check,quality,guide,strategist,outreach,telemetry,operations) also get a default emoji, color, aliases, and lane keywords, so a barescanner:entry already shows up in the dashboard as 🔍 with sensible triage keywords.
You almost never write a full roster by hand: applying an ACMM level (below) generates for you, and the dashboard edits it live.
Where configuration lives
Hive’s config is layered, and the layering is the point: a file’s location says who owns the setting.
/etc/hive/hive.yaml ← ConfigMap seed (Kubernetes) or bind mount (Docker, Podman, LXC).
│ The operator/platform layer. Re-seeded on every pod
│ boot; authoritative for acmm_level and hub.is_public.
├── /data/hive.yaml.dashboard ← Dashboard overlay on the PVC. Every save from the
│ dashboard UI lands here, secret-free, and is merged
│ over the ConfigMap seed at the next boot. Wins for
│ everything except the two ConfigMap-owned keys.
├── /data/agent-configs/ ← <name>.yaml per dashboard-managed agent
│ (created, imported, or generated by an ACMM pack).
│ Merged over the agents: map at load time.
├── /data/hive.yaml.runtime ← Persisted runtime config (was hive.yaml.bak). Never
│ edit. On K8s a post-merge snapshot the entrypoint
│ restores from if the seed is lost; outside Kubernetes the
│ boot-time source of truth. The legacy name is still
│ read as a fallback during the migration.
├── /data/secrets/ ← Secret VALUES written by the dashboard (writable PVC).
│ /secrets/ ← Secret VALUES from Kubernetes Secret mounts (read-only).
└── /data/policies/agents/ ← Kick templates and per-agent policy Markdown.
Read the tree top to bottom and you can answer “who set this?” for any value:
- The platform owns the seed. In Kubernetes, an init container re-copies the ConfigMap to
/etc/hive/hive.yamlon every boot. The entrypoint then merges the dashboard overlay over it — but the ConfigMap stays authoritative for the hub/admin-managed keys (acmm_level,hub.is_public). - You (via the dashboard) own the overlay. Every dashboard save writes
/data/hive.yaml.dashboard, so a LiteLLM endpoint or agent tweak survives pod restarts and upgrades. - Secrets never enter YAML.
hive.yamlstores env var names (api_key_env) and key file paths (api_key_file,governor.backup.key_file) — never key values. Values live in/data/secrets/(dashboard-entered) or/secrets/(Kubernetes Secret mounts). BecauseConfig.Save()writes the whole config back to disk, a key value in YAML would be persisted in plaintext — so the code refuses the pattern entirely.
Anatomy of an agent
Every field below exists in the config schema today. Grouped by what it does:
Identity — how the agent appears
agents:
scanner:
display_name: scanner # dashboard label (defaults to the YAML key)
description: "Triages issues and opens hold-gated fix PRs."
emoji: "🔍" # dashboard badge
color: "#3498db" # dashboard accent color
role: scanner # behavioral role; defaults to the agent name
sort_order: 20 # dashboard ordering (supervisors default to 0, others 100)
aliases: [sc] # short names accepted in dispatch/commands
Engine — what powers it
backend: copilot # the method: claude | copilot | goose | codex | pi |
# bob | aider, or an inference backend:
# vllm | llm-d | litellm | watsonx | named gateway
model: claude-sonnet-4-6 # model id for that method
cli_pinned: true # pin the CLI so nothing auto-switches it
launch_cmd: "/usr/bin/copilot --allow-all --model claude-sonnet-4-6"
# explicit launch command (optional — hive builds
# from backend + model + mode when omitted)
The dashboard also offers gemini as a live method (with live model discovery); as a persisted
backend:value inhive.yaml, stick to the validated list above.
Behavior — what it may do, and when
enabled: true # default true; set false to keep it configured but off
mode: ISSUES_AND_PRS # GitHub interaction tier: ADVISORY | ISSUES_ONLY |
# ISSUES_AND_PRS | ISSUES_PRS_MERGE
converse: true # let this agent comment on issues/PRs and leave PR
# reviews, INDEPENDENTLY of mode. Off by default.
# See "Conversation is not a tier" below.
bead_role: worker # worker | supervisor (supervisors sort first,
# monitor the others); default worker
kick_template: scanner-holdgated.md
# named work-prompt template in the policies dir
include_repos: true # append the project repo list to each kick (default true).
# Prompt text — it authorizes repos, it does
# NOT clone, mount, or provision anything on disk.: false # true = never kicked by the governor timer;
# triggered explicitly (e.g. inception)
clear_on_kick: true # default true; false keeps session context across kicks
stale_timeout: 28800 # seconds of silence before the agent counts as stale —
# must exceed its longest cadence
restart_strategy: immediate # how to bring a dead session back
beads_dir: /data/beads/scanner # work-record (bead) storage; default per-agent
replicas: 3 # materialize scanner, scanner-2, scanner-3 (max 5)
lane_keywords: [bug, triage, fix] # routes matching issues into this agent's lane
detect_keywords: [scanner, triage] # attributes GitHub activity back to this agent
Conversation is not a tier
mode is a ladder: each rung is a strict superset of the below, from
“observe” up to “merge on green CI”. converse is not on that ladder. It
grants exactly two things — posting a comment on an issue or PR, and leaving a
PR review — and nothing else moves.
It exists because those two operations had nowhere sensible to sit
(#4492). Commenting was
bundled with ISSUES_ONLY, alongside creating issues, editing issue bodies and
relabelling; leaving a PR review was bundled with ISSUES_AND_PRS, alongside
pushing branches. Both bundles are wrong in both directions:
- An ADVISORY agent that spots something on a thread could not reply. It could emit a bead nobody outside the hive ever sees.
- Letting it reply meant promoting it to
ISSUES_ONLY, which also handed it the ability to rewrite issue bodies and relabel — and a reviewer who wanted comment-only had no way to ask for it.
With converse those are separable:
| What you want | Configuration |
|---|---|
| An agent that observes and can reply, but files and edits nothing | mode: ADVISORY + converse: true |
| An agent that files issues but never speaks on a thread | mode: ISSUES_ONLY (the default — converse is off) |
| A merge-capable agent that also reviews at ADVISORY-level trust | not expressible; reviews come with ISSUES_AND_PRS anyway |
It ever widens. converse is checked beside the mode tier, not
instead of it, so an agent already at a tier that permits an operation keeps it.
Turning converse on can never take anything away, and it cannot reach anything
the tier ladder does not already gate: issue creation, editing, relabelling,
pushing, opening a PR and merging all stay exactly where they were. The
hard-denied routes (direct PR creation, direct merge) are unreachable by any
capability at all.
It is off everywhere by default, at every ACMM level, so a hive that does
not mention it behaves exactly as it did. The way an agent starts talking
is an operator writing converse: true.
Enforcement is the MITM proxy, over both REST and GraphQL — which matters,
because gh issue comment and gh pr review send GraphQL, not REST. On the
GraphQL side the grant is evaluated over the whole document: a mutation that
comments and edits an issue, or comments and merges, is not conversation and
is refused at the tier the non-conversational half requires.
For prompt file resolution and the complete built-in ${VAR} reference, see
Policy and prompt templates.
Declarative extensions
Three optional blocks replace hardcoded behavior with declarations:
channels: # how the agent gets triggered. Omit = governor timer.
- type: kick # kick | webhook | discord | schedule | bead
- type: webhook
events: ["issues.opened", "issues.labeled"]
repos: [repo-one] # optional repo-name filter
- type: bead
match: { nudge_target: scanner }
- type: schedule
schedule: "0 */4 * * *" # cron; required for type: schedule
tools: # tool permissions. Omit = the mode field governs.
preset: issues-only # advisory | issues-only | issues-prs | full
rules: # per-tool allow/deny overrides on top of the preset
- pattern: "mcp__github__create_issue"
action: allow
reason: "advisory issues are fine"
connections: # external integrations
- name: github-mcp
type: mcp # mcp | api | knowledge
uri: "stdio:///usr/local/bin/mcp-github"
Presets map modes (advisory denies issue and PR creation, issues-only denies PRs, issues-prs and full deny nothing), and an explicit allow rule overrides a preset deny.
Replicated agents
Set replicas: N on a declared agent to run a small pool with the same prompt, backend, model, mode, channel, tool, and metadata settings. N defaults to 1 and is capped at 5; config load fails if it is outside 1..5. Hive materializes derived names as -2, -3, … (scanner, scanner-2, scanner-3). Do not declare those derived names yourself: a real scanner-2 entry collides with scanner: { replicas: 2 } and is rejected. Derived replicas get their own IDs and bead directories (/data/beads/scanner-2) but inherit the base agent’s kick template/prompt selection. Runtime-derived replicas are stripped before saving and recreated on the next load.
Trigger channels
channels: declares non-default ways to wake an agent. If the block is omitted, governor timer kicks still work. If you include the block, add type: kick when the agent should keep normal governor kicks alongside other triggers.
| Type | Required fields | Behavior |
|---|---|---|
kick | none | Keep ordinary governor timer kicks. |
webhook | events | /webhook/GitHub webhook receiver matches X-GitHub-Event or event.action strings such as issues.opened; optional repos filters by repository name. HIVE_WEBHOOK_SECRET is required and every request must include a valid GitHub X-Hub-Signature-256 HMAC. Missing configuration or invalid signatures fail closed with 401. |
bead | match | The bead watcher polls the agent’s beads_dir about every 30 seconds and kicks when an individual JSON file has every key: value in match at the top level. Current watcher matching is not the nested metadata map inside the bd ledger file. |
schedule | schedule | Cron-style channel trigger independent of governor-mode cadences. |
discord | patterns | Declared shape for Discord-triggered work; patterns are validated by config load. |
Rounding out the schema — fields you will rarely touch:
| Field | What it does | Default |
|---|---|---|
id | Stable identifier | agent name |
acmm_levels | ACMM levels this agent participates in | all |
caveman_mode | Prompt-compression experiment: lite, full, ultra, wenyan; see below | off |
explain_mode | Ask the agent to report why it made each tool call: off, brief, full; see below | inherit the hive default |
metrics_collector | Named metrics source for the stats panel | none |
stats_display | Custom sidebar metrics (key, label, source, field, style) | none |
hidden (packs) | Keep a pack agent out of the default roster view | false |
Explain mode (debugging agent behaviour)
Agents are told to act, not narrate. Every policy carries an “Output Rules — Terse Mode” block, and on inference backends the agent manager appends an explicit EXECUTE, DO NOT NARRATE instruction to each kick. That rule earns its keep — weak models otherwise answer a kick with a plan for someone else to run instead of running it — but it also means that when an agent does the wrong thing, there is nothing in the log saying why.
explain_mode buys that visibility back for agent at a time, without relaxing the rule for anything else.
| Mode | What the agent is asked to add | Cost |
|---|---|---|
off | Nothing. Identical to the behaviour before this option existed. | none |
brief | EXPLAIN: line before each tool call, giving the reason for that specific call. | small, per tool call |
full | brief, plus a closing EXPLAIN: block: the goal as understood, the approach chosen, alternatives rejected and why, and what evidence would have changed the decision. | larger, per kick |
agents:
scanner:
backend: claude
explain_mode: brief
What it does and does not change
- The agent still acts. The instruction states that tool execution remains the requirement and that a response containing explanation is a failure. It is appended after the
EXECUTE, DO NOT NARRATEblock, so it reads as a qualification of that rule rather than a replacement for it. - Terse mode is suspended on
EXPLAIN:lines. A caveman-compressed explanation would be useless to the human reading it, but the agent’s real output — log lines, bead titles, PR descriptions — keeps whatever compression you configured. - It is per-kick, not a prompt edit. Nothing in
src/policies/orexamples/*/agents/*.mdchanges, so toggling it does not alter any agent’s actual instructions and does not require a redeploy.
Reading the explanation
Explanation lands in the agent’s ordinary log, tagged with the EXPLAIN: prefix. Agent logs are tmux pane scrapes, so there is no second channel to write to — but the prefix makes the split a read-time choice:
| URL | Shows |
|---|---|
/api/agents/<name>/log | The log as always: work and explanation interleaved. |
/api/agents/<name>/log?explain=only | Just the reasoning. |
/api/agents/<name>/log?explain=hide | The log as it would read with explanation off. |
grep EXPLAIN: works the same way on a downloaded log.
Fleet-wide default
To turn explanation on everywhere without editing each agent, set the hive-wide default. It lives in governor config, so it is settable from the dashboard:
Dashboard — Settings → Governor → General → Default explain mode. The field also reports which mode is in force right now and where it came from, and takes effect on the next kick; no restart.
hive.yaml
governor:
explain_mode: brief # off | brief | full — omit for "no hive default"
Environment — HIVE_EXPLAIN_MODE=brief on the deployment still works, as the fallback consulted when governor.explain_mode is unset. Prefer the config field: the env var is set on the deployment, which a hosted hive’s owner has no access to.
governor.explain_mode | HIVE_EXPLAIN_MODE | Hive default |
|---|---|---|
| unset | unset | off |
| unset | full | full |
brief | full | brief — config wins |
off | full | off — an explicit off in config is a choice, not “unset” |
The per-agent field is a tri-state, and the difference matters:
explain_mode | With a hive default of full | Meaning |
|---|---|---|
| unset | full | Inherit the hive default. |
off | off | Explicit opt-out; a fleet-wide default does not override it. |
brief | brief | Explicit per-agent choice wins. |
An unrecognized value in any of these places resolves to off, so a typo degrades to the previous behaviour rather than to a mode nobody asked for. Hive injects the resolved mode into each agent process as HIVE_EXPLAIN_MODE, so an agent’s own skills and scripts can branch on it without re-deriving the precedence rules.
Leave it off outside of debugging: the explanation is extra output tokens on every kick.
Caveman prompt compression
caveman_mode installs the upstream JuliusBrussee/caveman skill/proxy for supported backends before an agent starts. It is optional and experimental; leave it empty for maximum output fidelity.
| Mode | Dashboard description | When to use |
|---|---|---|
lite | Removes filler while preserving normal language. | Lowest-risk token reduction for routine agents. |
full | Converts output toward terse “caveman-speak”. | Default example mode when cost matters and operators accept rougher prose. |
ultra | Telegraphic compression. | High-volume lanes where compact summaries are more important than nuance. |
wenyan | Classical Chinese-style compression. | Specialized/experimental mode; use when readers and downstream tools can tolerate it. |
Implementation notes:
- Config validation accepts
lite,full,ultra,wenyan, or empty. claude,copilot, andgeminiare auto-wired before first message.goose,codex, andaiderget the skill installed and then receive/caveman <mode>after the CLI reaches an input prompt.- Unsupported backends log that caveman is not supported and continue without compression.
- The UI describes the feature as roughly 65% output reduction, but exact savings vary by prompt, backend, and task.
Methods: subscription CLIs vs self-hosted inference
backend: picks of two families. They differ in how you authenticate and where model lists come from:
| Method | Family | Auth | Model discovery |
|---|---|---|---|
claude | CLI | log in per method | maintained list (Anthropic exposes no “list my models” API) |
copilot | CLI | log in per method (GitHub device flow) | live — entitlement-filtered /models on your plan’s API host |
gemini | CLI | API key (GEMINI_API_KEY) | live — Google models API, filtered to generateContent |
goose | CLI | provider-configured (GOOSE_PROVIDER) | curated per-provider list |
vllm | inference | endpoint + optional key — no login | live — /v1/models |
llm-d | inference | endpoint + optional key — no login | live — /v1/models |
litellm | inference | endpoint + API key — no login | live — /v1/models, entitlement-filtered per key |
openrouter (gateway name) | inference | Model Gateway key or scan-to-fund flow — no CLI login | live — OpenRouter /v1/models, plus curated fallback |
Two rules of thumb:
- CLI methods are subscriptions. You log in per method from the dashboard, and every agent using that method shares the login. For
claude, sharing is not instantaneous: the OAuth token is shared immediately through the per-agent home bridge, while the session identity (~/.claude.json, which is what decides whether the CLI shows a login menu) is adopted from an already-signed-in agent the next time each other agent launches or is restarted. So on a fresh install, expect the remaining agents to clear their 🔑 badge on their next start rather than the moment you finish logging in. - Inference methods are endpoints. You configure a base URL and a key reference (env var name or key-file path — the value goes in
/data/secrets/, never in YAML). Agents onvllm/llm-d/litellmlaunch the Claude CLI routed through hive’s inference translator, so there is no separate login.POST /v1/messagesis translated into an OpenAI/v1/chat/completionscall. The Claude CLI also talks to its Anthropic host for housekeeping — telemetry batches (/api/event_logging/...), error reports,POST /v1/messages/count_tokens— and none of that has a meaning to an OpenAI-compatible gateway; forwarding it used to cost a gateway400 Missing required parameter: 'messages'per call, charged against the provider’s request rate limit (roughly two failures per real completion in practice). The translator and the MITM reroute now answer those locally:count_tokensreturns a chars-based estimate, anything under/api/returns{}, and any other path is a 404 in Anthropic error shape with aWARNlog line naming the method and path, so a new CLI endpoint shows up in the hive log rather than as gateway noise. Inference-routedclaudesessions are additionally launched withDISABLE_TELEMETRY=1,DISABLE_ERROR_REPORTING=1, andCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1; subscription sessions are not.
Every Model Gateway (and the bob backend) also accepts an optional key_name — a human-chosen LABEL for the configured key, e.g. key_name: openrouter-prod-key. It is safe-to-show metadata, not a secret: the dashboard’s gateway row displays it as “Using key: <name>”, or “(unnamed)” when no label is set, so operators can tell keys apart without ever seeing the value. See inference-backends.md for a full YAML example.
Kubernetes manifests for deploying inference backends (vllm Deployment, EPP RBAC, kustomization) are in deploy/inference/.
Agents can inspect peer panes with hive-panes [lines] when the deployment includes src/deploy/hive-panes.sh. It reads pluk JSONL logs from /var/run/pluk/logs, skips the calling agent named by HIVE_PROXY_AGENT, strips terminal escapes, and prints the last N raw-output lines for every other agent. Use it for situational awareness; it is read-only and does not attach to another agent’s tmux session. See Agent peer-awareness logging for the pluk log format, when it’s available, and its retention behavior.
Every discovery probe is best-effort: a failed or absent probe falls back to a current static list, so a model dropdown is never empty. Fallback entries are marked “unverified” in the UI.
Model pinning and auto-switching
Each agent shows a 📌 pin on its CLI and its model in the dashboard. The semantics are deliberately narrow — a model changes out from under you in exactly two cases:
- Unpinned + governor. The governor may auto-select a different model (budget, mode). A pin blocks this — and only this. Your own explicit switch always goes through; it simply retargets the pin to the new model, so the agent stays pinned.
- Discovery says the model is gone. When a genuine (non-fallback) discovery returns a model set that no longer contains an agent’s selected model — a key swap or endpoint change stripped the entitlement — the agent is switched to the first available model, with a toast. A static-fallback list never triggers this, and a model that is still present is never re-selected.
Pin a model when reproducibility matters more than the governor’s budget optimizations. Leave it unpinned when you want the hive to manage cost for you.
Cadences and the governor
Agents don’t schedule themselves. The governor evaluates the work queue every eval_interval_s (default 300s), computes a mode from queue depth — idle → quiet → busy → surge (default thresholds: quiet > 2, busy > 10, surge > 20 per watched repo; override with threshold:) — and kicks each agent on the cadence that mode assigns it:
governor:
eval_interval_s: 300
modes:
busy:
threshold: 16 # queue depth that activates this mode
supervisor: 5m # per-agent kick interval in this mode
scanner: 15m
reviewer:
times: ["09:00", "17:00"]
days: ["mon", "tue", "wed", "thu", "fri"]
tz: America/New_York
release:
cron: "30 9 * * 1-5"
tz: America/New_York
ci-maintainer: 1h
architect: pause # "pause" stops kicks for this agent in this mode
- Thresholds scale with repo count. The default thresholds above are per-repo bases, multiplied by
len(project.repos)— sosurgeis 20 on a 1-repo hive and 780 on a 39-repo, and the mode ladder means the same thing at any hive size. Athreshold:you set yourself is used verbatim and never scaled. Tune the curve withgovernor.threshold_scaling(lineardefault,sqrt,none). See Governor mode thresholds, which also covers the ACMM-pack interaction. - Mutually exclusive modes. Each per-agent, per-mode cadence is either an interval (
5m,2h,pause) or a time-of-day schedule — never both. Config load and API writes reject mixed forms with a 400/error. - Time-of-day schedules. Use
times: ["HH:MM"]with optionaldays(mon…sun) and a required IANAtz. The timezone is stored explicitly and displayed with the schedule; it does not float with the viewer. - Advanced cron. Power users can provide a constrained five-field cron expression plus
tz. Hive evaluates these with robfig/cron and schedule-local timezone handling. - Governor-mode semantics. Time-of-day schedules fire at their exact wall-clock times. Governor mode selects whether that mode’s schedule is active; quiet/busy/surge multipliers do not scale exact times.
pause/off, paused agents, on-demand agents, non-kick channels, and budget gates still suppress kicks. - Downtime catch-up. If Hive was down at a scheduled time, restart/eval grants at most catch-up kick when the missed occurrence is within the last 10 minutes. Older missed occurrences are skipped and the next future occurrence is used.
- Per-agent, per-mode intervals. Anything Go’s duration parser accepts works (
5m,2h) for interval mode. - Pausing. The value
pause(orpaused) suspends an agent for that mode without disabling it. - On-demand agents (
on_demand: true) are skipped by the governor timer entirely — they run when explicitly triggered (the inception workflow drivesbrainstormthis way). - Budget. When the weekly token budget is exhausted, kicks are suppressed hive-wide (exempt agents excepted) until the period rolls over.
Set stale_timeout with your cadences in mind: an agent kicked every 4h with a 30-minute stale timeout will look dead between kicks. The shipped packs use “longest cadence × 2”.
Cadences are not the way an agent gets kicked: the additive triggers:
config key declares CEL rules that kick a named agent directly off a
source-control event (issue opened, PR opened, a label applied, a comment
posted), independent of the governor’s queue-depth cadence. See
CEL-based agent triggers.
ACMM levels: agent rosters as packs
You don’t have to design a roster. Hive ships six ACMM packs (level-1.yaml … level-6.yaml, embedded in the binary and forkable) that pair a curated agent roster with governor cadences and a merge policy:
| Level | Name | Posture |
|---|---|---|
| L1 | Inception (Assisted) | inception: brainstorm + guide, everything conversational |
| L2 | Advisory (Instructed) | advisory beads; agents observe, humans act |
| L3 | Quality-Gated (Measured) | quality opens issues and hold-gated test PRs; the rest stay advisory |
| L4 | Security-Aware (Adaptive) | all agents open issues — no PRs yet |
| L5 | Semi-Autonomous (Semi-Automated) | issues and hold-gated PRs; humans batch-approve |
| L6 | Fully Autonomous | auto-merge on green CI, no hold label |
Applying a level reconciles the whole roster, not just the diff: missing agents are created (as overlay files in /data/agent-configs/), existing agents are merged — pack values fill blanks, but your explicit backend:, model:, and enabled: false always win — and the level’s kick_template and mode are updated so the agent’s policy matches the level. A failed agent doesn’t abort the rest; the level is recorded as cleanly applied when every agent reconciled.
The L5 roster is the canonical worked example — eleven agents, eight on the governor timer, two opt-in agents paused in every governor mode, plus on demand:
| Agent | Mode | Cadence (all governor modes) | |
|---|---|---|---|
| supervisor 👑 | health monitor, sweeps | ADVISORY | 5m |
| scanner 🔍 | triage + fix PRs | ISSUES_AND_PRS | 4h |
| ci-maintainer 🔧 | CI + dependencies | ISSUES_AND_PRS | 4h |
| quality 🧪 | test coverage | ISSUES_AND_PRS | 2h |
| guide 🧭 | documentation | ISSUES_AND_PRS | 4h |
| sec-check 🛡 | CVEs, vulnerabilities | ISSUES_AND_PRS | 4h |
| architect 🏗 | RFCs, refactors | ISSUES_AND_PRS | 4h |
| strategist 🧠 | cross-agent coordination | ISSUES_AND_PRS | 4h |
| telemetry 📡 | managed-project instrumentation | ISSUES_AND_PRS | paused |
| operations 🚨 | managed-project operational practice | ISSUES_AND_PRS | paused |
| brainstorm 💡 | ideation | ADVISORY | on demand |
At L5, every agent PR gets a hold label automatically. The system proposes; it does not merge autonomously.
Telemetry and operations are L5/L6-only agents. They stay absent below L5 and remain paused at L5/L6 until an operator deliberately opts in. Their lane keywords are disjoint: telemetry owns instrumentation and observability terms, while operations owns health, SLO, runbook, incident, rollback, and alerting terms.
Configure that opt-in under Settings → Project Observability. This tab is
for the managed project’s target stack; the Features tab separately controls
Hive’s own OpenTelemetry export. Selecting platforms persists them under
governor.project_observability, and enabling an agent replaces its all-mode
paused cadence with a conservative 24h interval (which can then be tuned from
the agent’s Cadences tab).
governor:
project_observability:
open_source: [opentelemetry, prometheus, grafana]
kube_native: [servicemonitor]
commercial: [honeycomb]
references:
honeycomb:
endpoint_env: OTEL_EXPORTER_OTLP_ENDPOINT
credential_secret: observability/honeycomb-key
Reference fields accept names: an environment-variable name or a
secret-name/key reference. Literal endpoints, tokens, and API keys are
rejected. With no selected backend, the policies fail closed: agents may detect
the existing stack and report recommendations, but may not add an exporter or
new external data flow.
After an initial telemetry advisory run, platforms mentioned in its findings are preselected as suggestions in the tab. They remain unsaved until an operator reviews them and clicks Save; after that, the persisted declaration governs future telemetry and operations work.
Live-linked agent definitions (definition_source)
An agent can be linked to a whole portable AgentDefinition YAML file living in a GitHub repo, so an edit to that file propagates into the agent’s config on the next reload or startup — no redeploy, no dashboard edit. This is the whole-agent analogue of promptTemplate/prompt-source live-linking (see Policy and prompt templates); both are implemented as graceful-fallback resolvers in src/pkg/promptsrc and src/pkg/defsrc respectively.
agents:
scanner:
backend: copilot
model: claude-sonnet-4-6
definition_source:
type: github # "github" is supported; defaults to it when repo is set
owner: my-org
repo: agent-definitions
path: scanner.yaml
ref: main # optional; branch/tag/SHA — omit for the default branch
definition_source is DefinitionSourceConfig (src/pkg/config/config.go:469), a field on AgentConfig (config.go:908). owner, repo, and path are required for the source to be considered set (IsSet(), config.go:492); ref is optional and falls back to the repo’s default branch. url is a fifth, informational-only field the dashboard import UI uses to round-trip the pasted github.com blob URL — it plays no part in fetching.
What it does
On startup and on every config reload, defsrc.ApplyToConfig (src/pkg/defsrc/defsrc.go:416) walks every agent that has definition_source set, fetches the file’s content from GitHub, and merges the parsed AgentDefinition’s operator-safe fields over the agent’s baked config in place. It is wired at two call sites in src/cmd/hive/main.go:
- Startup, line 1294 — applied before the first kick, so a repo edit made while the hive was down is already reflected.
- Config reload, line 3009 — re-applied on every reload, before
initAgentConfigDrivenSystems, so downstream systems see the merged config.
Both call sites build the same defsrc.Resolver (main.go:1287), gated by func(slug string) bool { return cfg.GitHubDefinitionAllowed(slug) } (main.go:1289).
What fields the live definition can change
mergeAllowedFields (defsrc.go:170) is an explicit allow-list, not a deny-list: fields it names are copied from the fetched AgentDefinition the baked AgentConfig. A field not in this list — including any privilege- or security-relevant field added to AgentConfig later — is preserved from the baked config by construction; a new field is safe-by-default rather than accidentally live-sourced.
AgentDefinition field (YAML tag) | Applied to AgentConfig field |
|---|---|
metadata.displayName | DisplayName |
metadata.description | Description |
metadata.emoji | Emoji |
metadata.color | Color |
spec.backend | Backend |
spec.model | Model |
spec.role | Role |
spec.mode | Mode |
spec.sortOrder | SortOrder |
spec.beadRole | BeadRole |
spec.staleTimeout | StaleTimeout |
spec.restartStrategy | RestartStrategy |
spec.clearOnKick | ClearOnKick |
spec.includeRepos | IncludeRepos |
spec.laneKeywords | LaneKeywords |
spec.detectKeywords | DetectKeywords |
spec.aliases | Aliases |
spec.cadences | Cadences |
spec.promptTemplate | KickTemplate/prompt template |
spec.channels | Channels |
spec.tools | Tools |
spec.connections | Connections |
Two merge rules to know before you rely on this:
- A blank field never clears a baked value. For most fields, an empty string or empty slice in the fetched definition is skipped, so a minimal definition can’t silently wipe presentation you set elsewhere.
ClearOnKickandIncludeReposare the deliberate exceptions — their zero value (false) is a legitimate setting, so the definition’s value is taken as authoritative whenever the source resolves live (defsrc.go:212-216). - Everything else on the agent is preserved untouched, explicitly including:
Enabled/Paused/Managed(operator lifecycle state),ID,BeadsDir,MetricsCollector,ACMMLevels,OnDemand,CavemanMode, and — critically — thedefinition_source/prompt_sourcepointers themselves. A live definition cannot re-point the agent at a different repo (ApplyToConfigre-asserts this atdefsrc.go:437-440even though the merge already excludes it). Nothing under the hive-levelvariables.securityblock is reachable either — it isn’t part ofAgentConfigat all.
The trust boundary: allowlisted repos are seed-only
definition_source is gated by Config.GitHubDefinitionAllowed(slug) (config.go:4162), which simply delegates to Config.GitHubPromptAllowed(slug) (config.go:4142) — the same seed-only gate used by prompt_source. Fetching requires both:
variables:
security:
allow_github_prompt: true # default false (deny)
github_prompt_allowlist:
- my-org/agent-definitions # exact "owner/repo" slugs
This is the property operators most need to understand before enabling the feature: variables.security is honored from the trusted config seed. LoadWithDashboardOverlay never merges the dashboard overlay’s Variables block (config.go:397-399, config.go:4157-4161), so:
- A dashboard save cannot turn
allow_github_prompton if the seed has it off. - A dashboard save cannot add a repo slug to
github_prompt_allowlist. - A compromised or malicious dashboard overlay can neither widen the set of readable repos nor repoint an agent’s
definition_sourceat an arbitrary repo — a seed edit (ConfigMap in Kubernetes, bind-mounted file under Docker, Podman or LXC) can do either.
An empty allowlist denies every repo even with allow_github_prompt: true — the allowlist is required, not merely advisory.
Fetch failures never blank an agent
Resolve (defsrc.go:283) never propagates an error to the caller: a reload must proceed even when GitHub is unreachable. On any failure it falls back, in order:
- Denied (not allowlisted) — logs a warning, keeps the baked config,
Source: "denied". - No fetcher (token-mode boot without a GitHub App client) — uses the last-known-good cached document if exists, else keeps baked,
Source: "no-client". - Fetch error (timeout, network, 404, etc.) — falls back to the last-known-good cached document if exists, else keeps baked,
Source: "error". Fetches are bounded to 8 seconds (defaultFetchTimeout,defsrc.go:47) so a hung GitHub call cannot stall a reload. - Malformed document (bad YAML, wrong
kind, missingmetadata.name) — keeps baked,Source: "error". A document is cached for fallback after it parses cleanly (defsrc.go:325-329), so a later failure never falls back to a corrupt document.
A fetched file is capped at 512 KiB (maxDefinitionBytes, defsrc.go:43); an oversized file is truncated (and likely then fails to parse) rather than consuming unbounded memory.
Validating a source before saving it
defsrc.FetchOnce (defsrc.go:372) does a single gated fetch — bypassing the resolver’s cache — and returns a parse error to the caller. This is what the dashboard’s import/“keep linked” flow uses to surface a bad owner/repo/path/document to the operator at save time, rather than discovering the problem silently on the next reload.
Format reference
The fetched file must be a valid portable AgentDefinition: kind: AgentDefinition and a non-empty metadata.name are required (ParseDefinition, defsrc.go:138); everything else is optional. For the full schema and a worked example, see ../AGENT-DEFINITION.md and ../examples/agents/customized-agent.yaml.
Kick templates: what an agent is told to do
kick_template names a Markdown file resolved from the hive’s policies checkout (/data/policies/examples/kubestellar/agents/, or the directory your policies: config points at), falling back to the defaults embedded in the binary (src/pkg/policies/defaults/). It is the agent’s periodic work prompt: on every kick, the template is loaded, variables like ${ISSUE_LIST}, ${PR_LIST}, ${AGENT_NAME}, ${PROJECT_ORG}, and ${KNOWLEDGE} are substituted, and the result is dispatched to the agent’s session.
Resolution order: the agent’s explicit kick_template wins; otherwise the ACMM pack’s template for that agent at the current level; otherwise convention — /data/agents/<name>/CLAUDE.md, then <name>.md in the policies checkout, then the embedded default. Pack templates carry the level’s policy in their names — scanner-holdgated.md is scanner-at-L5; the same scanner at L6 gets scanner-automerge.md.
Portable agents bundle everything — config plus a promptTemplate — in a single AgentDefinition YAML you can import from a URL in the dashboard. The reference schema is ../AGENT-DEFINITION.md, and a worked example lives at ../examples/agents/customized-agent.yaml.
Label policy: which issues agents may work
There is exactly one label-policy surface for the hive’s own agents — the Governor Configuration → Labels tab — and it has two polarities:
| Polarity | Config | Meaning |
|---|---|---|
| Exempt (deny-list) | governor.labels.exempt (+ permanent hold/on-hold/hold/review, do-not-merge) | “Never touch issues labeled with these.” Everything else is eligible. This has always existed. |
| Required (allow-list) | project.issue_filter.require_labels | “Only touch issues labeled with these.” Empty = every issue is eligible. This is what “only work approved issues” means. |
By default a hive treats every open issue in its repos as candidate work. Projects that gate automation on a maintainer’s explicit approval label want the require polarity: a maintainer reviews an issue, applies the approval/queue label, and then may agents touch it. (A fleet running against a busy upstream repo hit exactly this — the hive opened a PR for an issue the owner had not yet labeled for agent work; an exempt list cannot express that policy, because it can name what to avoid, not demand a label be present.)
project:
org: my-org
repos: [my-org/common]
issue_filter:
require_labels: [approved-for-agents] # agents may work these issues
Semantics:
- Absent/empty
require_labels= no gate — existing hives are unchanged; there is no default-on filtering. - An issue must carry at least required label to be eligible. Matching is case-insensitive and exact (a prefix like
approved-for-agents-maybedoes not satisfyapproved-for-agents— prefix matching would over-admit through an approval gate). - Exempt wins on conflict: an issue carrying both an exempt label and a required label stays excluded. There is deliberately no separate
exclude_labelsfield — the exempt list is the exclusion mechanism, applied first. - PRs and the Hold list are unaffected: open PRs are in-flight work, and held issues still appear under On Hold.
Both polarities are enforced at enumeration — the point where GitHub issues become the hive’s actionable set — not in the prompt. A filtered issue never enters the queue, never appears in a kick, never triggers plan-from-label, and cannot be re-selected by a confused (or prompt-injected) agent re-listing the repo. Kick prompts additionally state the active require policy so agents know the list is intentionally short. Both lists are edited on the Labels tab; an active require gate is also noted read-only under Repositories, and hub-managed hives can receive issue_filter with their project config over the heartbeat.
Not the same thing as the contribute filters. hub.contribute_labels_mode + its label list gate which issues are handed out to external contributors over /contribute — they have never gated the hive’s own agents, so an operator who allow-listed a queue label there (a common setup for routing labeled issues to contributors) still had a hive whose own scanner could work every other open issue. project.issue_filter.require_labels is the agent-side gate; configure both if you want the same label to govern both lanes.
When to add what
| Add… | …when |
|---|---|
| nothing (name + method + model) | you’re starting. Defaults are production defaults. |
display_name, emoji, color | the dashboard roster grows past a handful and you want it scannable |
| a model pin 📌 | you need reproducible behavior and the governor keeps optimizing your model away |
cli_pinned: true | a CLI update broke an agent and you never want that surprise again |
on_demand: true | the agent should run when something (you, inception) explicitly fires it |
| cadence overrides | the pack’s rhythm doesn’t match your project — a hot repo may want scanner: 15m, a quiet 4h |
pause in a mode | an agent is noisy exactly when the queue is deep (e.g. pause architect during surge) |
stale_timeout | you changed cadences — keep it above the longest interval |
clear_on_kick: false | the agent genuinely benefits from remembering previous kicks (rare; context bloat is real) |
channels | governor timer kicks aren’t enough — you want webhook-, schedule-, or bead-triggered work |
tools rules | agent needs a sharper permission edge than its mode tier provides |
| a higher ACMM level | your review capacity, CI trust, and appetite for autonomy have all grown — raise the level and let the pack reconcile the roster |
| an inference method | you have GPUs (or a LiteLLM gateway) and want agents off subscription seats |
What to read next
- Supervisor agent — what the supervisor does, how it differs from the governor, when to enable it,
bead_rolesemantics, and policy modes. - Documentation index — what hive is, setup, and the full topic-guide surface.
- Architecture — process model, deterministic pipeline, governor loop, guardrails, and hub/spoke design.
- Portable AgentDefinition format — standalone YAML schema for agent imports, exports, and overlays.
- AGENTS.md repo instructions — the per-repo instruction file format Hive’s parser understands. Injected into kicks
project.checkouts_dirgives Hive a checkout to read it from — see the page. - Dashboard route and health checks — listener probes and alert behavior for stuck sessions and restart loops.
- Troubleshooting — stuck sessions, login expiry, restart loops, and notification checks.
- ACMM policy matrix — the full per-level, per-agent policy table.
- Config layering — precedence for seed, dashboard overlay, agent overlays, and runtime snapshots.
- Cross-cluster migration — the manual procedure for moving a hive (and its PVC state) between clusters.
- Dashboard OpenAPI spec — REST endpoints used by the dashboard and integrations.
- SQLite state backend example — single-machine alternative to beads for state queries.
- ACMM policy fragments — per-level ACMM policy reference fragments.