codeloop-mcp-server 0.1.52 → 0.1.54
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.
- package/dist/auth/critical_floors.d.ts.map +1 -1
- package/dist/auth/critical_floors.js +8 -0
- package/dist/auth/critical_floors.js.map +1 -1
- package/dist/auth/update_check.d.ts.map +1 -1
- package/dist/auth/update_check.js +19 -1
- package/dist/auth/update_check.js.map +1 -1
- package/dist/evidence/anti_rationalisation.d.ts.map +1 -1
- package/dist/evidence/anti_rationalisation.js +15 -0
- package/dist/evidence/anti_rationalisation.js.map +1 -1
- package/dist/evidence/binary_freshness.d.ts +21 -0
- package/dist/evidence/binary_freshness.d.ts.map +1 -0
- package/dist/evidence/binary_freshness.js +113 -0
- package/dist/evidence/binary_freshness.js.map +1 -0
- package/dist/evidence/change_coverage.d.ts.map +1 -1
- package/dist/evidence/change_coverage.js +22 -1
- package/dist/evidence/change_coverage.js.map +1 -1
- package/dist/evidence/cycle_issues.d.ts +99 -0
- package/dist/evidence/cycle_issues.d.ts.map +1 -0
- package/dist/evidence/cycle_issues.js +120 -0
- package/dist/evidence/cycle_issues.js.map +1 -0
- package/dist/evidence/evidence_freshness.d.ts +39 -0
- package/dist/evidence/evidence_freshness.d.ts.map +1 -0
- package/dist/evidence/evidence_freshness.js +231 -0
- package/dist/evidence/evidence_freshness.js.map +1 -0
- package/dist/evidence/interaction_coverage.d.ts +15 -0
- package/dist/evidence/interaction_coverage.d.ts.map +1 -1
- package/dist/evidence/interaction_coverage.js +53 -4
- package/dist/evidence/interaction_coverage.js.map +1 -1
- package/dist/evidence/screenshot_diff.d.ts.map +1 -1
- package/dist/evidence/screenshot_diff.js +30 -12
- package/dist/evidence/screenshot_diff.js.map +1 -1
- package/dist/index.js +197 -4
- package/dist/index.js.map +1 -1
- package/dist/runners/modal_close_strategies.d.ts +82 -0
- package/dist/runners/modal_close_strategies.d.ts.map +1 -0
- package/dist/runners/modal_close_strategies.js +226 -0
- package/dist/runners/modal_close_strategies.js.map +1 -0
- package/dist/runners/modal_detector.d.ts +17 -0
- package/dist/runners/modal_detector.d.ts.map +1 -1
- package/dist/runners/modal_detector.js +95 -22
- package/dist/runners/modal_detector.js.map +1 -1
- package/dist/tools/design_compare.d.ts.map +1 -1
- package/dist/tools/design_compare.js +22 -3
- package/dist/tools/design_compare.js.map +1 -1
- package/dist/tools/gate_check.d.ts.map +1 -1
- package/dist/tools/gate_check.js +188 -14
- package/dist/tools/gate_check.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type ModalDetectionResult } from "./modal_detector.js";
|
|
2
|
+
/**
|
|
3
|
+
* 0.1.54 E1 — Multi-strategy modal close.
|
|
4
|
+
*
|
|
5
|
+
* Photometry-DB E2E #11 transcript shipped at 100% confidence after the
|
|
6
|
+
* agent had to HARD-RESTART the app process to drain a stack of three
|
|
7
|
+
* .NET 8 OpenFolderDialog instances that ignored every close attempt
|
|
8
|
+
* codeloop_handle_modal made (`WM_CLOSE` / `SC_CLOSE` / focused-Escape).
|
|
9
|
+
* The pre-0.1.54 handler only sent `Enter` / `Escape` keystrokes — when
|
|
10
|
+
* a Win32 file dialog stops responding to those (which it does after
|
|
11
|
+
* being stacked), the loop had no fallback path.
|
|
12
|
+
*
|
|
13
|
+
* This module ships a 4-step ladder, executed in order with re-detection
|
|
14
|
+
* after each step. Stops as soon as detectModal returns is_modal_present:
|
|
15
|
+
* false. Returns the full sequence of attempts so the agent (and the
|
|
16
|
+
* cycle_issues evidence channel from E2) can audit which strategy worked
|
|
17
|
+
* or whether the kill-window escalation is required.
|
|
18
|
+
*
|
|
19
|
+
* Strategies:
|
|
20
|
+
* 1. focused Escape (current 0.1.51 behaviour — works for most simple
|
|
21
|
+
* WPF / WinForms confirmation dialogs).
|
|
22
|
+
* 2. Alt+F4 against the foreground window — closes file dialogs that
|
|
23
|
+
* have rejected Escape because they're waiting on a button click.
|
|
24
|
+
* 3. UIA InvokePattern on the dialog's "Close" button — handles file
|
|
25
|
+
* dialogs whose top-right X is technically a button, not a window
|
|
26
|
+
* caption click.
|
|
27
|
+
* 4. Win32 EndDialog(hwnd, IDCANCEL) via Add-Type — last resort before
|
|
28
|
+
* kill-window. Works for true MODAL dialogs even when the message
|
|
29
|
+
* pump is wedged.
|
|
30
|
+
*
|
|
31
|
+
* If all 4 fail the caller surfaces the kill-window escalation; the
|
|
32
|
+
* separate `codeloop_kill_modal_window` tool sends WM_CLOSE to the
|
|
33
|
+
* specific HWND, then TerminateProcess if the dialog is still up
|
|
34
|
+
* after a 2 s grace period.
|
|
35
|
+
*/
|
|
36
|
+
export type StrategyName = "escape" | "alt_f4" | "uia_invoke_close" | "end_dialog";
|
|
37
|
+
export interface StrategyAttempt {
|
|
38
|
+
strategy: StrategyName;
|
|
39
|
+
success: boolean;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface MultiStrategyCloseResult {
|
|
43
|
+
closed: boolean;
|
|
44
|
+
strategies_tried: StrategyAttempt[];
|
|
45
|
+
/** Modal description as reported by the LAST detectModal probe. */
|
|
46
|
+
final_detection: ModalDetectionResult;
|
|
47
|
+
/** When closed=false, the agent should escalate via codeloop_kill_modal_window. */
|
|
48
|
+
escalation: "none" | "kill_window_required";
|
|
49
|
+
/** HWND captured at first detection, used by kill-window escalation. */
|
|
50
|
+
hwnd?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Run the strategy ladder against the detected modal. Re-detects after
|
|
54
|
+
* each step. Caller is expected to have already called detectModal and
|
|
55
|
+
* confirmed `is_modal_present: true`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function closeModalWithStrategies(opts: {
|
|
58
|
+
initial_detection: ModalDetectionResult;
|
|
59
|
+
app_name?: string;
|
|
60
|
+
cwd: string;
|
|
61
|
+
}): Promise<MultiStrategyCloseResult>;
|
|
62
|
+
/**
|
|
63
|
+
* 0.1.54 E1 — last-resort kill: PostMessage(WM_CLOSE) to the HWND, wait
|
|
64
|
+
* 2 s, re-detect, and if the modal is still up TerminateProcess on the
|
|
65
|
+
* owning process. Used by codeloop_kill_modal_window when the strategy
|
|
66
|
+
* ladder gives up.
|
|
67
|
+
*/
|
|
68
|
+
export interface KillModalWindowResult {
|
|
69
|
+
closed: boolean;
|
|
70
|
+
steps: Array<{
|
|
71
|
+
step: "post_wm_close" | "terminate_process";
|
|
72
|
+
success: boolean;
|
|
73
|
+
error?: string;
|
|
74
|
+
}>;
|
|
75
|
+
final_detection: ModalDetectionResult;
|
|
76
|
+
}
|
|
77
|
+
export declare function killModalWindow(opts: {
|
|
78
|
+
hwnd: string;
|
|
79
|
+
app_name?: string;
|
|
80
|
+
cwd: string;
|
|
81
|
+
}): Promise<KillModalWindowResult>;
|
|
82
|
+
//# sourceMappingURL=modal_close_strategies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modal_close_strategies.d.ts","sourceRoot":"","sources":["../../src/runners/modal_close_strategies.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,kBAAkB,GAAG,YAAY,CAAC;AAEnF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,mEAAmE;IACnE,eAAe,EAAE,oBAAoB,CAAC;IACtC,mFAAmF;IACnF,UAAU,EAAE,MAAM,GAAG,sBAAsB,CAAC;IAC5C,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAoGD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,iBAAiB,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAyEpC;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,eAAe,GAAG,mBAAmB,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChG,eAAe,EAAE,oBAAoB,CAAC;CACvC;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqEjC"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { runCommand } from "./base.js";
|
|
2
|
+
import { detectModal } from "./modal_detector.js";
|
|
3
|
+
const RE_DETECT_DELAY_MS = 400;
|
|
4
|
+
async function sleep(ms) {
|
|
5
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6
|
+
}
|
|
7
|
+
async function pressEscapeWindows(cwd) {
|
|
8
|
+
// SendKeys-based — focused window receives Escape. Mirrors what
|
|
9
|
+
// codeloop_interact action="keystroke", key="escape" does.
|
|
10
|
+
const script = [
|
|
11
|
+
"Add-Type -AssemblyName System.Windows.Forms",
|
|
12
|
+
"[System.Windows.Forms.SendKeys]::SendWait('{ESC}')",
|
|
13
|
+
].join("\n");
|
|
14
|
+
const r = await runCommand("powershell", ["-NoProfile", "-Command", script], cwd, undefined, undefined, 5000);
|
|
15
|
+
return r.exit_code === 0 ? { ok: true } : { ok: false, err: r.stderr.slice(0, 200) };
|
|
16
|
+
}
|
|
17
|
+
async function pressAltF4Windows(cwd) {
|
|
18
|
+
const script = [
|
|
19
|
+
"Add-Type -AssemblyName System.Windows.Forms",
|
|
20
|
+
"[System.Windows.Forms.SendKeys]::SendWait('%{F4}')",
|
|
21
|
+
].join("\n");
|
|
22
|
+
const r = await runCommand("powershell", ["-NoProfile", "-Command", script], cwd, undefined, undefined, 5000);
|
|
23
|
+
return r.exit_code === 0 ? { ok: true } : { ok: false, err: r.stderr.slice(0, 200) };
|
|
24
|
+
}
|
|
25
|
+
async function uiaInvokeCloseButtonWindows(cwd, hwnd) {
|
|
26
|
+
// Walk the modal window's children looking for a button named "Close",
|
|
27
|
+
// "Cancel", or "X". When hwnd is provided we can target the dialog
|
|
28
|
+
// directly via FromHandle; otherwise fall back to the foreground
|
|
29
|
+
// window via NativeWindowHandle property.
|
|
30
|
+
const handleArg = hwnd ?? "$null";
|
|
31
|
+
const script = [
|
|
32
|
+
"Add-Type -AssemblyName UIAutomationClient",
|
|
33
|
+
`$hwnd = ${handleArg}`,
|
|
34
|
+
"if ($hwnd -ne $null -and $hwnd -ne 0) {",
|
|
35
|
+
" $w = [System.Windows.Automation.AutomationElement]::FromHandle([IntPtr]$hwnd)",
|
|
36
|
+
"} else {",
|
|
37
|
+
" $root = [System.Windows.Automation.AutomationElement]::RootElement",
|
|
38
|
+
" $cond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ControlTypeProperty, [System.Windows.Automation.ControlType]::Window)",
|
|
39
|
+
" $w = $root.FindFirst([System.Windows.Automation.TreeScope]::Children, $cond)",
|
|
40
|
+
"}",
|
|
41
|
+
"if ($w -eq $null) { Write-Error 'no window found'; exit 1 }",
|
|
42
|
+
"$btnCond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ControlTypeProperty, [System.Windows.Automation.ControlType]::Button)",
|
|
43
|
+
"$buttons = $w.FindAll([System.Windows.Automation.TreeScope]::Subtree, $btnCond)",
|
|
44
|
+
"$invoked = $false",
|
|
45
|
+
"foreach ($b in $buttons) {",
|
|
46
|
+
" $name = $b.Current.Name",
|
|
47
|
+
" if ($name -match '^(Close|Cancel|X|Dismiss)$') {",
|
|
48
|
+
" try {",
|
|
49
|
+
" $invoke = $b.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)",
|
|
50
|
+
" $invoke.Invoke()",
|
|
51
|
+
" $invoked = $true",
|
|
52
|
+
" break",
|
|
53
|
+
" } catch { }",
|
|
54
|
+
" }",
|
|
55
|
+
"}",
|
|
56
|
+
"if (-not $invoked) { Write-Error 'no close button found'; exit 1 }",
|
|
57
|
+
].join("\n");
|
|
58
|
+
const r = await runCommand("powershell", ["-NoProfile", "-Command", script], cwd, undefined, undefined, 8000);
|
|
59
|
+
return r.exit_code === 0 ? { ok: true } : { ok: false, err: r.stderr.slice(0, 200) };
|
|
60
|
+
}
|
|
61
|
+
async function endDialogWindows(cwd, hwnd) {
|
|
62
|
+
if (!hwnd) {
|
|
63
|
+
return { ok: false, err: "EndDialog requires hwnd; detector did not capture one" };
|
|
64
|
+
}
|
|
65
|
+
// EndDialog Win32 call. Falls back to PostMessage(WM_CLOSE) when the
|
|
66
|
+
// window isn't a true dialog (EndDialog returns FALSE in that case but
|
|
67
|
+
// PostMessage can still terminate it).
|
|
68
|
+
const script = [
|
|
69
|
+
"Add-Type @\"",
|
|
70
|
+
"using System;",
|
|
71
|
+
"using System.Runtime.InteropServices;",
|
|
72
|
+
"public class CL {",
|
|
73
|
+
" [DllImport(\"user32.dll\")] public static extern bool EndDialog(IntPtr hDlg, IntPtr nResult);",
|
|
74
|
+
" [DllImport(\"user32.dll\")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);",
|
|
75
|
+
"}",
|
|
76
|
+
"\"@",
|
|
77
|
+
`$hwnd = [IntPtr]${hwnd}`,
|
|
78
|
+
"$ok = [CL]::EndDialog($hwnd, [IntPtr]2)",
|
|
79
|
+
// 2 == IDCANCEL
|
|
80
|
+
"if (-not $ok) {",
|
|
81
|
+
" $WM_CLOSE = 0x0010",
|
|
82
|
+
" $ok2 = [CL]::PostMessage($hwnd, $WM_CLOSE, [IntPtr]::Zero, [IntPtr]::Zero)",
|
|
83
|
+
" if (-not $ok2) { Write-Error 'EndDialog and PostMessage(WM_CLOSE) both failed'; exit 1 }",
|
|
84
|
+
"}",
|
|
85
|
+
].join("\n");
|
|
86
|
+
const r = await runCommand("powershell", ["-NoProfile", "-Command", script], cwd, undefined, undefined, 5000);
|
|
87
|
+
return r.exit_code === 0 ? { ok: true } : { ok: false, err: r.stderr.slice(0, 200) };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Run the strategy ladder against the detected modal. Re-detects after
|
|
91
|
+
* each step. Caller is expected to have already called detectModal and
|
|
92
|
+
* confirmed `is_modal_present: true`.
|
|
93
|
+
*/
|
|
94
|
+
export async function closeModalWithStrategies(opts) {
|
|
95
|
+
const { initial_detection: initial, app_name, cwd } = opts;
|
|
96
|
+
const hwnd = initial.hwnd;
|
|
97
|
+
const attempts = [];
|
|
98
|
+
const stepDispatch = {
|
|
99
|
+
escape: () => pressEscapeWindows(cwd),
|
|
100
|
+
alt_f4: () => pressAltF4Windows(cwd),
|
|
101
|
+
uia_invoke_close: () => uiaInvokeCloseButtonWindows(cwd, hwnd),
|
|
102
|
+
end_dialog: () => endDialogWindows(cwd, hwnd),
|
|
103
|
+
};
|
|
104
|
+
// 0.1.54 E1 — strategy order: cheap-and-fast first, expensive last.
|
|
105
|
+
// For confirm/alert kinds we don't really need Alt+F4 or EndDialog
|
|
106
|
+
// (they bias toward 'confirm' rather than 'cancel'), but the ladder
|
|
107
|
+
// is still safe to run because we re-detect between steps.
|
|
108
|
+
const ladder = ["escape", "alt_f4", "uia_invoke_close", "end_dialog"];
|
|
109
|
+
let lastDetection = initial;
|
|
110
|
+
for (const strategy of ladder) {
|
|
111
|
+
if (process.platform !== "win32") {
|
|
112
|
+
// Non-Windows desktop modal handling stays at the 0.1.51 single-
|
|
113
|
+
// keystroke behaviour because macOS / Linux modals don't show
|
|
114
|
+
// the same OpenFolderDialog stuck-stack problem. Surface that
|
|
115
|
+
// limitation explicitly so the caller doesn't get confused.
|
|
116
|
+
attempts.push({
|
|
117
|
+
strategy,
|
|
118
|
+
success: false,
|
|
119
|
+
error: "multi-strategy close ladder is Windows-only; macOS/Linux fall back to keystroke",
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
const r = await stepDispatch[strategy]();
|
|
124
|
+
attempts.push({
|
|
125
|
+
strategy,
|
|
126
|
+
success: r.ok,
|
|
127
|
+
error: r.err,
|
|
128
|
+
});
|
|
129
|
+
if (!r.ok)
|
|
130
|
+
continue;
|
|
131
|
+
// Re-detect. If the modal is gone, we're done.
|
|
132
|
+
await sleep(RE_DETECT_DELAY_MS);
|
|
133
|
+
lastDetection = await detectModal({
|
|
134
|
+
target_type: "desktop",
|
|
135
|
+
app_name,
|
|
136
|
+
cwd,
|
|
137
|
+
});
|
|
138
|
+
if (!lastDetection.is_modal_present) {
|
|
139
|
+
return {
|
|
140
|
+
closed: true,
|
|
141
|
+
strategies_tried: attempts,
|
|
142
|
+
final_detection: lastDetection,
|
|
143
|
+
escalation: "none",
|
|
144
|
+
hwnd,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// If the HWND changed (modal stack drained one level), keep going
|
|
148
|
+
// through the ladder against the next-up modal — but cap the loop
|
|
149
|
+
// by exhausting our 4 strategies. The next call to handle_modal
|
|
150
|
+
// will pick up any remaining stacked modal.
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
closed: false,
|
|
154
|
+
strategies_tried: attempts,
|
|
155
|
+
final_detection: lastDetection,
|
|
156
|
+
escalation: "kill_window_required",
|
|
157
|
+
hwnd,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
export async function killModalWindow(opts) {
|
|
161
|
+
if (process.platform !== "win32") {
|
|
162
|
+
return {
|
|
163
|
+
closed: false,
|
|
164
|
+
steps: [
|
|
165
|
+
{
|
|
166
|
+
step: "post_wm_close",
|
|
167
|
+
success: false,
|
|
168
|
+
error: "kill-window escalation is Windows-only",
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
final_detection: { is_modal_present: false, target_type: "desktop" },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const steps = [];
|
|
175
|
+
// Step 1: PostMessage(WM_CLOSE).
|
|
176
|
+
const wmCloseScript = [
|
|
177
|
+
"Add-Type @\"",
|
|
178
|
+
"using System;",
|
|
179
|
+
"using System.Runtime.InteropServices;",
|
|
180
|
+
"public class CL2 {",
|
|
181
|
+
" [DllImport(\"user32.dll\")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);",
|
|
182
|
+
" [DllImport(\"user32.dll\")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);",
|
|
183
|
+
"}",
|
|
184
|
+
"\"@",
|
|
185
|
+
`$hwnd = [IntPtr]${opts.hwnd}`,
|
|
186
|
+
"$WM_CLOSE = 0x0010",
|
|
187
|
+
"$ok = [CL2]::PostMessage($hwnd, $WM_CLOSE, [IntPtr]::Zero, [IntPtr]::Zero)",
|
|
188
|
+
"if (-not $ok) { Write-Error 'PostMessage(WM_CLOSE) failed'; exit 1 }",
|
|
189
|
+
].join("\n");
|
|
190
|
+
const r1 = await runCommand("powershell", ["-NoProfile", "-Command", wmCloseScript], opts.cwd, undefined, undefined, 5000);
|
|
191
|
+
steps.push({ step: "post_wm_close", success: r1.exit_code === 0, error: r1.exit_code === 0 ? undefined : r1.stderr.slice(0, 200) });
|
|
192
|
+
await sleep(2000);
|
|
193
|
+
let detection = await detectModal({
|
|
194
|
+
target_type: "desktop",
|
|
195
|
+
app_name: opts.app_name,
|
|
196
|
+
cwd: opts.cwd,
|
|
197
|
+
});
|
|
198
|
+
if (!detection.is_modal_present) {
|
|
199
|
+
return { closed: true, steps, final_detection: detection };
|
|
200
|
+
}
|
|
201
|
+
// Step 2: TerminateProcess on the owning PID.
|
|
202
|
+
const terminateScript = [
|
|
203
|
+
"Add-Type @\"",
|
|
204
|
+
"using System;",
|
|
205
|
+
"using System.Runtime.InteropServices;",
|
|
206
|
+
"public class CL3 {",
|
|
207
|
+
" [DllImport(\"user32.dll\")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);",
|
|
208
|
+
"}",
|
|
209
|
+
"\"@",
|
|
210
|
+
`$hwnd = [IntPtr]${opts.hwnd}`,
|
|
211
|
+
"$pid_out = 0",
|
|
212
|
+
"[void][CL3]::GetWindowThreadProcessId($hwnd, [ref]$pid_out)",
|
|
213
|
+
"if ($pid_out -eq 0) { Write-Error 'could not resolve PID for HWND'; exit 1 }",
|
|
214
|
+
"Stop-Process -Id $pid_out -Force",
|
|
215
|
+
].join("\n");
|
|
216
|
+
const r2 = await runCommand("powershell", ["-NoProfile", "-Command", terminateScript], opts.cwd, undefined, undefined, 5000);
|
|
217
|
+
steps.push({ step: "terminate_process", success: r2.exit_code === 0, error: r2.exit_code === 0 ? undefined : r2.stderr.slice(0, 200) });
|
|
218
|
+
await sleep(1000);
|
|
219
|
+
detection = await detectModal({
|
|
220
|
+
target_type: "desktop",
|
|
221
|
+
app_name: opts.app_name,
|
|
222
|
+
cwd: opts.cwd,
|
|
223
|
+
});
|
|
224
|
+
return { closed: !detection.is_modal_present, steps, final_detection: detection };
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=modal_close_strategies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modal_close_strategies.js","sourceRoot":"","sources":["../../src/runners/modal_close_strategies.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,10 +32,25 @@ import type { CodeLoopConfig } from "@codelooptech/shared";
|
|
|
32
32
|
* negative as proof there's no modal — the post-interact directive
|
|
33
33
|
* (layer 3) covers that case by always nudging the agent to look.
|
|
34
34
|
*/
|
|
35
|
+
/**
|
|
36
|
+
* 0.1.54 E1 — modal_kind classifies the detected modal so codeloop_handle_modal
|
|
37
|
+
* can pick the right close strategy. .NET 8 OpenFolderDialog / OpenFileDialog
|
|
38
|
+
* are the dominant "stuck" modal class — Photometry-DB E2E #11 had three
|
|
39
|
+
* stacked file dialogs that ignored every close attempt the handler tried.
|
|
40
|
+
* "file_dialog" routes them through the multi-strategy close ladder
|
|
41
|
+
* (Escape → Alt+F4 → UIA Invoke "Close" → EndDialog → kill-window).
|
|
42
|
+
*/
|
|
43
|
+
export type ModalKind = "file_dialog" | "confirm" | "alert" | "custom";
|
|
35
44
|
export interface ModalDetectionResult {
|
|
36
45
|
is_modal_present: boolean;
|
|
37
46
|
/** Human-readable description of the detected modal, when present. */
|
|
38
47
|
modal_description?: string;
|
|
48
|
+
/** 0.1.54 E1 — classification used by codeloop_handle_modal to pick close strategy. */
|
|
49
|
+
modal_kind?: ModalKind;
|
|
50
|
+
/** 0.1.54 E1 — Win32 HWND when available, used by codeloop_kill_modal_window. */
|
|
51
|
+
hwnd?: string;
|
|
52
|
+
/** 0.1.54 E1 — UIA ClassName when available (e.g. #32770 for Win32 dialogs). */
|
|
53
|
+
class_name?: string;
|
|
39
54
|
/** Suggested action for the agent: "confirm" | "cancel" | "dismiss" | "inspect". */
|
|
40
55
|
suggested_action?: string;
|
|
41
56
|
/** Heuristic confidence 0-1; > 0.7 should be acted on. */
|
|
@@ -45,6 +60,8 @@ export interface ModalDetectionResult {
|
|
|
45
60
|
/** target_type the detector ran against. */
|
|
46
61
|
target_type: "browser" | "desktop" | "android_emulator" | "ios_simulator" | "unknown";
|
|
47
62
|
}
|
|
63
|
+
/** 0.1.54 E1 — exported so codeloop_handle_modal can reuse the same heuristic. */
|
|
64
|
+
export declare function classifyModalKind(name: string, className?: string): ModalKind;
|
|
48
65
|
export declare function detectModal(opts: {
|
|
49
66
|
target_type?: "browser" | "desktop" | "android_emulator" | "ios_simulator";
|
|
50
67
|
app_name?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal_detector.d.ts","sourceRoot":"","sources":["../../src/runners/modal_detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,WAAW,EAAE,SAAS,GAAG,SAAS,GAAG,kBAAkB,GAAG,eAAe,GAAG,SAAS,CAAC;CACvF;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,kBAAkB,GAAG,eAAe,CAAC;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiBhC;
|
|
1
|
+
{"version":3,"file":"modal_detector.d.ts","sourceRoot":"","sources":["../../src/runners/modal_detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uFAAuF;IACvF,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,WAAW,EAAE,SAAS,GAAG,SAAS,GAAG,kBAAkB,GAAG,eAAe,GAAG,SAAS,CAAC;CACvF;AAqBD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAO7E;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,kBAAkB,GAAG,eAAe,CAAC;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiBhC;AAyLD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAiB5D"}
|
|
@@ -1,4 +1,35 @@
|
|
|
1
1
|
import { runCommand } from "./base.js";
|
|
2
|
+
/**
|
|
3
|
+
* 0.1.54 E1 — Class names that identify file dialogs across .NET / Win32:
|
|
4
|
+
* - `#32770` — every Win32 common dialog (CommonItemDialog included)
|
|
5
|
+
* - `Shell_Dialog` — Shell-based file pickers
|
|
6
|
+
* - `DirectUIHWND` — child of file dialogs but useful for ownership match
|
|
7
|
+
* - `WinUIDesktopWin32WindowClass` — WinUI 3 / .NET 8 desktop
|
|
8
|
+
*
|
|
9
|
+
* Name patterns that match file-dialog titles in EN locale; we accept any
|
|
10
|
+
* substring so localised "Open File" / "Save As" / "Browse Folder" still hit.
|
|
11
|
+
*/
|
|
12
|
+
const FILE_DIALOG_CLASS_NAMES = ["#32770", "Shell_Dialog", "DirectUIHWND", "WinUIDesktopWin32WindowClass"];
|
|
13
|
+
const FILE_DIALOG_NAME_PATTERNS = [
|
|
14
|
+
/\b(open|save|select|browse|choose)\b.*\b(file|folder|directory|location)\b/i,
|
|
15
|
+
/\bfile\s*open\b/i,
|
|
16
|
+
/\bsave\s*as\b/i,
|
|
17
|
+
/\bopenfolderdialog\b/i,
|
|
18
|
+
/\bopenfiledialog\b/i,
|
|
19
|
+
];
|
|
20
|
+
/** 0.1.54 E1 — exported so codeloop_handle_modal can reuse the same heuristic. */
|
|
21
|
+
export function classifyModalKind(name, className) {
|
|
22
|
+
const cls = (className ?? "").trim();
|
|
23
|
+
if (FILE_DIALOG_CLASS_NAMES.includes(cls))
|
|
24
|
+
return "file_dialog";
|
|
25
|
+
if (FILE_DIALOG_NAME_PATTERNS.some((re) => re.test(name)))
|
|
26
|
+
return "file_dialog";
|
|
27
|
+
if (/\b(confirm|are you sure|delete\?|discard)\b/i.test(name))
|
|
28
|
+
return "confirm";
|
|
29
|
+
if (/\b(error|warning|alert|attention)\b/i.test(name))
|
|
30
|
+
return "alert";
|
|
31
|
+
return "custom";
|
|
32
|
+
}
|
|
2
33
|
export async function detectModal(opts) {
|
|
3
34
|
const target = opts.target_type ?? "desktop";
|
|
4
35
|
try {
|
|
@@ -42,10 +73,12 @@ async function detectDesktopModal(opts) {
|
|
|
42
73
|
}
|
|
43
74
|
async function detectWindowsModal(opts) {
|
|
44
75
|
const app = opts.app_name ?? "";
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
76
|
+
const appLower = app.toLowerCase();
|
|
77
|
+
// PowerShell UIA probe. 0.1.54 E1 — also collects ClassName + HWND so
|
|
78
|
+
// codeloop_handle_modal can pick the right close strategy and so the
|
|
79
|
+
// kill-window escalation knows which window to terminate. We additionally
|
|
80
|
+
// accept windows that are NOT marked IsModal=true but match a file-dialog
|
|
81
|
+
// class name (.NET 8 OpenFolderDialog often does not set IsModal).
|
|
49
82
|
const script = [
|
|
50
83
|
"Add-Type -AssemblyName UIAutomationClient",
|
|
51
84
|
"$root = [System.Windows.Automation.AutomationElement]::RootElement",
|
|
@@ -54,15 +87,14 @@ async function detectWindowsModal(opts) {
|
|
|
54
87
|
"$result = @()",
|
|
55
88
|
"foreach ($w in $windows) {",
|
|
56
89
|
" $name = $w.Current.Name",
|
|
57
|
-
" $
|
|
90
|
+
" $cls = $w.Current.ClassName",
|
|
91
|
+
" $hwnd = $w.Current.NativeWindowHandle",
|
|
92
|
+
" $isModal = $false",
|
|
58
93
|
" try {",
|
|
59
94
|
" $pattern = $w.GetCurrentPattern([System.Windows.Automation.WindowPattern]::Pattern)",
|
|
60
|
-
" $
|
|
95
|
+
" $isModal = $pattern.Current.IsModal",
|
|
61
96
|
" } catch { }",
|
|
62
|
-
"
|
|
63
|
-
" if ($owned) {",
|
|
64
|
-
" $result += @{ name = $name; modal = $true }",
|
|
65
|
-
" }",
|
|
97
|
+
" $result += @{ name = $name; class_name = $cls; hwnd = $hwnd; is_modal = $isModal }",
|
|
66
98
|
"}",
|
|
67
99
|
"$result | ConvertTo-Json -Compress",
|
|
68
100
|
].join("\n");
|
|
@@ -74,22 +106,63 @@ async function detectWindowsModal(opts) {
|
|
|
74
106
|
detection_error: r.stderr.slice(0, 200),
|
|
75
107
|
};
|
|
76
108
|
}
|
|
109
|
+
let arr = [];
|
|
77
110
|
try {
|
|
78
111
|
const parsed = r.stdout.trim() ? JSON.parse(r.stdout) : null;
|
|
79
|
-
|
|
80
|
-
const modal = arr.find((w) => w.modal);
|
|
81
|
-
if (modal) {
|
|
82
|
-
return {
|
|
83
|
-
is_modal_present: true,
|
|
84
|
-
modal_description: `Windows modal: ${modal.name ?? "(unnamed)"}`,
|
|
85
|
-
suggested_action: "inspect",
|
|
86
|
-
confidence: 0.85,
|
|
87
|
-
target_type: "desktop",
|
|
88
|
-
};
|
|
89
|
-
}
|
|
112
|
+
arr = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];
|
|
90
113
|
}
|
|
91
114
|
catch {
|
|
92
|
-
|
|
115
|
+
return { is_modal_present: false, target_type: "desktop" };
|
|
116
|
+
}
|
|
117
|
+
// First pass: explicitly modal windows that pass the app filter.
|
|
118
|
+
// Second pass: file-dialog class match (regardless of IsModal flag) when
|
|
119
|
+
// the window's name suggests a file picker. The two-pass split makes sure
|
|
120
|
+
// we never spuriously match an unrelated #32770 dialog the OS itself owns
|
|
121
|
+
// (Windows Spotlight, Snipping Tool, etc.) — the file-dialog match
|
|
122
|
+
// requires either a name pattern OR an app filter that hits the running
|
|
123
|
+
// process.
|
|
124
|
+
const matchesAppFilter = (name) => {
|
|
125
|
+
if (!appLower)
|
|
126
|
+
return true;
|
|
127
|
+
return name.toLowerCase().includes(appLower);
|
|
128
|
+
};
|
|
129
|
+
const explicit = arr.find((w) => w.is_modal && matchesAppFilter(w.name ?? ""));
|
|
130
|
+
if (explicit) {
|
|
131
|
+
const name = explicit.name ?? "(unnamed)";
|
|
132
|
+
const cls = explicit.class_name ?? undefined;
|
|
133
|
+
return {
|
|
134
|
+
is_modal_present: true,
|
|
135
|
+
modal_description: `Windows modal: ${name}`,
|
|
136
|
+
modal_kind: classifyModalKind(name, cls),
|
|
137
|
+
class_name: cls,
|
|
138
|
+
hwnd: explicit.hwnd != null ? String(explicit.hwnd) : undefined,
|
|
139
|
+
suggested_action: "inspect",
|
|
140
|
+
confidence: 0.85,
|
|
141
|
+
target_type: "desktop",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const fileDialog = arr.find((w) => {
|
|
145
|
+
const name = w.name ?? "";
|
|
146
|
+
const cls = w.class_name ?? "";
|
|
147
|
+
if (!FILE_DIALOG_CLASS_NAMES.includes(cls))
|
|
148
|
+
return false;
|
|
149
|
+
// The app filter applies for class-only matches — without it any Win32
|
|
150
|
+
// dialog on the desktop would trigger.
|
|
151
|
+
return matchesAppFilter(name) || FILE_DIALOG_NAME_PATTERNS.some((re) => re.test(name));
|
|
152
|
+
});
|
|
153
|
+
if (fileDialog) {
|
|
154
|
+
const name = fileDialog.name ?? "(unnamed)";
|
|
155
|
+
const cls = fileDialog.class_name ?? undefined;
|
|
156
|
+
return {
|
|
157
|
+
is_modal_present: true,
|
|
158
|
+
modal_description: `Windows file dialog: ${name}`,
|
|
159
|
+
modal_kind: "file_dialog",
|
|
160
|
+
class_name: cls,
|
|
161
|
+
hwnd: fileDialog.hwnd != null ? String(fileDialog.hwnd) : undefined,
|
|
162
|
+
suggested_action: "dismiss",
|
|
163
|
+
confidence: 0.75,
|
|
164
|
+
target_type: "desktop",
|
|
165
|
+
};
|
|
93
166
|
}
|
|
94
167
|
return { is_modal_present: false, target_type: "desktop" };
|
|
95
168
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal_detector.js","sourceRoot":"","sources":["../../src/runners/modal_detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"modal_detector.js","sourceRoot":"","sources":["../../src/runners/modal_detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"design_compare.d.ts","sourceRoot":"","sources":["../../src/tools/design_compare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EAEhB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAyB3D,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,UAAU,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6FAA6F;IAC7F,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACrC;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE;IAAE,cAAc,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACrE,UAAU,GAAE,MAA4B,GACvC,OAAO,CAKT;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EACF;IACE,cAAc,CAAC,EAAE;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH,GACD,IAAI,GACJ,SAAS,GACZ,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAsBjC;AAgBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,GAAE,MAA4B,GAAG,OAAO,CA+BlG;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,GAAE,MAA4B,GAAG,aAAa,EAAE,CAiCzG;AAkBD;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AAgI7C;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAkCrD;AAmID;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAyI9B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"design_compare.d.ts","sourceRoot":"","sources":["../../src/tools/design_compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -856,9 +856,28 @@ export async function runDesignCompareAll(input, config, cwd = process.cwd()) {
|
|
|
856
856
|
return a.match_score - b.match_score;
|
|
857
857
|
});
|
|
858
858
|
const lead = sorted[0] || perScreen[0];
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
859
|
+
// 0.1.53 D5 — disambiguate skipped reasons. Photometry-DB E2E #10 had
|
|
860
|
+
// 18 references skipped: 15 because no actual screenshot existed for
|
|
861
|
+
// the reference name (weak-cross-run-only) and 3 because the reference
|
|
862
|
+
// PNG itself failed pngjs decode. The pre-D5 skipNote lumped both
|
|
863
|
+
// under "corrupt/truncated PNG" which sent the agent on a 30-min
|
|
864
|
+
// PowerShell `Bitmap.Save` re-encode hunt when the real fix for 15/18
|
|
865
|
+
// was simply "capture the missing screen". Split the bucket so the
|
|
866
|
+
// agent gets the right next step.
|
|
867
|
+
const corruptScreens = perScreen
|
|
868
|
+
.filter((p) => p.skipped && /corrupt or truncated PNG/i.test(p.reason ?? ""))
|
|
869
|
+
.map((p) => p.screen_name);
|
|
870
|
+
const missingActualScreens = perScreen
|
|
871
|
+
.filter((p) => p.skipped && /no strong reference|no screenshot/i.test(p.reason ?? ""))
|
|
872
|
+
.map((p) => p.screen_name);
|
|
873
|
+
const skipNoteParts = [];
|
|
874
|
+
if (missingActualScreens.length > 0) {
|
|
875
|
+
skipNoteParts.push(`${missingActualScreens.length} reference(s) skipped because no matching screenshot was captured — call codeloop_capture_screenshot for: ${missingActualScreens.slice(0, 3).join(", ")}${missingActualScreens.length > 3 ? "…" : ""}`);
|
|
876
|
+
}
|
|
877
|
+
if (corruptScreens.length > 0) {
|
|
878
|
+
skipNoteParts.push(`${corruptScreens.length} reference(s) skipped because the PNG file itself failed to decode after IEND-trim + checkCRC=false fallbacks — the file is genuinely corrupt; re-export from the design tool or delete it: ${corruptScreens.slice(0, 3).join(", ")}${corruptScreens.length > 3 ? "…" : ""}`);
|
|
879
|
+
}
|
|
880
|
+
const skipNote = skipNoteParts.length > 0 ? ` (${skipNoteParts.join("; ")})` : "";
|
|
862
881
|
const recommendation = allPassed
|
|
863
882
|
? `All ${scored.length} scored screen(s) match designs at or above ${(threshold * 100).toFixed(0)}% threshold (min: ${(minScore * 100).toFixed(1)}%).${skipNote}`
|
|
864
883
|
: `${failedScreens.length}/${scored.length} screen(s) below threshold ${(threshold * 100).toFixed(0)}%. Worst: "${lead.screen_name}" at ${(lead.match_score * 100).toFixed(1)}%. Fix and re-run codeloop_verify before gate_check.${skipNote}`;
|