@anuma/sdk 9.1.1 → 9.1.3
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 +59 -14
- package/dist/expo/index.d.mts +1 -14
- package/dist/expo/index.d.ts +1 -14
- package/dist/expo/index.mjs +59 -14
- package/dist/index.cjs +9 -4
- package/dist/index.mjs +9 -4
- package/dist/react/index.cjs +59 -14
- package/dist/react/index.d.mts +1 -14
- package/dist/react/index.d.ts +1 -14
- package/dist/react/index.mjs +59 -14
- package/dist/server/index.cjs +59 -14
- package/dist/server/index.d.mts +1 -14
- package/dist/server/index.d.ts +1 -14
- package/dist/server/index.mjs +59 -14
- package/package.json +1 -1
package/dist/expo/index.cjs
CHANGED
|
@@ -2326,6 +2326,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2326
2326
|
if (cachedKey) {
|
|
2327
2327
|
return cachedKey;
|
|
2328
2328
|
}
|
|
2329
|
+
const startEpoch = sessionEpoch;
|
|
2329
2330
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
2330
2331
|
if (!keyHex) {
|
|
2331
2332
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -2338,6 +2339,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2338
2339
|
false,
|
|
2339
2340
|
["encrypt", "decrypt"]
|
|
2340
2341
|
);
|
|
2342
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
2343
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
2344
|
+
}
|
|
2341
2345
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
2342
2346
|
return cryptoKey;
|
|
2343
2347
|
}
|
|
@@ -14817,9 +14821,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
|
|
|
14817
14821
|
}
|
|
14818
14822
|
function baseVaultSql(ctx, options) {
|
|
14819
14823
|
const clauses = [
|
|
14824
|
+
`"_status" is not 'deleted'`,
|
|
14820
14825
|
'"is_deleted" = 0',
|
|
14821
14826
|
...options?.includeArchived ? [] : ['"archived_at" is null'],
|
|
14822
|
-
`"trust_tier" is not 'quarantined'
|
|
14827
|
+
...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
|
|
14823
14828
|
'"superseded_by" is null'
|
|
14824
14829
|
];
|
|
14825
14830
|
const args = [];
|
|
@@ -15305,8 +15310,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
15305
15310
|
if (!entityCtx) {
|
|
15306
15311
|
throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
|
|
15307
15312
|
}
|
|
15308
|
-
|
|
15309
|
-
|
|
15313
|
+
let rows;
|
|
15314
|
+
try {
|
|
15315
|
+
const base = baseVaultSql(ctx);
|
|
15316
|
+
const sql = `select "id", "topics", "topics_user_managed", "topics_extracted_at", "topics_extracted_version", "updated_at" from "memory_vault" where ${base.sql} order by "created_at" desc`;
|
|
15317
|
+
rows = await ctx.vaultMemoryCollection.query(import_watermelondb21.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
15318
|
+
} catch (err) {
|
|
15319
|
+
getLogger().debug(
|
|
15320
|
+
"memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
15321
|
+
);
|
|
15322
|
+
rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb21.Q.sortBy("created_at", import_watermelondb21.Q.desc)).unsafeFetchRaw();
|
|
15323
|
+
}
|
|
15310
15324
|
const linkedNames = await linkedEntityNamesByMemory(
|
|
15311
15325
|
entityCtx,
|
|
15312
15326
|
rows.map((r) => r.id)
|
|
@@ -15365,10 +15379,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
15365
15379
|
}
|
|
15366
15380
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
15367
15381
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
15368
|
-
const pending =
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15382
|
+
const pending = [];
|
|
15383
|
+
for (let i = 0; i < limitedPendingRaw.length; i += 500) {
|
|
15384
|
+
const chunk = limitedPendingRaw.slice(i, i + 500);
|
|
15385
|
+
const memories = await getVaultMemoriesByIdsOp(
|
|
15386
|
+
ctx,
|
|
15387
|
+
chunk.map((raw) => raw.id)
|
|
15388
|
+
);
|
|
15389
|
+
const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
15390
|
+
for (const raw of chunk) {
|
|
15391
|
+
const memory = byId.get(raw.id);
|
|
15392
|
+
if (memory) pending.push(memory);
|
|
15393
|
+
}
|
|
15394
|
+
}
|
|
15372
15395
|
const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
|
|
15373
15396
|
return {
|
|
15374
15397
|
pending,
|
|
@@ -15515,10 +15538,20 @@ function assertVaultScopeForSweep(ctx) {
|
|
|
15515
15538
|
}
|
|
15516
15539
|
async function getDecayCandidatesRawOp(ctx) {
|
|
15517
15540
|
assertVaultScopeForSweep(ctx);
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15541
|
+
let results;
|
|
15542
|
+
try {
|
|
15543
|
+
const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
|
|
15544
|
+
const sql = `select "id", "fact_type", "event_time_end", "event_time_kind", "updated_at", "last_observed_at", "archived_at", "source", "trust_tier" from "memory_vault" where ${base.sql} and "kind" is null`;
|
|
15545
|
+
results = await ctx.vaultMemoryCollection.query(import_watermelondb21.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
15546
|
+
} catch (err) {
|
|
15547
|
+
getLogger().debug(
|
|
15548
|
+
"memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
15549
|
+
);
|
|
15550
|
+
results = await ctx.vaultMemoryCollection.query(
|
|
15551
|
+
...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
|
|
15552
|
+
import_watermelondb21.Q.where("kind", null)
|
|
15553
|
+
).unsafeFetchRaw();
|
|
15554
|
+
}
|
|
15522
15555
|
return results.map((raw) => ({
|
|
15523
15556
|
uniqueId: raw.id,
|
|
15524
15557
|
factType: raw.fact_type ?? null,
|
|
@@ -24986,6 +25019,18 @@ async function assembleMemoryContext(options) {
|
|
|
24986
25019
|
|
|
24987
25020
|
// src/lib/memory/durableExtraction.ts
|
|
24988
25021
|
var import_watermelondb25 = require("@nozbe/watermelondb");
|
|
25022
|
+
async function messagePositions(storage, ids) {
|
|
25023
|
+
try {
|
|
25024
|
+
return await storage.messagesCollection.query(
|
|
25025
|
+
import_watermelondb25.Q.unsafeSqlQuery(
|
|
25026
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
25027
|
+
ids
|
|
25028
|
+
)
|
|
25029
|
+
).unsafeFetchRaw();
|
|
25030
|
+
} catch {
|
|
25031
|
+
return await storage.messagesCollection.query(import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).unsafeFetchRaw();
|
|
25032
|
+
}
|
|
25033
|
+
}
|
|
24989
25034
|
var MAX_JOBS_PER_PASS = 3;
|
|
24990
25035
|
var MAX_ATTEMPTS = 3;
|
|
24991
25036
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -25554,9 +25599,9 @@ function createDurableAutoExtractor(options) {
|
|
|
25554
25599
|
r._setRaw("watermark_seq", null);
|
|
25555
25600
|
});
|
|
25556
25601
|
}
|
|
25557
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
25602
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
25558
25603
|
const foreignRows = new Set(
|
|
25559
|
-
rows.filter((row) => row.
|
|
25604
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
25560
25605
|
);
|
|
25561
25606
|
const sequences = /* @__PURE__ */ new Map();
|
|
25562
25607
|
if (fromSourceConversation) {
|
|
@@ -25566,7 +25611,7 @@ function createDurableAutoExtractor(options) {
|
|
|
25566
25611
|
}
|
|
25567
25612
|
for (const row of rows) {
|
|
25568
25613
|
if (foreignRows.has(row.id)) continue;
|
|
25569
|
-
const sequence = Number(row.
|
|
25614
|
+
const sequence = Number(row.message_id);
|
|
25570
25615
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
25571
25616
|
}
|
|
25572
25617
|
let boundary = -1;
|
package/dist/expo/index.d.mts
CHANGED
|
@@ -6090,20 +6090,7 @@ interface DecayCandidateRaw {
|
|
|
6090
6090
|
* not egress poison content — see the decay sweeper's `isBorderline`). */
|
|
6091
6091
|
trustTier: string | null;
|
|
6092
6092
|
}
|
|
6093
|
-
/**
|
|
6094
|
-
* Decay sweep candidate scan (PR2). Selects the plaintext columns
|
|
6095
|
-
* `classifyDecay` (in `memory/decay`) needs via
|
|
6096
|
-
* `unsafeFetchRaw` — NO Model per row (dodges the never-evicted RecordCache /
|
|
6097
|
-
* web Pile-2 OOM history) and NO `content` read / decrypt (zero-knowledge).
|
|
6098
|
-
*
|
|
6099
|
-
* Includes archived AND quarantined rows (so archived→delete transitions and
|
|
6100
|
-
* aged quarantined rows are seen) but excludes hard-deleted rows — the
|
|
6101
|
-
* `baseVaultConditions` default keeps `is_deleted = false`. Excludes kinded
|
|
6102
|
-
* (profile) memories, which never decay.
|
|
6103
|
-
*
|
|
6104
|
-
* Refuses to run on an unscoped multi-tenant context (see
|
|
6105
|
-
* {@link assertVaultScopeForSweep}).
|
|
6106
|
-
*/
|
|
6093
|
+
/** Return the projected plaintext fields for the decay sweep within the required vault scope. */
|
|
6107
6094
|
declare function getDecayCandidatesRawOp(ctx: VaultMemoryOperationsContext): Promise<DecayCandidateRaw[]>;
|
|
6108
6095
|
/**
|
|
6109
6096
|
* Archive a memory (decay soft state, PR2) — set `archived_at`. An archived row
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -6090,20 +6090,7 @@ interface DecayCandidateRaw {
|
|
|
6090
6090
|
* not egress poison content — see the decay sweeper's `isBorderline`). */
|
|
6091
6091
|
trustTier: string | null;
|
|
6092
6092
|
}
|
|
6093
|
-
/**
|
|
6094
|
-
* Decay sweep candidate scan (PR2). Selects the plaintext columns
|
|
6095
|
-
* `classifyDecay` (in `memory/decay`) needs via
|
|
6096
|
-
* `unsafeFetchRaw` — NO Model per row (dodges the never-evicted RecordCache /
|
|
6097
|
-
* web Pile-2 OOM history) and NO `content` read / decrypt (zero-knowledge).
|
|
6098
|
-
*
|
|
6099
|
-
* Includes archived AND quarantined rows (so archived→delete transitions and
|
|
6100
|
-
* aged quarantined rows are seen) but excludes hard-deleted rows — the
|
|
6101
|
-
* `baseVaultConditions` default keeps `is_deleted = false`. Excludes kinded
|
|
6102
|
-
* (profile) memories, which never decay.
|
|
6103
|
-
*
|
|
6104
|
-
* Refuses to run on an unscoped multi-tenant context (see
|
|
6105
|
-
* {@link assertVaultScopeForSweep}).
|
|
6106
|
-
*/
|
|
6093
|
+
/** Return the projected plaintext fields for the decay sweep within the required vault scope. */
|
|
6107
6094
|
declare function getDecayCandidatesRawOp(ctx: VaultMemoryOperationsContext): Promise<DecayCandidateRaw[]>;
|
|
6108
6095
|
/**
|
|
6109
6096
|
* Archive a memory (decay soft state, PR2) — set `archived_at`. An archived row
|
package/dist/expo/index.mjs
CHANGED
|
@@ -729,6 +729,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
729
729
|
if (cachedKey) {
|
|
730
730
|
return cachedKey;
|
|
731
731
|
}
|
|
732
|
+
const startEpoch = sessionEpoch;
|
|
732
733
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
733
734
|
if (!keyHex) {
|
|
734
735
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -741,6 +742,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
741
742
|
false,
|
|
742
743
|
["encrypt", "decrypt"]
|
|
743
744
|
);
|
|
745
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
746
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
747
|
+
}
|
|
744
748
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
745
749
|
return cryptoKey;
|
|
746
750
|
}
|
|
@@ -13191,9 +13195,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
|
|
|
13191
13195
|
}
|
|
13192
13196
|
function baseVaultSql(ctx, options) {
|
|
13193
13197
|
const clauses = [
|
|
13198
|
+
`"_status" is not 'deleted'`,
|
|
13194
13199
|
'"is_deleted" = 0',
|
|
13195
13200
|
...options?.includeArchived ? [] : ['"archived_at" is null'],
|
|
13196
|
-
`"trust_tier" is not 'quarantined'
|
|
13201
|
+
...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
|
|
13197
13202
|
'"superseded_by" is null'
|
|
13198
13203
|
];
|
|
13199
13204
|
const args = [];
|
|
@@ -13679,8 +13684,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
13679
13684
|
if (!entityCtx) {
|
|
13680
13685
|
throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
|
|
13681
13686
|
}
|
|
13682
|
-
|
|
13683
|
-
|
|
13687
|
+
let rows;
|
|
13688
|
+
try {
|
|
13689
|
+
const base = baseVaultSql(ctx);
|
|
13690
|
+
const sql = `select "id", "topics", "topics_user_managed", "topics_extracted_at", "topics_extracted_version", "updated_at" from "memory_vault" where ${base.sql} order by "created_at" desc`;
|
|
13691
|
+
rows = await ctx.vaultMemoryCollection.query(Q6.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
13692
|
+
} catch (err) {
|
|
13693
|
+
getLogger().debug(
|
|
13694
|
+
"memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
13695
|
+
);
|
|
13696
|
+
rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), Q6.sortBy("created_at", Q6.desc)).unsafeFetchRaw();
|
|
13697
|
+
}
|
|
13684
13698
|
const linkedNames = await linkedEntityNamesByMemory(
|
|
13685
13699
|
entityCtx,
|
|
13686
13700
|
rows.map((r) => r.id)
|
|
@@ -13739,10 +13753,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
13739
13753
|
}
|
|
13740
13754
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
13741
13755
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
13742
|
-
const pending =
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13756
|
+
const pending = [];
|
|
13757
|
+
for (let i = 0; i < limitedPendingRaw.length; i += 500) {
|
|
13758
|
+
const chunk = limitedPendingRaw.slice(i, i + 500);
|
|
13759
|
+
const memories = await getVaultMemoriesByIdsOp(
|
|
13760
|
+
ctx,
|
|
13761
|
+
chunk.map((raw) => raw.id)
|
|
13762
|
+
);
|
|
13763
|
+
const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
13764
|
+
for (const raw of chunk) {
|
|
13765
|
+
const memory = byId.get(raw.id);
|
|
13766
|
+
if (memory) pending.push(memory);
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13746
13769
|
const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
|
|
13747
13770
|
return {
|
|
13748
13771
|
pending,
|
|
@@ -13889,10 +13912,20 @@ function assertVaultScopeForSweep(ctx) {
|
|
|
13889
13912
|
}
|
|
13890
13913
|
async function getDecayCandidatesRawOp(ctx) {
|
|
13891
13914
|
assertVaultScopeForSweep(ctx);
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
13915
|
+
let results;
|
|
13916
|
+
try {
|
|
13917
|
+
const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
|
|
13918
|
+
const sql = `select "id", "fact_type", "event_time_end", "event_time_kind", "updated_at", "last_observed_at", "archived_at", "source", "trust_tier" from "memory_vault" where ${base.sql} and "kind" is null`;
|
|
13919
|
+
results = await ctx.vaultMemoryCollection.query(Q6.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
13920
|
+
} catch (err) {
|
|
13921
|
+
getLogger().debug(
|
|
13922
|
+
"memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
13923
|
+
);
|
|
13924
|
+
results = await ctx.vaultMemoryCollection.query(
|
|
13925
|
+
...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
|
|
13926
|
+
Q6.where("kind", null)
|
|
13927
|
+
).unsafeFetchRaw();
|
|
13928
|
+
}
|
|
13896
13929
|
return results.map((raw) => ({
|
|
13897
13930
|
uniqueId: raw.id,
|
|
13898
13931
|
factType: raw.fact_type ?? null,
|
|
@@ -23308,6 +23341,18 @@ async function assembleMemoryContext(options) {
|
|
|
23308
23341
|
|
|
23309
23342
|
// src/lib/memory/durableExtraction.ts
|
|
23310
23343
|
import { Q as Q10 } from "@nozbe/watermelondb";
|
|
23344
|
+
async function messagePositions(storage, ids) {
|
|
23345
|
+
try {
|
|
23346
|
+
return await storage.messagesCollection.query(
|
|
23347
|
+
Q10.unsafeSqlQuery(
|
|
23348
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
23349
|
+
ids
|
|
23350
|
+
)
|
|
23351
|
+
).unsafeFetchRaw();
|
|
23352
|
+
} catch {
|
|
23353
|
+
return await storage.messagesCollection.query(Q10.where("id", Q10.oneOf(ids))).unsafeFetchRaw();
|
|
23354
|
+
}
|
|
23355
|
+
}
|
|
23311
23356
|
var MAX_JOBS_PER_PASS = 3;
|
|
23312
23357
|
var MAX_ATTEMPTS = 3;
|
|
23313
23358
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -23876,9 +23921,9 @@ function createDurableAutoExtractor(options) {
|
|
|
23876
23921
|
r._setRaw("watermark_seq", null);
|
|
23877
23922
|
});
|
|
23878
23923
|
}
|
|
23879
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
23924
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
23880
23925
|
const foreignRows = new Set(
|
|
23881
|
-
rows.filter((row) => row.
|
|
23926
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
23882
23927
|
);
|
|
23883
23928
|
const sequences = /* @__PURE__ */ new Map();
|
|
23884
23929
|
if (fromSourceConversation) {
|
|
@@ -23888,7 +23933,7 @@ function createDurableAutoExtractor(options) {
|
|
|
23888
23933
|
}
|
|
23889
23934
|
for (const row of rows) {
|
|
23890
23935
|
if (foreignRows.has(row.id)) continue;
|
|
23891
|
-
const sequence = Number(row.
|
|
23936
|
+
const sequence = Number(row.message_id);
|
|
23892
23937
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
23893
23938
|
}
|
|
23894
23939
|
let boundary = -1;
|
package/dist/index.cjs
CHANGED
|
@@ -1404,6 +1404,7 @@ var EncryptionKeyMissingError = class extends Error {
|
|
|
1404
1404
|
}
|
|
1405
1405
|
};
|
|
1406
1406
|
var encryptionKeyStore = /* @__PURE__ */ new Map();
|
|
1407
|
+
var sessionEpoch = 0;
|
|
1407
1408
|
var cryptoKeyCache = /* @__PURE__ */ new Map();
|
|
1408
1409
|
var clearAllEncryptionStateCallbacks = /* @__PURE__ */ new Set();
|
|
1409
1410
|
function onClearAllEncryptionState(callback) {
|
|
@@ -1455,6 +1456,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1455
1456
|
if (cachedKey) {
|
|
1456
1457
|
return cachedKey;
|
|
1457
1458
|
}
|
|
1459
|
+
const startEpoch = sessionEpoch;
|
|
1458
1460
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
1459
1461
|
if (!keyHex) {
|
|
1460
1462
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -1467,6 +1469,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1467
1469
|
false,
|
|
1468
1470
|
["encrypt", "decrypt"]
|
|
1469
1471
|
);
|
|
1472
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
1473
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
1474
|
+
}
|
|
1470
1475
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
1471
1476
|
return cryptoKey;
|
|
1472
1477
|
}
|
|
@@ -2159,7 +2164,7 @@ async function createRemoteChatStore(options) {
|
|
|
2159
2164
|
var LRU_CAPACITY = 256;
|
|
2160
2165
|
var titleCache = /* @__PURE__ */ new Map();
|
|
2161
2166
|
var pendingDecrypts = /* @__PURE__ */ new Map();
|
|
2162
|
-
var
|
|
2167
|
+
var sessionEpoch2 = 0;
|
|
2163
2168
|
function buildCacheKey(address, encryptedTitle) {
|
|
2164
2169
|
return `${address}:${encryptedTitle}`;
|
|
2165
2170
|
}
|
|
@@ -2178,7 +2183,7 @@ function insertWithEviction(key, value) {
|
|
|
2178
2183
|
function clearLazyTitleCache() {
|
|
2179
2184
|
titleCache.clear();
|
|
2180
2185
|
pendingDecrypts.clear();
|
|
2181
|
-
|
|
2186
|
+
sessionEpoch2 += 1;
|
|
2182
2187
|
}
|
|
2183
2188
|
onClearAllEncryptionState(clearLazyTitleCache);
|
|
2184
2189
|
async function decryptConversationTitle(encryptedTitle, address) {
|
|
@@ -2200,13 +2205,13 @@ async function decryptConversationTitle(encryptedTitle, address) {
|
|
|
2200
2205
|
}
|
|
2201
2206
|
const inFlight = pendingDecrypts.get(cacheKey);
|
|
2202
2207
|
if (inFlight) return inFlight;
|
|
2203
|
-
const epochAtStart =
|
|
2208
|
+
const epochAtStart = sessionEpoch2;
|
|
2204
2209
|
const promise = (async () => {
|
|
2205
2210
|
const plaintext = await decryptField(encryptedTitle, address);
|
|
2206
2211
|
if (isEncrypted(plaintext)) {
|
|
2207
2212
|
throw new Error("decryptConversationTitle: decryption failed (returned ciphertext)");
|
|
2208
2213
|
}
|
|
2209
|
-
if (
|
|
2214
|
+
if (sessionEpoch2 === epochAtStart) {
|
|
2210
2215
|
insertWithEviction(cacheKey, plaintext);
|
|
2211
2216
|
}
|
|
2212
2217
|
return plaintext;
|
package/dist/index.mjs
CHANGED
|
@@ -1201,6 +1201,7 @@ var EncryptionKeyMissingError = class extends Error {
|
|
|
1201
1201
|
}
|
|
1202
1202
|
};
|
|
1203
1203
|
var encryptionKeyStore = /* @__PURE__ */ new Map();
|
|
1204
|
+
var sessionEpoch = 0;
|
|
1204
1205
|
var cryptoKeyCache = /* @__PURE__ */ new Map();
|
|
1205
1206
|
var clearAllEncryptionStateCallbacks = /* @__PURE__ */ new Set();
|
|
1206
1207
|
function onClearAllEncryptionState(callback) {
|
|
@@ -1252,6 +1253,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1252
1253
|
if (cachedKey) {
|
|
1253
1254
|
return cachedKey;
|
|
1254
1255
|
}
|
|
1256
|
+
const startEpoch = sessionEpoch;
|
|
1255
1257
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
1256
1258
|
if (!keyHex) {
|
|
1257
1259
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -1264,6 +1266,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
1264
1266
|
false,
|
|
1265
1267
|
["encrypt", "decrypt"]
|
|
1266
1268
|
);
|
|
1269
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
1270
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
1271
|
+
}
|
|
1267
1272
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
1268
1273
|
return cryptoKey;
|
|
1269
1274
|
}
|
|
@@ -1956,7 +1961,7 @@ async function createRemoteChatStore(options) {
|
|
|
1956
1961
|
var LRU_CAPACITY = 256;
|
|
1957
1962
|
var titleCache = /* @__PURE__ */ new Map();
|
|
1958
1963
|
var pendingDecrypts = /* @__PURE__ */ new Map();
|
|
1959
|
-
var
|
|
1964
|
+
var sessionEpoch2 = 0;
|
|
1960
1965
|
function buildCacheKey(address, encryptedTitle) {
|
|
1961
1966
|
return `${address}:${encryptedTitle}`;
|
|
1962
1967
|
}
|
|
@@ -1975,7 +1980,7 @@ function insertWithEviction(key, value) {
|
|
|
1975
1980
|
function clearLazyTitleCache() {
|
|
1976
1981
|
titleCache.clear();
|
|
1977
1982
|
pendingDecrypts.clear();
|
|
1978
|
-
|
|
1983
|
+
sessionEpoch2 += 1;
|
|
1979
1984
|
}
|
|
1980
1985
|
onClearAllEncryptionState(clearLazyTitleCache);
|
|
1981
1986
|
async function decryptConversationTitle(encryptedTitle, address) {
|
|
@@ -1997,13 +2002,13 @@ async function decryptConversationTitle(encryptedTitle, address) {
|
|
|
1997
2002
|
}
|
|
1998
2003
|
const inFlight = pendingDecrypts.get(cacheKey);
|
|
1999
2004
|
if (inFlight) return inFlight;
|
|
2000
|
-
const epochAtStart =
|
|
2005
|
+
const epochAtStart = sessionEpoch2;
|
|
2001
2006
|
const promise = (async () => {
|
|
2002
2007
|
const plaintext = await decryptField(encryptedTitle, address);
|
|
2003
2008
|
if (isEncrypted(plaintext)) {
|
|
2004
2009
|
throw new Error("decryptConversationTitle: decryption failed (returned ciphertext)");
|
|
2005
2010
|
}
|
|
2006
|
-
if (
|
|
2011
|
+
if (sessionEpoch2 === epochAtStart) {
|
|
2007
2012
|
insertWithEviction(cacheKey, plaintext);
|
|
2008
2013
|
}
|
|
2009
2014
|
return plaintext;
|
package/dist/react/index.cjs
CHANGED
|
@@ -2093,6 +2093,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2093
2093
|
if (cachedKey) {
|
|
2094
2094
|
return cachedKey;
|
|
2095
2095
|
}
|
|
2096
|
+
const startEpoch = sessionEpoch;
|
|
2096
2097
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
2097
2098
|
if (!keyHex) {
|
|
2098
2099
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -2105,6 +2106,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
2105
2106
|
false,
|
|
2106
2107
|
["encrypt", "decrypt"]
|
|
2107
2108
|
);
|
|
2109
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
2110
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
2111
|
+
}
|
|
2108
2112
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
2109
2113
|
return cryptoKey;
|
|
2110
2114
|
}
|
|
@@ -16634,9 +16638,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
|
|
|
16634
16638
|
}
|
|
16635
16639
|
function baseVaultSql(ctx, options) {
|
|
16636
16640
|
const clauses = [
|
|
16641
|
+
`"_status" is not 'deleted'`,
|
|
16637
16642
|
'"is_deleted" = 0',
|
|
16638
16643
|
...options?.includeArchived ? [] : ['"archived_at" is null'],
|
|
16639
|
-
`"trust_tier" is not 'quarantined'
|
|
16644
|
+
...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
|
|
16640
16645
|
'"superseded_by" is null'
|
|
16641
16646
|
];
|
|
16642
16647
|
const args = [];
|
|
@@ -17122,8 +17127,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17122
17127
|
if (!entityCtx) {
|
|
17123
17128
|
throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
|
|
17124
17129
|
}
|
|
17125
|
-
|
|
17126
|
-
|
|
17130
|
+
let rows;
|
|
17131
|
+
try {
|
|
17132
|
+
const base = baseVaultSql(ctx);
|
|
17133
|
+
const sql = `select "id", "topics", "topics_user_managed", "topics_extracted_at", "topics_extracted_version", "updated_at" from "memory_vault" where ${base.sql} order by "created_at" desc`;
|
|
17134
|
+
rows = await ctx.vaultMemoryCollection.query(import_watermelondb24.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
17135
|
+
} catch (err) {
|
|
17136
|
+
getLogger().debug(
|
|
17137
|
+
"memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
17138
|
+
);
|
|
17139
|
+
rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb24.Q.sortBy("created_at", import_watermelondb24.Q.desc)).unsafeFetchRaw();
|
|
17140
|
+
}
|
|
17127
17141
|
const linkedNames = await linkedEntityNamesByMemory(
|
|
17128
17142
|
entityCtx,
|
|
17129
17143
|
rows.map((r) => r.id)
|
|
@@ -17182,10 +17196,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
17182
17196
|
}
|
|
17183
17197
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
17184
17198
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
17185
|
-
const pending =
|
|
17186
|
-
|
|
17187
|
-
|
|
17188
|
-
|
|
17199
|
+
const pending = [];
|
|
17200
|
+
for (let i = 0; i < limitedPendingRaw.length; i += 500) {
|
|
17201
|
+
const chunk = limitedPendingRaw.slice(i, i + 500);
|
|
17202
|
+
const memories = await getVaultMemoriesByIdsOp(
|
|
17203
|
+
ctx,
|
|
17204
|
+
chunk.map((raw) => raw.id)
|
|
17205
|
+
);
|
|
17206
|
+
const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
17207
|
+
for (const raw of chunk) {
|
|
17208
|
+
const memory = byId.get(raw.id);
|
|
17209
|
+
if (memory) pending.push(memory);
|
|
17210
|
+
}
|
|
17211
|
+
}
|
|
17189
17212
|
const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
|
|
17190
17213
|
return {
|
|
17191
17214
|
pending,
|
|
@@ -17332,10 +17355,20 @@ function assertVaultScopeForSweep(ctx) {
|
|
|
17332
17355
|
}
|
|
17333
17356
|
async function getDecayCandidatesRawOp(ctx) {
|
|
17334
17357
|
assertVaultScopeForSweep(ctx);
|
|
17335
|
-
|
|
17336
|
-
|
|
17337
|
-
|
|
17338
|
-
|
|
17358
|
+
let results;
|
|
17359
|
+
try {
|
|
17360
|
+
const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
|
|
17361
|
+
const sql = `select "id", "fact_type", "event_time_end", "event_time_kind", "updated_at", "last_observed_at", "archived_at", "source", "trust_tier" from "memory_vault" where ${base.sql} and "kind" is null`;
|
|
17362
|
+
results = await ctx.vaultMemoryCollection.query(import_watermelondb24.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
17363
|
+
} catch (err) {
|
|
17364
|
+
getLogger().debug(
|
|
17365
|
+
"memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
17366
|
+
);
|
|
17367
|
+
results = await ctx.vaultMemoryCollection.query(
|
|
17368
|
+
...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
|
|
17369
|
+
import_watermelondb24.Q.where("kind", null)
|
|
17370
|
+
).unsafeFetchRaw();
|
|
17371
|
+
}
|
|
17339
17372
|
return results.map((raw) => ({
|
|
17340
17373
|
uniqueId: raw.id,
|
|
17341
17374
|
factType: raw.fact_type ?? null,
|
|
@@ -27299,6 +27332,18 @@ async function assembleMemoryContext(options) {
|
|
|
27299
27332
|
|
|
27300
27333
|
// src/lib/memory/durableExtraction.ts
|
|
27301
27334
|
var import_watermelondb34 = require("@nozbe/watermelondb");
|
|
27335
|
+
async function messagePositions(storage, ids) {
|
|
27336
|
+
try {
|
|
27337
|
+
return await storage.messagesCollection.query(
|
|
27338
|
+
import_watermelondb34.Q.unsafeSqlQuery(
|
|
27339
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
27340
|
+
ids
|
|
27341
|
+
)
|
|
27342
|
+
).unsafeFetchRaw();
|
|
27343
|
+
} catch {
|
|
27344
|
+
return await storage.messagesCollection.query(import_watermelondb34.Q.where("id", import_watermelondb34.Q.oneOf(ids))).unsafeFetchRaw();
|
|
27345
|
+
}
|
|
27346
|
+
}
|
|
27302
27347
|
var MAX_JOBS_PER_PASS = 3;
|
|
27303
27348
|
var MAX_ATTEMPTS = 3;
|
|
27304
27349
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -27867,9 +27912,9 @@ function createDurableAutoExtractor(options) {
|
|
|
27867
27912
|
r._setRaw("watermark_seq", null);
|
|
27868
27913
|
});
|
|
27869
27914
|
}
|
|
27870
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
27915
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
27871
27916
|
const foreignRows = new Set(
|
|
27872
|
-
rows.filter((row) => row.
|
|
27917
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
27873
27918
|
);
|
|
27874
27919
|
const sequences = /* @__PURE__ */ new Map();
|
|
27875
27920
|
if (fromSourceConversation) {
|
|
@@ -27879,7 +27924,7 @@ function createDurableAutoExtractor(options) {
|
|
|
27879
27924
|
}
|
|
27880
27925
|
for (const row of rows) {
|
|
27881
27926
|
if (foreignRows.has(row.id)) continue;
|
|
27882
|
-
const sequence = Number(row.
|
|
27927
|
+
const sequence = Number(row.message_id);
|
|
27883
27928
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
27884
27929
|
}
|
|
27885
27930
|
let boundary = -1;
|
package/dist/react/index.d.mts
CHANGED
|
@@ -6695,20 +6695,7 @@ interface DecayCandidateRaw {
|
|
|
6695
6695
|
* not egress poison content — see the decay sweeper's `isBorderline`). */
|
|
6696
6696
|
trustTier: string | null;
|
|
6697
6697
|
}
|
|
6698
|
-
/**
|
|
6699
|
-
* Decay sweep candidate scan (PR2). Selects the plaintext columns
|
|
6700
|
-
* `classifyDecay` (in `memory/decay`) needs via
|
|
6701
|
-
* `unsafeFetchRaw` — NO Model per row (dodges the never-evicted RecordCache /
|
|
6702
|
-
* web Pile-2 OOM history) and NO `content` read / decrypt (zero-knowledge).
|
|
6703
|
-
*
|
|
6704
|
-
* Includes archived AND quarantined rows (so archived→delete transitions and
|
|
6705
|
-
* aged quarantined rows are seen) but excludes hard-deleted rows — the
|
|
6706
|
-
* `baseVaultConditions` default keeps `is_deleted = false`. Excludes kinded
|
|
6707
|
-
* (profile) memories, which never decay.
|
|
6708
|
-
*
|
|
6709
|
-
* Refuses to run on an unscoped multi-tenant context (see
|
|
6710
|
-
* {@link assertVaultScopeForSweep}).
|
|
6711
|
-
*/
|
|
6698
|
+
/** Return the projected plaintext fields for the decay sweep within the required vault scope. */
|
|
6712
6699
|
declare function getDecayCandidatesRawOp(ctx: VaultMemoryOperationsContext): Promise<DecayCandidateRaw[]>;
|
|
6713
6700
|
/**
|
|
6714
6701
|
* Archive a memory (decay soft state, PR2) — set `archived_at`. An archived row
|
package/dist/react/index.d.ts
CHANGED
|
@@ -6695,20 +6695,7 @@ interface DecayCandidateRaw {
|
|
|
6695
6695
|
* not egress poison content — see the decay sweeper's `isBorderline`). */
|
|
6696
6696
|
trustTier: string | null;
|
|
6697
6697
|
}
|
|
6698
|
-
/**
|
|
6699
|
-
* Decay sweep candidate scan (PR2). Selects the plaintext columns
|
|
6700
|
-
* `classifyDecay` (in `memory/decay`) needs via
|
|
6701
|
-
* `unsafeFetchRaw` — NO Model per row (dodges the never-evicted RecordCache /
|
|
6702
|
-
* web Pile-2 OOM history) and NO `content` read / decrypt (zero-knowledge).
|
|
6703
|
-
*
|
|
6704
|
-
* Includes archived AND quarantined rows (so archived→delete transitions and
|
|
6705
|
-
* aged quarantined rows are seen) but excludes hard-deleted rows — the
|
|
6706
|
-
* `baseVaultConditions` default keeps `is_deleted = false`. Excludes kinded
|
|
6707
|
-
* (profile) memories, which never decay.
|
|
6708
|
-
*
|
|
6709
|
-
* Refuses to run on an unscoped multi-tenant context (see
|
|
6710
|
-
* {@link assertVaultScopeForSweep}).
|
|
6711
|
-
*/
|
|
6698
|
+
/** Return the projected plaintext fields for the decay sweep within the required vault scope. */
|
|
6712
6699
|
declare function getDecayCandidatesRawOp(ctx: VaultMemoryOperationsContext): Promise<DecayCandidateRaw[]>;
|
|
6713
6700
|
/**
|
|
6714
6701
|
* Archive a memory (decay soft state, PR2) — set `archived_at`. An archived row
|
package/dist/react/index.mjs
CHANGED
|
@@ -295,6 +295,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
295
295
|
if (cachedKey) {
|
|
296
296
|
return cachedKey;
|
|
297
297
|
}
|
|
298
|
+
const startEpoch = sessionEpoch;
|
|
298
299
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
299
300
|
if (!keyHex) {
|
|
300
301
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -307,6 +308,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
307
308
|
false,
|
|
308
309
|
["encrypt", "decrypt"]
|
|
309
310
|
);
|
|
311
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
312
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
313
|
+
}
|
|
310
314
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
311
315
|
return cryptoKey;
|
|
312
316
|
}
|
|
@@ -14803,9 +14807,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
|
|
|
14803
14807
|
}
|
|
14804
14808
|
function baseVaultSql(ctx, options) {
|
|
14805
14809
|
const clauses = [
|
|
14810
|
+
`"_status" is not 'deleted'`,
|
|
14806
14811
|
'"is_deleted" = 0',
|
|
14807
14812
|
...options?.includeArchived ? [] : ['"archived_at" is null'],
|
|
14808
|
-
`"trust_tier" is not 'quarantined'
|
|
14813
|
+
...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
|
|
14809
14814
|
'"superseded_by" is null'
|
|
14810
14815
|
];
|
|
14811
14816
|
const args = [];
|
|
@@ -15291,8 +15296,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
15291
15296
|
if (!entityCtx) {
|
|
15292
15297
|
throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
|
|
15293
15298
|
}
|
|
15294
|
-
|
|
15295
|
-
|
|
15299
|
+
let rows;
|
|
15300
|
+
try {
|
|
15301
|
+
const base = baseVaultSql(ctx);
|
|
15302
|
+
const sql = `select "id", "topics", "topics_user_managed", "topics_extracted_at", "topics_extracted_version", "updated_at" from "memory_vault" where ${base.sql} order by "created_at" desc`;
|
|
15303
|
+
rows = await ctx.vaultMemoryCollection.query(Q9.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
15304
|
+
} catch (err) {
|
|
15305
|
+
getLogger().debug(
|
|
15306
|
+
"memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
15307
|
+
);
|
|
15308
|
+
rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), Q9.sortBy("created_at", Q9.desc)).unsafeFetchRaw();
|
|
15309
|
+
}
|
|
15296
15310
|
const linkedNames = await linkedEntityNamesByMemory(
|
|
15297
15311
|
entityCtx,
|
|
15298
15312
|
rows.map((r) => r.id)
|
|
@@ -15351,10 +15365,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
15351
15365
|
}
|
|
15352
15366
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
15353
15367
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
15354
|
-
const pending =
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15368
|
+
const pending = [];
|
|
15369
|
+
for (let i = 0; i < limitedPendingRaw.length; i += 500) {
|
|
15370
|
+
const chunk = limitedPendingRaw.slice(i, i + 500);
|
|
15371
|
+
const memories = await getVaultMemoriesByIdsOp(
|
|
15372
|
+
ctx,
|
|
15373
|
+
chunk.map((raw) => raw.id)
|
|
15374
|
+
);
|
|
15375
|
+
const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
15376
|
+
for (const raw of chunk) {
|
|
15377
|
+
const memory = byId.get(raw.id);
|
|
15378
|
+
if (memory) pending.push(memory);
|
|
15379
|
+
}
|
|
15380
|
+
}
|
|
15358
15381
|
const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
|
|
15359
15382
|
return {
|
|
15360
15383
|
pending,
|
|
@@ -15501,10 +15524,20 @@ function assertVaultScopeForSweep(ctx) {
|
|
|
15501
15524
|
}
|
|
15502
15525
|
async function getDecayCandidatesRawOp(ctx) {
|
|
15503
15526
|
assertVaultScopeForSweep(ctx);
|
|
15504
|
-
|
|
15505
|
-
|
|
15506
|
-
|
|
15507
|
-
|
|
15527
|
+
let results;
|
|
15528
|
+
try {
|
|
15529
|
+
const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
|
|
15530
|
+
const sql = `select "id", "fact_type", "event_time_end", "event_time_kind", "updated_at", "last_observed_at", "archived_at", "source", "trust_tier" from "memory_vault" where ${base.sql} and "kind" is null`;
|
|
15531
|
+
results = await ctx.vaultMemoryCollection.query(Q9.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
15532
|
+
} catch (err) {
|
|
15533
|
+
getLogger().debug(
|
|
15534
|
+
"memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
15535
|
+
);
|
|
15536
|
+
results = await ctx.vaultMemoryCollection.query(
|
|
15537
|
+
...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
|
|
15538
|
+
Q9.where("kind", null)
|
|
15539
|
+
).unsafeFetchRaw();
|
|
15540
|
+
}
|
|
15508
15541
|
return results.map((raw) => ({
|
|
15509
15542
|
uniqueId: raw.id,
|
|
15510
15543
|
factType: raw.fact_type ?? null,
|
|
@@ -25413,6 +25446,18 @@ async function assembleMemoryContext(options) {
|
|
|
25413
25446
|
|
|
25414
25447
|
// src/lib/memory/durableExtraction.ts
|
|
25415
25448
|
import { Q as Q17 } from "@nozbe/watermelondb";
|
|
25449
|
+
async function messagePositions(storage, ids) {
|
|
25450
|
+
try {
|
|
25451
|
+
return await storage.messagesCollection.query(
|
|
25452
|
+
Q17.unsafeSqlQuery(
|
|
25453
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
25454
|
+
ids
|
|
25455
|
+
)
|
|
25456
|
+
).unsafeFetchRaw();
|
|
25457
|
+
} catch {
|
|
25458
|
+
return await storage.messagesCollection.query(Q17.where("id", Q17.oneOf(ids))).unsafeFetchRaw();
|
|
25459
|
+
}
|
|
25460
|
+
}
|
|
25416
25461
|
var MAX_JOBS_PER_PASS = 3;
|
|
25417
25462
|
var MAX_ATTEMPTS = 3;
|
|
25418
25463
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -25981,9 +26026,9 @@ function createDurableAutoExtractor(options) {
|
|
|
25981
26026
|
r._setRaw("watermark_seq", null);
|
|
25982
26027
|
});
|
|
25983
26028
|
}
|
|
25984
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
26029
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
25985
26030
|
const foreignRows = new Set(
|
|
25986
|
-
rows.filter((row) => row.
|
|
26031
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
25987
26032
|
);
|
|
25988
26033
|
const sequences = /* @__PURE__ */ new Map();
|
|
25989
26034
|
if (fromSourceConversation) {
|
|
@@ -25993,7 +26038,7 @@ function createDurableAutoExtractor(options) {
|
|
|
25993
26038
|
}
|
|
25994
26039
|
for (const row of rows) {
|
|
25995
26040
|
if (foreignRows.has(row.id)) continue;
|
|
25996
|
-
const sequence = Number(row.
|
|
26041
|
+
const sequence = Number(row.message_id);
|
|
25997
26042
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
25998
26043
|
}
|
|
25999
26044
|
let boundary = -1;
|
package/dist/server/index.cjs
CHANGED
|
@@ -17078,6 +17078,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
17078
17078
|
if (cachedKey) {
|
|
17079
17079
|
return cachedKey;
|
|
17080
17080
|
}
|
|
17081
|
+
const startEpoch = sessionEpoch;
|
|
17081
17082
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
17082
17083
|
if (!keyHex) {
|
|
17083
17084
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -17090,6 +17091,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
17090
17091
|
false,
|
|
17091
17092
|
["encrypt", "decrypt"]
|
|
17092
17093
|
);
|
|
17094
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
17095
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
17096
|
+
}
|
|
17093
17097
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
17094
17098
|
return cryptoKey;
|
|
17095
17099
|
}
|
|
@@ -28918,9 +28922,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
|
|
|
28918
28922
|
}
|
|
28919
28923
|
function baseVaultSql(ctx, options) {
|
|
28920
28924
|
const clauses = [
|
|
28925
|
+
`"_status" is not 'deleted'`,
|
|
28921
28926
|
'"is_deleted" = 0',
|
|
28922
28927
|
...options?.includeArchived ? [] : ['"archived_at" is null'],
|
|
28923
|
-
`"trust_tier" is not 'quarantined'
|
|
28928
|
+
...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
|
|
28924
28929
|
'"superseded_by" is null'
|
|
28925
28930
|
];
|
|
28926
28931
|
const args = [];
|
|
@@ -29406,8 +29411,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
29406
29411
|
if (!entityCtx) {
|
|
29407
29412
|
throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
|
|
29408
29413
|
}
|
|
29409
|
-
|
|
29410
|
-
|
|
29414
|
+
let rows;
|
|
29415
|
+
try {
|
|
29416
|
+
const base = baseVaultSql(ctx);
|
|
29417
|
+
const sql = `select "id", "topics", "topics_user_managed", "topics_extracted_at", "topics_extracted_version", "updated_at" from "memory_vault" where ${base.sql} order by "created_at" desc`;
|
|
29418
|
+
rows = await ctx.vaultMemoryCollection.query(import_watermelondb19.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
29419
|
+
} catch (err) {
|
|
29420
|
+
getLogger().debug(
|
|
29421
|
+
"memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
29422
|
+
);
|
|
29423
|
+
rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb19.Q.sortBy("created_at", import_watermelondb19.Q.desc)).unsafeFetchRaw();
|
|
29424
|
+
}
|
|
29411
29425
|
const linkedNames = await linkedEntityNamesByMemory(
|
|
29412
29426
|
entityCtx,
|
|
29413
29427
|
rows.map((r) => r.id)
|
|
@@ -29466,10 +29480,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
29466
29480
|
}
|
|
29467
29481
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
29468
29482
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
29469
|
-
const pending =
|
|
29470
|
-
|
|
29471
|
-
|
|
29472
|
-
|
|
29483
|
+
const pending = [];
|
|
29484
|
+
for (let i = 0; i < limitedPendingRaw.length; i += 500) {
|
|
29485
|
+
const chunk = limitedPendingRaw.slice(i, i + 500);
|
|
29486
|
+
const memories = await getVaultMemoriesByIdsOp(
|
|
29487
|
+
ctx,
|
|
29488
|
+
chunk.map((raw) => raw.id)
|
|
29489
|
+
);
|
|
29490
|
+
const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
29491
|
+
for (const raw of chunk) {
|
|
29492
|
+
const memory = byId.get(raw.id);
|
|
29493
|
+
if (memory) pending.push(memory);
|
|
29494
|
+
}
|
|
29495
|
+
}
|
|
29473
29496
|
const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
|
|
29474
29497
|
return {
|
|
29475
29498
|
pending,
|
|
@@ -29616,10 +29639,20 @@ function assertVaultScopeForSweep(ctx) {
|
|
|
29616
29639
|
}
|
|
29617
29640
|
async function getDecayCandidatesRawOp(ctx) {
|
|
29618
29641
|
assertVaultScopeForSweep(ctx);
|
|
29619
|
-
|
|
29620
|
-
|
|
29621
|
-
|
|
29622
|
-
|
|
29642
|
+
let results;
|
|
29643
|
+
try {
|
|
29644
|
+
const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
|
|
29645
|
+
const sql = `select "id", "fact_type", "event_time_end", "event_time_kind", "updated_at", "last_observed_at", "archived_at", "source", "trust_tier" from "memory_vault" where ${base.sql} and "kind" is null`;
|
|
29646
|
+
results = await ctx.vaultMemoryCollection.query(import_watermelondb19.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
29647
|
+
} catch (err) {
|
|
29648
|
+
getLogger().debug(
|
|
29649
|
+
"memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
29650
|
+
);
|
|
29651
|
+
results = await ctx.vaultMemoryCollection.query(
|
|
29652
|
+
...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
|
|
29653
|
+
import_watermelondb19.Q.where("kind", null)
|
|
29654
|
+
).unsafeFetchRaw();
|
|
29655
|
+
}
|
|
29623
29656
|
return results.map((raw) => ({
|
|
29624
29657
|
uniqueId: raw.id,
|
|
29625
29658
|
factType: raw.fact_type ?? null,
|
|
@@ -37112,6 +37145,18 @@ async function assembleMemoryContext(options) {
|
|
|
37112
37145
|
|
|
37113
37146
|
// src/lib/memory/durableExtraction.ts
|
|
37114
37147
|
var import_watermelondb25 = __toESM(require_watermelondb());
|
|
37148
|
+
async function messagePositions(storage, ids) {
|
|
37149
|
+
try {
|
|
37150
|
+
return await storage.messagesCollection.query(
|
|
37151
|
+
import_watermelondb25.Q.unsafeSqlQuery(
|
|
37152
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
37153
|
+
ids
|
|
37154
|
+
)
|
|
37155
|
+
).unsafeFetchRaw();
|
|
37156
|
+
} catch {
|
|
37157
|
+
return await storage.messagesCollection.query(import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).unsafeFetchRaw();
|
|
37158
|
+
}
|
|
37159
|
+
}
|
|
37115
37160
|
var MAX_JOBS_PER_PASS = 3;
|
|
37116
37161
|
var MAX_ATTEMPTS = 3;
|
|
37117
37162
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -37680,9 +37725,9 @@ function createDurableAutoExtractor(options) {
|
|
|
37680
37725
|
r._setRaw("watermark_seq", null);
|
|
37681
37726
|
});
|
|
37682
37727
|
}
|
|
37683
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
37728
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
37684
37729
|
const foreignRows = new Set(
|
|
37685
|
-
rows.filter((row) => row.
|
|
37730
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
37686
37731
|
);
|
|
37687
37732
|
const sequences = /* @__PURE__ */ new Map();
|
|
37688
37733
|
if (fromSourceConversation) {
|
|
@@ -37692,7 +37737,7 @@ function createDurableAutoExtractor(options) {
|
|
|
37692
37737
|
}
|
|
37693
37738
|
for (const row of rows) {
|
|
37694
37739
|
if (foreignRows.has(row.id)) continue;
|
|
37695
|
-
const sequence = Number(row.
|
|
37740
|
+
const sequence = Number(row.message_id);
|
|
37696
37741
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
37697
37742
|
}
|
|
37698
37743
|
let boundary = -1;
|
package/dist/server/index.d.mts
CHANGED
|
@@ -5666,20 +5666,7 @@ interface DecayCandidateRaw {
|
|
|
5666
5666
|
* not egress poison content — see the decay sweeper's `isBorderline`). */
|
|
5667
5667
|
trustTier: string | null;
|
|
5668
5668
|
}
|
|
5669
|
-
/**
|
|
5670
|
-
* Decay sweep candidate scan (PR2). Selects the plaintext columns
|
|
5671
|
-
* `classifyDecay` (in `memory/decay`) needs via
|
|
5672
|
-
* `unsafeFetchRaw` — NO Model per row (dodges the never-evicted RecordCache /
|
|
5673
|
-
* web Pile-2 OOM history) and NO `content` read / decrypt (zero-knowledge).
|
|
5674
|
-
*
|
|
5675
|
-
* Includes archived AND quarantined rows (so archived→delete transitions and
|
|
5676
|
-
* aged quarantined rows are seen) but excludes hard-deleted rows — the
|
|
5677
|
-
* `baseVaultConditions` default keeps `is_deleted = false`. Excludes kinded
|
|
5678
|
-
* (profile) memories, which never decay.
|
|
5679
|
-
*
|
|
5680
|
-
* Refuses to run on an unscoped multi-tenant context (see
|
|
5681
|
-
* {@link assertVaultScopeForSweep}).
|
|
5682
|
-
*/
|
|
5669
|
+
/** Return the projected plaintext fields for the decay sweep within the required vault scope. */
|
|
5683
5670
|
declare function getDecayCandidatesRawOp(ctx: VaultMemoryOperationsContext): Promise<DecayCandidateRaw[]>;
|
|
5684
5671
|
/**
|
|
5685
5672
|
* Archive a memory (decay soft state, PR2) — set `archived_at`. An archived row
|
package/dist/server/index.d.ts
CHANGED
|
@@ -5666,20 +5666,7 @@ interface DecayCandidateRaw {
|
|
|
5666
5666
|
* not egress poison content — see the decay sweeper's `isBorderline`). */
|
|
5667
5667
|
trustTier: string | null;
|
|
5668
5668
|
}
|
|
5669
|
-
/**
|
|
5670
|
-
* Decay sweep candidate scan (PR2). Selects the plaintext columns
|
|
5671
|
-
* `classifyDecay` (in `memory/decay`) needs via
|
|
5672
|
-
* `unsafeFetchRaw` — NO Model per row (dodges the never-evicted RecordCache /
|
|
5673
|
-
* web Pile-2 OOM history) and NO `content` read / decrypt (zero-knowledge).
|
|
5674
|
-
*
|
|
5675
|
-
* Includes archived AND quarantined rows (so archived→delete transitions and
|
|
5676
|
-
* aged quarantined rows are seen) but excludes hard-deleted rows — the
|
|
5677
|
-
* `baseVaultConditions` default keeps `is_deleted = false`. Excludes kinded
|
|
5678
|
-
* (profile) memories, which never decay.
|
|
5679
|
-
*
|
|
5680
|
-
* Refuses to run on an unscoped multi-tenant context (see
|
|
5681
|
-
* {@link assertVaultScopeForSweep}).
|
|
5682
|
-
*/
|
|
5669
|
+
/** Return the projected plaintext fields for the decay sweep within the required vault scope. */
|
|
5683
5670
|
declare function getDecayCandidatesRawOp(ctx: VaultMemoryOperationsContext): Promise<DecayCandidateRaw[]>;
|
|
5684
5671
|
/**
|
|
5685
5672
|
* Archive a memory (decay soft state, PR2) — set `archived_at`. An archived row
|
package/dist/server/index.mjs
CHANGED
|
@@ -15407,6 +15407,7 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
15407
15407
|
if (cachedKey) {
|
|
15408
15408
|
return cachedKey;
|
|
15409
15409
|
}
|
|
15410
|
+
const startEpoch = sessionEpoch;
|
|
15410
15411
|
const keyHex = getStoredKeyByVersion(address, version);
|
|
15411
15412
|
if (!keyHex) {
|
|
15412
15413
|
throw new EncryptionKeyMissingError(address, version);
|
|
@@ -15419,6 +15420,9 @@ async function getEncryptionKey(address, version = "v3") {
|
|
|
15419
15420
|
false,
|
|
15420
15421
|
["encrypt", "decrypt"]
|
|
15421
15422
|
);
|
|
15423
|
+
if (sessionEpoch !== startEpoch || getStoredKeyByVersion(address, version) !== keyHex) {
|
|
15424
|
+
throw new EncryptionKeyMissingError(address, version);
|
|
15425
|
+
}
|
|
15422
15426
|
cryptoKeyCache.set(cacheKey, cryptoKey);
|
|
15423
15427
|
return cryptoKey;
|
|
15424
15428
|
}
|
|
@@ -27209,9 +27213,10 @@ async function getVaultRankingProjectionsOp(ctx, options) {
|
|
|
27209
27213
|
}
|
|
27210
27214
|
function baseVaultSql(ctx, options) {
|
|
27211
27215
|
const clauses = [
|
|
27216
|
+
`"_status" is not 'deleted'`,
|
|
27212
27217
|
'"is_deleted" = 0',
|
|
27213
27218
|
...options?.includeArchived ? [] : ['"archived_at" is null'],
|
|
27214
|
-
`"trust_tier" is not 'quarantined'
|
|
27219
|
+
...options?.includeQuarantined ? [] : [`"trust_tier" is not 'quarantined'`],
|
|
27215
27220
|
'"superseded_by" is null'
|
|
27216
27221
|
];
|
|
27217
27222
|
const args = [];
|
|
@@ -27697,8 +27702,17 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
27697
27702
|
if (!entityCtx) {
|
|
27698
27703
|
throw new Error("getMemoriesNeedingTopicExtractionOp requires ctx.entityCtx");
|
|
27699
27704
|
}
|
|
27700
|
-
|
|
27701
|
-
|
|
27705
|
+
let rows;
|
|
27706
|
+
try {
|
|
27707
|
+
const base = baseVaultSql(ctx);
|
|
27708
|
+
const sql = `select "id", "topics", "topics_user_managed", "topics_extracted_at", "topics_extracted_version", "updated_at" from "memory_vault" where ${base.sql} order by "created_at" desc`;
|
|
27709
|
+
rows = await ctx.vaultMemoryCollection.query(import_watermelondb19.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
27710
|
+
} catch (err) {
|
|
27711
|
+
getLogger().debug(
|
|
27712
|
+
"memoryVault: topic sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
27713
|
+
);
|
|
27714
|
+
rows = await ctx.vaultMemoryCollection.query(...baseVaultConditions(ctx), import_watermelondb19.Q.sortBy("created_at", import_watermelondb19.Q.desc)).unsafeFetchRaw();
|
|
27715
|
+
}
|
|
27702
27716
|
const linkedNames = await linkedEntityNamesByMemory(
|
|
27703
27717
|
entityCtx,
|
|
27704
27718
|
rows.map((r) => r.id)
|
|
@@ -27757,10 +27771,19 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
|
|
|
27757
27771
|
}
|
|
27758
27772
|
const orderedPendingRaw = [...stampedPendingRaw, ...pendingRaw];
|
|
27759
27773
|
const limitedPendingRaw = cap !== void 0 ? orderedPendingRaw.slice(0, cap) : orderedPendingRaw;
|
|
27760
|
-
const pending =
|
|
27761
|
-
|
|
27762
|
-
|
|
27763
|
-
|
|
27774
|
+
const pending = [];
|
|
27775
|
+
for (let i = 0; i < limitedPendingRaw.length; i += 500) {
|
|
27776
|
+
const chunk = limitedPendingRaw.slice(i, i + 500);
|
|
27777
|
+
const memories = await getVaultMemoriesByIdsOp(
|
|
27778
|
+
ctx,
|
|
27779
|
+
chunk.map((raw) => raw.id)
|
|
27780
|
+
);
|
|
27781
|
+
const byId = new Map(memories.map((memory) => [memory.uniqueId, memory]));
|
|
27782
|
+
for (const raw of chunk) {
|
|
27783
|
+
const memory = byId.get(raw.id);
|
|
27784
|
+
if (memory) pending.push(memory);
|
|
27785
|
+
}
|
|
27786
|
+
}
|
|
27764
27787
|
const applyCap = (ids) => cap !== void 0 ? ids.slice(0, cap) : ids;
|
|
27765
27788
|
return {
|
|
27766
27789
|
pending,
|
|
@@ -27907,10 +27930,20 @@ function assertVaultScopeForSweep(ctx) {
|
|
|
27907
27930
|
}
|
|
27908
27931
|
async function getDecayCandidatesRawOp(ctx) {
|
|
27909
27932
|
assertVaultScopeForSweep(ctx);
|
|
27910
|
-
|
|
27911
|
-
|
|
27912
|
-
|
|
27913
|
-
|
|
27933
|
+
let results;
|
|
27934
|
+
try {
|
|
27935
|
+
const base = baseVaultSql(ctx, { includeArchived: true, includeQuarantined: true });
|
|
27936
|
+
const sql = `select "id", "fact_type", "event_time_end", "event_time_kind", "updated_at", "last_observed_at", "archived_at", "source", "trust_tier" from "memory_vault" where ${base.sql} and "kind" is null`;
|
|
27937
|
+
results = await ctx.vaultMemoryCollection.query(import_watermelondb19.Q.unsafeSqlQuery(sql, base.args)).unsafeFetchRaw();
|
|
27938
|
+
} catch (err) {
|
|
27939
|
+
getLogger().debug(
|
|
27940
|
+
"memoryVault: decay sweep SQL unavailable; use the adapter query: " + (err instanceof Error ? err.message : String(err))
|
|
27941
|
+
);
|
|
27942
|
+
results = await ctx.vaultMemoryCollection.query(
|
|
27943
|
+
...baseVaultConditions(ctx, { includeArchived: true, includeQuarantined: true }),
|
|
27944
|
+
import_watermelondb19.Q.where("kind", null)
|
|
27945
|
+
).unsafeFetchRaw();
|
|
27946
|
+
}
|
|
27914
27947
|
return results.map((raw) => ({
|
|
27915
27948
|
uniqueId: raw.id,
|
|
27916
27949
|
factType: raw.fact_type ?? null,
|
|
@@ -35359,6 +35392,18 @@ async function assembleMemoryContext(options) {
|
|
|
35359
35392
|
|
|
35360
35393
|
// src/lib/memory/durableExtraction.ts
|
|
35361
35394
|
var import_watermelondb25 = __toESM(require_watermelondb());
|
|
35395
|
+
async function messagePositions(storage, ids) {
|
|
35396
|
+
try {
|
|
35397
|
+
return await storage.messagesCollection.query(
|
|
35398
|
+
import_watermelondb25.Q.unsafeSqlQuery(
|
|
35399
|
+
`select "id", "conversation_id", "message_id" from "history" where "_status" is not 'deleted' and "id" in (${ids.map(() => "?").join(",")})`,
|
|
35400
|
+
ids
|
|
35401
|
+
)
|
|
35402
|
+
).unsafeFetchRaw();
|
|
35403
|
+
} catch {
|
|
35404
|
+
return await storage.messagesCollection.query(import_watermelondb25.Q.where("id", import_watermelondb25.Q.oneOf(ids))).unsafeFetchRaw();
|
|
35405
|
+
}
|
|
35406
|
+
}
|
|
35362
35407
|
var MAX_JOBS_PER_PASS = 3;
|
|
35363
35408
|
var MAX_ATTEMPTS = 3;
|
|
35364
35409
|
var MAX_FAILED_SESSIONS = 3;
|
|
@@ -35927,9 +35972,9 @@ function createDurableAutoExtractor(options) {
|
|
|
35927
35972
|
r._setRaw("watermark_seq", null);
|
|
35928
35973
|
});
|
|
35929
35974
|
}
|
|
35930
|
-
const rows = fromSourceConversation ? [] : await storage
|
|
35975
|
+
const rows = fromSourceConversation ? [] : await messagePositions(storage, ids);
|
|
35931
35976
|
const foreignRows = new Set(
|
|
35932
|
-
rows.filter((row) => row.
|
|
35977
|
+
rows.filter((row) => row.conversation_id !== conversationId).map((row) => row.id)
|
|
35933
35978
|
);
|
|
35934
35979
|
const sequences = /* @__PURE__ */ new Map();
|
|
35935
35980
|
if (fromSourceConversation) {
|
|
@@ -35939,7 +35984,7 @@ function createDurableAutoExtractor(options) {
|
|
|
35939
35984
|
}
|
|
35940
35985
|
for (const row of rows) {
|
|
35941
35986
|
if (foreignRows.has(row.id)) continue;
|
|
35942
|
-
const sequence = Number(row.
|
|
35987
|
+
const sequence = Number(row.message_id);
|
|
35943
35988
|
if (Number.isFinite(sequence) && sequence > 0) sequences.set(row.id, sequence);
|
|
35944
35989
|
}
|
|
35945
35990
|
let boundary = -1;
|