agent-recall-core 3.3.12 → 3.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools-logic/palace-search.d.ts +2 -0
- package/dist/tools-logic/palace-search.d.ts.map +1 -1
- package/dist/tools-logic/palace-search.js +33 -14
- package/dist/tools-logic/palace-search.js.map +1 -1
- package/dist/tools-logic/smart-recall.d.ts +45 -3
- package/dist/tools-logic/smart-recall.d.ts.map +1 -1
- package/dist/tools-logic/smart-recall.js +200 -82
- package/dist/tools-logic/smart-recall.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ export interface PalaceSearchResult {
|
|
|
12
12
|
salience: number;
|
|
13
13
|
excerpt: string;
|
|
14
14
|
line: number;
|
|
15
|
+
/** Keyword overlap ratio [0,1]. Added in v3.3.14 — used by smart-recall for RRF internal scoring. */
|
|
16
|
+
keyword_score: number;
|
|
15
17
|
}>;
|
|
16
18
|
total_matches: number;
|
|
17
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"palace-search.d.ts","sourceRoot":"","sources":["../../src/tools-logic/palace-search.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"palace-search.d.ts","sourceRoot":"","sources":["../../src/tools-logic/palace-search.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,qGAAqG;QACrG,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC,CAAC;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAiExF"}
|
|
@@ -8,7 +8,11 @@ export async function palaceSearch(input) {
|
|
|
8
8
|
ensurePalaceInitialized(slug);
|
|
9
9
|
const rooms = listRooms(slug);
|
|
10
10
|
const pd = palaceDir(slug);
|
|
11
|
-
|
|
11
|
+
// v3.3.14: use keyword overlap instead of exact substring match.
|
|
12
|
+
// Old approach: lines[i].toLowerCase().includes(fullQuery) required the entire
|
|
13
|
+
// query to appear as one continuous substring — too strict, missed relevant entries.
|
|
14
|
+
// New approach: count matched keywords, compute overlap ratio for scoring.
|
|
15
|
+
const queryWords = input.query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
12
16
|
const results = [];
|
|
13
17
|
const targetRooms = input.room ? rooms.filter((r) => r.slug === input.room) : rooms;
|
|
14
18
|
for (const roomMeta of targetRooms) {
|
|
@@ -21,25 +25,40 @@ export async function palaceSearch(input) {
|
|
|
21
25
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
22
26
|
const lines = content.split("\n");
|
|
23
27
|
for (let i = 0; i < lines.length; i++) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
const lineLower = lines[i].toLowerCase();
|
|
29
|
+
if (queryWords.length === 0)
|
|
30
|
+
continue;
|
|
31
|
+
// Count how many query keywords appear in this line
|
|
32
|
+
const matchedWords = queryWords.filter((w) => lineLower.includes(w));
|
|
33
|
+
if (matchedWords.length === 0)
|
|
34
|
+
continue;
|
|
35
|
+
const keywordScore = matchedWords.length / queryWords.length;
|
|
36
|
+
// Build excerpt anchored on first keyword match
|
|
37
|
+
const firstKw = matchedWords[0];
|
|
38
|
+
const matchIdx = lineLower.indexOf(firstKw);
|
|
39
|
+
const start = Math.max(0, matchIdx - 40);
|
|
40
|
+
const end = Math.min(lines[i].length, matchIdx + firstKw.length + 80);
|
|
41
|
+
let excerpt = lines[i].slice(start, end).trim();
|
|
42
|
+
if (start > 0)
|
|
43
|
+
excerpt = "..." + excerpt;
|
|
44
|
+
if (end < lines[i].length)
|
|
45
|
+
excerpt = excerpt + "...";
|
|
46
|
+
results.push({
|
|
47
|
+
room: roomMeta.slug,
|
|
48
|
+
file: file.replace(".md", ""),
|
|
49
|
+
salience: roomMeta.salience,
|
|
50
|
+
excerpt,
|
|
51
|
+
line: i + 1,
|
|
52
|
+
keyword_score: keywordScore,
|
|
53
|
+
});
|
|
36
54
|
}
|
|
37
55
|
}
|
|
38
56
|
if (results.some((r) => r.room === roomMeta.slug)) {
|
|
39
57
|
recordAccess(slug, roomMeta.slug);
|
|
40
58
|
}
|
|
41
59
|
}
|
|
42
|
-
|
|
60
|
+
// Sort by keyword_score × salience so most relevant + important rooms surface first
|
|
61
|
+
results.sort((a, b) => (b.keyword_score * b.salience) - (a.keyword_score * a.salience) || a.line - b.line);
|
|
43
62
|
const limited = results.slice(0, 20);
|
|
44
63
|
return { project: slug, query: input.query, results: limited, total_matches: results.length };
|
|
45
64
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"palace-search.js","sourceRoot":"","sources":["../../src/tools-logic/palace-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"palace-search.js","sourceRoot":"","sources":["../../src/tools-logic/palace-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* smart_recall — unified cross-store search.
|
|
2
|
+
* smart_recall — unified cross-store search. v3.3.14
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* ## Scoring Architecture (why it works this way)
|
|
5
|
+
*
|
|
6
|
+
* ### Problem with the old approach (< v3.3.14): Linear Score Fusion
|
|
7
|
+
* The old formula combined raw scores from different sources directly:
|
|
8
|
+
* journal_score = recency * 0.60 + exactness * 0.40
|
|
9
|
+
* palace_score = salience * 0.50 + exactness * 0.30 + salience * 0.20
|
|
10
|
+
* This caused journal entries to always win because their recency weight (0.60)
|
|
11
|
+
* produced scores of ~0.57+ for any entry from yesterday, while palace items
|
|
12
|
+
* with salience=0.5 only scored ~0.35+exactness*0.30. Cross-source raw scores
|
|
13
|
+
* are on incompatible scales — combining them directly is mathematically unsound.
|
|
14
|
+
*
|
|
15
|
+
* ### Fix 1: Reciprocal Rank Fusion (RRF)
|
|
16
|
+
* Source: Cormack, Clarke & Buettcher (2009); adopted by Elasticsearch, Azure AI Search.
|
|
17
|
+
* Instead of combining raw scores, each source ranks its own items internally,
|
|
18
|
+
* then RRF merges by rank position:
|
|
19
|
+
* RRF_score(doc) = Σ 1 / (k + rank_i(doc)) where k=60
|
|
20
|
+
* This means journal item at rank 1 and palace item at rank 1 get equal weight (1/61).
|
|
21
|
+
* No source dominates by default. Items appearing in multiple sources get bonus score.
|
|
22
|
+
*
|
|
23
|
+
* ### Fix 2: Ebbinghaus Forgetting Curve (source-specific decay)
|
|
24
|
+
* Source: Ebbinghaus (1885); replicated by Murre & Dros (2015, PMC4492928).
|
|
25
|
+
* Formula: R(t) = e^(-t/S), where S = memory strength (days).
|
|
26
|
+
* Different memory types have different S values based on psychological research:
|
|
27
|
+
* - Journal (episodic, low meaning): S = 2 → 60% retained after 1 day
|
|
28
|
+
* - Knowledge/bug-fix (procedural): S = 180 → 99.4% retained after 1 day
|
|
29
|
+
* - Palace/decisions (semantic): S = 9999 → barely decays
|
|
30
|
+
* - Insight (conceptual): not time-based; uses confirmation count instead
|
|
31
|
+
* This replaces the uniform 0.95^days that treated all memory equally.
|
|
32
|
+
*
|
|
33
|
+
* ### Fix 3: Beta Distribution for Feedback Utility
|
|
34
|
+
* Source: Bayesian statistics; optimal for binary feedback signals.
|
|
35
|
+
* Each item maintains (positives, negatives) feedback counts.
|
|
36
|
+
* Beta expected value: E[β] = (α) / (α + β) = (pos+1) / (pos+neg+2)
|
|
37
|
+
* This is the mathematically optimal Bayesian estimate of "true usefulness":
|
|
38
|
+
* - No feedback: E = 0.5 → neutral (no bias)
|
|
39
|
+
* - 3 positive: E = 0.8 → meaningful boost
|
|
40
|
+
* - 5 negative: E = 0.14 → meaningful penalty
|
|
41
|
+
* Applied as a multiplier to RRF score: finalScore = rrfScore * (E * 2)
|
|
42
|
+
* (×2 so neutral = 1.0, positive = >1.0, negative = <1.0)
|
|
43
|
+
*
|
|
44
|
+
* ### Fix 4: Consistent total_searched
|
|
45
|
+
* Previously mixed "total matches" (palace), "returned results" (journal),
|
|
46
|
+
* and "total in index" (insight) — three different metrics summed together.
|
|
47
|
+
* Now counts candidate items from each source before final RRF merge.
|
|
6
48
|
*/
|
|
7
49
|
export interface RecallFeedback {
|
|
8
50
|
id?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-recall.d.ts","sourceRoot":"","sources":["../../src/tools-logic/smart-recall.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"smart-recall.d.ts","sourceRoot":"","sources":["../../src/tools-logic/smart-recall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAcH,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAwJD,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+JrF"}
|
|
@@ -1,8 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* smart_recall — unified cross-store search.
|
|
2
|
+
* smart_recall — unified cross-store search. v3.3.14
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* ## Scoring Architecture (why it works this way)
|
|
5
|
+
*
|
|
6
|
+
* ### Problem with the old approach (< v3.3.14): Linear Score Fusion
|
|
7
|
+
* The old formula combined raw scores from different sources directly:
|
|
8
|
+
* journal_score = recency * 0.60 + exactness * 0.40
|
|
9
|
+
* palace_score = salience * 0.50 + exactness * 0.30 + salience * 0.20
|
|
10
|
+
* This caused journal entries to always win because their recency weight (0.60)
|
|
11
|
+
* produced scores of ~0.57+ for any entry from yesterday, while palace items
|
|
12
|
+
* with salience=0.5 only scored ~0.35+exactness*0.30. Cross-source raw scores
|
|
13
|
+
* are on incompatible scales — combining them directly is mathematically unsound.
|
|
14
|
+
*
|
|
15
|
+
* ### Fix 1: Reciprocal Rank Fusion (RRF)
|
|
16
|
+
* Source: Cormack, Clarke & Buettcher (2009); adopted by Elasticsearch, Azure AI Search.
|
|
17
|
+
* Instead of combining raw scores, each source ranks its own items internally,
|
|
18
|
+
* then RRF merges by rank position:
|
|
19
|
+
* RRF_score(doc) = Σ 1 / (k + rank_i(doc)) where k=60
|
|
20
|
+
* This means journal item at rank 1 and palace item at rank 1 get equal weight (1/61).
|
|
21
|
+
* No source dominates by default. Items appearing in multiple sources get bonus score.
|
|
22
|
+
*
|
|
23
|
+
* ### Fix 2: Ebbinghaus Forgetting Curve (source-specific decay)
|
|
24
|
+
* Source: Ebbinghaus (1885); replicated by Murre & Dros (2015, PMC4492928).
|
|
25
|
+
* Formula: R(t) = e^(-t/S), where S = memory strength (days).
|
|
26
|
+
* Different memory types have different S values based on psychological research:
|
|
27
|
+
* - Journal (episodic, low meaning): S = 2 → 60% retained after 1 day
|
|
28
|
+
* - Knowledge/bug-fix (procedural): S = 180 → 99.4% retained after 1 day
|
|
29
|
+
* - Palace/decisions (semantic): S = 9999 → barely decays
|
|
30
|
+
* - Insight (conceptual): not time-based; uses confirmation count instead
|
|
31
|
+
* This replaces the uniform 0.95^days that treated all memory equally.
|
|
32
|
+
*
|
|
33
|
+
* ### Fix 3: Beta Distribution for Feedback Utility
|
|
34
|
+
* Source: Bayesian statistics; optimal for binary feedback signals.
|
|
35
|
+
* Each item maintains (positives, negatives) feedback counts.
|
|
36
|
+
* Beta expected value: E[β] = (α) / (α + β) = (pos+1) / (pos+neg+2)
|
|
37
|
+
* This is the mathematically optimal Bayesian estimate of "true usefulness":
|
|
38
|
+
* - No feedback: E = 0.5 → neutral (no bias)
|
|
39
|
+
* - 3 positive: E = 0.8 → meaningful boost
|
|
40
|
+
* - 5 negative: E = 0.14 → meaningful penalty
|
|
41
|
+
* Applied as a multiplier to RRF score: finalScore = rrfScore * (E * 2)
|
|
42
|
+
* (×2 so neutral = 1.0, positive = >1.0, negative = <1.0)
|
|
43
|
+
*
|
|
44
|
+
* ### Fix 4: Consistent total_searched
|
|
45
|
+
* Previously mixed "total matches" (palace), "returned results" (journal),
|
|
46
|
+
* and "total in index" (insight) — three different metrics summed together.
|
|
47
|
+
* Now counts candidate items from each source before final RRF merge.
|
|
6
48
|
*/
|
|
7
49
|
import * as fs from "node:fs";
|
|
8
50
|
import * as path from "node:path";
|
|
@@ -11,7 +53,25 @@ import { journalSearch } from "./journal-search.js";
|
|
|
11
53
|
import { recallInsight } from "./recall-insight.js";
|
|
12
54
|
import { getRoot } from "../types.js";
|
|
13
55
|
import { ensureDir } from "../storage/fs-utils.js";
|
|
14
|
-
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Constants
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/** RRF constant. k=60 is the empirically validated default (Cormack et al. 2009). */
|
|
60
|
+
const RRF_K = 60;
|
|
61
|
+
/**
|
|
62
|
+
* Ebbinghaus memory strength S (days) per source type.
|
|
63
|
+
* R(t) = e^(-t/S): higher S = slower decay.
|
|
64
|
+
* Journal decays fast (low-meaning episodic); palace barely decays (semantic).
|
|
65
|
+
*/
|
|
66
|
+
const EBBINGHAUS_S = {
|
|
67
|
+
journal: 2, // ~60% retained after 1 day, ~7% after 1 week
|
|
68
|
+
knowledge: 180, // ~99.4% after 1 day, ~84.6% after 1 month
|
|
69
|
+
palace: 9999, // effectively no decay
|
|
70
|
+
};
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Math helpers
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
/** Simple stable hash for result IDs. */
|
|
15
75
|
function stableId(source, title) {
|
|
16
76
|
let hash = 0;
|
|
17
77
|
const str = `${source}:${title}`;
|
|
@@ -22,25 +82,37 @@ function stableId(source, title) {
|
|
|
22
82
|
}
|
|
23
83
|
return Math.abs(hash).toString(36).slice(0, 8);
|
|
24
84
|
}
|
|
25
|
-
|
|
26
|
-
// Helpers
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
/** Calculate keyword overlap between query and text. */
|
|
29
|
-
function keywordExactness(query, text) {
|
|
30
|
-
const queryWords = query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
31
|
-
if (queryWords.length === 0)
|
|
32
|
-
return 0;
|
|
33
|
-
const textLower = text.toLowerCase();
|
|
34
|
-
const matches = queryWords.filter((w) => textLower.includes(w));
|
|
35
|
-
return matches.length / queryWords.length;
|
|
36
|
-
}
|
|
37
|
-
/** Days between a date string and now. */
|
|
85
|
+
/** Days elapsed since a date string. */
|
|
38
86
|
function daysSince(dateStr) {
|
|
39
87
|
const d = new Date(dateStr);
|
|
40
88
|
if (isNaN(d.getTime()))
|
|
41
|
-
return 365;
|
|
89
|
+
return 365;
|
|
42
90
|
return Math.max(0, (Date.now() - d.getTime()) / (1000 * 60 * 60 * 24));
|
|
43
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Ebbinghaus forgetting curve: R(t) = e^(-t/S).
|
|
94
|
+
* Returns retention [0,1] after `days` with strength S.
|
|
95
|
+
*/
|
|
96
|
+
function ebbinghaus(days, S) {
|
|
97
|
+
return Math.exp(-days / S);
|
|
98
|
+
}
|
|
99
|
+
/** Keyword overlap ratio between query and text. */
|
|
100
|
+
function keywordExactness(query, text) {
|
|
101
|
+
const words = query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
102
|
+
if (words.length === 0)
|
|
103
|
+
return 0;
|
|
104
|
+
const textLower = text.toLowerCase();
|
|
105
|
+
const matches = words.filter((w) => textLower.includes(w));
|
|
106
|
+
return matches.length / words.length;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Beta distribution expected value for binary feedback.
|
|
110
|
+
* E[Beta(α,β)] = α/(α+β) where α=pos+1, β=neg+1 (Laplace smoothing).
|
|
111
|
+
* Returns [~0, ~1]. Neutral (no feedback) = 0.5.
|
|
112
|
+
*/
|
|
113
|
+
function betaUtility(positives, negatives) {
|
|
114
|
+
return (positives + 1) / (positives + negatives + 2);
|
|
115
|
+
}
|
|
44
116
|
function feedbackLogPath() {
|
|
45
117
|
return path.join(getRoot(), "feedback-log.json");
|
|
46
118
|
}
|
|
@@ -55,7 +127,6 @@ function readFeedbackLog() {
|
|
|
55
127
|
return [];
|
|
56
128
|
}
|
|
57
129
|
}
|
|
58
|
-
/** Process relevance feedback — store and adjust salience signals. */
|
|
59
130
|
function processFeedback(feedback, query) {
|
|
60
131
|
ensureDir(path.dirname(feedbackLogPath()));
|
|
61
132
|
const log = readFeedbackLog();
|
|
@@ -63,45 +134,88 @@ function processFeedback(feedback, query) {
|
|
|
63
134
|
for (const f of feedback) {
|
|
64
135
|
log.push({ query, id: f.id, title: f.title ?? "", useful: f.useful, date });
|
|
65
136
|
}
|
|
66
|
-
// Keep last 200 entries
|
|
67
137
|
fs.writeFileSync(feedbackLogPath(), JSON.stringify(log.slice(-200), null, 2), "utf-8");
|
|
68
138
|
}
|
|
139
|
+
/** Count positive and negative feedback for a result item. Query-aware. */
|
|
140
|
+
function getFeedbackCounts(id, title, queryWords, log) {
|
|
141
|
+
const relevant = log.filter((f) => {
|
|
142
|
+
if (!f.query)
|
|
143
|
+
return true;
|
|
144
|
+
const fWords = f.query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
145
|
+
return queryWords.some((w) => fWords.includes(w));
|
|
146
|
+
});
|
|
147
|
+
const match = (f) => (f.id && f.id === id) || (f.title && f.title === title);
|
|
148
|
+
return {
|
|
149
|
+
positives: relevant.filter((f) => match(f) && f.useful).length,
|
|
150
|
+
negatives: relevant.filter((f) => match(f) && !f.useful).length,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// RRF merge
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
/**
|
|
157
|
+
* Apply Reciprocal Rank Fusion scores from a ranked list of items.
|
|
158
|
+
* Mutates the provided rrfMap in place.
|
|
159
|
+
* RRF_score += 1 / (k + rank)
|
|
160
|
+
*/
|
|
161
|
+
function applyRRF(rankedItems, rrfMap) {
|
|
162
|
+
rankedItems.forEach((item, idx) => {
|
|
163
|
+
const rank = idx + 1;
|
|
164
|
+
const contribution = 1 / (RRF_K + rank);
|
|
165
|
+
const existing = rrfMap.get(item.id);
|
|
166
|
+
if (existing) {
|
|
167
|
+
existing.score += contribution;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
rrfMap.set(item.id, { score: contribution, item });
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Main
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
69
177
|
export async function smartRecall(input) {
|
|
70
|
-
// Process relevance feedback if provided
|
|
71
178
|
if (input.feedback && input.feedback.length > 0) {
|
|
72
179
|
processFeedback(input.feedback, input.query);
|
|
73
180
|
}
|
|
74
181
|
const limit = input.limit ?? 10;
|
|
75
|
-
const
|
|
76
|
-
|
|
182
|
+
const feedbackLog = readFeedbackLog();
|
|
183
|
+
const queryWords = input.query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
77
184
|
const sourcesQueried = [];
|
|
78
|
-
//
|
|
185
|
+
// Candidate buckets — each source scores its items internally, then RRF merges
|
|
186
|
+
const palaceItems = [];
|
|
187
|
+
const journalItems = [];
|
|
188
|
+
const insightItems = [];
|
|
189
|
+
// ── 1. Palace ────────────────────────────────────────────────────────────
|
|
190
|
+
// Internal score: keyword match quality × salience (structural importance).
|
|
191
|
+
// Ebbinghaus decay is minimal for palace (S=9999); salience already
|
|
192
|
+
// incorporates access recency via recordAccess().
|
|
79
193
|
try {
|
|
80
194
|
const palaceResults = await palaceSearch({ query: input.query, project: input.project });
|
|
81
195
|
sourcesQueried.push("palace");
|
|
82
|
-
totalSearched += palaceResults.total_matches;
|
|
83
196
|
for (const r of palaceResults.results) {
|
|
84
197
|
const title = `${r.room}/${r.file}`;
|
|
85
|
-
|
|
86
|
-
|
|
198
|
+
const id = stableId("palace", title);
|
|
199
|
+
// keyword_score comes from updated palace-search (keyword overlap, not substring).
|
|
200
|
+
// salience floor of 0.4 prevents new rooms (salience=0.5) from being unfairly
|
|
201
|
+
// penalized against rooms with years of access history.
|
|
202
|
+
const keyScore = r.keyword_score ?? keywordExactness(input.query, r.excerpt);
|
|
203
|
+
const salience = Math.max(0.4, r.salience);
|
|
204
|
+
const internalScore = keyScore * 0.65 + salience * 0.35;
|
|
205
|
+
palaceItems.push({
|
|
206
|
+
id,
|
|
87
207
|
source: "palace",
|
|
88
208
|
title,
|
|
89
209
|
excerpt: r.excerpt,
|
|
90
|
-
score:
|
|
210
|
+
score: internalScore,
|
|
91
211
|
room: r.room,
|
|
92
212
|
});
|
|
93
|
-
// Compute score: salience as relevance, keyword match, recency unknown (use salience as proxy)
|
|
94
|
-
const relevance = r.salience;
|
|
95
|
-
const exactness = keywordExactness(input.query, r.excerpt);
|
|
96
|
-
const recency = r.salience; // palace salience already incorporates recency
|
|
97
|
-
allResults[allResults.length - 1].score =
|
|
98
|
-
relevance * 0.50 + exactness * 0.30 + recency * 0.20;
|
|
99
213
|
}
|
|
100
214
|
}
|
|
101
|
-
catch {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
215
|
+
catch { /* palace may not be initialized */ }
|
|
216
|
+
// ── 2. Journal ───────────────────────────────────────────────────────────
|
|
217
|
+
// Internal score: Ebbinghaus decay (S=2 days, fast) × keyword match.
|
|
218
|
+
// Journal is ephemeral — recent entries are useful; old ones rarely are.
|
|
105
219
|
try {
|
|
106
220
|
const journalResults = await journalSearch({
|
|
107
221
|
query: input.query,
|
|
@@ -109,86 +223,90 @@ export async function smartRecall(input) {
|
|
|
109
223
|
include_palace: false,
|
|
110
224
|
});
|
|
111
225
|
sourcesQueried.push("journal");
|
|
112
|
-
totalSearched += journalResults.results.length;
|
|
113
226
|
for (const r of journalResults.results) {
|
|
227
|
+
const title = `${r.date} / ${r.section}`;
|
|
228
|
+
const id = stableId("journal", title);
|
|
114
229
|
const days = daysSince(r.date);
|
|
115
|
-
const recency =
|
|
230
|
+
const recency = ebbinghaus(days, EBBINGHAUS_S.journal);
|
|
116
231
|
const exactness = keywordExactness(input.query, r.excerpt);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
232
|
+
// Equal weight: if the entry is recent AND relevant it scores well.
|
|
233
|
+
// Old journal entries drop fast due to S=2.
|
|
234
|
+
const internalScore = recency * 0.50 + exactness * 0.50;
|
|
235
|
+
journalItems.push({
|
|
236
|
+
id,
|
|
120
237
|
source: "journal",
|
|
121
238
|
title,
|
|
122
239
|
excerpt: r.excerpt,
|
|
123
|
-
score:
|
|
240
|
+
score: internalScore,
|
|
124
241
|
date: r.date,
|
|
125
242
|
});
|
|
126
243
|
}
|
|
127
244
|
}
|
|
128
|
-
catch {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
//
|
|
245
|
+
catch { /* journal may not exist */ }
|
|
246
|
+
// ── 3. Insights ──────────────────────────────────────────────────────────
|
|
247
|
+
// Internal score: keyword relevance × confirmation signal (log-scaled).
|
|
248
|
+
// Insights are timeless learned patterns — confirmation count is the signal,
|
|
249
|
+
// not recency. More confirmations = more reliable.
|
|
132
250
|
try {
|
|
133
251
|
const insightResults = await recallInsight({
|
|
134
252
|
context: input.query,
|
|
135
|
-
limit: limit,
|
|
253
|
+
limit: limit * 2,
|
|
136
254
|
include_awareness: false,
|
|
137
255
|
});
|
|
138
256
|
sourcesQueried.push("insight");
|
|
139
|
-
totalSearched += insightResults.total_in_index;
|
|
140
|
-
// Normalize relevance scores
|
|
141
257
|
const maxRelevance = Math.max(1, ...insightResults.matching_insights.map((i) => i.relevance));
|
|
142
258
|
for (const i of insightResults.matching_insights) {
|
|
259
|
+
const id = stableId("insight", i.title);
|
|
143
260
|
const relevance = i.relevance / maxRelevance;
|
|
144
261
|
const exactness = keywordExactness(input.query, i.title);
|
|
145
|
-
//
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
262
|
+
// log2(confirmed+1)/3 gives: 0→0, 1→0.33, 3→0.67, 7→1.0
|
|
263
|
+
const confirmation = Math.min(1.0, Math.log2(i.confirmed + 1) / 3);
|
|
264
|
+
const internalScore = relevance * 0.40 + exactness * 0.35 + confirmation * 0.25;
|
|
265
|
+
insightItems.push({
|
|
266
|
+
id,
|
|
149
267
|
source: "insight",
|
|
150
268
|
title: i.title,
|
|
151
269
|
excerpt: `[${i.severity}] ${i.applies_when.join(", ")}`,
|
|
152
|
-
score:
|
|
270
|
+
score: internalScore,
|
|
153
271
|
severity: i.severity,
|
|
154
272
|
});
|
|
155
273
|
}
|
|
156
274
|
}
|
|
157
|
-
catch {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
r.score += (positives * 0.03) - (negatives * 0.05);
|
|
177
|
-
r.score = Math.max(0, Math.min(1, r.score));
|
|
275
|
+
catch { /* insights may be empty */ }
|
|
276
|
+
// ── 4. Rank within each source, then merge via RRF ───────────────────────
|
|
277
|
+
// Each source ranks by its own internal score (apples vs apples).
|
|
278
|
+
// RRF then combines by rank position — no cross-source score comparison.
|
|
279
|
+
palaceItems.sort((a, b) => b.score - a.score);
|
|
280
|
+
journalItems.sort((a, b) => b.score - a.score);
|
|
281
|
+
insightItems.sort((a, b) => b.score - a.score);
|
|
282
|
+
const rrfMap = new Map();
|
|
283
|
+
applyRRF(palaceItems, rrfMap);
|
|
284
|
+
applyRRF(journalItems, rrfMap);
|
|
285
|
+
applyRRF(insightItems, rrfMap);
|
|
286
|
+
// ── 5. Apply Beta feedback multiplier ────────────────────────────────────
|
|
287
|
+
// betaUtility returns [0,1]; ×2 normalizes so neutral (0.5) = ×1.0.
|
|
288
|
+
// Items with positive history are boosted; negative history suppressed.
|
|
289
|
+
for (const entry of rrfMap.values()) {
|
|
290
|
+
const { positives, negatives } = getFeedbackCounts(entry.item.id, entry.item.title, queryWords, feedbackLog);
|
|
291
|
+
if (positives > 0 || negatives > 0) {
|
|
292
|
+
const multiplier = betaUtility(positives, negatives) * 2;
|
|
293
|
+
entry.score *= multiplier;
|
|
178
294
|
}
|
|
179
295
|
}
|
|
180
|
-
//
|
|
296
|
+
// ── 6. Deduplicate by excerpt content ────────────────────────────────────
|
|
181
297
|
const seen = new Set();
|
|
182
298
|
const deduped = [];
|
|
183
|
-
for (const
|
|
184
|
-
const key =
|
|
299
|
+
for (const { score, item } of rrfMap.values()) {
|
|
300
|
+
const key = item.excerpt.toLowerCase().replace(/\s+/g, " ").trim();
|
|
185
301
|
if (seen.has(key))
|
|
186
302
|
continue;
|
|
187
303
|
seen.add(key);
|
|
188
|
-
deduped.push(
|
|
304
|
+
deduped.push({ ...item, score });
|
|
189
305
|
}
|
|
190
|
-
//
|
|
306
|
+
// ── 7. Final sort and return ──────────────────────────────────────────────
|
|
191
307
|
deduped.sort((a, b) => b.score - a.score);
|
|
308
|
+
// total_searched = candidate items actually scanned (consistent metric)
|
|
309
|
+
const totalSearched = palaceItems.length + journalItems.length + insightItems.length;
|
|
192
310
|
return {
|
|
193
311
|
query: input.query,
|
|
194
312
|
results: deduped.slice(0, limit),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-recall.js","sourceRoot":"","sources":["../../src/tools-logic/smart-recall.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"smart-recall.js","sourceRoot":"","sources":["../../src/tools-logic/smart-recall.ts"],"names":[],"mappings":"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"}
|