myagentmemory 0.5.2 → 0.5.4

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.
@@ -139,8 +139,8 @@ function activationPage(action, error) {
139
139
  <p class="terminal-note" id="terminal-note">Your terminal will finish setup after activation.</p>
140
140
  <details>
141
141
  <summary>What’s shared during activation</summary>
142
- <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>
143
- <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>
142
+ <p>Your email identifies your free daily allowance. The CLI also sends core and bundle versions, platform, architecture, and release channel. The service stores bounded activation metadata. Activation records expire after 365 days without use.</p>
143
+ <p class="never-sent"><strong>Not included in AgentMemory's application payload:</strong> memory, session content, queries, repository paths, raw agent session identifiers, IP addresses, or user-agent strings.</p>
144
144
  </details>
145
145
  </section>
146
146
  </main>
@@ -394,38 +394,21 @@ export class AgentMemoryServiceBackend {
394
394
  validatePluginEntitlementStatusV1(value.entitlement);
395
395
  if (typeof value.artifactGrant !== "string" || !value.artifactGrant)
396
396
  throw new PluginBootstrapFailure("service_response_invalid", "The access response omitted its artifact grant");
397
- if (typeof value.usageCredential !== "string" || !ACTIVATION_CREDENTIAL.test(value.usageCredential))
398
- throw new PluginBootstrapFailure("service_response_invalid", "The access response omitted its usage credential");
399
397
  const recallQuota = value.entitlement.capabilities.recall?.quota;
400
398
  const learningQuota = value.entitlement.capabilities.learning?.quota;
401
399
  if (value.entitlement.plan !== "free" ||
402
400
  value.entitlement.state !== "active" ||
403
401
  value.entitlement.capabilities.recall?.enabled !== true ||
404
402
  !recallQuota ||
405
- recallQuota.limit !== 20 ||
406
- recallQuota.scope !== "device" ||
407
- recallQuota.window !== "day" ||
408
403
  value.entitlement.capabilities.learning?.enabled !== true ||
409
404
  !learningQuota ||
410
- learningQuota.limit !== 5 ||
411
- learningQuota.scope !== "device" ||
412
- learningQuota.window !== "day" ||
413
405
  value.entitlement.capabilities["session-index"]?.enabled !== true ||
414
406
  value.entitlement.capabilities["session-worker"]?.enabled !== false ||
415
407
  value.entitlement.capabilities["web-console"]?.enabled !== true)
416
408
  throw new PluginBootstrapFailure("service_response_invalid", "The free preview policy is invalid");
417
- this.writeActivation(installationId, value.usageCredential, 1);
409
+ this.writeActivation(installationId);
418
410
  return { kind: "granted", entitlement: value.entitlement, artifactGrant: value.artifactGrant };
419
411
  }
420
- async reserveSession(operationId) {
421
- return this.sessionUsage("reserve", operationId);
422
- }
423
- async commitSession(operationId) {
424
- return this.sessionUsage("commit", operationId);
425
- }
426
- async releaseSession(operationId) {
427
- return this.sessionUsage("release", operationId);
428
- }
429
412
  async listReleases(request) {
430
413
  const response = await this.request(`${this.apiOrigin}/v1/plugin/releases`, {
431
414
  headers: { Authorization: `Bearer ${request.artifactGrant}` },
@@ -480,28 +463,28 @@ export class AgentMemoryServiceBackend {
480
463
  if (!stat.isFile() || stat.isSymbolicLink() || (process.platform !== "win32" && (stat.mode & 0o077) !== 0))
481
464
  return null;
482
465
  const value = JSON.parse(fs.readFileSync(activationPath, "utf-8"));
483
- if (value.schemaVersion !== 3 ||
484
- typeof value.installationId !== "string" ||
466
+ if (typeof value.installationId !== "string" ||
485
467
  !/^am_install_[A-Za-z0-9_-]{32}$/.test(value.installationId) ||
486
468
  !Number.isFinite(Date.parse(value.activatedAt)) ||
487
- !ACTIVATION_CREDENTIAL.test(value.usageCredential) ||
488
- !Number.isSafeInteger(value.dailySessionLimit) ||
489
- value.dailySessionLimit <= 0 ||
490
- value.dailySessionLimit > 10_000)
469
+ (value.schemaVersion !== 4 &&
470
+ (value.schemaVersion !== 3 ||
471
+ typeof value.usageCredential !== "string" ||
472
+ !ACTIVATION_CREDENTIAL.test(value.usageCredential) ||
473
+ !Number.isSafeInteger(value.dailySessionLimit))))
491
474
  return null;
492
- return value;
475
+ return {
476
+ schemaVersion: 4,
477
+ installationId: value.installationId,
478
+ activatedAt: value.activatedAt,
479
+ };
493
480
  }
494
481
  catch {
495
482
  return null;
496
483
  }
497
484
  }
498
- writeActivation(installationId, usageCredential, dailySessionLimit) {
485
+ writeActivation(installationId) {
499
486
  if (!/^am_install_[A-Za-z0-9_-]{32}$/.test(installationId))
500
487
  throw new PluginBootstrapFailure("activation_failed", "The installation identifier is invalid");
501
- if (!ACTIVATION_CREDENTIAL.test(usageCredential))
502
- throw new PluginBootstrapFailure("activation_failed", "The activation credential is invalid");
503
- if (!Number.isSafeInteger(dailySessionLimit) || dailySessionLimit <= 0 || dailySessionLimit > 10_000)
504
- throw new PluginBootstrapFailure("activation_failed", "The free session allowance is invalid");
505
488
  const target = this.activationPath();
506
489
  fs.mkdirSync(this.root, { recursive: true, mode: 0o700 });
507
490
  const rootStat = fs.lstatSync(this.root);
@@ -515,43 +498,12 @@ export class AgentMemoryServiceBackend {
515
498
  throw new PluginBootstrapFailure("activation_path_invalid", "The plugin activation directory is unsafe");
516
499
  const temporary = `${target}.tmp-${process.pid}-${randomUUID()}`;
517
500
  fs.writeFileSync(temporary, `${JSON.stringify({
518
- schemaVersion: 3,
501
+ schemaVersion: 4,
519
502
  installationId,
520
503
  activatedAt: new Date().toISOString(),
521
- usageCredential,
522
- dailySessionLimit,
523
504
  }, null, 2)}\n`, { mode: 0o600, flag: "wx" });
524
505
  fs.renameSync(temporary, target);
525
506
  }
526
- async sessionUsage(action, operationId) {
527
- const activation = this.readActivation();
528
- if (!activation)
529
- throw new PluginBootstrapFailure("auth_required", "Run plugin install to activate AgentMemory");
530
- const response = await this.request(`${this.apiOrigin}/v1/plugin/sessions/${action}`, {
531
- method: "POST",
532
- headers: {
533
- Authorization: `Bearer ${activation.usageCredential}`,
534
- "Content-Type": "application/json",
535
- },
536
- body: JSON.stringify({ schemaVersion: 1, operationId }),
537
- });
538
- const value = (await readJson(response));
539
- const decision = value.decision;
540
- if (!decision ||
541
- typeof decision.allowed !== "boolean" ||
542
- !["reserved", "committed", "released", "exhausted", "missing"].includes(String(decision.state)) ||
543
- !Number.isSafeInteger(decision.limit) ||
544
- Number(decision.limit) <= 0 ||
545
- !Number.isSafeInteger(decision.used) ||
546
- Number(decision.used) < 0 ||
547
- !Number.isSafeInteger(decision.remaining) ||
548
- Number(decision.remaining) < 0 ||
549
- typeof decision.resetAt !== "string" ||
550
- !Number.isFinite(Date.parse(decision.resetAt)) ||
551
- typeof decision.idempotent !== "boolean")
552
- throw new PluginBootstrapFailure("service_response_invalid", "The session usage response is invalid");
553
- return decision;
554
- }
555
507
  async request(url, init = {}) {
556
508
  let response;
557
509
  try {
package/dist/upgrade.d.ts CHANGED
@@ -1,15 +1,21 @@
1
1
  /**
2
2
  * Upgrade orchestration for the `agent-memory` CLI and its official Pro plugin bundle.
3
3
  *
4
- * Two consumers:
4
+ * Three consumers:
5
5
  * 1. `agent-memory upgrade` — explicit user command; checks and (optionally) installs.
6
- * 2. `agent-memory hook session-start` — passive notice from a 24h-cached record.
6
+ * 2. `agent-memory upgrade --background` — detached, non-interactive; checks, then
7
+ * installs any target whose `readUpgradePolicy()` value is `"auto"` (the default).
8
+ * Spawned by `refreshUpgradeCacheBackground()` from `hook session-start`.
9
+ * 3. `agent-memory hook session-start` — passive notice from a 24h-cached record,
10
+ * including the outcome of the last `--background` auto-install attempt.
7
11
  *
8
12
  * Network calls always have a hard timeout and always fail closed (upgrade is a
9
- * quality-of-life feature; a flaky registry must never break the CLI).
13
+ * quality-of-life feature; a flaky registry must never break the CLI). Same fail-closed
14
+ * contract applies to auto-install: a failed background install is recorded, never
15
+ * retried before the next cache refresh, and never thrown.
10
16
  */
11
17
  import { type SpawnOptions } from "node:child_process";
12
- export type InstallManager = "bun" | "npm" | "pnpm" | "yarn" | "unknown";
18
+ export type InstallManager = "bun" | "homebrew" | "npm" | "pnpm" | "yarn" | "unknown";
13
19
  export interface InstallMethod {
14
20
  manager: InstallManager;
15
21
  global: boolean;
@@ -24,6 +30,17 @@ export interface UpgradeCache {
24
30
  cliLatest: string | null;
25
31
  pluginCurrent: string | null;
26
32
  pluginLatest: string | null;
33
+ /** Outcome of the most recent `--background` auto-upgrade attempt, if any. */
34
+ cliAuto?: AutoUpgradeOutcome;
35
+ pluginAuto?: AutoUpgradeOutcome;
36
+ }
37
+ export interface AutoUpgradeOutcome {
38
+ at: string;
39
+ ok: boolean;
40
+ /** Version installed (ok) or the previous/current version (failure). */
41
+ version: string | null;
42
+ /** Failure reason; absent when ok. */
43
+ error?: string;
27
44
  }
28
45
  export interface UpgradeStatus {
29
46
  cli: {
@@ -42,6 +59,32 @@ export interface UpgradeStatus {
42
59
  export declare function readUpgradeCache(): UpgradeCache | null;
43
60
  export declare function writeUpgradeCache(record: UpgradeCache): void;
44
61
  export declare function isCacheFresh(record: UpgradeCache | null, now?: number): boolean;
62
+ export type UpgradePolicyValue = "off" | "notify" | "auto";
63
+ export interface UpgradePolicy {
64
+ cli: UpgradePolicyValue;
65
+ plugin: UpgradePolicyValue;
66
+ }
67
+ /**
68
+ * Resolve the persisted auto-upgrade policy.
69
+ * Precedence per target: `AGENT_MEMORY_AUTO_UPGRADE_{CLI,PLUGIN}` env var →
70
+ * `<memoryDir>/state/upgrade-policy.json` → default `"auto"`.
71
+ *
72
+ * `existed` tells callers whether the policy file was already on disk —
73
+ * used to fire a one-time "auto-upgrade is on" notice on first read.
74
+ */
75
+ export declare function readUpgradePolicy(): UpgradePolicy & {
76
+ existed: boolean;
77
+ };
78
+ /** Atomically persist the auto-upgrade policy. Merges with whatever is already on disk. */
79
+ export declare function writeUpgradePolicy(patch: Partial<UpgradePolicy>): UpgradePolicy;
80
+ export declare function resolveSelfLaunch(input: {
81
+ execPath: string;
82
+ scriptCandidate: string | undefined;
83
+ fileExists: (candidate: string) => boolean;
84
+ }): {
85
+ command: string;
86
+ args: string[];
87
+ };
45
88
  /**
46
89
  * Best-effort detection of how `myagentmemory` was installed. Path signatures
47
90
  * are heuristic but cover the common managers. On no match we fall back to
@@ -58,8 +101,10 @@ export interface InstallResult {
58
101
  export declare function runInstaller(method: InstallMethod, opts?: SpawnOptions): InstallResult;
59
102
  /**
60
103
  * Fire-and-forget: spawn a detached child that runs `agent-memory upgrade
61
- * --check --refresh --quiet` so the next session-start has a fresh cache.
62
- * Never awaits, never throws.
104
+ * --background --refresh --quiet` so the next session-start has a fresh
105
+ * cache. Unlike a plain check, `--background` also installs any target whose
106
+ * policy is `"auto"` (see `readUpgradePolicy`) — this is the one place
107
+ * auto-upgrade actually happens. Never awaits, never throws.
63
108
  */
64
109
  export declare function refreshUpgradeCacheBackground(): void;
65
110
  export interface CheckOptions {
@@ -77,4 +122,12 @@ export interface CheckOptions {
77
122
  pluginUpgradeAvailable?: boolean;
78
123
  }
79
124
  export declare function checkForUpgrades(opts: CheckOptions): Promise<UpgradeStatus>;
80
- export declare function formatUpgradeNotice(status: UpgradeStatus): string | null;
125
+ /**
126
+ * `cache` (when passed) lets this distinguish a plain "notify" signal from the
127
+ * outcome of the last `--background` auto-install attempt for that target:
128
+ * - succeeded, but this process is running older code than what's on disk
129
+ * (e.g. a long-running `serve --mcp`) → "auto-upgraded, restart to use it"
130
+ * - failed → surface the error and point at the manual command
131
+ * - succeeded and already caught up (this process's own version matches) → silent
132
+ */
133
+ export declare function formatUpgradeNotice(status: UpgradeStatus, cache?: UpgradeCache | null): string | null;
package/dist/upgrade.js CHANGED
@@ -1,12 +1,18 @@
1
1
  /**
2
2
  * Upgrade orchestration for the `agent-memory` CLI and its official Pro plugin bundle.
3
3
  *
4
- * Two consumers:
4
+ * Three consumers:
5
5
  * 1. `agent-memory upgrade` — explicit user command; checks and (optionally) installs.
6
- * 2. `agent-memory hook session-start` — passive notice from a 24h-cached record.
6
+ * 2. `agent-memory upgrade --background` — detached, non-interactive; checks, then
7
+ * installs any target whose `readUpgradePolicy()` value is `"auto"` (the default).
8
+ * Spawned by `refreshUpgradeCacheBackground()` from `hook session-start`.
9
+ * 3. `agent-memory hook session-start` — passive notice from a 24h-cached record,
10
+ * including the outcome of the last `--background` auto-install attempt.
7
11
  *
8
12
  * Network calls always have a hard timeout and always fail closed (upgrade is a
9
- * quality-of-life feature; a flaky registry must never break the CLI).
13
+ * quality-of-life feature; a flaky registry must never break the CLI). Same fail-closed
14
+ * contract applies to auto-install: a failed background install is recorded, never
15
+ * retried before the next cache refresh, and never thrown.
10
16
  */
11
17
  import { spawn, spawnSync } from "node:child_process";
12
18
  import * as fs from "node:fs";
@@ -50,6 +56,15 @@ async function fetchLatestFromNpm(fetchImpl = globalThis.fetch) {
50
56
  function upgradeCachePath() {
51
57
  return path.join(getMemoryDir(), "state", "upgrade-check.json");
52
58
  }
59
+ function isValidAutoOutcome(value) {
60
+ if (typeof value !== "object" || value === null)
61
+ return false;
62
+ const candidate = value;
63
+ return (typeof candidate.at === "string" &&
64
+ typeof candidate.ok === "boolean" &&
65
+ (candidate.version === null || typeof candidate.version === "string") &&
66
+ (candidate.error === undefined || typeof candidate.error === "string"));
67
+ }
53
68
  export function readUpgradeCache() {
54
69
  try {
55
70
  const raw = fs.readFileSync(upgradeCachePath(), "utf-8");
@@ -69,6 +84,8 @@ export function readUpgradeCache() {
69
84
  cliLatest: parsed.cliLatest ?? null,
70
85
  pluginCurrent: parsed.pluginCurrent ?? null,
71
86
  pluginLatest: parsed.pluginLatest ?? null,
87
+ cliAuto: isValidAutoOutcome(parsed.cliAuto) ? parsed.cliAuto : undefined,
88
+ pluginAuto: isValidAutoOutcome(parsed.pluginAuto) ? parsed.pluginAuto : undefined,
72
89
  };
73
90
  }
74
91
  catch {
@@ -93,15 +110,91 @@ export function isCacheFresh(record, now = Date.now()) {
93
110
  return false;
94
111
  return now - checked < CACHE_TTL_MS;
95
112
  }
113
+ const UPGRADE_POLICY_FILENAME = "upgrade-policy.json";
114
+ const UPGRADE_POLICY_DEFAULT = { cli: "auto", plugin: "auto" };
115
+ function upgradePolicyPath() {
116
+ return path.join(getMemoryDir(), "state", UPGRADE_POLICY_FILENAME);
117
+ }
118
+ function isPolicyValue(value) {
119
+ return value === "off" || value === "notify" || value === "auto";
120
+ }
121
+ /**
122
+ * Resolve the persisted auto-upgrade policy.
123
+ * Precedence per target: `AGENT_MEMORY_AUTO_UPGRADE_{CLI,PLUGIN}` env var →
124
+ * `<memoryDir>/state/upgrade-policy.json` → default `"auto"`.
125
+ *
126
+ * `existed` tells callers whether the policy file was already on disk —
127
+ * used to fire a one-time "auto-upgrade is on" notice on first read.
128
+ */
129
+ export function readUpgradePolicy() {
130
+ let stored = {};
131
+ let existed = false;
132
+ try {
133
+ const raw = fs.readFileSync(upgradePolicyPath(), "utf-8");
134
+ const parsed = JSON.parse(raw);
135
+ if (isPolicyValue(parsed.cli) || isPolicyValue(parsed.plugin)) {
136
+ stored = parsed;
137
+ existed = true;
138
+ }
139
+ }
140
+ catch { }
141
+ const envCli = process.env.AGENT_MEMORY_AUTO_UPGRADE_CLI;
142
+ const envPlugin = process.env.AGENT_MEMORY_AUTO_UPGRADE_PLUGIN;
143
+ return {
144
+ cli: isPolicyValue(envCli) ? envCli : isPolicyValue(stored.cli) ? stored.cli : UPGRADE_POLICY_DEFAULT.cli,
145
+ plugin: isPolicyValue(envPlugin)
146
+ ? envPlugin
147
+ : isPolicyValue(stored.plugin)
148
+ ? stored.plugin
149
+ : UPGRADE_POLICY_DEFAULT.plugin,
150
+ existed,
151
+ };
152
+ }
153
+ /** Atomically persist the auto-upgrade policy. Merges with whatever is already on disk. */
154
+ export function writeUpgradePolicy(patch) {
155
+ const current = readUpgradePolicy();
156
+ const next = {
157
+ cli: patch.cli ?? current.cli,
158
+ plugin: patch.plugin ?? current.plugin,
159
+ };
160
+ const target = upgradePolicyPath();
161
+ fs.mkdirSync(path.dirname(target), { recursive: true });
162
+ const temporary = `${target}.${process.pid}.tmp`;
163
+ fs.writeFileSync(temporary, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
164
+ fs.renameSync(temporary, target);
165
+ return next;
166
+ }
96
167
  // ---------------------------------------------------------------------------
97
168
  // Install-method detection
98
169
  // ---------------------------------------------------------------------------
170
+ export function resolveSelfLaunch(input) {
171
+ const scriptCandidate = input.scriptCandidate;
172
+ const isRealScript = typeof scriptCandidate === "string" &&
173
+ scriptCandidate.length > 0 &&
174
+ !scriptCandidate.startsWith("/$bunfs/") &&
175
+ input.fileExists(scriptCandidate);
176
+ return isRealScript ? { command: input.execPath, args: [scriptCandidate] } : { command: input.execPath, args: [] };
177
+ }
178
+ function currentSelfLaunch() {
179
+ return resolveSelfLaunch({
180
+ execPath: process.execPath,
181
+ scriptCandidate: process.argv[1],
182
+ fileExists: fs.existsSync,
183
+ });
184
+ }
99
185
  function selfInstallPath() {
186
+ const launch = currentSelfLaunch();
187
+ const candidate = launch.args[0] ?? launch.command;
100
188
  try {
101
- return url.fileURLToPath(import.meta.url);
189
+ return fs.realpathSync(candidate);
102
190
  }
103
191
  catch {
104
- return process.argv[1] ?? "";
192
+ try {
193
+ return url.fileURLToPath(import.meta.url);
194
+ }
195
+ catch {
196
+ return candidate;
197
+ }
105
198
  }
106
199
  }
107
200
  /**
@@ -113,6 +206,16 @@ export function detectInstallMethod(location = selfInstallPath()) {
113
206
  const normalized = location.replace(/\\/g, "/");
114
207
  const home = os.homedir().replace(/\\/g, "/");
115
208
  const pkg = `${NPM_PACKAGE_NAME}@latest`;
209
+ // Compiled CLI installed by the official Homebrew formula. Resolve symlinks
210
+ // before detection so /opt/homebrew/bin/agent-memory reaches its Cellar path.
211
+ if (normalized.includes("/Cellar/agent-memory/")) {
212
+ return {
213
+ manager: "homebrew",
214
+ global: true,
215
+ origin: location,
216
+ command: ["brew", "upgrade", "jayzeng/agentmemory/agent-memory"],
217
+ };
218
+ }
116
219
  // bun global install
117
220
  if (normalized.includes("/.bun/install/global/") || normalized.includes("/bun/install/global/")) {
118
221
  return { manager: "bun", global: true, origin: location, command: ["bun", "add", "-g", pkg] };
@@ -163,16 +266,17 @@ export function runInstaller(method, opts = {}) {
163
266
  // ---------------------------------------------------------------------------
164
267
  /**
165
268
  * Fire-and-forget: spawn a detached child that runs `agent-memory upgrade
166
- * --check --refresh --quiet` so the next session-start has a fresh cache.
167
- * Never awaits, never throws.
269
+ * --background --refresh --quiet` so the next session-start has a fresh
270
+ * cache. Unlike a plain check, `--background` also installs any target whose
271
+ * policy is `"auto"` (see `readUpgradePolicy`) — this is the one place
272
+ * auto-upgrade actually happens. Never awaits, never throws.
168
273
  */
169
274
  export function refreshUpgradeCacheBackground() {
170
275
  try {
171
- const binary = process.argv[0];
172
- const script = process.argv[1];
173
- if (!binary || !script)
276
+ const launch = currentSelfLaunch();
277
+ if (!launch.command)
174
278
  return;
175
- const child = spawn(binary, [script, "upgrade", "--check", "--refresh", "--quiet", "--json"], {
279
+ const child = spawn(launch.command, [...launch.args, "upgrade", "--background", "--refresh", "--quiet", "--json"], {
176
280
  detached: true,
177
281
  stdio: "ignore",
178
282
  env: { ...process.env, AGENT_MEMORY_UPGRADE_BACKGROUND: "1" },
@@ -231,13 +335,40 @@ export async function checkForUpgrades(opts) {
231
335
  fromCache,
232
336
  };
233
337
  }
234
- export function formatUpgradeNotice(status) {
338
+ /**
339
+ * `cache` (when passed) lets this distinguish a plain "notify" signal from the
340
+ * outcome of the last `--background` auto-install attempt for that target:
341
+ * - succeeded, but this process is running older code than what's on disk
342
+ * (e.g. a long-running `serve --mcp`) → "auto-upgraded, restart to use it"
343
+ * - failed → surface the error and point at the manual command
344
+ * - succeeded and already caught up (this process's own version matches) → silent
345
+ */
346
+ export function formatUpgradeNotice(status, cache) {
235
347
  const parts = [];
236
- if (status.cli.upgradeAvailable)
348
+ let needsManualRun = false;
349
+ if (cache?.cliAuto && !cache.cliAuto.ok) {
350
+ parts.push(`CLI auto-upgrade failed (${cache.cliAuto.error ?? "unknown error"})`);
351
+ needsManualRun = true;
352
+ }
353
+ else if (cache?.cliAuto?.ok && cache.cliAuto.version && cache.cliAuto.version !== status.cli.current) {
354
+ parts.push(`CLI auto-upgraded → ${cache.cliAuto.version} (restart any long-running agent-memory process to use it)`);
355
+ }
356
+ else if (status.cli.upgradeAvailable) {
237
357
  parts.push(`CLI ${status.cli.current} → ${status.cli.latest ?? "new"}`);
238
- if (status.plugin.upgradeAvailable)
358
+ needsManualRun = true;
359
+ }
360
+ if (cache?.pluginAuto && !cache.pluginAuto.ok) {
361
+ parts.push(`Pro auto-upgrade failed (${cache.pluginAuto.error ?? "unknown error"})`);
362
+ needsManualRun = true;
363
+ }
364
+ else if (cache?.pluginAuto?.ok && cache.pluginAuto.version && cache.pluginAuto.version !== status.plugin.current) {
365
+ parts.push(`Pro auto-upgraded → ${cache.pluginAuto.version}`);
366
+ }
367
+ else if (status.plugin.upgradeAvailable) {
239
368
  parts.push(`Pro ${status.plugin.current ?? "?"} → ${status.plugin.latest ?? "new"}`);
369
+ needsManualRun = true;
370
+ }
240
371
  if (!parts.length)
241
372
  return null;
242
- return `agent-memory: upgrade available (${parts.join(", ")}). Run: agent-memory upgrade`;
373
+ return `agent-memory: ${parts.join("; ")}${needsManualRun ? ". Run: agent-memory upgrade" : ""}`;
243
374
  }
@@ -89,7 +89,7 @@ The `pro` namespace is the user-facing surface. The `plugin` namespace remains s
89
89
  - `status` is read-only. It reports the installed bundle, selected channel, compatibility, entitlement state, and update availability.
90
90
  - `install` authenticates when necessary, then installs, upgrades, or reports current state.
91
91
  - `update` requires an existing installation and never starts a new purchase implicitly.
92
- - `uninstall` removes executable plugin material and the active receipt. It preserves core memory, plugin state, and the permission-restricted activation credential.
92
+ - `uninstall` removes executable plugin material and the active receipt. It preserves core memory, plugin state, and the permission-restricted local activation record.
93
93
  - `manage` remains unavailable until authenticated account and billing management exists.
94
94
 
95
95
  Installed plugins contribute top-level commands including `recall` and `learn`; `dashboard` is a product-facing alias for the lower-level `web` command. Bootstrap command names are reserved by the core and cannot be replaced by a plugin.
@@ -172,7 +172,6 @@ An Enterprise administrator may pre-provision an organization entitlement or man
172
172
  The service exposes:
173
173
 
174
174
  - `POST /v1/plugin/access` for an anonymous free-preview policy, compatibility credential, and short-lived artifact grant;
175
- - `POST /v1/plugin/sessions/reserve|commit|release` for migration compatibility with activation-v2 clients;
176
175
  - `GET /v1/plugin/releases` for an Ed25519-signed release selected from the private R2 catalog;
177
176
  - `GET|HEAD /v1/artifacts/download` for the exact content-addressed object authorized by the bearer grant.
178
177
 
@@ -191,7 +190,7 @@ The bootstrap may send only:
191
190
  - core version, plugin-host API version, platform, and architecture;
192
191
  - requested bundle ID, installed bundle version, and release channel;
193
192
  - a pseudonymous license or organization identifier;
194
- - protocol nonces, opaque quota operation IDs, and authentication material required for the request.
193
+ - protocol nonces and authentication material required for the request.
195
194
 
196
195
  It must never send memory contents, search queries, session contents, raw agent session identifiers, working-directory names, repository names, filesystem paths, or qmd data. The bounded allowance counter is authorization state, not general product telemetry.
197
196
 
@@ -267,7 +266,7 @@ An install or upgrade must:
267
266
 
268
267
  Failure before activation leaves the previous version active. Failure immediately after activation restores the previous receipt. Concurrent installers do not interleave. The core never invokes package-manager lifecycle scripts or elevates privileges.
269
268
 
270
- Uninstall removes executable versions, the active receipt, contributed skills, and managed hooks. It does not remove `MEMORY.md`, daily logs, topics, scratchpad items, source session logs, plugin-created review data, or billing state. A separate future purge command would require explicit scope and confirmation.
269
+ Uninstall removes executable versions, the active receipt, contributed skills, and managed hooks. It does not remove `MEMORY.md`, daily logs, topics, scratchpad items, source session logs, plugin-created review data, or local activation state. The top-level `agent-memory uninstall` command composes this with hook/skill/MCP/completion removal in one step; its explicit `--data` flag additionally deletes the memory directory and the entire plugin install root (bundles, receipts, and activation state) once the user opts in and confirms.
271
270
 
272
271
  ## Plugin host API v1
273
272
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagentmemory",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "agentmemory (agent-memory) is persistent memory for coding agents (Claude Code, OpenAI Codex, Cursor, Agent) with qmd-powered semantic search across daily logs, long-term memory, and scratchpad",
5
5
  "main": "./dist/core.js",
6
6
  "types": "./dist/core.d.ts",
@@ -93,7 +93,6 @@
93
93
  "dist/plugin-runtime.js",
94
94
  "dist/plugin-service.d.ts",
95
95
  "dist/plugin-service.js",
96
-
97
96
  "dist/mcp-server.d.ts",
98
97
  "dist/mcp-server.js",
99
98
  "dist/upgrade.d.ts",
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Install (or uninstall) agent-memory skills for Claude Code, Codex, Cursor, and Agent CLI.
2
+ # Install (or uninstall) agent-memory skills for Claude Code, Codex, Cursor, Agent CLI, and Qoder.
3
3
  # Usage: bash scripts/install-skills.sh [--uninstall]
4
4
 
5
5
  set -euo pipefail
@@ -63,12 +63,14 @@ SKILL_DIRS=(
63
63
  "$HOME/.codex/skills/agent-memory"
64
64
  "$HOME/.cursor/skills/agent-memory"
65
65
  "$HOME/.agents/skills/agent-memory"
66
+ "$HOME/.qoder/skills/agent-memory"
66
67
  )
67
68
  SKILL_LABELS=(
68
69
  "Claude Code skill"
69
70
  "Codex skill"
70
71
  "Cursor skill"
71
72
  "Agent CLI skill"
73
+ "Qoder skill"
72
74
  )
73
75
 
74
76
  if $UNINSTALL; then
@@ -84,6 +86,7 @@ else
84
86
  install_skill "Codex skill" "$PROJECT_DIR/skills/codex" "$HOME/.codex/skills/agent-memory" "$HOME/.codex" '[ -f "$HOME/.codex/config.toml" ] || command_exists codex'
85
87
  install_skill "Cursor skill" "$PROJECT_DIR/skills/cursor" "$HOME/.cursor/skills/agent-memory" "$HOME/.cursor"
86
88
  install_skill "Agent CLI skill" "$PROJECT_DIR/skills/agent" "$HOME/.agents/skills/agent-memory" "$HOME/.agents"
89
+ install_skill "Qoder skill" "$PROJECT_DIR/skills/qoder" "$HOME/.qoder/skills/agent-memory" "$HOME/.qoder" '[ -f "$HOME/.qoder/settings.json" ] || [ -f "$HOME/.qoder/settings.local.json" ] || command_exists qoder'
87
90
  echo ""
88
91
  echo "Done."
89
92
  fi
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agent-memory
3
- description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and semantic search.
3
+ description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and keyword/semantic search, plus recall of past chat sessions. Use whenever the user says "remember", "recall", or asks to look up/search memory.
4
4
  ---
5
5
 
6
6
  # Agent Memory
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agent-memory
3
- description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and semantic search.
3
+ description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and keyword/semantic search, plus recall of past chat sessions. Use whenever the user says "remember", "recall", or asks to look up/search memory.
4
4
  allowed-tools: Bash(agent-memory *)
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agent-memory
3
- description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and semantic search.
3
+ description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and keyword/semantic search, plus recall of past chat sessions. Use whenever the user says "remember", "recall", or asks to look up/search memory.
4
4
  ---
5
5
 
6
6
  # Agent Memory
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agent-memory
3
- description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and semantic search.
3
+ description: Persistent memory across coding sessions — long-term facts, daily logs, topic notes, scratchpad checklist, and keyword/semantic search, plus recall of past chat sessions. Use whenever the user says "remember", "recall", or asks to look up/search memory.
4
4
  ---
5
5
 
6
6
  # Agent Memory