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 +2 -0
- package/build/index.js +61 -39
- package/build/migrations/016_agent_curator.js +25987 -0
- package/build/migrations/017_drop_trust_weight.js +25985 -0
- package/package.json +2 -2
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
116063
|
-
|
|
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:
|
|
116069
|
-
|
|
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,
|
|
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 ??
|
|
117526
|
-
const curatorModel = args.curator_model ?? args.author_model ??
|
|
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
|
-
|
|
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: ${
|
|
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}`;
|