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 #status and dispatches statuschange
  • Lines 57-60: #emitHistoryChange() mutates this.history and dispatches historychange
  • Lines 66-68: #emitActivity() creates a CustomEvent with AgentActivity detail and dispatches activity

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: statuschange for lifecycle states, historychange for step mutations, and activity for transient operations.
  • EventTarget API compatibility means you use standard addEventListener and removeEventListener methods on any agent instance.
  • Source locations: Events emit from PageAgentCore.ts via private methods #emitStatusChange(), #emitHistoryChange(), and #emitActivity().
  • UI integration: The reference implementation in Panel.ts demonstrates 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →