@echomem/mcp 1.4.47 → 1.4.49
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/README.md +22 -26
- package/dist/context-analysis/claude-native-canonical.js +2 -2
- package/dist/context-analysis/vendored-canonical.js +2 -2
- package/dist/context-analysis/workspace-report.js +3 -3
- package/dist/index.js +5 -8
- package/dist/local-data-paths.js +68 -29
- package/dist/local-jsonl.js +87 -0
- package/dist/migrate.js +8 -9
- package/dist/onboarding-stats.js +16 -0
- package/dist/save-checkpoint-hook.js +1 -1
- package/dist/setup-page/client-core.js +18 -372
- package/dist/setup-page/client-extraction.js +14 -129
- package/dist/setup-page/client-lifecycle.js +28 -101
- package/dist/setup-page/client.js +0 -2
- package/dist/setup-page/styles-extraction.js +1 -31
- package/dist/setup-page/styles-foundation.js +0 -89
- package/dist/setup-page/styles-mvp.js +10 -155
- package/dist/setup-page/styles-website-alignment.js +0 -204
- package/dist/setup-page/styles.js +0 -4
- package/dist/setup-page.js +4 -4
- package/dist/setup-preview.js +4 -212
- package/dist/setup.js +119 -583
- package/dist/v1-contract.js +0 -8
- package/package.json +5 -7
- package/dist/city/README.md +0 -9
- package/dist/city/echo-ai-city-only.html +0 -2232
- package/dist/city/echo-extraction-plate.html +0 -330
- package/dist/city/echo-face-cutout.png +0 -0
- package/dist/city/personality_stickers/bossy.png +0 -0
- package/dist/city/personality_stickers/ghosty.png +0 -0
- package/dist/city/personality_stickers/loopy.png +0 -0
- package/dist/city/personality_stickers/lusty.png +0 -0
- package/dist/city/personality_stickers/maxxy.png +0 -0
- package/dist/city/personality_stickers/tabby.png +0 -0
- package/dist/city/vendor/OrbitControls.js +0 -1417
- package/dist/city/vendor/RoundedBoxGeometry.js +0 -155
- package/dist/city/vendor/echo_general-file-21.riv +0 -0
- package/dist/city/vendor/rive.js +0 -8139
- package/dist/city/vendor/rive.wasm +0 -0
- package/dist/city/vendor/three.module.min.js +0 -6
- package/dist/forensics.js +0 -1531
- package/dist/report.js +0 -721
- package/dist/setup-page/client-report-audit.js +0 -819
- package/dist/setup-page/client-report-city.js +0 -356
- package/dist/setup-page/client-report.js +0 -6
- package/dist/setup-page/styles-city-report.js +0 -880
- package/dist/setup-page/styles-context-audit.js +0 -470
|
@@ -1,819 +0,0 @@
|
|
|
1
|
-
// Generated from the prior inline setup page. This content is emitted into one browser IIFE/style tag.
|
|
2
|
-
export const SETUP_PAGE_CLIENT_REPORT_AUDIT = String.raw ` /* ---------- forensic report (pic 1/2) ---------- */
|
|
3
|
-
/* ---------- workspace readout dashboard (Part 2) ---------- */
|
|
4
|
-
var rdoReduce = !!(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches);
|
|
5
|
-
function gsClean(gs) {
|
|
6
|
-
return gs && !gs.error && gs.summary ? gs : null;
|
|
7
|
-
}
|
|
8
|
-
function gsBillingProjection(r, gs) {
|
|
9
|
-
var summary = (gsClean(gs) || {}).summary || {};
|
|
10
|
-
var supplied = r && r.billedWasteProjection;
|
|
11
|
-
if (!supplied || supplied.method !== "canonical-window-share-over-provider-input") {
|
|
12
|
-
throw new Error("Validated provider-billed projection is missing.");
|
|
13
|
-
}
|
|
14
|
-
var canonicalInput = Number(summary.officialInputTokens);
|
|
15
|
-
var canonicalWaste = Number(summary.wasteTokens);
|
|
16
|
-
var billedInput = Number(supplied.billedInputTokens);
|
|
17
|
-
var multiplier = Number(supplied.billingMultiplier);
|
|
18
|
-
var projectedWaste = Number(supplied.projectedWasteTokens);
|
|
19
|
-
var projectedUseful = Number(supplied.projectedUsefulTokens);
|
|
20
|
-
var tolerance = Math.max(2, billedInput * 0.000001);
|
|
21
|
-
if (![canonicalInput, canonicalWaste, billedInput, multiplier, projectedWaste, projectedUseful].every(function (v) { return isFinite(v); }) ||
|
|
22
|
-
canonicalInput <= 0 || canonicalWaste < 0 || canonicalWaste > canonicalInput || billedInput < canonicalInput || multiplier < 1 ||
|
|
23
|
-
Math.abs(Number(supplied.canonicalInputTokens) - canonicalInput) > 1 ||
|
|
24
|
-
Math.abs(Number(supplied.canonicalWasteTokens) - canonicalWaste) > 1 ||
|
|
25
|
-
Math.abs((canonicalInput * multiplier) - billedInput) > tolerance ||
|
|
26
|
-
Math.abs((projectedUseful + projectedWaste) - billedInput) > 1) {
|
|
27
|
-
throw new Error("Provider and canonical token ledgers do not reconcile.");
|
|
28
|
-
}
|
|
29
|
-
return {
|
|
30
|
-
billedInputTokens: billedInput,
|
|
31
|
-
canonicalInputTokens: canonicalInput,
|
|
32
|
-
canonicalWasteTokens: canonicalWaste,
|
|
33
|
-
billingMultiplier: multiplier,
|
|
34
|
-
projectedWasteTokens: projectedWaste,
|
|
35
|
-
projectedUsefulTokens: projectedUseful
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
function gsProjectedTokens(projection, canonicalTokens) {
|
|
39
|
-
return Math.max(0, Math.round(Number(canonicalTokens || 0) * Number((projection && projection.billingMultiplier) || 1)));
|
|
40
|
-
}
|
|
41
|
-
function gsClampPct(value) {
|
|
42
|
-
return Math.max(0, Math.min(100, Number(value) || 0));
|
|
43
|
-
}
|
|
44
|
-
function gsDisplayWastePct(gs) {
|
|
45
|
-
return gsClampPct(((gs && gs.summary) || {}).wastePct || 0);
|
|
46
|
-
}
|
|
47
|
-
function gsProblemShare(report, problemId) {
|
|
48
|
-
var summary = (report && report.summary) || {};
|
|
49
|
-
var totalWaste = Math.max(0, Number(summary.wasteTokens || 0));
|
|
50
|
-
if (!totalWaste) return null;
|
|
51
|
-
if (problemId === "UNLABELED") return (Number(summary.unattributedWasteTokens || 0) / totalWaste) * 100;
|
|
52
|
-
var problem = ((report && report.problems) || []).find(function (row) { return row.id === problemId; });
|
|
53
|
-
if (!problem) return 0;
|
|
54
|
-
return (Number(problem.allocatedWasteTokens || 0) / totalWaste) * 100;
|
|
55
|
-
}
|
|
56
|
-
function gsUnlabeledProblem(gs) {
|
|
57
|
-
var summary = (gs && gs.summary) || {};
|
|
58
|
-
var tokens = Math.max(0, Number(summary.unattributedWasteTokens || 0));
|
|
59
|
-
return {
|
|
60
|
-
id: "UNLABELED",
|
|
61
|
-
label: "Unlabeled residue",
|
|
62
|
-
bucket: "unattributed",
|
|
63
|
-
category: "unattributed",
|
|
64
|
-
allocatedWasteTokens: tokens,
|
|
65
|
-
count: 0,
|
|
66
|
-
sessions: Number(summary.sessionsAnalyzed || 0),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
function gsProblemRankValue(problem) {
|
|
70
|
-
return Number.isFinite(Number(problem.displaySharePct))
|
|
71
|
-
? Number(problem.displaySharePct || 0)
|
|
72
|
-
: Number(problem.allocatedWasteTokens || 0);
|
|
73
|
-
}
|
|
74
|
-
function gsSortProblems(problems) {
|
|
75
|
-
return problems.slice().sort(function (a, b) {
|
|
76
|
-
return gsProblemRankValue(b) - gsProblemRankValue(a) || String(a.id || "").localeCompare(String(b.id || ""));
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
function gsDisplayProblems(gs, includeUnlabeled) {
|
|
80
|
-
var problems = ((gs && gs.problems) || []).slice();
|
|
81
|
-
var unlabeled = includeUnlabeled ? gsUnlabeledProblem(gs) : null;
|
|
82
|
-
if (unlabeled) problems.push(unlabeled);
|
|
83
|
-
var sourceReports = [gs && gs.sourceReports && gs.sourceReports.codex, gs && gs.sourceReports && gs.sourceReports.claudeCode]
|
|
84
|
-
.filter(function (rep) { return rep && rep.summary && Number(rep.summary.sessionsAnalyzed || 0) > 0; });
|
|
85
|
-
if (sourceReports.length <= 1) return gsSortProblems(problems);
|
|
86
|
-
return gsSortProblems(problems.map(function (problem) {
|
|
87
|
-
var shares = sourceReports
|
|
88
|
-
.map(function (report) { return gsProblemShare(report, problem.id); })
|
|
89
|
-
.filter(function (share) { return share !== null && Number.isFinite(Number(share)); });
|
|
90
|
-
var displaySharePct = shares.length
|
|
91
|
-
? shares.reduce(function (sum, share) { return sum + Number(share || 0); }, 0) / shares.length
|
|
92
|
-
: 0;
|
|
93
|
-
return Object.assign({}, problem, { displaySharePct: displaySharePct });
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
96
|
-
function gsColor(p, i) {
|
|
97
|
-
return "#c7372f";
|
|
98
|
-
}
|
|
99
|
-
var GS_PROBLEM_DEFINITIONS = {
|
|
100
|
-
UNLABELED: "Wasteful context that is included in the headline score but does not have enough evidence to assign to one of the 13 named problems.",
|
|
101
|
-
P01: "Too many screenshots or images remain in context after they stop being the active reference.",
|
|
102
|
-
P02: "The model had a known user constraint or correction signal, but the work still violated it and required repair.",
|
|
103
|
-
P03: "Old file reads, duplicate file versions, or superseded patch drafts accumulate beside the current version.",
|
|
104
|
-
P04: "After compaction, the model has to rediscover files, searches, or git state that it had already found.",
|
|
105
|
-
P05: "The assistant treated work as done, but the user corrected it, so the next turn pays repair debt.",
|
|
106
|
-
P06: "After a time gap or state gap, the model's working view no longer matches the repo or task state.",
|
|
107
|
-
P07: "A long or aborted turn may leave file, process, or disk side effects that later turns must recover from.",
|
|
108
|
-
P08: "Git status, git diff, or related repo-state commands are repeated without adding enough new information.",
|
|
109
|
-
P09: "The execution path drifts and needs repeated correction, retry, validation, or reorientation.",
|
|
110
|
-
P10: "The model repeats the same search, query, or memory lookup instead of reusing prior knowledge.",
|
|
111
|
-
P11: "Browser/visual inspection outputs, DOM dumps, route checks, and viewport/debug metadata remain in context after serving visual QA.",
|
|
112
|
-
P12: "Initial discovery outputs such as broad repo searches, memory lookups, first git diffs, file listings, and one-time orientation reads remain raw instead of being compressed.",
|
|
113
|
-
P13: "Agent explanations, reconciliation notes, planning residue, and reasoning-adjacent text remain in context after they stop being operationally useful."
|
|
114
|
-
};
|
|
115
|
-
function gsProblemDefinition(p) {
|
|
116
|
-
return GS_PROBLEM_DEFINITIONS[p && p.id] || "No definition is available for this problem yet.";
|
|
117
|
-
}
|
|
118
|
-
function gsSourceList(gs) {
|
|
119
|
-
var sourceReports = (gs && gs.sourceReports) || {};
|
|
120
|
-
return [
|
|
121
|
-
{ key: "combined", label: "Combined", report: gs, combined: true },
|
|
122
|
-
{ key: "codex", label: "Codex", report: sourceReports.codex },
|
|
123
|
-
{ key: "claude", label: "Claude Code", report: sourceReports.claudeCode },
|
|
124
|
-
];
|
|
125
|
-
}
|
|
126
|
-
function gsSourceReport(gs, key) {
|
|
127
|
-
var sources = gsSourceList(gs);
|
|
128
|
-
for (var i = 0; i < sources.length; i++) if (sources[i].key === key) return sources[i];
|
|
129
|
-
return sources[0];
|
|
130
|
-
}
|
|
131
|
-
function gsSourceCallout(gs, label) {
|
|
132
|
-
var wastePct = Math.round(gsDisplayWastePct(gs));
|
|
133
|
-
var contextLabel = label === "Combined" ? "Of the context you use" : "Of the " + label + " context you use";
|
|
134
|
-
return '<div class="gs-waste-callout" aria-label="' + number(wastePct) + '% of ' + esc(label) + ' context is waste"><span>' + esc(contextLabel) + '</span><strong>' + number(wastePct) + '<small>%</small></strong><b>is waste</b></div>';
|
|
135
|
-
}
|
|
136
|
-
function gsSourceDetail(gs, label) {
|
|
137
|
-
var wastePct = Math.round(gsDisplayWastePct(gs));
|
|
138
|
-
var usefulPct = Math.max(0, 100 - wastePct);
|
|
139
|
-
var heading = label === "Combined" ? "The four biggest named sources of waste" : "The four biggest named sources of " + label + " waste";
|
|
140
|
-
return '<div class="gs-context-meter" aria-label="' + number(wastePct) + '% waste and ' + number(usefulPct) + '% useful ' + esc(label) + ' context"><div class="gs-context-meter-track"><i style="width:' + usefulPct + '%"></i><b style="width:' + wastePct + '%"></b></div><div class="gs-context-meter-labels"><span>' + number(usefulPct) + '% useful context</span><b>' + number(wastePct) + '% waste</b></div></div>' +
|
|
141
|
-
'<div class="gs-problem-intro"><div><p class="rdo-kicker">What is filling it</p><h4>' + esc(heading) + '</h4></div></div>' +
|
|
142
|
-
gsTopProblems(gs);
|
|
143
|
-
}
|
|
144
|
-
function gsProblemExampleText(ex) {
|
|
145
|
-
var evidence = String((ex && ex.evidence) || "").trim();
|
|
146
|
-
if (!evidence) return "Analyzer marked this turn as evidence for the pattern.";
|
|
147
|
-
return evidence
|
|
148
|
-
.replace(/\\s+/g, " ")
|
|
149
|
-
.replace(/git state command/gi, "git check")
|
|
150
|
-
.replace(new RegExp("(^|[^A-Za-z0-9])T([0-9]+)", "g"), "$1turn $2")
|
|
151
|
-
.slice(0, 280);
|
|
152
|
-
}
|
|
153
|
-
function gsExamplePreview(value, limit) {
|
|
154
|
-
var compacted = String(value || "").replace(/\\s+/g, " ").trim();
|
|
155
|
-
if (!compacted) return "";
|
|
156
|
-
limit = limit || 420;
|
|
157
|
-
return compacted.length > limit ? compacted.slice(0, limit - 1) + "…" : compacted;
|
|
158
|
-
}
|
|
159
|
-
function gsExampleBlockPreview(value, limit) {
|
|
160
|
-
var compacted = String(value || "")
|
|
161
|
-
.replace(/\\r\\n/g, "\\n")
|
|
162
|
-
.replace(/[ \\t]+\\n/g, "\\n")
|
|
163
|
-
.replace(/\\n{3,}/g, "\\n\\n")
|
|
164
|
-
.trim();
|
|
165
|
-
if (!compacted) return "";
|
|
166
|
-
limit = limit || 900;
|
|
167
|
-
return compacted.length > limit ? compacted.slice(0, limit - 1) + "…" : compacted;
|
|
168
|
-
}
|
|
169
|
-
function gsExamplePct(part, total) {
|
|
170
|
-
part = Number(part || 0);
|
|
171
|
-
total = Number(total || 0);
|
|
172
|
-
return total > 0 ? Math.round((part / total) * 100) : 0;
|
|
173
|
-
}
|
|
174
|
-
function gsSpecificProblemText(payload, ex, stats) {
|
|
175
|
-
var tokens = number(stats.problemTokens || 0);
|
|
176
|
-
var problemPct = number(stats.problemPct || 0);
|
|
177
|
-
var input = number(stats.input || 0);
|
|
178
|
-
var usefulPct = number(stats.usefulPct || 0);
|
|
179
|
-
var wastePct = number(stats.wastePct || 0);
|
|
180
|
-
var command = gsExamplePreview(ex && ex.command, 120);
|
|
181
|
-
var file = gsExamplePreview(ex && ex.file, 160);
|
|
182
|
-
var base = stats.input
|
|
183
|
-
? "This turn used " + input + " input tokens. About " + usefulPct + "% was useful, " + wastePct + "% was waste, and " + tokens + " tokens (" + problemPct + "% of the turn) were attributed to " + (payload.label || "this problem") + ". "
|
|
184
|
-
: "The scanner could not recover the whole turn size, but it attributed " + tokens + " tokens to " + (payload.label || "this problem") + ". ";
|
|
185
|
-
if (payload.id === "P13") {
|
|
186
|
-
return base + "The wasted part was agent reasoning or note residue: internal thinking carried forward after it had stopped helping the user's request. The user sees their own prompt, but the model was also dragging along this extra scratchpad context.";
|
|
187
|
-
}
|
|
188
|
-
if (payload.id === "P12") {
|
|
189
|
-
return base + "The wasted part was tool output still sitting in context after its useful signal had already been consumed." + (command ? " The captured command was '" + command + "'." : "") + " This is the kind of turn where logs become ballast instead of evidence.";
|
|
190
|
-
}
|
|
191
|
-
if (payload.id === "P03") {
|
|
192
|
-
return base + "This is a ghosting-file case: old file content stayed in the working window after the current task needed fresher state." + (file ? " The file tied to the finding was '" + file + "'." : "") + " The problem is not that the file was read once; it is that stale file context kept traveling with later turns.";
|
|
193
|
-
}
|
|
194
|
-
if (payload.id === "P10") {
|
|
195
|
-
return base + "The agent repeated or re-carried search results for information it had already found." + (command ? " The repeated search/tool command was '" + command + "'." : "") + " That made the turn heavier without adding new task signal.";
|
|
196
|
-
}
|
|
197
|
-
if (payload.id === "P08") {
|
|
198
|
-
return base + "The waste came from repeated repo-state checks, usually git status, diff, or related bookkeeping." + (command ? " The captured command was '" + command + "'." : "") + " Those checks are useful briefly, then become stale once the repo changes.";
|
|
199
|
-
}
|
|
200
|
-
if (payload.id === "P11") {
|
|
201
|
-
return base + "The waste came from visual or browser debugging output that stayed in context after the inspection had already served its purpose. Screenshots and layout probes help during verification, but they should not dominate later turns.";
|
|
202
|
-
}
|
|
203
|
-
if (payload.id === "P01") {
|
|
204
|
-
return base + "The wasted context was screenshot or image evidence that no longer needed to be carried at full weight. It made the turn expensive even though only the conclusion from the image was still useful.";
|
|
205
|
-
}
|
|
206
|
-
if (payload.id === "P04") {
|
|
207
|
-
return base + "The agent had to rediscover project context that should have been remembered or summarized. The cost here is repeated orientation work: reading or explaining things the session had already established.";
|
|
208
|
-
}
|
|
209
|
-
if (payload.id === "P06") {
|
|
210
|
-
return base + "This is a session re-heat problem. The agent spent context reloading old state before it could move the task forward, so a large part of the turn was setup rather than progress.";
|
|
211
|
-
}
|
|
212
|
-
if (payload.id === "P07") {
|
|
213
|
-
return base + "The wasted context came from leftovers after a failed or abandoned path. Keeping that dead branch in context made the next attempt pay for details that no longer guided the solution.";
|
|
214
|
-
}
|
|
215
|
-
if (payload.id === "P02") {
|
|
216
|
-
return base + "The problem is instruction drift: context from earlier turns competed with what the user was asking for in this turn. The stale instruction context counted as waste because it made the agent less aligned with the current prompt.";
|
|
217
|
-
}
|
|
218
|
-
if (payload.id === "P05") {
|
|
219
|
-
return base + "The waste came from premature-completion residue: context around work that looked done but still needed correction or verification. It carried closure signals instead of fresh evidence.";
|
|
220
|
-
}
|
|
221
|
-
if (payload.id === "P09") {
|
|
222
|
-
return base + "The turn carried repeated fix attempts for the same issue. Some history is useful, but this much retry context means the model is paying for prior attempts instead of a compact diagnosis.";
|
|
223
|
-
}
|
|
224
|
-
return base + (payload.definition || "The analyzer marked this turn as the strongest example for this problem.");
|
|
225
|
-
}
|
|
226
|
-
function gsExampleProblemSignal(payload, ex) {
|
|
227
|
-
var command = gsExamplePreview(ex && ex.command, 140);
|
|
228
|
-
var file = gsExamplePreview(ex && ex.file, 180);
|
|
229
|
-
var evidence = gsExamplePreview(ex && ex.evidence, 180);
|
|
230
|
-
if (payload.id === "P12" && command) return "Tool output from command: " + command;
|
|
231
|
-
if (payload.id === "P08" && command) return "Repeated repo-state command: " + command;
|
|
232
|
-
if (payload.id === "P10" && command) return "Repeated search/tool command: " + command;
|
|
233
|
-
if (payload.id === "P03" && file) return "Ghosting file: " + file;
|
|
234
|
-
if (payload.id === "P13") return "Reasoning-note residue carried into this turn";
|
|
235
|
-
if (payload.id === "P11") return "Visual/browser debug output carried past the inspection";
|
|
236
|
-
if (payload.id === "P01") return "Screenshot or image evidence carried at full weight";
|
|
237
|
-
return evidence || payload.definition || "";
|
|
238
|
-
}
|
|
239
|
-
function gsOutputHighlightCandidates(payload, ex) {
|
|
240
|
-
var out = [];
|
|
241
|
-
function add(value) {
|
|
242
|
-
var v = String(value || "").replace(/\\s+/g, " ").trim();
|
|
243
|
-
if (v.length < 4) return;
|
|
244
|
-
if (out.indexOf(v) === -1) out.push(v);
|
|
245
|
-
}
|
|
246
|
-
if (payload.id === "P12" || payload.id === "P08" || payload.id === "P10") add(ex && ex.command);
|
|
247
|
-
if (payload.id === "P03") add(ex && ex.file);
|
|
248
|
-
add(ex && ex.evidence);
|
|
249
|
-
add(gsExampleProblemSignal(payload, ex));
|
|
250
|
-
return out.sort(function (a, b) { return b.length - a.length; });
|
|
251
|
-
}
|
|
252
|
-
function gsMarkdownInlineHtml(text) {
|
|
253
|
-
var codeChar = String.fromCharCode(96);
|
|
254
|
-
var html = esc(text);
|
|
255
|
-
html = html.replace(new RegExp(codeChar + "([^" + codeChar + "]+)" + codeChar, "g"), function (_, code) {
|
|
256
|
-
return '<code>' + code + '</code>';
|
|
257
|
-
});
|
|
258
|
-
html = html
|
|
259
|
-
.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')
|
|
260
|
-
.replace(/__([^_]+)__/g, '<strong>$1</strong>')
|
|
261
|
-
.replace(/(^|\\W)\\*([^*]+)\\*(?=\\W|$)/g, '$1<em>$2</em>')
|
|
262
|
-
.replace(/(^|\\W)_([^_]+)_(?=\\W|$)/g, '$1<em>$2</em>');
|
|
263
|
-
return html;
|
|
264
|
-
}
|
|
265
|
-
function gsMarkdownHtml(text) {
|
|
266
|
-
var fence = String.fromCharCode(96) + String.fromCharCode(96) + String.fromCharCode(96);
|
|
267
|
-
var lines = String(text || "").replace(/\\r\\n/g, "\\n").split("\\n");
|
|
268
|
-
var html = [];
|
|
269
|
-
var paragraph = [];
|
|
270
|
-
var listTag = "";
|
|
271
|
-
var inCode = false;
|
|
272
|
-
var codeLines = [];
|
|
273
|
-
function flushParagraph() {
|
|
274
|
-
if (!paragraph.length) return;
|
|
275
|
-
html.push('<p>' + gsMarkdownInlineHtml(paragraph.join(" ")) + '</p>');
|
|
276
|
-
paragraph = [];
|
|
277
|
-
}
|
|
278
|
-
function closeList() {
|
|
279
|
-
if (!listTag) return;
|
|
280
|
-
html.push('</' + listTag + '>');
|
|
281
|
-
listTag = "";
|
|
282
|
-
}
|
|
283
|
-
function openList(tag) {
|
|
284
|
-
if (listTag === tag) return;
|
|
285
|
-
closeList();
|
|
286
|
-
flushParagraph();
|
|
287
|
-
listTag = tag;
|
|
288
|
-
html.push('<' + tag + '>');
|
|
289
|
-
}
|
|
290
|
-
lines.forEach(function (line) {
|
|
291
|
-
var trimmed = line.trim();
|
|
292
|
-
if (trimmed.indexOf(fence) === 0) {
|
|
293
|
-
if (inCode) {
|
|
294
|
-
html.push('<pre><code>' + esc(codeLines.join("\\n")) + '</code></pre>');
|
|
295
|
-
codeLines = [];
|
|
296
|
-
inCode = false;
|
|
297
|
-
} else {
|
|
298
|
-
flushParagraph();
|
|
299
|
-
closeList();
|
|
300
|
-
inCode = true;
|
|
301
|
-
}
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
if (inCode) {
|
|
305
|
-
codeLines.push(line);
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
if (!trimmed) {
|
|
309
|
-
flushParagraph();
|
|
310
|
-
closeList();
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
var heading = /^(#{1,3})\\s+(.+)$/.exec(trimmed);
|
|
314
|
-
if (heading) {
|
|
315
|
-
flushParagraph();
|
|
316
|
-
closeList();
|
|
317
|
-
html.push('<h' + heading[1].length + '>' + gsMarkdownInlineHtml(heading[2]) + '</h' + heading[1].length + '>');
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
var quote = /^>\\s?(.+)$/.exec(trimmed);
|
|
321
|
-
if (quote) {
|
|
322
|
-
flushParagraph();
|
|
323
|
-
closeList();
|
|
324
|
-
html.push('<blockquote>' + gsMarkdownInlineHtml(quote[1]) + '</blockquote>');
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
var bullet = /^[-*]\\s+(.+)$/.exec(trimmed);
|
|
328
|
-
if (bullet) {
|
|
329
|
-
openList("ul");
|
|
330
|
-
html.push('<li>' + gsMarkdownInlineHtml(bullet[1]) + '</li>');
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
var numbered = /^\\d+[.)]\\s+(.+)$/.exec(trimmed);
|
|
334
|
-
if (numbered) {
|
|
335
|
-
openList("ol");
|
|
336
|
-
html.push('<li>' + gsMarkdownInlineHtml(numbered[1]) + '</li>');
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
closeList();
|
|
340
|
-
paragraph.push(trimmed);
|
|
341
|
-
});
|
|
342
|
-
if (inCode) html.push('<pre><code>' + esc(codeLines.join("\\n")) + '</code></pre>');
|
|
343
|
-
flushParagraph();
|
|
344
|
-
closeList();
|
|
345
|
-
return html.join("");
|
|
346
|
-
}
|
|
347
|
-
function gsHighlightedOutputHtml(text, candidates) {
|
|
348
|
-
var raw = String(text || "");
|
|
349
|
-
var lower = raw.toLowerCase();
|
|
350
|
-
var best = null;
|
|
351
|
-
candidates.forEach(function (candidate) {
|
|
352
|
-
var idx = lower.indexOf(String(candidate || "").toLowerCase());
|
|
353
|
-
if (idx < 0) return;
|
|
354
|
-
if (!best || String(candidate).length > best.length) best = { index: idx, length: String(candidate).length };
|
|
355
|
-
});
|
|
356
|
-
if (!best) return { html: gsMarkdownHtml(raw), matched: false };
|
|
357
|
-
var startMark = "\\uE000";
|
|
358
|
-
var endMark = "\\uE001";
|
|
359
|
-
var markedRaw = raw.slice(0, best.index) + startMark + raw.slice(best.index, best.index + best.length) + endMark + raw.slice(best.index + best.length);
|
|
360
|
-
return {
|
|
361
|
-
html: gsMarkdownHtml(markedRaw).replace(new RegExp(startMark, "g"), "<mark>").replace(new RegExp(endMark, "g"), "</mark>"),
|
|
362
|
-
matched: true,
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
function gsExampleTime(ms) {
|
|
366
|
-
ms = Number(ms || 0);
|
|
367
|
-
if (!isFinite(ms) || ms <= 0) return "time unavailable";
|
|
368
|
-
var d = new Date(ms);
|
|
369
|
-
return d.toLocaleString("en-US", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
|
|
370
|
-
}
|
|
371
|
-
function gsAgentLabel(source) {
|
|
372
|
-
source = String(source || "").toLowerCase();
|
|
373
|
-
if (source === "claude-code" || source === "claude" || source === "claudecode") return "Claude Code";
|
|
374
|
-
if (source === "codex") return "Codex";
|
|
375
|
-
return "Agent unknown";
|
|
376
|
-
}
|
|
377
|
-
function gsExamplePayload(p, shareLabel) {
|
|
378
|
-
return {
|
|
379
|
-
id: p && p.id || "",
|
|
380
|
-
label: p && p.label || "Untitled problem",
|
|
381
|
-
category: p && (p.category || p.bucket) || "Context problem",
|
|
382
|
-
share: shareLabel,
|
|
383
|
-
definition: gsProblemDefinition(p),
|
|
384
|
-
tokens: Number((p && p.allocatedWasteTokens) || 0),
|
|
385
|
-
examples: ((p && p.examples) || []).slice(0, 1).map(function (ex) {
|
|
386
|
-
return {
|
|
387
|
-
repo: ex && ex.repo || "workspace",
|
|
388
|
-
session: ex && ex.session || "",
|
|
389
|
-
agentSource: ex && ex.agentSource || "",
|
|
390
|
-
turn: ex && ex.turn || 0,
|
|
391
|
-
tokens: ex && ex.tokens || 0,
|
|
392
|
-
prompt: gsExamplePreview(ex && ex.prompt, 420),
|
|
393
|
-
output: gsExampleBlockPreview(ex && ex.output, 900),
|
|
394
|
-
command: ex && ex.command || "",
|
|
395
|
-
file: ex && ex.file || "",
|
|
396
|
-
evidence: gsProblemExampleText(ex),
|
|
397
|
-
timestampMs: ex && ex.timestampMs || ex && ex.sessionStartedAt || null,
|
|
398
|
-
turnInputTokens: ex && ex.turnInputTokens || 0,
|
|
399
|
-
turnUsefulTokens: ex && ex.turnUsefulTokens || 0,
|
|
400
|
-
turnWasteTokens: ex && ex.turnWasteTokens || 0,
|
|
401
|
-
};
|
|
402
|
-
}),
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
function gsProblemExampleButton(p, shareLabel) {
|
|
406
|
-
var payload = encodeURIComponent(JSON.stringify(gsExamplePayload(p, shareLabel)));
|
|
407
|
-
return '<button class="gs-example-btn" type="button" data-gs-example="' + payload + '">See example</button>';
|
|
408
|
-
}
|
|
409
|
-
function exampleField(label, value, fallback) {
|
|
410
|
-
var missing = !String(value || "").trim();
|
|
411
|
-
return '<div class="example-field' + (missing ? ' missing' : '') + '"><b>' + esc(label) + '</b><span>' + esc(missing ? fallback : value) + '</span></div>';
|
|
412
|
-
}
|
|
413
|
-
function exampleBlockField(label, value, fallback, payload, ex) {
|
|
414
|
-
var missing = !String(value || "").trim();
|
|
415
|
-
if (missing) return '<div class="example-field missing"><b>' + esc(label) + '</b><div class="example-output"><div class="example-output-text">' + esc(fallback) + '</div></div></div>';
|
|
416
|
-
var highlighted = gsHighlightedOutputHtml(value, gsOutputHighlightCandidates(payload || {}, ex || {}));
|
|
417
|
-
var signal = gsExampleProblemSignal(payload || {}, ex || {});
|
|
418
|
-
var signalHtml = signal
|
|
419
|
-
? '<div class="example-problem-signal"><small>' + (highlighted.matched ? 'Highlighted problem span' : 'Problem signal') + '</small><span>' + esc(signal) + '</span></div>'
|
|
420
|
-
: "";
|
|
421
|
-
return '<div class="example-field"><b>' + esc(label) + '</b><div class="example-output">' + signalHtml + '<div class="example-output-text">' + highlighted.html + '</div></div></div>';
|
|
422
|
-
}
|
|
423
|
-
function rdoOpenProblemExample(encoded) {
|
|
424
|
-
var payload = null;
|
|
425
|
-
try { payload = JSON.parse(decodeURIComponent(encoded || "")); } catch (_) { return; }
|
|
426
|
-
var dlg = document.getElementById("echo-note"); if (!dlg || !payload) return;
|
|
427
|
-
var examples = payload.examples || [];
|
|
428
|
-
document.getElementById("echo-note-k").textContent = (payload.id ? payload.id + " · " : "") + (payload.category || "Context problem");
|
|
429
|
-
document.getElementById("echo-note-t").textContent = payload.label || "Problem example";
|
|
430
|
-
document.getElementById("echo-note-b").innerHTML =
|
|
431
|
-
'<p>This card uses the <b>worst turn</b> found for this problem. The red badge is <b>not the whole session</b>; it is the amount of this turn\'s input payload attributed to <b>' + esc(payload.label || "this pattern") + '</b>.</p>' +
|
|
432
|
-
(examples.length ? '<div class="example-list">' + examples.map(function (ex, index) {
|
|
433
|
-
var input = Number(ex.turnInputTokens || 0);
|
|
434
|
-
var useful = Number(ex.turnUsefulTokens || 0);
|
|
435
|
-
var waste = Number(ex.turnWasteTokens || 0);
|
|
436
|
-
var problemTokens = Number(ex.tokens || 0);
|
|
437
|
-
var usefulPct = gsExamplePct(useful, input);
|
|
438
|
-
var wastePct = gsExamplePct(waste, input);
|
|
439
|
-
var problemPct = gsExamplePct(problemTokens, input);
|
|
440
|
-
var stats = { input: input, useful: useful, waste: waste, problemTokens: problemTokens, usefulPct: usefulPct, wastePct: wastePct, problemPct: problemPct };
|
|
441
|
-
var meta = gsAgentLabel(ex.agentSource) + " · " + (ex.repo || "workspace") + (ex.turn ? " · turn " + number(ex.turn) : "") + (ex.session ? " · session " + String(ex.session).slice(0, 8) : "") + " · " + gsExampleTime(ex.timestampMs);
|
|
442
|
-
var statLine = input
|
|
443
|
-
? number(input) + " total input tokens · " + number(usefulPct) + "% useful · " + number(wastePct) + "% waste · " + number(problemPct) + "% this problem"
|
|
444
|
-
: "Turn token totals unavailable for this scan.";
|
|
445
|
-
return '<div class="example-card">' +
|
|
446
|
-
'<div class="example-meta"><span>' + esc(meta) + '</span><b>' + number(ex.tokens || 0) + ' problem tokens in this turn</b></div>' +
|
|
447
|
-
exampleField("Turn size", statLine, "Turn token totals unavailable for this scan.") +
|
|
448
|
-
exampleField("User prompt", ex.prompt, "This report snapshot did not capture the turn prompt preview. Re-run the scan to rebuild examples with transcript previews.") +
|
|
449
|
-
exampleBlockField("Agent output", ex.output, "This report snapshot did not capture the agent output preview. Re-run the scan to rebuild examples with transcript previews.", payload, ex) +
|
|
450
|
-
exampleField("Problem", gsSpecificProblemText(payload, ex, stats), "Problem definition is not available.") +
|
|
451
|
-
exampleField("Evidence", ex.evidence, "Evidence details are not available.") +
|
|
452
|
-
'</div>';
|
|
453
|
-
}).join("") + '</div>' : '<div class="example-list"><div class="example-card">' + exampleField("No examples captured", "", "This scan produced aggregate totals but no row-level evidence for this card.") + '</div></div>');
|
|
454
|
-
dlg.classList.add("example");
|
|
455
|
-
if (dlg.showModal) dlg.showModal(); else dlg.setAttribute("open", "");
|
|
456
|
-
}
|
|
457
|
-
function gsTopProblems(gs) {
|
|
458
|
-
var summary = (gs && gs.summary) || {};
|
|
459
|
-
var totalWaste = Math.max(0, Number(summary.wasteTokens || 0));
|
|
460
|
-
var problems = gsDisplayProblems(gs, false).slice(0, 4);
|
|
461
|
-
if (!problems.length) return '<p class="gs-lead">No recurring waste pattern was strong enough to rank in this scan.</p>';
|
|
462
|
-
return '<div class="gs-top-problems">' + problems.map(function (p, index) {
|
|
463
|
-
var rawShare = Number.isFinite(Number(p.displaySharePct))
|
|
464
|
-
? Number(p.displaySharePct || 0)
|
|
465
|
-
: (totalWaste ? (Number(p.allocatedWasteTokens || 0) / totalWaste) * 100 : 0);
|
|
466
|
-
var share = Math.round(rawShare);
|
|
467
|
-
var hasTokens = Number(p.allocatedWasteTokens || 0) > 0;
|
|
468
|
-
var shareLabel = share === 0 && hasTokens ? '<1' : number(share);
|
|
469
|
-
var accent = gsColor(p, index);
|
|
470
|
-
return '<article class="gs-problem-card" style="--gs-accent:' + esc(accent) + '">' +
|
|
471
|
-
'<div class="gs-problem-card-head"><span>' + esc(String(index + 1).padStart(2, "0")) + '</span><b>' + esc(shareLabel) + '% of waste</b></div>' +
|
|
472
|
-
'<h5>' + esc(p.label || "Untitled problem") + '</h5>' +
|
|
473
|
-
'<p>' + esc(gsProblemDefinition(p)) + '</p>' +
|
|
474
|
-
gsProblemExampleButton(p, shareLabel) +
|
|
475
|
-
'<div class="gs-problem-card-meter" aria-hidden="true"><i style="width:' + Math.max(0, Math.min(100, rawShare)) + '%"></i></div>' +
|
|
476
|
-
'</article>';
|
|
477
|
-
}).join("") + '</div>';
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
var AUDIT_GITHUB_MARK = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 .7a11.5 11.5 0 0 0-3.64 22.41c.58.11.79-.25.79-.56v-2.23c-3.22.7-3.9-1.37-3.9-1.37-.53-1.34-1.29-1.7-1.29-1.7-1.05-.72.08-.71.08-.71 1.17.08 1.78 1.2 1.78 1.2 1.04 1.78 2.72 1.27 3.38.97.1-.75.41-1.27.74-1.56-2.57-.29-5.27-1.29-5.27-5.68 0-1.26.45-2.28 1.19-3.09-.12-.29-.52-1.47.11-3.05 0 0 .97-.31 3.16 1.18A10.97 10.97 0 0 1 12 6.12c.98 0 1.96.13 2.88.39 2.2-1.49 3.16-1.18 3.16-1.18.63 1.58.23 2.76.11 3.05.74.81 1.19 1.83 1.19 3.09 0 4.4-2.71 5.38-5.29 5.67.42.36.79 1.06.79 2.14v3.27c0 .31.21.68.8.56A11.5 11.5 0 0 0 12 .7Z"/></svg>';
|
|
481
|
-
var AUDIT_GROUPS = [
|
|
482
|
-
{
|
|
483
|
-
key: "reasoning",
|
|
484
|
-
label: "Agent planning and reasoning never leave the context",
|
|
485
|
-
ids: ["P13"],
|
|
486
|
-
findingNoun: "reasoning residues",
|
|
487
|
-
explanation: "Agents generate planning and reasoning notes to guide each turn. Turn after turn, those notes decay — plans change, assumptions get corrected — yet they all stay in the context, and the model still walks through every one of them before it can act. These notes are often very long, and they build on one another, so one stale line of reasoning keeps steering later turns and interferes with the current direction."
|
|
488
|
-
},
|
|
489
|
-
{
|
|
490
|
-
key: "screenshots",
|
|
491
|
-
label: "Every screenshot stays in the context",
|
|
492
|
-
ids: ["P01", "P11"],
|
|
493
|
-
findingNoun: "obsolete screenshots",
|
|
494
|
-
explanation: "Screenshots enter the context from your uploads and from the agent’s own visual checks, and each one is heavy — a single image can cost as much as pages of text. Once its question is answered, the image should be done. Instead it stays attached, and every later turn re-processes the same obsolete pixels that no longer describe what is on screen."
|
|
495
|
-
},
|
|
496
|
-
{
|
|
497
|
-
key: "tools",
|
|
498
|
-
label: "Finished tool output stayed in the payload",
|
|
499
|
-
ids: ["P12", "P08", "P10"],
|
|
500
|
-
findingNoun: "retained tool outputs",
|
|
501
|
-
explanation: "Agents run tools to answer questions: read a file, search the code, check the repository state. The answer gets used in that turn, but the raw output — full logs, long file dumps, command results — keeps riding along in every later request. The question is closed; the model still pays to re-read the evidence each turn."
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
key: "dead-work",
|
|
505
|
-
label: "Discarded work survived the decision",
|
|
506
|
-
ids: ["P07", "P09", "P05", "P03"],
|
|
507
|
-
findingNoun: "discarded-work residues",
|
|
508
|
-
explanation: "Not every attempt survives: edits get reverted, drafts get replaced, whole approaches get abandoned. The decision moved on, but the discarded versions stay in the context beside the accepted one. Later turns carry both — and the model has to keep telling the dead ends apart from the work that actually counts."
|
|
509
|
-
}
|
|
510
|
-
];
|
|
511
|
-
function auditGroupData(gs, spec, projection) {
|
|
512
|
-
var rows = ((gs && gs.problems) || []).filter(function (p) { return spec.ids.indexOf(p.id) !== -1; });
|
|
513
|
-
var canonicalTokens = rows.reduce(function (sum, p) { return sum + Number(p.allocatedWasteTokens || 0); }, 0);
|
|
514
|
-
var tokens = gsProjectedTokens(projection, canonicalTokens);
|
|
515
|
-
var count = rows.reduce(function (sum, p) { return sum + Number(p.count || 0); }, 0);
|
|
516
|
-
var sessions = rows.reduce(function (set, p) {
|
|
517
|
-
((p && p.examples) || []).forEach(function (ex) { if (ex && ex.session) set[ex.session] = 1; });
|
|
518
|
-
return set;
|
|
519
|
-
}, {});
|
|
520
|
-
var candidates = [];
|
|
521
|
-
rows.forEach(function (problem) {
|
|
522
|
-
((problem && problem.examples) || []).forEach(function (example) {
|
|
523
|
-
candidates.push({ problem: problem, example: example, weight: Math.max(Number(example.tokens || 0), Number(example.turnWasteTokens || 0)) });
|
|
524
|
-
});
|
|
525
|
-
});
|
|
526
|
-
candidates.sort(function (a, b) { return b.weight - a.weight; });
|
|
527
|
-
return { spec: spec, rows: rows, tokens: tokens, canonicalTokens: canonicalTokens, count: count, sessions: Object.keys(sessions).length, best: candidates[0] || null };
|
|
528
|
-
}
|
|
529
|
-
var sessionTurnMap = null;
|
|
530
|
-
function sessionTurnTotal(sessionId) {
|
|
531
|
-
if (!sessionId) return 0;
|
|
532
|
-
if (!sessionTurnMap) {
|
|
533
|
-
sessionTurnMap = {};
|
|
534
|
-
var gs = gsClean(report && report.canonicalGoldenStandard);
|
|
535
|
-
var sources = gs ? [gs, gs.sourceReports && gs.sourceReports.codex, gs.sourceReports && gs.sourceReports.claudeCode] : [];
|
|
536
|
-
sources.forEach(function (src) {
|
|
537
|
-
(((src || {}).sessions) || []).forEach(function (row) {
|
|
538
|
-
if (row && row.session && Number(row.turns)) sessionTurnMap[row.session] = Number(row.turns);
|
|
539
|
-
});
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
return sessionTurnMap[sessionId] || sessionTurnMap[String(sessionId).slice(0, 8)] || 0;
|
|
543
|
-
}
|
|
544
|
-
function auditExampleHtml(group) {
|
|
545
|
-
var candidate = group.best;
|
|
546
|
-
if (!candidate) {
|
|
547
|
-
return '<details class="audit-example"><summary>No turn-level record captured</summary></details>';
|
|
548
|
-
}
|
|
549
|
-
var p = candidate.problem || {}, ex = candidate.example || {};
|
|
550
|
-
var input = Number(ex.turnInputTokens || 0);
|
|
551
|
-
var waste = Number(ex.turnWasteTokens || 0);
|
|
552
|
-
var problemTokens = Number(ex.tokens || 0);
|
|
553
|
-
var wastePct = gsExamplePct(waste, input);
|
|
554
|
-
var problemPct = gsExamplePct(problemTokens, input);
|
|
555
|
-
var source = gsAgentLabel(ex.agentSource);
|
|
556
|
-
var sourceKey = ex.agentSource === 'claude-code' ? 'claude-code' : 'codex';
|
|
557
|
-
var sourceIcon = sourceKey === 'claude-code' ? '/hud-assets/claude.svg' : '/hud-assets/codex.svg';
|
|
558
|
-
var sourceAccent = sourceKey === 'claude-code' ? '#D97757' : '#3941FF';
|
|
559
|
-
var sessionName = ex.sessionTitle || ((ex.repo || source) + ' session');
|
|
560
|
-
var totalTurns = Number(ex.sessionTurns || 0) || sessionTurnTotal(ex.session);
|
|
561
|
-
var turnLabel = ex.turn ? 'Turn ' + number(ex.turn) + (totalTurns ? ' of ' + number(totalTurns) : '') : '';
|
|
562
|
-
var eventMs = Number(ex.timestampMs || ex.sessionStartedAt || 0);
|
|
563
|
-
var whenClock = '';
|
|
564
|
-
var whenRel = '';
|
|
565
|
-
if (eventMs) {
|
|
566
|
-
var now = Date.now();
|
|
567
|
-
var eventDate = new Date(eventMs);
|
|
568
|
-
var sameDay = new Date(now).toDateString() === eventDate.toDateString();
|
|
569
|
-
var daysAgo = Math.max(0, Math.floor((new Date(now).setHours(0,0,0,0) - new Date(eventMs).setHours(0,0,0,0)) / 86400000));
|
|
570
|
-
whenClock = eventDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
|
|
571
|
-
whenRel = sameDay ? 'Today' : number(daysAgo) + (daysAgo === 1 ? ' day ago' : ' days ago');
|
|
572
|
-
}
|
|
573
|
-
var prompt = gsExamplePreview(ex.prompt, 700) || 'The scan did not retain a prompt preview for this turn.';
|
|
574
|
-
var signal = gsExampleProblemSignal(p, ex) || gsProblemExampleText(ex) || gsProblemDefinition(p);
|
|
575
|
-
var retainedLabel = group.spec.findingNoun || 'retained items';
|
|
576
|
-
var retainedExplain = ex.evidence || signal;
|
|
577
|
-
var retainedRecord = ex.command || ex.file || '';
|
|
578
|
-
var shortId = ex.session ? String(ex.session).slice(0, 8) : '';
|
|
579
|
-
return '<details class="audit-example">' +
|
|
580
|
-
'<summary>' +
|
|
581
|
-
'<img class="aet-icon" src="' + sourceIcon + '" alt="' + esc(source) + '" title="' + esc(source) + '" />' +
|
|
582
|
-
'<span class="aet-name">' + esc(sessionName) + '</span>' +
|
|
583
|
-
(shortId ? '<span class="aet-id">' + esc(shortId) + '</span>' : '') +
|
|
584
|
-
(whenRel ? '<span class="aet-when">' + esc(whenRel) + '</span>' : '') +
|
|
585
|
-
'</summary>' +
|
|
586
|
-
'<div class="audit-example-body">' +
|
|
587
|
-
'<div class="audit-session-locator">' +
|
|
588
|
-
'<button type="button" class="audit-session-open" data-open-session data-session-source="' + esc(sourceKey) + '" data-session-id="' + esc(ex.session || '') + '" style="--session-accent:' + sourceAccent + '">' +
|
|
589
|
-
'<span class="audit-session-name"><img src="' + sourceIcon + '" alt="' + esc(source) + '" title="' + esc(source) + '" /><span>' + esc(sessionName) + '</span></span>' +
|
|
590
|
-
'</button>' +
|
|
591
|
-
'<div class="audit-session-meta"><span class="audit-session-repo">' + AUDIT_GITHUB_MARK + esc(ex.repo || 'workspace') + '</span>' + (turnLabel ? '<span>·</span><span>' + esc(turnLabel) + '</span>' : '') + (eventMs ? '<span>·</span><time title="' + esc(new Date(eventMs).toLocaleString()) + '">' + esc(whenRel + ' · ' + whenClock) + '</time>' : '') + '</div>' +
|
|
592
|
-
'<div class="audit-session-status" aria-live="polite"></div>' +
|
|
593
|
-
'</div>' +
|
|
594
|
-
'<ul class="audit-turn-ledger">' +
|
|
595
|
-
'<li><span>Terminal window input</span><strong>' + big(input) + '</strong></li>' +
|
|
596
|
-
'<li><span>Noise in this window</span><strong>' + big(waste) + ' <em>· ' + number(wastePct) + '% of input</em></strong></li>' +
|
|
597
|
-
'<li><span>Attributed in this window</span><strong>' + big(problemTokens) + ' <em>· ' + number(problemPct) + '% of input</em></strong></li>' +
|
|
598
|
-
'</ul>' +
|
|
599
|
-
'<span class="audit-example-label">You said</span>' +
|
|
600
|
-
'<blockquote class="audit-prompt">' + esc(prompt) + '</blockquote>' +
|
|
601
|
-
'<details class="audit-retained">' +
|
|
602
|
-
'<summary>What stayed in this terminal window · ' + big(problemTokens) + ' tokens <kbd>Enter</kbd></summary>' +
|
|
603
|
-
'<div class="audit-retained-item">' +
|
|
604
|
-
'<b>' + esc(retainedLabel) + '</b><strong>' + big(problemTokens) + '</strong>' +
|
|
605
|
-
'<p>' + esc(retainedExplain) + '</p>' +
|
|
606
|
-
(retainedRecord ? '<code>' + esc(retainedRecord) + '</code>' : '') +
|
|
607
|
-
(ex.session ? '<button type="button" class="audit-retained-open" data-open-session data-session-source="' + esc(sourceKey) + '" data-session-id="' + esc(ex.session) + '" style="--session-accent:' + sourceAccent + '">See it in the original session' + (ex.turn ? ' · turn ' + number(ex.turn) : '') + ' ↗</button>' : '') +
|
|
608
|
-
'</div>' +
|
|
609
|
-
'</details>' +
|
|
610
|
-
'</div>' +
|
|
611
|
-
'</details>';
|
|
612
|
-
}
|
|
613
|
-
function auditRowHtml(group, index, totalWaste, slice) {
|
|
614
|
-
var pct = totalWaste ? Math.round((group.tokens / totalWaste) * 100) : 0;
|
|
615
|
-
var pieSlices = [slice ? { start: slice.start, end: slice.end, color: slice.color } : { start: 0, end: pct, color: '#6d84b4' }];
|
|
616
|
-
var noun = group.spec.findingNoun || 'events';
|
|
617
|
-
var nounCap = noun.charAt(0).toUpperCase() + noun.slice(1);
|
|
618
|
-
return '<section class="audit-row" id="audit-' + esc(group.spec.key) + '">' +
|
|
619
|
-
'<div class="audit-share"><canvas class="audit-share-pie" data-pencil-pie data-radius="90" data-slices="' + esc(JSON.stringify(pieSlices)) + '" aria-label="' + esc(pct) + '% of all noise"></canvas><strong>' + esc(pct) + '%</strong><span>of noise</span></div>' +
|
|
620
|
-
'<div class="audit-copy">' +
|
|
621
|
-
'<h3>' + esc(group.spec.label) + '</h3>' +
|
|
622
|
-
'<p>' + esc(group.spec.explanation) + '</p>' +
|
|
623
|
-
'<div class="audit-facts">' +
|
|
624
|
-
'<div class="audit-fact"><strong>' + big(group.tokens) + ' projected billed tokens</strong><span> across all API requests</span></div>' +
|
|
625
|
-
'<div class="audit-fact"><span>Canonical terminal-window evidence: </span><strong>' + big(group.canonicalTokens) + ' tokens</strong></div>' +
|
|
626
|
-
'<div class="audit-fact"><span>' + esc(nounCap) + ' detected: </span><strong>' + (group.count ? number(group.count) + ' pieces' : 'none counted') + '</strong><span> in all sessions</span></div>' +
|
|
627
|
-
(group.count ? '<div class="audit-fact"><span>Average projected impact per detected piece: </span><strong>~' + big(Math.round(group.tokens / group.count)) + ' billed tokens</strong></div>' : '') +
|
|
628
|
-
'</div>' +
|
|
629
|
-
auditExampleHtml(group) +
|
|
630
|
-
'</div>' +
|
|
631
|
-
'</section>';
|
|
632
|
-
}
|
|
633
|
-
function gsContextAuditStream(r) {
|
|
634
|
-
var gs = gsClean(r && r.canonicalGoldenStandard);
|
|
635
|
-
if (!gs) return '';
|
|
636
|
-
var s = gs.summary || {};
|
|
637
|
-
var wastePct = Math.round(gsDisplayWastePct(gs));
|
|
638
|
-
var projection = gsBillingProjection(r, gs);
|
|
639
|
-
var groups = AUDIT_GROUPS.map(function (spec) { return auditGroupData(gs, spec, projection); })
|
|
640
|
-
.sort(function (a, b) { return b.tokens - a.tokens; });
|
|
641
|
-
var totalWaste = Math.max(1, Number(projection.projectedWasteTokens || 0));
|
|
642
|
-
var dateLabel = function (value) {
|
|
643
|
-
if (!value) return 'Unknown';
|
|
644
|
-
var parsed = new Date(value.length === 10 ? value + 'T12:00:00' : value);
|
|
645
|
-
return parsed.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
|
|
646
|
-
};
|
|
647
|
-
var periodDateLabel = function (value) {
|
|
648
|
-
if (!value) return 'Unknown';
|
|
649
|
-
var parsed = new Date(value.length === 10 ? value + 'T12:00:00' : value);
|
|
650
|
-
var day = parsed.getDate();
|
|
651
|
-
var mod100 = day % 100;
|
|
652
|
-
var suffix = mod100 >= 11 && mod100 <= 13 ? 'th' : ({ 1: 'st', 2: 'nd', 3: 'rd' }[day % 10] || 'th');
|
|
653
|
-
return parsed.toLocaleDateString('en-US', { month: 'long' }).replace(/ \d+$/, '') + ' ' + day + suffix + ', ' + parsed.getFullYear();
|
|
654
|
-
};
|
|
655
|
-
var first = r && r.firstSession || null;
|
|
656
|
-
var numericDate = function (value) {
|
|
657
|
-
if (!value) return 'Unknown';
|
|
658
|
-
var parsed = new Date(value.length === 10 ? value + 'T12:00:00' : value);
|
|
659
|
-
var mm = String(parsed.getMonth() + 1).padStart(2, '0');
|
|
660
|
-
var dd = String(parsed.getDate()).padStart(2, '0');
|
|
661
|
-
return mm + '/' + dd + '/' + parsed.getFullYear();
|
|
662
|
-
};
|
|
663
|
-
var periodEl = document.getElementById("report-period");
|
|
664
|
-
if (periodEl) {
|
|
665
|
-
var periodStart = (first && first.date) || (r && r.scanStartDate);
|
|
666
|
-
periodEl.innerHTML = '<em>' + esc(numericDate(periodStart)) + '</em><span class="navAxis"></span><em class="isNow">' + esc(numericDate(r && r.scanEndDate)) + '</em>';
|
|
667
|
-
}
|
|
668
|
-
var firstSource = first && first.source === 'claude-code' ? 'Claude Code' : 'Codex';
|
|
669
|
-
var coverage = r && r.activeSessionCoverage || {};
|
|
670
|
-
// Prefer canonical per-source counts so the "X Codex + Y Claude" equation matches the
|
|
671
|
-
// ledger's sessionsAnalyzed exactly (activeSessionCoverage misses archived sessions).
|
|
672
|
-
var codexSessions = Number((gs.sourceReports && gs.sourceReports.codex && gs.sourceReports.codex.summary && gs.sourceReports.codex.summary.sessionsAnalyzed) || coverage.codex || 0);
|
|
673
|
-
var claudeSessions = Number((gs.sourceReports && gs.sourceReports.claudeCode && gs.sourceReports.claudeCode.summary && gs.sourceReports.claudeCode.summary.sessionsAnalyzed) || coverage.claudeCode || 0);
|
|
674
|
-
var averageSessionTokens = Number(s.sessionsAnalyzed || 0) ? Math.round(Number(s.officialInputTokens || 0) / Number(s.sessionsAnalyzed || 1)) : 0;
|
|
675
|
-
var averageContextTokens = Number(s.turnsAnalyzed || 0) ? Math.round(Number(s.officialInputTokens || 0) / Number(s.turnsAnalyzed || 1)) : 0;
|
|
676
|
-
var groupedNoiseTokens = groups.reduce(function (sum, group) { return sum + Number(group.tokens || 0); }, 0);
|
|
677
|
-
var groupedNoisePct = totalWaste ? Math.round((groupedNoiseTokens / totalWaste) * 100) : 0;
|
|
678
|
-
// One shared clock: the master pie carves the attributed sector clockwise in display order,
|
|
679
|
-
// and each problem row lights up the same fixed slice.
|
|
680
|
-
var SLICE_COLORS = ["#6d84b4", "#8ea0c6", "#aebcd6", "#ccd4e6"];
|
|
681
|
-
var sliceCursor = 0;
|
|
682
|
-
var slices = groups.map(function (group, i) {
|
|
683
|
-
var pct = totalWaste ? Math.round((group.tokens / totalWaste) * 100) : 0;
|
|
684
|
-
var start = sliceCursor;
|
|
685
|
-
sliceCursor += pct;
|
|
686
|
-
return { start: start, end: sliceCursor, color: SLICE_COLORS[i] || SLICE_COLORS[SLICE_COLORS.length - 1] };
|
|
687
|
-
});
|
|
688
|
-
return '<div class="context-audit">' +
|
|
689
|
-
'<section class="audit-intro">' +
|
|
690
|
-
'<h2><em>' + wastePct + '%</em> of provider-billed input is projected noise.</h2>' +
|
|
691
|
-
'<div class="audit-origin">' +
|
|
692
|
-
'<p class="audit-origin-line"><span>This analysis starts with a session in ' + esc(firstSource) + ' on</span><span class="audit-date-chip">' + esc(dateLabel(first && first.date)) + '</span><span>in</span><span class="audit-repo-chip">' + AUDIT_GITHUB_MARK + esc(first && first.repo || 'workspace') + '</span></p>' +
|
|
693
|
-
'</div>' +
|
|
694
|
-
'<div class="audit-proof">' +
|
|
695
|
-
'<div class="audit-sources">' +
|
|
696
|
-
'<span class="audit-source"><img src="/hud-assets/codex.svg" alt="Codex" /><strong>' + number(codexSessions) + '</strong> Codex sessions</span>' +
|
|
697
|
-
'<span class="audit-source-plus">+</span>' +
|
|
698
|
-
'<span class="audit-source"><img src="/hud-assets/claude.svg" alt="Claude Code" /><strong>' + number(claudeSessions) + '</strong> Claude Code sessions</span>' +
|
|
699
|
-
'<span class="audit-source-tail">on this machine</span>' +
|
|
700
|
-
'</div>' +
|
|
701
|
-
'</div>' +
|
|
702
|
-
'<p class="audit-narrative">The provider ledger records <b>' + big(projection.billedInputTokens) + ' input tokens</b> across every API request. Applying the ' + number(wastePct) + '% noise density estimated from the canonical terminal windows projects <b>' + big(projection.projectedWasteTokens) + ' billed input tokens</b> spent carrying old reasoning, screenshots, tool output, and discarded work.</p>' +
|
|
703
|
-
'<p class="audit-method">Method: one terminal provider request is classified per human turn, then its waste density is expanded across the exact all-request input ledger by a dynamic ' + (Math.round(Number(projection.billingMultiplier || 1) * 100) / 100).toFixed(2) + '× token factor. Aggregate billed figures are projections; the expandable turn examples below remain unscaled terminal-window evidence.</p>' +
|
|
704
|
-
'<ul class="audit-ledger" aria-label="Audit scope">' +
|
|
705
|
-
'<li class="audit-ledger-item"><span>Provider input across all requests</span><strong>' + big(projection.billedInputTokens) + '</strong></li>' +
|
|
706
|
-
'<li class="audit-ledger-item"><span>Terminal-window input analyzed</span><strong>' + big(projection.canonicalInputTokens) + '</strong></li>' +
|
|
707
|
-
'<li class="audit-ledger-item"><span>Effective payload expansion</span><strong>' + (Math.round(Number(projection.billingMultiplier || 1) * 100) / 100).toFixed(2) + '×</strong></li>' +
|
|
708
|
-
'<li class="audit-ledger-item"><span>Average terminal window per session</span><strong>' + big(averageSessionTokens) + '</strong></li>' +
|
|
709
|
-
'<li class="audit-ledger-item"><span>Average terminal window per turn</span><strong>' + big(averageContextTokens) + '</strong></li>' +
|
|
710
|
-
'<li class="audit-ledger-item"><span>Context SNR</span><strong>' + number(100 - wastePct) + '% signal</strong></li>' +
|
|
711
|
-
'</ul>' +
|
|
712
|
-
'<div class="audit-list-intro">' +
|
|
713
|
-
'<canvas class="audit-master-pie" data-pencil-pie data-radius="130" data-slices="' + esc(JSON.stringify(slices)) + '" role="img" aria-label="' + number(groupedNoisePct) + '% of noise split across four problem groups"></canvas>' +
|
|
714
|
-
'<p><b>' + number(groupedNoisePct) + '% of that projected noise</b> is concentrated in the four problem groups below.</p>' +
|
|
715
|
-
'</div>' +
|
|
716
|
-
'</section>' +
|
|
717
|
-
'<div class="audit-list">' + groups.map(function (group, index) { return auditRowHtml(group, index, totalWaste, slices[index]); }).join('') + '</div>' +
|
|
718
|
-
'<footer class="audit-end">' +
|
|
719
|
-
'<h3>Turn this lost context into memory.</h3>' +
|
|
720
|
-
'<p class="audit-memory-bridge">Your report found where context is being repeated. Next, Echo can turn selected coding sessions into memories your agents can recall.</p>' +
|
|
721
|
-
'<button class="rdo-cta" data-connect-echo>Build my first memories' +
|
|
722
|
-
'<img class="cta-face" src="/hud-assets/echo-face-cutout.png" alt="Echo" />' +
|
|
723
|
-
'<svg class="cta-arrow" viewBox="0 0 64 26" aria-hidden="true">' +
|
|
724
|
-
'<defs><filter id="ctaPencil" x="-20%" y="-40%" width="140%" height="180%"><feTurbulence type="fractalNoise" baseFrequency="0.05 0.12" numOctaves="3" seed="11"/><feDisplacementMap in="SourceGraphic" scale="2.6"/></filter></defs>' +
|
|
725
|
-
'<g filter="url(#ctaPencil)" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round">' +
|
|
726
|
-
'<path d="M2 14 C 18 12, 40 12, 57 13"/>' +
|
|
727
|
-
'<path d="M46 5 C 50 8.5, 54 11.5, 58 13 C 53.5 15, 49 18.5, 46 21.5"/>' +
|
|
728
|
-
'<path d="M6 15.5 C 22 14, 40 13.5, 52 14" opacity="0.4" stroke-width="1.4"/>' +
|
|
729
|
-
'</g>' +
|
|
730
|
-
'</svg>' +
|
|
731
|
-
'<span class="cta-tools">' +
|
|
732
|
-
'<img src="/hud-assets/codex.svg" alt="Codex" title="Codex" />' +
|
|
733
|
-
'<img src="/hud-assets/claude.svg" alt="Claude Code" title="Claude Code" />' +
|
|
734
|
-
'</span>' +
|
|
735
|
-
'</button>' +
|
|
736
|
-
'<p class="audit-footnote">Compiled from <code>~/.codex/sessions</code> and <code>~/.claude/projects</code>. API-equivalent figures are list-price estimates, not subscription charges. No transcript leaves this machine.</p>' +
|
|
737
|
-
'</footer>' +
|
|
738
|
-
'</div>';
|
|
739
|
-
}
|
|
740
|
-
function gsSwapSourceContent(container, markup) {
|
|
741
|
-
if (!container) return;
|
|
742
|
-
if (container.innerHTML === markup) return;
|
|
743
|
-
if (container._gsSwapTimer) clearTimeout(container._gsSwapTimer);
|
|
744
|
-
if (rdoReduce) { container.innerHTML = markup; return; }
|
|
745
|
-
container.classList.add("gs-source-fade");
|
|
746
|
-
container._gsSwapTimer = setTimeout(function () {
|
|
747
|
-
container.innerHTML = markup;
|
|
748
|
-
requestAnimationFrame(function () {
|
|
749
|
-
container.classList.remove("gs-source-fade");
|
|
750
|
-
container._gsSwapTimer = null;
|
|
751
|
-
});
|
|
752
|
-
}, 95);
|
|
753
|
-
}
|
|
754
|
-
function rdoWire() {
|
|
755
|
-
var root = document.getElementById("setup-details"); if (!root) return;
|
|
756
|
-
if (!root._rdoClickDelegated) {
|
|
757
|
-
root._rdoClickDelegated = true;
|
|
758
|
-
root.addEventListener("click", function (event) {
|
|
759
|
-
var target = event.target && event.target.closest ? event.target : event.target && event.target.parentElement;
|
|
760
|
-
if (!target || !target.closest) return;
|
|
761
|
-
var example = target.closest("[data-gs-example]");
|
|
762
|
-
if (example && root.contains(example)) {
|
|
763
|
-
event.preventDefault();
|
|
764
|
-
rdoOpenProblemExample(example.getAttribute("data-gs-example"));
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
var button = target.closest("[data-gs-source]");
|
|
768
|
-
if (button && root.contains(button) && !button.disabled) {
|
|
769
|
-
event.preventDefault();
|
|
770
|
-
var key = button.getAttribute("data-gs-source");
|
|
771
|
-
var section = button.closest(".gs-section");
|
|
772
|
-
var canonical = gsClean(report && report.canonicalGoldenStandard);
|
|
773
|
-
var source = canonical && key ? gsSourceReport(canonical, key) : null;
|
|
774
|
-
if (!section || !source || !source.report || !source.report.summary) return;
|
|
775
|
-
Array.prototype.forEach.call(section.querySelectorAll("[data-gs-source]"), function (el) {
|
|
776
|
-
var active = el === button;
|
|
777
|
-
el.classList.toggle("active", active);
|
|
778
|
-
el.setAttribute("aria-pressed", active ? "true" : "false");
|
|
779
|
-
});
|
|
780
|
-
var callout = section.querySelector("[data-gs-source-callout]");
|
|
781
|
-
gsSwapSourceContent(callout, gsSourceCallout(source.report, source.label));
|
|
782
|
-
var detail = section.querySelector("[data-gs-source-detail]");
|
|
783
|
-
gsSwapSourceContent(detail, gsSourceDetail(source.report, source.label));
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
var dlg = document.getElementById("echo-note");
|
|
789
|
-
if (dlg) { var cl = dlg.querySelector(".nclose"); if (cl) cl.onclick = function () { dlg.close(); }; dlg.onclick = function (e) { if (e.target === dlg) dlg.close(); }; }
|
|
790
|
-
}
|
|
791
|
-
function renderForensicReport(message) {
|
|
792
|
-
var r = report;
|
|
793
|
-
if (!r) { showReport(); return; }
|
|
794
|
-
if (!previewState && (!reportEnvelope || reportEnvelope.kind !== "ready" || reportEnvelope.report !== r)) {
|
|
795
|
-
throw new Error("A validated local report envelope is required before rendering.");
|
|
796
|
-
}
|
|
797
|
-
if (!reportMounted || !document.getElementById("setup-details")) mountReportShell();
|
|
798
|
-
setCityMode(true);
|
|
799
|
-
scheduleReportToCity();
|
|
800
|
-
setHead("AI Coding City", connected ? "Signed in" : "Local");
|
|
801
|
-
var dArrow = document.getElementById("details-arrow");
|
|
802
|
-
if (dArrow) dArrow.classList.remove("hidden");
|
|
803
|
-
|
|
804
|
-
if (!gsClean(r.canonicalGoldenStandard)) {
|
|
805
|
-
throw new Error("Validated canonical analysis is required before rendering.");
|
|
806
|
-
}
|
|
807
|
-
var details = document.getElementById("setup-details");
|
|
808
|
-
if (!details) throw new Error("The report surface could not be mounted.");
|
|
809
|
-
details.innerHTML =
|
|
810
|
-
(message ? '<div class="notice">' + esc(message) + '</div>' : '') +
|
|
811
|
-
gsContextAuditStream(r);
|
|
812
|
-
bindConnect();
|
|
813
|
-
bindSessionLinks();
|
|
814
|
-
bindDetailsArrow();
|
|
815
|
-
renderPencilPies();
|
|
816
|
-
rdoWire();
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
`;
|