Per DESIGN-a2a-agents.md v2.1 §2, §5, §5b: an agent is a persona, not a queue. Sibling of kb#133's model-registry.yaml, same load/get/CLI shape. - agent-registry.yaml: populates adolf (trusted, scoped core tools), claude-coder (trusted, implementer), torgash (sandboxed, marketplace- only, target state - not yet built), researcher (sandboxed/untrusted- input, target state), kimi-endpoint/local-small-endpoint (trivial model-agents from #133), and alvis/elizaveta as first-class human agents with trust=human, own inboxes, own private memory bank targets (adolf-alvis/adolf-elizaveta/adolf-shared - split itself is kb#153). Also carries trust_classes with numeric rank, a runtimes: section for Claude Code (kb#133 explicitly excluded it from model-registry.yaml), and memory_bank_policy documenting §5b's hard rules. - agent_registry.py: effective_card() is what makes "backbone swap is one field" true rather than aspirational - tier/cost_class/lifecycle are never stored statically on an agent, they're resolved at read time from `backbone` via model-registry.yaml or runtimes:. Also trust_rank() and can_reach_vault() (kb#147's vault-access DECIDED rule as a checkable function, not just a comment). Several Cards (torgash, researcher, elizaveta's KB identity, the split memory banks) describe target state for things not yet built/migrated - flagged with status/note fields rather than implied live, matching the task's scope (registry only, no runtime migration). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Qg9gi7H7HtT8MncoXAB
296 lines
18 KiB
YAML
296 lines
18 KiB
YAML
# Agent registry — agents are personas, not queues.
|
|
#
|
|
# Per DESIGN-a2a-agents.md v2.1 §2, §5, §5b (commit 714a9ca7), kanboard task
|
|
# #134 (A2A-2), sibling of #133's model-registry.yaml. An agent is
|
|
# (identity, Card, Policy, State): {persona/system prompt, memory bank(s),
|
|
# tool scope, trust class, preferred tier, current backbone}.
|
|
#
|
|
# THE ONE-FIELD-BACKBONE-SWAP MECHANISM: an agent's `backbone:` field is the
|
|
# ONLY thing here that names a concrete model/runtime. Everything a Card
|
|
# would otherwise duplicate from the model plane (tier the backbone actually
|
|
# delivers, cost_class, availability a(t), context window) is NOT stored
|
|
# statically on the agent — it is resolved at read time by dereferencing
|
|
# `backbone` into model-registry.yaml (via model_registry.py) or, for
|
|
# non-metered flat-subscription runtimes model-registry.yaml deliberately
|
|
# excludes (kb#133: "Claude Code ... belongs in the agent registry"), into
|
|
# the `runtimes:` section below. See agent_registry.py:effective_card().
|
|
# This is what makes "switching a backbone is one field" true rather than
|
|
# aspirational: edit `backbone: kimi` -> `backbone: local-small` and the
|
|
# derived tier/cost/a(t) change with it, with nothing else to keep in sync.
|
|
#
|
|
# `preferred_tier` is a POLICY field (what this persona asks for), distinct
|
|
# from the backbone's actual delivered tier (a FACT, resolved above) — they
|
|
# usually agree but don't have to (e.g. a large-preferring agent temporarily
|
|
# pinned to a small backbone during a quota outage).
|
|
#
|
|
# v2.1: personas + Cards live in git, deployed to runtimes — never
|
|
# live-edited in volumes (kb#156). Several agents below still point at a
|
|
# live-volume persona (adolf's SOUL.md) because #156 (git-deploy pipeline)
|
|
# hasn't landed yet; this registry records that fact, it doesn't fix it.
|
|
#
|
|
# Trust class is on every Card (§5) and is what kb#140 (capability routing)
|
|
# and kb#147 (grant enforcement / vault access) consume — see
|
|
# routing_consumption: at the bottom. Several Cards below (torgash,
|
|
# researcher, elizaveta's KB identity) describe TARGET state for agents/
|
|
# accounts that don't exist yet; each is flagged `status: target` /
|
|
# `note:` rather than silently implying they're live. Building them is
|
|
# explicitly out of kb#134's scope (registry only).
|
|
#
|
|
# Read with agent_registry.py (same directory): load_registry(),
|
|
# get_agent(), effective_card(), trust_rank().
|
|
|
|
schema_version: 1
|
|
|
|
# ── trust classes (§5) ──────────────────────────────────────────────────
|
|
# "human > trusted > sandboxed > untrusted". Numeric rank lets routing/grant
|
|
# code do `>=` comparisons instead of string-matching an ordered list.
|
|
trust_classes:
|
|
human: { rank: 3, note: "vault access yes; not MCP-tool-scoped, IS the reasoning" }
|
|
trusted: { rank: 2, note: "vault access yes (DECIDED, kb#147); outward actions per ask-first rules" }
|
|
sandboxed: { rank: 1, note: "no vault, no outward sends; scoped MCP allowlist; KB access project-scoped" }
|
|
untrusted: { rank: 0, note: "anything ingesting the open web; its OUTPUTS are tainted, not just its access restricted" }
|
|
|
|
# ── runtimes ─────────────────────────────────────────────────────────────
|
|
# Backbones deliberately OUTSIDE model-registry.yaml's `models:` list.
|
|
# kb#133 excluded Claude Code explicitly: "a flat-subscription runtime, not
|
|
# a metered API deployment — it belongs in the agent registry (#134), not
|
|
# here." This is that home. Same lookup contract as a model-registry entry
|
|
# (id, tier, cost_class, lifecycle, context_tokens) so agent_registry.py's
|
|
# backbone resolver can treat the two sources uniformly.
|
|
runtimes:
|
|
- id: claude-code-cli
|
|
role: "Claude Code CLI — flat Anthropic subscription runtime (this session's own kind)"
|
|
tier: large
|
|
context_tokens: 200000 # current Sonnet/Opus family context window; re-verify on model upgrades
|
|
cost_class: subscription
|
|
lifecycle: always-on # gated by Claude Code's own usage windows, not GPU/quota probes
|
|
quota_probe: "claude-usage (kanboard/bin/claude-usage); orchestration/quota-gating rules documented in kanboard/CLAUDE.md"
|
|
metered: false
|
|
opt_in_required: false
|
|
|
|
# ── agents ───────────────────────────────────────────────────────────────
|
|
agents:
|
|
|
|
# ── Adolf — proactive auditor / personal assistant ──────────────────────
|
|
- id: adolf
|
|
capabilities: [matrix-chat, task-triage, proactive-monitoring, memory-recall, cron-scheduling, browser]
|
|
persona:
|
|
role: "proactive auditor / personal assistant, talks to alvis and elizaveta over Matrix"
|
|
trivial: false
|
|
prompt_source:
|
|
current: "container adolf:/home/node/.openclaw/workspace/SOUL.md — lives in the adolf-state Docker VOLUME, live-editable, zero git history. This is the kb#156 migration-debt state; recorded here per kb#134's brief, not migrated."
|
|
companion_files: ["AGENTS.md", "IDENTITY.md", "TOOLS.md", "USER.md", "HEARTBEAT.md"] # same workspace, same volume, same debt
|
|
target: "git-controlled path once kb#156 lands (e.g. openai/personas/adolf/SOUL.md), deployed read-only into the volume"
|
|
trust_class: trusted
|
|
preferred_tier: large
|
|
backbone: kimi # resolves via model-registry.yaml (adolf-llm Kimi-CLI wrapper endpoint). Swap this ONE field to rebackbone Adolf.
|
|
tool_allowlist:
|
|
mcp_servers: [hindsight, openclaw-tools, kanboard, marketplace, agap]
|
|
gateway_tools: [cron, nodes, browser] # openclaw.json gateway.tools.allow, live 2026-07-21
|
|
vault_access: true # trusted-only per §5 DECIDED; reaches vw_* via the `agap` MCP server
|
|
note: >
|
|
"scoped core tools" per kb#134's brief — this IS openai/openclaw.json's
|
|
live mcp.servers block, not a narrower aspirational allowlist. Per-tool
|
|
(not per-server) scoping + LiteLLM virtual-key budgets are kb#147's job;
|
|
this field is the input #147 consumes.
|
|
memory:
|
|
banks:
|
|
- { id: adolf-alvis, role: private, interlocutor: alvis }
|
|
- { id: adolf-elizaveta, role: private, interlocutor: elizaveta }
|
|
- { id: adolf-shared, role: shared, interlocutor: household }
|
|
current_state: >
|
|
NOT split yet. A single live bank "adolf" (hindsight MCP,
|
|
http://hindsight:8888/mcp/adolf/, 269+ facts) serves every
|
|
interlocutor today with no per-human isolation — the exact defect
|
|
kb#153 exists to fix (depends on this registry existing first).
|
|
The three banks above are kb#153's target, not current fact.
|
|
kb_identity: { username: adolf, user_id: 3 }
|
|
availability_note: "a(t) inherited from backbone at read time (kimi: quota-gated, ~60msg/5h ~300/wk — see model-registry.yaml)"
|
|
|
|
# ── claude-coder — the Claude Code loop as an ordinary consumer ─────────
|
|
- id: claude-coder
|
|
capabilities: [coding, kanboard-dispatch, git, infra-ops, code-review]
|
|
persona:
|
|
role: "implementer — pulls complex coding tasks from the fabric (design §2 theorem 5, 'never special')"
|
|
trivial: false
|
|
prompt_source:
|
|
current: "git-native, layered: ~/.claude/CLAUDE.md (global user memory) + kanboard/CLAUDE.md (canonical kb orchestration ruleset) + per-repo CLAUDE.md files (e.g. agap_git/CLAUDE.md)"
|
|
note: "No single SOUL.md — persona is these CLAUDE.md conventions, already git-controlled. No kb#156 debt for this agent."
|
|
trust_class: trusted
|
|
preferred_tier: large
|
|
backbone: claude-code-cli # resolves via runtimes: above (kb#133 exclusion), NOT model-registry.yaml
|
|
tool_allowlist:
|
|
mcp_servers: [kanboard, agap]
|
|
native_tools: [Bash, Read, Edit, Write, Agent, WebFetch, WebSearch, git]
|
|
vault_access: true # trusted
|
|
note: >
|
|
Widest-scoped agent by design (this session's own tool surface) —
|
|
gated by ask-first rules on outward/destructive actions rather than
|
|
MCP allowlisting. Per kb#147, a real virtual-key budget still applies.
|
|
memory:
|
|
banks: []
|
|
model: "session (ephemeral, per invocation) + repo state (git history, CLAUDE.md files, kanboard task/comment history) — no persistent Hindsight bank"
|
|
kb_identity: { username: claude, user_id: 2 }
|
|
availability_note: "a(t) inherited from backbone at read time (claude-code-cli: always-on, gated by claude-usage windows)"
|
|
|
|
# ── torgash — marketplace analyst (sandboxed) ───────────────────────────
|
|
- id: torgash
|
|
capabilities: [price-comparison, marketplace-search, cart-ops, product-recommendations]
|
|
persona:
|
|
role: "marketplace analyst — price comparison / shopping across Ozon, Yandex Market, etc."
|
|
trivial: false
|
|
prompt_source:
|
|
current: null
|
|
note: "NOT yet stood up as a running persona — this Card is target state per design §2's agent table and the A2A design-review plan. Building the runtime is out of kb#134's scope (registry only); flagging as follow-up work."
|
|
trust_class: sandboxed
|
|
preferred_tier: small
|
|
backbone: local-small
|
|
tool_allowlist:
|
|
mcp_servers: [marketplace]
|
|
vault_access: false # sandboxed — hard rule §5, no exceptions
|
|
outward_sends: false
|
|
note: "scoped to mcp__marketplace__* tools only; no gitea/ha/zabbix/radicale, no kanboard project outside its own."
|
|
memory:
|
|
banks: [{ id: torgash, role: private }]
|
|
current_state: "bank not yet created — target state, same as the persona itself"
|
|
kb_identity:
|
|
username: null
|
|
note: "no Kanboard account provisioned yet; §5 calls for KB access project-scoped to a dedicated project once created — gap, not in kb#134's scope."
|
|
availability_note: "a(t) inherited from backbone at read time (local-small: always-on, VRAM-bound)"
|
|
|
|
# ── researcher — autonomous, sandboxed/untrusted-input loop ─────────────
|
|
- id: researcher
|
|
capabilities: [web-research, synthesis, low-priority-background-loop]
|
|
persona:
|
|
role: "autonomous researcher — low-priority self-submitting loop (design §6, §8)"
|
|
trivial: false
|
|
prompt_source: { current: null, note: "not yet built; target-state Card, same caveat as torgash." }
|
|
trust_class: sandboxed
|
|
trust_note: >
|
|
Ingests the open web, so its OUTPUTS are tainted regardless of its own
|
|
sandboxed access level (the untrusted-INPUT rule, §5). Promotion of
|
|
tainted output into a trusted agent's memory or into any action
|
|
requires a gate — initially a task to alvis's inbox.
|
|
preferred_tier: small
|
|
backbone: local-small # escalates to large via always-ask policy (§5) for synthesis — never silent retry-on-bigger-model
|
|
tool_allowlist:
|
|
mcp_servers: [] # target: a scoped web-search/fetch surface once built
|
|
native_tools: [WebSearch, WebFetch]
|
|
vault_access: false
|
|
outward_sends: false
|
|
memory:
|
|
banks: [{ id: researcher, role: private }]
|
|
current_state: "bank not yet created — target state"
|
|
kb_identity:
|
|
username: null
|
|
note: "needs its own KB project(s) per §5 ('researcher gets its own KB project(s)') — not yet created, gap outside kb#134's scope."
|
|
availability_note: "a(t) inherited from backbone at read time (local-small: always-on, VRAM-bound)"
|
|
|
|
# ── model-agents — trivial personas (from kb#133) ───────────────────────
|
|
# Design §2 table row 1: "LLM endpoint (kimi, gemma3:4b, ...) | persona:
|
|
# trivial (identity) | memory: none". These are direct-address targets
|
|
# (router mode target=agent-id) for a caller that wants THIS backbone
|
|
# specifically, bypassing any persona/tool-scope layer — distinct from
|
|
# e.g. `adolf`, which happens to use the `kimi` backbone today but adds a
|
|
# persona, memory, and tool scope on top. Only the two chat-capable
|
|
# models get an agent entry; bge-m3/tei-reranker are non-chat sidecars in
|
|
# model-registry.yaml, not addressable A2A targets.
|
|
- id: kimi-endpoint
|
|
capabilities: [raw-completion]
|
|
persona: { role: "trivial identity — the kimi backbone exposed directly, no persona layer", trivial: true, prompt_source: { current: null } }
|
|
trust_class: untrusted # a bare model endpoint makes no trust decisions itself; the caller's trust class governs what reaches it
|
|
preferred_tier: large
|
|
backbone: kimi
|
|
tool_allowlist: { mcp_servers: [], native_tools: [], vault_access: false }
|
|
memory: { banks: [], model: "none (trivial persona)" }
|
|
kb_identity: { username: null }
|
|
|
|
- id: local-small-endpoint
|
|
capabilities: [raw-completion]
|
|
persona: { role: "trivial identity — the local-small backbone exposed directly, no persona layer", trivial: true, prompt_source: { current: null } }
|
|
trust_class: untrusted
|
|
preferred_tier: small
|
|
backbone: local-small
|
|
tool_allowlist: { mcp_servers: [], native_tools: [], vault_access: false }
|
|
memory: { banks: [], model: "none (trivial persona)" }
|
|
kb_identity: { username: null }
|
|
|
|
# ── humans — first-class agents (§2, §5b) ───────────────────────────────
|
|
# "The human being an agent is not a metaphor": approval gates and
|
|
# decisions are ordinary tasks submitted to a human's inbox, which IS the
|
|
# Kanboard column/assignment he already processes. No `backbone` — humans
|
|
# ARE the reasoning, not a resolvable model.
|
|
- id: alvis
|
|
capabilities: [approval, decision, escalation-target]
|
|
persona: { role: "human — primary user/owner of Agap" }
|
|
trust_class: human
|
|
preferred_tier: null
|
|
backbone: null
|
|
tool_allowlist: null # n/a — outranks `trusted`, not MCP-scoped
|
|
memory:
|
|
banks:
|
|
- { id: adolf-alvis, role: private }
|
|
- { id: adolf-shared, role: shared }
|
|
current_state: "today's single unsplit 'adolf' bank mixes alvis + elizaveta content; split pending kb#153"
|
|
matrix_id: "@admin:mtx.alogins.net"
|
|
kb_identity:
|
|
username: admin
|
|
user_id: 1
|
|
note: "assumed == alvis (sole non-bot app-admin account); the Kanboard user record's email field is unpopulated so this can't be confirmed via API — verify by hand if it's ever ambiguous."
|
|
inbox: "tasks assigned to Kanboard user 'admin' (id 1) across projects — his approval/escalation inbox (design §2)"
|
|
availability: "a(t) = waking hours (informal, no fixed function yet); vacation mode sets a(t)=0 and parks his inbox per §5b"
|
|
|
|
- id: elizaveta
|
|
capabilities: [approval, decision, escalation-target]
|
|
persona: { role: "human — household member" }
|
|
trust_class: human
|
|
preferred_tier: null
|
|
backbone: null
|
|
tool_allowlist: null
|
|
memory:
|
|
banks:
|
|
- { id: adolf-elizaveta, role: private }
|
|
- { id: adolf-shared, role: shared }
|
|
current_state: "not yet split out of the single 'adolf' bank — kb#153, flagged urgent-ish there since she is on Adolf's Matrix allowlist TODAY with a shared, unpartitioned bank."
|
|
matrix_id: "@elizaveta:mtx.alogins.net"
|
|
kb_identity:
|
|
username: null
|
|
note: "no Kanboard account provisioned for her yet — her only inbox today is the Matrix DM channel (Adolf's dm.allowFrom), not a KB column. Gap / candidate follow-up task, out of kb#134's scope."
|
|
inbox: "none in Kanboard yet (see kb_identity note above); Matrix DM is the only channel today"
|
|
|
|
# ── memory bank policy (§5b) ─────────────────────────────────────────────
|
|
# Hard rules the hindsight-memory plugin's recall/retain hooks must enforce
|
|
# once kb#153 implements bank selection by interlocutor. Registry mirror of
|
|
# model-registry.yaml's gpu_residency_policy: the parameters a consumer
|
|
# reads, not the enforcement logic itself.
|
|
memory_bank_policy:
|
|
hard_rules:
|
|
- "content from one human's conversations must never surface to another human (correctness property, not preference)"
|
|
- "promotion private -> shared happens only by the owning human's explicit action or an approval task — never automatically"
|
|
- "recall is interlocutor-scoped: the hindsight-memory plugin selects the bank by interlocutor identity (Matrix sender)"
|
|
- "sandboxed agents (torgash, researcher) read at most the shared bank, never any private bank"
|
|
banks:
|
|
- { id: adolf-alvis, owner: alvis, role: private, status: target, note: "kb#153 not yet done" }
|
|
- { id: adolf-elizaveta, owner: elizaveta, role: private, status: target, note: "kb#153 not yet done" }
|
|
- { id: adolf-shared, owner: household, role: shared, status: target, note: "kb#153 not yet done" }
|
|
- { id: adolf, owner: null, role: legacy, status: live-today, note: "single unsplit bank every interlocutor currently reads/writes; superseded by the three rows above once kb#153 lands" }
|
|
- { id: torgash, owner: torgash, role: private, status: target, note: "agent not yet built" }
|
|
- { id: researcher, owner: researcher, role: private, status: target, note: "agent not yet built" }
|
|
|
|
# ── how downstream tasks consume this file ────────────────────────────────
|
|
# Documents the contract kb#140 (routing) and kb#147 (grant enforcement)
|
|
# build against, mirroring model-registry.yaml's `routing:` section.
|
|
routing_consumption:
|
|
trust_gate: >
|
|
kb#147: a task requiring vault access may target only an agent whose
|
|
trust_classes[...].rank >= trust_classes.trusted.rank (i.e. trusted or
|
|
human). Sandboxed/untrusted agents' tool_allowlist.vault_access is
|
|
always false by construction above — kb#147's job is to make that
|
|
provably true at the MCP/LiteLLM enforcement points, not just on paper.
|
|
capability_routing: >
|
|
kb#140: given a task's required capabilities + trust constraint, the
|
|
router filters agents by capabilities ⊆ agent.capabilities, trust rank
|
|
>= required, and current a(t) == 1 (resolved via agent_registry.py's
|
|
effective_card(), which dereferences `backbone` into model-registry.yaml
|
|
or `runtimes:` above) — no submitter-side hardcoded agent id needed.
|