agenr 1.9.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +25 -15
- package/dist/adapters/openclaw/index.js +132 -10
- package/dist/{chunk-I6A6DPNF.js → chunk-XD3446YW.js} +2 -2
- package/dist/{chunk-EMRMV2QR.js → chunk-Y2BC7RCE.js} +1347 -110
- package/dist/chunk-ZYADFKX3.js +115 -0
- package/dist/cli.js +767 -252
- package/dist/core/recall/index.js +1 -2
- package/dist/internal-recall-eval-server.js +131 -12
- package/package.json +5 -4
- package/dist/chunk-ETQPUJGS.js +0 -0
- package/dist/{chunk-GUDCFFRV.js → chunk-MEHOGUZE.js} +175 -175
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import "../../chunk-ETQPUJGS.js";
|
|
2
1
|
import {
|
|
3
2
|
buildLexicalPlan,
|
|
4
3
|
combinedRelevance,
|
|
@@ -12,7 +11,7 @@ import {
|
|
|
12
11
|
recencyScore,
|
|
13
12
|
scoreCandidate,
|
|
14
13
|
tokenize
|
|
15
|
-
} from "../../chunk-
|
|
14
|
+
} from "../../chunk-MEHOGUZE.js";
|
|
16
15
|
export {
|
|
17
16
|
buildLexicalPlan,
|
|
18
17
|
combinedRelevance,
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
composeProcedureRecallText,
|
|
4
|
+
computeProcedureRevisionHash,
|
|
5
|
+
computeProcedureSourceHash
|
|
6
|
+
} from "./chunk-ZYADFKX3.js";
|
|
3
7
|
import {
|
|
4
8
|
CLAIM_KEY_SOURCES,
|
|
5
9
|
CLAIM_KEY_STATUSES,
|
|
@@ -11,6 +15,7 @@ import {
|
|
|
11
15
|
createEmbeddingClient,
|
|
12
16
|
createRecallAdapter,
|
|
13
17
|
isRecord,
|
|
18
|
+
normalizeProcedureDefinition,
|
|
14
19
|
parseOptionalBoolean,
|
|
15
20
|
parseOptionalIntegerInRange,
|
|
16
21
|
parseOptionalTimestampString,
|
|
@@ -23,10 +28,10 @@ import {
|
|
|
23
28
|
resolveEmbeddingApiKey,
|
|
24
29
|
resolveEmbeddingModel,
|
|
25
30
|
runUnifiedRecall
|
|
26
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-Y2BC7RCE.js";
|
|
27
32
|
import {
|
|
28
33
|
recall
|
|
29
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-MEHOGUZE.js";
|
|
30
35
|
|
|
31
36
|
// src/internal-recall-eval-server.ts
|
|
32
37
|
import process from "process";
|
|
@@ -424,6 +429,21 @@ function buildMetadata(request, results, projectedEntries) {
|
|
|
424
429
|
routing: results.routing,
|
|
425
430
|
timeWindow: results.timeWindow,
|
|
426
431
|
asOf: results.asOf,
|
|
432
|
+
procedure: results.procedure ? {
|
|
433
|
+
id: results.procedure.id,
|
|
434
|
+
procedureKey: results.procedure.procedure_key,
|
|
435
|
+
title: results.procedure.title,
|
|
436
|
+
goal: results.procedure.goal
|
|
437
|
+
} : void 0,
|
|
438
|
+
procedureCandidates: results.procedureCandidates.map((candidate) => ({
|
|
439
|
+
id: candidate.procedure.id,
|
|
440
|
+
procedureKey: candidate.procedure.procedure_key,
|
|
441
|
+
title: candidate.procedure.title,
|
|
442
|
+
score: candidate.score,
|
|
443
|
+
lexicalScore: candidate.scores.lexical,
|
|
444
|
+
vectorScore: candidate.scores.vector
|
|
445
|
+
})),
|
|
446
|
+
procedureNotices: results.procedureNotices,
|
|
427
447
|
notices: results.notices,
|
|
428
448
|
episodeCount: results.episodes.length
|
|
429
449
|
}
|
|
@@ -635,6 +655,87 @@ function hashText(value) {
|
|
|
635
655
|
return createHash("sha256").update(value).digest("hex");
|
|
636
656
|
}
|
|
637
657
|
|
|
658
|
+
// src/app/evals/recall/provision-procedure-fixtures.ts
|
|
659
|
+
import { createHash as createHash2 } from "crypto";
|
|
660
|
+
async function provisionRecallEvalProcedureFixtures(params) {
|
|
661
|
+
const procedures = prepareProcedures(params.caseId, params.procedurePool, params.provisionedAt);
|
|
662
|
+
if (procedures.length === 0) {
|
|
663
|
+
return {
|
|
664
|
+
provisionedCount: 0
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
await params.store.withTransaction(async (store) => {
|
|
668
|
+
for (const procedure of procedures) {
|
|
669
|
+
await store.insertProcedure(procedure);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
return {
|
|
673
|
+
provisionedCount: procedures.length
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function prepareProcedures(caseId, fixtures, provisionedAt) {
|
|
677
|
+
const resolvedIds = fixtures.map((fixture, index) => fixture.id ?? createFixtureId2(caseId, index, fixture));
|
|
678
|
+
const duplicateIds = findDuplicateIds2(resolvedIds);
|
|
679
|
+
if (duplicateIds.length > 0) {
|
|
680
|
+
throw new Error(`Procedure fixture IDs must be unique. Duplicate IDs: ${duplicateIds.join(", ")}.`);
|
|
681
|
+
}
|
|
682
|
+
const knownIds = new Set(resolvedIds);
|
|
683
|
+
return fixtures.map((fixture, index) => {
|
|
684
|
+
if (fixture.superseded_by && !knownIds.has(fixture.superseded_by)) {
|
|
685
|
+
throw new Error(`procedurePool[${index}].superseded_by references unknown fixture id "${fixture.superseded_by}".`);
|
|
686
|
+
}
|
|
687
|
+
const normalizedBody = normalizeProcedureDefinition(
|
|
688
|
+
{
|
|
689
|
+
procedure_key: fixture.procedure_key,
|
|
690
|
+
title: fixture.title,
|
|
691
|
+
goal: fixture.goal,
|
|
692
|
+
when_to_use: fixture.when_to_use ?? [],
|
|
693
|
+
when_not_to_use: fixture.when_not_to_use ?? [],
|
|
694
|
+
prerequisites: fixture.prerequisites ?? [],
|
|
695
|
+
steps: fixture.steps,
|
|
696
|
+
verification: fixture.verification ?? [],
|
|
697
|
+
failure_modes: fixture.failure_modes ?? [],
|
|
698
|
+
sources: fixture.sources ?? [{ kind: "manual", label: "recall eval fixture" }]
|
|
699
|
+
},
|
|
700
|
+
`procedurePool[${index}]`
|
|
701
|
+
);
|
|
702
|
+
const createdAt = fixture.created_at ?? provisionedAt;
|
|
703
|
+
const updatedAt = fixture.updated_at ?? createdAt;
|
|
704
|
+
return {
|
|
705
|
+
id: resolvedIds[index] ?? "",
|
|
706
|
+
...normalizedBody,
|
|
707
|
+
source_file: fixture.source_file,
|
|
708
|
+
recall_text: composeProcedureRecallText(normalizedBody),
|
|
709
|
+
revision_hash: computeProcedureRevisionHash(normalizedBody),
|
|
710
|
+
source_hash: computeProcedureSourceHash(JSON.stringify(normalizedBody)),
|
|
711
|
+
retired: fixture.retired ?? false,
|
|
712
|
+
retired_at: fixture.retired_at,
|
|
713
|
+
retired_reason: fixture.retired_reason,
|
|
714
|
+
superseded_by: fixture.superseded_by,
|
|
715
|
+
created_at: createdAt,
|
|
716
|
+
updated_at: updatedAt
|
|
717
|
+
};
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
function createFixtureId2(caseId, index, fixture) {
|
|
721
|
+
const digest = createHash2("sha256").update(caseId).update(":").update(String(index)).update(":").update(fixture.procedure_key).update(":").update(fixture.title).update(":").update(fixture.goal).digest("hex");
|
|
722
|
+
return `eval-procedure-${digest.slice(0, 24)}`;
|
|
723
|
+
}
|
|
724
|
+
function findDuplicateIds2(ids) {
|
|
725
|
+
const seen = /* @__PURE__ */ new Set();
|
|
726
|
+
const duplicates = [];
|
|
727
|
+
for (const id of ids) {
|
|
728
|
+
if (seen.has(id)) {
|
|
729
|
+
if (!duplicates.includes(id)) {
|
|
730
|
+
duplicates.push(id);
|
|
731
|
+
}
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
seen.add(id);
|
|
735
|
+
}
|
|
736
|
+
return duplicates;
|
|
737
|
+
}
|
|
738
|
+
|
|
638
739
|
// src/app/evals/recall/sandbox.ts
|
|
639
740
|
import { mkdir, mkdtemp, rm } from "fs/promises";
|
|
640
741
|
import { tmpdir } from "os";
|
|
@@ -644,6 +745,7 @@ import path from "path";
|
|
|
644
745
|
function createRecallEvalFixtureStore(database) {
|
|
645
746
|
return {
|
|
646
747
|
insertEntry: async (entry, embedding, contentHash) => database.insertEntry(entry, embedding, contentHash),
|
|
748
|
+
insertProcedure: async (procedure) => database.upsertProcedure(procedure),
|
|
647
749
|
withTransaction: async (fn) => database.withTransaction(async (transaction) => fn(createRecallEvalFixtureStore(transaction)))
|
|
648
750
|
};
|
|
649
751
|
}
|
|
@@ -670,6 +772,7 @@ async function setupRecallEvalSandbox(request) {
|
|
|
670
772
|
preserved,
|
|
671
773
|
fixtureStore: createRecallEvalFixtureStore(openDatabase),
|
|
672
774
|
episodeDatabase: openDatabase,
|
|
775
|
+
procedureDatabase: openDatabase,
|
|
673
776
|
createRecallPorts: (embedding) => createRecallAdapter(openDatabase, embedding),
|
|
674
777
|
cleanup: async () => {
|
|
675
778
|
await openDatabase.close().catch(() => void 0);
|
|
@@ -759,17 +862,32 @@ async function runRecallEvalCase(request) {
|
|
|
759
862
|
timings: diagnostics.buildTimings(elapsedMs2(startedAt))
|
|
760
863
|
});
|
|
761
864
|
}
|
|
762
|
-
if (request.memoryPool.length > 0) {
|
|
865
|
+
if (request.memoryPool.length > 0 || (request.procedurePool?.length ?? 0) > 0) {
|
|
763
866
|
const provisionStartedAt = Date.now();
|
|
764
867
|
try {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
868
|
+
let entryProvisionResult;
|
|
869
|
+
if (request.memoryPool.length > 0) {
|
|
870
|
+
entryProvisionResult = await provisionRecallEvalFixtures({
|
|
871
|
+
caseId: request.caseId,
|
|
872
|
+
memoryPool: request.memoryPool,
|
|
873
|
+
store: sandbox.fixtureStore,
|
|
874
|
+
embedding: getEmbeddingPort(),
|
|
875
|
+
provisionedAt
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
if ((request.procedurePool?.length ?? 0) > 0) {
|
|
879
|
+
await provisionRecallEvalProcedureFixtures({
|
|
880
|
+
caseId: request.caseId,
|
|
881
|
+
procedurePool: request.procedurePool ?? [],
|
|
882
|
+
store: sandbox.fixtureStore,
|
|
883
|
+
provisionedAt
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
if (entryProvisionResult) {
|
|
887
|
+
diagnostics.recordProvision(entryProvisionResult, elapsedMs2(provisionStartedAt));
|
|
888
|
+
} else {
|
|
889
|
+
diagnostics.recordFixtureProvisionTiming(elapsedMs2(provisionStartedAt));
|
|
890
|
+
}
|
|
773
891
|
} catch (error) {
|
|
774
892
|
diagnostics.recordFixtureProvisionTiming(elapsedMs2(provisionStartedAt));
|
|
775
893
|
return buildRecallEvalErrorResponse({
|
|
@@ -807,6 +925,7 @@ async function runRecallEvalCase(request) {
|
|
|
807
925
|
},
|
|
808
926
|
{
|
|
809
927
|
database: sandbox.episodeDatabase,
|
|
928
|
+
procedures: sandbox.procedureDatabase,
|
|
810
929
|
recall: recallPorts,
|
|
811
930
|
embeddingAvailable: embeddingSupport.available,
|
|
812
931
|
...embeddingSupport.error ? { embeddingError: embeddingSupport.error } : {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agenr",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Agent memory - local-first knowledge infrastructure for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,14 +13,15 @@
|
|
|
13
13
|
"CHANGELOG.md"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
+
"@clack/prompts": "^1.1.0",
|
|
17
|
+
"@libsql/client": "^0.17.2",
|
|
16
18
|
"@mariozechner/pi-agent-core": "^0.63.1",
|
|
17
19
|
"@mariozechner/pi-ai": "^0.63.2",
|
|
18
20
|
"@sinclair/typebox": "^0.34.0",
|
|
19
|
-
"@clack/prompts": "^1.1.0",
|
|
20
|
-
"@libsql/client": "^0.17.2",
|
|
21
21
|
"chalk": "^5.6.2",
|
|
22
22
|
"commander": "^14.0.3",
|
|
23
|
-
"openclaw": "^2026.4.9"
|
|
23
|
+
"openclaw": "^2026.4.9",
|
|
24
|
+
"yaml": "^2.8.3"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@eslint/js": "^10.0.1",
|
package/dist/chunk-ETQPUJGS.js
DELETED
|
File without changes
|
|
@@ -1,176 +1,3 @@
|
|
|
1
|
-
// src/core/recall/scoring.ts
|
|
2
|
-
var DAY_IN_MILLISECONDS = 1e3 * 60 * 60 * 24;
|
|
3
|
-
var IMPORTANCE_FLOOR = 0.4;
|
|
4
|
-
var RELEVANCE_WEIGHT = 0.5;
|
|
5
|
-
var RECENCY_WEIGHT = 0.25;
|
|
6
|
-
var IMPORTANCE_WEIGHT = 0.25;
|
|
7
|
-
function recencyScore(createdAt, expiry, now = /* @__PURE__ */ new Date()) {
|
|
8
|
-
if (expiry === "core") {
|
|
9
|
-
return 1;
|
|
10
|
-
}
|
|
11
|
-
const createdDate = asValidDate(createdAt);
|
|
12
|
-
const nowDate = asValidDate(now);
|
|
13
|
-
if (!createdDate || !nowDate) {
|
|
14
|
-
return 0;
|
|
15
|
-
}
|
|
16
|
-
const halfLifeDays = expiry === "permanent" ? 365 : 30;
|
|
17
|
-
const daysOld = Math.max(0, (nowDate.getTime() - createdDate.getTime()) / DAY_IN_MILLISECONDS);
|
|
18
|
-
return clampUnit(Math.pow(0.5, daysOld / halfLifeDays));
|
|
19
|
-
}
|
|
20
|
-
function gaussianRecency(createdAt, aroundDate, radiusDays) {
|
|
21
|
-
const createdDate = asValidDate(createdAt);
|
|
22
|
-
const anchorDate = asValidDate(aroundDate);
|
|
23
|
-
const normalizedRadius = sanitizeNonNegative(radiusDays);
|
|
24
|
-
if (!createdDate || !anchorDate) {
|
|
25
|
-
return 0;
|
|
26
|
-
}
|
|
27
|
-
if (normalizedRadius <= 0) {
|
|
28
|
-
return createdDate.getTime() === anchorDate.getTime() ? 1 : 0;
|
|
29
|
-
}
|
|
30
|
-
const daysDelta = Math.abs(createdDate.getTime() - anchorDate.getTime()) / DAY_IN_MILLISECONDS;
|
|
31
|
-
return clampUnit(Math.exp(-0.5 * (daysDelta / normalizedRadius) ** 2));
|
|
32
|
-
}
|
|
33
|
-
function importanceScore(importance) {
|
|
34
|
-
const clampedImportance = clampRange(sanitizeNonNegative(importance), 1, 10);
|
|
35
|
-
return clampUnit(IMPORTANCE_FLOOR + (clampedImportance - 1) / 9 * (1 - IMPORTANCE_FLOOR));
|
|
36
|
-
}
|
|
37
|
-
function combinedRelevance(vectorSim, lexical) {
|
|
38
|
-
const normalizedVector = clampUnit(sanitizeNonNegative(vectorSim));
|
|
39
|
-
const normalizedLexical = clampUnit(sanitizeNonNegative(lexical));
|
|
40
|
-
if (normalizedVector > 0 && normalizedLexical > 0) {
|
|
41
|
-
return clampUnit(normalizedVector * 0.6 + normalizedLexical * 0.4);
|
|
42
|
-
}
|
|
43
|
-
return Math.max(normalizedVector, normalizedLexical);
|
|
44
|
-
}
|
|
45
|
-
function scoreCandidate(params) {
|
|
46
|
-
const vector = clampUnit(sanitizeNonNegative(params.vectorSim));
|
|
47
|
-
const lexical = clampUnit(sanitizeNonNegative(params.lexical));
|
|
48
|
-
const recency = clampUnit(sanitizeNonNegative(params.recency));
|
|
49
|
-
const importance = clampUnit(sanitizeNonNegative(params.importance));
|
|
50
|
-
const relevance = combinedRelevance(vector, lexical);
|
|
51
|
-
const score = clampUnit(relevance * RELEVANCE_WEIGHT + recency * RECENCY_WEIGHT + importance * IMPORTANCE_WEIGHT);
|
|
52
|
-
return {
|
|
53
|
-
score,
|
|
54
|
-
scores: {
|
|
55
|
-
relevance,
|
|
56
|
-
vector,
|
|
57
|
-
lexical,
|
|
58
|
-
recency,
|
|
59
|
-
importance
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
function cosineSimilarity(left, right) {
|
|
64
|
-
const size = Math.min(left.length, right.length);
|
|
65
|
-
if (size === 0) {
|
|
66
|
-
return 0;
|
|
67
|
-
}
|
|
68
|
-
let dot = 0;
|
|
69
|
-
let leftNorm = 0;
|
|
70
|
-
let rightNorm = 0;
|
|
71
|
-
for (let index = 0; index < size; index += 1) {
|
|
72
|
-
const leftValue = sanitizeFinite(left[index]);
|
|
73
|
-
const rightValue = sanitizeFinite(right[index]);
|
|
74
|
-
dot += leftValue * rightValue;
|
|
75
|
-
leftNorm += leftValue * leftValue;
|
|
76
|
-
rightNorm += rightValue * rightValue;
|
|
77
|
-
}
|
|
78
|
-
if (leftNorm <= 0 || rightNorm <= 0) {
|
|
79
|
-
return 0;
|
|
80
|
-
}
|
|
81
|
-
return clampUnit(dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm)));
|
|
82
|
-
}
|
|
83
|
-
var asValidDate = (value) => {
|
|
84
|
-
const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
85
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
86
|
-
};
|
|
87
|
-
var clampUnit = (value) => clampRange(sanitizeNonNegative(value), 0, 1);
|
|
88
|
-
var clampRange = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
89
|
-
var sanitizeFinite = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
90
|
-
var sanitizeNonNegative = (value) => Math.max(0, sanitizeFinite(value));
|
|
91
|
-
|
|
92
|
-
// src/core/recall/temporal.ts
|
|
93
|
-
var DAY_IN_MILLISECONDS2 = 1e3 * 60 * 60 * 24;
|
|
94
|
-
var MONTH_INDEX = /* @__PURE__ */ new Map([
|
|
95
|
-
["january", 0],
|
|
96
|
-
["february", 1],
|
|
97
|
-
["march", 2],
|
|
98
|
-
["april", 3],
|
|
99
|
-
["may", 4],
|
|
100
|
-
["june", 5],
|
|
101
|
-
["july", 6],
|
|
102
|
-
["august", 7],
|
|
103
|
-
["september", 8],
|
|
104
|
-
["october", 9],
|
|
105
|
-
["november", 10],
|
|
106
|
-
["december", 11]
|
|
107
|
-
]);
|
|
108
|
-
function inferAroundDate(text, now = /* @__PURE__ */ new Date()) {
|
|
109
|
-
const normalized = text.trim().toLowerCase();
|
|
110
|
-
const referenceNow = asValidDate2(now);
|
|
111
|
-
if (normalized.length === 0 || !referenceNow) {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
let inferred = null;
|
|
115
|
-
if (/\byesterday\b/.test(normalized)) {
|
|
116
|
-
inferred = offsetDays(referenceNow, 1);
|
|
117
|
-
} else if (/\blast week\b/.test(normalized)) {
|
|
118
|
-
inferred = offsetDays(referenceNow, 7);
|
|
119
|
-
} else if (/\blast month\b/.test(normalized)) {
|
|
120
|
-
inferred = offsetDays(referenceNow, 30);
|
|
121
|
-
} else if (/\blast year\b/.test(normalized)) {
|
|
122
|
-
inferred = offsetDays(referenceNow, 365);
|
|
123
|
-
} else if (/\bthis week\b/.test(normalized)) {
|
|
124
|
-
inferred = offsetDays(referenceNow, 3);
|
|
125
|
-
} else if (/\bthis month\b/.test(normalized)) {
|
|
126
|
-
inferred = offsetDays(referenceNow, 15);
|
|
127
|
-
} else {
|
|
128
|
-
const relativeMatch = normalized.match(/\b(\d+)\s+(day|days|week|weeks|month|months)\s+ago\b/);
|
|
129
|
-
if (relativeMatch) {
|
|
130
|
-
const amount = Number(relativeMatch[1]);
|
|
131
|
-
const unit = relativeMatch[2];
|
|
132
|
-
const multiplier = unit?.startsWith("week") ? 7 : unit?.startsWith("month") ? 30 : 1;
|
|
133
|
-
inferred = Number.isFinite(amount) ? offsetDays(referenceNow, amount * multiplier) : null;
|
|
134
|
-
} else {
|
|
135
|
-
const monthMatch = normalized.match(/\bin\s+(january|february|march|april|may|june|july|august|september|october|november|december)\b/);
|
|
136
|
-
if (monthMatch?.[1]) {
|
|
137
|
-
inferred = inferMonthAnchor(monthMatch[1], referenceNow);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (!inferred) {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
return inferred.getTime() > referenceNow.getTime() ? new Date(referenceNow.getTime()) : inferred;
|
|
145
|
-
}
|
|
146
|
-
function parseRelativeDate(input, now = /* @__PURE__ */ new Date()) {
|
|
147
|
-
const trimmed = input.trim();
|
|
148
|
-
const referenceNow = asValidDate2(now);
|
|
149
|
-
if (trimmed.length === 0 || !referenceNow) {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
const durationMatch = trimmed.match(/^(\d+)d$/i);
|
|
153
|
-
if (durationMatch?.[1]) {
|
|
154
|
-
const days = Number(durationMatch[1]);
|
|
155
|
-
return Number.isFinite(days) ? offsetDays(referenceNow, days) : null;
|
|
156
|
-
}
|
|
157
|
-
const parsed = new Date(trimmed);
|
|
158
|
-
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
159
|
-
}
|
|
160
|
-
var asValidDate2 = (value) => {
|
|
161
|
-
const date = new Date(value.getTime());
|
|
162
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
163
|
-
};
|
|
164
|
-
var offsetDays = (date, days) => new Date(date.getTime() - days * DAY_IN_MILLISECONDS2);
|
|
165
|
-
var inferMonthAnchor = (monthName, now) => {
|
|
166
|
-
const monthIndex = MONTH_INDEX.get(monthName);
|
|
167
|
-
if (monthIndex === void 0) {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
const year = monthIndex <= now.getUTCMonth() ? now.getUTCFullYear() : now.getUTCFullYear() - 1;
|
|
171
|
-
return new Date(Date.UTC(year, monthIndex, 15));
|
|
172
|
-
};
|
|
173
|
-
|
|
174
1
|
// src/core/recall/lexical.ts
|
|
175
2
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
176
3
|
"the",
|
|
@@ -361,6 +188,97 @@ var hasConsecutivePhrase = (haystack, needle) => {
|
|
|
361
188
|
return false;
|
|
362
189
|
};
|
|
363
190
|
|
|
191
|
+
// src/core/recall/scoring.ts
|
|
192
|
+
var DAY_IN_MILLISECONDS = 1e3 * 60 * 60 * 24;
|
|
193
|
+
var IMPORTANCE_FLOOR = 0.4;
|
|
194
|
+
var RELEVANCE_WEIGHT = 0.5;
|
|
195
|
+
var RECENCY_WEIGHT = 0.25;
|
|
196
|
+
var IMPORTANCE_WEIGHT = 0.25;
|
|
197
|
+
function recencyScore(createdAt, expiry, now = /* @__PURE__ */ new Date()) {
|
|
198
|
+
if (expiry === "core") {
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
const createdDate = asValidDate(createdAt);
|
|
202
|
+
const nowDate = asValidDate(now);
|
|
203
|
+
if (!createdDate || !nowDate) {
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
const halfLifeDays = expiry === "permanent" ? 365 : 30;
|
|
207
|
+
const daysOld = Math.max(0, (nowDate.getTime() - createdDate.getTime()) / DAY_IN_MILLISECONDS);
|
|
208
|
+
return clampUnit(Math.pow(0.5, daysOld / halfLifeDays));
|
|
209
|
+
}
|
|
210
|
+
function gaussianRecency(createdAt, aroundDate, radiusDays) {
|
|
211
|
+
const createdDate = asValidDate(createdAt);
|
|
212
|
+
const anchorDate = asValidDate(aroundDate);
|
|
213
|
+
const normalizedRadius = sanitizeNonNegative(radiusDays);
|
|
214
|
+
if (!createdDate || !anchorDate) {
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
if (normalizedRadius <= 0) {
|
|
218
|
+
return createdDate.getTime() === anchorDate.getTime() ? 1 : 0;
|
|
219
|
+
}
|
|
220
|
+
const daysDelta = Math.abs(createdDate.getTime() - anchorDate.getTime()) / DAY_IN_MILLISECONDS;
|
|
221
|
+
return clampUnit(Math.exp(-0.5 * (daysDelta / normalizedRadius) ** 2));
|
|
222
|
+
}
|
|
223
|
+
function importanceScore(importance) {
|
|
224
|
+
const clampedImportance = clampRange(sanitizeNonNegative(importance), 1, 10);
|
|
225
|
+
return clampUnit(IMPORTANCE_FLOOR + (clampedImportance - 1) / 9 * (1 - IMPORTANCE_FLOOR));
|
|
226
|
+
}
|
|
227
|
+
function combinedRelevance(vectorSim, lexical) {
|
|
228
|
+
const normalizedVector = clampUnit(sanitizeNonNegative(vectorSim));
|
|
229
|
+
const normalizedLexical = clampUnit(sanitizeNonNegative(lexical));
|
|
230
|
+
if (normalizedVector > 0 && normalizedLexical > 0) {
|
|
231
|
+
return clampUnit(normalizedVector * 0.6 + normalizedLexical * 0.4);
|
|
232
|
+
}
|
|
233
|
+
return Math.max(normalizedVector, normalizedLexical);
|
|
234
|
+
}
|
|
235
|
+
function scoreCandidate(params) {
|
|
236
|
+
const vector = clampUnit(sanitizeNonNegative(params.vectorSim));
|
|
237
|
+
const lexical = clampUnit(sanitizeNonNegative(params.lexical));
|
|
238
|
+
const recency = clampUnit(sanitizeNonNegative(params.recency));
|
|
239
|
+
const importance = clampUnit(sanitizeNonNegative(params.importance));
|
|
240
|
+
const relevance = combinedRelevance(vector, lexical);
|
|
241
|
+
const score = clampUnit(relevance * RELEVANCE_WEIGHT + recency * RECENCY_WEIGHT + importance * IMPORTANCE_WEIGHT);
|
|
242
|
+
return {
|
|
243
|
+
score,
|
|
244
|
+
scores: {
|
|
245
|
+
relevance,
|
|
246
|
+
vector,
|
|
247
|
+
lexical,
|
|
248
|
+
recency,
|
|
249
|
+
importance
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function cosineSimilarity(left, right) {
|
|
254
|
+
const size = Math.min(left.length, right.length);
|
|
255
|
+
if (size === 0) {
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
let dot = 0;
|
|
259
|
+
let leftNorm = 0;
|
|
260
|
+
let rightNorm = 0;
|
|
261
|
+
for (let index = 0; index < size; index += 1) {
|
|
262
|
+
const leftValue = sanitizeFinite(left[index]);
|
|
263
|
+
const rightValue = sanitizeFinite(right[index]);
|
|
264
|
+
dot += leftValue * rightValue;
|
|
265
|
+
leftNorm += leftValue * leftValue;
|
|
266
|
+
rightNorm += rightValue * rightValue;
|
|
267
|
+
}
|
|
268
|
+
if (leftNorm <= 0 || rightNorm <= 0) {
|
|
269
|
+
return 0;
|
|
270
|
+
}
|
|
271
|
+
return clampUnit(dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm)));
|
|
272
|
+
}
|
|
273
|
+
var asValidDate = (value) => {
|
|
274
|
+
const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
275
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
276
|
+
};
|
|
277
|
+
var clampUnit = (value) => clampRange(sanitizeNonNegative(value), 0, 1);
|
|
278
|
+
var clampRange = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
279
|
+
var sanitizeFinite = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
280
|
+
var sanitizeNonNegative = (value) => Math.max(0, sanitizeFinite(value));
|
|
281
|
+
|
|
364
282
|
// src/core/claim-key.ts
|
|
365
283
|
var UNKNOWN_SEGMENT = "unknown";
|
|
366
284
|
var SELF_REFERENTIAL_ENTITIES = /* @__PURE__ */ new Set(["i", "me", "myself", "the_user", "user", "we", "our_team", "the_project", "this_project"]);
|
|
@@ -877,6 +795,88 @@ function resolveConfiguredAttributeHeadPolicy(attributeHead, config) {
|
|
|
877
795
|
return config?.attributeHeads?.[loweredAttributeHead];
|
|
878
796
|
}
|
|
879
797
|
|
|
798
|
+
// src/core/recall/temporal.ts
|
|
799
|
+
var DAY_IN_MILLISECONDS2 = 1e3 * 60 * 60 * 24;
|
|
800
|
+
var MONTH_INDEX = /* @__PURE__ */ new Map([
|
|
801
|
+
["january", 0],
|
|
802
|
+
["february", 1],
|
|
803
|
+
["march", 2],
|
|
804
|
+
["april", 3],
|
|
805
|
+
["may", 4],
|
|
806
|
+
["june", 5],
|
|
807
|
+
["july", 6],
|
|
808
|
+
["august", 7],
|
|
809
|
+
["september", 8],
|
|
810
|
+
["october", 9],
|
|
811
|
+
["november", 10],
|
|
812
|
+
["december", 11]
|
|
813
|
+
]);
|
|
814
|
+
function inferAroundDate(text, now = /* @__PURE__ */ new Date()) {
|
|
815
|
+
const normalized = text.trim().toLowerCase();
|
|
816
|
+
const referenceNow = asValidDate2(now);
|
|
817
|
+
if (normalized.length === 0 || !referenceNow) {
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
let inferred = null;
|
|
821
|
+
if (/\byesterday\b/.test(normalized)) {
|
|
822
|
+
inferred = offsetDays(referenceNow, 1);
|
|
823
|
+
} else if (/\blast week\b/.test(normalized)) {
|
|
824
|
+
inferred = offsetDays(referenceNow, 7);
|
|
825
|
+
} else if (/\blast month\b/.test(normalized)) {
|
|
826
|
+
inferred = offsetDays(referenceNow, 30);
|
|
827
|
+
} else if (/\blast year\b/.test(normalized)) {
|
|
828
|
+
inferred = offsetDays(referenceNow, 365);
|
|
829
|
+
} else if (/\bthis week\b/.test(normalized)) {
|
|
830
|
+
inferred = offsetDays(referenceNow, 3);
|
|
831
|
+
} else if (/\bthis month\b/.test(normalized)) {
|
|
832
|
+
inferred = offsetDays(referenceNow, 15);
|
|
833
|
+
} else {
|
|
834
|
+
const relativeMatch = normalized.match(/\b(\d+)\s+(day|days|week|weeks|month|months)\s+ago\b/);
|
|
835
|
+
if (relativeMatch) {
|
|
836
|
+
const amount = Number(relativeMatch[1]);
|
|
837
|
+
const unit = relativeMatch[2];
|
|
838
|
+
const multiplier = unit?.startsWith("week") ? 7 : unit?.startsWith("month") ? 30 : 1;
|
|
839
|
+
inferred = Number.isFinite(amount) ? offsetDays(referenceNow, amount * multiplier) : null;
|
|
840
|
+
} else {
|
|
841
|
+
const monthMatch = normalized.match(/\bin\s+(january|february|march|april|may|june|july|august|september|october|november|december)\b/);
|
|
842
|
+
if (monthMatch?.[1]) {
|
|
843
|
+
inferred = inferMonthAnchor(monthMatch[1], referenceNow);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (!inferred) {
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
return inferred.getTime() > referenceNow.getTime() ? new Date(referenceNow.getTime()) : inferred;
|
|
851
|
+
}
|
|
852
|
+
function parseRelativeDate(input, now = /* @__PURE__ */ new Date()) {
|
|
853
|
+
const trimmed = input.trim();
|
|
854
|
+
const referenceNow = asValidDate2(now);
|
|
855
|
+
if (trimmed.length === 0 || !referenceNow) {
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
const durationMatch = trimmed.match(/^(\d+)d$/i);
|
|
859
|
+
if (durationMatch?.[1]) {
|
|
860
|
+
const days = Number(durationMatch[1]);
|
|
861
|
+
return Number.isFinite(days) ? offsetDays(referenceNow, days) : null;
|
|
862
|
+
}
|
|
863
|
+
const parsed = new Date(trimmed);
|
|
864
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
865
|
+
}
|
|
866
|
+
var asValidDate2 = (value) => {
|
|
867
|
+
const date = new Date(value.getTime());
|
|
868
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
869
|
+
};
|
|
870
|
+
var offsetDays = (date, days) => new Date(date.getTime() - days * DAY_IN_MILLISECONDS2);
|
|
871
|
+
var inferMonthAnchor = (monthName, now) => {
|
|
872
|
+
const monthIndex = MONTH_INDEX.get(monthName);
|
|
873
|
+
if (monthIndex === void 0) {
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
const year = monthIndex <= now.getUTCMonth() ? now.getUTCFullYear() : now.getUTCFullYear() - 1;
|
|
877
|
+
return new Date(Date.UTC(year, monthIndex, 15));
|
|
878
|
+
};
|
|
879
|
+
|
|
880
880
|
// src/core/recall/trace.ts
|
|
881
881
|
var NOOP_RECALL_TRACE_SINK = {
|
|
882
882
|
reportSummary() {
|
|
@@ -1507,11 +1507,11 @@ export {
|
|
|
1507
1507
|
describeClaimKeyNormalizationFailure,
|
|
1508
1508
|
describeExtractedClaimKeyRejection,
|
|
1509
1509
|
describeClaimKeySuspicion,
|
|
1510
|
-
inferAroundDate,
|
|
1511
|
-
parseRelativeDate,
|
|
1512
1510
|
tokenize,
|
|
1513
1511
|
buildLexicalPlan,
|
|
1514
1512
|
computeLexicalScore,
|
|
1513
|
+
inferAroundDate,
|
|
1514
|
+
parseRelativeDate,
|
|
1515
1515
|
resolveClaimSlotPolicy,
|
|
1516
1516
|
recall
|
|
1517
1517
|
};
|