@anuma/sdk 6.29.1 → 6.30.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.
@@ -18674,6 +18674,11 @@ function formatEventTime(start, end, kind) {
18674
18674
  if (kind === "ongoing") return `, event: since ${startDate}`;
18675
18675
  return `, event: ${startDate}`;
18676
18676
  }
18677
+ function formatSavedTime(createdAt) {
18678
+ const ms = createdAt?.getTime();
18679
+ if (ms === void 0 || !Number.isFinite(ms) || ms <= 0) return "";
18680
+ return `, saved: ${new Date(ms).toISOString().slice(0, 10)}`;
18681
+ }
18677
18682
  function generateFenceNonce() {
18678
18683
  const bytes = new Uint8Array(9);
18679
18684
  crypto.getRandomValues(bytes);
@@ -18692,8 +18697,9 @@ function formatRecallResult(memories) {
18692
18697
  const lines = memories.map((m, i) => {
18693
18698
  let body;
18694
18699
  if (m.kind === "fact") {
18700
+ const savedSuffix = formatSavedTime(m.createdAt);
18695
18701
  const eventSuffix = formatEventTime(m.eventTimeStart, m.eventTimeEnd, m.eventTimeKind);
18696
- body = `[${i + 1}] fact (id: ${m.id}${eventSuffix})
18702
+ body = `[${i + 1}] fact (id: ${m.id}${savedSuffix}${eventSuffix})
18697
18703
  ${m.content}`;
18698
18704
  } else {
18699
18705
  const date10 = m.createdAt.toISOString().slice(0, 10);
@@ -18743,6 +18749,40 @@ function isDumpQuery(query) {
18743
18749
  if (EVERYTHING_YOU_KNOW_RE.test(q)) return true;
18744
18750
  return false;
18745
18751
  }
18752
+ async function listRecentFacts(ctx, limit, toolOptions) {
18753
+ const vaultCtx = ctx.vaultCtx;
18754
+ if (!vaultCtx) return [];
18755
+ const ordered = await getVaultRankingProjectionsOp(vaultCtx, {
18756
+ ...toolOptions?.scopes && { scopes: toolOptions.scopes },
18757
+ ...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
18758
+ ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
18759
+ });
18760
+ const readable = [];
18761
+ for (let i = 0; i < ordered.length && readable.length < limit; i += limit) {
18762
+ const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
18763
+ const byId = new Map(
18764
+ (await getVaultMemoriesByIdsOp(vaultCtx, batch)).map((m) => [m.uniqueId, m])
18765
+ );
18766
+ for (const id of batch) {
18767
+ const m = byId.get(id);
18768
+ if (m && !isEncrypted(m.content)) readable.push(m);
18769
+ if (readable.length >= limit) break;
18770
+ }
18771
+ }
18772
+ return readable.map((m) => ({
18773
+ id: m.uniqueId,
18774
+ kind: "fact",
18775
+ content: m.content,
18776
+ score: 0,
18777
+ folderId: m.folderId,
18778
+ eventTimeStart: m.eventTimeStart,
18779
+ eventTimeEnd: m.eventTimeEnd,
18780
+ eventTimeKind: m.eventTimeKind,
18781
+ factType: m.factType,
18782
+ createdAt: m.createdAt,
18783
+ updatedAt: m.updatedAt
18784
+ }));
18785
+ }
18746
18786
  function createRecallTool(ctx, toolOptions, callbacks) {
18747
18787
  const defaultTypes = toolOptions?.types ?? ["fact", "chunk"];
18748
18788
  const defaultLimit = toolOptions?.limit ?? DEFAULT_LIMIT2;
@@ -18755,7 +18795,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
18755
18795
  type: "function",
18756
18796
  function: {
18757
18797
  name: RECALL_TOOL_NAME,
18758
- description: `Search the user's memory across stored facts/preferences and past conversation excerpts. Returns a unified ranked list \u2014 facts carry an \`id\` you can reference; conversation excerpts carry a date and role. Use this whenever the user's question may relate to anything previously discussed or saved (preferences, prior decisions, past topics). Phrase the query naturally. For multi-faceted / overview questions ("tell me about the user", "what's my tech stack"), prefer several targeted searches \u2014 one facet each (e.g. name, work, hobbies) \u2014 over a single broad query; fuse the results yourself.`,
18798
+ description: 'Search the user\'s memory across stored facts/preferences and past conversation excerpts. Returns a unified ranked list \u2014 facts carry an `id` you can reference and the date they were saved; conversation excerpts carry a date and role. Use this whenever the user\'s question may relate to anything previously discussed or saved (preferences, prior decisions, past topics). Phrase the query naturally. For multi-faceted / overview questions ("tell me about the user", "what\'s my tech stack"), prefer several targeted searches \u2014 one facet each (e.g. name, work, hobbies) \u2014 over a single broad query; fuse the results yourself. For "what did I save recently / latest memories" questions, set `sort` to "recent" to get the newest saved facts, newest first, instead of a relevance search.',
18759
18799
  arguments: {
18760
18800
  type: "object",
18761
18801
  properties: {
@@ -18766,6 +18806,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
18766
18806
  limit: {
18767
18807
  type: "integer",
18768
18808
  description: `Max number of results. Default: ${defaultLimit}.`
18809
+ },
18810
+ sort: {
18811
+ type: "string",
18812
+ enum: ["relevance", "recent"],
18813
+ description: `"relevance" (default) ranks by match to the query. "recent" returns the newest saved facts, newest first, and ignores the query's topic.`
18769
18814
  }
18770
18815
  },
18771
18816
  required: ["query"]
@@ -18810,35 +18855,41 @@ function createRecallTool(ctx, toolOptions, callbacks) {
18810
18855
  let surfaced = 0;
18811
18856
  try {
18812
18857
  let recallDegraded = [];
18813
- let subQueries;
18814
- if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
18815
- const decomp = await decomposeQuery(query, toolOptions.decomposeOptions);
18816
- if (decomp.mode === "composite" && decomp.subQueries.length >= 2) {
18817
- subQueries = decomp.subQueries;
18858
+ const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && ctx.vaultCtx !== void 0;
18859
+ let result;
18860
+ if (wantsRecent) {
18861
+ result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
18862
+ } else {
18863
+ let subQueries;
18864
+ if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
18865
+ const decomp = await decomposeQuery(query, toolOptions.decomposeOptions);
18866
+ if (decomp.mode === "composite" && decomp.subQueries.length >= 2) {
18867
+ subQueries = decomp.subQueries;
18868
+ }
18818
18869
  }
18870
+ const recallOpts = {
18871
+ onDiagnostics: (d) => {
18872
+ recallDegraded = d.degraded;
18873
+ },
18874
+ types: defaultTypes,
18875
+ limit: effectiveLimit,
18876
+ budget: defaultBudget,
18877
+ ...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
18878
+ ...toolOptions?.scopes && { scopes: toolOptions.scopes },
18879
+ ...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
18880
+ ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
18881
+ ...toolOptions?.excludeConversationId && {
18882
+ excludeConversationId: toolOptions.excludeConversationId
18883
+ },
18884
+ // Do NOT forward decomposeOptions into recall() — rewrite already
18885
+ // ran above, and forwarding would falsely trip `decompose-moved`
18886
+ // on every specific-mode high-budget call. graphRefine auth can
18887
+ // be threaded later when the tool opts into that path.
18888
+ ...toolOptions?.now !== void 0 && { now: toolOptions.now },
18889
+ ...subQueries && { subQueries }
18890
+ };
18891
+ result = await recall(query, ctx, recallOpts);
18819
18892
  }
18820
- const recallOpts = {
18821
- onDiagnostics: (d) => {
18822
- recallDegraded = d.degraded;
18823
- },
18824
- types: defaultTypes,
18825
- limit: effectiveLimit,
18826
- budget: defaultBudget,
18827
- ...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
18828
- ...toolOptions?.scopes && { scopes: toolOptions.scopes },
18829
- ...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
18830
- ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
18831
- ...toolOptions?.excludeConversationId && {
18832
- excludeConversationId: toolOptions.excludeConversationId
18833
- },
18834
- // Do NOT forward decomposeOptions into recall() — rewrite already
18835
- // ran above, and forwarding would falsely trip `decompose-moved`
18836
- // on every specific-mode high-budget call. graphRefine auth can
18837
- // be threaded later when the tool opts into that path.
18838
- ...toolOptions?.now !== void 0 && { now: toolOptions.now },
18839
- ...subQueries && { subQueries }
18840
- };
18841
- const result = await recall(query, ctx, recallOpts);
18842
18893
  if (callbacks?.onChunksRetrieved) {
18843
18894
  const convIds = Array.from(
18844
18895
  new Set(
@@ -18851,7 +18902,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
18851
18902
  const facts = result.memories.filter((m) => m.kind === "fact").map((m) => ({ id: m.id, score: m.score }));
18852
18903
  if (facts.length > 0) {
18853
18904
  callbacks.onFactsRetrieved?.(facts.map((f) => f.id));
18854
- callbacks.onFactsRanked?.(facts);
18905
+ if (!wantsRecent) callbacks.onFactsRanked?.(facts);
18855
18906
  }
18856
18907
  }
18857
18908
  surfaced = result.memories.length;
@@ -20032,6 +20083,10 @@ function createMessageSourceResolver(ctx) {
20032
20083
  }
20033
20084
 
20034
20085
  // src/lib/memory/context.ts
20086
+ function savedAt(createdAt) {
20087
+ const ms = createdAt?.getTime();
20088
+ return ms !== void 0 && Number.isFinite(ms) && ms > 0 ? { createdAt } : {};
20089
+ }
20035
20090
  function shouldRecallMemory(query) {
20036
20091
  const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
20037
20092
  return normalized.length > 0 && !/^(hi|hi there|hello|hey|yes|no|yep|nope|sure|k|ok|okay|thanks|thank you|thank you very much|thanks a lot|cool|great|got it|sounds good|hola|buenos días|buenas|sí|si|gracias|muchas gracias|vale|bonjour|salut|oui|non|merci|merci beaucoup|d'accord|hallo|guten tag|ja|nein|danke|danke schön|alles klar|olá|obrigado|obrigada|ciao|grazie|sì|你好|谢谢|好的|こんにちは|ありがとう|안녕하세요|감사합니다)$/u.test(
@@ -20069,7 +20124,8 @@ async function assembleMemoryContext(options) {
20069
20124
  content: m.content,
20070
20125
  score,
20071
20126
  lane,
20072
- sourceIds: m.sourceChunkIds ?? []
20127
+ sourceIds: m.sourceChunkIds ?? [],
20128
+ ...savedAt(m.createdAt)
20073
20129
  });
20074
20130
  const empty = {
20075
20131
  memories: [],
@@ -20134,7 +20190,8 @@ async function assembleMemoryContext(options) {
20134
20190
  score: m.score,
20135
20191
  lane,
20136
20192
  ...lane === "fact" && { recalled: true },
20137
- sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
20193
+ sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : []),
20194
+ ...savedAt(m.createdAt)
20138
20195
  }));
20139
20196
  const recalledFacts = toItems(facts, "fact");
20140
20197
  const recalledScores = /* @__PURE__ */ new Map();
@@ -7106,6 +7106,7 @@ declare function getVaultRankingProjectionsOp(ctx: VaultMemoryOperationsContext,
7106
7106
  since?: Date;
7107
7107
  limit?: number;
7108
7108
  folderId?: string | null;
7109
+ memoryIds?: string[];
7109
7110
  }): Promise<RankableVaultMemory[]>;
7110
7111
  /**
7111
7112
  * Bulk-decrypt a KNOWN set of memories by ID — the "decrypt last" half of
@@ -9552,6 +9553,9 @@ interface MemoryContextItem {
9552
9553
  sourceIds: string[];
9553
9554
  /** Ranked fact evidence, retained when profile deduplication wins placement. */
9554
9555
  recalled?: boolean;
9556
+ /** When the memory was written — lets the prompt show a saved date so the
9557
+ * model can answer "what did I save recently". */
9558
+ createdAt?: Date;
9555
9559
  }
9556
9560
  interface MemoryContextOptions {
9557
9561
  query: string;
@@ -7106,6 +7106,7 @@ declare function getVaultRankingProjectionsOp(ctx: VaultMemoryOperationsContext,
7106
7106
  since?: Date;
7107
7107
  limit?: number;
7108
7108
  folderId?: string | null;
7109
+ memoryIds?: string[];
7109
7110
  }): Promise<RankableVaultMemory[]>;
7110
7111
  /**
7111
7112
  * Bulk-decrypt a KNOWN set of memories by ID — the "decrypt last" half of
@@ -9552,6 +9553,9 @@ interface MemoryContextItem {
9552
9553
  sourceIds: string[];
9553
9554
  /** Ranked fact evidence, retained when profile deduplication wins placement. */
9554
9555
  recalled?: boolean;
9556
+ /** When the memory was written — lets the prompt show a saved date so the
9557
+ * model can answer "what did I save recently". */
9558
+ createdAt?: Date;
9555
9559
  }
9556
9560
  interface MemoryContextOptions {
9557
9561
  query: string;
@@ -17016,6 +17016,11 @@ function formatEventTime(start, end, kind) {
17016
17016
  if (kind === "ongoing") return `, event: since ${startDate}`;
17017
17017
  return `, event: ${startDate}`;
17018
17018
  }
17019
+ function formatSavedTime(createdAt) {
17020
+ const ms = createdAt?.getTime();
17021
+ if (ms === void 0 || !Number.isFinite(ms) || ms <= 0) return "";
17022
+ return `, saved: ${new Date(ms).toISOString().slice(0, 10)}`;
17023
+ }
17019
17024
  function generateFenceNonce() {
17020
17025
  const bytes = new Uint8Array(9);
17021
17026
  crypto.getRandomValues(bytes);
@@ -17034,8 +17039,9 @@ function formatRecallResult(memories) {
17034
17039
  const lines = memories.map((m, i) => {
17035
17040
  let body;
17036
17041
  if (m.kind === "fact") {
17042
+ const savedSuffix = formatSavedTime(m.createdAt);
17037
17043
  const eventSuffix = formatEventTime(m.eventTimeStart, m.eventTimeEnd, m.eventTimeKind);
17038
- body = `[${i + 1}] fact (id: ${m.id}${eventSuffix})
17044
+ body = `[${i + 1}] fact (id: ${m.id}${savedSuffix}${eventSuffix})
17039
17045
  ${m.content}`;
17040
17046
  } else {
17041
17047
  const date10 = m.createdAt.toISOString().slice(0, 10);
@@ -17085,6 +17091,40 @@ function isDumpQuery(query) {
17085
17091
  if (EVERYTHING_YOU_KNOW_RE.test(q)) return true;
17086
17092
  return false;
17087
17093
  }
17094
+ async function listRecentFacts(ctx, limit, toolOptions) {
17095
+ const vaultCtx = ctx.vaultCtx;
17096
+ if (!vaultCtx) return [];
17097
+ const ordered = await getVaultRankingProjectionsOp(vaultCtx, {
17098
+ ...toolOptions?.scopes && { scopes: toolOptions.scopes },
17099
+ ...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
17100
+ ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
17101
+ });
17102
+ const readable = [];
17103
+ for (let i = 0; i < ordered.length && readable.length < limit; i += limit) {
17104
+ const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
17105
+ const byId = new Map(
17106
+ (await getVaultMemoriesByIdsOp(vaultCtx, batch)).map((m) => [m.uniqueId, m])
17107
+ );
17108
+ for (const id of batch) {
17109
+ const m = byId.get(id);
17110
+ if (m && !isEncrypted(m.content)) readable.push(m);
17111
+ if (readable.length >= limit) break;
17112
+ }
17113
+ }
17114
+ return readable.map((m) => ({
17115
+ id: m.uniqueId,
17116
+ kind: "fact",
17117
+ content: m.content,
17118
+ score: 0,
17119
+ folderId: m.folderId,
17120
+ eventTimeStart: m.eventTimeStart,
17121
+ eventTimeEnd: m.eventTimeEnd,
17122
+ eventTimeKind: m.eventTimeKind,
17123
+ factType: m.factType,
17124
+ createdAt: m.createdAt,
17125
+ updatedAt: m.updatedAt
17126
+ }));
17127
+ }
17088
17128
  function createRecallTool(ctx, toolOptions, callbacks) {
17089
17129
  const defaultTypes = toolOptions?.types ?? ["fact", "chunk"];
17090
17130
  const defaultLimit = toolOptions?.limit ?? DEFAULT_LIMIT2;
@@ -17097,7 +17137,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
17097
17137
  type: "function",
17098
17138
  function: {
17099
17139
  name: RECALL_TOOL_NAME,
17100
- description: `Search the user's memory across stored facts/preferences and past conversation excerpts. Returns a unified ranked list \u2014 facts carry an \`id\` you can reference; conversation excerpts carry a date and role. Use this whenever the user's question may relate to anything previously discussed or saved (preferences, prior decisions, past topics). Phrase the query naturally. For multi-faceted / overview questions ("tell me about the user", "what's my tech stack"), prefer several targeted searches \u2014 one facet each (e.g. name, work, hobbies) \u2014 over a single broad query; fuse the results yourself.`,
17140
+ description: 'Search the user\'s memory across stored facts/preferences and past conversation excerpts. Returns a unified ranked list \u2014 facts carry an `id` you can reference and the date they were saved; conversation excerpts carry a date and role. Use this whenever the user\'s question may relate to anything previously discussed or saved (preferences, prior decisions, past topics). Phrase the query naturally. For multi-faceted / overview questions ("tell me about the user", "what\'s my tech stack"), prefer several targeted searches \u2014 one facet each (e.g. name, work, hobbies) \u2014 over a single broad query; fuse the results yourself. For "what did I save recently / latest memories" questions, set `sort` to "recent" to get the newest saved facts, newest first, instead of a relevance search.',
17101
17141
  arguments: {
17102
17142
  type: "object",
17103
17143
  properties: {
@@ -17108,6 +17148,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
17108
17148
  limit: {
17109
17149
  type: "integer",
17110
17150
  description: `Max number of results. Default: ${defaultLimit}.`
17151
+ },
17152
+ sort: {
17153
+ type: "string",
17154
+ enum: ["relevance", "recent"],
17155
+ description: `"relevance" (default) ranks by match to the query. "recent" returns the newest saved facts, newest first, and ignores the query's topic.`
17111
17156
  }
17112
17157
  },
17113
17158
  required: ["query"]
@@ -17152,35 +17197,41 @@ function createRecallTool(ctx, toolOptions, callbacks) {
17152
17197
  let surfaced = 0;
17153
17198
  try {
17154
17199
  let recallDegraded = [];
17155
- let subQueries;
17156
- if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
17157
- const decomp = await decomposeQuery(query, toolOptions.decomposeOptions);
17158
- if (decomp.mode === "composite" && decomp.subQueries.length >= 2) {
17159
- subQueries = decomp.subQueries;
17200
+ const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && ctx.vaultCtx !== void 0;
17201
+ let result;
17202
+ if (wantsRecent) {
17203
+ result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
17204
+ } else {
17205
+ let subQueries;
17206
+ if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
17207
+ const decomp = await decomposeQuery(query, toolOptions.decomposeOptions);
17208
+ if (decomp.mode === "composite" && decomp.subQueries.length >= 2) {
17209
+ subQueries = decomp.subQueries;
17210
+ }
17160
17211
  }
17212
+ const recallOpts = {
17213
+ onDiagnostics: (d) => {
17214
+ recallDegraded = d.degraded;
17215
+ },
17216
+ types: defaultTypes,
17217
+ limit: effectiveLimit,
17218
+ budget: defaultBudget,
17219
+ ...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
17220
+ ...toolOptions?.scopes && { scopes: toolOptions.scopes },
17221
+ ...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
17222
+ ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
17223
+ ...toolOptions?.excludeConversationId && {
17224
+ excludeConversationId: toolOptions.excludeConversationId
17225
+ },
17226
+ // Do NOT forward decomposeOptions into recall() — rewrite already
17227
+ // ran above, and forwarding would falsely trip `decompose-moved`
17228
+ // on every specific-mode high-budget call. graphRefine auth can
17229
+ // be threaded later when the tool opts into that path.
17230
+ ...toolOptions?.now !== void 0 && { now: toolOptions.now },
17231
+ ...subQueries && { subQueries }
17232
+ };
17233
+ result = await recall(query, ctx, recallOpts);
17161
17234
  }
17162
- const recallOpts = {
17163
- onDiagnostics: (d) => {
17164
- recallDegraded = d.degraded;
17165
- },
17166
- types: defaultTypes,
17167
- limit: effectiveLimit,
17168
- budget: defaultBudget,
17169
- ...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
17170
- ...toolOptions?.scopes && { scopes: toolOptions.scopes },
17171
- ...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
17172
- ...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
17173
- ...toolOptions?.excludeConversationId && {
17174
- excludeConversationId: toolOptions.excludeConversationId
17175
- },
17176
- // Do NOT forward decomposeOptions into recall() — rewrite already
17177
- // ran above, and forwarding would falsely trip `decompose-moved`
17178
- // on every specific-mode high-budget call. graphRefine auth can
17179
- // be threaded later when the tool opts into that path.
17180
- ...toolOptions?.now !== void 0 && { now: toolOptions.now },
17181
- ...subQueries && { subQueries }
17182
- };
17183
- const result = await recall(query, ctx, recallOpts);
17184
17235
  if (callbacks?.onChunksRetrieved) {
17185
17236
  const convIds = Array.from(
17186
17237
  new Set(
@@ -17193,7 +17244,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
17193
17244
  const facts = result.memories.filter((m) => m.kind === "fact").map((m) => ({ id: m.id, score: m.score }));
17194
17245
  if (facts.length > 0) {
17195
17246
  callbacks.onFactsRetrieved?.(facts.map((f) => f.id));
17196
- callbacks.onFactsRanked?.(facts);
17247
+ if (!wantsRecent) callbacks.onFactsRanked?.(facts);
17197
17248
  }
17198
17249
  }
17199
17250
  surfaced = result.memories.length;
@@ -18362,6 +18413,10 @@ function createMessageSourceResolver(ctx) {
18362
18413
  }
18363
18414
 
18364
18415
  // src/lib/memory/context.ts
18416
+ function savedAt(createdAt) {
18417
+ const ms = createdAt?.getTime();
18418
+ return ms !== void 0 && Number.isFinite(ms) && ms > 0 ? { createdAt } : {};
18419
+ }
18365
18420
  function shouldRecallMemory(query) {
18366
18421
  const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
18367
18422
  return normalized.length > 0 && !/^(hi|hi there|hello|hey|yes|no|yep|nope|sure|k|ok|okay|thanks|thank you|thank you very much|thanks a lot|cool|great|got it|sounds good|hola|buenos días|buenas|sí|si|gracias|muchas gracias|vale|bonjour|salut|oui|non|merci|merci beaucoup|d'accord|hallo|guten tag|ja|nein|danke|danke schön|alles klar|olá|obrigado|obrigada|ciao|grazie|sì|你好|谢谢|好的|こんにちは|ありがとう|안녕하세요|감사합니다)$/u.test(
@@ -18399,7 +18454,8 @@ async function assembleMemoryContext(options) {
18399
18454
  content: m.content,
18400
18455
  score,
18401
18456
  lane,
18402
- sourceIds: m.sourceChunkIds ?? []
18457
+ sourceIds: m.sourceChunkIds ?? [],
18458
+ ...savedAt(m.createdAt)
18403
18459
  });
18404
18460
  const empty = {
18405
18461
  memories: [],
@@ -18464,7 +18520,8 @@ async function assembleMemoryContext(options) {
18464
18520
  score: m.score,
18465
18521
  lane,
18466
18522
  ...lane === "fact" && { recalled: true },
18467
- sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
18523
+ sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : []),
18524
+ ...savedAt(m.createdAt)
18468
18525
  }));
18469
18526
  const recalledFacts = toItems(facts, "fact");
18470
18527
  const recalledScores = /* @__PURE__ */ new Map();
@@ -24,6 +24,10 @@ __export(context_exports, {
24
24
  shouldRecallMemory: () => shouldRecallMemory
25
25
  });
26
26
  module.exports = __toCommonJS(context_exports);
27
+ function savedAt(createdAt) {
28
+ const ms = createdAt?.getTime();
29
+ return ms !== void 0 && Number.isFinite(ms) && ms > 0 ? { createdAt } : {};
30
+ }
27
31
  function shouldRecallMemory(query) {
28
32
  const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
29
33
  return normalized.length > 0 && !/^(hi|hi there|hello|hey|yes|no|yep|nope|sure|k|ok|okay|thanks|thank you|thank you very much|thanks a lot|cool|great|got it|sounds good|hola|buenos días|buenas|sí|si|gracias|muchas gracias|vale|bonjour|salut|oui|non|merci|merci beaucoup|d'accord|hallo|guten tag|ja|nein|danke|danke schön|alles klar|olá|obrigado|obrigada|ciao|grazie|sì|你好|谢谢|好的|こんにちは|ありがとう|안녕하세요|감사합니다)$/u.test(
@@ -61,7 +65,8 @@ async function assembleMemoryContext(options) {
61
65
  content: m.content,
62
66
  score,
63
67
  lane,
64
- sourceIds: m.sourceChunkIds ?? []
68
+ sourceIds: m.sourceChunkIds ?? [],
69
+ ...savedAt(m.createdAt)
65
70
  });
66
71
  const empty = {
67
72
  memories: [],
@@ -126,7 +131,8 @@ async function assembleMemoryContext(options) {
126
131
  score: m.score,
127
132
  lane,
128
133
  ...lane === "fact" && { recalled: true },
129
- sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
134
+ sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : []),
135
+ ...savedAt(m.createdAt)
130
136
  }));
131
137
  const recalledFacts = toItems(facts, "fact");
132
138
  const recalledScores = /* @__PURE__ */ new Map();
@@ -920,6 +920,9 @@ interface MemoryContextItem {
920
920
  sourceIds: string[];
921
921
  /** Ranked fact evidence, retained when profile deduplication wins placement. */
922
922
  recalled?: boolean;
923
+ /** When the memory was written — lets the prompt show a saved date so the
924
+ * model can answer "what did I save recently". */
925
+ createdAt?: Date;
923
926
  }
924
927
  interface MemoryContextOptions {
925
928
  query: string;
@@ -920,6 +920,9 @@ interface MemoryContextItem {
920
920
  sourceIds: string[];
921
921
  /** Ranked fact evidence, retained when profile deduplication wins placement. */
922
922
  recalled?: boolean;
923
+ /** When the memory was written — lets the prompt show a saved date so the
924
+ * model can answer "what did I save recently". */
925
+ createdAt?: Date;
923
926
  }
924
927
  interface MemoryContextOptions {
925
928
  query: string;
@@ -1,4 +1,8 @@
1
1
  // src/lib/memory/context.ts
2
+ function savedAt(createdAt) {
3
+ const ms = createdAt?.getTime();
4
+ return ms !== void 0 && Number.isFinite(ms) && ms > 0 ? { createdAt } : {};
5
+ }
2
6
  function shouldRecallMemory(query) {
3
7
  const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
4
8
  return normalized.length > 0 && !/^(hi|hi there|hello|hey|yes|no|yep|nope|sure|k|ok|okay|thanks|thank you|thank you very much|thanks a lot|cool|great|got it|sounds good|hola|buenos días|buenas|sí|si|gracias|muchas gracias|vale|bonjour|salut|oui|non|merci|merci beaucoup|d'accord|hallo|guten tag|ja|nein|danke|danke schön|alles klar|olá|obrigado|obrigada|ciao|grazie|sì|你好|谢谢|好的|こんにちは|ありがとう|안녕하세요|감사합니다)$/u.test(
@@ -36,7 +40,8 @@ async function assembleMemoryContext(options) {
36
40
  content: m.content,
37
41
  score,
38
42
  lane,
39
- sourceIds: m.sourceChunkIds ?? []
43
+ sourceIds: m.sourceChunkIds ?? [],
44
+ ...savedAt(m.createdAt)
40
45
  });
41
46
  const empty = {
42
47
  memories: [],
@@ -101,7 +106,8 @@ async function assembleMemoryContext(options) {
101
106
  score: m.score,
102
107
  lane,
103
108
  ...lane === "fact" && { recalled: true },
104
- sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
109
+ sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : []),
110
+ ...savedAt(m.createdAt)
105
111
  }));
106
112
  const recalledFacts = toItems(facts, "fact");
107
113
  const recalledScores = /* @__PURE__ */ new Map();