"use strict"; (function () { var SEL = globalThis.DSH_SELECTORS; var BRIDGE = globalThis.DSH_BRIDGE; var extractCalls = null; if (typeof globalThis.extractCalls === "function") { extractCalls = globalThis.extractCalls; } var UI = globalThis.DSHMEM || null; var canStart = false; var started = false; var lastProcessedKey = ''; var lastSentAssistantKey = ''; var seenCallsHashes = new Set(); var CALL_HASH_CAP = 100; if (UI) { UI.onResetLoops(function () { queue.length = 0; pending.clear(); seenHashes.clear(); pumping = false; if (UI) UI.log('loops reset'); }); UI.onStop(function () { queue.length = 0; if (UI) UI.log('stopped'); }); UI.onSaveToken(function (v) { chrome.storage.local.set({ 'dshmem-bridge-token': v }, function () { if (UI) UI.log('token saved, reload page'); }); }); } if (UI) { UI.onStart(function () { if (!canStart) return; started = true; markExistingAsProcessed(); obs.observe(document.documentElement, { childList: true, subtree: true, characterData: true }); UI.setPhase('idle'); UI.setConnected(true); UI.log('started'); }); } if (!SEL) return; if (!BRIDGE) return; if (typeof extractCalls !== "function") return; var DEBOUNCE_MS = 800; var processed = new WeakSet(); var seenHashes = new Map(); var HASH_CAP = 50; var queue = []; var pumping = false; var timer = null; var pending = new Set(); function hashText(s) { var h = 5381; var i; for (i = 0; i < s.length; i++) { h = ((h << 5) + h + s.charCodeAt(i)) >>> 0; } return h.toString(36) + ":" + s.length; } function rememberHash(h) { seenHashes.set(h, true); if (seenHashes.size > HASH_CAP) { var first = seenHashes.keys().next().value; seenHashes.delete(first); } } function markExistingAsProcessed() { var sels = SEL.MESSAGE_SELECTORS; for (var i = 0; i < sels.length; i++) { try { var all = document.querySelectorAll(sels[i]); for (var j = 0; j < all.length; j++) { var el = all[j]; processed.add(el); try { var t = SEL.collectText(el); if (t && t.length > 0) rememberHash(hashText(t)); } catch (e2) {} } } catch (e) {} } } function rememberCallsHash(h) { seenCallsHashes.add(h); if (seenCallsHashes.size > CALL_HASH_CAP) { var first = seenCallsHashes.values().next().value; seenCallsHashes.delete(first); } } function keyOf(el) { if (!el) return ''; var t = el.textContent || el.innerText || ''; return t.length + ':' + t.slice(-64); } function lastVisible(list) { if (!list) return null; for (var i = 0; i < list.length; i++) { try { var all = document.querySelectorAll(list[i]); for (var j = all.length - 1; j >= 0; j--) { var el = all[j]; var r = el.getBoundingClientRect(); if (r.width > 0 || r.height > 0) return el; } } catch (e) {} } return null; } function findLastAssistant() { return lastVisible(SEL && SEL.assistantMarkdown); } function findInput() { return lastVisible(SEL && SEL.input); } function findSendButton() { return lastVisible(SEL && SEL.sendButton); } function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); } function setNativeValue(el, text) { if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') { var proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype; var setter = Object.getOwnPropertyDescriptor(proto, 'value').set; setter.call(el, text); el.dispatchEvent(new Event('input', { bubbles: true })); } else { el.focus(); var ok = false; try { ok = document.execCommand('insertText', false, text); } catch (e) { ok = false; } if (!ok) { el.textContent = text; el.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, cancelable: true, inputType: 'insertText', data: text })); el.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertText', data: text })); } } } function sendText(text) { var input = findInput(); if (!input) { console.warn('[dsh-memory] input not found'); return Promise.resolve(false); } var la = findLastAssistant(); if (la) lastSentAssistantKey = keyOf(la); setNativeValue(input, text); return sleep(150).then(function () { var btn = findSendButton(); if (btn && !btn.disabled && btn.getAttribute('aria-disabled') !== 'true') { btn.click(); } else { input.focus(); input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true })); } return sleep(600); }).then(function () { return true; }); } function buildResultMessage(results) { var BT3 = String.fromCharCode(96) + String.fromCharCode(96) + String.fromCharCode(96); var blocks = results.map(function (r) { return BT3 + 'dsh-result' + String.fromCharCode(10) + JSON.stringify(r) + String.fromCharCode(10) + BT3; }).join(String.fromCharCode(10) + String.fromCharCode(10)); var remind = '[dsh-memory] continue. When done output on its own line: DSH:DONE:summary.'; return '[dsh-memory tool result]' + String.fromCharCode(10) + String.fromCharCode(10) + blocks + String.fromCharCode(10) + String.fromCharCode(10) + remind; } function findMessages(node) { var out = []; if (!node) return out; var i; var j; for (i = 0; i < SEL.MESSAGE_SELECTORS.length; i++) { var sel = SEL.MESSAGE_SELECTORS[i]; if (node.matches) { if (node.matches(sel)) out.push(node); } if (node.querySelectorAll) { var kids = node.querySelectorAll(sel); for (j = 0; j < kids.length; j++) out.push(kids[j]); } } return out; } function handleNode(node) { if (!node) return; if (node.nodeType !== 1) return; var msgs = findMessages(node); var i; var k; for (i = 0; i < msgs.length; i++) { var m = msgs[i]; if (processed.has(m)) continue; var text = SEL.collectText(m); if (!started) { processed.add(m); continue; } if (!text) continue; var h = hashText(text); if (seenHashes.has(h)) { processed.add(m); continue; } var parsed = extractCalls(text, 3); if (!parsed) continue; if (!parsed.calls) continue; if (parsed.calls.length === 0) continue; var ch = hashText(JSON.stringify(parsed.calls)); if (seenCallsHashes.has(ch)) { processed.add(m); rememberHash(h); continue; } rememberCallsHash(ch); processed.add(m); rememberHash(h); for (k = 0; k < parsed.calls.length; k++) queue.push(parsed.calls[k]); if (UI) UI.setPhase('executing'); } pump(); } function executeOne(call) { return BRIDGE.sendCall(call).then(function (res) { var e = res && !res.ok && res.error; var needConfirm = e && e.code === "E_NEEDS_CONFIRM" && e.extra && e.extra.confirmToken; if (needConfirm) { console.log("[dsh-memory] needs confirm, asking user", call.tool); if (!UI || typeof UI.showConfirm !== 'function') { return { ok: false, error: { code: 'E_CONFIRM_UNAVAILABLE', message: 'confirm UI not available' } }; } return new Promise(function (resolve) { UI.showConfirm({ title: 'Allow ' + call.tool + '?', detail: JSON.stringify(call.args || {}).slice(0, 300), onAllow: function () { BRIDGE.sendCall({ tool: call.tool, args: call.args, confirmToken: e.extra.confirmToken }).then(resolve); }, onDeny: function () { resolve({ ok: false, error: { code: 'E_USER_DENIED', message: 'denied by user' } }); } }); }); } return res; }); } function pump() { if (pumping) return; if (queue.length === 0) return; pumping = true; var results = []; var step = function () { if (queue.length === 0) { pumping = false; if (results.length > 0) { var payload = buildResultMessage(results); if (UI) UI.setPhase('sending'); sendText(payload).then(function () { if (UI) UI.setPhase('idle'); }).catch(function (err) { console.warn('[dsh-memory] sendText failed', String(err)); if (UI) UI.setPhase('idle'); }); } else { if (UI) UI.setPhase('idle'); } return; } var call = queue.shift(); executeOne(call).then(function (res) { if (res) { results.push(res); if (res.ok) { console.log('[dsh-memory] call ok', call.tool, res.durationMs); if (UI) UI.log('ok ' + call.tool); } else { console.warn('[dsh-memory] call failed', call.tool, res.error); if (UI) UI.log('fail ' + call.tool); } } else { console.warn('[dsh-memory] call failed', call.tool); if (UI) UI.log('fail ' + call.tool); } }).catch(function (err) { console.warn('[dsh-memory] call throw', call.tool, String(err)); }).then(step); }; step(); } function flush() { timer = null; var nodes = Array.from(pending); pending.clear(); var i; for (i = 0; i < nodes.length; i++) handleNode(nodes[i]); } function schedule() { if (timer) clearTimeout(timer); timer = setTimeout(flush, DEBOUNCE_MS); } var obs = new MutationObserver(function (muts) { var i; var j; for (i = 0; i < muts.length; i++) { var m = muts[i]; if (m.type === "childList") { for (j = 0; j < m.addedNodes.length; j++) { var n = m.addedNodes[j]; if (n) { if (n.nodeType === 1) pending.add(n); } } } else if (m.type === "characterData") { if (m.target) { if (m.target.parentNode) pending.add(m.target.parentNode); } } } if (pending.size > 0) schedule(); }); BRIDGE.isEnabled().then(function (enabled) { if (!enabled) { console.warn("[dsh-memory] disabled (dsh-memory-enabled=false), idle"); if (UI) { UI.setPhase('off'); UI.setConnected(false); UI.log('disabled'); } return; } return BRIDGE.hasToken().then(function (has) { if (!has) { console.warn("[dsh-memory] no bridge token, idle"); if (UI) { UI.setPhase('off'); UI.setConnected(false); UI.log('no token'); } return; } markExistingAsProcessed(); obs.observe(document.documentElement, { childList: true, subtree: true, characterData: true }); console.log("[dsh-memory] content.js active"); if (UI) { UI.setPhase('idle'); UI.setConnected(true); canStart = true; UI.log('active'); } }); }); })();