// ==UserScript== // @name Unified AI C2 Harness // @namespace http://tampermonkey.net/ // @version 1.3 // @description Unified C2 harness for Google AI Search, ChatGPT, DeepSeek, and Google Gemini. // @author Vanguard // @match https://*.google.com/* // @match http://*.google.com/* // @match https://chatgpt.com/* // @match https://chat.openai.com/* // @match https://chat.deepseek.com/* // @match https://gemini.google.com/* // @connect localhost // @connect 127.0.0.1 // @grant GM_xmlhttpRequest // @run-at document-idle // ==/UserScript== (function() { 'use strict'; if (window.top !== window.self) { return; } const hostname = window.location.hostname; let currentPlatform = null; if (hostname.includes('chatgpt.com') || hostname.includes('chat.openai.com')) { currentPlatform = 'ChatGPT'; } else if (hostname.includes('chat.deepseek.com')) { currentPlatform = 'DeepSeek'; } else if (hostname.includes('gemini.google.com')) { currentPlatform = 'Gemini'; } else if (hostname.includes('google.com')) { currentPlatform = 'Google'; } else { return; // Not a supported platform } console.log(`[Harness] Initializing Unified Harness for ${currentPlatform}...`); const API_BASE = "http://localhost:8080/api/relay"; const POLLING_INTERVAL = 1000; const STATE_INTERVAL = 2000; let isBusy = false; let isPolling = false; let pollIntervalId = null; let stateIntervalId = null; let PLATFORM_NAME = ""; let ID_PREFIX = ""; let activeActions = {}; let activeTactics = null; let reportStateLogic = null; // --- TAMPERMONKEY FETCH WRAPPER (CORS BYPASS) --- function tmFetch(url, options = {}) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: options.method || 'GET', url: url, headers: options.headers || {}, data: options.body, onload: function(response) { resolve({ status: response.status, json: () => { try { return Promise.resolve(JSON.parse(response.responseText)); } catch (e) { return Promise.resolve({}); } }, text: () => Promise.resolve(response.responseText) }); }, onerror: function(error) { reject(error); } }); }); } // --- DOM TACTICS --- const standardTactics = { setNativeValue: (element, value) => { element.focus(); let success = false; try { success = document.execCommand('insertText', false, value); } catch (e) {} if (!success) { const valueSetter = Object.getOwnPropertyDescriptor(element, 'value')?.set; const prototype = Object.getPrototypeOf(element); const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set; if (prototypeValueSetter && valueSetter !== prototypeValueSetter) { prototypeValueSetter.call(element, value); } else if (valueSetter) { valueSetter.call(element, value); } else { element.value = value; } } element.dispatchEvent(new Event('input', { bubbles: true })); element.dispatchEvent(new Event('change', { bubbles: true })); } }; const contentEditableTactics = { setNativeValue: (element, value) => { element.focus(); // Safely select only the contents of the target element const selection = window.getSelection(); const range = document.createRange(); range.selectNodeContents(element); selection.removeAllRanges(); selection.addRange(range); try { document.execCommand('delete', false, null); document.execCommand('insertText', false, value); } catch (e) { console.error("[Harness] execCommand failed, falling back to textContent", e); element.textContent = value; } element.dispatchEvent(new Event('input', { bubbles: true })); element.dispatchEvent(new Event('change', { bubbles: true })); } }; // --- SMART EXTRACTION (Markdown Parsers) --- function googleHtmlToMarkdown(node) { if (node.nodeType === Node.TEXT_NODE) return node.textContent; if (node.nodeType !== Node.ELEMENT_NODE) return ""; const tag = node.tagName.toUpperCase(); if (tag === 'BUTTON' || tag === 'SVG' || tag === 'STYLE' || tag === 'SCRIPT' || tag === 'NOSCRIPT') return ""; if (node.style && node.style.display === 'none') return ""; if (node.getAttribute('aria-hidden') === 'true') return ""; if (node.classList) { if (node.classList.contains('DHPVt') || node.classList.contains('YHsVn') || node.classList.contains('bQ0Yzc') || node.classList.contains('CxFouc') || node.classList.contains('ZFcyjd') || node.classList.contains('lHqILb')) return ""; } if (node.hasAttribute('data-xpm-latex')) return node.getAttribute('data-xpm-latex'); if (tag === 'PRE') return `\n\n\`\`\`\n${node.textContent}\n\`\`\`\n\n`; if (tag === 'BR') return '\n'; let md = ""; for (let child of node.childNodes) md += googleHtmlToMarkdown(child); if (tag === 'P' || tag === 'DIV') { const displayStyle = node.style ? node.style.display : ''; if (displayStyle.includes('inline')) return md; return `\n\n${md}\n\n`; } if (tag === 'STRONG' || tag === 'B') return `**${md}**`; if (tag === 'EM' || tag === 'I') return `*${md}*`; if (tag === 'CODE') return `\`${md}\``; if (tag.match(/^H[1-6]$/)) return `\n\n${'#'.repeat(parseInt(tag[1]))} ${md}\n\n`; if (tag === 'LI') return `\n- ${md.trim()}`; if (tag === 'UL' || tag === 'OL') return `\n${md}\n`; if (tag === 'A') return `[${md}](${node.getAttribute('href') || ''})`; return md; } function chatgptHtmlToMarkdown(node) { if (node.nodeType === Node.TEXT_NODE) return node.textContent; if (node.nodeType !== Node.ELEMENT_NODE) return ""; const tag = node.tagName.toUpperCase(); if (tag === 'BUTTON' || tag === 'SVG' || tag === 'STYLE' || tag === 'SCRIPT' || tag === 'NOSCRIPT') return ""; if (node.style && node.style.display === 'none') return ""; if (node.getAttribute('aria-hidden') === 'true') return ""; if (tag === 'PRE') { const codeNode = node.querySelector('code'); const codeText = codeNode ? codeNode.textContent : node.textContent; return `\n\n\`\`\`\n${codeText}\n\`\`\`\n\n`; } if (tag === 'BR') return '\n'; let md = ""; for (let child of node.childNodes) md += chatgptHtmlToMarkdown(child); if (tag === 'P' || tag === 'DIV') return `\n\n${md}\n\n`; if (tag === 'STRONG' || tag === 'B') return `**${md}**`; if (tag === 'EM' || tag === 'I') return `*${md}*`; if (tag === 'CODE') return `\`${md}\``; if (tag.match(/^H[1-6]$/)) return `\n\n${'#'.repeat(parseInt(tag[1]))} ${md}\n\n`; if (tag === 'LI') return `\n- ${md.trim()}`; if (tag === 'UL' || tag === 'OL') return `\n${md}\n`; if (tag === 'A') { const href = node.getAttribute('href') || ''; const cleanMd = md.trim(); if (cleanMd && (href.includes(cleanMd) || cleanMd.includes(href))) return md; return `[${md}](${href})`; } return md; } function deepseekHtmlToMarkdown(node) { if (node.nodeType === Node.TEXT_NODE) return node.textContent; if (node.nodeType !== Node.ELEMENT_NODE) return ""; const tag = node.tagName.toUpperCase(); if (tag === 'BUTTON' || tag === 'SVG' || tag === 'STYLE' || tag === 'SCRIPT' || tag === 'NOSCRIPT') return ""; if (node.style && node.style.display === 'none') return ""; if (node.getAttribute('aria-hidden') === 'true') return ""; if (tag === 'PRE') return `\n\n\`\`\`\n${node.textContent}\n\`\`\`\n\n`; if (tag === 'BR') return '\n'; let md = ""; for (let child of node.childNodes) md += deepseekHtmlToMarkdown(child); if (tag === 'P' || tag === 'DIV') return `\n\n${md}\n\n`; if (tag === 'STRONG' || tag === 'B') return `**${md}**`; if (tag === 'EM' || tag === 'I') return `*${md}*`; if (tag === 'CODE') return `\`${md}\``; if (tag.match(/^H[1-6]$/)) return `\n\n${'#'.repeat(parseInt(tag[1]))} ${md}\n\n`; if (tag === 'LI') return `\n- ${md.trim()}`; if (tag === 'UL' || tag === 'OL') return `\n${md}\n`; if (tag === 'A') { const href = node.getAttribute('href') || ''; const cleanMd = md.trim(); if (cleanMd && (href.includes(cleanMd) || cleanMd.includes(href))) return md; return `[${md}](${href})`; } return md; } // --- ACTION MODULES --- const googleActions = { SHUTDOWN: async () => { clearInterval(pollIntervalId); clearInterval(stateIntervalId); console.log("[Harness] Shutdown command received. Loops stopped."); return "Harness shut down."; }, GENERATE: async (payload) => { const textareas = Array.from(document.querySelectorAll('textarea')).filter(el => el.offsetWidth > 0 && el.offsetHeight > 0); let input = textareas[textareas.length - 1]; if (!input) input = document.querySelector('input[name="q"]'); if (!input) return "Error: Prompt textarea not found on this Google page. SGE may not be active."; const preSubmitParagraphs = document.querySelectorAll('.n6owBd'); let preSubmitText = ""; let preSubmitNoResponseEl = null; const noRespSelector = '.Y3BBE, .luHWlf, [data-sfc-root], [jscontroller="zcfIf"]'; if (preSubmitParagraphs.length > 0) { preSubmitText = preSubmitParagraphs[preSubmitParagraphs.length - 1].parentElement.innerText || ""; } else { const noRespNodes = document.querySelectorAll(noRespSelector); for (let n of noRespNodes) { if (n.innerText && n.innerText.includes("no response available")) { preSubmitText = n.innerText; preSubmitNoResponseEl = n; break; } } } standardTactics.setNativeValue(input, payload); await new Promise(r => setTimeout(r, 800)); const submitSelectors = ['button.SAvKK', 'button.qmJOdc', 'button[aria-label="Submit"]', 'button[aria-label="Search"]']; let runBtn = null; for(let sel of submitSelectors) { const btns = Array.from(document.querySelectorAll(sel)).filter(b => b.offsetWidth > 0); for(let btn of btns) { if(!btn.disabled && btn.getAttribute('aria-disabled') !== 'true') { runBtn = btn; break; } } if(runBtn) break; } if (runBtn) { runBtn.click(); } else { console.log("[Harness] Submit button not found or disabled, dispatching Enter key fallback..."); const enterEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', code: 'Enter', keyCode: 13 }); input.dispatchEvent(enterEvent); } console.log("[Harness] Command sent. Entering Kinetic Wait (2500ms)..."); await new Promise(r => setTimeout(r, 2500)); console.log("[Harness] Wait complete. Engaging Semantic Lock..."); return new Promise((resolve) => { let lastTextLength = -1; let stabilityStreak = 0; const REQUIRED_STREAK = 1; let maxWait = 180; let isNewResponseStarted = false; const checkLoop = setInterval(() => { maxWait--; if (maxWait <= 0) { clearInterval(checkLoop); resolve("Error: Generation timed out or hung."); return; } const noResponseNodes = document.querySelectorAll(noRespSelector); let currentNoResponseEl = null; let currentNoResponseText = ""; for (let node of noResponseNodes) { if (node.innerText && node.innerText.includes("no response available")) { currentNoResponseEl = node; currentNoResponseText = node.innerText; break; } } if (currentNoResponseEl) { if (!isNewResponseStarted) { if (currentNoResponseText !== preSubmitText || currentNoResponseEl !== preSubmitNoResponseEl) { isNewResponseStarted = true; console.log("[Harness] Detected new 'no response' stream starting."); } else { console.log("[Harness] Waiting for old 'no response' to clear/change..."); return; } } if (isNewResponseStarted) { clearInterval(checkLoop); console.log("[Harness] Detected 'no response available' message. Returning empty response."); resolve(""); return; } } const paragraphs = document.querySelectorAll('.n6owBd'); if (paragraphs.length === 0) { console.log("[Harness] Waiting for SGE turn container..."); return; } const lastTurnContainer = paragraphs[paragraphs.length - 1].parentElement; const currentTextContent = lastTurnContainer.innerText || ""; const currentTextLength = currentTextContent.length; if (!isNewResponseStarted) { if (currentTextContent !== preSubmitText || currentTextContent === "" || currentTextLength < preSubmitText.length) { isNewResponseStarted = true; console.log("[Harness] Detected new response stream starting."); } else { console.log("[Harness] Waiting for old response to clear/change..."); return; } } const isTextGrowing = currentTextLength > lastTextLength; if (isTextGrowing) { console.log(`[Harness] Status: Streaming... (+${currentTextLength - lastTextLength})`); lastTextLength = currentTextLength; stabilityStreak = 0; } else if (currentTextLength === 0) { console.log(`[Harness] Status: Waiting for text generation to begin...`); stabilityStreak = 0; } else { stabilityStreak++; console.log(`[Harness] Status: Stable... (${stabilityStreak}/${REQUIRED_STREAK})`); lastTextLength = currentTextLength; } if (stabilityStreak >= REQUIRED_STREAK) { clearInterval(checkLoop); console.log("[Harness] Lock Released. Extraction initiated."); let turnMd = googleHtmlToMarkdown(lastTurnContainer); turnMd = turnMd.replace(/\n{3,}/g, '\n\n').trim(); resolve(turnMd); } }, 1000); }); }, NEW_CHAT: async () => { const newThreadBtn = document.querySelector('button[aria-label="New thread"]') || document.querySelector('.cV1Mfc'); if (newThreadBtn) { newThreadBtn.click(); return "Chat reset."; } return "Error: New thread button not found."; }, GET_CHAT_HISTORY: async () => { const history = []; const turns = document.querySelectorAll('.VndcI, .n6owBd'); turns.forEach(turn => { if (turn.classList.contains('VndcI')) { let text = turn.innerText.replace('You said:', '').trim(); if (text) history.push({ role: "User", text: text }); } else if (turn.classList.contains('n6owBd')) { let text = googleHtmlToMarkdown(turn).trim(); if (text) history.push({ role: "Model", text: text }); } }); return JSON.stringify(history); }, ANALYZE_SESSION: async () => { return JSON.stringify({ isHydrated: document.querySelectorAll('.n6owBd').length > 0, turnCount: document.querySelectorAll('.n6owBd').length, url: window.location.href }); }, GET_SETTINGS: async () => "{}", SET_SETTINGS: async () => "Success: Ignored in Google AI Search Mode", SET_URL_CONTEXT: async () => "Success: Ignored in Google AI Search Mode", SET_RESOLUTION: async () => "Success: Ignored in Google AI Search Mode", DELETE_TURN: async () => "Success: Ignored in Google AI Search Mode", CREATE_SHARD: async () => "Error: Context Sharding is not implemented for AI Search Mode", INJECT_SHARD: async () => "Error: Context Sharding is not implemented for AI Search Mode", UPLOAD_MEDIA: async () => "Error: Media Upload is not currently supported in AI Search Mode", INJECT_DOC: async () => "Error: Doc Injection is not currently supported in AI Search Mode", SCRAPE_HISTORY: async () => "[]" }; const chatgptActions = { SHUTDOWN: async () => { clearInterval(pollIntervalId); clearInterval(stateIntervalId); console.log("[Harness] Shutdown command received. Loops stopped."); return "Harness shut down."; }, GENERATE: async (payload) => { const input = document.querySelector('#prompt-textarea'); if (!input) return "Error: Prompt textarea not found on ChatGPT."; const preSubmitResponses = document.querySelectorAll('[data-message-author-role="assistant"]'); const preSubmitCount = preSubmitResponses.length; activeTactics.setNativeValue(input, payload); await new Promise(r => setTimeout(r, 500)); const sendBtn = document.querySelector('[data-testid="send-button"]'); if (sendBtn && !sendBtn.disabled) { sendBtn.click(); } else { console.log("[Harness] Submit button not found or disabled, dispatching Enter key fallback..."); const enterEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', code: 'Enter', keyCode: 13 }); input.dispatchEvent(enterEvent); } console.log("[Harness] Command sent. Entering Kinetic Wait (2000ms)..."); await new Promise(r => setTimeout(r, 2000)); console.log("[Harness] Wait complete. Engaging Semantic Lock..."); return new Promise((resolve) => { let lastTextLength = -1; let stabilityStreak = 0; const REQUIRED_STREAK = 2; let maxWait = 300; const checkLoop = setInterval(() => { maxWait--; if (maxWait <= 0) { clearInterval(checkLoop); resolve("Error: Generation timed out or hung."); return; } const responses = document.querySelectorAll('[data-message-author-role="assistant"]'); if (responses.length <= preSubmitCount && responses.length !== 0) { console.log("[Harness] Waiting for new ChatGPT response container..."); return; } const lastResponse = responses[responses.length - 1]; if (!lastResponse) return; const markdownContainer = lastResponse.querySelector('.markdown') || lastResponse; const currentTextContent = markdownContainer.innerText || ""; const currentTextLength = currentTextContent.length; const isTextGrowing = currentTextLength > lastTextLength; if (isTextGrowing) { console.log(`[Harness] Status: Streaming... (+${currentTextLength - lastTextLength})`); lastTextLength = currentTextLength; stabilityStreak = 0; } else if (currentTextLength === 0) { console.log(`[Harness] Status: Waiting for text generation to begin...`); stabilityStreak = 0; } else { stabilityStreak++; console.log(`[Harness] Status: Stable... (${stabilityStreak}/${REQUIRED_STREAK})`); lastTextLength = currentTextLength; } if (stabilityStreak >= REQUIRED_STREAK) { clearInterval(checkLoop); console.log("[Harness] Lock Released. Extraction initiated."); let turnMd = chatgptHtmlToMarkdown(markdownContainer); turnMd = turnMd.replace(/\n{3,}/g, '\n\n').trim(); resolve(turnMd); } }, 1000); }); }, NEW_CHAT: async () => { const newChatBtn = document.querySelector('[data-testid="create-new-chat-button"]'); if (newChatBtn) { newChatBtn.click(); return "Chat reset."; } return "Error: New chat button not found."; }, GET_CHAT_HISTORY: async () => { const history = []; const messages = document.querySelectorAll('[data-message-author-role]'); messages.forEach(msg => { const role = msg.getAttribute('data-message-author-role'); if (role === 'assistant') { const markdownContainer = msg.querySelector('.markdown') || msg; let text = chatgptHtmlToMarkdown(markdownContainer).trim(); if (text) history.push({ role: "Model", text: text }); } else if (role === 'user') { let text = msg.innerText.trim(); if (text) history.push({ role: "User", text: text }); } }); return JSON.stringify(history); }, ANALYZE_SESSION: async () => { return JSON.stringify({ isHydrated: document.querySelectorAll('[data-message-author-role="assistant"]').length > 0, turnCount: document.querySelectorAll('[data-message-author-role="assistant"]').length, url: window.location.href }); }, GET_SETTINGS: async () => "{}", SET_SETTINGS: async () => "Success: Ignored in ChatGPT Mode", SET_URL_CONTEXT: async () => "Success: Ignored in ChatGPT Mode", SET_RESOLUTION: async () => "Success: Ignored in ChatGPT Mode", DELETE_TURN: async () => "Success: Ignored in ChatGPT Mode", CREATE_SHARD: async () => "Error: Context Sharding is not implemented for ChatGPT Mode", INJECT_SHARD: async () => "Error: Context Sharding is not implemented for ChatGPT Mode", UPLOAD_MEDIA: async () => "Error: Media Upload is not currently supported in ChatGPT Mode", INJECT_DOC: async () => "Error: Doc Injection is not currently supported in ChatGPT Mode", SCRAPE_HISTORY: async () => "[]" }; const deepseekActions = { SHUTDOWN: async () => { clearInterval(pollIntervalId); clearInterval(stateIntervalId); console.log("[Harness] Shutdown command received. Loops stopped."); return "Harness shut down."; }, GENERATE: async (payload) => { let input = document.querySelector('textarea[placeholder="Message DeepSeek"]'); if (!input) input = document.querySelector('textarea'); if (!input) return "Error: Prompt textarea not found on DeepSeek."; const preSubmitResponses = document.querySelectorAll('.ds-assistant-message-main-content'); const preSubmitCount = preSubmitResponses.length; activeTactics.setNativeValue(input, payload); await new Promise(r => setTimeout(r, 800)); const sendBtn = document.querySelector('div[role="button"].ds-button--primary'); if (sendBtn && !sendBtn.classList.contains('ds-button--disabled')) { sendBtn.click(); } else { console.log("[Harness] Submit button not found or disabled, dispatching Enter key fallback..."); const enterEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', code: 'Enter', keyCode: 13 }); input.dispatchEvent(enterEvent); } console.log("[Harness] Command sent. Entering Kinetic Wait (2500ms)..."); await new Promise(r => setTimeout(r, 2500)); console.log("[Harness] Wait complete. Engaging Semantic Lock..."); return new Promise((resolve) => { let lastTextLength = -1; let stabilityStreak = 0; const REQUIRED_STREAK = 3; let maxWait = 600; const checkLoop = setInterval(() => { maxWait--; if (maxWait <= 0) { clearInterval(checkLoop); resolve("Error: Generation timed out or hung."); return; } const responses = document.querySelectorAll('.ds-assistant-message-main-content'); if (responses.length <= preSubmitCount && responses.length !== 0) { console.log("[Harness] Waiting for new DeepSeek response container..."); return; } const lastResponse = responses[responses.length - 1]; if (!lastResponse) return; const currentTextContent = lastResponse.innerText || ""; const currentTextLength = currentTextContent.length; const isTextGrowing = currentTextLength > lastTextLength; if (isTextGrowing) { console.log(`[Harness] Status: Streaming... (+${currentTextLength - lastTextLength})`); lastTextLength = currentTextLength; stabilityStreak = 0; } else if (currentTextLength === 0) { console.log(`[Harness] Status: Thinking / Waiting for text generation to begin...`); stabilityStreak = 0; } else { stabilityStreak++; console.log(`[Harness] Status: Stable... (${stabilityStreak}/${REQUIRED_STREAK})`); lastTextLength = currentTextLength; } if (stabilityStreak >= REQUIRED_STREAK) { clearInterval(checkLoop); console.log("[Harness] Lock Released. Extraction initiated."); let turnMd = deepseekHtmlToMarkdown(lastResponse); turnMd = turnMd.replace(/\n{3,}/g, '\n\n').trim(); resolve(turnMd); } }, 1000); }); }, NEW_CHAT: async () => { const spans = Array.from(document.querySelectorAll('span')); const newChatSpan = spans.find(el => el.innerText.trim() === 'New chat'); if (newChatSpan && newChatSpan.parentElement) { newChatSpan.parentElement.click(); return "Chat reset."; } return "Error: New chat button not found."; }, GET_CHAT_HISTORY: async () => { const history = []; const messages = document.querySelectorAll('.ds-message'); messages.forEach(msg => { const assistantContent = msg.querySelector('.ds-assistant-message-main-content'); if (assistantContent) { let text = deepseekHtmlToMarkdown(assistantContent).trim(); if (text) history.push({ role: "Model", text: text }); } else { let text = msg.innerText.trim(); if (text) history.push({ role: "User", text: text }); } }); return JSON.stringify(history); }, ANALYZE_SESSION: async () => { return JSON.stringify({ isHydrated: document.querySelectorAll('.ds-assistant-message-main-content').length > 0, turnCount: document.querySelectorAll('.ds-assistant-message-main-content').length, url: window.location.href }); }, GET_SETTINGS: async () => "{}", SET_SETTINGS: async () => "Success: Ignored in DeepSeek Mode", SET_URL_CONTEXT: async () => "Success: Ignored in DeepSeek Mode", SET_RESOLUTION: async () => "Success: Ignored in DeepSeek Mode", DELETE_TURN: async () => "Success: Ignored in DeepSeek Mode", CREATE_SHARD: async () => "Error: Context Sharding is not implemented for DeepSeek Mode", INJECT_SHARD: async () => "Error: Context Sharding is not implemented for DeepSeek Mode", UPLOAD_MEDIA: async () => "Error: Media Upload is not currently supported in DeepSeek Mode", INJECT_DOC: async () => "Error: Doc Injection is not currently supported in DeepSeek Mode", SCRAPE_HISTORY: async () => "[]" }; const geminiActions = { SHUTDOWN: async () => { clearInterval(pollIntervalId); clearInterval(stateIntervalId); console.log("[Harness] Shutdown command received. Loops stopped."); return "Harness shut down."; }, GENERATE: async (payload) => { // Helper to dismiss A/B testing dialogs that block the UI const dismissABTest = () => { const titles = document.querySelectorAll('span.title'); for (let title of titles) { if (title.textContent && title.textContent.includes('Which response is more helpful?')) { const container = title.closest('.container'); if (container) { const closeBtn = container.querySelector('button[aria-label="Close"]'); if (closeBtn) { console.log("[Harness] Dismissing A/B test dialog..."); closeBtn.click(); } } } } }; dismissABTest(); const input = document.querySelector('.ql-editor[contenteditable="true"]'); if (!input) return "Error: Prompt textarea not found on Gemini."; const preSubmitResponses = document.querySelectorAll('model-response'); const preSubmitCount = preSubmitResponses.length; activeTactics.setNativeValue(input, payload); await new Promise(r => setTimeout(r, 800)); const sendBtn = document.querySelector('button[aria-label="Send message"]'); if (sendBtn && !sendBtn.disabled && sendBtn.getAttribute('aria-disabled') !== 'true') { sendBtn.click(); } else { console.log("[Harness] Submit button not found or disabled, dispatching Enter key fallback..."); const enterEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'Enter', code: 'Enter', keyCode: 13 }); input.dispatchEvent(enterEvent); } console.log("[Harness] Command sent. Entering Kinetic Wait (2000ms)..."); await new Promise(r => setTimeout(r, 2000)); console.log("[Harness] Wait complete. Engaging Semantic Lock..."); return new Promise((resolve) => { let lastTextLength = -1; let stabilityStreak = 0; const REQUIRED_STREAK = 3; let maxWait = 300; const checkLoop = setInterval(() => { maxWait--; if (maxWait <= 0) { clearInterval(checkLoop); resolve("Error: Generation timed out or hung."); return; } dismissABTest(); const responses = document.querySelectorAll('model-response'); if (responses.length <= preSubmitCount && responses.length !== 0) { console.log("[Harness] Waiting for new Gemini response container..."); return; } const lastResponse = responses[responses.length - 1]; if (!lastResponse) return; const markdownContainer = lastResponse.querySelector('.markdown') || lastResponse; const currentTextContent = markdownContainer.innerText || ""; const currentTextLength = currentTextContent.length; const isTextGrowing = currentTextLength > lastTextLength; // Gemini uses aria-busy to indicate active generation const isBusy = markdownContainer.getAttribute('aria-busy') === 'true' || lastResponse.getAttribute('aria-busy') === 'true'; if (isTextGrowing || isBusy) { console.log(`[Harness] Status: Streaming... (+${currentTextLength - lastTextLength})`); lastTextLength = currentTextLength; stabilityStreak = 0; } else if (currentTextLength === 0) { console.log(`[Harness] Status: Waiting for text generation to begin...`); stabilityStreak = 0; } else { stabilityStreak++; console.log(`[Harness] Status: Stable... (${stabilityStreak}/${REQUIRED_STREAK})`); lastTextLength = currentTextLength; } if (stabilityStreak >= REQUIRED_STREAK && !isBusy) { clearInterval(checkLoop); console.log("[Harness] Lock Released. Extraction initiated."); let turnMd = googleHtmlToMarkdown(markdownContainer); turnMd = turnMd.replace(/\n{3,}/g, '\n\n').trim(); resolve(turnMd); } }, 1000); }); }, NEW_CHAT: async () => { const newChatBtn = document.querySelector('a[aria-label="New chat"]') || document.querySelector('a[href="/app"]'); if (newChatBtn) { newChatBtn.click(); return "Chat reset."; } return "Error: New chat button not found."; }, GET_CHAT_HISTORY: async () => { const history = []; const turns = document.querySelectorAll('user-query, model-response'); turns.forEach(turn => { if (turn.tagName.toLowerCase() === 'USER-QUERY') { const textEl = turn.querySelector('.query-text'); if (textEl) { let text = textEl.textContent.replace('You said', '').trim(); if (text) history.push({ role: "User", text: text }); } } else if (turn.tagName.toLowerCase() === 'MODEL-RESPONSE') { const markdownContainer = turn.querySelector('.markdown'); if (markdownContainer) { let text = googleHtmlToMarkdown(markdownContainer).trim(); if (text) history.push({ role: "Model", text: text }); } } }); return JSON.stringify(history); }, ANALYZE_SESSION: async () => { return JSON.stringify({ isHydrated: document.querySelectorAll('model-response').length > 0, turnCount: document.querySelectorAll('model-response').length, url: window.location.href }); }, GET_SETTINGS: async () => "{}", SET_SETTINGS: async () => "Success: Ignored in Gemini Mode", SET_URL_CONTEXT: async () => "Success: Ignored in Gemini Mode", SET_RESOLUTION: async () => "Success: Ignored in Gemini Mode", DELETE_TURN: async () => "Success: Ignored in Gemini Mode", CREATE_SHARD: async () => "Error: Context Sharding is not implemented for Gemini Mode", INJECT_SHARD: async () => "Error: Context Sharding is not implemented for Gemini Mode", UPLOAD_MEDIA: async () => "Error: Media Upload is not currently supported in Gemini Mode", INJECT_DOC: async () => "Error: Doc Injection is not currently supported in Gemini Mode", SCRAPE_HISTORY: async () => "[]" }; // --- PLATFORM ASSIGNMENT --- if (currentPlatform === 'Google') { PLATFORM_NAME = "Google AI Search"; ID_PREFIX = "inst_gai_"; activeActions = googleActions; activeTactics = standardTactics; reportStateLogic = () => document.querySelectorAll('.n6owBd').length; } else if (currentPlatform === 'ChatGPT') { PLATFORM_NAME = "ChatGPT"; ID_PREFIX = "inst_cgpt_"; activeActions = chatgptActions; activeTactics = contentEditableTactics; reportStateLogic = () => document.querySelectorAll('[data-message-author-role="assistant"]').length; } else if (currentPlatform === 'DeepSeek') { PLATFORM_NAME = "DeepSeek"; ID_PREFIX = "inst_ds_"; activeActions = deepseekActions; activeTactics = standardTactics; reportStateLogic = () => document.querySelectorAll('.ds-assistant-message-main-content').length; } else if (currentPlatform === 'Gemini') { PLATFORM_NAME = "Google Gemini"; ID_PREFIX = "inst_gem_"; activeActions = geminiActions; activeTactics = contentEditableTactics; reportStateLogic = () => document.querySelectorAll('model-response').length; } // --- INSTANCE IDENTITY --- function getInstanceId() { let id = sessionStorage.getItem('tm_instance_id'); if (!id || !id.startsWith(ID_PREFIX)) { id = ID_PREFIX + Math.random().toString(36).substr(2, 9) + '_' + Date.now(); sessionStorage.setItem('tm_instance_id', id); } return id; } const INSTANCE_ID = getInstanceId(); console.log(`[Harness] Bound to Instance ID: ${INSTANCE_ID}`); // --- MAIN LOOP & NETWORKING --- async function processCommand(cmdData) { const { type, payload } = cmdData.command; console.log(`[Harness] Processing: ${type}`, payload); let result = ""; try { if (activeActions[type]) { result = await activeActions[type](payload); } else { result = "Unknown command type"; } } catch (e) { console.error("Execution failed:", e); result = `Error: ${e.message}`; } console.log(`[Harness] Result for ${type}:`, result); await sendResult(result); } async function sendResult(data) { try { await tmFetch(`${API_BASE}/result/${INSTANCE_ID}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ timestamp: Date.now(), output: data }) }); } catch (e) { console.error("Relay failed:", e); } } async function poll() { if (isBusy || isPolling) return; isPolling = true; try { const res = await tmFetch(`${API_BASE}/command/${INSTANCE_ID}`); if (res.status === 200) { const data = await res.json(); if (data && data.command) { isBusy = true; await processCommand(data); isBusy = false; } } } catch (e) { // Network error or timeout, safely ignored. } finally { isPolling = false; } } function reportState() { if (isBusy) return; const turnCount = reportStateLogic(); tmFetch(`${API_BASE}/state/${INSTANCE_ID}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: window.location.href, turn_count: turnCount, is_busy: isBusy, platform: PLATFORM_NAME }) }).catch(() => {}); } pollIntervalId = setInterval(poll, POLLING_INTERVAL); stateIntervalId = setInterval(reportState, STATE_INTERVAL); console.log(`[Harness] ${PLATFORM_NAME} Active & Polling on Instance ${INSTANCE_ID}.`); })();