Hooks
Lifecycle hooks for reacting to agent events with full env and tool access.
Hooks
Lifecycle hooks let you run custom logic when specific events occur during an agent run. Every hook receives a HookContext as its first argument, giving you access to the full environment, state, and the ability to call tools.
HookContext
All hooks receive this context:
interface HookContext {
userId: string; // The user who triggered the event
chatId: string; // The active chat ID
env: PloyEnv; // Full Ploy environment bindings
state: StateManager; // Key-value state (get/set/delete/getJSON/setJSON)
execute: (toolName: string, args: Record<string, unknown>) => Promise<string>;
}The execute function lets you call any registered tool (built-in or custom) from within a hook:
hooks: {
async onMemorySet(ctx, key, secret) {
// Call the artifact_create tool from within a hook
await ctx.execute("artifact_create", {
filename: "memory-log.txt",
content: `Memory "${key}" was set (secret: ${secret})`,
});
},
}Available Hooks
onMemorySet
Fires when the agent stores a memory entry.
onMemorySet?: (ctx: HookContext, key: string, secret: boolean) => Promise<void>hooks: {
async onMemorySet(ctx, key, secret) {
if (secret) {
await ctx.state.set(`audit:${ctx.userId}:${Date.now()}`, `secret stored: ${key}`);
}
},
}onMemoryDelete
Fires when the agent deletes a memory entry.
onMemoryDelete?: (ctx: HookContext, key: string) => Promise<void>onArtifactCreated
Fires when the agent creates a file artifact.
onArtifactCreated?: (ctx: HookContext, filename: string, content: string) => Promise<void>hooks: {
async onArtifactCreated(ctx, filename, content) {
// Store metadata about created artifacts
const artifacts = await ctx.state.getJSON<string[]>(`artifacts:${ctx.userId}`) ?? [];
artifacts.push(filename);
await ctx.state.setJSON(`artifacts:${ctx.userId}`, artifacts);
},
}onTaskScheduled
Fires when the agent schedules a future task.
onTaskScheduled?: (ctx: HookContext, task: ScheduledTask) => Promise<void>The ScheduledTask contains:
interface ScheduledTask {
id: string;
description: string;
scheduledTime: number;
recurring: boolean;
intervalMs?: number;
}onRunComplete
Fires when the agent finishes a run with a response (no more tool calls).
onRunComplete?: (ctx: HookContext, response: string) => Promise<void>hooks: {
async onRunComplete(ctx, response) {
// Log the response for analytics
await ctx.state.set(
`log:${ctx.userId}:${Date.now()}`,
response.slice(0, 500),
);
},
}onRunError
Fires when the agent run fails with an error.
onRunError?: (ctx: HookContext, error: Error) => Promise<void>hooks: {
async onRunError(ctx, error) {
// Notify an external service
await fetch("https://alerts.example.com/webhook", {
method: "POST",
body: JSON.stringify({
userId: ctx.userId,
error: error.message,
}),
});
},
}Hooks run inside the durable workflow, so they benefit from automatic retries on failure. Keep hooks lightweight to avoid slowing down the agent loop.
Next Steps
- createAgent() -- Pass hooks via the
hooksconfig option - Tools -- Tools that trigger hooks (memory, artifacts, scheduling)
- Workflows -- Understand the durable tick loop that runs hooks
How is this guide?
Last updated on