@echomem/mcp 1.4.43 → 1.4.44
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 +25 -28
- package/dist/config-files.js +63 -0
- 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/hud/hooks.js +43 -31
- package/dist/index.js +15 -79
- package/dist/local-jsonl.js +87 -0
- package/dist/migrate.js +6 -4
- 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 +33 -188
- package/dist/setup-page/client-lifecycle.js +31 -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 +314 -667
- package/dist/v1-contract.js +0 -8
- package/package.json +9 -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,97 +1,7 @@
|
|
|
1
1
|
// Generated by splitting the previous inline setup page. Keep this source readable: it is emitted into one browser IIFE.
|
|
2
2
|
export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dashboard (post-auth extraction) ---------- */
|
|
3
|
-
function acceptReportScanId(envelope) {
|
|
4
|
-
var scanId = envelope && typeof envelope.scanId === "string" ? envelope.scanId : "";
|
|
5
|
-
if (!scanId) throw new Error("Report response is missing its scan identity.");
|
|
6
|
-
if (activeScanId && activeScanId !== scanId) throw new Error("The local scan identity changed before the report completed.");
|
|
7
|
-
activeScanId = scanId;
|
|
8
|
-
}
|
|
9
|
-
function validateProductionReportEnvelope(envelope) {
|
|
10
|
-
if (!envelope || typeof envelope !== "object" || envelope.schemaVersion !== 1 || envelope.mode !== "production") {
|
|
11
|
-
throw new Error("Report response has an unsupported production schema.");
|
|
12
|
-
}
|
|
13
|
-
acceptReportScanId(envelope);
|
|
14
|
-
if (["scanning", "ready", "empty", "failed"].indexOf(envelope.kind) === -1) {
|
|
15
|
-
throw new Error("Report response has an unknown state.");
|
|
16
|
-
}
|
|
17
|
-
if (envelope.kind === "scanning") return envelope;
|
|
18
|
-
if (envelope.kind === "failed") {
|
|
19
|
-
if (!envelope.error || typeof envelope.error.code !== "string" || typeof envelope.error.message !== "string") {
|
|
20
|
-
throw new Error("Report failure response is malformed.");
|
|
21
|
-
}
|
|
22
|
-
return envelope;
|
|
23
|
-
}
|
|
24
|
-
var r = envelope.report;
|
|
25
|
-
if (!r || typeof r !== "object" || r.schemaVersion !== 1 || r.dataOrigin !== "local-workspace-scan") {
|
|
26
|
-
throw new Error("Report did not prove that it came from this local workspace scan.");
|
|
27
|
-
}
|
|
28
|
-
var scale = r.scale || {};
|
|
29
|
-
var sessions = Number(scale.sessionCount);
|
|
30
|
-
var gs = gsClean(r.canonicalGoldenStandard);
|
|
31
|
-
var summary = (gs && gs.summary) || {};
|
|
32
|
-
var canonicalSessions = Number(summary.sessionsAnalyzed || 0);
|
|
33
|
-
var canonicalInput = Number(summary.officialInputTokens || 0);
|
|
34
|
-
var skipped = Number((gs && gs.diagnostics && gs.diagnostics.skippedSessions) || 0);
|
|
35
|
-
if (envelope.kind === "empty") {
|
|
36
|
-
if (sessions !== 0 || canonicalSessions !== 0 || canonicalInput !== 0 || Number(scale.totalInputTokens || 0) !== 0) {
|
|
37
|
-
throw new Error("An empty report contained non-empty usage data.");
|
|
38
|
-
}
|
|
39
|
-
return envelope;
|
|
40
|
-
}
|
|
41
|
-
if (
|
|
42
|
-
!gs ||
|
|
43
|
-
!Number.isFinite(sessions) || sessions <= 0 ||
|
|
44
|
-
!Number.isFinite(canonicalSessions) || canonicalSessions <= 0 || canonicalSessions > sessions ||
|
|
45
|
-
!Number.isFinite(skipped) || skipped < 0 || canonicalSessions + skipped !== sessions ||
|
|
46
|
-
!Number.isFinite(canonicalInput) || canonicalInput <= 0
|
|
47
|
-
) {
|
|
48
|
-
throw new Error("Canonical analysis is incomplete or does not match the provider session cohort.");
|
|
49
|
-
}
|
|
50
|
-
if (!Array.isArray(r.repos) || r.repos.length === 0) {
|
|
51
|
-
throw new Error("The local report has no workspace rows to render.");
|
|
52
|
-
}
|
|
53
|
-
gsBillingProjection(r, gs);
|
|
54
|
-
return envelope;
|
|
55
|
-
}
|
|
56
|
-
function renderReportIssue(code, message) {
|
|
57
|
-
void message;
|
|
58
|
-
report = null;
|
|
59
|
-
reportEnvelope = null;
|
|
60
|
-
resetReportSurface();
|
|
61
|
-
var canContinue = String(code || "").indexOf("REPORT_") === 0 || code === "RENDER_FAILED";
|
|
62
|
-
setHead("We couldn't finish this scan", "Needs attention");
|
|
63
|
-
app.className = "reportMessageStage";
|
|
64
|
-
app.innerHTML =
|
|
65
|
-
'<section class="reportMessage" data-report-state="failed">' +
|
|
66
|
-
'<h2>We couldn’t finish this scan.</h2>' +
|
|
67
|
-
'<p>Your coding history is unchanged.</p>' +
|
|
68
|
-
(canContinue
|
|
69
|
-
? '<p>This optional report can be retried later.</p><div class="actions"><button type="button" class="primary" data-connect-echo>Continue without report</button></div>'
|
|
70
|
-
: '<p>Close this tab and run <code>echomem-mcp init</code> again.</p>') +
|
|
71
|
-
'<details class="reportTechnical"><summary>Technical details</summary><code>' + esc(code || "REPORT_FAILED") + '</code></details>' +
|
|
72
|
-
'</section>';
|
|
73
|
-
if (canContinue) bindConnect();
|
|
74
|
-
}
|
|
75
|
-
function renderEmptyReport() {
|
|
76
|
-
report = null;
|
|
77
|
-
reportEnvelope = null;
|
|
78
|
-
resetReportSurface();
|
|
79
|
-
setHead("No local coding history found", "Local scan complete");
|
|
80
|
-
app.className = "reportMessageStage";
|
|
81
|
-
app.innerHTML =
|
|
82
|
-
'<section class="reportMessage" data-report-state="empty">' +
|
|
83
|
-
'<h2>No coding history found.</h2>' +
|
|
84
|
-
'<p>You can continue now and import local coding history later.</p>' +
|
|
85
|
-
'<div class="actions"><button type="button" class="primary" data-connect-echo>Continue setup</button></div>' +
|
|
86
|
-
'</section>';
|
|
87
|
-
bindConnect();
|
|
88
|
-
}
|
|
89
|
-
function renderBridgeIssue() {
|
|
90
|
-
renderReportIssue("BRIDGE_UNREACHABLE", "The local bridge stopped answering before your report was ready. Your terminal may have closed or the process may have stopped.");
|
|
91
|
-
}
|
|
92
3
|
function renderDiscoveryCheck(slow) {
|
|
93
4
|
void slow;
|
|
94
|
-
setCityMode(false);
|
|
95
5
|
setExtractMode(false);
|
|
96
6
|
setReadyMode(true);
|
|
97
7
|
setHead("Loading", "Working");
|
|
@@ -99,7 +9,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
99
9
|
app.innerHTML = '<div class="discoverySpinner" role="status" aria-label="Loading"><span aria-hidden="true"></span></div>';
|
|
100
10
|
}
|
|
101
11
|
function renderDiscoveryCheckIssue(error) {
|
|
102
|
-
setCityMode(false);
|
|
103
12
|
setExtractMode(false);
|
|
104
13
|
setReadyMode(true);
|
|
105
14
|
setHead("Session refresh paused", "Needs attention");
|
|
@@ -107,7 +16,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
107
16
|
app.innerHTML =
|
|
108
17
|
'<div style="padding:24px"><h2>Echo could not refresh session status</h2>' +
|
|
109
18
|
'<p class="helper">Keep the terminal open and try the local check again.</p>' +
|
|
110
|
-
(error ? '<details class="
|
|
19
|
+
(error ? '<details class="technicalDetails"><summary>Technical details</summary><code>' + esc(error) + '</code></details>' : '') +
|
|
111
20
|
'<div class="actions"><button id="retryDiscoveryCheck" class="primary">Try again</button></div></div>';
|
|
112
21
|
var retry = document.getElementById("retryDiscoveryCheck");
|
|
113
22
|
if (retry) retry.onclick = function () {
|
|
@@ -116,13 +25,11 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
116
25
|
};
|
|
117
26
|
}
|
|
118
27
|
function renderLogoutPending() {
|
|
119
|
-
setCityMode(false);
|
|
120
28
|
setHead("Signing out locally", "Working");
|
|
121
29
|
app.className = "loading-panel";
|
|
122
30
|
app.innerHTML = '<div style="padding:24px"><h2>Signing this device out</h2><p class="helper">If Echo is scanning local files, this can take a few seconds to answer.</p></div>';
|
|
123
31
|
}
|
|
124
32
|
function renderLogoutIssue(error) {
|
|
125
|
-
setCityMode(false);
|
|
126
33
|
setHead("Sign out did not finish", "Needs attention");
|
|
127
34
|
app.className = "loading-panel";
|
|
128
35
|
app.innerHTML =
|
|
@@ -136,7 +43,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
136
43
|
if (statsPollStarted) return;
|
|
137
44
|
var statsEpoch = accountStateEpoch;
|
|
138
45
|
statsPollStarted = true;
|
|
139
|
-
setCityMode(false);
|
|
140
46
|
setExtractMode(false);
|
|
141
47
|
setReadyMode(true);
|
|
142
48
|
renderDiscoveryCheck(false);
|
|
@@ -169,23 +75,14 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
169
75
|
if (res.status !== 202) throw new Error(await res.text() || ("HTTP " + res.status));
|
|
170
76
|
} catch (e) {
|
|
171
77
|
lastError = e && e.message ? e.message : String(e);
|
|
172
|
-
if (!stats && Date.now() - started > 25000)
|
|
78
|
+
if (!stats && Date.now() - started > 25000) renderDiscoveryCheckIssue(lastError);
|
|
173
79
|
if (Date.now() - started > 20000) { statsSlow = true; if (stats) renderDashboard(); }
|
|
174
80
|
}
|
|
175
81
|
await sleep(statsSlow ? 1500 : 500);
|
|
176
82
|
}
|
|
177
83
|
} finally { statsPollStarted = false; }
|
|
178
84
|
}
|
|
179
|
-
// The plate slot: ONLY the WebGL clay city (echo-extraction-plate.html). It self-fetches /report
|
|
180
|
-
// (your repos) and polls /progress (extraction state), growing as memories are created. The old SVG
|
|
181
|
-
// fallback board was removed — it was bleeding through behind the transparent canvas (the double board).
|
|
182
|
-
function plateBlock() {
|
|
183
|
-
return '<div class="exPlate"><div class="plateStack">' +
|
|
184
|
-
'<iframe class="plateGl" title="Extraction plate" src="/city/echo-extraction-plate.html?nonce=' + encodeURIComponent(nonce) + '"></iframe>' +
|
|
185
|
-
'</div></div>';
|
|
186
|
-
}
|
|
187
85
|
function renderPlanGate() {
|
|
188
|
-
setCityMode(false);
|
|
189
86
|
setExtractMode(false);
|
|
190
87
|
setReadyMode(true);
|
|
191
88
|
setHead("Choose your Echo", "Plan");
|
|
@@ -198,7 +95,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
198
95
|
readyStage = "plan";
|
|
199
96
|
app.className = "planGateStage";
|
|
200
97
|
app.innerHTML =
|
|
201
|
-
setupJourney(
|
|
98
|
+
setupJourney(2) +
|
|
202
99
|
'<section class="planGateDecision">' +
|
|
203
100
|
'<div class="setupPlanSlot" id="setupPlan"></div>' +
|
|
204
101
|
'<aside class="planGateAccount" aria-label="Connected account"><div class="setupAccountSlot" id="setupAccount"></div></aside>' +
|
|
@@ -207,7 +104,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
207
104
|
renderAccountChoice();
|
|
208
105
|
}
|
|
209
106
|
function renderDashboard(error) {
|
|
210
|
-
setCityMode(false);
|
|
211
107
|
setExtractMode(false);
|
|
212
108
|
setReadyMode(true);
|
|
213
109
|
var migratable = stats && stats.migratable ? stats.migratable : {};
|
|
@@ -253,19 +149,17 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
253
149
|
? "<strong>" + esc(number(pendN)) + " sessions</strong> found."
|
|
254
150
|
: "<strong>" + esc(number(pendN)) + " sessions</strong> are ready to review."));
|
|
255
151
|
var sub = degradedWithoutCounts
|
|
256
|
-
? "You can finish setup now. Your conversations stay on this
|
|
152
|
+
? "You can finish setup now. Your conversations stay on this computer, and you can retry the history import later."
|
|
257
153
|
: !pendingTrusted
|
|
258
154
|
? "Echo is matching your local history against what is already in memory."
|
|
259
155
|
: (knownDone
|
|
260
156
|
? (skippedActive ? number(skippedActive) + " active conversation" + (skippedActive === 1 ? " is" : "s are") + " still changing, so Echo will pick them up later." : "Your history is already in EchoMem. Nothing new to extract.")
|
|
261
157
|
: (planLimited
|
|
262
158
|
? (importableNow > 0
|
|
263
|
-
? currentPlanLabel + " can import " + number(importableNow) + " of them. Choose which ones to bring into memory; the other " + number(deferredByPlan) + " stay on this
|
|
264
|
-
: currentPlanLabel + " has no coding-session imports remaining. These sessions stay on this
|
|
159
|
+
? currentPlanLabel + " can import " + number(importableNow) + " of them. Choose which ones to bring into memory; the other " + number(deferredByPlan) + " stay on this computer."
|
|
160
|
+
: currentPlanLabel + " has no coding-session imports remaining. These sessions stay on this computer.")
|
|
265
161
|
: ""));
|
|
266
|
-
// Mount the dashboard shell
|
|
267
|
-
// (Rebuilding app.innerHTML on every partial-scan poll reloaded the iframe → the city flickered.)
|
|
268
|
-
// Later polls only patch the text/counts in place; the iframe stays mounted.
|
|
162
|
+
// Mount the dashboard shell once; later polls only patch its text and counts.
|
|
269
163
|
var live = dashMounted && document.getElementById("exHeadline");
|
|
270
164
|
if (!live) {
|
|
271
165
|
extractMounted = false;
|
|
@@ -277,7 +171,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
277
171
|
// one-line strip, not stat cards; the ETA is a commitment-moment note under the button,
|
|
278
172
|
// not a display-size headline.
|
|
279
173
|
app.innerHTML =
|
|
280
|
-
setupJourney(
|
|
174
|
+
setupJourney(3) +
|
|
281
175
|
'<aside class="readyUtilityNav" aria-label="Account and plan settings">' +
|
|
282
176
|
'<div class="planGateAccount readyAccountNav" aria-label="Connected account"><div class="setupAccountSlot" id="setupAccount"></div></div>' +
|
|
283
177
|
'<div class="readyPlanNav" aria-label="Current plan"><span class="readyPlanNavLabel">Plan</span><div class="setupPlanSlot" id="setupPlan"></div></div>' +
|
|
@@ -325,7 +219,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
325
219
|
? '<span class="pf">' + srcIcon("codex", "CX") + '<strong>' + esc(number(codexN)) + '</strong> Codex</span>' +
|
|
326
220
|
'<span class="pfDot">·</span>' +
|
|
327
221
|
'<span class="pf">' + srcIcon("claude-desktop", "CL") + '<strong>' + esc(number(claudeN)) + '</strong> Claude Code</span>' +
|
|
328
|
-
'<span class="pfNote">found on this
|
|
222
|
+
'<span class="pfNote">found on this computer</span>'
|
|
329
223
|
: "");
|
|
330
224
|
// Reassurances live at the moment of commitment — right under the button.
|
|
331
225
|
document.getElementById("exEta").innerHTML = degradedWithoutCounts
|
|
@@ -392,14 +286,12 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
392
286
|
stopBillingPoll();
|
|
393
287
|
decisionMade = true;
|
|
394
288
|
extractionEnded = false;
|
|
395
|
-
setCityMode(false);
|
|
396
|
-
setScanMode(false);
|
|
397
289
|
setReadyMode(false);
|
|
398
290
|
setExtractMode(true);
|
|
399
291
|
setHead("Extracting", "Starting");
|
|
400
292
|
app.className = "extractionStartStage";
|
|
401
293
|
app.innerHTML =
|
|
402
|
-
setupJourney(
|
|
294
|
+
setupJourney(3) +
|
|
403
295
|
'<div class="discoverySpinner" role="status" aria-label="Starting import"><span aria-hidden="true"></span></div>';
|
|
404
296
|
var started;
|
|
405
297
|
try {
|
|
@@ -491,7 +383,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
491
383
|
function renderProgress(progress) {
|
|
492
384
|
if (extractionEnded) return;
|
|
493
385
|
lastExtractionProgress = progress;
|
|
494
|
-
setCityMode(false);
|
|
495
386
|
setExtractMode(true);
|
|
496
387
|
var status = progress.status;
|
|
497
388
|
var done = status === "completed";
|
|
@@ -503,18 +394,15 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
503
394
|
var pct = total > 0 ? Math.round((completed / total) * 100) : (done ? 100 : 0);
|
|
504
395
|
var preparing = (status === "starting" || status === "preparing");
|
|
505
396
|
app.className = "";
|
|
506
|
-
// Mount the shell
|
|
507
|
-
// each poll only rebuilds the text column.
|
|
397
|
+
// Mount the shell once; each poll only rebuilds the text column.
|
|
508
398
|
if (!extractMounted) {
|
|
509
399
|
app.innerHTML =
|
|
510
|
-
'<div id="setupJourneySlot">' + setupJourney(
|
|
511
|
-
'<section class="exHero"><div class="exCopy" id="exText"></div>'
|
|
512
|
-
plateBlock() +
|
|
513
|
-
'</section>';
|
|
400
|
+
'<div id="setupJourneySlot">' + setupJourney(3) + '</div>' +
|
|
401
|
+
'<section class="exHero"><div class="exCopy" id="exText"></div></section>';
|
|
514
402
|
extractMounted = true;
|
|
515
403
|
}
|
|
516
404
|
var journeySlot = document.getElementById("setupJourneySlot");
|
|
517
|
-
if (journeySlot) journeySlot.innerHTML = setupJourney(done ?
|
|
405
|
+
if (journeySlot) journeySlot.innerHTML = setupJourney(done ? 4 : 3);
|
|
518
406
|
var shell = app.querySelector(".exHero");
|
|
519
407
|
if (shell) {
|
|
520
408
|
shell.classList.toggle("exHeroResult", done || stopped);
|
|
@@ -1035,13 +923,12 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1035
923
|
? account.email
|
|
1036
924
|
: "the connected account";
|
|
1037
925
|
accountSwitchPending = true;
|
|
1038
|
-
setCityMode(false);
|
|
1039
926
|
setExtractMode(false);
|
|
1040
927
|
setReadyMode(true);
|
|
1041
928
|
setHead("Switch EchoMem account", "Confirm");
|
|
1042
929
|
app.className = "accountSwitchStage";
|
|
1043
930
|
app.innerHTML =
|
|
1044
|
-
setupJourney(
|
|
931
|
+
setupJourney(3) +
|
|
1045
932
|
'<section class="accountSwitchConfirm" aria-labelledby="accountSwitchTitle">' +
|
|
1046
933
|
'<button type="button" class="flowBack" id="cancelAccountSwitch">← Back</button>' +
|
|
1047
934
|
'<h2 id="accountSwitchTitle">Switch account?</h2>' +
|
|
@@ -1090,41 +977,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1090
977
|
function paidRecallPlan(plan) {
|
|
1091
978
|
return ["pro", "power", "team", "enterprise"].indexOf(String(plan || "").toLowerCase()) >= 0;
|
|
1092
979
|
}
|
|
1093
|
-
var planQuotaCatalog = null;
|
|
1094
|
-
var planQuotaCatalogLastFetchedAt = 0;
|
|
1095
|
-
function quotaLimitsForPlan(plan) {
|
|
1096
|
-
var normalized = String(plan || "free").toLowerCase();
|
|
1097
|
-
if (normalized === "team") normalized = "pro";
|
|
1098
|
-
if (normalized === "enterprise") normalized = "power";
|
|
1099
|
-
var limits = planQuotaCatalog && planQuotaCatalog[normalized];
|
|
1100
|
-
if (!limits || typeof limits !== "object") return null;
|
|
1101
|
-
if (
|
|
1102
|
-
typeof limits.historicalConversationLimit !== "number" ||
|
|
1103
|
-
typeof limits.memoryProcessingInputTokensWeeklyLimit !== "number" ||
|
|
1104
|
-
typeof limits.memorySearchWeeklyLimit !== "number"
|
|
1105
|
-
) return null;
|
|
1106
|
-
return limits;
|
|
1107
|
-
}
|
|
1108
|
-
function quotaNumber(value) {
|
|
1109
|
-
return number(Math.max(0, Math.floor(value)));
|
|
1110
|
-
}
|
|
1111
|
-
function quotaTokens(value) {
|
|
1112
|
-
var normalized = Math.max(0, Math.floor(value));
|
|
1113
|
-
if (normalized > 0 && normalized % 1000000 === 0) return String(normalized / 1000000) + "M";
|
|
1114
|
-
return normalized > 0 && normalized % 1000 === 0 ? String(normalized / 1000) + "K" : quotaNumber(normalized);
|
|
1115
|
-
}
|
|
1116
|
-
async function refreshPlanQuotaCatalog() {
|
|
1117
|
-
if (Date.now() - planQuotaCatalogLastFetchedAt < 60000) return;
|
|
1118
|
-
try {
|
|
1119
|
-
var response = await fetch("https://echo-mem-chrome.vercel.app/api/public/plan-quotas", { cache: "no-store" });
|
|
1120
|
-
var payload = response.ok ? await response.json() : null;
|
|
1121
|
-
if (!payload || !payload.plans || typeof payload.plans !== "object") return;
|
|
1122
|
-
planQuotaCatalog = payload.plans;
|
|
1123
|
-
planQuotaCatalogLastFetchedAt = Date.now();
|
|
1124
|
-
} catch (_) {
|
|
1125
|
-
// Generic labels remain visible until the managed catalog is available.
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
980
|
function setupPlanPrice(plan) {
|
|
1129
981
|
return plan === "power" ? "$100" : "$20";
|
|
1130
982
|
}
|
|
@@ -1186,7 +1038,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1186
1038
|
var statusLabel = canceled ? "Canceled" : (state === "trialing" ? "Trial active" : "Active");
|
|
1187
1039
|
var lifecycle = paidPlanLifecycleText(plan);
|
|
1188
1040
|
var quota = billingStatus && billingStatus.historicalConversationQuota;
|
|
1189
|
-
var limit = quota && typeof quota.limit === "number" ? quota.limit :
|
|
1041
|
+
var limit = quota && typeof quota.limit === "number" ? quota.limit : (plan === "power" ? 2000 : 500);
|
|
1190
1042
|
var moneyFact = canceled
|
|
1191
1043
|
? "No future charge"
|
|
1192
1044
|
: (state === "trialing" ? setupPlanPrice(plan) + "/month after trial" : setupPlanPrice(plan) + "/month");
|
|
@@ -1195,7 +1047,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1195
1047
|
'<section class="billingCommitment' + (canceled ? ' is-canceled' : '') + '" aria-label="Current plan commitment">' +
|
|
1196
1048
|
'<div class="billingCommitmentIdentity"><span>' + esc(statusLabel) + '</span><strong>' + esc(planLabel) + '</strong></div>' +
|
|
1197
1049
|
'<div class="billingCommitmentFacts">' +
|
|
1198
|
-
'<span><b>' + esc(
|
|
1050
|
+
'<span><b>' + esc(number(limit)) + '</b> coding-session imports</span>' +
|
|
1199
1051
|
'<span><b>' + esc(moneyFact) + '</b></span>' +
|
|
1200
1052
|
'</div>' +
|
|
1201
1053
|
'<p>' + esc(lifecycle || "Your paid plan is active.") + '</p>' +
|
|
@@ -1206,14 +1058,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1206
1058
|
}
|
|
1207
1059
|
function setupPlanDefinition(plan) {
|
|
1208
1060
|
var trialAvailable = !billingStatus || billingStatus.trialAvailable !== false;
|
|
1209
|
-
var limits = quotaLimitsForPlan(plan);
|
|
1210
|
-
var features = limits
|
|
1211
|
-
? [
|
|
1212
|
-
quotaNumber(limits.historicalConversationLimit) + " past chats",
|
|
1213
|
-
quotaTokens(limits.memoryProcessingInputTokensWeeklyLimit) + " new-chat tokens / week",
|
|
1214
|
-
quotaNumber(limits.memorySearchWeeklyLimit) + " memory recalls / week"
|
|
1215
|
-
]
|
|
1216
|
-
: ["Past-chat imports", "Weekly new-chat processing", "Weekly memory recalls"];
|
|
1217
1061
|
if (plan === "power") return {
|
|
1218
1062
|
id: "power",
|
|
1219
1063
|
name: "Power",
|
|
@@ -1223,7 +1067,11 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1223
1067
|
cadence: trialAvailable ? "per month · 14-day trial" : "per month · billed immediately",
|
|
1224
1068
|
sticker: "/hud-assets/echo-pricing-power-sticker.png",
|
|
1225
1069
|
stickerAlt: "Power Echo arrives with a gold key and a crew of notebook helpers.",
|
|
1226
|
-
features:
|
|
1070
|
+
features: [
|
|
1071
|
+
"2,000 past chats",
|
|
1072
|
+
"agent-heavy memory",
|
|
1073
|
+
"2,000 memory recalls / week"
|
|
1074
|
+
]
|
|
1227
1075
|
};
|
|
1228
1076
|
if (plan === "pro") return {
|
|
1229
1077
|
id: "pro",
|
|
@@ -1235,7 +1083,11 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1235
1083
|
sticker: "/hud-assets/echo-pricing-pro-sticker.png",
|
|
1236
1084
|
stickerAlt: "Pro Echo organizes tabbed notebooks and a daily refresh control.",
|
|
1237
1085
|
popular: true,
|
|
1238
|
-
features:
|
|
1086
|
+
features: [
|
|
1087
|
+
"500 past chats",
|
|
1088
|
+
"daily memory updates",
|
|
1089
|
+
"500 memory recalls / week"
|
|
1090
|
+
]
|
|
1239
1091
|
};
|
|
1240
1092
|
return {
|
|
1241
1093
|
id: "free",
|
|
@@ -1246,7 +1098,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1246
1098
|
cadence: "free forever · no card needed",
|
|
1247
1099
|
sticker: "/hud-assets/echo-pricing-free-sticker.png",
|
|
1248
1100
|
stickerAlt: "Original Echo hugs one simple memory card.",
|
|
1249
|
-
features:
|
|
1101
|
+
features: ["100 coding sessions", "a few new sessions / week", "100 memory recalls / week"]
|
|
1250
1102
|
};
|
|
1251
1103
|
}
|
|
1252
1104
|
function renderPlanHabitat() {
|
|
@@ -1368,11 +1220,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1368
1220
|
if (setupPlanChoice === "free") {
|
|
1369
1221
|
if (readySettings) readySettings.classList.remove("is-plan-open", "is-checkout-pending");
|
|
1370
1222
|
if (planGateDecision) planGateDecision.classList.remove("is-checkout-pending");
|
|
1371
|
-
|
|
1372
|
-
var freePlanCopy = freeLimits
|
|
1373
|
-
? quotaNumber(freeLimits.historicalConversationLimit) + " coding sessions and " + quotaNumber(freeLimits.memorySearchWeeklyLimit) + " memory recalls each week."
|
|
1374
|
-
: "Your managed import and weekly memory allowances are ready.";
|
|
1375
|
-
slot.innerHTML = '<div class="setupPlanConfirmed"><span class="setupPlanConfirmedIcon" aria-hidden="true">' + setupIcon("check") + '</span><span class="setupPlanConfirmedCopy"><strong>Original Echo</strong><span>' + esc(freePlanCopy) + '</span></span><button type="button" class="textButton" id="changeSetupPlan">Change</button></div>';
|
|
1223
|
+
slot.innerHTML = '<div class="setupPlanConfirmed"><span class="setupPlanConfirmedIcon" aria-hidden="true">' + setupIcon("check") + '</span><span class="setupPlanConfirmedCopy"><strong>Original Echo</strong><span>100 coding sessions and 100 memory recalls each week.</span></span><button type="button" class="textButton" id="changeSetupPlan">Change</button></div>';
|
|
1376
1224
|
renderBillingCommitment("");
|
|
1377
1225
|
var changeBtn = document.getElementById("changeSetupPlan");
|
|
1378
1226
|
if (changeBtn) changeBtn.onclick = function () {
|
|
@@ -1473,7 +1321,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1473
1321
|
var nextBillingStatus = await getJson("/billing-status");
|
|
1474
1322
|
if (billingEpoch !== accountStateEpoch || !connected) return;
|
|
1475
1323
|
billingStatus = nextBillingStatus;
|
|
1476
|
-
await refreshPlanQuotaCatalog();
|
|
1477
1324
|
billingLastCheckedAt = Date.now();
|
|
1478
1325
|
billingCheckMessage = "";
|
|
1479
1326
|
if (readyStage === "sessions" && setupPlanConfirmed()) {
|
|
@@ -1576,7 +1423,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1576
1423
|
'<p class="recallPromptText">' + esc(primary) + '</p>' +
|
|
1577
1424
|
'<div class="recallPromptActions" aria-label="Try your first memory recall">' +
|
|
1578
1425
|
'<button type="button" class="recallAgent recallAgentPrimary" data-recall-codex><span class="recallAgentIcon"><img src="/hud-assets/codex.svg" alt="" /></span><span>Ask in Codex</span></button>' +
|
|
1579
|
-
'<button type="button" class="recallAgent is-claude" data-recall-claude><span class="recallAgentIcon"><img src="/hud-assets/claude.svg" alt="" /></span><span>Use Claude instead</span></button>' +
|
|
1426
|
+
(canOpenClaudeDesktop ? '<button type="button" class="recallAgent is-claude" data-recall-claude><span class="recallAgentIcon"><img src="/hud-assets/claude.svg" alt="" /></span><span>Use Claude instead</span></button>' : '') +
|
|
1580
1427
|
'</div>' +
|
|
1581
1428
|
'</article>' +
|
|
1582
1429
|
'<p class="launchStatus" id="launchStatus"></p>' +
|
|
@@ -1594,7 +1441,6 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1594
1441
|
});
|
|
1595
1442
|
}
|
|
1596
1443
|
function renderEnding() {
|
|
1597
|
-
setCityMode(false);
|
|
1598
1444
|
setExtractMode(true);
|
|
1599
1445
|
// This view replaces the live extraction shell. Mark it unmounted so a failed
|
|
1600
1446
|
// pause can rebuild the progress page instead of trying to patch missing DOM.
|
|
@@ -1602,7 +1448,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1602
1448
|
setHead("Ending extraction", "Ending");
|
|
1603
1449
|
app.className = "";
|
|
1604
1450
|
app.innerHTML =
|
|
1605
|
-
setupJourney(
|
|
1451
|
+
setupJourney(3) +
|
|
1606
1452
|
'<section class="lifecycleState lifecyclePending">' +
|
|
1607
1453
|
'<h2>Stopping the remaining queued work…</h2>' +
|
|
1608
1454
|
'<p>Saved memories stay intact.</p>' +
|
|
@@ -1610,13 +1456,12 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1610
1456
|
'</section>';
|
|
1611
1457
|
}
|
|
1612
1458
|
function renderSkipped() {
|
|
1613
|
-
setCityMode(false);
|
|
1614
1459
|
setExtractMode(true);
|
|
1615
1460
|
setHead("Extraction ended for now", "Ended");
|
|
1616
1461
|
app.className = "";
|
|
1617
1462
|
var savedCount = Math.max(0, Number(lastExtractionProgress && lastExtractionProgress.extracted || 0));
|
|
1618
1463
|
app.innerHTML =
|
|
1619
|
-
setupJourney(savedCount > 0 ?
|
|
1464
|
+
setupJourney(savedCount > 0 ? 4 : 3) +
|
|
1620
1465
|
'<section class="lifecycleState lifecyclePaused">' +
|
|
1621
1466
|
'<header class="lifecycleIntro">' +
|
|
1622
1467
|
'<h2>' + (savedCount > 0 ? esc(number(savedCount)) + ' memories are ready to use.' : 'Import ended safely.') + '</h2>' +
|
|
@@ -1680,7 +1525,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1680
1525
|
statsSlow = false;
|
|
1681
1526
|
connected = false;
|
|
1682
1527
|
decisionMade = false;
|
|
1683
|
-
|
|
1528
|
+
renderLocalLogin("Signed out locally. Connect EchoMem again to extract.");
|
|
1684
1529
|
} catch (e) {
|
|
1685
1530
|
decisionMade = false;
|
|
1686
1531
|
renderLogoutIssue(e && e.message ? e.message : String(e));
|