refactor(ext): drive heartbeat and running flag from statuschange
Project agent status into chrome.storage via a statuschange listener instead of pairing setup/teardown across lifecycle hooks. A throwing hook can no longer leak the heartbeat or strand isAgentRunning, and rejected concurrent execute() calls never touch the active run's state.
This commit is contained in:
@@ -39,15 +39,6 @@ export class MultiPageAgent extends PageAgentCore {
|
||||
const includeInitialTab = config.includeInitialTab ?? true
|
||||
const experimentalIncludeAllTabs = config.experimentalIncludeAllTabs ?? false
|
||||
|
||||
/**
|
||||
* When the agent is in side-panel and user closed the side-panel.
|
||||
* There is no chance for isAgentRunning to be set false.
|
||||
* (unload event doesn't work well in side panel.)
|
||||
* (I'm trying not to use long-lived connection because the lifecycle of a sw is hard to predict.)
|
||||
* This heartbeat mechanism acts as a backup.
|
||||
*/
|
||||
let heartBeatInterval: null | number = null
|
||||
|
||||
super({
|
||||
...config,
|
||||
pageController: pageController as any,
|
||||
@@ -56,27 +47,6 @@ export class MultiPageAgent extends PageAgentCore {
|
||||
|
||||
onBeforeTask: async (agent) => {
|
||||
await tabsController.init(agent.task, { includeInitialTab, experimentalIncludeAllTabs })
|
||||
|
||||
heartBeatInterval = window.setInterval(() => {
|
||||
chrome.storage.local.set({
|
||||
agentHeartbeat: Date.now(),
|
||||
})
|
||||
}, 1_000)
|
||||
|
||||
await chrome.storage.local.set({
|
||||
isAgentRunning: true,
|
||||
})
|
||||
},
|
||||
|
||||
onAfterTask: async () => {
|
||||
if (heartBeatInterval) {
|
||||
window.clearInterval(heartBeatInterval)
|
||||
heartBeatInterval = null
|
||||
}
|
||||
|
||||
await chrome.storage.local.set({
|
||||
isAgentRunning: false,
|
||||
})
|
||||
},
|
||||
|
||||
onBeforeStep: async (agent) => {
|
||||
@@ -86,17 +56,35 @@ export class MultiPageAgent extends PageAgentCore {
|
||||
},
|
||||
|
||||
onDispose: () => {
|
||||
if (heartBeatInterval) {
|
||||
window.clearInterval(heartBeatInterval)
|
||||
heartBeatInterval = null
|
||||
}
|
||||
|
||||
chrome.storage.local.set({
|
||||
isAgentRunning: false,
|
||||
})
|
||||
|
||||
tabsController.dispose()
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Project agent status into chrome.storage. The content script polls
|
||||
* `isAgentRunning` + `agentHeartbeat` (eventually consistent by design).
|
||||
*
|
||||
* When the agent is in side-panel and user closed the side-panel.
|
||||
* There is no chance for isAgentRunning to be set false.
|
||||
* (unload event doesn't work well in side panel.)
|
||||
* (I'm trying not to use long-lived connection because the lifecycle of a sw is hard to predict.)
|
||||
* This heartbeat mechanism acts as a backup.
|
||||
*/
|
||||
let heartBeatInterval: number | null = null
|
||||
|
||||
this.addEventListener('statuschange', () => {
|
||||
const running = this.status === 'running'
|
||||
|
||||
if (running && !heartBeatInterval) {
|
||||
heartBeatInterval = window.setInterval(() => {
|
||||
void chrome.storage.local.set({ agentHeartbeat: Date.now() })
|
||||
}, 1_000)
|
||||
} else if (!running && heartBeatInterval) {
|
||||
clearInterval(heartBeatInterval)
|
||||
heartBeatInterval = null
|
||||
}
|
||||
|
||||
chrome.storage.local.set({ isAgentRunning: running }).catch(console.error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user