@anuma/sdk 6.41.0 → 6.42.1
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 +47 -30
- package/dist/expo/index.d.mts +14 -0
- package/dist/expo/index.d.ts +14 -0
- package/dist/expo/index.mjs +47 -30
- package/dist/react/index.cjs +47 -30
- package/dist/react/index.d.mts +14 -0
- package/dist/react/index.d.ts +14 -0
- package/dist/react/index.mjs +47 -30
- package/dist/server/index.cjs +37 -16
- package/dist/server/index.d.mts +12 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.mjs +37 -16
- package/package.json +1 -1
package/dist/expo/index.cjs
CHANGED
|
@@ -20196,6 +20196,8 @@ var queryFields = [
|
|
|
20196
20196
|
"memory_ids",
|
|
20197
20197
|
"topics",
|
|
20198
20198
|
"updated_after",
|
|
20199
|
+
"topics_due",
|
|
20200
|
+
"topics_extracted",
|
|
20199
20201
|
"order",
|
|
20200
20202
|
"cursor",
|
|
20201
20203
|
"limit",
|
|
@@ -21083,11 +21085,6 @@ function sameTopics(stored2, next) {
|
|
|
21083
21085
|
const keys = new Set(stored2.map(key));
|
|
21084
21086
|
return next.every((t) => keys.has(key(t)));
|
|
21085
21087
|
}
|
|
21086
|
-
function extractionDue(memory) {
|
|
21087
|
-
const stamp = memory.topics_extracted_at;
|
|
21088
|
-
if (stamp === void 0 || stamp === null) return "fresh";
|
|
21089
|
-
return memory.updated_at > stamp || (memory.topics_extracted_version ?? 0) < TOPICS_EXTRACTION_VERSION ? "stale" : null;
|
|
21090
|
-
}
|
|
21091
21088
|
function canonicalTopics(row) {
|
|
21092
21089
|
return new Set((parseTopics(row.topics) ?? []).map((topic) => normalizeEntityName(topic.name)));
|
|
21093
21090
|
}
|
|
@@ -21336,17 +21333,16 @@ function createRemoteMemoryStore(options) {
|
|
|
21336
21333
|
};
|
|
21337
21334
|
const topicMaintenance = {
|
|
21338
21335
|
getTopicBacklog: async (backlogOptions = {}) => {
|
|
21339
|
-
const stale = [];
|
|
21340
|
-
const fresh = [];
|
|
21341
|
-
for (const { memory } of await pages(metadata, { order: "created" })) {
|
|
21342
|
-
if (memory.topics_user_managed) continue;
|
|
21343
|
-
const due = extractionDue(memory);
|
|
21344
|
-
if (due === "stale") stale.push(memory.memory_id);
|
|
21345
|
-
else if (due === "fresh") fresh.push(memory.memory_id);
|
|
21346
|
-
}
|
|
21347
21336
|
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
21337
|
+
const due = (extracted2, max) => pages(
|
|
21338
|
+
metadata,
|
|
21339
|
+
{ order: "created", topics_due: TOPICS_EXTRACTION_VERSION, topics_extracted: extracted2 },
|
|
21340
|
+
max
|
|
21341
|
+
);
|
|
21342
|
+
const stale = await due(true, limit);
|
|
21343
|
+
const fresh = stale.length < limit ? await due(false, limit - stale.length) : [];
|
|
21348
21344
|
return {
|
|
21349
|
-
pending: [...stale, ...fresh].
|
|
21345
|
+
pending: [...stale, ...fresh].map(({ memory }) => ({ uniqueId: memory.memory_id })),
|
|
21350
21346
|
linkedUnstamped: [],
|
|
21351
21347
|
topicsToRelink: [],
|
|
21352
21348
|
topicsBackfill: []
|
|
@@ -21843,12 +21839,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
|
|
|
21843
21839
|
interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
|
|
21844
21840
|
};
|
|
21845
21841
|
async function synthesizeProfile(ctx, options = {}) {
|
|
21846
|
-
|
|
21847
|
-
|
|
21842
|
+
const { store } = options;
|
|
21843
|
+
const vaultCtx = ctx.vaultCtx;
|
|
21844
|
+
if (!store && !vaultCtx) {
|
|
21845
|
+
throw new Error(
|
|
21846
|
+
"synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
|
|
21847
|
+
);
|
|
21848
21848
|
}
|
|
21849
|
-
if (!ctx.vaultCache) {
|
|
21850
|
-
throw new Error(
|
|
21849
|
+
if (!store && !ctx.vaultCache) {
|
|
21850
|
+
throw new Error(
|
|
21851
|
+
"synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
|
|
21852
|
+
);
|
|
21851
21853
|
}
|
|
21854
|
+
const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
|
|
21852
21855
|
const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
|
|
21853
21856
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
21854
21857
|
const config = {
|
|
@@ -21859,11 +21862,8 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
21859
21862
|
reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
|
|
21860
21863
|
};
|
|
21861
21864
|
const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
|
|
21862
|
-
const
|
|
21863
|
-
|
|
21864
|
-
includeDeleted: true,
|
|
21865
|
-
includeSuperseded: true
|
|
21866
|
-
});
|
|
21865
|
+
const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
|
|
21866
|
+
const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
|
|
21867
21867
|
const watermark = computeVaultWatermark(memories);
|
|
21868
21868
|
const observationTrends = summarizeObservationTrends(
|
|
21869
21869
|
memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
|
|
@@ -21873,11 +21873,14 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
21873
21873
|
}))
|
|
21874
21874
|
);
|
|
21875
21875
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
21876
|
+
const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
|
|
21877
|
+
const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
|
|
21878
|
+
const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
|
|
21876
21879
|
const hasStaleSections = previous?.sections.some((s) => s.stale);
|
|
21877
21880
|
const citesMissingFact = previous?.sections.some(
|
|
21878
21881
|
(s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
|
|
21879
21882
|
);
|
|
21880
|
-
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
|
|
21883
|
+
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
|
|
21881
21884
|
const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
|
|
21882
21885
|
if (sameOrder) {
|
|
21883
21886
|
if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
|
|
@@ -21890,12 +21893,13 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
21890
21893
|
};
|
|
21891
21894
|
}
|
|
21892
21895
|
const staleKeys = await computeStaleFacetKeys(
|
|
21893
|
-
|
|
21896
|
+
recallCtx,
|
|
21894
21897
|
memories,
|
|
21895
21898
|
facets,
|
|
21896
21899
|
previous,
|
|
21897
21900
|
watermark,
|
|
21898
|
-
options.reviewedMemoryIds
|
|
21901
|
+
options.reviewedMemoryIds,
|
|
21902
|
+
previousUnreadable
|
|
21899
21903
|
);
|
|
21900
21904
|
const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
21901
21905
|
const fallbackPriors = /* @__PURE__ */ new Map();
|
|
@@ -21915,7 +21919,7 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
21915
21919
|
if (prior && !staleKeys.has(facet.key)) {
|
|
21916
21920
|
return prior;
|
|
21917
21921
|
}
|
|
21918
|
-
return synthesizeFacet(facet,
|
|
21922
|
+
return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
|
|
21919
21923
|
})
|
|
21920
21924
|
);
|
|
21921
21925
|
const sections = settled.map((r, i) => {
|
|
@@ -21930,12 +21934,18 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
21930
21934
|
);
|
|
21931
21935
|
return fallbackSection(facet, fallbackPriors.get(facet.key));
|
|
21932
21936
|
});
|
|
21937
|
+
if (!sameUnreadable && unreadableMemoryIds.length > 0) {
|
|
21938
|
+
getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
|
|
21939
|
+
unreadable: unreadableMemoryIds.length
|
|
21940
|
+
});
|
|
21941
|
+
}
|
|
21933
21942
|
return {
|
|
21934
21943
|
version: PROFILE_DOC_VERSION,
|
|
21935
21944
|
sections,
|
|
21936
21945
|
vaultWatermark: watermark,
|
|
21937
21946
|
config,
|
|
21938
21947
|
observationTrends,
|
|
21948
|
+
...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
|
|
21939
21949
|
generatedAt: Date.now()
|
|
21940
21950
|
};
|
|
21941
21951
|
}
|
|
@@ -21969,11 +21979,13 @@ function computeVaultWatermark(memories) {
|
|
|
21969
21979
|
}
|
|
21970
21980
|
return max;
|
|
21971
21981
|
}
|
|
21972
|
-
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
|
|
21982
|
+
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
|
|
21973
21983
|
const allKeys = new Set(facets.map((f) => f.key));
|
|
21974
21984
|
if (previous && watermark < previous.vaultWatermark) return allKeys;
|
|
21975
21985
|
if (!previous) return allKeys;
|
|
21976
|
-
const changed = memories.filter(
|
|
21986
|
+
const changed = memories.filter(
|
|
21987
|
+
(m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
|
|
21988
|
+
);
|
|
21977
21989
|
const changedIds = new Set(changed.map((m) => m.uniqueId));
|
|
21978
21990
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
21979
21991
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -22047,7 +22059,12 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
22047
22059
|
for (const fv of factVectors) {
|
|
22048
22060
|
let matchedAny = false;
|
|
22049
22061
|
for (let i = 0; i < facets.length; i++) {
|
|
22050
|
-
|
|
22062
|
+
const query = queryVectors[i];
|
|
22063
|
+
const similarity = cosineSimilarity(
|
|
22064
|
+
fv,
|
|
22065
|
+
fv.length < query.length ? query.slice(0, fv.length) : query
|
|
22066
|
+
);
|
|
22067
|
+
if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
|
|
22051
22068
|
keys.add(facets[i].key);
|
|
22052
22069
|
matchedAny = true;
|
|
22053
22070
|
}
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -9590,6 +9590,8 @@ interface RemoteMemoryQueryOptions extends RemoteMemoryReadFilters {
|
|
|
9590
9590
|
memory_ids?: string[];
|
|
9591
9591
|
topics?: string[];
|
|
9592
9592
|
updated_after?: number;
|
|
9593
|
+
topics_due?: number;
|
|
9594
|
+
topics_extracted?: boolean;
|
|
9593
9595
|
order?: "created" | "updated" | "archived" | "changed";
|
|
9594
9596
|
cursor?: string;
|
|
9595
9597
|
limit?: number;
|
|
@@ -9821,6 +9823,12 @@ interface ProfileDoc {
|
|
|
9821
9823
|
* without another LLM pass. Recomputed every synthesis (not delta-cached).
|
|
9822
9824
|
*/
|
|
9823
9825
|
observationTrends: Record<ObservationTrend, number>;
|
|
9826
|
+
/**
|
|
9827
|
+
* Sorted ids the store listed but could not decrypt at synthesis time.
|
|
9828
|
+
* Absent when every memory was readable. A later call regenerates only when
|
|
9829
|
+
* this set changes, and attributes a memory once it becomes readable.
|
|
9830
|
+
*/
|
|
9831
|
+
unreadableMemoryIds?: string[];
|
|
9824
9832
|
/** Unix ms this doc was produced. */
|
|
9825
9833
|
generatedAt: number;
|
|
9826
9834
|
}
|
|
@@ -9874,6 +9882,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
|
|
|
9874
9882
|
* never accidentally disable the gate by finding it empty.
|
|
9875
9883
|
*/
|
|
9876
9884
|
reviewedMemoryIds?: readonly string[];
|
|
9885
|
+
/**
|
|
9886
|
+
* Read facts and recall through this store instead of `ctx.vaultCtx` and
|
|
9887
|
+
* `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
|
|
9888
|
+
* then read from `ctx`.
|
|
9889
|
+
*/
|
|
9890
|
+
store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
|
|
9877
9891
|
}
|
|
9878
9892
|
/**
|
|
9879
9893
|
* Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -9590,6 +9590,8 @@ interface RemoteMemoryQueryOptions extends RemoteMemoryReadFilters {
|
|
|
9590
9590
|
memory_ids?: string[];
|
|
9591
9591
|
topics?: string[];
|
|
9592
9592
|
updated_after?: number;
|
|
9593
|
+
topics_due?: number;
|
|
9594
|
+
topics_extracted?: boolean;
|
|
9593
9595
|
order?: "created" | "updated" | "archived" | "changed";
|
|
9594
9596
|
cursor?: string;
|
|
9595
9597
|
limit?: number;
|
|
@@ -9821,6 +9823,12 @@ interface ProfileDoc {
|
|
|
9821
9823
|
* without another LLM pass. Recomputed every synthesis (not delta-cached).
|
|
9822
9824
|
*/
|
|
9823
9825
|
observationTrends: Record<ObservationTrend, number>;
|
|
9826
|
+
/**
|
|
9827
|
+
* Sorted ids the store listed but could not decrypt at synthesis time.
|
|
9828
|
+
* Absent when every memory was readable. A later call regenerates only when
|
|
9829
|
+
* this set changes, and attributes a memory once it becomes readable.
|
|
9830
|
+
*/
|
|
9831
|
+
unreadableMemoryIds?: string[];
|
|
9824
9832
|
/** Unix ms this doc was produced. */
|
|
9825
9833
|
generatedAt: number;
|
|
9826
9834
|
}
|
|
@@ -9874,6 +9882,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
|
|
|
9874
9882
|
* never accidentally disable the gate by finding it empty.
|
|
9875
9883
|
*/
|
|
9876
9884
|
reviewedMemoryIds?: readonly string[];
|
|
9885
|
+
/**
|
|
9886
|
+
* Read facts and recall through this store instead of `ctx.vaultCtx` and
|
|
9887
|
+
* `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
|
|
9888
|
+
* then read from `ctx`.
|
|
9889
|
+
*/
|
|
9890
|
+
store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
|
|
9877
9891
|
}
|
|
9878
9892
|
/**
|
|
9879
9893
|
* Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
|
package/dist/expo/index.mjs
CHANGED
|
@@ -18563,6 +18563,8 @@ var queryFields = [
|
|
|
18563
18563
|
"memory_ids",
|
|
18564
18564
|
"topics",
|
|
18565
18565
|
"updated_after",
|
|
18566
|
+
"topics_due",
|
|
18567
|
+
"topics_extracted",
|
|
18566
18568
|
"order",
|
|
18567
18569
|
"cursor",
|
|
18568
18570
|
"limit",
|
|
@@ -19448,11 +19450,6 @@ function sameTopics(stored2, next) {
|
|
|
19448
19450
|
const keys = new Set(stored2.map(key));
|
|
19449
19451
|
return next.every((t) => keys.has(key(t)));
|
|
19450
19452
|
}
|
|
19451
|
-
function extractionDue(memory) {
|
|
19452
|
-
const stamp = memory.topics_extracted_at;
|
|
19453
|
-
if (stamp === void 0 || stamp === null) return "fresh";
|
|
19454
|
-
return memory.updated_at > stamp || (memory.topics_extracted_version ?? 0) < TOPICS_EXTRACTION_VERSION ? "stale" : null;
|
|
19455
|
-
}
|
|
19456
19453
|
function canonicalTopics(row) {
|
|
19457
19454
|
return new Set((parseTopics(row.topics) ?? []).map((topic) => normalizeEntityName(topic.name)));
|
|
19458
19455
|
}
|
|
@@ -19701,17 +19698,16 @@ function createRemoteMemoryStore(options) {
|
|
|
19701
19698
|
};
|
|
19702
19699
|
const topicMaintenance = {
|
|
19703
19700
|
getTopicBacklog: async (backlogOptions = {}) => {
|
|
19704
|
-
const stale = [];
|
|
19705
|
-
const fresh = [];
|
|
19706
|
-
for (const { memory } of await pages(metadata, { order: "created" })) {
|
|
19707
|
-
if (memory.topics_user_managed) continue;
|
|
19708
|
-
const due = extractionDue(memory);
|
|
19709
|
-
if (due === "stale") stale.push(memory.memory_id);
|
|
19710
|
-
else if (due === "fresh") fresh.push(memory.memory_id);
|
|
19711
|
-
}
|
|
19712
19701
|
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
19702
|
+
const due = (extracted2, max) => pages(
|
|
19703
|
+
metadata,
|
|
19704
|
+
{ order: "created", topics_due: TOPICS_EXTRACTION_VERSION, topics_extracted: extracted2 },
|
|
19705
|
+
max
|
|
19706
|
+
);
|
|
19707
|
+
const stale = await due(true, limit);
|
|
19708
|
+
const fresh = stale.length < limit ? await due(false, limit - stale.length) : [];
|
|
19713
19709
|
return {
|
|
19714
|
-
pending: [...stale, ...fresh].
|
|
19710
|
+
pending: [...stale, ...fresh].map(({ memory }) => ({ uniqueId: memory.memory_id })),
|
|
19715
19711
|
linkedUnstamped: [],
|
|
19716
19712
|
topicsToRelink: [],
|
|
19717
19713
|
topicsBackfill: []
|
|
@@ -20206,12 +20202,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
|
|
|
20206
20202
|
interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
|
|
20207
20203
|
};
|
|
20208
20204
|
async function synthesizeProfile(ctx, options = {}) {
|
|
20209
|
-
|
|
20210
|
-
|
|
20205
|
+
const { store } = options;
|
|
20206
|
+
const vaultCtx = ctx.vaultCtx;
|
|
20207
|
+
if (!store && !vaultCtx) {
|
|
20208
|
+
throw new Error(
|
|
20209
|
+
"synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
|
|
20210
|
+
);
|
|
20211
20211
|
}
|
|
20212
|
-
if (!ctx.vaultCache) {
|
|
20213
|
-
throw new Error(
|
|
20212
|
+
if (!store && !ctx.vaultCache) {
|
|
20213
|
+
throw new Error(
|
|
20214
|
+
"synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
|
|
20215
|
+
);
|
|
20214
20216
|
}
|
|
20217
|
+
const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
|
|
20215
20218
|
const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
|
|
20216
20219
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
20217
20220
|
const config = {
|
|
@@ -20222,11 +20225,8 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
20222
20225
|
reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
|
|
20223
20226
|
};
|
|
20224
20227
|
const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
|
|
20225
|
-
const
|
|
20226
|
-
|
|
20227
|
-
includeDeleted: true,
|
|
20228
|
-
includeSuperseded: true
|
|
20229
|
-
});
|
|
20228
|
+
const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
|
|
20229
|
+
const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
|
|
20230
20230
|
const watermark = computeVaultWatermark(memories);
|
|
20231
20231
|
const observationTrends = summarizeObservationTrends(
|
|
20232
20232
|
memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
|
|
@@ -20236,11 +20236,14 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
20236
20236
|
}))
|
|
20237
20237
|
);
|
|
20238
20238
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
20239
|
+
const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
|
|
20240
|
+
const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
|
|
20241
|
+
const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
|
|
20239
20242
|
const hasStaleSections = previous?.sections.some((s) => s.stale);
|
|
20240
20243
|
const citesMissingFact = previous?.sections.some(
|
|
20241
20244
|
(s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
|
|
20242
20245
|
);
|
|
20243
|
-
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
|
|
20246
|
+
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
|
|
20244
20247
|
const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
|
|
20245
20248
|
if (sameOrder) {
|
|
20246
20249
|
if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
|
|
@@ -20253,12 +20256,13 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
20253
20256
|
};
|
|
20254
20257
|
}
|
|
20255
20258
|
const staleKeys = await computeStaleFacetKeys(
|
|
20256
|
-
|
|
20259
|
+
recallCtx,
|
|
20257
20260
|
memories,
|
|
20258
20261
|
facets,
|
|
20259
20262
|
previous,
|
|
20260
20263
|
watermark,
|
|
20261
|
-
options.reviewedMemoryIds
|
|
20264
|
+
options.reviewedMemoryIds,
|
|
20265
|
+
previousUnreadable
|
|
20262
20266
|
);
|
|
20263
20267
|
const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
20264
20268
|
const fallbackPriors = /* @__PURE__ */ new Map();
|
|
@@ -20278,7 +20282,7 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
20278
20282
|
if (prior && !staleKeys.has(facet.key)) {
|
|
20279
20283
|
return prior;
|
|
20280
20284
|
}
|
|
20281
|
-
return synthesizeFacet(facet,
|
|
20285
|
+
return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
|
|
20282
20286
|
})
|
|
20283
20287
|
);
|
|
20284
20288
|
const sections = settled.map((r, i) => {
|
|
@@ -20293,12 +20297,18 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
20293
20297
|
);
|
|
20294
20298
|
return fallbackSection(facet, fallbackPriors.get(facet.key));
|
|
20295
20299
|
});
|
|
20300
|
+
if (!sameUnreadable && unreadableMemoryIds.length > 0) {
|
|
20301
|
+
getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
|
|
20302
|
+
unreadable: unreadableMemoryIds.length
|
|
20303
|
+
});
|
|
20304
|
+
}
|
|
20296
20305
|
return {
|
|
20297
20306
|
version: PROFILE_DOC_VERSION,
|
|
20298
20307
|
sections,
|
|
20299
20308
|
vaultWatermark: watermark,
|
|
20300
20309
|
config,
|
|
20301
20310
|
observationTrends,
|
|
20311
|
+
...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
|
|
20302
20312
|
generatedAt: Date.now()
|
|
20303
20313
|
};
|
|
20304
20314
|
}
|
|
@@ -20332,11 +20342,13 @@ function computeVaultWatermark(memories) {
|
|
|
20332
20342
|
}
|
|
20333
20343
|
return max;
|
|
20334
20344
|
}
|
|
20335
|
-
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
|
|
20345
|
+
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
|
|
20336
20346
|
const allKeys = new Set(facets.map((f) => f.key));
|
|
20337
20347
|
if (previous && watermark < previous.vaultWatermark) return allKeys;
|
|
20338
20348
|
if (!previous) return allKeys;
|
|
20339
|
-
const changed = memories.filter(
|
|
20349
|
+
const changed = memories.filter(
|
|
20350
|
+
(m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
|
|
20351
|
+
);
|
|
20340
20352
|
const changedIds = new Set(changed.map((m) => m.uniqueId));
|
|
20341
20353
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
20342
20354
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -20410,7 +20422,12 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
20410
20422
|
for (const fv of factVectors) {
|
|
20411
20423
|
let matchedAny = false;
|
|
20412
20424
|
for (let i = 0; i < facets.length; i++) {
|
|
20413
|
-
|
|
20425
|
+
const query = queryVectors[i];
|
|
20426
|
+
const similarity = cosineSimilarity(
|
|
20427
|
+
fv,
|
|
20428
|
+
fv.length < query.length ? query.slice(0, fv.length) : query
|
|
20429
|
+
);
|
|
20430
|
+
if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
|
|
20414
20431
|
keys.add(facets[i].key);
|
|
20415
20432
|
matchedAny = true;
|
|
20416
20433
|
}
|
package/dist/react/index.cjs
CHANGED
|
@@ -23569,6 +23569,8 @@ var queryFields = [
|
|
|
23569
23569
|
"memory_ids",
|
|
23570
23570
|
"topics",
|
|
23571
23571
|
"updated_after",
|
|
23572
|
+
"topics_due",
|
|
23573
|
+
"topics_extracted",
|
|
23572
23574
|
"order",
|
|
23573
23575
|
"cursor",
|
|
23574
23576
|
"limit",
|
|
@@ -24456,11 +24458,6 @@ function sameTopics(stored2, next) {
|
|
|
24456
24458
|
const keys = new Set(stored2.map(key));
|
|
24457
24459
|
return next.every((t) => keys.has(key(t)));
|
|
24458
24460
|
}
|
|
24459
|
-
function extractionDue(memory) {
|
|
24460
|
-
const stamp = memory.topics_extracted_at;
|
|
24461
|
-
if (stamp === void 0 || stamp === null) return "fresh";
|
|
24462
|
-
return memory.updated_at > stamp || (memory.topics_extracted_version ?? 0) < TOPICS_EXTRACTION_VERSION ? "stale" : null;
|
|
24463
|
-
}
|
|
24464
24461
|
function canonicalTopics(row) {
|
|
24465
24462
|
return new Set((parseTopics(row.topics) ?? []).map((topic) => normalizeEntityName(topic.name)));
|
|
24466
24463
|
}
|
|
@@ -24709,17 +24706,16 @@ function createRemoteMemoryStore(options) {
|
|
|
24709
24706
|
};
|
|
24710
24707
|
const topicMaintenance = {
|
|
24711
24708
|
getTopicBacklog: async (backlogOptions = {}) => {
|
|
24712
|
-
const stale = [];
|
|
24713
|
-
const fresh = [];
|
|
24714
|
-
for (const { memory } of await pages(metadata, { order: "created" })) {
|
|
24715
|
-
if (memory.topics_user_managed) continue;
|
|
24716
|
-
const due = extractionDue(memory);
|
|
24717
|
-
if (due === "stale") stale.push(memory.memory_id);
|
|
24718
|
-
else if (due === "fresh") fresh.push(memory.memory_id);
|
|
24719
|
-
}
|
|
24720
24709
|
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
24710
|
+
const due = (extracted2, max) => pages(
|
|
24711
|
+
metadata,
|
|
24712
|
+
{ order: "created", topics_due: TOPICS_EXTRACTION_VERSION, topics_extracted: extracted2 },
|
|
24713
|
+
max
|
|
24714
|
+
);
|
|
24715
|
+
const stale = await due(true, limit);
|
|
24716
|
+
const fresh = stale.length < limit ? await due(false, limit - stale.length) : [];
|
|
24721
24717
|
return {
|
|
24722
|
-
pending: [...stale, ...fresh].
|
|
24718
|
+
pending: [...stale, ...fresh].map(({ memory }) => ({ uniqueId: memory.memory_id })),
|
|
24723
24719
|
linkedUnstamped: [],
|
|
24724
24720
|
topicsToRelink: [],
|
|
24725
24721
|
topicsBackfill: []
|
|
@@ -25216,12 +25212,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
|
|
|
25216
25212
|
interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
|
|
25217
25213
|
};
|
|
25218
25214
|
async function synthesizeProfile(ctx, options = {}) {
|
|
25219
|
-
|
|
25220
|
-
|
|
25215
|
+
const { store } = options;
|
|
25216
|
+
const vaultCtx = ctx.vaultCtx;
|
|
25217
|
+
if (!store && !vaultCtx) {
|
|
25218
|
+
throw new Error(
|
|
25219
|
+
"synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
|
|
25220
|
+
);
|
|
25221
25221
|
}
|
|
25222
|
-
if (!ctx.vaultCache) {
|
|
25223
|
-
throw new Error(
|
|
25222
|
+
if (!store && !ctx.vaultCache) {
|
|
25223
|
+
throw new Error(
|
|
25224
|
+
"synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
|
|
25225
|
+
);
|
|
25224
25226
|
}
|
|
25227
|
+
const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
|
|
25225
25228
|
const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
|
|
25226
25229
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
25227
25230
|
const config = {
|
|
@@ -25232,11 +25235,8 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
25232
25235
|
reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
|
|
25233
25236
|
};
|
|
25234
25237
|
const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
|
|
25235
|
-
const
|
|
25236
|
-
|
|
25237
|
-
includeDeleted: true,
|
|
25238
|
-
includeSuperseded: true
|
|
25239
|
-
});
|
|
25238
|
+
const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
|
|
25239
|
+
const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
|
|
25240
25240
|
const watermark = computeVaultWatermark(memories);
|
|
25241
25241
|
const observationTrends = summarizeObservationTrends(
|
|
25242
25242
|
memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
|
|
@@ -25246,11 +25246,14 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
25246
25246
|
}))
|
|
25247
25247
|
);
|
|
25248
25248
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
25249
|
+
const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
|
|
25250
|
+
const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
|
|
25251
|
+
const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
|
|
25249
25252
|
const hasStaleSections = previous?.sections.some((s) => s.stale);
|
|
25250
25253
|
const citesMissingFact = previous?.sections.some(
|
|
25251
25254
|
(s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
|
|
25252
25255
|
);
|
|
25253
|
-
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
|
|
25256
|
+
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
|
|
25254
25257
|
const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
|
|
25255
25258
|
if (sameOrder) {
|
|
25256
25259
|
if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
|
|
@@ -25263,12 +25266,13 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
25263
25266
|
};
|
|
25264
25267
|
}
|
|
25265
25268
|
const staleKeys = await computeStaleFacetKeys(
|
|
25266
|
-
|
|
25269
|
+
recallCtx,
|
|
25267
25270
|
memories,
|
|
25268
25271
|
facets,
|
|
25269
25272
|
previous,
|
|
25270
25273
|
watermark,
|
|
25271
|
-
options.reviewedMemoryIds
|
|
25274
|
+
options.reviewedMemoryIds,
|
|
25275
|
+
previousUnreadable
|
|
25272
25276
|
);
|
|
25273
25277
|
const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
25274
25278
|
const fallbackPriors = /* @__PURE__ */ new Map();
|
|
@@ -25288,7 +25292,7 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
25288
25292
|
if (prior && !staleKeys.has(facet.key)) {
|
|
25289
25293
|
return prior;
|
|
25290
25294
|
}
|
|
25291
|
-
return synthesizeFacet(facet,
|
|
25295
|
+
return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
|
|
25292
25296
|
})
|
|
25293
25297
|
);
|
|
25294
25298
|
const sections = settled.map((r, i) => {
|
|
@@ -25303,12 +25307,18 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
25303
25307
|
);
|
|
25304
25308
|
return fallbackSection(facet, fallbackPriors.get(facet.key));
|
|
25305
25309
|
});
|
|
25310
|
+
if (!sameUnreadable && unreadableMemoryIds.length > 0) {
|
|
25311
|
+
getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
|
|
25312
|
+
unreadable: unreadableMemoryIds.length
|
|
25313
|
+
});
|
|
25314
|
+
}
|
|
25306
25315
|
return {
|
|
25307
25316
|
version: PROFILE_DOC_VERSION,
|
|
25308
25317
|
sections,
|
|
25309
25318
|
vaultWatermark: watermark,
|
|
25310
25319
|
config,
|
|
25311
25320
|
observationTrends,
|
|
25321
|
+
...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
|
|
25312
25322
|
generatedAt: Date.now()
|
|
25313
25323
|
};
|
|
25314
25324
|
}
|
|
@@ -25342,11 +25352,13 @@ function computeVaultWatermark(memories) {
|
|
|
25342
25352
|
}
|
|
25343
25353
|
return max;
|
|
25344
25354
|
}
|
|
25345
|
-
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
|
|
25355
|
+
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
|
|
25346
25356
|
const allKeys = new Set(facets.map((f) => f.key));
|
|
25347
25357
|
if (previous && watermark < previous.vaultWatermark) return allKeys;
|
|
25348
25358
|
if (!previous) return allKeys;
|
|
25349
|
-
const changed = memories.filter(
|
|
25359
|
+
const changed = memories.filter(
|
|
25360
|
+
(m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
|
|
25361
|
+
);
|
|
25350
25362
|
const changedIds = new Set(changed.map((m) => m.uniqueId));
|
|
25351
25363
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
25352
25364
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -25420,7 +25432,12 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
25420
25432
|
for (const fv of factVectors) {
|
|
25421
25433
|
let matchedAny = false;
|
|
25422
25434
|
for (let i = 0; i < facets.length; i++) {
|
|
25423
|
-
|
|
25435
|
+
const query = queryVectors[i];
|
|
25436
|
+
const similarity = cosineSimilarity(
|
|
25437
|
+
fv,
|
|
25438
|
+
fv.length < query.length ? query.slice(0, fv.length) : query
|
|
25439
|
+
);
|
|
25440
|
+
if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
|
|
25424
25441
|
keys.add(facets[i].key);
|
|
25425
25442
|
matchedAny = true;
|
|
25426
25443
|
}
|
package/dist/react/index.d.mts
CHANGED
|
@@ -11826,6 +11826,8 @@ interface RemoteMemoryQueryOptions extends RemoteMemoryReadFilters {
|
|
|
11826
11826
|
memory_ids?: string[];
|
|
11827
11827
|
topics?: string[];
|
|
11828
11828
|
updated_after?: number;
|
|
11829
|
+
topics_due?: number;
|
|
11830
|
+
topics_extracted?: boolean;
|
|
11829
11831
|
order?: "created" | "updated" | "archived" | "changed";
|
|
11830
11832
|
cursor?: string;
|
|
11831
11833
|
limit?: number;
|
|
@@ -12057,6 +12059,12 @@ interface ProfileDoc {
|
|
|
12057
12059
|
* without another LLM pass. Recomputed every synthesis (not delta-cached).
|
|
12058
12060
|
*/
|
|
12059
12061
|
observationTrends: Record<ObservationTrend, number>;
|
|
12062
|
+
/**
|
|
12063
|
+
* Sorted ids the store listed but could not decrypt at synthesis time.
|
|
12064
|
+
* Absent when every memory was readable. A later call regenerates only when
|
|
12065
|
+
* this set changes, and attributes a memory once it becomes readable.
|
|
12066
|
+
*/
|
|
12067
|
+
unreadableMemoryIds?: string[];
|
|
12060
12068
|
/** Unix ms this doc was produced. */
|
|
12061
12069
|
generatedAt: number;
|
|
12062
12070
|
}
|
|
@@ -12110,6 +12118,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
|
|
|
12110
12118
|
* never accidentally disable the gate by finding it empty.
|
|
12111
12119
|
*/
|
|
12112
12120
|
reviewedMemoryIds?: readonly string[];
|
|
12121
|
+
/**
|
|
12122
|
+
* Read facts and recall through this store instead of `ctx.vaultCtx` and
|
|
12123
|
+
* `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
|
|
12124
|
+
* then read from `ctx`.
|
|
12125
|
+
*/
|
|
12126
|
+
store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
|
|
12113
12127
|
}
|
|
12114
12128
|
/**
|
|
12115
12129
|
* Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
|
package/dist/react/index.d.ts
CHANGED
|
@@ -11826,6 +11826,8 @@ interface RemoteMemoryQueryOptions extends RemoteMemoryReadFilters {
|
|
|
11826
11826
|
memory_ids?: string[];
|
|
11827
11827
|
topics?: string[];
|
|
11828
11828
|
updated_after?: number;
|
|
11829
|
+
topics_due?: number;
|
|
11830
|
+
topics_extracted?: boolean;
|
|
11829
11831
|
order?: "created" | "updated" | "archived" | "changed";
|
|
11830
11832
|
cursor?: string;
|
|
11831
11833
|
limit?: number;
|
|
@@ -12057,6 +12059,12 @@ interface ProfileDoc {
|
|
|
12057
12059
|
* without another LLM pass. Recomputed every synthesis (not delta-cached).
|
|
12058
12060
|
*/
|
|
12059
12061
|
observationTrends: Record<ObservationTrend, number>;
|
|
12062
|
+
/**
|
|
12063
|
+
* Sorted ids the store listed but could not decrypt at synthesis time.
|
|
12064
|
+
* Absent when every memory was readable. A later call regenerates only when
|
|
12065
|
+
* this set changes, and attributes a memory once it becomes readable.
|
|
12066
|
+
*/
|
|
12067
|
+
unreadableMemoryIds?: string[];
|
|
12060
12068
|
/** Unix ms this doc was produced. */
|
|
12061
12069
|
generatedAt: number;
|
|
12062
12070
|
}
|
|
@@ -12110,6 +12118,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
|
|
|
12110
12118
|
* never accidentally disable the gate by finding it empty.
|
|
12111
12119
|
*/
|
|
12112
12120
|
reviewedMemoryIds?: readonly string[];
|
|
12121
|
+
/**
|
|
12122
|
+
* Read facts and recall through this store instead of `ctx.vaultCtx` and
|
|
12123
|
+
* `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
|
|
12124
|
+
* then read from `ctx`.
|
|
12125
|
+
*/
|
|
12126
|
+
store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
|
|
12113
12127
|
}
|
|
12114
12128
|
/**
|
|
12115
12129
|
* Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
|
package/dist/react/index.mjs
CHANGED
|
@@ -21665,6 +21665,8 @@ var queryFields = [
|
|
|
21665
21665
|
"memory_ids",
|
|
21666
21666
|
"topics",
|
|
21667
21667
|
"updated_after",
|
|
21668
|
+
"topics_due",
|
|
21669
|
+
"topics_extracted",
|
|
21668
21670
|
"order",
|
|
21669
21671
|
"cursor",
|
|
21670
21672
|
"limit",
|
|
@@ -22550,11 +22552,6 @@ function sameTopics(stored2, next) {
|
|
|
22550
22552
|
const keys = new Set(stored2.map(key));
|
|
22551
22553
|
return next.every((t) => keys.has(key(t)));
|
|
22552
22554
|
}
|
|
22553
|
-
function extractionDue(memory) {
|
|
22554
|
-
const stamp = memory.topics_extracted_at;
|
|
22555
|
-
if (stamp === void 0 || stamp === null) return "fresh";
|
|
22556
|
-
return memory.updated_at > stamp || (memory.topics_extracted_version ?? 0) < TOPICS_EXTRACTION_VERSION ? "stale" : null;
|
|
22557
|
-
}
|
|
22558
22555
|
function canonicalTopics(row) {
|
|
22559
22556
|
return new Set((parseTopics(row.topics) ?? []).map((topic) => normalizeEntityName(topic.name)));
|
|
22560
22557
|
}
|
|
@@ -22803,17 +22800,16 @@ function createRemoteMemoryStore(options) {
|
|
|
22803
22800
|
};
|
|
22804
22801
|
const topicMaintenance = {
|
|
22805
22802
|
getTopicBacklog: async (backlogOptions = {}) => {
|
|
22806
|
-
const stale = [];
|
|
22807
|
-
const fresh = [];
|
|
22808
|
-
for (const { memory } of await pages(metadata, { order: "created" })) {
|
|
22809
|
-
if (memory.topics_user_managed) continue;
|
|
22810
|
-
const due = extractionDue(memory);
|
|
22811
|
-
if (due === "stale") stale.push(memory.memory_id);
|
|
22812
|
-
else if (due === "fresh") fresh.push(memory.memory_id);
|
|
22813
|
-
}
|
|
22814
22803
|
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
22804
|
+
const due = (extracted2, max) => pages(
|
|
22805
|
+
metadata,
|
|
22806
|
+
{ order: "created", topics_due: TOPICS_EXTRACTION_VERSION, topics_extracted: extracted2 },
|
|
22807
|
+
max
|
|
22808
|
+
);
|
|
22809
|
+
const stale = await due(true, limit);
|
|
22810
|
+
const fresh = stale.length < limit ? await due(false, limit - stale.length) : [];
|
|
22815
22811
|
return {
|
|
22816
|
-
pending: [...stale, ...fresh].
|
|
22812
|
+
pending: [...stale, ...fresh].map(({ memory }) => ({ uniqueId: memory.memory_id })),
|
|
22817
22813
|
linkedUnstamped: [],
|
|
22818
22814
|
topicsToRelink: [],
|
|
22819
22815
|
topicsBackfill: []
|
|
@@ -23308,12 +23304,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
|
|
|
23308
23304
|
interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
|
|
23309
23305
|
};
|
|
23310
23306
|
async function synthesizeProfile(ctx, options = {}) {
|
|
23311
|
-
|
|
23312
|
-
|
|
23307
|
+
const { store } = options;
|
|
23308
|
+
const vaultCtx = ctx.vaultCtx;
|
|
23309
|
+
if (!store && !vaultCtx) {
|
|
23310
|
+
throw new Error(
|
|
23311
|
+
"synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
|
|
23312
|
+
);
|
|
23313
23313
|
}
|
|
23314
|
-
if (!ctx.vaultCache) {
|
|
23315
|
-
throw new Error(
|
|
23314
|
+
if (!store && !ctx.vaultCache) {
|
|
23315
|
+
throw new Error(
|
|
23316
|
+
"synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
|
|
23317
|
+
);
|
|
23316
23318
|
}
|
|
23319
|
+
const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
|
|
23317
23320
|
const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
|
|
23318
23321
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
23319
23322
|
const config = {
|
|
@@ -23324,11 +23327,8 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
23324
23327
|
reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
|
|
23325
23328
|
};
|
|
23326
23329
|
const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
|
|
23327
|
-
const
|
|
23328
|
-
|
|
23329
|
-
includeDeleted: true,
|
|
23330
|
-
includeSuperseded: true
|
|
23331
|
-
});
|
|
23330
|
+
const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
|
|
23331
|
+
const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
|
|
23332
23332
|
const watermark = computeVaultWatermark(memories);
|
|
23333
23333
|
const observationTrends = summarizeObservationTrends(
|
|
23334
23334
|
memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
|
|
@@ -23338,11 +23338,14 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
23338
23338
|
}))
|
|
23339
23339
|
);
|
|
23340
23340
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
23341
|
+
const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
|
|
23342
|
+
const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
|
|
23343
|
+
const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
|
|
23341
23344
|
const hasStaleSections = previous?.sections.some((s) => s.stale);
|
|
23342
23345
|
const citesMissingFact = previous?.sections.some(
|
|
23343
23346
|
(s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
|
|
23344
23347
|
);
|
|
23345
|
-
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
|
|
23348
|
+
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
|
|
23346
23349
|
const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
|
|
23347
23350
|
if (sameOrder) {
|
|
23348
23351
|
if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
|
|
@@ -23355,12 +23358,13 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
23355
23358
|
};
|
|
23356
23359
|
}
|
|
23357
23360
|
const staleKeys = await computeStaleFacetKeys(
|
|
23358
|
-
|
|
23361
|
+
recallCtx,
|
|
23359
23362
|
memories,
|
|
23360
23363
|
facets,
|
|
23361
23364
|
previous,
|
|
23362
23365
|
watermark,
|
|
23363
|
-
options.reviewedMemoryIds
|
|
23366
|
+
options.reviewedMemoryIds,
|
|
23367
|
+
previousUnreadable
|
|
23364
23368
|
);
|
|
23365
23369
|
const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
23366
23370
|
const fallbackPriors = /* @__PURE__ */ new Map();
|
|
@@ -23380,7 +23384,7 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
23380
23384
|
if (prior && !staleKeys.has(facet.key)) {
|
|
23381
23385
|
return prior;
|
|
23382
23386
|
}
|
|
23383
|
-
return synthesizeFacet(facet,
|
|
23387
|
+
return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
|
|
23384
23388
|
})
|
|
23385
23389
|
);
|
|
23386
23390
|
const sections = settled.map((r, i) => {
|
|
@@ -23395,12 +23399,18 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
23395
23399
|
);
|
|
23396
23400
|
return fallbackSection(facet, fallbackPriors.get(facet.key));
|
|
23397
23401
|
});
|
|
23402
|
+
if (!sameUnreadable && unreadableMemoryIds.length > 0) {
|
|
23403
|
+
getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
|
|
23404
|
+
unreadable: unreadableMemoryIds.length
|
|
23405
|
+
});
|
|
23406
|
+
}
|
|
23398
23407
|
return {
|
|
23399
23408
|
version: PROFILE_DOC_VERSION,
|
|
23400
23409
|
sections,
|
|
23401
23410
|
vaultWatermark: watermark,
|
|
23402
23411
|
config,
|
|
23403
23412
|
observationTrends,
|
|
23413
|
+
...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
|
|
23404
23414
|
generatedAt: Date.now()
|
|
23405
23415
|
};
|
|
23406
23416
|
}
|
|
@@ -23434,11 +23444,13 @@ function computeVaultWatermark(memories) {
|
|
|
23434
23444
|
}
|
|
23435
23445
|
return max;
|
|
23436
23446
|
}
|
|
23437
|
-
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
|
|
23447
|
+
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
|
|
23438
23448
|
const allKeys = new Set(facets.map((f) => f.key));
|
|
23439
23449
|
if (previous && watermark < previous.vaultWatermark) return allKeys;
|
|
23440
23450
|
if (!previous) return allKeys;
|
|
23441
|
-
const changed = memories.filter(
|
|
23451
|
+
const changed = memories.filter(
|
|
23452
|
+
(m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
|
|
23453
|
+
);
|
|
23442
23454
|
const changedIds = new Set(changed.map((m) => m.uniqueId));
|
|
23443
23455
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
23444
23456
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -23512,7 +23524,12 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
23512
23524
|
for (const fv of factVectors) {
|
|
23513
23525
|
let matchedAny = false;
|
|
23514
23526
|
for (let i = 0; i < facets.length; i++) {
|
|
23515
|
-
|
|
23527
|
+
const query = queryVectors[i];
|
|
23528
|
+
const similarity = cosineSimilarity(
|
|
23529
|
+
fv,
|
|
23530
|
+
fv.length < query.length ? query.slice(0, fv.length) : query
|
|
23531
|
+
);
|
|
23532
|
+
if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
|
|
23516
23533
|
keys.add(facets[i].key);
|
|
23517
23534
|
matchedAny = true;
|
|
23518
23535
|
}
|
package/dist/server/index.cjs
CHANGED
|
@@ -35411,12 +35411,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
|
|
|
35411
35411
|
interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
|
|
35412
35412
|
};
|
|
35413
35413
|
async function synthesizeProfile(ctx, options = {}) {
|
|
35414
|
-
|
|
35415
|
-
|
|
35414
|
+
const { store } = options;
|
|
35415
|
+
const vaultCtx = ctx.vaultCtx;
|
|
35416
|
+
if (!store && !vaultCtx) {
|
|
35417
|
+
throw new Error(
|
|
35418
|
+
"synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
|
|
35419
|
+
);
|
|
35416
35420
|
}
|
|
35417
|
-
if (!ctx.vaultCache) {
|
|
35418
|
-
throw new Error(
|
|
35421
|
+
if (!store && !ctx.vaultCache) {
|
|
35422
|
+
throw new Error(
|
|
35423
|
+
"synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
|
|
35424
|
+
);
|
|
35419
35425
|
}
|
|
35426
|
+
const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
|
|
35420
35427
|
const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
|
|
35421
35428
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
35422
35429
|
const config = {
|
|
@@ -35427,11 +35434,8 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
35427
35434
|
reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
|
|
35428
35435
|
};
|
|
35429
35436
|
const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
|
|
35430
|
-
const
|
|
35431
|
-
|
|
35432
|
-
includeDeleted: true,
|
|
35433
|
-
includeSuperseded: true
|
|
35434
|
-
});
|
|
35437
|
+
const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
|
|
35438
|
+
const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
|
|
35435
35439
|
const watermark = computeVaultWatermark(memories);
|
|
35436
35440
|
const observationTrends = summarizeObservationTrends(
|
|
35437
35441
|
memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
|
|
@@ -35441,11 +35445,14 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
35441
35445
|
}))
|
|
35442
35446
|
);
|
|
35443
35447
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
35448
|
+
const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
|
|
35449
|
+
const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
|
|
35450
|
+
const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
|
|
35444
35451
|
const hasStaleSections = previous?.sections.some((s) => s.stale);
|
|
35445
35452
|
const citesMissingFact = previous?.sections.some(
|
|
35446
35453
|
(s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
|
|
35447
35454
|
);
|
|
35448
|
-
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
|
|
35455
|
+
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
|
|
35449
35456
|
const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
|
|
35450
35457
|
if (sameOrder) {
|
|
35451
35458
|
if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
|
|
@@ -35458,12 +35465,13 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
35458
35465
|
};
|
|
35459
35466
|
}
|
|
35460
35467
|
const staleKeys = await computeStaleFacetKeys(
|
|
35461
|
-
|
|
35468
|
+
recallCtx,
|
|
35462
35469
|
memories,
|
|
35463
35470
|
facets,
|
|
35464
35471
|
previous,
|
|
35465
35472
|
watermark,
|
|
35466
|
-
options.reviewedMemoryIds
|
|
35473
|
+
options.reviewedMemoryIds,
|
|
35474
|
+
previousUnreadable
|
|
35467
35475
|
);
|
|
35468
35476
|
const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
35469
35477
|
const fallbackPriors = /* @__PURE__ */ new Map();
|
|
@@ -35483,7 +35491,7 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
35483
35491
|
if (prior && !staleKeys.has(facet.key)) {
|
|
35484
35492
|
return prior;
|
|
35485
35493
|
}
|
|
35486
|
-
return synthesizeFacet(facet,
|
|
35494
|
+
return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
|
|
35487
35495
|
})
|
|
35488
35496
|
);
|
|
35489
35497
|
const sections = settled.map((r, i) => {
|
|
@@ -35498,12 +35506,18 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
35498
35506
|
);
|
|
35499
35507
|
return fallbackSection(facet, fallbackPriors.get(facet.key));
|
|
35500
35508
|
});
|
|
35509
|
+
if (!sameUnreadable && unreadableMemoryIds.length > 0) {
|
|
35510
|
+
getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
|
|
35511
|
+
unreadable: unreadableMemoryIds.length
|
|
35512
|
+
});
|
|
35513
|
+
}
|
|
35501
35514
|
return {
|
|
35502
35515
|
version: PROFILE_DOC_VERSION,
|
|
35503
35516
|
sections,
|
|
35504
35517
|
vaultWatermark: watermark,
|
|
35505
35518
|
config,
|
|
35506
35519
|
observationTrends,
|
|
35520
|
+
...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
|
|
35507
35521
|
generatedAt: Date.now()
|
|
35508
35522
|
};
|
|
35509
35523
|
}
|
|
@@ -35537,11 +35551,13 @@ function computeVaultWatermark(memories) {
|
|
|
35537
35551
|
}
|
|
35538
35552
|
return max;
|
|
35539
35553
|
}
|
|
35540
|
-
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
|
|
35554
|
+
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
|
|
35541
35555
|
const allKeys = new Set(facets.map((f) => f.key));
|
|
35542
35556
|
if (previous && watermark < previous.vaultWatermark) return allKeys;
|
|
35543
35557
|
if (!previous) return allKeys;
|
|
35544
|
-
const changed = memories.filter(
|
|
35558
|
+
const changed = memories.filter(
|
|
35559
|
+
(m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
|
|
35560
|
+
);
|
|
35545
35561
|
const changedIds = new Set(changed.map((m) => m.uniqueId));
|
|
35546
35562
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
35547
35563
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -35615,7 +35631,12 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
35615
35631
|
for (const fv of factVectors) {
|
|
35616
35632
|
let matchedAny = false;
|
|
35617
35633
|
for (let i = 0; i < facets.length; i++) {
|
|
35618
|
-
|
|
35634
|
+
const query = queryVectors[i];
|
|
35635
|
+
const similarity = cosineSimilarity(
|
|
35636
|
+
fv,
|
|
35637
|
+
fv.length < query.length ? query.slice(0, fv.length) : query
|
|
35638
|
+
);
|
|
35639
|
+
if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
|
|
35619
35640
|
keys.add(facets[i].key);
|
|
35620
35641
|
matchedAny = true;
|
|
35621
35642
|
}
|
package/dist/server/index.d.mts
CHANGED
|
@@ -9766,6 +9766,12 @@ interface ProfileDoc {
|
|
|
9766
9766
|
* without another LLM pass. Recomputed every synthesis (not delta-cached).
|
|
9767
9767
|
*/
|
|
9768
9768
|
observationTrends: Record<ObservationTrend, number>;
|
|
9769
|
+
/**
|
|
9770
|
+
* Sorted ids the store listed but could not decrypt at synthesis time.
|
|
9771
|
+
* Absent when every memory was readable. A later call regenerates only when
|
|
9772
|
+
* this set changes, and attributes a memory once it becomes readable.
|
|
9773
|
+
*/
|
|
9774
|
+
unreadableMemoryIds?: string[];
|
|
9769
9775
|
/** Unix ms this doc was produced. */
|
|
9770
9776
|
generatedAt: number;
|
|
9771
9777
|
}
|
|
@@ -9819,6 +9825,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
|
|
|
9819
9825
|
* never accidentally disable the gate by finding it empty.
|
|
9820
9826
|
*/
|
|
9821
9827
|
reviewedMemoryIds?: readonly string[];
|
|
9828
|
+
/**
|
|
9829
|
+
* Read facts and recall through this store instead of `ctx.vaultCtx` and
|
|
9830
|
+
* `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
|
|
9831
|
+
* then read from `ctx`.
|
|
9832
|
+
*/
|
|
9833
|
+
store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
|
|
9822
9834
|
}
|
|
9823
9835
|
/**
|
|
9824
9836
|
* Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
|
package/dist/server/index.d.ts
CHANGED
|
@@ -9766,6 +9766,12 @@ interface ProfileDoc {
|
|
|
9766
9766
|
* without another LLM pass. Recomputed every synthesis (not delta-cached).
|
|
9767
9767
|
*/
|
|
9768
9768
|
observationTrends: Record<ObservationTrend, number>;
|
|
9769
|
+
/**
|
|
9770
|
+
* Sorted ids the store listed but could not decrypt at synthesis time.
|
|
9771
|
+
* Absent when every memory was readable. A later call regenerates only when
|
|
9772
|
+
* this set changes, and attributes a memory once it becomes readable.
|
|
9773
|
+
*/
|
|
9774
|
+
unreadableMemoryIds?: string[];
|
|
9769
9775
|
/** Unix ms this doc was produced. */
|
|
9770
9776
|
generatedAt: number;
|
|
9771
9777
|
}
|
|
@@ -9819,6 +9825,12 @@ interface SynthesizeProfileOptions extends PortalLlmAuth {
|
|
|
9819
9825
|
* never accidentally disable the gate by finding it empty.
|
|
9820
9826
|
*/
|
|
9821
9827
|
reviewedMemoryIds?: readonly string[];
|
|
9828
|
+
/**
|
|
9829
|
+
* Read facts and recall through this store instead of `ctx.vaultCtx` and
|
|
9830
|
+
* `ctx.vaultCache`, so a remote store works. Only `ctx.embeddingOptions` is
|
|
9831
|
+
* then read from `ctx`.
|
|
9832
|
+
*/
|
|
9833
|
+
store?: Pick<MemoryStore, "list" | "listProjections" | "factSource">;
|
|
9822
9834
|
}
|
|
9823
9835
|
/**
|
|
9824
9836
|
* Synthesize a shareable {@link ProfileDoc} from the user's vault, on-device.
|
package/dist/server/index.mjs
CHANGED
|
@@ -33686,12 +33686,19 @@ var FACET_STRUCTURED_RESPONSE_HINT = {
|
|
|
33686
33686
|
interests: `, "interests": <the same interests as an array of at most ${NEARBY_MAX_INTERESTS} strings of at most ${NEARBY_MAX_INTEREST_CODEPOINTS} characters each, e.g. ["trail running", "film photography"], or []>`
|
|
33687
33687
|
};
|
|
33688
33688
|
async function synthesizeProfile(ctx, options = {}) {
|
|
33689
|
-
|
|
33690
|
-
|
|
33689
|
+
const { store } = options;
|
|
33690
|
+
const vaultCtx = ctx.vaultCtx;
|
|
33691
|
+
if (!store && !vaultCtx) {
|
|
33692
|
+
throw new Error(
|
|
33693
|
+
"synthesizeProfile requires options.store or ctx.vaultCtx (vault-backed facts)."
|
|
33694
|
+
);
|
|
33691
33695
|
}
|
|
33692
|
-
if (!ctx.vaultCache) {
|
|
33693
|
-
throw new Error(
|
|
33696
|
+
if (!store && !ctx.vaultCache) {
|
|
33697
|
+
throw new Error(
|
|
33698
|
+
"synthesizeProfile requires options.store or ctx.vaultCache (semantic fact recall)."
|
|
33699
|
+
);
|
|
33694
33700
|
}
|
|
33701
|
+
const recallCtx = store ? { embeddingOptions: ctx.embeddingOptions, factSource: store.factSource } : ctx;
|
|
33695
33702
|
const facets = options.facets ?? DEFAULT_PROFILE_FACETS;
|
|
33696
33703
|
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
33697
33704
|
const config = {
|
|
@@ -33702,11 +33709,8 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
33702
33709
|
reviewedMemoryIdsSignature: reviewedMemoryIdsSignature(options.reviewedMemoryIds)
|
|
33703
33710
|
};
|
|
33704
33711
|
const previous = options.previous && options.previous.version === PROFILE_DOC_VERSION && configMatches(options.previous.config, config) ? options.previous : void 0;
|
|
33705
|
-
const
|
|
33706
|
-
|
|
33707
|
-
includeDeleted: true,
|
|
33708
|
-
includeSuperseded: true
|
|
33709
|
-
});
|
|
33712
|
+
const listOptions = { scopes, includeDeleted: true, includeSuperseded: true };
|
|
33713
|
+
const [memories, projections] = store ? await Promise.all([store.list(listOptions), store.listProjections(listOptions)]) : [await getAllVaultMemoriesOp(vaultCtx, listOptions), []];
|
|
33710
33714
|
const watermark = computeVaultWatermark(memories);
|
|
33711
33715
|
const observationTrends = summarizeObservationTrends(
|
|
33712
33716
|
memories.filter((m) => !m.isDeleted && m.supersededBy === null).map((m) => ({
|
|
@@ -33716,11 +33720,14 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
33716
33720
|
}))
|
|
33717
33721
|
);
|
|
33718
33722
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
33723
|
+
const unreadableMemoryIds = projections.map((p) => p.uniqueId).filter((id) => !presentIds.has(id)).sort();
|
|
33724
|
+
const previousUnreadable = new Set(previous?.unreadableMemoryIds ?? []);
|
|
33725
|
+
const sameUnreadable = unreadableMemoryIds.length === previousUnreadable.size && unreadableMemoryIds.every((id) => previousUnreadable.has(id));
|
|
33719
33726
|
const hasStaleSections = previous?.sections.some((s) => s.stale);
|
|
33720
33727
|
const citesMissingFact = previous?.sections.some(
|
|
33721
33728
|
(s) => s.sourceMemoryIds.some((id) => !presentIds.has(id))
|
|
33722
33729
|
);
|
|
33723
|
-
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact) {
|
|
33730
|
+
if (previous && previous.vaultWatermark === watermark && !hasStaleSections && !citesMissingFact && sameUnreadable) {
|
|
33724
33731
|
const sameOrder = previous.sections.length === facets.length && facets.every((f, i) => previous.sections[i]?.key === f.key);
|
|
33725
33732
|
if (sameOrder) {
|
|
33726
33733
|
if (trendsEqual(previous.observationTrends, observationTrends)) return previous;
|
|
@@ -33733,12 +33740,13 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
33733
33740
|
};
|
|
33734
33741
|
}
|
|
33735
33742
|
const staleKeys = await computeStaleFacetKeys(
|
|
33736
|
-
|
|
33743
|
+
recallCtx,
|
|
33737
33744
|
memories,
|
|
33738
33745
|
facets,
|
|
33739
33746
|
previous,
|
|
33740
33747
|
watermark,
|
|
33741
|
-
options.reviewedMemoryIds
|
|
33748
|
+
options.reviewedMemoryIds,
|
|
33749
|
+
previousUnreadable
|
|
33742
33750
|
);
|
|
33743
33751
|
const memoriesById = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
33744
33752
|
const fallbackPriors = /* @__PURE__ */ new Map();
|
|
@@ -33758,7 +33766,7 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
33758
33766
|
if (prior && !staleKeys.has(facet.key)) {
|
|
33759
33767
|
return prior;
|
|
33760
33768
|
}
|
|
33761
|
-
return synthesizeFacet(facet,
|
|
33769
|
+
return synthesizeFacet(facet, recallCtx, options, fallbackPriors.get(facet.key));
|
|
33762
33770
|
})
|
|
33763
33771
|
);
|
|
33764
33772
|
const sections = settled.map((r, i) => {
|
|
@@ -33773,12 +33781,18 @@ async function synthesizeProfile(ctx, options = {}) {
|
|
|
33773
33781
|
);
|
|
33774
33782
|
return fallbackSection(facet, fallbackPriors.get(facet.key));
|
|
33775
33783
|
});
|
|
33784
|
+
if (!sameUnreadable && unreadableMemoryIds.length > 0) {
|
|
33785
|
+
getLogger().warn("[memory/synthesizeProfile] some memories could not be read", {
|
|
33786
|
+
unreadable: unreadableMemoryIds.length
|
|
33787
|
+
});
|
|
33788
|
+
}
|
|
33776
33789
|
return {
|
|
33777
33790
|
version: PROFILE_DOC_VERSION,
|
|
33778
33791
|
sections,
|
|
33779
33792
|
vaultWatermark: watermark,
|
|
33780
33793
|
config,
|
|
33781
33794
|
observationTrends,
|
|
33795
|
+
...unreadableMemoryIds.length > 0 && { unreadableMemoryIds },
|
|
33782
33796
|
generatedAt: Date.now()
|
|
33783
33797
|
};
|
|
33784
33798
|
}
|
|
@@ -33812,11 +33826,13 @@ function computeVaultWatermark(memories) {
|
|
|
33812
33826
|
}
|
|
33813
33827
|
return max;
|
|
33814
33828
|
}
|
|
33815
|
-
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds) {
|
|
33829
|
+
async function computeStaleFacetKeys(ctx, memories, facets, previous, watermark, reviewedMemoryIds, previouslyUnreadable) {
|
|
33816
33830
|
const allKeys = new Set(facets.map((f) => f.key));
|
|
33817
33831
|
if (previous && watermark < previous.vaultWatermark) return allKeys;
|
|
33818
33832
|
if (!previous) return allKeys;
|
|
33819
|
-
const changed = memories.filter(
|
|
33833
|
+
const changed = memories.filter(
|
|
33834
|
+
(m) => changeTime(m) > previous.vaultWatermark || previouslyUnreadable.has(m.uniqueId)
|
|
33835
|
+
);
|
|
33820
33836
|
const changedIds = new Set(changed.map((m) => m.uniqueId));
|
|
33821
33837
|
const presentIds = new Set(memories.map((m) => m.uniqueId));
|
|
33822
33838
|
const stale = /* @__PURE__ */ new Set();
|
|
@@ -33890,7 +33906,12 @@ async function attributeFacts(ctx, candidates, facets) {
|
|
|
33890
33906
|
for (const fv of factVectors) {
|
|
33891
33907
|
let matchedAny = false;
|
|
33892
33908
|
for (let i = 0; i < facets.length; i++) {
|
|
33893
|
-
|
|
33909
|
+
const query = queryVectors[i];
|
|
33910
|
+
const similarity = cosineSimilarity(
|
|
33911
|
+
fv,
|
|
33912
|
+
fv.length < query.length ? query.slice(0, fv.length) : query
|
|
33913
|
+
);
|
|
33914
|
+
if (similarity >= NEW_FACT_ATTRIBUTION_MIN_SCORE) {
|
|
33894
33915
|
keys.add(facets[i].key);
|
|
33895
33916
|
matchedAny = true;
|
|
33896
33917
|
}
|