@hasna/mementos 0.14.12 → 0.14.14

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/dist/cli/index.js CHANGED
@@ -15088,6 +15088,155 @@ var init_projects = __esm(() => {
15088
15088
  init_database();
15089
15089
  });
15090
15090
 
15091
+ // src/db/agents.ts
15092
+ var exports_agents = {};
15093
+ __export(exports_agents, {
15094
+ updateAgent: () => updateAgent,
15095
+ touchAgent: () => touchAgent,
15096
+ registerAgent: () => registerAgent,
15097
+ listAgentsByProject: () => listAgentsByProject,
15098
+ listAgents: () => listAgents,
15099
+ getAgent: () => getAgent
15100
+ });
15101
+ function parseAgentRow(row) {
15102
+ return {
15103
+ id: row["id"],
15104
+ name: row["name"],
15105
+ session_id: row["session_id"] || null,
15106
+ description: row["description"] || null,
15107
+ role: row["role"] || null,
15108
+ metadata: JSON.parse(row["metadata"] || "{}"),
15109
+ active_project_id: row["active_project_id"] || null,
15110
+ created_at: row["created_at"],
15111
+ last_seen_at: row["last_seen_at"]
15112
+ };
15113
+ }
15114
+ function registerAgent(name, sessionId, description, role, projectId, db) {
15115
+ const d = db || getDatabase();
15116
+ const timestamp = now();
15117
+ const normalizedName = name.trim().toLowerCase();
15118
+ if (projectId) {
15119
+ const resolvedProjectId = resolvePartialId(d, "projects", projectId);
15120
+ if (!resolvedProjectId) {
15121
+ throw new Error(`Project not found: ${projectId}`);
15122
+ }
15123
+ projectId = resolvedProjectId;
15124
+ }
15125
+ const existing = d.query("SELECT * FROM agents WHERE LOWER(name) = ?").get(normalizedName);
15126
+ if (existing) {
15127
+ const existingId = existing["id"];
15128
+ const existingSessionId = existing["session_id"] || null;
15129
+ const existingLastSeen = existing["last_seen_at"];
15130
+ if (sessionId && existingSessionId && existingSessionId !== sessionId) {
15131
+ const lastSeenMs = new Date(existingLastSeen).getTime();
15132
+ const nowMs = Date.now();
15133
+ if (nowMs - lastSeenMs < CONFLICT_WINDOW_MS) {
15134
+ throw new AgentConflictError({
15135
+ existing_id: existingId,
15136
+ existing_name: normalizedName,
15137
+ last_seen_at: existingLastSeen,
15138
+ session_hint: existingSessionId.slice(0, 8),
15139
+ working_dir: null
15140
+ });
15141
+ }
15142
+ }
15143
+ d.run("UPDATE agents SET last_seen_at = ?, session_id = ? WHERE id = ?", [
15144
+ timestamp,
15145
+ sessionId ?? existingSessionId,
15146
+ existingId
15147
+ ]);
15148
+ if (description) {
15149
+ d.run("UPDATE agents SET description = ? WHERE id = ?", [description, existingId]);
15150
+ }
15151
+ if (role) {
15152
+ d.run("UPDATE agents SET role = ? WHERE id = ?", [role, existingId]);
15153
+ }
15154
+ if (projectId !== undefined) {
15155
+ d.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [projectId, existingId]);
15156
+ }
15157
+ return getAgent(existingId, d);
15158
+ }
15159
+ const id = shortUuid();
15160
+ d.run("INSERT INTO agents (id, name, session_id, description, role, active_project_id, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, normalizedName, sessionId ?? null, description || null, role || "agent", projectId ?? null, timestamp, timestamp]);
15161
+ return getAgent(id, d);
15162
+ }
15163
+ function getAgent(idOrName, db) {
15164
+ const d = db || getDatabase();
15165
+ let row = d.query("SELECT * FROM agents WHERE id = ?").get(idOrName);
15166
+ if (row)
15167
+ return parseAgentRow(row);
15168
+ row = d.query("SELECT * FROM agents WHERE LOWER(name) = ?").get(idOrName.trim().toLowerCase());
15169
+ if (row)
15170
+ return parseAgentRow(row);
15171
+ const rows = d.query("SELECT * FROM agents WHERE id LIKE ?").all(`${idOrName}%`);
15172
+ if (rows.length === 1)
15173
+ return parseAgentRow(rows[0]);
15174
+ return null;
15175
+ }
15176
+ function listAgents(db) {
15177
+ const d = db || getDatabase();
15178
+ const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
15179
+ return rows.map(parseAgentRow);
15180
+ }
15181
+ function touchAgent(idOrName, db) {
15182
+ const d = db || getDatabase();
15183
+ const agent = getAgent(idOrName, d);
15184
+ if (!agent)
15185
+ return;
15186
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
15187
+ }
15188
+ function listAgentsByProject(projectId, db) {
15189
+ const d = db || getDatabase();
15190
+ const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
15191
+ const rows = d.query("SELECT * FROM agents WHERE active_project_id = ? ORDER BY last_seen_at DESC").all(resolvedId);
15192
+ return rows.map(parseAgentRow);
15193
+ }
15194
+ function updateAgent(id, updates, db) {
15195
+ const d = db || getDatabase();
15196
+ const agent = getAgent(id, d);
15197
+ if (!agent)
15198
+ return null;
15199
+ const timestamp = now();
15200
+ if (updates.name) {
15201
+ const normalizedNewName = updates.name.trim().toLowerCase();
15202
+ if (normalizedNewName !== agent.name) {
15203
+ const existing = d.query("SELECT id FROM agents WHERE LOWER(name) = ? AND id != ?").get(normalizedNewName, agent.id);
15204
+ if (existing) {
15205
+ throw new Error(`Agent name already taken: ${normalizedNewName}`);
15206
+ }
15207
+ d.run("UPDATE agents SET name = ? WHERE id = ?", [normalizedNewName, agent.id]);
15208
+ }
15209
+ }
15210
+ if (updates.description !== undefined) {
15211
+ d.run("UPDATE agents SET description = ? WHERE id = ?", [updates.description, agent.id]);
15212
+ }
15213
+ if (updates.role !== undefined) {
15214
+ d.run("UPDATE agents SET role = ? WHERE id = ?", [updates.role, agent.id]);
15215
+ }
15216
+ if (updates.metadata !== undefined) {
15217
+ d.run("UPDATE agents SET metadata = ? WHERE id = ?", [JSON.stringify(updates.metadata), agent.id]);
15218
+ }
15219
+ if ("active_project_id" in updates) {
15220
+ let resolvedProjectId = updates.active_project_id ?? null;
15221
+ if (resolvedProjectId) {
15222
+ const fullId = resolvePartialId(d, "projects", resolvedProjectId);
15223
+ if (!fullId) {
15224
+ throw new Error(`Project not found: ${resolvedProjectId}`);
15225
+ }
15226
+ resolvedProjectId = fullId;
15227
+ }
15228
+ d.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProjectId, agent.id]);
15229
+ }
15230
+ d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
15231
+ return getAgent(agent.id, d);
15232
+ }
15233
+ var CONFLICT_WINDOW_MS;
15234
+ var init_agents = __esm(() => {
15235
+ init_types2();
15236
+ init_database();
15237
+ CONFLICT_WINDOW_MS = 30 * 60 * 1000;
15238
+ });
15239
+
15091
15240
  // src/db/entities.ts
15092
15241
  function parseEntityRow(row) {
15093
15242
  return {
@@ -16369,155 +16518,6 @@ var init_helpers = __esm(() => {
16369
16518
  VALID_CATEGORIES = ["preference", "fact", "knowledge", "history"];
16370
16519
  });
16371
16520
 
16372
- // src/db/agents.ts
16373
- var exports_agents = {};
16374
- __export(exports_agents, {
16375
- updateAgent: () => updateAgent,
16376
- touchAgent: () => touchAgent,
16377
- registerAgent: () => registerAgent,
16378
- listAgentsByProject: () => listAgentsByProject,
16379
- listAgents: () => listAgents,
16380
- getAgent: () => getAgent
16381
- });
16382
- function parseAgentRow(row) {
16383
- return {
16384
- id: row["id"],
16385
- name: row["name"],
16386
- session_id: row["session_id"] || null,
16387
- description: row["description"] || null,
16388
- role: row["role"] || null,
16389
- metadata: JSON.parse(row["metadata"] || "{}"),
16390
- active_project_id: row["active_project_id"] || null,
16391
- created_at: row["created_at"],
16392
- last_seen_at: row["last_seen_at"]
16393
- };
16394
- }
16395
- function registerAgent(name, sessionId, description, role, projectId, db) {
16396
- const d = db || getDatabase();
16397
- const timestamp = now();
16398
- const normalizedName = name.trim().toLowerCase();
16399
- if (projectId) {
16400
- const resolvedProjectId = resolvePartialId(d, "projects", projectId);
16401
- if (!resolvedProjectId) {
16402
- throw new Error(`Project not found: ${projectId}`);
16403
- }
16404
- projectId = resolvedProjectId;
16405
- }
16406
- const existing = d.query("SELECT * FROM agents WHERE LOWER(name) = ?").get(normalizedName);
16407
- if (existing) {
16408
- const existingId = existing["id"];
16409
- const existingSessionId = existing["session_id"] || null;
16410
- const existingLastSeen = existing["last_seen_at"];
16411
- if (sessionId && existingSessionId && existingSessionId !== sessionId) {
16412
- const lastSeenMs = new Date(existingLastSeen).getTime();
16413
- const nowMs = Date.now();
16414
- if (nowMs - lastSeenMs < CONFLICT_WINDOW_MS) {
16415
- throw new AgentConflictError({
16416
- existing_id: existingId,
16417
- existing_name: normalizedName,
16418
- last_seen_at: existingLastSeen,
16419
- session_hint: existingSessionId.slice(0, 8),
16420
- working_dir: null
16421
- });
16422
- }
16423
- }
16424
- d.run("UPDATE agents SET last_seen_at = ?, session_id = ? WHERE id = ?", [
16425
- timestamp,
16426
- sessionId ?? existingSessionId,
16427
- existingId
16428
- ]);
16429
- if (description) {
16430
- d.run("UPDATE agents SET description = ? WHERE id = ?", [description, existingId]);
16431
- }
16432
- if (role) {
16433
- d.run("UPDATE agents SET role = ? WHERE id = ?", [role, existingId]);
16434
- }
16435
- if (projectId !== undefined) {
16436
- d.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [projectId, existingId]);
16437
- }
16438
- return getAgent(existingId, d);
16439
- }
16440
- const id = shortUuid();
16441
- d.run("INSERT INTO agents (id, name, session_id, description, role, active_project_id, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [id, normalizedName, sessionId ?? null, description || null, role || "agent", projectId ?? null, timestamp, timestamp]);
16442
- return getAgent(id, d);
16443
- }
16444
- function getAgent(idOrName, db) {
16445
- const d = db || getDatabase();
16446
- let row = d.query("SELECT * FROM agents WHERE id = ?").get(idOrName);
16447
- if (row)
16448
- return parseAgentRow(row);
16449
- row = d.query("SELECT * FROM agents WHERE LOWER(name) = ?").get(idOrName.trim().toLowerCase());
16450
- if (row)
16451
- return parseAgentRow(row);
16452
- const rows = d.query("SELECT * FROM agents WHERE id LIKE ?").all(`${idOrName}%`);
16453
- if (rows.length === 1)
16454
- return parseAgentRow(rows[0]);
16455
- return null;
16456
- }
16457
- function listAgents(db) {
16458
- const d = db || getDatabase();
16459
- const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
16460
- return rows.map(parseAgentRow);
16461
- }
16462
- function touchAgent(idOrName, db) {
16463
- const d = db || getDatabase();
16464
- const agent = getAgent(idOrName, d);
16465
- if (!agent)
16466
- return;
16467
- d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
16468
- }
16469
- function listAgentsByProject(projectId, db) {
16470
- const d = db || getDatabase();
16471
- const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
16472
- const rows = d.query("SELECT * FROM agents WHERE active_project_id = ? ORDER BY last_seen_at DESC").all(resolvedId);
16473
- return rows.map(parseAgentRow);
16474
- }
16475
- function updateAgent(id, updates, db) {
16476
- const d = db || getDatabase();
16477
- const agent = getAgent(id, d);
16478
- if (!agent)
16479
- return null;
16480
- const timestamp = now();
16481
- if (updates.name) {
16482
- const normalizedNewName = updates.name.trim().toLowerCase();
16483
- if (normalizedNewName !== agent.name) {
16484
- const existing = d.query("SELECT id FROM agents WHERE LOWER(name) = ? AND id != ?").get(normalizedNewName, agent.id);
16485
- if (existing) {
16486
- throw new Error(`Agent name already taken: ${normalizedNewName}`);
16487
- }
16488
- d.run("UPDATE agents SET name = ? WHERE id = ?", [normalizedNewName, agent.id]);
16489
- }
16490
- }
16491
- if (updates.description !== undefined) {
16492
- d.run("UPDATE agents SET description = ? WHERE id = ?", [updates.description, agent.id]);
16493
- }
16494
- if (updates.role !== undefined) {
16495
- d.run("UPDATE agents SET role = ? WHERE id = ?", [updates.role, agent.id]);
16496
- }
16497
- if (updates.metadata !== undefined) {
16498
- d.run("UPDATE agents SET metadata = ? WHERE id = ?", [JSON.stringify(updates.metadata), agent.id]);
16499
- }
16500
- if ("active_project_id" in updates) {
16501
- let resolvedProjectId = updates.active_project_id ?? null;
16502
- if (resolvedProjectId) {
16503
- const fullId = resolvePartialId(d, "projects", resolvedProjectId);
16504
- if (!fullId) {
16505
- throw new Error(`Project not found: ${resolvedProjectId}`);
16506
- }
16507
- resolvedProjectId = fullId;
16508
- }
16509
- d.run("UPDATE agents SET active_project_id = ? WHERE id = ?", [resolvedProjectId, agent.id]);
16510
- }
16511
- d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
16512
- return getAgent(agent.id, d);
16513
- }
16514
- var CONFLICT_WINDOW_MS;
16515
- var init_agents = __esm(() => {
16516
- init_types2();
16517
- init_database();
16518
- CONFLICT_WINDOW_MS = 30 * 60 * 1000;
16519
- });
16520
-
16521
16521
  // src/lib/poll.ts
16522
16522
  var exports_poll = {};
16523
16523
  __export(exports_poll, {
@@ -20957,6 +20957,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
20957
20957
  init_database();
20958
20958
  init_memories();
20959
20959
  init_projects();
20960
+ init_agents();
20960
20961
  init_search();
20961
20962
  import chalk2 from "chalk";
20962
20963
  import { resolve as resolve3 } from "path";
@@ -21048,6 +21049,11 @@ function registerMemoryCommands(program2) {
21048
21049
  }
21049
21050
  const explicitTags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined;
21050
21051
  const mergedTags = explicitTags ? explicitTags : templateDefaults?.tags && templateDefaults.tags.length > 0 ? templateDefaults.tags : undefined;
21052
+ let resolvedAgentId;
21053
+ if (globalOpts.agent) {
21054
+ const ag = getAgent(globalOpts.agent);
21055
+ resolvedAgentId = ag?.id;
21056
+ }
21051
21057
  const input = {
21052
21058
  key,
21053
21059
  value,
@@ -21058,7 +21064,7 @@ function registerMemoryCommands(program2) {
21058
21064
  summary: opts.summary,
21059
21065
  ttl_ms: opts.ttl ? parseDuration(opts.ttl) : undefined,
21060
21066
  source: opts.source,
21061
- agent_id: globalOpts.agent,
21067
+ agent_id: resolvedAgentId,
21062
21068
  session_id: globalOpts.session
21063
21069
  };
21064
21070
  if (globalOpts.project) {
package/dist/mcp/index.js CHANGED
@@ -29023,57 +29023,6 @@ function registerUtilityTools(server) {
29023
29023
  return { content: [{ type: "text", text: formatError11(e) }], isError: true };
29024
29024
  }
29025
29025
  });
29026
- server.tool("session_extract", "Extract memories from a session summary. Auto-creates structured memories from title, topics, notes.", {
29027
- session_id: exports_external2.string(),
29028
- title: exports_external2.string().optional(),
29029
- project: exports_external2.string().optional(),
29030
- model: exports_external2.string().optional(),
29031
- messages: exports_external2.coerce.number().optional(),
29032
- key_topics: exports_external2.array(exports_external2.string()).optional(),
29033
- summary: exports_external2.string().optional(),
29034
- agent_id: exports_external2.string().optional(),
29035
- project_id: exports_external2.string().optional()
29036
- }, async (args) => {
29037
- try {
29038
- let saveExtracted = function(key, value, category, importance) {
29039
- try {
29040
- const mem = createMemory({
29041
- key,
29042
- value,
29043
- category,
29044
- scope: "shared",
29045
- importance,
29046
- source: "auto",
29047
- agent_id,
29048
- project_id,
29049
- session_id
29050
- });
29051
- created.push(mem.id);
29052
- } catch {}
29053
- };
29054
- const { session_id, title, project, model, messages, key_topics, summary, agent_id, project_id } = args;
29055
- const created = [];
29056
- if (title) {
29057
- const meta = [project && `project: ${project}`, model && `model: ${model}`, messages && `messages: ${messages}`].filter(Boolean).join(", ");
29058
- saveExtracted(`session-${session_id}-summary`, `${title}${meta ? ` (${meta})` : ""}`, "history", 6);
29059
- }
29060
- if (key_topics?.length) {
29061
- saveExtracted(`session-${session_id}-topics`, `Key topics: ${key_topics.join(", ")}`, "knowledge", 5);
29062
- }
29063
- if (summary) {
29064
- saveExtracted(`session-${session_id}-notes`, summary, "knowledge", 7);
29065
- }
29066
- return {
29067
- content: [{
29068
- type: "text",
29069
- text: `Extracted ${created.length} memor${created.length === 1 ? "y" : "ies"} from session ${session_id}.${created.length > 0 ? `
29070
- IDs: ${created.join(", ")}` : ""}`
29071
- }]
29072
- };
29073
- } catch (e) {
29074
- return { content: [{ type: "text", text: formatError11(e) }], isError: true };
29075
- }
29076
- });
29077
29026
  server.tool("memory_briefing", "Lightweight delta briefing: what memories changed since an agent's last session. Use at session start instead of memory_context to avoid re-reading everything.", {
29078
29027
  agent_id: exports_external2.string().optional().describe("Agent ID or name. If provided, defaults since to agent's last_seen_at."),
29079
29028
  since: exports_external2.string().optional().describe("ISO 8601 timestamp. Defaults to agent's last_seen_at if agent_id provided, otherwise 24h ago."),
@@ -29525,23 +29474,6 @@ ${lines.join(`
29525
29474
  return { content: [{ type: "text", text: formatError12(e) }], isError: true };
29526
29475
  }
29527
29476
  });
29528
- server.tool("entity_disambiguate", "Find potential duplicate entities by name similarity (trigram). Returns pairs above the threshold within same type+project.", {
29529
- threshold: exports_external2.coerce.number().min(0).max(1).optional().describe("Similarity threshold 0-1 (default 0.8)")
29530
- }, async (args) => {
29531
- try {
29532
- const { findDuplicateEntities: findDuplicateEntities2 } = await Promise.resolve().then(() => (init_entities(), exports_entities));
29533
- const pairs = findDuplicateEntities2(args.threshold ?? 0.8);
29534
- if (pairs.length === 0) {
29535
- return { content: [{ type: "text", text: "No duplicate entities found." }] };
29536
- }
29537
- const lines = pairs.map((p) => `${p.entity_a.name} <-> ${p.entity_b.name} [${p.entity_a.type}] similarity=${p.similarity.toFixed(2)}`);
29538
- return { content: [{ type: "text", text: `Found ${pairs.length} potential duplicate(s):
29539
- ${lines.join(`
29540
- `)}` }] };
29541
- } catch (e) {
29542
- return { content: [{ type: "text", text: formatError12(e) }], isError: true };
29543
- }
29544
- });
29545
29477
  server.tool("memory_compress", "Compress multiple memories into a single summary memory. Uses LLM if available, otherwise truncates.", {
29546
29478
  memory_ids: exports_external2.array(exports_external2.string()).describe("Memory IDs to compress"),
29547
29479
  max_length: exports_external2.coerce.number().optional().describe("Max chars for compression (default 500)")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.14.12",
3
+ "version": "0.14.14",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",