@remnic/core 9.3.597 → 9.3.598
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/access-cli.js +17 -17
- package/dist/access-http.js +6 -6
- package/dist/access-mcp.js +5 -5
- package/dist/access-service.js +4 -4
- package/dist/behavior-learner.js +2 -1
- package/dist/behavior-learner.js.map +1 -1
- package/dist/causal-behavior.js +3 -3
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.js +4 -4
- package/dist/causal-retrieval.js +3 -3
- package/dist/causal-trajectory.js +2 -2
- package/dist/{chunk-A2Z6UCWT.js → chunk-33JBK2XP.js} +2 -2
- package/dist/{chunk-D2MMMTDV.js → chunk-5SQ5CQJP.js} +2 -2
- package/dist/{chunk-F4LM4ULA.js → chunk-65JSA4MP.js} +12 -12
- package/dist/{chunk-SKGV326D.js → chunk-6GDHLVJC.js} +2 -2
- package/dist/chunk-6HEM6HTQ.js +359 -0
- package/dist/chunk-6HEM6HTQ.js.map +1 -0
- package/dist/{chunk-WXACKLKP.js → chunk-75O6YQ63.js} +22 -7
- package/dist/chunk-75O6YQ63.js.map +1 -0
- package/dist/{chunk-D65TSG24.js → chunk-7DZRO2DC.js} +2 -2
- package/dist/{chunk-TYICDVQW.js → chunk-BDCCWRHR.js} +4 -4
- package/dist/{chunk-LYPDMKUT.js → chunk-CL3MWNNQ.js} +2 -2
- package/dist/{chunk-YQMZ7IH2.js → chunk-D4KJ74JJ.js} +65 -27
- package/dist/chunk-D4KJ74JJ.js.map +1 -0
- package/dist/{chunk-W5O2FQTZ.js → chunk-GUPISBV2.js} +2 -2
- package/dist/{chunk-472U7RDF.js → chunk-JGSKJHF7.js} +2 -2
- package/dist/{chunk-IEFHBIU2.js → chunk-KDUVQU6Y.js} +14 -14
- package/dist/{chunk-6HZ6AO2P.js → chunk-LBJBNWS2.js} +37 -10
- package/dist/chunk-LBJBNWS2.js.map +1 -0
- package/dist/{chunk-Z4R6RI2N.js → chunk-NSKYFGDL.js} +2 -2
- package/dist/{chunk-OD5LFAPZ.js → chunk-V67GWXM2.js} +1 -1
- package/dist/{chunk-5NXIJZFX.js → chunk-WR64DQFE.js} +3 -3
- package/dist/{chunk-5BUGGPBR.js → chunk-WZA5Y6AC.js} +3 -3
- package/dist/chunk-ZBJMUXZH.js +121 -0
- package/dist/chunk-ZBJMUXZH.js.map +1 -0
- package/dist/{chunk-XPXEJRUB.js → chunk-ZRWB5D4H.js} +2 -2
- package/dist/{chunk-MA5MWGKP.js → chunk-ZT3EGNLR.js} +2 -2
- package/dist/{chunk-LMPHTYJC.js → chunk-ZZYF3BUL.js} +2 -2
- package/dist/cli.js +14 -14
- package/dist/compounding/engine.js +1 -1
- package/dist/direct-answer-wiring.js +3 -3
- package/dist/direct-answer.d.ts +1 -1
- package/dist/direct-answer.js +2 -2
- package/dist/harmonic-retrieval.js +2 -2
- package/dist/index.js +21 -21
- package/dist/orchestrator.js +16 -16
- package/dist/policy-runtime.js +3 -2
- package/dist/recall-query-policy.js +2 -1
- package/dist/recall-tokenization.d.ts +5 -1
- package/dist/recall-tokenization.js +3 -1
- package/dist/resume-bundles.js +3 -3
- package/dist/retrieval-agents.js +2 -2
- package/dist/schemas.d.ts +22 -22
- package/dist/semantic-consolidation.js +2 -2
- package/dist/semantic-rule-verifier.js +2 -2
- package/dist/temporal-index.js +1 -1
- package/dist/transfer/types.d.ts +12 -12
- package/dist/trust-zones.js +2 -2
- package/dist/verified-recall.js +2 -2
- package/dist/work-product-ledger.js +2 -2
- package/package.json +1 -1
- package/src/causal-chain.ts +80 -42
- package/src/direct-answer.test.ts +618 -15
- package/src/direct-answer.ts +259 -20
- package/src/recall-query-policy.ts +49 -27
- package/src/recall-tokenization.ts +131 -21
- package/src/temporal-index.ts +23 -6
- package/dist/chunk-6HZ6AO2P.js.map +0 -1
- package/dist/chunk-DT5TVLJE.js +0 -32
- package/dist/chunk-DT5TVLJE.js.map +0 -1
- package/dist/chunk-WXACKLKP.js.map +0 -1
- package/dist/chunk-Y4FHOFJ2.js +0 -140
- package/dist/chunk-Y4FHOFJ2.js.map +0 -1
- package/dist/chunk-YQMZ7IH2.js.map +0 -1
- /package/dist/{chunk-A2Z6UCWT.js.map → chunk-33JBK2XP.js.map} +0 -0
- /package/dist/{chunk-D2MMMTDV.js.map → chunk-5SQ5CQJP.js.map} +0 -0
- /package/dist/{chunk-F4LM4ULA.js.map → chunk-65JSA4MP.js.map} +0 -0
- /package/dist/{chunk-SKGV326D.js.map → chunk-6GDHLVJC.js.map} +0 -0
- /package/dist/{chunk-D65TSG24.js.map → chunk-7DZRO2DC.js.map} +0 -0
- /package/dist/{chunk-TYICDVQW.js.map → chunk-BDCCWRHR.js.map} +0 -0
- /package/dist/{chunk-LYPDMKUT.js.map → chunk-CL3MWNNQ.js.map} +0 -0
- /package/dist/{chunk-W5O2FQTZ.js.map → chunk-GUPISBV2.js.map} +0 -0
- /package/dist/{chunk-472U7RDF.js.map → chunk-JGSKJHF7.js.map} +0 -0
- /package/dist/{chunk-IEFHBIU2.js.map → chunk-KDUVQU6Y.js.map} +0 -0
- /package/dist/{chunk-Z4R6RI2N.js.map → chunk-NSKYFGDL.js.map} +0 -0
- /package/dist/{chunk-OD5LFAPZ.js.map → chunk-V67GWXM2.js.map} +0 -0
- /package/dist/{chunk-5NXIJZFX.js.map → chunk-WR64DQFE.js.map} +0 -0
- /package/dist/{chunk-5BUGGPBR.js.map → chunk-WZA5Y6AC.js.map} +0 -0
- /package/dist/{chunk-XPXEJRUB.js.map → chunk-ZRWB5D4H.js.map} +0 -0
- /package/dist/{chunk-MA5MWGKP.js.map → chunk-ZT3EGNLR.js.map} +0 -0
- /package/dist/{chunk-LMPHTYJC.js.map → chunk-ZZYF3BUL.js.map} +0 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeRecallTokenSet,
|
|
3
|
+
normalizeRecallTokens
|
|
4
|
+
} from "./chunk-ZBJMUXZH.js";
|
|
5
|
+
|
|
6
|
+
// src/direct-answer.ts
|
|
7
|
+
var FILTER_LABELS = {
|
|
8
|
+
nonActiveStatus: "non-active-status",
|
|
9
|
+
notTrustedZone: "not-trusted-zone",
|
|
10
|
+
ineligibleTaxonomyBucket: "ineligible-taxonomy-bucket",
|
|
11
|
+
belowImportanceFloor: "below-importance-floor",
|
|
12
|
+
entityRefMismatch: "entity-ref-mismatch",
|
|
13
|
+
belowTokenOverlapFloor: "below-token-overlap-floor"
|
|
14
|
+
};
|
|
15
|
+
var PROMPT_RECALL_WORDS = /* @__PURE__ */ new Set([
|
|
16
|
+
"what",
|
|
17
|
+
"who",
|
|
18
|
+
"where",
|
|
19
|
+
"when",
|
|
20
|
+
"why",
|
|
21
|
+
"how",
|
|
22
|
+
"is",
|
|
23
|
+
"are",
|
|
24
|
+
"was",
|
|
25
|
+
"were",
|
|
26
|
+
"do",
|
|
27
|
+
"does",
|
|
28
|
+
"did",
|
|
29
|
+
"find",
|
|
30
|
+
"get",
|
|
31
|
+
"show",
|
|
32
|
+
"search",
|
|
33
|
+
"lookup",
|
|
34
|
+
"recall",
|
|
35
|
+
"remember",
|
|
36
|
+
"list",
|
|
37
|
+
"status",
|
|
38
|
+
"include",
|
|
39
|
+
"tell",
|
|
40
|
+
"me",
|
|
41
|
+
"give",
|
|
42
|
+
"about",
|
|
43
|
+
"please",
|
|
44
|
+
"the",
|
|
45
|
+
"and",
|
|
46
|
+
"for",
|
|
47
|
+
"with",
|
|
48
|
+
"from",
|
|
49
|
+
"into",
|
|
50
|
+
"that",
|
|
51
|
+
"this",
|
|
52
|
+
"\u043D\u0430\u0439\u0434\u0438",
|
|
53
|
+
"\u043D\u0430\u0439\u0442\u0438",
|
|
54
|
+
"\u043F\u043E\u0438\u0441\u043A",
|
|
55
|
+
"\u043F\u043E\u043A\u0430\u0436\u0438",
|
|
56
|
+
"\u0441\u0442\u0430\u0442\u0443\u0441",
|
|
57
|
+
"\u0432\u043A\u043B\u044E\u0447\u0438"
|
|
58
|
+
]);
|
|
59
|
+
function hasUnsegmentableRecallChar(token) {
|
|
60
|
+
if (token.includes("\u30FC") || token.includes("\uFF70")) return true;
|
|
61
|
+
return /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(token);
|
|
62
|
+
}
|
|
63
|
+
function requiredCjkPhraseTokens(query) {
|
|
64
|
+
const phrases = /* @__PURE__ */ new Set();
|
|
65
|
+
let segment = "";
|
|
66
|
+
const addPhrase = (phrase) => {
|
|
67
|
+
if ([...phrase].length >= 4) {
|
|
68
|
+
phrases.add(phrase);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const flushSegment = () => {
|
|
72
|
+
let buffered = "";
|
|
73
|
+
for (const run of segment.split(/\s+/)) {
|
|
74
|
+
if (!run) continue;
|
|
75
|
+
if ([...run].length >= 4) {
|
|
76
|
+
addPhrase(buffered);
|
|
77
|
+
buffered = "";
|
|
78
|
+
addPhrase(run);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
buffered += run;
|
|
82
|
+
}
|
|
83
|
+
addPhrase(buffered);
|
|
84
|
+
segment = "";
|
|
85
|
+
};
|
|
86
|
+
for (const ch of query.toLowerCase().normalize("NFC")) {
|
|
87
|
+
if (hasUnsegmentableRecallChar(ch)) {
|
|
88
|
+
segment += ch;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (/\p{M}/u.test(ch) && segment.length > 0 && !/\s$/u.test(segment)) {
|
|
92
|
+
segment += ch;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (/\s/u.test(ch)) {
|
|
96
|
+
segment += " ";
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
flushSegment();
|
|
100
|
+
}
|
|
101
|
+
flushSegment();
|
|
102
|
+
return [...phrases];
|
|
103
|
+
}
|
|
104
|
+
function requiredMixedScriptTokens(query) {
|
|
105
|
+
const required = /* @__PURE__ */ new Set();
|
|
106
|
+
const parts = [];
|
|
107
|
+
let segment = "";
|
|
108
|
+
const flushSegment = () => {
|
|
109
|
+
if (segment) {
|
|
110
|
+
parts.push(segment);
|
|
111
|
+
}
|
|
112
|
+
segment = "";
|
|
113
|
+
};
|
|
114
|
+
const segmentableRecallTokens = (value) => {
|
|
115
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
116
|
+
let segment2 = "";
|
|
117
|
+
const flushSegmentableSegment = () => {
|
|
118
|
+
for (const token of normalizeRecallTokenSet(segment2, [], { minTokenLength: 1 })) {
|
|
119
|
+
tokens.add(token);
|
|
120
|
+
}
|
|
121
|
+
segment2 = "";
|
|
122
|
+
};
|
|
123
|
+
for (const ch of value) {
|
|
124
|
+
if (/[\p{L}\p{N}]/u.test(ch) && !hasUnsegmentableRecallChar(ch)) {
|
|
125
|
+
segment2 += ch;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (/\p{M}/u.test(ch) && segment2.length > 0) {
|
|
129
|
+
segment2 += ch;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
flushSegmentableSegment();
|
|
133
|
+
}
|
|
134
|
+
flushSegmentableSegment();
|
|
135
|
+
return tokens;
|
|
136
|
+
};
|
|
137
|
+
const hasRequiredSegmentableToken = (value) => {
|
|
138
|
+
return segmentableRecallTokens(value).size > 0;
|
|
139
|
+
};
|
|
140
|
+
const hasBoundarySegmentableToken = (value) => {
|
|
141
|
+
for (const token of segmentableRecallTokens(value)) {
|
|
142
|
+
if (PROMPT_RECALL_WORDS.has(token)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const hasNonAsciiCodepoint = [...token].some((ch) => (ch.codePointAt(0) ?? 0) > 127);
|
|
146
|
+
if (token.length >= 3 || /\p{N}/u.test(token) || hasNonAsciiCodepoint) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
};
|
|
152
|
+
const addRequiredTokens = (value) => {
|
|
153
|
+
for (const token of normalizeRecallTokenSet(value, [], { minTokenLength: 1 })) {
|
|
154
|
+
required.add(token);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
for (const ch of query.toLowerCase().normalize("NFC")) {
|
|
158
|
+
if (/[\p{L}\p{N}\p{M}]/u.test(ch) || hasUnsegmentableRecallChar(ch)) {
|
|
159
|
+
segment += ch;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
flushSegment();
|
|
163
|
+
}
|
|
164
|
+
flushSegment();
|
|
165
|
+
for (const part of parts) {
|
|
166
|
+
if (hasUnsegmentableRecallChar(part) && hasRequiredSegmentableToken(part)) {
|
|
167
|
+
addRequiredTokens(part);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
171
|
+
const current = parts[i];
|
|
172
|
+
const next = parts[i + 1];
|
|
173
|
+
const currentHasUnsegmentable = hasUnsegmentableRecallChar(current);
|
|
174
|
+
const nextHasUnsegmentable = hasUnsegmentableRecallChar(next);
|
|
175
|
+
if (currentHasUnsegmentable === nextHasUnsegmentable) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const segmentablePart = currentHasUnsegmentable ? next : current;
|
|
179
|
+
if (!hasBoundarySegmentableToken(segmentablePart)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
addRequiredTokens(current);
|
|
183
|
+
addRequiredTokens(next);
|
|
184
|
+
}
|
|
185
|
+
for (let i = 1; i < parts.length - 1; i += 1) {
|
|
186
|
+
const prev = parts[i - 1];
|
|
187
|
+
const current = parts[i];
|
|
188
|
+
const next = parts[i + 1];
|
|
189
|
+
if (!hasUnsegmentableRecallChar(prev) || hasUnsegmentableRecallChar(current) || !hasUnsegmentableRecallChar(next)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (!hasRequiredSegmentableToken(current)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
addRequiredTokens(prev);
|
|
196
|
+
addRequiredTokens(current);
|
|
197
|
+
addRequiredTokens(next);
|
|
198
|
+
}
|
|
199
|
+
return [...required];
|
|
200
|
+
}
|
|
201
|
+
function requiredSegmentableUnicodeTokens(queryTokens) {
|
|
202
|
+
const segmentableTokens = [...queryTokens].filter((token) => !hasUnsegmentableRecallChar(token));
|
|
203
|
+
const hasSegmentableUnicodeToken = segmentableTokens.some(
|
|
204
|
+
(token) => [...token].some((ch) => (ch.codePointAt(0) ?? 0) > 127)
|
|
205
|
+
);
|
|
206
|
+
if (!hasSegmentableUnicodeToken) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
return segmentableTokens.filter((token) => {
|
|
210
|
+
if (PROMPT_RECALL_WORDS.has(token)) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
const hasNonAsciiCodepoint = [...token].some((ch) => (ch.codePointAt(0) ?? 0) > 127);
|
|
214
|
+
if (hasNonAsciiCodepoint) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
if (!/^[a-z0-9]+$/u.test(token)) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return token.length >= 3 || /\p{N}/u.test(token);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
function countTokenOverlap(queryTokens, valueTokens) {
|
|
224
|
+
let matches = 0;
|
|
225
|
+
for (const token of queryTokens) {
|
|
226
|
+
if (valueTokens.has(token)) matches += 1;
|
|
227
|
+
}
|
|
228
|
+
return matches;
|
|
229
|
+
}
|
|
230
|
+
function isDirectAnswerEligible(input) {
|
|
231
|
+
const { query, candidates, config, queryEntityRefs } = input;
|
|
232
|
+
if (!config.enabled) {
|
|
233
|
+
return {
|
|
234
|
+
eligible: false,
|
|
235
|
+
reason: "disabled",
|
|
236
|
+
narrative: "direct-answer tier is disabled",
|
|
237
|
+
filteredBy: []
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const queryTokens = new Set(normalizeRecallTokens(query));
|
|
241
|
+
if (queryTokens.size === 0) {
|
|
242
|
+
return {
|
|
243
|
+
eligible: false,
|
|
244
|
+
reason: "empty-query",
|
|
245
|
+
narrative: "query has no searchable tokens after normalization",
|
|
246
|
+
filteredBy: []
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (candidates.length === 0) {
|
|
250
|
+
return {
|
|
251
|
+
eligible: false,
|
|
252
|
+
reason: "no-candidates",
|
|
253
|
+
narrative: "no candidates supplied",
|
|
254
|
+
filteredBy: []
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const filteredBy = [];
|
|
258
|
+
let working = candidates;
|
|
259
|
+
working = applyFilter(working, filteredBy, FILTER_LABELS.nonActiveStatus, (c) => {
|
|
260
|
+
const status = c.memory.frontmatter.status ?? "active";
|
|
261
|
+
return status === "active";
|
|
262
|
+
});
|
|
263
|
+
working = applyFilter(working, filteredBy, FILTER_LABELS.notTrustedZone, (c) => c.trustZone === "trusted");
|
|
264
|
+
working = applyFilter(
|
|
265
|
+
working,
|
|
266
|
+
filteredBy,
|
|
267
|
+
FILTER_LABELS.ineligibleTaxonomyBucket,
|
|
268
|
+
(c) => c.taxonomyBucket !== null && config.eligibleTaxonomyBuckets.includes(c.taxonomyBucket)
|
|
269
|
+
);
|
|
270
|
+
working = applyFilter(working, filteredBy, FILTER_LABELS.belowImportanceFloor, (c) => {
|
|
271
|
+
if (c.memory.frontmatter.verificationState === "user_confirmed") return true;
|
|
272
|
+
return c.importanceScore >= config.importanceFloor;
|
|
273
|
+
});
|
|
274
|
+
if (queryEntityRefs && queryEntityRefs.length > 0) {
|
|
275
|
+
const normRefs = new Set(queryEntityRefs.map((r) => r.toLowerCase()));
|
|
276
|
+
working = applyFilter(working, filteredBy, FILTER_LABELS.entityRefMismatch, (c) => {
|
|
277
|
+
const ref = c.memory.frontmatter.entityRef;
|
|
278
|
+
if (!ref) return true;
|
|
279
|
+
return normRefs.has(ref.toLowerCase());
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (working.length === 0) {
|
|
283
|
+
return {
|
|
284
|
+
eligible: false,
|
|
285
|
+
reason: "no-eligible-candidates",
|
|
286
|
+
narrative: "no candidates survived eligibility filters",
|
|
287
|
+
filteredBy
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const scored = working.map((candidate) => {
|
|
291
|
+
const searchable = `${candidate.memory.frontmatter.tags?.join(" ") ?? ""} ${candidate.memory.content}`.trim();
|
|
292
|
+
const searchableTokens = normalizeRecallTokenSet(searchable);
|
|
293
|
+
const requiredSearchableTokens = normalizeRecallTokenSet(searchable, [], { minTokenLength: 1 });
|
|
294
|
+
const requiredPhrases = requiredCjkPhraseTokens(query);
|
|
295
|
+
const requiredMixedTokens = requiredMixedScriptTokens(query);
|
|
296
|
+
const requiredUnicodeTokens = requiredSegmentableUnicodeTokens(queryTokens);
|
|
297
|
+
const hasRequiredPhrase = requiredPhrases.length === 0 || requiredPhrases.every((token) => searchableTokens.has(token));
|
|
298
|
+
const hasRequiredMixedTokens = requiredMixedTokens.length === 0 || requiredMixedTokens.every((token) => requiredSearchableTokens.has(token));
|
|
299
|
+
const hasRequiredUnicodeTokens = requiredUnicodeTokens.length === 0 || requiredUnicodeTokens.every((token) => searchableTokens.has(token));
|
|
300
|
+
const requiredTokenMismatch = !hasRequiredPhrase || !hasRequiredMixedTokens || !hasRequiredUnicodeTokens;
|
|
301
|
+
const matches = requiredTokenMismatch ? 0 : countTokenOverlap(queryTokens, searchableTokens);
|
|
302
|
+
return { candidate, tokenOverlap: matches / queryTokens.size, requiredTokenMismatch };
|
|
303
|
+
});
|
|
304
|
+
const overlapSurvivors = scored.filter((s) => !s.requiredTokenMismatch && s.tokenOverlap >= config.tokenOverlapFloor);
|
|
305
|
+
if (overlapSurvivors.length < scored.length) {
|
|
306
|
+
filteredBy.push(FILTER_LABELS.belowTokenOverlapFloor);
|
|
307
|
+
}
|
|
308
|
+
if (overlapSurvivors.length === 0) {
|
|
309
|
+
return {
|
|
310
|
+
eligible: false,
|
|
311
|
+
reason: "below-token-overlap-floor",
|
|
312
|
+
narrative: `no candidate met token-overlap floor ${config.tokenOverlapFloor}`,
|
|
313
|
+
filteredBy
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
overlapSurvivors.sort(compareScored);
|
|
317
|
+
if (overlapSurvivors.length >= 2) {
|
|
318
|
+
const topScore = scoreFor(overlapSurvivors[0]);
|
|
319
|
+
const secondScore = scoreFor(overlapSurvivors[1]);
|
|
320
|
+
if (topScore - secondScore < config.ambiguityMargin) {
|
|
321
|
+
return {
|
|
322
|
+
eligible: false,
|
|
323
|
+
reason: "ambiguous",
|
|
324
|
+
narrative: `top two candidates within ambiguityMargin ${config.ambiguityMargin}`,
|
|
325
|
+
filteredBy
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const winner = overlapSurvivors[0];
|
|
330
|
+
const bucket = winner.candidate.taxonomyBucket ?? "unknown";
|
|
331
|
+
return {
|
|
332
|
+
eligible: true,
|
|
333
|
+
reason: "eligible",
|
|
334
|
+
winner: winner.candidate,
|
|
335
|
+
tokenOverlap: winner.tokenOverlap,
|
|
336
|
+
narrative: `trusted ${bucket}, unambiguous, token-overlap ${winner.tokenOverlap.toFixed(2)}`,
|
|
337
|
+
filteredBy
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function applyFilter(working, filteredBy, label, keep) {
|
|
341
|
+
const before = working.length;
|
|
342
|
+
const next = working.filter(keep);
|
|
343
|
+
if (next.length < before) filteredBy.push(label);
|
|
344
|
+
return next;
|
|
345
|
+
}
|
|
346
|
+
function scoreFor(s) {
|
|
347
|
+
return s.candidate.matchScore ?? s.tokenOverlap;
|
|
348
|
+
}
|
|
349
|
+
function compareScored(a, b) {
|
|
350
|
+
const diff = scoreFor(b) - scoreFor(a);
|
|
351
|
+
if (diff !== 0) return diff;
|
|
352
|
+
return a.candidate.memory.path.localeCompare(b.candidate.memory.path);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export {
|
|
356
|
+
FILTER_LABELS,
|
|
357
|
+
isDirectAnswerEligible
|
|
358
|
+
};
|
|
359
|
+
//# sourceMappingURL=chunk-6HEM6HTQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/direct-answer.ts"],"sourcesContent":["/**\n * Direct-answer retrieval tier eligibility (issue #518 slice 2).\n *\n * This module is a pure decision layer. It takes a query, a set of\n * caller-resolved candidates (each already decorated with trust-zone,\n * taxonomy-bucket, and importance information), and the direct-answer\n * config, then returns an eligibility verdict.\n *\n * Keeping the module pure means:\n *\n * - Tests do not need a trust-zones store, taxonomy resolver, or importance\n * scorer on disk.\n * - Slice 3 (retrieval.ts wiring) is responsible for resolving those signals\n * before calling in; the wiring layer decides where candidates come from\n * (entity index, token prefilter, etc.). This module only decides\n * whether the surfaced candidates add up to a confident direct answer.\n *\n * Not wired into retrieval yet — see slice 3.\n */\n\nimport { normalizeRecallTokenSet, normalizeRecallTokens } from \"./recall-tokenization.js\";\nimport type { TrustZoneName } from \"./trust-zones.js\";\nimport type { MemoryFile, MemoryStatus } from \"./types.js\";\n\n/**\n * Caller-supplied candidate.\n *\n * `trustZone`, `taxonomyBucket`, and `importanceScore` are resolved outside\n * this module because each comes from a different subsystem. Passing them\n * as inputs keeps the function deterministic and easy to unit-test.\n *\n * `matchScore` is optional; if omitted, candidates are ranked by\n * token-overlap ratio. Callers that already computed a better ranking\n * score (e.g. BM25, vector similarity) can supply it to drive the\n * ambiguity check.\n */\nexport interface DirectAnswerCandidate {\n memory: MemoryFile;\n trustZone: TrustZoneName | null;\n taxonomyBucket: string | null;\n importanceScore: number;\n matchScore?: number;\n}\n\nexport interface DirectAnswerConfig {\n enabled: boolean;\n tokenOverlapFloor: number;\n importanceFloor: number;\n ambiguityMargin: number;\n eligibleTaxonomyBuckets: string[];\n}\n\nexport interface DirectAnswerInput {\n query: string;\n candidates: DirectAnswerCandidate[];\n config: DirectAnswerConfig;\n /**\n * Optional entity-ref hints resolved from the query upstream. When\n * supplied, a candidate with a set `entityRef` must match one of these\n * (case-insensitive) to remain eligible. Candidates without an\n * `entityRef` are allowed through regardless.\n */\n queryEntityRefs?: string[];\n}\n\nexport type DirectAnswerReason =\n | \"disabled\"\n | \"empty-query\"\n | \"no-candidates\"\n | \"no-eligible-candidates\"\n | \"below-token-overlap-floor\"\n | \"ambiguous\"\n | \"eligible\";\n\nexport interface DirectAnswerResult {\n eligible: boolean;\n reason: DirectAnswerReason;\n /** Winning candidate when eligible. */\n winner?: DirectAnswerCandidate;\n /** Computed token-overlap ratio (0..1) of the winner. */\n tokenOverlap?: number;\n /**\n * Human-readable summary suitable for\n * `RecallTierExplain.tierReason`.\n */\n narrative: string;\n /**\n * Filter labels that eliminated at least one candidate along the way.\n * Populated regardless of eligibility so the caller can surface the\n * narrowing steps in `RecallTierExplain.filteredBy`.\n */\n filteredBy: string[];\n}\n\n/** Filter labels — exported so callers and tests can match them structurally. */\nexport const FILTER_LABELS = {\n nonActiveStatus: \"non-active-status\",\n notTrustedZone: \"not-trusted-zone\",\n ineligibleTaxonomyBucket: \"ineligible-taxonomy-bucket\",\n belowImportanceFloor: \"below-importance-floor\",\n entityRefMismatch: \"entity-ref-mismatch\",\n belowTokenOverlapFloor: \"below-token-overlap-floor\",\n} as const;\n\nconst PROMPT_RECALL_WORDS = new Set([\n \"what\",\n \"who\",\n \"where\",\n \"when\",\n \"why\",\n \"how\",\n \"is\",\n \"are\",\n \"was\",\n \"were\",\n \"do\",\n \"does\",\n \"did\",\n \"find\",\n \"get\",\n \"show\",\n \"search\",\n \"lookup\",\n \"recall\",\n \"remember\",\n \"list\",\n \"status\",\n \"include\",\n \"tell\",\n \"me\",\n \"give\",\n \"about\",\n \"please\",\n \"the\",\n \"and\",\n \"for\",\n \"with\",\n \"from\",\n \"into\",\n \"that\",\n \"this\",\n \"найди\",\n \"найти\",\n \"поиск\",\n \"покажи\",\n \"статус\",\n \"включи\",\n]);\n\ninterface ScoredCandidate {\n candidate: DirectAnswerCandidate;\n tokenOverlap: number;\n requiredTokenMismatch: boolean;\n}\n\nfunction hasUnsegmentableRecallChar(token: string): boolean {\n if (token.includes(\"ー\") || token.includes(\"ー\")) return true;\n return /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}]/u.test(token);\n}\n\nfunction requiredCjkPhraseTokens(query: string): string[] {\n const phrases = new Set<string>();\n let segment = \"\";\n\n const addPhrase = (phrase: string) => {\n if ([...phrase].length >= 4) {\n phrases.add(phrase);\n }\n };\n\n const flushSegment = () => {\n let buffered = \"\";\n for (const run of segment.split(/\\s+/)) {\n if (!run) continue;\n if ([...run].length >= 4) {\n addPhrase(buffered);\n buffered = \"\";\n addPhrase(run);\n continue;\n }\n buffered += run;\n }\n addPhrase(buffered);\n segment = \"\";\n };\n\n for (const ch of query.toLowerCase().normalize(\"NFC\")) {\n if (hasUnsegmentableRecallChar(ch)) {\n segment += ch;\n continue;\n }\n if (/\\p{M}/u.test(ch) && segment.length > 0 && !/\\s$/u.test(segment)) {\n segment += ch;\n continue;\n }\n if (/\\s/u.test(ch)) {\n segment += \" \";\n continue;\n }\n flushSegment();\n }\n flushSegment();\n\n return [...phrases];\n}\n\nfunction requiredMixedScriptTokens(query: string): string[] {\n const required = new Set<string>();\n const parts: string[] = [];\n let segment = \"\";\n\n const flushSegment = () => {\n if (segment) {\n parts.push(segment);\n }\n segment = \"\";\n };\n\n const segmentableRecallTokens = (value: string) => {\n const tokens = new Set<string>();\n let segment = \"\";\n const flushSegmentableSegment = () => {\n for (const token of normalizeRecallTokenSet(segment, [], { minTokenLength: 1 })) {\n tokens.add(token);\n }\n segment = \"\";\n };\n\n for (const ch of value) {\n if (/[\\p{L}\\p{N}]/u.test(ch) && !hasUnsegmentableRecallChar(ch)) {\n segment += ch;\n continue;\n }\n if (/\\p{M}/u.test(ch) && segment.length > 0) {\n segment += ch;\n continue;\n }\n flushSegmentableSegment();\n }\n flushSegmentableSegment();\n return tokens;\n };\n\n const hasRequiredSegmentableToken = (value: string) => {\n return segmentableRecallTokens(value).size > 0;\n };\n\n const hasBoundarySegmentableToken = (value: string) => {\n for (const token of segmentableRecallTokens(value)) {\n if (PROMPT_RECALL_WORDS.has(token)) {\n continue;\n }\n const hasNonAsciiCodepoint = [...token].some((ch) => (ch.codePointAt(0) ?? 0) > 0x7f);\n if (token.length >= 3 || /\\p{N}/u.test(token) || hasNonAsciiCodepoint) {\n return true;\n }\n }\n return false;\n };\n\n const addRequiredTokens = (value: string) => {\n for (const token of normalizeRecallTokenSet(value, [], { minTokenLength: 1 })) {\n required.add(token);\n }\n };\n\n for (const ch of query.toLowerCase().normalize(\"NFC\")) {\n if (/[\\p{L}\\p{N}\\p{M}]/u.test(ch) || hasUnsegmentableRecallChar(ch)) {\n segment += ch;\n continue;\n }\n flushSegment();\n }\n flushSegment();\n\n for (const part of parts) {\n if (hasUnsegmentableRecallChar(part) && hasRequiredSegmentableToken(part)) {\n addRequiredTokens(part);\n }\n }\n\n for (let i = 0; i < parts.length - 1; i += 1) {\n const current = parts[i];\n const next = parts[i + 1];\n const currentHasUnsegmentable = hasUnsegmentableRecallChar(current);\n const nextHasUnsegmentable = hasUnsegmentableRecallChar(next);\n if (currentHasUnsegmentable === nextHasUnsegmentable) {\n continue;\n }\n const segmentablePart = currentHasUnsegmentable ? next : current;\n if (!hasBoundarySegmentableToken(segmentablePart)) {\n continue;\n }\n addRequiredTokens(current);\n addRequiredTokens(next);\n }\n\n for (let i = 1; i < parts.length - 1; i += 1) {\n const prev = parts[i - 1];\n const current = parts[i];\n const next = parts[i + 1];\n if (!hasUnsegmentableRecallChar(prev) || hasUnsegmentableRecallChar(current) || !hasUnsegmentableRecallChar(next)) {\n continue;\n }\n if (!hasRequiredSegmentableToken(current)) {\n continue;\n }\n addRequiredTokens(prev);\n addRequiredTokens(current);\n addRequiredTokens(next);\n }\n\n return [...required];\n}\n\nfunction requiredSegmentableUnicodeTokens(queryTokens: Set<string>): string[] {\n const segmentableTokens = [...queryTokens].filter((token) => !hasUnsegmentableRecallChar(token));\n const hasSegmentableUnicodeToken = segmentableTokens.some((token) =>\n [...token].some((ch) => (ch.codePointAt(0) ?? 0) > 0x7f)\n );\n if (!hasSegmentableUnicodeToken) {\n return [];\n }\n return segmentableTokens.filter((token) => {\n if (PROMPT_RECALL_WORDS.has(token)) {\n return false;\n }\n const hasNonAsciiCodepoint = [...token].some((ch) => (ch.codePointAt(0) ?? 0) > 0x7f);\n if (hasNonAsciiCodepoint) {\n return true;\n }\n if (!/^[a-z0-9]+$/u.test(token)) {\n return false;\n }\n return token.length >= 3 || /\\p{N}/u.test(token);\n });\n}\n\nfunction countTokenOverlap(queryTokens: Set<string>, valueTokens: Set<string>): number {\n let matches = 0;\n for (const token of queryTokens) {\n if (valueTokens.has(token)) matches += 1;\n }\n return matches;\n}\n\n/**\n * Determine whether a query can be served by the direct-answer tier.\n *\n * Decision ladder, in order:\n *\n * 1. config.enabled === false → \"disabled\"\n * 2. empty query tokens → \"empty-query\"\n * 3. empty candidates → \"no-candidates\"\n * 4. hard filters drop all candidates → \"no-eligible-candidates\"\n * 5. token-overlap floor drops all → \"below-token-overlap-floor\"\n * 6. top two candidates within ambiguityMargin → \"ambiguous\"\n * 7. otherwise → \"eligible\"\n */\nexport function isDirectAnswerEligible(input: DirectAnswerInput): DirectAnswerResult {\n const { query, candidates, config, queryEntityRefs } = input;\n\n if (!config.enabled) {\n return {\n eligible: false,\n reason: \"disabled\",\n narrative: \"direct-answer tier is disabled\",\n filteredBy: [],\n };\n }\n\n const queryTokens = new Set(normalizeRecallTokens(query));\n if (queryTokens.size === 0) {\n return {\n eligible: false,\n reason: \"empty-query\",\n narrative: \"query has no searchable tokens after normalization\",\n filteredBy: [],\n };\n }\n\n if (candidates.length === 0) {\n return {\n eligible: false,\n reason: \"no-candidates\",\n narrative: \"no candidates supplied\",\n filteredBy: [],\n };\n }\n\n const filteredBy: string[] = [];\n let working: DirectAnswerCandidate[] = candidates;\n\n working = applyFilter(working, filteredBy, FILTER_LABELS.nonActiveStatus, (c) => {\n const status: MemoryStatus = c.memory.frontmatter.status ?? \"active\";\n return status === \"active\";\n });\n\n working = applyFilter(working, filteredBy, FILTER_LABELS.notTrustedZone, (c) => c.trustZone === \"trusted\");\n\n working = applyFilter(\n working,\n filteredBy,\n FILTER_LABELS.ineligibleTaxonomyBucket,\n (c) => c.taxonomyBucket !== null && config.eligibleTaxonomyBuckets.includes(c.taxonomyBucket)\n );\n\n working = applyFilter(working, filteredBy, FILTER_LABELS.belowImportanceFloor, (c) => {\n if (c.memory.frontmatter.verificationState === \"user_confirmed\") return true;\n return c.importanceScore >= config.importanceFloor;\n });\n\n if (queryEntityRefs && queryEntityRefs.length > 0) {\n const normRefs = new Set(queryEntityRefs.map((r) => r.toLowerCase()));\n working = applyFilter(working, filteredBy, FILTER_LABELS.entityRefMismatch, (c) => {\n const ref = c.memory.frontmatter.entityRef;\n if (!ref) return true;\n return normRefs.has(ref.toLowerCase());\n });\n }\n\n if (working.length === 0) {\n return {\n eligible: false,\n reason: \"no-eligible-candidates\",\n narrative: \"no candidates survived eligibility filters\",\n filteredBy,\n };\n }\n\n const scored: ScoredCandidate[] = working.map((candidate) => {\n const searchable = `${candidate.memory.frontmatter.tags?.join(\" \") ?? \"\"} ${candidate.memory.content}`.trim();\n const searchableTokens = normalizeRecallTokenSet(searchable);\n const requiredSearchableTokens = normalizeRecallTokenSet(searchable, [], { minTokenLength: 1 });\n const requiredPhrases = requiredCjkPhraseTokens(query);\n const requiredMixedTokens = requiredMixedScriptTokens(query);\n const requiredUnicodeTokens = requiredSegmentableUnicodeTokens(queryTokens);\n const hasRequiredPhrase =\n requiredPhrases.length === 0 || requiredPhrases.every((token) => searchableTokens.has(token));\n const hasRequiredMixedTokens =\n requiredMixedTokens.length === 0 || requiredMixedTokens.every((token) => requiredSearchableTokens.has(token));\n const hasRequiredUnicodeTokens =\n requiredUnicodeTokens.length === 0 || requiredUnicodeTokens.every((token) => searchableTokens.has(token));\n const requiredTokenMismatch = !hasRequiredPhrase || !hasRequiredMixedTokens || !hasRequiredUnicodeTokens;\n const matches = requiredTokenMismatch ? 0 : countTokenOverlap(queryTokens, searchableTokens);\n return { candidate, tokenOverlap: matches / queryTokens.size, requiredTokenMismatch };\n });\n\n const overlapSurvivors = scored.filter((s) => !s.requiredTokenMismatch && s.tokenOverlap >= config.tokenOverlapFloor);\n if (overlapSurvivors.length < scored.length) {\n filteredBy.push(FILTER_LABELS.belowTokenOverlapFloor);\n }\n\n if (overlapSurvivors.length === 0) {\n return {\n eligible: false,\n reason: \"below-token-overlap-floor\",\n narrative: `no candidate met token-overlap floor ${config.tokenOverlapFloor}`,\n filteredBy,\n };\n }\n\n overlapSurvivors.sort(compareScored);\n\n if (overlapSurvivors.length >= 2) {\n const topScore = scoreFor(overlapSurvivors[0]);\n const secondScore = scoreFor(overlapSurvivors[1]);\n if (topScore - secondScore < config.ambiguityMargin) {\n return {\n eligible: false,\n reason: \"ambiguous\",\n narrative: `top two candidates within ambiguityMargin ${config.ambiguityMargin}`,\n filteredBy,\n };\n }\n }\n\n const winner = overlapSurvivors[0];\n const bucket = winner.candidate.taxonomyBucket ?? \"unknown\";\n return {\n eligible: true,\n reason: \"eligible\",\n winner: winner.candidate,\n tokenOverlap: winner.tokenOverlap,\n narrative: `trusted ${bucket}, unambiguous, token-overlap ${winner.tokenOverlap.toFixed(2)}`,\n filteredBy,\n };\n}\n\nfunction applyFilter(\n working: DirectAnswerCandidate[],\n filteredBy: string[],\n label: string,\n keep: (c: DirectAnswerCandidate) => boolean\n): DirectAnswerCandidate[] {\n const before = working.length;\n const next = working.filter(keep);\n if (next.length < before) filteredBy.push(label);\n return next;\n}\n\nfunction scoreFor(s: ScoredCandidate): number {\n return s.candidate.matchScore ?? s.tokenOverlap;\n}\n\nfunction compareScored(a: ScoredCandidate, b: ScoredCandidate): number {\n const diff = scoreFor(b) - scoreFor(a);\n if (diff !== 0) return diff;\n // Stable secondary key on path so the comparator returns 0 only for equal\n // entries (CLAUDE.md rule 19).\n return a.candidate.memory.path.localeCompare(b.candidate.memory.path);\n}\n"],"mappings":";;;;;;AA+FO,IAAM,gBAAgB;AAAA,EAC3B,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,0BAA0B;AAAA,EAC1B,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,wBAAwB;AAC1B;AAEA,IAAM,sBAAsB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAQD,SAAS,2BAA2B,OAAwB;AAC1D,MAAI,MAAM,SAAS,QAAG,KAAK,MAAM,SAAS,QAAG,EAAG,QAAO;AACvD,SAAO,0DAA0D,KAAK,KAAK;AAC7E;AAEA,SAAS,wBAAwB,OAAyB;AACxD,QAAM,UAAU,oBAAI,IAAY;AAChC,MAAI,UAAU;AAEd,QAAM,YAAY,CAAC,WAAmB;AACpC,QAAI,CAAC,GAAG,MAAM,EAAE,UAAU,GAAG;AAC3B,cAAQ,IAAI,MAAM;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AACzB,QAAI,WAAW;AACf,eAAW,OAAO,QAAQ,MAAM,KAAK,GAAG;AACtC,UAAI,CAAC,IAAK;AACV,UAAI,CAAC,GAAG,GAAG,EAAE,UAAU,GAAG;AACxB,kBAAU,QAAQ;AAClB,mBAAW;AACX,kBAAU,GAAG;AACb;AAAA,MACF;AACA,kBAAY;AAAA,IACd;AACA,cAAU,QAAQ;AAClB,cAAU;AAAA,EACZ;AAEA,aAAW,MAAM,MAAM,YAAY,EAAE,UAAU,KAAK,GAAG;AACrD,QAAI,2BAA2B,EAAE,GAAG;AAClC,iBAAW;AACX;AAAA,IACF;AACA,QAAI,SAAS,KAAK,EAAE,KAAK,QAAQ,SAAS,KAAK,CAAC,OAAO,KAAK,OAAO,GAAG;AACpE,iBAAW;AACX;AAAA,IACF;AACA,QAAI,MAAM,KAAK,EAAE,GAAG;AAClB,iBAAW;AACX;AAAA,IACF;AACA,iBAAa;AAAA,EACf;AACA,eAAa;AAEb,SAAO,CAAC,GAAG,OAAO;AACpB;AAEA,SAAS,0BAA0B,OAAyB;AAC1D,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU;AAEd,QAAM,eAAe,MAAM;AACzB,QAAI,SAAS;AACX,YAAM,KAAK,OAAO;AAAA,IACpB;AACA,cAAU;AAAA,EACZ;AAEA,QAAM,0BAA0B,CAAC,UAAkB;AACjD,UAAM,SAAS,oBAAI,IAAY;AAC/B,QAAIA,WAAU;AACd,UAAM,0BAA0B,MAAM;AACpC,iBAAW,SAAS,wBAAwBA,UAAS,CAAC,GAAG,EAAE,gBAAgB,EAAE,CAAC,GAAG;AAC/E,eAAO,IAAI,KAAK;AAAA,MAClB;AACA,MAAAA,WAAU;AAAA,IACZ;AAEA,eAAW,MAAM,OAAO;AACtB,UAAI,gBAAgB,KAAK,EAAE,KAAK,CAAC,2BAA2B,EAAE,GAAG;AAC/D,QAAAA,YAAW;AACX;AAAA,MACF;AACA,UAAI,SAAS,KAAK,EAAE,KAAKA,SAAQ,SAAS,GAAG;AAC3C,QAAAA,YAAW;AACX;AAAA,MACF;AACA,8BAAwB;AAAA,IAC1B;AACA,4BAAwB;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,8BAA8B,CAAC,UAAkB;AACrD,WAAO,wBAAwB,KAAK,EAAE,OAAO;AAAA,EAC/C;AAEA,QAAM,8BAA8B,CAAC,UAAkB;AACrD,eAAW,SAAS,wBAAwB,KAAK,GAAG;AAClD,UAAI,oBAAoB,IAAI,KAAK,GAAG;AAClC;AAAA,MACF;AACA,YAAM,uBAAuB,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,QAAQ,GAAG,YAAY,CAAC,KAAK,KAAK,GAAI;AACpF,UAAI,MAAM,UAAU,KAAK,SAAS,KAAK,KAAK,KAAK,sBAAsB;AACrE,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,oBAAoB,CAAC,UAAkB;AAC3C,eAAW,SAAS,wBAAwB,OAAO,CAAC,GAAG,EAAE,gBAAgB,EAAE,CAAC,GAAG;AAC7E,eAAS,IAAI,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,aAAW,MAAM,MAAM,YAAY,EAAE,UAAU,KAAK,GAAG;AACrD,QAAI,qBAAqB,KAAK,EAAE,KAAK,2BAA2B,EAAE,GAAG;AACnE,iBAAW;AACX;AAAA,IACF;AACA,iBAAa;AAAA,EACf;AACA,eAAa;AAEb,aAAW,QAAQ,OAAO;AACxB,QAAI,2BAA2B,IAAI,KAAK,4BAA4B,IAAI,GAAG;AACzE,wBAAkB,IAAI;AAAA,IACxB;AAAA,EACF;AAEA,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG;AAC5C,UAAM,UAAU,MAAM,CAAC;AACvB,UAAM,OAAO,MAAM,IAAI,CAAC;AACxB,UAAM,0BAA0B,2BAA2B,OAAO;AAClE,UAAM,uBAAuB,2BAA2B,IAAI;AAC5D,QAAI,4BAA4B,sBAAsB;AACpD;AAAA,IACF;AACA,UAAM,kBAAkB,0BAA0B,OAAO;AACzD,QAAI,CAAC,4BAA4B,eAAe,GAAG;AACjD;AAAA,IACF;AACA,sBAAkB,OAAO;AACzB,sBAAkB,IAAI;AAAA,EACxB;AAEA,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG;AAC5C,UAAM,OAAO,MAAM,IAAI,CAAC;AACxB,UAAM,UAAU,MAAM,CAAC;AACvB,UAAM,OAAO,MAAM,IAAI,CAAC;AACxB,QAAI,CAAC,2BAA2B,IAAI,KAAK,2BAA2B,OAAO,KAAK,CAAC,2BAA2B,IAAI,GAAG;AACjH;AAAA,IACF;AACA,QAAI,CAAC,4BAA4B,OAAO,GAAG;AACzC;AAAA,IACF;AACA,sBAAkB,IAAI;AACtB,sBAAkB,OAAO;AACzB,sBAAkB,IAAI;AAAA,EACxB;AAEA,SAAO,CAAC,GAAG,QAAQ;AACrB;AAEA,SAAS,iCAAiC,aAAoC;AAC5E,QAAM,oBAAoB,CAAC,GAAG,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC,2BAA2B,KAAK,CAAC;AAC/F,QAAM,6BAA6B,kBAAkB;AAAA,IAAK,CAAC,UACzD,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,QAAQ,GAAG,YAAY,CAAC,KAAK,KAAK,GAAI;AAAA,EACzD;AACA,MAAI,CAAC,4BAA4B;AAC/B,WAAO,CAAC;AAAA,EACV;AACA,SAAO,kBAAkB,OAAO,CAAC,UAAU;AACzC,QAAI,oBAAoB,IAAI,KAAK,GAAG;AAClC,aAAO;AAAA,IACT;AACA,UAAM,uBAAuB,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,QAAQ,GAAG,YAAY,CAAC,KAAK,KAAK,GAAI;AACpF,QAAI,sBAAsB;AACxB,aAAO;AAAA,IACT;AACA,QAAI,CAAC,eAAe,KAAK,KAAK,GAAG;AAC/B,aAAO;AAAA,IACT;AACA,WAAO,MAAM,UAAU,KAAK,SAAS,KAAK,KAAK;AAAA,EACjD,CAAC;AACH;AAEA,SAAS,kBAAkB,aAA0B,aAAkC;AACrF,MAAI,UAAU;AACd,aAAW,SAAS,aAAa;AAC/B,QAAI,YAAY,IAAI,KAAK,EAAG,YAAW;AAAA,EACzC;AACA,SAAO;AACT;AAeO,SAAS,uBAAuB,OAA8C;AACnF,QAAM,EAAE,OAAO,YAAY,QAAQ,gBAAgB,IAAI;AAEvD,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY,CAAC;AAAA,IACf;AAAA,EACF;AAEA,QAAM,cAAc,IAAI,IAAI,sBAAsB,KAAK,CAAC;AACxD,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY,CAAC;AAAA,IACf;AAAA,EACF;AAEA,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY,CAAC;AAAA,IACf;AAAA,EACF;AAEA,QAAM,aAAuB,CAAC;AAC9B,MAAI,UAAmC;AAEvC,YAAU,YAAY,SAAS,YAAY,cAAc,iBAAiB,CAAC,MAAM;AAC/E,UAAM,SAAuB,EAAE,OAAO,YAAY,UAAU;AAC5D,WAAO,WAAW;AAAA,EACpB,CAAC;AAED,YAAU,YAAY,SAAS,YAAY,cAAc,gBAAgB,CAAC,MAAM,EAAE,cAAc,SAAS;AAEzG,YAAU;AAAA,IACR;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,CAAC,MAAM,EAAE,mBAAmB,QAAQ,OAAO,wBAAwB,SAAS,EAAE,cAAc;AAAA,EAC9F;AAEA,YAAU,YAAY,SAAS,YAAY,cAAc,sBAAsB,CAAC,MAAM;AACpF,QAAI,EAAE,OAAO,YAAY,sBAAsB,iBAAkB,QAAO;AACxE,WAAO,EAAE,mBAAmB,OAAO;AAAA,EACrC,CAAC;AAED,MAAI,mBAAmB,gBAAgB,SAAS,GAAG;AACjD,UAAM,WAAW,IAAI,IAAI,gBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACpE,cAAU,YAAY,SAAS,YAAY,cAAc,mBAAmB,CAAC,MAAM;AACjF,YAAM,MAAM,EAAE,OAAO,YAAY;AACjC,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO,SAAS,IAAI,IAAI,YAAY,CAAC;AAAA,IACvC,CAAC;AAAA,EACH;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAA4B,QAAQ,IAAI,CAAC,cAAc;AAC3D,UAAM,aAAa,GAAG,UAAU,OAAO,YAAY,MAAM,KAAK,GAAG,KAAK,EAAE,IAAI,UAAU,OAAO,OAAO,GAAG,KAAK;AAC5G,UAAM,mBAAmB,wBAAwB,UAAU;AAC3D,UAAM,2BAA2B,wBAAwB,YAAY,CAAC,GAAG,EAAE,gBAAgB,EAAE,CAAC;AAC9F,UAAM,kBAAkB,wBAAwB,KAAK;AACrD,UAAM,sBAAsB,0BAA0B,KAAK;AAC3D,UAAM,wBAAwB,iCAAiC,WAAW;AAC1E,UAAM,oBACJ,gBAAgB,WAAW,KAAK,gBAAgB,MAAM,CAAC,UAAU,iBAAiB,IAAI,KAAK,CAAC;AAC9F,UAAM,yBACJ,oBAAoB,WAAW,KAAK,oBAAoB,MAAM,CAAC,UAAU,yBAAyB,IAAI,KAAK,CAAC;AAC9G,UAAM,2BACJ,sBAAsB,WAAW,KAAK,sBAAsB,MAAM,CAAC,UAAU,iBAAiB,IAAI,KAAK,CAAC;AAC1G,UAAM,wBAAwB,CAAC,qBAAqB,CAAC,0BAA0B,CAAC;AAChF,UAAM,UAAU,wBAAwB,IAAI,kBAAkB,aAAa,gBAAgB;AAC3F,WAAO,EAAE,WAAW,cAAc,UAAU,YAAY,MAAM,sBAAsB;AAAA,EACtF,CAAC;AAED,QAAM,mBAAmB,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,yBAAyB,EAAE,gBAAgB,OAAO,iBAAiB;AACpH,MAAI,iBAAiB,SAAS,OAAO,QAAQ;AAC3C,eAAW,KAAK,cAAc,sBAAsB;AAAA,EACtD;AAEA,MAAI,iBAAiB,WAAW,GAAG;AACjC,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW,wCAAwC,OAAO,iBAAiB;AAAA,MAC3E;AAAA,IACF;AAAA,EACF;AAEA,mBAAiB,KAAK,aAAa;AAEnC,MAAI,iBAAiB,UAAU,GAAG;AAChC,UAAM,WAAW,SAAS,iBAAiB,CAAC,CAAC;AAC7C,UAAM,cAAc,SAAS,iBAAiB,CAAC,CAAC;AAChD,QAAI,WAAW,cAAc,OAAO,iBAAiB;AACnD,aAAO;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,WAAW,6CAA6C,OAAO,eAAe;AAAA,QAC9E;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,iBAAiB,CAAC;AACjC,QAAM,SAAS,OAAO,UAAU,kBAAkB;AAClD,SAAO;AAAA,IACL,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ,OAAO;AAAA,IACf,cAAc,OAAO;AAAA,IACrB,WAAW,WAAW,MAAM,gCAAgC,OAAO,aAAa,QAAQ,CAAC,CAAC;AAAA,IAC1F;AAAA,EACF;AACF;AAEA,SAAS,YACP,SACA,YACA,OACA,MACyB;AACzB,QAAM,SAAS,QAAQ;AACvB,QAAM,OAAO,QAAQ,OAAO,IAAI;AAChC,MAAI,KAAK,SAAS,OAAQ,YAAW,KAAK,KAAK;AAC/C,SAAO;AACT;AAEA,SAAS,SAAS,GAA4B;AAC5C,SAAO,EAAE,UAAU,cAAc,EAAE;AACrC;AAEA,SAAS,cAAc,GAAoB,GAA4B;AACrE,QAAM,OAAO,SAAS,CAAC,IAAI,SAAS,CAAC;AACrC,MAAI,SAAS,EAAG,QAAO;AAGvB,SAAO,EAAE,UAAU,OAAO,KAAK,cAAc,EAAE,UAAU,OAAO,IAAI;AACtE;","names":["segment"]}
|
|
@@ -23,9 +23,7 @@ function tagIndexPath(memoryDir) {
|
|
|
23
23
|
}
|
|
24
24
|
function ensureStateDir(memoryDir) {
|
|
25
25
|
const dir = stateDir(memoryDir);
|
|
26
|
-
|
|
27
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
-
}
|
|
26
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
27
|
}
|
|
30
28
|
function readJsonSafe(filePath, fallback) {
|
|
31
29
|
try {
|
|
@@ -111,6 +109,11 @@ function lockOwnerIsRunning(owner) {
|
|
|
111
109
|
if (runningStartedAtMs === null) return true;
|
|
112
110
|
return runningStartedAtMs <= owner.processStartedAtMs + INDEX_PROCESS_START_TOLERANCE_MS;
|
|
113
111
|
}
|
|
112
|
+
function lockIsFresh(lockInfo, owner) {
|
|
113
|
+
const ownerCreatedAtMs = typeof owner?.createdAt === "string" && owner.createdAt.length > 0 ? Date.parse(owner.createdAt) : Number.NaN;
|
|
114
|
+
const referenceMs = Number.isFinite(ownerCreatedAtMs) ? ownerCreatedAtMs : lockInfo.mtimeMs;
|
|
115
|
+
return Date.now() - referenceMs < INDEX_LOCK_STALE_MS;
|
|
116
|
+
}
|
|
114
117
|
function removeAbandonedIndexLock(lockDir) {
|
|
115
118
|
try {
|
|
116
119
|
const info = fs.lstatSync(lockDir);
|
|
@@ -120,11 +123,14 @@ function removeAbandonedIndexLock(lockDir) {
|
|
|
120
123
|
return "removed";
|
|
121
124
|
}
|
|
122
125
|
const owner = readIndexLockOwner(lockDir);
|
|
123
|
-
if (owner !== null
|
|
124
|
-
|
|
126
|
+
if (owner !== null) {
|
|
127
|
+
if (lockOwnerIsRunning(owner)) return "wait";
|
|
128
|
+
}
|
|
129
|
+
if (owner === null && lockIsFresh(info, null)) return "wait";
|
|
125
130
|
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
126
131
|
return "removed";
|
|
127
|
-
} catch {
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error?.code === "ENOENT") return "removed";
|
|
128
134
|
return "blocked";
|
|
129
135
|
}
|
|
130
136
|
}
|
|
@@ -138,6 +144,15 @@ function withIndexFileLock(filePath, update) {
|
|
|
138
144
|
acquired = true;
|
|
139
145
|
} catch (error) {
|
|
140
146
|
const code = error?.code;
|
|
147
|
+
if (code === "ENOENT") {
|
|
148
|
+
try {
|
|
149
|
+
fs.mkdirSync(path.dirname(lockDir), { recursive: true });
|
|
150
|
+
} catch {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
sleepSync(INDEX_LOCK_POLL_MS);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
141
156
|
if (code !== "EEXIST") return;
|
|
142
157
|
const cleanupResult = removeAbandonedIndexLock(lockDir);
|
|
143
158
|
if (cleanupResult === "blocked") return;
|
|
@@ -773,4 +788,4 @@ export {
|
|
|
773
788
|
recencyWindowFromPrompt,
|
|
774
789
|
recencyWindowBoundsFromPrompt
|
|
775
790
|
};
|
|
776
|
-
//# sourceMappingURL=chunk-
|
|
791
|
+
//# sourceMappingURL=chunk-75O6YQ63.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/temporal-index.ts"],"sourcesContent":["/**\n * Temporal and Tag Indexes (v8.1 — SwiftMem-inspired)\n *\n * Maintains two fast on-disk lookup structures in `state/`:\n * index_time.json — maps YYYY-MM-DD date buckets to memory file paths\n * index_tags.json — maps tag strings to memory file paths\n *\n * Used as an optional prefilter in the retrieval pipeline:\n * given a time range or a set of tags, narrow the candidate set\n * before the QMD hybrid search so we can pass a smaller pool to scoring.\n *\n * Design constraints:\n * - Must be fail-open (any error returns empty / unfiltered)\n * - Reads/writes are batched per extraction run\n * - Both indexes are plain JSON; no external dependencies\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport interface TemporalIndex {\n /** version bumped when schema changes */\n version: number;\n /** Last full rebuild timestamp (ISO string) */\n lastRebuildAt?: string;\n /** Map from YYYY-MM-DD → array of memory paths */\n dates: Record<string, string[]>;\n}\n\nexport interface TagIndex {\n version: number;\n lastRebuildAt?: string;\n /** Map from canonical tag string → node metadata */\n tags: Record<string, TagNode | string[]>;\n /** Map from alias string → canonical tags */\n aliases?: Record<string, string[]>;\n}\n\nexport interface TagNode {\n paths: string[];\n aliases?: string[];\n parents?: string[];\n}\n\nconst INDEX_VERSION = 1;\nconst TEMPORAL_INDEX_FILE = \"index_time.json\";\nconst TAG_INDEX_FILE = \"index_tags.json\";\nconst TAG_INDEX_VERSION = 2;\nconst INDEX_LOCK_STALE_MS = 60_000;\nconst INDEX_LOCK_POLL_MS = 10;\nconst INDEX_PROCESS_START_TOLERANCE_MS = 2_000;\nconst INDEX_LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(4));\nconst INDEX_PROCESS_STARTED_AT_MS = Date.now() - process.uptime() * 1000;\n\ninterface IndexLockOwner {\n pid: number;\n createdAt?: string;\n processStartedAtMs?: number;\n}\n\ntype IndexLockCleanupResult = \"removed\" | \"wait\" | \"blocked\";\n\nfunction stateDir(memoryDir: string): string {\n return path.join(memoryDir, \"state\");\n}\n\nfunction temporalIndexPath(memoryDir: string): string {\n return path.join(stateDir(memoryDir), TEMPORAL_INDEX_FILE);\n}\n\nfunction tagIndexPath(memoryDir: string): string {\n return path.join(stateDir(memoryDir), TAG_INDEX_FILE);\n}\n\nfunction ensureStateDir(memoryDir: string): void {\n const dir = stateDir(memoryDir);\n fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction readJsonSafe<T>(filePath: string, fallback: T): T {\n try {\n const raw = fs.readFileSync(filePath, \"utf8\");\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction writeJsonSafe(filePath: string, data: unknown): void {\n try {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), \"utf8\");\n } catch {\n // Fail silently — indexes are advisory only\n }\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(INDEX_LOCK_SLEEP, 0, 0, ms);\n}\n\nfunction uniqueTempPath(filePath: string): string {\n const dir = path.dirname(filePath);\n const base = path.basename(filePath);\n const nonce = crypto.randomBytes(6).toString(\"hex\");\n return path.join(dir, `.${base}.${process.pid}.${Date.now()}.${nonce}.tmp`);\n}\n\nfunction lockOwnerPath(lockDir: string): string {\n return path.join(lockDir, \"owner.json\");\n}\n\nfunction writeIndexLockOwner(lockDir: string): void {\n try {\n fs.writeFileSync(\n lockOwnerPath(lockDir),\n JSON.stringify({\n pid: process.pid,\n createdAt: new Date().toISOString(),\n processStartedAtMs: INDEX_PROCESS_STARTED_AT_MS,\n }),\n {\n encoding: \"utf8\",\n flag: \"wx\",\n }\n );\n } catch {\n // Fail silently — the directory lock is still the serialization primitive.\n }\n}\n\nfunction readIndexLockOwner(lockDir: string): IndexLockOwner | null {\n try {\n const parsed = JSON.parse(fs.readFileSync(lockOwnerPath(lockDir), \"utf8\")) as { pid?: unknown };\n if (!(typeof parsed.pid === \"number\" && Number.isInteger(parsed.pid) && parsed.pid > 0)) return null;\n const owner: IndexLockOwner = { pid: parsed.pid };\n if (\n \"createdAt\" in parsed &&\n typeof (parsed as { createdAt?: unknown }).createdAt === \"string\" &&\n (parsed as { createdAt: string }).createdAt.length > 0\n ) {\n owner.createdAt = (parsed as { createdAt: string }).createdAt;\n }\n const processStartedAtMs = (parsed as { processStartedAtMs?: unknown }).processStartedAtMs;\n if (typeof processStartedAtMs === \"number\" && Number.isFinite(processStartedAtMs) && processStartedAtMs > 0) {\n owner.processStartedAtMs = processStartedAtMs;\n }\n return owner;\n } catch {\n return null;\n }\n}\n\nfunction processIsAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code;\n return code === \"EPERM\";\n }\n}\n\nfunction readProcessStartedAtMs(pid: number): number | null {\n try {\n const output = execFileSync(\"ps\", [\"-p\", String(pid), \"-o\", \"lstart=\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 1_000,\n }).trim();\n if (!output) return null;\n const startedAtMs = Date.parse(output);\n return Number.isFinite(startedAtMs) ? startedAtMs : null;\n } catch {\n return null;\n }\n}\n\nfunction lockOwnerIsRunning(owner: IndexLockOwner): boolean {\n if (!processIsAlive(owner.pid)) return false;\n if (owner.processStartedAtMs === undefined) return true;\n const runningStartedAtMs = readProcessStartedAtMs(owner.pid);\n if (runningStartedAtMs === null) return true;\n return runningStartedAtMs <= owner.processStartedAtMs + INDEX_PROCESS_START_TOLERANCE_MS;\n}\n\nfunction lockIsFresh(lockInfo: fs.Stats, owner: IndexLockOwner | null): boolean {\n const ownerCreatedAtMs =\n typeof owner?.createdAt === \"string\" && owner.createdAt.length > 0 ? Date.parse(owner.createdAt) : Number.NaN;\n const referenceMs = Number.isFinite(ownerCreatedAtMs) ? ownerCreatedAtMs : lockInfo.mtimeMs;\n return Date.now() - referenceMs < INDEX_LOCK_STALE_MS;\n}\n\nfunction removeAbandonedIndexLock(lockDir: string): IndexLockCleanupResult {\n try {\n const info = fs.lstatSync(lockDir);\n if (info.isSymbolicLink()) return \"blocked\";\n if (!info.isDirectory()) {\n fs.rmSync(lockDir, { force: true });\n return \"removed\";\n }\n const owner = readIndexLockOwner(lockDir);\n if (owner !== null) {\n if (lockOwnerIsRunning(owner)) return \"wait\";\n }\n if (owner === null && lockIsFresh(info, null)) return \"wait\";\n fs.rmSync(lockDir, { recursive: true, force: true });\n return \"removed\";\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") return \"removed\";\n // Fail silently — indexes are advisory only\n return \"blocked\";\n }\n}\n\nfunction withIndexFileLock(filePath: string, update: () => void): void {\n const lockDir = `${filePath}.lock.d`;\n let acquired = false;\n\n while (!acquired) {\n try {\n fs.mkdirSync(lockDir);\n writeIndexLockOwner(lockDir);\n acquired = true;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code;\n if (code === \"ENOENT\") {\n try {\n fs.mkdirSync(path.dirname(lockDir), { recursive: true });\n } catch {\n return;\n }\n sleepSync(INDEX_LOCK_POLL_MS);\n continue;\n }\n if (code !== \"EEXIST\") return;\n const cleanupResult = removeAbandonedIndexLock(lockDir);\n if (cleanupResult === \"blocked\") return;\n sleepSync(INDEX_LOCK_POLL_MS);\n }\n }\n\n try {\n update();\n } finally {\n try {\n fs.rmSync(lockDir, { recursive: true, force: true });\n } catch {\n // Fail silently — indexes are advisory only\n }\n }\n}\n\n/**\n * Atomic write: write to a unique `.tmp` sibling then rename so readers never\n * observe a partially-written file.\n */\nfunction writeJsonAtomic(filePath: string, data: unknown): void {\n const payload = JSON.stringify(data, null, 2);\n for (let attempt = 0; attempt < 3; attempt += 1) {\n const tmp = uniqueTempPath(filePath);\n try {\n fs.writeFileSync(tmp, payload, \"utf8\");\n fs.renameSync(tmp, filePath);\n return;\n } catch {\n try {\n fs.unlinkSync(tmp);\n } catch {\n // Fail silently — indexes are advisory only\n }\n sleepSync(INDEX_LOCK_POLL_MS);\n }\n }\n}\n\nfunction updateTemporalIndex(memoryDir: string, update: (index: TemporalIndex) => void): void {\n const indexPath = temporalIndexPath(memoryDir);\n withIndexFileLock(indexPath, () => {\n const index = readJsonSafe<TemporalIndex>(indexPath, { version: INDEX_VERSION, dates: {} });\n update(index);\n writeJsonAtomic(indexPath, index);\n });\n}\n\nfunction updateTagIndex(memoryDir: string, update: (index: TagIndex) => void): void {\n const indexPath = tagIndexPath(memoryDir);\n withIndexFileLock(indexPath, () => {\n const index = normalizeTagIndex(\n readJsonSafe<TagIndex>(indexPath, { version: TAG_INDEX_VERSION, tags: {}, aliases: {} })\n );\n update(index);\n writeJsonAtomic(indexPath, index);\n });\n}\n\nfunction isoDateFromTimestamp(isoString: string): string {\n if (typeof isoString !== \"string\" || isoString.length < 10) {\n // Malformed frontmatter — fall back to today so the memory is still indexed.\n // Log a warning to surface data-quality issues without aborting the write.\n console.warn(`[engram] temporal-index: malformed timestamp \"${isoString}\", falling back to today`);\n return new Date().toISOString().slice(0, 10);\n }\n return isoString.slice(0, 10); // YYYY-MM-DD\n}\n\nfunction addPathToSet(record: Record<string, string[]>, key: string, p: string): void {\n if (!record[key]) {\n record[key] = [];\n }\n if (!record[key].includes(p)) {\n record[key].push(p);\n }\n}\n\nfunction removePathFromSet(record: Record<string, string[]>, key: string, p: string): void {\n if (!record[key]) return;\n record[key] = record[key].filter((x) => x !== p);\n if (record[key].length === 0) {\n delete record[key];\n }\n}\n\nfunction normalizeTagSegment(segment: string): string {\n return segment\n .trim()\n .toLowerCase()\n .replace(/[_\\s]+/g, \"-\")\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction normalizeCanonicalTag(tag: string): string {\n return tag\n .trim()\n .toLowerCase()\n .replace(/\\s*[>:|.]+\\s*/g, \"/\")\n .replace(/\\s*\\/\\s*/g, \"/\")\n .split(\"/\")\n .map(normalizeTagSegment)\n .filter(Boolean)\n .join(\"/\");\n}\n\nfunction normalizeAliasKey(tag: string): string {\n return tag\n .trim()\n .toLowerCase()\n .replace(/[\\/_.:-]+/g, \" \")\n .replace(/[-]+/g, \" \")\n .replace(/[^a-z0-9\\s]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction singularizeAlias(alias: string): string | null {\n if (!alias.endsWith(\"s\") || alias.length <= 3) return null;\n return alias.slice(0, -1);\n}\n\nfunction deriveParentTags(canonical: string): string[] {\n const parts = canonical.split(\"/\").filter(Boolean);\n const parents: string[] = [];\n for (let i = parts.length - 1; i > 0; i -= 1) {\n parents.push(parts.slice(0, i).join(\"/\"));\n }\n return parents;\n}\n\nfunction deriveTagAliases(rawTag: string, canonical: string): string[] {\n const aliases = new Set<string>();\n const canonicalAlias = normalizeAliasKey(canonical);\n const rawAlias = normalizeAliasKey(rawTag);\n const leaf = canonical.split(\"/\").at(-1) ?? canonical;\n const leafAlias = normalizeAliasKey(leaf);\n\n for (const candidate of [canonicalAlias, rawAlias, leafAlias]) {\n if (!candidate) continue;\n aliases.add(candidate);\n const singular = singularizeAlias(candidate);\n if (singular) aliases.add(singular);\n }\n\n return Array.from(aliases);\n}\n\nfunction normalizeTagIndex(raw: TagIndex | null | undefined): TagIndex {\n const normalized: TagIndex = {\n version: TAG_INDEX_VERSION,\n tags: {},\n aliases: {},\n };\n\n if (!raw || typeof raw !== \"object\") {\n return normalized;\n }\n\n const sourceTags = raw.tags ?? {};\n for (const [rawCanonical, nodeOrPaths] of Object.entries(sourceTags)) {\n const canonical = normalizeCanonicalTag(rawCanonical);\n if (!canonical) continue;\n const node: TagNode = Array.isArray(nodeOrPaths)\n ? { paths: [...new Set(nodeOrPaths)] }\n : {\n paths: Array.isArray(nodeOrPaths?.paths) ? [...new Set(nodeOrPaths.paths)] : [],\n aliases: Array.isArray(nodeOrPaths?.aliases) ? [...new Set(nodeOrPaths.aliases)] : [],\n parents: Array.isArray(nodeOrPaths?.parents) ? [...new Set(nodeOrPaths.parents)] : [],\n };\n const existingNode = normalized.tags[canonical];\n if (existingNode && !Array.isArray(existingNode)) {\n existingNode.paths = [...new Set([...existingNode.paths, ...node.paths])];\n existingNode.aliases = [...new Set([...(existingNode.aliases ?? []), ...(node.aliases ?? [])])];\n existingNode.parents = [...new Set([...(existingNode.parents ?? []), ...(node.parents ?? [])])];\n } else if (Array.isArray(existingNode)) {\n normalized.tags[canonical] = {\n paths: [...new Set([...existingNode, ...node.paths])],\n aliases: [...new Set(node.aliases ?? [])],\n parents: [...new Set(node.parents ?? deriveParentTags(canonical))],\n };\n } else {\n normalized.tags[canonical] = node;\n }\n for (const alias of deriveTagAliases(canonical, canonical)) {\n const list = normalized.aliases![alias] ?? [];\n if (!list.includes(canonical)) list.push(canonical);\n normalized.aliases![alias] = list;\n }\n for (const alias of node.aliases ?? []) {\n const aliasKey = normalizeAliasKey(alias);\n if (!aliasKey) continue;\n const list = normalized.aliases![aliasKey] ?? [];\n if (!list.includes(canonical)) list.push(canonical);\n normalized.aliases![aliasKey] = list;\n }\n const mergedNode = normalized.tags[canonical];\n if (mergedNode && !Array.isArray(mergedNode)) {\n mergedNode.parents = [...new Set(mergedNode.parents ?? deriveParentTags(canonical))];\n }\n }\n\n for (const [alias, canonicals] of Object.entries(raw.aliases ?? {})) {\n const aliasKey = normalizeAliasKey(alias);\n if (!aliasKey) continue;\n const list = normalized.aliases![aliasKey] ?? [];\n for (const canonical of canonicals ?? []) {\n const normalizedCanonical = normalizeCanonicalTag(canonical);\n if (normalizedCanonical && !list.includes(normalizedCanonical)) {\n list.push(normalizedCanonical);\n }\n }\n normalized.aliases![aliasKey] = list;\n }\n\n return normalized;\n}\n\nfunction ensureTagNode(index: TagIndex, canonical: string): TagNode {\n const existing = index.tags[canonical];\n if (existing && !Array.isArray(existing)) {\n return existing;\n }\n const created: TagNode = {\n paths: Array.isArray(existing) ? [...new Set(existing)] : [],\n aliases: [],\n parents: deriveParentTags(canonical),\n };\n index.tags[canonical] = created;\n return created;\n}\n\nfunction addTagGraphEntry(index: TagIndex, rawTag: string, memoryPath: string): void {\n const canonical = normalizeCanonicalTag(rawTag);\n if (!canonical) return;\n const node = ensureTagNode(index, canonical);\n if (!node.paths.includes(memoryPath)) {\n node.paths.push(memoryPath);\n }\n\n for (const alias of deriveTagAliases(rawTag, canonical)) {\n const aliasKey = normalizeAliasKey(alias);\n if (!aliasKey) continue;\n if (!node.aliases?.includes(aliasKey)) {\n node.aliases = [...new Set([...(node.aliases ?? []), aliasKey])];\n }\n const list = index.aliases?.[aliasKey] ?? [];\n if (!list.includes(canonical)) {\n index.aliases![aliasKey] = [...list, canonical];\n }\n }\n}\n\nfunction removeTagGraphEntry(index: TagIndex, rawTag: string, memoryPath: string): void {\n const canonical = normalizeCanonicalTag(rawTag);\n if (!canonical) return;\n const node = index.tags[canonical];\n if (!node || Array.isArray(node)) return;\n node.paths = node.paths.filter((value) => value !== memoryPath);\n if (node.paths.length === 0) {\n delete index.tags[canonical];\n }\n}\n\nfunction expandCanonicalTags(index: TagIndex, rawTags: string[]): string[] {\n const canonicals = new Set<string>();\n for (const rawTag of rawTags) {\n const canonical = normalizeCanonicalTag(rawTag);\n if (canonical && index.tags[canonical]) {\n canonicals.add(canonical);\n }\n const aliasKey = normalizeAliasKey(rawTag);\n for (const resolved of index.aliases?.[aliasKey] ?? []) {\n canonicals.add(resolved);\n }\n }\n\n const expanded = new Set<string>();\n for (const canonical of canonicals) {\n expanded.add(canonical);\n const node = index.tags[canonical];\n if (node && !Array.isArray(node)) {\n for (const parent of node.parents ?? []) {\n expanded.add(parent);\n }\n }\n }\n return Array.from(expanded);\n}\n\nfunction aliasPhrase(alias: string): string {\n return alias.replace(/\\//g, \" \").replace(/-/g, \" \").replace(/\\s+/g, \" \").trim();\n}\n\nfunction promptContainsAlias(prompt: string, alias: string): boolean {\n const phrase = aliasPhrase(alias);\n if (!phrase) return false;\n const normalizedPrompt = ` ${normalizeAliasKey(prompt)} `;\n return normalizedPrompt.includes(` ${phrase} `);\n}\n\n// ─── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Add (or update) a memory file in both indexes.\n *\n * @param memoryDir Root memory directory\n * @param memoryPath Absolute path to the memory file\n * @param createdAt ISO timestamp of the memory's creation date\n * @param tags Array of tag strings from the memory's frontmatter\n */\nexport function indexMemory(memoryDir: string, memoryPath: string, createdAt: string, tags: string[]): void {\n try {\n ensureStateDir(memoryDir);\n\n const dateKey = isoDateFromTimestamp(createdAt);\n updateTemporalIndex(memoryDir, (index) => {\n addPathToSet(index.dates, dateKey, memoryPath);\n });\n\n updateTagIndex(memoryDir, (index) => {\n for (const tag of tags) {\n if (tag && typeof tag === \"string\") {\n addTagGraphEntry(index, tag, memoryPath);\n }\n }\n });\n } catch {\n // Fail silently\n }\n}\n\n/**\n * Remove a memory file from both indexes (called on deletion/archival).\n */\nexport function deindexMemory(memoryDir: string, memoryPath: string, createdAt: string, tags: string[]): void {\n try {\n ensureStateDir(memoryDir);\n\n const dateKey = isoDateFromTimestamp(createdAt);\n updateTemporalIndex(memoryDir, (index) => {\n removePathFromSet(index.dates, dateKey, memoryPath);\n });\n\n updateTagIndex(memoryDir, (index) => {\n for (const tag of tags) {\n if (tag && typeof tag === \"string\") {\n removeTagGraphEntry(index, tag, memoryPath);\n }\n }\n });\n } catch {\n // Fail silently\n }\n}\n\n/**\n * Reset both index files to empty state.\n * Called before a full-corpus rebuild so stale paths in any surviving index\n * file do not persist after the rebuild completes.\n */\nexport function clearIndexes(memoryDir: string): void {\n try {\n ensureStateDir(memoryDir);\n updateTemporalIndex(memoryDir, (index) => {\n index.version = INDEX_VERSION;\n index.lastRebuildAt = undefined;\n index.dates = {};\n });\n updateTagIndex(memoryDir, (index) => {\n index.version = TAG_INDEX_VERSION;\n index.lastRebuildAt = undefined;\n index.tags = {};\n index.aliases = {};\n });\n } catch {\n // Fail silently — indexes are advisory only\n }\n}\n\n/**\n * Returns true when both index files exist on disk.\n * Used to detect first-time enablement so callers can trigger a full rebuild.\n */\nexport function indexesExist(memoryDir: string): boolean {\n try {\n return fs.existsSync(temporalIndexPath(memoryDir)) && fs.existsSync(tagIndexPath(memoryDir));\n } catch {\n return false;\n }\n}\n\n/**\n * Batch-add multiple memories to both indexes in a single read-modify-write cycle.\n * More efficient than calling indexMemory() per file when adding many at once.\n */\nexport function indexMemoriesBatch(\n memoryDir: string,\n entries: Array<{ path: string; createdAt: string; tags: string[] }>\n): void {\n if (entries.length === 0) return;\n try {\n ensureStateDir(memoryDir);\n\n updateTemporalIndex(memoryDir, (index) => {\n for (const entry of entries) {\n const dateKey = isoDateFromTimestamp(entry.createdAt);\n addPathToSet(index.dates, dateKey, entry.path);\n }\n });\n\n updateTagIndex(memoryDir, (index) => {\n for (const entry of entries) {\n for (const tag of entry.tags) {\n if (tag && typeof tag === \"string\") {\n addTagGraphEntry(index, tag, entry.path);\n }\n }\n }\n });\n } catch {\n // Fail silently\n }\n}\n\n/**\n * Return the set of memory paths whose index date falls in [fromDate, toDate).\n *\n * Boundary semantics (exclusive upper bound):\n * - `fromDate` is INCLUSIVE — entries on this date ARE returned.\n * - `toDate` is EXCLUSIVE — entries on this date are NOT returned.\n * Pass `recencyWindowBoundsFromPrompt(query).toDate` to get the correct\n * exclusive boundary; do NOT add +1 day yourself.\n * - Default `toDate` = tomorrow, so omitting it includes all of today.\n *\n * @param memoryDir - root memory directory (contains state/index_time.json)\n * @param fromDate - inclusive start date, YYYY-MM-DD\n * @param toDate - exclusive end date, YYYY-MM-DD (default: tomorrow)\n * @returns Set of matching file paths, or null if the index is unavailable.\n */\nexport async function queryByDateRangeAsync(\n memoryDir: string,\n fromDate: string,\n toDate?: string\n): Promise<Set<string> | null> {\n try {\n const tPath = temporalIndexPath(memoryDir);\n let raw: string;\n try {\n raw = await fs.promises.readFile(tPath, \"utf8\");\n } catch {\n return null; // File missing or unreadable\n }\n let tIndex: TemporalIndex;\n try {\n tIndex = JSON.parse(raw) as TemporalIndex;\n } catch {\n tIndex = { version: INDEX_VERSION, dates: {} };\n }\n // toDate is exclusive (first day NOT included). Default: tomorrow so \"all of today\" is included.\n const end = toDate ?? new Date(Date.now() + 86_400_000).toISOString().slice(0, 10);\n\n const results = new Set<string>();\n for (const [date, paths] of Object.entries(tIndex.dates)) {\n if (date >= fromDate && date < end) {\n for (const p of paths) {\n results.add(p);\n }\n }\n }\n return results;\n } catch {\n return null;\n }\n}\n\n/**\n * Async version of queryByTags — uses non-blocking fs.promises.readFile\n * to avoid blocking the Node.js event loop.\n */\nexport async function queryByTagsAsync(memoryDir: string, tags: string[]): Promise<Set<string> | null> {\n if (tags.length === 0) return null;\n try {\n const gPath = tagIndexPath(memoryDir);\n let raw: string;\n try {\n raw = await fs.promises.readFile(gPath, \"utf8\");\n } catch {\n return null; // File missing or unreadable\n }\n let gIndex: TagIndex;\n try {\n gIndex = normalizeTagIndex(JSON.parse(raw) as TagIndex);\n } catch {\n gIndex = { version: TAG_INDEX_VERSION, tags: {}, aliases: {} };\n }\n\n return queryByTagsFromIndex(gIndex, tags);\n } catch {\n return null;\n }\n}\n\nfunction queryByTagsFromIndex(index: TagIndex, tags: string[]): Set<string> | null {\n const expandedTags = expandCanonicalTags(index, tags);\n const results = new Set<string>();\n for (const canonical of expandedTags) {\n const nodeOrPaths = index.tags[canonical];\n const paths = Array.isArray(nodeOrPaths) ? nodeOrPaths : (nodeOrPaths?.paths ?? []);\n for (const pathValue of paths) {\n results.add(pathValue);\n }\n }\n return results.size > 0 ? results : null;\n}\n\n/**\n * Extract tags from a prompt for tag-based prefiltering.\n * Looks for hashtag-style tokens (#foo).\n * Returns lowercase, deduplicated list.\n */\nexport function extractTagsFromPrompt(prompt: string): string[] {\n const found = new Set<string>();\n\n // Match #tag style tokens\n const hashMatches = prompt.matchAll(/#([a-zA-Z][\\w-]{1,30})/g);\n for (const m of hashMatches) {\n const canonical = normalizeCanonicalTag(m[1]);\n if (canonical) found.add(canonical);\n }\n\n return Array.from(found);\n}\n\nexport async function resolvePromptTagPrefilterAsync(\n memoryDir: string,\n prompt: string\n): Promise<{\n matchedTags: string[];\n expandedTags: string[];\n paths: Set<string> | null;\n}> {\n const explicitTags = extractTagsFromPrompt(prompt);\n try {\n const raw = await fs.promises.readFile(tagIndexPath(memoryDir), \"utf8\");\n const tagIndex = normalizeTagIndex(JSON.parse(raw) as TagIndex);\n const matched = new Set<string>(explicitTags);\n\n for (const canonical of Object.keys(tagIndex.tags)) {\n if (promptContainsAlias(prompt, canonical)) {\n matched.add(canonical);\n }\n }\n for (const [alias, canonicals] of Object.entries(tagIndex.aliases ?? {})) {\n if (!promptContainsAlias(prompt, alias)) continue;\n for (const canonical of canonicals) {\n matched.add(canonical);\n }\n }\n\n const expandedTags = expandCanonicalTags(tagIndex, Array.from(matched));\n const paths = queryByTagsFromIndex(tagIndex, expandedTags);\n return {\n matchedTags: Array.from(matched),\n expandedTags,\n paths,\n };\n } catch {\n return { matchedTags: explicitTags, expandedTags: explicitTags, paths: null };\n }\n}\n\n/**\n * Detect if a prompt is time-sensitive (mentions specific time references).\n * Used to decide whether to activate the temporal prefilter.\n */\nexport function isTemporalQuery(prompt: string): boolean {\n return /\\b(today|yesterday|this week|last week|this month|last month|recent(?:ly)?|lately|just now|earlier today|this morning|last night|last year|this year|\\d+ days? ago|\\d+ hours? ago|\\d+ weeks? ago|\\d+ months? ago|(?:in |on |during |since |before |after )?(?:january|february|march|april|may|june|july|august|september|october|november|december)(?:\\s+\\d{1,4})?|\\d{4}-\\d{2}-\\d{2}|\\d{1,2}\\/\\d{1,2}\\/\\d{2,4}|(?:spring|summer|fall|autumn|winter)\\s+\\d{4}|on the \\d{1,2}(?:st|nd|rd|th)?|last (?:monday|tuesday|wednesday|thursday|friday|saturday|sunday))\\b/i.test(\n prompt\n );\n}\n\n/**\n * Compute a \"from date\" string (YYYY-MM-DD) for a recency-based temporal query.\n * For \"recent\" / \"lately\" returns 7 days ago; for today/yesterday the obvious window.\n */\nexport function recencyWindowFromPrompt(prompt: string, nowMs: number = Date.now()): string {\n const p = prompt.toLowerCase();\n const now = new Date(nowMs);\n let daysBack = 7; // default\n\n if (/\\btoday\\b/.test(p) || /\\bthis morning\\b/.test(p) || /\\bjust now\\b/.test(p) || /\\bearlier today\\b/.test(p)) {\n daysBack = 0; // fromDate = today → window [today, today]\n } else if (/\\byesterday\\b/.test(p) || /\\blast night\\b/.test(p)) {\n daysBack = 1; // fromDate = yesterday → window [yesterday, today]\n } else if (/\\bthis week\\b/.test(p)) {\n daysBack = 7;\n } else if (/\\blast week\\b/.test(p)) {\n daysBack = 14;\n } else if (/\\bthis month\\b/.test(p)) {\n daysBack = 31;\n } else if (/\\blast month\\b/.test(p)) {\n daysBack = 62;\n } else if (/\\bthis year\\b/.test(p)) {\n // From Jan 1 of current year\n const jan1 = new Date(now.getFullYear(), 0, 1);\n return jan1.toISOString().slice(0, 10);\n } else if (/\\blast year\\b/.test(p)) {\n const jan1LastYear = new Date(now.getFullYear() - 1, 0, 1);\n return jan1LastYear.toISOString().slice(0, 10);\n } else {\n // Try specific month references: \"in March\", \"during January\", \"since February\"\n const monthNames = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n ];\n const monthMatch = p.match(\n /\\b(january|february|march|april|may|june|july|august|september|october|november|december)(?:\\s+(\\d{4}))?\\b/\n );\n if (monthMatch) {\n const monthIdx = monthNames.indexOf(monthMatch[1]);\n const year = monthMatch[2] ? Number.parseInt(monthMatch[2], 10) : now.getFullYear();\n // \"before <month>\" means everything prior to that month: use 2-year lookback\n // as fromDate so the window isn't unbounded. toDate is set to the month start\n // in recencyWindowBoundsFromPrompt.\n // IMPORTANT: when an explicit year is given, anchor the lookback to the named\n // month start (not to today). \"before January 2024\" should produce\n // fromDate ≈ 2022-01 — not today-730 days, which could land after 2024-01 and\n // invert the window for any explicitly past month within the 730-day horizon.\n if (/\\bbefore\\b/.test(p)) {\n if (monthMatch[2]) {\n // Explicit year: anchor fromDate 730 days before the named month start.\n const monthStart = new Date(year, monthIdx, 1);\n return new Date(monthStart.getTime() - 730 * 86_400_000).toISOString().slice(0, 10);\n }\n daysBack = 730;\n } else {\n const monthStart = new Date(year, monthIdx, 1);\n return monthStart.toISOString().slice(0, 10);\n }\n }\n\n // Try \"N weeks ago\"\n const weekMatch = p.match(/(\\d{1,5})\\s*weeks?\\s*ago/);\n if (weekMatch) {\n daysBack = Math.min(365, Number.parseInt(weekMatch[1], 10) * 7);\n } else {\n // Try \"N months ago\"\n const monthsAgoMatch = p.match(/(\\d{1,5})\\s*months?\\s*ago/);\n if (monthsAgoMatch) {\n daysBack = Math.min(730, Number.parseInt(monthsAgoMatch[1], 10) * 31);\n } else {\n const numMatch = p.match(/(\\d{1,5})\\s*days?\\s*ago/);\n if (numMatch) {\n daysBack = Math.min(365, Number.parseInt(numMatch[1], 10)); // no off-by-one: \"3 days ago\" → 3\n } else {\n const hrMatch = p.match(/(\\d{1,5})\\s*hours?\\s*ago/);\n if (hrMatch) {\n // Convert hours to days (ceiling); at least 1 day window\n daysBack = Math.max(1, Math.ceil(Number.parseInt(hrMatch[1], 10) / 24));\n }\n }\n }\n }\n\n // Try explicit date patterns: YYYY-MM-DD or MM/DD/YYYY\n const isoMatch = p.match(/(\\d{4})-(\\d{2})-(\\d{2})/);\n if (isoMatch) {\n return `${isoMatch[1]}-${isoMatch[2]}-${isoMatch[3]}`;\n }\n const usMatch = p.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{2,4})/);\n if (usMatch) {\n const year = usMatch[3].length === 2 ? 2000 + Number.parseInt(usMatch[3], 10) : Number.parseInt(usMatch[3], 10);\n return `${year}-${usMatch[1].padStart(2, \"0\")}-${usMatch[2].padStart(2, \"0\")}`;\n }\n\n // Try \"last Monday/Tuesday/etc\"\n const dayOfWeekMatch = p.match(/\\blast\\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\b/);\n if (dayOfWeekMatch) {\n const dayNames = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n const targetDay = dayNames.indexOf(dayOfWeekMatch[1]);\n const currentDay = now.getDay();\n daysBack = (currentDay - targetDay + 7) % 7 || 7; // at least 7 days back\n }\n }\n\n const from = new Date(nowMs - daysBack * 24 * 60 * 60 * 1000);\n return from.toISOString().slice(0, 10);\n}\n\n/**\n * Returns both the start and end of the temporal window implied by the prompt.\n *\n * `fromDate` is computed by delegating to `recencyWindowFromPrompt`, so the two\n * functions cannot diverge on the lower bound. `toDate` is computed independently\n * here using the same pattern-priority ordering, because the upper-bound arithmetic\n * differs (exclusive end vs. inclusive start) and would not be expressible as a\n * simple wrapper.\n *\n * Known divergence to be aware of: for \"N hours ago\", `recencyWindowFromPrompt`\n * uses `Math.max(1, Math.ceil(hours/24))` days back, so `fromDate` = yesterday for\n * sub-24h values. `toDate` here is always `tomorrow` when no explicit date is\n * specified (see the `toDaysBack === 0` branch), giving a 2-day window. This is\n * intentional — the window must cover both yesterday and today for recent hour-level\n * queries. Any fix to the hours formula in `recencyWindowFromPrompt` must be\n * manually reflected here.\n *\n * - `fromDate`: first day of the implied window (same as `recencyWindowFromPrompt`)\n * - `toDate`: first day NOT in the window (exclusive upper bound), so callers\n * should filter with `date >= fromDate && date < toDate`.\n */\nexport function recencyWindowBoundsFromPrompt(\n prompt: string,\n nowMs: number = Date.now()\n): { fromDate: string; toDate: string } {\n const fromDate = recencyWindowFromPrompt(prompt, nowMs);\n const p = prompt.toLowerCase();\n const now = new Date(nowMs);\n const today = now.toISOString().slice(0, 10);\n const tomorrow = new Date(nowMs + 86_400_000).toISOString().slice(0, 10);\n\n let toDate: string;\n\n if (/\\btoday\\b|\\bthis morning\\b|\\bjust now\\b|\\bearlier today\\b/.test(p)) {\n toDate = tomorrow; // exclusive: include all of today\n } else if (/\\byesterday\\b|\\blast night\\b/.test(p)) {\n toDate = today; // exclusive: include all of yesterday, stop before today\n } else if (/\\bthis week\\b|\\bthis month\\b|\\bthis year\\b/.test(p)) {\n toDate = tomorrow; // exclusive: include all of today\n } else if (/\\blast week\\b/.test(p)) {\n toDate = new Date(nowMs - 7 * 86_400_000).toISOString().slice(0, 10); // already exclusive\n } else if (/\\blast month\\b/.test(p)) {\n toDate = new Date(nowMs - 31 * 86_400_000).toISOString().slice(0, 10); // approximately exclusive\n } else if (/\\blast year\\b/.test(p)) {\n toDate = `${now.getFullYear()}-01-01`; // exclusive: stop at Jan 1 of current year\n } else {\n // Mirror the structure of recencyWindowFromPrompt's else branch:\n // month name → early set (highest priority), then ago patterns set a\n // working offset, then ISO/US/weekday patterns run AFTER ago patterns\n // and can override them — matching the priority ordering in recencyWindowFromPrompt.\n\n const monthNames = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n ];\n const monthMatch = p.match(\n /\\b(january|february|march|april|may|june|july|august|september|october|november|december)(?:\\s+(\\d{4}))?\\b/\n );\n if (monthMatch) {\n // \"since <month>\" / \"after <month>\" — open-ended: everything from that month to now.\n // \"before <month>\" — closed upper bound: everything before that month starts.\n // Plain \"<month>\" — just that calendar month.\n const monthIdx = monthNames.indexOf(monthMatch[1]);\n const year = monthMatch[2] ? Number.parseInt(monthMatch[2], 10) : now.getFullYear();\n const isSinceOrAfter = /\\b(since|after)\\b/.test(p);\n const isBefore = /\\bbefore\\b/.test(p);\n if (isSinceOrAfter) {\n toDate = tomorrow;\n } else if (isBefore) {\n // \"before <month>\" means everything prior to that month.\n // toDate = first day of that month (exclusive upper bound).\n toDate = new Date(year, monthIdx, 1).toISOString().slice(0, 10);\n } else {\n // Closed window: first day of the NEXT month is the exclusive upper bound.\n toDate = new Date(year, monthIdx + 1, 1).toISOString().slice(0, 10);\n }\n } else {\n // Ago patterns set a working offset (recent edge of the N-ago window).\n // Same nesting order as recencyWindowFromPrompt.\n // Sentinel -1 means \"no ago pattern matched\" → toDate falls back to tomorrow.\n let toDaysBack = -1;\n const weekMatch = p.match(/(\\d{1,5})\\s*weeks?\\s*ago/);\n if (weekMatch) {\n toDaysBack = Math.max(0, Math.min(52, Number.parseInt(weekMatch[1], 10)) - 1) * 7;\n } else {\n const monthsAgoMatch = p.match(/(\\d{1,5})\\s*months?\\s*ago/);\n if (monthsAgoMatch) {\n toDaysBack = Math.max(0, Math.min(24, Number.parseInt(monthsAgoMatch[1], 10)) - 1) * 31;\n } else {\n const numMatch = p.match(/(\\d{1,5})\\s*days?\\s*ago/);\n if (numMatch) {\n // (N-1) mirrors the weeks/months ago formula: \"3 days ago\" → window [today-3, today-2]\n // N=1 → toDaysBack=0 → toDate=today (exclusive) → window [yesterday, today) = 1 day. ✓\n toDaysBack = Math.max(0, Math.min(365, Number.parseInt(numMatch[1], 10)) - 1);\n } else {\n const hrMatch = p.match(/(\\d{1,5})\\s*hours?\\s*ago/);\n if (hrMatch) {\n // Sub-day precision: keep sentinel -1 so toDate falls back to tomorrow,\n // which includes today. fromDate = yesterday (recencyWindowFromPrompt uses ceiling).\n toDaysBack = -1;\n }\n }\n }\n }\n\n // Explicit date/weekday patterns run AFTER ago patterns (same as recencyWindowFromPrompt)\n // and override the ago-derived offset when present.\n const isoMatch = p.match(/(\\d{4})-(\\d{2})-(\\d{2})/);\n if (isoMatch) {\n // +1 day: exclusive upper bound includes the named date\n const d = new Date(`${isoMatch[1]}-${isoMatch[2]}-${isoMatch[3]}T00:00:00Z`);\n toDate = new Date(d.getTime() + 86_400_000).toISOString().slice(0, 10);\n } else {\n const usMatch = p.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{2,4})/);\n if (usMatch) {\n const year =\n usMatch[3].length === 2 ? 2000 + Number.parseInt(usMatch[3], 10) : Number.parseInt(usMatch[3], 10);\n // +1 day: exclusive upper bound includes the named date\n const d = new Date(`${year}-${usMatch[1].padStart(2, \"0\")}-${usMatch[2].padStart(2, \"0\")}T00:00:00Z`);\n toDate = new Date(d.getTime() + 86_400_000).toISOString().slice(0, 10);\n } else {\n const dayOfWeekMatch = p.match(/\\blast\\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\b/);\n if (dayOfWeekMatch) {\n const dayNames = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n const targetDay = dayNames.indexOf(dayOfWeekMatch[1]);\n const currentDay = now.getDay();\n const daysBack = (currentDay - targetDay + 7) % 7 || 7;\n // +1 day: exclusive upper bound includes the named weekday\n toDate = new Date(nowMs - (daysBack - 1) * 86_400_000).toISOString().slice(0, 10);\n } else {\n // Use the ago-derived offset.\n // toDaysBack=-1 means no pattern matched (or hours-ago): use tomorrow so today\n // is included in the window. toDaysBack=0 means N=1 ago (e.g. \"1 day ago\"):\n // toDate = today (exclusive) correctly creates a 1-day window [yesterday, today).\n toDate = toDaysBack < 0 ? tomorrow : new Date(nowMs - toDaysBack * 86_400_000).toISOString().slice(0, 10);\n }\n }\n }\n }\n }\n\n // Guard: if toDate would precede fromDate (inverted window from conflicting keywords),\n // fall back to tomorrow (exclusive upper bound that covers today) so we never produce\n // an empty window. Using `today` is insufficient because `date < today` excludes today.\n if (toDate <= fromDate) toDate = tomorrow;\n\n return { fromDate, toDate };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
countRecallTokenOverlap,
|
|
14
14
|
normalizeRecallTokens
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ZBJMUXZH.js";
|
|
16
16
|
import {
|
|
17
17
|
log
|
|
18
18
|
} from "./chunk-2ODBA7MQ.js";
|
|
@@ -217,4 +217,4 @@ export {
|
|
|
217
217
|
buildExtensionsBlockForConsolidation,
|
|
218
218
|
materializeAfterSemanticConsolidation
|
|
219
219
|
};
|
|
220
|
-
//# sourceMappingURL=chunk-
|
|
220
|
+
//# sourceMappingURL=chunk-7DZRO2DC.js.map
|