@danielmarbach/mnemonic-mcp 0.27.2 → 0.29.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/CHANGELOG.md +30 -1
- package/README.md +7 -0
- package/build/auto-relate.d.ts.map +1 -1
- package/build/auto-relate.js +11 -5
- package/build/auto-relate.js.map +1 -1
- package/build/brands.d.ts +38 -0
- package/build/brands.d.ts.map +1 -0
- package/build/brands.js +51 -0
- package/build/brands.js.map +1 -0
- package/build/cache.d.ts.map +1 -1
- package/build/cache.js +10 -12
- package/build/cache.js.map +1 -1
- package/build/cli/import-claude-memory.d.ts +4 -0
- package/build/cli/import-claude-memory.d.ts.map +1 -0
- package/build/cli/import-claude-memory.js +146 -0
- package/build/cli/import-claude-memory.js.map +1 -0
- package/build/cli/migrate.d.ts +2 -0
- package/build/cli/migrate.d.ts.map +1 -0
- package/build/cli/migrate.js +104 -0
- package/build/cli/migrate.js.map +1 -0
- package/build/config.d.ts +2 -0
- package/build/config.d.ts.map +1 -1
- package/build/config.js +52 -23
- package/build/config.js.map +1 -1
- package/build/consolidate.d.ts +2 -1
- package/build/consolidate.d.ts.map +1 -1
- package/build/consolidate.js +3 -6
- package/build/consolidate.js.map +1 -1
- package/build/context.d.ts +5 -0
- package/build/context.d.ts.map +1 -0
- package/build/context.js +47 -0
- package/build/context.js.map +1 -0
- package/build/date-utils.d.ts +3 -0
- package/build/date-utils.d.ts.map +1 -0
- package/build/date-utils.js +10 -0
- package/build/date-utils.js.map +1 -0
- package/build/domain-errors.d.ts +49 -0
- package/build/domain-errors.d.ts.map +1 -0
- package/build/domain-errors.js +99 -0
- package/build/domain-errors.js.map +1 -0
- package/build/embeddings.d.ts +2 -1
- package/build/embeddings.d.ts.map +1 -1
- package/build/embeddings.js +39 -9
- package/build/embeddings.js.map +1 -1
- package/build/error-utils.d.ts +33 -0
- package/build/error-utils.d.ts.map +1 -0
- package/build/error-utils.js +65 -0
- package/build/error-utils.js.map +1 -0
- package/build/git-constants.d.ts +2 -0
- package/build/git-constants.d.ts.map +1 -0
- package/build/git-constants.js +2 -0
- package/build/git-constants.js.map +1 -0
- package/build/git.d.ts +27 -18
- package/build/git.d.ts.map +1 -1
- package/build/git.js +45 -24
- package/build/git.js.map +1 -1
- package/build/helpers/embed.d.ts +13 -0
- package/build/helpers/embed.d.ts.map +1 -0
- package/build/helpers/embed.js +68 -0
- package/build/helpers/embed.js.map +1 -0
- package/build/helpers/git-commit.d.ts +43 -0
- package/build/helpers/git-commit.d.ts.map +1 -0
- package/build/helpers/git-commit.js +129 -0
- package/build/helpers/git-commit.js.map +1 -0
- package/build/helpers/index.d.ts +19 -0
- package/build/helpers/index.d.ts.map +1 -0
- package/build/helpers/index.js +83 -0
- package/build/helpers/index.js.map +1 -0
- package/build/helpers/persistence.d.ts +34 -0
- package/build/helpers/persistence.d.ts.map +1 -0
- package/build/helpers/persistence.js +177 -0
- package/build/helpers/persistence.js.map +1 -0
- package/build/helpers/project.d.ts +21 -0
- package/build/helpers/project.d.ts.map +1 -0
- package/build/helpers/project.js +75 -0
- package/build/helpers/project.js.map +1 -0
- package/build/helpers/vault.d.ts +50 -0
- package/build/helpers/vault.d.ts.map +1 -0
- package/build/helpers/vault.js +196 -0
- package/build/helpers/vault.js.map +1 -0
- package/build/index.js +12 -5467
- package/build/index.js.map +1 -1
- package/build/lexical.d.ts +1 -1
- package/build/lexical.d.ts.map +1 -1
- package/build/lexical.js +2 -3
- package/build/lexical.js.map +1 -1
- package/build/markdown-ast.d.ts.map +1 -1
- package/build/markdown-ast.js +4 -2
- package/build/markdown-ast.js.map +1 -1
- package/build/migration.d.ts.map +1 -1
- package/build/migration.js +12 -8
- package/build/migration.js.map +1 -1
- package/build/project-introspection.d.ts +4 -4
- package/build/project-introspection.d.ts.map +1 -1
- package/build/project-introspection.js +71 -27
- package/build/project-introspection.js.map +1 -1
- package/build/project-memory-policy.d.ts.map +1 -1
- package/build/project-memory-policy.js +38 -3
- package/build/project-memory-policy.js.map +1 -1
- package/build/project.d.ts +2 -1
- package/build/project.d.ts.map +1 -1
- package/build/project.js +29 -41
- package/build/project.js.map +1 -1
- package/build/projections.d.ts.map +1 -1
- package/build/projections.js +3 -2
- package/build/projections.js.map +1 -1
- package/build/prompts.d.ts +3 -0
- package/build/prompts.d.ts.map +1 -0
- package/build/prompts.js +138 -0
- package/build/prompts.js.map +1 -0
- package/build/provenance.d.ts +9 -3
- package/build/provenance.d.ts.map +1 -1
- package/build/provenance.js +46 -15
- package/build/provenance.js.map +1 -1
- package/build/recall.d.ts +2 -1
- package/build/recall.d.ts.map +1 -1
- package/build/recall.js +30 -14
- package/build/recall.js.map +1 -1
- package/build/relationships.d.ts +2 -2
- package/build/relationships.d.ts.map +1 -1
- package/build/relationships.js +39 -43
- package/build/relationships.js.map +1 -1
- package/build/semantic-patch.d.ts.map +1 -1
- package/build/semantic-patch.js +16 -9
- package/build/semantic-patch.js.map +1 -1
- package/build/server-context.d.ts +18 -0
- package/build/server-context.d.ts.map +1 -0
- package/build/server-context.js +2 -0
- package/build/server-context.js.map +1 -0
- package/build/startup.d.ts +5 -0
- package/build/startup.d.ts.map +1 -0
- package/build/startup.js +37 -0
- package/build/startup.js.map +1 -0
- package/build/storage.d.ts +17 -15
- package/build/storage.d.ts.map +1 -1
- package/build/storage.js +67 -93
- package/build/storage.js.map +1 -1
- package/build/structured-content.d.ts +115 -74
- package/build/structured-content.d.ts.map +1 -1
- package/build/structured-content.js +41 -17
- package/build/structured-content.js.map +1 -1
- package/build/temporal-interpretation.d.ts +2 -1
- package/build/temporal-interpretation.d.ts.map +1 -1
- package/build/temporal-interpretation.js +15 -8
- package/build/temporal-interpretation.js.map +1 -1
- package/build/tools/consolidate-helpers.d.ts +55 -0
- package/build/tools/consolidate-helpers.d.ts.map +1 -0
- package/build/tools/consolidate-helpers.js +815 -0
- package/build/tools/consolidate-helpers.js.map +1 -0
- package/build/tools/consolidate.d.ts +4 -0
- package/build/tools/consolidate.d.ts.map +1 -0
- package/build/tools/consolidate.js +127 -0
- package/build/tools/consolidate.js.map +1 -0
- package/build/tools/detect-project.d.ts +6 -0
- package/build/tools/detect-project.d.ts.map +1 -0
- package/build/tools/detect-project.js +79 -0
- package/build/tools/detect-project.js.map +1 -0
- package/build/tools/discover-tags.d.ts +4 -0
- package/build/tools/discover-tags.d.ts.map +1 -0
- package/build/tools/discover-tags.js +236 -0
- package/build/tools/discover-tags.js.map +1 -0
- package/build/tools/forget.d.ts +4 -0
- package/build/tools/forget.d.ts.map +1 -0
- package/build/tools/forget.js +123 -0
- package/build/tools/forget.js.map +1 -0
- package/build/tools/get-project-identity.d.ts +4 -0
- package/build/tools/get-project-identity.d.ts.map +1 -0
- package/build/tools/get-project-identity.js +59 -0
- package/build/tools/get-project-identity.js.map +1 -0
- package/build/tools/get.d.ts +4 -0
- package/build/tools/get.d.ts.map +1 -0
- package/build/tools/get.js +115 -0
- package/build/tools/get.js.map +1 -0
- package/build/tools/index.d.ts +4 -0
- package/build/tools/index.d.ts.map +1 -0
- package/build/tools/index.js +47 -0
- package/build/tools/index.js.map +1 -0
- package/build/tools/list.d.ts +4 -0
- package/build/tools/list.d.ts.map +1 -0
- package/build/tools/list.js +95 -0
- package/build/tools/list.js.map +1 -0
- package/build/tools/memory-graph.d.ts +4 -0
- package/build/tools/memory-graph.d.ts.map +1 -0
- package/build/tools/memory-graph.js +84 -0
- package/build/tools/memory-graph.js.map +1 -0
- package/build/tools/migration.d.ts +5 -0
- package/build/tools/migration.d.ts.map +1 -0
- package/build/tools/migration.js +158 -0
- package/build/tools/migration.js.map +1 -0
- package/build/tools/move-memory.d.ts +4 -0
- package/build/tools/move-memory.d.ts.map +1 -0
- package/build/tools/move-memory.js +170 -0
- package/build/tools/move-memory.js.map +1 -0
- package/build/tools/policy.d.ts +5 -0
- package/build/tools/policy.d.ts.map +1 -0
- package/build/tools/policy.js +195 -0
- package/build/tools/policy.js.map +1 -0
- package/build/tools/project-memory-summary.d.ts +4 -0
- package/build/tools/project-memory-summary.d.ts.map +1 -0
- package/build/tools/project-memory-summary.js +477 -0
- package/build/tools/project-memory-summary.js.map +1 -0
- package/build/tools/recall-helpers.d.ts +40 -0
- package/build/tools/recall-helpers.d.ts.map +1 -0
- package/build/tools/recall-helpers.js +217 -0
- package/build/tools/recall-helpers.js.map +1 -0
- package/build/tools/recall.d.ts +4 -0
- package/build/tools/recall.d.ts.map +1 -0
- package/build/tools/recall.js +413 -0
- package/build/tools/recall.js.map +1 -0
- package/build/tools/recent-memories.d.ts +4 -0
- package/build/tools/recent-memories.d.ts.map +1 -0
- package/build/tools/recent-memories.js +79 -0
- package/build/tools/recent-memories.js.map +1 -0
- package/build/tools/relate.d.ts +4 -0
- package/build/tools/relate.d.ts.map +1 -0
- package/build/tools/relate.js +180 -0
- package/build/tools/relate.js.map +1 -0
- package/build/tools/remember.d.ts +4 -0
- package/build/tools/remember.d.ts.map +1 -0
- package/build/tools/remember.js +219 -0
- package/build/tools/remember.js.map +1 -0
- package/build/tools/set-project-identity.d.ts +4 -0
- package/build/tools/set-project-identity.d.ts.map +1 -0
- package/build/tools/set-project-identity.js +113 -0
- package/build/tools/set-project-identity.js.map +1 -0
- package/build/tools/sync.d.ts +4 -0
- package/build/tools/sync.d.ts.map +1 -0
- package/build/tools/sync.js +127 -0
- package/build/tools/sync.js.map +1 -0
- package/build/tools/unrelate.d.ts +4 -0
- package/build/tools/unrelate.d.ts.map +1 -0
- package/build/tools/unrelate.js +179 -0
- package/build/tools/unrelate.js.map +1 -0
- package/build/tools/update.d.ts +4 -0
- package/build/tools/update.d.ts.map +1 -0
- package/build/tools/update.js +364 -0
- package/build/tools/update.js.map +1 -0
- package/build/tools/where-is-memory.d.ts +4 -0
- package/build/tools/where-is-memory.d.ts.map +1 -0
- package/build/tools/where-is-memory.js +61 -0
- package/build/tools/where-is-memory.js.map +1 -0
- package/build/validation.d.ts +24 -0
- package/build/validation.d.ts.map +1 -0
- package/build/validation.js +62 -0
- package/build/validation.js.map +1 -0
- package/build/vault.d.ts.map +1 -1
- package/build/vault.js +38 -47
- package/build/vault.js.map +1 -1
- package/package.json +5 -2
- package/skills/mnemonic-rpi-workflow/SKILL.md +48 -17
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { getEffectiveMetadata } from "../role-suggestions.js";
|
|
2
|
+
import { computeRecallMetadataBoost, shouldApplyTemporalFiltering, computeTemporalRecencyBoost, isWithinTemporalFilterWindow } from "../recall.js";
|
|
3
|
+
import { getOrBuildProjection } from "../projections.js";
|
|
4
|
+
import { attempt } from "../error-utils.js";
|
|
5
|
+
import { getSessionCachedProjectionTokens, setSessionCachedProjectionTokens, getOrBuildVaultNoteList } from "../cache.js";
|
|
6
|
+
import { tokenize, prepareTfIdfCorpusFromTokenizedDocuments, rankDocumentsByTfIdf, LEXICAL_RESCUE_CANDIDATE_LIMIT, LEXICAL_RESCUE_THRESHOLD, LEXICAL_RESCUE_RESULT_LIMIT } from "../lexical.js";
|
|
7
|
+
// ── Recall candidate context ──────────────────────────────────────────────────
|
|
8
|
+
export function buildRecallCandidateContext(note) {
|
|
9
|
+
const metadata = getEffectiveMetadata(note);
|
|
10
|
+
const relatedCount = note.relatedTo?.length ?? 0;
|
|
11
|
+
return {
|
|
12
|
+
metadata,
|
|
13
|
+
metadataBoost: computeRecallMetadataBoost(metadata),
|
|
14
|
+
lifecycle: note.lifecycle,
|
|
15
|
+
relatedCount,
|
|
16
|
+
connectionDiversity: new Set((note.relatedTo ?? []).map((rel) => rel.type)).size,
|
|
17
|
+
structureScore: Math.min(0.04, [
|
|
18
|
+
note.content.includes("## ") ? 0.02 : 0,
|
|
19
|
+
note.content.includes("- ") || note.content.includes("1. ") ? 0.01 : 0,
|
|
20
|
+
note.content.length >= 400 ? 0.01 : 0,
|
|
21
|
+
].reduce((sum, value) => sum + value, 0)),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// ── Lexical rescue ────────────────────────────────────────────────────────────
|
|
25
|
+
export const PROJECT_SCOPE_BOOST = 0.03;
|
|
26
|
+
export async function collectLexicalRescueCandidates(vaults, query, temporalQueryHint, project, scope, tags, lifecycle, existingIds) {
|
|
27
|
+
const projectId = project?.id;
|
|
28
|
+
const applyTemporalFilter = shouldApplyTemporalFiltering(temporalQueryHint);
|
|
29
|
+
const temporalFilterWindowDays = applyTemporalFilter ? temporalQueryHint?.filterWindowDays : undefined;
|
|
30
|
+
const existingIdSet = new Set(existingIds.map((c) => c.id));
|
|
31
|
+
const rescuePool = [];
|
|
32
|
+
for (const vault of vaults) {
|
|
33
|
+
const notes = await vault.storage.listNotes().catch(() => []);
|
|
34
|
+
for (const note of notes) {
|
|
35
|
+
if (existingIdSet.has(note.id))
|
|
36
|
+
continue;
|
|
37
|
+
if (tags && tags.length > 0) {
|
|
38
|
+
const noteTags = new Set(note.tags);
|
|
39
|
+
if (!tags.every((t) => noteTags.has(t)))
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (lifecycle && note.lifecycle !== lifecycle)
|
|
43
|
+
continue;
|
|
44
|
+
const isProjectNote = note.project !== undefined;
|
|
45
|
+
const isCurrentProject = project && note.project === project.id;
|
|
46
|
+
if (scope === "project" && !isCurrentProject)
|
|
47
|
+
continue;
|
|
48
|
+
if (scope === "global" && isProjectNote)
|
|
49
|
+
continue;
|
|
50
|
+
if (applyTemporalFilter
|
|
51
|
+
&& temporalFilterWindowDays !== undefined
|
|
52
|
+
&& !isWithinTemporalFilterWindow(note.updatedAt, temporalFilterWindowDays)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const projection = await getOrBuildProjection(vault.storage, note).catch(() => undefined);
|
|
56
|
+
if (!projection)
|
|
57
|
+
continue;
|
|
58
|
+
rescuePool.push({
|
|
59
|
+
id: note.id,
|
|
60
|
+
vault,
|
|
61
|
+
isCurrentProject: Boolean(isCurrentProject),
|
|
62
|
+
updatedAt: note.updatedAt,
|
|
63
|
+
projectionText: projection.projectionText,
|
|
64
|
+
projectionTokens: projectId
|
|
65
|
+
? getSessionCachedProjectionTokens(projectId, vault.storage.vaultPath, note.id, projection.projectionText) ?? tokenize(projection.projectionText)
|
|
66
|
+
: tokenize(projection.projectionText),
|
|
67
|
+
context: buildRecallCandidateContext(note),
|
|
68
|
+
});
|
|
69
|
+
if (projectId) {
|
|
70
|
+
setSessionCachedProjectionTokens(projectId, vault.storage.vaultPath, note.id, projection.projectionText, rescuePool[rescuePool.length - 1].projectionTokens);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const rescueDocuments = rescuePool.map((candidate) => ({
|
|
75
|
+
id: candidate.id,
|
|
76
|
+
text: candidate.projectionText,
|
|
77
|
+
}));
|
|
78
|
+
const preparedRescueCorpus = prepareTfIdfCorpusFromTokenizedDocuments(rescuePool.map((candidate) => ({
|
|
79
|
+
id: candidate.id,
|
|
80
|
+
text: candidate.projectionText,
|
|
81
|
+
tokens: candidate.projectionTokens,
|
|
82
|
+
})));
|
|
83
|
+
const rankedRescueIds = new Map(rankDocumentsByTfIdf(query, rescueDocuments, LEXICAL_RESCUE_CANDIDATE_LIMIT, preparedRescueCorpus).map((candidate) => [candidate.id, candidate.score]));
|
|
84
|
+
const candidates = [];
|
|
85
|
+
for (const candidate of rescuePool) {
|
|
86
|
+
const tfIdfScore = rankedRescueIds.get(candidate.id);
|
|
87
|
+
if (tfIdfScore === undefined || tfIdfScore <= 0)
|
|
88
|
+
continue;
|
|
89
|
+
const lexicalScore = tfIdfScore;
|
|
90
|
+
if (lexicalScore < LEXICAL_RESCUE_THRESHOLD)
|
|
91
|
+
continue;
|
|
92
|
+
const temporalBoost = temporalQueryHint
|
|
93
|
+
? computeTemporalRecencyBoost(candidate.updatedAt, temporalQueryHint)
|
|
94
|
+
: 0;
|
|
95
|
+
const boost = (candidate.isCurrentProject ? PROJECT_SCOPE_BOOST : 0) + candidate.context.metadataBoost + temporalBoost;
|
|
96
|
+
candidates.push({
|
|
97
|
+
id: candidate.id,
|
|
98
|
+
score: lexicalScore,
|
|
99
|
+
semanticScoreForPromotion: 0,
|
|
100
|
+
boosted: boost,
|
|
101
|
+
vault: candidate.vault,
|
|
102
|
+
isCurrentProject: candidate.isCurrentProject,
|
|
103
|
+
lexicalScore,
|
|
104
|
+
lifecycle: candidate.context.lifecycle,
|
|
105
|
+
relatedCount: candidate.context.relatedCount,
|
|
106
|
+
connectionDiversity: candidate.context.connectionDiversity,
|
|
107
|
+
structureScore: candidate.context.structureScore,
|
|
108
|
+
metadata: candidate.context.metadata,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return candidates
|
|
112
|
+
.sort((a, b) => (b.lexicalScore ?? 0) - (a.lexicalScore ?? 0))
|
|
113
|
+
.slice(0, LEXICAL_RESCUE_RESULT_LIMIT);
|
|
114
|
+
}
|
|
115
|
+
export function tokenizeTagDiscoveryText(value) {
|
|
116
|
+
return value
|
|
117
|
+
.toLowerCase()
|
|
118
|
+
.split(/[^a-z0-9]+/)
|
|
119
|
+
.filter(Boolean);
|
|
120
|
+
}
|
|
121
|
+
export function countTokenOverlap(tokens, other) {
|
|
122
|
+
let matches = 0;
|
|
123
|
+
for (const token of other) {
|
|
124
|
+
if (tokens.has(token)) {
|
|
125
|
+
matches++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return matches;
|
|
129
|
+
}
|
|
130
|
+
export function escapeRegex(value) {
|
|
131
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
132
|
+
}
|
|
133
|
+
export function hasExactTagContextMatch(tag, values) {
|
|
134
|
+
const normalizedTag = tag.toLowerCase();
|
|
135
|
+
const pattern = new RegExp(`(^|[^a-z0-9_-])${escapeRegex(normalizedTag)}([^a-z0-9_-]|$)`);
|
|
136
|
+
return values.some(value => value ? pattern.test(value.toLowerCase()) : false);
|
|
137
|
+
}
|
|
138
|
+
export async function identifyHighPriorityAnchors(vaults, projectId) {
|
|
139
|
+
const anchorIds = new Set();
|
|
140
|
+
const anchorLookup = new Map();
|
|
141
|
+
for (const vault of vaults) {
|
|
142
|
+
const vaultResult = await attempt("recall:anchor-scan", async () => {
|
|
143
|
+
const notes = await getOrBuildVaultNoteList(projectId, vault);
|
|
144
|
+
if (!notes)
|
|
145
|
+
return undefined;
|
|
146
|
+
const supersededTargets = new Set();
|
|
147
|
+
for (const note of notes) {
|
|
148
|
+
if (note.project !== projectId)
|
|
149
|
+
continue;
|
|
150
|
+
for (const rel of note.relatedTo ?? []) {
|
|
151
|
+
if (rel.type === "supersedes") {
|
|
152
|
+
supersededTargets.add(rel.id);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for (const note of notes) {
|
|
157
|
+
if (note.project !== projectId)
|
|
158
|
+
continue;
|
|
159
|
+
if ((note.alwaysLoad === true || note.role === "summary")
|
|
160
|
+
&& !supersededTargets.has(note.id)) {
|
|
161
|
+
anchorIds.add(note.id);
|
|
162
|
+
anchorLookup.set(note.id, note.title);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
});
|
|
167
|
+
if (!vaultResult.ok)
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
return { anchorIds, anchorLookup };
|
|
171
|
+
}
|
|
172
|
+
export async function computeRecallDiversity(results) {
|
|
173
|
+
const result = await attempt("recall:diversity", () => {
|
|
174
|
+
const allTags = new Set();
|
|
175
|
+
const roleCounts = new Map();
|
|
176
|
+
const lifecycleCounts = new Map();
|
|
177
|
+
for (const r of results) {
|
|
178
|
+
for (const tag of r.tags) {
|
|
179
|
+
allTags.add(tag);
|
|
180
|
+
}
|
|
181
|
+
if (r.role) {
|
|
182
|
+
roleCounts.set(r.role, (roleCounts.get(r.role) ?? 0) + 1);
|
|
183
|
+
}
|
|
184
|
+
lifecycleCounts.set(r.lifecycle, (lifecycleCounts.get(r.lifecycle) ?? 0) + 1);
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
themeCount: allTags.size,
|
|
188
|
+
roleMix: Object.fromEntries(roleCounts),
|
|
189
|
+
lifecycleMix: Object.fromEntries(lifecycleCounts),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
if (!result.ok)
|
|
193
|
+
return undefined;
|
|
194
|
+
return result.value;
|
|
195
|
+
}
|
|
196
|
+
export async function computeRecallRetrievalCoverage(resultIds, anchorIds, anchorLookup, maxMissing = 5) {
|
|
197
|
+
const result = await attempt("recall:coverage", () => {
|
|
198
|
+
const resultIdSet = new Set(resultIds);
|
|
199
|
+
const anchorsInResults = [...anchorIds].filter((id) => resultIdSet.has(id)).length;
|
|
200
|
+
const highPriorityAnchorsTotal = anchorIds.size;
|
|
201
|
+
const fraction = highPriorityAnchorsTotal > 0 ? anchorsInResults / highPriorityAnchorsTotal : 0;
|
|
202
|
+
const missingAnchors = [...anchorIds]
|
|
203
|
+
.filter((id) => !resultIdSet.has(id))
|
|
204
|
+
.slice(0, maxMissing)
|
|
205
|
+
.map((id) => ({ id, title: anchorLookup.get(id) ?? "(unknown)" }));
|
|
206
|
+
return {
|
|
207
|
+
anchorsInResults,
|
|
208
|
+
highPriorityAnchorsTotal,
|
|
209
|
+
fraction,
|
|
210
|
+
missingAnchors,
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
if (!result.ok)
|
|
214
|
+
return undefined;
|
|
215
|
+
return result.value;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=recall-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall-helpers.js","sourceRoot":"","sources":["../../src/tools/recall-helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAsD,4BAA4B,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AACvM,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,gCAAgC,EAAE,gCAAgC,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC1H,OAAO,EAAE,QAAQ,EAAE,wCAAwC,EAAE,oBAAoB,EAAE,8BAA8B,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAGhM,iFAAiF;AAEjF,MAAM,UAAU,2BAA2B,CAAC,IAAU;IACpD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC;IACjD,OAAO;QACL,QAAQ;QACR,aAAa,EAAE,0BAA0B,CAAC,QAAQ,CAAC;QACnD,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,YAAY;QACZ,mBAAmB,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QAChF,cAAc,EAAE,IAAI,CAAC,GAAG,CACtB,IAAI,EACJ;YACE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACtE,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACtC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CACzC;KACF,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,MAAe,EACf,KAAa,EACb,iBAAgD,EAChD,OAAiD,EACjD,KAAmC,EACnC,IAA0B,EAC1B,SAAoC,EACpC,WAAoC;IAEpC,MAAM,SAAS,GAAG,OAAO,EAAE,EAAE,CAAC;IAC9B,MAAM,mBAAmB,GAAG,4BAA4B,CAAC,iBAAiB,CAAC,CAAC;IAC5E,MAAM,wBAAwB,GAAG,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IACvG,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,UAAU,GAQX,EAAE,CAAC;IAER,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,SAAS;YAEzC,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBAAE,SAAS;YACpD,CAAC;YAED,IAAI,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;gBAAE,SAAS;YAExD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC;YACjD,MAAM,gBAAgB,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,EAAE,CAAC;YAEhE,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,gBAAgB;gBAAE,SAAS;YACvD,IAAI,KAAK,KAAK,QAAQ,IAAI,aAAa;gBAAE,SAAS;YAClD,IACE,mBAAmB;mBAChB,wBAAwB,KAAK,SAAS;mBACtC,CAAC,4BAA4B,CAAC,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC,EAC1E,CAAC;gBACD,SAAS;YACX,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC1F,IAAI,CAAC,UAAU;gBAAE,SAAS;YAE1B,UAAU,CAAC,IAAI,CAAC;gBACd,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,KAAK;gBACL,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAAC;gBAC3C,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,cAAc,EAAE,UAAU,CAAC,cAAc;gBACzC,gBAAgB,EAAE,SAAS;oBACzB,CAAC,CAAC,gCAAgC,CAChC,SAAS,EACT,KAAK,CAAC,OAAO,CAAC,SAAS,EACvB,IAAI,CAAC,EAAE,EACP,UAAU,CAAC,cAAc,CAC1B,IAAI,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC;oBACxC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC;gBACvC,OAAO,EAAE,2BAA2B,CAAC,IAAI,CAAC;aAC3C,CAAC,CAAC;YAEH,IAAI,SAAS,EAAE,CAAC;gBACd,gCAAgC,CAC9B,SAAS,EACT,KAAK,CAAC,OAAO,CAAC,SAAS,EACvB,IAAI,CAAC,EAAE,EACP,UAAU,CAAC,cAAc,EACzB,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,gBAAgB,CACpD,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,eAAe,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACrD,EAAE,EAAE,SAAS,CAAC,EAAE;QAChB,IAAI,EAAE,SAAS,CAAC,cAAc;KAC/B,CAAC,CAAC,CAAC;IAEJ,MAAM,oBAAoB,GAAG,wCAAwC,CACnE,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC7B,EAAE,EAAE,SAAS,CAAC,EAAE;QAChB,IAAI,EAAE,SAAS,CAAC,cAAc;QAC9B,MAAM,EAAE,SAAS,CAAC,gBAAgB;KACnC,CAAC,CAAC,CACJ,CAAC;IAEF,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,oBAAoB,CAClB,KAAK,EACL,eAAe,EACf,8BAA8B,EAC9B,oBAAoB,CACrB,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CACtD,CAAC;IAEF,MAAM,UAAU,GAA4B,EAAE,CAAC;IAC/C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACrD,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,IAAI,CAAC;YAAE,SAAS;QAE1D,MAAM,YAAY,GAAG,UAAU,CAAC;QAChC,IAAI,YAAY,GAAG,wBAAwB;YAAE,SAAS;QAEtD,MAAM,aAAa,GAAG,iBAAiB;YACrC,CAAC,CAAC,2BAA2B,CAAC,SAAS,CAAC,SAAS,EAAE,iBAAiB,CAAC;YACrE,CAAC,CAAC,CAAC,CAAC;QACN,MAAM,KAAK,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,aAAa,GAAG,aAAa,CAAC;QACvH,UAAU,CAAC,IAAI,CAAC;YACd,EAAE,EAAE,SAAS,CAAC,EAAE;YAChB,KAAK,EAAE,YAAY;YACnB,yBAAyB,EAAE,CAAC;YAC5B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,gBAAgB,EAAE,SAAS,CAAC,gBAAgB;YAC5C,YAAY;YACZ,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,SAAS;YACtC,YAAY,EAAE,SAAS,CAAC,OAAO,CAAC,YAAY;YAC5C,mBAAmB,EAAE,SAAS,CAAC,OAAO,CAAC,mBAAmB;YAC1D,cAAc,EAAE,SAAS,CAAC,OAAO,CAAC,cAAc;YAChD,QAAQ,EAAE,SAAS,CAAC,OAAO,CAAC,QAAQ;SACrC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU;SACd,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;SAC7D,KAAK,CAAC,CAAC,EAAE,2BAA2B,CAAC,CAAC;AAC3C,CAAC;AAYD,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,OAAO,KAAK;SACT,WAAW,EAAE;SACb,KAAK,CAAC,YAAY,CAAC;SACnB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAmB,EAAE,KAAuB;IAC5E,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,GAAW,EAAE,MAAiC;IACpF,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,kBAAkB,WAAW,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAC1F,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAe,EACf,SAAiB;IAEjB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,KAAK,GAAG,MAAM,uBAAuB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAC9D,IAAI,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;YAC7B,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;YAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;oBAAE,SAAS;gBACzC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;oBACvC,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;wBAC9B,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;oBAAE,SAAS;gBACzC,IACE,CAAC,IAAI,CAAC,UAAU,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;uBAClD,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAClC,CAAC;oBACD,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACvB,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,EAAE;YAAE,SAAS;IAChC,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAuF;IAEvF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,kBAAkB,EAAE,GAAG,EAAE;QACpD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC7C,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;QAElD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YACD,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;gBACX,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5D,CAAC;YACD,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChF,CAAC;QAED,OAAO;YACL,UAAU,EAAE,OAAO,CAAC,IAAI;YACxB,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,UAAU,CAA2B;YACjE,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,eAAe,CAA2B;SAC5E,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IACjC,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,SAAmB,EACnB,SAAsB,EACtB,YAAiC,EACjC,UAAU,GAAG,CAAC;IAEd,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,iBAAiB,EAAE,GAAG,EAAE;QACnD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,gBAAgB,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QACnF,MAAM,wBAAwB,GAAG,SAAS,CAAC,IAAI,CAAC;QAChD,MAAM,QAAQ,GAAG,wBAAwB,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,GAAG,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhG,MAAM,cAAc,GAAG,CAAC,GAAG,SAAS,CAAC;aAClC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;aACpC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;aACpB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC;QAErE,OAAO;YACL,gBAAgB;YAChB,wBAAwB;YACxB,QAAQ;YACR,cAAc;SACf,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IACjC,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/tools/recall.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAoE1D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CAuf9E"}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { performance } from "perf_hooks";
|
|
3
|
+
import { memoryId } from "../brands.js";
|
|
4
|
+
import { RecallResultSchema, } from "../structured-content.js";
|
|
5
|
+
import { embed, cosineSimilarity } from "../embeddings.js";
|
|
6
|
+
import { detectTemporalQueryHint, shouldApplyTemporalFiltering, isWithinTemporalFilterWindow, computeTemporalRecencyBoost, computeHybridScore, selectRecallResults, selectWorkflowResults, applyLexicalReranking, enrichRescueCandidateScores, resolveDiscoveredVaults, applyCanonicalExplanationPromotion, applyGraphSpreadingActivation, assignDenseRanks, } from "../recall.js";
|
|
7
|
+
import { shouldTriggerLexicalRescue } from "../lexical.js";
|
|
8
|
+
import { attempt } from "../error-utils.js";
|
|
9
|
+
import { getOrBuildVaultEmbeddings, getOrBuildVaultNoteList, setSessionCachedProjection, getSessionCachedProjection, recordSessionNoteAccess, setSessionCachedNote, getSessionCachedNote, } from "../cache.js";
|
|
10
|
+
import { getOrBuildProjection } from "../projections.js";
|
|
11
|
+
import { embedMissingNotes } from "../helpers/embed.js";
|
|
12
|
+
import { ensureBranchSynced, resolveProject, noteProjectRef, projectParam, } from "../helpers/project.js";
|
|
13
|
+
import { storageLabel } from "../helpers/vault.js";
|
|
14
|
+
import { formatNote, formatTemporalHistory, formatRelationshipPreview, toRecallFreshness, toRecallRankBand, formatRetrievalEvidenceHint, } from "../helpers/index.js";
|
|
15
|
+
import { getNoteProvenance, buildTemporalHistoryEntry, computeConfidence, computeSignalStrength, } from "../provenance.js";
|
|
16
|
+
import { enrichTemporalHistory } from "../temporal-interpretation.js";
|
|
17
|
+
import { getRelationshipPreview } from "../relationships.js";
|
|
18
|
+
import { collectLexicalRescueCandidates, buildRecallCandidateContext, identifyHighPriorityAnchors, computeRecallDiversity, computeRecallRetrievalCoverage } from "./recall-helpers.js";
|
|
19
|
+
export function registerRecallTool(server, ctx) {
|
|
20
|
+
server.registerTool("recall", {
|
|
21
|
+
title: "Recall",
|
|
22
|
+
description: "Semantic search over stored memories using embeddings.\n\n" +
|
|
23
|
+
"Supports opt-in temporal mode (`mode: \"temporal\"`) to enrich top semantic matches with compact git-backed history.\n\n" +
|
|
24
|
+
"Supports workflow mode (`mode: \"workflow\"`) to prioritize RPIR-style chain reconstruction while retaining compatibility with legacy relationships.\n\n" +
|
|
25
|
+
"Use this when:\n" +
|
|
26
|
+
"- You know the topic but not the exact memory id\n" +
|
|
27
|
+
"- You are starting a session and want relevant prior context\n" +
|
|
28
|
+
"- You want to check whether a memory already exists before creating another one\n" +
|
|
29
|
+
"- You explicitly want to inspect how a note evolved over time\n\n" +
|
|
30
|
+
"Do not use this when:\n" +
|
|
31
|
+
"- You already know the exact id; use `get`\n" +
|
|
32
|
+
"- You just want to browse by tags or scope; use `list`\n\n" +
|
|
33
|
+
"Returns:\n" +
|
|
34
|
+
"- Ranked memory matches with scores, vault label, tags, lifecycle, and updated time\n" +
|
|
35
|
+
"- Bounded 1-hop relationship previews automatically attached to top results\n" +
|
|
36
|
+
"- In temporal mode, optional compact history entries for top matches\n" +
|
|
37
|
+
"- Optional retrieval evidence via `evidence: \"compact\"` for why a result ranked\n" +
|
|
38
|
+
"- `recallScopeNoteCount`: total notes visible in the recall scope (omit-aware: if vault is small, consider increasing limit)\n" +
|
|
39
|
+
"- `diversity`: theme count (unique tags), role mix, and lifecycle mix of selected results — use to gauge whether results span enough perspectives\n" +
|
|
40
|
+
"- `retrievalCoverage`: fraction of high-priority anchors (alwaysLoad or summary notes) present in results, with capped missing list — use to decide if another recall with broader terms is needed\n" +
|
|
41
|
+
"- `signalStrength`: per-result composite quality signal (0-0.50) from role, centrality, lifecycle, and recency — higher values mean more structural support; use alongside confidence to assess note reliability\n\n" +
|
|
42
|
+
"Read-only.\n\n" +
|
|
43
|
+
"Typical next step:\n" +
|
|
44
|
+
"- Use `get`, `update`, `relate`, or `consolidate` based on the results.",
|
|
45
|
+
annotations: {
|
|
46
|
+
readOnlyHint: true,
|
|
47
|
+
destructiveHint: false,
|
|
48
|
+
idempotentHint: true,
|
|
49
|
+
openWorldHint: true,
|
|
50
|
+
},
|
|
51
|
+
inputSchema: z.object({
|
|
52
|
+
query: z.string().describe("Natural-language search query describing the topic, decision, bug, preference, or context you want to find."),
|
|
53
|
+
cwd: projectParam,
|
|
54
|
+
limit: z.number().int().min(1).max(20).optional().default(ctx.defaultRecallLimit),
|
|
55
|
+
minSimilarity: z.number().min(0).max(1).optional().default(ctx.defaultMinSimilarity),
|
|
56
|
+
mode: z.enum(["default", "temporal", "workflow"]).optional().default("default").describe("Use `temporal` for compact git-backed history, or `workflow` for RPIR-oriented chain reconstruction."),
|
|
57
|
+
verbose: z.boolean().optional().default(false).describe("Only meaningful with `mode: \"temporal\"`. Adds richer stats-based history context without returning raw diffs."),
|
|
58
|
+
evidence: z.enum(["compact"]).optional().describe("Optional retrieval rationale. Omit for default output; use `compact` for bounded rank and lineage signals."),
|
|
59
|
+
tags: z.array(z.string()).optional().describe("Filter results to notes with all of these tags."),
|
|
60
|
+
scope: z
|
|
61
|
+
.enum(["project", "global", "all"])
|
|
62
|
+
.optional()
|
|
63
|
+
.default("all")
|
|
64
|
+
.describe("'project' = only this project's memories (project-scoped storage), " +
|
|
65
|
+
"'global' = only unscoped memories (main/global storage), " +
|
|
66
|
+
"'all' = both, with project notes boosted (default)"),
|
|
67
|
+
lifecycle: z.enum(["temporary", "permanent"]).optional().describe("Filter results by lifecycle. Useful for recovering working-state with `lifecycle: temporary` after `project_memory_summary` orientation."),
|
|
68
|
+
}),
|
|
69
|
+
outputSchema: RecallResultSchema,
|
|
70
|
+
}, async ({ query, cwd, limit, minSimilarity, mode, verbose, evidence, tags, scope, lifecycle }) => {
|
|
71
|
+
const t0Recall = performance.now();
|
|
72
|
+
await ensureBranchSynced(ctx, cwd);
|
|
73
|
+
const project = await resolveProject(ctx, cwd);
|
|
74
|
+
const queryVec = await embed(query);
|
|
75
|
+
const vaults = await ctx.vaultManager.searchOrder(cwd);
|
|
76
|
+
let effectiveLimit = limit;
|
|
77
|
+
let recallScopeNoteCount;
|
|
78
|
+
if (project) {
|
|
79
|
+
const scopeResult = await attempt("recall:scope-notes", async () => {
|
|
80
|
+
let totalVisible = 0;
|
|
81
|
+
for (const vault of vaults) {
|
|
82
|
+
const noteList = await getOrBuildVaultNoteList(project.id, vault);
|
|
83
|
+
if (noteList) {
|
|
84
|
+
totalVisible += noteList.length;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
recallScopeNoteCount = totalVisible;
|
|
88
|
+
if (limit >= ctx.defaultRecallLimit && totalVisible <= 25) {
|
|
89
|
+
effectiveLimit = Math.min(totalVisible, 20);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
if (!scopeResult.ok) {
|
|
93
|
+
// fail-soft: use configured limit
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const noteCache = new Map();
|
|
97
|
+
const noteCacheKey = (vault, id) => `${vault.storage.vaultPath}::${id}`;
|
|
98
|
+
const readCachedNote = async (vault, id) => {
|
|
99
|
+
// Check session cache first (populated when getOrBuildVaultEmbeddings was called)
|
|
100
|
+
if (project) {
|
|
101
|
+
const sessionNote = getSessionCachedNote(project.id, vault.storage.vaultPath, id);
|
|
102
|
+
if (sessionNote !== undefined)
|
|
103
|
+
return sessionNote;
|
|
104
|
+
}
|
|
105
|
+
const key = noteCacheKey(vault, id);
|
|
106
|
+
const cached = noteCache.get(key);
|
|
107
|
+
if (cached) {
|
|
108
|
+
return cached;
|
|
109
|
+
}
|
|
110
|
+
const note = await vault.storage.readNote(memoryId(id));
|
|
111
|
+
if (note) {
|
|
112
|
+
noteCache.set(key, note);
|
|
113
|
+
}
|
|
114
|
+
return note;
|
|
115
|
+
};
|
|
116
|
+
await Promise.allSettled(vaults.map((vault) => embedMissingNotes(ctx, vault.storage).catch(() => { })));
|
|
117
|
+
const scored = [];
|
|
118
|
+
const temporalQueryHint = detectTemporalQueryHint(query);
|
|
119
|
+
const applyTemporalFilter = shouldApplyTemporalFiltering(temporalQueryHint);
|
|
120
|
+
const temporalFilterWindowDays = applyTemporalFilter ? temporalQueryHint?.filterWindowDays : undefined;
|
|
121
|
+
for (const vault of vaults) {
|
|
122
|
+
const embeddings = project
|
|
123
|
+
? (await getOrBuildVaultEmbeddings(project.id, vault)) ?? await vault.storage.listEmbeddings()
|
|
124
|
+
: await vault.storage.listEmbeddings();
|
|
125
|
+
for (const rec of embeddings) {
|
|
126
|
+
const rawScore = cosineSimilarity(queryVec, rec.embedding);
|
|
127
|
+
if (rawScore < minSimilarity)
|
|
128
|
+
continue;
|
|
129
|
+
const note = await readCachedNote(vault, rec.id);
|
|
130
|
+
if (!note)
|
|
131
|
+
continue;
|
|
132
|
+
if (tags && tags.length > 0) {
|
|
133
|
+
const noteTags = new Set(note.tags);
|
|
134
|
+
if (!tags.every((t) => noteTags.has(t)))
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (lifecycle && note.lifecycle !== lifecycle) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const isProjectNote = note.project !== undefined;
|
|
141
|
+
const isCurrentProject = project && note.project === project.id;
|
|
142
|
+
if (scope === "project") {
|
|
143
|
+
if (!isCurrentProject)
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
else if (scope === "global") {
|
|
147
|
+
if (isProjectNote)
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (applyTemporalFilter
|
|
151
|
+
&& temporalFilterWindowDays !== undefined
|
|
152
|
+
&& !isWithinTemporalFilterWindow(note.updatedAt, temporalFilterWindowDays)) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const context = buildRecallCandidateContext(note);
|
|
156
|
+
const temporalBoost = temporalQueryHint
|
|
157
|
+
? computeTemporalRecencyBoost(note.updatedAt, temporalQueryHint)
|
|
158
|
+
: 0;
|
|
159
|
+
const boost = (isCurrentProject ? ctx.projectScopeBoost : 0) + context.metadataBoost + temporalBoost;
|
|
160
|
+
scored.push({
|
|
161
|
+
id: rec.id,
|
|
162
|
+
score: rawScore,
|
|
163
|
+
semanticScoreForPromotion: rawScore,
|
|
164
|
+
boosted: rawScore + boost,
|
|
165
|
+
vault,
|
|
166
|
+
isCurrentProject: Boolean(isCurrentProject),
|
|
167
|
+
lifecycle: context.lifecycle,
|
|
168
|
+
relatedCount: context.relatedCount,
|
|
169
|
+
connectionDiversity: context.connectionDiversity,
|
|
170
|
+
structureScore: context.structureScore,
|
|
171
|
+
metadata: context.metadata,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const projectionTexts = new Map();
|
|
176
|
+
const noteRelationships = new Map();
|
|
177
|
+
for (const candidate of scored) {
|
|
178
|
+
const note = await readCachedNote(candidate.vault, candidate.id).catch(() => null);
|
|
179
|
+
if (!note) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (note.relatedTo && note.relatedTo.length > 0) {
|
|
183
|
+
noteRelationships.set(candidate.id, note.relatedTo.map((r) => ({ id: r.id, type: r.type })));
|
|
184
|
+
}
|
|
185
|
+
const projection = await getOrBuildProjection(candidate.vault.storage, note).catch(() => undefined);
|
|
186
|
+
if (!projection) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
projectionTexts.set(candidate.id, projection.projectionText);
|
|
190
|
+
if (project) {
|
|
191
|
+
setSessionCachedProjection(project.id, candidate.id, projection);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Apply lexical reranking over semantic candidates (fail-soft)
|
|
195
|
+
const getProjectionText = (id) => {
|
|
196
|
+
const inlineProjection = projectionTexts.get(id);
|
|
197
|
+
if (inlineProjection) {
|
|
198
|
+
return inlineProjection;
|
|
199
|
+
}
|
|
200
|
+
if (project) {
|
|
201
|
+
const cached = getSessionCachedProjection(project.id, id);
|
|
202
|
+
if (cached)
|
|
203
|
+
return cached.projectionText;
|
|
204
|
+
}
|
|
205
|
+
return undefined;
|
|
206
|
+
};
|
|
207
|
+
const strongestSemanticScore = scored.reduce((max, candidate) => max === undefined ? candidate.score : Math.max(max, candidate.score), undefined);
|
|
208
|
+
const reranked = applyLexicalReranking(scored, query, getProjectionText);
|
|
209
|
+
const semanticCandidateIds = new Set(reranked.map((candidate) => candidate.id));
|
|
210
|
+
// Apply graph spreading activation: traverse related notes and boost their scores
|
|
211
|
+
const preSpreadIds = new Set(reranked.map((c) => c.id));
|
|
212
|
+
const getNoteRelationships = (id) => {
|
|
213
|
+
return noteRelationships.get(id);
|
|
214
|
+
};
|
|
215
|
+
const withGraphSpread = applyGraphSpreadingActivation(reranked, getNoteRelationships);
|
|
216
|
+
const graphDiscoveredIds = new Set(withGraphSpread.filter((candidate) => !semanticCandidateIds.has(candidate.id)).map((candidate) => candidate.id));
|
|
217
|
+
// Resolve correct vault for graph-discovered candidates that inherited their
|
|
218
|
+
// entry point's vault instead of their own.
|
|
219
|
+
await resolveDiscoveredVaults(withGraphSpread, preSpreadIds, async (id) => {
|
|
220
|
+
for (const v of vaults) {
|
|
221
|
+
const note = await v.storage.readNote(memoryId(id)).catch(() => null);
|
|
222
|
+
if (note) {
|
|
223
|
+
const isCurrentProject = project ? note.project === project.id : false;
|
|
224
|
+
return { vault: v, isCurrentProject };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return undefined;
|
|
228
|
+
});
|
|
229
|
+
// Re-assign semanticRank after graph spreading since scores are now modified
|
|
230
|
+
// and graph-discovered candidates have no semanticRank.
|
|
231
|
+
const sortedByScore = [...withGraphSpread].sort((a, b) => b.score - a.score || b.boosted - a.boosted);
|
|
232
|
+
assignDenseRanks(sortedByScore, (candidate) => candidate.score, (candidate, rank) => {
|
|
233
|
+
candidate.semanticRank = rank;
|
|
234
|
+
});
|
|
235
|
+
let promoted = applyCanonicalExplanationPromotion(withGraphSpread);
|
|
236
|
+
let rescueCandidateIds = new Set();
|
|
237
|
+
// Lexical rescue: when semantic results are weak, scan projections for additional candidates.
|
|
238
|
+
// Skip rescue when the caller set a strict minSimilarity above the default,
|
|
239
|
+
// because rescue candidates lack genuine semantic backing.
|
|
240
|
+
const rescueAllowed = minSimilarity <= ctx.defaultMinSimilarity;
|
|
241
|
+
if (rescueAllowed && shouldTriggerLexicalRescue(strongestSemanticScore, scored.length)) {
|
|
242
|
+
const rescueCandidates = await collectLexicalRescueCandidates(vaults, query, temporalQueryHint, project ?? undefined, scope, tags, lifecycle, promoted);
|
|
243
|
+
promoted.push(...rescueCandidates);
|
|
244
|
+
rescueCandidateIds = new Set(rescueCandidates.map((candidate) => candidate.id));
|
|
245
|
+
enrichRescueCandidateScores(promoted, query, getProjectionText);
|
|
246
|
+
promoted = applyCanonicalExplanationPromotion(promoted);
|
|
247
|
+
}
|
|
248
|
+
const top = mode === "workflow"
|
|
249
|
+
? selectWorkflowResults(promoted, effectiveLimit, scope)
|
|
250
|
+
: selectRecallResults(promoted, effectiveLimit, scope);
|
|
251
|
+
if (top.length === 0) {
|
|
252
|
+
const structuredContent = {
|
|
253
|
+
action: "recalled",
|
|
254
|
+
query,
|
|
255
|
+
scope: scope || "all",
|
|
256
|
+
recallScopeNoteCount,
|
|
257
|
+
results: [],
|
|
258
|
+
};
|
|
259
|
+
return { content: [{ type: "text", text: "No memories found matching that query." }], structuredContent };
|
|
260
|
+
}
|
|
261
|
+
const header = project
|
|
262
|
+
? `Recall results for project **${project.name}** (scope: ${scope}):`
|
|
263
|
+
: `Recall results (global):`;
|
|
264
|
+
const sections = [];
|
|
265
|
+
const structuredResults = [];
|
|
266
|
+
// Determine how many top results get relationship expansion
|
|
267
|
+
// Top 1 by default, top 3 if result count is small
|
|
268
|
+
const recallRelationshipLimit = top.length <= 3 ? 3 : 1;
|
|
269
|
+
for (const [index, { id, score, vault, boosted, semanticRank, lexicalRank, canonicalExplanationScore, metadata, isCurrentProject }] of top.entries()) {
|
|
270
|
+
const note = await readCachedNote(vault, id);
|
|
271
|
+
if (note) {
|
|
272
|
+
const centrality = note.relatedTo?.length ?? 0;
|
|
273
|
+
const filePath = `${vault.notesRelDir}/${id}.md`;
|
|
274
|
+
const provenance = await getNoteProvenance(vault.git, filePath);
|
|
275
|
+
const signalStrengthResult = await attempt("recall:signal-strength", async () => {
|
|
276
|
+
const ss = computeSignalStrength({
|
|
277
|
+
lifecycle: note.lifecycle,
|
|
278
|
+
updatedAt: note.updatedAt,
|
|
279
|
+
role: note.role,
|
|
280
|
+
centrality,
|
|
281
|
+
});
|
|
282
|
+
return Number.isFinite(ss) ? ss : undefined;
|
|
283
|
+
});
|
|
284
|
+
const signalStrength = signalStrengthResult.ok ? signalStrengthResult.value : undefined;
|
|
285
|
+
const confidence = computeConfidence(note.lifecycle, note.updatedAt, centrality, signalStrength);
|
|
286
|
+
let history;
|
|
287
|
+
let historySummary;
|
|
288
|
+
if (mode === "temporal") {
|
|
289
|
+
if (index < ctx.temporalHistoryNoteLimit) {
|
|
290
|
+
const commits = await vault.git.getFileHistory(filePath, ctx.temporalHistoryCommitLimit);
|
|
291
|
+
const rawHistory = await Promise.all(commits.map(async (commit) => {
|
|
292
|
+
const stats = await vault.git.getCommitStats(filePath, commit.hash);
|
|
293
|
+
return buildTemporalHistoryEntry(commit, stats, verbose);
|
|
294
|
+
}));
|
|
295
|
+
const enriched = enrichTemporalHistory(rawHistory);
|
|
296
|
+
history = verbose
|
|
297
|
+
? enriched.interpretedHistory
|
|
298
|
+
: enriched.interpretedHistory.map(entry => ({ ...entry, stats: undefined }));
|
|
299
|
+
historySummary = enriched.historySummary;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Add relationship preview for top N results (fail-soft)
|
|
303
|
+
let relationships;
|
|
304
|
+
if (index < recallRelationshipLimit) {
|
|
305
|
+
relationships = await getRelationshipPreview(note, ctx.vaultManager.allKnownVaults(), { activeProjectId: project?.id, limit: 3 });
|
|
306
|
+
}
|
|
307
|
+
const formattedHistory = mode === "temporal" && history !== undefined
|
|
308
|
+
? `\n\n${formatTemporalHistory(history)}`
|
|
309
|
+
: "";
|
|
310
|
+
const formattedRelationships = relationships !== undefined
|
|
311
|
+
? `\n\n${formatRelationshipPreview(relationships)}`
|
|
312
|
+
: "";
|
|
313
|
+
const provenanceLine = provenance || confidence
|
|
314
|
+
? `\n**confidence:** ${confidence ?? "medium"}${provenance?.recentlyChanged ? " | **recently changed**" : ""}${signalStrength !== undefined ? ` | signalStrength: ${signalStrength.toFixed(2)}` : ""}`
|
|
315
|
+
: signalStrength !== undefined
|
|
316
|
+
? `\n**signalStrength:** ${signalStrength.toFixed(2)}`
|
|
317
|
+
: "";
|
|
318
|
+
const supersededRelations = (note.relatedTo ?? []).filter((rel) => rel.type === "supersedes");
|
|
319
|
+
const retrievalEvidence = evidence === "compact"
|
|
320
|
+
? {
|
|
321
|
+
channels: [
|
|
322
|
+
semanticRank !== undefined ? "semantic" : undefined,
|
|
323
|
+
lexicalRank !== undefined ? "lexical" : undefined,
|
|
324
|
+
graphDiscoveredIds.has(id) ? "graph" : undefined,
|
|
325
|
+
rescueCandidateIds.has(id) ? "rescue" : undefined,
|
|
326
|
+
canonicalExplanationScore !== undefined && canonicalExplanationScore > 0 ? "canonical" : undefined,
|
|
327
|
+
temporalQueryHint ? "temporal-boost" : undefined,
|
|
328
|
+
].filter((value) => value !== undefined),
|
|
329
|
+
rankBand: toRecallRankBand(semanticRank),
|
|
330
|
+
projectRelevant: isCurrentProject,
|
|
331
|
+
freshness: toRecallFreshness(note.updatedAt),
|
|
332
|
+
superseded: supersededRelations.length > 0,
|
|
333
|
+
supersededBy: supersededRelations.length > 0 ? supersededRelations[0]?.id : undefined,
|
|
334
|
+
supersededCount: supersededRelations.length > 0 ? supersededRelations.length : undefined,
|
|
335
|
+
}
|
|
336
|
+
: undefined;
|
|
337
|
+
const evidenceLine = retrievalEvidence
|
|
338
|
+
? `\n${formatRetrievalEvidenceHint(retrievalEvidence, metadata?.role)}`
|
|
339
|
+
: "";
|
|
340
|
+
// Suppress raw related IDs when enriched preview is shown to avoid duplication
|
|
341
|
+
sections.push(`${formatNote(note, score, relationships === undefined)}${provenanceLine}${evidenceLine}${formattedHistory}${formattedRelationships}`);
|
|
342
|
+
structuredResults.push({
|
|
343
|
+
id,
|
|
344
|
+
title: note.title,
|
|
345
|
+
score,
|
|
346
|
+
boosted,
|
|
347
|
+
project: noteProjectRef(note),
|
|
348
|
+
vault: storageLabel(vault),
|
|
349
|
+
tags: note.tags,
|
|
350
|
+
lifecycle: note.lifecycle,
|
|
351
|
+
role: note.role,
|
|
352
|
+
updatedAt: note.updatedAt,
|
|
353
|
+
provenance,
|
|
354
|
+
confidence,
|
|
355
|
+
signalStrength,
|
|
356
|
+
history,
|
|
357
|
+
historySummary,
|
|
358
|
+
relationships,
|
|
359
|
+
retrievalEvidence,
|
|
360
|
+
});
|
|
361
|
+
if (project) {
|
|
362
|
+
setSessionCachedNote(project.id, vault.storage.vaultPath, note);
|
|
363
|
+
recordSessionNoteAccess(project.id, vault.storage.vaultPath, id, "recall", computeHybridScore({
|
|
364
|
+
id,
|
|
365
|
+
score,
|
|
366
|
+
boosted,
|
|
367
|
+
vault,
|
|
368
|
+
isCurrentProject: note.project === project.id,
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
let diversity;
|
|
374
|
+
let retrievalCoverage;
|
|
375
|
+
if (structuredResults.length > 0) {
|
|
376
|
+
diversity = await computeRecallDiversity(structuredResults);
|
|
377
|
+
if (project) {
|
|
378
|
+
const { anchorIds, anchorLookup } = await identifyHighPriorityAnchors(vaults, project.id);
|
|
379
|
+
const coverageResult = await computeRecallRetrievalCoverage(structuredResults.map((r) => r.id), anchorIds, anchorLookup);
|
|
380
|
+
if (coverageResult) {
|
|
381
|
+
retrievalCoverage = coverageResult;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const diagnosticsParts = [];
|
|
386
|
+
if (recallScopeNoteCount !== undefined) {
|
|
387
|
+
diagnosticsParts.push(`scope notes: ${recallScopeNoteCount}`);
|
|
388
|
+
}
|
|
389
|
+
if (diversity) {
|
|
390
|
+
diagnosticsParts.push(`themes: ${diversity.themeCount}`);
|
|
391
|
+
}
|
|
392
|
+
if (retrievalCoverage) {
|
|
393
|
+
diagnosticsParts.push(`anchor coverage: ${retrievalCoverage.fraction.toFixed(2)} (${retrievalCoverage.anchorsInResults}/${retrievalCoverage.highPriorityAnchorsTotal})`);
|
|
394
|
+
}
|
|
395
|
+
const diagnosticsLine = diagnosticsParts.length > 0 ? `\n${diagnosticsParts.join(" | ")}` : "";
|
|
396
|
+
const textContent = `${header}${diagnosticsLine}\n\n${sections.join("\n\n---\n\n")}`;
|
|
397
|
+
const structuredContent = {
|
|
398
|
+
action: "recalled",
|
|
399
|
+
query,
|
|
400
|
+
scope: scope || "all",
|
|
401
|
+
recallScopeNoteCount,
|
|
402
|
+
diversity,
|
|
403
|
+
retrievalCoverage,
|
|
404
|
+
results: structuredResults,
|
|
405
|
+
};
|
|
406
|
+
console.error(`[recall:timing] ${(performance.now() - t0Recall).toFixed(1)}ms`);
|
|
407
|
+
return {
|
|
408
|
+
content: [{ type: "text", text: textContent }],
|
|
409
|
+
structuredContent,
|
|
410
|
+
};
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=recall.js.map
|