Agents
Apps can define AI agents that execute autonomously in response to events, cron schedules, or manual triggers. Agents run in the same sandboxed environment as server routes but are driven by an AI model that can call tools.
Key Concepts
Agents are defined in informer.yaml and deployed alongside the app. Each agent has:
- Instructions — a system prompt guiding the AI's behavior
- Tools — app-scoped tools from the
tools/directory - Toolkits — external toolkit integrations (e.g., custom toolkits)
- Assistants — Informer assistants whose instructions are merged into the agent's system prompt
- Triggers — events, cron schedules, or manual invocation
Runs are individual executions of an agent. Each run tracks status, steps, token usage, and errors.
Events are the primary trigger mechanism. Server route handlers or other agents can emit events via emit(), and agents listening for those events execute automatically.
Agent API Endpoints
GET /api/apps/{id}/agents
List all agents for an app, ordered by name.
Authentication: Required
Permissions Required: None (any user who can read the app)
[
{
"id": "00000000-0000-4000-8000-000000000002",
"tenant": "acme",
"appId": "00000000-0000-4000-8000-000000000001",
"libraryId": null,
"name": "order-watcher",
"description": null,
"instructions": "When an order fails, summarize the failure and email the operations lead.",
"instructionsOverride": null,
"model": "go_everyday",
"triggers": {
"on": [
"order_failed"
]
},
"status": "active",
"nextRunAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
]
GET /api/apps/{id}/agents/{agentId}
Get a single agent's details.
Authentication: Required
Permissions Required: None (any user who can read the app)
Response: Same shape as the array item above.
PUT /api/apps/{id}/agents/{agentId}
Update an agent's runtime settings. Only certain fields can be changed via API — the core definition comes from informer.yaml on deploy.
Authentication: Required
Permissions Required: app:write (Designer for Magic Reports, Admin
for full Apps)
Payload:
| Field | Type | Description |
|---|---|---|
instructionsOverride | string | null | Override the YAML-defined instructions (set to null to revert) |
status | string | "active" or "stopped" |
model | string | AI model slug (e.g., "go_everyday") |
{
"status": "stopped"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/00000000-0000-4000-8000-000000000001/agents/00000000-0000-4000-8000-000000000002"
}
},
"id": "00000000-0000-4000-8000-000000000002",
"tenant": "acme",
"appId": "00000000-0000-4000-8000-000000000001",
"libraryId": null,
"name": "order-watcher",
"description": null,
"instructions": "When an order fails, summarize the failure and email the operations lead.",
"instructionsOverride": null,
"model": "go_everyday",
"triggers": {
"on": [
"order_failed"
]
},
"status": "stopped",
"nextRunAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
Changing status also syncs the agent's deploy-generated cron
automation: stopping the agent pauses the <name>:cron automation,
re-activating it recomputes the next fire time.
GET /api/apps/{id}/agents/{agentId}/runs
List execution history for an agent, newest first.
Authentication: Required
Permissions Required: None (any user who can read the app)
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results (1-200) |
offset | number | 0 | Pagination offset |
List items carry trigger, context, status, error, tokens,
cost, and timing, but NOT steps; the per-step transcript comes from
the single-run endpoint below.
GET /api/apps/{id}/agents/{agentId}/runs/{runId}
Get details for a single run, including the full steps transcript
(tool calls with args and results, text outputs).
Authentication: Required
Permissions Required: None (any user who can read the app)
POST /api/apps/{id}/agents/{agentId}/_trigger
Manually trigger an agent. Useful for testing or one-off executions.
Authentication: Required
Permissions Required: app:write (Designer for Magic Reports, Admin
for full Apps)
Payload:
| Field | Type | Required | Description |
|---|---|---|---|
event | string | No | Event name (default: "_manual") |
payload | object | No | Data passed to the agent as trigger context |
Response:
Execution is awaited: the response is the finished run record with its
final status, token and cost totals, and steps.
Defining Agents in informer.yaml
Agents are declared in the agents section of informer.yaml:
agents:
order-processor:
description: Processes new orders
instructions: |
You are an order processing agent. When triggered by an order_created event,
verify the order details, update inventory, and send a confirmation email.
tools:
- send-email
- update-inventory
on: order_created
model: go_everyday
daily-report:
description: Generates daily summary
instructions: Generate a summary of today's orders and email it to the team.
tools:
- send-email
cron: "0 17 * * 1-5"
model: go_everyday
smart-assistant:
description: Customer support agent with toolkit and assistant tools
instructions: Help customers with their questions using CRM data.
toolkits:
- admin:crm-toolkit
assistants:
- admin:support-persona
on: support_request
webSearch: true
Agent Fields
| Field | Type | Required | Description |
|---|---|---|---|
description | string | No | Human-readable description |
instructions | string | No | System prompt for the AI |
tools | string[] | No | App tool names (from tools/ directory) |
toolkits | string[] | No | Toolkit natural IDs to attach |
assistants | string[] | No | Assistant natural IDs — instructions are merged into the agent's system prompt |
on | string | string[] | No | Event name(s) that trigger this agent |
cron | string | No | Cron schedule (5-field format: min hour dom mon dow) |
webSearch | boolean | No | Enable web search tool |
model | string | No | AI model slug (default: "go_everyday") |
Trigger Types
Agents can be triggered in three ways (any combination):
- Events (
on) — fires when the named event is emitted viaemit()in a server route or webhook handler - Cron (
cron) — fires on a schedule (e.g.,"0 3 * * *"for 3 AM daily) - Manual — via
POST /agents/{agentId}/_trigger
App Tools
Agents can call tools defined in the app's tools/ directory. Each tool is a JavaScript file that exports a handler:
// tools/send-email.js
export const description = 'Send an email to a recipient';
export const schema = {
properties: {
to: { type: 'string', description: 'Recipient email' },
subject: { type: 'string', description: 'Email subject' },
body: { type: 'string', description: 'Email body' }
},
required: ['to', 'subject', 'body']
};
export async function handler({ args, fetch }) {
const { to, subject, body } = args;
const result = await fetch('integrations/sendgrid/request', {
method: 'POST',
body: {
url: '/v3/mail/send',
method: 'POST',
data: { to, subject, body }
}
});
return { sent: result.status === 202 };
}
A tool file exports a named handler plus optional description and schema (used to build the tool's AI function definition). Tools are bundled with esbuild during deploy and executed in V8 isolates.
Tool Handler Signature
The handler receives a single context bag — the same service surface as server routes and webhooks, so a tool can do anything a route can:
export async function handler({ args, run, context, query, fetch, emit, notify, email, crypto, markdown, extractText, log, env }) {
// ...
}
| Property | Description |
|---|---|
args | The AI-supplied tool input, validated against schema (the tool's inbound payload) |
run | Agent-run metadata: { appId, agentId, runId, trigger } |
context | Typed bound dependencies — context.<slot>.<method>(...) (see Dependencies) |
query, fetch, emit, notify, email, crypto, markdown, extractText, log, env | Identical to server routes |
Migration note: earlier tools used
export default async function (args, ctx)and read run metadata fromctx.context. Tools now take a single bag: the AI input isargs, run metadata moved torun, andcontextis the typed dependency proxy. Tools also now havecrypto,env,notify, and
Returning a document or image
A tool's return value is serialized to the model as JSON (or as text for a string). To instead hand the model a file as a native document or image part, so it reads the actual PDF or image rather than extracted text, return a $file (or $files) marker:
export async function handler({ args, query }) {
const [doc] = await query('SELECT filename, content_type, data FROM documents WHERE id = $1', [args.id]);
// `data` is base64-encoded file bytes
return {
$file: { mediaType: doc.content_type, data: doc.data, filename: doc.filename },
text: `Loaded ${doc.filename}.`
};
}
| Field | Type | Description |
|---|---|---|
$file | object | A single file: { mediaType, data, filename? }, where data is base64 |
$files | array | Several files, each { mediaType, data } |
text | string | Optional caption emitted before the file part(s) |
mediaType is application/pdf or an image/* type; provider support follows the underlying model. Reach for this when the document's layout, tables, or visuals matter, or for a scanned PDF with no text layer. For plain text-heavy files, extractText() is the cheaper option.
Toolkit Integration
Agents can reference external toolkits that provide additional tool definitions. Toolkits are resolved during deploy — the app creates junction rows in app_toolkit linking the app to each referenced toolkit.
At execution time, the agent executor:
- Loads the app's toolkit references from
app_toolkit - Calls
toolkit.getToolDefinitions()for each toolkit - Merges toolkit tools into the agent's available tools
- Appends
toolkit.getSystemInstructions()to the system prompt
agents:
crm-agent:
description: Handles CRM operations
instructions: Use CRM tools to look up and update customer records.
toolkits:
- admin:crm-toolkit
on: customer_update
If a referenced toolkit doesn't exist, deploy fails with a 400 error.
Assistant Integration
Agents can reference Informer assistants to inherit their instructions. Each referenced assistant's system prompt is merged into the agent's own instructions, allowing you to compose agent behavior from reusable assistant personas.
agents:
support-agent:
description: Handles customer support with a specialized persona
instructions: Process support tickets and escalate as needed.
assistants:
- admin:support-persona
on: support_request
Assistant references are resolved at deploy time — if a referenced assistant doesn't exist, deploy fails with a 400 error. Use either the assistant's naturalId (owner:slug) or UUID.
At execution time, the agent executor:
- Loads each referenced assistant by ID
- Collects the assistant's
instructionsfield - Appends assistant instructions to the agent's system prompt (before toolkit instructions)
Events
Events are the glue between server routes and agents. Declare event types in informer.yaml:
events:
order_created:
description: Fired when a new order is submitted
order_shipped:
description: Fired when an order ships
agents:
order-processor:
instructions: Process new orders.
on: order_created
Emit events from server routes:
// server/orders/index.js
export async function POST({ query, emit, request }) {
const [order] = await query(
'INSERT INTO orders (customer, total) VALUES ($1, $2) RETURNING *',
[request.body.customer, request.body.total]
);
// This triggers the order-processor agent
await emit('order_created', { orderId: order.id, total: order.total });
return { status: 201, body: order };
}
Event Dispatch
Events are dispatched in two ways:
- Real-time — a Redis pub/sub listener picks up events immediately
- Sweep — a periodic scan (every 10 seconds) catches any missed events
Events use atomic claiming (FOR UPDATE SKIP LOCKED) to prevent duplicate execution in clustered environments.
Execution Model
When an agent is triggered:
- A
runrecord is created with status"running" - A workspace database connection is acquired
- The AI model is resolved (default:
go_everyday) - App tools are loaded from bundled code in
_bundles/tools/ - Assistant instructions are loaded and merged into the system prompt
- Toolkit tools are loaded from the app's toolkit references
- The AI loop runs with
stepCountIs(20)as the stop condition - Each tool call executes in a V8 isolate, receiving the unified context bag (see Tool Handler Signature)
- The run record is updated with status, steps, and token usage
Bag members available to tools (identical to server routes):
| Member | Description |
|---|---|
args | AI-supplied tool input (validated against the tool's schema) |
run | Agent-run metadata: { appId, agentId, runId, trigger } |
context | Typed bound dependencies — context.<slot>.<method>(...) |
query(sql, params) | Execute SQL against the app's workspace |
fetch(path, options) | Make authenticated API calls (whitelist enforced) |
emit(event, payload) | Create app events (can chain-trigger other agents) |
notify(user, message) / email(to, message) | Enqueue a push notification / email |
crypto | Full crypto surface — hmac, hash, randomUUID, randomBytes, timingSafeEqual, verifyHmac, encrypt/decrypt, verify (see the crypto object) |
markdown(text) | Convert markdown to HTML |
extractText(data, contentType) | Extract plain text from a base64 file (PDF, Excel, Word, text); see the extractText() helper |
log(message, data?) | Structured logging (also log.info, log.warn, log.error, log.debug) |
env | App environment variables |