myagentmemory 0.4.15 → 0.4.17

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.
@@ -29,12 +29,12 @@ const MISSING_ENTITLEMENT: PluginEntitlementStatusV1 = {
29
29
  state: "missing",
30
30
  features: [],
31
31
  capabilities: {},
32
- reason: "Enter an email address to activate the free daily session allowance",
32
+ reason: "Install the no-account Pro preview to activate local recall and learning",
33
33
  };
34
34
 
35
35
  interface TemporaryActivationV1 {
36
- schemaVersion: 2;
37
- email: string;
36
+ schemaVersion: 3;
37
+ installationId: string;
38
38
  activatedAt: string;
39
39
  usageCredential: string;
40
40
  dailySessionLimit: number;
@@ -54,24 +54,23 @@ function cloneEntitlement(value: PluginEntitlementStatusV1): PluginEntitlementSt
54
54
  return structuredClone(value);
55
55
  }
56
56
 
57
- function freeEntitlement(dailySessionLimit: number): PluginEntitlementStatusV1 {
57
+ function freeEntitlement(): PluginEntitlementStatusV1 {
58
58
  return {
59
59
  plan: "free",
60
60
  state: "active",
61
61
  features: ["session-intelligence", "web-console"],
62
62
  capabilities: {
63
63
  "session-index": { enabled: true },
64
- "session-worker": {
65
- enabled: true,
66
- quota: { limit: dailySessionLimit, window: "day", scope: "account" },
67
- },
68
- learning: { enabled: true },
64
+ recall: { enabled: true, quota: { limit: 10, window: "day", scope: "device" } },
65
+ "session-worker": { enabled: false },
66
+ learning: { enabled: true, quota: { limit: 1, window: "day", scope: "device" } },
69
67
  "retrieval-evaluation": { enabled: true },
70
68
  "operational-metrics": { enabled: true },
71
69
  "web-console": { enabled: true },
72
70
  "memory-explorer": { enabled: true },
73
71
  },
74
- reason: `${dailySessionLimit} free agent sessions per UTC day`,
72
+ reason:
73
+ "Free preview: 10 recalls and 1 learning scan per local day; indexing and dashboard access remain available",
75
74
  };
76
75
  }
77
76
 
@@ -94,13 +93,104 @@ function securityHeaders(contentType: string): Record<string, string> {
94
93
  };
95
94
  }
96
95
 
96
+ const ACTIVATION_PAGE_STYLES = `
97
+ :root{color-scheme:light;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#172033;background:#f4f7fb;font-synthesis:none}
98
+ *{box-sizing:border-box}
99
+ body{min-height:100vh;margin:0;padding:2rem 1.25rem;display:grid;place-items:center;background:radial-gradient(circle at 12% 0%,#e8f0ff 0,transparent 38rem),radial-gradient(circle at 100% 100%,#eef2ff 0,transparent 32rem),#f7f9fc}
100
+ main{width:min(100%,34rem)}
101
+ .skip-link{position:fixed;z-index:1;top:1rem;left:1rem;padding:.65rem .8rem;border-radius:.55rem;background:#172033;color:#fff;font-weight:700;transform:translateY(-200%)}
102
+ .skip-link:focus{transform:translateY(0)}
103
+ .brand{display:flex;align-items:center;gap:.75rem;margin:0 0 1.25rem .25rem;color:#33415c;font-size:.875rem;font-weight:700;letter-spacing:.01em}
104
+ .mark{width:2.25rem;height:2.25rem;display:grid;place-items:center;border-radius:.7rem;background:linear-gradient(145deg,#172033,#3159b8);color:#fff;box-shadow:0 .5rem 1.25rem rgba(35,65,130,.2);font-size:1.05rem;letter-spacing:-.08em}
105
+ .card{padding:clamp(1.5rem,5vw,2.5rem);border:1px solid rgba(205,216,231,.9);border-radius:1.5rem;background:rgba(255,255,255,.94);box-shadow:0 1.5rem 4rem rgba(32,51,84,.12),0 .125rem .375rem rgba(32,51,84,.06);backdrop-filter:blur(1rem)}
106
+ .eyebrow{display:inline-flex;align-items:center;gap:.45rem;margin:0 0 1rem;padding:.42rem .7rem;border:1px solid #d9e4f7;border-radius:999px;background:#f3f7ff;color:#315798;font-size:.78rem;font-weight:750;letter-spacing:.03em;text-transform:uppercase}
107
+ .eyebrow::before{content:"";width:.45rem;height:.45rem;border-radius:50%;background:#2b67d1;box-shadow:0 0 0 .22rem #dbe8ff}
108
+ h1{margin:0;color:#141d2c;font-size:clamp(2rem,7vw,2.75rem);line-height:1.06;letter-spacing:-.045em;text-wrap:balance}
109
+ .intro{margin:.9rem 0 1.75rem;color:#526176;font-size:1.05rem;line-height:1.55}
110
+ form{display:grid;gap:.75rem}
111
+ label{color:#29364a;font-size:.9rem;font-weight:700}
112
+ input{width:100%;min-height:3.25rem;padding:.8rem 3.5rem .8rem 1rem;border:1px solid #bcc8d8;border-radius:.8rem;background:#fff;color:#172033;font:inherit;box-shadow:inset 0 1px 2px rgba(24,39,65,.04)}
113
+ input::placeholder{color:#8a96a7}
114
+ input:hover{border-color:#92a2b8}
115
+ input:focus-visible{outline:0;border-color:#2b67d1;box-shadow:0 0 0 .25rem rgba(43,103,209,.15)}
116
+ button{min-height:3.35rem;margin-top:.25rem;padding:.85rem 1rem;border:1px solid #172033;border-radius:.8rem;background:linear-gradient(180deg,#24334a,#172033);color:#fff;font:inherit;font-weight:750;cursor:pointer;box-shadow:0 .55rem 1.2rem rgba(23,32,51,.18);touch-action:manipulation;transition:transform .15s}
117
+ button:hover{background:linear-gradient(180deg,#2d405c,#1d2a40);box-shadow:0 .7rem 1.4rem rgba(23,32,51,.23);transform:translateY(-1px)}
118
+ button:active{transform:translateY(0)}
119
+ button:focus-visible,summary:focus-visible{outline:.2rem solid rgba(43,103,209,.32);outline-offset:.18rem}
120
+ .terminal-note{margin:.8rem 0 0;color:#6a7789;font-size:.85rem;text-align:center}
121
+ .error{margin:0 0 1rem;padding:.8rem 1rem;border:1px solid #efb8bd;border-radius:.75rem;background:#fff3f4;color:#982631;font-size:.9rem;line-height:1.45}
122
+ details{margin-top:1.5rem;padding-top:1.25rem;border-top:1px solid #e2e8f0;color:#5b687a;font-size:.85rem;line-height:1.55}
123
+ summary{color:#46566d;font-weight:700;cursor:pointer;list-style-position:outside;touch-action:manipulation}
124
+ details p{margin:.8rem 0 0}
125
+ .never-sent{padding:.75rem .85rem;border-radius:.65rem;background:#f5f7fa;color:#536176}
126
+ .success{display:grid;place-items:center;width:3.5rem;height:3.5rem;margin-bottom:1.4rem;border-radius:1rem;background:#eaf7ef;color:#197542;font-size:1.65rem;font-weight:800;box-shadow:inset 0 0 0 1px #c9ead6}
127
+ .completion .intro{margin-bottom:0}
128
+ @media (max-width:30rem){body{padding:1rem}.brand{margin-left:.1rem}.card{border-radius:1.15rem}h1{font-size:2rem}}
129
+ @media (prefers-reduced-motion:reduce){input,button{transition:none}}
130
+ `;
131
+
97
132
  function activationPage(action: string, error?: string): string {
98
- const errorHtml = error ? `<p class="error">${error}</p>` : "";
99
- return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Activate AgentMemory</title><style>body{font:16px system-ui;max-width:34rem;margin:10vh auto;padding:0 1.5rem;color:#18212b}form{display:grid;gap:1rem}input,button{font:inherit;padding:.8rem;border-radius:.5rem;border:1px solid #aab4bf}button{background:#18212b;color:#fff;cursor:pointer}.muted{color:#586574}.error{color:#a21d24}</style></head><body><h1>Activate AgentMemory</h1><p>Enter an email address to enable the free daily agent-session allowance on this device.</p>${errorHtml}<form method="post" action="${action}"><label>Email <input type="email" name="email" autocomplete="email" maxlength="254" required autofocus></label><button type="submit">Activate and return to terminal</button></form><p class="muted">The AgentMemory CLI sends your email plus core, bundle, platform, architecture, and release-channel metadata to the private activation service. D1 stores a daily count of opaque SessionStart operations for your normalized email. The request never includes memory, session content, queries, repository paths, raw agent session identifiers, IP addresses, or user-agent strings. Activation records expire after 365 days without use.</p></body></html>`;
133
+ const errorHtml = error ? `<p class="error" id="activation-error" role="alert">${error}</p>` : "";
134
+ const describedBy = error ? "activation-error terminal-note" : "terminal-note";
135
+ const autofocus = error ? " autofocus" : "";
136
+ return `<!doctype html>
137
+ <html lang="en">
138
+ <head>
139
+ <meta charset="utf-8">
140
+ <meta name="viewport" content="width=device-width, initial-scale=1">
141
+ <meta name="theme-color" content="#f4f7fb">
142
+ <title>Activate AgentMemory</title>
143
+ <style>${ACTIVATION_PAGE_STYLES}</style>
144
+ </head>
145
+ <body>
146
+ <a class="skip-link" href="#activation">Skip to Activation</a>
147
+ <main id="activation">
148
+ <div class="brand" translate="no"><span class="mark" aria-hidden="true">AM</span><span>AgentMemory</span></div>
149
+ <section class="card" aria-labelledby="activation-title">
150
+ <p class="eyebrow">Free daily allowance</p>
151
+ <h1 id="activation-title">Activate This Device</h1>
152
+ <p class="intro">Use your email to enable AgentMemory Pro for your local agent sessions.</p>
153
+ <form method="post" action="${action}">
154
+ <label for="email">Email address</label>
155
+ ${errorHtml}
156
+ <input id="email" type="email" name="email" autocomplete="email" inputmode="email" spellcheck="false" placeholder="you@example.com…" maxlength="254" required${autofocus} aria-describedby="${describedBy}">
157
+ <button type="submit">Activate AgentMemory</button>
158
+ </form>
159
+ <p class="terminal-note" id="terminal-note">Your terminal will finish setup after activation.</p>
160
+ <details>
161
+ <summary>What’s shared during activation</summary>
162
+ <p>Your email identifies your free daily allowance. The CLI also sends core and bundle versions, platform, architecture, and release channel. The service stores a daily count of opaque session-start operations. Activation records expire after 365 days without use.</p>
163
+ <p class="never-sent"><strong>Never sent:</strong> The request never includes memory, session content, queries, repository paths, raw agent session identifiers, IP addresses, or user-agent strings.</p>
164
+ </details>
165
+ </section>
166
+ </main>
167
+ </body>
168
+ </html>`;
100
169
  }
101
170
 
102
171
  function completionPage(): string {
103
- return '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>AgentMemory activated</title></head><body><h1>Activation complete</h1><p>You can close this tab and return to the terminal.</p></body></html>';
172
+ return `<!doctype html>
173
+ <html lang="en">
174
+ <head>
175
+ <meta charset="utf-8">
176
+ <meta name="viewport" content="width=device-width, initial-scale=1">
177
+ <meta name="theme-color" content="#f4f7fb">
178
+ <title>AgentMemory activated</title>
179
+ <style>${ACTIVATION_PAGE_STYLES}</style>
180
+ </head>
181
+ <body>
182
+ <a class="skip-link" href="#completion">Skip to Activation Status</a>
183
+ <main id="completion">
184
+ <div class="brand" translate="no"><span class="mark" aria-hidden="true">AM</span><span>AgentMemory</span></div>
185
+ <section class="card completion" aria-labelledby="completion-title">
186
+ <div class="success" aria-hidden="true">✓</div>
187
+ <p class="eyebrow">Device activated</p>
188
+ <h1 id="completion-title">You’re All Set</h1>
189
+ <p class="intro">Return to your terminal to finish installing AgentMemory Pro. You can close this tab.</p>
190
+ </section>
191
+ </main>
192
+ </body>
193
+ </html>`;
104
194
  }
105
195
 
106
196
  function send(response: ServerResponse, status: number, body: string, contentType = "text/html; charset=utf-8"): void {
@@ -297,12 +387,12 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
297
387
  this.artifactOrigin = options.artifactOrigin ?? ARTIFACT_ORIGIN;
298
388
  this.fetchImplementation = options.fetch ?? globalThis.fetch;
299
389
  this.openUrl = options.openUrl ?? openLoopbackUrl;
300
- this.activate = options.activate ?? (() => collectTemporaryActivation(this.openUrl));
390
+ this.activate = options.activate ?? (async () => `am_install_${randomBytes(24).toString("base64url")}`);
301
391
  }
302
392
 
303
393
  async getLocalEntitlement(): Promise<PluginEntitlementStatusV1> {
304
394
  const activation = this.readActivation();
305
- return activation ? freeEntitlement(activation.dailySessionLimit) : cloneEntitlement(MISSING_ENTITLEMENT);
395
+ return activation ? freeEntitlement() : cloneEntitlement(MISSING_ENTITLEMENT);
306
396
  }
307
397
 
308
398
  async resolveAccess(request: {
@@ -312,33 +402,19 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
312
402
  allowAuthentication: boolean;
313
403
  }): Promise<PluginAccessDecisionV1> {
314
404
  const activation = this.readActivation();
315
- let email = activation?.email;
316
- if (!email) {
317
- if (!request.allowAuthentication)
318
- return {
319
- kind: "auth_required",
320
- entitlement: cloneEntitlement(MISSING_ENTITLEMENT),
321
- nextAction: {
322
- kind: "authenticate",
323
- url: "https://jayzeng.github.io/agentmemory/",
324
- message: "Run plugin install in an interactive terminal to enter an email address",
325
- },
326
- };
327
- email = await this.activate();
328
- }
405
+ const installationId = activation?.installationId ?? (await this.activate());
329
406
  const response = await this.request(`${this.apiOrigin}/v1/plugin/access`, {
330
407
  method: "POST",
331
408
  headers: { "Content-Type": "application/json" },
332
409
  body: JSON.stringify({
333
- schemaVersion: 1,
334
- email,
410
+ schemaVersion: 2,
411
+ installationId,
335
412
  bundleId: request.bundleId,
336
413
  installedVersion: request.installedVersion ?? null,
337
414
  coreVersion: this.coreVersion,
338
415
  channel: request.channel,
339
416
  platform: process.platform,
340
417
  architecture: process.arch,
341
- consentVersion: "activation-v2",
342
418
  }),
343
419
  });
344
420
  const value = (await readJson(response)) as {
@@ -354,16 +430,22 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
354
430
  "service_response_invalid",
355
431
  "The access response omitted its usage credential",
356
432
  );
357
- const freeQuota = value.entitlement.capabilities["session-worker"]?.quota;
433
+ const recallQuota = value.entitlement.capabilities.recall?.quota;
434
+ const learningQuota = value.entitlement.capabilities.learning?.quota;
358
435
  if (
359
436
  value.entitlement.plan !== "free" ||
360
437
  value.entitlement.state !== "active" ||
361
- !freeQuota ||
362
- freeQuota.scope !== "account" ||
363
- freeQuota.window !== "day"
438
+ !recallQuota ||
439
+ recallQuota.scope !== "device" ||
440
+ recallQuota.window !== "day" ||
441
+ !learningQuota ||
442
+ learningQuota.scope !== "device" ||
443
+ learningQuota.window !== "day" ||
444
+ value.entitlement.capabilities["session-index"]?.enabled !== true ||
445
+ value.entitlement.capabilities["web-console"]?.enabled !== true
364
446
  )
365
- throw new PluginBootstrapFailure("service_response_invalid", "The free session policy is invalid");
366
- this.writeActivation(email, value.usageCredential, freeQuota.limit);
447
+ throw new PluginBootstrapFailure("service_response_invalid", "The free preview policy is invalid");
448
+ this.writeActivation(installationId, value.usageCredential, 1);
367
449
  return { kind: "granted", entitlement: value.entitlement, artifactGrant: value.artifactGrant };
368
450
  }
369
451
 
@@ -432,8 +514,9 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
432
514
  return null;
433
515
  const value = JSON.parse(fs.readFileSync(activationPath, "utf-8")) as TemporaryActivationV1;
434
516
  if (
435
- value.schemaVersion !== 2 ||
436
- !isEmail(value.email) ||
517
+ value.schemaVersion !== 3 ||
518
+ typeof value.installationId !== "string" ||
519
+ !/^am_install_[A-Za-z0-9_-]{32}$/.test(value.installationId) ||
437
520
  !Number.isFinite(Date.parse(value.activatedAt)) ||
438
521
  !ACTIVATION_CREDENTIAL.test(value.usageCredential) ||
439
522
  !Number.isSafeInteger(value.dailySessionLimit) ||
@@ -447,8 +530,9 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
447
530
  }
448
531
  }
449
532
 
450
- private writeActivation(email: string, usageCredential: string, dailySessionLimit: number): void {
451
- if (!isEmail(email)) throw new PluginBootstrapFailure("email_invalid", "Enter a valid email address");
533
+ private writeActivation(installationId: string, usageCredential: string, dailySessionLimit: number): void {
534
+ if (!/^am_install_[A-Za-z0-9_-]{32}$/.test(installationId))
535
+ throw new PluginBootstrapFailure("activation_failed", "The installation identifier is invalid");
452
536
  if (!ACTIVATION_CREDENTIAL.test(usageCredential))
453
537
  throw new PluginBootstrapFailure("activation_failed", "The activation credential is invalid");
454
538
  if (!Number.isSafeInteger(dailySessionLimit) || dailySessionLimit <= 0 || dailySessionLimit > 10_000)
@@ -467,7 +551,13 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
467
551
  fs.writeFileSync(
468
552
  temporary,
469
553
  `${JSON.stringify(
470
- { schemaVersion: 2, email, activatedAt: new Date().toISOString(), usageCredential, dailySessionLimit },
554
+ {
555
+ schemaVersion: 3,
556
+ installationId,
557
+ activatedAt: new Date().toISOString(),
558
+ usageCredential,
559
+ dailySessionLimit,
560
+ },
471
561
  null,
472
562
  2,
473
563
  )}\n`,