sentienceapi 0.90.16__py3-none-any.whl → 0.98.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sentienceapi might be problematic. Click here for more details.

Files changed (90) hide show
  1. sentience/__init__.py +120 -6
  2. sentience/_extension_loader.py +156 -1
  3. sentience/action_executor.py +217 -0
  4. sentience/actions.py +758 -30
  5. sentience/agent.py +806 -293
  6. sentience/agent_config.py +3 -0
  7. sentience/agent_runtime.py +840 -0
  8. sentience/asserts/__init__.py +70 -0
  9. sentience/asserts/expect.py +621 -0
  10. sentience/asserts/query.py +383 -0
  11. sentience/async_api.py +89 -1141
  12. sentience/backends/__init__.py +137 -0
  13. sentience/backends/actions.py +372 -0
  14. sentience/backends/browser_use_adapter.py +241 -0
  15. sentience/backends/cdp_backend.py +393 -0
  16. sentience/backends/exceptions.py +211 -0
  17. sentience/backends/playwright_backend.py +194 -0
  18. sentience/backends/protocol.py +216 -0
  19. sentience/backends/sentience_context.py +469 -0
  20. sentience/backends/snapshot.py +483 -0
  21. sentience/base_agent.py +95 -0
  22. sentience/browser.py +678 -39
  23. sentience/browser_evaluator.py +299 -0
  24. sentience/canonicalization.py +207 -0
  25. sentience/cloud_tracing.py +507 -42
  26. sentience/constants.py +6 -0
  27. sentience/conversational_agent.py +77 -43
  28. sentience/cursor_policy.py +142 -0
  29. sentience/element_filter.py +136 -0
  30. sentience/expect.py +98 -2
  31. sentience/extension/background.js +56 -185
  32. sentience/extension/content.js +150 -287
  33. sentience/extension/injected_api.js +1088 -1368
  34. sentience/extension/manifest.json +1 -1
  35. sentience/extension/pkg/sentience_core.d.ts +22 -22
  36. sentience/extension/pkg/sentience_core.js +275 -433
  37. sentience/extension/pkg/sentience_core_bg.wasm +0 -0
  38. sentience/extension/release.json +47 -47
  39. sentience/failure_artifacts.py +241 -0
  40. sentience/formatting.py +9 -53
  41. sentience/inspector.py +183 -1
  42. sentience/integrations/__init__.py +6 -0
  43. sentience/integrations/langchain/__init__.py +12 -0
  44. sentience/integrations/langchain/context.py +18 -0
  45. sentience/integrations/langchain/core.py +326 -0
  46. sentience/integrations/langchain/tools.py +180 -0
  47. sentience/integrations/models.py +46 -0
  48. sentience/integrations/pydanticai/__init__.py +15 -0
  49. sentience/integrations/pydanticai/deps.py +20 -0
  50. sentience/integrations/pydanticai/toolset.py +468 -0
  51. sentience/llm_interaction_handler.py +191 -0
  52. sentience/llm_provider.py +765 -66
  53. sentience/llm_provider_utils.py +120 -0
  54. sentience/llm_response_builder.py +153 -0
  55. sentience/models.py +595 -3
  56. sentience/ordinal.py +280 -0
  57. sentience/overlay.py +109 -2
  58. sentience/protocols.py +228 -0
  59. sentience/query.py +67 -5
  60. sentience/read.py +95 -3
  61. sentience/recorder.py +223 -3
  62. sentience/schemas/trace_v1.json +128 -9
  63. sentience/screenshot.py +48 -2
  64. sentience/sentience_methods.py +86 -0
  65. sentience/snapshot.py +599 -55
  66. sentience/snapshot_diff.py +126 -0
  67. sentience/text_search.py +120 -5
  68. sentience/trace_event_builder.py +148 -0
  69. sentience/trace_file_manager.py +197 -0
  70. sentience/trace_indexing/index_schema.py +95 -7
  71. sentience/trace_indexing/indexer.py +105 -48
  72. sentience/tracer_factory.py +120 -9
  73. sentience/tracing.py +172 -8
  74. sentience/utils/__init__.py +40 -0
  75. sentience/utils/browser.py +46 -0
  76. sentience/{utils.py → utils/element.py} +3 -42
  77. sentience/utils/formatting.py +59 -0
  78. sentience/verification.py +618 -0
  79. sentience/visual_agent.py +2058 -0
  80. sentience/wait.py +68 -2
  81. {sentienceapi-0.90.16.dist-info → sentienceapi-0.98.0.dist-info}/METADATA +199 -40
  82. sentienceapi-0.98.0.dist-info/RECORD +92 -0
  83. sentience/extension/test-content.js +0 -4
  84. sentienceapi-0.90.16.dist-info/RECORD +0 -50
  85. {sentienceapi-0.90.16.dist-info → sentienceapi-0.98.0.dist-info}/WHEEL +0 -0
  86. {sentienceapi-0.90.16.dist-info → sentienceapi-0.98.0.dist-info}/entry_points.txt +0 -0
  87. {sentienceapi-0.90.16.dist-info → sentienceapi-0.98.0.dist-info}/licenses/LICENSE +0 -0
  88. {sentienceapi-0.90.16.dist-info → sentienceapi-0.98.0.dist-info}/licenses/LICENSE-APACHE +0 -0
  89. {sentienceapi-0.90.16.dist-info → sentienceapi-0.98.0.dist-info}/licenses/LICENSE-MIT +0 -0
  90. {sentienceapi-0.90.16.dist-info → sentienceapi-0.98.0.dist-info}/top_level.txt +0 -0
@@ -1,298 +1,161 @@
1
- // content.js - ISOLATED WORLD (Bridge between Main World and Background)
2
- console.log('[Sentience Bridge] Loaded.');
3
-
4
- // Detect if we're in a child frame (for iframe support)
5
- const isChildFrame = window !== window.top;
6
- if (isChildFrame) {
7
- console.log('[Sentience Bridge] Running in child frame:', window.location.href);
8
- }
9
-
10
- // 1. Pass Extension ID to Main World (So API knows where to find resources)
11
- document.documentElement.dataset.sentienceExtensionId = chrome.runtime.id;
12
-
13
- // 2. Message Router - Handles all communication between page and background
14
- window.addEventListener('message', (event) => {
15
- // Security check: only accept messages from same window
16
- if (event.source !== window) return;
17
-
18
- // Route different message types
19
- switch (event.data.type) {
20
- case 'SENTIENCE_SCREENSHOT_REQUEST':
21
- handleScreenshotRequest(event.data);
22
- break;
23
-
24
- case 'SENTIENCE_SNAPSHOT_REQUEST':
25
- handleSnapshotRequest(event.data);
1
+ !function() {
2
+ "use strict";
3
+ window, window.top;
4
+ document.documentElement.dataset.sentienceExtensionId = chrome.runtime.id, window.addEventListener("message", event => {
5
+ var data;
6
+ if (event.source === window) switch (event.data.type) {
7
+ case "SENTIENCE_SCREENSHOT_REQUEST":
8
+ data = event.data, chrome.runtime.sendMessage({
9
+ action: "captureScreenshot",
10
+ options: data.options
11
+ }, response => {
12
+ window.postMessage({
13
+ type: "SENTIENCE_SCREENSHOT_RESULT",
14
+ requestId: data.requestId,
15
+ screenshot: response?.success ? response.screenshot : null,
16
+ error: response?.error
17
+ }, "*");
18
+ });
26
19
  break;
27
20
 
28
- case 'SENTIENCE_SHOW_OVERLAY':
29
- handleShowOverlay(event.data);
21
+ case "SENTIENCE_SNAPSHOT_REQUEST":
22
+ !function(data) {
23
+ const startTime = performance.now();
24
+ let responded = !1;
25
+ const timeoutId = setTimeout(() => {
26
+ if (!responded) {
27
+ responded = !0;
28
+ const duration = performance.now() - startTime;
29
+ window.postMessage({
30
+ type: "SENTIENCE_SNAPSHOT_RESULT",
31
+ requestId: data.requestId,
32
+ error: "WASM processing timeout - background script may be unresponsive",
33
+ duration: duration
34
+ }, "*");
35
+ }
36
+ }, 2e4);
37
+ try {
38
+ chrome.runtime.sendMessage({
39
+ action: "processSnapshot",
40
+ rawData: data.rawData,
41
+ options: data.options
42
+ }, response => {
43
+ if (responded) return;
44
+ responded = !0, clearTimeout(timeoutId);
45
+ const duration = performance.now() - startTime;
46
+ chrome.runtime.lastError ? window.postMessage({
47
+ type: "SENTIENCE_SNAPSHOT_RESULT",
48
+ requestId: data.requestId,
49
+ error: `Chrome runtime error: ${chrome.runtime.lastError.message}`,
50
+ duration: duration
51
+ }, "*") : response?.success ? window.postMessage({
52
+ type: "SENTIENCE_SNAPSHOT_RESULT",
53
+ requestId: data.requestId,
54
+ elements: response.result.elements,
55
+ raw_elements: response.result.raw_elements,
56
+ duration: duration
57
+ }, "*") : window.postMessage({
58
+ type: "SENTIENCE_SNAPSHOT_RESULT",
59
+ requestId: data.requestId,
60
+ error: response?.error || "Processing failed",
61
+ duration: duration
62
+ }, "*");
63
+ });
64
+ } catch (error) {
65
+ if (!responded) {
66
+ responded = !0, clearTimeout(timeoutId);
67
+ const duration = performance.now() - startTime;
68
+ window.postMessage({
69
+ type: "SENTIENCE_SNAPSHOT_RESULT",
70
+ requestId: data.requestId,
71
+ error: `Failed to send message: ${error.message}`,
72
+ duration: duration
73
+ }, "*");
74
+ }
75
+ }
76
+ }(event.data);
30
77
  break;
31
78
 
32
- case 'SENTIENCE_CLEAR_OVERLAY':
33
- handleClearOverlay();
79
+ case "SENTIENCE_SHOW_OVERLAY":
80
+ !function(data) {
81
+ const {elements: elements, targetElementId: targetElementId} = data;
82
+ if (!elements || !Array.isArray(elements)) return;
83
+ removeOverlay();
84
+ const host = document.createElement("div");
85
+ host.id = OVERLAY_HOST_ID, host.style.cssText = "\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n width: 100vw !important;\n height: 100vh !important;\n pointer-events: none !important;\n z-index: 2147483647 !important;\n margin: 0 !important;\n padding: 0 !important;\n ",
86
+ document.body.appendChild(host);
87
+ const shadow = host.attachShadow({
88
+ mode: "closed"
89
+ }), maxImportance = Math.max(...elements.map(e => e.importance || 0), 1);
90
+ elements.forEach(element => {
91
+ const bbox = element.bbox;
92
+ if (!bbox) return;
93
+ const isTarget = element.id === targetElementId, isPrimary = element.visual_cues?.is_primary || !1, importance = element.importance || 0;
94
+ let color;
95
+ color = isTarget ? "#FF0000" : isPrimary ? "#0066FF" : "#00FF00";
96
+ const importanceRatio = maxImportance > 0 ? importance / maxImportance : .5, borderOpacity = isTarget ? 1 : isPrimary ? .9 : Math.max(.4, .5 + .5 * importanceRatio), fillOpacity = .2 * borderOpacity, borderWidth = isTarget ? 2 : isPrimary ? 1.5 : Math.max(.5, Math.round(2 * importanceRatio)), hexOpacity = Math.round(255 * fillOpacity).toString(16).padStart(2, "0"), box = document.createElement("div");
97
+ if (box.style.cssText = `\n position: absolute;\n left: ${bbox.x}px;\n top: ${bbox.y}px;\n width: ${bbox.width}px;\n height: ${bbox.height}px;\n border: ${borderWidth}px solid ${color};\n background-color: ${color}${hexOpacity};\n box-sizing: border-box;\n opacity: ${borderOpacity};\n pointer-events: none;\n `,
98
+ importance > 0 || isPrimary) {
99
+ const badge = document.createElement("span");
100
+ badge.textContent = isPrimary ? `⭐${importance}` : `${importance}`, badge.style.cssText = `\n position: absolute;\n top: -18px;\n left: 0;\n background: ${color};\n color: white;\n font-size: 11px;\n font-weight: bold;\n padding: 2px 6px;\n font-family: Arial, sans-serif;\n border-radius: 3px;\n opacity: 0.95;\n white-space: nowrap;\n pointer-events: none;\n `,
101
+ box.appendChild(badge);
102
+ }
103
+ if (isTarget) {
104
+ const targetIndicator = document.createElement("span");
105
+ targetIndicator.textContent = "🎯", targetIndicator.style.cssText = "\n position: absolute;\n top: -18px;\n right: 0;\n font-size: 16px;\n pointer-events: none;\n ",
106
+ box.appendChild(targetIndicator);
107
+ }
108
+ shadow.appendChild(box);
109
+ }), overlayTimeout = setTimeout(() => {
110
+ removeOverlay();
111
+ }, 5e3);
112
+ }(event.data);
34
113
  break;
35
114
 
36
- default:
37
- // Ignore unknown message types
115
+ case "SENTIENCE_CLEAR_OVERLAY":
116
+ removeOverlay();
38
117
  break;
39
- }
40
- });
41
-
42
- /**
43
- * Handle screenshot requests (existing functionality)
44
- */
45
- function handleScreenshotRequest(data) {
46
- chrome.runtime.sendMessage(
47
- { action: 'captureScreenshot', options: data.options },
48
- (response) => {
49
- window.postMessage({
50
- type: 'SENTIENCE_SCREENSHOT_RESULT',
51
- requestId: data.requestId,
52
- screenshot: response?.success ? response.screenshot : null,
53
- error: response?.error
54
- }, '*');
55
- }
56
- );
57
- }
58
-
59
- /**
60
- * Handle snapshot processing requests (NEW!)
61
- * Sends raw DOM data to background worker for WASM processing
62
- * Includes timeout protection to prevent extension crashes
63
- */
64
- function handleSnapshotRequest(data) {
65
- const startTime = performance.now();
66
- const TIMEOUT_MS = 20000; // 20 seconds (longer than injected_api timeout)
67
- let responded = false;
68
-
69
- // Timeout protection: if background doesn't respond, send error
70
- const timeoutId = setTimeout(() => {
71
- if (!responded) {
72
- responded = true;
73
- const duration = performance.now() - startTime;
74
- console.error(`[Sentience Bridge] ⚠️ WASM processing timeout after ${duration.toFixed(1)}ms`);
75
- window.postMessage({
76
- type: 'SENTIENCE_SNAPSHOT_RESULT',
77
- requestId: data.requestId,
78
- error: 'WASM processing timeout - background script may be unresponsive',
79
- duration: duration
80
- }, '*');
81
- }
82
- }, TIMEOUT_MS);
83
-
84
- try {
85
- chrome.runtime.sendMessage(
86
- {
87
- action: 'processSnapshot',
88
- rawData: data.rawData,
89
- options: data.options
90
- },
91
- (response) => {
92
- if (responded) return; // Already responded via timeout
93
- responded = true;
94
- clearTimeout(timeoutId);
95
-
96
- const duration = performance.now() - startTime;
97
-
98
- // Handle Chrome extension errors (e.g., background script crashed)
99
- if (chrome.runtime.lastError) {
100
- console.error('[Sentience Bridge] Chrome runtime error:', chrome.runtime.lastError.message);
101
- window.postMessage({
102
- type: 'SENTIENCE_SNAPSHOT_RESULT',
103
- requestId: data.requestId,
104
- error: `Chrome runtime error: ${chrome.runtime.lastError.message}`,
105
- duration: duration
106
- }, '*');
107
- return;
108
- }
109
-
110
- if (response?.success) {
111
- console.log(`[Sentience Bridge] ✓ WASM processing complete in ${duration.toFixed(1)}ms`);
112
- window.postMessage({
113
- type: 'SENTIENCE_SNAPSHOT_RESULT',
114
- requestId: data.requestId,
115
- elements: response.result.elements,
116
- raw_elements: response.result.raw_elements,
117
- duration: duration
118
- }, '*');
119
- } else {
120
- console.error('[Sentience Bridge] WASM processing failed:', response?.error);
121
- window.postMessage({
122
- type: 'SENTIENCE_SNAPSHOT_RESULT',
123
- requestId: data.requestId,
124
- error: response?.error || 'Processing failed',
125
- duration: duration
126
- }, '*');
127
- }
128
- }
129
- );
130
- } catch (error) {
131
- if (!responded) {
132
- responded = true;
133
- clearTimeout(timeoutId);
134
- const duration = performance.now() - startTime;
135
- console.error('[Sentience Bridge] Exception sending message:', error);
136
- window.postMessage({
137
- type: 'SENTIENCE_SNAPSHOT_RESULT',
138
- requestId: data.requestId,
139
- error: `Failed to send message: ${error.message}`,
140
- duration: duration
141
- }, '*');
142
- }
143
- }
144
- }
145
-
146
- // ============================================================================
147
- // Visual Overlay - Shadow DOM Implementation
148
- // ============================================================================
149
118
 
150
- const OVERLAY_HOST_ID = 'sentience-overlay-host';
151
- let overlayTimeout = null;
152
-
153
- /**
154
- * Show visual overlay highlighting elements using Shadow DOM
155
- * @param {Object} data - Message data with elements and targetElementId
156
- */
157
- function handleShowOverlay(data) {
158
- const { elements, targetElementId } = data;
159
-
160
- if (!elements || !Array.isArray(elements)) {
161
- console.warn('[Sentience Bridge] showOverlay: elements must be an array');
162
- return;
163
- }
164
-
165
- removeOverlay();
166
-
167
- // Create host with Shadow DOM for CSS isolation
168
- const host = document.createElement('div');
169
- host.id = OVERLAY_HOST_ID;
170
- host.style.cssText = `
171
- position: fixed !important;
172
- top: 0 !important;
173
- left: 0 !important;
174
- width: 100vw !important;
175
- height: 100vh !important;
176
- pointer-events: none !important;
177
- z-index: 2147483647 !important;
178
- margin: 0 !important;
179
- padding: 0 !important;
180
- `;
181
- document.body.appendChild(host);
182
-
183
- // Attach shadow root (closed mode for security and CSS isolation)
184
- const shadow = host.attachShadow({ mode: 'closed' });
185
-
186
- // Calculate max importance for scaling
187
- const maxImportance = Math.max(...elements.map(e => e.importance || 0), 1);
188
-
189
- elements.forEach((element) => {
190
- const bbox = element.bbox;
191
- if (!bbox) return;
192
-
193
- const isTarget = element.id === targetElementId;
194
- const isPrimary = element.visual_cues?.is_primary || false;
195
- const importance = element.importance || 0;
196
-
197
- // Color: Red (target), Blue (primary), Green (regular)
198
- let color;
199
- if (isTarget) color = '#FF0000';
200
- else if (isPrimary) color = '#0066FF';
201
- else color = '#00FF00';
202
-
203
- // Scale opacity and border width based on importance
204
- const importanceRatio = maxImportance > 0 ? importance / maxImportance : 0.5;
205
- const borderOpacity = isTarget ? 1.0 : (isPrimary ? 0.9 : Math.max(0.4, 0.5 + importanceRatio * 0.5));
206
- const fillOpacity = borderOpacity * 0.2;
207
- const borderWidth = isTarget ? 2 : (isPrimary ? 1.5 : Math.max(0.5, Math.round(importanceRatio * 2)));
208
-
209
- // Convert fill opacity to hex for background-color
210
- const hexOpacity = Math.round(fillOpacity * 255).toString(16).padStart(2, '0');
211
-
212
- // Create box with semi-transparent fill
213
- const box = document.createElement('div');
214
- box.style.cssText = `
215
- position: absolute;
216
- left: ${bbox.x}px;
217
- top: ${bbox.y}px;
218
- width: ${bbox.width}px;
219
- height: ${bbox.height}px;
220
- border: ${borderWidth}px solid ${color};
221
- background-color: ${color}${hexOpacity};
222
- box-sizing: border-box;
223
- opacity: ${borderOpacity};
224
- pointer-events: none;
225
- `;
226
-
227
- // Add badge showing importance score
228
- if (importance > 0 || isPrimary) {
229
- const badge = document.createElement('span');
230
- badge.textContent = isPrimary ? `⭐${importance}` : `${importance}`;
231
- badge.style.cssText = `
232
- position: absolute;
233
- top: -18px;
234
- left: 0;
235
- background: ${color};
236
- color: white;
237
- font-size: 11px;
238
- font-weight: bold;
239
- padding: 2px 6px;
240
- font-family: Arial, sans-serif;
241
- border-radius: 3px;
242
- opacity: 0.95;
243
- white-space: nowrap;
244
- pointer-events: none;
245
- `;
246
- box.appendChild(badge);
247
- }
248
-
249
- // Add target emoji for target element
250
- if (isTarget) {
251
- const targetIndicator = document.createElement('span');
252
- targetIndicator.textContent = '🎯';
253
- targetIndicator.style.cssText = `
254
- position: absolute;
255
- top: -18px;
256
- right: 0;
257
- font-size: 16px;
258
- pointer-events: none;
259
- `;
260
- box.appendChild(targetIndicator);
119
+ case "SENTIENCE_SHOW_GRID_OVERLAY":
120
+ !function(data) {
121
+ const {grids: grids, targetGridId: targetGridId} = data;
122
+ if (!grids || !Array.isArray(grids)) return;
123
+ removeOverlay();
124
+ const host = document.createElement("div");
125
+ host.id = OVERLAY_HOST_ID, host.style.cssText = "\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n width: 100vw !important;\n height: 100vh !important;\n pointer-events: none !important;\n z-index: 2147483647 !important;\n margin: 0 !important;\n padding: 0 !important;\n ",
126
+ document.body.appendChild(host);
127
+ const shadow = host.attachShadow({
128
+ mode: "closed"
129
+ });
130
+ grids.forEach(grid => {
131
+ const bbox = grid.bbox;
132
+ if (!bbox) return;
133
+ const isTarget = grid.grid_id === targetGridId, isDominant = !0 === grid.is_dominant;
134
+ let color = "#9B59B6";
135
+ isTarget ? color = "#FF0000" : isDominant && (color = "#FF8C00");
136
+ const borderStyle = isTarget ? "solid" : "dashed", borderWidth = isTarget ? 3 : isDominant ? 2.5 : 2, opacity = isTarget ? 1 : isDominant ? .9 : .8, fillOpacity = .1 * opacity, hexOpacity = Math.round(255 * fillOpacity).toString(16).padStart(2, "0"), box = document.createElement("div");
137
+ box.style.cssText = `\n position: absolute;\n left: ${bbox.x}px;\n top: ${bbox.y}px;\n width: ${bbox.width}px;\n height: ${bbox.height}px;\n border: ${borderWidth}px ${borderStyle} ${color};\n background-color: ${color}${hexOpacity};\n box-sizing: border-box;\n opacity: ${opacity};\n pointer-events: none;\n `;
138
+ let labelText = grid.label ? `Grid ${grid.grid_id}: ${grid.label}` : `Grid ${grid.grid_id}`;
139
+ grid.is_dominant && (labelText = `⭐ ${labelText} (dominant)`);
140
+ const badge = document.createElement("span");
141
+ if (badge.textContent = labelText, badge.style.cssText = `\n position: absolute;\n top: -18px;\n left: 0;\n background: ${color};\n color: white;\n font-size: 11px;\n font-weight: bold;\n padding: 2px 6px;\n font-family: Arial, sans-serif;\n border-radius: 3px;\n opacity: 0.95;\n white-space: nowrap;\n pointer-events: none;\n `,
142
+ box.appendChild(badge), isTarget) {
143
+ const targetIndicator = document.createElement("span");
144
+ targetIndicator.textContent = "🎯", targetIndicator.style.cssText = "\n position: absolute;\n top: -18px;\n right: 0;\n font-size: 16px;\n pointer-events: none;\n ",
145
+ box.appendChild(targetIndicator);
146
+ }
147
+ shadow.appendChild(box);
148
+ }), overlayTimeout = setTimeout(() => {
149
+ removeOverlay();
150
+ }, 5e3);
151
+ }(event.data);
261
152
  }
262
-
263
- shadow.appendChild(box);
264
153
  });
265
-
266
- console.log(`[Sentience Bridge] Overlay shown for ${elements.length} elements`);
267
-
268
- // Auto-remove after 5 seconds
269
- overlayTimeout = setTimeout(() => {
270
- removeOverlay();
271
- console.log('[Sentience Bridge] Overlay auto-cleared after 5 seconds');
272
- }, 5000);
273
- }
274
-
275
- /**
276
- * Clear overlay manually
277
- */
278
- function handleClearOverlay() {
279
- removeOverlay();
280
- console.log('[Sentience Bridge] Overlay cleared manually');
281
- }
282
-
283
- /**
284
- * Remove overlay from DOM
285
- */
286
- function removeOverlay() {
287
- const existing = document.getElementById(OVERLAY_HOST_ID);
288
- if (existing) {
289
- existing.remove();
154
+ const OVERLAY_HOST_ID = "sentience-overlay-host";
155
+ let overlayTimeout = null;
156
+ function removeOverlay() {
157
+ const existing = document.getElementById(OVERLAY_HOST_ID);
158
+ existing && existing.remove(), overlayTimeout && (clearTimeout(overlayTimeout),
159
+ overlayTimeout = null);
290
160
  }
291
-
292
- if (overlayTimeout) {
293
- clearTimeout(overlayTimeout);
294
- overlayTimeout = null;
295
- }
296
- }
297
-
298
- // console.log('[Sentience Bridge] Ready - Extension ID:', chrome.runtime.id);
161
+ }();