@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.mjs
CHANGED
|
@@ -4946,11 +4946,17 @@ async function generateEmbeddingsBatch(texts, headers, baseUrl, model, onUsage,
|
|
|
4946
4946
|
}
|
|
4947
4947
|
return response.data.data.map((item) => item.embedding ?? []);
|
|
4948
4948
|
}
|
|
4949
|
-
async function generateEmbeddings(texts, options) {
|
|
4949
|
+
async function generateEmbeddings(texts, options, signal) {
|
|
4950
|
+
signal?.throwIfAborted();
|
|
4950
4951
|
if (texts.length === 0) return [];
|
|
4951
4952
|
const total = options.totalTimeoutMs;
|
|
4952
|
-
if (total === void 0) return embedMany(texts, options,
|
|
4953
|
-
return withDeadline(
|
|
4953
|
+
if (total === void 0) return embedMany(texts, options, signal);
|
|
4954
|
+
return withDeadline(
|
|
4955
|
+
(inner) => embedMany(texts, options, inner),
|
|
4956
|
+
total,
|
|
4957
|
+
"embedding batch",
|
|
4958
|
+
signal
|
|
4959
|
+
);
|
|
4954
4960
|
}
|
|
4955
4961
|
async function embedMany(texts, options, outer) {
|
|
4956
4962
|
const { baseUrl = BASE_URL, model, batchSize, cache } = options;
|
|
@@ -10745,13 +10751,13 @@ function normalizeEntityName(name) {
|
|
|
10745
10751
|
return name.trim().toLowerCase();
|
|
10746
10752
|
}
|
|
10747
10753
|
var TOPIC_SALIENCE_WEIGHTS = { primary: 1, secondary: 0.6, mention: 0.3 };
|
|
10748
|
-
function topicWeight(
|
|
10749
|
-
return
|
|
10754
|
+
function topicWeight(topic2) {
|
|
10755
|
+
return topic2.source === "user" ? 1 : topic2.weight ?? 1;
|
|
10750
10756
|
}
|
|
10751
10757
|
function primaryTopic(topics) {
|
|
10752
10758
|
let best = null;
|
|
10753
|
-
for (const
|
|
10754
|
-
if (best === null || topicWeight(
|
|
10759
|
+
for (const topic2 of topics ?? []) {
|
|
10760
|
+
if (best === null || topicWeight(topic2) > topicWeight(best)) best = topic2;
|
|
10755
10761
|
}
|
|
10756
10762
|
return best;
|
|
10757
10763
|
}
|
|
@@ -10889,7 +10895,7 @@ function resolveTopicsWrite(row, linked, inputs, source, mode) {
|
|
|
10889
10895
|
}
|
|
10890
10896
|
function weightsOf(stored2, inputs) {
|
|
10891
10897
|
const out = /* @__PURE__ */ new Map();
|
|
10892
|
-
for (const
|
|
10898
|
+
for (const topic2 of stored2 ?? []) out.set(normalizeEntityName(topic2.name), topic2.weight);
|
|
10893
10899
|
for (const input of inputs) {
|
|
10894
10900
|
const weight = parseTopicWeight(input.weight);
|
|
10895
10901
|
if (weight === void 0) continue;
|
|
@@ -14035,6 +14041,20 @@ async function getAllVaultMemoryContentsOp(ctx, options) {
|
|
|
14035
14041
|
async function countActiveVaultMemoriesOp(ctx) {
|
|
14036
14042
|
return ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx)).fetchCount();
|
|
14037
14043
|
}
|
|
14044
|
+
async function getVaultMemoryCountsOp(ctx) {
|
|
14045
|
+
const [live, archived, quarantined] = await Promise.all([
|
|
14046
|
+
countActiveVaultMemoriesOp(ctx),
|
|
14047
|
+
ctx.vaultMemoryCollection.query(
|
|
14048
|
+
...baseVaultConditions(ctx, { includeArchived: true }),
|
|
14049
|
+
Q9.where("archived_at", Q9.notEq(null))
|
|
14050
|
+
).fetchCount(),
|
|
14051
|
+
ctx.vaultMemoryCollection.query(
|
|
14052
|
+
...baseVaultConditions(ctx, { includeQuarantined: true }),
|
|
14053
|
+
Q9.where("trust_tier", "quarantined")
|
|
14054
|
+
).fetchCount()
|
|
14055
|
+
]);
|
|
14056
|
+
return { live, archived, quarantined };
|
|
14057
|
+
}
|
|
14038
14058
|
async function getActiveVaultMemoryIdsOp(ctx, ids) {
|
|
14039
14059
|
if (ids.length === 0) return /* @__PURE__ */ new Set();
|
|
14040
14060
|
const rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), Q9.where("id", Q9.oneOf(ids))).unsafeFetchRaw();
|
|
@@ -21190,7 +21210,8 @@ Each memory below is written as "<id>: <text>". Output strict JSON: {"memories":
|
|
|
21190
21210
|
- exactly one element per input memory; copy its id EXACTLY as written (the token before the first ": "), with no brackets, quotes, or extra characters
|
|
21191
21211
|
- "entities" may be empty when a memory mentions no named entities \u2014 most short memories have 0-3
|
|
21192
21212
|
No prose.`;
|
|
21193
|
-
async function extractEntitiesForMemories(memories, options) {
|
|
21213
|
+
async function extractEntitiesForMemories(memories, options, signal) {
|
|
21214
|
+
signal?.throwIfAborted();
|
|
21194
21215
|
const out = /* @__PURE__ */ new Map();
|
|
21195
21216
|
if (memories.length === 0) return out;
|
|
21196
21217
|
const redactor = resolvePiiRedactor(options.piiRedaction);
|
|
@@ -21203,6 +21224,7 @@ When a memory refers to one of these, reuse the EXACT existing name (same spelli
|
|
|
21203
21224
|
|
|
21204
21225
|
` : "";
|
|
21205
21226
|
for (let i = 0; i < memories.length; i += TOPIC_EXTRACTION_BATCH_SIZE) {
|
|
21227
|
+
signal?.throwIfAborted();
|
|
21206
21228
|
const batch = memories.slice(i, i + TOPIC_EXTRACTION_BATCH_SIZE);
|
|
21207
21229
|
const start = Date.now();
|
|
21208
21230
|
let attempts = 0;
|
|
@@ -21250,6 +21272,7 @@ List each memory's named entities.`,
|
|
|
21250
21272
|
...options.totalTimeoutMs !== void 0 && { totalTimeoutMs: options.totalTimeoutMs },
|
|
21251
21273
|
...options.backoffMs && { backoffMs: options.backoffMs }
|
|
21252
21274
|
});
|
|
21275
|
+
signal?.throwIfAborted();
|
|
21253
21276
|
if (parsed === null) {
|
|
21254
21277
|
reportBatch(batch.length);
|
|
21255
21278
|
getLogger().warn(
|
|
@@ -21484,6 +21507,7 @@ function createLocalMemoryStore(options) {
|
|
|
21484
21507
|
backfillTopics: (memoryIds) => backfillMemoryTopicsOp(vaultCtx, memoryIds)
|
|
21485
21508
|
};
|
|
21486
21509
|
return {
|
|
21510
|
+
counts: () => getVaultMemoryCountsOp(vaultCtx),
|
|
21487
21511
|
masksEmbeddingInput: options.embeddingOptions.maskInput !== void 0,
|
|
21488
21512
|
list: (listOptions) => getAllVaultMemoriesOp(vaultCtx, listOptions),
|
|
21489
21513
|
get: (id) => getVaultMemoryOp(vaultCtx, id),
|
|
@@ -21708,6 +21732,134 @@ async function decryptMemoryField(value, rings) {
|
|
|
21708
21732
|
return decryptWith(value, rings);
|
|
21709
21733
|
}
|
|
21710
21734
|
|
|
21735
|
+
// src/lib/memory/store/remoteTopics.ts
|
|
21736
|
+
function topic(value) {
|
|
21737
|
+
const t = value;
|
|
21738
|
+
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")
|
|
21739
|
+
throw new Error("Invalid nearby topic");
|
|
21740
|
+
return t;
|
|
21741
|
+
}
|
|
21742
|
+
function createRemoteTopicPersistence(request) {
|
|
21743
|
+
return {
|
|
21744
|
+
topics: async () => {
|
|
21745
|
+
const out = [];
|
|
21746
|
+
let cursor;
|
|
21747
|
+
do {
|
|
21748
|
+
const page = await request(
|
|
21749
|
+
"/topics?limit=200" + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""),
|
|
21750
|
+
{}
|
|
21751
|
+
);
|
|
21752
|
+
if (!Array.isArray(page.items) || page.next_cursor !== void 0 && typeof page.next_cursor !== "string")
|
|
21753
|
+
throw new Error("Invalid nearby topic page");
|
|
21754
|
+
out.push(...page.items.map(topic));
|
|
21755
|
+
cursor = page.next_cursor;
|
|
21756
|
+
} while (cursor);
|
|
21757
|
+
return out;
|
|
21758
|
+
},
|
|
21759
|
+
putTopic: async (value, expectedVersion) => {
|
|
21760
|
+
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
21761
|
+
throw new Error("Expected topic version must be a nonnegative integer");
|
|
21762
|
+
return topic(
|
|
21763
|
+
await request("/topics", {
|
|
21764
|
+
method: "PUT",
|
|
21765
|
+
body: JSON.stringify({ topic: value, expected_version: expectedVersion })
|
|
21766
|
+
})
|
|
21767
|
+
);
|
|
21768
|
+
},
|
|
21769
|
+
importTopics: async (topics, signal) => {
|
|
21770
|
+
for (let i = 0; i < topics.length; i += 50) {
|
|
21771
|
+
signal?.throwIfAborted();
|
|
21772
|
+
await request(
|
|
21773
|
+
"/topics/import",
|
|
21774
|
+
{
|
|
21775
|
+
method: "POST",
|
|
21776
|
+
body: JSON.stringify({ topics: topics.slice(i, i + 50) })
|
|
21777
|
+
},
|
|
21778
|
+
signal
|
|
21779
|
+
);
|
|
21780
|
+
}
|
|
21781
|
+
signal?.throwIfAborted();
|
|
21782
|
+
}
|
|
21783
|
+
};
|
|
21784
|
+
}
|
|
21785
|
+
async function createRemoteTopic(persistence, name) {
|
|
21786
|
+
const canonical = normalizeEntityName(name);
|
|
21787
|
+
if (!canonical) return null;
|
|
21788
|
+
const existing = (await persistence.topics()).find((t) => t.name === canonical);
|
|
21789
|
+
if (existing && !existing.is_deleted) return canonical;
|
|
21790
|
+
try {
|
|
21791
|
+
await persistence.putTopic(
|
|
21792
|
+
{ name: canonical, is_deleted: false, ...existing?.kind && { kind: existing.kind } },
|
|
21793
|
+
existing?.version ?? 0
|
|
21794
|
+
);
|
|
21795
|
+
} catch (error) {
|
|
21796
|
+
if (!(error instanceof RemoteMemoryError) || error.code !== "version_conflict") throw error;
|
|
21797
|
+
const current = (await persistence.topics()).find((t) => t.name === canonical);
|
|
21798
|
+
if (!current || current.is_deleted) throw error;
|
|
21799
|
+
}
|
|
21800
|
+
return canonical;
|
|
21801
|
+
}
|
|
21802
|
+
|
|
21803
|
+
// src/lib/memory/store/remoteWork.ts
|
|
21804
|
+
function createRemoteWorkPersistence(request) {
|
|
21805
|
+
return {
|
|
21806
|
+
claimWork: async (kind, processorVersion, token) => {
|
|
21807
|
+
const result = await request("/work/claim", {
|
|
21808
|
+
method: "POST",
|
|
21809
|
+
body: JSON.stringify({
|
|
21810
|
+
kind,
|
|
21811
|
+
processor_version: processorVersion,
|
|
21812
|
+
...token && { token }
|
|
21813
|
+
})
|
|
21814
|
+
});
|
|
21815
|
+
if (!result || typeof result !== "object") throw new Error("Invalid nearby work lease");
|
|
21816
|
+
const lease = result.lease;
|
|
21817
|
+
if (lease === void 0) return null;
|
|
21818
|
+
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)))
|
|
21819
|
+
throw new Error("Invalid nearby work lease");
|
|
21820
|
+
return lease;
|
|
21821
|
+
},
|
|
21822
|
+
releaseWork: async (kind, token) => {
|
|
21823
|
+
await request("/work/release", { method: "POST", body: JSON.stringify({ kind, token }) });
|
|
21824
|
+
}
|
|
21825
|
+
};
|
|
21826
|
+
}
|
|
21827
|
+
async function withRemoteWorkLease(persistence, kind, processorVersion, task) {
|
|
21828
|
+
const lease = await persistence.claimWork(kind, processorVersion);
|
|
21829
|
+
if (!lease) return null;
|
|
21830
|
+
const controller = new AbortController();
|
|
21831
|
+
const leaseLostError = new RemoteMemoryError("Maintenance lease lost", 409, "work_lease_expired");
|
|
21832
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
21833
|
+
controller.signal.addEventListener("abort", () => reject(leaseLostError), {
|
|
21834
|
+
once: true
|
|
21835
|
+
});
|
|
21836
|
+
});
|
|
21837
|
+
let renewing;
|
|
21838
|
+
const timer = setInterval(() => {
|
|
21839
|
+
if (renewing || controller.signal.aborted) return;
|
|
21840
|
+
renewing = persistence.claimWork(kind, processorVersion, lease.token).then((renewed) => {
|
|
21841
|
+
if (!renewed) controller.abort(leaseLostError);
|
|
21842
|
+
}).catch((error) => getLogger().warn("[memory/work] Lease renewal failed:", error)).finally(() => {
|
|
21843
|
+
renewing = void 0;
|
|
21844
|
+
});
|
|
21845
|
+
}, 3e4);
|
|
21846
|
+
try {
|
|
21847
|
+
const result = await Promise.race([task(lease.token, controller.signal), cancelled]);
|
|
21848
|
+
await renewing;
|
|
21849
|
+
return controller.signal.aborted ? null : result;
|
|
21850
|
+
} catch (error) {
|
|
21851
|
+
if (controller.signal.aborted || isRemoteWorkLeaseExpired(error)) return null;
|
|
21852
|
+
throw error;
|
|
21853
|
+
} finally {
|
|
21854
|
+
clearInterval(timer);
|
|
21855
|
+
await renewing;
|
|
21856
|
+
await persistence.releaseWork(kind, lease.token).catch((error) => getLogger().warn("[memory/work] Lease release failed:", error));
|
|
21857
|
+
}
|
|
21858
|
+
}
|
|
21859
|
+
function isRemoteWorkLeaseExpired(error) {
|
|
21860
|
+
return error instanceof RemoteMemoryError && error.code === "work_lease_expired";
|
|
21861
|
+
}
|
|
21862
|
+
|
|
21711
21863
|
// src/lib/memory/store/remotePersistence.ts
|
|
21712
21864
|
var RemoteMemoryError = class extends Error {
|
|
21713
21865
|
constructor(message, status, code, retryAfterMs) {
|
|
@@ -22087,6 +22239,33 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
22087
22239
|
};
|
|
22088
22240
|
};
|
|
22089
22241
|
return {
|
|
22242
|
+
...createRemoteTopicPersistence(request),
|
|
22243
|
+
...createRemoteWorkPersistence(request),
|
|
22244
|
+
counts: async () => {
|
|
22245
|
+
const result = await request("/counts", {});
|
|
22246
|
+
if (!record(result) || ["live", "archived", "quarantined"].some(
|
|
22247
|
+
(key) => !Number.isSafeInteger(result[key]) || result[key] < 0
|
|
22248
|
+
))
|
|
22249
|
+
throw new Error("Invalid nearby memory counts");
|
|
22250
|
+
return {
|
|
22251
|
+
live: result.live,
|
|
22252
|
+
archived: result.archived,
|
|
22253
|
+
quarantined: result.quarantined
|
|
22254
|
+
};
|
|
22255
|
+
},
|
|
22256
|
+
temporal: async (window2, limit = 100) => {
|
|
22257
|
+
if (!Number.isFinite(window2.start) || !Number.isFinite(window2.end) || window2.end <= window2.start || !Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
22258
|
+
throw new Error("Temporal window must have end > start and limit must be 1\u2013100");
|
|
22259
|
+
const result = await request("/temporal", {
|
|
22260
|
+
method: "POST",
|
|
22261
|
+
body: JSON.stringify({ ...window2, limit })
|
|
22262
|
+
});
|
|
22263
|
+
if (!record(result) || !Array.isArray(result.items) || result.items.some(
|
|
22264
|
+
(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
|
|
22265
|
+
))
|
|
22266
|
+
throw new Error("Invalid nearby temporal candidates");
|
|
22267
|
+
return result.items.map((item) => item.memory_id);
|
|
22268
|
+
},
|
|
22090
22269
|
get: async (id, signal) => {
|
|
22091
22270
|
try {
|
|
22092
22271
|
return await decode(await request(memoryPath(id), {}, signal));
|
|
@@ -22135,7 +22314,7 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
22135
22314
|
write
|
|
22136
22315
|
);
|
|
22137
22316
|
},
|
|
22138
|
-
putMany: async (writes, signal) => {
|
|
22317
|
+
putMany: async (writes, signal, leaseToken) => {
|
|
22139
22318
|
if (writes.length < 1 || writes.length > 50 || new Set(writes.map((w) => w.memory.memory_id)).size !== writes.length)
|
|
22140
22319
|
throw new Error("Batch must contain 1\u201350 distinct memory ids");
|
|
22141
22320
|
const prepared = await Promise.all(
|
|
@@ -22145,7 +22324,11 @@ async function createRemoteMemoryPersistence(options) {
|
|
|
22145
22324
|
"/memories/batch",
|
|
22146
22325
|
{
|
|
22147
22326
|
method: "POST",
|
|
22148
|
-
body: JSON.stringify({
|
|
22327
|
+
body: JSON.stringify({
|
|
22328
|
+
key_id: options.keyId,
|
|
22329
|
+
writes: prepared.map((w) => w.body),
|
|
22330
|
+
...leaseToken && { lease_token: leaseToken }
|
|
22331
|
+
})
|
|
22149
22332
|
},
|
|
22150
22333
|
signal
|
|
22151
22334
|
);
|
|
@@ -22904,6 +23087,12 @@ async function migrateMemoriesToRemote(options) {
|
|
|
22904
23087
|
let claimed = account.key_id === canonical.keyId;
|
|
22905
23088
|
if (!active) await createDecaySweeper({ vaultCtx: options.vaultCtx }).runSweep();
|
|
22906
23089
|
await backfillTopics(options.vaultCtx);
|
|
23090
|
+
const localTopics = options.topics ?? (options.vaultCtx.singleTenant === true ? (await options.vaultCtx.database.get("entity").query().unsafeFetchRaw()).map((t) => ({
|
|
23091
|
+
name: normalizeEntityName(typeof t.canonical_name === "string" ? t.canonical_name : ""),
|
|
23092
|
+
...typeof t.kind === "string" && { kind: t.kind }
|
|
23093
|
+
})).filter((t) => t.name.length > 0) : []);
|
|
23094
|
+
if (localTopics.length)
|
|
23095
|
+
await createRemoteTopicPersistence(request).importTopics(localTopics, signal);
|
|
22907
23096
|
const skipped = /* @__PURE__ */ new Map();
|
|
22908
23097
|
const reembedded = /* @__PURE__ */ new Set();
|
|
22909
23098
|
const unembedded = /* @__PURE__ */ new Set();
|
|
@@ -23030,7 +23219,6 @@ var MAX_QUERY_IDS = 1e3;
|
|
|
23030
23219
|
var MAX_QUERY_TOPICS = 100;
|
|
23031
23220
|
var MAX_QUERY_FILTER = 20;
|
|
23032
23221
|
var GRAPH_LANE_ROWS = 256;
|
|
23033
|
-
var TEMPORAL_LANE_ROWS = 256;
|
|
23034
23222
|
var REPAIR_ROWS = 50;
|
|
23035
23223
|
var REPAIR_SCAN = 2e3;
|
|
23036
23224
|
var DECAY_TRANSITIONS_PER_SWEEP = 200;
|
|
@@ -23101,21 +23289,21 @@ function subscriptionKey({ memory, version }, options) {
|
|
|
23101
23289
|
]);
|
|
23102
23290
|
}
|
|
23103
23291
|
function topicInputs(topics, source) {
|
|
23104
|
-
return topics.flatMap((
|
|
23105
|
-
const name = (typeof
|
|
23106
|
-
const kind = typeof
|
|
23107
|
-
const weight = typeof
|
|
23292
|
+
return topics.flatMap((topic2) => {
|
|
23293
|
+
const name = (typeof topic2 === "string" ? topic2 : topic2.name).trim();
|
|
23294
|
+
const kind = typeof topic2 === "string" ? void 0 : topic2.kind;
|
|
23295
|
+
const weight = typeof topic2 === "string" || source === "user" ? void 0 : parseTopicWeight(topic2.weight);
|
|
23108
23296
|
return name ? [{ name, ...kind && { kind }, source, ...weight && { weight } }] : [];
|
|
23109
23297
|
});
|
|
23110
23298
|
}
|
|
23111
23299
|
function mergeTopics(existing, added) {
|
|
23112
23300
|
const byName = /* @__PURE__ */ new Map();
|
|
23113
|
-
for (const
|
|
23114
|
-
const key = normalizeEntityName(
|
|
23301
|
+
for (const topic2 of [...existing, ...added]) {
|
|
23302
|
+
const key = normalizeEntityName(topic2.name);
|
|
23115
23303
|
const kept = byName.get(key);
|
|
23116
|
-
if (!kept) byName.set(key,
|
|
23117
|
-
else if (kept.weight !== void 0 &&
|
|
23118
|
-
byName.set(key, { ...kept, weight:
|
|
23304
|
+
if (!kept) byName.set(key, topic2);
|
|
23305
|
+
else if (kept.weight !== void 0 && topic2.weight !== void 0 && topic2.weight > kept.weight)
|
|
23306
|
+
byName.set(key, { ...kept, weight: topic2.weight });
|
|
23119
23307
|
}
|
|
23120
23308
|
return [...byName.values()];
|
|
23121
23309
|
}
|
|
@@ -23126,7 +23314,7 @@ function sameTopics(stored2, next) {
|
|
|
23126
23314
|
return next.every((t) => keys.has(key(t)));
|
|
23127
23315
|
}
|
|
23128
23316
|
function canonicalTopics(row) {
|
|
23129
|
-
return new Set((parseTopics(row.topics) ?? []).map((
|
|
23317
|
+
return new Set((parseTopics(row.topics) ?? []).map((topic2) => normalizeEntityName(topic2.name)));
|
|
23130
23318
|
}
|
|
23131
23319
|
function createRemoteMemoryStore(options) {
|
|
23132
23320
|
const { persistence, embeddingOptions } = options;
|
|
@@ -23201,20 +23389,7 @@ function createRemoteMemoryStore(options) {
|
|
|
23201
23389
|
const eventTimeRanking = async (query, now) => {
|
|
23202
23390
|
const window2 = parseQueryTimeWindow(query, now);
|
|
23203
23391
|
if (!window2) return [];
|
|
23204
|
-
|
|
23205
|
-
metadata2,
|
|
23206
|
-
{ event_time_overlaps: { start: window2.start, end: window2.end } },
|
|
23207
|
-
TEMPORAL_LANE_ROWS
|
|
23208
|
-
);
|
|
23209
|
-
return rows.map(({ memory }) => ({
|
|
23210
|
-
memoryId: memory.memory_id,
|
|
23211
|
-
score: scoreEventTimeOverlap(
|
|
23212
|
-
memory.event_time_start ?? null,
|
|
23213
|
-
memory.event_time_end ?? null,
|
|
23214
|
-
memory.event_time_kind ?? null,
|
|
23215
|
-
window2
|
|
23216
|
-
)
|
|
23217
|
-
})).filter((row) => row.score > 0).sort((a, b) => b.score - a.score).map((row) => row.memoryId);
|
|
23392
|
+
return persistence.temporal({ start: window2.start, end: window2.end });
|
|
23218
23393
|
};
|
|
23219
23394
|
const enumerateIds = async (read, ids, filters) => (await Promise.all(
|
|
23220
23395
|
chunks2([...new Set(ids)], MAX_QUERY_IDS).map(
|
|
@@ -23265,22 +23440,28 @@ function createRemoteMemoryStore(options) {
|
|
|
23265
23440
|
writeOptions?.signal?.throwIfAborted();
|
|
23266
23441
|
const next = edit(structuredClone(record2.memory));
|
|
23267
23442
|
if (!next) return null;
|
|
23268
|
-
const saved = writeOptions?.
|
|
23443
|
+
const saved = writeOptions?.leaseToken ? (await persistence.putMany(
|
|
23444
|
+
[{ memory: next, expectedVersion: record2 }],
|
|
23445
|
+
writeOptions.signal,
|
|
23446
|
+
writeOptions.leaseToken
|
|
23447
|
+
))[0] : writeOptions?.signal ? await persistence.put(next, record2, writeOptions.signal) : await persistence.put(next, record2);
|
|
23269
23448
|
notify(typeof change === "function" ? change(record2.memory) : change, [saved]);
|
|
23270
23449
|
return saved;
|
|
23271
23450
|
};
|
|
23272
23451
|
const mutate = (id, change, edit, writeOptions) => serialized(id, () => mutateLocked(id, change, edit, writeOptions));
|
|
23273
23452
|
const serializedAll = (ids, task) => [...new Set(ids)].sort().reduceRight((inner, id) => () => serialized(id, inner), task)();
|
|
23274
|
-
const mutateMany = async (ids, change, edit) => {
|
|
23453
|
+
const mutateMany = async (ids, change, edit, leaseToken, signal) => {
|
|
23275
23454
|
const saved = /* @__PURE__ */ new Map();
|
|
23276
23455
|
const failed = /* @__PURE__ */ new Map();
|
|
23277
23456
|
for (const slice of chunks2([...new Set(ids)], MAX_BATCH)) {
|
|
23278
23457
|
await serializedAll(slice, async () => {
|
|
23458
|
+
signal?.throwIfAborted();
|
|
23279
23459
|
const writes = [];
|
|
23280
23460
|
let read;
|
|
23281
23461
|
try {
|
|
23282
|
-
read = await readEveryState(slice);
|
|
23462
|
+
read = await readEveryState(slice, signal);
|
|
23283
23463
|
} catch (error) {
|
|
23464
|
+
signal?.throwIfAborted();
|
|
23284
23465
|
for (const id of slice) failed.set(id, error);
|
|
23285
23466
|
return;
|
|
23286
23467
|
}
|
|
@@ -23297,7 +23478,9 @@ function createRemoteMemoryStore(options) {
|
|
|
23297
23478
|
if (writes.length === 0) return;
|
|
23298
23479
|
try {
|
|
23299
23480
|
const written = await persistence.putMany(
|
|
23300
|
-
writes.map(({ before, memory }) => ({ memory, expectedVersion: before }))
|
|
23481
|
+
writes.map(({ before, memory }) => ({ memory, expectedVersion: before })),
|
|
23482
|
+
signal,
|
|
23483
|
+
leaseToken
|
|
23301
23484
|
);
|
|
23302
23485
|
const byChange = /* @__PURE__ */ new Map();
|
|
23303
23486
|
written.forEach((record2, i) => {
|
|
@@ -23307,7 +23490,9 @@ function createRemoteMemoryStore(options) {
|
|
|
23307
23490
|
});
|
|
23308
23491
|
for (const [kind, changed] of byChange) notify(kind, changed);
|
|
23309
23492
|
} catch (error) {
|
|
23310
|
-
|
|
23493
|
+
signal?.throwIfAborted();
|
|
23494
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
23495
|
+
const perRow = error instanceof RemoteMemoryError && (error.code === "version_conflict" || error.status === 429 && !leaseToken);
|
|
23311
23496
|
for (const { before } of writes) {
|
|
23312
23497
|
const id = before.memory.memory_id;
|
|
23313
23498
|
if (!perRow) {
|
|
@@ -23315,9 +23500,16 @@ function createRemoteMemoryStore(options) {
|
|
|
23315
23500
|
continue;
|
|
23316
23501
|
}
|
|
23317
23502
|
try {
|
|
23318
|
-
const record2 = await mutateLocked(
|
|
23503
|
+
const record2 = await mutateLocked(
|
|
23504
|
+
id,
|
|
23505
|
+
change,
|
|
23506
|
+
edit,
|
|
23507
|
+
leaseToken || signal ? { leaseToken, signal } : void 0
|
|
23508
|
+
);
|
|
23319
23509
|
if (record2) saved.set(id, record2);
|
|
23320
23510
|
} catch (rowError) {
|
|
23511
|
+
signal?.throwIfAborted();
|
|
23512
|
+
if (isRemoteWorkLeaseExpired(rowError)) throw rowError;
|
|
23321
23513
|
failed.set(id, rowError);
|
|
23322
23514
|
}
|
|
23323
23515
|
}
|
|
@@ -23381,7 +23573,7 @@ function createRemoteMemoryStore(options) {
|
|
|
23381
23573
|
return records.map((record2) => stored(record2.memory));
|
|
23382
23574
|
};
|
|
23383
23575
|
const asStored = (record2) => record2 ? stored(record2.memory) : null;
|
|
23384
|
-
const knownKinds = async (topics) => {
|
|
23576
|
+
const knownKinds = async (topics, signal) => {
|
|
23385
23577
|
const unkinded = [
|
|
23386
23578
|
...new Set(topics.filter((t) => !t.kind).map((t) => normalizeEntityName(t.name)))
|
|
23387
23579
|
];
|
|
@@ -23391,21 +23583,22 @@ function createRemoteMemoryStore(options) {
|
|
|
23391
23583
|
topics: names,
|
|
23392
23584
|
include_archived: true,
|
|
23393
23585
|
include_quarantined: true,
|
|
23394
|
-
include_superseded: true
|
|
23586
|
+
include_superseded: true,
|
|
23587
|
+
...signal && { signal }
|
|
23395
23588
|
})) {
|
|
23396
|
-
for (const
|
|
23397
|
-
const name = normalizeEntityName(
|
|
23398
|
-
if (
|
|
23589
|
+
for (const topic2 of parseTopics(memory.topics) ?? []) {
|
|
23590
|
+
const name = normalizeEntityName(topic2.name);
|
|
23591
|
+
if (topic2.kind && !kinds.has(name)) kinds.set(name, topic2.kind);
|
|
23399
23592
|
}
|
|
23400
23593
|
}
|
|
23401
23594
|
}
|
|
23402
|
-
return (
|
|
23403
|
-
const kind =
|
|
23404
|
-
return kind ? { ...
|
|
23595
|
+
return (topic2) => {
|
|
23596
|
+
const kind = topic2.kind ?? kinds.get(normalizeEntityName(topic2.name));
|
|
23597
|
+
return kind ? { ...topic2, kind } : topic2;
|
|
23405
23598
|
};
|
|
23406
23599
|
};
|
|
23407
23600
|
const withKnownKinds = async (topics) => topics.map(await knownKinds(topics));
|
|
23408
|
-
const readEveryState = async (ids) => {
|
|
23601
|
+
const readEveryState = async (ids, signal) => {
|
|
23409
23602
|
const records = /* @__PURE__ */ new Map();
|
|
23410
23603
|
const unreadable = /* @__PURE__ */ new Set();
|
|
23411
23604
|
for (const memory_ids of chunks2(ids, MAX_QUERY_IDS)) {
|
|
@@ -23418,6 +23611,7 @@ function createRemoteMemoryStore(options) {
|
|
|
23418
23611
|
include_quarantined: true,
|
|
23419
23612
|
include_superseded: true,
|
|
23420
23613
|
limit: 200,
|
|
23614
|
+
...signal && { signal },
|
|
23421
23615
|
...cursor2 && { cursor: cursor2 }
|
|
23422
23616
|
});
|
|
23423
23617
|
warnFailed(page.failed);
|
|
@@ -23428,6 +23622,111 @@ function createRemoteMemoryStore(options) {
|
|
|
23428
23622
|
}
|
|
23429
23623
|
return { records, unreadable };
|
|
23430
23624
|
};
|
|
23625
|
+
const extractTopicsUnlocked = async (memoryIds, extractOptions, leaseToken, signal) => {
|
|
23626
|
+
const start = Date.now();
|
|
23627
|
+
const extractedAt = extractOptions.now ?? start;
|
|
23628
|
+
const skippedIds = [];
|
|
23629
|
+
const skippedReasons = /* @__PURE__ */ new Map();
|
|
23630
|
+
const skip = (id, reason) => {
|
|
23631
|
+
if (skippedReasons.has(id)) return;
|
|
23632
|
+
skippedIds.push(id);
|
|
23633
|
+
skippedReasons.set(id, reason);
|
|
23634
|
+
};
|
|
23635
|
+
const ids = [...new Set(memoryIds)];
|
|
23636
|
+
const { records, unreadable } = await readEveryState(ids, signal);
|
|
23637
|
+
signal?.throwIfAborted();
|
|
23638
|
+
const inputs = [];
|
|
23639
|
+
for (const id of ids) {
|
|
23640
|
+
const memory = records.get(id)?.memory;
|
|
23641
|
+
if (unreadable.has(id)) skip(id, "unreadable");
|
|
23642
|
+
else if (!memory) skip(id, "not-found");
|
|
23643
|
+
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
23644
|
+
else inputs.push({ id, content: memory.content });
|
|
23645
|
+
}
|
|
23646
|
+
const batches = [];
|
|
23647
|
+
const llmFailures = /* @__PURE__ */ new Map();
|
|
23648
|
+
const entitiesByMemory = await extractEntitiesForMemories(
|
|
23649
|
+
inputs,
|
|
23650
|
+
{
|
|
23651
|
+
...extractOptions,
|
|
23652
|
+
onBatch: (batch) => {
|
|
23653
|
+
batches.push(batch);
|
|
23654
|
+
if (batch.failure) {
|
|
23655
|
+
for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
|
|
23656
|
+
}
|
|
23657
|
+
notifyMemoryCallback(extractOptions.onBatch, batch);
|
|
23658
|
+
}
|
|
23659
|
+
},
|
|
23660
|
+
signal
|
|
23661
|
+
);
|
|
23662
|
+
signal?.throwIfAborted();
|
|
23663
|
+
const answered = /* @__PURE__ */ new Map();
|
|
23664
|
+
for (const input of inputs) {
|
|
23665
|
+
const entities = entitiesByMemory.get(input.id);
|
|
23666
|
+
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
23667
|
+
else
|
|
23668
|
+
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
23669
|
+
}
|
|
23670
|
+
const stampedIds = [];
|
|
23671
|
+
const fail = (id, reason) => {
|
|
23672
|
+
skip(id, reason);
|
|
23673
|
+
entitiesByMemory.delete(id);
|
|
23674
|
+
};
|
|
23675
|
+
try {
|
|
23676
|
+
const kinded = await knownKinds(
|
|
23677
|
+
[...answered.values()].flatMap((a) => a.topics),
|
|
23678
|
+
signal
|
|
23679
|
+
);
|
|
23680
|
+
signal?.throwIfAborted();
|
|
23681
|
+
const topics = new Map(
|
|
23682
|
+
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
23683
|
+
);
|
|
23684
|
+
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
23685
|
+
const { saved, failed } = await mutateMany(
|
|
23686
|
+
[...answered.keys()],
|
|
23687
|
+
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
23688
|
+
(memory) => {
|
|
23689
|
+
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
23690
|
+
return null;
|
|
23691
|
+
return {
|
|
23692
|
+
...memory,
|
|
23693
|
+
...!unchanged(memory) && {
|
|
23694
|
+
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
23695
|
+
topics_updated_at: Date.now()
|
|
23696
|
+
},
|
|
23697
|
+
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
23698
|
+
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
23699
|
+
};
|
|
23700
|
+
},
|
|
23701
|
+
leaseToken,
|
|
23702
|
+
signal
|
|
23703
|
+
);
|
|
23704
|
+
for (const id of answered.keys()) {
|
|
23705
|
+
if (saved.has(id)) stampedIds.push(id);
|
|
23706
|
+
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
23707
|
+
}
|
|
23708
|
+
if (failed.size)
|
|
23709
|
+
getLogger().warn(
|
|
23710
|
+
"[memory/remote-store] Failed to write extracted topics:",
|
|
23711
|
+
failed.values().next().value
|
|
23712
|
+
);
|
|
23713
|
+
} catch (error) {
|
|
23714
|
+
signal?.throwIfAborted();
|
|
23715
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
23716
|
+
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
23717
|
+
for (const id of answered.keys()) fail(id, "link-failed");
|
|
23718
|
+
}
|
|
23719
|
+
return {
|
|
23720
|
+
entitiesByMemory,
|
|
23721
|
+
stampedIds,
|
|
23722
|
+
skippedIds,
|
|
23723
|
+
skippedReasons,
|
|
23724
|
+
batchCount: batches.length,
|
|
23725
|
+
durationMs: Date.now() - start,
|
|
23726
|
+
batches,
|
|
23727
|
+
llmFailures
|
|
23728
|
+
};
|
|
23729
|
+
};
|
|
23431
23730
|
const topicMaintenance = {
|
|
23432
23731
|
getTopicBacklog: async (backlogOptions = {}) => {
|
|
23433
23732
|
const limit = backlogOptions.limit !== void 0 && backlogOptions.limit > 0 ? backlogOptions.limit : Infinity;
|
|
@@ -23445,97 +23744,12 @@ function createRemoteMemoryStore(options) {
|
|
|
23445
23744
|
topicsBackfill: []
|
|
23446
23745
|
};
|
|
23447
23746
|
},
|
|
23448
|
-
extractTopics: async (
|
|
23449
|
-
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
|
|
23454
|
-
if (skippedReasons.has(id)) return;
|
|
23455
|
-
skippedIds.push(id);
|
|
23456
|
-
skippedReasons.set(id, reason);
|
|
23457
|
-
};
|
|
23458
|
-
const ids = [...new Set(memoryIds)];
|
|
23459
|
-
const { records, unreadable } = await readEveryState(ids);
|
|
23460
|
-
const inputs = [];
|
|
23461
|
-
for (const id of ids) {
|
|
23462
|
-
const memory = records.get(id)?.memory;
|
|
23463
|
-
if (unreadable.has(id)) skip(id, "unreadable");
|
|
23464
|
-
else if (!memory) skip(id, "not-found");
|
|
23465
|
-
else if (memory.is_deleted || memory.topics_user_managed) skip(id, "excluded");
|
|
23466
|
-
else inputs.push({ id, content: memory.content });
|
|
23467
|
-
}
|
|
23468
|
-
const batches = [];
|
|
23469
|
-
const llmFailures = /* @__PURE__ */ new Map();
|
|
23470
|
-
const entitiesByMemory = await extractEntitiesForMemories(inputs, {
|
|
23471
|
-
...extractOptions,
|
|
23472
|
-
onBatch: (batch) => {
|
|
23473
|
-
batches.push(batch);
|
|
23474
|
-
if (batch.failure) {
|
|
23475
|
-
for (const id of batch.memoryIds) llmFailures.set(id, batch.failure);
|
|
23476
|
-
}
|
|
23477
|
-
notifyMemoryCallback(extractOptions.onBatch, batch);
|
|
23478
|
-
}
|
|
23479
|
-
});
|
|
23480
|
-
const answered = /* @__PURE__ */ new Map();
|
|
23481
|
-
for (const input of inputs) {
|
|
23482
|
-
const entities = entitiesByMemory.get(input.id);
|
|
23483
|
-
if (entities === void 0) skip(input.id, "llm-unanswered");
|
|
23484
|
-
else
|
|
23485
|
-
answered.set(input.id, { content: input.content, topics: topicInputs(entities, "auto") });
|
|
23486
|
-
}
|
|
23487
|
-
const stampedIds = [];
|
|
23488
|
-
const fail = (id, reason) => {
|
|
23489
|
-
skip(id, reason);
|
|
23490
|
-
entitiesByMemory.delete(id);
|
|
23491
|
-
};
|
|
23492
|
-
try {
|
|
23493
|
-
const kinded = await knownKinds([...answered.values()].flatMap((a) => a.topics));
|
|
23494
|
-
const topics = new Map(
|
|
23495
|
-
[...answered].map(([id, a]) => [id, mergeTopics([], a.topics.map(kinded))])
|
|
23496
|
-
);
|
|
23497
|
-
const unchanged = (memory) => sameTopics(parseTopics(memory.topics), topics.get(memory.memory_id));
|
|
23498
|
-
const { saved, failed } = await mutateMany(
|
|
23499
|
-
[...answered.keys()],
|
|
23500
|
-
(before) => sameTopics(parseTopics(before.topics) ?? [], topics.get(before.memory_id)) ? "metadata" : "edit",
|
|
23501
|
-
(memory) => {
|
|
23502
|
-
if (memory.topics_user_managed || memory.content !== answered.get(memory.memory_id).content)
|
|
23503
|
-
return null;
|
|
23504
|
-
return {
|
|
23505
|
-
...memory,
|
|
23506
|
-
...!unchanged(memory) && {
|
|
23507
|
-
topics: serializeTopics(topics.get(memory.memory_id)),
|
|
23508
|
-
topics_updated_at: Date.now()
|
|
23509
|
-
},
|
|
23510
|
-
topics_extracted_at: Math.max(extractedAt, memory.updated_at),
|
|
23511
|
-
topics_extracted_version: TOPICS_EXTRACTION_VERSION
|
|
23512
|
-
};
|
|
23513
|
-
}
|
|
23514
|
-
);
|
|
23515
|
-
for (const id of answered.keys()) {
|
|
23516
|
-
if (saved.has(id)) stampedIds.push(id);
|
|
23517
|
-
else fail(id, failed.has(id) ? "link-failed" : "link-declined");
|
|
23518
|
-
}
|
|
23519
|
-
if (failed.size)
|
|
23520
|
-
getLogger().warn(
|
|
23521
|
-
"[memory/remote-store] Failed to write extracted topics:",
|
|
23522
|
-
failed.values().next().value
|
|
23523
|
-
);
|
|
23524
|
-
} catch (error) {
|
|
23525
|
-
getLogger().warn("[memory/remote-store] Failed to write extracted topics:", error);
|
|
23526
|
-
for (const id of answered.keys()) fail(id, "link-failed");
|
|
23527
|
-
}
|
|
23528
|
-
return {
|
|
23529
|
-
entitiesByMemory,
|
|
23530
|
-
stampedIds,
|
|
23531
|
-
skippedIds,
|
|
23532
|
-
skippedReasons,
|
|
23533
|
-
batchCount: batches.length,
|
|
23534
|
-
durationMs: Date.now() - start,
|
|
23535
|
-
batches,
|
|
23536
|
-
llmFailures
|
|
23537
|
-
};
|
|
23538
|
-
},
|
|
23747
|
+
extractTopics: async (ids, extractOptions) => await withRemoteWorkLease(
|
|
23748
|
+
persistence,
|
|
23749
|
+
"topics",
|
|
23750
|
+
String(TOPICS_EXTRACTION_VERSION),
|
|
23751
|
+
(token, signal) => extractTopicsUnlocked(ids, extractOptions, token, signal)
|
|
23752
|
+
) ?? { ...await extractTopicsUnlocked([], extractOptions), deferred: true },
|
|
23539
23753
|
stampTopicsExtracted: async (memoryIds, extractedAt, version = TOPICS_EXTRACTION_VERSION) => {
|
|
23540
23754
|
const ids = [...new Set(memoryIds)];
|
|
23541
23755
|
const { saved, failed } = await mutateMany(
|
|
@@ -23632,14 +23846,29 @@ function createRemoteMemoryStore(options) {
|
|
|
23632
23846
|
let ready = false;
|
|
23633
23847
|
let cursor;
|
|
23634
23848
|
let pending = /* @__PURE__ */ new Set();
|
|
23849
|
+
let topicFingerprint;
|
|
23635
23850
|
let polling = false;
|
|
23636
23851
|
const poll = () => {
|
|
23637
23852
|
if (polling) return;
|
|
23638
23853
|
polling = true;
|
|
23639
23854
|
const current = generation;
|
|
23640
|
-
changesSince(cursor, pending).then(({ records, resume, failed }) => {
|
|
23855
|
+
changesSince(cursor, pending).then(async ({ records, resume, failed }) => {
|
|
23856
|
+
let topicState;
|
|
23857
|
+
if ([...subscribers].some((s) => s.options.topics))
|
|
23858
|
+
try {
|
|
23859
|
+
topicState = JSON.stringify(
|
|
23860
|
+
(await persistence.topics()).map((t) => [t.name, t.version])
|
|
23861
|
+
);
|
|
23862
|
+
} catch (error) {
|
|
23863
|
+
getLogger().warn("[memory/remote-store] Topic subscription poll failed:", error);
|
|
23864
|
+
}
|
|
23641
23865
|
if (current !== generation) return;
|
|
23642
23866
|
const changed = /* @__PURE__ */ new Set();
|
|
23867
|
+
if (topicState !== void 0 && topicFingerprint !== void 0 && topicState !== topicFingerprint) {
|
|
23868
|
+
for (const subscriber of subscribers)
|
|
23869
|
+
if (subscriber.options.topics) changed.add(subscriber);
|
|
23870
|
+
}
|
|
23871
|
+
if (topicState !== void 0) topicFingerprint = topicState;
|
|
23643
23872
|
for (const record2 of records) {
|
|
23644
23873
|
const id = record2.memory.memory_id;
|
|
23645
23874
|
const previous = seen.get(id);
|
|
@@ -23663,11 +23892,11 @@ function createRemoteMemoryStore(options) {
|
|
|
23663
23892
|
};
|
|
23664
23893
|
const unrepairable = /* @__PURE__ */ new Set();
|
|
23665
23894
|
let repairCursor;
|
|
23666
|
-
const
|
|
23895
|
+
const repairEmbeddingsUnlocked = async ({
|
|
23667
23896
|
maxRows = REPAIR_ROWS,
|
|
23668
23897
|
maxScan = REPAIR_SCAN,
|
|
23669
23898
|
cursor: start = repairCursor
|
|
23670
|
-
} = {}) => {
|
|
23899
|
+
} = {}, leaseToken, signal) => {
|
|
23671
23900
|
const scannedRows = [];
|
|
23672
23901
|
let cursor2 = start;
|
|
23673
23902
|
do {
|
|
@@ -23676,6 +23905,7 @@ function createRemoteMemoryStore(options) {
|
|
|
23676
23905
|
include_archived: true,
|
|
23677
23906
|
include_quarantined: true,
|
|
23678
23907
|
include_superseded: true,
|
|
23908
|
+
...signal && { signal },
|
|
23679
23909
|
limit: Math.min(200, Math.max(1, maxScan - scannedRows.length)),
|
|
23680
23910
|
...cursor2 && { cursor: cursor2 }
|
|
23681
23911
|
});
|
|
@@ -23693,6 +23923,7 @@ function createRemoteMemoryStore(options) {
|
|
|
23693
23923
|
skippedConflict: 0
|
|
23694
23924
|
};
|
|
23695
23925
|
const finish = () => {
|
|
23926
|
+
signal?.throwIfAborted();
|
|
23696
23927
|
const settled = allStale.every(
|
|
23697
23928
|
({ memory }) => repaired.has(memory.memory_id) || unrepairable.has(memory.memory_id)
|
|
23698
23929
|
);
|
|
@@ -23701,13 +23932,15 @@ function createRemoteMemoryStore(options) {
|
|
|
23701
23932
|
};
|
|
23702
23933
|
const stale = allStale.slice(0, maxRows).map(({ memory }) => memory.memory_id);
|
|
23703
23934
|
if (stale.length === 0) return finish();
|
|
23704
|
-
const { records, unreadable } = await readEveryState(stale);
|
|
23935
|
+
const { records, unreadable } = await readEveryState(stale, signal);
|
|
23936
|
+
signal?.throwIfAborted();
|
|
23705
23937
|
result.failed += unreadable.size;
|
|
23706
23938
|
for (const id of stale)
|
|
23707
23939
|
if (unreadable.has(id) || !records.get(id)?.memory.content.trim()) unrepairable.add(id);
|
|
23708
23940
|
const targets = stale.filter((id) => !unrepairable.has(id)).map((id) => records.get(id));
|
|
23709
23941
|
if (targets.length === 0) return finish();
|
|
23710
|
-
const vectors = await embedForRepair(targets);
|
|
23942
|
+
const vectors = await embedForRepair(targets, signal);
|
|
23943
|
+
signal?.throwIfAborted();
|
|
23711
23944
|
if (!vectors) {
|
|
23712
23945
|
result.failed += targets.length;
|
|
23713
23946
|
return finish();
|
|
@@ -23719,15 +23952,28 @@ function createRemoteMemoryStore(options) {
|
|
|
23719
23952
|
continue;
|
|
23720
23953
|
}
|
|
23721
23954
|
await serialized(record2.memory.memory_id, async () => {
|
|
23955
|
+
signal?.throwIfAborted();
|
|
23722
23956
|
try {
|
|
23723
|
-
const saved = await persistence.
|
|
23724
|
-
|
|
23725
|
-
|
|
23957
|
+
const [saved] = await persistence.putMany(
|
|
23958
|
+
[
|
|
23959
|
+
{
|
|
23960
|
+
memory: {
|
|
23961
|
+
...record2.memory,
|
|
23962
|
+
embedding: storedVector(vector),
|
|
23963
|
+
embedding_model: model
|
|
23964
|
+
},
|
|
23965
|
+
expectedVersion: record2
|
|
23966
|
+
}
|
|
23967
|
+
],
|
|
23968
|
+
signal,
|
|
23969
|
+
leaseToken
|
|
23726
23970
|
);
|
|
23727
23971
|
notify("embedding", [saved]);
|
|
23728
23972
|
repaired.add(record2.memory.memory_id);
|
|
23729
23973
|
result.reembedded++;
|
|
23730
23974
|
} catch (error) {
|
|
23975
|
+
signal?.throwIfAborted();
|
|
23976
|
+
if (isRemoteWorkLeaseExpired(error)) throw error;
|
|
23731
23977
|
if (error instanceof RemoteMemoryError && error.code === "version_conflict")
|
|
23732
23978
|
result.skippedConflict++;
|
|
23733
23979
|
else result.failed++;
|
|
@@ -23736,19 +23982,28 @@ function createRemoteMemoryStore(options) {
|
|
|
23736
23982
|
}
|
|
23737
23983
|
return finish();
|
|
23738
23984
|
};
|
|
23739
|
-
const
|
|
23985
|
+
const repairEmbeddings = async (repairOptions = {}) => await withRemoteWorkLease(
|
|
23986
|
+
persistence,
|
|
23987
|
+
"embeddings",
|
|
23988
|
+
model,
|
|
23989
|
+
(token, signal) => repairEmbeddingsUnlocked(repairOptions, token, signal)
|
|
23990
|
+
) ?? { scanned: 0, reembedded: 0, failed: 0, skippedConflict: 0, deferred: true };
|
|
23991
|
+
const embedForRepair = async (targets, signal) => {
|
|
23740
23992
|
const texts = targets.map(({ memory }) => memory.content);
|
|
23741
23993
|
try {
|
|
23742
|
-
return await generateEmbeddings(texts, embeddingOptions);
|
|
23994
|
+
return await generateEmbeddings(texts, embeddingOptions, signal);
|
|
23743
23995
|
} catch (error) {
|
|
23996
|
+
signal?.throwIfAborted();
|
|
23744
23997
|
getLogger().warn("[memory/remote-store] Failed to embed memories for repair:", error);
|
|
23745
23998
|
if (!rejectsInput(error)) return null;
|
|
23746
23999
|
}
|
|
23747
24000
|
const vectors = [];
|
|
23748
24001
|
for (const [i, text14] of texts.entries()) {
|
|
24002
|
+
signal?.throwIfAborted();
|
|
23749
24003
|
try {
|
|
23750
|
-
[vectors[i]] = await generateEmbeddings([text14], embeddingOptions);
|
|
24004
|
+
[vectors[i]] = await generateEmbeddings([text14], embeddingOptions, signal);
|
|
23751
24005
|
} catch (error) {
|
|
24006
|
+
signal?.throwIfAborted();
|
|
23752
24007
|
if (!rejectsInput(error)) return null;
|
|
23753
24008
|
unrepairable.add(targets[i].memory.memory_id);
|
|
23754
24009
|
}
|
|
@@ -23756,6 +24011,26 @@ function createRemoteMemoryStore(options) {
|
|
|
23756
24011
|
return vectors;
|
|
23757
24012
|
};
|
|
23758
24013
|
return {
|
|
24014
|
+
createTopic: async (name) => {
|
|
24015
|
+
const canonical = await createRemoteTopic(persistence, name);
|
|
24016
|
+
if (canonical) notify("edit", []);
|
|
24017
|
+
return canonical;
|
|
24018
|
+
},
|
|
24019
|
+
importTopics: async (topics) => {
|
|
24020
|
+
if (!topics.length) return;
|
|
24021
|
+
const existing = new Set((await persistence.topics()).map((topic2) => topic2.name));
|
|
24022
|
+
const missing = topics.flatMap((topic2) => {
|
|
24023
|
+
const name = normalizeEntityName(topic2.name);
|
|
24024
|
+
if (existing.has(name)) return [];
|
|
24025
|
+
existing.add(name);
|
|
24026
|
+
return [{ ...topic2, name }];
|
|
24027
|
+
});
|
|
24028
|
+
if (!missing.length) return;
|
|
24029
|
+
await persistence.importTopics(missing);
|
|
24030
|
+
for (const subscriber of [...subscribers])
|
|
24031
|
+
if (subscriber.options.topics) safely(subscriber.onChange);
|
|
24032
|
+
},
|
|
24033
|
+
counts: () => persistence.counts(),
|
|
23759
24034
|
masksEmbeddingInput: embeddingOptions.maskInput !== void 0,
|
|
23760
24035
|
topicMaintenance,
|
|
23761
24036
|
repairEmbeddings,
|
|
@@ -23772,8 +24047,8 @@ function createRemoteMemoryStore(options) {
|
|
|
23772
24047
|
include_quarantined: true,
|
|
23773
24048
|
include_superseded: true
|
|
23774
24049
|
})) {
|
|
23775
|
-
for (const
|
|
23776
|
-
const name = normalizeEntityName(
|
|
24050
|
+
for (const topic2 of parseTopics(memory.topics) ?? []) {
|
|
24051
|
+
const name = normalizeEntityName(topic2.name);
|
|
23777
24052
|
if (!name) continue;
|
|
23778
24053
|
const entry = topics.get(name) ?? {
|
|
23779
24054
|
name,
|
|
@@ -23781,12 +24056,24 @@ function createRemoteMemoryStore(options) {
|
|
|
23781
24056
|
memoryCount: 0,
|
|
23782
24057
|
members: /* @__PURE__ */ new Set()
|
|
23783
24058
|
};
|
|
23784
|
-
entry.kind ?? (entry.kind =
|
|
24059
|
+
entry.kind ?? (entry.kind = topic2.kind ?? null);
|
|
23785
24060
|
entry.members.add(memory.memory_id);
|
|
23786
24061
|
topics.set(name, entry);
|
|
23787
24062
|
}
|
|
23788
24063
|
}
|
|
23789
|
-
|
|
24064
|
+
for (const topic2 of await persistence.topics()) {
|
|
24065
|
+
if (topic2.is_deleted) continue;
|
|
24066
|
+
const current = topics.get(topic2.name);
|
|
24067
|
+
if (current) current.kind ?? (current.kind = topic2.kind ?? null);
|
|
24068
|
+
else
|
|
24069
|
+
topics.set(topic2.name, {
|
|
24070
|
+
name: topic2.name,
|
|
24071
|
+
kind: topic2.kind ?? null,
|
|
24072
|
+
memoryCount: 0,
|
|
24073
|
+
members: /* @__PURE__ */ new Set()
|
|
24074
|
+
});
|
|
24075
|
+
}
|
|
24076
|
+
return [...topics.values()].map(({ members, ...topic2 }) => ({ ...topic2, memoryCount: members.size })).sort((a, b) => a.name.localeCompare(b.name));
|
|
23790
24077
|
},
|
|
23791
24078
|
get: async (id) => asStored(await live(id)),
|
|
23792
24079
|
listArchived: async () => (await enumerate({ archived_only: true, order: "archived", include_embeddings: true })).map(
|
|
@@ -23931,10 +24218,10 @@ function createRemoteMemoryStore(options) {
|
|
|
23931
24218
|
if (!saved) return [];
|
|
23932
24219
|
const now = /* @__PURE__ */ new Date();
|
|
23933
24220
|
return mergeTopics([], added).map(
|
|
23934
|
-
(
|
|
23935
|
-
uniqueId: normalizeEntityName(
|
|
23936
|
-
canonicalName: normalizeEntityName(
|
|
23937
|
-
kind:
|
|
24221
|
+
(topic2) => ({
|
|
24222
|
+
uniqueId: normalizeEntityName(topic2.name),
|
|
24223
|
+
canonicalName: normalizeEntityName(topic2.name),
|
|
24224
|
+
kind: topic2.kind ?? null,
|
|
23938
24225
|
createdAt: now,
|
|
23939
24226
|
updatedAt: now
|
|
23940
24227
|
})
|