@anuma/sdk 8.2.0 → 9.0.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 +451 -161
- package/dist/expo/index.d.mts +67 -5
- package/dist/expo/index.d.ts +67 -5
- package/dist/expo/index.mjs +448 -161
- package/dist/index.cjs +9 -3
- package/dist/index.mjs +9 -3
- package/dist/react/index.cjs +451 -161
- package/dist/react/index.d.mts +67 -5
- package/dist/react/index.d.ts +67 -5
- package/dist/react/index.mjs +448 -161
- package/dist/server/index.cjs +28 -4
- package/dist/server/index.d.mts +22 -3
- package/dist/server/index.d.ts +22 -3
- package/dist/server/index.mjs +28 -4
- package/dist/tools/selection/index.cjs +9 -3
- package/dist/tools/selection/index.mjs +9 -3
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -6757,11 +6757,17 @@ async function generateEmbeddingsBatch(texts, headers, baseUrl, model, onUsage,
|
|
|
6757
6757
|
}
|
|
6758
6758
|
return response.data.data.map((item) => item.embedding ?? []);
|
|
6759
6759
|
}
|
|
6760
|
-
async function generateEmbeddings(texts, options) {
|
|
6760
|
+
async function generateEmbeddings(texts, options, signal) {
|
|
6761
|
+
signal?.throwIfAborted();
|
|
6761
6762
|
if (texts.length === 0) return [];
|
|
6762
6763
|
const total = options.totalTimeoutMs;
|
|
6763
|
-
if (total === void 0) return embedMany(texts, options,
|
|
6764
|
-
return withDeadline(
|
|
6764
|
+
if (total === void 0) return embedMany(texts, options, signal);
|
|
6765
|
+
return withDeadline(
|
|
6766
|
+
(inner) => embedMany(texts, options, inner),
|
|
6767
|
+
total,
|
|
6768
|
+
"embedding batch",
|
|
6769
|
+
signal
|
|
6770
|
+
);
|
|
6765
6771
|
}
|
|
6766
6772
|
async function embedMany(texts, options, outer) {
|
|
6767
6773
|
const { baseUrl = BASE_URL, model, batchSize, cache } = options;
|
|
@@ -12570,13 +12576,13 @@ function normalizeEntityName(name) {
|
|
|
12570
12576
|
return name.trim().toLowerCase();
|
|
12571
12577
|
}
|
|
12572
12578
|
var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
|
|
12573
|
-
function topicWeight(
|
|
12574
|
-
return
|
|
12579
|
+
function topicWeight(topic2) {
|
|
12580
|
+
return topic2.source === "user" ? 1 : topic2.weight ?? 1;
|
|
12575
12581
|
}
|
|
12576
12582
|
function primaryTopic(topics) {
|
|
12577
12583
|
let best = null;
|
|
12578
|
-
for (const
|
|
12579
|
-
if (best === null || topicWeight(
|
|
12584
|
+
for (const topic2 of topics ?? []) {
|
|
12585
|
+
if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
|
|
12580
12586
|
}
|
|
12581
12587
|
return best;
|
|
12582
12588
|
}
|
|
@@ -12714,7 +12720,7 @@ function resolveTopicsWrite(row, linked, inputs, source, mode) {
|
|
|
12714
12720
|
}
|
|
12715
12721
|
function weightsOf(stored2, inputs) {
|
|
12716
12722
|
const out = /* @__PURE__ */ new Map();
|
|
12717
|
-
for (const
|
|
12723
|
+
for (const topic2 of stored2 ?? []) out.set(normalizeEntityName(topic2.name), topic2.weight);
|
|
12718
12724
|
for (const input of inputs) {
|
|
12719
12725
|
const weight = parseTopicWeight(input.weight);
|
|
12720
12726
|
if (weight === void 0) continue;
|
|
@@ -15860,6 +15866,20 @@ async function getAllVaultMemoryContentsOp(ctx, options) {
|
|
|
15860
15866
|
async function countActiveVaultMemoriesOp(ctx) {
|
|
15861
15867
|
return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
|
|
15862
15868
|
}
|
|
15869
|
+
async function getVaultMemoryCountsOp(ctx) {
|
|
15870
|
+
const [live, archived, quarantined] = await Promise.all([
|
|
15871
|
+
countActiveVaultMemoriesOp(ctx),
|
|
15872
|
+
ctx.vaultMemoryCollection.query(
|
|
15873
|
+
...baseVaultConditions(ctx, { includeArchived: true }),
|
|
15874
|
+
import_watermelondb24.Q.where("archived_at", import_watermelondb24.Q.notEq(null))
|
|
15875
|
+
).fetchCount(),
|
|
15876
|
+
ctx.vaultMemoryCollection.query(
|
|
15877
|
+
...baseVaultConditions(ctx, { includeQuarantined: true }),
|
|
15878
|
+
import_watermelondb24.Q.where("trust_tier", "quarantined")
|
|
15879
|
+
).fetchCount()
|
|
15880
|
+
]);
|
|
15881
|
+
return { live, archived, quarantined };
|
|
15882
|
+
}
|
|
15863
15883
|
async function getActiveVaultMemoryIdsOp(ctx, ids) {
|
|
15864
15884
|
if (ids.length === 0) return /* @__PURE__ */ new Set();
|
|
15865
15885
|
const rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb24.Q.where("id", import_watermelondb24.Q.oneOf(ids))).unsafeFetchRaw();
|
|
@@ -23063,7 +23083,8 @@ Each memory below is written as "<id>: <text>". Output strict JSON: {"memories":
|
|
|
23063
23083
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
23064
23084
|
- "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
|
|
23065
23085
|
No prose.`;
|
|
23066
|
-
async function extractEntitiesForMemories(memories, options) {
|
|
23086
|
+
async function extractEntitiesForMemories(memories, options, signal) {
|
|
23087
|
+
signal?.throwIfAborted();
|
|
23067
23088
|
const out = /* @__PURE__ */ new Map();
|
|
23068
23089
|
if (memories.length === 0) return out;
|
|
23069
23090
|
const redactor = resolvePiiRedactor(options.piiRedaction);
|
|
@@ -23076,6 +23097,7 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
|
|
|
23076
23097
|
|
|
23077
23098
|
` : "";
|
|
23078
23099
|
for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
|
|
23100
|
+
signal?.throwIfAborted();
|
|
23079
23101
|
const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
|
|
23080
23102
|
const start = Date.now();
|
|
23081
23103
|
let attempts = 0;
|
|
@@ -23123,6 +23145,7 @@ List each memory's named entities.`,
|
|
|
23123
23145
|
...options.totalTimeoutMs !== void 0 && { totalTimeoutMs: options.totalTimeoutMs },
|
|
23124
23146
|
...options.backoffMs && { backoffMs: options.backoffMs }
|
|
23125
23147
|
});
|
|
23148
|
+
signal?.throwIfAborted();
|
|
23126
23149
|
if (parsed === null) {
|
|
23127
23150
|
reportBatch(batch.length);
|
|
23128
23151
|
getLogger().warn(
|
|
@@ -23357,6 +23380,7 @@ function createLocalMemoryStore(options) {
|
|
|
23357
23380
|
backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
|
|
23358
23381
|
};
|
|
23359
23382
|
return {
|
|
23383
|
+
counts: () => getVaultMemoryCountsOp(vaultCtx),
|
|
23360
23384
|
masksEmbeddingInput: options.embeddingOptions.maskInput !== void 0,
|
|
23361
23385
|
list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
|
|
23362
23386
|
get: (id) => getVaultMemoryOp(vaultCtx, id),
|
|
@@ -23586,6 +23610,137 @@ init_logger();
|
|
|
23586
23610
|
|
|
23587
23611
|
// src/lib/memory/store/remotePersistence.ts
|
|
23588
23612
|
init_portalLlm();
|
|
23613
|
+
|
|
23614
|
+
// src/lib/memory/store/remoteTopics.ts
|
|
23615
|
+
function topic(value) {
|
|
23616
|
+
const t = value;
|
|
23617
|
+
if (!t || typeof t.name !== "string" || !t.name || !Number.isSafeInteger(t.version) || t.version < 1 || typeof t.is_deleted !== "boolean" || typeof t.server_updated_at !== "string" || t.kind !== void 0 && typeof t.kind !== "string")
|
|
23618
|
+
throw new Error("Invalid nearby topic");
|
|
23619
|
+
return t;
|
|
23620
|
+
}
|
|
23621
|
+
function createRemoteTopicPersistence(request) {
|
|
23622
|
+
return {
|
|
23623
|
+
topics: async () => {
|
|
23624
|
+
const out = [];
|
|
23625
|
+
let cursor;
|
|
23626
|
+
do {
|
|
23627
|
+
const page = await request(
|
|
23628
|
+
"/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
|
|
23629
|
+
{}
|
|
23630
|
+
);
|
|
23631
|
+
if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
|
|
23632
|
+
throw new Error("Invalid nearby topic page");
|
|
23633
|
+
out.push(...page.items.map(topic));
|
|
23634
|
+
cursor = page.next_cursor;
|
|
23635
|
+
} while (cursor);
|
|
23636
|
+
return out;
|
|
23637
|
+
},
|
|
23638
|
+
putTopic: async (value, expectedVersion) => {
|
|
23639
|
+
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
23640
|
+
throw new Error("Expected topic version must be a nonnegative integer");
|
|
23641
|
+
return topic(
|
|
23642
|
+
await request("/topics", {
|
|
23643
|
+
method: "PUT",
|
|
23644
|
+
body: JSON.stringify({ topic: value, expected_version: expectedVersion })
|
|
23645
|
+
})
|
|
23646
|
+
);
|
|
23647
|
+
},
|
|
23648
|
+
importTopics: async (topics, signal) => {
|
|
23649
|
+
for (let i = 0; i < topics.length; i += 50) {
|
|
23650
|
+
signal?.throwIfAborted();
|
|
23651
|
+
await request(
|
|
23652
|
+
"/topics/import",
|
|
23653
|
+
{
|
|
23654
|
+
method: "POST",
|
|
23655
|
+
body: JSON.stringify({ topics: topics.slice(i, i + 50) })
|
|
23656
|
+
},
|
|
23657
|
+
signal
|
|
23658
|
+
);
|
|
23659
|
+
}
|
|
23660
|
+
signal?.throwIfAborted();
|
|
23661
|
+
}
|
|
23662
|
+
};
|
|
23663
|
+
}
|
|
23664
|
+
async function createRemoteTopic(persistence, name) {
|
|
23665
|
+
const canonical = normalizeEntityName(name);
|
|
23666
|
+
if (!canonical) return null;
|
|
23667
|
+
const existing = (await persistence.topics()).find((t) => t.name === canonical);
|
|
23668
|
+
if (existing && !existing.is_deleted) return canonical;
|
|
23669
|
+
try {
|
|
23670
|
+
await persistence.putTopic(
|
|
23671
|
+
{ name: canonical, is_deleted: false, ...existing?.kind && { kind: existing.kind } },
|
|
23672
|
+
existing?.version ?? 0
|
|
23673
|
+
);
|
|
23674
|
+
} catch (error) {
|
|
23675
|
+
if (!(error instanceof RemoteMemoryError) || error.code !== "version_conflict") throw error;
|
|
23676
|
+
const current = (await persistence.topics()).find((t) => t.name === canonical);
|
|
23677
|
+
if (!current || current.is_deleted) throw error;
|
|
23678
|
+
}
|
|
23679
|
+
return canonical;
|
|
23680
|
+
}
|
|
23681
|
+
|
|
23682
|
+
// src/lib/memory/store/remoteWork.ts
|
|
23683
|
+
init_logger();
|
|
23684
|
+
function createRemoteWorkPersistence(request) {
|
|
23685
|
+
return {
|
|
23686
|
+
claimWork: async (kind, processorVersion, token) => {
|
|
23687
|
+
const result = await request("/work/claim", {
|
|
23688
|
+
method: "POST",
|
|
23689
|
+
body: JSON.stringify({
|
|
23690
|
+
kind,
|
|
23691
|
+
processor_version: processorVersion,
|
|
23692
|
+
...token && { token }
|
|
23693
|
+
})
|
|
23694
|
+
});
|
|
23695
|
+
if (!result || typeof result !== "object") throw new Error("Invalid nearby work lease");
|
|
23696
|
+
const lease = result.lease;
|
|
23697
|
+
if (lease === void 0) return null;
|
|
23698
|
+
if (!lease || lease.kind !== kind || typeof lease.token !== "string" || !lease.token || lease.processor_version !== processorVersion || typeof lease.expires_at !== "string" || !Number.isFinite(Date.parse(lease.expires_at)))
|
|
23699
|
+
throw new Error("Invalid nearby work lease");
|
|
23700
|
+
return lease;
|
|
23701
|
+
},
|
|
23702
|
+
releaseWork: async (kind, token) => {
|
|
23703
|
+
await request("/work/release", { method: "POST", body: JSON.stringify({ kind, token }) });
|
|
23704
|
+
}
|
|
23705
|
+
};
|
|
23706
|
+
}
|
|
23707
|
+
async function withRemoteWorkLease(persistence, kind, processorVersion, task) {
|
|
23708
|
+
const lease = await persistence.claimWork(kind, processorVersion);
|
|
23709
|
+
if (!lease) return null;
|
|
23710
|
+
const controller = new AbortController();
|
|
23711
|
+
const leaseLostError = new RemoteMemoryError("Maintenance lease lost", 409, "work_lease_expired");
|
|
23712
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
23713
|
+
controller.signal.addEventListener("abort", () => reject(leaseLostError), {
|
|
23714
|
+
once: true
|
|
23715
|
+
});
|
|
23716
|
+
});
|
|
23717
|
+
let renewing;
|
|
23718
|
+
const timer = setInterval(() => {
|
|
23719
|
+
if (renewing || controller.signal.aborted) return;
|
|
23720
|
+
renewing = persistence.claimWork(kind, processorVersion, lease.token).then((renewed) => {
|
|
23721
|
+
if (!renewed) controller.abort(leaseLostError);
|
|
23722
|
+
}).catch((error) => getLogger().warn("[memory/work] Lease renewal failed:", error)).finally(() => {
|
|
23723
|
+
renewing = void 0;
|
|
23724
|
+
});
|
|
23725
|
+
}, 3e4);
|
|
23726
|
+
try {
|
|
23727
|
+
const result = await Promise.race([task(lease.token, controller.signal), cancelled]);
|
|
23728
|
+
await renewing;
|
|
23729
|
+
return controller.signal.aborted ? null : result;
|
|
23730
|
+
} catch (error) {
|
|
23731
|
+
if (controller.signal.aborted || isRemoteWorkLeaseExpired(error)) return null;
|
|
23732
|
+
throw error;
|
|
23733
|
+
} finally {
|
|
23734
|
+
clearInterval(timer);
|
|
23735
|
+
await renewing;
|
|
23736
|
+
await persistence.releaseWork(kind, lease.token).catch((error) => getLogger().warn("[memory/work] Lease release failed:", error));
|
|
23737
|
+
}
|
|
23738
|
+
}
|
|
23739
|
+
function isRemoteWorkLeaseExpired(error) {
|
|
23740
|
+
return error instanceof RemoteMemoryError && error.code === "work_lease_expired";
|
|
23741
|
+
}
|
|
23742
|
+
|
|
23743
|
+
// src/lib/memory/store/remotePersistence.ts
|
|
23589
23744
|
var RemoteMemoryError = class extends Error {
|
|
23590
23745
|
constructor(message, status, code, retryAfterMs) {
|
|
23591
23746
|
super(message);
|
|
@@ -23964,6 +24119,33 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
23964
24119
|
};
|
|
23965
24120
|
};
|
|
23966
24121
|
return {
|
|
24122
|
+
...createRemoteTopicPersistence(request),
|
|
24123
|
+
...createRemoteWorkPersistence(request),
|
|
24124
|
+
counts: async () => {
|
|
24125
|
+
const result = await request("/counts", {});
|
|
24126
|
+
if (!record(result) || ["live", "archived", "quarantined"].some(
|
|
24127
|
+
(key) => !Number.isSafeInteger(result[key]) || result[key] < 0
|
|
24128
|
+
))
|
|
24129
|
+
throw new Error("Invalid nearby memory counts");
|
|
24130
|
+
return {
|
|
24131
|
+
live: result.live,
|
|
24132
|
+
archived: result.archived,
|
|
24133
|
+
quarantined: result.quarantined
|
|
24134
|
+
};
|
|
24135
|
+
},
|
|
24136
|
+
temporal: async (window2, limit = 100) => {
|
|
24137
|
+
if (!Number.isFinite(window2.start) || !Number.isFinite(window2.end) || window2.end <= window2.start || !Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
24138
|
+
throw new Error("Temporal window must have end > start and limit must be 1\u2013100");
|
|
24139
|
+
const result = await request("/temporal", {
|
|
24140
|
+
method: "POST",
|
|
24141
|
+
body: JSON.stringify({ ...window2, limit })
|
|
24142
|
+
});
|
|
24143
|
+
if (!record(result) || !Array.isArray(result.items) || result.items.some(
|
|
24144
|
+
(item) => !record(item) || typeof item.memory_id !== "string" || !item.memory_id || typeof item.score !== "number" || !Number.isFinite(item.score) || item.score <= 0 || item.score > 1
|
|
24145
|
+
))
|
|
24146
|
+
throw new Error("Invalid nearby temporal candidates");
|
|
24147
|
+
return result.items.map((item) => item.memory_id);
|
|
24148
|
+
},
|
|
23967
24149
|
get: async (id, signal) => {
|
|
23968
24150
|
try {
|
|
23969
24151
|
return await decode(await request(memoryPath(id), {}, signal));
|
|
@@ -24012,7 +24194,7 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
24012
24194
|
write
|
|
24013
24195
|
);
|
|
24014
24196
|
},
|
|
24015
|
-
putMany: async (writes, signal) => {
|
|
24197
|
+
putMany: async (writes, signal, leaseToken) => {
|
|
24016
24198
|
if (writes.length < 1 || writes.length > 50 || new Set(writes.map((w) => w.memory.memory_id)).size !== writes.length)
|
|
24017
24199
|
throw new Error("Batch must contain 1\u201350 distinct memory ids");
|
|
24018
24200
|
const prepared = await Promise.all(
|
|
@@ -24022,7 +24204,11 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
24022
24204
|
"/memories/batch",
|
|
24023
24205
|
{
|
|
24024
24206
|
method: "POST",
|
|
24025
|
-
body: JSON.stringify({
|
|
24207
|
+
body: JSON.stringify({
|
|
24208
|
+
key_id: options.keyId,
|
|
24209
|
+
writes: prepared.map((w) => w.body),
|
|
24210
|
+
...leaseToken && { lease_token: leaseToken }
|
|
24211
|
+
})
|
|
24026
24212
|
},
|
|
24027
24213
|
signal
|
|
24028
24214
|
);
|
|
@@ -24782,6 +24968,12 @@ async function migrateMemoriesToRemote(options) {
|
|
|
24782
24968
|
let claimed = account.key_id === canonical.keyId;
|
|
24783
24969
|
if (!active) await createDecaySweeper({ vaultCtx: options.vaultCtx }).runSweep();
|
|
24784
24970
|
await backfillTopics(options.vaultCtx);
|
|
24971
|
+
const localTopics = options.topics ?? (options.vaultCtx.singleTenant === true ? (await options.vaultCtx.database.get("entity").query().unsafeFetchRaw()).map((t) => ({
|
|
24972
|
+
name: normalizeEntityName(typeof t.canonical_name === "string" ? t.canonical_name : ""),
|
|
24973
|
+
...typeof t.kind === "string" && { kind: t.kind }
|
|
24974
|
+
})).filter((t) => t.name.length > 0) : []);
|
|
24975
|
+
if (localTopics.length)
|
|
24976
|
+
await createRemoteTopicPersistence(request).importTopics(localTopics, signal);
|
|
24785
24977
|
const skipped = /* @__PURE__ */ new Map();
|
|
24786
24978
|
const reembedded = /* @__PURE__ */ new Set();
|
|
24787
24979
|
const unembedded = /* @__PURE__ */ new Set();
|
|
@@ -24909,7 +25101,6 @@ var MAX_QUERY_IDS = 1e3;
|
|
|
24909
25101
|
var MAX_QUERY_TOPICS = 100;
|
|
24910
25102
|
var MAX_QUERY_FILTER = 20;
|
|
24911
25103
|
var GRAPH_LANE_ROWS = 256;
|
|
24912
|
-
var TEMPORAL_LANE_ROWS = 256;
|
|
24913
25104
|
var REPAIR_ROWS = 50;
|
|
24914
25105
|
var REPAIR_SCAN = 2e3;
|
|
24915
25106
|
var DECAY_TRANSITIONS_PER_SWEEP = 200;
|
|
@@ -24980,21 +25171,21 @@ function subscriptionKey({ memory, version }, options) {
|
|
|
24980
25171
|
]);
|
|
24981
25172
|
}
|
|
24982
25173
|
function topicInputs(topics, source) {
|
|
24983
|
-
return topics.flatMap((
|
|
24984
|
-
const name = (typeof
|
|
24985
|
-
const kind = typeof
|
|
24986
|
-
const weight = typeof
|
|
25174
|
+
return topics.flatMap((topic2) => {
|
|
25175
|
+
const name = (typeof topic2 === "string" ? topic2 : topic2.name).trim();
|
|
25176
|
+
const kind = typeof topic2 === "string" ? void 0 : topic2.kind;
|
|
25177
|
+
const weight = typeof topic2 === "string" || source === "user" ? void 0 : parseTopicWeight(topic2.weight);
|
|
24987
25178
|
return name ? [{ name, ...kind && { kind }, source, ...weight && { weight } }] : [];
|
|
24988
25179
|
});
|
|
24989
25180
|
}
|
|
24990
25181
|
function mergeTopics(existing, added) {
|
|
24991
25182
|
const byName = /* @__PURE__ */ new Map();
|
|
24992
|
-
for (const
|
|
24993
|
-
const key = normalizeEntityName(
|
|
25183
|
+
for (const topic2 of [...existing, ...added]) {
|
|
25184
|
+
const key = normalizeEntityName(topic2.name);
|
|
24994
25185
|
const kept = byName.get(key);
|
|
24995
|
-
if (!kept) byName.set(key,
|
|
24996
|
-
else if (kept.weight !== void 0 &&
|
|
24997
|
-
byName.set(key, { ...kept, weight:
|
|
25186
|
+
if (!kept) byName.set(key, topic2);
|
|
25187
|
+
else if (kept.weight !== void 0 && topic2.weight !== void 0 && topic2.weight > kept.weight)
|
|
25188
|
+
byName.set(key, { ...kept, weight: topic2.weight });
|
|
24998
25189
|
}
|
|
24999
25190
|
return [...byName.values()];
|
|
25000
25191
|
}
|
|
@@ -25005,7 +25196,7 @@ function sameTopics(stored2, next) {
|
|
|
25005
25196
|
return next.every((t) => keys.has(key(t)));
|
|
25006
25197
|
}
|
|
25007
25198
|
function canonicalTopics(row) {
|
|
25008
|
-
return new Set((parseTopics(row.topics) ?? []).map((
|
|
25199
|
+
return new Set((parseTopics(row.topics) ?? []).map((topic2) => normalizeEntityName(topic2.name)));
|
|
25009
25200
|
}
|
|
25010
25201
|
function createRemoteMemoryStore(options) {
|
|
25011
25202
|
const { persistence, embeddingOptions } = options;
|
|
@@ -25080,20 +25271,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25080
25271
|
const eventTimeRanking = async (query, now) => {
|
|
25081
25272
|
const window2 = parseQueryTimeWindow(query, now);
|
|
25082
25273
|
if (!window2) return [];
|
|
25083
|
-
|
|
25084
|
-
metadata2,
|
|
25085
|
-
{ event_time_overlaps: { start: window2.start, end: window2.end } },
|
|
25086
|
-
TEMPORAL_LANE_ROWS
|
|
25087
|
-
);
|
|
25088
|
-
return rows.map(({ memory }) => ({
|
|
25089
|
-
memoryId: memory.memory_id,
|
|
25090
|
-
score: scoreEventTimeOverlap(
|
|
25091
|
-
memory.event_time_start ?? null,
|
|
25092
|
-
memory.event_time_end ?? null,
|
|
25093
|
-
memory.event_time_kind ?? null,
|
|
25094
|
-
window2
|
|
25095
|
-
)
|
|
25096
|
-
})).filter((row) => row.score > 0).sort((a, b) => b.score - a.score).map((row) => row.memoryId);
|
|
25274
|
+
return persistence.temporal({ start: window2.start, end: window2.end });
|
|
25097
25275
|
};
|
|
25098
25276
|
const enumerateIds = async (read, ids, filters) => (await Promise.all(
|
|
25099
25277
|
chunks2([...new Set(ids)], MAX_QUERY_IDS).map(
|
|
@@ -25144,22 +25322,28 @@ function createRemoteMemoryStore(options) {
|
|
|
25144
25322
|
writeOptions?.signal?.throwIfAborted();
|
|
25145
25323
|
const next = edit(structuredClone(record2.memory));
|
|
25146
25324
|
if (!next) return null;
|
|
25147
|
-
const saved = writeOptions?.
|
|
25325
|
+
const saved = writeOptions?.leaseToken ? (await persistence.putMany(
|
|
25326
|
+
[{ memory: next, expectedVersion: record2 }],
|
|
25327
|
+
writeOptions.signal,
|
|
25328
|
+
writeOptions.leaseToken
|
|
25329
|
+
))[0] : writeOptions?.signal ? await persistence.put(next, record2, writeOptions.signal) : await persistence.put(next, record2);
|
|
25148
25330
|
notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
|
|
25149
25331
|
return saved;
|
|
25150
25332
|
};
|
|
25151
25333
|
const mutate = (id, change, edit, writeOptions) => serialized(id, () => mutateLocked(id, change, edit, writeOptions));
|
|
25152
25334
|
const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
|
|
25153
|
-
const mutateMany = async (ids, change, edit) => {
|
|
25335
|
+
const mutateMany = async (ids, change, edit, leaseToken, signal) => {
|
|
25154
25336
|
const saved = /* @__PURE__ */ new Map();
|
|
25155
25337
|
const failed = /* @__PURE__ */ new Map();
|
|
25156
25338
|
for (const slice of chunks2([...new Set(ids)], MAX_BATCH)) {
|
|
25157
25339
|
await serializedAll(slice, async () => {
|
|
25340
|
+
signal?.throwIfAborted();
|
|
25158
25341
|
const writes = [];
|
|
25159
25342
|
let read;
|
|
25160
25343
|
try {
|
|
25161
|
-
read = await readEveryState(slice);
|
|
25344
|
+
read = await readEveryState(slice, signal);
|
|
25162
25345
|
} catch (error) {
|
|
25346
|
+
signal?.throwIfAborted();
|
|
25163
25347
|
for (const id of slice) failed.set(id, error);
|
|
25164
25348
|
return;
|
|
25165
25349
|
}
|
|
@@ -25176,7 +25360,9 @@ function createRemoteMemoryStore(options) {
|
|
|
25176
25360
|
if (writes.length === 0) return;
|
|
25177
25361
|
try {
|
|
25178
25362
|
const written = await persistence.putMany(
|
|
25179
|
-
writes.map(({ before, memory }) => ({ memory, expectedVersion: before }))
|
|
25363
|
+
writes.map(({ before, memory }) => ({ memory, expectedVersion: before })),
|
|
25364
|
+
signal,
|
|
25365
|
+
leaseToken
|
|
25180
25366
|
);
|
|
25181
25367
|
const byChange = /* @__PURE__ */ new Map();
|
|
25182
25368
|
written.forEach((record2, i) => {
|
|
@@ -25186,7 +25372,9 @@ function createRemoteMemoryStore(options) {
|
|
|
25186
25372
|
});
|
|
25187
25373
|
for (const [kind, changed] of byChange) notify(kind, changed);
|
|
25188
25374
|
} catch (error) {
|
|
25189
|
-
|
|
25375
|
+
signal?.throwIfAborted();
|
|
25376
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
25377
|
+
const perRow = error instanceof RemoteMemoryError && (error.code === "version_conflict" || error.status === 429 && !leaseToken);
|
|
25190
25378
|
for (const { before } of writes) {
|
|
25191
25379
|
const id = before.memory.memory_id;
|
|
25192
25380
|
if (!perRow) {
|
|
@@ -25194,9 +25382,16 @@ function createRemoteMemoryStore(options) {
|
|
|
25194
25382
|
continue;
|
|
25195
25383
|
}
|
|
25196
25384
|
try {
|
|
25197
|
-
const record2 = await mutateLocked(
|
|
25385
|
+
const record2 = await mutateLocked(
|
|
25386
|
+
id,
|
|
25387
|
+
change,
|
|
25388
|
+
edit,
|
|
25389
|
+
leaseToken || signal ? { leaseToken, signal } : void 0
|
|
25390
|
+
);
|
|
25198
25391
|
if (record2) saved.set(id, record2);
|
|
25199
25392
|
} catch (rowError) {
|
|
25393
|
+
signal?.throwIfAborted();
|
|
25394
|
+
if (isRemoteWorkLeaseExpired(rowError)) throw rowError;
|
|
25200
25395
|
failed.set(id, rowError);
|
|
25201
25396
|
}
|
|
25202
25397
|
}
|
|
@@ -25260,7 +25455,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25260
25455
|
return records.map((record2) => stored(record2.memory));
|
|
25261
25456
|
};
|
|
25262
25457
|
const asStored = (record2) => record2 ? stored(record2.memory) : null;
|
|
25263
|
-
const knownKinds = async (topics) => {
|
|
25458
|
+
const knownKinds = async (topics, signal) => {
|
|
25264
25459
|
const unkinded = [
|
|
25265
25460
|
...new Set(topics.filter((t) => !t.kind).map((t) => normalizeEntityName(t.name)))
|
|
25266
25461
|
];
|
|
@@ -25270,21 +25465,22 @@ function createRemoteMemoryStore(options) {
|
|
|
25270
25465
|
topics: names,
|
|
25271
25466
|
include_archived: true,
|
|
25272
25467
|
include_quarantined: true,
|
|
25273
|
-
include_superseded: true
|
|
25468
|
+
include_superseded: true,
|
|
25469
|
+
...signal && { signal }
|
|
25274
25470
|
})) {
|
|
25275
|
-
for (const
|
|
25276
|
-
const name = normalizeEntityName(
|
|
25277
|
-
if (
|
|
25471
|
+
for (const topic2 of parseTopics(memory.topics) ?? []) {
|
|
25472
|
+
const name = normalizeEntityName(topic2.name);
|
|
25473
|
+
if (topic2.kind && !kinds.has(name)) kinds.set(name, topic2.kind);
|
|
25278
25474
|
}
|
|
25279
25475
|
}
|
|
25280
25476
|
}
|
|
25281
|
-
return (
|
|
25282
|
-
const kind =
|
|
25283
|
-
return kind ? { ...
|
|
25477
|
+
return (topic2) => {
|
|
25478
|
+
const kind = topic2.kind ?? kinds.get(normalizeEntityName(topic2.name));
|
|
25479
|
+
return kind ? { ...topic2, kind } : topic2;
|
|
25284
25480
|
};
|
|
25285
25481
|
};
|
|
25286
25482
|
const withKnownKinds = async (topics) => topics.map(await knownKinds(topics));
|
|
25287
|
-
const readEveryState = async (ids) => {
|
|
25483
|
+
const readEveryState = async (ids, signal) => {
|
|
25288
25484
|
const records = /* @__PURE__ */ new Map();
|
|
25289
25485
|
const unreadable = /* @__PURE__ */ new Set();
|
|
25290
25486
|
for (const memory_ids of chunks2(ids, MAX_QUERY_IDS)) {
|
|
@@ -25297,6 +25493,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25297
25493
|
include_quarantined: true,
|
|
25298
25494
|
include_superseded: true,
|
|
25299
25495
|
limit: 200,
|
|
25496
|
+
...signal && { signal },
|
|
25300
25497
|
...cursor2 && { cursor: cursor2 }
|
|
25301
25498
|
});
|
|
25302
25499
|
warnFailed(page.failed);
|
|
@@ -25307,6 +25504,111 @@ function createRemoteMemoryStore(options) {
|
|
|
25307
25504
|
}
|
|
25308
25505
|
return { records, unreadable };
|
|
25309
25506
|
};
|
|
25507
|
+
const extractTopicsUnlocked = async (memoryIds, extractOptions, leaseToken, signal) => {
|
|
25508
|
+
const start = Date.now();
|
|
25509
|
+
const extractedAt = extractOptions.now ?? start;
|
|
25510
|
+
const skippedIds = [];
|
|
25511
|
+
const skippedReasons = /* @__PURE__ */ new Map();
|
|
25512
|
+
const skip = (id, reason) => {
|
|
25513
|
+
if (skippedReasons.has(id)) return;
|
|
25514
|
+
skippedIds.push(id);
|
|
25515
|
+
skippedReasons.set(id, reason);
|
|
25516
|
+
};
|
|
25517
|
+
const ids = [...new Set(memoryIds)];
|
|
25518
|
+
const { records, unreadable } = await readEveryState(ids, signal);
|
|
25519
|
+
signal?.throwIfAborted();
|
|
25520
|
+
const inputs = [];
|
|
25521
|
+
for (const id of ids) {
|
|
25522
|
+
const memory = records.get(id)?.memory;
|
|
25523
|
+
if (unreadable.has(id)) skip(id, "unreadable");
|
|
25524
|
+
else if (!memory) skip(id, "not-found");
|
|
25525
|
+
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
25526
|
+
else inputs.push({ id, content: memory.content });
|
|
25527
|
+
}
|
|
25528
|
+
const batches = [];
|
|
25529
|
+
const llmFailures = /* @__PURE__ */ new Map();
|
|
25530
|
+
const entitiesByMemory = await extractEntitiesForMemories(
|
|
25531
|
+
inputs,
|
|
25532
|
+
{
|
|
25533
|
+
...extractOptions,
|
|
25534
|
+
onBatch: (batch) => {
|
|
25535
|
+
batches.push(batch);
|
|
25536
|
+
if (batch.failure) {
|
|
25537
|
+
for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
|
|
25538
|
+
}
|
|
25539
|
+
notifyMemoryCallback(extractOptions.onBatch, batch);
|
|
25540
|
+
}
|
|
25541
|
+
},
|
|
25542
|
+
signal
|
|
25543
|
+
);
|
|
25544
|
+
signal?.throwIfAborted();
|
|
25545
|
+
const answered = /* @__PURE__ */ new Map();
|
|
25546
|
+
for (const input of inputs) {
|
|
25547
|
+
const entities = entitiesByMemory.get(input.id);
|
|
25548
|
+
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
25549
|
+
else
|
|
25550
|
+
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
25551
|
+
}
|
|
25552
|
+
const stampedIds = [];
|
|
25553
|
+
const fail = (id, reason) => {
|
|
25554
|
+
skip(id, reason);
|
|
25555
|
+
entitiesByMemory.delete(id);
|
|
25556
|
+
};
|
|
25557
|
+
try {
|
|
25558
|
+
const kinded = await knownKinds(
|
|
25559
|
+
[...answered.values()].flatMap((a) => a.topics),
|
|
25560
|
+
signal
|
|
25561
|
+
);
|
|
25562
|
+
signal?.throwIfAborted();
|
|
25563
|
+
const topics = new Map(
|
|
25564
|
+
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
25565
|
+
);
|
|
25566
|
+
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
25567
|
+
const { saved, failed } = await mutateMany(
|
|
25568
|
+
[...answered.keys()],
|
|
25569
|
+
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
25570
|
+
(memory) => {
|
|
25571
|
+
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
25572
|
+
return null;
|
|
25573
|
+
return {
|
|
25574
|
+
...memory,
|
|
25575
|
+
...!unchanged(memory) && {
|
|
25576
|
+
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
25577
|
+
topics_updated_at: Date.now()
|
|
25578
|
+
},
|
|
25579
|
+
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
25580
|
+
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
25581
|
+
};
|
|
25582
|
+
},
|
|
25583
|
+
leaseToken,
|
|
25584
|
+
signal
|
|
25585
|
+
);
|
|
25586
|
+
for (const id of answered.keys()) {
|
|
25587
|
+
if (saved.has(id)) stampedIds.push(id);
|
|
25588
|
+
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
25589
|
+
}
|
|
25590
|
+
if (failed.size)
|
|
25591
|
+
getLogger().warn(
|
|
25592
|
+
"[memory/remote-store] Failed to write extracted topics:",
|
|
25593
|
+
failed.values().next().value
|
|
25594
|
+
);
|
|
25595
|
+
} catch (error) {
|
|
25596
|
+
signal?.throwIfAborted();
|
|
25597
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
25598
|
+
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
25599
|
+
for (const id of answered.keys()) fail(id, "link-failed");
|
|
25600
|
+
}
|
|
25601
|
+
return {
|
|
25602
|
+
entitiesByMemory,
|
|
25603
|
+
stampedIds,
|
|
25604
|
+
skippedIds,
|
|
25605
|
+
skippedReasons,
|
|
25606
|
+
batchCount: batches.length,
|
|
25607
|
+
durationMs: Date.now() - start,
|
|
25608
|
+
batches,
|
|
25609
|
+
llmFailures
|
|
25610
|
+
};
|
|
25611
|
+
};
|
|
25310
25612
|
const topicMaintenance = {
|
|
25311
25613
|
getTopicBacklog: async (backlogOptions = {}) => {
|
|
25312
25614
|
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
@@ -25324,97 +25626,12 @@ function createRemoteMemoryStore(options) {
|
|
|
25324
25626
|
topicsBackfill: []
|
|
25325
25627
|
};
|
|
25326
25628
|
},
|
|
25327
|
-
extractTopics: async (
|
|
25328
|
-
|
|
25329
|
-
|
|
25330
|
-
|
|
25331
|
-
|
|
25332
|
-
|
|
25333
|
-
if (skippedReasons.has(id)) return;
|
|
25334
|
-
skippedIds.push(id);
|
|
25335
|
-
skippedReasons.set(id, reason);
|
|
25336
|
-
};
|
|
25337
|
-
const ids = [...new Set(memoryIds)];
|
|
25338
|
-
const { records, unreadable } = await readEveryState(ids);
|
|
25339
|
-
const inputs = [];
|
|
25340
|
-
for (const id of ids) {
|
|
25341
|
-
const memory = records.get(id)?.memory;
|
|
25342
|
-
if (unreadable.has(id)) skip(id, "unreadable");
|
|
25343
|
-
else if (!memory) skip(id, "not-found");
|
|
25344
|
-
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
25345
|
-
else inputs.push({ id, content: memory.content });
|
|
25346
|
-
}
|
|
25347
|
-
const batches = [];
|
|
25348
|
-
const llmFailures = /* @__PURE__ */ new Map();
|
|
25349
|
-
const entitiesByMemory = await extractEntitiesForMemories(inputs, {
|
|
25350
|
-
...extractOptions,
|
|
25351
|
-
onBatch: (batch) => {
|
|
25352
|
-
batches.push(batch);
|
|
25353
|
-
if (batch.failure) {
|
|
25354
|
-
for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
|
|
25355
|
-
}
|
|
25356
|
-
notifyMemoryCallback(extractOptions.onBatch, batch);
|
|
25357
|
-
}
|
|
25358
|
-
});
|
|
25359
|
-
const answered = /* @__PURE__ */ new Map();
|
|
25360
|
-
for (const input of inputs) {
|
|
25361
|
-
const entities = entitiesByMemory.get(input.id);
|
|
25362
|
-
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
25363
|
-
else
|
|
25364
|
-
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
25365
|
-
}
|
|
25366
|
-
const stampedIds = [];
|
|
25367
|
-
const fail = (id, reason) => {
|
|
25368
|
-
skip(id, reason);
|
|
25369
|
-
entitiesByMemory.delete(id);
|
|
25370
|
-
};
|
|
25371
|
-
try {
|
|
25372
|
-
const kinded = await knownKinds([...answered.values()].flatMap((a) => a.topics));
|
|
25373
|
-
const topics = new Map(
|
|
25374
|
-
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
25375
|
-
);
|
|
25376
|
-
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
25377
|
-
const { saved, failed } = await mutateMany(
|
|
25378
|
-
[...answered.keys()],
|
|
25379
|
-
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
25380
|
-
(memory) => {
|
|
25381
|
-
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
25382
|
-
return null;
|
|
25383
|
-
return {
|
|
25384
|
-
...memory,
|
|
25385
|
-
...!unchanged(memory) && {
|
|
25386
|
-
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
25387
|
-
topics_updated_at: Date.now()
|
|
25388
|
-
},
|
|
25389
|
-
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
25390
|
-
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
25391
|
-
};
|
|
25392
|
-
}
|
|
25393
|
-
);
|
|
25394
|
-
for (const id of answered.keys()) {
|
|
25395
|
-
if (saved.has(id)) stampedIds.push(id);
|
|
25396
|
-
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
25397
|
-
}
|
|
25398
|
-
if (failed.size)
|
|
25399
|
-
getLogger().warn(
|
|
25400
|
-
"[memory/remote-store] Failed to write extracted topics:",
|
|
25401
|
-
failed.values().next().value
|
|
25402
|
-
);
|
|
25403
|
-
} catch (error) {
|
|
25404
|
-
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
25405
|
-
for (const id of answered.keys()) fail(id, "link-failed");
|
|
25406
|
-
}
|
|
25407
|
-
return {
|
|
25408
|
-
entitiesByMemory,
|
|
25409
|
-
stampedIds,
|
|
25410
|
-
skippedIds,
|
|
25411
|
-
skippedReasons,
|
|
25412
|
-
batchCount: batches.length,
|
|
25413
|
-
durationMs: Date.now() - start,
|
|
25414
|
-
batches,
|
|
25415
|
-
llmFailures
|
|
25416
|
-
};
|
|
25417
|
-
},
|
|
25629
|
+
extractTopics: async (ids, extractOptions) => await withRemoteWorkLease(
|
|
25630
|
+
persistence,
|
|
25631
|
+
"topics",
|
|
25632
|
+
String(TOPICS_EXTRACTION_VERSION),
|
|
25633
|
+
(token, signal) => extractTopicsUnlocked(ids, extractOptions, token, signal)
|
|
25634
|
+
) ?? { ...await extractTopicsUnlocked([], extractOptions), deferred: true },
|
|
25418
25635
|
stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
|
|
25419
25636
|
const ids = [...new Set(memoryIds)];
|
|
25420
25637
|
const { saved, failed } = await mutateMany(
|
|
@@ -25511,14 +25728,29 @@ function createRemoteMemoryStore(options) {
|
|
|
25511
25728
|
let ready = false;
|
|
25512
25729
|
let cursor;
|
|
25513
25730
|
let pending = /* @__PURE__ */ new Set();
|
|
25731
|
+
let topicFingerprint;
|
|
25514
25732
|
let polling = false;
|
|
25515
25733
|
const poll = () => {
|
|
25516
25734
|
if (polling) return;
|
|
25517
25735
|
polling = true;
|
|
25518
25736
|
const current = generation;
|
|
25519
|
-
changesSince(cursor, pending).then(({ records, resume, failed }) => {
|
|
25737
|
+
changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
|
|
25738
|
+
let topicState;
|
|
25739
|
+
if ([...subscribers].some((s) => s.options.topics))
|
|
25740
|
+
try {
|
|
25741
|
+
topicState = JSON.stringify(
|
|
25742
|
+
(await persistence.topics()).map((t) => [t.name, t.version])
|
|
25743
|
+
);
|
|
25744
|
+
} catch (error) {
|
|
25745
|
+
getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
|
|
25746
|
+
}
|
|
25520
25747
|
if (current !== generation) return;
|
|
25521
25748
|
const changed = /* @__PURE__ */ new Set();
|
|
25749
|
+
if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
|
|
25750
|
+
for (const subscriber of subscribers)
|
|
25751
|
+
if (subscriber.options.topics) changed.add(subscriber);
|
|
25752
|
+
}
|
|
25753
|
+
if (topicState !== void 0) topicFingerprint = topicState;
|
|
25522
25754
|
for (const record2 of records) {
|
|
25523
25755
|
const id = record2.memory.memory_id;
|
|
25524
25756
|
const previous = seen.get(id);
|
|
@@ -25542,11 +25774,11 @@ function createRemoteMemoryStore(options) {
|
|
|
25542
25774
|
};
|
|
25543
25775
|
const unrepairable = /* @__PURE__ */ new Set();
|
|
25544
25776
|
let repairCursor;
|
|
25545
|
-
const
|
|
25777
|
+
const repairEmbeddingsUnlocked = async ({
|
|
25546
25778
|
maxRows = REPAIR_ROWS,
|
|
25547
25779
|
maxScan = REPAIR_SCAN,
|
|
25548
25780
|
cursor: start = repairCursor
|
|
25549
|
-
} = {}) => {
|
|
25781
|
+
} = {}, leaseToken, signal) => {
|
|
25550
25782
|
const scannedRows = [];
|
|
25551
25783
|
let cursor2 = start;
|
|
25552
25784
|
do {
|
|
@@ -25555,6 +25787,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25555
25787
|
include_archived: true,
|
|
25556
25788
|
include_quarantined: true,
|
|
25557
25789
|
include_superseded: true,
|
|
25790
|
+
...signal && { signal },
|
|
25558
25791
|
limit: Math.min(200, Math.max(1, maxScan - scannedRows.length)),
|
|
25559
25792
|
...cursor2 && { cursor: cursor2 }
|
|
25560
25793
|
});
|
|
@@ -25572,6 +25805,7 @@ function createRemoteMemoryStore(options) {
|
|
|
25572
25805
|
skippedConflict: 0
|
|
25573
25806
|
};
|
|
25574
25807
|
const finish = () => {
|
|
25808
|
+
signal?.throwIfAborted();
|
|
25575
25809
|
const settled = allStale.every(
|
|
25576
25810
|
({ memory }) => repaired.has(memory.memory_id) || unrepairable.has(memory.memory_id)
|
|
25577
25811
|
);
|
|
@@ -25580,13 +25814,15 @@ function createRemoteMemoryStore(options) {
|
|
|
25580
25814
|
};
|
|
25581
25815
|
const stale = allStale.slice(0, maxRows).map(({ memory }) => memory.memory_id);
|
|
25582
25816
|
if (stale.length === 0) return finish();
|
|
25583
|
-
const { records, unreadable } = await readEveryState(stale);
|
|
25817
|
+
const { records, unreadable } = await readEveryState(stale, signal);
|
|
25818
|
+
signal?.throwIfAborted();
|
|
25584
25819
|
result.failed += unreadable.size;
|
|
25585
25820
|
for (const id of stale)
|
|
25586
25821
|
if (unreadable.has(id) || !records.get(id)?.memory.content.trim()) unrepairable.add(id);
|
|
25587
25822
|
const targets = stale.filter((id) => !unrepairable.has(id)).map((id) => records.get(id));
|
|
25588
25823
|
if (targets.length === 0) return finish();
|
|
25589
|
-
const vectors = await embedForRepair(targets);
|
|
25824
|
+
const vectors = await embedForRepair(targets, signal);
|
|
25825
|
+
signal?.throwIfAborted();
|
|
25590
25826
|
if (!vectors) {
|
|
25591
25827
|
result.failed += targets.length;
|
|
25592
25828
|
return finish();
|
|
@@ -25598,15 +25834,28 @@ function createRemoteMemoryStore(options) {
|
|
|
25598
25834
|
continue;
|
|
25599
25835
|
}
|
|
25600
25836
|
await serialized(record2.memory.memory_id, async () => {
|
|
25837
|
+
signal?.throwIfAborted();
|
|
25601
25838
|
try {
|
|
25602
|
-
const saved = await persistence.
|
|
25603
|
-
|
|
25604
|
-
|
|
25839
|
+
const [saved] = await persistence.putMany(
|
|
25840
|
+
[
|
|
25841
|
+
{
|
|
25842
|
+
memory: {
|
|
25843
|
+
...record2.memory,
|
|
25844
|
+
embedding: storedVector(vector),
|
|
25845
|
+
embedding_model: model
|
|
25846
|
+
},
|
|
25847
|
+
expectedVersion: record2
|
|
25848
|
+
}
|
|
25849
|
+
],
|
|
25850
|
+
signal,
|
|
25851
|
+
leaseToken
|
|
25605
25852
|
);
|
|
25606
25853
|
notify("embedding", [saved]);
|
|
25607
25854
|
repaired.add(record2.memory.memory_id);
|
|
25608
25855
|
result.reembedded++;
|
|
25609
25856
|
} catch (error) {
|
|
25857
|
+
signal?.throwIfAborted();
|
|
25858
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
25610
25859
|
if (error instanceof RemoteMemoryError && error.code === "version_conflict")
|
|
25611
25860
|
result.skippedConflict++;
|
|
25612
25861
|
else result.failed++;
|
|
@@ -25615,19 +25864,28 @@ function createRemoteMemoryStore(options) {
|
|
|
25615
25864
|
}
|
|
25616
25865
|
return finish();
|
|
25617
25866
|
};
|
|
25618
|
-
const
|
|
25867
|
+
const repairEmbeddings = async (repairOptions = {}) => await withRemoteWorkLease(
|
|
25868
|
+
persistence,
|
|
25869
|
+
"embeddings",
|
|
25870
|
+
model,
|
|
25871
|
+
(token, signal) => repairEmbeddingsUnlocked(repairOptions, token, signal)
|
|
25872
|
+
) ?? { scanned: 0, reembedded: 0, failed: 0, skippedConflict: 0, deferred: true };
|
|
25873
|
+
const embedForRepair = async (targets, signal) => {
|
|
25619
25874
|
const texts = targets.map(({ memory }) => memory.content);
|
|
25620
25875
|
try {
|
|
25621
|
-
return await generateEmbeddings(texts, embeddingOptions);
|
|
25876
|
+
return await generateEmbeddings(texts, embeddingOptions, signal);
|
|
25622
25877
|
} catch (error) {
|
|
25878
|
+
signal?.throwIfAborted();
|
|
25623
25879
|
getLogger().warn("[memory/remote-store] Failed to embed memories for repair:", error);
|
|
25624
25880
|
if (!rejectsInput(error)) return null;
|
|
25625
25881
|
}
|
|
25626
25882
|
const vectors = [];
|
|
25627
25883
|
for (const [i, text14] of texts.entries()) {
|
|
25884
|
+
signal?.throwIfAborted();
|
|
25628
25885
|
try {
|
|
25629
|
-
[vectors[i]] = await generateEmbeddings([text14], embeddingOptions);
|
|
25886
|
+
[vectors[i]] = await generateEmbeddings([text14], embeddingOptions, signal);
|
|
25630
25887
|
} catch (error) {
|
|
25888
|
+
signal?.throwIfAborted();
|
|
25631
25889
|
if (!rejectsInput(error)) return null;
|
|
25632
25890
|
unrepairable.add(targets[i].memory.memory_id);
|
|
25633
25891
|
}
|
|
@@ -25635,6 +25893,26 @@ function createRemoteMemoryStore(options) {
|
|
|
25635
25893
|
return vectors;
|
|
25636
25894
|
};
|
|
25637
25895
|
return {
|
|
25896
|
+
createTopic: async (name) => {
|
|
25897
|
+
const canonical = await createRemoteTopic(persistence, name);
|
|
25898
|
+
if (canonical) notify("edit", []);
|
|
25899
|
+
return canonical;
|
|
25900
|
+
},
|
|
25901
|
+
importTopics: async (topics) => {
|
|
25902
|
+
if (!topics.length) return;
|
|
25903
|
+
const existing = new Set((await persistence.topics()).map((topic2) => topic2.name));
|
|
25904
|
+
const missing = topics.flatMap((topic2) => {
|
|
25905
|
+
const name = normalizeEntityName(topic2.name);
|
|
25906
|
+
if (existing.has(name)) return [];
|
|
25907
|
+
existing.add(name);
|
|
25908
|
+
return [{ ...topic2, name }];
|
|
25909
|
+
});
|
|
25910
|
+
if (!missing.length) return;
|
|
25911
|
+
await persistence.importTopics(missing);
|
|
25912
|
+
for (const subscriber of [...subscribers])
|
|
25913
|
+
if (subscriber.options.topics) safely(subscriber.onChange);
|
|
25914
|
+
},
|
|
25915
|
+
counts: () => persistence.counts(),
|
|
25638
25916
|
masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
|
|
25639
25917
|
topicMaintenance,
|
|
25640
25918
|
repairEmbeddings,
|
|
@@ -25651,8 +25929,8 @@ function createRemoteMemoryStore(options) {
|
|
|
25651
25929
|
include_quarantined: true,
|
|
25652
25930
|
include_superseded: true
|
|
25653
25931
|
})) {
|
|
25654
|
-
for (const
|
|
25655
|
-
const name = normalizeEntityName(
|
|
25932
|
+
for (const topic2 of parseTopics(memory.topics) ?? []) {
|
|
25933
|
+
const name = normalizeEntityName(topic2.name);
|
|
25656
25934
|
if (!name) continue;
|
|
25657
25935
|
const entry = topics.get(name) ?? {
|
|
25658
25936
|
name,
|
|
@@ -25660,12 +25938,24 @@ function createRemoteMemoryStore(options) {
|
|
|
25660
25938
|
memoryCount: 0,
|
|
25661
25939
|
members: /* @__PURE__ */ new Set()
|
|
25662
25940
|
};
|
|
25663
|
-
entry.kind ?? (entry.kind =
|
|
25941
|
+
entry.kind ?? (entry.kind = topic2.kind ?? null);
|
|
25664
25942
|
entry.members.add(memory.memory_id);
|
|
25665
25943
|
topics.set(name, entry);
|
|
25666
25944
|
}
|
|
25667
25945
|
}
|
|
25668
|
-
|
|
25946
|
+
for (const topic2 of await persistence.topics()) {
|
|
25947
|
+
if (topic2.is_deleted) continue;
|
|
25948
|
+
const current = topics.get(topic2.name);
|
|
25949
|
+
if (current) current.kind ?? (current.kind = topic2.kind ?? null);
|
|
25950
|
+
else
|
|
25951
|
+
topics.set(topic2.name, {
|
|
25952
|
+
name: topic2.name,
|
|
25953
|
+
kind: topic2.kind ?? null,
|
|
25954
|
+
memoryCount: 0,
|
|
25955
|
+
members: /* @__PURE__ */ new Set()
|
|
25956
|
+
});
|
|
25957
|
+
}
|
|
25958
|
+
return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
|
|
25669
25959
|
},
|
|
25670
25960
|
get: async (id) => asStored(await live(id)),
|
|
25671
25961
|
listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
|
|
@@ -25810,10 +26100,10 @@ function createRemoteMemoryStore(options) {
|
|
|
25810
26100
|
if (!saved) return [];
|
|
25811
26101
|
const now = /* @__PURE__ */ new Date();
|
|
25812
26102
|
return mergeTopics([], added).map(
|
|
25813
|
-
(
|
|
25814
|
-
uniqueId: normalizeEntityName(
|
|
25815
|
-
canonicalName: normalizeEntityName(
|
|
25816
|
-
kind:
|
|
26103
|
+
(topic2) => ({
|
|
26104
|
+
uniqueId: normalizeEntityName(topic2.name),
|
|
26105
|
+
canonicalName: normalizeEntityName(topic2.name),
|
|
26106
|
+
kind: topic2.kind ?? null,
|
|
25817
26107
|
createdAt: now,
|
|
25818
26108
|
updatedAt: now
|
|
25819
26109
|
})
|