mindweave 3.2.2 → 3.2.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 (53) hide show
  1. package/dist/cli/App.js +2 -2
  2. package/dist/cli/App.js.map +1 -1
  3. package/dist/cli/agentLines.js +196 -0
  4. package/dist/cli/agentLines.js.map +1 -0
  5. package/dist/cli/components/BlockView.js +1 -1
  6. package/dist/cli/components/BlockView.js.map +1 -1
  7. package/dist/cli/components/SubagentView.js +43 -74
  8. package/dist/cli/components/SubagentView.js.map +1 -1
  9. package/dist/cli/help.js +3 -1
  10. package/dist/cli/help.js.map +1 -1
  11. package/dist/cli/transcript.js +13 -3
  12. package/dist/cli/transcript.js.map +1 -1
  13. package/dist/core/turnRunner.js +2 -2
  14. package/dist/core/turnRunner.js.map +1 -1
  15. package/dist/dynamo/engine.js +6 -3
  16. package/dist/dynamo/engine.js.map +1 -1
  17. package/dist/dynamo/guard.js +6 -0
  18. package/dist/dynamo/guard.js.map +1 -1
  19. package/dist/dynamo/marathon.js +17 -4
  20. package/dist/dynamo/marathon.js.map +1 -1
  21. package/dist/memory/compaction.js +60 -6
  22. package/dist/memory/compaction.js.map +1 -1
  23. package/dist/memory/session.js +2 -0
  24. package/dist/memory/session.js.map +1 -1
  25. package/dist/tools/guard.js +13 -0
  26. package/dist/tools/guard.js.map +1 -1
  27. package/dist/tools/registry.js +1 -1
  28. package/dist/tools/registry.js.map +1 -1
  29. package/dist/tools/runCommand.js +9 -0
  30. package/dist/tools/runCommand.js.map +1 -1
  31. package/dist/tools/screenshot.js +28 -9
  32. package/dist/tools/screenshot.js.map +1 -1
  33. package/dist/tools/screenshotWin.js +258 -175
  34. package/dist/tools/screenshotWin.js.map +1 -1
  35. package/dist/tools/subagent.js +55 -27
  36. package/dist/tools/subagent.js.map +1 -1
  37. package/dist/tools/subagentOutcome.js +24 -0
  38. package/dist/tools/subagentOutcome.js.map +1 -0
  39. package/dist/tools/treeGuard.js +164 -0
  40. package/dist/tools/treeGuard.js.map +1 -0
  41. package/dist/tools/ui.js +27 -0
  42. package/dist/tools/ui.js.map +1 -1
  43. package/dist/tools/uiHide.js +19 -6
  44. package/dist/tools/uiHide.js.map +1 -1
  45. package/dist/tools/verifierCommands.js +188 -0
  46. package/dist/tools/verifierCommands.js.map +1 -0
  47. package/dist/tools/verifierRun.js +88 -0
  48. package/dist/tools/verifierRun.js.map +1 -0
  49. package/dist/tools/verifierTools.js +19 -0
  50. package/dist/tools/verifierTools.js.map +1 -0
  51. package/dist/tools/verifyReport.js +79 -9
  52. package/dist/tools/verifyReport.js.map +1 -1
  53. package/package.json +3 -3
@@ -58,6 +58,11 @@ export function pickWindow(query, windows) {
58
58
  return { kind: "none", candidates: [] };
59
59
  if (!query) {
60
60
  const focused = windows.find((w) => w.foreground);
61
+ // The focused window is the one the user is typing into, and while the agent works that is the terminal
62
+ // Mindweave runs in. "Whichever has focus" then photographs the conversation itself, which is never what
63
+ // was meant; an app was asked for by name, or was focused on purpose.
64
+ if (focused?.host)
65
+ return { kind: "host", window: focused, candidates: windows.filter((w) => !w.host) };
61
66
  return focused
62
67
  ? { kind: "match", window: focused, tied: windows.filter((w) => w !== focused) }
63
68
  : { kind: "ambiguous", candidates: windows };
@@ -123,12 +128,22 @@ export function ambiguousMessage(query, candidates) {
123
128
  return (`"${query}" matches ${candidates.length} windows, so it is not clear which to capture. ` +
124
129
  `Name one more precisely:\n${listTitles(candidates)}`);
125
130
  }
131
+ /**
132
+ * What to say when the focused window is the one Mindweave runs in (pure). It is the answer to a
133
+ * call with no title while the user is looking at the terminal, which is nearly every call: the model
134
+ * is told what the window is, that nothing was captured, and how to name the app instead.
135
+ */
136
+ export function hostMessage(host, others) {
137
+ return (`The focused window is the one Mindweave itself runs in ("${host.title}"), so nothing was captured: a picture of ` +
138
+ `it cannot tell you anything about the app you are testing. Name the app's window by part of its title. ` +
139
+ `A window with a blank title bar is listed by its process name, such as "electron".\n${listTitles(others)}`);
140
+ }
126
141
  /** Render window titles for an error the model can act on without another call. */
127
142
  export function listTitles(windows) {
128
143
  if (windows.length === 0)
129
144
  return "No capturable windows are open.";
130
145
  const shown = windows.slice(0, MAX_LISTED);
131
- const lines = shown.map((w) => ` - ${w.title}${w.foreground ? " (focused)" : ""}`);
146
+ const lines = shown.map((w) => ` - ${w.title}${w.foreground ? " (focused)" : ""}${w.host ? " (where Mindweave runs)" : ""}`);
132
147
  const hidden = windows.length - shown.length;
133
148
  if (hidden > 0)
134
149
  lines.push(` … and ${hidden} more`);
@@ -156,10 +171,11 @@ export const screenshot = {
156
171
  description: "Take a picture of one open window and look at it. Use it to check that an app " +
157
172
  "actually came up and renders correctly — a running process is not proof of that — " +
158
173
  "or to see a layout, a chart, or an error dialog for yourself. " +
159
- "Pass `window` with part of the window's title; leave it out to capture the window " +
160
- "the user is currently focused on. For an app you just launched, or one with a custom " +
161
- "title bar that shows a blank OS title (common for desktop apps), omit `window` to grab " +
162
- "the focused window rather than guessing its title. " +
174
+ "Pass `window` with part of the window's title. An app with a custom title bar that shows a blank " +
175
+ "OS title (common for desktop apps) is listed by its process name, such as \"electron\": use that. " +
176
+ "Leave `window` out only to capture the window the user has focused on purpose; the window " +
177
+ "Mindweave itself runs in is never captured that way, because while you work it is almost always the " +
178
+ "one with focus and a picture of it shows you nothing about the app. " +
163
179
  "It photographs whatever that window is showing, so call it when looking will " +
164
180
  "genuinely tell you something, not as a routine check. " +
165
181
  "Windows only, one window at a time — the whole screen is never captured. " +
@@ -171,8 +187,8 @@ export const screenshot = {
171
187
  properties: {
172
188
  window: {
173
189
  type: "string",
174
- description: "Part of the target window's title, e.g. \"Vite\" or \"localhost\". " +
175
- "Omit to capture whichever window has focus.",
190
+ description: "Part of the target window's title, e.g. \"Vite\" or \"localhost\", or the process name for a window " +
191
+ "with a blank title (\"electron\"). Omit only to capture a window the user has focused on purpose.",
176
192
  },
177
193
  },
178
194
  },
@@ -209,14 +225,17 @@ export const screenshot = {
209
225
  ? `No open window's title contains "${query}". ${listTitles(pick.candidates)}\n` +
210
226
  `Three things to try, in order: if you just launched the app, its window may need a ` +
211
227
  `moment — call again. If it has a custom title bar (many desktop apps, and Tauri or ` +
212
- `Electron with window decorations off, show a blank OS title), omit \`window\` to ` +
213
- `capture whichever window is focused. Or name one of the windows listed above. ` +
228
+ `Electron with window decorations off, show a blank OS title), it is listed by its ` +
229
+ `process name: use that. Or name one of the windows listed above. ` +
214
230
  `Do NOT capture a different named window as a stand-in: a picture of something else ` +
215
231
  `cannot tell you anything about "${query}".`
216
232
  : `There is no window to capture. ${listTitles(pick.candidates)}`);
217
233
  }
218
234
  if (pick.kind === "ambiguous")
219
235
  return fail(ambiguousMessage(query, pick.candidates));
236
+ // Refused before anyone is asked: the user is not asked to approve a picture that was never going to help.
237
+ if (pick.kind === "host")
238
+ return fail(hostMessage(pick.window, pick.candidates));
220
239
  const target = pick.window;
221
240
  // The window is resolved BEFORE this either way, so the user is never asked about a
222
241
  // capture that was never going to happen.
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"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"}
@@ -42,176 +42,221 @@ import { tmpdir } from "node:os";
42
42
  import { join } from "node:path";
43
43
  import { DESK_CS, HIDDEN_MARK, hiddenDesktopName, windowArgs } from "./hiddenDesktop.js";
44
44
  const PS_TIMEOUT_MS = 20_000;
45
- const SCRIPT = String.raw `
46
- param([string]$Mode = "list", [string]$Handle = "", [string]$Out = "", [string]$Desktop = "", [string]$Hidden = "")
47
- $ErrorActionPreference = "Stop"
48
- Add-Type -AssemblyName System.Drawing
49
-
50
- Add-Type -ReferencedAssemblies System.Drawing -TypeDefinition @"
51
- using System;
52
- using System.Collections.Generic;
53
- using System.Drawing;
54
- using System.Drawing.Imaging;
55
- using System.Runtime.InteropServices;
56
- using System.Text;
57
-
58
- public static class MwCapture {
59
- delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
60
- [DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr l);
61
- [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h);
62
- [DllImport("user32.dll")] static extern bool IsIconic(IntPtr h);
63
- [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetWindowTextLength(IntPtr h);
64
- [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetWindowText(IntPtr h, StringBuilder s, int n);
65
- [DllImport("user32.dll")] static extern bool PrintWindow(IntPtr h, IntPtr hdc, uint flags);
66
- [DllImport("user32.dll")] static extern bool SetProcessDPIAware();
67
- [DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr h, out RECT r);
68
- [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
69
- [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
70
- [DllImport("user32.dll")] static extern int GetWindowLong(IntPtr h, int index);
71
- [DllImport("dwmapi.dll")] static extern int DwmGetWindowAttribute(IntPtr h, int attr, out RECT r, int size);
72
- [DllImport("dwmapi.dll", EntryPoint = "DwmGetWindowAttribute")] static extern int DwmGetIntAttribute(IntPtr h, int attr, out int v, int size);
73
-
74
- [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
75
-
76
- public static void Dpi() { SetProcessDPIAware(); }
77
-
78
- // A DWM-cloaked window passes IsWindowVisible but is not actually on screen — the
79
- // invisible UWP and host windows that would otherwise flood the list. Any failure to
80
- // read the attribute is treated as not-cloaked, so a window is never dropped on error.
81
- static bool Cloaked(IntPtr h) {
82
- int v;
83
- return DwmGetIntAttribute(h, 14 /* DWMWA_CLOAKED */, out v, sizeof(int)) == 0 && v != 0;
84
- }
85
-
86
- // The Shell's own "is this an Alt-Tab window" test: a tool window is a palette or
87
- // tray surface and is never what "the app" means, unless it also asks for the taskbar.
88
- static bool AltTabWorthy(IntPtr h) {
89
- int ex = GetWindowLong(h, -20 /* GWL_EXSTYLE */);
90
- bool tool = (ex & 0x00000080) != 0; // WS_EX_TOOLWINDOW
91
- bool app = (ex & 0x00040000) != 0; // WS_EX_APPWINDOW
92
- return !tool || app;
93
- }
94
-
95
- // The window's process name, for a window that has no title of its own. Wrapped
96
- // because the process can exit between the enum and the lookup.
97
- static string ProcName(IntPtr h) {
98
- try {
99
- uint pid;
100
- GetWindowThreadProcessId(h, out pid);
101
- if (pid == 0) return "";
102
- using (var p = System.Diagnostics.Process.GetProcessById((int)pid)) return p.ProcessName;
103
- } catch { return ""; }
104
- }
105
-
106
- // DWMWA_EXTENDED_FRAME_BOUNDS. GetWindowRect includes an invisible border on
107
- // Windows 10+, so it is the fallback rather than the first choice.
108
- static bool Bounds(IntPtr h, out RECT r) {
109
- if (DwmGetWindowAttribute(h, 9, out r, Marshal.SizeOf(typeof(RECT))) == 0
110
- && r.Right > r.Left && r.Bottom > r.Top) return true;
111
- return GetWindowRect(h, out r);
112
- }
113
-
114
- // The hidden desktop's windows are marked, so a capture is sent back there.
115
- public static string ListOn(string desk) {
116
- return MwDesk.On(desk, () => string.Join("\n", List("${HIDDEN_MARK}").ToArray()));
117
- }
118
-
119
- public static List<string> List(string mark) {
120
- var found = new List<string>();
121
- IntPtr fg = GetForegroundWindow();
122
- EnumWindows(delegate(IntPtr h, IntPtr l) {
123
- if (!IsWindowVisible(h) || IsIconic(h)) return true;
124
- if (Cloaked(h) || !AltTabWorthy(h)) return true;
125
- RECT r;
126
- if (!Bounds(h, out r)) return true;
127
- // Tool windows and tray hosts are real but never what anyone means.
128
- if (r.Right - r.Left < 120 || r.Bottom - r.Top < 120) return true;
129
- int len = GetWindowTextLength(h);
130
- string title = "";
131
- if (len > 0) {
132
- var sb = new StringBuilder(len + 1);
133
- GetWindowText(h, sb, sb.Capacity);
134
- title = sb.ToString().Trim();
135
- }
136
- // An empty title is NOT a reason to drop the window. A custom title bar
137
- // (Tauri or Electron with decorations off) leaves the OS title blank, as does
138
- // an app whose web content has not set document.title yet — and dropping it
139
- // makes a window the user is plainly looking at impossible to capture, by name
140
- // OR as the foreground. Label it by its process so it stays nameable.
141
- if (title.Length == 0) {
142
- string pn = ProcName(h);
143
- title = pn.Length > 0 ? pn : "(untitled window)";
144
- }
145
- // The leading marker is how the caller knows which window the user is
146
- // actually looking at, so it can name it when asking permission.
147
- found.Add((h == fg && mark.Length == 0 ? "*" : "-") + mark + h.ToInt64().ToString() + "\t" + title);
148
- return true;
149
- }, IntPtr.Zero);
150
- return found;
151
- }
152
-
153
- public static string CaptureOn(string desk, IntPtr h, string path) {
154
- return MwDesk.On(desk, () => Capture(h, path, string.IsNullOrEmpty(desk)));
155
- }
156
-
157
- // screen: whether a blank PrintWindow may fall back to copying the screen. Never for a
158
- // window on the hidden desktop: the screen is the USER's, and that window is not on it.
159
- public static string Capture(IntPtr h, string path, bool screen) {
160
- RECT r;
161
- if (!Bounds(h, out r)) return "ERR could not measure the window";
162
- int w = r.Right - r.Left, ht = r.Bottom - r.Top;
163
- if (w <= 0 || ht <= 0) return "ERR the window has no visible area";
164
- using (var bmp = new Bitmap(w, ht)) {
165
- bool drew = false;
166
- using (var g = Graphics.FromImage(bmp)) {
167
- IntPtr hdc = g.GetHdc();
168
- drew = PrintWindow(h, hdc, 2); // PW_RENDERFULLCONTENT
169
- g.ReleaseHdc(hdc);
170
- }
171
- if (!drew || Blank(bmp)) {
172
- if (!screen) return "ERR the window drew nothing to capture";
173
- using (var g = Graphics.FromImage(bmp)) {
174
- g.CopyFromScreen(r.Left, r.Top, 0, 0, new Size(w, ht));
175
- }
176
- }
177
- bmp.Save(path, ImageFormat.Png);
178
- return "OK " + w + " " + ht;
179
- }
180
- }
181
-
182
- // PrintWindow can report success and draw nothing. Sampling three corners of a
183
- // fully transparent bitmap is enough to tell that apart from a real capture.
184
- static bool Blank(Bitmap b) {
185
- if (b.Width < 4 || b.Height < 4) return false;
186
- return b.GetPixel(1, 1).A == 0
187
- && b.GetPixel(b.Width / 2, b.Height / 2).A == 0
188
- && b.GetPixel(b.Width - 2, b.Height - 2).A == 0;
189
- }
190
- }
191
- ${DESK_CS}
192
- "@
193
-
194
- # Before any measurement, or every rectangle is in the wrong coordinate space.
195
- [MwCapture]::Dpi() | Out-Null
196
-
197
- if ($Mode -eq "list") {
198
- [MwCapture]::List("") | ForEach-Object { Write-Output $_ }
199
- # The windows of apps started for testing, which are never on the screen.
200
- if ($Hidden -and [MwDesk]::Exists($Hidden)) { Write-Output ([MwCapture]::ListOn($Hidden)) }
201
- exit 0
202
- }
203
-
204
- if ($Mode -eq "capture") {
205
- $h = if ($Handle -eq "foreground") { [MwCapture]::GetForegroundWindow() } else { [IntPtr][Int64]$Handle }
206
- if ($h -eq [IntPtr]::Zero) { Write-Output "ERR no such window"; exit 1 }
207
- $r = [MwCapture]::CaptureOn($Desktop, $h, $Out)
208
- Write-Output $r
209
- if ($r.StartsWith("ERR")) { exit 1 }
210
- exit 0
211
- }
212
-
213
- Write-Output "ERR unknown mode"
214
- exit 1
45
+ const SCRIPT = String.raw `
46
+ param([string]$Mode = "list", [string]$Handle = "", [string]$Out = "", [string]$Desktop = "", [string]$Hidden = "", [int]$Owner = 0)
47
+ $ErrorActionPreference = "Stop"
48
+ Add-Type -AssemblyName System.Drawing
49
+
50
+ Add-Type -ReferencedAssemblies System.Drawing -TypeDefinition @"
51
+ using System;
52
+ using System.Collections.Generic;
53
+ using System.Drawing;
54
+ using System.Drawing.Imaging;
55
+ using System.Runtime.InteropServices;
56
+ using System.Text;
57
+
58
+ public static class MwCapture {
59
+ // The processes this one runs inside (the terminal, its console host, the app hosting it).
60
+ // Their windows are marked "host" in the list, so the caller can tell them from the app under test.
61
+ public static HashSet<uint> HostPids = new HashSet<uint>();
62
+ delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
63
+ [DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr l);
64
+ [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h);
65
+ [DllImport("user32.dll")] static extern bool IsIconic(IntPtr h);
66
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetWindowTextLength(IntPtr h);
67
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetWindowText(IntPtr h, StringBuilder s, int n);
68
+ [DllImport("user32.dll")] static extern bool PrintWindow(IntPtr h, IntPtr hdc, uint flags);
69
+ [DllImport("user32.dll")] static extern bool SetProcessDPIAware();
70
+ [DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr h, out RECT r);
71
+ [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
72
+ [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
73
+ [DllImport("user32.dll")] static extern int GetWindowLong(IntPtr h, int index);
74
+ [DllImport("dwmapi.dll")] static extern int DwmGetWindowAttribute(IntPtr h, int attr, out RECT r, int size);
75
+ [DllImport("dwmapi.dll", EntryPoint = "DwmGetWindowAttribute")] static extern int DwmGetIntAttribute(IntPtr h, int attr, out int v, int size);
76
+
77
+ [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
78
+
79
+ public static void Dpi() { SetProcessDPIAware(); }
80
+
81
+ [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow();
82
+ [DllImport("user32.dll")] static extern IntPtr GetAncestor(IntPtr h, uint flags);
83
+
84
+ // The window the console this process is attached to is drawn in. In Windows Terminal the console
85
+ // window is a hidden stand-in owned by the real top-level window, so the owner is what is returned.
86
+ // Empty when there is no console (the desktop app, a service).
87
+ public static string HostWindow() {
88
+ IntPtr c = GetConsoleWindow();
89
+ if (c == IntPtr.Zero) return "";
90
+ IntPtr root = GetAncestor(c, 3 /* GA_ROOTOWNER */);
91
+ return (root == IntPtr.Zero ? c : root).ToInt64().ToString();
92
+ }
93
+
94
+ // A DWM-cloaked window passes IsWindowVisible but is not actually on screen — the
95
+ // invisible UWP and host windows that would otherwise flood the list. Any failure to
96
+ // read the attribute is treated as not-cloaked, so a window is never dropped on error.
97
+ static bool Cloaked(IntPtr h) {
98
+ int v;
99
+ return DwmGetIntAttribute(h, 14 /* DWMWA_CLOAKED */, out v, sizeof(int)) == 0 && v != 0;
100
+ }
101
+
102
+ // The Shell's own "is this an Alt-Tab window" test: a tool window is a palette or
103
+ // tray surface and is never what "the app" means, unless it also asks for the taskbar.
104
+ static bool AltTabWorthy(IntPtr h) {
105
+ int ex = GetWindowLong(h, -20 /* GWL_EXSTYLE */);
106
+ bool tool = (ex & 0x00000080) != 0; // WS_EX_TOOLWINDOW
107
+ bool app = (ex & 0x00040000) != 0; // WS_EX_APPWINDOW
108
+ return !tool || app;
109
+ }
110
+
111
+ // The window's process name, for a window that has no title of its own. Wrapped
112
+ // because the process can exit between the enum and the lookup.
113
+ static string ProcName(IntPtr h) {
114
+ try {
115
+ uint pid;
116
+ GetWindowThreadProcessId(h, out pid);
117
+ if (pid == 0) return "";
118
+ using (var p = System.Diagnostics.Process.GetProcessById((int)pid)) return p.ProcessName;
119
+ } catch { return ""; }
120
+ }
121
+
122
+ // DWMWA_EXTENDED_FRAME_BOUNDS. GetWindowRect includes an invisible border on
123
+ // Windows 10+, so it is the fallback rather than the first choice.
124
+ static bool Bounds(IntPtr h, out RECT r) {
125
+ if (DwmGetWindowAttribute(h, 9, out r, Marshal.SizeOf(typeof(RECT))) == 0
126
+ && r.Right > r.Left && r.Bottom > r.Top) return true;
127
+ return GetWindowRect(h, out r);
128
+ }
129
+
130
+ // The hidden desktop's windows are marked, so a capture is sent back there.
131
+ public static string ListOn(string desk) {
132
+ return MwDesk.On(desk, () => string.Join("\n", List("${HIDDEN_MARK}").ToArray()));
133
+ }
134
+
135
+ public static List<string> List(string mark) {
136
+ var found = new List<string>();
137
+ IntPtr fg = GetForegroundWindow();
138
+ EnumWindows(delegate(IntPtr h, IntPtr l) {
139
+ if (!IsWindowVisible(h) || IsIconic(h)) return true;
140
+ if (Cloaked(h) || !AltTabWorthy(h)) return true;
141
+ RECT r;
142
+ if (!Bounds(h, out r)) return true;
143
+ // Tool windows and tray hosts are real but never what anyone means.
144
+ if (r.Right - r.Left < 120 || r.Bottom - r.Top < 120) return true;
145
+ int len = GetWindowTextLength(h);
146
+ string title = "";
147
+ if (len > 0) {
148
+ var sb = new StringBuilder(len + 1);
149
+ GetWindowText(h, sb, sb.Capacity);
150
+ title = sb.ToString().Trim();
151
+ }
152
+ // An empty title is NOT a reason to drop the window. A custom title bar
153
+ // (Tauri or Electron with decorations off) leaves the OS title blank, as does
154
+ // an app whose web content has not set document.title yet — and dropping it
155
+ // makes a window the user is plainly looking at impossible to capture, by name
156
+ // OR as the foreground. Label it by its process so it stays nameable.
157
+ if (title.Length == 0) {
158
+ string pn = ProcName(h);
159
+ title = pn.Length > 0 ? pn : "(untitled window)";
160
+ }
161
+ // The leading marker is how the caller knows which window the user is
162
+ // actually looking at, so it can name it when asking permission.
163
+ uint wpid; GetWindowThreadProcessId(h, out wpid);
164
+ string hostCol = mark.Length == 0 && HostPids.Contains(wpid) ? "\thost" : "";
165
+ found.Add((h == fg && mark.Length == 0 ? "*" : "-") + mark + h.ToInt64().ToString() + "\t" + title + hostCol);
166
+ return true;
167
+ }, IntPtr.Zero);
168
+ return found;
169
+ }
170
+
171
+ public static string CaptureOn(string desk, IntPtr h, string path) {
172
+ return MwDesk.On(desk, () => Capture(h, path, string.IsNullOrEmpty(desk)));
173
+ }
174
+
175
+ // screen: whether a blank PrintWindow may fall back to copying the screen. Never for a
176
+ // window on the hidden desktop: the screen is the USER's, and that window is not on it.
177
+ public static string Capture(IntPtr h, string path, bool screen) {
178
+ RECT r;
179
+ if (!Bounds(h, out r)) return "ERR could not measure the window";
180
+ int w = r.Right - r.Left, ht = r.Bottom - r.Top;
181
+ if (w <= 0 || ht <= 0) return "ERR the window has no visible area";
182
+ using (var bmp = new Bitmap(w, ht)) {
183
+ bool drew = false;
184
+ using (var g = Graphics.FromImage(bmp)) {
185
+ IntPtr hdc = g.GetHdc();
186
+ drew = PrintWindow(h, hdc, 2); // PW_RENDERFULLCONTENT
187
+ g.ReleaseHdc(hdc);
188
+ }
189
+ if (!drew || Blank(bmp)) {
190
+ if (!screen) return "ERR the window drew nothing to capture";
191
+ using (var g = Graphics.FromImage(bmp)) {
192
+ g.CopyFromScreen(r.Left, r.Top, 0, 0, new Size(w, ht));
193
+ }
194
+ }
195
+ bmp.Save(path, ImageFormat.Png);
196
+ return "OK " + w + " " + ht;
197
+ }
198
+ }
199
+
200
+ // PrintWindow can report success and draw nothing. Sampling three corners of a
201
+ // fully transparent bitmap is enough to tell that apart from a real capture.
202
+ static bool Blank(Bitmap b) {
203
+ if (b.Width < 4 || b.Height < 4) return false;
204
+ return b.GetPixel(1, 1).A == 0
205
+ && b.GetPixel(b.Width / 2, b.Height / 2).A == 0
206
+ && b.GetPixel(b.Width - 2, b.Height - 2).A == 0;
207
+ }
208
+ }
209
+ ${DESK_CS}
210
+ "@
211
+
212
+ # Before any measurement, or every rectangle is in the wrong coordinate space.
213
+ [MwCapture]::Dpi() | Out-Null
214
+
215
+ if ($Mode -eq "host") {
216
+ Write-Output ([MwCapture]::HostWindow())
217
+ exit 0
218
+ }
219
+
220
+ if ($Mode -eq "list") {
221
+ # Which windows are the one this process runs in: walk up from it, stopping at the shell that
222
+ # started the whole session (the desktop, services), and take each console host that belongs to
223
+ # one of those on the way. Every process passed may own the window.
224
+ if ($Owner -gt 0) {
225
+ $procs = Get-CimInstance Win32_Process -Property ProcessId, ParentProcessId, Name
226
+ $parent = @{}
227
+ $name = @{}
228
+ foreach ($p in $procs) { $parent[[int]$p.ProcessId] = [int]$p.ParentProcessId; $name[[int]$p.ProcessId] = ([string]$p.Name).ToLower() }
229
+ $stop = @("explorer.exe", "services.exe", "wininit.exe", "winlogon.exe", "csrss.exe", "svchost.exe", "system")
230
+ $seen = @{}
231
+ $at = $Owner
232
+ while ($at -gt 0 -and -not $seen.ContainsKey($at)) {
233
+ $seen[$at] = $true
234
+ if ($stop -contains $name[$at]) { break }
235
+ [void][MwCapture]::HostPids.Add([uint32]$at)
236
+ $at = $parent[$at]
237
+ }
238
+ foreach ($p in $procs) {
239
+ $n = ([string]$p.Name).ToLower()
240
+ if (($n -eq "conhost.exe" -or $n -eq "openconsole.exe") -and [MwCapture]::HostPids.Contains([uint32]$p.ParentProcessId)) { [void][MwCapture]::HostPids.Add([uint32]$p.ProcessId) }
241
+ }
242
+ }
243
+ [MwCapture]::List("") | ForEach-Object { Write-Output $_ }
244
+ # The windows of apps started for testing, which are never on the screen.
245
+ if ($Hidden -and [MwDesk]::Exists($Hidden)) { Write-Output ([MwCapture]::ListOn($Hidden)) }
246
+ exit 0
247
+ }
248
+
249
+ if ($Mode -eq "capture") {
250
+ $h = if ($Handle -eq "foreground") { [MwCapture]::GetForegroundWindow() } else { [IntPtr][Int64]$Handle }
251
+ if ($h -eq [IntPtr]::Zero) { Write-Output "ERR no such window"; exit 1 }
252
+ $r = [MwCapture]::CaptureOn($Desktop, $h, $Out)
253
+ Write-Output $r
254
+ if ($r.StartsWith("ERR")) { exit 1 }
255
+ exit 0
256
+ }
257
+
258
+ Write-Output "ERR unknown mode"
259
+ exit 1
215
260
  `;
216
261
  let scriptPath = null;
217
262
  /** Write the capture script to a temp file once per process. */
@@ -225,10 +270,12 @@ async function ensureScript() {
225
270
  return path;
226
271
  }
227
272
  /** Run the script in one of its two modes. Rejects with the script's own message. */
228
- async function runScript(args, signal) {
273
+ async function runScript(args, signal, shareConsole = false) {
229
274
  const script = await ensureScript();
230
275
  return await new Promise((resolve, reject) => {
231
- const child = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", script, ...args], { windowsHide: true });
276
+ const child = spawn("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", script, ...args],
277
+ // Hidden, with a console of its own, unless it must see THIS process's console (see hostWindowHandle).
278
+ shareConsole ? { stdio: ["ignore", "pipe", "pipe"] } : { windowsHide: true });
232
279
  let out = "";
233
280
  let err = "";
234
281
  const timer = setTimeout(() => {
@@ -269,11 +316,14 @@ export function parseWindowList(stdout) {
269
316
  if (tab <= 0)
270
317
  continue;
271
318
  const marked = line.slice(0, tab).trim();
272
- const title = line.slice(tab + 1).trim();
319
+ // A third column, "host", marks the window this process runs in.
320
+ const rest = line.slice(tab + 1).split("\t");
321
+ const host = rest.length > 1 && rest[rest.length - 1].trim() === "host";
322
+ const title = (host ? rest.slice(0, -1) : rest).join("\t").trim();
273
323
  const match = new RegExp(`^([*-])(${HIDDEN_MARK}?\\d+)$`).exec(marked);
274
324
  if (!match || !title)
275
325
  continue;
276
- windows.push({ handle: match[2], title, foreground: match[1] === "*" });
326
+ windows.push({ handle: match[2], title, foreground: match[1] === "*", ...(host ? { host: true } : {}) });
277
327
  }
278
328
  return windows;
279
329
  }
@@ -283,7 +333,40 @@ export function parseWindowList(stdout) {
283
333
  * title (a custom title bar) kept and labelled by its process rather than dropped.
284
334
  */
285
335
  export async function listWindows(signal) {
286
- return parseWindowList(await runScript(["-Mode", "list", "-Hidden", hiddenDesktopName()], signal));
336
+ const [raw, host] = await Promise.all([
337
+ runScript(["-Mode", "list", "-Hidden", hiddenDesktopName(), "-Owner", String(process.pid)], signal),
338
+ hostWindowHandle(signal),
339
+ ]);
340
+ const windows = parseWindowList(raw);
341
+ // The terminal's own window: found from the console this process is attached to, which the list cannot see
342
+ // (Windows Terminal is not a parent of the shell, only the owner of its window).
343
+ if (host)
344
+ for (const w of windows)
345
+ if (w.handle === host)
346
+ w.host = true;
347
+ return windows;
348
+ }
349
+ let hostHandleCache;
350
+ /**
351
+ * The window of the terminal this process is typed into, as a handle, or null when it has none (the desktop
352
+ * app) or it cannot be told. Asked once and kept: it does not change while the process runs. The question
353
+ * is put by a child that shares this console rather than making one of its own, since a console is what
354
+ * names the window, and that is only done when this process has a terminal at all, so a program with none
355
+ * is never made to open a window.
356
+ */
357
+ export async function hostWindowHandle(signal) {
358
+ if (hostHandleCache !== undefined)
359
+ return hostHandleCache;
360
+ if (!process.stdout.isTTY && !process.stdin.isTTY)
361
+ return (hostHandleCache = null);
362
+ try {
363
+ const out = (await runScript(["-Mode", "host"], signal, true)).trim().split(/\s+/).pop() ?? "";
364
+ hostHandleCache = /^\d+$/.test(out) ? out : null;
365
+ }
366
+ catch {
367
+ hostHandleCache = null;
368
+ }
369
+ return hostHandleCache;
287
370
  }
288
371
  /** Capture one window to `outPath`. `handle` may be the literal "foreground". */
289
372
  export async function captureWindow(handle, outPath, signal) {