mcp-agents-memory 0.6.2 → 0.7.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.
package/README.md CHANGED
@@ -102,6 +102,8 @@ Claude Desktop / Claude Code / any MCP-aware client:
102
102
 
103
103
  `AGENT_PLATFORM` is recorded as the Curator's harness identity on every memory_add call. The Curator's model is captured per-call (defaulting to the Producer's author_model) — set explicitly via the curator_model argument when an orchestrator saves memories on behalf of a different model (e.g. delegating to a subagent). This avoids the staleness that env-static model values would introduce when /model swaps mid-session.
104
104
 
105
+ `agent_key` (optional): Agent persona key for multi-persona harnesses (OpenClaw, Hermes, Opencode). Single-persona setups can ignore — `AGENT_KEY` env is the default. Applies to `memory_add`, `memory_save_skill`, and `memory_curator_run`.
106
+
105
107
  ### Cross-machine memory
106
108
 
107
109
  On a second computer, run `npm i -g mcp-agents-memory` and `mcp-agents-memory setup` pointing to the **same** `DATABASE_URL`. Memory shares automatically — the database is the source of truth and the MCP server is stateless.
package/build/index.js CHANGED
@@ -116026,22 +116026,21 @@ ${JSON.stringify(toAudit)}`,
116026
116026
  }
116027
116027
  var MODEL_CACHE = /* @__PURE__ */ new Map();
116028
116028
  var PLATFORM_CACHE = /* @__PURE__ */ new Map();
116029
- var DEFAULT_TRUST_WEIGHT = 0.8;
116030
116029
  async function resolvePlatform(name) {
116031
116030
  if (!name) return null;
116032
116031
  const key = name.toLowerCase();
116033
116032
  if (PLATFORM_CACHE.has(key)) return PLATFORM_CACHE.get(key);
116034
- const sel = await db.query("SELECT id, name, trust_weight FROM platforms WHERE LOWER(name) = $1 LIMIT 1", [key]);
116033
+ const sel = await db.query("SELECT id, name FROM platforms WHERE LOWER(name) = $1 LIMIT 1", [key]);
116035
116034
  if (sel.rows.length > 0) {
116036
- const r2 = { id: sel.rows[0].id, name: sel.rows[0].name, trust_weight: parseFloat(sel.rows[0].trust_weight) };
116035
+ const r2 = { id: sel.rows[0].id, name: sel.rows[0].name };
116037
116036
  PLATFORM_CACHE.set(key, r2);
116038
116037
  return r2;
116039
116038
  }
116040
116039
  const ins = await db.query(
116041
- "INSERT INTO platforms (name, trust_weight) VALUES ($1, 1.00) ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name RETURNING id, name, trust_weight",
116040
+ "INSERT INTO platforms (name) VALUES ($1) ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name RETURNING id, name",
116042
116041
  [name]
116043
116042
  );
116044
- const r = { id: ins.rows[0].id, name: ins.rows[0].name, trust_weight: parseFloat(ins.rows[0].trust_weight) };
116043
+ const r = { id: ins.rows[0].id, name: ins.rows[0].name };
116045
116044
  PLATFORM_CACHE.set(key, r);
116046
116045
  console.error(`\u2728 [Librarian] Auto-registered platform "${name}"`);
116047
116046
  return r;
@@ -116052,34 +116051,46 @@ async function resolveModel(name) {
116052
116051
  if (MODEL_CACHE.has(key)) return MODEL_CACHE.get(key);
116053
116052
  try {
116054
116053
  const res = await db.query(
116055
- `SELECT id, model_name, trust_weight
116054
+ `SELECT id, model_name
116056
116055
  FROM models
116057
116056
  WHERE LOWER(model_name) = $1
116058
116057
  OR LOWER(metadata->>'alias') = $1
116059
116058
  LIMIT 1`,
116060
116059
  [key]
116061
116060
  );
116062
- if (res.rows.length === 0) {
116063
- console.error(`\u26A0\uFE0F [Librarian] Unknown model "${name}" \u2014 defaulting trust_weight ${DEFAULT_TRUST_WEIGHT}, author_model_id=NULL`);
116061
+ if (res.rows.length > 0) {
116062
+ const resolved2 = {
116063
+ id: res.rows[0].id,
116064
+ model_name: res.rows[0].model_name
116065
+ };
116066
+ MODEL_CACHE.set(key, resolved2);
116067
+ return resolved2;
116068
+ }
116069
+ const provider = inferProvider(name);
116070
+ if (!provider) {
116071
+ console.error(`\u26A0\uFE0F [Librarian] Unknown model "${name}" \u2014 provider not inferable, author_model_id=NULL`);
116064
116072
  MODEL_CACHE.set(key, null);
116065
116073
  return null;
116066
116074
  }
116075
+ const ins = await db.query(
116076
+ `INSERT INTO models (provider, model_name, metadata)
116077
+ VALUES ($1, $2, '{}'::jsonb)
116078
+ ON CONFLICT (model_name) DO UPDATE SET model_name = EXCLUDED.model_name
116079
+ RETURNING id, model_name`,
116080
+ [provider, name]
116081
+ );
116067
116082
  const resolved = {
116068
- id: res.rows[0].id,
116069
- trust_weight: parseFloat(res.rows[0].trust_weight),
116070
- model_name: res.rows[0].model_name
116083
+ id: ins.rows[0].id,
116084
+ model_name: ins.rows[0].model_name
116071
116085
  };
116072
116086
  MODEL_CACHE.set(key, resolved);
116087
+ console.error(`\u2728 [Librarian] Auto-registered model "${name}" (provider=${provider})`);
116073
116088
  return resolved;
116074
116089
  } catch (err) {
116075
116090
  console.error(`\u274C [Librarian] resolveModel failed for "${name}":`, err);
116076
116091
  return null;
116077
116092
  }
116078
116093
  }
116079
- function computeEffectiveConfidence(confidence, trustWeight) {
116080
- const raw = confidence / 10 * trustWeight;
116081
- return Math.round(raw * 100) / 100;
116082
- }
116083
116094
  async function resolveContradiction(newFact, subjectId, existingEmbedding) {
116084
116095
  try {
116085
116096
  const embedding = existingEmbedding || await generateEmbedding(newFact.content);
@@ -116220,8 +116231,6 @@ async function processBatch(text, subjectId, projectId, rawSource, provenance =
116220
116231
  }
116221
116232
  const resolvedModel = await resolveModel(provenance.author_model);
116222
116233
  const resolvedPlatform = await resolvePlatform(provenance.platform);
116223
- const trustWeight = resolvedModel?.trust_weight ?? DEFAULT_TRUST_WEIGHT;
116224
- const effectiveConfidence = computeEffectiveConfidence(fact.confidence, trustWeight);
116225
116234
  const auditedStatus = auditResult ? tierToStatus(auditResult.validation_tier) : null;
116226
116235
  const validationStatus = auditedStatus ?? (fact.importance > 7 ? "pending" : "valid");
116227
116236
  const insertRes = await db.query(
@@ -116229,8 +116238,8 @@ async function processBatch(text, subjectId, projectId, rawSource, provenance =
116229
116238
  subject_id, project_subject_id, content, fact_type,
116230
116239
  confidence, importance, tags, embedding, validation_status,
116231
116240
  author_model, platform, session_id,
116232
- agent_platform, agent_model,
116233
- author_model_id, platform_id, effective_confidence, source
116241
+ agent_platform, agent_model, agent_curator_id,
116242
+ author_model_id, platform_id, source
116234
116243
  )
116235
116244
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
116236
116245
  RETURNING id`,
@@ -116249,9 +116258,9 @@ async function processBatch(text, subjectId, projectId, rawSource, provenance =
116249
116258
  provenance.session_id,
116250
116259
  provenance.agent_platform,
116251
116260
  provenance.agent_model,
116261
+ provenance.agent_curator_id ?? null,
116252
116262
  resolvedModel?.id ?? null,
116253
116263
  resolvedPlatform?.id ?? null,
116254
- effectiveConfidence,
116255
116264
  provenance.source ?? "librarian"
116256
116265
  ]
116257
116266
  );
@@ -116398,7 +116407,7 @@ async function recordSkillExposure(skillIds) {
116398
116407
  [skillIds]
116399
116408
  );
116400
116409
  }
116401
- async function updateOrCreateSkill(candidate, audit) {
116410
+ async function updateOrCreateSkill(candidate, audit, agentCuratorId) {
116402
116411
  const persisted = getPersistedSkillFields(candidate, audit);
116403
116412
  const embedding = await generateEmbedding(`${candidate.title}
116404
116413
 
@@ -116425,9 +116434,9 @@ ${persisted.content}`);
116425
116434
  await client2.query(
116426
116435
  `INSERT INTO skill_changelog (
116427
116436
  skill_id, change_type, content_diff, source_memory_ids,
116428
- author_model_id, platform_id, metadata
116437
+ author_model_id, platform_id, metadata, agent_curator_id
116429
116438
  )
116430
- VALUES ($1, 'append', $2, $3, $4, $5, $6)`,
116439
+ VALUES ($1, 'append', $2, $3, $4, $5, $6, $7)`,
116431
116440
  [
116432
116441
  match.id,
116433
116442
  persisted.content,
@@ -116439,7 +116448,8 @@ ${persisted.content}`);
116439
116448
  matched_skill_id: match.id,
116440
116449
  similarity,
116441
116450
  audit: persisted.auditMetadata
116442
- })
116451
+ }),
116452
+ agentCuratorId ?? null
116443
116453
  ]
116444
116454
  );
116445
116455
  await client2.query("COMMIT");
@@ -116481,9 +116491,9 @@ ${persisted.content}`);
116481
116491
  await client2.query(
116482
116492
  `INSERT INTO skill_changelog (
116483
116493
  skill_id, change_type, content_diff, source_memory_ids,
116484
- author_model_id, platform_id, metadata
116494
+ author_model_id, platform_id, metadata, agent_curator_id
116485
116495
  )
116486
- VALUES ($1, 'branched', $2, $3, $4, $5, $6)`,
116496
+ VALUES ($1, 'branched', $2, $3, $4, $5, $6, $7)`,
116487
116497
  [
116488
116498
  skillId2,
116489
116499
  persisted.content,
@@ -116494,7 +116504,8 @@ ${persisted.content}`);
116494
116504
  branched_from_skill_id: match.id,
116495
116505
  similarity,
116496
116506
  audit: persisted.auditMetadata
116497
- })
116507
+ }),
116508
+ agentCuratorId ?? null
116498
116509
  ]
116499
116510
  );
116500
116511
  await client2.query("COMMIT");
@@ -116528,9 +116539,9 @@ ${persisted.content}`);
116528
116539
  await client2.query(
116529
116540
  `INSERT INTO skill_changelog (
116530
116541
  skill_id, change_type, content_diff, source_memory_ids,
116531
- author_model_id, platform_id, metadata
116542
+ author_model_id, platform_id, metadata, agent_curator_id
116532
116543
  )
116533
- VALUES ($1, 'created', $2, $3, $4, $5, $6)`,
116544
+ VALUES ($1, 'created', $2, $3, $4, $5, $6, $7)`,
116534
116545
  [
116535
116546
  skillId,
116536
116547
  persisted.content,
@@ -116541,7 +116552,8 @@ ${persisted.content}`);
116541
116552
  matched_skill_id: match?.id ?? null,
116542
116553
  similarity,
116543
116554
  audit: persisted.auditMetadata
116544
- })
116555
+ }),
116556
+ agentCuratorId ?? null
116545
116557
  ]
116546
116558
  );
116547
116559
  await client2.query("COMMIT");
@@ -116924,7 +116936,7 @@ async function runCurator(options = {}) {
116924
116936
  }
116925
116937
  let skillResult = null;
116926
116938
  if (!normalized.dryRun) {
116927
- skillResult = await updateOrCreateSkill(candidate, audited);
116939
+ skillResult = await updateOrCreateSkill(candidate, audited, options.agentCuratorId ?? null);
116928
116940
  result.skills_saved++;
116929
116941
  }
116930
116942
  result.candidates.push({
@@ -117511,19 +117523,22 @@ function registerTools(server) {
117511
117523
  project_key: external_exports.string().optional().describe("Project key if relevant."),
117512
117524
  author_model: external_exports.string().optional().describe("Model name or alias (e.g., 'sonnet', 'opus', 'gemini')."),
117513
117525
  curator_model: external_exports.string().optional().describe("Curator model override \u2014 the model running memory_add. Defaults to author_model (Producer == Curator solo case)."),
117526
+ agent_key: external_exports.string().optional().describe("Agent persona key (e.g., 'agent_openclaw_reviewer'). Multi-persona harnesses pass this per-call to differentiate personas in calibration data. Defaults to env AGENT_KEY."),
117514
117527
  platform: external_exports.string().optional().describe("Platform (e.g., 'antigravity', 'claude-code')."),
117515
117528
  session_id: external_exports.string().optional().describe("Unique session identifier.")
117516
117529
  }
117517
117530
  },
117518
117531
  async (args) => {
117519
117532
  const agentPlatform = process.env.AGENT_PLATFORM;
117533
+ const agentKeyRaw = args.agent_key ?? process.env.AGENT_KEY ?? null;
117534
+ const agentCuratorId = agentKeyRaw ? await getOrCreateSubject(agentKeyRaw, "agent") : null;
117520
117535
  const subjectId = await getOrCreateSubject(args.subject_key, "person");
117521
117536
  let projectId = null;
117522
117537
  if (args.project_key) {
117523
117538
  projectId = await getOrCreateSubject(args.project_key, "project");
117524
117539
  }
117525
- const authorModel = args.author_model ?? null;
117526
- const curatorModel = args.curator_model ?? args.author_model ?? null;
117540
+ const authorModel = args.author_model ?? void 0;
117541
+ const curatorModel = args.curator_model ?? args.author_model ?? void 0;
117527
117542
  const platform = args.platform ?? agentPlatform;
117528
117543
  const result = await processBatch(
117529
117544
  args.text,
@@ -117535,6 +117550,7 @@ function registerTools(server) {
117535
117550
  platform,
117536
117551
  agent_platform: agentPlatform,
117537
117552
  agent_model: curatorModel,
117553
+ agent_curator_id: agentCuratorId,
117538
117554
  session_id: args.session_id
117539
117555
  }
117540
117556
  );
@@ -117568,6 +117584,7 @@ function registerTools(server) {
117568
117584
  source_memory_ids: external_exports.array(external_exports.number()).optional().describe("Memory IDs that produced this skill."),
117569
117585
  author_model: external_exports.string().optional().describe("Model name or alias that authored the skill."),
117570
117586
  platform: external_exports.string().optional().describe("Platform where the skill was authored."),
117587
+ agent_key: external_exports.string().optional().describe("Agent persona key. See memory_add for details."),
117571
117588
  audit: external_exports.boolean().optional().default(true).describe("Run Skill Auditor before saving. Default: true.")
117572
117589
  }
117573
117590
  },
@@ -117579,8 +117596,10 @@ function registerTools(server) {
117579
117596
  author_model: args.author_model,
117580
117597
  platform: args.platform
117581
117598
  };
117599
+ const agentKeyRaw = args.agent_key ?? process.env.AGENT_KEY ?? null;
117600
+ const agentCuratorId = agentKeyRaw ? await getOrCreateSubject(agentKeyRaw, "agent") : null;
117582
117601
  const audited = args.audit !== false ? await auditSkill(candidate) : void 0;
117583
- const result = await updateOrCreateSkill(candidate, audited);
117602
+ const result = await updateOrCreateSkill(candidate, audited, agentCuratorId);
117584
117603
  const validationTier = audited?.validation_tier ?? "unvalidated";
117585
117604
  const auditReasoning = audited?.audit_reasoning ?? "audit disabled";
117586
117605
  const sourcesCount = audited?.sources.length ?? 0;
@@ -117613,10 +117632,13 @@ function registerTools(server) {
117613
117632
  min_cluster_size: external_exports.number().optional().describe("Minimum memory count per cluster."),
117614
117633
  similarity_threshold: external_exports.number().optional().describe("Minimum cosine similarity for cluster membership."),
117615
117634
  min_importance: external_exports.number().optional().describe("Minimum average importance for accepted clusters."),
117616
- max_clusters: external_exports.number().optional().describe("Maximum clusters to analyze in one run.")
117635
+ max_clusters: external_exports.number().optional().describe("Maximum clusters to analyze in one run."),
117636
+ agent_key: external_exports.string().optional().describe("Agent persona key for the curator caller. Auto-promotion loop passes none (NULL).")
117617
117637
  }
117618
117638
  },
117619
117639
  async (args) => {
117640
+ const agentKeyRaw = args.agent_key ?? process.env.AGENT_KEY ?? null;
117641
+ const agentCuratorId = agentKeyRaw ? await getOrCreateSubject(agentKeyRaw, "agent") : null;
117620
117642
  const result = await runCurator({
117621
117643
  subjectKey: args.subject_key,
117622
117644
  projectKey: args.project_key,
@@ -117624,7 +117646,8 @@ function registerTools(server) {
117624
117646
  minClusterSize: args.min_cluster_size,
117625
117647
  similarityThreshold: args.similarity_threshold,
117626
117648
  minImportance: args.min_importance,
117627
- maxClusters: args.max_clusters
117649
+ maxClusters: args.max_clusters,
117650
+ agentCuratorId
117628
117651
  });
117629
117652
  const lines = [];
117630
117653
  lines.push(`SKILL CURATOR ${result.dry_run ? "DRY RUN" : "RUN"}`);
@@ -117726,7 +117749,7 @@ function registerTools(server) {
117726
117749
  const whereClause = conditions.join(" AND ");
117727
117750
  const results = await db.query(
117728
117751
  `SELECT f.id, f.content, f.fact_type, f.confidence, f.importance, f.tags, f.created_at,
117729
- f.effective_confidence, m.model_name as author_model,
117752
+ m.model_name as author_model,
117730
117753
  subj.display_name as subject_name,
117731
117754
  proj.display_name as project_name,
117732
117755
  CASE WHEN f.embedding IS NOT NULL
@@ -117753,9 +117776,8 @@ function registerTools(server) {
117753
117776
  let formatted = baseContext + "\u{1F9E0} Recalled Facts:\n\n";
117754
117777
  results.rows.forEach((r) => {
117755
117778
  const sim = (r.similarity * 100).toFixed(0);
117756
- const conf = r.effective_confidence ? `${r.effective_confidence} (eff)` : r.confidence;
117757
117779
  const author = r.author_model ? ` | via ${r.author_model}` : "";
117758
- formatted += `[#${r.id}] [${r.fact_type}] (imp: ${r.importance}, conf: ${conf}, sim: ${sim}%${author})
117780
+ formatted += `[#${r.id}] [${r.fact_type}] (imp: ${r.importance}, conf: ${r.confidence}, sim: ${sim}%${author})
117759
117781
  `;
117760
117782
  if (r.subject_name) formatted += `Subject: ${r.subject_name}`;
117761
117783
  if (r.project_name) formatted += ` | Project: ${r.project_name}`;