@gethmy/mcp 2.3.0 → 2.3.2
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.js +80 -23
- package/dist/index.js +80 -23
- package/dist/lib/active-learning.js +939 -787
- package/dist/lib/api-client.js +2527 -638
- package/dist/lib/auto-session.js +177 -196
- package/dist/lib/cli.js +34954 -128
- package/dist/lib/config.js +235 -201
- package/dist/lib/consolidation.js +374 -289
- package/dist/lib/context-assembly.js +1265 -838
- package/dist/lib/graph-expansion.js +139 -155
- package/dist/lib/http.js +1917 -130
- package/dist/lib/index.js +29525 -5
- package/dist/lib/lifecycle-maintenance.js +663 -79
- package/dist/lib/memory-cleanup.js +1316 -381
- package/dist/lib/onboard.js +2588 -32
- package/dist/lib/prompt-builder.js +438 -445
- package/dist/lib/remote.js +31733 -143
- package/dist/lib/server.js +29389 -3216
- package/dist/lib/skills.js +315 -132
- package/dist/lib/tui/agents.js +128 -107
- package/dist/lib/tui/docs.js +1590 -687
- package/dist/lib/tui/setup.js +5698 -804
- package/dist/lib/tui/theme.js +183 -86
- package/dist/lib/tui/writer.js +1149 -176
- package/package.json +2 -2
- package/src/api-client.ts +37 -1
- package/src/memory-cleanup.ts +92 -52
- package/src/server.ts +16 -1
- package/dist/lib/__tests__/active-learning.test.js +0 -386
- package/dist/lib/__tests__/agent-performance-profiles.test.js +0 -325
- package/dist/lib/__tests__/auto-session.test.js +0 -661
- package/dist/lib/__tests__/context-assembly.test.js +0 -362
- package/dist/lib/__tests__/graph-expansion.test.js +0 -150
- package/dist/lib/__tests__/integration-memory-crud.test.js +0 -797
- package/dist/lib/__tests__/integration-memory-system.test.js +0 -281
- package/dist/lib/__tests__/lifecycle-maintenance.test.js +0 -207
- package/dist/lib/__tests__/pattern-detection.test.js +0 -295
- package/dist/lib/__tests__/prompt-builder.test.js +0 -418
package/dist/cli.js
CHANGED
|
@@ -27273,6 +27273,12 @@ class HarmonyApiClient {
|
|
|
27273
27273
|
async endAgentSession(cardId, data) {
|
|
27274
27274
|
return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
|
|
27275
27275
|
}
|
|
27276
|
+
async flushActivityLog(cardId, data) {
|
|
27277
|
+
return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
|
|
27278
|
+
}
|
|
27279
|
+
async getActivityLog(cardId, sessionId) {
|
|
27280
|
+
return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
|
|
27281
|
+
}
|
|
27276
27282
|
async getAgentSession(cardId, options) {
|
|
27277
27283
|
const params = new URLSearchParams;
|
|
27278
27284
|
if (options?.includeEnded)
|
|
@@ -28066,6 +28072,10 @@ var ALL_STEPS = [
|
|
|
28066
28072
|
"duplicates",
|
|
28067
28073
|
"backfill"
|
|
28068
28074
|
];
|
|
28075
|
+
var MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
28076
|
+
var MAX_ENTITIES_FETCH = 200;
|
|
28077
|
+
var DUPLICATE_SIMILARITY_THRESHOLD = 0.85;
|
|
28078
|
+
var CONCURRENCY_LIMIT = 5;
|
|
28069
28079
|
async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
28070
28080
|
const dryRun = options?.dryRun !== false;
|
|
28071
28081
|
const steps = options?.steps ?? ALL_STEPS;
|
|
@@ -28087,7 +28097,7 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
28087
28097
|
const listResult = await client3.listMemoryEntities({
|
|
28088
28098
|
workspace_id: workspaceId,
|
|
28089
28099
|
project_id: projectId,
|
|
28090
|
-
limit:
|
|
28100
|
+
limit: MAX_ENTITIES_FETCH
|
|
28091
28101
|
});
|
|
28092
28102
|
entities = listResult.entities || [];
|
|
28093
28103
|
report.summary.totalEntities = entities.length;
|
|
@@ -28108,7 +28118,12 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
28108
28118
|
try {
|
|
28109
28119
|
await client3.deleteMemoryEntity(item.id);
|
|
28110
28120
|
report.steps.prune.pruned++;
|
|
28111
|
-
} catch {
|
|
28121
|
+
} catch (err) {
|
|
28122
|
+
report.errors.push({
|
|
28123
|
+
step: "prune",
|
|
28124
|
+
message: `Failed to delete ${item.id}: ${err.message}`
|
|
28125
|
+
});
|
|
28126
|
+
}
|
|
28112
28127
|
}
|
|
28113
28128
|
report.summary.actionsTaken += report.steps.prune.pruned;
|
|
28114
28129
|
}
|
|
@@ -28150,7 +28165,12 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
28150
28165
|
try {
|
|
28151
28166
|
await client3.deleteMemoryEntity(item.id);
|
|
28152
28167
|
report.steps.orphans.removed++;
|
|
28153
|
-
} catch {
|
|
28168
|
+
} catch (err) {
|
|
28169
|
+
report.errors.push({
|
|
28170
|
+
step: "orphans",
|
|
28171
|
+
message: `Failed to delete ${item.id}: ${err.message}`
|
|
28172
|
+
});
|
|
28173
|
+
}
|
|
28154
28174
|
}
|
|
28155
28175
|
report.summary.actionsTaken += report.steps.orphans.removed;
|
|
28156
28176
|
}
|
|
@@ -28170,7 +28190,12 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
28170
28190
|
try {
|
|
28171
28191
|
await client3.deleteMemoryEntity(pair.removeId);
|
|
28172
28192
|
report.steps.duplicates.resolved++;
|
|
28173
|
-
} catch {
|
|
28193
|
+
} catch (err) {
|
|
28194
|
+
report.errors.push({
|
|
28195
|
+
step: "duplicates",
|
|
28196
|
+
message: `Failed to delete ${pair.removeId}: ${err.message}`
|
|
28197
|
+
});
|
|
28198
|
+
}
|
|
28174
28199
|
}
|
|
28175
28200
|
report.summary.actionsTaken += report.steps.duplicates.resolved;
|
|
28176
28201
|
}
|
|
@@ -28214,7 +28239,7 @@ function runPruneStep(entities, maxAgeDays) {
|
|
|
28214
28239
|
const drafts = entities.filter((e) => e.memory_tier === "draft");
|
|
28215
28240
|
const stale = [];
|
|
28216
28241
|
for (const entity of drafts) {
|
|
28217
|
-
const ageDays = (now - new Date(entity.created_at).getTime()) /
|
|
28242
|
+
const ageDays = (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
|
|
28218
28243
|
if (ageDays < maxAgeDays)
|
|
28219
28244
|
continue;
|
|
28220
28245
|
const lifecycle2 = evaluateLifecycle(entity);
|
|
@@ -28235,26 +28260,32 @@ async function runOrphanStep(client3, entities, orphanAgeDays) {
|
|
|
28235
28260
|
return false;
|
|
28236
28261
|
if (e.access_count >= 2)
|
|
28237
28262
|
return false;
|
|
28238
|
-
const ageDays = (now - new Date(e.created_at).getTime()) /
|
|
28263
|
+
const ageDays = (now - new Date(e.created_at).getTime()) / MS_PER_DAY;
|
|
28239
28264
|
return ageDays >= orphanAgeDays;
|
|
28240
28265
|
});
|
|
28241
|
-
for (
|
|
28242
|
-
|
|
28266
|
+
for (let i = 0;i < candidates.length; i += CONCURRENCY_LIMIT) {
|
|
28267
|
+
const batch = candidates.slice(i, i + CONCURRENCY_LIMIT);
|
|
28268
|
+
const results = await Promise.allSettled(batch.map(async (entity) => {
|
|
28243
28269
|
const related = await client3.getRelatedEntities(entity.id);
|
|
28244
28270
|
const totalRelations = (related.outgoing?.length || 0) + (related.incoming?.length || 0);
|
|
28245
28271
|
if (totalRelations > 0)
|
|
28246
|
-
|
|
28247
|
-
const ageDays = (now - new Date(entity.created_at).getTime()) /
|
|
28248
|
-
|
|
28272
|
+
return null;
|
|
28273
|
+
const ageDays = (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
|
|
28274
|
+
return {
|
|
28249
28275
|
id: entity.id,
|
|
28250
28276
|
title: entity.title,
|
|
28251
28277
|
type: entity.type,
|
|
28252
28278
|
tier: entity.memory_tier,
|
|
28253
28279
|
ageDays: Math.round(ageDays),
|
|
28254
28280
|
accessCount: entity.access_count
|
|
28255
|
-
}
|
|
28256
|
-
|
|
28257
|
-
|
|
28281
|
+
};
|
|
28282
|
+
}));
|
|
28283
|
+
for (const r of results) {
|
|
28284
|
+
if (r.status === "fulfilled" && r.value) {
|
|
28285
|
+
result.items.push(r.value);
|
|
28286
|
+
result.orphansFound++;
|
|
28287
|
+
}
|
|
28288
|
+
}
|
|
28258
28289
|
}
|
|
28259
28290
|
return result;
|
|
28260
28291
|
}
|
|
@@ -28266,15 +28297,24 @@ async function runDuplicateStep(client3, entities, workspaceId, projectId) {
|
|
|
28266
28297
|
};
|
|
28267
28298
|
const seenPairs = new Set;
|
|
28268
28299
|
const flaggedForRemoval = new Set;
|
|
28300
|
+
const entityMap = new Map(entities.map((e) => [e.id, e]));
|
|
28301
|
+
const similarityMap = new Map;
|
|
28302
|
+
for (let i = 0;i < entities.length; i += CONCURRENCY_LIMIT) {
|
|
28303
|
+
const batch = entities.slice(i, i + CONCURRENCY_LIMIT);
|
|
28304
|
+
const results = await Promise.allSettled(batch.map(async (entity) => {
|
|
28305
|
+
const similar = await findSimilarEntities(client3, entity.title, entity.content, workspaceId, { projectId, limit: 5, minRrfScore: 0.05, excludeIds: [entity.id] });
|
|
28306
|
+
return { entityId: entity.id, similar };
|
|
28307
|
+
}));
|
|
28308
|
+
for (const r of results) {
|
|
28309
|
+
if (r.status === "fulfilled") {
|
|
28310
|
+
similarityMap.set(r.value.entityId, r.value.similar);
|
|
28311
|
+
}
|
|
28312
|
+
}
|
|
28313
|
+
}
|
|
28269
28314
|
for (const entity of entities) {
|
|
28270
28315
|
if (flaggedForRemoval.has(entity.id))
|
|
28271
28316
|
continue;
|
|
28272
|
-
|
|
28273
|
-
try {
|
|
28274
|
-
similar = await findSimilarEntities(client3, entity.title, entity.content, workspaceId, { projectId, limit: 5, minRrfScore: 0.05, excludeIds: [entity.id] });
|
|
28275
|
-
} catch {
|
|
28276
|
-
continue;
|
|
28277
|
-
}
|
|
28317
|
+
const similar = similarityMap.get(entity.id) || [];
|
|
28278
28318
|
for (const match of similar) {
|
|
28279
28319
|
if (flaggedForRemoval.has(match.id))
|
|
28280
28320
|
continue;
|
|
@@ -28283,10 +28323,10 @@ async function runDuplicateStep(client3, entities, workspaceId, projectId) {
|
|
|
28283
28323
|
continue;
|
|
28284
28324
|
seenPairs.add(pairKey);
|
|
28285
28325
|
const sim = titleSimilarity(entity.title, match.title);
|
|
28286
|
-
if (sim <
|
|
28326
|
+
if (sim < DUPLICATE_SIMILARITY_THRESHOLD)
|
|
28287
28327
|
continue;
|
|
28288
28328
|
const entityScore = entityQualityScore(entity);
|
|
28289
|
-
const matchEntity =
|
|
28329
|
+
const matchEntity = entityMap.get(match.id);
|
|
28290
28330
|
const matchScore = matchEntity ? entityQualityScore(matchEntity) : match.confidence;
|
|
28291
28331
|
const [keep, remove] = entityScore >= matchScore ? [entity, { id: match.id, title: match.title }] : [{ id: match.id, title: match.title }, entity];
|
|
28292
28332
|
flaggedForRemoval.add(remove.id);
|
|
@@ -28335,6 +28375,10 @@ function generateHealthReport(report) {
|
|
|
28335
28375
|
`**Mode:** ${mode} | **Entities:** ${report.summary.totalEntities} | **Issues:** ${report.summary.issuesFound} | **Actions:** ${report.summary.actionsTaken}`,
|
|
28336
28376
|
""
|
|
28337
28377
|
];
|
|
28378
|
+
if (report.summary.totalEntities >= MAX_ENTITIES_FETCH) {
|
|
28379
|
+
lines.push(`> **Note:** Entity count hit the ${MAX_ENTITIES_FETCH} fetch limit. Some entities may not have been analyzed.
|
|
28380
|
+
`);
|
|
28381
|
+
}
|
|
28338
28382
|
if (report.steps.prune) {
|
|
28339
28383
|
const p = report.steps.prune;
|
|
28340
28384
|
lines.push("## Stale Drafts");
|
|
@@ -31413,9 +31457,22 @@ async function handleToolCall(name, args, deps) {
|
|
|
31413
31457
|
throw new Error("No workspace specified. Use harmony_set_workspace_context or provide workspaceId.");
|
|
31414
31458
|
}
|
|
31415
31459
|
const projectId = args.projectId || deps.getActiveProjectId() || undefined;
|
|
31460
|
+
const validSteps = [
|
|
31461
|
+
"prune",
|
|
31462
|
+
"consolidate",
|
|
31463
|
+
"orphans",
|
|
31464
|
+
"duplicates",
|
|
31465
|
+
"backfill"
|
|
31466
|
+
];
|
|
31467
|
+
const rawSteps = args.steps;
|
|
31468
|
+
const steps = rawSteps?.filter((s) => validSteps.includes(s));
|
|
31469
|
+
if (rawSteps && steps && steps.length < rawSteps.length) {
|
|
31470
|
+
const invalid = rawSteps.filter((s) => !validSteps.includes(s));
|
|
31471
|
+
console.warn(`Unknown cleanup steps ignored: ${invalid.join(", ")}`);
|
|
31472
|
+
}
|
|
31416
31473
|
const report = await runMemoryCleanup(client3, workspaceId, projectId, {
|
|
31417
31474
|
dryRun: args.dryRun,
|
|
31418
|
-
steps
|
|
31475
|
+
steps,
|
|
31419
31476
|
maxAgeDays: args.maxAgeDays,
|
|
31420
31477
|
minClusterSize: args.minClusterSize,
|
|
31421
31478
|
orphanAgeDays: args.orphanAgeDays
|
package/dist/index.js
CHANGED
|
@@ -25033,6 +25033,12 @@ class HarmonyApiClient {
|
|
|
25033
25033
|
async endAgentSession(cardId, data) {
|
|
25034
25034
|
return this.request("DELETE", `/cards/${cardId}/agent-context`, data);
|
|
25035
25035
|
}
|
|
25036
|
+
async flushActivityLog(cardId, data) {
|
|
25037
|
+
return this.request("POST", `/cards/${cardId}/agent-activity-log`, data);
|
|
25038
|
+
}
|
|
25039
|
+
async getActivityLog(cardId, sessionId) {
|
|
25040
|
+
return this.request("GET", `/cards/${cardId}/agent-activity-log?sessionId=${sessionId}`);
|
|
25041
|
+
}
|
|
25036
25042
|
async getAgentSession(cardId, options) {
|
|
25037
25043
|
const params = new URLSearchParams;
|
|
25038
25044
|
if (options?.includeEnded)
|
|
@@ -25826,6 +25832,10 @@ var ALL_STEPS = [
|
|
|
25826
25832
|
"duplicates",
|
|
25827
25833
|
"backfill"
|
|
25828
25834
|
];
|
|
25835
|
+
var MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
25836
|
+
var MAX_ENTITIES_FETCH = 200;
|
|
25837
|
+
var DUPLICATE_SIMILARITY_THRESHOLD = 0.85;
|
|
25838
|
+
var CONCURRENCY_LIMIT = 5;
|
|
25829
25839
|
async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
25830
25840
|
const dryRun = options?.dryRun !== false;
|
|
25831
25841
|
const steps = options?.steps ?? ALL_STEPS;
|
|
@@ -25847,7 +25857,7 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
25847
25857
|
const listResult = await client3.listMemoryEntities({
|
|
25848
25858
|
workspace_id: workspaceId,
|
|
25849
25859
|
project_id: projectId,
|
|
25850
|
-
limit:
|
|
25860
|
+
limit: MAX_ENTITIES_FETCH
|
|
25851
25861
|
});
|
|
25852
25862
|
entities = listResult.entities || [];
|
|
25853
25863
|
report.summary.totalEntities = entities.length;
|
|
@@ -25868,7 +25878,12 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
25868
25878
|
try {
|
|
25869
25879
|
await client3.deleteMemoryEntity(item.id);
|
|
25870
25880
|
report.steps.prune.pruned++;
|
|
25871
|
-
} catch {
|
|
25881
|
+
} catch (err) {
|
|
25882
|
+
report.errors.push({
|
|
25883
|
+
step: "prune",
|
|
25884
|
+
message: `Failed to delete ${item.id}: ${err.message}`
|
|
25885
|
+
});
|
|
25886
|
+
}
|
|
25872
25887
|
}
|
|
25873
25888
|
report.summary.actionsTaken += report.steps.prune.pruned;
|
|
25874
25889
|
}
|
|
@@ -25910,7 +25925,12 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
25910
25925
|
try {
|
|
25911
25926
|
await client3.deleteMemoryEntity(item.id);
|
|
25912
25927
|
report.steps.orphans.removed++;
|
|
25913
|
-
} catch {
|
|
25928
|
+
} catch (err) {
|
|
25929
|
+
report.errors.push({
|
|
25930
|
+
step: "orphans",
|
|
25931
|
+
message: `Failed to delete ${item.id}: ${err.message}`
|
|
25932
|
+
});
|
|
25933
|
+
}
|
|
25914
25934
|
}
|
|
25915
25935
|
report.summary.actionsTaken += report.steps.orphans.removed;
|
|
25916
25936
|
}
|
|
@@ -25930,7 +25950,12 @@ async function runMemoryCleanup(client3, workspaceId, projectId, options) {
|
|
|
25930
25950
|
try {
|
|
25931
25951
|
await client3.deleteMemoryEntity(pair.removeId);
|
|
25932
25952
|
report.steps.duplicates.resolved++;
|
|
25933
|
-
} catch {
|
|
25953
|
+
} catch (err) {
|
|
25954
|
+
report.errors.push({
|
|
25955
|
+
step: "duplicates",
|
|
25956
|
+
message: `Failed to delete ${pair.removeId}: ${err.message}`
|
|
25957
|
+
});
|
|
25958
|
+
}
|
|
25934
25959
|
}
|
|
25935
25960
|
report.summary.actionsTaken += report.steps.duplicates.resolved;
|
|
25936
25961
|
}
|
|
@@ -25974,7 +25999,7 @@ function runPruneStep(entities, maxAgeDays) {
|
|
|
25974
25999
|
const drafts = entities.filter((e) => e.memory_tier === "draft");
|
|
25975
26000
|
const stale = [];
|
|
25976
26001
|
for (const entity of drafts) {
|
|
25977
|
-
const ageDays = (now - new Date(entity.created_at).getTime()) /
|
|
26002
|
+
const ageDays = (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
|
|
25978
26003
|
if (ageDays < maxAgeDays)
|
|
25979
26004
|
continue;
|
|
25980
26005
|
const lifecycle2 = evaluateLifecycle(entity);
|
|
@@ -25995,26 +26020,32 @@ async function runOrphanStep(client3, entities, orphanAgeDays) {
|
|
|
25995
26020
|
return false;
|
|
25996
26021
|
if (e.access_count >= 2)
|
|
25997
26022
|
return false;
|
|
25998
|
-
const ageDays = (now - new Date(e.created_at).getTime()) /
|
|
26023
|
+
const ageDays = (now - new Date(e.created_at).getTime()) / MS_PER_DAY;
|
|
25999
26024
|
return ageDays >= orphanAgeDays;
|
|
26000
26025
|
});
|
|
26001
|
-
for (
|
|
26002
|
-
|
|
26026
|
+
for (let i = 0;i < candidates.length; i += CONCURRENCY_LIMIT) {
|
|
26027
|
+
const batch = candidates.slice(i, i + CONCURRENCY_LIMIT);
|
|
26028
|
+
const results = await Promise.allSettled(batch.map(async (entity) => {
|
|
26003
26029
|
const related = await client3.getRelatedEntities(entity.id);
|
|
26004
26030
|
const totalRelations = (related.outgoing?.length || 0) + (related.incoming?.length || 0);
|
|
26005
26031
|
if (totalRelations > 0)
|
|
26006
|
-
|
|
26007
|
-
const ageDays = (now - new Date(entity.created_at).getTime()) /
|
|
26008
|
-
|
|
26032
|
+
return null;
|
|
26033
|
+
const ageDays = (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
|
|
26034
|
+
return {
|
|
26009
26035
|
id: entity.id,
|
|
26010
26036
|
title: entity.title,
|
|
26011
26037
|
type: entity.type,
|
|
26012
26038
|
tier: entity.memory_tier,
|
|
26013
26039
|
ageDays: Math.round(ageDays),
|
|
26014
26040
|
accessCount: entity.access_count
|
|
26015
|
-
}
|
|
26016
|
-
|
|
26017
|
-
|
|
26041
|
+
};
|
|
26042
|
+
}));
|
|
26043
|
+
for (const r of results) {
|
|
26044
|
+
if (r.status === "fulfilled" && r.value) {
|
|
26045
|
+
result.items.push(r.value);
|
|
26046
|
+
result.orphansFound++;
|
|
26047
|
+
}
|
|
26048
|
+
}
|
|
26018
26049
|
}
|
|
26019
26050
|
return result;
|
|
26020
26051
|
}
|
|
@@ -26026,15 +26057,24 @@ async function runDuplicateStep(client3, entities, workspaceId, projectId) {
|
|
|
26026
26057
|
};
|
|
26027
26058
|
const seenPairs = new Set;
|
|
26028
26059
|
const flaggedForRemoval = new Set;
|
|
26060
|
+
const entityMap = new Map(entities.map((e) => [e.id, e]));
|
|
26061
|
+
const similarityMap = new Map;
|
|
26062
|
+
for (let i = 0;i < entities.length; i += CONCURRENCY_LIMIT) {
|
|
26063
|
+
const batch = entities.slice(i, i + CONCURRENCY_LIMIT);
|
|
26064
|
+
const results = await Promise.allSettled(batch.map(async (entity) => {
|
|
26065
|
+
const similar = await findSimilarEntities(client3, entity.title, entity.content, workspaceId, { projectId, limit: 5, minRrfScore: 0.05, excludeIds: [entity.id] });
|
|
26066
|
+
return { entityId: entity.id, similar };
|
|
26067
|
+
}));
|
|
26068
|
+
for (const r of results) {
|
|
26069
|
+
if (r.status === "fulfilled") {
|
|
26070
|
+
similarityMap.set(r.value.entityId, r.value.similar);
|
|
26071
|
+
}
|
|
26072
|
+
}
|
|
26073
|
+
}
|
|
26029
26074
|
for (const entity of entities) {
|
|
26030
26075
|
if (flaggedForRemoval.has(entity.id))
|
|
26031
26076
|
continue;
|
|
26032
|
-
|
|
26033
|
-
try {
|
|
26034
|
-
similar = await findSimilarEntities(client3, entity.title, entity.content, workspaceId, { projectId, limit: 5, minRrfScore: 0.05, excludeIds: [entity.id] });
|
|
26035
|
-
} catch {
|
|
26036
|
-
continue;
|
|
26037
|
-
}
|
|
26077
|
+
const similar = similarityMap.get(entity.id) || [];
|
|
26038
26078
|
for (const match of similar) {
|
|
26039
26079
|
if (flaggedForRemoval.has(match.id))
|
|
26040
26080
|
continue;
|
|
@@ -26043,10 +26083,10 @@ async function runDuplicateStep(client3, entities, workspaceId, projectId) {
|
|
|
26043
26083
|
continue;
|
|
26044
26084
|
seenPairs.add(pairKey);
|
|
26045
26085
|
const sim = titleSimilarity(entity.title, match.title);
|
|
26046
|
-
if (sim <
|
|
26086
|
+
if (sim < DUPLICATE_SIMILARITY_THRESHOLD)
|
|
26047
26087
|
continue;
|
|
26048
26088
|
const entityScore = entityQualityScore(entity);
|
|
26049
|
-
const matchEntity =
|
|
26089
|
+
const matchEntity = entityMap.get(match.id);
|
|
26050
26090
|
const matchScore = matchEntity ? entityQualityScore(matchEntity) : match.confidence;
|
|
26051
26091
|
const [keep, remove] = entityScore >= matchScore ? [entity, { id: match.id, title: match.title }] : [{ id: match.id, title: match.title }, entity];
|
|
26052
26092
|
flaggedForRemoval.add(remove.id);
|
|
@@ -26095,6 +26135,10 @@ function generateHealthReport(report) {
|
|
|
26095
26135
|
`**Mode:** ${mode} | **Entities:** ${report.summary.totalEntities} | **Issues:** ${report.summary.issuesFound} | **Actions:** ${report.summary.actionsTaken}`,
|
|
26096
26136
|
""
|
|
26097
26137
|
];
|
|
26138
|
+
if (report.summary.totalEntities >= MAX_ENTITIES_FETCH) {
|
|
26139
|
+
lines.push(`> **Note:** Entity count hit the ${MAX_ENTITIES_FETCH} fetch limit. Some entities may not have been analyzed.
|
|
26140
|
+
`);
|
|
26141
|
+
}
|
|
26098
26142
|
if (report.steps.prune) {
|
|
26099
26143
|
const p = report.steps.prune;
|
|
26100
26144
|
lines.push("## Stale Drafts");
|
|
@@ -29173,9 +29217,22 @@ async function handleToolCall(name, args, deps) {
|
|
|
29173
29217
|
throw new Error("No workspace specified. Use harmony_set_workspace_context or provide workspaceId.");
|
|
29174
29218
|
}
|
|
29175
29219
|
const projectId = args.projectId || deps.getActiveProjectId() || undefined;
|
|
29220
|
+
const validSteps = [
|
|
29221
|
+
"prune",
|
|
29222
|
+
"consolidate",
|
|
29223
|
+
"orphans",
|
|
29224
|
+
"duplicates",
|
|
29225
|
+
"backfill"
|
|
29226
|
+
];
|
|
29227
|
+
const rawSteps = args.steps;
|
|
29228
|
+
const steps = rawSteps?.filter((s) => validSteps.includes(s));
|
|
29229
|
+
if (rawSteps && steps && steps.length < rawSteps.length) {
|
|
29230
|
+
const invalid = rawSteps.filter((s) => !validSteps.includes(s));
|
|
29231
|
+
console.warn(`Unknown cleanup steps ignored: ${invalid.join(", ")}`);
|
|
29232
|
+
}
|
|
29176
29233
|
const report = await runMemoryCleanup(client3, workspaceId, projectId, {
|
|
29177
29234
|
dryRun: args.dryRun,
|
|
29178
|
-
steps
|
|
29235
|
+
steps,
|
|
29179
29236
|
maxAgeDays: args.maxAgeDays,
|
|
29180
29237
|
minClusterSize: args.minClusterSize,
|
|
29181
29238
|
orphanAgeDays: args.orphanAgeDays
|