"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; var cfgToken = ''; var execMode = 'dom'; var lastSsePid = ''; var domDebugSeen = new Set(); var domDebugCount = 0; 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; started = false; try { obs.disconnect(); } catch (e) {} if (UI) { UI.setPhase('off'); UI.setStartEnabled(true); if (UI.setDot) UI.setDot('stopped'); 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) { if (UI.setStartEnabled) UI.setStartEnabled(true); return; } started = true; markExistingAsProcessed(); obs.observe(document.documentElement, { childList: true, subtree: true, characterData: true }); UI.setPhase('idle'); UI.setConnected(true); if (UI.setDot) UI.setDot('ok'); UI.log('started'); }); } window.addEventListener('dshmem:metrics', function (e) { try { var mm = JSON.parse(e.detail); if (UI && UI.setMetrics) UI.setMetrics(mm); } catch (err) {} }); if (!SEL) return; if (!BRIDGE) return; if (typeof extractCalls !== "function") return; var DEBOUNCE_MS = 800; function __uiLog(m) { try { if (UI ? UI.log : null) UI.log(m) } catch (e) { } } function __debugReport(rec) { try { if (!cfgToken) return; var url = 'http' + ':' + '//127.0.0.1:8766/log/append'; var hh = { 'Content-Type': 'application/json', Authorization: 'Bearer ' + cfgToken }; fetch(url, { method: 'POST', headers: hh, body: JSON.stringify({ file: 'sse-debug.jsonl', lines: [JSON.stringify(rec)] }) }).catch(function () { }); } catch (e) { } } function __refreshExecMode() { try { chrome.storage.local.get('dshmem-bridge-token', function (vv) { if (vv) { cfgToken = vv['dshmem-bridge-token'] ? String(vv['dshmem-bridge-token']) : '' } }) } catch (e) { } try { chrome.storage.local.get('dshmem-transport', function (v) { var raw = v ? v['dshmem-transport'] : null; var tv = (raw === 'sse') ? 'sse' : 'dom'; if (execMode === tv) return; execMode = tv; lastSsePid = ''; seenCallsHashes.clear(); queue.length = 0; pending.clear(); pumping = false; if (tv === 'dom') { try { markExistingAsProcessed(); } catch (e) { } } __uiLog('exec mode: ' + tv); }); } catch (e) { } } try { chrome.storage.onChanged.addListener(function (ch, area) { if (area === 'local') { if (ch['dshmem-transport']) __refreshExecMode(); } }); } catch (e) { } try { __refreshExecMode(); } catch (e) { } window.addEventListener('dshmem:sse-answer', function (ev) { try { if (!started) return; if (execMode === 'dom') return; var d = null; try { d = JSON.parse(ev.detail); } catch (e) { return; } if (d ? (d.text ? false : true) : true) return; var pid = d.pid ? String(d.pid) : ''; if (pid.length === 0) return; if (pid === lastSsePid) { __uiLog('sse dup skipped ' + pid); return; } lastSsePid = pid; var parsed = extractCalls(d.text, 10); try { var __callSum = []; if (parsed ? (parsed.calls ? true : false) : false) { for (var __ci = 0; __ci < parsed.calls.length; __ci++) { var __c = parsed.calls[__ci]; __callSum.push({ i: __ci, tool: (__c ? String(__c.tool) : 'null'), hasArgs: (__c ? (__c.args ? true : false) : false), argsPreview: (__c ? String(JSON.stringify(__c.args || {})).slice(0, 400) : '') }) } } __debugReport({ ev: 'sse-parse', ts: Date.now(), pid: pid, execSource: execMode, consumed: (parsed ? parsed.consumed : -1), textLen: d.text.length, calls: __callSum, parsedCount: (parsed ? parsed.calls.length : 0), text: String(d.text).slice(0, 2000) }) } catch (e7) { } if (parsed ? (parsed.calls ? (parsed.calls.length > 0) : false) : false) { var validCount = 0; for (var k = 0; k < parsed.calls.length; k++) { var c = parsed.calls[k]; if (c && c.tool === 'invalid') { try { __debugReport({ ev: 'sse-exec-result', ts: Date.now(), pid: pid, execSource: execMode, tool: 'invalid', ok: false, errorCode: 'E_PARSE_INVALID', errorMsg: String((c.args && c.args._reason) || '') }) } catch (e) { } __uiLog('sse invalid call: ' + String((c.args && c.args._reason) || '')); continue; } queue.push(c); validCount++; } if (validCount === 0) { if (UI) UI.setPhase('idle'); return; } if (UI) { if (UI.setDot) UI.setDot('ok-busy'); UI.setPhase('executing'); } __uiLog('sse exec ' + parsed.calls.length + ' pid ' + pid); pump(); } } catch (e) { console.warn('[dsh-memory] sse-answer error', String(e)); } }); var processed = new WeakSet(); var seenHashes = new Map(); var HASH_CAP = 50; var queue = []; var pumping = false; var timer = null; var pending = new Set(); var lastPath = location.pathname; function __onPathChange() { var p = location.pathname; if (p === lastPath) return; var __prev = lastPath; lastPath = p; var __fromRoot = (__prev === '/' || __prev === ''); queue.length = 0; pending.clear(); seenHashes.clear(); seenCallsHashes.clear(); pumping = false; lastSsePid = ''; if (!__fromRoot) setTimeout(function () { try { markExistingAsProcessed(); } catch (e) { } }, 200); if (!__fromRoot) setTimeout(function () { try { markExistingAsProcessed(); } catch (e) { } }, 1200); if (UI) { UI.setPhase('idle'); if (UI.log) UI.log('chat changed ' + p); } try { window.dispatchEvent(new CustomEvent('dshmem:chat-change', { detail: p })); } catch (e) {} } window.addEventListener('popstate', __onPathChange); try { var __ps = history.pushState; var __rs = history.replaceState; history.pushState = function () { var r = __ps.apply(this, arguments); setTimeout(__onPathChange, 0); return r; }; history.replaceState = function () { var r = __rs.apply(this, arguments); setTimeout(__onPathChange, 0); return r; }; } catch (e) {} setInterval(__onPathChange, 1000); 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.'; 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 (execMode === 'sse') { var __ms = findMessages(node); for (var __mi = 0; __mi < __ms.length; __mi++) processed.add(__ms[__mi]); 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 hasMarker = text.indexOf(String.fromCharCode(10214)) !== -1 || text.indexOf('[DSH') !== -1; if (hasMarker && UI && UI.setDot) UI.setDot('busy'); var parsed = extractCalls(text, 10); if (!parsed) continue; if (!parsed.calls) continue; if (parsed.calls.length === 0) { if (hasMarker) { try { var __dh = hashText(text); if (!domDebugSeen.has(__dh) && domDebugCount < 3) { domDebugSeen.add(__dh); domDebugCount = domDebugCount + 1; __debugReport({ ev: 'dom-parse-fail', ts: Date.now(), execSource: execMode, textLen: text.length, msgText: String(text).slice(0, 2000) }) } } catch (e9) { } } continue; } var ch = hashText(JSON.stringify(parsed.calls)); if (seenCallsHashes.has(ch)) { if (UI) { if (UI.setDot) UI.setDot('ok'); UI.log('dup call skipped'); } processed.add(m); rememberHash(h); continue; } rememberCallsHash(ch); processed.add(m); rememberHash(h); for (k = 0; k < parsed.calls.length; k++) { var __dc = parsed.calls[k]; if (__dc && __dc.tool === 'invalid') { try { __debugReport({ ev: 'sse-exec-result', ts: Date.now(), pid: lastSsePid, execSource: execMode, tool: 'invalid', ok: false, errorCode: 'E_PARSE_INVALID', errorMsg: String((__dc.args && __dc.args._reason) || '') }) } catch (e) { } continue; } queue.push(__dc); } if (UI && UI.setDot) UI.setDot('ok-busy'); 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'); var anyFail = results.some(function (r) { return !r || !r.ok; }); sendText(payload).then(function () { if (UI) { UI.setPhase('idle'); if (UI.setDot) UI.setDot(anyFail ? 'error' : 'ok'); } }).catch(function (err) { console.warn('[dsh-memory] sendText failed', String(err)); if (UI) { UI.setPhase('idle'); if (UI.setDot) UI.setDot('error'); } }); } else { if (UI) UI.setPhase('idle'); } return; } var call = queue.shift(); executeOne(call).then(function (res) { try { __debugReport({ ev: 'sse-exec-result', ts: Date.now(), pid: lastSsePid, execSource: execMode, tool: (call ? String(call.tool) : 'null'), ok: (res ? (res.ok ? true : false) : false), errorCode: (res ? (res.error ? String(res.error.code || '') : '') : 'nores'), errorMsg: (res ? (res.error ? String(res.error.message || '').slice(0, 300) : '') : '') }) } catch (e8) { } 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 { try { console.warn('[dsh-memory] call failed', call.tool, JSON.stringify(res.error)); } catch (e) { } if (UI) UI.log('fail ' + call.tool + ' ' + JSON.stringify((res && res.error) || 'nores')); } } else { console.warn('[dsh-memory] call failed (no res)', call.tool); if (UI) UI.log('fail ' + call.tool + ' ' + JSON.stringify((res && res.error) || 'nores')); } }).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"); BRIDGE.health().then(function (r) { if (UI) { UI.setPhase('off'); UI.setConnected(!!(r && r.ok)); if (!r || !r.ok) UI.log('bridge down: ' + ((r && r.error && r.error.message) || '?')); else { if (UI.setDot) UI.setDot('off'); UI.log('ready, press Start'); } } canStart = true; }).catch(function (e) { if (UI) { UI.setPhase('off'); UI.setConnected(false); UI.log('bridge down: ' + String(e)); } canStart = true; }); setInterval(function () { BRIDGE.health().then(function (r) { if (UI && UI.setConnected) UI.setConnected(!!(r && r.ok)); }); }, 30000); __refreshStats(); }); }); function __refreshStats() { try { var SB = globalThis.STORAGE_BRIDGE; if (!SB || !UI || !UI.setStats) return; SB.authHeaders().then(function (h) { return fetch(SB.BU + '/memory/state', { headers: h }); }).then(function (r) { return r.json(); }).then(function (j) { var st = (j && j.ok && j.data) ? j.data : {}; var entries = st.entries || []; var total = entries.length; var alwaysN = entries.filter(function (e) { return e.importance === 'always'; }).length; return SB.authHeaders().then(function (h) { return fetch(SB.BU + '/health', { headers: h }); }).then(function (r) { return r.json(); }).then(function (hj) { UI.setStats((hj && hj.workspace) || '?', total, alwaysN); }); }).catch(function (e) { console.warn('[dsh-memory] stats ' + String(e)); UI.setStats('?', 0, 0); }); } catch (e) { console.warn('[dsh-memory] stats ' + String(e)); } } })();