How to Use the Page-Agent Event System for Monitoring: statuschange, historychange, and activity Events
The Page-Agent event system exposes three DOM-style events—statuschange, historychange, and activity—that you can monitor using standard addEventListener calls on any PageAgent or PageAgentCore instance.
The alibaba/page-agent repository provides a browser automation framework where the core execution logic and UI layer communicate through a robust event architecture. By tapping into the Page-Agent event system, you can build real-time monitoring dashboards, audit logs, or custom UI components without modifying the agent's internal Re-Act loop. This system uses the standard EventTarget API, allowing multiple independent listeners to subscribe to state changes, history mutations, and transient activities.
The Three Core Monitoring Events
The event system centers on three distinct event types, each serving a specific monitoring purpose. These events are dispatched from the PageAgentCore class and bubble up through the PageAgent UI wrapper.
statuschange
The statuschange event fires whenever the agent transitions between lifecycle states: idle, running, completed, or error. According to the source code in packages/core/src/PageAgentCore.ts, this event is emitted by the private #emitStatusChange() method immediately after the internal #status field updates.
This event carries no custom payload—just a standard Event object. Use it to drive global state indicators, such as showing a "thinking" spinner when the status shifts to running or displaying a success banner when it reaches completed.
historychange
The historychange event triggers after any mutation to the agent's internal history array (typed as HistoricalEvent[]). In PageAgentCore.ts, the #emitHistoryChange() method dispatches this event following operations like pushing a new step, flushing an observation, or recording a retry.
Like statuschange, this emits a plain Event object without additional data. Access the agent.history property in your handler to inspect the full sequence of steps. This event is ideal for persisting execution logs to a remote analytics service or refreshing a timeline view in a debug panel.
activity
The activity event provides granular, transient feedback about the current operation, such as "clicking element #3" or "retry (2/3)". This is dispatched by #emitActivity() in PageAgentCore.ts and carries a CustomEvent<AgentActivity> payload where the detail field contains the activity record.
Use this event for real-time status bars that display tool execution progress, wait times, or error recovery attempts. The Panel class in packages/ui/src/panel/Panel.ts uses this pattern to update the on-page UI header and status indicators.
Event Emission Points in the Source Code
Understanding where these events originate helps you predict when they fire during the agent lifecycle.
In packages/core/src/PageAgentCore.ts:
- Lines 52-56:
#emitStatusChange()updates#statusand dispatchesstatuschange - Lines 57-60:
#emitHistoryChange()mutatesthis.historyand dispatcheshistorychange - Lines 66-68:
#emitActivity()creates aCustomEventwithAgentActivitydetail and dispatchesactivity
The Panel component in packages/ui/src/panel/Panel.ts (lines 83-88) demonstrates production usage by registering listeners for all three events to synchronize the DOM with the agent state. Similarly, browser extension implementations in packages/extension/src/agent/useAgent.ts (lines 94-100) forward these events to background scripts for cross-tab monitoring.
Practical Implementation Examples
Basic Listener Setup
Attach listeners immediately after instantiating the agent. This example shows all three event types in a standard TypeScript setup:
import { PageAgent } from '@page-agent/page-agent'
const agent = new PageAgent({ pageController })
// Monitor lifecycle state
agent.addEventListener('statuschange', () => {
console.log('Agent status:', agent.status)
})
// Monitor history mutations
agent.addEventListener('historychange', () => {
const lastEntry = agent.history[agent.history.length - 1]
console.log('History updated:', lastEntry.type, lastEntry)
})
// Monitor transient activities
agent.addEventListener('activity', (e) => {
const activity = (e as CustomEvent<any>).detail
console.log(`Activity: ${activity.type}`, activity)
})
Centralized Monitoring Service
For production monitoring, create a dedicated module that streams events to a log file or remote endpoint:
import { PageAgent } from '@page-agent/page-agent'
import { createWriteStream } from 'fs'
export function attachMonitoring(agent: PageAgent) {
const logStream = createWriteStream('agent-audit.log', { flags: 'a' })
agent.addEventListener('statuschange', () => {
logStream.write(`[${new Date().toISOString()}] STATUS ${agent.status}\n`)
})
agent.addEventListener('historychange', () => {
const last = agent.history[agent.history.length - 1]
logStream.write(
`[${new Date().toISOString()}] HISTORY ${last.type} ${JSON.stringify(last)}\n`
)
})
agent.addEventListener('activity', (e) => {
const a = (e as CustomEvent<any>).detail
logStream.write(`[${new Date().toISOString()}] ACTIVITY ${a.type} ${a.tool || ''}\n`)
})
}
React Component Integration
Sync a React UI with the agent using useEffect to manage listener subscriptions:
import { useEffect, useState } from 'react'
import { PageAgent } from '@page-agent/page-agent'
export const AgentMonitor: React.FC<{ agent: PageAgent }> = ({ agent }) => {
const [status, setStatus] = useState(agent.status)
const [currentActivity, setCurrentActivity] = useState<string>('idle')
useEffect(() => {
const handleStatus = () => setStatus(agent.status)
const handleActivity = (e: Event) => {
const detail = (e as CustomEvent<any>).detail
setCurrentActivity(`${detail.type}${detail.tool ? ` (${detail.tool})` : ''}`)
}
agent.addEventListener('statuschange', handleStatus)
agent.addEventListener('activity', handleActivity)
return () => {
agent.removeEventListener('statuschange', handleStatus)
agent.removeEventListener('activity', handleActivity)
}
}, [agent])
return (
<div className="monitor-bar">
<strong>Status:</strong> {status} | <strong>Activity:</strong> {currentActivity}
</div>
)
}
Summary
- Three event types drive the Page-Agent event system:
statuschangefor lifecycle states,historychangefor step mutations, andactivityfor transient operations. - EventTarget API compatibility means you use standard
addEventListenerandremoveEventListenermethods on any agent instance. - Source locations: Events emit from
PageAgentCore.tsvia private methods#emitStatusChange(),#emitHistoryChange(), and#emitActivity(). - UI integration: The reference implementation in
Panel.tsdemonstrates how to transform these events into DOM updates. - Monitoring use cases: Attach listeners for audit logging, real-time dashboards, or browser extension messaging without altering core agent logic.
Frequently Asked Questions
What data does the activity event payload contain?
The activity event dispatches a CustomEvent where the detail property contains an AgentActivity object. This object includes fields like type (e.g., "thinking", "executing", "retrying"), tool (the tool name when executing), and retry counters. The exact TypeScript definitions are available in packages/core/src/types.ts.
Can I attach multiple listeners to the same event?
Yes. Because PageAgentCore extends the standard EventTarget interface, you can register any number of listeners for statuschange, historychange, or activity. Each listener executes independently when the event fires, making it safe to combine monitoring services with UI updates.
How do I stop monitoring when the agent completes?
Call agent.removeEventListener(eventName, handler) for each subscribed event, passing the same handler reference used in addEventListener. In React components, return the cleanup function from useEffect to ensure listeners detach when the component unmounts or the agent instance changes.
Does the event system work with PageAgentCore directly, or only the UI wrapper?
The event system works with both. PageAgent is a UI-enhanced wrapper around PageAgentCore, but both classes inherit from EventTarget. You can attach listeners to a bare PageAgentCore instance if you are running headless monitoring without the DOM panel, as shown in the Node.js service example.
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 →