@hasna/mementos 0.1.1 → 0.2.0

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.
@@ -3,4 +3,10 @@ import type { Agent } from "../types/index.js";
3
3
  export declare function registerAgent(name: string, description?: string, role?: string, db?: Database): Agent;
4
4
  export declare function getAgent(idOrName: string, db?: Database): Agent | null;
5
5
  export declare function listAgents(db?: Database): Agent[];
6
+ export declare function updateAgent(id: string, updates: {
7
+ name?: string;
8
+ description?: string;
9
+ role?: string;
10
+ metadata?: Record<string, unknown>;
11
+ }, db?: Database): Agent | null;
6
12
  //# sourceMappingURL=agents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAe/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CAqCP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsBd;AAED,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAMjD"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAe/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CAqCP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsBd;AAED,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAMjD;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACnG,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAkCd"}
@@ -1,5 +1,6 @@
1
1
  import { Database } from "bun:sqlite";
2
2
  import type { CreateMemoryInput, DedupeMode, Memory, MemoryFilter, UpdateMemoryInput } from "../types/index.js";
3
+ export declare function parseMemoryRow(row: Record<string, unknown>): Memory;
3
4
  export declare function createMemory(input: CreateMemoryInput, dedupeMode?: DedupeMode, db?: Database): Memory;
4
5
  export declare function getMemory(id: string, db?: Database): Memory | null;
5
6
  export declare function getMemoryByKey(key: string, scope?: string, agentId?: string, projectId?: string, sessionId?: string, db?: Database): Memory | null;
@@ -1 +1 @@
1
- {"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAyB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,YAAY,EACZ,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAyC3B,wBAAgB,YAAY,CAC1B,KAAK,EAAE,iBAAiB,EACxB,UAAU,GAAE,UAAoB,EAChC,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAoGR;AAMD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,GAAG,IAAI,CA4Bf;AAMD,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,CA4G3E;AAMD,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,iBAAiB,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAiER;AAMD,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAI/D;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAUvE;AAMD,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAM3D;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAQ1D"}
1
+ {"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAyB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,YAAY,EACZ,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAW3B,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAwBnE;AAMD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,iBAAiB,EACxB,UAAU,GAAE,UAAoB,EAChC,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAoGR;AAMD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,GAAG,IAAI,CA4Bf;AAMD,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,CA4G3E;AAMD,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,iBAAiB,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAiER;AAMD,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAI/D;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAUvE;AAMD,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAM3D;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAQ1D"}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export type { Memory, MemoryWithRelations, MemoryScope, MemoryCategory, MemorySo
2
2
  export { MemoryNotFoundError, DuplicateMemoryError, MemoryExpiredError, InvalidScopeError, VersionConflictError, } from "./types/index.js";
3
3
  export { getDatabase, closeDatabase, resetDatabase, getDbPath, resolvePartialId, now, uuid, shortUuid, } from "./db/database.js";
4
4
  export { createMemory, getMemory, getMemoryByKey, listMemories, updateMemory, deleteMemory, bulkDeleteMemories, touchMemory, cleanExpiredMemories, } from "./db/memories.js";
5
- export { registerAgent, getAgent, listAgents, } from "./db/agents.js";
5
+ export { registerAgent, getAgent, listAgents, updateAgent, } from "./db/agents.js";
6
6
  export { registerProject, getProject, listProjects, } from "./db/projects.js";
7
7
  export { searchMemories } from "./lib/search.js";
8
8
  export { loadConfig, DEFAULT_CONFIG } from "./lib/config.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,MAAM,EACN,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,KAAK,EACL,OAAO,EACP,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,GAAG,EACH,IAAI,EACJ,SAAS,GACV,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,YAAY,EACZ,SAAS,EACT,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,aAAa,EACb,QAAQ,EACR,UAAU,GACX,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAG7E,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,MAAM,EACN,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,KAAK,EACL,OAAO,EACP,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,GAAG,EACH,IAAI,EACJ,SAAS,GACV,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,YAAY,EACZ,SAAS,EACT,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAG7E,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -602,6 +602,31 @@ function listAgents(db) {
602
602
  const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
603
603
  return rows.map(parseAgentRow);
604
604
  }
605
+ function updateAgent(id, updates, db) {
606
+ const d = db || getDatabase();
607
+ const agent = getAgent(id, d);
608
+ if (!agent)
609
+ return null;
610
+ const timestamp = now();
611
+ if (updates.name && updates.name !== agent.name) {
612
+ const existing = d.query("SELECT id FROM agents WHERE name = ? AND id != ?").get(updates.name, agent.id);
613
+ if (existing) {
614
+ throw new Error(`Agent name already taken: ${updates.name}`);
615
+ }
616
+ d.run("UPDATE agents SET name = ? WHERE id = ?", [updates.name, agent.id]);
617
+ }
618
+ if (updates.description !== undefined) {
619
+ d.run("UPDATE agents SET description = ? WHERE id = ?", [updates.description, agent.id]);
620
+ }
621
+ if (updates.role !== undefined) {
622
+ d.run("UPDATE agents SET role = ? WHERE id = ?", [updates.role, agent.id]);
623
+ }
624
+ if (updates.metadata !== undefined) {
625
+ d.run("UPDATE agents SET metadata = ? WHERE id = ?", [JSON.stringify(updates.metadata), agent.id]);
626
+ }
627
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
628
+ return getAgent(agent.id, d);
629
+ }
605
630
  // src/db/projects.ts
606
631
  function parseProjectRow(row) {
607
632
  return {
@@ -1136,6 +1161,7 @@ var defaultSyncAgents = ["claude", "codex", "gemini"];
1136
1161
  export {
1137
1162
  uuid,
1138
1163
  updateMemory,
1164
+ updateAgent,
1139
1165
  touchMemory,
1140
1166
  syncMemories,
1141
1167
  shortUuid,
@@ -0,0 +1,17 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import type { Memory, MemoryScope, MemoryCategory } from "../types/index.js";
3
+ export interface PollOptions {
4
+ interval_ms?: number;
5
+ scope?: MemoryScope;
6
+ category?: MemoryCategory;
7
+ agent_id?: string;
8
+ project_id?: string;
9
+ on_memories: (memories: Memory[]) => void;
10
+ on_error?: (error: Error) => void;
11
+ db?: Database;
12
+ }
13
+ export interface PollHandle {
14
+ stop: () => void;
15
+ }
16
+ export declare function startPolling(opts: PollOptions): PollHandle;
17
+ //# sourceMappingURL=poll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../src/lib/poll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAQ7E,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAClC,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAMD,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CA0F1D"}
@@ -0,0 +1,18 @@
1
+ import { Database } from "bun:sqlite";
2
+ import type { Project } from "../types/index.js";
3
+ /**
4
+ * Auto-detect the current project from cwd's git root.
5
+ * - Finds the git root directory
6
+ * - Extracts the repo name from the directory basename
7
+ * - Checks if a project is already registered by path
8
+ * - If not, registers it automatically
9
+ * - Returns the Project object, or null if not in a git repo
10
+ *
11
+ * Results are cached for the lifetime of the process.
12
+ */
13
+ export declare function detectProject(db?: Database): Project | null;
14
+ /**
15
+ * Reset the cached project (useful for tests).
16
+ */
17
+ export declare function resetProjectCache(): void;
18
+ //# sourceMappingURL=project-detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-detect.d.ts","sourceRoot":"","sources":["../../src/lib/project-detect.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAkBjD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CA0B3D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
package/dist/mcp/index.js CHANGED
@@ -4577,6 +4577,31 @@ function listAgents(db) {
4577
4577
  const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
4578
4578
  return rows.map(parseAgentRow);
4579
4579
  }
4580
+ function updateAgent(id, updates, db) {
4581
+ const d = db || getDatabase();
4582
+ const agent = getAgent(id, d);
4583
+ if (!agent)
4584
+ return null;
4585
+ const timestamp = now();
4586
+ if (updates.name && updates.name !== agent.name) {
4587
+ const existing = d.query("SELECT id FROM agents WHERE name = ? AND id != ?").get(updates.name, agent.id);
4588
+ if (existing) {
4589
+ throw new Error(`Agent name already taken: ${updates.name}`);
4590
+ }
4591
+ d.run("UPDATE agents SET name = ? WHERE id = ?", [updates.name, agent.id]);
4592
+ }
4593
+ if (updates.description !== undefined) {
4594
+ d.run("UPDATE agents SET description = ? WHERE id = ?", [updates.description, agent.id]);
4595
+ }
4596
+ if (updates.role !== undefined) {
4597
+ d.run("UPDATE agents SET role = ? WHERE id = ?", [updates.role, agent.id]);
4598
+ }
4599
+ if (updates.metadata !== undefined) {
4600
+ d.run("UPDATE agents SET metadata = ? WHERE id = ?", [JSON.stringify(updates.metadata), agent.id]);
4601
+ }
4602
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
4603
+ return getAgent(agent.id, d);
4604
+ }
4580
4605
 
4581
4606
  // src/db/projects.ts
4582
4607
  function parseProjectRow(row) {
@@ -4622,11 +4647,211 @@ function listProjects(db) {
4622
4647
  return rows.map(parseProjectRow);
4623
4648
  }
4624
4649
 
4650
+ // src/lib/search.ts
4651
+ function parseMemoryRow2(row) {
4652
+ return {
4653
+ id: row["id"],
4654
+ key: row["key"],
4655
+ value: row["value"],
4656
+ category: row["category"],
4657
+ scope: row["scope"],
4658
+ summary: row["summary"] || null,
4659
+ tags: JSON.parse(row["tags"] || "[]"),
4660
+ importance: row["importance"],
4661
+ source: row["source"],
4662
+ status: row["status"],
4663
+ pinned: !!row["pinned"],
4664
+ agent_id: row["agent_id"] || null,
4665
+ project_id: row["project_id"] || null,
4666
+ session_id: row["session_id"] || null,
4667
+ metadata: JSON.parse(row["metadata"] || "{}"),
4668
+ access_count: row["access_count"],
4669
+ version: row["version"],
4670
+ expires_at: row["expires_at"] || null,
4671
+ created_at: row["created_at"],
4672
+ updated_at: row["updated_at"],
4673
+ accessed_at: row["accessed_at"] || null
4674
+ };
4675
+ }
4676
+ function determineMatchType(memory, queryLower) {
4677
+ if (memory.key.toLowerCase() === queryLower)
4678
+ return "exact";
4679
+ if (memory.tags.some((t) => t.toLowerCase() === queryLower))
4680
+ return "tag";
4681
+ return "fuzzy";
4682
+ }
4683
+ function computeScore(memory, queryLower) {
4684
+ let score = 0;
4685
+ const keyLower = memory.key.toLowerCase();
4686
+ if (keyLower === queryLower) {
4687
+ score += 10;
4688
+ } else if (keyLower.includes(queryLower)) {
4689
+ score += 7;
4690
+ }
4691
+ if (memory.tags.some((t) => t.toLowerCase() === queryLower)) {
4692
+ score += 6;
4693
+ }
4694
+ if (memory.summary && memory.summary.toLowerCase().includes(queryLower)) {
4695
+ score += 4;
4696
+ }
4697
+ if (memory.value.toLowerCase().includes(queryLower)) {
4698
+ score += 3;
4699
+ }
4700
+ return score;
4701
+ }
4702
+ function searchMemories(query, filter, db) {
4703
+ const d = db || getDatabase();
4704
+ const queryLower = query.toLowerCase();
4705
+ const queryParam = `%${query}%`;
4706
+ const conditions = [];
4707
+ const params = [];
4708
+ conditions.push("m.status = 'active'");
4709
+ conditions.push("(m.expires_at IS NULL OR m.expires_at >= datetime('now'))");
4710
+ conditions.push(`(m.key LIKE ? OR m.value LIKE ? OR m.summary LIKE ? OR m.id IN (SELECT memory_id FROM memory_tags WHERE tag LIKE ?))`);
4711
+ params.push(queryParam, queryParam, queryParam, queryParam);
4712
+ if (filter) {
4713
+ if (filter.scope) {
4714
+ if (Array.isArray(filter.scope)) {
4715
+ conditions.push(`m.scope IN (${filter.scope.map(() => "?").join(",")})`);
4716
+ params.push(...filter.scope);
4717
+ } else {
4718
+ conditions.push("m.scope = ?");
4719
+ params.push(filter.scope);
4720
+ }
4721
+ }
4722
+ if (filter.category) {
4723
+ if (Array.isArray(filter.category)) {
4724
+ conditions.push(`m.category IN (${filter.category.map(() => "?").join(",")})`);
4725
+ params.push(...filter.category);
4726
+ } else {
4727
+ conditions.push("m.category = ?");
4728
+ params.push(filter.category);
4729
+ }
4730
+ }
4731
+ if (filter.source) {
4732
+ if (Array.isArray(filter.source)) {
4733
+ conditions.push(`m.source IN (${filter.source.map(() => "?").join(",")})`);
4734
+ params.push(...filter.source);
4735
+ } else {
4736
+ conditions.push("m.source = ?");
4737
+ params.push(filter.source);
4738
+ }
4739
+ }
4740
+ if (filter.status) {
4741
+ conditions.shift();
4742
+ if (Array.isArray(filter.status)) {
4743
+ conditions.push(`m.status IN (${filter.status.map(() => "?").join(",")})`);
4744
+ params.push(...filter.status);
4745
+ } else {
4746
+ conditions.push("m.status = ?");
4747
+ params.push(filter.status);
4748
+ }
4749
+ }
4750
+ if (filter.project_id) {
4751
+ conditions.push("m.project_id = ?");
4752
+ params.push(filter.project_id);
4753
+ }
4754
+ if (filter.agent_id) {
4755
+ conditions.push("m.agent_id = ?");
4756
+ params.push(filter.agent_id);
4757
+ }
4758
+ if (filter.session_id) {
4759
+ conditions.push("m.session_id = ?");
4760
+ params.push(filter.session_id);
4761
+ }
4762
+ if (filter.min_importance) {
4763
+ conditions.push("m.importance >= ?");
4764
+ params.push(filter.min_importance);
4765
+ }
4766
+ if (filter.pinned !== undefined) {
4767
+ conditions.push("m.pinned = ?");
4768
+ params.push(filter.pinned ? 1 : 0);
4769
+ }
4770
+ if (filter.tags && filter.tags.length > 0) {
4771
+ for (const tag of filter.tags) {
4772
+ conditions.push("m.id IN (SELECT memory_id FROM memory_tags WHERE tag = ?)");
4773
+ params.push(tag);
4774
+ }
4775
+ }
4776
+ }
4777
+ const sql = `SELECT m.* FROM memories m WHERE ${conditions.join(" AND ")}`;
4778
+ const rows = d.query(sql).all(...params);
4779
+ const scored = [];
4780
+ for (const row of rows) {
4781
+ const memory = parseMemoryRow2(row);
4782
+ const rawScore = computeScore(memory, queryLower);
4783
+ if (rawScore === 0)
4784
+ continue;
4785
+ const weightedScore = rawScore * memory.importance / 10;
4786
+ const matchType = determineMatchType(memory, queryLower);
4787
+ scored.push({
4788
+ memory,
4789
+ score: weightedScore,
4790
+ match_type: matchType
4791
+ });
4792
+ }
4793
+ scored.sort((a, b) => {
4794
+ if (b.score !== a.score)
4795
+ return b.score - a.score;
4796
+ return b.memory.importance - a.memory.importance;
4797
+ });
4798
+ const offset = filter?.offset ?? 0;
4799
+ const limit = filter?.limit ?? scored.length;
4800
+ return scored.slice(offset, offset + limit);
4801
+ }
4802
+
4803
+ // src/lib/project-detect.ts
4804
+ import { existsSync as existsSync2 } from "fs";
4805
+ import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
4806
+ function findGitRoot2(startDir) {
4807
+ let dir = resolve2(startDir);
4808
+ while (true) {
4809
+ if (existsSync2(join2(dir, ".git")))
4810
+ return dir;
4811
+ const parent = dirname2(dir);
4812
+ if (parent === dir)
4813
+ break;
4814
+ dir = parent;
4815
+ }
4816
+ return null;
4817
+ }
4818
+ var _cachedProject = undefined;
4819
+ function detectProject(db) {
4820
+ if (_cachedProject !== undefined)
4821
+ return _cachedProject;
4822
+ const d = db || getDatabase();
4823
+ const cwd = process.cwd();
4824
+ const gitRoot = findGitRoot2(cwd);
4825
+ if (!gitRoot) {
4826
+ _cachedProject = null;
4827
+ return null;
4828
+ }
4829
+ const repoName = basename(gitRoot);
4830
+ const absPath = resolve2(gitRoot);
4831
+ const existing = getProject(absPath, d);
4832
+ if (existing) {
4833
+ _cachedProject = existing;
4834
+ return existing;
4835
+ }
4836
+ const project = registerProject(repoName, absPath, undefined, undefined, d);
4837
+ _cachedProject = project;
4838
+ return project;
4839
+ }
4840
+
4625
4841
  // src/mcp/index.ts
4626
4842
  var server = new McpServer({
4627
4843
  name: "mementos",
4628
4844
  version: "0.1.0"
4629
4845
  });
4846
+ var _autoProjectInitialized = false;
4847
+ function ensureAutoProject() {
4848
+ if (_autoProjectInitialized)
4849
+ return;
4850
+ _autoProjectInitialized = true;
4851
+ try {
4852
+ detectProject();
4853
+ } catch {}
4854
+ }
4630
4855
  function formatError(error) {
4631
4856
  if (error instanceof VersionConflictError)
4632
4857
  return `Version conflict: ${error.message}`;
@@ -4686,17 +4911,18 @@ server.tool("memory_save", "Save a memory (create or upsert). Use scope 'global'
4686
4911
  value: exports_external.string().describe("Memory content/value"),
4687
4912
  scope: exports_external.enum(["global", "shared", "private"]).optional().describe("Memory scope (default: private)"),
4688
4913
  category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional().describe("Memory category (default: knowledge)"),
4689
- importance: exports_external.number().min(1).max(10).optional().describe("Importance 1-10 (default: 5)"),
4914
+ importance: exports_external.coerce.number().min(1).max(10).optional().describe("Importance 1-10 (default: 5)"),
4690
4915
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for categorization"),
4691
4916
  summary: exports_external.string().optional().describe("Brief summary of the memory"),
4692
4917
  agent_id: exports_external.string().optional().describe("Agent ID (for scoping)"),
4693
4918
  project_id: exports_external.string().optional().describe("Project ID (for scoping)"),
4694
4919
  session_id: exports_external.string().optional().describe("Session ID (for scoping)"),
4695
- ttl_ms: exports_external.number().optional().describe("Time-to-live in milliseconds"),
4920
+ ttl_ms: exports_external.coerce.number().optional().describe("Time-to-live in milliseconds"),
4696
4921
  source: exports_external.enum(["user", "agent", "system", "auto", "imported"]).optional().describe("Source of the memory"),
4697
4922
  metadata: exports_external.record(exports_external.unknown()).optional().describe("Arbitrary metadata")
4698
4923
  }, async (args) => {
4699
4924
  try {
4925
+ ensureAutoProject();
4700
4926
  const memory = createMemory(args);
4701
4927
  return { content: [{ type: "text", text: `Memory saved:
4702
4928
  ${formatMemory(memory)}` }] };
@@ -4712,12 +4938,31 @@ server.tool("memory_recall", "Recall a memory by key. Returns the best matching
4712
4938
  session_id: exports_external.string().optional()
4713
4939
  }, async (args) => {
4714
4940
  try {
4941
+ ensureAutoProject();
4715
4942
  const memory = getMemoryByKey(args.key, args.scope, args.agent_id, args.project_id, args.session_id);
4716
- if (!memory) {
4717
- return { content: [{ type: "text", text: `No memory found for key: ${args.key}` }] };
4943
+ if (memory) {
4944
+ touchMemory(memory.id);
4945
+ return { content: [{ type: "text", text: formatMemory(memory) }] };
4718
4946
  }
4719
- touchMemory(memory.id);
4720
- return { content: [{ type: "text", text: formatMemory(memory) }] };
4947
+ const results = searchMemories(args.key, {
4948
+ scope: args.scope,
4949
+ agent_id: args.agent_id,
4950
+ project_id: args.project_id,
4951
+ session_id: args.session_id,
4952
+ limit: 1
4953
+ });
4954
+ if (results.length > 0) {
4955
+ const best = results[0];
4956
+ touchMemory(best.memory.id);
4957
+ return {
4958
+ content: [{
4959
+ type: "text",
4960
+ text: `No exact match for key "${args.key}", showing best result (score: ${best.score.toFixed(2)}, match: ${best.match_type}):
4961
+ ${formatMemory(best.memory)}`
4962
+ }]
4963
+ };
4964
+ }
4965
+ return { content: [{ type: "text", text: `No memory found for key: ${args.key}` }] };
4721
4966
  } catch (e) {
4722
4967
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
4723
4968
  }
@@ -4726,14 +4971,14 @@ server.tool("memory_list", "List memories with optional filters", {
4726
4971
  scope: exports_external.enum(["global", "shared", "private"]).optional(),
4727
4972
  category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
4728
4973
  tags: exports_external.array(exports_external.string()).optional(),
4729
- min_importance: exports_external.number().optional(),
4974
+ min_importance: exports_external.coerce.number().optional(),
4730
4975
  pinned: exports_external.boolean().optional(),
4731
4976
  agent_id: exports_external.string().optional(),
4732
4977
  project_id: exports_external.string().optional(),
4733
4978
  session_id: exports_external.string().optional(),
4734
4979
  status: exports_external.enum(["active", "archived", "expired"]).optional(),
4735
- limit: exports_external.number().optional().describe("Max results (default: 50)"),
4736
- offset: exports_external.number().optional()
4980
+ limit: exports_external.coerce.number().optional().describe("Max results (default: 50)"),
4981
+ offset: exports_external.coerce.number().optional()
4737
4982
  }, async (args) => {
4738
4983
  try {
4739
4984
  const filter = {
@@ -4757,14 +5002,14 @@ server.tool("memory_update", "Update a memory's metadata (value, importance, tag
4757
5002
  value: exports_external.string().optional(),
4758
5003
  category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
4759
5004
  scope: exports_external.enum(["global", "shared", "private"]).optional(),
4760
- importance: exports_external.number().min(1).max(10).optional(),
5005
+ importance: exports_external.coerce.number().min(1).max(10).optional(),
4761
5006
  tags: exports_external.array(exports_external.string()).optional(),
4762
5007
  summary: exports_external.string().nullable().optional(),
4763
5008
  pinned: exports_external.boolean().optional(),
4764
5009
  status: exports_external.enum(["active", "archived", "expired"]).optional(),
4765
5010
  metadata: exports_external.record(exports_external.unknown()).optional(),
4766
5011
  expires_at: exports_external.string().nullable().optional(),
4767
- version: exports_external.number().describe("Current version (for optimistic locking)")
5012
+ version: exports_external.coerce.number().describe("Current version (for optimistic locking)")
4768
5013
  }, async (args) => {
4769
5014
  try {
4770
5015
  const id = resolveId(args.id);
@@ -4809,7 +5054,7 @@ server.tool("memory_search", "Search memories by keyword across key, value, summ
4809
5054
  tags: exports_external.array(exports_external.string()).optional(),
4810
5055
  agent_id: exports_external.string().optional(),
4811
5056
  project_id: exports_external.string().optional(),
4812
- limit: exports_external.number().optional().describe("Max results (default: 20)")
5057
+ limit: exports_external.coerce.number().optional().describe("Max results (default: 20)")
4813
5058
  }, async (args) => {
4814
5059
  try {
4815
5060
  const filter = {
@@ -4898,7 +5143,7 @@ server.tool("memory_import", "Import memories from JSON array", {
4898
5143
  value: exports_external.string(),
4899
5144
  scope: exports_external.enum(["global", "shared", "private"]).optional(),
4900
5145
  category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
4901
- importance: exports_external.number().optional(),
5146
+ importance: exports_external.coerce.number().optional(),
4902
5147
  tags: exports_external.array(exports_external.string()).optional(),
4903
5148
  summary: exports_external.string().optional(),
4904
5149
  source: exports_external.enum(["user", "agent", "system", "auto", "imported"]).optional(),
@@ -4924,9 +5169,9 @@ server.tool("memory_inject", "Get formatted memory context for injection into ag
4924
5169
  agent_id: exports_external.string().optional().describe("Agent ID for scope filtering"),
4925
5170
  project_id: exports_external.string().optional().describe("Project ID for scope filtering"),
4926
5171
  session_id: exports_external.string().optional().describe("Session ID for scope filtering"),
4927
- max_tokens: exports_external.number().optional().describe("Max approximate token budget (default: 500)"),
5172
+ max_tokens: exports_external.coerce.number().optional().describe("Max approximate token budget (default: 500)"),
4928
5173
  categories: exports_external.array(exports_external.enum(["preference", "fact", "knowledge", "history"])).optional(),
4929
- min_importance: exports_external.number().optional().describe("Minimum importance threshold (default: 3)")
5174
+ min_importance: exports_external.coerce.number().optional().describe("Minimum importance threshold (default: 3)")
4930
5175
  }, async (args) => {
4931
5176
  try {
4932
5177
  const maxTokens = args.max_tokens || 500;
@@ -5059,6 +5304,35 @@ Last seen: ${agent.last_seen_at}`
5059
5304
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
5060
5305
  }
5061
5306
  });
5307
+ server.tool("update_agent", "Update an agent's name, description, role, or metadata. Agents can update themselves.", {
5308
+ id: exports_external.string().describe("Agent ID or name"),
5309
+ name: exports_external.string().optional().describe("New agent name"),
5310
+ description: exports_external.string().optional().describe("New description"),
5311
+ role: exports_external.string().optional().describe("New role"),
5312
+ metadata: exports_external.record(exports_external.unknown()).optional().describe("Updated metadata")
5313
+ }, async (args) => {
5314
+ try {
5315
+ const { id, ...updates } = args;
5316
+ const agent = updateAgent(id, updates);
5317
+ if (!agent) {
5318
+ return { content: [{ type: "text", text: `Agent not found: ${id}` }] };
5319
+ }
5320
+ return {
5321
+ content: [{
5322
+ type: "text",
5323
+ text: `Agent updated:
5324
+ ID: ${agent.id}
5325
+ Name: ${agent.name}
5326
+ Description: ${agent.description || "-"}
5327
+ Role: ${agent.role || "agent"}
5328
+ Metadata: ${JSON.stringify(agent.metadata)}
5329
+ Last seen: ${agent.last_seen_at}`
5330
+ }]
5331
+ };
5332
+ } catch (e) {
5333
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
5334
+ }
5335
+ });
5062
5336
  server.tool("register_project", "Register a project for memory scoping", {
5063
5337
  name: exports_external.string().describe("Project name"),
5064
5338
  path: exports_external.string().describe("Absolute path to project"),
@@ -5108,7 +5382,7 @@ server.tool("bulk_forget", "Delete multiple memories by IDs", {
5108
5382
  });
5109
5383
  server.tool("bulk_update", "Update multiple memories with the same changes", {
5110
5384
  ids: exports_external.array(exports_external.string()).describe("Memory IDs to update"),
5111
- importance: exports_external.number().min(1).max(10).optional(),
5385
+ importance: exports_external.coerce.number().min(1).max(10).optional(),
5112
5386
  tags: exports_external.array(exports_external.string()).optional(),
5113
5387
  pinned: exports_external.boolean().optional(),
5114
5388
  category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
@@ -5142,7 +5416,7 @@ server.tool("memory_context", "Get all memories relevant to the current context.
5142
5416
  agent_id: exports_external.string().optional(),
5143
5417
  project_id: exports_external.string().optional(),
5144
5418
  scope: exports_external.enum(["global", "shared", "private"]).optional().describe("Limit to specific scope"),
5145
- limit: exports_external.number().optional().describe("Max memories (default: 30)")
5419
+ limit: exports_external.coerce.number().optional().describe("Max memories (default: 30)")
5146
5420
  }, async (args) => {
5147
5421
  try {
5148
5422
  const filter = {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAslBH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkD9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAslBH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAoG9C"}
@@ -1158,6 +1158,42 @@ function startServer(port) {
1158
1158
  if (pathname === "/api/health" || pathname === "/health") {
1159
1159
  return json({ status: "ok", version: "0.1.0" });
1160
1160
  }
1161
+ if (pathname === "/api/memories/stream" && req.method === "GET") {
1162
+ const stream = new ReadableStream({
1163
+ start(controller) {
1164
+ const encoder = new TextEncoder;
1165
+ let lastSeen = new Date().toISOString();
1166
+ const send = (data) => {
1167
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
1168
+
1169
+ `));
1170
+ };
1171
+ send({ type: "connected", timestamp: lastSeen });
1172
+ const interval = setInterval(() => {
1173
+ try {
1174
+ const db = getDatabase();
1175
+ const rows = db.query("SELECT * FROM memories WHERE updated_at > ? OR created_at > ? ORDER BY updated_at DESC LIMIT 50").all(lastSeen, lastSeen);
1176
+ if (rows.length > 0) {
1177
+ lastSeen = new Date().toISOString();
1178
+ send({ type: "memories", data: rows, count: rows.length });
1179
+ }
1180
+ } catch {}
1181
+ }, 1000);
1182
+ req.signal.addEventListener("abort", () => {
1183
+ clearInterval(interval);
1184
+ controller.close();
1185
+ });
1186
+ }
1187
+ });
1188
+ return new Response(stream, {
1189
+ headers: {
1190
+ "Content-Type": "text/event-stream",
1191
+ "Cache-Control": "no-cache",
1192
+ Connection: "keep-alive",
1193
+ ...CORS_HEADERS
1194
+ }
1195
+ });
1196
+ }
1161
1197
  const matched = matchRoute(req.method, pathname);
1162
1198
  if (!matched) {
1163
1199
  if (pathname.startsWith("/api/")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",