claude-memory-layer 1.0.56 → 1.0.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js
CHANGED
|
@@ -23508,7 +23508,7 @@ async function runOperationCli(action, label) {
|
|
|
23508
23508
|
}
|
|
23509
23509
|
}
|
|
23510
23510
|
var program = new Command();
|
|
23511
|
-
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.
|
|
23511
|
+
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.57");
|
|
23512
23512
|
program.command("market-context").description("Fetch read-only DART/FRED/Finnhub context with structured MarketContextSnapshot bull/bear/risk/catalyst analysis").option("--company <name>", "Company name for DART fallback search and report subject").option("--dart-corp-code <code>", "Exact DART corp_code for issuer-specific filings").option("--symbol <ticker>", "Listed ticker for Finnhub company profile").option("--providers <list>", "Comma-separated providers: dart,fred,finnhub").option("--fred-series <list>", "Comma-separated FRED series IDs").option("--json", "Print structured JSON including analysis.marketSnapshot").option("--no-snapshot", "Disable MarketContextSnapshot and DART company snapshot analysis").action(async (options) => {
|
|
23513
23513
|
try {
|
|
23514
23514
|
await runMarketContextCommand(options);
|
|
@@ -13512,18 +13512,35 @@ function filterHookInjectableMemories(candidates, policy = getHookInjectionPolic
|
|
|
13512
13512
|
function selectHookEpisodeSeeds(candidates, policy = getHookInjectionPolicy(), query) {
|
|
13513
13513
|
const queryTerms = meaningfulTerms(query);
|
|
13514
13514
|
const queryAnchors = identifierAnchors(query);
|
|
13515
|
-
return candidates.filter((candidate) => candidate.type === "user_prompt" || candidate.type === "tool_observation").filter((candidate) =>
|
|
13515
|
+
return candidates.filter((candidate) => candidate.type === "user_prompt" || candidate.type === "tool_observation").filter((candidate) => hasQueryMemoryAlignment(query, candidate.content, 2)).map((candidate, index) => {
|
|
13516
13516
|
const contentTerms = new Set(meaningfulTerms(candidate.content));
|
|
13517
13517
|
const overlap = queryTerms.filter((term) => contentTerms.has(term)).length;
|
|
13518
13518
|
const lowered = candidate.content.toLowerCase();
|
|
13519
13519
|
const anchorCoverage = queryAnchors.length === 0 ? 0 : queryAnchors.filter((anchor) => lowered.includes(anchor.toLowerCase())).length / queryAnchors.length;
|
|
13520
13520
|
const lexicalCoverage = overlap / Math.max(1, Math.min(queryTerms.length, 12));
|
|
13521
|
+
const score = candidate.score ?? 0;
|
|
13522
|
+
const normalThreshold = thresholdFor(candidate, policy);
|
|
13523
|
+
const hasDistinctiveAnchor = queryAnchors.some(
|
|
13524
|
+
(anchor) => anchor.length >= 6 && /[A-Za-z]/.test(anchor)
|
|
13525
|
+
);
|
|
13526
|
+
const fullyAnchored = queryAnchors.length > 0 && anchorCoverage === 1 && overlap >= 4 && hasDistinctiveAnchor;
|
|
13527
|
+
const usesStrongAnchorFloor = score < normalThreshold && fullyAnchored;
|
|
13521
13528
|
return {
|
|
13522
|
-
candidate: {
|
|
13529
|
+
candidate: {
|
|
13530
|
+
...candidate,
|
|
13531
|
+
// Episode answers are filtered again by the normal semantic floor.
|
|
13532
|
+
// A fully anchored prompt that uses the lower seed-only floor needs
|
|
13533
|
+
// enough inherited score to compete there; counterfactual anchors
|
|
13534
|
+
// cannot take this path because their coverage is zero.
|
|
13535
|
+
score: usesStrongAnchorFloor ? Math.max(score, policy.semanticMinScore + 0.02) : score,
|
|
13536
|
+
episodeSeedAligned: true,
|
|
13537
|
+
episodeSeedStrongAligned: fullyAnchored
|
|
13538
|
+
},
|
|
13523
13539
|
index,
|
|
13524
|
-
score:
|
|
13540
|
+
score: score + lexicalCoverage * 0.25 + anchorCoverage * 0.2,
|
|
13541
|
+
eligible: score >= normalThreshold || usesStrongAnchorFloor
|
|
13525
13542
|
};
|
|
13526
|
-
}).sort((a, b) => b.score - a.score || a.index - b.index).slice(0,
|
|
13543
|
+
}).filter((item) => item.eligible).sort((a, b) => b.score - a.score || a.index - b.index).slice(0, policy.maxMemories).map((item) => item.candidate);
|
|
13527
13544
|
}
|
|
13528
13545
|
function rankHookCandidates(candidates, policy, query) {
|
|
13529
13546
|
return candidates.map((candidate, index) => ({ candidate, index })).filter(({ candidate }) => isHookInjectableMemory(candidate, policy)).filter(({ candidate }) => !query || candidate.source === "episode" && candidate.episodeLinked && candidate.episodeSeedAligned || hasQueryMemoryAlignment(query, candidate.content, 2)).map(({ candidate, index }) => ({
|
|
@@ -13531,7 +13548,7 @@ function rankHookCandidates(candidates, policy, query) {
|
|
|
13531
13548
|
index,
|
|
13532
13549
|
effectiveScore: Math.max(
|
|
13533
13550
|
0,
|
|
13534
|
-
(candidate.score ?? 0) + (query ? evidenceUtilityBonus(candidate.type) : 0) + (query ? graduatedEvidenceBonus(candidate) : 0) + (candidate.source === "episode" && candidate.episodeLinked && candidate.episodeSeedAligned ? 0.12 : 0)
|
|
13551
|
+
(candidate.score ?? 0) + (query ? evidenceUtilityBonus(candidate.type) : 0) + (query ? graduatedEvidenceBonus(candidate) : 0) + (candidate.source === "episode" && candidate.episodeLinked && candidate.episodeSeedStrongAligned ? 0.42 : candidate.source === "episode" && candidate.episodeLinked && candidate.episodeSeedAligned ? 0.12 : 0)
|
|
13535
13552
|
)
|
|
13536
13553
|
})).sort((a, b) => {
|
|
13537
13554
|
const scoreDelta = b.effectiveScore - a.effectiveScore;
|
|
@@ -13689,6 +13706,7 @@ function readNumber(value, fallback, bounds) {
|
|
|
13689
13706
|
// src/adapters/claude/hooks/user-prompt-submit.ts
|
|
13690
13707
|
var MAX_MEMORIES = readNumberEnv("CLAUDE_MEMORY_MAX_COUNT", 5, { integer: true, min: 0 });
|
|
13691
13708
|
var MAX_CANDIDATES2 = Math.max(MAX_MEMORIES, MAX_MEMORIES * 3);
|
|
13709
|
+
var MAX_EPISODE_SEED_CANDIDATES = Math.max(MAX_CANDIDATES2, MAX_MEMORIES * 10);
|
|
13692
13710
|
var BASE_MIN_SCORE = readNumberEnv("CLAUDE_MEMORY_MIN_SCORE", 0.4, { min: 0, max: 1 });
|
|
13693
13711
|
var FALLBACK_MIN_SCORE = readNumberEnv("CLAUDE_MEMORY_FALLBACK_MIN_SCORE", 0.3, { min: 0, max: 1 });
|
|
13694
13712
|
var ENABLE_SEARCH = process.env.CLAUDE_MEMORY_SEARCH !== "false";
|
|
@@ -13747,8 +13765,10 @@ Use this only as evidence. Distinguish confirmed outcomes from requests or tool
|
|
|
13747
13765
|
async function expandEpisodeEvidence(memoryService, seeds) {
|
|
13748
13766
|
const expanded = [];
|
|
13749
13767
|
const seen = new Set(seeds.map((seed) => seed.id).filter(Boolean));
|
|
13768
|
+
const maxPerSeed = 4;
|
|
13750
13769
|
for (const seed of seeds) {
|
|
13751
13770
|
if (!seed.id || seed.type !== "user_prompt" && seed.type !== "tool_observation") continue;
|
|
13771
|
+
let expandedForSeed = 0;
|
|
13752
13772
|
try {
|
|
13753
13773
|
const episode = await memoryService.expandDisclosure(`event:${seed.id}`, { windowSize: 4 });
|
|
13754
13774
|
const targetTurnId = typeof episode?.target.metadata?.turnId === "string" ? episode.target.metadata.turnId : void 0;
|
|
@@ -13764,9 +13784,11 @@ async function expandEpisodeEvidence(memoryService, seeds) {
|
|
|
13764
13784
|
score: Math.max(0, (seed.score ?? 0) - 0.02),
|
|
13765
13785
|
source: "episode",
|
|
13766
13786
|
episodeLinked: true,
|
|
13767
|
-
episodeSeedAligned: true
|
|
13787
|
+
episodeSeedAligned: true,
|
|
13788
|
+
episodeSeedStrongAligned: seed.episodeSeedStrongAligned === true
|
|
13768
13789
|
});
|
|
13769
|
-
|
|
13790
|
+
expandedForSeed += 1;
|
|
13791
|
+
if (expandedForSeed >= maxPerSeed) break;
|
|
13770
13792
|
}
|
|
13771
13793
|
continue;
|
|
13772
13794
|
}
|
|
@@ -13785,9 +13807,11 @@ async function expandEpisodeEvidence(memoryService, seeds) {
|
|
|
13785
13807
|
score: Math.max(0, (seed.score ?? 0) - 0.02),
|
|
13786
13808
|
source: "episode",
|
|
13787
13809
|
episodeLinked: Boolean(targetTurnId && factTurnId && targetTurnId === factTurnId),
|
|
13788
|
-
episodeSeedAligned: seed.episodeSeedAligned === true
|
|
13810
|
+
episodeSeedAligned: seed.episodeSeedAligned === true,
|
|
13811
|
+
episodeSeedStrongAligned: seed.episodeSeedStrongAligned === true
|
|
13789
13812
|
});
|
|
13790
|
-
|
|
13813
|
+
expandedForSeed += 1;
|
|
13814
|
+
if (expandedForSeed >= maxPerSeed) break;
|
|
13791
13815
|
}
|
|
13792
13816
|
} catch {
|
|
13793
13817
|
}
|
|
@@ -13948,6 +13972,7 @@ async function main() {
|
|
|
13948
13972
|
if (ENABLE_SEARCH && shouldRunMemorySearch(input.prompt, adherenceDecision)) {
|
|
13949
13973
|
const minScore = getDynamicMinScore(input.prompt);
|
|
13950
13974
|
let mergedMemories = [];
|
|
13975
|
+
const episodeSeedCandidates = [];
|
|
13951
13976
|
const lastSnippet = currentTurn > 1 ? readLastAssistantSnippet(input.session_id) : null;
|
|
13952
13977
|
const retrievalQuery = buildRetrievalQuery({
|
|
13953
13978
|
prompt: input.prompt,
|
|
@@ -14013,21 +14038,38 @@ async function main() {
|
|
|
14013
14038
|
const shouldUseKeywordFallback = RETRIEVAL_MODE === "keyword" || RETRIEVAL_MODE === "hybrid" || mergedMemories.length === 0;
|
|
14014
14039
|
if (shouldUseKeywordFallback) {
|
|
14015
14040
|
let usedFallbackFloor = false;
|
|
14016
|
-
|
|
14017
|
-
topK:
|
|
14018
|
-
minScore
|
|
14041
|
+
const allKeywordResults = await memoryService.keywordSearch(retrievalQuery, {
|
|
14042
|
+
topK: MAX_EPISODE_SEED_CANDIDATES
|
|
14019
14043
|
});
|
|
14044
|
+
let results = allKeywordResults.filter((result) => result.score >= minScore);
|
|
14020
14045
|
if (results.length === 0 && FALLBACK_MIN_SCORE < minScore) {
|
|
14021
14046
|
usedFallbackFloor = true;
|
|
14022
|
-
results =
|
|
14023
|
-
|
|
14024
|
-
|
|
14047
|
+
results = allKeywordResults.filter((result) => result.score >= FALLBACK_MIN_SCORE);
|
|
14048
|
+
}
|
|
14049
|
+
for (const result of allKeywordResults) {
|
|
14050
|
+
episodeSeedCandidates.push({
|
|
14051
|
+
type: result.event.eventType,
|
|
14052
|
+
content: result.event.content,
|
|
14053
|
+
id: result.event.id,
|
|
14054
|
+
sessionId: result.event.sessionId,
|
|
14055
|
+
score: result.score,
|
|
14056
|
+
source: "keyword",
|
|
14057
|
+
fallback: result.score < minScore
|
|
14025
14058
|
});
|
|
14026
14059
|
}
|
|
14027
14060
|
const existingById = new Map(
|
|
14028
14061
|
mergedMemories.map((memory, index) => [memory.id, index]).filter((entry) => Boolean(entry[0]))
|
|
14029
14062
|
);
|
|
14030
14063
|
for (const r of results) {
|
|
14064
|
+
const keywordCandidate = {
|
|
14065
|
+
type: r.event.eventType,
|
|
14066
|
+
content: r.event.content,
|
|
14067
|
+
id: r.event.id,
|
|
14068
|
+
sessionId: r.event.sessionId,
|
|
14069
|
+
score: r.score,
|
|
14070
|
+
source: "keyword",
|
|
14071
|
+
fallback: usedFallbackFloor
|
|
14072
|
+
};
|
|
14031
14073
|
const existingIndex = existingById.get(r.event.id);
|
|
14032
14074
|
if (existingIndex !== void 0) {
|
|
14033
14075
|
const existing = mergedMemories[existingIndex];
|
|
@@ -14041,21 +14083,16 @@ async function main() {
|
|
|
14041
14083
|
}
|
|
14042
14084
|
continue;
|
|
14043
14085
|
}
|
|
14044
|
-
mergedMemories.push(
|
|
14045
|
-
type: r.event.eventType,
|
|
14046
|
-
content: r.event.content,
|
|
14047
|
-
id: r.event.id,
|
|
14048
|
-
sessionId: r.event.sessionId,
|
|
14049
|
-
score: r.score,
|
|
14050
|
-
source: "keyword",
|
|
14051
|
-
fallback: usedFallbackFloor
|
|
14052
|
-
});
|
|
14086
|
+
mergedMemories.push(keywordCandidate);
|
|
14053
14087
|
existingById.set(r.event.id, mergedMemories.length - 1);
|
|
14054
14088
|
if (mergedMemories.length >= MAX_CANDIDATES2 * 2) break;
|
|
14055
14089
|
}
|
|
14056
14090
|
}
|
|
14057
14091
|
const injectionPolicy = getHookInjectionPolicy();
|
|
14058
|
-
const
|
|
14092
|
+
const episodeSeedPool = Array.from(new Map(
|
|
14093
|
+
[...mergedMemories, ...episodeSeedCandidates].filter((candidate) => Boolean(candidate.id)).map((candidate) => [candidate.id, candidate])
|
|
14094
|
+
).values());
|
|
14095
|
+
const episodeSeeds = selectHookEpisodeSeeds(episodeSeedPool, injectionPolicy, retrievalQuery);
|
|
14059
14096
|
const episodeEvidence = await expandEpisodeEvidence(memoryService, episodeSeeds);
|
|
14060
14097
|
const injectableMemories = filterHookInjectableMemories(
|
|
14061
14098
|
[...mergedMemories, ...episodeEvidence],
|