codeloop-mcp-server 0.1.56 → 0.1.60
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/evidence/backend_verification.d.ts +64 -0
- package/dist/evidence/backend_verification.d.ts.map +1 -0
- package/dist/evidence/backend_verification.js +94 -0
- package/dist/evidence/backend_verification.js.map +1 -0
- package/dist/evidence/cycle_issues.d.ts +8 -0
- package/dist/evidence/cycle_issues.d.ts.map +1 -1
- package/dist/evidence/cycle_issues.js +7 -2
- package/dist/evidence/cycle_issues.js.map +1 -1
- package/dist/evidence/deep_internal.d.ts +45 -0
- package/dist/evidence/deep_internal.d.ts.map +1 -0
- package/dist/evidence/deep_internal.js +99 -0
- package/dist/evidence/deep_internal.js.map +1 -0
- package/dist/evidence/runtime_log_scan.d.ts +43 -0
- package/dist/evidence/runtime_log_scan.d.ts.map +1 -0
- package/dist/evidence/runtime_log_scan.js +112 -0
- package/dist/evidence/runtime_log_scan.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +243 -17
- package/dist/index.js.map +1 -1
- package/dist/runners/backend_runtime.d.ts +40 -0
- package/dist/runners/backend_runtime.d.ts.map +1 -0
- package/dist/runners/backend_runtime.js +380 -0
- package/dist/runners/backend_runtime.js.map +1 -0
- package/dist/runners/coverage.d.ts +66 -0
- package/dist/runners/coverage.d.ts.map +1 -0
- package/dist/runners/coverage.js +380 -0
- package/dist/runners/coverage.js.map +1 -0
- package/dist/runners/modal_detector.d.ts.map +1 -1
- package/dist/runners/modal_detector.js +38 -7
- package/dist/runners/modal_detector.js.map +1 -1
- package/dist/runners/static_analysis.d.ts +72 -0
- package/dist/runners/static_analysis.d.ts.map +1 -0
- package/dist/runners/static_analysis.js +248 -0
- package/dist/runners/static_analysis.js.map +1 -0
- package/dist/tools/diagnose.d.ts.map +1 -1
- package/dist/tools/diagnose.js +143 -0
- package/dist/tools/diagnose.js.map +1 -1
- package/dist/tools/gate_check.d.ts.map +1 -1
- package/dist/tools/gate_check.js +116 -0
- package/dist/tools/gate_check.js.map +1 -1
- package/dist/tools/verify.d.ts.map +1 -1
- package/dist/tools/verify.js +83 -0
- package/dist/tools/verify.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -174,6 +174,35 @@ startUpdateCheck();
|
|
|
174
174
|
* issues we've already written to disk continue to gate ready_for_review.
|
|
175
175
|
*/
|
|
176
176
|
const modalPersistenceTracker = new Map();
|
|
177
|
+
/**
|
|
178
|
+
* 0.1.57 H2 — Throttle for the post-interact modal probe.
|
|
179
|
+
*
|
|
180
|
+
* 0.1.55 only ran the probe after SUCCESSFUL clicky/escape actions, so a
|
|
181
|
+
* file dialog that blocked the app (subsequent clicks fail, or the agent
|
|
182
|
+
* switches to wait / capture_screenshot / win_ui_automate) was never
|
|
183
|
+
* re-detected and the persistence counter never climbed — the modal sat
|
|
184
|
+
* open with zero cycle issues recorded (E2E #12). We now probe on the
|
|
185
|
+
* broader modal-related action set REGARDLESS of success, and use this map
|
|
186
|
+
* to keep the PowerShell cost bounded: clicky/escape always probe; other
|
|
187
|
+
* modal-related actions probe at most once per throttle window per app.
|
|
188
|
+
*/
|
|
189
|
+
const modalProbeLastAt = new Map();
|
|
190
|
+
const MODAL_PROBE_THROTTLE_MS = 1500;
|
|
191
|
+
/**
|
|
192
|
+
* 0.1.57 H3 — auto-close stuck external file dialogs.
|
|
193
|
+
*
|
|
194
|
+
* Default ON. The user can opt out per-machine with
|
|
195
|
+
* CODELOOP_AUTO_CLOSE_STUCK_MODALS=0 (or "false"/"off") if a workflow
|
|
196
|
+
* legitimately keeps a file picker open across many interactions.
|
|
197
|
+
*/
|
|
198
|
+
function autoCloseStuckModalsEnabled() {
|
|
199
|
+
const v = (process.env.CODELOOP_AUTO_CLOSE_STUCK_MODALS ?? "").trim().toLowerCase();
|
|
200
|
+
if (v === "0" || v === "false" || v === "off" || v === "no")
|
|
201
|
+
return false;
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
/** Consecutive same-dialog detections that mark a file dialog as genuinely stuck. */
|
|
205
|
+
const MODAL_AUTOCLOSE_THRESHOLD = 3;
|
|
177
206
|
const server = new McpServer({
|
|
178
207
|
name: "codeloop",
|
|
179
208
|
version: "0.1.14",
|
|
@@ -1924,6 +1953,59 @@ After stopping, call codeloop_interaction_replay with the run_id to extract fram
|
|
|
1924
1953
|
The response includes log_path if app logs were captured during the recording session.`, {
|
|
1925
1954
|
recording_id: z.string().describe("The recording_id returned by codeloop_start_recording"),
|
|
1926
1955
|
}, async (params) => {
|
|
1956
|
+
// 0.1.57 H4 — stuck-modal sweep. If a file dialog is still open when the
|
|
1957
|
+
// recording stops (E2E #12: the external folder picker that "kept opening
|
|
1958
|
+
// all the time"), close it automatically so it does not linger on the
|
|
1959
|
+
// user's desktop after the cycle. Best-effort and Windows-only; never
|
|
1960
|
+
// blocks the stop. Runs BEFORE finalising so the close is visible in the
|
|
1961
|
+
// final frames.
|
|
1962
|
+
let sweepNote;
|
|
1963
|
+
if (process.platform === "win32" && autoCloseStuckModalsEnabled()) {
|
|
1964
|
+
try {
|
|
1965
|
+
const { detectModal } = await import("./runners/modal_detector.js");
|
|
1966
|
+
const { closeModalWithStrategies } = await import("./runners/modal_close_strategies.js");
|
|
1967
|
+
const vrMod = await import("./runners/video_recorder.js");
|
|
1968
|
+
const appNameForModal = vrMod.getActiveRecordingAppName() || undefined;
|
|
1969
|
+
const detection = await detectModal({
|
|
1970
|
+
target_type: "desktop",
|
|
1971
|
+
app_name: appNameForModal,
|
|
1972
|
+
cwd: projectDir,
|
|
1973
|
+
});
|
|
1974
|
+
if (detection.is_modal_present && detection.modal_kind === "file_dialog") {
|
|
1975
|
+
const closeResult = await closeModalWithStrategies({
|
|
1976
|
+
initial_detection: detection,
|
|
1977
|
+
app_name: appNameForModal,
|
|
1978
|
+
cwd: projectDir,
|
|
1979
|
+
});
|
|
1980
|
+
const { recordCycleIssue } = await import("./evidence/cycle_issues.js");
|
|
1981
|
+
if (closeResult.closed) {
|
|
1982
|
+
const usedStrategy = closeResult.strategies_tried.find((s) => s.success)?.strategy ?? "ladder";
|
|
1983
|
+
sweepNote = `CodeLoop auto-closed a lingering file dialog (${detection.modal_description ?? "(unnamed)"}) at stop via ${usedStrategy}.`;
|
|
1984
|
+
await recordCycleIssue(projectDir, {
|
|
1985
|
+
kind: "modal_close_failed",
|
|
1986
|
+
modal_kind: detection.modal_kind ?? "file_dialog",
|
|
1987
|
+
modal_description: `${detection.modal_description ?? "(unnamed)"} (auto-closed by CodeLoop H4 stop sweep via ${usedStrategy})`,
|
|
1988
|
+
strategies_tried: closeResult.strategies_tried.filter((s) => s.success).map((s) => s.strategy),
|
|
1989
|
+
hwnd: detection.hwnd,
|
|
1990
|
+
auto_resolved: true,
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
else {
|
|
1994
|
+
sweepNote = `A file dialog (${detection.modal_description ?? "(unnamed)"}) was still open at stop and the close ladder failed — call codeloop_kill_modal_window with hwnd ${detection.hwnd ?? "(see logs)"}.`;
|
|
1995
|
+
await recordCycleIssue(projectDir, {
|
|
1996
|
+
kind: "modal_close_failed",
|
|
1997
|
+
modal_kind: detection.modal_kind ?? "file_dialog",
|
|
1998
|
+
modal_description: detection.modal_description,
|
|
1999
|
+
strategies_tried: closeResult.strategies_tried.map((s) => s.strategy),
|
|
2000
|
+
hwnd: detection.hwnd ?? closeResult.hwnd,
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
catch {
|
|
2006
|
+
/* best-effort sweep — never block the stop */
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
1927
2009
|
const authResult = await withAuth(async () => {
|
|
1928
2010
|
const { stopBackgroundRecording } = await import("./runners/video_recorder.js");
|
|
1929
2011
|
return stopBackgroundRecording(params.recording_id);
|
|
@@ -1945,6 +2027,7 @@ The response includes log_path if app logs were captured during the recording se
|
|
|
1945
2027
|
" 1. codeloop_interaction_replay — extract frames + app logs from the just-saved video. This populates the data the replay/journey gates score against.",
|
|
1946
2028
|
" 2. codeloop_gate_check — confirm confidence ≥ 94%. If continue_fixing, fix the failing gate's next_step and re-record / re-capture.",
|
|
1947
2029
|
"Do NOT skip step 1 — without replay frames the interaction_replay_evidence gate fails even when the video exists. Do NOT pause to ask the user 'should I run replay now?' — yes, always.",
|
|
2030
|
+
...(sweepNote ? ["", `[CodeLoop H4] ${sweepNote}`] : []),
|
|
1948
2031
|
].join("\n");
|
|
1949
2032
|
return {
|
|
1950
2033
|
content: withInitHint([{ type: "text", text: JSON.stringify(result, null, 2) + nextStepDirective }]),
|
|
@@ -2166,6 +2249,36 @@ The agent MUST then write the report to docs/DEVELOPMENT_LOG.md and present it t
|
|
|
2166
2249
|
.slice(0, 5)
|
|
2167
2250
|
.map((i) => `(${i.kind}) ${summariseCycleIssue(i)}`)
|
|
2168
2251
|
.join(" | ")}${ci.issues.length > 5 ? " | … (see logs/cycle_issues.jsonl for the full list)" : ""}`;
|
|
2252
|
+
// 0.1.58-0.1.60 — Deep Internal Verification aggregate. Pull the most
|
|
2253
|
+
// recent run that produced each evidence file so the dev report can
|
|
2254
|
+
// document static analysis, changed-line coverage, backend smoke,
|
|
2255
|
+
// API contract, DB schema, and runtime-log cleanliness.
|
|
2256
|
+
const { loadCodeQualityEvidence } = await import("./evidence/deep_internal.js");
|
|
2257
|
+
const { loadBackendVerification } = await import("./evidence/backend_verification.js");
|
|
2258
|
+
const { loadRuntimeLogScan } = await import("./evidence/runtime_log_scan.js");
|
|
2259
|
+
const deepInternal = { static_analysis: null, coverage: null, backend: null, runtime_log: null };
|
|
2260
|
+
for (const runId of [...runs].reverse()) {
|
|
2261
|
+
const runDir = getRunDir(runId, baseDir);
|
|
2262
|
+
if (deepInternal.static_analysis === null || deepInternal.coverage === null) {
|
|
2263
|
+
const cq = loadCodeQualityEvidence(runDir);
|
|
2264
|
+
if (cq) {
|
|
2265
|
+
if (deepInternal.static_analysis === null)
|
|
2266
|
+
deepInternal.static_analysis = cq.static_analysis;
|
|
2267
|
+
if (deepInternal.coverage === null)
|
|
2268
|
+
deepInternal.coverage = cq.coverage;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
if (deepInternal.backend === null) {
|
|
2272
|
+
const b = loadBackendVerification(runDir);
|
|
2273
|
+
if (b)
|
|
2274
|
+
deepInternal.backend = b;
|
|
2275
|
+
}
|
|
2276
|
+
if (deepInternal.runtime_log === null) {
|
|
2277
|
+
const s = loadRuntimeLogScan(runDir);
|
|
2278
|
+
if (s)
|
|
2279
|
+
deepInternal.runtime_log = s;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2169
2282
|
const report = {
|
|
2170
2283
|
project_name: params.project_name,
|
|
2171
2284
|
project_description: params.project_description || "",
|
|
@@ -2190,6 +2303,7 @@ The agent MUST then write the report to docs/DEVELOPMENT_LOG.md and present it t
|
|
|
2190
2303
|
cycle_issues_summary: cycleSummary,
|
|
2191
2304
|
cycle_issues: cycleIssuesEntries,
|
|
2192
2305
|
cycle_issues_directive: cycleIssuesDirective,
|
|
2306
|
+
deep_internal_verification: deepInternal,
|
|
2193
2307
|
};
|
|
2194
2308
|
await trackUsage(apiKey, "verification_run");
|
|
2195
2309
|
return report;
|
|
@@ -2255,6 +2369,19 @@ For EACH video recording session, document:
|
|
|
2255
2369
|
Create a table with: | # | Bug Description | Severity | How Found | Fix Applied |
|
|
2256
2370
|
List every issue discovered by CodeLoop during the development process.
|
|
2257
2371
|
|
|
2372
|
+
**7b. Deep Internal Verification** (0.1.58+)
|
|
2373
|
+
Document the internal (code / backend / terminal) verification evidence from the
|
|
2374
|
+
\`deep_internal_verification\` block above. Include a table:
|
|
2375
|
+
| Layer | Check | Result | Gate |
|
|
2376
|
+
|-------|-------|--------|------|
|
|
2377
|
+
| Static analysis | tsc / eslint / ruff / mypy / clippy / dotnet-format / flutter analyze | errors found / clean / n/a | static_analysis_passes |
|
|
2378
|
+
| Coverage | changed-line coverage % (covered/total) | pass / below threshold / n/a | code_coverage_threshold |
|
|
2379
|
+
| Backend smoke | health + route probes, 5xx count | healthy / failed / n/a | backend_smoke_evidence |
|
|
2380
|
+
| API contract | OpenAPI route conformance (violations) | pass / violations / n/a | api_contract_evidence |
|
|
2381
|
+
| DB schema | migration apply + drift | clean / drift / n/a | db_schema_evidence |
|
|
2382
|
+
| Runtime logs | exceptions / 5xx / fatal lines in logs | clean / errors / n/a | runtime_log_clean_evidence |
|
|
2383
|
+
State clearly which checks were applicable for this project's stack and which were n/a.
|
|
2384
|
+
|
|
2258
2385
|
**8. Cross-Platform Coverage**
|
|
2259
2386
|
Document which OS and platform combinations CodeLoop supports:
|
|
2260
2387
|
| OS | App Type | Video Method | Interaction Method | Log Capture |
|
|
@@ -3538,15 +3665,40 @@ Wait 1-2 seconds between interactions so video frames capture state changes.`, {
|
|
|
3538
3665
|
const isClosingIntent = /\b(close|dismiss|cancel|escape|exit)\b/.test(closingIntent);
|
|
3539
3666
|
const isClickyAction = action === "click" || action === "double_click" || action === "right_click";
|
|
3540
3667
|
const isEscapeKeystroke = action === "keystroke" && (params.key ?? "").toLowerCase() === "escape";
|
|
3541
|
-
|
|
3542
|
-
|
|
3668
|
+
// 0.1.57 H2 — probe on the broader modal-related action set REGARDLESS
|
|
3669
|
+
// of success. A blocked / failed click against a stuck file dialog is
|
|
3670
|
+
// itself the signal we need (E2E #12: subsequent clicks failed, so the
|
|
3671
|
+
// 0.1.55 `success &&` gate never re-ran the probe and the picker stayed
|
|
3672
|
+
// open with zero cycle issues). Clicky/escape always probe; the wider
|
|
3673
|
+
// set (type/hotkey/win_ui_automate/sequence/wait) probes at most once
|
|
3674
|
+
// per throttle window so the PowerShell cost stays bounded.
|
|
3675
|
+
const trackerKey = (params.app_name || vr.getActiveRecordingAppName() || "<default>").toLowerCase();
|
|
3676
|
+
const clicky = isClickyAction || isEscapeKeystroke;
|
|
3677
|
+
const widerModalActions = new Set([
|
|
3678
|
+
"type",
|
|
3679
|
+
"type_and_submit",
|
|
3680
|
+
"type_and_tab",
|
|
3681
|
+
"keystroke",
|
|
3682
|
+
"hotkey",
|
|
3683
|
+
"win_ui_automate",
|
|
3684
|
+
"sequence",
|
|
3685
|
+
"wait",
|
|
3686
|
+
"hover",
|
|
3687
|
+
]);
|
|
3688
|
+
const nowMs = Date.now();
|
|
3689
|
+
const lastProbeMs = modalProbeLastAt.get(trackerKey) ?? 0;
|
|
3690
|
+
const throttleOk = clicky || (widerModalActions.has(action) && nowMs - lastProbeMs > MODAL_PROBE_THROTTLE_MS);
|
|
3691
|
+
const shouldCheckModal = tt === "desktop" && throttleOk;
|
|
3692
|
+
if (shouldCheckModal) {
|
|
3543
3693
|
try {
|
|
3694
|
+
modalProbeLastAt.set(trackerKey, nowMs);
|
|
3544
3695
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3545
3696
|
const { detectModal } = await import("./runners/modal_detector.js");
|
|
3546
|
-
const
|
|
3697
|
+
const { closeModalWithStrategies } = await import("./runners/modal_close_strategies.js");
|
|
3698
|
+
const appNameForModal = params.app_name || vr.getActiveRecordingAppName() || undefined;
|
|
3547
3699
|
const detection = await detectModal({
|
|
3548
3700
|
target_type: "desktop",
|
|
3549
|
-
app_name:
|
|
3701
|
+
app_name: appNameForModal,
|
|
3550
3702
|
cwd,
|
|
3551
3703
|
});
|
|
3552
3704
|
const { recordCycleIssue } = await import("./evidence/cycle_issues.js");
|
|
@@ -3605,11 +3757,79 @@ Wait 1-2 seconds between interactions so video frames capture state changes.`, {
|
|
|
3605
3757
|
modal_kind: detection.modal_kind,
|
|
3606
3758
|
});
|
|
3607
3759
|
}
|
|
3608
|
-
// 0.1.
|
|
3609
|
-
//
|
|
3610
|
-
// the
|
|
3611
|
-
//
|
|
3612
|
-
|
|
3760
|
+
// 0.1.57 H3 — AUTOMATIC close of stuck external file dialogs.
|
|
3761
|
+
//
|
|
3762
|
+
// E2E #12: the agent rationalised a recurring OpenFolderDialog as
|
|
3763
|
+
// a "test artifact" and NEVER called codeloop_handle_modal, so the
|
|
3764
|
+
// picker was left open and kept reopening. CodeLoop must close it
|
|
3765
|
+
// itself rather than only directing the agent.
|
|
3766
|
+
//
|
|
3767
|
+
// Trigger conditions (all required) keep this safe against legit
|
|
3768
|
+
// picker usage:
|
|
3769
|
+
// - it's a file_dialog (external OS picker — the exact class the
|
|
3770
|
+
// user reports; confirm/alert kinds may need a real decision
|
|
3771
|
+
// and are intentionally left to the agent),
|
|
3772
|
+
// - it has persisted across >= MODAL_AUTOCLOSE_THRESHOLD (3)
|
|
3773
|
+
// consecutive detections of the SAME dialog. A legitimate
|
|
3774
|
+
// open→confirm picker flow clears within 1-2 interactions and
|
|
3775
|
+
// resets the tracker, so reaching 3 means the agent is stuck.
|
|
3776
|
+
const isStuckFileDialog = detection.modal_kind === "file_dialog" &&
|
|
3777
|
+
consecutive >= MODAL_AUTOCLOSE_THRESHOLD;
|
|
3778
|
+
let autoClosed = false;
|
|
3779
|
+
if (isStuckFileDialog && autoCloseStuckModalsEnabled()) {
|
|
3780
|
+
try {
|
|
3781
|
+
const closeResult = await closeModalWithStrategies({
|
|
3782
|
+
initial_detection: detection,
|
|
3783
|
+
app_name: appNameForModal,
|
|
3784
|
+
cwd,
|
|
3785
|
+
});
|
|
3786
|
+
autoClosed = closeResult.closed;
|
|
3787
|
+
if (autoClosed) {
|
|
3788
|
+
modalPersistenceTracker.delete(trackerKey);
|
|
3789
|
+
const usedStrategy = closeResult.strategies_tried.find((s) => s.success)?.strategy ?? "ladder";
|
|
3790
|
+
detail = `${detail} | CodeLoop auto-closed stuck file dialog (${desc}) via ${usedStrategy}`;
|
|
3791
|
+
clickEffectVerification = {
|
|
3792
|
+
...(clickEffectVerification ?? { intent: closingIntent, modal_still_present: false }),
|
|
3793
|
+
modal_still_present: false,
|
|
3794
|
+
modal_description: detection.modal_description,
|
|
3795
|
+
modal_kind: detection.modal_kind,
|
|
3796
|
+
consecutive_persistences: consecutive,
|
|
3797
|
+
};
|
|
3798
|
+
// Surface the auto-handling so the dev report shows it was
|
|
3799
|
+
// caught and resolved (not silently swallowed).
|
|
3800
|
+
await recordCycleIssue(cwd, {
|
|
3801
|
+
kind: "modal_close_failed",
|
|
3802
|
+
modal_kind: detection.modal_kind ?? "file_dialog",
|
|
3803
|
+
modal_description: `${desc} (auto-closed by CodeLoop H3 after ${consecutive} consecutive detections via ${usedStrategy})`,
|
|
3804
|
+
strategies_tried: closeResult.strategies_tried
|
|
3805
|
+
.filter((s) => s.success)
|
|
3806
|
+
.map((s) => s.strategy),
|
|
3807
|
+
hwnd: detection.hwnd,
|
|
3808
|
+
auto_resolved: true,
|
|
3809
|
+
});
|
|
3810
|
+
}
|
|
3811
|
+
else {
|
|
3812
|
+
// Ladder exhausted — record the unresolved failure and
|
|
3813
|
+
// escalate the directive to kill-window.
|
|
3814
|
+
await recordCycleIssue(cwd, {
|
|
3815
|
+
kind: "modal_close_failed",
|
|
3816
|
+
modal_kind: detection.modal_kind ?? "file_dialog",
|
|
3817
|
+
modal_description: desc,
|
|
3818
|
+
strategies_tried: closeResult.strategies_tried.map((s) => s.strategy),
|
|
3819
|
+
hwnd: detection.hwnd ?? closeResult.hwnd,
|
|
3820
|
+
});
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
catch {
|
|
3824
|
+
/* best-effort auto-close; fall through to the F3/F4 path */
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
// 0.1.55 F3 — at 3+ consecutive persistences, treat the modal as
|
|
3828
|
+
// actively stuck and write a modal_close_failed entry so the
|
|
3829
|
+
// cycle_issues_acknowledged gate fails even when the agent never
|
|
3830
|
+
// called codeloop_handle_modal. Skipped when H3 already recorded
|
|
3831
|
+
// the (auto-resolved or unresolved) outcome above.
|
|
3832
|
+
if (consecutive >= 3 && !isStuckFileDialog) {
|
|
3613
3833
|
await recordCycleIssue(cwd, {
|
|
3614
3834
|
kind: "modal_close_failed",
|
|
3615
3835
|
modal_kind: detection.modal_kind ?? "custom",
|
|
@@ -3620,14 +3840,20 @@ Wait 1-2 seconds between interactions so video frames capture state changes.`, {
|
|
|
3620
3840
|
hwnd: detection.hwnd,
|
|
3621
3841
|
});
|
|
3622
3842
|
}
|
|
3623
|
-
// 0.1.55 F4 — HARD directive for the post-interact
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
`
|
|
3630
|
-
`
|
|
3843
|
+
// 0.1.55 F4 / 0.1.57 H3 — HARD directive for the post-interact
|
|
3844
|
+
// postscript. Suppressed when CodeLoop already auto-closed the
|
|
3845
|
+
// dialog; otherwise escalated to kill-window when an auto-close
|
|
3846
|
+
// attempt was made and failed.
|
|
3847
|
+
if (!autoClosed) {
|
|
3848
|
+
const killHint = isStuckFileDialog
|
|
3849
|
+
? `CodeLoop already attempted the multi-strategy close ladder and it FAILED — call codeloop_kill_modal_window with hwnd ${detection.hwnd ?? "(from codeloop_handle_modal)"} now. `
|
|
3850
|
+
: `Stop sending raw clicks / Escape keystrokes against it and call codeloop_handle_modal with decision: "cancel" or "dismiss" — the multi-strategy ladder (Escape → Alt+F4 → UIA Invoke "Close" → EndDialog) handles file dialogs the keystroke path cannot. If codeloop_handle_modal returns escalation: "kill_window_required", call codeloop_kill_modal_window with the returned hwnd. `;
|
|
3851
|
+
modalPersistenceDirective =
|
|
3852
|
+
`\n\n[CodeLoop F4] HARD: A ${detection.modal_kind ?? "modal"} dialog (${desc}) is STILL present after this interaction ` +
|
|
3853
|
+
`(${consecutive} consecutive interactions have not cleared it). ` +
|
|
3854
|
+
killHint +
|
|
3855
|
+
`Continuing to ignore this modal will fail the cycle_issues_acknowledged gate and block ready_for_review.`;
|
|
3856
|
+
}
|
|
3631
3857
|
}
|
|
3632
3858
|
else {
|
|
3633
3859
|
// Modal cleared — reset the tracker for this app so the next
|