feat(ext): handling page reload/redirect/close

This commit is contained in:
Simon
2026-01-21 18:46:50 +08:00
parent 570c79623b
commit 3fea74faa9
5 changed files with 250 additions and 18 deletions

View File

@@ -17,11 +17,14 @@ import {
type AgentStatus,
type HistoricalEvent,
agentCommands,
contentScriptQuery,
} from '../messaging/protocol'
import { DEMO_API_KEY, DEMO_BASE_URL, DEMO_MODEL } from '../utils/constants'
// Agent instance (singleton for now - single page control)
let agent: PageAgentCore | null = null
// Track the target tab ID for event filtering
let targetTabId: number | null = null
// LLM configuration (persisted in storage)
interface LLMConfig {
@@ -46,6 +49,12 @@ export default defineBackground(() => {
// Register command handlers
registerCommandHandlers()
// Register tab event listeners for page reload/close detection
registerTabEventListeners()
// Register content script notification handlers
registerContentScriptHandlers()
// Open sidepanel on action click
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: true })
@@ -99,6 +108,12 @@ function getAgentState(): AgentState {
function createAgent(): PageAgentCore {
const pageController = new RemotePageController()
// Track the target tab ID for event filtering
pageController.tabIdPromise.then((tabId) => {
targetTabId = tabId
console.log('[PageAgentExt] Tracking tab:', tabId)
})
const newAgent = new PageAgentCore({
...llmConfig,
pageController: pageController as any, // Type assertion for interface compatibility
@@ -122,6 +137,7 @@ function createAgent(): PageAgentCore {
newAgent.addEventListener('dispose', () => {
if (agent === newAgent) {
agent = null
targetTabId = null
}
eventBroadcaster.status('idle')
})
@@ -180,3 +196,53 @@ function registerCommandHandlers(): void {
console.log('[PageAgentExt] Command handlers registered')
}
/**
* Register tab event listeners for detecting page reload/navigation/close
*/
function registerTabEventListeners(): void {
// Listen for tab updates (page reload, navigation)
chrome.tabs.onUpdated.addListener((tabId, changeInfo, _tab) => {
// Only handle events for the target tab when agent is running
if (!agent || agent.disposed || tabId !== targetTabId) return
if (changeInfo.status === 'loading') {
// Page is reloading or navigating
console.log('[PageAgentExt] Target page is reloading/navigating')
agent.pushObservation(
'⚠️ Page is reloading. DOM state will change - wait for page to stabilize before next action.'
)
}
})
// Listen for tab close
chrome.tabs.onRemoved.addListener((tabId, _removeInfo) => {
// Only handle events for the target tab when agent is running
if (!agent || agent.disposed || tabId !== targetTabId) return
console.log('[PageAgentExt] Target page was closed')
agent.pushObservation(
'⚠️ Target page was closed by user. If this page is required for the task, consider marking the task as failed.'
)
// Clear target tab ID since it no longer exists
targetTabId = null
})
console.log('[PageAgentExt] Tab event listeners registered')
}
/**
* Register handlers for content script queries
*/
function registerContentScriptHandlers(): void {
// Handle shouldShowMask query - content script asks if mask should be shown
contentScriptQuery.onMessage('content:shouldShowMask', async ({ sender }) => {
const tabId = sender.tab?.id
// Check if there's an active task for this tab
const shouldShow = Boolean(tabId && agent && !agent.disposed && tabId === targetTabId)
console.log('[PageAgentExt] shouldShowMask query:', { tabId, targetTabId, shouldShow })
return shouldShow
})
console.log('[PageAgentExt] Content script handlers registered')
}

View File

@@ -9,13 +9,13 @@
*/
import { PageController } from '@page-agent/page-controller'
import { pageControllerRPC } from '../messaging/protocol'
import { contentScriptQuery, pageControllerRPC } from '../messaging/protocol'
export default defineContentScript({
matches: ['<all_urls>'],
runAt: 'document_idle',
main() {
async main() {
console.log('[PageAgentExt] Content script loaded')
// Lazy-initialized controller - created on demand, disposed between tasks
@@ -40,6 +40,24 @@ export default defineContentScript({
}
)
// Check if there's an active task that needs mask to be shown
// This handles page reload/navigation during task execution
setTimeout(async () => {
try {
const shouldShowMask = await contentScriptQuery.sendMessage(
'content:shouldShowMask',
undefined
)
if (shouldShowMask) {
console.log('[PageAgentExt] Restoring mask after page reload')
await getController().showMask()
}
} catch (error) {
// Ignore errors - background may not be ready
console.log('[PageAgentExt] shouldShowMask check skipped:', error)
}
}, 100)
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
controller?.dispose()