@echomem/mcp 1.4.7 → 1.4.9
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 +35 -9
- package/assets/canonical-scorer/README.md +18 -0
- package/assets/canonical-scorer/analyze-10-problems.mjs +857 -0
- package/assets/canonical-scorer/build-session-waste-dashboard.mjs +1628 -0
- package/assets/canonical-scorer/golden_anchors.mjs +83 -0
- package/assets/canonical-scorer/optimizable_detail.mjs +633 -0
- package/assets/hud/claude.svg +1 -0
- package/assets/hud/codex.svg +1 -0
- package/assets/hud/session-viewer.html +35 -0
- package/dist/city/chaos-to-clarity-pencil.html +582 -0
- package/dist/city/echo-ai-city-only.html +1126 -109
- package/dist/city/echo-ai-city-only.template.html +1126 -109
- package/dist/city/echo-face-cutout.png +0 -0
- package/dist/city/pencil-pie-generator.html +883 -0
- package/dist/city/pencil-webgl-landscape.html +1239 -0
- package/dist/city/spatial-fan-story.html +479 -0
- package/dist/codex-session-files.js +283 -0
- package/dist/codex-sync.js +7 -2
- package/dist/context-analysis/canonical-golden.js +47 -0
- package/dist/context-analysis/claude-native-canonical.js +1193 -0
- package/dist/context-analysis/vendored-canonical.js +793 -0
- package/dist/context-analysis/workspace-report.js +1838 -0
- package/dist/context-metrics/calculate.js +56 -0
- package/dist/context-metrics/model-limits.js +26 -0
- package/dist/context-metrics/types.js +1 -0
- package/dist/forensics-10-problems.js +7 -6
- package/dist/forensics.js +863 -132
- package/dist/hud/adapters.js +8 -4
- package/dist/hud/autostart.js +66 -0
- package/dist/hud/cli.js +31 -0
- package/dist/hud/electron-main.js +182 -19
- package/dist/hud/metric.js +13 -4
- package/dist/hud/monitor.js +171 -84
- package/dist/hud/preload.cjs +3 -0
- package/dist/hud/server.js +321 -4
- package/dist/hud/web.js +880 -270
- package/dist/index.js +122 -24
- package/dist/local-data-paths.js +87 -0
- package/dist/migrate.js +55 -29
- package/dist/report.js +101 -40
- package/dist/setup-page.js +4257 -245
- package/dist/setup-preview.js +245 -0
- package/dist/setup.js +786 -75
- package/dist/v1-contract.js +20 -2
- package/package.json +6 -4
- package/templates/echomem-recall.md +2 -2
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function calculateContextMetrics(input) {
|
|
2
|
+
const latestInputTokens = nonNegative(input.latestInputTokens);
|
|
3
|
+
const modelContextLimitTokens = optionalPositive(input.modelContextLimitTokens);
|
|
4
|
+
const currentResidentWasteTokens = nonNegative(input.currentResidentWasteTokens);
|
|
5
|
+
const currentResidentUsefulTokens = Math.max(0, latestInputTokens - currentResidentWasteTokens);
|
|
6
|
+
const newOutputThisTurnTokens = optionalNonNegative(input.newOutputThisTurnTokens);
|
|
7
|
+
const newWasteThisTurnTokens = optionalNonNegative(input.newWasteThisTurnTokens);
|
|
8
|
+
const contextFullnessPct = modelContextLimitTokens ? latestInputTokens / modelContextLimitTokens : undefined;
|
|
9
|
+
const noisePct = latestInputTokens > 0 ? currentResidentWasteTokens / latestInputTokens : 0;
|
|
10
|
+
const usefulPct = latestInputTokens > 0 ? currentResidentUsefulTokens / latestInputTokens : 0;
|
|
11
|
+
const projectedNextFullnessPct = modelContextLimitTokens && newOutputThisTurnTokens !== undefined
|
|
12
|
+
? (latestInputTokens + newOutputThisTurnTokens) / modelContextLimitTokens
|
|
13
|
+
: undefined;
|
|
14
|
+
const healthScorePct = calculateHealthScorePct({
|
|
15
|
+
noisePct,
|
|
16
|
+
contextFullnessPct,
|
|
17
|
+
projectedNextFullnessPct,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
latestInputTokens,
|
|
21
|
+
modelContextLimitTokens,
|
|
22
|
+
currentResidentWasteTokens,
|
|
23
|
+
currentResidentUsefulTokens,
|
|
24
|
+
...(newOutputThisTurnTokens !== undefined ? { newOutputThisTurnTokens } : {}),
|
|
25
|
+
...(newWasteThisTurnTokens !== undefined ? { newWasteThisTurnTokens } : {}),
|
|
26
|
+
...(contextFullnessPct !== undefined ? { contextFullnessPct } : {}),
|
|
27
|
+
noisePct,
|
|
28
|
+
usefulPct,
|
|
29
|
+
healthScorePct,
|
|
30
|
+
snr: currentResidentWasteTokens > 0 ? currentResidentUsefulTokens / currentResidentWasteTokens : null,
|
|
31
|
+
...(projectedNextFullnessPct !== undefined ? { projectedNextFullnessPct } : {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function calculateHealthScorePct(input) {
|
|
35
|
+
const pressurePct = Math.max(input.contextFullnessPct ?? 0, input.projectedNextFullnessPct ?? 0);
|
|
36
|
+
// Noise is a direct quality loss. Fullness becomes a separate pressure penalty after 70% because
|
|
37
|
+
// clean-but-nearly-full sessions still become slow, brittle, or compaction-prone.
|
|
38
|
+
const fullnessPenaltyPct = pressurePct > 0.7
|
|
39
|
+
? clamp01((pressurePct - 0.7) / 0.3) * 35
|
|
40
|
+
: 0;
|
|
41
|
+
return Math.max(0, Math.min(100, Math.round(100 - input.noisePct * 100 - fullnessPenaltyPct)));
|
|
42
|
+
}
|
|
43
|
+
function nonNegative(value) {
|
|
44
|
+
return Number.isFinite(value) ? Math.max(0, Math.round(value)) : 0;
|
|
45
|
+
}
|
|
46
|
+
function optionalNonNegative(value) {
|
|
47
|
+
return value === undefined ? undefined : nonNegative(value);
|
|
48
|
+
}
|
|
49
|
+
function optionalPositive(value) {
|
|
50
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
|
|
51
|
+
return null;
|
|
52
|
+
return Math.round(value);
|
|
53
|
+
}
|
|
54
|
+
function clamp01(value) {
|
|
55
|
+
return Math.max(0, Math.min(1, value));
|
|
56
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const MODEL_CONTEXT_LIMITS = [
|
|
2
|
+
// Claude transcript logs do not carry context_window_size, so use the published family limit only
|
|
3
|
+
// as an explicit fallback. Codex logs should prefer their per-call model_context_window field.
|
|
4
|
+
{ match: /^claude-(?:opus|sonnet|haiku|fable)-4/i, tokens: 200_000 },
|
|
5
|
+
];
|
|
6
|
+
export function modelContextLimitTokensFor(model) {
|
|
7
|
+
const name = String(model || "").trim();
|
|
8
|
+
if (!name)
|
|
9
|
+
return null;
|
|
10
|
+
const found = MODEL_CONTEXT_LIMITS.find((entry) => entry.match.test(name));
|
|
11
|
+
return found?.tokens ?? null;
|
|
12
|
+
}
|
|
13
|
+
export function resolveModelContextLimit(params) {
|
|
14
|
+
const logged = positive(params.loggedLimitTokens);
|
|
15
|
+
if (logged)
|
|
16
|
+
return { tokens: logged, source: "logged" };
|
|
17
|
+
const fallback = modelContextLimitTokensFor(params.model);
|
|
18
|
+
if (fallback)
|
|
19
|
+
return { tokens: fallback, source: "model-default" };
|
|
20
|
+
return { tokens: null, source: "unavailable" };
|
|
21
|
+
}
|
|
22
|
+
function positive(value) {
|
|
23
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
|
|
24
|
+
return null;
|
|
25
|
+
return Math.round(value);
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
*
|
|
25
25
|
* All data stays local. No LLM calls. Transcripts never leave the machine.
|
|
26
26
|
*/
|
|
27
|
-
import os from "node:os";
|
|
28
27
|
import path from "node:path";
|
|
29
28
|
import { eachLine, walk } from "./report.js";
|
|
29
|
+
import { discoverCodexSessionFiles } from "./codex-session-files.js";
|
|
30
|
+
import { resolveClaudeProjectsDir } from "./local-data-paths.js";
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
31
32
|
// Constants
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
@@ -584,11 +585,11 @@ function aggregateResults(sessionResults, firstTs, lastTs) {
|
|
|
584
585
|
// ---------------------------------------------------------------------------
|
|
585
586
|
export function buildProblemReport(opts) {
|
|
586
587
|
const sources = opts?.sources ?? ["codex", "claude"];
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const claudeFiles =
|
|
591
|
-
? walk(
|
|
588
|
+
const codexDiscovery = sources.includes("codex") ? discoverCodexSessionFiles({ includeArchived: true }) : null;
|
|
589
|
+
const claudeRoot = sources.includes("claude") ? resolveClaudeProjectsDir() : null;
|
|
590
|
+
const codexFiles = codexDiscovery?.files.map((file) => file.path) ?? [];
|
|
591
|
+
const claudeFiles = claudeRoot
|
|
592
|
+
? walk(claudeRoot, (p) => p.endsWith(".jsonl"), (name) => name === "subagents" || name === "workflows").sort()
|
|
592
593
|
: [];
|
|
593
594
|
const total = codexFiles.length + claudeFiles.length;
|
|
594
595
|
let done = 0;
|