@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/expo/index.mjs
CHANGED
|
@@ -4325,11 +4325,17 @@ async function generateEmbeddingsBatch(texts, headers, baseUrl, model, onUsage,
|
|
|
4325
4325
|
}
|
|
4326
4326
|
return response.data.data.map((item) => item.embedding ?? []);
|
|
4327
4327
|
}
|
|
4328
|
-
async function generateEmbeddings(texts, options) {
|
|
4328
|
+
async function generateEmbeddings(texts, options, signal) {
|
|
4329
|
+
signal?.throwIfAborted();
|
|
4329
4330
|
if (texts.length === 0) return [];
|
|
4330
4331
|
const total = options.totalTimeoutMs;
|
|
4331
|
-
if (total === void 0) return embedMany(texts, options,
|
|
4332
|
-
return withDeadline(
|
|
4332
|
+
if (total === void 0) return embedMany(texts, options, signal);
|
|
4333
|
+
return withDeadline(
|
|
4334
|
+
(inner) => embedMany(texts, options, inner),
|
|
4335
|
+
total,
|
|
4336
|
+
"embedding batch",
|
|
4337
|
+
signal
|
|
4338
|
+
);
|
|
4333
4339
|
}
|
|
4334
4340
|
async function embedMany(texts, options, outer) {
|
|
4335
4341
|
const { baseUrl = BASE_URL, model, batchSize, cache } = options;
|
|
@@ -9965,13 +9971,13 @@ function normalizeEntityName(name) {
|
|
|
9965
9971
|
return name.trim().toLowerCase();
|
|
9966
9972
|
}
|
|
9967
9973
|
var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
|
|
9968
|
-
function topicWeight(
|
|
9969
|
-
return
|
|
9974
|
+
function topicWeight(topic2) {
|
|
9975
|
+
return topic2.source === "user" ? 1 : topic2.weight ?? 1;
|
|
9970
9976
|
}
|
|
9971
9977
|
function primaryTopic(topics) {
|
|
9972
9978
|
let best = null;
|
|
9973
|
-
for (const
|
|
9974
|
-
if (best === null || topicWeight(
|
|
9979
|
+
for (const topic2 of topics ?? []) {
|
|
9980
|
+
if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
|
|
9975
9981
|
}
|
|
9976
9982
|
return best;
|
|
9977
9983
|
}
|
|
@@ -11540,7 +11546,7 @@ function resolveTopicsWrite(row, linked, inputs, source, mode) {
|
|
|
11540
11546
|
}
|
|
11541
11547
|
function weightsOf(stored2, inputs) {
|
|
11542
11548
|
const out = /* @__PURE__ */ new Map();
|
|
11543
|
-
for (const
|
|
11549
|
+
for (const topic2 of stored2 ?? []) out.set(normalizeEntityName(topic2.name), topic2.weight);
|
|
11544
11550
|
for (const input of inputs) {
|
|
11545
11551
|
const weight = parseTopicWeight(input.weight);
|
|
11546
11552
|
if (weight === void 0) continue;
|
|
@@ -12423,6 +12429,20 @@ async function getAllVaultMemoryContentsOp(ctx, options) {
|
|
|
12423
12429
|
async function countActiveVaultMemoriesOp(ctx) {
|
|
12424
12430
|
return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
|
|
12425
12431
|
}
|
|
12432
|
+
async function getVaultMemoryCountsOp(ctx) {
|
|
12433
|
+
const [live, archived, quarantined] = await Promise.all([
|
|
12434
|
+
countActiveVaultMemoriesOp(ctx),
|
|
12435
|
+
ctx.vaultMemoryCollection.query(
|
|
12436
|
+
...baseVaultConditions(ctx, { includeArchived: true }),
|
|
12437
|
+
Q6.where("archived_at", Q6.notEq(null))
|
|
12438
|
+
).fetchCount(),
|
|
12439
|
+
ctx.vaultMemoryCollection.query(
|
|
12440
|
+
...baseVaultConditions(ctx, { includeQuarantined: true }),
|
|
12441
|
+
Q6.where("trust_tier", "quarantined")
|
|
12442
|
+
).fetchCount()
|
|
12443
|
+
]);
|
|
12444
|
+
return { live, archived, quarantined };
|
|
12445
|
+
}
|
|
12426
12446
|
async function getActiveVaultMemoryIdsOp(ctx, ids) {
|
|
12427
12447
|
if (ids.length === 0) return /* @__PURE__ */ new Set();
|
|
12428
12448
|
const rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), Q6.where("id", Q6.oneOf(ids))).unsafeFetchRaw();
|
|
@@ -19085,7 +19105,8 @@ Each memory below is written as "<id>: <text>". Output strict JSON: {"memories":
|
|
|
19085
19105
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
19086
19106
|
- "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
|
|
19087
19107
|
No prose.`;
|
|
19088
|
-
async function extractEntitiesForMemories(memories, options) {
|
|
19108
|
+
async function extractEntitiesForMemories(memories, options, signal) {
|
|
19109
|
+
signal?.throwIfAborted();
|
|
19089
19110
|
const out = /* @__PURE__ */ new Map();
|
|
19090
19111
|
if (memories.length === 0) return out;
|
|
19091
19112
|
const redactor = resolvePiiRedactor(options.piiRedaction);
|
|
@@ -19098,6 +19119,7 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
|
|
|
19098
19119
|
|
|
19099
19120
|
` : "";
|
|
19100
19121
|
for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
|
|
19122
|
+
signal?.throwIfAborted();
|
|
19101
19123
|
const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
|
|
19102
19124
|
const start = Date.now();
|
|
19103
19125
|
let attempts = 0;
|
|
@@ -19145,6 +19167,7 @@ List each memory's named entities.`,
|
|
|
19145
19167
|
...options.totalTimeoutMs !== void 0 && { totalTimeoutMs: options.totalTimeoutMs },
|
|
19146
19168
|
...options.backoffMs && { backoffMs: options.backoffMs }
|
|
19147
19169
|
});
|
|
19170
|
+
signal?.throwIfAborted();
|
|
19148
19171
|
if (parsed === null) {
|
|
19149
19172
|
reportBatch(batch.length);
|
|
19150
19173
|
getLogger().warn(
|
|
@@ -19379,6 +19402,7 @@ function createLocalMemoryStore(options) {
|
|
|
19379
19402
|
backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
|
|
19380
19403
|
};
|
|
19381
19404
|
return {
|
|
19405
|
+
counts: () => getVaultMemoryCountsOp(vaultCtx),
|
|
19382
19406
|
masksEmbeddingInput: options.embeddingOptions.maskInput !== void 0,
|
|
19383
19407
|
list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
|
|
19384
19408
|
get: (id) => getVaultMemoryOp(vaultCtx, id),
|
|
@@ -19603,6 +19627,134 @@ async function decryptMemoryField(value, rings) {
|
|
|
19603
19627
|
return decryptWith(value, rings);
|
|
19604
19628
|
}
|
|
19605
19629
|
|
|
19630
|
+
// src/lib/memory/store/remoteTopics.ts
|
|
19631
|
+
function topic(value) {
|
|
19632
|
+
const t = value;
|
|
19633
|
+
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")
|
|
19634
|
+
throw new Error("Invalid nearby topic");
|
|
19635
|
+
return t;
|
|
19636
|
+
}
|
|
19637
|
+
function createRemoteTopicPersistence(request) {
|
|
19638
|
+
return {
|
|
19639
|
+
topics: async () => {
|
|
19640
|
+
const out = [];
|
|
19641
|
+
let cursor;
|
|
19642
|
+
do {
|
|
19643
|
+
const page = await request(
|
|
19644
|
+
"/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
|
|
19645
|
+
{}
|
|
19646
|
+
);
|
|
19647
|
+
if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
|
|
19648
|
+
throw new Error("Invalid nearby topic page");
|
|
19649
|
+
out.push(...page.items.map(topic));
|
|
19650
|
+
cursor = page.next_cursor;
|
|
19651
|
+
} while (cursor);
|
|
19652
|
+
return out;
|
|
19653
|
+
},
|
|
19654
|
+
putTopic: async (value, expectedVersion) => {
|
|
19655
|
+
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
19656
|
+
throw new Error("Expected topic version must be a nonnegative integer");
|
|
19657
|
+
return topic(
|
|
19658
|
+
await request("/topics", {
|
|
19659
|
+
method: "PUT",
|
|
19660
|
+
body: JSON.stringify({ topic: value, expected_version: expectedVersion })
|
|
19661
|
+
})
|
|
19662
|
+
);
|
|
19663
|
+
},
|
|
19664
|
+
importTopics: async (topics, signal) => {
|
|
19665
|
+
for (let i = 0; i < topics.length; i += 50) {
|
|
19666
|
+
signal?.throwIfAborted();
|
|
19667
|
+
await request(
|
|
19668
|
+
"/topics/import",
|
|
19669
|
+
{
|
|
19670
|
+
method: "POST",
|
|
19671
|
+
body: JSON.stringify({ topics: topics.slice(i, i + 50) })
|
|
19672
|
+
},
|
|
19673
|
+
signal
|
|
19674
|
+
);
|
|
19675
|
+
}
|
|
19676
|
+
signal?.throwIfAborted();
|
|
19677
|
+
}
|
|
19678
|
+
};
|
|
19679
|
+
}
|
|
19680
|
+
async function createRemoteTopic(persistence, name) {
|
|
19681
|
+
const canonical = normalizeEntityName(name);
|
|
19682
|
+
if (!canonical) return null;
|
|
19683
|
+
const existing = (await persistence.topics()).find((t) => t.name === canonical);
|
|
19684
|
+
if (existing && !existing.is_deleted) return canonical;
|
|
19685
|
+
try {
|
|
19686
|
+
await persistence.putTopic(
|
|
19687
|
+
{ name: canonical, is_deleted: false, ...existing?.kind && { kind: existing.kind } },
|
|
19688
|
+
existing?.version ?? 0
|
|
19689
|
+
);
|
|
19690
|
+
} catch (error) {
|
|
19691
|
+
if (!(error instanceof RemoteMemoryError) || error.code !== "version_conflict") throw error;
|
|
19692
|
+
const current = (await persistence.topics()).find((t) => t.name === canonical);
|
|
19693
|
+
if (!current || current.is_deleted) throw error;
|
|
19694
|
+
}
|
|
19695
|
+
return canonical;
|
|
19696
|
+
}
|
|
19697
|
+
|
|
19698
|
+
// src/lib/memory/store/remoteWork.ts
|
|
19699
|
+
function createRemoteWorkPersistence(request) {
|
|
19700
|
+
return {
|
|
19701
|
+
claimWork: async (kind, processorVersion, token) => {
|
|
19702
|
+
const result = await request("/work/claim", {
|
|
19703
|
+
method: "POST",
|
|
19704
|
+
body: JSON.stringify({
|
|
19705
|
+
kind,
|
|
19706
|
+
processor_version: processorVersion,
|
|
19707
|
+
...token && { token }
|
|
19708
|
+
})
|
|
19709
|
+
});
|
|
19710
|
+
if (!result || typeof result !== "object") throw new Error("Invalid nearby work lease");
|
|
19711
|
+
const lease = result.lease;
|
|
19712
|
+
if (lease === void 0) return null;
|
|
19713
|
+
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)))
|
|
19714
|
+
throw new Error("Invalid nearby work lease");
|
|
19715
|
+
return lease;
|
|
19716
|
+
},
|
|
19717
|
+
releaseWork: async (kind, token) => {
|
|
19718
|
+
await request("/work/release", { method: "POST", body: JSON.stringify({ kind, token }) });
|
|
19719
|
+
}
|
|
19720
|
+
};
|
|
19721
|
+
}
|
|
19722
|
+
async function withRemoteWorkLease(persistence, kind, processorVersion, task) {
|
|
19723
|
+
const lease = await persistence.claimWork(kind, processorVersion);
|
|
19724
|
+
if (!lease) return null;
|
|
19725
|
+
const controller = new AbortController();
|
|
19726
|
+
const leaseLostError = new RemoteMemoryError("Maintenance lease lost", 409, "work_lease_expired");
|
|
19727
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
19728
|
+
controller.signal.addEventListener("abort", () => reject(leaseLostError), {
|
|
19729
|
+
once: true
|
|
19730
|
+
});
|
|
19731
|
+
});
|
|
19732
|
+
let renewing;
|
|
19733
|
+
const timer = setInterval(() => {
|
|
19734
|
+
if (renewing || controller.signal.aborted) return;
|
|
19735
|
+
renewing = persistence.claimWork(kind, processorVersion, lease.token).then((renewed) => {
|
|
19736
|
+
if (!renewed) controller.abort(leaseLostError);
|
|
19737
|
+
}).catch((error) => getLogger().warn("[memory/work] Lease renewal failed:", error)).finally(() => {
|
|
19738
|
+
renewing = void 0;
|
|
19739
|
+
});
|
|
19740
|
+
}, 3e4);
|
|
19741
|
+
try {
|
|
19742
|
+
const result = await Promise.race([task(lease.token, controller.signal), cancelled]);
|
|
19743
|
+
await renewing;
|
|
19744
|
+
return controller.signal.aborted ? null : result;
|
|
19745
|
+
} catch (error) {
|
|
19746
|
+
if (controller.signal.aborted || isRemoteWorkLeaseExpired(error)) return null;
|
|
19747
|
+
throw error;
|
|
19748
|
+
} finally {
|
|
19749
|
+
clearInterval(timer);
|
|
19750
|
+
await renewing;
|
|
19751
|
+
await persistence.releaseWork(kind, lease.token).catch((error) => getLogger().warn("[memory/work] Lease release failed:", error));
|
|
19752
|
+
}
|
|
19753
|
+
}
|
|
19754
|
+
function isRemoteWorkLeaseExpired(error) {
|
|
19755
|
+
return error instanceof RemoteMemoryError && error.code === "work_lease_expired";
|
|
19756
|
+
}
|
|
19757
|
+
|
|
19606
19758
|
// src/lib/memory/store/remotePersistence.ts
|
|
19607
19759
|
var RemoteMemoryError = class extends Error {
|
|
19608
19760
|
constructor(message, status, code, retryAfterMs) {
|
|
@@ -19982,6 +20134,33 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
19982
20134
|
};
|
|
19983
20135
|
};
|
|
19984
20136
|
return {
|
|
20137
|
+
...createRemoteTopicPersistence(request),
|
|
20138
|
+
...createRemoteWorkPersistence(request),
|
|
20139
|
+
counts: async () => {
|
|
20140
|
+
const result = await request("/counts", {});
|
|
20141
|
+
if (!record(result) || ["live", "archived", "quarantined"].some(
|
|
20142
|
+
(key) => !Number.isSafeInteger(result[key]) || result[key] < 0
|
|
20143
|
+
))
|
|
20144
|
+
throw new Error("Invalid nearby memory counts");
|
|
20145
|
+
return {
|
|
20146
|
+
live: result.live,
|
|
20147
|
+
archived: result.archived,
|
|
20148
|
+
quarantined: result.quarantined
|
|
20149
|
+
};
|
|
20150
|
+
},
|
|
20151
|
+
temporal: async (window2, limit = 100) => {
|
|
20152
|
+
if (!Number.isFinite(window2.start) || !Number.isFinite(window2.end) || window2.end <= window2.start || !Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
20153
|
+
throw new Error("Temporal window must have end > start and limit must be 1\u2013100");
|
|
20154
|
+
const result = await request("/temporal", {
|
|
20155
|
+
method: "POST",
|
|
20156
|
+
body: JSON.stringify({ ...window2, limit })
|
|
20157
|
+
});
|
|
20158
|
+
if (!record(result) || !Array.isArray(result.items) || result.items.some(
|
|
20159
|
+
(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
|
|
20160
|
+
))
|
|
20161
|
+
throw new Error("Invalid nearby temporal candidates");
|
|
20162
|
+
return result.items.map((item) => item.memory_id);
|
|
20163
|
+
},
|
|
19985
20164
|
get: async (id, signal) => {
|
|
19986
20165
|
try {
|
|
19987
20166
|
return await decode(await request(memoryPath(id), {}, signal));
|
|
@@ -20030,7 +20209,7 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
20030
20209
|
write
|
|
20031
20210
|
);
|
|
20032
20211
|
},
|
|
20033
|
-
putMany: async (writes, signal) => {
|
|
20212
|
+
putMany: async (writes, signal, leaseToken) => {
|
|
20034
20213
|
if (writes.length < 1 || writes.length > 50 || new Set(writes.map((w) => w.memory.memory_id)).size !== writes.length)
|
|
20035
20214
|
throw new Error("Batch must contain 1\u201350 distinct memory ids");
|
|
20036
20215
|
const prepared = await Promise.all(
|
|
@@ -20040,7 +20219,11 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
20040
20219
|
"/memories/batch",
|
|
20041
20220
|
{
|
|
20042
20221
|
method: "POST",
|
|
20043
|
-
body: JSON.stringify({
|
|
20222
|
+
body: JSON.stringify({
|
|
20223
|
+
key_id: options.keyId,
|
|
20224
|
+
writes: prepared.map((w) => w.body),
|
|
20225
|
+
...leaseToken && { lease_token: leaseToken }
|
|
20226
|
+
})
|
|
20044
20227
|
},
|
|
20045
20228
|
signal
|
|
20046
20229
|
);
|
|
@@ -20799,6 +20982,12 @@ async function migrateMemoriesToRemote(options) {
|
|
|
20799
20982
|
let claimed = account.key_id === canonical.keyId;
|
|
20800
20983
|
if (!active) await createDecaySweeper({ vaultCtx: options.vaultCtx }).runSweep();
|
|
20801
20984
|
await backfillTopics(options.vaultCtx);
|
|
20985
|
+
const localTopics = options.topics ?? (options.vaultCtx.singleTenant === true ? (await options.vaultCtx.database.get("entity").query().unsafeFetchRaw()).map((t) => ({
|
|
20986
|
+
name: normalizeEntityName(typeof t.canonical_name === "string" ? t.canonical_name : ""),
|
|
20987
|
+
...typeof t.kind === "string" && { kind: t.kind }
|
|
20988
|
+
})).filter((t) => t.name.length > 0) : []);
|
|
20989
|
+
if (localTopics.length)
|
|
20990
|
+
await createRemoteTopicPersistence(request).importTopics(localTopics, signal);
|
|
20802
20991
|
const skipped = /* @__PURE__ */ new Map();
|
|
20803
20992
|
const reembedded = /* @__PURE__ */ new Set();
|
|
20804
20993
|
const unembedded = /* @__PURE__ */ new Set();
|
|
@@ -20925,7 +21114,6 @@ var MAX_QUERY_IDS = 1e3;
|
|
|
20925
21114
|
var MAX_QUERY_TOPICS = 100;
|
|
20926
21115
|
var MAX_QUERY_FILTER = 20;
|
|
20927
21116
|
var GRAPH_LANE_ROWS = 256;
|
|
20928
|
-
var TEMPORAL_LANE_ROWS = 256;
|
|
20929
21117
|
var REPAIR_ROWS = 50;
|
|
20930
21118
|
var REPAIR_SCAN = 2e3;
|
|
20931
21119
|
var DECAY_TRANSITIONS_PER_SWEEP = 200;
|
|
@@ -20996,21 +21184,21 @@ function subscriptionKey({ memory, version }, options) {
|
|
|
20996
21184
|
]);
|
|
20997
21185
|
}
|
|
20998
21186
|
function topicInputs(topics, source) {
|
|
20999
|
-
return topics.flatMap((
|
|
21000
|
-
const name = (typeof
|
|
21001
|
-
const kind = typeof
|
|
21002
|
-
const weight = typeof
|
|
21187
|
+
return topics.flatMap((topic2) => {
|
|
21188
|
+
const name = (typeof topic2 === "string" ? topic2 : topic2.name).trim();
|
|
21189
|
+
const kind = typeof topic2 === "string" ? void 0 : topic2.kind;
|
|
21190
|
+
const weight = typeof topic2 === "string" || source === "user" ? void 0 : parseTopicWeight(topic2.weight);
|
|
21003
21191
|
return name ? [{ name, ...kind && { kind }, source, ...weight && { weight } }] : [];
|
|
21004
21192
|
});
|
|
21005
21193
|
}
|
|
21006
21194
|
function mergeTopics(existing, added) {
|
|
21007
21195
|
const byName = /* @__PURE__ */ new Map();
|
|
21008
|
-
for (const
|
|
21009
|
-
const key = normalizeEntityName(
|
|
21196
|
+
for (const topic2 of [...existing, ...added]) {
|
|
21197
|
+
const key = normalizeEntityName(topic2.name);
|
|
21010
21198
|
const kept = byName.get(key);
|
|
21011
|
-
if (!kept) byName.set(key,
|
|
21012
|
-
else if (kept.weight !== void 0 &&
|
|
21013
|
-
byName.set(key, { ...kept, weight:
|
|
21199
|
+
if (!kept) byName.set(key, topic2);
|
|
21200
|
+
else if (kept.weight !== void 0 && topic2.weight !== void 0 && topic2.weight > kept.weight)
|
|
21201
|
+
byName.set(key, { ...kept, weight: topic2.weight });
|
|
21014
21202
|
}
|
|
21015
21203
|
return [...byName.values()];
|
|
21016
21204
|
}
|
|
@@ -21021,7 +21209,7 @@ function sameTopics(stored2, next) {
|
|
|
21021
21209
|
return next.every((t) => keys.has(key(t)));
|
|
21022
21210
|
}
|
|
21023
21211
|
function canonicalTopics(row) {
|
|
21024
|
-
return new Set((parseTopics(row.topics) ?? []).map((
|
|
21212
|
+
return new Set((parseTopics(row.topics) ?? []).map((topic2) => normalizeEntityName(topic2.name)));
|
|
21025
21213
|
}
|
|
21026
21214
|
function createRemoteMemoryStore(options) {
|
|
21027
21215
|
const { persistence, embeddingOptions } = options;
|
|
@@ -21096,20 +21284,7 @@ function createRemoteMemoryStore(options) {
|
|
|
21096
21284
|
const eventTimeRanking = async (query, now) => {
|
|
21097
21285
|
const window2 = parseQueryTimeWindow(query, now);
|
|
21098
21286
|
if (!window2) return [];
|
|
21099
|
-
|
|
21100
|
-
metadata2,
|
|
21101
|
-
{ event_time_overlaps: { start: window2.start, end: window2.end } },
|
|
21102
|
-
TEMPORAL_LANE_ROWS
|
|
21103
|
-
);
|
|
21104
|
-
return rows.map(({ memory }) => ({
|
|
21105
|
-
memoryId: memory.memory_id,
|
|
21106
|
-
score: scoreEventTimeOverlap(
|
|
21107
|
-
memory.event_time_start ?? null,
|
|
21108
|
-
memory.event_time_end ?? null,
|
|
21109
|
-
memory.event_time_kind ?? null,
|
|
21110
|
-
window2
|
|
21111
|
-
)
|
|
21112
|
-
})).filter((row) => row.score > 0).sort((a, b) => b.score - a.score).map((row) => row.memoryId);
|
|
21287
|
+
return persistence.temporal({ start: window2.start, end: window2.end });
|
|
21113
21288
|
};
|
|
21114
21289
|
const enumerateIds = async (read, ids, filters) => (await Promise.all(
|
|
21115
21290
|
chunks2([...new Set(ids)], MAX_QUERY_IDS).map(
|
|
@@ -21160,22 +21335,28 @@ function createRemoteMemoryStore(options) {
|
|
|
21160
21335
|
writeOptions?.signal?.throwIfAborted();
|
|
21161
21336
|
const next = edit(structuredClone(record2.memory));
|
|
21162
21337
|
if (!next) return null;
|
|
21163
|
-
const saved = writeOptions?.
|
|
21338
|
+
const saved = writeOptions?.leaseToken ? (await persistence.putMany(
|
|
21339
|
+
[{ memory: next, expectedVersion: record2 }],
|
|
21340
|
+
writeOptions.signal,
|
|
21341
|
+
writeOptions.leaseToken
|
|
21342
|
+
))[0] : writeOptions?.signal ? await persistence.put(next, record2, writeOptions.signal) : await persistence.put(next, record2);
|
|
21164
21343
|
notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
|
|
21165
21344
|
return saved;
|
|
21166
21345
|
};
|
|
21167
21346
|
const mutate = (id, change, edit, writeOptions) => serialized(id, () => mutateLocked(id, change, edit, writeOptions));
|
|
21168
21347
|
const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
|
|
21169
|
-
const mutateMany = async (ids, change, edit) => {
|
|
21348
|
+
const mutateMany = async (ids, change, edit, leaseToken, signal) => {
|
|
21170
21349
|
const saved = /* @__PURE__ */ new Map();
|
|
21171
21350
|
const failed = /* @__PURE__ */ new Map();
|
|
21172
21351
|
for (const slice of chunks2([...new Set(ids)], MAX_BATCH)) {
|
|
21173
21352
|
await serializedAll(slice, async () => {
|
|
21353
|
+
signal?.throwIfAborted();
|
|
21174
21354
|
const writes = [];
|
|
21175
21355
|
let read;
|
|
21176
21356
|
try {
|
|
21177
|
-
read = await readEveryState(slice);
|
|
21357
|
+
read = await readEveryState(slice, signal);
|
|
21178
21358
|
} catch (error) {
|
|
21359
|
+
signal?.throwIfAborted();
|
|
21179
21360
|
for (const id of slice) failed.set(id, error);
|
|
21180
21361
|
return;
|
|
21181
21362
|
}
|
|
@@ -21192,7 +21373,9 @@ function createRemoteMemoryStore(options) {
|
|
|
21192
21373
|
if (writes.length === 0) return;
|
|
21193
21374
|
try {
|
|
21194
21375
|
const written = await persistence.putMany(
|
|
21195
|
-
writes.map(({ before, memory }) => ({ memory, expectedVersion: before }))
|
|
21376
|
+
writes.map(({ before, memory }) => ({ memory, expectedVersion: before })),
|
|
21377
|
+
signal,
|
|
21378
|
+
leaseToken
|
|
21196
21379
|
);
|
|
21197
21380
|
const byChange = /* @__PURE__ */ new Map();
|
|
21198
21381
|
written.forEach((record2, i) => {
|
|
@@ -21202,7 +21385,9 @@ function createRemoteMemoryStore(options) {
|
|
|
21202
21385
|
});
|
|
21203
21386
|
for (const [kind, changed] of byChange) notify(kind, changed);
|
|
21204
21387
|
} catch (error) {
|
|
21205
|
-
|
|
21388
|
+
signal?.throwIfAborted();
|
|
21389
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
21390
|
+
const perRow = error instanceof RemoteMemoryError && (error.code === "version_conflict" || error.status === 429 && !leaseToken);
|
|
21206
21391
|
for (const { before } of writes) {
|
|
21207
21392
|
const id = before.memory.memory_id;
|
|
21208
21393
|
if (!perRow) {
|
|
@@ -21210,9 +21395,16 @@ function createRemoteMemoryStore(options) {
|
|
|
21210
21395
|
continue;
|
|
21211
21396
|
}
|
|
21212
21397
|
try {
|
|
21213
|
-
const record2 = await mutateLocked(
|
|
21398
|
+
const record2 = await mutateLocked(
|
|
21399
|
+
id,
|
|
21400
|
+
change,
|
|
21401
|
+
edit,
|
|
21402
|
+
leaseToken || signal ? { leaseToken, signal } : void 0
|
|
21403
|
+
);
|
|
21214
21404
|
if (record2) saved.set(id, record2);
|
|
21215
21405
|
} catch (rowError) {
|
|
21406
|
+
signal?.throwIfAborted();
|
|
21407
|
+
if (isRemoteWorkLeaseExpired(rowError)) throw rowError;
|
|
21216
21408
|
failed.set(id, rowError);
|
|
21217
21409
|
}
|
|
21218
21410
|
}
|
|
@@ -21276,7 +21468,7 @@ function createRemoteMemoryStore(options) {
|
|
|
21276
21468
|
return records.map((record2) => stored(record2.memory));
|
|
21277
21469
|
};
|
|
21278
21470
|
const asStored = (record2) => record2 ? stored(record2.memory) : null;
|
|
21279
|
-
const knownKinds = async (topics) => {
|
|
21471
|
+
const knownKinds = async (topics, signal) => {
|
|
21280
21472
|
const unkinded = [
|
|
21281
21473
|
...new Set(topics.filter((t) => !t.kind).map((t) => normalizeEntityName(t.name)))
|
|
21282
21474
|
];
|
|
@@ -21286,21 +21478,22 @@ function createRemoteMemoryStore(options) {
|
|
|
21286
21478
|
topics: names,
|
|
21287
21479
|
include_archived: true,
|
|
21288
21480
|
include_quarantined: true,
|
|
21289
|
-
include_superseded: true
|
|
21481
|
+
include_superseded: true,
|
|
21482
|
+
...signal && { signal }
|
|
21290
21483
|
})) {
|
|
21291
|
-
for (const
|
|
21292
|
-
const name = normalizeEntityName(
|
|
21293
|
-
if (
|
|
21484
|
+
for (const topic2 of parseTopics(memory.topics) ?? []) {
|
|
21485
|
+
const name = normalizeEntityName(topic2.name);
|
|
21486
|
+
if (topic2.kind && !kinds.has(name)) kinds.set(name, topic2.kind);
|
|
21294
21487
|
}
|
|
21295
21488
|
}
|
|
21296
21489
|
}
|
|
21297
|
-
return (
|
|
21298
|
-
const kind =
|
|
21299
|
-
return kind ? { ...
|
|
21490
|
+
return (topic2) => {
|
|
21491
|
+
const kind = topic2.kind ?? kinds.get(normalizeEntityName(topic2.name));
|
|
21492
|
+
return kind ? { ...topic2, kind } : topic2;
|
|
21300
21493
|
};
|
|
21301
21494
|
};
|
|
21302
21495
|
const withKnownKinds = async (topics) => topics.map(await knownKinds(topics));
|
|
21303
|
-
const readEveryState = async (ids) => {
|
|
21496
|
+
const readEveryState = async (ids, signal) => {
|
|
21304
21497
|
const records = /* @__PURE__ */ new Map();
|
|
21305
21498
|
const unreadable = /* @__PURE__ */ new Set();
|
|
21306
21499
|
for (const memory_ids of chunks2(ids, MAX_QUERY_IDS)) {
|
|
@@ -21313,6 +21506,7 @@ function createRemoteMemoryStore(options) {
|
|
|
21313
21506
|
include_quarantined: true,
|
|
21314
21507
|
include_superseded: true,
|
|
21315
21508
|
limit: 200,
|
|
21509
|
+
...signal && { signal },
|
|
21316
21510
|
...cursor2 && { cursor: cursor2 }
|
|
21317
21511
|
});
|
|
21318
21512
|
warnFailed(page.failed);
|
|
@@ -21323,6 +21517,111 @@ function createRemoteMemoryStore(options) {
|
|
|
21323
21517
|
}
|
|
21324
21518
|
return { records, unreadable };
|
|
21325
21519
|
};
|
|
21520
|
+
const extractTopicsUnlocked = async (memoryIds, extractOptions, leaseToken, signal) => {
|
|
21521
|
+
const start = Date.now();
|
|
21522
|
+
const extractedAt = extractOptions.now ?? start;
|
|
21523
|
+
const skippedIds = [];
|
|
21524
|
+
const skippedReasons = /* @__PURE__ */ new Map();
|
|
21525
|
+
const skip = (id, reason) => {
|
|
21526
|
+
if (skippedReasons.has(id)) return;
|
|
21527
|
+
skippedIds.push(id);
|
|
21528
|
+
skippedReasons.set(id, reason);
|
|
21529
|
+
};
|
|
21530
|
+
const ids = [...new Set(memoryIds)];
|
|
21531
|
+
const { records, unreadable } = await readEveryState(ids, signal);
|
|
21532
|
+
signal?.throwIfAborted();
|
|
21533
|
+
const inputs = [];
|
|
21534
|
+
for (const id of ids) {
|
|
21535
|
+
const memory = records.get(id)?.memory;
|
|
21536
|
+
if (unreadable.has(id)) skip(id, "unreadable");
|
|
21537
|
+
else if (!memory) skip(id, "not-found");
|
|
21538
|
+
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
21539
|
+
else inputs.push({ id, content: memory.content });
|
|
21540
|
+
}
|
|
21541
|
+
const batches = [];
|
|
21542
|
+
const llmFailures = /* @__PURE__ */ new Map();
|
|
21543
|
+
const entitiesByMemory = await extractEntitiesForMemories(
|
|
21544
|
+
inputs,
|
|
21545
|
+
{
|
|
21546
|
+
...extractOptions,
|
|
21547
|
+
onBatch: (batch) => {
|
|
21548
|
+
batches.push(batch);
|
|
21549
|
+
if (batch.failure) {
|
|
21550
|
+
for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
|
|
21551
|
+
}
|
|
21552
|
+
notifyMemoryCallback(extractOptions.onBatch, batch);
|
|
21553
|
+
}
|
|
21554
|
+
},
|
|
21555
|
+
signal
|
|
21556
|
+
);
|
|
21557
|
+
signal?.throwIfAborted();
|
|
21558
|
+
const answered = /* @__PURE__ */ new Map();
|
|
21559
|
+
for (const input of inputs) {
|
|
21560
|
+
const entities = entitiesByMemory.get(input.id);
|
|
21561
|
+
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
21562
|
+
else
|
|
21563
|
+
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
21564
|
+
}
|
|
21565
|
+
const stampedIds = [];
|
|
21566
|
+
const fail = (id, reason) => {
|
|
21567
|
+
skip(id, reason);
|
|
21568
|
+
entitiesByMemory.delete(id);
|
|
21569
|
+
};
|
|
21570
|
+
try {
|
|
21571
|
+
const kinded = await knownKinds(
|
|
21572
|
+
[...answered.values()].flatMap((a) => a.topics),
|
|
21573
|
+
signal
|
|
21574
|
+
);
|
|
21575
|
+
signal?.throwIfAborted();
|
|
21576
|
+
const topics = new Map(
|
|
21577
|
+
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
21578
|
+
);
|
|
21579
|
+
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
21580
|
+
const { saved, failed } = await mutateMany(
|
|
21581
|
+
[...answered.keys()],
|
|
21582
|
+
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
21583
|
+
(memory) => {
|
|
21584
|
+
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
21585
|
+
return null;
|
|
21586
|
+
return {
|
|
21587
|
+
...memory,
|
|
21588
|
+
...!unchanged(memory) && {
|
|
21589
|
+
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
21590
|
+
topics_updated_at: Date.now()
|
|
21591
|
+
},
|
|
21592
|
+
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
21593
|
+
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
21594
|
+
};
|
|
21595
|
+
},
|
|
21596
|
+
leaseToken,
|
|
21597
|
+
signal
|
|
21598
|
+
);
|
|
21599
|
+
for (const id of answered.keys()) {
|
|
21600
|
+
if (saved.has(id)) stampedIds.push(id);
|
|
21601
|
+
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
21602
|
+
}
|
|
21603
|
+
if (failed.size)
|
|
21604
|
+
getLogger().warn(
|
|
21605
|
+
"[memory/remote-store] Failed to write extracted topics:",
|
|
21606
|
+
failed.values().next().value
|
|
21607
|
+
);
|
|
21608
|
+
} catch (error) {
|
|
21609
|
+
signal?.throwIfAborted();
|
|
21610
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
21611
|
+
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
21612
|
+
for (const id of answered.keys()) fail(id, "link-failed");
|
|
21613
|
+
}
|
|
21614
|
+
return {
|
|
21615
|
+
entitiesByMemory,
|
|
21616
|
+
stampedIds,
|
|
21617
|
+
skippedIds,
|
|
21618
|
+
skippedReasons,
|
|
21619
|
+
batchCount: batches.length,
|
|
21620
|
+
durationMs: Date.now() - start,
|
|
21621
|
+
batches,
|
|
21622
|
+
llmFailures
|
|
21623
|
+
};
|
|
21624
|
+
};
|
|
21326
21625
|
const topicMaintenance = {
|
|
21327
21626
|
getTopicBacklog: async (backlogOptions = {}) => {
|
|
21328
21627
|
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
@@ -21340,97 +21639,12 @@ function createRemoteMemoryStore(options) {
|
|
|
21340
21639
|
topicsBackfill: []
|
|
21341
21640
|
};
|
|
21342
21641
|
},
|
|
21343
|
-
extractTopics: async (
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21349
|
-
if (skippedReasons.has(id)) return;
|
|
21350
|
-
skippedIds.push(id);
|
|
21351
|
-
skippedReasons.set(id, reason);
|
|
21352
|
-
};
|
|
21353
|
-
const ids = [...new Set(memoryIds)];
|
|
21354
|
-
const { records, unreadable } = await readEveryState(ids);
|
|
21355
|
-
const inputs = [];
|
|
21356
|
-
for (const id of ids) {
|
|
21357
|
-
const memory = records.get(id)?.memory;
|
|
21358
|
-
if (unreadable.has(id)) skip(id, "unreadable");
|
|
21359
|
-
else if (!memory) skip(id, "not-found");
|
|
21360
|
-
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
21361
|
-
else inputs.push({ id, content: memory.content });
|
|
21362
|
-
}
|
|
21363
|
-
const batches = [];
|
|
21364
|
-
const llmFailures = /* @__PURE__ */ new Map();
|
|
21365
|
-
const entitiesByMemory = await extractEntitiesForMemories(inputs, {
|
|
21366
|
-
...extractOptions,
|
|
21367
|
-
onBatch: (batch) => {
|
|
21368
|
-
batches.push(batch);
|
|
21369
|
-
if (batch.failure) {
|
|
21370
|
-
for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
|
|
21371
|
-
}
|
|
21372
|
-
notifyMemoryCallback(extractOptions.onBatch, batch);
|
|
21373
|
-
}
|
|
21374
|
-
});
|
|
21375
|
-
const answered = /* @__PURE__ */ new Map();
|
|
21376
|
-
for (const input of inputs) {
|
|
21377
|
-
const entities = entitiesByMemory.get(input.id);
|
|
21378
|
-
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
21379
|
-
else
|
|
21380
|
-
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
21381
|
-
}
|
|
21382
|
-
const stampedIds = [];
|
|
21383
|
-
const fail = (id, reason) => {
|
|
21384
|
-
skip(id, reason);
|
|
21385
|
-
entitiesByMemory.delete(id);
|
|
21386
|
-
};
|
|
21387
|
-
try {
|
|
21388
|
-
const kinded = await knownKinds([...answered.values()].flatMap((a) => a.topics));
|
|
21389
|
-
const topics = new Map(
|
|
21390
|
-
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
21391
|
-
);
|
|
21392
|
-
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
21393
|
-
const { saved, failed } = await mutateMany(
|
|
21394
|
-
[...answered.keys()],
|
|
21395
|
-
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
21396
|
-
(memory) => {
|
|
21397
|
-
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
21398
|
-
return null;
|
|
21399
|
-
return {
|
|
21400
|
-
...memory,
|
|
21401
|
-
...!unchanged(memory) && {
|
|
21402
|
-
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
21403
|
-
topics_updated_at: Date.now()
|
|
21404
|
-
},
|
|
21405
|
-
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
21406
|
-
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
21407
|
-
};
|
|
21408
|
-
}
|
|
21409
|
-
);
|
|
21410
|
-
for (const id of answered.keys()) {
|
|
21411
|
-
if (saved.has(id)) stampedIds.push(id);
|
|
21412
|
-
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
21413
|
-
}
|
|
21414
|
-
if (failed.size)
|
|
21415
|
-
getLogger().warn(
|
|
21416
|
-
"[memory/remote-store] Failed to write extracted topics:",
|
|
21417
|
-
failed.values().next().value
|
|
21418
|
-
);
|
|
21419
|
-
} catch (error) {
|
|
21420
|
-
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
21421
|
-
for (const id of answered.keys()) fail(id, "link-failed");
|
|
21422
|
-
}
|
|
21423
|
-
return {
|
|
21424
|
-
entitiesByMemory,
|
|
21425
|
-
stampedIds,
|
|
21426
|
-
skippedIds,
|
|
21427
|
-
skippedReasons,
|
|
21428
|
-
batchCount: batches.length,
|
|
21429
|
-
durationMs: Date.now() - start,
|
|
21430
|
-
batches,
|
|
21431
|
-
llmFailures
|
|
21432
|
-
};
|
|
21433
|
-
},
|
|
21642
|
+
extractTopics: async (ids, extractOptions) => await withRemoteWorkLease(
|
|
21643
|
+
persistence,
|
|
21644
|
+
"topics",
|
|
21645
|
+
String(TOPICS_EXTRACTION_VERSION),
|
|
21646
|
+
(token, signal) => extractTopicsUnlocked(ids, extractOptions, token, signal)
|
|
21647
|
+
) ?? { ...await extractTopicsUnlocked([], extractOptions), deferred: true },
|
|
21434
21648
|
stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
|
|
21435
21649
|
const ids = [...new Set(memoryIds)];
|
|
21436
21650
|
const { saved, failed } = await mutateMany(
|
|
@@ -21527,14 +21741,29 @@ function createRemoteMemoryStore(options) {
|
|
|
21527
21741
|
let ready = false;
|
|
21528
21742
|
let cursor;
|
|
21529
21743
|
let pending = /* @__PURE__ */ new Set();
|
|
21744
|
+
let topicFingerprint;
|
|
21530
21745
|
let polling = false;
|
|
21531
21746
|
const poll = () => {
|
|
21532
21747
|
if (polling) return;
|
|
21533
21748
|
polling = true;
|
|
21534
21749
|
const current = generation;
|
|
21535
|
-
changesSince(cursor, pending).then(({ records, resume, failed }) => {
|
|
21750
|
+
changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
|
|
21751
|
+
let topicState;
|
|
21752
|
+
if ([...subscribers].some((s) => s.options.topics))
|
|
21753
|
+
try {
|
|
21754
|
+
topicState = JSON.stringify(
|
|
21755
|
+
(await persistence.topics()).map((t) => [t.name, t.version])
|
|
21756
|
+
);
|
|
21757
|
+
} catch (error) {
|
|
21758
|
+
getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
|
|
21759
|
+
}
|
|
21536
21760
|
if (current !== generation) return;
|
|
21537
21761
|
const changed = /* @__PURE__ */ new Set();
|
|
21762
|
+
if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
|
|
21763
|
+
for (const subscriber of subscribers)
|
|
21764
|
+
if (subscriber.options.topics) changed.add(subscriber);
|
|
21765
|
+
}
|
|
21766
|
+
if (topicState !== void 0) topicFingerprint = topicState;
|
|
21538
21767
|
for (const record2 of records) {
|
|
21539
21768
|
const id = record2.memory.memory_id;
|
|
21540
21769
|
const previous = seen.get(id);
|
|
@@ -21558,11 +21787,11 @@ function createRemoteMemoryStore(options) {
|
|
|
21558
21787
|
};
|
|
21559
21788
|
const unrepairable = /* @__PURE__ */ new Set();
|
|
21560
21789
|
let repairCursor;
|
|
21561
|
-
const
|
|
21790
|
+
const repairEmbeddingsUnlocked = async ({
|
|
21562
21791
|
maxRows = REPAIR_ROWS,
|
|
21563
21792
|
maxScan = REPAIR_SCAN,
|
|
21564
21793
|
cursor: start = repairCursor
|
|
21565
|
-
} = {}) => {
|
|
21794
|
+
} = {}, leaseToken, signal) => {
|
|
21566
21795
|
const scannedRows = [];
|
|
21567
21796
|
let cursor2 = start;
|
|
21568
21797
|
do {
|
|
@@ -21571,6 +21800,7 @@ function createRemoteMemoryStore(options) {
|
|
|
21571
21800
|
include_archived: true,
|
|
21572
21801
|
include_quarantined: true,
|
|
21573
21802
|
include_superseded: true,
|
|
21803
|
+
...signal && { signal },
|
|
21574
21804
|
limit: Math.min(200, Math.max(1, maxScan - scannedRows.length)),
|
|
21575
21805
|
...cursor2 && { cursor: cursor2 }
|
|
21576
21806
|
});
|
|
@@ -21588,6 +21818,7 @@ function createRemoteMemoryStore(options) {
|
|
|
21588
21818
|
skippedConflict: 0
|
|
21589
21819
|
};
|
|
21590
21820
|
const finish = () => {
|
|
21821
|
+
signal?.throwIfAborted();
|
|
21591
21822
|
const settled = allStale.every(
|
|
21592
21823
|
({ memory }) => repaired.has(memory.memory_id) || unrepairable.has(memory.memory_id)
|
|
21593
21824
|
);
|
|
@@ -21596,13 +21827,15 @@ function createRemoteMemoryStore(options) {
|
|
|
21596
21827
|
};
|
|
21597
21828
|
const stale = allStale.slice(0, maxRows).map(({ memory }) => memory.memory_id);
|
|
21598
21829
|
if (stale.length === 0) return finish();
|
|
21599
|
-
const { records, unreadable } = await readEveryState(stale);
|
|
21830
|
+
const { records, unreadable } = await readEveryState(stale, signal);
|
|
21831
|
+
signal?.throwIfAborted();
|
|
21600
21832
|
result.failed += unreadable.size;
|
|
21601
21833
|
for (const id of stale)
|
|
21602
21834
|
if (unreadable.has(id) || !records.get(id)?.memory.content.trim()) unrepairable.add(id);
|
|
21603
21835
|
const targets = stale.filter((id) => !unrepairable.has(id)).map((id) => records.get(id));
|
|
21604
21836
|
if (targets.length === 0) return finish();
|
|
21605
|
-
const vectors = await embedForRepair(targets);
|
|
21837
|
+
const vectors = await embedForRepair(targets, signal);
|
|
21838
|
+
signal?.throwIfAborted();
|
|
21606
21839
|
if (!vectors) {
|
|
21607
21840
|
result.failed += targets.length;
|
|
21608
21841
|
return finish();
|
|
@@ -21614,15 +21847,28 @@ function createRemoteMemoryStore(options) {
|
|
|
21614
21847
|
continue;
|
|
21615
21848
|
}
|
|
21616
21849
|
await serialized(record2.memory.memory_id, async () => {
|
|
21850
|
+
signal?.throwIfAborted();
|
|
21617
21851
|
try {
|
|
21618
|
-
const saved = await persistence.
|
|
21619
|
-
|
|
21620
|
-
|
|
21852
|
+
const [saved] = await persistence.putMany(
|
|
21853
|
+
[
|
|
21854
|
+
{
|
|
21855
|
+
memory: {
|
|
21856
|
+
...record2.memory,
|
|
21857
|
+
embedding: storedVector(vector),
|
|
21858
|
+
embedding_model: model
|
|
21859
|
+
},
|
|
21860
|
+
expectedVersion: record2
|
|
21861
|
+
}
|
|
21862
|
+
],
|
|
21863
|
+
signal,
|
|
21864
|
+
leaseToken
|
|
21621
21865
|
);
|
|
21622
21866
|
notify("embedding", [saved]);
|
|
21623
21867
|
repaired.add(record2.memory.memory_id);
|
|
21624
21868
|
result.reembedded++;
|
|
21625
21869
|
} catch (error) {
|
|
21870
|
+
signal?.throwIfAborted();
|
|
21871
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
21626
21872
|
if (error instanceof RemoteMemoryError && error.code === "version_conflict")
|
|
21627
21873
|
result.skippedConflict++;
|
|
21628
21874
|
else result.failed++;
|
|
@@ -21631,19 +21877,28 @@ function createRemoteMemoryStore(options) {
|
|
|
21631
21877
|
}
|
|
21632
21878
|
return finish();
|
|
21633
21879
|
};
|
|
21634
|
-
const
|
|
21880
|
+
const repairEmbeddings = async (repairOptions = {}) => await withRemoteWorkLease(
|
|
21881
|
+
persistence,
|
|
21882
|
+
"embeddings",
|
|
21883
|
+
model,
|
|
21884
|
+
(token, signal) => repairEmbeddingsUnlocked(repairOptions, token, signal)
|
|
21885
|
+
) ?? { scanned: 0, reembedded: 0, failed: 0, skippedConflict: 0, deferred: true };
|
|
21886
|
+
const embedForRepair = async (targets, signal) => {
|
|
21635
21887
|
const texts = targets.map(({ memory }) => memory.content);
|
|
21636
21888
|
try {
|
|
21637
|
-
return await generateEmbeddings(texts, embeddingOptions);
|
|
21889
|
+
return await generateEmbeddings(texts, embeddingOptions, signal);
|
|
21638
21890
|
} catch (error) {
|
|
21891
|
+
signal?.throwIfAborted();
|
|
21639
21892
|
getLogger().warn("[memory/remote-store] Failed to embed memories for repair:", error);
|
|
21640
21893
|
if (!rejectsInput(error)) return null;
|
|
21641
21894
|
}
|
|
21642
21895
|
const vectors = [];
|
|
21643
21896
|
for (const [i, text14] of texts.entries()) {
|
|
21897
|
+
signal?.throwIfAborted();
|
|
21644
21898
|
try {
|
|
21645
|
-
[vectors[i]] = await generateEmbeddings([text14], embeddingOptions);
|
|
21899
|
+
[vectors[i]] = await generateEmbeddings([text14], embeddingOptions, signal);
|
|
21646
21900
|
} catch (error) {
|
|
21901
|
+
signal?.throwIfAborted();
|
|
21647
21902
|
if (!rejectsInput(error)) return null;
|
|
21648
21903
|
unrepairable.add(targets[i].memory.memory_id);
|
|
21649
21904
|
}
|
|
@@ -21651,6 +21906,26 @@ function createRemoteMemoryStore(options) {
|
|
|
21651
21906
|
return vectors;
|
|
21652
21907
|
};
|
|
21653
21908
|
return {
|
|
21909
|
+
createTopic: async (name) => {
|
|
21910
|
+
const canonical = await createRemoteTopic(persistence, name);
|
|
21911
|
+
if (canonical) notify("edit", []);
|
|
21912
|
+
return canonical;
|
|
21913
|
+
},
|
|
21914
|
+
importTopics: async (topics) => {
|
|
21915
|
+
if (!topics.length) return;
|
|
21916
|
+
const existing = new Set((await persistence.topics()).map((topic2) => topic2.name));
|
|
21917
|
+
const missing = topics.flatMap((topic2) => {
|
|
21918
|
+
const name = normalizeEntityName(topic2.name);
|
|
21919
|
+
if (existing.has(name)) return [];
|
|
21920
|
+
existing.add(name);
|
|
21921
|
+
return [{ ...topic2, name }];
|
|
21922
|
+
});
|
|
21923
|
+
if (!missing.length) return;
|
|
21924
|
+
await persistence.importTopics(missing);
|
|
21925
|
+
for (const subscriber of [...subscribers])
|
|
21926
|
+
if (subscriber.options.topics) safely(subscriber.onChange);
|
|
21927
|
+
},
|
|
21928
|
+
counts: () => persistence.counts(),
|
|
21654
21929
|
masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
|
|
21655
21930
|
topicMaintenance,
|
|
21656
21931
|
repairEmbeddings,
|
|
@@ -21667,8 +21942,8 @@ function createRemoteMemoryStore(options) {
|
|
|
21667
21942
|
include_quarantined: true,
|
|
21668
21943
|
include_superseded: true
|
|
21669
21944
|
})) {
|
|
21670
|
-
for (const
|
|
21671
|
-
const name = normalizeEntityName(
|
|
21945
|
+
for (const topic2 of parseTopics(memory.topics) ?? []) {
|
|
21946
|
+
const name = normalizeEntityName(topic2.name);
|
|
21672
21947
|
if (!name) continue;
|
|
21673
21948
|
const entry = topics.get(name) ?? {
|
|
21674
21949
|
name,
|
|
@@ -21676,12 +21951,24 @@ function createRemoteMemoryStore(options) {
|
|
|
21676
21951
|
memoryCount: 0,
|
|
21677
21952
|
members: /* @__PURE__ */ new Set()
|
|
21678
21953
|
};
|
|
21679
|
-
entry.kind ?? (entry.kind =
|
|
21954
|
+
entry.kind ?? (entry.kind = topic2.kind ?? null);
|
|
21680
21955
|
entry.members.add(memory.memory_id);
|
|
21681
21956
|
topics.set(name, entry);
|
|
21682
21957
|
}
|
|
21683
21958
|
}
|
|
21684
|
-
|
|
21959
|
+
for (const topic2 of await persistence.topics()) {
|
|
21960
|
+
if (topic2.is_deleted) continue;
|
|
21961
|
+
const current = topics.get(topic2.name);
|
|
21962
|
+
if (current) current.kind ?? (current.kind = topic2.kind ?? null);
|
|
21963
|
+
else
|
|
21964
|
+
topics.set(topic2.name, {
|
|
21965
|
+
name: topic2.name,
|
|
21966
|
+
kind: topic2.kind ?? null,
|
|
21967
|
+
memoryCount: 0,
|
|
21968
|
+
members: /* @__PURE__ */ new Set()
|
|
21969
|
+
});
|
|
21970
|
+
}
|
|
21971
|
+
return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
|
|
21685
21972
|
},
|
|
21686
21973
|
get: async (id) => asStored(await live(id)),
|
|
21687
21974
|
listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
|
|
@@ -21826,10 +22113,10 @@ function createRemoteMemoryStore(options) {
|
|
|
21826
22113
|
if (!saved) return [];
|
|
21827
22114
|
const now = /* @__PURE__ */ new Date();
|
|
21828
22115
|
return mergeTopics([], added).map(
|
|
21829
|
-
(
|
|
21830
|
-
uniqueId: normalizeEntityName(
|
|
21831
|
-
canonicalName: normalizeEntityName(
|
|
21832
|
-
kind:
|
|
22116
|
+
(topic2) => ({
|
|
22117
|
+
uniqueId: normalizeEntityName(topic2.name),
|
|
22118
|
+
canonicalName: normalizeEntityName(topic2.name),
|
|
22119
|
+
kind: topic2.kind ?? null,
|
|
21833
22120
|
createdAt: now,
|
|
21834
22121
|
updatedAt: now
|
|
21835
22122
|
})
|