Events & Lifecycle
Lifecycle hooks for monitoring and extending agent behavior
Events provide lifecycle hooks for monitoring agent execution. Use them for logging, metrics, analytics, and error tracking.
Agent Events
Track individual agent execution with started, completed, and errored events:
import { createAgent } from '@agentuity/runtime';
import { s } from '@agentuity/schema';
const agent = createAgent('TaskProcessor', {
schema: {
input: s.object({ task: s.string() }),
output: s.object({ result: s.string() }),
},
handler: async (ctx, input) => {
ctx.logger.info('Processing task', { task: input.task });
return { result: `Completed: ${input.task}` };
},
});
// Track execution timing
agent.addEventListener('started', (event, agent, ctx) => {
ctx.state.set('startTime', Date.now());
ctx.logger.info('Agent started', { agent: agent.metadata.name });
});
agent.addEventListener('completed', (event, agent, ctx) => {
const startTime = ctx.state.get('startTime') as number;
const duration = Date.now() - startTime;
ctx.logger.info('Agent completed', {
agent: agent.metadata.name,
durationMs: duration,
});
// Warn on slow executions
if (duration > 1000) {
ctx.logger.warn('Slow execution detected', { duration, threshold: 1000 });
}
});
agent.addEventListener('errored', (event, agent, ctx, error) => {
const startTime = ctx.state.get('startTime') as number;
const duration = Date.now() - startTime;
ctx.logger.error('Agent failed', {
agent: agent.metadata.name,
error: error.message,
durationMs: duration,
});
});
export default agent;Event listeners receive: event name, agent instance, context, and (for errored) the error object.
App-Level Events
Monitor all agents globally by registering listeners in app.ts:
import { createApp } from '@agentuity/runtime';
const app = await createApp();
// Track all agent executions
app.addEventListener('agent.started', (event, agent, ctx) => {
ctx.logger.info('Agent execution started', {
agent: agent.metadata.name,
sessionId: ctx.sessionId,
});
});
app.addEventListener('agent.completed', (event, agent, ctx) => {
ctx.logger.info('Agent execution completed', {
agent: agent.metadata.name,
sessionId: ctx.sessionId,
});
});
app.addEventListener('agent.errored', (event, agent, ctx, error) => {
ctx.logger.error('Agent execution failed', {
agent: agent.metadata.name,
error: error.message,
sessionId: ctx.sessionId,
});
});Available App Events
| Event | Description |
|---|---|
agent.started | Any agent starts execution |
agent.completed | Any agent completes successfully |
agent.errored | Any agent throws an error |
session.started | New session begins |
session.completed | Session ends |
thread.created | New thread created |
thread.destroyed | Thread expired or destroyed |
App Lifecycle Hooks
Use setup and shutdown hooks to initialize resources and manage typed app state:
import { createApp } from '@agentuity/runtime';
const { server, logger } = await createApp({
setup: async () => {
// Initialize resources when the app starts
const dbClient = await connectToDatabase();
// Return typed app state - available in all agents via ctx.app
return {
db: dbClient,
startedAt: new Date(),
};
},
shutdown: async (state) => {
// Cleanup when the app shuts down
// state is typed from setup's return value
await state.db.close();
logger.info('App ran for:', Date.now() - state.startedAt.getTime(), 'ms');
},
});Access app state in any agent handler:
const agent = createAgent('DataFetcher', {
handler: async (ctx, input) => {
// ctx.app is fully typed from setup's return value
const results = await ctx.app.db.query('SELECT * FROM users');
return { users: results };
},
});Use Cases
Common uses for app lifecycle hooks:
- Database connection pools
- Cache initialization
- External service clients
- Metrics/telemetry setup
Shared State
Event handlers share state via ctx.state:
agent.addEventListener('started', (event, agent, ctx) => {
ctx.state.set('startTime', Date.now());
ctx.state.set('metadata', { userId: '123', source: 'api' });
});
agent.addEventListener('completed', (event, agent, ctx) => {
const startTime = ctx.state.get('startTime') as number;
const metadata = ctx.state.get('metadata') as Record<string, string>;
ctx.logger.info('Execution complete', {
duration: Date.now() - startTime,
...metadata,
});
});Background Work
Use ctx.waitUntil() in event handlers for non-blocking operations like sending metrics to external services:
agent.addEventListener('completed', (event, agent, ctx) => {
ctx.waitUntil(async () => {
await sendMetricsToExternalService({ agent: agent.metadata.name });
});
});Events vs Evals
| Aspect | Events | Evals |
|---|---|---|
| Purpose | Monitoring, logging | Quality assessment |
| Timing | During execution | After completion |
| Blocking | Synchronous | Background (waitUntil) |
| Output | Logs, metrics | Pass/fail, scores |
Use events for observability. Use evaluations for output quality checks.
Next Steps
- Evaluations: Automated quality testing for agent outputs
- State Management: Thread and session state patterns
- Calling Other Agents: Multi-agent coordination
Need Help?
Join our Community for assistance or just to hang with other humans building agents.
Send us an email at hi@agentuity.com if you'd like to get in touch.
Please Follow us on
If you haven't already, please Signup for your free account now and start building your first agent!