@anuma/sdk 3.3.1 → 3.5.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.
@@ -3357,11 +3357,20 @@ async function getModel() {
3357
3357
  function sigmoid(x) {
3358
3358
  return 1 / (1 + Math.exp(-x));
3359
3359
  }
3360
+ function formatRerankDoc(content, dateMs) {
3361
+ if (dateMs === null || dateMs === void 0 || !Number.isFinite(dateMs)) return content;
3362
+ const d = new Date(dateMs);
3363
+ if (!Number.isFinite(d.getTime())) return content;
3364
+ const yyyy = d.getFullYear();
3365
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
3366
+ const dd = String(d.getDate()).padStart(2, "0");
3367
+ return `[Date: ${yyyy}-${mm}-${dd}] ${content}`;
3368
+ }
3360
3369
  async function rerankPairs(query, items) {
3361
3370
  if (items.length === 0 || !query) return [];
3362
3371
  const { tokenizer, model } = await getModel();
3363
3372
  const queries = items.map(() => query);
3364
- const docs = items.map((i) => i.content);
3373
+ const docs = items.map((i) => formatRerankDoc(i.content, i.dateMs));
3365
3374
  const tokenize2 = tokenizer;
3366
3375
  const inputs = tokenize2(queries, { text_pair: docs, padding: true, truncation: true });
3367
3376
  const forward = model;
@@ -3710,6 +3719,23 @@ function createVaultEmbeddingCache(maxSize = DEFAULT_VAULT_CACHE_SIZE) {
3710
3719
  function normalizeEventTimeKind(kind) {
3711
3720
  return kind === "point" || kind === "range" || kind === "ongoing" ? kind : null;
3712
3721
  }
3722
+ function rerankDateMs(item) {
3723
+ if (item.eventTimeStart !== null && item.eventTimeStart !== void 0 && Number.isFinite(item.eventTimeStart) && item.eventTimeStart > 0) {
3724
+ return item.eventTimeStart;
3725
+ }
3726
+ if (item.lastObservedAt !== null && item.lastObservedAt !== void 0 && Number.isFinite(item.lastObservedAt) && item.lastObservedAt > 0) {
3727
+ return item.lastObservedAt;
3728
+ }
3729
+ const updatedMs = item.updatedAt?.getTime();
3730
+ if (updatedMs !== null && updatedMs !== void 0 && Number.isFinite(updatedMs) && updatedMs > 0) {
3731
+ return updatedMs;
3732
+ }
3733
+ const createdMs = item.createdAt?.getTime();
3734
+ if (createdMs !== null && createdMs !== void 0 && Number.isFinite(createdMs) && createdMs > 0) {
3735
+ return createdMs;
3736
+ }
3737
+ return void 0;
3738
+ }
3713
3739
  var SUPERSESSION_SIMILARITY_THRESHOLD = 0.7;
3714
3740
  var SUPERSESSION_MIN_AGE_GAP_MS = 30 * 24 * 60 * 60 * 1e3;
3715
3741
  var SUPERSESSION_BOOST_FACTOR = 0.8;
@@ -3916,6 +3942,7 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
3916
3942
  }
3917
3943
  let combined;
3918
3944
  let tailSlice = [];
3945
+ const itemById = new Map(items.map((i) => [i.id, i]));
3919
3946
  if (options?.rerank) {
3920
3947
  const rerankTopN = options.rerankTopN ?? 30;
3921
3948
  const ceWeight = options.ceWeight ?? 0.1;
@@ -3923,7 +3950,12 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
3923
3950
  try {
3924
3951
  const reranked = await rerankPairs(
3925
3952
  query,
3926
- headSlice.map((r) => ({ id: r.uniqueId, content: r.content }))
3953
+ headSlice.map((r) => ({
3954
+ id: r.uniqueId,
3955
+ content: r.content,
3956
+ // C4: date-prefix the CE doc so temporal alignment influences rank.
3957
+ dateMs: rerankDateMs(itemById.get(r.uniqueId) ?? r)
3958
+ }))
3927
3959
  );
3928
3960
  tailSlice = v2Ranked.slice(rerankTopN);
3929
3961
  const v2ScoreById = new Map(headSlice.map((r) => [r.uniqueId, r.similarity]));
@@ -3958,11 +3990,11 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
3958
3990
  const fused = rrfFuse([headIds, headIds, ...sideLanesAsync], options?.rrfK);
3959
3991
  const fusedHead = combined.map((r) => ({ ...r, similarity: fused.get(r.uniqueId) ?? r.similarity })).sort((a, b) => b.similarity - a.similarity);
3960
3992
  const seen = new Set(combined.map((r) => r.uniqueId));
3961
- const itemById = new Map(items.map((i) => [i.id, i]));
3993
+ const itemById2 = new Map(items.map((i) => [i.id, i]));
3962
3994
  for (const lane of sideLanesAsync) {
3963
3995
  for (const id of lane) {
3964
3996
  if (seen.has(id)) continue;
3965
- const it = itemById.get(id);
3997
+ const it = itemById2.get(id);
3966
3998
  if (!it) continue;
3967
3999
  fusedHead.push({
3968
4000
  uniqueId: id,
@@ -3981,11 +4013,11 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
3981
4013
  if (options?.mmr) {
3982
4014
  const lambda = options.mmrLambda ?? 0.7;
3983
4015
  const mmrTopN = options.mmrTopN ?? 20;
3984
- const itemById = new Map(items.map((i) => [i.id, i]));
4016
+ const itemById2 = new Map(items.map((i) => [i.id, i]));
3985
4017
  const mmrCandidates = combined.slice(0, mmrTopN).map((r) => ({
3986
4018
  id: r.uniqueId,
3987
4019
  score: r.similarity,
3988
- embedding: itemById.get(r.uniqueId)?.embedding ?? [],
4020
+ embedding: itemById2.get(r.uniqueId)?.embedding ?? [],
3989
4021
  content: r.content
3990
4022
  }));
3991
4023
  const picked = applyMMR(mmrCandidates, limit, lambda);
@@ -4107,7 +4139,12 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
4107
4139
  try {
4108
4140
  const reranked = await rerankPairs(
4109
4141
  originalQuery,
4110
- headSlice.map((r) => ({ id: r.uniqueId, content: r.content }))
4142
+ headSlice.map((r) => ({
4143
+ id: r.uniqueId,
4144
+ content: r.content,
4145
+ // C4: date-prefix the CE doc so temporal alignment influences rank.
4146
+ dateMs: rerankDateMs(itemById.get(r.uniqueId) ?? r)
4147
+ }))
4111
4148
  );
4112
4149
  const ceById = new Map(reranked.map((r) => [r.id, r.score]));
4113
4150
  const head = headSlice.map((r) => ({
@@ -4294,6 +4331,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4294
4331
  createdAt: m.createdAt,
4295
4332
  updatedAt: m.updatedAt,
4296
4333
  proofCount: m.proofCount,
4334
+ lastObservedAt: m.lastObservedAt,
4297
4335
  eventTimeStart: m.eventTimeStart,
4298
4336
  sourceChunkIds: m.sourceChunkIds,
4299
4337
  eventTimeEnd: m.eventTimeEnd,
@@ -4310,13 +4348,27 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
4310
4348
  );
4311
4349
  }
4312
4350
  }
4313
- const timestampById = new Map(
4314
- memories.map((m) => [m.uniqueId, { createdAt: m.createdAt, updatedAt: m.updatedAt }])
4351
+ const metaById = new Map(
4352
+ memories.map((m) => [
4353
+ m.uniqueId,
4354
+ {
4355
+ createdAt: m.createdAt,
4356
+ updatedAt: m.updatedAt,
4357
+ proofCount: m.proofCount,
4358
+ lastObservedAt: m.lastObservedAt
4359
+ }
4360
+ ])
4315
4361
  );
4316
4362
  const stampTimestamps = (out) => {
4317
4363
  const results2 = out.results.map((r) => {
4318
- const ts = timestampById.get(r.uniqueId);
4319
- return ts ? { ...r, createdAt: ts.createdAt, updatedAt: ts.updatedAt } : r;
4364
+ const meta = metaById.get(r.uniqueId);
4365
+ return meta ? {
4366
+ ...r,
4367
+ createdAt: meta.createdAt,
4368
+ updatedAt: meta.updatedAt,
4369
+ proofCount: meta.proofCount,
4370
+ lastObservedAt: meta.lastObservedAt
4371
+ } : r;
4320
4372
  });
4321
4373
  return {
4322
4374
  results: results2,
@@ -4510,7 +4562,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
4510
4562
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
4511
4563
  );
4512
4564
  }
4513
- const { recall: recall2 } = await import("./recall-C3LJ5R2E.mjs");
4565
+ const { recall: recall2 } = await import("./recall-IO4KJ5W7.mjs");
4514
4566
  const result = await recall2(
4515
4567
  query,
4516
4568
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -4774,6 +4826,42 @@ Which should be expanded?`,
4774
4826
  };
4775
4827
  }
4776
4828
 
4829
+ // src/lib/memory/observationTrend.ts
4830
+ var DAY_MS2 = 1e3 * 60 * 60 * 24;
4831
+ var TREND_RECENT_WINDOW_DAYS = 30;
4832
+ var TREND_STALE_WINDOW_DAYS = 90;
4833
+ function toMs(value) {
4834
+ return value instanceof Date ? value.getTime() : value;
4835
+ }
4836
+ function classifyObservationTrend(input, now = Date.now()) {
4837
+ const createdMs = toMs(input.createdAt);
4838
+ if (!Number.isFinite(createdMs) || !Number.isFinite(now)) return "stable";
4839
+ const lastMsRaw = input.lastObservedAt;
4840
+ const lastMs = lastMsRaw !== null && lastMsRaw !== void 0 && Number.isFinite(lastMsRaw) ? Math.max(createdMs, lastMsRaw) : createdMs;
4841
+ const proofs = Math.max(1, input.proofCount ?? 1);
4842
+ const ageDays = Math.max(0, (now - createdMs) / DAY_MS2);
4843
+ const daysSinceLast = Math.max(0, (now - lastMs) / DAY_MS2);
4844
+ if (daysSinceLast >= TREND_STALE_WINDOW_DAYS) return "stale";
4845
+ if (daysSinceLast >= TREND_RECENT_WINDOW_DAYS) return "weakening";
4846
+ if (ageDays <= TREND_RECENT_WINDOW_DAYS && proofs <= 2) return "new";
4847
+ if (proofs >= 3) return "strengthening";
4848
+ if (proofs >= 2 && ageDays > TREND_RECENT_WINDOW_DAYS) return "strengthening";
4849
+ return "stable";
4850
+ }
4851
+ function summarizeObservationTrends(inputs, now = Date.now()) {
4852
+ const counts = {
4853
+ new: 0,
4854
+ strengthening: 0,
4855
+ stable: 0,
4856
+ weakening: 0,
4857
+ stale: 0
4858
+ };
4859
+ for (const input of inputs) {
4860
+ counts[classifyObservationTrend(input, now)]++;
4861
+ }
4862
+ return counts;
4863
+ }
4864
+
4777
4865
  // src/lib/memory/queryTemporal.ts
4778
4866
  var RELATIVE_DAY_ENTRIES = [
4779
4867
  [/\btoday\b/, "today", 0],
@@ -5168,7 +5256,7 @@ async function recall(query, ctx, options = {}) {
5168
5256
  const fuseStart = nowMs();
5169
5257
  if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
5170
5258
  const memories2 = [
5171
- ...factResults.map(toFactMemory),
5259
+ ...factResults.map((r) => toFactMemory(r, options.now)),
5172
5260
  ...chunkResults.map(toChunkMemory)
5173
5261
  ];
5174
5262
  memories2.sort((a, b) => b.score - a.score);
@@ -5189,7 +5277,7 @@ async function recall(query, ctx, options = {}) {
5189
5277
  const fused = rrfFuse([factRanking, chunkRanking], options.rrfK);
5190
5278
  const byId = /* @__PURE__ */ new Map();
5191
5279
  for (const r of factResults) {
5192
- const m = toFactMemory(r);
5280
+ const m = toFactMemory(r, options.now);
5193
5281
  m.score = fused.get(`fact:${r.uniqueId}`) ?? 0;
5194
5282
  if (!m.scoreBreakdown) m.scoreBreakdown = {};
5195
5283
  m.scoreBreakdown.fused = r.similarity;
@@ -5238,7 +5326,10 @@ async function recall(query, ctx, options = {}) {
5238
5326
  ...vaultSize !== void 0 && { vaultSize }
5239
5327
  };
5240
5328
  }
5241
- function toFactMemory(r) {
5329
+ function toFactMemory(r, now) {
5330
+ const createdAt = r.createdAt ?? /* @__PURE__ */ new Date(0);
5331
+ const proofCount = r.proofCount ?? void 0;
5332
+ const lastObservedAt = r.lastObservedAt ?? null;
5242
5333
  return {
5243
5334
  id: r.uniqueId,
5244
5335
  kind: "fact",
@@ -5254,6 +5345,18 @@ function toFactMemory(r) {
5254
5345
  // (and UI reading them) carry the fact's type. Undefined when untyped.
5255
5346
  ...r.factType !== void 0 && { factType: r.factType },
5256
5347
  ...r.sourceChunkIds !== void 0 && r.sourceChunkIds !== null && { sourceChunkIds: r.sourceChunkIds },
5348
+ ...proofCount !== void 0 && proofCount !== null && { proofCount },
5349
+ ...lastObservedAt !== null && { lastObservedAt },
5350
+ // C2 — algorithmic trend from evidence timestamps (no LLM). Honor
5351
+ // RecallOptions.now so back-dated eval harnesses get consistent labels.
5352
+ observationTrend: classifyObservationTrend(
5353
+ {
5354
+ createdAt,
5355
+ lastObservedAt,
5356
+ proofCount
5357
+ },
5358
+ now ?? Date.now()
5359
+ ),
5257
5360
  // r.similarity from searchVaultMemoriesWithSize is the fused score
5258
5361
  // (cosine + BM25 + RRF + recency + proof) when useFusion=true (the
5259
5362
  // default) and pure cosine when useFusion=false. The breakdown
@@ -5262,7 +5365,7 @@ function toFactMemory(r) {
5262
5365
  content: r.content,
5263
5366
  score: r.similarity,
5264
5367
  scoreBreakdown: { fused: r.similarity },
5265
- createdAt: r.createdAt ?? /* @__PURE__ */ new Date(0),
5368
+ createdAt,
5266
5369
  updatedAt: r.updatedAt ?? /* @__PURE__ */ new Date(0)
5267
5370
  };
5268
5371
  }
@@ -5430,6 +5533,10 @@ export {
5430
5533
  capHopsForDensity,
5431
5534
  traverseGraphLane,
5432
5535
  createLlmNeighborRefiner,
5536
+ TREND_RECENT_WINDOW_DAYS,
5537
+ TREND_STALE_WINDOW_DAYS,
5538
+ classifyObservationTrend,
5539
+ summarizeObservationTrends,
5433
5540
  recall,
5434
5541
  preEmbedVaultMemories,
5435
5542
  eagerEmbedContent,
@@ -4442,11 +4442,20 @@ async function getModel() {
4442
4442
  function sigmoid(x) {
4443
4443
  return 1 / (1 + Math.exp(-x));
4444
4444
  }
4445
+ function formatRerankDoc(content, dateMs) {
4446
+ if (dateMs === null || dateMs === void 0 || !Number.isFinite(dateMs)) return content;
4447
+ const d = new Date(dateMs);
4448
+ if (!Number.isFinite(d.getTime())) return content;
4449
+ const yyyy = d.getFullYear();
4450
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
4451
+ const dd = String(d.getDate()).padStart(2, "0");
4452
+ return `[Date: ${yyyy}-${mm}-${dd}] ${content}`;
4453
+ }
4445
4454
  async function rerankPairs(query, items) {
4446
4455
  if (items.length === 0 || !query) return [];
4447
4456
  const { tokenizer, model } = await getModel();
4448
4457
  const queries = items.map(() => query);
4449
- const docs = items.map((i) => i.content);
4458
+ const docs = items.map((i) => formatRerankDoc(i.content, i.dateMs));
4450
4459
  const tokenize2 = tokenizer;
4451
4460
  const inputs = tokenize2(queries, { text_pair: docs, padding: true, truncation: true });
4452
4461
  const forward = model;
@@ -5072,6 +5081,48 @@ var init_graphTraversal = __esm({
5072
5081
  }
5073
5082
  });
5074
5083
 
5084
+ // src/lib/memory/observationTrend.ts
5085
+ function toMs(value) {
5086
+ return value instanceof Date ? value.getTime() : value;
5087
+ }
5088
+ function classifyObservationTrend(input, now = Date.now()) {
5089
+ const createdMs = toMs(input.createdAt);
5090
+ if (!Number.isFinite(createdMs) || !Number.isFinite(now)) return "stable";
5091
+ const lastMsRaw = input.lastObservedAt;
5092
+ const lastMs = lastMsRaw !== null && lastMsRaw !== void 0 && Number.isFinite(lastMsRaw) ? Math.max(createdMs, lastMsRaw) : createdMs;
5093
+ const proofs = Math.max(1, input.proofCount ?? 1);
5094
+ const ageDays2 = Math.max(0, (now - createdMs) / DAY_MS2);
5095
+ const daysSinceLast = Math.max(0, (now - lastMs) / DAY_MS2);
5096
+ if (daysSinceLast >= TREND_STALE_WINDOW_DAYS) return "stale";
5097
+ if (daysSinceLast >= TREND_RECENT_WINDOW_DAYS) return "weakening";
5098
+ if (ageDays2 <= TREND_RECENT_WINDOW_DAYS && proofs <= 2) return "new";
5099
+ if (proofs >= 3) return "strengthening";
5100
+ if (proofs >= 2 && ageDays2 > TREND_RECENT_WINDOW_DAYS) return "strengthening";
5101
+ return "stable";
5102
+ }
5103
+ function summarizeObservationTrends(inputs, now = Date.now()) {
5104
+ const counts = {
5105
+ new: 0,
5106
+ strengthening: 0,
5107
+ stable: 0,
5108
+ weakening: 0,
5109
+ stale: 0
5110
+ };
5111
+ for (const input of inputs) {
5112
+ counts[classifyObservationTrend(input, now)]++;
5113
+ }
5114
+ return counts;
5115
+ }
5116
+ var DAY_MS2, TREND_RECENT_WINDOW_DAYS, TREND_STALE_WINDOW_DAYS;
5117
+ var init_observationTrend = __esm({
5118
+ "src/lib/memory/observationTrend.ts"() {
5119
+ "use strict";
5120
+ DAY_MS2 = 1e3 * 60 * 60 * 24;
5121
+ TREND_RECENT_WINDOW_DAYS = 30;
5122
+ TREND_STALE_WINDOW_DAYS = 90;
5123
+ }
5124
+ });
5125
+
5075
5126
  // src/lib/memory/queryTemporal.ts
5076
5127
  function startOfDay(ts) {
5077
5128
  const d = new Date(ts);
@@ -5471,7 +5522,7 @@ async function recall(query, ctx, options = {}) {
5471
5522
  const fuseStart = nowMs();
5472
5523
  if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
5473
5524
  const memories2 = [
5474
- ...factResults.map(toFactMemory),
5525
+ ...factResults.map((r) => toFactMemory(r, options.now)),
5475
5526
  ...chunkResults.map(toChunkMemory)
5476
5527
  ];
5477
5528
  memories2.sort((a, b) => b.score - a.score);
@@ -5492,7 +5543,7 @@ async function recall(query, ctx, options = {}) {
5492
5543
  const fused = rrfFuse([factRanking, chunkRanking], options.rrfK);
5493
5544
  const byId = /* @__PURE__ */ new Map();
5494
5545
  for (const r of factResults) {
5495
- const m = toFactMemory(r);
5546
+ const m = toFactMemory(r, options.now);
5496
5547
  m.score = fused.get(`fact:${r.uniqueId}`) ?? 0;
5497
5548
  if (!m.scoreBreakdown) m.scoreBreakdown = {};
5498
5549
  m.scoreBreakdown.fused = r.similarity;
@@ -5541,7 +5592,10 @@ async function recall(query, ctx, options = {}) {
5541
5592
  ...vaultSize !== void 0 && { vaultSize }
5542
5593
  };
5543
5594
  }
5544
- function toFactMemory(r) {
5595
+ function toFactMemory(r, now) {
5596
+ const createdAt = r.createdAt ?? /* @__PURE__ */ new Date(0);
5597
+ const proofCount = r.proofCount ?? void 0;
5598
+ const lastObservedAt = r.lastObservedAt ?? null;
5545
5599
  return {
5546
5600
  id: r.uniqueId,
5547
5601
  kind: "fact",
@@ -5557,6 +5611,18 @@ function toFactMemory(r) {
5557
5611
  // (and UI reading them) carry the fact's type. Undefined when untyped.
5558
5612
  ...r.factType !== void 0 && { factType: r.factType },
5559
5613
  ...r.sourceChunkIds !== void 0 && r.sourceChunkIds !== null && { sourceChunkIds: r.sourceChunkIds },
5614
+ ...proofCount !== void 0 && proofCount !== null && { proofCount },
5615
+ ...lastObservedAt !== null && { lastObservedAt },
5616
+ // C2 — algorithmic trend from evidence timestamps (no LLM). Honor
5617
+ // RecallOptions.now so back-dated eval harnesses get consistent labels.
5618
+ observationTrend: classifyObservationTrend(
5619
+ {
5620
+ createdAt,
5621
+ lastObservedAt,
5622
+ proofCount
5623
+ },
5624
+ now ?? Date.now()
5625
+ ),
5560
5626
  // r.similarity from searchVaultMemoriesWithSize is the fused score
5561
5627
  // (cosine + BM25 + RRF + recency + proof) when useFusion=true (the
5562
5628
  // default) and pure cosine when useFusion=false. The breakdown
@@ -5565,7 +5631,7 @@ function toFactMemory(r) {
5565
5631
  content: r.content,
5566
5632
  score: r.similarity,
5567
5633
  scoreBreakdown: { fused: r.similarity },
5568
- createdAt: r.createdAt ?? /* @__PURE__ */ new Date(0),
5634
+ createdAt,
5569
5635
  updatedAt: r.updatedAt ?? /* @__PURE__ */ new Date(0)
5570
5636
  };
5571
5637
  }
@@ -5648,6 +5714,7 @@ var init_recall = __esm({
5648
5714
  init_embeddings();
5649
5715
  init_searchTool();
5650
5716
  init_graphTraversal();
5717
+ init_observationTrend();
5651
5718
  init_queryEntities();
5652
5719
  init_queryTemporal();
5653
5720
  init_rrf();
@@ -5663,6 +5730,23 @@ var init_recall = __esm({
5663
5730
  function normalizeEventTimeKind(kind) {
5664
5731
  return kind === "point" || kind === "range" || kind === "ongoing" ? kind : null;
5665
5732
  }
5733
+ function rerankDateMs(item) {
5734
+ if (item.eventTimeStart !== null && item.eventTimeStart !== void 0 && Number.isFinite(item.eventTimeStart) && item.eventTimeStart > 0) {
5735
+ return item.eventTimeStart;
5736
+ }
5737
+ if (item.lastObservedAt !== null && item.lastObservedAt !== void 0 && Number.isFinite(item.lastObservedAt) && item.lastObservedAt > 0) {
5738
+ return item.lastObservedAt;
5739
+ }
5740
+ const updatedMs = item.updatedAt?.getTime();
5741
+ if (updatedMs !== null && updatedMs !== void 0 && Number.isFinite(updatedMs) && updatedMs > 0) {
5742
+ return updatedMs;
5743
+ }
5744
+ const createdMs = item.createdAt?.getTime();
5745
+ if (createdMs !== null && createdMs !== void 0 && Number.isFinite(createdMs) && createdMs > 0) {
5746
+ return createdMs;
5747
+ }
5748
+ return void 0;
5749
+ }
5666
5750
  function supersessionDelta(olderScore, newerScore, boostFactor = SUPERSESSION_BOOST_FACTOR) {
5667
5751
  const gap = olderScore - newerScore;
5668
5752
  return gap * boostFactor;
@@ -5865,6 +5949,7 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
5865
5949
  }
5866
5950
  let combined;
5867
5951
  let tailSlice = [];
5952
+ const itemById = new Map(items.map((i) => [i.id, i]));
5868
5953
  if (options?.rerank) {
5869
5954
  const rerankTopN = options.rerankTopN ?? 30;
5870
5955
  const ceWeight = options.ceWeight ?? 0.1;
@@ -5872,7 +5957,12 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
5872
5957
  try {
5873
5958
  const reranked = await rerankPairs(
5874
5959
  query,
5875
- headSlice.map((r) => ({ id: r.uniqueId, content: r.content }))
5960
+ headSlice.map((r) => ({
5961
+ id: r.uniqueId,
5962
+ content: r.content,
5963
+ // C4: date-prefix the CE doc so temporal alignment influences rank.
5964
+ dateMs: rerankDateMs(itemById.get(r.uniqueId) ?? r)
5965
+ }))
5876
5966
  );
5877
5967
  tailSlice = v2Ranked.slice(rerankTopN);
5878
5968
  const v2ScoreById = new Map(headSlice.map((r) => [r.uniqueId, r.similarity]));
@@ -5907,11 +5997,11 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
5907
5997
  const fused = rrfFuse([headIds, headIds, ...sideLanesAsync], options?.rrfK);
5908
5998
  const fusedHead = combined.map((r) => ({ ...r, similarity: fused.get(r.uniqueId) ?? r.similarity })).sort((a, b) => b.similarity - a.similarity);
5909
5999
  const seen = new Set(combined.map((r) => r.uniqueId));
5910
- const itemById = new Map(items.map((i) => [i.id, i]));
6000
+ const itemById2 = new Map(items.map((i) => [i.id, i]));
5911
6001
  for (const lane of sideLanesAsync) {
5912
6002
  for (const id of lane) {
5913
6003
  if (seen.has(id)) continue;
5914
- const it = itemById.get(id);
6004
+ const it = itemById2.get(id);
5915
6005
  if (!it) continue;
5916
6006
  fusedHead.push({
5917
6007
  uniqueId: id,
@@ -5930,11 +6020,11 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
5930
6020
  if (options?.mmr) {
5931
6021
  const lambda = options.mmrLambda ?? 0.7;
5932
6022
  const mmrTopN = options.mmrTopN ?? 20;
5933
- const itemById = new Map(items.map((i) => [i.id, i]));
6023
+ const itemById2 = new Map(items.map((i) => [i.id, i]));
5934
6024
  const mmrCandidates = combined.slice(0, mmrTopN).map((r) => ({
5935
6025
  id: r.uniqueId,
5936
6026
  score: r.similarity,
5937
- embedding: itemById.get(r.uniqueId)?.embedding ?? [],
6027
+ embedding: itemById2.get(r.uniqueId)?.embedding ?? [],
5938
6028
  content: r.content
5939
6029
  }));
5940
6030
  const picked = applyMMR(mmrCandidates, limit, lambda);
@@ -6056,7 +6146,12 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
6056
6146
  try {
6057
6147
  const reranked = await rerankPairs(
6058
6148
  originalQuery,
6059
- headSlice.map((r) => ({ id: r.uniqueId, content: r.content }))
6149
+ headSlice.map((r) => ({
6150
+ id: r.uniqueId,
6151
+ content: r.content,
6152
+ // C4: date-prefix the CE doc so temporal alignment influences rank.
6153
+ dateMs: rerankDateMs(itemById.get(r.uniqueId) ?? r)
6154
+ }))
6060
6155
  );
6061
6156
  const ceById = new Map(reranked.map((r) => [r.id, r.score]));
6062
6157
  const head = headSlice.map((r) => ({
@@ -6243,6 +6338,7 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6243
6338
  createdAt: m.createdAt,
6244
6339
  updatedAt: m.updatedAt,
6245
6340
  proofCount: m.proofCount,
6341
+ lastObservedAt: m.lastObservedAt,
6246
6342
  eventTimeStart: m.eventTimeStart,
6247
6343
  sourceChunkIds: m.sourceChunkIds,
6248
6344
  eventTimeEnd: m.eventTimeEnd,
@@ -6259,13 +6355,27 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
6259
6355
  );
6260
6356
  }
6261
6357
  }
6262
- const timestampById = new Map(
6263
- memories.map((m) => [m.uniqueId, { createdAt: m.createdAt, updatedAt: m.updatedAt }])
6358
+ const metaById = new Map(
6359
+ memories.map((m) => [
6360
+ m.uniqueId,
6361
+ {
6362
+ createdAt: m.createdAt,
6363
+ updatedAt: m.updatedAt,
6364
+ proofCount: m.proofCount,
6365
+ lastObservedAt: m.lastObservedAt
6366
+ }
6367
+ ])
6264
6368
  );
6265
6369
  const stampTimestamps = (out) => {
6266
6370
  const results2 = out.results.map((r) => {
6267
- const ts = timestampById.get(r.uniqueId);
6268
- return ts ? { ...r, createdAt: ts.createdAt, updatedAt: ts.updatedAt } : r;
6371
+ const meta = metaById.get(r.uniqueId);
6372
+ return meta ? {
6373
+ ...r,
6374
+ createdAt: meta.createdAt,
6375
+ updatedAt: meta.updatedAt,
6376
+ proofCount: meta.proofCount,
6377
+ lastObservedAt: meta.lastObservedAt
6378
+ } : r;
6269
6379
  });
6270
6380
  return {
6271
6381
  results: results2,
@@ -6707,6 +6817,8 @@ __export(index_exports, {
6707
6817
  StreamExpiredError: () => StreamExpiredError,
6708
6818
  TOPICS_EXTRACTION_VERSION: () => TOPICS_EXTRACTION_VERSION,
6709
6819
  TOPIC_EXTRACTION_BATCH_SIZE: () => TOPIC_EXTRACTION_BATCH_SIZE,
6820
+ TREND_RECENT_WINDOW_DAYS: () => TREND_RECENT_WINDOW_DAYS,
6821
+ TREND_STALE_WINDOW_DAYS: () => TREND_STALE_WINDOW_DAYS,
6710
6822
  VAULT_SIZE_HOP_CAP: () => VAULT_SIZE_HOP_CAP,
6711
6823
  WalletPoller: () => WalletPoller,
6712
6824
  addConversationMemoriesOp: () => addConversationMemoriesOp,
@@ -6722,6 +6834,7 @@ __export(index_exports, {
6722
6834
  classifyCryptoPriceBatch: () => classifyCryptoPriceBatch,
6723
6835
  classifyDecay: () => classifyDecay,
6724
6836
  classifyInjectionCandidates: () => classifyInjectionCandidates,
6837
+ classifyObservationTrend: () => classifyObservationTrend,
6725
6838
  classifyStockPrice: () => classifyStockPrice,
6726
6839
  classifyStockPriceBatch: () => classifyStockPriceBatch,
6727
6840
  classifyWeather: () => classifyWeather,
@@ -6821,6 +6934,7 @@ __export(index_exports, {
6821
6934
  stampTopicsExtractedAtOp: () => stampTopicsExtractedAtOp,
6822
6935
  streamCancelPath: () => streamCancelPath,
6823
6936
  streamReplayPath: () => streamReplayPath,
6937
+ summarizeObservationTrends: () => summarizeObservationTrends,
6824
6938
  supersedeVaultMemoryOp: () => supersedeVaultMemoryOp,
6825
6939
  synthesizeProfile: () => synthesizeProfile,
6826
6940
  traverseGraphLane: () => traverseGraphLane,
@@ -14703,12 +14817,12 @@ function createChunkVectorCache(maxSize = DEFAULT_CHUNK_CACHE_SIZE) {
14703
14817
 
14704
14818
  // src/lib/memory/decay.ts
14705
14819
  init_logger();
14706
- var DAY_MS2 = 24 * 60 * 60 * 1e3;
14707
- var SHORT_TTL_MS = 30 * DAY_MS2;
14708
- var MEDIUM_TTL_MS = 180 * DAY_MS2;
14820
+ var DAY_MS3 = 24 * 60 * 60 * 1e3;
14821
+ var SHORT_TTL_MS = 30 * DAY_MS3;
14822
+ var MEDIUM_TTL_MS = 180 * DAY_MS3;
14709
14823
  var NEVER_TTL_MS = Number.POSITIVE_INFINITY;
14710
- var HARD_DELETE_WINDOW_MS = 30 * DAY_MS2;
14711
- var PAST_EVENT_GRACE_MS = 7 * DAY_MS2;
14824
+ var HARD_DELETE_WINDOW_MS = 30 * DAY_MS3;
14825
+ var PAST_EVENT_GRACE_MS = 7 * DAY_MS3;
14712
14826
  var DEFAULT_DECAY_POLICY = {
14713
14827
  ttlByType: {
14714
14828
  identity: NEVER_TTL_MS,
@@ -14968,6 +15082,7 @@ function createDecaySweeper(options) {
14968
15082
 
14969
15083
  // src/lib/memory/index.ts
14970
15084
  init_graphTraversal();
15085
+ init_observationTrend();
14971
15086
  init_recall();
14972
15087
 
14973
15088
  // src/lib/memory/recallTool.ts
@@ -15334,6 +15449,7 @@ init_logger();
15334
15449
  init_constants();
15335
15450
  init_embeddings();
15336
15451
  init_vector();
15452
+ init_observationTrend();
15337
15453
  var DEFAULT_SYNTHESIS_MODEL = "inclusionai/ling-2.6-flash";
15338
15454
  var DEFAULT_FACET_RECALL_LIMIT = 20;
15339
15455
  var DEFAULT_FACET_MAX_TOKENS = 512;
@@ -15419,6 +15535,13 @@ async function synthesizeProfile(ctx, options = {}) {
15419
15535
  includeSuperseded: true
15420
15536
  });
15421
15537
  const watermark = computeVaultWatermark(memories);
15538
+ const observationTrends = summarizeObservationTrends(
15539
+ memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
15540
+ createdAt: m.createdAt,
15541
+ lastObservedAt: m.lastObservedAt,
15542
+ proofCount: m.proofCount
15543
+ }))
15544
+ );
15422
15545
  const presentIds = new Set(memories.map((m) => m.uniqueId));
15423
15546
  const hasStaleSections = previous?.sections.some((s) => s.stale);
15424
15547
  const citesMissingFact = previous?.sections.some(
@@ -15426,10 +15549,14 @@ async function synthesizeProfile(ctx, options = {}) {
15426
15549
  );
15427
15550
  if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
15428
15551
  const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
15429
- if (sameOrder) return previous;
15552
+ if (sameOrder) {
15553
+ if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
15554
+ return { ...previous, observationTrends };
15555
+ }
15430
15556
  return {
15431
15557
  ...previous,
15432
- sections: facets.map((f) => previous.sections.find((s) => s.key === f.key))
15558
+ sections: facets.map((f) => previous.sections.find((s) => s.key === f.key)),
15559
+ observationTrends
15433
15560
  };
15434
15561
  }
15435
15562
  const staleKeys = await computeStaleFacetKeys(ctx, memories, facets, previous, watermark);
@@ -15462,6 +15589,7 @@ async function synthesizeProfile(ctx, options = {}) {
15462
15589
  sections,
15463
15590
  vaultWatermark: watermark,
15464
15591
  config,
15592
+ observationTrends,
15465
15593
  generatedAt: Date.now()
15466
15594
  };
15467
15595
  }
@@ -15472,6 +15600,10 @@ function configMatches(a, b) {
15472
15600
  function facetsSignature(facets) {
15473
15601
  return facets.map((f) => JSON.stringify([f.key, f.label, f.query, f.guidance])).sort().join("\n");
15474
15602
  }
15603
+ function trendsEqual(a, b) {
15604
+ if (!a) return false;
15605
+ return a.new === b.new && a.strengthening === b.strengthening && a.stable === b.stable && a.weakening === b.weakening && a.stale === b.stale;
15606
+ }
15475
15607
  function changeTime(m) {
15476
15608
  return Math.max(m.updatedAt.getTime(), m.supersededAt ?? 0, m.lastObservedAt ?? 0);
15477
15609
  }
@@ -21226,6 +21358,8 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
21226
21358
  StreamExpiredError,
21227
21359
  TOPICS_EXTRACTION_VERSION,
21228
21360
  TOPIC_EXTRACTION_BATCH_SIZE,
21361
+ TREND_RECENT_WINDOW_DAYS,
21362
+ TREND_STALE_WINDOW_DAYS,
21229
21363
  VAULT_SIZE_HOP_CAP,
21230
21364
  WalletPoller,
21231
21365
  addConversationMemoriesOp,
@@ -21241,6 +21375,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
21241
21375
  classifyCryptoPriceBatch,
21242
21376
  classifyDecay,
21243
21377
  classifyInjectionCandidates,
21378
+ classifyObservationTrend,
21244
21379
  classifyStockPrice,
21245
21380
  classifyStockPriceBatch,
21246
21381
  classifyWeather,
@@ -21340,6 +21475,7 @@ function createNotionTools(getAccessToken, requestNotionAccess) {
21340
21475
  stampTopicsExtractedAtOp,
21341
21476
  streamCancelPath,
21342
21477
  streamReplayPath,
21478
+ summarizeObservationTrends,
21343
21479
  supersedeVaultMemoryOp,
21344
21480
  synthesizeProfile,
21345
21481
  traverseGraphLane,