claude-memory-layer 1.0.46 → 1.0.47

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/README.md CHANGED
@@ -312,6 +312,7 @@ MCP client가 환경에 따라 PATH를 못 찾으면 `command -v claude-memory-l
312
312
  ### Advanced Features
313
313
 
314
314
  - **Citations System**: 검색 결과를 `[mem:abc123]` 형태로 추적하고 `source`/`mem-source-ref`로 근거 확인
315
+ - **Source Neighbor Expansion**: `mem-source-ref(includeNeighbors=true, neighborWindow=1..5)`로 MemPalace식 hit 주변 세션 이벤트를 privacy-safe preview로 함께 확인
315
316
  - **Progressive Disclosure**: index → timeline → details 순서로 필요한 만큼만 확장해 토큰 비용 절감
316
317
  - **Codex/Hermes Importers**: read-only validate/replay 후 explicit import로만 mutation 수행
317
318
  - **Perspective Query Agent**: 관점별 observation + raw memory를 읽기 전용으로 조합하고 source refs를 유지
@@ -681,7 +682,7 @@ node dist/mcp/index.js
681
682
  | Context | `mem-context-pack` | 작업 시작용 relevant memory + recent timeline + follow-up refs |
682
683
  | Context | `mem-import-latest` | 최신 Claude/Codex/Hermes 세션을 bounded import 후 context-pack freshness 확보 |
683
684
  | Context | `mem-project-timeline` | 최근 프로젝트 메모리를 session/source/count/safe-preview로 요약 |
684
- | Context | `mem-source-ref` | `mem:`/`event:` ref를 redacted preview와 safe metadata로 해석 |
685
+ | Context | `mem-source-ref` | `mem:`/`event:` ref를 redacted preview와 safe metadata로 해석; `includeNeighbors` + `neighborWindow`로 같은 세션의 전후 이벤트를 bounded/privacy-safe preview로 확장 |
685
686
  | Operations | `mem-facet-query` / `mem-facet-tag` | project-scoped facet 조회/태깅 |
686
687
  | Operations | `mem-action-list` / `mem-action-update` | 다음 작업/action 상태 조회·갱신 |
687
688
  | Operations | `mem-frontier` | blocked/next action frontier와 safe resume hints |
@@ -703,7 +704,7 @@ node dist/mcp/index.js
703
704
  ```text
704
705
  1. 새 작업 시작: mem-context-pack(projectPath, query)
705
706
  2. 최근 흐름 확인: mem-project-timeline(projectPath)
706
- 3. 근거가 더 필요할 때: mem-source-ref(projectPath, ids=["mem:abc123"])
707
+ 3. 근거가 더 필요할 때: mem-source-ref(projectPath, ids=["mem:abc123"], includeNeighbors=true, neighborWindow=1)
707
708
  ```
708
709
 
709
710
  이 workflow는 Hermes/Codex/Claude Code가 같은 project-scoped memory backend를 공유할 때 특히 유용합니다. `mem-source-ref`는 raw transcript를 그대로 덤프하지 않고 allowlisted metadata와 privacy-filtered preview만 반환합니다.
package/dist/cli/index.js CHANGED
@@ -2574,6 +2574,7 @@ var VectorOutbox = class {
2574
2574
  // src/core/retrieval-debug-lanes.ts
2575
2575
  var RETRIEVAL_DEBUG_LANE_NAMES = [
2576
2576
  "raw_event",
2577
+ "session_event",
2577
2578
  "session_summary",
2578
2579
  "graph_path",
2579
2580
  "facet_match"
@@ -10310,6 +10311,7 @@ var Retriever = class {
10310
10311
  }
10311
10312
  async retrieve(query, options = {}) {
10312
10313
  const opts = { ...DEFAULT_OPTIONS, ...options };
10314
+ const retrievalMode = options.retrievalMode ?? ((options.strategy ?? DEFAULT_OPTIONS.strategy) === "auto" ? "session-event-hybrid" : "event");
10313
10315
  const sessionFilter = opts.scope?.sessionId ?? opts.sessionId;
10314
10316
  const fallbackTrace = [];
10315
10317
  const qualityQuery = buildRetrievalQualityQuery(query);
@@ -10340,6 +10342,7 @@ var Retriever = class {
10340
10342
  decayPolicy: opts.decayPolicy,
10341
10343
  intentRewrite: opts.intentRewrite === true,
10342
10344
  graphHop: opts.graphHop,
10345
+ retrievalMode,
10343
10346
  projectScopeMode: opts.projectScopeMode,
10344
10347
  projectHash: opts.projectHash,
10345
10348
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -10358,6 +10361,7 @@ var Retriever = class {
10358
10361
  rerankWeights: opts.rerankWeights,
10359
10362
  decayPolicy: opts.decayPolicy,
10360
10363
  graphHop: opts.graphHop,
10364
+ retrievalMode,
10361
10365
  projectScopeMode: opts.projectScopeMode,
10362
10366
  projectHash: opts.projectHash,
10363
10367
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -10377,6 +10381,7 @@ var Retriever = class {
10377
10381
  rerankWeights: opts.rerankWeights,
10378
10382
  decayPolicy: opts.decayPolicy,
10379
10383
  graphHop: opts.graphHop,
10384
+ retrievalMode,
10380
10385
  projectScopeMode: opts.projectScopeMode,
10381
10386
  projectHash: opts.projectHash,
10382
10387
  allowedProjectHashes: opts.allowedProjectHashes,
@@ -10397,10 +10402,26 @@ var Retriever = class {
10397
10402
  query,
10398
10403
  minScore: opts.minScore
10399
10404
  });
10405
+ const expandedSummary = retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(filteredSummary, {
10406
+ query: qualityQuery,
10407
+ currentStateQuery: query,
10408
+ limit: opts.topK * 4
10409
+ }) : filteredSummary;
10410
+ const scopedExpandedSummary = retrievalMode === "session-event-hybrid" ? await this.applyScopeFilters(expandedSummary, {
10411
+ scope: opts.scope,
10412
+ projectScopeMode: opts.projectScopeMode,
10413
+ projectHash: opts.projectHash,
10414
+ allowedProjectHashes: opts.allowedProjectHashes,
10415
+ facets: opts.facets
10416
+ }) : expandedSummary;
10417
+ const finalSummary = retrievalMode === "session-event-hybrid" ? this.applyQualityFilters(scopedExpandedSummary, {
10418
+ query,
10419
+ minScore: opts.minScore
10420
+ }) : scopedExpandedSummary;
10400
10421
  current = {
10401
- results: filteredSummary,
10402
- candidateResults: filteredSummary,
10403
- matchResult: this.matcher.matchSearchResults(filteredSummary, () => 0)
10422
+ results: finalSummary,
10423
+ candidateResults: finalSummary,
10424
+ matchResult: this.matcher.matchSearchResults(finalSummary, () => 0)
10404
10425
  };
10405
10426
  fallbackTrace.push("fallback:summary");
10406
10427
  }
@@ -10486,13 +10507,18 @@ var Retriever = class {
10486
10507
  initialResults = this.mergeResults(initialResults, rewrittenResults, input.topK * 3);
10487
10508
  }
10488
10509
  }
10489
- const expandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
10510
+ const graphExpandedResults = input.graphHop?.enabled === false ? initialResults : await this.expandGraphHops(initialResults, {
10490
10511
  query,
10491
10512
  queryGraphEnabled: this.queryGraphExpansionEnabled,
10492
10513
  maxHops: clampGraphHops(input.graphHop?.maxHops ?? 1),
10493
10514
  hopPenalty: Math.max(0, input.graphHop?.hopPenalty ?? 0.08),
10494
10515
  limit: input.topK * 4
10495
10516
  });
10517
+ const expandedResults = input.retrievalMode === "session-event-hybrid" ? await this.expandSessionEventHybrid(graphExpandedResults, {
10518
+ query: rerankQuery,
10519
+ currentStateQuery: query,
10520
+ limit: input.topK * 4
10521
+ }) : graphExpandedResults;
10496
10522
  const rerankedResults = input.rerankWithKeyword ? this.rerankByKeywordOverlap(expandedResults, rerankQuery, input.rerankWeights, input.decayPolicy) : expandedResults;
10497
10523
  const filtered = await this.applyScopeFilters(rerankedResults, {
10498
10524
  scope: input.scope,
@@ -10540,6 +10566,47 @@ var Retriever = class {
10540
10566
  }
10541
10567
  return [...byId.values()].sort((a, b) => b.score - a.score).slice(0, limit);
10542
10568
  }
10569
+ async expandSessionEventHybrid(seeds, opts) {
10570
+ if (seeds.length === 0 || opts.limit <= seeds.length) return seeds;
10571
+ const queryTokens = this.tokenize(opts.query);
10572
+ if (queryTokens.length === 0) return seeds;
10573
+ const byId = /* @__PURE__ */ new Map();
10574
+ for (const seed of seeds) byId.set(seed.eventId, seed);
10575
+ const bestSeedBySession = /* @__PURE__ */ new Map();
10576
+ for (const seed of [...seeds].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId))) {
10577
+ if (!seed.sessionId || bestSeedBySession.has(seed.sessionId)) continue;
10578
+ bestSeedBySession.set(seed.sessionId, seed);
10579
+ }
10580
+ const suppressStaleState = isCurrentStateQuery(opts.currentStateQuery);
10581
+ for (const [sessionId, seed] of bestSeedBySession) {
10582
+ const sessionEvents = await this.eventStore.getSessionEvents(sessionId);
10583
+ for (const event of [...sessionEvents].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())) {
10584
+ if (byId.has(event.id)) continue;
10585
+ if (isLowSignalContextContent(event.content)) continue;
10586
+ if (suppressStaleState && isStaleOrSupersededContent(event.content)) continue;
10587
+ const lexicalScore = this.keywordOverlap(queryTokens, this.tokenize(event.content));
10588
+ if (lexicalScore <= 0) continue;
10589
+ if (shouldApplyTechnicalGuard(opts.query) && !hasTechnicalTermOverlap(opts.query, event.content)) continue;
10590
+ const score = Math.min(0.95, Math.max(0.35, seed.score * 0.72 + lexicalScore * 0.28));
10591
+ const row = withRetrievalLane({
10592
+ id: `session-event-${seed.eventId}-${event.id}`,
10593
+ eventId: event.id,
10594
+ content: event.content,
10595
+ score,
10596
+ sessionId: event.sessionId,
10597
+ eventType: event.eventType,
10598
+ timestamp: event.timestamp.toISOString(),
10599
+ semanticScore: seed.semanticScore ?? seed.score,
10600
+ lexicalScore,
10601
+ recencyScore: seed.recencyScore
10602
+ }, { lane: "session_event", reason: `same_session:${seed.eventId}`, score });
10603
+ byId.set(row.eventId, row);
10604
+ if (byId.size >= opts.limit) break;
10605
+ }
10606
+ if (byId.size >= opts.limit) break;
10607
+ }
10608
+ return [...byId.values()].sort((a, b) => b.score - a.score || compareStable(a.eventId, b.eventId)).slice(0, opts.limit);
10609
+ }
10543
10610
  async expandGraphHops(seeds, opts) {
10544
10611
  const byId = /* @__PURE__ */ new Map();
10545
10612
  for (const s of seeds) byId.set(s.eventId, s);
@@ -21037,7 +21104,7 @@ async function runOperationCli(action, label) {
21037
21104
  }
21038
21105
  }
21039
21106
  var program = new Command();
21040
- program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.46");
21107
+ program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.47");
21041
21108
  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) => {
21042
21109
  try {
21043
21110
  await runMarketContextCommand(options);