agentskeptic 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +342 -342
- package/dist/cli.js +157 -157
- package/dist/debug-ui/app.css +188 -188
- package/dist/debug-ui/app.js +245 -245
- package/dist/debug-ui/index.html +79 -79
- package/dist/enforceCli.js +11 -11
- package/dist/planTransition.test.js +235 -235
- package/dist/planTransitionPathHarvest.test.js +116 -116
- package/dist/quickVerify/postgresCatalog.js +53 -53
- package/package.json +1 -1
- package/schemas/agent-run-record-v1.schema.json +51 -51
- package/schemas/agent-run-record-v2.schema.json +61 -61
- package/schemas/assurance-manifest-v1.schema.json +28 -28
- package/schemas/assurance-run-report-v1.schema.json +28 -28
- package/schemas/ci-lock-v1.schema.json +163 -163
- package/schemas/cli-error-envelope.schema.json +48 -48
- package/schemas/event.schema.json +111 -111
- package/schemas/execution-trace-view.schema.json +122 -122
- package/schemas/plan-validation-core.schema.json +95 -95
- package/schemas/quick-verify-report.schema.json +251 -251
- package/schemas/registry-validation-result.schema.json +99 -99
- package/schemas/run-comparison-report.schema.json +513 -513
- package/schemas/tools-registry-export.schema.json +9 -9
- package/schemas/tools-registry.schema.json +284 -284
- package/schemas/workflow-engine-result.schema.json +591 -591
- package/schemas/workflow-result-compare-input.schema.json +15 -15
- package/schemas/workflow-result-signature.schema.json +20 -20
- package/schemas/workflow-result-v9.schema.json +85 -85
- package/schemas/workflow-result.schema.json +80 -80
- package/schemas/workflow-truth-report.schema.json +761 -761
- package/dist/failureOriginSchemaEnum.test.d.ts +0 -2
- package/dist/failureOriginSchemaEnum.test.d.ts.map +0 -1
- package/dist/failureOriginSchemaEnum.test.js +0 -21
- package/dist/failureOriginSchemaEnum.test.js.map +0 -1
- package/dist/failureOriginSchemaParity.test.d.ts +0 -2
- package/dist/failureOriginSchemaParity.test.d.ts.map +0 -1
- package/dist/failureOriginSchemaParity.test.js +0 -33
- package/dist/failureOriginSchemaParity.test.js.map +0 -1
- package/dist/slice6.compare.ac.test.d.ts +0 -2
- package/dist/slice6.compare.ac.test.d.ts.map +0 -1
- package/dist/slice6.compare.ac.test.js +0 -81
- package/dist/slice6.compare.ac.test.js.map +0 -1
package/dist/debug-ui/app.js
CHANGED
|
@@ -1,245 +1,245 @@
|
|
|
1
|
-
/* global fetch, URLSearchParams, document, window */
|
|
2
|
-
|
|
3
|
-
const state = {
|
|
4
|
-
nextCursor: null,
|
|
5
|
-
filterParams: new URLSearchParams(),
|
|
6
|
-
selected: new Set(),
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
function api(path, opts) {
|
|
10
|
-
return fetch(path, opts).then(async (r) => {
|
|
11
|
-
const text = await r.text();
|
|
12
|
-
let data;
|
|
13
|
-
try {
|
|
14
|
-
data = text ? JSON.parse(text) : null;
|
|
15
|
-
} catch {
|
|
16
|
-
data = { _raw: text };
|
|
17
|
-
}
|
|
18
|
-
if (!r.ok) {
|
|
19
|
-
const err = new Error(data?.message || r.statusText);
|
|
20
|
-
err.status = r.status;
|
|
21
|
-
err.data = data;
|
|
22
|
-
throw err;
|
|
23
|
-
}
|
|
24
|
-
return data;
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function buildFilterParamsFromForm() {
|
|
29
|
-
const form = document.getElementById("filters");
|
|
30
|
-
const fd = new FormData(form);
|
|
31
|
-
const p = new URLSearchParams();
|
|
32
|
-
for (const [k, v] of fd.entries()) {
|
|
33
|
-
if (k === "includeLoadErrors" || k === "hasPathFindings") continue;
|
|
34
|
-
if (v !== "" && v != null) {
|
|
35
|
-
p.set(k, String(v));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// Unchecked checkboxes are omitted from FormData.
|
|
39
|
-
const includeLoadErrorsEl = form.querySelector('[name="includeLoadErrors"]');
|
|
40
|
-
if (includeLoadErrorsEl && !includeLoadErrorsEl.checked) {
|
|
41
|
-
p.set("includeLoadErrors", "false");
|
|
42
|
-
}
|
|
43
|
-
const hasPathFindingsEl = form.querySelector('[name="hasPathFindings"]');
|
|
44
|
-
if (hasPathFindingsEl && hasPathFindingsEl.checked) {
|
|
45
|
-
p.set("hasPathFindings", "true");
|
|
46
|
-
}
|
|
47
|
-
return p;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function loadRuns(append) {
|
|
51
|
-
const p = new URLSearchParams(state.filterParams);
|
|
52
|
-
p.set("limit", "100");
|
|
53
|
-
if (append && state.nextCursor) p.set("cursor", state.nextCursor);
|
|
54
|
-
else state.nextCursor = null;
|
|
55
|
-
|
|
56
|
-
const data = await api(`/api/runs?${p.toString()}`);
|
|
57
|
-
const tbody = document.getElementById("runs-body");
|
|
58
|
-
if (!append) {
|
|
59
|
-
tbody.innerHTML = "";
|
|
60
|
-
state.selected.clear();
|
|
61
|
-
}
|
|
62
|
-
document.getElementById("runs-meta").textContent = `totalMatched=${data.totalMatched} showing ${tbody.children.length + data.items.length} (paged)`;
|
|
63
|
-
for (const row of data.items) {
|
|
64
|
-
const tr = document.createElement("tr");
|
|
65
|
-
tr.className = row.loadStatus === "error" ? "load-error" : "load-ok";
|
|
66
|
-
tr.dataset.runId = row.runId;
|
|
67
|
-
const codes = (row.primaryReasonCodes || []).slice(0, 6).join(", ");
|
|
68
|
-
const pathCodes = (row.pathFindingCodes || []).slice(0, 4).join(", ");
|
|
69
|
-
tr.innerHTML = `
|
|
70
|
-
<td><input type="checkbox" class="pick" aria-label="select ${row.runId}" /></td>
|
|
71
|
-
<td><button type="button" class="open-run">${escapeHtml(row.runId)}</button></td>
|
|
72
|
-
<td>${escapeHtml(row.loadStatus)}</td>
|
|
73
|
-
<td>${escapeHtml(row.workflowId || "—")}</td>
|
|
74
|
-
<td>${escapeHtml(row.status || "—")}</td>
|
|
75
|
-
<td>${escapeHtml(row.actionableCategory || "—")}</td>
|
|
76
|
-
<td>${escapeHtml(row.customerId || "—")}</td>
|
|
77
|
-
<td>${escapeHtml(pathCodes || "—")}</td>
|
|
78
|
-
<td>${escapeHtml(codes)}</td>
|
|
79
|
-
`;
|
|
80
|
-
tr.querySelector(".open-run").addEventListener("click", () => openDetail(row.runId));
|
|
81
|
-
tr.querySelector(".pick").addEventListener("change", (ev) => {
|
|
82
|
-
if (ev.target.checked) {
|
|
83
|
-
state.selected.add(row.runId);
|
|
84
|
-
tr.classList.add("selected");
|
|
85
|
-
} else {
|
|
86
|
-
state.selected.delete(row.runId);
|
|
87
|
-
tr.classList.remove("selected");
|
|
88
|
-
}
|
|
89
|
-
document.getElementById("run-compare").disabled = state.selected.size < 2;
|
|
90
|
-
});
|
|
91
|
-
tbody.appendChild(tr);
|
|
92
|
-
}
|
|
93
|
-
state.nextCursor = data.nextCursor;
|
|
94
|
-
document.getElementById("load-more").hidden = !data.nextCursor;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function escapeHtml(s) {
|
|
98
|
-
return String(s)
|
|
99
|
-
.replace(/&/g, "&")
|
|
100
|
-
.replace(/</g, "<")
|
|
101
|
-
.replace(/>/g, ">")
|
|
102
|
-
.replace(/"/g, """);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function formatVerdictSurface(vs) {
|
|
106
|
-
if (!vs || typeof vs !== "object") return "";
|
|
107
|
-
const counts = vs.stepStatusCounts || {};
|
|
108
|
-
const parts = Object.entries(counts)
|
|
109
|
-
.filter(([, n]) => n > 0)
|
|
110
|
-
.map(([k, n]) => `${k}: ${n}`);
|
|
111
|
-
const countsLine = parts.length ? parts.join(" · ") : "(no steps)";
|
|
112
|
-
return `
|
|
113
|
-
<div class="verdict-panel" role="region" aria-label="Workflow verdict">
|
|
114
|
-
<div class="verdict-status">Workflow status: <code>${escapeHtml(vs.status)}</code></div>
|
|
115
|
-
<div class="verdict-trust">${escapeHtml(vs.trustSummary || "")}</div>
|
|
116
|
-
<div class="verdict-counts">Step outcomes: ${escapeHtml(countsLine)}</div>
|
|
117
|
-
</div>`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async function openDetail(runId) {
|
|
121
|
-
const drawer = document.getElementById("detail-drawer");
|
|
122
|
-
const body = document.getElementById("detail-body");
|
|
123
|
-
const title = document.getElementById("detail-title");
|
|
124
|
-
drawer.classList.remove("hidden");
|
|
125
|
-
title.textContent = `Run: ${runId}`;
|
|
126
|
-
body.innerHTML = "<p>Loading…</p>";
|
|
127
|
-
try {
|
|
128
|
-
const data = await api(`/api/runs/${encodeURIComponent(runId)}`);
|
|
129
|
-
if (data.loadStatus === "error") {
|
|
130
|
-
body.innerHTML = `
|
|
131
|
-
<p class="focus-panel"><strong>Load error</strong> <code>${escapeHtml(data.error.code)}</code></p>
|
|
132
|
-
<pre class="json-out">${escapeHtml(JSON.stringify(data, null, 2))}</pre>
|
|
133
|
-
`;
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
let focusHtml = "";
|
|
137
|
-
const focusSet = new Set();
|
|
138
|
-
try {
|
|
139
|
-
const focus = await api(`/api/runs/${encodeURIComponent(runId)}/focus`);
|
|
140
|
-
focusHtml = `<div class="focus-panel"><strong>Focus targets (from API)</strong><pre>${escapeHtml(JSON.stringify(focus.targets, null, 2))}</pre></div>`;
|
|
141
|
-
for (const t of focus.targets) {
|
|
142
|
-
if (t.kind === "seq") focusSet.add(`seq:${t.value}`);
|
|
143
|
-
if (t.kind === "ingestIndex") focusSet.add(`ingest:${t.value}`);
|
|
144
|
-
if (t.kind === "runEventId") focusSet.add(`runEventId:${t.value}`);
|
|
145
|
-
}
|
|
146
|
-
} catch (e) {
|
|
147
|
-
focusHtml = `<p class="meta">Focus: ${escapeHtml(e.message)}</p>`;
|
|
148
|
-
}
|
|
149
|
-
const verdictHtml = formatVerdictSurface(data.workflowVerdictSurface);
|
|
150
|
-
const steps = (data.executionTrace?.nodes || []).map((n, i) => {
|
|
151
|
-
const seq = n.toolSeq ?? n.verificationLink?.seq;
|
|
152
|
-
const seqKey = seq != null ? `seq:${seq}` : "";
|
|
153
|
-
const ingestKey = `ingest:${n.ingestIndex}`;
|
|
154
|
-
const runEvKey = n.runEventId ? `runEventId:${n.runEventId}` : "";
|
|
155
|
-
const hit =
|
|
156
|
-
(seqKey && focusSet.has(seqKey)) ||
|
|
157
|
-
focusSet.has(ingestKey) ||
|
|
158
|
-
(runEvKey && focusSet.has(runEvKey));
|
|
159
|
-
return `<div class="trace-step ${hit ? "focus-hit" : ""}" data-idx="${i}">${escapeHtml(JSON.stringify(n))}</div>`;
|
|
160
|
-
});
|
|
161
|
-
const trustHtml =
|
|
162
|
-
typeof data.runTrustPanelHtml === "string"
|
|
163
|
-
? `<div class="run-trust-panel">${data.runTrustPanelHtml}</div>`
|
|
164
|
-
: "";
|
|
165
|
-
body.innerHTML = `
|
|
166
|
-
${trustHtml}
|
|
167
|
-
${verdictHtml}
|
|
168
|
-
${focusHtml}
|
|
169
|
-
<h3>Trace nodes</h3>
|
|
170
|
-
${steps.join("") || "<p>(no trace nodes)</p>"}
|
|
171
|
-
<h3>WorkflowResult</h3>
|
|
172
|
-
<pre class="json-out">${escapeHtml(JSON.stringify(data.workflowResult, null, 2))}</pre>
|
|
173
|
-
`;
|
|
174
|
-
} catch (e) {
|
|
175
|
-
body.innerHTML = `<p class="focus-panel">Error: ${escapeHtml(e.message)}</p>`;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
document.getElementById("filters").addEventListener("submit", (ev) => {
|
|
180
|
-
ev.preventDefault();
|
|
181
|
-
state.filterParams = buildFilterParamsFromForm();
|
|
182
|
-
loadRuns(false).catch((e) => alert(e.message));
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
document.getElementById("clear-filters").addEventListener("click", () => {
|
|
186
|
-
document.getElementById("filters").reset();
|
|
187
|
-
document.querySelector('[name="includeLoadErrors"]').checked = true;
|
|
188
|
-
state.filterParams = buildFilterParamsFromForm();
|
|
189
|
-
loadRuns(false).catch((e) => alert(e.message));
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
document.getElementById("load-more").addEventListener("click", () => {
|
|
193
|
-
loadRuns(true).catch((e) => alert(e.message));
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
document.querySelectorAll(".tab").forEach((btn) => {
|
|
197
|
-
btn.addEventListener("click", () => {
|
|
198
|
-
document.querySelectorAll(".tab").forEach((b) => b.classList.remove("active"));
|
|
199
|
-
document.querySelectorAll(".panel").forEach((p) => p.classList.remove("active"));
|
|
200
|
-
btn.classList.add("active");
|
|
201
|
-
document.getElementById(`panel-${btn.dataset.tab}`).classList.add("active");
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
document.getElementById("refresh-patterns").addEventListener("click", async () => {
|
|
206
|
-
const out = document.getElementById("patterns-out");
|
|
207
|
-
out.textContent = "Loading…";
|
|
208
|
-
const p = new URLSearchParams(state.filterParams);
|
|
209
|
-
try {
|
|
210
|
-
const data = await api(`/api/corpus-patterns?${p.toString()}`);
|
|
211
|
-
out.textContent = JSON.stringify(data, null, 2);
|
|
212
|
-
} catch (e) {
|
|
213
|
-
out.textContent = JSON.stringify(e.data || { message: e.message }, null, 2);
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
document.getElementById("run-compare").addEventListener("click", async () => {
|
|
218
|
-
const ids = [...state.selected];
|
|
219
|
-
const out = document.getElementById("compare-out");
|
|
220
|
-
out.textContent = "Loading…";
|
|
221
|
-
try {
|
|
222
|
-
const data = await api("/api/compare", {
|
|
223
|
-
method: "POST",
|
|
224
|
-
headers: { "Content-Type": "application/json" },
|
|
225
|
-
body: JSON.stringify({ runIds: ids }),
|
|
226
|
-
});
|
|
227
|
-
if (typeof data.comparePanelHtml === "string") {
|
|
228
|
-
out.innerHTML = data.comparePanelHtml;
|
|
229
|
-
} else {
|
|
230
|
-
out.textContent = "comparePanelHtml missing in response.";
|
|
231
|
-
}
|
|
232
|
-
} catch (e) {
|
|
233
|
-
out.textContent = "";
|
|
234
|
-
out.appendChild(document.createTextNode(JSON.stringify(e.data || { message: e.message }, null, 2)));
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
document.getElementById("close-detail").addEventListener("click", () => {
|
|
239
|
-
document.getElementById("detail-drawer").classList.add("hidden");
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
state.filterParams = buildFilterParamsFromForm();
|
|
243
|
-
loadRuns(false).catch((e) => {
|
|
244
|
-
document.getElementById("runs-meta").textContent = `Failed to load: ${e.message}`;
|
|
245
|
-
});
|
|
1
|
+
/* global fetch, URLSearchParams, document, window */
|
|
2
|
+
|
|
3
|
+
const state = {
|
|
4
|
+
nextCursor: null,
|
|
5
|
+
filterParams: new URLSearchParams(),
|
|
6
|
+
selected: new Set(),
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function api(path, opts) {
|
|
10
|
+
return fetch(path, opts).then(async (r) => {
|
|
11
|
+
const text = await r.text();
|
|
12
|
+
let data;
|
|
13
|
+
try {
|
|
14
|
+
data = text ? JSON.parse(text) : null;
|
|
15
|
+
} catch {
|
|
16
|
+
data = { _raw: text };
|
|
17
|
+
}
|
|
18
|
+
if (!r.ok) {
|
|
19
|
+
const err = new Error(data?.message || r.statusText);
|
|
20
|
+
err.status = r.status;
|
|
21
|
+
err.data = data;
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
return data;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildFilterParamsFromForm() {
|
|
29
|
+
const form = document.getElementById("filters");
|
|
30
|
+
const fd = new FormData(form);
|
|
31
|
+
const p = new URLSearchParams();
|
|
32
|
+
for (const [k, v] of fd.entries()) {
|
|
33
|
+
if (k === "includeLoadErrors" || k === "hasPathFindings") continue;
|
|
34
|
+
if (v !== "" && v != null) {
|
|
35
|
+
p.set(k, String(v));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Unchecked checkboxes are omitted from FormData.
|
|
39
|
+
const includeLoadErrorsEl = form.querySelector('[name="includeLoadErrors"]');
|
|
40
|
+
if (includeLoadErrorsEl && !includeLoadErrorsEl.checked) {
|
|
41
|
+
p.set("includeLoadErrors", "false");
|
|
42
|
+
}
|
|
43
|
+
const hasPathFindingsEl = form.querySelector('[name="hasPathFindings"]');
|
|
44
|
+
if (hasPathFindingsEl && hasPathFindingsEl.checked) {
|
|
45
|
+
p.set("hasPathFindings", "true");
|
|
46
|
+
}
|
|
47
|
+
return p;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function loadRuns(append) {
|
|
51
|
+
const p = new URLSearchParams(state.filterParams);
|
|
52
|
+
p.set("limit", "100");
|
|
53
|
+
if (append && state.nextCursor) p.set("cursor", state.nextCursor);
|
|
54
|
+
else state.nextCursor = null;
|
|
55
|
+
|
|
56
|
+
const data = await api(`/api/runs?${p.toString()}`);
|
|
57
|
+
const tbody = document.getElementById("runs-body");
|
|
58
|
+
if (!append) {
|
|
59
|
+
tbody.innerHTML = "";
|
|
60
|
+
state.selected.clear();
|
|
61
|
+
}
|
|
62
|
+
document.getElementById("runs-meta").textContent = `totalMatched=${data.totalMatched} showing ${tbody.children.length + data.items.length} (paged)`;
|
|
63
|
+
for (const row of data.items) {
|
|
64
|
+
const tr = document.createElement("tr");
|
|
65
|
+
tr.className = row.loadStatus === "error" ? "load-error" : "load-ok";
|
|
66
|
+
tr.dataset.runId = row.runId;
|
|
67
|
+
const codes = (row.primaryReasonCodes || []).slice(0, 6).join(", ");
|
|
68
|
+
const pathCodes = (row.pathFindingCodes || []).slice(0, 4).join(", ");
|
|
69
|
+
tr.innerHTML = `
|
|
70
|
+
<td><input type="checkbox" class="pick" aria-label="select ${row.runId}" /></td>
|
|
71
|
+
<td><button type="button" class="open-run">${escapeHtml(row.runId)}</button></td>
|
|
72
|
+
<td>${escapeHtml(row.loadStatus)}</td>
|
|
73
|
+
<td>${escapeHtml(row.workflowId || "—")}</td>
|
|
74
|
+
<td>${escapeHtml(row.status || "—")}</td>
|
|
75
|
+
<td>${escapeHtml(row.actionableCategory || "—")}</td>
|
|
76
|
+
<td>${escapeHtml(row.customerId || "—")}</td>
|
|
77
|
+
<td>${escapeHtml(pathCodes || "—")}</td>
|
|
78
|
+
<td>${escapeHtml(codes)}</td>
|
|
79
|
+
`;
|
|
80
|
+
tr.querySelector(".open-run").addEventListener("click", () => openDetail(row.runId));
|
|
81
|
+
tr.querySelector(".pick").addEventListener("change", (ev) => {
|
|
82
|
+
if (ev.target.checked) {
|
|
83
|
+
state.selected.add(row.runId);
|
|
84
|
+
tr.classList.add("selected");
|
|
85
|
+
} else {
|
|
86
|
+
state.selected.delete(row.runId);
|
|
87
|
+
tr.classList.remove("selected");
|
|
88
|
+
}
|
|
89
|
+
document.getElementById("run-compare").disabled = state.selected.size < 2;
|
|
90
|
+
});
|
|
91
|
+
tbody.appendChild(tr);
|
|
92
|
+
}
|
|
93
|
+
state.nextCursor = data.nextCursor;
|
|
94
|
+
document.getElementById("load-more").hidden = !data.nextCursor;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function escapeHtml(s) {
|
|
98
|
+
return String(s)
|
|
99
|
+
.replace(/&/g, "&")
|
|
100
|
+
.replace(/</g, "<")
|
|
101
|
+
.replace(/>/g, ">")
|
|
102
|
+
.replace(/"/g, """);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatVerdictSurface(vs) {
|
|
106
|
+
if (!vs || typeof vs !== "object") return "";
|
|
107
|
+
const counts = vs.stepStatusCounts || {};
|
|
108
|
+
const parts = Object.entries(counts)
|
|
109
|
+
.filter(([, n]) => n > 0)
|
|
110
|
+
.map(([k, n]) => `${k}: ${n}`);
|
|
111
|
+
const countsLine = parts.length ? parts.join(" · ") : "(no steps)";
|
|
112
|
+
return `
|
|
113
|
+
<div class="verdict-panel" role="region" aria-label="Workflow verdict">
|
|
114
|
+
<div class="verdict-status">Workflow status: <code>${escapeHtml(vs.status)}</code></div>
|
|
115
|
+
<div class="verdict-trust">${escapeHtml(vs.trustSummary || "")}</div>
|
|
116
|
+
<div class="verdict-counts">Step outcomes: ${escapeHtml(countsLine)}</div>
|
|
117
|
+
</div>`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function openDetail(runId) {
|
|
121
|
+
const drawer = document.getElementById("detail-drawer");
|
|
122
|
+
const body = document.getElementById("detail-body");
|
|
123
|
+
const title = document.getElementById("detail-title");
|
|
124
|
+
drawer.classList.remove("hidden");
|
|
125
|
+
title.textContent = `Run: ${runId}`;
|
|
126
|
+
body.innerHTML = "<p>Loading…</p>";
|
|
127
|
+
try {
|
|
128
|
+
const data = await api(`/api/runs/${encodeURIComponent(runId)}`);
|
|
129
|
+
if (data.loadStatus === "error") {
|
|
130
|
+
body.innerHTML = `
|
|
131
|
+
<p class="focus-panel"><strong>Load error</strong> <code>${escapeHtml(data.error.code)}</code></p>
|
|
132
|
+
<pre class="json-out">${escapeHtml(JSON.stringify(data, null, 2))}</pre>
|
|
133
|
+
`;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
let focusHtml = "";
|
|
137
|
+
const focusSet = new Set();
|
|
138
|
+
try {
|
|
139
|
+
const focus = await api(`/api/runs/${encodeURIComponent(runId)}/focus`);
|
|
140
|
+
focusHtml = `<div class="focus-panel"><strong>Focus targets (from API)</strong><pre>${escapeHtml(JSON.stringify(focus.targets, null, 2))}</pre></div>`;
|
|
141
|
+
for (const t of focus.targets) {
|
|
142
|
+
if (t.kind === "seq") focusSet.add(`seq:${t.value}`);
|
|
143
|
+
if (t.kind === "ingestIndex") focusSet.add(`ingest:${t.value}`);
|
|
144
|
+
if (t.kind === "runEventId") focusSet.add(`runEventId:${t.value}`);
|
|
145
|
+
}
|
|
146
|
+
} catch (e) {
|
|
147
|
+
focusHtml = `<p class="meta">Focus: ${escapeHtml(e.message)}</p>`;
|
|
148
|
+
}
|
|
149
|
+
const verdictHtml = formatVerdictSurface(data.workflowVerdictSurface);
|
|
150
|
+
const steps = (data.executionTrace?.nodes || []).map((n, i) => {
|
|
151
|
+
const seq = n.toolSeq ?? n.verificationLink?.seq;
|
|
152
|
+
const seqKey = seq != null ? `seq:${seq}` : "";
|
|
153
|
+
const ingestKey = `ingest:${n.ingestIndex}`;
|
|
154
|
+
const runEvKey = n.runEventId ? `runEventId:${n.runEventId}` : "";
|
|
155
|
+
const hit =
|
|
156
|
+
(seqKey && focusSet.has(seqKey)) ||
|
|
157
|
+
focusSet.has(ingestKey) ||
|
|
158
|
+
(runEvKey && focusSet.has(runEvKey));
|
|
159
|
+
return `<div class="trace-step ${hit ? "focus-hit" : ""}" data-idx="${i}">${escapeHtml(JSON.stringify(n))}</div>`;
|
|
160
|
+
});
|
|
161
|
+
const trustHtml =
|
|
162
|
+
typeof data.runTrustPanelHtml === "string"
|
|
163
|
+
? `<div class="run-trust-panel">${data.runTrustPanelHtml}</div>`
|
|
164
|
+
: "";
|
|
165
|
+
body.innerHTML = `
|
|
166
|
+
${trustHtml}
|
|
167
|
+
${verdictHtml}
|
|
168
|
+
${focusHtml}
|
|
169
|
+
<h3>Trace nodes</h3>
|
|
170
|
+
${steps.join("") || "<p>(no trace nodes)</p>"}
|
|
171
|
+
<h3>WorkflowResult</h3>
|
|
172
|
+
<pre class="json-out">${escapeHtml(JSON.stringify(data.workflowResult, null, 2))}</pre>
|
|
173
|
+
`;
|
|
174
|
+
} catch (e) {
|
|
175
|
+
body.innerHTML = `<p class="focus-panel">Error: ${escapeHtml(e.message)}</p>`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
document.getElementById("filters").addEventListener("submit", (ev) => {
|
|
180
|
+
ev.preventDefault();
|
|
181
|
+
state.filterParams = buildFilterParamsFromForm();
|
|
182
|
+
loadRuns(false).catch((e) => alert(e.message));
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
document.getElementById("clear-filters").addEventListener("click", () => {
|
|
186
|
+
document.getElementById("filters").reset();
|
|
187
|
+
document.querySelector('[name="includeLoadErrors"]').checked = true;
|
|
188
|
+
state.filterParams = buildFilterParamsFromForm();
|
|
189
|
+
loadRuns(false).catch((e) => alert(e.message));
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
document.getElementById("load-more").addEventListener("click", () => {
|
|
193
|
+
loadRuns(true).catch((e) => alert(e.message));
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
document.querySelectorAll(".tab").forEach((btn) => {
|
|
197
|
+
btn.addEventListener("click", () => {
|
|
198
|
+
document.querySelectorAll(".tab").forEach((b) => b.classList.remove("active"));
|
|
199
|
+
document.querySelectorAll(".panel").forEach((p) => p.classList.remove("active"));
|
|
200
|
+
btn.classList.add("active");
|
|
201
|
+
document.getElementById(`panel-${btn.dataset.tab}`).classList.add("active");
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
document.getElementById("refresh-patterns").addEventListener("click", async () => {
|
|
206
|
+
const out = document.getElementById("patterns-out");
|
|
207
|
+
out.textContent = "Loading…";
|
|
208
|
+
const p = new URLSearchParams(state.filterParams);
|
|
209
|
+
try {
|
|
210
|
+
const data = await api(`/api/corpus-patterns?${p.toString()}`);
|
|
211
|
+
out.textContent = JSON.stringify(data, null, 2);
|
|
212
|
+
} catch (e) {
|
|
213
|
+
out.textContent = JSON.stringify(e.data || { message: e.message }, null, 2);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
document.getElementById("run-compare").addEventListener("click", async () => {
|
|
218
|
+
const ids = [...state.selected];
|
|
219
|
+
const out = document.getElementById("compare-out");
|
|
220
|
+
out.textContent = "Loading…";
|
|
221
|
+
try {
|
|
222
|
+
const data = await api("/api/compare", {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: { "Content-Type": "application/json" },
|
|
225
|
+
body: JSON.stringify({ runIds: ids }),
|
|
226
|
+
});
|
|
227
|
+
if (typeof data.comparePanelHtml === "string") {
|
|
228
|
+
out.innerHTML = data.comparePanelHtml;
|
|
229
|
+
} else {
|
|
230
|
+
out.textContent = "comparePanelHtml missing in response.";
|
|
231
|
+
}
|
|
232
|
+
} catch (e) {
|
|
233
|
+
out.textContent = "";
|
|
234
|
+
out.appendChild(document.createTextNode(JSON.stringify(e.data || { message: e.message }, null, 2)));
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
document.getElementById("close-detail").addEventListener("click", () => {
|
|
239
|
+
document.getElementById("detail-drawer").classList.add("hidden");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
state.filterParams = buildFilterParamsFromForm();
|
|
243
|
+
loadRuns(false).catch((e) => {
|
|
244
|
+
document.getElementById("runs-meta").textContent = `Failed to load: ${e.message}`;
|
|
245
|
+
});
|
package/dist/debug-ui/index.html
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>workflow-verifier — Debug Console</title>
|
|
7
|
-
<link rel="stylesheet" href="app.css" />
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<header>
|
|
11
|
-
<h1>Debug Console</h1>
|
|
12
|
-
<p class="sub">Local corpus triage (127.0.0.1 only). API provides focus targets; no duplicated linking logic in the browser.</p>
|
|
13
|
-
</header>
|
|
14
|
-
|
|
15
|
-
<nav class="tabs">
|
|
16
|
-
<button type="button" class="tab active" data-tab="runs">Runs</button>
|
|
17
|
-
<button type="button" class="tab" data-tab="patterns">Patterns</button>
|
|
18
|
-
<button type="button" class="tab" data-tab="compare">Compare</button>
|
|
19
|
-
</nav>
|
|
20
|
-
|
|
21
|
-
<section id="panel-runs" class="panel active">
|
|
22
|
-
<form id="filters" class="filters">
|
|
23
|
-
<label>loadStatus <select name="loadStatus"><option value="">(any)</option><option value="ok">ok</option><option value="error">error</option></select></label>
|
|
24
|
-
<label>workflowId <input name="workflowId" type="text" placeholder="exact" /></label>
|
|
25
|
-
<label>status <select name="status"><option value="">(any)</option><option value="complete">complete</option><option value="incomplete">incomplete</option><option value="inconsistent">inconsistent</option></select></label>
|
|
26
|
-
<label>failureCategory <input name="failureCategory" type="text" /></label>
|
|
27
|
-
<label>reasonCode <input name="reasonCode" type="text" /></label>
|
|
28
|
-
<label>toolId <input name="toolId" type="text" /></label>
|
|
29
|
-
<label>customerId <input name="customerId" type="text" placeholder="__unspecified__" /></label>
|
|
30
|
-
<label>timeFrom (ms) <input name="timeFrom" type="number" /></label>
|
|
31
|
-
<label>timeTo (ms) <input name="timeTo" type="number" /></label>
|
|
32
|
-
<label class="check"><input name="includeLoadErrors" type="checkbox" checked /> includeLoadErrors</label>
|
|
33
|
-
<label class="check"><input name="hasPathFindings" type="checkbox" /> hasPathFindings</label>
|
|
34
|
-
<button type="submit">Apply</button>
|
|
35
|
-
<button type="button" id="clear-filters">Clear</button>
|
|
36
|
-
</form>
|
|
37
|
-
<p id="runs-meta" class="meta"></p>
|
|
38
|
-
<table class="runs">
|
|
39
|
-
<thead>
|
|
40
|
-
<tr>
|
|
41
|
-
<th></th>
|
|
42
|
-
<th>runId</th>
|
|
43
|
-
<th>load</th>
|
|
44
|
-
<th>workflowId</th>
|
|
45
|
-
<th>status</th>
|
|
46
|
-
<th>category</th>
|
|
47
|
-
<th>customer</th>
|
|
48
|
-
<th>path findings</th>
|
|
49
|
-
<th>codes</th>
|
|
50
|
-
</tr>
|
|
51
|
-
</thead>
|
|
52
|
-
<tbody id="runs-body"></tbody>
|
|
53
|
-
</table>
|
|
54
|
-
<p><button type="button" id="load-more" hidden>Next page</button></p>
|
|
55
|
-
</section>
|
|
56
|
-
|
|
57
|
-
<section id="panel-patterns" class="panel">
|
|
58
|
-
<p class="meta">Uses current filter semantics from the Runs tab (re-query when you change filters).</p>
|
|
59
|
-
<button type="button" id="refresh-patterns">Load corpus patterns</button>
|
|
60
|
-
<pre id="patterns-out" class="json-out"></pre>
|
|
61
|
-
</section>
|
|
62
|
-
|
|
63
|
-
<section id="panel-compare" class="panel">
|
|
64
|
-
<p>Select runs in the table (checkbox), then:</p>
|
|
65
|
-
<button type="button" id="run-compare" disabled>Compare selected</button>
|
|
66
|
-
<div id="compare-out" class="compare-panel-out" aria-live="polite"></div>
|
|
67
|
-
</section>
|
|
68
|
-
|
|
69
|
-
<section id="detail-drawer" class="drawer hidden">
|
|
70
|
-
<div class="drawer-head">
|
|
71
|
-
<h2 id="detail-title">Run</h2>
|
|
72
|
-
<button type="button" id="close-detail">Close</button>
|
|
73
|
-
</div>
|
|
74
|
-
<div id="detail-body"></div>
|
|
75
|
-
</section>
|
|
76
|
-
|
|
77
|
-
<script src="app.js"></script>
|
|
78
|
-
</body>
|
|
79
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>workflow-verifier — Debug Console</title>
|
|
7
|
+
<link rel="stylesheet" href="app.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<header>
|
|
11
|
+
<h1>Debug Console</h1>
|
|
12
|
+
<p class="sub">Local corpus triage (127.0.0.1 only). API provides focus targets; no duplicated linking logic in the browser.</p>
|
|
13
|
+
</header>
|
|
14
|
+
|
|
15
|
+
<nav class="tabs">
|
|
16
|
+
<button type="button" class="tab active" data-tab="runs">Runs</button>
|
|
17
|
+
<button type="button" class="tab" data-tab="patterns">Patterns</button>
|
|
18
|
+
<button type="button" class="tab" data-tab="compare">Compare</button>
|
|
19
|
+
</nav>
|
|
20
|
+
|
|
21
|
+
<section id="panel-runs" class="panel active">
|
|
22
|
+
<form id="filters" class="filters">
|
|
23
|
+
<label>loadStatus <select name="loadStatus"><option value="">(any)</option><option value="ok">ok</option><option value="error">error</option></select></label>
|
|
24
|
+
<label>workflowId <input name="workflowId" type="text" placeholder="exact" /></label>
|
|
25
|
+
<label>status <select name="status"><option value="">(any)</option><option value="complete">complete</option><option value="incomplete">incomplete</option><option value="inconsistent">inconsistent</option></select></label>
|
|
26
|
+
<label>failureCategory <input name="failureCategory" type="text" /></label>
|
|
27
|
+
<label>reasonCode <input name="reasonCode" type="text" /></label>
|
|
28
|
+
<label>toolId <input name="toolId" type="text" /></label>
|
|
29
|
+
<label>customerId <input name="customerId" type="text" placeholder="__unspecified__" /></label>
|
|
30
|
+
<label>timeFrom (ms) <input name="timeFrom" type="number" /></label>
|
|
31
|
+
<label>timeTo (ms) <input name="timeTo" type="number" /></label>
|
|
32
|
+
<label class="check"><input name="includeLoadErrors" type="checkbox" checked /> includeLoadErrors</label>
|
|
33
|
+
<label class="check"><input name="hasPathFindings" type="checkbox" /> hasPathFindings</label>
|
|
34
|
+
<button type="submit">Apply</button>
|
|
35
|
+
<button type="button" id="clear-filters">Clear</button>
|
|
36
|
+
</form>
|
|
37
|
+
<p id="runs-meta" class="meta"></p>
|
|
38
|
+
<table class="runs">
|
|
39
|
+
<thead>
|
|
40
|
+
<tr>
|
|
41
|
+
<th></th>
|
|
42
|
+
<th>runId</th>
|
|
43
|
+
<th>load</th>
|
|
44
|
+
<th>workflowId</th>
|
|
45
|
+
<th>status</th>
|
|
46
|
+
<th>category</th>
|
|
47
|
+
<th>customer</th>
|
|
48
|
+
<th>path findings</th>
|
|
49
|
+
<th>codes</th>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>
|
|
52
|
+
<tbody id="runs-body"></tbody>
|
|
53
|
+
</table>
|
|
54
|
+
<p><button type="button" id="load-more" hidden>Next page</button></p>
|
|
55
|
+
</section>
|
|
56
|
+
|
|
57
|
+
<section id="panel-patterns" class="panel">
|
|
58
|
+
<p class="meta">Uses current filter semantics from the Runs tab (re-query when you change filters).</p>
|
|
59
|
+
<button type="button" id="refresh-patterns">Load corpus patterns</button>
|
|
60
|
+
<pre id="patterns-out" class="json-out"></pre>
|
|
61
|
+
</section>
|
|
62
|
+
|
|
63
|
+
<section id="panel-compare" class="panel">
|
|
64
|
+
<p>Select runs in the table (checkbox), then:</p>
|
|
65
|
+
<button type="button" id="run-compare" disabled>Compare selected</button>
|
|
66
|
+
<div id="compare-out" class="compare-panel-out" aria-live="polite"></div>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<section id="detail-drawer" class="drawer hidden">
|
|
70
|
+
<div class="drawer-head">
|
|
71
|
+
<h2 id="detail-title">Run</h2>
|
|
72
|
+
<button type="button" id="close-detail">Close</button>
|
|
73
|
+
</div>
|
|
74
|
+
<div id="detail-body"></div>
|
|
75
|
+
</section>
|
|
76
|
+
|
|
77
|
+
<script src="app.js"></script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|