@weavetab/mcp 2.5.0-beta.0

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.
Files changed (281) hide show
  1. package/AGENTS.md +48 -0
  2. package/ARCHITECTURE.md +590 -0
  3. package/LICENSE +631 -0
  4. package/README.md +909 -0
  5. package/SECURITY.md +196 -0
  6. package/TROUBLESHOOTING.md +365 -0
  7. package/dist/anticaptcha/detector.d.ts +49 -0
  8. package/dist/anticaptcha/detector.js +465 -0
  9. package/dist/anticaptcha/integration.d.ts +36 -0
  10. package/dist/anticaptcha/integration.js +123 -0
  11. package/dist/anticaptcha/solver.d.ts +27 -0
  12. package/dist/anticaptcha/solver.js +552 -0
  13. package/dist/audit/logger.d.ts +13 -0
  14. package/dist/audit/logger.js +177 -0
  15. package/dist/audit/telemetry.d.ts +36 -0
  16. package/dist/audit/telemetry.js +162 -0
  17. package/dist/cdp/bridge.d.ts +16 -0
  18. package/dist/cdp/bridge.js +826 -0
  19. package/dist/cdp/confirm.d.ts +26 -0
  20. package/dist/cdp/confirm.js +79 -0
  21. package/dist/cdp/connector.d.ts +37 -0
  22. package/dist/cdp/connector.js +661 -0
  23. package/dist/cdp/extension.d.ts +36 -0
  24. package/dist/cdp/extension.js +118 -0
  25. package/dist/cdp/finder.d.ts +1 -0
  26. package/dist/cdp/finder.js +2 -0
  27. package/dist/cdp/helpers.d.ts +11 -0
  28. package/dist/cdp/helpers.js +195 -0
  29. package/dist/cdp/label_cache.d.ts +12 -0
  30. package/dist/cdp/label_cache.js +51 -0
  31. package/dist/cdp/lock.d.ts +33 -0
  32. package/dist/cdp/lock.js +206 -0
  33. package/dist/cdp/omni_broker.d.ts +17 -0
  34. package/dist/cdp/omni_broker.js +119 -0
  35. package/dist/cdp/profiles.d.ts +1 -0
  36. package/dist/cdp/profiles.js +87 -0
  37. package/dist/cdp/pruner.d.ts +41 -0
  38. package/dist/cdp/pruner.js +263 -0
  39. package/dist/cdp/ref_cache.d.ts +101 -0
  40. package/dist/cdp/ref_cache.js +234 -0
  41. package/dist/cdp/retry.d.ts +5 -0
  42. package/dist/cdp/retry.js +39 -0
  43. package/dist/cdp/retry_enhanced.d.ts +44 -0
  44. package/dist/cdp/retry_enhanced.js +102 -0
  45. package/dist/cdp/security.d.ts +50 -0
  46. package/dist/cdp/security.js +237 -0
  47. package/dist/cdp/verify.d.ts +39 -0
  48. package/dist/cdp/verify.js +143 -0
  49. package/dist/cdp/walker.d.ts +91 -0
  50. package/dist/cdp/walker.js +896 -0
  51. package/dist/cdp/walker.test.d.ts +2 -0
  52. package/dist/cdp/walker.test.js +172 -0
  53. package/dist/cdp/websocket_transport.d.ts +15 -0
  54. package/dist/cdp/websocket_transport.js +118 -0
  55. package/dist/cli/plugin.d.ts +9 -0
  56. package/dist/cli/plugin.js +1134 -0
  57. package/dist/cli/updater.d.ts +1 -0
  58. package/dist/cli/updater.js +72 -0
  59. package/dist/config/loader.d.ts +120 -0
  60. package/dist/config/loader.js +704 -0
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +1248 -0
  63. package/dist/intelligence/hints.d.ts +11 -0
  64. package/dist/intelligence/hints.js +39 -0
  65. package/dist/intelligence/intent.d.ts +33 -0
  66. package/dist/intelligence/intent.js +171 -0
  67. package/dist/intelligence/sessionMemory.d.ts +66 -0
  68. package/dist/intelligence/sessionMemory.js +284 -0
  69. package/dist/intelligence/sessionMemory.test.d.ts +2 -0
  70. package/dist/intelligence/sessionMemory.test.js +62 -0
  71. package/dist/intelligence/trail.d.ts +64 -0
  72. package/dist/intelligence/trail.js +233 -0
  73. package/dist/intelligence/trail.test.d.ts +2 -0
  74. package/dist/intelligence/trail.test.js +110 -0
  75. package/dist/intelligence/vision.d.ts +24 -0
  76. package/dist/intelligence/vision.js +151 -0
  77. package/dist/lifecycle/mission_manager.d.ts +16 -0
  78. package/dist/lifecycle/mission_manager.js +44 -0
  79. package/dist/overlay/bubble.d.ts +16 -0
  80. package/dist/overlay/bubble.js +37 -0
  81. package/dist/overlay/ghost.d.ts +35 -0
  82. package/dist/overlay/ghost.js +508 -0
  83. package/dist/overlay/injector.d.ts +22 -0
  84. package/dist/overlay/injector.js +198 -0
  85. package/dist/plugin/interface.d.ts +134 -0
  86. package/dist/plugin/interface.js +1 -0
  87. package/dist/plugin/loader.d.ts +18 -0
  88. package/dist/plugin/loader.js +155 -0
  89. package/dist/plugin/registry.d.ts +50 -0
  90. package/dist/plugin/registry.js +234 -0
  91. package/dist/security/blacklist.d.ts +24 -0
  92. package/dist/security/blacklist.js +80 -0
  93. package/dist/security/filter.d.ts +2 -0
  94. package/dist/security/filter.js +20 -0
  95. package/dist/security/guard.d.ts +8 -0
  96. package/dist/security/guard.js +35 -0
  97. package/dist/security/ratelimit.d.ts +11 -0
  98. package/dist/security/ratelimit.js +57 -0
  99. package/dist/security/secrets.d.ts +75 -0
  100. package/dist/security/secrets.js +365 -0
  101. package/dist/sensors/actionability.d.ts +81 -0
  102. package/dist/sensors/actionability.js +303 -0
  103. package/dist/sensors/guard.d.ts +25 -0
  104. package/dist/sensors/guard.js +168 -0
  105. package/dist/sensors/mutations.d.ts +23 -0
  106. package/dist/sensors/mutations.js +93 -0
  107. package/dist/sensors/network.d.ts +67 -0
  108. package/dist/sensors/network.js +193 -0
  109. package/dist/sensors/operation_monitor.d.ts +80 -0
  110. package/dist/sensors/operation_monitor.js +203 -0
  111. package/dist/sensors/pacing.d.ts +44 -0
  112. package/dist/sensors/pacing.js +111 -0
  113. package/dist/sensors/thoughts.d.ts +2 -0
  114. package/dist/sensors/thoughts.js +59 -0
  115. package/dist/sensors/wait.d.ts +12 -0
  116. package/dist/sensors/wait.js +44 -0
  117. package/dist/server.d.ts +7 -0
  118. package/dist/server.js +1687 -0
  119. package/dist/state/agent.d.ts +36 -0
  120. package/dist/state/agent.js +101 -0
  121. package/dist/state/cache.d.ts +16 -0
  122. package/dist/state/cache.js +92 -0
  123. package/dist/state/cache.test.d.ts +2 -0
  124. package/dist/state/cache.test.js +76 -0
  125. package/dist/state/delta.d.ts +71 -0
  126. package/dist/state/delta.js +234 -0
  127. package/dist/state/delta.test.d.ts +2 -0
  128. package/dist/state/delta.test.js +279 -0
  129. package/dist/state/groups.d.ts +15 -0
  130. package/dist/state/groups.js +80 -0
  131. package/dist/state/session.d.ts +30 -0
  132. package/dist/state/session.js +155 -0
  133. package/dist/tools/accessibility.d.ts +14 -0
  134. package/dist/tools/accessibility.js +32 -0
  135. package/dist/tools/ask.d.ts +14 -0
  136. package/dist/tools/ask.js +49 -0
  137. package/dist/tools/automation.d.ts +36 -0
  138. package/dist/tools/automation.js +218 -0
  139. package/dist/tools/browser_detect.d.ts +13 -0
  140. package/dist/tools/browser_detect.js +73 -0
  141. package/dist/tools/burst.d.ts +35 -0
  142. package/dist/tools/burst.js +344 -0
  143. package/dist/tools/canvas.d.ts +25 -0
  144. package/dist/tools/canvas.js +132 -0
  145. package/dist/tools/captcha.d.ts +38 -0
  146. package/dist/tools/captcha.js +209 -0
  147. package/dist/tools/click.d.ts +36 -0
  148. package/dist/tools/click.js +561 -0
  149. package/dist/tools/click_and_wait.d.ts +33 -0
  150. package/dist/tools/click_and_wait.js +149 -0
  151. package/dist/tools/clipboard.d.ts +29 -0
  152. package/dist/tools/clipboard.js +279 -0
  153. package/dist/tools/console.d.ts +21 -0
  154. package/dist/tools/console.js +107 -0
  155. package/dist/tools/cookies.d.ts +67 -0
  156. package/dist/tools/cookies.js +121 -0
  157. package/dist/tools/design_extract.d.ts +109 -0
  158. package/dist/tools/design_extract.js +641 -0
  159. package/dist/tools/dialog.d.ts +24 -0
  160. package/dist/tools/dialog.js +109 -0
  161. package/dist/tools/drag.d.ts +15 -0
  162. package/dist/tools/drag.js +29 -0
  163. package/dist/tools/emulation.d.ts +33 -0
  164. package/dist/tools/emulation.js +69 -0
  165. package/dist/tools/eval.d.ts +20 -0
  166. package/dist/tools/eval.js +92 -0
  167. package/dist/tools/fill.d.ts +41 -0
  168. package/dist/tools/fill.js +667 -0
  169. package/dist/tools/find.d.ts +30 -0
  170. package/dist/tools/find.js +60 -0
  171. package/dist/tools/geo.d.ts +19 -0
  172. package/dist/tools/geo.js +29 -0
  173. package/dist/tools/github/analyze.d.ts +4 -0
  174. package/dist/tools/github/analyze.js +113 -0
  175. package/dist/tools/github/cache.d.ts +21 -0
  176. package/dist/tools/github/cache.js +28 -0
  177. package/dist/tools/github/index.d.ts +4 -0
  178. package/dist/tools/github/index.js +4 -0
  179. package/dist/tools/github/issues.d.ts +9 -0
  180. package/dist/tools/github/issues.js +88 -0
  181. package/dist/tools/github/pr.d.ts +5 -0
  182. package/dist/tools/github/pr.js +88 -0
  183. package/dist/tools/github/rateLimiter.d.ts +4 -0
  184. package/dist/tools/github/rateLimiter.js +46 -0
  185. package/dist/tools/github/read.d.ts +34 -0
  186. package/dist/tools/github/read.js +74 -0
  187. package/dist/tools/github/repoContext.d.ts +20 -0
  188. package/dist/tools/github/repoContext.js +129 -0
  189. package/dist/tools/highlight.d.ts +20 -0
  190. package/dist/tools/highlight.js +140 -0
  191. package/dist/tools/hover.d.ts +19 -0
  192. package/dist/tools/hover.js +57 -0
  193. package/dist/tools/inspect.d.ts +17 -0
  194. package/dist/tools/inspect.js +125 -0
  195. package/dist/tools/integration.test.d.ts +2 -0
  196. package/dist/tools/integration.test.js +96 -0
  197. package/dist/tools/key.d.ts +25 -0
  198. package/dist/tools/key.js +173 -0
  199. package/dist/tools/macro_compiler.d.ts +7 -0
  200. package/dist/tools/macro_compiler.js +82 -0
  201. package/dist/tools/mouse.d.ts +21 -0
  202. package/dist/tools/mouse.js +62 -0
  203. package/dist/tools/navigate.d.ts +16 -0
  204. package/dist/tools/navigate.js +188 -0
  205. package/dist/tools/network_intercept.d.ts +17 -0
  206. package/dist/tools/network_intercept.js +151 -0
  207. package/dist/tools/office/index.d.ts +9 -0
  208. package/dist/tools/office/index.js +7 -0
  209. package/dist/tools/office/list.d.ts +15 -0
  210. package/dist/tools/office/list.js +41 -0
  211. package/dist/tools/office/parse.d.ts +20 -0
  212. package/dist/tools/office/parse.js +172 -0
  213. package/dist/tools/pdf.d.ts +22 -0
  214. package/dist/tools/pdf.js +42 -0
  215. package/dist/tools/performance.d.ts +12 -0
  216. package/dist/tools/performance.js +47 -0
  217. package/dist/tools/performance_test.d.ts +70 -0
  218. package/dist/tools/performance_test.js +128 -0
  219. package/dist/tools/plan.d.ts +39 -0
  220. package/dist/tools/plan.js +161 -0
  221. package/dist/tools/pointer.d.ts +35 -0
  222. package/dist/tools/pointer.js +522 -0
  223. package/dist/tools/read.d.ts +30 -0
  224. package/dist/tools/read.js +488 -0
  225. package/dist/tools/recording.d.ts +45 -0
  226. package/dist/tools/recording.js +124 -0
  227. package/dist/tools/response.d.ts +129 -0
  228. package/dist/tools/response.js +5 -0
  229. package/dist/tools/scrape.d.ts +24 -0
  230. package/dist/tools/scrape.js +140 -0
  231. package/dist/tools/screenshot.d.ts +40 -0
  232. package/dist/tools/screenshot.js +79 -0
  233. package/dist/tools/scroll.d.ts +23 -0
  234. package/dist/tools/scroll.js +196 -0
  235. package/dist/tools/select.d.ts +13 -0
  236. package/dist/tools/select.js +217 -0
  237. package/dist/tools/snapshot.d.ts +27 -0
  238. package/dist/tools/snapshot.js +99 -0
  239. package/dist/tools/storage.d.ts +18 -0
  240. package/dist/tools/storage.js +29 -0
  241. package/dist/tools/tabs.d.ts +77 -0
  242. package/dist/tools/tabs.js +342 -0
  243. package/dist/tools/thought.d.ts +16 -0
  244. package/dist/tools/thought.js +79 -0
  245. package/dist/tools/type.d.ts +65 -0
  246. package/dist/tools/type.js +1061 -0
  247. package/dist/tools/type_secret.d.ts +23 -0
  248. package/dist/tools/type_secret.js +229 -0
  249. package/dist/tools/upload.d.ts +19 -0
  250. package/dist/tools/upload.js +103 -0
  251. package/dist/tools/video_compiler.d.ts +24 -0
  252. package/dist/tools/video_compiler.js +199 -0
  253. package/dist/tools/viewport.d.ts +44 -0
  254. package/dist/tools/viewport.js +230 -0
  255. package/dist/tools/wait.d.ts +19 -0
  256. package/dist/tools/wait.js +202 -0
  257. package/dist/tools/wait_for.d.ts +23 -0
  258. package/dist/tools/wait_for.js +30 -0
  259. package/dist/tools/windows.d.ts +22 -0
  260. package/dist/tools/windows.js +57 -0
  261. package/dist/ui/index.d.ts +1 -0
  262. package/dist/ui/index.js +12 -0
  263. package/docs/DEV_MODE.md +68 -0
  264. package/docs/DOCUMENTATION.md +532 -0
  265. package/docs/PLUGINS.md +538 -0
  266. package/docs/TOOLS.md +104 -0
  267. package/docs/dev_plugin.md +310 -0
  268. package/ext-dist/background.js +467 -0
  269. package/ext-dist/content.js +3089 -0
  270. package/ext-dist/dashboard.html +583 -0
  271. package/ext-dist/hud/hud.js +73 -0
  272. package/ext-dist/hud/index.html +20 -0
  273. package/ext-dist/manifest.json +77 -0
  274. package/ext-dist/sidepanel/sidepanel.js +25 -0
  275. package/ext-dist/sidepanel.html +547 -0
  276. package/ext-dist/styles.css +218 -0
  277. package/icon.png +0 -0
  278. package/mcp.json +796 -0
  279. package/package.json +113 -0
  280. package/server.json +23 -0
  281. package/smithery.yaml +11 -0
@@ -0,0 +1,467 @@
1
+ const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
2
+
3
+ // Allow users to open the side panel by clicking on the action toolbar icon
4
+ if (browserAPI.sidePanel) {
5
+ browserAPI.sidePanel
6
+ .setPanelBehavior({ openPanelOnActionClick: true })
7
+ .catch((error) => console.error(error));
8
+ }
9
+
10
+ // ── Browser Startup: auto-open sidepanel ────────────────────────
11
+ // Clear minimized state on browser launch so sidepanel opens automatically
12
+ browserAPI.runtime.onStartup.addListener(function() {
13
+ try { browserAPI.storage.local.remove('wt_panel_minimized'); } catch(e) {}
14
+ });
15
+ // Also clear on install (first launch)
16
+ browserAPI.runtime.onInstalled.addListener(function() {
17
+ try { browserAPI.storage.local.remove('wt_panel_minimized'); } catch(e) {}
18
+ });
19
+
20
+ // HUD toggle command handler
21
+ browserAPI.commands.onCommand.addListener(function(command) {
22
+ if (command === 'toggle-hud') {
23
+ browserAPI.tabs.query({ active: true, currentWindow: true }, function(tabs) {
24
+ if (tabs[0] && tabs[0].id) {
25
+ browserAPI.tabs.sendMessage(tabs[0].id, { type: 'TOGGLE_HUD' }).catch(function(){});
26
+ }
27
+ });
28
+ }
29
+ if (command === 'toggle-debug') {
30
+ browserAPI.tabs.query({ active: true, currentWindow: true }, function(tabs) {
31
+ if (tabs[0] && tabs[0].id) {
32
+ browserAPI.tabs.sendMessage(tabs[0].id, { type: 'TOGGLE_DEBUG' }).catch(function(){});
33
+ }
34
+ });
35
+ }
36
+ });
37
+
38
+ // Runtime state
39
+ const controlledTabs = new Set();
40
+ const anchorTabs = new Set();
41
+ let currentGroupState = 'idle';
42
+ let activeSessionTabId = null;
43
+
44
+ // ── Sidepanel Open/Close Tracking ────────────────────────────────────────────
45
+ let isSidepanelOpen = false;
46
+ browserAPI.runtime.onConnect.addListener(function(port) {
47
+ if (port.name === 'wt-sidepanel') {
48
+ isSidepanelOpen = true;
49
+ // Notify all controlled tabs so HUD can suppress browser_thought pill
50
+ controlledTabs.forEach(tabId => {
51
+ browserAPI.tabs.sendMessage(tabId, { type: 'WT_SIDEPANEL_STATE', open: true }).catch(() => {});
52
+ });
53
+ port.onDisconnect.addListener(function() {
54
+ isSidepanelOpen = false;
55
+ controlledTabs.forEach(tabId => {
56
+ browserAPI.tabs.sendMessage(tabId, { type: 'WT_SIDEPANEL_STATE', open: false }).catch(() => {});
57
+ });
58
+ });
59
+ }
60
+ });
61
+
62
+ // State color mapping — always blue, never yellow
63
+ const STATE_COLORS = {
64
+ idle: 'grey',
65
+ weaving: 'blue',
66
+ stuck: 'blue', // Keep blue, never yellow
67
+ };
68
+
69
+ const STATE_BADGE = {
70
+ idle: { text: '', color: null },
71
+ weaving: { text: '\u25CF', color: '#3B82F6' },
72
+ stuck: { text: '\u25CF', color: '#3B82F6' }, // blue dot, not red
73
+ };
74
+
75
+
76
+ browserAPI.tabs.onCreated.addListener(async (tab) => {
77
+ if (!controlledTabs.has(tab.id)) return;
78
+ // Ungroup immediately — prevents Chrome from keeping this tab in any group
79
+ try { browserAPI.tabs.ungroup(tab.id).catch(() => {}); } catch(e) {}
80
+ });
81
+
82
+ browserAPI.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
83
+ if (!controlledTabs.has(tabId)) return;
84
+ if (changeInfo.status === 'complete') {
85
+ // Ungroup on update to handle cases where Chrome re-groups after navigation
86
+ try { browserAPI.tabs.ungroup(tabId).catch(() => {}); } catch(e) {}
87
+ browserAPI.tabs.sendMessage(tabId, { type: 'Weavetab_GROUP_STATE', state: currentGroupState, isActive: tabId === activeSessionTabId }).catch(() => {});
88
+ }
89
+ });
90
+
91
+
92
+ // Per-tab network interception using declarativeNetRequest
93
+ async function enableIntercept(tabId, pattern, mockStatus, mockBody) {
94
+ try {
95
+ await browserAPI.declarativeNetRequest.updateSessionRules({
96
+ addRules: [{
97
+ id: tabId,
98
+ priority: 1,
99
+ condition: { urlFilter: pattern, tabIds: [tabId] },
100
+ action: {
101
+ type: 'redirect',
102
+ redirect: {
103
+ url: `data:application/json,${encodeURIComponent(mockBody)}`
104
+ }
105
+ }
106
+ }]
107
+ });
108
+ return { success: true };
109
+ } catch (e) {
110
+ console.error("Failed to enable intercept:", e);
111
+ return { success: false, error: e.message };
112
+ }
113
+ }
114
+
115
+ async function disableIntercept(tabId) {
116
+ try {
117
+ await browserAPI.declarativeNetRequest.updateSessionRules({
118
+ removeRuleIds: [tabId]
119
+ });
120
+ return { success: true };
121
+ } catch (e) {
122
+ console.error("Failed to disable intercept:", e);
123
+ return { success: false, error: e.message };
124
+ }
125
+ }
126
+
127
+ // Stale state guard: if no Weavetab_GROUP_STATE message for 90s, reset all controlled tabs to idle
128
+ let __wt_stateTimeout = null;
129
+
130
+ function resetToIdle() {
131
+ if (currentGroupState === 'idle') return;
132
+ currentGroupState = 'idle';
133
+ activeSessionTabId = null;
134
+ controlledTabs.forEach(tabId => {
135
+ browserAPI.tabs.sendMessage(tabId, { type: 'Weavetab_GROUP_STATE', state: 'idle', isActive: false }).catch(() => {});
136
+ });
137
+ if (browserAPI.action) {
138
+ browserAPI.action.setBadgeText({ text: '' }).catch(() => {});
139
+ }
140
+ }
141
+
142
+ function refreshStateTimeout() {
143
+ if (__wt_stateTimeout) clearTimeout(__wt_stateTimeout);
144
+ __wt_stateTimeout = setTimeout(resetToIdle, 120000); // 2 min stale guard
145
+ }
146
+
147
+ // Bridge: relay state from CLI (via CDP executeScript) to content scripts
148
+ // ── Sidepanel broadcast ─────────────────────────────────────────────────────
149
+ // Routes agent-activity messages (tool calls, thoughts, status) to the sidepanel
150
+ // so all visual state lives there, not in in-page overlays.
151
+ function broadcastToSidepanel(msg) {
152
+ browserAPI.runtime.sendMessage(msg).catch(() => {}); // sidepanel listens on runtime
153
+ }
154
+
155
+ browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
156
+ // ── Forward tool call events to sidepanel ──
157
+ if (message.type === 'TOOL_CALL_EVENT') {
158
+ broadcastToSidepanel({ type: 'TOOL_CALL_EVENT', payload: message.payload });
159
+ }
160
+ // ── Smart thought routing ──
161
+ // browser_thought (kind='thought') → sidepanel when open, HUD when closed
162
+ // Scripted pill suppression is handled in the HUD via WT_SIDEPANEL_STATE
163
+ if (message.type === 'WT_THOUGHT') {
164
+ if (isSidepanelOpen) {
165
+ // Route to sidepanel only — suppress HUD rendering
166
+ broadcastToSidepanel({ type: 'WT_THOUGHT', text: message.text, kind: message.kind });
167
+ // Tell the HUD iframe in the active tab to suppress the browser_thought pill
168
+ if (sender.tab && sender.tab.id) {
169
+ browserAPI.tabs.sendMessage(sender.tab.id, { type: 'WT_SUPPRESS_THOUGHT_HUD' }).catch(() => {});
170
+ }
171
+ } else {
172
+ // Sidepanel closed — let the HUD handle it normally via the iframe relay
173
+ broadcastToSidepanel({ type: 'WT_THOUGHT', text: message.text, kind: message.kind });
174
+ }
175
+ }
176
+
177
+ // ── User messaging ──
178
+ if (message.type === 'WT_USER_NOTE') {
179
+ const payload = {
180
+ type: 'WT_USER_NOTE_INJECT',
181
+ text: message.text,
182
+ snipe: message.snipe
183
+ };
184
+ // Query active tab in current window and send message immediately
185
+ browserAPI.tabs.query({ active: true, currentWindow: true }, function(tabs) {
186
+ if (tabs[0] && tabs[0].id) {
187
+ browserAPI.tabs.sendMessage(tabs[0].id, payload).catch(function(){});
188
+ }
189
+ });
190
+ // Send to activeSessionTabId if set
191
+ if (activeSessionTabId) {
192
+ browserAPI.tabs.sendMessage(activeSessionTabId, payload).catch(function(){});
193
+ }
194
+ // Fallback to all controlled tabs
195
+ controlledTabs.forEach(tabId => {
196
+ browserAPI.tabs.sendMessage(tabId, payload).catch(function(){});
197
+ });
198
+ }
199
+
200
+ if (message.type === 'WT_TOGGLE_MASTER_VISUALS') {
201
+ browserAPI.storage.local.set({ wt_master_visuals: message.enabled }).catch(() => {});
202
+ controlledTabs.forEach(tabId => {
203
+ browserAPI.tabs.sendMessage(tabId, { type: 'WT_TOGGLE_MASTER_VISUALS', enabled: message.enabled }).catch(() => {});
204
+ });
205
+ sendResponse({ ok: true });
206
+ }
207
+
208
+ if (message.type === 'Weavetab_STATE') {
209
+ broadcastToSidepanel(message);
210
+ controlledTabs.forEach(tabId => {
211
+ browserAPI.tabs.sendMessage(tabId, message).catch(() => {});
212
+ });
213
+ sendResponse({ ok: true });
214
+ }
215
+ if (message.type === 'Weavetab_CONFIG') {
216
+ const { groupTabs } = message;
217
+ browserAPI.storage.local.set({
218
+ weaveGroupEnabled: groupTabs !== false,
219
+ }).catch(() => {});
220
+ sendResponse({ ok: true });
221
+ }
222
+ if (message.type === 'Weavetab_GROUP_STATE') {
223
+ const { state, word } = message;
224
+ currentGroupState = state;
225
+ refreshStateTimeout();
226
+ const color = STATE_COLORS[state] || 'blue';
227
+ const badge = STATE_BADGE[state] || STATE_BADGE.idle;
228
+
229
+ // Track which tab relayed this state update (the active session tab)
230
+ if (sender.tab) activeSessionTabId = sender.tab.id;
231
+ // Per-tab badge
232
+ if (browserAPI.action) {
233
+ const ids = Array.from(controlledTabs);
234
+ ids.forEach(tid => {
235
+ if (badge.text) {
236
+ browserAPI.action.setBadgeText({ tabId: tid, text: badge.text }).catch(() => {});
237
+ if (badge.color) {
238
+ browserAPI.action.setBadgeBackgroundColor({ tabId: tid, color: badge.color }).catch(() => {});
239
+ }
240
+ } else {
241
+ browserAPI.action.setBadgeText({ tabId: tid, text: '' }).catch(() => {});
242
+ }
243
+ });
244
+ }
245
+
246
+ // Broadcast state to all controlled tabs so they update their favicons
247
+ controlledTabs.forEach(tabId => {
248
+ browserAPI.tabs.sendMessage(tabId, { type: 'Weavetab_GROUP_STATE', state, isActive: tabId === activeSessionTabId }).catch(() => {});
249
+ });
250
+
251
+ // ── Mirror to sidepanel status dot ──
252
+ broadcastToSidepanel({ type: 'WT_AGENT_STATUS', status: state });
253
+
254
+ sendResponse({ ok: true });
255
+ }
256
+ if (message.type === 'REGISTER_TAB') {
257
+ // Skip about:blank and chrome:// pages
258
+ if (sender.tab.url === 'about:blank' || sender.tab.url.startsWith('chrome://')) {
259
+ sendResponse({ ok: true, skipped: true });
260
+ return;
261
+ }
262
+ controlledTabs.add(sender.tab.id);
263
+ refreshStateTimeout();
264
+
265
+ // Immediately inform the tab of the current group state so it can set its favicon
266
+ browserAPI.tabs.sendMessage(sender.tab.id, { type: 'Weavetab_GROUP_STATE', state: currentGroupState, isActive: sender.tab.id === activeSessionTabId }).catch(() => {});
267
+
268
+ updateBadge();
269
+ sendResponse({ ok: true });
270
+ }
271
+
272
+ if (message.type === 'NETWORK_INTERCEPT_ENABLE') {
273
+ enableIntercept(sender.tab.id, message.pattern, message.mockStatus, message.mockBody)
274
+ .then(res => sendResponse(res));
275
+ return true;
276
+ }
277
+ if (message.type === 'NETWORK_INTERCEPT_DISABLE') {
278
+ disableIntercept(sender.tab.id)
279
+ .then(res => sendResponse(res));
280
+ return true;
281
+ }
282
+ if (message.type === 'GET_ANCHOR_STATUS') {
283
+ sendResponse({ isAnchor: anchorTabs.has(sender.tab.id) });
284
+ return true;
285
+ }
286
+ if (message.type === 'SET_ANCHOR_TAB') {
287
+ if (message.anchor) anchorTabs.add(sender.tab.id);
288
+ else anchorTabs.delete(sender.tab.id);
289
+ sendResponse({ ok: true });
290
+ }
291
+
292
+ // ── User feedback relay: sidepanel / content script → storage queue ────────
293
+ // CDP polls window.__Weavetab.popUserFeedback() or window.__wt_chat_queue which reads from this queue.
294
+ if (message.type === 'USER_FEEDBACK' || message.type === 'WT_USER_NOTE') {
295
+ if (message.text || message.snipe) {
296
+ const noteEntry = {
297
+ text: message.text || '',
298
+ snipe: message.snipe || null,
299
+ ts: message.ts || Date.now(),
300
+ tabId: sender.tab && sender.tab.id
301
+ };
302
+
303
+ browserAPI.storage.local.get('__wt_feedback_queue', function(res) {
304
+ const queue = res['__wt_feedback_queue'] || [];
305
+ queue.push(noteEntry);
306
+ // Cap queue at 20 entries
307
+ if (queue.length > 20) queue.splice(0, queue.length - 20);
308
+ browserAPI.storage.local.set({ '__wt_feedback_queue': queue });
309
+ });
310
+
311
+ // Forward directly to active session tab content script for immediate DOM queue push & interrupt
312
+ browserAPI.tabs.query({ active: true, currentWindow: true }, (tabs) => {
313
+ if (tabs[0]?.id) {
314
+ browserAPI.tabs.sendMessage(tabs[0].id, {
315
+ type: 'WT_SNIPE_INJECT',
316
+ entry: noteEntry
317
+ }).catch(() => {});
318
+ }
319
+ });
320
+ }
321
+ sendResponse({ ok: true });
322
+ }
323
+
324
+
325
+ // Alert tab: mark tabs with alert dialogs waiting + write to storage so CDP can see it
326
+ if (message.type === 'TAB_ALERT_WAITING') {
327
+ const alertTabId = sender.tab && sender.tab.id;
328
+ if (alertTabId) {
329
+ browserAPI.tabs.sendMessage(alertTabId, { type: 'Weavetab_GROUP_STATE', state: 'weaving', isActive: false }).catch(() => {});
330
+ // Write alert state to storage — CDP polls __Weavetab.getAlertState()
331
+ browserAPI.storage.local.set({ '__wt_alert_state': { pending: true, tabId: alertTabId, ts: Date.now() } });
332
+ }
333
+ sendResponse({ ok: true });
334
+ }
335
+ if (message.type === 'TAB_ALERT_CLEARED') {
336
+ const alertTabId = sender.tab && sender.tab.id;
337
+ if (alertTabId) {
338
+ browserAPI.tabs.sendMessage(alertTabId, { type: 'Weavetab_GROUP_STATE', state: 'idle', isActive: false }).catch(() => {});
339
+ browserAPI.storage.local.set({ '__wt_alert_state': { pending: false, tabId: alertTabId, ts: Date.now() } });
340
+ }
341
+ sendResponse({ ok: true });
342
+ }
343
+ if (message.type === 'RELOAD_EXTENSION') {
344
+ sendResponse({ ok: true });
345
+ browserAPI.runtime.reload();
346
+ }
347
+
348
+
349
+ });
350
+
351
+ browserAPI.tabs.onRemoved.addListener((tabId) => {
352
+ if (controlledTabs.has(tabId)) {
353
+ controlledTabs.delete(tabId);
354
+ updateBadge();
355
+ }
356
+ anchorTabs.delete(tabId);
357
+ });
358
+
359
+
360
+ // Startup cleanup: Ungroup all Weavetab tabs and clear badges
361
+ browserAPI.runtime.onInstalled.addListener(() => {
362
+ browserAPI.tabs.query({}, (tabs) => {
363
+ const ids = tabs.map(t => t.id).filter(id => id !== undefined);
364
+ if (ids.length > 0) {
365
+ browserAPI.tabs.ungroup(ids).catch(() => {});
366
+ }
367
+ });
368
+ });
369
+
370
+ function updateBadge() {
371
+ if (!browserAPI.action) return;
372
+ const ids = Array.from(controlledTabs);
373
+ if (ids.length === 0) {
374
+ browserAPI.action.setBadgeText({ text: '' }).catch(() => {});
375
+ } else {
376
+ ids.forEach(tid => {
377
+ // Just set the dot, but don't group
378
+ browserAPI.action.setBadgeText({ tabId: tid, text: '\u25CF' }).catch(() => {});
379
+ browserAPI.action.setBadgeBackgroundColor({ tabId: tid, color: '#3B82F6' }).catch(() => {});
380
+ });
381
+ }
382
+ }
383
+
384
+ // On startup: close stale blank tabs — but NEVER the last remaining tab.
385
+ // Weavetab launches Chrome on about:blank, so removing the only tab closes
386
+ // the browser window (graceful shutdown ~1s after launch → ECONNREFUSED loop).
387
+ browserAPI.runtime.onStartup.addListener(async () => {
388
+ try {
389
+ const blanks = await browserAPI.tabs.query({ url: 'about:blank' });
390
+ if (blanks.length === 0) return;
391
+ const all = await browserAPI.tabs.query({});
392
+ // Keep one tab alive if every tab would otherwise be closed.
393
+ const mustKeep = blanks.length === all.length ? 1 : 0;
394
+ blanks.slice(0, blanks.length - mustKeep).forEach(t =>
395
+ browserAPI.tabs.remove(t.id).catch(() => {})
396
+ );
397
+ } catch (e) {
398
+ // Non-fatal: never let tab cleanup crash the service worker or close the browser.
399
+ }
400
+ });
401
+
402
+ // Network blocking for speed (delayed to avoid 'No SW' on cold start)
403
+ // Guard: only run if the service worker is still alive (not being terminated)
404
+ setTimeout(function() {
405
+ // Check the runtime is still valid before calling declarativeNetRequest
406
+ try {
407
+ if (!browserAPI.runtime || !browserAPI.runtime.id) return;
408
+ browserAPI.declarativeNetRequest.updateDynamicRules({
409
+ removeRuleIds: [1, 2, 3, 4],
410
+ addRules: [
411
+ { id: 1, priority: 1, action: { type: 'block' }, condition: { urlFilter: '*google-analytics.com*', resourceTypes: ['script', 'xmlhttprequest', 'ping'] } },
412
+ { id: 2, priority: 1, action: { type: 'block' }, condition: { urlFilter: '*doubleclick.net*', resourceTypes: ['script', 'xmlhttprequest', 'ping', 'image', 'sub_frame'] } },
413
+ { id: 3, priority: 1, action: { type: 'block' }, condition: { urlFilter: '*facebook.net/en_US/fbevents.js*', resourceTypes: ['script'] } }
414
+ // NOTE: GTM blocking removed — some sites bundle critical resources through GTM
415
+ ]
416
+ }).catch(function(e) {
417
+ // Non-fatal — log for debugging only
418
+ console.warn('[Weavetab] Dynamic rules update failed (non-fatal):', e && e.message);
419
+ });
420
+ } catch(e) {
421
+ // Service worker terminated or runtime invalidated — safe to ignore
422
+ }
423
+ }, 1000);
424
+
425
+ // ─── Omnibox: Weavetab:// protocol via browserAPI.omnibox ─────────────────────
426
+
427
+ const DASHBOARD_URL = browserAPI.runtime.getURL('dashboard.html');
428
+
429
+ browserAPI.omnibox.onInputStarted.addListener(() => {
430
+ browserAPI.omnibox.setDefaultSuggestion({
431
+ description: 'Open Weavetab Dashboard <url>chrome-extension://...</url>'
432
+ });
433
+ });
434
+
435
+ browserAPI.omnibox.onInputChanged.addListener((text, suggest) => {
436
+ const suggestions = [];
437
+ const lower = text.toLowerCase();
438
+ if (!lower || lower === 'dashboard' || 'dashboard'.startsWith(lower)) {
439
+ suggestions.push({
440
+ content: 'dashboard',
441
+ description: 'Open Weavetab Dashboard'
442
+ });
443
+ }
444
+ if ('help'.startsWith(lower)) {
445
+ suggestions.push({
446
+ content: 'help',
447
+ description: 'Weavetab Help & Documentation'
448
+ });
449
+ }
450
+ suggest(suggestions);
451
+ });
452
+
453
+ browserAPI.omnibox.onInputEntered.addListener((text, disposition) => {
454
+ const branch = text && text !== 'dashboard' ? encodeURIComponent(text) : 'default';
455
+ const url = DASHBOARD_URL + `?branch=${branch}`;
456
+ switch (disposition) {
457
+ case 'currentTab':
458
+ browserAPI.tabs.update({ url });
459
+ break;
460
+ case 'newForegroundTab':
461
+ browserAPI.tabs.create({ url });
462
+ break;
463
+ case 'newBackgroundTab':
464
+ browserAPI.tabs.create({ url, active: false });
465
+ break;
466
+ }
467
+ });