@anuma/sdk 6.29.2 → 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.
- package/dist/expo/index.cjs +89 -32
- package/dist/expo/index.d.mts +4 -0
- package/dist/expo/index.d.ts +4 -0
- package/dist/expo/index.mjs +89 -32
- package/dist/memory/context.cjs +8 -2
- package/dist/memory/context.d.mts +3 -0
- package/dist/memory/context.d.ts +3 -0
- package/dist/memory/context.mjs +8 -2
- package/dist/react/index.cjs +89 -32
- package/dist/react/index.d.mts +4 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.mjs +89 -32
- package/dist/server/index.cjs +116 -32
- package/dist/server/index.d.mts +3 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.mjs +116 -32
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -20021,6 +20021,11 @@ function formatEventTime(start, end, kind) {
|
|
|
20021
20021
|
if (kind === "ongoing") return `, event: since ${startDate}`;
|
|
20022
20022
|
return `, event: ${startDate}`;
|
|
20023
20023
|
}
|
|
20024
|
+
function formatSavedTime(createdAt) {
|
|
20025
|
+
const ms = createdAt?.getTime();
|
|
20026
|
+
if (ms === void 0 || !Number.isFinite(ms) || ms <= 0) return "";
|
|
20027
|
+
return `, saved: ${new Date(ms).toISOString().slice(0, 10)}`;
|
|
20028
|
+
}
|
|
20024
20029
|
function generateFenceNonce() {
|
|
20025
20030
|
const bytes = new Uint8Array(9);
|
|
20026
20031
|
crypto.getRandomValues(bytes);
|
|
@@ -20039,8 +20044,9 @@ function formatRecallResult(memories) {
|
|
|
20039
20044
|
const lines = memories.map((m, i) => {
|
|
20040
20045
|
let body;
|
|
20041
20046
|
if (m.kind === "fact") {
|
|
20047
|
+
const savedSuffix = formatSavedTime(m.createdAt);
|
|
20042
20048
|
const eventSuffix = formatEventTime(m.eventTimeStart, m.eventTimeEnd, m.eventTimeKind);
|
|
20043
|
-
body = `[${i + 1}] fact (id: ${m.id}${eventSuffix})
|
|
20049
|
+
body = `[${i + 1}] fact (id: ${m.id}${savedSuffix}${eventSuffix})
|
|
20044
20050
|
${m.content}`;
|
|
20045
20051
|
} else {
|
|
20046
20052
|
const date10 = m.createdAt.toISOString().slice(0, 10);
|
|
@@ -20090,6 +20096,40 @@ function isDumpQuery(query) {
|
|
|
20090
20096
|
if (EVERYTHING_YOU_KNOW_RE.test(q)) return true;
|
|
20091
20097
|
return false;
|
|
20092
20098
|
}
|
|
20099
|
+
async function listRecentFacts(ctx, limit, toolOptions) {
|
|
20100
|
+
const vaultCtx = ctx.vaultCtx;
|
|
20101
|
+
if (!vaultCtx) return [];
|
|
20102
|
+
const ordered = await getVaultRankingProjectionsOp(vaultCtx, {
|
|
20103
|
+
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
20104
|
+
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
20105
|
+
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
|
|
20106
|
+
});
|
|
20107
|
+
const readable = [];
|
|
20108
|
+
for (let i = 0; i < ordered.length && readable.length < limit; i += limit) {
|
|
20109
|
+
const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
|
|
20110
|
+
const byId = new Map(
|
|
20111
|
+
(await getVaultMemoriesByIdsOp(vaultCtx, batch)).map((m) => [m.uniqueId, m])
|
|
20112
|
+
);
|
|
20113
|
+
for (const id of batch) {
|
|
20114
|
+
const m = byId.get(id);
|
|
20115
|
+
if (m && !isEncrypted(m.content)) readable.push(m);
|
|
20116
|
+
if (readable.length >= limit) break;
|
|
20117
|
+
}
|
|
20118
|
+
}
|
|
20119
|
+
return readable.map((m) => ({
|
|
20120
|
+
id: m.uniqueId,
|
|
20121
|
+
kind: "fact",
|
|
20122
|
+
content: m.content,
|
|
20123
|
+
score: 0,
|
|
20124
|
+
folderId: m.folderId,
|
|
20125
|
+
eventTimeStart: m.eventTimeStart,
|
|
20126
|
+
eventTimeEnd: m.eventTimeEnd,
|
|
20127
|
+
eventTimeKind: m.eventTimeKind,
|
|
20128
|
+
factType: m.factType,
|
|
20129
|
+
createdAt: m.createdAt,
|
|
20130
|
+
updatedAt: m.updatedAt
|
|
20131
|
+
}));
|
|
20132
|
+
}
|
|
20093
20133
|
function createRecallTool(ctx, toolOptions, callbacks) {
|
|
20094
20134
|
const defaultTypes = toolOptions?.types ?? ["fact", "chunk"];
|
|
20095
20135
|
const defaultLimit = toolOptions?.limit ?? DEFAULT_LIMIT2;
|
|
@@ -20102,7 +20142,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
20102
20142
|
type: "function",
|
|
20103
20143
|
function: {
|
|
20104
20144
|
name: RECALL_TOOL_NAME,
|
|
20105
|
-
description:
|
|
20145
|
+
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.',
|
|
20106
20146
|
arguments: {
|
|
20107
20147
|
type: "object",
|
|
20108
20148
|
properties: {
|
|
@@ -20113,6 +20153,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
20113
20153
|
limit: {
|
|
20114
20154
|
type: "integer",
|
|
20115
20155
|
description: `Max number of results. Default: ${defaultLimit}.`
|
|
20156
|
+
},
|
|
20157
|
+
sort: {
|
|
20158
|
+
type: "string",
|
|
20159
|
+
enum: ["relevance", "recent"],
|
|
20160
|
+
description: `"relevance" (default) ranks by match to the query. "recent" returns the newest saved facts, newest first, and ignores the query's topic.`
|
|
20116
20161
|
}
|
|
20117
20162
|
},
|
|
20118
20163
|
required: ["query"]
|
|
@@ -20157,35 +20202,41 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
20157
20202
|
let surfaced = 0;
|
|
20158
20203
|
try {
|
|
20159
20204
|
let recallDegraded = [];
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20205
|
+
const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && ctx.vaultCtx !== void 0;
|
|
20206
|
+
let result;
|
|
20207
|
+
if (wantsRecent) {
|
|
20208
|
+
result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
|
|
20209
|
+
} else {
|
|
20210
|
+
let subQueries;
|
|
20211
|
+
if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
|
|
20212
|
+
const decomp = await decomposeQuery(query, toolOptions.decomposeOptions);
|
|
20213
|
+
if (decomp.mode === "composite" && decomp.subQueries.length >= 2) {
|
|
20214
|
+
subQueries = decomp.subQueries;
|
|
20215
|
+
}
|
|
20165
20216
|
}
|
|
20217
|
+
const recallOpts = {
|
|
20218
|
+
onDiagnostics: (d) => {
|
|
20219
|
+
recallDegraded = d.degraded;
|
|
20220
|
+
},
|
|
20221
|
+
types: defaultTypes,
|
|
20222
|
+
limit: effectiveLimit,
|
|
20223
|
+
budget: defaultBudget,
|
|
20224
|
+
...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
|
|
20225
|
+
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
20226
|
+
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
20227
|
+
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
|
|
20228
|
+
...toolOptions?.excludeConversationId && {
|
|
20229
|
+
excludeConversationId: toolOptions.excludeConversationId
|
|
20230
|
+
},
|
|
20231
|
+
// Do NOT forward decomposeOptions into recall() — rewrite already
|
|
20232
|
+
// ran above, and forwarding would falsely trip `decompose-moved`
|
|
20233
|
+
// on every specific-mode high-budget call. graphRefine auth can
|
|
20234
|
+
// be threaded later when the tool opts into that path.
|
|
20235
|
+
...toolOptions?.now !== void 0 && { now: toolOptions.now },
|
|
20236
|
+
...subQueries && { subQueries }
|
|
20237
|
+
};
|
|
20238
|
+
result = await recall(query, ctx, recallOpts);
|
|
20166
20239
|
}
|
|
20167
|
-
const recallOpts = {
|
|
20168
|
-
onDiagnostics: (d) => {
|
|
20169
|
-
recallDegraded = d.degraded;
|
|
20170
|
-
},
|
|
20171
|
-
types: defaultTypes,
|
|
20172
|
-
limit: effectiveLimit,
|
|
20173
|
-
budget: defaultBudget,
|
|
20174
|
-
...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
|
|
20175
|
-
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
20176
|
-
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
20177
|
-
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
|
|
20178
|
-
...toolOptions?.excludeConversationId && {
|
|
20179
|
-
excludeConversationId: toolOptions.excludeConversationId
|
|
20180
|
-
},
|
|
20181
|
-
// Do NOT forward decomposeOptions into recall() — rewrite already
|
|
20182
|
-
// ran above, and forwarding would falsely trip `decompose-moved`
|
|
20183
|
-
// on every specific-mode high-budget call. graphRefine auth can
|
|
20184
|
-
// be threaded later when the tool opts into that path.
|
|
20185
|
-
...toolOptions?.now !== void 0 && { now: toolOptions.now },
|
|
20186
|
-
...subQueries && { subQueries }
|
|
20187
|
-
};
|
|
20188
|
-
const result = await recall(query, ctx, recallOpts);
|
|
20189
20240
|
if (callbacks?.onChunksRetrieved) {
|
|
20190
20241
|
const convIds = Array.from(
|
|
20191
20242
|
new Set(
|
|
@@ -20198,7 +20249,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
20198
20249
|
const facts = result.memories.filter((m) => m.kind === "fact").map((m) => ({ id: m.id, score: m.score }));
|
|
20199
20250
|
if (facts.length > 0) {
|
|
20200
20251
|
callbacks.onFactsRetrieved?.(facts.map((f) => f.id));
|
|
20201
|
-
callbacks.onFactsRanked?.(facts);
|
|
20252
|
+
if (!wantsRecent) callbacks.onFactsRanked?.(facts);
|
|
20202
20253
|
}
|
|
20203
20254
|
}
|
|
20204
20255
|
surfaced = result.memories.length;
|
|
@@ -21379,6 +21430,10 @@ function createMessageSourceResolver(ctx) {
|
|
|
21379
21430
|
}
|
|
21380
21431
|
|
|
21381
21432
|
// src/lib/memory/context.ts
|
|
21433
|
+
function savedAt(createdAt) {
|
|
21434
|
+
const ms = createdAt?.getTime();
|
|
21435
|
+
return ms !== void 0 && Number.isFinite(ms) && ms > 0 ? { createdAt } : {};
|
|
21436
|
+
}
|
|
21382
21437
|
function shouldRecallMemory(query) {
|
|
21383
21438
|
const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
|
|
21384
21439
|
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(
|
|
@@ -21416,7 +21471,8 @@ async function assembleMemoryContext(options) {
|
|
|
21416
21471
|
content: m.content,
|
|
21417
21472
|
score,
|
|
21418
21473
|
lane,
|
|
21419
|
-
sourceIds: m.sourceChunkIds ?? []
|
|
21474
|
+
sourceIds: m.sourceChunkIds ?? [],
|
|
21475
|
+
...savedAt(m.createdAt)
|
|
21420
21476
|
});
|
|
21421
21477
|
const empty = {
|
|
21422
21478
|
memories: [],
|
|
@@ -21481,7 +21537,8 @@ async function assembleMemoryContext(options) {
|
|
|
21481
21537
|
score: m.score,
|
|
21482
21538
|
lane,
|
|
21483
21539
|
...lane === "fact" && { recalled: true },
|
|
21484
|
-
sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
|
|
21540
|
+
sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : []),
|
|
21541
|
+
...savedAt(m.createdAt)
|
|
21485
21542
|
}));
|
|
21486
21543
|
const recalledFacts = toItems(facts, "fact");
|
|
21487
21544
|
const recalledScores = /* @__PURE__ */ new Map();
|
package/dist/react/index.d.mts
CHANGED
|
@@ -8433,6 +8433,7 @@ declare function getVaultRankingProjectionsOp(ctx: VaultMemoryOperationsContext,
|
|
|
8433
8433
|
since?: Date;
|
|
8434
8434
|
limit?: number;
|
|
8435
8435
|
folderId?: string | null;
|
|
8436
|
+
memoryIds?: string[];
|
|
8436
8437
|
}): Promise<RankableVaultMemory[]>;
|
|
8437
8438
|
interface VaultCandidateKey {
|
|
8438
8439
|
uniqueId: string;
|
|
@@ -10845,6 +10846,9 @@ interface MemoryContextItem {
|
|
|
10845
10846
|
sourceIds: string[];
|
|
10846
10847
|
/** Ranked fact evidence, retained when profile deduplication wins placement. */
|
|
10847
10848
|
recalled?: boolean;
|
|
10849
|
+
/** When the memory was written — lets the prompt show a saved date so the
|
|
10850
|
+
* model can answer "what did I save recently". */
|
|
10851
|
+
createdAt?: Date;
|
|
10848
10852
|
}
|
|
10849
10853
|
interface MemoryContextOptions {
|
|
10850
10854
|
query: string;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -8433,6 +8433,7 @@ declare function getVaultRankingProjectionsOp(ctx: VaultMemoryOperationsContext,
|
|
|
8433
8433
|
since?: Date;
|
|
8434
8434
|
limit?: number;
|
|
8435
8435
|
folderId?: string | null;
|
|
8436
|
+
memoryIds?: string[];
|
|
8436
8437
|
}): Promise<RankableVaultMemory[]>;
|
|
8437
8438
|
interface VaultCandidateKey {
|
|
8438
8439
|
uniqueId: string;
|
|
@@ -10845,6 +10846,9 @@ interface MemoryContextItem {
|
|
|
10845
10846
|
sourceIds: string[];
|
|
10846
10847
|
/** Ranked fact evidence, retained when profile deduplication wins placement. */
|
|
10847
10848
|
recalled?: boolean;
|
|
10849
|
+
/** When the memory was written — lets the prompt show a saved date so the
|
|
10850
|
+
* model can answer "what did I save recently". */
|
|
10851
|
+
createdAt?: Date;
|
|
10848
10852
|
}
|
|
10849
10853
|
interface MemoryContextOptions {
|
|
10850
10854
|
query: string;
|
package/dist/react/index.mjs
CHANGED
|
@@ -18118,6 +18118,11 @@ function formatEventTime(start, end, kind) {
|
|
|
18118
18118
|
if (kind === "ongoing") return `, event: since ${startDate}`;
|
|
18119
18119
|
return `, event: ${startDate}`;
|
|
18120
18120
|
}
|
|
18121
|
+
function formatSavedTime(createdAt) {
|
|
18122
|
+
const ms = createdAt?.getTime();
|
|
18123
|
+
if (ms === void 0 || !Number.isFinite(ms) || ms <= 0) return "";
|
|
18124
|
+
return `, saved: ${new Date(ms).toISOString().slice(0, 10)}`;
|
|
18125
|
+
}
|
|
18121
18126
|
function generateFenceNonce() {
|
|
18122
18127
|
const bytes = new Uint8Array(9);
|
|
18123
18128
|
crypto.getRandomValues(bytes);
|
|
@@ -18136,8 +18141,9 @@ function formatRecallResult(memories) {
|
|
|
18136
18141
|
const lines = memories.map((m, i) => {
|
|
18137
18142
|
let body;
|
|
18138
18143
|
if (m.kind === "fact") {
|
|
18144
|
+
const savedSuffix = formatSavedTime(m.createdAt);
|
|
18139
18145
|
const eventSuffix = formatEventTime(m.eventTimeStart, m.eventTimeEnd, m.eventTimeKind);
|
|
18140
|
-
body = `[${i + 1}] fact (id: ${m.id}${eventSuffix})
|
|
18146
|
+
body = `[${i + 1}] fact (id: ${m.id}${savedSuffix}${eventSuffix})
|
|
18141
18147
|
${m.content}`;
|
|
18142
18148
|
} else {
|
|
18143
18149
|
const date10 = m.createdAt.toISOString().slice(0, 10);
|
|
@@ -18187,6 +18193,40 @@ function isDumpQuery(query) {
|
|
|
18187
18193
|
if (EVERYTHING_YOU_KNOW_RE.test(q)) return true;
|
|
18188
18194
|
return false;
|
|
18189
18195
|
}
|
|
18196
|
+
async function listRecentFacts(ctx, limit, toolOptions) {
|
|
18197
|
+
const vaultCtx = ctx.vaultCtx;
|
|
18198
|
+
if (!vaultCtx) return [];
|
|
18199
|
+
const ordered = await getVaultRankingProjectionsOp(vaultCtx, {
|
|
18200
|
+
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
18201
|
+
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
18202
|
+
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
|
|
18203
|
+
});
|
|
18204
|
+
const readable = [];
|
|
18205
|
+
for (let i = 0; i < ordered.length && readable.length < limit; i += limit) {
|
|
18206
|
+
const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
|
|
18207
|
+
const byId = new Map(
|
|
18208
|
+
(await getVaultMemoriesByIdsOp(vaultCtx, batch)).map((m) => [m.uniqueId, m])
|
|
18209
|
+
);
|
|
18210
|
+
for (const id of batch) {
|
|
18211
|
+
const m = byId.get(id);
|
|
18212
|
+
if (m && !isEncrypted(m.content)) readable.push(m);
|
|
18213
|
+
if (readable.length >= limit) break;
|
|
18214
|
+
}
|
|
18215
|
+
}
|
|
18216
|
+
return readable.map((m) => ({
|
|
18217
|
+
id: m.uniqueId,
|
|
18218
|
+
kind: "fact",
|
|
18219
|
+
content: m.content,
|
|
18220
|
+
score: 0,
|
|
18221
|
+
folderId: m.folderId,
|
|
18222
|
+
eventTimeStart: m.eventTimeStart,
|
|
18223
|
+
eventTimeEnd: m.eventTimeEnd,
|
|
18224
|
+
eventTimeKind: m.eventTimeKind,
|
|
18225
|
+
factType: m.factType,
|
|
18226
|
+
createdAt: m.createdAt,
|
|
18227
|
+
updatedAt: m.updatedAt
|
|
18228
|
+
}));
|
|
18229
|
+
}
|
|
18190
18230
|
function createRecallTool(ctx, toolOptions, callbacks) {
|
|
18191
18231
|
const defaultTypes = toolOptions?.types ?? ["fact", "chunk"];
|
|
18192
18232
|
const defaultLimit = toolOptions?.limit ?? DEFAULT_LIMIT2;
|
|
@@ -18199,7 +18239,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
18199
18239
|
type: "function",
|
|
18200
18240
|
function: {
|
|
18201
18241
|
name: RECALL_TOOL_NAME,
|
|
18202
|
-
description:
|
|
18242
|
+
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.',
|
|
18203
18243
|
arguments: {
|
|
18204
18244
|
type: "object",
|
|
18205
18245
|
properties: {
|
|
@@ -18210,6 +18250,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
18210
18250
|
limit: {
|
|
18211
18251
|
type: "integer",
|
|
18212
18252
|
description: `Max number of results. Default: ${defaultLimit}.`
|
|
18253
|
+
},
|
|
18254
|
+
sort: {
|
|
18255
|
+
type: "string",
|
|
18256
|
+
enum: ["relevance", "recent"],
|
|
18257
|
+
description: `"relevance" (default) ranks by match to the query. "recent" returns the newest saved facts, newest first, and ignores the query's topic.`
|
|
18213
18258
|
}
|
|
18214
18259
|
},
|
|
18215
18260
|
required: ["query"]
|
|
@@ -18254,35 +18299,41 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
18254
18299
|
let surfaced = 0;
|
|
18255
18300
|
try {
|
|
18256
18301
|
let recallDegraded = [];
|
|
18257
|
-
|
|
18258
|
-
|
|
18259
|
-
|
|
18260
|
-
|
|
18261
|
-
|
|
18302
|
+
const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && ctx.vaultCtx !== void 0;
|
|
18303
|
+
let result;
|
|
18304
|
+
if (wantsRecent) {
|
|
18305
|
+
result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
|
|
18306
|
+
} else {
|
|
18307
|
+
let subQueries;
|
|
18308
|
+
if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
|
|
18309
|
+
const decomp = await decomposeQuery(query, toolOptions.decomposeOptions);
|
|
18310
|
+
if (decomp.mode === "composite" && decomp.subQueries.length >= 2) {
|
|
18311
|
+
subQueries = decomp.subQueries;
|
|
18312
|
+
}
|
|
18262
18313
|
}
|
|
18314
|
+
const recallOpts = {
|
|
18315
|
+
onDiagnostics: (d) => {
|
|
18316
|
+
recallDegraded = d.degraded;
|
|
18317
|
+
},
|
|
18318
|
+
types: defaultTypes,
|
|
18319
|
+
limit: effectiveLimit,
|
|
18320
|
+
budget: defaultBudget,
|
|
18321
|
+
...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
|
|
18322
|
+
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
18323
|
+
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
18324
|
+
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
|
|
18325
|
+
...toolOptions?.excludeConversationId && {
|
|
18326
|
+
excludeConversationId: toolOptions.excludeConversationId
|
|
18327
|
+
},
|
|
18328
|
+
// Do NOT forward decomposeOptions into recall() — rewrite already
|
|
18329
|
+
// ran above, and forwarding would falsely trip `decompose-moved`
|
|
18330
|
+
// on every specific-mode high-budget call. graphRefine auth can
|
|
18331
|
+
// be threaded later when the tool opts into that path.
|
|
18332
|
+
...toolOptions?.now !== void 0 && { now: toolOptions.now },
|
|
18333
|
+
...subQueries && { subQueries }
|
|
18334
|
+
};
|
|
18335
|
+
result = await recall(query, ctx, recallOpts);
|
|
18263
18336
|
}
|
|
18264
|
-
const recallOpts = {
|
|
18265
|
-
onDiagnostics: (d) => {
|
|
18266
|
-
recallDegraded = d.degraded;
|
|
18267
|
-
},
|
|
18268
|
-
types: defaultTypes,
|
|
18269
|
-
limit: effectiveLimit,
|
|
18270
|
-
budget: defaultBudget,
|
|
18271
|
-
...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
|
|
18272
|
-
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
18273
|
-
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
18274
|
-
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
|
|
18275
|
-
...toolOptions?.excludeConversationId && {
|
|
18276
|
-
excludeConversationId: toolOptions.excludeConversationId
|
|
18277
|
-
},
|
|
18278
|
-
// Do NOT forward decomposeOptions into recall() — rewrite already
|
|
18279
|
-
// ran above, and forwarding would falsely trip `decompose-moved`
|
|
18280
|
-
// on every specific-mode high-budget call. graphRefine auth can
|
|
18281
|
-
// be threaded later when the tool opts into that path.
|
|
18282
|
-
...toolOptions?.now !== void 0 && { now: toolOptions.now },
|
|
18283
|
-
...subQueries && { subQueries }
|
|
18284
|
-
};
|
|
18285
|
-
const result = await recall(query, ctx, recallOpts);
|
|
18286
18337
|
if (callbacks?.onChunksRetrieved) {
|
|
18287
18338
|
const convIds = Array.from(
|
|
18288
18339
|
new Set(
|
|
@@ -18295,7 +18346,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
18295
18346
|
const facts = result.memories.filter((m) => m.kind === "fact").map((m) => ({ id: m.id, score: m.score }));
|
|
18296
18347
|
if (facts.length > 0) {
|
|
18297
18348
|
callbacks.onFactsRetrieved?.(facts.map((f) => f.id));
|
|
18298
|
-
callbacks.onFactsRanked?.(facts);
|
|
18349
|
+
if (!wantsRecent) callbacks.onFactsRanked?.(facts);
|
|
18299
18350
|
}
|
|
18300
18351
|
}
|
|
18301
18352
|
surfaced = result.memories.length;
|
|
@@ -19464,6 +19515,10 @@ function createMessageSourceResolver(ctx) {
|
|
|
19464
19515
|
}
|
|
19465
19516
|
|
|
19466
19517
|
// src/lib/memory/context.ts
|
|
19518
|
+
function savedAt(createdAt) {
|
|
19519
|
+
const ms = createdAt?.getTime();
|
|
19520
|
+
return ms !== void 0 && Number.isFinite(ms) && ms > 0 ? { createdAt } : {};
|
|
19521
|
+
}
|
|
19467
19522
|
function shouldRecallMemory(query) {
|
|
19468
19523
|
const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
|
|
19469
19524
|
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(
|
|
@@ -19501,7 +19556,8 @@ async function assembleMemoryContext(options) {
|
|
|
19501
19556
|
content: m.content,
|
|
19502
19557
|
score,
|
|
19503
19558
|
lane,
|
|
19504
|
-
sourceIds: m.sourceChunkIds ?? []
|
|
19559
|
+
sourceIds: m.sourceChunkIds ?? [],
|
|
19560
|
+
...savedAt(m.createdAt)
|
|
19505
19561
|
});
|
|
19506
19562
|
const empty = {
|
|
19507
19563
|
memories: [],
|
|
@@ -19566,7 +19622,8 @@ async function assembleMemoryContext(options) {
|
|
|
19566
19622
|
score: m.score,
|
|
19567
19623
|
lane,
|
|
19568
19624
|
...lane === "fact" && { recalled: true },
|
|
19569
|
-
sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
|
|
19625
|
+
sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : []),
|
|
19626
|
+
...savedAt(m.createdAt)
|
|
19570
19627
|
}));
|
|
19571
19628
|
const recalledFacts = toItems(facts, "fact");
|
|
19572
19629
|
const recalledScores = /* @__PURE__ */ new Map();
|
package/dist/server/index.cjs
CHANGED
|
@@ -22386,6 +22386,33 @@ async function getAllVaultMemoriesOp(ctx, options) {
|
|
|
22386
22386
|
(raw) => vaultMemoryRawToStored(raw, ctx.walletAddress, ctx.signMessage, ctx.embeddedWalletSigner)
|
|
22387
22387
|
);
|
|
22388
22388
|
}
|
|
22389
|
+
function vaultMemoryRawToRankable(raw) {
|
|
22390
|
+
return {
|
|
22391
|
+
uniqueId: raw.id,
|
|
22392
|
+
// @text coerces NULL→"" on the Model path; unsafeFetchRaw returns raw NULL, so guard.
|
|
22393
|
+
scope: raw.scope ?? "",
|
|
22394
|
+
folderId: raw.folder_id ?? null,
|
|
22395
|
+
embedding: raw.embedding ?? null,
|
|
22396
|
+
embeddingModel: raw.embedding_model ?? null,
|
|
22397
|
+
createdAt: new Date(raw.created_at),
|
|
22398
|
+
updatedAt: new Date(raw.updated_at),
|
|
22399
|
+
// Same mapping as vaultMemoryRawToStoredRaw. A consolidation rewrite moves
|
|
22400
|
+
// ONLY this column (preserveUpdatedAt pins updated_at), so a change-since
|
|
22401
|
+
// check that reads this projection needs it — see the type doc.
|
|
22402
|
+
lastObservedAt: raw.last_observed_at ?? null
|
|
22403
|
+
};
|
|
22404
|
+
}
|
|
22405
|
+
async function getVaultRankingProjectionsOp(ctx, options) {
|
|
22406
|
+
const conditions = [
|
|
22407
|
+
...baseVaultConditions(ctx, options),
|
|
22408
|
+
...options?.scopes?.length ? [import_watermelondb18.Q.where("scope", import_watermelondb18.Q.oneOf(options.scopes))] : [],
|
|
22409
|
+
...options?.folderId !== void 0 ? [import_watermelondb18.Q.where("folder_id", options.folderId)] : [],
|
|
22410
|
+
import_watermelondb18.Q.sortBy(options?.since ? "updated_at" : "created_at", import_watermelondb18.Q.desc),
|
|
22411
|
+
...options?.limit !== null && options?.limit !== void 0 && options.limit > 0 ? [import_watermelondb18.Q.take(options.limit)] : []
|
|
22412
|
+
];
|
|
22413
|
+
const results = await ctx.vaultMemoryCollection.query(...conditions).unsafeFetchRaw();
|
|
22414
|
+
return results.map(vaultMemoryRawToRankable);
|
|
22415
|
+
}
|
|
22389
22416
|
function baseVaultSql(ctx, options) {
|
|
22390
22417
|
const clauses = [
|
|
22391
22418
|
'"is_deleted" = 0',
|
|
@@ -30795,6 +30822,11 @@ function formatEventTime(start, end, kind) {
|
|
|
30795
30822
|
if (kind === "ongoing") return `, event: since ${startDate}`;
|
|
30796
30823
|
return `, event: ${startDate}`;
|
|
30797
30824
|
}
|
|
30825
|
+
function formatSavedTime(createdAt) {
|
|
30826
|
+
const ms = createdAt?.getTime();
|
|
30827
|
+
if (ms === void 0 || !Number.isFinite(ms) || ms <= 0) return "";
|
|
30828
|
+
return `, saved: ${new Date(ms).toISOString().slice(0, 10)}`;
|
|
30829
|
+
}
|
|
30798
30830
|
function generateFenceNonce() {
|
|
30799
30831
|
const bytes = new Uint8Array(9);
|
|
30800
30832
|
crypto.getRandomValues(bytes);
|
|
@@ -30813,8 +30845,9 @@ function formatRecallResult(memories) {
|
|
|
30813
30845
|
const lines = memories.map((m, i) => {
|
|
30814
30846
|
let body;
|
|
30815
30847
|
if (m.kind === "fact") {
|
|
30848
|
+
const savedSuffix = formatSavedTime(m.createdAt);
|
|
30816
30849
|
const eventSuffix = formatEventTime(m.eventTimeStart, m.eventTimeEnd, m.eventTimeKind);
|
|
30817
|
-
body = `[${i + 1}] fact (id: ${m.id}${eventSuffix})
|
|
30850
|
+
body = `[${i + 1}] fact (id: ${m.id}${savedSuffix}${eventSuffix})
|
|
30818
30851
|
${m.content}`;
|
|
30819
30852
|
} else {
|
|
30820
30853
|
const date10 = m.createdAt.toISOString().slice(0, 10);
|
|
@@ -30864,6 +30897,40 @@ function isDumpQuery(query) {
|
|
|
30864
30897
|
if (EVERYTHING_YOU_KNOW_RE.test(q)) return true;
|
|
30865
30898
|
return false;
|
|
30866
30899
|
}
|
|
30900
|
+
async function listRecentFacts(ctx, limit, toolOptions) {
|
|
30901
|
+
const vaultCtx = ctx.vaultCtx;
|
|
30902
|
+
if (!vaultCtx) return [];
|
|
30903
|
+
const ordered = await getVaultRankingProjectionsOp(vaultCtx, {
|
|
30904
|
+
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
30905
|
+
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
30906
|
+
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId }
|
|
30907
|
+
});
|
|
30908
|
+
const readable = [];
|
|
30909
|
+
for (let i = 0; i < ordered.length && readable.length < limit; i += limit) {
|
|
30910
|
+
const batch = ordered.slice(i, i + limit).map((p) => p.uniqueId);
|
|
30911
|
+
const byId = new Map(
|
|
30912
|
+
(await getVaultMemoriesByIdsOp(vaultCtx, batch)).map((m) => [m.uniqueId, m])
|
|
30913
|
+
);
|
|
30914
|
+
for (const id of batch) {
|
|
30915
|
+
const m = byId.get(id);
|
|
30916
|
+
if (m && !isEncrypted(m.content)) readable.push(m);
|
|
30917
|
+
if (readable.length >= limit) break;
|
|
30918
|
+
}
|
|
30919
|
+
}
|
|
30920
|
+
return readable.map((m) => ({
|
|
30921
|
+
id: m.uniqueId,
|
|
30922
|
+
kind: "fact",
|
|
30923
|
+
content: m.content,
|
|
30924
|
+
score: 0,
|
|
30925
|
+
folderId: m.folderId,
|
|
30926
|
+
eventTimeStart: m.eventTimeStart,
|
|
30927
|
+
eventTimeEnd: m.eventTimeEnd,
|
|
30928
|
+
eventTimeKind: m.eventTimeKind,
|
|
30929
|
+
factType: m.factType,
|
|
30930
|
+
createdAt: m.createdAt,
|
|
30931
|
+
updatedAt: m.updatedAt
|
|
30932
|
+
}));
|
|
30933
|
+
}
|
|
30867
30934
|
function createRecallTool(ctx, toolOptions, callbacks) {
|
|
30868
30935
|
const defaultTypes = toolOptions?.types ?? ["fact", "chunk"];
|
|
30869
30936
|
const defaultLimit = toolOptions?.limit ?? DEFAULT_LIMIT2;
|
|
@@ -30876,7 +30943,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
30876
30943
|
type: "function",
|
|
30877
30944
|
function: {
|
|
30878
30945
|
name: RECALL_TOOL_NAME,
|
|
30879
|
-
description:
|
|
30946
|
+
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.',
|
|
30880
30947
|
arguments: {
|
|
30881
30948
|
type: "object",
|
|
30882
30949
|
properties: {
|
|
@@ -30887,6 +30954,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
30887
30954
|
limit: {
|
|
30888
30955
|
type: "integer",
|
|
30889
30956
|
description: `Max number of results. Default: ${defaultLimit}.`
|
|
30957
|
+
},
|
|
30958
|
+
sort: {
|
|
30959
|
+
type: "string",
|
|
30960
|
+
enum: ["relevance", "recent"],
|
|
30961
|
+
description: `"relevance" (default) ranks by match to the query. "recent" returns the newest saved facts, newest first, and ignores the query's topic.`
|
|
30890
30962
|
}
|
|
30891
30963
|
},
|
|
30892
30964
|
required: ["query"]
|
|
@@ -30931,35 +31003,41 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
30931
31003
|
let surfaced = 0;
|
|
30932
31004
|
try {
|
|
30933
31005
|
let recallDegraded = [];
|
|
30934
|
-
|
|
30935
|
-
|
|
30936
|
-
|
|
30937
|
-
|
|
30938
|
-
|
|
31006
|
+
const wantsRecent = args.sort === "recent" && defaultTypes.includes("fact") && ctx.vaultCtx !== void 0;
|
|
31007
|
+
let result;
|
|
31008
|
+
if (wantsRecent) {
|
|
31009
|
+
result = { memories: await listRecentFacts(ctx, effectiveLimit, toolOptions) };
|
|
31010
|
+
} else {
|
|
31011
|
+
let subQueries;
|
|
31012
|
+
if (defaultBudget === "high" && toolOptions?.decomposeOptions) {
|
|
31013
|
+
const decomp = await decomposeQuery(query, toolOptions.decomposeOptions);
|
|
31014
|
+
if (decomp.mode === "composite" && decomp.subQueries.length >= 2) {
|
|
31015
|
+
subQueries = decomp.subQueries;
|
|
31016
|
+
}
|
|
30939
31017
|
}
|
|
31018
|
+
const recallOpts = {
|
|
31019
|
+
onDiagnostics: (d) => {
|
|
31020
|
+
recallDegraded = d.degraded;
|
|
31021
|
+
},
|
|
31022
|
+
types: defaultTypes,
|
|
31023
|
+
limit: effectiveLimit,
|
|
31024
|
+
budget: defaultBudget,
|
|
31025
|
+
...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
|
|
31026
|
+
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
31027
|
+
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
31028
|
+
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
|
|
31029
|
+
...toolOptions?.excludeConversationId && {
|
|
31030
|
+
excludeConversationId: toolOptions.excludeConversationId
|
|
31031
|
+
},
|
|
31032
|
+
// Do NOT forward decomposeOptions into recall() — rewrite already
|
|
31033
|
+
// ran above, and forwarding would falsely trip `decompose-moved`
|
|
31034
|
+
// on every specific-mode high-budget call. graphRefine auth can
|
|
31035
|
+
// be threaded later when the tool opts into that path.
|
|
31036
|
+
...toolOptions?.now !== void 0 && { now: toolOptions.now },
|
|
31037
|
+
...subQueries && { subQueries }
|
|
31038
|
+
};
|
|
31039
|
+
result = await recall(query, ctx, recallOpts);
|
|
30940
31040
|
}
|
|
30941
|
-
const recallOpts = {
|
|
30942
|
-
onDiagnostics: (d) => {
|
|
30943
|
-
recallDegraded = d.degraded;
|
|
30944
|
-
},
|
|
30945
|
-
types: defaultTypes,
|
|
30946
|
-
limit: effectiveLimit,
|
|
30947
|
-
budget: defaultBudget,
|
|
30948
|
-
...toolOptions?.minScore !== void 0 && { minScore: toolOptions.minScore },
|
|
30949
|
-
...toolOptions?.scopes && { scopes: toolOptions.scopes },
|
|
30950
|
-
...toolOptions?.memoryIds !== void 0 && { memoryIds: toolOptions.memoryIds },
|
|
30951
|
-
...toolOptions?.folderId !== void 0 && { folderId: toolOptions.folderId },
|
|
30952
|
-
...toolOptions?.excludeConversationId && {
|
|
30953
|
-
excludeConversationId: toolOptions.excludeConversationId
|
|
30954
|
-
},
|
|
30955
|
-
// Do NOT forward decomposeOptions into recall() — rewrite already
|
|
30956
|
-
// ran above, and forwarding would falsely trip `decompose-moved`
|
|
30957
|
-
// on every specific-mode high-budget call. graphRefine auth can
|
|
30958
|
-
// be threaded later when the tool opts into that path.
|
|
30959
|
-
...toolOptions?.now !== void 0 && { now: toolOptions.now },
|
|
30960
|
-
...subQueries && { subQueries }
|
|
30961
|
-
};
|
|
30962
|
-
const result = await recall(query, ctx, recallOpts);
|
|
30963
31041
|
if (callbacks?.onChunksRetrieved) {
|
|
30964
31042
|
const convIds = Array.from(
|
|
30965
31043
|
new Set(
|
|
@@ -30972,7 +31050,7 @@ function createRecallTool(ctx, toolOptions, callbacks) {
|
|
|
30972
31050
|
const facts = result.memories.filter((m) => m.kind === "fact").map((m) => ({ id: m.id, score: m.score }));
|
|
30973
31051
|
if (facts.length > 0) {
|
|
30974
31052
|
callbacks.onFactsRetrieved?.(facts.map((f) => f.id));
|
|
30975
|
-
callbacks.onFactsRanked?.(facts);
|
|
31053
|
+
if (!wantsRecent) callbacks.onFactsRanked?.(facts);
|
|
30976
31054
|
}
|
|
30977
31055
|
}
|
|
30978
31056
|
surfaced = result.memories.length;
|
|
@@ -32153,6 +32231,10 @@ function createMessageSourceResolver(ctx) {
|
|
|
32153
32231
|
}
|
|
32154
32232
|
|
|
32155
32233
|
// src/lib/memory/context.ts
|
|
32234
|
+
function savedAt(createdAt) {
|
|
32235
|
+
const ms = createdAt?.getTime();
|
|
32236
|
+
return ms !== void 0 && Number.isFinite(ms) && ms > 0 ? { createdAt } : {};
|
|
32237
|
+
}
|
|
32156
32238
|
function shouldRecallMemory(query) {
|
|
32157
32239
|
const normalized = query.trim().toLowerCase().replace(/[.!?。!?¡¿]+$/u, "").replace(/^[¡¿]+/u, "");
|
|
32158
32240
|
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(
|
|
@@ -32190,7 +32272,8 @@ async function assembleMemoryContext(options) {
|
|
|
32190
32272
|
content: m.content,
|
|
32191
32273
|
score,
|
|
32192
32274
|
lane,
|
|
32193
|
-
sourceIds: m.sourceChunkIds ?? []
|
|
32275
|
+
sourceIds: m.sourceChunkIds ?? [],
|
|
32276
|
+
...savedAt(m.createdAt)
|
|
32194
32277
|
});
|
|
32195
32278
|
const empty = {
|
|
32196
32279
|
memories: [],
|
|
@@ -32255,7 +32338,8 @@ async function assembleMemoryContext(options) {
|
|
|
32255
32338
|
score: m.score,
|
|
32256
32339
|
lane,
|
|
32257
32340
|
...lane === "fact" && { recalled: true },
|
|
32258
|
-
sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : [])
|
|
32341
|
+
sourceIds: m.sourceChunkIds ?? (m.messageId ? [m.messageId] : []),
|
|
32342
|
+
...savedAt(m.createdAt)
|
|
32259
32343
|
}));
|
|
32260
32344
|
const recalledFacts = toItems(facts, "fact");
|
|
32261
32345
|
const recalledScores = /* @__PURE__ */ new Map();
|
package/dist/server/index.d.mts
CHANGED
|
@@ -10200,6 +10200,9 @@ interface MemoryContextItem {
|
|
|
10200
10200
|
sourceIds: string[];
|
|
10201
10201
|
/** Ranked fact evidence, retained when profile deduplication wins placement. */
|
|
10202
10202
|
recalled?: boolean;
|
|
10203
|
+
/** When the memory was written — lets the prompt show a saved date so the
|
|
10204
|
+
* model can answer "what did I save recently". */
|
|
10205
|
+
createdAt?: Date;
|
|
10203
10206
|
}
|
|
10204
10207
|
interface MemoryContextOptions {
|
|
10205
10208
|
query: string;
|