How to Configure Lifecycle Hooks in Page Agent: beforeStep, afterStep, and Task Completion
Page Agent exposes an experimental lifecycle-hook API through the AgentConfig object that lets you execute custom logic at five key moments: before the task starts, before each step, after each step, when the task ends (success or failure), and during disposal.
The alibaba/page-agent repository provides an experimental lifecycle system that enables developers to inject custom behavior throughout a task's execution. You configure these hooks—including onBeforeStep, onAfterStep, and onAfterTask (which handles both success and error states)—once in the constructor configuration object. This guide covers the hook signatures, implementation locations, and practical patterns for telemetry, error handling, and resource management.
Available Lifecycle Hooks in Page Agent
Page Agent defines five lifecycle hooks in packages/core/src/types.ts (lines 70–105). Each hook receives the agent instance and context-specific parameters:
onBeforeTask– Runs immediately before the task begins (after optional tool filtering). Receives(agent: PageAgentCore) => Promise<void> | void. Use this to initialize state or start UI heartbeats.onBeforeStep– Executes at the start of every step before the observation phase. Receives(agent: PageAgentCore, stepCount: number) => Promise<void> | void. Ideal for ensuring the page is fully loaded or injecting scripts.onAfterStep– Fires after a step completes and its result is pushed toagent.history. Receives(agent: PageAgentCore, history: HistoricalEvent[]) => Promise<void> | void. Use for persisting step data or updating progress bars.onAfterTask– Called when the task ends regardless of outcome. Receives(agent: PageAgentCore, result: ExecutionResult) => Promise<void> | void. This is where you handle success/failure logic and cleanup.onDispose– Invoked whenagent.dispose()is called or the page unloads. Receives(agent: PageAgentCore, reason?: string) => void. Use to release listeners and stop timers.
Configuring Hooks in AgentConfig
All hooks are defined once inside the AgentConfig object passed to the PageAgent or PageAgentCore constructor. The core invokes these hooks at specific points in packages/core/src/PageAgentCore.ts (lines 206–236 for step hooks, and around lines 309, 327, and 342 for task completion).
import { PageAgent } from 'page-agent'
const agent = new PageAgent({
// ... LLM credentials and other config
onBeforeStep: (agent, step) => {
console.log(`▶️ Starting step #${step}`)
},
onAfterStep: (agent, history) => {
const lastEvent = history[history.length - 1]
console.log('✅ Step completed:', lastEvent)
},
onAfterTask: async (agent, result) => {
console.log('🏁 Task finished:', result.success ? 'Success' : 'Failure')
}
})
Handling Success and Errors with onAfterTask
Unlike separate onError and onSuccess hooks, Page Agent consolidates final-state handling into onAfterTask, which receives an ExecutionResult object defined in the core types:
export interface ExecutionResult {
success: boolean // true if the "done" action reported success
data: string // final text or error message
history: HistoricalEvent[]
}
When an exception occurs during a step, the core catches it, pushes an error event to history, and still guarantees onAfterTask fires with success: false. This design provides a single, reliable cleanup and reporting location.
const agent = new PageAgent({
onAfterTask: async (agent, result) => {
if (result.success) {
console.log('✅ Task succeeded:', result.data)
// Show success toast, store payload
} else {
console.error('❌ Task failed:', result.data)
// Report to monitoring service
await fetch('https://example.com/api/agent-error', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ error: result.data, history: result.history })
})
}
}
})
Practical Code Examples
Logging Step Progress
This basic configuration logs the start and end of each step, useful for debugging and telemetry. The hook definitions are located in packages/core/src/types.ts (lines 78–91), and the invocation happens in packages/core/src/PageAgentCore.ts (lines 31–38).
import { PageAgent } from 'page-agent'
const agent = new PageAgent({
onBeforeStep: (agent, stepCount) => {
console.log(`[${new Date().toISOString()}] Starting step ${stepCount}`)
},
onAfterStep: (agent, history) => {
const lastAction = history[history.length - 1]
console.log(`Step result: ${lastAction.type}`)
}
})
Ensuring Page Load Before Each Step
For browser extension scenarios using MultiPageAgent, use onBeforeStep to guarantee the active tab is loaded before the agent observes the page. This pattern appears in packages/extension/src/agent/MultiPageAgent.ts (lines 77–80).
import { MultiPageAgent } from 'page-agent/extension'
const agent = new MultiPageAgent({
// ...
onBeforeStep: async (agent) => {
// tabsController is available in the extension context
await tabsController.waitUntilTabLoaded(tabsController.currentTabId!)
}
})
Custom Error Reporting
Because onAfterTask always runs, you can centralize error reporting logic here without worrying about uncaught exceptions bypassing your handler.
const agent = new PageAgent({
onAfterTask: async (agent, result) => {
if (!result.success) {
await fetch('/api/agent-errors', {
method: 'POST',
body: JSON.stringify({
message: result.data,
sessionHistory: result.history,
timestamp: Date.now()
})
})
}
}
})
Resource Cleanup on Disposal
Use onDispose to stop background processes when the agent shuts down. The signature is defined in packages/core/src/types.ts (lines 108–114), with usage examples in packages/extension/src/agent/MultiPageAgent.ts (lines 82–90).
let heartbeatInterval: NodeJS.Timeout
const agent = new PageAgent({
onBeforeTask: () => {
heartbeatInterval = setInterval(() => console.log('Agent heartbeat'), 5000)
},
onDispose: (agent, reason) => {
if (heartbeatInterval) clearInterval(heartbeatInterval)
chrome.storage.local.set({ isAgentRunning: false })
console.log('Agent disposed. Reason:', reason ?? 'None provided')
}
})
Core Implementation Details
The hook system is implemented across three key files:
packages/core/src/types.ts– Declares theAgentConfiginterface and all hook signatures (lines 70–105, 108–114).packages/core/src/PageAgentCore.ts– Contains the execution loop that invokes hooks.onBeforeStepandonAfterStepare called around the step execution logic (lines 206–236), whileonAfterTaskis invoked at task completion points (lines 309, 327, 342).packages/extension/src/agent/MultiPageAgent.ts– Demonstrates real-world hook usage in a browser extension context, specifically showing tab synchronization and disposal cleanup (lines 77–90).
Summary
- Lifecycle hooks are configured once in the
AgentConfigobject passed toPageAgentorPageAgentCore. - Five hooks are available:
onBeforeTask,onBeforeStep,onAfterStep,onAfterTask, andonDispose. - Error and success handling is unified under
onAfterTask, which receives anExecutionResultcontaining asuccessboolean and the final history. - Hook invocation is guaranteed even when steps throw exceptions, ensuring reliable cleanup and reporting logic.
- Source locations: Type definitions live in
packages/core/src/types.ts, while invocation logic resides inpackages/core/src/PageAgentCore.ts.
Frequently Asked Questions
What happened to separate onError and onSuccess hooks?
Page Agent does not expose separate onError or onSuccess hooks. Instead, the onAfterTask hook receives an ExecutionResult object with a success boolean. You branch on this property to handle success or failure in one location, ensuring your cleanup code always runs regardless of outcome.
Can I use async functions in lifecycle hooks?
Yes. All lifecycle hooks except onDispose support async functions returning Promise<void>. The agent awaits these promises at the appropriate execution points (e.g., before starting a step or after task completion). The onDispose hook is synchronous and intended for immediate cleanup.
How do I ensure my hook runs before every step in a multi-page browser extension?
Use the onBeforeStep hook in MultiPageAgent to synchronize tab state. As shown in packages/extension/src/agent/MultiPageAgent.ts (lines 77–80), you can call tabsController.waitUntilTabLoaded() inside onBeforeStep to guarantee the active tab is ready before the agent performs observations.
Where are the hook signatures defined in the source code?
The TypeScript interfaces for all lifecycle hooks are defined in packages/core/src/types.ts between lines 70 and 114. The AgentConfig interface aggregates these optional hooks, and the ExecutionResult interface (used by onAfterTask) is declared nearby.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →