From aff433495f9acbd7a8d609a2728b1fdfbec37f5d Mon Sep 17 00:00:00 2001 From: rcholic Date: Mon, 22 Dec 2025 19:44:41 +0000 Subject: [PATCH 1/2] chore: sync extension files from sentience-chrome v0.10.4 --- sentience/extension/injected_api.js | 88 +++++++++++++----- sentience/extension/pkg/sentience_core.d.ts | 9 ++ sentience/extension/pkg/sentience_core.js | 16 ++++ .../extension/pkg/sentience_core_bg.wasm | Bin 98075 -> 101498 bytes .../extension/pkg/sentience_core_bg.wasm.d.ts | 1 + sentience/extension/release.json | 60 ++++++------ 6 files changed, 122 insertions(+), 52 deletions(-) diff --git a/sentience/extension/injected_api.js b/sentience/extension/injected_api.js index 8f6eb15..55352e2 100644 --- a/sentience/extension/injected_api.js +++ b/sentience/extension/injected_api.js @@ -20,14 +20,34 @@ window.sentience_registry = []; let wasmModule = null; - // --- HELPER: Deep Walker --- + // --- HELPER: Deep Walker with Native Filter --- function getAllElements(root = document) { const elements = []; - const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT); + // FILTER: Skip Script, Style, Comments, Metadata tags during traversal + // This prevents collecting them in the first place, saving memory and CPU + const filter = { + acceptNode: function(node) { + // Skip metadata and script/style tags + if (['SCRIPT', 'STYLE', 'NOSCRIPT', 'META', 'LINK', 'HEAD'].includes(node.tagName)) { + return NodeFilter.FILTER_REJECT; + } + // Skip deep SVG children (keep root only, unless you need path data) + // This reduces noise from complex SVG graphics while preserving icon containers + if (node.parentNode && node.parentNode.tagName === 'SVG' && node.tagName !== 'SVG') { + return NodeFilter.FILTER_REJECT; + } + return NodeFilter.FILTER_ACCEPT; + } + }; + + const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter); while(walker.nextNode()) { const node = walker.currentNode; - elements.push(node); - if (node.shadowRoot) elements.push(...getAllElements(node.shadowRoot)); + // Pre-check: Don't even process empty/detached nodes + if (node.isConnected) { + elements.push(node); + if (node.shadowRoot) elements.push(...getAllElements(node.shadowRoot)); + } } return elements; } @@ -40,6 +60,15 @@ return (el.innerText || '').replace(/\s+/g, ' ').trim().substring(0, 100); } + // --- HELPER: Safe Class Name Extractor --- + // Fixes the SVGAnimatedString error by ensuring we always get a primitive string + function getClassName(el) { + if (typeof el.className === 'string') return el.className; + // Handle SVGAnimatedString (has baseVal and animVal) + if (el.className && typeof el.className.baseVal === 'string') return el.className.baseVal; + return ''; + } + // --- HELPER: Viewport Check (NEW) --- function isInViewport(rect) { return ( @@ -287,13 +316,13 @@ // Verify functions are available if (!wasmModule.analyze_page) { - console.error('[SentienceAPI.com] WASM functions not available'); + console.error('[SentienceAPI.com] available'); } else { - console.log('[SentienceAPI.com] ✓ API Ready!'); + console.log('[SentienceAPI.com] ✓ Ready!'); console.log('[SentienceAPI.com] Available functions:', Object.keys(wasmModule).filter(k => k.startsWith('analyze'))); } } catch (e) { - console.error('[SentienceAPI.com] WASM Load Failed:', e); + console.error('[SentienceAPI.com] Extension Load Failed:', e); } // REMOVED: Headless detection - no longer needed (license system removed) @@ -332,19 +361,20 @@ display: style.display, visibility: style.visibility, opacity: style.opacity, - z_index: style.zIndex || "0", + z_index: String(style.zIndex || "auto"), // Force string conversion bg_color: style.backgroundColor, color: style.color, cursor: style.cursor, - font_weight: style.fontWeight, + font_weight: String(style.fontWeight), // Force string conversion font_size: style.fontSize }, attributes: { role: el.getAttribute('role'), type_: el.getAttribute('type'), aria_label: el.getAttribute('aria-label'), - href: el.href, - class: el.className + // Convert SVGAnimatedString to string for SVG elements + href: el.href?.baseVal || el.href || null, + class: getClassName(el) || null }, // Pass to WASM text: textVal || null, @@ -353,10 +383,6 @@ }); }); - // FREE TIER: No license checks - extension provides basic geometry data - // Pro/Enterprise tiers will be handled server-side (future work) - - // 1. Get Geometry from WASM let result; try { if (options.limit || options.filter) { @@ -368,24 +394,33 @@ return { status: "error", error: e.message }; } - // Hydration step removed as WASM now returns populated structs - + // Hydration step removed // Capture Screenshot let screenshot = null; if (options.screenshot) { screenshot = await captureScreenshot(options.screenshot); } - // C. Clean up null/undefined fields to save tokens (Your existing cleaner) + // C. Clean up null/undefined fields to save tokens const cleanElement = (obj) => { if (Array.isArray(obj)) { return obj.map(cleanElement); - } else if (obj !== null && typeof obj === 'object') { + } + if (obj !== null && typeof obj === 'object') { const cleaned = {}; for (const [key, value] of Object.entries(obj)) { - // Keep boolean false for critical flags if desired, or remove to match Rust defaults + // Explicitly skip null AND undefined if (value !== null && value !== undefined) { - cleaned[key] = cleanElement(value); + // Recursively clean objects + if (typeof value === 'object') { + const deepClean = cleanElement(value); + // Only keep object if it's not empty (optional optimization) + if (Object.keys(deepClean).length > 0) { + cleaned[key] = deepClean; + } + } else { + cleaned[key] = value; + } } } return cleaned; @@ -395,11 +430,20 @@ const cleanedElements = cleanElement(result); + // DEBUG: Check rawData before pruning + // console.log(`[DEBUG] rawData length BEFORE pruning: ${rawData.length}`); + // Prune raw elements using WASM before sending to API + // This prevents 413 errors on large sites (Amazon: 5000+ -> ~200-400) + const prunedRawData = wasmModule.prune_for_api(rawData); + + // Clean up null/undefined fields in raw_elements as well + const cleanedRawElements = cleanElement(prunedRawData); + return { status: "success", url: window.location.href, elements: cleanedElements, - raw_elements: rawData, // Include raw data for server-side processing (safe to expose - no proprietary value) + raw_elements: cleanedRawElements, // Send cleaned pruned data to prevent 413 errors screenshot: screenshot }; }, diff --git a/sentience/extension/pkg/sentience_core.d.ts b/sentience/extension/pkg/sentience_core.d.ts index 017160d..e280c26 100644 --- a/sentience/extension/pkg/sentience_core.d.ts +++ b/sentience/extension/pkg/sentience_core.d.ts @@ -7,6 +7,14 @@ export function analyze_page_with_options(val: any, options: any): any; export function decide_and_act(_raw_elements: any): void; +/** + * Prune raw elements before sending to API + * This is a "dumb" filter that reduces payload size without leaking proprietary IP + * Filters out: tiny elements, invisible elements, non-interactive wrapper divs + * Amazon: 5000-6000 elements -> ~200-400 elements (~95% reduction) + */ +export function prune_for_api(val: any): any; + export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; export interface InitOutput { @@ -14,6 +22,7 @@ export interface InitOutput { readonly analyze_page: (a: number) => number; readonly analyze_page_with_options: (a: number, b: number) => number; readonly decide_and_act: (a: number) => void; + readonly prune_for_api: (a: number) => number; readonly __wbindgen_export: (a: number, b: number) => number; readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number; readonly __wbindgen_export3: (a: number) => void; diff --git a/sentience/extension/pkg/sentience_core.js b/sentience/extension/pkg/sentience_core.js index bb44be7..b232d13 100644 --- a/sentience/extension/pkg/sentience_core.js +++ b/sentience/extension/pkg/sentience_core.js @@ -223,6 +223,19 @@ export function decide_and_act(_raw_elements) { wasm.decide_and_act(addHeapObject(_raw_elements)); } +/** + * Prune raw elements before sending to API + * This is a "dumb" filter that reduces payload size without leaking proprietary IP + * Filters out: tiny elements, invisible elements, non-interactive wrapper divs + * Amazon: 5000-6000 elements -> ~200-400 elements (~95% reduction) + * @param {any} val + * @returns {any} + */ +export function prune_for_api(val) { + const ret = wasm.prune_for_api(addHeapObject(val)); + return takeObject(ret); +} + const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']); async function __wbg_load(module, imports) { @@ -338,6 +351,9 @@ function __wbg_get_imports() { const ret = getObject(arg0).done; return ret; }; + imports.wbg.__wbg_error_7bc7d576a6aaf855 = function(arg0) { + console.error(getObject(arg0)); + }; imports.wbg.__wbg_get_6b7bd52aca3f9671 = function(arg0, arg1) { const ret = getObject(arg0)[arg1 >>> 0]; return addHeapObject(ret); diff --git a/sentience/extension/pkg/sentience_core_bg.wasm b/sentience/extension/pkg/sentience_core_bg.wasm index ea9c9cfad20916ed5ca7b648b5642a045cdb7553..778e8189046317c9aab0f479cc46f2bffcbb71d0 100644 GIT binary patch delta 27232 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 delta 24927 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 I6`l8g0A2{8F#rGn diff --git a/sentience/extension/pkg/sentience_core_bg.wasm.d.ts b/sentience/extension/pkg/sentience_core_bg.wasm.d.ts index 3544143..dccf049 100644 --- a/sentience/extension/pkg/sentience_core_bg.wasm.d.ts +++ b/sentience/extension/pkg/sentience_core_bg.wasm.d.ts @@ -4,6 +4,7 @@ export const memory: WebAssembly.Memory; export const analyze_page: (a: number) => number; export const analyze_page_with_options: (a: number, b: number) => number; export const decide_and_act: (a: number) => void; +export const prune_for_api: (a: number) => number; export const __wbindgen_export: (a: number, b: number) => number; export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number; export const __wbindgen_export3: (a: number) => void; diff --git a/sentience/extension/release.json b/sentience/extension/release.json index 1cc6f95..bcba583 100644 --- a/sentience/extension/release.json +++ b/sentience/extension/release.json @@ -1,9 +1,9 @@ { - "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/272122068", - "assets_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/272122068/assets", - "upload_url": "https://uploads.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/272122068/assets{?name,label}", - "html_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/tag/v0.10.3", - "id": 272122068, + "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/272300094", + "assets_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/272300094/assets", + "upload_url": "https://uploads.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/272300094/assets{?name,label}", + "html_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/tag/v0.10.4", + "id": 272300094, "author": { "login": "github-actions[bot]", "id": 41898282, @@ -25,21 +25,21 @@ "user_view_type": "public", "site_admin": false }, - "node_id": "RE_kwDOQshiJ84QOEDU", - "tag_name": "v0.10.3", + "node_id": "RE_kwDOQshiJ84QOvg-", + "tag_name": "v0.10.4", "target_commitish": "main", - "name": "Release v0.10.3", + "name": "Release v0.10.4", "draft": false, "immutable": false, "prerelease": false, - "created_at": "2025-12-22T07:06:41Z", - "updated_at": "2025-12-22T07:07:53Z", - "published_at": "2025-12-22T07:07:53Z", + "created_at": "2025-12-22T19:42:33Z", + "updated_at": "2025-12-22T19:43:39Z", + "published_at": "2025-12-22T19:43:38Z", "assets": [ { - "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/331630535", - "id": 331630535, - "node_id": "RA_kwDOQshiJ84TxEfH", + "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/331850955", + "id": 331850955, + "node_id": "RA_kwDOQshiJ84Tx6TL", "name": "extension-files.tar.gz", "label": "", "uploader": { @@ -65,17 +65,17 @@ }, "content_type": "application/gzip", "state": "uploaded", - "size": 61175, - "digest": "sha256:21ef5d067ffba9a1dd93630c03b39efd57dabb3d1598543c7e3519ff9d3bf387", + "size": 63026, + "digest": "sha256:f1f888c2b98e15c4433cee3a45e0109bbcef0dec0b4eef9d889156c45382f1ca", "download_count": 0, - "created_at": "2025-12-22T07:07:53Z", - "updated_at": "2025-12-22T07:07:53Z", - "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v0.10.3/extension-files.tar.gz" + "created_at": "2025-12-22T19:43:39Z", + "updated_at": "2025-12-22T19:43:39Z", + "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v0.10.4/extension-files.tar.gz" }, { - "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/331630534", - "id": 331630534, - "node_id": "RA_kwDOQshiJ84TxEfG", + "url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/assets/331850954", + "id": 331850954, + "node_id": "RA_kwDOQshiJ84Tx6TK", "name": "extension-package.zip", "label": "", "uploader": { @@ -101,16 +101,16 @@ }, "content_type": "application/zip", "state": "uploaded", - "size": 63347, - "digest": "sha256:78ca5a87ce41e249a66de63b3fa34aa34268920abe590e412ba6036ec93ceb2f", + "size": 65474, + "digest": "sha256:798aa7b8a37ea110d25310ce62c4796245124a48202fa01ca7909d2fb13b07ee", "download_count": 0, - "created_at": "2025-12-22T07:07:53Z", - "updated_at": "2025-12-22T07:07:53Z", - "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v0.10.3/extension-package.zip" + "created_at": "2025-12-22T19:43:39Z", + "updated_at": "2025-12-22T19:43:39Z", + "browser_download_url": "https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/releases/download/v0.10.4/extension-package.zip" } ], - "tarball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/tarball/v0.10.3", - "zipball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/zipball/v0.10.3", - "body": "## What's Changed\n* release fix by @rcholic in https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/pull/8\n* transferred by @rcholic in https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/pull/9\n* fix build and release by @rcholic in https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/pull/10\n* verify loop by @rcholic in https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/pull/11\n* more robust by @rcholic in https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/pull/12\n\n\n**Full Changelog**: https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/compare/vv0.9.0...v0.10.3", + "tarball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/tarball/v0.10.4", + "zipball_url": "https://api.github.com/repos/SentienceAPI/Sentience-Geometry-Chrome-Extension/zipball/v0.10.4", + "body": "## What's Changed\n* prune raw element by @rcholic in https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/pull/13\n\n\n**Full Changelog**: https://github.com/SentienceAPI/Sentience-Geometry-Chrome-Extension/compare/v0.10.3...v0.10.4", "mentions_count": 1 } From 0c8a6b251408b197ff04238e6e036a3bddecd907 Mon Sep 17 00:00:00 2001 From: rcholic Date: Mon, 22 Dec 2025 12:13:19 -0800 Subject: [PATCH 2/2] fix tests --- tests/test_generator.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/test_generator.py b/tests/test_generator.py index 7d9d98d..dd6bb43 100644 --- a/tests/test_generator.py +++ b/tests/test_generator.py @@ -6,7 +6,7 @@ import tempfile import os from sentience import SentienceBrowser, record -from sentience.recorder import Trace +from sentience.recorder import Trace, TraceStep from sentience.generator import ScriptGenerator, generate @@ -111,10 +111,18 @@ def test_generator_without_selector(): browser.page.goto("https://example.com") browser.page.wait_for_load_state("networkidle") - with record(browser) as rec: - rec.record_click(1) # No selector - - generator = ScriptGenerator(rec.trace) + # Create a trace manually with a step that has no selector + # (The recorder automatically infers selectors, so we create the step directly) + trace = Trace("https://example.com") + step = TraceStep( + ts=0, + type="click", + element_id=1, + selector=None # Explicitly no selector + ) + trace.add_step(step) + + generator = ScriptGenerator(trace) code = generator.generate_python() # Should include TODO comment for missing selector