fix(agent-browser): restore bounded presentation lifecycle
This commit is contained in:
@@ -4,10 +4,17 @@ import type { CodingAttachmentStore } from '../../../coding-projects/attachment-
|
||||
import type { AgentBrowserDetailsV1 } from '../../contracts';
|
||||
|
||||
export interface AgentBrowserToolContext {
|
||||
conversationId: string;
|
||||
runId: string;
|
||||
projectId: string;
|
||||
projectPath: string;
|
||||
}
|
||||
|
||||
export type AgentBrowserPresentationRequester = (snapshot: AgentBrowserSnapshot) => void;
|
||||
export type AgentBrowserStatePublisher = (snapshot: AgentBrowserSnapshot) => void;
|
||||
|
||||
const PRESENTATION_TIMEOUT_MS = 5_000;
|
||||
|
||||
export interface AgentBrowserToolResult {
|
||||
content: Array<{ type: 'text'; text: string }>;
|
||||
details: AgentBrowserDetailsV1;
|
||||
@@ -70,11 +77,27 @@ async function readPayloadJson(
|
||||
}
|
||||
|
||||
export class PiAgentBrowserTool {
|
||||
private readonly diagnosticRuns = new Map<string, { projectPath: string; owner: string }>();
|
||||
|
||||
constructor(
|
||||
private readonly browser: AgentBrowserModule,
|
||||
private readonly attachments: CodingAttachmentStore,
|
||||
private readonly requestPresentation?: AgentBrowserPresentationRequester,
|
||||
private readonly publishState?: AgentBrowserStatePublisher,
|
||||
) {}
|
||||
|
||||
async releaseRun(conversationId: string, runId: string): Promise<void> {
|
||||
const key = `${conversationId}:${runId}`;
|
||||
const diagnostic = this.diagnosticRuns.get(key);
|
||||
if (!diagnostic) return;
|
||||
this.diagnosticRuns.delete(key);
|
||||
await this.browser.setDiagnostics({
|
||||
projectPath: diagnostic.projectPath,
|
||||
enabled: false,
|
||||
owner: diagnostic.owner,
|
||||
}).catch(() => undefined);
|
||||
}
|
||||
|
||||
async execute(
|
||||
context: AgentBrowserToolContext,
|
||||
input: unknown,
|
||||
@@ -85,20 +108,63 @@ export class PiAgentBrowserTool {
|
||||
return result(action, publicSnapshot(await this.browser.getSnapshot(context.projectPath)));
|
||||
}
|
||||
if (action === 'open') {
|
||||
const snapshot = await this.browser.open({
|
||||
const owner = `agent:${context.conversationId}:${context.runId}`;
|
||||
let snapshot = await this.browser.open({
|
||||
projectId: context.projectId,
|
||||
projectPath: context.projectPath,
|
||||
url: string(body.url, true) as string,
|
||||
visible: false,
|
||||
diagnosticsOwner: owner,
|
||||
...(body.injectProjectData === true ? { injectProjectData: true } : {}),
|
||||
});
|
||||
const runKey = `${context.conversationId}:${context.runId}`;
|
||||
this.diagnosticRuns.set(runKey, { projectPath: context.projectPath, owner });
|
||||
if (this.requestPresentation) {
|
||||
try {
|
||||
this.requestPresentation(snapshot);
|
||||
snapshot = await this.browser.waitForPresentation({
|
||||
projectPath: context.projectPath,
|
||||
generation: snapshot.generation,
|
||||
timeoutMs: PRESENTATION_TIMEOUT_MS,
|
||||
});
|
||||
} catch (error) {
|
||||
this.diagnosticRuns.delete(runKey);
|
||||
const closed = await this.browser.close(context.projectPath).catch(() => null);
|
||||
if (closed) {
|
||||
this.publishState?.({
|
||||
...closed,
|
||||
projectId: context.projectId,
|
||||
projectPath: context.projectPath,
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return result(action, publicSnapshot(snapshot));
|
||||
}
|
||||
if (action === 'close') {
|
||||
return result(action, publicSnapshot(await this.browser.close(context.projectPath)));
|
||||
for (const [key, diagnostic] of this.diagnosticRuns) {
|
||||
if (diagnostic.projectPath === context.projectPath) this.diagnosticRuns.delete(key);
|
||||
}
|
||||
const snapshot = await this.browser.close(context.projectPath);
|
||||
this.publishState?.({
|
||||
...snapshot,
|
||||
projectId: context.projectId,
|
||||
projectPath: context.projectPath,
|
||||
});
|
||||
return result(action, publicSnapshot(snapshot));
|
||||
}
|
||||
if (action === 'reset_profile') {
|
||||
return result(action, publicSnapshot(await this.browser.resetProfile(context.projectPath)));
|
||||
for (const [key, diagnostic] of this.diagnosticRuns) {
|
||||
if (diagnostic.projectPath === context.projectPath) this.diagnosticRuns.delete(key);
|
||||
}
|
||||
const snapshot = await this.browser.resetProfile(context.projectPath);
|
||||
this.publishState?.({
|
||||
...snapshot,
|
||||
projectId: context.projectId,
|
||||
projectPath: context.projectPath,
|
||||
});
|
||||
return result(action, publicSnapshot(snapshot));
|
||||
}
|
||||
if (action === 'navigate') {
|
||||
const navigation = string(body.navigation, true);
|
||||
@@ -110,6 +176,7 @@ export class PiAgentBrowserTool {
|
||||
action: navigation as 'url' | 'back' | 'forward' | 'reload',
|
||||
...(body.url === undefined ? {} : { url: string(body.url) }),
|
||||
});
|
||||
this.publishState?.(snapshot);
|
||||
return result(action, publicSnapshot(snapshot));
|
||||
}
|
||||
if (action === 'read_events') {
|
||||
|
||||
Reference in New Issue
Block a user