mindweave 3.3.0 → 3.4.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.
Files changed (131) hide show
  1. package/dist/alternator/chassis/lsp.js +1 -1
  2. package/dist/alternator/chassis/lsp.js.map +1 -1
  3. package/dist/alternator/chassis/provision.js +2 -2
  4. package/dist/alternator/chassis/provision.js.map +1 -1
  5. package/dist/cli/App.js +221 -57
  6. package/dist/cli/App.js.map +1 -1
  7. package/dist/cli/attachments.js +2 -1
  8. package/dist/cli/attachments.js.map +1 -1
  9. package/dist/cli/blockHeights.js +14 -0
  10. package/dist/cli/blockHeights.js.map +1 -1
  11. package/dist/cli/components/BlockView.js +9 -1
  12. package/dist/cli/components/BlockView.js.map +1 -1
  13. package/dist/cli/components/EditGroup.js +50 -0
  14. package/dist/cli/components/EditGroup.js.map +1 -0
  15. package/dist/cli/components/FadeText.js +47 -0
  16. package/dist/cli/components/FadeText.js.map +1 -0
  17. package/dist/cli/components/PromptInput.js +26 -4
  18. package/dist/cli/components/PromptInput.js.map +1 -1
  19. package/dist/cli/components/ToolLine.js +5 -3
  20. package/dist/cli/components/ToolLine.js.map +1 -1
  21. package/dist/cli/components/WorkGroup.js +34 -23
  22. package/dist/cli/components/WorkGroup.js.map +1 -1
  23. package/dist/cli/diffRows.js +16 -0
  24. package/dist/cli/diffRows.js.map +1 -1
  25. package/dist/cli/editGroup.js +122 -0
  26. package/dist/cli/editGroup.js.map +1 -0
  27. package/dist/cli/entrance.js +95 -0
  28. package/dist/cli/entrance.js.map +1 -0
  29. package/dist/cli/framebuffer/parse.js +99 -16
  30. package/dist/cli/framebuffer/parse.js.map +1 -1
  31. package/dist/cli/framebuffer/writer.js +46 -28
  32. package/dist/cli/framebuffer/writer.js.map +1 -1
  33. package/dist/cli/help.js +6 -0
  34. package/dist/cli/help.js.map +1 -1
  35. package/dist/cli/inkSpeedLoader.js +9 -5
  36. package/dist/cli/inkSpeedLoader.js.map +1 -1
  37. package/dist/cli/inkThrottlePatch.js +43 -0
  38. package/dist/cli/inkThrottlePatch.js.map +1 -0
  39. package/dist/cli/screenMode.js +8 -1
  40. package/dist/cli/screenMode.js.map +1 -1
  41. package/dist/cli/sessionCarry.js.map +1 -1
  42. package/dist/cli/toolDisplay.js +2 -2
  43. package/dist/cli/toolDisplay.js.map +1 -1
  44. package/dist/cli/transcript.js +67 -14
  45. package/dist/cli/transcript.js.map +1 -1
  46. package/dist/cli/workLines.js +27 -4
  47. package/dist/cli/workLines.js.map +1 -1
  48. package/dist/core/turnRunner.js +98 -7
  49. package/dist/core/turnRunner.js.map +1 -1
  50. package/dist/drivers/anthropic/client.js +16 -3
  51. package/dist/drivers/anthropic/client.js.map +1 -1
  52. package/dist/drivers/anthropic/manifest.js +10 -0
  53. package/dist/drivers/anthropic/manifest.js.map +1 -1
  54. package/dist/drivers/glm/manifest.js +9 -0
  55. package/dist/drivers/glm/manifest.js.map +1 -1
  56. package/dist/drivers/groq/manifest.js +5 -0
  57. package/dist/drivers/groq/manifest.js.map +1 -1
  58. package/dist/drivers/mistral/client.js +3 -0
  59. package/dist/drivers/mistral/client.js.map +1 -1
  60. package/dist/drivers/openai/client.js +3 -0
  61. package/dist/drivers/openai/client.js.map +1 -1
  62. package/dist/drivers/openai/manifest.js +5 -0
  63. package/dist/drivers/openai/manifest.js.map +1 -1
  64. package/dist/drivers/openaiCompat/wire.js +7 -2
  65. package/dist/drivers/openaiCompat/wire.js.map +1 -1
  66. package/dist/drivers/openrouter/client.js +6 -0
  67. package/dist/drivers/openrouter/client.js.map +1 -1
  68. package/dist/drivers/xai/client.js +9 -0
  69. package/dist/drivers/xai/client.js.map +1 -1
  70. package/dist/dynamo/cacheBreak.js +8 -5
  71. package/dist/dynamo/cacheBreak.js.map +1 -1
  72. package/dist/dynamo/contextWindow.js +64 -12
  73. package/dist/dynamo/contextWindow.js.map +1 -1
  74. package/dist/dynamo/engine.js +132 -66
  75. package/dist/dynamo/engine.js.map +1 -1
  76. package/dist/dynamo/prompt.js +8 -2
  77. package/dist/dynamo/prompt.js.map +1 -1
  78. package/dist/dynamo/toolAliases.js +73 -0
  79. package/dist/dynamo/toolAliases.js.map +1 -0
  80. package/dist/dynamo/toolArgs.js +82 -0
  81. package/dist/dynamo/toolArgs.js.map +1 -0
  82. package/dist/dynamo/verify.js +16 -2
  83. package/dist/dynamo/verify.js.map +1 -1
  84. package/dist/memory/clearedArchive.js +2 -20
  85. package/dist/memory/clearedArchive.js.map +1 -1
  86. package/dist/memory/compaction.js +3 -1
  87. package/dist/memory/compaction.js.map +1 -1
  88. package/dist/memory/localCommand.js +30 -3
  89. package/dist/memory/localCommand.js.map +1 -1
  90. package/dist/memory/presence.js +44 -0
  91. package/dist/memory/presence.js.map +1 -1
  92. package/dist/memory/session.js +4 -0
  93. package/dist/memory/session.js.map +1 -1
  94. package/dist/memory/store.js +27 -4
  95. package/dist/memory/store.js.map +1 -1
  96. package/dist/memory/types.js.map +1 -1
  97. package/dist/memory/userNotes.js +17 -0
  98. package/dist/memory/userNotes.js.map +1 -0
  99. package/dist/tools/backgroundShells.js +38 -7
  100. package/dist/tools/backgroundShells.js.map +1 -1
  101. package/dist/tools/deferredNative.js +24 -0
  102. package/dist/tools/deferredNative.js.map +1 -1
  103. package/dist/tools/edit.js +18 -3
  104. package/dist/tools/edit.js.map +1 -1
  105. package/dist/tools/editCore.js +107 -1
  106. package/dist/tools/editCore.js.map +1 -1
  107. package/dist/tools/mcpSearch.js +8 -2
  108. package/dist/tools/mcpSearch.js.map +1 -1
  109. package/dist/tools/readFile.js +16 -1
  110. package/dist/tools/readFile.js.map +1 -1
  111. package/dist/tools/rebuildDetail.js +94 -3
  112. package/dist/tools/rebuildDetail.js.map +1 -1
  113. package/dist/tools/registry.js +12 -12
  114. package/dist/tools/registry.js.map +1 -1
  115. package/dist/tools/runCommand.js +10 -3
  116. package/dist/tools/runCommand.js.map +1 -1
  117. package/dist/tools/screenshot.js +42 -3
  118. package/dist/tools/screenshot.js.map +1 -1
  119. package/dist/tools/screenshotWin.js +221 -216
  120. package/dist/tools/screenshotWin.js.map +1 -1
  121. package/dist/tools/search.js +4 -1
  122. package/dist/tools/search.js.map +1 -1
  123. package/dist/tools/shellTools.js +17 -25
  124. package/dist/tools/shellTools.js.map +1 -1
  125. package/dist/tools/ui.js +26 -9
  126. package/dist/tools/ui.js.map +1 -1
  127. package/dist/tools/uiWin.js +4 -1
  128. package/dist/tools/uiWin.js.map +1 -1
  129. package/dist/tools/untrusted.js +24 -0
  130. package/dist/tools/untrusted.js.map +1 -1
  131. package/package.json +78 -78
@@ -42,221 +42,225 @@ 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 = "", [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
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
+ # The window list is read back as UTF-8. Without this the console's own code page is used, and every title with a
49
+ # dash, an accent or a CJK character reached the model as "?" (real sessions: "Microsoft? Edge", "? Greeting"),
50
+ # which made the title impossible to copy back into a call.
51
+ try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) } catch {}
52
+ Add-Type -AssemblyName System.Drawing
53
+
54
+ Add-Type -ReferencedAssemblies System.Drawing -TypeDefinition @"
55
+ using System;
56
+ using System.Collections.Generic;
57
+ using System.Drawing;
58
+ using System.Drawing.Imaging;
59
+ using System.Runtime.InteropServices;
60
+ using System.Text;
61
+
62
+ public static class MwCapture {
63
+ // The processes this one runs inside (the terminal, its console host, the app hosting it).
64
+ // Their windows are marked "host" in the list, so the caller can tell them from the app under test.
65
+ public static HashSet<uint> HostPids = new HashSet<uint>();
66
+ delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
67
+ [DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr l);
68
+ [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h);
69
+ [DllImport("user32.dll")] static extern bool IsIconic(IntPtr h);
70
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetWindowTextLength(IntPtr h);
71
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int GetWindowText(IntPtr h, StringBuilder s, int n);
72
+ [DllImport("user32.dll")] static extern bool PrintWindow(IntPtr h, IntPtr hdc, uint flags);
73
+ [DllImport("user32.dll")] static extern bool SetProcessDPIAware();
74
+ [DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr h, out RECT r);
75
+ [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
76
+ [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
77
+ [DllImport("user32.dll")] static extern int GetWindowLong(IntPtr h, int index);
78
+ [DllImport("dwmapi.dll")] static extern int DwmGetWindowAttribute(IntPtr h, int attr, out RECT r, int size);
79
+ [DllImport("dwmapi.dll", EntryPoint = "DwmGetWindowAttribute")] static extern int DwmGetIntAttribute(IntPtr h, int attr, out int v, int size);
80
+
81
+ [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
82
+
83
+ public static void Dpi() { SetProcessDPIAware(); }
84
+
85
+ [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow();
86
+ [DllImport("user32.dll")] static extern IntPtr GetAncestor(IntPtr h, uint flags);
87
+
88
+ // The window the console this process is attached to is drawn in. In Windows Terminal the console
89
+ // window is a hidden stand-in owned by the real top-level window, so the owner is what is returned.
90
+ // Empty when there is no console (the desktop app, a service).
91
+ public static string HostWindow() {
92
+ IntPtr c = GetConsoleWindow();
93
+ if (c == IntPtr.Zero) return "";
94
+ IntPtr root = GetAncestor(c, 3 /* GA_ROOTOWNER */);
95
+ return (root == IntPtr.Zero ? c : root).ToInt64().ToString();
96
+ }
97
+
98
+ // A DWM-cloaked window passes IsWindowVisible but is not actually on screen — the
99
+ // invisible UWP and host windows that would otherwise flood the list. Any failure to
100
+ // read the attribute is treated as not-cloaked, so a window is never dropped on error.
101
+ static bool Cloaked(IntPtr h) {
102
+ int v;
103
+ return DwmGetIntAttribute(h, 14 /* DWMWA_CLOAKED */, out v, sizeof(int)) == 0 && v != 0;
104
+ }
105
+
106
+ // The Shell's own "is this an Alt-Tab window" test: a tool window is a palette or
107
+ // tray surface and is never what "the app" means, unless it also asks for the taskbar.
108
+ static bool AltTabWorthy(IntPtr h) {
109
+ int ex = GetWindowLong(h, -20 /* GWL_EXSTYLE */);
110
+ bool tool = (ex & 0x00000080) != 0; // WS_EX_TOOLWINDOW
111
+ bool app = (ex & 0x00040000) != 0; // WS_EX_APPWINDOW
112
+ return !tool || app;
113
+ }
114
+
115
+ // The window's process name, for a window that has no title of its own. Wrapped
116
+ // because the process can exit between the enum and the lookup.
117
+ static string ProcName(IntPtr h) {
118
+ try {
119
+ uint pid;
120
+ GetWindowThreadProcessId(h, out pid);
121
+ if (pid == 0) return "";
122
+ using (var p = System.Diagnostics.Process.GetProcessById((int)pid)) return p.ProcessName;
123
+ } catch { return ""; }
124
+ }
125
+
126
+ // DWMWA_EXTENDED_FRAME_BOUNDS. GetWindowRect includes an invisible border on
127
+ // Windows 10+, so it is the fallback rather than the first choice.
128
+ static bool Bounds(IntPtr h, out RECT r) {
129
+ if (DwmGetWindowAttribute(h, 9, out r, Marshal.SizeOf(typeof(RECT))) == 0
130
+ && r.Right > r.Left && r.Bottom > r.Top) return true;
131
+ return GetWindowRect(h, out r);
132
+ }
133
+
134
+ // The hidden desktop's windows are marked, so a capture is sent back there.
135
+ public static string ListOn(string desk) {
136
+ return MwDesk.On(desk, () => string.Join("\n", List("${HIDDEN_MARK}").ToArray()));
137
+ }
138
+
139
+ public static List<string> List(string mark) {
140
+ var found = new List<string>();
141
+ IntPtr fg = GetForegroundWindow();
142
+ EnumWindows(delegate(IntPtr h, IntPtr l) {
143
+ if (!IsWindowVisible(h) || IsIconic(h)) return true;
144
+ if (Cloaked(h) || !AltTabWorthy(h)) return true;
145
+ RECT r;
146
+ if (!Bounds(h, out r)) return true;
147
+ // Tool windows and tray hosts are real but never what anyone means.
148
+ if (r.Right - r.Left < 120 || r.Bottom - r.Top < 120) return true;
149
+ int len = GetWindowTextLength(h);
150
+ string title = "";
151
+ if (len > 0) {
152
+ var sb = new StringBuilder(len + 1);
153
+ GetWindowText(h, sb, sb.Capacity);
154
+ title = sb.ToString().Trim();
155
+ }
156
+ // An empty title is NOT a reason to drop the window. A custom title bar
157
+ // (Tauri or Electron with decorations off) leaves the OS title blank, as does
158
+ // an app whose web content has not set document.title yet — and dropping it
159
+ // makes a window the user is plainly looking at impossible to capture, by name
160
+ // OR as the foreground. Label it by its process so it stays nameable.
161
+ if (title.Length == 0) {
162
+ string pn = ProcName(h);
163
+ title = pn.Length > 0 ? pn : "(untitled window)";
164
+ }
165
+ // The leading marker is how the caller knows which window the user is
166
+ // actually looking at, so it can name it when asking permission.
167
+ uint wpid; GetWindowThreadProcessId(h, out wpid);
168
+ string hostCol = mark.Length == 0 && HostPids.Contains(wpid) ? "\thost" : "";
169
+ found.Add((h == fg && mark.Length == 0 ? "*" : "-") + mark + h.ToInt64().ToString() + "\t" + title + hostCol);
170
+ return true;
171
+ }, IntPtr.Zero);
172
+ return found;
173
+ }
174
+
175
+ public static string CaptureOn(string desk, IntPtr h, string path) {
176
+ return MwDesk.On(desk, () => Capture(h, path, string.IsNullOrEmpty(desk)));
177
+ }
178
+
179
+ // screen: whether a blank PrintWindow may fall back to copying the screen. Never for a
180
+ // window on the hidden desktop: the screen is the USER's, and that window is not on it.
181
+ public static string Capture(IntPtr h, string path, bool screen) {
182
+ RECT r;
183
+ if (!Bounds(h, out r)) return "ERR could not measure the window";
184
+ int w = r.Right - r.Left, ht = r.Bottom - r.Top;
185
+ if (w <= 0 || ht <= 0) return "ERR the window has no visible area";
186
+ using (var bmp = new Bitmap(w, ht)) {
187
+ bool drew = false;
188
+ using (var g = Graphics.FromImage(bmp)) {
189
+ IntPtr hdc = g.GetHdc();
190
+ drew = PrintWindow(h, hdc, 2); // PW_RENDERFULLCONTENT
191
+ g.ReleaseHdc(hdc);
192
+ }
193
+ if (!drew || Blank(bmp)) {
194
+ if (!screen) return "ERR the window drew nothing to capture";
195
+ using (var g = Graphics.FromImage(bmp)) {
196
+ g.CopyFromScreen(r.Left, r.Top, 0, 0, new Size(w, ht));
197
+ }
198
+ }
199
+ bmp.Save(path, ImageFormat.Png);
200
+ return "OK " + w + " " + ht;
201
+ }
202
+ }
203
+
204
+ // PrintWindow can report success and draw nothing. Sampling three corners of a
205
+ // fully transparent bitmap is enough to tell that apart from a real capture.
206
+ static bool Blank(Bitmap b) {
207
+ if (b.Width < 4 || b.Height < 4) return false;
208
+ return b.GetPixel(1, 1).A == 0
209
+ && b.GetPixel(b.Width / 2, b.Height / 2).A == 0
210
+ && b.GetPixel(b.Width - 2, b.Height - 2).A == 0;
211
+ }
212
+ }
213
+ ${DESK_CS}
214
+ "@
215
+
216
+ # Before any measurement, or every rectangle is in the wrong coordinate space.
217
+ [MwCapture]::Dpi() | Out-Null
218
+
219
+ if ($Mode -eq "host") {
220
+ Write-Output ([MwCapture]::HostWindow())
221
+ exit 0
222
+ }
223
+
224
+ if ($Mode -eq "list") {
225
+ # Which windows are the one this process runs in: walk up from it, stopping at the shell that
226
+ # started the whole session (the desktop, services), and take each console host that belongs to
227
+ # one of those on the way. Every process passed may own the window.
228
+ if ($Owner -gt 0) {
229
+ $procs = Get-CimInstance Win32_Process -Property ProcessId, ParentProcessId, Name
230
+ $parent = @{}
231
+ $name = @{}
232
+ foreach ($p in $procs) { $parent[[int]$p.ProcessId] = [int]$p.ParentProcessId; $name[[int]$p.ProcessId] = ([string]$p.Name).ToLower() }
233
+ $stop = @("explorer.exe", "services.exe", "wininit.exe", "winlogon.exe", "csrss.exe", "svchost.exe", "system")
234
+ $seen = @{}
235
+ $at = $Owner
236
+ while ($at -gt 0 -and -not $seen.ContainsKey($at)) {
237
+ $seen[$at] = $true
238
+ if ($stop -contains $name[$at]) { break }
239
+ [void][MwCapture]::HostPids.Add([uint32]$at)
240
+ $at = $parent[$at]
241
+ }
242
+ foreach ($p in $procs) {
243
+ $n = ([string]$p.Name).ToLower()
244
+ if (($n -eq "conhost.exe" -or $n -eq "openconsole.exe") -and [MwCapture]::HostPids.Contains([uint32]$p.ParentProcessId)) { [void][MwCapture]::HostPids.Add([uint32]$p.ProcessId) }
245
+ }
246
+ }
247
+ [MwCapture]::List("") | ForEach-Object { Write-Output $_ }
248
+ # The windows of apps started for testing, which are never on the screen.
249
+ if ($Hidden -and [MwDesk]::Exists($Hidden)) { Write-Output ([MwCapture]::ListOn($Hidden)) }
250
+ exit 0
251
+ }
252
+
253
+ if ($Mode -eq "capture") {
254
+ $h = if ($Handle -eq "foreground") { [MwCapture]::GetForegroundWindow() } else { [IntPtr][Int64]$Handle }
255
+ if ($h -eq [IntPtr]::Zero) { Write-Output "ERR no such window"; exit 1 }
256
+ $r = [MwCapture]::CaptureOn($Desktop, $h, $Out)
257
+ Write-Output $r
258
+ if ($r.StartsWith("ERR")) { exit 1 }
259
+ exit 0
260
+ }
261
+
262
+ Write-Output "ERR unknown mode"
263
+ exit 1
260
264
  `;
261
265
  let scriptPath = null;
262
266
  /** Write the capture script to a temp file once per process. */
@@ -284,7 +288,8 @@ async function runScript(args, signal, shareConsole = false) {
284
288
  }, PS_TIMEOUT_MS);
285
289
  const onAbort = () => child.kill();
286
290
  signal?.addEventListener("abort", onAbort, { once: true });
287
- child.stdout.on("data", (d) => (out += d.toString()));
291
+ child.stdout.setEncoding("utf8"); // a character split across two chunks is joined, not broken
292
+ child.stdout.on("data", (d) => (out += d));
288
293
  child.stderr.on("data", (d) => (err += d.toString()));
289
294
  child.on("error", (e) => {
290
295
  clearTimeout(timer);
@@ -1 +1 @@
1
- {"version":3,"file":"screenshotWin.js","sourceRoot":"","sources":["../../src/tools/screenshotWin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenshotWin.js","sourceRoot":"","sources":["../../src/tools/screenshotWin.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { FULL_DETAIL_MAX, capLines } from "./detail.js";
1
2
  import { grepDef, GREP_MAX_OUTPUT_LINES } from "./grep.js";
2
3
  import { globDef, GLOB_MAX_RESULTS } from "./glob.js";
3
4
  import { listDir, MAX_ENTRIES } from "./listDir.js";
@@ -115,7 +116,9 @@ export const search = {
115
116
  // Quiet is applied HERE, on every path including errors, rather than inside either
116
117
  // implementation. One tool, one display policy — and a failed search is exactly the
117
118
  // row most likely to creep back onto the screen if each branch decided for itself.
118
- return { ...result, quiet: true };
119
+ // The words behind the row's click: what the search printed, which the model got whole.
120
+ const printed = result.isError || result.detail ? undefined : capLines(result.output.split("\n"), FULL_DETAIL_MAX);
121
+ return { ...result, quiet: true, ...(printed ? { detail: printed, detailKind: "text" } : {}) };
119
122
  },
120
123
  };
121
124
  //# sourceMappingURL=search.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { relativize } from "./paths.js";
2
2
  import { MAX_READ_CHARS } from "./backgroundShells.js";
3
3
  import { fail, failQuietly } from "./results.js";
4
+ import { COMMAND_OUTPUT_NOTE, commandReachesOutside } from "./untrusted.js";
4
5
  /**
5
6
  * One read-only tool at two levels of specificity: no `id` lists, an `id` reads.
6
7
  *
@@ -19,24 +20,12 @@ import { fail, failQuietly } from "./results.js";
19
20
  export const shellsTool = {
20
21
  name: "shells",
21
22
  readOnly: true,
22
- /**
23
- * Advertised only once this session has actually started a background shell.
24
- *
25
- * Nothing is lost by hiding it before then: with no shells there is nothing to list
26
- * and nothing to read, so the tool can only be called to be told so. It is worth
27
- * roughly 600 tokens of standing prompt across this tool and `kill_shell`, paid on
28
- * every uncached request of every session — including the many that never background
29
- * anything.
30
- *
31
- * The condition is `list()`, which retains FINISHED shells, and not `running()`.
32
- * That is deliberate and is about the prompt cache rather than about correctness: the
33
- * tool list is part of the cached prefix, so a condition that flipped off again when
34
- * the last process exited would invalidate the whole prefix twice per shell instead
35
- * of once per session. `list()` only ever goes from empty to non-empty, so the cost
36
- * is a single rebuild the first time a shell appears. Reading a finished shell's
37
- * output is a real use anyway.
38
- */
39
- relevantWhen: (ctx) => (ctx.backgroundShells?.list().length ?? 0) > 0,
23
+ // Always advertised. The tool list is the first thing a provider hashes for its prompt cache,
24
+ // so a tool that appears when the first background shell starts re-bills the system prompt
25
+ // and the whole conversation at full price at that moment. Recorded sessions lost 35k
26
+ // tokens per occurrence on average, and about half of them start a background command.
27
+ // This tool and `kill_shell` weigh roughly 600 tokens, read from the cache after the first
28
+ // request, which costs far less than one rebuild.
40
29
  // The description never mentioned polling, while the tool itself pushes a strong
41
30
  // don't-poll nudge at runtime; the two now agree. It also never mentioned either cap,
42
31
  // and the buffer one matters: a chatty process can roll output out of the retained
@@ -80,8 +69,13 @@ export const shellsTool = {
80
69
  if (id === null)
81
70
  return failQuietly("`id` must be a shell number.");
82
71
  const res = await mgr.read(id);
83
- if (!res)
84
- return fail(`no background shell #${id}.`);
72
+ if (!res) {
73
+ // Say what DOES exist: the model usually has a stale number (shells do not survive a restart or /continue).
74
+ const known = mgr.list();
75
+ return fail(known.length === 0
76
+ ? `no background shell #${id}. None exist in this session (they do not survive a restart or /continue), so start the command again with run_in_background.`
77
+ : `no background shell #${id}. The shells that exist: ${known.map((s) => `#${s.id}`).join(", ")}.`);
78
+ }
85
79
  const head = statusLine(res.info);
86
80
  const body = res.chunk.trim();
87
81
  // If it's still running, stop the model polling: the event is pushed to it, so a
@@ -99,7 +93,8 @@ export const shellsTool = {
99
93
  : "\n[Still running. You'll be notified automatically when it finishes — do NOT poll again. " +
100
94
  "End your turn and let the user know you'll report when it's done.]";
101
95
  return {
102
- output: (body ? `${head}\n${body}` : `${head}\n(no new output)`) + nudge,
96
+ output: (body ? `${head}\n${body}${commandReachesOutside(res.info.command) ? `\n${COMMAND_OUTPUT_NOTE}` : ""}` : `${head}\n(no new output)`) +
97
+ nudge,
103
98
  summary: `shell #${id} (${res.info.status})`,
104
99
  };
105
100
  },
@@ -107,10 +102,7 @@ export const shellsTool = {
107
102
  export const killShell = {
108
103
  name: "kill_shell",
109
104
  readOnly: false,
110
- /** Same gate as `shells`, and for the same reason — including using `list()` rather
111
- * than `running()` so the tool list latches once per session instead of flipping
112
- * with every process that starts and stops. */
113
- relevantWhen: (ctx) => (ctx.backgroundShells?.list().length ?? 0) > 0,
105
+ // Always advertised, for the same reason as `shells`.
114
106
  description: "Stop a running background shell by id. Kills the whole process tree, not just the " +
115
107
  "shell, so a dev server that spawned its own children goes down with it. " +
116
108
  "It records that YOU stopped it, which is what later lets the status be reported as " +