@weavetab/mcp 2.5.0-beta.0 → 2.5.0-beta.1

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.
@@ -13,7 +13,7 @@ import { fileURLToPath } from "node:url";
13
13
  import CDP from "chrome-remote-interface";
14
14
  import { readLock, writeLock, clearLock, isLockValid, isLockPending } from "./lock.js";
15
15
  import { getSessionProfile } from "./profiles.js";
16
- import { getOutputDir, getDownloadDir, getConfigHeadless, getConfigProxy, getConfigDisableExtensions, getConfigDevMode, getConfigBlock, getConfigAllow, getConfigBrowserType, getConfigBrowserPath, getConfigBrowserFlags, getConfigCookies, getConfigProfileDir } from "../config/loader.js";
16
+ import { getOutputDir, getDownloadDir, getConfigHeadless, getConfigProxy, getConfigDisableExtensions, getConfigDevMode, getConfigBlock, getConfigAllow, getConfigBrowserType, getConfigBrowserPath, getConfigBrowserFlags, getConfigCookies, getConfigProfileDir, getSystemDir } from "../config/loader.js";
17
17
  import { createBrowserFingerprint } from "./security.js";
18
18
  function withCDPTimeout(promise, ms, label) {
19
19
  return Promise.race([
@@ -524,10 +524,24 @@ export async function launchWithBridge(config) {
524
524
  await new Promise(r => setTimeout(r, 1500));
525
525
  }
526
526
  try {
527
- const extPath = path.resolve(__dirname, "../../ext-dist");
528
- // Verify extension directory exists
529
- if (!fs.existsSync(extPath)) {
530
- throw new Error(`[Weavetab] Extension directory does not exist: ${extPath}. Build the extension first.`);
527
+ let extPath = path.resolve(__dirname, "../../ext-dist");
528
+ const systemExtPath = getSystemDir("extension");
529
+ if (fs.existsSync(extPath)) {
530
+ try {
531
+ fs.cpSync(extPath, systemExtPath, { recursive: true, force: true });
532
+ }
533
+ catch (e) {
534
+ logDebug(`[Weavetab] Failed to sync extension to system directory: ${e}`);
535
+ }
536
+ }
537
+ else {
538
+ if (fs.existsSync(systemExtPath)) {
539
+ logDebug(`[Weavetab] Local extension missing. Falling back to system extension directory.`);
540
+ extPath = systemExtPath;
541
+ }
542
+ else {
543
+ throw new Error(`[Weavetab] Extension directory does not exist: ${extPath}. Build the extension first.`);
544
+ }
531
545
  }
532
546
  const manifestPath = path.join(extPath, "manifest.json");
533
547
  const contentPath = path.join(extPath, "content.js");
@@ -150,9 +150,6 @@ async function initSession(session, targetId, port, config) {
150
150
  if (clientRef)
151
151
  clientRef.overlayInstalled = false;
152
152
  await ensureVisualLayer(session, cfg, clientRef ?? undefined);
153
- // Re-inject active plan into the new page
154
- const { restoreActivePlanIntoPage } = await import("../tools/plan.js");
155
- await restoreActivePlanIntoPage(session);
156
153
  }
157
154
  catch { /* ignore */ }
158
155
  }
@@ -55,7 +55,6 @@ export async function extensionHealth(session) {
55
55
  if (document.querySelector('weavetab-host') || document.getElementById('__Weavetab_cursor_host')) features.push('cursor');
56
56
  if (document.querySelector('[data-wt-internal]') || document.getElementById('wt-ui-styles')) features.push('styles');
57
57
  if (document.querySelector('.wt-bubble')) features.push('bubble');
58
- if (document.getElementById('wt-plan-panel')) features.push('plan');
59
58
  return JSON.stringify({
60
59
  loaded: ${loaded},
61
60
  features
@@ -164,7 +164,6 @@ export async function hideWeaveTabOverlay(session) {
164
164
  // - [data-wt-ignore] : any element flagged as agent-invisible
165
165
  // - .wt-bubble : thought bubble / action text popups
166
166
  // - #__wt_ask_bridge : question input bridge element
167
- // - #wt-plan-dashboard : mission plan dashboard
168
167
  // - #wt-debug-overlay : debug overlay panel
169
168
  // - #wt-main-container : WeaveTab OS panel (todos + chat)
170
169
  style.textContent = [
@@ -175,7 +174,6 @@ export async function hideWeaveTabOverlay(session) {
175
174
  '[data-wt-ignore]',
176
175
  '.wt-bubble',
177
176
  '#__wt_ask_bridge',
178
- '#wt-plan-dashboard',
179
177
  '#wt-debug-overlay',
180
178
  '#wt-main-container',
181
179
  ].join(', ') + ' { display: none !important; visibility: hidden !important; opacity: 0 !important; }';
@@ -9,7 +9,6 @@ export interface MissionState {
9
9
  agentId: string;
10
10
  status: "active" | "suspended" | "completed";
11
11
  timestamp: number;
12
- plan: any;
13
12
  tabs: any[];
14
13
  }
15
14
  export declare function suspendMission(agentId: string, missionId: string): Promise<void>;
@@ -4,7 +4,6 @@
4
4
  * Handles suspension, hibernation, and resumption of Weavetab missions.
5
5
  * Weavetab by fy2ne
6
6
  */
7
- import { getActivePlan } from "../tools/plan.js";
8
7
  import { getTabList } from "../cdp/connector.js";
9
8
  import * as fs from "node:fs";
10
9
  import * as path from "node:path";
@@ -17,14 +16,12 @@ export async function suspendMission(agentId, missionId) {
17
16
  if (!fs.existsSync(stateDir)) {
18
17
  fs.mkdirSync(stateDir, { recursive: true });
19
18
  }
20
- const plan = getActivePlan();
21
19
  const tabs = await getTabList(agentId).catch(() => []);
22
20
  const state = {
23
21
  missionId,
24
22
  agentId,
25
23
  status: "suspended",
26
24
  timestamp: Date.now(),
27
- plan,
28
25
  tabs
29
26
  };
30
27
  fs.writeFileSync(getMissionPath(missionId), JSON.stringify(state, null, 2));
@@ -5,6 +5,10 @@ export interface CursorOptions {
5
5
  opacity?: number;
6
6
  speed?: "human" | "fast" | "instant";
7
7
  }
8
+ export declare function ensureInitialCursorPosition(session: CDP.Client): Promise<{
9
+ x: number;
10
+ y: number;
11
+ }>;
8
12
  export interface BurstProfile {
9
13
  groupSize: [number, number];
10
14
  interCharMin: number;
@@ -22,42 +22,87 @@ function wtCursor(session, x, y, state, opts) {
22
22
  const trail = opts?.trail && opts.trail !== "off" ? getCursorTrail(opts.trail === "full" ? 20 : 8) : undefined;
23
23
  sendToExtension(session, { type: 'Weavetab_STATE', cursor: { x, y, trail }, state }).catch(() => { });
24
24
  }
25
+ let _wanderActive = false;
26
+ export async function ensureInitialCursorPosition(session) {
27
+ if (currentX >= 0 && currentY >= 0) {
28
+ return { x: currentX, y: currentY };
29
+ }
30
+ try {
31
+ const layout = await session.Page.getLayoutMetrics().catch(() => null);
32
+ if (layout?.visualViewport?.clientWidth && layout?.visualViewport?.clientHeight) {
33
+ currentX = Math.round(layout.visualViewport.clientWidth / 2);
34
+ currentY = Math.round(layout.visualViewport.clientHeight / 2);
35
+ }
36
+ else {
37
+ const evalRes = await session.Runtime.evaluate({
38
+ expression: `({ x: Math.round(window.innerWidth / 2), y: Math.round(window.innerHeight / 2) })`,
39
+ returnByValue: true
40
+ }).catch(() => null);
41
+ if (evalRes?.result?.value?.x && evalRes?.result?.value?.y) {
42
+ currentX = evalRes.result.value.x;
43
+ currentY = evalRes.result.value.y;
44
+ }
45
+ else {
46
+ currentX = 640;
47
+ currentY = 400;
48
+ }
49
+ }
50
+ }
51
+ catch {
52
+ currentX = 640;
53
+ currentY = 400;
54
+ }
55
+ wtCursor(session, currentX, currentY, "idle");
56
+ return { x: currentX, y: currentY };
57
+ }
25
58
  async function wtThought(session, text) {
26
59
  await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: text } }).catch(() => { });
27
- // Add realistic wandering movement connected to thoughts
28
- if (currentX > 0 && currentY > 0 && Math.random() > 0.2) {
29
- const wanderDistance = 15 + Math.random() * 35;
60
+ if (currentX < 0 || currentY < 0) {
61
+ await ensureInitialCursorPosition(session);
62
+ }
63
+ // Add realistic wandering movement connected to thoughts (non-racing)
64
+ if (currentX > 0 && currentY > 0 && !_wanderActive && Math.random() > 0.3) {
65
+ _wanderActive = true;
66
+ const startX = currentX;
67
+ const startY = currentY;
68
+ const wanderDistance = 10 + Math.random() * 25;
30
69
  const angle = Math.random() * Math.PI * 2;
31
- const tx = Math.max(0, currentX + Math.cos(angle) * wanderDistance);
32
- const ty = Math.max(0, currentY + Math.sin(angle) * wanderDistance);
33
- const steps = 15;
70
+ const tx = Math.max(20, startX + Math.cos(angle) * wanderDistance);
71
+ const ty = Math.max(20, startY + Math.sin(angle) * wanderDistance);
72
+ const steps = 12;
34
73
  const waypoints = [];
35
- const cp1X = currentX + (tx - currentX) * 0.3 + (Math.random() - 0.5) * 15;
36
- const cp1Y = currentY + (ty - currentY) * 0.3 + (Math.random() - 0.5) * 15;
37
- const cp2X = currentX + (tx - currentX) * 0.7 + (Math.random() - 0.5) * 15;
38
- const cp2Y = currentY + (ty - currentY) * 0.7 + (Math.random() - 0.5) * 15;
74
+ const cp1X = startX + (tx - startX) * 0.3;
75
+ const cp1Y = startY + (ty - startY) * 0.3;
76
+ const cp2X = startX + (tx - startX) * 0.7;
77
+ const cp2Y = startY + (ty - startY) * 0.7;
39
78
  for (let i = 1; i <= steps; i++) {
40
79
  let t = i / steps;
41
80
  t = 1 - Math.pow(1 - t, 3);
42
81
  waypoints.push({
43
- x: cubicBezier(t, currentX, cp1X, cp2X, tx),
44
- y: cubicBezier(t, currentY, cp1Y, cp2Y, ty)
82
+ x: Math.round(cubicBezier(t, startX, cp1X, cp2X, tx)),
83
+ y: Math.round(cubicBezier(t, startY, cp1Y, cp2Y, ty))
45
84
  });
46
85
  }
47
86
  sendToExtension(session, {
48
87
  type: 'Weavetab_STATE',
49
- cursor: { waypoints, duration_ms: steps * 20, x: currentX, y: currentY },
88
+ cursor: { waypoints, duration_ms: steps * 25, x: startX, y: startY },
50
89
  state: "thinking"
51
90
  }).catch(() => { });
52
- // Async physical dispatch to not block the main logic flow
53
91
  (async () => {
54
- for (let i = 1; i <= steps; i++) {
55
- const x = waypoints[i - 1].x;
56
- const y = waypoints[i - 1].y;
57
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x, y, button: "none" }).catch(() => { });
58
- currentX = x;
59
- currentY = y;
60
- await new Promise(r => setTimeout(r, 20));
92
+ try {
93
+ for (let i = 1; i <= steps; i++) {
94
+ if (!_wanderActive)
95
+ break;
96
+ const x = waypoints[i - 1].x;
97
+ const y = waypoints[i - 1].y;
98
+ await session.Input.dispatchMouseEvent({ type: "mouseMoved", x, y, button: "none" }).catch(() => { });
99
+ currentX = x;
100
+ currentY = y;
101
+ await new Promise(r => setTimeout(r, 25));
102
+ }
103
+ }
104
+ finally {
105
+ _wanderActive = false;
61
106
  }
62
107
  })();
63
108
  }
@@ -139,6 +184,9 @@ targetSelector, profileName) {
139
184
  let wordsSinceBreak = 0;
140
185
  let wordCharCount = 0;
141
186
  let prevChar = '';
187
+ if (currentX < 0 || currentY < 0) {
188
+ await ensureInitialCursorPosition(session);
189
+ }
142
190
  wtCursor(session, currentX, currentY, "typing");
143
191
  let currentGroupSize = Math.floor(Math.random() * (p.groupSize[1] - p.groupSize[0] + 1)) + p.groupSize[0];
144
192
  let charsTypedInGroup = 0;
@@ -271,6 +319,10 @@ function cubicBezier(t, p0, p1, p2, p3) {
271
319
  }
272
320
  let _firstMoveDone = false;
273
321
  export async function ghostMove(session, targetX, targetY, config, label, profile, cursorOpts) {
322
+ _wanderActive = false; // Stop any ongoing thought wander immediately
323
+ if (currentX < 0 || currentY < 0) {
324
+ await ensureInitialCursorPosition(session);
325
+ }
274
326
  const resolvedProfile = profile || (getConfigHeadless(config) ? "instant" : "human");
275
327
  if (resolvedProfile === "instant") {
276
328
  await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
@@ -290,9 +342,9 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
290
342
  return;
291
343
  }
292
344
  if (resolvedProfile === "fast") {
293
- const steps = 4;
294
- const startX = currentX === -1 ? targetX : currentX;
295
- const startY = currentY === -1 ? targetY : currentY;
345
+ const steps = 6;
346
+ const startX = currentX;
347
+ const startY = currentY;
296
348
  for (let i = 1; i <= steps; i++) {
297
349
  const t = i / steps;
298
350
  const x = Math.round(startX + (targetX - startX) * t);
@@ -301,7 +353,7 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
301
353
  currentX = x;
302
354
  currentY = y;
303
355
  wtCursor(session, x, y, "moving", cursorOpts);
304
- await sleep(5);
356
+ await sleep(6);
305
357
  }
306
358
  currentX = targetX;
307
359
  currentY = targetY;
@@ -311,93 +363,61 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
311
363
  return;
312
364
  }
313
365
  if (!_firstMoveDone) {
314
- logDebug(`[Weavetab Ghost] First mouse move → (${targetX}, ${targetY}) — CDP session active`);
366
+ logDebug(`[Weavetab Ghost] First mouse move from (${currentX}, ${currentY}) → (${targetX}, ${targetY}) — CDP session active`);
315
367
  _firstMoveDone = true;
316
368
  }
317
- // Don't fake-init cursor position — injector.ts hides cursor until first real move
318
- if (currentX === -1 && currentY === -1) {
319
- currentX = targetX;
320
- currentY = targetY;
321
- wtCursor(session, targetX, targetY, "hover", cursorOpts);
322
- recordCursorPosition(targetX, targetY);
323
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
324
- await session.Runtime.evaluate({
325
- expression: `(function(){
326
- var el = document.elementFromPoint(${targetX}, ${targetY});
327
- if (el) {
328
- el.dispatchEvent(new MouseEvent('mouseover', {bubbles:true,cancelable:true,clientX:${targetX},clientY:${targetY}}));
329
- el.dispatchEvent(new MouseEvent('mouseenter', {bubbles:false,cancelable:true,clientX:${targetX},clientY:${targetY}}));
330
- }
331
- })()`
332
- }).catch(() => { });
333
- return;
334
- }
335
369
  const dx = targetX - currentX;
336
370
  const dy = targetY - currentY;
337
371
  const distance = Math.sqrt(dx * dx + dy * dy);
338
- wtCursor(session, currentX, currentY, label && distance >= 150 ? "searching" : "idle", cursorOpts);
339
- if (label && distance >= 150 && getConfigVisuals(config) !== false) {
340
- wtThought(session, `moving to ${label.slice(0, 40)}...`);
341
- await sleep(80 + Math.random() * 120);
342
- }
343
- if (distance < 150) {
372
+ if (distance < 4) {
344
373
  currentX = targetX;
345
374
  currentY = targetY;
346
375
  await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
347
376
  wtCursor(session, targetX, targetY, "hover", cursorOpts);
348
377
  recordCursorPosition(targetX, targetY);
349
- await session.Runtime.evaluate({
350
- expression: `(function(){
351
- var el = document.elementFromPoint(${targetX}, ${targetY});
352
- if (el) {
353
- el.dispatchEvent(new MouseEvent('mouseover', {bubbles:true,cancelable:true,clientX:${targetX},clientY:${targetY}}));
354
- el.dispatchEvent(new MouseEvent('mouseenter', {bubbles:false,cancelable:true,clientX:${targetX},clientY:${targetY}}));
355
- }
356
- })()`
357
- }).catch(() => { });
358
378
  return;
359
379
  }
360
- const duration = Math.max(80, Math.min(300, distance * 0.6));
361
- let steps = Math.floor(duration / 16);
362
- if (steps > 10)
363
- steps = 10;
364
- if (steps < 3)
365
- steps = 3;
366
- const cp1X = currentX + dx * 0.2 + randomGaussian(0, distance * 0.2);
367
- const cp1Y = currentY + dy * 0.2 + randomGaussian(0, distance * 0.2);
368
- const cp2X = currentX + dx * 0.8 + randomGaussian(0, distance * 0.2);
369
- const cp2Y = currentY + dy * 0.8 + randomGaussian(0, distance * 0.2);
370
- const doOvershoot = distance >= 300 && Math.random() > 0.3;
371
- const overshootAmount = doOvershoot ? randomGaussian(10, 5) : 0;
372
- const angle = Math.atan2(dy, dx);
373
- const overshootX = targetX + Math.cos(angle) * overshootAmount;
374
- const overshootY = targetY + Math.sin(angle) * overshootAmount;
375
- wtCursor(session, targetX, targetY, "idle", cursorOpts);
380
+ wtCursor(session, currentX, currentY, label && distance >= 150 ? "searching" : "idle", cursorOpts);
381
+ if (label && distance >= 150 && getConfigVisuals(config) !== false) {
382
+ wtThought(session, `moving to ${label.slice(0, 40)}...`);
383
+ await sleep(60 + Math.random() * 80);
384
+ }
385
+ // Smooth, natural human hand curve
386
+ const duration = Math.max(120, Math.min(380, 80 + Math.sqrt(distance) * 12));
387
+ const steps = Math.max(8, Math.min(24, Math.floor(duration / 16)));
388
+ const stepDelay = Math.max(10, Math.floor(duration / steps));
389
+ // Gentle perpendicular curvature (arcing motion)
390
+ const nx = -dy / distance;
391
+ const ny = dx / distance;
392
+ const maxArc = Math.min(45, distance * 0.15);
393
+ const arcSign = (Math.random() > 0.5 ? 1 : -1);
394
+ const arcAmount = maxArc * arcSign * (0.4 + Math.random() * 0.6);
395
+ const cp1X = currentX + dx * 0.3 + nx * arcAmount;
396
+ const cp1Y = currentY + dy * 0.3 + ny * arcAmount;
397
+ const cp2X = currentX + dx * 0.7 + nx * (arcAmount * 0.6);
398
+ const cp2Y = currentY + dy * 0.7 + ny * (arcAmount * 0.6);
376
399
  const waypoints = [];
377
400
  for (let i = 1; i <= steps; i++) {
378
- let t = i / steps;
379
- t = 1 - Math.pow(1 - t, 4);
380
- const actualTargetX = doOvershoot && i < steps * 0.8 ? overshootX : targetX;
381
- const actualTargetY = doOvershoot && i < steps * 0.8 ? overshootY : targetY;
401
+ const rawT = i / steps;
402
+ // Cubic ease-out for natural deceleration
403
+ const t = 1 - Math.pow(1 - rawT, 3);
382
404
  waypoints.push({
383
- x: cubicBezier(t, currentX, cp1X, cp2X, actualTargetX),
384
- y: cubicBezier(t, currentY, cp1Y, cp2Y, actualTargetY)
405
+ x: Math.round(cubicBezier(t, currentX, cp1X, cp2X, targetX)),
406
+ y: Math.round(cubicBezier(t, currentY, cp1Y, cp2Y, targetY))
385
407
  });
386
408
  }
387
- // Pre-announce the move
409
+ // Pre-announce waypoints for smooth client-side interpolation
388
410
  sendToExtension(session, {
389
411
  type: 'Weavetab_STATE',
390
- cursor: { waypoints, duration_ms: steps * 8, x: currentX, y: currentY },
412
+ cursor: { waypoints, duration_ms: steps * stepDelay, x: currentX, y: currentY },
391
413
  state: "moving"
392
414
  }).catch(() => { });
393
415
  for (let i = 1; i <= steps; i++) {
394
- const x = waypoints[i - 1].x;
395
- const y = waypoints[i - 1].y;
396
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x, y, button: "none" });
397
- currentX = x;
398
- currentY = y;
399
- wtCursor(session, x, y, "moving", cursorOpts);
400
- await sleep(8);
416
+ const pt = waypoints[i - 1];
417
+ await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: pt.x, y: pt.y, button: "none" }).catch(() => { });
418
+ currentX = pt.x;
419
+ currentY = pt.y;
420
+ await sleep(stepDelay);
401
421
  }
402
422
  currentX = targetX;
403
423
  currentY = targetY;
@@ -405,7 +425,7 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
405
425
  recordCursorPosition(targetX, targetY);
406
426
  if (label)
407
427
  wtClearThought(session);
408
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
428
+ await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" }).catch(() => { });
409
429
  await session.Runtime.evaluate({
410
430
  expression: `(function(){
411
431
  var el = document.elementFromPoint(${targetX}, ${targetY});
@@ -501,8 +521,12 @@ export async function ghostClick(session, x, y, options) {
501
521
  }).catch(() => { });
502
522
  }
503
523
  export function ghostWait(session) {
504
- wtCursor(session, currentX, currentY, "waiting");
524
+ const x = currentX >= 0 ? currentX : 640;
525
+ const y = currentY >= 0 ? currentY : 400;
526
+ wtCursor(session, x, y, "waiting");
505
527
  }
506
528
  export function ghostIdle(session) {
507
- wtCursor(session, currentX, currentY, "idle");
529
+ const x = currentX >= 0 ? currentX : 640;
530
+ const y = currentY >= 0 ? currentY : 400;
531
+ wtCursor(session, x, y, "idle");
508
532
  }
@@ -2,7 +2,7 @@
2
2
  * src/overlay/injector.ts
3
3
  * Weavetab — Browser Presence Layer v3 (stripped)
4
4
  *
5
- * Kept: __wt_plan (mission dashboard), SPA nav detection,
5
+ * Kept: SPA nav detection,
6
6
  * __wt_setConfig, __wt_status, favicon state.
7
7
  * Removed: cursor (moved to content.js), chat panel, thought bubble,
8
8
  * ask modal, screen glow, CSS keyframes, emoji system.
@@ -52,23 +52,11 @@ export function getMinimalOverlayScript() {
52
52
  document.addEventListener('NavigationEnd', function() { em('angular', location.href); });
53
53
  }
54
54
 
55
- // ── Mission plan dashboard ──────────────────────────────────────────────
56
- window.__wt_plan_comments = window.__wt_plan_comments || [];
57
- window.__wt_plan = function(action, goal, tasks) {
58
- window.postMessage({ type: '__WT_PLAN', action: action, goal: goal, tasks: tasks }, '*');
59
- };
60
-
61
55
  // The message listeners below are guarded so they are registered exactly once
62
56
  // per page, even when __wt_install_minimal() is called repeatedly (e.g. SPAs
63
57
  // with frequent navigations).
64
58
  if (!window.__wt_msg_installed) {
65
59
  window.__wt_msg_installed = true;
66
- window.addEventListener("message", function(e) {
67
- if (e.data && e.data.type === "__WT_PLAN_COMMENT") {
68
- window.__wt_plan_comments.push({ taskId: Number(e.data.taskId), comment: e.data.comment, time: Date.now() });
69
- }
70
- });
71
-
72
60
  window.__wt_chat_queue = window.__wt_chat_queue || [];
73
61
  window.addEventListener("message", function(e) {
74
62
  if (e.data && e.data.type === "__WT_FEEDBACK") {
package/dist/server.js CHANGED
@@ -11,7 +11,6 @@ import { checkRateLimit } from "./security/ratelimit.js";
11
11
  import { checkDomain, isToolAllowedInMode } from "./security/blacklist.js";
12
12
  import { logAction, logActionStructured } from "./audit/logger.js";
13
13
  import { recordToolUsage, broadcastTelemetry } from "./audit/telemetry.js";
14
- import { clearLock } from "./cdp/lock.js";
15
14
  import { browserMap } from "./tools/read.js";
16
15
  import { weaveNavigate } from "./tools/navigate.js";
17
16
  import { weaveClick } from "./tools/click.js";
@@ -25,7 +24,6 @@ import { browserWait } from "./tools/wait.js";
25
24
  import { browserPointer } from "./tools/pointer.js";
26
25
  import { browserSelect } from "./tools/select.js";
27
26
  import { browserFill } from "./tools/fill.js";
28
- import { weavePlan } from "./tools/plan.js";
29
27
  import { browserHighlight } from "./tools/highlight.js";
30
28
  import { browserUpload } from "./tools/upload.js";
31
29
  import { browserFind } from "./tools/find.js";
@@ -794,28 +792,6 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
794
792
  return wrapError(e);
795
793
  }
796
794
  });
797
- // ── 15. browser_plan ───────────────────────────────────────────────────────
798
- server.tool("browser_plan", "Create a live task panel in the browser.", {
799
- action: z.enum(["create", "update", "comments", "hide"]).describe("Action to perform."),
800
- goal: z.string().optional().describe("Mission goal description (required for create)."),
801
- tasks: z.array(z.object({
802
- id: z.number(),
803
- description: z.string(),
804
- status: z.enum(["pending", "in-progress", "done", "failed"]),
805
- })).optional().describe("Task list with statuses (required for create)."),
806
- taskId: z.number().optional().describe("Task ID to update (required for update)."),
807
- status: z.enum(["pending", "in-progress", "done", "failed"]).optional().describe("New status (required for update)."),
808
- }, async (args) => {
809
- try {
810
- return await withSecurity(config, "browser_plan", async (session, liveConfig) => {
811
- const result = await weavePlan(session, args, liveConfig);
812
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
813
- });
814
- }
815
- catch (e) {
816
- return wrapError(e);
817
- }
818
- });
819
795
  // ── 18. browser_highlight ───────────────────────────────────────────────────
820
796
  server.tool("browser_highlight", "Highlight an element with a colored overlay.", {
821
797
  selector: z.string().optional().describe("CSS Selector of the element to highlight."),
@@ -1678,10 +1654,8 @@ process.on("SIGTERM", async () => {
1678
1654
  process.exit(0);
1679
1655
  });
1680
1656
  process.on("exit", () => {
1681
- if (global.__wt_is_server)
1682
- return;
1683
- try {
1684
- clearLock();
1685
- }
1686
- catch { /* ignore */ }
1657
+ // We purposefully do NOT clear the lock file here.
1658
+ // Weavetab launches the browser detached, so it survives the server dying.
1659
+ // Leaving the lock file allows the next MCP server run to find and reuse
1660
+ // the existing browser instead of launching a duplicate.
1687
1661
  });
@@ -9,7 +9,6 @@ const ENGINE_COVERAGE = {
9
9
  "browser_network_intercept",
10
10
  "browser_viewport",
11
11
  "browser_recording",
12
- "browser_plan",
13
12
  "browser_thought",
14
13
  "browser_highlight",
15
14
  "browser_inspect",
@@ -24,7 +23,6 @@ const ENGINE_COVERAGE = {
24
23
  "browser_network_intercept",
25
24
  "browser_viewport",
26
25
  "browser_recording",
27
- "browser_plan",
28
26
  "browser_thought",
29
27
  "browser_highlight",
30
28
  "browser_inspect",
@@ -484,5 +484,5 @@ export async function browserMap(session, options = {}, targetId = "default", co
484
484
  sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: '' } }).catch(() => { });
485
485
  return output;
486
486
  }
487
- // Keep backward-compat alias used by plan.ts
487
+ // Backward-compat alias
488
488
  export { browserMap as weaveRead };
@@ -164,7 +164,9 @@ function __wt_safeAppendToBody(el) {
164
164
  }
165
165
  if (typeof __weaveCursor !== 'undefined' && __weaveCursor) {
166
166
  __weaveCursor.setVisible(__wt_masterVisualsEnabled);
167
- if (__weaveCursor._canvas) {
167
+ if (__weaveCursor._cursor) {
168
+ __weaveCursor._cursor.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
169
+ } else if (__weaveCursor._canvas) {
168
170
  __weaveCursor._canvas.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
169
171
  }
170
172
  }
@@ -213,8 +215,13 @@ function __wt_safeAppendToBody(el) {
213
215
  if (msg.type === 'WT_TOGGLE_MASTER_VISUALS') {
214
216
  __wt_applyMasterVisuals(msg.enabled);
215
217
  }
216
- if (msg.type === 'GHOST_CURSOR_MOVE' && iframe && iframe.contentWindow) {
217
- iframe.contentWindow.postMessage({ type: 'RT_CURSOR_MOVE', x: msg.x, y: msg.y, phase: msg.phase }, '*');
218
+ if (msg.type === 'GHOST_CURSOR_MOVE') {
219
+ if (typeof msg.x === 'number' && typeof msg.y === 'number') {
220
+ moveCursor(msg.x, msg.y, msg.phase);
221
+ }
222
+ if (iframe && iframe.contentWindow) {
223
+ iframe.contentWindow.postMessage({ type: 'RT_CURSOR_MOVE', x: msg.x, y: msg.y, phase: msg.phase }, '*');
224
+ }
218
225
  }
219
226
  if (msg.type === 'TOOL_CALL_EVENT' && iframe && iframe.contentWindow) {
220
227
  iframe.contentWindow.postMessage({ type: 'TOOL_CALL_EVENT', payload: msg.payload }, '*');
@@ -932,71 +939,88 @@ window.__Weavetab_readConsole = function({ since = 'last', level = null } = {})
932
939
  return level ? entries.filter(e => e.level === level) : entries;
933
940
  };
934
941
 
935
- // ── PREMIUM WEAVE CURSOR (Phase 5.1 / 5.3) ─────────────────────
936
- // Shadow-DOM-isolated, canvas-based, with cognitive-load speed scaling
937
- // and state-driven visual transitions: idle, moving, reading, typing, clicking, thinking
942
+ // ── BLUE SVG WEAVE CURSOR ───────────────────────────────────────
943
+ // Shadow-DOM-isolated, hardware-accelerated SVG cursor with smooth interpolation
938
944
  class WeaveCursor {
939
945
  constructor() {
940
946
  // Attach a shadow host that sits above all page content
941
947
  this._host = document.createElement('div');
942
948
  this._host.id = '__Weavetab_cursor_host';
949
+ this._host.setAttribute('data-wt-ignore', 'true');
943
950
  Object.assign(this._host.style, {
944
951
  position: 'fixed',
945
952
  top: '0',
946
953
  left: '0',
947
954
  width: '0',
948
955
  height: '0',
949
- zIndex: '2147483640',
956
+ zIndex: '2147483647',
950
957
  pointerEvents: 'none',
951
958
  overflow: 'visible',
952
959
  });
953
960
 
954
961
  const shadow = this._host.attachShadow({ mode: 'closed' });
955
962
 
956
- // Canvas layer
957
- this._canvas = document.createElement('canvas');
958
- this._canvas.width = 64;
959
- this._canvas.height = 64;
960
- Object.assign(this._canvas.style, {
963
+ // Hotspot aligned with the top-left pointer tip (3.0, 5.0 in 32x32 viewBox)
964
+ this._hotX = 3.0;
965
+ this._hotY = 5.0;
966
+ this._scale = 0.4;
967
+
968
+ const initX = typeof window !== 'undefined' && window.innerWidth ? Math.round(window.innerWidth / 2) : 640;
969
+ const initY = typeof window !== 'undefined' && window.innerHeight ? Math.round(window.innerHeight / 2) : 400;
970
+
971
+ // Exact vector SVG cursor matching user design
972
+ this._cursor = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
973
+ this._cursor.setAttribute('class', 'cursor');
974
+ this._cursor.setAttribute('viewBox', '0 0 32 32');
975
+ this._cursor.setAttribute('width', '32');
976
+ this._cursor.setAttribute('height', '32');
977
+ this._cursor.setAttribute('aria-hidden', 'true');
978
+ Object.assign(this._cursor.style, {
961
979
  position: 'fixed',
962
980
  top: '0',
963
981
  left: '0',
964
- width: '64px',
965
- height: '64px',
982
+ width: '32px',
983
+ height: '32px',
984
+ display: 'block',
985
+ transformOrigin: `${this._hotX}px ${this._hotY}px`,
986
+ transform: `translate3d(${initX - this._hotX}px, ${initY - this._hotY}px, 0) scale(${this._scale})`,
987
+ overflow: 'visible',
966
988
  pointerEvents: 'none',
967
- transform: 'translate(-9999px,-9999px)',
968
989
  willChange: 'transform',
969
- transition: 'opacity 0.15s ease-out',
990
+ transition: 'opacity 0.2s ease-out',
991
+ opacity: '1',
992
+ zIndex: '2147483647',
993
+ filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.35))',
970
994
  });
971
- shadow.appendChild(this._canvas);
972
-
973
- this._ctx = this._canvas.getContext('2d');
974
- this._x = -9999;
975
- this._y = -9999;
976
- this._targetX = -9999;
977
- this._targetY = -9999;
978
- this._lerpX = -9999;
979
- this._lerpY = -9999;
995
+
996
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
997
+ path.setAttribute('class', 'cursor-shape');
998
+ path.setAttribute('d', 'M 6.1,4.7 C 4.5,4.1 3.0,5.0 3.0,6.7 C 3.0,7.1 3.1,7.5 3.3,8.0 L 10.2,27.0 C 10.8,28.7 12.0,29.6 13.6,29.5 C 15.1,29.4 16.0,28.5 16.5,27.0 L 19.4,18.8 L 25.9,17.0 C 27.7,16.5 28.9,15.2 28.9,13.6 C 28.9,12.1 28.0,10.9 26.4,10.3 Z');
999
+ path.setAttribute('fill', '#1268ff');
1000
+ path.setAttribute('stroke', '#ffffff');
1001
+ path.setAttribute('stroke-width', '3.1');
1002
+ path.setAttribute('stroke-linejoin', 'round');
1003
+ path.setAttribute('stroke-linecap', 'round');
1004
+
1005
+ this._cursor.appendChild(path);
1006
+ shadow.appendChild(this._cursor);
1007
+
1008
+ // Alias _canvas to _cursor for backward compatibility
1009
+ this._canvas = this._cursor;
1010
+
1011
+ this._x = initX;
1012
+ this._y = initY;
1013
+ this._targetX = initX;
1014
+ this._targetY = initY;
1015
+ this._lerpX = initX;
1016
+ this._lerpY = initY;
980
1017
  this._state = 'idle';
981
- this._visible = false;
1018
+ this._visible = true;
982
1019
  this._animFrame = null;
983
- this._lastMoveTime = 0;
984
1020
  this._lastTime = performance.now();
985
1021
  this._waypointQueue = [];
986
1022
  this._playingQueue = false;
987
1023
 
988
- this._hotX = 0;
989
- this._hotY = 0;
990
-
991
- // Sharp precision arrow pointer path (tip at 0,0)
992
- this._outerPath = new Path2D("M 0 0 L 0 16 L 4.2 12.2 L 7.5 19 L 10.2 17.6 L 6.8 10.8 L 11.8 10.8 Z");
993
- this._innerPath = new Path2D("M 1.2 2.5 L 1.2 13.5 L 3.8 10.8 L 6.6 16.5 L 8.2 15.6 L 5.4 9.8 L 9.8 9.8 Z");
994
-
995
- // Vivid linear gradient (#06B6D4 -> #3B82F6)
996
- this._blueGrad = this._ctx.createLinearGradient(0, 0, 12, 19);
997
- this._blueGrad.addColorStop(0, '#06B6D4');
998
- this._blueGrad.addColorStop(1, '#3B82F6');
999
-
1000
1024
  __wt_safeAppendToBody(this._host);
1001
1025
  this._startLoop();
1002
1026
  }
@@ -1018,23 +1042,25 @@ class WeaveCursor {
1018
1042
  setVisible(visible) {
1019
1043
  if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) {
1020
1044
  this._visible = false;
1021
- this._canvas.style.opacity = '0';
1045
+ this._cursor.style.opacity = '0';
1022
1046
  return;
1023
1047
  }
1024
1048
  this._visible = visible;
1025
- this._canvas.style.opacity = visible ? '1' : '0';
1049
+ this._cursor.style.opacity = visible ? '1' : '0';
1026
1050
  }
1027
1051
 
1028
1052
  moveTo(x, y) {
1029
1053
  if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
1030
- if (this._playingQueue) return;
1054
+ if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) return;
1031
1055
  this._targetX = x;
1032
1056
  this._targetY = y;
1033
- if (this._lerpX === -9999) { this._lerpX = x; this._lerpY = y; }
1034
- this._lastMoveTime = Date.now();
1057
+ if (this._lerpX === -9999) {
1058
+ this._lerpX = x;
1059
+ this._lerpY = y;
1060
+ }
1035
1061
  if (!this._visible) {
1036
1062
  this._visible = true;
1037
- this._canvas.style.opacity = '1';
1063
+ this._cursor.style.opacity = '1';
1038
1064
  }
1039
1065
  if (!this._animFrame) {
1040
1066
  this._startLoop();
@@ -1043,20 +1069,23 @@ class WeaveCursor {
1043
1069
 
1044
1070
  setWaypoints(waypoints, durationMs) {
1045
1071
  if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
1072
+ if (!waypoints || !waypoints.length) return;
1046
1073
  this._waypointQueue = waypoints;
1047
1074
  this._playingQueue = true;
1048
1075
  this._waypointStartTime = performance.now();
1049
- this._waypointDuration = durationMs;
1076
+ this._waypointDuration = durationMs || (waypoints.length * 16);
1050
1077
  if (waypoints.length > 0 && this._lerpX === -9999) {
1051
1078
  this._lerpX = waypoints[0].x;
1052
1079
  this._lerpY = waypoints[0].y;
1053
1080
  }
1054
- if (!this._visible) { this._visible = true; this._canvas.style.opacity = '1'; }
1081
+ if (!this._visible) {
1082
+ this._visible = true;
1083
+ this._cursor.style.opacity = '1';
1084
+ }
1055
1085
  if (!this._animFrame) this._startLoop();
1056
1086
  }
1057
1087
 
1058
1088
  _startLoop() {
1059
- this._idleFrames = 0;
1060
1089
  this._lastTime = performance.now();
1061
1090
  const draw = (currentTime) => {
1062
1091
  const dt = Math.min((currentTime - this._lastTime) / 1000, 0.1);
@@ -1064,89 +1093,86 @@ class WeaveCursor {
1064
1093
 
1065
1094
  if (this._playingQueue && this._waypointQueue.length > 0) {
1066
1095
  const elapsed = currentTime - this._waypointStartTime;
1067
- const progress = Math.min(elapsed / this._waypointDuration, 1);
1068
- const idx = Math.min(Math.floor(progress * this._waypointQueue.length), this._waypointQueue.length - 1);
1069
- const pt = this._waypointQueue[idx];
1070
- if (pt) {
1071
- this._targetX = pt.x;
1072
- this._targetY = pt.y;
1073
- this._lastMoveTime = Date.now();
1096
+ const progress = Math.max(0, Math.min(elapsed / (this._waypointDuration || 1), 1));
1097
+ const totalSegments = this._waypointQueue.length - 1;
1098
+
1099
+ if (totalSegments <= 0) {
1100
+ this._targetX = this._waypointQueue[0].x;
1101
+ this._targetY = this._waypointQueue[0].y;
1102
+ } else {
1103
+ const floatIdx = progress * totalSegments;
1104
+ const i0 = Math.floor(floatIdx);
1105
+ const i1 = Math.min(i0 + 1, totalSegments);
1106
+ const t = floatIdx - i0;
1107
+
1108
+ const p0 = this._waypointQueue[i0];
1109
+ const p1 = this._waypointQueue[i1];
1110
+ if (p0 && p1) {
1111
+ this._targetX = p0.x + (p1.x - p0.x) * t;
1112
+ this._targetY = p0.y + (p1.y - p0.y) * t;
1113
+ }
1074
1114
  }
1115
+
1075
1116
  if (progress >= 1) {
1076
1117
  this._playingQueue = false;
1077
1118
  this._waypointQueue = [];
1078
1119
  }
1079
1120
  }
1080
1121
 
1081
- // Smooth lerp (k = 14)
1082
- const lerpFactor = 1 - Math.exp(-14 * dt);
1122
+ // Smooth exponential lerp (k = 24 for fluid human-smooth tracking)
1123
+ const lerpFactor = 1 - Math.exp(-24 * dt);
1083
1124
  if (this._targetX !== -9999) {
1084
1125
  this._lerpX += (this._targetX - this._lerpX) * lerpFactor;
1085
1126
  this._lerpY += (this._targetY - this._lerpY) * lerpFactor;
1086
- if (Math.abs(this._targetX - this._lerpX) < 0.3) this._lerpX = this._targetX;
1087
- if (Math.abs(this._targetY - this._lerpY) < 0.3) this._lerpY = this._targetY;
1088
- }
1089
-
1090
- const hasRecentMove = (Date.now() - this._lastMoveTime) < 1200;
1091
- if (hasRecentMove && (typeof __wt_masterVisualsEnabled === 'undefined' || __wt_masterVisualsEnabled)) {
1092
- if (!this._visible) {
1093
- this._visible = true;
1094
- this._canvas.style.opacity = '1';
1095
- }
1096
- this._idleFrames = 0;
1097
- } else {
1098
- this._idleFrames++;
1099
- if (this._idleFrames > 90 || !__wt_masterVisualsEnabled) {
1127
+ if (Math.abs(this._targetX - this._lerpX) < 0.1) this._lerpX = this._targetX;
1128
+ if (Math.abs(this._targetY - this._lerpY) < 0.1) this._lerpY = this._targetY;
1129
+
1130
+ // "always show in browser" - stays visible once loaded and active
1131
+ if (typeof __wt_masterVisualsEnabled === 'undefined' || __wt_masterVisualsEnabled) {
1132
+ if (!this._visible) {
1133
+ this._visible = true;
1134
+ this._cursor.style.opacity = '1';
1135
+ }
1136
+ } else {
1100
1137
  if (this._visible) {
1101
1138
  this._visible = false;
1102
- this._canvas.style.opacity = '0';
1139
+ this._cursor.style.opacity = '0';
1103
1140
  }
1104
1141
  }
1142
+
1143
+ this._render();
1105
1144
  }
1106
1145
 
1107
- this._render();
1108
1146
  this._animFrame = requestAnimationFrame(draw);
1109
1147
  };
1148
+ if (this._animFrame) cancelAnimationFrame(this._animFrame);
1110
1149
  this._animFrame = requestAnimationFrame(draw);
1111
1150
  }
1112
1151
 
1113
1152
  _render() {
1114
1153
  if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) {
1115
- this._canvas.style.opacity = '0';
1154
+ this._cursor.style.opacity = '0';
1116
1155
  return;
1117
1156
  }
1118
-
1119
- const { _ctx: ctx, _lerpX: x, _lerpY: y } = this;
1120
-
1121
- this._canvas.style.transform = `translate(${x - this._hotX}px, ${y - this._hotY}px)`;
1122
- ctx.clearRect(0, 0, 64, 64);
1123
-
1124
- ctx.save();
1125
- ctx.translate(2, 2);
1126
-
1127
- // Sharp outer stroke — White
1128
- ctx.fillStyle = '#FFFFFF';
1129
- ctx.strokeStyle = '#FFFFFF';
1130
- ctx.lineWidth = 2;
1131
- ctx.lineJoin = 'miter';
1132
- ctx.miterLimit = 3;
1133
- ctx.fill(this._outerPath);
1134
- ctx.stroke(this._outerPath);
1135
-
1136
- // Sharp inner core — Gradient
1137
- ctx.fillStyle = this._blueGrad;
1138
- ctx.fill(this._innerPath);
1139
-
1140
- ctx.restore();
1157
+ const x = this._lerpX;
1158
+ const y = this._lerpY;
1159
+ if (x === -9999 || y === -9999) return;
1160
+ this._cursor.style.transform = `translate3d(${x - this._hotX}px, ${y - this._hotY}px, 0) scale(${this._scale})`;
1141
1161
  }
1142
1162
  }
1143
1163
 
1144
- // Instantiate the premium cursor
1145
- let __weaveCursor = { setState: function(){}, moveTo: function(){}, destroy: function(){}, setVisible: function(){}, setWaypoints: function(){} };
1164
+ // Instantiate the active cursor
1165
+ let __weaveCursor = new WeaveCursor();
1146
1166
  window.__weaveCursor_instance = __weaveCursor;
1147
1167
 
1148
1168
  function moveCursor(x, y, state) {
1149
- // Cursor visuals disabled
1169
+ if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
1170
+ if (__weaveCursor && typeof __weaveCursor.moveTo === 'function') {
1171
+ if (typeof x === 'number' && typeof y === 'number' && x >= 0 && y >= 0) {
1172
+ __weaveCursor.moveTo(x, y);
1173
+ }
1174
+ if (state) __weaveCursor.setState(state);
1175
+ }
1150
1176
  }
1151
1177
 
1152
1178
 
package/mcp.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weavetab/mcp",
3
3
  "mcpName": "io.github.weavetab/mcp",
4
- "version": "2.5.0-beta.0",
4
+ "version": "2.5.0-beta.1",
5
5
  "description": "Weavetab MCP — Production-grade browser automation MCP server with direct Chrome DevTools Protocol (CDP) control. Direct socket transport, zero WebDriver/Node.js wrapper overhead, deterministic state execution.",
6
6
  "icon": "icon.png",
7
7
  "author": "fy2ne",
@@ -185,15 +185,6 @@
185
185
  "Optional form submission click step"
186
186
  ]
187
187
  },
188
- "browser_plan": {
189
- "category": "mission_execution",
190
- "description": "Create or update a live task panel in the browser. Renders a real-time task dashboard within a Shadow DOM overlay displaying goal progress and step status.",
191
- "features": [
192
- "Real-time task state and progress display",
193
- "Isolated Shadow DOM layout structure",
194
- "Structured task state tracking (pending, in-progress, done, failed)"
195
- ]
196
- },
197
188
  "browser_highlight": {
198
189
  "category": "canvas_and_visual",
199
190
  "description": "Highlight an element with a colored overlay. Renders temporary visual CDP overlay highlights over target elements without DOM tree mutations.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weavetab/mcp",
3
- "version": "2.5.0-beta.0",
3
+ "version": "2.5.0-beta.1",
4
4
  "description": "A production-grade local MCP server for AI browser automation via Chrome DevTools Protocol. No extensions. No cloud. Full control.",
5
5
  "author": "fy2ne",
6
6
  "license": "AGPL-3.0-only",
package/server.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "https://github.com/weavetab/mcp",
8
8
  "source": "github"
9
9
  },
10
- "version": "2.5.0-beta.0",
10
+ "version": "2.5.0-beta.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
@@ -1,39 +0,0 @@
1
- import CDP from "chrome-remote-interface";
2
- import type { Config } from "../config/loader.js";
3
- interface PlanTask {
4
- id: number;
5
- description: string;
6
- status: "pending" | "in-progress" | "done" | "failed";
7
- comment?: string;
8
- }
9
- export interface PlanOptions {
10
- action: "create" | "update" | "comments" | "hide";
11
- goal?: string;
12
- tasks?: PlanTask[];
13
- taskId?: number;
14
- status?: PlanTask["status"];
15
- }
16
- interface PlanComment {
17
- taskId: number;
18
- comment: string;
19
- time: number;
20
- }
21
- export declare function weavePlan(session: CDP.Client, options: PlanOptions, _config: Config): Promise<{
22
- success: boolean;
23
- plan?: {
24
- goal: string;
25
- tasks: PlanTask[];
26
- };
27
- comments?: PlanComment[];
28
- error?: string;
29
- }>;
30
- export declare function addPlanComment(taskId: number, comment: string): void;
31
- /** Returns current in-memory plan (goal + tasks) — used to re-inject on page navigation */
32
- export declare function getActivePlan(): {
33
- goal: string;
34
- tasks: PlanTask[];
35
- } | null;
36
- export declare function setActivePlan(goal: string, tasks: PlanTask[]): void;
37
- /** Re-inject plan into a (possibly fresh) page session — used after navigation / reconnect */
38
- export declare function restoreActivePlanIntoPage(session: CDP.Client): Promise<void>;
39
- export {};
@@ -1,161 +0,0 @@
1
- import { logAction } from "../audit/logger.js";
2
- import { sendToExtension } from "../cdp/helpers.js";
3
- // In-memory comment store (agent-side)
4
- const planComments = [];
5
- let activeGoal = "";
6
- let activeTasks = [];
7
- // Dormant Guard (Layer 1)
8
- let dormantTimer = null;
9
- const DORMANT_TIMEOUT_MS = 45000;
10
- function resetDormantGuard(session) {
11
- if (dormantTimer)
12
- clearTimeout(dormantTimer);
13
- dormantTimer = setTimeout(async () => {
14
- try {
15
- await session.Runtime.evaluate({
16
- expression: `(function() { if (typeof window.__wt_plan === 'function') window.__wt_plan('hide'); })()`
17
- });
18
- }
19
- catch { /* session may be dead */ }
20
- }, DORMANT_TIMEOUT_MS);
21
- }
22
- /** Poll the page for new user comments on plan tasks */
23
- async function pollComments(session) {
24
- try {
25
- const { result } = await session.Runtime.evaluate({
26
- expression: `(function() {
27
- var q = window.__wt_plan_comments || [];
28
- if (q.length > 0) { window.__wt_plan_comments = []; return JSON.stringify(q); }
29
- return '[]';
30
- })()`,
31
- returnByValue: true,
32
- });
33
- if (result?.value && result.value !== '[]') {
34
- return JSON.parse(result.value);
35
- }
36
- }
37
- catch { /* non-fatal */ }
38
- return [];
39
- }
40
- export async function weavePlan(session, options, _config) {
41
- const { action, goal, tasks, taskId, status } = options;
42
- // Always poll for new user comments from the overlay
43
- const newComments = await pollComments(session);
44
- for (const c of newComments) {
45
- planComments.push(c);
46
- const task = activeTasks.find(t => t.id === c.taskId);
47
- if (task) {
48
- task.comment = c.comment;
49
- logAction("browser_plan", "comment", `task ${c.taskId}: ${c.comment}`);
50
- }
51
- }
52
- if (action === "create") {
53
- activeGoal = goal || "";
54
- activeTasks = tasks || [];
55
- // Use sendToExtension for reliable delivery to content script
56
- await sendToExtension(session, {
57
- type: 'Weavetab_STATE',
58
- plan: { action: 'create', goal: activeGoal, tasks: activeTasks },
59
- }).catch(() => { });
60
- // Also try direct injection as fallback
61
- await session.Runtime.evaluate({
62
- expression: `(function() {
63
- if (typeof window.__wt_plan === 'function') {
64
- window.__wt_plan('create', ${JSON.stringify(activeGoal)}, ${JSON.stringify(activeTasks)});
65
- }
66
- })()`,
67
- returnByValue: true,
68
- }).catch(() => { });
69
- resetDormantGuard(session);
70
- logAction("browser_plan", "create", `goal: ${activeGoal}, ${activeTasks.length} tasks`);
71
- return { success: true, plan: { goal: activeGoal, tasks: activeTasks } };
72
- }
73
- if (action === "update") {
74
- if (taskId === undefined || !status) {
75
- return { success: false, error: "browser_plan update requires taskId and status" };
76
- }
77
- const task = activeTasks.find(t => t.id === taskId);
78
- if (!task) {
79
- return { success: false, error: `Task ${taskId} not found` };
80
- }
81
- task.status = status;
82
- // Use sendToExtension for reliable delivery
83
- await sendToExtension(session, {
84
- type: 'Weavetab_STATE',
85
- plan: { action: 'update', goal: activeGoal, tasks: activeTasks },
86
- }).catch(() => { });
87
- // Also try direct injection as fallback
88
- await session.Runtime.evaluate({
89
- expression: `(function() {
90
- if (typeof window.__wt_plan === 'function') {
91
- window.__wt_plan('update', ${JSON.stringify(activeGoal)}, ${JSON.stringify(activeTasks)});
92
- }
93
- })()`,
94
- returnByValue: true,
95
- }).catch(() => { });
96
- resetDormantGuard(session);
97
- logAction("browser_plan", "update", `task ${taskId} \u2192 ${status}`);
98
- return { success: true, plan: { goal: activeGoal, tasks: activeTasks } };
99
- }
100
- if (action === "hide") {
101
- if (dormantTimer) {
102
- clearTimeout(dormantTimer);
103
- dormantTimer = null;
104
- }
105
- // Use sendToExtension for reliable delivery
106
- await sendToExtension(session, {
107
- type: 'Weavetab_STATE',
108
- plan: { action: 'hide', goal: '', tasks: [] },
109
- }).catch(() => { });
110
- // Also try direct injection as fallback
111
- await session.Runtime.evaluate({
112
- expression: `(function() {
113
- if (typeof window.__wt_plan === 'function') window.__wt_plan('hide');
114
- })()`,
115
- returnByValue: true,
116
- }).catch(() => { });
117
- return { success: true };
118
- }
119
- if (action === "comments") {
120
- return { success: true, comments: planComments.slice(), plan: { goal: activeGoal, tasks: activeTasks } };
121
- }
122
- return { success: false, error: `Unknown action: ${action}` };
123
- }
124
- export function addPlanComment(taskId, comment) {
125
- planComments.push({ taskId, comment, time: Date.now() });
126
- }
127
- /** Returns current in-memory plan (goal + tasks) — used to re-inject on page navigation */
128
- export function getActivePlan() {
129
- if (!activeGoal && activeTasks.length === 0)
130
- return null;
131
- return { goal: activeGoal, tasks: activeTasks };
132
- }
133
- export function setActivePlan(goal, tasks) {
134
- activeGoal = goal || "";
135
- activeTasks = tasks || [];
136
- }
137
- /** Re-inject plan into a (possibly fresh) page session — used after navigation / reconnect */
138
- export async function restoreActivePlanIntoPage(session) {
139
- if (!activeGoal && activeTasks.length === 0)
140
- return;
141
- // Use sendToExtension for reliable delivery
142
- try {
143
- await sendToExtension(session, {
144
- type: 'Weavetab_STATE',
145
- plan: { action: 'create', goal: activeGoal, tasks: activeTasks },
146
- });
147
- }
148
- catch { /* non-fatal */ }
149
- // Also try direct injection as fallback
150
- try {
151
- await session.Runtime.evaluate({
152
- expression: `(function() {
153
- if (typeof window.__wt_plan === 'function') {
154
- window.__wt_plan('create', ${JSON.stringify(activeGoal)}, ${JSON.stringify(activeTasks)});
155
- }
156
- })()`,
157
- returnByValue: true,
158
- });
159
- }
160
- catch { /* page may not be ready */ }
161
- }