@@ -1103,6 +1103,79 @@ test('remaining model-facing screenshot fallbacks apply redaction', () => {
11031103 'Firefox done verification should not embed base64 in the done result');
11041104});
11051105
1106+ test('Firefox done verifies inactive scheduled tabs before accepting completion', async () => {
1107+ const originalBrowser = globalThis.browser;
1108+ const tabId = 417;
1109+ let executeScriptCalls = 0;
1110+ const captures = [];
1111+ try {
1112+ globalThis.browser = {
1113+ tabs: {
1114+ get: async () => ({
1115+ id: tabId,
1116+ active: false,
1117+ url: 'https://example.test/items/new',
1118+ title: 'Create item',
1119+ }),
1120+ executeScript: async () => {
1121+ executeScriptCalls += 1;
1122+ return [{
1123+ url: 'https://example.test/items/new',
1124+ title: 'Create item',
1125+ openDialogCount: 1,
1126+ dialogTitles: ['Create item'],
1127+ visibleFormCount: 1,
1128+ relevantFormCount: 1,
1129+ formDescriptors: [{
1130+ label: 'Create item',
1131+ relevant: true,
1132+ utility: false,
1133+ editableCount: 2,
1134+ submitCount: 1,
1135+ }],
1136+ liveRegionMessages: [],
1137+ successMessages: [],
1138+ }];
1139+ },
1140+ captureTab: async (capturedTabId, options) => {
1141+ captures.push({ tabId: capturedTabId, options });
1142+ return 'data:image/png;base64,AA==';
1143+ },
1144+ sendMessage: async () => ({}),
1145+ },
1146+ };
1147+
1148+ const agent = new AgentFx({
1149+ getActive: () => ({ supportsVision: true }),
1150+ getVisionProvider: async () => null,
1151+ });
1152+ agent.conversationModes.set(tabId, 'act');
1153+
1154+ const result = await agent.executeTool(tabId, 'done', {
1155+ summary: 'Created the item.',
1156+ outcome: 'success',
1157+ });
1158+
1159+ assert.equal(executeScriptCalls, 1,
1160+ 'inactive Firefox runs should still probe dialogs and forms before done');
1161+ assert.deepEqual(captures, [{
1162+ tabId,
1163+ options: { format: 'png', quality: 80 },
1164+ }], 'inactive Firefox runs should capture their own tab for done verification');
1165+ assert.equal(result.blockedDone, true,
1166+ 'an unfinished inactive-tab form should block a successful done result');
1167+
1168+ const source = fs.readFileSync(path.join(ROOT, 'src/firefox/src/agent/agent.js'), 'utf8');
1169+ const doneStart = source.indexOf("if (name === 'done')");
1170+ const doneEnd = source.indexOf('// Network & download tools', doneStart);
1171+ assert.doesNotMatch(source.slice(doneStart, doneEnd), /if \(tab\?\.active\)/,
1172+ 'Firefox done verification must not be gated on active-tab state');
1173+ } finally {
1174+ if (originalBrowser === undefined) delete globalThis.browser;
1175+ else globalThis.browser = originalBrowser;
1176+ }
1177+ });
1178+
11061179test('firefox auto and media screenshot helpers redact model-facing data URLs', () => {
11071180 const source = fs.readFileSync(path.join(ROOT, 'src/firefox/src/agent/agent.js'), 'utf8');
11081181 // Signature may include optional opts for Chrome call-site parity.
0 commit comments