Build
Agent definitions
An agent definition is the set of fields that decide how an agent behaves on a turn: its instructions, model, reasoning effort, per-turn timeout, toolkits, and skills. By default those fields live in Mobius, on the agent row. You can also send a definition with a request, or have Mobius fetch one from your own backend. Whichever source produces it, the runtime runs the same flat definition the same way.
Pluggable definitions exist for one job: when you embed Mobius as the agentic backend for a product, your app already owns how the agent should behave and ships changes on its own cadence. Copying those definitions into per-tenant Mobius rows means every behavior change fans out across tenants and drifts. Keeping the definition in your system means a behavior change is just different bytes on the next call.
The agent row still exists as the durable anchor for identity and state: its principal, memory, sessions, and run history. Only its behavior becomes source-pluggable.
Where a definition comes from
There are three sources. They converge on one definition; the executor never knows which one produced it.
| Source | Who supplies it | Use when |
|---|---|---|
mobius_stored | Mobius, from the agent row | You author and edit agents in Mobius. This is the default. |
Inline config | You, on the invoke request | You initiate the run and want to send the definition with it. |
| Client resolver | Your endpoint, called by Mobius | Mobius initiates the run (a schedule or a trigger) and must fetch your current definition at run start. |
The split follows the two ways a run starts. A run you initiate carries a
request that can hold the definition, so inline config fits. A run Mobius
initiates has no such request, so Mobius calls your resolver instead. Both land
on the same definition shape below.
What's in a definition
Every field is optional. When a field is present it replaces the agent's stored value; when it is absent the agent's stored value stands.
| Field | Meaning |
|---|---|
instructions | System-prompt instructions for the agent. An empty value falls back to the generated default. |
model | Model identifier. Resolves through the same model routing and allow rules as a stored agent's model. |
effort | Reasoning effort for the turn: low, medium, high, xhigh, max, or inherit. |
timeout_seconds | Per-turn execution timeout. Zero uses the platform default. A loop step's own timeout still overrides this. |
toolkits | Toolkit selections that replace the agent's toolkit assignments. Each names actions from this project's catalog. |
skills | Skills that replace the agent's skill assignments. Each carries its own instruction body, loaded on demand. |
toolkits and skills are lists, and a supplied list replaces the agent's list
whole. Mobius does not merge lists item by item, because a partial merge would
make the resulting tool surface depend on state you can't see in the request.
Two rules keep a supplied definition from widening access:
toolkitsselect from your project's catalog, they do not create tools. A toolkit can name only actions the project already has (built-ins and connected integrations). Names outside the catalog are ignored, so a definition can scope tools down, never fabricate them. See toolkits and skills for how the final tool manifest resolves.- The org ceiling clamps, it does not grant. If your organization caps a field (today, the per-turn timeout), a definition from any source is clamped to the allowed maximum. A source can narrow behavior but never widen it past org policy.
Send a definition with the invoke request
For runs you initiate, add a config block to the compound invoke call. This is
the path most embedding products use, and it works the same way system and
tools travel with a Messages API request.
{
"agent_ref": { "id": "agt_scout" },
"session": {
"mode": "continue_or_create",
"session_key": "app:acct_123:user_456:support"
},
"config": {
"instructions": "You are Acme's support agent. Be concise and cite ticket numbers.",
"model": "claude-sonnet-4-6",
"effort": "medium",
"timeout_seconds": 120,
"toolkits": [
{ "name": "tickets", "actions": ["tickets.search", "tickets.get"] }
],
"skills": [
{ "name": "refunds", "description": "How to issue a refund", "body": "..." }
]
},
"input": {
"content": [{ "type": "text", "text": "Summarize my open tickets." }],
"idempotency_key": "msg_01J8..."
}
}Mobius stores config on the resolved session,
so you send it once and later turns reuse it:
- Send it on the call that creates the session and it becomes the session's definition.
- Send it again on a later turn to replace it. A session holds one
configat a time, and the last one Mobius saves wins. - Leave it out on a later turn and the session keeps the definition it has.
- Send an empty
{}to clear it and revert the session to the agent's own definition.
A stateless client can simply send the full document on every invoke. To run two
different definitions at the same time, give each its own session_key, or send
session.mode: "new" so each call gets its own session.
Precedence on each turn is inline config, then the agent's stored
overrides, then the resolved source, all clamped by the org ceiling. The
serialized config is capped at 256 KB, large enough for real instruction sets
and small enough to keep the request cheap to validate.
Note:
configis for one-off invocations. Loops and schedules must point to a stored agent, so creating or updating a loop withconfigis rejected. Loops reference a definition; they never freeze a snapshot of one. For Mobius-initiated runs, use a client resolver instead.
See agent invocation for the full invoke mechanics: streaming modes, idempotency, and reconnect rules.
Definitions for Mobius-initiated runs
A loop run started by a schedule or a
trigger has no request to carry config. If your
definitions live in your own system, point your organization at an HTTPS endpoint
Mobius calls at run start. This is an embedding-platform capability; an org admin
(Admin or Owner) sets it up with Mobius rather than through a self-serve API.
At run start Mobius POSTs your endpoint with Authorization: Bearer <token>
and a batch-of-one request:
{
"protocol_version": 1,
"org_id": "org_123",
"project": { "id": "prj_456", "external_ref": "workspace_789" },
"trigger": { "kind": "schedule" },
"refs": [{ "selector": "agent/scout", "known_digest": "sha256:abc..." }]
}Your endpoint reads the tenant from project.external_ref, then returns the flat
definition, or unchanged when your digest still matches what Mobius cached:
{
"resolved": {
"agent/scout": {
"digest": "sha256:def...",
"value": {
"agent": {
"instructions": "...",
"model": "claude-opus-4-8",
"effort": "high",
"timeout_seconds": 600
},
"toolkits": [{ "name": "crm", "actions": ["crm.lookup"] }],
"skills": [{ "name": "triage", "body": "..." }]
}
}
}
}Return { "unchanged": true } for a ref whose known_digest is current, and
Mobius serves its cached copy. Return { "error": { "code": "unknown_agent" } }
to fail the run with a reason rather than run an empty definition.
- Tenant identity is
external_ref, not the token. Set an immutableexternal_ref(your own tenant or workspace id) on each project. Mobius sends it on every resolve so you map it to your own definition. The bearer token only proves the caller is Mobius. - Canary and rollback are yours. Vary the returned
digestper cohort in your resolver. Mobius stores no pins, so a rollout is a branch in your code, not a migration in ours.
When your endpoint is unavailable
If your endpoint is briefly down, the run does not have to fail. The resolver config sets a degradation policy:
last_known_good(the default) serves the last successful definition, which Mobius keeps durably in Postgres, up to a staleness bound you set.failfails the run to start with a clear reason.
Either way, Mobius never silently runs an empty definition. A fresh cache also skips the network entirely, so a short revalidation window keeps run-start fast without a fetch on every fire.
Warning: Resolver URLs must be
https. Mobius refuses loopback, link-local (including cloud metadata), private, and reserved targets, pins the connection to the validated address so a rebind can't redirect it, and never follows redirects. The bearer token is sealed at rest and redacted on read.
Guarantees
- Definitions come from you; execution binds to the project. A
gmailaction in a supplied definition resolves this project's Gmail connection at run time, exactly as a built-in tool does. No per-user credential crosses the resolver boundary. - A source can narrow, never widen. Both the org ceiling and the catalog-only toolkit rule hold for every source, so a mistake or a compromise in your definition can only reduce scope, not escalate it.
- The agent row is the constant. Identity, memory, sessions, and run history stay on the agent regardless of where a turn's behavior came from.
FAQ
Does sending config change the stored agent?
No. Inline config lives on the session, not the agent row. The agent's stored
definition is unchanged, and other sessions on the same agent are unaffected.
Can I define brand-new actions or MCP servers in a definition?
Not yet. A definition can select existing catalog actions through toolkits
and carry skills bodies. Bundle actions and mcp_servers fields are carried
for provenance but are not executable, and per-reference ($ref) resolution is a
later addition. For now, connect the integration
in the project and select its actions.
What happens to model if my org restricts models?
The model you send resolves through the same routing and allow rules as a
stored agent's model. If your organization restricts models, a restricted value
is rejected the same way it would be on a stored agent.
Next
- Agent invocation covers the invoke endpoint that
carries inline
config: streaming, idempotency, and reconnect. - Agents are the durable identity behind every definition.
- Agent sessions hold the inline
configand the transcript it runs against. - Toolkits and skills explain how a definition's tool surface resolves.