@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.
Files changed (48) hide show
  1. package/README.md +25 -28
  2. package/dist/config-files.js +63 -0
  3. package/dist/context-analysis/claude-native-canonical.js +2 -2
  4. package/dist/context-analysis/vendored-canonical.js +2 -2
  5. package/dist/context-analysis/workspace-report.js +3 -3
  6. package/dist/hud/hooks.js +43 -31
  7. package/dist/index.js +15 -79
  8. package/dist/local-jsonl.js +87 -0
  9. package/dist/migrate.js +6 -4
  10. package/dist/onboarding-stats.js +16 -0
  11. package/dist/save-checkpoint-hook.js +1 -1
  12. package/dist/setup-page/client-core.js +18 -372
  13. package/dist/setup-page/client-extraction.js +33 -188
  14. package/dist/setup-page/client-lifecycle.js +31 -101
  15. package/dist/setup-page/client.js +0 -2
  16. package/dist/setup-page/styles-extraction.js +1 -31
  17. package/dist/setup-page/styles-foundation.js +0 -89
  18. package/dist/setup-page/styles-mvp.js +10 -155
  19. package/dist/setup-page/styles-website-alignment.js +0 -204
  20. package/dist/setup-page/styles.js +0 -4
  21. package/dist/setup-page.js +4 -4
  22. package/dist/setup-preview.js +4 -212
  23. package/dist/setup.js +314 -667
  24. package/dist/v1-contract.js +0 -8
  25. package/package.json +9 -7
  26. package/dist/city/README.md +0 -9
  27. package/dist/city/echo-ai-city-only.html +0 -2232
  28. package/dist/city/echo-extraction-plate.html +0 -330
  29. package/dist/city/echo-face-cutout.png +0 -0
  30. package/dist/city/personality_stickers/bossy.png +0 -0
  31. package/dist/city/personality_stickers/ghosty.png +0 -0
  32. package/dist/city/personality_stickers/loopy.png +0 -0
  33. package/dist/city/personality_stickers/lusty.png +0 -0
  34. package/dist/city/personality_stickers/maxxy.png +0 -0
  35. package/dist/city/personality_stickers/tabby.png +0 -0
  36. package/dist/city/vendor/OrbitControls.js +0 -1417
  37. package/dist/city/vendor/RoundedBoxGeometry.js +0 -155
  38. package/dist/city/vendor/echo_general-file-21.riv +0 -0
  39. package/dist/city/vendor/rive.js +0 -8139
  40. package/dist/city/vendor/rive.wasm +0 -0
  41. package/dist/city/vendor/three.module.min.js +0 -6
  42. package/dist/forensics.js +0 -1531
  43. package/dist/report.js +0 -721
  44. package/dist/setup-page/client-report-audit.js +0 -819
  45. package/dist/setup-page/client-report-city.js +0 -356
  46. package/dist/setup-page/client-report.js +0 -6
  47. package/dist/setup-page/styles-city-report.js +0 -880
  48. 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="reportTechnical"><summary>Technical details</summary><code>' + esc(error) + '</code></details>' : '') +
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) renderBridgeIssue(lastError);
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(3) +
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 Mac, and you can retry the history import later."
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 Mac."
264
- : currentPlanLabel + " has no coding-session imports remaining. These sessions stay on this Mac.")
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 ONCE so the WebGL plate iframe is never re-created across /stats polls.
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(4) +
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">&middot;</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 Mac</span>'
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(4) +
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 ONCE so the WebGL plate iframe survives every poll (it self-polls /progress);
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(4) + '</div>' +
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 ? 5 : 4);
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(4) +
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 : null;
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(limit === null ? "Plan import allowance" : quotaNumber(limit)) + '</b>' + (limit === null ? "" : " coding-session imports") + '</span>' +
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: 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: 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: 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
- var freeLimits = quotaLimitsForPlan("free");
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(4) +
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 ? 5 : 4) +
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
- renderForensicReport("Signed out locally. Connect EchoMem again to extract.");
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));