@hasna/mementos 0.14.30 → 0.14.32

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 (240) hide show
  1. package/LICENSE +190 -3
  2. package/README.md +34 -34
  3. package/dist/cli/brains.d.ts +3 -0
  4. package/dist/cli/brains.d.ts.map +1 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +25767 -0
  8. package/dist/db/acl.d.ts +34 -0
  9. package/dist/db/acl.d.ts.map +1 -0
  10. package/dist/db/agents.d.ts +15 -0
  11. package/dist/db/agents.d.ts.map +1 -0
  12. package/dist/db/audit.d.ts +39 -0
  13. package/dist/db/audit.d.ts.map +1 -0
  14. package/dist/db/database.d.ts +10 -0
  15. package/dist/db/database.d.ts.map +1 -0
  16. package/dist/db/entities.d.ts +62 -0
  17. package/dist/db/entities.d.ts.map +1 -0
  18. package/dist/db/entity-memories.d.ts +34 -0
  19. package/dist/db/entity-memories.d.ts.map +1 -0
  20. package/dist/db/locks.d.ts +63 -0
  21. package/dist/db/locks.d.ts.map +1 -0
  22. package/dist/db/machines.d.ts +16 -0
  23. package/dist/db/machines.d.ts.map +1 -0
  24. package/dist/db/memories.d.ts +40 -0
  25. package/dist/db/memories.d.ts.map +1 -0
  26. package/dist/db/pg-migrate.d.ts +14 -0
  27. package/dist/db/pg-migrate.d.ts.map +1 -0
  28. package/dist/db/pg-migrations.d.ts +8 -0
  29. package/dist/db/pg-migrations.d.ts.map +1 -0
  30. package/dist/db/projects.d.ts +6 -0
  31. package/dist/db/projects.d.ts.map +1 -0
  32. package/dist/db/ratings.d.ts +23 -0
  33. package/dist/db/ratings.d.ts.map +1 -0
  34. package/dist/db/relations.d.ts +29 -0
  35. package/dist/db/relations.d.ts.map +1 -0
  36. package/dist/db/session-jobs.d.ts +49 -0
  37. package/dist/db/session-jobs.d.ts.map +1 -0
  38. package/dist/db/synthesis.d.ts +101 -0
  39. package/dist/db/synthesis.d.ts.map +1 -0
  40. package/dist/db/tool-events.d.ts +27 -0
  41. package/dist/db/tool-events.d.ts.map +1 -0
  42. package/dist/db/webhook_hooks.d.ts +25 -0
  43. package/dist/db/webhook_hooks.d.ts.map +1 -0
  44. package/dist/index.d.ts +27 -4
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +14642 -1006
  47. package/dist/lib/activation-matcher.d.ts +16 -0
  48. package/dist/lib/activation-matcher.d.ts.map +1 -0
  49. package/dist/lib/asmr/categorizer.d.ts +31 -0
  50. package/dist/lib/asmr/categorizer.d.ts.map +1 -0
  51. package/dist/lib/asmr/context-agent.d.ts +4 -0
  52. package/dist/lib/asmr/context-agent.d.ts.map +1 -0
  53. package/dist/lib/asmr/ensemble.d.ts +23 -0
  54. package/dist/lib/asmr/ensemble.d.ts.map +1 -0
  55. package/dist/lib/asmr/fact-agent.d.ts +4 -0
  56. package/dist/lib/asmr/fact-agent.d.ts.map +1 -0
  57. package/dist/lib/asmr/index.d.ts +7 -0
  58. package/dist/lib/asmr/index.d.ts.map +1 -0
  59. package/dist/lib/asmr/orchestrator.d.ts +4 -0
  60. package/dist/lib/asmr/orchestrator.d.ts.map +1 -0
  61. package/dist/lib/asmr/temporal-agent.d.ts +4 -0
  62. package/dist/lib/asmr/temporal-agent.d.ts.map +1 -0
  63. package/dist/lib/asmr/types.d.ts +27 -0
  64. package/dist/lib/asmr/types.d.ts.map +1 -0
  65. package/dist/lib/auto-inject-orchestrator.d.ts +57 -0
  66. package/dist/lib/auto-inject-orchestrator.d.ts.map +1 -0
  67. package/dist/lib/auto-memory-queue.d.ts +46 -0
  68. package/dist/lib/auto-memory-queue.d.ts.map +1 -0
  69. package/dist/lib/auto-memory.d.ts +18 -0
  70. package/dist/lib/auto-memory.d.ts.map +1 -0
  71. package/dist/lib/bench.d.ts +20 -0
  72. package/dist/lib/bench.d.ts.map +1 -0
  73. package/dist/lib/built-in-hooks.d.ts +12 -0
  74. package/dist/lib/built-in-hooks.d.ts.map +1 -0
  75. package/dist/lib/channel-pusher.d.ts +39 -0
  76. package/dist/lib/channel-pusher.d.ts.map +1 -0
  77. package/dist/lib/config.d.ts +9 -0
  78. package/dist/lib/config.d.ts.map +1 -0
  79. package/dist/lib/connectors/files.d.ts +8 -0
  80. package/dist/lib/connectors/files.d.ts.map +1 -0
  81. package/dist/lib/connectors/github.d.ts +7 -0
  82. package/dist/lib/connectors/github.d.ts.map +1 -0
  83. package/dist/lib/connectors/index.d.ts +12 -0
  84. package/dist/lib/connectors/index.d.ts.map +1 -0
  85. package/dist/lib/connectors/notion.d.ts +7 -0
  86. package/dist/lib/connectors/notion.d.ts.map +1 -0
  87. package/dist/lib/connectors/types.d.ts +27 -0
  88. package/dist/lib/connectors/types.d.ts.map +1 -0
  89. package/dist/lib/context-extractor.d.ts +14 -0
  90. package/dist/lib/context-extractor.d.ts.map +1 -0
  91. package/dist/lib/context.d.ts +35 -0
  92. package/dist/lib/context.d.ts.map +1 -0
  93. package/dist/lib/contradiction.d.ts +50 -0
  94. package/dist/lib/contradiction.d.ts.map +1 -0
  95. package/dist/lib/decay.d.ts +35 -0
  96. package/dist/lib/decay.d.ts.map +1 -0
  97. package/dist/lib/dedup.d.ts +33 -0
  98. package/dist/lib/dedup.d.ts.map +1 -0
  99. package/dist/lib/duration.d.ts +28 -0
  100. package/dist/lib/duration.d.ts.map +1 -0
  101. package/dist/lib/embeddings.d.ts +20 -0
  102. package/dist/lib/embeddings.d.ts.map +1 -0
  103. package/dist/lib/export-v1.d.ts +30 -0
  104. package/dist/lib/export-v1.d.ts.map +1 -0
  105. package/dist/lib/extractor.d.ts +9 -0
  106. package/dist/lib/extractor.d.ts.map +1 -0
  107. package/dist/lib/extractors/audio.d.ts +8 -0
  108. package/dist/lib/extractors/audio.d.ts.map +1 -0
  109. package/dist/lib/extractors/index.d.ts +12 -0
  110. package/dist/lib/extractors/index.d.ts.map +1 -0
  111. package/dist/lib/extractors/ocr.d.ts +7 -0
  112. package/dist/lib/extractors/ocr.d.ts.map +1 -0
  113. package/dist/lib/extractors/pdf.d.ts +7 -0
  114. package/dist/lib/extractors/pdf.d.ts.map +1 -0
  115. package/dist/lib/extractors/types.d.ts +12 -0
  116. package/dist/lib/extractors/types.d.ts.map +1 -0
  117. package/dist/lib/file-deps.d.ts +27 -0
  118. package/dist/lib/file-deps.d.ts.map +1 -0
  119. package/dist/lib/focus.d.ts +58 -0
  120. package/dist/lib/focus.d.ts.map +1 -0
  121. package/dist/lib/gatherer.d.ts +16 -0
  122. package/dist/lib/gatherer.d.ts.map +1 -0
  123. package/dist/lib/gdpr.d.ts +24 -0
  124. package/dist/lib/gdpr.d.ts.map +1 -0
  125. package/dist/lib/hooks.d.ts +50 -0
  126. package/dist/lib/hooks.d.ts.map +1 -0
  127. package/dist/lib/importers/index.d.ts +38 -0
  128. package/dist/lib/importers/index.d.ts.map +1 -0
  129. package/dist/lib/injector.d.ts +86 -0
  130. package/dist/lib/injector.d.ts.map +1 -0
  131. package/dist/lib/matryoshka.d.ts +50 -0
  132. package/dist/lib/matryoshka.d.ts.map +1 -0
  133. package/dist/lib/memory-lock.d.ts +58 -0
  134. package/dist/lib/memory-lock.d.ts.map +1 -0
  135. package/dist/lib/model-config.d.ts +14 -0
  136. package/dist/lib/model-config.d.ts.map +1 -0
  137. package/dist/lib/open-sessions-connector.d.ts +60 -0
  138. package/dist/lib/open-sessions-connector.d.ts.map +1 -0
  139. package/dist/lib/poisoning.d.ts +19 -0
  140. package/dist/lib/poisoning.d.ts.map +1 -0
  141. package/dist/lib/poll.d.ts +17 -0
  142. package/dist/lib/poll.d.ts.map +1 -0
  143. package/dist/lib/procedural-extractor.d.ts +21 -0
  144. package/dist/lib/procedural-extractor.d.ts.map +1 -0
  145. package/dist/lib/profile-sync.d.ts +20 -0
  146. package/dist/lib/profile-sync.d.ts.map +1 -0
  147. package/dist/lib/profile-synthesizer.d.ts +20 -0
  148. package/dist/lib/profile-synthesizer.d.ts.map +1 -0
  149. package/dist/lib/project-detect.d.ts +18 -0
  150. package/dist/lib/project-detect.d.ts.map +1 -0
  151. package/dist/lib/providers/anthropic.d.ts +21 -0
  152. package/dist/lib/providers/anthropic.d.ts.map +1 -0
  153. package/dist/lib/providers/base.d.ts +96 -0
  154. package/dist/lib/providers/base.d.ts.map +1 -0
  155. package/dist/lib/providers/cerebras.d.ts +20 -0
  156. package/dist/lib/providers/cerebras.d.ts.map +1 -0
  157. package/dist/lib/providers/grok.d.ts +19 -0
  158. package/dist/lib/providers/grok.d.ts.map +1 -0
  159. package/dist/lib/providers/index.d.ts +7 -0
  160. package/dist/lib/providers/index.d.ts.map +1 -0
  161. package/dist/lib/providers/openai-compat.d.ts +18 -0
  162. package/dist/lib/providers/openai-compat.d.ts.map +1 -0
  163. package/dist/lib/providers/openai.d.ts +20 -0
  164. package/dist/lib/providers/openai.d.ts.map +1 -0
  165. package/dist/lib/providers/registry.d.ts +38 -0
  166. package/dist/lib/providers/registry.d.ts.map +1 -0
  167. package/dist/lib/quality.d.ts +16 -0
  168. package/dist/lib/quality.d.ts.map +1 -0
  169. package/dist/lib/redact.d.ts +10 -0
  170. package/dist/lib/redact.d.ts.map +1 -0
  171. package/dist/lib/remote-sync.d.ts +49 -0
  172. package/dist/lib/remote-sync.d.ts.map +1 -0
  173. package/dist/lib/retention.d.ts +17 -0
  174. package/dist/lib/retention.d.ts.map +1 -0
  175. package/dist/lib/search.d.ts +52 -0
  176. package/dist/lib/search.d.ts.map +1 -0
  177. package/dist/lib/session-auto-resolve.d.ts +28 -0
  178. package/dist/lib/session-auto-resolve.d.ts.map +1 -0
  179. package/dist/lib/session-processor.d.ts +38 -0
  180. package/dist/lib/session-processor.d.ts.map +1 -0
  181. package/dist/lib/session-queue.d.ts +28 -0
  182. package/dist/lib/session-queue.d.ts.map +1 -0
  183. package/dist/lib/session-registry.d.ts +47 -0
  184. package/dist/lib/session-registry.d.ts.map +1 -0
  185. package/dist/lib/session-start-briefing.d.ts +10 -0
  186. package/dist/lib/session-start-briefing.d.ts.map +1 -0
  187. package/dist/lib/session-watcher.d.ts +30 -0
  188. package/dist/lib/session-watcher.d.ts.map +1 -0
  189. package/dist/lib/sync.d.ts +10 -0
  190. package/dist/lib/sync.d.ts.map +1 -0
  191. package/dist/lib/synthesis/corpus-builder.d.ts +30 -0
  192. package/dist/lib/synthesis/corpus-builder.d.ts.map +1 -0
  193. package/dist/lib/synthesis/executor.d.ts +14 -0
  194. package/dist/lib/synthesis/executor.d.ts.map +1 -0
  195. package/dist/lib/synthesis/index.d.ts +37 -0
  196. package/dist/lib/synthesis/index.d.ts.map +1 -0
  197. package/dist/lib/synthesis/llm-analyzer.d.ts +18 -0
  198. package/dist/lib/synthesis/llm-analyzer.d.ts.map +1 -0
  199. package/dist/lib/synthesis/metrics.d.ts +13 -0
  200. package/dist/lib/synthesis/metrics.d.ts.map +1 -0
  201. package/dist/lib/synthesis/scheduler.d.ts +18 -0
  202. package/dist/lib/synthesis/scheduler.d.ts.map +1 -0
  203. package/dist/lib/synthesis/validator.d.ts +19 -0
  204. package/dist/lib/synthesis/validator.d.ts.map +1 -0
  205. package/dist/lib/tool-lesson-extractor.d.ts +24 -0
  206. package/dist/lib/tool-lesson-extractor.d.ts.map +1 -0
  207. package/dist/lib/tool-memory-synthesizer.d.ts +28 -0
  208. package/dist/lib/tool-memory-synthesizer.d.ts.map +1 -0
  209. package/dist/lib/topic-clusterer.d.ts +21 -0
  210. package/dist/lib/topic-clusterer.d.ts.map +1 -0
  211. package/dist/lib/when-to-use-generator.d.ts +22 -0
  212. package/dist/lib/when-to-use-generator.d.ts.map +1 -0
  213. package/dist/mcp/index.d.ts +5 -0
  214. package/dist/mcp/index.d.ts.map +1 -0
  215. package/dist/mcp/index.js +30258 -0
  216. package/dist/mcp/memory-broadcast.d.ts +12 -0
  217. package/dist/mcp/memory-broadcast.d.ts.map +1 -0
  218. package/dist/sdk/index.js +397 -0
  219. package/dist/server/index.d.ts +7 -0
  220. package/dist/server/index.d.ts.map +1 -0
  221. package/dist/server/index.js +18250 -0
  222. package/dist/types/hooks.d.ts +136 -0
  223. package/dist/types/hooks.d.ts.map +1 -0
  224. package/dist/types/index.d.ts +345 -0
  225. package/dist/types/index.d.ts.map +1 -0
  226. package/package.json +53 -13
  227. package/dist/agent-setup.d.ts +0 -51
  228. package/dist/agent-setup.d.ts.map +0 -1
  229. package/dist/bridge.d.ts +0 -50
  230. package/dist/bridge.d.ts.map +0 -1
  231. package/dist/cli.d.ts +0 -3
  232. package/dist/cli.d.ts.map +0 -1
  233. package/dist/cli.js +0 -1362
  234. package/dist/mcp-runtime.d.ts +0 -24
  235. package/dist/mcp-runtime.d.ts.map +0 -1
  236. package/dist/mcp.d.ts +0 -3
  237. package/dist/mcp.d.ts.map +0 -1
  238. package/dist/mcp.js +0 -354
  239. package/dist/remote-client.d.ts +0 -49
  240. package/dist/remote-client.d.ts.map +0 -1
package/dist/cli.js DELETED
@@ -1,1362 +0,0 @@
1
- #!/usr/bin/env bun
2
- // @bun
3
-
4
- // src/agent-setup.ts
5
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
6
- import { homedir } from "os";
7
- import { dirname, join } from "path";
8
- var SUPPORTED_AGENT_HOSTS = ["claude-code", "codex", "opencode", "antigravity"];
9
- function buildAgentSetupPlan(options = {}) {
10
- const home = options.homeDir ?? homedir();
11
- const baseDir = join(home, ".config", "mementos", "agent-integrations");
12
- const hosts = uniqueHosts(options.hosts ?? [...SUPPORTED_AGENT_HOSTS]);
13
- const apiUrl = options.apiUrl ?? "https://mementos.md";
14
- const mcpCommand = "mementos-mcp";
15
- const hookCommand = "mementos hook stop";
16
- const injectCommand = "mementos inject";
17
- const files = [
18
- {
19
- path: join(baseDir, "mementos-instructions.md"),
20
- content: renderSharedInstructions({ apiUrl, injectCommand, mcpCommand })
21
- },
22
- {
23
- path: join(baseDir, "mementos-injection.md"),
24
- content: renderInjectionInstructions({ apiUrl, injectCommand })
25
- },
26
- {
27
- path: join(baseDir, "mementos-stop-hook.sh"),
28
- executable: true,
29
- content: renderHookScript(hookCommand)
30
- },
31
- {
32
- path: join(baseDir, "mcp-server.json"),
33
- content: `${JSON.stringify(renderMcpServerConfig({ apiUrl, mcpCommand }), null, 2)}
34
- `
35
- }
36
- ];
37
- for (const host of hosts) {
38
- files.push({
39
- path: join(baseDir, `${host}-instructions.md`),
40
- content: renderHostInstructions(host, { apiUrl, injectCommand, mcpCommand })
41
- });
42
- files.push({
43
- path: join(baseDir, `${host}.json`),
44
- content: `${JSON.stringify(renderHostConfig(host, { apiUrl, hookCommand, injectCommand, mcpCommand }), null, 2)}
45
- `
46
- });
47
- files.push({
48
- path: hostManagedConfigPath(home, host),
49
- content: `${JSON.stringify(renderManagedHostConfig(host, { apiUrl, baseDir, hookCommand, injectCommand, mcpCommand }), null, 2)}
50
- `
51
- });
52
- }
53
- return {
54
- baseDir,
55
- files,
56
- hostResults: hosts.map((host) => ({ host, path: hostNativeConfigPath(home, host), status: "planned" })),
57
- hosts,
58
- mcpCommand
59
- };
60
- }
61
- function diagnoseAgentSetup(options = {}) {
62
- const plan = installAgentSetup({ ...options, action: "verify" });
63
- const home = options.homeDir ?? homedir();
64
- const localStoragePath = join(home, ".hasna", "mementos");
65
- const localDatabaseFiles = [
66
- ...findLocalDatabaseFiles(localStoragePath),
67
- ...findLocalDatabaseFiles(join(home, ".config", "mementos"))
68
- ];
69
- const env = options.env ?? process.env;
70
- const tokenSource = ["MEMENTOS_API_TOKEN", "MEMENTOS_PLATFORM_TOKEN", "PLATFORM_API_TOKEN", "APP_API_KEY"].find((key) => Boolean(env[key]));
71
- return {
72
- apiUrl: options.apiUrl ?? "https://mementos.md",
73
- hostResults: plan.hostResults,
74
- localDatabaseFiles,
75
- localStorage: {
76
- path: localStoragePath,
77
- status: existsSync(localStoragePath) ? "present" : "absent"
78
- },
79
- ok: plan.hostResults.every((result) => result.status === "present") && !existsSync(localStoragePath) && localDatabaseFiles.length === 0,
80
- remoteOnly: true,
81
- tokenConfigured: Boolean(tokenSource),
82
- ...tokenSource ? { tokenSource } : {}
83
- };
84
- }
85
- function installAgentSetup(options = {}) {
86
- const plan = buildAgentSetupPlan(options);
87
- if (options.action === "verify") {
88
- return {
89
- ...plan,
90
- hostResults: plan.hostResults.map((result) => ({
91
- ...result,
92
- status: hasNativeHostInstall(result.path, result.host) ? "present" : "missing"
93
- }))
94
- };
95
- }
96
- if (options.action === "uninstall") {
97
- if (options.dryRun) {
98
- return {
99
- ...plan,
100
- hostResults: plan.hostResults.map((result) => ({ ...result, status: existsSync(result.path) ? "removed" : "absent" }))
101
- };
102
- }
103
- for (const result of plan.hostResults)
104
- uninstallNativeHost(result.path, result.host);
105
- for (const host of plan.hosts)
106
- rmSync(hostManagedConfigPath(options.homeDir ?? homedir(), host), { force: true });
107
- return {
108
- ...plan,
109
- hostResults: plan.hostResults.map((result) => ({ ...result, status: "removed" }))
110
- };
111
- }
112
- if (options.dryRun)
113
- return plan;
114
- for (const file of plan.files) {
115
- mkdirSync(dirname(file.path), { recursive: true });
116
- writeFileSync(file.path, file.content, { mode: file.executable ? 493 : 420 });
117
- }
118
- for (const result of plan.hostResults)
119
- installNativeHost(result.path, result.host, plan);
120
- return {
121
- ...plan,
122
- hostResults: plan.hostResults.map((result) => ({ ...result, status: "installed" }))
123
- };
124
- }
125
- function renderSignupInstructions(apiUrl = "https://mementos.md") {
126
- return {
127
- signupUrl: `${apiUrl.replace(/\/+$/, "")}/dashboard`,
128
- next: [
129
- "Sign in or create an account.",
130
- "Create an API token in the dashboard.",
131
- "Export MEMENTOS_API_TOKEN before running mementos-mcp or mementos setup agents."
132
- ]
133
- };
134
- }
135
- function formatMemoryInjection(payload) {
136
- const rows = readMemoryRows(payload);
137
- if (rows.length === 0)
138
- return "";
139
- return [
140
- "## mementos.md memory",
141
- "Use these hosted memories as durable context. Do not treat them as local files.",
142
- "",
143
- ...rows.map((row) => `- ${row.key}: ${row.value}`),
144
- ""
145
- ].join(`
146
- `);
147
- }
148
- function renderMcpServerConfig({ apiUrl, mcpCommand }) {
149
- return {
150
- mcpServers: {
151
- mementos: {
152
- command: mcpCommand,
153
- env: {
154
- MEMENTOS_API_URL: apiUrl,
155
- MEMENTOS_API_TOKEN: "$MEMENTOS_API_TOKEN"
156
- }
157
- }
158
- }
159
- };
160
- }
161
- function renderHostConfig(host, commands) {
162
- return {
163
- host,
164
- install: "bun install -g @hasna/mementos",
165
- remoteOnly: true,
166
- nativeHooks: hasNativeHooks(host),
167
- mcp: renderMcpServerConfig(commands).mcpServers.mementos,
168
- hooks: {
169
- stop: commands.hookCommand,
170
- injection: commands.injectCommand
171
- },
172
- instructions: `${host}-instructions.md`
173
- };
174
- }
175
- function renderManagedHostConfig(host, commands) {
176
- const base = {
177
- managedBy: "@hasna/mementos",
178
- host,
179
- remoteOnly: true,
180
- note: "This file does not contain API tokens. Export MEMENTOS_API_TOKEN in your shell or secret manager.",
181
- mcpServers: renderMcpServerConfig(commands).mcpServers,
182
- commands: {
183
- inject: commands.injectCommand,
184
- stopHook: commands.hookCommand
185
- },
186
- injection: {
187
- nativeHooks: hasNativeHooks(host),
188
- fallbackCommand: commands.injectCommand,
189
- mcpTools: ["memory.search", "memory.save", "memory.get", "memory.update", "memory.delete", "memory.usage"]
190
- },
191
- files: {
192
- integrationDir: commands.baseDir,
193
- sharedInstructions: join(commands.baseDir, "mementos-instructions.md"),
194
- hostInstructions: join(commands.baseDir, `${host}-instructions.md`),
195
- injectionInstructions: join(commands.baseDir, "mementos-injection.md"),
196
- stopHook: join(commands.baseDir, "mementos-stop-hook.sh")
197
- }
198
- };
199
- if (host === "claude-code") {
200
- return {
201
- ...base,
202
- hooks: {
203
- UserPromptSubmit: [{ matcher: "", hooks: [{ type: "command", command: "mementos hook user-prompt-submit" }] }],
204
- SessionStart: [{ matcher: "", hooks: [{ type: "command", command: "mementos hook session-start" }] }],
205
- Stop: [{ matcher: "", hooks: [{ type: "command", command: commands.hookCommand }] }]
206
- }
207
- };
208
- }
209
- if (host === "codex") {
210
- return {
211
- ...base,
212
- mcp_servers: {
213
- mementos: {
214
- command: commands.mcpCommand,
215
- env: { MEMENTOS_API_URL: commands.apiUrl, MEMENTOS_API_TOKEN: "$MEMENTOS_API_TOKEN" }
216
- }
217
- }
218
- };
219
- }
220
- return base;
221
- }
222
- function hostManagedConfigPath(home, host) {
223
- switch (host) {
224
- case "claude-code":
225
- return join(home, ".claude", "mementos.managed.json");
226
- case "codex":
227
- return join(home, ".codex", "mementos.managed.json");
228
- case "opencode":
229
- return join(home, ".config", "opencode", "mementos.managed.json");
230
- case "antigravity":
231
- return join(home, ".config", "antigravity", "mementos.managed.json");
232
- }
233
- }
234
- function hostNativeConfigPath(home, host) {
235
- switch (host) {
236
- case "claude-code":
237
- return join(home, ".claude", "settings.json");
238
- case "codex":
239
- return join(home, ".codex", "config.toml");
240
- case "opencode":
241
- return join(home, ".config", "opencode", "opencode.json");
242
- case "antigravity":
243
- return join(home, ".antigravity", "mcp.json");
244
- }
245
- }
246
- function installNativeHost(path, host, plan) {
247
- mkdirSync(dirname(path), { recursive: true });
248
- if (host === "codex") {
249
- writeFileSync(path, installCodexToml(readTextFile(path), plan), { mode: 420 });
250
- return;
251
- }
252
- const config = readJsonObject(path);
253
- if (host === "claude-code") {
254
- writeJsonObject(path, installClaudeConfig(config, plan));
255
- return;
256
- }
257
- if (host === "opencode") {
258
- writeJsonObject(path, installOpenCodeConfig(config, plan));
259
- return;
260
- }
261
- writeJsonObject(path, installAntigravityConfig(config, plan));
262
- }
263
- function uninstallNativeHost(path, host) {
264
- if (!existsSync(path))
265
- return;
266
- if (host === "codex") {
267
- writeFileSync(path, uninstallCodexToml(readTextFile(path)), { mode: 420 });
268
- return;
269
- }
270
- const config = readJsonObject(path);
271
- if (host === "claude-code")
272
- writeJsonObject(path, uninstallClaudeConfig(config));
273
- if (host === "opencode")
274
- writeJsonObject(path, uninstallOpenCodeConfig(config));
275
- if (host === "antigravity")
276
- writeJsonObject(path, uninstallMcpServersConfig(config));
277
- }
278
- function hasNativeHostInstall(path, host) {
279
- if (!existsSync(path))
280
- return false;
281
- if (host === "codex")
282
- return /\[mcp_servers\.mementos\]/.test(readTextFile(path));
283
- const config = readJsonObject(path);
284
- if (host === "opencode")
285
- return isRecord(config.mcp) && isRecord(config.mcp.mementos);
286
- return isRecord(config.mcpServers) && isRecord(config.mcpServers.mementos);
287
- }
288
- function installClaudeConfig(config, plan) {
289
- return {
290
- ...config,
291
- mcpServers: {
292
- ...recordValue(config.mcpServers),
293
- mementos: stdioMcpServer(plan, true)
294
- },
295
- hooks: mergeAgentHooks(recordValue(config.hooks), {
296
- UserPromptSubmit: "mementos hook user-prompt-submit",
297
- SessionStart: "mementos hook session-start",
298
- Stop: "mementos hook stop"
299
- })
300
- };
301
- }
302
- function uninstallClaudeConfig(config) {
303
- return {
304
- ...config,
305
- mcpServers: omitKey(recordValue(config.mcpServers), "mementos"),
306
- hooks: removeAgentHooks(recordValue(config.hooks), [
307
- "mementos hook user-prompt-submit",
308
- "mementos hook session-start",
309
- "mementos hook stop"
310
- ])
311
- };
312
- }
313
- function installOpenCodeConfig(config, plan) {
314
- return {
315
- ...config,
316
- mcp: {
317
- ...recordValue(config.mcp),
318
- mementos: {
319
- type: "local",
320
- command: [plan.mcpCommand],
321
- enabled: true,
322
- environment: remoteEnv(baseApiUrlFromPlan(plan))
323
- }
324
- }
325
- };
326
- }
327
- function uninstallOpenCodeConfig(config) {
328
- return {
329
- ...config,
330
- mcp: omitKey(recordValue(config.mcp), "mementos")
331
- };
332
- }
333
- function installAntigravityConfig(config, plan) {
334
- return {
335
- ...config,
336
- mcpServers: {
337
- ...recordValue(config.mcpServers),
338
- mementos: stdioMcpServer(plan, false)
339
- }
340
- };
341
- }
342
- function uninstallMcpServersConfig(config) {
343
- return {
344
- ...config,
345
- mcpServers: omitKey(recordValue(config.mcpServers), "mementos")
346
- };
347
- }
348
- function installCodexToml(current, plan) {
349
- const withoutExisting = removeTomlTable(removeManagedCodexBlock(current), "mcp_servers.mementos");
350
- const block = [
351
- "# BEGIN @hasna/mementos",
352
- "[mcp_servers.mementos]",
353
- `command = ${JSON.stringify(plan.mcpCommand)}`,
354
- "[mcp_servers.mementos.env]",
355
- `MEMENTOS_API_URL = ${JSON.stringify(baseApiUrlFromPlan(plan))}`,
356
- 'MEMENTOS_API_TOKEN = "$MEMENTOS_API_TOKEN"',
357
- "# END @hasna/mementos",
358
- ""
359
- ].join(`
360
- `);
361
- return `${withoutExisting.trimEnd()}
362
-
363
- ${block}`;
364
- }
365
- function uninstallCodexToml(current) {
366
- return `${removeTomlTable(removeManagedCodexBlock(current), "mcp_servers.mementos").trimEnd()}
367
- `;
368
- }
369
- function mergeAgentHooks(hooks, commands) {
370
- const next = { ...hooks };
371
- for (const [event, command] of Object.entries(commands)) {
372
- const current = next[event];
373
- const entries = Array.isArray(current) ? [...current] : [];
374
- if (!entries.some((entry) => JSON.stringify(entry).includes(command))) {
375
- entries.push({ matcher: "", hooks: [{ type: "command", command }] });
376
- }
377
- next[event] = entries;
378
- }
379
- return next;
380
- }
381
- function removeAgentHooks(hooks, commands) {
382
- const next = { ...hooks };
383
- for (const [event, entries] of Object.entries(next)) {
384
- if (!Array.isArray(entries))
385
- continue;
386
- next[event] = entries.filter((entry) => !commands.some((command) => JSON.stringify(entry).includes(command)));
387
- }
388
- return next;
389
- }
390
- function stdioMcpServer(plan, includeType) {
391
- return {
392
- ...includeType ? { type: "stdio" } : {},
393
- command: plan.mcpCommand,
394
- env: remoteEnv(baseApiUrlFromPlan(plan))
395
- };
396
- }
397
- function remoteEnv(apiUrl) {
398
- return {
399
- MEMENTOS_API_URL: apiUrl,
400
- MEMENTOS_API_TOKEN: "$MEMENTOS_API_TOKEN"
401
- };
402
- }
403
- function baseApiUrlFromPlan(plan) {
404
- const mcpFile = plan.files.find((file) => file.path.endsWith("mcp-server.json"));
405
- if (!mcpFile)
406
- return "https://mementos.md";
407
- try {
408
- const parsed = JSON.parse(mcpFile.content);
409
- return parsed.mcpServers?.mementos?.env?.MEMENTOS_API_URL ?? "https://mementos.md";
410
- } catch {
411
- return "https://mementos.md";
412
- }
413
- }
414
- function removeManagedCodexBlock(value) {
415
- return value.replace(/\n?# BEGIN @hasna\/mementos\n[\s\S]*?# END @hasna\/mementos\n?/g, `
416
- `);
417
- }
418
- function removeTomlTable(value, table) {
419
- const tablePattern = table.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
420
- const lines = value.split(`
421
- `);
422
- const output = [];
423
- let skipping = false;
424
- for (const line of lines) {
425
- if (new RegExp(`^\\[${tablePattern}(?:\\.|\\])`).test(line.trim())) {
426
- skipping = true;
427
- continue;
428
- }
429
- if (skipping && /^\[[^\]]+\]/.test(line.trim()))
430
- skipping = false;
431
- if (!skipping)
432
- output.push(line);
433
- }
434
- return output.join(`
435
- `);
436
- }
437
- function readTextFile(path) {
438
- return existsSync(path) ? readFileSync(path, "utf8") : "";
439
- }
440
- function readJsonObject(path) {
441
- if (!existsSync(path))
442
- return {};
443
- const parsed = JSON.parse(readFileSync(path, "utf8"));
444
- if (!isRecord(parsed))
445
- throw new Error(`${path} must contain a JSON object`);
446
- return parsed;
447
- }
448
- function writeJsonObject(path, value) {
449
- mkdirSync(dirname(path), { recursive: true });
450
- writeFileSync(path, `${JSON.stringify(value, null, 2)}
451
- `, { mode: 420 });
452
- }
453
- function recordValue(value) {
454
- return isRecord(value) ? value : {};
455
- }
456
- function isRecord(value) {
457
- return typeof value === "object" && value !== null && !Array.isArray(value);
458
- }
459
- function omitKey(record, key) {
460
- const { [key]: _removed, ...rest } = record;
461
- return rest;
462
- }
463
- function renderInjectionInstructions({ apiUrl, injectCommand }) {
464
- return `# mementos.md Memory Injection
465
-
466
- Run this before or after an agent turn when it needs durable project context:
467
-
468
- \`\`\`bash
469
- MEMENTOS_API_URL=${apiUrl} MEMENTOS_API_TOKEN=$MEMENTOS_API_TOKEN ${injectCommand} "$MEMENTOS_INJECTION_QUERY"
470
- \`\`\`
471
-
472
- This command reads hosted memory through the mementos.md API and prints a prompt-ready block.
473
- `;
474
- }
475
- function renderSharedInstructions({
476
- apiUrl,
477
- injectCommand,
478
- mcpCommand
479
- }) {
480
- return `# mementos.md Agent Instructions
481
-
482
- Use mementos.md as hosted durable memory for this agent host.
483
-
484
- - The MCP server command is \`${mcpCommand}\`.
485
- - The hosted API is \`${apiUrl}\`.
486
- - Use \`memory.search\` before non-trivial project work.
487
- - Use \`memory.save\` for durable decisions, root causes, and reusable context.
488
- - If the host does not provide a native hook, run \`${injectCommand} "<current task or question>"\` and include the returned hosted context in the next prompt.
489
- - Do not create or depend on a local memory database for the SaaS package.
490
- `;
491
- }
492
- function renderHostInstructions(host, commands) {
493
- const common = renderSharedInstructions(commands);
494
- if (host === "claude-code") {
495
- return `${common}
496
- ## Claude Code
497
-
498
- Claude Code has native mementos hooks installed by \`mementos setup agents\`.
499
- Use the mementos MCP server for explicit memory operations and let the
500
- \`UserPromptSubmit\`, \`SessionStart\`, and \`Stop\` hooks inject hosted context.
501
- `;
502
- }
503
- if (host === "codex") {
504
- return `${common}
505
- ## Codex
506
-
507
- Codex does not have a mementos native hook in its config file. Use the
508
- mementos MCP server for durable memory and run \`${commands.injectCommand}\`
509
- when you need prompt-ready hosted context before a turn.
510
- `;
511
- }
512
- if (host === "opencode") {
513
- return `${common}
514
- ## OpenCode
515
-
516
- Use the mementos MCP server for durable memory. When OpenCode cannot invoke a
517
- native lifecycle hook, run \`${commands.injectCommand}\` with the active task
518
- or error text and paste the hosted context into the next prompt.
519
- `;
520
- }
521
- return `${common}
522
- ## Antigravity
523
-
524
- Use the mementos MCP server for durable memory. When Antigravity cannot invoke
525
- a native lifecycle hook, run \`${commands.injectCommand}\` with the active task
526
- or error text and include the hosted context before continuing.
527
- `;
528
- }
529
- function hasNativeHooks(host) {
530
- return host === "claude-code";
531
- }
532
- function renderHookScript(command) {
533
- return `#!/usr/bin/env bash
534
- set -euo pipefail
535
-
536
- ${command}
537
- `;
538
- }
539
- function uniqueHosts(hosts) {
540
- const allowed = new Set(SUPPORTED_AGENT_HOSTS);
541
- return [...new Set(hosts)].filter((host) => allowed.has(host));
542
- }
543
- function readMemoryRows(payload) {
544
- if (typeof payload !== "object" || payload === null)
545
- return [];
546
- const maybeRows = "memories" in payload ? payload.memories : payload;
547
- if (!Array.isArray(maybeRows))
548
- return [];
549
- return maybeRows.flatMap((row) => {
550
- if (typeof row !== "object" || row === null)
551
- return [];
552
- const key = readText(row.key);
553
- const value = readText(row.value ?? row.content);
554
- return key && value ? [{ key, value }] : [];
555
- });
556
- }
557
- function readText(input) {
558
- return typeof input === "string" && input.trim() ? input.trim() : undefined;
559
- }
560
- function findLocalDatabaseFiles(dir) {
561
- const localDbPattern = new RegExp(`\\.(${["db", "sql" + "ite"].join("|")})(?:$|-)`);
562
- if (!existsSync(dir))
563
- return [];
564
- const stat = statSync(dir);
565
- if (!stat.isDirectory())
566
- return localDbPattern.test(dir) ? [dir] : [];
567
- return readdirSync(dir).flatMap((entry) => {
568
- const path = join(dir, entry);
569
- const entryStat = statSync(path);
570
- if (entryStat.isDirectory())
571
- return findLocalDatabaseFiles(path);
572
- return localDbPattern.test(entry) ? [path] : [];
573
- });
574
- }
575
-
576
- // src/bridge.ts
577
- import { spawn } from "child_process";
578
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
579
- import { homedir as homedir2 } from "os";
580
- import { dirname as dirname2, join as join2 } from "path";
581
- function buildBridgePaths(homeDir = homedir2(), platform = process.platform) {
582
- const baseDir = join2(homeDir, ".config", "mementos", "bridge");
583
- return {
584
- baseDir,
585
- logFile: join2(baseDir, "bridge.log"),
586
- pidFile: join2(baseDir, "bridge.pid.json"),
587
- serviceFile: platform === "darwin" ? join2(homeDir, "Library", "LaunchAgents", "md.mementos.bridge.plist") : join2(homeDir, ".config", "systemd", "user", "mementos-bridge.service")
588
- };
589
- }
590
- function getBridgeStatus(options = {}) {
591
- const paths = buildBridgePaths(options.homeDir, options.platform);
592
- const pidFile = readBridgePid(paths.pidFile);
593
- if (pidFile && !isRunning(pidFile.pid)) {
594
- rmSync2(paths.pidFile, { force: true });
595
- return baseStatus(paths, options, false);
596
- }
597
- return {
598
- ...baseStatus(paths, options, Boolean(pidFile)),
599
- ...pidFile?.agentName ? { agentName: pidFile.agentName } : {},
600
- ...pidFile ? { pid: pidFile.pid, startedAt: pidFile.startedAt } : {}
601
- };
602
- }
603
- function startBridge(options = {}) {
604
- const current = getBridgeStatus(options);
605
- if (current.running)
606
- return current;
607
- const paths = buildBridgePaths(options.homeDir, options.platform);
608
- mkdirSync2(paths.baseDir, { recursive: true });
609
- const out = openSync(paths.logFile, "a");
610
- const err = openSync(paths.logFile, "a");
611
- const command = options.command ?? [process.execPath, process.argv[1] ?? "mementos", "bridge", "run"];
612
- const child = spawn(command[0] ?? "mementos", command.slice(1), {
613
- detached: true,
614
- env: {
615
- ...process.env,
616
- ...options.agentName ? { MEMENTOS_AGENT_NAME: options.agentName } : {},
617
- ...options.apiUrl ? { MEMENTOS_API_URL: options.apiUrl } : {},
618
- ...options.homeDir ? { MEMENTOS_BRIDGE_HOME: options.homeDir } : {},
619
- ...options.intervalMs ? { MEMENTOS_BRIDGE_INTERVAL_MS: String(options.intervalMs) } : {}
620
- },
621
- stdio: ["ignore", out, err]
622
- });
623
- child.unref();
624
- writeBridgePid({
625
- agentName: options.agentName,
626
- apiUrl: options.apiUrl,
627
- homeDir: options.homeDir,
628
- pid: child.pid ?? 0,
629
- startedAt: new Date().toISOString()
630
- });
631
- return getBridgeStatus(options);
632
- }
633
- function stopBridge(options = {}) {
634
- const paths = buildBridgePaths(options.homeDir, options.platform);
635
- const pidFile = readBridgePid(paths.pidFile);
636
- if (!pidFile)
637
- return { path: paths.pidFile, remoteOnly: true, status: "absent" };
638
- if (isRunning(pidFile.pid))
639
- process.kill(pidFile.pid, "SIGTERM");
640
- clearBridgePid(options);
641
- return { path: paths.pidFile, remoteOnly: true, status: "removed" };
642
- }
643
- function installBridgeService(options = {}) {
644
- const platform = options.platform ?? process.platform;
645
- const paths = buildBridgePaths(options.homeDir, platform);
646
- mkdirSync2(dirname2(paths.serviceFile), { recursive: true });
647
- const command = options.command ?? "mementos bridge run";
648
- const apiUrl = options.apiUrl ?? "https://mementos.md/api/v1";
649
- const content = platform === "darwin" ? renderLaunchdService(command, apiUrl, options.agentName) : renderSystemdService(command, apiUrl, options.agentName);
650
- writeFileSync2(paths.serviceFile, content, { mode: 420 });
651
- return { path: paths.serviceFile, remoteOnly: true, status: "installed" };
652
- }
653
- function uninstallBridgeService(options = {}) {
654
- const paths = buildBridgePaths(options.homeDir, options.platform);
655
- if (!existsSync2(paths.serviceFile))
656
- return { path: paths.serviceFile, remoteOnly: true, status: "absent" };
657
- rmSync2(paths.serviceFile, { force: true });
658
- return { path: paths.serviceFile, remoteOnly: true, status: "removed" };
659
- }
660
- function readBridgeLogs(options = {}) {
661
- const paths = buildBridgePaths(options.homeDir, options.platform);
662
- if (!existsSync2(paths.logFile))
663
- return [];
664
- return readFileSync2(paths.logFile, "utf8").split(`
665
- `).filter(Boolean).slice(-(options.lines ?? 40));
666
- }
667
- function writeBridgePid(options) {
668
- const paths = buildBridgePaths(options.homeDir, options.platform);
669
- mkdirSync2(paths.baseDir, { recursive: true });
670
- writeFileSync2(paths.pidFile, `${JSON.stringify({
671
- ...options.agentName ? { agentName: options.agentName } : {},
672
- ...options.apiUrl ? { apiUrl: options.apiUrl } : {},
673
- pid: options.pid,
674
- startedAt: options.startedAt
675
- }, null, 2)}
676
- `, { mode: 420 });
677
- }
678
- function clearBridgePid(options = {}) {
679
- rmSync2(buildBridgePaths(options.homeDir, options.platform).pidFile, { force: true });
680
- }
681
- function baseStatus(paths, options, running) {
682
- return {
683
- ...options.agentName ? { agentName: options.agentName } : {},
684
- apiUrl: options.apiUrl ?? "https://mementos.md/api/v1",
685
- localDatabase: false,
686
- logFile: paths.logFile,
687
- pidFile: paths.pidFile,
688
- remoteOnly: true,
689
- running
690
- };
691
- }
692
- function readBridgePid(path) {
693
- if (!existsSync2(path))
694
- return;
695
- try {
696
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
697
- return Number.isInteger(parsed.pid) && parsed.pid > 0 ? parsed : undefined;
698
- } catch {
699
- return;
700
- }
701
- }
702
- function isRunning(pid) {
703
- try {
704
- process.kill(pid, 0);
705
- return true;
706
- } catch {
707
- return false;
708
- }
709
- }
710
- function renderSystemdService(command, apiUrl, agentName) {
711
- return `[Unit]
712
- Description=mementos.md remote bridge
713
-
714
- [Service]
715
- Type=simple
716
- Environment=MEMENTOS_API_URL=${apiUrl}
717
- ${agentName ? `Environment=MEMENTOS_AGENT_NAME=${agentName}
718
- ` : ""}ExecStart=${command}
719
- Restart=on-failure
720
-
721
- [Install]
722
- WantedBy=default.target
723
- `;
724
- }
725
- function renderLaunchdService(command, apiUrl, agentName) {
726
- const args = command.split(/\s+/).map((part) => ` <string>${escapeXml(part)}</string>`).join(`
727
- `);
728
- return `<?xml version="1.0" encoding="UTF-8"?>
729
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
730
- <plist version="1.0">
731
- <dict>
732
- <key>Label</key>
733
- <string>md.mementos.bridge</string>
734
- <key>ProgramArguments</key>
735
- <array>
736
- ${args}
737
- </array>
738
- <key>EnvironmentVariables</key>
739
- <dict>
740
- <key>MEMENTOS_API_URL</key>
741
- <string>${escapeXml(apiUrl)}</string>
742
- ${agentName ? ` <key>MEMENTOS_AGENT_NAME</key>
743
- <string>${escapeXml(agentName)}</string>
744
- ` : ""} </dict>
745
- <key>RunAtLoad</key>
746
- <true/>
747
- </dict>
748
- </plist>
749
- `;
750
- }
751
- function escapeXml(value) {
752
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
753
- }
754
-
755
- // src/remote-client.ts
756
- var DEFAULT_API_URL = "https://mementos.md/api/v1";
757
-
758
- class MementosRemoteError extends Error {
759
- status;
760
- response;
761
- constructor(message, status, response) {
762
- super(message);
763
- this.name = "MementosRemoteError";
764
- this.status = status;
765
- this.response = response;
766
- }
767
- }
768
-
769
- class MementosRemoteClient {
770
- apiUrl;
771
- token;
772
- fetchImpl;
773
- constructor(config = {}) {
774
- this.apiUrl = normalizeApiUrl(config.apiUrl ?? DEFAULT_API_URL);
775
- this.token = config.token;
776
- this.fetchImpl = config.fetch ?? fetch;
777
- }
778
- listCapabilities() {
779
- return this.request("/capabilities", { requiresAuth: false });
780
- }
781
- listAgents() {
782
- return this.request("/agents");
783
- }
784
- registerAgent(input) {
785
- return this.request("/agents/register", { method: "POST", body: input });
786
- }
787
- heartbeatAgent(input) {
788
- return this.request("/agents/heartbeat", { method: "POST", body: input });
789
- }
790
- saveMemory(input) {
791
- return this.request("/memories", { method: "POST", body: input });
792
- }
793
- searchMemories(input = {}) {
794
- return this.request("/memories", { query: input });
795
- }
796
- injectionContext(input = {}) {
797
- return this.request("/injections/context", { method: "POST", body: input });
798
- }
799
- sendInboxMessage(input) {
800
- return this.request("/inbox", { method: "POST", body: input });
801
- }
802
- listInboxMessages(input = {}) {
803
- return this.request("/inbox", { query: input });
804
- }
805
- claimInboxMessage(id, input = {}) {
806
- return this.request(`/inbox/${encodeURIComponent(id)}/claim`, { method: "POST", body: input });
807
- }
808
- getMemory(id) {
809
- return this.request(`/memories/${encodeURIComponent(id)}`);
810
- }
811
- updateMemory(id, input) {
812
- return this.request(`/memories/${encodeURIComponent(id)}`, { method: "PATCH", body: input });
813
- }
814
- deleteMemory(id) {
815
- return this.request(`/memories/${encodeURIComponent(id)}`, { method: "DELETE" });
816
- }
817
- memoryUsage() {
818
- return this.request("/usage");
819
- }
820
- listRuns(input = {}) {
821
- return this.request("/runs", { query: input });
822
- }
823
- createRun(slug, input = {}) {
824
- return this.request(`/runs/${encodeURIComponent(slug)}`, { method: "POST", body: input });
825
- }
826
- getRun(id) {
827
- return this.request(`/runs/${encodeURIComponent(id)}`);
828
- }
829
- listApprovals() {
830
- return this.request("/approvals");
831
- }
832
- billingStatus() {
833
- return this.request("/billing/status");
834
- }
835
- listCreditPacks() {
836
- return this.request("/billing/credits");
837
- }
838
- buyCredits(input) {
839
- return this.request("/billing/credits", { method: "POST", body: input });
840
- }
841
- async request(path, options = {}) {
842
- const method = options.method ?? "GET";
843
- const requiresAuth = options.requiresAuth !== false;
844
- if (requiresAuth && !this.token) {
845
- throw new MementosRemoteError("MEMENTOS_API_TOKEN is required for hosted mementos.md requests", 401, null);
846
- }
847
- const url = new URL(`${this.apiUrl}${path.startsWith("/") ? path : `/${path}`}`);
848
- for (const [key, value] of Object.entries(options.query ?? {})) {
849
- if (value === undefined || value === null || value === "")
850
- continue;
851
- url.searchParams.set(key, String(value));
852
- }
853
- const headers = new Headers({ accept: "application/json" });
854
- if (requiresAuth)
855
- headers.set("authorization", `Bearer ${this.token}`);
856
- if (options.body !== undefined)
857
- headers.set("content-type", "application/json");
858
- const response = await this.fetchImpl(url, {
859
- method,
860
- headers,
861
- body: options.body === undefined ? undefined : JSON.stringify(options.body)
862
- });
863
- const payload = await readPayload(response);
864
- if (!response.ok) {
865
- throw new MementosRemoteError(readErrorMessage(payload, response.status), response.status, payload);
866
- }
867
- return payload;
868
- }
869
- }
870
- function createRemoteClientFromEnv(env = process.env) {
871
- return new MementosRemoteClient({
872
- apiUrl: env.MEMENTOS_API_URL,
873
- token: env.MEMENTOS_API_TOKEN ?? env.MEMENTOS_PLATFORM_TOKEN ?? env.PLATFORM_API_TOKEN ?? env.APP_API_KEY
874
- });
875
- }
876
- function normalizeApiUrl(input) {
877
- const trimmed = input.trim().replace(/\/+$/, "");
878
- if (!trimmed)
879
- return DEFAULT_API_URL;
880
- return trimmed.endsWith("/api/v1") ? trimmed : `${trimmed}/api/v1`;
881
- }
882
- async function readPayload(response) {
883
- const text = await response.text();
884
- if (!text)
885
- return null;
886
- try {
887
- return JSON.parse(text);
888
- } catch {
889
- return text;
890
- }
891
- }
892
- function readErrorMessage(payload, status) {
893
- if (typeof payload === "object" && payload !== null && "error" in payload) {
894
- const value = payload.error;
895
- if (typeof value === "string" && value.trim())
896
- return value;
897
- }
898
- return `mementos.md request failed with status ${status}`;
899
- }
900
-
901
- // src/cli.ts
902
- var command = process.argv[2] ?? "help";
903
- var client = createRemoteClientFromEnv();
904
- try {
905
- const result = await run(command, process.argv.slice(3));
906
- if (result !== undefined) {
907
- process.stdout.write(`${JSON.stringify(result, null, 2)}
908
- `);
909
- }
910
- } catch (error) {
911
- if (error instanceof MementosRemoteError) {
912
- process.stderr.write(`${error.message}
913
- `);
914
- process.exit(error.status === 401 || error.status === 403 ? 2 : 1);
915
- }
916
- process.stderr.write(`${error.message}
917
- `);
918
- process.exit(1);
919
- }
920
- async function run(name, args) {
921
- switch (name) {
922
- case "help":
923
- case "--help":
924
- case "-h":
925
- process.stdout.write(helpText());
926
- return;
927
- case "auth":
928
- return runAuth(args);
929
- case "setup":
930
- return runSetup(args);
931
- case "inject":
932
- return injectMemory(args);
933
- case "hook":
934
- return runHook(args);
935
- case "bridge":
936
- return runBridge(args);
937
- case "billing":
938
- return runBilling(args);
939
- case "inbox":
940
- return runInbox(args);
941
- case "capabilities":
942
- return client.listCapabilities();
943
- case "agents":
944
- return client.listAgents();
945
- case "usage":
946
- return client.memoryUsage();
947
- case "search":
948
- return client.searchMemories({ q: args.join(" ").trim() });
949
- case "save": {
950
- const key = args[0];
951
- const value = args.slice(1).join(" ");
952
- if (!key || !value)
953
- throw new Error("usage: mementos save <key> <value>");
954
- return client.saveMemory({ key, value });
955
- }
956
- default:
957
- throw new Error(`Unknown command: ${name}
958
-
959
- ${helpText()}`);
960
- }
961
- }
962
- async function runBridge(args) {
963
- const subcommand = args[0] ?? "status";
964
- const options = parseBridgeOptions(args.slice(1));
965
- const bridgeOptions = {
966
- agentName: options.agentName ?? process.env.MEMENTOS_AGENT_NAME,
967
- apiUrl: baseApiUrl().endsWith("/api/v1") ? baseApiUrl() : `${baseApiUrl()}/api/v1`,
968
- homeDir: options.homeDir ?? process.env.MEMENTOS_BRIDGE_HOME,
969
- intervalMs: options.intervalMs ?? readPositiveInteger(process.env.MEMENTOS_BRIDGE_INTERVAL_MS)
970
- };
971
- switch (subcommand) {
972
- case "start":
973
- return startBridge(bridgeOptions);
974
- case "status":
975
- return getBridgeStatus(bridgeOptions);
976
- case "stop":
977
- return stopBridge(bridgeOptions);
978
- case "install":
979
- return installBridgeService({
980
- ...bridgeOptions,
981
- command: "mementos bridge run"
982
- });
983
- case "uninstall":
984
- return uninstallBridgeService(bridgeOptions);
985
- case "logs":
986
- return { lines: readBridgeLogs({ ...bridgeOptions, lines: options.lines }) };
987
- case "run":
988
- return runBridgeDaemon(bridgeOptions, Boolean(options.once));
989
- default:
990
- throw new Error("usage: mementos bridge <start|status|stop|install|uninstall|logs|run>");
991
- }
992
- }
993
- async function runInbox(args) {
994
- const subcommand = args[0] ?? "list";
995
- switch (subcommand) {
996
- case "send": {
997
- const agentName = args[1];
998
- const message = args.slice(2).join(" ");
999
- if (!agentName || !message)
1000
- throw new Error("usage: mementos inbox send <agentName> <message>");
1001
- return client.sendInboxMessage({ targetAgentName: agentName, message });
1002
- }
1003
- case "list": {
1004
- const options = parseInboxOptions(args.slice(1));
1005
- return client.listInboxMessages(options);
1006
- }
1007
- case "claim": {
1008
- const id = args[1];
1009
- if (!id)
1010
- throw new Error("usage: mementos inbox claim <id> [--agent <agentName>]");
1011
- const options = parseInboxOptions(args.slice(2));
1012
- return client.claimInboxMessage(id, options);
1013
- }
1014
- default:
1015
- throw new Error("usage: mementos inbox <send|list|claim>");
1016
- }
1017
- }
1018
- async function runBridgeDaemon(options, once) {
1019
- writeBridgePid({
1020
- agentName: options.agentName,
1021
- apiUrl: options.apiUrl,
1022
- homeDir: options.homeDir,
1023
- pid: process.pid,
1024
- startedAt: new Date().toISOString()
1025
- });
1026
- const cleanup = () => clearBridgePid(options);
1027
- process.once("SIGINT", () => {
1028
- cleanup();
1029
- process.exit(0);
1030
- });
1031
- process.once("SIGTERM", () => {
1032
- cleanup();
1033
- process.exit(0);
1034
- });
1035
- try {
1036
- const intervalMs = options.intervalMs ?? 30000;
1037
- do {
1038
- if (options.agentName) {
1039
- await client.heartbeatAgent({
1040
- name: options.agentName,
1041
- source: "mementos-bridge"
1042
- });
1043
- }
1044
- if (once)
1045
- break;
1046
- await sleep(intervalMs);
1047
- } while (true);
1048
- if (once)
1049
- cleanup();
1050
- return getBridgeStatus(options);
1051
- } finally {
1052
- if (once)
1053
- cleanup();
1054
- }
1055
- }
1056
- async function runBilling(args) {
1057
- const subcommand = args[0] ?? "status";
1058
- switch (subcommand) {
1059
- case "status":
1060
- return client.billingStatus();
1061
- case "credits":
1062
- return client.listCreditPacks();
1063
- case "buy": {
1064
- const credits = Number(args[1]);
1065
- if (!Number.isInteger(credits) || credits < 1)
1066
- throw new Error("usage: mementos billing buy <credits>");
1067
- return client.buyCredits({ credits });
1068
- }
1069
- default:
1070
- throw new Error("usage: mementos billing <status|credits|buy>");
1071
- }
1072
- }
1073
- async function runAuth(args) {
1074
- const subcommand = args[0] ?? "help";
1075
- if (subcommand !== "signup" && subcommand !== "login") {
1076
- throw new Error("usage: mementos auth signup");
1077
- }
1078
- return renderSignupInstructions(baseProductUrl());
1079
- }
1080
- async function runSetup(args) {
1081
- const subcommand = args[0] ?? "help";
1082
- if (subcommand !== "agents") {
1083
- throw new Error("usage: mementos setup agents [--dry-run] [--home <path>] [--hosts <list>]");
1084
- }
1085
- const options = parseSetupOptions(args.slice(1));
1086
- const plan = installAgentSetup({
1087
- action: options.action,
1088
- apiUrl: baseProductUrl(),
1089
- dryRun: options.dryRun,
1090
- homeDir: options.homeDir,
1091
- hosts: options.hosts
1092
- });
1093
- return {
1094
- installed: !options.dryRun,
1095
- action: options.action ?? "install",
1096
- baseDir: plan.baseDir,
1097
- hosts: plan.hosts,
1098
- files: plan.files.map((file) => file.path),
1099
- hostResults: plan.hostResults,
1100
- mcpCommand: plan.mcpCommand
1101
- };
1102
- }
1103
- async function injectMemory(args) {
1104
- const query = args.filter((arg) => !arg.startsWith("--")).join(" ").trim();
1105
- const payload = await client.injectionContext({ query, limit: 12 });
1106
- const block = readTextPayload(payload) ?? formatMemoryInjection(payload);
1107
- if (block) {
1108
- process.stdout.write(block);
1109
- return;
1110
- }
1111
- return { memories: 0 };
1112
- }
1113
- async function runHook(args) {
1114
- const subcommand = args[0] ?? "help";
1115
- if (subcommand === "doctor") {
1116
- const options = parseSetupOptions(args.slice(1));
1117
- return diagnoseAgentSetup({
1118
- apiUrl: baseProductUrl(),
1119
- homeDir: options.homeDir,
1120
- hosts: options.hosts
1121
- });
1122
- }
1123
- const hookEvents = {
1124
- notification: "Notification",
1125
- "post-tool-use": "PostToolUse",
1126
- "pre-tool-use": "PreToolUse",
1127
- setup: "Setup",
1128
- "subagent-start": "SubagentStart",
1129
- "subagent-stop": "SubagentStop",
1130
- "session-start": "SessionStart",
1131
- "user-prompt-submit": "UserPromptSubmit"
1132
- };
1133
- if (subcommand in hookEvents) {
1134
- const hookInput = await readHookInput();
1135
- const query2 = hookInput.query;
1136
- const payload = await client.injectionContext({ query: query2, limit: 12 });
1137
- const block = readTextPayload(payload) ?? formatMemoryInjection(payload);
1138
- const inboxBlock = await readInboxHookContext(hookInput);
1139
- return {
1140
- suppressOutput: true,
1141
- hookSpecificOutput: {
1142
- hookEventName: hookEvents[subcommand],
1143
- additionalContext: combineContextBlocks(inboxBlock, block)
1144
- }
1145
- };
1146
- }
1147
- if (subcommand !== "stop") {
1148
- throw new Error("usage: mementos hook <stop|user-prompt-submit|session-start|subagent-start|subagent-stop|pre-tool-use|post-tool-use|notification|setup|doctor>");
1149
- }
1150
- const query = process.env.MEMENTOS_INJECTION_QUERY ?? process.cwd();
1151
- return injectMemory([query]);
1152
- }
1153
- async function readHookInput() {
1154
- if (process.env.MEMENTOS_INJECTION_QUERY) {
1155
- return {
1156
- agentName: process.env.MEMENTOS_AGENT_NAME,
1157
- query: process.env.MEMENTOS_INJECTION_QUERY
1158
- };
1159
- }
1160
- const stdin = await readOptionalStdin();
1161
- if (!stdin) {
1162
- return {
1163
- agentName: process.env.MEMENTOS_AGENT_NAME,
1164
- query: process.cwd()
1165
- };
1166
- }
1167
- try {
1168
- const parsed = JSON.parse(stdin);
1169
- const prompt = parsed.prompt ?? parsed.userPrompt ?? parsed.message ?? parsed.cwd;
1170
- return {
1171
- agentName: readHookText(parsed.agentName ?? parsed.agent ?? parsed.name) ?? process.env.MEMENTOS_AGENT_NAME,
1172
- projectKey: readHookText(parsed.projectKey ?? parsed.project),
1173
- query: typeof prompt === "string" && prompt.trim() ? prompt.trim() : process.cwd(),
1174
- sessionId: readHookText(parsed.sessionId ?? parsed.session_id)
1175
- };
1176
- } catch {
1177
- if (stdin.trim()) {
1178
- return {
1179
- agentName: process.env.MEMENTOS_AGENT_NAME,
1180
- query: stdin.trim()
1181
- };
1182
- }
1183
- }
1184
- return {
1185
- agentName: process.env.MEMENTOS_AGENT_NAME,
1186
- query: process.cwd()
1187
- };
1188
- }
1189
- async function readInboxHookContext(input) {
1190
- if (!input.agentName)
1191
- return;
1192
- const response = await client.listInboxMessages({
1193
- agentName: input.agentName,
1194
- projectKey: input.projectKey,
1195
- sessionId: input.sessionId,
1196
- limit: 10
1197
- });
1198
- const messages = readInboxMessages(response);
1199
- if (messages.length === 0)
1200
- return;
1201
- await Promise.all(messages.map((message) => client.claimInboxMessage(message.id, { agentName: input.agentName })));
1202
- return formatInboxContext(messages);
1203
- }
1204
- function readInboxMessages(payload) {
1205
- if (typeof payload !== "object" || payload === null)
1206
- return [];
1207
- const messages = payload.messages;
1208
- if (!Array.isArray(messages))
1209
- return [];
1210
- return messages.flatMap((message) => {
1211
- if (typeof message !== "object" || message === null)
1212
- return [];
1213
- const record = message;
1214
- const id = readHookText(record.id);
1215
- const value = readHookText(record.message);
1216
- if (!id || !value)
1217
- return [];
1218
- return [{
1219
- id,
1220
- kind: readHookText(record.kind) ?? "directive",
1221
- message: value,
1222
- priority: readHookText(record.priority) ?? "normal",
1223
- title: readHookText(record.title)
1224
- }];
1225
- });
1226
- }
1227
- function formatInboxContext(messages) {
1228
- return [
1229
- "## mementos.md inbox",
1230
- "Apply these hosted agent directives before continuing. They have been claimed remotely.",
1231
- "",
1232
- ...messages.map((message) => `- [${message.priority}] ${message.title ? `${message.title}: ` : ""}${message.message} (${message.kind})`),
1233
- ""
1234
- ].join(`
1235
- `);
1236
- }
1237
- function combineContextBlocks(...blocks) {
1238
- return blocks.map((block) => block?.trim()).filter(Boolean).join(`
1239
-
1240
- `);
1241
- }
1242
- function readHookText(value) {
1243
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
1244
- }
1245
- async function readOptionalStdin() {
1246
- if (process.stdin.isTTY)
1247
- return "";
1248
- return new Response(Bun.stdin.stream()).text();
1249
- }
1250
- function parseSetupOptions(args) {
1251
- const options = {};
1252
- for (let index = 0;index < args.length; index += 1) {
1253
- const arg = args[index];
1254
- if (arg === "--dry-run") {
1255
- options.dryRun = true;
1256
- } else if (arg === "--verify") {
1257
- options.action = "verify";
1258
- } else if (arg === "--uninstall") {
1259
- options.action = "uninstall";
1260
- } else if (arg === "--home") {
1261
- options.homeDir = args[++index];
1262
- } else if (arg === "--hosts") {
1263
- options.hosts = parseHosts(args[++index] ?? "");
1264
- } else {
1265
- throw new Error(`Unknown setup option: ${arg}`);
1266
- }
1267
- }
1268
- return options;
1269
- }
1270
- function parseBridgeOptions(args) {
1271
- const options = {};
1272
- for (let index = 0;index < args.length; index += 1) {
1273
- const arg = args[index];
1274
- if (arg === "--agent" || arg === "--agent-name") {
1275
- options.agentName = args[++index];
1276
- } else if (arg === "--home") {
1277
- options.homeDir = args[++index];
1278
- } else if (arg === "--interval-ms") {
1279
- options.intervalMs = readPositiveInteger(args[++index]);
1280
- } else if (arg === "--lines") {
1281
- options.lines = readPositiveInteger(args[++index]);
1282
- } else if (arg === "--once") {
1283
- options.once = true;
1284
- } else {
1285
- throw new Error(`Unknown bridge option: ${arg}`);
1286
- }
1287
- }
1288
- return options;
1289
- }
1290
- function parseInboxOptions(args) {
1291
- const options = {};
1292
- for (let index = 0;index < args.length; index += 1) {
1293
- const arg = args[index];
1294
- if (arg === "--agent" || arg === "--agent-name") {
1295
- options.agentName = args[++index];
1296
- } else if (arg === "--agent-id") {
1297
- options.agentId = args[++index];
1298
- } else if (arg === "--project") {
1299
- options.projectKey = args[++index];
1300
- } else if (arg === "--session") {
1301
- options.sessionId = args[++index];
1302
- } else if (arg === "--include-delivered") {
1303
- options.includeDelivered = true;
1304
- } else if (arg === "--limit") {
1305
- options.limit = Number(args[++index]);
1306
- } else {
1307
- throw new Error(`Unknown inbox option: ${arg}`);
1308
- }
1309
- }
1310
- return options;
1311
- }
1312
- function parseHosts(input) {
1313
- const requested = input.split(",").map((host) => host.trim()).filter(Boolean);
1314
- const unknown = requested.filter((host) => !SUPPORTED_AGENT_HOSTS.includes(host));
1315
- if (unknown.length > 0)
1316
- throw new Error(`Unknown agent host: ${unknown.join(", ")}`);
1317
- return requested;
1318
- }
1319
- function baseProductUrl() {
1320
- return (process.env.MEMENTOS_API_URL ?? DEFAULT_API_URL).replace(/\/api\/v1\/?$/, "").replace(/\/+$/, "");
1321
- }
1322
- function baseApiUrl() {
1323
- return (process.env.MEMENTOS_API_URL ?? DEFAULT_API_URL).replace(/\/+$/, "");
1324
- }
1325
- function readPositiveInteger(input) {
1326
- if (!input)
1327
- return;
1328
- const value = Number(input);
1329
- return Number.isInteger(value) && value > 0 ? value : undefined;
1330
- }
1331
- function sleep(ms) {
1332
- return new Promise((resolve) => setTimeout(resolve, ms));
1333
- }
1334
- function readTextPayload(payload) {
1335
- if (typeof payload !== "object" || payload === null)
1336
- return;
1337
- const text = payload.text;
1338
- return typeof text === "string" && text.trim() ? text : undefined;
1339
- }
1340
- function helpText() {
1341
- return `mementos remote-only SaaS client
1342
-
1343
- Usage:
1344
- mementos auth signup
1345
- mementos setup agents [--dry-run|--verify|--uninstall]
1346
- mementos inject <query>
1347
- mementos hook <stop|user-prompt-submit|session-start|subagent-start|subagent-stop|pre-tool-use|post-tool-use|notification|setup|doctor>
1348
- mementos bridge <start|status|stop|install|uninstall|logs|run>
1349
- mementos inbox <send|list|claim>
1350
- mementos billing <status|credits|buy>
1351
- mementos capabilities
1352
- mementos agents
1353
- mementos usage
1354
- mementos search <query>
1355
- mementos save <key> <value>
1356
- MEMENTOS_API_TOKEN=<token> mementos-mcp
1357
-
1358
- Environment:
1359
- MEMENTOS_API_URL Defaults to https://mementos.md/api/v1
1360
- MEMENTOS_API_TOKEN Required for hosted private operations
1361
- `;
1362
- }