A Trigger binds a function to an event. When the event occurs (HTTP request, queue message, cron schedule, etc.), the iii-engine invokes the function automatically.
What is a Trigger?
A trigger consists of:
Type : The event type (http, queue, pubsub, cron)
Function ID : The function to invoke when the event occurs
Config : Type-specific configuration (route, topic, schedule, etc.)
Triggers are registered after functions. You can’t trigger a function that doesn’t exist.
Trigger Types
AgentOS supports four types of triggers:
HTTP Invoke function via HTTP request (REST API)
Queue Invoke function when message arrives in queue topic
PubSub Invoke function when message published to topic
Cron Invoke function on schedule (cron expression)
HTTP Triggers
HTTP triggers expose functions as REST API endpoints.
// From src/agent-core.ts:924-928
registerTrigger ({
type: "http" ,
function_id: "api::chat_completions" ,
config: {
api_path: "v1/chat/completions" ,
http_method: "POST"
}
});
This creates an endpoint at http://localhost:3111/v1/chat/completions that invokes api::chat_completions. HTTP Configuration Field Type Description api_pathstring URL path (without leading /) http_methodstring HTTP method: GET, POST, PUT, DELETE, OPTIONS
// Rust uses the same config structure
iii . register_trigger ( "http" , "agent::get" , json! ({
"api_path" : "agents/:id" ,
"http_method" : "GET"
})) ? ;
HTTP Configuration from config.yaml
The REST API module is configured in config.yaml:
# From config.yaml:4-13
modules :
- class : modules::api::RestApiModule
config :
port : 3111
host : 0.0.0.0
default_timeout : 300000
concurrency_request_limit : 2048
cors :
allowed_origins : [ '*' ]
allowed_methods : [ GET , POST , PUT , DELETE , OPTIONS ]
allowed_headers : [ '*' ]
HTTP triggers are served on port 3111 (from config). The main WebSocket connection is on port 49134.
Queue Triggers
Queue triggers invoke functions when messages arrive in a queue topic.
// From src/agent-core.ts:924-928
registerTrigger ({
type: "queue" ,
function_id: "agent::chat" ,
config: { topic: "agent.inbox" }
});
Now when a message is pushed to agent.inbox, the agent::chat function is invoked with the message data. // From crates/agent-core/src/main.rs:95
iii . register_trigger ( "queue" , "agent::chat" , json! ({
"topic" : "agent.inbox"
})) ? ;
Queue Configuration
Field Type Description topicstring Queue topic name to subscribe to
Queue Module Configuration
# From config.yaml:33-36
modules :
- class : modules::queue::QueueModule
config :
adapter :
class : modules::queue::BuiltinQueueAdapter
Sending Messages to Queue
Push messages to the queue using trigger("queue::push", ...):
await trigger ( "queue::push" , {
topic: "agent.inbox" ,
data: {
agentId: "agent-123" ,
message: "Hello!" ,
sessionId: "sess-456"
}
});
PubSub Triggers
PubSub triggers invoke functions when messages are published to a topic.
registerTrigger ({
type: "pubsub" ,
function_id: "audit::log" ,
config: { topic: "audit" }
});
iii . register_trigger ( "pubsub" , "audit::log" , json! ({
"topic" : "audit"
})) ? ;
PubSub vs Queue
Feature Queue PubSub Delivery One consumer receives each message All subscribers receive each message Ordering FIFO (first in, first out) No ordering guarantee Use Case Task distribution, load balancing Events, notifications, logging
PubSub Module Configuration
# From config.yaml:38-41
modules :
- class : modules::pubsub::PubSubModule
config :
adapter :
class : modules::pubsub::LocalAdapter
Publishing Messages
Publish messages using trigger("publish", ...) or triggerVoid("publish", ...):
// From src/agent-core.ts:873-876
triggerVoid ( "publish" , {
topic: "agent.lifecycle" ,
data: { type: "created" , agentId }
});
Real example from Rust:
// From crates/agent-core/src/main.rs:85-88
let _ = iii . trigger_void ( "publish" , json! ({
"topic" : "agent.lifecycle" ,
"data" : { "type" : "deleted" , "agentId" : agent_id },
}));
Cron Triggers
Cron triggers invoke functions on a schedule.
registerTrigger ({
type: "cron" ,
function_id: "cleanup::old_sessions" ,
config: {
schedule: "0 2 * * *" // Every day at 2 AM
}
});
Cron Configuration
Field Type Description schedulestring Cron expression (5-field format)
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
│ │ │ │ │
│ │ │ │ │
* * * * *
Common Cron Patterns
Expression Description */5 * * * *Every 5 minutes 0 * * * *Every hour 0 0 * * *Every day at midnight 0 2 * * *Every day at 2 AM 0 0 * * 0Every Sunday at midnight 0 0 1 * *First day of every month
Cron Module Configuration
# From config.yaml:43-46
modules :
- class : modules::cron::CronModule
config :
adapter :
class : modules::cron::KvCronAdapter
Real-World Examples
Agent Inbox Queue Trigger
The agent-core worker registers a queue trigger to process chat messages:
Rust Queue Trigger
TypeScript Queue Trigger
// From crates/agent-core/src/main.rs:95
iii . register_trigger ( "queue" , "agent::chat" , json! ({
"topic" : "agent.inbox"
})) ? ;
Audit Event PubSub Trigger
Publishing audit events that multiple subscribers can consume:
// From src/agent-core.ts:503-511
try {
await triggerVoid ( "publish" , {
topic: "audit" ,
data: {
type: "tool_execution" ,
agentId ,
tools: currentResponse . toolCalls . map (( tc : ToolCall ) => tc . id ),
iteration: iterations ,
},
});
} catch ( err : any ) {
console . warn ( "Audit publish failed" , { agentId , error: err ?. message });
}
Lifecycle Events
Publishing agent lifecycle events:
Created Event
Deleted Event
// From src/agent-core.ts:873-876
triggerVoid ( "publish" , {
topic: "agent.lifecycle" ,
data: { type: "created" , agentId },
});
Trigger Registration Patterns
Register All Triggers at Startup
// Register functions first
registerFunction ({ id: "agent::chat" , description: "..." }, handler );
registerFunction ({ id: "agent::create" , description: "..." }, handler );
// Then register triggers
registerTrigger ({ type: "queue" , function_id: "agent::chat" , config: { topic: "agent.inbox" } });
registerTrigger ({ type: "http" , function_id: "agent::create" , config: { api_path: "agents" , http_method: "POST" } });
Multiple Triggers per Function
A single function can have multiple triggers:
registerFunction ({ id: "notify::send" , description: "Send notification" }, handler );
// Trigger via HTTP
registerTrigger ({ type: "http" , function_id: "notify::send" , config: { api_path: "notify" , http_method: "POST" } });
// Trigger via queue
registerTrigger ({ type: "queue" , function_id: "notify::send" , config: { topic: "notifications" } });
// Trigger via pubsub
registerTrigger ({ type: "pubsub" , function_id: "notify::send" , config: { topic: "alerts" } });
Managing Triggers at Runtime
Create triggers dynamically using the engine API:
// Create cron trigger via API
await trigger ( "engine::triggers::create" , {
type: "cron" ,
function_id: "cleanup::sessions" ,
config: { schedule: "0 3 * * *" }
});
// List all triggers
const triggers = await trigger ( "engine::triggers::list" , {});
// Delete trigger
await trigger ( "engine::triggers::delete" , { triggerId: "trigger-123" });
Best Practices
Register After Functions Always register functions before their triggers. The engine can’t trigger a function that doesn’t exist.
Use Queue for Tasks Use queue triggers for task distribution and load balancing across workers.
Use PubSub for Events Use pubsub triggers for events that multiple subscribers need to process.
Descriptive Topics Use namespaced topic names: agent.lifecycle, audit.security, task.completed
Handle Failures Queue and pubsub handlers should handle errors gracefully - failed messages may be retried.
Idempotent Handlers Design handlers to be safely retried. Don’t assume messages are delivered exactly once.
Trigger Flow Diagram
Complete Example: Agent System
Here’s how triggers work together in the agent system:
// 1. Register chat function
registerFunction (
{ id: "agent::chat" , description: "Process chat message" },
async ( input ) => {
// Process message
const response = await trigger ( "llm::complete" , { ... });
// Publish lifecycle event
triggerVoid ( "publish" , {
topic: "agent.lifecycle" ,
data: { type: "message_processed" , agentId: input . agentId }
});
return response ;
}
);
// 2. Register HTTP trigger for direct API calls
registerTrigger ({
type: "http" ,
function_id: "agent::chat" ,
config: { api_path: "v1/chat/completions" , http_method: "POST" }
});
// 3. Register queue trigger for async processing
registerTrigger ({
type: "queue" ,
function_id: "agent::chat" ,
config: { topic: "agent.inbox" }
});
// 4. Register audit logger for lifecycle events
registerFunction (
{ id: "audit::log" , description: "Log audit events" },
async ( event ) => {
await trigger ( "state::append" , {
scope: "audit" ,
key: "lifecycle" ,
value: event
});
}
);
registerTrigger ({
type: "pubsub" ,
function_id: "audit::log" ,
config: { topic: "agent.lifecycle" }
});
// 5. Register cleanup cron
registerFunction (
{ id: "cleanup::sessions" , description: "Clean old sessions" },
async () => {
const cutoff = Date . now () - 7 * 24 * 60 * 60 * 1000 ; // 7 days
await trigger ( "memory::cleanup" , { before: cutoff });
}
);
registerTrigger ({
type: "cron" ,
function_id: "cleanup::sessions" ,
config: { schedule: "0 3 * * *" } // 3 AM daily
});
Next Steps
Architecture See how all components work together in the system architecture
Workers Learn more about creating and managing workers