codeloop-mcp-server 0.1.18 → 0.1.20
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/evidence/artifacts.d.ts.map +1 -1
- package/dist/evidence/artifacts.js +23 -5
- package/dist/evidence/artifacts.js.map +1 -1
- package/dist/evidence/interaction_coverage.d.ts +75 -0
- package/dist/evidence/interaction_coverage.d.ts.map +1 -0
- package/dist/evidence/interaction_coverage.js +347 -0
- package/dist/evidence/interaction_coverage.js.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -1
- package/dist/runners/screenshot.d.ts.map +1 -1
- package/dist/runners/screenshot.js +4 -4
- package/dist/runners/screenshot.js.map +1 -1
- package/dist/runners/video_recorder.d.ts.map +1 -1
- package/dist/runners/video_recorder.js +21 -3
- package/dist/runners/video_recorder.js.map +1 -1
- package/dist/runners/window_manager.d.ts +21 -0
- package/dist/runners/window_manager.d.ts.map +1 -1
- package/dist/runners/window_manager.js +39 -4
- package/dist/runners/window_manager.js.map +1 -1
- package/dist/tools/design_compare.d.ts.map +1 -1
- package/dist/tools/design_compare.js +25 -5
- 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 +21 -0
- package/dist/tools/gate_check.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAEvE;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAWnC;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,GACzB,IAAI,CAGN;AAED,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAmB7B;
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAEvE;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAWnC;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,GACzB,IAAI,CAGN;AAED,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAmB7B;AAYD,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBnD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjE"}
|
|
@@ -42,16 +42,34 @@ export function loadRunMeta(runIdOrDir, baseDir) {
|
|
|
42
42
|
return null;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Real run IDs generated by `generateRunId()` have the shape
|
|
47
|
+
* `run_<ms-timestamp>_<6-char-rand>`. Phantom directories (`run_visual_v2`,
|
|
48
|
+
* `run_test`, ad-hoc seed folders, etc.) silently broke design_compare and
|
|
49
|
+
* check_workflow because the old lexicographic sort placed `run_v…` before
|
|
50
|
+
* every numeric run (digits < letters in ASCII). We now require the strict
|
|
51
|
+
* shape and sort by parsed timestamp numerically, descending.
|
|
52
|
+
*/
|
|
53
|
+
const RUN_ID_PATTERN = /^run_(\d+)_[a-z0-9]+$/i;
|
|
45
54
|
export function listRuns(baseDir) {
|
|
46
55
|
const base = baseDir || getArtifactsBaseDir();
|
|
47
56
|
if (!existsSync(base)) {
|
|
48
57
|
return [];
|
|
49
58
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
.
|
|
53
|
-
|
|
54
|
-
.
|
|
59
|
+
const validRuns = [];
|
|
60
|
+
for (const entry of readdirSync(base, { withFileTypes: true })) {
|
|
61
|
+
if (!entry.isDirectory())
|
|
62
|
+
continue;
|
|
63
|
+
const match = entry.name.match(RUN_ID_PATTERN);
|
|
64
|
+
if (!match)
|
|
65
|
+
continue;
|
|
66
|
+
const ts = parseInt(match[1], 10);
|
|
67
|
+
if (Number.isNaN(ts))
|
|
68
|
+
continue;
|
|
69
|
+
validRuns.push({ name: entry.name, ts });
|
|
70
|
+
}
|
|
71
|
+
validRuns.sort((a, b) => b.ts - a.ts);
|
|
72
|
+
return validRuns.map((r) => r.name);
|
|
55
73
|
}
|
|
56
74
|
export function getRunDir(runId, baseDir) {
|
|
57
75
|
const base = baseDir || getArtifactsBaseDir();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAEtF,MAAM,UAAU,aAAa;IAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtD,OAAO,OAAO,SAAS,IAAI,MAAM,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC7D,OAAO,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,KAAc,EACd,OAAgB;IAEhB,MAAM,EAAE,GAAG,KAAK,IAAI,aAAa,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,OAAO,IAAI,mBAAmB,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAE9B,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,IAA0B;IAE1B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,OAAgB;IAEhB,IAAI,QAAgB,CAAC;IAErB,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;QAC9C,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,GAAG,OAAO,IAAI,mBAAmB,EAAE,CAAC;QAC9C,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAyB,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAgB;IACvC,MAAM,IAAI,GAAG,OAAO,IAAI,mBAAmB,EAAE,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action-type buckets used to measure how DEEPLY an interaction recording
|
|
3
|
+
* exercised an app.
|
|
4
|
+
*
|
|
5
|
+
* The earlier `interaction_replay_evidence` gate is binary — it passes if
|
|
6
|
+
* an interaction log exists at all. That's too weak. The Photometry DB
|
|
7
|
+
* E2E session ran 47 entries but 28/29 successful interactions were pure
|
|
8
|
+
* navigation; only 1 was `type` and 0 were form submissions / toggles /
|
|
9
|
+
* uploads / gestures. The replay gate still passed.
|
|
10
|
+
*
|
|
11
|
+
* These buckets let a downstream gate distinguish "I clicked a sidebar
|
|
12
|
+
* link 8 times" from "I exercised every form field, submitted, toggled
|
|
13
|
+
* modes, and dragged a slider". Buckets are intentionally coarse — fine
|
|
14
|
+
* enough to spot a navigation-only run, broad enough that the agent
|
|
15
|
+
* doesn't have to game the categories.
|
|
16
|
+
*/
|
|
17
|
+
export interface InteractionBuckets {
|
|
18
|
+
navigation: number;
|
|
19
|
+
input: number;
|
|
20
|
+
commit: number;
|
|
21
|
+
toggle: number;
|
|
22
|
+
gesture: number;
|
|
23
|
+
upload: number;
|
|
24
|
+
click: number;
|
|
25
|
+
keystroke: number;
|
|
26
|
+
inspect: number;
|
|
27
|
+
wait: number;
|
|
28
|
+
other: number;
|
|
29
|
+
}
|
|
30
|
+
export interface InteractionCoverage {
|
|
31
|
+
total: number;
|
|
32
|
+
successful: number;
|
|
33
|
+
buckets: InteractionBuckets;
|
|
34
|
+
unique_screens: number;
|
|
35
|
+
/** Aggregate "depth points" — successful actions in buckets that exercise app state. */
|
|
36
|
+
depth_actions: number;
|
|
37
|
+
}
|
|
38
|
+
export interface DepthMinimums {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
total_successful: number;
|
|
41
|
+
input: number;
|
|
42
|
+
commit: number;
|
|
43
|
+
click: number;
|
|
44
|
+
navigation: number;
|
|
45
|
+
toggle: number;
|
|
46
|
+
gesture: number;
|
|
47
|
+
upload: number;
|
|
48
|
+
}
|
|
49
|
+
export declare const DEFAULT_DEPTH_MINIMUMS: DepthMinimums;
|
|
50
|
+
/**
|
|
51
|
+
* Aggregate every interaction across every run in the project's artifacts
|
|
52
|
+
* directory. Runs without logs are skipped silently.
|
|
53
|
+
*/
|
|
54
|
+
export declare function collectInteractionCoverage(cwd: string): InteractionCoverage;
|
|
55
|
+
export interface DepthVerdict {
|
|
56
|
+
passed: boolean;
|
|
57
|
+
reason: string;
|
|
58
|
+
shortfalls: Array<{
|
|
59
|
+
bucket: keyof InteractionBuckets | "total_successful";
|
|
60
|
+
required: number;
|
|
61
|
+
have: number;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compare the observed coverage against the project's minimums. Returns
|
|
66
|
+
* the verdict plus a human-readable reason that lists EVERY shortfall —
|
|
67
|
+
* the agent uses this to know which buckets to fill in before re-gating.
|
|
68
|
+
*/
|
|
69
|
+
export declare function evaluateDepth(coverage: InteractionCoverage, minimums: DepthMinimums): DepthVerdict;
|
|
70
|
+
/**
|
|
71
|
+
* Merge user-supplied minimums onto DEFAULT_DEPTH_MINIMUMS. Unknown / non-numeric
|
|
72
|
+
* fields fall back to the default.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveDepthMinimums(override: Partial<DepthMinimums> | undefined): DepthMinimums;
|
|
75
|
+
//# sourceMappingURL=interaction_coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction_coverage.d.ts","sourceRoot":"","sources":["../../src/evidence/interaction_coverage.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAUpC,CAAC;AA+MF;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CA0C3E;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9G;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,aAAa,GACtB,YAAY,CA4Cd;AA4CD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,GAC3C,aAAa,CAqBf"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { getArtifactsBaseDir, getRunDir, listRuns } from "./artifacts.js";
|
|
4
|
+
export const DEFAULT_DEPTH_MINIMUMS = {
|
|
5
|
+
enabled: true,
|
|
6
|
+
total_successful: 20,
|
|
7
|
+
input: 3,
|
|
8
|
+
commit: 1,
|
|
9
|
+
click: 10,
|
|
10
|
+
navigation: 5,
|
|
11
|
+
toggle: 0,
|
|
12
|
+
gesture: 0,
|
|
13
|
+
upload: 0,
|
|
14
|
+
};
|
|
15
|
+
function emptyBuckets() {
|
|
16
|
+
return {
|
|
17
|
+
navigation: 0,
|
|
18
|
+
input: 0,
|
|
19
|
+
commit: 0,
|
|
20
|
+
toggle: 0,
|
|
21
|
+
gesture: 0,
|
|
22
|
+
upload: 0,
|
|
23
|
+
click: 0,
|
|
24
|
+
keystroke: 0,
|
|
25
|
+
inspect: 0,
|
|
26
|
+
wait: 0,
|
|
27
|
+
other: 0,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const COMMIT_HINT_WORDS = [
|
|
31
|
+
"submit",
|
|
32
|
+
"save",
|
|
33
|
+
"update",
|
|
34
|
+
"send",
|
|
35
|
+
"create",
|
|
36
|
+
"apply",
|
|
37
|
+
"confirm",
|
|
38
|
+
"login",
|
|
39
|
+
"log in",
|
|
40
|
+
"log-in",
|
|
41
|
+
"sign in",
|
|
42
|
+
"sign-in",
|
|
43
|
+
"sign up",
|
|
44
|
+
"sign-up",
|
|
45
|
+
"signup",
|
|
46
|
+
"register",
|
|
47
|
+
"ok",
|
|
48
|
+
"enter",
|
|
49
|
+
"search",
|
|
50
|
+
"go",
|
|
51
|
+
];
|
|
52
|
+
function isCommitClick(args) {
|
|
53
|
+
const hints = [args.selector, args.text, args.aria_label, args.label, args.target, args.automationId]
|
|
54
|
+
.filter((v) => typeof v === "string")
|
|
55
|
+
.map((v) => v.toLowerCase());
|
|
56
|
+
if (hints.length === 0)
|
|
57
|
+
return false;
|
|
58
|
+
return hints.some((h) => COMMIT_HINT_WORDS.some((w) => h.includes(w)));
|
|
59
|
+
}
|
|
60
|
+
function isCommitKey(args) {
|
|
61
|
+
const key = args.key?.toLowerCase() ?? "";
|
|
62
|
+
const combo = args.combo?.toLowerCase() ?? "";
|
|
63
|
+
return /(?:^|[+ ])(enter|return)$/.test(key) || /(?:^|[+ ])(enter|return)$/.test(combo);
|
|
64
|
+
}
|
|
65
|
+
function bucketOne(entry, buckets) {
|
|
66
|
+
const action = (entry.action ?? "").toLowerCase();
|
|
67
|
+
const args = entry.input_args ?? {};
|
|
68
|
+
const success = entry.success !== false; // default to true when absent
|
|
69
|
+
// `sequence` is a batch wrapper; walk its children and don't double-count.
|
|
70
|
+
if (action === "sequence" && Array.isArray(entry.steps)) {
|
|
71
|
+
for (const child of entry.steps)
|
|
72
|
+
bucketOne(child, buckets);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!success) {
|
|
76
|
+
// Failed interactions don't count toward coverage but we still bucket
|
|
77
|
+
// them so the agent can see what it tried. Keep simple: skip entirely.
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
switch (action) {
|
|
81
|
+
case "navigate_url":
|
|
82
|
+
case "navigate_back":
|
|
83
|
+
case "navigate_forward":
|
|
84
|
+
case "deep_link":
|
|
85
|
+
case "back_button":
|
|
86
|
+
case "home_button":
|
|
87
|
+
buckets.navigation += 1;
|
|
88
|
+
return;
|
|
89
|
+
case "type":
|
|
90
|
+
case "fill":
|
|
91
|
+
buckets.input += 1;
|
|
92
|
+
return;
|
|
93
|
+
case "type_and_submit":
|
|
94
|
+
case "type_and_tab":
|
|
95
|
+
buckets.input += 1;
|
|
96
|
+
buckets.commit += 1;
|
|
97
|
+
return;
|
|
98
|
+
case "fill_form": {
|
|
99
|
+
const fields = args.fields ?? [];
|
|
100
|
+
buckets.input += Math.max(1, fields.length);
|
|
101
|
+
if (args.submit_selector || args.submit)
|
|
102
|
+
buckets.commit += 1;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
case "toggle":
|
|
106
|
+
case "select_option":
|
|
107
|
+
case "select":
|
|
108
|
+
buckets.toggle += 1;
|
|
109
|
+
return;
|
|
110
|
+
case "scroll":
|
|
111
|
+
case "drag_drop":
|
|
112
|
+
case "swipe":
|
|
113
|
+
case "long_press":
|
|
114
|
+
case "hover":
|
|
115
|
+
case "pinch":
|
|
116
|
+
case "rotate_device":
|
|
117
|
+
buckets.gesture += 1;
|
|
118
|
+
return;
|
|
119
|
+
case "upload_file":
|
|
120
|
+
buckets.upload += 1;
|
|
121
|
+
return;
|
|
122
|
+
case "wait":
|
|
123
|
+
case "sleep":
|
|
124
|
+
buckets.wait += 1;
|
|
125
|
+
return;
|
|
126
|
+
case "win_ui_inspect":
|
|
127
|
+
buckets.inspect += 1;
|
|
128
|
+
return;
|
|
129
|
+
case "click":
|
|
130
|
+
case "double_click":
|
|
131
|
+
case "right_click":
|
|
132
|
+
case "tap":
|
|
133
|
+
if (isCommitClick(args))
|
|
134
|
+
buckets.commit += 1;
|
|
135
|
+
else
|
|
136
|
+
buckets.click += 1;
|
|
137
|
+
return;
|
|
138
|
+
case "keystroke":
|
|
139
|
+
case "hotkey":
|
|
140
|
+
if (isCommitKey(args))
|
|
141
|
+
buckets.commit += 1;
|
|
142
|
+
else
|
|
143
|
+
buckets.keystroke += 1;
|
|
144
|
+
return;
|
|
145
|
+
case "win_ui_automate": {
|
|
146
|
+
// Sub-action lives in args.action for UIA dispatch.
|
|
147
|
+
const sub = args.action?.toLowerCase() ?? "";
|
|
148
|
+
if (sub === "setvalue" || sub === "set_value") {
|
|
149
|
+
buckets.input += 1;
|
|
150
|
+
}
|
|
151
|
+
else if (sub === "toggle") {
|
|
152
|
+
buckets.toggle += 1;
|
|
153
|
+
}
|
|
154
|
+
else if (sub === "invoke") {
|
|
155
|
+
// Invoke on a UIA button — treat like a click; flag commits via name hint.
|
|
156
|
+
if (isCommitClick(args))
|
|
157
|
+
buckets.commit += 1;
|
|
158
|
+
else
|
|
159
|
+
buckets.click += 1;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
buckets.other += 1;
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
default:
|
|
167
|
+
buckets.other += 1;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function parseLogFile(path) {
|
|
172
|
+
try {
|
|
173
|
+
const raw = readFileSync(path, "utf-8");
|
|
174
|
+
const out = [];
|
|
175
|
+
for (const line of raw.split("\n")) {
|
|
176
|
+
const trimmed = line.trim();
|
|
177
|
+
if (!trimmed)
|
|
178
|
+
continue;
|
|
179
|
+
try {
|
|
180
|
+
out.push(JSON.parse(trimmed));
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
/* skip malformed */
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function uniqueScreens(entries) {
|
|
193
|
+
const seen = new Set();
|
|
194
|
+
for (const e of entries) {
|
|
195
|
+
const args = e.input_args ?? {};
|
|
196
|
+
const url = typeof args.url === "string" ? args.url : "";
|
|
197
|
+
const app = typeof args.app_name === "string" ? args.app_name : "";
|
|
198
|
+
const key = url || app;
|
|
199
|
+
if (key)
|
|
200
|
+
seen.add(key);
|
|
201
|
+
}
|
|
202
|
+
return seen.size;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Aggregate every interaction across every run in the project's artifacts
|
|
206
|
+
* directory. Runs without logs are skipped silently.
|
|
207
|
+
*/
|
|
208
|
+
export function collectInteractionCoverage(cwd) {
|
|
209
|
+
const baseDir = getArtifactsBaseDir(cwd);
|
|
210
|
+
const buckets = emptyBuckets();
|
|
211
|
+
const allEntries = [];
|
|
212
|
+
if (!existsSync(baseDir)) {
|
|
213
|
+
return { total: 0, successful: 0, buckets, unique_screens: 0, depth_actions: 0 };
|
|
214
|
+
}
|
|
215
|
+
const runs = listRuns(baseDir);
|
|
216
|
+
for (const runId of runs) {
|
|
217
|
+
const logsDir = join(getRunDir(runId, baseDir), "logs");
|
|
218
|
+
if (!existsSync(logsDir))
|
|
219
|
+
continue;
|
|
220
|
+
const files = readdirSync(logsDir).filter((f) => f === "interaction_log.jsonl" || f.startsWith("interaction_log") && f.endsWith(".jsonl"));
|
|
221
|
+
for (const f of files) {
|
|
222
|
+
const entries = parseLogFile(join(logsDir, f));
|
|
223
|
+
allEntries.push(...entries);
|
|
224
|
+
for (const e of entries)
|
|
225
|
+
bucketOne(e, buckets);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const total = allEntries.length;
|
|
229
|
+
const successful = allEntries.filter((e) => e.success !== false).length;
|
|
230
|
+
const depthActions = buckets.navigation +
|
|
231
|
+
buckets.input +
|
|
232
|
+
buckets.commit +
|
|
233
|
+
buckets.toggle +
|
|
234
|
+
buckets.gesture +
|
|
235
|
+
buckets.upload +
|
|
236
|
+
buckets.click +
|
|
237
|
+
buckets.keystroke;
|
|
238
|
+
return {
|
|
239
|
+
total,
|
|
240
|
+
successful,
|
|
241
|
+
buckets,
|
|
242
|
+
unique_screens: uniqueScreens(allEntries),
|
|
243
|
+
depth_actions: depthActions,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Compare the observed coverage against the project's minimums. Returns
|
|
248
|
+
* the verdict plus a human-readable reason that lists EVERY shortfall —
|
|
249
|
+
* the agent uses this to know which buckets to fill in before re-gating.
|
|
250
|
+
*/
|
|
251
|
+
export function evaluateDepth(coverage, minimums) {
|
|
252
|
+
if (!minimums.enabled) {
|
|
253
|
+
return {
|
|
254
|
+
passed: true,
|
|
255
|
+
reason: "Interaction depth gate is disabled in .codeloop/config.json (interaction_depth_minimums.enabled=false).",
|
|
256
|
+
shortfalls: [],
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const shortfalls = [];
|
|
260
|
+
const checks = [
|
|
261
|
+
{ bucket: "total_successful", required: minimums.total_successful, have: coverage.successful },
|
|
262
|
+
{ bucket: "input", required: minimums.input, have: coverage.buckets.input },
|
|
263
|
+
{ bucket: "commit", required: minimums.commit, have: coverage.buckets.commit },
|
|
264
|
+
{ bucket: "click", required: minimums.click, have: coverage.buckets.click },
|
|
265
|
+
{ bucket: "navigation", required: minimums.navigation, have: coverage.buckets.navigation },
|
|
266
|
+
{ bucket: "toggle", required: minimums.toggle, have: coverage.buckets.toggle },
|
|
267
|
+
{ bucket: "gesture", required: minimums.gesture, have: coverage.buckets.gesture },
|
|
268
|
+
{ bucket: "upload", required: minimums.upload, have: coverage.buckets.upload },
|
|
269
|
+
];
|
|
270
|
+
for (const c of checks) {
|
|
271
|
+
if (c.required > 0 && c.have < c.required)
|
|
272
|
+
shortfalls.push(c);
|
|
273
|
+
}
|
|
274
|
+
if (shortfalls.length === 0) {
|
|
275
|
+
const b = coverage.buckets;
|
|
276
|
+
return {
|
|
277
|
+
passed: true,
|
|
278
|
+
reason: `Deep interaction coverage met: ${coverage.successful} successful actions (click=${b.click}, navigation=${b.navigation}, input=${b.input}, commit=${b.commit}, toggle=${b.toggle}, gesture=${b.gesture}, upload=${b.upload}).`,
|
|
279
|
+
shortfalls: [],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const lines = shortfalls.map((s) => ` - ${s.bucket}: need >= ${s.required}, have ${s.have}`);
|
|
283
|
+
const hint = buildHint(shortfalls);
|
|
284
|
+
return {
|
|
285
|
+
passed: false,
|
|
286
|
+
reason: `Deep interaction coverage NOT met. Shortfalls:\n${lines.join("\n")}\n${hint}`,
|
|
287
|
+
shortfalls,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function buildHint(shortfalls) {
|
|
291
|
+
const tips = [];
|
|
292
|
+
const has = (b) => shortfalls.some((s) => s.bucket === b);
|
|
293
|
+
if (has("input")) {
|
|
294
|
+
tips.push('Fill input fields with codeloop_interact action: "type" or "fill_form" (or "win_ui_automate" with action: "setValue" on a WPF TextBox).');
|
|
295
|
+
}
|
|
296
|
+
if (has("commit")) {
|
|
297
|
+
tips.push('Submit at least one form: click a button whose name contains submit/save/update/login/signup, or send action "type_and_submit" / "hotkey" enter after typing.');
|
|
298
|
+
}
|
|
299
|
+
if (has("toggle")) {
|
|
300
|
+
tips.push('Exercise a toggle, switch, or dropdown via action: "toggle" / "select_option" (or "win_ui_automate" with action: "toggle" on a ToggleSwitch).');
|
|
301
|
+
}
|
|
302
|
+
if (has("click")) {
|
|
303
|
+
tips.push('Click more interactive elements — buttons, list items, expanders, tabs. Pure navigation does not count toward click coverage.');
|
|
304
|
+
}
|
|
305
|
+
if (has("navigation")) {
|
|
306
|
+
tips.push('Visit more screens. Use action: "navigate_url" for web, "click" on sidebar/menu items for desktop, "deep_link" for mobile.');
|
|
307
|
+
}
|
|
308
|
+
if (has("gesture")) {
|
|
309
|
+
tips.push('Add at least one gesture: scroll (action: "scroll"), drag (action: "drag_drop"), swipe (action: "swipe" on mobile), or hover (action: "hover").');
|
|
310
|
+
}
|
|
311
|
+
if (has("upload")) {
|
|
312
|
+
tips.push('Exercise a file upload via action: "upload_file".');
|
|
313
|
+
}
|
|
314
|
+
if (has("total_successful")) {
|
|
315
|
+
tips.push("Overall successful-interaction count is too low; combine the above to reach the minimum.");
|
|
316
|
+
}
|
|
317
|
+
return tips.length > 0 ? `Next steps:\n * ${tips.join("\n * ")}` : "";
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Merge user-supplied minimums onto DEFAULT_DEPTH_MINIMUMS. Unknown / non-numeric
|
|
321
|
+
* fields fall back to the default.
|
|
322
|
+
*/
|
|
323
|
+
export function resolveDepthMinimums(override) {
|
|
324
|
+
if (!override)
|
|
325
|
+
return DEFAULT_DEPTH_MINIMUMS;
|
|
326
|
+
const out = { ...DEFAULT_DEPTH_MINIMUMS };
|
|
327
|
+
if (typeof override.enabled === "boolean")
|
|
328
|
+
out.enabled = override.enabled;
|
|
329
|
+
const numFields = [
|
|
330
|
+
"total_successful",
|
|
331
|
+
"input",
|
|
332
|
+
"commit",
|
|
333
|
+
"click",
|
|
334
|
+
"navigation",
|
|
335
|
+
"toggle",
|
|
336
|
+
"gesture",
|
|
337
|
+
"upload",
|
|
338
|
+
];
|
|
339
|
+
for (const k of numFields) {
|
|
340
|
+
const v = override[k];
|
|
341
|
+
if (typeof v === "number" && v >= 0 && Number.isFinite(v)) {
|
|
342
|
+
out[k] = v;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return out;
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=interaction_coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction_coverage.js","sourceRoot":"","sources":["../../src/evidence/interaction_coverage.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -313,6 +313,7 @@ Returns: per-screen pixel diff scores + worst-failing reference, actual, and dif
|
|
|
313
313
|
figma_file_url: z.string().optional().describe("Figma file URL or key. Triggers a Figma sync into designs/ before comparing."),
|
|
314
314
|
figma_token: z.string().optional().describe("Figma personal access token. Falls back to FIGMA_API_TOKEN env var."),
|
|
315
315
|
designs_dir: z.string().optional().describe("Override the default designs/ folder."),
|
|
316
|
+
run_id: z.string().optional().describe("Explicit run_id whose screenshots/ folder should be the source of actuals. When omitted, design_compare picks the newest run that has PNGs. Honour this when the caller has just captured fresh screenshots into a specific run so a stale or seed run cannot shadow the comparison."),
|
|
316
317
|
project_dir: z.string().optional().describe("Absolute path to the project root. Defaults to CODELOOP_PROJECT_DIR or cwd."),
|
|
317
318
|
}, async (params) => {
|
|
318
319
|
const authResult = await withAuth(async () => {
|
|
@@ -327,6 +328,7 @@ Returns: per-screen pixel diff scores + worst-failing reference, actual, and dif
|
|
|
327
328
|
figma_file_url: params.figma_file_url,
|
|
328
329
|
figma_token: params.figma_token,
|
|
329
330
|
designs_dir: params.designs_dir,
|
|
331
|
+
run_id: params.run_id,
|
|
330
332
|
};
|
|
331
333
|
const cwd = params.project_dir || projectDir;
|
|
332
334
|
const result = await runDesignCompare(input, config, cwd);
|
|
@@ -1287,6 +1289,10 @@ Returns: checklist of completed and pending verification steps.`, {
|
|
|
1287
1289
|
const coveredRoutes = new Set();
|
|
1288
1290
|
let discoveredRoutes = [];
|
|
1289
1291
|
let uncoveredRoutes = [];
|
|
1292
|
+
// Deep-interaction bucket breakdown — set even for non-UI projects so
|
|
1293
|
+
// the diagnostic step can show "n/a" with the same shape.
|
|
1294
|
+
let depthDetail = "Not a UI project — interaction depth not applicable";
|
|
1295
|
+
let depthStatus = "n/a";
|
|
1290
1296
|
if (isUIProject) {
|
|
1291
1297
|
try {
|
|
1292
1298
|
const { readFileSync: rfs } = await import("fs");
|
|
@@ -1319,6 +1325,28 @@ Returns: checklist of completed and pending verification steps.`, {
|
|
|
1319
1325
|
uncoveredRoutes = discoveredRoutes.filter(route => {
|
|
1320
1326
|
return !coveredRoutes.has(route) && ![...coveredRoutes].some(c => c.includes(route) || route.includes(c));
|
|
1321
1327
|
});
|
|
1328
|
+
// Deep-interaction depth check — same machinery the
|
|
1329
|
+
// interaction_depth_evidence gate uses, surfaced HERE so the
|
|
1330
|
+
// agent sees "input=1, commit=0 — need >= 3 / >= 1" BEFORE
|
|
1331
|
+
// calling gate_check and gets a structured next step.
|
|
1332
|
+
const { collectInteractionCoverage, evaluateDepth, resolveDepthMinimums, } = await import("./evidence/interaction_coverage.js");
|
|
1333
|
+
const minimums = resolveDepthMinimums(config.interaction_depth_minimums);
|
|
1334
|
+
const coverage = collectInteractionCoverage(cwd);
|
|
1335
|
+
const verdict = evaluateDepth(coverage, minimums);
|
|
1336
|
+
const b = coverage.buckets;
|
|
1337
|
+
const breakdown = `click=${b.click}, navigation=${b.navigation}, input=${b.input}, commit=${b.commit}, toggle=${b.toggle}, gesture=${b.gesture}, upload=${b.upload}, keystroke=${b.keystroke}, inspect=${b.inspect}`;
|
|
1338
|
+
if (!minimums.enabled) {
|
|
1339
|
+
depthStatus = "n/a";
|
|
1340
|
+
depthDetail = `Depth gate disabled in .codeloop/config.json. Observed buckets: ${breakdown}.`;
|
|
1341
|
+
}
|
|
1342
|
+
else if (verdict.passed) {
|
|
1343
|
+
depthStatus = "done";
|
|
1344
|
+
depthDetail = `${coverage.successful} successful interactions across ${runs.length} run(s) (${breakdown}). Depth minimums met.`;
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
depthStatus = "PENDING";
|
|
1348
|
+
depthDetail = `${coverage.successful} successful interactions across ${runs.length} run(s) (${breakdown}). ${verdict.reason}`;
|
|
1349
|
+
}
|
|
1322
1350
|
}
|
|
1323
1351
|
catch { /* best-effort coverage check */ }
|
|
1324
1352
|
}
|
|
@@ -1381,6 +1409,11 @@ Returns: checklist of completed and pending verification steps.`, {
|
|
|
1381
1409
|
? `All ${discoveredRoutes.length} discovered routes covered by ${interactionCount} interactions`
|
|
1382
1410
|
: `${uncoveredRoutes.length} of ${discoveredRoutes.length} routes NOT covered by interactions. Uncovered: ${uncoveredRoutes.join(", ")}. Navigate to these routes and interact with their elements.`,
|
|
1383
1411
|
},
|
|
1412
|
+
{
|
|
1413
|
+
step: "7. Deep interaction depth",
|
|
1414
|
+
status: depthStatus,
|
|
1415
|
+
detail: depthDetail,
|
|
1416
|
+
},
|
|
1384
1417
|
];
|
|
1385
1418
|
const pendingSteps = steps.filter(s => s.status === "PENDING" || s.status === "NEEDS_RERUN");
|
|
1386
1419
|
const allDone = pendingSteps.length === 0;
|