diff --git a/packages/gpt-control-driver/src/driver.ts b/packages/gpt-control-driver/src/driver.ts index cc9bde9..4fd3a96 100755 --- a/packages/gpt-control-driver/src/driver.ts +++ b/packages/gpt-control-driver/src/driver.ts @@ -358,6 +358,18 @@ export function extractAssistantTurn(html: string): { text: string; imageUrls: s return { text, imageUrls: images, fingerprint: `${text}\u0000${images.join(",")}` }; } +export function nextAssistantProgress( + current: { assistantCount: number; lastAssistantFingerprint: string; awaitingAssistant: boolean }, + fingerprint: string, +): { assistantCount: number; lastAssistantFingerprint: string; awaitingAssistant: false } | undefined { + if (fingerprint === current.lastAssistantFingerprint) return undefined; + return { + assistantCount: current.assistantCount + (current.awaitingAssistant ? 1 : 0), + lastAssistantFingerprint: fingerprint, + awaitingAssistant: false, + }; +} + async function readAssistant(session: DriverSession): Promise<{ count: number; text: string; imageUrls: string[] }> { const client = await connectSession(session.sessionId); try { @@ -379,19 +391,13 @@ async function readAssistant(session: DriverSession): Promise<{ count: number; t } const turn = extractAssistantTurn(snapshot.content); const metadata = await readMetadata(session.sessionId); - let assistantCount = metadata.assistantCount; - if (metadata.awaitingAssistant && turn.fingerprint !== metadata.lastAssistantFingerprint) { - assistantCount += 1; - } - if (turn.fingerprint !== metadata.lastAssistantFingerprint || metadata.awaitingAssistant) { - await writeMetadata({ - ...metadata, - assistantCount, - lastAssistantFingerprint: turn.fingerprint, - awaitingAssistant: false, - }); - } - return { count: assistantCount, text: turn.text, imageUrls: turn.imageUrls }; + const progress = nextAssistantProgress(metadata, turn.fingerprint); + if (progress) await writeMetadata({ ...metadata, ...progress }); + return { + count: progress?.assistantCount ?? metadata.assistantCount, + text: turn.text, + imageUrls: turn.imageUrls, + }; } finally { client.close(); } diff --git a/packages/gpt-control-driver/test/driver.test.ts b/packages/gpt-control-driver/test/driver.test.ts index 1ecdb90..f1f008d 100644 --- a/packages/gpt-control-driver/test/driver.test.ts +++ b/packages/gpt-control-driver/test/driver.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { join } from "node:path"; -import { extractAssistantTurn } from "../src/driver.js"; +import { extractAssistantTurn, nextAssistantProgress } from "../src/driver.js"; describe("extractAssistantTurn", () => { test("returns only assistant prose and approved generated images", () => { @@ -26,6 +26,40 @@ describe("extractAssistantTurn", () => { }); }); +describe("nextAssistantProgress", () => { + test("keeps waiting when polling still sees the previous assistant turn", () => { + expect(nextAssistantProgress({ + assistantCount: 2, + lastAssistantFingerprint: "previous", + awaitingAssistant: true, + }, "previous")).toBeUndefined(); + }); + + test("counts and records a new assistant turn", () => { + expect(nextAssistantProgress({ + assistantCount: 2, + lastAssistantFingerprint: "previous", + awaitingAssistant: true, + }, "next")).toEqual({ + assistantCount: 3, + lastAssistantFingerprint: "next", + awaitingAssistant: false, + }); + }); + + test("records baseline content without incrementing the assistant count", () => { + expect(nextAssistantProgress({ + assistantCount: 0, + lastAssistantFingerprint: "", + awaitingAssistant: false, + }, "baseline")).toEqual({ + assistantCount: 0, + lastAssistantFingerprint: "baseline", + awaitingAssistant: false, + }); + }); +}); + test("rejects browser sessions outside the GPT-Control ownership namespace", async () => { const child = Bun.spawn( [process.execPath, join(import.meta.dir, "../src/driver.ts")],