An agent in Griffin is a row in a table, not a class in the code. It has an id, a label, a line about what it does, its own instructions, a model, a list of tools, and a list of who may call it and with what. You create one from #/agents → ایجنت جدید, from the API, or by importing a JSON profile. Nothing about agents requires a deploy.
A fresh install has exactly one agent (griffin) with the tools that exist everywhere — asking,
delegating, charts, files, notes, jobs, messengers. Everything else you add.
| Field | What it decides |
|---|---|
id |
the handle other agents use (delegate {agent: "release-manager"}), and its workspace directory |
label, domain |
how it appears in the UI and in list_agents, so peers know what it is for |
instructions |
the agent’s own brief, appended to the shared core rules on every run |
provider, model |
Cursor or Claude, and which model |
tools / allTools |
what it may use at all: a named list, or everything this install has |
callers |
who may call it, and which subset of its tools they get |
The prompt an agent actually receives is assembled per run:
core rules (honesty, tools over memory, delegation, asking) ← shared, in code
its own instructions ← yours, editable
who is calling now + that caller's tool list ← when it is not the owner
"a missing tool is not a result" ← shared
live peers it can delegate to ← from the other agents
reviewed knowledge notes ← notes the owner approved
Every run has a caller: the owner (you, in the UI), another agent (through delegate /
ask_agent), the scheduler (a job), the ops room, a person through a messenger bridge
(team), or an external peer connected over MCP (peer:<user>).
The agent’s callers table says what each of them gets. The rules are deliberately blunt:
agent_tools_*, list_agents) are never given to a non-owner, whatever a
row says: an agent that could widen its own quota has no quota.On top of that, irreversible calls (merging, writing to a database, DNS, deletes, router writes) go
through the approval gate: with a human at the root of the chain the owner is asked and the run
waits; in an unattended chain (a job, the ops room) it is refused. Every decision lands in the
approvals table.
From the UI: #/agents → ایجنت جدید, then set its tools and callers on the detail page.
From the API:
curl -sX POST localhost:3100/api/agents -H 'content-type: application/json' -d '{
"id": "release-manager",
"label": "Release manager",
"domain": "prepares and checks releases",
"instructions": "You prepare releases. You never merge without an explicit approval.",
"tools": ["ask_owner", "ask_requester", "show_media", "knowledge_list"],
"callers": { "owner": { "tools": "*" }, "griffin": { "tools": ["show_media"] } }
}'
Or import one of the ready-made profiles in examples/agents/:
curl -sX POST localhost:3100/api/agents -H 'content-type: application/json' \
--data-binary @examples/agents/researcher.json
| Example | What it shows |
|---|---|
assistant.json |
a personal assistant: Telegram messages, notes and scheduled jobs; other agents may read but never send as you |
writer.json |
drafts, edits and translates; it has no tool that sends anything |
researcher.json |
a pure reading/summarising agent |
platform.json |
an infrastructure agent that may hold every tool, with narrow quotas for its callers |
cdn-arvan.json, cdn-nsin.json |
vendor-scoped agents that hand cluster questions back to the platform agent |
They are examples, not defaults: import them only if they fit your world, and edit the instructions to match it.
The core rules already cover honesty, using tools instead of memory, delegating, and asking before irreversible actions — do not repeat them. Spend the instructions on what is specific:
Keep it short. An instruction the model cannot act on is decoration, and a tool it does not have is not a rule — take the tool away instead.
delegate {agent, request} starts a subtask and returns immediately; the result arrives in the
calling chat when it lands. ask_agent is the blocking variant for a quick read. subtasks lists,
steers or cancels what is running. A child runs with the calling agent’s quota on the target, so
delegation can never be used to reach a tool the caller was not given.
list_agents is how an agent discovers who exists — ids, labels and domains come from the same
table you edit, so a new agent is immediately visible to the others.