ft-scout 9.0.1 → 9.0.3

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 (50) hide show
  1. package/.firebase/hosting.d2Vi.cache +7 -7
  2. package/README.md +93 -13
  3. package/bin/src/commands/agent.d.ts.map +1 -1
  4. package/bin/src/commands/agent.js +46 -3
  5. package/bin/src/commands/agent.js.map +1 -1
  6. package/bin/src/engine/actionVerifier.d.ts.map +1 -0
  7. package/bin/src/engine/actionVerifier.js +223 -0
  8. package/bin/src/engine/actionVerifier.js.map +1 -0
  9. package/bin/src/engine/agentEngine.d.ts.map +1 -1
  10. package/bin/src/engine/agentEngine.js +237 -1
  11. package/bin/src/engine/agentEngine.js.map +1 -1
  12. package/bin/src/engine/agentState.d.ts.map +1 -0
  13. package/bin/src/engine/agentState.js +445 -0
  14. package/bin/src/engine/agentState.js.map +1 -0
  15. package/bin/src/engine/appControl.d.ts.map +1 -1
  16. package/bin/src/engine/appControl.js +165 -59
  17. package/bin/src/engine/appControl.js.map +1 -1
  18. package/bin/src/engine/browser/browserController.d.ts.map +1 -0
  19. package/bin/src/engine/browser/browserController.js +689 -0
  20. package/bin/src/engine/browser/browserController.js.map +1 -0
  21. package/bin/src/engine/browser/index.d.ts.map +1 -0
  22. package/bin/src/engine/browser/index.js +2 -0
  23. package/bin/src/engine/browser/index.js.map +1 -0
  24. package/bin/src/engine/computer/computerController.d.ts.map +1 -0
  25. package/bin/src/engine/computer/computerController.js +295 -0
  26. package/bin/src/engine/computer/computerController.js.map +1 -0
  27. package/bin/src/engine/computer/index.d.ts.map +1 -0
  28. package/bin/src/engine/computer/index.js +2 -0
  29. package/bin/src/engine/computer/index.js.map +1 -0
  30. package/bin/src/engine/liveScreenEngine.js +2 -2
  31. package/bin/src/engine/liveScreenEngine.js.map +1 -1
  32. package/bin/src/engine/planner.d.ts.map +1 -0
  33. package/bin/src/engine/planner.js +242 -0
  34. package/bin/src/engine/planner.js.map +1 -0
  35. package/bin/src/engine/safety.d.ts.map +1 -0
  36. package/bin/src/engine/safety.js +281 -0
  37. package/bin/src/engine/safety.js.map +1 -0
  38. package/bin/src/engine/toolRegistry.d.ts.map +1 -0
  39. package/bin/src/engine/toolRegistry.js +218 -0
  40. package/bin/src/engine/toolRegistry.js.map +1 -0
  41. package/bin/src/index.js +6 -4
  42. package/bin/src/index.js.map +1 -1
  43. package/bin/src/server/server.d.ts.map +1 -1
  44. package/bin/src/server/server.js +116 -0
  45. package/bin/src/server/server.js.map +1 -1
  46. package/bin/src/utils/branding.js +1 -1
  47. package/downloads/INVOICE_2026_ACME.pdf +1 -0
  48. package/package.json +1 -1
  49. package/web/app.js +277 -46
  50. package/web/styles.css +79 -2
@@ -62,7 +62,7 @@ export function buildSocialUrl(platformOrAppName, target, text) {
62
62
  const isLinkedIn = pLower.includes('linkedin') || cleanTarget.includes('linkedin.com');
63
63
  if (isInstagram) {
64
64
  if (handle && handle !== 'instagram' && !cleanTarget.includes('http')) {
65
- return `https://www.instagram.com/direct/i/`;
65
+ return `https://www.instagram.com/${handle}/`;
66
66
  }
67
67
  return `https://www.instagram.com/direct/inbox/`;
68
68
  }
@@ -653,9 +653,23 @@ export async function executeInApp(appName, action, payload) {
653
653
  if (!msgText && typeof payload === 'string' && payload.length > 0 && !payload.startsWith('@') && !payload.startsWith('http') && !payload.includes('@')) {
654
654
  msgText = payload;
655
655
  }
656
- const isEmail = ['send_mail', 'send_email', 'mail', 'email'].includes(actLower) || recipient.includes('@');
657
- const targetUrl = buildSocialUrl(isEmail ? 'gmail' : name, recipient, msgText);
658
- const openRes = await openApp('browser', targetUrl);
656
+ const cleanRecipient = String(recipient || '').trim();
657
+ const cleanMsg = String(msgText || '').trim();
658
+ if (!cleanRecipient && !cleanMsg) {
659
+ const targetPlatform = name || 'browser';
660
+ return {
661
+ success: false,
662
+ app: targetPlatform,
663
+ action: actLower,
664
+ output: `Missing recipient username/handle and message text for ${targetPlatform}. You MUST call the ask_user tool to ask: "Who would you like to message on ${targetPlatform}, and what message should I send?" before completing the task.`,
665
+ details: { platform: targetPlatform, recipient: '', msgText: '' },
666
+ };
667
+ }
668
+ const isEmail = ['send_mail', 'send_email', 'mail', 'email'].includes(actLower) || cleanRecipient.includes('@');
669
+ const targetUrl = buildSocialUrl(isEmail ? 'gmail' : name, cleanRecipient, cleanMsg);
670
+ await openApp('browser', targetUrl);
671
+ // Display aura HUD directly over the browser window
672
+ showScreenAuraOverlay(4500, `Scout is messaging on ${isEmail ? 'Gmail' : (name || 'browser')}...`, false, 'browser').catch(() => { });
659
673
  let keystrokeOutput = '';
660
674
  if (isEmail) {
661
675
  // For Gmail direct compose: wait 2.5s for page & compose dialog to render, then press Ctrl+Enter to send!
@@ -664,19 +678,38 @@ export async function executeInApp(appName, action, payload) {
664
678
  const ksRes = await sendKeystrokes('browser', '', false, 'ctrl+enter');
665
679
  keystrokeOutput = `\nAuto-Send Keystroke Dispatch (Ctrl+Enter): ${ksRes.output}`;
666
680
  }
667
- else if (msgText) {
681
+ else if (cleanRecipient && (name.toLowerCase().includes('instagram') || targetUrl.includes('instagram.com'))) {
682
+ // Real Instagram Web UI Automation:
683
+ // 1. Wait for profile / direct page to fully render
684
+ await new Promise((r) => setTimeout(r, 3500));
685
+ // 2. Focus browser window
686
+ await focusWindow('chrome');
687
+ await focusWindow('browser');
688
+ // 3. If message text is provided, click "Message" on profile and type text
689
+ if (cleanMsg) {
690
+ await new Promise((r) => setTimeout(r, 1000));
691
+ await findAndClickElement('Message', 'browser');
692
+ await new Promise((r) => setTimeout(r, 2200));
693
+ const ksRes = await sendKeystrokes('browser', cleanMsg, true);
694
+ keystrokeOutput = `\nInstagram Message Dispatched to ${cleanRecipient}: ${ksRes.output}`;
695
+ }
696
+ else {
697
+ keystrokeOutput = `\nNavigated to Instagram profile for ${cleanRecipient}. Message text was not specified. Call ask_user to get message content.`;
698
+ }
699
+ }
700
+ else if (cleanMsg) {
668
701
  // Lock user interruption during scratchpad keystroke dispatch so user clicks don't interrupt
669
702
  await lockAppInput(name || 'browser', 3000);
670
- await new Promise((r) => setTimeout(r, 1200));
671
- const ksRes = await sendKeystrokes(name || 'browser', msgText, true);
703
+ await new Promise((r) => setTimeout(r, 2000));
704
+ const ksRes = await sendKeystrokes(name || 'browser', cleanMsg, true);
672
705
  keystrokeOutput = `\nAuto-Keystroke Automated Dispatch: ${ksRes.output}`;
673
706
  }
674
707
  return {
675
708
  success: true,
676
709
  app: name,
677
710
  action: actLower,
678
- output: `Executed ${isEmail ? 'Gmail / Email Dispatch' : `${name} Direct Message`} for target recipient "${recipient}" at ${targetUrl}.${keystrokeOutput}`,
679
- details: { platform: isEmail ? 'gmail' : name, recipient, msgText, targetUrl },
711
+ output: `Executed ${isEmail ? 'Gmail / Email Dispatch' : `${name} Direct Message`} for target recipient "${cleanRecipient}" at ${targetUrl}.${keystrokeOutput}`,
712
+ details: { platform: isEmail ? 'gmail' : name, recipient: cleanRecipient, msgText: cleanMsg, targetUrl },
680
713
  };
681
714
  }
682
715
  }
@@ -1069,13 +1102,87 @@ export async function getScreenInfo(appName) {
1069
1102
  };
1070
1103
  }
1071
1104
  }
1105
+ function escapeXaml(text) {
1106
+ return text
1107
+ .replace(/&/g, '&')
1108
+ .replace(/</g, '&lt;')
1109
+ .replace(/>/g, '&gt;')
1110
+ .replace(/"/g, '&quot;')
1111
+ .replace(/'/g, '&apos;')
1112
+ .replace(/•/g, '&#x2022;');
1113
+ }
1072
1114
  let persistentAuraProcess = null;
1073
1115
  let persistentAuraFlagPath = null;
1116
+ function buildTargetWindowRectSnippet(targetApp) {
1117
+ const clean = (targetApp || '').trim().replace(/["'`$\\]/g, '');
1118
+ if (!clean || clean.toLowerCase() === 'desktop' || clean.toLowerCase() === 'system') {
1119
+ return `
1120
+ $window.Left = [System.Windows.SystemParameters]::VirtualScreenLeft
1121
+ $window.Top = [System.Windows.SystemParameters]::VirtualScreenTop
1122
+ $window.Width = [System.Windows.SystemParameters]::VirtualScreenWidth
1123
+ $window.Height = [System.Windows.SystemParameters]::VirtualScreenHeight
1124
+ `;
1125
+ }
1126
+ return `
1127
+ $targetApp = "${clean}"
1128
+ $winLeft = [System.Windows.SystemParameters]::VirtualScreenLeft
1129
+ $winTop = [System.Windows.SystemParameters]::VirtualScreenTop
1130
+ $winWidth = [System.Windows.SystemParameters]::VirtualScreenWidth
1131
+ $winHeight = [System.Windows.SystemParameters]::VirtualScreenHeight
1132
+
1133
+ $procNames = @($targetApp)
1134
+ if ($targetApp -match "browser|chrome|edge|web") {
1135
+ $procNames = @("chrome", "msedge", "brave", "firefox")
1136
+ } elseif ($targetApp -match "editor|code|vscode") {
1137
+ $procNames = @("Code", "cursor", "notepad")
1138
+ }
1139
+
1140
+ $proc = Get-Process -Name $procNames -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowHandle -ne 0 } | Select-Object -First 1
1141
+ if (-not $proc) {
1142
+ $proc = Get-Process | Where-Object { ($_.MainWindowTitle -like "*$targetApp*" -or $_.ProcessName -like "*$targetApp*") -and $_.MainWindowHandle -ne 0 } | Select-Object -First 1
1143
+ }
1144
+
1145
+ if ($proc) {
1146
+ try {
1147
+ $sigRect = '[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }'
1148
+ $wType = Add-Type -MemberDefinition $sigRect -Name ('WinRect_' + [Guid]::NewGuid().ToString('N').Substring(0,8)) -Namespace 'Win32' -PassThru
1149
+ $rc = [Activator]::CreateInstance($wType.GetNestedType('RECT'))
1150
+ [void]$wType::GetWindowRect($proc.MainWindowHandle, [ref]$rc)
1151
+ $calcW = $rc.Right - $rc.Left
1152
+ $calcH = $rc.Bottom - $rc.Top
1153
+ if ($calcW -gt 120 -and $calcH -gt 120) {
1154
+ $winLeft = [double]$rc.Left
1155
+ $winTop = [double]$rc.Top
1156
+ $winWidth = [double]$calcW
1157
+ $winHeight = [double]$calcH
1158
+ }
1159
+ } catch {
1160
+ try {
1161
+ Add-Type -AssemblyName UIAutomationClient
1162
+ $el = [System.Windows.Automation.AutomationElement]::FromHandle($proc.MainWindowHandle)
1163
+ $b = $el.Current.BoundingRectangle
1164
+ if ($b.Width -gt 120 -and $b.Height -gt 120) {
1165
+ $winLeft = [double]$b.X
1166
+ $winTop = [double]$b.Y
1167
+ $winWidth = [double]$b.Width
1168
+ $winHeight = [double]$b.Height
1169
+ }
1170
+ } catch {}
1171
+ }
1172
+ }
1173
+
1174
+ $window.Left = $winLeft
1175
+ $window.Top = $winTop
1176
+ $window.Width = $winWidth
1177
+ $window.Height = $winHeight
1178
+ `;
1179
+ }
1074
1180
  /**
1075
1181
  * Start persistent futuristic sky-blue aura overlay on screen borders and corners.
1076
1182
  * Stays continuously active and animating as long as live screen sharing is running.
1183
+ * When targetApp is specified, bounds strictly to that application window.
1077
1184
  */
1078
- export async function startScreenAuraOverlay(message = "Live Screen Share Active • Streaming to Model") {
1185
+ export async function startScreenAuraOverlay(message = "Live Screen Share Active • Streaming to Model", targetApp) {
1079
1186
  const platform = process.platform;
1080
1187
  if (platform !== 'win32')
1081
1188
  return;
@@ -1090,7 +1197,9 @@ export async function startScreenAuraOverlay(message = "Live Screen Share Active
1090
1197
  const flagFile = path.join(scriptsDir, 'aura_active.flag');
1091
1198
  fs.writeFileSync(flagFile, String(Date.now()), 'utf8');
1092
1199
  persistentAuraFlagPath = flagFile;
1093
- const subtitleText = " Perception Active";
1200
+ const subtitleText = targetApp ? ` • Focused: ${targetApp}` : " Perception Active";
1201
+ const safeMessage = escapeXaml(message);
1202
+ const safeSubtitle = escapeXaml(subtitleText);
1094
1203
  const parentPid = process.pid;
1095
1204
  const overlayPs = `
1096
1205
  Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase
@@ -1138,55 +1247,55 @@ Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase
1138
1247
 
1139
1248
  <!-- Sky Blue Corner Brackets with Aura Glow -->
1140
1249
  <!-- Top Left Corner -->
1141
- <Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Height="120" Margin="6,6,0,0">
1142
- <Path x:Name="CornerTL" Data="M 0,90 L 0,0 L 90,0" Stroke="#00e5ff" StrokeThickness="6">
1250
+ <Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="100" Margin="4,4,0,0">
1251
+ <Path x:Name="CornerTL" Data="M 0,70 L 0,0 L 70,0" Stroke="#00e5ff" StrokeThickness="5">
1143
1252
  <Path.Effect>
1144
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1253
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1145
1254
  </Path.Effect>
1146
1255
  </Path>
1147
1256
  </Canvas>
1148
1257
 
1149
1258
  <!-- Top Right Corner -->
1150
- <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Width="120" Height="120" Margin="0,6,6,0">
1151
- <Path x:Name="CornerTR" Data="M 30,0 L 120,0 L 120,90" Stroke="#00e5ff" StrokeThickness="6">
1259
+ <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Width="100" Height="100" Margin="0,4,4,0">
1260
+ <Path x:Name="CornerTR" Data="M 30,0 L 100,0 L 100,70" Stroke="#00e5ff" StrokeThickness="5">
1152
1261
  <Path.Effect>
1153
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1262
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1154
1263
  </Path.Effect>
1155
1264
  </Path>
1156
1265
  </Canvas>
1157
1266
 
1158
1267
  <!-- Bottom Left Corner -->
1159
- <Canvas HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="120" Height="120" Margin="6,0,0,6">
1160
- <Path x:Name="CornerBL" Data="M 0,30 L 0,120 L 90,120" Stroke="#00e5ff" StrokeThickness="6">
1268
+ <Canvas HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="100" Height="100" Margin="4,0,0,4">
1269
+ <Path x:Name="CornerBL" Data="M 0,30 L 0,100 L 70,100" Stroke="#00e5ff" StrokeThickness="5">
1161
1270
  <Path.Effect>
1162
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1271
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1163
1272
  </Path.Effect>
1164
1273
  </Path>
1165
1274
  </Canvas>
1166
1275
 
1167
1276
  <!-- Bottom Right Corner -->
1168
- <Canvas HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="120" Height="120" Margin="0,0,6,6">
1169
- <Path x:Name="CornerBR" Data="M 30,120 L 120,120 L 120,30" Stroke="#00e5ff" StrokeThickness="6">
1277
+ <Canvas HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="100" Height="100" Margin="0,0,4,4">
1278
+ <Path x:Name="CornerBR" Data="M 30,100 L 100,100 L 100,30" Stroke="#00e5ff" StrokeThickness="5">
1170
1279
  <Path.Effect>
1171
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1280
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1172
1281
  </Path.Effect>
1173
1282
  </Path>
1174
1283
  </Canvas>
1175
1284
 
1176
1285
  <!-- Top Floating Status Badge -->
1177
- <Border HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,22,0,0"
1178
- Background="#EE03121F" BorderBrush="#00e5ff" BorderThickness="1.8" CornerRadius="24" Padding="26,10">
1286
+ <Border HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,16,0,0"
1287
+ Background="#EE03121F" BorderBrush="#00e5ff" BorderThickness="1.8" CornerRadius="24" Padding="22,8">
1179
1288
  <Border.Effect>
1180
1289
  <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="0.95"/>
1181
1290
  </Border.Effect>
1182
1291
  <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
1183
- <Ellipse Width="12" Height="12" Fill="#00e5ff" Margin="0,0,12,0">
1292
+ <Ellipse Width="10" Height="10" Fill="#00e5ff" Margin="0,0,10,0">
1184
1293
  <Ellipse.Effect>
1185
1294
  <DropShadowEffect Color="#00e5ff" BlurRadius="12" ShadowDepth="0"/>
1186
1295
  </Ellipse.Effect>
1187
1296
  </Ellipse>
1188
- <TextBlock Text="${message}" Foreground="#ffffff" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
1189
- <TextBlock Text="${subtitleText}" Foreground="#38bdf8" FontSize="13.5" FontWeight="SemiBold" Margin="10,0,0,0" VerticalAlignment="Center"/>
1297
+ <TextBlock Text="${safeMessage}" Foreground="#ffffff" FontSize="15" FontWeight="Bold" VerticalAlignment="Center"/>
1298
+ <TextBlock Text="${safeSubtitle}" Foreground="#38bdf8" FontSize="13" FontWeight="SemiBold" Margin="8,0,0,0" VerticalAlignment="Center"/>
1190
1299
  </StackPanel>
1191
1300
  </Border>
1192
1301
  </Grid>
@@ -1196,10 +1305,7 @@ Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase
1196
1305
  $reader = (New-Object System.Xml.XmlNodeReader $xaml)
1197
1306
  $window = [System.Windows.Markup.XamlReader]::Load($reader)
1198
1307
 
1199
- $window.Left = [System.Windows.SystemParameters]::VirtualScreenLeft
1200
- $window.Top = [System.Windows.SystemParameters]::VirtualScreenTop
1201
- $window.Width = [System.Windows.SystemParameters]::VirtualScreenWidth
1202
- $window.Height = [System.Windows.SystemParameters]::VirtualScreenHeight
1308
+ ${buildTargetWindowRectSnippet(targetApp)}
1203
1309
  $window.Topmost = $true
1204
1310
  $window.ShowActivated = $false
1205
1311
 
@@ -1233,7 +1339,7 @@ $timer.Start()
1233
1339
 
1234
1340
  [void]$window.ShowDialog()
1235
1341
  `;
1236
- fs.writeFileSync(psFile, overlayPs, 'utf8');
1342
+ fs.writeFileSync(psFile, '\uFEFF' + overlayPs, 'utf8');
1237
1343
  const child = spawn('powershell', ['-WindowStyle', 'Hidden', '-NoProfile', '-Sta', '-ExecutionPolicy', 'Bypass', '-File', psFile], {
1238
1344
  detached: false,
1239
1345
  stdio: 'ignore',
@@ -1265,11 +1371,12 @@ export function stopScreenAuraOverlay() {
1265
1371
  /**
1266
1372
  * Display a futuristic sky-blue aura animation on screen borders and corners,
1267
1373
  * show the warning "Scout is on the screen.", and optionally block external inputs.
1374
+ * When targetApp is specified, bounds strictly to that application window.
1268
1375
  * If durationMs <= 0, aura stays on continuously until stopScreenAuraOverlay() is called.
1269
1376
  */
1270
- export async function showScreenAuraOverlay(durationMs = 2200, message = "Scout is viewing the screen.", blockInput = false) {
1377
+ export async function showScreenAuraOverlay(durationMs = 2200, message = "Scout is viewing the screen.", blockInput = false, targetApp) {
1271
1378
  if (durationMs <= 0) {
1272
- return await startScreenAuraOverlay(message);
1379
+ return await startScreenAuraOverlay(message, targetApp);
1273
1380
  }
1274
1381
  const platform = process.platform;
1275
1382
  if (platform !== 'win32')
@@ -1288,7 +1395,9 @@ try { [void]$b::BlockInput($true) } catch {}
1288
1395
  const unblockSnippet = blockInput ? `
1289
1396
  try { [void]$b::BlockInput($false) } catch {}
1290
1397
  ` : '';
1291
- const subtitleText = blockInput ? " • External Input Locked" : " Perception Active";
1398
+ const subtitleText = blockInput ? (targetApp ? ` • Controlling ${targetApp} (Input Locked)` : " • External Input Locked") : (targetApp ? ` • Focused: ${targetApp}` : " Perception Active");
1399
+ const safeMessage = escapeXaml(message);
1400
+ const safeSubtitle = escapeXaml(subtitleText);
1292
1401
  const overlayPs = `
1293
1402
  Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase
1294
1403
  ${blockInputSnippet}
@@ -1336,55 +1445,55 @@ ${blockInputSnippet}
1336
1445
 
1337
1446
  <!-- Sky Blue Corner Brackets with Aura Glow -->
1338
1447
  <!-- Top Left Corner -->
1339
- <Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Height="120" Margin="6,6,0,0">
1340
- <Path x:Name="CornerTL" Data="M 0,90 L 0,0 L 90,0" Stroke="#00e5ff" StrokeThickness="6">
1448
+ <Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="100" Margin="4,4,0,0">
1449
+ <Path x:Name="CornerTL" Data="M 0,70 L 0,0 L 70,0" Stroke="#00e5ff" StrokeThickness="5">
1341
1450
  <Path.Effect>
1342
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1451
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1343
1452
  </Path.Effect>
1344
1453
  </Path>
1345
1454
  </Canvas>
1346
1455
 
1347
1456
  <!-- Top Right Corner -->
1348
- <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Width="120" Height="120" Margin="0,6,6,0">
1349
- <Path x:Name="CornerTR" Data="M 30,0 L 120,0 L 120,90" Stroke="#00e5ff" StrokeThickness="6">
1457
+ <Canvas HorizontalAlignment="Right" VerticalAlignment="Top" Width="100" Height="100" Margin="0,4,4,0">
1458
+ <Path x:Name="CornerTR" Data="M 30,0 L 100,0 L 100,70" Stroke="#00e5ff" StrokeThickness="5">
1350
1459
  <Path.Effect>
1351
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1460
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1352
1461
  </Path.Effect>
1353
1462
  </Path>
1354
1463
  </Canvas>
1355
1464
 
1356
1465
  <!-- Bottom Left Corner -->
1357
- <Canvas HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="120" Height="120" Margin="6,0,0,6">
1358
- <Path x:Name="CornerBL" Data="M 0,30 L 0,120 L 90,120" Stroke="#00e5ff" StrokeThickness="6">
1466
+ <Canvas HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="100" Height="100" Margin="4,0,0,4">
1467
+ <Path x:Name="CornerBL" Data="M 0,30 L 0,100 L 70,100" Stroke="#00e5ff" StrokeThickness="5">
1359
1468
  <Path.Effect>
1360
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1469
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1361
1470
  </Path.Effect>
1362
1471
  </Path>
1363
1472
  </Canvas>
1364
1473
 
1365
1474
  <!-- Bottom Right Corner -->
1366
- <Canvas HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="120" Height="120" Margin="0,0,6,6">
1367
- <Path x:Name="CornerBR" Data="M 30,120 L 120,120 L 120,30" Stroke="#00e5ff" StrokeThickness="6">
1475
+ <Canvas HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="100" Height="100" Margin="0,0,4,4">
1476
+ <Path x:Name="CornerBR" Data="M 30,100 L 100,100 L 100,30" Stroke="#00e5ff" StrokeThickness="5">
1368
1477
  <Path.Effect>
1369
- <DropShadowEffect Color="#00e5ff" BlurRadius="30" ShadowDepth="0" Opacity="1"/>
1478
+ <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="1"/>
1370
1479
  </Path.Effect>
1371
1480
  </Path>
1372
1481
  </Canvas>
1373
1482
 
1374
1483
  <!-- Top Floating Status Badge -->
1375
- <Border HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,22,0,0"
1376
- Background="#EE03121F" BorderBrush="#00e5ff" BorderThickness="1.8" CornerRadius="24" Padding="26,10">
1484
+ <Border HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,16,0,0"
1485
+ Background="#EE03121F" BorderBrush="#00e5ff" BorderThickness="1.8" CornerRadius="24" Padding="22,8">
1377
1486
  <Border.Effect>
1378
1487
  <DropShadowEffect Color="#00e5ff" BlurRadius="25" ShadowDepth="0" Opacity="0.95"/>
1379
1488
  </Border.Effect>
1380
1489
  <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
1381
- <Ellipse Width="12" Height="12" Fill="#00e5ff" Margin="0,0,12,0">
1490
+ <Ellipse Width="10" Height="10" Fill="#00e5ff" Margin="0,0,10,0">
1382
1491
  <Ellipse.Effect>
1383
1492
  <DropShadowEffect Color="#00e5ff" BlurRadius="12" ShadowDepth="0"/>
1384
1493
  </Ellipse.Effect>
1385
1494
  </Ellipse>
1386
- <TextBlock Text="${message}" Foreground="#ffffff" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
1387
- <TextBlock Text="${subtitleText}" Foreground="#38bdf8" FontSize="13.5" FontWeight="SemiBold" Margin="10,0,0,0" VerticalAlignment="Center"/>
1495
+ <TextBlock Text="${safeMessage}" Foreground="#ffffff" FontSize="15" FontWeight="Bold" VerticalAlignment="Center"/>
1496
+ <TextBlock Text="${safeSubtitle}" Foreground="#38bdf8" FontSize="13" FontWeight="SemiBold" Margin="8,0,0,0" VerticalAlignment="Center"/>
1388
1497
  </StackPanel>
1389
1498
  </Border>
1390
1499
  </Grid>
@@ -1394,10 +1503,7 @@ ${blockInputSnippet}
1394
1503
  $reader = (New-Object System.Xml.XmlNodeReader $xaml)
1395
1504
  $window = [System.Windows.Markup.XamlReader]::Load($reader)
1396
1505
 
1397
- $window.Left = [System.Windows.SystemParameters]::VirtualScreenLeft
1398
- $window.Top = [System.Windows.SystemParameters]::VirtualScreenTop
1399
- $window.Width = [System.Windows.SystemParameters]::VirtualScreenWidth
1400
- $window.Height = [System.Windows.SystemParameters]::VirtualScreenHeight
1506
+ ${buildTargetWindowRectSnippet(targetApp)}
1401
1507
  $window.Topmost = $true
1402
1508
  $window.ShowActivated = $false
1403
1509
 
@@ -1419,7 +1525,7 @@ $timer.Start()
1419
1525
  [void]$window.ShowDialog()
1420
1526
  ${unblockSnippet}
1421
1527
  `;
1422
- fs.writeFileSync(psFile, overlayPs, 'utf8');
1528
+ fs.writeFileSync(psFile, '\uFEFF' + overlayPs, 'utf8');
1423
1529
  await execAsync(`powershell -WindowStyle Hidden -NoProfile -Sta -ExecutionPolicy Bypass -File "${psFile}"`, { timeout: durationMs + 3000 });
1424
1530
  }
1425
1531
  catch {
@@ -1702,7 +1808,7 @@ export async function takeoverControl(appName, durationMs = 3000, x, y) {
1702
1808
  // Trigger visual sky-blue aura overlay on screen borders & corners with warning badge & input block
1703
1809
  try {
1704
1810
  const auraLabel = targetApp ? `Scout is controlling ${targetApp}...` : "Scout is on the screen.";
1705
- await showScreenAuraOverlay(Math.min(5000, Math.max(800, durationMs)), auraLabel, true);
1811
+ await showScreenAuraOverlay(Math.min(5000, Math.max(800, durationMs)), auraLabel, true, targetApp);
1706
1812
  startLiveScreenShare({ targetApp }).catch(() => { });
1707
1813
  }
1708
1814
  catch { }