@hasna/mementos 0.14.40 → 0.14.42

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.
package/LICENSE CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  Apache License
2
3
  Version 2.0, January 2004
3
4
  http://www.apache.org/licenses/
@@ -175,7 +176,7 @@
175
176
 
176
177
  END OF TERMS AND CONDITIONS
177
178
 
178
- Copyright 2024 Andrei Hasna
179
+ Copyright 2026 Hasna, Inc.
179
180
 
180
181
  Licensed under the Apache License, Version 2.0 (the "License");
181
182
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -17,6 +17,39 @@ npm install -g @hasna/mementos
17
17
  mementos --help
18
18
  ```
19
19
 
20
+ ## Shared Event Webhooks
21
+
22
+ `mementos` exposes the shared `@hasna/events` commands so memory events can
23
+ trigger deterministic or agentic automation without custom glue scripts. To
24
+ route mementos events into an OpenLoops worker/verifier template, register a
25
+ command webhook:
26
+
27
+ ```bash
28
+ mementos webhooks add loops \
29
+ --id openloops-mementos-events \
30
+ --transport command \
31
+ --source mementos \
32
+ --type "*" \
33
+ --arg=events \
34
+ --arg=handle \
35
+ --arg=generic \
36
+ --arg=--provider \
37
+ --arg=codewith \
38
+ --arg=--auth-profile \
39
+ --arg=account005 \
40
+ --arg=--permission-mode \
41
+ --arg=bypass \
42
+ --arg=--sandbox \
43
+ --arg=danger-full-access \
44
+ --timeout-ms 900000 \
45
+ --json
46
+ ```
47
+
48
+ `@hasna/events` sends the event envelope on stdin and in `HASNA_EVENT_JSON`.
49
+ OpenLoops can then create a deduped one-shot workflow for the event. Keep the
50
+ event payload scoped and include `working_dir`, `project_path`, or `repo_path`
51
+ when a downstream agent needs to run inside a specific repository.
52
+
20
53
  ## MCP Server
21
54
 
22
55
  ```bash
@@ -25,6 +58,18 @@ mementos-mcp
25
58
 
26
59
  116 tools available.
27
60
 
61
+ ## HTTP mode
62
+
63
+ Run a shared Streamable HTTP MCP server (stateless, `127.0.0.1` only):
64
+
65
+ ```bash
66
+ mementos-mcp --http
67
+ # or: MCP_HTTP=1 mementos-mcp
68
+ # default port: 8824 (override with --port or MCP_HTTP_PORT)
69
+ ```
70
+
71
+ Endpoints: `GET /health`, `POST /mcp` (Streamable HTTP).
72
+
28
73
  ## REST API
29
74
 
30
75
  ```bash
package/dist/cli/index.js CHANGED
@@ -1011,7 +1011,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1011
1011
  this._exitCallback = (err) => {
1012
1012
  if (err.code !== "commander.executeSubCommandAsync") {
1013
1013
  throw err;
1014
- }
1014
+ } else {}
1015
1015
  };
1016
1016
  }
1017
1017
  return this;
@@ -29179,7 +29179,7 @@ class JSONSchemaGenerator {
29179
29179
  if (val === undefined) {
29180
29180
  if (this.unrepresentable === "throw") {
29181
29181
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
29182
- }
29182
+ } else {}
29183
29183
  } else if (typeof val === "bigint") {
29184
29184
  if (this.unrepresentable === "throw") {
29185
29185
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -49430,7 +49430,7 @@ var require_tracestate_impl = __commonJS((exports) => {
49430
49430
  const value = listMember.slice(i + 1, part.length);
49431
49431
  if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
49432
49432
  agg.set(key, value);
49433
- }
49433
+ } else {}
49434
49434
  }
49435
49435
  return agg;
49436
49436
  }, new Map);
@@ -64425,6 +64425,9 @@ import chalk19 from "chalk";
64425
64425
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync2, unlinkSync, cpSync as cpSync2 } from "fs";
64426
64426
  import { homedir as homedir3 } from "os";
64427
64427
  import { basename, dirname as dirname3, join as join5, resolve as resolve13 } from "path";
64428
+ function isInMemoryDb2(path) {
64429
+ return path === ":memory:" || path.startsWith("file::memory:");
64430
+ }
64428
64431
  var DEFAULT_CONFIG = {
64429
64432
  default_scope: "private",
64430
64433
  default_category: "knowledge",
@@ -64596,6 +64599,9 @@ function getDbPath2() {
64596
64599
  }
64597
64600
  const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
64598
64601
  if (envDbPath) {
64602
+ if (isInMemoryDb2(envDbPath)) {
64603
+ return envDbPath;
64604
+ }
64599
64605
  const resolved = resolve13(envDbPath);
64600
64606
  ensureDir2(dirname3(resolved));
64601
64607
  return resolved;
package/dist/index.js CHANGED
@@ -20191,7 +20191,7 @@ class JSONSchemaGenerator {
20191
20191
  if (val === undefined) {
20192
20192
  if (this.unrepresentable === "throw") {
20193
20193
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
20194
- }
20194
+ } else {}
20195
20195
  } else if (typeof val === "bigint") {
20196
20196
  if (this.unrepresentable === "throw") {
20197
20197
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -40442,7 +40442,7 @@ var require_tracestate_impl = __commonJS((exports) => {
40442
40442
  const value = listMember.slice(i + 1, part.length);
40443
40443
  if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
40444
40444
  agg.set(key, value);
40445
- }
40445
+ } else {}
40446
40446
  }
40447
40447
  return agg;
40448
40448
  }, new Map);
@@ -55359,6 +55359,9 @@ function logSearchQuery(query, resultCount, agentId, projectId, db) {
55359
55359
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync, writeFileSync as writeFileSync2, unlinkSync, cpSync as cpSync2 } from "fs";
55360
55360
  import { homedir as homedir2 } from "os";
55361
55361
  import { basename, dirname as dirname2, join as join3, resolve as resolve2 } from "path";
55362
+ function isInMemoryDb2(path) {
55363
+ return path === ":memory:" || path.startsWith("file::memory:");
55364
+ }
55362
55365
  var DEFAULT_CONFIG = {
55363
55366
  default_scope: "private",
55364
55367
  default_category: "knowledge",
@@ -55530,6 +55533,9 @@ function getDbPath2() {
55530
55533
  }
55531
55534
  const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
55532
55535
  if (envDbPath) {
55536
+ if (isInMemoryDb2(envDbPath)) {
55537
+ return envDbPath;
55538
+ }
55533
55539
  const resolved = resolve2(envDbPath);
55534
55540
  ensureDir2(dirname2(resolved));
55535
55541
  return resolved;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAA+B,MAAM,UAAU,CAAC;AAM5E,eAAO,MAAM,cAAc,EAAE,cA4B5B,CAAC;AA4DF,wBAAgB,UAAU,IAAI,cAAc,CAwC3C;AAwED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAMhD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAQ1D;AAED,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAOvC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED,wBAAgB,SAAS,IAAI,MAAM,CAgDlC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAA+B,MAAM,UAAU,CAAC;AAU5E,eAAO,MAAM,cAAc,EAAE,cA4B5B,CAAC;AA4DF,wBAAgB,UAAU,IAAI,cAAc,CAwC3C;AAwED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAMhD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAQ1D;AAED,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAOvC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED,wBAAgB,SAAS,IAAI,MAAM,CAmDlC"}
@@ -0,0 +1,17 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare const MCP_HTTP_SERVICE_NAME = "mementos";
3
+ export declare const DEFAULT_MCP_HTTP_PORT = 8867;
4
+ export declare function isHttpMode(argv?: string[], env?: NodeJS.ProcessEnv): boolean;
5
+ export declare function isStdioMode(argv?: string[], env?: NodeJS.ProcessEnv): boolean;
6
+ export declare function resolveMcpHttpPort(argv?: string[], env?: NodeJS.ProcessEnv): number;
7
+ export type McpHttpServerHandle = {
8
+ port: number;
9
+ host: string;
10
+ close: () => Promise<void>;
11
+ };
12
+ export declare function startMcpHttpServer(buildServer: () => McpServer, options?: {
13
+ port?: number;
14
+ host?: string;
15
+ serviceName?: string;
16
+ }): Promise<McpHttpServerHandle>;
17
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/mcp/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAChD,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,wBAAgB,UAAU,CACxB,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAET;AAED,wBAAgB,WAAW,CACzB,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAET;AAED,wBAAgB,kBAAkB,CAChC,IAAI,GAAE,MAAM,EAAiB,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CASR;AAoBD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,SAAS,EAC5B,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D,OAAO,CAAC,mBAAmB,CAAC,CAwE9B"}
@@ -2,4 +2,5 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  /** Exported so other modules can push channel notifications via the underlying Server. */
4
4
  export declare let mcpServer: McpServer | null;
5
+ export declare function buildServer(): McpServer;
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4CpE,0FAA0F;AAC1F,eAAO,IAAI,SAAS,EAAE,SAAS,GAAG,IAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6CpE,0FAA0F;AAC1F,eAAO,IAAI,SAAS,EAAE,SAAS,GAAG,IAAW,CAAC;AAqB9C,wBAAgB,WAAW,IAAI,SAAS,CA4EvC"}
package/dist/mcp/index.js CHANGED
@@ -14668,6 +14668,9 @@ var init_export_v1 = __esm(() => {
14668
14668
  import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2, unlinkSync, cpSync as cpSync2 } from "fs";
14669
14669
  import { homedir as homedir2 } from "os";
14670
14670
  import { basename as basename2, dirname as dirname4, join as join6, resolve as resolve3 } from "path";
14671
+ function isInMemoryDb2(path) {
14672
+ return path === ":memory:" || path.startsWith("file::memory:");
14673
+ }
14671
14674
  function deepMerge(target, source) {
14672
14675
  const result = { ...target };
14673
14676
  for (const key of Object.keys(source)) {
@@ -14774,6 +14777,9 @@ function getDbPath2() {
14774
14777
  }
14775
14778
  const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
14776
14779
  if (envDbPath) {
14780
+ if (isInMemoryDb2(envDbPath)) {
14781
+ return envDbPath;
14782
+ }
14777
14783
  const resolved = resolve3(envDbPath);
14778
14784
  ensureDir2(dirname4(resolved));
14779
14785
  return resolved;
@@ -26264,7 +26270,7 @@ class JSONSchemaGenerator {
26264
26270
  if (val === undefined) {
26265
26271
  if (this.unrepresentable === "throw") {
26266
26272
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
26267
- }
26273
+ } else {}
26268
26274
  } else if (typeof val === "bigint") {
26269
26275
  if (this.unrepresentable === "throw") {
26270
26276
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -46515,7 +46521,7 @@ var require_tracestate_impl = __commonJS((exports) => {
46515
46521
  const value = listMember.slice(i + 1, part.length);
46516
46522
  if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
46517
46523
  agg.set(key, value);
46518
- }
46524
+ } else {}
46519
46525
  }
46520
46526
  return agg;
46521
46527
  }, new Map);
@@ -68285,74 +68291,174 @@ function registerConsolidationTools(server) {
68285
68291
  });
68286
68292
  }
68287
68293
 
68294
+ // src/mcp/http.ts
68295
+ import { createServer } from "http";
68296
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
68297
+ var MCP_HTTP_SERVICE_NAME = "mementos";
68298
+ var DEFAULT_MCP_HTTP_PORT = 8867;
68299
+ function isStdioMode(argv = process.argv, env = process.env) {
68300
+ return argv.includes("--stdio") || env.MCP_STDIO === "1";
68301
+ }
68302
+ function resolveMcpHttpPort(argv = process.argv, env = process.env) {
68303
+ const portIdx = argv.indexOf("--port");
68304
+ if (portIdx !== -1 && argv[portIdx + 1]) {
68305
+ return parsePort(argv[portIdx + 1], "--port");
68306
+ }
68307
+ if (env.MCP_HTTP_PORT) {
68308
+ return parsePort(env.MCP_HTTP_PORT, "MCP_HTTP_PORT");
68309
+ }
68310
+ return DEFAULT_MCP_HTTP_PORT;
68311
+ }
68312
+ function parsePort(raw, source) {
68313
+ const parsed = Number(raw);
68314
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
68315
+ throw new Error(`Invalid ${source} value "${raw}". Expected 0-65535.`);
68316
+ }
68317
+ return parsed;
68318
+ }
68319
+ async function readJsonBody(req) {
68320
+ const chunks = [];
68321
+ for await (const chunk of req) {
68322
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
68323
+ }
68324
+ const text3 = Buffer.concat(chunks).toString("utf8");
68325
+ if (!text3)
68326
+ return;
68327
+ return JSON.parse(text3);
68328
+ }
68329
+ async function startMcpHttpServer(buildServer, options) {
68330
+ const host = options?.host ?? "127.0.0.1";
68331
+ const requestedPort = options?.port ?? resolveMcpHttpPort();
68332
+ const serviceName = options?.serviceName ?? MCP_HTTP_SERVICE_NAME;
68333
+ const httpServer = createServer(async (req, res) => {
68334
+ try {
68335
+ const url2 = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
68336
+ if (req.method === "GET" && url2.pathname === "/health") {
68337
+ res.writeHead(200, { "Content-Type": "application/json" });
68338
+ res.end(JSON.stringify({ status: "ok", name: serviceName }));
68339
+ return;
68340
+ }
68341
+ if (url2.pathname !== "/mcp") {
68342
+ res.writeHead(404, { "Content-Type": "text/plain" });
68343
+ res.end("Not Found");
68344
+ return;
68345
+ }
68346
+ const server = buildServer();
68347
+ const transport = new StreamableHTTPServerTransport({
68348
+ sessionIdGenerator: undefined
68349
+ });
68350
+ await server.connect(transport);
68351
+ let parsedBody;
68352
+ if (req.method === "POST") {
68353
+ parsedBody = await readJsonBody(req);
68354
+ }
68355
+ await transport.handleRequest(req, res, parsedBody);
68356
+ res.on("close", () => {
68357
+ transport.close();
68358
+ server.close();
68359
+ });
68360
+ } catch (error40) {
68361
+ console.error(`[${serviceName}-mcp] HTTP error:`, error40);
68362
+ if (!res.headersSent) {
68363
+ res.writeHead(500, { "Content-Type": "application/json" });
68364
+ res.end(JSON.stringify({
68365
+ jsonrpc: "2.0",
68366
+ error: { code: -32603, message: "Internal server error" },
68367
+ id: null
68368
+ }));
68369
+ }
68370
+ }
68371
+ });
68372
+ await new Promise((resolve6, reject) => {
68373
+ httpServer.once("error", reject);
68374
+ httpServer.listen(requestedPort, host, () => resolve6());
68375
+ });
68376
+ const addr = httpServer.address();
68377
+ const port = typeof addr === "object" && addr ? addr.port : requestedPort;
68378
+ console.error(`[${serviceName}-mcp] Streamable HTTP listening on http://${host}:${port}/mcp`);
68379
+ return {
68380
+ port,
68381
+ host,
68382
+ close: () => new Promise((resolve6, reject) => {
68383
+ httpServer.close((err) => err ? reject(err) : resolve6());
68384
+ })
68385
+ };
68386
+ }
68387
+
68288
68388
  // src/mcp/index.ts
68289
68389
  import { createRequire } from "module";
68290
68390
  var _require = createRequire(import.meta.url);
68291
68391
  var _pkg = _require("../../package.json");
68292
68392
  var mcpServer = null;
68293
- var server = new McpServer({
68294
- name: "mementos",
68295
- version: _pkg.version
68296
- }, {
68297
- capabilities: {
68298
- experimental: { "claude/channel": {} }
68299
- },
68300
- instructions: `Mementos is the persistent memory layer for AI agents. It stores, searches, and manages memories across sessions and projects.
68301
-
68302
- When running with --dangerously-load-development-channels, mementos will proactively push relevant memories into your conversation via channel notifications. These appear as <channel source="mementos"> tags. They contain memories activated by your current task context \u2014 use them to inform your work. You don't need to call memory_inject when auto-inject is active.`
68303
- });
68304
- mcpServer = server;
68305
68393
  function hasFlag(...flags) {
68306
68394
  return process.argv.some((arg) => flags.includes(arg));
68307
68395
  }
68308
68396
  function printHelp() {
68309
68397
  process.stdout.write(`Usage: mementos-mcp [options]
68310
68398
 
68311
- Mementos MCP server (stdio transport)
68399
+ Mementos MCP server (stdio transport by default)
68312
68400
 
68313
68401
  Options:
68402
+ --http Serve MCP over Streamable HTTP (127.0.0.1)
68403
+ --port <number> HTTP port (default: 8824, env: MCP_HTTP_PORT)
68314
68404
  -h, --help Show help
68315
68405
  -V, --version Show version
68316
68406
  `);
68317
68407
  }
68318
- registerMemoryCrudTools(server);
68319
- registerMemoryHistoryTools(server);
68320
- registerMemoryHealthTools(server);
68321
- registerMemoryValidationTools(server);
68322
- registerMemorySearchTools(server);
68323
- registerMemoryLifecycleTools(server);
68324
- registerMemorySyncTools(server);
68325
- registerMemoryStatsTools(server);
68326
- registerMemoryAuditTools(server);
68327
- registerMemoryIoTools(server);
68328
- registerMemoryInjectTools(server);
68329
- registerEntityTools(server);
68330
- registerRelationTools(server);
68331
- registerGraphQueryTools(server);
68332
- registerAgentTools(server);
68333
- registerProjectTools(server);
68334
- registerBulkTools(server);
68335
- registerLockTools(server);
68336
- registerFocusTools(server);
68337
- registerHookTools(server);
68338
- registerSynthesisTools(server);
68339
- registerAutoMemoryTools(server);
68340
- registerSessionTools(server);
68341
- registerUtilityTools(server);
68342
- registerSystemTools(server);
68343
- registerConsolidationTools(server);
68344
- server.resource("memories", "mementos://memories", { description: "All active memories", mimeType: "application/json" }, async () => {
68345
- const memories = listMemories({ status: "active", limit: 1000 });
68346
- return { contents: [{ uri: "mementos://memories", text: JSON.stringify(memories, null, 2), mimeType: "application/json" }] };
68347
- });
68348
- server.resource("agents", "mementos://agents", { description: "All registered agents", mimeType: "application/json" }, async () => {
68349
- const agents = listAgents();
68350
- return { contents: [{ uri: "mementos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
68351
- });
68352
- server.resource("projects", "mementos://projects", { description: "All registered projects", mimeType: "application/json" }, async () => {
68353
- const projects = listProjects();
68354
- return { contents: [{ uri: "mementos://projects", text: JSON.stringify(projects, null, 2), mimeType: "application/json" }] };
68355
- });
68408
+ function buildServer() {
68409
+ const server = new McpServer({
68410
+ name: "mementos",
68411
+ version: _pkg.version
68412
+ }, {
68413
+ capabilities: {
68414
+ experimental: { "claude/channel": {} }
68415
+ },
68416
+ instructions: `Mementos is the persistent memory layer for AI agents. It stores, searches, and manages memories across sessions and projects.
68417
+
68418
+ When running with --dangerously-load-development-channels, mementos will proactively push relevant memories into your conversation via channel notifications. These appear as <channel source="mementos"> tags. They contain memories activated by your current task context \u2014 use them to inform your work. You don't need to call memory_inject when auto-inject is active.`
68419
+ });
68420
+ registerMemoryCrudTools(server);
68421
+ registerMemoryHistoryTools(server);
68422
+ registerMemoryHealthTools(server);
68423
+ registerMemoryValidationTools(server);
68424
+ registerMemorySearchTools(server);
68425
+ registerMemoryLifecycleTools(server);
68426
+ registerMemorySyncTools(server);
68427
+ registerMemoryStatsTools(server);
68428
+ registerMemoryAuditTools(server);
68429
+ registerMemoryIoTools(server);
68430
+ registerMemoryInjectTools(server);
68431
+ registerEntityTools(server);
68432
+ registerRelationTools(server);
68433
+ registerGraphQueryTools(server);
68434
+ registerAgentTools(server);
68435
+ registerProjectTools(server);
68436
+ registerBulkTools(server);
68437
+ registerLockTools(server);
68438
+ registerFocusTools(server);
68439
+ registerHookTools(server);
68440
+ registerSynthesisTools(server);
68441
+ registerAutoMemoryTools(server);
68442
+ registerSessionTools(server);
68443
+ registerUtilityTools(server);
68444
+ registerSystemTools(server);
68445
+ registerMementosStorageTools(server);
68446
+ registerConsolidationTools(server);
68447
+ server.resource("memories", "mementos://memories", { description: "All active memories", mimeType: "application/json" }, async () => {
68448
+ const memories = listMemories({ status: "active", limit: 1000 });
68449
+ return { contents: [{ uri: "mementos://memories", text: JSON.stringify(memories, null, 2), mimeType: "application/json" }] };
68450
+ });
68451
+ server.resource("agents", "mementos://agents", { description: "All registered agents", mimeType: "application/json" }, async () => {
68452
+ const agents = listAgents();
68453
+ return { contents: [{ uri: "mementos://agents", text: JSON.stringify(agents, null, 2), mimeType: "application/json" }] };
68454
+ });
68455
+ server.resource("projects", "mementos://projects", { description: "All registered projects", mimeType: "application/json" }, async () => {
68456
+ const projects = listProjects();
68457
+ return { contents: [{ uri: "mementos://projects", text: JSON.stringify(projects, null, 2), mimeType: "application/json" }] };
68458
+ });
68459
+ mcpServer = server;
68460
+ return server;
68461
+ }
68356
68462
  async function ensureRestServerRunning() {
68357
68463
  try {
68358
68464
  const res = await fetch("http://127.0.0.1:19428/api/memories?limit=0", {
@@ -68378,6 +68484,16 @@ async function ensureRestServerRunning() {
68378
68484
  } catch {}
68379
68485
  }
68380
68486
  }
68487
+ async function prepareMcpRuntime() {
68488
+ try {
68489
+ const warning = getPrimaryMachineStartupWarning(getDatabase());
68490
+ if (warning) {
68491
+ console.warn(`[mementos-mcp] ${warning}`);
68492
+ }
68493
+ } catch {}
68494
+ await ensureRestServerRunning();
68495
+ loadWebhooksFromDb();
68496
+ }
68381
68497
  async function main() {
68382
68498
  if (hasFlag("--help", "-h")) {
68383
68499
  printHelp();
@@ -68388,37 +68504,45 @@ async function main() {
68388
68504
  `);
68389
68505
  return;
68390
68506
  }
68391
- try {
68392
- const warning = getPrimaryMachineStartupWarning(getDatabase());
68393
- if (warning) {
68394
- console.warn(`[mementos-mcp] ${warning}`);
68395
- }
68396
- } catch {}
68397
- await ensureRestServerRunning();
68398
- loadWebhooksFromDb();
68399
- const transport = new StdioServerTransport;
68400
- registerMementosStorageTools(server);
68401
- await server.connect(transport);
68402
- const autoProject = detectProject();
68403
- startAutoInject({
68404
- server,
68405
- project_id: autoProject?.id,
68406
- project_name: autoProject?.name,
68407
- cwd: process.cwd()
68408
- }).catch(() => {});
68507
+ await prepareMcpRuntime();
68508
+ if (isStdioMode()) {
68509
+ const server = buildServer();
68510
+ const transport = new StdioServerTransport;
68511
+ await server.connect(transport);
68512
+ const autoProject = detectProject();
68513
+ startAutoInject({
68514
+ server,
68515
+ project_id: autoProject?.id,
68516
+ project_name: autoProject?.name,
68517
+ cwd: process.cwd()
68518
+ }).catch(() => {});
68519
+ process.on("SIGINT", () => {
68520
+ stopAutoInject();
68521
+ process.exit(0);
68522
+ });
68523
+ process.on("SIGTERM", () => {
68524
+ stopAutoInject();
68525
+ process.exit(0);
68526
+ });
68527
+ return;
68528
+ }
68529
+ const handle = await startMcpHttpServer(buildServer, {
68530
+ port: resolveMcpHttpPort()
68531
+ });
68409
68532
  process.on("SIGINT", () => {
68410
- stopAutoInject();
68411
- process.exit(0);
68533
+ handle.close().finally(() => process.exit(0));
68412
68534
  });
68413
68535
  process.on("SIGTERM", () => {
68414
- stopAutoInject();
68415
- process.exit(0);
68536
+ handle.close().finally(() => process.exit(0));
68537
+ });
68538
+ }
68539
+ if (import.meta.main) {
68540
+ main().catch((error40) => {
68541
+ console.error("MCP server error:", error40);
68542
+ process.exit(1);
68416
68543
  });
68417
68544
  }
68418
- main().catch((error40) => {
68419
- console.error("MCP server error:", error40);
68420
- process.exit(1);
68421
- });
68422
68545
  export {
68423
- mcpServer
68546
+ mcpServer,
68547
+ buildServer
68424
68548
  };
@@ -5954,7 +5954,7 @@ __export(exports_database, {
5954
5954
  });
5955
5955
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, cpSync as cpSync2 } from "fs";
5956
5956
  import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
5957
- function isInMemoryDb(path) {
5957
+ function isInMemoryDb2(path) {
5958
5958
  return path === ":memory:" || path.startsWith("file::memory:");
5959
5959
  }
5960
5960
  function findNearestMementosDb(startDir) {
@@ -6011,7 +6011,7 @@ function getDbPath2() {
6011
6011
  return join3(home, ".hasna", "mementos", "mementos.db");
6012
6012
  }
6013
6013
  function ensureDir2(filePath) {
6014
- if (isInMemoryDb(filePath))
6014
+ if (isInMemoryDb2(filePath))
6015
6015
  return;
6016
6016
  const dir = dirname2(resolve2(filePath));
6017
6017
  if (!existsSync2(dir)) {
@@ -22974,7 +22974,7 @@ class JSONSchemaGenerator {
22974
22974
  if (val === undefined) {
22975
22975
  if (this.unrepresentable === "throw") {
22976
22976
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
22977
- }
22977
+ } else {}
22978
22978
  } else if (typeof val === "bigint") {
22979
22979
  if (this.unrepresentable === "throw") {
22980
22980
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -43225,7 +43225,7 @@ var require_tracestate_impl = __commonJS((exports) => {
43225
43225
  const value = listMember.slice(i + 1, part.length);
43226
43226
  if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
43227
43227
  agg.set(key, value);
43228
- }
43228
+ } else {}
43229
43229
  }
43230
43230
  return agg;
43231
43231
  }, new Map);
@@ -55997,6 +55997,9 @@ import { join as join5, resolve as resolve4, sep } from "path";
55997
55997
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, unlinkSync, cpSync } from "fs";
55998
55998
  import { homedir } from "os";
55999
55999
  import { basename, dirname, join, resolve } from "path";
56000
+ function isInMemoryDb(path) {
56001
+ return path === ":memory:" || path.startsWith("file::memory:");
56002
+ }
56000
56003
  function findFileWalkingUp(filename) {
56001
56004
  let dir = process.cwd();
56002
56005
  while (true) {
@@ -56063,6 +56066,9 @@ function getDbPath() {
56063
56066
  }
56064
56067
  const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
56065
56068
  if (envDbPath) {
56069
+ if (isInMemoryDb(envDbPath)) {
56070
+ return envDbPath;
56071
+ }
56066
56072
  const resolved = resolve(envDbPath);
56067
56073
  ensureDir(dirname(resolved));
56068
56074
  return resolved;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.14.40",
3
+ "version": "0.14.42",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -35,7 +35,7 @@
35
35
  "build": "bun run clean && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/index.ts src/storage.ts --outdir dist --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun && tsc --emitDeclarationOnly --outDir dist",
36
36
  "prepare": "bun run build",
37
37
  "typecheck": "tsc --noEmit",
38
- "test": "bun test",
38
+ "test": "bun test --isolate --timeout=10000",
39
39
  "dev:cli": "bun run src/cli/index.tsx",
40
40
  "dev:mcp": "bun run src/mcp/index.ts",
41
41
  "dev:serve": "bun run src/server/index.ts",