@agentmemory/agentmemory 0.9.28 → 0.9.29

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 (125) hide show
  1. package/.env.example +15 -6
  2. package/AGENTS.md +6 -6
  3. package/README.md +220 -152
  4. package/dist/.env.example +15 -6
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +233 -35
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{connect-JnsJXc68.mjs → connect-Qaeq15TQ.mjs} +599 -41
  9. package/dist/connect-Qaeq15TQ.mjs.map +1 -0
  10. package/dist/docker-compose.yml +2 -2
  11. package/dist/hooks/antigravity-bridge.d.mts +8 -0
  12. package/dist/hooks/antigravity-bridge.d.mts.map +1 -0
  13. package/dist/hooks/antigravity-bridge.mjs +114 -0
  14. package/dist/hooks/antigravity-bridge.mjs.map +1 -0
  15. package/dist/hooks/notification.mjs +19 -4
  16. package/dist/hooks/notification.mjs.map +1 -1
  17. package/dist/hooks/post-commit.mjs +13 -1
  18. package/dist/hooks/post-commit.mjs.map +1 -1
  19. package/dist/hooks/post-tool-failure.mjs +14 -3
  20. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  21. package/dist/hooks/post-tool-use.mjs +14 -3
  22. package/dist/hooks/post-tool-use.mjs.map +1 -1
  23. package/dist/hooks/pre-compact.mjs +12 -2
  24. package/dist/hooks/pre-compact.mjs.map +1 -1
  25. package/dist/hooks/pre-tool-use.mjs +1 -1
  26. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  27. package/dist/hooks/prompt-submit.mjs +14 -3
  28. package/dist/hooks/prompt-submit.mjs.map +1 -1
  29. package/dist/hooks/session-end.mjs +82 -18
  30. package/dist/hooks/session-end.mjs.map +1 -1
  31. package/dist/hooks/session-start.mjs +22 -4
  32. package/dist/hooks/session-start.mjs.map +1 -1
  33. package/dist/hooks/stop.mjs +1 -7
  34. package/dist/hooks/stop.mjs.map +1 -1
  35. package/dist/hooks/subagent-start.mjs +14 -3
  36. package/dist/hooks/subagent-start.mjs.map +1 -1
  37. package/dist/hooks/subagent-stop.mjs +14 -3
  38. package/dist/hooks/subagent-stop.mjs.map +1 -1
  39. package/dist/hooks/task-completed.mjs +14 -3
  40. package/dist/hooks/task-completed.mjs.map +1 -1
  41. package/dist/{image-refs-C2YU7BIm.mjs → image-refs-CJgZmb9X.mjs} +2 -2
  42. package/dist/{image-refs-C2YU7BIm.mjs.map → image-refs-CJgZmb9X.mjs.map} +1 -1
  43. package/dist/index.mjs +1557 -755
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/schema-BFZhNTF7.mjs +197 -0
  46. package/dist/schema-BFZhNTF7.mjs.map +1 -0
  47. package/dist/{src-CzgoepGU.mjs → src-G7yt8gGm.mjs} +1531 -883
  48. package/dist/src-G7yt8gGm.mjs.map +1 -0
  49. package/dist/{standalone-YsZFFQBM.mjs → standalone-BqNJDJ04.mjs} +26 -15
  50. package/dist/standalone-BqNJDJ04.mjs.map +1 -0
  51. package/dist/standalone.d.mts.map +1 -1
  52. package/dist/standalone.mjs +50 -14
  53. package/dist/standalone.mjs.map +1 -1
  54. package/dist/{tools-registry-D3NEoe5m.mjs → tools-registry-BV_2bu7z.mjs} +293 -3
  55. package/dist/tools-registry-BV_2bu7z.mjs.map +1 -0
  56. package/dist/viewer/favicon.svg +35 -1
  57. package/dist/viewer/index.html +473 -83
  58. package/docker-compose.yml +2 -2
  59. package/integrations/pi/README.md +77 -0
  60. package/integrations/pi/index.ts +403 -0
  61. package/integrations/pi/package.json +32 -0
  62. package/integrations/pi/security.ts +35 -0
  63. package/package.json +3 -4
  64. package/plugin/.claude-plugin/plugin.json +2 -2
  65. package/plugin/.codex-plugin/plugin.json +2 -2
  66. package/plugin/.devin-plugin/plugin.json +32 -0
  67. package/plugin/cursor/hooks.json +41 -0
  68. package/plugin/cursor/mcp.json +12 -0
  69. package/plugin/hooks/hooks.antigravity.json +43 -0
  70. package/plugin/hooks/hooks.devin.json +67 -0
  71. package/plugin/hooks/hooks.droid.json +55 -0
  72. package/plugin/opencode/README.md +1 -1
  73. package/plugin/opencode/agentmemory-capture.ts +73 -13
  74. package/plugin/opencode/plugin.json +1 -1
  75. package/plugin/plugin.json +2 -2
  76. package/plugin/scripts/antigravity-bridge.d.mts +8 -0
  77. package/plugin/scripts/antigravity-bridge.d.mts.map +1 -0
  78. package/plugin/scripts/antigravity-bridge.mjs +114 -0
  79. package/plugin/scripts/antigravity-bridge.mjs.map +1 -0
  80. package/plugin/scripts/notification.mjs +19 -4
  81. package/plugin/scripts/notification.mjs.map +1 -1
  82. package/plugin/scripts/post-commit.mjs +13 -1
  83. package/plugin/scripts/post-commit.mjs.map +1 -1
  84. package/plugin/scripts/post-tool-failure.mjs +14 -3
  85. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  86. package/plugin/scripts/post-tool-use.mjs +14 -3
  87. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  88. package/plugin/scripts/pre-compact.mjs +12 -2
  89. package/plugin/scripts/pre-compact.mjs.map +1 -1
  90. package/plugin/scripts/pre-tool-use.mjs +1 -1
  91. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  92. package/plugin/scripts/prompt-submit.mjs +14 -3
  93. package/plugin/scripts/prompt-submit.mjs.map +1 -1
  94. package/plugin/scripts/session-end.mjs +82 -18
  95. package/plugin/scripts/session-end.mjs.map +1 -1
  96. package/plugin/scripts/session-start.mjs +22 -4
  97. package/plugin/scripts/session-start.mjs.map +1 -1
  98. package/plugin/scripts/stop.mjs +1 -7
  99. package/plugin/scripts/stop.mjs.map +1 -1
  100. package/plugin/scripts/subagent-start.mjs +14 -3
  101. package/plugin/scripts/subagent-start.mjs.map +1 -1
  102. package/plugin/scripts/subagent-stop.mjs +14 -3
  103. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  104. package/plugin/scripts/task-completed.mjs +14 -3
  105. package/plugin/scripts/task-completed.mjs.map +1 -1
  106. package/plugin/skills/agentmemory-agents/REFERENCE.md +6 -3
  107. package/plugin/skills/agentmemory-config/REFERENCE.md +4 -1
  108. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +3 -2
  109. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +14 -1
  110. package/plugin/skills/forget/SKILL.md +4 -1
  111. package/plugin/skills/lesson/SKILL.md +64 -0
  112. package/plugin/skills/memory-discipline/SKILL.md +57 -0
  113. package/plugin/skills/recall/SKILL.md +4 -1
  114. package/plugin/skills/remember/SKILL.md +6 -1
  115. package/dist/config-ChC9EtgV.mjs +0 -259
  116. package/dist/config-ChC9EtgV.mjs.map +0 -1
  117. package/dist/connect-JnsJXc68.mjs.map +0 -1
  118. package/dist/schema-CNiCWzV9.mjs +0 -78
  119. package/dist/schema-CNiCWzV9.mjs.map +0 -1
  120. package/dist/src-CzgoepGU.mjs.map +0 -1
  121. package/dist/standalone-YsZFFQBM.mjs.map +0 -1
  122. package/dist/tools-registry-D3NEoe5m.mjs.map +0 -1
  123. package/plugin/scripts/diagnostics.d.mts +0 -17
  124. package/plugin/scripts/diagnostics.d.mts.map +0 -1
  125. package/plugin/scripts/diagnostics.mjs.map +0 -1
package/dist/.env.example CHANGED
@@ -26,22 +26,24 @@
26
26
  # The detection order is OPENAI_API_KEY → MINIMAX_API_KEY → ANTHROPIC_API_KEY
27
27
  # → GEMINI_API_KEY → OPENROUTER_API_KEY → noop.
28
28
 
29
- # OPENAI_API_KEY=sk-... # Used for OpenAI-compatible embeddings today. PR #307 will extend this to chat completions (DeepSeek, SiliconFlow, vLLM, LM Studio, Ollama via `/v1`).
29
+ # OPENAI_API_KEY=sk-... # Activates both the OpenAI-compatible LLM provider (DeepSeek, SiliconFlow, vLLM, LM Studio, Ollama via `/v1`) and OpenAI embeddings. Set OPENAI_API_KEY_FOR_LLM=false to scope it to embeddings only.
30
30
  # OPENAI_BASE_URL=https://api.openai.com # Override for OpenAI-compatible providers
31
+ # OPENAI_MODEL=gpt-5.6-luna # Default OpenAI-compatible chat model
32
+ # OPENAI_API_KEY_FOR_LLM=false # Skip OpenAI auto-detection for LLM; key stays active for embeddings
31
33
 
32
34
  # ANTHROPIC_API_KEY=sk-ant-...
33
- # ANTHROPIC_MODEL=claude-sonnet-4-20250514 # Default Anthropic model
35
+ # ANTHROPIC_MODEL=claude-sonnet-5 # Default Anthropic model
34
36
  # ANTHROPIC_BASE_URL=https://api.anthropic.com # Override for Anthropic-compatible proxies / Azure AI Foundry
35
37
 
36
38
  # GEMINI_API_KEY=... # Either env name works; GEMINI_API_KEY takes precedence
37
39
  # GOOGLE_API_KEY=... # Alias for GEMINI_API_KEY when set alone (emits a one-time stderr hint)
38
- # GEMINI_MODEL=gemini-2.5-flash # Default Gemini model (auto-detected GA model)
40
+ # GEMINI_MODEL=gemini-3.7-flash # Default Gemini model (current stable Flash)
39
41
 
40
42
  # OPENROUTER_API_KEY=sk-or-...
41
- # OPENROUTER_MODEL=anthropic/claude-sonnet-4-20250514
43
+ # OPENROUTER_MODEL=anthropic/claude-sonnet-5
42
44
 
43
45
  # MINIMAX_API_KEY=...
44
- # MINIMAX_MODEL=MiniMax-M2.7
46
+ # MINIMAX_MODEL=MiniMax-M3
45
47
 
46
48
  # MAX_TOKENS=4096 # Cap LLM completion tokens for compression / summarise calls
47
49
 
@@ -111,6 +113,13 @@
111
113
  # CONSOLIDATION_DECAY_DAYS=30 # Age (days) after which non-reinforced memories decay during consolidation
112
114
  # GRAPH_EXTRACTION_ENABLED=true # Extract concept-graph edges on remember; powers the graph-traversal recall path
113
115
  # GRAPH_EXTRACTION_BATCH_SIZE=8 # Memories per graph-extraction batch
116
+
117
+ # Local reasoning models only: set to 1 to ask the model to skip its
118
+ # hidden thinking pass during graph extraction. Extraction runs several
119
+ # times faster; relation quality can drop slightly. Leave unset to let
120
+ # the model think (default).
121
+ # AGENTMEMORY_LLM_NOTHINK=1
122
+
114
123
  # AGENTMEMORY_REFLECT=true # Periodically auto-synthesize lessons from memories
115
124
  # AGENTMEMORY_DROP_STALE_INDEX=true # Drop on-disk BM25 / vector index on startup if dim guard fires (#248). Recovery toggle for stuck-state debugging.
116
125
  # AGENTMEMORY_IMAGE_EMBEDDINGS=true # Enable image embeddings when an image provider is present (experimental).
@@ -119,7 +128,7 @@
119
128
  # 6. CLI / runtime knobs
120
129
  # -----------------------------------------------------------------------------
121
130
 
122
- # AGENTMEMORY_TOOLS=all # core (7 tools, default) | all (51 tools) — surface exposed to MCP clients
131
+ # AGENTMEMORY_TOOLS=core # all (54 tools, default) | core (8 tools): surface exposed to MCP clients
123
132
  # AGENTMEMORY_SLOTS=memory # Comma-separated plugin slot names the CLI should claim
124
133
  # AGENTMEMORY_DEBUG=1 # Trace MCP shim probe + standalone fallback decisions to stderr
125
134
  # AGENTMEMORY_FORCE_PROXY=1 # Skip the MCP shim livez probe and trust AGENTMEMORY_URL (for sandboxed MCP clients that can't reach localhost)
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";iBAqRsB,kBAAA,CAAA,GAAsB,OAAA"}
1
+ {"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";iBAoSsB,kBAAA,CAAA,GAAsB,OAAA"}
package/dist/cli.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { a as runAdapter, i as resolveAdapter, r as knownAgents, t as ADAPTERS } from "./connect-JnsJXc68.mjs";
3
- import { i as generateId } from "./schema-CNiCWzV9.mjs";
2
+ import { a as runAdapter, i as resolveAdapter, r as knownAgents, t as ADAPTERS } from "./connect-Qaeq15TQ.mjs";
3
+ import { i as generateId } from "./schema-BFZhNTF7.mjs";
4
4
  import { r as setBootVerbose } from "./logger-BEB7pCI9.mjs";
5
- import { i as VERSION, n as getAllTools, t as ESSENTIAL_TOOLS } from "./tools-registry-D3NEoe5m.mjs";
5
+ import { i as VERSION, n as getAllTools, p as hydrateProcessEnvFromFile, t as ESSENTIAL_TOOLS } from "./tools-registry-BV_2bu7z.mjs";
6
6
  import { execFileSync, spawn, spawnSync } from "node:child_process";
7
7
  import { appendFileSync, closeSync, constants, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
8
- import { delimiter, dirname, join } from "node:path";
8
+ import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { homedir, platform } from "node:os";
11
11
  import * as p from "@clack/prompts";
@@ -525,7 +525,7 @@ const PROVIDERS = [
525
525
  },
526
526
  {
527
527
  value: "minimax",
528
- label: "MiniMax — minimax-m1",
528
+ label: "MiniMax — MiniMax-M3",
529
529
  envKey: "MINIMAX_API_KEY"
530
530
  },
531
531
  {
@@ -765,6 +765,87 @@ async function wireSelectedAgents(agents) {
765
765
  p.note(summary.join("\n"), "wire summary");
766
766
  }
767
767
  //#endregion
768
+ //#region src/cli-data-dir.ts
769
+ function argValue(args, name) {
770
+ const equals = `${name}=`;
771
+ const inline = args.find((arg) => arg.startsWith(equals));
772
+ if (inline) return inline.slice(equals.length);
773
+ const idx = args.indexOf(name);
774
+ if (idx !== -1) return args[idx + 1];
775
+ }
776
+ function expandHome(pathValue, home) {
777
+ if (pathValue === "~") return home;
778
+ if (pathValue.startsWith("~/") || pathValue.startsWith("~\\")) return join(home, pathValue.slice(2));
779
+ return pathValue;
780
+ }
781
+ function absoluteDataDir(pathValue, cwd, home) {
782
+ const expanded = expandHome(pathValue, home);
783
+ return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
784
+ }
785
+ function platformDefaultDataDir(env, home, nodePlatform, useXdg = true) {
786
+ if (nodePlatform === "darwin") return join(home, "Library", "Application Support", "agentmemory");
787
+ if (nodePlatform === "win32") {
788
+ const appData = env["APPDATA"];
789
+ return appData ? join(appData, "agentmemory") : join(home, ".agentmemory");
790
+ }
791
+ const xdgDataHome = env["XDG_DATA_HOME"];
792
+ if (useXdg && xdgDataHome && isAbsolute(xdgDataHome)) return join(xdgDataHome, "agentmemory");
793
+ return join(home, ".local", "share", "agentmemory");
794
+ }
795
+ function nearestGitParent(pathValue) {
796
+ let current = pathValue;
797
+ while (true) {
798
+ if (existsSync(join(current, ".git"))) return current;
799
+ const parent = dirname(current);
800
+ if (parent === current) return void 0;
801
+ current = parent;
802
+ }
803
+ }
804
+ function isWithin(parent, child) {
805
+ const pathFromParent = relative(parent, child);
806
+ return pathFromParent === "" || pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep}`) && !isAbsolute(pathFromParent);
807
+ }
808
+ function resolveDataDir(options = {}) {
809
+ const args = options.args ?? process.argv.slice(2);
810
+ const env = options.env ?? process.env;
811
+ const cwd = options.cwd ?? process.cwd();
812
+ const home = options.home ?? homedir();
813
+ const nodePlatform = options.platform ?? platform();
814
+ const parsedInstance = parseInt(argValue(args, "--instance") ?? "0", 10);
815
+ const instance = parsedInstance > 0 && parsedInstance <= 50 ? parsedInstance : 0;
816
+ const forInstance = (dataDir) => instance ? join(dataDir, `instance-${instance}`) : dataDir;
817
+ const flagValue = argValue(args, "--data-dir");
818
+ if (flagValue) return {
819
+ dataDir: forInstance(absoluteDataDir(flagValue, cwd, home)),
820
+ source: "flag"
821
+ };
822
+ const envValue = env["AGENTMEMORY_DATA_DIR"];
823
+ if (envValue) return {
824
+ dataDir: forInstance(absoluteDataDir(envValue, cwd, home)),
825
+ source: "env"
826
+ };
827
+ const legacyDir = resolve(cwd, "data");
828
+ const hasLegacyStore = existsSync(join(legacyDir, "state_store.db")) || existsSync(join(legacyDir, "iii-config.yaml"));
829
+ if (instance === 0 && hasLegacyStore) return {
830
+ dataDir: legacyDir,
831
+ source: "default"
832
+ };
833
+ const defaultDir = platformDefaultDataDir(env, home, nodePlatform);
834
+ const gitParent = nearestGitParent(cwd);
835
+ if (gitParent && isWithin(gitParent, defaultDir)) {
836
+ const relocated = platformDefaultDataDir(env, home, nodePlatform, false);
837
+ if (relocated !== defaultDir) return {
838
+ dataDir: forInstance(relocated),
839
+ source: "default",
840
+ relocatedFrom: defaultDir
841
+ };
842
+ }
843
+ return {
844
+ dataDir: forInstance(defaultDir),
845
+ source: "default"
846
+ };
847
+ }
848
+ //#endregion
768
849
  //#region src/cli.ts
769
850
  const c = {
770
851
  url: pc.cyan,
@@ -784,6 +865,7 @@ const IS_WINDOWS = platform() === "win32";
784
865
  const IS_VERBOSE = args.includes("--verbose") || args.includes("-v") || process.env["AGENTMEMORY_VERBOSE"] === "1" || process.env["AGENTMEMORY_VERBOSE"] === "true";
785
866
  setBootVerbose(IS_VERBOSE);
786
867
  const IS_RESET = args.includes("--reset");
868
+ hydrateProcessEnvFromFile();
787
869
  if (args.includes("--version") || args.includes("-V")) {
788
870
  process.stdout.write(`${VERSION}\n`);
789
871
  process.exit(0);
@@ -867,10 +949,12 @@ Options:
867
949
  --instance <N> Shortcut for --port (3111 + N*100) to run multiple
868
950
  daemons side-by-side without env gymnastics.
869
951
  --instance 1 -> 3211/3212/3213/49234, etc. (max N=50)
952
+ --data-dir <path> Store iii-engine state outside the current repo
870
953
 
871
954
  Environment:
872
955
  AGENTMEMORY_URL Full REST base URL (e.g. http://localhost:3111).
873
956
  Honored by status, doctor, and MCP shim commands.
957
+ AGENTMEMORY_DATA_DIR State directory fallback when --data-dir is not set.
874
958
  AGENTMEMORY_USE_DOCKER=1 Prefer the bundled docker-compose path over the
875
959
  native iii-engine binary on first run.
876
960
  AGENTMEMORY_III_VERSION Override pinned iii-engine version (default ${IIPINNED_VERSION}).
@@ -905,6 +989,8 @@ if (instanceIdx !== -1 && args[instanceIdx + 1]) {
905
989
  if (!process.env["III_REST_PORT"]) process.env["III_REST_PORT"] = String(base);
906
990
  }
907
991
  }
992
+ const dataDirResolution = resolveDataDir({ args });
993
+ if (dataDirResolution.source !== "default") process.env["AGENTMEMORY_DATA_DIR"] = dataDirResolution.dataDir;
908
994
  const skipEngine = args.includes("--no-engine");
909
995
  function getRestPort() {
910
996
  const url = process.env["AGENTMEMORY_URL"];
@@ -999,6 +1085,30 @@ function findIiiConfig() {
999
1085
  for (const c of candidates) if (existsSync(c)) return c;
1000
1086
  return "";
1001
1087
  }
1088
+ function yamlSingleQuoted(value) {
1089
+ return `'${value.replace(/'/g, "''")}'`;
1090
+ }
1091
+ function renderIiiConfig(template, dataDir) {
1092
+ return template.replace("file_path: ./data/state_store.db", `file_path: ${yamlSingleQuoted(join(dataDir, "state_store.db"))}`).replace("file_path: ./data/stream_store", `file_path: ${yamlSingleQuoted(join(dataDir, "stream_store"))}`);
1093
+ }
1094
+ function writeRuntimeIiiConfig(configPath, dataDir) {
1095
+ mkdirSync(dataDir, { recursive: true });
1096
+ const runtimeConfigPath = join(dataDir, "iii-config.yaml");
1097
+ writeFileSync(runtimeConfigPath, renderIiiConfig(readFileSync(configPath, "utf8"), dataDir));
1098
+ return runtimeConfigPath;
1099
+ }
1100
+ function warnIfRelocatedDataDir() {
1101
+ if (!dataDirResolution.relocatedFrom) return;
1102
+ try {
1103
+ mkdirSync(dataDirResolution.dataDir, { recursive: true });
1104
+ const marker = join(dataDirResolution.dataDir, ".cwd-relocation-warning");
1105
+ if (existsSync(marker)) return;
1106
+ p.log.warn(`Default data dir ${dataDirResolution.relocatedFrom} is inside a git worktree; using ${dataDirResolution.dataDir} instead.`);
1107
+ writeFileSync(marker, (/* @__PURE__ */ new Date()).toISOString());
1108
+ } catch {
1109
+ p.log.warn(`Default data dir ${dataDirResolution.relocatedFrom} is inside a git worktree; using ${dataDirResolution.dataDir} instead.`);
1110
+ }
1111
+ }
1002
1112
  function whichBinary(name) {
1003
1113
  const cmd = IS_WINDOWS ? "where" : "which";
1004
1114
  try {
@@ -1175,8 +1285,8 @@ async function maybeOfferGlobalInstall() {
1175
1285
  if (runCommand(npmBin, [
1176
1286
  "install",
1177
1287
  "-g",
1178
- `@agentmemory/agentmemory@0.9.28`
1179
- ], { label: `Installing @agentmemory/agentmemory@0.9.28 globally` })) {
1288
+ `@agentmemory/agentmemory@0.9.29`
1289
+ ], { label: `Installing @agentmemory/agentmemory@0.9.29 globally` })) {
1180
1290
  p.log.success("Installed globally. `agentmemory stop` etc. will now work in new shells.");
1181
1291
  writePrefs({ skipGlobalInstall: true });
1182
1292
  } else p.log.warn("Global install failed. Try manually: npm install -g @agentmemory/agentmemory");
@@ -1231,6 +1341,13 @@ function adoptRunningEngine() {
1231
1341
  const existingPid = readEnginePidfile();
1232
1342
  if (existingState && existingPid) return;
1233
1343
  const enginePid = findEnginePidsByPort(getRestPort())[0];
1344
+ if (enginePid) {
1345
+ const comm = pidCommand(enginePid);
1346
+ if (isForeignPortHolder(comm)) {
1347
+ vlog(`adoptRunningEngine: refusing to adopt pid ${enginePid} (${comm}) — not the iii engine binary`);
1348
+ return;
1349
+ }
1350
+ }
1234
1351
  if (enginePid && !existingPid) writeEnginePidfile(enginePid);
1235
1352
  if (!existingState) writeEngineState({
1236
1353
  kind: "native",
@@ -1352,17 +1469,19 @@ function pickCompatibleIii(candidates) {
1352
1469
  }
1353
1470
  async function startEngine() {
1354
1471
  const configPath = findIiiConfig();
1472
+ warnIfRelocatedDataDir();
1473
+ const runtimeConfigPath = configPath ? writeRuntimeIiiConfig(configPath, dataDirResolution.dataDir) : "";
1355
1474
  const pathIii = whichBinary("iii");
1356
- vlog(`iii binary: ${pathIii ?? "(not on PATH)"}, config: ${configPath || "(not found)"}`);
1475
+ vlog(`iii binary: ${pathIii ?? "(not on PATH)"}, config: ${runtimeConfigPath || "(not found)"}`);
1357
1476
  const fallbacks = fallbackIiiPaths().filter((p) => existsSync(p));
1358
1477
  for (const f of fallbacks) vlog(`fallback iii at ${f} reports version: ${iiiBinVersion(f) ?? "unknown"}`);
1359
1478
  let iiiBin = pickCompatibleIii([pathIii, ...fallbacks]);
1360
- if (iiiBin && configPath) {
1479
+ if (iiiBin && runtimeConfigPath) {
1361
1480
  if (iiiBin !== pathIii) {
1362
1481
  p.log.info(`Using iii at: ${c.dim(iiiBin)} (v${c.accent(IIPINNED_VERSION)})`);
1363
1482
  process.env["PATH"] = `${dirname(iiiBin)}${delimiter}${process.env["PATH"] ?? ""}`;
1364
1483
  }
1365
- return startIiiBin(iiiBin, configPath);
1484
+ return startIiiBin(iiiBin, runtimeConfigPath);
1366
1485
  }
1367
1486
  if (pathIii && !iiiBin) vlog(`iii on PATH is v${iiiBinVersion(pathIii) ?? "unknown"}, pin is v${IIPINNED_VERSION}. Will install pinned engine to ${privateIiiPath()}.`);
1368
1487
  if (!configPath) {
@@ -1371,6 +1490,7 @@ async function startEngine() {
1371
1490
  }
1372
1491
  const dockerBin = whichBinary("docker");
1373
1492
  vlog(`docker binary: ${dockerBin ?? "(not on PATH)"}`);
1493
+ mkdirSync(dataDirResolution.dataDir, { recursive: true });
1374
1494
  const composeFile = [
1375
1495
  join(__dirname, "..", "docker-compose.yml"),
1376
1496
  join(__dirname, "docker-compose.yml"),
@@ -1545,6 +1665,12 @@ function printReadyHint(consoleState) {
1545
1665
  process.stdout.write(`\n${c.dim("Try:")} ${c.cmd(demoCommand)}\n`);
1546
1666
  }
1547
1667
  async function main() {
1668
+ try {
1669
+ if ((await fetch(`${getBaseUrl()}/agentmemory/livez`, { signal: AbortSignal.timeout(1500) })).ok) {
1670
+ p.log.error(`agentmemory is already running on port ${getRestPort()}. Starting a second instance here would corrupt the running daemon's REST routing. Use the REST API (or the MCP tools) against the running instance, run a different --instance, or stop it first with \`agentmemory stop\`.`);
1671
+ process.exit(1);
1672
+ }
1673
+ } catch {}
1548
1674
  if (IS_RESET) resetPrefs();
1549
1675
  const firstRun = isFirstRun();
1550
1676
  const prefs = readPrefs();
@@ -1552,7 +1678,7 @@ async function main() {
1552
1678
  if (firstRun || IS_RESET) await runOnboarding();
1553
1679
  if (skipEngine) {
1554
1680
  if (IS_VERBOSE) p.log.info("Skipping engine check (--no-engine)");
1555
- await import("./src-CzgoepGU.mjs");
1681
+ await import("./src-G7yt8gGm.mjs");
1556
1682
  if (await waitForAgentmemoryReady(15e3)) {
1557
1683
  const consoleState = await ensureIiiConsole();
1558
1684
  await maybeOfferGlobalInstall();
@@ -1567,7 +1693,7 @@ async function main() {
1567
1693
  const detected = attachedBin ? iiiBinVersion(attachedBin) : null;
1568
1694
  if (detected === IIPINNED_VERSION) {
1569
1695
  adoptRunningEngine();
1570
- await import("./src-CzgoepGU.mjs");
1696
+ await import("./src-G7yt8gGm.mjs");
1571
1697
  if (await waitForAgentmemoryReady(15e3)) {
1572
1698
  const consoleState = await ensureIiiConsole();
1573
1699
  await maybeOfferGlobalInstall();
@@ -1638,7 +1764,7 @@ async function main() {
1638
1764
  process.exit(1);
1639
1765
  }
1640
1766
  s.stop(c.ok("iii-engine is ready"));
1641
- await import("./src-CzgoepGU.mjs");
1767
+ await import("./src-G7yt8gGm.mjs");
1642
1768
  if (await waitForAgentmemoryReady(15e3)) {
1643
1769
  const consoleState = await ensureIiiConsole();
1644
1770
  await maybeOfferGlobalInstall();
@@ -2286,7 +2412,7 @@ async function startServerForDemo() {
2286
2412
  process.exit(1);
2287
2413
  }
2288
2414
  }
2289
- await import("./src-CzgoepGU.mjs");
2415
+ await import("./src-G7yt8gGm.mjs");
2290
2416
  if (!await waitForAgentmemoryReady(15e3)) {
2291
2417
  p.log.error("agentmemory worker did not become ready within 15s.");
2292
2418
  process.exit(1);
@@ -2346,6 +2472,7 @@ async function runDemoBody(base) {
2346
2472
  const results = [];
2347
2473
  for (const query of queries) results.push(await runDemoSearch(base, query));
2348
2474
  sQuery.stop("Search complete");
2475
+ const semanticHits = results.find((r) => r.query === "database performance optimization")?.hits ?? 0;
2349
2476
  const lines = [
2350
2477
  `Project: ${demoProject}`,
2351
2478
  `Sessions: ${sessions.length} seeded (${totalObs} observations)`,
@@ -2353,8 +2480,11 @@ async function runDemoBody(base) {
2353
2480
  c.label("Search results:"),
2354
2481
  ...results.flatMap((r) => [` ${c.label(`"${r.query}"`)}`, ` ${c.dim("→")} ${c.ok(`${r.hits} hit(s)`)}, top: ${r.topTitle.slice(0, 60)}`]),
2355
2482
  "",
2356
- c.accent(`Notice: searching "database performance optimization"`),
2357
- c.accent(`found the N+1 query fix — keyword matching can't do that.`),
2483
+ ...semanticHits > 0 ? [c.accent(`Notice: searching "database performance optimization"`), c.accent(`found the N+1 query fix — keyword matching can't do that.`)] : [
2484
+ c.dim(`Note: "database performance optimization" found nothing —`),
2485
+ c.dim(`semantic recall needs an embedding provider key (e.g.`),
2486
+ c.dim(`OPENAI_API_KEY or GEMINI_API_KEY in ~/.agentmemory/.env).`)
2487
+ ],
2358
2488
  "",
2359
2489
  `Viewer: ${c.url(getViewerUrl())}`,
2360
2490
  `Clean up with: ${c.dim(`curl -X DELETE "${base}/agentmemory/sessions?project=${demoProject}"`)}`
@@ -2476,6 +2606,38 @@ async function signalAndWait(pid, initialSignal, timeoutMs) {
2476
2606
  await new Promise((r) => setTimeout(r, 200));
2477
2607
  return !pidAlive(pid);
2478
2608
  }
2609
+ async function stopWorkerPid(pid, graceMs) {
2610
+ const s = p.spinner();
2611
+ s.start(`Stopping agentmemory worker (pid ${pid})... [flushing state]`);
2612
+ const ok = await signalAndWait(pid, "SIGTERM", graceMs);
2613
+ s.stop(ok ? `Stopped worker pid ${pid}` : `Failed to stop worker pid ${pid}`);
2614
+ return ok;
2615
+ }
2616
+ function pidCommand(pid) {
2617
+ if (IS_WINDOWS) return "";
2618
+ try {
2619
+ return execFileSync("ps", [
2620
+ "-p",
2621
+ String(pid),
2622
+ "-o",
2623
+ "comm="
2624
+ ], {
2625
+ encoding: "utf-8",
2626
+ stdio: [
2627
+ "ignore",
2628
+ "pipe",
2629
+ "ignore"
2630
+ ]
2631
+ }).trim();
2632
+ } catch {
2633
+ return "";
2634
+ }
2635
+ }
2636
+ function isForeignPortHolder(comm) {
2637
+ if (!comm) return false;
2638
+ const base = comm.split("/").pop() || comm;
2639
+ return base !== "iii" && !base.startsWith("iii-");
2640
+ }
2479
2641
  function findEnginePidsByPort(port) {
2480
2642
  if (IS_WINDOWS) return [];
2481
2643
  const lsof = whichBinary("lsof");
@@ -2510,17 +2672,35 @@ async function stopDockerEngine(composeFile, port) {
2510
2672
  p.log.error(`Engine state references ${composeFile}, but the file is gone. Stop it manually:\n docker compose down (from the dir holding the original docker-compose.yml)`);
2511
2673
  process.exit(1);
2512
2674
  }
2675
+ const workerPid = readWorkerPidfile();
2676
+ let workerStopped = true;
2677
+ if (workerPid) workerStopped = await stopWorkerPid(workerPid, 5e3);
2678
+ let composeText = "";
2679
+ try {
2680
+ composeText = readFileSync(composeFile, "utf-8");
2681
+ } catch {
2682
+ composeText = "";
2683
+ }
2684
+ const ownServices = ["iii-engine", "iii-init"].filter((svc) => new RegExp(`^\\s+${svc}:`, "m").test(composeText));
2685
+ if (ownServices.length === 0) {
2686
+ p.log.error(`${composeFile} does not define the agentmemory services (iii-engine/iii-init). Refusing to run an unscoped \`docker compose down\` against it — that would tear down every service in the file.\n\nStop the engine service manually:\n docker compose -f ${composeFile} stop <service>`);
2687
+ process.exit(1);
2688
+ }
2513
2689
  const ok = runCommand(dockerBin, [
2514
2690
  "compose",
2515
2691
  "-f",
2516
2692
  composeFile,
2517
- "down"
2518
- ], { label: `docker compose -f ${composeFile} down` });
2519
- clearEnginePidfile();
2520
- clearEngineState();
2521
- clearWorkerPidfile();
2522
- if (!ok) {
2523
- p.log.error(`docker compose down failed. The engine may still be running on :${port}. Inspect with:\n docker compose -f ${composeFile} ps`);
2693
+ "rm",
2694
+ "-s",
2695
+ "-f",
2696
+ ...ownServices
2697
+ ], { label: `docker compose -f ${composeFile} rm -s -f ${ownServices.join(" ")}` });
2698
+ if (workerStopped) clearWorkerPidfile();
2699
+ if (ok) {
2700
+ clearEnginePidfile();
2701
+ clearEngineState();
2702
+ } else {
2703
+ p.log.error(`docker compose rm failed. The engine may still be running on :${port}. Inspect with:\n docker compose -f ${composeFile} ps`);
2524
2704
  process.exit(1);
2525
2705
  }
2526
2706
  p.outro("Stopped. Memories persisted to disk; restart anytime with: npx @agentmemory/agentmemory");
@@ -2594,15 +2774,18 @@ async function runStop() {
2594
2774
  process.exit(1);
2595
2775
  }
2596
2776
  let allStopped = true;
2597
- for (const pid of workerCandidates) {
2598
- const s = p.spinner();
2599
- s.start(`Stopping agentmemory worker (pid ${pid})... [flushing state]`);
2600
- const ok = await signalAndWait(pid, "SIGTERM", 5e3);
2601
- s.stop(ok ? `Stopped worker pid ${pid}` : `Failed to stop worker pid ${pid}`);
2602
- if (!ok) allStopped = false;
2603
- }
2777
+ for (const pid of workerCandidates) if (!await stopWorkerPid(pid, 5e3)) allStopped = false;
2778
+ const skippedForeign = [];
2604
2779
  for (const pid of candidates) {
2605
2780
  if (workerCandidates.has(pid)) continue;
2781
+ const comm = pidCommand(pid);
2782
+ if (!force && isForeignPortHolder(comm)) {
2783
+ skippedForeign.push({
2784
+ pid,
2785
+ comm
2786
+ });
2787
+ continue;
2788
+ }
2606
2789
  const s = p.spinner();
2607
2790
  s.start(`Stopping iii-engine (pid ${pid})...`);
2608
2791
  const ok = await signalAndWait(pid, "SIGTERM", 3e3);
@@ -2612,6 +2795,11 @@ async function runStop() {
2612
2795
  clearEnginePidfile();
2613
2796
  clearEngineState();
2614
2797
  clearWorkerPidfile();
2798
+ if (skippedForeign.length > 0) {
2799
+ const list = skippedForeign.map((sf) => ` pid ${sf.pid} ${sf.comm}`).join("\n");
2800
+ p.log.error(`Refused to signal process(es) holding :${port} that are not the iii engine:\n${list}\n\nIf the engine runs in Docker, stop it there:\n docker compose ps && docker compose rm -s -f <service>\n\nOr re-run with --force to signal them anyway.`);
2801
+ process.exit(1);
2802
+ }
2615
2803
  if (!allStopped) {
2616
2804
  p.log.error("One or more processes survived SIGKILL. Investigate with `ps`.");
2617
2805
  process.exit(1);
@@ -2619,14 +2807,18 @@ async function runStop() {
2619
2807
  p.outro("Stopped. Memories persisted to disk; restart anytime with: npx @agentmemory/agentmemory");
2620
2808
  }
2621
2809
  async function runMcp() {
2622
- await import("./standalone-YsZFFQBM.mjs");
2810
+ await import("./standalone-BqNJDJ04.mjs");
2623
2811
  }
2624
2812
  async function runConnectCmd() {
2625
- const { runConnect } = await import("./connect-JnsJXc68.mjs").then((n) => n.n);
2813
+ const { runConnect } = await import("./connect-Qaeq15TQ.mjs").then((n) => n.n);
2626
2814
  await runConnect(args.slice(1));
2627
2815
  }
2628
2816
  async function runImportJsonl() {
2629
- const VALUE_FLAGS = new Set(["--port", "--tools"]);
2817
+ const VALUE_FLAGS = new Set([
2818
+ "--port",
2819
+ "--tools",
2820
+ "--data-dir"
2821
+ ]);
2630
2822
  let maxFiles;
2631
2823
  const tail = args.slice(1);
2632
2824
  const positional = [];
@@ -2838,7 +3030,7 @@ async function runRemove() {
2838
3030
  }
2839
3031
  p.outro("Done. agentmemory cleanly removed. The npm package itself: npm uninstall -g @agentmemory/agentmemory");
2840
3032
  }
2841
- ({
3033
+ const commands = {
2842
3034
  init: runInit,
2843
3035
  connect: runConnectCmd,
2844
3036
  status: runStatus,
@@ -2849,7 +3041,13 @@ async function runRemove() {
2849
3041
  remove: runRemove,
2850
3042
  mcp: runMcp,
2851
3043
  "import-jsonl": runImportJsonl
2852
- }[args[0] ?? ""] ?? main)().catch((err) => {
3044
+ };
3045
+ const first = args[0] ?? "";
3046
+ async function unknownCommand() {
3047
+ p.log.error(`Unknown command: ${first}. Supported: ${Object.keys(commands).join(", ")}. Run \`agentmemory\` with no arguments to start the memory server, or \`agentmemory --help\` for usage.`);
3048
+ process.exit(1);
3049
+ }
3050
+ (commands[first] ?? (first && !first.startsWith("-") ? unknownCommand : main))().catch((err) => {
2853
3051
  p.log.error(err instanceof Error ? err.message : String(err));
2854
3052
  process.exit(1);
2855
3053
  });